reamkit 1.0.0 → 1.0.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.
Files changed (57) hide show
  1. package/README.md +4 -3
  2. package/dist/esm/core/bidi/index.d.ts +2 -0
  3. package/dist/esm/core/bidi/index.js +1 -0
  4. package/dist/esm/core/bidi/segments.d.ts +6 -0
  5. package/dist/esm/core/bidi/segments.js +41 -0
  6. package/dist/esm/core/converter/facade.js +1 -1
  7. package/dist/esm/core/converter/project.js +3 -2
  8. package/dist/esm/core/converter/ream.js +2 -1
  9. package/dist/esm/core/font/index.d.ts +2 -0
  10. package/dist/esm/core/font/measure.d.ts +7 -0
  11. package/dist/esm/core/font/measure.js +30 -0
  12. package/dist/esm/core/images.d.ts +17 -0
  13. package/dist/esm/core/images.js +308 -0
  14. package/dist/esm/core/line-breaker/index.d.ts +1 -1
  15. package/dist/esm/core/line-breaker/knuth-plass.d.ts +1 -0
  16. package/dist/esm/core/line-breaker/knuth-plass.js +2 -2
  17. package/dist/esm/core/numbering/apply.d.ts +3 -0
  18. package/dist/esm/core/numbering/apply.js +62 -0
  19. package/dist/esm/core/numbering/index.d.ts +1 -0
  20. package/dist/esm/core/opc/package.d.ts +2 -1
  21. package/dist/esm/core/opc/package.js +6 -2
  22. package/dist/esm/core/style-cascade/index.d.ts +1 -1
  23. package/dist/esm/core/style-cascade/resolver.d.ts +3 -1
  24. package/dist/esm/core/style-cascade/resolver.js +69 -2
  25. package/dist/esm/core/style-cascade/types.d.ts +2 -1
  26. package/dist/esm/core/vector.d.ts +45 -0
  27. package/dist/esm/core/vector.js +48 -0
  28. package/dist/esm/excel/xlsx-reader.js +2 -2
  29. package/dist/esm/index.d.ts +3 -3
  30. package/dist/esm/index.js +2 -1
  31. package/dist/esm/{pdf → layout}/arc-to-bezier.d.ts +1 -1
  32. package/dist/esm/{pdf → layout}/arc-to-bezier.js +2 -2
  33. package/dist/esm/{pdf → layout}/math-layout.d.ts +1 -1
  34. package/dist/esm/{pdf → layout}/math-layout.js +1 -1
  35. package/dist/esm/layout/page-doc.d.ts +133 -0
  36. package/dist/esm/layout/page-doc.js +38 -0
  37. package/dist/esm/{pdf → layout}/preset-geometry.d.ts +1 -1
  38. package/dist/esm/{pdf → layout}/preset-geometry.js +2 -2
  39. package/dist/esm/layout/styled-layout.d.ts +84 -0
  40. package/dist/esm/layout/styled-layout.js +1729 -0
  41. package/dist/esm/pdf/cid-font.d.ts +0 -6
  42. package/dist/esm/pdf/cid-font.js +2 -28
  43. package/dist/esm/pdf/image-xobject.d.ts +3 -17
  44. package/dist/esm/pdf/image-xobject.js +2 -306
  45. package/dist/esm/pdf/index.js +1 -0
  46. package/dist/esm/pdf/styled-page-emitter.d.ts +8 -2
  47. package/dist/esm/pdf/styled-page-emitter.js +29 -14
  48. package/dist/esm/pdf/styled-page-renderer.d.ts +4 -217
  49. package/dist/esm/pdf/styled-page-renderer.js +3 -1824
  50. package/dist/esm/pdf/vector-graphics.d.ts +3 -45
  51. package/dist/esm/pdf/vector-graphics.js +2 -46
  52. package/dist/esm/svg/svg-writer.d.ts +1 -1
  53. package/dist/esm/svg/svg-writer.js +14 -15
  54. package/dist/esm/word/docx-reader.js +4 -3
  55. package/package.json +1 -1
  56. /package/dist/esm/{pdf → layout}/chart-geometry.d.ts +0 -0
  57. /package/dist/esm/{pdf → layout}/chart-geometry.js +0 -0
@@ -0,0 +1,1729 @@
1
+ import { resolveFamilyKey } from "../core/fonts/remote-fonts.js";
2
+ import { shapeText } from "../core/font/opentype-layout.js";
3
+ import { createFontMeasure } from "../core/font/measure.js";
4
+ import { halfPtToPt, pt } from "../core/ir/units.js";
5
+ import { ResourceStore } from "../core/ir/resources.js";
6
+ import { prepareImage } from "../core/images.js";
7
+ import { segmentLevels } from "../core/bidi/segments.js";
8
+ import { analyzeString, hasBidiCharacters } from "../core/bidi/index.js";
9
+ import { FORCED_BREAK, breakLines } from "../core/line-breaker/knuth-plass.js";
10
+ import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
11
+ import { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN } from "../core/style-cascade/types.js";
12
+ import { resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
13
+ import "../core/style-cascade/index.js";
14
+ import { PathBuilder } from "../core/vector.js";
15
+ import { arcPoint, arcToBeziers } from "./arc-to-bezier.js";
16
+ import { buildChartScene } from "./chart-geometry.js";
17
+ import { layoutMath, mathGlyphSegments, variantStyle } from "./math-layout.js";
18
+ import { customPaths, presetPaths, rectPath } from "./preset-geometry.js";
19
+ import { StructTreeBuilder } from "../pdf/struct-tree.js";
20
+ //#region src/layout/styled-layout.ts
21
+ function parsePdfAProfile(pdfA) {
22
+ const part = Number(pdfA.charAt(6));
23
+ const level = pdfA.charAt(7);
24
+ return {
25
+ part,
26
+ level,
27
+ tagged: level === "a",
28
+ version: part === 1 ? "1.4" : "1.7"
29
+ };
30
+ }
31
+ var TWIP_TO_PT = 1 / 20;
32
+ var EIGHTH_PT = 1 / 8;
33
+ var DEFAULT_CELL_PADDING_TWIPS = 108;
34
+ var DEFAULT_BORDER_SIZE_EIGHTH = 4;
35
+ function layoutStyledDocument(body, options) {
36
+ const sectionList = resolveSectionList(body, options);
37
+ const numberedBody = applyNumbering(body, options.numbering);
38
+ const numberedHeadersFooters = applyNumberingToHeadersFooters(options.headersFooters, options.numbering);
39
+ const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
40
+ const tagged = options.tagged === true || (pdfaProfile?.tagged ?? false);
41
+ const structBuilder = tagged ? new StructTreeBuilder() : void 0;
42
+ const fontResources = collectFontResources(numberedBody, numberedHeadersFooters, options);
43
+ const imageResources = collectImageResources(numberedBody, numberedHeadersFooters, options);
44
+ const sectionCtxs = sectionList.map((s) => buildSectionContext(s, options, numberedHeadersFooters, fontResources));
45
+ let sectionIdx = 0;
46
+ return {
47
+ pages: paginateSections(numberedBody.map((el, idx) => {
48
+ while (sectionIdx < sectionCtxs.length - 1 && idx >= sectionCtxs[sectionIdx].endIndex) sectionIdx++;
49
+ const ctx = sectionCtxs[sectionIdx];
50
+ return layoutBodyElement(el, options, fontResources, imageResources, ctx.contentWidth, ctx.pageContentHeight);
51
+ }), sectionCtxs, structBuilder, options.language ?? "en-US"),
52
+ resources: options.resources ?? new ResourceStore(),
53
+ fontResources,
54
+ imageResources,
55
+ pdf: {
56
+ structBuilder,
57
+ sectionCtxs,
58
+ pdfaProfile,
59
+ tagged
60
+ }
61
+ };
62
+ }
63
+ function resolvePageDimensions(options, section) {
64
+ const sectionPageWidth = section?.pageSize !== void 0 ? section.pageSize.width : void 0;
65
+ const sectionPageHeight = section?.pageSize !== void 0 ? section.pageSize.height : void 0;
66
+ const sectionLeft = section?.margins?.left !== void 0 ? section.margins.left : void 0;
67
+ const sectionRight = section?.margins?.right !== void 0 ? section.margins.right : void 0;
68
+ const sectionTop = section?.margins?.top !== void 0 ? section.margins.top : void 0;
69
+ const sectionBottom = section?.margins?.bottom !== void 0 ? section.margins.bottom : void 0;
70
+ const headerOffsetPt = section?.margins?.header ?? 720 * TWIP_TO_PT;
71
+ const footerOffsetPt = section?.margins?.footer ?? 720 * TWIP_TO_PT;
72
+ return {
73
+ pageWidth: options.pageWidth ?? sectionPageWidth ?? 595,
74
+ pageHeight: options.pageHeight ?? sectionPageHeight ?? 842,
75
+ marginLeft: options.marginLeft ?? sectionLeft ?? 72,
76
+ marginRight: options.marginRight ?? sectionRight ?? 72,
77
+ marginTop: options.marginTop ?? sectionTop ?? 72,
78
+ marginBottom: options.marginBottom ?? sectionBottom ?? 72,
79
+ headerOffsetPt,
80
+ footerOffsetPt
81
+ };
82
+ }
83
+ function resolveSectionList(body, options) {
84
+ if (options.sections && options.sections.length > 0) return options.sections;
85
+ if (options.section) return [{
86
+ properties: options.section,
87
+ endIndex: body.length
88
+ }];
89
+ return [{
90
+ properties: {
91
+ headers: [],
92
+ footers: []
93
+ },
94
+ endIndex: body.length
95
+ }];
96
+ }
97
+ function buildSectionContext(section, options, headersFooters, fontResources) {
98
+ const dims = resolvePageDimensions(options, section.properties);
99
+ const contentWidth = dims.pageWidth - dims.marginLeft - dims.marginRight;
100
+ const headerSet = layoutHeaderSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.pageHeight, dims.headerOffsetPt);
101
+ const footerSet = layoutFooterSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.pageHeight, dims.footerOffsetPt);
102
+ return {
103
+ endIndex: section.endIndex,
104
+ properties: section.properties,
105
+ pageWidth: dims.pageWidth,
106
+ pageHeight: dims.pageHeight,
107
+ marginLeft: dims.marginLeft,
108
+ marginTop: dims.marginTop,
109
+ marginBottom: dims.marginBottom,
110
+ contentWidth,
111
+ pageContentHeight: dims.pageHeight - dims.marginTop - dims.marginBottom,
112
+ headerSet,
113
+ footerSet,
114
+ titlePg: section.properties.titlePg === true,
115
+ evenAndOddHeaders: section.properties.evenAndOddHeaders === true
116
+ };
117
+ }
118
+ function refByType(refs, type) {
119
+ if (!refs || refs.length === 0) return void 0;
120
+ return refs.find((r) => r.type === type);
121
+ }
122
+ function markPagination(cmds) {
123
+ return cmds.map((c) => ({
124
+ ...c,
125
+ artifact: "pagination"
126
+ }));
127
+ }
128
+ function layoutHeaderSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, pageHeight, headerOffsetPt) {
129
+ const band = (type) => {
130
+ const ref = refByType(section.headers, type);
131
+ if (!ref) return [];
132
+ const content = headersFooters.get(ref.relationshipId);
133
+ if (!content) return [];
134
+ return markPagination(drawBlocksSequentially(laidOutBlocksFor(content, options, fontResources, contentWidth), marginLeft, pageHeight - headerOffsetPt, pageHeight));
135
+ };
136
+ return {
137
+ default: band("default"),
138
+ first: band("first"),
139
+ even: band("even")
140
+ };
141
+ }
142
+ function layoutFooterSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, pageHeight, footerOffsetPt) {
143
+ const band = (type) => {
144
+ const ref = refByType(section.footers, type);
145
+ if (!ref) return [];
146
+ const content = headersFooters.get(ref.relationshipId);
147
+ if (!content) return [];
148
+ const blocks = laidOutBlocksFor(content, options, fontResources, contentWidth);
149
+ return markPagination(drawBlocksSequentially(blocks, marginLeft, footerOffsetPt + blocks.reduce((sum, b) => sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : b.heightPt), 0), pageHeight));
150
+ };
151
+ return {
152
+ default: band("default"),
153
+ first: band("first"),
154
+ even: band("even")
155
+ };
156
+ }
157
+ function pickBand(set, band) {
158
+ if (band === "first") return set.first.length > 0 ? set.first : set.default;
159
+ if (band === "even") return set.even.length > 0 ? set.even : set.default;
160
+ return set.default;
161
+ }
162
+ function bandForPage(pageInSection, globalPageIdx, titlePg, evenAndOddHeaders) {
163
+ if (pageInSection === 0 && titlePg) return "first";
164
+ if (evenAndOddHeaders && (globalPageIdx + 1) % 2 === 0) return "even";
165
+ return "default";
166
+ }
167
+ function laidOutBlocksFor(elements, options, fontResources, contentWidth) {
168
+ return elements.map((el) => layoutBodyElement(el, options, fontResources, void 0, contentWidth));
169
+ }
170
+ function layoutBodyElement(el, options, fontResources, imageResources, contentWidth, maxHeight) {
171
+ if (el.kind === "paragraph") return layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, contentWidth);
172
+ if (el.kind === "table") return layoutTableBlock(el.table, options, fontResources, imageResources, contentWidth);
173
+ if (el.kind === "image") return layoutImageBlock(el.image, imageResources, contentWidth);
174
+ if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight);
175
+ return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight);
176
+ }
177
+ var EMU_PER_PT = 12700;
178
+ function layoutImageBlock(image, imageResources, contentWidth) {
179
+ let widthPt = image.width;
180
+ let heightPt = image.height;
181
+ if (widthPt > contentWidth) {
182
+ const scale = contentWidth / widthPt;
183
+ widthPt = contentWidth;
184
+ heightPt = heightPt * scale;
185
+ }
186
+ const res = image.resource ? imageResources?.get(image.resource) : void 0;
187
+ const resolvedAlignment = image.paragraphProperties.alignment ?? "left";
188
+ return {
189
+ kind: "image",
190
+ widthPt,
191
+ heightPt,
192
+ resolvedAlignment,
193
+ resourceName: res?.resourceName ?? "",
194
+ spacingBeforePt: image.paragraphProperties.spacingBefore ?? 0,
195
+ spacingAfterPt: image.paragraphProperties.spacingAfter ?? 0,
196
+ ...image.altText ? { altText: image.altText } : {}
197
+ };
198
+ }
199
+ var DEFAULT_LINE_WIDTH_EMU = 9525;
200
+ var DEFAULT_INSET_LR_EMU = 91440;
201
+ var DEFAULT_INSET_TB_EMU = 45720;
202
+ function layoutShapeBlock(shape, options, fontResources, imageResources, contentWidth, maxHeight) {
203
+ let widthPt = shape.width;
204
+ let heightPt = shape.height;
205
+ if (widthPt > contentWidth && widthPt > 0) {
206
+ const scale = contentWidth / widthPt;
207
+ widthPt = contentWidth;
208
+ heightPt *= scale;
209
+ }
210
+ if (maxHeight !== void 0 && heightPt > maxHeight && heightPt > 0) {
211
+ const scale = maxHeight / heightPt;
212
+ widthPt *= scale;
213
+ heightPt = maxHeight;
214
+ }
215
+ const paths = buildShapePaths(shape.geometry, widthPt, heightPt);
216
+ const fillColorHex = shape.fill.kind === "solid" ? shape.fill.colorHex : void 0;
217
+ const stroke = buildStroke(shape.line);
218
+ const t = shape.transform;
219
+ const pp = shape.paragraphProperties;
220
+ const text = shape.text;
221
+ const insetLeftPt = text?.insetLeft ?? DEFAULT_INSET_LR_EMU / EMU_PER_PT;
222
+ const insetRightPt = text?.insetRight ?? DEFAULT_INSET_LR_EMU / EMU_PER_PT;
223
+ const insetTopPt = text?.insetTop ?? DEFAULT_INSET_TB_EMU / EMU_PER_PT;
224
+ const insetBottomPt = text?.insetBottom ?? DEFAULT_INSET_TB_EMU / EMU_PER_PT;
225
+ const textLines = [];
226
+ let textHeightPt = 0;
227
+ if (text && text.content.length > 0) {
228
+ const innerWidth = Math.max(1, widthPt - insetLeftPt - insetRightPt);
229
+ for (const el of text.content) {
230
+ if (el.kind !== "paragraph") continue;
231
+ const blk = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, innerWidth);
232
+ for (const line of blk.lines) {
233
+ textLines.push(line);
234
+ textHeightPt += computeLineHeight(line, blk.resolved);
235
+ }
236
+ textHeightPt += blk.spacingAfterPt;
237
+ }
238
+ }
239
+ return {
240
+ kind: "shape",
241
+ widthPt,
242
+ heightPt,
243
+ paths,
244
+ ...fillColorHex ? { fillColorHex } : {},
245
+ ...stroke ? { stroke } : {},
246
+ rotation60k: t?.rotation60k ?? 0,
247
+ flipH: t?.flipH ?? false,
248
+ flipV: t?.flipV ?? false,
249
+ resolvedAlignment: pp.alignment ?? "left",
250
+ spacingBeforePt: pp.spacingBefore ?? 0,
251
+ spacingAfterPt: pp.spacingAfter ?? 0,
252
+ textLines,
253
+ textHeightPt,
254
+ insetLeftPt,
255
+ insetRightPt,
256
+ insetTopPt,
257
+ insetBottomPt,
258
+ anchor: text?.anchor ?? "t",
259
+ ...shape.altText ? { altText: shape.altText } : {}
260
+ };
261
+ }
262
+ function buildShapePaths(geometry, widthPt, heightPt) {
263
+ if (geometry.kind === "preset") return presetPaths(geometry.preset ?? "rect", widthPt, heightPt, geometry.adjust ?? /* @__PURE__ */ new Map()) ?? [rectPath(widthPt, heightPt)];
264
+ if (geometry.custom) return customPaths(geometry.custom, widthPt, heightPt);
265
+ return [rectPath(widthPt, heightPt)];
266
+ }
267
+ function buildStroke(line) {
268
+ if (!line || line.fill === "none") return void 0;
269
+ const widthPt = line.width ?? DEFAULT_LINE_WIDTH_EMU / EMU_PER_PT;
270
+ const dash = line.dash && line.dash !== "solid" ? dashPattern(line.dash, widthPt) : void 0;
271
+ const cap = line.cap === "flat" ? "butt" : line.cap;
272
+ return {
273
+ colorHex: line.colorHex ?? "000000",
274
+ widthPt,
275
+ ...dash ? { dash } : {},
276
+ ...cap ? { cap } : {}
277
+ };
278
+ }
279
+ function dashPattern(dash, w) {
280
+ const u = Math.max(w, .1);
281
+ switch (dash) {
282
+ case "solid": return;
283
+ case "dot": return [u, 2 * u];
284
+ case "dash": return [4 * u, 3 * u];
285
+ case "dashDot": return [
286
+ 4 * u,
287
+ 3 * u,
288
+ u,
289
+ 3 * u
290
+ ];
291
+ case "lgDash": return [8 * u, 3 * u];
292
+ case "lgDashDot": return [
293
+ 8 * u,
294
+ 3 * u,
295
+ u,
296
+ 3 * u
297
+ ];
298
+ case "sysDash": return [3 * u, u];
299
+ case "sysDot": return [u, u];
300
+ }
301
+ }
302
+ function buildShapeTransform(pageX, pageY, widthPt, heightPt, rotation60k, flipH, flipV) {
303
+ const theta = -rotation60k / 6e4 * (Math.PI / 180);
304
+ const sx = flipH ? -1 : 1;
305
+ const sy = flipV ? -1 : 1;
306
+ const cos = Math.cos(theta);
307
+ const sin = Math.sin(theta);
308
+ const a = sx * cos;
309
+ const b = sx * sin;
310
+ const c = -sy * sin;
311
+ const d = sy * cos;
312
+ const cxL = widthPt / 2;
313
+ const cyL = heightPt / 2;
314
+ const centerX = pageX + cxL;
315
+ const centerY = pageY + cyL;
316
+ return [
317
+ a,
318
+ b,
319
+ c,
320
+ d,
321
+ centerX - (a * cxL + c * cyL),
322
+ centerY - (b * cxL + d * cyL)
323
+ ];
324
+ }
325
+ function flipTransform(m, pageHeight) {
326
+ return [
327
+ m[0],
328
+ -m[1],
329
+ m[2],
330
+ -m[3],
331
+ m[4],
332
+ pageHeight - m[5]
333
+ ];
334
+ }
335
+ function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight) {
336
+ let widthPt = block.width;
337
+ let heightPt = block.height;
338
+ if (widthPt > contentWidth && widthPt > 0) {
339
+ const scale = contentWidth / widthPt;
340
+ widthPt = contentWidth;
341
+ heightPt *= scale;
342
+ }
343
+ if (maxHeight !== void 0 && heightPt > maxHeight && heightPt > 0) {
344
+ const scale = maxHeight / heightPt;
345
+ widthPt *= scale;
346
+ heightPt = maxHeight;
347
+ }
348
+ const chart = options.charts?.get(block.chartRelId);
349
+ const { variant } = options.registry.resolveByStyle(false, false);
350
+ const font = fontResources.get(variant);
351
+ const layout = chart && font ? buildChartLayout(chart, widthPt, heightPt, font) : {
352
+ shapes: [],
353
+ texts: []
354
+ };
355
+ const pp = block.paragraphProperties;
356
+ const altText = block.altText ?? chart?.title;
357
+ return {
358
+ kind: "chart",
359
+ widthPt,
360
+ heightPt,
361
+ layout,
362
+ resolvedAlignment: pp.alignment ?? "left",
363
+ spacingBeforePt: pp.spacingBefore ?? 0,
364
+ spacingAfterPt: pp.spacingAfter ?? 0,
365
+ ...altText ? { altText } : {}
366
+ };
367
+ }
368
+ function buildChartLayout(chart, widthPt, heightPt, font) {
369
+ const measure = (text, sizePt) => font.measure.textWidthPt(text, sizePt);
370
+ const scene = buildChartScene(chart, widthPt, heightPt, measure);
371
+ if (!scene) return {
372
+ shapes: [{
373
+ paths: [rectPath(widthPt, heightPt)],
374
+ stroke: {
375
+ colorHex: "D9D9D9",
376
+ widthPt: 1
377
+ }
378
+ }],
379
+ texts: []
380
+ };
381
+ const shapes = [];
382
+ for (const pg of scene.polygons ?? []) shapes.push(polygonPrim(pg));
383
+ for (const r of scene.rects) shapes.push(rectPrim(r));
384
+ for (const p of scene.polylines) shapes.push(polylinePrim(p));
385
+ for (const w of scene.wedges) shapes.push(wedgePrim(w));
386
+ return {
387
+ shapes,
388
+ texts: scene.labels.map((l) => labelPrim(l, font))
389
+ };
390
+ }
391
+ function rectAtPath(x, y, w, h) {
392
+ return new PathBuilder().moveTo(x, y).lineTo(x + w, y).lineTo(x + w, y + h).lineTo(x, y + h).close().build();
393
+ }
394
+ function rectPrim(r) {
395
+ return {
396
+ paths: [rectAtPath(r.x, r.y, r.w, r.h)],
397
+ ...r.fillHex ? { fillColorHex: r.fillHex } : {},
398
+ ...r.strokeHex ? { stroke: {
399
+ colorHex: r.strokeHex,
400
+ widthPt: r.strokeWidthPt ?? 1
401
+ } } : {}
402
+ };
403
+ }
404
+ function polylinePrim(p) {
405
+ const b = new PathBuilder();
406
+ p.points.forEach(([x, y], i) => i === 0 ? b.moveTo(x, y) : b.lineTo(x, y));
407
+ return {
408
+ paths: [b.build()],
409
+ stroke: {
410
+ colorHex: p.strokeHex,
411
+ widthPt: p.widthPt
412
+ }
413
+ };
414
+ }
415
+ function polygonPrim(p) {
416
+ const b = new PathBuilder();
417
+ p.points.forEach(([x, y], i) => i === 0 ? b.moveTo(x, y) : b.lineTo(x, y));
418
+ b.close();
419
+ return {
420
+ paths: [b.build()],
421
+ fillColorHex: p.fillHex,
422
+ ...p.strokeHex ? { stroke: {
423
+ colorHex: p.strokeHex,
424
+ widthPt: p.widthPt ?? 1
425
+ } } : {}
426
+ };
427
+ }
428
+ function wedgePrim(w) {
429
+ const start = arcPoint(w.cx, w.cy, w.r, w.r, w.startRad);
430
+ return {
431
+ paths: [new PathBuilder().moveTo(w.cx, w.cy).lineTo(start[0], start[1]).append(arcToBeziers(w.cx, w.cy, w.r, w.r, w.startRad, w.sweepRad)).close().build()],
432
+ fillColorHex: w.fillHex,
433
+ ...w.strokeHex ? { stroke: {
434
+ colorHex: w.strokeHex,
435
+ widthPt: 1
436
+ } } : {}
437
+ };
438
+ }
439
+ function labelPrim(l, font) {
440
+ const line = makeChartLabelLine(l.text, font, l.sizePt, l.colorHex);
441
+ const w = line.contentWidthPt;
442
+ return {
443
+ line,
444
+ x: l.align === "center" ? l.x - w / 2 : l.align === "right" ? l.x - w : l.x,
445
+ y: l.y
446
+ };
447
+ }
448
+ function makeChartLabelLine(text, font, sizePt, colorHex) {
449
+ const widthPt = font.measure.textWidthPt(text, sizePt);
450
+ return {
451
+ tokens: [{
452
+ kind: "text",
453
+ text,
454
+ isSpace: false,
455
+ resolvedRun: {
456
+ ...DEFAULT_RESOLVED_RUN,
457
+ colorHex,
458
+ fontSizePt: halfPtToPt(Math.round(sizePt * 2))
459
+ },
460
+ font,
461
+ fontSizePt: sizePt,
462
+ widthPt,
463
+ bidiLevel: 0
464
+ }],
465
+ contentWidthPt: widthPt,
466
+ maxFontSizePt: sizePt,
467
+ availableWidthPt: widthPt,
468
+ firstLine: true,
469
+ resolved: DEFAULT_RESOLVED_PARAGRAPH,
470
+ isLastInParagraph: true
471
+ };
472
+ }
473
+ function collectImageResources(body, headersFooters, options) {
474
+ const out = /* @__PURE__ */ new Map();
475
+ if (!options.resources || options.resources.size === 0) return out;
476
+ const seen = /* @__PURE__ */ new Set();
477
+ const visit = (elements) => {
478
+ for (const el of elements) if (el.kind === "image") {
479
+ if (el.image.resource) seen.add(el.image.resource);
480
+ } else if (el.kind === "paragraph") {
481
+ for (const run of el.paragraph.runs) if (run.inlineImage?.resource) seen.add(run.inlineImage.resource);
482
+ } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
483
+ else if (el.kind === "shape") {
484
+ if (el.shape.text) visit(el.shape.text.content);
485
+ }
486
+ };
487
+ visit(body);
488
+ for (const hf of headersFooters.values()) visit(hf);
489
+ const flattenAlpha = options.pdfA ? parsePdfAProfile(options.pdfA).part === 1 : false;
490
+ let counter = 0;
491
+ for (const resourceId of seen) {
492
+ const bytes = options.resources.get(resourceId);
493
+ if (!bytes) continue;
494
+ let prepared;
495
+ try {
496
+ prepared = prepareImage(bytes, { flattenAlpha });
497
+ } catch {
498
+ continue;
499
+ }
500
+ counter++;
501
+ out.set(resourceId, {
502
+ resourceName: `Im${counter}`,
503
+ prepared
504
+ });
505
+ }
506
+ return out;
507
+ }
508
+ function drawBlocksSequentially(blocks, startX, startY, pageHeight) {
509
+ const out = [];
510
+ let cursorY = startY;
511
+ for (const block of blocks) {
512
+ if (block.kind !== "paragraph") continue;
513
+ cursorY -= block.spacingBeforePt;
514
+ for (const line of block.lines) {
515
+ const h = computeLineHeight(line, block.resolved);
516
+ cursorY -= h;
517
+ const indentLeft = block.resolved.indentLeft + (line.firstLine ? block.resolved.indentFirstLine : 0);
518
+ const offset = alignmentOffset(block.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
519
+ out.push({
520
+ type: "line",
521
+ line,
522
+ originX: pt(startX + indentLeft + offset),
523
+ baselineY: pt(pageHeight - (cursorY + lineDescent(line)))
524
+ });
525
+ }
526
+ cursorY -= block.spacingAfterPt;
527
+ }
528
+ return out;
529
+ }
530
+ function runFontKeyAndParsed(options, ascii, bold, italic) {
531
+ if (ascii && options.embeddedFonts) {
532
+ const name = ascii.trim().toLowerCase();
533
+ const emb = options.embeddedFonts.get(name);
534
+ if (emb) {
535
+ const { variant, parsed } = emb.resolveByStyle(bold, italic);
536
+ return {
537
+ fontKey: `embed:${name}:${variant}`,
538
+ parsed
539
+ };
540
+ }
541
+ }
542
+ const byFamily = options.registriesByFamily;
543
+ if (byFamily && byFamily.size > 0) {
544
+ let key = resolveFamilyKey(ascii);
545
+ let reg = byFamily.get(key);
546
+ if (!reg) {
547
+ key = byFamily.keys().next().value;
548
+ reg = byFamily.get(key);
549
+ }
550
+ const { variant, parsed } = reg.resolveByStyle(bold, italic);
551
+ return {
552
+ fontKey: `${key}:${variant}`,
553
+ parsed
554
+ };
555
+ }
556
+ const { variant, parsed } = options.registry.resolveByStyle(bold, italic);
557
+ return {
558
+ fontKey: variant,
559
+ parsed
560
+ };
561
+ }
562
+ function lookupFont(resources, fontKey) {
563
+ return resources.get(fontKey) ?? resources.values().next().value;
564
+ }
565
+ function collectFontResources(body, headersFooters, options) {
566
+ const used = /* @__PURE__ */ new Map();
567
+ const addRun = (run, para) => {
568
+ const resolved = resolveRunProperties(run.properties, para.properties, options.styles);
569
+ const { fontKey, parsed } = runFontKeyAndParsed(options, resolved.fontFamily.ascii, resolved.bold, resolved.italic);
570
+ let bucket = used.get(fontKey);
571
+ if (!bucket) {
572
+ bucket = {
573
+ parsed,
574
+ gids: /* @__PURE__ */ new Set()
575
+ };
576
+ used.set(fontKey, bucket);
577
+ }
578
+ const shaped = shapeText(run.text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
579
+ for (const g of shaped.gids) bucket.gids.add(g);
580
+ };
581
+ const addMath = (node) => {
582
+ for (const seg of mathGlyphSegments(node)) {
583
+ const { bold, italic } = variantStyle(seg.variant);
584
+ const { variant, parsed } = options.registry.resolveByStyle(bold, italic);
585
+ let bucket = used.get(variant);
586
+ if (!bucket) {
587
+ bucket = {
588
+ parsed,
589
+ gids: /* @__PURE__ */ new Set()
590
+ };
591
+ used.set(variant, bucket);
592
+ }
593
+ for (const ch of seg.text) bucket.gids.add(parsed.glyphForCodepoint(ch.codePointAt(0)));
594
+ }
595
+ };
596
+ const visit = (elements) => {
597
+ for (const el of elements) if (el.kind === "paragraph") for (const run of el.paragraph.runs) {
598
+ if (run.math) {
599
+ addMath(run.math);
600
+ continue;
601
+ }
602
+ if (run.inlineImage && !run.text) continue;
603
+ addRun(run, el.paragraph);
604
+ }
605
+ else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
606
+ else if (el.kind === "shape") {
607
+ if (el.shape.text) visit(el.shape.text.content);
608
+ } else if (el.kind === "chart") {
609
+ const chart = options.charts?.get(el.chart.chartRelId);
610
+ if (chart) {
611
+ const reg = options.registry.resolveByStyle(false, false);
612
+ let bucket = used.get(reg.variant);
613
+ if (!bucket) {
614
+ bucket = {
615
+ parsed: reg.parsed,
616
+ gids: /* @__PURE__ */ new Set()
617
+ };
618
+ used.set(reg.variant, bucket);
619
+ }
620
+ const add = (s) => {
621
+ for (const ch of s) bucket.gids.add(reg.parsed.glyphForCodepoint(ch.codePointAt(0)));
622
+ };
623
+ if (chart.title) add(chart.title);
624
+ for (const c of chart.categories) add(c);
625
+ for (const sr of chart.series) if (sr.name) add(sr.name);
626
+ add("0123456789.,-%() ");
627
+ }
628
+ }
629
+ };
630
+ visit(body);
631
+ for (const hf of headersFooters.values()) visit(hf);
632
+ if (used.size === 0) {
633
+ const regular = options.registry.resolveByStyle(false, false);
634
+ used.set(regular.variant, {
635
+ parsed: regular.parsed,
636
+ gids: new Set([0])
637
+ });
638
+ }
639
+ const out = /* @__PURE__ */ new Map();
640
+ let counter = 0;
641
+ for (const [variant, info] of used) {
642
+ counter++;
643
+ const resourceName = `F${counter}`;
644
+ out.set(variant, {
645
+ resourceName,
646
+ parsed: info.parsed,
647
+ measure: createFontMeasure(info.parsed),
648
+ gids: info.gids
649
+ });
650
+ }
651
+ return out;
652
+ }
653
+ function layoutParagraphBlock(paragraph, options, fontResources, imageResources, contentWidth) {
654
+ const baseResolved = resolveParagraphProperties(paragraph.properties, options.styles);
655
+ const baseDir = paragraphBaseDirection(paragraph, baseResolved);
656
+ const resolved = baseDir === "rtl" && baseResolved.alignment === "left" ? {
657
+ ...baseResolved,
658
+ alignment: "right"
659
+ } : baseResolved;
660
+ const lines = wrap(tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir), paragraphMaxWidth(resolved, contentWidth, true), paragraphMaxWidth(resolved, contentWidth, false), resolved, options.hyphenator);
661
+ let heightPt = 0;
662
+ for (const line of lines) heightPt += computeLineHeight(line, resolved);
663
+ const numbering = paragraph.properties.numbering;
664
+ return {
665
+ kind: "paragraph",
666
+ resolved,
667
+ lines,
668
+ heightPt,
669
+ spacingBeforePt: resolved.spacingBefore,
670
+ spacingAfterPt: resolved.spacingAfter,
671
+ ...numbering ? { list: {
672
+ numId: numbering.numId,
673
+ level: numbering.ilvl
674
+ } } : {},
675
+ ...paragraph.runs.some((r) => r.pageBreak) ? { pageBreakAfter: true } : {}
676
+ };
677
+ }
678
+ function resolveMathItems(items, fontFor) {
679
+ return items.map((it) => {
680
+ if (it.kind === "glyph") return {
681
+ kind: "glyph",
682
+ x: it.x,
683
+ y: it.y,
684
+ text: it.text,
685
+ font: fontFor(it.variant),
686
+ sizePt: it.sizePt
687
+ };
688
+ if (it.kind === "rule") return {
689
+ kind: "rule",
690
+ x: it.x,
691
+ y: it.y,
692
+ w: it.w,
693
+ h: it.h
694
+ };
695
+ return {
696
+ kind: "path",
697
+ segments: it.segments,
698
+ ...it.strokeWidthPt !== void 0 ? { strokeWidthPt: it.strokeWidthPt } : {},
699
+ ...it.fill ? { fill: true } : {}
700
+ };
701
+ });
702
+ }
703
+ function tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir) {
704
+ const plans = paragraph.runs.map((run) => {
705
+ if (run.inlineImage) {
706
+ const naturalW = run.inlineImage.width;
707
+ const widthPt = Math.min(naturalW, contentWidth);
708
+ const scale = naturalW > 0 ? widthPt / naturalW : 1;
709
+ const heightPt = run.inlineImage.height * scale;
710
+ const res = run.inlineImage.resource ? imageResources?.get(run.inlineImage.resource) : void 0;
711
+ const resolvedRun = resolveRunProperties(run.properties, paragraph.properties, options.styles);
712
+ const { fontKey } = runFontKeyAndParsed(options, resolvedRun.fontFamily.ascii, resolvedRun.bold, resolvedRun.italic);
713
+ return {
714
+ run,
715
+ resolvedRun,
716
+ font: lookupFont(fontResources, fontKey),
717
+ fontSizePt: resolvedRun.fontSizePt,
718
+ isImage: true,
719
+ imageWidthPt: widthPt,
720
+ imageHeightPt: heightPt,
721
+ imageResourceName: res?.resourceName ?? ""
722
+ };
723
+ }
724
+ if (run.math) {
725
+ const resolvedRun = resolveRunProperties(run.properties, paragraph.properties, options.styles);
726
+ const sizePt = resolvedRun.fontSizePt;
727
+ const fontFor = (v) => {
728
+ const { bold, italic } = variantStyle(v);
729
+ const r = options.registry.resolveByStyle(bold, italic);
730
+ return fontResources.get(r.variant) ?? fontResources.get(options.registry.resolveByStyle(false, false).variant);
731
+ };
732
+ const measure = (text, sz, v) => fontFor(v).measure.textWidthPt(text, sz);
733
+ const box = layoutMath(run.math, { sizePt }, measure);
734
+ const { variant } = options.registry.resolveByStyle(resolvedRun.bold, resolvedRun.italic);
735
+ return {
736
+ run,
737
+ resolvedRun,
738
+ font: lookupFont(fontResources, variant),
739
+ fontSizePt: sizePt,
740
+ isImage: false,
741
+ imageWidthPt: 0,
742
+ imageHeightPt: 0,
743
+ imageResourceName: "",
744
+ math: {
745
+ items: resolveMathItems(box.items, fontFor),
746
+ widthPt: box.width,
747
+ ascentPt: box.ascent,
748
+ descentPt: box.descent
749
+ }
750
+ };
751
+ }
752
+ const resolvedRun = resolveRunProperties(run.properties, paragraph.properties, options.styles);
753
+ const { fontKey } = runFontKeyAndParsed(options, resolvedRun.fontFamily.ascii, resolvedRun.bold, resolvedRun.italic);
754
+ return {
755
+ run,
756
+ resolvedRun,
757
+ font: lookupFont(fontResources, fontKey),
758
+ fontSizePt: resolvedRun.fontSizePt,
759
+ isImage: false,
760
+ imageWidthPt: 0,
761
+ imageHeightPt: 0,
762
+ imageResourceName: ""
763
+ };
764
+ });
765
+ const anyRtlRun = plans.some((p) => p.resolvedRun.rtl);
766
+ const anyBidiChars = plans.some((p) => !p.isImage && hasBidiCharacters(p.run.text));
767
+ if (!(baseDir === "rtl" || anyRtlRun || anyBidiChars)) return tokenizePlansLtr(plans);
768
+ return tokenizePlansBidi(plans, segmentLevels(plans.map((plan) => ({
769
+ text: plan.run.text,
770
+ ...plan.isImage || plan.math !== void 0 ? { object: true } : {},
771
+ ...plan.resolvedRun.rtl ? { rtl: true } : {}
772
+ })), baseDir));
773
+ }
774
+ function tokenizePlansLtr(plans) {
775
+ const tokens = [];
776
+ for (const plan of plans) {
777
+ if (plan.isImage) {
778
+ tokens.push({
779
+ kind: "image",
780
+ imageResourceName: plan.imageResourceName,
781
+ widthPt: plan.imageWidthPt,
782
+ heightPt: plan.imageHeightPt,
783
+ isSpace: false,
784
+ bidiLevel: 0
785
+ });
786
+ continue;
787
+ }
788
+ if (plan.math) {
789
+ tokens.push({
790
+ kind: "math",
791
+ items: plan.math.items,
792
+ widthPt: plan.math.widthPt,
793
+ ascentPt: plan.math.ascentPt,
794
+ descentPt: plan.math.descentPt,
795
+ isSpace: false,
796
+ bidiLevel: 0
797
+ });
798
+ continue;
799
+ }
800
+ for (const t of tokenizeText(plan.run.text)) tokens.push({
801
+ kind: "text",
802
+ text: t.text,
803
+ isSpace: t.isSpace,
804
+ resolvedRun: plan.resolvedRun,
805
+ font: plan.font,
806
+ fontSizePt: plan.fontSizePt,
807
+ widthPt: plan.font.measure.textWidthPt(t.text, plan.fontSizePt),
808
+ bidiLevel: 0
809
+ });
810
+ }
811
+ return tokens;
812
+ }
813
+ function tokenizePlansBidi(plans, realLevels) {
814
+ const tokens = [];
815
+ let realIdx = 0;
816
+ for (let r = 0; r < plans.length; r++) {
817
+ const plan = plans[r];
818
+ if (plan.isImage) {
819
+ tokens.push({
820
+ kind: "image",
821
+ imageResourceName: plan.imageResourceName,
822
+ widthPt: plan.imageWidthPt,
823
+ heightPt: plan.imageHeightPt,
824
+ isSpace: false,
825
+ bidiLevel: realLevels[realIdx] ?? 0
826
+ });
827
+ realIdx++;
828
+ continue;
829
+ }
830
+ if (plan.math) {
831
+ tokens.push({
832
+ kind: "math",
833
+ items: plan.math.items,
834
+ widthPt: plan.math.widthPt,
835
+ ascentPt: plan.math.ascentPt,
836
+ descentPt: plan.math.descentPt,
837
+ isSpace: false,
838
+ bidiLevel: realLevels[realIdx] ?? 0
839
+ });
840
+ realIdx++;
841
+ continue;
842
+ }
843
+ const chars = [...plan.run.text];
844
+ let bufStart = 0;
845
+ let curSpace = false;
846
+ let curLevel = -1;
847
+ const flush = (endExclusive) => {
848
+ if (endExclusive <= bufStart) return;
849
+ const text = chars.slice(bufStart, endExclusive).join("");
850
+ tokens.push({
851
+ kind: "text",
852
+ text,
853
+ isSpace: curSpace,
854
+ resolvedRun: plan.resolvedRun,
855
+ font: plan.font,
856
+ fontSizePt: plan.fontSizePt,
857
+ widthPt: plan.font.measure.textWidthPt(text, plan.fontSizePt),
858
+ bidiLevel: curLevel
859
+ });
860
+ };
861
+ for (let c = 0; c < chars.length; c++) {
862
+ const ch = chars[c];
863
+ const isSpace = /\s/.test(ch);
864
+ const level = realLevels[realIdx] ?? 0;
865
+ if (c === 0) {
866
+ curSpace = isSpace;
867
+ curLevel = level;
868
+ } else if (isSpace !== curSpace || level !== curLevel) {
869
+ flush(c);
870
+ bufStart = c;
871
+ curSpace = isSpace;
872
+ curLevel = level;
873
+ }
874
+ realIdx++;
875
+ }
876
+ flush(chars.length);
877
+ }
878
+ return tokens;
879
+ }
880
+ function paragraphBaseDirection(paragraph, resolved) {
881
+ if (resolved.bidi) return "rtl";
882
+ let text = "";
883
+ for (const run of paragraph.runs) {
884
+ if (!run.inlineImage) text += run.text;
885
+ if (text.length > 64) break;
886
+ }
887
+ if (!hasBidiCharacters(text)) return "ltr";
888
+ return analyzeString(text, "auto").paragraphLevel === 1 ? "rtl" : "ltr";
889
+ }
890
+ function paragraphMaxWidth(p, contentWidth, firstLine) {
891
+ const indentLeft = p.indentLeft;
892
+ const indentRight = p.indentRight;
893
+ const firstLineExtra = firstLine ? p.indentFirstLine : 0;
894
+ return Math.max(1, contentWidth - indentLeft - indentRight - firstLineExtra);
895
+ }
896
+ function tokenizeText(text) {
897
+ const out = [];
898
+ if (text.length === 0) return out;
899
+ const re = /(\s+)|(\S+)/g;
900
+ let m;
901
+ while ((m = re.exec(text)) !== null) if (m[1] !== void 0) out.push({
902
+ text: m[1],
903
+ isSpace: true
904
+ });
905
+ else if (m[2] !== void 0) out.push({
906
+ text: m[2],
907
+ isSpace: false
908
+ });
909
+ return out;
910
+ }
911
+ var HYPHENATION_PENALTY = 50;
912
+ function paragraphItemStream(tokens, hyphenator) {
913
+ const entries = [];
914
+ for (const tok of tokens) {
915
+ if (tok.isSpace || tok.kind === "image" || tok.kind === "math") {
916
+ entries.push({
917
+ item: tok.isSpace ? {
918
+ type: "glue",
919
+ width: tok.widthPt,
920
+ stretch: tok.widthPt * .6,
921
+ shrink: tok.widthPt * .3
922
+ } : {
923
+ type: "box",
924
+ width: tok.widthPt
925
+ },
926
+ token: tok
927
+ });
928
+ continue;
929
+ }
930
+ const positions = hyphenator ? hyphenator.hyphenate(tok.text) : [];
931
+ if (positions.length === 0) {
932
+ entries.push({
933
+ item: {
934
+ type: "box",
935
+ width: tok.widthPt
936
+ },
937
+ token: tok
938
+ });
939
+ continue;
940
+ }
941
+ const hyphenWidth = tok.font.measure.textWidthPt("-", tok.fontSizePt);
942
+ let prev = 0;
943
+ for (let pi = 0; pi <= positions.length; pi++) {
944
+ const end = pi < positions.length ? positions[pi] : tok.text.length;
945
+ const fragText = tok.text.substring(prev, end);
946
+ const fragWidth = tok.font.measure.textWidthPt(fragText, tok.fontSizePt);
947
+ const fragTok = {
948
+ kind: "text",
949
+ text: fragText,
950
+ isSpace: false,
951
+ resolvedRun: tok.resolvedRun,
952
+ font: tok.font,
953
+ fontSizePt: tok.fontSizePt,
954
+ widthPt: fragWidth,
955
+ bidiLevel: tok.bidiLevel
956
+ };
957
+ entries.push({
958
+ item: {
959
+ type: "box",
960
+ width: fragWidth
961
+ },
962
+ token: fragTok
963
+ });
964
+ if (pi < positions.length) entries.push({
965
+ item: {
966
+ type: "penalty",
967
+ width: hyphenWidth,
968
+ penalty: HYPHENATION_PENALTY,
969
+ flagged: true
970
+ },
971
+ token: null,
972
+ hyphenWidthPt: hyphenWidth
973
+ });
974
+ prev = end;
975
+ }
976
+ }
977
+ entries.push({
978
+ item: {
979
+ type: "glue",
980
+ width: 0,
981
+ stretch: 1e6,
982
+ shrink: 0
983
+ },
984
+ token: null
985
+ });
986
+ entries.push({
987
+ item: {
988
+ type: "penalty",
989
+ width: 0,
990
+ penalty: FORCED_BREAK,
991
+ flagged: false
992
+ },
993
+ token: null
994
+ });
995
+ return entries;
996
+ }
997
+ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, resolved) {
998
+ let st = start;
999
+ let et = breakIdx;
1000
+ while (st < et && (entries[st].token === null || entries[st].token?.isSpace)) st++;
1001
+ while (et > st && (entries[et - 1].token === null || entries[et - 1].token?.isSpace)) et--;
1002
+ if (st >= et) return null;
1003
+ const lineTokens = [];
1004
+ for (let i = st; i < et; i++) {
1005
+ const ft = entries[i].token;
1006
+ if (ft) lineTokens.push(ft);
1007
+ }
1008
+ const hyphenWidth = entries[breakIdx]?.hyphenWidthPt;
1009
+ if (hyphenWidth !== void 0) {
1010
+ const lastIdx = lineTokens.length - 1;
1011
+ const last = lineTokens[lastIdx];
1012
+ if (last && last.kind === "text") lineTokens[lastIdx] = {
1013
+ ...last,
1014
+ text: last.text + "-",
1015
+ widthPt: last.widthPt + hyphenWidth
1016
+ };
1017
+ }
1018
+ const tokenLineSize = (t) => t.kind === "text" ? t.fontSizePt : t.kind === "image" ? t.heightPt : 0;
1019
+ let contentWidth = 0;
1020
+ let maxSize = 0;
1021
+ let mathAscent = 0;
1022
+ let mathDescent = 0;
1023
+ for (const t of lineTokens) {
1024
+ contentWidth += t.widthPt;
1025
+ const sz = tokenLineSize(t);
1026
+ if (sz > maxSize) maxSize = sz;
1027
+ if (t.kind === "math") {
1028
+ mathAscent = Math.max(mathAscent, t.ascentPt);
1029
+ mathDescent = Math.max(mathDescent, t.descentPt);
1030
+ }
1031
+ }
1032
+ return {
1033
+ tokens: lineTokens,
1034
+ contentWidthPt: contentWidth,
1035
+ maxFontSizePt: maxSize,
1036
+ availableWidthPt,
1037
+ firstLine: isFirst,
1038
+ resolved,
1039
+ isLastInParagraph: false,
1040
+ mathAscentPt: mathAscent,
1041
+ mathDescentPt: mathDescent
1042
+ };
1043
+ }
1044
+ function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator) {
1045
+ if (tokens.length === 0) return [];
1046
+ const entries = paragraphItemStream(tokens, hyphenator);
1047
+ const { breaks } = breakLines(entries.map((e) => e.item), [firstLineWidth, otherWidth]);
1048
+ const lines = [];
1049
+ let start = 0;
1050
+ let isFirst = true;
1051
+ for (const breakIdx of breaks) {
1052
+ const line = lineFromRange(entries, start, breakIdx, isFirst ? firstLineWidth : otherWidth, isFirst, resolved);
1053
+ if (line) lines.push(line);
1054
+ start = breakIdx + 1;
1055
+ isFirst = false;
1056
+ }
1057
+ if (lines.length > 0) lines[lines.length - 1].isLastInParagraph = true;
1058
+ return lines;
1059
+ }
1060
+ function computeLineHeight(line, p) {
1061
+ const fontSize = line.maxFontSizePt || 12;
1062
+ const mathH = (line.mathAscentPt ?? 0) + (line.mathDescentPt ?? 0);
1063
+ const mathNeed = mathH > 0 ? mathH * 1.05 : 0;
1064
+ if (p.spacingLineRule === "exact" && p.spacingLine > 0) return Math.max(p.spacingLine, mathNeed);
1065
+ if (p.spacingLineRule === "atLeast" && p.spacingLine > 0) return Math.max(fontSize * 1.2, p.spacingLine, mathNeed);
1066
+ const lineTwips = Math.round(p.spacingLine * 20);
1067
+ const multiple = lineTwips > 0 ? lineTwips / 240 : 1;
1068
+ return Math.max(fontSize * 1.2 * multiple, mathNeed);
1069
+ }
1070
+ function lineDescent(line) {
1071
+ const fs = line.maxFontSizePt || 12;
1072
+ return Math.max(fs * .2, line.mathDescentPt ?? 0);
1073
+ }
1074
+ function alignmentOffset(alignment, lineWidth, available) {
1075
+ if (alignment === "right") return Math.max(0, available - lineWidth);
1076
+ if (alignment === "center") return Math.max(0, (available - lineWidth) / 2);
1077
+ return 0;
1078
+ }
1079
+ function layoutTableBlock(table, options, fontResources, imageResources, contentWidth) {
1080
+ const columnWidthsPt = computeColumnWidths(table, options, fontResources, contentWidth);
1081
+ const mergeRoles = table.rows.map((r) => r.cells.map((c) => c.properties.merge ?? "standalone"));
1082
+ const rows = [];
1083
+ const colCount = columnWidthsPt.length;
1084
+ let aboveBordersByCol = [];
1085
+ for (let r = 0; r < table.rows.length; r++) {
1086
+ const rl = layoutTableRow(table.rows[r], r, table.rows.length, colCount, table.properties, columnWidthsPt, options, fontResources, imageResources, mergeRoles[r] ?? [], aboveBordersByCol);
1087
+ const nextAbove = [];
1088
+ let ci = 0;
1089
+ for (const cell of table.rows[r].cells) {
1090
+ const span = Math.max(1, cell.properties.colSpan ?? 1);
1091
+ for (let k = 0; k < span; k++) nextAbove[ci + k] = cell.properties.borders;
1092
+ ci += span;
1093
+ }
1094
+ aboveBordersByCol = nextAbove;
1095
+ const rp = table.rows[r].properties;
1096
+ rows.push(rp.isHeader || rp.pageBreakBefore ? {
1097
+ ...rl,
1098
+ ...rp.isHeader ? { isHeader: true } : {},
1099
+ ...rp.pageBreakBefore ? { breakBefore: true } : {}
1100
+ } : rl);
1101
+ }
1102
+ const heightPt = rows.reduce((s, r) => s + r.heightPt, 0);
1103
+ const totalWidthPt = columnWidthsPt.reduce((s, w) => s + w, 0);
1104
+ return {
1105
+ kind: "table",
1106
+ rows,
1107
+ heightPt,
1108
+ totalWidthPt,
1109
+ colCount,
1110
+ xOffsetPt: tableXOffset(table.properties.alignment, contentWidth, totalWidthPt)
1111
+ };
1112
+ }
1113
+ function tableXOffset(alignment, contentWidth, totalWidthPt) {
1114
+ const slack = contentWidth - totalWidthPt;
1115
+ if (slack <= 0 || !alignment || alignment === "left") return 0;
1116
+ return alignment === "center" ? slack / 2 : slack;
1117
+ }
1118
+ function computeColumnWidths(table, options, fontResources, contentWidth) {
1119
+ let colCount = table.grid.length;
1120
+ for (const row of table.rows) {
1121
+ let rowCols = 0;
1122
+ for (const cell of row.cells) rowCols += Math.max(1, cell.properties.colSpan ?? 1);
1123
+ if (rowCols > colCount) colCount = rowCols;
1124
+ }
1125
+ if (colCount === 0) return [];
1126
+ if (table.properties.layout === "fixed") return gridWidthsScaled(table, contentWidth, colCount);
1127
+ const colNaturalWidths = new Array(colCount).fill(0);
1128
+ for (const row of table.rows) {
1129
+ let colIdx = 0;
1130
+ for (let i = 0; i < row.cells.length; i++) {
1131
+ const cell = row.cells[i];
1132
+ const span = Math.max(1, cell.properties.colSpan ?? 1);
1133
+ const padLeft = cell.properties.margins?.left ?? table.properties.defaultCellMargins?.left ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
1134
+ const padRight = cell.properties.margins?.right ?? table.properties.defaultCellMargins?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
1135
+ let maxContent = 0;
1136
+ for (const el of cell.content) {
1137
+ if (el.kind !== "paragraph") continue;
1138
+ const w = measureSingleLine(el.paragraph, options, fontResources);
1139
+ if (w > maxContent) maxContent = w;
1140
+ }
1141
+ const perColumn = (maxContent + padLeft + padRight) / span;
1142
+ for (let k = 0; k < span && colIdx + k < colCount; k++) if (perColumn > colNaturalWidths[colIdx + k]) colNaturalWidths[colIdx + k] = perColumn;
1143
+ colIdx += span;
1144
+ }
1145
+ }
1146
+ const naturalTotal = colNaturalWidths.reduce((s, w) => s + w, 0);
1147
+ const explicitTarget = explicitTableTargetWidth(table, contentWidth);
1148
+ if (naturalTotal === 0) return gridWidthsScaled(table, contentWidth, colCount);
1149
+ if (explicitTarget !== void 0 && explicitTarget > 0) {
1150
+ const scale = explicitTarget / naturalTotal;
1151
+ return colNaturalWidths.map((w) => w * scale);
1152
+ }
1153
+ if (naturalTotal > contentWidth) {
1154
+ const scale = contentWidth / naturalTotal;
1155
+ return colNaturalWidths.map((w) => w * scale);
1156
+ }
1157
+ return colNaturalWidths;
1158
+ }
1159
+ function gridColTwips(table) {
1160
+ return table.grid.map((w) => Math.round(w * 20));
1161
+ }
1162
+ function gridWidthsScaled(table, contentWidth, colCount) {
1163
+ const gridTwips = gridColTwips(table);
1164
+ const totalGridTwips = gridTwips.reduce((s, w) => s + w, 0);
1165
+ if (totalGridTwips > 0) {
1166
+ const target = totalTableTarget(table, contentWidth);
1167
+ return gridTwips.map((w) => w / totalGridTwips * target);
1168
+ }
1169
+ const each = contentWidth / colCount;
1170
+ return new Array(colCount).fill(each);
1171
+ }
1172
+ function totalTableTarget(table, contentWidth) {
1173
+ const explicit = explicitTableTargetWidth(table, contentWidth);
1174
+ if (explicit !== void 0 && explicit > 0) return explicit;
1175
+ const sum = gridColTwips(table).reduce((s, g) => s + g, 0);
1176
+ if (sum > 0) return sum * TWIP_TO_PT;
1177
+ return contentWidth;
1178
+ }
1179
+ function explicitTableTargetWidth(table, contentWidth) {
1180
+ const w = table.properties.widthPt;
1181
+ const type = table.properties.widthType;
1182
+ if (w !== void 0 && w > 0 && type === "dxa") return w;
1183
+ const f = table.properties.widthFraction;
1184
+ if (f !== void 0 && type === "pct") return f * contentWidth;
1185
+ }
1186
+ function measureSingleLine(paragraph, options, fontResources) {
1187
+ let total = 0;
1188
+ for (const run of paragraph.runs) {
1189
+ const resolved = resolveRunProperties(run.properties, paragraph.properties, options.styles);
1190
+ const { fontKey } = runFontKeyAndParsed(options, resolved.fontFamily.ascii, resolved.bold, resolved.italic);
1191
+ const font = lookupFont(fontResources, fontKey);
1192
+ const fontSizePt = resolved.fontSizePt;
1193
+ total += font.measure.textWidthPt(run.text, fontSizePt);
1194
+ }
1195
+ return total;
1196
+ }
1197
+ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidthsPt, options, fontResources, imageResources, rowMergeRoles, aboveBordersByCol) {
1198
+ const cells = [];
1199
+ const columnXOffsets = [];
1200
+ let cursorX = 0;
1201
+ let colIdx = 0;
1202
+ for (let cellIdx = 0; cellIdx < row.cells.length; cellIdx++) {
1203
+ const cell = row.cells[cellIdx];
1204
+ const span = Math.max(1, cell.properties.colSpan ?? 1);
1205
+ let widthPt = 0;
1206
+ for (let k = 0; k < span && colIdx + k < columnWidthsPt.length; k++) widthPt += columnWidthsPt[colIdx + k];
1207
+ columnXOffsets.push(cursorX);
1208
+ const mergeRole = rowMergeRoles[cellIdx] ?? "standalone";
1209
+ const leftNeighbor = cellIdx > 0 ? row.cells[cellIdx - 1].properties.borders : void 0;
1210
+ const cellLayout = layoutTableCell(cell, tableProps, widthPt, options, fontResources, imageResources, rowIdx, colIdx, span, rowCount, colCount, mergeRole, leftNeighbor, aboveBordersByCol[colIdx]);
1211
+ cells.push(cellLayout);
1212
+ cursorX += widthPt;
1213
+ colIdx += span;
1214
+ }
1215
+ let heightPt = 0;
1216
+ for (const c of cells) if (c.totalHeightPt > heightPt) heightPt = c.totalHeightPt;
1217
+ if (row.properties.height && row.properties.heightRule === "exact") heightPt = row.properties.height;
1218
+ else if (row.properties.height && row.properties.heightRule === "atLeast") heightPt = Math.max(heightPt, row.properties.height);
1219
+ return {
1220
+ heightPt,
1221
+ cells,
1222
+ columnXOffsets,
1223
+ rowIdx,
1224
+ rowCount
1225
+ };
1226
+ }
1227
+ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imageResources, rowIdx, colStart, colSpan, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders) {
1228
+ const cellMar = cell.properties.margins;
1229
+ const tableMar = tableProps.defaultCellMargins;
1230
+ const padTopPt = cellMar?.top ?? tableMar?.top ?? 0;
1231
+ const padBottomPt = cellMar?.bottom ?? tableMar?.bottom ?? 0;
1232
+ const padLeftPt = cellMar?.left ?? tableMar?.left ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
1233
+ const padRightPt = cellMar?.right ?? tableMar?.right ?? DEFAULT_CELL_PADDING_TWIPS * TWIP_TO_PT;
1234
+ const innerWidth = Math.max(1, widthPt - padLeftPt - padRightPt);
1235
+ const lines = [];
1236
+ const nestedTables = [];
1237
+ let contentHeightPt = 0;
1238
+ if (mergeRole !== "middle" && mergeRole !== "end") {
1239
+ for (const el of cell.content) if (el.kind === "paragraph") {
1240
+ const block = layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, innerWidth);
1241
+ for (const line of block.lines) {
1242
+ lines.push(line);
1243
+ contentHeightPt += computeLineHeight(line, block.resolved);
1244
+ }
1245
+ contentHeightPt += block.spacingAfterPt;
1246
+ } else if (el.kind === "table") {
1247
+ const nested = layoutTableBlock(el.table, options, fontResources, imageResources, innerWidth);
1248
+ nestedTables.push(nested);
1249
+ contentHeightPt += nested.heightPt;
1250
+ }
1251
+ }
1252
+ const colEnd = colStart + colSpan - 1;
1253
+ const borders = resolveCellBorders(cell.properties.borders, tableProps.borders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighborBorders, aboveNeighborBorders);
1254
+ const totalHeightPt = mergeRole === "middle" || mergeRole === "end" ? 0 : padTopPt + contentHeightPt + padBottomPt;
1255
+ return {
1256
+ widthPt,
1257
+ padTopPt,
1258
+ padRightPt,
1259
+ padBottomPt,
1260
+ padLeftPt,
1261
+ borders,
1262
+ ...cell.properties.shading?.colorHex ? { shadingColorHex: cell.properties.shading.colorHex } : {},
1263
+ lines,
1264
+ ...nestedTables.length > 0 ? { nestedTables } : {},
1265
+ contentHeightPt,
1266
+ totalHeightPt,
1267
+ colStart,
1268
+ colSpan,
1269
+ mergeRole
1270
+ };
1271
+ }
1272
+ var BORDER_STYLE_RANK = {
1273
+ double: 5,
1274
+ thick: 4,
1275
+ single: 3,
1276
+ dashed: 2,
1277
+ dotted: 1,
1278
+ none: 0
1279
+ };
1280
+ function borderWeight(b) {
1281
+ if (!b || b.style === "none") return 0;
1282
+ return b.width ?? .5;
1283
+ }
1284
+ function heavierBorder(a, b) {
1285
+ const wa = borderWeight(a);
1286
+ const wb = borderWeight(b);
1287
+ if (wa !== wb) return wa > wb ? a : b;
1288
+ if (wa === 0) return void 0;
1289
+ return BORDER_STYLE_RANK[a.style] >= BORDER_STYLE_RANK[b.style] ? a : b;
1290
+ }
1291
+ function resolveCellBorders(cellBorders, tableBorders, rowIdx, colStart, colEnd, rowCount, colCount, mergeRole, leftNeighbor, aboveNeighbor) {
1292
+ const isFirstRow = rowIdx === 0;
1293
+ const isLastRow = rowIdx === rowCount - 1;
1294
+ const isFirstCol = colStart === 0;
1295
+ const isLastCol = colEnd === colCount - 1;
1296
+ const out = {};
1297
+ const ownTop = cellBorders?.top ?? (isFirstRow ? tableBorders?.top : tableBorders?.insideH);
1298
+ const ownBottom = cellBorders?.bottom ?? (isLastRow ? tableBorders?.bottom : tableBorders?.insideH);
1299
+ const ownLeft = cellBorders?.left ?? (isFirstCol ? tableBorders?.left : tableBorders?.insideV);
1300
+ const ownRight = cellBorders?.right ?? (isLastCol ? tableBorders?.right : tableBorders?.insideV);
1301
+ const aboveBottom = aboveNeighbor ? aboveNeighbor.bottom ?? tableBorders?.insideH : void 0;
1302
+ const leftRight = leftNeighbor ? leftNeighbor.right ?? tableBorders?.insideV : void 0;
1303
+ const top = isFirstRow ? ownTop : heavierBorder(ownTop, aboveBottom);
1304
+ const left = isFirstCol ? ownLeft : heavierBorder(ownLeft, leftRight);
1305
+ const bottom = ownBottom;
1306
+ const right = ownRight;
1307
+ const suppressTop = mergeRole === "middle" || mergeRole === "end";
1308
+ const suppressBottom = mergeRole === "start" || mergeRole === "middle";
1309
+ if (top && !suppressTop) out.top = top;
1310
+ if (bottom && !suppressBottom) out.bottom = bottom;
1311
+ if (left) out.left = left;
1312
+ if (right) out.right = right;
1313
+ return out;
1314
+ }
1315
+ function paragraphStructType(resolved) {
1316
+ const lvl = resolved.outlineLevel;
1317
+ if (lvl !== void 0 && lvl >= 0 && lvl <= 8) return `H${Math.min(lvl, 5) + 1}`;
1318
+ return headingFromStyleId(resolved.styleId) ?? "P";
1319
+ }
1320
+ function headingFromStyleId(styleId) {
1321
+ if (!styleId) return null;
1322
+ const m = /^Heading\s*([1-9])$/i.exec(styleId);
1323
+ if (m) return `H${Math.min(Number(m[1]), 6)}`;
1324
+ if (/^(Title|Subtitle)$/i.test(styleId)) return "H1";
1325
+ return null;
1326
+ }
1327
+ function dominantParagraphLang(lines) {
1328
+ const counts = /* @__PURE__ */ new Map();
1329
+ for (const line of lines) for (const tok of line.tokens) {
1330
+ if (tok.kind !== "text") continue;
1331
+ const l = tok.resolvedRun.lang;
1332
+ if (l) counts.set(l, (counts.get(l) ?? 0) + tok.text.length);
1333
+ }
1334
+ let best;
1335
+ let bestN = 0;
1336
+ for (const [l, n] of counts) if (n > bestN) {
1337
+ best = l;
1338
+ bestN = n;
1339
+ }
1340
+ return best;
1341
+ }
1342
+ function createFigure(builder, alt, fallback) {
1343
+ const node = builder.create("Figure", builder.root);
1344
+ node.alt = alt && alt.trim().length > 0 ? alt : fallback;
1345
+ return node.id;
1346
+ }
1347
+ function listItemParagraphNode(builder, stack, list) {
1348
+ const lvl = list.level;
1349
+ while (stack.length > 0) {
1350
+ const t = stack[stack.length - 1];
1351
+ if (t.level > lvl || t.level === lvl && t.numId !== list.numId) stack.pop();
1352
+ else break;
1353
+ }
1354
+ let frame = stack[stack.length - 1];
1355
+ if (!frame || frame.level < lvl) {
1356
+ const parent = frame?.lbody ?? builder.root;
1357
+ const listNode = builder.create("L", parent);
1358
+ frame = {
1359
+ level: lvl,
1360
+ numId: list.numId,
1361
+ listNode,
1362
+ lbody: null
1363
+ };
1364
+ stack.push(frame);
1365
+ }
1366
+ const li = builder.create("LI", frame.listNode);
1367
+ const lbody = builder.create("LBody", li);
1368
+ frame.lbody = lbody;
1369
+ return builder.create("P", lbody).id;
1370
+ }
1371
+ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1372
+ if (sectionCtxs.length === 0) return [];
1373
+ const pages = [];
1374
+ let ctx = sectionCtxs[0];
1375
+ let secIdx = 0;
1376
+ let pageInSection = 0;
1377
+ let globalPageIdx = 0;
1378
+ let current = [];
1379
+ let pendingPageBreak = false;
1380
+ let cursorY = ctx.pageHeight - ctx.marginTop;
1381
+ const listStack = [];
1382
+ const flushPage = (force = false) => {
1383
+ if (current.length === 0 && !force) return;
1384
+ const band = bandForPage(pageInSection, globalPageIdx, ctx.titlePg, ctx.evenAndOddHeaders);
1385
+ const header = pickBand(ctx.headerSet, band);
1386
+ const footer = pickBand(ctx.footerSet, band);
1387
+ pages.push({
1388
+ commands: [
1389
+ ...header,
1390
+ ...current,
1391
+ ...footer
1392
+ ],
1393
+ width: pt(ctx.pageWidth),
1394
+ height: pt(ctx.pageHeight)
1395
+ });
1396
+ current = [];
1397
+ pageInSection++;
1398
+ globalPageIdx++;
1399
+ cursorY = ctx.pageHeight - ctx.marginTop;
1400
+ };
1401
+ for (let blockIdx = 0; blockIdx < blocks.length; blockIdx++) {
1402
+ while (secIdx < sectionCtxs.length - 1 && blockIdx >= ctx.endIndex) {
1403
+ flushPage();
1404
+ secIdx++;
1405
+ ctx = sectionCtxs[secIdx];
1406
+ pageInSection = 0;
1407
+ cursorY = ctx.pageHeight - ctx.marginTop;
1408
+ }
1409
+ const block = blocks[blockIdx];
1410
+ if (pendingPageBreak) {
1411
+ pendingPageBreak = false;
1412
+ if (current.length > 0) flushPage();
1413
+ }
1414
+ if (builder && !(block.kind === "paragraph" && block.list)) listStack.length = 0;
1415
+ if (block.kind === "paragraph") {
1416
+ if (block.resolved.pageBreakBefore && current.length > 0) flushPage();
1417
+ cursorY -= block.spacingBeforePt;
1418
+ let structId;
1419
+ if (builder) {
1420
+ structId = block.list ? listItemParagraphNode(builder, listStack, block.list) : builder.create(paragraphStructType(block.resolved), builder.root).id;
1421
+ const lang = dominantParagraphLang(block.lines);
1422
+ if (lang && lang !== defaultLang) builder.node(structId).lang = lang;
1423
+ }
1424
+ for (const line of block.lines) {
1425
+ const h = computeLineHeight(line, block.resolved);
1426
+ if (cursorY - h < ctx.marginBottom && current.length > 0) flushPage();
1427
+ cursorY -= h;
1428
+ const indentLeft = block.resolved.indentLeft + (line.firstLine ? block.resolved.indentFirstLine : 0);
1429
+ const offset = alignmentOffset(block.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
1430
+ current.push({
1431
+ type: "line",
1432
+ line,
1433
+ originX: pt(ctx.marginLeft + indentLeft + offset),
1434
+ baselineY: pt(ctx.pageHeight - (cursorY + lineDescent(line))),
1435
+ ...structId !== void 0 ? { structId } : {}
1436
+ });
1437
+ }
1438
+ cursorY -= block.spacingAfterPt;
1439
+ if (block.pageBreakAfter) pendingPageBreak = true;
1440
+ } else if (block.kind === "image") {
1441
+ const figId = builder ? createFigure(builder, block.altText, "Image") : void 0;
1442
+ cursorY -= block.spacingBeforePt;
1443
+ if (cursorY - block.heightPt < ctx.marginBottom && current.length > 0) flushPage();
1444
+ cursorY -= block.heightPt;
1445
+ current.push({
1446
+ type: "image",
1447
+ x: pt(ctx.marginLeft),
1448
+ y: pt(ctx.pageHeight - cursorY - block.heightPt),
1449
+ width: pt(block.widthPt),
1450
+ height: pt(block.heightPt),
1451
+ imageResourceName: block.resourceName,
1452
+ ...figId !== void 0 ? { structId: figId } : {}
1453
+ });
1454
+ cursorY -= block.spacingAfterPt;
1455
+ } else if (block.kind === "shape") {
1456
+ const figId = builder ? createFigure(builder, block.altText, "Shape") : void 0;
1457
+ cursorY -= block.spacingBeforePt;
1458
+ if (cursorY - block.heightPt < ctx.marginBottom && current.length > 0) flushPage();
1459
+ cursorY -= block.heightPt;
1460
+ const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, ctx.contentWidth);
1461
+ const x = ctx.marginLeft + offset;
1462
+ const transform = flipTransform(buildShapeTransform(x, cursorY, block.widthPt, block.heightPt, block.rotation60k, block.flipH, block.flipV), ctx.pageHeight);
1463
+ current.push({
1464
+ type: "shape",
1465
+ shape: {
1466
+ paths: block.paths,
1467
+ ...block.fillColorHex ? { fillColorHex: block.fillColorHex } : {},
1468
+ ...block.stroke ? { stroke: block.stroke } : {},
1469
+ transform
1470
+ },
1471
+ ...figId !== void 0 ? { structId: figId } : {}
1472
+ });
1473
+ if (block.textLines.length > 0) {
1474
+ const shapeBottom = cursorY;
1475
+ const shapeTop = cursorY + block.heightPt;
1476
+ const innerWidth = Math.max(1, block.widthPt - block.insetLeftPt - block.insetRightPt);
1477
+ let textY;
1478
+ if (block.anchor === "b") textY = shapeBottom + block.insetBottomPt + block.textHeightPt;
1479
+ else if (block.anchor === "ctr") textY = shapeBottom + (block.heightPt + block.textHeightPt) / 2;
1480
+ else textY = shapeTop - block.insetTopPt;
1481
+ for (const line of block.textLines) {
1482
+ const h = computeLineHeight(line, line.resolved);
1483
+ textY -= h;
1484
+ const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, innerWidth);
1485
+ current.push({
1486
+ type: "line",
1487
+ line,
1488
+ originX: pt(x + block.insetLeftPt + lineOffset),
1489
+ baselineY: pt(ctx.pageHeight - (textY + lineDescent(line))),
1490
+ ...figId !== void 0 ? { structId: figId } : {}
1491
+ });
1492
+ }
1493
+ }
1494
+ cursorY -= block.spacingAfterPt;
1495
+ } else if (block.kind === "chart") {
1496
+ const figId = builder ? createFigure(builder, block.altText, "Chart") : void 0;
1497
+ const fig = figId !== void 0 ? { structId: figId } : {};
1498
+ cursorY -= block.spacingBeforePt;
1499
+ if (cursorY - block.heightPt < ctx.marginBottom && current.length > 0) flushPage();
1500
+ cursorY -= block.heightPt;
1501
+ const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, ctx.contentWidth);
1502
+ const x = ctx.marginLeft + offset;
1503
+ const y = cursorY;
1504
+ for (const s of block.layout.shapes) current.push({
1505
+ type: "shape",
1506
+ shape: {
1507
+ paths: s.paths,
1508
+ ...s.fillColorHex ? { fillColorHex: s.fillColorHex } : {},
1509
+ ...s.stroke ? { stroke: s.stroke } : {},
1510
+ transform: flipTransform([
1511
+ 1,
1512
+ 0,
1513
+ 0,
1514
+ 1,
1515
+ x,
1516
+ y
1517
+ ], ctx.pageHeight)
1518
+ },
1519
+ ...fig
1520
+ });
1521
+ for (const t of block.layout.texts) current.push({
1522
+ type: "line",
1523
+ line: t.line,
1524
+ originX: pt(x + t.x),
1525
+ baselineY: pt(ctx.pageHeight - (y + t.y)),
1526
+ ...fig
1527
+ });
1528
+ cursorY -= block.spacingAfterPt;
1529
+ } else {
1530
+ const colCount = block.rows.reduce((max, r) => Math.max(max, r.cells.reduce((s, c) => Math.max(s, c.colStart + c.colSpan), 0)), 0);
1531
+ const headerRows = [];
1532
+ for (const r of block.rows) if (r.isHeader) headerRows.push(r);
1533
+ else break;
1534
+ const headerHeightPt = headerRows.reduce((s, r) => s + r.heightPt, 0);
1535
+ const tableNode = builder ? builder.create("Table", builder.root) : void 0;
1536
+ const tableX = ctx.marginLeft + block.xOffsetPt;
1537
+ for (let ri = 0; ri < block.rows.length; ri++) {
1538
+ const row = block.rows[ri];
1539
+ const isLeadingHeader = ri < headerRows.length;
1540
+ let cellStructIds;
1541
+ if (builder && tableNode) {
1542
+ const b = builder;
1543
+ const tr = b.create("TR", tableNode);
1544
+ cellStructIds = row.cells.map((cell) => {
1545
+ if (cell.mergeRole === "middle" || cell.mergeRole === "end") return void 0;
1546
+ const cellNode = b.create(row.isHeader ? "TH" : "TD", tr);
1547
+ if (row.isHeader) cellNode.scope = "Column";
1548
+ if (cell.colSpan > 1) cellNode.colSpan = cell.colSpan;
1549
+ const rowSpan = tableCellRowSpan(block.rows, ri, cell.colStart);
1550
+ if (rowSpan > 1) cellNode.rowSpan = rowSpan;
1551
+ return b.create("P", cellNode).id;
1552
+ });
1553
+ }
1554
+ const chunks = row.heightPt > ctx.pageContentHeight ? splitRowIntoChunks(row, ctx.pageContentHeight) : [row];
1555
+ for (let ci = 0; ci < chunks.length; ci++) {
1556
+ const chunk = chunks[ci];
1557
+ const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && current.length > 0;
1558
+ const overflow = cursorY - chunk.heightPt < ctx.marginBottom && current.length > 0;
1559
+ if (forcedBreak || overflow) {
1560
+ flushPage();
1561
+ if (!isLeadingHeader && headerRows.length > 0 && cursorY - headerHeightPt - chunk.heightPt >= ctx.marginBottom) for (const hr of headerRows) {
1562
+ emitRowChunk(current, hr, tableX, cursorY, ctx.pageHeight, colCount, void 0);
1563
+ cursorY -= hr.heightPt;
1564
+ }
1565
+ }
1566
+ emitRowChunk(current, chunk, tableX, cursorY, ctx.pageHeight, colCount, cellStructIds);
1567
+ cursorY -= chunk.heightPt;
1568
+ }
1569
+ }
1570
+ }
1571
+ }
1572
+ flushPage();
1573
+ if (pages.length === 0) flushPage(true);
1574
+ return pages;
1575
+ }
1576
+ function tableCellRowSpan(rows, startRowIdx, colStart) {
1577
+ let span = 1;
1578
+ for (let r = startRowIdx + 1; r < rows.length; r++) {
1579
+ const cont = rows[r].cells.find((c) => c.colStart === colStart && (c.mergeRole === "middle" || c.mergeRole === "end"));
1580
+ if (!cont) break;
1581
+ span++;
1582
+ if (cont.mergeRole === "end") break;
1583
+ }
1584
+ return span;
1585
+ }
1586
+ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellStructIds) {
1587
+ const rowTop = cursorY;
1588
+ const rowBottom = cursorY - row.heightPt;
1589
+ for (let i = 0; i < row.cells.length; i++) {
1590
+ const cell = row.cells[i];
1591
+ const cellX = marginLeft + (row.columnXOffsets[i] ?? 0);
1592
+ const structId = cellStructIds?.[i];
1593
+ if (cell.shadingColorHex && cell.mergeRole !== "middle" && cell.mergeRole !== "end") out.push({
1594
+ type: "fill",
1595
+ x: pt(cellX),
1596
+ y: pt(pageHeight - rowBottom - row.heightPt),
1597
+ width: pt(cell.widthPt),
1598
+ height: pt(row.heightPt),
1599
+ fillColorHex: cell.shadingColorHex
1600
+ });
1601
+ emitCellBorders(out, cell, cellX, rowBottom, row.heightPt, pageHeight, row.rowIdx, row.rowCount, colCount);
1602
+ if (cell.mergeRole === "middle" || cell.mergeRole === "end") continue;
1603
+ let textY = rowTop - cell.padTopPt;
1604
+ for (const line of cell.lines) {
1605
+ const h = computeLineHeight(line, line.resolved);
1606
+ textY -= h;
1607
+ const offset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, cell.widthPt - cell.padLeftPt - cell.padRightPt);
1608
+ out.push({
1609
+ type: "line",
1610
+ line,
1611
+ originX: pt(cellX + cell.padLeftPt + offset),
1612
+ baselineY: pt(pageHeight - (textY + lineDescent(line))),
1613
+ ...structId !== void 0 ? { structId } : {}
1614
+ });
1615
+ }
1616
+ if (cell.nestedTables) {
1617
+ const nestedX = cellX + cell.padLeftPt;
1618
+ for (const nt of cell.nestedTables) for (const nrow of nt.rows) {
1619
+ const nestedIds = structId !== void 0 ? nrow.cells.map(() => structId) : void 0;
1620
+ emitRowChunk(out, nrow, nestedX, textY, pageHeight, nt.colCount, nestedIds);
1621
+ textY -= nrow.heightPt;
1622
+ }
1623
+ }
1624
+ }
1625
+ }
1626
+ function splitRowIntoChunks(row, capacity) {
1627
+ if (row.cells.some((c) => c.nestedTables && c.nestedTables.length > 0)) return [row];
1628
+ const queues = row.cells.map((c) => ({
1629
+ remaining: [...c.lines],
1630
+ template: c
1631
+ }));
1632
+ const anyHasLines = () => queues.some((q) => q.template.mergeRole === "middle" || q.template.mergeRole === "end" ? false : q.remaining.length > 0);
1633
+ const out = [];
1634
+ let isFirst = true;
1635
+ let safety = 1e3;
1636
+ while (anyHasLines() && safety-- > 0) {
1637
+ const chunkCells = [];
1638
+ let chunkHeight = 0;
1639
+ for (let i = 0; i < queues.length; i++) {
1640
+ const q = queues[i];
1641
+ const tpl = q.template;
1642
+ if (tpl.mergeRole === "middle" || tpl.mergeRole === "end") {
1643
+ chunkCells.push(tpl);
1644
+ continue;
1645
+ }
1646
+ const padTop = isFirst ? tpl.padTopPt : 0;
1647
+ const capacityForLines = Math.max(0, capacity - padTop);
1648
+ const taken = [];
1649
+ let takenHeight = 0;
1650
+ while (q.remaining.length > 0) {
1651
+ const next = q.remaining[0];
1652
+ const lh = computeLineHeight(next, next.resolved);
1653
+ if (taken.length > 0 && takenHeight + lh > capacityForLines) break;
1654
+ taken.push(next);
1655
+ takenHeight += lh;
1656
+ q.remaining.shift();
1657
+ }
1658
+ const cellHeight = padTop + takenHeight;
1659
+ const chunkCell = {
1660
+ ...tpl,
1661
+ padTopPt: padTop,
1662
+ padBottomPt: 0,
1663
+ lines: taken,
1664
+ contentHeightPt: takenHeight,
1665
+ totalHeightPt: cellHeight
1666
+ };
1667
+ chunkCells.push(chunkCell);
1668
+ if (cellHeight > chunkHeight) chunkHeight = cellHeight;
1669
+ }
1670
+ const isLast = !anyHasLines();
1671
+ if (isLast) for (let i = 0; i < chunkCells.length; i++) {
1672
+ const tpl = queues[i].template;
1673
+ if (tpl.mergeRole === "middle" || tpl.mergeRole === "end") continue;
1674
+ const cell = chunkCells[i];
1675
+ const total = cell.padTopPt + cell.contentHeightPt + tpl.padBottomPt;
1676
+ chunkCells[i] = {
1677
+ ...cell,
1678
+ padBottomPt: tpl.padBottomPt,
1679
+ totalHeightPt: total
1680
+ };
1681
+ if (total > chunkHeight) chunkHeight = total;
1682
+ }
1683
+ for (let i = 0; i < chunkCells.length; i++) {
1684
+ const cell = chunkCells[i];
1685
+ const src = cell.borders;
1686
+ const borders = {
1687
+ ...isFirst && src.top ? { top: src.top } : {},
1688
+ ...isLast && src.bottom ? { bottom: src.bottom } : {},
1689
+ ...src.left ? { left: src.left } : {},
1690
+ ...src.right ? { right: src.right } : {}
1691
+ };
1692
+ chunkCells[i] = {
1693
+ ...cell,
1694
+ borders
1695
+ };
1696
+ }
1697
+ out.push({
1698
+ heightPt: chunkHeight,
1699
+ cells: chunkCells,
1700
+ columnXOffsets: row.columnXOffsets,
1701
+ rowIdx: row.rowIdx,
1702
+ rowCount: row.rowCount
1703
+ });
1704
+ isFirst = false;
1705
+ }
1706
+ return out.length > 0 ? out : [row];
1707
+ }
1708
+ function emitCellBorders(out, cell, cellX, cellY, rowHeight, pageHeight, rowIdx, rowCount, colCount) {
1709
+ const pushSide = (side, border) => {
1710
+ if (!border || border.style === "none") return;
1711
+ const sz = border.width ?? DEFAULT_BORDER_SIZE_EIGHTH * EIGHTH_PT;
1712
+ out.push({
1713
+ type: "border",
1714
+ side,
1715
+ x: pt(cellX),
1716
+ y: pt(pageHeight - cellY - rowHeight),
1717
+ width: pt(cell.widthPt),
1718
+ height: pt(rowHeight),
1719
+ borderSizePt: sz,
1720
+ borderColorHex: border.colorHex ?? "000000"
1721
+ });
1722
+ };
1723
+ pushSide("top", cell.borders.top);
1724
+ pushSide("left", cell.borders.left);
1725
+ if (rowIdx === rowCount - 1) pushSide("bottom", cell.borders.bottom);
1726
+ if (cell.colStart + cell.colSpan - 1 === colCount - 1) pushSide("right", cell.borders.right);
1727
+ }
1728
+ //#endregion
1729
+ export { layoutStyledDocument };