aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2016-11-19 14:53:45 +0000
committerMichael Williamson <mike@zwobble.org>2016-11-19 14:53:45 +0000
commita7146d18d1183fbe0203025f5e0e6aee5cd3775c (patch)
tree0742a812f4488265ca33ff7221bd97b78c500e53
parent599b3f17555a9d0c03ff040df6dca232b152a6f0 (diff)
Include embedded style map when explicit style map is set
-rw-r--r--NEWS5
-rw-r--r--README.md7
-rw-r--r--lib/index.js24
-rw-r--r--lib/options-reader.js131
-rw-r--r--test/mammoth.tests.js11
-rw-r--r--test/options-reader.tests.js29
6 files changed, 119 insertions, 88 deletions
diff --git a/NEWS b/NEWS
index 1f5db7e..facee0e 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,11 @@
* Allow a separator to be specified when HTML elements are collapsed.
+* Add includeEmbeddedStyleMap option to allow embedded style maps to be
+ disabled.
+
+* Include embedded styles when explicit style map is passed.
+
# 1.2.5
* Ignore bold, italic, underline and strikethrough elements that have a value of
diff --git a/README.md b/README.md
index fa4cbd6..027c494 100644
--- a/README.md
+++ b/README.md
@@ -345,6 +345,11 @@ Converts the source document to HTML.
each element is expected to be a string representing a single style mapping.
See "Writing style maps" for a reference to the syntax for style maps.
+ * `includeEmbeddedStyleMap`: by default,
+ if the document contains an embedded style, then it is combined with the default style map.
+ To ignore any embedded style maps,
+ set `options.includeEmbeddedStyleMap` to `false`.
+
* `includeDefaultStyleMap`: by default,
the style map passed in `styleMap` is combined with the default style map.
To stop using the default style map altogether,
@@ -407,7 +412,7 @@ Each paragraph is followed by two newlines.
Given an existing docx file,
`embedStyleMap` will generate a new docx file with the passed style map embedded.
When the new docx file is read by Mammoth,
-it will use the embedded style if no explicit style map is provided.
+it will use the embedded style map.
* `input`: an object describing the source document.
On node.js, the following inputs are supported:
diff --git a/lib/index.js b/lib/index.js
index ae95371..212b0b7 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,3 +1,5 @@
+var _ = require("underscore");
+
var docxReader = require("./docx/docx-reader");
var docxStyleMap = require("./docx/style-map");
var DocumentConverter = require("./document-to-html").DocumentConverter;
@@ -27,24 +29,21 @@ function convertToMarkdown(input, options) {
}
function convert(input, options) {
- options = Object.create(options || {});
+ options = readOptions(options);
return unzip.openZip(input)
.tap(function(docxFile) {
- if (!options.styleMap) {
- return docxStyleMap.readStyleMap(docxFile).then(function(styleMap) {
- options.styleMap = styleMap || "";
- });
- }
+ return docxStyleMap.readStyleMap(docxFile).then(function(styleMap) {
+ options.embeddedStyleMap = styleMap;
+ });
})
.then(function(docxFile) {
- var fullOptions = readOptions(options);
return docxReader.read(docxFile, input)
.then(function(documentResult) {
- return documentResult.map(fullOptions.transformDocument);
+ return documentResult.map(options.transformDocument);
})
.then(function(documentResult) {
- return convertDocumentToHtml(documentResult, fullOptions);
+ return convertDocumentToHtml(documentResult, options);
});
});
}
@@ -55,9 +54,10 @@ function readEmbeddedStyleMap(input) {
}
function convertDocumentToHtml(documentResult, options) {
- var parsedOptions = Object.create(options);
- var styleMapResult = parseStyleMap(options.styleMap);
- parsedOptions.styleMap = styleMapResult.value;
+ var styleMapResult = parseStyleMap(options.readStyleMap());
+ var parsedOptions = _.extend({}, options, {
+ styleMap: styleMapResult.value
+ });
var documentConverter = new DocumentConverter(parsedOptions);
return documentResult.flatMapThen(function(document) {
diff --git a/lib/options-reader.js b/lib/options-reader.js
index 4a7db9d..e7e94fd 100644
--- a/lib/options-reader.js
+++ b/lib/options-reader.js
@@ -3,79 +3,81 @@ exports.readOptions = readOptions;
var _ = require("underscore");
+var defaultStyleMap = exports._defaultStyleMap = [
+ "p.Heading1 => h1:fresh",
+ "p.Heading2 => h2:fresh",
+ "p.Heading3 => h3:fresh",
+ "p.Heading4 => h4:fresh",
+ "p.Heading5 => h5:fresh",
+ "p.Heading6 => h6:fresh",
+ "p[style-name='Heading 1'] => h1:fresh",
+ "p[style-name='Heading 2'] => h2:fresh",
+ "p[style-name='Heading 3'] => h3:fresh",
+ "p[style-name='Heading 4'] => h4:fresh",
+ "p[style-name='Heading 5'] => h5:fresh",
+ "p[style-name='Heading 6'] => h6:fresh",
+ "p[style-name='heading 1'] => h1:fresh",
+ "p[style-name='heading 2'] => h2:fresh",
+ "p[style-name='heading 3'] => h3:fresh",
+ "p[style-name='heading 4'] => h4:fresh",
+ "p[style-name='heading 5'] => h5:fresh",
+ "p[style-name='heading 6'] => h6:fresh",
+
+ "r[style-name='Strong'] => strong",
+
+ "p[style-name='footnote text'] => p",
+ "r[style-name='footnote reference'] =>",
+ "p[style-name='endnote text'] => p",
+ "r[style-name='endnote reference'] =>",
+ "p[style-name='annotation text'] => p",
+ "r[style-name='annotation reference'] =>",
+
+ // LibreOffice
+ "p[style-name='Footnote'] => p",
+ "r[style-name='Footnote anchor'] =>",
+ "p[style-name='Endnote'] => p",
+ "r[style-name='Endnote anchor'] =>",
+
+ "p:unordered-list(1) => ul > li:fresh",
+ "p:unordered-list(2) => ul|ol > li > ul > li:fresh",
+ "p:unordered-list(3) => ul|ol > li > ul|ol > li > ul > li:fresh",
+ "p:unordered-list(4) => ul|ol > li > ul|ol > li > ul|ol > li > ul > li:fresh",
+ "p:unordered-list(5) => ul|ol > li > ul|ol > li > ul|ol > li > ul|ol > li > ul > li:fresh",
+ "p:ordered-list(1) => ol > li:fresh",
+ "p:ordered-list(2) => ul|ol > li > ol > li:fresh",
+ "p:ordered-list(3) => ul|ol > li > ul|ol > li > ol > li:fresh",
+ "p:ordered-list(4) => ul|ol > li > ul|ol > li > ul|ol > li > ol > li:fresh",
+ "p:ordered-list(5) => ul|ol > li > ul|ol > li > ul|ol > li > ul|ol > li > ol > li:fresh",
+
+ "r[style-name='Hyperlink'] =>",
+
+ "p[style-name='Normal'] => p:fresh"
+];
var standardOptions = exports._standardOptions = {
- styleMap: [
- "p.Heading1 => h1:fresh",
- "p.Heading2 => h2:fresh",
- "p.Heading3 => h3:fresh",
- "p.Heading4 => h4:fresh",
- "p.Heading5 => h5:fresh",
- "p.Heading6 => h6:fresh",
- "p[style-name='Heading 1'] => h1:fresh",
- "p[style-name='Heading 2'] => h2:fresh",
- "p[style-name='Heading 3'] => h3:fresh",
- "p[style-name='Heading 4'] => h4:fresh",
- "p[style-name='Heading 5'] => h5:fresh",
- "p[style-name='Heading 6'] => h6:fresh",
- "p[style-name='heading 1'] => h1:fresh",
- "p[style-name='heading 2'] => h2:fresh",
- "p[style-name='heading 3'] => h3:fresh",
- "p[style-name='heading 4'] => h4:fresh",
- "p[style-name='heading 5'] => h5:fresh",
- "p[style-name='heading 6'] => h6:fresh",
-
- "r[style-name='Strong'] => strong",
-
- "p[style-name='footnote text'] => p",
- "r[style-name='footnote reference'] =>",
- "p[style-name='endnote text'] => p",
- "r[style-name='endnote reference'] =>",
- "p[style-name='annotation text'] => p",
- "r[style-name='annotation reference'] =>",
-
- // LibreOffice
- "p[style-name='Footnote'] => p",
- "r[style-name='Footnote anchor'] =>",
- "p[style-name='Endnote'] => p",
- "r[style-name='Endnote anchor'] =>",
-
- "p:unordered-list(1) => ul > li:fresh",
- "p:unordered-list(2) => ul|ol > li > ul > li:fresh",
- "p:unordered-list(3) => ul|ol > li > ul|ol > li > ul > li:fresh",
- "p:unordered-list(4) => ul|ol > li > ul|ol > li > ul|ol > li > ul > li:fresh",
- "p:unordered-list(5) => ul|ol > li > ul|ol > li > ul|ol > li > ul|ol > li > ul > li:fresh",
- "p:ordered-list(1) => ol > li:fresh",
- "p:ordered-list(2) => ul|ol > li > ol > li:fresh",
- "p:ordered-list(3) => ul|ol > li > ul|ol > li > ol > li:fresh",
- "p:ordered-list(4) => ul|ol > li > ul|ol > li > ul|ol > li > ol > li:fresh",
- "p:ordered-list(5) => ul|ol > li > ul|ol > li > ul|ol > li > ul|ol > li > ol > li:fresh",
-
- "r[style-name='Hyperlink'] =>",
-
- "p[style-name='Normal'] => p:fresh"
-
- ],
transformDocument: identity,
- includeDefaultStyleMap: true
+ includeDefaultStyleMap: true,
+ includeEmbeddedStyleMap: true
};
function readOptions(options) {
options = options || {};
- var fullOptions = {};
- _.extend(fullOptions, standardOptions, options);
-
- fullOptions.styleMap = readStyleMap(options.styleMap, fullOptions);
-
- return fullOptions;
+ return _.extend({}, standardOptions, options, {
+ customStyleMap: readStyleMap(options.styleMap),
+ readStyleMap: function() {
+ var styleMap = this.customStyleMap;
+ if (this.includeEmbeddedStyleMap) {
+ styleMap = styleMap.concat(readStyleMap(this.embeddedStyleMap));
+ }
+ if (this.includeDefaultStyleMap) {
+ styleMap = styleMap.concat(defaultStyleMap);
+ }
+ return styleMap;
+ }
+ });
}
-function readStyleMap(styleMap, options) {
- var customStyleMap = readCustomStyleMap(styleMap);
- return customStyleMap.concat(options.includeDefaultStyleMap ? standardOptions.styleMap : []);
-}
-
-function readCustomStyleMap(styleMap) {
+function readStyleMap(styleMap) {
if (!styleMap) {
return [];
} else if (_.isString(styleMap)) {
@@ -91,7 +93,6 @@ function readCustomStyleMap(styleMap) {
}
}
-
function identity(value) {
return value;
}
diff --git a/test/mammoth.tests.js b/test/mammoth.tests.js
index db2b338..19e0c47 100644
--- a/test/mammoth.tests.js
+++ b/test/mammoth.tests.js
@@ -100,6 +100,17 @@ test('explicit style map takes precedence over embedded style map', function() {
});
});
+test('embedded style maps can be disabled', function() {
+ var docxPath = path.join(__dirname, "test-data/embedded-style-map.docx");
+ var options = {
+ includeEmbeddedStyleMap: false
+ };
+ return mammoth.convertToHtml({path: docxPath}, options).then(function(result) {
+ assert.equal(result.value, "<p>Walking on imported air</p>");
+ assert.deepEqual(result.messages, []);
+ });
+});
+
test('embedded style map can be written and then read', function() {
var docxPath = path.join(__dirname, "test-data/single-paragraph.docx");
return promises.nfcall(fs.readFile, docxPath)
diff --git a/test/options-reader.tests.js b/test/options-reader.tests.js
index b0f202b..bad4c56 100644
--- a/test/options-reader.tests.js
+++ b/test/options-reader.tests.js
@@ -1,5 +1,7 @@
var assert = require("assert");
+var _ = require("underscore");
+
var optionsReader = require("../lib/options-reader");
var standardOptions = optionsReader._standardOptions;
var readOptions = optionsReader.readOptions;
@@ -7,42 +9,49 @@ var test = require("./test")(module);
test('standard options are used if options is undefined', function() {
- assert.deepEqual(standardOptions, readOptions(undefined));
+ var options = readOptions(undefined);
+ assert.deepEqual(standardOptions, _.omit(options, "customStyleMap", "readStyleMap"));
+ assert.deepEqual(options.customStyleMap, []);
});
test('standard options are used if options is empty', function() {
- assert.deepEqual(standardOptions, readOptions({}));
+ var options = readOptions({});
+ assert.deepEqual(standardOptions, _.omit(options, "customStyleMap", "readStyleMap"));
+ assert.deepEqual(options.customStyleMap, []);
});
test('custom style map as string is prepended to standard style map', function() {
var options = readOptions({
styleMap: "p.SectionTitle => h2"
});
- assert.deepEqual("p.SectionTitle => h2", options.styleMap[0]);
- assert.deepEqual(standardOptions.styleMap, options.styleMap.slice(1));
+ var styleMap = options.readStyleMap();
+ assert.deepEqual("p.SectionTitle => h2", styleMap[0]);
+ assert.deepEqual(optionsReader._defaultStyleMap, styleMap.slice(1));
});
test('custom style map as array is prepended to standard style map', function() {
var options = readOptions({
styleMap: ["p.SectionTitle => h2"]
});
- assert.deepEqual("p.SectionTitle => h2", options.styleMap[0]);
- assert.deepEqual(standardOptions.styleMap, options.styleMap.slice(1));
+ var styleMap = options.readStyleMap();
+ assert.deepEqual("p.SectionTitle => h2", styleMap[0]);
+ assert.deepEqual(optionsReader._defaultStyleMap, styleMap.slice(1));
});
test('lines starting with # in custom style map are ignored', function() {
var options = readOptions({
styleMap: "# p.SectionTitle => h3\np.SectionTitle => h2"
});
- assert.deepEqual("p.SectionTitle => h2", options.styleMap[0]);
- assert.deepEqual(standardOptions.styleMap, options.styleMap.slice(1));
+ var styleMap = options.readStyleMap();
+ assert.deepEqual("p.SectionTitle => h2", styleMap[0]);
+ assert.deepEqual(optionsReader._defaultStyleMap, styleMap.slice(1));
});
test('blank lines in custom style map are ignored', function() {
var options = readOptions({
styleMap: "\n\n"
});
- assert.deepEqual(standardOptions.styleMap, options.styleMap);
+ assert.deepEqual(optionsReader._defaultStyleMap, options.readStyleMap());
});
test('default style mappings are ignored if includeDefaultStyleMap is false', function() {
@@ -50,5 +59,5 @@ test('default style mappings are ignored if includeDefaultStyleMap is false', fu
styleMap: "p.SectionTitle => h2",
includeDefaultStyleMap: false
});
- assert.deepEqual(["p.SectionTitle => h2"], options.styleMap);
+ assert.deepEqual(["p.SectionTitle => h2"], options.readStyleMap());
});