Store ops bboxes in a linear Uint8Array
This PR changes the way we store bounding boxes so that they use less memory and can be more easily shared across threads in the future. Instead of storing the bounding box and list of dependencies for each operation that renders _something_, we now only store the bounding box of _every_ operation and no dependencies list. The bounding box of each operation covers the bounding box of all the operations affected by it that render something. For example, the bounding box of a `setFont` operation will be the bounding box of all the `showText` operations that use that font. This affects the debugging experience in pdfBug, since now the bounding box of an operation may be larger than what it renders itself. To help with this, now when hovering on an operation we also highlight (in red) all its dependents. We highlight with white stripes operations that do not affect any part of the page (i.e. with an empty bbox). To save memory, we now save bounding box x/y coordinates as uint8 rather than float64. This effectively gives us a 256x256 uniform grid that covers the page, which is high enough resolution for the usecase.
This commit is contained in:
parent
e37a58f978
commit
e4ea2e0c79
@ -1242,8 +1242,8 @@ class PDFDocumentProxy {
|
|||||||
* @property {boolean} [isEditing] - Render the page in editing mode.
|
* @property {boolean} [isEditing] - Render the page in editing mode.
|
||||||
* @property {boolean} [recordOperations] - Record the dependencies and bounding
|
* @property {boolean} [recordOperations] - Record the dependencies and bounding
|
||||||
* boxes of all PDF operations that render onto the canvas.
|
* boxes of all PDF operations that render onto the canvas.
|
||||||
* @property {Set<number>} [filteredOperationIndexes] - If provided, only run
|
* @property {(index: number) => boolean} [operationsFilter] - If provided, only
|
||||||
* the PDF operations that are included in this set.
|
* run for which this function returns `true`.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1314,7 +1314,7 @@ class PDFPageProxy {
|
|||||||
|
|
||||||
this._intentStates = new Map();
|
this._intentStates = new Map();
|
||||||
this.destroyed = false;
|
this.destroyed = false;
|
||||||
this.recordedGroups = null;
|
this.recordedBBoxes = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1440,7 +1440,7 @@ class PDFPageProxy {
|
|||||||
printAnnotationStorage = null,
|
printAnnotationStorage = null,
|
||||||
isEditing = false,
|
isEditing = false,
|
||||||
recordOperations = false,
|
recordOperations = false,
|
||||||
filteredOperationIndexes = null,
|
operationsFilter = null,
|
||||||
}) {
|
}) {
|
||||||
this._stats?.time("Overall");
|
this._stats?.time("Overall");
|
||||||
|
|
||||||
@ -1487,23 +1487,28 @@ class PDFPageProxy {
|
|||||||
this._pumpOperatorList(intentArgs);
|
this._pumpOperatorList(intentArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const recordForDebugger = Boolean(
|
||||||
|
this._pdfBug && globalThis.StepperManager?.enabled
|
||||||
|
);
|
||||||
|
|
||||||
const shouldRecordOperations =
|
const shouldRecordOperations =
|
||||||
!this.recordedGroups &&
|
!this.recordedBBoxes && (recordOperations || recordForDebugger);
|
||||||
(recordOperations ||
|
|
||||||
(this._pdfBug && globalThis.StepperManager?.enabled));
|
|
||||||
|
|
||||||
const complete = error => {
|
const complete = error => {
|
||||||
intentState.renderTasks.delete(internalRenderTask);
|
intentState.renderTasks.delete(internalRenderTask);
|
||||||
|
|
||||||
if (shouldRecordOperations) {
|
if (shouldRecordOperations) {
|
||||||
const recordedGroups = internalRenderTask.gfx?.dependencyTracker.take();
|
const recordedBBoxes = internalRenderTask.gfx?.dependencyTracker.take();
|
||||||
if (recordedGroups) {
|
if (recordedBBoxes) {
|
||||||
internalRenderTask.stepper?.setOperatorGroups(recordedGroups);
|
if (internalRenderTask.stepper) {
|
||||||
if (recordOperations) {
|
internalRenderTask.stepper.setOperatorBBoxes(
|
||||||
this.recordedGroups = recordedGroups;
|
recordedBBoxes,
|
||||||
|
internalRenderTask.gfx.dependencyTracker.takeDebugMetadata()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (recordOperations) {
|
||||||
|
this.recordedBBoxes = recordedBBoxes;
|
||||||
}
|
}
|
||||||
} else if (recordOperations) {
|
|
||||||
this.recordedGroups = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1542,7 +1547,11 @@ class PDFPageProxy {
|
|||||||
canvas,
|
canvas,
|
||||||
canvasContext,
|
canvasContext,
|
||||||
dependencyTracker: shouldRecordOperations
|
dependencyTracker: shouldRecordOperations
|
||||||
? new CanvasDependencyTracker(canvas)
|
? new CanvasDependencyTracker(
|
||||||
|
canvas,
|
||||||
|
intentState.operatorList.length,
|
||||||
|
recordForDebugger
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
viewport,
|
viewport,
|
||||||
transform,
|
transform,
|
||||||
@ -1559,7 +1568,7 @@ class PDFPageProxy {
|
|||||||
pdfBug: this._pdfBug,
|
pdfBug: this._pdfBug,
|
||||||
pageColors,
|
pageColors,
|
||||||
enableHWA: this._transport.enableHWA,
|
enableHWA: this._transport.enableHWA,
|
||||||
filteredOperationIndexes,
|
operationsFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
(intentState.renderTasks ||= new Set()).add(internalRenderTask);
|
(intentState.renderTasks ||= new Set()).add(internalRenderTask);
|
||||||
@ -3169,7 +3178,7 @@ class InternalRenderTask {
|
|||||||
pdfBug = false,
|
pdfBug = false,
|
||||||
pageColors = null,
|
pageColors = null,
|
||||||
enableHWA = false,
|
enableHWA = false,
|
||||||
filteredOperationIndexes = null,
|
operationsFilter = null,
|
||||||
}) {
|
}) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
@ -3201,7 +3210,7 @@ class InternalRenderTask {
|
|||||||
this._canvasContext = params.canvas ? null : params.canvasContext;
|
this._canvasContext = params.canvas ? null : params.canvasContext;
|
||||||
this._enableHWA = enableHWA;
|
this._enableHWA = enableHWA;
|
||||||
this._dependencyTracker = params.dependencyTracker;
|
this._dependencyTracker = params.dependencyTracker;
|
||||||
this._filteredOperationIndexes = filteredOperationIndexes;
|
this._operationsFilter = operationsFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
get completed() {
|
get completed() {
|
||||||
@ -3288,6 +3297,9 @@ class InternalRenderTask {
|
|||||||
this.graphicsReadyCallback ||= this._continueBound;
|
this.graphicsReadyCallback ||= this._continueBound;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.gfx.dependencyTracker?.growOperationsCount(
|
||||||
|
this.operatorList.fnArray.length
|
||||||
|
);
|
||||||
this.stepper?.updateOperatorList(this.operatorList);
|
this.stepper?.updateOperatorList(this.operatorList);
|
||||||
|
|
||||||
if (this.running) {
|
if (this.running) {
|
||||||
@ -3328,7 +3340,7 @@ class InternalRenderTask {
|
|||||||
this.operatorListIdx,
|
this.operatorListIdx,
|
||||||
this._continueBound,
|
this._continueBound,
|
||||||
this.stepper,
|
this.stepper,
|
||||||
this._filteredOperationIndexes
|
this._operationsFilter
|
||||||
);
|
);
|
||||||
if (this.operatorListIdx === this.operatorList.argsArray.length) {
|
if (this.operatorListIdx === this.operatorList.argsArray.length) {
|
||||||
this.running = false;
|
this.running = false;
|
||||||
|
|||||||
@ -763,7 +763,7 @@ class CanvasGraphics {
|
|||||||
executionStartIdx,
|
executionStartIdx,
|
||||||
continueCallback,
|
continueCallback,
|
||||||
stepper,
|
stepper,
|
||||||
filteredOperationIndexes
|
operationsFilter
|
||||||
) {
|
) {
|
||||||
const argsArray = operatorList.argsArray;
|
const argsArray = operatorList.argsArray;
|
||||||
const fnArray = operatorList.fnArray;
|
const fnArray = operatorList.fnArray;
|
||||||
@ -791,7 +791,7 @@ class CanvasGraphics {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filteredOperationIndexes || filteredOperationIndexes.has(i)) {
|
if (!operationsFilter || operationsFilter(i)) {
|
||||||
fnId = fnArray[i];
|
fnId = fnArray[i];
|
||||||
// TODO: There is a `undefined` coming from somewhere.
|
// TODO: There is a `undefined` coming from somewhere.
|
||||||
fnArgs = argsArray[i] ?? null;
|
fnArgs = argsArray[i] ?? null;
|
||||||
@ -1100,7 +1100,7 @@ class CanvasGraphics {
|
|||||||
-offsetY,
|
-offsetY,
|
||||||
]);
|
]);
|
||||||
fillCtx.fillStyle = isPatternFill
|
fillCtx.fillStyle = isPatternFill
|
||||||
? fillColor.getPattern(ctx, this, inverse, PathType.FILL)
|
? fillColor.getPattern(ctx, this, inverse, PathType.FILL, opIdx)
|
||||||
: fillColor;
|
: fillColor;
|
||||||
|
|
||||||
fillCtx.fillRect(0, 0, width, height);
|
fillCtx.fillRect(0, 0, width, height);
|
||||||
@ -1549,7 +1549,8 @@ class CanvasGraphics {
|
|||||||
ctx,
|
ctx,
|
||||||
this,
|
this,
|
||||||
getCurrentTransformInverse(ctx),
|
getCurrentTransformInverse(ctx),
|
||||||
PathType.STROKE
|
PathType.STROKE,
|
||||||
|
opIdx
|
||||||
);
|
);
|
||||||
if (baseTransform) {
|
if (baseTransform) {
|
||||||
const newPath = new Path2D();
|
const newPath = new Path2D();
|
||||||
@ -1603,7 +1604,8 @@ class CanvasGraphics {
|
|||||||
ctx,
|
ctx,
|
||||||
this,
|
this,
|
||||||
getCurrentTransformInverse(ctx),
|
getCurrentTransformInverse(ctx),
|
||||||
PathType.FILL
|
PathType.FILL,
|
||||||
|
opIdx
|
||||||
);
|
);
|
||||||
if (baseTransform) {
|
if (baseTransform) {
|
||||||
const newPath = new Path2D();
|
const newPath = new Path2D();
|
||||||
@ -1759,7 +1761,7 @@ class CanvasGraphics {
|
|||||||
setFont(opIdx, fontRefName, size) {
|
setFont(opIdx, fontRefName, size) {
|
||||||
this.dependencyTracker
|
this.dependencyTracker
|
||||||
?.recordSimpleData("font", opIdx)
|
?.recordSimpleData("font", opIdx)
|
||||||
.recordNamedDependency(opIdx, fontRefName);
|
.recordSimpleDataFromNamed("fontObj", fontRefName, opIdx);
|
||||||
const fontObj = this.commonObjs.get(fontRefName);
|
const fontObj = this.commonObjs.get(fontRefName);
|
||||||
const current = this.current;
|
const current = this.current;
|
||||||
|
|
||||||
@ -2034,7 +2036,6 @@ class CanvasGraphics {
|
|||||||
if (this.dependencyTracker) {
|
if (this.dependencyTracker) {
|
||||||
this.dependencyTracker
|
this.dependencyTracker
|
||||||
.recordDependencies(opIdx, Dependencies.showText)
|
.recordDependencies(opIdx, Dependencies.showText)
|
||||||
.copyDependenciesFromIncrementalOperation(opIdx, "sameLineText")
|
|
||||||
.resetBBox(opIdx);
|
.resetBBox(opIdx);
|
||||||
if (this.current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) {
|
if (this.current.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG) {
|
||||||
this.dependencyTracker
|
this.dependencyTracker
|
||||||
@ -2047,9 +2048,7 @@ class CanvasGraphics {
|
|||||||
const font = current.font;
|
const font = current.font;
|
||||||
if (font.isType3Font) {
|
if (font.isType3Font) {
|
||||||
this.showType3Text(opIdx, glyphs);
|
this.showType3Text(opIdx, glyphs);
|
||||||
this.dependencyTracker
|
this.dependencyTracker?.recordShowTextOperation(opIdx);
|
||||||
?.recordOperation(opIdx)
|
|
||||||
.recordIncrementalData("sameLineText", opIdx);
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2095,7 +2094,8 @@ class CanvasGraphics {
|
|||||||
ctx,
|
ctx,
|
||||||
this,
|
this,
|
||||||
getCurrentTransformInverse(ctx),
|
getCurrentTransformInverse(ctx),
|
||||||
PathType.FILL
|
PathType.FILL,
|
||||||
|
opIdx
|
||||||
);
|
);
|
||||||
patternFillTransform = getCurrentTransform(ctx);
|
patternFillTransform = getCurrentTransform(ctx);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
@ -2108,7 +2108,8 @@ class CanvasGraphics {
|
|||||||
ctx,
|
ctx,
|
||||||
this,
|
this,
|
||||||
getCurrentTransformInverse(ctx),
|
getCurrentTransformInverse(ctx),
|
||||||
PathType.STROKE
|
PathType.STROKE,
|
||||||
|
opIdx
|
||||||
);
|
);
|
||||||
patternStrokeTransform = getCurrentTransform(ctx);
|
patternStrokeTransform = getCurrentTransform(ctx);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
@ -2157,8 +2158,7 @@ class CanvasGraphics {
|
|||||||
-measure.actualBoundingBoxAscent,
|
-measure.actualBoundingBoxAscent,
|
||||||
measure.actualBoundingBoxDescent
|
measure.actualBoundingBoxDescent
|
||||||
)
|
)
|
||||||
.recordOperation(opIdx)
|
.recordShowTextOperation(opIdx);
|
||||||
.recordIncrementalData("sameLineText", opIdx);
|
|
||||||
}
|
}
|
||||||
current.x += width * widthAdvanceScale * textHScale;
|
current.x += width * widthAdvanceScale * textHScale;
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
@ -2277,9 +2277,7 @@ class CanvasGraphics {
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
this.compose();
|
this.compose();
|
||||||
|
|
||||||
this.dependencyTracker
|
this.dependencyTracker?.recordShowTextOperation(opIdx);
|
||||||
?.recordOperation(opIdx)
|
|
||||||
.recordIncrementalData("sameLineText", opIdx);
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2351,7 +2349,6 @@ class CanvasGraphics {
|
|||||||
}
|
}
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
if (dependencyTracker) {
|
if (dependencyTracker) {
|
||||||
this.dependencyTracker.recordNestedDependencies();
|
|
||||||
this.dependencyTracker = dependencyTracker;
|
this.dependencyTracker = dependencyTracker;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2378,7 +2375,7 @@ class CanvasGraphics {
|
|||||||
if (IR[0] === "TilingPattern") {
|
if (IR[0] === "TilingPattern") {
|
||||||
const baseTransform = this.baseTransform || getCurrentTransform(this.ctx);
|
const baseTransform = this.baseTransform || getCurrentTransform(this.ctx);
|
||||||
const canvasGraphicsFactory = {
|
const canvasGraphicsFactory = {
|
||||||
createCanvasGraphics: ctx =>
|
createCanvasGraphics: (ctx, renderingOpIdx) =>
|
||||||
new CanvasGraphics(
|
new CanvasGraphics(
|
||||||
ctx,
|
ctx,
|
||||||
this.commonObjs,
|
this.commonObjs,
|
||||||
@ -2392,7 +2389,11 @@ class CanvasGraphics {
|
|||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
this.dependencyTracker
|
this.dependencyTracker
|
||||||
? new CanvasNestedDependencyTracker(this.dependencyTracker, opIdx)
|
? new CanvasNestedDependencyTracker(
|
||||||
|
this.dependencyTracker,
|
||||||
|
renderingOpIdx,
|
||||||
|
/* ignoreBBoxes */ true
|
||||||
|
)
|
||||||
: null
|
: null
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@ -2470,7 +2471,8 @@ class CanvasGraphics {
|
|||||||
ctx,
|
ctx,
|
||||||
this,
|
this,
|
||||||
getCurrentTransformInverse(ctx),
|
getCurrentTransformInverse(ctx),
|
||||||
PathType.SHADING
|
PathType.SHADING,
|
||||||
|
opIdx
|
||||||
);
|
);
|
||||||
|
|
||||||
const inv = getCurrentTransformInverse(ctx);
|
const inv = getCurrentTransformInverse(ctx);
|
||||||
@ -2937,7 +2939,8 @@ class CanvasGraphics {
|
|||||||
maskCtx,
|
maskCtx,
|
||||||
this,
|
this,
|
||||||
getCurrentTransformInverse(ctx),
|
getCurrentTransformInverse(ctx),
|
||||||
PathType.FILL
|
PathType.FILL,
|
||||||
|
opIdx
|
||||||
)
|
)
|
||||||
: fillColor;
|
: fillColor;
|
||||||
maskCtx.fillRect(0, 0, width, height);
|
maskCtx.fillRect(0, 0, width, height);
|
||||||
|
|||||||
@ -2,10 +2,70 @@ import { Util } from "../shared/util.js";
|
|||||||
|
|
||||||
const FORCED_DEPENDENCY_LABEL = "__forcedDependency";
|
const FORCED_DEPENDENCY_LABEL = "__forcedDependency";
|
||||||
|
|
||||||
|
const { floor, ceil } = Math;
|
||||||
|
|
||||||
|
function expandBBox(array, index, minX, minY, maxX, maxY) {
|
||||||
|
array[index * 4 + 0] = Math.min(array[index * 4 + 0], minX);
|
||||||
|
array[index * 4 + 1] = Math.min(array[index * 4 + 1], minY);
|
||||||
|
array[index * 4 + 2] = Math.max(array[index * 4 + 2], maxX);
|
||||||
|
array[index * 4 + 3] = Math.max(array[index * 4 + 3], maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is computed rathter than hard-coded to keep into
|
||||||
|
// account the platform's endianess.
|
||||||
|
const EMPTY_BBOX = new Uint32Array(new Uint8Array([255, 255, 0, 0]).buffer)[0];
|
||||||
|
|
||||||
|
class BBoxReader {
|
||||||
|
#bboxes;
|
||||||
|
|
||||||
|
#coords;
|
||||||
|
|
||||||
|
constructor(bboxes, coords) {
|
||||||
|
this.#bboxes = bboxes;
|
||||||
|
this.#coords = coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
get length() {
|
||||||
|
return this.#bboxes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmpty(i) {
|
||||||
|
return this.#bboxes[i] === EMPTY_BBOX;
|
||||||
|
}
|
||||||
|
|
||||||
|
minX(i) {
|
||||||
|
return this.#coords[i * 4 + 0] / 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
minY(i) {
|
||||||
|
return this.#coords[i * 4 + 1] / 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxX(i) {
|
||||||
|
return (this.#coords[i * 4 + 2] + 1) / 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
maxY(i) {
|
||||||
|
return (this.#coords[i * 4 + 3] + 1) / 256;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ensureDebugMetadata = (map, key) => {
|
||||||
|
if (!map) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
let value = map.get(key);
|
||||||
|
if (!value) {
|
||||||
|
value = { dependencies: new Set(), isRenderingOperation: false };
|
||||||
|
map.set(key, value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {"lineWidth" | "lineCap" | "lineJoin" | "miterLimit" | "dash" |
|
* @typedef {"lineWidth" | "lineCap" | "lineJoin" | "miterLimit" | "dash" |
|
||||||
* "strokeAlpha" | "fillColor" | "fillAlpha" | "globalCompositeOperation" |
|
* "strokeAlpha" | "fillColor" | "fillAlpha" | "globalCompositeOperation" |
|
||||||
* "path" | "filter"} SimpleDependency
|
* "path" | "filter" | "font" | "fontObj"} SimpleDependency
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,9 +114,38 @@ class CanvasDependencyTracker {
|
|||||||
|
|
||||||
#canvasHeight;
|
#canvasHeight;
|
||||||
|
|
||||||
constructor(canvas) {
|
// Uint8ClampedArray<minX, minY, maxX, maxY>
|
||||||
|
#bboxesCoords;
|
||||||
|
|
||||||
|
#bboxes;
|
||||||
|
|
||||||
|
#debugMetadata;
|
||||||
|
|
||||||
|
constructor(canvas, operationsCount, recordDebugMetadata = false) {
|
||||||
this.#canvasWidth = canvas.width;
|
this.#canvasWidth = canvas.width;
|
||||||
this.#canvasHeight = canvas.height;
|
this.#canvasHeight = canvas.height;
|
||||||
|
this.#initializeBBoxes(operationsCount);
|
||||||
|
if (recordDebugMetadata) {
|
||||||
|
this.#debugMetadata = new Map();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
growOperationsCount(operationsCount) {
|
||||||
|
if (operationsCount >= this.#bboxes.length) {
|
||||||
|
this.#initializeBBoxes(operationsCount, this.#bboxes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#initializeBBoxes(operationsCount, oldBBoxes) {
|
||||||
|
const buffer = new ArrayBuffer(operationsCount * 4);
|
||||||
|
this.#bboxesCoords = new Uint8ClampedArray(buffer);
|
||||||
|
this.#bboxes = new Uint32Array(buffer);
|
||||||
|
if (oldBBoxes && oldBBoxes.length > 0) {
|
||||||
|
this.#bboxes.set(oldBBoxes);
|
||||||
|
this.#bboxes.fill(EMPTY_BBOX, oldBBoxes.length);
|
||||||
|
} else {
|
||||||
|
this.#bboxes.fill(EMPTY_BBOX);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
save(opIdx) {
|
save(opIdx) {
|
||||||
@ -71,7 +160,7 @@ class CanvasDependencyTracker {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
this.#clipBox = { __proto__: this.#clipBox };
|
this.#clipBox = { __proto__: this.#clipBox };
|
||||||
this.#savesStack.push([opIdx, null]);
|
this.#savesStack.push(opIdx);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -87,9 +176,12 @@ class CanvasDependencyTracker {
|
|||||||
this.#incremental = Object.getPrototypeOf(this.#incremental);
|
this.#incremental = Object.getPrototypeOf(this.#incremental);
|
||||||
this.#clipBox = Object.getPrototypeOf(this.#clipBox);
|
this.#clipBox = Object.getPrototypeOf(this.#clipBox);
|
||||||
|
|
||||||
const lastPair = this.#savesStack.pop();
|
const lastSave = this.#savesStack.pop();
|
||||||
if (lastPair !== undefined) {
|
if (lastSave !== undefined) {
|
||||||
lastPair[1] = opIdx;
|
ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(
|
||||||
|
lastSave
|
||||||
|
);
|
||||||
|
this.#bboxes[opIdx] = this.#bboxes[lastSave];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@ -99,7 +191,7 @@ class CanvasDependencyTracker {
|
|||||||
* @param {number} idx
|
* @param {number} idx
|
||||||
*/
|
*/
|
||||||
recordOpenMarker(idx) {
|
recordOpenMarker(idx) {
|
||||||
this.#savesStack.push([idx, null]);
|
this.#savesStack.push(idx);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,13 +199,16 @@ class CanvasDependencyTracker {
|
|||||||
if (this.#savesStack.length === 0) {
|
if (this.#savesStack.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.#savesStack.at(-1)[0];
|
return this.#savesStack.at(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
recordCloseMarker(idx) {
|
recordCloseMarker(opIdx) {
|
||||||
const lastPair = this.#savesStack.pop();
|
const lastSave = this.#savesStack.pop();
|
||||||
if (lastPair !== undefined) {
|
if (lastSave !== undefined) {
|
||||||
lastPair[1] = idx;
|
ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(
|
||||||
|
lastSave
|
||||||
|
);
|
||||||
|
this.#bboxes[opIdx] = this.#bboxes[lastSave];
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -121,14 +216,17 @@ class CanvasDependencyTracker {
|
|||||||
// Marked content needs a separate stack from save/restore, because they
|
// Marked content needs a separate stack from save/restore, because they
|
||||||
// form two independent trees.
|
// form two independent trees.
|
||||||
beginMarkedContent(opIdx) {
|
beginMarkedContent(opIdx) {
|
||||||
this.#markedContentStack.push([opIdx, null]);
|
this.#markedContentStack.push(opIdx);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
endMarkedContent(opIdx) {
|
endMarkedContent(opIdx) {
|
||||||
const lastPair = this.#markedContentStack.pop();
|
const lastSave = this.#markedContentStack.pop();
|
||||||
if (lastPair !== undefined) {
|
if (lastSave !== undefined) {
|
||||||
lastPair[1] = opIdx;
|
ensureDebugMetadata(this.#debugMetadata, opIdx)?.dependencies.add(
|
||||||
|
lastSave
|
||||||
|
);
|
||||||
|
this.#bboxes[opIdx] = this.#bboxes[lastSave];
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -182,6 +280,15 @@ class CanvasDependencyTracker {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SimpleDependency} name
|
||||||
|
* @param {string} depName
|
||||||
|
* @param {number} fallbackIdx
|
||||||
|
*/
|
||||||
|
recordSimpleDataFromNamed(name, depName, fallbackIdx) {
|
||||||
|
this.#simple[name] = this.#namedDependencies.get(depName) ?? fallbackIdx;
|
||||||
|
}
|
||||||
|
|
||||||
// All next operations, until the next .restore(), will depend on this
|
// All next operations, until the next .restore(), will depend on this
|
||||||
recordFutureForcedDependency(name, idx) {
|
recordFutureForcedDependency(name, idx) {
|
||||||
this.recordIncrementalData(FORCED_DEPENDENCY_LABEL, idx);
|
this.recordIncrementalData(FORCED_DEPENDENCY_LABEL, idx);
|
||||||
@ -207,18 +314,16 @@ class CanvasDependencyTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetBBox(idx) {
|
resetBBox(idx) {
|
||||||
this.#pendingBBoxIdx = idx;
|
if (this.#pendingBBoxIdx !== idx) {
|
||||||
this.#pendingBBox[0] = Infinity;
|
this.#pendingBBoxIdx = idx;
|
||||||
this.#pendingBBox[1] = Infinity;
|
this.#pendingBBox[0] = Infinity;
|
||||||
this.#pendingBBox[2] = -Infinity;
|
this.#pendingBBox[1] = Infinity;
|
||||||
this.#pendingBBox[3] = -Infinity;
|
this.#pendingBBox[2] = -Infinity;
|
||||||
|
this.#pendingBBox[3] = -Infinity;
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasPendingBBox() {
|
|
||||||
return this.#pendingBBoxIdx !== -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
|
recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
|
||||||
const transform = Util.multiplyByDOMMatrix(
|
const transform = Util.multiplyByDOMMatrix(
|
||||||
this.#baseTransformStack.at(-1),
|
this.#baseTransformStack.at(-1),
|
||||||
@ -384,20 +489,6 @@ class CanvasDependencyTracker {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
copyDependenciesFromIncrementalOperation(idx, name) {
|
|
||||||
const operations = this.#operations;
|
|
||||||
const pendingDependencies = this.#pendingDependencies;
|
|
||||||
for (const depIdx of this.#incremental[name]) {
|
|
||||||
operations
|
|
||||||
.get(depIdx)
|
|
||||||
.dependencies.forEach(
|
|
||||||
pendingDependencies.add,
|
|
||||||
pendingDependencies.add(depIdx)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
recordNamedDependency(idx, name) {
|
recordNamedDependency(idx, name) {
|
||||||
if (this.#namedDependencies.has(name)) {
|
if (this.#namedDependencies.has(name)) {
|
||||||
this.#pendingDependencies.add(this.#namedDependencies.get(name));
|
this.#pendingDependencies.add(this.#namedDependencies.get(name));
|
||||||
@ -409,38 +500,74 @@ class CanvasDependencyTracker {
|
|||||||
/**
|
/**
|
||||||
* @param {number} idx
|
* @param {number} idx
|
||||||
*/
|
*/
|
||||||
recordOperation(idx, preserveBbox = false) {
|
recordOperation(idx, preserve = false) {
|
||||||
this.recordDependencies(idx, [FORCED_DEPENDENCY_LABEL]);
|
this.recordDependencies(idx, [FORCED_DEPENDENCY_LABEL]);
|
||||||
const dependencies = new Set(this.#pendingDependencies);
|
|
||||||
const pairs = this.#savesStack.concat(this.#markedContentStack);
|
if (this.#debugMetadata) {
|
||||||
const bbox =
|
const metadata = ensureDebugMetadata(this.#debugMetadata, idx);
|
||||||
this.#pendingBBoxIdx === idx
|
const { dependencies } = metadata;
|
||||||
? {
|
this.#pendingDependencies.forEach(dependencies.add, dependencies);
|
||||||
minX: this.#pendingBBox[0],
|
this.#savesStack.forEach(dependencies.add, dependencies);
|
||||||
minY: this.#pendingBBox[1],
|
this.#markedContentStack.forEach(dependencies.add, dependencies);
|
||||||
maxX: this.#pendingBBox[2],
|
dependencies.delete(idx);
|
||||||
maxY: this.#pendingBBox[3],
|
metadata.isRenderingOperation = true;
|
||||||
}
|
}
|
||||||
: null;
|
|
||||||
this.#operations.set(idx, { bbox, pairs, dependencies });
|
if (this.#pendingBBoxIdx === idx) {
|
||||||
if (!preserveBbox) {
|
const minX = floor((this.#pendingBBox[0] * 256) / this.#canvasWidth);
|
||||||
this.#pendingBBoxIdx = -1;
|
const minY = floor((this.#pendingBBox[1] * 256) / this.#canvasHeight);
|
||||||
|
const maxX = ceil((this.#pendingBBox[2] * 256) / this.#canvasWidth);
|
||||||
|
const maxY = ceil((this.#pendingBBox[3] * 256) / this.#canvasHeight);
|
||||||
|
|
||||||
|
expandBBox(this.#bboxesCoords, idx, minX, minY, maxX, maxY);
|
||||||
|
for (const depIdx of this.#pendingDependencies) {
|
||||||
|
if (depIdx !== idx) {
|
||||||
|
expandBBox(this.#bboxesCoords, depIdx, minX, minY, maxX, maxY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const saveIdx of this.#savesStack) {
|
||||||
|
if (saveIdx !== idx) {
|
||||||
|
expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const saveIdx of this.#markedContentStack) {
|
||||||
|
if (saveIdx !== idx) {
|
||||||
|
expandBBox(this.#bboxesCoords, saveIdx, minX, minY, maxX, maxY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preserve) {
|
||||||
|
this.#pendingDependencies.clear();
|
||||||
|
this.#pendingBBoxIdx = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.#pendingDependencies.clear();
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bboxToClipBoxDropOperation(idx) {
|
recordShowTextOperation(idx, preserve = false) {
|
||||||
if (this.#pendingBBoxIdx !== -1) {
|
const deps = Array.from(this.#pendingDependencies);
|
||||||
|
this.recordOperation(idx, preserve);
|
||||||
|
this.recordIncrementalData("sameLineText", idx);
|
||||||
|
for (const dep of deps) {
|
||||||
|
this.recordIncrementalData("sameLineText", dep);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bboxToClipBoxDropOperation(idx, preserve = false) {
|
||||||
|
if (this.#pendingBBoxIdx === idx) {
|
||||||
this.#pendingBBoxIdx = -1;
|
this.#pendingBBoxIdx = -1;
|
||||||
|
|
||||||
this.#clipBox[0] = Math.max(this.#clipBox[0], this.#pendingBBox[0]);
|
this.#clipBox[0] = Math.max(this.#clipBox[0], this.#pendingBBox[0]);
|
||||||
this.#clipBox[1] = Math.max(this.#clipBox[1], this.#pendingBBox[1]);
|
this.#clipBox[1] = Math.max(this.#clipBox[1], this.#pendingBBox[1]);
|
||||||
this.#clipBox[2] = Math.min(this.#clipBox[2], this.#pendingBBox[2]);
|
this.#clipBox[2] = Math.min(this.#clipBox[2], this.#pendingBBox[2]);
|
||||||
this.#clipBox[3] = Math.min(this.#clipBox[3], this.#pendingBBox[3]);
|
this.#clipBox[3] = Math.min(this.#clipBox[3], this.#pendingBBox[3]);
|
||||||
|
|
||||||
|
if (!preserve) {
|
||||||
|
this.#pendingDependencies.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.#pendingDependencies.clear();
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,21 +591,11 @@ class CanvasDependencyTracker {
|
|||||||
|
|
||||||
take() {
|
take() {
|
||||||
this.#fontBBoxTrustworthy.clear();
|
this.#fontBBoxTrustworthy.clear();
|
||||||
return Array.from(
|
return new BBoxReader(this.#bboxes, this.#bboxesCoords);
|
||||||
this.#operations,
|
}
|
||||||
([idx, { bbox, pairs, dependencies }]) => {
|
|
||||||
pairs.forEach(pair => pair.forEach(dependencies.add, dependencies));
|
takeDebugMetadata() {
|
||||||
dependencies.delete(idx);
|
return this.#debugMetadata;
|
||||||
return {
|
|
||||||
minX: (bbox?.minX ?? 0) / this.#canvasWidth,
|
|
||||||
maxX: (bbox?.maxX ?? this.#canvasWidth) / this.#canvasWidth,
|
|
||||||
minY: (bbox?.minY ?? 0) / this.#canvasHeight,
|
|
||||||
maxY: (bbox?.maxY ?? this.#canvasHeight) / this.#canvasHeight,
|
|
||||||
dependencies: Array.from(dependencies).sort((a, b) => a - b),
|
|
||||||
idx,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,14 +613,17 @@ class CanvasNestedDependencyTracker {
|
|||||||
/** @type {number} */
|
/** @type {number} */
|
||||||
#opIdx;
|
#opIdx;
|
||||||
|
|
||||||
#nestingLevel = 0;
|
#ignoreBBoxes;
|
||||||
|
|
||||||
#outerDependencies;
|
#nestingLevel = 0;
|
||||||
|
|
||||||
#savesLevel = 0;
|
#savesLevel = 0;
|
||||||
|
|
||||||
constructor(dependencyTracker, opIdx) {
|
constructor(dependencyTracker, opIdx, ignoreBBoxes) {
|
||||||
if (dependencyTracker instanceof CanvasNestedDependencyTracker) {
|
if (
|
||||||
|
dependencyTracker instanceof CanvasNestedDependencyTracker &&
|
||||||
|
dependencyTracker.#ignoreBBoxes === !!ignoreBBoxes
|
||||||
|
) {
|
||||||
// The goal of CanvasNestedDependencyTracker is to collapse all operations
|
// The goal of CanvasNestedDependencyTracker is to collapse all operations
|
||||||
// into a single one. If we are already in a
|
// into a single one. If we are already in a
|
||||||
// CanvasNestedDependencyTracker, that is already happening.
|
// CanvasNestedDependencyTracker, that is already happening.
|
||||||
@ -511,8 +631,12 @@ class CanvasNestedDependencyTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.#dependencyTracker = dependencyTracker;
|
this.#dependencyTracker = dependencyTracker;
|
||||||
this.#outerDependencies = dependencyTracker._takePendingDependencies();
|
|
||||||
this.#opIdx = opIdx;
|
this.#opIdx = opIdx;
|
||||||
|
this.#ignoreBBoxes = !!ignoreBBoxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
growOperationsCount() {
|
||||||
|
throw new Error("Unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
save(opIdx) {
|
save(opIdx) {
|
||||||
@ -595,6 +719,20 @@ class CanvasNestedDependencyTracker {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SimpleDependency} name
|
||||||
|
* @param {string} depName
|
||||||
|
* @param {number} fallbackIdx
|
||||||
|
*/
|
||||||
|
recordSimpleDataFromNamed(name, depName, fallbackIdx) {
|
||||||
|
this.#dependencyTracker.recordSimpleDataFromNamed(
|
||||||
|
name,
|
||||||
|
depName,
|
||||||
|
this.#opIdx
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
// All next operations, until the next .restore(), will depend on this
|
// All next operations, until the next .restore(), will depend on this
|
||||||
recordFutureForcedDependency(name, idx) {
|
recordFutureForcedDependency(name, idx) {
|
||||||
this.#dependencyTracker.recordFutureForcedDependency(name, this.#opIdx);
|
this.#dependencyTracker.recordFutureForcedDependency(name, this.#opIdx);
|
||||||
@ -614,55 +752,59 @@ class CanvasNestedDependencyTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetBBox(idx) {
|
resetBBox(idx) {
|
||||||
if (!this.#dependencyTracker.hasPendingBBox) {
|
if (!this.#ignoreBBoxes) {
|
||||||
this.#dependencyTracker.resetBBox(this.#opIdx);
|
this.#dependencyTracker.resetBBox(this.#opIdx);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasPendingBBox() {
|
|
||||||
return this.#dependencyTracker.hasPendingBBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
|
recordClipBox(idx, ctx, minX, maxX, minY, maxY) {
|
||||||
this.#dependencyTracker.recordClipBox(
|
if (!this.#ignoreBBoxes) {
|
||||||
this.#opIdx,
|
this.#dependencyTracker.recordClipBox(
|
||||||
ctx,
|
this.#opIdx,
|
||||||
minX,
|
ctx,
|
||||||
maxX,
|
minX,
|
||||||
minY,
|
maxX,
|
||||||
maxY
|
minY,
|
||||||
);
|
maxY
|
||||||
|
);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
recordBBox(idx, ctx, minX, maxX, minY, maxY) {
|
recordBBox(idx, ctx, minX, maxX, minY, maxY) {
|
||||||
this.#dependencyTracker.recordBBox(
|
if (!this.#ignoreBBoxes) {
|
||||||
this.#opIdx,
|
this.#dependencyTracker.recordBBox(
|
||||||
ctx,
|
this.#opIdx,
|
||||||
minX,
|
ctx,
|
||||||
maxX,
|
minX,
|
||||||
minY,
|
maxX,
|
||||||
maxY
|
minY,
|
||||||
);
|
maxY
|
||||||
|
);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
recordCharacterBBox(idx, ctx, font, scale, x, y, getMeasure) {
|
recordCharacterBBox(idx, ctx, font, scale, x, y, getMeasure) {
|
||||||
this.#dependencyTracker.recordCharacterBBox(
|
if (!this.#ignoreBBoxes) {
|
||||||
this.#opIdx,
|
this.#dependencyTracker.recordCharacterBBox(
|
||||||
ctx,
|
this.#opIdx,
|
||||||
font,
|
ctx,
|
||||||
scale,
|
font,
|
||||||
x,
|
scale,
|
||||||
y,
|
x,
|
||||||
getMeasure
|
y,
|
||||||
);
|
getMeasure
|
||||||
|
);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
recordFullPageBBox(idx) {
|
recordFullPageBBox(idx) {
|
||||||
this.#dependencyTracker.recordFullPageBBox(this.#opIdx);
|
if (!this.#ignoreBBoxes) {
|
||||||
|
this.#dependencyTracker.recordFullPageBBox(this.#opIdx);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,14 +817,6 @@ class CanvasNestedDependencyTracker {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
copyDependenciesFromIncrementalOperation(idx, name) {
|
|
||||||
this.#dependencyTracker.copyDependenciesFromIncrementalOperation(
|
|
||||||
this.#opIdx,
|
|
||||||
name
|
|
||||||
);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
recordNamedDependency(idx, name) {
|
recordNamedDependency(idx, name) {
|
||||||
this.#dependencyTracker.recordNamedDependency(this.#opIdx, name);
|
this.#dependencyTracker.recordNamedDependency(this.#opIdx, name);
|
||||||
return this;
|
return this;
|
||||||
@ -694,25 +828,26 @@ class CanvasNestedDependencyTracker {
|
|||||||
*/
|
*/
|
||||||
recordOperation(idx) {
|
recordOperation(idx) {
|
||||||
this.#dependencyTracker.recordOperation(this.#opIdx, true);
|
this.#dependencyTracker.recordOperation(this.#opIdx, true);
|
||||||
const operation = this.#dependencyTracker._extractOperation(this.#opIdx);
|
return this;
|
||||||
for (const depIdx of operation.dependencies) {
|
}
|
||||||
this.#outerDependencies.add(depIdx);
|
|
||||||
}
|
recordShowTextOperation(idx) {
|
||||||
this.#outerDependencies.delete(this.#opIdx);
|
this.#dependencyTracker.recordShowTextOperation(this.#opIdx, true);
|
||||||
this.#outerDependencies.delete(null);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bboxToClipBoxDropOperation(idx) {
|
bboxToClipBoxDropOperation(idx) {
|
||||||
this.#dependencyTracker.bboxToClipBoxDropOperation(this.#opIdx);
|
if (!this.#ignoreBBoxes) {
|
||||||
|
this.#dependencyTracker.bboxToClipBoxDropOperation(this.#opIdx, true);
|
||||||
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
recordNestedDependencies() {
|
take() {
|
||||||
this.#dependencyTracker._pushPendingDependencies(this.#outerDependencies);
|
throw new Error("Unreachable");
|
||||||
}
|
}
|
||||||
|
|
||||||
take() {
|
takeDebugMetadata() {
|
||||||
throw new Error("Unreachable");
|
throw new Error("Unreachable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -759,6 +894,7 @@ const Dependencies = {
|
|||||||
"moveText",
|
"moveText",
|
||||||
"textMatrix",
|
"textMatrix",
|
||||||
"font",
|
"font",
|
||||||
|
"fontObj",
|
||||||
"filter",
|
"filter",
|
||||||
"fillColor",
|
"fillColor",
|
||||||
"textRenderingMode",
|
"textRenderingMode",
|
||||||
@ -766,7 +902,8 @@ const Dependencies = {
|
|||||||
"fillAlpha",
|
"fillAlpha",
|
||||||
"strokeAlpha",
|
"strokeAlpha",
|
||||||
"globalCompositeOperation",
|
"globalCompositeOperation",
|
||||||
// TODO: More
|
|
||||||
|
"sameLineText",
|
||||||
],
|
],
|
||||||
transform: ["transform"],
|
transform: ["transform"],
|
||||||
transformAndFill: ["transform", "fillColor"],
|
transformAndFill: ["transform", "fillColor"],
|
||||||
|
|||||||
@ -478,7 +478,7 @@ class TilingPattern {
|
|||||||
this.baseTransform = baseTransform;
|
this.baseTransform = baseTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
createPatternCanvas(owner) {
|
createPatternCanvas(owner, opIdx) {
|
||||||
const {
|
const {
|
||||||
bbox,
|
bbox,
|
||||||
operatorList,
|
operatorList,
|
||||||
@ -567,7 +567,7 @@ class TilingPattern {
|
|||||||
dimy.size
|
dimy.size
|
||||||
);
|
);
|
||||||
const tmpCtx = tmpCanvas.context;
|
const tmpCtx = tmpCanvas.context;
|
||||||
const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx);
|
const graphics = canvasGraphicsFactory.createCanvasGraphics(tmpCtx, opIdx);
|
||||||
graphics.groupLevel = owner.groupLevel;
|
graphics.groupLevel = owner.groupLevel;
|
||||||
|
|
||||||
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
this.setFillAndStrokeStyleToContext(graphics, paintType, color);
|
||||||
@ -600,7 +600,7 @@ class TilingPattern {
|
|||||||
|
|
||||||
graphics.endDrawing();
|
graphics.endDrawing();
|
||||||
|
|
||||||
graphics.dependencyTracker?.restore().recordNestedDependencies?.();
|
graphics.dependencyTracker?.restore();
|
||||||
tmpCtx.restore();
|
tmpCtx.restore();
|
||||||
|
|
||||||
if (redrawHorizontally || redrawVertically) {
|
if (redrawHorizontally || redrawVertically) {
|
||||||
@ -726,7 +726,7 @@ class TilingPattern {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPattern(ctx, owner, inverse, pathType) {
|
getPattern(ctx, owner, inverse, pathType, opIdx) {
|
||||||
// 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;
|
||||||
if (pathType !== PathType.SHADING) {
|
if (pathType !== PathType.SHADING) {
|
||||||
@ -736,7 +736,7 @@ class TilingPattern {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const temporaryPatternCanvas = this.createPatternCanvas(owner);
|
const temporaryPatternCanvas = this.createPatternCanvas(owner, opIdx);
|
||||||
|
|
||||||
let domMatrix = new DOMMatrix(matrix);
|
let domMatrix = new DOMMatrix(matrix);
|
||||||
// Rescale and so that the ctx.createPattern call generates a pattern with
|
// Rescale and so that the ctx.createPattern call generates a pattern with
|
||||||
|
|||||||
@ -1117,28 +1117,7 @@ class Driver {
|
|||||||
const baseline = ctx.canvas.toDataURL("image/png");
|
const baseline = ctx.canvas.toDataURL("image/png");
|
||||||
this._clearCanvas();
|
this._clearCanvas();
|
||||||
|
|
||||||
const filteredIndexes = new Set();
|
const recordedBBoxes = page.recordedBBoxes;
|
||||||
|
|
||||||
// TODO: This logic is copy-psated from PDFPageDetailView.
|
|
||||||
// We should export it instead, because even though it's
|
|
||||||
// not the core logic of partial rendering it is still
|
|
||||||
// relevant
|
|
||||||
const recordedGroups = page.recordedGroups;
|
|
||||||
for (let i = 0, ii = recordedGroups.length; i < ii; i++) {
|
|
||||||
const group = recordedGroups[i];
|
|
||||||
if (
|
|
||||||
group.minX <= partialCrop.maxX &&
|
|
||||||
group.maxX >= partialCrop.minX &&
|
|
||||||
group.minY <= partialCrop.maxY &&
|
|
||||||
group.maxY >= partialCrop.minY
|
|
||||||
) {
|
|
||||||
filteredIndexes.add(group.idx);
|
|
||||||
group.dependencies.forEach(
|
|
||||||
filteredIndexes.add,
|
|
||||||
filteredIndexes
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const partialRenderContext = {
|
const partialRenderContext = {
|
||||||
canvasContext: ctx,
|
canvasContext: ctx,
|
||||||
@ -1149,7 +1128,17 @@ class Driver {
|
|||||||
pageColors,
|
pageColors,
|
||||||
transform,
|
transform,
|
||||||
recordOperations: false,
|
recordOperations: false,
|
||||||
filteredOperationIndexes: filteredIndexes,
|
operationsFilter(index) {
|
||||||
|
if (recordedBBoxes.isEmpty(index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
recordedBBoxes.minX(index) <= partialCrop.maxX &&
|
||||||
|
recordedBBoxes.maxX(index) >= partialCrop.minX &&
|
||||||
|
recordedBBoxes.minY(index) <= partialCrop.maxY &&
|
||||||
|
recordedBBoxes.maxY(index) >= partialCrop.minY
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const partialRenderTask = page.render(partialRenderContext);
|
const partialRenderTask = page.render(partialRenderContext);
|
||||||
|
|||||||
@ -541,8 +541,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bug1753983",
|
"id": "bug1753983",
|
||||||
@ -673,8 +672,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bug1473809",
|
"id": "bug1473809",
|
||||||
@ -780,8 +778,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": "Gives slightly different results in Firefox when running in headless mode"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "issue2128",
|
"id": "issue2128",
|
||||||
@ -1556,8 +1553,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": "Gives slightly different results in Firefox when running in headless mode"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bug1811668",
|
"id": "bug1811668",
|
||||||
@ -2904,8 +2900,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bug1245391-text",
|
"id": "bug1245391-text",
|
||||||
@ -3250,8 +3245,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "bug1337429",
|
"id": "bug1337429",
|
||||||
@ -8080,8 +8074,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": "Half pixel rendering at the edge of the image"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "issue16454",
|
"id": "issue16454",
|
||||||
@ -11270,8 +11263,7 @@
|
|||||||
"maxX": 0.5,
|
"maxX": 0.5,
|
||||||
"minY": 0.25,
|
"minY": 0.25,
|
||||||
"maxY": 0.5
|
"maxY": 0.5
|
||||||
},
|
}
|
||||||
"knownPartialMismatch": "One-bit difference in two pixels at the edge"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "issue14724",
|
"id": "issue14724",
|
||||||
|
|||||||
@ -44,7 +44,7 @@ class BasePDFPageView {
|
|||||||
|
|
||||||
pageColors = null;
|
pageColors = null;
|
||||||
|
|
||||||
recordedGroups = null;
|
recordedBBoxes = null;
|
||||||
|
|
||||||
renderingQueue = null;
|
renderingQueue = null;
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ class BasePDFPageView {
|
|||||||
if (renderTask === this.renderTask) {
|
if (renderTask === this.renderTask) {
|
||||||
this.renderTask = null;
|
this.renderTask = null;
|
||||||
if (this.enableOptimizedPartialRendering) {
|
if (this.enableOptimizedPartialRendering) {
|
||||||
this.recordedGroups ??= renderTask.recordedGroups;
|
this.recordedBBoxes ??= renderTask.recordedBBoxes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
155
web/debugger.mjs
155
web/debugger.mjs
@ -295,7 +295,7 @@ class Stepper {
|
|||||||
this.currentIdx = -1;
|
this.currentIdx = -1;
|
||||||
this.operatorListIdx = 0;
|
this.operatorListIdx = 0;
|
||||||
this.indentLevel = 0;
|
this.indentLevel = 0;
|
||||||
this.operatorGroups = null;
|
this.operatorsGroups = null;
|
||||||
this.pageContainer = pageContainer;
|
this.pageContainer = pageContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,7 +439,7 @@ class Stepper {
|
|||||||
this.table.append(chunk);
|
this.table.append(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
setOperatorGroups(groups) {
|
setOperatorBBoxes(bboxes, metadata) {
|
||||||
let boxesContainer = this.pageContainer.querySelector(".pdfBugGroupsLayer");
|
let boxesContainer = this.pageContainer.querySelector(".pdfBugGroupsLayer");
|
||||||
if (!boxesContainer) {
|
if (!boxesContainer) {
|
||||||
boxesContainer = this.#c("div");
|
boxesContainer = this.#c("div");
|
||||||
@ -457,12 +457,55 @@ class Stepper {
|
|||||||
}
|
}
|
||||||
boxesContainer.innerHTML = "";
|
boxesContainer.innerHTML = "";
|
||||||
|
|
||||||
groups = groups.toSorted((a, b) => {
|
const dependents = new Map();
|
||||||
|
for (const [dependentIdx, { dependencies: ownDependencies }] of metadata) {
|
||||||
|
for (const dependencyIdx of ownDependencies) {
|
||||||
|
let ownDependents = dependents.get(dependencyIdx);
|
||||||
|
if (!ownDependents) {
|
||||||
|
dependents.set(dependencyIdx, (ownDependents = new Set()));
|
||||||
|
}
|
||||||
|
ownDependents.add(dependentIdx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const groups = Array.from({ length: bboxes.length }, (_, i) => {
|
||||||
|
let minX = null;
|
||||||
|
let minY = null;
|
||||||
|
let maxX = null;
|
||||||
|
let maxY = null;
|
||||||
|
if (!bboxes.isEmpty(i)) {
|
||||||
|
minX = bboxes.minX(i);
|
||||||
|
minY = bboxes.minY(i);
|
||||||
|
maxX = bboxes.maxX(i);
|
||||||
|
maxY = bboxes.maxY(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
minX,
|
||||||
|
minY,
|
||||||
|
maxX,
|
||||||
|
maxY,
|
||||||
|
dependencies: Array.from(metadata.get(i)?.dependencies ?? []).sort(),
|
||||||
|
dependents: Array.from(dependents.get(i) ?? []).sort(),
|
||||||
|
isRenderingOperation: metadata.get(i)?.isRenderingOperation ?? false,
|
||||||
|
idx: i,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.operatorsGroups = groups;
|
||||||
|
|
||||||
|
const operatorsGroupsByZindex = groups.toSorted((a, b) => {
|
||||||
|
if (a.minX === null) {
|
||||||
|
return b.minX === null ? 0 : 1;
|
||||||
|
}
|
||||||
|
if (b.minX === null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
const diffs = [
|
const diffs = [
|
||||||
a.minX - b.minX,
|
|
||||||
a.minY - b.minY,
|
a.minY - b.minY,
|
||||||
b.maxX - a.maxX,
|
a.minX - b.minX,
|
||||||
b.maxY - a.maxY,
|
b.maxY - a.maxY,
|
||||||
|
b.maxX - a.maxX,
|
||||||
];
|
];
|
||||||
for (const diff of diffs) {
|
for (const diff of diffs) {
|
||||||
if (Math.abs(diff) > 0.01) {
|
if (Math.abs(diff) > 0.01) {
|
||||||
@ -476,16 +519,18 @@ class Stepper {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
this.operatorGroups = groups;
|
|
||||||
|
|
||||||
for (let i = 0; i < groups.length; i++) {
|
for (let i = 0; i < operatorsGroupsByZindex.length; i++) {
|
||||||
const el = this.#c("div");
|
const group = operatorsGroupsByZindex[i];
|
||||||
el.style.left = `${groups[i].minX * 100}%`;
|
if (group.minX !== null) {
|
||||||
el.style.top = `${groups[i].minY * 100}%`;
|
const el = this.#c("div");
|
||||||
el.style.width = `${(groups[i].maxX - groups[i].minX) * 100}%`;
|
el.style.left = `${group.minX * 100}%`;
|
||||||
el.style.height = `${(groups[i].maxY - groups[i].minY) * 100}%`;
|
el.style.top = `${group.minY * 100}%`;
|
||||||
el.dataset.groupIdx = i;
|
el.style.width = `${(group.maxX - group.minX) * 100}%`;
|
||||||
boxesContainer.append(el);
|
el.style.height = `${(group.maxY - group.minY) * 100}%`;
|
||||||
|
el.dataset.idx = group.idx;
|
||||||
|
boxesContainer.append(el);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,51 +541,85 @@ class Stepper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const index = +tr.dataset.idx;
|
const index = +tr.dataset.idx;
|
||||||
|
this.#highlightStepsGroup(index);
|
||||||
const closestGroupIndex =
|
|
||||||
this.operatorGroups?.findIndex(({ idx }) => idx === index) ?? -1;
|
|
||||||
if (closestGroupIndex === -1) {
|
|
||||||
this.hoverStyle.innerText = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#highlightStepsGroup(closestGroupIndex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleDebugBoxHover(e) {
|
#handleDebugBoxHover(e) {
|
||||||
if (e.target.dataset.groupIdx === undefined) {
|
if (e.target.dataset.idx === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupIdx = Number(e.target.dataset.groupIdx);
|
const idx = Number(e.target.dataset.idx);
|
||||||
this.#highlightStepsGroup(groupIdx);
|
this.#highlightStepsGroup(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleDebugBoxClick(e) {
|
#handleDebugBoxClick(e) {
|
||||||
if (e.target.dataset.groupIdx === undefined) {
|
if (e.target.dataset.idx === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupIdx = Number(e.target.dataset.groupIdx);
|
const idx = Number(e.target.dataset.idx);
|
||||||
const group = this.operatorGroups[groupIdx];
|
|
||||||
|
|
||||||
this.table.childNodes[group.idx].scrollIntoView();
|
this.table.childNodes[idx].scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
#highlightStepsGroup(groupIndex) {
|
#highlightStepsGroup(index) {
|
||||||
const group = this.operatorGroups[groupIndex];
|
const group = this.operatorsGroups?.[index];
|
||||||
|
if (!group) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.hoverStyle.innerText = `#${this.panel.id} tr[data-idx="${group.idx}"] { background-color: rgba(0, 0, 0, 0.1); }`;
|
const renderingColor = `rgba(0, 0, 0, 0.1)`;
|
||||||
|
const dependencyColor = `rgba(0, 255, 255, 0.1)`;
|
||||||
|
const dependentColor = `rgba(255, 0, 0, 0.1)`;
|
||||||
|
|
||||||
|
const solid = color => `background-color: ${color}`;
|
||||||
|
// Used for operations that have an empty bounding box
|
||||||
|
const striped = color => `
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
-45deg,
|
||||||
|
white,
|
||||||
|
white 10px,
|
||||||
|
${color} 10px,
|
||||||
|
${color} 20px
|
||||||
|
)
|
||||||
|
`;
|
||||||
|
|
||||||
|
const select = idx => `#${this.panel.id} tr[data-idx="${idx}"]`;
|
||||||
|
const selectN = idxs =>
|
||||||
|
idxs.length === 0 ? "q:not(q)" : idxs.map(select).join(", ");
|
||||||
|
|
||||||
|
const isEmpty = idx =>
|
||||||
|
!this.operatorsGroups[idx] || this.operatorsGroups[idx].minX === null;
|
||||||
|
|
||||||
|
const selfColor = group.isRenderingOperation
|
||||||
|
? renderingColor
|
||||||
|
: dependentColor;
|
||||||
|
|
||||||
|
this.hoverStyle.innerText = `${select(index)} {
|
||||||
|
${group.minX === null ? striped(selfColor) : solid(selfColor)}
|
||||||
|
}`;
|
||||||
if (group.dependencies.length > 0) {
|
if (group.dependencies.length > 0) {
|
||||||
const selector = group.dependencies
|
this.hoverStyle.innerText += `
|
||||||
.map(idx => `#${this.panel.id} tr[data-idx="${idx}"]`)
|
${selectN(group.dependencies.filter(idx => !isEmpty(idx)))} {
|
||||||
.join(", ");
|
${solid(dependencyColor)}
|
||||||
this.hoverStyle.innerText += `${selector} { background-color: rgba(0, 255, 255, 0.1); }`;
|
}
|
||||||
|
${selectN(group.dependencies.filter(isEmpty))} {
|
||||||
|
${striped(dependencyColor)}
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
if (group.dependents.length > 0) {
|
||||||
|
this.hoverStyle.innerText += `
|
||||||
|
${selectN(group.dependents.filter(idx => !isEmpty(idx)))} {
|
||||||
|
${solid(dependentColor)}
|
||||||
|
}
|
||||||
|
${selectN(group.dependents.filter(isEmpty))} {
|
||||||
|
${striped(dependentColor)}
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hoverStyle.innerText += `
|
this.hoverStyle.innerText += `
|
||||||
#viewer [data-page-number="${this.pageIndex + 1}"] .pdfBugGroupsLayer :nth-child(${groupIndex + 1}) {
|
#viewer [data-page-number="${this.pageIndex + 1}"] .pdfBugGroupsLayer [data-idx="${index}"] {
|
||||||
background-color: var(--hover-background-color);
|
background-color: var(--hover-background-color);
|
||||||
outline-style: var(--hover-outline-style);
|
outline-style: var(--hover-outline-style);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -188,18 +188,12 @@ class PDFPageDetailView extends BasePDFPageView {
|
|||||||
|
|
||||||
_getRenderingContext(canvas, transform) {
|
_getRenderingContext(canvas, transform) {
|
||||||
const baseContext = this.pageView._getRenderingContext(canvas, transform);
|
const baseContext = this.pageView._getRenderingContext(canvas, transform);
|
||||||
const recordedGroups = this.pdfPage.recordedGroups;
|
const recordedBBoxes = this.pdfPage.recordedBBoxes;
|
||||||
|
|
||||||
if (!recordedGroups || !this.enableOptimizedPartialRendering) {
|
if (!recordedBBoxes || !this.enableOptimizedPartialRendering) {
|
||||||
return { ...baseContext, recordOperations: false };
|
return { ...baseContext, recordOperations: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: There is probably a better data structure for this.
|
|
||||||
// The indexes are always checked in increasing order, so we can just try
|
|
||||||
// to build a pre-sorted array which should have faster lookups.
|
|
||||||
// Needs benchmarking.
|
|
||||||
const filteredIndexes = new Set();
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
viewport: { width: vWidth, height: vHeight },
|
viewport: { width: vWidth, height: vHeight },
|
||||||
} = this.pageView;
|
} = this.pageView;
|
||||||
@ -215,23 +209,20 @@ class PDFPageDetailView extends BasePDFPageView {
|
|||||||
const detailMaxX = (aMinX + aWidth) / vWidth;
|
const detailMaxX = (aMinX + aWidth) / vWidth;
|
||||||
const detailMaxY = (aMinY + aHeight) / vHeight;
|
const detailMaxY = (aMinY + aHeight) / vHeight;
|
||||||
|
|
||||||
for (let i = 0, ii = recordedGroups.length; i < ii; i++) {
|
|
||||||
const group = recordedGroups[i];
|
|
||||||
if (
|
|
||||||
group.minX <= detailMaxX &&
|
|
||||||
group.maxX >= detailMinX &&
|
|
||||||
group.minY <= detailMaxY &&
|
|
||||||
group.maxY >= detailMinY
|
|
||||||
) {
|
|
||||||
filteredIndexes.add(group.idx);
|
|
||||||
group.dependencies.forEach(filteredIndexes.add, filteredIndexes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...baseContext,
|
...baseContext,
|
||||||
recordOperations: false,
|
recordOperations: false,
|
||||||
filteredOperationIndexes: filteredIndexes,
|
operationsFilter(index) {
|
||||||
|
if (recordedBBoxes.isEmpty(index)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
recordedBBoxes.minX(index) <= detailMaxX &&
|
||||||
|
recordedBBoxes.maxX(index) >= detailMinX &&
|
||||||
|
recordedBBoxes.minY(index) <= detailMaxY &&
|
||||||
|
recordedBBoxes.maxY(index) >= detailMinY
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -934,7 +934,7 @@ class PDFPageView extends BasePDFPageView {
|
|||||||
pageColors: this.pageColors,
|
pageColors: this.pageColors,
|
||||||
isEditing: this.#isEditing,
|
isEditing: this.#isEditing,
|
||||||
recordOperations:
|
recordOperations:
|
||||||
this.enableOptimizedPartialRendering && !this.recordedGroups,
|
this.enableOptimizedPartialRendering && !this.recordedBBoxes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user