summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2020-11-05 19:35:33 +0000
committerMichael Williamson <mike@zwobble.org>2020-11-05 19:35:33 +0000
commitf7cfac642bf63fac77c99d4cc30601ded8b2c107 (patch)
tree771fee9fc5c43e54f94a4f6bb7475edec50cda8e
parente832f604e34d344d996f8101023b6272085f934c (diff)
Add style mapping for all caps
-rw-r--r--NEWS4
-rw-r--r--README.md11
-rw-r--r--lib/document-to-html.js3
-rw-r--r--lib/documents.js1
-rw-r--r--lib/docx/body-reader.js1
-rw-r--r--lib/style-reader.js28
-rw-r--r--lib/styles/document-matchers.js1
-rw-r--r--test/document-to-html.tests.js24
-rw-r--r--test/docx/body-reader.tests.js1
-rw-r--r--test/style-reader.tests.js57
10 files changed, 93 insertions, 38 deletions
diff --git a/NEWS b/NEWS
index 30178ae..28e58e8 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,7 @@
+# 1.4.14
+
+* Add style mapping for all caps.
+
# 1.4.13
* Use package-lock.json instead of npm-shrinkwrap.json.
diff --git a/README.md b/README.md
index 2185b22..6aa6a55 100644
--- a/README.md
+++ b/README.md
@@ -726,6 +726,17 @@ strike
Note that this matches text that has had strikethrough explicitly applied to it.
It will not match any text that is struckthrough because of its paragraph or run style.
+#### All caps
+
+Match explicitly all caps text:
+
+```
+all-caps
+```
+
+Note that this matches text that has had all caps explicitly applied to it.
+It will not match any text that is all caps because of its paragraph or run style.
+
#### Small caps
Match explicitly small caps text:
diff --git a/lib/document-to-html.js b/lib/document-to-html.js
index 2267933..32e8b03 100644
--- a/lib/document-to-html.js
+++ b/lib/document-to-html.js
@@ -130,6 +130,9 @@ function DocumentConversion(options, comments) {
if (run.isSmallCaps) {
paths.push(findHtmlPathForRunProperty("smallCaps"));
}
+ if (run.isAllCaps) {
+ paths.push(findHtmlPathForRunProperty("allCaps"));
+ }
if (run.isStrikethrough) {
paths.push(findHtmlPathForRunProperty("strikethrough", "s"));
}
diff --git a/lib/documents.js b/lib/documents.js
index df48beb..edc9b7c 100644
--- a/lib/documents.js
+++ b/lib/documents.js
@@ -59,6 +59,7 @@ function Run(children, properties) {
isUnderline: properties.isUnderline,
isItalic: properties.isItalic,
isStrikethrough: properties.isStrikethrough,
+ isAllCaps: properties.isAllCaps,
isSmallCaps: properties.isSmallCaps,
verticalAlignment: properties.verticalAlignment || verticalAlignment.baseline,
font: properties.font || null,
diff --git a/lib/docx/body-reader.js b/lib/docx/body-reader.js
index ad4f409..77f194b 100644
--- a/lib/docx/body-reader.js
+++ b/lib/docx/body-reader.js
@@ -73,6 +73,7 @@ function BodyReader(options) {
isUnderline: readUnderline(element.first("w:u")),
isItalic: readBooleanElement(element.first("w:i")),
isStrikethrough: readBooleanElement(element.first("w:strike")),
+ isAllCaps: readBooleanElement(element.first("w:caps")),
isSmallCaps: readBooleanElement(element.first("w:smallCaps"))
};
});
diff --git a/lib/style-reader.js b/lib/style-reader.js
index f686b28..07521ad 100644
--- a/lib/style-reader.js
+++ b/lib/style-reader.js
@@ -39,7 +39,7 @@ function readDocumentMatcher(string) {
function documentMatcherRule() {
var sequence = lop.rules.sequence;
-
+
var identifierToConstant = function(identifier, constant) {
return lop.rules.then(
lop.rules.token("identifier", identifier),
@@ -48,15 +48,15 @@ function documentMatcherRule() {
}
);
};
-
+
var paragraphRule = identifierToConstant("p", documentMatchers.paragraph);
var runRule = identifierToConstant("r", documentMatchers.run);
-
+
var elementTypeRule = lop.rules.firstOf("p or r or table",
paragraphRule,
runRule
);
-
+
var styleIdRule = lop.rules.then(
classRule,
function(styleId) {
@@ -86,7 +86,7 @@ function documentMatcherRule() {
}
)
);
-
+
var styleNameRule = lop.rules.sequence(
lop.rules.tokenOfType("open-square-bracket"),
lop.rules.sequence.cut(),
@@ -95,7 +95,7 @@ function documentMatcherRule() {
lop.rules.tokenOfType("close-square-bracket")
).head();
-
+
var listTypeRule = lop.rules.firstOf("list type",
identifierToConstant("ordered-list", {isOrdered: true}),
identifierToConstant("unordered-list", {isOrdered: false})
@@ -130,7 +130,7 @@ function documentMatcherRule() {
return matcherOptions;
});
}
-
+
var paragraphOrRun = sequence(
sequence.capture(elementTypeRule),
sequence.capture(createMatcherSuffixesRule([
@@ -141,7 +141,7 @@ function documentMatcherRule() {
).map(function(createMatcher, matcherOptions) {
return createMatcher(matcherOptions);
});
-
+
var table = sequence(
lop.rules.token("identifier", "table"),
sequence.capture(createMatcherSuffixesRule([
@@ -156,9 +156,10 @@ function documentMatcherRule() {
var italic = identifierToConstant("i", documentMatchers.italic);
var underline = identifierToConstant("u", documentMatchers.underline);
var strikethrough = identifierToConstant("strike", documentMatchers.strikethrough);
+ var allCaps = identifierToConstant("all-caps", documentMatchers.allCaps);
var smallCaps = identifierToConstant("small-caps", documentMatchers.smallCaps);
var commentReference = identifierToConstant("comment-reference", documentMatchers.commentReference);
-
+
var breakMatcher = sequence(
lop.rules.token("identifier", "br"),
sequence.cut(),
@@ -187,6 +188,7 @@ function documentMatcherRule() {
italic,
underline,
strikethrough,
+ allCaps,
smallCaps,
commentReference,
breakMatcher
@@ -211,7 +213,7 @@ function htmlPathRule() {
}).valueOrElse(false);
}
);
-
+
var separatorRule = lop.rules.then(
lop.rules.optional(lop.rules.sequence(
lop.rules.tokenOfType("colon"),
@@ -229,7 +231,7 @@ function htmlPathRule() {
identifierRule,
lop.rules.tokenOfType("choice")
);
-
+
var styleElementRule = lop.rules.sequence(
capture(tagNamesRule),
capture(lop.rules.zeroOrMore(classRule)),
@@ -249,7 +251,7 @@ function htmlPathRule() {
}
return htmlPaths.element(tagName, attributes, options);
});
-
+
return lop.rules.firstOf("html path",
lop.rules.then(lop.rules.tokenOfType("bang"), function() {
return htmlPaths.ignore;
@@ -267,7 +269,7 @@ function htmlPathRule() {
)
);
}
-
+
var identifierRule = lop.rules.then(
lop.rules.tokenOfType("identifier"),
decodeEscapeSequences
diff --git a/lib/styles/document-matchers.js b/lib/styles/document-matchers.js
index 11b577f..005e2db 100644
--- a/lib/styles/document-matchers.js
+++ b/lib/styles/document-matchers.js
@@ -5,6 +5,7 @@ exports.bold = new Matcher("bold");
exports.italic = new Matcher("italic");
exports.underline = new Matcher("underline");
exports.strikethrough = new Matcher("strikethrough");
+exports.allCaps = new Matcher("allCaps");
exports.smallCaps = new Matcher("smallCaps");
exports.commentReference = new Matcher("commentReference");
exports.lineBreak = new Matcher("break", {breakType: "line"});
diff --git a/test/document-to-html.tests.js b/test/document-to-html.tests.js
index 6e61d45..4283e86 100644
--- a/test/document-to-html.tests.js
+++ b/test/document-to-html.tests.js
@@ -296,6 +296,30 @@ test('subscript runs are wrapped in <sub> tags', function() {
});
});
+test('all caps runs are ignored by default', function() {
+ var run = runOfText("Hello.", {isAllCaps: true});
+ var converter = new DocumentConverter();
+ return converter.convertToHtml(run).then(function(result) {
+ assert.equal(result.value, "Hello.");
+ });
+});
+
+test('all caps runs can be configured with style mapping', function() {
+ var run = runOfText("Hello.", {isAllCaps: true});
+ var converter = new DocumentConverter({
+ styleMap: [
+ {
+ from: documentMatchers.allCaps,
+ to: htmlPaths.elements([htmlPaths.element("span")])
+ }
+ ]
+ });
+ return converter.convertToHtml(run).then(function(result) {
+ assert.equal(result.value, "<span>Hello.</span>");
+ });
+});
+
+
test('small caps runs are ignored by default', function() {
var run = runOfText("Hello.", {isSmallCaps: true});
var converter = new DocumentConverter();
diff --git a/test/docx/body-reader.tests.js b/test/docx/body-reader.tests.js
index 62ae12f..adb32f8 100644
--- a/test/docx/body-reader.tests.js
+++ b/test/docx/body-reader.tests.js
@@ -531,6 +531,7 @@ var booleanRunProperties = [
{name: "isUnderline", tagName: "w:u"},
{name: "isItalic", tagName: "w:i"},
{name: "isStrikethrough", tagName: "w:strike"},
+ {name: "isAllCaps", tagName: "w:caps"},
{name: "isSmallCaps", tagName: "w:smallCaps"}
];
diff --git a/test/style-reader.tests.js b/test/style-reader.tests.js
index 7b37f25..b77ce7e 100644
--- a/test/style-reader.tests.js
+++ b/test/style-reader.tests.js
@@ -15,11 +15,11 @@ test('styleReader.readHtmlPath', {
'reads empty path': function() {
assertHtmlPath("", htmlPaths.empty);
},
-
+
'reads single element': function() {
assertHtmlPath("p", htmlPaths.elements(["p"]));
},
-
+
'reads choice of elements': function() {
assertHtmlPath(
"ul|ol",
@@ -28,18 +28,18 @@ test('styleReader.readHtmlPath', {
])
);
},
-
+
'reads nested elements': function() {
assertHtmlPath("ul > li", htmlPaths.elements(["ul", "li"]));
},
-
+
'reads class on element': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {"class": "tip"})
]);
assertHtmlPath("p.tip", expected);
},
-
+
'reads class with escaped colon': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {"class": "a:b"})
@@ -53,21 +53,21 @@ test('styleReader.readHtmlPath', {
]);
assertHtmlPath("p.tip.help", expected);
},
-
+
'reads when element must be fresh': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {}, {"fresh": true})
]);
assertHtmlPath("p:fresh", expected);
},
-
+
'reads separator for elements': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {}, {separator: "x"})
]);
assertHtmlPath("p:separator('x')", expected);
},
-
+
'reads separator with escape sequence': function() {
var expected = htmlPaths.elements([
htmlPaths.element("p", {}, {separator: "\r\n\t\'\\"})
@@ -88,21 +88,21 @@ test("styleReader.readDocumentMatcher", {
"reads plain paragraph": function() {
assertDocumentMatcher("p", documentMatchers.paragraph());
},
-
+
"reads paragraph with style ID": function() {
assertDocumentMatcher(
"p.Heading1",
documentMatchers.paragraph({styleId: "Heading1"})
);
},
-
+
"reads paragraph with exact style name": function() {
assertDocumentMatcher(
"p[style-name='Heading 1']",
documentMatchers.paragraph({styleName: documentMatchers.equalTo("Heading 1")})
);
},
-
+
"reads paragraph with style name prefix": function() {
assertDocumentMatcher(
"p[style-name^='Heading']",
@@ -116,14 +116,14 @@ test("styleReader.readDocumentMatcher", {
documentMatchers.paragraph({list: {isOrdered: true, levelIndex: 0}})
);
},
-
+
"reads p:unordered-list(1) as unordered list with index of 0": function() {
assertDocumentMatcher(
"p:unordered-list(1)",
documentMatchers.paragraph({list: {isOrdered: false, levelIndex: 0}})
);
},
-
+
"reads plain run": function() {
assertDocumentMatcher(
"r",
@@ -152,36 +152,43 @@ test("styleReader.readDocumentMatcher", {
})
);
},
-
+
"reads bold": function() {
assertDocumentMatcher(
"b",
documentMatchers.bold
);
},
-
+
"reads italic": function() {
assertDocumentMatcher(
"i",
documentMatchers.italic
);
},
-
+
"reads underline": function() {
assertDocumentMatcher(
"u",
documentMatchers.underline
);
},
-
+
"reads strikethrough": function() {
assertDocumentMatcher(
"strike",
documentMatchers.strikethrough
);
},
-
- "reads smallcaps": function() {
+
+ "reads all-caps": function() {
+ assertDocumentMatcher(
+ "all-caps",
+ documentMatchers.allCaps
+ );
+ },
+
+ "reads small-caps": function() {
assertDocumentMatcher(
"small-caps",
documentMatchers.smallCaps
@@ -194,21 +201,21 @@ test("styleReader.readDocumentMatcher", {
documentMatchers.commentReference
);
},
-
+
"reads line breaks": function() {
assertDocumentMatcher(
"br[type='line']",
documentMatchers.lineBreak
);
},
-
+
"reads page breaks": function() {
assertDocumentMatcher(
"br[type='page']",
documentMatchers.pageBreak
);
},
-
+
"reads column breaks": function() {
assertDocumentMatcher(
"br[type='column']",
@@ -217,7 +224,7 @@ test("styleReader.readDocumentMatcher", {
}
});
-
+
function assertDocumentMatcher(input, expected) {
assert.deepEqual(readDocumentMatcher(input), results.success(expected));
}
@@ -232,7 +239,7 @@ test("styleReader.read", {
}
);
},
-
+
"reads style mapping with no HTML path": function() {
assertStyleMapping(
"r =>",
@@ -242,7 +249,7 @@ test("styleReader.read", {
}
);
},
-
+
"error when not all input is consumed": function() {
assert.deepEqual(
readStyle("r => span a"),