@hyperlex/mammoth 1.4.9-beta
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 +77 -0
- package/.github/ISSUE_TEMPLATE.md +12 -0
- package/.idea/mammoth.js.iml +12 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/.travis.yml +10 -0
- package/LICENSE +22 -0
- package/NEWS +373 -0
- package/README.md +883 -0
- package/bin/mammoth +38 -0
- package/browser/docx/files.js +14 -0
- package/browser/unzip.js +12 -0
- package/lib/document-to-html.js +453 -0
- package/lib/documents.js +238 -0
- package/lib/docx/body-reader.js +636 -0
- package/lib/docx/comments-reader.js +31 -0
- package/lib/docx/content-types-reader.js +58 -0
- package/lib/docx/document-xml-reader.js +26 -0
- package/lib/docx/docx-reader.js +222 -0
- package/lib/docx/files.js +67 -0
- package/lib/docx/notes-reader.js +28 -0
- package/lib/docx/numbering-xml.js +69 -0
- package/lib/docx/office-xml-reader.js +58 -0
- package/lib/docx/relationships-reader.js +43 -0
- package/lib/docx/style-map.js +75 -0
- package/lib/docx/styles-reader.js +70 -0
- package/lib/docx/uris.js +21 -0
- package/lib/html/ast.js +50 -0
- package/lib/html/index.js +41 -0
- package/lib/html/simplify.js +88 -0
- package/lib/images.js +29 -0
- package/lib/index.js +115 -0
- package/lib/main.js +63 -0
- package/lib/options-reader.js +98 -0
- package/lib/promises.js +42 -0
- package/lib/results.js +72 -0
- package/lib/style-reader.js +321 -0
- package/lib/styles/document-matchers.js +74 -0
- package/lib/styles/html-paths.js +81 -0
- package/lib/styles/parser/tokeniser.js +30 -0
- package/lib/transforms.js +61 -0
- package/lib/underline.js +11 -0
- package/lib/unzip.js +22 -0
- package/lib/writers/html-writer.js +160 -0
- package/lib/writers/index.js +14 -0
- package/lib/writers/markdown-writer.js +163 -0
- package/lib/xml/index.js +7 -0
- package/lib/xml/nodes.js +69 -0
- package/lib/xml/reader.js +83 -0
- package/lib/xml/writer.js +61 -0
- package/lib/zipfile.js +77 -0
- package/mammoth.browser.js +32950 -0
- package/mammoth.browser.min.js +18 -0
- package/package.json +65 -0
- package/test/.eslintrc.json +7 -0
- package/test/document-to-html.tests.js +834 -0
- package/test/docx/body-reader.tests.js +1342 -0
- package/test/docx/comments-reader.tests.js +52 -0
- package/test/docx/content-types-reader.tests.js +45 -0
- package/test/docx/document-matchers.js +37 -0
- package/test/docx/docx-reader.tests.js +179 -0
- package/test/docx/files.tests.js +94 -0
- package/test/docx/notes-reader.tests.js +35 -0
- package/test/docx/numbering-xml.tests.js +65 -0
- package/test/docx/office-xml-reader.tests.js +24 -0
- package/test/docx/relationships-reader.tests.js +65 -0
- package/test/docx/style-map.tests.js +112 -0
- package/test/docx/styles-reader.tests.js +133 -0
- package/test/docx/uris.tests.js +22 -0
- package/test/html/simplify.tests.js +134 -0
- package/test/html/write.tests.js +42 -0
- package/test/images.tests.js +34 -0
- package/test/main.tests.js +89 -0
- package/test/mammoth.tests.js +429 -0
- package/test/mocha.opts +1 -0
- package/test/options-reader.tests.js +63 -0
- package/test/results.tests.js +15 -0
- package/test/style-reader.tests.js +256 -0
- package/test/styles/document-matchers.tests.js +71 -0
- package/test/styles/html-paths.tests.js +20 -0
- package/test/styles/parser/tokeniser.tests.js +104 -0
- package/test/test-data/comments.docx +0 -0
- package/test/test-data/embedded-style-map.docx +0 -0
- package/test/test-data/empty.docx +0 -0
- package/test/test-data/empty.zip +0 -0
- package/test/test-data/endnotes.docx +0 -0
- package/test/test-data/external-picture.docx +0 -0
- package/test/test-data/footnote-hyperlink.docx +0 -0
- package/test/test-data/footnotes.docx +0 -0
- package/test/test-data/hello.zip +0 -0
- package/test/test-data/hyperlinks/word/_rels/document.xml.rels +10 -0
- package/test/test-data/hyperlinks/word/document.xml +18 -0
- package/test/test-data/simple/word/document.xml +18 -0
- package/test/test-data/simple-list.docx +0 -0
- package/test/test-data/single-paragraph.docx +0 -0
- package/test/test-data/strikethrough.docx +0 -0
- package/test/test-data/tables.docx +0 -0
- package/test/test-data/text-box.docx +0 -0
- package/test/test-data/tiny-picture-target-base-relative.docx +0 -0
- package/test/test-data/tiny-picture.docx +0 -0
- package/test/test-data/tiny-picture.png +0 -0
- package/test/test-data/underline.docx +0 -0
- package/test/test-data/utf8-bom.docx +0 -0
- package/test/test.js +11 -0
- package/test/testing.js +55 -0
- package/test/transforms.tests.js +125 -0
- package/test/unzip.tests.js +38 -0
- package/test/writers/html-writer.tests.js +133 -0
- package/test/writers/markdown-writer.tests.js +304 -0
- package/test/xml/reader.tests.js +85 -0
- package/test/xml/writer.tests.js +81 -0
- package/test/zipfile.tests.js +59 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var JSZip = require("jszip");
|
|
4
|
+
|
|
5
|
+
var zipfile = require("../../lib/zipfile");
|
|
6
|
+
var styleMap = require("../../lib/docx/style-map");
|
|
7
|
+
var test = require("../test")(module);
|
|
8
|
+
|
|
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);
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
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");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
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");
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
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);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
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");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
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);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
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);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
var expectedRelationshipsXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
86
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
|
|
87
|
+
'<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>' +
|
|
88
|
+
'<Relationship Id="rMammothStyleMap" Type="http://schemas.zwobble.org/mammoth/style-map" Target="/mammoth/style-map"/>' +
|
|
89
|
+
'</Relationships>';
|
|
90
|
+
|
|
91
|
+
var expectedContentTypesXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
92
|
+
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
|
|
93
|
+
'<Default Extension="png" ContentType="image/png"/>' +
|
|
94
|
+
'<Override PartName="/mammoth/style-map" ContentType="text/prs.mammoth.style-map"/>' +
|
|
95
|
+
'</Types>';
|
|
96
|
+
|
|
97
|
+
function normalDocx() {
|
|
98
|
+
var zip = new JSZip();
|
|
99
|
+
var originalRelationshipsXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
100
|
+
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
|
|
101
|
+
'<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/>' +
|
|
102
|
+
'</Relationships>';
|
|
103
|
+
var originalContentTypesXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
104
|
+
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
|
|
105
|
+
'<Default Extension="png" ContentType="image/png"/>' +
|
|
106
|
+
'</Types>';
|
|
107
|
+
zip.file("word/_rels/document.xml.rels", originalRelationshipsXml);
|
|
108
|
+
zip.file("[Content_Types].xml", originalContentTypesXml);
|
|
109
|
+
var buffer = zip.generate({type: "arraybuffer"});
|
|
110
|
+
return zipfile.openArrayBuffer(buffer);
|
|
111
|
+
}
|
|
112
|
+
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var readStylesXml = require("../../lib/docx/styles-reader").readStylesXml;
|
|
4
|
+
var XmlElement = require("../../lib/xml").Element;
|
|
5
|
+
var test = require("../test")(module);
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
test('paragraph style is null if no style with that ID exists', function() {
|
|
9
|
+
var styles = readStylesXml(
|
|
10
|
+
new XmlElement("w:styles", {}, [])
|
|
11
|
+
);
|
|
12
|
+
assert.equal(styles.findParagraphStyleById("Heading1"), null);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('paragraph style can be found by ID', function() {
|
|
16
|
+
var styles = readStylesXml(
|
|
17
|
+
new XmlElement("w:styles", {}, [
|
|
18
|
+
paragraphStyleElement("Heading1", "Heading 1")
|
|
19
|
+
])
|
|
20
|
+
);
|
|
21
|
+
assert.equal(styles.findParagraphStyleById("Heading1").styleId, "Heading1");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('table style can be found by ID', function() {
|
|
25
|
+
var styles = readStylesXml(
|
|
26
|
+
new XmlElement("w:styles", {}, [
|
|
27
|
+
tableStyleElement("TableNormal", "Normal Table")
|
|
28
|
+
])
|
|
29
|
+
);
|
|
30
|
+
assert.equal(styles.findTableStyleById("TableNormal").styleId, "TableNormal");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('character style can be found by ID', function() {
|
|
34
|
+
var styles = readStylesXml(
|
|
35
|
+
new XmlElement("w:styles", {}, [
|
|
36
|
+
characterStyleElement("Heading1Char", "Heading 1 Char")
|
|
37
|
+
])
|
|
38
|
+
);
|
|
39
|
+
assert.equal(styles.findCharacterStyleById("Heading1Char").styleId, "Heading1Char");
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('paragraph and character styles are distinct', function() {
|
|
43
|
+
var styles = readStylesXml(
|
|
44
|
+
new XmlElement("w:styles", {}, [
|
|
45
|
+
paragraphStyleElement("Heading1", "Heading 1"),
|
|
46
|
+
characterStyleElement("Heading1Char", "Heading 1 Char")
|
|
47
|
+
])
|
|
48
|
+
);
|
|
49
|
+
assert.equal(styles.findCharacterStyleById("Heading1"), null);
|
|
50
|
+
assert.equal(styles.findParagraphStyleById("Heading1Char"), null);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('character and table styles are distinct', function() {
|
|
54
|
+
var styles = readStylesXml(
|
|
55
|
+
new XmlElement("w:styles", {}, [
|
|
56
|
+
tableStyleElement("Heading1", "Heading 1")
|
|
57
|
+
])
|
|
58
|
+
);
|
|
59
|
+
assert.equal(styles.findCharacterStyleById("Heading1"), null);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('styles include names', function() {
|
|
63
|
+
var styles = readStylesXml(
|
|
64
|
+
new XmlElement("w:styles", {}, [
|
|
65
|
+
paragraphStyleElement("Heading1", "Heading 1")
|
|
66
|
+
])
|
|
67
|
+
);
|
|
68
|
+
assert.equal(styles.findParagraphStyleById("Heading1").name, "Heading 1");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('style name is null if w:name element does not exist', function() {
|
|
72
|
+
var styles = readStylesXml(
|
|
73
|
+
new XmlElement("w:styles", {}, [
|
|
74
|
+
styleWithoutWNameElement("paragraph", "Heading1"),
|
|
75
|
+
styleWithoutWNameElement("character", "Heading1Char")
|
|
76
|
+
])
|
|
77
|
+
);
|
|
78
|
+
assert.equal(styles.findParagraphStyleById("Heading1").name, null);
|
|
79
|
+
assert.equal(styles.findCharacterStyleById("Heading1Char").name, null);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('numbering style is null if no style with that ID exists', function() {
|
|
83
|
+
var styles = readStylesXml(
|
|
84
|
+
new XmlElement("w:styles", {}, [])
|
|
85
|
+
);
|
|
86
|
+
assert.equal(styles.findNumberingStyleById("List1"), null);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('numbering style has null numId if style has no paragraph properties', function() {
|
|
90
|
+
var styles = readStylesXml(
|
|
91
|
+
new XmlElement("w:styles", {}, [
|
|
92
|
+
new XmlElement("w:style", {"w:type": "numbering", "w:styleId": "List1"})
|
|
93
|
+
])
|
|
94
|
+
);
|
|
95
|
+
assert.equal(styles.findNumberingStyleById("List1").numId, null);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('numbering style has numId read from paragraph properties', function() {
|
|
99
|
+
var styles = readStylesXml(
|
|
100
|
+
new XmlElement("w:styles", {}, [
|
|
101
|
+
new XmlElement("w:style", {"w:type": "numbering", "w:styleId": "List1"}, [
|
|
102
|
+
new XmlElement("w:pPr", {}, [
|
|
103
|
+
new XmlElement("w:numPr", {}, [
|
|
104
|
+
new XmlElement("w:numId", {"w:val": "42"})
|
|
105
|
+
])
|
|
106
|
+
])
|
|
107
|
+
])
|
|
108
|
+
])
|
|
109
|
+
);
|
|
110
|
+
assert.equal(styles.findNumberingStyleById("List1").numId, "42");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
function paragraphStyleElement(id, name) {
|
|
114
|
+
return styleElement("paragraph", id, name);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function characterStyleElement(id, name) {
|
|
118
|
+
return styleElement("character", id, name);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function tableStyleElement(id, name) {
|
|
122
|
+
return styleElement("table", id, name);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function styleElement(type, id, name) {
|
|
126
|
+
return new XmlElement("w:style", {"w:type": type, "w:styleId": id}, [
|
|
127
|
+
new XmlElement("w:name", {"w:val": name}, [])
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function styleWithoutWNameElement(type, id) {
|
|
132
|
+
return new XmlElement("w:style", {"w:type": type, "w:styleId": id}, []);
|
|
133
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var zipfile = require("../../lib/docx/uris");
|
|
4
|
+
var test = require("../test")(module);
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
test("uriToZipEntryName", {
|
|
8
|
+
"when path does not have leading slash then path is resolved relative to base": function() {
|
|
9
|
+
assert.equal(
|
|
10
|
+
zipfile.uriToZipEntryName("one/two", "three/four"),
|
|
11
|
+
"one/two/three/four"
|
|
12
|
+
);
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
"when path has leading slash then base is ignored": function() {
|
|
16
|
+
assert.equal(
|
|
17
|
+
zipfile.uriToZipEntryName("one/two", "/three/four"),
|
|
18
|
+
"three/four"
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var _ = require("underscore");
|
|
4
|
+
|
|
5
|
+
var test = require("../test")(module);
|
|
6
|
+
var html = require("../../lib/html");
|
|
7
|
+
var htmlPaths = require("../../lib/styles/html-paths");
|
|
8
|
+
|
|
9
|
+
var nonFreshElement = html.nonFreshElement;
|
|
10
|
+
var text = html.text;
|
|
11
|
+
|
|
12
|
+
test("empty text nodes are removed", function() {
|
|
13
|
+
assert.deepEqual(
|
|
14
|
+
simplifyNode(text("")),
|
|
15
|
+
[]
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("elements with no children are removed", function() {
|
|
20
|
+
assert.deepEqual(
|
|
21
|
+
simplifyNode(nonFreshElement("p", {}, [])),
|
|
22
|
+
[]
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("elements only containing empty nodes are removed", function() {
|
|
27
|
+
assert.deepEqual(
|
|
28
|
+
simplifyNode(nonFreshElement("p", {}, [text("")])),
|
|
29
|
+
[]
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("empty children of element are removed", function() {
|
|
34
|
+
assert.deepEqual(
|
|
35
|
+
simplifyNode(nonFreshElement("p", {}, [text("Hello"), text("")])),
|
|
36
|
+
[nonFreshElement("p", {}, [text("Hello")])]
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("successive fresh elements are not collapsed", function() {
|
|
41
|
+
var path = htmlPaths.elements([
|
|
42
|
+
htmlPaths.element("p", {}, {fresh: true})
|
|
43
|
+
]);
|
|
44
|
+
var original = concat(
|
|
45
|
+
pathToNodes(path, [text("Hello")]),
|
|
46
|
+
pathToNodes(path, [text(" there")])
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
assert.deepEqual(
|
|
50
|
+
html.simplify(original),
|
|
51
|
+
original);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("successive plain non-fresh elements are collapsed if they have the same tag name", function() {
|
|
55
|
+
var path = htmlPaths.elements([
|
|
56
|
+
htmlPaths.element("p", {}, {fresh: false})
|
|
57
|
+
]);
|
|
58
|
+
assert.deepEqual(
|
|
59
|
+
html.simplify(concat(
|
|
60
|
+
pathToNodes(path, [text("Hello")]),
|
|
61
|
+
pathToNodes(path, [text(" there")])
|
|
62
|
+
)),
|
|
63
|
+
pathToNodes(path, [text("Hello"), text(" there")])
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("non-fresh can collapse into preceding fresh element", function() {
|
|
68
|
+
var freshPath = htmlPaths.elements([
|
|
69
|
+
htmlPaths.element("p", {}, {fresh: true})]);
|
|
70
|
+
var nonFreshPath = htmlPaths.elements([
|
|
71
|
+
htmlPaths.element("p", {}, {fresh: false})]);
|
|
72
|
+
assert.deepEqual(
|
|
73
|
+
html.simplify(concat(
|
|
74
|
+
pathToNodes(freshPath, [text("Hello")]),
|
|
75
|
+
pathToNodes(nonFreshPath, [text(" there")])
|
|
76
|
+
)),
|
|
77
|
+
pathToNodes(freshPath, [text("Hello"), text(" there")])
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("children of collapsed element can collapse with children of another collapsed element", function() {
|
|
82
|
+
assert.deepEqual(
|
|
83
|
+
html.simplify([
|
|
84
|
+
nonFreshElement("blockquote", {}, [nonFreshElement("p", {}, [text("Hello")])]),
|
|
85
|
+
nonFreshElement("blockquote", {}, [nonFreshElement("p", {}, [text("there")])])
|
|
86
|
+
]),
|
|
87
|
+
[nonFreshElement("blockquote", {}, [nonFreshElement("p", {}, [text("Hello"), text("there")])])]
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("empty elements are removed before collapsing", function() {
|
|
92
|
+
var freshPath = htmlPaths.elements([
|
|
93
|
+
htmlPaths.element("p", {}, {fresh: true})]);
|
|
94
|
+
var nonFreshPath = htmlPaths.elements([
|
|
95
|
+
htmlPaths.element("p", {}, {fresh: false})]);
|
|
96
|
+
assert.deepEqual(
|
|
97
|
+
html.simplify(concat(
|
|
98
|
+
pathToNodes(nonFreshPath, [text("Hello")]),
|
|
99
|
+
pathToNodes(freshPath, []),
|
|
100
|
+
pathToNodes(nonFreshPath, [text(" there")])
|
|
101
|
+
)),
|
|
102
|
+
pathToNodes(nonFreshPath, [text("Hello"), text(" there")])
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("when separator is present then separator is prepended to collapsed element", function() {
|
|
107
|
+
var unseparatedPath = htmlPaths.elements([
|
|
108
|
+
htmlPaths.element("pre", {}, {fresh: false})
|
|
109
|
+
]);
|
|
110
|
+
var separatedPath = htmlPaths.elements([
|
|
111
|
+
htmlPaths.element("pre", {}, {fresh: false, separator: "\n"})
|
|
112
|
+
]);
|
|
113
|
+
assert.deepEqual(
|
|
114
|
+
html.simplify(concat(
|
|
115
|
+
pathToNodes(unseparatedPath, [text("Hello")]),
|
|
116
|
+
pathToNodes(separatedPath, [text(" the"), text("re")])
|
|
117
|
+
)),
|
|
118
|
+
pathToNodes(unseparatedPath, [text("Hello"), text("\n"), text(" the"), text("re")])
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
function simplifyNode(node) {
|
|
123
|
+
return html.simplify([node]);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function concat() {
|
|
127
|
+
return _.flatten(arguments, true);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function pathToNodes(path, nodes) {
|
|
131
|
+
return path.wrap(function() {
|
|
132
|
+
return nodes;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var test = require("../test")(module);
|
|
4
|
+
var html = require("../../lib/html");
|
|
5
|
+
var writers = require("../../lib/writers");
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
test("text is HTML escaped", function() {
|
|
9
|
+
assert.equal(
|
|
10
|
+
generateString(html.text("<>&")),
|
|
11
|
+
"<>&");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("double quotes outside of attributes are not escaped", function() {
|
|
15
|
+
assert.equal(
|
|
16
|
+
generateString(html.text('"')),
|
|
17
|
+
'"');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("element attributes are HTML escaped", function() {
|
|
21
|
+
assert.equal(
|
|
22
|
+
generateString(html.freshElement("p", {"x": "<"})),
|
|
23
|
+
'<p x="<"></p>');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("double quotes inside attributes are escaped", function() {
|
|
27
|
+
assert.equal(
|
|
28
|
+
generateString(html.freshElement("p", {"x": '"'})),
|
|
29
|
+
'<p x="""></p>');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("element children are written", function() {
|
|
33
|
+
assert.equal(
|
|
34
|
+
generateString(html.freshElement("p", {}, [html.text("Hello")])),
|
|
35
|
+
'<p>Hello</p>');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
function generateString(node) {
|
|
39
|
+
var writer = writers.writer();
|
|
40
|
+
html.write(writer, [node]);
|
|
41
|
+
return writer.asString();
|
|
42
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var hamjest = require("hamjest");
|
|
4
|
+
var assertThat = hamjest.assertThat;
|
|
5
|
+
var contains = hamjest.contains;
|
|
6
|
+
var hasProperties = hamjest.hasProperties;
|
|
7
|
+
|
|
8
|
+
var mammoth = require("../");
|
|
9
|
+
var documents = require("../lib/documents");
|
|
10
|
+
var promises = require("../lib/promises");
|
|
11
|
+
|
|
12
|
+
var test = require("./test")(module);
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
test('mammoth.images.inline() should be an alias of mammoth.images.imgElement()', function() {
|
|
16
|
+
assert.ok(mammoth.images.inline === mammoth.images.imgElement);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
test('mammoth.images.dataUri() encodes images in base64', function() {
|
|
21
|
+
var imageBuffer = new Buffer("abc");
|
|
22
|
+
var image = new documents.Image({
|
|
23
|
+
readImage: function(encoding) {
|
|
24
|
+
return promises.when(imageBuffer.toString(encoding));
|
|
25
|
+
},
|
|
26
|
+
contentType: "image/jpeg"
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return mammoth.images.dataUri(image).then(function(result) {
|
|
30
|
+
assertThat(result, contains(
|
|
31
|
+
hasProperties({tag: hasProperties({attributes: {"src": "data:image/jpeg;base64,YWJj"}})})
|
|
32
|
+
));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
var child_process = require("child_process"); // eslint-disable-line camelcase
|
|
3
|
+
var path = require("path");
|
|
4
|
+
var fs = require("fs");
|
|
5
|
+
|
|
6
|
+
var temp = require('temp').track();
|
|
7
|
+
|
|
8
|
+
var promises = require("../lib/promises");
|
|
9
|
+
var test = require("./test")(module);
|
|
10
|
+
var testPath = require("./testing").testPath;
|
|
11
|
+
|
|
12
|
+
test("HTML is printed to stdout if output file is not set", function() {
|
|
13
|
+
return runMammoth(testPath("single-paragraph.docx")).then(function(result) {
|
|
14
|
+
assert.equal(result.stderrOutput, "");
|
|
15
|
+
assert.equal(result.output, "<p>Walking on imported air</p>");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("HTML is written to file if output file is set", function() {
|
|
20
|
+
return createTempDir().then(function(tempDir) {
|
|
21
|
+
var outputPath = path.join(tempDir, "output.html");
|
|
22
|
+
return runMammoth(testPath("single-paragraph.docx"), outputPath).then(function(result) {
|
|
23
|
+
assert.equal(result.stderrOutput, "");
|
|
24
|
+
assert.equal(result.output, "");
|
|
25
|
+
assert.equal(fs.readFileSync(outputPath, "utf8"), "<p>Walking on imported air</p>");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
var imageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAAAXNSR0IArs4c6QAAAAlwSFlzAAAOvgAADr4B6kKxwAAAABNJREFUKFNj/M+ADzDhlWUYqdIAQSwBE8U+X40AAAAASUVORK5CYII=";
|
|
31
|
+
|
|
32
|
+
test("inline images are included in output if writing to single file", function() {
|
|
33
|
+
return runMammoth(testPath("tiny-picture.docx")).then(function(result) {
|
|
34
|
+
assert.equal(result.stderrOutput, "");
|
|
35
|
+
assert.equal(result.output, '<p><img src="data:image/png;base64,' + imageBase64 + '" /></p>');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("images are written to separate files if output dir is set", function() {
|
|
40
|
+
return createTempDir().then(function(tempDir) {
|
|
41
|
+
var outputPath = path.join(tempDir, "tiny-picture.html");
|
|
42
|
+
var imagePath = path.join(tempDir, "1.png");
|
|
43
|
+
return runMammoth(testPath("tiny-picture.docx"), "--output-dir", tempDir).then(function(result) {
|
|
44
|
+
assert.equal(result.stderrOutput, "");
|
|
45
|
+
assert.equal(result.output, "");
|
|
46
|
+
assert.equal(fs.readFileSync(outputPath, "utf8"), '<p><img src="1.png" /></p>');
|
|
47
|
+
assert.equal(fs.readFileSync(imagePath, "base64"), imageBase64);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("style map is used if set", function() {
|
|
53
|
+
return createTempDir().then(function(tempDir) {
|
|
54
|
+
var styleMapPath = path.join(tempDir, "style-map");
|
|
55
|
+
fs.writeFileSync(styleMapPath, "p => span:fresh");
|
|
56
|
+
return runMammoth(testPath("single-paragraph.docx"), "--style-map", styleMapPath).then(function(result) {
|
|
57
|
+
assert.equal(result.stderrOutput, "");
|
|
58
|
+
assert.equal(result.output, "<span>Walking on imported air</span>");
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("--output-format=markdown option generate markdown output", function() {
|
|
64
|
+
return runMammoth(testPath("single-paragraph.docx"), "--output-format=markdown").then(function(result) {
|
|
65
|
+
assert.equal(result.stderrOutput, "");
|
|
66
|
+
assert.equal(result.output, "Walking on imported air\n\n");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
function runMammoth() {
|
|
72
|
+
var args = Array.prototype.slice.call(arguments, 0);
|
|
73
|
+
var deferred = promises.defer();
|
|
74
|
+
|
|
75
|
+
var processArgs = ["node", "bin/mammoth"].concat(args);
|
|
76
|
+
// TODO: proper escaping of args
|
|
77
|
+
var command = processArgs.join(" ");
|
|
78
|
+
child_process.exec(command, function(error, stdout, stderr) { // eslint-disable-line camelcase
|
|
79
|
+
console.log(stderr); // eslint-disable-line no-console
|
|
80
|
+
assert.equal(error, null);
|
|
81
|
+
deferred.resolve({output: stdout, stderrOutput: stderr});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return deferred.promise;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function createTempDir() {
|
|
88
|
+
return promises.nfcall(temp.mkdir, null);
|
|
89
|
+
}
|