@deckflow/deckuse 1.2.1 → 1.3.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.es.md +342 -0
- package/README.fr.md +342 -0
- package/README.ja.md +342 -0
- package/README.ko.md +342 -0
- package/README.md +60 -8
- package/README.pt-BR.md +342 -0
- package/README.ru.md +342 -0
- package/README.zh-CN.md +367 -0
- package/assets/default.docx +0 -0
- package/assets/default.pptx +0 -0
- package/dist/bin.js +188 -21
- package/dist/bin.js.map +1 -1
- package/dist/core/adapter.d.ts +24 -0
- package/dist/core/adapter.d.ts.map +1 -0
- package/dist/core/adapter.js +20 -0
- package/dist/core/adapter.js.map +1 -0
- package/dist/core/executor.d.ts +24 -0
- package/dist/core/executor.d.ts.map +1 -0
- package/dist/core/executor.js +65 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/length.d.ts +27 -0
- package/dist/core/length.d.ts.map +1 -0
- package/dist/core/length.js +63 -0
- package/dist/core/length.js.map +1 -0
- package/dist/core/measure.d.ts +24 -0
- package/dist/core/measure.d.ts.map +1 -0
- package/dist/core/measure.js +79 -0
- package/dist/core/measure.js.map +1 -0
- package/dist/core/not-implemented.d.ts +3 -0
- package/dist/core/not-implemented.d.ts.map +1 -0
- package/dist/core/not-implemented.js +12 -0
- package/dist/core/not-implemented.js.map +1 -0
- package/dist/core/schema.d.ts +3065 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +799 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/default-template.d.ts +8 -0
- package/dist/default-template.d.ts.map +1 -0
- package/dist/default-template.js +29 -0
- package/dist/default-template.js.map +1 -0
- package/dist/docx/adapter.d.ts +29 -0
- package/dist/docx/adapter.d.ts.map +1 -0
- package/dist/docx/adapter.js +442 -0
- package/dist/docx/adapter.js.map +1 -0
- package/dist/docx/addressing.d.ts +18 -0
- package/dist/docx/addressing.d.ts.map +1 -0
- package/dist/docx/addressing.js +111 -0
- package/dist/docx/addressing.js.map +1 -0
- package/dist/docx/blank.d.ts +4 -0
- package/dist/docx/blank.d.ts.map +1 -0
- package/dist/docx/blank.js +75 -0
- package/dist/docx/blank.js.map +1 -0
- package/dist/docx/index-sync.d.ts +7 -0
- package/dist/docx/index-sync.d.ts.map +1 -0
- package/dist/docx/index-sync.js +24 -0
- package/dist/docx/index-sync.js.map +1 -0
- package/dist/docx/index.d.ts +5 -0
- package/dist/docx/index.d.ts.map +1 -0
- package/dist/docx/index.js +4 -0
- package/dist/docx/index.js.map +1 -0
- package/dist/docx/indexer.d.ts +41 -0
- package/dist/docx/indexer.d.ts.map +1 -0
- package/dist/docx/indexer.js +353 -0
- package/dist/docx/indexer.js.map +1 -0
- package/dist/docx/mutations.d.ts +5 -0
- package/dist/docx/mutations.d.ts.map +1 -0
- package/dist/docx/mutations.js +311 -0
- package/dist/docx/mutations.js.map +1 -0
- package/dist/docx/paragraphs.d.ts +26 -0
- package/dist/docx/paragraphs.d.ts.map +1 -0
- package/dist/docx/paragraphs.js +314 -0
- package/dist/docx/paragraphs.js.map +1 -0
- package/dist/docx/properties.d.ts +23 -0
- package/dist/docx/properties.d.ts.map +1 -0
- package/dist/docx/properties.js +414 -0
- package/dist/docx/properties.js.map +1 -0
- package/dist/docx/tables.d.ts +4 -0
- package/dist/docx/tables.d.ts.map +1 -0
- package/dist/docx/tables.js +64 -0
- package/dist/docx/tables.js.map +1 -0
- package/dist/docx/text-spans.d.ts +25 -0
- package/dist/docx/text-spans.d.ts.map +1 -0
- package/dist/docx/text-spans.js +142 -0
- package/dist/docx/text-spans.js.map +1 -0
- package/dist/docx/types.d.ts +27 -0
- package/dist/docx/types.d.ts.map +1 -0
- package/dist/docx/types.js +2 -0
- package/dist/docx/types.js.map +1 -0
- package/dist/docx/workspace.d.ts +20 -0
- package/dist/docx/workspace.d.ts.map +1 -0
- package/dist/docx/workspace.js +145 -0
- package/dist/docx/workspace.js.map +1 -0
- package/dist/docx/xml.d.ts +18 -0
- package/dist/docx/xml.d.ts.map +1 -0
- package/dist/docx/xml.js +87 -0
- package/dist/docx/xml.js.map +1 -0
- package/dist/edition-config/index.d.ts +25 -0
- package/dist/edition-config/index.d.ts.map +1 -0
- package/dist/edition-config/index.js +22 -0
- package/dist/edition-config/index.js.map +1 -0
- package/dist/edition.d.ts +2 -2
- package/dist/edition.d.ts.map +1 -1
- package/dist/edition.js +1 -1
- package/dist/edition.js.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +124 -32
- package/dist/help.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/key/index.d.ts +2 -0
- package/dist/key/index.d.ts.map +1 -0
- package/dist/key/index.js +3 -0
- package/dist/key/index.js.map +1 -0
- package/dist/monitor-daemon.d.ts +21 -8
- package/dist/monitor-daemon.d.ts.map +1 -1
- package/dist/monitor-daemon.js +189 -51
- package/dist/monitor-daemon.js.map +1 -1
- package/dist/monitor-registry.d.ts +34 -0
- package/dist/monitor-registry.d.ts.map +1 -0
- package/dist/monitor-registry.js +191 -0
- package/dist/monitor-registry.js.map +1 -0
- package/dist/monitor.d.ts.map +1 -1
- package/dist/monitor.js +2 -12
- package/dist/monitor.js.map +1 -1
- package/dist/numbers/index.d.ts +2 -0
- package/dist/numbers/index.d.ts.map +1 -0
- package/dist/numbers/index.js +3 -0
- package/dist/numbers/index.js.map +1 -0
- package/dist/opc/index.d.ts +96 -0
- package/dist/opc/index.d.ts.map +1 -0
- package/dist/opc/index.js +596 -0
- package/dist/opc/index.js.map +1 -0
- package/dist/pptx/adapter.d.ts +167 -0
- package/dist/pptx/adapter.d.ts.map +1 -0
- package/dist/pptx/adapter.js +841 -0
- package/dist/pptx/adapter.js.map +1 -0
- package/dist/pptx/addressing.d.ts +47 -0
- package/dist/pptx/addressing.d.ts.map +1 -0
- package/dist/pptx/addressing.js +481 -0
- package/dist/pptx/addressing.js.map +1 -0
- package/dist/pptx/align.d.ts +24 -0
- package/dist/pptx/align.d.ts.map +1 -0
- package/dist/pptx/align.js +112 -0
- package/dist/pptx/align.js.map +1 -0
- package/dist/pptx/chart-classify.d.ts +10 -0
- package/dist/pptx/chart-classify.d.ts.map +1 -0
- package/dist/pptx/chart-classify.js +58 -0
- package/dist/pptx/chart-classify.js.map +1 -0
- package/dist/pptx/chart.d.ts +42 -0
- package/dist/pptx/chart.d.ts.map +1 -0
- package/dist/pptx/chart.js +614 -0
- package/dist/pptx/chart.js.map +1 -0
- package/dist/pptx/edition-extension.d.ts +25 -0
- package/dist/pptx/edition-extension.d.ts.map +1 -0
- package/dist/pptx/edition-extension.js +10 -0
- package/dist/pptx/edition-extension.js.map +1 -0
- package/dist/pptx/edition.d.ts +20 -0
- package/dist/pptx/edition.d.ts.map +1 -0
- package/dist/pptx/edition.js +69 -0
- package/dist/pptx/edition.js.map +1 -0
- package/dist/pptx/elements.d.ts +12 -0
- package/dist/pptx/elements.d.ts.map +1 -0
- package/dist/pptx/elements.js +224 -0
- package/dist/pptx/elements.js.map +1 -0
- package/dist/pptx/hyperlink.d.ts +7 -0
- package/dist/pptx/hyperlink.d.ts.map +1 -0
- package/dist/pptx/hyperlink.js +81 -0
- package/dist/pptx/hyperlink.js.map +1 -0
- package/dist/pptx/index-sync.d.ts +7 -0
- package/dist/pptx/index-sync.d.ts.map +1 -0
- package/dist/pptx/index-sync.js +24 -0
- package/dist/pptx/index-sync.js.map +1 -0
- package/dist/pptx/index.d.ts +10 -0
- package/dist/pptx/index.d.ts.map +1 -0
- package/dist/pptx/index.js +7 -0
- package/dist/pptx/index.js.map +1 -0
- package/dist/pptx/indexer.d.ts +19 -0
- package/dist/pptx/indexer.d.ts.map +1 -0
- package/dist/pptx/indexer.js +320 -0
- package/dist/pptx/indexer.js.map +1 -0
- package/dist/pptx/layout-ref.d.ts +41 -0
- package/dist/pptx/layout-ref.d.ts.map +1 -0
- package/dist/pptx/layout-ref.js +145 -0
- package/dist/pptx/layout-ref.js.map +1 -0
- package/dist/pptx/media.d.ts +15 -0
- package/dist/pptx/media.d.ts.map +1 -0
- package/dist/pptx/media.js +145 -0
- package/dist/pptx/media.js.map +1 -0
- package/dist/pptx/mutations.d.ts +11 -0
- package/dist/pptx/mutations.d.ts.map +1 -0
- package/dist/pptx/mutations.js +1114 -0
- package/dist/pptx/mutations.js.map +1 -0
- package/dist/pptx/picture.d.ts +25 -0
- package/dist/pptx/picture.d.ts.map +1 -0
- package/dist/pptx/picture.js +118 -0
- package/dist/pptx/picture.js.map +1 -0
- package/dist/pptx/placeholder-role.d.ts +19 -0
- package/dist/pptx/placeholder-role.d.ts.map +1 -0
- package/dist/pptx/placeholder-role.js +71 -0
- package/dist/pptx/placeholder-role.js.map +1 -0
- package/dist/pptx/properties.d.ts +13 -0
- package/dist/pptx/properties.d.ts.map +1 -0
- package/dist/pptx/properties.js +587 -0
- package/dist/pptx/properties.js.map +1 -0
- package/dist/pptx/resolve-properties.d.ts +17 -0
- package/dist/pptx/resolve-properties.d.ts.map +1 -0
- package/dist/pptx/resolve-properties.js +623 -0
- package/dist/pptx/resolve-properties.js.map +1 -0
- package/dist/pptx/slide-size.d.ts +9 -0
- package/dist/pptx/slide-size.d.ts.map +1 -0
- package/dist/pptx/slide-size.js +29 -0
- package/dist/pptx/slide-size.js.map +1 -0
- package/dist/pptx/slides.d.ts +15 -0
- package/dist/pptx/slides.d.ts.map +1 -0
- package/dist/pptx/slides.js +247 -0
- package/dist/pptx/slides.js.map +1 -0
- package/dist/pptx/table-measure.d.ts +43 -0
- package/dist/pptx/table-measure.d.ts.map +1 -0
- package/dist/pptx/table-measure.js +70 -0
- package/dist/pptx/table-measure.js.map +1 -0
- package/dist/pptx/table.d.ts +26 -0
- package/dist/pptx/table.d.ts.map +1 -0
- package/dist/pptx/table.js +638 -0
- package/dist/pptx/table.js.map +1 -0
- package/dist/pptx/types.d.ts +27 -0
- package/dist/pptx/types.d.ts.map +1 -0
- package/dist/pptx/types.js +2 -0
- package/dist/pptx/types.js.map +1 -0
- package/dist/pptx/workspace.d.ts +23 -0
- package/dist/pptx/workspace.d.ts.map +1 -0
- package/dist/pptx/workspace.js +148 -0
- package/dist/pptx/workspace.js.map +1 -0
- package/dist/pptx/xml.d.ts +75 -0
- package/dist/pptx/xml.d.ts.map +1 -0
- package/dist/pptx/xml.js +390 -0
- package/dist/pptx/xml.js.map +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +14 -2
- package/dist/render.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace/git.d.ts +7 -0
- package/dist/workspace/git.d.ts.map +1 -0
- package/dist/workspace/git.js +119 -0
- package/dist/workspace/git.js.map +1 -0
- package/dist/workspace/index.d.ts +7 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +7 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/lock.d.ts +5 -0
- package/dist/workspace/lock.d.ts.map +1 -0
- package/dist/workspace/lock.js +104 -0
- package/dist/workspace/lock.js.map +1 -0
- package/dist/workspace/metadata.d.ts +4 -0
- package/dist/workspace/metadata.d.ts.map +1 -0
- package/dist/workspace/metadata.js +21 -0
- package/dist/workspace/metadata.js.map +1 -0
- package/dist/workspace/operations.d.ts +14 -0
- package/dist/workspace/operations.d.ts.map +1 -0
- package/dist/workspace/operations.js +35 -0
- package/dist/workspace/operations.js.map +1 -0
- package/dist/workspace/paths.d.ts +11 -0
- package/dist/workspace/paths.d.ts.map +1 -0
- package/dist/workspace/paths.js +12 -0
- package/dist/workspace/paths.js.map +1 -0
- package/dist/workspace/revision.d.ts +6 -0
- package/dist/workspace/revision.d.ts.map +1 -0
- package/dist/workspace/revision.js +15 -0
- package/dist/workspace/revision.js.map +1 -0
- package/dist/xlsx/index.d.ts +2 -0
- package/dist/xlsx/index.d.ts.map +1 -0
- package/dist/xlsx/index.js +3 -0
- package/dist/xlsx/index.js.map +1 -0
- package/package.json +51 -27
- package/schema/command.schema.json +2405 -0
- package/dist/.tsbuildinfo +0 -1
|
@@ -0,0 +1,638 @@
|
|
|
1
|
+
import { err, ok } from '../core/index.js';
|
|
2
|
+
import { applyShapeProperties, shapePropertyKeys, } from './properties.js';
|
|
3
|
+
import { readSlideSize } from './slide-size.js';
|
|
4
|
+
import { estimateTableHeightEmu, estimateTableRowHeightEmu, measureTableLayout, } from './table-measure.js';
|
|
5
|
+
import { NS, attr, children, descendants, first, setNodeText } from './xml.js';
|
|
6
|
+
const DEFAULT_ROW_H = '370840';
|
|
7
|
+
const DEFAULT_COL_W = '914400';
|
|
8
|
+
const EMU_PER_PT = 12700;
|
|
9
|
+
const DEFAULT_STROKE_PT = 1;
|
|
10
|
+
const MAX_LINE_WIDTH_EMU = 20116800; // ST_LineWidth maxInclusive
|
|
11
|
+
const MAX_LINE_WIDTH_PT = MAX_LINE_WIDTH_EMU / EMU_PER_PT;
|
|
12
|
+
/** DrawingML 2014 table unique ids (PowerPoint writes these on modern tables). */
|
|
13
|
+
const NS_A16 = 'http://schemas.microsoft.com/office/drawing/2014/main';
|
|
14
|
+
const COL_ID_URI = '{9D8B030D-6E8A-4147-A177-3AD203B41FA5}';
|
|
15
|
+
const ROW_ID_URI = '{0D108BD9-81ED-4DB2-BD59-A6C34878D82A}';
|
|
16
|
+
/** Convert line.width to EMU. Values above max pt are treated as already-EMU. */
|
|
17
|
+
const lineWidthToEmu = (width) => {
|
|
18
|
+
const emu = width > MAX_LINE_WIDTH_PT ? Math.round(width) : Math.round(width * EMU_PER_PT);
|
|
19
|
+
return Math.min(MAX_LINE_WIDTH_EMU, Math.max(1, emu));
|
|
20
|
+
};
|
|
21
|
+
const STROKE_ALIASES = ['stroke', 'border', 'outline', 'line'];
|
|
22
|
+
const TABLE_CELL_BORDER_SIDES = ['lnL', 'lnR', 'lnT', 'lnB'];
|
|
23
|
+
const TABLE_CELL_BORDER_LOCAL = new Set([
|
|
24
|
+
...TABLE_CELL_BORDER_SIDES,
|
|
25
|
+
'lnTlToBr',
|
|
26
|
+
'lnBlToTr',
|
|
27
|
+
'cell3D',
|
|
28
|
+
]);
|
|
29
|
+
const TABLE_CELL_FILL_LOCAL = new Set([
|
|
30
|
+
'noFill',
|
|
31
|
+
'solidFill',
|
|
32
|
+
'gradFill',
|
|
33
|
+
'blipFill',
|
|
34
|
+
'pattFill',
|
|
35
|
+
'grpFill',
|
|
36
|
+
]);
|
|
37
|
+
const SHAPE_KEY_SET = new Set(shapePropertyKeys);
|
|
38
|
+
/** Insert fill after border sides (OOXML CT_TableCellProperties order). */
|
|
39
|
+
const insertTcPrFill = (tcPr, fill) => {
|
|
40
|
+
const kids = children(tcPr);
|
|
41
|
+
let lastBorder;
|
|
42
|
+
for (const child of kids)
|
|
43
|
+
if (child.localName && TABLE_CELL_BORDER_LOCAL.has(child.localName))
|
|
44
|
+
lastBorder = child;
|
|
45
|
+
if (lastBorder?.nextSibling)
|
|
46
|
+
tcPr.insertBefore(fill, lastBorder.nextSibling);
|
|
47
|
+
else if (lastBorder)
|
|
48
|
+
tcPr.appendChild(fill);
|
|
49
|
+
else if (tcPr.firstChild)
|
|
50
|
+
tcPr.insertBefore(fill, tcPr.firstChild);
|
|
51
|
+
else
|
|
52
|
+
tcPr.appendChild(fill);
|
|
53
|
+
};
|
|
54
|
+
/** Insert a border side before fill / margins (OOXML CT_TableCellProperties order). */
|
|
55
|
+
const insertTcPrBorder = (tcPr, ln) => {
|
|
56
|
+
const kids = children(tcPr);
|
|
57
|
+
const fillOrLater = kids.find((child) => child.localName && !TABLE_CELL_BORDER_LOCAL.has(child.localName));
|
|
58
|
+
if (fillOrLater)
|
|
59
|
+
tcPr.insertBefore(ln, fillOrLater);
|
|
60
|
+
else
|
|
61
|
+
tcPr.appendChild(ln);
|
|
62
|
+
};
|
|
63
|
+
const directChildren = (node, localName) => children(node).filter((c) => c.localName === localName);
|
|
64
|
+
const normalizeColor = (value) => {
|
|
65
|
+
const hex = value.trim().replace(/^#/, '').toUpperCase();
|
|
66
|
+
if (!/^[0-9A-F]{6}$/.test(hex))
|
|
67
|
+
throw new Error(`Invalid color: ${value}`);
|
|
68
|
+
return hex;
|
|
69
|
+
};
|
|
70
|
+
const isNone = (value) => {
|
|
71
|
+
if (value === null || value === false || value === 'none')
|
|
72
|
+
return true;
|
|
73
|
+
return (typeof value === 'object' &&
|
|
74
|
+
value !== null &&
|
|
75
|
+
'type' in value &&
|
|
76
|
+
value.type === 'none');
|
|
77
|
+
};
|
|
78
|
+
const tblOf = (node) => {
|
|
79
|
+
const tbl = first(node, 'tbl');
|
|
80
|
+
if (!tbl)
|
|
81
|
+
throw new Error('Table element has no a:tbl');
|
|
82
|
+
return tbl;
|
|
83
|
+
};
|
|
84
|
+
const makeCell = (doc, text = '') => {
|
|
85
|
+
const tc = doc.createElementNS(NS.a, 'a:tc');
|
|
86
|
+
const txBody = doc.createElementNS(NS.a, 'a:txBody');
|
|
87
|
+
txBody.appendChild(doc.createElementNS(NS.a, 'a:bodyPr'));
|
|
88
|
+
txBody.appendChild(doc.createElementNS(NS.a, 'a:lstStyle'));
|
|
89
|
+
const p = doc.createElementNS(NS.a, 'a:p');
|
|
90
|
+
const r = doc.createElementNS(NS.a, 'a:r');
|
|
91
|
+
const t = doc.createElementNS(NS.a, 'a:t');
|
|
92
|
+
t.appendChild(doc.createTextNode(text));
|
|
93
|
+
r.appendChild(t);
|
|
94
|
+
p.appendChild(r);
|
|
95
|
+
txBody.appendChild(p);
|
|
96
|
+
tc.appendChild(doc.createElementNS(NS.a, 'a:tcPr'));
|
|
97
|
+
return tc;
|
|
98
|
+
};
|
|
99
|
+
/** Clone a cell's formatting (tcPr, bodyPr, lstStyle, pPr, rPr) and set plain text. */
|
|
100
|
+
const cloneCellWithText = (template, text) => {
|
|
101
|
+
const doc = template.ownerDocument;
|
|
102
|
+
if (!doc)
|
|
103
|
+
throw new Error('Element has no document');
|
|
104
|
+
const tc = template.cloneNode(true);
|
|
105
|
+
// Drop a16 / extLst unique ids that must not be duplicated on the new cell.
|
|
106
|
+
for (const extLst of [...descendants(tc, 'extLst')])
|
|
107
|
+
extLst.parentNode?.removeChild(extLst);
|
|
108
|
+
setNodeText(tc, text);
|
|
109
|
+
return tc;
|
|
110
|
+
};
|
|
111
|
+
const growFrame = (graphicFrame, deltaCx, deltaCy) => {
|
|
112
|
+
const xfrm = first(graphicFrame, 'xfrm');
|
|
113
|
+
const ext = xfrm ? first(xfrm, 'ext') : undefined;
|
|
114
|
+
if (!ext)
|
|
115
|
+
return;
|
|
116
|
+
if (deltaCx !== 0) {
|
|
117
|
+
const cx = Number(attr(ext, 'cx') ?? 0);
|
|
118
|
+
ext.setAttribute('cx', String(Math.max(1, Math.round(cx + deltaCx))));
|
|
119
|
+
}
|
|
120
|
+
if (deltaCy !== 0) {
|
|
121
|
+
const cy = Number(attr(ext, 'cy') ?? 0);
|
|
122
|
+
ext.setAttribute('cy', String(Math.max(1, Math.round(cy + deltaCy))));
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const frameBoxOf = (node) => {
|
|
126
|
+
const xfrm = first(node, 'xfrm');
|
|
127
|
+
const off = xfrm ? first(xfrm, 'off') : undefined;
|
|
128
|
+
const ext = xfrm ? first(xfrm, 'ext') : undefined;
|
|
129
|
+
if (!off || !ext)
|
|
130
|
+
return undefined;
|
|
131
|
+
const x = Number(attr(off, 'x') ?? 0);
|
|
132
|
+
const y = Number(attr(off, 'y') ?? 0);
|
|
133
|
+
const cx = Number(attr(ext, 'cx') ?? 0);
|
|
134
|
+
const cy = Number(attr(ext, 'cy') ?? 0);
|
|
135
|
+
if (![x, y, cx, cy].every(Number.isFinite))
|
|
136
|
+
return undefined;
|
|
137
|
+
return { x, y, cx, cy };
|
|
138
|
+
};
|
|
139
|
+
const boxesIntersect = (a, b) => a.x < b.x + b.cx && a.x + a.cx > b.x && a.y < b.y + b.cy && a.y + a.cy > b.y;
|
|
140
|
+
const SHAPE_FRAME_LOCAL = new Set(['sp', 'pic', 'graphicFrame', 'cxnSp', 'grpSp']);
|
|
141
|
+
/** Warn when a grown table overlaps peers or extends past the slide bottom. */
|
|
142
|
+
export const tableLayoutWarnings = (graphicFrame, context) => {
|
|
143
|
+
const box = frameBoxOf(graphicFrame);
|
|
144
|
+
if (!box)
|
|
145
|
+
return [];
|
|
146
|
+
const diagnostics = [];
|
|
147
|
+
if (context?.archive) {
|
|
148
|
+
const { heightEmu } = readSlideSize(context.archive);
|
|
149
|
+
if (box.y + box.cy > heightEmu) {
|
|
150
|
+
diagnostics.push({
|
|
151
|
+
severity: 'warning',
|
|
152
|
+
code: 'TABLE_EXTENDS_PAST_SLIDE',
|
|
153
|
+
message: `Table bottom (${String(box.y + box.cy)} EMU) extends past slide height (${String(heightEmu)} EMU)`,
|
|
154
|
+
details: { bottomEmu: box.y + box.cy, slideHeightEmu: heightEmu },
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const doc = graphicFrame.ownerDocument;
|
|
159
|
+
if (!doc)
|
|
160
|
+
return diagnostics;
|
|
161
|
+
for (const peer of descendants(doc)) {
|
|
162
|
+
if (peer === graphicFrame)
|
|
163
|
+
continue;
|
|
164
|
+
if (!peer.localName || !SHAPE_FRAME_LOCAL.has(peer.localName))
|
|
165
|
+
continue;
|
|
166
|
+
const other = frameBoxOf(peer);
|
|
167
|
+
if (!other || other.cx <= 0 || other.cy <= 0)
|
|
168
|
+
continue;
|
|
169
|
+
if (boxesIntersect(box, other)) {
|
|
170
|
+
const name = attr(first(peer, 'cNvPr'), 'name') ?? peer.localName;
|
|
171
|
+
diagnostics.push({
|
|
172
|
+
severity: 'warning',
|
|
173
|
+
code: 'TABLE_OVERLAPS_SHAPE',
|
|
174
|
+
message: `Table frame overlaps shape "${name}" after structural edit; consider setTableLayout to reflow`,
|
|
175
|
+
details: { peer: name },
|
|
176
|
+
});
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return diagnostics;
|
|
181
|
+
};
|
|
182
|
+
/** True when any gridCol already carries a16:colId (PowerPoint modern table). */
|
|
183
|
+
const tableUsesColIds = (tbl) => descendants(tbl, 'colId').some((el) => el.namespaceURI === NS_A16 || el.prefix === 'a16' || el.localName === 'colId');
|
|
184
|
+
/** True when any row already carries a16:rowId. */
|
|
185
|
+
const tableUsesRowIds = (tbl) => descendants(tbl, 'rowId').some((el) => el.namespaceURI === NS_A16 || el.prefix === 'a16' || el.localName === 'rowId');
|
|
186
|
+
const nextTableUniqueId = (tbl, localName) => {
|
|
187
|
+
let max = 0;
|
|
188
|
+
for (const el of descendants(tbl, localName)) {
|
|
189
|
+
const n = Number(attr(el, 'val') ?? 0);
|
|
190
|
+
if (Number.isFinite(n) && n > max)
|
|
191
|
+
max = n;
|
|
192
|
+
}
|
|
193
|
+
// PowerPoint typically uses 10000+ for rows and 20000+ for cols; stay above either.
|
|
194
|
+
return String(Math.max(max + 1, localName === 'colId' ? 20000 : 10000));
|
|
195
|
+
};
|
|
196
|
+
const attachA16Id = (parent, kind, id) => {
|
|
197
|
+
const doc = parent.ownerDocument;
|
|
198
|
+
if (!doc)
|
|
199
|
+
throw new Error('Element has no document');
|
|
200
|
+
const uri = kind === 'colId' ? COL_ID_URI : ROW_ID_URI;
|
|
201
|
+
const extLst = doc.createElementNS(NS.a, 'a:extLst');
|
|
202
|
+
const ext = doc.createElementNS(NS.a, 'a:ext');
|
|
203
|
+
ext.setAttribute('uri', uri);
|
|
204
|
+
const idEl = doc.createElementNS(NS_A16, `a16:${kind}`);
|
|
205
|
+
idEl.setAttribute('val', id);
|
|
206
|
+
ext.appendChild(idEl);
|
|
207
|
+
extLst.appendChild(ext);
|
|
208
|
+
parent.appendChild(extLst);
|
|
209
|
+
};
|
|
210
|
+
const parseIndex = (value, name) => {
|
|
211
|
+
if (typeof value === 'number' && Number.isInteger(value) && value >= 0)
|
|
212
|
+
return value;
|
|
213
|
+
if (typeof value === 'object' && value !== null && 'index' in value) {
|
|
214
|
+
const index = value.index;
|
|
215
|
+
if (typeof index === 'number' && Number.isInteger(index) && index >= 0)
|
|
216
|
+
return index;
|
|
217
|
+
}
|
|
218
|
+
throw new Error(`${name} requires a non-negative integer index`);
|
|
219
|
+
};
|
|
220
|
+
const cellsFromSpec = (value) => {
|
|
221
|
+
if (typeof value !== 'object' || value === null)
|
|
222
|
+
return undefined;
|
|
223
|
+
const cells = value.cells;
|
|
224
|
+
if (cells === undefined)
|
|
225
|
+
return undefined;
|
|
226
|
+
if (!Array.isArray(cells) || !cells.every((c) => typeof c === 'string'))
|
|
227
|
+
throw new Error('cells must be an array of strings');
|
|
228
|
+
return cells;
|
|
229
|
+
};
|
|
230
|
+
export function insertTableRow(node, spec) {
|
|
231
|
+
const tbl = tblOf(node);
|
|
232
|
+
const doc = node.ownerDocument;
|
|
233
|
+
if (!doc)
|
|
234
|
+
throw new Error('Element has no document');
|
|
235
|
+
const index = parseIndex(spec, 'insertRow');
|
|
236
|
+
const rows = directChildren(tbl, 'tr');
|
|
237
|
+
const grid = first(tbl, 'tblGrid');
|
|
238
|
+
const colCount = grid
|
|
239
|
+
? directChildren(grid, 'gridCol').length
|
|
240
|
+
: rows[0]
|
|
241
|
+
? directChildren(rows[0], 'tc').length
|
|
242
|
+
: 1;
|
|
243
|
+
const texts = cellsFromSpec(spec) ?? Array.from({ length: colCount }, () => '');
|
|
244
|
+
// Inherit from the row at the insert point, or the last row when appending.
|
|
245
|
+
const templateRow = rows[index] ?? rows[rows.length - 1];
|
|
246
|
+
const templateCells = templateRow ? directChildren(templateRow, 'tc') : [];
|
|
247
|
+
const rowH = templateRow ? (attr(templateRow, 'h') ?? DEFAULT_ROW_H) : DEFAULT_ROW_H;
|
|
248
|
+
const tr = doc.createElementNS(NS.a, 'a:tr');
|
|
249
|
+
tr.setAttribute('h', rowH);
|
|
250
|
+
for (let i = 0; i < colCount; i++) {
|
|
251
|
+
const text = texts[i] ?? '';
|
|
252
|
+
const template = templateCells[i];
|
|
253
|
+
tr.appendChild(template ? cloneCellWithText(template, text) : makeCell(doc, text));
|
|
254
|
+
}
|
|
255
|
+
// Keep a16:rowId complete when the source table already uses them — partial
|
|
256
|
+
// coverage makes PowerPoint prompt to repair the package.
|
|
257
|
+
if (tableUsesRowIds(tbl))
|
|
258
|
+
attachA16Id(tr, 'rowId', nextTableUniqueId(tbl, 'rowId'));
|
|
259
|
+
const anchor = rows[index];
|
|
260
|
+
if (anchor)
|
|
261
|
+
tbl.insertBefore(tr, anchor);
|
|
262
|
+
else
|
|
263
|
+
tbl.appendChild(tr);
|
|
264
|
+
growFrame(node, 0, Number(rowH) || Number(DEFAULT_ROW_H));
|
|
265
|
+
}
|
|
266
|
+
export function deleteTableRow(node, spec) {
|
|
267
|
+
const tbl = tblOf(node);
|
|
268
|
+
const index = parseIndex(spec, 'deleteRow');
|
|
269
|
+
const rows = directChildren(tbl, 'tr');
|
|
270
|
+
if (rows.length <= 1)
|
|
271
|
+
throw new Error('Cannot delete the last table row');
|
|
272
|
+
const row = rows[index];
|
|
273
|
+
if (!row)
|
|
274
|
+
throw new Error(`deleteRow index ${String(index)} out of range`);
|
|
275
|
+
tbl.removeChild(row);
|
|
276
|
+
}
|
|
277
|
+
export function insertTableColumn(node, spec) {
|
|
278
|
+
const tbl = tblOf(node);
|
|
279
|
+
const doc = node.ownerDocument;
|
|
280
|
+
if (!doc)
|
|
281
|
+
throw new Error('Element has no document');
|
|
282
|
+
const index = parseIndex(spec, 'insertColumn');
|
|
283
|
+
let grid = first(tbl, 'tblGrid');
|
|
284
|
+
if (!grid) {
|
|
285
|
+
grid = doc.createElementNS(NS.a, 'a:tblGrid');
|
|
286
|
+
const firstRow = directChildren(tbl, 'tr')[0];
|
|
287
|
+
if (firstRow)
|
|
288
|
+
tbl.insertBefore(grid, firstRow);
|
|
289
|
+
else
|
|
290
|
+
tbl.appendChild(grid);
|
|
291
|
+
}
|
|
292
|
+
const cols = directChildren(grid, 'gridCol');
|
|
293
|
+
const templateCol = cols[index] ?? cols[cols.length - 1];
|
|
294
|
+
const colW = templateCol ? (attr(templateCol, 'w') ?? DEFAULT_COL_W) : DEFAULT_COL_W;
|
|
295
|
+
const gridCol = doc.createElementNS(NS.a, 'a:gridCol');
|
|
296
|
+
gridCol.setAttribute('w', colW);
|
|
297
|
+
if (tableUsesColIds(tbl))
|
|
298
|
+
attachA16Id(gridCol, 'colId', nextTableUniqueId(tbl, 'colId'));
|
|
299
|
+
const colAnchor = cols[index];
|
|
300
|
+
if (colAnchor)
|
|
301
|
+
grid.insertBefore(gridCol, colAnchor);
|
|
302
|
+
else
|
|
303
|
+
grid.appendChild(gridCol);
|
|
304
|
+
for (const row of directChildren(tbl, 'tr')) {
|
|
305
|
+
const cells = directChildren(row, 'tc');
|
|
306
|
+
const template = cells[index] ?? cells[cells.length - 1];
|
|
307
|
+
const cell = template ? cloneCellWithText(template, '') : makeCell(doc, '');
|
|
308
|
+
const cellAnchor = cells[index];
|
|
309
|
+
if (cellAnchor)
|
|
310
|
+
row.insertBefore(cell, cellAnchor);
|
|
311
|
+
else
|
|
312
|
+
row.appendChild(cell);
|
|
313
|
+
}
|
|
314
|
+
growFrame(node, Number(colW) || Number(DEFAULT_COL_W), 0);
|
|
315
|
+
}
|
|
316
|
+
export function deleteTableColumn(node, spec) {
|
|
317
|
+
const tbl = tblOf(node);
|
|
318
|
+
const index = parseIndex(spec, 'deleteColumn');
|
|
319
|
+
const grid = first(tbl, 'tblGrid');
|
|
320
|
+
const cols = grid ? directChildren(grid, 'gridCol') : [];
|
|
321
|
+
const rowCells = directChildren(tbl, 'tr').map((r) => directChildren(r, 'tc').length);
|
|
322
|
+
if ((cols.length || Math.max(0, ...rowCells)) <= 1)
|
|
323
|
+
throw new Error('Cannot delete the last table column');
|
|
324
|
+
if (grid) {
|
|
325
|
+
const col = cols[index];
|
|
326
|
+
if (!col)
|
|
327
|
+
throw new Error(`deleteColumn index ${String(index)} out of range`);
|
|
328
|
+
grid.removeChild(col);
|
|
329
|
+
}
|
|
330
|
+
for (const row of directChildren(tbl, 'tr')) {
|
|
331
|
+
const cells = directChildren(row, 'tc');
|
|
332
|
+
const cell = cells[index];
|
|
333
|
+
if (!cell)
|
|
334
|
+
throw new Error(`deleteColumn index ${String(index)} out of range`);
|
|
335
|
+
row.removeChild(cell);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
export function setTableCellFill(cell, value) {
|
|
339
|
+
const doc = cell.ownerDocument;
|
|
340
|
+
if (!doc)
|
|
341
|
+
throw new Error('Element has no document');
|
|
342
|
+
let tcPr = directChildren(cell, 'tcPr')[0] ?? first(cell, 'tcPr');
|
|
343
|
+
if (!tcPr) {
|
|
344
|
+
tcPr = doc.createElementNS(NS.a, 'a:tcPr');
|
|
345
|
+
cell.appendChild(tcPr);
|
|
346
|
+
}
|
|
347
|
+
for (const child of [...children(tcPr)])
|
|
348
|
+
if (child.localName && TABLE_CELL_FILL_LOCAL.has(child.localName))
|
|
349
|
+
tcPr.removeChild(child);
|
|
350
|
+
if (isNone(value)) {
|
|
351
|
+
insertTcPrFill(tcPr, doc.createElementNS(NS.a, 'a:noFill'));
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
let color;
|
|
355
|
+
if (typeof value === 'string')
|
|
356
|
+
color = normalizeColor(value);
|
|
357
|
+
else if (typeof value === 'object' && value !== null) {
|
|
358
|
+
const record = value;
|
|
359
|
+
if (typeof record['color'] !== 'string')
|
|
360
|
+
throw new Error('fill.color must be a hex string');
|
|
361
|
+
color = normalizeColor(record['color']);
|
|
362
|
+
}
|
|
363
|
+
else
|
|
364
|
+
throw new Error('Unsupported fill value');
|
|
365
|
+
const solidFill = doc.createElementNS(NS.a, 'a:solidFill');
|
|
366
|
+
const srgb = doc.createElementNS(NS.a, 'a:srgbClr');
|
|
367
|
+
srgb.setAttribute('val', color);
|
|
368
|
+
solidFill.appendChild(srgb);
|
|
369
|
+
insertTcPrFill(tcPr, solidFill);
|
|
370
|
+
}
|
|
371
|
+
const ensureTcPr = (cell) => {
|
|
372
|
+
const doc = cell.ownerDocument;
|
|
373
|
+
if (!doc)
|
|
374
|
+
throw new Error('Element has no document');
|
|
375
|
+
let tcPr = directChildren(cell, 'tcPr')[0] ?? first(cell, 'tcPr');
|
|
376
|
+
if (!tcPr) {
|
|
377
|
+
tcPr = doc.createElementNS(NS.a, 'a:tcPr');
|
|
378
|
+
cell.appendChild(tcPr);
|
|
379
|
+
}
|
|
380
|
+
return tcPr;
|
|
381
|
+
};
|
|
382
|
+
const tableCells = (node) => directChildren(tblOf(node), 'tr').flatMap((tr) => directChildren(tr, 'tc'));
|
|
383
|
+
const setTableCellBorders = (cell, value) => {
|
|
384
|
+
const doc = cell.ownerDocument;
|
|
385
|
+
if (!doc)
|
|
386
|
+
throw new Error('Element has no document');
|
|
387
|
+
const tcPr = ensureTcPr(cell);
|
|
388
|
+
for (const child of [...children(tcPr)])
|
|
389
|
+
if (child.localName && TABLE_CELL_BORDER_SIDES.includes(child.localName))
|
|
390
|
+
tcPr.removeChild(child);
|
|
391
|
+
if (isNone(value)) {
|
|
392
|
+
for (const side of TABLE_CELL_BORDER_SIDES) {
|
|
393
|
+
const ln = doc.createElementNS(NS.a, `a:${side}`);
|
|
394
|
+
ln.appendChild(doc.createElementNS(NS.a, 'a:noFill'));
|
|
395
|
+
insertTcPrBorder(tcPr, ln);
|
|
396
|
+
}
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
let color = '0000FF';
|
|
400
|
+
let widthPt = DEFAULT_STROKE_PT;
|
|
401
|
+
let dash;
|
|
402
|
+
if (typeof value === 'string')
|
|
403
|
+
color = normalizeColor(value);
|
|
404
|
+
else if (typeof value === 'object' && value !== null) {
|
|
405
|
+
const record = value;
|
|
406
|
+
if (typeof record['color'] === 'string')
|
|
407
|
+
color = normalizeColor(record['color']);
|
|
408
|
+
else if (record['color'] !== undefined)
|
|
409
|
+
throw new Error('stroke.color must be a hex string');
|
|
410
|
+
const width = record['width'] ?? record['widthPt'];
|
|
411
|
+
if (typeof width === 'number' && Number.isFinite(width) && width > 0)
|
|
412
|
+
widthPt = width;
|
|
413
|
+
else if (width !== undefined)
|
|
414
|
+
throw new Error('stroke.width must be a positive number (pt)');
|
|
415
|
+
if (typeof record['dash'] === 'string')
|
|
416
|
+
dash = record['dash'];
|
|
417
|
+
else if (record['dash'] !== undefined)
|
|
418
|
+
throw new Error('stroke.dash must be a string');
|
|
419
|
+
}
|
|
420
|
+
else
|
|
421
|
+
throw new Error('Unsupported stroke value');
|
|
422
|
+
const widthEmu = String(lineWidthToEmu(widthPt));
|
|
423
|
+
for (const side of TABLE_CELL_BORDER_SIDES) {
|
|
424
|
+
const ln = doc.createElementNS(NS.a, `a:${side}`);
|
|
425
|
+
ln.setAttribute('w', widthEmu);
|
|
426
|
+
const solidFill = doc.createElementNS(NS.a, 'a:solidFill');
|
|
427
|
+
const srgb = doc.createElementNS(NS.a, 'a:srgbClr');
|
|
428
|
+
srgb.setAttribute('val', color);
|
|
429
|
+
solidFill.appendChild(srgb);
|
|
430
|
+
ln.appendChild(solidFill);
|
|
431
|
+
if (dash) {
|
|
432
|
+
const prstDash = doc.createElementNS(NS.a, 'a:prstDash');
|
|
433
|
+
prstDash.setAttribute('val', dash);
|
|
434
|
+
ln.appendChild(prstDash);
|
|
435
|
+
}
|
|
436
|
+
insertTcPrBorder(tcPr, ln);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
const TABLE_STRUCTURAL_KEYS = new Set([
|
|
440
|
+
'insertRow',
|
|
441
|
+
'deleteRow',
|
|
442
|
+
'insertColumn',
|
|
443
|
+
'deleteColumn',
|
|
444
|
+
'text',
|
|
445
|
+
'name',
|
|
446
|
+
]);
|
|
447
|
+
const TABLE_CELL_KEYS = new Set(['text', 'fill', 'paragraph.align']);
|
|
448
|
+
export function applyTableProperties(node, properties, context) {
|
|
449
|
+
const structural = {};
|
|
450
|
+
const visual = {};
|
|
451
|
+
const unexpected = [];
|
|
452
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
453
|
+
if (TABLE_STRUCTURAL_KEYS.has(key))
|
|
454
|
+
structural[key] = value;
|
|
455
|
+
else if (SHAPE_KEY_SET.has(key))
|
|
456
|
+
visual[key] = value;
|
|
457
|
+
else
|
|
458
|
+
unexpected.push(key);
|
|
459
|
+
}
|
|
460
|
+
if (unexpected.length)
|
|
461
|
+
return err('INVALID_COMMAND', `Unsupported table setProperties keys: ${unexpected.join(', ')}`);
|
|
462
|
+
const applied = [];
|
|
463
|
+
const diagnostics = [];
|
|
464
|
+
let structureGrew = false;
|
|
465
|
+
try {
|
|
466
|
+
if ('insertRow' in structural) {
|
|
467
|
+
insertTableRow(node, structural['insertRow']);
|
|
468
|
+
applied.push('insertRow');
|
|
469
|
+
structureGrew = true;
|
|
470
|
+
}
|
|
471
|
+
if ('deleteRow' in structural) {
|
|
472
|
+
deleteTableRow(node, structural['deleteRow']);
|
|
473
|
+
applied.push('deleteRow');
|
|
474
|
+
}
|
|
475
|
+
if ('insertColumn' in structural) {
|
|
476
|
+
insertTableColumn(node, structural['insertColumn']);
|
|
477
|
+
applied.push('insertColumn');
|
|
478
|
+
structureGrew = true;
|
|
479
|
+
}
|
|
480
|
+
if ('deleteColumn' in structural) {
|
|
481
|
+
deleteTableColumn(node, structural['deleteColumn']);
|
|
482
|
+
applied.push('deleteColumn');
|
|
483
|
+
}
|
|
484
|
+
if (typeof structural['text'] === 'string') {
|
|
485
|
+
setNodeText(node, structural['text']);
|
|
486
|
+
applied.push('text');
|
|
487
|
+
}
|
|
488
|
+
else if (structural['text'] !== undefined)
|
|
489
|
+
throw new Error('text must be a string');
|
|
490
|
+
if (typeof structural['name'] === 'string') {
|
|
491
|
+
const pr = first(node, 'cNvPr');
|
|
492
|
+
if (!pr)
|
|
493
|
+
throw new Error('Element has no cNvPr to set name');
|
|
494
|
+
pr.setAttribute('name', structural['name']);
|
|
495
|
+
applied.push('name');
|
|
496
|
+
}
|
|
497
|
+
else if (structural['name'] !== undefined)
|
|
498
|
+
throw new Error('name must be a string');
|
|
499
|
+
// Table fill/line map onto cells — graphicFrame has no spPr.
|
|
500
|
+
if ('fill' in visual) {
|
|
501
|
+
const cells = tableCells(node);
|
|
502
|
+
if (cells.length === 0)
|
|
503
|
+
throw new Error('Table has no cells to fill');
|
|
504
|
+
for (const cell of cells)
|
|
505
|
+
setTableCellFill(cell, visual['fill']);
|
|
506
|
+
applied.push('fill');
|
|
507
|
+
delete visual['fill'];
|
|
508
|
+
}
|
|
509
|
+
const strokeKeys = STROKE_ALIASES.filter((key) => key in visual);
|
|
510
|
+
if (strokeKeys.length > 1)
|
|
511
|
+
throw new Error(`Use only one stroke alias; found: ${strokeKeys.join(', ')}`);
|
|
512
|
+
if (strokeKeys[0]) {
|
|
513
|
+
const key = strokeKeys[0];
|
|
514
|
+
const cells = tableCells(node);
|
|
515
|
+
if (cells.length === 0)
|
|
516
|
+
throw new Error('Table has no cells for stroke');
|
|
517
|
+
for (const cell of cells)
|
|
518
|
+
setTableCellBorders(cell, visual[key]);
|
|
519
|
+
applied.push(key);
|
|
520
|
+
delete visual[key];
|
|
521
|
+
}
|
|
522
|
+
if (Object.keys(visual).length > 0) {
|
|
523
|
+
const styled = applyShapeProperties(node, visual, context);
|
|
524
|
+
if (!styled.ok)
|
|
525
|
+
return styled;
|
|
526
|
+
applied.push(...styled.value.applied);
|
|
527
|
+
}
|
|
528
|
+
if (structureGrew)
|
|
529
|
+
diagnostics.push(...tableLayoutWarnings(node, context));
|
|
530
|
+
}
|
|
531
|
+
catch (cause) {
|
|
532
|
+
return err('INVALID_COMMAND', cause instanceof Error ? cause.message : 'Invalid table properties');
|
|
533
|
+
}
|
|
534
|
+
return ok({ applied, diagnostics });
|
|
535
|
+
}
|
|
536
|
+
export function applyTableCellProperties(node, properties) {
|
|
537
|
+
const unexpected = Object.keys(properties).filter((k) => !TABLE_CELL_KEYS.has(k));
|
|
538
|
+
if (unexpected.length)
|
|
539
|
+
return err('INVALID_COMMAND', `Unsupported tableCell setProperties keys: ${unexpected.join(', ')}`);
|
|
540
|
+
const applied = [];
|
|
541
|
+
try {
|
|
542
|
+
if (typeof properties['text'] === 'string') {
|
|
543
|
+
setNodeText(node, properties['text']);
|
|
544
|
+
applied.push('text');
|
|
545
|
+
}
|
|
546
|
+
else if (properties['text'] !== undefined)
|
|
547
|
+
throw new Error('text must be a string');
|
|
548
|
+
if ('fill' in properties) {
|
|
549
|
+
setTableCellFill(node, properties['fill']);
|
|
550
|
+
applied.push('fill');
|
|
551
|
+
}
|
|
552
|
+
if ('paragraph.align' in properties) {
|
|
553
|
+
const styled = applyShapeProperties(node, {
|
|
554
|
+
'paragraph.align': properties['paragraph.align'],
|
|
555
|
+
});
|
|
556
|
+
if (!styled.ok)
|
|
557
|
+
return styled;
|
|
558
|
+
applied.push(...styled.value.applied);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
catch (cause) {
|
|
562
|
+
return err('INVALID_COMMAND', cause instanceof Error ? cause.message : 'Invalid tableCell properties');
|
|
563
|
+
}
|
|
564
|
+
return ok({ applied });
|
|
565
|
+
}
|
|
566
|
+
const cellPlainText = (tc) => descendants(tc, 't')
|
|
567
|
+
.map((t) => t.textContent ?? '')
|
|
568
|
+
.join('');
|
|
569
|
+
/** Read table grid width (sum of gridCol) or fall back to graphicFrame cx. */
|
|
570
|
+
const tableWidthEmu = (graphicFrame) => {
|
|
571
|
+
const gridCols = descendants(graphicFrame, 'gridCol');
|
|
572
|
+
if (gridCols.length > 0) {
|
|
573
|
+
const sum = gridCols.reduce((acc, col) => acc + Number(attr(col, 'w') ?? 0), 0);
|
|
574
|
+
if (sum > 0)
|
|
575
|
+
return sum;
|
|
576
|
+
}
|
|
577
|
+
const xfrm = first(graphicFrame, 'xfrm');
|
|
578
|
+
const ext = xfrm ? first(xfrm, 'ext') : undefined;
|
|
579
|
+
return Number(attr(ext, 'cx') ?? 914400);
|
|
580
|
+
};
|
|
581
|
+
export function applyTableLayout(graphicFrame, options) {
|
|
582
|
+
if (graphicFrame.localName !== 'graphicFrame')
|
|
583
|
+
return err('INVALID_COMMAND', 'setTableLayout target must be a table graphicFrame');
|
|
584
|
+
const tbl = first(graphicFrame, 'tbl');
|
|
585
|
+
if (!tbl)
|
|
586
|
+
return err('INVALID_COMMAND', 'Target is not a DrawingML table');
|
|
587
|
+
const rows = directChildren(tbl, 'tr');
|
|
588
|
+
if (rows.length === 0)
|
|
589
|
+
return err('INVALID_COMMAND', 'Table has no rows');
|
|
590
|
+
const widthEmu = tableWidthEmu(graphicFrame);
|
|
591
|
+
const textRows = rows.map((tr) => directChildren(tr, 'tc').map((tc) => cellPlainText(tc)));
|
|
592
|
+
const redistribute = options.redistribute ??
|
|
593
|
+
(options.height === 'auto' || options.height === undefined ? 'content' : 'equal');
|
|
594
|
+
const xfrm = first(graphicFrame, 'xfrm');
|
|
595
|
+
const ext = xfrm ? first(xfrm, 'ext') : undefined;
|
|
596
|
+
if (!ext)
|
|
597
|
+
return err('INVALID_COMMAND', 'Table graphicFrame has no xfrm/ext');
|
|
598
|
+
const applied = [];
|
|
599
|
+
let totalHeightEmu;
|
|
600
|
+
let estimatedEmu;
|
|
601
|
+
if (redistribute === 'content' || options.height === 'auto') {
|
|
602
|
+
const layout = measureTableLayout({ rows: textRows, widthEmu, fontPt: 11 });
|
|
603
|
+
estimatedEmu = layout.totalHeightEmu;
|
|
604
|
+
for (let i = 0; i < rows.length; i++) {
|
|
605
|
+
const h = layout.rowHeightsEmu[i] ?? estimateTableRowHeightEmu(11);
|
|
606
|
+
rows[i].setAttribute('h', String(h));
|
|
607
|
+
}
|
|
608
|
+
if (typeof options.height === 'number') {
|
|
609
|
+
totalHeightEmu = options.height;
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
totalHeightEmu = layout.totalHeightEmu;
|
|
613
|
+
}
|
|
614
|
+
applied.push('redistribute:content');
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
totalHeightEmu =
|
|
618
|
+
typeof options.height === 'number'
|
|
619
|
+
? options.height
|
|
620
|
+
: Number(attr(ext, 'cy') ?? estimateTableHeightEmu(rows.length));
|
|
621
|
+
const rowH = Math.max(1, Math.floor(totalHeightEmu / rows.length));
|
|
622
|
+
let assigned = 0;
|
|
623
|
+
for (let i = 0; i < rows.length; i++) {
|
|
624
|
+
const h = i === rows.length - 1 ? Math.max(1, totalHeightEmu - assigned) : rowH;
|
|
625
|
+
rows[i].setAttribute('h', String(h));
|
|
626
|
+
assigned += h;
|
|
627
|
+
}
|
|
628
|
+
applied.push('redistribute:equal');
|
|
629
|
+
}
|
|
630
|
+
ext.setAttribute('cy', String(Math.round(totalHeightEmu)));
|
|
631
|
+
applied.push('height');
|
|
632
|
+
return ok({
|
|
633
|
+
applied,
|
|
634
|
+
totalHeightEmu,
|
|
635
|
+
...(estimatedEmu !== undefined ? { estimatedEmu } : {}),
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
//# sourceMappingURL=table.js.map
|