reamkit 1.0.2 → 1.2.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 (53) hide show
  1. package/README.md +12 -6
  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/bytes.d.ts +1 -0
  5. package/dist/esm/core/bytes.js +6 -1
  6. package/dist/esm/core/converter/facade.d.ts +2 -2
  7. package/dist/esm/core/converter/facade.js +12 -0
  8. package/dist/esm/core/converter/project.js +2 -0
  9. package/dist/esm/core/converter/ream.d.ts +1 -1
  10. package/dist/esm/core/converter/ream.js +10 -0
  11. package/dist/esm/core/document-model/index.d.ts +1 -1
  12. package/dist/esm/core/document-model/types.d.ts +58 -0
  13. package/dist/esm/{layout → core/drawingml}/chart-geometry.d.ts +1 -1
  14. package/dist/esm/{layout → core/drawingml}/preset-geometry.d.ts +2 -2
  15. package/dist/esm/{layout → core/drawingml}/preset-geometry.js +3 -3
  16. package/dist/esm/core/drawingml/shape-render.d.ts +8 -0
  17. package/dist/esm/core/drawingml/shape-render.js +71 -0
  18. package/dist/esm/core/ir/features.d.ts +1 -0
  19. package/dist/esm/core/ir/features.js +1 -0
  20. package/dist/esm/core/ir/flow.d.ts +3 -0
  21. package/dist/esm/core/links.d.ts +6 -0
  22. package/dist/esm/core/links.js +19 -0
  23. package/dist/esm/core/numbering/apply.js +3 -1
  24. package/dist/esm/core/style-cascade/index.d.ts +1 -0
  25. package/dist/esm/core/style-cascade/index.js +1 -0
  26. package/dist/esm/core/style-cascade/table.d.ts +2 -0
  27. package/dist/esm/core/style-cascade/table.js +161 -0
  28. package/dist/esm/core/vector.d.ts +2 -0
  29. package/dist/esm/core/vector.js +19 -1
  30. package/dist/esm/html/html-writer.d.ts +4 -0
  31. package/dist/esm/html/html-writer.js +464 -0
  32. package/dist/esm/index.d.ts +1 -0
  33. package/dist/esm/index.js +2 -1
  34. package/dist/esm/layout/page-doc.d.ts +3 -0
  35. package/dist/esm/layout/styled-layout.d.ts +19 -3
  36. package/dist/esm/layout/styled-layout.js +533 -197
  37. package/dist/esm/pdf/struct-tree.d.ts +7 -1
  38. package/dist/esm/pdf/struct-tree.js +22 -6
  39. package/dist/esm/pdf/styled-page-emitter.d.ts +1 -1
  40. package/dist/esm/pdf/styled-page-emitter.js +161 -29
  41. package/dist/esm/pdf/xmp.d.ts +1 -0
  42. package/dist/esm/pdf/xmp.js +4 -0
  43. package/dist/esm/svg/svg-writer.js +3 -11
  44. package/dist/esm/word/document-parser.d.ts +3 -0
  45. package/dist/esm/word/document-parser.js +210 -23
  46. package/dist/esm/word/docx-reader.js +30 -4
  47. package/dist/esm/word/drawing-parser.d.ts +4 -1
  48. package/dist/esm/word/drawing-parser.js +53 -2
  49. package/dist/esm/word/index.d.ts +2 -2
  50. package/dist/esm/word/styles-parser.js +134 -1
  51. package/dist/esm/word/table-parser.js +35 -0
  52. package/package.json +1 -1
  53. /package/dist/esm/{layout → core/drawingml}/chart-geometry.js +0 -0
@@ -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,199 @@ 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 FOOTNOTE_RULE_WIDTH = 144;
40
+ function assignNoteNumbers(body) {
41
+ const footnotes = /* @__PURE__ */ new Map();
42
+ const endnotes = /* @__PURE__ */ new Map();
43
+ const paragraphFootnotes = /* @__PURE__ */ new Set();
44
+ const numberRun = (run, n) => ({
45
+ ...run,
46
+ text: String(n),
47
+ properties: {
48
+ ...run.properties,
49
+ verticalAlign: "superscript"
50
+ }
51
+ });
52
+ const mapParagraph = (paragraph) => {
53
+ if (!paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0)) return paragraph;
54
+ return {
55
+ ...paragraph,
56
+ runs: paragraph.runs.map((run) => {
57
+ if (run.footnoteRef !== void 0) {
58
+ let n = footnotes.get(run.footnoteRef);
59
+ if (n === void 0) {
60
+ n = footnotes.size + 1;
61
+ footnotes.set(run.footnoteRef, n);
62
+ }
63
+ return numberRun(run, n);
64
+ }
65
+ if (run.endnoteRef !== void 0) {
66
+ let n = endnotes.get(run.endnoteRef);
67
+ if (n === void 0) {
68
+ n = endnotes.size + 1;
69
+ endnotes.set(run.endnoteRef, n);
70
+ }
71
+ return numberRun(run, n);
72
+ }
73
+ return run;
74
+ })
75
+ };
76
+ };
77
+ const mapElement = (el, topLevel) => {
78
+ if (el.kind === "paragraph") {
79
+ if (topLevel) {
80
+ for (const r of el.paragraph.runs) if (r.footnoteRef !== void 0) paragraphFootnotes.add(r.footnoteRef);
81
+ }
82
+ return {
83
+ kind: "paragraph",
84
+ paragraph: mapParagraph(el.paragraph)
85
+ };
86
+ }
87
+ if (el.kind === "table") return {
88
+ kind: "table",
89
+ table: {
90
+ ...el.table,
91
+ rows: el.table.rows.map((row) => ({
92
+ ...row,
93
+ cells: row.cells.map((cell) => ({
94
+ ...cell,
95
+ content: cell.content.map((c) => mapElement(c, false))
96
+ }))
97
+ }))
98
+ }
99
+ };
100
+ if (el.kind === "shape" && el.shape.text) return {
101
+ kind: "shape",
102
+ shape: {
103
+ ...el.shape,
104
+ text: {
105
+ ...el.shape.text,
106
+ content: el.shape.text.content.map((c) => mapElement(c, false))
107
+ }
108
+ }
109
+ };
110
+ return el;
111
+ };
112
+ const hasRefs = (els) => els.some((el) => {
113
+ if (el.kind === "paragraph") return el.paragraph.runs.some((r) => r.footnoteRef !== void 0 || r.endnoteRef !== void 0);
114
+ if (el.kind === "table") return el.table.rows.some((row) => row.cells.some((c) => hasRefs(c.content)));
115
+ if (el.kind === "shape" && el.shape.text) return hasRefs(el.shape.text.content);
116
+ return false;
117
+ });
118
+ if (!hasRefs(body)) return {
119
+ body,
120
+ footnotes,
121
+ endnotes,
122
+ deferredFootnotes: []
123
+ };
124
+ return {
125
+ body: body.map((el) => mapElement(el, true)),
126
+ footnotes,
127
+ endnotes,
128
+ deferredFootnotes: [...footnotes.keys()].filter((id) => !paragraphFootnotes.has(id))
129
+ };
130
+ }
131
+ function substituteNoteNumber(content, n) {
132
+ const hasPlaceholder = content.some((el) => el.kind === "paragraph" && el.paragraph.runs.some((r) => r.noteNumber));
133
+ const out = content.map((el) => {
134
+ if (el.kind !== "paragraph") return el;
135
+ if (!el.paragraph.runs.some((r) => r.noteNumber)) return el;
136
+ return {
137
+ kind: "paragraph",
138
+ paragraph: {
139
+ ...el.paragraph,
140
+ runs: el.paragraph.runs.map((r) => r.noteNumber ? {
141
+ ...r,
142
+ text: String(n),
143
+ properties: {
144
+ ...r.properties,
145
+ verticalAlign: "superscript"
146
+ }
147
+ } : r)
148
+ }
149
+ };
150
+ });
151
+ if (hasPlaceholder || out.length === 0) return out;
152
+ const first = out[0];
153
+ if (first.kind !== "paragraph") return out;
154
+ return [{
155
+ kind: "paragraph",
156
+ paragraph: {
157
+ ...first.paragraph,
158
+ runs: [{
159
+ text: `${n} `,
160
+ properties: { verticalAlign: "superscript" }
161
+ }, ...first.paragraph.runs]
162
+ }
163
+ }, ...out.slice(1)];
164
+ }
35
165
  function layoutStyledDocument(body, options) {
36
166
  const sectionList = resolveSectionList(body, options);
37
- const numberedBody = applyNumbering(body, options.numbering);
167
+ const noteAssigned = assignNoteNumbers(applyNumbering(body, options.numbering));
168
+ const numberedBody = noteAssigned.body;
38
169
  const numberedHeadersFooters = applyNumberingToHeadersFooters(options.headersFooters, options.numbering);
39
170
  const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
40
- const tagged = options.tagged === true || (pdfaProfile?.tagged ?? false);
171
+ const tagged = options.tagged === true || options.pdfUA === true || (pdfaProfile?.tagged ?? false);
41
172
  const structBuilder = tagged ? new StructTreeBuilder() : void 0;
42
173
  const fontResources = collectFontResources(numberedBody, numberedHeadersFooters, options);
43
174
  const imageResources = collectImageResources(numberedBody, numberedHeadersFooters, options);
44
175
  const sectionCtxs = sectionList.map((s) => buildSectionContext(s, options, numberedHeadersFooters, fontResources));
45
176
  let sectionIdx = 0;
177
+ const blocks = numberedBody.map((el, idx) => {
178
+ while (sectionIdx < sectionCtxs.length - 1 && idx >= sectionCtxs[sectionIdx].endIndex) sectionIdx++;
179
+ const ctx = sectionCtxs[sectionIdx];
180
+ return layoutBodyElement(el, options, fontResources, imageResources, ctx.columns ? ctx.columns[0].widthPt : ctx.contentWidth, ctx.pageContentHeight);
181
+ });
182
+ const noteBlockCache = /* @__PURE__ */ new Map();
183
+ const notePlan = options.footnotes && noteAssigned.footnotes.size > 0 ? {
184
+ numbers: noteAssigned.footnotes,
185
+ layout: (sectionCtx, id) => {
186
+ let byId = noteBlockCache.get(sectionCtx);
187
+ if (!byId) {
188
+ byId = /* @__PURE__ */ new Map();
189
+ noteBlockCache.set(sectionCtx, byId);
190
+ }
191
+ if (byId.has(id)) return byId.get(id);
192
+ const content = options.footnotes?.get(id);
193
+ const n = noteAssigned.footnotes.get(id);
194
+ let laid = void 0;
195
+ if (content && n !== void 0 && !noteAssigned.deferredFootnotes.includes(id)) {
196
+ const noteBlocks = substituteNoteNumber(content, n).map((el) => layoutBodyElement(el, options, fontResources, imageResources, sectionCtx.contentWidth, sectionCtx.pageContentHeight));
197
+ laid = {
198
+ blocks: noteBlocks,
199
+ heightPt: noteBlocks.reduce((sum, b) => sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : 0), 0)
200
+ };
201
+ }
202
+ byId.set(id, laid);
203
+ return laid;
204
+ }
205
+ } : void 0;
206
+ const lastCtx = sectionCtxs[sectionCtxs.length - 1];
207
+ if (lastCtx) {
208
+ const tailNotes = [];
209
+ for (const id of noteAssigned.deferredFootnotes) {
210
+ const content = options.footnotes?.get(id);
211
+ const n = noteAssigned.footnotes.get(id);
212
+ if (content && n !== void 0) tailNotes.push({
213
+ content,
214
+ n
215
+ });
216
+ }
217
+ for (const [id, n] of noteAssigned.endnotes) {
218
+ const content = options.endnotes?.get(id);
219
+ if (content) tailNotes.push({
220
+ content,
221
+ n
222
+ });
223
+ }
224
+ 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));
225
+ }
226
+ const bookmarks = /* @__PURE__ */ new Map();
46
227
  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"),
228
+ pages: paginateSections(blocks, sectionCtxs, structBuilder, options.language ?? "en-US", notePlan, bookmarks),
52
229
  resources: options.resources ?? new ResourceStore(),
53
230
  fontResources,
54
231
  imageResources,
@@ -56,7 +233,8 @@ function layoutStyledDocument(body, options) {
56
233
  structBuilder,
57
234
  sectionCtxs,
58
235
  pdfaProfile,
59
- tagged
236
+ tagged,
237
+ bookmarks
60
238
  }
61
239
  };
62
240
  }
@@ -99,6 +277,7 @@ function buildSectionContext(section, options, headersFooters, fontResources) {
99
277
  const contentWidth = dims.pageWidth - dims.marginLeft - dims.marginRight;
100
278
  const headerSet = layoutHeaderSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.pageHeight, dims.headerOffsetPt);
101
279
  const footerSet = layoutFooterSet(section.properties, headersFooters, options, fontResources, contentWidth, dims.marginLeft, dims.pageHeight, dims.footerOffsetPt);
280
+ const columns = buildColumnGeometry(section.properties.columns, contentWidth);
102
281
  return {
103
282
  endIndex: section.endIndex,
104
283
  properties: section.properties,
@@ -109,12 +288,35 @@ function buildSectionContext(section, options, headersFooters, fontResources) {
109
288
  marginBottom: dims.marginBottom,
110
289
  contentWidth,
111
290
  pageContentHeight: dims.pageHeight - dims.marginTop - dims.marginBottom,
291
+ ...columns ? { columns } : {},
112
292
  headerSet,
113
293
  footerSet,
114
294
  titlePg: section.properties.titlePg === true,
115
295
  evenAndOddHeaders: section.properties.evenAndOddHeaders === true
116
296
  };
117
297
  }
298
+ function buildColumnGeometry(cols, contentWidth) {
299
+ if (!cols || cols.count <= 1) return void 0;
300
+ const out = [];
301
+ if (cols.explicit && cols.explicit.length > 1) {
302
+ let x = 0;
303
+ for (const c of cols.explicit) {
304
+ out.push({
305
+ xOffsetPt: x,
306
+ widthPt: c.widthPt
307
+ });
308
+ x += c.widthPt + c.spacePt;
309
+ }
310
+ return out;
311
+ }
312
+ const width = (contentWidth - cols.spacePt * (cols.count - 1)) / cols.count;
313
+ if (width <= 0) return void 0;
314
+ for (let i = 0; i < cols.count; i++) out.push({
315
+ xOffsetPt: i * (width + cols.spacePt),
316
+ widthPt: width
317
+ });
318
+ return out;
319
+ }
118
320
  function refByType(refs, type) {
119
321
  if (!refs || refs.length === 0) return void 0;
120
322
  return refs.find((r) => r.type === type);
@@ -128,10 +330,17 @@ function markPagination(cmds) {
128
330
  function layoutHeaderSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, pageHeight, headerOffsetPt) {
129
331
  const band = (type) => {
130
332
  const ref = refByType(section.headers, type);
131
- if (!ref) return [];
333
+ if (!ref) return { commands: [] };
132
334
  const content = headersFooters.get(ref.relationshipId);
133
- if (!content) return [];
134
- return markPagination(drawBlocksSequentially(laidOutBlocksFor(content, options, fontResources, contentWidth), marginLeft, pageHeight - headerOffsetPt, pageHeight));
335
+ if (!content) return { commands: [] };
336
+ const render = (c) => {
337
+ return markPagination(drawBlocksSequentially(laidOutBlocksFor(c, options, fontResources, contentWidth), marginLeft, pageHeight - headerOffsetPt, pageHeight));
338
+ };
339
+ if (contentHasPageFields(content)) return {
340
+ commands: [],
341
+ renderDynamic: (n, total) => render(substitutePageFields(content, n, total))
342
+ };
343
+ return { commands: render(content) };
135
344
  };
136
345
  return {
137
346
  default: band("default"),
@@ -142,11 +351,18 @@ function layoutHeaderSet(section, headersFooters, options, fontResources, conten
142
351
  function layoutFooterSet(section, headersFooters, options, fontResources, contentWidth, marginLeft, pageHeight, footerOffsetPt) {
143
352
  const band = (type) => {
144
353
  const ref = refByType(section.footers, type);
145
- if (!ref) return [];
354
+ if (!ref) return { commands: [] };
146
355
  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));
356
+ if (!content) return { commands: [] };
357
+ const render = (c) => {
358
+ const blocks = laidOutBlocksFor(c, options, fontResources, contentWidth);
359
+ return markPagination(drawBlocksSequentially(blocks, marginLeft, footerOffsetPt + blocks.reduce((sum, b) => sum + (b.kind === "paragraph" ? b.spacingBeforePt + b.heightPt + b.spacingAfterPt : b.heightPt), 0), pageHeight));
360
+ };
361
+ if (contentHasPageFields(content)) return {
362
+ commands: [],
363
+ renderDynamic: (n, total) => render(substitutePageFields(content, n, total))
364
+ };
365
+ return { commands: render(content) };
150
366
  };
151
367
  return {
152
368
  default: band("default"),
@@ -154,9 +370,30 @@ function layoutFooterSet(section, headersFooters, options, fontResources, conten
154
370
  even: band("even")
155
371
  };
156
372
  }
373
+ function contentHasPageFields(content) {
374
+ for (const el of content) if (el.kind === "paragraph" && el.paragraph.runs.some((r) => r.field !== void 0)) return true;
375
+ return false;
376
+ }
377
+ function substitutePageFields(content, pageNumber, totalPages) {
378
+ return content.map((el) => {
379
+ if (el.kind !== "paragraph") return el;
380
+ if (!el.paragraph.runs.some((r) => r.field !== void 0)) return el;
381
+ return {
382
+ kind: "paragraph",
383
+ paragraph: {
384
+ ...el.paragraph,
385
+ runs: el.paragraph.runs.map((r) => r.field === void 0 ? r : {
386
+ ...r,
387
+ text: String(r.field === "PAGE" ? pageNumber : totalPages)
388
+ })
389
+ }
390
+ };
391
+ });
392
+ }
157
393
  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;
394
+ const has = (e) => e.commands.length > 0 || e.renderDynamic !== void 0;
395
+ if (band === "first") return has(set.first) ? set.first : set.default;
396
+ if (band === "even") return has(set.even) ? set.even : set.default;
160
397
  return set.default;
161
398
  }
162
399
  function bandForPage(pageInSection, globalPageIdx, titlePg, evenAndOddHeaders) {
@@ -174,7 +411,6 @@ function layoutBodyElement(el, options, fontResources, imageResources, contentWi
174
411
  if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight);
175
412
  return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight);
176
413
  }
177
- var EMU_PER_PT = 12700;
178
414
  function layoutImageBlock(image, imageResources, contentWidth) {
179
415
  let widthPt = image.width;
180
416
  let heightPt = image.height;
@@ -193,12 +429,10 @@ function layoutImageBlock(image, imageResources, contentWidth) {
193
429
  resourceName: res?.resourceName ?? "",
194
430
  spacingBeforePt: image.paragraphProperties.spacingBefore ?? 0,
195
431
  spacingAfterPt: image.paragraphProperties.spacingAfter ?? 0,
196
- ...image.altText ? { altText: image.altText } : {}
432
+ ...image.altText ? { altText: image.altText } : {},
433
+ ...image.float ? { float: image.float } : {}
197
434
  };
198
435
  }
199
- var DEFAULT_LINE_WIDTH_EMU = 9525;
200
- var DEFAULT_INSET_LR_EMU = 91440;
201
- var DEFAULT_INSET_TB_EMU = 45720;
202
436
  function layoutShapeBlock(shape, options, fontResources, imageResources, contentWidth, maxHeight) {
203
437
  let widthPt = shape.width;
204
438
  let heightPt = shape.height;
@@ -218,10 +452,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
218
452
  const t = shape.transform;
219
453
  const pp = shape.paragraphProperties;
220
454
  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;
455
+ const insetLeftPt = text?.insetLeft ?? 7.2;
456
+ const insetRightPt = text?.insetRight ?? 7.2;
457
+ const insetTopPt = text?.insetTop ?? 3.6;
458
+ const insetBottomPt = text?.insetBottom ?? 3.6;
225
459
  const textLines = [];
226
460
  let textHeightPt = 0;
227
461
  if (text && text.content.length > 0) {
@@ -256,82 +490,10 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
256
490
  insetTopPt,
257
491
  insetBottomPt,
258
492
  anchor: text?.anchor ?? "t",
259
- ...shape.altText ? { altText: shape.altText } : {}
493
+ ...shape.altText ? { altText: shape.altText } : {},
494
+ ...shape.float ? { float: shape.float } : {}
260
495
  };
261
496
  }
262
- function buildShapePaths(geometry, widthPt, heightPt) {
263
- if (geometry.kind === "preset") return presetPaths(geometry.preset ?? "rect", widthPt, heightPt, geometry.adjust ?? /* @__PURE__ */ new Map()) ?? [rectPath(widthPt, heightPt)];
264
- if (geometry.custom) return customPaths(geometry.custom, widthPt, heightPt);
265
- return [rectPath(widthPt, heightPt)];
266
- }
267
- function buildStroke(line) {
268
- if (!line || line.fill === "none") return void 0;
269
- const widthPt = line.width ?? DEFAULT_LINE_WIDTH_EMU / EMU_PER_PT;
270
- const dash = line.dash && line.dash !== "solid" ? dashPattern(line.dash, widthPt) : void 0;
271
- const cap = line.cap === "flat" ? "butt" : line.cap;
272
- return {
273
- colorHex: line.colorHex ?? "000000",
274
- widthPt,
275
- ...dash ? { dash } : {},
276
- ...cap ? { cap } : {}
277
- };
278
- }
279
- function dashPattern(dash, w) {
280
- const u = Math.max(w, .1);
281
- switch (dash) {
282
- case "solid": return;
283
- case "dot": return [u, 2 * u];
284
- case "dash": return [4 * u, 3 * u];
285
- case "dashDot": return [
286
- 4 * u,
287
- 3 * u,
288
- u,
289
- 3 * u
290
- ];
291
- case "lgDash": return [8 * u, 3 * u];
292
- case "lgDashDot": return [
293
- 8 * u,
294
- 3 * u,
295
- u,
296
- 3 * u
297
- ];
298
- case "sysDash": return [3 * u, u];
299
- case "sysDot": return [u, u];
300
- }
301
- }
302
- function buildShapeTransform(pageX, pageY, widthPt, heightPt, rotation60k, flipH, flipV) {
303
- const theta = -rotation60k / 6e4 * (Math.PI / 180);
304
- const sx = flipH ? -1 : 1;
305
- const sy = flipV ? -1 : 1;
306
- const cos = Math.cos(theta);
307
- const sin = Math.sin(theta);
308
- const a = sx * cos;
309
- const b = sx * sin;
310
- const c = -sy * sin;
311
- const d = sy * cos;
312
- const cxL = widthPt / 2;
313
- const cyL = heightPt / 2;
314
- const centerX = pageX + cxL;
315
- const centerY = pageY + cyL;
316
- return [
317
- a,
318
- b,
319
- c,
320
- d,
321
- centerX - (a * cxL + c * cyL),
322
- centerY - (b * cxL + d * cyL)
323
- ];
324
- }
325
- function flipTransform(m, pageHeight) {
326
- return [
327
- m[0],
328
- -m[1],
329
- m[2],
330
- -m[3],
331
- m[4],
332
- pageHeight - m[5]
333
- ];
334
- }
335
497
  function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight) {
336
498
  let widthPt = block.width;
337
499
  let heightPt = block.height;
@@ -362,7 +524,8 @@ function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight
362
524
  resolvedAlignment: pp.alignment ?? "left",
363
525
  spacingBeforePt: pp.spacingBefore ?? 0,
364
526
  spacingAfterPt: pp.spacingAfter ?? 0,
365
- ...altText ? { altText } : {}
527
+ ...altText ? { altText } : {},
528
+ ...block.float ? { float: block.float } : {}
366
529
  };
367
530
  }
368
531
  function buildChartLayout(chart, widthPt, heightPt, font) {
@@ -486,6 +649,8 @@ function collectImageResources(body, headersFooters, options) {
486
649
  };
487
650
  visit(body);
488
651
  for (const hf of headersFooters.values()) visit(hf);
652
+ for (const note of options.footnotes?.values() ?? []) visit(note);
653
+ for (const note of options.endnotes?.values() ?? []) visit(note);
489
654
  const flattenAlpha = options.pdfA ? parsePdfAProfile(options.pdfA).part === 1 : false;
490
655
  let counter = 0;
491
656
  for (const resourceId of seen) {
@@ -505,7 +670,7 @@ function collectImageResources(body, headersFooters, options) {
505
670
  }
506
671
  return out;
507
672
  }
508
- function drawBlocksSequentially(blocks, startX, startY, pageHeight) {
673
+ function drawBlocksSequentially(blocks, startX, startY, pageHeight, structId) {
509
674
  const out = [];
510
675
  let cursorY = startY;
511
676
  for (const block of blocks) {
@@ -520,7 +685,8 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight) {
520
685
  type: "line",
521
686
  line,
522
687
  originX: pt(startX + indentLeft + offset),
523
- baselineY: pt(pageHeight - (cursorY + lineDescent(line)))
688
+ baselineY: pt(pageHeight - (cursorY + lineDescent(line))),
689
+ ...structId !== void 0 ? { structId } : {}
524
690
  });
525
691
  }
526
692
  cursorY -= block.spacingAfterPt;
@@ -600,6 +766,20 @@ function collectFontResources(body, headersFooters, options) {
600
766
  continue;
601
767
  }
602
768
  if (run.inlineImage && !run.text) continue;
769
+ if (run.field !== void 0) {
770
+ addRun({
771
+ text: `${run.text}0123456789`,
772
+ properties: run.properties
773
+ }, el.paragraph);
774
+ continue;
775
+ }
776
+ if (run.footnoteRef !== void 0 || run.endnoteRef !== void 0 || run.noteNumber) {
777
+ addRun({
778
+ text: `${run.text}0123456789 `,
779
+ properties: run.properties
780
+ }, el.paragraph);
781
+ continue;
782
+ }
603
783
  addRun(run, el.paragraph);
604
784
  }
605
785
  else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
@@ -629,6 +809,8 @@ function collectFontResources(body, headersFooters, options) {
629
809
  };
630
810
  visit(body);
631
811
  for (const hf of headersFooters.values()) visit(hf);
812
+ for (const note of options.footnotes?.values() ?? []) visit(note);
813
+ for (const note of options.endnotes?.values() ?? []) visit(note);
632
814
  if (used.size === 0) {
633
815
  const regular = options.registry.resolveByStyle(false, false);
634
816
  used.set(regular.variant, {
@@ -672,7 +854,8 @@ function layoutParagraphBlock(paragraph, options, fontResources, imageResources,
672
854
  numId: numbering.numId,
673
855
  level: numbering.ilvl
674
856
  } } : {},
675
- ...paragraph.runs.some((r) => r.pageBreak) ? { pageBreakAfter: true } : {}
857
+ ...paragraph.runs.some((r) => r.pageBreak) ? { pageBreakAfter: true } : {},
858
+ ...paragraph.bookmarks && paragraph.bookmarks.length > 0 ? { bookmarks: paragraph.bookmarks } : {}
676
859
  };
677
860
  }
678
861
  function resolveMathItems(items, fontFor) {
@@ -801,6 +984,9 @@ function tokenizePlansLtr(plans) {
801
984
  kind: "text",
802
985
  text: t.text,
803
986
  isSpace: t.isSpace,
987
+ ...plan.run.href !== void 0 ? { href: plan.run.href } : {},
988
+ ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
989
+ ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
804
990
  resolvedRun: plan.resolvedRun,
805
991
  font: plan.font,
806
992
  fontSizePt: plan.fontSizePt,
@@ -851,6 +1037,9 @@ function tokenizePlansBidi(plans, realLevels) {
851
1037
  kind: "text",
852
1038
  text,
853
1039
  isSpace: curSpace,
1040
+ ...plan.run.href !== void 0 ? { href: plan.run.href } : {},
1041
+ ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
1042
+ ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
854
1043
  resolvedRun: plan.resolvedRun,
855
1044
  font: plan.font,
856
1045
  fontSizePt: plan.fontSizePt,
@@ -1368,7 +1557,7 @@ function listItemParagraphNode(builder, stack, list) {
1368
1557
  frame.lbody = lbody;
1369
1558
  return builder.create("P", lbody).id;
1370
1559
  }
1371
- function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1560
+ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", notes, bookmarkPositions) {
1372
1561
  if (sectionCtxs.length === 0) return [];
1373
1562
  const pages = [];
1374
1563
  let ctx = sectionCtxs[0];
@@ -1378,22 +1567,121 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1378
1567
  let current = [];
1379
1568
  let pendingPageBreak = false;
1380
1569
  let cursorY = ctx.pageHeight - ctx.marginTop;
1570
+ let colIdx = 0;
1571
+ let colStartLen = 0;
1572
+ const colLeft = () => ctx.marginLeft + (ctx.columns?.[colIdx]?.xOffsetPt ?? 0);
1573
+ const colWidth = () => ctx.columns?.[colIdx]?.widthPt ?? ctx.contentWidth;
1574
+ const colHasContent = () => current.length > colStartLen;
1575
+ const advanceColumn = () => {
1576
+ if (ctx.columns && colIdx + 1 < ctx.columns.length) {
1577
+ colIdx++;
1578
+ colStartLen = current.length;
1579
+ cursorY = ctx.pageHeight - ctx.marginTop;
1580
+ } else flushPage();
1581
+ };
1582
+ let floatsBehind = [];
1583
+ let floatsFront = [];
1584
+ const floatX = (f, widthPt) => {
1585
+ const h = f.posH;
1586
+ if (!h) return colLeft();
1587
+ const base = h.relativeFrom === "page" ? 0 : h.relativeFrom === "column" ? colLeft() : ctx.marginLeft;
1588
+ const span = h.relativeFrom === "page" ? ctx.pageWidth : h.relativeFrom === "column" ? colWidth() : ctx.contentWidth;
1589
+ if (h.align === "center") return base + (span - widthPt) / 2;
1590
+ if (h.align === "right") return base + span - widthPt;
1591
+ return base + (h.offsetPt ?? 0);
1592
+ };
1593
+ const floatTopYUp = (f) => {
1594
+ const v = f.posV;
1595
+ if (!v) return cursorY;
1596
+ if (v.relativeFrom === "page") return ctx.pageHeight - (v.offsetPt ?? 0);
1597
+ if (v.relativeFrom === "margin") return ctx.pageHeight - ctx.marginTop - (v.offsetPt ?? 0);
1598
+ return cursorY - (v.offsetPt ?? 0);
1599
+ };
1381
1600
  const listStack = [];
1601
+ let pageNotes = [];
1602
+ let noteReserve = 0;
1603
+ const placedNotes = /* @__PURE__ */ new Set();
1604
+ const bottomLimit = () => ctx.marginBottom + noteReserve;
1605
+ const lineFootnotes = (line) => {
1606
+ if (!notes) return [];
1607
+ const out = [];
1608
+ for (const tok of line.tokens) {
1609
+ if (tok.kind !== "text" || tok.footnoteRef === void 0) continue;
1610
+ const id = tok.footnoteRef;
1611
+ if (placedNotes.has(id) || out.some((o) => o.id === id)) continue;
1612
+ const laid = notes.layout(ctx, id);
1613
+ const n = notes.numbers.get(id);
1614
+ if (!laid || n === void 0) continue;
1615
+ out.push({
1616
+ id,
1617
+ n,
1618
+ blocks: laid.blocks,
1619
+ heightPt: laid.heightPt
1620
+ });
1621
+ }
1622
+ return out;
1623
+ };
1624
+ const renderNotesBand = () => {
1625
+ if (pageNotes.length === 0) return [];
1626
+ const out = [];
1627
+ const top = ctx.marginBottom + noteReserve;
1628
+ out.push({
1629
+ type: "fill",
1630
+ x: pt(ctx.marginLeft),
1631
+ y: pt(ctx.pageHeight - (top - FOOTNOTE_RULE_GAP_ABOVE)),
1632
+ width: pt(FOOTNOTE_RULE_WIDTH),
1633
+ height: pt(FOOTNOTE_RULE_PT),
1634
+ fillColorHex: "000000"
1635
+ });
1636
+ let cursor = top - FOOTNOTE_SEPARATOR_HEIGHT;
1637
+ for (const note of pageNotes.sort((a, b) => a.n - b.n)) {
1638
+ let structId;
1639
+ if (builder) {
1640
+ const noteNode = builder.create("Note", builder.root);
1641
+ structId = builder.create("P", noteNode).id;
1642
+ }
1643
+ out.push(...drawBlocksSequentially(note.blocks, ctx.marginLeft, cursor, ctx.pageHeight, structId));
1644
+ cursor -= note.heightPt;
1645
+ }
1646
+ return out;
1647
+ };
1648
+ const dynBands = [];
1382
1649
  const flushPage = (force = false) => {
1383
1650
  if (current.length === 0 && !force) return;
1384
1651
  const band = bandForPage(pageInSection, globalPageIdx, ctx.titlePg, ctx.evenAndOddHeaders);
1385
1652
  const header = pickBand(ctx.headerSet, band);
1386
1653
  const footer = pickBand(ctx.footerSet, band);
1654
+ if (header.renderDynamic) dynBands.push({
1655
+ pageIdx: pages.length,
1656
+ pageNumber: globalPageIdx + 1,
1657
+ position: "header",
1658
+ render: header.renderDynamic
1659
+ });
1660
+ if (footer.renderDynamic) dynBands.push({
1661
+ pageIdx: pages.length,
1662
+ pageNumber: globalPageIdx + 1,
1663
+ position: "footer",
1664
+ render: footer.renderDynamic
1665
+ });
1387
1666
  pages.push({
1388
1667
  commands: [
1389
- ...header,
1668
+ ...header.commands,
1669
+ ...floatsBehind,
1390
1670
  ...current,
1391
- ...footer
1671
+ ...floatsFront,
1672
+ ...renderNotesBand(),
1673
+ ...footer.commands
1392
1674
  ],
1393
1675
  width: pt(ctx.pageWidth),
1394
1676
  height: pt(ctx.pageHeight)
1395
1677
  });
1396
1678
  current = [];
1679
+ floatsBehind = [];
1680
+ floatsFront = [];
1681
+ pageNotes = [];
1682
+ noteReserve = 0;
1683
+ colIdx = 0;
1684
+ colStartLen = 0;
1397
1685
  pageInSection++;
1398
1686
  globalPageIdx++;
1399
1687
  cursorY = ctx.pageHeight - ctx.marginTop;
@@ -1421,16 +1709,42 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1421
1709
  const lang = dominantParagraphLang(block.lines);
1422
1710
  if (lang && lang !== defaultLang) builder.node(structId).lang = lang;
1423
1711
  }
1712
+ let firstLineOfBlock = true;
1424
1713
  for (const line of block.lines) {
1425
1714
  const h = computeLineHeight(line, block.resolved);
1426
- if (cursorY - h < ctx.marginBottom && current.length > 0) flushPage();
1715
+ let newNotes = lineFootnotes(line);
1716
+ const addedReserve = (sub) => sub.reduce((sum, x) => sum + x.heightPt, 0) + (pageNotes.length === 0 && sub.length > 0 ? FOOTNOTE_SEPARATOR_HEIGHT : 0);
1717
+ if (cursorY - h < bottomLimit() + addedReserve(newNotes) && colHasContent()) {
1718
+ advanceColumn();
1719
+ newNotes = lineFootnotes(line);
1720
+ }
1721
+ if (newNotes.length > 0) {
1722
+ noteReserve += addedReserve(newNotes);
1723
+ for (const x of newNotes) {
1724
+ placedNotes.add(x.id);
1725
+ pageNotes.push({
1726
+ n: x.n,
1727
+ blocks: x.blocks,
1728
+ heightPt: x.heightPt
1729
+ });
1730
+ }
1731
+ }
1732
+ if (firstLineOfBlock) {
1733
+ firstLineOfBlock = false;
1734
+ if (block.bookmarks && bookmarkPositions) {
1735
+ for (const bookmarkName of block.bookmarks) if (!bookmarkPositions.has(bookmarkName)) bookmarkPositions.set(bookmarkName, {
1736
+ pageIdx: pages.length,
1737
+ yTopPt: cursorY
1738
+ });
1739
+ }
1740
+ }
1427
1741
  cursorY -= h;
1428
1742
  const indentLeft = block.resolved.indentLeft + (line.firstLine ? block.resolved.indentFirstLine : 0);
1429
1743
  const offset = alignmentOffset(block.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
1430
1744
  current.push({
1431
1745
  type: "line",
1432
1746
  line,
1433
- originX: pt(ctx.marginLeft + indentLeft + offset),
1747
+ originX: pt(colLeft() + indentLeft + offset),
1434
1748
  baselineY: pt(ctx.pageHeight - (cursorY + lineDescent(line))),
1435
1749
  ...structId !== void 0 ? { structId } : {}
1436
1750
  });
@@ -1439,93 +1753,108 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1439
1753
  if (block.pageBreakAfter) pendingPageBreak = true;
1440
1754
  } else if (block.kind === "image") {
1441
1755
  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;
1756
+ const emitImageAt = (x, topYUp, sink) => {
1757
+ sink.push({
1758
+ type: "image",
1759
+ x: pt(x),
1760
+ y: pt(ctx.pageHeight - topYUp),
1761
+ width: pt(block.widthPt),
1762
+ height: pt(block.heightPt),
1763
+ imageResourceName: block.resourceName,
1764
+ ...figId !== void 0 ? { structId: figId } : {}
1765
+ });
1766
+ };
1767
+ if (block.float?.wrap === "none") emitImageAt(floatX(block.float, block.widthPt), floatTopYUp(block.float), block.float.behind ? floatsBehind : floatsFront);
1768
+ else {
1769
+ cursorY -= block.spacingBeforePt;
1770
+ if (cursorY - block.heightPt < bottomLimit() && colHasContent()) advanceColumn();
1771
+ cursorY -= block.heightPt;
1772
+ emitImageAt(colLeft(), cursorY + block.heightPt, current);
1773
+ cursorY -= block.spacingAfterPt;
1774
+ }
1455
1775
  } else if (block.kind === "shape") {
1456
1776
  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
- });
1777
+ const emitShapeAt = (x, bottomYUp, sink) => {
1778
+ const transform = flipTransform(buildShapeTransform(x, bottomYUp, block.widthPt, block.heightPt, block.rotation60k, block.flipH, block.flipV), ctx.pageHeight);
1779
+ sink.push({
1780
+ type: "shape",
1781
+ shape: {
1782
+ paths: block.paths,
1783
+ ...block.fillColorHex ? { fillColorHex: block.fillColorHex } : {},
1784
+ ...block.stroke ? { stroke: block.stroke } : {},
1785
+ transform
1786
+ },
1787
+ ...figId !== void 0 ? { structId: figId } : {}
1788
+ });
1789
+ if (block.textLines.length > 0) {
1790
+ const shapeBottom = bottomYUp;
1791
+ const shapeTop = bottomYUp + block.heightPt;
1792
+ const innerWidth = Math.max(1, block.widthPt - block.insetLeftPt - block.insetRightPt);
1793
+ let textY;
1794
+ if (block.anchor === "b") textY = shapeBottom + block.insetBottomPt + block.textHeightPt;
1795
+ else if (block.anchor === "ctr") textY = shapeBottom + (block.heightPt + block.textHeightPt) / 2;
1796
+ else textY = shapeTop - block.insetTopPt;
1797
+ for (const line of block.textLines) {
1798
+ const h = computeLineHeight(line, line.resolved);
1799
+ textY -= h;
1800
+ const lineOffset = alignmentOffset(line.resolved.alignment, line.contentWidthPt, innerWidth);
1801
+ sink.push({
1802
+ type: "line",
1803
+ line,
1804
+ originX: pt(x + block.insetLeftPt + lineOffset),
1805
+ baselineY: pt(ctx.pageHeight - (textY + lineDescent(line))),
1806
+ ...figId !== void 0 ? { structId: figId } : {}
1807
+ });
1808
+ }
1492
1809
  }
1810
+ };
1811
+ if (block.float?.wrap === "none") emitShapeAt(floatX(block.float, block.widthPt), floatTopYUp(block.float) - block.heightPt, block.float.behind ? floatsBehind : floatsFront);
1812
+ else {
1813
+ cursorY -= block.spacingBeforePt;
1814
+ if (cursorY - block.heightPt < bottomLimit() && colHasContent()) advanceColumn();
1815
+ cursorY -= block.heightPt;
1816
+ const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, colWidth());
1817
+ emitShapeAt(colLeft() + offset, cursorY, current);
1818
+ cursorY -= block.spacingAfterPt;
1493
1819
  }
1494
- cursorY -= block.spacingAfterPt;
1495
1820
  } else if (block.kind === "chart") {
1496
1821
  const figId = builder ? createFigure(builder, block.altText, "Chart") : void 0;
1497
1822
  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;
1823
+ const emitChartAt = (x, bottomYUp, sink) => {
1824
+ for (const s of block.layout.shapes) sink.push({
1825
+ type: "shape",
1826
+ shape: {
1827
+ paths: s.paths,
1828
+ ...s.fillColorHex ? { fillColorHex: s.fillColorHex } : {},
1829
+ ...s.stroke ? { stroke: s.stroke } : {},
1830
+ transform: flipTransform([
1831
+ 1,
1832
+ 0,
1833
+ 0,
1834
+ 1,
1835
+ x,
1836
+ bottomYUp
1837
+ ], ctx.pageHeight)
1838
+ },
1839
+ ...fig
1840
+ });
1841
+ for (const t of block.layout.texts) sink.push({
1842
+ type: "line",
1843
+ line: t.line,
1844
+ originX: pt(x + t.x),
1845
+ baselineY: pt(ctx.pageHeight - (bottomYUp + t.y)),
1846
+ ...fig
1847
+ });
1848
+ };
1849
+ if (block.float?.wrap === "none") emitChartAt(floatX(block.float, block.widthPt), floatTopYUp(block.float) - block.heightPt, block.float.behind ? floatsBehind : floatsFront);
1850
+ else {
1851
+ cursorY -= block.spacingBeforePt;
1852
+ if (cursorY - block.heightPt < bottomLimit() && colHasContent()) advanceColumn();
1853
+ cursorY -= block.heightPt;
1854
+ const offset = alignmentOffset(block.resolvedAlignment, block.widthPt, colWidth());
1855
+ emitChartAt(colLeft() + offset, cursorY, current);
1856
+ cursorY -= block.spacingAfterPt;
1857
+ }
1529
1858
  } else {
1530
1859
  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
1860
  const headerRows = [];
@@ -1533,7 +1862,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1533
1862
  else break;
1534
1863
  const headerHeightPt = headerRows.reduce((s, r) => s + r.heightPt, 0);
1535
1864
  const tableNode = builder ? builder.create("Table", builder.root) : void 0;
1536
- const tableX = ctx.marginLeft + block.xOffsetPt;
1865
+ const tableX = colLeft() + block.xOffsetPt;
1537
1866
  for (let ri = 0; ri < block.rows.length; ri++) {
1538
1867
  const row = block.rows[ri];
1539
1868
  const isLeadingHeader = ri < headerRows.length;
@@ -1555,10 +1884,11 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1555
1884
  for (let ci = 0; ci < chunks.length; ci++) {
1556
1885
  const chunk = chunks[ci];
1557
1886
  const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && current.length > 0;
1558
- const overflow = cursorY - chunk.heightPt < ctx.marginBottom && current.length > 0;
1887
+ const overflow = cursorY - chunk.heightPt < bottomLimit() && colHasContent();
1559
1888
  if (forcedBreak || overflow) {
1560
- flushPage();
1561
- if (!isLeadingHeader && headerRows.length > 0 && cursorY - headerHeightPt - chunk.heightPt >= ctx.marginBottom) for (const hr of headerRows) {
1889
+ if (forcedBreak) flushPage();
1890
+ else advanceColumn();
1891
+ if (!isLeadingHeader && headerRows.length > 0 && cursorY - headerHeightPt - chunk.heightPt >= bottomLimit()) for (const hr of headerRows) {
1562
1892
  emitRowChunk(current, hr, tableX, cursorY, ctx.pageHeight, colCount, void 0);
1563
1893
  cursorY -= hr.heightPt;
1564
1894
  }
@@ -1571,6 +1901,12 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US") {
1571
1901
  }
1572
1902
  flushPage();
1573
1903
  if (pages.length === 0) flushPage(true);
1904
+ for (const d of dynBands) {
1905
+ const cmds = d.render(d.pageNumber, pages.length);
1906
+ const page = pages[d.pageIdx];
1907
+ if (d.position === "header") page.commands.unshift(...cmds);
1908
+ else page.commands.push(...cmds);
1909
+ }
1574
1910
  return pages;
1575
1911
  }
1576
1912
  function tableCellRowSpan(rows, startRowIdx, colStart) {