Skip to content

Commit cee62ee

Browse files
committed
Handle numbering levels defined without an index
1 parent 660da13 commit cee62ee

File tree

5 files changed

+99
-8
lines changed

5 files changed

+99
-8
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
* Support disabling external file accesses using the externalFileAccess option.
66

7+
* Handle numbering levels defined without an index.
8+
79
# 1.10.0
810

911
* Add "Heading" and "Body" styles, as found in documents created by Apple Pages,

lib/docx/body-reader.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,13 @@ function readNumberingProperties(styleId, element, numbering) {
670670
}
671671
}
672672

673+
// Some malformed documents define numbering levels without an index, and
674+
// reference the numbering using a w:numPr element without a w:ilvl child.
675+
// To handle such cases, we assume a level of 0 as a fallback.
676+
if (numId !== undefined) {
677+
return numbering.findLevel(numId, "0");
678+
}
679+
673680
return null;
674681
}
675682

lib/docx/numbering-xml.js

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,37 @@ function readAbstractNums(root) {
6464

6565
function readAbstractNum(element) {
6666
var levels = {};
67+
68+
// Some malformed documents define numbering levels without an index, and
69+
// reference the numbering using a w:numPr element without a w:ilvl child.
70+
// To handle such cases, we assume a level of 0 as a fallback.
71+
var levelWithoutIndex = null;
72+
6773
element.getElementsByTagName("w:lvl").forEach(function(levelElement) {
6874
var levelIndex = levelElement.attributes["w:ilvl"];
6975
var numFmt = levelElement.firstOrEmpty("w:numFmt").attributes["w:val"];
76+
var isOrdered = numFmt !== "bullet";
7077
var paragraphStyleId = levelElement.firstOrEmpty("w:pStyle").attributes["w:val"];
7178

72-
levels[levelIndex] = {
73-
isOrdered: numFmt !== "bullet",
74-
level: levelIndex,
75-
paragraphStyleId: paragraphStyleId
76-
};
79+
if (levelIndex === undefined) {
80+
levelWithoutIndex = {
81+
isOrdered: isOrdered,
82+
level: "0",
83+
paragraphStyleId: paragraphStyleId
84+
};
85+
} else {
86+
levels[levelIndex] = {
87+
isOrdered: isOrdered,
88+
level: levelIndex,
89+
paragraphStyleId: paragraphStyleId
90+
};
91+
}
7792
});
7893

94+
if (levels["0"] === undefined && levelWithoutIndex !== null) {
95+
levels["0"] = levelWithoutIndex;
96+
}
97+
7998
var numStyleLink = element.firstOrEmpty("w:numStyleLink").attributes["w:val"];
8099

81100
return {levels: levels, numStyleLink: numStyleLink};

test/docx/body-reader.tests.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,17 @@ test("numbering properties are converted to numbering at specified level", funct
223223
assert.deepEqual(numberingLevel, {level: "1", isOrdered: true});
224224
});
225225

226-
test("numbering properties are ignored if w:ilvl is missing", function() {
226+
test("when numbering properties are missing w:ilvl then level of 0 is assumed", function() {
227227
var numberingPropertiesXml = new XmlElement("w:numPr", {}, [
228228
new XmlElement("w:numId", {"w:val": "42"})
229229
]);
230230

231231
var numbering = new NumberingMap({
232-
findLevel: {"42": {"1": {isOrdered: true, level: "1"}}}
232+
findLevel: {"42": {"0": {isOrdered: true, level: "0"}}}
233233
});
234234

235235
var numberingLevel = _readNumberingProperties(null, numberingPropertiesXml, numbering);
236-
assert.equal(numberingLevel, null);
236+
assert.deepEqual(numberingLevel, {level: "0", isOrdered: true});
237237
});
238238

239239
test("numbering properties are ignored if w:numId is missing", function() {

test/docx/numbering-xml.tests.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,69 @@ test('w:num element referencing non-existent w:abstractNumId is ignored', functi
4545
duck.assertThat(numbering.findLevel("47", "0"), duck.equalTo(null));
4646
});
4747

48+
test('given no other levels with index of 0 when level is missing w:ilvl then level index is 0', function() {
49+
var numbering = readNumberingXml(
50+
new XmlElement("w:numbering", {}, [
51+
new XmlElement("w:abstractNum", {"w:abstractNumId": "42"}, [
52+
new XmlElement("w:lvl", {}, [
53+
new XmlElement("w:numFmt", {"w:val": "decimal"})
54+
])
55+
]),
56+
new XmlElement("w:num", {"w:numId": "47"}, [
57+
new XmlElement("w:abstractNumId", {"w:val": "42"})
58+
])
59+
]),
60+
{styles: stylesReader.defaultStyles}
61+
);
62+
duck.assertThat(numbering.findLevel("47", "0"), duck.hasProperties({
63+
isOrdered: true
64+
}));
65+
});
66+
67+
test('given previous other level with index of 0 when level is missing w:ilvl then level is ignored', function() {
68+
var numbering = readNumberingXml(
69+
new XmlElement("w:numbering", {}, [
70+
new XmlElement("w:abstractNum", {"w:abstractNumId": "42"}, [
71+
new XmlElement("w:lvl", {"w:ilvl": "0"}, [
72+
new XmlElement("w:numFmt", {"w:val": "bullet"})
73+
]),
74+
new XmlElement("w:lvl", {}, [
75+
new XmlElement("w:numFmt", {"w:val": "decimal"})
76+
])
77+
]),
78+
new XmlElement("w:num", {"w:numId": "47"}, [
79+
new XmlElement("w:abstractNumId", {"w:val": "42"})
80+
])
81+
]),
82+
{styles: stylesReader.defaultStyles}
83+
);
84+
duck.assertThat(numbering.findLevel("47", "0"), duck.hasProperties({
85+
isOrdered: false
86+
}));
87+
});
88+
89+
test('given subsequent other level with index of 0 when level is missing w:ilvl then level is ignored', function() {
90+
var numbering = readNumberingXml(
91+
new XmlElement("w:numbering", {}, [
92+
new XmlElement("w:abstractNum", {"w:abstractNumId": "42"}, [
93+
new XmlElement("w:lvl", {}, [
94+
new XmlElement("w:numFmt", {"w:val": "decimal"})
95+
]),
96+
new XmlElement("w:lvl", {"w:ilvl": "0"}, [
97+
new XmlElement("w:numFmt", {"w:val": "bullet"})
98+
])
99+
]),
100+
new XmlElement("w:num", {"w:numId": "47"}, [
101+
new XmlElement("w:abstractNumId", {"w:val": "42"})
102+
])
103+
]),
104+
{styles: stylesReader.defaultStyles}
105+
);
106+
duck.assertThat(numbering.findLevel("47", "0"), duck.hasProperties({
107+
isOrdered: false
108+
}));
109+
});
110+
48111
test('when level is missing w:numFmt then level is ordered', function() {
49112
var numbering = readNumberingXml(
50113
new XmlElement("w:numbering", {}, [

0 commit comments

Comments
 (0)