From b85d40b1b3323a223fc9db1d97380006bd47eb88 Mon Sep 17 00:00:00 2001 From: hacksalot Date: Thu, 31 Dec 2015 06:38:30 -0500 Subject: [PATCH] Improve XML encoding for Word docs. Fix various encoding errors. --- src/core/fresh-resume.js | 79 +++++++++++++++++++++++++++++++++ src/eng/generic-helpers.js | 9 ++-- src/eng/handlebars-generator.js | 7 ++- src/utils/html-to-wpml.js | 2 + 4 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/core/fresh-resume.js b/src/core/fresh-resume.js index 58c97a5..54d08ac 100644 --- a/src/core/fresh-resume.js +++ b/src/core/fresh-resume.js @@ -13,6 +13,7 @@ Definition of the FRESHResume class. , __ = require('lodash') , PATH = require('path') , moment = require('moment') + , XML = require('xml-escape') , MD = require('marked') , CONVERTER = require('./convert') , JRSResume = require('./jrs-resume'); @@ -81,12 +82,90 @@ Definition of the FRESHResume class. return JSON.stringify( obj, replacer, 2 ); }; + /** + Create a copy of this resume in which all string fields have been run through + a transformation function (such as a Markdown filter or XML encoder). + */ + FreshResume.prototype.transformStrings = function( filters, transformer ) { + + var that = this; + var ret = this.dupe(); + + // TODO: refactor recursion + function transformStringsInObject( obj ) { + + if( !obj ) return; + if( moment.isMoment( obj ) ) return; + + if( _.isArray( obj ) ) { + obj.forEach( function(elem, idx, ar) { + if( typeof elem === 'string' || elem instanceof String ) + ar[idx] = transformer( null, elem ); + else if (_.isObject(elem)) + transformStringsInObject( elem ); + }); + } + else if (_.isObject( obj )) { + Object.keys( obj ).forEach(function(k) { + var sub = obj[k]; + if( typeof sub === 'string' || sub instanceof String ) { + if( filters.length && _.contains(filters, k) ) + return; + obj[k] = transformer( k, sub ); + } + else if (_.isObject( sub )) + transformStringsInObject( sub ); + }); + } + + } + + Object.keys( ret ).forEach(function(member){ + transformStringsInObject( ret[ member ] ); + }); + + return ret; + }; + /** Create a copy of this resume in which all fields have been interpreted as Markdown. */ FreshResume.prototype.markdownify = function() { + function MDIN( txt ){ + return MD(txt || '' ).replace(/^\s*

|<\/p>\s*$/gi, ''); + } + + function trx(key, val) { + if( key === 'summary' ) { + return MD(val); + } + return MDIN(val); + } + + return this.transformStrings( ['skills','url','start','end','date'], trx ); + }; + + /** + Create a copy of this resume in which all fields have been interpreted as + Markdown. + */ + FreshResume.prototype.xmlify = function() { + + function trx(key, val) { + return XML(val); + } + + return this.transformStrings( [], trx ); + }; + + /** + Create a copy of this resume in which all fields have been interpreted as + Markdown. + */ + FreshResume.prototype.markdownify2 = function() { + var that = this; var ret = this.dupe(); diff --git a/src/eng/generic-helpers.js b/src/eng/generic-helpers.js index a930933..6b10dc8 100644 --- a/src/eng/generic-helpers.js +++ b/src/eng/generic-helpers.js @@ -9,6 +9,7 @@ Generic template helper definitions for HackMyResume / FluentCV. var MD = require('marked') , H2W = require('../utils/html-to-wpml') + , XML = require('xml-escape') , moment = require('moment') , _ = require('underscore'); @@ -33,10 +34,12 @@ Generic template helper definitions for HackMyResume / FluentCV. wpml: function( txt, inline ) { if(!txt) return ''; inline = (inline && !inline.hash) || false; + txt = XML(txt.trim()); txt = inline ? - MD(txt.trim()).replace(/^\s*

|<\/p>\s*$/gi, '') : - MD(txt.trim()); - txt = H2W( txt.trim() ); + MD(txt).replace(/^\s*

|<\/p>\s*$/gi, '') : + MD(txt); + txt = H2W( txt ); + console.log(txt); return txt; }, diff --git a/src/eng/handlebars-generator.js b/src/eng/handlebars-generator.js index 27a8f7d..b9895eb 100644 --- a/src/eng/handlebars-generator.js +++ b/src/eng/handlebars-generator.js @@ -35,8 +35,13 @@ Definition of the HandlebarsGenerator class. // Compile and run the Handlebars template. var template = HANDLEBARS.compile(jst); + + var encData = json; + ( format === 'html' || format === 'pdf' ) && (encData = json.markdownify()); + ( format === 'doc' ) && (encData = json.xmlify()); + return template({ - r: format === 'html' || format === 'pdf' || format === 'png' ? json.markdownify() : json, + r: encData, RAW: json, filt: opts.filters, cssInfo: cssInfo, diff --git a/src/utils/html-to-wpml.js b/src/utils/html-to-wpml.js index 89f1c51..243a18d 100644 --- a/src/utils/html-to-wpml.js +++ b/src/utils/html-to-wpml.js @@ -47,6 +47,7 @@ Definition of the Markdown to WordProcessingML conversion routine. break; case 'Chars': + if( tok.chars.trim().length ) { var style = is_bold ? '' : ''; style += is_italic ? '': ''; style += is_link ? '' : ''; @@ -54,6 +55,7 @@ Definition of the Markdown to WordProcessingML conversion routine. (is_link ? ('') : '') + '' + style + '' + tok.chars + '' + (is_link ? '' : ''); + } break; } });