@hyperlex/mammoth 1.4.10 → 1.4.21

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 (41) hide show
  1. package/.eslintrc.json +0 -1
  2. package/.idea/compiler.xml +6 -0
  3. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  4. package/.idea/mammoth.js.iml +1 -5
  5. package/.idea/vcs.xml +1 -1
  6. package/.idea/workspace.xml +173 -0
  7. package/NEWS +55 -0
  8. package/README.md +39 -18
  9. package/lib/document-to-html.js +3 -0
  10. package/lib/documents.js +2 -0
  11. package/lib/docx/body-reader.js +74 -17
  12. package/lib/docx/numbering-xml.js +27 -4
  13. package/lib/index.d.ts +78 -0
  14. package/lib/index.js +7 -10
  15. package/lib/raw-text.js +14 -0
  16. package/lib/style-reader.js +15 -13
  17. package/lib/styles/document-matchers.js +1 -0
  18. package/lib/zipfile.js +26 -26
  19. package/mammoth.browser.js +10436 -19087
  20. package/mammoth.browser.min.js +21 -18
  21. package/package-lock.json +2654 -0
  22. package/package.json +11 -12
  23. package/test/document-to-html.tests.js +24 -0
  24. package/test/docx/body-reader.tests.js +170 -13
  25. package/test/docx/numbering-xml.tests.js +38 -0
  26. package/test/docx/style-map.tests.js +45 -44
  27. package/test/raw-text.tests.js +61 -0
  28. package/test/style-reader.tests.js +32 -25
  29. package/test/test-data/comments.docx +0 -0
  30. package/test/test-data/footnote-hyperlink.docx +0 -0
  31. package/test/test-data/footnotes.docx +0 -0
  32. package/test/test-data/simple-list.docx +0 -0
  33. package/test/test-data/single-paragraph.docx +0 -0
  34. package/test/test-data/strikethrough.docx +0 -0
  35. package/test/test-data/tables.docx +0 -0
  36. package/test/test-data/text-box.docx +0 -0
  37. package/test/test-data/tiny-picture.docx +0 -0
  38. package/test/test-data/underline.docx +0 -0
  39. package/test/zipfile.tests.js +12 -10
  40. package/.github/ISSUE_TEMPLATE.md +0 -12
  41. package/.travis.yml +0 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperlex/mammoth",
3
- "version": "1.4.10",
3
+ "version": "1.4.21",
4
4
  "author": "Michael Williamson <mike@zwobble.org>",
5
5
  "description": "Convert Word documents from docx to simple HTML and Markdown",
6
6
  "keywords": [
@@ -19,30 +19,29 @@
19
19
  "dependencies": {
20
20
  "argparse": "~1.0.3",
21
21
  "bluebird": "~3.4.0",
22
- "jszip": "~2.5.0",
23
- "lop": "~0.4.1",
22
+ "dingbat-to-unicode": "^1.0.1",
23
+ "jszip": "^3.7.1",
24
+ "lop": "^0.4.1",
24
25
  "path-is-absolute": "^1.0.0",
25
26
  "sax": "~1.1.1",
26
- "underscore": "~1.13.1",
27
+ "underscore": "^1.13.1",
27
28
  "xmlbuilder": "^10.0.0"
28
29
  },
29
30
  "devDependencies": {
30
31
  "browserify": "~13.0.1",
31
32
  "browserify-prepend-licenses": "~1.0.0",
32
- "duck": "~0.1.12",
33
+ "duck": "^0.1.12",
33
34
  "eslint": "^7.28.0",
34
35
  "hamjest": "^3.7.2",
35
36
  "mocha": "~7.2.0",
36
- "temp": "~0.9.4",
37
+ "temp": "^0.9.4",
37
38
  "uglify-js": "~2.8.29"
38
39
  },
39
40
  "browser": {
40
- "./lib/unzip.js": "./browser/unzip.js",
41
- "./lib/docx/files.js": "./browser/docx/files.js"
42
- },
43
- "bin": {
44
- "mammoth": "bin/mammoth"
41
+ "./lib/docx/files.js": "./browser/docx/files.js",
42
+ "./lib/unzip.js": "./browser/unzip.js"
45
43
  },
44
+ "bin": "bin/mammoth",
46
45
  "scripts": {
47
46
  "pretest": "eslint lib test",
48
47
  "test": "mocha 'test/**/*.tests.js'",
@@ -62,4 +61,4 @@
62
61
  "lib": "lib",
63
62
  "test": "test"
64
63
  }
65
- }
64
+ }
@@ -330,6 +330,30 @@ test('subscript runs are wrapped in <sub> tags', function() {
330
330
  });
331
331
  });
332
332
 
333
+ test('all caps runs are ignored by default', function() {
334
+ var run = runOfText("Hello.", {isAllCaps: true});
335
+ var converter = new DocumentConverter();
336
+ return converter.convertToHtml(run).then(function(result) {
337
+ assert.equal(result.value, "Hello.");
338
+ });
339
+ });
340
+
341
+ test('all caps runs can be configured with style mapping', function() {
342
+ var run = runOfText("Hello.", {isAllCaps: true});
343
+ var converter = new DocumentConverter({
344
+ styleMap: [
345
+ {
346
+ from: documentMatchers.allCaps,
347
+ to: htmlPaths.elements([htmlPaths.element("span")])
348
+ }
349
+ ]
350
+ });
351
+ return converter.convertToHtml(run).then(function(result) {
352
+ assert.equal(result.value, "<span>Hello.</span>");
353
+ });
354
+ });
355
+
356
+
333
357
  test('small caps runs are ignored by default', function() {
334
358
  var run = runOfText("Hello.", {isSmallCaps: true});
335
359
  var converter = new DocumentConverter();
@@ -24,7 +24,7 @@ var _readNumberingProperties = require("../../lib/docx/body-reader")._readNumber
24
24
  var documents = require("../../lib/documents");
25
25
  var xml = require("../../lib/xml");
26
26
  var XmlElement = xml.Element;
27
- var Numbering = require("../../lib/docx/numbering-xml").Numbering;
27
+ var defaultNumbering = require("../../lib/docx/numbering-xml").defaultNumbering;
28
28
  var Relationships = require("../../lib/docx/relationships-reader").Relationships;
29
29
  var Styles = require("../../lib/docx/styles-reader").Styles;
30
30
  var warning = require("../../lib/results").warning;
@@ -36,6 +36,7 @@ var createFakeDocxFile = testing.createFakeDocxFile;
36
36
  function readXmlElement(element, options) {
37
37
  options = Object.create(options || {});
38
38
  options.styles = options.styles || new Styles({}, {});
39
+ options.numbering = options.numbering || defaultNumbering;
39
40
  return createBodyReader(options).readXmlElement(element);
40
41
  }
41
42
 
@@ -207,21 +208,45 @@ test("paragraph has numbering properties from paragraph properties if present",
207
208
  var propertiesXml = new XmlElement("w:pPr", {}, [numberingPropertiesXml]);
208
209
  var paragraphXml = new XmlElement("w:p", {}, [propertiesXml]);
209
210
 
210
- var numbering = new NumberingMap({"42": {"1": {isOrdered: true, level: "1"}}});
211
+ var numbering = new NumberingMap({
212
+ findLevel: {"42": {"1": {isOrdered: true, level: "1"}}}
213
+ });
211
214
 
212
215
  var paragraph = readXmlElementValue(paragraphXml, {numbering: numbering});
213
216
  assert.deepEqual(paragraph.numbering, {level: "1", isOrdered: true});
214
217
  });
215
218
 
219
+ test("numbering on paragraph style takes precedence over numPr", function() {
220
+ var numberingPropertiesXml = new XmlElement("w:numPr", {}, [
221
+ new XmlElement("w:ilvl", {"w:val": "1"}),
222
+ new XmlElement("w:numId", {"w:val": "42"})
223
+ ]);
224
+ var propertiesXml = new XmlElement("w:pPr", {}, [
225
+ new XmlElement("w:pStyle", {"w:val": "List"}),
226
+ numberingPropertiesXml
227
+ ]);
228
+ var paragraphXml = new XmlElement("w:p", {}, [propertiesXml]);
229
+
230
+ var numbering = new NumberingMap({
231
+ findLevelByParagraphStyleId: {"List": {isOrdered: true, level: "1"}}
232
+ });
233
+ var styles = new Styles({"List": {name: "List"}}, {});
234
+
235
+ var paragraph = readXmlElementValue(paragraphXml, {numbering: numbering, styles: styles});
236
+ assert.deepEqual(paragraph.numbering, {level: "1", isOrdered: true});
237
+ });
238
+
216
239
  test("numbering properties are converted to numbering at specified level", function() {
217
240
  var numberingPropertiesXml = new XmlElement("w:numPr", {}, [
218
241
  new XmlElement("w:ilvl", {"w:val": "1"}),
219
242
  new XmlElement("w:numId", {"w:val": "42"})
220
243
  ]);
221
244
 
222
- var numbering = new NumberingMap({"42": {"1": {isOrdered: true, level: "1"}}});
245
+ var numbering = new NumberingMap({
246
+ findLevel: {"42": {"1": {isOrdered: true, level: "1"}}}
247
+ });
223
248
 
224
- var numberingLevel = _readNumberingProperties(numberingPropertiesXml, numbering);
249
+ var numberingLevel = _readNumberingProperties(null, numberingPropertiesXml, numbering);
225
250
  assert.deepEqual(numberingLevel, {level: "1", isOrdered: true});
226
251
  });
227
252
 
@@ -230,9 +255,11 @@ test("numbering properties are ignored if w:ilvl is missing", function() {
230
255
  new XmlElement("w:numId", {"w:val": "42"})
231
256
  ]);
232
257
 
233
- var numbering = new Numbering({"42": {"1": {isOrdered: true, level: "1"}}});
258
+ var numbering = new NumberingMap({
259
+ findLevel: {"42": {"1": {isOrdered: true, level: "1"}}}
260
+ });
234
261
 
235
- var numberingLevel = _readNumberingProperties(numberingPropertiesXml, numbering);
262
+ var numberingLevel = _readNumberingProperties(null, numberingPropertiesXml, numbering);
236
263
  assert.equal(numberingLevel, null);
237
264
  });
238
265
 
@@ -241,9 +268,11 @@ test("numbering properties are ignored if w:numId is missing", function() {
241
268
  new XmlElement("w:ilvl", {"w:val": "1"})
242
269
  ]);
243
270
 
244
- var numbering = new Numbering({"42": {"1": {isOrdered: true, level: "1"}}});
271
+ var numbering = new NumberingMap({
272
+ findLevel: {"42": {"1": {isOrdered: true, level: "1"}}}
273
+ });
245
274
 
246
- var numberingLevel = _readNumberingProperties(numberingPropertiesXml, numbering);
275
+ var numberingLevel = _readNumberingProperties(null, numberingPropertiesXml, numbering);
247
276
  assert.equal(numberingLevel, null);
248
277
  });
249
278
 
@@ -268,7 +297,7 @@ test("complex fields", (function() {
268
297
  function isHyperlinkedRun(hyperlinkProperties) {
269
298
  return isRun({
270
299
  children: contains(
271
- isHyperlink(_.extend({href: uri}, hyperlinkProperties))
300
+ isHyperlink(hyperlinkProperties)
272
301
  )
273
302
  });
274
303
  }
@@ -279,7 +308,7 @@ test("complex fields", (function() {
279
308
  assert.deepEqual(instrText, []);
280
309
  },
281
310
 
282
- "runs in a complex field for hyperlinks are read as hyperlinks": function() {
311
+ "runs in a complex field for hyperlink without switch are read as external hyperlinks": function() {
283
312
  var hyperlinkRunXml = runOfText("this is a hyperlink");
284
313
  var paragraphXml = new XmlElement("w:p", {}, [
285
314
  beginXml,
@@ -294,6 +323,33 @@ test("complex fields", (function() {
294
323
  isEmptyRun,
295
324
  isEmptyHyperlinkedRun,
296
325
  isHyperlinkedRun({
326
+ href: uri,
327
+ children: contains(
328
+ isText("this is a hyperlink")
329
+ )
330
+ }),
331
+ isEmptyRun
332
+ ));
333
+ },
334
+
335
+ "runs in a complex field for hyperlink with l switch are read as internal hyperlinks": function() {
336
+ var hyperlinkRunXml = runOfText("this is a hyperlink");
337
+ var paragraphXml = new XmlElement("w:p", {}, [
338
+ beginXml,
339
+ new XmlElement("w:instrText", {}, [
340
+ xml.text(' HYPERLINK \\l "InternalLink"')
341
+ ]),
342
+ separateXml,
343
+ hyperlinkRunXml,
344
+ endXml
345
+ ]);
346
+ var paragraph = readXmlElementValue(paragraphXml);
347
+
348
+ assertThat(paragraph.children, contains(
349
+ isEmptyRun,
350
+ isEmptyHyperlinkedRun,
351
+ isHyperlinkedRun({
352
+ anchor: "InternalLink",
297
353
  children: contains(
298
354
  isText("this is a hyperlink")
299
355
  )
@@ -346,6 +402,7 @@ test("complex fields", (function() {
346
402
  isEmptyRun,
347
403
  isEmptyHyperlinkedRun,
348
404
  isHyperlinkedRun({
405
+ href: uri,
349
406
  children: contains(
350
407
  isText("this is a hyperlink")
351
408
  )
@@ -378,6 +435,7 @@ test("complex fields", (function() {
378
435
  isEmptyHyperlinkedRun,
379
436
  isEmptyHyperlinkedRun,
380
437
  isHyperlinkedRun({
438
+ href: uri,
381
439
  children: contains(
382
440
  isText("this is a hyperlink")
383
441
  )
@@ -409,6 +467,7 @@ test("complex fields", (function() {
409
467
  isEmptyHyperlinkedRun,
410
468
  isEmptyHyperlinkedRun,
411
469
  isHyperlinkedRun({
470
+ href: uri,
412
471
  children: contains(
413
472
  isText("John Doe")
414
473
  )
@@ -437,6 +496,7 @@ test("complex fields", (function() {
437
496
  isEmptyHyperlinkedRun,
438
497
  isEmptyHyperlinkedRun,
439
498
  isHyperlinkedRun({
499
+ href: uri,
440
500
  children: contains(
441
501
  isText("this is a hyperlink")
442
502
  )
@@ -503,10 +563,38 @@ test("isUnderline is false if underline element is not present", function() {
503
563
  assert.deepEqual(run.isUnderline, false);
504
564
  });
505
565
 
506
- test("isUnderline is true if underline element is present", function() {
566
+ test("isUnderline is false if underline element is present without w:val attribute", function() {
507
567
  var underlineXml = new XmlElement("w:u");
508
568
  var runXml = runWithProperties([underlineXml]);
509
569
  var run = readXmlElementValue(runXml);
570
+ assert.equal(run.isUnderline, false);
571
+ });
572
+
573
+ test("isUnderline is false if underline element is present and w:val is false", function() {
574
+ var underlineXml = new XmlElement("w:u", {"w:val": "false"});
575
+ var runXml = runWithProperties([underlineXml]);
576
+ var run = readXmlElementValue(runXml);
577
+ assert.equal(run.isUnderline, false);
578
+ });
579
+
580
+ test("isUnderline is false if underline element is present and w:val is 0", function() {
581
+ var underlineXml = new XmlElement("w:u", {"w:val": "0"});
582
+ var runXml = runWithProperties([underlineXml]);
583
+ var run = readXmlElementValue(runXml);
584
+ assert.equal(run.isUnderline, false);
585
+ });
586
+
587
+ test("isUnderline is false if underline element is present and w:val is none", function() {
588
+ var underlineXml = new XmlElement("w:u", {"w:val": "none"});
589
+ var runXml = runWithProperties([underlineXml]);
590
+ var run = readXmlElementValue(runXml);
591
+ assert.equal(run.isUnderline, false);
592
+ });
593
+
594
+ test("isUnderline is true if underline element is present and w:val is not none or falsy", function() {
595
+ var underlineXml = new XmlElement("w:u", {"w:val": "single"});
596
+ var runXml = runWithProperties([underlineXml]);
597
+ var run = readXmlElementValue(runXml);
510
598
  assert.equal(run.isUnderline, true);
511
599
  });
512
600
 
@@ -554,6 +642,7 @@ var booleanRunProperties = [
554
642
  {name: "isUnderline", tagName: "w:u"},
555
643
  {name: "isItalic", tagName: "w:i"},
556
644
  {name: "isStrikethrough", tagName: "w:strike"},
645
+ {name: "isAllCaps", tagName: "w:caps"},
557
646
  {name: "isSmallCaps", tagName: "w:smallCaps"}
558
647
  ];
559
648
 
@@ -632,6 +721,29 @@ test("run has highlight read from properties", function() {
632
721
  assert.deepEqual(run.highlight, "FFFFFF");
633
722
  });
634
723
 
724
+ test("run has null fontSize by default", function() {
725
+ var runXml = runWithProperties([]);
726
+
727
+ var run = readXmlElementValue(runXml);
728
+ assert.deepEqual(run.fontSize, null);
729
+ });
730
+
731
+ test("run has fontSize read from properties", function() {
732
+ var fontSizeXml = new XmlElement("w:sz", {"w:val": "28"});
733
+ var runXml = runWithProperties([fontSizeXml]);
734
+
735
+ var run = readXmlElementValue(runXml);
736
+ assert.deepEqual(run.fontSize, 14);
737
+ });
738
+
739
+ test("run with invalid w:sz has null font size", function() {
740
+ var fontSizeXml = new XmlElement("w:sz", {"w:val": "28a"});
741
+ var runXml = runWithProperties([fontSizeXml]);
742
+
743
+ var run = readXmlElementValue(runXml);
744
+ assert.deepEqual(run.fontSize, null);
745
+ });
746
+
635
747
  test("run properties not included as child of run", function() {
636
748
  var runStyleXml = new XmlElement("w:rStyle");
637
749
  var runPropertiesXml = new XmlElement("w:rPr", {}, [runStyleXml]);
@@ -652,6 +764,33 @@ test("w:noBreakHyphen is read as non-breaking hyphen character", function() {
652
764
  assert.deepEqual(result.value, new documents.Text("\u2011"));
653
765
  });
654
766
 
767
+ test("soft hyphens are read as text", function() {
768
+ var element = new XmlElement("w:softHyphen", {}, []);
769
+ var text = readXmlElementValue(element);
770
+ assert.deepEqual(text, new documents.Text("\u00AD"));
771
+ });
772
+
773
+ test("w:sym with supported font and supported code point in ASCII range is converted to text", function() {
774
+ var element = new XmlElement("w:sym", {"w:font": "Wingdings", "w:char": "28"}, []);
775
+ var text = readXmlElementValue(element);
776
+ assert.deepEqual(text, new documents.Text("🕿"));
777
+ });
778
+
779
+ test("w:sym with supported font and supported code point in private use area is converted to text", function() {
780
+ var element = new XmlElement("w:sym", {"w:font": "Wingdings", "w:char": "F028"}, []);
781
+ var text = readXmlElementValue(element);
782
+ assert.deepEqual(text, new documents.Text("🕿"));
783
+ });
784
+
785
+ test("w:sym with unsupported font and code point produces empty result with warning", function() {
786
+ var element = new XmlElement("w:sym", {"w:font": "Dingwings", "w:char": "28"}, []);
787
+
788
+ var result = readXmlElement(element);
789
+
790
+ assert.deepEqual(result.value, []);
791
+ assert.deepEqual(result.messages, [warning("A w:sym element with an unsupported character was ignored: char 28 in font Dingwings")]);
792
+ });
793
+
655
794
  test("w:tbl is read as document table element", function() {
656
795
  var tableXml = new XmlElement("w:tbl", {}, [
657
796
  new XmlElement("w:tr", {}, [
@@ -1015,6 +1154,18 @@ test("can read linked pictures", function() {
1015
1154
  }));
1016
1155
  });
1017
1156
 
1157
+ test("warning if blip has no image file", function() {
1158
+ var drawing = createInlineImage({
1159
+ blip: new XmlElement("a:blip"),
1160
+ description: "It's a hat"
1161
+ });
1162
+
1163
+ var result = readXmlElement(drawing);
1164
+
1165
+ assert.deepEqual(result.messages, [warning("Could not find image file for a:blip element")]);
1166
+ assert.deepEqual(result.value, []);
1167
+ });
1168
+
1018
1169
  test("warning if unsupported image type", function() {
1019
1170
  var drawing = createInlineImage({
1020
1171
  blip: createEmbeddedBlip("rId5"),
@@ -1333,10 +1484,16 @@ function imageRelationship(relationshipId, target) {
1333
1484
  };
1334
1485
  }
1335
1486
 
1336
- function NumberingMap(nums) {
1487
+ function NumberingMap(options) {
1488
+ var findLevel = options.findLevel;
1489
+ var findLevelByParagraphStyleId = options.findLevelByParagraphStyleId || {};
1490
+
1337
1491
  return {
1338
1492
  findLevel: function(numId, level) {
1339
- return nums[numId][level];
1493
+ return findLevel[numId][level];
1494
+ },
1495
+ findLevelByParagraphStyleId: function(styleId) {
1496
+ return findLevelByParagraphStyleId[styleId];
1340
1497
  }
1341
1498
  };
1342
1499
  }
@@ -33,6 +33,19 @@ test('w:num element inherits levels from w:abstractNum', function() {
33
33
  });
34
34
 
35
35
 
36
+ test('w:num element referencing non-existent w:abstractNumId is ignored', function() {
37
+ var numbering = readNumberingXml(
38
+ new XmlElement("w:numbering", {}, [
39
+ new XmlElement("w:num", {"w:numId": "47"}, [
40
+ new XmlElement("w:abstractNumId", {"w:val": "42"})
41
+ ])
42
+ ]),
43
+ {styles: stylesReader.defaultStyles}
44
+ );
45
+ duck.assertThat(numbering.findLevel("47", "0"), duck.equalTo(null));
46
+ });
47
+
48
+
36
49
  test('when w:abstractNum has w:numStyleLink then style is used to find w:num', function() {
37
50
  var numbering = readNumberingXml(
38
51
  new XmlElement("w:numbering", {}, [
@@ -58,6 +71,31 @@ test('when w:abstractNum has w:numStyleLink then style is used to find w:num', f
58
71
  }));
59
72
  });
60
73
 
74
+
75
+ // See: 17.9.23 pStyle (Paragraph Style's Associated Numbering Level) in ECMA-376, 4th Edition
76
+ test('numbering level can be found by paragraph style ID', function() {
77
+ var numbering = readNumberingXml(
78
+ new XmlElement("w:numbering", {}, [
79
+ new XmlElement("w:abstractNum", {"w:abstractNumId": "42"}, [
80
+ new XmlElement("w:lvl", {"w:ilvl": "0"}, [
81
+ new XmlElement("w:numFmt", {"w:val": "bullet"})
82
+ ])
83
+ ]),
84
+ new XmlElement("w:abstractNum", {"w:abstractNumId": "43"}, [
85
+ new XmlElement("w:lvl", {"w:ilvl": "0"}, [
86
+ new XmlElement("w:pStyle", {"w:val": "List"}),
87
+ new XmlElement("w:numFmt", {"w:val": "decimal"})
88
+ ])
89
+ ])
90
+ ]),
91
+ {styles: stylesReader.defaultStyles}
92
+ );
93
+ duck.assertThat(numbering.findLevelByParagraphStyleId("List"), duck.hasProperties({
94
+ isOrdered: true
95
+ }));
96
+ duck.assertThat(numbering.findLevelByParagraphStyleId("Paragraph"), duck.equalTo(null));
97
+ });
98
+
61
99
  test('when styles is missing then error is thrown', function() {
62
100
  assert.throws(function() {
63
101
  readNumberingXml(new XmlElement("w:numbering", {}, []));
@@ -7,77 +7,77 @@ var styleMap = require("../../lib/docx/style-map");
7
7
  var test = require("../test")(module);
8
8
 
9
9
  test('reading embedded style map on document without embedded style map returns null', function() {
10
- var zip = normalDocx();
11
-
12
- return styleMap.readStyleMap(zip).then(function(contents) {
13
- assert.equal(contents, null);
10
+ return normalDocx().then(function(zip) {
11
+ return styleMap.readStyleMap(zip).then(function(contents) {
12
+ assert.equal(contents, null);
13
+ });
14
14
  });
15
15
  });
16
16
 
17
17
  test('embedded style map can be read after being written', function() {
18
- var zip = normalDocx();
19
-
20
- return styleMap.writeStyleMap(zip, "p => h1").then(function() {
21
- return styleMap.readStyleMap(zip).then(function(contents) {
22
- assert.equal(contents, "p => h1");
18
+ return normalDocx().then(function(zip) {
19
+ return styleMap.writeStyleMap(zip, "p => h1").then(function() {
20
+ return styleMap.readStyleMap(zip).then(function(contents) {
21
+ assert.equal(contents, "p => h1");
22
+ });
23
23
  });
24
24
  });
25
25
  });
26
26
 
27
27
  test('embedded style map is written to separate file', function() {
28
- var zip = normalDocx();
29
-
30
- return styleMap.writeStyleMap(zip, "p => h1").then(function() {
31
- return zip.read("mammoth/style-map", "utf8").then(function(contents) {
32
- assert.equal(contents, "p => h1");
28
+ return normalDocx().then(function(zip) {
29
+ return styleMap.writeStyleMap(zip, "p => h1").then(function() {
30
+ return zip.read("mammoth/style-map", "utf8").then(function(contents) {
31
+ assert.equal(contents, "p => h1");
32
+ });
33
33
  });
34
34
  });
35
35
  });
36
36
 
37
37
  test('embedded style map is referenced in relationships', function() {
38
- var zip = normalDocx();
39
-
40
- return styleMap.writeStyleMap(zip, "p => h1").then(function() {
41
- return zip.read("word/_rels/document.xml.rels", "utf8").then(function(contents) {
42
- assert.equal(contents, expectedRelationshipsXml);
38
+ return normalDocx().then(function(zip) {
39
+ return styleMap.writeStyleMap(zip, "p => h1").then(function() {
40
+ return zip.read("word/_rels/document.xml.rels", "utf8").then(function(contents) {
41
+ assert.equal(contents, expectedRelationshipsXml);
42
+ });
43
43
  });
44
44
  });
45
45
  });
46
46
 
47
47
  test('re-embedding style map replaces original', function() {
48
- var zip = normalDocx();
49
-
50
- return styleMap.writeStyleMap(zip, "p => h1").then(function() {
51
- return styleMap.writeStyleMap(zip, "p => h2");
52
- }).then(function() {
53
- return zip.read("word/_rels/document.xml.rels", "utf8").then(function(contents) {
54
- assert.equal(contents, expectedRelationshipsXml);
55
- });
56
- }).then(function() {
57
- return styleMap.readStyleMap(zip).then(function(contents) {
58
- assert.equal(contents, "p => h2");
48
+ return normalDocx().then(function(zip) {
49
+ return styleMap.writeStyleMap(zip, "p => h1").then(function() {
50
+ return styleMap.writeStyleMap(zip, "p => h2");
51
+ }).then(function() {
52
+ return zip.read("word/_rels/document.xml.rels", "utf8").then(function(contents) {
53
+ assert.equal(contents, expectedRelationshipsXml);
54
+ });
55
+ }).then(function() {
56
+ return styleMap.readStyleMap(zip).then(function(contents) {
57
+ assert.equal(contents, "p => h2");
58
+ });
59
59
  });
60
60
  });
61
61
  });
62
62
 
63
63
  test('embedded style map has override content type in [Content_Types].xml', function() {
64
- var zip = normalDocx();
65
-
66
- return styleMap.writeStyleMap(zip, "p => h1").then(function() {
67
- return zip.read("[Content_Types].xml", "utf8").then(function(contents) {
68
- assert.equal(contents, expectedContentTypesXml);
64
+ return normalDocx().then(function(zip) {
65
+ return styleMap.writeStyleMap(zip, "p => h1").then(function() {
66
+ return zip.read("[Content_Types].xml", "utf8").then(function(contents) {
67
+ assert.equal(contents, expectedContentTypesXml);
68
+ });
69
69
  });
70
70
  });
71
71
  });
72
72
 
73
73
  test('replacing style map keeps content type', function() {
74
- var zip = normalDocx();
75
-
76
- return styleMap.writeStyleMap(zip, "p => h1").then(function() {
77
- return styleMap.writeStyleMap(zip, "p => h2");
78
- }).then(function() {
79
- return zip.read("[Content_Types].xml", "utf8").then(function(contents) {
80
- assert.equal(contents, expectedContentTypesXml);
74
+ return normalDocx().then(function(zip) {
75
+ return styleMap.writeStyleMap(zip, "p => h1").then(function() {
76
+ return styleMap.writeStyleMap(zip, "p => h2");
77
+ }).then(function() {
78
+ return zip.read("[Content_Types].xml", "utf8").then(function(contents) {
79
+ assert.equal(contents, expectedContentTypesXml);
80
+ });
81
81
  });
82
82
  });
83
83
  });
@@ -106,7 +106,8 @@ function normalDocx() {
106
106
  '</Types>';
107
107
  zip.file("word/_rels/document.xml.rels", originalRelationshipsXml);
108
108
  zip.file("[Content_Types].xml", originalContentTypesXml);
109
- var buffer = zip.generate({type: "arraybuffer"});
110
- return zipfile.openArrayBuffer(buffer);
109
+ return zip.generateAsync({type: "arraybuffer"}).then(function(buffer) {
110
+ return zipfile.openArrayBuffer(buffer);
111
+ });
111
112
  }
112
113
 
@@ -0,0 +1,61 @@
1
+ var assert = require("assert");
2
+
3
+ var documents = require("../lib/documents");
4
+ var test = require("./test")(module);
5
+ var convertElementToRawText = require("../lib/raw-text").convertElementToRawText;
6
+
7
+
8
+ test('text element is converted to text content', function() {
9
+ var element = new documents.Text("Hello.");
10
+
11
+ var result = convertElementToRawText(element);
12
+
13
+ assert.strictEqual(result, "Hello.");
14
+ });
15
+
16
+ test('tab element is converted to tab character', function() {
17
+ var element = documents.tab();
18
+
19
+ var result = convertElementToRawText(element);
20
+
21
+ assert.strictEqual(result, "\t");
22
+ });
23
+
24
+ test('paragraphs are terminated with newlines', function() {
25
+ var element = new documents.Paragraph(
26
+ [
27
+ new documents.Text("Hello "),
28
+ new documents.Text("world.")
29
+ ],
30
+ {}
31
+ );
32
+
33
+ var result = convertElementToRawText(element);
34
+
35
+ assert.strictEqual(result, "Hello world.\n\n");
36
+ });
37
+
38
+ test('children are recursively converted to text', function() {
39
+ var element = new documents.Document([
40
+ new documents.Paragraph(
41
+ [
42
+ new documents.Text("Hello "),
43
+ new documents.Text("world.")
44
+ ],
45
+ {}
46
+ )
47
+ ]);
48
+
49
+ var result = convertElementToRawText(element);
50
+
51
+ assert.strictEqual(result, "Hello world.\n\n");
52
+ });
53
+
54
+
55
+ test('non-text element without children is converted to empty string', function() {
56
+ var element = documents.lineBreak;
57
+
58
+ var result = convertElementToRawText(element);
59
+
60
+ assert.strictEqual(result, "");
61
+ });