Compare commits

..

No commits in common. "d009e4b3a743661d91d4c0d97367b7abe51d2b18" and "11e89b74d926fa121945a4aee95d24d46257e94b" have entirely different histories.

11 changed files with 530 additions and 486 deletions

View File

@ -16,7 +16,6 @@
import { import {
AbortException, AbortException,
assert, assert,
DrawOPS,
FONT_IDENTITY_MATRIX, FONT_IDENTITY_MATRIX,
FormatError, FormatError,
IDENTITY_MATRIX, IDENTITY_MATRIX,
@ -926,7 +925,7 @@ class PartialEvaluator {
smaskOptions, smaskOptions,
operatorList, operatorList,
task, task,
stateManager.state.clone({ newPath: true }), stateManager.state.clone(),
localColorSpaceCache localColorSpaceCache
); );
} }
@ -1384,112 +1383,80 @@ class PartialEvaluator {
return promise; return promise;
} }
buildPath(fn, args, state) { buildPath(operatorList, fn, args, parsingText = false) {
const { pathMinMax: minMax, pathBuffer } = state; const lastIndex = operatorList.length - 1;
switch (fn | 0) { if (!args) {
case OPS.rectangle: { args = [];
const x = (state.currentPointX = args[0]); }
const y = (state.currentPointY = args[1]); if (
const width = args[2]; lastIndex < 0 ||
const height = args[3]; operatorList.fnArray[lastIndex] !== OPS.constructPath
const xw = x + width; ) {
const yh = y + height; // Handle corrupt PDF documents that contains path operators inside of
if (width === 0 || height === 0) { // text objects, which may shift subsequent text, by enclosing the path
pathBuffer.push( // operator in save/restore operators (fixes issue10542_reduced.pdf).
DrawOPS.moveTo, //
x, // Note that this will effectively disable the optimization in the
y, // `else` branch below, but given that this type of corruption is
DrawOPS.lineTo, // *extremely* rare that shouldn't really matter much in practice.
xw, if (parsingText) {
yh, warn(`Encountered path operator "${fn}" inside of a text object.`);
DrawOPS.closePath operatorList.addOp(OPS.save, null);
); }
let minMax;
switch (fn) {
case OPS.rectangle:
const x = args[0] + args[2];
const y = args[1] + args[3];
minMax = [
Math.min(args[0], x),
Math.min(args[1], y),
Math.max(args[0], x),
Math.max(args[1], y),
];
break;
case OPS.moveTo:
case OPS.lineTo:
minMax = [args[0], args[1], args[0], args[1]];
break;
default:
minMax = [Infinity, Infinity, -Infinity, -Infinity];
break;
}
operatorList.addOp(OPS.constructPath, [[fn], args, minMax]);
if (parsingText) {
operatorList.addOp(OPS.restore, null);
}
} else { } else {
pathBuffer.push( const opArgs = operatorList.argsArray[lastIndex];
DrawOPS.moveTo, opArgs[0].push(fn);
x, opArgs[1].push(...args);
y, const minMax = opArgs[2];
DrawOPS.lineTo,
xw, // Compute min/max in the worker instead of the main thread.
y, // If the current matrix (when drawing) is a scaling one
DrawOPS.lineTo, // then min/max can be easily computed in using those values.
xw, // Only rectangle, lineTo and moveTo are handled here since
yh, // Bezier stuff requires to have the starting point.
DrawOPS.lineTo, switch (fn) {
x, case OPS.rectangle:
yh, const x = args[0] + args[2];
DrawOPS.closePath const y = args[1] + args[3];
); minMax[0] = Math.min(minMax[0], args[0], x);
} minMax[1] = Math.min(minMax[1], args[1], y);
minMax[0] = Math.min(minMax[0], x, xw); minMax[2] = Math.max(minMax[2], args[0], x);
minMax[1] = Math.min(minMax[1], y, yh); minMax[3] = Math.max(minMax[3], args[1], y);
minMax[2] = Math.max(minMax[2], x, xw); break;
minMax[3] = Math.max(minMax[3], y, yh); case OPS.moveTo:
case OPS.lineTo:
minMax[0] = Math.min(minMax[0], args[0]);
minMax[1] = Math.min(minMax[1], args[1]);
minMax[2] = Math.max(minMax[2], args[0]);
minMax[3] = Math.max(minMax[3], args[1]);
break; break;
} }
case OPS.moveTo: {
const x = (state.currentPointX = args[0]);
const y = (state.currentPointY = args[1]);
pathBuffer.push(DrawOPS.moveTo, x, y);
minMax[0] = Math.min(minMax[0], x);
minMax[1] = Math.min(minMax[1], y);
minMax[2] = Math.max(minMax[2], x);
minMax[3] = Math.max(minMax[3], y);
break;
}
case OPS.lineTo: {
const x = (state.currentPointX = args[0]);
const y = (state.currentPointY = args[1]);
pathBuffer.push(DrawOPS.lineTo, x, y);
minMax[0] = Math.min(minMax[0], x);
minMax[1] = Math.min(minMax[1], y);
minMax[2] = Math.max(minMax[2], x);
minMax[3] = Math.max(minMax[3], y);
break;
}
case OPS.curveTo: {
const startX = state.currentPointX;
const startY = state.currentPointY;
const [x1, y1, x2, y2, x, y] = args;
state.currentPointX = x;
state.currentPointY = y;
pathBuffer.push(DrawOPS.curveTo, x1, y1, x2, y2, x, y);
Util.bezierBoundingBox(startX, startY, x1, y1, x2, y2, x, y, minMax);
break;
}
case OPS.curveTo2: {
const startX = state.currentPointX;
const startY = state.currentPointY;
const [x1, y1, x, y] = args;
state.currentPointX = x;
state.currentPointY = y;
pathBuffer.push(DrawOPS.curveTo, startX, startY, x1, y1, x, y);
Util.bezierBoundingBox(
startX,
startY,
startX,
startY,
x1,
y1,
x,
y,
minMax
);
break;
}
case OPS.curveTo3: {
const startX = state.currentPointX;
const startY = state.currentPointY;
const [x1, y1, x, y] = args;
state.currentPointX = x;
state.currentPointY = y;
pathBuffer.push(DrawOPS.curveTo, x1, y1, x, y, x, y);
Util.bezierBoundingBox(startX, startY, x1, y1, x, y, x, y, minMax);
break;
}
case OPS.closePath:
pathBuffer.push(DrawOPS.closePath);
break;
} }
} }
@ -1764,6 +1731,7 @@ class PartialEvaluator {
const self = this; const self = this;
const xref = this.xref; const xref = this.xref;
let parsingText = false;
const localImageCache = new LocalImageCache(); const localImageCache = new LocalImageCache();
const localColorSpaceCache = new LocalColorSpaceCache(); const localColorSpaceCache = new LocalColorSpaceCache();
const localGStateCache = new LocalGStateCache(); const localGStateCache = new LocalGStateCache();
@ -1879,7 +1847,7 @@ class PartialEvaluator {
null, null,
operatorList, operatorList,
task, task,
stateManager.state.clone({ newPath: true }), stateManager.state.clone(),
localColorSpaceCache localColorSpaceCache
) )
.then(function () { .then(function () {
@ -1941,6 +1909,12 @@ class PartialEvaluator {
}) })
); );
return; return;
case OPS.beginText:
parsingText = true;
break;
case OPS.endText:
parsingText = false;
break;
case OPS.endInlineImage: case OPS.endInlineImage:
const cacheKey = args[0].cacheKey; const cacheKey = args[0].cacheKey;
if (cacheKey) { if (cacheKey) {
@ -2263,40 +2237,8 @@ class PartialEvaluator {
case OPS.curveTo3: case OPS.curveTo3:
case OPS.closePath: case OPS.closePath:
case OPS.rectangle: case OPS.rectangle:
self.buildPath(fn, args, stateManager.state); self.buildPath(operatorList, fn, args, parsingText);
continue; continue;
case OPS.stroke:
case OPS.closeStroke:
case OPS.fill:
case OPS.eoFill:
case OPS.fillStroke:
case OPS.eoFillStroke:
case OPS.closeFillStroke:
case OPS.closeEOFillStroke:
case OPS.endPath: {
const {
state: { pathBuffer, pathMinMax },
} = stateManager;
if (
fn === OPS.closeStroke ||
fn === OPS.closeFillStroke ||
fn === OPS.closeEOFillStroke
) {
pathBuffer.push(DrawOPS.closePath);
}
if (pathBuffer.length === 0) {
operatorList.addOp(OPS.constructPath, [fn, [null], null]);
} else {
operatorList.addOp(OPS.constructPath, [
fn,
[new Float32Array(pathBuffer)],
pathMinMax.slice(),
]);
pathBuffer.length = 0;
pathMinMax.set([Infinity, Infinity, -Infinity, -Infinity], 0);
}
continue;
}
case OPS.markPoint: case OPS.markPoint:
case OPS.markPointProps: case OPS.markPointProps:
case OPS.beginCompat: case OPS.beginCompat:
@ -4993,16 +4935,6 @@ class EvalState {
this._fillColorSpace = this._strokeColorSpace = ColorSpaceUtils.gray; this._fillColorSpace = this._strokeColorSpace = ColorSpaceUtils.gray;
this.patternFillColorSpace = null; this.patternFillColorSpace = null;
this.patternStrokeColorSpace = null; this.patternStrokeColorSpace = null;
// Path stuff.
this.currentPointX = this.currentPointY = 0;
this.pathMinMax = new Float32Array([
Infinity,
Infinity,
-Infinity,
-Infinity,
]);
this.pathBuffer = [];
} }
get fillColorSpace() { get fillColorSpace() {
@ -5021,18 +4953,8 @@ class EvalState {
this._strokeColorSpace = this.patternStrokeColorSpace = colorSpace; this._strokeColorSpace = this.patternStrokeColorSpace = colorSpace;
} }
clone({ newPath = false } = {}) { clone() {
const clone = Object.create(this); return Object.create(this);
if (newPath) {
clone.pathBuffer = [];
clone.pathMinMax = new Float32Array([
Infinity,
Infinity,
-Infinity,
-Infinity,
]);
}
return clone;
} }
} }

View File

@ -703,12 +703,6 @@ class OperatorList {
transfers.push(arg.data.buffer); transfers.push(arg.data.buffer);
} }
break; break;
case OPS.constructPath:
const [, [data], minMax] = argsArray[i];
if (data) {
transfers.push(data.buffer, minMax.buffer);
}
break;
} }
} }
return transfers; return transfers;

View File

@ -14,7 +14,6 @@
*/ */
import { import {
DrawOPS,
FeatureTest, FeatureTest,
FONT_IDENTITY_MATRIX, FONT_IDENTITY_MATRIX,
IDENTITY_MATRIX, IDENTITY_MATRIX,
@ -59,10 +58,6 @@ const MAX_SIZE_TO_COMPILE = 1000;
const FULL_CHUNK_HEIGHT = 16; const FULL_CHUNK_HEIGHT = 16;
// Only used in rescaleAndStroke. The goal is to avoid
// creating a new DOMMatrix object each time we need it.
const SCALE_MATRIX = new DOMMatrix();
/** /**
* Overrides certain methods on a 2d ctx so that when they are called they * Overrides certain methods on a 2d ctx so that when they are called they
* will also call the same method on the destCtx. The methods that are * will also call the same method on the destCtx. The methods that are
@ -507,6 +502,19 @@ class CanvasExtraState {
return clone; return clone;
} }
setCurrentPoint(x, y) {
this.x = x;
this.y = y;
}
updatePathMinMax(transform, x, y) {
[x, y] = Util.applyTransform([x, y], transform);
this.minX = Math.min(this.minX, x);
this.minY = Math.min(this.minY, y);
this.maxX = Math.max(this.maxX, x);
this.maxY = Math.max(this.maxY, y);
}
updateRectMinMax(transform, rect) { updateRectMinMax(transform, rect) {
const p1 = Util.applyTransform(rect, transform); const p1 = Util.applyTransform(rect, transform);
const p2 = Util.applyTransform(rect.slice(2), transform); const p2 = Util.applyTransform(rect.slice(2), transform);
@ -519,6 +527,22 @@ class CanvasExtraState {
this.maxY = Math.max(this.maxY, p1[1], p2[1], p3[1], p4[1]); this.maxY = Math.max(this.maxY, p1[1], p2[1], p3[1], p4[1]);
} }
updateScalingPathMinMax(transform, minMax) {
Util.scaleMinMax(transform, minMax);
this.minX = Math.min(this.minX, minMax[0]);
this.minY = Math.min(this.minY, minMax[1]);
this.maxX = Math.max(this.maxX, minMax[2]);
this.maxY = Math.max(this.maxY, minMax[3]);
}
updateCurvePathMinMax(transform, x0, y0, x1, y1, x2, y2, x3, y3, minMax) {
const box = Util.bezierBoundingBox(x0, y0, x1, y1, x2, y2, x3, y3, minMax);
if (minMax) {
return;
}
this.updateRectMinMax(transform, box);
}
getPathBoundingBox(pathType = PathType.FILL, transform = null) { getPathBoundingBox(pathType = PathType.FILL, transform = null) {
const box = [this.minX, this.minY, this.maxX, this.maxY]; const box = [this.minX, this.minY, this.maxX, this.maxY];
if (pathType === PathType.STROKE) { if (pathType === PathType.STROKE) {
@ -1588,54 +1612,156 @@ class CanvasGraphics {
} }
// Path // Path
constructPath(op, data, minMax) { constructPath(ops, args, minMax) {
let [path] = data; const ctx = this.ctx;
if (!minMax) { const current = this.current;
// The path is empty, so no need to update the current minMax. let x = current.x,
path ||= data[0] = new Path2D(); y = current.y;
this[op](path); let startX, startY;
return; const currentTransform = getCurrentTransform(ctx);
// Most of the time the current transform is a scaling matrix
// so we don't need to transform points before computing min/max:
// we can compute min/max first and then smartly "apply" the
// transform (see Util.scaleMinMax).
// For rectangle, moveTo and lineTo, min/max are computed in the
// worker (see evaluator.js).
const isScalingMatrix =
(currentTransform[0] === 0 && currentTransform[3] === 0) ||
(currentTransform[1] === 0 && currentTransform[2] === 0);
const minMaxForBezier = isScalingMatrix ? minMax.slice(0) : null;
for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {
switch (ops[i] | 0) {
case OPS.rectangle:
x = args[j++];
y = args[j++];
const width = args[j++];
const height = args[j++];
const xw = x + width;
const yh = y + height;
ctx.moveTo(x, y);
if (width === 0 || height === 0) {
ctx.lineTo(xw, yh);
} else {
ctx.lineTo(xw, y);
ctx.lineTo(xw, yh);
ctx.lineTo(x, yh);
} }
if (!(path instanceof Path2D)) { if (!isScalingMatrix) {
// Using a SVG string is slightly slower than using the following loop. current.updateRectMinMax(currentTransform, [x, y, xw, yh]);
const path2d = (data[0] = new Path2D()); }
for (let i = 0, ii = path.length; i < ii; ) { ctx.closePath();
switch (path[i++]) {
case DrawOPS.moveTo:
path2d.moveTo(path[i++], path[i++]);
break; break;
case DrawOPS.lineTo: case OPS.moveTo:
path2d.lineTo(path[i++], path[i++]); x = args[j++];
y = args[j++];
ctx.moveTo(x, y);
if (!isScalingMatrix) {
current.updatePathMinMax(currentTransform, x, y);
}
break; break;
case DrawOPS.curveTo: case OPS.lineTo:
path2d.bezierCurveTo( x = args[j++];
path[i++], y = args[j++];
path[i++], ctx.lineTo(x, y);
path[i++], if (!isScalingMatrix) {
path[i++], current.updatePathMinMax(currentTransform, x, y);
path[i++], }
path[i++] break;
case OPS.curveTo:
startX = x;
startY = y;
x = args[j + 4];
y = args[j + 5];
ctx.bezierCurveTo(
args[j],
args[j + 1],
args[j + 2],
args[j + 3],
x,
y
); );
current.updateCurvePathMinMax(
currentTransform,
startX,
startY,
args[j],
args[j + 1],
args[j + 2],
args[j + 3],
x,
y,
minMaxForBezier
);
j += 6;
break; break;
case DrawOPS.closePath: case OPS.curveTo2:
path2d.closePath(); startX = x;
startY = y;
ctx.bezierCurveTo(
x,
y,
args[j],
args[j + 1],
args[j + 2],
args[j + 3]
);
current.updateCurvePathMinMax(
currentTransform,
startX,
startY,
x,
y,
args[j],
args[j + 1],
args[j + 2],
args[j + 3],
minMaxForBezier
);
x = args[j + 2];
y = args[j + 3];
j += 4;
break; break;
default: case OPS.curveTo3:
warn(`Unrecognized drawing path operator: ${path[i - 1]}`); startX = x;
startY = y;
x = args[j + 2];
y = args[j + 3];
ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);
current.updateCurvePathMinMax(
currentTransform,
startX,
startY,
args[j],
args[j + 1],
x,
y,
x,
y,
minMaxForBezier
);
j += 4;
break;
case OPS.closePath:
ctx.closePath();
break; break;
} }
} }
path = path2d;
if (isScalingMatrix) {
current.updateScalingPathMinMax(currentTransform, minMaxForBezier);
} }
this.current.updateRectMinMax(getCurrentTransform(this.ctx), minMax);
this[op](path); current.setCurrentPoint(x, y);
} }
closePath() { closePath() {
this.ctx.closePath(); this.ctx.closePath();
} }
stroke(path, consumePath = true) { stroke(consumePath = true) {
const ctx = this.ctx; const ctx = this.ctx;
const strokeColor = this.current.strokeColor; const strokeColor = this.current.strokeColor;
// For stroke we want to temporarily change the global alpha to the // For stroke we want to temporarily change the global alpha to the
@ -1643,9 +1769,6 @@ class CanvasGraphics {
ctx.globalAlpha = this.current.strokeAlpha; ctx.globalAlpha = this.current.strokeAlpha;
if (this.contentVisible) { if (this.contentVisible) {
if (typeof strokeColor === "object" && strokeColor?.getPattern) { if (typeof strokeColor === "object" && strokeColor?.getPattern) {
const baseTransform = strokeColor.isModifyingCurrentTransform()
? ctx.getTransform()
: null;
ctx.save(); ctx.save();
ctx.strokeStyle = strokeColor.getPattern( ctx.strokeStyle = strokeColor.getPattern(
ctx, ctx,
@ -1653,41 +1776,31 @@ class CanvasGraphics {
getCurrentTransformInverse(ctx), getCurrentTransformInverse(ctx),
PathType.STROKE PathType.STROKE
); );
if (baseTransform) { this.rescaleAndStroke(/* saveRestore */ false);
const newPath = new Path2D();
newPath.addPath(
path,
ctx.getTransform().invertSelf().multiplySelf(baseTransform)
);
path = newPath;
}
this.rescaleAndStroke(path, /* saveRestore */ false);
ctx.restore(); ctx.restore();
} else { } else {
this.rescaleAndStroke(path, /* saveRestore */ true); this.rescaleAndStroke(/* saveRestore */ true);
} }
} }
if (consumePath) { if (consumePath) {
this.consumePath(path, this.current.getClippedPathBoundingBox()); this.consumePath(this.current.getClippedPathBoundingBox());
} }
// Restore the global alpha to the fill alpha // Restore the global alpha to the fill alpha
ctx.globalAlpha = this.current.fillAlpha; ctx.globalAlpha = this.current.fillAlpha;
} }
closeStroke(path) { closeStroke() {
this.stroke(path); this.closePath();
this.stroke();
} }
fill(path, consumePath = true) { fill(consumePath = true) {
const ctx = this.ctx; const ctx = this.ctx;
const fillColor = this.current.fillColor; const fillColor = this.current.fillColor;
const isPatternFill = this.current.patternFill; const isPatternFill = this.current.patternFill;
let needRestore = false; let needRestore = false;
if (isPatternFill) { if (isPatternFill) {
const baseTransform = fillColor.isModifyingCurrentTransform()
? ctx.getTransform()
: null;
ctx.save(); ctx.save();
ctx.fillStyle = fillColor.getPattern( ctx.fillStyle = fillColor.getPattern(
ctx, ctx,
@ -1695,24 +1808,16 @@ class CanvasGraphics {
getCurrentTransformInverse(ctx), getCurrentTransformInverse(ctx),
PathType.FILL PathType.FILL
); );
if (baseTransform) {
const newPath = new Path2D();
newPath.addPath(
path,
ctx.getTransform().invertSelf().multiplySelf(baseTransform)
);
path = newPath;
}
needRestore = true; needRestore = true;
} }
const intersect = this.current.getClippedPathBoundingBox(); const intersect = this.current.getClippedPathBoundingBox();
if (this.contentVisible && intersect !== null) { if (this.contentVisible && intersect !== null) {
if (this.pendingEOFill) { if (this.pendingEOFill) {
ctx.fill(path, "evenodd"); ctx.fill("evenodd");
this.pendingEOFill = false; this.pendingEOFill = false;
} else { } else {
ctx.fill(path); ctx.fill();
} }
} }
@ -1720,38 +1825,40 @@ class CanvasGraphics {
ctx.restore(); ctx.restore();
} }
if (consumePath) { if (consumePath) {
this.consumePath(path, intersect); this.consumePath(intersect);
} }
} }
eoFill(path) { eoFill() {
this.pendingEOFill = true; this.pendingEOFill = true;
this.fill(path); this.fill();
} }
fillStroke(path) { fillStroke() {
this.fill(path, false); this.fill(false);
this.stroke(path, false); this.stroke(false);
this.consumePath(path); this.consumePath();
} }
eoFillStroke(path) { eoFillStroke() {
this.pendingEOFill = true; this.pendingEOFill = true;
this.fillStroke(path); this.fillStroke();
} }
closeFillStroke(path) { closeFillStroke() {
this.fillStroke(path); this.closePath();
this.fillStroke();
} }
closeEOFillStroke(path) { closeEOFillStroke() {
this.pendingEOFill = true; this.pendingEOFill = true;
this.fillStroke(path); this.closePath();
this.fillStroke();
} }
endPath(path) { endPath() {
this.consumePath(path); this.consumePath();
} }
// Clipping // Clipping
@ -3061,7 +3168,7 @@ class CanvasGraphics {
// Helper functions // Helper functions
consumePath(path, clipBox) { consumePath(clipBox) {
const isEmpty = this.current.isEmptyClip(); const isEmpty = this.current.isEmptyClip();
if (this.pendingClip) { if (this.pendingClip) {
this.current.updateClipFromPath(); this.current.updateClipFromPath();
@ -3073,9 +3180,9 @@ class CanvasGraphics {
if (this.pendingClip) { if (this.pendingClip) {
if (!isEmpty) { if (!isEmpty) {
if (this.pendingClip === EO_CLIP) { if (this.pendingClip === EO_CLIP) {
ctx.clip(path, "evenodd"); ctx.clip("evenodd");
} else { } else {
ctx.clip(path); ctx.clip();
} }
} }
this.pendingClip = null; this.pendingClip = null;
@ -3160,16 +3267,15 @@ class CanvasGraphics {
// Rescale before stroking in order to have a final lineWidth // Rescale before stroking in order to have a final lineWidth
// with both thicknesses greater or equal to 1. // with both thicknesses greater or equal to 1.
rescaleAndStroke(path, saveRestore) { rescaleAndStroke(saveRestore) {
const { const { ctx } = this;
ctx, const { lineWidth } = this.current;
current: { lineWidth },
} = this;
const [scaleX, scaleY] = this.getScaleForStroking(); const [scaleX, scaleY] = this.getScaleForStroking();
if (scaleX === scaleY) { ctx.lineWidth = lineWidth || 1;
ctx.lineWidth = (lineWidth || 1) * scaleX;
ctx.stroke(path); if (scaleX === 1 && scaleY === 1) {
ctx.stroke();
return; return;
} }
@ -3179,10 +3285,6 @@ class CanvasGraphics {
} }
ctx.scale(scaleX, scaleY); ctx.scale(scaleX, scaleY);
SCALE_MATRIX.a = 1 / scaleX;
SCALE_MATRIX.d = 1 / scaleY;
const newPath = new Path2D();
newPath.addPath(path, SCALE_MATRIX);
// How the dashed line is rendered depends on the current transform... // How the dashed line is rendered depends on the current transform...
// so we added a rescale to handle too thin lines and consequently // so we added a rescale to handle too thin lines and consequently
@ -3197,8 +3299,7 @@ class CanvasGraphics {
ctx.lineDashOffset /= scale; ctx.lineDashOffset /= scale;
} }
ctx.lineWidth = lineWidth || 1; ctx.stroke();
ctx.stroke(newPath);
if (saveRestore) { if (saveRestore) {
ctx.restore(); ctx.restore();

View File

@ -43,10 +43,6 @@ class BaseShadingPattern {
} }
} }
isModifyingCurrentTransform() {
return false;
}
getPattern() { getPattern() {
unreachable("Abstract method `getPattern` called."); unreachable("Abstract method `getPattern` called.");
} }
@ -392,10 +388,6 @@ class MeshShadingPattern extends BaseShadingPattern {
}; };
} }
isModifyingCurrentTransform() {
return true;
}
getPattern(ctx, owner, inverse, pathType) { getPattern(ctx, owner, inverse, pathType) {
applyBoundingBox(ctx, this._bbox); applyBoundingBox(ctx, this._bbox);
let scale; let scale;
@ -712,10 +704,6 @@ class TilingPattern {
} }
} }
isModifyingCurrentTransform() {
return false;
}
getPattern(ctx, owner, inverse, pathType) { getPattern(ctx, owner, inverse, pathType) {
// PDF spec 8.7.2 NOTE 1: pattern's matrix is relative to initial matrix. // PDF spec 8.7.2 NOTE 1: pattern's matrix is relative to initial matrix.
let matrix = inverse; let matrix = inverse;

View File

@ -341,15 +341,6 @@ const OPS = {
setFillTransparent: 93, setFillTransparent: 93,
}; };
// In order to have a switch statement that is fast (i.e. which use a jump
// table), we need to have the OPS in a contiguous range.
const DrawOPS = {
moveTo: 0,
lineTo: 1,
curveTo: 2,
closePath: 3,
};
const PasswordResponses = { const PasswordResponses = {
NEED_PASSWORD: 1, NEED_PASSWORD: 1,
INCORRECT_PASSWORD: 2, INCORRECT_PASSWORD: 2,
@ -676,6 +667,57 @@ class Util {
return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`; return `#${hexNumbers[r]}${hexNumbers[g]}${hexNumbers[b]}`;
} }
// Apply a scaling matrix to some min/max values.
// If a scaling factor is negative then min and max must be
// swapped.
static scaleMinMax(transform, minMax) {
let temp;
if (transform[0]) {
if (transform[0] < 0) {
temp = minMax[0];
minMax[0] = minMax[2];
minMax[2] = temp;
}
minMax[0] *= transform[0];
minMax[2] *= transform[0];
if (transform[3] < 0) {
temp = minMax[1];
minMax[1] = minMax[3];
minMax[3] = temp;
}
minMax[1] *= transform[3];
minMax[3] *= transform[3];
} else {
temp = minMax[0];
minMax[0] = minMax[1];
minMax[1] = temp;
temp = minMax[2];
minMax[2] = minMax[3];
minMax[3] = temp;
if (transform[1] < 0) {
temp = minMax[1];
minMax[1] = minMax[3];
minMax[3] = temp;
}
minMax[1] *= transform[1];
minMax[3] *= transform[1];
if (transform[2] < 0) {
temp = minMax[0];
minMax[0] = minMax[2];
minMax[2] = temp;
}
minMax[0] *= transform[2];
minMax[2] *= transform[2];
}
minMax[0] += transform[4];
minMax[1] += transform[5];
minMax[2] += transform[4];
minMax[3] += transform[5];
}
// Concatenates two transformation matrices together and returns the result. // Concatenates two transformation matrices together and returns the result.
static transform(m1, m2) { static transform(m1, m2) {
return [ return [
@ -1181,7 +1223,6 @@ export {
bytesToString, bytesToString,
createValidAbsoluteUrl, createValidAbsoluteUrl,
DocumentActionEventType, DocumentActionEventType,
DrawOPS,
FeatureTest, FeatureTest,
FONT_IDENTITY_MATRIX, FONT_IDENTITY_MATRIX,
FormatError, FormatError,

View File

@ -74,11 +74,11 @@ describe("Interaction", () => {
describe("in 160F-2019.pdf", () => { describe("in 160F-2019.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("160F-2019.pdf", getSelector("416R")); pages = await loadAndWait("160F-2019.pdf", getSelector("416R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -150,12 +150,15 @@ describe("Interaction", () => {
await page.type(getSelector("416R"), "3.14159"); await page.type(getSelector("416R"), "3.14159");
await page.click(getSelector("419R")); await page.click(getSelector("419R"));
await page.waitForFunction(
`${getQuerySelector("416R")}.value === "3,14"` const valueFnStr = `${getQuerySelector("416R")}.value !== "3.14159"`;
); await page.waitForFunction(valueFnStr);
await page.waitForFunction(
`${getQuerySelector("427R")}.value === "3,14"` const text = await page.$eval(getSelector("416R"), el => el.value);
); expect(text).withContext(`In ${browserName}`).toEqual("3,14");
const sum = await page.$eval(getSelector("427R"), el => el.value);
expect(sum).withContext(`In ${browserName}`).toEqual("3,14");
}) })
); );
}); });
@ -167,23 +170,36 @@ describe("Interaction", () => {
await page.type(getSelector("448R"), "61803"); await page.type(getSelector("448R"), "61803");
await page.click(getSelector("419R")); await page.click(getSelector("419R"));
await page.waitForFunction(
`${getQuerySelector("448R")}.value === "61.803,00"` const valueOneFnStr = `${getQuerySelector("448R")}.value !== "61803"`;
); await page.waitForFunction(valueOneFnStr);
let text = await page.$eval(getSelector("448R"), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual("61.803,00");
await page.click(getSelector("448R")); await page.click(getSelector("448R"));
await page.waitForFunction(
`${getQuerySelector("448R")}.value === "61803"` const valueTwoFnStr = `${getQuerySelector(
); "448R"
)}.value !== "61.803,00"`;
await page.waitForFunction(valueTwoFnStr);
text = await page.$eval(getSelector("448R"), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual("61803");
// Clear the textfield // Clear the textfield
await clearInput(page, getSelector("448R")); await clearInput(page, getSelector("448R"));
await page.type(getSelector("448R"), "1.61803"); await page.type(getSelector("448R"), "1.61803");
await page.click(getSelector("419R")); await page.click(getSelector("419R"));
await page.waitForFunction(
`${getQuerySelector("448R")}.value === "1,62"` const valueThreeFnStr = `${getQuerySelector(
); "448R"
)}.value !== "1.61803"`;
await page.waitForFunction(valueThreeFnStr);
text = await page.$eval(getSelector("448R"), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual("1,62");
}) })
); );
}); });
@ -193,14 +209,24 @@ describe("Interaction", () => {
pages.map(async ([browserName, page]) => { pages.map(async ([browserName, page]) => {
await waitForScripting(page); await waitForScripting(page);
const prevSum = await page.$eval(getSelector("427R"), el => el.value);
await page.type(getSelector("422R"), "2.7182818"); await page.type(getSelector("422R"), "2.7182818");
await page.keyboard.press("Tab"); await page.keyboard.press("Tab");
await page.waitForFunction( await page.waitForFunction(
`${getQuerySelector("422R")}.value === "2,72"` `${getQuerySelector("422R")}.value !== "2.7182818"`
); );
const text = await page.$eval(getSelector("422R"), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual("2,72");
await page.waitForFunction( await page.waitForFunction(
`${getQuerySelector("427R")}.value === "2,72"` `${getQuerySelector("427R")}.value !== "${prevSum}"`
); );
const sum = await page.$eval(getSelector("427R"), el => el.value);
expect(sum).withContext(`In ${browserName}`).toEqual("5,86");
}) })
); );
}); });
@ -210,14 +236,21 @@ describe("Interaction", () => {
pages.map(async ([browserName, page]) => { pages.map(async ([browserName, page]) => {
await waitForScripting(page); await waitForScripting(page);
let sum = await page.$eval(getSelector("471R"), el => el.value);
expect(sum).withContext(`In ${browserName}`).toEqual("4,24");
await page.type(getSelector("436R"), "0.69314"); await page.type(getSelector("436R"), "0.69314");
await page.keyboard.press("Escape"); await page.keyboard.press("Escape");
const text = await page.$eval(getSelector("436R"), el => el.value);
expect(text).withContext(`In ${browserName}`).toEqual("0.69314");
await page.waitForFunction( await page.waitForFunction(
`${getQuerySelector("436R")}.value === "0.69314"` `${getQuerySelector("471R")}.value !== "${sum}"`
);
await page.waitForFunction(
`${getQuerySelector("471R")}.value === "0,69"`
); );
sum = await page.$eval(getSelector("471R"), el => el.value);
expect(sum).withContext(`In ${browserName}`).toEqual("3,55");
}) })
); );
}); });
@ -227,14 +260,19 @@ describe("Interaction", () => {
pages.map(async ([browserName, page]) => { pages.map(async ([browserName, page]) => {
await waitForScripting(page); await waitForScripting(page);
const prevSum = await page.$eval(getSelector("427R"), el => el.value);
await page.type(getSelector("419R"), "0.577215"); await page.type(getSelector("419R"), "0.577215");
await page.keyboard.press("Enter"); await page.keyboard.press("Enter");
const text = await page.$eval(getSelector("419R"), el => el.value);
expect(text).toEqual("0.577215");
await page.waitForFunction( await page.waitForFunction(
`${getQuerySelector("419R")}.value === "0.577215"` `${getQuerySelector("427R")}.value !== "${prevSum}"`
);
await page.waitForFunction(
`${getQuerySelector("427R")}.value === "0,58"`
); );
const sum = await page.$eval(getSelector("427R"), el => el.value);
expect(sum).toEqual("6,44");
}) })
); );
}); });
@ -291,11 +329,11 @@ describe("Interaction", () => {
describe("in js-buttons.pdf", () => { describe("in js-buttons.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("js-buttons.pdf", getSelector("80R")); pages = await loadAndWait("js-buttons.pdf", getSelector("80R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -418,7 +456,7 @@ describe("Interaction", () => {
describe("in doc_actions.pdf for printing", () => { describe("in doc_actions.pdf for printing", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("doc_actions.pdf", getSelector("47R"), null, { pages = await loadAndWait("doc_actions.pdf", getSelector("47R"), null, {
earlySetup: () => { earlySetup: () => {
// No need to trigger the print dialog. // No need to trigger the print dialog.
@ -459,11 +497,11 @@ describe("Interaction", () => {
describe("in doc_actions.pdf for saving", () => { describe("in doc_actions.pdf for saving", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("doc_actions.pdf", getSelector("47R")); pages = await loadAndWait("doc_actions.pdf", getSelector("47R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -506,11 +544,11 @@ describe("Interaction", () => {
describe("in doc_actions.pdf for page actions", () => { describe("in doc_actions.pdf for page actions", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("doc_actions.pdf", getSelector("47R")); pages = await loadAndWait("doc_actions.pdf", getSelector("47R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -563,11 +601,11 @@ describe("Interaction", () => {
describe("in js-authors.pdf", () => { describe("in js-authors.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("js-authors.pdf", getSelector("25R")); pages = await loadAndWait("js-authors.pdf", getSelector("25R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -594,11 +632,11 @@ describe("Interaction", () => {
describe("in listbox_actions.pdf", () => { describe("in listbox_actions.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("listbox_actions.pdf", getSelector("33R")); pages = await loadAndWait("listbox_actions.pdf", getSelector("33R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -736,11 +774,11 @@ describe("Interaction", () => {
describe("in js-colors.pdf", () => { describe("in js-colors.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("js-colors.pdf", getSelector("34R")); pages = await loadAndWait("js-colors.pdf", getSelector("34R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -809,11 +847,11 @@ describe("Interaction", () => {
describe("in issue13132.pdf", () => { describe("in issue13132.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue13132.pdf", getSelector("171R")); pages = await loadAndWait("issue13132.pdf", getSelector("171R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -868,11 +906,11 @@ describe("Interaction", () => {
describe("Check field properties", () => { describe("Check field properties", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("evaljs.pdf", getSelector("55R")); pages = await loadAndWait("evaljs.pdf", getSelector("55R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -943,11 +981,11 @@ describe("Interaction", () => {
describe("in issue13269.pdf", () => { describe("in issue13269.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue13269.pdf", getSelector("27R")); pages = await loadAndWait("issue13269.pdf", getSelector("27R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -978,14 +1016,13 @@ describe("Interaction", () => {
describe("in secHandler.pdf", () => { describe("in secHandler.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("secHandler.pdf", getSelector("25R")); pages = await loadAndWait("secHandler.pdf", getSelector("25R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
it("must print securityHandler value in a text field", async () => { it("must print securityHandler value in a text field", async () => {
await Promise.all( await Promise.all(
pages.map(async ([browserName, page]) => { pages.map(async ([browserName, page]) => {
@ -1007,7 +1044,7 @@ describe("Interaction", () => {
describe("in issue14307.pdf (1)", () => { describe("in issue14307.pdf (1)", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue14307.pdf", getSelector("30R")); pages = await loadAndWait("issue14307.pdf", getSelector("30R"));
pages.map(async ([, page]) => { pages.map(async ([, page]) => {
page.on("dialog", async dialog => { page.on("dialog", async dialog => {
@ -1016,7 +1053,7 @@ describe("Interaction", () => {
}); });
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1050,7 +1087,7 @@ describe("Interaction", () => {
describe("in issue14307.pdf (2)", () => { describe("in issue14307.pdf (2)", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue14307.pdf", getSelector("30R")); pages = await loadAndWait("issue14307.pdf", getSelector("30R"));
pages.map(async ([, page]) => { pages.map(async ([, page]) => {
page.on("dialog", async dialog => { page.on("dialog", async dialog => {
@ -1059,7 +1096,7 @@ describe("Interaction", () => {
}); });
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1095,7 +1132,7 @@ describe("Interaction", () => {
describe("in issue14307.pdf (3)", () => { describe("in issue14307.pdf (3)", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue14307.pdf", getSelector("30R")); pages = await loadAndWait("issue14307.pdf", getSelector("30R"));
pages.map(async ([, page]) => { pages.map(async ([, page]) => {
page.on("dialog", async dialog => { page.on("dialog", async dialog => {
@ -1104,7 +1141,7 @@ describe("Interaction", () => {
}); });
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1140,7 +1177,7 @@ describe("Interaction", () => {
describe("in issue14862.pdf", () => { describe("in issue14862.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue14862.pdf", getSelector("27R")); pages = await loadAndWait("issue14862.pdf", getSelector("27R"));
pages.map(async ([, page]) => { pages.map(async ([, page]) => {
page.on("dialog", async dialog => { page.on("dialog", async dialog => {
@ -1149,7 +1186,7 @@ describe("Interaction", () => {
}); });
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1235,7 +1272,7 @@ describe("Interaction", () => {
describe("in issue14705.pdf", () => { describe("in issue14705.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue14705.pdf", getSelector("29R")); pages = await loadAndWait("issue14705.pdf", getSelector("29R"));
pages.map(async ([, page]) => { pages.map(async ([, page]) => {
page.on("dialog", async dialog => { page.on("dialog", async dialog => {
@ -1244,7 +1281,7 @@ describe("Interaction", () => {
}); });
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1279,11 +1316,11 @@ describe("Interaction", () => {
describe("in bug1766987.pdf", () => { describe("in bug1766987.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1766987.pdf", getSelector("75R")); pages = await loadAndWait("bug1766987.pdf", getSelector("75R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1311,11 +1348,11 @@ describe("Interaction", () => {
describe("in issue15053.pdf", () => { describe("in issue15053.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue15053.pdf", getSelector("44R")); pages = await loadAndWait("issue15053.pdf", getSelector("44R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1364,11 +1401,11 @@ describe("Interaction", () => {
describe("in bug1675139.pdf", () => { describe("in bug1675139.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1675139.pdf", getSelector("48R")); pages = await loadAndWait("bug1675139.pdf", getSelector("48R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1410,11 +1447,11 @@ describe("Interaction", () => {
describe("in issue15092.pdf", () => { describe("in issue15092.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue15092.pdf", getSelector("39R")); pages = await loadAndWait("issue15092.pdf", getSelector("39R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1445,11 +1482,11 @@ describe("Interaction", () => {
describe("in bug1782564.pdf", () => { describe("in bug1782564.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1782564.pdf", getSelector("7R")); pages = await loadAndWait("bug1782564.pdf", getSelector("7R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1490,11 +1527,11 @@ describe("Interaction", () => {
describe("in bug1802888.pdf", () => { describe("in bug1802888.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1802888.pdf", getSelector("30R")); pages = await loadAndWait("bug1802888.pdf", getSelector("30R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1520,11 +1557,11 @@ describe("Interaction", () => {
describe("in issue15753.pdf", () => { describe("in issue15753.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue15753.pdf", getSelector("27R")); pages = await loadAndWait("issue15753.pdf", getSelector("27R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1565,11 +1602,11 @@ describe("Interaction", () => {
describe("in issue15815.pdf", () => { describe("in issue15815.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue15815.pdf", getSelector("24R")); pages = await loadAndWait("issue15815.pdf", getSelector("24R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1612,11 +1649,11 @@ describe("Interaction", () => {
describe("in issue15818.pdf", () => { describe("in issue15818.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue15818.pdf", getSelector("27R")); pages = await loadAndWait("issue15818.pdf", getSelector("27R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1659,7 +1696,7 @@ describe("Interaction", () => {
describe("in autoprint.pdf", () => { describe("in autoprint.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
// Autoprinting is triggered by the `Open` event, which is one of the // Autoprinting is triggered by the `Open` event, which is one of the
// first events to be dispatched to the sandbox, even before scripting // first events to be dispatched to the sandbox, even before scripting
// is reported to be ready. It's therefore important that `loadAndWait` // is reported to be ready. It's therefore important that `loadAndWait`
@ -1688,7 +1725,7 @@ describe("Interaction", () => {
}); });
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1710,11 +1747,11 @@ describe("Interaction", () => {
describe("in bug1811694.pdf", () => { describe("in bug1811694.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1811694.pdf", getSelector("25R")); pages = await loadAndWait("bug1811694.pdf", getSelector("25R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1742,11 +1779,11 @@ describe("Interaction", () => {
describe("in bug1811510.pdf", () => { describe("in bug1811510.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1811510.pdf", getSelector("22R")); pages = await loadAndWait("bug1811510.pdf", getSelector("22R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1782,11 +1819,11 @@ describe("Interaction", () => {
describe("in issue16067.pdf", () => { describe("in issue16067.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue16067.pdf", getSelector("6R")); pages = await loadAndWait("issue16067.pdf", getSelector("6R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1817,11 +1854,11 @@ describe("Interaction", () => {
describe("in bug1825002.pdf", () => { describe("in bug1825002.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1825002.pdf", getSelector("23R")); pages = await loadAndWait("bug1825002.pdf", getSelector("23R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1871,11 +1908,11 @@ describe("Interaction", () => {
describe("in bug1844576.pdf", () => { describe("in bug1844576.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1844576.pdf", getSelector("9R")); pages = await loadAndWait("bug1844576.pdf", getSelector("9R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1926,14 +1963,14 @@ describe("Interaction", () => {
describe("in annotation_hidden_noview.pdf", () => { describe("in annotation_hidden_noview.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait( pages = await loadAndWait(
"annotation_hidden_noview.pdf", "annotation_hidden_noview.pdf",
getSelector("11R") getSelector("11R")
); );
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -1985,11 +2022,11 @@ describe("Interaction", () => {
describe("in issue16863.pdf", () => { describe("in issue16863.pdf", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue16863.pdf", getSelector("334R")); pages = await loadAndWait("issue16863.pdf", getSelector("334R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2049,7 +2086,7 @@ describe("Interaction", () => {
let pages; let pages;
let otherPages; let otherPages;
beforeEach(async () => { beforeAll(async () => {
otherPages = await Promise.all( otherPages = await Promise.all(
global.integrationSessions.map(async session => global.integrationSessions.map(async session =>
session.browser.newPage() session.browser.newPage()
@ -2058,7 +2095,7 @@ describe("Interaction", () => {
pages = await loadAndWait("evaljs.pdf", getSelector("55R")); pages = await loadAndWait("evaljs.pdf", getSelector("55R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
await Promise.all(otherPages.map(page => page.close())); await Promise.all(otherPages.map(page => page.close()));
}); });
@ -2092,11 +2129,11 @@ describe("Interaction", () => {
describe("Textfield with a Blur callback", () => { describe("Textfield with a Blur callback", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1863910.pdf", getSelector("25R")); pages = await loadAndWait("bug1863910.pdf", getSelector("25R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2124,11 +2161,11 @@ describe("Interaction", () => {
describe("Radio button without T value", () => { describe("Radio button without T value", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1860602.pdf", getSelector("22R")); pages = await loadAndWait("bug1860602.pdf", getSelector("22R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2181,11 +2218,11 @@ describe("Interaction", () => {
describe("Textfield with a number and some decimals", () => { describe("Textfield with a number and some decimals", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue17540.pdf", getSelector("15R")); pages = await loadAndWait("issue17540.pdf", getSelector("15R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2214,11 +2251,11 @@ describe("Interaction", () => {
describe("Textfield with a zip code starting with 0", () => { describe("Textfield with a zip code starting with 0", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1889122.pdf", getSelector("24R")); pages = await loadAndWait("bug1889122.pdf", getSelector("24R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2242,11 +2279,11 @@ describe("Interaction", () => {
describe("Value of event.change when a choice list is modified", () => { describe("Value of event.change when a choice list is modified", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue17998.pdf", getSelector("7R")); pages = await loadAndWait("issue17998.pdf", getSelector("7R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2276,11 +2313,11 @@ describe("Interaction", () => {
describe("PageOpen and PageClose actions in fields", () => { describe("PageOpen and PageClose actions in fields", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue18305.pdf", getSelector("7R")); pages = await loadAndWait("issue18305.pdf", getSelector("7R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2321,11 +2358,11 @@ describe("Interaction", () => {
describe("Compute product of different fields", () => { describe("Compute product of different fields", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue18536.pdf", getSelector("34R")); pages = await loadAndWait("issue18536.pdf", getSelector("34R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2358,11 +2395,11 @@ describe("Interaction", () => {
describe("Calculate field value even if one callback throws", () => { describe("Calculate field value even if one callback throws", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue18561.pdf", getSelector("24R")); pages = await loadAndWait("issue18561.pdf", getSelector("24R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2386,11 +2423,11 @@ describe("Interaction", () => {
describe("Correctly format numbers", () => { describe("Correctly format numbers", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1918115.pdf", getSelector("33R")); pages = await loadAndWait("bug1918115.pdf", getSelector("33R"));
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2414,11 +2451,11 @@ describe("Interaction", () => {
describe("Change radio property", () => { describe("Change radio property", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1922766.pdf", "[data-annotation-id='44R']"); pages = await loadAndWait("bug1922766.pdf", "[data-annotation-id='44R']");
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2457,11 +2494,11 @@ describe("Interaction", () => {
describe("Date creation must be timezone consistent", () => { describe("Date creation must be timezone consistent", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("bug1934157.pdf", "[data-annotation-id='24R']"); pages = await loadAndWait("bug1934157.pdf", "[data-annotation-id='24R']");
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });
@ -2495,11 +2532,11 @@ describe("Interaction", () => {
describe("Skip throwing actions (issue 19505)", () => { describe("Skip throwing actions (issue 19505)", () => {
let pages; let pages;
beforeEach(async () => { beforeAll(async () => {
pages = await loadAndWait("issue19505.pdf", "[data-annotation-id='24R']"); pages = await loadAndWait("issue19505.pdf", "[data-annotation-id='24R']");
}); });
afterEach(async () => { afterAll(async () => {
await closePages(pages); await closePages(pages);
}); });

View File

@ -1 +0,0 @@
https://bugzilla.mozilla.org/attachment.cgi?id=9464841

View File

@ -12002,15 +12002,5 @@
"md5": "9993aa298c0214a3d3ff5f90ce0d40bb", "md5": "9993aa298c0214a3d3ff5f90ce0d40bb",
"rounds": 1, "rounds": 1,
"type": "eq" "type": "eq"
},
{
"id": "bug1946953",
"file": "pdfs/bug1946953.pdf",
"md5": "a71fb64e348b9c7161945e48e75c6681",
"rounds": 1,
"link": true,
"firstPage": 1,
"lastPage": 1,
"type": "eq"
} }
] ]

View File

@ -26,7 +26,6 @@ import {
AnnotationFieldFlag, AnnotationFieldFlag,
AnnotationFlag, AnnotationFlag,
AnnotationType, AnnotationType,
DrawOPS,
OPS, OPS,
RenderingIntentFlag, RenderingIntentFlag,
stringToBytes, stringToBytes,
@ -4286,13 +4285,14 @@ describe("annotation", function () {
null null
); );
expect(opList.fnArray.length).toEqual(15); expect(opList.fnArray.length).toEqual(16);
expect(opList.fnArray).toEqual([ expect(opList.fnArray).toEqual([
OPS.beginAnnotation, OPS.beginAnnotation,
OPS.save, OPS.save,
OPS.transform, OPS.transform,
OPS.clip,
OPS.constructPath, OPS.constructPath,
OPS.clip,
OPS.endPath,
OPS.beginText, OPS.beginText,
OPS.setFillRGBColor, OPS.setFillRGBColor,
OPS.setCharSpacing, OPS.setCharSpacing,
@ -4659,7 +4659,7 @@ describe("annotation", function () {
null null
); );
expect(opList.argsArray.length).toEqual(7); expect(opList.argsArray.length).toEqual(8);
expect(opList.fnArray).toEqual([ expect(opList.fnArray).toEqual([
OPS.beginAnnotation, OPS.beginAnnotation,
OPS.setLineWidth, OPS.setLineWidth,
@ -4667,6 +4667,7 @@ describe("annotation", function () {
OPS.setLineJoin, OPS.setLineJoin,
OPS.setStrokeRGBColor, OPS.setStrokeRGBColor,
OPS.constructPath, OPS.constructPath,
OPS.stroke,
OPS.endAnnotation, OPS.endAnnotation,
]); ]);
@ -4679,23 +4680,10 @@ describe("annotation", function () {
// Color. // Color.
expect(opList.argsArray[4]).toEqual(new Uint8ClampedArray([0, 255, 0])); expect(opList.argsArray[4]).toEqual(new Uint8ClampedArray([0, 255, 0]));
// Path. // Path.
expect(opList.argsArray[5][0]).toEqual(OPS.stroke); expect(opList.argsArray[5][0]).toEqual([OPS.moveTo, OPS.curveTo]);
expect(opList.argsArray[5][1]).toEqual([ expect(opList.argsArray[5][1]).toEqual([1, 2, 3, 4, 5, 6, 7, 8]);
new Float32Array([
DrawOPS.moveTo,
1,
2,
DrawOPS.curveTo,
3,
4,
5,
6,
7,
8,
]),
]);
// Min-max. // Min-max.
expect(opList.argsArray[5][2]).toEqual(new Float32Array([1, 2, 7, 8])); expect(opList.argsArray[5][2]).toEqual([1, 2, 1, 2]);
}); });
}); });
@ -4843,12 +4831,13 @@ describe("annotation", function () {
null null
); );
expect(opList.argsArray.length).toEqual(5); expect(opList.argsArray.length).toEqual(6);
expect(opList.fnArray).toEqual([ expect(opList.fnArray).toEqual([
OPS.beginAnnotation, OPS.beginAnnotation,
OPS.setFillRGBColor, OPS.setFillRGBColor,
OPS.setGState, OPS.setGState,
OPS.constructPath, OPS.constructPath,
OPS.eoFill,
OPS.endAnnotation, OPS.endAnnotation,
]); ]);
}); });
@ -4964,12 +4953,13 @@ describe("annotation", function () {
null null
); );
expect(opList.argsArray.length).toEqual(5); expect(opList.argsArray.length).toEqual(6);
expect(opList.fnArray).toEqual([ expect(opList.fnArray).toEqual([
OPS.beginAnnotation, OPS.beginAnnotation,
OPS.setFillRGBColor, OPS.setFillRGBColor,
OPS.setGState, OPS.setGState,
OPS.constructPath, OPS.constructPath,
OPS.fill,
OPS.endAnnotation, OPS.endAnnotation,
]); ]);
}); });

View File

@ -17,7 +17,6 @@ import {
AnnotationEditorType, AnnotationEditorType,
AnnotationMode, AnnotationMode,
AnnotationType, AnnotationType,
DrawOPS,
ImageKind, ImageKind,
InvalidPDFException, InvalidPDFException,
isNodeJS, isNodeJS,
@ -795,25 +794,17 @@ describe("api", function () {
OPS.setLineWidth, OPS.setLineWidth,
OPS.setStrokeRGBColor, OPS.setStrokeRGBColor,
OPS.constructPath, OPS.constructPath,
OPS.closeStroke,
]); ]);
expect(opList.argsArray).toEqual([ expect(opList.argsArray).toEqual([
[0.5], [0.5],
new Uint8ClampedArray([255, 0, 0]), new Uint8ClampedArray([255, 0, 0]),
[ [
OPS.closeStroke, [OPS.moveTo, OPS.lineTo],
[ [0, 9.75, 0.5, 9.75],
new Float32Array([ [0, 9.75, 0.5, 9.75],
DrawOPS.moveTo,
0,
9.75,
DrawOPS.lineTo,
0.5,
9.75,
DrawOPS.closePath,
]),
],
new Float32Array([0, 9.75, 0.5, 9.75]),
], ],
null,
]); ]);
expect(opList.lastChunk).toEqual(true); expect(opList.lastChunk).toEqual(true);
@ -4245,8 +4236,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`)
const opListAnnotEnable = await pdfPage.getOperatorList({ const opListAnnotEnable = await pdfPage.getOperatorList({
annotationMode: AnnotationMode.ENABLE, annotationMode: AnnotationMode.ENABLE,
}); });
expect(opListAnnotEnable.fnArray.length).toBeGreaterThan(130); expect(opListAnnotEnable.fnArray.length).toBeGreaterThan(140);
expect(opListAnnotEnable.argsArray.length).toBeGreaterThan(130); expect(opListAnnotEnable.argsArray.length).toBeGreaterThan(140);
expect(opListAnnotEnable.lastChunk).toEqual(true); expect(opListAnnotEnable.lastChunk).toEqual(true);
expect(opListAnnotEnable.separateAnnots).toEqual({ expect(opListAnnotEnable.separateAnnots).toEqual({
form: false, form: false,
@ -4279,8 +4270,8 @@ Caron Broadcasting, Inc., an Ohio corporation (“Lessee”).`)
const opListAnnotEnableStorage = await pdfPage.getOperatorList({ const opListAnnotEnableStorage = await pdfPage.getOperatorList({
annotationMode: AnnotationMode.ENABLE_STORAGE, annotationMode: AnnotationMode.ENABLE_STORAGE,
}); });
expect(opListAnnotEnableStorage.fnArray.length).toBeGreaterThan(150); expect(opListAnnotEnableStorage.fnArray.length).toBeGreaterThan(170);
expect(opListAnnotEnableStorage.argsArray.length).toBeGreaterThan(150); expect(opListAnnotEnableStorage.argsArray.length).toBeGreaterThan(170);
expect(opListAnnotEnableStorage.lastChunk).toEqual(true); expect(opListAnnotEnableStorage.lastChunk).toEqual(true);
expect(opListAnnotEnableStorage.separateAnnots).toEqual({ expect(opListAnnotEnableStorage.separateAnnots).toEqual({
form: false, form: false,

View File

@ -74,8 +74,8 @@ describe("evaluator", function () {
); );
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(1); expect(result.fnArray.length).toEqual(1);
expect(result.fnArray[0]).toEqual(OPS.constructPath); expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.argsArray[0]).toEqual([OPS.fill, [null], null]); expect(result.argsArray[0]).toEqual(null);
}); });
it("should handle one operation", async function () { it("should handle one operation", async function () {
@ -130,14 +130,9 @@ describe("evaluator", function () {
); );
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(3); expect(result.fnArray.length).toEqual(3);
expect(result.fnArray).toEqual([ expect(result.fnArray[0]).toEqual(OPS.fill);
OPS.constructPath, expect(result.fnArray[1]).toEqual(OPS.fill);
OPS.constructPath, expect(result.fnArray[2]).toEqual(OPS.fill);
OPS.constructPath,
]);
expect(result.argsArray[0][0]).toEqual(OPS.fill);
expect(result.argsArray[1][0]).toEqual(OPS.fill);
expect(result.argsArray[2][0]).toEqual(OPS.fill);
}); });
it("should handle three glued operations #2", async function () { it("should handle three glued operations #2", async function () {
@ -150,14 +145,10 @@ describe("evaluator", function () {
resources resources
); );
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray).toEqual([ expect(result.fnArray.length).toEqual(3);
OPS.constructPath, expect(result.fnArray[0]).toEqual(OPS.eoFillStroke);
OPS.constructPath, expect(result.fnArray[1]).toEqual(OPS.fillStroke);
OPS.constructPath, expect(result.fnArray[2]).toEqual(OPS.eoFill);
]);
expect(result.argsArray[0][0]).toEqual(OPS.eoFillStroke);
expect(result.argsArray[1][0]).toEqual(OPS.fillStroke);
expect(result.argsArray[2][0]).toEqual(OPS.eoFill);
}); });
it("should handle glued operations and operands", async function () { it("should handle glued operations and operands", async function () {
@ -169,7 +160,7 @@ describe("evaluator", function () {
); );
expect(!!result.fnArray && !!result.argsArray).toEqual(true); expect(!!result.fnArray && !!result.argsArray).toEqual(true);
expect(result.fnArray.length).toEqual(2); expect(result.fnArray.length).toEqual(2);
expect(result.fnArray[0]).toEqual(OPS.constructPath); expect(result.fnArray[0]).toEqual(OPS.fill);
expect(result.fnArray[1]).toEqual(OPS.setTextRise); expect(result.fnArray[1]).toEqual(OPS.setTextRise);
expect(result.argsArray.length).toEqual(2); expect(result.argsArray.length).toEqual(2);
expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray[1].length).toEqual(1);
@ -187,13 +178,13 @@ describe("evaluator", function () {
expect(result.fnArray.length).toEqual(3); expect(result.fnArray.length).toEqual(3);
expect(result.fnArray[0]).toEqual(OPS.setFlatness); expect(result.fnArray[0]).toEqual(OPS.setFlatness);
expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent); expect(result.fnArray[1]).toEqual(OPS.setRenderingIntent);
expect(result.fnArray[2]).toEqual(OPS.constructPath); expect(result.fnArray[2]).toEqual(OPS.endPath);
expect(result.argsArray.length).toEqual(3); expect(result.argsArray.length).toEqual(3);
expect(result.argsArray[0].length).toEqual(1); expect(result.argsArray[0].length).toEqual(1);
expect(result.argsArray[0][0]).toEqual(true); expect(result.argsArray[0][0]).toEqual(true);
expect(result.argsArray[1].length).toEqual(1); expect(result.argsArray[1].length).toEqual(1);
expect(result.argsArray[1][0]).toEqual(false); expect(result.argsArray[1][0]).toEqual(false);
expect(result.argsArray[2]).toEqual([OPS.endPath, [null], null]); expect(result.argsArray[2]).toEqual(null);
}); });
}); });