@poe-platform/safe-bash 0.1.780 → 0.1.782
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.
- package/dist/image-ast/index.browser.js +200 -165
- package/dist/image-ast/index.js +200 -165
- package/dist/pdf-ast/index.js +200 -165
- package/dist/safe-bash/command-chunks/{chunk-ZY6PMCW5.js → chunk-ADBFNLLK.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-QLYJUVVM.js → chunk-GBHSE6ZO.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-3444RZLY.js → chunk-GZXLPWT5.js} +200 -165
- package/dist/safe-bash-command-ffmpeg/index.js +5745 -5715
- package/dist/safe-bash-command-imagemagick/index.js +2 -2
- package/dist/safe-bash-command-pandoc/command.js +3 -3
- package/dist/safe-bash-command-pandoc/index.js +3 -3
- package/dist/safe-bash-command-pdfimages/index.js +200 -165
- package/dist/safe-bash-command-pdfinfo/index.js +200 -165
- package/dist/safe-bash-command-pdftk/index.js +200 -165
- package/dist/safe-bash-command-pdftoppm/index.js +200 -165
- package/dist/safe-bash-command-pdftotext/index.js +200 -165
- package/dist/safe-bash-command-qpdf/index.js +200 -165
- package/dist/safe-bash-command-sips/index.js +2 -2
- package/dist/safe-bash-command-soffice/index.js +1 -1
- package/dist/safe-bash-command-ssconvert/index.js +200 -165
- package/dist/safe-bash-command-wkhtmltopdf/index.js +201 -165
- package/package.json +2 -2
- package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +73 -5
|
@@ -3456,7 +3456,7 @@ function cosString(value, span) {
|
|
|
3456
3456
|
if (typeof value === "string") {
|
|
3457
3457
|
let needsUtf16 = false;
|
|
3458
3458
|
for (let i = 0; i < value.length; i++) {
|
|
3459
|
-
if (value.charCodeAt(i)
|
|
3459
|
+
if (!PDF_DOC_ENCODING_REVERSE.has(value.charCodeAt(i))) {
|
|
3460
3460
|
needsUtf16 = true;
|
|
3461
3461
|
break;
|
|
3462
3462
|
}
|
|
@@ -3474,7 +3474,7 @@ function cosString(value, span) {
|
|
|
3474
3474
|
}
|
|
3475
3475
|
const bytes = new Uint8Array(value.length);
|
|
3476
3476
|
for (let i = 0; i < value.length; i++) {
|
|
3477
|
-
bytes[i] = value.charCodeAt(i)
|
|
3477
|
+
bytes[i] = PDF_DOC_ENCODING_REVERSE.get(value.charCodeAt(i));
|
|
3478
3478
|
}
|
|
3479
3479
|
return { kind: "string", format: "literal", bytes, span };
|
|
3480
3480
|
}
|
|
@@ -11454,6 +11454,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
11454
11454
|
};
|
|
11455
11455
|
const initialState = {
|
|
11456
11456
|
ctm: [1, 0, 0, 1, 0, 0],
|
|
11457
|
+
initialCtm: [1, 0, 0, 1, 0, 0],
|
|
11457
11458
|
strokeColor: { r: 0, g: 0, b: 0 },
|
|
11458
11459
|
fillColor: { r: 0, g: 0, b: 0 },
|
|
11459
11460
|
strokeAlpha: 1,
|
|
@@ -11751,6 +11752,116 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
11751
11752
|
] }], void 0, void 0, resources, activeFonts, depth);
|
|
11752
11753
|
stateStack.pop();
|
|
11753
11754
|
};
|
|
11755
|
+
const paintPattern = (segments, fillRule, resources, activeFonts, depth, mcid, actualText) => {
|
|
11756
|
+
const st = curState(), doc = params.cosDoc;
|
|
11757
|
+
if (!st.fillPatternName || !doc || !resources) return false;
|
|
11758
|
+
if (depth >= 8) throw new PdfError("E_LIMIT", "Pattern nesting exceeds the form depth limit");
|
|
11759
|
+
const patterns = doc.resolveDict(dictGet(resources, "Pattern"));
|
|
11760
|
+
const pattern = patterns && doc.resolve(dictGet(patterns, st.fillPatternName));
|
|
11761
|
+
const dict = pattern?.kind === "stream" ? pattern.dict : pattern?.kind === "dict" ? pattern : void 0;
|
|
11762
|
+
if (!dict) return false;
|
|
11763
|
+
const nums = (key, fallback) => {
|
|
11764
|
+
const array = doc.resolveArray(dictGet(dict, key));
|
|
11765
|
+
return array ? array.items.map((item, i) => {
|
|
11766
|
+
const value = doc.resolve(item);
|
|
11767
|
+
return value?.kind === "number" ? value.value : fallback[i] ?? 0;
|
|
11768
|
+
}) : fallback;
|
|
11769
|
+
};
|
|
11770
|
+
let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
|
|
11771
|
+
const include = (x, y) => {
|
|
11772
|
+
x0 = Math.min(x0, x);
|
|
11773
|
+
y0 = Math.min(y0, y);
|
|
11774
|
+
x1 = Math.max(x1, x);
|
|
11775
|
+
y1 = Math.max(y1, y);
|
|
11776
|
+
};
|
|
11777
|
+
for (const segment of segments) {
|
|
11778
|
+
if (segment.kind === "close") continue;
|
|
11779
|
+
include(segment.x, segment.y);
|
|
11780
|
+
if (segment.kind === "cubic") {
|
|
11781
|
+
include(segment.x1, segment.y1);
|
|
11782
|
+
include(segment.x2, segment.y2);
|
|
11783
|
+
}
|
|
11784
|
+
if (segment.kind === "rect") include(segment.x + segment.width, segment.y + segment.height);
|
|
11785
|
+
}
|
|
11786
|
+
const [originX, originY] = params.origin ?? [0, 0];
|
|
11787
|
+
const clip = st.clipRect ?? [originX, originY, originX + params.width, originY + params.height];
|
|
11788
|
+
const bounds = [Math.max(x0, clip[0]), Math.max(y0, clip[1]), Math.min(x1, clip[2]), Math.min(y1, clip[3])];
|
|
11789
|
+
if (!(bounds[2] > bounds[0] && bounds[3] > bounds[1])) return true;
|
|
11790
|
+
const matrix = multiplyMatrices(nums("Matrix", [1, 0, 0, 1, 0, 0]), st.initialCtm);
|
|
11791
|
+
const type = doc.resolve(dictGet(dict, "PatternType"));
|
|
11792
|
+
stateStack.push({ ...st, clipRect: bounds, clipPaths: [...st.clipPaths ?? [], { segments, fillRule }] });
|
|
11793
|
+
try {
|
|
11794
|
+
if (type?.kind === "number" && type.value === 2) {
|
|
11795
|
+
const shading = doc.resolve(dictGet(dict, "Shading"));
|
|
11796
|
+
const shadingDict = shading?.kind === "stream" ? shading.dict : shading?.kind === "dict" ? shading : void 0;
|
|
11797
|
+
if (!shadingDict) return false;
|
|
11798
|
+
const image = renderShadingDictToImage(
|
|
11799
|
+
doc,
|
|
11800
|
+
shadingDict,
|
|
11801
|
+
matrix,
|
|
11802
|
+
bounds,
|
|
11803
|
+
st.fillAlpha,
|
|
11804
|
+
"PatternShading_" + st.fillPatternName,
|
|
11805
|
+
bounds,
|
|
11806
|
+
shading?.kind === "stream" ? shading : void 0,
|
|
11807
|
+
st.blendMode
|
|
11808
|
+
);
|
|
11809
|
+
if (image) emit({ kind: "image", value: image });
|
|
11810
|
+
return !!image;
|
|
11811
|
+
}
|
|
11812
|
+
if (pattern?.kind !== "stream") return false;
|
|
11813
|
+
const xStepNode = doc.resolve(dictGet(dict, "XStep"));
|
|
11814
|
+
const yStepNode = doc.resolve(dictGet(dict, "YStep"));
|
|
11815
|
+
const xStep = xStepNode?.kind === "number" ? Math.abs(xStepNode.value) : 0;
|
|
11816
|
+
const yStep = yStepNode?.kind === "number" ? Math.abs(yStepNode.value) : 0;
|
|
11817
|
+
const determinant = matrix[0] * matrix[3] - matrix[1] * matrix[2];
|
|
11818
|
+
if (!xStep || !yStep || !determinant) return true;
|
|
11819
|
+
const inverse = [
|
|
11820
|
+
matrix[3] / determinant,
|
|
11821
|
+
-matrix[1] / determinant,
|
|
11822
|
+
-matrix[2] / determinant,
|
|
11823
|
+
matrix[0] / determinant,
|
|
11824
|
+
(matrix[2] * matrix[5] - matrix[3] * matrix[4]) / determinant,
|
|
11825
|
+
(matrix[1] * matrix[4] - matrix[0] * matrix[5]) / determinant
|
|
11826
|
+
];
|
|
11827
|
+
const corners = [[bounds[0], bounds[1]], [bounds[2], bounds[1]], [bounds[2], bounds[3]], [bounds[0], bounds[3]]].map(([x, y]) => transformPoint(inverse, x, y));
|
|
11828
|
+
const box = nums("BBox", [0, 0, xStep, yStep]);
|
|
11829
|
+
const ix0 = Math.floor((Math.min(...corners.map((p) => p[0])) - box[2]) / xStep) + 1;
|
|
11830
|
+
const ix1 = Math.ceil((Math.max(...corners.map((p) => p[0])) - box[0]) / xStep) - 1;
|
|
11831
|
+
const iy0 = Math.floor((Math.min(...corners.map((p) => p[1])) - box[3]) / yStep) + 1;
|
|
11832
|
+
const iy1 = Math.ceil((Math.max(...corners.map((p) => p[1])) - box[1]) / yStep) - 1;
|
|
11833
|
+
if ((ix1 - ix0 + 1) * (iy1 - iy0 + 1) > 2e4) throw new PdfError("E_LIMIT", "Pattern tile count exceeds 20000");
|
|
11834
|
+
const nodes = parseContentStream(doc.decodeStream(pattern));
|
|
11835
|
+
const patternResources = doc.resolveDict(dictGet(dict, "Resources")) ?? resources;
|
|
11836
|
+
const patternFonts = new Map(activeFonts);
|
|
11837
|
+
for (const [key, font] of resolvePageFonts(doc, patternResources)) patternFonts.set(key, font);
|
|
11838
|
+
for (let iy = iy0; iy <= iy1; iy++) {
|
|
11839
|
+
for (let ix = ix0; ix <= ix1; ix++) {
|
|
11840
|
+
const tileCtm = multiplyMatrices([1, 0, 0, 1, ix * xStep, iy * yStep], matrix);
|
|
11841
|
+
const points = [[box[0], box[1]], [box[2], box[1]], [box[2], box[3]], [box[0], box[3]]].map(([x, y]) => transformPoint(tileCtm, x, y));
|
|
11842
|
+
const tileClip = [
|
|
11843
|
+
...points.map(([x, y], i) => ({ kind: i === 0 ? "move" : "line", x, y })),
|
|
11844
|
+
{ kind: "close" }
|
|
11845
|
+
];
|
|
11846
|
+
stateStack.push({
|
|
11847
|
+
...curState(),
|
|
11848
|
+
fillPatternName: void 0,
|
|
11849
|
+
ctm: tileCtm,
|
|
11850
|
+
initialCtm: tileCtm,
|
|
11851
|
+
clipPaths: [...curState().clipPaths ?? [], tileClip]
|
|
11852
|
+
});
|
|
11853
|
+
try {
|
|
11854
|
+
walkNodes(nodes, mcid, actualText, patternResources, patternFonts, depth + 1);
|
|
11855
|
+
} finally {
|
|
11856
|
+
stateStack.pop();
|
|
11857
|
+
}
|
|
11858
|
+
}
|
|
11859
|
+
}
|
|
11860
|
+
return true;
|
|
11861
|
+
} finally {
|
|
11862
|
+
stateStack.pop();
|
|
11863
|
+
}
|
|
11864
|
+
};
|
|
11754
11865
|
const paintForm = (form, activeResources, activeFonts, depth, mcid, actualText, maskGroup = false) => {
|
|
11755
11866
|
const st = curState();
|
|
11756
11867
|
const group = params.cosDoc.resolveDict(dictGet(form.dict, "Group"));
|
|
@@ -11809,7 +11920,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
11809
11920
|
}
|
|
11810
11921
|
const parentOperations = capturedOperations;
|
|
11811
11922
|
const children = [];
|
|
11812
|
-
const nextState = { ...st, ctm: nextCtm };
|
|
11923
|
+
const nextState = { ...st, ctm: nextCtm, initialCtm: nextCtm };
|
|
11813
11924
|
if (compositeGroup) {
|
|
11814
11925
|
capturedOperations = children;
|
|
11815
11926
|
nextState.fillAlpha = nextState.strokeAlpha = 1;
|
|
@@ -11967,136 +12078,15 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
11967
12078
|
}
|
|
11968
12079
|
const isFill = ["f", "F", "f*", "B", "B*", "b", "b*"].includes(node.paint);
|
|
11969
12080
|
const isStroke = ["S", "s", "B", "B*", "b", "b*"].includes(node.paint);
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
|
|
11975
|
-
|
|
11976
|
-
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
for (const s of transformedSegments) {
|
|
11980
|
-
if (s.kind === "move" || s.kind === "line") {
|
|
11981
|
-
pMinX = Math.min(pMinX, s.x);
|
|
11982
|
-
pMinY = Math.min(pMinY, s.y);
|
|
11983
|
-
pMaxX = Math.max(pMaxX, s.x);
|
|
11984
|
-
pMaxY = Math.max(pMaxY, s.y);
|
|
11985
|
-
} else if (s.kind === "cubic") {
|
|
11986
|
-
pMinX = Math.min(pMinX, s.x1, s.x2, s.x);
|
|
11987
|
-
pMinY = Math.min(pMinY, s.y1, s.y2, s.y);
|
|
11988
|
-
pMaxX = Math.max(pMaxX, s.x1, s.x2, s.x);
|
|
11989
|
-
pMaxY = Math.max(pMaxY, s.y1, s.y2, s.y);
|
|
11990
|
-
} else if (s.kind === "rect") {
|
|
11991
|
-
pMinX = Math.min(pMinX, s.x);
|
|
11992
|
-
pMinY = Math.min(pMinY, s.y);
|
|
11993
|
-
pMaxX = Math.max(pMaxX, s.x + s.width);
|
|
11994
|
-
pMaxY = Math.max(pMaxY, s.y + s.height);
|
|
11995
|
-
}
|
|
11996
|
-
}
|
|
11997
|
-
if (Number.isFinite(pMinX) && Number.isFinite(pMinY) && pMaxX > pMinX && pMaxY > pMinY) {
|
|
11998
|
-
const shading = params.cosDoc.resolve(dictGet(patDict, "Shading"));
|
|
11999
|
-
const shDict = shading?.kind === "stream" ? shading.dict : shading?.kind === "dict" ? shading : void 0;
|
|
12000
|
-
if (shDict) {
|
|
12001
|
-
let patMatrix = [1, 0, 0, 1, 0, 0];
|
|
12002
|
-
const pmArr = params.cosDoc.resolveArray(dictGet(patDict, "Matrix"));
|
|
12003
|
-
if (pmArr && pmArr.items.length >= 6) {
|
|
12004
|
-
const mn = (idx, fb = 0) => {
|
|
12005
|
-
const r = params.cosDoc.resolve(pmArr.items[idx]);
|
|
12006
|
-
return r?.kind === "number" ? r.value : fb;
|
|
12007
|
-
};
|
|
12008
|
-
patMatrix = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
|
|
12009
|
-
}
|
|
12010
|
-
const patClip = st.clipRect ? [
|
|
12011
|
-
Math.max(st.clipRect[0], pMinX),
|
|
12012
|
-
Math.max(st.clipRect[1], pMinY),
|
|
12013
|
-
Math.min(st.clipRect[2], pMaxX),
|
|
12014
|
-
Math.min(st.clipRect[3], pMaxY)
|
|
12015
|
-
] : [pMinX, pMinY, pMaxX, pMaxY];
|
|
12016
|
-
const shImg = renderShadingDictToImage(
|
|
12017
|
-
params.cosDoc,
|
|
12018
|
-
shDict,
|
|
12019
|
-
patMatrix,
|
|
12020
|
-
patClip,
|
|
12021
|
-
st.fillAlpha,
|
|
12022
|
-
"PatternShading_" + st.fillPatternName,
|
|
12023
|
-
patClip,
|
|
12024
|
-
shading?.kind === "stream" ? shading : void 0,
|
|
12025
|
-
st.blendMode
|
|
12026
|
-
);
|
|
12027
|
-
if (shImg) {
|
|
12028
|
-
emit({ kind: "image", value: shImg });
|
|
12029
|
-
evaluatedFillPattern = true;
|
|
12030
|
-
}
|
|
12031
|
-
}
|
|
12032
|
-
}
|
|
12033
|
-
} else if (patNode?.kind === "stream") {
|
|
12034
|
-
let pMinX = Infinity, pMinY = Infinity, pMaxX = -Infinity, pMaxY = -Infinity;
|
|
12035
|
-
for (const s of transformedSegments) {
|
|
12036
|
-
if (s.kind === "move" || s.kind === "line") {
|
|
12037
|
-
pMinX = Math.min(pMinX, s.x);
|
|
12038
|
-
pMinY = Math.min(pMinY, s.y);
|
|
12039
|
-
pMaxX = Math.max(pMaxX, s.x);
|
|
12040
|
-
pMaxY = Math.max(pMaxY, s.y);
|
|
12041
|
-
} else if (s.kind === "cubic") {
|
|
12042
|
-
pMinX = Math.min(pMinX, s.x1, s.x2, s.x);
|
|
12043
|
-
pMinY = Math.min(pMinY, s.y1, s.y2, s.y);
|
|
12044
|
-
pMaxX = Math.max(pMaxX, s.x1, s.x2, s.x);
|
|
12045
|
-
pMaxY = Math.max(pMaxY, s.y1, s.y2, s.y);
|
|
12046
|
-
} else if (s.kind === "rect") {
|
|
12047
|
-
pMinX = Math.min(pMinX, s.x);
|
|
12048
|
-
pMinY = Math.min(pMinY, s.y);
|
|
12049
|
-
pMaxX = Math.max(pMaxX, s.x + s.width);
|
|
12050
|
-
pMaxY = Math.max(pMaxY, s.y + s.height);
|
|
12051
|
-
}
|
|
12052
|
-
}
|
|
12053
|
-
if (Number.isFinite(pMinX) && Number.isFinite(pMinY) && pMaxX > pMinX && pMaxY > pMinY) {
|
|
12054
|
-
const xsNode = params.cosDoc.resolve(dictGet(patNode.dict, "XStep"));
|
|
12055
|
-
const ysNode = params.cosDoc.resolve(dictGet(patNode.dict, "YStep"));
|
|
12056
|
-
const xStep = xsNode?.kind === "number" && Math.abs(xsNode.value) > 1e-3 ? Math.abs(xsNode.value) : 40;
|
|
12057
|
-
const yStep = ysNode?.kind === "number" && Math.abs(ysNode.value) > 1e-3 ? Math.abs(ysNode.value) : 40;
|
|
12058
|
-
let patMatrix = [1, 0, 0, 1, 0, 0];
|
|
12059
|
-
const pmArr = params.cosDoc.resolveArray(dictGet(patNode.dict, "Matrix"));
|
|
12060
|
-
if (pmArr && pmArr.items.length >= 6) {
|
|
12061
|
-
const mn = (idx, fb = 0) => {
|
|
12062
|
-
const r = params.cosDoc.resolve(pmArr.items[idx]);
|
|
12063
|
-
return r?.kind === "number" ? r.value : fb;
|
|
12064
|
-
};
|
|
12065
|
-
patMatrix = [mn(0, 1), mn(1, 0), mn(2, 0), mn(3, 1), mn(4, 0), mn(5, 0)];
|
|
12066
|
-
}
|
|
12067
|
-
const tileClip = st.clipRect ? [
|
|
12068
|
-
Math.max(st.clipRect[0], pMinX),
|
|
12069
|
-
Math.max(st.clipRect[1], pMinY),
|
|
12070
|
-
Math.min(st.clipRect[2], pMaxX),
|
|
12071
|
-
Math.min(st.clipRect[3], pMaxY)
|
|
12072
|
-
] : [pMinX, pMinY, pMaxX, pMaxY];
|
|
12073
|
-
const patNodes = parseContentStream(params.cosDoc.decodeStream(patNode));
|
|
12074
|
-
const patResDict = params.cosDoc.resolveDict(dictGet(patNode.dict, "Resources")) ?? activeResources;
|
|
12075
|
-
const patFonts = new Map(activeFonts);
|
|
12076
|
-
for (const [fk, fv] of resolvePageFonts(params.cosDoc, patResDict).entries()) {
|
|
12077
|
-
patFonts.set(fk, fv);
|
|
12078
|
-
}
|
|
12079
|
-
const ixStart = Math.floor(pMinX / xStep);
|
|
12080
|
-
const ixEnd = Math.min(ixStart + 8, Math.ceil(pMaxX / xStep) - 1);
|
|
12081
|
-
const iyStart = Math.floor(pMinY / yStep);
|
|
12082
|
-
const iyEnd = Math.min(iyStart + 8, Math.ceil(pMaxY / yStep) - 1);
|
|
12083
|
-
for (let iy = iyStart; iy <= iyEnd; iy++) {
|
|
12084
|
-
for (let ix = ixStart; ix <= ixEnd; ix++) {
|
|
12085
|
-
const tileCtm = multiplyMatrices([1, 0, 0, 1, ix * xStep, iy * yStep], patMatrix);
|
|
12086
|
-
stateStack.push({
|
|
12087
|
-
...st,
|
|
12088
|
-
fillPatternName: void 0,
|
|
12089
|
-
ctm: tileCtm,
|
|
12090
|
-
clipRect: tileClip
|
|
12091
|
-
});
|
|
12092
|
-
walkNodes(patNodes, mcid, actualText, patResDict, patFonts, depth + 1);
|
|
12093
|
-
if (stateStack.length > 1) stateStack.pop();
|
|
12094
|
-
}
|
|
12095
|
-
}
|
|
12096
|
-
evaluatedFillPattern = true;
|
|
12097
|
-
}
|
|
12098
|
-
}
|
|
12099
|
-
}
|
|
12081
|
+
const evaluatedFillPattern = isFill && paintPattern(
|
|
12082
|
+
transformedSegments,
|
|
12083
|
+
node.paint.includes("*") ? "evenodd" : "nonzero",
|
|
12084
|
+
activeResources,
|
|
12085
|
+
activeFonts,
|
|
12086
|
+
depth,
|
|
12087
|
+
mcid,
|
|
12088
|
+
actualText
|
|
12089
|
+
);
|
|
12100
12090
|
if (evaluatedFillPattern && !isStroke) {
|
|
12101
12091
|
applyClip();
|
|
12102
12092
|
break;
|
|
@@ -12297,10 +12287,19 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
12297
12287
|
if (st.textRenderMode >= 4 && st.textRenderMode <= 7) pendingTextClip.push(...transformedGlyphSegs);
|
|
12298
12288
|
const isFillGlyph = st.textRenderMode === 0 || st.textRenderMode === 2 || st.textRenderMode === 4 || st.textRenderMode === 6;
|
|
12299
12289
|
const isStrokeGlyph = st.textRenderMode === 1 || st.textRenderMode === 2 || st.textRenderMode === 5 || st.textRenderMode === 6;
|
|
12290
|
+
const patterned = isFillGlyph && paintPattern(
|
|
12291
|
+
transformedGlyphSegs,
|
|
12292
|
+
"nonzero",
|
|
12293
|
+
activeResources,
|
|
12294
|
+
activeFonts,
|
|
12295
|
+
depth,
|
|
12296
|
+
mcid,
|
|
12297
|
+
actualText
|
|
12298
|
+
);
|
|
12300
12299
|
const paint = {
|
|
12301
12300
|
segments: transformedGlyphSegs,
|
|
12302
|
-
fillColor: isFillGlyph ? st.fillColor : void 0,
|
|
12303
|
-
fillAlpha: isFillGlyph ? st.fillAlpha : void 0,
|
|
12301
|
+
fillColor: isFillGlyph && !patterned ? st.fillColor : void 0,
|
|
12302
|
+
fillAlpha: isFillGlyph && !patterned ? st.fillAlpha : void 0,
|
|
12304
12303
|
strokeColor: isStrokeGlyph ? st.strokeColor : void 0,
|
|
12305
12304
|
strokeAlpha: isStrokeGlyph ? st.strokeAlpha : void 0,
|
|
12306
12305
|
strokeWidth: isStrokeGlyph ? st.strokeWidth : 0,
|
|
@@ -14443,10 +14442,14 @@ function computePdfBlendRgb(cbR, cbG, cbB, csR, csG, csB, mode) {
|
|
|
14443
14442
|
blendChannelSeparable(cbB, csB, mode)
|
|
14444
14443
|
];
|
|
14445
14444
|
}
|
|
14446
|
-
function blendPixel(rgba, width, height, px, py, color, alpha, blendMode, clipMask) {
|
|
14445
|
+
function blendPixel(rgba, width, height, px, py, color, alpha, blendMode, clipMask, groupAlpha) {
|
|
14447
14446
|
if (px < 0 || py < 0 || px >= width || py >= height || alpha <= 0) return;
|
|
14448
14447
|
const a = Math.min(1, Math.max(0, alpha)) * (clipMask ? clipMask[(py * width + px) * 4 + 3] / 255 : 1);
|
|
14449
14448
|
const idx = (py * width + px) * 4;
|
|
14449
|
+
if (groupAlpha) {
|
|
14450
|
+
const pixel = py * width + px;
|
|
14451
|
+
groupAlpha[pixel] = a + groupAlpha[pixel] * (1 - a);
|
|
14452
|
+
}
|
|
14450
14453
|
const csR = Math.min(1, Math.max(0, color.r));
|
|
14451
14454
|
const csG = Math.min(1, Math.max(0, color.g));
|
|
14452
14455
|
const csB = Math.min(1, Math.max(0, color.b));
|
|
@@ -14578,7 +14581,7 @@ function pathsToEdges(paths, closeSubpaths = false) {
|
|
|
14578
14581
|
}
|
|
14579
14582
|
return edges;
|
|
14580
14583
|
}
|
|
14581
|
-
function fillEdgesScanline4x4(rgba, width, height, edges, color, alpha = 1, fillRule = "nonzero", clipScreen, antialias = true, blendMode, clipMask) {
|
|
14584
|
+
function fillEdgesScanline4x4(rgba, width, height, edges, color, alpha = 1, fillRule = "nonzero", clipScreen, antialias = true, blendMode, clipMask, groupAlpha) {
|
|
14582
14585
|
if (edges.length === 0) return;
|
|
14583
14586
|
let minY = Infinity;
|
|
14584
14587
|
let maxY = -Infinity;
|
|
@@ -14644,7 +14647,7 @@ function fillEdgesScanline4x4(rgba, width, height, edges, color, alpha = 1, fill
|
|
|
14644
14647
|
const count = rowCounts[px];
|
|
14645
14648
|
if (count > 0) {
|
|
14646
14649
|
const cov = antialias ? count / 16 : count >= 8 ? 1 : 0;
|
|
14647
|
-
blendPixel(rgba, width, height, px, py, color, cov * alpha, blendMode, clipMask);
|
|
14650
|
+
blendPixel(rgba, width, height, px, py, color, cov * alpha, blendMode, clipMask, groupAlpha);
|
|
14648
14651
|
}
|
|
14649
14652
|
}
|
|
14650
14653
|
}
|
|
@@ -14723,19 +14726,16 @@ function hasCompositingEffects(operations, includeSoftMasks = false) {
|
|
|
14723
14726
|
return operations.some((operation) => includeSoftMasks && !!operation.value.softMask || !!operation.value.blendMode && operation.value.blendMode !== "Normal" && operation.value.blendMode !== "Compatible" || operation.kind === "group" && hasCompositingEffects(operation.value.operations, includeSoftMasks));
|
|
14724
14727
|
}
|
|
14725
14728
|
function needsGroupBackdrop(group) {
|
|
14726
|
-
return group.isolated === false &&
|
|
14729
|
+
return group.isolated === false && hasCompositingEffects(group.operations, true);
|
|
14727
14730
|
}
|
|
14728
|
-
function
|
|
14729
|
-
const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
|
|
14730
|
-
const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
|
|
14731
|
-
const scaleY = options.dpiY !== void 0 ? options.dpiY / 72 : baseScale;
|
|
14732
|
-
const scale = scaleX;
|
|
14731
|
+
function renderDisplayListLayer(displayList, options, scale, backdrop) {
|
|
14733
14732
|
const [originX, originY] = displayList.origin ?? [0, 0];
|
|
14734
14733
|
const pageTop = originY + displayList.height;
|
|
14735
14734
|
const toScreen = (x, y) => [(x - originX) * scale, (pageTop - y) * scale];
|
|
14736
14735
|
const width = Math.max(1, Math.round(displayList.width * scale));
|
|
14737
14736
|
const height = Math.max(1, Math.round(displayList.height * scale));
|
|
14738
|
-
const rgba = new Uint8Array(width * height * 4);
|
|
14737
|
+
const rgba = backdrop ? backdrop.slice() : new Uint8Array(width * height * 4);
|
|
14738
|
+
const groupAlpha = backdrop ? new Float32Array(width * height) : void 0;
|
|
14739
14739
|
const deferBackground = !options.transparent && hasCompositingEffects(paintOperations(displayList));
|
|
14740
14740
|
const transparent = options.transparent || deferBackground;
|
|
14741
14741
|
const bg = options.background ?? { r: 1, g: 1, b: 1 };
|
|
@@ -14743,7 +14743,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
14743
14743
|
const bgG = transparent ? 0 : Math.round(bg.g * 255);
|
|
14744
14744
|
const bgB = transparent ? 0 : Math.round(bg.b * 255);
|
|
14745
14745
|
const bgA = transparent ? 0 : 255;
|
|
14746
|
-
for (let i = 0; i < width * height; i++) {
|
|
14746
|
+
for (let i = 0; !backdrop && i < width * height; i++) {
|
|
14747
14747
|
rgba[i * 4] = bgR;
|
|
14748
14748
|
rgba[i * 4 + 1] = bgG;
|
|
14749
14749
|
rgba[i * 4 + 2] = bgB;
|
|
@@ -14761,20 +14761,12 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
14761
14761
|
let operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
|
|
14762
14762
|
if (operation.kind === "group") {
|
|
14763
14763
|
const group = operation.value;
|
|
14764
|
-
|
|
14765
|
-
|
|
14766
|
-
|
|
14767
|
-
|
|
14768
|
-
|
|
14769
|
-
|
|
14770
|
-
decodedRgba: rgba.slice(),
|
|
14771
|
-
matrix: [width / scale, 0, 0, height / scale, originX, pageTop - height / scale],
|
|
14772
|
-
colorSpace: "DeviceRGB",
|
|
14773
|
-
bitsPerComponent: 8,
|
|
14774
|
-
...group.bboxClip ? { clipPaths: [group.bboxClip] } : {}
|
|
14775
|
-
} }, ...operations];
|
|
14776
|
-
}
|
|
14777
|
-
const bitmap = renderDisplayListToBitmap({ ...displayList, rotation: 0, operations }, { ...options, scale, dpiX: void 0, dpiY: void 0, cropRect: void 0, useCropBox: false, transparent: true });
|
|
14764
|
+
const bitmap = renderDisplayListLayer(
|
|
14765
|
+
{ ...displayList, operations: group.operations },
|
|
14766
|
+
{ ...options, transparent: true },
|
|
14767
|
+
scale,
|
|
14768
|
+
needsGroupBackdrop(group) ? rgba : void 0
|
|
14769
|
+
);
|
|
14778
14770
|
for (let i = 3; i < bitmap.data.length; i += 4) bitmap.data[i] = Math.round(bitmap.data[i] * group.alpha);
|
|
14779
14771
|
operation = { kind: "image", value: {
|
|
14780
14772
|
name: "TransparencyGroup",
|
|
@@ -14844,7 +14836,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
14844
14836
|
if (path.fillColor) {
|
|
14845
14837
|
const edges = pathsToEdges(segmentsToScreenPaths(path.segments, displayList.height, scale, toScreen), true);
|
|
14846
14838
|
const clipScreen = path.clipRect ? [(path.clipRect[0] - originX) * scale, (pageTop - path.clipRect[3]) * scale, (path.clipRect[2] - originX) * scale, (pageTop - path.clipRect[1]) * scale] : void 0;
|
|
14847
|
-
fillEdgesScanline4x4(rgba, width, height, edges, path.fillColor, path.fillAlpha ?? 1, path.fillRule ?? "nonzero", clipScreen, original.kind === "glyph" ? aaTxt : aaVec, path.blendMode, clipMask);
|
|
14839
|
+
fillEdgesScanline4x4(rgba, width, height, edges, path.fillColor, path.fillAlpha ?? 1, path.fillRule ?? "nonzero", clipScreen, original.kind === "glyph" ? aaTxt : aaVec, path.blendMode, clipMask, groupAlpha);
|
|
14848
14840
|
}
|
|
14849
14841
|
if (path.strokeColor) {
|
|
14850
14842
|
const rawSw = path.strokeWidth * scale * (path.strokeMatrix ? Math.hypot(path.strokeMatrix[0], path.strokeMatrix[1]) : 1);
|
|
@@ -14862,7 +14854,8 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
14862
14854
|
clipScreen,
|
|
14863
14855
|
original.kind === "glyph" ? aaTxt : aaVec,
|
|
14864
14856
|
path.blendMode,
|
|
14865
|
-
clipMask
|
|
14857
|
+
clipMask,
|
|
14858
|
+
groupAlpha
|
|
14866
14859
|
);
|
|
14867
14860
|
}
|
|
14868
14861
|
} else if (operation.kind === "image") {
|
|
@@ -14926,12 +14919,31 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
14926
14919
|
},
|
|
14927
14920
|
sample[3] / 255,
|
|
14928
14921
|
img.blendMode,
|
|
14929
|
-
clipMask
|
|
14922
|
+
clipMask,
|
|
14923
|
+
groupAlpha
|
|
14930
14924
|
);
|
|
14931
14925
|
}
|
|
14932
14926
|
}
|
|
14933
14927
|
}
|
|
14934
14928
|
}
|
|
14929
|
+
if (backdrop && groupAlpha) {
|
|
14930
|
+
for (let i = 0; i < rgba.length; i += 4) {
|
|
14931
|
+
const alpha = groupAlpha[i / 4];
|
|
14932
|
+
if (alpha === 0) {
|
|
14933
|
+
rgba.fill(0, i, i + 4);
|
|
14934
|
+
continue;
|
|
14935
|
+
}
|
|
14936
|
+
const factor = backdrop[i + 3] / 255 * (1 / alpha - 1);
|
|
14937
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
14938
|
+
const color = rgba[i + channel];
|
|
14939
|
+
rgba[i + channel] = Math.max(0, Math.min(
|
|
14940
|
+
255,
|
|
14941
|
+
Math.round(color + (color - backdrop[i + channel]) * factor)
|
|
14942
|
+
));
|
|
14943
|
+
}
|
|
14944
|
+
rgba[i + 3] = Math.round(alpha * 255);
|
|
14945
|
+
}
|
|
14946
|
+
}
|
|
14935
14947
|
if (deferBackground) {
|
|
14936
14948
|
for (let i = 0; i < rgba.length; i += 4) {
|
|
14937
14949
|
const alpha = rgba[i + 3] / 255;
|
|
@@ -14941,7 +14953,14 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
14941
14953
|
rgba[i + 3] = 255;
|
|
14942
14954
|
}
|
|
14943
14955
|
}
|
|
14944
|
-
|
|
14956
|
+
return { width, height, data: rgba };
|
|
14957
|
+
}
|
|
14958
|
+
function renderDisplayListToBitmap(displayList, options = {}) {
|
|
14959
|
+
const baseScale = options.scale ?? (options.dpi ? options.dpi / 72 : 1.5);
|
|
14960
|
+
const scaleX = options.dpiX !== void 0 ? options.dpiX / 72 : baseScale;
|
|
14961
|
+
const scaleY = options.dpiY !== void 0 ? options.dpiY / 72 : baseScale;
|
|
14962
|
+
let result = renderDisplayListLayer(displayList, options, scaleX);
|
|
14963
|
+
const { width, height, data: rgba } = result;
|
|
14945
14964
|
if (Math.abs(scaleX - scaleY) > 1e-6) {
|
|
14946
14965
|
const targetW = Math.max(1, Math.round(displayList.width * scaleX));
|
|
14947
14966
|
const targetH = Math.max(1, Math.round(displayList.height * scaleY));
|
|
@@ -15472,6 +15491,7 @@ var CCITT_WHITE_CODES;
|
|
|
15472
15491
|
var CCITT_BLACK_CODES;
|
|
15473
15492
|
var textEncoder;
|
|
15474
15493
|
var PDF_DOC_ENCODING_MAP;
|
|
15494
|
+
var PDF_DOC_ENCODING_REVERSE;
|
|
15475
15495
|
var CosByteLexer;
|
|
15476
15496
|
var MD5_S;
|
|
15477
15497
|
var MD5_K;
|
|
@@ -39928,6 +39948,9 @@ var init_dist = __esm({
|
|
|
39928
39948
|
160: 8364,
|
|
39929
39949
|
173: 65533
|
|
39930
39950
|
};
|
|
39951
|
+
PDF_DOC_ENCODING_REVERSE = new Map(
|
|
39952
|
+
Array.from({ length: 256 }, (_, byte) => [PDF_DOC_ENCODING_MAP[byte] ?? byte, byte])
|
|
39953
|
+
);
|
|
39931
39954
|
CosByteLexer = class {
|
|
39932
39955
|
constructor(bytes, start = 0, end = bytes.length, maxTokenBytes = Infinity, knownCommands) {
|
|
39933
39956
|
this.knownCommands = knownCommands;
|
|
@@ -40160,6 +40183,7 @@ var init_dist = __esm({
|
|
|
40160
40183
|
const start = this.pos;
|
|
40161
40184
|
this.pos++;
|
|
40162
40185
|
let depth = 1;
|
|
40186
|
+
let recovery;
|
|
40163
40187
|
const out = [];
|
|
40164
40188
|
while (this.pos < this.end && depth > 0) {
|
|
40165
40189
|
const b = this.bytes[this.pos++];
|
|
@@ -40194,7 +40218,15 @@ var init_dist = __esm({
|
|
|
40194
40218
|
out.push(b);
|
|
40195
40219
|
} else if (b === 41) {
|
|
40196
40220
|
depth--;
|
|
40197
|
-
if (depth > 0)
|
|
40221
|
+
if (depth > 0) {
|
|
40222
|
+
const next = this.bytes[this.pos];
|
|
40223
|
+
const afterLine = next === 13 && this.bytes[this.pos + 1] === 10 ? this.pos + 2 : this.pos + 1;
|
|
40224
|
+
const delimiter = this.bytes[afterLine];
|
|
40225
|
+
if (!recovery && this.pos + 2 < this.end && (next === 10 || next === 13) && (delimiter === 47 || delimiter === 62)) {
|
|
40226
|
+
recovery = { offset: this.pos, length: out.length };
|
|
40227
|
+
}
|
|
40228
|
+
out.push(b);
|
|
40229
|
+
}
|
|
40198
40230
|
} else if (b === 13) {
|
|
40199
40231
|
if (this.pos < this.end && this.bytes[this.pos] === 10) this.pos++;
|
|
40200
40232
|
out.push(10);
|
|
@@ -40205,6 +40237,10 @@ var init_dist = __esm({
|
|
|
40205
40237
|
throw new PdfError("E_LIMIT", "PDF literal string exceeds maximum byte length");
|
|
40206
40238
|
}
|
|
40207
40239
|
}
|
|
40240
|
+
if (depth > 0 && recovery) {
|
|
40241
|
+
this.pos = recovery.offset;
|
|
40242
|
+
out.length = recovery.length;
|
|
40243
|
+
}
|
|
40208
40244
|
return {
|
|
40209
40245
|
kind: "string",
|
|
40210
40246
|
bytes: Uint8Array.from(out),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poe-platform/safe-bash",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.782",
|
|
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.
|
|
844
|
+
"@poe-platform/safe-fs": "0.1.782",
|
|
845
845
|
"pdf-lib": "1.17.1",
|
|
846
846
|
"@noble/hashes": "2.4.0",
|
|
847
847
|
"pako": "3.0.1",
|
|
@@ -404,15 +404,16 @@ and intermediate-surface state reset. Bitmap and SVG exports follow
|
|
|
404
404
|
`beginDrawing` in keeping the viewer background outside page blend calculations.
|
|
405
405
|
`src/render/transparency-group.test.ts` adds local opacity/blend/mask cases and
|
|
406
406
|
ports the unchanged `transparency_group.pdf` and `bug1873345.pdf` equality
|
|
407
|
-
fixtures, with independently obtained reference pixels.
|
|
408
|
-
|
|
409
|
-
|
|
407
|
+
fixtures, with independently obtained reference pixels. Non-isolated backdrop
|
|
408
|
+
compositing now uses PDFBox's group-alpha and backdrop-removal algorithm,
|
|
409
|
+
described below.
|
|
410
410
|
|
|
411
411
|
Masked non-isolated Forms with inner blending or soft-mask effects follow
|
|
412
412
|
PDF.js `beginGroup`'s `needsBackdropCopy && inSMaskMode` path. The raster adapter
|
|
413
413
|
copies the existing page within the transformed Form BBox before painting the
|
|
414
|
-
group
|
|
415
|
-
|
|
414
|
+
group. The PDFBox adaptation below extends this to ordinary outer-opacity
|
|
415
|
+
groups and removes the copied backdrop before final compositing.
|
|
416
|
+
`src/render/masked-group-backdrop.test.ts` covers these cases and the
|
|
416
417
|
unchanged `issue13520.pdf` fixture. SVG uses a raster fallback for pages requiring
|
|
417
418
|
this backdrop copy; other Form groups retain their vector output.
|
|
418
419
|
|
|
@@ -504,3 +505,70 @@ or zero when absent. Standard-font widths apply only without an explicit
|
|
|
504
505
|
Widths table. `src/fonts/explicit-widths.test.ts` covers Type1, TrueType and
|
|
505
506
|
Type3 widths with and without ToUnicode, plus the unchanged upstream
|
|
506
507
|
`Type3WordSpacing.pdf` fixture.
|
|
508
|
+
|
|
509
|
+
# Apache PDFBox rendering regressions
|
|
510
|
+
|
|
511
|
+
`src/render/pdfbox-patterns.test.ts` adapts `TestQuality.java` at revision
|
|
512
|
+
`c4d556abc9d5f0cbc486d459c84682321dd38ff4`, retaining its original page,
|
|
513
|
+
100 dpi resolution, pixel coordinates, and assertions. Test fixture provenance
|
|
514
|
+
and checksums are recorded in `src/fixtures/SOURCES.md`. Licensed under
|
|
515
|
+
Apache-2.0; see `licenses/PDFJS-APACHE-2.0.txt`.
|
|
516
|
+
|
|
517
|
+
Source: https://github.com/apache/pdfbox
|
|
518
|
+
|
|
519
|
+
The pattern transform handling in `src/content/evaluator.ts` follows PDFBox
|
|
520
|
+
`TilingPaint` and `PageDrawer`: concatenate the containing stream initial
|
|
521
|
+
matrix with the pattern matrix, then clip the paint to the glyph or path.
|
|
522
|
+
|
|
523
|
+
Relevant upstream NOTICE:
|
|
524
|
+
|
|
525
|
+
Apache PDFBox
|
|
526
|
+
Copyright 2014 The Apache Software Foundation
|
|
527
|
+
|
|
528
|
+
This product includes software developed at
|
|
529
|
+
The Apache Software Foundation (http://www.apache.org/).
|
|
530
|
+
|
|
531
|
+
Based on source code originally developed in the PDFBox and
|
|
532
|
+
FontBox projects.
|
|
533
|
+
|
|
534
|
+
Copyright (c) 2002-2007, www.pdfbox.org
|
|
535
|
+
|
|
536
|
+
Based on source code originally developed in the PaDaF project.
|
|
537
|
+
Copyright (c) 2010 Atos Worldline SAS
|
|
538
|
+
|
|
539
|
+
`src/pdfbox-strings.test.ts` adapts `TestCOSString` and
|
|
540
|
+
`PDFDocEncodingTest` at the same PDFBox revision, retaining the Unicode inputs
|
|
541
|
+
and expected round trips, including all 256 PDFBOX-3864 characters. Local COS
|
|
542
|
+
nodes preserve byte representation, so decoded strings replace Java COSString
|
|
543
|
+
value equality. `src/ast.ts` follows `COSString` in using PDFDocEncoding only
|
|
544
|
+
for representable characters and UTF-16BE otherwise. The reverse encoding map
|
|
545
|
+
is derived from the existing decoder table. Apache-2.0, ASF notice above.
|
|
546
|
+
|
|
547
|
+
`src/cos/pdfbox-lexer.test.ts` adapts `TestCOSParser` and `TestCOSNumber`,
|
|
548
|
+
keeping the upstream literal, name, and numeric expectations through the local
|
|
549
|
+
token API. Java-specific number range and object identity checks are omitted.
|
|
550
|
+
Literal recovery in `src/cos/lexer.ts` follows `BaseParser.checkForEndOfString`;
|
|
551
|
+
the adapter defers the recovery until EOF to preserve balanced multiline
|
|
552
|
+
strings. Additional local cases check following fields and token budgets.
|
|
553
|
+
|
|
554
|
+
`src/edit/pdfbox-forms.test.ts` adapts `TestRadioButtons.testRadioButtonPDModel`
|
|
555
|
+
and the PDFBOX-4366/PDFBOX-6207 checkbox regressions. In-memory COS dictionaries
|
|
556
|
+
replace PDFBox model builders; field values and widget appearance states retain
|
|
557
|
+
the original expectations. Empty dictionaries stand in for unused appearances.
|
|
558
|
+
`src/render/pdfbox-blend-mode.test.ts` ports all four
|
|
559
|
+
`TestPDFRendererBlendMode` cases, preserving page dimensions, sample coordinates,
|
|
560
|
+
and expected blue/white pixels. Vitest replaces JUnit; bitmap RGBA values replace
|
|
561
|
+
Java packed ARGB integers. Both use the same PDFBox revision and ASF license.
|
|
562
|
+
|
|
563
|
+
Non-isolated group compositing in `src/render/raster.ts` adapts PDFBox
|
|
564
|
+
`PageDrawer.TransparencyGroup` and `GroupGraphics.removeBackdrop` at the same
|
|
565
|
+
revision. Each paint accumulates group-only alpha separately from its color
|
|
566
|
+
surface, which starts with the parent backdrop. Backdrop removal retains inner
|
|
567
|
+
blend effects and restores group alpha before applying outer opacity and masks.
|
|
568
|
+
The local implementation uses a Float32 alpha plane and straight RGBA bytes,
|
|
569
|
+
with a raster SVG fallback for affected pages. `transparency-group.test.ts`
|
|
570
|
+
checks opaque and partial backdrops, nested opacity, masks, overlapping paints,
|
|
571
|
+
strokes, images, untouched pixels, and normal/incremental/copied-page saves.
|
|
572
|
+
Opaque RGB expectations were independently checked using Poppler 26.08.0's
|
|
573
|
+
Splash renderer; transparent expectations also check the PDF compositing
|
|
574
|
+
formula. ASF, Apache-2.0, notices and license above.
|