UNPKG

mammoth-style

Version:

Convert Word documents (.docx files) to HTML (with style)

1,584 lines (1,386 loc) 1.05 MB
// Module: base64-js@1.3.0 // License: MIT // // Module: bluebird@3.4.7 // License: MIT // // Module: buffer@4.9.1 // License: MIT // // Module: clone@1.0.4 // License: MIT // // Module: core-util-is@1.0.2 // License: MIT // // Module: events@1.1.1 // License: MIT // // Module: ieee754@1.1.13 // License: BSD-3-Clause // // Module: inherits@2.0.3 // License: ISC // // Module: isarray@1.0.0 // License: MIT // // Module: jszip@2.5.0 // License: MIT or GPLv3 // // Module: lodash@3.10.1 // License: MIT // // Module: lop@0.4.0 // License: BSD // // Module: mammoth-style@0.0.3 // License: BSD-2-Clause // // Module: option@0.2.4 // License: BSD-2-Clause // // Module: pako@0.2.9 // License: MIT // // Module: path-browserify@0.0.1 // License: MIT // // Module: process-nextick-args@2.0.0 // License: MIT // // Module: process@0.11.10 // License: MIT // // Module: readable-stream@2.3.6 // License: MIT // // Module: safe-buffer@5.1.2 // License: MIT // // Module: sax@1.1.6 // License: ISC // // Module: stream-browserify@2.0.2 // License: MIT // // Module: string_decoder@0.10.31 // License: MIT // // Module: string_decoder@1.1.1 // License: MIT // // Module: timers-browserify@1.4.2 // License: MIT // // Module: underscore@1.4.4 // License: MIT // // Module: underscore@1.6.0 // License: MIT // // Module: util-deprecate@1.0.2 // License: MIT // // Module: util@0.10.4 // License: MIT // // Module: xmlbuilder@2.6.5 // License: MIT // (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.mammoth = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ var promises = require("../../lib/promises"); exports.Files = Files; function Files() { function read(uri) { return promises.reject(new Error("could not open external image: '" + uri + "'\ncannot open linked files from a web browser")); } return { read: read }; } },{"../../lib/promises":24}],2:[function(require,module,exports){ var promises = require("../lib/promises"); var zipfile = require("../lib/zipfile"); exports.openZip = openZip; function openZip(options) { if (options.arrayBuffer) { return promises.resolve(zipfile.openArrayBuffer(options.arrayBuffer)); } else { return promises.reject(new Error("Could not find file in options")); } } },{"../lib/promises":24,"../lib/zipfile":36}],3:[function(require,module,exports){ exports.paragraph = paragraph; exports.run = run; exports.bold = new Matcher("bold"); exports.italic = new Matcher("italic"); exports.underline = new Matcher("underline"); exports.strikethrough = new Matcher("strikethrough"); exports.commentReference = new Matcher("commentReference"); function paragraph(options) { return new Matcher("paragraph", options); } function run(options) { return new Matcher("run", options); } function Matcher(elementType, options) { options = options || {}; this._elementType = elementType; this._styleId = options.styleId; this._styleName = options.styleName; if (options.list) { this._listIndex = options.list.levelIndex; this._listIsOrdered = options.list.isOrdered; } } Matcher.prototype.matches = function(element) { return element.type === this._elementType && (this._styleId === undefined || element.styleId === this._styleId) && (this._styleName === undefined || (element.styleName && element.styleName.toUpperCase() === this._styleName.toUpperCase())) && (this._listIndex === undefined || isList(element, this._listIndex, this._listIsOrdered)); }; function isList(element, levelIndex, isOrdered) { return element.numbering && element.numbering.level == levelIndex && element.numbering.isOrdered == isOrdered; } },{}],4:[function(require,module,exports){ var _ = require("underscore"); var promises = require("./promises"); var documents = require("./documents"); var htmlPaths = require("./html-paths"); var results = require("./results"); var images = require("./images"); var Html = require("./html"); var writers = require("./writers"); var clone = require('clone'); exports.DocumentConverter = DocumentConverter; function DocumentConverter(options) { return { 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, comments) { var noteNumber = 1; var noteReferences = []; var referencedComments = []; options = _.extend({ignoreEmptyParagraphs: true}, options); var idPrefix = options.idPrefix === undefined ? "" : options.idPrefix; var defaultParagraphStyle = htmlPaths.topLevelElement("p"); var styleMap = options.styleMap || []; function convertToHtml(document) { var messages = []; var html = elementToHtml(document, messages); var deferredNodes = []; walkHtml(html, function(node) { if (node.type === "deferred") { deferredNodes.push(node); } }); var deferredValues = {}; return promises.mapSeries(deferredNodes, function(deferred) { return deferred.value().then(function(value) { deferredValues[deferred.id] = value; }); }).then(function() { function replaceDeferred(nodes) { return flatMap(nodes, function(node) { if (node.type === "deferred") { return deferredValues[node.id]; } else if (node.children) { return [ _.extend({}, node, { children: replaceDeferred(node.children) }) ]; } else { return [node]; } }); } var writer = writers.writer({ prettyPrint: options.prettyPrint, outputFormat: options.outputFormat }); Html.write(writer, Html.simplify(replaceDeferred(html))); return new results.Result(writer.asString(), messages); }); } function convertElements(elements, messages) { return flatMap(elements, function(element) { return elementToHtml(element, messages); }); } function elementToHtml(element, messages) { var handler = elementConverters[element.type]; if (handler) { return handler(element, messages); } else { return []; } } function convertParagraph(element, messages) { return htmlPathForParagraph(element, messages).wrap(function() { var content = convertElements(element.children, messages); if (options.ignoreEmptyParagraphs) { return content; } else { return [Html.forceWrite].concat(content); } }); } function htmlPathForParagraph(element, messages) { var style = findStyle(element); var listTypes = { "decimal" : "1", "upperRoman" : "I", "lowerRoman" : "i", "upperLetter" : "A", "lowerLetter" : "a" } if (style) { var style_clone = clone(style); for (var i in style_clone.to._elements) { if (style_clone.to._elements[i].tagName == 'ol') { if (element.numbering && element.numbering.format) { style_clone.to._elements[i].attributes['type'] = listTypes[element.numbering.format]; } } } return style_clone.to; } else { if (element.styleId) { messages.push(unrecognisedStyleWarning("paragraph", element)); } return defaultParagraphStyle; } } function convertRun(run, messages) { var nodes = function() { return convertElements(run.children, messages); }; var paths = []; if (run.isStrikethrough) { paths.push(findHtmlPathForRunProperty("strikethrough", "s")); } if (run.isUnderline) { paths.push(findHtmlPathForRunProperty("underline")); } if (run.verticalAlignment === documents.verticalAlignment.subscript) { paths.push(htmlPaths.element("sub", {}, {fresh: false})); } if (run.verticalAlignment === documents.verticalAlignment.superscript) { paths.push(htmlPaths.element("sup", {}, {fresh: false})); } if (run.isItalic) { paths.push(findHtmlPathForRunProperty("italic", "em")); } if (run.isBold) { paths.push(findHtmlPathForRunProperty("bold", "strong")); } if (run.color) { paths.push(htmlPaths.element('font', {color : run.color}, {fresh: false})); } 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 findHtmlPathForRunProperty(elementType, defaultTagName) { var path = findHtmlPath({type: elementType}); if (path) { return path; } else if (defaultTagName) { return htmlPaths.element(defaultTagName, {}, {fresh: false}); } else { return htmlPaths.empty; } } 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)) { return styleMap[i]; } } } var defaultConvertImage = images.imgElement(function(element) { return element.read("base64").then(function(imageBuffer) { return { src: "data:" + element.contentType + ";base64," + imageBuffer }; }); }); function recoveringConvertImage(convertImage) { return function(image, messages) { return convertImage(image, messages).caught(function(error) { messages.push(results.warning(error.message)); return []; }); }; } function noteHtmlId(note) { return referentHtmlId(note.noteType, note.noteId); } function noteRefHtmlId(note) { 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) { return idPrefix + suffix; } function convertTable(element, messages) { return wrapChildrenInFreshElement(element, "table", messages); } function convertTableRow(element, messages) { return wrapChildrenInFreshElement(element, "tr", messages); } function convertTableCell(element, messages) { var children = convertElements(element.children, messages); var attributes = {}; if (element.colSpan !== 1) { attributes.colspan = element.colSpan.toString(); } if (element.rowSpan !== 1) { attributes.rowspan = element.rowSpan.toString(); } return [ Html.freshElement("td", attributes, [Html.forceWrite].concat(children)) ]; } function convertCommentReference(reference, messages) { return findHtmlPath(reference, htmlPaths.ignore).wrap(function() { var comment = comments[reference.commentId]; var count = referencedComments.length + 1; var label = "[" + commentAuthorLabel(comment) + 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 [ Html.freshElement(wrapElementName, {}, [Html.forceWrite].concat(children)) ]; } var elementConverters = { "document": function(document, messages) { var children = convertElements(document.children, messages); var notes = noteReferences.map(function(noteReference) { return document.notes.resolve(noteReference); }); var notesNodes = convertElements(notes, messages); return children.concat([ Html.freshElement("ol", {}, notesNodes), Html.freshElement("dl", {}, flatMap(referencedComments, function(referencedComment) { return convertComment(referencedComment, messages); })) ]); }, "paragraph": convertParagraph, "run": convertRun, "text": function(element, messages) { return [Html.text(element.value)]; }, "tab": function(element, messages) { return [Html.text("\t")]; }, "hyperlink": function(element, messages) { var href = element.anchor ? "#" + htmlId(element.anchor) : element.href; var children = convertElements(element.children, messages); return [Html.freshElement("a", {href: href}, children)]; }, "bookmarkStart": function(element, messages) { var anchor = Html.freshElement("a", { id: htmlId(element.name) }, [Html.forceWrite]); return [anchor]; }, "noteReference": function(element, messages) { noteReferences.push(element); var anchor = Html.freshElement("a", { href: "#" + noteHtmlId(element), id: noteRefHtmlId(element) }, [Html.text("[" + (noteNumber++) + "]")]); return [Html.freshElement("sup", {}, [anchor])]; }, "note": function(element, messages) { var children = convertElements(element.body, messages); var backLink = Html.elementWithTag(htmlPaths.element("p", {}, {fresh: false}), [ Html.text(" "), Html.freshElement("a", {href: "#" + noteRefHtmlId(element)}, [Html.text("↑")]) ]); var body = children.concat([backLink]); return Html.freshElement("li", {id: noteHtmlId(element)}, body); }, "commentReference": convertCommentReference, "comment": convertComment, "image": deferredConversion(recoveringConvertImage(options.convertImage || defaultConvertImage)), "table": convertTable, "tableRow": convertTableRow, "tableCell": convertTableCell, "lineBreak": function(element, messages) { return [Html.selfClosingElement("br")]; } }; return { convertToHtml: convertToHtml }; } var deferredId = 1; function deferredConversion(func) { return function(element, messages) { return [ { type: "deferred", id: deferredId++, value: function() { return func(element, messages); } } ]; }; } function unrecognisedStyleWarning(type, element) { return results.warning( "Unrecognised " + type + " style: '" + element.styleName + "'" + " (Style ID: " + element.styleId + ")" ); } function flatMap(values, func) { return _.flatten(values.map(func), true); } function walkHtml(nodes, callback) { nodes.forEach(function(node) { callback(node); if (node.children) { walkHtml(node.children, callback); } }); } var commentAuthorLabel = exports.commentAuthorLabel = function commentAuthorLabel(comment) { return comment.authorInitials || ""; }; },{"./documents":5,"./html":19,"./html-paths":17,"./images":21,"./promises":24,"./results":25,"./writers":30,"clone":75,"underscore":215}],5:[function(require,module,exports){ var _ = require("underscore"); var types = exports.types = { document: "document", paragraph: "paragraph", run: "run", text: "text", tab: "tab", hyperlink: "hyperlink", noteReference: "noteReference", image: "image", note: "note", commentReference: "commentReference", comment: "comment", table: "table", tableRow: "tableRow", tableCell: "tableCell", lineBreak: "lineBreak", bookmarkStart: "bookmarkStart" }; function Document(children, options) { options = options || {}; return { type: types.document, children: children, notes: options.notes || new Notes({}), comments: options.comments || [] }; } function Paragraph(children, properties) { properties = properties || {}; return { type: types.paragraph, children: children, styleId: properties.styleId || null, styleName: properties.styleName || null, numbering: properties.numbering || null, alignment: properties.alignment || null }; } function Run(children, properties) { properties = properties || {}; return { type: types.run, children: children, styleId: properties.styleId || null, styleName: properties.styleName || null, isBold: properties.isBold, isUnderline: properties.isUnderline, isItalic: properties.isItalic, isStrikethrough: properties.isStrikethrough, verticalAlignment: properties.verticalAlignment || verticalAlignment.baseline, color: properties.color }; } var verticalAlignment = { baseline: "baseline", superscript: "superscript", subscript: "subscript" }; function Text(value) { return { type: types.text, value: value }; } function Tab() { return { type: types.tab }; } function Hyperlink(children, options) { return { type: types.hyperlink, children: children, href: options.href, anchor: options.anchor }; } function NoteReference(options) { return { type: types.noteReference, noteType: options.noteType, noteId: options.noteId }; } function Notes(notes) { this._notes = _.indexBy(notes, function(note) { return noteKey(note.noteType, note.noteId); }); } Notes.prototype.resolve = function(reference) { return this.findNoteByKey(noteKey(reference.noteType, reference.noteId)); }; Notes.prototype.findNoteByKey = function(key) { return this._notes[key] || null; }; function Note(options) { return { type: types.note, noteType: options.noteType, noteId: options.noteId, body: options.body }; } function commentReference(options) { return { type: types.commentReference, commentId: options.commentId }; } function comment(options) { return { type: types.comment, commentId: options.commentId, body: options.body, authorName: options.authorName, authorInitials: options.authorInitials }; } function noteKey(noteType, id) { return noteType + "-" + id; } function Image(options) { return { type: types.image, read: options.readImage, altText: options.altText, contentType: options.contentType }; } function Table(children) { return { type: types.table, children: children }; } function TableRow(children) { return { type: types.tableRow, children: children }; } function TableCell(children, options) { options = options || {}; return { type: types.tableCell, children: children, colSpan: options.colSpan == null ? 1 : options.colSpan, rowSpan: options.rowSpan == null ? 1 : options.rowSpan }; } function LineBreak() { return { type: types.lineBreak }; } function BookmarkStart(options) { return { type: types.bookmarkStart, name: options.name }; } exports.document = exports.Document = Document; exports.paragraph = exports.Paragraph = Paragraph; exports.run = exports.Run = Run; exports.Text = Text; exports.Tab = Tab; exports.Hyperlink = Hyperlink; exports.noteReference = exports.NoteReference = NoteReference; exports.Notes = Notes; exports.Note = Note; exports.commentReference = commentReference; exports.comment = comment; exports.Image = Image; exports.Table = Table; exports.TableRow = TableRow; exports.TableCell = TableCell; exports.LineBreak = LineBreak; exports.BookmarkStart = BookmarkStart; exports.verticalAlignment = verticalAlignment; },{"underscore":215}],6:[function(require,module,exports){ exports.BodyReader = BodyReader; var _ = require("underscore"); var documents = require("../documents"); var Result = require("../results").Result; var warning = require("../results").warning; function BodyReader(options) { var relationships = options.relationships; var contentTypes = options.contentTypes; var docxFile = options.docxFile; var files = options.files; var numbering = options.numbering; var styles = options.styles; function readXmlElements(elements) { var results = elements.map(readXmlElement); return combineResults(results); } function readXmlElement(element) { if (element.type === "element") { var handler = xmlElementReaders[element.name]; if (handler) { return handler(element); } else if (!Object.prototype.hasOwnProperty.call(ignoreElements, element.name)) { var message = warning("An unrecognised element was ignored: " + element.name); return emptyResultWithMessages([message]); } } return emptyResult(); } function readRunProperties(element) { var properties = { type: "runProperties" }; var verticalAlignmentElement = element.first("w:vertAlign"); if (verticalAlignmentElement) { properties.verticalAlignment = verticalAlignmentElement.attributes["w:val"]; } properties.isBold = !!element.first("w:b"); properties.isUnderline = !!element.first("w:u"); properties.isItalic = !!element.first("w:i"); properties.isStrikethrough = !!element.first("w:strike"); properties.color = false; if (!!element.first("w:color")) { properties.color = '#' + element.first("w:color").attributes['w:val']; } return readRunStyle(element).map(function(style) { properties.styleId = style.styleId; properties.styleName = style.name; return properties; }); } function readParagraphStyle(element) { return readStyle(element, "w:pStyle", "Paragraph", styles.findParagraphStyleById); } function readRunStyle(element) { return readStyle(element, "w:rStyle", "Run", styles.findCharacterStyleById); } function readStyle(element, styleTagName, styleType, findStyleById) { var messages = []; var styleElement = element.first(styleTagName); var styleId = null; var name = null; if (styleElement) { styleId = styleElement.attributes["w:val"]; if (styleId) { var style = findStyleById(styleId); if (style) { name = style.name; } else { messages.push(undefinedStyleWarning(styleType, styleId)); } } } return elementResultWithMessages({styleId: styleId, name: name}, messages); } function noteReferenceReader(noteType) { return function(element) { var noteId = element.attributes["w:id"]; return elementResult(new documents.NoteReference({ noteType: noteType, noteId: noteId })); }; } function readCommentReference(element) { return elementResult(documents.commentReference({ commentId: element.attributes["w:id"] })); } function readChildElements(element) { return readXmlElements(element.children); } var xmlElementReaders = { "w:p": function(element) { return readXmlElements(element.children) .map(function(children) { var properties = _.find(children, isParagraphProperties); return new documents.Paragraph( children.filter(negate(isParagraphProperties)), properties ); }) .insertExtra(); }, "w:pPr": function(element) { var properties = { type: "paragraphProperties" }; var alignElement = element.first("w:jc"); if (alignElement) { properties.alignment = alignElement.attributes["w:val"]; } properties.numbering = readNumberingProperties(element.firstOrEmpty("w:numPr")); return readParagraphStyle(element).map(function(style) { properties.styleId = style.styleId; properties.styleName = style.name; return properties; }); }, "w:r": function(element) { return readXmlElements(element.children) .map(function(children) { var properties = _.find(children, isRunProperties); return new documents.Run( children.filter(negate(isRunProperties)), properties ); }); }, "w:rPr": readRunProperties, "w:t": function(element) { return elementResult(new documents.Text(element.text())); }, "w:tab": function(element) { return elementResult(new documents.Tab()); }, "w:hyperlink": function(element) { var relationshipId = element.attributes["r:id"]; var anchor = element.attributes["w:anchor"]; return readXmlElements(element.children).map(function(children) { if (relationshipId) { var href = relationships[relationshipId].target; return new documents.Hyperlink(children, {href: href}); } else if (anchor) { return new documents.Hyperlink(children, {anchor: anchor}); } else { return children; } }); }, "w:tbl": readTable, "w:tr": readTableRow, "w:tc": readTableCell, "w:footnoteReference": noteReferenceReader("footnote"), "w:endnoteReference": noteReferenceReader("endnote"), "w:commentReference": readCommentReference, "w:br": function(element) { var breakType = element.attributes["w:type"]; if (breakType) { return emptyResultWithMessages([warning("Unsupported break type: " + breakType)]); } else { return elementResult(new documents.LineBreak()); } }, "w:bookmarkStart": function(element){ var name = element.attributes["w:name"]; if (name === "_GoBack") { return emptyResult(); } else { return elementResult(new documents.BookmarkStart({name: name})); } }, "mc:AlternateContent": function(element) { return readChildElements(element.first("mc:Fallback")); }, "w:sdt": function(element) { return readXmlElements(element.firstOrEmpty("w:sdtContent").children); }, "w:ins": readChildElements, "w:smartTag": readChildElements, "w:drawing": readChildElements, "w:pict": function(element) { return readChildElements(element).toExtra(); }, "v:roundrect": readChildElements, "v:shape": readChildElements, "v:textbox": readChildElements, "w:txbxContent": readChildElements, "wp:inline": readDrawingElement, "wp:anchor": readDrawingElement, "v:imagedata": readImageData, "v:imageData": readImageData }; return { readXmlElement: readXmlElement, readXmlElements: readXmlElements, _readNumberingProperties: readNumberingProperties }; function readNumberingProperties(element) { var level = element.firstOrEmpty("w:ilvl").attributes["w:val"]; var numId = element.firstOrEmpty("w:numId").attributes["w:val"]; if (level === undefined || numId === undefined) { return null; } else { return numbering.findLevel(numId, level); } } function readTable(element) { return readXmlElements(element.children) .flatMap(calculateRowSpans) .map(documents.Table); } function readTableRow(element) { return readXmlElements(element.children).map(documents.TableRow); } function readTableCell(element) { return readXmlElements(element.children).map(function(children) { var properties = element.firstOrEmpty("w:tcPr"); var gridSpan = properties.firstOrEmpty("w:gridSpan").attributes["w:val"]; var colSpan = gridSpan ? parseInt(gridSpan, 10) : 1; var cell = documents.TableCell(children, {colSpan: colSpan}); cell._vMerge = readVMerge(properties); return cell; }); } function readVMerge(properties) { var element = properties.first("w:vMerge"); if (element) { var val = element.attributes["w:val"]; return val === "continue" || !val; } else { return null; } } function calculateRowSpans(rows) { var unexpectedNonRows = _.any(rows, function(row) { return row.type !== documents.types.tableRow; }); if (unexpectedNonRows) { return elementResultWithMessages(rows, [warning( "unexpected non-row element in table, cell merging may be incorrect" )]); } var unexpectedNonCells = _.any(rows, function(row) { return _.any(row.children, function(cell) { return cell.type !== documents.types.tableCell; }); }); if (unexpectedNonCells) { return elementResultWithMessages(rows, [warning( "unexpected non-cell element in table row, cell merging may be incorrect" )]); } var columns = {}; rows.forEach(function(row) { var cellIndex = 0; row.children.forEach(function(cell) { if (cell._vMerge && columns[cellIndex]) { columns[cellIndex].rowSpan++; } else { columns[cellIndex] = cell; cell._vMerge = false; } cellIndex += cell.colSpan; }); }); rows.forEach(function(row) { row.children = row.children.filter(function(cell) { return !cell._vMerge; }); row.children.forEach(function(cell) { delete cell._vMerge; }); }); return elementResult(rows); } function readDrawingElement(element) { var blips = element .getElementsByTagName("a:graphic") .getElementsByTagName("a:graphicData") .getElementsByTagName("pic:pic") .getElementsByTagName("pic:blipFill") .getElementsByTagName("a:blip"); return combineResults(blips.map(readBlip.bind(null, element))); } function readBlip(element, blip) { var altText = element.first("wp:docPr").attributes.descr; return readImage(findBlipImageFile(blip), altText); } function findBlipImageFile(blip) { var embedRelationshipId = blip.attributes["r:embed"]; var linkRelationshipid = blip.attributes["r:link"]; if (embedRelationshipId) { return findEmbeddedImageFile(embedRelationshipId); } else { var imagePath = relationships[linkRelationshipid].target; return { path: imagePath, read: files.read.bind(files, imagePath) }; } } function readImageData(element) { return readImage( findEmbeddedImageFile(element.attributes["r:id"]), element.attributes["o:title"]); } function findEmbeddedImageFile(relationshipId) { var path = joinZipPath("word", relationships[relationshipId].target); return { path: path, read: docxFile.read.bind(docxFile, path) }; } function readImage(imageFile, altText) { var contentType = contentTypes.findContentType(imageFile.path); var image = documents.Image({ readImage: imageFile.read, altText: altText, contentType: contentType }); var warnings = supportedImageTypes[contentType] ? [] : warning("Image of type " + contentType + " is unlikely to display in web browsers"); return elementResultWithMessages(image, warnings); } function undefinedStyleWarning(type, styleId) { return warning( type + " style with ID " + styleId + " was referenced but not defined in the document"); } } var supportedImageTypes = { "image/png": true, "image/gif": true, "image/jpeg": true, "image/svg+xml": true, "image/tiff": true }; 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:del": true, "w:footnoteRef": true, "w:endnoteRef": true, "w:tblPr": true, "w:tblGrid": true, "w:tcPr": true }; function isParagraphProperties(element) { return element.type === "paragraphProperties"; } function isRunProperties(element) { return element.type === "runProperties"; } function negate(predicate) { return function(value) { return !predicate(value); }; } function joinZipPath(first, second) { // In general, we should check first and second for trailing and leading slashes, // but in our specific case this seems to be sufficient return first + "/" + second; } function emptyResultWithMessages(messages) { return new ReadResult(null, null, messages); } function emptyResult() { return new ReadResult(null); } function elementResult(element) { return new ReadResult(element); } function elementResultWithMessages(element, messages) { return new ReadResult(element, null, messages); } function ReadResult(element, extra, messages) { this.value = element || []; this.extra = extra; this._result = new Result({ element: this.value, extra: extra }, messages); this.messages = this._result.messages; } ReadResult.prototype.toExtra = function() { return new ReadResult(null, joinElements(this.extra, this.value), this.messages); }; ReadResult.prototype.insertExtra = function() { var extra = this.extra; if (extra && extra.length) { return new ReadResult(joinElements(this.value, extra), null, this.messages); } else { return this; } }; ReadResult.prototype.map = function(func) { var result = this._result.map(function(value) { return func(value.element); }); return new ReadResult(result.value, this.extra, result.messages); }; ReadResult.prototype.flatMap = function(func) { var result = this._result.flatMap(function(value) { return func(value.element)._result; }); return new ReadResult(result.value.element, joinElements(this.extra, result.value.extra), result.messages); }; function combineResults(results) { var result = Result.combine(_.pluck(results, "_result")); return new ReadResult( _.flatten(_.pluck(result.value, "element")), _.filter(_.flatten(_.pluck(result.value, "extra")), identity), result.messages ); } function joinElements(first, second) { return _.flatten([first, second]); } function identity(value) { return value; } },{"../documents":5,"../results":25,"underscore":215}],7:[function(require,module,exports){ var documents = require("../documents"); var Result = require("../results").Result; function createCommentsReader(bodyReader) { function readCommentsXml(element) { return Result.combine(element.getElementsByTagName("w:comment") .map(readCommentElement)); } function readCommentElement(element) { var id = element.attributes["w:id"]; function readOptionalAttribute(name) { return (element.attributes[name] || "").trim() || null; } return bodyReader.readXmlElements(element.children) .map(function(body) { return documents.comment({ commentId: id, body: body, authorName: readOptionalAttribute("w:author"), authorInitials: readOptionalAttribute("w:initials") }); }); } return readCommentsXml; } exports.createCommentsReader = createCommentsReader; },{"../documents":5,"../results":25}],8:[function(require,module,exports){ exports.readContentTypesFromXml = readContentTypesFromXml; var fallbackContentTypes = { "png": "png", "gif": "gif", "jpeg": "jpeg", "jpg": "jpeg", "tif": "tiff", "tiff": "tiff", "bmp": "bmp" }; exports.defaultContentTypes = contentTypes({}, {}); function readContentTypesFromXml(element) { var extensionDefaults = {}; var overrides = {}; element.children.forEach(function(child) { if (child.name === "content-types:Default") { extensionDefaults[child.attributes.Extension] = child.attributes.ContentType; } if (child.name === "content-types:Override") { var name = child.attributes.PartName; if (name.charAt(0) === "/") { name = name.substring(1); } overrides[name] = child.attributes.ContentType; } }); return contentTypes(overrides, extensionDefaults); } function contentTypes(overrides, extensionDefaults) { return { findContentType: function(path) { var overrideContentType = overrides[path]; if (overrideContentType) { return overrideContentType; } else { var pathParts = path.split("."); var extension = pathParts[pathParts.length - 1]; if (extensionDefaults.hasOwnProperty(extension)) { return extensionDefaults[extension]; } else { var fallback = fallbackContentTypes[extension.toLowerCase()]; if (fallback) { return "image/" + fallback; } else { return null; } } } } }; } },{}],9:[function(require,module,exports){ exports.DocumentXmlReader = DocumentXmlReader; var documents = require("../documents"); var Result = require("../results").Result; function DocumentXmlReader(options) { var bodyReader = options.bodyReader; function convertXmlToDocument(element) { var body = element.first("w:body"); var result = bodyReader.readXmlElements(body.children) .map(function(children) { return new documents.Document(children, { notes: options.notes, comments: options.comments }); }); return new Result(result.value, result.messages); } return { convertXmlToDocument: convertXmlToDocument }; } },{"../documents":5,"../results":25}],10:[function(require,module,exports){ exports.read = read; var path = require("path"); var promises = require("../promises"); var documents = require("../documents"); var Result = require("../results").Result; var readXmlFromZipFile = require("./office-xml-reader").readXmlFromZipFile; var BodyReader = require("./body-reader").BodyReader; var DocumentXmlReader = require("./document-xml-reader").DocumentXmlReader; var relationshipsReader = require("./relationships-reader"); 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; function read(docxFile, input) { input = input || {}; return promises.props({ contentTypes: readContentTypesFromZipFile(docxFile), numbering: readNumberingFromZipFile(docxFile), styles: readStylesFromZipFile(docxFile), docxFile: docxFile, files: new Files(input.path ? path.dirname(input.path) : null) }).also(function(result) { return { footnotes: readXmlFileWithBody("footnotes", result, function(bodyReader, xml) { if (xml) { return notesReader.createFootnotesReader(bodyReader)(xml); } else { return new Result([]); } }), endnotes: readXmlFileWithBody("endnotes", result, function(bodyReader, xml) { if (xml) { return notesReader.createEndnotesReader(bodyReader)(xml); } 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) { return { notes: result.footnotes.flatMap(function(footnotes) { return result.endnotes.map(function(endnotes) { return new documents.Notes(footnotes.concat(endnotes)); }); }) }; }).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 }); return reader.convertXmlToDocument(xml); }); }); }); }); } function xmlFileReader(options) { return function(zipFile) { return readXmlFromZipFile(zipFile, options.filename) .then(function(element) { return element ? options.readElement(element) : options.defaultValue; }); }; } function readXmlFileWithBody(name, options, func) { var readRelationshipsFromZipFile = xmlFileReader({ filename: "word/_rels/" + name + ".xml.rels", readElement: relationshipsReader.readRelationships, defaultValue: {} }); return readRelationshipsFromZipFile(options.docxFile).then(function(relationships) { var bodyReader = new BodyReader({ relationships: relationships, contentTypes: options.contentTypes, docxFile: options.docxFile, numbering: options.numbering, styles: options.styles, files: options.files }); return readXmlFromZipFile(options.docxFile, "word/" + name + ".xml") .then(function(xml) { return func(bodyReader, xml); }); }); } var readContentTypesFromZipFile = xmlFileReader({ filename: "[Content_Types].xml", readElement: contentTypesReader.readContentTypesFromXml, defaultValue: contentTypesReader.defaultContentTypes }); var readNumberingFromZipFile = xmlFileReader({ filename: "word/numbering.xml", readElement: numberingXml.readNumberingXml, defaultValue: numberingXml.defaultNumbering }); var readStylesFromZipFile = xmlFileReader({ filename: "word/styles.xml", readElement: stylesReader.readStylesXml, defaultValue: stylesReader.defaultStyles }); },{"../documents":5,"../promises":24,"../results":25,"./body-reader":6,"./comments-reader":7,"./content-types-reader":8,"./document-xml-reader":9,"./files":1,"./notes-reader":11,"./numbering-xml":12,"./office-xml-reader":13,"./relationships-reader":14,"./styles-reader":16,"path":193}],11:[function(require,module,exports){ var documents = require("../documents"); var Result = require("../results").Result; exports.createFootnotesReader = createReader.bind(this, "footnote"); exports.createEndnotesReader = createReader.bind(this, "endnote"); function createReader(noteType, bodyReader) { function readNotesXml(element) { return Result.combine(element.getElementsByTagName("w:" + noteType) .filter(isFootnoteElement) .map(readFootnoteElement)); } function isFootnoteElement(element) { var type = element.attributes["w:type"]; return type !== "continuationSeparator" && type !== "separator"; } function readFootnoteElement(footnoteElement) { var id = footnoteElement.attributes["w:id"]; return bodyReader.readXmlElements(footnoteElement.children) .map(function(body) { return documents.Note({noteType: noteType, noteId: id, body: body}); }); } return readNotesXml; } },{"../documents":5,"../results":25}],12:[function(require,module,exports){ exports.readNumberingXml = readNumberingXml; exports.Numbering = Numbering; exports.defaultNumbering = new Numbering({}); function Numbering(nums) { return { findLevel: function(numId, level) { var num = nums[numId]; if (num) { return num[level]; } else { return null; } } }; } function readNumberingXml(root) { var abstractNums = readAbstractNums(root); var nums = readNums(root, abstractNums); return new Numbering(nums); } fun