Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 8 additions & 13 deletions src/core/obj_bin_transform_core.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,20 +389,15 @@ function compilePatternInfo(ir) {
}

function compileFontPathInfo(path) {
let data;
let buffer;
if (
(typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) ||
FeatureTest.isFloat16ArraySupported
) {
buffer = new ArrayBuffer(path.length * 2);
data = new Float16Array(buffer);
} else {
buffer = new ArrayBuffer(path.length * 4);
data = new Float32Array(buffer);
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
assert(
FeatureTest.isFloat16ArraySupported
? path instanceof Float16Array
: path instanceof Float32Array,
"compileFontPathInfo: Unexpected path format."
);
}
data.set(path);
return buffer;
return path.slice().buffer;
}

export {
Expand Down
29 changes: 17 additions & 12 deletions src/display/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,33 +205,32 @@ function mirrorContextOperations(ctx, destCtx) {
}

class CachedCanvases {
#cache = new Map();

constructor(canvasFactory) {
this.canvasFactory = canvasFactory;
this.cache = Object.create(null);
}

getCanvas(id, width, height) {
let canvasEntry;
if (this.cache[id] !== undefined) {
canvasEntry = this.cache[id];
let canvasEntry = this.#cache.get(id);
if (canvasEntry) {
this.canvasFactory.reset(canvasEntry, width, height);
} else {
canvasEntry = this.canvasFactory.create(width, height);
this.cache[id] = canvasEntry;
this.#cache.set(id, canvasEntry);
}
return canvasEntry;
}

delete(id) {
delete this.cache[id];
this.#cache.delete(id);
}

clear() {
for (const id in this.cache) {
const canvasEntry = this.cache[id];
for (const canvasEntry of this.#cache.values()) {
this.canvasFactory.destroy(canvasEntry);
delete this.cache[id];
}
this.#cache.clear();
}
}

Expand Down Expand Up @@ -787,9 +786,15 @@ class CanvasGraphics {
let fnId, fnArgs;

while (true) {
if (stepper !== undefined && i === stepper.nextBreakPoint) {
stepper.breakIt(i, continueCallback);
return i;
if (stepper !== undefined) {
if (i === stepper.nextBreakPoint) {
stepper.breakIt(i, continueCallback);
return i;
}
if (stepper.shouldSkip(i)) {
i++;
continue;
}
}

if (!operationsFilter || operationsFilter(i)) {
Expand Down
4 changes: 4 additions & 0 deletions web/debugger.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,10 @@ class Stepper {
this.goTo(idx);
}

shouldSkip(idx) {
return false;
}

goTo(idx) {
const allRows = this.panel.getElementsByClassName("line");
for (const row of allRows) {
Expand Down
60 changes: 2 additions & 58 deletions web/internal/canvas_context_details_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,56 +62,6 @@ const COLOR_CTX_PROPS = new Set(["fillStyle", "shadowColor", "strokeStyle"]);

const MATHML_NS = "http://www.w3.org/1998/Math/MathML";

// Cached media queries used by drawCheckerboard.
const _prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
const _prefersHCM = window.matchMedia("(forced-colors: active)");

/**
* Draw a checkerboard pattern filling the canvas, to reveal transparency.
* Mirrors the pattern used in src/display/editor/stamp.js.
*/
function drawCheckerboard(ctx, width, height) {
const isHCM = _prefersHCM.matches;
const isDark = _prefersDark.matches;
let light, dark;
if (isHCM) {
light = "white";
dark = "black";
} else if (isDark) {
light = "#8f8f9d";
dark = "#42414d";
} else {
light = "white";
dark = "#cfcfd8";
}
const boxDim = 15;
const pattern =
typeof OffscreenCanvas !== "undefined"
? new OffscreenCanvas(boxDim * 2, boxDim * 2)
: Object.assign(document.createElement("canvas"), {
width: boxDim * 2,
height: boxDim * 2,
});
const patternCtx = pattern.getContext("2d");
if (!patternCtx) {
return;
}
patternCtx.fillStyle = light;
patternCtx.fillRect(0, 0, boxDim * 2, boxDim * 2);
patternCtx.fillStyle = dark;
patternCtx.fillRect(0, 0, boxDim, boxDim);
patternCtx.fillRect(boxDim, boxDim, boxDim, boxDim);
ctx.save();
const fillPattern = ctx.createPattern(pattern, "repeat");
if (!fillPattern) {
ctx.restore();
return;
}
ctx.fillStyle = fillPattern;
ctx.fillRect(0, 0, width, height);
ctx.restore();
}

/**
* Tracks and displays the CanvasRenderingContext2D graphics state for all
* contexts created during a stepped render.
Expand Down Expand Up @@ -240,12 +190,6 @@ class CanvasContextDetailsView {
return ctx;
}
if (!wrappedCtx) {
if (
globalThis.StepperManager._active !== null &&
args[0]?.alpha !== false
) {
drawCheckerboard(ctx, canvas.width, canvas.height);
}
wrappedCtx = this.wrapContext(ctx, label);
}
return wrappedCtx;
Expand Down Expand Up @@ -284,15 +228,15 @@ class CanvasContextDetailsView {

const prevBtn = document.createElement("button");
prevBtn.className = "gfx-state-stack-button";
prevBtn.ariaLabel = "View older saved state";
prevBtn.title = "View older saved state";
prevBtn.textContent = "←";

const pos = document.createElement("span");
pos.className = "gfx-state-stack-pos";

const nextBtn = document.createElement("button");
nextBtn.className = "gfx-state-stack-button";
nextBtn.ariaLabel = "View newer saved state";
nextBtn.title = "View newer saved state";
nextBtn.textContent = "→";

navContainer.append(prevBtn, pos, nextBtn);
Expand Down
14 changes: 8 additions & 6 deletions web/internal/debugger.html
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,18 @@ <h1>PDF.js — Debugging tools</h1>
</div>
<div id="canvas-panel">
<div id="canvas-toolbar" role="toolbar" aria-label="Zoom controls">
<button id="zoom-out-button" aria-label="Zoom out">−</button>
<button id="zoom-out-button" title="Zoom out">−</button>
<span id="zoom-level" aria-live="polite"></span>
<button id="zoom-in-button" aria-label="Zoom in">+</button>
<button id="redraw-button" aria-label="Redraw page">Redraw</button>
<button id="step-button" aria-label="Step one instruction" disabled><u>S</u>tep</button>
<button id="continue-button" aria-label="Continue to next breakpoint" disabled><u>C</u>ontinue</button>
<button id="zoom-in-button" title="Zoom in">+</button>
<button id="redraw-button" title="Redraw page">Redraw</button>
<button id="step-button" title="Step one instruction" disabled><u>S</u>tep</button>
<button id="continue-button" title="Continue to next breakpoint" disabled><u>C</u>ontinue</button>
</div>
<div id="canvas-scroll">
<div id="canvas-wrapper">
<canvas id="render-canvas"></canvas>
<div class="canvas-checker">
<canvas id="render-canvas"></canvas>
</div>
<canvas id="highlight-canvas" aria-hidden="true"></canvas>
</div>
</div>
Expand Down
21 changes: 18 additions & 3 deletions web/internal/draw_ops_view.css
Original file line number Diff line number Diff line change
Expand Up @@ -159,17 +159,25 @@
&::before {
content: "●";
color: var(--changed-color);
font-size: 0.75em;
font-size: 0.9em;
opacity: 0;
}

&:hover::before {
opacity: 0.4;
}

&.active::before {
&[data-bp="pause"]::before {
opacity: 1;
}

&[data-bp="skip"]::before {
content: "✕";
opacity: 1;
}
}
.op-line.op-skipped > :not(.bp-gutter) {
opacity: 0.4;
}
.op-line.paused {
background: var(--paused-bg);
Expand All @@ -194,9 +202,16 @@
.bp-gutter:hover::before {
color: ButtonBorder;
}
.bp-gutter.active::before {
.bp-gutter[data-bp="pause"]::before {
color: ButtonText;
}
.bp-gutter[data-bp="skip"]::before {
color: ButtonText;
}
.op-line.op-skipped > :not(.bp-gutter) {
opacity: 1;
color: GrayText;
}

/* Color swatch preserves the actual PDF color value. */
.color-swatch {
Expand Down
45 changes: 32 additions & 13 deletions web/internal/draw_ops_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ for (const [name, id] of Object.entries(OPS)) {
OPS_TO_NAME[id] = name;
}

const BreakpointType = {
PAUSE: 0,
SKIP: 1,
};

// Single hidden color input reused for all swatch pickers.
const colorPickerInput = document.createElement("input");
colorPickerInput.type = "color";
Expand Down Expand Up @@ -408,7 +413,8 @@ class DrawOpsView {

#selectedLine = null;

#breakpoints = new Set();
// Map<opIndex, BreakpointType>
#breakpoints = new Map();

#originalColors = new Map();

Expand Down Expand Up @@ -553,27 +559,40 @@ class DrawOpsView {
line.ariaSelected = "false";
line.tabIndex = i === 0 ? 0 : -1;

// Breakpoint gutter — click to toggle a red-bullet breakpoint.
// Breakpoint gutter — click cycles: none → pause (●) → skip (✕) → none.
const gutter = document.createElement("span");
gutter.className = "bp-gutter";
gutter.role = "checkbox";
gutter.tabIndex = 0;
gutter.ariaLabel = "Breakpoint";
const isInitiallyActive = this.#breakpoints.has(i);
gutter.ariaChecked = String(isInitiallyActive);
if (isInitiallyActive) {
gutter.classList.add("active");
const initBpType = this.#breakpoints.get(i);
if (initBpType === BreakpointType.PAUSE) {
gutter.dataset.bp = "pause";
gutter.ariaChecked = "true";
} else if (initBpType === BreakpointType.SKIP) {
gutter.dataset.bp = "skip";
gutter.ariaChecked = "mixed";
line.classList.add("op-skipped");
} else {
gutter.ariaChecked = "false";
}
gutter.addEventListener("click", e => {
e.stopPropagation();
if (this.#breakpoints.has(i)) {
const current = this.#breakpoints.get(i);
if (current === undefined) {
this.#breakpoints.set(i, BreakpointType.PAUSE);
gutter.dataset.bp = "pause";
gutter.ariaChecked = "true";
} else if (current === BreakpointType.PAUSE) {
this.#breakpoints.set(i, BreakpointType.SKIP);
gutter.dataset.bp = "skip";
gutter.ariaChecked = "mixed";
line.classList.add("op-skipped");
} else {
this.#breakpoints.delete(i);
gutter.classList.remove("active");
delete gutter.dataset.bp;
gutter.ariaChecked = "false";
} else {
this.#breakpoints.add(i);
gutter.classList.add("active");
gutter.ariaChecked = "true";
line.classList.remove("op-skipped");
}
});
gutter.addEventListener("keydown", e => {
Expand Down Expand Up @@ -656,4 +675,4 @@ class DrawOpsView {
}
}

export { DrawOpsView };
export { BreakpointType, DrawOpsView };
9 changes: 0 additions & 9 deletions web/internal/multiline_view.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,6 @@
white-space: nowrap;
min-width: 4ch;
}

.mlc-check-label {
display: flex;
align-items: center;
gap: 3px;
font-size: 0.85em;
cursor: pointer;
white-space: nowrap;
}
}

.mlc-num-item {
Expand Down
Loading
Loading