aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2016-05-10 21:25:10 +0100
committerMichael Williamson <mike@zwobble.org>2016-05-10 21:25:10 +0100
commit1417c93bebe0426be97d8f334fc279a6eb10deda (patch)
treef3c3505e247f7f5a7a0e93b452d38dae8815a580 /lib
parentcda64f608faf532bb67a131453269edfa888955a (diff)
Add support for table cells spanning multiple rows
Diffstat (limited to 'lib')
-rw-r--r--lib/documents.js5
-rw-r--r--lib/docx/body-reader.js98
2 files changed, 85 insertions, 18 deletions
diff --git a/lib/documents.js b/lib/documents.js
index 8b979c2..5665198 100644
--- a/lib/documents.js
+++ b/lib/documents.js
@@ -1,6 +1,6 @@
var _ = require("underscore");
-var types = {
+var types = exports.types = {
document: "document",
paragraph: "paragraph",
run: "run",
@@ -144,7 +144,8 @@ function TableCell(children, options) {
return {
type: types.tableCell,
children: children,
- colSpan: options.colSpan == null ? 1 : options.colSpan
+ colSpan: options.colSpan == null ? 1 : options.colSpan,
+ rowSpan: options.rowSpan == null ? 1 : options.rowSpan
};
}
diff --git a/lib/docx/body-reader.js b/lib/docx/body-reader.js
index 126896c..70d5402 100644
--- a/lib/docx/body-reader.js
+++ b/lib/docx/body-reader.js
@@ -157,22 +157,9 @@ function BodyReader(options) {
}
});
},
- "w:tbl": function(element) {
- return readXmlElements(element.children).map(documents.Table);
- },
- "w:tr": function(element) {
- return readXmlElements(element.children).map(documents.TableRow);
- },
- "w:tc": function(element) {
- return readXmlElements(element.children).map(function(children) {
- var gridSpan = element
- .firstOrEmpty("w:tcPr")
- .firstOrEmpty("w:gridSpan")
- .attributes["w:val"];
- var colSpan = gridSpan ? parseInt(gridSpan) : 1;
- return documents.TableCell(children, {colSpan: colSpan});
- });
- },
+ "w:tbl": readTable,
+ "w:tr": readTableRow,
+ "w:tc": readTableCell,
"w:footnoteReference": noteReferenceReader("footnote"),
"w:endnoteReference": noteReferenceReader("endnote"),
"w:br": function(element) {
@@ -225,6 +212,85 @@ function BodyReader(options) {
return numbering.findLevel(numId, level);
}
}
+
+ function readTable(element) {
+ return readXmlElements(element.children)
+ .flatMap(calculateRowSpans)
+ .map(documents.Table);
+ }
+
+ function readTableRow(element) {
+ return readXmlElements(element.children).map(documents.TableRow);
+ }
+
+ function readTableCell(element) {
+ return readXmlElements(element.children).map(function(children) {
+ var properties = element.firstOrEmpty("w:tcPr");
+
+ var gridSpan = properties.firstOrEmpty("w:gridSpan").attributes["w:val"];
+ var colSpan = gridSpan ? parseInt(gridSpan) : 1;
+
+ var cell = documents.TableCell(children, {colSpan: colSpan});
+ cell._vMerge = readVMerge(properties);
+ return cell;
+ });
+ }
+
+ function readVMerge(properties) {
+ var element = properties.first("w:vMerge");
+ if (element) {
+ var val = element.attributes["w:val"];
+ return val ? val : "continue";
+ } else {
+ return null;
+ }
+ }
+
+ function calculateRowSpans(rows) {
+ var unexpectedNonRows = _.any(rows, function(row) {
+ return row.type !== documents.types.tableRow;
+ });
+ if (unexpectedNonRows) {
+ return elementResultWithMessages(rows, [warning(
+ "unexpected non-row element in table, cell merging may be incorrect"
+ )]);
+ }
+ var unexpectedNonCells = _.any(rows, function(row) {
+ return _.any(row.children, function(cell) {
+ return cell.type !== documents.types.tableCell;
+ });
+ });
+ if (unexpectedNonCells) {
+ return elementResultWithMessages(rows, [warning(
+ "unexpected non-cell element in table row, cell merging may be incorrect"
+ )]);
+ }
+
+ var columns = {};
+
+ rows.forEach(function(row, rowIndex) {
+ var cellIndex = 0;
+ row.children.forEach(function(cell) {
+ if (cell._vMerge === "continue" && columns[cellIndex]) {
+ columns[cellIndex].rowSpan++;
+ } else {
+ columns[cellIndex] = cell;
+ }
+ cellIndex += cell.colSpan;
+ });
+ });
+
+ rows.forEach(function(row, rowIndex) {
+ row.children = row.children.filter(function(cell) {
+ return cell._vMerge !== "continue";
+ });
+ row.children.forEach(function(cell) {
+ delete cell._vMerge;
+ });
+ });
+
+ return elementResult(rows);
+ }
function readDrawingElement(element) {
var blips = element