reamkit 1.20.0 → 1.22.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 (41) hide show
  1. package/dist/esm/core/bytes.d.ts +22 -4
  2. package/dist/esm/core/bytes.js +73 -5
  3. package/dist/esm/core/crypto/offcrypto.d.ts +1 -0
  4. package/dist/esm/core/crypto/offcrypto.js +2 -1
  5. package/dist/esm/core/document-model/types.d.ts +60 -0
  6. package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
  7. package/dist/esm/core/drawingml/chart-parser.js +23 -1
  8. package/dist/esm/core/drawingml/colors.d.ts +27 -3
  9. package/dist/esm/core/drawingml/colors.js +30 -6
  10. package/dist/esm/core/drawingml/preset-geometry.js +236 -0
  11. package/dist/esm/core/metafile/emf.js +2 -1
  12. package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
  13. package/dist/esm/core/metafile/symbol-fonts.js +154 -0
  14. package/dist/esm/core/metafile/wmf.js +35 -2
  15. package/dist/esm/core/vector.d.ts +5 -0
  16. package/dist/esm/excel/sheet-shape-parser.js +1 -12
  17. package/dist/esm/excel/xlsx-reader.d.ts +1 -1
  18. package/dist/esm/excel/xlsx-reader.js +3 -3
  19. package/dist/esm/index.d.ts +1 -0
  20. package/dist/esm/index.js +2 -1
  21. package/dist/esm/layout/page-doc.d.ts +38 -0
  22. package/dist/esm/layout/page-doc.js +34 -18
  23. package/dist/esm/layout/styled-layout.js +174 -49
  24. package/dist/esm/pdf/shading.d.ts +19 -0
  25. package/dist/esm/pdf/shading.js +84 -11
  26. package/dist/esm/pdf/styled-page-emitter.js +614 -444
  27. package/dist/esm/pptx/ole-preview.d.ts +10 -0
  28. package/dist/esm/pptx/ole-preview.js +52 -0
  29. package/dist/esm/pptx/placeholder-cascade.d.ts +69 -5
  30. package/dist/esm/pptx/placeholder-cascade.js +162 -25
  31. package/dist/esm/pptx/pptx-reader.js +247 -39
  32. package/dist/esm/pptx/slide-parser.d.ts +72 -10
  33. package/dist/esm/pptx/slide-parser.js +310 -82
  34. package/dist/esm/pptx/sp-helpers.js +19 -4
  35. package/dist/esm/pptx/table-style.d.ts +56 -0
  36. package/dist/esm/pptx/table-style.js +161 -0
  37. package/dist/esm/svg/svg-writer.js +53 -5
  38. package/dist/esm/word/docx-reader.js +2 -2
  39. package/dist/esm/word/drawing-parser.d.ts +34 -0
  40. package/dist/esm/word/drawing-parser.js +159 -13
  41. package/package.json +1 -1
@@ -5,30 +5,46 @@ function paintPlan(commands) {
5
5
  const borders = [];
6
6
  const shapes = [];
7
7
  const lines = [];
8
- for (const c of commands) switch (c.type) {
9
- case "fill":
10
- fills.push(c);
11
- break;
12
- case "image":
13
- images.push(c);
14
- break;
15
- case "border":
16
- borders.push(c);
17
- break;
18
- case "shape":
19
- shapes.push(c);
20
- break;
21
- case "line":
22
- lines.push(c);
23
- break;
24
- default: assertNeverPageItem(c);
8
+ const pictures = /* @__PURE__ */ new Map();
9
+ const behind = [];
10
+ for (const c of commands) {
11
+ if (c.behind === true) {
12
+ behind.push(c);
13
+ continue;
14
+ }
15
+ if (c.pictureId !== void 0) {
16
+ const group = pictures.get(c.pictureId);
17
+ if (group) group.push(c);
18
+ else pictures.set(c.pictureId, [c]);
19
+ continue;
20
+ }
21
+ switch (c.type) {
22
+ case "fill":
23
+ fills.push(c);
24
+ break;
25
+ case "image":
26
+ images.push(c);
27
+ break;
28
+ case "border":
29
+ borders.push(c);
30
+ break;
31
+ case "shape":
32
+ shapes.push(c);
33
+ break;
34
+ case "line":
35
+ lines.push(c);
36
+ break;
37
+ default: assertNeverPageItem(c);
38
+ }
25
39
  }
26
40
  return {
41
+ behind,
27
42
  fills,
28
43
  images,
29
44
  borders,
30
45
  shapes,
31
- lines
46
+ lines,
47
+ pictures: [...pictures.values()]
32
48
  };
33
49
  }
34
50
  function assertNeverPageItem(item) {
@@ -951,7 +951,7 @@ function layoutBodyElement(el, options, fontResources, imageResources, contentWi
951
951
  const meta = el.image.resource ? imageResources?.get(el.image.resource)?.metafile : void 0;
952
952
  return meta ? layoutMetafileBlock(el.image, meta, options, fontResources, contentWidth, maxHeight, box) : layoutImageBlock(el.image, imageResources, contentWidth, maxHeight, box);
953
953
  }
954
- if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight);
954
+ if (el.kind === "chart") return layoutChartBlock(el.chart, options, fontResources, contentWidth, maxHeight, imageResources);
955
955
  return layoutShapeBlock(el.shape, options, fontResources, imageResources, contentWidth, maxHeight, box);
956
956
  }
957
957
  function layoutImageBlock(image, imageResources, contentWidth, maxHeight, box) {
@@ -999,6 +999,7 @@ function layoutImageBlock(image, imageResources, contentWidth, maxHeight, box) {
999
999
  resourceName: res?.resourceName ?? "",
1000
1000
  ...image.outline ? { outline: image.outline } : {},
1001
1001
  ...image.wash ? { wash: image.wash } : {},
1002
+ ...image.alpha !== void 0 && image.alpha < 1 ? { alpha: image.alpha } : {},
1002
1003
  ...image.crop ? { crop: image.crop } : {},
1003
1004
  ...image.rotation60k ? { rotationDeg: image.rotation60k / 6e4 } : {},
1004
1005
  ...image.flipH ? { flipH: true } : {},
@@ -1073,7 +1074,7 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1073
1074
  continue;
1074
1075
  }
1075
1076
  if (el.kind === "chart") {
1076
- const laid = layoutChartBlock(el.chart, options, fontResources, innerWidth);
1077
+ const laid = layoutChartBlock(el.chart, options, fontResources, innerWidth, void 0, imageResources);
1077
1078
  const line = {
1078
1079
  tokens: [],
1079
1080
  contentWidthPt: laid.widthPt,
@@ -1185,6 +1186,8 @@ function layoutShapeBlock(shape, options, fontResources, imageResources, content
1185
1186
  ...fillImageResourceName ? { fillImageResourceName } : {},
1186
1187
  ...fillImageTile ? { fillImageTile } : {},
1187
1188
  ...fillImageResourceName && shape.fill.imageCrop ? { fillImageCrop: shape.fill.imageCrop } : {},
1189
+ ...fillImageResourceName && shape.fill.imageFillRect ? { fillImageRect: shape.fill.imageFillRect } : {},
1190
+ ...fillImageResourceName && shape.fill.duotone ? { fillImageDuotone: shape.fill.duotone } : {},
1188
1191
  ...fillColorHex ? { fillColorHex } : {},
1189
1192
  ...fillGradient ? { fillGradient } : {},
1190
1193
  ...shape.fill.alpha !== void 0 && shape.fill.alpha < 1 ? { fillAlpha: shape.fill.alpha } : {},
@@ -1226,7 +1229,7 @@ function layoutMetafileBlock(image, pic, options, fontResources, contentWidth, m
1226
1229
  const placed = layoutImageBlock(image, void 0, contentWidth, maxHeight, box);
1227
1230
  const widthPt = placed.widthPt;
1228
1231
  const heightPt = placed.heightPt;
1229
- const layout = rotateDrawing(metafileDrawing(pic, widthPt, heightPt, options, fontResources), (image.rotation60k ?? 0) / 6e4, widthPt / 2, heightPt / 2);
1232
+ const layout = rotateDrawing(metafileDrawing(pic, widthPt, heightPt, options, fontResources, image.colorChange), (image.rotation60k ?? 0) / 6e4, widthPt / 2, heightPt / 2);
1230
1233
  const pp = image.paragraphProperties;
1231
1234
  return {
1232
1235
  kind: "chart",
@@ -1253,6 +1256,7 @@ function rotateDrawing(layout, deg, cx, cy) {
1253
1256
  });
1254
1257
  return {
1255
1258
  shapes: layout.shapes.map((sh) => ({
1259
+ ...sh.seq !== void 0 ? { seq: sh.seq } : {},
1256
1260
  ...sh,
1257
1261
  paths: sh.paths.map((path) => ({
1258
1262
  ...path,
@@ -1297,16 +1301,29 @@ function rotateDrawing(layout, deg, cx, cy) {
1297
1301
  * `heightPt` one and turned y-up about its bottom-left corner — the frame both
1298
1302
  * a chart block and an inline picture token are drawn in.
1299
1303
  */
1300
- function metafileDrawing(pic, widthPt, heightPt, options, fontResources) {
1304
+ function metafileDrawing(pic, widthPt, heightPt, options, fontResources, colorChange) {
1305
+ const recolour = (hex) => {
1306
+ if (hex === void 0 || colorChange === void 0) return hex;
1307
+ if (hex.toUpperCase() !== colorChange.fromHex.toUpperCase()) return hex;
1308
+ return colorChange.transparent ? null : colorChange.toHex;
1309
+ };
1301
1310
  const sx = widthPt / pic.width;
1302
1311
  const sy = heightPt / pic.height;
1303
1312
  const mapX = (x) => (x - pic.left) * sx;
1304
1313
  const mapY = (y) => heightPt - (y - pic.top) * sy;
1305
1314
  const shapes = [];
1306
1315
  const texts = [];
1316
+ let seq = 0;
1307
1317
  for (const prim of pic.prims) {
1308
1318
  if (prim.kind === "path") {
1319
+ const fill = recolour(prim.fillColorHex);
1320
+ const strokeHex = recolour(prim.stroke?.colorHex);
1321
+ if (fill === null && (prim.stroke === void 0 || strokeHex === null)) {
1322
+ seq++;
1323
+ continue;
1324
+ }
1309
1325
  shapes.push({
1326
+ seq: seq++,
1310
1327
  paths: prim.paths.map((path) => ({
1311
1328
  ...path,
1312
1329
  segments: path.segments.map((seg) => seg.op === "close" ? seg : seg.op === "cubic" ? {
@@ -1323,9 +1340,10 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources) {
1323
1340
  y: mapY(seg.y)
1324
1341
  })
1325
1342
  })),
1326
- ...prim.fillColorHex !== void 0 ? { fillColorHex: prim.fillColorHex } : {},
1327
- ...prim.stroke ? { stroke: {
1343
+ ...fill !== void 0 && fill !== null ? { fillColorHex: fill } : {},
1344
+ ...prim.stroke && strokeHex !== null ? { stroke: {
1328
1345
  ...prim.stroke,
1346
+ ...strokeHex !== void 0 ? { colorHex: strokeHex } : {},
1329
1347
  widthPt: Math.max(.25, prim.stroke.widthPt * sx)
1330
1348
  } } : {}
1331
1349
  });
@@ -1336,10 +1354,16 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources) {
1336
1354
  const font = fontResources.get(variant);
1337
1355
  if (!font) continue;
1338
1356
  const sizePt = Math.max(1, prim.sizeLu * sy);
1339
- const line = makeChartLabelLine(prim.text, font, sizePt, prim.colorHex);
1357
+ const textHex = recolour(prim.colorHex);
1358
+ if (textHex === null) {
1359
+ seq++;
1360
+ continue;
1361
+ }
1362
+ const line = makeChartLabelLine(prim.text, font, sizePt, textHex ?? prim.colorHex);
1340
1363
  const shift = prim.alignH === "center" ? -line.contentWidthPt / 2 : prim.alignH === "right" ? -line.contentWidthPt : 0;
1341
1364
  const baselineY = mapY(prim.y) - (prim.alignBaseline ? 0 : sizePt * .8);
1342
1365
  texts.push({
1366
+ seq: seq++,
1343
1367
  line,
1344
1368
  x: mapX(prim.x) + shift,
1345
1369
  y: baselineY,
@@ -1351,7 +1375,7 @@ function metafileDrawing(pic, widthPt, heightPt, options, fontResources) {
1351
1375
  texts
1352
1376
  };
1353
1377
  }
1354
- function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight) {
1378
+ function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight, imageResources) {
1355
1379
  let widthPt = block.width;
1356
1380
  let heightPt = block.height;
1357
1381
  if (widthPt > contentWidth && widthPt > 0) {
@@ -1373,11 +1397,20 @@ function layoutChartBlock(block, options, fontResources, contentWidth, maxHeight
1373
1397
  };
1374
1398
  const pp = block.paragraphProperties;
1375
1399
  const altText = block.altText ?? chart?.title;
1400
+ const bgRes = chart?.frameFillImage ? imageResources?.get(chart.frameFillImage.resource) : void 0;
1401
+ const bgTile = chart?.frameFillImage?.tiled === true && bgRes?.prepared ? {
1402
+ widthPt: bgRes.prepared.widthPx * 72 / 96,
1403
+ heightPt: bgRes.prepared.heightPx * 72 / 96
1404
+ } : void 0;
1376
1405
  return {
1377
1406
  kind: "chart",
1378
1407
  widthPt,
1379
1408
  heightPt,
1380
1409
  layout,
1410
+ ...bgRes ? { background: {
1411
+ resourceName: bgRes.resourceName,
1412
+ ...bgTile ? { tile: bgTile } : {}
1413
+ } } : {},
1381
1414
  resolvedAlignment: pp.alignment ?? "left",
1382
1415
  spacingBeforePt: pp.spacingBefore ?? 0,
1383
1416
  spacingAfterPt: pp.spacingAfter ?? 0,
@@ -1653,6 +1686,7 @@ function collectImageResources(body, headersFooters, options) {
1653
1686
  }
1654
1687
  };
1655
1688
  visit(body);
1689
+ for (const chart of options.charts?.values() ?? []) if (chart.frameFillImage) seen.add(chart.frameFillImage.resource);
1656
1690
  if (options.pageBackgroundFill?.imageResource !== void 0) seen.add(options.pageBackgroundFill.imageResource);
1657
1691
  for (const hf of headersFooters.values()) visit(hf);
1658
1692
  for (const note of options.footnotes?.values() ?? []) visit(note);
@@ -1688,8 +1722,47 @@ function collectImageResources(body, headersFooters, options) {
1688
1722
  }
1689
1723
  function chartPageItems(laid, x, bottomYUp, pageHeight, structId) {
1690
1724
  const fig = structId !== void 0 ? { structId } : {};
1691
- const out = [];
1692
- for (const sh of laid.layout.shapes) out.push({
1725
+ const ordered = laid.layout.shapes.some((sh) => sh.seq !== void 0);
1726
+ const picture = ordered ? { pictureId: nextPictureId++ } : {};
1727
+ const backdrop = [];
1728
+ if (laid.background) {
1729
+ const box = {
1730
+ x,
1731
+ y: pageHeight - (bottomYUp + laid.heightPt),
1732
+ width: laid.widthPt,
1733
+ height: laid.heightPt
1734
+ };
1735
+ const tile = laid.background.tile;
1736
+ const clip = {
1737
+ paths: [rectPath(laid.widthPt, laid.heightPt)],
1738
+ transform: flipTransform([
1739
+ 1,
1740
+ 0,
1741
+ 0,
1742
+ 1,
1743
+ x,
1744
+ bottomYUp
1745
+ ], pageHeight)
1746
+ };
1747
+ const items = tile ? PageAssembler.tileItems(laid.background.resourceName, tile, box) : [{
1748
+ type: "image",
1749
+ x: pt(box.x),
1750
+ y: pt(box.y),
1751
+ width: pt(box.width),
1752
+ height: pt(box.height),
1753
+ imageResourceName: laid.background.resourceName
1754
+ }];
1755
+ for (const item of items) {
1756
+ if (item.type !== "image") continue;
1757
+ backdrop.push({
1758
+ ...item,
1759
+ clip,
1760
+ ...fig,
1761
+ ...picture
1762
+ });
1763
+ }
1764
+ }
1765
+ const shapes = laid.layout.shapes.map((sh) => ({
1693
1766
  type: "shape",
1694
1767
  shape: {
1695
1768
  paths: sh.paths,
@@ -1704,18 +1777,25 @@ function chartPageItems(laid, x, bottomYUp, pageHeight, structId) {
1704
1777
  bottomYUp
1705
1778
  ], pageHeight)
1706
1779
  },
1707
- ...fig
1708
- });
1709
- for (const t of laid.layout.texts) out.push({
1780
+ ...fig,
1781
+ ...picture,
1782
+ seq: sh.seq ?? 0
1783
+ }));
1784
+ const texts = laid.layout.texts.map((t) => ({
1710
1785
  type: "line",
1711
1786
  line: t.line,
1712
1787
  originX: pt(x + t.x),
1713
1788
  baselineY: pt(pageHeight - (bottomYUp + t.y)),
1714
1789
  ...t.rotationDeg ? { rotationDeg: t.rotationDeg } : {},
1715
- ...fig
1716
- });
1717
- return out;
1790
+ ...fig,
1791
+ ...picture,
1792
+ seq: t.seq ?? Number.MAX_SAFE_INTEGER
1793
+ }));
1794
+ const all = [...shapes, ...texts];
1795
+ if (ordered) all.sort((a, b) => a.seq - b.seq);
1796
+ return [...backdrop, ...all.map(({ seq: _seq, ...item }) => item)];
1718
1797
  }
1798
+ var nextPictureId = 1;
1719
1799
  function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
1720
1800
  if (sh.textLines.length === 0) return;
1721
1801
  if (sh.vertical) {
@@ -1767,10 +1847,11 @@ function emitShapeText(sh, x, bottomYUp, sink, pageHeight, figId) {
1767
1847
  }
1768
1848
  return;
1769
1849
  }
1850
+ const indentLeft = line.resolved.indentLeft + (line.firstLine ? line.resolved.indentFirstLine : 0);
1770
1851
  sink.push({
1771
1852
  type: "line",
1772
1853
  line,
1773
- originX: pt(x + sh.insetLeftPt + lineOffset),
1854
+ originX: pt(x + sh.insetLeftPt + indentLeft + lineOffset),
1774
1855
  baselineY: pt(pageHeight - (textY + lineBaselineOffset(line, line.resolved))),
1775
1856
  ...figId !== void 0 ? { structId: figId } : {}
1776
1857
  });
@@ -1806,6 +1887,7 @@ function emitCellFloat(cf, ctx, frame) {
1806
1887
  imageResourceName: cf.block.resourceName,
1807
1888
  ...cf.block.crop ? { crop: cf.block.crop } : {},
1808
1889
  ...cf.block.wash ? { wash: cf.block.wash } : {},
1890
+ ...cf.block.alpha !== void 0 ? { alpha: cf.block.alpha } : {},
1809
1891
  ...cf.block.rotationDeg ? { rotationDeg: cf.block.rotationDeg } : {},
1810
1892
  ...cf.block.flipH ? { flipH: true } : {},
1811
1893
  ...cf.block.flipV ? { flipV: true } : {}
@@ -1832,20 +1914,30 @@ function emitShapeItems(sh, x, bottomYUp, sink, pageHeight, figId) {
1832
1914
  sink.push({
1833
1915
  ...item,
1834
1916
  clip,
1917
+ ...fig,
1918
+ ...sh.fillAlpha !== void 0 ? { alpha: sh.fillAlpha } : {}
1919
+ });
1920
+ }
1921
+ else {
1922
+ const r = sh.fillImageRect;
1923
+ const boxX = x + (r ? r.left * sh.widthPt : 0);
1924
+ const boxW = sh.widthPt * (r ? 1 - r.left - r.right : 1);
1925
+ const boxH = sh.heightPt * (r ? 1 - r.top - r.bottom : 1);
1926
+ const boxTop = pageHeight - (bottomYUp + sh.heightPt) + (r ? r.top * sh.heightPt : 0);
1927
+ sink.push({
1928
+ type: "image",
1929
+ x: pt(boxX),
1930
+ y: pt(boxTop),
1931
+ width: pt(boxW),
1932
+ height: pt(boxH),
1933
+ imageResourceName: sh.fillImageResourceName,
1934
+ ...sh.fillImageCrop ? { crop: sh.fillImageCrop } : {},
1935
+ ...sh.fillAlpha !== void 0 ? { alpha: sh.fillAlpha } : {},
1936
+ ...sh.fillImageDuotone ? { duotone: sh.fillImageDuotone } : {},
1937
+ clip,
1835
1938
  ...fig
1836
1939
  });
1837
1940
  }
1838
- else sink.push({
1839
- type: "image",
1840
- x: pt(x),
1841
- y: pt(pageHeight - (bottomYUp + sh.heightPt)),
1842
- width: pt(sh.widthPt),
1843
- height: pt(sh.heightPt),
1844
- imageResourceName: sh.fillImageResourceName,
1845
- ...sh.fillImageCrop ? { crop: sh.fillImageCrop } : {},
1846
- clip,
1847
- ...fig
1848
- });
1849
1941
  }
1850
1942
  if (sh.markerPaths && sh.stroke) sink.push({
1851
1943
  type: "shape",
@@ -1982,6 +2074,7 @@ function drawBlocksSequentially(blocks, startX, startY, pageHeight, contentWidth
1982
2074
  imageResourceName: block.resourceName,
1983
2075
  ...block.crop ? { crop: block.crop } : {},
1984
2076
  ...block.wash ? { wash: block.wash } : {},
2077
+ ...block.alpha !== void 0 ? { alpha: block.alpha } : {},
1985
2078
  ...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
1986
2079
  ...block.flipH ? { flipH: true } : {},
1987
2080
  ...block.flipV ? { flipV: true } : {}
@@ -2114,7 +2207,7 @@ function collectFontResources(body, headersFooters, options, imageResources) {
2114
2207
  };
2115
2208
  used.set(fontKey, bucket);
2116
2209
  }
2117
- const shaped = shapeText(run.text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
2210
+ const shaped = shapeText(run.listMarker === true ? markerText(run.text, parsed) : run.text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, parsed.kerning, parsed.joiningForms);
2118
2211
  for (const g of shaped.gids) bucket.gids.add(g);
2119
2212
  if (run.text.includes(" ")) for (const stop of resolveParagraphProperties(para.properties, options.styles).tabs) {
2120
2213
  const ch = stop.leader === void 0 ? void 0 : TAB_LEADER_CHARS.get(stop.leader);
@@ -2597,28 +2690,55 @@ function tokenizePlansLtr(plans) {
2597
2690
  continue;
2598
2691
  }
2599
2692
  const highlight = (plan.run.commentRangeRefs?.length ?? 0) > 0;
2600
- for (const t of tokenizeText(plan.run.text)) tokens.push({
2601
- kind: "text",
2602
- text: t.text,
2603
- isSpace: t.isSpace,
2604
- ...t.tab ? { tab: true } : {},
2605
- ...plan.run.href !== void 0 ? { href: plan.run.href } : {},
2606
- ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
2607
- ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
2608
- ...plan.run.listMarker ? { listMarker: true } : {},
2609
- ...plan.run.columnBreak ? { columnBreak: true } : {},
2610
- ...highlight ? { highlight: true } : {},
2611
- resolvedRun: plan.resolvedRun,
2612
- font: plan.font,
2613
- fontSizePt: plan.fontSizePt,
2614
- ...plan.risePt !== void 0 ? { risePt: plan.risePt } : {},
2615
- widthPt: measureRunText(plan, t.text),
2616
- bidiLevel: 0
2617
- });
2693
+ for (const t of tokenizeText(plan.run.text)) {
2694
+ const text = plan.run.listMarker === true ? markerText(t.text, plan.font.parsed) : t.text;
2695
+ tokens.push({
2696
+ kind: "text",
2697
+ text,
2698
+ isSpace: t.isSpace,
2699
+ ...t.tab ? { tab: true } : {},
2700
+ ...plan.run.href !== void 0 ? { href: plan.run.href } : {},
2701
+ ...plan.run.footnoteRef !== void 0 ? { footnoteRef: plan.run.footnoteRef } : {},
2702
+ ...plan.run.anchor !== void 0 ? { anchor: plan.run.anchor } : {},
2703
+ ...plan.run.listMarker ? { listMarker: true } : {},
2704
+ ...plan.run.columnBreak ? { columnBreak: true } : {},
2705
+ ...highlight ? { highlight: true } : {},
2706
+ resolvedRun: plan.resolvedRun,
2707
+ font: plan.font,
2708
+ fontSizePt: plan.fontSizePt,
2709
+ ...plan.risePt !== void 0 ? { risePt: plan.risePt } : {},
2710
+ widthPt: measureRunText(plan, text),
2711
+ bidiLevel: 0
2712
+ });
2713
+ }
2618
2714
  }
2619
2715
  return tokens;
2620
2716
  }
2621
2717
  /**
2718
+ * A LIST MARKER the font has no glyph for, swapped for the dot every text font
2719
+ * carries.
2720
+ *
2721
+ * §21.1.2.4.5 — a bullet is stated in a face of its own, and the symbol faces
2722
+ * are not alphabets: Wingdings `l` is a filled circle, U+25CF once translated.
2723
+ * The font a document is RENDERED with may have no such character — Roboto does
2724
+ * not — and a missing glyph is a box: 45541_Header wore a row of them down
2725
+ * eleven slides. The marker is the one place where the shape matters more than
2726
+ * the code point, so it takes the nearest thing the font can draw.
2727
+ *
2728
+ * @param text The marker's text.
2729
+ * @param font The font it is set in.
2730
+ * @returns The text to draw.
2731
+ */
2732
+ function markerText(text, parsed) {
2733
+ let out = "";
2734
+ for (const ch of text) {
2735
+ const cp = ch.codePointAt(0) ?? 0;
2736
+ const missing = cp > 127 && parsed.glyphForCodepoint(cp) === 0;
2737
+ out += missing && parsed.glyphForCodepoint(8226) !== 0 ? "•" : ch;
2738
+ }
2739
+ return out;
2740
+ }
2741
+ /**
2622
2742
  * §17.3.2.35 — a run's text measured with the space it puts between its
2623
2743
  * characters. PDF adds the same amount per glyph through `Tc`, so the width the
2624
2744
  * line is broken at and the width the page draws agree.
@@ -2677,7 +2797,8 @@ function tokenizePlansBidi(plans, realLevels) {
2677
2797
  let curLevel = -1;
2678
2798
  const flush = (endExclusive) => {
2679
2799
  if (endExclusive <= bufStart) return;
2680
- const text = chars.slice(bufStart, endExclusive).join("");
2800
+ const raw = chars.slice(bufStart, endExclusive).join("");
2801
+ const text = plan.run.listMarker === true ? markerText(raw, plan.font.parsed) : raw;
2681
2802
  tokens.push({
2682
2803
  kind: "text",
2683
2804
  text,
@@ -4328,7 +4449,10 @@ var PageAssembler = class PageAssembler {
4328
4449
  ...this.pageBorderItems(),
4329
4450
  ...this.columnSeparatorItems(),
4330
4451
  ...header.commands,
4331
- ...PageAssembler.byZ(this.floatsBehind),
4452
+ ...PageAssembler.byZ(this.floatsBehind).map((item) => ({
4453
+ ...item,
4454
+ behind: true
4455
+ })),
4332
4456
  ...this.current,
4333
4457
  ...PageAssembler.byZ(this.floatsFront),
4334
4458
  ...this.renderNotesBand(),
@@ -4701,6 +4825,7 @@ function paginateSections(blocks, sectionCtxs, builder, defaultLang = "en-US", n
4701
4825
  imageResourceName: block.resourceName,
4702
4826
  ...block.crop ? { crop: block.crop } : {},
4703
4827
  ...block.wash ? { wash: block.wash } : {},
4828
+ ...block.alpha !== void 0 ? { alpha: block.alpha } : {},
4704
4829
  ...block.rotationDeg ? { rotationDeg: block.rotationDeg } : {},
4705
4830
  ...block.flipH ? { flipH: true } : {},
4706
4831
  ...block.flipV ? { flipV: true } : {},
@@ -25,4 +25,23 @@ export declare function shapeBbox(shape: VectorShape): Bbox | undefined;
25
25
  * @returns A reference to the `/Pattern` object, for a `/Pattern cs /Pn scn` fill.
26
26
  */
27
27
  export declare function buildGradientPattern(doc: PdfDocument, gradient: ShapeGradient, bbox: Bbox, ctm: readonly [number, number, number, number, number, number]): PdfRef;
28
+ /**
29
+ * §11.6.5.2 — the luminosity soft mask a gradient with per-stop TRANSPARENCY is
30
+ * painted through.
31
+ *
32
+ * A PDF shading has one colour per point and no alpha, so a gradient that fades
33
+ * out is drawn as the colour shading masked by a second shading of the same
34
+ * geometry whose GREY is the transparency: white where the stop is opaque,
35
+ * black where it is clear. 45541_Footer's master lays a white band down the
36
+ * left of every slide that starts half transparent, and painted flat it covered
37
+ * the artwork behind it.
38
+ *
39
+ * @param doc The document to add the objects to.
40
+ * @param gradient The gradient fill.
41
+ * @param bbox The shape's local-space bounding box.
42
+ * @param ctm The shape's CTM, as the form's `/Matrix`.
43
+ * @returns The mask form XObject, or `undefined` when every stop is opaque or
44
+ * the sweep has no shading type that can carry a mask.
45
+ */
46
+ export declare function buildGradientAlphaMask(doc: PdfDocument, gradient: ShapeGradient, bbox: Bbox, ctm: readonly [number, number, number, number, number, number]): PdfRef | undefined;
28
47
  export {};
@@ -49,7 +49,7 @@ function buildGradientPattern(doc, gradient, bbox, ctm) {
49
49
  Matrix: [...ctm]
50
50
  }));
51
51
  }
52
- function axialShading(angleDeg, b, fnRef) {
52
+ function axialShading(angleDeg, b, fnRef, cs = "DeviceRGB") {
53
53
  const rad = angleDeg * Math.PI / 180;
54
54
  const dx = Math.cos(rad);
55
55
  const dy = -Math.sin(rad);
@@ -70,7 +70,7 @@ function axialShading(angleDeg, b, fnRef) {
70
70
  }
71
71
  return dict({
72
72
  ShadingType: 2,
73
- ColorSpace: name("DeviceRGB"),
73
+ ColorSpace: name(cs),
74
74
  Coords: [
75
75
  cx + (tmin - cproj) * dx,
76
76
  cy + (tmin - cproj) * dy,
@@ -81,13 +81,13 @@ function axialShading(angleDeg, b, fnRef) {
81
81
  Extend: [true, true]
82
82
  });
83
83
  }
84
- function radialShading(b, fnRef, center) {
84
+ function radialShading(b, fnRef, center, cs = "DeviceRGB") {
85
85
  const cx = center ? b.minX + center.x * (b.maxX - b.minX) : (b.minX + b.maxX) / 2;
86
86
  const cy = center ? b.maxY - center.y * (b.maxY - b.minY) : (b.minY + b.maxY) / 2;
87
87
  const r = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) / 2;
88
88
  return dict({
89
89
  ShadingType: 3,
90
- ColorSpace: name("DeviceRGB"),
90
+ ColorSpace: name(cs),
91
91
  Coords: [
92
92
  cx,
93
93
  cy,
@@ -171,6 +171,77 @@ function ps(n) {
171
171
  const s = n.toFixed(6).replace(/0+$/u, "").replace(/\.$/u, "");
172
172
  return s === "" || s === "-0" ? "0" : s;
173
173
  }
174
+ /**
175
+ * §11.6.5.2 — the luminosity soft mask a gradient with per-stop TRANSPARENCY is
176
+ * painted through.
177
+ *
178
+ * A PDF shading has one colour per point and no alpha, so a gradient that fades
179
+ * out is drawn as the colour shading masked by a second shading of the same
180
+ * geometry whose GREY is the transparency: white where the stop is opaque,
181
+ * black where it is clear. 45541_Footer's master lays a white band down the
182
+ * left of every slide that starts half transparent, and painted flat it covered
183
+ * the artwork behind it.
184
+ *
185
+ * @param doc The document to add the objects to.
186
+ * @param gradient The gradient fill.
187
+ * @param bbox The shape's local-space bounding box.
188
+ * @param ctm The shape's CTM, as the form's `/Matrix`.
189
+ * @returns The mask form XObject, or `undefined` when every stop is opaque or
190
+ * the sweep has no shading type that can carry a mask.
191
+ */
192
+ function buildGradientAlphaMask(doc, gradient, bbox, ctm) {
193
+ if (!gradient.stops.some((s) => (s.alpha ?? 1) < 1)) return void 0;
194
+ if (gradient.kind === "radial" && gradient.sweep === "rect") return void 0;
195
+ const fn = doc.add(buildAlphaRamp(gradient.stops));
196
+ const shading = gradient.kind === "radial" ? radialShading(bbox, fn, gradient.center, "DeviceGray") : axialShading(gradient.angle ?? 0, bbox, fn, "DeviceGray");
197
+ return doc.add(stream({
198
+ Type: name("XObject"),
199
+ Subtype: name("Form"),
200
+ BBox: [
201
+ bbox.minX,
202
+ bbox.minY,
203
+ bbox.maxX,
204
+ bbox.maxY
205
+ ],
206
+ Matrix: [...ctm],
207
+ Group: dict({
208
+ S: name("Transparency"),
209
+ CS: name("DeviceGray")
210
+ }),
211
+ Resources: dict({ Shading: dict({ Sm: doc.add(shading) }) })
212
+ }, new TextEncoder().encode("q /Sm sh Q")));
213
+ }
214
+ function buildAlphaRamp(stopsIn) {
215
+ const stops = normalizeStops(stopsIn);
216
+ if (stops.length <= 2) return dict({
217
+ FunctionType: 2,
218
+ Domain: [0, 1],
219
+ C0: [stops[0].alpha],
220
+ C1: [stops[stops.length - 1].alpha],
221
+ N: 1
222
+ });
223
+ const functions = [];
224
+ const bounds = [];
225
+ const encode = [];
226
+ for (let i = 0; i < stops.length - 1; i++) {
227
+ functions.push(dict({
228
+ FunctionType: 2,
229
+ Domain: [0, 1],
230
+ C0: [stops[i].alpha],
231
+ C1: [stops[i + 1].alpha],
232
+ N: 1
233
+ }));
234
+ if (i > 0) bounds.push(stops[i].offset);
235
+ encode.push(0, 1);
236
+ }
237
+ return dict({
238
+ FunctionType: 3,
239
+ Domain: [0, 1],
240
+ Functions: functions,
241
+ Bounds: bounds,
242
+ Encode: encode
243
+ });
244
+ }
174
245
  function buildRamp(stopsIn) {
175
246
  const stops = normalizeStops(stopsIn);
176
247
  if (stops.length <= 2) return dict({
@@ -205,19 +276,21 @@ function buildRamp(stopsIn) {
205
276
  function normalizeStops(stops) {
206
277
  const out = stops.map((s) => ({
207
278
  offset: Math.max(0, Math.min(1, s.offset)),
208
- colorHex: s.colorHex
279
+ colorHex: s.colorHex,
280
+ alpha: Math.max(0, Math.min(1, s.alpha ?? 1))
209
281
  })).sort((a, b) => a.offset - b.offset);
210
282
  if (out.length === 0) return [{
211
283
  offset: 0,
212
- colorHex: "000000"
284
+ colorHex: "000000",
285
+ alpha: 1
213
286
  }];
214
287
  out[0] = {
215
- offset: 0,
216
- colorHex: out[0].colorHex
288
+ ...out[0],
289
+ offset: 0
217
290
  };
218
291
  out[out.length - 1] = {
219
- offset: 1,
220
- colorHex: out[out.length - 1].colorHex
292
+ ...out[out.length - 1],
293
+ offset: 1
221
294
  };
222
295
  return out;
223
296
  }
@@ -235,4 +308,4 @@ function rgb01(hex) {
235
308
  ];
236
309
  }
237
310
  //#endregion
238
- export { buildGradientPattern, shapeBbox };
311
+ export { buildGradientAlphaMask, buildGradientPattern, shapeBbox };