mammoth-style
Version:
Convert Word documents (.docx files) to HTML (with style)
1,584 lines (1,386 loc) • 1.05 MB
JavaScript
// 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