@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,798 @@
1
+ exports.createBodyReader = createBodyReader;
2
+ exports._readNumberingProperties = readNumberingProperties;
3
+
4
+ var dingbatToUnicode = require("dingbat-to-unicode");
5
+ var _ = require("underscore");
6
+
7
+ var documents = require("../documents");
8
+ var Result = require("../results").Result;
9
+ var warning = require("../results").warning;
10
+ var xml = require("../xml");
11
+ var transforms = require("../transforms");
12
+ var uris = require("./uris");
13
+
14
+ function createBodyReader(options) {
15
+ return {
16
+ readXmlElement: function(element) {
17
+ return new BodyReader(options).readXmlElement(element);
18
+ },
19
+ readXmlElements: function(elements) {
20
+ return new BodyReader(options).readXmlElements(elements);
21
+ }
22
+ };
23
+ }
24
+
25
+ function BodyReader(options) {
26
+ var complexFieldStack = [];
27
+ var currentInstrText = [];
28
+
29
+ // When a paragraph is marked as deleted, its contents should be combined
30
+ // with the following paragraph. See 17.13.5.15 del (Deleted Paragraph) of
31
+ // ECMA-376 4th edition Part 1.
32
+ var deletedParagraphContents = [];
33
+
34
+ var relationships = options.relationships;
35
+ var contentTypes = options.contentTypes;
36
+ var docxFile = options.docxFile;
37
+ var files = options.files;
38
+ var numbering = options.numbering;
39
+ var styles = options.styles;
40
+
41
+ function readXmlElements(elements) {
42
+ var results = elements.map(readXmlElement);
43
+ return combineResults(results);
44
+ }
45
+
46
+ function readXmlElement(element) {
47
+ if (element.type === "element") {
48
+ var handler = xmlElementReaders[element.name];
49
+ if (handler) {
50
+ return handler(element);
51
+ } else if (!Object.prototype.hasOwnProperty.call(ignoreElements, element.name)) {
52
+ var message = warning("An unrecognised element was ignored: " + element.name);
53
+ return emptyResultWithMessages([message]);
54
+ }
55
+ }
56
+ return emptyResult();
57
+ }
58
+
59
+ function readParagraphProperties(element) {
60
+ return readParagraphStyle(element).map(function(style) {
61
+ return {
62
+ type: "paragraphProperties",
63
+ styleId: style.styleId,
64
+ styleName: style.name,
65
+ alignment: element.firstOrEmpty("w:jc").attributes["w:val"],
66
+ numbering: readNumberingProperties(style.styleId, element.firstOrEmpty("w:numPr"), numbering),
67
+ indent: readParagraphIndent(element.firstOrEmpty("w:ind"))
68
+ };
69
+ });
70
+ }
71
+
72
+ function readParagraphIndent(element) {
73
+ return {
74
+ start: element.attributes["w:start"] || element.attributes["w:left"],
75
+ end: element.attributes["w:end"] || element.attributes["w:right"],
76
+ firstLine: element.attributes["w:firstLine"],
77
+ hanging: element.attributes["w:hanging"]
78
+ };
79
+ }
80
+
81
+ function readRunProperties(element) {
82
+ return readRunStyle(element).map(function(style) {
83
+ var fontSizeString = element.firstOrEmpty("w:sz").attributes["w:val"];
84
+ // w:sz gives the font size in half points, so halve the value to get the size in points
85
+ var fontSize = /^[0-9]+$/.test(fontSizeString) ? parseInt(fontSizeString, 10) / 2 : null;
86
+
87
+ return {
88
+ type: "runProperties",
89
+ styleId: style.styleId,
90
+ styleName: style.name,
91
+ verticalAlignment: element.firstOrEmpty("w:vertAlign").attributes["w:val"],
92
+ font: element.firstOrEmpty("w:rFonts").attributes["w:ascii"],
93
+ fontSize: fontSize,
94
+ isBold: readBooleanElement(element.first("w:b")),
95
+ isUnderline: readUnderline(element.first("w:u")),
96
+ isItalic: readBooleanElement(element.first("w:i")),
97
+ isStrikethrough: readBooleanElement(element.first("w:strike")),
98
+ isAllCaps: readBooleanElement(element.first("w:caps")),
99
+ isSmallCaps: readBooleanElement(element.first("w:smallCaps")),
100
+ highlight: readHighlightValue(element.firstOrEmpty("w:highlight").attributes["w:val"])
101
+ };
102
+ });
103
+ }
104
+
105
+ function readUnderline(element) {
106
+ if (element) {
107
+ var value = element.attributes["w:val"];
108
+ return value !== undefined && value !== "false" && value !== "0" && value !== "none";
109
+ } else {
110
+ return false;
111
+ }
112
+ }
113
+
114
+ function readBooleanElement(element) {
115
+ if (element) {
116
+ var value = element.attributes["w:val"];
117
+ return value !== "false" && value !== "0";
118
+ } else {
119
+ return false;
120
+ }
121
+ }
122
+
123
+ function readBooleanAttributeValue(value) {
124
+ return value !== "false" && value !== "0";
125
+ }
126
+
127
+ function readHighlightValue(value) {
128
+ if (!value || value === "none") {
129
+ return null;
130
+ } else {
131
+ return value;
132
+ }
133
+ }
134
+
135
+ function readParagraphStyle(element) {
136
+ return readStyle(element, "w:pStyle", "Paragraph", styles.findParagraphStyleById);
137
+ }
138
+
139
+ function readRunStyle(element) {
140
+ return readStyle(element, "w:rStyle", "Run", styles.findCharacterStyleById);
141
+ }
142
+
143
+ function readTableStyle(element) {
144
+ return readStyle(element, "w:tblStyle", "Table", styles.findTableStyleById);
145
+ }
146
+
147
+ function readStyle(element, styleTagName, styleType, findStyleById) {
148
+ var messages = [];
149
+ var styleElement = element.first(styleTagName);
150
+ var styleId = null;
151
+ var name = null;
152
+ if (styleElement) {
153
+ styleId = styleElement.attributes["w:val"];
154
+ if (styleId) {
155
+ var style = findStyleById(styleId);
156
+ if (style) {
157
+ name = style.name;
158
+ } else {
159
+ messages.push(undefinedStyleWarning(styleType, styleId));
160
+ }
161
+ }
162
+ }
163
+ return elementResultWithMessages({styleId: styleId, name: name}, messages);
164
+ }
165
+
166
+ function readFldChar(element) {
167
+ var type = element.attributes["w:fldCharType"];
168
+ if (type === "begin") {
169
+ complexFieldStack.push({type: "begin", fldChar: element});
170
+ currentInstrText = [];
171
+ } else if (type === "end") {
172
+ var complexFieldEnd = complexFieldStack.pop();
173
+ if (complexFieldEnd.type === "begin") {
174
+ complexFieldEnd = parseCurrentInstrText(complexFieldEnd);
175
+ }
176
+ if (complexFieldEnd.type === "checkbox") {
177
+ return elementResult(documents.checkbox({
178
+ checked: complexFieldEnd.checked
179
+ }));
180
+ }
181
+ } else if (type === "separate") {
182
+ var complexFieldSeparate = complexFieldStack.pop();
183
+ var complexField = parseCurrentInstrText(complexFieldSeparate);
184
+ complexFieldStack.push(complexField);
185
+ }
186
+ return emptyResult();
187
+ }
188
+
189
+ function currentHyperlinkOptions() {
190
+ var topHyperlink = _.last(complexFieldStack.filter(function(complexField) {
191
+ return complexField.type === "hyperlink";
192
+ }));
193
+ return topHyperlink ? topHyperlink.options : null;
194
+ }
195
+
196
+ function parseCurrentInstrText(complexField) {
197
+ return parseInstrText(
198
+ currentInstrText.join(''),
199
+ complexField.type === "begin"
200
+ ? complexField.fldChar
201
+ : xml.emptyElement
202
+ );
203
+ }
204
+
205
+ function parseInstrText(instrText, fldChar) {
206
+ var linkResult = /^\s*HYPERLINK\s+(\\l\s+)?(?:"(.*)"|([^\\]\S*))/.exec(instrText);
207
+ if (linkResult) {
208
+ var location = linkResult[2] === undefined ? linkResult[3] : linkResult[2];
209
+ var options = linkResult[1] === undefined ? {href: location} : {anchor: location};
210
+ return {type: "hyperlink", options: options};
211
+ }
212
+
213
+ var checkboxResult = /\s*FORMCHECKBOX\s*/.exec(instrText);
214
+ if (checkboxResult) {
215
+ var checkboxElement = fldChar
216
+ .firstOrEmpty("w:ffData")
217
+ .firstOrEmpty("w:checkBox");
218
+ var checkedElement = checkboxElement.first("w:checked");
219
+ var checked = checkedElement == null
220
+ ? readBooleanElement(checkboxElement.first("w:default"))
221
+ : readBooleanElement(checkedElement);
222
+ return {type: "checkbox", checked: checked};
223
+ }
224
+
225
+ return {type: "unknown"};
226
+ }
227
+
228
+ function readInstrText(element) {
229
+ currentInstrText.push(element.text());
230
+ return emptyResult();
231
+ }
232
+
233
+ function readSymbol(element) {
234
+ // See 17.3.3.30 sym (Symbol Character) of ECMA-376 4th edition Part 1
235
+ var font = element.attributes["w:font"];
236
+ var char = element.attributes["w:char"];
237
+ var unicodeCharacter = dingbatToUnicode.hex(font, char);
238
+ if (unicodeCharacter == null && /^F0..$/.test(char)) {
239
+ unicodeCharacter = dingbatToUnicode.hex(font, char.substring(2));
240
+ }
241
+
242
+ if (unicodeCharacter == null) {
243
+ return emptyResultWithMessages([warning(
244
+ "A w:sym element with an unsupported character was ignored: char " + char + " in font " + font
245
+ )]);
246
+ } else {
247
+ return elementResult(new documents.Text(unicodeCharacter.string));
248
+ }
249
+ }
250
+
251
+ function noteReferenceReader(noteType) {
252
+ return function(element) {
253
+ var noteId = element.attributes["w:id"];
254
+ return elementResult(new documents.NoteReference({
255
+ noteType: noteType,
256
+ noteId: noteId
257
+ }));
258
+ };
259
+ }
260
+
261
+ function readCommentReference(element) {
262
+ return elementResult(documents.commentReference({
263
+ commentId: element.attributes["w:id"]
264
+ }));
265
+ }
266
+
267
+ function readChildElements(element) {
268
+ return readXmlElements(element.children);
269
+ }
270
+
271
+ var xmlElementReaders = {
272
+ "w:p": function(element) {
273
+ var paragraphPropertiesElement = element.firstOrEmpty("w:pPr");
274
+
275
+ var isDeleted = !!paragraphPropertiesElement
276
+ .firstOrEmpty("w:rPr")
277
+ .first("w:del");
278
+
279
+ if (isDeleted) {
280
+ element.children.forEach(function(child) {
281
+ deletedParagraphContents.push(child);
282
+ });
283
+ return emptyResult();
284
+ } else {
285
+ var childrenXml = element.children;
286
+ if (deletedParagraphContents.length > 0) {
287
+ childrenXml = deletedParagraphContents.concat(childrenXml);
288
+ deletedParagraphContents = [];
289
+ }
290
+ return ReadResult.map(
291
+ readParagraphProperties(paragraphPropertiesElement),
292
+ readXmlElements(childrenXml),
293
+ function(properties, children) {
294
+ return new documents.Paragraph(children, properties);
295
+ }
296
+ ).insertExtra();
297
+ }
298
+ },
299
+ "w:r": function(element) {
300
+ return ReadResult.map(
301
+ readRunProperties(element.firstOrEmpty("w:rPr")),
302
+ readXmlElements(element.children),
303
+ function(properties, children) {
304
+ var hyperlinkOptions = currentHyperlinkOptions();
305
+ if (hyperlinkOptions !== null) {
306
+ children = [new documents.Hyperlink(children, hyperlinkOptions)];
307
+ }
308
+
309
+ return new documents.Run(children, properties);
310
+ }
311
+ );
312
+ },
313
+ "w:fldChar": readFldChar,
314
+ "w:instrText": readInstrText,
315
+ "w:t": function(element) {
316
+ return elementResult(new documents.Text(element.text()));
317
+ },
318
+ "w:tab": function(element) {
319
+ return elementResult(new documents.Tab());
320
+ },
321
+ "w:noBreakHyphen": function() {
322
+ return elementResult(new documents.Text("\u2011"));
323
+ },
324
+ "w:softHyphen": function(element) {
325
+ return elementResult(new documents.Text("\u00AD"));
326
+ },
327
+ "w:sym": readSymbol,
328
+ "w:hyperlink": function(element) {
329
+ var relationshipId = element.attributes["r:id"];
330
+ var anchor = element.attributes["w:anchor"];
331
+ return readXmlElements(element.children).map(function(children) {
332
+ function create(options) {
333
+ var targetFrame = element.attributes["w:tgtFrame"] || null;
334
+
335
+ return new documents.Hyperlink(
336
+ children,
337
+ _.extend({targetFrame: targetFrame}, options)
338
+ );
339
+ }
340
+
341
+ if (relationshipId) {
342
+ var href = relationships.findTargetByRelationshipId(relationshipId);
343
+ if (anchor) {
344
+ href = uris.replaceFragment(href, anchor);
345
+ }
346
+ return create({href: href});
347
+ } else if (anchor) {
348
+ return create({anchor: anchor});
349
+ } else {
350
+ return children;
351
+ }
352
+ });
353
+ },
354
+ "w:tbl": readTable,
355
+ "w:tr": readTableRow,
356
+ "w:tc": readTableCell,
357
+ "w:footnoteReference": noteReferenceReader("footnote"),
358
+ "w:endnoteReference": noteReferenceReader("endnote"),
359
+ "w:commentReference": readCommentReference,
360
+ "w:br": function(element) {
361
+ var breakType = element.attributes["w:type"];
362
+ if (breakType == null || breakType === "textWrapping") {
363
+ return elementResult(documents.lineBreak);
364
+ } else if (breakType === "page") {
365
+ return elementResult(documents.pageBreak);
366
+ } else if (breakType === "column") {
367
+ return elementResult(documents.columnBreak);
368
+ } else {
369
+ return emptyResultWithMessages([warning("Unsupported break type: " + breakType)]);
370
+ }
371
+ },
372
+ "w:bookmarkStart": function(element){
373
+ var name = element.attributes["w:name"];
374
+ if (name === "_GoBack") {
375
+ return emptyResult();
376
+ } else {
377
+ return elementResult(new documents.BookmarkStart({name: name}));
378
+ }
379
+ },
380
+
381
+ "mc:AlternateContent": function(element) {
382
+ return readChildElements(element.firstOrEmpty("mc:Fallback"));
383
+ },
384
+
385
+ "w:sdt": function(element) {
386
+ var contentResult = readXmlElements(element.firstOrEmpty("w:sdtContent").children);
387
+ return contentResult.map(function(content) {
388
+ // From the WordML standard: https://learn.microsoft.com/en-us/openspecs/office_standards/ms-docx/3350cb64-931f-41f7-8824-f18b2568ce66
389
+ //
390
+ // > A CT_SdtCheckbox element that specifies that the parent
391
+ // > structured document tag is a checkbox when displayed in the
392
+ // > document. The parent structured document tag contents MUST
393
+ // > contain a single character and optionally an additional
394
+ // > character in a deleted run.
395
+
396
+ var checkbox = element
397
+ .firstOrEmpty("w:sdtPr")
398
+ .first("wordml:checkbox");
399
+
400
+ if (checkbox) {
401
+ var checkedElement = checkbox.first("wordml:checked");
402
+ var isChecked = !!checkedElement && readBooleanAttributeValue(
403
+ checkedElement.attributes["wordml:val"]
404
+ );
405
+ var documentCheckbox = documents.checkbox({
406
+ checked: isChecked
407
+ });
408
+
409
+ var hasCheckbox = false;
410
+ var replacedContent = content.map(transforms._elementsOfType(
411
+ documents.types.text,
412
+ function(text) {
413
+ if (text.value.length > 0 && !hasCheckbox) {
414
+ hasCheckbox = true;
415
+ return documentCheckbox;
416
+ } else {
417
+ return text;
418
+ }
419
+ }
420
+ ));
421
+
422
+ if (hasCheckbox) {
423
+ return replacedContent;
424
+ } else {
425
+ return documentCheckbox;
426
+ }
427
+
428
+ } else {
429
+ return content;
430
+ }
431
+ });
432
+ },
433
+
434
+ "w:ins": readChildElements,
435
+ "w:object": readChildElements,
436
+ "w:smartTag": readChildElements,
437
+ "w:drawing": readChildElements,
438
+ "w:pict": function(element) {
439
+ return readChildElements(element).toExtra();
440
+ },
441
+ "v:roundrect": readChildElements,
442
+ "v:shape": readChildElements,
443
+ "v:textbox": readChildElements,
444
+ "w:txbxContent": readChildElements,
445
+ "wp:inline": readDrawingElement,
446
+ "wp:anchor": readDrawingElement,
447
+ "v:imagedata": readImageData,
448
+ "v:group": readChildElements,
449
+ "v:rect": readChildElements
450
+ };
451
+
452
+ return {
453
+ readXmlElement: readXmlElement,
454
+ readXmlElements: readXmlElements
455
+ };
456
+
457
+
458
+ function readTable(element) {
459
+ var propertiesResult = readTableProperties(element.firstOrEmpty("w:tblPr"));
460
+ return readXmlElements(element.children)
461
+ .flatMap(calculateRowSpans)
462
+ .flatMap(function(children) {
463
+ return propertiesResult.map(function(properties) {
464
+ return documents.Table(children, properties);
465
+ });
466
+ });
467
+ }
468
+
469
+ function readTableProperties(element) {
470
+ return readTableStyle(element).map(function(style) {
471
+ return {
472
+ styleId: style.styleId,
473
+ styleName: style.name
474
+ };
475
+ });
476
+ }
477
+
478
+ function readTableRow(element) {
479
+ var properties = element.firstOrEmpty("w:trPr");
480
+
481
+ // See 17.13.5.12 del (Deleted Table Row) of ECMA-376 4th edition Part 1
482
+ var isDeleted = !!properties.first("w:del");
483
+ if (isDeleted) {
484
+ return emptyResult();
485
+ }
486
+
487
+ var isHeader = !!properties.first("w:tblHeader");
488
+ return readXmlElements(element.children).map(function(children) {
489
+ return documents.TableRow(children, {isHeader: isHeader});
490
+ });
491
+ }
492
+
493
+ function readTableCell(element) {
494
+ return readXmlElements(element.children).map(function(children) {
495
+ var properties = element.firstOrEmpty("w:tcPr");
496
+
497
+ var gridSpan = properties.firstOrEmpty("w:gridSpan").attributes["w:val"];
498
+ var colSpan = gridSpan ? parseInt(gridSpan, 10) : 1;
499
+
500
+ var cell = documents.TableCell(children, {colSpan: colSpan});
501
+ cell._vMerge = readVMerge(properties);
502
+ return cell;
503
+ });
504
+ }
505
+
506
+ function readVMerge(properties) {
507
+ var element = properties.first("w:vMerge");
508
+ if (element) {
509
+ var val = element.attributes["w:val"];
510
+ return val === "continue" || !val;
511
+ } else {
512
+ return null;
513
+ }
514
+ }
515
+
516
+ function calculateRowSpans(rows) {
517
+ var unexpectedNonRows = _.any(rows, function(row) {
518
+ return row.type !== documents.types.tableRow;
519
+ });
520
+ if (unexpectedNonRows) {
521
+ removeVMergeProperties(rows);
522
+ return elementResultWithMessages(rows, [warning(
523
+ "unexpected non-row element in table, cell merging may be incorrect"
524
+ )]);
525
+ }
526
+ var unexpectedNonCells = _.any(rows, function(row) {
527
+ return _.any(row.children, function(cell) {
528
+ return cell.type !== documents.types.tableCell;
529
+ });
530
+ });
531
+ if (unexpectedNonCells) {
532
+ removeVMergeProperties(rows);
533
+ return elementResultWithMessages(rows, [warning(
534
+ "unexpected non-cell element in table row, cell merging may be incorrect"
535
+ )]);
536
+ }
537
+
538
+ var columns = {};
539
+
540
+ rows.forEach(function(row) {
541
+ var cellIndex = 0;
542
+ row.children.forEach(function(cell) {
543
+ if (cell._vMerge && columns[cellIndex]) {
544
+ columns[cellIndex].rowSpan++;
545
+ } else {
546
+ columns[cellIndex] = cell;
547
+ cell._vMerge = false;
548
+ }
549
+ cellIndex += cell.colSpan;
550
+ });
551
+ });
552
+
553
+ rows.forEach(function(row) {
554
+ row.children = row.children.filter(function(cell) {
555
+ return !cell._vMerge;
556
+ });
557
+ row.children.forEach(function(cell) {
558
+ delete cell._vMerge;
559
+ });
560
+ });
561
+
562
+ return elementResult(rows);
563
+ }
564
+
565
+ function removeVMergeProperties(rows) {
566
+ rows.forEach(function(row) {
567
+ var cells = transforms.getDescendantsOfType(row, documents.types.tableCell);
568
+ cells.forEach(function(cell) {
569
+ delete cell._vMerge;
570
+ });
571
+ });
572
+ }
573
+
574
+ function readDrawingElement(element) {
575
+ var blips = element
576
+ .getElementsByTagName("a:graphic")
577
+ .getElementsByTagName("a:graphicData")
578
+ .getElementsByTagName("pic:pic")
579
+ .getElementsByTagName("pic:blipFill")
580
+ .getElementsByTagName("a:blip");
581
+
582
+ return combineResults(blips.map(readBlip.bind(null, element)));
583
+ }
584
+
585
+ function readBlip(element, blip) {
586
+ var propertiesElement = element.firstOrEmpty("wp:docPr");
587
+ var properties = propertiesElement.attributes;
588
+
589
+ var altText = isBlank(properties.descr) ? properties.title : properties.descr;
590
+
591
+ var blipImageFile = findBlipImageFile(blip);
592
+ if (blipImageFile === null) {
593
+ return emptyResultWithMessages([warning("Could not find image file for a:blip element")]);
594
+ }
595
+
596
+ return readImage(blipImageFile, altText).map(function(imageElement) {
597
+ var hlinkClickElement = propertiesElement.firstOrEmpty("a:hlinkClick");
598
+ var relationshipId = hlinkClickElement.attributes["r:id"];
599
+ if (relationshipId) {
600
+ var href = relationships.findTargetByRelationshipId(relationshipId);
601
+ return new documents.Hyperlink([imageElement], {href: href});
602
+ } else {
603
+ return imageElement;
604
+ }
605
+ });
606
+ }
607
+
608
+ function isBlank(value) {
609
+ return value == null || /^\s*$/.test(value);
610
+ }
611
+
612
+ function findBlipImageFile(blip) {
613
+ var embedRelationshipId = blip.attributes["r:embed"];
614
+ var linkRelationshipId = blip.attributes["r:link"];
615
+ if (embedRelationshipId) {
616
+ return findEmbeddedImageFile(embedRelationshipId);
617
+ } else if (linkRelationshipId) {
618
+ var imagePath = relationships.findTargetByRelationshipId(linkRelationshipId);
619
+ return {
620
+ path: imagePath,
621
+ read: files.read.bind(files, imagePath)
622
+ };
623
+ } else {
624
+ return null;
625
+ }
626
+ }
627
+
628
+ function readImageData(element) {
629
+ var relationshipId = element.attributes['r:id'];
630
+
631
+ if (relationshipId) {
632
+ return readImage(
633
+ findEmbeddedImageFile(relationshipId),
634
+ element.attributes["o:title"]);
635
+ } else {
636
+ return emptyResultWithMessages([warning("A v:imagedata element without a relationship ID was ignored")]);
637
+ }
638
+ }
639
+
640
+ function findEmbeddedImageFile(relationshipId) {
641
+ var path = uris.uriToZipEntryName("word", relationships.findTargetByRelationshipId(relationshipId));
642
+ return {
643
+ path: path,
644
+ read: docxFile.read.bind(docxFile, path)
645
+ };
646
+ }
647
+
648
+ function readImage(imageFile, altText) {
649
+ var contentType = contentTypes.findContentType(imageFile.path);
650
+
651
+ var image = documents.Image({
652
+ readImage: imageFile.read,
653
+ altText: altText,
654
+ contentType: contentType
655
+ });
656
+ var warnings = supportedImageTypes[contentType] ?
657
+ [] : warning("Image of type " + contentType + " is unlikely to display in web browsers");
658
+ return elementResultWithMessages(image, warnings);
659
+ }
660
+
661
+ function undefinedStyleWarning(type, styleId) {
662
+ return warning(
663
+ type + " style with ID " + styleId + " was referenced but not defined in the document");
664
+ }
665
+ }
666
+
667
+
668
+ function readNumberingProperties(styleId, element, numbering) {
669
+ var level = element.firstOrEmpty("w:ilvl").attributes["w:val"];
670
+ var numId = element.firstOrEmpty("w:numId").attributes["w:val"];
671
+ if (level !== undefined && numId !== undefined) {
672
+ return numbering.findLevel(numId, level);
673
+ }
674
+
675
+ if (styleId != null) {
676
+ var levelByStyleId = numbering.findLevelByParagraphStyleId(styleId);
677
+ if (levelByStyleId != null) {
678
+ return levelByStyleId;
679
+ }
680
+ }
681
+
682
+ // Some malformed documents define numbering levels without an index, and
683
+ // reference the numbering using a w:numPr element without a w:ilvl child.
684
+ // To handle such cases, we assume a level of 0 as a fallback.
685
+ if (numId !== undefined) {
686
+ return numbering.findLevel(numId, "0");
687
+ }
688
+
689
+ return null;
690
+ }
691
+
692
+ var supportedImageTypes = {
693
+ "image/png": true,
694
+ "image/gif": true,
695
+ "image/jpeg": true,
696
+ "image/svg+xml": true,
697
+ "image/tiff": true
698
+ };
699
+
700
+ var ignoreElements = {
701
+ "office-word:wrap": true,
702
+ "v:shadow": true,
703
+ "v:shapetype": true,
704
+ "w:annotationRef": true,
705
+ "w:bookmarkEnd": true,
706
+ "w:sectPr": true,
707
+ "w:proofErr": true,
708
+ "w:lastRenderedPageBreak": true,
709
+ "w:commentRangeStart": true,
710
+ "w:commentRangeEnd": true,
711
+ "w:del": true,
712
+ "w:footnoteRef": true,
713
+ "w:endnoteRef": true,
714
+ "w:pPr": true,
715
+ "w:rPr": true,
716
+ "w:tblPr": true,
717
+ "w:tblGrid": true,
718
+ "w:trPr": true,
719
+ "w:tcPr": true
720
+ };
721
+
722
+ function emptyResultWithMessages(messages) {
723
+ return new ReadResult(null, null, messages);
724
+ }
725
+
726
+ function emptyResult() {
727
+ return new ReadResult(null);
728
+ }
729
+
730
+ function elementResult(element) {
731
+ return new ReadResult(element);
732
+ }
733
+
734
+ function elementResultWithMessages(element, messages) {
735
+ return new ReadResult(element, null, messages);
736
+ }
737
+
738
+ function ReadResult(element, extra, messages) {
739
+ this.value = element || [];
740
+ this.extra = extra || [];
741
+ this._result = new Result({
742
+ element: this.value,
743
+ extra: extra
744
+ }, messages);
745
+ this.messages = this._result.messages;
746
+ }
747
+
748
+ ReadResult.prototype.toExtra = function() {
749
+ return new ReadResult(null, joinElements(this.extra, this.value), this.messages);
750
+ };
751
+
752
+ ReadResult.prototype.insertExtra = function() {
753
+ var extra = this.extra;
754
+ if (extra && extra.length) {
755
+ return new ReadResult(joinElements(this.value, extra), null, this.messages);
756
+ } else {
757
+ return this;
758
+ }
759
+ };
760
+
761
+ ReadResult.prototype.map = function(func) {
762
+ var result = this._result.map(function(value) {
763
+ return func(value.element);
764
+ });
765
+ return new ReadResult(result.value, this.extra, result.messages);
766
+ };
767
+
768
+ ReadResult.prototype.flatMap = function(func) {
769
+ var result = this._result.flatMap(function(value) {
770
+ return func(value.element)._result;
771
+ });
772
+ return new ReadResult(result.value.element, joinElements(this.extra, result.value.extra), result.messages);
773
+ };
774
+
775
+ ReadResult.map = function(first, second, func) {
776
+ return new ReadResult(
777
+ func(first.value, second.value),
778
+ joinElements(first.extra, second.extra),
779
+ first.messages.concat(second.messages)
780
+ );
781
+ };
782
+
783
+ function combineResults(results) {
784
+ var result = Result.combine(_.pluck(results, "_result"));
785
+ return new ReadResult(
786
+ _.flatten(_.pluck(result.value, "element")),
787
+ _.filter(_.flatten(_.pluck(result.value, "extra")), identity),
788
+ result.messages
789
+ );
790
+ }
791
+
792
+ function joinElements(first, second) {
793
+ return _.flatten([first, second]);
794
+ }
795
+
796
+ function identity(value) {
797
+ return value;
798
+ }