@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,52 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var createCommentsReader = require("../../lib/docx/comments-reader").createCommentsReader;
|
|
4
|
+
var createBodyReader = require("../../lib/docx/body-reader").createBodyReader;
|
|
5
|
+
var documents = require("../../lib/documents");
|
|
6
|
+
var xml = require("../../lib/xml");
|
|
7
|
+
var test = require("../test")(module);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
function readComment(element) {
|
|
11
|
+
var bodyReader = createBodyReader({});
|
|
12
|
+
var commentsReader = createCommentsReader(bodyReader);
|
|
13
|
+
var comments = commentsReader(element);
|
|
14
|
+
assert.equal(comments.value.length, 1);
|
|
15
|
+
return comments.value[0];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
test('ID and body of comment are read', function() {
|
|
19
|
+
var body = [xml.element("w:p")];
|
|
20
|
+
var comment = readComment(xml.element("w:comments", {}, [
|
|
21
|
+
xml.element("w:comment", {"w:id": "1"}, body)
|
|
22
|
+
]));
|
|
23
|
+
assert.deepEqual(comment.body, [new documents.Paragraph([])]);
|
|
24
|
+
assert.deepEqual(comment.commentId, "1");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
test('when optional attributes of comment are missing then they are read as null', function() {
|
|
29
|
+
var comment = readComment(xml.element("w:comments", {}, [
|
|
30
|
+
xml.element("w:comment", {"w:id": "1"})
|
|
31
|
+
]));
|
|
32
|
+
assert.strictEqual(comment.authorName, null);
|
|
33
|
+
assert.strictEqual(comment.authorInitials, null);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
test('when optional attributes of comment are blank then they are read as null', function() {
|
|
38
|
+
var comment = readComment(xml.element("w:comments", {}, [
|
|
39
|
+
xml.element("w:comment", {"w:id": "1", "w:author": " ", "w:initials": " "})
|
|
40
|
+
]));
|
|
41
|
+
assert.strictEqual(comment.authorName, null);
|
|
42
|
+
assert.strictEqual(comment.authorInitials, null);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
test('when optional attributes of comment are not blank then they are read', function() {
|
|
47
|
+
var comment = readComment(xml.element("w:comments", {}, [
|
|
48
|
+
xml.element("w:comment", {"w:id": "1", "w:author": "The Piemaker", "w:initials": "TP"})
|
|
49
|
+
]));
|
|
50
|
+
assert.strictEqual(comment.authorName, "The Piemaker");
|
|
51
|
+
assert.strictEqual(comment.authorInitials, "TP");
|
|
52
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var readContentTypesFromXml = require("../../lib/docx/content-types-reader").readContentTypesFromXml;
|
|
4
|
+
var XmlElement = require("../../lib/xml").Element;
|
|
5
|
+
var test = require("../test")(module);
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
test('reads default-per-extension from XML', function() {
|
|
9
|
+
var contentTypes = readContentTypesFromXml(
|
|
10
|
+
new XmlElement("content-types:Types", {}, [
|
|
11
|
+
new XmlElement("content-types:Default", {Extension: "png", ContentType: "image/png"})
|
|
12
|
+
])
|
|
13
|
+
);
|
|
14
|
+
assert.equal(contentTypes.findContentType("word/media/hat.png"), "image/png");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('reads overrides in preference to defaults', function() {
|
|
18
|
+
var contentTypes = readContentTypesFromXml(
|
|
19
|
+
new XmlElement("content-types:Types", {}, [
|
|
20
|
+
new XmlElement("content-types:Default", {Extension: "png", ContentType: "image/png"}),
|
|
21
|
+
new XmlElement("content-types:Override", {PartName: "/word/media/hat.png", ContentType: "image/hat"})
|
|
22
|
+
])
|
|
23
|
+
);
|
|
24
|
+
assert.equal(contentTypes.findContentType("word/media/hat.png"), "image/hat");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('fallback content types have common image types', function() {
|
|
28
|
+
var contentTypes = readContentTypesFromXml(
|
|
29
|
+
new XmlElement("content-types:Types", {}, [])
|
|
30
|
+
);
|
|
31
|
+
assert.equal(contentTypes.findContentType("word/media/hat.png"), "image/png");
|
|
32
|
+
assert.equal(contentTypes.findContentType("word/media/hat.gif"), "image/gif");
|
|
33
|
+
assert.equal(contentTypes.findContentType("word/media/hat.jpg"), "image/jpeg");
|
|
34
|
+
assert.equal(contentTypes.findContentType("word/media/hat.jpeg"), "image/jpeg");
|
|
35
|
+
assert.equal(contentTypes.findContentType("word/media/hat.bmp"), "image/bmp");
|
|
36
|
+
assert.equal(contentTypes.findContentType("word/media/hat.tif"), "image/tiff");
|
|
37
|
+
assert.equal(contentTypes.findContentType("word/media/hat.tiff"), "image/tiff");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('fallback content types are case insensitive on extension', function() {
|
|
41
|
+
var contentTypes = readContentTypesFromXml(
|
|
42
|
+
new XmlElement("content-types:Types", {}, [])
|
|
43
|
+
);
|
|
44
|
+
assert.equal(contentTypes.findContentType("word/media/hat.PnG"), "image/png");
|
|
45
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
var hamjest = require("hamjest");
|
|
2
|
+
var _ = require("underscore");
|
|
3
|
+
|
|
4
|
+
var documents = require("../../lib/documents");
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
exports.isEmptyRun = isRun({children: []});
|
|
8
|
+
exports.isRun = isRun;
|
|
9
|
+
exports.isText = isText;
|
|
10
|
+
exports.isHyperlink = isHyperlink;
|
|
11
|
+
exports.isTable = isTable;
|
|
12
|
+
exports.isRow = isRow;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
function isRun(properties) {
|
|
16
|
+
return isDocumentElement(documents.types.run, properties);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isText(text) {
|
|
20
|
+
return isDocumentElement(documents.types.text, {value: text});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isHyperlink(properties) {
|
|
24
|
+
return isDocumentElement(documents.types.hyperlink, properties);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isTable(options) {
|
|
28
|
+
return isDocumentElement(documents.types.table, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isRow(options) {
|
|
32
|
+
return isDocumentElement(documents.types.tableRow, options);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isDocumentElement(type, properties) {
|
|
36
|
+
return hamjest.hasProperties(_.extend({type: hamjest.equalTo(type)}, properties));
|
|
37
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var docxReader = require("../../lib/docx/docx-reader");
|
|
4
|
+
var documents = require("../../lib/documents");
|
|
5
|
+
var xml = require("../../lib/xml");
|
|
6
|
+
|
|
7
|
+
var testing = require("../testing");
|
|
8
|
+
var test = require("../test")(module);
|
|
9
|
+
var testData = testing.testData;
|
|
10
|
+
var createFakeDocxFile = testing.createFakeDocxFile;
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
test("can read document with single paragraph with single run of text", function() {
|
|
14
|
+
var expectedDocument = documents.Document([
|
|
15
|
+
documents.Paragraph([
|
|
16
|
+
documents.Run([
|
|
17
|
+
documents.Text("Hello.")
|
|
18
|
+
])
|
|
19
|
+
])
|
|
20
|
+
]);
|
|
21
|
+
var docxFile = createFakeDocxFile({
|
|
22
|
+
"word/document.xml": testData("simple/word/document.xml")
|
|
23
|
+
});
|
|
24
|
+
return docxReader.read(docxFile).then(function(result) {
|
|
25
|
+
assert.deepEqual(expectedDocument, result.value);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("hyperlink hrefs are read from relationships file", function() {
|
|
30
|
+
var docxFile = createFakeDocxFile({
|
|
31
|
+
"word/document.xml": testData("hyperlinks/word/document.xml"),
|
|
32
|
+
"word/_rels/document.xml.rels": testData("hyperlinks/word/_rels/document.xml.rels")
|
|
33
|
+
});
|
|
34
|
+
return docxReader.read(docxFile).then(function(result) {
|
|
35
|
+
var paragraph = result.value.children[0];
|
|
36
|
+
assert.equal(1, paragraph.children.length);
|
|
37
|
+
var hyperlink = paragraph.children[0];
|
|
38
|
+
assert.equal(hyperlink.href, "http://www.example.com");
|
|
39
|
+
assert.equal(hyperlink.children.length, 1);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
var relationshipNamespaces = {
|
|
44
|
+
"r": "http://schemas.openxmlformats.org/package/2006/relationships"
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
test("main document is found using _rels/.rels", function() {
|
|
48
|
+
var relationships = xml.element("r:Relationships", {}, [
|
|
49
|
+
xml.element("r:Relationship", {
|
|
50
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
51
|
+
"Target": "/word/document2.xml"
|
|
52
|
+
})
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
var docxFile = createFakeDocxFile({
|
|
56
|
+
"word/document2.xml": testData("simple/word/document.xml"),
|
|
57
|
+
"_rels/.rels": xml.writeString(relationships, relationshipNamespaces)
|
|
58
|
+
});
|
|
59
|
+
var expectedDocument = documents.Document([
|
|
60
|
+
documents.Paragraph([
|
|
61
|
+
documents.Run([
|
|
62
|
+
documents.Text("Hello.")
|
|
63
|
+
])
|
|
64
|
+
])
|
|
65
|
+
]);
|
|
66
|
+
return docxReader.read(docxFile).then(function(result) {
|
|
67
|
+
assert.deepEqual(expectedDocument, result.value);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
test("error is thrown when main document part does not exist", function() {
|
|
73
|
+
var relationships = xml.element("r:Relationships", {}, [
|
|
74
|
+
xml.element("r:Relationship", {
|
|
75
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
76
|
+
"Target": "/word/document2.xml"
|
|
77
|
+
})
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
var docxFile = createFakeDocxFile({
|
|
81
|
+
"_rels/.rels": xml.writeString(relationships, relationshipNamespaces)
|
|
82
|
+
});
|
|
83
|
+
return docxReader.read(docxFile).then(function(result) {
|
|
84
|
+
assert.ok(false, "Expected error");
|
|
85
|
+
}, function(error) {
|
|
86
|
+
assert.equal(error.message, "Could not find main document part. Are you sure this is a valid .docx file?");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
test("part paths", {
|
|
92
|
+
"main document part is found using package relationships": function() {
|
|
93
|
+
var relationships = xml.element("r:Relationships", {}, [
|
|
94
|
+
xml.element("r:Relationship", {
|
|
95
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
96
|
+
"Target": "/word/document2.xml"
|
|
97
|
+
})
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
var docxFile = createFakeDocxFile({
|
|
101
|
+
"word/document2.xml": " ",
|
|
102
|
+
"_rels/.rels": xml.writeString(relationships, relationshipNamespaces)
|
|
103
|
+
});
|
|
104
|
+
return docxReader._findPartPaths(docxFile).then(function(partPaths) {
|
|
105
|
+
assert.equal(partPaths.mainDocument, "word/document2.xml");
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
"word/document.xml is used as fallback location for main document part": function() {
|
|
110
|
+
var docxFile = createFakeDocxFile({
|
|
111
|
+
"word/document.xml": " "
|
|
112
|
+
});
|
|
113
|
+
return docxReader._findPartPaths(docxFile).then(function(partPaths) {
|
|
114
|
+
assert.equal(partPaths.mainDocument, "word/document.xml");
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
[
|
|
120
|
+
{
|
|
121
|
+
name: "comments",
|
|
122
|
+
type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: "endnotes",
|
|
126
|
+
type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "footnotes",
|
|
130
|
+
type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footnotes"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "numbering",
|
|
134
|
+
type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: "styles",
|
|
138
|
+
type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
|
|
139
|
+
}
|
|
140
|
+
].forEach(function(options) {
|
|
141
|
+
test(options.name + " part is found using main document relationships", function() {
|
|
142
|
+
var docxFile = createFakeDocxFile({
|
|
143
|
+
"_rels/.rels": createPackageRelationships("word/document.xml"),
|
|
144
|
+
"word/document.xml": " ",
|
|
145
|
+
"word/_rels/document.xml.rels": xml.writeString(xml.element("r:Relationships", {}, [
|
|
146
|
+
xml.element("r:Relationship", {
|
|
147
|
+
"Type": options.type,
|
|
148
|
+
"Target": "target-path.xml"
|
|
149
|
+
})
|
|
150
|
+
]), relationshipNamespaces),
|
|
151
|
+
"word/target-path.xml": " "
|
|
152
|
+
});
|
|
153
|
+
return docxReader._findPartPaths(docxFile).then(function(partPaths) {
|
|
154
|
+
assert.equal(partPaths[options.name], "word/target-path.xml");
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("word/" + options.name + ".xml is used as fallback location for " + options.name + " part", function() {
|
|
159
|
+
var zipContents = {
|
|
160
|
+
"_rels/.rels": createPackageRelationships("word/document.xml"),
|
|
161
|
+
"word/document.xml": " "
|
|
162
|
+
};
|
|
163
|
+
zipContents["word/" + options.name + ".xml"] = " ";
|
|
164
|
+
var docxFile = createFakeDocxFile(zipContents);
|
|
165
|
+
return docxReader._findPartPaths(docxFile).then(function(partPaths) {
|
|
166
|
+
assert.equal(partPaths[options.name], "word/" + options.name + ".xml");
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
function createPackageRelationships(mainDocumentPath) {
|
|
173
|
+
return xml.writeString(xml.element("r:Relationships", {}, [
|
|
174
|
+
xml.element("r:Relationship", {
|
|
175
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument",
|
|
176
|
+
"Target": mainDocumentPath
|
|
177
|
+
})
|
|
178
|
+
]), relationshipNamespaces);
|
|
179
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
var path = require("path");
|
|
2
|
+
var fs = require("fs");
|
|
3
|
+
var assert = require("assert");
|
|
4
|
+
|
|
5
|
+
var promises = require("../../lib/promises");
|
|
6
|
+
var Files = require("../../lib/docx/files").Files;
|
|
7
|
+
var uriToPath = require("../../lib/docx/files").uriToPath;
|
|
8
|
+
|
|
9
|
+
var testing = require("../testing");
|
|
10
|
+
var test = require("../test")(module);
|
|
11
|
+
|
|
12
|
+
var readFile = promises.promisify(fs.readFile.bind(fs));
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
test("Files", {
|
|
16
|
+
"can open files with file URI": function() {
|
|
17
|
+
var filePath = path.resolve(testing.testPath("tiny-picture.png"));
|
|
18
|
+
var files = new Files(null);
|
|
19
|
+
return files.read("file:///" + filePath.replace(/^\//, ""), "base64").then(function(contents) {
|
|
20
|
+
return readFile(filePath, "base64").then(function(expectedContents) {
|
|
21
|
+
assert.deepEqual(contents, expectedContents);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
"can open files with relative URI": function() {
|
|
27
|
+
var filePath = path.resolve(testing.testPath("tiny-picture.png"));
|
|
28
|
+
var files = new Files(testing.testPath("."));
|
|
29
|
+
return files.read("tiny-picture.png", "base64").then(function(contents) {
|
|
30
|
+
return readFile(filePath, "base64").then(function(expectedContents) {
|
|
31
|
+
assert.deepEqual(contents, expectedContents);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
"given base is not set when opening relative uri then error is raised": function() {
|
|
37
|
+
var files = new Files(null);
|
|
38
|
+
return assertError(files.read("not-a-real-file.png", "base64"), function(err) {
|
|
39
|
+
assert.equal(err.message, "could not find external image 'not-a-real-file.png', path of input document is unknown");
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
"error if relative uri cannot be opened": function() {
|
|
44
|
+
var files = new Files("/tmp");
|
|
45
|
+
return assertError(files.read("not-a-real-file.png", "base64"), function(err) {
|
|
46
|
+
assertRegex(err.message, /could not open external image: 'not-a-real-file.png' \(document directory: '\/tmp'\)\nENOENT.*\/tmp\/not-a-real-file.png.*/);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
function assertError(promise, func) {
|
|
52
|
+
return promise.then(function() {
|
|
53
|
+
assert(false, "Expected error");
|
|
54
|
+
}, func);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function assertRegex(actual, expected) {
|
|
58
|
+
assert.ok(expected.test(actual), "Expected regex: " + expected + "\nbut was: " + actual);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
test("uriToPath", {
|
|
63
|
+
"leading slash is retained on non-Windows file URIs": function() {
|
|
64
|
+
assert.equal(uriToPath("file:///a/b/c", "linux"), "/a/b/c");
|
|
65
|
+
assert.equal(uriToPath("file:///a/b/c", "win32"), "/a/b/c");
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
"URI is unquoted": function() {
|
|
69
|
+
assert.equal(uriToPath("file:///a%20b"), "/a b");
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
"when host is set to localhost then path can be found": function() {
|
|
73
|
+
assert.equal(uriToPath("file://localhost/a/b/c"), "/a/b/c");
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
"when host is set but not localhost then path cannot be found": function() {
|
|
77
|
+
assert.throws(function() {
|
|
78
|
+
uriToPath("file://example/a/b/c");
|
|
79
|
+
}, /Could not convert URI to path: file:\/\/example\/a\/b\/c/);
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
"leading slash is not dropped on Windows file URIs when platform is not Windows": function() {
|
|
83
|
+
assert.equal(uriToPath("file:///c:/a", "linux"), "/c:/a");
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
"leading slash is dropped on Windows file URIs when platform is Windows": function() {
|
|
87
|
+
assert.equal(uriToPath("file:///c:/a", "win32"), "c:/a");
|
|
88
|
+
assert.equal(uriToPath("file:///C:/a", "win32"), "C:/a");
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
"relative URI is unquoted": function() {
|
|
92
|
+
assert.equal(uriToPath("a%20b/c"), "a b/c");
|
|
93
|
+
}
|
|
94
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var createFootnotesReader = require("../../lib/docx/notes-reader").createFootnotesReader;
|
|
4
|
+
var createBodyReader = require("../../lib/docx/body-reader").createBodyReader;
|
|
5
|
+
var documents = require("../../lib/documents");
|
|
6
|
+
var XmlElement = require("../../lib/xml").Element;
|
|
7
|
+
var test = require("../test")(module);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
test('ID and body of footnote are read', function() {
|
|
11
|
+
var bodyReader = new createBodyReader({});
|
|
12
|
+
var footnoteBody = [new XmlElement("w:p", {}, [])];
|
|
13
|
+
var footnotes = createFootnotesReader(bodyReader)(
|
|
14
|
+
new XmlElement("w:footnotes", {}, [
|
|
15
|
+
new XmlElement("w:footnote", {"w:id": "1"}, footnoteBody)
|
|
16
|
+
])
|
|
17
|
+
);
|
|
18
|
+
assert.equal(footnotes.value.length, 1);
|
|
19
|
+
assert.deepEqual(footnotes.value[0].body, [new documents.Paragraph([])]);
|
|
20
|
+
assert.deepEqual(footnotes.value[0].noteId, "1");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
footnoteTypeIsIgnored('continuationSeparator');
|
|
24
|
+
footnoteTypeIsIgnored('separator');
|
|
25
|
+
|
|
26
|
+
function footnoteTypeIsIgnored(type) {
|
|
27
|
+
test('footnotes of type ' + type + ' are ignored', function() {
|
|
28
|
+
var footnotes = createFootnotesReader()(
|
|
29
|
+
new XmlElement("w:footnotes", {}, [
|
|
30
|
+
new XmlElement("w:footnote", {"w:id": "1", "w:type": type}, [])
|
|
31
|
+
])
|
|
32
|
+
);
|
|
33
|
+
assert.equal(footnotes.value.length, 0);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
var duck = require("duck");
|
|
3
|
+
|
|
4
|
+
var readNumberingXml = require("../../lib/docx/numbering-xml").readNumberingXml;
|
|
5
|
+
var stylesReader = require("../../lib/docx/styles-reader");
|
|
6
|
+
var XmlElement = require("../../lib/xml").Element;
|
|
7
|
+
var test = require("../test")(module);
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
test('w:num element inherits levels from w:abstractNum', function() {
|
|
11
|
+
var numbering = readNumberingXml(
|
|
12
|
+
new XmlElement("w:numbering", {}, [
|
|
13
|
+
new XmlElement("w:abstractNum", {"w:abstractNumId": "42"}, [
|
|
14
|
+
new XmlElement("w:lvl", {"w:ilvl": "0"}, [
|
|
15
|
+
new XmlElement("w:numFmt", {"w:val": "bullet"})
|
|
16
|
+
]),
|
|
17
|
+
new XmlElement("w:lvl", {"w:ilvl": "1"}, [
|
|
18
|
+
new XmlElement("w:numFmt", {"w:val": "decimal"})
|
|
19
|
+
])
|
|
20
|
+
]),
|
|
21
|
+
new XmlElement("w:num", {"w:numId": "47"}, [
|
|
22
|
+
new XmlElement("w:abstractNumId", {"w:val": "42"})
|
|
23
|
+
])
|
|
24
|
+
]),
|
|
25
|
+
{styles: stylesReader.defaultStyles}
|
|
26
|
+
);
|
|
27
|
+
duck.assertThat(numbering.findLevel("47", "0"), duck.hasProperties({
|
|
28
|
+
isOrdered: false
|
|
29
|
+
}));
|
|
30
|
+
duck.assertThat(numbering.findLevel("47", "1"), duck.hasProperties({
|
|
31
|
+
isOrdered: true
|
|
32
|
+
}));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
test('when w:abstractNum has w:numStyleLink then style is used to find w:num', function() {
|
|
37
|
+
var numbering = readNumberingXml(
|
|
38
|
+
new XmlElement("w:numbering", {}, [
|
|
39
|
+
new XmlElement("w:abstractNum", {"w:abstractNumId": "100"}, [
|
|
40
|
+
new XmlElement("w:lvl", {"w:ilvl": "0"}, [
|
|
41
|
+
new XmlElement("w:numFmt", {"w:val": "decimal"})
|
|
42
|
+
])
|
|
43
|
+
]),
|
|
44
|
+
new XmlElement("w:abstractNum", {"w:abstractNumId": "101"}, [
|
|
45
|
+
new XmlElement("w:numStyleLink", {"w:val": "List1"})
|
|
46
|
+
]),
|
|
47
|
+
new XmlElement("w:num", {"w:numId": "200"}, [
|
|
48
|
+
new XmlElement("w:abstractNumId", {"w:val": "100"})
|
|
49
|
+
]),
|
|
50
|
+
new XmlElement("w:num", {"w:numId": "201"}, [
|
|
51
|
+
new XmlElement("w:abstractNumId", {"w:val": "101"})
|
|
52
|
+
])
|
|
53
|
+
]),
|
|
54
|
+
{styles: new stylesReader.Styles({}, {}, {}, {"List1": {numId: "200"}})}
|
|
55
|
+
);
|
|
56
|
+
duck.assertThat(numbering.findLevel("201", "0"), duck.hasProperties({
|
|
57
|
+
isOrdered: true
|
|
58
|
+
}));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('when styles is missing then error is thrown', function() {
|
|
62
|
+
assert.throws(function() {
|
|
63
|
+
readNumberingXml(new XmlElement("w:numbering", {}, []));
|
|
64
|
+
}, /styles is missing/);
|
|
65
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var xml = require("../../lib/xml");
|
|
4
|
+
var officeXmlReader = require("../../lib/docx/office-xml-reader");
|
|
5
|
+
var test = require("../test")(module);
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
test("mc:AlternateContent is replaced by contents of mc:Fallback", function() {
|
|
9
|
+
var xmlString =
|
|
10
|
+
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
|
11
|
+
'<numbering xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">' +
|
|
12
|
+
'<mc:AlternateContent>' +
|
|
13
|
+
'<mc:Choice Requires="w14">' +
|
|
14
|
+
'<choice/>' +
|
|
15
|
+
'</mc:Choice>' +
|
|
16
|
+
'<mc:Fallback>' +
|
|
17
|
+
'<fallback/>' +
|
|
18
|
+
'</mc:Fallback>' +
|
|
19
|
+
'</mc:AlternateContent>' +
|
|
20
|
+
'</numbering>';
|
|
21
|
+
return officeXmlReader.read(xmlString).then(function(element) {
|
|
22
|
+
assert.deepEqual(element.children, [xml.element("fallback")]);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
var assert = require("assert");
|
|
2
|
+
|
|
3
|
+
var readRelationships = require("../../lib/docx/relationships-reader").readRelationships;
|
|
4
|
+
var xml = require("../../lib/xml");
|
|
5
|
+
var test = require("../test")(module);
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
test("relationships can be found by ID", function() {
|
|
9
|
+
var relationships = readRelationships(relationshipsElement([
|
|
10
|
+
relationshipElement({
|
|
11
|
+
"Id": "rId1",
|
|
12
|
+
"Target": "http://example.com/",
|
|
13
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
|
14
|
+
}),
|
|
15
|
+
relationshipElement({
|
|
16
|
+
"Id": "rId2",
|
|
17
|
+
"Target": "http://example.net/",
|
|
18
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
|
19
|
+
})
|
|
20
|
+
]));
|
|
21
|
+
assert.equal(relationships.findTargetByRelationshipId("rId1"), "http://example.com/");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
test("relationships can be found by type", function() {
|
|
26
|
+
var relationships = readRelationships(relationshipsElement([
|
|
27
|
+
relationshipElement({
|
|
28
|
+
"Id": "rId2",
|
|
29
|
+
"Target": "docProps/core.xml",
|
|
30
|
+
"Type": "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"
|
|
31
|
+
}),
|
|
32
|
+
relationshipElement({
|
|
33
|
+
"Id": "rId1",
|
|
34
|
+
"Target": "word/document.xml",
|
|
35
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
|
36
|
+
}),
|
|
37
|
+
relationshipElement({
|
|
38
|
+
"Id": "rId3",
|
|
39
|
+
"Target": "word/document2.xml",
|
|
40
|
+
"Type": "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"
|
|
41
|
+
})
|
|
42
|
+
]));
|
|
43
|
+
assert.deepEqual(
|
|
44
|
+
relationships.findTargetsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"),
|
|
45
|
+
["word/document.xml", "word/document2.xml"]
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
test("when there are no relationships of requested type then empty array is returned", function() {
|
|
51
|
+
var relationships = readRelationships(relationshipsElement([]));
|
|
52
|
+
assert.deepEqual(
|
|
53
|
+
relationships.findTargetsByType("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument"),
|
|
54
|
+
[]
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
function relationshipsElement(children) {
|
|
60
|
+
return xml.element("{http://schemas.openxmlformats.org/package/2006/relationships}Relationships", {}, children);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function relationshipElement(attributes) {
|
|
64
|
+
return xml.element("{http://schemas.openxmlformats.org/package/2006/relationships}Relationship", attributes, []);
|
|
65
|
+
}
|