reamkit 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +11 -5
  2. package/dist/esm/{layout → core}/arc-to-bezier.d.ts +1 -1
  3. package/dist/esm/{layout → core}/arc-to-bezier.js +2 -2
  4. package/dist/esm/core/converter/project.js +2 -0
  5. package/dist/esm/core/converter/ream.js +4 -3
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +60 -0
  8. package/dist/esm/{layout → core/drawingml}/chart-geometry.d.ts +2 -2
  9. package/dist/esm/{layout → core/drawingml}/chart-geometry.js +79 -117
  10. package/dist/esm/core/drawingml/chart-parser.d.ts +3 -0
  11. package/dist/esm/core/drawingml/chart-parser.js +28 -1
  12. package/dist/esm/{layout → core/drawingml}/preset-geometry.d.ts +2 -2
  13. package/dist/esm/{layout → core/drawingml}/preset-geometry.js +3 -3
  14. package/dist/esm/core/drawingml/shape-render.d.ts +8 -0
  15. package/dist/esm/core/drawingml/shape-render.js +71 -0
  16. package/dist/esm/core/ir/features.d.ts +1 -0
  17. package/dist/esm/core/ir/features.js +1 -0
  18. package/dist/esm/core/ir/flow.d.ts +3 -0
  19. package/dist/esm/core/links.d.ts +6 -0
  20. package/dist/esm/core/links.js +19 -0
  21. package/dist/esm/core/numbering/apply.js +5 -2
  22. package/dist/esm/core/opc/index.d.ts +1 -0
  23. package/dist/esm/core/opc/package.js +3 -2
  24. package/dist/esm/core/opc/relationship-types.d.ts +1 -0
  25. package/dist/esm/core/opc/relationship-types.js +5 -2
  26. package/dist/esm/core/style-cascade/index.d.ts +1 -0
  27. package/dist/esm/core/style-cascade/index.js +1 -0
  28. package/dist/esm/core/style-cascade/table.d.ts +2 -0
  29. package/dist/esm/core/style-cascade/table.js +161 -0
  30. package/dist/esm/core/vector.d.ts +2 -0
  31. package/dist/esm/core/vector.js +19 -1
  32. package/dist/esm/excel/print-model.d.ts +3 -0
  33. package/dist/esm/excel/print-model.js +4 -8
  34. package/dist/esm/excel/sheet-drawing.d.ts +9 -0
  35. package/dist/esm/excel/sheet-drawing.js +113 -0
  36. package/dist/esm/excel/worksheet-parser.d.ts +1 -0
  37. package/dist/esm/excel/worksheet-parser.js +4 -1
  38. package/dist/esm/excel/xlsx-reader.js +41 -0
  39. package/dist/esm/excel/xlsx-to-pdf.js +25 -18
  40. package/dist/esm/html/html-writer.js +188 -16
  41. package/dist/esm/index.d.ts +1 -0
  42. package/dist/esm/layout/page-doc.d.ts +4 -0
  43. package/dist/esm/layout/styled-layout.d.ts +21 -3
  44. package/dist/esm/layout/styled-layout.js +745 -259
  45. package/dist/esm/pdf/encryption.d.ts +34 -0
  46. package/dist/esm/pdf/encryption.js +143 -0
  47. package/dist/esm/pdf/index.d.ts +2 -1
  48. package/dist/esm/pdf/struct-tree.d.ts +7 -1
  49. package/dist/esm/pdf/struct-tree.js +22 -6
  50. package/dist/esm/pdf/styled-page-emitter.d.ts +3 -1
  51. package/dist/esm/pdf/styled-page-emitter.js +185 -35
  52. package/dist/esm/pdf/styled-page-renderer.d.ts +1 -0
  53. package/dist/esm/pdf/styled-page-renderer.js +17 -2
  54. package/dist/esm/pdf/writer.d.ts +2 -0
  55. package/dist/esm/pdf/writer.js +6 -1
  56. package/dist/esm/pdf/xmp.d.ts +1 -0
  57. package/dist/esm/pdf/xmp.js +4 -0
  58. package/dist/esm/svg/svg-writer.js +2 -6
  59. package/dist/esm/word/document-parser.d.ts +3 -0
  60. package/dist/esm/word/document-parser.js +214 -23
  61. package/dist/esm/word/docx-reader.js +37 -15
  62. package/dist/esm/word/docx-to-pdf.js +23 -13
  63. package/dist/esm/word/drawing-parser.d.ts +4 -1
  64. package/dist/esm/word/drawing-parser.js +53 -2
  65. package/dist/esm/word/index.d.ts +2 -2
  66. package/dist/esm/word/styles-parser.js +134 -1
  67. package/dist/esm/word/table-parser.js +35 -0
  68. package/package.json +1 -1
@@ -11,11 +11,12 @@ import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numberin
11
11
  import { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN } from "../core/style-cascade/types.js";
12
12
  import { resolveParagraphProperties, resolveRunProperties } from "../core/style-cascade/resolver.js";
13
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";
14
+ import { PathBuilder, flipTransform } from "../core/vector.js";
15
+ import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
16
+ import { buildChartScene } from "../core/drawingml/chart-geometry.js";
17
17
  import { layoutMath, mathGlyphSegments, variantStyle } from "./math-layout.js";
18
- import { customPaths, presetPaths, rectPath } from "./preset-geometry.js";
18
+ import { rectPath } from "../core/drawingml/preset-geometry.js";
19
+ import { buildShapePaths, buildShapeTransform, buildStroke } from "../core/drawingml/shape-render.js";
19
20
  import { StructTreeBuilder } from "../pdf/struct-tree.js";
20
21
  //#region src/layout/styled-layout.ts
21
22
  function parsePdfAProfile(pdfA) {
@@ -32,23 +33,205 @@ var TWIP_TO_PT = 1 / 20;
32
33
  var EIGHTH_PT = 1 / 8;
33
34
  var DEFAULT_CELL_PADDING_TWIPS = 108;
34
35
  var DEFAULT_BORDER_SIZE_EIGHTH = 4;
36
+ var FOOTNOTE_RULE_PT = .75;
37
+ var FOOTNOTE_RULE_GAP_ABOVE = 4;
38
+ var FOOTNOTE_SEPARATOR_HEIGHT = 10;
39
+ var FLOAT_TEXT_GAP = 6;
40
+ var MIN_WRAP_WIDTH = 36;
41
+ function isOutOfFlowFloat(f) {
42
+ return f !== void 0 && f.wrap !== "topAndBottom";
43
+ }
44
+ var FOOTNOTE_RULE_WIDTH = 144;
45
+ function assignNoteNumbers(body) {
46
+ const footnotes = /* @__PURE__ */ new Map();
47
+ const endnotes = /* @__PURE__ */ new Map();
48
+ const paragraphFootnotes = /* @__PURE__ */ new Set();
49
+ const numberRun = (run, n) => ({
50
+ ...run,
51
+ text: String(n),
52
+ properties: {
53
+ ...run.properties,
54
+ verticalAlign: "superscript"
55
+ }
56
+ });
57
+ const mapParagraph = (paragraph) => {
58
+ if (!paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0)) return paragraph;
59
+ return {
60
+ ...paragraph,
61
+ runs: paragraph.runs.map((run) => {
62
+ if (run.footnoteRef !== void 0) {
63
+ let n = footnotes.get(run.footnoteRef);
64
+ if (n === void 0) {
65
+ n = footnotes.size + 1;
66
+ footnotes.set(run.footnoteRef, n);
67
+ }
68
+ return numberRun(run, n);
69
+ }
70
+ if (run.endnoteRef !== void 0) {
71
+ let n = endnotes.get(run.endnoteRef);
72
+ if (n === void 0) {
73
+ n = endnotes.size + 1;
74
+ endnotes.set(run.endnoteRef, n);
75
+ }
76
+ return numberRun(run, n);
77
+ }
78
+ return run;
79
+ })
80
+ };
81
+ };
82
+ const mapElement = (el, topLevel) => {
83
+ if (el.kind === "paragraph") {
84
+ if (topLevel) {
85
+ for (const r of el.paragraph.runs) if (r.footnoteRef !== void 0) paragraphFootnotes.add(r.footnoteRef);
86
+ }
87
+ return {
88
+ kind: "paragraph",
89
+ paragraph: mapParagraph(el.paragraph)
90
+ };
91
+ }
92
+ if (el.kind === "table") return {
93
+ kind: "table",
94
+ table: {
95
+ ...el.table,
96
+ rows: el.table.rows.map((row) => ({
97
+ ...row,
98
+ cells: row.cells.map((cell) => ({
99
+ ...cell,
100
+ content: cell.content.map((c) => mapElement(c, false))
101
+ }))
102
+ }))
103
+ }
104
+ };
105
+ if (el.kind === "shape" && el.shape.text) return {
106
+ kind: "shape",
107
+ shape: {
108
+ ...el.shape,
109
+ text: {
110
+ ...el.shape.text,
111
+ content: el.shape.text.content.map((c) => mapElement(c, false))
112
+ }
113
+ }
114
+ };
115
+ return el;
116
+ };
117
+ const hasRefs = (els) => els.some((el) => {
118
+ if (el.kind === "paragraph") return el.paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0);
119
+ if (el.kind === "table") return el.table.rows.some((row) => row.cells.some((c) => hasRefs(c.content)));
120
+ if (el.kind === "shape" && el.shape.text) return hasRefs(el.shape.text.content);
121
+ return false;
122
+ });
123
+ if (!hasRefs(body)) return {
124
+ body,
125
+ footnotes,
126
+ endnotes,
127
+ deferredFootnotes: []
128
+ };
129
+ return {
130
+ body: body.map((el) => mapElement(el, true)),
131
+ footnotes,
132
+ endnotes,
133
+ deferredFootnotes: [...footnotes.keys()].filter((id) => !paragraphFootnotes.has(id))
134
+ };
135
+ }
136
+ function substituteNoteNumber(content, n) {
137
+ const hasPlaceholder = content.some((el) => el.kind === "paragraph" && el.paragraph.runs.some((r) => r.noteNumber));
138
+ const out = content.map((el) => {
139
+ if (el.kind !== "paragraph") return el;
140
+ if (!el.paragraph.runs.some((r) => r.noteNumber)) return el;
141
+ return {
142
+ kind: "paragraph",
143
+ paragraph: {
144
+ ...el.paragraph,
145
+ runs: el.paragraph.runs.map((r) => r.noteNumber ? {
146
+ ...r,
147
+ text: String(n),
148
+ properties: {
149
+ ...r.properties,
150
+ verticalAlign: "superscript"
151
+ }
152
+ } : r)
153
+ }
154
+ };
155
+ });
156
+ if (hasPlaceholder || out.length === 0) return out;
157
+ const first = out[0];
158
+ if (first.kind !== "paragraph") return out;
159
+ return [{
160
+ kind: "paragraph",
161
+ paragraph: {
162
+ ...first.paragraph,
163
+ runs: [{
164
+ text: `${n} `,
165
+ properties: { verticalAlign: "superscript" }
166
+ }, ...first.paragraph.runs]
167
+ }
168
+ }, ...out.slice(1)];
169
+ }
35
170
  function layoutStyledDocument(body, options) {
36
171
  const sectionList = resolveSectionList(body, options);
37
- const numberedBody = applyNumbering(body, options.numbering);
172
+ const noteAssigned = assignNoteNumbers(applyNumbering(body, options.numbering));
173
+ const numberedBody = noteAssigned.body;
38
174
  const numberedHeadersFooters = applyNumberingToHeadersFooters(options.headersFooters, options.numbering);
39
175
  const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
40
- const tagged = options.tagged === true || (pdfaProfile?.tagged ?? false);
176
+ const tagged = options.tagged === true || options.pdfUA === true || (pdfaProfile?.tagged ?? false);
41
177
  const structBuilder = tagged ? new StructTreeBuilder() : void 0;
42
178
  const fontResources = collectFontResources(numberedBody, numberedHeadersFooters, options);
43
179
  const imageResources = collectImageResources(numberedBody, numberedHeadersFooters, options);
44
180
  const sectionCtxs = sectionList.map((s) => buildSectionContext(s, options, numberedHeadersFooters, fontResources));
45
181
  let sectionIdx = 0;
182
+ const blocks = numberedBody.map((el, idx) => {
183
+ while (sectionIdx < sectionCtxs.length - 1 && idx >= sectionCtxs[sectionIdx].endIndex) sectionIdx++;
184
+ const ctx = sectionCtxs[sectionIdx];
185
+ return layoutBodyElement(el, options, fontResources, imageResources, ctx.columns ? ctx.columns[0].widthPt : ctx.contentWidth, ctx.pageContentHeight);
186
+ });
187
+ const noteBlockCache = /* @__PURE__ */ new Map();
188
+ const notePlan = options.footnotes && noteAssigned.footnotes.size > 0 ? {
189
+ numbers: noteAssigned.footnotes,
190
+ layout: (sectionCtx, id) => {
191
+ let byId = noteBlockCache.get(sectionCtx);
192
+ if (!byId) {
193
+ byId = /* @__PURE__ */ new Map();
194
+ noteBlockCache.set(sectionCtx, byId);
195
+ }
196
+ if (byId.has(id)) return byId.get(id);
197
+ const content = options.footnotes?.get(id);
198
+ const n = noteAssigned.footnotes.get(id);
199
+ let laid = void 0;
200
+ if (content && n !== void 0 && !noteAssigned.deferredFootnotes.includes(id)) {
201
+ const noteBlocks = substituteNoteNumber(content, n).map((el) => layoutBodyElement(el, options, fontResources, imageResources, sectionCtx.contentWidth, sectionCtx.pageContentHeight));
202
+ laid = {
203
+ blocks: noteBlocks,
204
+ heightPt: noteBlocks.reduce((sum, b) => sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : 0), 0)
205
+ };
206
+ }
207
+ byId.set(id, laid);
208
+ return laid;
209
+ }
210
+ } : void 0;
211
+ const lastCtx = sectionCtxs[sectionCtxs.length - 1];
212
+ if (lastCtx) {
213
+ const tailNotes = [];
214
+ for (const id of noteAssigned.deferredFootnotes) {
215
+ const content = options.footnotes?.get(id);
216
+ const n = noteAssigned.footnotes.get(id);
217
+ if (content && n !== void 0) tailNotes.push({
218
+ content,
219
+ n
220
+ });
221
+ }
222
+ for (const [id, n] of noteAssigned.endnotes) {
223
+ const content = options.endnotes?.get(id);
224
+ if (content) tailNotes.push({
225
+ content,
226
+ n
227
+ });
228
+ }
229
+ for (const note of tailNotes.sort((a, b) => a.n - b.n)) for (const el of substituteNoteNumber(note.content, note.n)) blocks.push(layoutBodyElement(el, options, fontResources, imageResources, lastCtx.contentWidth, lastCtx.pageContentHeight));
230
+ }
231
+ const bookmarks = /* @__PURE__ */ new Map();
232
+ const reflowParagraph = (paragraph, width, widths) => layoutParagraphBlock(paragraph, options, fontResources, imageResources, width, widths);
46
233
  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"),
234
+ pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks, reflowParagraph),
52
235
  resources: options.resources ?? new ResourceStore(),
53
236
  fontResources,
54
237
  imageResources,
@@ -56,7 +239,8 @@ function layoutStyledDocument(body, options) {
56
239
  structBuilder,
57
240
  sectionCtxs,
58
241
  pdfaProfile,
59
- tagged
242
+ tagged,
243
+ bookmarks
60
244
  }
61
245
  };
62
246
  }
@@ -99,6 +283,7 @@ function buildSectionContext(section, options, headersFooters, fontResources) {
99
283
  const contentWidth = dims.pageWidth - dims.marginLeft - dims.marginRight;
100
284
  const headerSet = layoutHeaderSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.pageHeight, dims.headerOffsetPt);
101
285
  const footerSet = layoutFooterSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.pageHeight, dims.footerOffsetPt);
286
+ const columns = buildColumnGeometry(section.properties.columns, contentWidth);
102
287
  return {
103
288
  endIndex: section.endIndex,
104
289
  properties: section.properties,
@@ -109,12 +294,35 @@ function buildSectionContext(section, options, headersFooters, fontResources) {
109
294
  marginBottom: dims.marginBottom,
110
295
  contentWidth,
111
296
  pageContentHeight: dims.pageHeight - dims.marginTop - dims.marginBottom,
297
+ ...columns ? { columns } : {},
112
298
  headerSet,
113
299
  footerSet,
114
300
  titlePg: section.properties.titlePg === true,
115
301
  evenAndOddHeaders: section.properties.evenAndOddHeaders === true
116
302
  };
117
303
  }
304
+ function buildColumnGeometry(cols, contentWidth) {
305
+ if (!cols || cols.count <= 1) return void 0;
306
+ const out = [];
307
+ if (cols.explicit && cols.explicit.length > 1) {
308
+ let x = 0;
309
+ for (const c of cols.explicit) {
310
+ out.push({
311
+ xOffsetPt: x,
312
+ widthPt: c.widthPt
313
+ });
314
+ x += c.widthPt + c.spacePt;
315
+ }
316
+ return out;
317
+ }
318
+ const width = (contentWidth - cols.spacePt * (cols.count - 1)) / cols.count;
319
+ if (width <= 0) return void 0;
320
+ for (let i = 0; i < cols.count; i++) out.push({
321
+ xOffsetPt: i * (width + cols.spacePt),
322
+ widthPt: width
323
+ });
324
+ return out;
325
+ }
118
326
  function refByType(refs, type) {
119
327
  if (!refs || refs.length === 0) return void 0;
120
328
  return refs.find((r) => r.type === type);
@@ -128,10 +336,17 @@ function markPagination(cmds) {
128
336
  function layoutHeaderSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, pageHeight, headerOffsetPt) {
129
337
  const band = (type) => {
130
338
  const ref = refByType(section.headers, type);
131
- if (!ref) return [];
339
+ if (!ref) return { commands: [] };
132
340
  const content = headersFooters.get(ref.relationshipId);
133
- if (!content) return [];
134
- return markPagination(drawBlocksSequentially(laidOutBlocksFor(content, options, fontResources, contentWidth), marginLeft, pageHeight - headerOffsetPt, pageHeight));
341
+ if (!content) return { commands: [] };
342
+ const render = (c) => {
343
+ return markPagination(drawBlocksSequentially(laidOutBlocksFor(c, options, fontResources, contentWidth), marginLeft, pageHeight - headerOffsetPt, pageHeight));
344
+ };
345
+ if (contentHasPageFields(content)) return {
346
+ commands: [],
347
+ renderDynamic: (n, total) => render(substitutePageFields(content, n, total))
348
+ };
349
+ return { commands: render(content) };
135
350
  };
136
351
  return {
137
352
  default: band("default"),
@@ -142,11 +357,18 @@ function layoutHeaderSet(section, headersFooters, options, fontResources, conten
142
357
  function layoutFooterSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, pageHeight, footerOffsetPt) {
143
358
  const band = (type) => {
144
359
  const ref = refByType(section.footers, type);
145
- if (!ref) return [];
360
+ if (!ref) return { commands: [] };
146
361
  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));
362
+ if (!content) return { commands: [] };
363
+ const render = (c) => {
364
+ const blocks = laidOutBlocksFor(c, options, fontResources, contentWidth);
365
+ return markPagination(drawBlocksSequentially(blocks, marginLeft, footerOffsetPt + blocks.reduce((sum, b) => sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : b.heightPt), 0), pageHeight));
366
+ };
367
+ if (contentHasPageFields(content)) return {
368
+ commands: [],
369
+ renderDynamic: (n, total) => render(substitutePageFields(content, n, total))
370
+ };
371
+ return { commands: render(content) };
150
372
  };
151
373
  return {
152
374
  default: band("default"),
@@ -154,9 +376,30 @@ function layoutFooterSet(section, headersFooters, options, fontResources, conten
154
376
  even: band("even")
155
377
  };
156
378
  }
379
+ function contentHasPageFields(content) {
380
+ for (const el of content) if (el.kind === "paragraph" && el.paragraph.runs.some((r) => r.field !== void 0)) return true;
381
+ return false;
382
+ }
383
+ function substitutePageFields(content, pageNumber, totalPages) {
384
+ return content.map((el) => {
385
+ if (el.kind !== "paragraph") return el;
386
+ if (!el.paragraph.runs.some((r) => r.field !== void 0)) return el;
387
+ return {
388
+ kind: "paragraph",
389
+ paragraph: {
390
+ ...el.paragraph,
391
+ runs: el.paragraph.runs.map((r) => r.field === void 0 ? r : {
392
+ ...r,
393
+ text: String(r.field === "PAGE" ? pageNumber : totalPages)
394
+ })
395
+ }
396
+ };
397
+ });
398
+ }
157
399
  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;
400
+ const has = (e) => e.commands.length > 0 || e.renderDynamic !== void 0;
401
+ if (band === "first") return has(set.first) ? set.first : set.default;
402
+ if (band === "even") return has(set.even) ? set.even : set.default;
160
403
  return set.default;
161
404
  }
162
405
  function bandForPage(pageInSection, globalPageIdx, titlePg, evenAndOddHeaders) {
@@ -174,7 +417,6 @@ function layoutBodyElement(el, options, fontResources, imageResources, contentWi
174
417
  if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight);
175
418
  return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight);
176
419
  }
177
- var EMU_PER_PT = 12700;
178
420
  function layoutImageBlock(image, imageResources, contentWidth) {
179
421
  let widthPt = image.width;
180
422
  let heightPt = image.height;
@@ -193,12 +435,10 @@ function layoutImageBlock(image, imageResources, contentWidth) {
193
435
  resourceName: res?.resourceName ?? "",
194
436
  spacingBeforePt: image.paragraphProperties.spacingBefore ?? 0,
195
437
  spacingAfterPt: image.paragraphProperties.spacingAfter ?? 0,
196
- ...image.altText ? { altText: image.altText } : {}
438
+ ...image.altText ? { altText: image.altText } : {},
439
+ ...image.float ? { float: image.float } : {}
197
440
  };
198
441
  }
199
- var DEFAULT_LINE_WIDTH_EMU = 9525;
200
- var DEFAULT_INSET_LR_EMU = 91440;
201
- var DEFAULT_INSET_TB_EMU = 45720;
202
442
  function layoutShapeBlock(shape, options, fontResources, imageResources, contentWidth, maxHeight) {
203
443
  let widthPt = shape.width;
204
444
  let heightPt = shape.height;
@@ -218,10 +458,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
218
458
  const t = shape.transform;
219
459
  const pp = shape.paragraphProperties;
220
460
  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;
461
+ const insetLeftPt = text?.insetLeft ?? 7.2;
462
+ const insetRightPt = text?.insetRight ?? 7.2;
463
+ const insetTopPt = text?.insetTop ?? 3.6;
464
+ const insetBottomPt = text?.insetBottom ?? 3.6;
225
465
  const textLines = [];
226
466
  let textHeightPt = 0;
227
467
  if (text && text.content.length > 0) {
@@ -256,82 +496,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
256
496
  insetTopPt,
257
497
  insetBottomPt,
258
498
  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 } : {}
499
+ ...shape.altText ? { altText: shape.altText } : {},
500
+ ...shape.float ? { float: shape.float } : {}
277
501
  };
278
502
  }
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
503
  function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight) {
336
504
  let widthPt = block.width;
337
505
  let heightPt = block.height;
@@ -362,7 +530,8 @@ function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight
362
530
  resolvedAlignment: pp.alignment ?? "left",
363
531
  spacingBeforePt: pp.spacingBefore ?? 0,
364
532
  spacingAfterPt: pp.spacingAfter ?? 0,
365
- ...altText ? { altText } : {}
533
+ ...altText ? { altText } : {},
534
+ ...block.float ? { float: block.float } : {}
366
535
  };
367
536
  }
368
537
  function buildChartLayout(chart, widthPt, heightPt, font) {
@@ -486,6 +655,8 @@ function collectImageResources(body, headersFooters, options) {
486
655
  };
487
656
  visit(body);
488
657
  for (const hf of headersFooters.values()) visit(hf);
658
+ for (const note of options.footnotes?.values() ?? []) visit(note);
659
+ for (const note of options.endnotes?.values() ?? []) visit(note);
489
660
  const flattenAlpha = options.pdfA ? parsePdfAProfile(options.pdfA).part === 1 : false;
490
661
  let counter = 0;
491
662
  for (const resourceId of seen) {
@@ -505,7 +676,7 @@ function collectImageResources(body, headersFooters, options) {
505
676
  }
506
677
  return out;
507
678
  }
508
- function drawBlocksSequentially(blocks, startX, startY, pageHeight) {
679
+ function drawBlocksSequentially(blocks, startX, startY, pageHeight, structId) {
509
680
  const out = [];
510
681
  let cursorY = startY;
511
682
  for (const block of blocks) {
@@ -520,7 +691,8 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight) {
520
691
  type: "line",
521
692
  line,
522
693
  originX: pt(startX + indentLeft + offset),
523
- baselineY: pt(pageHeight - (cursorY + lineDescent(line)))
694
+ baselineY: pt(pageHeight - (cursorY + lineDescent(line))),
695
+ ...structId !== void 0 ? { structId } : {}
524
696
  });
525
697
  }
526
698
  cursorY -= block.spacingAfterPt;
@@ -600,6 +772,20 @@ function collectFontResources(body, headersFooters, options) {
600
772
  continue;
601
773
  }
602
774
  if (run.inlineImage && !run.text) continue;
775
+ if (run.field !== void 0) {
776
+ addRun({
777
+ text: `${run.text}0123456789`,
778
+ properties: run.properties
779
+ }, el.paragraph);
780
+ continue;
781
+ }
782
+ if (run.footnoteRef !== void 0 || run.endnoteRef !== void 0 || run.noteNumber) {
783
+ addRun({
784
+ text: `${run.text}0123456789 `,
785
+ properties: run.properties
786
+ }, el.paragraph);
787
+ continue;
788
+ }
603
789
  addRun(run, el.paragraph);
604
790
  }
605
791
  else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
@@ -629,6 +815,8 @@ function collectFontResources(body, headersFooters, options) {
629
815
  };
630
816
  visit(body);
631
817
  for (const hf of headersFooters.values()) visit(hf);
818
+ for (const note of options.footnotes?.values() ?? []) visit(note);
819
+ for (const note of options.endnotes?.values() ?? []) visit(note);
632
820
  if (used.size === 0) {
633
821
  const regular = options.registry.resolveByStyle(false, false);
634
822
  used.set(regular.variant, {
@@ -650,14 +838,14 @@ function collectFontResources(body, headersFooters, options) {
650
838
  }
651
839
  return out;
652
840
  }
653
- function layoutParagraphBlock(paragraph, options, fontResources, imageResources, contentWidth) {
841
+ function layoutParagraphBlock(paragraph, options, fontResources, imageResources, contentWidth, lineWidths) {
654
842
  const baseResolved = resolveParagraphProperties(paragraph.properties, options.styles);
655
843
  const baseDir = paragraphBaseDirection(paragraph, baseResolved);
656
844
  const resolved = baseDir === "rtl" && baseResolved.alignment === "left" ? {
657
845
  ...baseResolved,
658
846
  alignment: "right"
659
847
  } : baseResolved;
660
- const lines = wrap(tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir), paragraphMaxWidth(resolved, contentWidth, true), paragraphMaxWidth(resolved, contentWidth, false), resolved, options.hyphenator);
848
+ const lines = wrap(tokenizeParagraph(paragraph, options, fontResources, imageResources, contentWidth, baseDir), paragraphMaxWidth(resolved, contentWidth, true), paragraphMaxWidth(resolved, contentWidth, false), resolved, options.hyphenator, lineWidths);
661
849
  let heightPt = 0;
662
850
  for (const line of lines) heightPt += computeLineHeight(line, resolved);
663
851
  const numbering = paragraph.properties.numbering;
@@ -672,7 +860,9 @@ function layoutParagraphBlock(paragraph, options, fontResources, imageResources,
672
860
  numId: numbering.numId,
673
861
  level: numbering.ilvl
674
862
  } } : {},
675
- ...paragraph.runs.some((r) => r.pageBreak) ? { pageBreakAfter: true } : {}
863
+ ...paragraph.runs.some((r) => r.pageBreak) ? { pageBreakAfter: true } : {},
864
+ ...paragraph.bookmarks && paragraph.bookmarks.length > 0 ? { bookmarks: paragraph.bookmarks } : {},
865
+ source: paragraph
676
866
  };
677
867
  }
678
868
  function resolveMathItems(items, fontFor) {
@@ -801,6 +991,10 @@ function tokenizePlansLtr(plans) {
801
991
  kind: "text",
802
992
  text: t.text,
803
993
  isSpace: t.isSpace,
994
+ ...plan.run.href !== void 0 ? { href: plan.run.href } : {},
995
+ ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
996
+ ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
997
+ ...plan.run.listMarker ? { listMarker: true } : {},
804
998
  resolvedRun: plan.resolvedRun,
805
999
  font: plan.font,
806
1000
  fontSizePt: plan.fontSizePt,
@@ -851,6 +1045,10 @@ function tokenizePlansBidi(plans, realLevels) {
851
1045
  kind: "text",
852
1046
  text,
853
1047
  isSpace: curSpace,
1048
+ ...plan.run.href !== void 0 ? { href: plan.run.href } : {},
1049
+ ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
1050
+ ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
1051
+ ...plan.run.listMarker ? { listMarker: true } : {},
854
1052
  resolvedRun: plan.resolvedRun,
855
1053
  font: plan.font,
856
1054
  fontSizePt: plan.fontSizePt,
@@ -1041,18 +1239,22 @@ function lineFromRange(entries, start, breakIdx, availableWidthPt, isFirst, reso
1041
1239
  mathDescentPt: mathDescent
1042
1240
  };
1043
1241
  }
1044
- function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator) {
1242
+ function wrap(tokens, firstLineWidth, otherWidth, resolved, hyphenator, lineWidths) {
1045
1243
  if (tokens.length === 0) return [];
1244
+ const widths = lineWidths ?? [firstLineWidth, otherWidth];
1046
1245
  const entries = paragraphItemStream(tokens, hyphenator);
1047
- const { breaks } = breakLines(entries.map((e) => e.item), [firstLineWidth, otherWidth]);
1246
+ const { breaks } = breakLines(entries.map((e) => e.item), widths);
1048
1247
  const lines = [];
1049
1248
  let start = 0;
1050
1249
  let isFirst = true;
1250
+ let lineIdx = 0;
1051
1251
  for (const breakIdx of breaks) {
1052
- const line = lineFromRange(entries, start, breakIdx, isFirst ? firstLineWidth : otherWidth, isFirst, resolved);
1252
+ const width = lineIdx < widths.length ? widths[lineIdx] : widths[widths.length - 1];
1253
+ const line = lineFromRange(entries, start, breakIdx, width, isFirst, resolved);
1053
1254
  if (line) lines.push(line);
1054
1255
  start = breakIdx + 1;
1055
1256
  isFirst = false;
1257
+ lineIdx++;
1056
1258
  }
1057
1259
  if (lines.length > 0) lines[lines.length - 1].isLastInParagraph = true;
1058
1260
  return lines;
@@ -1344,7 +1546,7 @@ function createFigure(builder, alt, fallback) {
1344
1546
  node.alt = alt && alt.trim().length > 0 ? alt : fallback;
1345
1547
  return node.id;
1346
1548
  }
1347
- function listItemParagraphNode(builder, stack, list) {
1549
+ function listItemParagraphNode(builder, stack, list, wantLbl) {
1348
1550
  const lvl = list.level;
1349
1551
  while (stack.length > 0) {
1350
1552
  const t = stack[stack.length - 1];
@@ -1364,168 +1566,445 @@ function listItemParagraphNode(builder, stack, list) {
1364
1566
  stack.push(frame);
1365
1567
  }
1366
1568
  const li = builder.create("LI", frame.listNode);
1569
+ const lblId = wantLbl ? builder.create("Lbl", li).id : void 0;
1367
1570
  const lbody = builder.create("LBody", li);
1368
1571
  frame.lbody = lbody;
1369
- return builder.create("P", lbody).id;
1572
+ return {
1573
+ pId: builder.create("P", lbody).id,
1574
+ ...lblId !== void 0 ? { lblId } : {}
1575
+ };
1370
1576
  }
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({
1577
+ var PageAssembler = class {
1578
+ constructor(sectionCtxs, builder, notes, bookmarkPositions) {
1579
+ this.sectionCtxs = sectionCtxs;
1580
+ this.builder = builder;
1581
+ this.notes = notes;
1582
+ this.bookmarkPositions = bookmarkPositions;
1583
+ this.ctx = sectionCtxs[0];
1584
+ this.cursorY = this.ctx.pageHeight - this.ctx.marginTop;
1585
+ }
1586
+ pages = [];
1587
+ ctx;
1588
+ secIdx = 0;
1589
+ pageInSection = 0;
1590
+ globalPageIdx = 0;
1591
+ current = [];
1592
+ pendingPageBreak = false;
1593
+ cursorY;
1594
+ colIdx = 0;
1595
+ colStartLen = 0;
1596
+ colLeft = () => this.ctx.marginLeft + (this.ctx.columns?.[this.colIdx]?.xOffsetPt ?? 0);
1597
+ colWidth = () => this.ctx.columns?.[this.colIdx]?.widthPt ?? this.ctx.contentWidth;
1598
+ colHasContent = () => this.current.length > this.colStartLen;
1599
+ advanceColumn = () => {
1600
+ if (this.ctx.columns && this.colIdx + 1 < this.ctx.columns.length) {
1601
+ this.colIdx++;
1602
+ this.colStartLen = this.current.length;
1603
+ this.cursorY = this.ctx.pageHeight - this.ctx.marginTop;
1604
+ } else this.flushPage();
1605
+ };
1606
+ floatsBehind = [];
1607
+ floatsFront = [];
1608
+ exclusions = [];
1609
+ floatX = (f, widthPt) => {
1610
+ const h = f.posH;
1611
+ if (!h) return this.colLeft();
1612
+ const base = h.relativeFrom === "page" ? 0 : h.relativeFrom === "column" ? this.colLeft() : this.ctx.marginLeft;
1613
+ const span = h.relativeFrom === "page" ? this.ctx.pageWidth : h.relativeFrom === "column" ? this.colWidth() : this.ctx.contentWidth;
1614
+ if (h.align === "center") return base + (span - widthPt) / 2;
1615
+ if (h.align === "right") return base + span - widthPt;
1616
+ return base + (h.offsetPt ?? 0);
1617
+ };
1618
+ floatTopYUp = (f) => {
1619
+ const v = f.posV;
1620
+ if (!v) return this.cursorY;
1621
+ if (v.relativeFrom === "page") return this.ctx.pageHeight - (v.offsetPt ?? 0);
1622
+ if (v.relativeFrom === "margin") return this.ctx.pageHeight - this.ctx.marginTop - (v.offsetPt ?? 0);
1623
+ return this.cursorY - (v.offsetPt ?? 0);
1624
+ };
1625
+ listStack = [];
1626
+ pageNotes = [];
1627
+ noteReserve = 0;
1628
+ placedNotes = /* @__PURE__ */ new Set();
1629
+ bottomLimit = () => this.ctx.marginBottom + this.noteReserve;
1630
+ exclusionAt = (yTop, h) => {
1631
+ const cl = this.colLeft();
1632
+ const cr = cl + this.colWidth();
1633
+ for (const e of this.exclusions) {
1634
+ if (yTop - h >= e.topYUp || yTop <= e.bottomYUp) continue;
1635
+ if (e.x1 <= cl || e.x0 >= cr) continue;
1636
+ return e;
1637
+ }
1638
+ };
1639
+ lineGeometryAt = (yTop, h) => {
1640
+ const full = this.colWidth();
1641
+ const e = this.exclusionAt(yTop, h);
1642
+ if (!e) return {
1643
+ width: full,
1644
+ xOffset: 0
1645
+ };
1646
+ const cl = this.colLeft();
1647
+ const leftRoom = e.x0 - FLOAT_TEXT_GAP - cl;
1648
+ const rightRoom = cl + full - (e.x1 + FLOAT_TEXT_GAP);
1649
+ if (rightRoom >= leftRoom) {
1650
+ const w = Math.max(MIN_WRAP_WIDTH, rightRoom);
1651
+ return {
1652
+ width: w,
1653
+ xOffset: full - w
1654
+ };
1655
+ }
1656
+ return {
1657
+ width: Math.max(MIN_WRAP_WIDTH, leftRoom),
1658
+ xOffset: 0
1659
+ };
1660
+ };
1661
+ lineWidthsFor = (block, startY) => {
1662
+ if (this.exclusions.length === 0 || block.lines.length === 0) return void 0;
1663
+ const h0 = computeLineHeight(block.lines[0], block.resolved);
1664
+ if (h0 <= 0) return void 0;
1665
+ const widths = [];
1666
+ let narrowed = false;
1667
+ let y = startY;
1668
+ for (let i = 0; i < 200; i++) {
1669
+ const g = this.lineGeometryAt(y, h0);
1670
+ widths.push(g.width);
1671
+ if (g.width < this.colWidth()) narrowed = true;
1672
+ else if (i > 0) break;
1673
+ y -= h0;
1674
+ if (y < this.bottomLimit()) break;
1675
+ }
1676
+ return narrowed ? widths : void 0;
1677
+ };
1678
+ lineFootnotes = (line) => {
1679
+ if (!this.notes) return [];
1680
+ const out = [];
1681
+ for (const tok of line.tokens) {
1682
+ if (tok.kind !== "text" || tok.footnoteRef === void 0) continue;
1683
+ const id = tok.footnoteRef;
1684
+ if (this.placedNotes.has(id) || out.some((o) => o.id === id)) continue;
1685
+ const laid = this.notes.layout(this.ctx, id);
1686
+ const n = this.notes.numbers.get(id);
1687
+ if (!laid || n === void 0) continue;
1688
+ out.push({
1689
+ id,
1690
+ n,
1691
+ blocks: laid.blocks,
1692
+ heightPt: laid.heightPt
1693
+ });
1694
+ }
1695
+ return out;
1696
+ };
1697
+ renderNotesBand = () => {
1698
+ if (this.pageNotes.length === 0) return [];
1699
+ const out = [];
1700
+ const top = this.ctx.marginBottom + this.noteReserve;
1701
+ out.push({
1702
+ type: "fill",
1703
+ x: pt(this.ctx.marginLeft),
1704
+ y: pt(this.ctx.pageHeight - (top - FOOTNOTE_RULE_GAP_ABOVE)),
1705
+ width: pt(FOOTNOTE_RULE_WIDTH),
1706
+ height: pt(FOOTNOTE_RULE_PT),
1707
+ fillColorHex: "000000"
1708
+ });
1709
+ let cursor = top - FOOTNOTE_SEPARATOR_HEIGHT;
1710
+ for (const note of this.pageNotes.sort((a, b) => a.n - b.n)) {
1711
+ let structId;
1712
+ if (this.builder) {
1713
+ const noteNode = this.builder.create("Note", this.builder.root);
1714
+ structId = this.builder.create("P", noteNode).id;
1715
+ }
1716
+ out.push(...drawBlocksSequentially(note.blocks, this.ctx.marginLeft, cursor, this.ctx.pageHeight, structId));
1717
+ cursor -= note.heightPt;
1718
+ }
1719
+ return out;
1720
+ };
1721
+ dynBands = [];
1722
+ flushPage = (force = false) => {
1723
+ if (this.current.length === 0 && !force) return;
1724
+ const band = bandForPage(this.pageInSection, this.globalPageIdx, this.ctx.titlePg, this.ctx.evenAndOddHeaders);
1725
+ const header = pickBand(this.ctx.headerSet, band);
1726
+ const footer = pickBand(this.ctx.footerSet, band);
1727
+ if (header.renderDynamic) this.dynBands.push({
1728
+ pageIdx: this.pages.length,
1729
+ pageNumber: this.globalPageIdx + 1,
1730
+ position: "header",
1731
+ render: header.renderDynamic
1732
+ });
1733
+ if (footer.renderDynamic) this.dynBands.push({
1734
+ pageIdx: this.pages.length,
1735
+ pageNumber: this.globalPageIdx + 1,
1736
+ position: "footer",
1737
+ render: footer.renderDynamic
1738
+ });
1739
+ this.pages.push({
1388
1740
  commands: [
1389
- ...header,
1390
- ...current,
1391
- ...footer
1741
+ ...header.commands,
1742
+ ...this.floatsBehind,
1743
+ ...this.current,
1744
+ ...this.floatsFront,
1745
+ ...this.renderNotesBand(),
1746
+ ...footer.commands
1392
1747
  ],
1393
- width: pt(ctx.pageWidth),
1394
- height: pt(ctx.pageHeight)
1748
+ width: pt(this.ctx.pageWidth),
1749
+ height: pt(this.ctx.pageHeight)
1395
1750
  });
1396
- current = [];
1397
- pageInSection++;
1398
- globalPageIdx++;
1399
- cursorY = ctx.pageHeight - ctx.marginTop;
1751
+ this.current = [];
1752
+ this.floatsBehind = [];
1753
+ this.floatsFront = [];
1754
+ this.exclusions = [];
1755
+ this.pageNotes = [];
1756
+ this.noteReserve = 0;
1757
+ this.colIdx = 0;
1758
+ this.colStartLen = 0;
1759
+ this.pageInSection++;
1760
+ this.globalPageIdx++;
1761
+ this.cursorY = this.ctx.pageHeight - this.ctx.marginTop;
1400
1762
  };
1763
+ };
1764
+ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions, reflowParagraph) {
1765
+ if (sectionCtxs.length === 0) return [];
1766
+ const asm = new PageAssembler(sectionCtxs, builder, notes, bookmarkPositions);
1401
1767
  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;
1768
+ while (asm.secIdx < sectionCtxs.length - 1 && blockIdx >= asm.ctx.endIndex) {
1769
+ asm.flushPage();
1770
+ asm.secIdx++;
1771
+ asm.ctx = sectionCtxs[asm.secIdx];
1772
+ asm.pageInSection = 0;
1773
+ asm.cursorY = asm.ctx.pageHeight - asm.ctx.marginTop;
1408
1774
  }
1409
1775
  const block = blocks[blockIdx];
1410
- if (pendingPageBreak) {
1411
- pendingPageBreak = false;
1412
- if (current.length > 0) flushPage();
1776
+ if (asm.pendingPageBreak) {
1777
+ asm.pendingPageBreak = false;
1778
+ if (asm.current.length > 0) asm.flushPage();
1413
1779
  }
1414
- if (builder && !(block.kind === "paragraph" && block.list)) listStack.length = 0;
1780
+ if (builder && !(block.kind === "paragraph" && block.list)) asm.listStack.length = 0;
1415
1781
  if (block.kind === "paragraph") {
1416
- if (block.resolved.pageBreakBefore && current.length > 0) flushPage();
1417
- cursorY -= block.spacingBeforePt;
1782
+ if (block.resolved.pageBreakBefore && asm.current.length > 0) asm.flushPage();
1783
+ asm.cursorY -= block.spacingBeforePt;
1784
+ let pb = block;
1785
+ if (reflowParagraph && pb.source && asm.exclusions.length > 0) {
1786
+ const widths = asm.lineWidthsFor(block, asm.cursorY);
1787
+ if (widths) pb = reflowParagraph(pb.source, asm.colWidth(), widths);
1788
+ }
1418
1789
  let structId;
1790
+ let markerLblId;
1791
+ const firstLine = pb.lines[0];
1792
+ const splitMarker = builder !== void 0 && pb.list !== void 0 && firstLine !== void 0 && pb.resolved.alignment === "left" && pb.resolved.bidi !== true && !firstLine.tokens.some((t) => t.bidiLevel % 2 === 1) && firstLine.tokens.some((t) => t.kind === "text" && t.listMarker) && firstLine.tokens.some((t) => !(t.kind === "text" && t.listMarker));
1419
1793
  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);
1794
+ if (pb.list) {
1795
+ const nodes = listItemParagraphNode(builder, asm.listStack, pb.list, splitMarker);
1796
+ structId = nodes.pId;
1797
+ markerLblId = nodes.lblId;
1798
+ } else structId = builder.create(paragraphStructType(pb.resolved), builder.root).id;
1799
+ const lang = dominantParagraphLang(pb.lines);
1422
1800
  if (lang && lang !== defaultLang) builder.node(structId).lang = lang;
1423
1801
  }
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({
1802
+ let firstLineOfBlock = true;
1803
+ for (const line of pb.lines) {
1804
+ const h = computeLineHeight(line, pb.resolved);
1805
+ let newNotes = asm.lineFootnotes(line);
1806
+ const addedReserve = (sub) => sub.reduce((sum, x) => sum + x.heightPt, 0) + (asm.pageNotes.length === 0 && sub.length > 0 ? FOOTNOTE_SEPARATOR_HEIGHT : 0);
1807
+ if (asm.cursorY - h < asm.bottomLimit() + addedReserve(newNotes) && asm.colHasContent()) {
1808
+ asm.advanceColumn();
1809
+ newNotes = asm.lineFootnotes(line);
1810
+ }
1811
+ if (newNotes.length > 0) {
1812
+ asm.noteReserve += addedReserve(newNotes);
1813
+ for (const x of newNotes) {
1814
+ asm.placedNotes.add(x.id);
1815
+ asm.pageNotes.push({
1816
+ n: x.n,
1817
+ blocks: x.blocks,
1818
+ heightPt: x.heightPt
1819
+ });
1820
+ }
1821
+ }
1822
+ if (firstLineOfBlock) {
1823
+ firstLineOfBlock = false;
1824
+ if (pb.bookmarks && asm.bookmarkPositions) {
1825
+ for (const bookmarkName of pb.bookmarks) if (!asm.bookmarkPositions.has(bookmarkName)) asm.bookmarkPositions.set(bookmarkName, {
1826
+ pageIdx: asm.pages.length,
1827
+ yTopPt: asm.cursorY
1828
+ });
1829
+ }
1830
+ }
1831
+ asm.cursorY -= h;
1832
+ const indentLeft = pb.resolved.indentLeft + (line.firstLine ? pb.resolved.indentFirstLine : 0);
1833
+ const offset = alignmentOffset(pb.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
1834
+ const baselineY = pt(asm.ctx.pageHeight - (asm.cursorY + lineDescent(line)));
1835
+ const exclusionShift = asm.exclusions.length > 0 ? asm.lineGeometryAt(asm.cursorY + h, h).xOffset : 0;
1836
+ const originX = asm.colLeft() + indentLeft + offset + exclusionShift;
1837
+ if (markerLblId !== void 0 && line === firstLine) {
1838
+ let k = 0;
1839
+ let markerWidth = 0;
1840
+ while (k < line.tokens.length) {
1841
+ const t = line.tokens[k];
1842
+ if (t.kind !== "text" || !t.listMarker) break;
1843
+ markerWidth += t.widthPt;
1844
+ k++;
1845
+ }
1846
+ asm.current.push({
1847
+ type: "line",
1848
+ line: {
1849
+ ...line,
1850
+ tokens: line.tokens.slice(0, k),
1851
+ isLastInParagraph: false
1852
+ },
1853
+ originX: pt(originX),
1854
+ baselineY,
1855
+ structId: markerLblId
1856
+ });
1857
+ asm.current.push({
1858
+ type: "line",
1859
+ line: {
1860
+ ...line,
1861
+ tokens: line.tokens.slice(k)
1862
+ },
1863
+ originX: pt(originX + markerWidth),
1864
+ baselineY,
1865
+ ...structId !== void 0 ? { structId } : {}
1866
+ });
1867
+ } else asm.current.push({
1431
1868
  type: "line",
1432
1869
  line,
1433
- originX: pt(ctx.marginLeft + indentLeft + offset),
1434
- baselineY: pt(ctx.pageHeight - (cursorY + lineDescent(line))),
1870
+ originX: pt(originX),
1871
+ baselineY,
1435
1872
  ...structId !== void 0 ? { structId } : {}
1436
1873
  });
1437
1874
  }
1438
- cursorY -= block.spacingAfterPt;
1439
- if (block.pageBreakAfter) pendingPageBreak = true;
1875
+ asm.cursorY -= pb.spacingAfterPt;
1876
+ if (pb.pageBreakAfter) asm.pendingPageBreak = true;
1440
1877
  } else if (block.kind === "image") {
1441
1878
  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;
1879
+ const emitImageAt = (x, topYUp, sink) => {
1880
+ sink.push({
1881
+ type: "image",
1882
+ x: pt(x),
1883
+ y: pt(asm.ctx.pageHeight - topYUp),
1884
+ width: pt(block.widthPt),
1885
+ height: pt(block.heightPt),
1886
+ imageResourceName: block.resourceName,
1887
+ ...figId !== void 0 ? { structId: figId } : {}
1888
+ });
1889
+ };
1890
+ if (isOutOfFlowFloat(block.float)) {
1891
+ const fx = asm.floatX(block.float, block.widthPt);
1892
+ const fy = asm.floatTopYUp(block.float);
1893
+ emitImageAt(fx, fy, block.float.behind ? asm.floatsBehind : asm.floatsFront);
1894
+ if (block.float.wrap !== "none") asm.exclusions.push({
1895
+ x0: fx,
1896
+ x1: fx + block.widthPt,
1897
+ topYUp: fy,
1898
+ bottomYUp: fy - block.heightPt
1899
+ });
1900
+ } else {
1901
+ asm.cursorY -= block.spacingBeforePt;
1902
+ if (asm.cursorY - block.heightPt < asm.bottomLimit() && asm.colHasContent()) asm.advanceColumn();
1903
+ asm.cursorY -= block.heightPt;
1904
+ emitImageAt(asm.colLeft(), asm.cursorY + block.heightPt, asm.current);
1905
+ asm.cursorY -= block.spacingAfterPt;
1906
+ }
1455
1907
  } else if (block.kind === "shape") {
1456
1908
  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
- });
1909
+ const emitShapeAt = (x, bottomYUp, sink) => {
1910
+ const transform = flipTransform(buildShapeTransform(x, bottomYUp, block.widthPt, block.heightPt, block.rotation60k, block.flipH, block.flipV), asm.ctx.pageHeight);
1911
+ sink.push({
1912
+ type: "shape",
1913
+ shape: {
1914
+ paths: block.paths,
1915
+ ...block.fillColorHex ? { fillColorHex: block.fillColorHex } : {},
1916
+ ...block.stroke ? { stroke: block.stroke } : {},
1917
+ transform
1918
+ },
1919
+ ...figId !== void 0 ? { structId: figId } : {}
1920
+ });
1921
+ if (block.textLines.length > 0) {
1922
+ const shapeBottom = bottomYUp;
1923
+ const shapeTop = bottomYUp + block.heightPt;
1924
+ const innerWidth = Math.max(1, block.widthPt - block.insetLeftPt - block.insetRightPt);
1925
+ let textY;
1926
+ if (block.anchor === "b") textY = shapeBottom + block.insetBottomPt + block.textHeightPt;
1927
+ else if (block.anchor === "ctr") textY = shapeBottom + (block.heightPt + block.textHeightPt) / 2;
1928
+ else textY = shapeTop - block.insetTopPt;
1929
+ for (const line of block.textLines) {
1930
+ const h = computeLineHeight(line, line.resolved);
1931
+ textY -= h;
1932
+ const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, innerWidth);
1933
+ sink.push({
1934
+ type: "line",
1935
+ line,
1936
+ originX: pt(x + block.insetLeftPt + lineOffset),
1937
+ baselineY: pt(asm.ctx.pageHeight - (textY + lineDescent(line))),
1938
+ ...figId !== void 0 ? { structId: figId } : {}
1939
+ });
1940
+ }
1492
1941
  }
1942
+ };
1943
+ if (isOutOfFlowFloat(block.float)) {
1944
+ const fx = asm.floatX(block.float, block.widthPt);
1945
+ const fy = asm.floatTopYUp(block.float);
1946
+ emitShapeAt(fx, fy - block.heightPt, block.float.behind ? asm.floatsBehind : asm.floatsFront);
1947
+ if (block.float.wrap !== "none") asm.exclusions.push({
1948
+ x0: fx,
1949
+ x1: fx + block.widthPt,
1950
+ topYUp: fy,
1951
+ bottomYUp: fy - block.heightPt
1952
+ });
1953
+ } else {
1954
+ asm.cursorY -= block.spacingBeforePt;
1955
+ if (asm.cursorY - block.heightPt < asm.bottomLimit() && asm.colHasContent()) asm.advanceColumn();
1956
+ asm.cursorY -= block.heightPt;
1957
+ const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, asm.colWidth());
1958
+ emitShapeAt(asm.colLeft() + offset, asm.cursorY, asm.current);
1959
+ asm.cursorY -= block.spacingAfterPt;
1493
1960
  }
1494
- cursorY -= block.spacingAfterPt;
1495
1961
  } else if (block.kind === "chart") {
1496
1962
  const figId = builder ? createFigure(builder, block.altText, "Chart") : void 0;
1497
1963
  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;
1964
+ const emitChartAt = (x, bottomYUp, sink) => {
1965
+ for (const s of block.layout.shapes) sink.push({
1966
+ type: "shape",
1967
+ shape: {
1968
+ paths: s.paths,
1969
+ ...s.fillColorHex ? { fillColorHex: s.fillColorHex } : {},
1970
+ ...s.stroke ? { stroke: s.stroke } : {},
1971
+ transform: flipTransform([
1972
+ 1,
1973
+ 0,
1974
+ 0,
1975
+ 1,
1976
+ x,
1977
+ bottomYUp
1978
+ ], asm.ctx.pageHeight)
1979
+ },
1980
+ ...fig
1981
+ });
1982
+ for (const t of block.layout.texts) sink.push({
1983
+ type: "line",
1984
+ line: t.line,
1985
+ originX: pt(x + t.x),
1986
+ baselineY: pt(asm.ctx.pageHeight - (bottomYUp + t.y)),
1987
+ ...fig
1988
+ });
1989
+ };
1990
+ if (isOutOfFlowFloat(block.float)) {
1991
+ const fx = asm.floatX(block.float, block.widthPt);
1992
+ const fy = asm.floatTopYUp(block.float);
1993
+ emitChartAt(fx, fy - block.heightPt, block.float.behind ? asm.floatsBehind : asm.floatsFront);
1994
+ if (block.float.wrap !== "none") asm.exclusions.push({
1995
+ x0: fx,
1996
+ x1: fx + block.widthPt,
1997
+ topYUp: fy,
1998
+ bottomYUp: fy - block.heightPt
1999
+ });
2000
+ } else {
2001
+ asm.cursorY -= block.spacingBeforePt;
2002
+ if (asm.cursorY - block.heightPt < asm.bottomLimit() && asm.colHasContent()) asm.advanceColumn();
2003
+ asm.cursorY -= block.heightPt;
2004
+ const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, asm.colWidth());
2005
+ emitChartAt(asm.colLeft() + offset, asm.cursorY, asm.current);
2006
+ asm.cursorY -= block.spacingAfterPt;
2007
+ }
1529
2008
  } else {
1530
2009
  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
2010
  const headerRows = [];
@@ -1533,7 +2012,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1533
2012
  else break;
1534
2013
  const headerHeightPt = headerRows.reduce((s, r) => s + r.heightPt, 0);
1535
2014
  const tableNode = builder ? builder.create("Table", builder.root) : void 0;
1536
- const tableX = ctx.marginLeft + block.xOffsetPt;
2015
+ const tableX = asm.colLeft() + block.xOffsetPt;
1537
2016
  for (let ri = 0; ri < block.rows.length; ri++) {
1538
2017
  const row = block.rows[ri];
1539
2018
  const isLeadingHeader = ri < headerRows.length;
@@ -1551,27 +2030,34 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1551
2030
  return b.create("P", cellNode).id;
1552
2031
  });
1553
2032
  }
1554
- const chunks = row.heightPt > ctx.pageContentHeight ? splitRowIntoChunks(row, ctx.pageContentHeight) : [row];
2033
+ const chunks = row.heightPt > asm.ctx.pageContentHeight ? splitRowIntoChunks(row, asm.ctx.pageContentHeight) : [row];
1555
2034
  for (let ci = 0; ci < chunks.length; ci++) {
1556
2035
  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;
2036
+ const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.current.length > 0;
2037
+ const overflow = asm.cursorY - chunk.heightPt < asm.bottomLimit() && asm.colHasContent();
1559
2038
  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;
2039
+ if (forcedBreak) asm.flushPage();
2040
+ else asm.advanceColumn();
2041
+ if (!isLeadingHeader && headerRows.length > 0 && asm.cursorY - headerHeightPt - chunk.heightPt >= asm.bottomLimit()) for (const hr of headerRows) {
2042
+ emitRowChunk(asm.current, hr, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, void 0);
2043
+ asm.cursorY -= hr.heightPt;
1564
2044
  }
1565
2045
  }
1566
- emitRowChunk(current, chunk, tableX, cursorY, ctx.pageHeight, colCount, cellStructIds);
1567
- cursorY -= chunk.heightPt;
2046
+ emitRowChunk(asm.current, chunk, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, cellStructIds);
2047
+ asm.cursorY -= chunk.heightPt;
1568
2048
  }
1569
2049
  }
1570
2050
  }
1571
2051
  }
1572
- flushPage();
1573
- if (pages.length === 0) flushPage(true);
1574
- return pages;
2052
+ asm.flushPage();
2053
+ if (asm.pages.length === 0) asm.flushPage(true);
2054
+ for (const d of asm.dynBands) {
2055
+ const cmds = d.render(d.pageNumber, asm.pages.length);
2056
+ const page = asm.pages[d.pageIdx];
2057
+ if (d.position === "header") page.commands.unshift(...cmds);
2058
+ else page.commands.push(...cmds);
2059
+ }
2060
+ return asm.pages;
1575
2061
  }
1576
2062
  function tableCellRowSpan(rows, startRowIdx, colStart) {
1577
2063
  let span = 1;