@depup/mammoth 1.12.0-depup.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +75 -0
- package/.github/ISSUE_TEMPLATE.md +12 -0
- package/.github/pull_request_template.md +3 -0
- package/.github/workflows/tests.yml +26 -0
- package/LICENSE +22 -0
- package/NEWS +530 -0
- package/README.md +37 -0
- package/bin/mammoth +38 -0
- package/browser/docx/files.js +14 -0
- package/browser/unzip.js +12 -0
- package/changes.json +34 -0
- package/lib/document-to-html.js +469 -0
- package/lib/documents.js +254 -0
- package/lib/docx/body-reader.js +798 -0
- package/lib/docx/comments-reader.js +31 -0
- package/lib/docx/content-types-reader.js +58 -0
- package/lib/docx/document-xml-reader.js +30 -0
- package/lib/docx/docx-reader.js +226 -0
- package/lib/docx/files.js +80 -0
- package/lib/docx/notes-reader.js +28 -0
- package/lib/docx/numbering-xml.js +111 -0
- package/lib/docx/office-xml-reader.js +73 -0
- package/lib/docx/relationships-reader.js +43 -0
- package/lib/docx/style-map.js +75 -0
- package/lib/docx/styles-reader.js +90 -0
- package/lib/docx/uris.js +21 -0
- package/lib/html/ast.js +51 -0
- package/lib/html/index.js +41 -0
- package/lib/html/simplify.js +88 -0
- package/lib/images.js +31 -0
- package/lib/index.d.ts +86 -0
- package/lib/index.js +111 -0
- package/lib/main.js +63 -0
- package/lib/options-reader.js +107 -0
- package/lib/promises.js +42 -0
- package/lib/raw-text.js +14 -0
- package/lib/results.js +72 -0
- package/lib/style-reader.js +365 -0
- package/lib/styles/document-matchers.js +100 -0
- package/lib/styles/html-paths.js +75 -0
- package/lib/styles/parser/tokeniser.js +30 -0
- package/lib/transforms.js +62 -0
- package/lib/underline.js +11 -0
- package/lib/unzip.js +20 -0
- package/lib/writers/html-writer.js +159 -0
- package/lib/writers/index.js +14 -0
- package/lib/writers/markdown-writer.js +163 -0
- package/lib/xml/index.js +8 -0
- package/lib/xml/nodes.js +70 -0
- package/lib/xml/reader.js +69 -0
- package/lib/xml/writer.js +61 -0
- package/lib/xml/xmldom.js +23 -0
- package/lib/zipfile.js +72 -0
- package/mammoth.browser.js +22096 -0
- package/mammoth.browser.min.js +20 -0
- package/package.json +106 -0
- package/test/.eslintrc.json +7 -0
- package/test/document-to-html.tests.js +892 -0
- package/test/docx/body-reader.tests.js +2037 -0
- package/test/docx/comments-reader.tests.js +53 -0
- package/test/docx/content-types-reader.tests.js +45 -0
- package/test/docx/document-matchers.js +42 -0
- package/test/docx/document-xml-reader.tests.js +41 -0
- package/test/docx/docx-reader.tests.js +179 -0
- package/test/docx/files.tests.js +107 -0
- package/test/docx/notes-reader.tests.js +36 -0
- package/test/docx/numbering-xml.tests.js +183 -0
- package/test/docx/office-xml-reader.tests.js +41 -0
- package/test/docx/relationships-reader.tests.js +65 -0
- package/test/docx/style-map.tests.js +113 -0
- package/test/docx/styles-reader.tests.js +143 -0
- package/test/docx/testing.js +12 -0
- package/test/docx/uris.tests.js +22 -0
- package/test/html/simplify.tests.js +141 -0
- package/test/html/write.tests.js +42 -0
- package/test/images.tests.js +112 -0
- package/test/main.tests.js +89 -0
- package/test/mammoth.tests.js +523 -0
- package/test/mocha.opts +1 -0
- package/test/options-reader.tests.js +63 -0
- package/test/raw-text.tests.js +61 -0
- package/test/results.tests.js +15 -0
- package/test/style-reader.tests.js +291 -0
- package/test/styles/document-matchers.tests.js +91 -0
- package/test/styles/html-paths.tests.js +20 -0
- package/test/styles/parser/tokeniser.tests.js +104 -0
- package/test/test-data/comments.docx +0 -0
- package/test/test-data/embedded-style-map.docx +0 -0
- package/test/test-data/empty.docx +0 -0
- package/test/test-data/empty.zip +0 -0
- package/test/test-data/endnotes.docx +0 -0
- package/test/test-data/external-picture.docx +0 -0
- package/test/test-data/footnote-hyperlink.docx +0 -0
- package/test/test-data/footnotes.docx +0 -0
- package/test/test-data/hello.zip +0 -0
- package/test/test-data/hyperlinks/word/_rels/document.xml.rels +10 -0
- package/test/test-data/hyperlinks/word/document.xml +18 -0
- package/test/test-data/simple/word/document.xml +18 -0
- package/test/test-data/simple-list.docx +0 -0
- package/test/test-data/single-paragraph.docx +0 -0
- package/test/test-data/strict-format.docx +0 -0
- package/test/test-data/strikethrough.docx +0 -0
- package/test/test-data/tables.docx +0 -0
- package/test/test-data/text-box.docx +0 -0
- package/test/test-data/tiny-picture-target-base-relative.docx +0 -0
- package/test/test-data/tiny-picture.docx +0 -0
- package/test/test-data/tiny-picture.png +0 -0
- package/test/test-data/underline.docx +0 -0
- package/test/test-data/utf8-bom.docx +0 -0
- package/test/test.js +11 -0
- package/test/testing.js +55 -0
- package/test/transforms.tests.js +125 -0
- package/test/unzip.tests.js +38 -0
- package/test/writers/html-writer.tests.js +133 -0
- package/test/writers/markdown-writer.tests.js +304 -0
- package/test/xml/reader.tests.js +85 -0
- package/test/xml/writer.tests.js +81 -0
- package/test/zipfile.tests.js +61 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
var documents = require("../documents");
|
|
2
|
+
var Result = require("../results").Result;
|
|
3
|
+
|
|
4
|
+
function createCommentsReader(bodyReader) {
|
|
5
|
+
function readCommentsXml(element) {
|
|
6
|
+
return Result.combine(element.getElementsByTagName("w:comment")
|
|
7
|
+
.map(readCommentElement));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function readCommentElement(element) {
|
|
11
|
+
var id = element.attributes["w:id"];
|
|
12
|
+
|
|
13
|
+
function readOptionalAttribute(name) {
|
|
14
|
+
return (element.attributes[name] || "").trim() || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return bodyReader.readXmlElements(element.children)
|
|
18
|
+
.map(function(body) {
|
|
19
|
+
return documents.comment({
|
|
20
|
+
commentId: id,
|
|
21
|
+
body: body,
|
|
22
|
+
authorName: readOptionalAttribute("w:author"),
|
|
23
|
+
authorInitials: readOptionalAttribute("w:initials")
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return readCommentsXml;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
exports.createCommentsReader = createCommentsReader;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
exports.readContentTypesFromXml = readContentTypesFromXml;
|
|
2
|
+
|
|
3
|
+
var fallbackContentTypes = {
|
|
4
|
+
"png": "png",
|
|
5
|
+
"gif": "gif",
|
|
6
|
+
"jpeg": "jpeg",
|
|
7
|
+
"jpg": "jpeg",
|
|
8
|
+
"tif": "tiff",
|
|
9
|
+
"tiff": "tiff",
|
|
10
|
+
"bmp": "bmp"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
exports.defaultContentTypes = contentTypes({}, {});
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
function readContentTypesFromXml(element) {
|
|
17
|
+
var extensionDefaults = {};
|
|
18
|
+
var overrides = {};
|
|
19
|
+
|
|
20
|
+
element.children.forEach(function(child) {
|
|
21
|
+
if (child.name === "content-types:Default") {
|
|
22
|
+
extensionDefaults[child.attributes.Extension] = child.attributes.ContentType;
|
|
23
|
+
}
|
|
24
|
+
if (child.name === "content-types:Override") {
|
|
25
|
+
var name = child.attributes.PartName;
|
|
26
|
+
if (name.charAt(0) === "/") {
|
|
27
|
+
name = name.substring(1);
|
|
28
|
+
}
|
|
29
|
+
overrides[name] = child.attributes.ContentType;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return contentTypes(overrides, extensionDefaults);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function contentTypes(overrides, extensionDefaults) {
|
|
36
|
+
return {
|
|
37
|
+
findContentType: function(path) {
|
|
38
|
+
var overrideContentType = overrides[path];
|
|
39
|
+
if (overrideContentType) {
|
|
40
|
+
return overrideContentType;
|
|
41
|
+
} else {
|
|
42
|
+
var pathParts = path.split(".");
|
|
43
|
+
var extension = pathParts[pathParts.length - 1];
|
|
44
|
+
if (extensionDefaults.hasOwnProperty(extension)) {
|
|
45
|
+
return extensionDefaults[extension];
|
|
46
|
+
} else {
|
|
47
|
+
var fallback = fallbackContentTypes[extension.toLowerCase()];
|
|
48
|
+
if (fallback) {
|
|
49
|
+
return "image/" + fallback;
|
|
50
|
+
} else {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
exports.DocumentXmlReader = DocumentXmlReader;
|
|
2
|
+
|
|
3
|
+
var documents = require("../documents");
|
|
4
|
+
var Result = require("../results").Result;
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
function DocumentXmlReader(options) {
|
|
8
|
+
var bodyReader = options.bodyReader;
|
|
9
|
+
|
|
10
|
+
function convertXmlToDocument(element) {
|
|
11
|
+
var body = element.first("w:body");
|
|
12
|
+
|
|
13
|
+
if (body == null) {
|
|
14
|
+
throw new Error("Could not find the body element: are you sure this is a docx file?");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
var result = bodyReader.readXmlElements(body.children)
|
|
18
|
+
.map(function(children) {
|
|
19
|
+
return new documents.Document(children, {
|
|
20
|
+
notes: options.notes,
|
|
21
|
+
comments: options.comments
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
return new Result(result.value, result.messages);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
convertXmlToDocument: convertXmlToDocument
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
exports.read = read;
|
|
2
|
+
exports._findPartPaths = findPartPaths;
|
|
3
|
+
|
|
4
|
+
var promises = require("../promises");
|
|
5
|
+
var documents = require("../documents");
|
|
6
|
+
var Result = require("../results").Result;
|
|
7
|
+
var zipfile = require("../zipfile");
|
|
8
|
+
|
|
9
|
+
var readXmlFromZipFile = require("./office-xml-reader").readXmlFromZipFile;
|
|
10
|
+
var createBodyReader = require("./body-reader").createBodyReader;
|
|
11
|
+
var DocumentXmlReader = require("./document-xml-reader").DocumentXmlReader;
|
|
12
|
+
var relationshipsReader = require("./relationships-reader");
|
|
13
|
+
var contentTypesReader = require("./content-types-reader");
|
|
14
|
+
var numberingXml = require("./numbering-xml");
|
|
15
|
+
var stylesReader = require("./styles-reader");
|
|
16
|
+
var notesReader = require("./notes-reader");
|
|
17
|
+
var commentsReader = require("./comments-reader");
|
|
18
|
+
var Files = require("./files").Files;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
function read(docxFile, input, options) {
|
|
22
|
+
input = input || {};
|
|
23
|
+
options = options || {};
|
|
24
|
+
|
|
25
|
+
var files = new Files({
|
|
26
|
+
externalFileAccess: options.externalFileAccess,
|
|
27
|
+
relativeToFile: input.path
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
return promises.props({
|
|
31
|
+
contentTypes: readContentTypesFromZipFile(docxFile),
|
|
32
|
+
partPaths: findPartPaths(docxFile),
|
|
33
|
+
docxFile: docxFile,
|
|
34
|
+
files: files
|
|
35
|
+
}).also(function(result) {
|
|
36
|
+
return {
|
|
37
|
+
styles: readStylesFromZipFile(docxFile, result.partPaths.styles)
|
|
38
|
+
};
|
|
39
|
+
}).also(function(result) {
|
|
40
|
+
return {
|
|
41
|
+
numbering: readNumberingFromZipFile(docxFile, result.partPaths.numbering, result.styles)
|
|
42
|
+
};
|
|
43
|
+
}).also(function(result) {
|
|
44
|
+
return {
|
|
45
|
+
footnotes: readXmlFileWithBody(result.partPaths.footnotes, result, function(bodyReader, xml) {
|
|
46
|
+
if (xml) {
|
|
47
|
+
return notesReader.createFootnotesReader(bodyReader)(xml);
|
|
48
|
+
} else {
|
|
49
|
+
return new Result([]);
|
|
50
|
+
}
|
|
51
|
+
}),
|
|
52
|
+
endnotes: readXmlFileWithBody(result.partPaths.endnotes, result, function(bodyReader, xml) {
|
|
53
|
+
if (xml) {
|
|
54
|
+
return notesReader.createEndnotesReader(bodyReader)(xml);
|
|
55
|
+
} else {
|
|
56
|
+
return new Result([]);
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
59
|
+
comments: readXmlFileWithBody(result.partPaths.comments, result, function(bodyReader, xml) {
|
|
60
|
+
if (xml) {
|
|
61
|
+
return commentsReader.createCommentsReader(bodyReader)(xml);
|
|
62
|
+
} else {
|
|
63
|
+
return new Result([]);
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
};
|
|
67
|
+
}).also(function(result) {
|
|
68
|
+
return {
|
|
69
|
+
notes: result.footnotes.flatMap(function(footnotes) {
|
|
70
|
+
return result.endnotes.map(function(endnotes) {
|
|
71
|
+
return new documents.Notes(footnotes.concat(endnotes));
|
|
72
|
+
});
|
|
73
|
+
})
|
|
74
|
+
};
|
|
75
|
+
}).then(function(result) {
|
|
76
|
+
return readXmlFileWithBody(result.partPaths.mainDocument, result, function(bodyReader, xml) {
|
|
77
|
+
return result.notes.flatMap(function(notes) {
|
|
78
|
+
return result.comments.flatMap(function(comments) {
|
|
79
|
+
var reader = new DocumentXmlReader({
|
|
80
|
+
bodyReader: bodyReader,
|
|
81
|
+
notes: notes,
|
|
82
|
+
comments: comments
|
|
83
|
+
});
|
|
84
|
+
return reader.convertXmlToDocument(xml);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function findPartPaths(docxFile) {
|
|
92
|
+
return readPackageRelationships(docxFile).then(function(packageRelationships) {
|
|
93
|
+
var mainDocumentPath = findPartPath({
|
|
94
|
+
docxFile: docxFile,
|
|
95
|
+
relationships: packageRelationships,
|
|
96
|
+
relationshipType: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
97
|
+
basePath: "",
|
|
98
|
+
fallbackPath: "word/document.xml"
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (!docxFile.exists(mainDocumentPath)) {
|
|
102
|
+
throw new Error("Could not find main document part. Are you sure this is a valid .docx file?");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return xmlFileReader({
|
|
106
|
+
filename: relationshipsFilename(mainDocumentPath),
|
|
107
|
+
readElement: relationshipsReader.readRelationships,
|
|
108
|
+
defaultValue: relationshipsReader.defaultValue
|
|
109
|
+
})(docxFile).then(function(documentRelationships) {
|
|
110
|
+
function findPartRelatedToMainDocument(name) {
|
|
111
|
+
return findPartPath({
|
|
112
|
+
docxFile: docxFile,
|
|
113
|
+
relationships: documentRelationships,
|
|
114
|
+
relationshipType: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/" + name,
|
|
115
|
+
basePath: zipfile.splitPath(mainDocumentPath).dirname,
|
|
116
|
+
fallbackPath: "word/" + name + ".xml"
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
mainDocument: mainDocumentPath,
|
|
122
|
+
comments: findPartRelatedToMainDocument("comments"),
|
|
123
|
+
endnotes: findPartRelatedToMainDocument("endnotes"),
|
|
124
|
+
footnotes: findPartRelatedToMainDocument("footnotes"),
|
|
125
|
+
numbering: findPartRelatedToMainDocument("numbering"),
|
|
126
|
+
styles: findPartRelatedToMainDocument("styles")
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function findPartPath(options) {
|
|
133
|
+
var docxFile = options.docxFile;
|
|
134
|
+
var relationships = options.relationships;
|
|
135
|
+
var relationshipType = options.relationshipType;
|
|
136
|
+
var basePath = options.basePath;
|
|
137
|
+
var fallbackPath = options.fallbackPath;
|
|
138
|
+
|
|
139
|
+
var targets = relationships.findTargetsByType(relationshipType);
|
|
140
|
+
var normalisedTargets = targets.map(function(target) {
|
|
141
|
+
return stripPrefix(zipfile.joinPath(basePath, target), "/");
|
|
142
|
+
});
|
|
143
|
+
var validTargets = normalisedTargets.filter(function(target) {
|
|
144
|
+
return docxFile.exists(target);
|
|
145
|
+
});
|
|
146
|
+
if (validTargets.length === 0) {
|
|
147
|
+
return fallbackPath;
|
|
148
|
+
} else {
|
|
149
|
+
return validTargets[0];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function stripPrefix(value, prefix) {
|
|
154
|
+
if (value.substring(0, prefix.length) === prefix) {
|
|
155
|
+
return value.substring(prefix.length);
|
|
156
|
+
} else {
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function xmlFileReader(options) {
|
|
162
|
+
return function(zipFile) {
|
|
163
|
+
return readXmlFromZipFile(zipFile, options.filename)
|
|
164
|
+
.then(function(element) {
|
|
165
|
+
return element ? options.readElement(element) : options.defaultValue;
|
|
166
|
+
});
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function readXmlFileWithBody(filename, options, func) {
|
|
171
|
+
var readRelationshipsFromZipFile = xmlFileReader({
|
|
172
|
+
filename: relationshipsFilename(filename),
|
|
173
|
+
readElement: relationshipsReader.readRelationships,
|
|
174
|
+
defaultValue: relationshipsReader.defaultValue
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return readRelationshipsFromZipFile(options.docxFile).then(function(relationships) {
|
|
178
|
+
var bodyReader = new createBodyReader({
|
|
179
|
+
relationships: relationships,
|
|
180
|
+
contentTypes: options.contentTypes,
|
|
181
|
+
docxFile: options.docxFile,
|
|
182
|
+
numbering: options.numbering,
|
|
183
|
+
styles: options.styles,
|
|
184
|
+
files: options.files
|
|
185
|
+
});
|
|
186
|
+
return readXmlFromZipFile(options.docxFile, filename)
|
|
187
|
+
.then(function(xml) {
|
|
188
|
+
return func(bodyReader, xml);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function relationshipsFilename(filename) {
|
|
194
|
+
var split = zipfile.splitPath(filename);
|
|
195
|
+
return zipfile.joinPath(split.dirname, "_rels", split.basename + ".rels");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
var readContentTypesFromZipFile = xmlFileReader({
|
|
199
|
+
filename: "[Content_Types].xml",
|
|
200
|
+
readElement: contentTypesReader.readContentTypesFromXml,
|
|
201
|
+
defaultValue: contentTypesReader.defaultContentTypes
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
function readNumberingFromZipFile(zipFile, path, styles) {
|
|
205
|
+
return xmlFileReader({
|
|
206
|
+
filename: path,
|
|
207
|
+
readElement: function(element) {
|
|
208
|
+
return numberingXml.readNumberingXml(element, {styles: styles});
|
|
209
|
+
},
|
|
210
|
+
defaultValue: numberingXml.defaultNumbering
|
|
211
|
+
})(zipFile);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function readStylesFromZipFile(zipFile, path) {
|
|
215
|
+
return xmlFileReader({
|
|
216
|
+
filename: path,
|
|
217
|
+
readElement: stylesReader.readStylesXml,
|
|
218
|
+
defaultValue: stylesReader.defaultStyles
|
|
219
|
+
})(zipFile);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
var readPackageRelationships = xmlFileReader({
|
|
223
|
+
filename: "_rels/.rels",
|
|
224
|
+
readElement: relationshipsReader.readRelationships,
|
|
225
|
+
defaultValue: relationshipsReader.defaultValue
|
|
226
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
var fs = require("fs");
|
|
2
|
+
var url = require("url");
|
|
3
|
+
var os = require("os");
|
|
4
|
+
var dirname = require("path").dirname;
|
|
5
|
+
var resolvePath = require("path").resolve;
|
|
6
|
+
var isAbsolutePath = require('path-is-absolute');
|
|
7
|
+
|
|
8
|
+
var promises = require("../promises");
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
exports.Files = Files;
|
|
12
|
+
exports.uriToPath = uriToPath;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
function Files(options) {
|
|
16
|
+
options = options || {};
|
|
17
|
+
if (!options.externalFileAccess) {
|
|
18
|
+
return {
|
|
19
|
+
read: function(uri) {
|
|
20
|
+
return promises.reject(new Error("could not read external image '" + uri + "', external file access is disabled"));
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var base = options.relativeToFile ? dirname(options.relativeToFile) : null;
|
|
26
|
+
|
|
27
|
+
function read(uri, encoding) {
|
|
28
|
+
return resolveUri(uri).then(function(path) {
|
|
29
|
+
return readFile(path, encoding).caught(function(error) {
|
|
30
|
+
var message = "could not open external image: '" + uri + "' (document directory: '" + base + "')\n" + error.message;
|
|
31
|
+
return promises.reject(new Error(message));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveUri(uri) {
|
|
37
|
+
var path = uriToPath(uri);
|
|
38
|
+
if (isAbsolutePath(path)) {
|
|
39
|
+
return promises.resolve(path);
|
|
40
|
+
} else if (base) {
|
|
41
|
+
return promises.resolve(resolvePath(base, path));
|
|
42
|
+
} else {
|
|
43
|
+
return promises.reject(new Error("could not find external image '" + uri + "', path of input document is unknown"));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
read: read
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
var readFile = promises.promisify(fs.readFile.bind(fs));
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
function uriToPath(uriString, platform) {
|
|
57
|
+
if (!platform) {
|
|
58
|
+
platform = os.platform();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
var uri = url.parse(uriString);
|
|
62
|
+
if (isLocalFileUri(uri) || isRelativeUri(uri)) {
|
|
63
|
+
var path = decodeURIComponent(uri.path);
|
|
64
|
+
if (platform === "win32" && /^\/[a-z]:/i.test(path)) {
|
|
65
|
+
return path.slice(1);
|
|
66
|
+
} else {
|
|
67
|
+
return path;
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
throw new Error("Could not convert URI to path: " + uriString);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isLocalFileUri(uri) {
|
|
75
|
+
return uri.protocol === "file:" && (!uri.host || uri.host === "localhost");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function isRelativeUri(uri) {
|
|
79
|
+
return !uri.protocol && !uri.host;
|
|
80
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
var documents = require("../documents");
|
|
2
|
+
var Result = require("../results").Result;
|
|
3
|
+
|
|
4
|
+
exports.createFootnotesReader = createReader.bind(this, "footnote");
|
|
5
|
+
exports.createEndnotesReader = createReader.bind(this, "endnote");
|
|
6
|
+
|
|
7
|
+
function createReader(noteType, bodyReader) {
|
|
8
|
+
function readNotesXml(element) {
|
|
9
|
+
return Result.combine(element.getElementsByTagName("w:" + noteType)
|
|
10
|
+
.filter(isFootnoteElement)
|
|
11
|
+
.map(readFootnoteElement));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isFootnoteElement(element) {
|
|
15
|
+
var type = element.attributes["w:type"];
|
|
16
|
+
return type !== "continuationSeparator" && type !== "separator";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readFootnoteElement(footnoteElement) {
|
|
20
|
+
var id = footnoteElement.attributes["w:id"];
|
|
21
|
+
return bodyReader.readXmlElements(footnoteElement.children)
|
|
22
|
+
.map(function(body) {
|
|
23
|
+
return documents.Note({noteType: noteType, noteId: id, body: body});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return readNotesXml;
|
|
28
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
var _ = require("underscore");
|
|
2
|
+
|
|
3
|
+
exports.readNumberingXml = readNumberingXml;
|
|
4
|
+
exports.Numbering = Numbering;
|
|
5
|
+
exports.defaultNumbering = new Numbering({}, {});
|
|
6
|
+
|
|
7
|
+
function Numbering(nums, abstractNums, styles) {
|
|
8
|
+
var allLevels = _.flatten(_.values(abstractNums).map(function(abstractNum) {
|
|
9
|
+
return _.values(abstractNum.levels);
|
|
10
|
+
}));
|
|
11
|
+
|
|
12
|
+
var levelsByParagraphStyleId = _.indexBy(
|
|
13
|
+
allLevels.filter(function(level) {
|
|
14
|
+
return level.paragraphStyleId != null;
|
|
15
|
+
}),
|
|
16
|
+
"paragraphStyleId"
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
function findLevel(numId, level) {
|
|
20
|
+
var num = nums[numId];
|
|
21
|
+
if (num) {
|
|
22
|
+
var abstractNum = abstractNums[num.abstractNumId];
|
|
23
|
+
if (!abstractNum) {
|
|
24
|
+
return null;
|
|
25
|
+
} else if (abstractNum.numStyleLink == null) {
|
|
26
|
+
return abstractNums[num.abstractNumId].levels[level];
|
|
27
|
+
} else {
|
|
28
|
+
var style = styles.findNumberingStyleById(abstractNum.numStyleLink);
|
|
29
|
+
return findLevel(style.numId, level);
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function findLevelByParagraphStyleId(styleId) {
|
|
37
|
+
return levelsByParagraphStyleId[styleId] || null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
findLevel: findLevel,
|
|
42
|
+
findLevelByParagraphStyleId: findLevelByParagraphStyleId
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readNumberingXml(root, options) {
|
|
47
|
+
if (!options || !options.styles) {
|
|
48
|
+
throw new Error("styles is missing");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var abstractNums = readAbstractNums(root);
|
|
52
|
+
var nums = readNums(root, abstractNums);
|
|
53
|
+
return new Numbering(nums, abstractNums, options.styles);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function readAbstractNums(root) {
|
|
57
|
+
var abstractNums = {};
|
|
58
|
+
root.getElementsByTagName("w:abstractNum").forEach(function(element) {
|
|
59
|
+
var id = element.attributes["w:abstractNumId"];
|
|
60
|
+
abstractNums[id] = readAbstractNum(element);
|
|
61
|
+
});
|
|
62
|
+
return abstractNums;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function readAbstractNum(element) {
|
|
66
|
+
var levels = {};
|
|
67
|
+
|
|
68
|
+
// Some malformed documents define numbering levels without an index, and
|
|
69
|
+
// reference the numbering using a w:numPr element without a w:ilvl child.
|
|
70
|
+
// To handle such cases, we assume a level of 0 as a fallback.
|
|
71
|
+
var levelWithoutIndex = null;
|
|
72
|
+
|
|
73
|
+
element.getElementsByTagName("w:lvl").forEach(function(levelElement) {
|
|
74
|
+
var levelIndex = levelElement.attributes["w:ilvl"];
|
|
75
|
+
var numFmt = levelElement.firstOrEmpty("w:numFmt").attributes["w:val"];
|
|
76
|
+
var isOrdered = numFmt !== "bullet";
|
|
77
|
+
var paragraphStyleId = levelElement.firstOrEmpty("w:pStyle").attributes["w:val"];
|
|
78
|
+
|
|
79
|
+
if (levelIndex === undefined) {
|
|
80
|
+
levelWithoutIndex = {
|
|
81
|
+
isOrdered: isOrdered,
|
|
82
|
+
level: "0",
|
|
83
|
+
paragraphStyleId: paragraphStyleId
|
|
84
|
+
};
|
|
85
|
+
} else {
|
|
86
|
+
levels[levelIndex] = {
|
|
87
|
+
isOrdered: isOrdered,
|
|
88
|
+
level: levelIndex,
|
|
89
|
+
paragraphStyleId: paragraphStyleId
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (levelWithoutIndex !== null && levels[levelWithoutIndex.level] === undefined) {
|
|
95
|
+
levels[levelWithoutIndex.level] = levelWithoutIndex;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var numStyleLink = element.firstOrEmpty("w:numStyleLink").attributes["w:val"];
|
|
99
|
+
|
|
100
|
+
return {levels: levels, numStyleLink: numStyleLink};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function readNums(root) {
|
|
104
|
+
var nums = {};
|
|
105
|
+
root.getElementsByTagName("w:num").forEach(function(element) {
|
|
106
|
+
var numId = element.attributes["w:numId"];
|
|
107
|
+
var abstractNumId = element.first("w:abstractNumId").attributes["w:val"];
|
|
108
|
+
nums[numId] = {abstractNumId: abstractNumId};
|
|
109
|
+
});
|
|
110
|
+
return nums;
|
|
111
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
var _ = require("underscore");
|
|
2
|
+
|
|
3
|
+
var promises = require("../promises");
|
|
4
|
+
var xml = require("../xml");
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
exports.read = read;
|
|
8
|
+
exports.readXmlFromZipFile = readXmlFromZipFile;
|
|
9
|
+
|
|
10
|
+
var xmlNamespaceMap = {
|
|
11
|
+
// Transitional format
|
|
12
|
+
"http://schemas.openxmlformats.org/wordprocessingml/2006/main": "w",
|
|
13
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships": "r",
|
|
14
|
+
"http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing": "wp",
|
|
15
|
+
"http://schemas.openxmlformats.org/drawingml/2006/main": "a",
|
|
16
|
+
"http://schemas.openxmlformats.org/drawingml/2006/picture": "pic",
|
|
17
|
+
|
|
18
|
+
// Strict format
|
|
19
|
+
"http://purl.oclc.org/ooxml/wordprocessingml/main": "w",
|
|
20
|
+
"http://purl.oclc.org/ooxml/officeDocument/relationships": "r",
|
|
21
|
+
"http://purl.oclc.org/ooxml/drawingml/wordprocessingDrawing": "wp",
|
|
22
|
+
"http://purl.oclc.org/ooxml/drawingml/main": "a",
|
|
23
|
+
"http://purl.oclc.org/ooxml/drawingml/picture": "pic",
|
|
24
|
+
|
|
25
|
+
// Common
|
|
26
|
+
"http://schemas.openxmlformats.org/package/2006/content-types": "content-types",
|
|
27
|
+
"http://schemas.openxmlformats.org/package/2006/relationships": "relationships",
|
|
28
|
+
"http://schemas.openxmlformats.org/markup-compatibility/2006": "mc",
|
|
29
|
+
"urn:schemas-microsoft-com:vml": "v",
|
|
30
|
+
"urn:schemas-microsoft-com:office:word": "office-word",
|
|
31
|
+
|
|
32
|
+
// [MS-DOCX]: Word Extensions to the Office Open XML (.docx) File Format
|
|
33
|
+
// https://learn.microsoft.com/en-us/openspecs/office_standards/ms-docx/b839fe1f-e1ca-4fa6-8c26-5954d0abbccd
|
|
34
|
+
"http://schemas.microsoft.com/office/word/2010/wordml": "wordml"
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
function read(xmlString) {
|
|
39
|
+
return xml.readString(xmlString, xmlNamespaceMap)
|
|
40
|
+
.then(function(document) {
|
|
41
|
+
return collapseAlternateContent(document)[0];
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
function readXmlFromZipFile(docxFile, path) {
|
|
47
|
+
if (docxFile.exists(path)) {
|
|
48
|
+
return docxFile.read(path, "utf-8")
|
|
49
|
+
.then(stripUtf8Bom)
|
|
50
|
+
.then(read);
|
|
51
|
+
} else {
|
|
52
|
+
return promises.resolve(null);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
function stripUtf8Bom(xmlString) {
|
|
58
|
+
return xmlString.replace(/^\uFEFF/g, '');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
function collapseAlternateContent(node) {
|
|
63
|
+
if (node.type === "element") {
|
|
64
|
+
if (node.name === "mc:AlternateContent") {
|
|
65
|
+
return node.firstOrEmpty("mc:Fallback").children;
|
|
66
|
+
} else {
|
|
67
|
+
node.children = _.flatten(node.children.map(collapseAlternateContent, true));
|
|
68
|
+
return [node];
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
return [node];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
exports.readRelationships = readRelationships;
|
|
2
|
+
exports.defaultValue = new Relationships([]);
|
|
3
|
+
exports.Relationships = Relationships;
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
function readRelationships(element) {
|
|
7
|
+
var relationships = [];
|
|
8
|
+
element.children.forEach(function(child) {
|
|
9
|
+
if (child.name === "relationships:Relationship") {
|
|
10
|
+
var relationship = {
|
|
11
|
+
relationshipId: child.attributes.Id,
|
|
12
|
+
target: child.attributes.Target,
|
|
13
|
+
type: child.attributes.Type
|
|
14
|
+
};
|
|
15
|
+
relationships.push(relationship);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
return new Relationships(relationships);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Relationships(relationships) {
|
|
22
|
+
var targetsByRelationshipId = {};
|
|
23
|
+
relationships.forEach(function(relationship) {
|
|
24
|
+
targetsByRelationshipId[relationship.relationshipId] = relationship.target;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
var targetsByType = {};
|
|
28
|
+
relationships.forEach(function(relationship) {
|
|
29
|
+
if (!targetsByType[relationship.type]) {
|
|
30
|
+
targetsByType[relationship.type] = [];
|
|
31
|
+
}
|
|
32
|
+
targetsByType[relationship.type].push(relationship.target);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
findTargetByRelationshipId: function(relationshipId) {
|
|
37
|
+
return targetsByRelationshipId[relationshipId];
|
|
38
|
+
},
|
|
39
|
+
findTargetsByType: function(type) {
|
|
40
|
+
return targetsByType[type] || [];
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|