[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:
Jonas Jenwald 2026-03-30 09:52:50 +02:00
parent 5347c22703
commit f6bac014ea
14 changed files with 25 additions and 1686 deletions

View File

@ -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
);

View File

@ -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();

View File

@ -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(),
});

View File

@ -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() {

View File

@ -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 };

View File

@ -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 };

View File

@ -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,

View File

@ -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,

View File

@ -57,7 +57,6 @@ describe("annotation", function () {
},
};
this.evaluatorOptions = {
isEvalSupported: true,
isOffscreenCanvasSupported: false,
};
}

View File

@ -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",

View File

@ -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);
});

View File

@ -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]));"
);
});
});
});

View File

@ -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",

View File

@ -486,11 +486,6 @@ const defaultOptions = {
: "../web/iccs/",
kind: OptionKind.API,
},
isEvalSupported: {
/** @type {boolean} */
value: true,
kind: OptionKind.API,
},
isOffscreenCanvasSupported: {
/** @type {boolean} */
value: true,