@poe-platform/safe-bash 0.1.762 → 0.1.763

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 (28) hide show
  1. package/dist/image-ast/index.browser.js +186 -55
  2. package/dist/image-ast/index.js +186 -55
  3. package/dist/pdf-ast/ast.d.ts +23 -0
  4. package/dist/pdf-ast/index.js +273 -91
  5. package/dist/safe-bash/command-chunks/{chunk-D4GXXV65.js → chunk-4XHONGIU.js} +2 -2
  6. package/dist/safe-bash/command-chunks/{chunk-P3Q2QSCA.js → chunk-S3G2B4OX.js} +186 -55
  7. package/dist/safe-bash/command-chunks/{chunk-F7MJRXQ2.js → chunk-S5EX6UP5.js} +1 -1
  8. package/dist/safe-bash/core.browser.js +1 -1
  9. package/dist/safe-bash/shell/runtime.js +1 -1
  10. package/dist/safe-bash-checksum-engine/index.d.ts +1 -1
  11. package/dist/safe-bash-checksum-engine/index.js +5 -1
  12. package/dist/safe-bash-command-ffmpeg/index.js +7486 -7432
  13. package/dist/safe-bash-command-gh/index.js +174 -105
  14. package/dist/safe-bash-command-imagemagick/index.js +2 -2
  15. package/dist/safe-bash-command-pandoc/command.js +3 -3
  16. package/dist/safe-bash-command-pandoc/index.js +3 -3
  17. package/dist/safe-bash-command-pdfimages/index.js +186 -55
  18. package/dist/safe-bash-command-pdfinfo/index.js +273 -91
  19. package/dist/safe-bash-command-pdftk/index.js +186 -55
  20. package/dist/safe-bash-command-pdftoppm/index.js +273 -91
  21. package/dist/safe-bash-command-pdftotext/index.js +186 -55
  22. package/dist/safe-bash-command-qpdf/index.js +186 -55
  23. package/dist/safe-bash-command-sips/index.js +2 -2
  24. package/dist/safe-bash-command-soffice/index.js +1 -1
  25. package/dist/safe-bash-command-ssconvert/index.js +249 -58
  26. package/dist/safe-bash-command-wkhtmltopdf/index.js +186 -55
  27. package/package.json +2 -2
  28. package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +13 -0
@@ -36107,14 +36107,18 @@ function evaluateContentStreamToDisplayList(params) {
36107
36107
  const paths = [];
36108
36108
  const images = [];
36109
36109
  const operations = [];
36110
+ let capturedOperations;
36111
+ let insideSoftMask = false;
36110
36112
  const emit = (operation) => {
36111
- const { clipPaths, clipImages } = curState();
36112
- if (clipPaths || clipImages) operation = { ...operation, value: {
36113
+ const { clipPaths, clipImages, softMask } = curState();
36114
+ if (clipPaths || clipImages || softMask) operation = { ...operation, value: {
36113
36115
  ...operation.value,
36114
36116
  ...clipPaths ? { clipPaths } : {},
36115
- ...clipImages ? { clipImages } : {}
36117
+ ...clipImages ? { clipImages } : {},
36118
+ ...softMask ? { softMask } : {}
36116
36119
  } };
36117
- operations.push(operation);
36120
+ (capturedOperations ?? operations).push(operation);
36121
+ if (insideSoftMask) return;
36118
36122
  switch (operation.kind) {
36119
36123
  case "path":
36120
36124
  paths.push(operation.value);
@@ -36220,7 +36224,7 @@ function evaluateContentStreamToDisplayList(params) {
36220
36224
  );
36221
36225
  return { r: kClamp(r), g: kClamp(g), b: kClamp(b) };
36222
36226
  };
36223
- const applyStateOperator = (st, operator, ops, activeResources) => {
36227
+ const applyStateOperator = (st, operator, ops, activeResources, activeFonts, depth) => {
36224
36228
  const num = (i, fb = 0) => ops[i]?.kind === "number" ? ops[i].value : fb;
36225
36229
  if (operator === "cm") {
36226
36230
  const m = [num(0, 1), num(1, 0), num(2, 0), num(3, 1), num(4, 0), num(5, 0)];
@@ -36303,6 +36307,46 @@ function evaluateContentStreamToDisplayList(params) {
36303
36307
  const extDict = params.cosDoc.resolveDict(dictGet(activeResources, "ExtGState"));
36304
36308
  const gsDict = extDict ? params.cosDoc.resolveDict(dictGet(extDict, ops[0].decoded)) : void 0;
36305
36309
  if (gsDict) {
36310
+ const mask = params.cosDoc.resolve(dictGet(gsDict, "SMask"));
36311
+ if (mask?.kind === "name" && mask.decoded === "None") {
36312
+ st.softMask = void 0;
36313
+ } else if (mask?.kind === "dict") {
36314
+ const subtype = params.cosDoc.resolve(dictGet(mask, "S"));
36315
+ const form = params.cosDoc.resolve(dictGet(mask, "G"));
36316
+ if (form?.kind === "stream" && subtype?.kind === "name" && (subtype.decoded === "Alpha" || subtype.decoded === "Luminosity")) {
36317
+ if (depth >= 8) throw new PdfError("E_LIMIT", "Soft-mask nesting exceeds the form depth limit");
36318
+ const parentOperations = capturedOperations;
36319
+ const parentInsideSoftMask = insideSoftMask;
36320
+ const savedTextState = { pendingTextClip, hasTextClip, activeTm, activeTlm };
36321
+ const captured = [];
36322
+ capturedOperations = captured;
36323
+ insideSoftMask = true;
36324
+ const maskState = { ...st, softMask: void 0, fillAlpha: 1, strokeAlpha: 1, blendMode: "Normal" };
36325
+ delete maskState.clipPaths;
36326
+ delete maskState.clipImages;
36327
+ delete maskState.clipRect;
36328
+ stateStack.push(maskState);
36329
+ try {
36330
+ paintForm(form, activeResources, activeFonts, depth, void 0, void 0, true);
36331
+ } finally {
36332
+ stateStack.pop();
36333
+ capturedOperations = parentOperations;
36334
+ insideSoftMask = parentInsideSoftMask;
36335
+ ({ pendingTextClip, hasTextClip, activeTm, activeTlm } = savedTextState);
36336
+ }
36337
+ const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
36338
+ const colorSpace = group ? dictGet(group, "CS") : void 0;
36339
+ const bc = params.cosDoc.resolveArray(dictGet(mask, "BC"));
36340
+ const components = bc?.items.map((item) => {
36341
+ const value = params.cosDoc.resolve(item);
36342
+ return value?.kind === "number" ? value.value : 0;
36343
+ });
36344
+ const [r, g, b] = components ? convertColorSpaceComponentsToRgb(params.cosDoc, colorSpace, "DeviceRGB", components, activeResources) : [0, 0, 0];
36345
+ const transfer = params.cosDoc.resolve(dictGet(mask, "TR"));
36346
+ const transferMap = transfer?.kind === "dict" || transfer?.kind === "stream" ? Uint8Array.from({ length: 256 }, (_, i) => Math.floor(kClamp(Math.fround(evalShadingFunctionToComponents(params.cosDoc, transfer, Math.fround(i / 255))[0] ?? 0)) * 255)) : void 0;
36347
+ st.softMask = { subtype: subtype.decoded, operations: captured, backdrop: { r, g, b }, transferMap };
36348
+ }
36349
+ }
36306
36350
  const bmNode = params.cosDoc.resolve(dictGet(gsDict, "BM"));
36307
36351
  if (bmNode?.kind === "name") {
36308
36352
  st.blendMode = bmNode.decoded === "Compatible" ? "Normal" : bmNode.decoded;
@@ -36389,6 +36433,84 @@ function evaluateContentStreamToDisplayList(params) {
36389
36433
  ] }], void 0, void 0, resources, activeFonts, depth);
36390
36434
  stateStack.pop();
36391
36435
  };
36436
+ const paintForm = (form, activeResources, activeFonts, depth, mcid, actualText, maskGroup = false) => {
36437
+ const st = curState();
36438
+ const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
36439
+ const groupType = group ? params.cosDoc.resolve(dictGet(group, "S")) : void 0;
36440
+ const isolated = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
36441
+ const isolate = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && isolated?.kind === "boolean" && isolated.value;
36442
+ const formStreamBytes = params.cosDoc.decodeStream(form);
36443
+ const formNodes = parseContentStream(formStreamBytes);
36444
+ const formResDict = params.cosDoc.resolveDict(dictGet(form.dict, "Resources")) ?? activeResources;
36445
+ const formFonts = new Map(activeFonts);
36446
+ for (const [k, v] of resolvePageFonts(params.cosDoc, formResDict).entries()) {
36447
+ formFonts.set(k, v);
36448
+ }
36449
+ let nextCtm = [...st.ctm];
36450
+ const matArr = params.cosDoc.resolveArray(dictGet(form.dict, "Matrix"));
36451
+ if (matArr && matArr.items.length >= 6) {
36452
+ const mn = (idx, fb = 0) => {
36453
+ const resolved = params.cosDoc.resolve(matArr.items[idx]);
36454
+ return resolved?.kind === "number" ? resolved.value : fb;
36455
+ };
36456
+ const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
36457
+ nextCtm = multiplyMatrices(formMat, nextCtm);
36458
+ }
36459
+ let nextClip = !isolate && st.clipRect ? [...st.clipRect] : void 0;
36460
+ let nextClipPaths = isolate ? void 0 : st.clipPaths;
36461
+ const bboxArr = params.cosDoc.resolveArray(dictGet(form.dict, "BBox"));
36462
+ if (bboxArr && bboxArr.items.length >= 4) {
36463
+ const bn = (idx, fb = 0) => {
36464
+ const resolved = params.cosDoc.resolve(bboxArr.items[idx]);
36465
+ return resolved?.kind === "number" ? resolved.value : fb;
36466
+ };
36467
+ const bx0 = bn(0, 0), by0 = bn(1, 0), bx1 = bn(2, 0), by1 = bn(3, 0);
36468
+ const pts = [
36469
+ transformPoint(nextCtm, bx0, by0),
36470
+ transformPoint(nextCtm, bx1, by0),
36471
+ transformPoint(nextCtm, bx1, by1),
36472
+ transformPoint(nextCtm, bx0, by1)
36473
+ ];
36474
+ nextClipPaths = [...nextClipPaths ?? [], [
36475
+ ...pts.map(([x, y], index) => ({ kind: index === 0 ? "move" : "line", x, y })),
36476
+ { kind: "close" }
36477
+ ]];
36478
+ const fMinX = Math.min(...pts.map((p) => p[0]));
36479
+ const fMinY = Math.min(...pts.map((p) => p[1]));
36480
+ const fMaxX = Math.max(...pts.map((p) => p[0]));
36481
+ const fMaxY = Math.max(...pts.map((p) => p[1]));
36482
+ if (fMaxX > fMinX && fMaxY > fMinY) {
36483
+ nextClip = nextClip ? [
36484
+ Math.max(nextClip[0], fMinX),
36485
+ Math.max(nextClip[1], fMinY),
36486
+ Math.min(nextClip[2], fMaxX),
36487
+ Math.min(nextClip[3], fMaxY)
36488
+ ] : [fMinX, fMinY, fMaxX, fMaxY];
36489
+ }
36490
+ }
36491
+ const parentOperations = capturedOperations;
36492
+ const children = [];
36493
+ const nextState = { ...st, ctm: nextCtm };
36494
+ if (isolate) {
36495
+ capturedOperations = children;
36496
+ nextState.fillAlpha = nextState.strokeAlpha = 1;
36497
+ nextState.blendMode = "Normal";
36498
+ nextState.softMask = void 0;
36499
+ delete nextState.clipImages;
36500
+ delete nextState.clipPaths;
36501
+ delete nextState.clipRect;
36502
+ }
36503
+ if (nextClip) nextState.clipRect = nextClip;
36504
+ if (nextClipPaths) nextState.clipPaths = nextClipPaths;
36505
+ stateStack.push(nextState);
36506
+ try {
36507
+ walkNodes(formNodes, mcid, actualText, formResDict, formFonts, depth + 1);
36508
+ } finally {
36509
+ stateStack.pop();
36510
+ capturedOperations = parentOperations;
36511
+ }
36512
+ if (isolate) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
36513
+ };
36392
36514
  const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
36393
36515
  for (const node of nodes) {
36394
36516
  switch (node.kind) {
@@ -36433,7 +36555,7 @@ function evaluateContentStreamToDisplayList(params) {
36433
36555
  break;
36434
36556
  }
36435
36557
  case "state-op": {
36436
- applyStateOperator(curState(), node.operator, node.operands, activeResources);
36558
+ applyStateOperator(curState(), node.operator, node.operands, activeResources, activeFonts, depth);
36437
36559
  break;
36438
36560
  }
36439
36561
  case "path-op": {
@@ -36719,53 +36841,7 @@ function evaluateContentStreamToDisplayList(params) {
36719
36841
  ...st.clipRect ? { clipRect: [...st.clipRect] } : {}
36720
36842
  }, patternMask, activeResources, activeFonts, depth);
36721
36843
  } else if (sub === "Form" && depth < 8) {
36722
- const formStreamBytes = params.cosDoc.decodeStream(xobjNode);
36723
- const formNodes = parseContentStream(formStreamBytes);
36724
- const formResDict = params.cosDoc.resolveDict(dictGet(xobjNode.dict, "Resources")) ?? activeResources;
36725
- const formFonts = new Map(activeFonts);
36726
- for (const [k, v] of resolvePageFonts(params.cosDoc, formResDict).entries()) {
36727
- formFonts.set(k, v);
36728
- }
36729
- let nextCtm = [...st.ctm];
36730
- const matArr = params.cosDoc.resolveArray(dictGet(xobjNode.dict, "Matrix"));
36731
- if (matArr && matArr.items.length >= 6) {
36732
- const mn = (idx, fb = 0) => {
36733
- const resolved = params.cosDoc.resolve(matArr.items[idx]);
36734
- return resolved?.kind === "number" ? resolved.value : fb;
36735
- };
36736
- const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
36737
- nextCtm = multiplyMatrices(formMat, nextCtm);
36738
- }
36739
- let nextClip = st.clipRect ? [...st.clipRect] : void 0;
36740
- const bboxArr = params.cosDoc.resolveArray(dictGet(xobjNode.dict, "BBox"));
36741
- if (bboxArr && bboxArr.items.length >= 4) {
36742
- const bn = (idx, fb = 0) => {
36743
- const resolved = params.cosDoc.resolve(bboxArr.items[idx]);
36744
- return resolved?.kind === "number" ? resolved.value : fb;
36745
- };
36746
- const bx0 = bn(0, 0), by0 = bn(1, 0), bx1 = bn(2, 0), by1 = bn(3, 0);
36747
- const pts = [
36748
- transformPoint(nextCtm, bx0, by0),
36749
- transformPoint(nextCtm, bx1, by0),
36750
- transformPoint(nextCtm, bx0, by1),
36751
- transformPoint(nextCtm, bx1, by1)
36752
- ];
36753
- const fMinX = Math.min(...pts.map((p) => p[0]));
36754
- const fMinY = Math.min(...pts.map((p) => p[1]));
36755
- const fMaxX = Math.max(...pts.map((p) => p[0]));
36756
- const fMaxY = Math.max(...pts.map((p) => p[1]));
36757
- if (fMaxX > fMinX && fMaxY > fMinY) {
36758
- nextClip = nextClip ? [
36759
- Math.max(nextClip[0], fMinX),
36760
- Math.max(nextClip[1], fMinY),
36761
- Math.min(nextClip[2], fMaxX),
36762
- Math.min(nextClip[3], fMaxY)
36763
- ] : [fMinX, fMinY, fMaxX, fMaxY];
36764
- }
36765
- }
36766
- stateStack.push({ ...st, ctm: nextCtm, ...nextClip ? { clipRect: nextClip } : {} });
36767
- walkNodes(formNodes, mcid, actualText, formResDict, formFonts, depth + 1);
36768
- if (stateStack.length > 1) stateStack.pop();
36844
+ paintForm(xobjNode, activeResources, activeFonts, depth, mcid, actualText);
36769
36845
  }
36770
36846
  }
36771
36847
  }
@@ -36984,7 +37060,7 @@ function evaluateContentStreamToDisplayList(params) {
36984
37060
  st.textRenderMode = cmd.mode;
36985
37061
  break;
36986
37062
  case "state-op":
36987
- applyStateOperator(st, cmd.operator, cmd.operands, activeResources);
37063
+ applyStateOperator(st, cmd.operator, cmd.operands, activeResources, activeFonts, depth);
36988
37064
  break;
36989
37065
  case "show-text":
36990
37066
  emitTokenBytes(cmd.token.bytes);
@@ -40593,6 +40669,30 @@ function paintOperations(displayList) {
40593
40669
  ...displayList.glyphs.map((value) => ({ kind: "glyph", value }))
40594
40670
  ];
40595
40671
  }
40672
+ function renderSoftMask(mask, displayList, scale) {
40673
+ const bitmap = renderDisplayListToBitmap({
40674
+ ...displayList,
40675
+ rotation: 0,
40676
+ glyphs: [],
40677
+ paths: [],
40678
+ images: [],
40679
+ operations: mask.operations
40680
+ }, { scale, transparent: true });
40681
+ const { data } = bitmap;
40682
+ for (let i = 0; i < data.length; i += 4) {
40683
+ let value = data[i + 3];
40684
+ if (mask.subtype === "Luminosity") {
40685
+ const alpha = value / 255;
40686
+ const r = Math.round(data[i] * alpha + mask.backdrop.r * 255 * (1 - alpha));
40687
+ const g = Math.round(data[i + 1] * alpha + mask.backdrop.g * 255 * (1 - alpha));
40688
+ const b = Math.round(data[i + 2] * alpha + mask.backdrop.b * 255 * (1 - alpha));
40689
+ value = Math.round(pdfLum(r, g, b));
40690
+ }
40691
+ data[i] = data[i + 1] = data[i + 2] = 0;
40692
+ data[i + 3] = mask.transferMap?.[value] ?? value;
40693
+ }
40694
+ return bitmap;
40695
+ }
40596
40696
  function renderDisplayListToBitmap(displayList, options = {}) {
40597
40697
  const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
40598
40698
  const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
@@ -40616,10 +40716,28 @@ function renderDisplayListToBitmap(displayList, options = {}) {
40616
40716
  const aaTxt = options.antialiasText !== false;
40617
40717
  let cachedClips;
40618
40718
  let cachedClipMask;
40719
+ let cachedSoftMask;
40720
+ let cachedSoftMaskPixels;
40619
40721
  const imageClipMasks = /* @__PURE__ */ new Map();
40620
40722
  for (const original of paintOperations(displayList)) {
40621
40723
  if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
40622
- const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
40724
+ let operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
40725
+ if (operation.kind === "group") {
40726
+ const group = operation.value;
40727
+ const bitmap = renderDisplayListToBitmap({ ...displayList, rotation: 0, operations: group.operations }, { ...options, scale, dpiX: void 0, dpiY: void 0, cropRect: void 0, transparent: true });
40728
+ for (let i = 3; i < bitmap.data.length; i += 4) bitmap.data[i] = Math.round(bitmap.data[i] * group.alpha);
40729
+ operation = { kind: "image", value: {
40730
+ name: "TransparencyGroup",
40731
+ width: bitmap.width,
40732
+ height: bitmap.height,
40733
+ decodedRgba: bitmap.data,
40734
+ matrix: [bitmap.width / scale, 0, 0, bitmap.height / scale, 0, displayList.height - bitmap.height / scale],
40735
+ colorSpace: "DeviceRGB",
40736
+ bitsPerComponent: 8,
40737
+ blendMode: group.blendMode,
40738
+ clipRect: group.clipRect
40739
+ } };
40740
+ }
40623
40741
  const clips = original.value.clipPaths;
40624
40742
  let clipMask = clips && clips === cachedClips ? cachedClipMask : void 0;
40625
40743
  if (clips && !clipMask) {
@@ -40633,6 +40751,19 @@ function renderDisplayListToBitmap(displayList, options = {}) {
40633
40751
  cachedClips = clips;
40634
40752
  cachedClipMask = clipMask;
40635
40753
  }
40754
+ const softMask = original.value.softMask;
40755
+ if (softMask) {
40756
+ if (softMask !== cachedSoftMask) {
40757
+ cachedSoftMaskPixels = renderSoftMask(softMask, displayList, scale).data;
40758
+ cachedSoftMask = softMask;
40759
+ }
40760
+ if (clipMask) {
40761
+ clipMask = clipMask.slice();
40762
+ for (let i = 3; i < clipMask.length; i += 4) clipMask[i] = Math.round(clipMask[i] * cachedSoftMaskPixels[i] / 255);
40763
+ } else {
40764
+ clipMask = cachedSoftMaskPixels;
40765
+ }
40766
+ }
40636
40767
  const imageClips = original.value.clipImages;
40637
40768
  if (imageClips) {
40638
40769
  let imageMask = imageClipMasks.get(imageClips);
@@ -40814,6 +40945,27 @@ function renderDisplayListToPng(displayList, options = {}) {
40814
40945
  const bmp = renderDisplayListToBitmap(displayList, options);
40815
40946
  return encodeRgbaToPng(bmp.width, bmp.height, bmp.data);
40816
40947
  }
40948
+ function pdfBlendModeToCss(mode) {
40949
+ if (!mode || mode === "Normal" || mode === "Compatible") return void 0;
40950
+ const map = {
40951
+ Multiply: "multiply",
40952
+ Screen: "screen",
40953
+ Overlay: "overlay",
40954
+ Darken: "darken",
40955
+ Lighten: "lighten",
40956
+ ColorDodge: "color-dodge",
40957
+ ColorBurn: "color-burn",
40958
+ HardLight: "hard-light",
40959
+ SoftLight: "soft-light",
40960
+ Difference: "difference",
40961
+ Exclusion: "exclusion",
40962
+ Hue: "hue",
40963
+ Saturation: "saturation",
40964
+ Color: "color",
40965
+ Luminosity: "luminosity"
40966
+ };
40967
+ return map[mode];
40968
+ }
40817
40969
  function escapeXmlText(str) {
40818
40970
  let valid = "";
40819
40971
  for (const char of str) {
@@ -40875,44 +41027,74 @@ function renderDisplayListToSvg(displayList, options = {}) {
40875
41027
  parts.push(`<g transform="matrix(${a} ${b} ${-c} ${-d} ${e + c * displayList.height} ${f + d * displayList.height})">`);
40876
41028
  }
40877
41029
  let clipId = 0;
40878
- for (const original of paintOperations(displayList)) {
40879
- if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
40880
- const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
40881
- const clips = original.value.clipPaths ?? [];
40882
- const imageClips = original.value.clipImages ?? [];
40883
- for (const clip of clips) {
40884
- const { segments, fillRule } = "segments" in clip ? clip : { segments: clip, fillRule: "nonzero" };
40885
- const id = `text-clip-${clipId++}`;
40886
- const path = `<path d="${svgPathData(segments, displayList.height)}" clip-rule="${fillRule}"/>`;
40887
- parts.push(`<defs><clipPath id="${id}" clipPathUnits="userSpaceOnUse">${path}</clipPath></defs><g clip-path="url(#${id})">`);
40888
- }
40889
- for (const image of imageClips) {
40890
- const id = `image-clip-${clipId++}`;
40891
- parts.push(`<defs><mask id="${id}" maskUnits="userSpaceOnUse" x="0" y="0" width="${displayList.width}" height="${displayList.height}" style="mask-type:alpha">${svgImage(image, displayList.height)}</mask></defs><g mask="url(#${id})">`);
40892
- }
40893
- if (operation.kind === "path") {
40894
- const p = operation.value;
40895
- const pathData = svgPathData(p.segments, displayList.height);
40896
- if (pathData) {
40897
- const fill = p.fillColor ? `rgb(${Math.round(p.fillColor.r * 255)},${Math.round(p.fillColor.g * 255)},${Math.round(p.fillColor.b * 255)})` : "none";
40898
- const stroke = p.strokeColor ? `rgb(${Math.round(p.strokeColor.r * 255)},${Math.round(p.strokeColor.g * 255)},${Math.round(p.strokeColor.b * 255)})` : "none";
40899
- const fillRuleAttr = p.fillRule === "evenodd" ? ` fill-rule="evenodd"` : "";
40900
- const fillOpacityAttr = p.fillAlpha !== void 0 && p.fillAlpha < 1 ? ` fill-opacity="${p.fillAlpha}"` : "";
40901
- const strokeOpacityAttr = p.strokeAlpha !== void 0 && p.strokeAlpha < 1 ? ` stroke-opacity="${p.strokeAlpha}"` : "";
40902
- const strokeWidthAttr = p.strokeWidth <= 0 ? ` stroke-width="1" vector-effect="non-scaling-stroke"` : ` stroke-width="${p.strokeWidth}"`;
40903
- const lineCapAttr = p.lineCap === 1 ? ` stroke-linecap="round"` : p.lineCap === 2 ? ` stroke-linecap="square"` : "";
40904
- const lineJoinAttr = p.lineJoin === 1 ? ` stroke-linejoin="round"` : p.lineJoin === 2 ? ` stroke-linejoin="bevel"` : "";
40905
- const miterLimitAttr = p.miterLimit !== void 0 && p.miterLimit !== 10 ? ` stroke-miterlimit="${p.miterLimit}"` : "";
40906
- const dashArrayAttr = p.dashArray && p.dashArray.length > 0 ? ` stroke-dasharray="${p.dashArray.join(" ")}"` : "";
40907
- const dashOffsetAttr = p.dashPhase ? ` stroke-dashoffset="${p.dashPhase}"` : "";
40908
- const labelAttr = original.kind === "glyph" ? ` aria-label="${escapeXmlText(original.value.unicode)}"` : "";
40909
- parts.push(` <path${labelAttr} d="${pathData}" fill="${fill}"${fillRuleAttr}${fillOpacityAttr} stroke="${stroke}"${strokeOpacityAttr}${strokeWidthAttr}${lineCapAttr}${lineJoinAttr}${miterLimitAttr}${dashArrayAttr}${dashOffsetAttr}/>`);
41030
+ const softMaskIds = /* @__PURE__ */ new Map();
41031
+ const appendOperations = (operations) => {
41032
+ for (const original of operations) {
41033
+ if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
41034
+ const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
41035
+ const clips = original.value.clipPaths ?? [];
41036
+ const imageClips = original.value.clipImages ?? [];
41037
+ const blend = pdfBlendModeToCss(original.value.blendMode);
41038
+ if (blend) parts.push(`<g style="mix-blend-mode:${blend}">`);
41039
+ const softMask = original.value.softMask;
41040
+ if (softMask) {
41041
+ let id = softMaskIds.get(softMask);
41042
+ if (!id) {
41043
+ id = `soft-mask-${clipId++}`;
41044
+ softMaskIds.set(softMask, id);
41045
+ const bitmap = renderSoftMask(softMask, displayList, Math.max(scaleX, scaleY));
41046
+ const image = {
41047
+ name: id,
41048
+ width: bitmap.width,
41049
+ height: bitmap.height,
41050
+ decodedRgba: bitmap.data,
41051
+ matrix: [displayList.width, 0, 0, displayList.height, 0, 0],
41052
+ colorSpace: "DeviceGray",
41053
+ bitsPerComponent: 8
41054
+ };
41055
+ parts.push(`<defs><mask id="${id}" maskUnits="userSpaceOnUse" x="0" y="0" width="${displayList.width}" height="${displayList.height}" style="mask-type:alpha">${svgImage(image, displayList.height)}</mask></defs>`);
41056
+ }
41057
+ parts.push(`<g mask="url(#${id})">`);
40910
41058
  }
40911
- } else if (operation.kind === "image") {
40912
- parts.push(` ${svgImage(operation.value, displayList.height)}`);
41059
+ for (const clip of clips) {
41060
+ const { segments, fillRule } = "segments" in clip ? clip : { segments: clip, fillRule: "nonzero" };
41061
+ const id = `text-clip-${clipId++}`;
41062
+ const path = `<path d="${svgPathData(segments, displayList.height)}" clip-rule="${fillRule}"/>`;
41063
+ parts.push(`<defs><clipPath id="${id}" clipPathUnits="userSpaceOnUse">${path}</clipPath></defs><g clip-path="url(#${id})">`);
41064
+ }
41065
+ for (const image of imageClips) {
41066
+ const id = `image-clip-${clipId++}`;
41067
+ parts.push(`<defs><mask id="${id}" maskUnits="userSpaceOnUse" x="0" y="0" width="${displayList.width}" height="${displayList.height}" style="mask-type:alpha">${svgImage(image, displayList.height)}</mask></defs><g mask="url(#${id})">`);
41068
+ }
41069
+ if (operation.kind === "group") {
41070
+ parts.push(`<g opacity="${operation.value.alpha}" style="isolation:isolate">`);
41071
+ appendOperations(operation.value.operations);
41072
+ parts.push("</g>");
41073
+ } else if (operation.kind === "path") {
41074
+ const p = operation.value;
41075
+ const pathData = svgPathData(p.segments, displayList.height);
41076
+ if (pathData) {
41077
+ const fill = p.fillColor ? `rgb(${Math.round(p.fillColor.r * 255)},${Math.round(p.fillColor.g * 255)},${Math.round(p.fillColor.b * 255)})` : "none";
41078
+ const stroke = p.strokeColor ? `rgb(${Math.round(p.strokeColor.r * 255)},${Math.round(p.strokeColor.g * 255)},${Math.round(p.strokeColor.b * 255)})` : "none";
41079
+ const fillRuleAttr = p.fillRule === "evenodd" ? ` fill-rule="evenodd"` : "";
41080
+ const fillOpacityAttr = p.fillAlpha !== void 0 && p.fillAlpha < 1 ? ` fill-opacity="${p.fillAlpha}"` : "";
41081
+ const strokeOpacityAttr = p.strokeAlpha !== void 0 && p.strokeAlpha < 1 ? ` stroke-opacity="${p.strokeAlpha}"` : "";
41082
+ const strokeWidthAttr = p.strokeWidth <= 0 ? ` stroke-width="1" vector-effect="non-scaling-stroke"` : ` stroke-width="${p.strokeWidth}"`;
41083
+ const lineCapAttr = p.lineCap === 1 ? ` stroke-linecap="round"` : p.lineCap === 2 ? ` stroke-linecap="square"` : "";
41084
+ const lineJoinAttr = p.lineJoin === 1 ? ` stroke-linejoin="round"` : p.lineJoin === 2 ? ` stroke-linejoin="bevel"` : "";
41085
+ const miterLimitAttr = p.miterLimit !== void 0 && p.miterLimit !== 10 ? ` stroke-miterlimit="${p.miterLimit}"` : "";
41086
+ const dashArrayAttr = p.dashArray && p.dashArray.length > 0 ? ` stroke-dasharray="${p.dashArray.join(" ")}"` : "";
41087
+ const dashOffsetAttr = p.dashPhase ? ` stroke-dashoffset="${p.dashPhase}"` : "";
41088
+ const labelAttr = original.kind === "glyph" ? ` aria-label="${escapeXmlText(original.value.unicode)}"` : "";
41089
+ parts.push(` <path${labelAttr} d="${pathData}" fill="${fill}"${fillRuleAttr}${fillOpacityAttr} stroke="${stroke}"${strokeOpacityAttr}${strokeWidthAttr}${lineCapAttr}${lineJoinAttr}${miterLimitAttr}${dashArrayAttr}${dashOffsetAttr}/>`);
41090
+ }
41091
+ } else if (operation.kind === "image") {
41092
+ parts.push(` ${svgImage(operation.value, displayList.height)}`);
41093
+ }
41094
+ for (let i = 0; i < clips.length + imageClips.length + (softMask ? 1 : 0) + (blend ? 1 : 0); i++) parts.push("</g>");
40913
41095
  }
40914
- for (let i = 0; i < clips.length + imageClips.length; i++) parts.push("</g>");
40915
- }
41096
+ };
41097
+ appendOperations(paintOperations(displayList));
40916
41098
  if (displayList.rotation) parts.push("</g>");
40917
41099
  parts.push("</svg>\n");
40918
41100
  return parts.join("\n");