@deckflow/deckuse-pptx 0.1.0 → 1.1.0
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/LICENSE +662 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapter.d.ts +70 -3
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +519 -86
- package/dist/adapter.js.map +1 -1
- package/dist/addressing.d.ts +45 -0
- package/dist/addressing.d.ts.map +1 -0
- package/dist/addressing.js +401 -0
- package/dist/addressing.js.map +1 -0
- package/dist/chart-classify.d.ts +10 -0
- package/dist/chart-classify.d.ts.map +1 -0
- package/dist/chart-classify.js +54 -0
- package/dist/chart-classify.js.map +1 -0
- package/dist/chart.d.ts +33 -0
- package/dist/chart.d.ts.map +1 -0
- package/dist/chart.js +492 -0
- package/dist/chart.js.map +1 -0
- package/dist/edition.d.ts +20 -0
- package/dist/edition.d.ts.map +1 -0
- package/dist/edition.js +46 -0
- package/dist/edition.js.map +1 -0
- package/dist/elements.d.ts.map +1 -1
- package/dist/elements.js +61 -46
- package/dist/elements.js.map +1 -1
- package/dist/hyperlink.d.ts +7 -0
- package/dist/hyperlink.d.ts.map +1 -0
- package/dist/hyperlink.js +83 -0
- package/dist/hyperlink.js.map +1 -0
- package/dist/index-sync.d.ts +7 -0
- package/dist/index-sync.d.ts.map +1 -0
- package/dist/index-sync.js +24 -0
- package/dist/index-sync.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/indexer.d.ts +4 -0
- package/dist/indexer.d.ts.map +1 -1
- package/dist/indexer.js +105 -27
- package/dist/indexer.js.map +1 -1
- package/dist/media.d.ts +15 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +141 -0
- package/dist/media.js.map +1 -0
- package/dist/mutations.d.ts +8 -2
- package/dist/mutations.d.ts.map +1 -1
- package/dist/mutations.js +695 -72
- package/dist/mutations.js.map +1 -1
- package/dist/picture.d.ts.map +1 -1
- package/dist/picture.js +1 -3
- package/dist/picture.js.map +1 -1
- package/dist/properties.d.ts +6 -1
- package/dist/properties.d.ts.map +1 -1
- package/dist/properties.js +159 -10
- package/dist/properties.js.map +1 -1
- package/dist/resolve-properties.d.ts +17 -0
- package/dist/resolve-properties.d.ts.map +1 -0
- package/dist/resolve-properties.js +545 -0
- package/dist/resolve-properties.js.map +1 -0
- package/dist/slides.d.ts +3 -1
- package/dist/slides.d.ts.map +1 -1
- package/dist/slides.js +77 -4
- package/dist/slides.js.map +1 -1
- package/dist/table.d.ts +15 -0
- package/dist/table.d.ts.map +1 -0
- package/dist/table.js +456 -0
- package/dist/table.js.map +1 -0
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/workspace.d.ts +9 -9
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +67 -47
- package/dist/workspace.js.map +1 -1
- package/dist/xml.d.ts +7 -0
- package/dist/xml.d.ts.map +1 -1
- package/dist/xml.js +86 -13
- package/dist/xml.js.map +1 -1
- package/package.json +12 -4
package/dist/slides.js
CHANGED
|
@@ -3,6 +3,8 @@ import { OpcArchive } from '@deckflow/deckuse-opc';
|
|
|
3
3
|
import { cleanupUnreferencedPart } from './picture.js';
|
|
4
4
|
import { NS, REL, attr, descendants, first } from './xml.js';
|
|
5
5
|
const SLIDE_CT = 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml';
|
|
6
|
+
const NOTES_CT = 'application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml';
|
|
7
|
+
const EP_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/extended-properties';
|
|
6
8
|
const nextNumber = (archive, prefix) => Math.max(0, ...[...archive.parts.keys()]
|
|
7
9
|
.filter((n) => n.startsWith(prefix))
|
|
8
10
|
.map((n) => Number(/(\d+)\.xml$/.exec(n)?.[1] ?? 0))) + 1;
|
|
@@ -14,6 +16,67 @@ const nextRelId = (rels) => {
|
|
|
14
16
|
n++;
|
|
15
17
|
return `rId${String(n)}`;
|
|
16
18
|
};
|
|
19
|
+
/** Keep docProps/app.xml Slides/Notes in sync with the package (PowerPoint repairs on mismatch). */
|
|
20
|
+
const syncAppSlideCounts = (archive) => {
|
|
21
|
+
const part = archive.getPart('/docProps/app.xml');
|
|
22
|
+
if (!part)
|
|
23
|
+
return;
|
|
24
|
+
const slideCount = descendants(archive.readXml('/ppt/presentation.xml'), 'sldId').length;
|
|
25
|
+
let notesCount = 0;
|
|
26
|
+
for (const [slidePart, rels] of archive.relationships) {
|
|
27
|
+
if (!slidePart.startsWith('/ppt/slides/'))
|
|
28
|
+
continue;
|
|
29
|
+
if (rels.some((r) => r.type === REL.notes))
|
|
30
|
+
notesCount++;
|
|
31
|
+
}
|
|
32
|
+
const app = archive.readXml('/docProps/app.xml');
|
|
33
|
+
let dirty = false;
|
|
34
|
+
for (const [localName, value] of [
|
|
35
|
+
['Slides', String(slideCount)],
|
|
36
|
+
['Notes', String(notesCount)],
|
|
37
|
+
]) {
|
|
38
|
+
const el = descendants(app, localName).find((node) => !node.namespaceURI || node.namespaceURI === EP_NS);
|
|
39
|
+
if (!el)
|
|
40
|
+
continue;
|
|
41
|
+
if ((el.textContent ?? '') === value)
|
|
42
|
+
continue;
|
|
43
|
+
while (el.firstChild)
|
|
44
|
+
el.removeChild(el.firstChild);
|
|
45
|
+
el.appendChild(app.createTextNode(value));
|
|
46
|
+
dirty = true;
|
|
47
|
+
}
|
|
48
|
+
if (dirty)
|
|
49
|
+
archive.writeXml('/docProps/app.xml', app, part.mediaType);
|
|
50
|
+
};
|
|
51
|
+
/** Create a notes slide part for `slidePart` when missing; return the notes part URI. */
|
|
52
|
+
export function ensureNotes(archive, slidePart) {
|
|
53
|
+
const existing = archive.getRelationships(slidePart).find((r) => r.type === REL.notes);
|
|
54
|
+
if (existing?.resolvedTarget && archive.getPart(existing.resolvedTarget))
|
|
55
|
+
return existing.resolvedTarget;
|
|
56
|
+
const number = nextNumber(archive, '/ppt/notesSlides/');
|
|
57
|
+
const part = `/ppt/notesSlides/notesSlide${String(number)}.xml`;
|
|
58
|
+
archive.setPart(part, new TextEncoder().encode(`<p:notes xmlns:p="${NS.p}" xmlns:a="${NS.a}" xmlns:r="${NS.r}"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr/><p:sp><p:nvSpPr><p:cNvPr id="2" name="Notes Placeholder"/><p:cNvSpPr txBox="1"/><p:nvPr><p:ph type="body" idx="1"/></p:nvPr></p:nvSpPr><p:spPr/><p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:t></a:t></a:r></a:p></p:txBody></p:sp></p:spTree></p:cSld></p:notes>`), NOTES_CT);
|
|
59
|
+
archive.setRelationships(part, [
|
|
60
|
+
{
|
|
61
|
+
id: 'rId1',
|
|
62
|
+
type: REL.slide,
|
|
63
|
+
target: relativeTarget(part, slidePart),
|
|
64
|
+
external: false,
|
|
65
|
+
resolvedTarget: slidePart,
|
|
66
|
+
},
|
|
67
|
+
]);
|
|
68
|
+
const rels = [...archive.getRelationships(slidePart)];
|
|
69
|
+
const rid = nextRelId(rels);
|
|
70
|
+
rels.push({
|
|
71
|
+
id: rid,
|
|
72
|
+
type: REL.notes,
|
|
73
|
+
target: relativeTarget(slidePart, part),
|
|
74
|
+
external: false,
|
|
75
|
+
resolvedTarget: part,
|
|
76
|
+
});
|
|
77
|
+
archive.setRelationships(slidePart, rels);
|
|
78
|
+
return part;
|
|
79
|
+
}
|
|
17
80
|
const cloneMutableTargets = (archive, sourcePart, newPart) => archive.getRelationships(sourcePart).map((rel) => {
|
|
18
81
|
if (rel.external || !rel.resolvedTarget)
|
|
19
82
|
return rel;
|
|
@@ -42,7 +105,7 @@ const presentationState = (archive) => {
|
|
|
42
105
|
throw new Error('presentation.xml has no sldIdLst');
|
|
43
106
|
return { doc, list, rels: [...archive.getRelationships('/ppt/presentation.xml')] };
|
|
44
107
|
};
|
|
45
|
-
export function addSlide(archive, templatePart, layoutPart) {
|
|
108
|
+
export function addSlide(archive, templatePart, layoutPart, afterIndex) {
|
|
46
109
|
const { doc, list, rels } = presentationState(archive);
|
|
47
110
|
const number = nextNumber(archive, '/ppt/slides/');
|
|
48
111
|
const part = `/ppt/slides/slide${String(number)}.xml`;
|
|
@@ -83,8 +146,17 @@ export function addSlide(archive, templatePart, layoutPart) {
|
|
|
83
146
|
const sld = doc.createElementNS(NS.p, 'p:sldId');
|
|
84
147
|
sld.setAttribute('id', String(Math.max(255, ...descendants(doc, 'sldId').map((n) => Number(attr(n, 'id') ?? 255))) + 1));
|
|
85
148
|
sld.setAttributeNS(NS.r, 'r:id', rid);
|
|
86
|
-
|
|
87
|
-
|
|
149
|
+
// Insert after the given 1-based slide index; append when omitted / out of range.
|
|
150
|
+
const existing = descendants(list, 'sldId');
|
|
151
|
+
const anchor = afterIndex !== undefined && afterIndex > 0 ? existing[afterIndex - 1] : undefined;
|
|
152
|
+
if (anchor?.nextSibling)
|
|
153
|
+
list.insertBefore(sld, anchor.nextSibling);
|
|
154
|
+
else if (anchor)
|
|
155
|
+
list.appendChild(sld);
|
|
156
|
+
else
|
|
157
|
+
list.appendChild(sld);
|
|
158
|
+
archive.writeXml('/ppt/presentation.xml', doc);
|
|
159
|
+
syncAppSlideCounts(archive);
|
|
88
160
|
return part;
|
|
89
161
|
}
|
|
90
162
|
export function duplicateSlide(archive, part) {
|
|
@@ -107,7 +179,8 @@ export function removeSlide(archive, part) {
|
|
|
107
179
|
archive.deletePart(part);
|
|
108
180
|
for (const target of targets)
|
|
109
181
|
cleanupUnreferencedPart(archive, target);
|
|
110
|
-
archive.writeXml('/ppt/presentation.xml', doc
|
|
182
|
+
archive.writeXml('/ppt/presentation.xml', doc);
|
|
183
|
+
syncAppSlideCounts(archive);
|
|
111
184
|
}
|
|
112
185
|
export function slideElementPart(refPart) {
|
|
113
186
|
return refPart.split('#')[0] ?? refPart;
|
package/dist/slides.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slides.js","sourceRoot":"","sources":["../src/slides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAwB,MAAM,uBAAuB,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAC7D,MAAM,QAAQ,GAAG,wEAAwE,CAAC;AAC1F,MAAM,UAAU,GAAG,CAAC,OAAmB,EAAE,MAAc,EAAU,EAAE,CACjE,IAAI,CAAC,GAAG,CACN,CAAC,EACD,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;KACzB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;KACnC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACvD,GAAG,CAAC,CAAC;AACR,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,MAAc,EAAU,EAAE,CAChE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACnE,MAAM,SAAS,GAAG,CAAC,IAAgC,EAAU,EAAE;IAC7D,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5C,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,IAAI,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAAE,CAAC,EAAE,CAAC;IACxC,OAAO,MAAM,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"slides.js","sourceRoot":"","sources":["../src/slides.ts"],"names":[],"mappings":"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"}
|
package/dist/table.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Result } from '@deckflow/deckuse-core';
|
|
2
|
+
import type { Element } from '@xmldom/xmldom';
|
|
3
|
+
import { type ShapePropertyContext } from './properties.js';
|
|
4
|
+
export declare function insertTableRow(node: Element, spec: unknown): void;
|
|
5
|
+
export declare function deleteTableRow(node: Element, spec: unknown): void;
|
|
6
|
+
export declare function insertTableColumn(node: Element, spec: unknown): void;
|
|
7
|
+
export declare function deleteTableColumn(node: Element, spec: unknown): void;
|
|
8
|
+
export declare function setTableCellFill(cell: Element, value: unknown): void;
|
|
9
|
+
export declare function applyTableProperties(node: Element, properties: Record<string, unknown>, context?: ShapePropertyContext): Result<{
|
|
10
|
+
applied: string[];
|
|
11
|
+
}>;
|
|
12
|
+
export declare function applyTableCellProperties(node: Element, properties: Record<string, unknown>): Result<{
|
|
13
|
+
applied: string[];
|
|
14
|
+
}>;
|
|
15
|
+
//# sourceMappingURL=table.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,iBAAiB,CAAC;AAgKzB,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAsBjE;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAQjE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CA2BpE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAmBpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CA4BpE;AA6ED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnC,OAAO,CAAC,EAAE,oBAAoB,GAC7B,MAAM,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA0E/B;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,EACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,MAAM,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAwB/B"}
|
package/dist/table.js
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
import { err, ok } from '@deckflow/deckuse-core';
|
|
2
|
+
import { applyShapeProperties, shapePropertyKeys, } from './properties.js';
|
|
3
|
+
import { NS, attr, children, descendants, first, setNodeText } from './xml.js';
|
|
4
|
+
const DEFAULT_ROW_H = '370840';
|
|
5
|
+
const DEFAULT_COL_W = '914400';
|
|
6
|
+
const EMU_PER_PT = 12700;
|
|
7
|
+
const DEFAULT_STROKE_PT = 1;
|
|
8
|
+
const MAX_LINE_WIDTH_EMU = 20116800; // ST_LineWidth maxInclusive
|
|
9
|
+
const MAX_LINE_WIDTH_PT = MAX_LINE_WIDTH_EMU / EMU_PER_PT;
|
|
10
|
+
/** DrawingML 2014 table unique ids (PowerPoint writes these on modern tables). */
|
|
11
|
+
const NS_A16 = 'http://schemas.microsoft.com/office/drawing/2014/main';
|
|
12
|
+
const COL_ID_URI = '{9D8B030D-6E8A-4147-A177-3AD203B41FA5}';
|
|
13
|
+
const ROW_ID_URI = '{0D108BD9-81ED-4DB2-BD59-A6C34878D82A}';
|
|
14
|
+
/** Convert line.width to EMU. Values above max pt are treated as already-EMU. */
|
|
15
|
+
const lineWidthToEmu = (width) => {
|
|
16
|
+
const emu = width > MAX_LINE_WIDTH_PT ? Math.round(width) : Math.round(width * EMU_PER_PT);
|
|
17
|
+
return Math.min(MAX_LINE_WIDTH_EMU, Math.max(1, emu));
|
|
18
|
+
};
|
|
19
|
+
const STROKE_ALIASES = ['stroke', 'border', 'outline', 'line'];
|
|
20
|
+
const TABLE_CELL_BORDER_SIDES = ['lnL', 'lnR', 'lnT', 'lnB'];
|
|
21
|
+
const TABLE_CELL_BORDER_LOCAL = new Set([
|
|
22
|
+
...TABLE_CELL_BORDER_SIDES,
|
|
23
|
+
'lnTlToBr',
|
|
24
|
+
'lnBlToTr',
|
|
25
|
+
'cell3D',
|
|
26
|
+
]);
|
|
27
|
+
const TABLE_CELL_FILL_LOCAL = new Set([
|
|
28
|
+
'noFill',
|
|
29
|
+
'solidFill',
|
|
30
|
+
'gradFill',
|
|
31
|
+
'blipFill',
|
|
32
|
+
'pattFill',
|
|
33
|
+
'grpFill',
|
|
34
|
+
]);
|
|
35
|
+
const SHAPE_KEY_SET = new Set(shapePropertyKeys);
|
|
36
|
+
/** Insert fill after border sides (OOXML CT_TableCellProperties order). */
|
|
37
|
+
const insertTcPrFill = (tcPr, fill) => {
|
|
38
|
+
const kids = children(tcPr);
|
|
39
|
+
let lastBorder;
|
|
40
|
+
for (const child of kids)
|
|
41
|
+
if (child.localName && TABLE_CELL_BORDER_LOCAL.has(child.localName))
|
|
42
|
+
lastBorder = child;
|
|
43
|
+
if (lastBorder?.nextSibling)
|
|
44
|
+
tcPr.insertBefore(fill, lastBorder.nextSibling);
|
|
45
|
+
else if (lastBorder)
|
|
46
|
+
tcPr.appendChild(fill);
|
|
47
|
+
else if (tcPr.firstChild)
|
|
48
|
+
tcPr.insertBefore(fill, tcPr.firstChild);
|
|
49
|
+
else
|
|
50
|
+
tcPr.appendChild(fill);
|
|
51
|
+
};
|
|
52
|
+
/** Insert a border side before fill / margins (OOXML CT_TableCellProperties order). */
|
|
53
|
+
const insertTcPrBorder = (tcPr, ln) => {
|
|
54
|
+
const kids = children(tcPr);
|
|
55
|
+
const fillOrLater = kids.find((child) => child.localName && !TABLE_CELL_BORDER_LOCAL.has(child.localName));
|
|
56
|
+
if (fillOrLater)
|
|
57
|
+
tcPr.insertBefore(ln, fillOrLater);
|
|
58
|
+
else
|
|
59
|
+
tcPr.appendChild(ln);
|
|
60
|
+
};
|
|
61
|
+
const directChildren = (node, localName) => children(node).filter((c) => c.localName === localName);
|
|
62
|
+
const normalizeColor = (value) => {
|
|
63
|
+
const hex = value.trim().replace(/^#/, '').toUpperCase();
|
|
64
|
+
if (!/^[0-9A-F]{6}$/.test(hex))
|
|
65
|
+
throw new Error(`Invalid color: ${value}`);
|
|
66
|
+
return hex;
|
|
67
|
+
};
|
|
68
|
+
const isNone = (value) => {
|
|
69
|
+
if (value === null || value === false || value === 'none')
|
|
70
|
+
return true;
|
|
71
|
+
return (typeof value === 'object' &&
|
|
72
|
+
value !== null &&
|
|
73
|
+
'type' in value &&
|
|
74
|
+
value.type === 'none');
|
|
75
|
+
};
|
|
76
|
+
const tblOf = (node) => {
|
|
77
|
+
const tbl = first(node, 'tbl');
|
|
78
|
+
if (!tbl)
|
|
79
|
+
throw new Error('Table element has no a:tbl');
|
|
80
|
+
return tbl;
|
|
81
|
+
};
|
|
82
|
+
const makeCell = (doc, text = '') => {
|
|
83
|
+
const tc = doc.createElementNS(NS.a, 'a:tc');
|
|
84
|
+
const txBody = doc.createElementNS(NS.a, 'a:txBody');
|
|
85
|
+
txBody.appendChild(doc.createElementNS(NS.a, 'a:bodyPr'));
|
|
86
|
+
txBody.appendChild(doc.createElementNS(NS.a, 'a:lstStyle'));
|
|
87
|
+
const p = doc.createElementNS(NS.a, 'a:p');
|
|
88
|
+
const r = doc.createElementNS(NS.a, 'a:r');
|
|
89
|
+
const t = doc.createElementNS(NS.a, 'a:t');
|
|
90
|
+
t.appendChild(doc.createTextNode(text));
|
|
91
|
+
r.appendChild(t);
|
|
92
|
+
p.appendChild(r);
|
|
93
|
+
txBody.appendChild(p);
|
|
94
|
+
tc.appendChild(txBody);
|
|
95
|
+
tc.appendChild(doc.createElementNS(NS.a, 'a:tcPr'));
|
|
96
|
+
return tc;
|
|
97
|
+
};
|
|
98
|
+
/** True when any gridCol already carries a16:colId (PowerPoint modern table). */
|
|
99
|
+
const tableUsesColIds = (tbl) => descendants(tbl, 'colId').some((el) => el.namespaceURI === NS_A16 || el.prefix === 'a16' || el.localName === 'colId');
|
|
100
|
+
/** True when any row already carries a16:rowId. */
|
|
101
|
+
const tableUsesRowIds = (tbl) => descendants(tbl, 'rowId').some((el) => el.namespaceURI === NS_A16 || el.prefix === 'a16' || el.localName === 'rowId');
|
|
102
|
+
const nextTableUniqueId = (tbl, localName) => {
|
|
103
|
+
let max = 0;
|
|
104
|
+
for (const el of descendants(tbl, localName)) {
|
|
105
|
+
const n = Number(attr(el, 'val') ?? 0);
|
|
106
|
+
if (Number.isFinite(n) && n > max)
|
|
107
|
+
max = n;
|
|
108
|
+
}
|
|
109
|
+
// PowerPoint typically uses 10000+ for rows and 20000+ for cols; stay above either.
|
|
110
|
+
return String(Math.max(max + 1, localName === 'colId' ? 20000 : 10000));
|
|
111
|
+
};
|
|
112
|
+
const attachA16Id = (parent, kind, id) => {
|
|
113
|
+
const doc = parent.ownerDocument;
|
|
114
|
+
if (!doc)
|
|
115
|
+
throw new Error('Element has no document');
|
|
116
|
+
const uri = kind === 'colId' ? COL_ID_URI : ROW_ID_URI;
|
|
117
|
+
const extLst = doc.createElementNS(NS.a, 'a:extLst');
|
|
118
|
+
const ext = doc.createElementNS(NS.a, 'a:ext');
|
|
119
|
+
ext.setAttribute('uri', uri);
|
|
120
|
+
const idEl = doc.createElementNS(NS_A16, `a16:${kind}`);
|
|
121
|
+
idEl.setAttribute('val', id);
|
|
122
|
+
ext.appendChild(idEl);
|
|
123
|
+
extLst.appendChild(ext);
|
|
124
|
+
parent.appendChild(extLst);
|
|
125
|
+
};
|
|
126
|
+
const parseIndex = (value, name) => {
|
|
127
|
+
if (typeof value === 'number' && Number.isInteger(value) && value >= 0)
|
|
128
|
+
return value;
|
|
129
|
+
if (typeof value === 'object' && value !== null && 'index' in value) {
|
|
130
|
+
const index = value.index;
|
|
131
|
+
if (typeof index === 'number' && Number.isInteger(index) && index >= 0)
|
|
132
|
+
return index;
|
|
133
|
+
}
|
|
134
|
+
throw new Error(`${name} requires a non-negative integer index`);
|
|
135
|
+
};
|
|
136
|
+
const cellsFromSpec = (value) => {
|
|
137
|
+
if (typeof value !== 'object' || value === null)
|
|
138
|
+
return undefined;
|
|
139
|
+
const cells = value.cells;
|
|
140
|
+
if (cells === undefined)
|
|
141
|
+
return undefined;
|
|
142
|
+
if (!Array.isArray(cells) || !cells.every((c) => typeof c === 'string'))
|
|
143
|
+
throw new Error('cells must be an array of strings');
|
|
144
|
+
return cells;
|
|
145
|
+
};
|
|
146
|
+
export function insertTableRow(node, spec) {
|
|
147
|
+
const tbl = tblOf(node);
|
|
148
|
+
const doc = node.ownerDocument;
|
|
149
|
+
if (!doc)
|
|
150
|
+
throw new Error('Element has no document');
|
|
151
|
+
const index = parseIndex(spec, 'insertRow');
|
|
152
|
+
const rows = directChildren(tbl, 'tr');
|
|
153
|
+
const grid = first(tbl, 'tblGrid');
|
|
154
|
+
const colCount = grid
|
|
155
|
+
? directChildren(grid, 'gridCol').length
|
|
156
|
+
: rows[0]
|
|
157
|
+
? directChildren(rows[0], 'tc').length
|
|
158
|
+
: 1;
|
|
159
|
+
const texts = cellsFromSpec(spec) ?? Array.from({ length: colCount }, () => '');
|
|
160
|
+
const tr = doc.createElementNS(NS.a, 'a:tr');
|
|
161
|
+
tr.setAttribute('h', DEFAULT_ROW_H);
|
|
162
|
+
for (let i = 0; i < colCount; i++)
|
|
163
|
+
tr.appendChild(makeCell(doc, texts[i] ?? ''));
|
|
164
|
+
// Keep a16:rowId complete when the source table already uses them — partial
|
|
165
|
+
// coverage makes PowerPoint prompt to repair the package.
|
|
166
|
+
if (tableUsesRowIds(tbl))
|
|
167
|
+
attachA16Id(tr, 'rowId', nextTableUniqueId(tbl, 'rowId'));
|
|
168
|
+
const anchor = rows[index];
|
|
169
|
+
if (anchor)
|
|
170
|
+
tbl.insertBefore(tr, anchor);
|
|
171
|
+
else
|
|
172
|
+
tbl.appendChild(tr);
|
|
173
|
+
}
|
|
174
|
+
export function deleteTableRow(node, spec) {
|
|
175
|
+
const tbl = tblOf(node);
|
|
176
|
+
const index = parseIndex(spec, 'deleteRow');
|
|
177
|
+
const rows = directChildren(tbl, 'tr');
|
|
178
|
+
if (rows.length <= 1)
|
|
179
|
+
throw new Error('Cannot delete the last table row');
|
|
180
|
+
const row = rows[index];
|
|
181
|
+
if (!row)
|
|
182
|
+
throw new Error(`deleteRow index ${String(index)} out of range`);
|
|
183
|
+
tbl.removeChild(row);
|
|
184
|
+
}
|
|
185
|
+
export function insertTableColumn(node, spec) {
|
|
186
|
+
const tbl = tblOf(node);
|
|
187
|
+
const doc = node.ownerDocument;
|
|
188
|
+
if (!doc)
|
|
189
|
+
throw new Error('Element has no document');
|
|
190
|
+
const index = parseIndex(spec, 'insertColumn');
|
|
191
|
+
let grid = first(tbl, 'tblGrid');
|
|
192
|
+
if (!grid) {
|
|
193
|
+
grid = doc.createElementNS(NS.a, 'a:tblGrid');
|
|
194
|
+
const firstRow = directChildren(tbl, 'tr')[0];
|
|
195
|
+
if (firstRow)
|
|
196
|
+
tbl.insertBefore(grid, firstRow);
|
|
197
|
+
else
|
|
198
|
+
tbl.appendChild(grid);
|
|
199
|
+
}
|
|
200
|
+
const cols = directChildren(grid, 'gridCol');
|
|
201
|
+
const gridCol = doc.createElementNS(NS.a, 'a:gridCol');
|
|
202
|
+
gridCol.setAttribute('w', cols[0] ? (attr(cols[0], 'w') ?? DEFAULT_COL_W) : DEFAULT_COL_W);
|
|
203
|
+
if (tableUsesColIds(tbl))
|
|
204
|
+
attachA16Id(gridCol, 'colId', nextTableUniqueId(tbl, 'colId'));
|
|
205
|
+
const colAnchor = cols[index];
|
|
206
|
+
if (colAnchor)
|
|
207
|
+
grid.insertBefore(gridCol, colAnchor);
|
|
208
|
+
else
|
|
209
|
+
grid.appendChild(gridCol);
|
|
210
|
+
for (const row of directChildren(tbl, 'tr')) {
|
|
211
|
+
const cells = directChildren(row, 'tc');
|
|
212
|
+
const cell = makeCell(doc, '');
|
|
213
|
+
const cellAnchor = cells[index];
|
|
214
|
+
if (cellAnchor)
|
|
215
|
+
row.insertBefore(cell, cellAnchor);
|
|
216
|
+
else
|
|
217
|
+
row.appendChild(cell);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
export function deleteTableColumn(node, spec) {
|
|
221
|
+
const tbl = tblOf(node);
|
|
222
|
+
const index = parseIndex(spec, 'deleteColumn');
|
|
223
|
+
const grid = first(tbl, 'tblGrid');
|
|
224
|
+
const cols = grid ? directChildren(grid, 'gridCol') : [];
|
|
225
|
+
const rowCells = directChildren(tbl, 'tr').map((r) => directChildren(r, 'tc').length);
|
|
226
|
+
if ((cols.length || Math.max(0, ...rowCells)) <= 1)
|
|
227
|
+
throw new Error('Cannot delete the last table column');
|
|
228
|
+
if (grid) {
|
|
229
|
+
const col = cols[index];
|
|
230
|
+
if (!col)
|
|
231
|
+
throw new Error(`deleteColumn index ${String(index)} out of range`);
|
|
232
|
+
grid.removeChild(col);
|
|
233
|
+
}
|
|
234
|
+
for (const row of directChildren(tbl, 'tr')) {
|
|
235
|
+
const cells = directChildren(row, 'tc');
|
|
236
|
+
const cell = cells[index];
|
|
237
|
+
if (!cell)
|
|
238
|
+
throw new Error(`deleteColumn index ${String(index)} out of range`);
|
|
239
|
+
row.removeChild(cell);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
export function setTableCellFill(cell, value) {
|
|
243
|
+
const doc = cell.ownerDocument;
|
|
244
|
+
if (!doc)
|
|
245
|
+
throw new Error('Element has no document');
|
|
246
|
+
let tcPr = directChildren(cell, 'tcPr')[0] ?? first(cell, 'tcPr');
|
|
247
|
+
if (!tcPr) {
|
|
248
|
+
tcPr = doc.createElementNS(NS.a, 'a:tcPr');
|
|
249
|
+
cell.appendChild(tcPr);
|
|
250
|
+
}
|
|
251
|
+
for (const child of [...children(tcPr)])
|
|
252
|
+
if (child.localName && TABLE_CELL_FILL_LOCAL.has(child.localName))
|
|
253
|
+
tcPr.removeChild(child);
|
|
254
|
+
if (isNone(value)) {
|
|
255
|
+
insertTcPrFill(tcPr, doc.createElementNS(NS.a, 'a:noFill'));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
let color;
|
|
259
|
+
if (typeof value === 'string')
|
|
260
|
+
color = normalizeColor(value);
|
|
261
|
+
else if (typeof value === 'object' && value !== null) {
|
|
262
|
+
const record = value;
|
|
263
|
+
if (typeof record['color'] !== 'string')
|
|
264
|
+
throw new Error('fill.color must be a hex string');
|
|
265
|
+
color = normalizeColor(record['color']);
|
|
266
|
+
}
|
|
267
|
+
else
|
|
268
|
+
throw new Error('Unsupported fill value');
|
|
269
|
+
const solidFill = doc.createElementNS(NS.a, 'a:solidFill');
|
|
270
|
+
const srgb = doc.createElementNS(NS.a, 'a:srgbClr');
|
|
271
|
+
srgb.setAttribute('val', color);
|
|
272
|
+
solidFill.appendChild(srgb);
|
|
273
|
+
insertTcPrFill(tcPr, solidFill);
|
|
274
|
+
}
|
|
275
|
+
const ensureTcPr = (cell) => {
|
|
276
|
+
const doc = cell.ownerDocument;
|
|
277
|
+
if (!doc)
|
|
278
|
+
throw new Error('Element has no document');
|
|
279
|
+
let tcPr = directChildren(cell, 'tcPr')[0] ?? first(cell, 'tcPr');
|
|
280
|
+
if (!tcPr) {
|
|
281
|
+
tcPr = doc.createElementNS(NS.a, 'a:tcPr');
|
|
282
|
+
cell.appendChild(tcPr);
|
|
283
|
+
}
|
|
284
|
+
return tcPr;
|
|
285
|
+
};
|
|
286
|
+
const tableCells = (node) => directChildren(tblOf(node), 'tr').flatMap((tr) => directChildren(tr, 'tc'));
|
|
287
|
+
const setTableCellBorders = (cell, value) => {
|
|
288
|
+
const doc = cell.ownerDocument;
|
|
289
|
+
if (!doc)
|
|
290
|
+
throw new Error('Element has no document');
|
|
291
|
+
const tcPr = ensureTcPr(cell);
|
|
292
|
+
for (const child of [...children(tcPr)])
|
|
293
|
+
if (child.localName && TABLE_CELL_BORDER_SIDES.includes(child.localName))
|
|
294
|
+
tcPr.removeChild(child);
|
|
295
|
+
if (isNone(value)) {
|
|
296
|
+
for (const side of TABLE_CELL_BORDER_SIDES) {
|
|
297
|
+
const ln = doc.createElementNS(NS.a, `a:${side}`);
|
|
298
|
+
ln.appendChild(doc.createElementNS(NS.a, 'a:noFill'));
|
|
299
|
+
insertTcPrBorder(tcPr, ln);
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let color = '0000FF';
|
|
304
|
+
let widthPt = DEFAULT_STROKE_PT;
|
|
305
|
+
let dash;
|
|
306
|
+
if (typeof value === 'string')
|
|
307
|
+
color = normalizeColor(value);
|
|
308
|
+
else if (typeof value === 'object' && value !== null) {
|
|
309
|
+
const record = value;
|
|
310
|
+
if (typeof record['color'] === 'string')
|
|
311
|
+
color = normalizeColor(record['color']);
|
|
312
|
+
else if (record['color'] !== undefined)
|
|
313
|
+
throw new Error('stroke.color must be a hex string');
|
|
314
|
+
const width = record['width'] ?? record['widthPt'];
|
|
315
|
+
if (typeof width === 'number' && Number.isFinite(width) && width > 0)
|
|
316
|
+
widthPt = width;
|
|
317
|
+
else if (width !== undefined)
|
|
318
|
+
throw new Error('stroke.width must be a positive number (pt)');
|
|
319
|
+
if (typeof record['dash'] === 'string')
|
|
320
|
+
dash = record['dash'];
|
|
321
|
+
else if (record['dash'] !== undefined)
|
|
322
|
+
throw new Error('stroke.dash must be a string');
|
|
323
|
+
}
|
|
324
|
+
else
|
|
325
|
+
throw new Error('Unsupported stroke value');
|
|
326
|
+
const widthEmu = String(lineWidthToEmu(widthPt));
|
|
327
|
+
for (const side of TABLE_CELL_BORDER_SIDES) {
|
|
328
|
+
const ln = doc.createElementNS(NS.a, `a:${side}`);
|
|
329
|
+
ln.setAttribute('w', widthEmu);
|
|
330
|
+
const solidFill = doc.createElementNS(NS.a, 'a:solidFill');
|
|
331
|
+
const srgb = doc.createElementNS(NS.a, 'a:srgbClr');
|
|
332
|
+
srgb.setAttribute('val', color);
|
|
333
|
+
solidFill.appendChild(srgb);
|
|
334
|
+
ln.appendChild(solidFill);
|
|
335
|
+
if (dash) {
|
|
336
|
+
const prstDash = doc.createElementNS(NS.a, 'a:prstDash');
|
|
337
|
+
prstDash.setAttribute('val', dash);
|
|
338
|
+
ln.appendChild(prstDash);
|
|
339
|
+
}
|
|
340
|
+
insertTcPrBorder(tcPr, ln);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
const TABLE_STRUCTURAL_KEYS = new Set([
|
|
344
|
+
'insertRow',
|
|
345
|
+
'deleteRow',
|
|
346
|
+
'insertColumn',
|
|
347
|
+
'deleteColumn',
|
|
348
|
+
'text',
|
|
349
|
+
'name',
|
|
350
|
+
]);
|
|
351
|
+
const TABLE_CELL_KEYS = new Set(['text', 'fill']);
|
|
352
|
+
export function applyTableProperties(node, properties, context) {
|
|
353
|
+
const structural = {};
|
|
354
|
+
const visual = {};
|
|
355
|
+
const unexpected = [];
|
|
356
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
357
|
+
if (TABLE_STRUCTURAL_KEYS.has(key))
|
|
358
|
+
structural[key] = value;
|
|
359
|
+
else if (SHAPE_KEY_SET.has(key))
|
|
360
|
+
visual[key] = value;
|
|
361
|
+
else
|
|
362
|
+
unexpected.push(key);
|
|
363
|
+
}
|
|
364
|
+
if (unexpected.length)
|
|
365
|
+
return err('INVALID_COMMAND', `Unsupported table setProperties keys: ${unexpected.join(', ')}`);
|
|
366
|
+
const applied = [];
|
|
367
|
+
try {
|
|
368
|
+
if ('insertRow' in structural) {
|
|
369
|
+
insertTableRow(node, structural['insertRow']);
|
|
370
|
+
applied.push('insertRow');
|
|
371
|
+
}
|
|
372
|
+
if ('deleteRow' in structural) {
|
|
373
|
+
deleteTableRow(node, structural['deleteRow']);
|
|
374
|
+
applied.push('deleteRow');
|
|
375
|
+
}
|
|
376
|
+
if ('insertColumn' in structural) {
|
|
377
|
+
insertTableColumn(node, structural['insertColumn']);
|
|
378
|
+
applied.push('insertColumn');
|
|
379
|
+
}
|
|
380
|
+
if ('deleteColumn' in structural) {
|
|
381
|
+
deleteTableColumn(node, structural['deleteColumn']);
|
|
382
|
+
applied.push('deleteColumn');
|
|
383
|
+
}
|
|
384
|
+
if (typeof structural['text'] === 'string') {
|
|
385
|
+
setNodeText(node, structural['text']);
|
|
386
|
+
applied.push('text');
|
|
387
|
+
}
|
|
388
|
+
else if (structural['text'] !== undefined)
|
|
389
|
+
throw new Error('text must be a string');
|
|
390
|
+
if (typeof structural['name'] === 'string') {
|
|
391
|
+
const pr = first(node, 'cNvPr');
|
|
392
|
+
if (!pr)
|
|
393
|
+
throw new Error('Element has no cNvPr to set name');
|
|
394
|
+
pr.setAttribute('name', structural['name']);
|
|
395
|
+
applied.push('name');
|
|
396
|
+
}
|
|
397
|
+
else if (structural['name'] !== undefined)
|
|
398
|
+
throw new Error('name must be a string');
|
|
399
|
+
// Table fill/line map onto cells — graphicFrame has no spPr.
|
|
400
|
+
if ('fill' in visual) {
|
|
401
|
+
const cells = tableCells(node);
|
|
402
|
+
if (cells.length === 0)
|
|
403
|
+
throw new Error('Table has no cells to fill');
|
|
404
|
+
for (const cell of cells)
|
|
405
|
+
setTableCellFill(cell, visual['fill']);
|
|
406
|
+
applied.push('fill');
|
|
407
|
+
delete visual['fill'];
|
|
408
|
+
}
|
|
409
|
+
const strokeKeys = STROKE_ALIASES.filter((key) => key in visual);
|
|
410
|
+
if (strokeKeys.length > 1)
|
|
411
|
+
throw new Error(`Use only one stroke alias; found: ${strokeKeys.join(', ')}`);
|
|
412
|
+
if (strokeKeys[0]) {
|
|
413
|
+
const key = strokeKeys[0];
|
|
414
|
+
const cells = tableCells(node);
|
|
415
|
+
if (cells.length === 0)
|
|
416
|
+
throw new Error('Table has no cells for stroke');
|
|
417
|
+
for (const cell of cells)
|
|
418
|
+
setTableCellBorders(cell, visual[key]);
|
|
419
|
+
applied.push(key);
|
|
420
|
+
delete visual[key];
|
|
421
|
+
}
|
|
422
|
+
if (Object.keys(visual).length > 0) {
|
|
423
|
+
const styled = applyShapeProperties(node, visual, context);
|
|
424
|
+
if (!styled.ok)
|
|
425
|
+
return styled;
|
|
426
|
+
applied.push(...styled.value.applied);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch (cause) {
|
|
430
|
+
return err('INVALID_COMMAND', cause instanceof Error ? cause.message : 'Invalid table properties');
|
|
431
|
+
}
|
|
432
|
+
return ok({ applied });
|
|
433
|
+
}
|
|
434
|
+
export function applyTableCellProperties(node, properties) {
|
|
435
|
+
const unexpected = Object.keys(properties).filter((k) => !TABLE_CELL_KEYS.has(k));
|
|
436
|
+
if (unexpected.length)
|
|
437
|
+
return err('INVALID_COMMAND', `Unsupported tableCell setProperties keys: ${unexpected.join(', ')}`);
|
|
438
|
+
const applied = [];
|
|
439
|
+
try {
|
|
440
|
+
if (typeof properties['text'] === 'string') {
|
|
441
|
+
setNodeText(node, properties['text']);
|
|
442
|
+
applied.push('text');
|
|
443
|
+
}
|
|
444
|
+
else if (properties['text'] !== undefined)
|
|
445
|
+
throw new Error('text must be a string');
|
|
446
|
+
if ('fill' in properties) {
|
|
447
|
+
setTableCellFill(node, properties['fill']);
|
|
448
|
+
applied.push('fill');
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
catch (cause) {
|
|
452
|
+
return err('INVALID_COMMAND', cause instanceof Error ? cause.message : 'Invalid tableCell properties');
|
|
453
|
+
}
|
|
454
|
+
return ok({ applied });
|
|
455
|
+
}
|
|
456
|
+
//# sourceMappingURL=table.js.map
|