reamkit 1.21.0 → 1.23.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 (90) 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/converter/ream.d.ts +16 -0
  4. package/dist/esm/core/converter/ream.js +59 -9
  5. package/dist/esm/core/document-model/types.d.ts +78 -0
  6. package/dist/esm/core/drawingml/chart-geometry.js +11 -5
  7. package/dist/esm/core/drawingml/chart-parser.d.ts +2 -1
  8. package/dist/esm/core/drawingml/chart-parser.js +23 -1
  9. package/dist/esm/core/drawingml/colors.d.ts +30 -4
  10. package/dist/esm/core/drawingml/colors.js +45 -6
  11. package/dist/esm/core/drawingml/preset-geometry.js +236 -0
  12. package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
  13. package/dist/esm/core/drawingml/theme-parser.js +64 -1
  14. package/dist/esm/core/fonts/families.d.ts +10 -0
  15. package/dist/esm/core/fonts/families.js +44 -0
  16. package/dist/esm/core/fonts/index.d.ts +3 -2
  17. package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
  18. package/dist/esm/core/fonts/remote-fonts.js +114 -8
  19. package/dist/esm/core/fonts/scripts.d.ts +26 -0
  20. package/dist/esm/core/fonts/scripts.js +177 -0
  21. package/dist/esm/core/metafile/blit.d.ts +33 -0
  22. package/dist/esm/core/metafile/blit.js +67 -0
  23. package/dist/esm/core/metafile/dib.d.ts +61 -0
  24. package/dist/esm/core/metafile/dib.js +205 -0
  25. package/dist/esm/core/metafile/emf.js +139 -24
  26. package/dist/esm/core/metafile/picture.d.ts +12 -1
  27. package/dist/esm/core/metafile/symbol-fonts.d.ts +42 -0
  28. package/dist/esm/core/metafile/symbol-fonts.js +154 -0
  29. package/dist/esm/core/metafile/wmf.js +183 -10
  30. package/dist/esm/core/ole/escher-blip.d.ts +12 -0
  31. package/dist/esm/core/ole/escher-blip.js +88 -0
  32. package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
  33. package/dist/esm/core/vector.d.ts +5 -0
  34. package/dist/esm/excel/print-model.js +3 -1
  35. package/dist/esm/excel/sheet-shape-parser.js +1 -12
  36. package/dist/esm/excel/styles-parser.js +1 -1
  37. package/dist/esm/excel/xls/biff-reader.js +53 -18
  38. package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
  39. package/dist/esm/excel/xls/biff-styles.js +9 -7
  40. package/dist/esm/excel/xls/escher.js +90 -24
  41. package/dist/esm/excel/xlsx-reader.d.ts +1 -1
  42. package/dist/esm/excel/xlsx-reader.js +3 -3
  43. package/dist/esm/layout/page-doc.d.ts +77 -0
  44. package/dist/esm/layout/page-doc.js +34 -18
  45. package/dist/esm/layout/styled-layout.d.ts +2 -2
  46. package/dist/esm/layout/styled-layout.js +467 -76
  47. package/dist/esm/pdf/shading.d.ts +19 -0
  48. package/dist/esm/pdf/shading.js +84 -11
  49. package/dist/esm/pdf/styled-page-emitter.js +647 -444
  50. package/dist/esm/pdf-reader/image-decode.js +1 -1
  51. package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
  52. package/dist/esm/pptx/embedded-fonts.js +104 -0
  53. package/dist/esm/pptx/ole-preview.d.ts +10 -0
  54. package/dist/esm/pptx/ole-preview.js +52 -0
  55. package/dist/esm/pptx/placeholder-cascade.d.ts +136 -5
  56. package/dist/esm/pptx/placeholder-cascade.js +260 -27
  57. package/dist/esm/pptx/ppt/ppt-reader.js +120 -10
  58. package/dist/esm/pptx/ppt/ppt-text.d.ts +66 -1
  59. package/dist/esm/pptx/ppt/ppt-text.js +576 -137
  60. package/dist/esm/pptx/pptx-reader.d.ts +8 -0
  61. package/dist/esm/pptx/pptx-reader.js +271 -41
  62. package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
  63. package/dist/esm/pptx/preset-table-styles.js +27 -0
  64. package/dist/esm/pptx/slide-parser.d.ts +78 -10
  65. package/dist/esm/pptx/slide-parser.js +396 -95
  66. package/dist/esm/pptx/sp-helpers.d.ts +2 -1
  67. package/dist/esm/pptx/sp-helpers.js +22 -6
  68. package/dist/esm/pptx/table-style.d.ts +64 -0
  69. package/dist/esm/pptx/table-style.js +203 -0
  70. package/dist/esm/svg/svg-writer.js +53 -5
  71. package/dist/esm/word/document-parser.d.ts +7 -1
  72. package/dist/esm/word/document-parser.js +6 -6
  73. package/dist/esm/word/docx-reader.js +8 -5
  74. package/dist/esm/word/docx-to-pdf.d.ts +8 -7
  75. package/dist/esm/word/docx-to-pdf.js +25 -7
  76. package/dist/esm/word/drawing-parser.d.ts +34 -0
  77. package/dist/esm/word/drawing-parser.js +173 -15
  78. package/dist/esm/word/numbering-parser.d.ts +2 -1
  79. package/dist/esm/word/numbering-parser.js +6 -6
  80. package/dist/esm/word/paragraph-properties.d.ts +2 -1
  81. package/dist/esm/word/paragraph-properties.js +2 -2
  82. package/dist/esm/word/run-properties.d.ts +5 -2
  83. package/dist/esm/word/run-properties.js +13 -8
  84. package/dist/esm/word/styles-parser.d.ts +2 -1
  85. package/dist/esm/word/styles-parser.js +12 -12
  86. package/dist/esm/word/table-parser.js +3 -3
  87. package/dist/esm/word/theme-fonts.d.ts +10 -0
  88. package/dist/esm/word/theme-fonts.js +28 -0
  89. package/package.json +1 -1
  90. /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
@@ -6,7 +6,7 @@ import { reverseByCodePoint } from "../core/bidi/index.js";
6
6
  import { GLUE_SHRINK_RATIO } from "../layout/styled-layout.js";
7
7
  import { paintPlan } from "../layout/page-doc.js";
8
8
  import { emitClipPath, emitVectorShape, shadowBlurLayers } from "./vector-graphics.js";
9
- import { buildGradientPattern, shapeBbox } from "./shading.js";
9
+ import { buildGradientAlphaMask, buildGradientPattern, shapeBbox } from "./shading.js";
10
10
  import { sanitizeHref } from "../core/links.js";
11
11
  import { embedAssociatedFile } from "./embedded-file.js";
12
12
  import { buildSrgbIccProfile } from "./icc-profile.js";
@@ -99,11 +99,20 @@ function assembleStyledPdf(laid, options, doc) {
99
99
  let annotParentCount = 0;
100
100
  const fontResourceDict = buildFontResourceDict(laid.fontResources, embeddedFonts);
101
101
  const xobjectResourceDict = buildXObjectResourceDict(laid.imageResources, embeddedImages);
102
+ const imageRefByName = /* @__PURE__ */ new Map();
103
+ for (const [resourceId, res] of laid.imageResources) {
104
+ const r = embeddedImages.get(resourceId);
105
+ if (r) imageRefByName.set(res.resourceName, r);
106
+ }
102
107
  const gradientNames = /* @__PURE__ */ new Map();
108
+ const gradientMaskNames = /* @__PURE__ */ new Map();
103
109
  const patternEntries = {};
110
+ const extGStateEntries = {};
104
111
  if (!pdfaProfile) for (const page of renderedPages) {
105
112
  const ph = page.height;
106
- for (const item of paintPlan(page.commands).shapes) {
113
+ const plan = paintPlan(page.commands);
114
+ const shapesNeedingPatterns = [...plan.shapes, ...[...plan.behind, ...plan.pictures.flat()].filter((i) => i.type === "shape")];
115
+ for (const item of shapesNeedingPatterns) {
107
116
  const gradient = item.shape.fillGradient;
108
117
  if (!gradient) continue;
109
118
  const bbox = shapeBbox(item.shape);
@@ -120,10 +129,65 @@ function assembleStyledPdf(laid, options, doc) {
120
129
  const nm = `Sh${gradientNames.size}`;
121
130
  patternEntries[nm] = ref(buildGradientPattern(doc, gradient, bbox, ctm).id);
122
131
  gradientNames.set(item.shape, nm);
132
+ const mask = buildGradientAlphaMask(doc, gradient, bbox, ctm);
133
+ if (mask) {
134
+ const gs = `GSm${gradientMaskNames.size}`;
135
+ gradientMaskNames.set(item.shape, gs);
136
+ extGStateEntries[gs] = dict({ SMask: dict({
137
+ S: name("Luminosity"),
138
+ G: ref(mask.id),
139
+ BC: [0]
140
+ }) });
141
+ }
123
142
  }
124
143
  }
144
+ const duotoneStates = /* @__PURE__ */ new Map();
145
+ const wantDuotone = (item, pageHeight) => {
146
+ if (!item.duotone || item.imageResourceName === "" || pdfaProfile) return void 0;
147
+ const key = [
148
+ item.imageResourceName,
149
+ Math.round(item.x * 100),
150
+ Math.round(item.y * 100),
151
+ Math.round(item.width * 100),
152
+ Math.round(item.height * 100)
153
+ ].join("|");
154
+ const known = duotoneStates.get(key);
155
+ if (known !== void 0) return known;
156
+ const imageRef = imageRefByName.get(item.imageResourceName);
157
+ if (!imageRef) return void 0;
158
+ const form = doc.add(stream({
159
+ Type: name("XObject"),
160
+ Subtype: name("Form"),
161
+ BBox: [
162
+ 0,
163
+ 0,
164
+ item.width,
165
+ item.height
166
+ ],
167
+ Matrix: [
168
+ 1,
169
+ 0,
170
+ 0,
171
+ 1,
172
+ item.x,
173
+ pageHeight - item.y - item.height
174
+ ],
175
+ Group: dict({
176
+ S: name("Transparency"),
177
+ CS: name("DeviceGray")
178
+ }),
179
+ Resources: dict({ XObject: dict({ [item.imageResourceName]: ref(imageRef.id) }) })
180
+ }, encoder.encode(`q ${formatNumber(item.width)} 0 0 ${formatNumber(item.height)} 0 0 cm /${item.imageResourceName} Do Q`)));
181
+ const nm = `GSd${duotoneStates.size}`;
182
+ duotoneStates.set(key, nm);
183
+ extGStateEntries[nm] = dict({ SMask: dict({
184
+ S: name("Luminosity"),
185
+ G: ref(form.id),
186
+ BC: [0]
187
+ }) });
188
+ return nm;
189
+ };
125
190
  const alphaStateNames = /* @__PURE__ */ new Map();
126
- const extGStateEntries = {};
127
191
  const wantAlpha = (alpha) => {
128
192
  if (alpha === void 0 || alpha >= 1) return;
129
193
  const key = Math.round(alpha * 1e3) / 1e3;
@@ -142,7 +206,23 @@ function assembleStyledPdf(laid, options, doc) {
142
206
  if (s) wantAlpha(shadowBlurLayers(s).alpha);
143
207
  wantAlpha(item.shape.fillAlpha);
144
208
  }
145
- for (const img of plan.images) wantAlpha(washVeil(img.wash)?.alpha);
209
+ for (const img of plan.images) {
210
+ wantAlpha(washVeil(img.wash)?.alpha);
211
+ wantAlpha(img.alpha);
212
+ wantDuotone(img, page.height);
213
+ }
214
+ for (const item of [...plan.behind, ...plan.pictures.flat()]) {
215
+ if (item.type === "image") {
216
+ wantAlpha(washVeil(item.wash)?.alpha);
217
+ wantAlpha(item.alpha);
218
+ wantDuotone(item, page.height);
219
+ }
220
+ if (item.type === "shape") {
221
+ const s = item.shape.shadow;
222
+ if (s) wantAlpha(shadowBlurLayers(s).alpha);
223
+ wantAlpha(item.shape.fillAlpha);
224
+ }
225
+ }
146
226
  for (const line of plan.lines) for (const tok of line.line.tokens) {
147
227
  if (tok.kind === "image" && tok.shadow) wantAlpha(shadowBlurLayers(tok.shadow).alpha);
148
228
  if (tok.kind === "image") wantAlpha(washVeil(tok.wash)?.alpha);
@@ -179,7 +259,7 @@ function assembleStyledPdf(laid, options, doc) {
179
259
  tagFor: (structId) => builder.node(structId).type
180
260
  };
181
261
  }
182
- const { content: contentBytes, links } = emitPageContent(page, pageTagging, gradientNames, alphaStateNames);
262
+ const { content: contentBytes, links } = emitPageContent(page, pageTagging, gradientNames, alphaStateNames, wantDuotone, gradientMaskNames);
183
263
  const contentsRef = doc.add(stream({}, contentBytes));
184
264
  const pageEntries = {
185
265
  Type: name("Page"),
@@ -449,6 +529,32 @@ function buildFontResourceDict(resources, embedded) {
449
529
  }
450
530
  return dict(entries);
451
531
  }
532
+ /**
533
+ * The operators that paint one picture: normally the XObject itself, but for a
534
+ * §20.1.8.23 duotone the two colours it is recoloured between, composited
535
+ * through the picture's own luminosity (`state` is that soft-mask ExtGState).
536
+ *
537
+ * @param item The image to paint.
538
+ * @param yUp Its bottom edge in the page's y-up frame.
539
+ * @param state The duotone's soft-mask state name, when it has one.
540
+ * @returns The operators, without the surrounding `q`/`Q`.
541
+ */
542
+ function paintImageBody(item, yUp, state) {
543
+ const place = placeImage(item.x, yUp, item.width, item.height, item.crop, item.rotationDeg, item.flipH, item.flipV);
544
+ if (state === void 0 || !item.duotone) return [...place, `/${item.imageResourceName} Do`];
545
+ const rect = `${formatNumber(item.x)} ${formatNumber(yUp)} ${formatNumber(item.width)} ${formatNumber(item.height)} re`;
546
+ const [sr, sg, sb] = hexToRgb01(item.duotone.shadowHex);
547
+ const [hr, hg, hb] = hexToRgb01(item.duotone.highlightHex);
548
+ return [
549
+ `${formatNumber(sr)} ${formatNumber(sg)} ${formatNumber(sb)} rg`,
550
+ rect,
551
+ "f",
552
+ `/${state} gs`,
553
+ `${formatNumber(hr)} ${formatNumber(hg)} ${formatNumber(hb)} rg`,
554
+ rect,
555
+ "f"
556
+ ];
557
+ }
452
558
  function buildXObjectResourceDict(resources, embedded) {
453
559
  if (embedded.size === 0) return void 0;
454
560
  const entries = {};
@@ -515,12 +621,13 @@ var BORDER_DASHES = new Map([
515
621
  2.5
516
622
  ]]
517
623
  ]);
624
+ var FAUX_ITALIC_SHEAR = .2126;
518
625
  /** The PDF dash array for a border style; an empty string means a solid rule. */
519
626
  function dashPatternFor(style) {
520
627
  const pattern = style !== void 0 ? BORDER_DASHES.get(style) : void 0;
521
628
  return pattern ? `[${pattern.map(formatNumber).join(" ")}]` : "";
522
629
  }
523
- function emitPageContent(page, tagging, gradientNames, alphaStateNames) {
630
+ function emitPageContent(page, tagging, gradientNames, alphaStateNames, duotoneStateFor, gradientMaskNames) {
524
631
  const commands = page.commands;
525
632
  const links = [];
526
633
  if (commands.length === 0) return {
@@ -530,17 +637,535 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames) {
530
637
  const H = page.height;
531
638
  const out = [];
532
639
  const plan = paintPlan(commands);
640
+ const lines = plan.lines;
641
+ let inBT = false;
642
+ let lastFont = "";
643
+ let lastTc = 0;
644
+ let lastSize = -1;
645
+ let lastColor = "";
646
+ let lastFauxWidth = 0;
647
+ let lastTz = 100;
648
+ const switchFontIfNeeded = (tok) => {
649
+ const fontKey = tok.font.resourceName;
650
+ if (fontKey !== lastFont || tok.fontSizePt !== lastSize) {
651
+ out.push(`/${fontKey} ${formatNumber(tok.fontSizePt)} Tf`);
652
+ lastFont = fontKey;
653
+ lastSize = tok.fontSizePt;
654
+ }
655
+ const fauxWidth = tok.synthetic?.bold === true ? tok.fontSizePt * .03 : 0;
656
+ if (fauxWidth !== lastFauxWidth) {
657
+ out.push(fauxWidth > 0 ? `2 Tr ${formatNumber(fauxWidth)} w` : "0 Tr");
658
+ lastFauxWidth = fauxWidth;
659
+ }
660
+ const tz = Math.round((tok.synthetic?.widthScale ?? 1) * 100);
661
+ if (tz !== lastTz) {
662
+ out.push(`${formatNumber(tz)} Tz`);
663
+ lastTz = tz;
664
+ }
665
+ const tc = tok.resolvedRun.letterSpacingPt ?? 0;
666
+ if (tc !== lastTc) {
667
+ out.push(`${formatNumber(tc)} Tc`);
668
+ lastTc = tc;
669
+ }
670
+ if (tok.resolvedRun.colorHex !== lastColor) {
671
+ const [r, g, b] = hexToRgb01(tok.resolvedRun.colorHex);
672
+ out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
673
+ lastColor = tok.resolvedRun.colorHex;
674
+ }
675
+ };
676
+ const emitImageToken = (tok, x, baselineY) => {
677
+ const meta = tok.metafile;
678
+ if (meta) {
679
+ if (inBT) {
680
+ out.push("ET");
681
+ inBT = false;
682
+ }
683
+ const b = tok.drawBox;
684
+ const ox = x + (b?.dxPt ?? 0);
685
+ const oy = baselineY + (b?.dyPt ?? 0);
686
+ for (const img of meta.images ?? []) {
687
+ if (img.resourceName === "") continue;
688
+ out.push("q");
689
+ out.push(...placeImage(ox + img.x, oy + img.y, img.width, img.height, void 0, img.rotationDeg));
690
+ out.push(`/${img.resourceName} Do`, "Q");
691
+ }
692
+ for (const sh of meta.shapes) for (const op of emitVectorShape({
693
+ ...sh,
694
+ transform: [
695
+ 1,
696
+ 0,
697
+ 0,
698
+ 1,
699
+ ox,
700
+ oy
701
+ ]
702
+ })) out.push(op);
703
+ for (const t of meta.texts) {
704
+ const first = t.line.tokens.find((k) => k.kind === "text");
705
+ if (!first) continue;
706
+ const [r, g, bl] = hexToRgb01(first.resolvedRun.colorHex);
707
+ out.push("BT");
708
+ out.push(`/${first.font.resourceName} ${formatNumber(first.fontSizePt)} Tf`);
709
+ out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(bl)} rg`);
710
+ out.push(`1 0 0 1 ${formatNumber(ox + t.x)} ${formatNumber(oy + t.y)} Tm`);
711
+ out.push(first.font.measure.showText(first.text));
712
+ out.push("ET");
713
+ }
714
+ lastFont = "";
715
+ lastFauxWidth = 0;
716
+ lastTz = 100;
717
+ lastSize = -1;
718
+ lastColor = "";
719
+ return;
720
+ }
721
+ if (!tok.imageResourceName) return;
722
+ if (inBT) {
723
+ out.push("ET");
724
+ inBT = false;
725
+ }
726
+ const shdw = tok.shadow;
727
+ if (shdw) {
728
+ const bx = tok.drawBox;
729
+ const [shr, shg, shb] = hexToRgb01(shdw.colorHex);
730
+ const layers = shadowBlurLayers(shdw);
731
+ const state = layers.alpha < 1 ? alphaStateNames?.get(Math.round(layers.alpha * 1e3) / 1e3) : void 0;
732
+ const sx = x + (bx?.dxPt ?? 0) + shdw.dxPt;
733
+ const sy = baselineY + (bx?.dyPt ?? 0) - shdw.dyPt;
734
+ const sw = bx?.widthPt ?? tok.widthPt;
735
+ const sh = bx?.heightPt ?? tok.heightPt;
736
+ out.push("q");
737
+ if (state) out.push(`/${state} gs`);
738
+ out.push(`${formatNumber(shr)} ${formatNumber(shg)} ${formatNumber(shb)} rg`);
739
+ for (let i = 0; i < layers.count; i++) {
740
+ const grow = layers.count > 1 ? -shdw.blurPt / 2 + (i + .5) * shdw.blurPt / layers.count : 0;
741
+ out.push(`${formatNumber(sx - grow)} ${formatNumber(sy - grow)} ${formatNumber(Math.max(sw + 2 * grow, 0))} ${formatNumber(Math.max(sh + 2 * grow, 0))} re`);
742
+ out.push("f");
743
+ }
744
+ out.push("Q");
745
+ }
746
+ out.push("q");
747
+ const b = tok.drawBox;
748
+ out.push(...b ? placeImage(x + b.dxPt, baselineY + b.dyPt, b.widthPt, b.heightPt, tok.crop, tok.rotationDeg, tok.flipH, tok.flipV) : placeImage(x, baselineY, tok.widthPt, tok.heightPt, tok.crop, tok.rotationDeg, tok.flipH, tok.flipV));
749
+ out.push(`/${tok.imageResourceName} Do`);
750
+ out.push(...washItems(tok.wash, alphaStateNames));
751
+ out.push("Q");
752
+ const frame = tok.outline;
753
+ if (frame) {
754
+ const [fr, fg, fb] = hexToRgb01(frame.colorHex);
755
+ out.push("q");
756
+ out.push(`${formatNumber(frame.widthPt)} w`);
757
+ out.push(`${formatNumber(fr)} ${formatNumber(fg)} ${formatNumber(fb)} RG`);
758
+ out.push(`${formatNumber(x + (b?.dxPt ?? 0))} ${formatNumber(baselineY + (b?.dyPt ?? 0))} ${formatNumber(b?.widthPt ?? tok.widthPt)} ${formatNumber(b?.heightPt ?? tok.heightPt)} re`);
759
+ out.push("S");
760
+ out.push("Q");
761
+ }
762
+ lastFont = "";
763
+ lastFauxWidth = 0;
764
+ lastTz = 100;
765
+ lastSize = -1;
766
+ lastColor = "";
767
+ lastTc = 0;
768
+ };
769
+ const emitMathToken = (tok, originX, baselineY) => {
770
+ for (const it of tok.items) if (it.kind === "glyph") {
771
+ if (!inBT) {
772
+ out.push("BT");
773
+ inBT = true;
774
+ }
775
+ if (it.font.resourceName !== lastFont || it.sizePt !== lastSize) {
776
+ out.push(`/${it.font.resourceName} ${formatNumber(it.sizePt)} Tf`);
777
+ lastFont = it.font.resourceName;
778
+ lastSize = it.sizePt;
779
+ }
780
+ if (lastColor !== "000000") {
781
+ out.push("0 0 0 rg");
782
+ lastColor = "000000";
783
+ }
784
+ out.push(`1 0 0 1 ${formatNumber(originX + it.x)} ${formatNumber(baselineY + it.y)} Tm`);
785
+ out.push(it.font.measure.showText(it.text));
786
+ } else if (it.kind === "rule") {
787
+ if (inBT) {
788
+ out.push("ET");
789
+ inBT = false;
790
+ }
791
+ out.push("q");
792
+ out.push("0 0 0 rg");
793
+ out.push(`${formatNumber(originX + it.x)} ${formatNumber(baselineY + it.y)} ${formatNumber(it.w)} ${formatNumber(it.h)} re`);
794
+ out.push("f");
795
+ out.push("Q");
796
+ lastFont = "";
797
+ lastFauxWidth = 0;
798
+ lastTz = 100;
799
+ lastSize = -1;
800
+ lastColor = "";
801
+ } else {
802
+ if (inBT) {
803
+ out.push("ET");
804
+ inBT = false;
805
+ }
806
+ const shape = {
807
+ paths: [{ segments: it.segments }],
808
+ ...it.fill ? { fillColorHex: "000000" } : {},
809
+ ...it.strokeWidthPt !== void 0 ? { stroke: {
810
+ colorHex: "000000",
811
+ widthPt: it.strokeWidthPt
812
+ } } : {},
813
+ transform: [
814
+ 1,
815
+ 0,
816
+ 0,
817
+ 1,
818
+ originX,
819
+ baselineY
820
+ ]
821
+ };
822
+ for (const op of emitVectorShape(shape)) out.push(op);
823
+ lastFont = "";
824
+ lastFauxWidth = 0;
825
+ lastTz = 100;
826
+ lastSize = -1;
827
+ lastColor = "";
828
+ }
829
+ };
830
+ const emitClipped = (cmd, body) => {
831
+ const clip = cmd.clip;
832
+ if (!clip) {
833
+ body(cmd);
834
+ return;
835
+ }
836
+ if (inBT) {
837
+ out.push("ET");
838
+ inBT = false;
839
+ }
840
+ out.push("q");
841
+ out.push(`${formatNumber(clip.x)} ${formatNumber(H - clip.y - clip.height)} ${formatNumber(clip.width)} ${formatNumber(clip.height)} re W n`);
842
+ lastFont = "";
843
+ lastFauxWidth = 0;
844
+ lastTz = 100;
845
+ lastSize = -1;
846
+ lastColor = "";
847
+ lastTc = 0;
848
+ out.push("BT");
849
+ inBT = true;
850
+ body(cmd);
851
+ if (inBT) {
852
+ out.push("ET");
853
+ inBT = false;
854
+ }
855
+ out.push("Q");
856
+ lastFont = "";
857
+ lastFauxWidth = 0;
858
+ lastTz = 100;
859
+ lastSize = -1;
860
+ lastColor = "";
861
+ lastTc = 0;
862
+ };
863
+ const emitOneLine = (cmd) => {
864
+ const line = cmd.line;
865
+ const originX = cmd.originX;
866
+ const baselineY = H - cmd.baselineY;
867
+ const rotationDeg = cmd.rotationDeg ?? 0;
868
+ const lineFs = line.maxFontSizePt || 12;
869
+ const linkAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
870
+ const linkDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
871
+ let linkHref;
872
+ let linkAnchor;
873
+ let linkText = "";
874
+ let linkX0 = 0;
875
+ let linkX1 = 0;
876
+ const flushLink = () => {
877
+ if (linkHref === void 0 && linkAnchor === void 0) return;
878
+ const safe = linkHref !== void 0 ? sanitizeHref(linkHref) : void 0;
879
+ const target = safe !== void 0 ? { href: safe } : linkAnchor !== void 0 ? { anchor: linkAnchor } : void 0;
880
+ if (target) links.push({
881
+ ...target,
882
+ text: linkText.trim(),
883
+ rect: [
884
+ linkX0,
885
+ baselineY - linkDescent,
886
+ linkX1,
887
+ baselineY + linkAscent
888
+ ],
889
+ ...cmd.structId !== void 0 ? { structId: cmd.structId } : {}
890
+ });
891
+ linkHref = void 0;
892
+ linkAnchor = void 0;
893
+ linkText = "";
894
+ };
895
+ const trackLink = (href, anchor, text, x0, x1) => {
896
+ if (href !== linkHref || anchor !== linkAnchor) {
897
+ flushLink();
898
+ if (href !== void 0 || anchor !== void 0) {
899
+ linkHref = href;
900
+ linkAnchor = anchor;
901
+ linkX0 = x0;
902
+ }
903
+ }
904
+ if (href !== void 0 || anchor !== void 0) {
905
+ linkX1 = x1;
906
+ linkText += text;
907
+ }
908
+ };
909
+ const extraPerSpace = computeJustifyExtra(line);
910
+ const hasImageToken = line.tokens.some((t) => t.kind === "image");
911
+ const hasMathToken = line.tokens.some((t) => t.kind === "math");
912
+ const hasRtl = line.tokens.some((t) => t.bidiLevel % 2 === 1);
913
+ const hasTab = line.tokens.some((t) => t.kind === "text" && t.tab === true);
914
+ /** A text token carrying underline or strikethrough. */
915
+ const isDecorated = (tok) => tok.kind === "text" && (tok.resolvedRun.underline !== "none" || tok.resolvedRun.strike);
916
+ const showToken = (tok) => {
917
+ const text = tok.bidiLevel % 2 === 1 ? reverseByCodePoint(tok.text) : tok.text;
918
+ return tok.font.measure.showText(text);
919
+ };
920
+ if (line.tokens.some((t) => t.kind === "text" && t.resolvedRun.shadingColorHex)) {
921
+ if (inBT) {
922
+ out.push("ET");
923
+ inBT = false;
924
+ }
925
+ const shAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
926
+ const shDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
927
+ out.push("q");
928
+ let sx = originX;
929
+ let painted = "";
930
+ for (const tok of line.tokens) {
931
+ const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
932
+ const hex = tok.kind === "text" ? tok.resolvedRun.shadingColorHex : void 0;
933
+ if (hex !== void 0) {
934
+ if (hex !== painted) {
935
+ if (painted !== "") out.push("f");
936
+ const [sr, sg, sb] = hexToRgb01(hex);
937
+ out.push(`${formatNumber(sr)} ${formatNumber(sg)} ${formatNumber(sb)} rg`);
938
+ painted = hex;
939
+ }
940
+ out.push(`${formatNumber(sx)} ${formatNumber(baselineY - shDescent)} ${formatNumber(w)} ${formatNumber(shAscent + shDescent)} re`);
941
+ }
942
+ sx += w;
943
+ }
944
+ if (painted !== "") out.push("f");
945
+ out.push("Q");
946
+ }
947
+ if (line.tokens.some((t) => t.kind === "text" && t.highlight === true)) {
948
+ if (inBT) {
949
+ out.push("ET");
950
+ inBT = false;
951
+ }
952
+ const hlAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
953
+ const hlDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
954
+ const [hr, hg, hb] = hexToRgb01("fff3a3");
955
+ out.push("q");
956
+ out.push(`${formatNumber(hr)} ${formatNumber(hg)} ${formatNumber(hb)} rg`);
957
+ let hx = originX;
958
+ for (const tok of line.tokens) {
959
+ const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
960
+ if (tok.kind === "text" && tok.highlight === true) out.push(`${formatNumber(hx)} ${formatNumber(baselineY - hlDescent)} ${formatNumber(w)} ${formatNumber(hlAscent + hlDescent)} re`);
961
+ hx += w;
962
+ }
963
+ out.push("f");
964
+ out.push("Q");
965
+ }
966
+ if (line.tokens.some(isDecorated)) {
967
+ if (inBT) {
968
+ out.push("ET");
969
+ inBT = false;
970
+ }
971
+ out.push("q");
972
+ let dx = originX;
973
+ for (const tok of line.tokens) {
974
+ const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
975
+ if (isDecorated(tok)) {
976
+ const thickness = Math.max(.4, tok.fontSizePt * .06);
977
+ const paint = (hex) => {
978
+ const [r, g, b] = hexToRgb01(hex);
979
+ out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
980
+ };
981
+ paint(tok.resolvedRun.colorHex);
982
+ if (tok.resolvedRun.underline !== "none") {
983
+ if (tok.resolvedRun.underlineColorHex) paint(tok.resolvedRun.underlineColorHex);
984
+ const style = tok.resolvedRun.underline;
985
+ const t = style === "thick" || style === "dottedHeavy" || style === "dashHeavy" ? thickness * 2 : thickness;
986
+ const y = baselineY - tok.fontSizePt * .12 - t;
987
+ const dashed = style === "dotted" || style === "dottedHeavy" ? [t, t * 2] : style === "dash" || style === "dashHeavy" ? [t * 4, t * 3] : void 0;
988
+ if (dashed) {
989
+ out.push(`${formatNumber(t)} w`);
990
+ out.push(`[${dashed.map((n) => formatNumber(n)).join(" ")}] 0 d`);
991
+ const [r, g, b] = hexToRgb01(tok.resolvedRun.underlineColorHex ?? tok.resolvedRun.colorHex);
992
+ out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} RG`);
993
+ out.push(`${formatNumber(dx)} ${formatNumber(y + t / 2)} m ${formatNumber(dx + w)} ${formatNumber(y + t / 2)} l S`);
994
+ out.push("[] 0 d");
995
+ } else {
996
+ out.push(`${formatNumber(dx)} ${formatNumber(y)} ${formatNumber(w)} ${formatNumber(t)} re f`);
997
+ if (style === "double") out.push(`${formatNumber(dx)} ${formatNumber(y - t * 2)} ${formatNumber(w)} ${formatNumber(t)} re f`);
998
+ }
999
+ if (tok.resolvedRun.underlineColorHex) paint(tok.resolvedRun.colorHex);
1000
+ }
1001
+ if (tok.resolvedRun.strike) {
1002
+ const y = baselineY + tok.fontSizePt * .26;
1003
+ out.push(`${formatNumber(dx)} ${formatNumber(y)} ${formatNumber(w)} ${formatNumber(thickness)} re f`);
1004
+ }
1005
+ }
1006
+ dx += w;
1007
+ }
1008
+ out.push("Q");
1009
+ }
1010
+ const hasRise = line.tokens.some((t) => t.kind === "text" && t.risePt !== void 0);
1011
+ const hasFauxItalic = line.tokens.some((t) => t.kind === "text" && t.synthetic?.italic === true);
1012
+ if (rotationDeg !== 0 && !hasImageToken && !hasMathToken) {
1013
+ if (!inBT) {
1014
+ out.push("BT");
1015
+ inBT = true;
1016
+ }
1017
+ const rad = rotationDeg * Math.PI / 180;
1018
+ const cos = formatNumber(Math.cos(rad));
1019
+ const sin = formatNumber(Math.sin(rad));
1020
+ out.push(`${cos} ${sin} ${formatNumber(-Math.sin(rad))} ${cos} ${formatNumber(originX)} ${formatNumber(baselineY)} Tm`);
1021
+ for (const tok of line.tokens) {
1022
+ if (tok.kind !== "text") continue;
1023
+ switchFontIfNeeded(tok);
1024
+ out.push(tok.font.measure.showText(tok.text));
1025
+ }
1026
+ } else if (extraPerSpace !== 0 || hasImageToken || hasMathToken || hasRtl || hasRise || hasFauxItalic || hasTab) {
1027
+ const order = hasRtl ? lineVisualOrder(line) : line.tokens.map((_, i) => i);
1028
+ let x = originX;
1029
+ for (const ti of order) {
1030
+ const tok = line.tokens[ti];
1031
+ if (tok.kind === "image") {
1032
+ flushLink();
1033
+ emitImageToken(tok, x, baselineY);
1034
+ x += tok.widthPt;
1035
+ continue;
1036
+ }
1037
+ if (tok.kind === "math") {
1038
+ flushLink();
1039
+ emitMathToken(tok, x, baselineY);
1040
+ x += tok.widthPt;
1041
+ continue;
1042
+ }
1043
+ if (!inBT) {
1044
+ out.push("BT");
1045
+ inBT = true;
1046
+ }
1047
+ switchFontIfNeeded(tok);
1048
+ const slant = tok.synthetic?.italic === true ? FAUX_ITALIC_SHEAR : 0;
1049
+ out.push(`1 0 ${formatNumber(slant)} 1 ${formatNumber(x)} ${formatNumber(baselineY + (tok.risePt ?? 0))} Tm`);
1050
+ out.push(showToken(tok));
1051
+ const tokenX0 = x;
1052
+ x += tok.widthPt;
1053
+ if (tok.isSpace) x += extraPerSpace;
1054
+ trackLink(tok.href, tok.anchor, tok.text, tokenX0, x);
1055
+ }
1056
+ flushLink();
1057
+ } else {
1058
+ if (!inBT) {
1059
+ out.push("BT");
1060
+ inBT = true;
1061
+ }
1062
+ out.push(`1 0 0 1 ${formatNumber(originX)} ${formatNumber(baselineY)} Tm`);
1063
+ let x = originX;
1064
+ for (const tok of line.tokens) {
1065
+ if (tok.kind !== "text") continue;
1066
+ switchFontIfNeeded(tok);
1067
+ out.push(tok.font.measure.showText(tok.text));
1068
+ trackLink(tok.href, tok.anchor, tok.text, x, x + tok.widthPt);
1069
+ x += tok.widthPt;
1070
+ }
1071
+ flushLink();
1072
+ }
1073
+ };
1074
+ const emitLinesPass = () => {
1075
+ if (lines.length === 0) return;
1076
+ if (tagging) for (const cmd of lines) {
1077
+ if (inBT) {
1078
+ out.push("ET");
1079
+ inBT = false;
1080
+ }
1081
+ const sid = cmd.structId;
1082
+ if (sid !== void 0) {
1083
+ const mcid = tagging.next++;
1084
+ tagging.assigned = true;
1085
+ tagging.record(sid, mcid);
1086
+ out.push(`/${tagging.tagFor(sid)} <</MCID ${mcid}>> BDC`);
1087
+ } else if (cmd.artifact === "pagination") out.push("/Artifact <</Type /Pagination>> BDC");
1088
+ else out.push("/Artifact BMC");
1089
+ emitClipped(cmd, emitOneLine);
1090
+ if (inBT) {
1091
+ out.push("ET");
1092
+ inBT = false;
1093
+ }
1094
+ out.push("EMC");
1095
+ }
1096
+ else {
1097
+ out.push("BT");
1098
+ inBT = true;
1099
+ for (const cmd of lines) emitClipped(cmd, emitOneLine);
1100
+ if (inBT) out.push("ET");
1101
+ }
1102
+ };
1103
+ const emitInOrder = (item) => {
1104
+ if (item.type === "shape") {
1105
+ const t = item.shape.transform;
1106
+ const fillAlpha = item.shape.fillAlpha;
1107
+ for (const op of emitVectorShape({
1108
+ ...item.shape,
1109
+ transform: [
1110
+ t[0],
1111
+ -t[1],
1112
+ t[2],
1113
+ -t[3],
1114
+ t[4],
1115
+ H - t[5]
1116
+ ]
1117
+ }, gradientNames?.get(item.shape), void 0, gradientMaskNames?.get(item.shape) ?? (fillAlpha !== void 0 && fillAlpha < 1 ? alphaStateNames?.get(Math.round(fillAlpha * 1e3) / 1e3) : void 0))) out.push(op);
1118
+ return;
1119
+ }
1120
+ if (item.type === "image") {
1121
+ if (item.imageResourceName === "") return;
1122
+ out.push("q");
1123
+ if (item.clip) {
1124
+ const t = item.clip.transform;
1125
+ out.push(...emitClipPath(item.clip.paths, [
1126
+ t[0],
1127
+ -t[1],
1128
+ t[2],
1129
+ -t[3],
1130
+ t[4],
1131
+ H - t[5]
1132
+ ]));
1133
+ }
1134
+ const state = item.alpha !== void 0 && item.alpha < 1 ? alphaStateNames?.get(Math.round(item.alpha * 1e3) / 1e3) : void 0;
1135
+ if (state !== void 0) out.push(`/${state} gs`);
1136
+ out.push(...paintImageBody(item, H - item.y - item.height, duotoneStateFor?.(item, H)));
1137
+ out.push(...washItems(item.wash, alphaStateNames));
1138
+ out.push("Q");
1139
+ return;
1140
+ }
1141
+ if (item.type === "fill") {
1142
+ const [r, g, b] = hexToRgb01(item.fillColorHex);
1143
+ out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`, `${formatNumber(item.x)} ${formatNumber(H - item.y - item.height)} ${formatNumber(item.width)} ${formatNumber(item.height)} re`, "f");
1144
+ return;
1145
+ }
1146
+ if (item.type !== "line") return;
1147
+ emitClipped(item, emitOneLine);
1148
+ if (inBT) {
1149
+ out.push("ET");
1150
+ inBT = false;
1151
+ }
1152
+ };
1153
+ if (plan.behind.length > 0) {
1154
+ if (tagging) out.push("/Artifact BMC");
1155
+ for (const item of plan.behind) emitInOrder(item);
1156
+ if (tagging) out.push("EMC");
1157
+ }
533
1158
  const fills = plan.fills;
534
1159
  if (fills.length > 0) {
535
1160
  if (tagging) out.push("/Artifact BMC");
536
1161
  out.push("q");
537
- let lastColor = "";
1162
+ let lastFill = "";
538
1163
  for (const f of fills) {
539
1164
  const color = f.fillColorHex;
540
- if (color !== lastColor) {
1165
+ if (color !== lastFill) {
541
1166
  const [r, g, b] = hexToRgb01(color);
542
1167
  out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
543
- lastColor = color;
1168
+ lastFill = color;
544
1169
  }
545
1170
  out.push(`${formatNumber(f.x)} ${formatNumber(H - f.y - f.height)} ${formatNumber(f.width)} ${formatNumber(f.height)} re`);
546
1171
  out.push("f");
@@ -561,8 +1186,9 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames) {
561
1186
  H - t[5]
562
1187
  ]));
563
1188
  }
564
- out.push(...placeImage(img.x, H - img.y - img.height, img.width, img.height, img.crop, img.rotationDeg, img.flipH, img.flipV));
565
- out.push(`/${img.imageResourceName} Do`);
1189
+ const state = img.alpha !== void 0 && img.alpha < 1 ? alphaStateNames?.get(Math.round(img.alpha * 1e3) / 1e3) : void 0;
1190
+ if (state !== void 0) out.push(`/${state} gs`);
1191
+ out.push(...paintImageBody(img, H - img.y - img.height, duotoneStateFor?.(img, H)));
566
1192
  out.push(...washItems(img.wash, alphaStateNames));
567
1193
  out.push("Q");
568
1194
  });
@@ -572,7 +1198,7 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames) {
572
1198
  out.push("q");
573
1199
  out.push("2 J");
574
1200
  let lastWidth = -1;
575
- let lastColor = "";
1201
+ let lastStroke = "";
576
1202
  let lastDash = "";
577
1203
  for (const b of borders) {
578
1204
  const width = b.borderSizePt;
@@ -587,10 +1213,10 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames) {
587
1213
  out.push(`${formatNumber(strokeW)} w`);
588
1214
  lastWidth = strokeW;
589
1215
  }
590
- if (color !== lastColor) {
1216
+ if (color !== lastStroke) {
591
1217
  const [r, g, bl] = hexToRgb01(color);
592
1218
  out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(bl)} RG`);
593
- lastColor = color;
1219
+ lastStroke = color;
594
1220
  }
595
1221
  const x = b.x;
596
1222
  const y = H - b.y - b.height;
@@ -652,439 +1278,16 @@ function emitPageContent(page, tagging, gradientNames, alphaStateNames) {
652
1278
  const shadowLayer = sh.shape.shadow ? shadowBlurLayers(sh.shape.shadow) : void 0;
653
1279
  const alphaState = shadowLayer && shadowLayer.alpha < 1 ? alphaStateNames?.get(Math.round(shadowLayer.alpha * 1e3) / 1e3) : void 0;
654
1280
  const fillAlpha = sh.shape.fillAlpha;
655
- const fillAlphaState = fillAlpha !== void 0 && fillAlpha < 1 ? alphaStateNames?.get(Math.round(fillAlpha * 1e3) / 1e3) : void 0;
1281
+ const fillAlphaState = gradientMaskNames?.get(sh.shape) ?? (fillAlpha !== void 0 && fillAlpha < 1 ? alphaStateNames?.get(Math.round(fillAlpha * 1e3) / 1e3) : void 0);
656
1282
  for (const op of emitVectorShape(shape, gradientNames?.get(sh.shape), alphaState, fillAlphaState)) out.push(op);
657
1283
  });
658
- const lines = plan.lines;
659
- if (lines.length > 0) {
660
- let inBT = false;
661
- let lastFont = "";
662
- let lastTc = 0;
663
- let lastSize = -1;
664
- let lastColor = "";
665
- const switchFontIfNeeded = (tok) => {
666
- const fontKey = tok.font.resourceName;
667
- if (fontKey !== lastFont || tok.fontSizePt !== lastSize) {
668
- out.push(`/${fontKey} ${formatNumber(tok.fontSizePt)} Tf`);
669
- lastFont = fontKey;
670
- lastSize = tok.fontSizePt;
671
- }
672
- const tc = tok.resolvedRun.letterSpacingPt ?? 0;
673
- if (tc !== lastTc) {
674
- out.push(`${formatNumber(tc)} Tc`);
675
- lastTc = tc;
676
- }
677
- if (tok.resolvedRun.colorHex !== lastColor) {
678
- const [r, g, b] = hexToRgb01(tok.resolvedRun.colorHex);
679
- out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
680
- lastColor = tok.resolvedRun.colorHex;
681
- }
682
- };
683
- const emitImageToken = (tok, x, baselineY) => {
684
- const meta = tok.metafile;
685
- if (meta) {
686
- if (inBT) {
687
- out.push("ET");
688
- inBT = false;
689
- }
690
- const b = tok.drawBox;
691
- const ox = x + (b?.dxPt ?? 0);
692
- const oy = baselineY + (b?.dyPt ?? 0);
693
- for (const sh of meta.shapes) for (const op of emitVectorShape({
694
- ...sh,
695
- transform: [
696
- 1,
697
- 0,
698
- 0,
699
- 1,
700
- ox,
701
- oy
702
- ]
703
- })) out.push(op);
704
- for (const t of meta.texts) {
705
- const first = t.line.tokens.find((k) => k.kind === "text");
706
- if (!first) continue;
707
- const [r, g, bl] = hexToRgb01(first.resolvedRun.colorHex);
708
- out.push("BT");
709
- out.push(`/${first.font.resourceName} ${formatNumber(first.fontSizePt)} Tf`);
710
- out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(bl)} rg`);
711
- out.push(`1 0 0 1 ${formatNumber(ox + t.x)} ${formatNumber(oy + t.y)} Tm`);
712
- out.push(first.font.measure.showText(first.text));
713
- out.push("ET");
714
- }
715
- lastFont = "";
716
- lastSize = -1;
717
- lastColor = "";
718
- return;
719
- }
720
- if (!tok.imageResourceName) return;
721
- if (inBT) {
722
- out.push("ET");
723
- inBT = false;
724
- }
725
- const shdw = tok.shadow;
726
- if (shdw) {
727
- const bx = tok.drawBox;
728
- const [shr, shg, shb] = hexToRgb01(shdw.colorHex);
729
- const layers = shadowBlurLayers(shdw);
730
- const state = layers.alpha < 1 ? alphaStateNames?.get(Math.round(layers.alpha * 1e3) / 1e3) : void 0;
731
- const sx = x + (bx?.dxPt ?? 0) + shdw.dxPt;
732
- const sy = baselineY + (bx?.dyPt ?? 0) - shdw.dyPt;
733
- const sw = bx?.widthPt ?? tok.widthPt;
734
- const sh = bx?.heightPt ?? tok.heightPt;
735
- out.push("q");
736
- if (state) out.push(`/${state} gs`);
737
- out.push(`${formatNumber(shr)} ${formatNumber(shg)} ${formatNumber(shb)} rg`);
738
- for (let i = 0; i < layers.count; i++) {
739
- const grow = layers.count > 1 ? -shdw.blurPt / 2 + (i + .5) * shdw.blurPt / layers.count : 0;
740
- out.push(`${formatNumber(sx - grow)} ${formatNumber(sy - grow)} ${formatNumber(Math.max(sw + 2 * grow, 0))} ${formatNumber(Math.max(sh + 2 * grow, 0))} re`);
741
- out.push("f");
742
- }
743
- out.push("Q");
744
- }
745
- out.push("q");
746
- const b = tok.drawBox;
747
- out.push(...b ? placeImage(x + b.dxPt, baselineY + b.dyPt, b.widthPt, b.heightPt, tok.crop, tok.rotationDeg, tok.flipH, tok.flipV) : placeImage(x, baselineY, tok.widthPt, tok.heightPt, tok.crop, tok.rotationDeg, tok.flipH, tok.flipV));
748
- out.push(`/${tok.imageResourceName} Do`);
749
- out.push(...washItems(tok.wash, alphaStateNames));
750
- out.push("Q");
751
- const frame = tok.outline;
752
- if (frame) {
753
- const [fr, fg, fb] = hexToRgb01(frame.colorHex);
754
- out.push("q");
755
- out.push(`${formatNumber(frame.widthPt)} w`);
756
- out.push(`${formatNumber(fr)} ${formatNumber(fg)} ${formatNumber(fb)} RG`);
757
- out.push(`${formatNumber(x + (b?.dxPt ?? 0))} ${formatNumber(baselineY + (b?.dyPt ?? 0))} ${formatNumber(b?.widthPt ?? tok.widthPt)} ${formatNumber(b?.heightPt ?? tok.heightPt)} re`);
758
- out.push("S");
759
- out.push("Q");
760
- }
761
- lastFont = "";
762
- lastSize = -1;
763
- lastColor = "";
764
- lastTc = 0;
765
- };
766
- const emitMathToken = (tok, originX, baselineY) => {
767
- for (const it of tok.items) if (it.kind === "glyph") {
768
- if (!inBT) {
769
- out.push("BT");
770
- inBT = true;
771
- }
772
- if (it.font.resourceName !== lastFont || it.sizePt !== lastSize) {
773
- out.push(`/${it.font.resourceName} ${formatNumber(it.sizePt)} Tf`);
774
- lastFont = it.font.resourceName;
775
- lastSize = it.sizePt;
776
- }
777
- if (lastColor !== "000000") {
778
- out.push("0 0 0 rg");
779
- lastColor = "000000";
780
- }
781
- out.push(`1 0 0 1 ${formatNumber(originX + it.x)} ${formatNumber(baselineY + it.y)} Tm`);
782
- out.push(it.font.measure.showText(it.text));
783
- } else if (it.kind === "rule") {
784
- if (inBT) {
785
- out.push("ET");
786
- inBT = false;
787
- }
788
- out.push("q");
789
- out.push("0 0 0 rg");
790
- out.push(`${formatNumber(originX + it.x)} ${formatNumber(baselineY + it.y)} ${formatNumber(it.w)} ${formatNumber(it.h)} re`);
791
- out.push("f");
792
- out.push("Q");
793
- lastFont = "";
794
- lastSize = -1;
795
- lastColor = "";
796
- } else {
797
- if (inBT) {
798
- out.push("ET");
799
- inBT = false;
800
- }
801
- const shape = {
802
- paths: [{ segments: it.segments }],
803
- ...it.fill ? { fillColorHex: "000000" } : {},
804
- ...it.strokeWidthPt !== void 0 ? { stroke: {
805
- colorHex: "000000",
806
- widthPt: it.strokeWidthPt
807
- } } : {},
808
- transform: [
809
- 1,
810
- 0,
811
- 0,
812
- 1,
813
- originX,
814
- baselineY
815
- ]
816
- };
817
- for (const op of emitVectorShape(shape)) out.push(op);
818
- lastFont = "";
819
- lastSize = -1;
820
- lastColor = "";
821
- }
822
- };
823
- const emitClipped = (cmd, body) => {
824
- const clip = cmd.clip;
825
- if (!clip) {
826
- body(cmd);
827
- return;
828
- }
829
- if (inBT) {
830
- out.push("ET");
831
- inBT = false;
832
- }
833
- out.push("q");
834
- out.push(`${formatNumber(clip.x)} ${formatNumber(H - clip.y - clip.height)} ${formatNumber(clip.width)} ${formatNumber(clip.height)} re W n`);
835
- lastFont = "";
836
- lastSize = -1;
837
- lastColor = "";
838
- lastTc = 0;
839
- out.push("BT");
840
- inBT = true;
841
- body(cmd);
842
- if (inBT) {
843
- out.push("ET");
844
- inBT = false;
845
- }
846
- out.push("Q");
847
- lastFont = "";
848
- lastSize = -1;
849
- lastColor = "";
850
- lastTc = 0;
851
- };
852
- const emitOneLine = (cmd) => {
853
- const line = cmd.line;
854
- const originX = cmd.originX;
855
- const baselineY = H - cmd.baselineY;
856
- const rotationDeg = cmd.rotationDeg ?? 0;
857
- const lineFs = line.maxFontSizePt || 12;
858
- const linkAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
859
- const linkDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
860
- let linkHref;
861
- let linkAnchor;
862
- let linkText = "";
863
- let linkX0 = 0;
864
- let linkX1 = 0;
865
- const flushLink = () => {
866
- if (linkHref === void 0 && linkAnchor === void 0) return;
867
- const safe = linkHref !== void 0 ? sanitizeHref(linkHref) : void 0;
868
- const target = safe !== void 0 ? { href: safe } : linkAnchor !== void 0 ? { anchor: linkAnchor } : void 0;
869
- if (target) links.push({
870
- ...target,
871
- text: linkText.trim(),
872
- rect: [
873
- linkX0,
874
- baselineY - linkDescent,
875
- linkX1,
876
- baselineY + linkAscent
877
- ],
878
- ...cmd.structId !== void 0 ? { structId: cmd.structId } : {}
879
- });
880
- linkHref = void 0;
881
- linkAnchor = void 0;
882
- linkText = "";
883
- };
884
- const trackLink = (href, anchor, text, x0, x1) => {
885
- if (href !== linkHref || anchor !== linkAnchor) {
886
- flushLink();
887
- if (href !== void 0 || anchor !== void 0) {
888
- linkHref = href;
889
- linkAnchor = anchor;
890
- linkX0 = x0;
891
- }
892
- }
893
- if (href !== void 0 || anchor !== void 0) {
894
- linkX1 = x1;
895
- linkText += text;
896
- }
897
- };
898
- const extraPerSpace = computeJustifyExtra(line);
899
- const hasImageToken = line.tokens.some((t) => t.kind === "image");
900
- const hasMathToken = line.tokens.some((t) => t.kind === "math");
901
- const hasRtl = line.tokens.some((t) => t.bidiLevel % 2 === 1);
902
- const hasTab = line.tokens.some((t) => t.kind === "text" && t.tab === true);
903
- /** A text token carrying underline or strikethrough. */
904
- const isDecorated = (tok) => tok.kind === "text" && (tok.resolvedRun.underline !== "none" || tok.resolvedRun.strike);
905
- const showToken = (tok) => {
906
- const text = tok.bidiLevel % 2 === 1 ? reverseByCodePoint(tok.text) : tok.text;
907
- return tok.font.measure.showText(text);
908
- };
909
- if (line.tokens.some((t) => t.kind === "text" && t.resolvedRun.shadingColorHex)) {
910
- if (inBT) {
911
- out.push("ET");
912
- inBT = false;
913
- }
914
- const shAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
915
- const shDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
916
- out.push("q");
917
- let sx = originX;
918
- let painted = "";
919
- for (const tok of line.tokens) {
920
- const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
921
- const hex = tok.kind === "text" ? tok.resolvedRun.shadingColorHex : void 0;
922
- if (hex !== void 0) {
923
- if (hex !== painted) {
924
- if (painted !== "") out.push("f");
925
- const [sr, sg, sb] = hexToRgb01(hex);
926
- out.push(`${formatNumber(sr)} ${formatNumber(sg)} ${formatNumber(sb)} rg`);
927
- painted = hex;
928
- }
929
- out.push(`${formatNumber(sx)} ${formatNumber(baselineY - shDescent)} ${formatNumber(w)} ${formatNumber(shAscent + shDescent)} re`);
930
- }
931
- sx += w;
932
- }
933
- if (painted !== "") out.push("f");
934
- out.push("Q");
935
- }
936
- if (line.tokens.some((t) => t.kind === "text" && t.highlight === true)) {
937
- if (inBT) {
938
- out.push("ET");
939
- inBT = false;
940
- }
941
- const hlAscent = Math.max(lineFs, line.mathAscentPt ?? 0);
942
- const hlDescent = Math.max(lineFs * .2, line.mathDescentPt ?? 0);
943
- const [hr, hg, hb] = hexToRgb01("fff3a3");
944
- out.push("q");
945
- out.push(`${formatNumber(hr)} ${formatNumber(hg)} ${formatNumber(hb)} rg`);
946
- let hx = originX;
947
- for (const tok of line.tokens) {
948
- const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
949
- if (tok.kind === "text" && tok.highlight === true) out.push(`${formatNumber(hx)} ${formatNumber(baselineY - hlDescent)} ${formatNumber(w)} ${formatNumber(hlAscent + hlDescent)} re`);
950
- hx += w;
951
- }
952
- out.push("f");
953
- out.push("Q");
954
- }
955
- if (line.tokens.some(isDecorated)) {
956
- if (inBT) {
957
- out.push("ET");
958
- inBT = false;
959
- }
960
- out.push("q");
961
- let dx = originX;
962
- for (const tok of line.tokens) {
963
- const w = tok.widthPt + (tok.kind === "text" && tok.isSpace ? extraPerSpace : 0);
964
- if (isDecorated(tok)) {
965
- const thickness = Math.max(.4, tok.fontSizePt * .06);
966
- const paint = (hex) => {
967
- const [r, g, b] = hexToRgb01(hex);
968
- out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} rg`);
969
- };
970
- paint(tok.resolvedRun.colorHex);
971
- if (tok.resolvedRun.underline !== "none") {
972
- if (tok.resolvedRun.underlineColorHex) paint(tok.resolvedRun.underlineColorHex);
973
- const style = tok.resolvedRun.underline;
974
- const t = style === "thick" || style === "dottedHeavy" || style === "dashHeavy" ? thickness * 2 : thickness;
975
- const y = baselineY - tok.fontSizePt * .12 - t;
976
- const dashed = style === "dotted" || style === "dottedHeavy" ? [t, t * 2] : style === "dash" || style === "dashHeavy" ? [t * 4, t * 3] : void 0;
977
- if (dashed) {
978
- out.push(`${formatNumber(t)} w`);
979
- out.push(`[${dashed.map((n) => formatNumber(n)).join(" ")}] 0 d`);
980
- const [r, g, b] = hexToRgb01(tok.resolvedRun.underlineColorHex ?? tok.resolvedRun.colorHex);
981
- out.push(`${formatNumber(r)} ${formatNumber(g)} ${formatNumber(b)} RG`);
982
- out.push(`${formatNumber(dx)} ${formatNumber(y + t / 2)} m ${formatNumber(dx + w)} ${formatNumber(y + t / 2)} l S`);
983
- out.push("[] 0 d");
984
- } else {
985
- out.push(`${formatNumber(dx)} ${formatNumber(y)} ${formatNumber(w)} ${formatNumber(t)} re f`);
986
- if (style === "double") out.push(`${formatNumber(dx)} ${formatNumber(y - t * 2)} ${formatNumber(w)} ${formatNumber(t)} re f`);
987
- }
988
- if (tok.resolvedRun.underlineColorHex) paint(tok.resolvedRun.colorHex);
989
- }
990
- if (tok.resolvedRun.strike) {
991
- const y = baselineY + tok.fontSizePt * .26;
992
- out.push(`${formatNumber(dx)} ${formatNumber(y)} ${formatNumber(w)} ${formatNumber(thickness)} re f`);
993
- }
994
- }
995
- dx += w;
996
- }
997
- out.push("Q");
998
- }
999
- const hasRise = line.tokens.some((t) => t.kind === "text" && t.risePt !== void 0);
1000
- if (rotationDeg !== 0 && !hasImageToken && !hasMathToken) {
1001
- if (!inBT) {
1002
- out.push("BT");
1003
- inBT = true;
1004
- }
1005
- const rad = rotationDeg * Math.PI / 180;
1006
- const cos = formatNumber(Math.cos(rad));
1007
- const sin = formatNumber(Math.sin(rad));
1008
- out.push(`${cos} ${sin} ${formatNumber(-Math.sin(rad))} ${cos} ${formatNumber(originX)} ${formatNumber(baselineY)} Tm`);
1009
- for (const tok of line.tokens) {
1010
- if (tok.kind !== "text") continue;
1011
- switchFontIfNeeded(tok);
1012
- out.push(tok.font.measure.showText(tok.text));
1013
- }
1014
- } else if (extraPerSpace !== 0 || hasImageToken || hasMathToken || hasRtl || hasRise || hasTab) {
1015
- const order = hasRtl ? lineVisualOrder(line) : line.tokens.map((_, i) => i);
1016
- let x = originX;
1017
- for (const ti of order) {
1018
- const tok = line.tokens[ti];
1019
- if (tok.kind === "image") {
1020
- flushLink();
1021
- emitImageToken(tok, x, baselineY);
1022
- x += tok.widthPt;
1023
- continue;
1024
- }
1025
- if (tok.kind === "math") {
1026
- flushLink();
1027
- emitMathToken(tok, x, baselineY);
1028
- x += tok.widthPt;
1029
- continue;
1030
- }
1031
- if (!inBT) {
1032
- out.push("BT");
1033
- inBT = true;
1034
- }
1035
- switchFontIfNeeded(tok);
1036
- out.push(`1 0 0 1 ${formatNumber(x)} ${formatNumber(baselineY + (tok.risePt ?? 0))} Tm`);
1037
- out.push(showToken(tok));
1038
- const tokenX0 = x;
1039
- x += tok.widthPt;
1040
- if (tok.isSpace) x += extraPerSpace;
1041
- trackLink(tok.href, tok.anchor, tok.text, tokenX0, x);
1042
- }
1043
- flushLink();
1044
- } else {
1045
- if (!inBT) {
1046
- out.push("BT");
1047
- inBT = true;
1048
- }
1049
- out.push(`1 0 0 1 ${formatNumber(originX)} ${formatNumber(baselineY)} Tm`);
1050
- let x = originX;
1051
- for (const tok of line.tokens) {
1052
- if (tok.kind !== "text") continue;
1053
- switchFontIfNeeded(tok);
1054
- out.push(tok.font.measure.showText(tok.text));
1055
- trackLink(tok.href, tok.anchor, tok.text, x, x + tok.widthPt);
1056
- x += tok.widthPt;
1057
- }
1058
- flushLink();
1059
- }
1060
- };
1061
- if (tagging) for (const cmd of lines) {
1062
- if (inBT) {
1063
- out.push("ET");
1064
- inBT = false;
1065
- }
1066
- const sid = cmd.structId;
1067
- if (sid !== void 0) {
1068
- const mcid = tagging.next++;
1069
- tagging.assigned = true;
1070
- tagging.record(sid, mcid);
1071
- out.push(`/${tagging.tagFor(sid)} <</MCID ${mcid}>> BDC`);
1072
- } else if (cmd.artifact === "pagination") out.push("/Artifact <</Type /Pagination>> BDC");
1073
- else out.push("/Artifact BMC");
1074
- emitClipped(cmd, emitOneLine);
1075
- if (inBT) {
1076
- out.push("ET");
1077
- inBT = false;
1078
- }
1079
- out.push("EMC");
1080
- }
1081
- else {
1082
- out.push("BT");
1083
- inBT = true;
1084
- for (const cmd of lines) emitClipped(cmd, emitOneLine);
1085
- if (inBT) out.push("ET");
1086
- }
1284
+ for (const run of plan.pictures) {
1285
+ if (run.length === 0) continue;
1286
+ if (tagging) out.push("/Artifact BMC");
1287
+ for (const item of run) emitInOrder(item);
1288
+ if (tagging) out.push("EMC");
1087
1289
  }
1290
+ emitLinesPass();
1088
1291
  return {
1089
1292
  content: encoder.encode(out.join("\n")),
1090
1293
  links