From b5768bced10c6dafe8bc621c6ef01aa0bd9d975c Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 7 Jul 2016 20:25:41 +0100 Subject: Store children in thunks when generating HTML nodes --- lib/document-to-html.js | 80 ++++++++++++++++++++++++------------------------- lib/html-paths.js | 26 ++++++++++++++-- lib/html/index.js | 9 ------ 3 files changed, 63 insertions(+), 52 deletions(-) (limited to 'lib') diff --git a/lib/document-to-html.js b/lib/document-to-html.js index f2d5229..2b7815a 100644 --- a/lib/document-to-html.js +++ b/lib/document-to-html.js @@ -89,16 +89,17 @@ function DocumentConversion(options) { } function convertParagraph(element, messages) { - var style = styleForParagraph(element, messages); - - var children = convertElements(element.children, messages); - if (!options.ignoreEmptyParagraphs) { - children = [Html.forceWrite].concat(children); - } - return Html.pathToNodes(style, children); + return htmlPathForParagraph(element, messages).wrap(function() { + var content = convertElements(element.children, messages); + if (options.ignoreEmptyParagraphs) { + return content; + } else { + return [Html.forceWrite].concat(content); + } + }); } - function styleForParagraph(element, messages) { + function htmlPathForParagraph(element, messages) { var style = findStyle(element); if (style) { @@ -112,57 +113,54 @@ function DocumentConversion(options) { } function convertRun(run, messages) { - var style; - if (run.styleId) { - style = findStyle(run); - if (!style) { - messages.push(unrecognisedStyleWarning("run", run)); - } - } - var result = convertElements(run.children, messages); + var nodes = function() { + return convertElements(run.children, messages); + }; + var paths = []; if (run.isStrikethrough) { - result = convertRunProperty(result, "strikethrough", "s"); + paths.push(findHtmlPathForRunProperty("strikethrough", "s")); } if (run.isUnderline) { - result = convertUnderline(result); + paths.push(findHtmlPathForRunProperty("underline")); } if (run.verticalAlignment === documents.verticalAlignment.subscript) { - result = wrapInNonFreshElement(result, "sub"); + paths.push(htmlPaths.element("sub", {}, {fresh: false})); } if (run.verticalAlignment === documents.verticalAlignment.superscript) { - result = wrapInNonFreshElement(result, "sup"); + paths.push(htmlPaths.element("sup", {}, {fresh: false})); } if (run.isItalic) { - result = convertRunProperty(result, "italic", "em"); + paths.push(findHtmlPathForRunProperty("italic", "em")); } if (run.isBold) { - result = convertRunProperty(result, "bold", "strong"); + paths.push(findHtmlPathForRunProperty("bold", "strong")); } - if (style) { - result = Html.pathToNodes(style.to, result); - } - return result; - } - - function wrapInNonFreshElement(nodes, tagName) { - return [Html.nonFreshElement(tagName, {}, nodes)]; - } - - function convertUnderline(runHtml) { - var style = findStyle({type: "underline"}); - if (style) { - return Html.pathToNodes(style.to, runHtml); - } else { - return runHtml; + var stylePath = htmlPaths.empty; + if (run.styleId) { + var style = findStyle(run); + if (style) { + stylePath = style.to; + } else { + messages.push(unrecognisedStyleWarning("run", run)); + } } + paths.push(stylePath); + + paths.forEach(function(path) { + nodes = path.wrap.bind(path, nodes); + }); + + return nodes(); } - function convertRunProperty(result, elementType, defaultTagName) { + function findHtmlPathForRunProperty(elementType, defaultTagName) { var style = findStyle({type: elementType}); if (style) { - return Html.pathToNodes(style.to, result); + return style.to; + } else if (defaultTagName) { + return htmlPaths.element(defaultTagName, {}, {fresh: false}); } else { - return wrapInNonFreshElement(result, defaultTagName); + return htmlPaths.empty; } } diff --git a/lib/html-paths.js b/lib/html-paths.js index 04bc655..4021887 100644 --- a/lib/html-paths.js +++ b/lib/html-paths.js @@ -1,5 +1,7 @@ var _ = require("underscore"); +var html = require("./html"); + exports.topLevelElement = topLevelElement; exports.elements = elements; exports.element = element; @@ -9,15 +11,27 @@ function topLevelElement(tagName, attributes) { } function elements(elementStyles) { - return elementStyles.map(function(elementStyle) { + return new HtmlPath(elementStyles.map(function(elementStyle) { if (_.isString(elementStyle)) { return element(elementStyle); } else { return elementStyle; } - }); + })); +} + +function HtmlPath(elements) { + this._elements = elements; } +HtmlPath.prototype.wrap = function wrap(children) { + var result = children(); + for (var index = this._elements.length - 1; index >= 0; index--) { + result = this._elements[index].wrapNodes(result); + } + return result; +}; + function element(tagName, attributes, options) { options = options || {}; return new Element(tagName, attributes, options); @@ -44,4 +58,12 @@ Element.prototype.matchesElement = function(element) { return this.tagNames[element.tagName] && _.isEqual(this.attributes || {}, element.attributes || {}); }; +Element.prototype.wrap = function wrap(generateNodes) { + return this.wrapNodes(generateNodes()); +}; + +Element.prototype.wrapNodes = function wrapNodes(nodes) { + return [html.elementWithTag(this, nodes)]; +}; + exports.empty = elements([]); diff --git a/lib/html/index.js b/lib/html/index.js index edc8256..600d783 100644 --- a/lib/html/index.js +++ b/lib/html/index.js @@ -7,15 +7,6 @@ exports.selfClosingElement = ast.selfClosingElement; exports.text = ast.text; exports.forceWrite = ast.forceWrite; -function pathToNodes(path, children) { - var result = children; - for (var index = path.length - 1; index >= 0; index--) { - result = [ast.elementWithTag(path[index], result)]; - } - return result; -} - -exports.pathToNodes = pathToNodes; exports.simplify = require("./simplify"); function write(writer, nodes) { -- cgit v1.2.3 From baf9808eae3441de70a00cd043e10e0ebf06b7c0 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 7 Jul 2016 20:44:20 +0100 Subject: Add HTML path for ignoring elements --- lib/html-paths.js | 5 +++++ lib/style-reader.js | 28 +++++++++++++++++----------- test/mammoth.tests.js | 6 +++--- test/style-reader.tests.js | 4 ++++ 4 files changed, 29 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/html-paths.js b/lib/html-paths.js index 4021887..5a00cd8 100644 --- a/lib/html-paths.js +++ b/lib/html-paths.js @@ -67,3 +67,8 @@ Element.prototype.wrapNodes = function wrapNodes(nodes) { }; exports.empty = elements([]); +exports.ignore = { + wrap: function() { + return []; + } +}; diff --git a/lib/style-reader.js b/lib/style-reader.js index ab9a171..fe60930 100644 --- a/lib/style-reader.js +++ b/lib/style-reader.js @@ -177,16 +177,21 @@ function htmlPathRule() { return htmlPaths.element(tagName, attributes, options); }); - return lop.rules.then( - lop.rules.zeroOrMoreWithSeparator( - styleElementRule, - lop.rules.sequence( - whitespaceRule, - lop.rules.tokenOfType("gt"), - whitespaceRule - ) - ), - htmlPaths.elements + return lop.rules.firstOf("html path", + lop.rules.then(lop.rules.tokenOfType("bang"), function() { + return htmlPaths.ignore; + }), + lop.rules.then( + lop.rules.zeroOrMoreWithSeparator( + styleElementRule, + lop.rules.sequence( + whitespaceRule, + lop.rules.tokenOfType("gt"), + whitespaceRule + ) + ), + htmlPaths.elements + ) ); } @@ -234,7 +239,8 @@ function tokenise(string) { {name: "close-square-bracket", regex: /\]/}, {name: "string", regex: /'([^']*)'/}, {name: "integer", regex: /([0-9]+)/}, - {name: "choice", regex: /\|/} + {name: "choice", regex: /\|/}, + {name: "bang", regex: /(!)/} ]); return tokeniser.tokenise(string); } diff --git a/test/mammoth.tests.js b/test/mammoth.tests.js index 1a82014..12bee96 100644 --- a/test/mammoth.tests.js +++ b/test/mammoth.tests.js @@ -114,12 +114,12 @@ describe('mammoth', function() { test('warning if style mapping is not understood', function() { var docxPath = path.join(__dirname, "test-data/single-paragraph.docx"); var options = { - styleMap: "!!!!\np => h1" + styleMap: "????\np => h1" }; return mammoth.convertToHtml({path: docxPath}, options).then(function(result) { assert.equal("

Walking on imported air

", result.value); - var warning = "Did not understand this style mapping, so ignored it: !!!!\n" + - 'Error was at character number 1: Expected element type but got unrecognisedCharacter "!"'; + var warning = "Did not understand this style mapping, so ignored it: ????\n" + + 'Error was at character number 1: Expected element type but got unrecognisedCharacter "?"'; assert.deepEqual(result.messages, [results.warning(warning)]); }); }); diff --git a/test/style-reader.tests.js b/test/style-reader.tests.js index 7265e8f..46a9fa1 100644 --- a/test/style-reader.tests.js +++ b/test/style-reader.tests.js @@ -54,6 +54,10 @@ describe('styleReader.readHtmlPath', function() { assertHtmlPath("p:fresh", expected); }); + test('reads ignore element', function() { + assertHtmlPath("!", htmlPaths.ignore); + }); + function assertHtmlPath(input, expected) { assert.deepEqual(readHtmlPath(input), results.success(expected)); } -- cgit v1.2.3 From b0ff9fc6b25bbe74aaa7245d24a6476b4f5f1ec6 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 7 Jul 2016 20:49:22 +0100 Subject: Test that comments are ignored by default --- lib/documents.js | 6 +++--- test/document-to-html.tests.js | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/documents.js b/lib/documents.js index d4a28af..9f01d88 100644 --- a/lib/documents.js +++ b/lib/documents.js @@ -181,9 +181,9 @@ function BookmarkStart(options) { }; } -exports.Document = Document; -exports.Paragraph = Paragraph; -exports.Run = Run; +exports.document = exports.Document = Document; +exports.paragraph = exports.Paragraph = Paragraph; +exports.run = exports.Run = Run; exports.Text = Text; exports.Tab = Tab; exports.Hyperlink = Hyperlink; diff --git a/test/document-to-html.tests.js b/test/document-to-html.tests.js index 0b4f905..a4678ec 100644 --- a/test/document-to-html.tests.js +++ b/test/document-to-html.tests.js @@ -486,6 +486,26 @@ describe('DocumentConverter', function() { }); }); + test('comments are ignored by default', function() { + var reference = documents.commentReference("4"); + var comment = documents.comment({ + commentId: "4", + body: [paragraphOfText("Who's there?")] + }); + var document = documents.document([ + documents.paragraph([ + runOfText("Knock knock"), + documents.run([reference]) + ]) + ], {comments: [comment]}); + + var converter = new DocumentConverter({}); + return converter.convertToHtml(document).then(function(result) { + assert.equal(result.value, '

Knock knock

'); + }); + + }); + test('images are written with data URIs', function() { var imageBuffer = new Buffer("Not an image at all!"); var image = new documents.Image({ -- cgit v1.2.3 From c0a14c171f5bdfda6cb76b1589a43ada3b5768ce Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 7 Jul 2016 21:27:53 +0100 Subject: Add conversion of comments --- lib/document-matchers.js | 2 +- lib/document-to-html.js | 88 +++++++++++++++++++++++++++++++++++++----- lib/documents.js | 3 +- test/document-to-html.tests.js | 32 ++++++++++++++- 4 files changed, 112 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/document-matchers.js b/lib/document-matchers.js index 60bc65f..bdc213c 100644 --- a/lib/document-matchers.js +++ b/lib/document-matchers.js @@ -4,7 +4,7 @@ exports.bold = new Matcher("bold"); exports.italic = new Matcher("italic"); exports.underline = new Matcher("underline"); exports.strikethrough = new Matcher("strikethrough"); -exports.commentReference = new Matcher("comment-reference"); +exports.commentReference = new Matcher("commentReference"); function paragraph(options) { diff --git a/lib/document-to-html.js b/lib/document-to-html.js index 2b7815a..1b3a3df 100644 --- a/lib/document-to-html.js +++ b/lib/document-to-html.js @@ -13,18 +13,25 @@ exports.DocumentConverter = DocumentConverter; function DocumentConverter(options) { return { - convertToHtml: function() { - var conversion = new DocumentConversion(options); - return conversion.convertToHtml.apply(this, arguments); + convertToHtml: function(element) { + var comments = _.indexBy( + element.type === documents.types.document ? element.comments : [], + "commentId" + ); + var conversion = new DocumentConversion(options, comments); + return conversion.convertToHtml(element); } }; } -function DocumentConversion(options) { +function DocumentConversion(options, comments) { var noteNumber = 1; var noteReferences = []; + var referencedComments = []; + var commentCountByInitials = {}; + options = _.extend({ignoreEmptyParagraphs: true}, options); var idPrefix = options.idPrefix === undefined ? "" : options.idPrefix; @@ -154,9 +161,9 @@ function DocumentConversion(options) { } function findHtmlPathForRunProperty(elementType, defaultTagName) { - var style = findStyle({type: elementType}); - if (style) { - return style.to; + var path = findHtmlPath({type: elementType}); + if (path) { + return path; } else if (defaultTagName) { return htmlPaths.element(defaultTagName, {}, {fresh: false}); } else { @@ -164,6 +171,11 @@ function DocumentConversion(options) { } } + function findHtmlPath(element, defaultPath) { + var style = findStyle(element); + return style ? style.to : defaultPath; + } + function findStyle(element) { for (var i = 0; i < styleMap.length; i++) { if (styleMap[i].from.matches(element)) { @@ -190,11 +202,19 @@ function DocumentConversion(options) { } function noteHtmlId(note) { - return htmlId(note.noteType + "-" + note.noteId); + return referentHtmlId(note.noteType, note.noteId); } function noteRefHtmlId(note) { - return htmlId(note.noteType + "-ref-" + note.noteId); + return referenceHtmlId(note.noteType, note.noteId); + } + + function referentHtmlId(referenceType, referenceId) { + return htmlId(referenceType + "-" + referenceId); + } + + function referenceHtmlId(referenceType, referenceId) { + return htmlId(referenceType + "-ref-" + referenceId); } function htmlId(suffix) { @@ -224,6 +244,49 @@ function DocumentConversion(options) { ]; } + function convertCommentReference(reference, messages) { + return findHtmlPath(reference, htmlPaths.ignore).wrap(function() { + var comment = comments[reference.commentId]; + if (commentCountByInitials[comment.authorInitials] === undefined) { + commentCountByInitials[comment.authorInitials] = 0; + } + var count = ++commentCountByInitials[comment.authorInitials]; + var label = "[" + comment.authorInitials + count + "]"; + referencedComments.push({label: label, comment: comment}); + // TODO: remove duplication with note references + return [ + Html.freshElement("a", { + href: "#" + referentHtmlId("comment", reference.commentId), + id: referenceHtmlId("comment", reference.commentId) + }, [Html.text(label)]) + ]; + }); + } + + function convertComment(referencedComment, messages) { + // TODO: remove duplication with note references + + var label = referencedComment.label; + var comment = referencedComment.comment; + var body = convertElements(comment.body).concat([ + Html.nonFreshElement("p", {}, [ + Html.text(" "), + Html.freshElement("a", {"href": "#" + referenceHtmlId("comment", comment.commentId)}, [ + Html.text("↑") + ]) + ]) + ]); + + return [ + Html.freshElement( + "dt", + {"id": referentHtmlId("comment", comment.commentId)}, + [Html.text("Comment " + label)] + ), + Html.freshElement("dd", {}, body) + ]; + } + function wrapChildrenInFreshElement(element, wrapElementName, messages) { var children = convertElements(element.children, messages); return [ @@ -239,7 +302,10 @@ function DocumentConversion(options) { }); var notesNodes = convertElements(notes, messages); return children.concat([ - Html.freshElement("ol", {}, notesNodes) + Html.freshElement("ol", {}, notesNodes), + Html.freshElement("dl", {}, flatMap(referencedComments, function(referencedComment) { + return convertComment(referencedComment, messages); + })) ]); }, "paragraph": convertParagraph, @@ -281,6 +347,8 @@ function DocumentConversion(options) { return Html.freshElement("li", {id: noteHtmlId(element)}, body); }, + "commentReference": convertCommentReference, + "comment": convertComment, "image": deferredConversion(recoveringConvertImage(options.convertImage || defaultConvertImage)), "table": convertTable, "tableRow": convertTableRow, diff --git a/lib/documents.js b/lib/documents.js index 9f01d88..428dc09 100644 --- a/lib/documents.js +++ b/lib/documents.js @@ -24,7 +24,8 @@ function Document(children, options) { return { type: types.document, children: children, - notes: options.notes || new Notes({}) + notes: options.notes || new Notes({}), + comments: options.comments || [] }; } diff --git a/test/document-to-html.tests.js b/test/document-to-html.tests.js index 436014f..af08050 100644 --- a/test/document-to-html.tests.js +++ b/test/document-to-html.tests.js @@ -486,7 +486,7 @@ test('footnotes are included after the main body', function() { }); test('comments are ignored by default', function() { - var reference = documents.commentReference("4"); + var reference = documents.commentReference({commentId: "4"}); var comment = documents.comment({ commentId: "4", body: [paragraphOfText("Who's there?")] @@ -503,7 +503,37 @@ test('comments are ignored by default', function() { assert.equal(result.value, '

Knock knock

'); assert.deepEqual(result.messages, []); }); +}); +test('comment references are linked to comment after main body', function() { + var reference = documents.commentReference({commentId: "4"}); + var comment = documents.comment({ + commentId: "4", + body: [paragraphOfText("Who's there?")], + authorName: "The Piemaker", + authorInitials: "TP" + }); + var document = documents.document([ + documents.paragraph([ + runOfText("Knock knock"), + documents.run([reference]) + ]) + ], {comments: [comment]}); + + var converter = new DocumentConverter({ + idPrefix: "doc-42-", + styleMap: [ + {from: documentMatchers.commentReference, to: htmlPaths.element("sup")} + ] + }); + return converter.convertToHtml(document).then(function(result) { + var expectedHtml = ( + '

Knock knock[TP1]

' + + '
Comment [TP1]

Who\'s there? ↑

' + ); + assert.equal(result.value, expectedHtml); + assert.deepEqual(result.messages, []); + }); }); test('images are written with data URIs', function() { -- cgit v1.2.3 From bf37d9d321dad98f3fa2ca99a5235d07cb66892e Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 7 Jul 2016 21:37:21 +0100 Subject: Wire up comment conversion code --- lib/docx/body-reader.js | 2 +- lib/docx/document-xml-reader.js | 5 ++++- lib/docx/docx-reader.js | 19 +++++++++++++++---- lib/options-reader.js | 2 ++ test/mammoth.tests.js | 19 +++++++++++++++++++ test/test-data/comments.docx | Bin 0 -> 15526 bytes 6 files changed, 41 insertions(+), 6 deletions(-) create mode 100755 test/test-data/comments.docx (limited to 'lib') diff --git a/lib/docx/body-reader.js b/lib/docx/body-reader.js index b514363..7b3120d 100644 --- a/lib/docx/body-reader.js +++ b/lib/docx/body-reader.js @@ -379,13 +379,13 @@ var ignoreElements = { "office-word:wrap": true, "v:shadow": true, "v:shapetype": true, + "w:annotationRef": true, "w:bookmarkEnd": true, "w:sectPr": true, "w:proofErr": true, "w:lastRenderedPageBreak": true, "w:commentRangeStart": true, "w:commentRangeEnd": true, - "w:commentReference": true, "w:del": true, "w:footnoteRef": true, "w:endnoteRef": true, diff --git a/lib/docx/document-xml-reader.js b/lib/docx/document-xml-reader.js index 12ca19e..bb912e4 100644 --- a/lib/docx/document-xml-reader.js +++ b/lib/docx/document-xml-reader.js @@ -12,7 +12,10 @@ function DocumentXmlReader(options) { var result = bodyReader.readXmlElements(body.children) .map(function(children) { - return new documents.Document(children, {notes: options.notes}); + return new documents.Document(children, { + notes: options.notes, + comments: options.comments + }); }); return new Result(result.value, result.messages); } diff --git a/lib/docx/docx-reader.js b/lib/docx/docx-reader.js index 6ad63ab..57830d7 100644 --- a/lib/docx/docx-reader.js +++ b/lib/docx/docx-reader.js @@ -14,6 +14,7 @@ var contentTypesReader = require("./content-types-reader"); var numberingXml = require("./numbering-xml"); var stylesReader = require("./styles-reader"); var notesReader = require("./notes-reader"); +var commentsReader = require("./comments-reader"); var Files = require("./files").Files; @@ -40,6 +41,13 @@ function read(docxFile, input) { } else { return new Result([]); } + }), + comments: readXmlFileWithBody("comments", result, function(bodyReader, xml) { + if (xml) { + return commentsReader.createCommentsReader(bodyReader)(xml); + } else { + return new Result([]); + } }) }; }).also(function(result) { @@ -53,11 +61,14 @@ function read(docxFile, input) { }).then(function(result) { return readXmlFileWithBody("document", result, function(bodyReader, xml) { return result.notes.flatMap(function(notes) { - var reader = new DocumentXmlReader({ - bodyReader: bodyReader, - notes: notes + return result.comments.flatMap(function(comments) { + var reader = new DocumentXmlReader({ + bodyReader: bodyReader, + notes: notes, + comments: comments + }); + return reader.convertXmlToDocument(xml); }); - return reader.convertXmlToDocument(xml); }); }); }); diff --git a/lib/options-reader.js b/lib/options-reader.js index 19934d8..3765fd3 100644 --- a/lib/options-reader.js +++ b/lib/options-reader.js @@ -26,6 +26,8 @@ var standardOptions = exports._standardOptions = { "r[style-name='footnote reference'] =>", "p[style-name='endnote text'] => p", "r[style-name='endnote reference'] =>", + "p[style-name='annotation text'] => p", + "r[style-name='annotation reference'] =>", // LibreOffice "p[style-name='Footnote'] => p", diff --git a/test/mammoth.tests.js b/test/mammoth.tests.js index 12bee96..0515b2a 100644 --- a/test/mammoth.tests.js +++ b/test/mammoth.tests.js @@ -269,6 +269,25 @@ describe('mammoth', function() { }); }); + test('when style mapping is defined for comment references then comments are included', function() { + var docxPath = path.join(__dirname, "test-data/comments.docx"); + var options = { + idPrefix: "doc-42-", + styleMap: "comment-reference => sup" + }; + return mammoth.convertToHtml({path: docxPath}, options).then(function(result) { + var expectedOutput = ( + '

Ouch' + + '[MW1].' + + '[MW2]

' + + '
Comment [MW1]

A tachyon walks into a bar. ↑

' + + '
Comment [MW2]

Fin. ↑

' + ); + assert.equal(result.value, expectedOutput); + assert.deepEqual(result.messages, []); + }); + }); + test('textboxes are read', function() { var docxPath = path.join(__dirname, "test-data/text-box.docx"); return mammoth.convertToHtml({path: docxPath}).then(function(result) { diff --git a/test/test-data/comments.docx b/test/test-data/comments.docx new file mode 100755 index 0000000..32cdae1 Binary files /dev/null and b/test/test-data/comments.docx differ -- cgit v1.2.3 From 30d04c156a26f40cb7f4cf72d66c3f29bb87ff60 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 7 Jul 2016 22:27:20 +0100 Subject: Use overall comment count for comment numbering This matches the behaviour in Word more closely. --- lib/document-to-html.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/document-to-html.js b/lib/document-to-html.js index 1b3a3df..eb7c8e1 100644 --- a/lib/document-to-html.js +++ b/lib/document-to-html.js @@ -30,7 +30,6 @@ function DocumentConversion(options, comments) { var noteReferences = []; var referencedComments = []; - var commentCountByInitials = {}; options = _.extend({ignoreEmptyParagraphs: true}, options); var idPrefix = options.idPrefix === undefined ? "" : options.idPrefix; @@ -247,10 +246,7 @@ function DocumentConversion(options, comments) { function convertCommentReference(reference, messages) { return findHtmlPath(reference, htmlPaths.ignore).wrap(function() { var comment = comments[reference.commentId]; - if (commentCountByInitials[comment.authorInitials] === undefined) { - commentCountByInitials[comment.authorInitials] = 0; - } - var count = ++commentCountByInitials[comment.authorInitials]; + var count = referencedComments.length + 1; var label = "[" + comment.authorInitials + count + "]"; referencedComments.push({label: label, comment: comment}); // TODO: remove duplication with note references -- cgit v1.2.3 From ade35ac659f20fc27bb4c87d8511789cc7d05c07 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Thu, 7 Jul 2016 22:32:35 +0100 Subject: Handle comments without initials --- lib/document-to-html.js | 6 +++++- test/document-to-html.tests.js | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/document-to-html.js b/lib/document-to-html.js index eb7c8e1..bc61e34 100644 --- a/lib/document-to-html.js +++ b/lib/document-to-html.js @@ -247,7 +247,7 @@ function DocumentConversion(options, comments) { return findHtmlPath(reference, htmlPaths.ignore).wrap(function() { var comment = comments[reference.commentId]; var count = referencedComments.length + 1; - var label = "[" + comment.authorInitials + count + "]"; + var label = "[" + commentAuthorLabel(comment) + count + "]"; referencedComments.push({label: label, comment: comment}); // TODO: remove duplication with note references return [ @@ -393,3 +393,7 @@ function walkHtml(nodes, callback) { } }); } + +var commentAuthorLabel = exports.commentAuthorLabel = function commentAuthorLabel(comment) { + return comment.authorInitials || ""; +}; diff --git a/test/document-to-html.tests.js b/test/document-to-html.tests.js index af08050..cc8aff5 100644 --- a/test/document-to-html.tests.js +++ b/test/document-to-html.tests.js @@ -2,7 +2,9 @@ var assert = require("assert"); var promises = require("../lib/promises"); var documents = require("../lib/documents"); -var DocumentConverter = require("../lib/document-to-html").DocumentConverter; +var documentToHtml = require("../lib/document-to-html"); +var DocumentConverter = documentToHtml.DocumentConverter; +var commentAuthorLabel = documentToHtml.commentAuthorLabel; var test = require("./test")(module); var htmlPaths = require("../lib/html-paths"); var xml = require("../lib/xml"); @@ -612,3 +614,13 @@ function runOfText(text, properties) { var textElement = new documents.Text(text); return new documents.Run([textElement], properties); } + +test('when initials are not blank then comment author label is initials', function() { + assert.equal(commentAuthorLabel({authorInitials: "TP"}), "TP") +}); + +test('when initials are blank then comment author label is blank', function() { + assert.equal(commentAuthorLabel({authorInitials: ""}), "") + assert.equal(commentAuthorLabel({authorInitials: undefined}), "") + assert.equal(commentAuthorLabel({authorInitials: null}), "") +}); -- cgit v1.2.3 From c42714e5f4b643de27b106a084601e39b7e5d04c Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 28 Aug 2016 19:44:42 +0100 Subject: Throw more informative error when word/document.xml cannot be found --- NEWS | 4 ++++ lib/docx/docx-reader.js | 20 ++++++++++++-------- test/mammoth.tests.js | 10 ++++++++++ test/test-data/empty.zip | Bin 0 -> 22 bytes 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 test/test-data/empty.zip (limited to 'lib') diff --git a/NEWS b/NEWS index 07e8ee6..3c3a715 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +# 1.2.0 + +* Throw more informative error when word/document.xml cannot be found. + # 1.1.0 * Add support for comments. diff --git a/lib/docx/docx-reader.js b/lib/docx/docx-reader.js index 57830d7..0c7d57f 100644 --- a/lib/docx/docx-reader.js +++ b/lib/docx/docx-reader.js @@ -60,16 +60,20 @@ function read(docxFile, input) { }; }).then(function(result) { return readXmlFileWithBody("document", result, function(bodyReader, xml) { - return result.notes.flatMap(function(notes) { - return result.comments.flatMap(function(comments) { - var reader = new DocumentXmlReader({ - bodyReader: bodyReader, - notes: notes, - comments: comments + if (xml) { + return result.notes.flatMap(function(notes) { + return result.comments.flatMap(function(comments) { + var reader = new DocumentXmlReader({ + bodyReader: bodyReader, + notes: notes, + comments: comments + }); + return reader.convertXmlToDocument(xml); }); - return reader.convertXmlToDocument(xml); }); - }); + } else { + throw new Error("Could not find word/document.xml in ZIP file. Are you sure this is a valid .docx file?"); + } }); }); } diff --git a/test/mammoth.tests.js b/test/mammoth.tests.js index f4d445b..bf3d2fd 100644 --- a/test/mammoth.tests.js +++ b/test/mammoth.tests.js @@ -368,3 +368,13 @@ test('extractRawText can use .docx files represented by a Buffer', function() { assert.deepEqual(result.messages, []); }); }); + + +test('should throw error if file is not a valid docx document', function() { + var docxPath = path.join(__dirname, "test-data/empty.zip"); + return mammoth.convertToHtml({path: docxPath}).then(function(result) { + assert.ok(false, "Expected error"); + }, function(error) { + assert.equal(error.message, "Could not find word/document.xml in ZIP file. Are you sure this is a valid .docx file?"); + }); +}); diff --git a/test/test-data/empty.zip b/test/test-data/empty.zip new file mode 100644 index 0000000..15cb0ec Binary files /dev/null and b/test/test-data/empty.zip differ -- cgit v1.2.3