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
4 changes: 3 additions & 1 deletion src/display/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,9 @@ class CanvasGraphics {
return i;
}
if (stepper.shouldSkip(i)) {
i++;
if (++i === argsArrayLen) {
return i;
}
continue;
}
}
Expand Down
18 changes: 15 additions & 3 deletions web/internal/canvas_context_details_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,18 @@ class CanvasContextDetailsView {
// Map<label, {container, prevBtn, pos, nextBtn}> — stack-nav DOM elements.
#gfxStateNavElements = new Map();

// When true, suppress live DOM updates; build() re-reads state and resets it.
#frozen = false;

constructor(panelEl) {
this.#panel = panelEl;
}

/** Suppress live DOM updates until the next build() call. */
freeze() {
this.#frozen = true;
}

/**
* Wrap a CanvasRenderingContext2D to track its graphics state.
* Returns a Proxy that keeps internal state in sync and updates the DOM.
Expand Down Expand Up @@ -202,6 +210,7 @@ class CanvasContextDetailsView {
* Shows the panel if it was hidden.
*/
build() {
this.#frozen = false;
this.#panel.hidden = false;
this.#panel.replaceChildren();
this.#gfxStateValueElements.clear();
Expand Down Expand Up @@ -361,10 +370,10 @@ class CanvasContextDetailsView {
}
}

// Update DOM for a live setter — skipped when the user is browsing a saved
// state so that live updates don't overwrite the frozen view.
// Update DOM for a live setter — skipped when frozen (continuous execution)
// or when the user is browsing a saved state.
#updatePropEl(label, prop, value) {
if (this.#ctxStackViewIdx.get(label) !== null) {
if (this.#frozen || this.#ctxStackViewIdx.get(label) !== null) {
return;
}
this.#applyPropEl(label, prop, value);
Expand All @@ -388,6 +397,9 @@ class CanvasContextDetailsView {

// Sync the stack-nav button states and position counter for a context.
#updateStackNav(label) {
if (this.#frozen) {
return;
}
const nav = this.#gfxStateNavElements.get(label);
if (!nav) {
return;
Expand Down
11 changes: 9 additions & 2 deletions web/internal/debugger.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

@import url(canvas_context_details_view.css);
@import url(draw_ops_view.css);
@import url(font_view.css);
@import url(multiline_view.css);
@import url(page_view.css);
@import url(split_view.css);
Expand All @@ -35,6 +36,7 @@
--text-color: light-dark(#1e1e1e, #d4d4d4);
--muted-color: light-dark(#6e6e6e, #888);
--accent-color: light-dark(#0070c1, #9cdcfe);
--accent-fg: light-dark(white, #1e1e1e);

/* Borders */
--border-color: light-dark(#e0e0e0, #3c3c3c);
Expand Down Expand Up @@ -285,8 +287,8 @@ body:has(#debug-view:not([hidden])) {
color: var(--muted-color);
font-style: italic;
}
#debug-button,
#debug-back-button {
#tree-button,
#debug-button {
padding: 4px 12px;
border-radius: 3px;
border: 1px solid var(--input-border-color);
Expand All @@ -299,4 +301,9 @@ body:has(#debug-view:not([hidden])) {
&:hover {
background: var(--button-hover-bg);
}
&:disabled {
opacity: 0.4;
cursor: default;
pointer-events: none;
}
}
44 changes: 36 additions & 8 deletions web/internal/debugger.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>PDF.js — Debugging tools</title>
<link rel="stylesheet" href="debugger.css" />
<link rel="stylesheet" href="../text_layer_builder.css" />
</head>
<body>
<div id="header">
Expand All @@ -34,8 +35,8 @@ <h1>PDF.js — Debugging tools</h1>
<span id="goto-input-hint" class="sr-only">
Enter a page number (e.g. 5), a reference as numR (e.g. 10R) or numRgen (e.g. 10R2). Press Enter to navigate.
</span>
<button id="debug-button" hidden>Debug page</button>
<button id="debug-back-button" hidden>← Back to tree</button>
<button id="tree-button">Tree</button>
<button id="debug-button" disabled>Debug</button>
<span id="status" role="status" aria-live="polite"> Select a PDF file to explore its internal structure. </span>
<a id="github-link" href="https://github.com/mozilla/pdf.js" target="_blank" rel="noopener noreferrer" aria-label="PDF.js on GitHub">
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false">
Expand All @@ -55,14 +56,41 @@ <h1>PDF.js — Debugging tools</h1>
<div id="op-detail-panel"></div>
<div id="gfx-state-panel" aria-label="Graphics state" hidden></div>
</div>
<div id="font-panel" hidden></div>
<div id="canvas-panel">
<div id="canvas-toolbar" role="toolbar" aria-label="Zoom controls">
<button id="zoom-out-button" title="Zoom out">−</button>
<span id="zoom-level" aria-live="polite"></span>
<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 class="toolbar-left">
<button id="text-filter-button" title="Show only text drawing operations" aria-pressed="false">T</button>
<button id="text-layer-color-button" title="Text layer color">
<span id="text-layer-color-swatch"></span>
</button>
<input type="color" id="text-layer-color-input" hidden />
<button id="text-span-border-button" title="Show span borders" aria-pressed="false">
<svg
width="18"
height="14"
viewBox="0 0 18 14"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-dasharray="2 1.5"
aria-hidden="true"
>
<rect x="0.75" y="0.75" width="16.5" height="12.5" />
</svg>
</button>
<button id="font-view-button" title="Show fonts used on page" aria-pressed="false">F</button>
</div>
<div class="toolbar-center">
<button id="zoom-out-button" title="Zoom out">−</button>
<span id="zoom-level" aria-live="polite"></span>
<button id="zoom-in-button" title="Zoom in">+</button>
</div>
<div class="toolbar-right">
<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>
<div id="canvas-scroll">
<div id="canvas-wrapper">
Expand Down
32 changes: 20 additions & 12 deletions web/internal/debugger.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ function markLoading(delta) {
}

// Cache frequently accessed elements.
const treeButton = document.getElementById("tree-button");
const debugButton = document.getElementById("debug-button");
const debugBackButton = document.getElementById("debug-back-button");
const debugViewEl = document.getElementById("debug-view");
const treeEl = document.getElementById("tree");
const statusEl = document.getElementById("status");
Expand All @@ -81,8 +81,7 @@ const treeView = new TreeView(treeEl, { onMarkLoading: markLoading });

async function loadTree(data, rootLabel = null) {
currentPage = typeof data.page === "number" ? data.page : null;
debugButton.hidden = currentPage === null;
debugBackButton.hidden = true;
debugButton.disabled = currentPage === null;
pageView.reset();
debugViewEl.hidden = true;
treeEl.hidden = false;
Expand Down Expand Up @@ -113,6 +112,7 @@ async function openDocument(source, name) {
wasmUrl: "../web/wasm/",
useWorkerFetch: true,
pdfBug: true,
fontExtraProperties: true,
CanvasFactory: pageView.DebugCanvasFactory,
});
loadingTask.onPassword = (updateCallback, reason) => {
Expand Down Expand Up @@ -230,9 +230,17 @@ gotoInput.addEventListener("keydown", async ({ key, target }) => {
return;
}
target.removeAttribute("aria-invalid");
await (result.page !== undefined
? loadTree({ page: result.page })
: loadTree({ ref: result.ref }));
// If we're in debug view and navigating to a page, stay in debug view
// without switching to the tree at all.
if (!debugViewEl.hidden && result.page !== undefined) {
currentPage = result.page;
pageView.reset();
await pageView.show(pdfDoc, currentPage);
} else {
await (result.page !== undefined
? loadTree({ page: result.page })
: loadTree({ ref: result.ref }));
}
});

gotoInput.addEventListener("input", ({ target }) => {
Expand All @@ -242,14 +250,14 @@ gotoInput.addEventListener("input", ({ target }) => {
});

debugButton.addEventListener("click", async () => {
debugButton.hidden = treeEl.hidden = true;
debugBackButton.hidden = debugViewEl.hidden = false;
treeEl.hidden = true;
debugViewEl.hidden = false;
// Only render if not already loaded for this page; re-entering from the
// back button keeps the existing debug state (op-list, canvas, breakpoints).
// tree button keeps the existing debug state (op-list, canvas, breakpoints).
await pageView.show(pdfDoc, currentPage);
});

debugBackButton.addEventListener("click", () => {
debugBackButton.hidden = debugViewEl.hidden = true;
debugButton.hidden = treeEl.hidden = false;
treeButton.addEventListener("click", () => {
debugViewEl.hidden = true;
treeEl.hidden = false;
});
Loading
Loading