reamkit 1.17.0 → 1.18.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.
@@ -4,6 +4,9 @@ import { createFontMeasure } from "../core/font/measure.js";
4
4
  import { halfPtToPt, pt } from "../core/ir/units.js";
5
5
  import { ResourceStore } from "../core/ir/resources.js";
6
6
  import { prepareImage } from "../core/images.js";
7
+ import { PathBuilder, flipTransform } from "../core/vector.js";
8
+ import { isEmf, readEmf } from "../core/metafile/emf.js";
9
+ import { isWmf, readWmf } from "../core/metafile/wmf.js";
7
10
  import { segmentLevels } from "../core/bidi/segments.js";
8
11
  import { analyzeString, hasBidiCharacters } from "../core/bidi/index.js";
9
12
  import { FORCED_BREAK, breakLines } from "../core/line-breaker/knuth-plass.js";
@@ -15,7 +18,6 @@ import { resolveParagraphProperties, resolveRunProperties } from "../core/style-
15
18
  import "../core/style-cascade/index.js";
16
19
  import { applyNumbering, applyNumberingToHeadersFooters } from "../core/numbering/apply.js";
17
20
  import "../core/numbering/index.js";
18
- import { PathBuilder, flipTransform } from "../core/vector.js";
19
21
  import { buildSparkline } from "../core/drawingml/sparkline-geometry.js";
20
22
  import { arcPoint, arcToBeziers } from "../core/arc-to-bezier.js";
21
23
  import { buildChartScene, legendSeriesName } from "../core/drawingml/chart-geometry.js";
@@ -55,6 +57,35 @@ var MIN_WRAP_WIDTH = 36;
55
57
  function isOutOfFlowFloat(f) {
56
58
  return f !== void 0 && f.wrap !== "topAndBottom";
57
59
  }
60
+ /** The float's left edge, resolved from its horizontal anchor (§20.4.3.3). */
61
+ function floatXIn(frame, f, widthPt) {
62
+ const h = f.posH;
63
+ if (!h) return frame.columnLeft;
64
+ const textRight = frame.marginLeft + frame.contentWidth;
65
+ const pageRight = frame.pageLeft + frame.pageWidth;
66
+ const base = h.relativeFrom === "page" || h.relativeFrom === "leftMargin" ? frame.pageLeft : h.relativeFrom === "column" ? frame.columnLeft : h.relativeFrom === "rightMargin" ? textRight : frame.marginLeft;
67
+ const span = h.relativeFrom === "page" ? frame.pageWidth : h.relativeFrom === "column" ? frame.columnWidth : h.relativeFrom === "leftMargin" ? frame.marginLeft - frame.pageLeft : h.relativeFrom === "rightMargin" ? pageRight - textRight : frame.contentWidth;
68
+ if (h.align === "center") return base + (span - widthPt) / 2;
69
+ if (h.align === "right") return base + span - widthPt;
70
+ return base + (h.offsetPt ?? 0);
71
+ }
72
+ /** The float's TOP, y-up, resolved from its vertical anchor (§20.4.3.4). */
73
+ function floatTopYUpIn(frame, f, heightPt = 0) {
74
+ const v = f.posV;
75
+ if (!v) return frame.anchorY;
76
+ if (v.align !== void 0) {
77
+ const page = v.relativeFrom === "page";
78
+ const topYUp = page ? frame.pageTopYUp : frame.marginTopYUp;
79
+ const bottomYUp = page ? frame.pageBottomYUp : frame.marginBottomYUp;
80
+ if (v.align === "top") return topYUp;
81
+ if (v.align === "bottom") return bottomYUp + heightPt;
82
+ return bottomYUp + (topYUp - bottomYUp + heightPt) / 2;
83
+ }
84
+ if (v.relativeFrom === "page" || v.relativeFrom === "topMargin") return frame.pageTopYUp - (v.offsetPt ?? 0);
85
+ if (v.relativeFrom === "bottomMargin") return frame.marginBottomYUp - (v.offsetPt ?? 0);
86
+ if (v.relativeFrom === "margin") return frame.marginTopYUp - (v.offsetPt ?? 0);
87
+ return frame.anchorY - (v.offsetPt ?? 0);
88
+ }
58
89
  var FOOTNOTE_RULE_WIDTH = 144;
59
90
  function assignNoteNumbers(body, commentIds) {
60
91
  const footnotes = /* @__PURE__ */ new Map();
@@ -273,8 +304,8 @@ function layoutStyledDocument(body, options) {
273
304
  const pdfaProfile = options.pdfA ? parsePdfAProfile(options.pdfA) : void 0;
274
305
  const tagged = options.tagged === true || options.pdfUA === true || (pdfaProfile?.tagged ?? false);
275
306
  const structBuilder = tagged ? new StructTreeBuilder() : void 0;
276
- const fontResources = collectFontResources(numberedBody, numberedHeadersFooters, options);
277
307
  const imageResources = collectImageResources(numberedBody, numberedHeadersFooters, options);
308
+ const fontResources = collectFontResources(numberedBody, numberedHeadersFooters, options, imageResources);
278
309
  const sectionCtxs = sectionList.map((s) => buildSectionContext(s, options, numberedHeadersFooters, fontResources, imageResources));
279
310
  let sectionIdx = 0;
280
311
  const bodyFrames = frameGroups(numberedBody);
@@ -309,6 +340,7 @@ function layoutStyledDocument(body, options) {
309
340
  spacingBeforePt: 0
310
341
  };
311
342
  }
343
+ flowLinkedTextBoxes(blocks);
312
344
  const noteBlockCache = /* @__PURE__ */ new Map();
313
345
  const notePlan = options.footnotes && noteAssigned.footnotes.size > 0 ? {
314
346
  numbers: noteAssigned.footnotes,
@@ -775,8 +807,98 @@ function frameShape(frame, paragraphs, widthPt, exact) {
775
807
  };
776
808
  }
777
809
  /** §17.3.1.11 → §20.4.2.3: the frame's anchor, in the terms floats are placed in. */
810
+ /**
811
+ * `wps:linkedTxbx` — carry each chain's overflow from one box to the next.
812
+ * The words all live in the chain's FIRST box; a box shows as many of its lines
813
+ * as its height holds and hands the rest on, so LinkedTextBoxes.docx reads down
814
+ * one column and continues at the top of the other. A box that ends a chain
815
+ * keeps whatever is left, overflowing as it would have anyway.
816
+ *
817
+ * The lines are broken at the first box's width, so a chain of boxes of
818
+ * different widths sets its later columns to the wrong measure; re-breaking
819
+ * needs the source paragraphs split at the carry point, which the laid-out
820
+ * lines no longer know.
821
+ */
822
+ function flowLinkedTextBoxes(blocks) {
823
+ const chains = /* @__PURE__ */ new Map();
824
+ blocks.forEach((b, i) => {
825
+ if (b.kind !== "shape" || !b.textChain) return;
826
+ const at = chains.get(b.textChain.id) ?? [];
827
+ at.push(i);
828
+ chains.set(b.textChain.id, at);
829
+ });
830
+ for (const indices of chains.values()) {
831
+ if (indices.length < 2) continue;
832
+ indices.sort((a, b) => {
833
+ return (blocks[a].textChain?.seq ?? 0) - (blocks[b].textChain?.seq ?? 0);
834
+ });
835
+ let carried = [];
836
+ indices.forEach((idx, n) => {
837
+ const box = blocks[idx];
838
+ const all = n === 0 ? boxLines(box) : carried;
839
+ const last = n === indices.length - 1;
840
+ const room = box.heightPt - box.insetTopPt - box.insetBottomPt;
841
+ const kept = [];
842
+ let used = 0;
843
+ for (const l of all) {
844
+ const h = computeLineHeight(l.line, l.line.resolved) + l.gapPt;
845
+ if (!last && kept.length > 0 && used + h > room) break;
846
+ kept.push(l);
847
+ used += h;
848
+ }
849
+ carried = all.slice(kept.length);
850
+ blocks[idx] = rebuildBoxLines(box, kept);
851
+ });
852
+ }
853
+ }
854
+ function boxLines(box) {
855
+ return box.textLines.map((line, i) => ({
856
+ line,
857
+ gapPt: box.textLineGaps?.get(i) ?? 0,
858
+ ...box.textCharts?.get(i) ? { chart: box.textCharts.get(i) } : {},
859
+ ...box.textShapes?.get(i) ? { shape: box.textShapes.get(i) } : {},
860
+ ...box.textTables?.get(i) ? { table: box.textTables.get(i) } : {}
861
+ }));
862
+ }
863
+ function rebuildBoxLines(box, lines) {
864
+ const charts = /* @__PURE__ */ new Map();
865
+ const shapes = /* @__PURE__ */ new Map();
866
+ const tables = /* @__PURE__ */ new Map();
867
+ const gaps = /* @__PURE__ */ new Map();
868
+ let height = 0;
869
+ lines.forEach((l, i) => {
870
+ if (l.chart) charts.set(i, l.chart);
871
+ if (l.shape) shapes.set(i, l.shape);
872
+ if (l.table) tables.set(i, l.table);
873
+ if (l.gapPt > 0) gaps.set(i, l.gapPt);
874
+ height += computeLineHeight(l.line, l.line.resolved) + l.gapPt;
875
+ });
876
+ const { textCharts: _c, textShapes: _s, textTables: _t, textLineGaps: _g, ...rest } = box;
877
+ return {
878
+ ...rest,
879
+ textLines: lines.map((l) => l.line),
880
+ ...charts.size > 0 ? { textCharts: charts } : {},
881
+ ...shapes.size > 0 ? { textShapes: shapes } : {},
882
+ ...tables.size > 0 ? { textTables: tables } : {},
883
+ ...gaps.size > 0 ? { textLineGaps: gaps } : {},
884
+ textHeightPt: height
885
+ };
886
+ }
887
+ /**
888
+ * §17.3.1.11 — whether the frame is placed DOWN THE PAGE by itself, rather than
889
+ * where the flow has reached. `w:vAnchor="text"` (the default) measures `w:y`
890
+ * from the paragraph's own position, so such frames stack as paragraphs do —
891
+ * and a float takes no flow space, so lifting them out puts every one of them
892
+ * on the same line: tdf105035_framePrB.docx writes its title and its author
893
+ * line as text-anchored frames and both landed on the cursor, one over the
894
+ * other. Only a frame anchored to the page or the margin, or aligned by
895
+ * keyword, truly stands somewhere of its own.
896
+ */
897
+ function framePositioned(frame) {
898
+ return frame.yAlign !== void 0 || frame.vAnchor !== void 0 && frame.vAnchor !== "text";
899
+ }
778
900
  function frameFloat(frame) {
779
- const wrap = frame.wrap === "none" ? "none" : frame.wrap === "notBeside" ? "topAndBottom" : "square";
901
+ const wrap = frame.wrap === "none" ? "none" : frame.wrap === "notBeside" ? framePositioned(frame) ? "notBeside" : "topAndBottom" : "square";
780
902
  const hRel = frame.hAnchor === "page" ? "page" : frame.hAnchor === "margin" ? "margin" : "column";
781
903
  const vRel = frame.vAnchor === "page" ? "page" : frame.vAnchor === "margin" ? "margin" : "paragraph";
782
904
  const align = frame.xAlign === "left" || frame.xAlign === "center" || frame.xAlign === "right" ? frame.xAlign : void 0;
@@ -825,7 +947,10 @@ function laidOutBlocksFor(elements, options, fontResources, contentWidth, imageR
825
947
  function layoutBodyElement(el, options, fontResources, imageResources, contentWidth, maxHeight, box) {
826
948
  if (el.kind === "paragraph") return layoutParagraphBlock(el.paragraph, options, fontResources, imageResources, contentWidth);
827
949
  if (el.kind === "table") return layoutTableBlock(el.table, options, fontResources, imageResources, contentWidth);
828
- if (el.kind === "image") return layoutImageBlock(el.image, imageResources, contentWidth, maxHeight, box);
950
+ if (el.kind === "image") {
951
+ const meta = el.image.resource ? imageResources?.get(el.image.resource)?.metafile : void 0;
952
+ return meta ? layoutMetafileBlock(el.image, meta, options, fontResources, contentWidth, maxHeight, box) : layoutImageBlock(el.image, imageResources, contentWidth, maxHeight, box);
953
+ }
829
954
  if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight);
830
955
  return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight, box);
831
956
  }
@@ -873,6 +998,7 @@ function layoutImageBlock(image, imageResources, contentWidth, maxHeight, box) {
873
998
  resolvedAlignment,
874
999
  resourceName: res?.resourceName ?? "",
875
1000
  ...image.outline ? { outline: image.outline } : {},
1001
+ ...image.wash ? { wash: image.wash } : {},
876
1002
  ...image.crop ? { crop: image.crop } : {},
877
1003
  ...image.rotation60k ? { rotationDeg: image.rotation60k / 6e4 } : {},
878
1004
  ...image.flipH ? { flipH: true } : {},
@@ -1055,6 +1181,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1055
1181
  ...fillImageResourceName && shape.fill.imageCrop ? { fillImageCrop: shape.fill.imageCrop } : {},
1056
1182
  ...fillColorHex ? { fillColorHex } : {},
1057
1183
  ...fillGradient ? { fillGradient } : {},
1184
+ ...shape.fill.alpha !== void 0 && shape.fill.alpha < 1 ? { fillAlpha: shape.fill.alpha } : {},
1058
1185
  ...stroke ? { stroke } : {},
1059
1186
  ...shape.shadow ? { shadow: shape.shadow } : {},
1060
1187
  rotation60k: t?.rotation60k ?? 0,
@@ -1069,6 +1196,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1069
1196
  ...textTables.size > 0 ? { textTables } : {},
1070
1197
  ...textLineGaps.size > 0 ? { textLineGaps } : {},
1071
1198
  textHeightPt,
1199
+ ...text?.chain ? { textChain: text.chain } : {},
1072
1200
  insetLeftPt,
1073
1201
  insetRightPt,
1074
1202
  insetTopPt,
@@ -1079,6 +1207,144 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1079
1207
  ...shape.float ? { float: shape.float } : {}
1080
1208
  };
1081
1209
  }
1210
+ /**
1211
+ * MS-EMF / MS-WMF — a metafile picture as the primitives a chart is made of,
1212
+ * scaled from its own logical box into the box the drawing reserves.
1213
+ *
1214
+ * A metafile IS a little drawing program, and what it draws is paths and words:
1215
+ * the same two things a chart's layout carries, placed in the same local y-up
1216
+ * frame. So one is turned into the other rather than growing a block kind, a
1217
+ * pagination arm and an emitter of its own.
1218
+ */
1219
+ function layoutMetafileBlock(image, pic, options, fontResources, contentWidth, maxHeight, box) {
1220
+ const placed = layoutImageBlock(image, void 0, contentWidth, maxHeight, box);
1221
+ const widthPt = placed.widthPt;
1222
+ const heightPt = placed.heightPt;
1223
+ const layout = rotateDrawing(metafileDrawing(pic, widthPt, heightPt, options, fontResources), (image.rotation60k ?? 0) / 6e4, widthPt / 2, heightPt / 2);
1224
+ const pp = image.paragraphProperties;
1225
+ return {
1226
+ kind: "chart",
1227
+ widthPt,
1228
+ heightPt,
1229
+ layout,
1230
+ resolvedAlignment: pp.alignment ?? "left",
1231
+ spacingBeforePt: pp.spacingBefore ?? 0,
1232
+ spacingAfterPt: pp.spacingAfter ?? 0,
1233
+ figureRole: "Image",
1234
+ ...image.altText ? { altText: image.altText } : {},
1235
+ ...image.float ? { float: image.float } : {}
1236
+ };
1237
+ }
1238
+ /** The same primitives, turned `deg` CLOCKWISE about a point of the local frame. */
1239
+ function rotateDrawing(layout, deg, cx, cy) {
1240
+ if (!deg) return layout;
1241
+ const a = -deg * Math.PI / 180;
1242
+ const cos = Math.cos(a);
1243
+ const sin = Math.sin(a);
1244
+ const at = (x, y) => ({
1245
+ x: cx + (x - cx) * cos - (y - cy) * sin,
1246
+ y: cy + (x - cx) * sin + (y - cy) * cos
1247
+ });
1248
+ return {
1249
+ shapes: layout.shapes.map((sh) => ({
1250
+ ...sh,
1251
+ paths: sh.paths.map((path) => ({
1252
+ ...path,
1253
+ segments: path.segments.map((seg) => {
1254
+ if (seg.op === "close") return seg;
1255
+ if (seg.op === "cubic") {
1256
+ const c1 = at(seg.x1, seg.y1);
1257
+ const c2 = at(seg.x2, seg.y2);
1258
+ const e = at(seg.x, seg.y);
1259
+ return {
1260
+ op: "cubic",
1261
+ x1: c1.x,
1262
+ y1: c1.y,
1263
+ x2: c2.x,
1264
+ y2: c2.y,
1265
+ x: e.x,
1266
+ y: e.y
1267
+ };
1268
+ }
1269
+ const p = at(seg.x, seg.y);
1270
+ return {
1271
+ op: seg.op,
1272
+ x: p.x,
1273
+ y: p.y
1274
+ };
1275
+ })
1276
+ }))
1277
+ })),
1278
+ texts: layout.texts.map((t) => {
1279
+ const p = at(t.x, t.y);
1280
+ return {
1281
+ ...t,
1282
+ x: p.x,
1283
+ y: p.y,
1284
+ rotationDeg: (t.rotationDeg ?? 0) - deg
1285
+ };
1286
+ })
1287
+ };
1288
+ }
1289
+ /**
1290
+ * A metafile's primitives, scaled from its own logical box into a `widthPt` ×
1291
+ * `heightPt` one and turned y-up about its bottom-left corner — the frame both
1292
+ * a chart block and an inline picture token are drawn in.
1293
+ */
1294
+ function metafileDrawing(pic, widthPt, heightPt, options, fontResources) {
1295
+ const sx = widthPt / pic.width;
1296
+ const sy = heightPt / pic.height;
1297
+ const mapX = (x) => (x - pic.left) * sx;
1298
+ const mapY = (y) => heightPt - (y - pic.top) * sy;
1299
+ const shapes = [];
1300
+ const texts = [];
1301
+ for (const prim of pic.prims) {
1302
+ if (prim.kind === "path") {
1303
+ shapes.push({
1304
+ paths: prim.paths.map((path) => ({
1305
+ ...path,
1306
+ segments: path.segments.map((seg) => seg.op === "close" ? seg : seg.op === "cubic" ? {
1307
+ op: "cubic",
1308
+ x1: mapX(seg.x1),
1309
+ y1: mapY(seg.y1),
1310
+ x2: mapX(seg.x2),
1311
+ y2: mapY(seg.y2),
1312
+ x: mapX(seg.x),
1313
+ y: mapY(seg.y)
1314
+ } : {
1315
+ op: seg.op,
1316
+ x: mapX(seg.x),
1317
+ y: mapY(seg.y)
1318
+ })
1319
+ })),
1320
+ ...prim.fillColorHex !== void 0 ? { fillColorHex: prim.fillColorHex } : {},
1321
+ ...prim.stroke ? { stroke: {
1322
+ ...prim.stroke,
1323
+ widthPt: Math.max(.25, prim.stroke.widthPt * sx)
1324
+ } } : {}
1325
+ });
1326
+ continue;
1327
+ }
1328
+ if (prim.kind !== "text") continue;
1329
+ const { variant } = options.registry.resolveByStyle(prim.bold === true, prim.italic === true);
1330
+ const font = fontResources.get(variant);
1331
+ if (!font) continue;
1332
+ const sizePt = Math.max(1, prim.sizeLu * sy);
1333
+ const line = makeChartLabelLine(prim.text, font, sizePt, prim.colorHex);
1334
+ const shift = prim.alignH === "center" ? -line.contentWidthPt / 2 : prim.alignH === "right" ? -line.contentWidthPt : 0;
1335
+ const baselineY = mapY(prim.y) - (prim.alignBaseline ? 0 : sizePt * .8);
1336
+ texts.push({
1337
+ line,
1338
+ x: mapX(prim.x) + shift,
1339
+ y: baselineY,
1340
+ ...prim.escapement ? { rotationDeg: prim.escapement / 10 } : {}
1341
+ });
1342
+ }
1343
+ return {
1344
+ shapes,
1345
+ texts
1346
+ };
1347
+ }
1082
1348
  function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight) {
1083
1349
  let widthPt = block.width;
1084
1350
  let heightPt = block.height;
@@ -1376,6 +1642,15 @@ function collectImageResources(body, headersFooters, options) {
1376
1642
  for (const resourceId of seen) {
1377
1643
  const bytes = options.resources.get(resourceId);
1378
1644
  if (!bytes) continue;
1645
+ if (isEmf(bytes) || isWmf(bytes)) {
1646
+ try {
1647
+ out.set(resourceId, {
1648
+ resourceName: "",
1649
+ metafile: isEmf(bytes) ? readEmf(bytes) : readWmf(bytes)
1650
+ });
1651
+ } catch {}
1652
+ continue;
1653
+ }
1379
1654
  let prepared;
1380
1655
  try {
1381
1656
  prepared = prepareImage(bytes, { flattenAlpha });
@@ -1481,6 +1756,43 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
1481
1756
  textY -= sh.textLineGaps?.get(i) ?? 0;
1482
1757
  });
1483
1758
  }
1759
+ /** The page a table is being drawn onto, as a cell's floats need to see it. */
1760
+ function cellAnchorCtx(asm) {
1761
+ return {
1762
+ sink: (behind, zOrder) => PageAssembler.zSink(behind ? asm.floatsBehind : asm.floatsFront, zOrder),
1763
+ frame: asm.floatFrame(),
1764
+ exclude: (f, x, topYUp, widthPt, heightPt) => {
1765
+ asm.excludeFloat(f, x, topYUp, widthPt, heightPt);
1766
+ },
1767
+ pageHeight: asm.ctx.pageHeight
1768
+ };
1769
+ }
1770
+ function emitCellFloat(cf, ctx, frame) {
1771
+ const float = cf.block.float;
1772
+ if (!float) return;
1773
+ const x = floatXIn(frame, float, cf.block.widthPt);
1774
+ const topYUp = floatTopYUpIn(frame, float, cf.block.heightPt);
1775
+ const sink = ctx.sink(float.behind === true, float.zOrder);
1776
+ if (cf.block.kind === "shape") emitShapeItems(cf.block, x, topYUp - cf.block.heightPt, sink, ctx.pageHeight, void 0);
1777
+ else {
1778
+ const ins = cf.block.drawInset;
1779
+ sink.push({
1780
+ type: "image",
1781
+ x: pt(x + (ins?.dxPt ?? 0)),
1782
+ y: pt(ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0))),
1783
+ width: pt(ins?.widthPt ?? cf.block.widthPt),
1784
+ height: pt(ins?.heightPt ?? cf.block.heightPt),
1785
+ imageResourceName: cf.block.resourceName,
1786
+ ...cf.block.crop ? { crop: cf.block.crop } : {},
1787
+ ...cf.block.wash ? { wash: cf.block.wash } : {},
1788
+ ...cf.block.rotationDeg ? { rotationDeg: cf.block.rotationDeg } : {},
1789
+ ...cf.block.flipH ? { flipH: true } : {},
1790
+ ...cf.block.flipV ? { flipV: true } : {}
1791
+ });
1792
+ if (cf.block.outline) sink.push(...pictureOutlineItems(cf.block.outline, x + (ins?.dxPt ?? 0), ctx.pageHeight - (topYUp - (ins?.dyTopPt ?? 0)), ins?.widthPt ?? cf.block.widthPt, ins?.heightPt ?? cf.block.heightPt, void 0));
1793
+ }
1794
+ if (float.wrap !== "none") ctx.exclude(float, x, topYUp, cf.block.widthPt, cf.block.heightPt);
1795
+ }
1484
1796
  function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
1485
1797
  if (sh.fillImageResourceName) sink.push({
1486
1798
  type: "image",
@@ -1511,6 +1823,7 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
1511
1823
  paths: sh.paths,
1512
1824
  ...sh.fillColorHex ? { fillColorHex: sh.fillColorHex } : {},
1513
1825
  ...sh.fillGradient ? { fillGradient: sh.fillGradient } : {},
1826
+ ...sh.fillAlpha !== void 0 ? { fillAlpha: sh.fillAlpha } : {},
1514
1827
  ...sh.stroke ? { stroke: sh.stroke } : {},
1515
1828
  ...sh.shadow ? { shadow: sh.shadow } : {},
1516
1829
  transform: flipTransform(buildShapeTransform(x, bottomYUp, sh.widthPt, sh.heightPt, sh.rotation60k, sh.flipH, sh.flipV), pageHeight)
@@ -1620,6 +1933,7 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
1620
1933
  height: pt(block.heightPt),
1621
1934
  imageResourceName: block.resourceName,
1622
1935
  ...block.crop ? { crop: block.crop } : {},
1936
+ ...block.wash ? { wash: block.wash } : {},
1623
1937
  ...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
1624
1938
  ...block.flipH ? { flipH: true } : {},
1625
1939
  ...block.flipV ? { flipV: true } : {}
@@ -1739,7 +2053,7 @@ function runFontKeyAndParsed(options, ascii, bold, italic) {
1739
2053
  function lookupFont(resources, fontKey) {
1740
2054
  return resources.get(fontKey) ?? resources.values().next().value;
1741
2055
  }
1742
- function collectFontResources(body, headersFooters, options) {
2056
+ function collectFontResources(body, headersFooters, options, imageResources) {
1743
2057
  const used = /* @__PURE__ */ new Map();
1744
2058
  const addRun = (run, para) => {
1745
2059
  const resolved = resolveRunProperties(run.properties, para.properties, options.styles);
@@ -1774,30 +2088,48 @@ function collectFontResources(body, headersFooters, options) {
1774
2088
  for (const ch of seg.text) bucket.gids.add(parsed.glyphForCodepoint(ch.codePointAt(0)));
1775
2089
  }
1776
2090
  };
1777
- const visit = (elements) => {
1778
- for (const el of elements) if (el.kind === "paragraph") for (const run of displayCapsRuns(el.paragraph, options)) {
1779
- if (run.math) {
1780
- addMath(run.math);
1781
- continue;
1782
- }
1783
- if (run.inlineImage && !run.text) continue;
1784
- if (run.field !== void 0) {
1785
- addRun({
1786
- text: `${run.text}0123456789`,
1787
- properties: run.properties
1788
- }, el.paragraph);
1789
- continue;
1790
- }
1791
- if (run.footnoteRef !== void 0 || run.endnoteRef !== void 0 || run.noteNumber) {
1792
- addRun({
1793
- text: `${run.text}0123456789 `,
1794
- properties: run.properties
1795
- }, el.paragraph);
1796
- continue;
2091
+ const addMetafile = (resource) => {
2092
+ const pic = resource ? imageResources.get(resource)?.metafile : void 0;
2093
+ for (const prim of pic?.prims ?? []) {
2094
+ if (prim.kind !== "text") continue;
2095
+ const reg = options.registry.resolveByStyle(prim.bold === true, prim.italic === true);
2096
+ let bucket = used.get(reg.variant);
2097
+ if (!bucket) {
2098
+ bucket = {
2099
+ parsed: reg.parsed,
2100
+ gids: /* @__PURE__ */ new Set()
2101
+ };
2102
+ used.set(reg.variant, bucket);
1797
2103
  }
1798
- addRun(run, el.paragraph);
2104
+ for (const ch of prim.text) bucket.gids.add(reg.parsed.glyphForCodepoint(ch.codePointAt(0)));
1799
2105
  }
1800
- else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) {
2106
+ };
2107
+ const visit = (elements) => {
2108
+ for (const el of elements) if (el.kind === "paragraph") {
2109
+ for (const run of el.paragraph.runs) addMetafile(run.inlineImage?.resource);
2110
+ for (const run of displayCapsRuns(el.paragraph, options)) {
2111
+ if (run.math) {
2112
+ addMath(run.math);
2113
+ continue;
2114
+ }
2115
+ if (run.inlineImage && !run.text) continue;
2116
+ if (run.field !== void 0) {
2117
+ addRun({
2118
+ text: `${run.text}0123456789`,
2119
+ properties: run.properties
2120
+ }, el.paragraph);
2121
+ continue;
2122
+ }
2123
+ if (run.footnoteRef !== void 0 || run.endnoteRef !== void 0 || run.noteNumber) {
2124
+ addRun({
2125
+ text: `${run.text}0123456789 `,
2126
+ properties: run.properties
2127
+ }, el.paragraph);
2128
+ continue;
2129
+ }
2130
+ addRun(run, el.paragraph);
2131
+ }
2132
+ } else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) {
1801
2133
  if (cell.properties.hashOnOverflow === true) for (const inner of cell.content) {
1802
2134
  if (inner.kind !== "paragraph") continue;
1803
2135
  for (const run of inner.paragraph.runs) addRun({
@@ -1813,7 +2145,8 @@ function collectFontResources(body, headersFooters, options) {
1813
2145
  for (const child of sh.children ?? []) shapeText(child.shape);
1814
2146
  };
1815
2147
  shapeText(el.shape);
1816
- } else if (el.kind === "chart") {
2148
+ } else if (el.kind === "image") addMetafile(el.image.resource);
2149
+ else {
1817
2150
  const chart = options.charts?.get(el.chart.chartRelId);
1818
2151
  if (chart) {
1819
2152
  const reg = options.registry.resolveByStyle(false, false);
@@ -2104,8 +2437,10 @@ function tokenizeParagraph(paragraph, options, fontResources, imageResources, co
2104
2437
  imageWidthPt: widthPt,
2105
2438
  imageHeightPt: heightPt,
2106
2439
  imageResourceName: res?.resourceName ?? "",
2440
+ ...res?.metafile ? { imageMetafile: metafileDrawing(res.metafile, drawBox?.widthPt ?? widthPt, drawBox?.heightPt ?? heightPt, options, fontResources) } : {},
2107
2441
  ...run.inlineImage.outline ? { imageOutline: run.inlineImage.outline } : {},
2108
2442
  ...run.inlineImage.shadow ? { imageShadow: run.inlineImage.shadow } : {},
2443
+ ...run.inlineImage.wash ? { imageWash: run.inlineImage.wash } : {},
2109
2444
  ...run.inlineImage.crop ? { imageCrop: run.inlineImage.crop } : {},
2110
2445
  ...run.inlineImage.rotation60k ? { imageRotationDeg: run.inlineImage.rotation60k / 6e4 } : {},
2111
2446
  ...run.inlineImage.flipH ? { imageFlipH: true } : {},
@@ -2189,6 +2524,8 @@ function tokenizePlansLtr(plans) {
2189
2524
  heightPt: plan.imageHeightPt,
2190
2525
  ...plan.imageOutline ? { outline: plan.imageOutline } : {},
2191
2526
  ...plan.imageShadow ? { shadow: plan.imageShadow } : {},
2527
+ ...plan.imageWash ? { wash: plan.imageWash } : {},
2528
+ ...plan.imageMetafile ? { metafile: plan.imageMetafile } : {},
2192
2529
  ...plan.imageCrop ? { crop: plan.imageCrop } : {},
2193
2530
  ...plan.imageRotationDeg ? { rotationDeg: plan.imageRotationDeg } : {},
2194
2531
  ...plan.imageFlipH ? { flipH: true } : {},
@@ -2260,6 +2597,8 @@ function tokenizePlansBidi(plans, realLevels) {
2260
2597
  heightPt: plan.imageHeightPt,
2261
2598
  ...plan.imageOutline ? { outline: plan.imageOutline } : {},
2262
2599
  ...plan.imageShadow ? { shadow: plan.imageShadow } : {},
2600
+ ...plan.imageWash ? { wash: plan.imageWash } : {},
2601
+ ...plan.imageMetafile ? { metafile: plan.imageMetafile } : {},
2263
2602
  ...plan.imageCrop ? { crop: plan.imageCrop } : {},
2264
2603
  ...plan.imageRotationDeg ? { rotationDeg: plan.imageRotationDeg } : {},
2265
2604
  ...plan.imageFlipH ? { flipH: true } : {},
@@ -3158,7 +3497,8 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
3158
3497
  cells: cells.map((c) => clipCellToHeight(c, heightPt)),
3159
3498
  columnXOffsets,
3160
3499
  rowIdx,
3161
- rowCount
3500
+ rowCount,
3501
+ ...row.properties.cantSplit ? { cantSplit: true } : {}
3162
3502
  };
3163
3503
  } else if (row.properties.height && row.properties.heightRule === "atLeast") heightPt = Math.max(heightPt, row.properties.height);
3164
3504
  return {
@@ -3166,7 +3506,8 @@ function layoutTableRow(row, rowIdx, rowCount, colCount, tableProps, columnWidth
3166
3506
  cells,
3167
3507
  columnXOffsets,
3168
3508
  rowIdx,
3169
- rowCount
3509
+ rowCount,
3510
+ ...row.properties.cantSplit ? { cantSplit: true } : {}
3170
3511
  };
3171
3512
  }
3172
3513
  /**
@@ -3212,6 +3553,7 @@ function imageRun(image) {
3212
3553
  inlineImage: {
3213
3554
  ...image.resource ? { resource: image.resource } : {},
3214
3555
  ...image.outline ? { outline: image.outline } : {},
3556
+ ...image.wash ? { wash: image.wash } : {},
3215
3557
  width: image.width,
3216
3558
  height: image.height,
3217
3559
  ...image.crop ? { crop: image.crop } : {},
@@ -3235,6 +3577,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
3235
3577
  const lines = [];
3236
3578
  const nestedTables = [];
3237
3579
  const cellShapes = [];
3580
+ const cellFloats = [];
3238
3581
  const lineGaps = /* @__PURE__ */ new Map();
3239
3582
  let contentHeightPt = 0;
3240
3583
  let clipped = false;
@@ -3288,7 +3631,14 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
3288
3631
  });
3289
3632
  contentHeightPt += block.heightPt;
3290
3633
  pendingGapPt = block.spacingAfterPt;
3291
- }
3634
+ } else if (el.kind === "image" && isOutOfFlowFloat(el.image.float)) cellFloats.push({
3635
+ block: layoutImageBlock(el.image, imageResources, innerWidth),
3636
+ afterLine: lines.length
3637
+ });
3638
+ else if (el.kind === "shape" && isOutOfFlowFloat(el.shape.float)) cellFloats.push({
3639
+ block: layoutShapeBlock(el.shape, options, fontResources, imageResources, innerWidth),
3640
+ afterLine: lines.length
3641
+ });
3292
3642
  contentHeightPt += pendingGapPt;
3293
3643
  }
3294
3644
  const colEnd = colStart + colSpan - 1;
@@ -3309,6 +3659,7 @@ function layoutTableCell(cell, tableProps, widthPt, options, fontResources, imag
3309
3659
  ...lineGaps.size > 0 ? { lineGaps } : {},
3310
3660
  ...nestedTables.length > 0 ? { nestedTables } : {},
3311
3661
  ...cellShapes.length > 0 ? { shapes: cellShapes } : {},
3662
+ ...cellFloats.length > 0 ? { floats: cellFloats } : {},
3312
3663
  contentHeightPt,
3313
3664
  totalHeightPt,
3314
3665
  ...cell.properties.verticalAlign ? { verticalAlign: cell.properties.verticalAlign } : {},
@@ -3488,6 +3839,12 @@ var PageAssembler = class PageAssembler {
3488
3839
  /** The current column's width (the section content width when single-column). */
3489
3840
  colWidth = () => this.ctx.columns?.[this.colIdx]?.widthPt ?? this.ctx.contentWidth;
3490
3841
  /**
3842
+ * The measure the section's blocks were BROKEN at — the first column's, since
3843
+ * that is all the block pass can know. A column of another width has to
3844
+ * re-break what lands in it.
3845
+ */
3846
+ builtWidth = () => this.ctx.columns?.[0]?.widthPt ?? this.ctx.contentWidth;
3847
+ /**
3491
3848
  * Whether the current column is already in use.
3492
3849
  *
3493
3850
  * This gates every overflow break, so that a block too tall for an empty page
@@ -3639,44 +3996,36 @@ var PageAssembler = class PageAssembler {
3639
3996
  */
3640
3997
  excludeFloat = (f, x, topYUp, w, h) => {
3641
3998
  const d = f.wrapDist;
3999
+ const wide = f.wrap === "notBeside";
3642
4000
  this.exclusions.push({
3643
- x0: x - (d?.leftPt ?? 0),
3644
- x1: x + w + (d?.rightPt ?? 0),
4001
+ x0: wide ? this.colLeft() : x - (d?.leftPt ?? 0),
4002
+ x1: wide ? this.colLeft() + this.colWidth() : x + w + (d?.rightPt ?? 0),
3645
4003
  topYUp: topYUp + (d?.topPt ?? 0),
3646
- bottomYUp: topYUp - h - (d?.bottomPt ?? 0)
4004
+ bottomYUp: topYUp - h - (d?.bottomPt ?? 0),
4005
+ ...f.wrapSide === "bothSides" ? { wrapBothSides: true } : {}
3647
4006
  });
3648
4007
  };
4008
+ /** The frame a float anchored in the body flow is resolved against. */
4009
+ floatFrame = () => ({
4010
+ pageLeft: 0,
4011
+ pageWidth: this.ctx.pageWidth,
4012
+ pageTopYUp: this.ctx.pageHeight,
4013
+ pageBottomYUp: 0,
4014
+ marginLeft: this.ctx.marginLeft,
4015
+ contentWidth: this.ctx.contentWidth,
4016
+ marginTopYUp: this.ctx.pageHeight - this.ctx.marginTop,
4017
+ marginBottomYUp: this.ctx.marginBottom,
4018
+ columnLeft: this.colLeft(),
4019
+ columnWidth: this.colWidth(),
4020
+ anchorY: this.cursorY
4021
+ });
3649
4022
  /** The float's left edge on the page, resolved from its horizontal anchor. */
3650
- floatX = (f, widthPt) => {
3651
- const h = f.posH;
3652
- if (!h) return this.colLeft();
3653
- const textRight = this.ctx.marginLeft + this.ctx.contentWidth;
3654
- const base = h.relativeFrom === "page" || h.relativeFrom === "leftMargin" ? 0 : h.relativeFrom === "column" ? this.colLeft() : h.relativeFrom === "rightMargin" ? textRight : this.ctx.marginLeft;
3655
- const span = h.relativeFrom === "page" ? this.ctx.pageWidth : h.relativeFrom === "column" ? this.colWidth() : h.relativeFrom === "leftMargin" ? this.ctx.marginLeft : h.relativeFrom === "rightMargin" ? this.ctx.pageWidth - textRight : this.ctx.contentWidth;
3656
- if (h.align === "center") return base + (span - widthPt) / 2;
3657
- if (h.align === "right") return base + span - widthPt;
3658
- return base + (h.offsetPt ?? 0);
3659
- };
4023
+ floatX = (f, widthPt) => floatXIn(this.floatFrame(), f, widthPt);
3660
4024
  /**
3661
4025
  * The drawing's TOP in the y-up cursor frame. Paragraph/line-relative offsets
3662
4026
  * hang off the anchoring paragraph's current position.
3663
4027
  */
3664
- floatTopYUp = (f, heightPt = 0) => {
3665
- const v = f.posV;
3666
- if (!v) return this.cursorY;
3667
- if (v.align !== void 0) {
3668
- const page = v.relativeFrom === "page";
3669
- const topYUp = page ? this.ctx.pageHeight : this.ctx.pageHeight - this.ctx.marginTop;
3670
- const bottomYUp = page ? 0 : this.ctx.marginBottom;
3671
- if (v.align === "top") return topYUp;
3672
- if (v.align === "bottom") return bottomYUp + heightPt;
3673
- return bottomYUp + (topYUp - bottomYUp + heightPt) / 2;
3674
- }
3675
- if (v.relativeFrom === "page" || v.relativeFrom === "topMargin") return this.ctx.pageHeight - (v.offsetPt ?? 0);
3676
- if (v.relativeFrom === "bottomMargin") return this.ctx.marginBottom - (v.offsetPt ?? 0);
3677
- if (v.relativeFrom === "margin") return this.ctx.pageHeight - this.ctx.marginTop - (v.offsetPt ?? 0);
3678
- return this.cursorY - (v.offsetPt ?? 0);
3679
- };
4028
+ floatTopYUp = (f, heightPt = 0) => floatTopYUpIn(this.floatFrame(), f, heightPt);
3680
4029
  /**
3681
4030
  * Tagged PDF: the stack of open list levels (for L/LI/LBody nesting). Cleared
3682
4031
  * whenever a non-list-item block interrupts the run of list paragraphs.
@@ -3739,6 +4088,14 @@ var PageAssembler = class PageAssembler {
3739
4088
  xOffset: 0,
3740
4089
  blockedUntilYUp: e.bottomYUp
3741
4090
  };
4091
+ if (leftRoom >= MIN_WRAP_WIDTH && rightRoom >= MIN_WRAP_WIDTH && e.wrapBothSides) return {
4092
+ width: leftRoom,
4093
+ xOffset: 0,
4094
+ pair: {
4095
+ width: rightRoom,
4096
+ xOffset: full - rightRoom
4097
+ }
4098
+ };
3742
4099
  if (rightRoom >= leftRoom) return {
3743
4100
  width: rightRoom,
3744
4101
  xOffset: full - rightRoom
@@ -3770,6 +4127,7 @@ var PageAssembler = class PageAssembler {
3770
4127
  continue;
3771
4128
  }
3772
4129
  widths.push(g.width);
4130
+ if (g.pair) widths.push(g.pair.width);
3773
4131
  if (g.width < this.colWidth()) narrowed = true;
3774
4132
  else if (narrowed) break;
3775
4133
  y -= h0;
@@ -4083,9 +4441,11 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4083
4441
  if ((block.resolved.pageBreakBefore || block.pageBreakBefore) && asm.pageHasContent()) asm.flushPage();
4084
4442
  asm.cursorY -= block.spacingBeforePt;
4085
4443
  let pb = block;
4444
+ const colW = asm.colWidth();
4445
+ if (reflowParagraph && pb.source && Math.abs(colW - asm.builtWidth()) > .5) pb = reflowParagraph(pb.source, colW);
4086
4446
  if (reflowParagraph && pb.source && asm.exclusions.length > 0) {
4087
- const widths = asm.lineWidthsFor(block, asm.cursorY);
4088
- if (widths) pb = reflowParagraph(pb.source, asm.colWidth(), widths);
4447
+ const widths = asm.lineWidthsFor(pb, asm.cursorY);
4448
+ if (widths) pb = reflowParagraph(pb.source, colW, widths);
4089
4449
  }
4090
4450
  const borderTopY = asm.cursorY;
4091
4451
  const borderPage = asm.pages.length;
@@ -4103,6 +4463,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4103
4463
  if (lang && lang !== defaultLang) builder.node(structId).lang = lang;
4104
4464
  }
4105
4465
  let firstLineOfBlock = true;
4466
+ let pairPending;
4106
4467
  for (const [lineIdx, line] of pb.lines.entries()) {
4107
4468
  if (pb.columnBreakLines?.has(lineIdx) && asm.colHasContent()) asm.advanceColumn();
4108
4469
  const h = computeLineHeight(line, pb.resolved);
@@ -4132,16 +4493,22 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4132
4493
  });
4133
4494
  }
4134
4495
  }
4135
- for (let guard = 0; guard < 8 && asm.exclusions.length > 0; guard++) {
4136
- const blocked = asm.lineGeometryAt(asm.cursorY, h).blockedUntilYUp;
4137
- if (blocked === void 0 || blocked >= asm.cursorY) break;
4138
- asm.cursorY = blocked;
4496
+ const pairedNow = pairPending;
4497
+ pairPending = void 0;
4498
+ if (pairedNow === void 0) {
4499
+ for (let guard = 0; guard < 8 && asm.exclusions.length > 0; guard++) {
4500
+ const blocked = asm.lineGeometryAt(asm.cursorY, h).blockedUntilYUp;
4501
+ if (blocked === void 0 || blocked >= asm.cursorY) break;
4502
+ asm.cursorY = blocked;
4503
+ }
4504
+ asm.cursorY -= h;
4139
4505
  }
4140
- asm.cursorY -= h;
4141
4506
  const indentLeft = pb.resolved.indentLeft + (line.firstLine ? pb.resolved.indentFirstLine : 0);
4142
4507
  const offset = alignmentOffset(pb.resolved.alignment, line.contentWidthPt, line.availableWidthPt);
4143
4508
  const baselineY = pt(asm.ctx.pageHeight - (asm.cursorY + lineBaselineOffset(line, pb.resolved)));
4144
- const exclusionShift = asm.exclusions.length > 0 ? asm.lineGeometryAt(asm.cursorY + h, h).xOffset : 0;
4509
+ const geometry = asm.exclusions.length > 0 ? asm.lineGeometryAt(asm.cursorY + h, h) : void 0;
4510
+ const exclusionShift = pairedNow ?? geometry?.xOffset ?? 0;
4511
+ if (pairedNow === void 0 && geometry?.pair && Math.abs(line.availableWidthPt - geometry.width) < 1) pairPending = geometry.pair.xOffset;
4145
4512
  const originX = asm.colLeft() + indentLeft + offset + exclusionShift;
4146
4513
  if (markerLblId !== void 0 && line === firstLine) {
4147
4514
  let k = 0;
@@ -4197,6 +4564,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4197
4564
  height: pt(ins?.heightPt ?? block.heightPt),
4198
4565
  imageResourceName: block.resourceName,
4199
4566
  ...block.crop ? { crop: block.crop } : {},
4567
+ ...block.wash ? { wash: block.wash } : {},
4200
4568
  ...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
4201
4569
  ...block.flipH ? { flipH: true } : {},
4202
4570
  ...block.flipV ? { flipV: true } : {},
@@ -4236,7 +4604,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4236
4604
  asm.cursorY -= block.spacingAfterPt;
4237
4605
  }
4238
4606
  } else if (block.kind === "chart") {
4239
- const figId = builder ? createFigure(builder, block.altText, "Chart") : void 0;
4607
+ const figId = builder ? createFigure(builder, block.altText, block.figureRole ?? "Chart") : void 0;
4240
4608
  const emitChartAt = (x, bottomYUp, sink) => {
4241
4609
  sink.push(...chartPageItems(block, x, bottomYUp, asm.ctx.pageHeight, figId));
4242
4610
  };
@@ -4262,7 +4630,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4262
4630
  const sink = PageAssembler.zSink(block.float.behind ? asm.floatsBehind : asm.floatsFront, block.float.zOrder);
4263
4631
  let rowY = fy;
4264
4632
  for (const row of block.rows) {
4265
- emitRowChunk(sink, row, fx, rowY, asm.ctx.pageHeight, colCount);
4633
+ emitRowChunk(sink, row, fx, rowY, asm.ctx.pageHeight, colCount, void 0, void 0, cellAnchorCtx(asm));
4266
4634
  rowY -= row.heightPt;
4267
4635
  }
4268
4636
  if (block.float.wrap !== "none") asm.excludeFloat(block.float, fx, fy, block.totalWidthPt, block.heightPt);
@@ -4291,7 +4659,9 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4291
4659
  return b.create("P", cellNode).id;
4292
4660
  });
4293
4661
  }
4294
- const chunks = row.heightPt > asm.ctx.pageContentHeight ? splitRowIntoChunks(row, asm.ctx.pageContentHeight) : [row];
4662
+ const room = asm.cursorY - asm.bottomLimit();
4663
+ const page = asm.ctx.pageContentHeight;
4664
+ const chunks = !row.cantSplit && row.heightPt > room && asm.colHasContent() && room > 0 ? splitRowIntoChunks(row, room, page) : row.heightPt > page ? splitRowIntoChunks(row, page) : [row];
4295
4665
  for (let ci = 0; ci < chunks.length; ci++) {
4296
4666
  const chunk = chunks[ci];
4297
4667
  const forcedBreak = ci === 0 && row.breakBefore && !isLeadingHeader && asm.current.length > 0;
@@ -4305,7 +4675,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4305
4675
  }
4306
4676
  }
4307
4677
  const mergedHeights = chunks.length === 1 && row.cells.some((c) => c.mergeRole === "start") ? row.cells.map((c) => c.mergeRole === "start" ? mergedBoxHeightPt(block.rows, ri, c.colStart, asm.cursorY - asm.bottomLimit()) : void 0) : void 0;
4308
- emitRowChunk(asm.current, chunk, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, cellStructIds, mergedHeights);
4678
+ emitRowChunk(asm.current, chunk, tableX, asm.cursorY, asm.ctx.pageHeight, colCount, cellStructIds, mergedHeights, cellAnchorCtx(asm));
4309
4679
  asm.cursorY -= chunk.heightPt;
4310
4680
  if (ci === chunks.length - 1 && ri < block.rows.length - 1) {
4311
4681
  const next = block.rows[ri + 1];
@@ -4374,7 +4744,7 @@ function tableCellRowSpan(rows, startRowIdx, colStart) {
4374
4744
  }
4375
4745
  return span;
4376
4746
  }
4377
- function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellStructIds, mergedHeights) {
4747
+ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellStructIds, mergedHeights, anchored) {
4378
4748
  const rowTop = cursorY;
4379
4749
  const rowBottom = cursorY - row.heightPt;
4380
4750
  {
@@ -4553,7 +4923,7 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
4553
4923
  if (nt.afterLine !== n) continue;
4554
4924
  for (const nrow of nt.block.rows) {
4555
4925
  const nestedIds = structId !== void 0 ? nrow.cells.map(() => structId) : void 0;
4556
- emitRowChunk(out, nrow, nestedX, textY, pageHeight, nt.block.colCount, nestedIds);
4926
+ emitRowChunk(out, nrow, nestedX, textY, pageHeight, nt.block.colCount, nestedIds, void 0, anchored);
4557
4927
  textY -= nrow.heightPt;
4558
4928
  }
4559
4929
  }
@@ -4563,6 +4933,29 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
4563
4933
  textY -= cs.block.heightPt;
4564
4934
  emitShapeItems(cs.block, nestedX + offset, textY, out, pageHeight, structId);
4565
4935
  }
4936
+ for (const cf of cell.floats ?? []) {
4937
+ if (cf.afterLine !== n || !anchored) continue;
4938
+ const inner = cell.widthPt - cell.padLeftPt - cell.padRightPt;
4939
+ const cellTop = rowTop - cell.padTopPt;
4940
+ const cellBottom = rowTop - (mergedHeights?.[i] ?? row.heightPt) + cell.padBottomPt;
4941
+ const own = cf.block.float?.inCell !== false;
4942
+ emitCellFloat(cf, anchored, {
4943
+ ...anchored.frame,
4944
+ ...own ? {
4945
+ pageLeft: nestedX,
4946
+ pageWidth: inner,
4947
+ pageTopYUp: cellTop,
4948
+ pageBottomYUp: cellBottom,
4949
+ marginLeft: nestedX,
4950
+ contentWidth: inner,
4951
+ marginTopYUp: cellTop,
4952
+ marginBottomYUp: cellBottom
4953
+ } : {},
4954
+ columnLeft: nestedX,
4955
+ columnWidth: inner,
4956
+ anchorY: textY
4957
+ });
4958
+ }
4566
4959
  };
4567
4960
  for (const [lineIdx, line] of cell.lines.entries()) {
4568
4961
  drawNestedBefore(lineIdx);
@@ -4588,10 +4981,13 @@ function emitRowChunk(out, row, marginLeft, cursorY, pageHeight, colCount, cellS
4588
4981
  drawNestedBefore(cell.lines.length);
4589
4982
  }
4590
4983
  }
4591
- function splitRowIntoChunks(row, capacity) {
4984
+ function splitRowIntoChunks(row, capacity, restCapacity = capacity) {
4592
4985
  if (row.cells.some((c) => c.nestedTables && c.nestedTables.length > 0)) return [row];
4593
4986
  const queues = row.cells.map((c) => ({
4594
- remaining: [...c.lines],
4987
+ remaining: c.lines.map((line, i) => ({
4988
+ line,
4989
+ gapPt: c.lineGaps?.get(i) ?? 0
4990
+ })),
4595
4991
  template: c
4596
4992
  }));
4597
4993
  const anyHasLines = () => queues.some((q) => q.template.mergeRole === "middle" || q.template.mergeRole === "end" ? false : q.remaining.length > 0);
@@ -4609,23 +5005,31 @@ function splitRowIntoChunks(row, capacity) {
4609
5005
  continue;
4610
5006
  }
4611
5007
  const padTop = isFirst ? tpl.padTopPt : 0;
4612
- const capacityForLines = Math.max(0, capacity - padTop);
5008
+ const capacityForLines = Math.max(0, (isFirst ? capacity : restCapacity) - padTop);
4613
5009
  const taken = [];
4614
5010
  let takenHeight = 0;
4615
5011
  while (q.remaining.length > 0) {
4616
5012
  const next = q.remaining[0];
4617
- const lh = computeLineHeight(next, next.resolved);
5013
+ const lh = computeLineHeight(next.line, next.line.resolved) + next.gapPt;
4618
5014
  if (taken.length > 0 && takenHeight + lh > capacityForLines) break;
4619
5015
  taken.push(next);
4620
5016
  takenHeight += lh;
4621
5017
  q.remaining.shift();
4622
5018
  }
4623
5019
  const cellHeight = padTop + takenHeight;
5020
+ const gaps = /* @__PURE__ */ new Map();
5021
+ taken.forEach((t, li) => {
5022
+ if (t.gapPt > 0) gaps.set(li, t.gapPt);
5023
+ });
5024
+ const { lineGaps: _g, shapes: _s, floats: _f, ...restOfCell } = tpl;
4624
5025
  const chunkCell = {
4625
- ...tpl,
5026
+ ...restOfCell,
5027
+ ...isFirst && tpl.shapes ? { shapes: tpl.shapes } : {},
5028
+ ...isFirst && tpl.floats ? { floats: tpl.floats } : {},
5029
+ ...gaps.size > 0 ? { lineGaps: gaps } : {},
4626
5030
  padTopPt: padTop,
4627
5031
  padBottomPt: 0,
4628
- lines: taken,
5032
+ lines: taken.map((t) => t.line),
4629
5033
  contentHeightPt: takenHeight,
4630
5034
  totalHeightPt: cellHeight
4631
5035
  };
@@ -4664,7 +5068,8 @@ function splitRowIntoChunks(row, capacity) {
4664
5068
  cells: chunkCells,
4665
5069
  columnXOffsets: row.columnXOffsets,
4666
5070
  rowIdx: row.rowIdx,
4667
- rowCount: row.rowCount
5071
+ rowCount: row.rowCount,
5072
+ ...row.isHeader ? { isHeader: true } : {}
4668
5073
  });
4669
5074
  isFirst = false;
4670
5075
  }