doc-codec 1.0.2 → 1.1.1
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/README.md +192 -50
- package/dist/color.cjs +166 -0
- package/dist/color.d.cts +20 -0
- package/dist/color.d.ts +20 -0
- package/dist/color.js +160 -0
- package/dist/detect.cjs +3 -0
- package/dist/detect.d.cts +3 -1
- package/dist/detect.d.ts +3 -1
- package/dist/detect.js +3 -1
- package/dist/fib/fib.cjs +9 -1
- package/dist/fib/fib.d.cts +1 -28
- package/dist/fib/fib.d.ts +1 -28
- package/dist/fib/fib.js +9 -1
- package/dist/fib/offsets.cjs +9 -1
- package/dist/fib/offsets.d.cts +8 -0
- package/dist/fib/offsets.d.ts +8 -0
- package/dist/fib/offsets.js +9 -1
- package/dist/fib/write.cjs +43 -0
- package/dist/fib/write.d.cts +21 -0
- package/dist/fib/write.d.ts +21 -0
- package/dist/fib/write.js +42 -0
- package/dist/fib-CtsLtF96.d.cts +37 -0
- package/dist/fib-CtsLtF96.d.ts +37 -0
- package/dist/index.cjs +31 -0
- package/dist/index.d.cts +18 -7
- package/dist/index.d.ts +18 -7
- package/dist/index.js +14 -3
- package/dist/list/numbering.cjs +206 -0
- package/dist/list/numbering.d.cts +2 -0
- package/dist/list/numbering.d.ts +2 -0
- package/dist/list/numbering.js +205 -0
- package/dist/metadata.cjs +14 -0
- package/dist/metadata.d.cts +6 -0
- package/dist/metadata.d.ts +6 -0
- package/dist/metadata.js +13 -0
- package/dist/numbering-Cir-uGdc.d.cts +22 -0
- package/dist/numbering-DF1z1PyG.d.ts +22 -0
- package/dist/pap-BkFo8dZO.d.ts +29 -0
- package/dist/pap-BmelEYEP.d.cts +29 -0
- package/dist/prop/chp-write.cjs +47 -0
- package/dist/prop/chp-write.d.cts +5 -0
- package/dist/prop/chp-write.d.ts +5 -0
- package/dist/prop/chp-write.js +46 -0
- package/dist/prop/chp.cjs +15 -107
- package/dist/prop/chp.d.cts +3 -2
- package/dist/prop/chp.d.ts +3 -2
- package/dist/prop/chp.js +15 -107
- package/dist/prop/fkp-write.cjs +139 -0
- package/dist/prop/fkp-write.d.cts +21 -0
- package/dist/prop/fkp-write.d.ts +21 -0
- package/dist/prop/fkp-write.js +135 -0
- package/dist/prop/pap-write.cjs +67 -0
- package/dist/prop/pap-write.d.cts +5 -0
- package/dist/prop/pap-write.d.ts +5 -0
- package/dist/prop/pap-write.js +66 -0
- package/dist/prop/pap.cjs +10 -0
- package/dist/prop/pap.d.cts +1 -24
- package/dist/prop/pap.d.ts +1 -24
- package/dist/prop/pap.js +11 -1
- package/dist/prop/sep-write.cjs +63 -0
- package/dist/prop/sep-write.d.cts +9 -0
- package/dist/prop/sep-write.d.ts +9 -0
- package/dist/prop/sep-write.js +60 -0
- package/dist/prop/sep.cjs +63 -0
- package/dist/prop/sep.d.cts +16 -0
- package/dist/prop/sep.d.ts +16 -0
- package/dist/prop/sep.js +61 -0
- package/dist/prop/sprm.d.cts +1 -25
- package/dist/prop/sprm.d.ts +1 -25
- package/dist/read.cjs +44 -18
- package/dist/read.d.cts +21 -4
- package/dist/read.d.ts +21 -4
- package/dist/read.js +46 -20
- package/dist/sprm-Djg5RNiq.d.cts +26 -0
- package/dist/sprm-Djg5RNiq.d.ts +26 -0
- package/dist/style/fonts.cjs +68 -0
- package/dist/style/fonts.d.cts +5 -0
- package/dist/style/fonts.d.ts +5 -0
- package/dist/style/fonts.js +66 -0
- package/dist/style/stsh.cjs +23 -0
- package/dist/style/stsh.d.cts +2 -1
- package/dist/style/stsh.d.ts +2 -1
- package/dist/style/stsh.js +23 -1
- package/dist/table/decoration.cjs +220 -0
- package/dist/table/decoration.d.cts +41 -0
- package/dist/table/decoration.d.ts +41 -0
- package/dist/table/decoration.js +205 -0
- package/dist/table/read.cjs +192 -0
- package/dist/table/read.d.cts +6 -0
- package/dist/table/read.d.ts +6 -0
- package/dist/table/read.js +191 -0
- package/dist/table/tap-write.cjs +125 -0
- package/dist/table/tap-write.d.cts +13 -0
- package/dist/table/tap-write.d.ts +13 -0
- package/dist/table/tap-write.js +124 -0
- package/dist/table/tap.cjs +217 -0
- package/dist/table/tap.d.cts +29 -0
- package/dist/table/tap.d.ts +29 -0
- package/dist/table/tap.js +213 -0
- package/dist/table/write.cjs +136 -0
- package/dist/table/write.d.cts +13 -0
- package/dist/table/write.d.ts +13 -0
- package/dist/table/write.js +135 -0
- package/dist/text/piece-table-write.cjs +24 -0
- package/dist/text/piece-table-write.d.cts +4 -0
- package/dist/text/piece-table-write.d.ts +4 -0
- package/dist/text/piece-table-write.js +23 -0
- package/dist/write.cjs +175 -0
- package/dist/write.d.cts +5 -0
- package/dist/write.d.ts +5 -0
- package/dist/write.js +174 -0
- package/package.json +3 -3
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_errors = require("../errors.cjs");
|
|
3
|
+
require("../text/special.cjs");
|
|
4
|
+
const require_table_tap = require("./tap.cjs");
|
|
5
|
+
//#region src/table/read.ts
|
|
6
|
+
const TWIPS_PER_POINT = 20;
|
|
7
|
+
function isSameColumnBoundary(left, right, toleranceTwips) {
|
|
8
|
+
return Math.abs(left - right) <= toleranceTwips;
|
|
9
|
+
}
|
|
10
|
+
function effectiveColumnBoundaryTolerance(definitions) {
|
|
11
|
+
let narrowestRealGapTwips;
|
|
12
|
+
for (const definition of definitions) {
|
|
13
|
+
const boundaries = definition.columnBoundariesTwips;
|
|
14
|
+
for (let index = 1; index < boundaries.length; index += 1) {
|
|
15
|
+
const left = boundaries[index - 1];
|
|
16
|
+
const right = boundaries[index];
|
|
17
|
+
if (left === void 0 || right === void 0) continue;
|
|
18
|
+
const gap = right - left;
|
|
19
|
+
if (gap > 0 && (narrowestRealGapTwips === void 0 || gap < narrowestRealGapTwips)) narrowestRealGapTwips = gap;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return narrowestRealGapTwips === void 0 ? TWIPS_PER_POINT : Math.min(TWIPS_PER_POINT, narrowestRealGapTwips - 1);
|
|
23
|
+
}
|
|
24
|
+
function assembleBlocks(entries) {
|
|
25
|
+
const blocks = [];
|
|
26
|
+
let index = 0;
|
|
27
|
+
while (index < entries.length) {
|
|
28
|
+
const entry = entries[index];
|
|
29
|
+
if (entry === void 0) break;
|
|
30
|
+
if (entry.properties.inTable !== true) {
|
|
31
|
+
blocks.push(entry.paragraph);
|
|
32
|
+
index += 1;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const { runEntries, nextIndex } = collectTableRun(entries, index);
|
|
36
|
+
const table = tryAssembleTable(runEntries);
|
|
37
|
+
blocks.push(...table !== void 0 ? [table] : runEntries.map((run) => run.paragraph));
|
|
38
|
+
index = nextIndex;
|
|
39
|
+
}
|
|
40
|
+
return blocks;
|
|
41
|
+
}
|
|
42
|
+
function collectTableRun(entries, start) {
|
|
43
|
+
const runEntries = [];
|
|
44
|
+
let index = start;
|
|
45
|
+
while (index < entries.length) {
|
|
46
|
+
const entry = entries[index];
|
|
47
|
+
if (entry?.properties.inTable !== true) break;
|
|
48
|
+
if (entry.properties.tableDepth !== void 0 && entry.properties.tableDepth > 1 || entry.properties.nestedTableMark === true) throw new require_errors.DocUnsupportedError("doc-codec does not support a table nested inside a table cell (table depth greater than 1)");
|
|
49
|
+
runEntries.push(entry);
|
|
50
|
+
index += 1;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
runEntries,
|
|
54
|
+
nextIndex: index
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function tryAssembleTable(runEntries) {
|
|
58
|
+
const rawRows = [];
|
|
59
|
+
const rowDefinitions = [];
|
|
60
|
+
const rowHeights = [];
|
|
61
|
+
let cellParagraphs = [];
|
|
62
|
+
let rowCells = [];
|
|
63
|
+
for (const entry of runEntries) {
|
|
64
|
+
cellParagraphs.push(entry.paragraph);
|
|
65
|
+
if (entry.properties.tableRowEnd === true) {
|
|
66
|
+
const rowProperties = require_table_tap.applyTableSprms(entry.grpprl, {});
|
|
67
|
+
const definition = rowProperties.definition;
|
|
68
|
+
if (definition === void 0) return void 0;
|
|
69
|
+
if (rowCells.length !== definition.cells.length) return void 0;
|
|
70
|
+
rawRows.push(rowCells.map((cell, cellIndex) => {
|
|
71
|
+
const merge = definition.cells[cellIndex];
|
|
72
|
+
if (merge === void 0) throw new require_errors.DocFormatError(`internal defect: table row cell ${cellIndex} has no TAP merge entry despite the length check above`);
|
|
73
|
+
return {
|
|
74
|
+
horzMerge: merge.horzMerge,
|
|
75
|
+
vertMerge: merge.vertMerge,
|
|
76
|
+
borders: merge.borders,
|
|
77
|
+
background: merge.background,
|
|
78
|
+
blocks: cell.blocks
|
|
79
|
+
};
|
|
80
|
+
}));
|
|
81
|
+
rowDefinitions.push(definition);
|
|
82
|
+
rowHeights.push(rowProperties.heightPt);
|
|
83
|
+
rowCells = [];
|
|
84
|
+
cellParagraphs = [];
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (entry.terminator === 7) {
|
|
88
|
+
rowCells.push({ blocks: cellParagraphs });
|
|
89
|
+
cellParagraphs = [];
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (cellParagraphs.length > 0 || rowCells.length > 0) throw new require_errors.DocFormatError("a table's paragraphs end without a row-ending mark to close the row's last cell");
|
|
93
|
+
const toleranceTwips = effectiveColumnBoundaryTolerance(rowDefinitions);
|
|
94
|
+
const columnBoundariesTwips = canonicalColumnBoundariesTwips(rowDefinitions, toleranceTwips);
|
|
95
|
+
return {
|
|
96
|
+
kind: "table",
|
|
97
|
+
rows: buildRows(rawRows, rowDefinitions, columnBoundariesTwips, rowHeights, toleranceTwips),
|
|
98
|
+
columnWidthsPt: columnWidthsFromBoundaries(columnBoundariesTwips)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function canonicalColumnBoundariesTwips(definitions, toleranceTwips) {
|
|
102
|
+
const sorted = definitions.flatMap((definition) => definition.columnBoundariesTwips).sort((left, right) => left - right);
|
|
103
|
+
const canonical = [];
|
|
104
|
+
for (const boundary of sorted) {
|
|
105
|
+
const representative = canonical[canonical.length - 1];
|
|
106
|
+
if (representative === void 0 || !isSameColumnBoundary(representative, boundary, toleranceTwips)) canonical.push(boundary);
|
|
107
|
+
}
|
|
108
|
+
return canonical;
|
|
109
|
+
}
|
|
110
|
+
function gridIndexFor(canonicalBoundariesTwips, boundary, toleranceTwips) {
|
|
111
|
+
const index = canonicalBoundariesTwips.findIndex((candidate) => isSameColumnBoundary(candidate, boundary, toleranceTwips));
|
|
112
|
+
if (index === -1) throw new require_errors.DocFormatError(`internal defect: a table row's column boundary ${String(boundary)} matches no boundary on the table's own reconstructed grid, which was built from that boundary among others`);
|
|
113
|
+
return index;
|
|
114
|
+
}
|
|
115
|
+
function columnWidthsFromBoundaries(boundaries) {
|
|
116
|
+
const widths = [];
|
|
117
|
+
for (let index = 0; index < boundaries.length - 1; index += 1) {
|
|
118
|
+
const left = boundaries[index];
|
|
119
|
+
const right = boundaries[index + 1];
|
|
120
|
+
if (left === void 0 || right === void 0) continue;
|
|
121
|
+
widths.push((right - left) / TWIPS_PER_POINT);
|
|
122
|
+
}
|
|
123
|
+
return widths;
|
|
124
|
+
}
|
|
125
|
+
function logicalCellsForRow(cells, rowBoundariesTwips, canonicalBoundariesTwips, toleranceTwips) {
|
|
126
|
+
const logical = [];
|
|
127
|
+
let physicalIndex = 0;
|
|
128
|
+
while (physicalIndex < cells.length) {
|
|
129
|
+
const cell = cells[physicalIndex];
|
|
130
|
+
if (cell === void 0) break;
|
|
131
|
+
if (cell.horzMerge === 1) {
|
|
132
|
+
physicalIndex += 1;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
let consumed = 1;
|
|
136
|
+
while (cells[physicalIndex + consumed]?.horzMerge === 1) consumed += 1;
|
|
137
|
+
const left = rowBoundariesTwips[physicalIndex];
|
|
138
|
+
const right = rowBoundariesTwips[physicalIndex + consumed];
|
|
139
|
+
if (left === void 0 || right === void 0) throw new require_errors.DocFormatError("a table row's own column-boundary array has fewer entries than its physical cell count requires");
|
|
140
|
+
const startGridIndex = gridIndexFor(canonicalBoundariesTwips, left, toleranceTwips);
|
|
141
|
+
const endGridIndex = gridIndexFor(canonicalBoundariesTwips, right, toleranceTwips);
|
|
142
|
+
logical.push({
|
|
143
|
+
startGridIndex,
|
|
144
|
+
colSpan: endGridIndex > startGridIndex ? endGridIndex - startGridIndex : 1,
|
|
145
|
+
vertMerge: cell.vertMerge,
|
|
146
|
+
borders: cell.borders,
|
|
147
|
+
background: cell.background,
|
|
148
|
+
blocks: cell.blocks
|
|
149
|
+
});
|
|
150
|
+
physicalIndex += consumed;
|
|
151
|
+
}
|
|
152
|
+
return logical;
|
|
153
|
+
}
|
|
154
|
+
function buildRows(rawRows, rowDefinitions, canonicalBoundariesTwips, rowHeights, toleranceTwips) {
|
|
155
|
+
const logicalRows = rawRows.map((row, rowIndex) => {
|
|
156
|
+
const definition = rowDefinitions[rowIndex];
|
|
157
|
+
if (definition === void 0) throw new require_errors.DocFormatError(`internal defect: table row ${rowIndex} has no TAP definition despite the earlier length check`);
|
|
158
|
+
return logicalCellsForRow(row, definition.columnBoundariesTwips, canonicalBoundariesTwips, toleranceTwips);
|
|
159
|
+
});
|
|
160
|
+
return logicalRows.map((row, rowIndex) => {
|
|
161
|
+
const cells = [];
|
|
162
|
+
for (const cell of row) {
|
|
163
|
+
const colSpan = cell.colSpan > 1 ? cell.colSpan : void 0;
|
|
164
|
+
if (cell.vertMerge === 1) {
|
|
165
|
+
cells.push({
|
|
166
|
+
blocks: [],
|
|
167
|
+
colSpan
|
|
168
|
+
});
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
let rowSpan = 1;
|
|
172
|
+
for (let r = rowIndex + 1; r < logicalRows.length; r += 1) {
|
|
173
|
+
if ((logicalRows[r]?.find((candidate) => candidate.startGridIndex === cell.startGridIndex))?.vertMerge !== 1) break;
|
|
174
|
+
rowSpan += 1;
|
|
175
|
+
}
|
|
176
|
+
cells.push({
|
|
177
|
+
blocks: cell.blocks,
|
|
178
|
+
colSpan,
|
|
179
|
+
rowSpan: rowSpan > 1 ? rowSpan : void 0,
|
|
180
|
+
background: cell.background,
|
|
181
|
+
borders: cell.borders
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const heightPt = rowHeights[rowIndex];
|
|
185
|
+
return heightPt !== void 0 ? {
|
|
186
|
+
cells,
|
|
187
|
+
heightPt
|
|
188
|
+
} : { cells };
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
exports.assembleBlocks = assembleBlocks;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { DocFormatError, DocUnsupportedError } from "../errors.js";
|
|
2
|
+
import "../text/special.js";
|
|
3
|
+
import { applyTableSprms } from "./tap.js";
|
|
4
|
+
//#region src/table/read.ts
|
|
5
|
+
const TWIPS_PER_POINT = 20;
|
|
6
|
+
function isSameColumnBoundary(left, right, toleranceTwips) {
|
|
7
|
+
return Math.abs(left - right) <= toleranceTwips;
|
|
8
|
+
}
|
|
9
|
+
function effectiveColumnBoundaryTolerance(definitions) {
|
|
10
|
+
let narrowestRealGapTwips;
|
|
11
|
+
for (const definition of definitions) {
|
|
12
|
+
const boundaries = definition.columnBoundariesTwips;
|
|
13
|
+
for (let index = 1; index < boundaries.length; index += 1) {
|
|
14
|
+
const left = boundaries[index - 1];
|
|
15
|
+
const right = boundaries[index];
|
|
16
|
+
if (left === void 0 || right === void 0) continue;
|
|
17
|
+
const gap = right - left;
|
|
18
|
+
if (gap > 0 && (narrowestRealGapTwips === void 0 || gap < narrowestRealGapTwips)) narrowestRealGapTwips = gap;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return narrowestRealGapTwips === void 0 ? TWIPS_PER_POINT : Math.min(TWIPS_PER_POINT, narrowestRealGapTwips - 1);
|
|
22
|
+
}
|
|
23
|
+
function assembleBlocks(entries) {
|
|
24
|
+
const blocks = [];
|
|
25
|
+
let index = 0;
|
|
26
|
+
while (index < entries.length) {
|
|
27
|
+
const entry = entries[index];
|
|
28
|
+
if (entry === void 0) break;
|
|
29
|
+
if (entry.properties.inTable !== true) {
|
|
30
|
+
blocks.push(entry.paragraph);
|
|
31
|
+
index += 1;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const { runEntries, nextIndex } = collectTableRun(entries, index);
|
|
35
|
+
const table = tryAssembleTable(runEntries);
|
|
36
|
+
blocks.push(...table !== void 0 ? [table] : runEntries.map((run) => run.paragraph));
|
|
37
|
+
index = nextIndex;
|
|
38
|
+
}
|
|
39
|
+
return blocks;
|
|
40
|
+
}
|
|
41
|
+
function collectTableRun(entries, start) {
|
|
42
|
+
const runEntries = [];
|
|
43
|
+
let index = start;
|
|
44
|
+
while (index < entries.length) {
|
|
45
|
+
const entry = entries[index];
|
|
46
|
+
if (entry?.properties.inTable !== true) break;
|
|
47
|
+
if (entry.properties.tableDepth !== void 0 && entry.properties.tableDepth > 1 || entry.properties.nestedTableMark === true) throw new DocUnsupportedError("doc-codec does not support a table nested inside a table cell (table depth greater than 1)");
|
|
48
|
+
runEntries.push(entry);
|
|
49
|
+
index += 1;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
runEntries,
|
|
53
|
+
nextIndex: index
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function tryAssembleTable(runEntries) {
|
|
57
|
+
const rawRows = [];
|
|
58
|
+
const rowDefinitions = [];
|
|
59
|
+
const rowHeights = [];
|
|
60
|
+
let cellParagraphs = [];
|
|
61
|
+
let rowCells = [];
|
|
62
|
+
for (const entry of runEntries) {
|
|
63
|
+
cellParagraphs.push(entry.paragraph);
|
|
64
|
+
if (entry.properties.tableRowEnd === true) {
|
|
65
|
+
const rowProperties = applyTableSprms(entry.grpprl, {});
|
|
66
|
+
const definition = rowProperties.definition;
|
|
67
|
+
if (definition === void 0) return void 0;
|
|
68
|
+
if (rowCells.length !== definition.cells.length) return void 0;
|
|
69
|
+
rawRows.push(rowCells.map((cell, cellIndex) => {
|
|
70
|
+
const merge = definition.cells[cellIndex];
|
|
71
|
+
if (merge === void 0) throw new DocFormatError(`internal defect: table row cell ${cellIndex} has no TAP merge entry despite the length check above`);
|
|
72
|
+
return {
|
|
73
|
+
horzMerge: merge.horzMerge,
|
|
74
|
+
vertMerge: merge.vertMerge,
|
|
75
|
+
borders: merge.borders,
|
|
76
|
+
background: merge.background,
|
|
77
|
+
blocks: cell.blocks
|
|
78
|
+
};
|
|
79
|
+
}));
|
|
80
|
+
rowDefinitions.push(definition);
|
|
81
|
+
rowHeights.push(rowProperties.heightPt);
|
|
82
|
+
rowCells = [];
|
|
83
|
+
cellParagraphs = [];
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (entry.terminator === 7) {
|
|
87
|
+
rowCells.push({ blocks: cellParagraphs });
|
|
88
|
+
cellParagraphs = [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (cellParagraphs.length > 0 || rowCells.length > 0) throw new DocFormatError("a table's paragraphs end without a row-ending mark to close the row's last cell");
|
|
92
|
+
const toleranceTwips = effectiveColumnBoundaryTolerance(rowDefinitions);
|
|
93
|
+
const columnBoundariesTwips = canonicalColumnBoundariesTwips(rowDefinitions, toleranceTwips);
|
|
94
|
+
return {
|
|
95
|
+
kind: "table",
|
|
96
|
+
rows: buildRows(rawRows, rowDefinitions, columnBoundariesTwips, rowHeights, toleranceTwips),
|
|
97
|
+
columnWidthsPt: columnWidthsFromBoundaries(columnBoundariesTwips)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function canonicalColumnBoundariesTwips(definitions, toleranceTwips) {
|
|
101
|
+
const sorted = definitions.flatMap((definition) => definition.columnBoundariesTwips).sort((left, right) => left - right);
|
|
102
|
+
const canonical = [];
|
|
103
|
+
for (const boundary of sorted) {
|
|
104
|
+
const representative = canonical[canonical.length - 1];
|
|
105
|
+
if (representative === void 0 || !isSameColumnBoundary(representative, boundary, toleranceTwips)) canonical.push(boundary);
|
|
106
|
+
}
|
|
107
|
+
return canonical;
|
|
108
|
+
}
|
|
109
|
+
function gridIndexFor(canonicalBoundariesTwips, boundary, toleranceTwips) {
|
|
110
|
+
const index = canonicalBoundariesTwips.findIndex((candidate) => isSameColumnBoundary(candidate, boundary, toleranceTwips));
|
|
111
|
+
if (index === -1) throw new DocFormatError(`internal defect: a table row's column boundary ${String(boundary)} matches no boundary on the table's own reconstructed grid, which was built from that boundary among others`);
|
|
112
|
+
return index;
|
|
113
|
+
}
|
|
114
|
+
function columnWidthsFromBoundaries(boundaries) {
|
|
115
|
+
const widths = [];
|
|
116
|
+
for (let index = 0; index < boundaries.length - 1; index += 1) {
|
|
117
|
+
const left = boundaries[index];
|
|
118
|
+
const right = boundaries[index + 1];
|
|
119
|
+
if (left === void 0 || right === void 0) continue;
|
|
120
|
+
widths.push((right - left) / TWIPS_PER_POINT);
|
|
121
|
+
}
|
|
122
|
+
return widths;
|
|
123
|
+
}
|
|
124
|
+
function logicalCellsForRow(cells, rowBoundariesTwips, canonicalBoundariesTwips, toleranceTwips) {
|
|
125
|
+
const logical = [];
|
|
126
|
+
let physicalIndex = 0;
|
|
127
|
+
while (physicalIndex < cells.length) {
|
|
128
|
+
const cell = cells[physicalIndex];
|
|
129
|
+
if (cell === void 0) break;
|
|
130
|
+
if (cell.horzMerge === 1) {
|
|
131
|
+
physicalIndex += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
let consumed = 1;
|
|
135
|
+
while (cells[physicalIndex + consumed]?.horzMerge === 1) consumed += 1;
|
|
136
|
+
const left = rowBoundariesTwips[physicalIndex];
|
|
137
|
+
const right = rowBoundariesTwips[physicalIndex + consumed];
|
|
138
|
+
if (left === void 0 || right === void 0) throw new DocFormatError("a table row's own column-boundary array has fewer entries than its physical cell count requires");
|
|
139
|
+
const startGridIndex = gridIndexFor(canonicalBoundariesTwips, left, toleranceTwips);
|
|
140
|
+
const endGridIndex = gridIndexFor(canonicalBoundariesTwips, right, toleranceTwips);
|
|
141
|
+
logical.push({
|
|
142
|
+
startGridIndex,
|
|
143
|
+
colSpan: endGridIndex > startGridIndex ? endGridIndex - startGridIndex : 1,
|
|
144
|
+
vertMerge: cell.vertMerge,
|
|
145
|
+
borders: cell.borders,
|
|
146
|
+
background: cell.background,
|
|
147
|
+
blocks: cell.blocks
|
|
148
|
+
});
|
|
149
|
+
physicalIndex += consumed;
|
|
150
|
+
}
|
|
151
|
+
return logical;
|
|
152
|
+
}
|
|
153
|
+
function buildRows(rawRows, rowDefinitions, canonicalBoundariesTwips, rowHeights, toleranceTwips) {
|
|
154
|
+
const logicalRows = rawRows.map((row, rowIndex) => {
|
|
155
|
+
const definition = rowDefinitions[rowIndex];
|
|
156
|
+
if (definition === void 0) throw new DocFormatError(`internal defect: table row ${rowIndex} has no TAP definition despite the earlier length check`);
|
|
157
|
+
return logicalCellsForRow(row, definition.columnBoundariesTwips, canonicalBoundariesTwips, toleranceTwips);
|
|
158
|
+
});
|
|
159
|
+
return logicalRows.map((row, rowIndex) => {
|
|
160
|
+
const cells = [];
|
|
161
|
+
for (const cell of row) {
|
|
162
|
+
const colSpan = cell.colSpan > 1 ? cell.colSpan : void 0;
|
|
163
|
+
if (cell.vertMerge === 1) {
|
|
164
|
+
cells.push({
|
|
165
|
+
blocks: [],
|
|
166
|
+
colSpan
|
|
167
|
+
});
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
let rowSpan = 1;
|
|
171
|
+
for (let r = rowIndex + 1; r < logicalRows.length; r += 1) {
|
|
172
|
+
if ((logicalRows[r]?.find((candidate) => candidate.startGridIndex === cell.startGridIndex))?.vertMerge !== 1) break;
|
|
173
|
+
rowSpan += 1;
|
|
174
|
+
}
|
|
175
|
+
cells.push({
|
|
176
|
+
blocks: cell.blocks,
|
|
177
|
+
colSpan,
|
|
178
|
+
rowSpan: rowSpan > 1 ? rowSpan : void 0,
|
|
179
|
+
background: cell.background,
|
|
180
|
+
borders: cell.borders
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
const heightPt = rowHeights[rowIndex];
|
|
184
|
+
return heightPt !== void 0 ? {
|
|
185
|
+
cells,
|
|
186
|
+
heightPt
|
|
187
|
+
} : { cells };
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
export { assembleBlocks };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_errors = require("../errors.cjs");
|
|
3
|
+
const require_table_decoration = require("./decoration.cjs");
|
|
4
|
+
//#region src/table/tap-write.ts
|
|
5
|
+
/** sprmTDefTable, [MS-DOC] 2.6.3 (0xD608). */
|
|
6
|
+
const SPRM_T_DEF_TABLE = 54792;
|
|
7
|
+
/** sprmTDyaRowHeight (0x9407). */
|
|
8
|
+
const SPRM_T_DYA_ROW_HEIGHT = 37895;
|
|
9
|
+
/** sprmTDefTableShd (0xD612): a DefTableShdOperand ([MS-DOC] 2.9.53) shading cells 1-22 of the row, then sprmTDefTableShd2nd (0xD616) for 23-44 and sprmTDefTableShd3rd (0xD60C) for 45-63. Three sprms rather than one because a DefTableShdOperand's own cb is a single byte and its rgShd "MUST NOT exceed 22 elements", which is exactly why [MS-DOC] splits a row's shading across three opcodes at all. */
|
|
10
|
+
const SPRM_T_DEF_TABLE_SHD = 54802;
|
|
11
|
+
const SPRM_T_DEF_TABLE_SHD_2ND = 54806;
|
|
12
|
+
const SPRM_T_DEF_TABLE_SHD_3RD = 54796;
|
|
13
|
+
/** sprmTSetBrc (0xD62F): a TableBrcOperand ([MS-DOC] 2.9.305) restating one cell's borders on the named sides with an exact COLORREF. */
|
|
14
|
+
const SPRM_T_SET_BRC = 54831;
|
|
15
|
+
/** The three DefTableShdOperand sprms in cell order, each with the cell index its own rgShd starts at -- [MS-DOC] 2.6.3's own "Cells 1 - 22 ... cells 23 - 44 ... cells 45 - 63", one-based there and zero-based here. */
|
|
16
|
+
const SHD_ARRAYS = [
|
|
17
|
+
{
|
|
18
|
+
opcode: SPRM_T_DEF_TABLE_SHD,
|
|
19
|
+
first: 0
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
opcode: SPRM_T_DEF_TABLE_SHD_2ND,
|
|
23
|
+
first: 22
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
opcode: SPRM_T_DEF_TABLE_SHD_3RD,
|
|
27
|
+
first: 44
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
/** A DefTableShdOperand's own rgShd bound, [MS-DOC] 2.9.53: "The number of elements is equal to cb / 10 and MUST NOT exceed 22." */
|
|
31
|
+
const MAX_SHD_PER_ARRAY = 22;
|
|
32
|
+
const TWIPS_PER_POINT = 20;
|
|
33
|
+
/** A table row has "between 1 and 63 table cells" ([MS-DOC] 2.4.3), and TDefTableOperand.NumberOfColumns is itself a single byte "MUST NOT exceed 63". */
|
|
34
|
+
const MAX_COLUMNS = 63;
|
|
35
|
+
/** sprmPDyaBefore/After's unsigned 2-byte operand range, reused here for XAS column boundaries (also an unsigned 2-byte field in practice for the non-negative widths this writer produces). */
|
|
36
|
+
const MIN_INT16 = -32768;
|
|
37
|
+
const MAX_INT16 = 32767;
|
|
38
|
+
/** TableBrcOperand.cb, [MS-DOC] 2.9.305: "This value MUST be 11" -- the ItcFirstLim (2), bordersToApply (1) and Brc (8) that follow it. */
|
|
39
|
+
const TABLE_BRC_OPERAND_CB = 11;
|
|
40
|
+
function int16(value, what) {
|
|
41
|
+
const rounded = Math.round(value);
|
|
42
|
+
if (rounded < MIN_INT16 || rounded > MAX_INT16) throw new require_errors.DocFormatError(`${what} is ${rounded}, outside the ${MIN_INT16}..${MAX_INT16} range a signed 2-byte sprm operand can hold`);
|
|
43
|
+
const unsigned = rounded < 0 ? rounded + 65536 : rounded;
|
|
44
|
+
return [unsigned & 255, unsigned >> 8 & 255];
|
|
45
|
+
}
|
|
46
|
+
function le16(value) {
|
|
47
|
+
return [value & 255, value >> 8 & 255];
|
|
48
|
+
}
|
|
49
|
+
function buildTc80(cell) {
|
|
50
|
+
return [
|
|
51
|
+
...le16((cell.vertMerge & 3) << 5),
|
|
52
|
+
0,
|
|
53
|
+
0,
|
|
54
|
+
...require_table_decoration.CELL_BORDER_SIDES.flatMap((side) => require_table_decoration.writeBrc80(cell.borders?.[side]))
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
/** Groups a cell's sides by the border they carry, so four identical sides become one TableBrcOperand rather than four -- bordersToApply is a bitmask of "any subset" of the edges precisely so a producer can state them together, and the row mark's grpprl has a 510-byte ceiling to stay under. Only sides whose colour the Brc80 palette cannot state exactly are grouped at all; every other side is already exact in TC80 itself. */
|
|
58
|
+
function exactColorBorderGroups(borders) {
|
|
59
|
+
const groups = [];
|
|
60
|
+
for (const side of require_table_decoration.CELL_BORDER_SIDES) {
|
|
61
|
+
const border = borders?.[side];
|
|
62
|
+
if (border === void 0 || !require_table_decoration.borderNeedsExactColor(border)) continue;
|
|
63
|
+
const existing = groups.find((group) => sameBorder(group.border, border));
|
|
64
|
+
if (existing === void 0) {
|
|
65
|
+
groups.push({
|
|
66
|
+
bordersToApply: require_table_decoration.BORDERS_TO_APPLY[side],
|
|
67
|
+
border
|
|
68
|
+
});
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
existing.bordersToApply |= require_table_decoration.BORDERS_TO_APPLY[side];
|
|
72
|
+
}
|
|
73
|
+
return groups;
|
|
74
|
+
}
|
|
75
|
+
function sameBorder(left, right) {
|
|
76
|
+
return left.widthPt === right.widthPt && (left.style ?? "solid") === (right.style ?? "solid") && left.color.r === right.color.r && left.color.g === right.color.g && left.color.b === right.color.b;
|
|
77
|
+
}
|
|
78
|
+
/** One cell's sprmTSetBrc Prls: an ItcFirstLim naming that cell alone, the sides sharing one border, and that border's exact Brc. */
|
|
79
|
+
function setBrcPrls(cell, index) {
|
|
80
|
+
const bytes = [];
|
|
81
|
+
for (const group of exactColorBorderGroups(cell.borders)) bytes.push(...le16(SPRM_T_SET_BRC), TABLE_BRC_OPERAND_CB, index, index + 1, group.bordersToApply, ...require_table_decoration.writeBrc(group.border));
|
|
82
|
+
return bytes;
|
|
83
|
+
}
|
|
84
|
+
/** The row's shading Prls: one DefTableShdOperand per 22-cell window that contains at least one shaded cell. A window with none is omitted entirely rather than written as an array of ShdAuto entries, since "no cells are shaded" is already the row's default -- so an undecorated table's row mark is byte-identical to what it was before shading was written at all. */
|
|
85
|
+
function shadingPrls(cells) {
|
|
86
|
+
const bytes = [];
|
|
87
|
+
for (const { opcode, first } of SHD_ARRAYS) {
|
|
88
|
+
const window = cells.slice(first, first + MAX_SHD_PER_ARRAY);
|
|
89
|
+
if (window.length === 0) continue;
|
|
90
|
+
let lastShaded = -1;
|
|
91
|
+
window.forEach((cell, index) => {
|
|
92
|
+
if (cell.background !== void 0) lastShaded = index;
|
|
93
|
+
});
|
|
94
|
+
if (lastShaded === -1) continue;
|
|
95
|
+
const shd = window.slice(0, lastShaded + 1).flatMap((cell) => require_table_decoration.writeShd(cell.background));
|
|
96
|
+
bytes.push(...le16(opcode), shd.length, ...shd);
|
|
97
|
+
}
|
|
98
|
+
return bytes;
|
|
99
|
+
}
|
|
100
|
+
function encodeTableRowGrpprl(columnBoundariesTwips, cells, heightPt) {
|
|
101
|
+
if (columnBoundariesTwips.length !== cells.length + 1) throw new require_errors.DocFormatError(`a table row's column-boundary array must carry exactly one more entry than its cell count (got ${columnBoundariesTwips.length} boundaries for ${cells.length} cells)`);
|
|
102
|
+
if (cells.length < 1 || cells.length > MAX_COLUMNS) throw new require_errors.DocFormatError(`a table row must have between 1 and ${MAX_COLUMNS} cells, got ${cells.length}`);
|
|
103
|
+
const remainder = [
|
|
104
|
+
cells.length,
|
|
105
|
+
...columnBoundariesTwips.flatMap((boundary) => int16(boundary, "table column boundary")),
|
|
106
|
+
...cells.flatMap(buildTc80)
|
|
107
|
+
];
|
|
108
|
+
const cb = remainder.length + 1;
|
|
109
|
+
const bytes = [
|
|
110
|
+
...le16(SPRM_T_DEF_TABLE),
|
|
111
|
+
...le16(cb),
|
|
112
|
+
...remainder
|
|
113
|
+
];
|
|
114
|
+
bytes.push(...shadingPrls(cells));
|
|
115
|
+
cells.forEach((cell, index) => {
|
|
116
|
+
bytes.push(...setBrcPrls(cell, index));
|
|
117
|
+
});
|
|
118
|
+
if (heightPt !== void 0) {
|
|
119
|
+
const dyaRowHeight = Math.round(heightPt * TWIPS_PER_POINT);
|
|
120
|
+
bytes.push(...le16(SPRM_T_DYA_ROW_HEIGHT), ...int16(dyaRowHeight, "table row heightPt"));
|
|
121
|
+
}
|
|
122
|
+
return bytes;
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
exports.encodeTableRowGrpprl = encodeTableRowGrpprl;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Color, ContentCellBorders } from "document-schema.js";
|
|
2
|
+
//#region src/table/tap-write.d.ts
|
|
3
|
+
interface TableCellToWrite {
|
|
4
|
+
/** VerticalMergeFlag: 0 fvmClear, 1 fvmMerge (continuation), 3 fvmRestart (first cell). */
|
|
5
|
+
readonly vertMerge: 0 | 1 | 3;
|
|
6
|
+
/** The cell's own four borders, from ContentTableCell.borders; an absent side is written as the Brc80MayBeNil no-border sentinel. */
|
|
7
|
+
readonly borders?: ContentCellBorders;
|
|
8
|
+
/** The cell's own flat background, from ContentTableCell.background. */
|
|
9
|
+
readonly background?: Color;
|
|
10
|
+
}
|
|
11
|
+
declare function encodeTableRowGrpprl(columnBoundariesTwips: readonly number[], cells: readonly TableCellToWrite[], heightPt: number | undefined): number[];
|
|
12
|
+
//#endregion
|
|
13
|
+
export { TableCellToWrite, encodeTableRowGrpprl };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Color, ContentCellBorders } from "document-schema.js";
|
|
2
|
+
//#region src/table/tap-write.d.ts
|
|
3
|
+
interface TableCellToWrite {
|
|
4
|
+
/** VerticalMergeFlag: 0 fvmClear, 1 fvmMerge (continuation), 3 fvmRestart (first cell). */
|
|
5
|
+
readonly vertMerge: 0 | 1 | 3;
|
|
6
|
+
/** The cell's own four borders, from ContentTableCell.borders; an absent side is written as the Brc80MayBeNil no-border sentinel. */
|
|
7
|
+
readonly borders?: ContentCellBorders;
|
|
8
|
+
/** The cell's own flat background, from ContentTableCell.background. */
|
|
9
|
+
readonly background?: Color;
|
|
10
|
+
}
|
|
11
|
+
declare function encodeTableRowGrpprl(columnBoundariesTwips: readonly number[], cells: readonly TableCellToWrite[], heightPt: number | undefined): number[];
|
|
12
|
+
//#endregion
|
|
13
|
+
export { TableCellToWrite, encodeTableRowGrpprl };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { DocFormatError } from "../errors.js";
|
|
2
|
+
import { BORDERS_TO_APPLY, CELL_BORDER_SIDES, borderNeedsExactColor, writeBrc, writeBrc80, writeShd } from "./decoration.js";
|
|
3
|
+
//#region src/table/tap-write.ts
|
|
4
|
+
/** sprmTDefTable, [MS-DOC] 2.6.3 (0xD608). */
|
|
5
|
+
const SPRM_T_DEF_TABLE = 54792;
|
|
6
|
+
/** sprmTDyaRowHeight (0x9407). */
|
|
7
|
+
const SPRM_T_DYA_ROW_HEIGHT = 37895;
|
|
8
|
+
/** sprmTDefTableShd (0xD612): a DefTableShdOperand ([MS-DOC] 2.9.53) shading cells 1-22 of the row, then sprmTDefTableShd2nd (0xD616) for 23-44 and sprmTDefTableShd3rd (0xD60C) for 45-63. Three sprms rather than one because a DefTableShdOperand's own cb is a single byte and its rgShd "MUST NOT exceed 22 elements", which is exactly why [MS-DOC] splits a row's shading across three opcodes at all. */
|
|
9
|
+
const SPRM_T_DEF_TABLE_SHD = 54802;
|
|
10
|
+
const SPRM_T_DEF_TABLE_SHD_2ND = 54806;
|
|
11
|
+
const SPRM_T_DEF_TABLE_SHD_3RD = 54796;
|
|
12
|
+
/** sprmTSetBrc (0xD62F): a TableBrcOperand ([MS-DOC] 2.9.305) restating one cell's borders on the named sides with an exact COLORREF. */
|
|
13
|
+
const SPRM_T_SET_BRC = 54831;
|
|
14
|
+
/** The three DefTableShdOperand sprms in cell order, each with the cell index its own rgShd starts at -- [MS-DOC] 2.6.3's own "Cells 1 - 22 ... cells 23 - 44 ... cells 45 - 63", one-based there and zero-based here. */
|
|
15
|
+
const SHD_ARRAYS = [
|
|
16
|
+
{
|
|
17
|
+
opcode: SPRM_T_DEF_TABLE_SHD,
|
|
18
|
+
first: 0
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
opcode: SPRM_T_DEF_TABLE_SHD_2ND,
|
|
22
|
+
first: 22
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
opcode: SPRM_T_DEF_TABLE_SHD_3RD,
|
|
26
|
+
first: 44
|
|
27
|
+
}
|
|
28
|
+
];
|
|
29
|
+
/** A DefTableShdOperand's own rgShd bound, [MS-DOC] 2.9.53: "The number of elements is equal to cb / 10 and MUST NOT exceed 22." */
|
|
30
|
+
const MAX_SHD_PER_ARRAY = 22;
|
|
31
|
+
const TWIPS_PER_POINT = 20;
|
|
32
|
+
/** A table row has "between 1 and 63 table cells" ([MS-DOC] 2.4.3), and TDefTableOperand.NumberOfColumns is itself a single byte "MUST NOT exceed 63". */
|
|
33
|
+
const MAX_COLUMNS = 63;
|
|
34
|
+
/** sprmPDyaBefore/After's unsigned 2-byte operand range, reused here for XAS column boundaries (also an unsigned 2-byte field in practice for the non-negative widths this writer produces). */
|
|
35
|
+
const MIN_INT16 = -32768;
|
|
36
|
+
const MAX_INT16 = 32767;
|
|
37
|
+
/** TableBrcOperand.cb, [MS-DOC] 2.9.305: "This value MUST be 11" -- the ItcFirstLim (2), bordersToApply (1) and Brc (8) that follow it. */
|
|
38
|
+
const TABLE_BRC_OPERAND_CB = 11;
|
|
39
|
+
function int16(value, what) {
|
|
40
|
+
const rounded = Math.round(value);
|
|
41
|
+
if (rounded < MIN_INT16 || rounded > MAX_INT16) throw new DocFormatError(`${what} is ${rounded}, outside the ${MIN_INT16}..${MAX_INT16} range a signed 2-byte sprm operand can hold`);
|
|
42
|
+
const unsigned = rounded < 0 ? rounded + 65536 : rounded;
|
|
43
|
+
return [unsigned & 255, unsigned >> 8 & 255];
|
|
44
|
+
}
|
|
45
|
+
function le16(value) {
|
|
46
|
+
return [value & 255, value >> 8 & 255];
|
|
47
|
+
}
|
|
48
|
+
function buildTc80(cell) {
|
|
49
|
+
return [
|
|
50
|
+
...le16((cell.vertMerge & 3) << 5),
|
|
51
|
+
0,
|
|
52
|
+
0,
|
|
53
|
+
...CELL_BORDER_SIDES.flatMap((side) => writeBrc80(cell.borders?.[side]))
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
/** Groups a cell's sides by the border they carry, so four identical sides become one TableBrcOperand rather than four -- bordersToApply is a bitmask of "any subset" of the edges precisely so a producer can state them together, and the row mark's grpprl has a 510-byte ceiling to stay under. Only sides whose colour the Brc80 palette cannot state exactly are grouped at all; every other side is already exact in TC80 itself. */
|
|
57
|
+
function exactColorBorderGroups(borders) {
|
|
58
|
+
const groups = [];
|
|
59
|
+
for (const side of CELL_BORDER_SIDES) {
|
|
60
|
+
const border = borders?.[side];
|
|
61
|
+
if (border === void 0 || !borderNeedsExactColor(border)) continue;
|
|
62
|
+
const existing = groups.find((group) => sameBorder(group.border, border));
|
|
63
|
+
if (existing === void 0) {
|
|
64
|
+
groups.push({
|
|
65
|
+
bordersToApply: BORDERS_TO_APPLY[side],
|
|
66
|
+
border
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
existing.bordersToApply |= BORDERS_TO_APPLY[side];
|
|
71
|
+
}
|
|
72
|
+
return groups;
|
|
73
|
+
}
|
|
74
|
+
function sameBorder(left, right) {
|
|
75
|
+
return left.widthPt === right.widthPt && (left.style ?? "solid") === (right.style ?? "solid") && left.color.r === right.color.r && left.color.g === right.color.g && left.color.b === right.color.b;
|
|
76
|
+
}
|
|
77
|
+
/** One cell's sprmTSetBrc Prls: an ItcFirstLim naming that cell alone, the sides sharing one border, and that border's exact Brc. */
|
|
78
|
+
function setBrcPrls(cell, index) {
|
|
79
|
+
const bytes = [];
|
|
80
|
+
for (const group of exactColorBorderGroups(cell.borders)) bytes.push(...le16(SPRM_T_SET_BRC), TABLE_BRC_OPERAND_CB, index, index + 1, group.bordersToApply, ...writeBrc(group.border));
|
|
81
|
+
return bytes;
|
|
82
|
+
}
|
|
83
|
+
/** The row's shading Prls: one DefTableShdOperand per 22-cell window that contains at least one shaded cell. A window with none is omitted entirely rather than written as an array of ShdAuto entries, since "no cells are shaded" is already the row's default -- so an undecorated table's row mark is byte-identical to what it was before shading was written at all. */
|
|
84
|
+
function shadingPrls(cells) {
|
|
85
|
+
const bytes = [];
|
|
86
|
+
for (const { opcode, first } of SHD_ARRAYS) {
|
|
87
|
+
const window = cells.slice(first, first + MAX_SHD_PER_ARRAY);
|
|
88
|
+
if (window.length === 0) continue;
|
|
89
|
+
let lastShaded = -1;
|
|
90
|
+
window.forEach((cell, index) => {
|
|
91
|
+
if (cell.background !== void 0) lastShaded = index;
|
|
92
|
+
});
|
|
93
|
+
if (lastShaded === -1) continue;
|
|
94
|
+
const shd = window.slice(0, lastShaded + 1).flatMap((cell) => writeShd(cell.background));
|
|
95
|
+
bytes.push(...le16(opcode), shd.length, ...shd);
|
|
96
|
+
}
|
|
97
|
+
return bytes;
|
|
98
|
+
}
|
|
99
|
+
function encodeTableRowGrpprl(columnBoundariesTwips, cells, heightPt) {
|
|
100
|
+
if (columnBoundariesTwips.length !== cells.length + 1) throw new DocFormatError(`a table row's column-boundary array must carry exactly one more entry than its cell count (got ${columnBoundariesTwips.length} boundaries for ${cells.length} cells)`);
|
|
101
|
+
if (cells.length < 1 || cells.length > MAX_COLUMNS) throw new DocFormatError(`a table row must have between 1 and ${MAX_COLUMNS} cells, got ${cells.length}`);
|
|
102
|
+
const remainder = [
|
|
103
|
+
cells.length,
|
|
104
|
+
...columnBoundariesTwips.flatMap((boundary) => int16(boundary, "table column boundary")),
|
|
105
|
+
...cells.flatMap(buildTc80)
|
|
106
|
+
];
|
|
107
|
+
const cb = remainder.length + 1;
|
|
108
|
+
const bytes = [
|
|
109
|
+
...le16(SPRM_T_DEF_TABLE),
|
|
110
|
+
...le16(cb),
|
|
111
|
+
...remainder
|
|
112
|
+
];
|
|
113
|
+
bytes.push(...shadingPrls(cells));
|
|
114
|
+
cells.forEach((cell, index) => {
|
|
115
|
+
bytes.push(...setBrcPrls(cell, index));
|
|
116
|
+
});
|
|
117
|
+
if (heightPt !== void 0) {
|
|
118
|
+
const dyaRowHeight = Math.round(heightPt * TWIPS_PER_POINT);
|
|
119
|
+
bytes.push(...le16(SPRM_T_DYA_ROW_HEIGHT), ...int16(dyaRowHeight, "table row heightPt"));
|
|
120
|
+
}
|
|
121
|
+
return bytes;
|
|
122
|
+
}
|
|
123
|
+
//#endregion
|
|
124
|
+
export { encodeTableRowGrpprl };
|