Compare commits

...

9 Commits

Author SHA1 Message Date
calixteman
6cc37c8415
Merge pull request #19772 from calixteman/settextmatrix
Avoid to create an array when setting the text matrix
2025-04-05 21:59:30 +02:00
Calixte Denizet
4c63905a18 Avoid to create an array when setting the text matrix 2025-04-05 20:45:26 +02:00
Jonas Jenwald
9217d253aa
Merge pull request #19771 from Snuffleupagus/canvas-shorter-CanvasExtraState
Change how (most) fields are initialized in the `CanvasExtraState` class
2025-04-05 18:46:24 +02:00
Jonas Jenwald
7cfb1be650
Merge pull request #19758 from Snuffleupagus/OperatorList-setOptions
Initialize the `isOffscreenCanvasSupported` option, in the `OperatorList` class, once per document
2025-04-05 18:45:55 +02:00
Jonas Jenwald
a70f42339e
Merge pull request #19756 from Snuffleupagus/canvas-unconditional-ctx-filter
Use `ctx.filter` unconditionally in the `src/display/canvas.js` file
2025-04-05 18:45:19 +02:00
Jonas Jenwald
010b6ad886 Change how (most) fields are initialized in the CanvasExtraState class
The majority of the class fields don't depend on any parameters, hence we can re-factor and shorten by this using modern JavaScript features.
2025-04-05 15:03:06 +02:00
Jonas Jenwald
624d8a418e Remove "unnecessary" inline function names in the src/display/canvas.js file
This is ever so slightly shorter, which cannot hurt.
2025-04-05 14:59:04 +02:00
Jonas Jenwald
4a6c47489e Initialize the isOffscreenCanvasSupported option, in the OperatorList class, once per document
Currently we're setting this option for each small inline image, which seems unnecessary since it should suffice to do that once per document.
2025-04-03 14:00:07 +02:00
Jonas Jenwald
0c78b46184 Use ctx.filter unconditionally in the src/display/canvas.js file
It seems that the `@napi-rs/canvas` dependency has *basic* canvas-filter support, whereas the "old" `canvas` dependency didn't, hence we no longer need the Node.js-specific checks in the `src/display/canvas.js` file.

Note that I've successfully tested the [`pdf2png` example](https://github.com/mozilla/pdf.js/tree/master/examples/node/pdf2png) with this patch applied and things appear to work as before.
2025-04-03 12:30:29 +02:00
4 changed files with 101 additions and 82 deletions

View File

@ -728,8 +728,6 @@ class PartialEvaluator {
/* forceRGBA = */ true,
/* isOffscreenCanvasSupported = */ false
);
operatorList.isOffscreenCanvasSupported =
this.options.isOffscreenCanvasSupported;
operatorList.addImageOps(
OPS.paintInlineImageXObject,
[imgData],
@ -2239,6 +2237,9 @@ class PartialEvaluator {
}
continue;
}
case OPS.setTextMatrix:
operatorList.addOp(fn, [new Float32Array(args)]);
continue;
case OPS.markPoint:
case OPS.markPointProps:
case OPS.beginCompat:

View File

@ -567,17 +567,12 @@ class QueueOptimizer extends NullOptimizer {
iCurr: 0,
fnArray: queue.fnArray,
argsArray: queue.argsArray,
isOffscreenCanvasSupported: false,
isOffscreenCanvasSupported: OperatorList.isOffscreenCanvasSupported,
};
this.match = null;
this.lastProcessed = 0;
}
// eslint-disable-next-line accessor-pairs
set isOffscreenCanvasSupported(value) {
this.context.isOffscreenCanvasSupported = value;
}
_optimize() {
// Process new fnArray item(s) chunk.
const fnArray = this.queue.fnArray;
@ -656,6 +651,8 @@ class OperatorList {
// Close to chunk size.
static CHUNK_SIZE_ABOUT = this.CHUNK_SIZE - 5;
static isOffscreenCanvasSupported = false;
constructor(intent = 0, streamSink) {
this._streamSink = streamSink;
this.fnArray = [];
@ -670,9 +667,8 @@ class OperatorList {
this._resolved = streamSink ? null : Promise.resolve();
}
// eslint-disable-next-line accessor-pairs
set isOffscreenCanvasSupported(value) {
this.optimizer.isOffscreenCanvasSupported = value;
static setOptions({ isOffscreenCanvasSupported }) {
this.isOffscreenCanvasSupported = isOffscreenCanvasSupported;
}
get length() {
@ -791,6 +787,9 @@ class OperatorList {
transfers.push(bbox.buffer);
}
break;
case OPS.setTextMatrix:
transfers.push(argsArray[i][0].buffer);
break;
}
}
return transfers;

View File

@ -25,6 +25,7 @@ import { ImageResizer } from "./image_resizer.js";
import { JpegStream } from "./jpeg_stream.js";
import { JpxImage } from "./jpx.js";
import { MissingDataException } from "./core_utils.js";
import { OperatorList } from "./operator_list.js";
import { PDFDocument } from "./document.js";
import { Stream } from "./stream.js";
@ -74,6 +75,7 @@ class BasePdfManager {
// Initialize image-options once per document.
ImageResizer.setOptions(evaluatorOptions);
JpegStream.setOptions(evaluatorOptions);
OperatorList.setOptions(evaluatorOptions);
const options = { ...evaluatorOptions, handler };
JpxImage.setOptions(options);

View File

@ -17,10 +17,8 @@ import {
DrawOPS,
FeatureTest,
FONT_IDENTITY_MATRIX,
IDENTITY_MATRIX,
ImageKind,
info,
isNodeJS,
OPS,
shadow,
TextRenderingMode,
@ -125,47 +123,47 @@ function mirrorContextOperations(ctx, destCtx) {
delete ctx._removeMirroring;
};
ctx.save = function ctxSave() {
ctx.save = function () {
destCtx.save();
this.__originalSave();
};
ctx.restore = function ctxRestore() {
ctx.restore = function () {
destCtx.restore();
this.__originalRestore();
};
ctx.translate = function ctxTranslate(x, y) {
ctx.translate = function (x, y) {
destCtx.translate(x, y);
this.__originalTranslate(x, y);
};
ctx.scale = function ctxScale(x, y) {
ctx.scale = function (x, y) {
destCtx.scale(x, y);
this.__originalScale(x, y);
};
ctx.transform = function ctxTransform(a, b, c, d, e, f) {
ctx.transform = function (a, b, c, d, e, f) {
destCtx.transform(a, b, c, d, e, f);
this.__originalTransform(a, b, c, d, e, f);
};
ctx.setTransform = function ctxSetTransform(a, b, c, d, e, f) {
ctx.setTransform = function (a, b, c, d, e, f) {
destCtx.setTransform(a, b, c, d, e, f);
this.__originalSetTransform(a, b, c, d, e, f);
};
ctx.resetTransform = function ctxResetTransform() {
ctx.resetTransform = function () {
destCtx.resetTransform();
this.__originalResetTransform();
};
ctx.rotate = function ctxRotate(angle) {
ctx.rotate = function (angle) {
destCtx.rotate(angle);
this.__originalRotate(angle);
};
ctx.clip = function ctxRotate(rule) {
ctx.clip = function (rule) {
destCtx.clip(rule);
this.__originalClip(rule);
};
@ -305,39 +303,63 @@ function drawImageAtIntegerCoords(
}
class CanvasExtraState {
constructor(width, height) {
// Are soft masks and alpha values shapes or opacities?
this.alphaIsShape = false;
this.fontSize = 0;
this.fontSizeScale = 1;
this.textMatrix = IDENTITY_MATRIX;
this.textMatrixScale = 1;
this.fontMatrix = FONT_IDENTITY_MATRIX;
this.leading = 0;
// Current point (in user coordinates)
this.x = 0;
this.y = 0;
// Start of text line (in text coordinates)
this.lineX = 0;
this.lineY = 0;
// Character and word spacing
this.charSpacing = 0;
this.wordSpacing = 0;
this.textHScale = 1;
this.textRenderingMode = TextRenderingMode.FILL;
this.textRise = 0;
// Default fore and background colors
this.fillColor = "#000000";
this.strokeColor = "#000000";
this.patternFill = false;
this.patternStroke = false;
// Note: fill alpha applies to all non-stroking operations
this.fillAlpha = 1;
this.strokeAlpha = 1;
this.lineWidth = 1;
this.activeSMask = null;
this.transferMaps = "none";
// Are soft masks and alpha values shapes or opacities?
alphaIsShape = false;
fontSize = 0;
fontSizeScale = 1;
textMatrix = null;
textMatrixScale = 1;
fontMatrix = FONT_IDENTITY_MATRIX;
leading = 0;
// Current point (in user coordinates)
x = 0;
y = 0;
// Start of text line (in text coordinates)
lineX = 0;
lineY = 0;
// Character and word spacing
charSpacing = 0;
wordSpacing = 0;
textHScale = 1;
textRenderingMode = TextRenderingMode.FILL;
textRise = 0;
// Default fore and background colors
fillColor = "#000000";
strokeColor = "#000000";
patternFill = false;
patternStroke = false;
// Note: fill alpha applies to all non-stroking operations
fillAlpha = 1;
strokeAlpha = 1;
lineWidth = 1;
activeSMask = null;
transferMaps = "none";
constructor(width, height) {
this.clipBox = new Float32Array([0, 0, width, height]);
this.minMax = MIN_MAX_INIT.slice();
}
@ -589,14 +611,9 @@ function resetCtxToDefault(ctx) {
ctx.setLineDash([]);
ctx.lineDashOffset = 0;
}
if (
(typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) ||
!isNodeJS
) {
const { filter } = ctx;
if (filter !== "none" && filter !== "") {
ctx.filter = "none";
}
const { filter } = ctx;
if (filter !== "none" && filter !== "") {
ctx.filter = "none";
}
}
@ -1593,7 +1610,7 @@ class CanvasGraphics {
// Text
beginText() {
this.current.textMatrix = IDENTITY_MATRIX;
this.current.textMatrix = null;
this.current.textMatrixScale = 1;
this.current.x = this.current.lineX = 0;
this.current.y = this.current.lineY = 0;
@ -1714,12 +1731,13 @@ class CanvasGraphics {
this.moveText(x, y);
}
setTextMatrix(a, b, c, d, e, f) {
this.current.textMatrix = [a, b, c, d, e, f];
this.current.textMatrixScale = Math.hypot(a, b);
setTextMatrix(matrix) {
const { current } = this;
current.textMatrix = matrix;
current.textMatrixScale = Math.hypot(matrix[0], matrix[1]);
this.current.x = this.current.lineX = 0;
this.current.y = this.current.lineY = 0;
current.x = current.lineX = 0;
current.y = current.lineY = 0;
}
nextLine() {
@ -1886,7 +1904,9 @@ class CanvasGraphics {
!current.patternFill;
ctx.save();
ctx.transform(...current.textMatrix);
if (current.textMatrix) {
ctx.transform(...current.textMatrix);
}
ctx.translate(current.x, current.y + current.textRise);
if (fontDirection > 0) {
@ -2081,7 +2101,9 @@ class CanvasGraphics {
this._cachedGetSinglePixelWidth = null;
ctx.save();
ctx.transform(...current.textMatrix);
if (current.textMatrix) {
ctx.transform(...current.textMatrix);
}
ctx.translate(current.x, current.y + current.textRise);
ctx.scale(textHScale, fontDirection);
@ -2734,18 +2756,13 @@ class CanvasGraphics {
this.save();
if (
(typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) ||
!isNodeJS
) {
// The filter, if any, will be applied in applyTransferMapsToBitmap.
// It must be applied to the image before rescaling else some artifacts
// could appear.
// The final restore will reset it to its value.
const { filter } = ctx;
if (filter !== "none" && filter !== "") {
ctx.filter = "none";
}
// The filter, if any, will be applied in applyTransferMapsToBitmap.
// It must be applied to the image before rescaling else some artifacts
// could appear.
// The final restore will reset it to its value.
const { filter } = ctx;
if (filter !== "none" && filter !== "") {
ctx.filter = "none";
}
// scale the image to the unit square