@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.
Files changed (118) hide show
  1. package/.eslintrc.json +75 -0
  2. package/.github/ISSUE_TEMPLATE.md +12 -0
  3. package/.github/pull_request_template.md +3 -0
  4. package/.github/workflows/tests.yml +26 -0
  5. package/LICENSE +22 -0
  6. package/NEWS +530 -0
  7. package/README.md +37 -0
  8. package/bin/mammoth +38 -0
  9. package/browser/docx/files.js +14 -0
  10. package/browser/unzip.js +12 -0
  11. package/changes.json +34 -0
  12. package/lib/document-to-html.js +469 -0
  13. package/lib/documents.js +254 -0
  14. package/lib/docx/body-reader.js +798 -0
  15. package/lib/docx/comments-reader.js +31 -0
  16. package/lib/docx/content-types-reader.js +58 -0
  17. package/lib/docx/document-xml-reader.js +30 -0
  18. package/lib/docx/docx-reader.js +226 -0
  19. package/lib/docx/files.js +80 -0
  20. package/lib/docx/notes-reader.js +28 -0
  21. package/lib/docx/numbering-xml.js +111 -0
  22. package/lib/docx/office-xml-reader.js +73 -0
  23. package/lib/docx/relationships-reader.js +43 -0
  24. package/lib/docx/style-map.js +75 -0
  25. package/lib/docx/styles-reader.js +90 -0
  26. package/lib/docx/uris.js +21 -0
  27. package/lib/html/ast.js +51 -0
  28. package/lib/html/index.js +41 -0
  29. package/lib/html/simplify.js +88 -0
  30. package/lib/images.js +31 -0
  31. package/lib/index.d.ts +86 -0
  32. package/lib/index.js +111 -0
  33. package/lib/main.js +63 -0
  34. package/lib/options-reader.js +107 -0
  35. package/lib/promises.js +42 -0
  36. package/lib/raw-text.js +14 -0
  37. package/lib/results.js +72 -0
  38. package/lib/style-reader.js +365 -0
  39. package/lib/styles/document-matchers.js +100 -0
  40. package/lib/styles/html-paths.js +75 -0
  41. package/lib/styles/parser/tokeniser.js +30 -0
  42. package/lib/transforms.js +62 -0
  43. package/lib/underline.js +11 -0
  44. package/lib/unzip.js +20 -0
  45. package/lib/writers/html-writer.js +159 -0
  46. package/lib/writers/index.js +14 -0
  47. package/lib/writers/markdown-writer.js +163 -0
  48. package/lib/xml/index.js +8 -0
  49. package/lib/xml/nodes.js +70 -0
  50. package/lib/xml/reader.js +69 -0
  51. package/lib/xml/writer.js +61 -0
  52. package/lib/xml/xmldom.js +23 -0
  53. package/lib/zipfile.js +72 -0
  54. package/mammoth.browser.js +22096 -0
  55. package/mammoth.browser.min.js +20 -0
  56. package/package.json +106 -0
  57. package/test/.eslintrc.json +7 -0
  58. package/test/document-to-html.tests.js +892 -0
  59. package/test/docx/body-reader.tests.js +2037 -0
  60. package/test/docx/comments-reader.tests.js +53 -0
  61. package/test/docx/content-types-reader.tests.js +45 -0
  62. package/test/docx/document-matchers.js +42 -0
  63. package/test/docx/document-xml-reader.tests.js +41 -0
  64. package/test/docx/docx-reader.tests.js +179 -0
  65. package/test/docx/files.tests.js +107 -0
  66. package/test/docx/notes-reader.tests.js +36 -0
  67. package/test/docx/numbering-xml.tests.js +183 -0
  68. package/test/docx/office-xml-reader.tests.js +41 -0
  69. package/test/docx/relationships-reader.tests.js +65 -0
  70. package/test/docx/style-map.tests.js +113 -0
  71. package/test/docx/styles-reader.tests.js +143 -0
  72. package/test/docx/testing.js +12 -0
  73. package/test/docx/uris.tests.js +22 -0
  74. package/test/html/simplify.tests.js +141 -0
  75. package/test/html/write.tests.js +42 -0
  76. package/test/images.tests.js +112 -0
  77. package/test/main.tests.js +89 -0
  78. package/test/mammoth.tests.js +523 -0
  79. package/test/mocha.opts +1 -0
  80. package/test/options-reader.tests.js +63 -0
  81. package/test/raw-text.tests.js +61 -0
  82. package/test/results.tests.js +15 -0
  83. package/test/style-reader.tests.js +291 -0
  84. package/test/styles/document-matchers.tests.js +91 -0
  85. package/test/styles/html-paths.tests.js +20 -0
  86. package/test/styles/parser/tokeniser.tests.js +104 -0
  87. package/test/test-data/comments.docx +0 -0
  88. package/test/test-data/embedded-style-map.docx +0 -0
  89. package/test/test-data/empty.docx +0 -0
  90. package/test/test-data/empty.zip +0 -0
  91. package/test/test-data/endnotes.docx +0 -0
  92. package/test/test-data/external-picture.docx +0 -0
  93. package/test/test-data/footnote-hyperlink.docx +0 -0
  94. package/test/test-data/footnotes.docx +0 -0
  95. package/test/test-data/hello.zip +0 -0
  96. package/test/test-data/hyperlinks/word/_rels/document.xml.rels +10 -0
  97. package/test/test-data/hyperlinks/word/document.xml +18 -0
  98. package/test/test-data/simple/word/document.xml +18 -0
  99. package/test/test-data/simple-list.docx +0 -0
  100. package/test/test-data/single-paragraph.docx +0 -0
  101. package/test/test-data/strict-format.docx +0 -0
  102. package/test/test-data/strikethrough.docx +0 -0
  103. package/test/test-data/tables.docx +0 -0
  104. package/test/test-data/text-box.docx +0 -0
  105. package/test/test-data/tiny-picture-target-base-relative.docx +0 -0
  106. package/test/test-data/tiny-picture.docx +0 -0
  107. package/test/test-data/tiny-picture.png +0 -0
  108. package/test/test-data/underline.docx +0 -0
  109. package/test/test-data/utf8-bom.docx +0 -0
  110. package/test/test.js +11 -0
  111. package/test/testing.js +55 -0
  112. package/test/transforms.tests.js +125 -0
  113. package/test/unzip.tests.js +38 -0
  114. package/test/writers/html-writer.tests.js +133 -0
  115. package/test/writers/markdown-writer.tests.js +304 -0
  116. package/test/xml/reader.tests.js +85 -0
  117. package/test/xml/writer.tests.js +81 -0
  118. 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
+ }