ooxml.js 3.0.1 → 3.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.
@@ -0,0 +1,675 @@
1
+ import { el, txt } from "../../xml/fragment.js";
2
+ import { encodeXmlText } from "../../xml/entities.js";
3
+ import { ptToEighthPoints, ptToEmu, ptToHalfPoints, ptToTwips } from "../shared/units.js";
4
+ import { TABLE_OF_CONTENTS_GALLERY, isDeletedChange } from "./constructs.js";
5
+ import { colorToRgbHex, findConstructMarkerImbalance } from "document-schema.js";
6
+ //#region src/typed/docx/write.ts
7
+ const WML_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
8
+ const REL_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
9
+ const PKG_RELS_NS = "http://schemas.openxmlformats.org/package/2006/relationships";
10
+ const CONTENT_TYPES_NS = "http://schemas.openxmlformats.org/package/2006/content-types";
11
+ const CORE_PROPS_NS = "http://schemas.openxmlformats.org/package/2006/metadata/core-properties";
12
+ const DC_NS = "http://purl.org/dc/elements/1.1/";
13
+ const DCTERMS_NS = "http://purl.org/dc/terms/";
14
+ const XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
15
+ const EXTENDED_PROPS_NS = "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties";
16
+ const DRAWINGML_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
17
+ const DRAWING_WP_NS = "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing";
18
+ const DRAWING_PIC_NS = "http://schemas.openxmlformats.org/drawingml/2006/picture";
19
+ const MARKUP_COMPAT_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006";
20
+ const W14_NS = "http://schemas.microsoft.com/office/word/2010/wordml";
21
+ const W15_NS = "http://schemas.microsoft.com/office/word/2012/wordml";
22
+ const CT_DOCUMENT = "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml";
23
+ const CT_CORE_PROPS = "application/vnd.openxmlformats-package.core-properties+xml";
24
+ const CT_EXTENDED_PROPS = "application/vnd.openxmlformats-officedocument.extended-properties+xml";
25
+ const REL_OFFICE_DOCUMENT = `${REL_NS}/officeDocument`;
26
+ const REL_CORE_PROPS = `${PKG_RELS_NS}/metadata/core-properties`;
27
+ const REL_EXTENDED_PROPS = `${REL_NS}/extended-properties`;
28
+ const REL_HYPERLINK = `${REL_NS}/hyperlink`;
29
+ const REL_IMAGE = `${REL_NS}/image`;
30
+ const DOCUMENT_PART_PATH = "word/document.xml";
31
+ function newWriteState() {
32
+ return {
33
+ relationships: [],
34
+ hyperlinkIds: /* @__PURE__ */ new Map(),
35
+ mediaIds: /* @__PURE__ */ new Map(),
36
+ mediaParts: /* @__PURE__ */ new Map(),
37
+ nextDrawingId: 1,
38
+ nextMarkerId: 1
39
+ };
40
+ }
41
+ function addRelationship(state, type, target, external) {
42
+ const id = `rId${state.relationships.length + 1}`;
43
+ state.relationships.push({
44
+ id,
45
+ type,
46
+ target,
47
+ external
48
+ });
49
+ return id;
50
+ }
51
+ function hyperlinkRelationshipId(state, uri) {
52
+ const existing = state.hyperlinkIds.get(uri);
53
+ if (existing !== void 0) return existing;
54
+ const id = addRelationship(state, REL_HYPERLINK, encodeXmlText(uri), true);
55
+ state.hyperlinkIds.set(uri, id);
56
+ return id;
57
+ }
58
+ function imageRelationshipId(state, image) {
59
+ const key = `${image.format}:${image.base64}`;
60
+ const existing = state.mediaIds.get(key);
61
+ if (existing !== void 0) return existing;
62
+ const name = `image${state.mediaParts.size + 1}.${image.format === "png" ? "png" : "jpeg"}`;
63
+ state.mediaParts.set(name, {
64
+ format: image.format,
65
+ base64: image.base64
66
+ });
67
+ const id = addRelationship(state, REL_IMAGE, `media/${name}`, false);
68
+ state.mediaIds.set(key, id);
69
+ return id;
70
+ }
71
+ function xmlDeclaration() {
72
+ return {
73
+ type: "declaration",
74
+ attributes: [
75
+ {
76
+ name: "version",
77
+ value: "1.0"
78
+ },
79
+ {
80
+ name: "encoding",
81
+ value: "UTF-8"
82
+ },
83
+ {
84
+ name: "standalone",
85
+ value: "yes"
86
+ }
87
+ ]
88
+ };
89
+ }
90
+ function xmlPart(root) {
91
+ return {
92
+ kind: "xml",
93
+ nodes: [xmlDeclaration(), root]
94
+ };
95
+ }
96
+ function toggleElement(tag, value) {
97
+ return el(tag, { "w:val": value ? "1" : "0" });
98
+ }
99
+ function buildRunProperties(run) {
100
+ const children = [];
101
+ if (run.fontFamily !== void 0) {
102
+ const font = encodeXmlText(run.fontFamily);
103
+ children.push(el("w:rFonts", {
104
+ "w:ascii": font,
105
+ "w:hAnsi": font
106
+ }));
107
+ }
108
+ if (run.bold !== void 0) children.push(toggleElement("w:b", run.bold));
109
+ if (run.italic !== void 0) children.push(toggleElement("w:i", run.italic));
110
+ if (run.strike !== void 0) children.push(toggleElement("w:strike", run.strike));
111
+ if (run.color !== void 0) children.push(el("w:color", { "w:val": colorToRgbHex(run.color) }));
112
+ if (run.sizePt !== void 0) children.push(el("w:sz", { "w:val": String(ptToHalfPoints(run.sizePt)) }));
113
+ if (run.underline !== void 0) children.push(el("w:u", { "w:val": run.underline ? "single" : "none" }));
114
+ return children.length === 0 ? void 0 : el("w:rPr", {}, children);
115
+ }
116
+ function buildRunContent(text, deleted) {
117
+ const textTag = deleted ? "w:delText" : "w:t";
118
+ const children = [];
119
+ for (const piece of text.split(/(\t|\n)/)) if (piece === " ") children.push(el("w:tab"));
120
+ else if (piece === "\n") children.push(el("w:br"));
121
+ else if (piece.length > 0) children.push(el(textTag, { "xml:space": "preserve" }, [txt(encodeXmlText(piece))]));
122
+ if (children.length === 0) children.push(el(textTag, { "xml:space": "preserve" }));
123
+ return children;
124
+ }
125
+ function buildRun(run, state, deleted) {
126
+ const rPr = buildRunProperties(run);
127
+ const runElement = el("w:r", {}, [...rPr === void 0 ? [] : [rPr], ...buildRunContent(run.text, deleted)]);
128
+ if (run.hyperlink === void 0) return runElement;
129
+ return el("w:hyperlink", { "r:id": hyperlinkRelationshipId(state, run.hyperlink) }, [runElement]);
130
+ }
131
+ const JUSTIFICATION_BY_ALIGNMENT = {
132
+ left: "left",
133
+ center: "center",
134
+ right: "right",
135
+ justify: "both"
136
+ };
137
+ function buildParagraphProperties(paragraph, pageBreakBefore) {
138
+ const children = [];
139
+ if (paragraph.styleId !== void 0) children.push(el("w:pStyle", { "w:val": encodeXmlText(paragraph.styleId) }));
140
+ if (pageBreakBefore) children.push(el("w:pageBreakBefore"));
141
+ if (paragraph.list !== void 0) {
142
+ const numPrChildren = [el("w:ilvl", { "w:val": String(paragraph.list.level) })];
143
+ if (paragraph.list.numId !== void 0) numPrChildren.push(el("w:numId", { "w:val": encodeXmlText(paragraph.list.numId) }));
144
+ children.push(el("w:numPr", {}, numPrChildren));
145
+ }
146
+ const spacing = {};
147
+ if (paragraph.spacingBeforePt !== void 0) spacing["w:before"] = String(ptToTwips(paragraph.spacingBeforePt));
148
+ if (paragraph.spacingAfterPt !== void 0) spacing["w:after"] = String(ptToTwips(paragraph.spacingAfterPt));
149
+ if (paragraph.lineSpacing !== void 0) {
150
+ spacing["w:line"] = String(Math.round(paragraph.lineSpacing * LINE_UNITS_PER_LINE));
151
+ spacing["w:lineRule"] = "auto";
152
+ }
153
+ if (Object.keys(spacing).length > 0) children.push(el("w:spacing", spacing));
154
+ const indent = {};
155
+ if (paragraph.indentLeftPt !== void 0) indent["w:left"] = String(ptToTwips(paragraph.indentLeftPt));
156
+ if (paragraph.indentFirstLinePt !== void 0) if (paragraph.indentFirstLinePt < 0) indent["w:hanging"] = String(ptToTwips(-paragraph.indentFirstLinePt));
157
+ else indent["w:firstLine"] = String(ptToTwips(paragraph.indentFirstLinePt));
158
+ if (Object.keys(indent).length > 0) children.push(el("w:ind", indent));
159
+ if (paragraph.alignment !== void 0) children.push(el("w:jc", { "w:val": JUSTIFICATION_BY_ALIGNMENT[paragraph.alignment] }));
160
+ if (paragraph.headingLevel !== void 0) children.push(el("w:outlineLvl", { "w:val": String(paragraph.headingLevel - 1) }));
161
+ return children.length === 0 ? void 0 : el("w:pPr", {}, children);
162
+ }
163
+ const LINE_UNITS_PER_LINE = 240;
164
+ function buildParagraph(paragraph, state, pageBreakBefore, deleted, provenance) {
165
+ const properties = buildParagraphProperties(paragraph, pageBreakBefore);
166
+ const changeTag = provenance === void 0 ? void 0 : TRACKED_CHANGE_TAG_BY_CHANGE[provenance.change];
167
+ const pPr = properties === void 0 && changeTag !== void 0 ? el("w:pPr", {}, []) : properties;
168
+ if (pPr !== void 0 && changeTag !== void 0 && provenance !== void 0) pPr.children.push(el("w:rPr", {}, [el(changeTag, trackChangeAttrs(state, provenance))]));
169
+ const runs = paragraph.runs.map((run) => buildRun(run, state, deleted));
170
+ const content = changeTag === void 0 || provenance === void 0 ? runs : [el(changeTag, trackChangeAttrs(state, provenance), runs)];
171
+ return el("w:p", {}, [...pPr === void 0 ? [] : [pPr], ...content]);
172
+ }
173
+ function trailingEmptyRunElements(paragraph, element) {
174
+ const runElements = [];
175
+ for (const child of element.children) if (child.type === "element" && (child.tag === "w:r" || child.tag === "w:hyperlink")) runElements.push(child);
176
+ const trailing = [];
177
+ for (let index = paragraph.runs.length - 1; index >= 0; index--) {
178
+ const run = paragraph.runs[index];
179
+ const runElement = runElements[index];
180
+ if (run === void 0 || runElement === void 0 || run.text !== "" || run.hyperlink !== void 0 || runElement.tag !== "w:r") break;
181
+ trailing.unshift(runElement);
182
+ }
183
+ return trailing;
184
+ }
185
+ function buildCellBorders(borders) {
186
+ const edges = [];
187
+ const edge = (tag, border) => {
188
+ if (border === void 0) return;
189
+ const style = border.style ?? "solid";
190
+ edges.push(el(tag, {
191
+ "w:val": STROKE_STYLE_KEYWORD[style],
192
+ "w:sz": String(ptToEighthPoints(border.widthPt)),
193
+ "w:color": colorToRgbHex(border.color)
194
+ }));
195
+ };
196
+ edge("w:top", borders.top);
197
+ edge("w:left", borders.left);
198
+ edge("w:bottom", borders.bottom);
199
+ edge("w:right", borders.right);
200
+ return el("w:tcBorders", {}, edges);
201
+ }
202
+ const STROKE_STYLE_KEYWORD = {
203
+ solid: "single",
204
+ dashed: "dashed",
205
+ dotted: "dotted",
206
+ double: "double"
207
+ };
208
+ function buildCell(cell, state, deleted, gridSpan, vMerge) {
209
+ const tcPrChildren = [];
210
+ if (gridSpan > 1) tcPrChildren.push(el("w:gridSpan", { "w:val": String(gridSpan) }));
211
+ if (vMerge === "restart") tcPrChildren.push(el("w:vMerge", { "w:val": "restart" }));
212
+ else if (vMerge === "continue") tcPrChildren.push(el("w:vMerge"));
213
+ if (cell.background !== void 0) tcPrChildren.push(el("w:shd", {
214
+ "w:val": "clear",
215
+ "w:color": "auto",
216
+ "w:fill": colorToRgbHex(cell.background)
217
+ }));
218
+ if (cell.borders !== void 0) tcPrChildren.push(buildCellBorders(cell.borders));
219
+ const content = buildBlockFlow(cell.blocks, state, deleted);
220
+ const body = content.length === 0 ? [el("w:p")] : content;
221
+ return el("w:tc", {}, [...tcPrChildren.length === 0 ? [] : [el("w:tcPr", {}, tcPrChildren)], ...body]);
222
+ }
223
+ function buildTable(table, state, deleted) {
224
+ const grid = el("w:tblGrid", {}, table.columnWidthsPt.map((widthPt) => el("w:gridCol", { "w:w": String(ptToTwips(widthPt)) })));
225
+ const active = /* @__PURE__ */ new Map();
226
+ const rows = table.rows.map((row) => {
227
+ const cells = [];
228
+ let column = 0;
229
+ for (const cell of row.cells) {
230
+ const covered = active.get(column);
231
+ if (covered !== void 0 && covered.remaining > 0) {
232
+ covered.remaining--;
233
+ cells.push(buildCell(cell, state, deleted, covered.span, "continue"));
234
+ column += covered.span;
235
+ continue;
236
+ }
237
+ const gridSpan = cell.colSpan ?? 1;
238
+ const rowSpan = cell.rowSpan ?? 1;
239
+ cells.push(buildCell(cell, state, deleted, gridSpan, rowSpan > 1 ? "restart" : void 0));
240
+ if (rowSpan > 1) active.set(column, {
241
+ remaining: rowSpan - 1,
242
+ span: gridSpan
243
+ });
244
+ column += gridSpan;
245
+ }
246
+ const trPr = row.heightPt === void 0 ? void 0 : el("w:trPr", {}, [el("w:trHeight", { "w:val": String(ptToTwips(row.heightPt)) })]);
247
+ return el("w:tr", {}, [...trPr === void 0 ? [] : [trPr], ...cells]);
248
+ });
249
+ const tblPr = el("w:tblPr", {}, [el("w:tblW", {
250
+ "w:w": "0",
251
+ "w:type": "auto"
252
+ })]);
253
+ return el("w:tbl", {}, [
254
+ tblPr,
255
+ grid,
256
+ ...rows
257
+ ]);
258
+ }
259
+ function buildDrawing(image, state) {
260
+ const relId = imageRelationshipId(state, image);
261
+ const drawingId = state.nextDrawingId++;
262
+ const cx = String(ptToEmu(image.widthPt));
263
+ const cy = String(ptToEmu(image.heightPt));
264
+ const docPrAttrs = {
265
+ id: String(drawingId),
266
+ name: `Picture ${String(drawingId)}`
267
+ };
268
+ if (image.altText !== void 0) docPrAttrs.descr = encodeXmlText(image.altText);
269
+ const picture = el("pic:pic", { "xmlns:pic": DRAWING_PIC_NS }, [
270
+ el("pic:nvPicPr", {}, [el("pic:cNvPr", {
271
+ id: String(drawingId),
272
+ name: `Picture ${String(drawingId)}`
273
+ }), el("pic:cNvPicPr")]),
274
+ el("pic:blipFill", {}, [el("a:blip", { "r:embed": relId }), el("a:stretch", {}, [el("a:fillRect")])]),
275
+ el("pic:spPr", {}, [el("a:xfrm", {}, [el("a:off", {
276
+ x: "0",
277
+ y: "0"
278
+ }), el("a:ext", {
279
+ cx,
280
+ cy
281
+ })]), el("a:prstGeom", { prst: "rect" }, [el("a:avLst")])])
282
+ ]);
283
+ return el("w:drawing", {}, [el("wp:inline", {
284
+ distT: "0",
285
+ distB: "0",
286
+ distL: "0",
287
+ distR: "0"
288
+ }, [
289
+ el("wp:extent", {
290
+ cx,
291
+ cy
292
+ }),
293
+ el("wp:docPr", docPrAttrs),
294
+ el("a:graphic", { "xmlns:a": DRAWINGML_NS }, [el("a:graphicData", { uri: DRAWING_PIC_NS }, [picture])])
295
+ ])]);
296
+ }
297
+ const TRACKED_CHANGE_TAG_BY_CHANGE = {
298
+ insertion: "w:ins",
299
+ deletion: "w:del",
300
+ moveFrom: "w:moveFrom",
301
+ moveTo: "w:moveTo",
302
+ formatChange: void 0
303
+ };
304
+ const UNKNOWN_PROVENANCE_AUTHOR = "Unknown";
305
+ function trackChangeAttrs(state, descriptor) {
306
+ const attrs = {
307
+ "w:id": String(state.nextMarkerId++),
308
+ "w:author": encodeXmlText(descriptor.author ?? UNKNOWN_PROVENANCE_AUTHOR)
309
+ };
310
+ if (descriptor.dateIso !== void 0) attrs["w:date"] = encodeXmlText(descriptor.dateIso);
311
+ return attrs;
312
+ }
313
+ const SDT_TYPE_ELEMENT = {
314
+ richText: "w:richText",
315
+ plainText: "w:text",
316
+ checkbox: "w14:checkbox",
317
+ dropDown: "w:dropDownList",
318
+ comboBox: "w:comboBox",
319
+ date: "w:date",
320
+ picture: "w:picture",
321
+ repeatingSection: "w15:repeatingSection",
322
+ group: "w:group",
323
+ button: void 0,
324
+ index: void 0
325
+ };
326
+ const SDT_LOCK_VALUE = {
327
+ content: "contentLocked",
328
+ container: "sdtLocked",
329
+ both: "sdtContentLocked"
330
+ };
331
+ function buildSdtProperties(descriptor) {
332
+ const children = [];
333
+ if (descriptor.alias !== void 0) children.push(el("w:alias", { "w:val": encodeXmlText(descriptor.alias) }));
334
+ if (descriptor.tag !== void 0) children.push(el("w:tag", { "w:val": encodeXmlText(descriptor.tag) }));
335
+ if (descriptor.lock !== void 0) children.push(el("w:lock", { "w:val": SDT_LOCK_VALUE[descriptor.lock] }));
336
+ if (descriptor.controlType === "index") {
337
+ children.push(el("w:docPartObj", {}, [el("w:docPartGallery", { "w:val": TABLE_OF_CONTENTS_GALLERY }), el("w:docPartUnique")]));
338
+ return el("w:sdtPr", {}, children);
339
+ }
340
+ const options = descriptor.options ?? [];
341
+ if (descriptor.controlType === "dropDown" || descriptor.controlType === "comboBox") {
342
+ const items = options.map((option) => el("w:listItem", {
343
+ "w:displayText": encodeXmlText(option),
344
+ "w:value": encodeXmlText(option)
345
+ }));
346
+ children.push(el(descriptor.controlType === "dropDown" ? "w:dropDownList" : "w:comboBox", {}, items));
347
+ return el("w:sdtPr", {}, children);
348
+ }
349
+ if (descriptor.controlType === "checkbox") {
350
+ children.push(el("w14:checkbox", {}, [el("w14:checked", { "w14:val": descriptor.checked === true ? "1" : "0" })]));
351
+ return el("w:sdtPr", {}, children);
352
+ }
353
+ if (descriptor.controlType === "date") {
354
+ children.push(el("w:date", descriptor.value === void 0 ? {} : { "w:fullDate": encodeXmlText(descriptor.value) }));
355
+ return el("w:sdtPr", {}, children);
356
+ }
357
+ const typeTag = SDT_TYPE_ELEMENT[descriptor.controlType];
358
+ children.push(el(typeTag ?? "w:richText"));
359
+ return el("w:sdtPr", {}, children);
360
+ }
361
+ function parseFlow(blocks) {
362
+ const imbalance = findConstructMarkerImbalance(blocks);
363
+ if (imbalance !== void 0) throw new Error(`buildDocxPackage: construct markers do not balance (${imbalance.kind} at block ${String(imbalance.index)})`);
364
+ const roots = [];
365
+ const stack = [roots];
366
+ for (const block of blocks) {
367
+ const current = stack[stack.length - 1];
368
+ if (block.kind === "constructStart") {
369
+ const item = {
370
+ kind: "construct",
371
+ descriptor: block.descriptor,
372
+ children: []
373
+ };
374
+ current.push(item);
375
+ stack.push(item.children);
376
+ continue;
377
+ }
378
+ if (block.kind === "constructEnd") {
379
+ stack.pop();
380
+ continue;
381
+ }
382
+ current.push({
383
+ kind: "block",
384
+ block
385
+ });
386
+ }
387
+ return roots;
388
+ }
389
+ function findParagraph(nodes, last) {
390
+ const ordered = last ? [...nodes].reverse() : nodes;
391
+ for (const node of ordered) {
392
+ if (node.type !== "element") continue;
393
+ if (node.tag === "w:p") return node;
394
+ if (node.tag === "w:tbl") continue;
395
+ const nested = findParagraph(node.children, last);
396
+ if (nested !== void 0) return nested;
397
+ }
398
+ }
399
+ function fieldCharRun(type) {
400
+ return el("w:r", {}, [el("w:fldChar", { "w:fldCharType": type })]);
401
+ }
402
+ function fieldOpeningRuns(instruction) {
403
+ return [
404
+ fieldCharRun("begin"),
405
+ el("w:r", {}, [el("w:instrText", { "xml:space": "preserve" }, [txt(encodeXmlText(instruction))])]),
406
+ fieldCharRun("separate")
407
+ ];
408
+ }
409
+ function insertAfterProperties(paragraph, runs) {
410
+ const first = paragraph.children[0];
411
+ const offset = first?.type === "element" && first.tag === "w:pPr" ? 1 : 0;
412
+ paragraph.children.splice(offset, 0, ...runs);
413
+ }
414
+ function buildFieldNodes(instruction, content) {
415
+ const first = findParagraph(content, false);
416
+ const last = findParagraph(content, true);
417
+ if (first === void 0 || last === void 0) return [
418
+ el("w:p", {}, fieldOpeningRuns(instruction)),
419
+ ...content,
420
+ el("w:p", {}, [fieldCharRun("end")])
421
+ ];
422
+ insertAfterProperties(first, fieldOpeningRuns(instruction));
423
+ last.children.push(fieldCharRun("end"));
424
+ return content;
425
+ }
426
+ function buildConstructNodes(descriptor, children, state, deleted, provenance) {
427
+ if (descriptor.kind === "contentControl") return [el("w:sdt", {}, [buildSdtProperties(descriptor), el("w:sdtContent", {}, buildFlowItems(children, state, deleted, provenance))])];
428
+ if (descriptor.kind === "provenance") {
429
+ if (TRACKED_CHANGE_TAG_BY_CHANGE[descriptor.change] !== void 0) return buildFlowItems(children, state, deleted || isDeletedChange(descriptor.change), descriptor);
430
+ }
431
+ if (descriptor.kind === "anchor" && descriptor.anchorType === "bookmark") {
432
+ const id = String(state.nextMarkerId++);
433
+ return [
434
+ el("w:bookmarkStart", {
435
+ "w:id": id,
436
+ "w:name": encodeXmlText(descriptor.name)
437
+ }),
438
+ ...buildFlowItems(children, state, deleted, provenance),
439
+ el("w:bookmarkEnd", { "w:id": id })
440
+ ];
441
+ }
442
+ if (descriptor.kind === "field") return buildFieldNodes(descriptor.instruction, buildFlowItems(children, state, deleted, provenance));
443
+ return buildFlowItems(children, state, deleted, provenance);
444
+ }
445
+ function buildFlowItems(items, state, deleted, provenance) {
446
+ const nodes = [];
447
+ let pendingPageBreak = false;
448
+ let lastParagraph;
449
+ let availableImageRuns = [];
450
+ for (const item of items) {
451
+ if (item.kind === "construct") {
452
+ const constructChildren = pendingPageBreak ? [{
453
+ kind: "block",
454
+ block: { kind: "pageBreak" }
455
+ }, ...item.children] : item.children;
456
+ nodes.push(...buildConstructNodes(item.descriptor, constructChildren, state, deleted, provenance));
457
+ pendingPageBreak = false;
458
+ lastParagraph = void 0;
459
+ availableImageRuns = [];
460
+ continue;
461
+ }
462
+ const block = item.block;
463
+ if (block.kind === "pageBreak") {
464
+ pendingPageBreak = true;
465
+ continue;
466
+ }
467
+ if (block.kind === "paragraph") {
468
+ const paragraph = buildParagraph(block, state, pendingPageBreak, deleted, provenance);
469
+ pendingPageBreak = false;
470
+ lastParagraph = paragraph;
471
+ availableImageRuns = trailingEmptyRunElements(block, paragraph);
472
+ nodes.push(paragraph);
473
+ continue;
474
+ }
475
+ if (block.kind === "image") {
476
+ if (pendingPageBreak) {
477
+ const breakParagraph = el("w:p", {}, [el("w:pPr", {}, [el("w:pageBreakBefore")])]);
478
+ nodes.push(breakParagraph);
479
+ lastParagraph = breakParagraph;
480
+ availableImageRuns = [];
481
+ pendingPageBreak = false;
482
+ }
483
+ const drawing = buildDrawing(block, state);
484
+ const reusable = availableImageRuns.shift();
485
+ if (reusable !== void 0) reusable.children.push(drawing);
486
+ else if (lastParagraph !== void 0) lastParagraph.children.push(el("w:r", {}, [drawing]));
487
+ else {
488
+ const paragraph = el("w:p", {}, [el("w:r", {}, [drawing])]);
489
+ lastParagraph = paragraph;
490
+ nodes.push(paragraph);
491
+ }
492
+ continue;
493
+ }
494
+ if (block.kind === "table") {
495
+ if (pendingPageBreak) {
496
+ nodes.push(el("w:p", {}, [el("w:pPr", {}, [el("w:pageBreakBefore")])]));
497
+ pendingPageBreak = false;
498
+ }
499
+ nodes.push(buildTable(block, state, deleted));
500
+ lastParagraph = void 0;
501
+ availableImageRuns = [];
502
+ continue;
503
+ }
504
+ lastParagraph = void 0;
505
+ availableImageRuns = [];
506
+ }
507
+ if (pendingPageBreak) nodes.push(el("w:p", {}, [el("w:pPr", {}, [el("w:pageBreakBefore")])]));
508
+ return nodes;
509
+ }
510
+ function buildBlockFlow(blocks, state, deleted) {
511
+ return buildFlowItems(parseFlow(blocks), state, deleted, void 0);
512
+ }
513
+ function buildSectionProperties(section) {
514
+ return el("w:sectPr", {}, [el("w:pgSz", {
515
+ "w:w": String(ptToTwips(section.pageSize.widthPt)),
516
+ "w:h": String(ptToTwips(section.pageSize.heightPt))
517
+ }), el("w:pgMar", {
518
+ "w:top": String(ptToTwips(section.margins.topPt)),
519
+ "w:right": String(ptToTwips(section.margins.rightPt)),
520
+ "w:bottom": String(ptToTwips(section.margins.bottomPt)),
521
+ "w:left": String(ptToTwips(section.margins.leftPt))
522
+ })]);
523
+ }
524
+ function attachSectionBreak(nodes, section) {
525
+ const target = findParagraph(nodes, true);
526
+ if (target === void 0) {
527
+ nodes.push(el("w:p", {}, [el("w:pPr", {}, [buildSectionProperties(section)])]));
528
+ return;
529
+ }
530
+ const first = target.children[0];
531
+ if (first?.type === "element" && first.tag === "w:pPr") {
532
+ first.children.push(buildSectionProperties(section));
533
+ return;
534
+ }
535
+ target.children.unshift(el("w:pPr", {}, [buildSectionProperties(section)]));
536
+ }
537
+ function buildDocumentPart(sections, state) {
538
+ const bodyChildren = [];
539
+ sections.forEach((section, index) => {
540
+ const nodes = buildBlockFlow(section.blocks, state, false);
541
+ if (index === sections.length - 1) {
542
+ bodyChildren.push(...nodes, buildSectionProperties(section));
543
+ return;
544
+ }
545
+ attachSectionBreak(nodes, section);
546
+ bodyChildren.push(...nodes);
547
+ });
548
+ return xmlPart(el("w:document", {
549
+ "xmlns:w": WML_NS,
550
+ "xmlns:r": REL_NS,
551
+ "xmlns:a": DRAWINGML_NS,
552
+ "xmlns:wp": DRAWING_WP_NS,
553
+ "xmlns:pic": DRAWING_PIC_NS,
554
+ "xmlns:mc": MARKUP_COMPAT_NS,
555
+ "xmlns:w14": W14_NS,
556
+ "xmlns:w15": W15_NS,
557
+ "mc:Ignorable": "w14 w15"
558
+ }, [el("w:body", {}, bodyChildren)]));
559
+ }
560
+ function buildContentTypesPart(mediaFormats) {
561
+ const defaults = [el("Default", {
562
+ Extension: "rels",
563
+ ContentType: "application/vnd.openxmlformats-package.relationships+xml"
564
+ }), el("Default", {
565
+ Extension: "xml",
566
+ ContentType: "application/xml"
567
+ })];
568
+ if (mediaFormats.has("png")) defaults.push(el("Default", {
569
+ Extension: "png",
570
+ ContentType: "image/png"
571
+ }));
572
+ if (mediaFormats.has("jpeg")) defaults.push(el("Default", {
573
+ Extension: "jpeg",
574
+ ContentType: "image/jpeg"
575
+ }));
576
+ return xmlPart(el("Types", { xmlns: CONTENT_TYPES_NS }, [
577
+ ...defaults,
578
+ el("Override", {
579
+ PartName: `/${DOCUMENT_PART_PATH}`,
580
+ ContentType: CT_DOCUMENT
581
+ }),
582
+ el("Override", {
583
+ PartName: "/docProps/core.xml",
584
+ ContentType: CT_CORE_PROPS
585
+ }),
586
+ el("Override", {
587
+ PartName: "/docProps/app.xml",
588
+ ContentType: CT_EXTENDED_PROPS
589
+ })
590
+ ]));
591
+ }
592
+ function buildPackageRelsPart() {
593
+ return xmlPart(el("Relationships", { xmlns: PKG_RELS_NS }, [
594
+ el("Relationship", {
595
+ Id: "rId1",
596
+ Type: REL_OFFICE_DOCUMENT,
597
+ Target: DOCUMENT_PART_PATH
598
+ }),
599
+ el("Relationship", {
600
+ Id: "rId2",
601
+ Type: REL_CORE_PROPS,
602
+ Target: "docProps/core.xml"
603
+ }),
604
+ el("Relationship", {
605
+ Id: "rId3",
606
+ Type: REL_EXTENDED_PROPS,
607
+ Target: "docProps/app.xml"
608
+ })
609
+ ]));
610
+ }
611
+ function buildDocumentRelsPart(state) {
612
+ const relationships = state.relationships.map((rel) => el("Relationship", rel.external ? {
613
+ Id: rel.id,
614
+ Type: rel.type,
615
+ Target: rel.target,
616
+ TargetMode: "External"
617
+ } : {
618
+ Id: rel.id,
619
+ Type: rel.type,
620
+ Target: rel.target
621
+ }));
622
+ return xmlPart(el("Relationships", { xmlns: PKG_RELS_NS }, relationships));
623
+ }
624
+ function buildCorePropertiesPart(metadata) {
625
+ const children = [];
626
+ if (metadata.title !== void 0) children.push(el("dc:title", {}, [txt(encodeXmlText(metadata.title))]));
627
+ if (metadata.author !== void 0) children.push(el("dc:creator", {}, [txt(encodeXmlText(metadata.author))]));
628
+ if (metadata.subject !== void 0) children.push(el("dc:subject", {}, [txt(encodeXmlText(metadata.subject))]));
629
+ if (metadata.keywords !== void 0 && metadata.keywords.length > 0) children.push(el("cp:keywords", {}, [txt(encodeXmlText(metadata.keywords.join(", ")))]));
630
+ if (metadata.createdIso !== void 0) children.push(el("dcterms:created", { "xsi:type": "dcterms:W3CDTF" }, [txt(encodeXmlText(metadata.createdIso))]));
631
+ if (metadata.modifiedIso !== void 0) children.push(el("dcterms:modified", { "xsi:type": "dcterms:W3CDTF" }, [txt(encodeXmlText(metadata.modifiedIso))]));
632
+ return xmlPart(el("cp:coreProperties", {
633
+ "xmlns:cp": CORE_PROPS_NS,
634
+ "xmlns:dc": DC_NS,
635
+ "xmlns:dcterms": DCTERMS_NS,
636
+ "xmlns:xsi": XSI_NS
637
+ }, children));
638
+ }
639
+ function buildExtendedPropertiesPart(metadata) {
640
+ const children = [];
641
+ if (metadata.creator !== void 0) children.push(el("Application", {}, [txt(encodeXmlText(metadata.creator))]));
642
+ return xmlPart(el("Properties", { xmlns: EXTENDED_PROPS_NS }, children));
643
+ }
644
+ function buildDocxPackage(content) {
645
+ const state = newWriteState();
646
+ const documentPart = buildDocumentPart(content.sections.length === 0 ? [{
647
+ pageSize: {
648
+ widthPt: 612,
649
+ heightPt: 792
650
+ },
651
+ margins: {
652
+ topPt: 72,
653
+ rightPt: 72,
654
+ bottomPt: 72,
655
+ leftPt: 72
656
+ },
657
+ blocks: []
658
+ }] : content.sections, state);
659
+ const metadata = content.metadata ?? {};
660
+ const parts = {
661
+ "[Content_Types].xml": buildContentTypesPart(new Set([...state.mediaParts.values()].map((media) => media.format))),
662
+ "_rels/.rels": buildPackageRelsPart(),
663
+ [DOCUMENT_PART_PATH]: documentPart,
664
+ "word/_rels/document.xml.rels": buildDocumentRelsPart(state),
665
+ "docProps/core.xml": buildCorePropertiesPart(metadata),
666
+ "docProps/app.xml": buildExtendedPropertiesPart(metadata)
667
+ };
668
+ for (const [name, media] of state.mediaParts) parts[`word/media/${name}`] = {
669
+ kind: "binary",
670
+ base64: media.base64
671
+ };
672
+ return { parts };
673
+ }
674
+ //#endregion
675
+ export { buildDocxPackage };
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  //#region src/typed/shared/source-path.ts
3
3
  function assignSourcePaths(blocks, prefix) {
4
4
  blocks.forEach((block, blockIndex) => {
5
+ if (block.kind === "constructStart" || block.kind === "constructEnd") return;
5
6
  const blockPath = `${prefix}.blocks[${blockIndex}]`;
6
7
  block.sourcePath = blockPath;
7
8
  if (block.kind === "paragraph") block.runs.forEach((run, runIndex) => {
@@ -1,6 +1,7 @@
1
1
  //#region src/typed/shared/source-path.ts
2
2
  function assignSourcePaths(blocks, prefix) {
3
3
  blocks.forEach((block, blockIndex) => {
4
+ if (block.kind === "constructStart" || block.kind === "constructEnd") return;
4
5
  const blockPath = `${prefix}.blocks[${blockIndex}]`;
5
6
  block.sourcePath = blockPath;
6
7
  if (block.kind === "paragraph") block.runs.forEach((run, runIndex) => {