@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
@@ -35762,14 +35762,18 @@ function evaluateContentStreamToDisplayList(params) {
35762
35762
  const paths = [];
35763
35763
  const images = [];
35764
35764
  const operations = [];
35765
+ let capturedOperations;
35766
+ let insideSoftMask = false;
35765
35767
  const emit = (operation) => {
35766
- const { clipPaths, clipImages } = curState();
35767
- if (clipPaths || clipImages) operation = { ...operation, value: {
35768
+ const { clipPaths, clipImages, softMask } = curState();
35769
+ if (clipPaths || clipImages || softMask) operation = { ...operation, value: {
35768
35770
  ...operation.value,
35769
35771
  ...clipPaths ? { clipPaths } : {},
35770
- ...clipImages ? { clipImages } : {}
35772
+ ...clipImages ? { clipImages } : {},
35773
+ ...softMask ? { softMask } : {}
35771
35774
  } };
35772
- operations.push(operation);
35775
+ (capturedOperations ?? operations).push(operation);
35776
+ if (insideSoftMask) return;
35773
35777
  switch (operation.kind) {
35774
35778
  case "path":
35775
35779
  paths.push(operation.value);
@@ -35875,7 +35879,7 @@ function evaluateContentStreamToDisplayList(params) {
35875
35879
  );
35876
35880
  return { r: kClamp(r), g: kClamp(g), b: kClamp(b) };
35877
35881
  };
35878
- const applyStateOperator = (st, operator, ops, activeResources) => {
35882
+ const applyStateOperator = (st, operator, ops, activeResources, activeFonts, depth) => {
35879
35883
  const num = (i, fb = 0) => ops[i]?.kind === "number" ? ops[i].value : fb;
35880
35884
  if (operator === "cm") {
35881
35885
  const m = [num(0, 1), num(1, 0), num(2, 0), num(3, 1), num(4, 0), num(5, 0)];
@@ -35958,6 +35962,46 @@ function evaluateContentStreamToDisplayList(params) {
35958
35962
  const extDict = params.cosDoc.resolveDict(dictGet(activeResources, "ExtGState"));
35959
35963
  const gsDict = extDict ? params.cosDoc.resolveDict(dictGet(extDict, ops[0].decoded)) : void 0;
35960
35964
  if (gsDict) {
35965
+ const mask = params.cosDoc.resolve(dictGet(gsDict, "SMask"));
35966
+ if (mask?.kind === "name" && mask.decoded === "None") {
35967
+ st.softMask = void 0;
35968
+ } else if (mask?.kind === "dict") {
35969
+ const subtype = params.cosDoc.resolve(dictGet(mask, "S"));
35970
+ const form = params.cosDoc.resolve(dictGet(mask, "G"));
35971
+ if (form?.kind === "stream" && subtype?.kind === "name" && (subtype.decoded === "Alpha" || subtype.decoded === "Luminosity")) {
35972
+ if (depth >= 8) throw new PdfError("E_LIMIT", "Soft-mask nesting exceeds the form depth limit");
35973
+ const parentOperations = capturedOperations;
35974
+ const parentInsideSoftMask = insideSoftMask;
35975
+ const savedTextState = { pendingTextClip, hasTextClip, activeTm, activeTlm };
35976
+ const captured = [];
35977
+ capturedOperations = captured;
35978
+ insideSoftMask = true;
35979
+ const maskState = { ...st, softMask: void 0, fillAlpha: 1, strokeAlpha: 1, blendMode: "Normal" };
35980
+ delete maskState.clipPaths;
35981
+ delete maskState.clipImages;
35982
+ delete maskState.clipRect;
35983
+ stateStack.push(maskState);
35984
+ try {
35985
+ paintForm(form, activeResources, activeFonts, depth, void 0, void 0, true);
35986
+ } finally {
35987
+ stateStack.pop();
35988
+ capturedOperations = parentOperations;
35989
+ insideSoftMask = parentInsideSoftMask;
35990
+ ({ pendingTextClip, hasTextClip, activeTm, activeTlm } = savedTextState);
35991
+ }
35992
+ const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
35993
+ const colorSpace = group ? dictGet(group, "CS") : void 0;
35994
+ const bc = params.cosDoc.resolveArray(dictGet(mask, "BC"));
35995
+ const components = bc?.items.map((item) => {
35996
+ const value = params.cosDoc.resolve(item);
35997
+ return value?.kind === "number" ? value.value : 0;
35998
+ });
35999
+ const [r, g, b] = components ? convertColorSpaceComponentsToRgb(params.cosDoc, colorSpace, "DeviceRGB", components, activeResources) : [0, 0, 0];
36000
+ const transfer = params.cosDoc.resolve(dictGet(mask, "TR"));
36001
+ 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;
36002
+ st.softMask = { subtype: subtype.decoded, operations: captured, backdrop: { r, g, b }, transferMap };
36003
+ }
36004
+ }
35961
36005
  const bmNode = params.cosDoc.resolve(dictGet(gsDict, "BM"));
35962
36006
  if (bmNode?.kind === "name") {
35963
36007
  st.blendMode = bmNode.decoded === "Compatible" ? "Normal" : bmNode.decoded;
@@ -36044,6 +36088,84 @@ function evaluateContentStreamToDisplayList(params) {
36044
36088
  ] }], void 0, void 0, resources, activeFonts, depth);
36045
36089
  stateStack.pop();
36046
36090
  };
36091
+ const paintForm = (form, activeResources, activeFonts, depth, mcid, actualText, maskGroup = false) => {
36092
+ const st = curState();
36093
+ const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
36094
+ const groupType = group ? params.cosDoc.resolve(dictGet(group, "S")) : void 0;
36095
+ const isolated = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
36096
+ const isolate = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && isolated?.kind === "boolean" && isolated.value;
36097
+ const formStreamBytes = params.cosDoc.decodeStream(form);
36098
+ const formNodes = parseContentStream(formStreamBytes);
36099
+ const formResDict = params.cosDoc.resolveDict(dictGet(form.dict, "Resources")) ?? activeResources;
36100
+ const formFonts = new Map(activeFonts);
36101
+ for (const [k, v] of resolvePageFonts(params.cosDoc, formResDict).entries()) {
36102
+ formFonts.set(k, v);
36103
+ }
36104
+ let nextCtm = [...st.ctm];
36105
+ const matArr = params.cosDoc.resolveArray(dictGet(form.dict, "Matrix"));
36106
+ if (matArr && matArr.items.length >= 6) {
36107
+ const mn = (idx, fb = 0) => {
36108
+ const resolved = params.cosDoc.resolve(matArr.items[idx]);
36109
+ return resolved?.kind === "number" ? resolved.value : fb;
36110
+ };
36111
+ const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
36112
+ nextCtm = multiplyMatrices(formMat, nextCtm);
36113
+ }
36114
+ let nextClip = !isolate && st.clipRect ? [...st.clipRect] : void 0;
36115
+ let nextClipPaths = isolate ? void 0 : st.clipPaths;
36116
+ const bboxArr = params.cosDoc.resolveArray(dictGet(form.dict, "BBox"));
36117
+ if (bboxArr && bboxArr.items.length >= 4) {
36118
+ const bn = (idx, fb = 0) => {
36119
+ const resolved = params.cosDoc.resolve(bboxArr.items[idx]);
36120
+ return resolved?.kind === "number" ? resolved.value : fb;
36121
+ };
36122
+ const bx0 = bn(0, 0), by0 = bn(1, 0), bx1 = bn(2, 0), by1 = bn(3, 0);
36123
+ const pts = [
36124
+ transformPoint(nextCtm, bx0, by0),
36125
+ transformPoint(nextCtm, bx1, by0),
36126
+ transformPoint(nextCtm, bx1, by1),
36127
+ transformPoint(nextCtm, bx0, by1)
36128
+ ];
36129
+ nextClipPaths = [...nextClipPaths ?? [], [
36130
+ ...pts.map(([x, y], index) => ({ kind: index === 0 ? "move" : "line", x, y })),
36131
+ { kind: "close" }
36132
+ ]];
36133
+ const fMinX = Math.min(...pts.map((p) => p[0]));
36134
+ const fMinY = Math.min(...pts.map((p) => p[1]));
36135
+ const fMaxX = Math.max(...pts.map((p) => p[0]));
36136
+ const fMaxY = Math.max(...pts.map((p) => p[1]));
36137
+ if (fMaxX > fMinX && fMaxY > fMinY) {
36138
+ nextClip = nextClip ? [
36139
+ Math.max(nextClip[0], fMinX),
36140
+ Math.max(nextClip[1], fMinY),
36141
+ Math.min(nextClip[2], fMaxX),
36142
+ Math.min(nextClip[3], fMaxY)
36143
+ ] : [fMinX, fMinY, fMaxX, fMaxY];
36144
+ }
36145
+ }
36146
+ const parentOperations = capturedOperations;
36147
+ const children = [];
36148
+ const nextState = { ...st, ctm: nextCtm };
36149
+ if (isolate) {
36150
+ capturedOperations = children;
36151
+ nextState.fillAlpha = nextState.strokeAlpha = 1;
36152
+ nextState.blendMode = "Normal";
36153
+ nextState.softMask = void 0;
36154
+ delete nextState.clipImages;
36155
+ delete nextState.clipPaths;
36156
+ delete nextState.clipRect;
36157
+ }
36158
+ if (nextClip) nextState.clipRect = nextClip;
36159
+ if (nextClipPaths) nextState.clipPaths = nextClipPaths;
36160
+ stateStack.push(nextState);
36161
+ try {
36162
+ walkNodes(formNodes, mcid, actualText, formResDict, formFonts, depth + 1);
36163
+ } finally {
36164
+ stateStack.pop();
36165
+ capturedOperations = parentOperations;
36166
+ }
36167
+ if (isolate) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
36168
+ };
36047
36169
  const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
36048
36170
  for (const node of nodes) {
36049
36171
  switch (node.kind) {
@@ -36088,7 +36210,7 @@ function evaluateContentStreamToDisplayList(params) {
36088
36210
  break;
36089
36211
  }
36090
36212
  case "state-op": {
36091
- applyStateOperator(curState(), node.operator, node.operands, activeResources);
36213
+ applyStateOperator(curState(), node.operator, node.operands, activeResources, activeFonts, depth);
36092
36214
  break;
36093
36215
  }
36094
36216
  case "path-op": {
@@ -36374,53 +36496,7 @@ function evaluateContentStreamToDisplayList(params) {
36374
36496
  ...st.clipRect ? { clipRect: [...st.clipRect] } : {}
36375
36497
  }, patternMask, activeResources, activeFonts, depth);
36376
36498
  } else if (sub === "Form" && depth < 8) {
36377
- const formStreamBytes = params.cosDoc.decodeStream(xobjNode);
36378
- const formNodes = parseContentStream(formStreamBytes);
36379
- const formResDict = params.cosDoc.resolveDict(dictGet(xobjNode.dict, "Resources")) ?? activeResources;
36380
- const formFonts = new Map(activeFonts);
36381
- for (const [k, v] of resolvePageFonts(params.cosDoc, formResDict).entries()) {
36382
- formFonts.set(k, v);
36383
- }
36384
- let nextCtm = [...st.ctm];
36385
- const matArr = params.cosDoc.resolveArray(dictGet(xobjNode.dict, "Matrix"));
36386
- if (matArr && matArr.items.length >= 6) {
36387
- const mn = (idx, fb = 0) => {
36388
- const resolved = params.cosDoc.resolve(matArr.items[idx]);
36389
- return resolved?.kind === "number" ? resolved.value : fb;
36390
- };
36391
- const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
36392
- nextCtm = multiplyMatrices(formMat, nextCtm);
36393
- }
36394
- let nextClip = st.clipRect ? [...st.clipRect] : void 0;
36395
- const bboxArr = params.cosDoc.resolveArray(dictGet(xobjNode.dict, "BBox"));
36396
- if (bboxArr && bboxArr.items.length >= 4) {
36397
- const bn = (idx, fb = 0) => {
36398
- const resolved = params.cosDoc.resolve(bboxArr.items[idx]);
36399
- return resolved?.kind === "number" ? resolved.value : fb;
36400
- };
36401
- const bx0 = bn(0, 0), by0 = bn(1, 0), bx1 = bn(2, 0), by1 = bn(3, 0);
36402
- const pts = [
36403
- transformPoint(nextCtm, bx0, by0),
36404
- transformPoint(nextCtm, bx1, by0),
36405
- transformPoint(nextCtm, bx0, by1),
36406
- transformPoint(nextCtm, bx1, by1)
36407
- ];
36408
- const fMinX = Math.min(...pts.map((p) => p[0]));
36409
- const fMinY = Math.min(...pts.map((p) => p[1]));
36410
- const fMaxX = Math.max(...pts.map((p) => p[0]));
36411
- const fMaxY = Math.max(...pts.map((p) => p[1]));
36412
- if (fMaxX > fMinX && fMaxY > fMinY) {
36413
- nextClip = nextClip ? [
36414
- Math.max(nextClip[0], fMinX),
36415
- Math.max(nextClip[1], fMinY),
36416
- Math.min(nextClip[2], fMaxX),
36417
- Math.min(nextClip[3], fMaxY)
36418
- ] : [fMinX, fMinY, fMaxX, fMaxY];
36419
- }
36420
- }
36421
- stateStack.push({ ...st, ctm: nextCtm, ...nextClip ? { clipRect: nextClip } : {} });
36422
- walkNodes(formNodes, mcid, actualText, formResDict, formFonts, depth + 1);
36423
- if (stateStack.length > 1) stateStack.pop();
36499
+ paintForm(xobjNode, activeResources, activeFonts, depth, mcid, actualText);
36424
36500
  }
36425
36501
  }
36426
36502
  }
@@ -36639,7 +36715,7 @@ function evaluateContentStreamToDisplayList(params) {
36639
36715
  st.textRenderMode = cmd.mode;
36640
36716
  break;
36641
36717
  case "state-op":
36642
- applyStateOperator(st, cmd.operator, cmd.operands, activeResources);
36718
+ applyStateOperator(st, cmd.operator, cmd.operands, activeResources, activeFonts, depth);
36643
36719
  break;
36644
36720
  case "show-text":
36645
36721
  emitTokenBytes(cmd.token.bytes);
@@ -40248,6 +40324,30 @@ function paintOperations(displayList) {
40248
40324
  ...displayList.glyphs.map((value) => ({ kind: "glyph", value }))
40249
40325
  ];
40250
40326
  }
40327
+ function renderSoftMask(mask, displayList, scale) {
40328
+ const bitmap = renderDisplayListToBitmap({
40329
+ ...displayList,
40330
+ rotation: 0,
40331
+ glyphs: [],
40332
+ paths: [],
40333
+ images: [],
40334
+ operations: mask.operations
40335
+ }, { scale, transparent: true });
40336
+ const { data } = bitmap;
40337
+ for (let i = 0; i < data.length; i += 4) {
40338
+ let value = data[i + 3];
40339
+ if (mask.subtype === "Luminosity") {
40340
+ const alpha = value / 255;
40341
+ const r = Math.round(data[i] * alpha + mask.backdrop.r * 255 * (1 - alpha));
40342
+ const g = Math.round(data[i + 1] * alpha + mask.backdrop.g * 255 * (1 - alpha));
40343
+ const b = Math.round(data[i + 2] * alpha + mask.backdrop.b * 255 * (1 - alpha));
40344
+ value = Math.round(pdfLum(r, g, b));
40345
+ }
40346
+ data[i] = data[i + 1] = data[i + 2] = 0;
40347
+ data[i + 3] = mask.transferMap?.[value] ?? value;
40348
+ }
40349
+ return bitmap;
40350
+ }
40251
40351
  function renderDisplayListToBitmap(displayList, options = {}) {
40252
40352
  const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
40253
40353
  const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
@@ -40271,10 +40371,28 @@ function renderDisplayListToBitmap(displayList, options = {}) {
40271
40371
  const aaTxt = options.antialiasText !== false;
40272
40372
  let cachedClips;
40273
40373
  let cachedClipMask;
40374
+ let cachedSoftMask;
40375
+ let cachedSoftMaskPixels;
40274
40376
  const imageClipMasks = /* @__PURE__ */ new Map();
40275
40377
  for (const original of paintOperations(displayList)) {
40276
40378
  if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
40277
- const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
40379
+ let operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
40380
+ if (operation.kind === "group") {
40381
+ const group = operation.value;
40382
+ const bitmap = renderDisplayListToBitmap({ ...displayList, rotation: 0, operations: group.operations }, { ...options, scale, dpiX: void 0, dpiY: void 0, cropRect: void 0, transparent: true });
40383
+ for (let i = 3; i < bitmap.data.length; i += 4) bitmap.data[i] = Math.round(bitmap.data[i] * group.alpha);
40384
+ operation = { kind: "image", value: {
40385
+ name: "TransparencyGroup",
40386
+ width: bitmap.width,
40387
+ height: bitmap.height,
40388
+ decodedRgba: bitmap.data,
40389
+ matrix: [bitmap.width / scale, 0, 0, bitmap.height / scale, 0, displayList.height - bitmap.height / scale],
40390
+ colorSpace: "DeviceRGB",
40391
+ bitsPerComponent: 8,
40392
+ blendMode: group.blendMode,
40393
+ clipRect: group.clipRect
40394
+ } };
40395
+ }
40278
40396
  const clips = original.value.clipPaths;
40279
40397
  let clipMask = clips && clips === cachedClips ? cachedClipMask : void 0;
40280
40398
  if (clips && !clipMask) {
@@ -40288,6 +40406,19 @@ function renderDisplayListToBitmap(displayList, options = {}) {
40288
40406
  cachedClips = clips;
40289
40407
  cachedClipMask = clipMask;
40290
40408
  }
40409
+ const softMask = original.value.softMask;
40410
+ if (softMask) {
40411
+ if (softMask !== cachedSoftMask) {
40412
+ cachedSoftMaskPixels = renderSoftMask(softMask, displayList, scale).data;
40413
+ cachedSoftMask = softMask;
40414
+ }
40415
+ if (clipMask) {
40416
+ clipMask = clipMask.slice();
40417
+ for (let i = 3; i < clipMask.length; i += 4) clipMask[i] = Math.round(clipMask[i] * cachedSoftMaskPixels[i] / 255);
40418
+ } else {
40419
+ clipMask = cachedSoftMaskPixels;
40420
+ }
40421
+ }
40291
40422
  const imageClips = original.value.clipImages;
40292
40423
  if (imageClips) {
40293
40424
  let imageMask = imageClipMasks.get(imageClips);
@@ -40469,6 +40600,27 @@ function renderDisplayListToPng(displayList, options = {}) {
40469
40600
  const bmp = renderDisplayListToBitmap(displayList, options);
40470
40601
  return encodeRgbaToPng(bmp.width, bmp.height, bmp.data);
40471
40602
  }
40603
+ function pdfBlendModeToCss(mode) {
40604
+ if (!mode || mode === "Normal" || mode === "Compatible") return void 0;
40605
+ const map = {
40606
+ Multiply: "multiply",
40607
+ Screen: "screen",
40608
+ Overlay: "overlay",
40609
+ Darken: "darken",
40610
+ Lighten: "lighten",
40611
+ ColorDodge: "color-dodge",
40612
+ ColorBurn: "color-burn",
40613
+ HardLight: "hard-light",
40614
+ SoftLight: "soft-light",
40615
+ Difference: "difference",
40616
+ Exclusion: "exclusion",
40617
+ Hue: "hue",
40618
+ Saturation: "saturation",
40619
+ Color: "color",
40620
+ Luminosity: "luminosity"
40621
+ };
40622
+ return map[mode];
40623
+ }
40472
40624
  function escapeXmlText(str) {
40473
40625
  let valid = "";
40474
40626
  for (const char of str) {
@@ -40530,44 +40682,74 @@ function renderDisplayListToSvg(displayList, options = {}) {
40530
40682
  parts.push(`<g transform="matrix(${a} ${b} ${-c} ${-d} ${e + c * displayList.height} ${f + d * displayList.height})">`);
40531
40683
  }
40532
40684
  let clipId = 0;
40533
- for (const original of paintOperations(displayList)) {
40534
- if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
40535
- const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
40536
- const clips = original.value.clipPaths ?? [];
40537
- const imageClips = original.value.clipImages ?? [];
40538
- for (const clip of clips) {
40539
- const { segments, fillRule } = "segments" in clip ? clip : { segments: clip, fillRule: "nonzero" };
40540
- const id = `text-clip-${clipId++}`;
40541
- const path = `<path d="${svgPathData(segments, displayList.height)}" clip-rule="${fillRule}"/>`;
40542
- parts.push(`<defs><clipPath id="${id}" clipPathUnits="userSpaceOnUse">${path}</clipPath></defs><g clip-path="url(#${id})">`);
40543
- }
40544
- for (const image of imageClips) {
40545
- const id = `image-clip-${clipId++}`;
40546
- 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})">`);
40547
- }
40548
- if (operation.kind === "path") {
40549
- const p = operation.value;
40550
- const pathData = svgPathData(p.segments, displayList.height);
40551
- if (pathData) {
40552
- const fill = p.fillColor ? `rgb(${Math.round(p.fillColor.r * 255)},${Math.round(p.fillColor.g * 255)},${Math.round(p.fillColor.b * 255)})` : "none";
40553
- const stroke = p.strokeColor ? `rgb(${Math.round(p.strokeColor.r * 255)},${Math.round(p.strokeColor.g * 255)},${Math.round(p.strokeColor.b * 255)})` : "none";
40554
- const fillRuleAttr = p.fillRule === "evenodd" ? ` fill-rule="evenodd"` : "";
40555
- const fillOpacityAttr = p.fillAlpha !== void 0 && p.fillAlpha < 1 ? ` fill-opacity="${p.fillAlpha}"` : "";
40556
- const strokeOpacityAttr = p.strokeAlpha !== void 0 && p.strokeAlpha < 1 ? ` stroke-opacity="${p.strokeAlpha}"` : "";
40557
- const strokeWidthAttr = p.strokeWidth <= 0 ? ` stroke-width="1" vector-effect="non-scaling-stroke"` : ` stroke-width="${p.strokeWidth}"`;
40558
- const lineCapAttr = p.lineCap === 1 ? ` stroke-linecap="round"` : p.lineCap === 2 ? ` stroke-linecap="square"` : "";
40559
- const lineJoinAttr = p.lineJoin === 1 ? ` stroke-linejoin="round"` : p.lineJoin === 2 ? ` stroke-linejoin="bevel"` : "";
40560
- const miterLimitAttr = p.miterLimit !== void 0 && p.miterLimit !== 10 ? ` stroke-miterlimit="${p.miterLimit}"` : "";
40561
- const dashArrayAttr = p.dashArray && p.dashArray.length > 0 ? ` stroke-dasharray="${p.dashArray.join(" ")}"` : "";
40562
- const dashOffsetAttr = p.dashPhase ? ` stroke-dashoffset="${p.dashPhase}"` : "";
40563
- const labelAttr = original.kind === "glyph" ? ` aria-label="${escapeXmlText(original.value.unicode)}"` : "";
40564
- parts.push(` <path${labelAttr} d="${pathData}" fill="${fill}"${fillRuleAttr}${fillOpacityAttr} stroke="${stroke}"${strokeOpacityAttr}${strokeWidthAttr}${lineCapAttr}${lineJoinAttr}${miterLimitAttr}${dashArrayAttr}${dashOffsetAttr}/>`);
40685
+ const softMaskIds = /* @__PURE__ */ new Map();
40686
+ const appendOperations = (operations) => {
40687
+ for (const original of operations) {
40688
+ if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
40689
+ const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
40690
+ const clips = original.value.clipPaths ?? [];
40691
+ const imageClips = original.value.clipImages ?? [];
40692
+ const blend = pdfBlendModeToCss(original.value.blendMode);
40693
+ if (blend) parts.push(`<g style="mix-blend-mode:${blend}">`);
40694
+ const softMask = original.value.softMask;
40695
+ if (softMask) {
40696
+ let id = softMaskIds.get(softMask);
40697
+ if (!id) {
40698
+ id = `soft-mask-${clipId++}`;
40699
+ softMaskIds.set(softMask, id);
40700
+ const bitmap = renderSoftMask(softMask, displayList, Math.max(scaleX, scaleY));
40701
+ const image = {
40702
+ name: id,
40703
+ width: bitmap.width,
40704
+ height: bitmap.height,
40705
+ decodedRgba: bitmap.data,
40706
+ matrix: [displayList.width, 0, 0, displayList.height, 0, 0],
40707
+ colorSpace: "DeviceGray",
40708
+ bitsPerComponent: 8
40709
+ };
40710
+ 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>`);
40711
+ }
40712
+ parts.push(`<g mask="url(#${id})">`);
40565
40713
  }
40566
- } else if (operation.kind === "image") {
40567
- parts.push(` ${svgImage(operation.value, displayList.height)}`);
40714
+ for (const clip of clips) {
40715
+ const { segments, fillRule } = "segments" in clip ? clip : { segments: clip, fillRule: "nonzero" };
40716
+ const id = `text-clip-${clipId++}`;
40717
+ const path = `<path d="${svgPathData(segments, displayList.height)}" clip-rule="${fillRule}"/>`;
40718
+ parts.push(`<defs><clipPath id="${id}" clipPathUnits="userSpaceOnUse">${path}</clipPath></defs><g clip-path="url(#${id})">`);
40719
+ }
40720
+ for (const image of imageClips) {
40721
+ const id = `image-clip-${clipId++}`;
40722
+ 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})">`);
40723
+ }
40724
+ if (operation.kind === "group") {
40725
+ parts.push(`<g opacity="${operation.value.alpha}" style="isolation:isolate">`);
40726
+ appendOperations(operation.value.operations);
40727
+ parts.push("</g>");
40728
+ } else if (operation.kind === "path") {
40729
+ const p = operation.value;
40730
+ const pathData = svgPathData(p.segments, displayList.height);
40731
+ if (pathData) {
40732
+ const fill = p.fillColor ? `rgb(${Math.round(p.fillColor.r * 255)},${Math.round(p.fillColor.g * 255)},${Math.round(p.fillColor.b * 255)})` : "none";
40733
+ const stroke = p.strokeColor ? `rgb(${Math.round(p.strokeColor.r * 255)},${Math.round(p.strokeColor.g * 255)},${Math.round(p.strokeColor.b * 255)})` : "none";
40734
+ const fillRuleAttr = p.fillRule === "evenodd" ? ` fill-rule="evenodd"` : "";
40735
+ const fillOpacityAttr = p.fillAlpha !== void 0 && p.fillAlpha < 1 ? ` fill-opacity="${p.fillAlpha}"` : "";
40736
+ const strokeOpacityAttr = p.strokeAlpha !== void 0 && p.strokeAlpha < 1 ? ` stroke-opacity="${p.strokeAlpha}"` : "";
40737
+ const strokeWidthAttr = p.strokeWidth <= 0 ? ` stroke-width="1" vector-effect="non-scaling-stroke"` : ` stroke-width="${p.strokeWidth}"`;
40738
+ const lineCapAttr = p.lineCap === 1 ? ` stroke-linecap="round"` : p.lineCap === 2 ? ` stroke-linecap="square"` : "";
40739
+ const lineJoinAttr = p.lineJoin === 1 ? ` stroke-linejoin="round"` : p.lineJoin === 2 ? ` stroke-linejoin="bevel"` : "";
40740
+ const miterLimitAttr = p.miterLimit !== void 0 && p.miterLimit !== 10 ? ` stroke-miterlimit="${p.miterLimit}"` : "";
40741
+ const dashArrayAttr = p.dashArray && p.dashArray.length > 0 ? ` stroke-dasharray="${p.dashArray.join(" ")}"` : "";
40742
+ const dashOffsetAttr = p.dashPhase ? ` stroke-dashoffset="${p.dashPhase}"` : "";
40743
+ const labelAttr = original.kind === "glyph" ? ` aria-label="${escapeXmlText(original.value.unicode)}"` : "";
40744
+ parts.push(` <path${labelAttr} d="${pathData}" fill="${fill}"${fillRuleAttr}${fillOpacityAttr} stroke="${stroke}"${strokeOpacityAttr}${strokeWidthAttr}${lineCapAttr}${lineJoinAttr}${miterLimitAttr}${dashArrayAttr}${dashOffsetAttr}/>`);
40745
+ }
40746
+ } else if (operation.kind === "image") {
40747
+ parts.push(` ${svgImage(operation.value, displayList.height)}`);
40748
+ }
40749
+ for (let i = 0; i < clips.length + imageClips.length + (softMask ? 1 : 0) + (blend ? 1 : 0); i++) parts.push("</g>");
40568
40750
  }
40569
- for (let i = 0; i < clips.length + imageClips.length; i++) parts.push("</g>");
40570
- }
40751
+ };
40752
+ appendOperations(paintOperations(displayList));
40571
40753
  if (displayList.rotation) parts.push("</g>");
40572
40754
  parts.push("</svg>\n");
40573
40755
  return parts.join("\n");