@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.
@@ -146879,16 +146879,13 @@ async function lotusFormula(bytes, format2, group, row, column, sheetIndex, shee
146879
146879
  at++;
146880
146880
  if (op === 8 && name.startsWith("$")) name = name.slice(1);
146881
146881
  const range3 = names2?.get(name);
146882
- if (!range3) {
146883
- const external = lotusExternalVariable(name);
146884
- if (external !== void 0) stack.push(external);
146885
- else {
146882
+ const external = range3 ? void 0 : lotusExternalVariable(name);
146883
+ if (external !== void 0) stack.push(external);
146884
+ else {
146885
+ if (!range3)
146886
146886
  await context.diagnostic?.({ code: "lotus", severity: "warning", message: `Unknown Lotus named reference '${name}'.` });
146887
- stack.push("#NAME?");
146888
- }
146889
- } else {
146890
- const fixedSheet = range3.first.sheet !== sheetIndex || range3.last.sheet !== sheetIndex;
146891
- stack.push(`@name.${op === 7 ? "relative" : "absolute"}${fixedSheet ? ".fixed-sheet" : ""}[0,0,0]:` + quoteFormulaString(name, '"', gnumericGrammar));
146887
+ const fixedSheet = range3 && (range3.first.sheet !== sheetIndex || range3.last.sheet !== sheetIndex);
146888
+ stack.push(name ? `@name.${op === 7 ? "relative" : "absolute"}${fixedSheet ? ".fixed-sheet" : ""}[0,0,0]:` + quoteFormulaString(name, '"', gnumericGrammar) : "#NAME?");
146892
146889
  }
146893
146890
  } else if (modern && op >= 9 && op <= 11) {
146894
146891
  const length = op === 9 ? 4 : op === 10 ? 5 : 11;
@@ -209346,10 +209343,11 @@ function evaluateContentStreamToDisplayList(params) {
209346
209343
  const patArr = params.cosDoc.resolveArray(dArr.items[0]);
209347
209344
  const phaseNode = params.cosDoc.resolve(dArr.items[1]);
209348
209345
  if (patArr) {
209349
- st.dashArray = patArr.items.map((it) => {
209346
+ const dashArray = patArr.items.map((it) => {
209350
209347
  const r = params.cosDoc.resolve(it);
209351
209348
  return r?.kind === "number" ? r.value : 0;
209352
- }).filter((n) => n > 0);
209349
+ }).filter((n) => n >= 0);
209350
+ st.dashArray = dashArray.some((value3) => value3 > 0) ? dashArray : void 0;
209353
209351
  st.dashPhase = phaseNode?.kind === "number" ? phaseNode.value : 0;
209354
209352
  }
209355
209353
  }
@@ -209404,8 +209402,9 @@ function evaluateContentStreamToDisplayList(params) {
209404
209402
  const st = curState();
209405
209403
  const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
209406
209404
  const groupType = group ? params.cosDoc.resolve(dictGet(group, "S")) : void 0;
209407
- const isolated = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
209408
- const isolate = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && isolated?.kind === "boolean" && isolated.value;
209405
+ const isolation = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
209406
+ const isolated = isolation?.kind === "boolean" && isolation.value;
209407
+ const compositeGroup = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && (isolated || st.fillAlpha !== 1 || !!st.softMask || st.blendMode !== "Normal" && st.blendMode !== "Compatible");
209409
209408
  const formStreamBytes = params.cosDoc.decodeStream(form);
209410
209409
  const formNodes = parseContentStream(formStreamBytes);
209411
209410
  const formResDict = params.cosDoc.resolveDict(dictGet(form.dict, "Resources")) ?? activeResources;
@@ -209423,8 +209422,8 @@ function evaluateContentStreamToDisplayList(params) {
209423
209422
  const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
209424
209423
  nextCtm = multiplyMatrices(formMat, nextCtm);
209425
209424
  }
209426
- let nextClip = !isolate && st.clipRect ? [...st.clipRect] : void 0;
209427
- let nextClipPaths = isolate ? void 0 : st.clipPaths;
209425
+ let nextClip = !compositeGroup && st.clipRect ? [...st.clipRect] : void 0;
209426
+ let nextClipPaths = compositeGroup ? void 0 : st.clipPaths;
209428
209427
  const bboxArr = params.cosDoc.resolveArray(dictGet(form.dict, "BBox"));
209429
209428
  if (bboxArr && bboxArr.items.length >= 4) {
209430
209429
  const bn = (idx, fb = 0) => {
@@ -209458,7 +209457,7 @@ function evaluateContentStreamToDisplayList(params) {
209458
209457
  const parentOperations = capturedOperations;
209459
209458
  const children8 = [];
209460
209459
  const nextState = { ...st, ctm: nextCtm };
209461
- if (isolate) {
209460
+ if (compositeGroup) {
209462
209461
  capturedOperations = children8;
209463
209462
  nextState.fillAlpha = nextState.strokeAlpha = 1;
209464
209463
  nextState.blendMode = "Normal";
@@ -209476,7 +209475,7 @@ function evaluateContentStreamToDisplayList(params) {
209476
209475
  stateStack.pop();
209477
209476
  capturedOperations = parentOperations;
209478
209477
  }
209479
- if (isolate) emit({ kind: "group", value: { operations: children8, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
209478
+ if (compositeGroup) emit({ kind: "group", value: { operations: children8, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
209480
209479
  };
209481
209480
  const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
209482
209481
  for (const node3 of nodes) {
@@ -212353,7 +212352,7 @@ function dashSubpath(path2, pattern, phase) {
212353
212352
  while (remaining <= 0) {
212354
212353
  if (++steps > MAX_STROKE_VERTICES) throw new PdfError2("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
212355
212354
  if (index2 % 2 === 0) {
212356
- if (pattern[index2] === 0) result2.push({ points: [point, point], closed: false });
212355
+ if (pattern[index2] === 0) result2.push({ points: [point, point], closed: false, zeroLengthDash: true });
212357
212356
  flush();
212358
212357
  }
212359
212358
  index2 = (index2 + 1) % pattern.length;
@@ -212368,7 +212367,7 @@ function dashSubpath(path2, pattern, phase) {
212368
212367
  throw new PdfError2("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
212369
212368
  }
212370
212369
  let distance2 = 0;
212371
- while (distance2 < length) {
212370
+ while (length - distance2 > 8 * Number.EPSILON * Math.max(1, length)) {
212372
212371
  const point = [start[0] + dx * distance2 / length, start[1] + dy * distance2 / length];
212373
212372
  advance2(point);
212374
212373
  const step = Math.min(remaining, length - distance2);
@@ -212483,7 +212482,15 @@ function strokeOutlines(paths, width2, cap, join3, miterLimit, dashArray = [], d
212483
212482
  let closed = subpath.closed;
212484
212483
  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();
212485
212484
  if (points.length < 3) closed = false;
212486
- if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
212485
+ if (points.length === 1 && cap === 2 && subpath.zeroLengthDash) {
212486
+ contour = [];
212487
+ const [x, y] = points[0];
212488
+ add5(x - half, y - half);
212489
+ add5(x + half, y - half);
212490
+ add5(x + half, y + half);
212491
+ add5(x - half, y + half);
212492
+ contours.push(contour);
212493
+ } else if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
212487
212494
  contour = [];
212488
212495
  const [x, y] = points[0];
212489
212496
  addCap([x, y], [x + 1, y]);
@@ -213275,7 +213282,7 @@ function prepareStroke(path2, scale2) {
213275
213282
  }
213276
213283
  return { matrix: matrix2, width: width2, dashArray, dashPhase };
213277
213284
  }
213278
- function strokeEdges(path2, pageHeight, scale2) {
213285
+ function strokeContours(path2, pageHeight, scale2) {
213279
213286
  const stroke = prepareStroke(path2, scale2);
213280
213287
  if (!stroke) return [];
213281
213288
  const inverse2 = inverseStrokeMatrix(stroke.matrix);
@@ -213299,7 +213306,7 @@ function strokeEdges(path2, pageHeight, scale2) {
213299
213306
  stroke.dashArray?.map((value3) => Math.max(0, value3 * strokeScale)),
213300
213307
  stroke.dashPhase * strokeScale
213301
213308
  );
213302
- return pathsToEdges(contours.map((points) => ({ points: points.map(toScreen), closed: true })));
213309
+ return contours.map((points) => points.map(toScreen));
213303
213310
  }
213304
213311
  function pathsToEdges(paths, closeSubpaths = false) {
213305
213312
  const edges2 = [];
@@ -213446,6 +213453,9 @@ function renderSoftMask(mask, displayList, scale2) {
213446
213453
  }
213447
213454
  return bitmap;
213448
213455
  }
213456
+ function hasBlendModes(operations) {
213457
+ return operations.some((operation) => !!operation.value.blendMode && operation.value.blendMode !== "Normal" && operation.value.blendMode !== "Compatible" || operation.kind === "group" && hasBlendModes(operation.value.operations));
213458
+ }
213449
213459
  function renderDisplayListToBitmap(displayList, options = {}) {
213450
213460
  const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
213451
213461
  const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
@@ -213454,11 +213464,13 @@ function renderDisplayListToBitmap(displayList, options = {}) {
213454
213464
  const width2 = Math.max(1, Math.round(displayList.width * scale2));
213455
213465
  const height = Math.max(1, Math.round(displayList.height * scale2));
213456
213466
  const rgba = new Uint8Array(width2 * height * 4);
213467
+ const deferBackground = !options.transparent && hasBlendModes(paintOperations(displayList));
213468
+ const transparent = options.transparent || deferBackground;
213457
213469
  const bg = options.background ?? { r: 1, g: 1, b: 1 };
213458
- const bgR = options.transparent ? 0 : Math.round(bg.r * 255);
213459
- const bgG = options.transparent ? 0 : Math.round(bg.g * 255);
213460
- const bgB = options.transparent ? 0 : Math.round(bg.b * 255);
213461
- const bgA = options.transparent ? 0 : 255;
213470
+ const bgR = transparent ? 0 : Math.round(bg.r * 255);
213471
+ const bgG = transparent ? 0 : Math.round(bg.g * 255);
213472
+ const bgB = transparent ? 0 : Math.round(bg.b * 255);
213473
+ const bgA = transparent ? 0 : 255;
213462
213474
  for (let i = 0; i < width2 * height; i++) {
213463
213475
  rgba[i * 4] = bgR;
213464
213476
  rgba[i * 4 + 1] = bgG;
@@ -213552,7 +213564,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
213552
213564
  if (path2.strokeColor) {
213553
213565
  const rawSw = path2.strokeWidth * scale2 * (path2.strokeMatrix ? Math.hypot(path2.strokeMatrix[0], path2.strokeMatrix[1]) : 1);
213554
213566
  const strokeAlpha = options.thinLineMode === "shape" && rawSw < 1 ? (path2.strokeAlpha ?? 1) * Math.max(0.25, rawSw) : path2.strokeAlpha ?? 1;
213555
- const edges2 = strokeEdges(path2, displayList.height, scale2);
213567
+ const edges2 = pathsToEdges(strokeContours(path2, displayList.height, scale2).map((points) => ({ points, closed: true })));
213556
213568
  const clipScreen = path2.clipRect ? [path2.clipRect[0] * scale2, (displayList.height - path2.clipRect[3]) * scale2, path2.clipRect[2] * scale2, (displayList.height - path2.clipRect[1]) * scale2] : void 0;
213557
213569
  fillEdgesScanline4x4(
213558
213570
  rgba,
@@ -213635,6 +213647,15 @@ function renderDisplayListToBitmap(displayList, options = {}) {
213635
213647
  }
213636
213648
  }
213637
213649
  }
213650
+ if (deferBackground) {
213651
+ for (let i = 0; i < rgba.length; i += 4) {
213652
+ const alpha = rgba[i + 3] / 255;
213653
+ rgba[i] = Math.round(rgba[i] * alpha + bg.r * 255 * (1 - alpha));
213654
+ rgba[i + 1] = Math.round(rgba[i + 1] * alpha + bg.g * 255 * (1 - alpha));
213655
+ rgba[i + 2] = Math.round(rgba[i + 2] * alpha + bg.b * 255 * (1 - alpha));
213656
+ rgba[i + 3] = 255;
213657
+ }
213658
+ }
213638
213659
  let result2 = { width: width2, height, data: rgba };
213639
213660
  if (Math.abs(scaleX - scaleY) > 1e-6) {
213640
213661
  const targetW = Math.max(1, Math.round(displayList.width * scaleX));
@@ -11877,10 +11877,11 @@ function evaluateContentStreamToDisplayList(params) {
11877
11877
  const patArr = params.cosDoc.resolveArray(dArr.items[0]);
11878
11878
  const phaseNode = params.cosDoc.resolve(dArr.items[1]);
11879
11879
  if (patArr) {
11880
- st.dashArray = patArr.items.map((it) => {
11880
+ const dashArray = patArr.items.map((it) => {
11881
11881
  const r = params.cosDoc.resolve(it);
11882
11882
  return r?.kind === "number" ? r.value : 0;
11883
- }).filter((n) => n > 0);
11883
+ }).filter((n) => n >= 0);
11884
+ st.dashArray = dashArray.some((value) => value > 0) ? dashArray : void 0;
11884
11885
  st.dashPhase = phaseNode?.kind === "number" ? phaseNode.value : 0;
11885
11886
  }
11886
11887
  }
@@ -11935,8 +11936,9 @@ function evaluateContentStreamToDisplayList(params) {
11935
11936
  const st = curState();
11936
11937
  const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
11937
11938
  const groupType = group ? params.cosDoc.resolve(dictGet(group, "S")) : void 0;
11938
- const isolated = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
11939
- const isolate = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && isolated?.kind === "boolean" && isolated.value;
11939
+ const isolation = group ? params.cosDoc.resolve(dictGet(group, "I")) : void 0;
11940
+ const isolated = isolation?.kind === "boolean" && isolation.value;
11941
+ const compositeGroup = !maskGroup && groupType?.kind === "name" && groupType.decoded === "Transparency" && (isolated || st.fillAlpha !== 1 || !!st.softMask || st.blendMode !== "Normal" && st.blendMode !== "Compatible");
11940
11942
  const formStreamBytes = params.cosDoc.decodeStream(form);
11941
11943
  const formNodes = parseContentStream(formStreamBytes);
11942
11944
  const formResDict = params.cosDoc.resolveDict(dictGet(form.dict, "Resources")) ?? activeResources;
@@ -11954,8 +11956,8 @@ function evaluateContentStreamToDisplayList(params) {
11954
11956
  const formMat = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
11955
11957
  nextCtm = multiplyMatrices(formMat, nextCtm);
11956
11958
  }
11957
- let nextClip = !isolate && st.clipRect ? [...st.clipRect] : void 0;
11958
- let nextClipPaths = isolate ? void 0 : st.clipPaths;
11959
+ let nextClip = !compositeGroup && st.clipRect ? [...st.clipRect] : void 0;
11960
+ let nextClipPaths = compositeGroup ? void 0 : st.clipPaths;
11959
11961
  const bboxArr = params.cosDoc.resolveArray(dictGet(form.dict, "BBox"));
11960
11962
  if (bboxArr && bboxArr.items.length >= 4) {
11961
11963
  const bn = (idx, fb = 0) => {
@@ -11989,7 +11991,7 @@ function evaluateContentStreamToDisplayList(params) {
11989
11991
  const parentOperations = capturedOperations;
11990
11992
  const children = [];
11991
11993
  const nextState = { ...st, ctm: nextCtm };
11992
- if (isolate) {
11994
+ if (compositeGroup) {
11993
11995
  capturedOperations = children;
11994
11996
  nextState.fillAlpha = nextState.strokeAlpha = 1;
11995
11997
  nextState.blendMode = "Normal";
@@ -12007,7 +12009,7 @@ function evaluateContentStreamToDisplayList(params) {
12007
12009
  stateStack.pop();
12008
12010
  capturedOperations = parentOperations;
12009
12011
  }
12010
- if (isolate) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
12012
+ if (compositeGroup) emit({ kind: "group", value: { operations: children, alpha: st.fillAlpha, blendMode: st.blendMode, clipRect: st.clipRect } });
12011
12013
  };
12012
12014
  const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
12013
12015
  for (const node of nodes) {
@@ -14098,7 +14100,7 @@ function dashSubpath(path, pattern, phase) {
14098
14100
  while (remaining <= 0) {
14099
14101
  if (++steps > MAX_STROKE_VERTICES) throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
14100
14102
  if (index % 2 === 0) {
14101
- if (pattern[index] === 0) result.push({ points: [point, point], closed: false });
14103
+ if (pattern[index] === 0) result.push({ points: [point, point], closed: false, zeroLengthDash: true });
14102
14104
  flush();
14103
14105
  }
14104
14106
  index = (index + 1) % pattern.length;
@@ -14113,7 +14115,7 @@ function dashSubpath(path, pattern, phase) {
14113
14115
  throw new PdfError("E_LIMIT", "Stroke dash expansion exceeds the vertex limit");
14114
14116
  }
14115
14117
  let distance = 0;
14116
- while (distance < length2) {
14118
+ while (length2 - distance > 8 * Number.EPSILON * Math.max(1, length2)) {
14117
14119
  const point = [start[0] + dx * distance / length2, start[1] + dy * distance / length2];
14118
14120
  advance(point);
14119
14121
  const step = Math.min(remaining, length2 - distance);
@@ -14228,7 +14230,15 @@ function strokeOutlines(paths, width, cap, join, miterLimit, dashArray = [], das
14228
14230
  let closed = subpath.closed;
14229
14231
  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();
14230
14232
  if (points.length < 3) closed = false;
14231
- if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
14233
+ if (points.length === 1 && cap === 2 && subpath.zeroLengthDash) {
14234
+ contour = [];
14235
+ const [x, y] = points[0];
14236
+ add(x - half, y - half);
14237
+ add(x + half, y - half);
14238
+ add(x + half, y + half);
14239
+ add(x - half, y + half);
14240
+ contours.push(contour);
14241
+ } else if (points.length === 1 && subpath.points.length > 1 && cap === 1) {
14232
14242
  contour = [];
14233
14243
  const [x, y] = points[0];
14234
14244
  addCap([x, y], [x + 1, y]);
@@ -14708,7 +14718,7 @@ function prepareStroke(path, scale) {
14708
14718
  }
14709
14719
  return { matrix, width, dashArray, dashPhase };
14710
14720
  }
14711
- function strokeEdges(path, pageHeight, scale) {
14721
+ function strokeContours(path, pageHeight, scale) {
14712
14722
  const stroke = prepareStroke(path, scale);
14713
14723
  if (!stroke) return [];
14714
14724
  const inverse = inverseStrokeMatrix(stroke.matrix);
@@ -14732,7 +14742,7 @@ function strokeEdges(path, pageHeight, scale) {
14732
14742
  stroke.dashArray?.map((value) => Math.max(0, value * strokeScale)),
14733
14743
  stroke.dashPhase * strokeScale
14734
14744
  );
14735
- return pathsToEdges(contours.map((points) => ({ points: points.map(toScreen), closed: true })));
14745
+ return contours.map((points) => points.map(toScreen));
14736
14746
  }
14737
14747
  function pathsToEdges(paths, closeSubpaths = false) {
14738
14748
  const edges = [];
@@ -14879,6 +14889,9 @@ function renderSoftMask(mask, displayList, scale) {
14879
14889
  }
14880
14890
  return bitmap;
14881
14891
  }
14892
+ function hasBlendModes(operations) {
14893
+ return operations.some((operation) => !!operation.value.blendMode && operation.value.blendMode !== "Normal" && operation.value.blendMode !== "Compatible" || operation.kind === "group" && hasBlendModes(operation.value.operations));
14894
+ }
14882
14895
  function renderDisplayListToBitmap(displayList, options = {}) {
14883
14896
  const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
14884
14897
  const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
@@ -14887,11 +14900,13 @@ function renderDisplayListToBitmap(displayList, options = {}) {
14887
14900
  const width = Math.max(1, Math.round(displayList.width * scale));
14888
14901
  const height = Math.max(1, Math.round(displayList.height * scale));
14889
14902
  const rgba = new Uint8Array(width * height * 4);
14903
+ const deferBackground = !options.transparent && hasBlendModes(paintOperations(displayList));
14904
+ const transparent = options.transparent || deferBackground;
14890
14905
  const bg = options.background ?? { r: 1, g: 1, b: 1 };
14891
- const bgR = options.transparent ? 0 : Math.round(bg.r * 255);
14892
- const bgG = options.transparent ? 0 : Math.round(bg.g * 255);
14893
- const bgB = options.transparent ? 0 : Math.round(bg.b * 255);
14894
- const bgA = options.transparent ? 0 : 255;
14906
+ const bgR = transparent ? 0 : Math.round(bg.r * 255);
14907
+ const bgG = transparent ? 0 : Math.round(bg.g * 255);
14908
+ const bgB = transparent ? 0 : Math.round(bg.b * 255);
14909
+ const bgA = transparent ? 0 : 255;
14895
14910
  for (let i = 0; i < width * height; i++) {
14896
14911
  rgba[i * 4] = bgR;
14897
14912
  rgba[i * 4 + 1] = bgG;
@@ -14985,7 +15000,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
14985
15000
  if (path.strokeColor) {
14986
15001
  const rawSw = path.strokeWidth * scale * (path.strokeMatrix ? Math.hypot(path.strokeMatrix[0], path.strokeMatrix[1]) : 1);
14987
15002
  const strokeAlpha = options.thinLineMode === "shape" && rawSw < 1 ? (path.strokeAlpha ?? 1) * Math.max(0.25, rawSw) : path.strokeAlpha ?? 1;
14988
- const edges = strokeEdges(path, displayList.height, scale);
15003
+ const edges = pathsToEdges(strokeContours(path, displayList.height, scale).map((points) => ({ points, closed: true })));
14989
15004
  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;
14990
15005
  fillEdgesScanline4x4(
14991
15006
  rgba,
@@ -15068,6 +15083,15 @@ function renderDisplayListToBitmap(displayList, options = {}) {
15068
15083
  }
15069
15084
  }
15070
15085
  }
15086
+ if (deferBackground) {
15087
+ for (let i = 0; i < rgba.length; i += 4) {
15088
+ const alpha = rgba[i + 3] / 255;
15089
+ rgba[i] = Math.round(rgba[i] * alpha + bg.r * 255 * (1 - alpha));
15090
+ rgba[i + 1] = Math.round(rgba[i + 1] * alpha + bg.g * 255 * (1 - alpha));
15091
+ rgba[i + 2] = Math.round(rgba[i + 2] * alpha + bg.b * 255 * (1 - alpha));
15092
+ rgba[i + 3] = 255;
15093
+ }
15094
+ }
15071
15095
  let result = { width, height, data: rgba };
15072
15096
  if (Math.abs(scaleX - scaleY) > 1e-6) {
15073
15097
  const targetW = Math.max(1, Math.round(displayList.width * scaleX));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-bash",
3
- "version": "0.1.765",
3
+ "version": "0.1.767",
4
4
  "description": "Extensible virtual shell with pluggable filesystems and streaming commands",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -841,7 +841,7 @@
841
841
  "access": "public"
842
842
  },
843
843
  "dependencies": {
844
- "@poe-platform/safe-fs": "0.1.765",
844
+ "@poe-platform/safe-fs": "0.1.767",
845
845
  "pdf-lib": "1.17.1",
846
846
  "@noble/hashes": "2.4.0",
847
847
  "pako": "3.0.1",
@@ -222,6 +222,9 @@ permission terms. The adapter uses double-precision arrays, PDFium's
222
222
  `miter_join_revert` bevel fallback, and a one-million-vertex expansion limit.
223
223
  Local dash splitting preserves joins across vertices and closed seams, resets
224
224
  phase per subpath, and handles odd arrays and zero-length round dots.
225
+ Square zero-length dashes follow PDF.js/Canvas user-axis squares. Dash stepping
226
+ ignores a few ULPs of transformed endpoint residue; SVG reuses these contours for
227
+ zero-length patterns so its endpoint behavior and opacity match PDF painting.
225
228
  `src/render/stroke-joins.test.ts` checks native cap/join vectors plus independent
226
229
  PDF.js pixels; Poppler also confirms the final zero-length dash is omitted.
227
230
  The native harness calls unchanged upstream math routines with minimal support
@@ -396,6 +399,15 @@ in `src/fixtures/SOURCES.md`, and adds local alpha/transform/group tests plus
396
399
  the original issue17069 signature regression. Soft-mask evaluation is bounded
397
400
  by the existing eight-level Form limit; mask-only text is excluded from extraction.
398
401
 
402
+ Non-isolated transparency Forms follow `beginGroup`'s direct-paint eligibility
403
+ and intermediate-surface state reset. Bitmap and SVG exports follow
404
+ `beginDrawing` in keeping the viewer background outside page blend calculations.
405
+ `src/render/transparency-group.test.ts` adds local opacity/blend/mask cases and
406
+ ports the unchanged `transparency_group.pdf` and `bug1873345.pdf` equality
407
+ fixtures, with independently obtained reference pixels. Full non-isolated
408
+ backdrop compositing for groups with outer effects retains PDF.js's documented
409
+ limitation. Form contents remain vectors in SVG output.
410
+
399
411
  Malformed dictionary-key recovery follows PDF.js `Parser.getObj`, skipping
400
412
  stray non-Name tokens only in local repair mode. Damaged optional ToUnicode
401
413
  streams follow `PartialEvaluator.readToUnicode` error recovery while retaining