@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.
- package/.eslintrc.json +0 -1
- package/.idea/compiler.xml +6 -0
- package/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/mammoth.js.iml +1 -5
- package/.idea/vcs.xml +1 -1
- package/.idea/workspace.xml +173 -0
- package/NEWS +55 -0
- package/README.md +39 -18
- package/lib/document-to-html.js +3 -0
- package/lib/documents.js +2 -0
- package/lib/docx/body-reader.js +74 -17
- package/lib/docx/numbering-xml.js +27 -4
- package/lib/index.d.ts +78 -0
- package/lib/index.js +7 -10
- package/lib/raw-text.js +14 -0
- package/lib/style-reader.js +15 -13
- package/lib/styles/document-matchers.js +1 -0
- package/lib/zipfile.js +26 -26
- package/mammoth.browser.js +10436 -19087
- package/mammoth.browser.min.js +21 -18
- package/package-lock.json +2654 -0
- package/package.json +11 -12
- package/test/document-to-html.tests.js +24 -0
- package/test/docx/body-reader.tests.js +170 -13
- package/test/docx/numbering-xml.tests.js +38 -0
- package/test/docx/style-map.tests.js +45 -44
- package/test/raw-text.tests.js +61 -0
- package/test/style-reader.tests.js +32 -25
- package/test/test-data/comments.docx +0 -0
- package/test/test-data/footnote-hyperlink.docx +0 -0
- package/test/test-data/footnotes.docx +0 -0
- package/test/test-data/simple-list.docx +0 -0
- package/test/test-data/single-paragraph.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.docx +0 -0
- package/test/test-data/underline.docx +0 -0
- package/test/zipfile.tests.js +12 -10
- package/.github/ISSUE_TEMPLATE.md +0 -12
- package/.travis.yml +0 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hyperlex/mammoth",
|
|
3
|
-
"version": "1.4.
|
|
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
|
-
"
|
|
23
|
-
"
|
|
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": "
|
|
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": "
|
|
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": "
|
|
37
|
+
"temp": "^0.9.4",
|
|
37
38
|
"uglify-js": "~2.8.29"
|
|
38
39
|
},
|
|
39
40
|
"browser": {
|
|
40
|
-
"./lib/
|
|
41
|
-
"./lib/
|
|
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
|
|
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({
|
|
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({
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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
|
+
});
|