Skip to content

Commit 952952c

Browse files
committed
[api-minor] Rewrite the ps lexer & parser and add a small Wasm compiler
The main goal is to remove the eval-based interpreter. In order to have some good performances, the new parser performs some optimizations on the AST (similar to the ones in the previous implementation), and the Wasm compiler generates code for the optimized AST. For now, in case of errors or unsupported features, the Wasm compiler returns null and the old interpreter is used as a fallback. Few things are still missing: - a wasm-based interpreter using a stack (in case the ps code isn't stack-free); - a better js implementation in case of disabled wasm. but they will be added in follow-up patches.
1 parent cb2640d commit 952952c

8 files changed

Lines changed: 4478 additions & 0 deletions

File tree

src/core/function.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,30 @@ import {
2121
MathClamp,
2222
shadow,
2323
unreachable,
24+
warn,
2425
} from "../shared/util.js";
2526
import { PostScriptLexer, PostScriptParser } from "./ps_parser.js";
2627
import { BaseStream } from "./base_stream.js";
28+
import { buildPostScriptWasmFunction } from "./postscript/wasm_compiler.js";
2729
import { isNumberArray } from "./core_utils.js";
2830
import { LocalFunctionCache } from "./image_utils.js";
2931

3032
class PDFFunctionFactory {
33+
static #useWasm = true;
34+
35+
static setOptions({ useWasm }) {
36+
PDFFunctionFactory.#useWasm = useWasm;
37+
}
38+
3139
constructor({ xref, isEvalSupported = true }) {
3240
this.xref = xref;
3341
this.isEvalSupported = isEvalSupported !== false;
3442
}
3543

44+
get useWasm() {
45+
return PDFFunctionFactory.#useWasm;
46+
}
47+
3648
create(fn, parseArray = false) {
3749
let fnRef, parsedFn;
3850

@@ -358,6 +370,24 @@ class PDFFunction {
358370
throw new FormatError("No range.");
359371
}
360372

373+
if (factory.useWasm) {
374+
try {
375+
const wasmFn = buildPostScriptWasmFunction(
376+
fn.getString(),
377+
domain,
378+
range
379+
);
380+
if (wasmFn) {
381+
return wasmFn; // (src, srcOffset, dest, destOffset) → void
382+
}
383+
} catch {
384+
// Fall through to the existing interpreter-based path.
385+
}
386+
}
387+
388+
warn("Unable to compile PS function, using interpreter");
389+
fn.reset();
390+
361391
const lexer = new PostScriptLexer(fn);
362392
const parser = new PostScriptParser(lexer);
363393
const code = parser.parse();

src/core/pdf_manager.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { JpxImage } from "./jpx.js";
2828
import { MissingDataException } from "./core_utils.js";
2929
import { OperatorList } from "./operator_list.js";
3030
import { PDFDocument } from "./document.js";
31+
import { PDFFunctionFactory } from "./function.js";
3132
import { Stream } from "./stream.js";
3233

3334
function parseDocBaseUrl(url) {
@@ -97,6 +98,7 @@ class BasePdfManager {
9798
IccColorSpace.setOptions(options);
9899
CmykICCBasedCS.setOptions(options);
99100
JBig2CCITTFaxWasmImage.setOptions(options);
101+
PDFFunctionFactory.setOptions(options);
100102
}
101103

102104
get docId() {

0 commit comments

Comments
 (0)