@poe-platform/safe-bash 0.1.757 → 0.1.758
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 +72 -19
- package/dist/image-ast/index.js +72 -19
- package/dist/pdf-ast/ast.d.ts +3 -0
- package/dist/pdf-ast/index.js +88 -36
- package/dist/safe-bash/command-chunks/{chunk-F3J5ZLKF.js → chunk-522TDVLS.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-VFS2MDAB.js → chunk-6YLD37NH.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-YI3GKJ7Q.js → chunk-766IWMTB.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-KSH2F6RG.js → chunk-CUFCNL6G.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-S4MLIYSN.js → chunk-J2NPH7DO.js} +4 -0
- package/dist/safe-bash/command-chunks/{chunk-KBBI4CSU.js → chunk-L2C5TIII.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-H2FQA4OU.js → chunk-TCAGGM44.js} +72 -19
- package/dist/safe-bash/command-chunks/{chunk-WI35236J.js → chunk-XYCWDGOG.js} +1 -1
- package/dist/safe-bash/command-chunks/{chunk-4F6PG2OT.js → chunk-YJWWRCIG.js} +8 -8
- package/dist/safe-bash-command-column/column.js +3 -3
- package/dist/safe-bash-command-column/index.js +3 -3
- package/dist/safe-bash-command-column/layout.js +2 -2
- package/dist/safe-bash-command-factor/command.js +2 -2
- package/dist/safe-bash-command-factor/index.js +2 -2
- package/dist/safe-bash-command-ffmpeg/index.js +5923 -5893
- 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 +72 -19
- package/dist/safe-bash-command-pdfinfo/index.js +88 -36
- package/dist/safe-bash-command-pdftk/index.js +72 -19
- package/dist/safe-bash-command-pdftoppm/index.js +88 -36
- package/dist/safe-bash-command-pdftotext/index.js +72 -19
- package/dist/safe-bash-command-qpdf/index.js +72 -19
- package/dist/safe-bash-command-sips/index.js +2 -2
- package/dist/safe-bash-command-soffice/index.js +2 -2
- package/dist/safe-bash-command-ssconvert/command-bindings.d.ts +18 -0
- package/dist/safe-bash-command-ssconvert/command.d.ts +5 -14
- package/dist/safe-bash-command-ssconvert/commands.d.ts +6 -0
- package/dist/safe-bash-command-ssconvert/commands.js +871 -0
- package/dist/safe-bash-command-ssconvert/core.js +1 -1
- package/dist/safe-bash-command-ssconvert/index.js +269 -209
- package/dist/safe-bash-command-wkhtmltopdf/index.js +72 -19
- package/dist/safe-bash-command-yq/index.js +4 -4
- package/dist/safe-bash-compression-engine/files.js +2 -2
- package/dist/safe-bash-compression-engine/operand.js +4 -4
- package/dist/safe-bash-markdown-engine/commonmark-blocks.js +4 -4
- package/dist/safe-bash-markdown-engine/commonmark-inlines.js +4 -4
- package/dist/safe-bash-markdown-engine/commonmark-syntax.js +2 -2
- package/dist/safe-bash-markdown-engine/gfm-syntax.js +3 -3
- package/dist/safe-bash-markdown-engine/index.js +7 -7
- package/dist/safe-bash-query-engine/index.js +5 -5
- package/dist/safe-bash-query-engine/interpreter.js +4 -4
- package/dist/safe-bash-query-engine/query-core.js +5 -5
- package/package.json +6 -2
- package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +16 -0
- package/dist/safe-bash/command-chunks/{chunk-OQVIJMBY.js → chunk-7HINP7ZK.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-XAYEDOV5.js → chunk-CG4JSOK2.js} +3 -3
- package/dist/safe-bash/command-chunks/{chunk-YIK4ZYQ4.js → chunk-DC52GWRK.js} +9 -9
- package/dist/safe-bash/command-chunks/{chunk-TOTZFBDT.js → chunk-TKACIO4X.js} +4 -4
- package/dist/safe-bash/command-chunks/{chunk-VTWKS4RM.js → chunk-WF2LSKMI.js} +3 -3
|
@@ -28,8 +28,8 @@ import {
|
|
|
28
28
|
thresholdImage,
|
|
29
29
|
tintImage,
|
|
30
30
|
trimImage
|
|
31
|
-
} from "../safe-bash/command-chunks/chunk-
|
|
32
|
-
import "../safe-bash/command-chunks/chunk-
|
|
31
|
+
} from "../safe-bash/command-chunks/chunk-XYCWDGOG.js";
|
|
32
|
+
import "../safe-bash/command-chunks/chunk-TCAGGM44.js";
|
|
33
33
|
import {
|
|
34
34
|
init_buffer
|
|
35
35
|
} from "../safe-bash/command-chunks/chunk-HX3SHTCE.js";
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
createPandocCommand,
|
|
3
3
|
createPandocCommands,
|
|
4
4
|
pandocCommands
|
|
5
|
-
} from "../safe-bash/command-chunks/chunk-
|
|
5
|
+
} from "../safe-bash/command-chunks/chunk-L2C5TIII.js";
|
|
6
6
|
import "../safe-bash/command-chunks/chunk-MKQVQMXO.js";
|
|
7
7
|
import "../safe-bash/command-chunks/chunk-RDV7UCYZ.js";
|
|
8
|
-
import "../safe-bash/command-chunks/chunk-
|
|
9
|
-
import "../safe-bash/command-chunks/chunk-
|
|
8
|
+
import "../safe-bash/command-chunks/chunk-XYCWDGOG.js";
|
|
9
|
+
import "../safe-bash/command-chunks/chunk-TCAGGM44.js";
|
|
10
10
|
import "../safe-bash/command-chunks/chunk-Y2KHC6TZ.js";
|
|
11
11
|
import "../safe-bash/command-chunks/chunk-YGYOVXTO.js";
|
|
12
12
|
import "../safe-bash/command-chunks/chunk-I5GLTNVH.js";
|
|
@@ -21,11 +21,11 @@ import {
|
|
|
21
21
|
readDocument,
|
|
22
22
|
resolveConversionArgs,
|
|
23
23
|
writeDocument
|
|
24
|
-
} from "../safe-bash/command-chunks/chunk-
|
|
24
|
+
} from "../safe-bash/command-chunks/chunk-L2C5TIII.js";
|
|
25
25
|
import "../safe-bash/command-chunks/chunk-MKQVQMXO.js";
|
|
26
26
|
import "../safe-bash/command-chunks/chunk-RDV7UCYZ.js";
|
|
27
|
-
import "../safe-bash/command-chunks/chunk-
|
|
28
|
-
import "../safe-bash/command-chunks/chunk-
|
|
27
|
+
import "../safe-bash/command-chunks/chunk-XYCWDGOG.js";
|
|
28
|
+
import "../safe-bash/command-chunks/chunk-TCAGGM44.js";
|
|
29
29
|
import {
|
|
30
30
|
createLuaFilterCapability
|
|
31
31
|
} from "../safe-bash/command-chunks/chunk-Y2KHC6TZ.js";
|
|
@@ -25274,7 +25274,7 @@ var ParsedCosDocument = class {
|
|
|
25274
25274
|
return void 0;
|
|
25275
25275
|
}
|
|
25276
25276
|
};
|
|
25277
|
-
function parseNodeFromLexer(lexer, bytes, maxDepth,
|
|
25277
|
+
function parseNodeFromLexer(lexer, bytes, maxDepth, repair = false) {
|
|
25278
25278
|
const stack = [];
|
|
25279
25279
|
while (true) {
|
|
25280
25280
|
const tok = lexer.nextToken();
|
|
@@ -25283,7 +25283,7 @@ function parseNodeFromLexer(lexer, bytes, maxDepth, recoverEmptyObject = false)
|
|
|
25283
25283
|
if (!parent) return void 0;
|
|
25284
25284
|
throw new PdfError("E_PARSE", parent.kind === "array" ? "Unterminated PDF array" : "Unterminated PDF dictionary");
|
|
25285
25285
|
}
|
|
25286
|
-
if (!parent &&
|
|
25286
|
+
if (!parent && repair && tok.kind === "keyword" && tok.value === "endobj") {
|
|
25287
25287
|
return { kind: "null", span: tok.span };
|
|
25288
25288
|
}
|
|
25289
25289
|
let node;
|
|
@@ -25298,6 +25298,7 @@ function parseNodeFromLexer(lexer, bytes, maxDepth, recoverEmptyObject = false)
|
|
|
25298
25298
|
parent.key = { kind: "name", rawBytes: tok.rawBytes, decoded: tok.decoded, span: tok.span };
|
|
25299
25299
|
continue;
|
|
25300
25300
|
} else {
|
|
25301
|
+
if (repair) continue;
|
|
25301
25302
|
throw new PdfError("E_PARSE", `Expected dictionary key /Name, got ${tok.kind}`);
|
|
25302
25303
|
}
|
|
25303
25304
|
} else {
|
|
@@ -25466,7 +25467,7 @@ function parseHeaderVersion(bytes) {
|
|
|
25466
25467
|
}
|
|
25467
25468
|
return head.slice(idx + 5, idx + 8);
|
|
25468
25469
|
}
|
|
25469
|
-
function parseObjectAtOffset(bytes, offset, maxRecursionDepth,
|
|
25470
|
+
function parseObjectAtOffset(bytes, offset, maxRecursionDepth, repair = false) {
|
|
25470
25471
|
const lexer = new CosByteLexer(bytes, offset);
|
|
25471
25472
|
const objNumTok = lexer.nextToken();
|
|
25472
25473
|
const genNumTok = lexer.nextToken();
|
|
@@ -25474,7 +25475,7 @@ function parseObjectAtOffset(bytes, offset, maxRecursionDepth, recoverEmptyObjec
|
|
|
25474
25475
|
if (objNumTok?.kind !== "number" || genNumTok?.kind !== "number" || objKwTok?.kind !== "keyword" || objKwTok.value !== "obj") {
|
|
25475
25476
|
throw new PdfError("E_PARSE", `Malformed indirect object header at byte offset ${offset}`);
|
|
25476
25477
|
}
|
|
25477
|
-
const value = parseNodeFromLexer(lexer, bytes, maxRecursionDepth,
|
|
25478
|
+
const value = parseNodeFromLexer(lexer, bytes, maxRecursionDepth, repair);
|
|
25478
25479
|
if (!value) {
|
|
25479
25480
|
throw new PdfError("E_PARSE", `Empty indirect object ${objNumTok.value} at offset ${offset}`);
|
|
25480
25481
|
}
|
|
@@ -25663,7 +25664,7 @@ function repairScanCosDocument(bytes, maxObjects, maxRecursionDepth) {
|
|
|
25663
25664
|
try {
|
|
25664
25665
|
const token = lexer.nextToken();
|
|
25665
25666
|
if (token?.kind === "keyword" && token.value === "trailer") {
|
|
25666
|
-
const trailer = parseNodeFromLexer(lexer, bytes, maxRecursionDepth);
|
|
25667
|
+
const trailer = parseNodeFromLexer(lexer, bytes, maxRecursionDepth, true);
|
|
25667
25668
|
if (trailer?.kind === "dict") trailers.push(trailer);
|
|
25668
25669
|
pos = lexer.offset;
|
|
25669
25670
|
continue;
|
|
@@ -25719,7 +25720,7 @@ function unpackRecoveredObjects(objects, maxObjects, maxDecompressedBytes, maxRe
|
|
|
25719
25720
|
const t = dictGet(obj.value.dict, "Type");
|
|
25720
25721
|
if (t?.kind === "name" && t.decoded === "ObjStm") {
|
|
25721
25722
|
try {
|
|
25722
|
-
for (const [unpackedNum, unpackedVal] of unpackObjectStream(obj.value, maxDecompressedBytes, maxRecursionDepth).entries()) {
|
|
25723
|
+
for (const [unpackedNum, unpackedVal] of unpackObjectStream(obj.value, maxDecompressedBytes, maxRecursionDepth, true).entries()) {
|
|
25723
25724
|
if (!objects.has(unpackedNum)) {
|
|
25724
25725
|
objects.set(unpackedNum, {
|
|
25725
25726
|
objectNumber: unpackedNum,
|
|
@@ -25755,7 +25756,7 @@ function recoveredReferences(objects) {
|
|
|
25755
25756
|
}
|
|
25756
25757
|
return { ...rootRef ? { rootRef } : {}, ...infoRef ? { infoRef } : {} };
|
|
25757
25758
|
}
|
|
25758
|
-
function unpackObjectStream(streamObj, maxDecompressedBytes, maxRecursionDepth) {
|
|
25759
|
+
function unpackObjectStream(streamObj, maxDecompressedBytes, maxRecursionDepth, repair = false) {
|
|
25759
25760
|
const nNode = dictGet(streamObj.dict, "N");
|
|
25760
25761
|
const firstNode = dictGet(streamObj.dict, "First");
|
|
25761
25762
|
if (nNode?.kind !== "number" || firstNode?.kind !== "number") {
|
|
@@ -25775,7 +25776,7 @@ function unpackObjectStream(streamObj, maxDecompressedBytes, maxRecursionDepth)
|
|
|
25775
25776
|
for (const pair of pairs) {
|
|
25776
25777
|
try {
|
|
25777
25778
|
const valLexer = new CosByteLexer(decoded, firstNode.value + pair.relativeOffset);
|
|
25778
|
-
const node = parseNodeFromLexer(valLexer, decoded, maxRecursionDepth);
|
|
25779
|
+
const node = parseNodeFromLexer(valLexer, decoded, maxRecursionDepth, repair);
|
|
25779
25780
|
if (node) {
|
|
25780
25781
|
result.set(pair.objectNumber, node);
|
|
25781
25782
|
}
|
|
@@ -25958,7 +25959,7 @@ function parseCosDocument(bytes, options = {}) {
|
|
|
25958
25959
|
if (!unpacked) {
|
|
25959
25960
|
const stmObj = objects.get(entry.objectStreamNumber)?.value;
|
|
25960
25961
|
if (stmObj?.kind === "stream") {
|
|
25961
|
-
unpacked = unpackObjectStream(stmObj, maxDecompressedBytes, maxRecursionDepth);
|
|
25962
|
+
unpacked = unpackObjectStream(stmObj, maxDecompressedBytes, maxRecursionDepth, recovery === "repair");
|
|
25962
25963
|
objStmCache.set(entry.objectStreamNumber, unpacked);
|
|
25963
25964
|
}
|
|
25964
25965
|
}
|
|
@@ -34775,7 +34776,11 @@ function resolvePageFonts(doc, resourcesDict) {
|
|
|
34775
34776
|
let cmap;
|
|
34776
34777
|
const toUniNode = doc.resolve(dictGet(fObj, "ToUnicode"));
|
|
34777
34778
|
if (toUniNode?.kind === "stream") {
|
|
34778
|
-
|
|
34779
|
+
try {
|
|
34780
|
+
cmap = parseToUnicodeCMap(doc.decodeStream(toUniNode));
|
|
34781
|
+
} catch (error) {
|
|
34782
|
+
if (error instanceof PdfError && error.code === "E_LIMIT") throw error;
|
|
34783
|
+
}
|
|
34779
34784
|
}
|
|
34780
34785
|
const encNode = doc.resolve(dictGet(fObj, "Encoding"));
|
|
34781
34786
|
if (encNode?.kind === "dict") {
|
|
@@ -35899,8 +35904,12 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
35899
35904
|
const images = [];
|
|
35900
35905
|
const operations = [];
|
|
35901
35906
|
const emit = (operation) => {
|
|
35902
|
-
const clipPaths = curState()
|
|
35903
|
-
if (clipPaths) operation = { ...operation, value: {
|
|
35907
|
+
const { clipPaths, clipImages } = curState();
|
|
35908
|
+
if (clipPaths || clipImages) operation = { ...operation, value: {
|
|
35909
|
+
...operation.value,
|
|
35910
|
+
...clipPaths ? { clipPaths } : {},
|
|
35911
|
+
...clipImages ? { clipImages } : {}
|
|
35912
|
+
} };
|
|
35904
35913
|
operations.push(operation);
|
|
35905
35914
|
switch (operation.kind) {
|
|
35906
35915
|
case "path":
|
|
@@ -36164,6 +36173,18 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36164
36173
|
let hasTextClip = false;
|
|
36165
36174
|
let activeTm = [1, 0, 0, 1, 0, 0];
|
|
36166
36175
|
let activeTlm = [1, 0, 0, 1, 0, 0];
|
|
36176
|
+
const paintImage = (image, patternMask, resources, activeFonts, depth) => {
|
|
36177
|
+
if (!patternMask) {
|
|
36178
|
+
emit({ kind: "image", value: image });
|
|
36179
|
+
return;
|
|
36180
|
+
}
|
|
36181
|
+
const st = curState();
|
|
36182
|
+
stateStack.push({ ...st, clipImages: [...st.clipImages ?? [], image] });
|
|
36183
|
+
walkNodes([{ kind: "path-op", paint: "f", segments: [
|
|
36184
|
+
{ kind: "rect", x: 0, y: 0, width: 1, height: 1 }
|
|
36185
|
+
] }], void 0, void 0, resources, activeFonts, depth);
|
|
36186
|
+
stateStack.pop();
|
|
36187
|
+
};
|
|
36167
36188
|
const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
|
|
36168
36189
|
for (const node of nodes) {
|
|
36169
36190
|
switch (node.kind) {
|
|
@@ -36347,7 +36368,8 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36347
36368
|
st.fillAlpha,
|
|
36348
36369
|
"PatternShading_" + st.fillPatternName,
|
|
36349
36370
|
patClip,
|
|
36350
|
-
shading?.kind === "stream" ? shading : void 0
|
|
36371
|
+
shading?.kind === "stream" ? shading : void 0,
|
|
36372
|
+
st.blendMode
|
|
36351
36373
|
);
|
|
36352
36374
|
if (shImg) {
|
|
36353
36375
|
emit({ kind: "image", value: shImg });
|
|
@@ -36456,18 +36478,20 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36456
36478
|
const subNode = params.cosDoc.resolve(dictGet(xobjNode.dict, "Subtype"));
|
|
36457
36479
|
const sub = subNode?.kind === "name" ? subNode.decoded : "";
|
|
36458
36480
|
if (sub === "Image") {
|
|
36481
|
+
const maskNode = params.cosDoc.resolve(dictGet(xobjNode.dict, "ImageMask"));
|
|
36482
|
+
const patternMask = !!st.fillPatternName && maskNode?.kind === "boolean" && maskNode.value;
|
|
36459
36483
|
const decoded = decodeXObjectImageToRgba(
|
|
36460
36484
|
params.cosDoc,
|
|
36461
36485
|
xobjNode,
|
|
36462
36486
|
activeResources,
|
|
36463
|
-
{
|
|
36487
|
+
patternMask ? { r: 1, g: 1, b: 1, alpha: 1 } : {
|
|
36464
36488
|
r: st.fillColor.r,
|
|
36465
36489
|
g: st.fillColor.g,
|
|
36466
36490
|
b: st.fillColor.b,
|
|
36467
36491
|
alpha: st.fillAlpha
|
|
36468
36492
|
}
|
|
36469
36493
|
);
|
|
36470
|
-
|
|
36494
|
+
paintImage({
|
|
36471
36495
|
name: node.name,
|
|
36472
36496
|
matrix: [...st.ctm],
|
|
36473
36497
|
width: decoded.width,
|
|
@@ -36477,7 +36501,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36477
36501
|
decodedRgba: decoded.rgba,
|
|
36478
36502
|
...st.blendMode && st.blendMode !== "Normal" ? { blendMode: st.blendMode } : {},
|
|
36479
36503
|
...st.clipRect ? { clipRect: [...st.clipRect] } : {}
|
|
36480
|
-
}
|
|
36504
|
+
}, patternMask, activeResources, activeFonts, depth);
|
|
36481
36505
|
} else if (sub === "Form" && depth < 8) {
|
|
36482
36506
|
const formStreamBytes = params.cosDoc.decodeStream(xobjNode);
|
|
36483
36507
|
const formNodes = parseContentStream(formStreamBytes);
|
|
@@ -36533,19 +36557,22 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36533
36557
|
}
|
|
36534
36558
|
case "inline-image": {
|
|
36535
36559
|
const st = curState();
|
|
36560
|
+
const maskEntry = dictGet(node.dict, "ImageMask") ?? dictGet(node.dict, "IM");
|
|
36561
|
+
const maskNode = params.cosDoc ? params.cosDoc.resolve(maskEntry) : maskEntry;
|
|
36562
|
+
const patternMask = !!st.fillPatternName && maskNode?.kind === "boolean" && maskNode.value;
|
|
36536
36563
|
const decoded = decodeInlineImageNodeToRgba(
|
|
36537
36564
|
params.cosDoc,
|
|
36538
36565
|
node.dict,
|
|
36539
36566
|
node.data,
|
|
36540
36567
|
activeResources,
|
|
36541
|
-
{
|
|
36568
|
+
patternMask ? { r: 1, g: 1, b: 1, alpha: 1 } : {
|
|
36542
36569
|
r: st.fillColor.r,
|
|
36543
36570
|
g: st.fillColor.g,
|
|
36544
36571
|
b: st.fillColor.b,
|
|
36545
36572
|
alpha: st.fillAlpha
|
|
36546
36573
|
}
|
|
36547
36574
|
);
|
|
36548
|
-
|
|
36575
|
+
paintImage({
|
|
36549
36576
|
name: "InlineImage",
|
|
36550
36577
|
matrix: [...st.ctm],
|
|
36551
36578
|
width: decoded.width,
|
|
@@ -36555,7 +36582,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36555
36582
|
decodedRgba: decoded.rgba,
|
|
36556
36583
|
...st.blendMode && st.blendMode !== "Normal" ? { blendMode: st.blendMode } : {},
|
|
36557
36584
|
...st.clipRect ? { clipRect: [...st.clipRect] } : {}
|
|
36558
|
-
}
|
|
36585
|
+
}, patternMask, activeResources, activeFonts, depth);
|
|
36559
36586
|
break;
|
|
36560
36587
|
}
|
|
36561
36588
|
case "text-object": {
|
|
@@ -40059,6 +40086,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
40059
40086
|
const aaVec = options.antialiasVector !== false;
|
|
40060
40087
|
const aaTxt = options.antialiasText !== false;
|
|
40061
40088
|
const clipMasks = /* @__PURE__ */ new Map();
|
|
40089
|
+
const imageClipMasks = /* @__PURE__ */ new Map();
|
|
40062
40090
|
for (const original of paintOperations(displayList)) {
|
|
40063
40091
|
if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
|
|
40064
40092
|
const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
|
|
@@ -40073,6 +40101,31 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
40073
40101
|
}
|
|
40074
40102
|
clipMasks.set(clips, clipMask);
|
|
40075
40103
|
}
|
|
40104
|
+
const imageClips = original.value.clipImages;
|
|
40105
|
+
if (imageClips) {
|
|
40106
|
+
let imageMask = imageClipMasks.get(imageClips);
|
|
40107
|
+
if (!imageMask) {
|
|
40108
|
+
imageMask = new Uint8Array(width * height * 4).fill(255);
|
|
40109
|
+
for (const image of imageClips) {
|
|
40110
|
+
const layer = renderDisplayListToBitmap({
|
|
40111
|
+
...displayList,
|
|
40112
|
+
rotation: 0,
|
|
40113
|
+
paths: [],
|
|
40114
|
+
glyphs: [],
|
|
40115
|
+
images: [image],
|
|
40116
|
+
operations: [{ kind: "image", value: image }]
|
|
40117
|
+
}, { scale, transparent: true }).data;
|
|
40118
|
+
for (let i = 3; i < layer.length; i += 4) imageMask[i] = Math.round(imageMask[i] * layer[i] / 255);
|
|
40119
|
+
}
|
|
40120
|
+
imageClipMasks.set(imageClips, imageMask);
|
|
40121
|
+
}
|
|
40122
|
+
if (clipMask) {
|
|
40123
|
+
clipMask = clipMask.slice();
|
|
40124
|
+
for (let i = 3; i < clipMask.length; i += 4) clipMask[i] = Math.round(clipMask[i] * imageMask[i] / 255);
|
|
40125
|
+
} else {
|
|
40126
|
+
clipMask = imageMask;
|
|
40127
|
+
}
|
|
40128
|
+
}
|
|
40076
40129
|
if (operation.kind === "path") {
|
|
40077
40130
|
const path = operation.value;
|
|
40078
40131
|
if (path.fillColor) {
|
|
@@ -25468,7 +25468,7 @@ var ParsedCosDocument = class {
|
|
|
25468
25468
|
return void 0;
|
|
25469
25469
|
}
|
|
25470
25470
|
};
|
|
25471
|
-
function parseNodeFromLexer(lexer, bytes, maxDepth,
|
|
25471
|
+
function parseNodeFromLexer(lexer, bytes, maxDepth, repair = false) {
|
|
25472
25472
|
const stack = [];
|
|
25473
25473
|
while (true) {
|
|
25474
25474
|
const tok = lexer.nextToken();
|
|
@@ -25477,7 +25477,7 @@ function parseNodeFromLexer(lexer, bytes, maxDepth, recoverEmptyObject = false)
|
|
|
25477
25477
|
if (!parent) return void 0;
|
|
25478
25478
|
throw new PdfError("E_PARSE", parent.kind === "array" ? "Unterminated PDF array" : "Unterminated PDF dictionary");
|
|
25479
25479
|
}
|
|
25480
|
-
if (!parent &&
|
|
25480
|
+
if (!parent && repair && tok.kind === "keyword" && tok.value === "endobj") {
|
|
25481
25481
|
return { kind: "null", span: tok.span };
|
|
25482
25482
|
}
|
|
25483
25483
|
let node;
|
|
@@ -25492,6 +25492,7 @@ function parseNodeFromLexer(lexer, bytes, maxDepth, recoverEmptyObject = false)
|
|
|
25492
25492
|
parent.key = { kind: "name", rawBytes: tok.rawBytes, decoded: tok.decoded, span: tok.span };
|
|
25493
25493
|
continue;
|
|
25494
25494
|
} else {
|
|
25495
|
+
if (repair) continue;
|
|
25495
25496
|
throw new PdfError("E_PARSE", `Expected dictionary key /Name, got ${tok.kind}`);
|
|
25496
25497
|
}
|
|
25497
25498
|
} else {
|
|
@@ -25660,7 +25661,7 @@ function parseHeaderVersion(bytes) {
|
|
|
25660
25661
|
}
|
|
25661
25662
|
return head.slice(idx + 5, idx + 8);
|
|
25662
25663
|
}
|
|
25663
|
-
function parseObjectAtOffset(bytes, offset, maxRecursionDepth,
|
|
25664
|
+
function parseObjectAtOffset(bytes, offset, maxRecursionDepth, repair = false) {
|
|
25664
25665
|
const lexer = new CosByteLexer(bytes, offset);
|
|
25665
25666
|
const objNumTok = lexer.nextToken();
|
|
25666
25667
|
const genNumTok = lexer.nextToken();
|
|
@@ -25668,7 +25669,7 @@ function parseObjectAtOffset(bytes, offset, maxRecursionDepth, recoverEmptyObjec
|
|
|
25668
25669
|
if (objNumTok?.kind !== "number" || genNumTok?.kind !== "number" || objKwTok?.kind !== "keyword" || objKwTok.value !== "obj") {
|
|
25669
25670
|
throw new PdfError("E_PARSE", `Malformed indirect object header at byte offset ${offset}`);
|
|
25670
25671
|
}
|
|
25671
|
-
const value = parseNodeFromLexer(lexer, bytes, maxRecursionDepth,
|
|
25672
|
+
const value = parseNodeFromLexer(lexer, bytes, maxRecursionDepth, repair);
|
|
25672
25673
|
if (!value) {
|
|
25673
25674
|
throw new PdfError("E_PARSE", `Empty indirect object ${objNumTok.value} at offset ${offset}`);
|
|
25674
25675
|
}
|
|
@@ -25857,7 +25858,7 @@ function repairScanCosDocument(bytes, maxObjects, maxRecursionDepth) {
|
|
|
25857
25858
|
try {
|
|
25858
25859
|
const token = lexer.nextToken();
|
|
25859
25860
|
if (token?.kind === "keyword" && token.value === "trailer") {
|
|
25860
|
-
const trailer = parseNodeFromLexer(lexer, bytes, maxRecursionDepth);
|
|
25861
|
+
const trailer = parseNodeFromLexer(lexer, bytes, maxRecursionDepth, true);
|
|
25861
25862
|
if (trailer?.kind === "dict") trailers.push(trailer);
|
|
25862
25863
|
pos = lexer.offset;
|
|
25863
25864
|
continue;
|
|
@@ -25913,7 +25914,7 @@ function unpackRecoveredObjects(objects, maxObjects, maxDecompressedBytes, maxRe
|
|
|
25913
25914
|
const t = dictGet(obj.value.dict, "Type");
|
|
25914
25915
|
if (t?.kind === "name" && t.decoded === "ObjStm") {
|
|
25915
25916
|
try {
|
|
25916
|
-
for (const [unpackedNum, unpackedVal] of unpackObjectStream(obj.value, maxDecompressedBytes, maxRecursionDepth).entries()) {
|
|
25917
|
+
for (const [unpackedNum, unpackedVal] of unpackObjectStream(obj.value, maxDecompressedBytes, maxRecursionDepth, true).entries()) {
|
|
25917
25918
|
if (!objects.has(unpackedNum)) {
|
|
25918
25919
|
objects.set(unpackedNum, {
|
|
25919
25920
|
objectNumber: unpackedNum,
|
|
@@ -25949,7 +25950,7 @@ function recoveredReferences(objects) {
|
|
|
25949
25950
|
}
|
|
25950
25951
|
return { ...rootRef ? { rootRef } : {}, ...infoRef ? { infoRef } : {} };
|
|
25951
25952
|
}
|
|
25952
|
-
function unpackObjectStream(streamObj, maxDecompressedBytes, maxRecursionDepth) {
|
|
25953
|
+
function unpackObjectStream(streamObj, maxDecompressedBytes, maxRecursionDepth, repair = false) {
|
|
25953
25954
|
const nNode = dictGet(streamObj.dict, "N");
|
|
25954
25955
|
const firstNode = dictGet(streamObj.dict, "First");
|
|
25955
25956
|
if (nNode?.kind !== "number" || firstNode?.kind !== "number") {
|
|
@@ -25969,7 +25970,7 @@ function unpackObjectStream(streamObj, maxDecompressedBytes, maxRecursionDepth)
|
|
|
25969
25970
|
for (const pair of pairs) {
|
|
25970
25971
|
try {
|
|
25971
25972
|
const valLexer = new CosByteLexer(decoded, firstNode.value + pair.relativeOffset);
|
|
25972
|
-
const node = parseNodeFromLexer(valLexer, decoded, maxRecursionDepth);
|
|
25973
|
+
const node = parseNodeFromLexer(valLexer, decoded, maxRecursionDepth, repair);
|
|
25973
25974
|
if (node) {
|
|
25974
25975
|
result.set(pair.objectNumber, node);
|
|
25975
25976
|
}
|
|
@@ -26152,7 +26153,7 @@ function parseCosDocument(bytes, options = {}) {
|
|
|
26152
26153
|
if (!unpacked) {
|
|
26153
26154
|
const stmObj = objects.get(entry.objectStreamNumber)?.value;
|
|
26154
26155
|
if (stmObj?.kind === "stream") {
|
|
26155
|
-
unpacked = unpackObjectStream(stmObj, maxDecompressedBytes, maxRecursionDepth);
|
|
26156
|
+
unpacked = unpackObjectStream(stmObj, maxDecompressedBytes, maxRecursionDepth, recovery === "repair");
|
|
26156
26157
|
objStmCache.set(entry.objectStreamNumber, unpacked);
|
|
26157
26158
|
}
|
|
26158
26159
|
}
|
|
@@ -34969,7 +34970,11 @@ function resolvePageFonts(doc, resourcesDict) {
|
|
|
34969
34970
|
let cmap;
|
|
34970
34971
|
const toUniNode = doc.resolve(dictGet(fObj, "ToUnicode"));
|
|
34971
34972
|
if (toUniNode?.kind === "stream") {
|
|
34972
|
-
|
|
34973
|
+
try {
|
|
34974
|
+
cmap = parseToUnicodeCMap(doc.decodeStream(toUniNode));
|
|
34975
|
+
} catch (error) {
|
|
34976
|
+
if (error instanceof PdfError && error.code === "E_LIMIT") throw error;
|
|
34977
|
+
}
|
|
34973
34978
|
}
|
|
34974
34979
|
const encNode = doc.resolve(dictGet(fObj, "Encoding"));
|
|
34975
34980
|
if (encNode?.kind === "dict") {
|
|
@@ -36093,8 +36098,12 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36093
36098
|
const images = [];
|
|
36094
36099
|
const operations = [];
|
|
36095
36100
|
const emit = (operation) => {
|
|
36096
|
-
const clipPaths = curState()
|
|
36097
|
-
if (clipPaths) operation = { ...operation, value: {
|
|
36101
|
+
const { clipPaths, clipImages } = curState();
|
|
36102
|
+
if (clipPaths || clipImages) operation = { ...operation, value: {
|
|
36103
|
+
...operation.value,
|
|
36104
|
+
...clipPaths ? { clipPaths } : {},
|
|
36105
|
+
...clipImages ? { clipImages } : {}
|
|
36106
|
+
} };
|
|
36098
36107
|
operations.push(operation);
|
|
36099
36108
|
switch (operation.kind) {
|
|
36100
36109
|
case "path":
|
|
@@ -36358,6 +36367,18 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36358
36367
|
let hasTextClip = false;
|
|
36359
36368
|
let activeTm = [1, 0, 0, 1, 0, 0];
|
|
36360
36369
|
let activeTlm = [1, 0, 0, 1, 0, 0];
|
|
36370
|
+
const paintImage = (image, patternMask, resources, activeFonts, depth) => {
|
|
36371
|
+
if (!patternMask) {
|
|
36372
|
+
emit({ kind: "image", value: image });
|
|
36373
|
+
return;
|
|
36374
|
+
}
|
|
36375
|
+
const st = curState();
|
|
36376
|
+
stateStack.push({ ...st, clipImages: [...st.clipImages ?? [], image] });
|
|
36377
|
+
walkNodes([{ kind: "path-op", paint: "f", segments: [
|
|
36378
|
+
{ kind: "rect", x: 0, y: 0, width: 1, height: 1 }
|
|
36379
|
+
] }], void 0, void 0, resources, activeFonts, depth);
|
|
36380
|
+
stateStack.pop();
|
|
36381
|
+
};
|
|
36361
36382
|
const walkNodes = (nodes, mcid, actualText, activeResources = params.resourcesDict, activeFonts = fonts, depth = 0) => {
|
|
36362
36383
|
for (const node of nodes) {
|
|
36363
36384
|
switch (node.kind) {
|
|
@@ -36541,7 +36562,8 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36541
36562
|
st.fillAlpha,
|
|
36542
36563
|
"PatternShading_" + st.fillPatternName,
|
|
36543
36564
|
patClip,
|
|
36544
|
-
shading?.kind === "stream" ? shading : void 0
|
|
36565
|
+
shading?.kind === "stream" ? shading : void 0,
|
|
36566
|
+
st.blendMode
|
|
36545
36567
|
);
|
|
36546
36568
|
if (shImg) {
|
|
36547
36569
|
emit({ kind: "image", value: shImg });
|
|
@@ -36650,18 +36672,20 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36650
36672
|
const subNode = params.cosDoc.resolve(dictGet(xobjNode.dict, "Subtype"));
|
|
36651
36673
|
const sub = subNode?.kind === "name" ? subNode.decoded : "";
|
|
36652
36674
|
if (sub === "Image") {
|
|
36675
|
+
const maskNode = params.cosDoc.resolve(dictGet(xobjNode.dict, "ImageMask"));
|
|
36676
|
+
const patternMask = !!st.fillPatternName && maskNode?.kind === "boolean" && maskNode.value;
|
|
36653
36677
|
const decoded = decodeXObjectImageToRgba(
|
|
36654
36678
|
params.cosDoc,
|
|
36655
36679
|
xobjNode,
|
|
36656
36680
|
activeResources,
|
|
36657
|
-
{
|
|
36681
|
+
patternMask ? { r: 1, g: 1, b: 1, alpha: 1 } : {
|
|
36658
36682
|
r: st.fillColor.r,
|
|
36659
36683
|
g: st.fillColor.g,
|
|
36660
36684
|
b: st.fillColor.b,
|
|
36661
36685
|
alpha: st.fillAlpha
|
|
36662
36686
|
}
|
|
36663
36687
|
);
|
|
36664
|
-
|
|
36688
|
+
paintImage({
|
|
36665
36689
|
name: node.name,
|
|
36666
36690
|
matrix: [...st.ctm],
|
|
36667
36691
|
width: decoded.width,
|
|
@@ -36671,7 +36695,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36671
36695
|
decodedRgba: decoded.rgba,
|
|
36672
36696
|
...st.blendMode && st.blendMode !== "Normal" ? { blendMode: st.blendMode } : {},
|
|
36673
36697
|
...st.clipRect ? { clipRect: [...st.clipRect] } : {}
|
|
36674
|
-
}
|
|
36698
|
+
}, patternMask, activeResources, activeFonts, depth);
|
|
36675
36699
|
} else if (sub === "Form" && depth < 8) {
|
|
36676
36700
|
const formStreamBytes = params.cosDoc.decodeStream(xobjNode);
|
|
36677
36701
|
const formNodes = parseContentStream(formStreamBytes);
|
|
@@ -36727,19 +36751,22 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36727
36751
|
}
|
|
36728
36752
|
case "inline-image": {
|
|
36729
36753
|
const st = curState();
|
|
36754
|
+
const maskEntry = dictGet(node.dict, "ImageMask") ?? dictGet(node.dict, "IM");
|
|
36755
|
+
const maskNode = params.cosDoc ? params.cosDoc.resolve(maskEntry) : maskEntry;
|
|
36756
|
+
const patternMask = !!st.fillPatternName && maskNode?.kind === "boolean" && maskNode.value;
|
|
36730
36757
|
const decoded = decodeInlineImageNodeToRgba(
|
|
36731
36758
|
params.cosDoc,
|
|
36732
36759
|
node.dict,
|
|
36733
36760
|
node.data,
|
|
36734
36761
|
activeResources,
|
|
36735
|
-
{
|
|
36762
|
+
patternMask ? { r: 1, g: 1, b: 1, alpha: 1 } : {
|
|
36736
36763
|
r: st.fillColor.r,
|
|
36737
36764
|
g: st.fillColor.g,
|
|
36738
36765
|
b: st.fillColor.b,
|
|
36739
36766
|
alpha: st.fillAlpha
|
|
36740
36767
|
}
|
|
36741
36768
|
);
|
|
36742
|
-
|
|
36769
|
+
paintImage({
|
|
36743
36770
|
name: "InlineImage",
|
|
36744
36771
|
matrix: [...st.ctm],
|
|
36745
36772
|
width: decoded.width,
|
|
@@ -36749,7 +36776,7 @@ function evaluateContentStreamToDisplayList(params) {
|
|
|
36749
36776
|
decodedRgba: decoded.rgba,
|
|
36750
36777
|
...st.blendMode && st.blendMode !== "Normal" ? { blendMode: st.blendMode } : {},
|
|
36751
36778
|
...st.clipRect ? { clipRect: [...st.clipRect] } : {}
|
|
36752
|
-
}
|
|
36779
|
+
}, patternMask, activeResources, activeFonts, depth);
|
|
36753
36780
|
break;
|
|
36754
36781
|
}
|
|
36755
36782
|
case "text-object": {
|
|
@@ -40495,6 +40522,7 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
40495
40522
|
const aaVec = options.antialiasVector !== false;
|
|
40496
40523
|
const aaTxt = options.antialiasText !== false;
|
|
40497
40524
|
const clipMasks = /* @__PURE__ */ new Map();
|
|
40525
|
+
const imageClipMasks = /* @__PURE__ */ new Map();
|
|
40498
40526
|
for (const original of paintOperations(displayList)) {
|
|
40499
40527
|
if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
|
|
40500
40528
|
const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
|
|
@@ -40509,6 +40537,31 @@ function renderDisplayListToBitmap(displayList, options = {}) {
|
|
|
40509
40537
|
}
|
|
40510
40538
|
clipMasks.set(clips, clipMask);
|
|
40511
40539
|
}
|
|
40540
|
+
const imageClips = original.value.clipImages;
|
|
40541
|
+
if (imageClips) {
|
|
40542
|
+
let imageMask = imageClipMasks.get(imageClips);
|
|
40543
|
+
if (!imageMask) {
|
|
40544
|
+
imageMask = new Uint8Array(width * height * 4).fill(255);
|
|
40545
|
+
for (const image of imageClips) {
|
|
40546
|
+
const layer = renderDisplayListToBitmap({
|
|
40547
|
+
...displayList,
|
|
40548
|
+
rotation: 0,
|
|
40549
|
+
paths: [],
|
|
40550
|
+
glyphs: [],
|
|
40551
|
+
images: [image],
|
|
40552
|
+
operations: [{ kind: "image", value: image }]
|
|
40553
|
+
}, { scale, transparent: true }).data;
|
|
40554
|
+
for (let i = 3; i < layer.length; i += 4) imageMask[i] = Math.round(imageMask[i] * layer[i] / 255);
|
|
40555
|
+
}
|
|
40556
|
+
imageClipMasks.set(imageClips, imageMask);
|
|
40557
|
+
}
|
|
40558
|
+
if (clipMask) {
|
|
40559
|
+
clipMask = clipMask.slice();
|
|
40560
|
+
for (let i = 3; i < clipMask.length; i += 4) clipMask[i] = Math.round(clipMask[i] * imageMask[i] / 255);
|
|
40561
|
+
} else {
|
|
40562
|
+
clipMask = imageMask;
|
|
40563
|
+
}
|
|
40564
|
+
}
|
|
40512
40565
|
if (operation.kind === "path") {
|
|
40513
40566
|
const path = operation.value;
|
|
40514
40567
|
if (path.fillColor) {
|
|
@@ -40657,6 +40710,15 @@ function escapeXmlText(str) {
|
|
|
40657
40710
|
}
|
|
40658
40711
|
return encodeToXmlString(valid);
|
|
40659
40712
|
}
|
|
40713
|
+
function svgImage(image, pageHeight) {
|
|
40714
|
+
if (!image.decodedRgba) return "";
|
|
40715
|
+
const png = encodeRgbaToPng(image.width, image.height, image.decodedRgba);
|
|
40716
|
+
const chunks = [];
|
|
40717
|
+
for (let offset = 0; offset < png.length; offset += 8192)
|
|
40718
|
+
chunks.push(String.fromCharCode(...png.subarray(offset, offset + 8192)));
|
|
40719
|
+
const [a, b, c, d, e, f] = image.matrix;
|
|
40720
|
+
return `<image width="1" height="1" preserveAspectRatio="none" transform="matrix(${a} ${-b} ${-c} ${d} ${e + c} ${pageHeight - f - d})" href="data:image/png;base64,${btoa(chunks.join(""))}"/>`;
|
|
40721
|
+
}
|
|
40660
40722
|
function svgPathData(segments, height) {
|
|
40661
40723
|
const dParts = [];
|
|
40662
40724
|
for (const seg of segments) {
|
|
@@ -40705,11 +40767,16 @@ function renderDisplayListToSvg(displayList, options = {}) {
|
|
|
40705
40767
|
if (original.kind === "glyph" && (original.value.renderMode === 3 || !original.value.outline && !original.value.unicode.trim())) continue;
|
|
40706
40768
|
const operation = original.kind === "glyph" ? { kind: "path", value: glyphPaint(original.value) } : original;
|
|
40707
40769
|
const clips = original.value.clipPaths ?? [];
|
|
40770
|
+
const imageClips = original.value.clipImages ?? [];
|
|
40708
40771
|
for (const segments of clips) {
|
|
40709
40772
|
const id = `text-clip-${clipId++}`;
|
|
40710
40773
|
const path = `<path d="${svgPathData(segments, displayList.height)}"/>`;
|
|
40711
40774
|
parts.push(`<defs><clipPath id="${id}" clipPathUnits="userSpaceOnUse">${path}</clipPath></defs><g clip-path="url(#${id})">`);
|
|
40712
40775
|
}
|
|
40776
|
+
for (const image of imageClips) {
|
|
40777
|
+
const id = `image-clip-${clipId++}`;
|
|
40778
|
+
parts.push(`<defs><mask id="${id}" maskUnits="userSpaceOnUse" x="0" y="0" width="${displayList.width}" height="${displayList.height}" style="mask-type:alpha">${svgImage(image, displayList.height)}</mask></defs><g mask="url(#${id})">`);
|
|
40779
|
+
}
|
|
40713
40780
|
if (operation.kind === "path") {
|
|
40714
40781
|
const p = operation.value;
|
|
40715
40782
|
const pathData = svgPathData(p.segments, displayList.height);
|
|
@@ -40729,24 +40796,9 @@ function renderDisplayListToSvg(displayList, options = {}) {
|
|
|
40729
40796
|
parts.push(` <path${labelAttr} d="${pathData}" fill="${fill}"${fillRuleAttr}${fillOpacityAttr} stroke="${stroke}"${strokeOpacityAttr}${strokeWidthAttr}${lineCapAttr}${lineJoinAttr}${miterLimitAttr}${dashArrayAttr}${dashOffsetAttr}/>`);
|
|
40730
40797
|
}
|
|
40731
40798
|
} else if (operation.kind === "image") {
|
|
40732
|
-
|
|
40733
|
-
if (!img.decodedRgba) {
|
|
40734
|
-
for (let i = 0; i < clips.length; i++) parts.push("</g>");
|
|
40735
|
-
continue;
|
|
40736
|
-
}
|
|
40737
|
-
const pngBytes = encodeRgbaToPng(img.width, img.height, img.decodedRgba);
|
|
40738
|
-
const chunks = [];
|
|
40739
|
-
for (let offset = 0; offset < pngBytes.length; offset += 8192)
|
|
40740
|
-
chunks.push(String.fromCharCode(...pngBytes.subarray(offset, offset + 8192)));
|
|
40741
|
-
const b64 = btoa(chunks.join(""));
|
|
40742
|
-
const [a, b, c, d, e, f] = img.matrix;
|
|
40743
|
-
const svgTx = e + c;
|
|
40744
|
-
const svgTy = displayList.height - f - d;
|
|
40745
|
-
parts.push(
|
|
40746
|
-
` <image width="1" height="1" preserveAspectRatio="none" transform="matrix(${a} ${-b} ${-c} ${d} ${svgTx} ${svgTy})" href="data:image/png;base64,${b64}"/>`
|
|
40747
|
-
);
|
|
40799
|
+
parts.push(` ${svgImage(operation.value, displayList.height)}`);
|
|
40748
40800
|
}
|
|
40749
|
-
for (let i = 0; i < clips.length; i++) parts.push("</g>");
|
|
40801
|
+
for (let i = 0; i < clips.length + imageClips.length; i++) parts.push("</g>");
|
|
40750
40802
|
}
|
|
40751
40803
|
if (displayList.rotation) parts.push("</g>");
|
|
40752
40804
|
parts.push("</svg>\n");
|