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