@poe-platform/safe-bash 0.1.765 → 0.1.767

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.
@@ -43838,10 +43838,11 @@ function evaluateContentStreamToDisplayList(params) {
43838
43838
  const patArr = params.cosDoc.resolveArray(dArr.items[0]);
43839
43839
  const phaseNode = params.cosDoc.resolve(dArr.items[1]);
43840
43840
  if (patArr) {
43841
- st.dashArray = patArr.items.map((it) => {
43841
+ const dashArray = patArr.items.map((it) => {
43842
43842
  const r = params.cosDoc.resolve(it);
43843
43843
  return r?.kind === "number" ? r.value : 0;
43844
- }).filter((n) => n > 0);
43844
+ }).filter((n) => n >= 0);
43845
+ st.dashArray = dashArray.some((value) => value > 0) ? dashArray : void 0;
43845
43846
  st.dashPhase = phaseNode?.kind === "number" ? phaseNode.value : 0;
43846
43847
  }
43847
43848
  }
@@ -43896,8 +43897,9 @@ function evaluateContentStreamToDisplayList(params) {
43896
43897
  const st = curState();
43897
43898
  const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
43898
43899
  const groupType = group ? params.cosDoc.resolve(dictGet(group, "S")) : void 0;
43899
- const isolated = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
43900
- const isolate = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && isolated?.kind === "boolean" && isolated.value;
43900
+ const isolation = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
43901
+ const isolated = isolation?.kind === "boolean" && isolation.value;
43902
+ const compositeGroup = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && (isolated || st.fillAlpha !== 1 || !!st.softMask || st.blendMode !== "Normal" && st.blendMode !== "Compatible");
43901
43903
  const formStreamBytes = params.cosDoc.decodeStream(form);
43902
43904
  const formNodes = parseContentStream(formStreamBytes);
43903
43905
  const formResDict = params.cosDoc.resolveDict(dictGet(form.dict, "Resources")) ?? activeResources;
@@ -43915,8 +43917,8 @@ function evaluateContentStreamToDisplayList(params) {
43915
43917
  const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
43916
43918
  nextCtm = multiplyMatrices(formMat, nextCtm);
43917
43919
  }
43918
- let nextClip = !isolate && st.clipRect ? [...st.clipRect] : void 0;
43919
- let nextClipPaths = isolate ? void 0 : st.clipPaths;
43920
+ let nextClip = !compositeGroup && st.clipRect ? [...st.clipRect] : void 0;
43921
+ let nextClipPaths = compositeGroup ? void 0 : st.clipPaths;
43920
43922
  const bboxArr = params.cosDoc.resolveArray(dictGet(form.dict, "BBox"));
43921
43923
  if (bboxArr && bboxArr.items.length >= 4) {
43922
43924
  const bn = (idx, fb = 0) => {
@@ -43950,7 +43952,7 @@ function evaluateContentStreamToDisplayList(params) {
43950
43952
  const parentOperations = capturedOperations;
43951
43953
  const children = [];
43952
43954
  const nextState = { ...st, ctm: nextCtm };
43953
- if (isolate) {
43955
+ if (compositeGroup) {
43954
43956
  capturedOperations = children;
43955
43957
  nextState.fillAlpha = nextState.strokeAlpha = 1;
43956
43958
  nextState.blendMode = "Normal";
@@ -43968,7 +43970,7 @@ function evaluateContentStreamToDisplayList(params) {
43968
43970
  stateStack.pop();
43969
43971
  capturedOperations = parentOperations;
43970
43972
  }
43971
- if (isolate) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
43973
+ if (compositeGroup) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
43972
43974
  };
43973
43975
  const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
43974
43976
  for (const node of nodes) {
@@ -46845,7 +46847,7 @@ function dashSubpath(path, pattern, phase) {
46845
46847
  while (remaining <= 0) {
46846
46848
  if (++steps > MAX_STROKE_VERTICES) throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
46847
46849
  if (index % 2 === 0) {
46848
- if (pattern[index] === 0) result.push({ points: [point, point], closed: false });
46850
+ if (pattern[index] === 0) result.push({ points: [point, point], closed: false, zeroLengthDash: true });
46849
46851
  flush();
46850
46852
  }
46851
46853
  index = (index + 1) % pattern.length;
@@ -46860,7 +46862,7 @@ function dashSubpath(path, pattern, phase) {
46860
46862
  throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
46861
46863
  }
46862
46864
  let distance = 0;
46863
- while (distance < length) {
46865
+ while (length - distance > 8 * Number.EPSILON * Math.max(1, length)) {
46864
46866
  const point = [start[0] + dx * distance / length, start[1] + dy * distance / length];
46865
46867
  advance(point);
46866
46868
  const step = Math.min(remaining, length - distance);
@@ -46975,7 +46977,15 @@ function strokeOutlines(paths, width, cap, join, miterLimit, dashArray = [], das
46975
46977
  let closed = subpath.closed;
46976
46978
  if (closed && points.length > 1 && Math.hypot(points[0][0] - points[points.length - 1][0], points[0][1] - points[points.length - 1][1]) <= 1e-14) points.pop();
46977
46979
  if (points.length < 3) closed = false;
46978
- if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
46980
+ if (points.length === 1 && cap === 2 && subpath.zeroLengthDash) {
46981
+ contour = [];
46982
+ const [x, y] = points[0];
46983
+ add(x - half, y - half);
46984
+ add(x + half, y - half);
46985
+ add(x + half, y + half);
46986
+ add(x - half, y + half);
46987
+ contours.push(contour);
46988
+ } else if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
46979
46989
  contour = [];
46980
46990
  const [x, y] = points[0];
46981
46991
  addCap([x, y], [x + 1, y]);
@@ -47767,7 +47777,7 @@ function prepareStroke(path, scale) {
47767
47777
  }
47768
47778
  return { matrix, width, dashArray, dashPhase };
47769
47779
  }
47770
- function strokeEdges(path, pageHeight, scale) {
47780
+ function strokeContours(path, pageHeight, scale) {
47771
47781
  const stroke = prepareStroke(path, scale);
47772
47782
  if (!stroke) return [];
47773
47783
  const inverse = inverseStrokeMatrix(stroke.matrix);
@@ -47791,7 +47801,7 @@ function strokeEdges(path, pageHeight, scale) {
47791
47801
  stroke.dashArray?.map((value) => Math.max(0, value * strokeScale)),
47792
47802
  stroke.dashPhase * strokeScale
47793
47803
  );
47794
- return pathsToEdges(contours.map((points) => ({ points: points.map(toScreen), closed: true })));
47804
+ return contours.map((points) => points.map(toScreen));
47795
47805
  }
47796
47806
  function pathsToEdges(paths, closeSubpaths = false) {
47797
47807
  const edges = [];
@@ -47938,6 +47948,9 @@ function renderSoftMask(mask, displayList, scale) {
47938
47948
  }
47939
47949
  return bitmap;
47940
47950
  }
47951
+ function hasBlendModes(operations) {
47952
+ return operations.some((operation) => !!operation.value.blendMode && operation.value.blendMode !== "Normal" && operation.value.blendMode !== "Compatible" || operation.kind === "group" && hasBlendModes(operation.value.operations));
47953
+ }
47941
47954
  function renderDisplayListToBitmap(displayList, options = {}) {
47942
47955
  const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
47943
47956
  const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
@@ -47946,11 +47959,13 @@ function renderDisplayListToBitmap(displayList, options = {}) {
47946
47959
  const width = Math.max(1, Math.round(displayList.width * scale));
47947
47960
  const height = Math.max(1, Math.round(displayList.height * scale));
47948
47961
  const rgba = new Uint8Array(width * height * 4);
47962
+ const deferBackground = !options.transparent && hasBlendModes(paintOperations(displayList));
47963
+ const transparent = options.transparent || deferBackground;
47949
47964
  const bg = options.background ?? { r: 1, g: 1, b: 1 };
47950
- const bgR = options.transparent ? 0 : Math.round(bg.r * 255);
47951
- const bgG = options.transparent ? 0 : Math.round(bg.g * 255);
47952
- const bgB = options.transparent ? 0 : Math.round(bg.b * 255);
47953
- const bgA = options.transparent ? 0 : 255;
47965
+ const bgR = transparent ? 0 : Math.round(bg.r * 255);
47966
+ const bgG = transparent ? 0 : Math.round(bg.g * 255);
47967
+ const bgB = transparent ? 0 : Math.round(bg.b * 255);
47968
+ const bgA = transparent ? 0 : 255;
47954
47969
  for (let i = 0; i < width * height; i++) {
47955
47970
  rgba[i * 4] = bgR;
47956
47971
  rgba[i * 4 + 1] = bgG;
@@ -48044,7 +48059,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
48044
48059
  if (path.strokeColor) {
48045
48060
  const rawSw = path.strokeWidth * scale * (path.strokeMatrix ? Math.hypot(path.strokeMatrix[0], path.strokeMatrix[1]) : 1);
48046
48061
  const strokeAlpha = options.thinLineMode === "shape" && rawSw < 1 ? (path.strokeAlpha ?? 1) * Math.max(0.25, rawSw) : path.strokeAlpha ?? 1;
48047
- const edges = strokeEdges(path, displayList.height, scale);
48062
+ const edges = pathsToEdges(strokeContours(path, displayList.height, scale).map((points) => ({ points, closed: true })));
48048
48063
  const clipScreen = path.clipRect ? [path.clipRect[0] * scale, (displayList.height - path.clipRect[3]) * scale, path.clipRect[2] * scale, (displayList.height - path.clipRect[1]) * scale] : void 0;
48049
48064
  fillEdgesScanline4x4(
48050
48065
  rgba,
@@ -48127,6 +48142,15 @@ function renderDisplayListToBitmap(displayList, options = {}) {
48127
48142
  }
48128
48143
  }
48129
48144
  }
48145
+ if (deferBackground) {
48146
+ for (let i = 0; i < rgba.length; i += 4) {
48147
+ const alpha = rgba[i + 3] / 255;
48148
+ rgba[i] = Math.round(rgba[i] * alpha + bg.r * 255 * (1 - alpha));
48149
+ rgba[i + 1] = Math.round(rgba[i + 1] * alpha + bg.g * 255 * (1 - alpha));
48150
+ rgba[i + 2] = Math.round(rgba[i + 2] * alpha + bg.b * 255 * (1 - alpha));
48151
+ rgba[i + 3] = 255;
48152
+ }
48153
+ }
48130
48154
  let result = { width, height, data: rgba };
48131
48155
  if (Math.abs(scaleX - scaleY) > 1e-6) {
48132
48156
  const targetW = Math.max(1, Math.round(displayList.width * scaleX));
@@ -43838,10 +43838,11 @@ function evaluateContentStreamToDisplayList(params) {
43838
43838
  const patArr = params.cosDoc.resolveArray(dArr.items[0]);
43839
43839
  const phaseNode = params.cosDoc.resolve(dArr.items[1]);
43840
43840
  if (patArr) {
43841
- st.dashArray = patArr.items.map((it) => {
43841
+ const dashArray = patArr.items.map((it) => {
43842
43842
  const r = params.cosDoc.resolve(it);
43843
43843
  return r?.kind === "number" ? r.value : 0;
43844
- }).filter((n) => n > 0);
43844
+ }).filter((n) => n >= 0);
43845
+ st.dashArray = dashArray.some((value) => value > 0) ? dashArray : void 0;
43845
43846
  st.dashPhase = phaseNode?.kind === "number" ? phaseNode.value : 0;
43846
43847
  }
43847
43848
  }
@@ -43896,8 +43897,9 @@ function evaluateContentStreamToDisplayList(params) {
43896
43897
  const st = curState();
43897
43898
  const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
43898
43899
  const groupType = group ? params.cosDoc.resolve(dictGet(group, "S")) : void 0;
43899
- const isolated = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
43900
- const isolate = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && isolated?.kind === "boolean" && isolated.value;
43900
+ const isolation = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
43901
+ const isolated = isolation?.kind === "boolean" && isolation.value;
43902
+ const compositeGroup = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && (isolated || st.fillAlpha !== 1 || !!st.softMask || st.blendMode !== "Normal" && st.blendMode !== "Compatible");
43901
43903
  const formStreamBytes = params.cosDoc.decodeStream(form);
43902
43904
  const formNodes = parseContentStream(formStreamBytes);
43903
43905
  const formResDict = params.cosDoc.resolveDict(dictGet(form.dict, "Resources")) ?? activeResources;
@@ -43915,8 +43917,8 @@ function evaluateContentStreamToDisplayList(params) {
43915
43917
  const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
43916
43918
  nextCtm = multiplyMatrices(formMat, nextCtm);
43917
43919
  }
43918
- let nextClip = !isolate && st.clipRect ? [...st.clipRect] : void 0;
43919
- let nextClipPaths = isolate ? void 0 : st.clipPaths;
43920
+ let nextClip = !compositeGroup && st.clipRect ? [...st.clipRect] : void 0;
43921
+ let nextClipPaths = compositeGroup ? void 0 : st.clipPaths;
43920
43922
  const bboxArr = params.cosDoc.resolveArray(dictGet(form.dict, "BBox"));
43921
43923
  if (bboxArr && bboxArr.items.length >= 4) {
43922
43924
  const bn = (idx, fb = 0) => {
@@ -43950,7 +43952,7 @@ function evaluateContentStreamToDisplayList(params) {
43950
43952
  const parentOperations = capturedOperations;
43951
43953
  const children = [];
43952
43954
  const nextState = { ...st, ctm: nextCtm };
43953
- if (isolate) {
43955
+ if (compositeGroup) {
43954
43956
  capturedOperations = children;
43955
43957
  nextState.fillAlpha = nextState.strokeAlpha = 1;
43956
43958
  nextState.blendMode = "Normal";
@@ -43968,7 +43970,7 @@ function evaluateContentStreamToDisplayList(params) {
43968
43970
  stateStack.pop();
43969
43971
  capturedOperations = parentOperations;
43970
43972
  }
43971
- if (isolate) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
43973
+ if (compositeGroup) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
43972
43974
  };
43973
43975
  const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
43974
43976
  for (const node of nodes) {
@@ -46845,7 +46847,7 @@ function dashSubpath(path, pattern, phase) {
46845
46847
  while (remaining <= 0) {
46846
46848
  if (++steps > MAX_STROKE_VERTICES) throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
46847
46849
  if (index % 2 === 0) {
46848
- if (pattern[index] === 0) result.push({ points: [point, point], closed: false });
46850
+ if (pattern[index] === 0) result.push({ points: [point, point], closed: false, zeroLengthDash: true });
46849
46851
  flush();
46850
46852
  }
46851
46853
  index = (index + 1) % pattern.length;
@@ -46860,7 +46862,7 @@ function dashSubpath(path, pattern, phase) {
46860
46862
  throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
46861
46863
  }
46862
46864
  let distance = 0;
46863
- while (distance < length) {
46865
+ while (length - distance > 8 * Number.EPSILON * Math.max(1, length)) {
46864
46866
  const point = [start[0] + dx * distance / length, start[1] + dy * distance / length];
46865
46867
  advance(point);
46866
46868
  const step = Math.min(remaining, length - distance);
@@ -46975,7 +46977,15 @@ function strokeOutlines(paths, width, cap, join, miterLimit, dashArray = [], das
46975
46977
  let closed = subpath.closed;
46976
46978
  if (closed && points.length > 1 && Math.hypot(points[0][0] - points[points.length - 1][0], points[0][1] - points[points.length - 1][1]) <= 1e-14) points.pop();
46977
46979
  if (points.length < 3) closed = false;
46978
- if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
46980
+ if (points.length === 1 && cap === 2 && subpath.zeroLengthDash) {
46981
+ contour = [];
46982
+ const [x, y] = points[0];
46983
+ add(x - half, y - half);
46984
+ add(x + half, y - half);
46985
+ add(x + half, y + half);
46986
+ add(x - half, y + half);
46987
+ contours.push(contour);
46988
+ } else if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
46979
46989
  contour = [];
46980
46990
  const [x, y] = points[0];
46981
46991
  addCap([x, y], [x + 1, y]);
@@ -47767,7 +47777,7 @@ function prepareStroke(path, scale) {
47767
47777
  }
47768
47778
  return { matrix, width, dashArray, dashPhase };
47769
47779
  }
47770
- function strokeEdges(path, pageHeight, scale) {
47780
+ function strokeContours(path, pageHeight, scale) {
47771
47781
  const stroke = prepareStroke(path, scale);
47772
47782
  if (!stroke) return [];
47773
47783
  const inverse = inverseStrokeMatrix(stroke.matrix);
@@ -47791,7 +47801,7 @@ function strokeEdges(path, pageHeight, scale) {
47791
47801
  stroke.dashArray?.map((value) => Math.max(0, value * strokeScale)),
47792
47802
  stroke.dashPhase * strokeScale
47793
47803
  );
47794
- return pathsToEdges(contours.map((points) => ({ points: points.map(toScreen), closed: true })));
47804
+ return contours.map((points) => points.map(toScreen));
47795
47805
  }
47796
47806
  function pathsToEdges(paths, closeSubpaths = false) {
47797
47807
  const edges = [];
@@ -47938,6 +47948,9 @@ function renderSoftMask(mask, displayList, scale) {
47938
47948
  }
47939
47949
  return bitmap;
47940
47950
  }
47951
+ function hasBlendModes(operations) {
47952
+ return operations.some((operation) => !!operation.value.blendMode && operation.value.blendMode !== "Normal" && operation.value.blendMode !== "Compatible" || operation.kind === "group" && hasBlendModes(operation.value.operations));
47953
+ }
47941
47954
  function renderDisplayListToBitmap(displayList, options = {}) {
47942
47955
  const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
47943
47956
  const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
@@ -47946,11 +47959,13 @@ function renderDisplayListToBitmap(displayList, options = {}) {
47946
47959
  const width = Math.max(1, Math.round(displayList.width * scale));
47947
47960
  const height = Math.max(1, Math.round(displayList.height * scale));
47948
47961
  const rgba = new Uint8Array(width * height * 4);
47962
+ const deferBackground = !options.transparent && hasBlendModes(paintOperations(displayList));
47963
+ const transparent = options.transparent || deferBackground;
47949
47964
  const bg = options.background ?? { r: 1, g: 1, b: 1 };
47950
- const bgR = options.transparent ? 0 : Math.round(bg.r * 255);
47951
- const bgG = options.transparent ? 0 : Math.round(bg.g * 255);
47952
- const bgB = options.transparent ? 0 : Math.round(bg.b * 255);
47953
- const bgA = options.transparent ? 0 : 255;
47965
+ const bgR = transparent ? 0 : Math.round(bg.r * 255);
47966
+ const bgG = transparent ? 0 : Math.round(bg.g * 255);
47967
+ const bgB = transparent ? 0 : Math.round(bg.b * 255);
47968
+ const bgA = transparent ? 0 : 255;
47954
47969
  for (let i = 0; i < width * height; i++) {
47955
47970
  rgba[i * 4] = bgR;
47956
47971
  rgba[i * 4 + 1] = bgG;
@@ -48044,7 +48059,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
48044
48059
  if (path.strokeColor) {
48045
48060
  const rawSw = path.strokeWidth * scale * (path.strokeMatrix ? Math.hypot(path.strokeMatrix[0], path.strokeMatrix[1]) : 1);
48046
48061
  const strokeAlpha = options.thinLineMode === "shape" && rawSw < 1 ? (path.strokeAlpha ?? 1) * Math.max(0.25, rawSw) : path.strokeAlpha ?? 1;
48047
- const edges = strokeEdges(path, displayList.height, scale);
48062
+ const edges = pathsToEdges(strokeContours(path, displayList.height, scale).map((points) => ({ points, closed: true })));
48048
48063
  const clipScreen = path.clipRect ? [path.clipRect[0] * scale, (displayList.height - path.clipRect[3]) * scale, path.clipRect[2] * scale, (displayList.height - path.clipRect[1]) * scale] : void 0;
48049
48064
  fillEdgesScanline4x4(
48050
48065
  rgba,
@@ -48127,6 +48142,15 @@ function renderDisplayListToBitmap(displayList, options = {}) {
48127
48142
  }
48128
48143
  }
48129
48144
  }
48145
+ if (deferBackground) {
48146
+ for (let i = 0; i < rgba.length; i += 4) {
48147
+ const alpha = rgba[i + 3] / 255;
48148
+ rgba[i] = Math.round(rgba[i] * alpha + bg.r * 255 * (1 - alpha));
48149
+ rgba[i + 1] = Math.round(rgba[i + 1] * alpha + bg.g * 255 * (1 - alpha));
48150
+ rgba[i + 2] = Math.round(rgba[i + 2] * alpha + bg.b * 255 * (1 - alpha));
48151
+ rgba[i + 3] = 255;
48152
+ }
48153
+ }
48130
48154
  let result = { width, height, data: rgba };
48131
48155
  if (Math.abs(scaleX - scaleY) > 1e-6) {
48132
48156
  const targetW = Math.max(1, Math.round(displayList.width * scaleX));
@@ -294,7 +294,7 @@ export type PdfPaintOperation = {
294
294
  readonly kind: "group";
295
295
  readonly value: PdfPaintGroup;
296
296
  };
297
- /** An isolated transparency Form, composited after painting its children. */
297
+ /** A transparency Form, composited after painting its children. */
298
298
  export interface PdfPaintGroup {
299
299
  readonly operations: readonly PdfPaintOperation[];
300
300
  readonly alpha: number;
@@ -36552,10 +36552,11 @@ function evaluateContentStreamToDisplayList(params) {
36552
36552
  const patArr = params.cosDoc.resolveArray(dArr.items[0]);
36553
36553
  const phaseNode = params.cosDoc.resolve(dArr.items[1]);
36554
36554
  if (patArr) {
36555
- st.dashArray = patArr.items.map((it) => {
36555
+ const dashArray = patArr.items.map((it) => {
36556
36556
  const r = params.cosDoc.resolve(it);
36557
36557
  return r?.kind === "number" ? r.value : 0;
36558
- }).filter((n) => n > 0);
36558
+ }).filter((n) => n >= 0);
36559
+ st.dashArray = dashArray.some((value) => value > 0) ? dashArray : void 0;
36559
36560
  st.dashPhase = phaseNode?.kind === "number" ? phaseNode.value : 0;
36560
36561
  }
36561
36562
  }
@@ -36610,8 +36611,9 @@ function evaluateContentStreamToDisplayList(params) {
36610
36611
  const st = curState();
36611
36612
  const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
36612
36613
  const groupType = group ? params.cosDoc.resolve(dictGet(group, "S")) : void 0;
36613
- const isolated = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
36614
- const isolate = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && isolated?.kind === "boolean" && isolated.value;
36614
+ const isolation = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
36615
+ const isolated = isolation?.kind === "boolean" && isolation.value;
36616
+ const compositeGroup = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && (isolated || st.fillAlpha !== 1 || !!st.softMask || st.blendMode !== "Normal" && st.blendMode !== "Compatible");
36615
36617
  const formStreamBytes = params.cosDoc.decodeStream(form);
36616
36618
  const formNodes = parseContentStream(formStreamBytes);
36617
36619
  const formResDict = params.cosDoc.resolveDict(dictGet(form.dict, "Resources")) ?? activeResources;
@@ -36629,8 +36631,8 @@ function evaluateContentStreamToDisplayList(params) {
36629
36631
  const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
36630
36632
  nextCtm = multiplyMatrices(formMat, nextCtm);
36631
36633
  }
36632
- let nextClip = !isolate && st.clipRect ? [...st.clipRect] : void 0;
36633
- let nextClipPaths = isolate ? void 0 : st.clipPaths;
36634
+ let nextClip = !compositeGroup && st.clipRect ? [...st.clipRect] : void 0;
36635
+ let nextClipPaths = compositeGroup ? void 0 : st.clipPaths;
36634
36636
  const bboxArr = params.cosDoc.resolveArray(dictGet(form.dict, "BBox"));
36635
36637
  if (bboxArr && bboxArr.items.length >= 4) {
36636
36638
  const bn = (idx, fb = 0) => {
@@ -36664,7 +36666,7 @@ function evaluateContentStreamToDisplayList(params) {
36664
36666
  const parentOperations = capturedOperations;
36665
36667
  const children = [];
36666
36668
  const nextState = { ...st, ctm: nextCtm };
36667
- if (isolate) {
36669
+ if (compositeGroup) {
36668
36670
  capturedOperations = children;
36669
36671
  nextState.fillAlpha = nextState.strokeAlpha = 1;
36670
36672
  nextState.blendMode = "Normal";
@@ -36682,7 +36684,7 @@ function evaluateContentStreamToDisplayList(params) {
36682
36684
  stateStack.pop();
36683
36685
  capturedOperations = parentOperations;
36684
36686
  }
36685
- if (isolate) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
36687
+ if (compositeGroup) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
36686
36688
  };
36687
36689
  const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
36688
36690
  for (const node of nodes) {
@@ -40254,7 +40256,7 @@ function dashSubpath(path, pattern, phase) {
40254
40256
  while (remaining <= 0) {
40255
40257
  if (++steps > MAX_STROKE_VERTICES) throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
40256
40258
  if (index % 2 === 0) {
40257
- if (pattern[index] === 0) result.push({ points: [point, point], closed: false });
40259
+ if (pattern[index] === 0) result.push({ points: [point, point], closed: false, zeroLengthDash: true });
40258
40260
  flush();
40259
40261
  }
40260
40262
  index = (index + 1) % pattern.length;
@@ -40269,7 +40271,7 @@ function dashSubpath(path, pattern, phase) {
40269
40271
  throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
40270
40272
  }
40271
40273
  let distance = 0;
40272
- while (distance < length) {
40274
+ while (length - distance > 8 * Number.EPSILON * Math.max(1, length)) {
40273
40275
  const point = [start[0] + dx * distance / length, start[1] + dy * distance / length];
40274
40276
  advance(point);
40275
40277
  const step = Math.min(remaining, length - distance);
@@ -40384,7 +40386,15 @@ function strokeOutlines(paths, width, cap, join, miterLimit, dashArray = [], das
40384
40386
  let closed = subpath.closed;
40385
40387
  if (closed && points.length > 1 && Math.hypot(points[0][0] - points[points.length - 1][0], points[0][1] - points[points.length - 1][1]) <= 1e-14) points.pop();
40386
40388
  if (points.length < 3) closed = false;
40387
- if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
40389
+ if (points.length === 1 && cap === 2 && subpath.zeroLengthDash) {
40390
+ contour = [];
40391
+ const [x, y] = points[0];
40392
+ add(x - half, y - half);
40393
+ add(x + half, y - half);
40394
+ add(x + half, y + half);
40395
+ add(x - half, y + half);
40396
+ contours.push(contour);
40397
+ } else if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
40388
40398
  contour = [];
40389
40399
  const [x, y] = points[0];
40390
40400
  addCap([x, y], [x + 1, y]);
@@ -41574,7 +41584,7 @@ function prepareStroke(path, scale) {
41574
41584
  }
41575
41585
  return { matrix, width, dashArray, dashPhase };
41576
41586
  }
41577
- function strokeEdges(path, pageHeight, scale) {
41587
+ function strokeContours(path, pageHeight, scale) {
41578
41588
  const stroke = prepareStroke(path, scale);
41579
41589
  if (!stroke) return [];
41580
41590
  const inverse = inverseStrokeMatrix(stroke.matrix);
@@ -41598,7 +41608,7 @@ function strokeEdges(path, pageHeight, scale) {
41598
41608
  stroke.dashArray?.map((value) => Math.max(0, value * strokeScale)),
41599
41609
  stroke.dashPhase * strokeScale
41600
41610
  );
41601
- return pathsToEdges(contours.map((points) => ({ points: points.map(toScreen), closed: true })));
41611
+ return contours.map((points) => points.map(toScreen));
41602
41612
  }
41603
41613
  function pathsToEdges(paths, closeSubpaths = false) {
41604
41614
  const edges = [];
@@ -41745,6 +41755,9 @@ function renderSoftMask(mask, displayList, scale) {
41745
41755
  }
41746
41756
  return bitmap;
41747
41757
  }
41758
+ function hasBlendModes(operations) {
41759
+ return operations.some((operation) => !!operation.value.blendMode && operation.value.blendMode !== "Normal" && operation.value.blendMode !== "Compatible" || operation.kind === "group" && hasBlendModes(operation.value.operations));
41760
+ }
41748
41761
  function renderDisplayListToBitmap(displayList, options = {}) {
41749
41762
  const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
41750
41763
  const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
@@ -41753,11 +41766,13 @@ function renderDisplayListToBitmap(displayList, options = {}) {
41753
41766
  const width = Math.max(1, Math.round(displayList.width * scale));
41754
41767
  const height = Math.max(1, Math.round(displayList.height * scale));
41755
41768
  const rgba = new Uint8Array(width * height * 4);
41769
+ const deferBackground = !options.transparent && hasBlendModes(paintOperations(displayList));
41770
+ const transparent = options.transparent || deferBackground;
41756
41771
  const bg = options.background ?? { r: 1, g: 1, b: 1 };
41757
- const bgR = options.transparent ? 0 : Math.round(bg.r * 255);
41758
- const bgG = options.transparent ? 0 : Math.round(bg.g * 255);
41759
- const bgB = options.transparent ? 0 : Math.round(bg.b * 255);
41760
- const bgA = options.transparent ? 0 : 255;
41772
+ const bgR = transparent ? 0 : Math.round(bg.r * 255);
41773
+ const bgG = transparent ? 0 : Math.round(bg.g * 255);
41774
+ const bgB = transparent ? 0 : Math.round(bg.b * 255);
41775
+ const bgA = transparent ? 0 : 255;
41761
41776
  for (let i = 0; i < width * height; i++) {
41762
41777
  rgba[i * 4] = bgR;
41763
41778
  rgba[i * 4 + 1] = bgG;
@@ -41851,7 +41866,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
41851
41866
  if (path.strokeColor) {
41852
41867
  const rawSw = path.strokeWidth * scale * (path.strokeMatrix ? Math.hypot(path.strokeMatrix[0], path.strokeMatrix[1]) : 1);
41853
41868
  const strokeAlpha = options.thinLineMode === "shape" && rawSw < 1 ? (path.strokeAlpha ?? 1) * Math.max(0.25, rawSw) : path.strokeAlpha ?? 1;
41854
- const edges = strokeEdges(path, displayList.height, scale);
41869
+ const edges = pathsToEdges(strokeContours(path, displayList.height, scale).map((points) => ({ points, closed: true })));
41855
41870
  const clipScreen = path.clipRect ? [path.clipRect[0] * scale, (displayList.height - path.clipRect[3]) * scale, path.clipRect[2] * scale, (displayList.height - path.clipRect[1]) * scale] : void 0;
41856
41871
  fillEdgesScanline4x4(
41857
41872
  rgba,
@@ -41934,6 +41949,15 @@ function renderDisplayListToBitmap(displayList, options = {}) {
41934
41949
  }
41935
41950
  }
41936
41951
  }
41952
+ if (deferBackground) {
41953
+ for (let i = 0; i < rgba.length; i += 4) {
41954
+ const alpha = rgba[i + 3] / 255;
41955
+ rgba[i] = Math.round(rgba[i] * alpha + bg.r * 255 * (1 - alpha));
41956
+ rgba[i + 1] = Math.round(rgba[i + 1] * alpha + bg.g * 255 * (1 - alpha));
41957
+ rgba[i + 2] = Math.round(rgba[i + 2] * alpha + bg.b * 255 * (1 - alpha));
41958
+ rgba[i + 3] = 255;
41959
+ }
41960
+ }
41937
41961
  let result = { width, height, data: rgba };
41938
41962
  if (Math.abs(scaleX - scaleY) > 1e-6) {
41939
41963
  const targetW = Math.max(1, Math.round(displayList.width * scaleX));
@@ -42042,6 +42066,7 @@ function renderDisplayListToSvg(displayList, options = {}) {
42042
42066
  if (!options.transparent) {
42043
42067
  parts.push(` <rect x="${vbX}" y="${vbY}" width="${vbW}" height="${vbH}" fill="#ffffff"/>`);
42044
42068
  }
42069
+ parts.push('<g style="isolation:isolate">');
42045
42070
  if (displayList.rotation) {
42046
42071
  const [a, b, c, d, e, f] = viewport.transform;
42047
42072
  parts.push(`<g transform="matrix(${a} ${b} ${-c} ${-d} ${e + c * displayList.height} ${f + d * displayList.height})">`);
@@ -42093,6 +42118,7 @@ function renderDisplayListToSvg(displayList, options = {}) {
42093
42118
  } else if (operation.kind === "path") {
42094
42119
  const p = operation.value;
42095
42120
  const prepared = p.strokeColor ? prepareStroke(p, Math.max(scaleX, scaleY)) : void 0;
42121
+ const outlineStroke = prepared && p.dashArray?.includes(0);
42096
42122
  const matrix = prepared && (prepared.matrix[0] !== 1 || prepared.matrix[1] !== 0 || prepared.matrix[2] !== 0 || prepared.matrix[3] !== 1) ? prepared.matrix : void 0;
42097
42123
  const inverse = matrix ? inverseStrokeMatrix(matrix) : void 0;
42098
42124
  const pathData = svgPathData(p.segments, inverse ? 0 : displayList.height, inverse);
@@ -42107,10 +42133,18 @@ function renderDisplayListToSvg(displayList, options = {}) {
42107
42133
  const lineCapAttr = p.lineCap === 1 ? ` stroke-linecap="round"` : p.lineCap === 2 ? ` stroke-linecap="square"` : "";
42108
42134
  const lineJoinAttr = p.lineJoin === 1 ? ` stroke-linejoin="round"` : p.lineJoin === 2 ? ` stroke-linejoin="bevel"` : "";
42109
42135
  const miterLimitAttr = p.miterLimit !== void 0 && p.miterLimit !== 10 ? ` stroke-miterlimit="${p.miterLimit}"` : "";
42110
- const dashArrayAttr = prepared?.dashArray?.length ? ` stroke-dasharray="${prepared.dashArray.join(" ")}"` : "";
42111
- const dashOffsetAttr = prepared?.dashPhase ? ` stroke-dashoffset="${prepared.dashPhase}"` : "";
42136
+ const dashArrayAttr = !outlineStroke && prepared?.dashArray?.length ? ` stroke-dasharray="${prepared.dashArray.join(" ")}"` : "";
42137
+ const dashOffsetAttr = !outlineStroke && prepared?.dashPhase ? ` stroke-dashoffset="${prepared.dashPhase}"` : "";
42112
42138
  const labelAttr = original.kind === "glyph" ? ` aria-label="${escapeXmlText(original.value.unicode)}"` : "";
42113
- parts.push(` <path${labelAttr}${transformAttr} d="${pathData}" fill="${fill}"${fillRuleAttr}${fillOpacityAttr} stroke="${stroke}"${strokeOpacityAttr}${strokeWidthAttr}${lineCapAttr}${lineJoinAttr}${miterLimitAttr}${dashArrayAttr}${dashOffsetAttr}/>`);
42139
+ if (!outlineStroke || p.fillColor) {
42140
+ parts.push(` <path${labelAttr}${transformAttr} d="${pathData}" fill="${fill}"${fillRuleAttr}${fillOpacityAttr} stroke="${outlineStroke ? "none" : stroke}"${strokeOpacityAttr}${strokeWidthAttr}${lineCapAttr}${lineJoinAttr}${miterLimitAttr}${dashArrayAttr}${dashOffsetAttr}/>`);
42141
+ }
42142
+ if (outlineStroke) {
42143
+ const outlineScale = Math.max(scaleX, scaleY);
42144
+ const contours = strokeContours(p, displayList.height, outlineScale);
42145
+ const outlineData = contours.map((points) => points.map(([x, y], i) => `${i === 0 ? "M" : "L"} ${x / outlineScale} ${y / outlineScale}`).join(" ") + " Z").join(" ");
42146
+ if (outlineData) parts.push(` <path${labelAttr} d="${outlineData}" fill="${stroke}" fill-opacity="${p.strokeAlpha ?? 1}"/>`);
42147
+ }
42114
42148
  }
42115
42149
  } else if (operation.kind === "image") {
42116
42150
  parts.push(` ${svgImage(operation.value, displayList.height)}`);
@@ -42120,6 +42154,7 @@ function renderDisplayListToSvg(displayList, options = {}) {
42120
42154
  };
42121
42155
  appendOperations(paintOperations(displayList));
42122
42156
  if (displayList.rotation) parts.push("</g>");
42157
+ parts.push("</g>");
42123
42158
  parts.push("</svg>\n");
42124
42159
  return parts.join("\n");
42125
42160
  }