mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-04-09 23:04:02 +02:00
[api-minor] Remove PostScriptCompiler and PostScriptEvaluator, since it's now dead code (PR 21023 follow-up)
These classes, and various related code, became unused after PR 21023 with only unit-tests actually running that code now. Also removes the `isEvalSupported` API option, since the `PostScriptCompiler` was the only remaining code where `eval` was used.
This commit is contained in:
parent
5347c22703
commit
f6bac014ea
@ -4007,14 +4007,13 @@ class FreeTextAnnotation extends MarkupAnnotation {
|
||||
// We want to be able to add mouse listeners to the annotation.
|
||||
this.data.noHTML = false;
|
||||
|
||||
const { annotationGlobals, evaluatorOptions, xref } = params;
|
||||
const { annotationGlobals, xref } = params;
|
||||
this.setDefaultAppearance(params);
|
||||
this._hasAppearance = !!this.appearance;
|
||||
|
||||
if (this._hasAppearance) {
|
||||
const { fontColor, fontSize } = parseAppearanceStream(
|
||||
this.appearance,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
annotationGlobals.globalColorSpaceCache
|
||||
);
|
||||
|
||||
@ -97,10 +97,9 @@ function parseDefaultAppearance(str) {
|
||||
}
|
||||
|
||||
class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
|
||||
constructor(stream, evaluatorOptions, xref, globalColorSpaceCache) {
|
||||
constructor(stream, xref, globalColorSpaceCache) {
|
||||
super(stream);
|
||||
this.stream = stream;
|
||||
this.evaluatorOptions = evaluatorOptions;
|
||||
this.xref = xref;
|
||||
this.globalColorSpaceCache = globalColorSpaceCache;
|
||||
|
||||
@ -202,25 +201,19 @@ class AppearanceStreamEvaluator extends EvaluatorPreprocessor {
|
||||
}
|
||||
|
||||
get _pdfFunctionFactory() {
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref: this.xref,
|
||||
isEvalSupported: this.evaluatorOptions.isEvalSupported,
|
||||
});
|
||||
return shadow(this, "_pdfFunctionFactory", pdfFunctionFactory);
|
||||
return shadow(
|
||||
this,
|
||||
"_pdfFunctionFactory",
|
||||
new PDFFunctionFactory({ xref: this.xref })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse appearance stream to extract font and color information.
|
||||
// It returns the font properties used to render the first text object.
|
||||
function parseAppearanceStream(
|
||||
stream,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
) {
|
||||
function parseAppearanceStream(stream, xref, globalColorSpaceCache) {
|
||||
return new AppearanceStreamEvaluator(
|
||||
stream,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
).parse();
|
||||
|
||||
@ -2089,15 +2089,11 @@ class PDFDocument {
|
||||
value.numComps = value.bitsPerComponent = 1;
|
||||
}
|
||||
try {
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref: this.xref,
|
||||
isEvalSupported: this.pdfManager.evaluatorOptions.isEvalSupported,
|
||||
});
|
||||
const imageObj = await PDFImage.buildImage({
|
||||
xref: this.xref,
|
||||
res: Dict.empty,
|
||||
image: value,
|
||||
pdfFunctionFactory,
|
||||
pdfFunctionFactory: new PDFFunctionFactory({ xref: this.xref }),
|
||||
globalColorSpaceCache: this.catalog.globalColorSpaceCache,
|
||||
localColorSpaceCache: new LocalColorSpaceCache(),
|
||||
});
|
||||
|
||||
@ -93,7 +93,6 @@ const DefaultPartialEvaluatorOptions = Object.freeze({
|
||||
maxImageSize: -1,
|
||||
disableFontFace: false,
|
||||
ignoreErrors: false,
|
||||
isEvalSupported: true,
|
||||
isOffscreenCanvasSupported: false,
|
||||
isImageDecoderSupported: false,
|
||||
canvasMaxAreaInBytes: -1,
|
||||
@ -263,11 +262,11 @@ class PartialEvaluator {
|
||||
* `PDFFunctionFactory` instance within this `PartialEvaluator` instance.
|
||||
*/
|
||||
get _pdfFunctionFactory() {
|
||||
const pdfFunctionFactory = new PDFFunctionFactory({
|
||||
xref: this.xref,
|
||||
isEvalSupported: this.options.isEvalSupported,
|
||||
});
|
||||
return shadow(this, "_pdfFunctionFactory", pdfFunctionFactory);
|
||||
return shadow(
|
||||
this,
|
||||
"_pdfFunctionFactory",
|
||||
new PDFFunctionFactory({ xref: this.xref })
|
||||
);
|
||||
}
|
||||
|
||||
get parsingType3Font() {
|
||||
|
||||
@ -14,13 +14,7 @@
|
||||
*/
|
||||
|
||||
import { Dict, Ref } from "./primitives.js";
|
||||
import {
|
||||
FormatError,
|
||||
info,
|
||||
shadow,
|
||||
unreachable,
|
||||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { FormatError, info, shadow, warn } from "../shared/util.js";
|
||||
import { BaseStream } from "./base_stream.js";
|
||||
import { buildPostScriptJsFunction } from "./postscript/js_evaluator.js";
|
||||
import { buildPostScriptWasmFunction } from "./postscript/wasm_compiler.js";
|
||||
@ -32,12 +26,11 @@ class PDFFunctionFactory {
|
||||
static #useWasm = true;
|
||||
|
||||
static setOptions({ useWasm }) {
|
||||
PDFFunctionFactory.#useWasm = useWasm;
|
||||
this.#useWasm = useWasm;
|
||||
}
|
||||
|
||||
constructor({ xref, isEvalSupported = true }) {
|
||||
constructor({ xref }) {
|
||||
this.xref = xref;
|
||||
this.isEvalSupported = isEvalSupported !== false;
|
||||
}
|
||||
|
||||
get useWasm() {
|
||||
@ -398,732 +391,4 @@ function isPDFFunction(v) {
|
||||
return fnDict.has("FunctionType");
|
||||
}
|
||||
|
||||
class PostScriptStack {
|
||||
static MAX_STACK_SIZE = 100;
|
||||
|
||||
constructor(initialStack) {
|
||||
this.stack = initialStack ? Array.from(initialStack) : [];
|
||||
}
|
||||
|
||||
push(value) {
|
||||
if (this.stack.length >= PostScriptStack.MAX_STACK_SIZE) {
|
||||
throw new Error("PostScript function stack overflow.");
|
||||
}
|
||||
this.stack.push(value);
|
||||
}
|
||||
|
||||
pop() {
|
||||
if (this.stack.length <= 0) {
|
||||
throw new Error("PostScript function stack underflow.");
|
||||
}
|
||||
return this.stack.pop();
|
||||
}
|
||||
|
||||
copy(n) {
|
||||
if (this.stack.length + n >= PostScriptStack.MAX_STACK_SIZE) {
|
||||
throw new Error("PostScript function stack overflow.");
|
||||
}
|
||||
const stack = this.stack;
|
||||
for (let i = stack.length - n, j = n - 1; j >= 0; j--, i++) {
|
||||
stack.push(stack[i]);
|
||||
}
|
||||
}
|
||||
|
||||
index(n) {
|
||||
this.push(this.stack[this.stack.length - n - 1]);
|
||||
}
|
||||
|
||||
// rotate the last n stack elements p times
|
||||
roll(n, p) {
|
||||
const stack = this.stack;
|
||||
const l = stack.length - n;
|
||||
const r = stack.length - 1;
|
||||
const c = l + (p - Math.floor(p / n) * n);
|
||||
|
||||
for (let i = l, j = r; i < j; i++, j--) {
|
||||
const t = stack[i];
|
||||
stack[i] = stack[j];
|
||||
stack[j] = t;
|
||||
}
|
||||
for (let i = l, j = c - 1; i < j; i++, j--) {
|
||||
const t = stack[i];
|
||||
stack[i] = stack[j];
|
||||
stack[j] = t;
|
||||
}
|
||||
for (let i = c, j = r; i < j; i++, j--) {
|
||||
const t = stack[i];
|
||||
stack[i] = stack[j];
|
||||
stack[j] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PostScriptEvaluator {
|
||||
constructor(operators) {
|
||||
this.operators = operators;
|
||||
}
|
||||
|
||||
execute(initialStack) {
|
||||
const stack = new PostScriptStack(initialStack);
|
||||
let counter = 0;
|
||||
const operators = this.operators;
|
||||
const length = operators.length;
|
||||
let operator, a, b;
|
||||
while (counter < length) {
|
||||
operator = operators[counter++];
|
||||
if (typeof operator === "number") {
|
||||
// Operator is really an operand and should be pushed to the stack.
|
||||
stack.push(operator);
|
||||
continue;
|
||||
}
|
||||
switch (operator) {
|
||||
// non standard ps operators
|
||||
case "jz": // jump if false
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (!a) {
|
||||
counter = b;
|
||||
}
|
||||
break;
|
||||
case "j": // jump
|
||||
a = stack.pop();
|
||||
counter = a;
|
||||
break;
|
||||
|
||||
// all ps operators in alphabetical order (excluding if/ifelse)
|
||||
case "abs":
|
||||
a = stack.pop();
|
||||
stack.push(Math.abs(a));
|
||||
break;
|
||||
case "add":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a + b);
|
||||
break;
|
||||
case "and":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (typeof a === "boolean" && typeof b === "boolean") {
|
||||
stack.push(a && b);
|
||||
} else {
|
||||
stack.push(a & b);
|
||||
}
|
||||
break;
|
||||
case "atan":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
a = (Math.atan2(a, b) / Math.PI) * 180;
|
||||
if (a < 0) {
|
||||
a += 360;
|
||||
}
|
||||
stack.push(a);
|
||||
break;
|
||||
case "bitshift":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (a > 0) {
|
||||
stack.push(a << b);
|
||||
} else {
|
||||
stack.push(a >> b);
|
||||
}
|
||||
break;
|
||||
case "ceiling":
|
||||
a = stack.pop();
|
||||
stack.push(Math.ceil(a));
|
||||
break;
|
||||
case "copy":
|
||||
a = stack.pop();
|
||||
stack.copy(a);
|
||||
break;
|
||||
case "cos":
|
||||
a = stack.pop();
|
||||
stack.push(Math.cos(((a % 360) / 180) * Math.PI));
|
||||
break;
|
||||
case "cvi":
|
||||
a = stack.pop() | 0;
|
||||
stack.push(a);
|
||||
break;
|
||||
case "cvr":
|
||||
// noop
|
||||
break;
|
||||
case "div":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a / b);
|
||||
break;
|
||||
case "dup":
|
||||
stack.copy(1);
|
||||
break;
|
||||
case "eq":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a === b);
|
||||
break;
|
||||
case "exch":
|
||||
stack.roll(2, 1);
|
||||
break;
|
||||
case "exp":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a ** b);
|
||||
break;
|
||||
case "false":
|
||||
stack.push(false);
|
||||
break;
|
||||
case "floor":
|
||||
a = stack.pop();
|
||||
stack.push(Math.floor(a));
|
||||
break;
|
||||
case "ge":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a >= b);
|
||||
break;
|
||||
case "gt":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a > b);
|
||||
break;
|
||||
case "idiv":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push((a / b) | 0);
|
||||
break;
|
||||
case "index":
|
||||
a = stack.pop();
|
||||
stack.index(a);
|
||||
break;
|
||||
case "le":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a <= b);
|
||||
break;
|
||||
case "ln":
|
||||
a = stack.pop();
|
||||
stack.push(Math.log(a));
|
||||
break;
|
||||
case "log":
|
||||
a = stack.pop();
|
||||
stack.push(Math.log10(a));
|
||||
break;
|
||||
case "lt":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a < b);
|
||||
break;
|
||||
case "mod":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a % b);
|
||||
break;
|
||||
case "mul":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a * b);
|
||||
break;
|
||||
case "ne":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a !== b);
|
||||
break;
|
||||
case "neg":
|
||||
a = stack.pop();
|
||||
stack.push(-a);
|
||||
break;
|
||||
case "not":
|
||||
a = stack.pop();
|
||||
if (typeof a === "boolean") {
|
||||
stack.push(!a);
|
||||
} else {
|
||||
stack.push(~a);
|
||||
}
|
||||
break;
|
||||
case "or":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (typeof a === "boolean" && typeof b === "boolean") {
|
||||
stack.push(a || b);
|
||||
} else {
|
||||
stack.push(a | b);
|
||||
}
|
||||
break;
|
||||
case "pop":
|
||||
stack.pop();
|
||||
break;
|
||||
case "roll":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.roll(a, b);
|
||||
break;
|
||||
case "round":
|
||||
a = stack.pop();
|
||||
stack.push(Math.round(a));
|
||||
break;
|
||||
case "sin":
|
||||
a = stack.pop();
|
||||
stack.push(Math.sin(((a % 360) / 180) * Math.PI));
|
||||
break;
|
||||
case "sqrt":
|
||||
a = stack.pop();
|
||||
stack.push(Math.sqrt(a));
|
||||
break;
|
||||
case "sub":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
stack.push(a - b);
|
||||
break;
|
||||
case "true":
|
||||
stack.push(true);
|
||||
break;
|
||||
case "truncate":
|
||||
a = stack.pop();
|
||||
a = a < 0 ? Math.ceil(a) : Math.floor(a);
|
||||
stack.push(a);
|
||||
break;
|
||||
case "xor":
|
||||
b = stack.pop();
|
||||
a = stack.pop();
|
||||
if (typeof a === "boolean" && typeof b === "boolean") {
|
||||
stack.push(a !== b);
|
||||
} else {
|
||||
stack.push(a ^ b);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new FormatError(`Unknown operator ${operator}`);
|
||||
}
|
||||
}
|
||||
return stack.stack;
|
||||
}
|
||||
}
|
||||
|
||||
class AstNode {
|
||||
constructor(type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
unreachable("abstract method");
|
||||
}
|
||||
}
|
||||
|
||||
class AstArgument extends AstNode {
|
||||
constructor(index, min, max) {
|
||||
super("args");
|
||||
this.index = index;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
visitor.visitArgument(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AstLiteral extends AstNode {
|
||||
constructor(number) {
|
||||
super("literal");
|
||||
this.number = number;
|
||||
this.min = number;
|
||||
this.max = number;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
visitor.visitLiteral(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AstBinaryOperation extends AstNode {
|
||||
constructor(op, arg1, arg2, min, max) {
|
||||
super("binary");
|
||||
this.op = op;
|
||||
this.arg1 = arg1;
|
||||
this.arg2 = arg2;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
visitor.visitBinaryOperation(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AstMin extends AstNode {
|
||||
constructor(arg, max) {
|
||||
super("max");
|
||||
this.arg = arg;
|
||||
this.min = arg.min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
visitor.visitMin(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AstVariable extends AstNode {
|
||||
constructor(index, min, max) {
|
||||
super("var");
|
||||
this.index = index;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
visitor.visitVariable(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AstVariableDefinition extends AstNode {
|
||||
constructor(variable, arg) {
|
||||
super("definition");
|
||||
this.variable = variable;
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
visit(visitor) {
|
||||
visitor.visitVariableDefinition(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionBuilderVisitor {
|
||||
parts = [];
|
||||
|
||||
visitArgument(arg) {
|
||||
this.parts.push(
|
||||
"Math.max(",
|
||||
arg.min,
|
||||
", Math.min(",
|
||||
arg.max,
|
||||
", src[srcOffset + ",
|
||||
arg.index,
|
||||
"]))"
|
||||
);
|
||||
}
|
||||
|
||||
visitVariable(variable) {
|
||||
this.parts.push("v", variable.index);
|
||||
}
|
||||
|
||||
visitLiteral(literal) {
|
||||
this.parts.push(literal.number);
|
||||
}
|
||||
|
||||
visitBinaryOperation(operation) {
|
||||
this.parts.push("(");
|
||||
operation.arg1.visit(this);
|
||||
this.parts.push(" ", operation.op, " ");
|
||||
operation.arg2.visit(this);
|
||||
this.parts.push(")");
|
||||
}
|
||||
|
||||
visitVariableDefinition(definition) {
|
||||
this.parts.push("var ");
|
||||
definition.variable.visit(this);
|
||||
this.parts.push(" = ");
|
||||
definition.arg.visit(this);
|
||||
this.parts.push(";");
|
||||
}
|
||||
|
||||
visitMin(max) {
|
||||
this.parts.push("Math.min(");
|
||||
max.arg.visit(this);
|
||||
this.parts.push(", ", max.max, ")");
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.parts.join("");
|
||||
}
|
||||
}
|
||||
|
||||
function buildAddOperation(num1, num2) {
|
||||
if (num2.type === "literal" && num2.number === 0) {
|
||||
// optimization: second operand is 0
|
||||
return num1;
|
||||
}
|
||||
if (num1.type === "literal" && num1.number === 0) {
|
||||
// optimization: first operand is 0
|
||||
return num2;
|
||||
}
|
||||
if (num2.type === "literal" && num1.type === "literal") {
|
||||
// optimization: operands operand are literals
|
||||
return new AstLiteral(num1.number + num2.number);
|
||||
}
|
||||
return new AstBinaryOperation(
|
||||
"+",
|
||||
num1,
|
||||
num2,
|
||||
num1.min + num2.min,
|
||||
num1.max + num2.max
|
||||
);
|
||||
}
|
||||
|
||||
function buildMulOperation(num1, num2) {
|
||||
if (num2.type === "literal") {
|
||||
// optimization: second operands is a literal...
|
||||
if (num2.number === 0) {
|
||||
return new AstLiteral(0); // and it's 0
|
||||
} else if (num2.number === 1) {
|
||||
return num1; // and it's 1
|
||||
} else if (num1.type === "literal") {
|
||||
// ... and first operands is a literal too
|
||||
return new AstLiteral(num1.number * num2.number);
|
||||
}
|
||||
}
|
||||
if (num1.type === "literal") {
|
||||
// optimization: first operands is a literal...
|
||||
if (num1.number === 0) {
|
||||
return new AstLiteral(0); // and it's 0
|
||||
} else if (num1.number === 1) {
|
||||
return num2; // and it's 1
|
||||
}
|
||||
}
|
||||
const min = Math.min(
|
||||
num1.min * num2.min,
|
||||
num1.min * num2.max,
|
||||
num1.max * num2.min,
|
||||
num1.max * num2.max
|
||||
);
|
||||
const max = Math.max(
|
||||
num1.min * num2.min,
|
||||
num1.min * num2.max,
|
||||
num1.max * num2.min,
|
||||
num1.max * num2.max
|
||||
);
|
||||
return new AstBinaryOperation("*", num1, num2, min, max);
|
||||
}
|
||||
|
||||
function buildSubOperation(num1, num2) {
|
||||
if (num2.type === "literal") {
|
||||
// optimization: second operands is a literal...
|
||||
if (num2.number === 0) {
|
||||
return num1; // ... and it's 0
|
||||
} else if (num1.type === "literal") {
|
||||
// ... and first operands is a literal too
|
||||
return new AstLiteral(num1.number - num2.number);
|
||||
}
|
||||
}
|
||||
if (
|
||||
num2.type === "binary" &&
|
||||
num2.op === "-" &&
|
||||
num1.type === "literal" &&
|
||||
num1.number === 1 &&
|
||||
num2.arg1.type === "literal" &&
|
||||
num2.arg1.number === 1
|
||||
) {
|
||||
// optimization for case: 1 - (1 - x)
|
||||
return num2.arg2;
|
||||
}
|
||||
return new AstBinaryOperation(
|
||||
"-",
|
||||
num1,
|
||||
num2,
|
||||
num1.min - num2.max,
|
||||
num1.max - num2.min
|
||||
);
|
||||
}
|
||||
|
||||
function buildMinOperation(num1, max) {
|
||||
if (num1.min >= max) {
|
||||
// optimization: num1 min value is not less than required max
|
||||
return new AstLiteral(max); // just returning max
|
||||
} else if (num1.max <= max) {
|
||||
// optimization: num1 max value is not greater than required max
|
||||
return num1; // just returning an argument
|
||||
}
|
||||
return new AstMin(num1, max);
|
||||
}
|
||||
|
||||
// Most of the PDFs functions consist of simple operations such as:
|
||||
// roll, exch, sub, cvr, pop, index, dup, mul, if, gt, add.
|
||||
//
|
||||
// We can compile most of such programs, and at the same moment, we can
|
||||
// optimize some expressions using basic math properties. Keeping track of
|
||||
// min/max values will allow us to avoid extra Math.min/Math.max calls.
|
||||
class PostScriptCompiler {
|
||||
compile(code, domain, range) {
|
||||
const stack = [];
|
||||
const instructions = [];
|
||||
const inputSize = domain.length >> 1,
|
||||
outputSize = range.length >> 1;
|
||||
let lastRegister = 0;
|
||||
let n, j;
|
||||
let num1, num2, ast1, ast2, tmpVar, item;
|
||||
for (let i = 0; i < inputSize; i++) {
|
||||
stack.push(new AstArgument(i, domain[i * 2], domain[i * 2 + 1]));
|
||||
}
|
||||
|
||||
for (let i = 0, ii = code.length; i < ii; i++) {
|
||||
item = code[i];
|
||||
if (typeof item === "number") {
|
||||
stack.push(new AstLiteral(item));
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (item) {
|
||||
case "add":
|
||||
if (stack.length < 2) {
|
||||
return null;
|
||||
}
|
||||
num2 = stack.pop();
|
||||
num1 = stack.pop();
|
||||
stack.push(buildAddOperation(num1, num2));
|
||||
break;
|
||||
case "cvr":
|
||||
if (stack.length < 1) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
case "mul":
|
||||
if (stack.length < 2) {
|
||||
return null;
|
||||
}
|
||||
num2 = stack.pop();
|
||||
num1 = stack.pop();
|
||||
stack.push(buildMulOperation(num1, num2));
|
||||
break;
|
||||
case "sub":
|
||||
if (stack.length < 2) {
|
||||
return null;
|
||||
}
|
||||
num2 = stack.pop();
|
||||
num1 = stack.pop();
|
||||
stack.push(buildSubOperation(num1, num2));
|
||||
break;
|
||||
case "exch":
|
||||
if (stack.length < 2) {
|
||||
return null;
|
||||
}
|
||||
ast1 = stack.pop();
|
||||
ast2 = stack.pop();
|
||||
stack.push(ast1, ast2);
|
||||
break;
|
||||
case "pop":
|
||||
if (stack.length < 1) {
|
||||
return null;
|
||||
}
|
||||
stack.pop();
|
||||
break;
|
||||
case "index":
|
||||
if (stack.length < 1) {
|
||||
return null;
|
||||
}
|
||||
num1 = stack.pop();
|
||||
if (num1.type !== "literal") {
|
||||
return null;
|
||||
}
|
||||
n = num1.number;
|
||||
if (n < 0 || !Number.isInteger(n) || stack.length < n) {
|
||||
return null;
|
||||
}
|
||||
ast1 = stack[stack.length - n - 1];
|
||||
if (ast1.type === "literal" || ast1.type === "var") {
|
||||
stack.push(ast1);
|
||||
break;
|
||||
}
|
||||
tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
|
||||
stack[stack.length - n - 1] = tmpVar;
|
||||
stack.push(tmpVar);
|
||||
instructions.push(new AstVariableDefinition(tmpVar, ast1));
|
||||
break;
|
||||
case "dup":
|
||||
if (stack.length < 1) {
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
typeof code[i + 1] === "number" &&
|
||||
code[i + 2] === "gt" &&
|
||||
code[i + 3] === i + 7 &&
|
||||
code[i + 4] === "jz" &&
|
||||
code[i + 5] === "pop" &&
|
||||
code[i + 6] === code[i + 1]
|
||||
) {
|
||||
// special case of the commands sequence for the min operation
|
||||
num1 = stack.pop();
|
||||
stack.push(buildMinOperation(num1, code[i + 1]));
|
||||
i += 6;
|
||||
break;
|
||||
}
|
||||
ast1 = stack.at(-1);
|
||||
if (ast1.type === "literal" || ast1.type === "var") {
|
||||
// we don't have to save into intermediate variable a literal or
|
||||
// variable.
|
||||
stack.push(ast1);
|
||||
break;
|
||||
}
|
||||
tmpVar = new AstVariable(lastRegister++, ast1.min, ast1.max);
|
||||
stack[stack.length - 1] = tmpVar;
|
||||
stack.push(tmpVar);
|
||||
instructions.push(new AstVariableDefinition(tmpVar, ast1));
|
||||
break;
|
||||
case "roll":
|
||||
if (stack.length < 2) {
|
||||
return null;
|
||||
}
|
||||
num2 = stack.pop();
|
||||
num1 = stack.pop();
|
||||
if (num2.type !== "literal" || num1.type !== "literal") {
|
||||
// both roll operands must be numbers
|
||||
return null;
|
||||
}
|
||||
j = num2.number;
|
||||
n = num1.number;
|
||||
if (
|
||||
n <= 0 ||
|
||||
!Number.isInteger(n) ||
|
||||
!Number.isInteger(j) ||
|
||||
stack.length < n
|
||||
) {
|
||||
// ... and integers
|
||||
return null;
|
||||
}
|
||||
j = ((j % n) + n) % n;
|
||||
if (j === 0) {
|
||||
break; // just skipping -- there are nothing to rotate
|
||||
}
|
||||
stack.push(...stack.splice(stack.length - n, n - j));
|
||||
break;
|
||||
default:
|
||||
return null; // unsupported operator
|
||||
}
|
||||
}
|
||||
|
||||
if (stack.length !== outputSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const result = [];
|
||||
for (const instruction of instructions) {
|
||||
const statementBuilder = new ExpressionBuilderVisitor();
|
||||
instruction.visit(statementBuilder);
|
||||
result.push(statementBuilder.toString());
|
||||
}
|
||||
for (let i = 0, ii = stack.length; i < ii; i++) {
|
||||
const expr = stack[i],
|
||||
statementBuilder = new ExpressionBuilderVisitor();
|
||||
expr.visit(statementBuilder);
|
||||
const min = range[i * 2],
|
||||
max = range[i * 2 + 1];
|
||||
const out = [statementBuilder.toString()];
|
||||
if (min > expr.min) {
|
||||
out.unshift("Math.max(", min, ", ");
|
||||
out.push(")");
|
||||
}
|
||||
if (max < expr.max) {
|
||||
out.unshift("Math.min(", max, ", ");
|
||||
out.push(")");
|
||||
}
|
||||
out.unshift("dest[destOffset + ", i, "] = ");
|
||||
out.push(";");
|
||||
result.push(out.join(""));
|
||||
}
|
||||
return result.join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
isPDFFunction,
|
||||
PDFFunctionFactory,
|
||||
PostScriptCompiler,
|
||||
PostScriptEvaluator,
|
||||
};
|
||||
export { isPDFFunction, PDFFunctionFactory };
|
||||
|
||||
@ -1,268 +0,0 @@
|
||||
/* Copyright 2014 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { FormatError, shadow } from "../shared/util.js";
|
||||
import { EOF } from "./primitives.js";
|
||||
import { isWhiteSpace } from "./core_utils.js";
|
||||
|
||||
class PostScriptParser {
|
||||
constructor(lexer) {
|
||||
this.lexer = lexer;
|
||||
this.operators = [];
|
||||
this.token = null;
|
||||
this.prev = null;
|
||||
}
|
||||
|
||||
nextToken() {
|
||||
this.prev = this.token;
|
||||
this.token = this.lexer.getToken();
|
||||
}
|
||||
|
||||
accept(type) {
|
||||
if (this.token.type === type) {
|
||||
this.nextToken();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
expect(type) {
|
||||
if (this.accept(type)) {
|
||||
return true;
|
||||
}
|
||||
throw new FormatError(
|
||||
`Unexpected symbol: found ${this.token.type} expected ${type}.`
|
||||
);
|
||||
}
|
||||
|
||||
parse() {
|
||||
this.nextToken();
|
||||
this.expect(PostScriptTokenTypes.LBRACE);
|
||||
this.parseBlock();
|
||||
this.expect(PostScriptTokenTypes.RBRACE);
|
||||
return this.operators;
|
||||
}
|
||||
|
||||
parseBlock() {
|
||||
while (true) {
|
||||
if (this.accept(PostScriptTokenTypes.NUMBER)) {
|
||||
this.operators.push(this.prev.value);
|
||||
} else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
|
||||
this.operators.push(this.prev.value);
|
||||
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||
this.parseCondition();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseCondition() {
|
||||
// Add two place holders that will be updated later
|
||||
const conditionLocation = this.operators.length;
|
||||
this.operators.push(null, null);
|
||||
|
||||
this.parseBlock();
|
||||
this.expect(PostScriptTokenTypes.RBRACE);
|
||||
if (this.accept(PostScriptTokenTypes.IF)) {
|
||||
// The true block is right after the 'if' so it just falls through on true
|
||||
// else it jumps and skips the true block.
|
||||
this.operators[conditionLocation] = this.operators.length;
|
||||
this.operators[conditionLocation + 1] = "jz";
|
||||
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||
const jumpLocation = this.operators.length;
|
||||
this.operators.push(null, null);
|
||||
const endOfTrue = this.operators.length;
|
||||
this.parseBlock();
|
||||
this.expect(PostScriptTokenTypes.RBRACE);
|
||||
this.expect(PostScriptTokenTypes.IFELSE);
|
||||
// The jump is added at the end of the true block to skip the false block.
|
||||
this.operators[jumpLocation] = this.operators.length;
|
||||
this.operators[jumpLocation + 1] = "j";
|
||||
|
||||
this.operators[conditionLocation] = endOfTrue;
|
||||
this.operators[conditionLocation + 1] = "jz";
|
||||
} else {
|
||||
throw new FormatError("PS Function: error parsing conditional.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const PostScriptTokenTypes = {
|
||||
LBRACE: 0,
|
||||
RBRACE: 1,
|
||||
NUMBER: 2,
|
||||
OPERATOR: 3,
|
||||
IF: 4,
|
||||
IFELSE: 5,
|
||||
};
|
||||
|
||||
class PostScriptToken {
|
||||
static get opCache() {
|
||||
return shadow(this, "opCache", Object.create(null));
|
||||
}
|
||||
|
||||
constructor(type, value) {
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
static getOperator(op) {
|
||||
return (PostScriptToken.opCache[op] ||= new PostScriptToken(
|
||||
PostScriptTokenTypes.OPERATOR,
|
||||
op
|
||||
));
|
||||
}
|
||||
|
||||
static get LBRACE() {
|
||||
return shadow(
|
||||
this,
|
||||
"LBRACE",
|
||||
new PostScriptToken(PostScriptTokenTypes.LBRACE, "{")
|
||||
);
|
||||
}
|
||||
|
||||
static get RBRACE() {
|
||||
return shadow(
|
||||
this,
|
||||
"RBRACE",
|
||||
new PostScriptToken(PostScriptTokenTypes.RBRACE, "}")
|
||||
);
|
||||
}
|
||||
|
||||
static get IF() {
|
||||
return shadow(
|
||||
this,
|
||||
"IF",
|
||||
new PostScriptToken(PostScriptTokenTypes.IF, "IF")
|
||||
);
|
||||
}
|
||||
|
||||
static get IFELSE() {
|
||||
return shadow(
|
||||
this,
|
||||
"IFELSE",
|
||||
new PostScriptToken(PostScriptTokenTypes.IFELSE, "IFELSE")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PostScriptLexer {
|
||||
constructor(stream) {
|
||||
this.stream = stream;
|
||||
this.nextChar();
|
||||
|
||||
this.strBuf = [];
|
||||
}
|
||||
|
||||
nextChar() {
|
||||
return (this.currentChar = this.stream.getByte());
|
||||
}
|
||||
|
||||
getToken() {
|
||||
let comment = false;
|
||||
let ch = this.currentChar;
|
||||
|
||||
// skip comments
|
||||
while (true) {
|
||||
if (ch < 0) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
if (ch === 0x0a || ch === 0x0d) {
|
||||
comment = false;
|
||||
}
|
||||
} else if (ch === /* '%' = */ 0x25) {
|
||||
comment = true;
|
||||
} else if (!isWhiteSpace(ch)) {
|
||||
break;
|
||||
}
|
||||
ch = this.nextChar();
|
||||
}
|
||||
switch (ch | 0) {
|
||||
case 0x30: // '0'
|
||||
case 0x31: // '1'
|
||||
case 0x32: // '2'
|
||||
case 0x33: // '3'
|
||||
case 0x34: // '4'
|
||||
case 0x35: // '5'
|
||||
case 0x36: // '6'
|
||||
case 0x37: // '7'
|
||||
case 0x38: // '8'
|
||||
case 0x39: // '9'
|
||||
case 0x2b: // '+'
|
||||
case 0x2d: // '-'
|
||||
case 0x2e: // '.'
|
||||
return new PostScriptToken(
|
||||
PostScriptTokenTypes.NUMBER,
|
||||
this.getNumber()
|
||||
);
|
||||
case 0x7b: // '{'
|
||||
this.nextChar();
|
||||
return PostScriptToken.LBRACE;
|
||||
case 0x7d: // '}'
|
||||
this.nextChar();
|
||||
return PostScriptToken.RBRACE;
|
||||
}
|
||||
// operator
|
||||
const strBuf = this.strBuf;
|
||||
strBuf.length = 0;
|
||||
strBuf[0] = String.fromCharCode(ch);
|
||||
|
||||
while (
|
||||
(ch = this.nextChar()) >= 0 &&
|
||||
((ch >= /* 'A' = */ 0x41 && ch <= /* 'Z' = */ 0x5a) ||
|
||||
(ch >= /* 'a' = */ 0x61 && ch <= /* 'z' = */ 0x7a))
|
||||
) {
|
||||
strBuf.push(String.fromCharCode(ch));
|
||||
}
|
||||
const str = strBuf.join("");
|
||||
switch (str.toLowerCase()) {
|
||||
case "if":
|
||||
return PostScriptToken.IF;
|
||||
case "ifelse":
|
||||
return PostScriptToken.IFELSE;
|
||||
default:
|
||||
return PostScriptToken.getOperator(str);
|
||||
}
|
||||
}
|
||||
|
||||
getNumber() {
|
||||
let ch = this.currentChar;
|
||||
const strBuf = this.strBuf;
|
||||
strBuf.length = 0;
|
||||
strBuf[0] = String.fromCharCode(ch);
|
||||
|
||||
while ((ch = this.nextChar()) >= 0) {
|
||||
if (
|
||||
(ch >= /* '0' = */ 0x30 && ch <= /* '9' = */ 0x39) ||
|
||||
ch === /* '-' = */ 0x2d ||
|
||||
ch === /* '.' = */ 0x2e
|
||||
) {
|
||||
strBuf.push(String.fromCharCode(ch));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const value = parseFloat(strBuf.join(""));
|
||||
if (isNaN(value)) {
|
||||
throw new FormatError(`Invalid floating point number: ${value}`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
export { PostScriptLexer, PostScriptParser };
|
||||
@ -159,9 +159,6 @@ const RENDERING_CANCELLED_TIMEOUT = 100; // ms
|
||||
* @property {number} [maxImageSize] - The maximum allowed image size in total
|
||||
* pixels, i.e. width * height. Images above this value will not be rendered.
|
||||
* Use -1 for no limit, which is also the default value.
|
||||
* @property {boolean} [isEvalSupported] - Determines if we can evaluate strings
|
||||
* as JavaScript. Primarily used to improve performance of PDF functions.
|
||||
* The default value is `true`.
|
||||
* @property {boolean} [isOffscreenCanvasSupported] - Determines if we can use
|
||||
* `OffscreenCanvas` in the worker. Primarily used to improve performance of
|
||||
* image conversion/rendering.
|
||||
@ -283,7 +280,6 @@ function getDocument(src = {}) {
|
||||
Number.isInteger(src.maxImageSize) && src.maxImageSize > -1
|
||||
? src.maxImageSize
|
||||
: -1;
|
||||
const isEvalSupported = src.isEvalSupported !== false;
|
||||
const isOffscreenCanvasSupported =
|
||||
typeof src.isOffscreenCanvasSupported === "boolean"
|
||||
? src.isOffscreenCanvasSupported
|
||||
@ -397,7 +393,6 @@ function getDocument(src = {}) {
|
||||
maxImageSize,
|
||||
disableFontFace,
|
||||
ignoreErrors,
|
||||
isEvalSupported,
|
||||
isOffscreenCanvasSupported,
|
||||
isImageDecoderSupported,
|
||||
canvasMaxAreaInBytes,
|
||||
|
||||
@ -620,25 +620,11 @@ function isLittleEndian() {
|
||||
return view32[0] === 1;
|
||||
}
|
||||
|
||||
// Checks if it's possible to eval JS expressions.
|
||||
function isEvalSupported() {
|
||||
try {
|
||||
new Function(""); // eslint-disable-line no-new, no-new-func
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureTest {
|
||||
static get isLittleEndian() {
|
||||
return shadow(this, "isLittleEndian", isLittleEndian());
|
||||
}
|
||||
|
||||
static get isEvalSupported() {
|
||||
return shadow(this, "isEvalSupported", isEvalSupported());
|
||||
}
|
||||
|
||||
static get isOffscreenCanvasSupported() {
|
||||
return shadow(
|
||||
this,
|
||||
|
||||
@ -57,7 +57,6 @@ describe("annotation", function () {
|
||||
},
|
||||
};
|
||||
this.evaluatorOptions = {
|
||||
isEvalSupported: true,
|
||||
isOffscreenCanvasSupported: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
"event_utils_spec.js",
|
||||
"fetch_stream_spec.js",
|
||||
"font_substitutions_spec.js",
|
||||
"function_spec.js",
|
||||
"image_utils_spec.js",
|
||||
"message_handler_spec.js",
|
||||
"metadata_spec.js",
|
||||
|
||||
@ -57,19 +57,14 @@ describe("Default appearance", function () {
|
||||
});
|
||||
|
||||
describe("parseAppearanceStream", () => {
|
||||
let evaluatorOptions, xref, globalColorSpaceCache;
|
||||
let xref, globalColorSpaceCache;
|
||||
|
||||
beforeAll(function () {
|
||||
evaluatorOptions = {
|
||||
isEvalSupported: true,
|
||||
isOffscreenCanvasSupported: false,
|
||||
};
|
||||
xref = new XRefMock();
|
||||
globalColorSpaceCache = new GlobalColorSpaceCache();
|
||||
});
|
||||
|
||||
afterAll(function () {
|
||||
evaluatorOptions = null;
|
||||
xref = null;
|
||||
globalColorSpaceCache = null;
|
||||
});
|
||||
@ -105,12 +100,7 @@ describe("Default appearance", function () {
|
||||
fontColor: new Uint8ClampedArray([107, 217, 41]),
|
||||
};
|
||||
expect(
|
||||
parseAppearanceStream(
|
||||
appearance,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
)
|
||||
parseAppearanceStream(appearance, xref, globalColorSpaceCache)
|
||||
).toEqual(result);
|
||||
expect(appearance.pos).toEqual(0);
|
||||
});
|
||||
@ -131,12 +121,7 @@ describe("Default appearance", function () {
|
||||
fontColor: new Uint8ClampedArray([237, 43, 112]),
|
||||
};
|
||||
expect(
|
||||
parseAppearanceStream(
|
||||
appearance,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
)
|
||||
parseAppearanceStream(appearance, xref, globalColorSpaceCache)
|
||||
).toEqual(result);
|
||||
expect(appearance.pos).toEqual(0);
|
||||
});
|
||||
@ -173,12 +158,7 @@ describe("Default appearance", function () {
|
||||
fontColor: new Uint8ClampedArray([135, 78, 254]),
|
||||
};
|
||||
expect(
|
||||
parseAppearanceStream(
|
||||
appearance,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
)
|
||||
parseAppearanceStream(appearance, xref, globalColorSpaceCache)
|
||||
).toEqual(result);
|
||||
expect(appearance.pos).toEqual(0);
|
||||
});
|
||||
@ -201,12 +181,7 @@ describe("Default appearance", function () {
|
||||
fontColor: new Uint8ClampedArray([16, 124, 16]),
|
||||
};
|
||||
expect(
|
||||
parseAppearanceStream(
|
||||
appearance,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
)
|
||||
parseAppearanceStream(appearance, xref, globalColorSpaceCache)
|
||||
).toEqual(result);
|
||||
expect(appearance.pos).toEqual(0);
|
||||
});
|
||||
@ -232,12 +207,7 @@ describe("Default appearance", function () {
|
||||
fontColor: new Uint8ClampedArray([149, 63, 60]),
|
||||
};
|
||||
expect(
|
||||
parseAppearanceStream(
|
||||
appearance,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
)
|
||||
parseAppearanceStream(appearance, xref, globalColorSpaceCache)
|
||||
).toEqual(result);
|
||||
expect(appearance.pos).toEqual(0);
|
||||
});
|
||||
@ -261,12 +231,7 @@ describe("Default appearance", function () {
|
||||
fontColor: new Uint8ClampedArray([0, 85, 127]),
|
||||
};
|
||||
expect(
|
||||
parseAppearanceStream(
|
||||
appearance,
|
||||
evaluatorOptions,
|
||||
xref,
|
||||
globalColorSpaceCache
|
||||
)
|
||||
parseAppearanceStream(appearance, xref, globalColorSpaceCache)
|
||||
).toEqual(result);
|
||||
expect(appearance.pos).toEqual(0);
|
||||
});
|
||||
|
||||
@ -1,583 +0,0 @@
|
||||
/* Copyright 2017 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
PostScriptCompiler,
|
||||
PostScriptEvaluator,
|
||||
} from "../../src/core/function.js";
|
||||
import { PostScriptLexer, PostScriptParser } from "../../src/core/ps_parser.js";
|
||||
import { StringStream } from "../../src/core/stream.js";
|
||||
|
||||
describe("function", function () {
|
||||
describe("PostScriptParser", function () {
|
||||
function parse(program) {
|
||||
const stream = new StringStream(program);
|
||||
const parser = new PostScriptParser(new PostScriptLexer(stream));
|
||||
return parser.parse();
|
||||
}
|
||||
it("parses empty programs", function () {
|
||||
const output = parse("{}");
|
||||
expect(output.length).toEqual(0);
|
||||
});
|
||||
it("parses positive numbers", function () {
|
||||
const number = 999;
|
||||
const program = parse("{ " + number + " }");
|
||||
const expectedProgram = [number];
|
||||
expect(program).toEqual(expectedProgram);
|
||||
});
|
||||
it("parses negative numbers", function () {
|
||||
const number = -999;
|
||||
const program = parse("{ " + number + " }");
|
||||
const expectedProgram = [number];
|
||||
expect(program).toEqual(expectedProgram);
|
||||
});
|
||||
it("parses negative floats", function () {
|
||||
const number = 3.3;
|
||||
const program = parse("{ " + number + " }");
|
||||
const expectedProgram = [number];
|
||||
expect(program).toEqual(expectedProgram);
|
||||
});
|
||||
it("parses operators", function () {
|
||||
const program = parse("{ sub }");
|
||||
const expectedProgram = ["sub"];
|
||||
expect(program).toEqual(expectedProgram);
|
||||
});
|
||||
it("parses if statements", function () {
|
||||
const program = parse("{ { 99 } if }");
|
||||
const expectedProgram = [3, "jz", 99];
|
||||
expect(program).toEqual(expectedProgram);
|
||||
});
|
||||
it("parses ifelse statements", function () {
|
||||
const program = parse("{ { 99 } { 44 } ifelse }");
|
||||
const expectedProgram = [5, "jz", 99, 6, "j", 44];
|
||||
expect(program).toEqual(expectedProgram);
|
||||
});
|
||||
it("handles missing brackets", function () {
|
||||
expect(function () {
|
||||
parse("{");
|
||||
}).toThrow(new Error("Unexpected symbol: found undefined expected 1."));
|
||||
});
|
||||
it("handles junk after the end", function () {
|
||||
const number = 3.3;
|
||||
const program = parse("{ " + number + " }#");
|
||||
const expectedProgram = [number];
|
||||
expect(program).toEqual(expectedProgram);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PostScriptEvaluator", function () {
|
||||
function evaluate(program) {
|
||||
const stream = new StringStream(program);
|
||||
const parser = new PostScriptParser(new PostScriptLexer(stream));
|
||||
const code = parser.parse();
|
||||
const evaluator = new PostScriptEvaluator(code);
|
||||
const output = evaluator.execute();
|
||||
return output;
|
||||
}
|
||||
|
||||
it("pushes stack", function () {
|
||||
const stack = evaluate("{ 99 }");
|
||||
const expectedStack = [99];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles if with true", function () {
|
||||
const stack = evaluate("{ 1 {99} if }");
|
||||
const expectedStack = [99];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles if with false", function () {
|
||||
const stack = evaluate("{ 0 {99} if }");
|
||||
const expectedStack = [];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles ifelse with true", function () {
|
||||
const stack = evaluate("{ 1 {99} {77} ifelse }");
|
||||
const expectedStack = [99];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles ifelse with false", function () {
|
||||
const stack = evaluate("{ 0 {99} {77} ifelse }");
|
||||
const expectedStack = [77];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles nested if", function () {
|
||||
const stack = evaluate("{ 1 {1 {77} if} if }");
|
||||
const expectedStack = [77];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
|
||||
it("abs", function () {
|
||||
const stack = evaluate("{ -2 abs }");
|
||||
const expectedStack = [2];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("adds", function () {
|
||||
const stack = evaluate("{ 1 2 add }");
|
||||
const expectedStack = [3];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("boolean and", function () {
|
||||
const stack = evaluate("{ true false and }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("bitwise and", function () {
|
||||
const stack = evaluate("{ 254 1 and }");
|
||||
const expectedStack = [254 & 1];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("the angle in degrees (0-360) whose tangent is num/den.", function () {
|
||||
const stack = evaluate("{ 1 -1 atan }");
|
||||
const expectedStack = [135];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles bitshifting ", function () {
|
||||
const stack = evaluate("{ 50 2 bitshift }");
|
||||
const expectedStack = [200];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates the ceiling value", function () {
|
||||
const stack = evaluate("{ 9.9 ceiling }");
|
||||
const expectedStack = [10];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("copies", function () {
|
||||
const stack = evaluate("{ 99 98 2 copy }");
|
||||
const expectedStack = [99, 98, 99, 98];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates the cosine of an angle in degrees", function () {
|
||||
const stack = evaluate("{ 180 cos }");
|
||||
const expectedStack = [-1];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("converts to int", function () {
|
||||
const stack = evaluate("{ 9.9 cvi }");
|
||||
const expectedStack = [9];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("converts negatives to int", function () {
|
||||
const stack = evaluate("{ -9.9 cvi }");
|
||||
const expectedStack = [-9];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("converts to real", function () {
|
||||
const stack = evaluate("{ 55.34 cvr }");
|
||||
const expectedStack = [55.34];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("divides", function () {
|
||||
const stack = evaluate("{ 6 5 div }");
|
||||
const expectedStack = [1.2];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("maps division by zero to infinity", function () {
|
||||
const stack = evaluate("{ 6 0 div }");
|
||||
const expectedStack = [Infinity];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("duplicates", function () {
|
||||
const stack = evaluate("{ 99 dup }");
|
||||
const expectedStack = [99, 99];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("accepts an equality", function () {
|
||||
const stack = evaluate("{ 9 9 eq }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rejects an inequality", function () {
|
||||
const stack = evaluate("{ 9 8 eq }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("exchanges", function () {
|
||||
const stack = evaluate("{ 44 99 exch }");
|
||||
const expectedStack = [99, 44];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles exponentiation", function () {
|
||||
const stack = evaluate("{ 10 2 exp }");
|
||||
const expectedStack = [100];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("pushes false onto the stack", function () {
|
||||
const stack = evaluate("{ false }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates the floor value", function () {
|
||||
const stack = evaluate("{ 9.9 floor }");
|
||||
const expectedStack = [9];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles greater than or equal to", function () {
|
||||
const stack = evaluate("{ 10 9 ge }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rejects less than for greater than or equal to", function () {
|
||||
const stack = evaluate("{ 8 9 ge }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles greater than", function () {
|
||||
const stack = evaluate("{ 10 9 gt }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rejects less than or equal for greater than", function () {
|
||||
const stack = evaluate("{ 9 9 gt }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("divides to integer", function () {
|
||||
const stack = evaluate("{ 2 3 idiv }");
|
||||
const expectedStack = [0];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("divides to negative integer", function () {
|
||||
const stack = evaluate("{ -2 3 idiv }");
|
||||
const expectedStack = [0];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("duplicates index", function () {
|
||||
const stack = evaluate("{ 4 3 2 1 2 index }");
|
||||
const expectedStack = [4, 3, 2, 1, 3];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles less than or equal to", function () {
|
||||
const stack = evaluate("{ 9 10 le }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rejects greater than for less than or equal to", function () {
|
||||
const stack = evaluate("{ 10 9 le }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates the natural logarithm", function () {
|
||||
const stack = evaluate("{ 10 ln }");
|
||||
const expectedStack = [Math.log(10)];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates the base 10 logarithm", function () {
|
||||
const stack = evaluate("{ 100 log }");
|
||||
const expectedStack = [2];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("handles less than", function () {
|
||||
const stack = evaluate("{ 9 10 lt }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rejects greater than or equal to for less than", function () {
|
||||
const stack = evaluate("{ 10 9 lt }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("performs the modulo operation", function () {
|
||||
const stack = evaluate("{ 4 3 mod }");
|
||||
const expectedStack = [1];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("multiplies two numbers (positive result)", function () {
|
||||
const stack = evaluate("{ 9 8 mul }");
|
||||
const expectedStack = [72];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("multiplies two numbers (negative result)", function () {
|
||||
const stack = evaluate("{ 9 -8 mul }");
|
||||
const expectedStack = [-72];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("accepts an inequality", function () {
|
||||
const stack = evaluate("{ 9 8 ne }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rejects an equality", function () {
|
||||
const stack = evaluate("{ 9 9 ne }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("negates", function () {
|
||||
const stack = evaluate("{ 4.5 neg }");
|
||||
const expectedStack = [-4.5];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("boolean not", function () {
|
||||
const stack = evaluate("{ true not }");
|
||||
const expectedStack = [false];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("bitwise not", function () {
|
||||
const stack = evaluate("{ 12 not }");
|
||||
const expectedStack = [-13];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("boolean or", function () {
|
||||
const stack = evaluate("{ true false or }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("bitwise or", function () {
|
||||
const stack = evaluate("{ 254 1 or }");
|
||||
const expectedStack = [254 | 1];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("pops stack", function () {
|
||||
const stack = evaluate("{ 1 2 pop }");
|
||||
const expectedStack = [1];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rolls stack right", function () {
|
||||
const stack = evaluate("{ 1 3 2 2 4 1 roll }");
|
||||
const expectedStack = [2, 1, 3, 2];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rolls stack left", function () {
|
||||
const stack = evaluate("{ 1 3 2 2 4 -1 roll }");
|
||||
const expectedStack = [3, 2, 2, 1];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("rounds a number", function () {
|
||||
const stack = evaluate("{ 9.52 round }");
|
||||
const expectedStack = [10];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates the sine of an angle in degrees", function () {
|
||||
const stack = evaluate("{ 90 sin }");
|
||||
const expectedStack = [1];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates a square root (integer)", function () {
|
||||
const stack = evaluate("{ 100 sqrt }");
|
||||
const expectedStack = [10];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates a square root (float)", function () {
|
||||
const stack = evaluate("{ 99 sqrt }");
|
||||
const expectedStack = [Math.sqrt(99)];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("subtracts (positive result)", function () {
|
||||
const stack = evaluate("{ 6 4 sub }");
|
||||
const expectedStack = [2];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("subtracts (negative result)", function () {
|
||||
const stack = evaluate("{ 4 6 sub }");
|
||||
const expectedStack = [-2];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("pushes true onto the stack", function () {
|
||||
const stack = evaluate("{ true }");
|
||||
const expectedStack = [true];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("truncates a number", function () {
|
||||
const stack = evaluate("{ 35.004 truncate }");
|
||||
const expectedStack = [35];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
it("calculates an exclusive or value", function () {
|
||||
const stack = evaluate("{ 3 9 xor }");
|
||||
const expectedStack = [10];
|
||||
expect(stack).toEqual(expectedStack);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PostScriptCompiler", function () {
|
||||
function check(code, domain, range, samples) {
|
||||
const compiler = new PostScriptCompiler();
|
||||
const compiledCode = compiler.compile(code, domain, range);
|
||||
if (samples === null) {
|
||||
expect(compiledCode).toBeNull();
|
||||
} else {
|
||||
expect(compiledCode).not.toBeNull();
|
||||
// eslint-disable-next-line no-new-func
|
||||
const fn = new Function(
|
||||
"src",
|
||||
"srcOffset",
|
||||
"dest",
|
||||
"destOffset",
|
||||
compiledCode
|
||||
);
|
||||
for (const { input, output } of samples) {
|
||||
const out = new Float32Array(output.length);
|
||||
fn(input, 0, out, 0);
|
||||
expect(Array.from(out)).toEqual(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
it("check compiled add", function () {
|
||||
check([0.25, 0.5, "add"], [], [0, 1], [{ input: [], output: [0.75] }]);
|
||||
check([0, "add"], [0, 1], [0, 1], [{ input: [0.25], output: [0.25] }]);
|
||||
check([0.5, "add"], [0, 1], [0, 1], [{ input: [0.25], output: [0.75] }]);
|
||||
check(
|
||||
[0, "exch", "add"],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.25], output: [0.25] }]
|
||||
);
|
||||
check(
|
||||
[0.5, "exch", "add"],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.25], output: [0.75] }]
|
||||
);
|
||||
check(
|
||||
["add"],
|
||||
[0, 1, 0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.25, 0.5], output: [0.75] }]
|
||||
);
|
||||
check(["add"], [0, 1], [0, 1], null);
|
||||
});
|
||||
it("check compiled sub", function () {
|
||||
check([0.5, 0.25, "sub"], [], [0, 1], [{ input: [], output: [0.25] }]);
|
||||
check([0, "sub"], [0, 1], [0, 1], [{ input: [0.25], output: [0.25] }]);
|
||||
check([0.5, "sub"], [0, 1], [0, 1], [{ input: [0.75], output: [0.25] }]);
|
||||
check(
|
||||
[0, "exch", "sub"],
|
||||
[0, 1],
|
||||
[-1, 1],
|
||||
[{ input: [0.25], output: [-0.25] }]
|
||||
);
|
||||
check(
|
||||
[0.75, "exch", "sub"],
|
||||
[0, 1],
|
||||
[-1, 1],
|
||||
[{ input: [0.25], output: [0.5] }]
|
||||
);
|
||||
check(
|
||||
["sub"],
|
||||
[0, 1, 0, 1],
|
||||
[-1, 1],
|
||||
[{ input: [0.25, 0.5], output: [-0.25] }]
|
||||
);
|
||||
check(["sub"], [0, 1], [0, 1], null);
|
||||
|
||||
check(
|
||||
[1, "dup", 3, 2, "roll", "sub", "sub"],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.75], output: [0.75] }]
|
||||
);
|
||||
});
|
||||
it("check compiled mul", function () {
|
||||
check([0.25, 0.5, "mul"], [], [0, 1], [{ input: [], output: [0.125] }]);
|
||||
check([0, "mul"], [0, 1], [0, 1], [{ input: [0.25], output: [0] }]);
|
||||
check([0.5, "mul"], [0, 1], [0, 1], [{ input: [0.25], output: [0.125] }]);
|
||||
check([1, "mul"], [0, 1], [0, 1], [{ input: [0.25], output: [0.25] }]);
|
||||
check(
|
||||
[0, "exch", "mul"],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.25], output: [0] }]
|
||||
);
|
||||
check(
|
||||
[0.5, "exch", "mul"],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.25], output: [0.125] }]
|
||||
);
|
||||
check(
|
||||
[1, "exch", "mul"],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.25], output: [0.25] }]
|
||||
);
|
||||
check(
|
||||
["mul"],
|
||||
[0, 1, 0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.25, 0.5], output: [0.125] }]
|
||||
);
|
||||
check(["mul"], [0, 1], [0, 1], null);
|
||||
});
|
||||
it("check compiled max", function () {
|
||||
check(
|
||||
["dup", 0.75, "gt", 7, "jz", "pop", 0.75],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [0.5], output: [0.5] }]
|
||||
);
|
||||
check(
|
||||
["dup", 0.75, "gt", 7, "jz", "pop", 0.75],
|
||||
[0, 1],
|
||||
[0, 1],
|
||||
[{ input: [1], output: [0.75] }]
|
||||
);
|
||||
check(["dup", 0.75, "gt", 5, "jz", "pop", 0.75], [0, 1], [0, 1], null);
|
||||
});
|
||||
it("check pop/roll/index", function () {
|
||||
check([1, "pop"], [0, 1], [0, 1], [{ input: [0.5], output: [0.5] }]);
|
||||
check(
|
||||
[1, 3, -1, "roll"],
|
||||
[0, 1, 0, 1],
|
||||
[0, 1, 0, 1, 0, 1],
|
||||
[{ input: [0.25, 0.5], output: [0.5, 1, 0.25] }]
|
||||
);
|
||||
check(
|
||||
[1, 3, 1, "roll"],
|
||||
[0, 1, 0, 1],
|
||||
[0, 1, 0, 1, 0, 1],
|
||||
[{ input: [0.25, 0.5], output: [1, 0.25, 0.5] }]
|
||||
);
|
||||
check([1, 3, 1.5, "roll"], [0, 1, 0, 1], [0, 1, 0, 1, 0, 1], null);
|
||||
check(
|
||||
[1, 1, "index"],
|
||||
[0, 1],
|
||||
[0, 1, 0, 1, 0, 1],
|
||||
[{ input: [0.5], output: [0.5, 1, 0.5] }]
|
||||
);
|
||||
check([1, 3, "index", "pop"], [0, 1], [0, 1], null);
|
||||
check([1, 0.5, "index", "pop"], [0, 1], [0, 1], null);
|
||||
});
|
||||
it("check input boundaries", function () {
|
||||
check([], [0, 0.5], [0, 1], [{ input: [1], output: [0.5] }]);
|
||||
check([], [0.5, 1], [0, 1], [{ input: [0], output: [0.5] }]);
|
||||
check(
|
||||
["dup"],
|
||||
[0.5, 0.75],
|
||||
[0, 1, 0, 1],
|
||||
[{ input: [0], output: [0.5, 0.5] }]
|
||||
);
|
||||
check([], [100, 1001], [0, 10000], [{ input: [1000], output: [1000] }]);
|
||||
});
|
||||
it("check output boundaries", function () {
|
||||
check([], [0, 1], [0, 0.5], [{ input: [1], output: [0.5] }]);
|
||||
check([], [0, 1], [0.5, 1], [{ input: [0], output: [0.5] }]);
|
||||
check(
|
||||
["dup"],
|
||||
[0, 1],
|
||||
[0.5, 1, 0.75, 1],
|
||||
[{ input: [0], output: [0.5, 0.75] }]
|
||||
);
|
||||
check([], [0, 10000], [100, 1001], [{ input: [1000], output: [1000] }]);
|
||||
});
|
||||
it("compile optimized", function () {
|
||||
const compiler = new PostScriptCompiler();
|
||||
const code = [0, "add", 1, 1, 3, -1, "roll", "sub", "sub", 1, "mul"];
|
||||
const compiledCode = compiler.compile(code, [0, 1], [0, 1]);
|
||||
expect(compiledCode).toEqual(
|
||||
"dest[destOffset + 0] = Math.max(0, Math.min(1, src[srcOffset + 0]));"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -69,7 +69,6 @@ async function initializePDFJS(callback) {
|
||||
"pdfjs-test/unit/event_utils_spec.js",
|
||||
"pdfjs-test/unit/fetch_stream_spec.js",
|
||||
"pdfjs-test/unit/font_substitutions_spec.js",
|
||||
"pdfjs-test/unit/function_spec.js",
|
||||
"pdfjs-test/unit/image_utils_spec.js",
|
||||
"pdfjs-test/unit/message_handler_spec.js",
|
||||
"pdfjs-test/unit/metadata_spec.js",
|
||||
|
||||
@ -486,11 +486,6 @@ const defaultOptions = {
|
||||
: "../web/iccs/",
|
||||
kind: OptionKind.API,
|
||||
},
|
||||
isEvalSupported: {
|
||||
/** @type {boolean} */
|
||||
value: true,
|
||||
kind: OptionKind.API,
|
||||
},
|
||||
isOffscreenCanvasSupported: {
|
||||
/** @type {boolean} */
|
||||
value: true,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user