class PDF {
constructor({ fns, args, ops=false, chs=false, ...rest }) {
this.fns = fns;
this.args = args;
if (ops === true) {
this.ops = Array.from(this.fns).map((fn) => PDF.fn2op(fn));
} else if (ops !== false) {
this.ops = ops;
}
if (chs === true) {
let chs = '';
for (const fn of this.fns) {
const ch = PDF.fn2ch(fn);
chs = chs.concat(ch);
}
this.chs = chs;
} else if (chs !== false) {
this.chs = chs;
}
Object.assign(this, rest);
}
static async load(url, { page=1, want=['ops', 'chs', 'fns', 'args'] }={}) {
const pdfDoc = await pdfjsLib.getDocument(url).promise;
const pdfPage = await pdfDoc.getPage(page);
const pdfList = await pdfPage.getOperatorList();
const parts = {};
if (want.includes('doc')) {
parts.doc = pdfDoc;
}
if (want.includes('page')) {
parts.page = pdfPage;
}
if (want.includes('ops')) {
parts.ops = true;
}
if (want.includes('chs')) {
parts.chs = true;
}
if (want.includes('fns')) {
parts.fns = new Uint8Array(pdfList.fnArray);
}
if (want.includes('args')) {
parts.args = pdfList.argsArray;
}
return new PDF(parts);
}
static alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[]^_`{|}~';
static #opcodes = null;
static get opcodes() {
if (this.#opcodes === null) {
const ops = Object.entries(pdfjsLib.OPS);
ops.sort((a, b) => a[1] - b[1]);
if (ops[0][1] >= ops[1][1]) throw 'should be sorted correctly';
const opcodes = [];
for (const [op, fn] of ops) {
const ch = this.alphabet.at(fn);
if (ch === undefined) throw 'alphabet too small';
opcodes.push({ op, fn, ch });
}
opcodes.push({ op: 'noop', fn: 0, ch: ' ' });
this.#opcodes = opcodes;
}
return this.#opcodes;
}
static #OPS = null;
static OPS() {
if (this.#OPS === null) {
this.#OPS = this.opcodes.map(({ op }) => op);
}
return this.#OPS;
}
static #FNS = null;
static FNS() {
if (this.#FNS === null) {
this.#FNS = this.opcodes.map(({ fn }) => fn);
}
return this.#FNS;
}
static #CHS = null;
static CHS() {
if (this.#CHS === null) {
this.#CHS = this.opcodes.map(({ ch }) => ch);
}
return this.#CHS;
}
static #fn2op = null;
static fn2op(fn) {
if (this.#fn2op === null) {
this.#fn2op = new Map(this.opcodes.map(({ fn, op }) => [fn, op]));
}
return this.#fn2op.get(fn);
}
static #op2fn = null;
static op2fn(op) {
if (this.#op2fn === null) {
this.#op2fn = new Map(this.opcodes.map(({ op, fn }) => [op, fn]));
}
return this.#op2fn.get(op);
}
static #fn2ch = null;
static fn2ch(fn) {
if (this.#fn2ch === null) {
this.#fn2ch = new Map(this.opcodes.map(({ fn, ch }) => [fn, ch]));
}
return this.#fn2ch.get(fn);
}
static #ch2fn = null;
static ch2fn(ch) {
if (this.#ch2fn === null) {
this.#ch2fn = new Map(this.opcodes.map(({ ch, fn }) => [ch, fn]));
}
return this.#ch2fn.get(ch);
}
static #op2ch = null;
static op2ch(op) {
if (this.#op2ch === null) {
this.#op2ch = new Map(this.opcodes.map(({ op, ch }) => [op, ch]));
}
return this.#op2ch.get(op);
}
static #ch2op = null;
static ch2op(ch) {
if (this.#ch2op === null) {
this.#ch2op = new Map(this.opcodes.map(({ ch, op }) => [ch, op]));
}
return this.#ch2op.get(ch);
}
}