Skip to content

Commit e577141

Browse files
authored
Merge pull request #20906 from calixteman/debugger_text
Add the possibility to debug only text rendering by filtering the op list.
2 parents ab228da + cf3b3fa commit e577141

11 files changed

Lines changed: 1001 additions & 60 deletions

src/display/canvas.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,9 @@ class CanvasGraphics {
792792
return i;
793793
}
794794
if (stepper.shouldSkip(i)) {
795-
i++;
795+
if (++i === argsArrayLen) {
796+
return i;
797+
}
796798
continue;
797799
}
798800
}

web/internal/canvas_context_details_view.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,18 @@ class CanvasContextDetailsView {
8686
// Map<label, {container, prevBtn, pos, nextBtn}> — stack-nav DOM elements.
8787
#gfxStateNavElements = new Map();
8888

89+
// When true, suppress live DOM updates; build() re-reads state and resets it.
90+
#frozen = false;
91+
8992
constructor(panelEl) {
9093
this.#panel = panelEl;
9194
}
9295

96+
/** Suppress live DOM updates until the next build() call. */
97+
freeze() {
98+
this.#frozen = true;
99+
}
100+
93101
/**
94102
* Wrap a CanvasRenderingContext2D to track its graphics state.
95103
* Returns a Proxy that keeps internal state in sync and updates the DOM.
@@ -202,6 +210,7 @@ class CanvasContextDetailsView {
202210
* Shows the panel if it was hidden.
203211
*/
204212
build() {
213+
this.#frozen = false;
205214
this.#panel.hidden = false;
206215
this.#panel.replaceChildren();
207216
this.#gfxStateValueElements.clear();
@@ -361,10 +370,10 @@ class CanvasContextDetailsView {
361370
}
362371
}
363372

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

389398
// Sync the stack-nav button states and position counter for a context.
390399
#updateStackNav(label) {
400+
if (this.#frozen) {
401+
return;
402+
}
391403
const nav = this.#gfxStateNavElements.get(label);
392404
if (!nav) {
393405
return;

web/internal/debugger.css

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
@import url(canvas_context_details_view.css);
1717
@import url(draw_ops_view.css);
18+
@import url(font_view.css);
1819
@import url(multiline_view.css);
1920
@import url(page_view.css);
2021
@import url(split_view.css);
@@ -35,6 +36,7 @@
3536
--text-color: light-dark(#1e1e1e, #d4d4d4);
3637
--muted-color: light-dark(#6e6e6e, #888);
3738
--accent-color: light-dark(#0070c1, #9cdcfe);
39+
--accent-fg: light-dark(white, #1e1e1e);
3840

3941
/* Borders */
4042
--border-color: light-dark(#e0e0e0, #3c3c3c);
@@ -285,8 +287,8 @@ body:has(#debug-view:not([hidden])) {
285287
color: var(--muted-color);
286288
font-style: italic;
287289
}
288-
#debug-button,
289-
#debug-back-button {
290+
#tree-button,
291+
#debug-button {
290292
padding: 4px 12px;
291293
border-radius: 3px;
292294
border: 1px solid var(--input-border-color);
@@ -299,4 +301,9 @@ body:has(#debug-view:not([hidden])) {
299301
&:hover {
300302
background: var(--button-hover-bg);
301303
}
304+
&:disabled {
305+
opacity: 0.4;
306+
cursor: default;
307+
pointer-events: none;
308+
}
302309
}

web/internal/debugger.html

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<meta name="viewport" content="width=device-width, initial-scale=1" />
2121
<title>PDF.js — Debugging tools</title>
2222
<link rel="stylesheet" href="debugger.css" />
23+
<link rel="stylesheet" href="../text_layer_builder.css" />
2324
</head>
2425
<body>
2526
<div id="header">
@@ -34,8 +35,8 @@ <h1>PDF.js — Debugging tools</h1>
3435
<span id="goto-input-hint" class="sr-only">
3536
Enter a page number (e.g. 5), a reference as numR (e.g. 10R) or numRgen (e.g. 10R2). Press Enter to navigate.
3637
</span>
37-
<button id="debug-button" hidden>Debug page</button>
38-
<button id="debug-back-button" hidden>← Back to tree</button>
38+
<button id="tree-button">Tree</button>
39+
<button id="debug-button" disabled>Debug</button>
3940
<span id="status" role="status" aria-live="polite"> Select a PDF file to explore its internal structure. </span>
4041
<a id="github-link" href="https://github.com/mozilla/pdf.js" target="_blank" rel="noopener noreferrer" aria-label="PDF.js on GitHub">
4142
<svg viewBox="0 0 16 16" aria-hidden="true" focusable="false">
@@ -55,14 +56,41 @@ <h1>PDF.js — Debugging tools</h1>
5556
<div id="op-detail-panel"></div>
5657
<div id="gfx-state-panel" aria-label="Graphics state" hidden></div>
5758
</div>
59+
<div id="font-panel" hidden></div>
5860
<div id="canvas-panel">
5961
<div id="canvas-toolbar" role="toolbar" aria-label="Zoom controls">
60-
<button id="zoom-out-button" title="Zoom out"></button>
61-
<span id="zoom-level" aria-live="polite"></span>
62-
<button id="zoom-in-button" title="Zoom in">+</button>
63-
<button id="redraw-button" title="Redraw page">Redraw</button>
64-
<button id="step-button" title="Step one instruction" disabled><u>S</u>tep</button>
65-
<button id="continue-button" title="Continue to next breakpoint" disabled><u>C</u>ontinue</button>
62+
<div class="toolbar-left">
63+
<button id="text-filter-button" title="Show only text drawing operations" aria-pressed="false">T</button>
64+
<button id="text-layer-color-button" title="Text layer color">
65+
<span id="text-layer-color-swatch"></span>
66+
</button>
67+
<input type="color" id="text-layer-color-input" hidden />
68+
<button id="text-span-border-button" title="Show span borders" aria-pressed="false">
69+
<svg
70+
width="18"
71+
height="14"
72+
viewBox="0 0 18 14"
73+
fill="none"
74+
stroke="currentColor"
75+
stroke-width="1.5"
76+
stroke-dasharray="2 1.5"
77+
aria-hidden="true"
78+
>
79+
<rect x="0.75" y="0.75" width="16.5" height="12.5" />
80+
</svg>
81+
</button>
82+
<button id="font-view-button" title="Show fonts used on page" aria-pressed="false">F</button>
83+
</div>
84+
<div class="toolbar-center">
85+
<button id="zoom-out-button" title="Zoom out"></button>
86+
<span id="zoom-level" aria-live="polite"></span>
87+
<button id="zoom-in-button" title="Zoom in">+</button>
88+
</div>
89+
<div class="toolbar-right">
90+
<button id="redraw-button" title="Redraw page">Redraw</button>
91+
<button id="step-button" title="Step one instruction" disabled><u>S</u>tep</button>
92+
<button id="continue-button" title="Continue to next breakpoint" disabled><u>C</u>ontinue</button>
93+
</div>
6694
</div>
6795
<div id="canvas-scroll">
6896
<div id="canvas-wrapper">

web/internal/debugger.js

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ function markLoading(delta) {
6767
}
6868

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

8282
async function loadTree(data, rootLabel = null) {
8383
currentPage = typeof data.page === "number" ? data.page : null;
84-
debugButton.hidden = currentPage === null;
85-
debugBackButton.hidden = true;
84+
debugButton.disabled = currentPage === null;
8685
pageView.reset();
8786
debugViewEl.hidden = true;
8887
treeEl.hidden = false;
@@ -113,6 +112,7 @@ async function openDocument(source, name) {
113112
wasmUrl: "../web/wasm/",
114113
useWorkerFetch: true,
115114
pdfBug: true,
115+
fontExtraProperties: true,
116116
CanvasFactory: pageView.DebugCanvasFactory,
117117
});
118118
loadingTask.onPassword = (updateCallback, reason) => {
@@ -230,9 +230,17 @@ gotoInput.addEventListener("keydown", async ({ key, target }) => {
230230
return;
231231
}
232232
target.removeAttribute("aria-invalid");
233-
await (result.page !== undefined
234-
? loadTree({ page: result.page })
235-
: loadTree({ ref: result.ref }));
233+
// If we're in debug view and navigating to a page, stay in debug view
234+
// without switching to the tree at all.
235+
if (!debugViewEl.hidden && result.page !== undefined) {
236+
currentPage = result.page;
237+
pageView.reset();
238+
await pageView.show(pdfDoc, currentPage);
239+
} else {
240+
await (result.page !== undefined
241+
? loadTree({ page: result.page })
242+
: loadTree({ ref: result.ref }));
243+
}
236244
});
237245

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

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

252-
debugBackButton.addEventListener("click", () => {
253-
debugBackButton.hidden = debugViewEl.hidden = true;
254-
debugButton.hidden = treeEl.hidden = false;
260+
treeButton.addEventListener("click", () => {
261+
debugViewEl.hidden = true;
262+
treeEl.hidden = false;
255263
});

0 commit comments

Comments
 (0)