@poe-platform/safe-bash 0.1.754 → 0.1.756
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 +39 -3
- package/dist/image-ast/index.js +39 -3
- package/dist/pdf-ast/canvas.d.ts +1 -0
- package/dist/pdf-ast/fonts/truetype.d.ts +1 -0
- package/dist/pdf-ast/index.js +39 -3
- package/dist/safe-bash/chunks/{chunk-3W6CRM25.js → chunk-CBVANNKK.js} +553 -289
- package/dist/safe-bash/chunks/{chunk-R2WG64VB.js → chunk-JCN3FXLP.js} +40 -4
- package/dist/safe-bash/chunks/{chunk-WNB4XH45.js → chunk-OSUXMC43.js} +3 -3
- package/dist/safe-bash/chunks/{chunk-37Y7GZTK.js → chunk-RSAWYAEH.js} +2 -2
- package/dist/safe-bash/chunks/{pdf-renderer-DHQDZC6N.js → pdf-renderer-CMF3CSVU.js} +3 -3
- package/dist/safe-bash/chunks/{src-S52LORVD.js → src-MXIKQCIC.js} +5 -3
- package/dist/safe-bash/command-chunks/{chunk-IJHV6I6X.js → chunk-3DOHQQJR.js} +8 -6
- package/dist/safe-bash/command-chunks/{chunk-PUOJAS3O.js → chunk-H2FQA4OU.js} +39 -3
- package/dist/safe-bash/command-chunks/chunk-IDR3WWKI.js +46 -0
- package/dist/safe-bash/command-chunks/{chunk-XAZ3NTJ4.js → chunk-KBBI4CSU.js} +2 -2
- package/dist/safe-bash/command-chunks/{chunk-YY7HJMBG.js → chunk-KOM3FNCE.js} +1 -37
- package/dist/safe-bash/command-chunks/chunk-QQOAK7JQ.js +888 -0
- package/dist/safe-bash/command-chunks/{chunk-BE4AM2JW.js → chunk-UGX6MXDD.js} +5 -3
- package/dist/safe-bash/command-chunks/{chunk-CM4SVTIA.js → chunk-WI35236J.js} +1 -1
- package/dist/safe-bash/commands/ssconvert/index.d.ts +1 -2
- package/dist/safe-bash/commands/ssconvert/index.js +6 -260
- package/dist/safe-bash/core.browser.js +5 -207
- package/dist/safe-bash-command-csvkit/index.js +277 -49
- package/dist/safe-bash-command-ffmpeg/index.js +1741 -1717
- 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 +39 -3
- package/dist/safe-bash-command-pdfinfo/index.js +39 -3
- package/dist/safe-bash-command-pdftk/index.js +39 -3
- package/dist/safe-bash-command-pdftoppm/index.js +39 -3
- package/dist/safe-bash-command-pdftotext/index.js +39 -3
- package/dist/safe-bash-command-qpdf/index.js +39 -3
- package/dist/safe-bash-command-sips/index.js +2 -2
- package/dist/safe-bash-command-soffice/index.js +5 -4
- package/dist/safe-bash-command-ssconvert/formats/csv.js +4 -3
- package/dist/safe-bash-command-ssconvert/formats/spreadsheetml.js +4 -2
- package/dist/safe-bash-command-ssconvert/formats/xls.js +10 -666
- package/dist/safe-bash-command-ssconvert/index.d.ts +1 -0
- package/dist/safe-bash-command-ssconvert/index.js +706 -392
- package/dist/safe-bash-command-ssconvert/sync.d.ts +7 -0
- package/dist/safe-bash-command-timeout/index.js +1 -1
- package/dist/safe-bash-command-wkhtmltopdf/index.js +39 -3
- package/dist/spreadsheet-format-csv/text-export.d.ts +14 -0
- package/dist/spreadsheet-format-csv/text-export.js +2 -2
- package/dist/spreadsheet-format-xls/biff-binary.d.ts +7 -2
- package/dist/spreadsheet-format-xls/biff-binary.js +10 -1
- package/dist/spreadsheet-format-xls/biff-strings.d.ts +2 -3
- package/dist/spreadsheet-format-xls/biff-strings.js +14 -2
- package/package.json +2 -3
- package/third-party/pdf-ast/THIRD_PARTY_NOTICES.md +8 -1
- package/dist/safe-bash/command-chunks/chunk-MPUNHXWI.js +0 -205
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CommandHandler } from "../safe-bash-contracts/index.js";
|
|
2
|
+
export interface SynchronousSpreadsheetArchive {
|
|
3
|
+
read(bytes: Uint8Array): ReadonlyMap<string, Uint8Array>;
|
|
4
|
+
write(entries: Readonly<Record<string, Uint8Array>>): Uint8Array;
|
|
5
|
+
}
|
|
6
|
+
export declare function createSyncSsconvertEvaluator(archive: SynchronousSpreadsheetArchive): (execute: CommandHandler, opArgs: readonly string[], inBytes?: Uint8Array, readFileSync?: (filePath: string) => Uint8Array | undefined, writeFileSync?: (filePath: string, bytes: Uint8Array) => boolean) => string | undefined;
|
|
7
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -441,7 +441,7 @@ function evalSyncTimeout(args) {
|
|
|
441
441
|
return void 0;
|
|
442
442
|
const durStr = args[i];
|
|
443
443
|
const parsed = parseDuration(durStr);
|
|
444
|
-
if (parsed.kind !== "value" || parsed.milliseconds
|
|
444
|
+
if (parsed.kind !== "value" || parsed.milliseconds <= 0)
|
|
445
445
|
return void 0;
|
|
446
446
|
if (killAfterMs !== void 0 && killAfterMs !== 0 && parsed.milliseconds !== 0 && parsed.milliseconds !== Infinity) {
|
|
447
447
|
return void 0;
|
|
@@ -5773,17 +5773,22 @@ function parseTrueTypeFont(bytes) {
|
|
|
5773
5773
|
const cmapNumTables = cmap ? readU16(view, cmapOffset + 2) : 0;
|
|
5774
5774
|
let format4Offset = 0;
|
|
5775
5775
|
let format12Offset = 0;
|
|
5776
|
+
let format4IsSymbolic = false;
|
|
5777
|
+
let format12IsSymbolic = false;
|
|
5776
5778
|
for (let i = 0; i < cmapNumTables; i++) {
|
|
5777
5779
|
const rec = cmapOffset + 4 + i * 8;
|
|
5778
5780
|
if (rec + 8 > bytes.byteLength) break;
|
|
5779
5781
|
const platformID = readU16(view, rec);
|
|
5782
|
+
const encodingID = readU16(view, rec + 2);
|
|
5780
5783
|
const subOffset = cmapOffset + readU32(view, rec + 4);
|
|
5781
5784
|
if (subOffset + 4 > bytes.byteLength) continue;
|
|
5782
5785
|
const fmt = readU16(view, subOffset);
|
|
5783
5786
|
if (fmt === 12 && (platformID === 3 || platformID === 0)) {
|
|
5784
5787
|
format12Offset = subOffset;
|
|
5788
|
+
format12IsSymbolic = platformID === 3 && encodingID === 0;
|
|
5785
5789
|
} else if (fmt === 4 && (platformID === 3 || platformID === 0)) {
|
|
5786
5790
|
format4Offset = subOffset;
|
|
5791
|
+
format4IsSymbolic = platformID === 3 && encodingID === 0;
|
|
5787
5792
|
}
|
|
5788
5793
|
}
|
|
5789
5794
|
if (format12Offset > 0) {
|
|
@@ -6072,6 +6077,7 @@ function parseTrueTypeFont(bytes) {
|
|
|
6072
6077
|
numGlyphs,
|
|
6073
6078
|
isCff: renderCffGlyph !== void 0,
|
|
6074
6079
|
hasCmap: cmap !== void 0,
|
|
6080
|
+
isSymbolicCmap: format12Offset > 0 ? format12IsSymbolic : format4IsSymbolic,
|
|
6075
6081
|
glyphNames,
|
|
6076
6082
|
getGlyphId(codePoint) {
|
|
6077
6083
|
return codePointToGlyph.get(codePoint) ?? 0;
|
|
@@ -10587,7 +10593,13 @@ function resolvePageFonts(doc, resourcesDict) {
|
|
|
10587
10593
|
embeddedCff = parseEmbeddedCffFont(doc.decodeStream(program), baseEncoding?.kind === "name" ? baseEncoding.decoded : void 0, glyphNames);
|
|
10588
10594
|
} else {
|
|
10589
10595
|
embeddedTrueType = parseTrueTypeFont(doc.decodeStream(program));
|
|
10590
|
-
if (subtype !== "Type0" &&
|
|
10596
|
+
if (subtype !== "Type0" && embeddedTrueType.isSymbolicCmap) {
|
|
10597
|
+
simpleToGid = /* @__PURE__ */ new Map();
|
|
10598
|
+
for (let code = 0; code < 256; code++) {
|
|
10599
|
+
const gid = embeddedTrueType.getGlyphId(61440 + code) || embeddedTrueType.getGlyphId(code);
|
|
10600
|
+
if (gid > 0) simpleToGid.set(code, gid);
|
|
10601
|
+
}
|
|
10602
|
+
} else if (subtype !== "Type0" && !embeddedTrueType.hasCmap) {
|
|
10591
10603
|
simpleToGid = /* @__PURE__ */ new Map();
|
|
10592
10604
|
const encoding = baseEncoding?.kind === "name" ? getEncoding(baseEncoding.decoded) : void 0;
|
|
10593
10605
|
for (let code = 0; code < 256; code++) {
|
|
@@ -40676,7 +40688,7 @@ var init_dist = __esm({
|
|
|
40676
40688
|
this.cachedContentAst = parseContentStream(bytes);
|
|
40677
40689
|
this.isolatedInitialStream = false;
|
|
40678
40690
|
const stm = cosStream(bytes, { compress });
|
|
40679
|
-
|
|
40691
|
+
this.replaceContentStream(stm);
|
|
40680
40692
|
this.cachedContentsNode = dictGet(this.pageDict, "Contents");
|
|
40681
40693
|
}
|
|
40682
40694
|
getIsolatedContentAst() {
|
|
@@ -40693,9 +40705,33 @@ var init_dist = __esm({
|
|
|
40693
40705
|
this.cachedContentAst = [...nodes];
|
|
40694
40706
|
const bytes = serializeContentAst(this.cachedContentAst);
|
|
40695
40707
|
const streamObj = cosStream(bytes, { compress });
|
|
40696
|
-
|
|
40708
|
+
this.replaceContentStream(streamObj);
|
|
40697
40709
|
this.cachedContentsNode = dictGet(this.pageDict, "Contents");
|
|
40698
40710
|
}
|
|
40711
|
+
replaceContentStream(stream) {
|
|
40712
|
+
const contents = dictGet(this.pageDict, "Contents");
|
|
40713
|
+
if (contents?.kind === "ref") {
|
|
40714
|
+
let references = 0;
|
|
40715
|
+
const pending = [...this.cosDoc.objects.values()].map((object) => object.value);
|
|
40716
|
+
const visited = /* @__PURE__ */ new Set();
|
|
40717
|
+
while (pending.length > 0 && references < 2) {
|
|
40718
|
+
const node = pending.pop();
|
|
40719
|
+
if (node.kind === "ref") {
|
|
40720
|
+
if (node.objectNumber === contents.objectNumber && node.generationNumber === contents.generationNumber) references++;
|
|
40721
|
+
} else if (!visited.has(node)) {
|
|
40722
|
+
visited.add(node);
|
|
40723
|
+
if (node.kind === "array") for (const item of node.items) pending.push(item);
|
|
40724
|
+
else if (node.kind === "dict") for (const entry of node.entries) pending.push(entry.value);
|
|
40725
|
+
else if (node.kind === "stream") pending.push(node.dict);
|
|
40726
|
+
}
|
|
40727
|
+
}
|
|
40728
|
+
if (references === 1) {
|
|
40729
|
+
this.cosDoc.setObject(contents.objectNumber, stream, contents.generationNumber);
|
|
40730
|
+
return;
|
|
40731
|
+
}
|
|
40732
|
+
}
|
|
40733
|
+
dictSet(this.pageDict, "Contents", this.cosDoc.allocateObject(stream));
|
|
40734
|
+
}
|
|
40699
40735
|
drawText(text, options) {
|
|
40700
40736
|
const size = options.size ?? options.fontSize ?? 12;
|
|
40701
40737
|
const color = options.color ?? { r: 0, g: 0, b: 0 };
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
import { type TextFormatMode } from "../spreadsheet-engine/formatting.js";
|
|
1
2
|
import type { Codec } from "../spreadsheet-engine/codecs/types.js";
|
|
3
|
+
interface TextOptions {
|
|
4
|
+
separator: string;
|
|
5
|
+
quote: string;
|
|
6
|
+
eol: string;
|
|
7
|
+
mode: "never" | "auto" | "always";
|
|
8
|
+
whitespace: boolean;
|
|
9
|
+
format: TextFormatMode;
|
|
10
|
+
charset: string;
|
|
11
|
+
transliterate: boolean;
|
|
12
|
+
locale?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function appendTextField(text: string, options: Pick<TextOptions, "mode" | "quote" | "whitespace">, append: (text: string) => void): void;
|
|
2
15
|
export declare const writeConfigurableText: NonNullable<Codec["write"]>;
|
|
3
16
|
export declare const writePlainCsv: NonNullable<Codec["write"]>;
|
|
17
|
+
export {};
|
|
@@ -70,7 +70,7 @@ function byteField(source, options, maximum, tick) {
|
|
|
70
70
|
append([quote]);
|
|
71
71
|
return Uint8Array.from(result);
|
|
72
72
|
}
|
|
73
|
-
function
|
|
73
|
+
export function appendTextField(text, options, append) {
|
|
74
74
|
// Preserve NUL and scan the entire field. Triggers retain the initial
|
|
75
75
|
// configuration even after separator, quote and eol properties change via -O.
|
|
76
76
|
const whitespace = (c) => c !== undefined && c !== "\u000b" && c !== "\ufeff" && c.trim() === "";
|
|
@@ -151,7 +151,7 @@ async function exportText(args, options) {
|
|
|
151
151
|
chunks.push(bytes);
|
|
152
152
|
}
|
|
153
153
|
else
|
|
154
|
-
|
|
154
|
+
appendTextField(cell ? await renderCellText(cell, book, renderingContext, options.format) : "", options, append);
|
|
155
155
|
}
|
|
156
156
|
append(options.eol);
|
|
157
157
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type CapabilityContext } from "../spreadsheet-engine/contracts.js";
|
|
2
1
|
export declare function invalidBiff(message: string): never;
|
|
3
2
|
/** All binary reads, including slices, require explicit admission. */
|
|
4
3
|
export declare class Binary {
|
|
@@ -29,4 +28,10 @@ export interface BiffRecord {
|
|
|
29
28
|
readonly offset: number;
|
|
30
29
|
readonly data: Binary;
|
|
31
30
|
}
|
|
32
|
-
export
|
|
31
|
+
export interface BiffReadContext extends CfbReadContext {
|
|
32
|
+
readonly limits: CfbReadContext["limits"] & {
|
|
33
|
+
readonly workbookNodes?: number;
|
|
34
|
+
readonly workbookTextBytes?: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare function readBiffRecords(bytes: Uint8Array, context: BiffReadContext): BiffRecord[];
|
|
@@ -174,15 +174,24 @@ export function readCfb(bytes, context) {
|
|
|
174
174
|
return result;
|
|
175
175
|
}
|
|
176
176
|
export function readBiffRecords(bytes, context) {
|
|
177
|
+
context.signal.throwIfAborted();
|
|
178
|
+
if (bytes.length > context.limits.inputBytes)
|
|
179
|
+
throw new SsconvertError("resource-limit", "ssconvert input bytes limit exceeded");
|
|
177
180
|
const binary = new Binary(bytes), records = [];
|
|
178
181
|
for (let offset = 0; offset < bytes.length;) {
|
|
179
182
|
context.signal.throwIfAborted();
|
|
183
|
+
context.work?.();
|
|
180
184
|
binary.check(offset, 4);
|
|
181
185
|
const opcode = binary.u16(offset), length = binary.u16(offset + 2);
|
|
182
|
-
if (opcode === 0 && length === 0 && bytes.subarray(offset).every(byte =>
|
|
186
|
+
if (opcode === 0 && length === 0 && bytes.subarray(offset).every(byte => {
|
|
187
|
+
context.signal.throwIfAborted();
|
|
188
|
+
context.work?.();
|
|
189
|
+
return byte === 0;
|
|
190
|
+
}))
|
|
183
191
|
break;
|
|
184
192
|
if (records.length >= (context.limits.workbookNodes ?? context.limits.inputBytes))
|
|
185
193
|
throw new SsconvertError("resource-limit", "ssconvert BIFF record limit exceeded");
|
|
194
|
+
context.retain?.(128);
|
|
186
195
|
records.push({ opcode, offset, data: new Binary(binary.slice(offset + 4, length)) });
|
|
187
196
|
offset += length + 4;
|
|
188
197
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { Binary } from "./biff-binary.js";
|
|
1
|
+
import { Binary, type BiffReadContext } from "./biff-binary.js";
|
|
3
2
|
import type { RichTextRun } from "../spreadsheet-ast/index.js";
|
|
4
3
|
export declare function biffOverrideCodepage(encoding: string | undefined): number | undefined;
|
|
5
4
|
export declare function biffDecode(bytes: Uint8Array, codepage: number): string;
|
|
@@ -10,7 +9,7 @@ export declare class BiffStrings {
|
|
|
10
9
|
private readonly codepage;
|
|
11
10
|
private part;
|
|
12
11
|
private offset;
|
|
13
|
-
constructor(parts: readonly Binary[], context:
|
|
12
|
+
constructor(parts: readonly Binary[], context: BiffReadContext, codepage: number);
|
|
14
13
|
get consumedBytes(): number;
|
|
15
14
|
private advance;
|
|
16
15
|
byte(): number;
|
|
@@ -12,6 +12,8 @@ export function biffOverrideCodepage(encoding) {
|
|
|
12
12
|
return encoding === undefined ? undefined : overrideCodepages[encoding];
|
|
13
13
|
}
|
|
14
14
|
export function biffDecode(bytes, codepage) {
|
|
15
|
+
if (codepage === 65001)
|
|
16
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
15
17
|
const multibyte = biffDbcsTables[codepage];
|
|
16
18
|
if (multibyte) {
|
|
17
19
|
const characters = [];
|
|
@@ -28,7 +30,7 @@ export function biffDecode(bytes, codepage) {
|
|
|
28
30
|
}
|
|
29
31
|
return characters.join("");
|
|
30
32
|
}
|
|
31
|
-
const name = codepage === 1200 || codepage === 1201 ? "iso-8859-1" :
|
|
33
|
+
const name = codepage === 20127 ? "ascii" : codepage === 1200 || codepage === 1201 || codepage === 28591 ? "iso-8859-1" :
|
|
32
34
|
codepage === 10000 ? "macintosh" : codepage >= 1250 && codepage <= 1258 ? `windows-${codepage}` : `cp${codepage}`;
|
|
33
35
|
const table = singleByteTables[name];
|
|
34
36
|
if (!table)
|
|
@@ -59,13 +61,20 @@ export class BiffStrings {
|
|
|
59
61
|
}
|
|
60
62
|
advance() {
|
|
61
63
|
while (this.part < this.parts.length && this.offset === this.parts[this.part].bytes.length) {
|
|
64
|
+
this.context.signal.throwIfAborted();
|
|
65
|
+
this.context.work?.();
|
|
62
66
|
this.part++;
|
|
63
67
|
this.offset = 0;
|
|
64
68
|
}
|
|
65
69
|
if (this.part >= this.parts.length)
|
|
66
70
|
invalidBiff("truncated string/CONTINUE");
|
|
67
71
|
}
|
|
68
|
-
byte() {
|
|
72
|
+
byte() {
|
|
73
|
+
this.context.signal.throwIfAborted();
|
|
74
|
+
this.context.work?.();
|
|
75
|
+
this.advance();
|
|
76
|
+
return this.parts[this.part].u8(this.offset++);
|
|
77
|
+
}
|
|
69
78
|
word() { const low = this.byte(); return low + this.byte() * 256; }
|
|
70
79
|
dword() { const low = this.word(); return low + this.word() * 65536; }
|
|
71
80
|
legacy(length) {
|
|
@@ -79,6 +88,8 @@ export class BiffStrings {
|
|
|
79
88
|
this.context.signal.throwIfAborted();
|
|
80
89
|
if (length > (this.context.limits.workbookTextBytes ?? this.context.limits.inputBytes))
|
|
81
90
|
throw new SsconvertError("resource-limit", "ssconvert BIFF string limit exceeded");
|
|
91
|
+
// Covers temporary byte/character arrays, decoded text and rich-run objects.
|
|
92
|
+
this.context.retain?.(length * 16);
|
|
82
93
|
}
|
|
83
94
|
unicode(length) {
|
|
84
95
|
this.admit(length * 3);
|
|
@@ -91,6 +102,7 @@ export class BiffStrings {
|
|
|
91
102
|
const characters = [];
|
|
92
103
|
for (let i = 0; i < length; i++) {
|
|
93
104
|
this.context.signal.throwIfAborted();
|
|
105
|
+
this.context.work?.();
|
|
94
106
|
if (this.offset === this.parts[this.part].bytes.length) {
|
|
95
107
|
this.advance();
|
|
96
108
|
const width = this.byte();
|
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.756",
|
|
4
4
|
"description": "Extensible virtual shell with pluggable filesystems and streaming commands",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -837,12 +837,11 @@
|
|
|
837
837
|
"access": "public"
|
|
838
838
|
},
|
|
839
839
|
"dependencies": {
|
|
840
|
-
"@poe-platform/safe-fs": "0.1.
|
|
840
|
+
"@poe-platform/safe-fs": "0.1.756",
|
|
841
841
|
"pdf-lib": "1.17.1",
|
|
842
842
|
"@noble/hashes": "2.4.0",
|
|
843
843
|
"pako": "3.0.1",
|
|
844
844
|
"@types/node": "^25.2.2",
|
|
845
|
-
"@e965/xlsx": "0.20.3",
|
|
846
845
|
"@noble/ciphers": "2.4.0",
|
|
847
846
|
"buffer": "6.0.3",
|
|
848
847
|
"@kayahr/text-encoding": "^2.2.0",
|
|
@@ -286,5 +286,12 @@ cases exercise direct and chained references to the shading stream.
|
|
|
286
286
|
`tests/test_writer.py`, replacing its downloaded PDF with an in-memory page
|
|
287
287
|
and checking every copy and save/reopen. Page copying follows `_writer.py`
|
|
288
288
|
`_add_page`: each occurrence gets a distinct page dictionary while resources
|
|
289
|
-
can remain shared.
|
|
289
|
+
can remain shared. Edits to shared content replace the page's Contents reference so
|
|
290
290
|
other pages retain their original stream. pypdf attribution/license is above.
|
|
291
|
+
|
|
292
|
+
Windows Symbol cmap selection in `src/content/evaluator.ts` follows PDF.js
|
|
293
|
+
`src/core/fonts.js` `checkAndRepair`: use encoded character codes for (3,0)
|
|
294
|
+
tables and clear the high byte only for F000–F0FF. The TrueType reader records
|
|
295
|
+
the selected cmap's encoding. Tests in `src/fonts/cid-to-gid.test.ts` cover
|
|
296
|
+
raw and prefixed symbol codes plus the original issue2948 fixture at the
|
|
297
|
+
same pinned PDF.js revision documented above.
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SsconvertError
|
|
3
|
-
} from "./chunk-F5MORDSU.js";
|
|
4
|
-
import {
|
|
5
|
-
init_buffer
|
|
6
|
-
} from "./chunk-HX3SHTCE.js";
|
|
7
|
-
|
|
8
|
-
// packages/spreadsheet-format-xls/src/biff-binary.ts
|
|
9
|
-
init_buffer();
|
|
10
|
-
function invalidBiff(message) {
|
|
11
|
-
throw new SsconvertError("io", `E Invalid Excel BIFF: ${message}`);
|
|
12
|
-
}
|
|
13
|
-
var Binary = class {
|
|
14
|
-
constructor(bytes) {
|
|
15
|
-
this.bytes = bytes;
|
|
16
|
-
this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
17
|
-
}
|
|
18
|
-
bytes;
|
|
19
|
-
view;
|
|
20
|
-
check(offset, length) {
|
|
21
|
-
if (!Number.isSafeInteger(offset) || !Number.isSafeInteger(length) || offset < 0 || length < 0 || offset > this.bytes.length - length) invalidBiff("truncated binary data");
|
|
22
|
-
}
|
|
23
|
-
u8(offset) {
|
|
24
|
-
this.check(offset, 1);
|
|
25
|
-
return this.view.getUint8(offset);
|
|
26
|
-
}
|
|
27
|
-
u16(offset) {
|
|
28
|
-
this.check(offset, 2);
|
|
29
|
-
return this.view.getUint16(offset, true);
|
|
30
|
-
}
|
|
31
|
-
u32(offset) {
|
|
32
|
-
this.check(offset, 4);
|
|
33
|
-
return this.view.getUint32(offset, true);
|
|
34
|
-
}
|
|
35
|
-
f64(offset) {
|
|
36
|
-
this.check(offset, 8);
|
|
37
|
-
return this.view.getFloat64(offset, true);
|
|
38
|
-
}
|
|
39
|
-
slice(offset, length) {
|
|
40
|
-
this.check(offset, length);
|
|
41
|
-
return this.bytes.subarray(offset, offset + length);
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
var signature = [208, 207, 17, 224, 161, 177, 26, 225];
|
|
45
|
-
var free = 4294967295;
|
|
46
|
-
var end = 4294967294;
|
|
47
|
-
function isCfb(bytes) {
|
|
48
|
-
return signature.every((byte, index) => bytes[index] === byte);
|
|
49
|
-
}
|
|
50
|
-
function readCfb(bytes, context) {
|
|
51
|
-
context.signal.throwIfAborted();
|
|
52
|
-
context.work?.();
|
|
53
|
-
if (bytes.length > context.limits.inputBytes) throw new SsconvertError("resource-limit", "ssconvert input bytes limit exceeded");
|
|
54
|
-
context.retain?.(256);
|
|
55
|
-
const file = new Binary(bytes);
|
|
56
|
-
file.check(0, 512);
|
|
57
|
-
if (!isCfb(bytes) || file.u16(28) !== 65534) invalidBiff("invalid CFB header");
|
|
58
|
-
const version = file.u16(26), shift = file.u16(30);
|
|
59
|
-
if (!(version === 3 && shift === 9 || version === 4 && shift === 12) || file.u16(32) !== 6)
|
|
60
|
-
invalidBiff("unsupported CFB sector layout");
|
|
61
|
-
const sectorSize = 2 ** shift, sectorCount = Math.floor(bytes.length / sectorSize) - 1;
|
|
62
|
-
if (sectorCount < 0 || bytes.length % sectorSize !== 0 || file.u32(56) !== 4096) invalidBiff("invalid CFB size");
|
|
63
|
-
const sector = (id) => {
|
|
64
|
-
context.signal.throwIfAborted();
|
|
65
|
-
context.work?.();
|
|
66
|
-
if (id >= sectorCount) invalidBiff("CFB sector outside file");
|
|
67
|
-
return new Binary(file.slice((id + 1) * sectorSize, sectorSize));
|
|
68
|
-
};
|
|
69
|
-
const fatCount = file.u32(44), difatCount = file.u32(72), miniCount = file.u32(64);
|
|
70
|
-
if (fatCount > sectorCount || difatCount > sectorCount || miniCount > sectorCount) invalidBiff("invalid CFB chain count");
|
|
71
|
-
const fatIds = [], seenFat = /* @__PURE__ */ new Set(), seenDifat = /* @__PURE__ */ new Set();
|
|
72
|
-
const addFat = (id) => {
|
|
73
|
-
context.work?.();
|
|
74
|
-
if (id === free) return;
|
|
75
|
-
if (id >= sectorCount || fatIds.length >= fatCount || seenFat.has(id)) invalidBiff("invalid CFB FAT sector");
|
|
76
|
-
context.retain?.(64);
|
|
77
|
-
seenFat.add(id);
|
|
78
|
-
fatIds.push(id);
|
|
79
|
-
};
|
|
80
|
-
for (let offset = 76; offset < 512; offset += 4) addFat(file.u32(offset));
|
|
81
|
-
let difat = file.u32(68);
|
|
82
|
-
for (let index = 0; index < difatCount; index++) {
|
|
83
|
-
context.work?.();
|
|
84
|
-
if (seenDifat.has(difat)) invalidBiff("CFB DIFAT cycle");
|
|
85
|
-
context.retain?.(64);
|
|
86
|
-
seenDifat.add(difat);
|
|
87
|
-
const data = sector(difat);
|
|
88
|
-
for (let offset = 0; offset < sectorSize - 4; offset += 4) addFat(data.u32(offset));
|
|
89
|
-
difat = data.u32(sectorSize - 4);
|
|
90
|
-
}
|
|
91
|
-
if (difatCount && difat !== end || fatIds.length !== fatCount) invalidBiff("CFB FAT count mismatch");
|
|
92
|
-
context.retain?.(fatIds.length * 64);
|
|
93
|
-
const fatSectors = fatIds.map(sector), fatWidth = sectorSize / 4;
|
|
94
|
-
const nextFat = (id) => {
|
|
95
|
-
if (id >= sectorCount || !fatSectors[Math.floor(id / fatWidth)]) invalidBiff("CFB FAT reference outside table");
|
|
96
|
-
return fatSectors[Math.floor(id / fatWidth)].u32(id % fatWidth * 4);
|
|
97
|
-
};
|
|
98
|
-
const chain = (start, maximum, next) => {
|
|
99
|
-
const ids = [], seen = /* @__PURE__ */ new Set();
|
|
100
|
-
for (let id = start; id !== end; id = next(id)) {
|
|
101
|
-
context.signal.throwIfAborted();
|
|
102
|
-
context.work?.();
|
|
103
|
-
if (seen.has(id)) invalidBiff("CFB chain cycle");
|
|
104
|
-
if (ids.length >= maximum) invalidBiff("CFB chain exceeds declared size");
|
|
105
|
-
context.retain?.(64);
|
|
106
|
-
seen.add(id);
|
|
107
|
-
ids.push(id);
|
|
108
|
-
}
|
|
109
|
-
return ids;
|
|
110
|
-
};
|
|
111
|
-
let allocated = 0;
|
|
112
|
-
const collect = (ids, size, width, source) => {
|
|
113
|
-
if (size > context.limits.inputBytes - allocated) throw new SsconvertError("resource-limit", "ssconvert CFB decoded bytes limit exceeded");
|
|
114
|
-
if (ids.length !== Math.ceil(size / width)) invalidBiff("CFB stream size mismatch");
|
|
115
|
-
context.retain?.(size);
|
|
116
|
-
allocated += size;
|
|
117
|
-
const result2 = new Uint8Array(size);
|
|
118
|
-
for (let i = 0; i < ids.length; i++) {
|
|
119
|
-
context.work?.();
|
|
120
|
-
result2.set(source(ids[i]).slice(0, Math.min(width, size - i * width)), i * width);
|
|
121
|
-
}
|
|
122
|
-
return result2;
|
|
123
|
-
};
|
|
124
|
-
const dirIds = chain(file.u32(48), sectorCount, nextFat);
|
|
125
|
-
if (version === 4 && dirIds.length !== file.u32(40)) invalidBiff("CFB directory count mismatch");
|
|
126
|
-
const directory = new Binary(collect(dirIds, dirIds.length * sectorSize, sectorSize, sector));
|
|
127
|
-
const entries = [];
|
|
128
|
-
for (let offset = 0; offset < directory.bytes.length; offset += 128) {
|
|
129
|
-
context.signal.throwIfAborted();
|
|
130
|
-
context.work?.();
|
|
131
|
-
const type = directory.u8(offset + 66), length = directory.u16(offset + 64);
|
|
132
|
-
if (type && (length < 2 || length > 64 || length % 2 || directory.u16(offset + length - 2) !== 0)) invalidBiff("invalid CFB directory name");
|
|
133
|
-
if (![0, 1, 2, 5].includes(type)) invalidBiff("invalid CFB entry type");
|
|
134
|
-
const high = directory.u32(offset + 124), low = directory.u32(offset + 120);
|
|
135
|
-
const size = version === 3 ? low : high * 2 ** 32 + low;
|
|
136
|
-
if (!Number.isSafeInteger(size) || type && size > bytes.length) invalidBiff("invalid CFB stream size");
|
|
137
|
-
context.retain?.(128 + (type ? length : 0));
|
|
138
|
-
entries.push({
|
|
139
|
-
name: type ? new TextDecoder("utf-16le", { fatal: true }).decode(directory.slice(offset, length - 2)) : "",
|
|
140
|
-
type,
|
|
141
|
-
left: directory.u32(offset + 68),
|
|
142
|
-
right: directory.u32(offset + 72),
|
|
143
|
-
child: directory.u32(offset + 76),
|
|
144
|
-
start: directory.u32(offset + 116),
|
|
145
|
-
size
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
const root = entries[0];
|
|
149
|
-
if (!root || root.type !== 5) invalidBiff("missing CFB root");
|
|
150
|
-
const miniIds = chain(file.u32(60), miniCount, nextFat);
|
|
151
|
-
if (miniIds.length !== miniCount) invalidBiff("CFB miniFAT count mismatch");
|
|
152
|
-
const miniFat = new Binary(collect(miniIds, miniCount * sectorSize, sectorSize, sector));
|
|
153
|
-
const miniStream = new Binary(collect(chain(root.start, Math.ceil(root.size / sectorSize), nextFat), root.size, sectorSize, sector));
|
|
154
|
-
const miniSector = (id) => new Binary(miniStream.slice(id * 64, 64));
|
|
155
|
-
const nextMini = (id) => {
|
|
156
|
-
if (id >= Math.ceil(root.size / 64)) invalidBiff("CFB mini sector outside stream");
|
|
157
|
-
return miniFat.u32(id * 4);
|
|
158
|
-
};
|
|
159
|
-
const result = /* @__PURE__ */ new Map(), visited = /* @__PURE__ */ new Set(), pending = [root.child];
|
|
160
|
-
while (pending.length) {
|
|
161
|
-
context.signal.throwIfAborted();
|
|
162
|
-
context.work?.();
|
|
163
|
-
const id = pending.pop();
|
|
164
|
-
if (id === free) continue;
|
|
165
|
-
if (visited.has(id)) invalidBiff("CFB directory cycle");
|
|
166
|
-
context.retain?.(96);
|
|
167
|
-
visited.add(id);
|
|
168
|
-
const entry = entries[id];
|
|
169
|
-
if (!entry || !entry.type || entry.type === 5) invalidBiff("invalid CFB directory reference");
|
|
170
|
-
pending.push(entry.left, entry.right);
|
|
171
|
-
if (entry.type !== 2) continue;
|
|
172
|
-
if (result.has(entry.name)) invalidBiff("duplicate CFB stream name");
|
|
173
|
-
const mini = entry.size < 4096;
|
|
174
|
-
context.retain?.(64);
|
|
175
|
-
result.set(entry.name, collect(
|
|
176
|
-
chain(entry.start, Math.ceil(entry.size / (mini ? 64 : sectorSize)), mini ? nextMini : nextFat),
|
|
177
|
-
entry.size,
|
|
178
|
-
mini ? 64 : sectorSize,
|
|
179
|
-
mini ? miniSector : sector
|
|
180
|
-
));
|
|
181
|
-
}
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
function readBiffRecords(bytes, context) {
|
|
185
|
-
const binary = new Binary(bytes), records = [];
|
|
186
|
-
for (let offset = 0; offset < bytes.length; ) {
|
|
187
|
-
context.signal.throwIfAborted();
|
|
188
|
-
binary.check(offset, 4);
|
|
189
|
-
const opcode = binary.u16(offset), length = binary.u16(offset + 2);
|
|
190
|
-
if (opcode === 0 && length === 0 && bytes.subarray(offset).every((byte) => byte === 0)) break;
|
|
191
|
-
if (records.length >= (context.limits.workbookNodes ?? context.limits.inputBytes))
|
|
192
|
-
throw new SsconvertError("resource-limit", "ssconvert BIFF record limit exceeded");
|
|
193
|
-
records.push({ opcode, offset, data: new Binary(binary.slice(offset + 4, length)) });
|
|
194
|
-
offset += length + 4;
|
|
195
|
-
}
|
|
196
|
-
return records;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export {
|
|
200
|
-
invalidBiff,
|
|
201
|
-
Binary,
|
|
202
|
-
isCfb,
|
|
203
|
-
readCfb,
|
|
204
|
-
readBiffRecords
|
|
205
|
-
};
|