Skip to content

Commit

Permalink
Hook up the ops bbox logic to the pdf debugger
Browse files Browse the repository at this point in the history
When using the pdf debugger, when hovering over a step now:
- it highlights the steps in the same groups
- it highlights the steps that they depend on
- it highlights on the PDF itself the bounding box
  • Loading branch information
nicolo-ribaudo committed Nov 14, 2024
1 parent e507c36 commit 355758e
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 3 deletions.
16 changes: 15 additions & 1 deletion src/display/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
NodeStandardFontDataFactory,
} from "display-node_utils";
import { CanvasGraphics } from "./canvas.js";
import { CanvasRecorder } from "./canvas_recorder.js";
import { DOMCanvasFactory } from "./canvas_factory.js";
import { DOMCMapReaderFactory } from "display-cmap_reader_factory";
import { DOMFilterFactory } from "./filter_factory.js";
Expand Down Expand Up @@ -1517,9 +1518,22 @@ class PDFPageProxy {
this._pumpOperatorList(intentArgs);
}

const recordingContext =
this._pdfBug &&
globalThis.StepperManager?.enabled &&
!this._recordedGroups
? new CanvasRecorder(canvasContext)
: null;

const complete = error => {
intentState.renderTasks.delete(internalRenderTask);

if (recordingContext) {
this._recordedGroups =
CanvasRecorder.getFinishedGroups(recordingContext);
internalRenderTask.stepper.setOperatorGroups(this._recordedGroups);
}

// Attempt to reduce memory usage during *printing*, by always running
// cleanup immediately once rendering has finished.
if (this._maybeCleanupAfterRender || intentPrint) {
Expand Down Expand Up @@ -1552,7 +1566,7 @@ class PDFPageProxy {
callback: complete,
// Only include the required properties, and *not* the entire object.
params: {
canvasContext,
canvasContext: recordingContext ?? canvasContext,
viewport,
transform,
background,
Expand Down
34 changes: 34 additions & 0 deletions web/debugger.css
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,37 @@
background-color: rgb(255 255 255 / 0.6);
color: rgb(0 0 0);
}

.pdfBugGroupsLayer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;

> * {
position: absolute;
outline-color: red;
outline-width: 2px;

--hover-outline-style: solid !important;
--hover-background-color: rgba(255, 0, 0, 0.2);

&:hover {
outline-style: var(--hover-outline-style);
background-color: var(--hover-background-color);
cursor: pointer;
}

.showDebugBoxes & {
outline-style: dashed;
}
}
}

.showDebugBoxes {
.pdfBugGroupsLayer {
pointer-events: all;
}
}
140 changes: 138 additions & 2 deletions web/debugger.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ const StepperManager = (function StepperManagerClosure() {
active: false,
// Stepper specific functions.
create(pageIndex) {
const pageContainer = document.querySelector(
`#viewer div[data-page-number="${pageIndex + 1}"]`
);

const debug = document.createElement("div");
debug.id = "stepper" + pageIndex;
debug.hidden = true;
Expand All @@ -210,7 +214,12 @@ const StepperManager = (function StepperManagerClosure() {
b.value = pageIndex;
stepperChooser.append(b);
const initBreakPoints = breakPoints[pageIndex] || [];
const stepper = new Stepper(debug, pageIndex, initBreakPoints);
const stepper = new Stepper(
debug,
pageIndex,
initBreakPoints,
pageContainer
);
steppers.push(stepper);
if (steppers.length === 1) {
this.selectStepper(pageIndex, false);
Expand Down Expand Up @@ -277,7 +286,7 @@ class Stepper {
return simpleObj;
}

constructor(panel, pageIndex, initialBreakPoints) {
constructor(panel, pageIndex, initialBreakPoints, pageContainer) {
this.panel = panel;
this.breakPoint = 0;
this.nextBreakPoint = null;
Expand All @@ -286,11 +295,20 @@ class Stepper {
this.currentIdx = -1;
this.operatorListIdx = 0;
this.indentLevel = 0;
this.operatorGroups = null;
this.pageContainer = pageContainer;
}

init(operatorList) {
const panel = this.panel;
const content = this.#c("div", "c=continue, s=step");

const showBoxesToggle = this.#c("label", "Show bounding boxes");
const showBoxesCheckbox = this.#c("input");
showBoxesCheckbox.type = "checkbox";
showBoxesToggle.prepend(showBoxesCheckbox);
content.append(this.#c("br"), showBoxesToggle);

const table = this.#c("table");
content.append(table);
table.cellSpacing = 0;
Expand All @@ -305,6 +323,22 @@ class Stepper {
panel.append(content);
this.table = table;
this.updateOperatorList(operatorList);

const hoverStyle = this.#c("style");
this.hoverStyle = hoverStyle;
content.prepend(hoverStyle);
table.addEventListener("mouseover", this.#handleStepHover.bind(this));
table.addEventListener("mouseleave", e => {
hoverStyle.innerText = "";
});

showBoxesCheckbox.addEventListener("change", () => {
if (showBoxesCheckbox.checked) {
this.pageContainer.classList.add("showDebugBoxes");
} else {
this.pageContainer.classList.remove("showDebugBoxes");
}
});
}

updateOperatorList(operatorList) {
Expand Down Expand Up @@ -397,6 +431,108 @@ class Stepper {
this.table.append(chunk);
}

setOperatorGroups(groups) {
this.operatorGroups = groups;

let boxesContainer = this.pageContainer.querySelector(".pdfBugGroupsLayer");
if (!boxesContainer) {
boxesContainer = this.#c("div");
boxesContainer.classList.add("pdfBugGroupsLayer");
this.pageContainer.append(boxesContainer);

boxesContainer.addEventListener(
"click",
this.#handleDebugBoxClick.bind(this)
);
boxesContainer.addEventListener(
"mouseover",
this.#handleDebugBoxHover.bind(this)
);
}
boxesContainer.innerHTML = "";

for (let i = 0; i < groups.length; i++) {
const el = this.#c("div");
el.style.left = `${groups[i].minX * 100}%`;
el.style.top = `${groups[i].minY * 100}%`;
el.style.width = `${(groups[i].maxX - groups[i].minX) * 100}%`;
el.style.height = `${(groups[i].maxY - groups[i].minY) * 100}%`;
el.dataset.groupIdx = i;
boxesContainer.append(el);
}
}

#handleStepHover(e) {
const tr = e.target.closest("tr");
if (!tr || tr.dataset.idx === undefined) {
return;
}

const index = +tr.dataset.idx;

const closestGroupIndex =
this.operatorGroups?.findIndex(({ data }) => {
if ("idx" in data) {
return data.idx === index;
}
if ("startIdx" in data) {
return data.startIdx <= index && index <= data.endIdx;
}
return false;
}) ?? -1;
if (closestGroupIndex === -1) {
this.hoverStyle.innerText = "";
return;
}

this.#highlightStepsGroup(closestGroupIndex);
}

#handleDebugBoxHover(e) {
if (e.target.dataset.groupIdx === undefined) return;

Check failure on line 492 in web/debugger.mjs

View workflow job for this annotation

GitHub Actions / Lint (lts/*)

Expected { after 'if' condition

const groupIdx = Number(e.target.dataset.groupIdx);
this.#highlightStepsGroup(groupIdx);
}

#handleDebugBoxClick(e) {
if (e.target.dataset.groupIdx === undefined) return;

Check failure on line 499 in web/debugger.mjs

View workflow job for this annotation

GitHub Actions / Lint (lts/*)

Expected { after 'if' condition

const groupIdx = Number(e.target.dataset.groupIdx);
const group = this.operatorGroups[groupIdx];

const firstOp = "idx" in group.data ? group.data.idx : group.data.startIdx;

this.table.childNodes[firstOp].scrollIntoView();
}

#highlightStepsGroup(groupIndex) {
const group = this.operatorGroups[groupIndex];

let cssSelector;
if ("idx" in group.data) {
cssSelector = `tr[data-idx="${group.data.idx}"]`;
} else if ("startIdx" in group.data) {
cssSelector = `:nth-child(n+${group.data.startIdx + 1} of tr[data-idx]):nth-child(-n+${group.data.endIdx + 1} of tr[data-idx])`;
}

this.hoverStyle.innerText = `#${this.panel.id} ${cssSelector} { background-color: rgba(0, 0, 0, 0.1); }`;

if (group.data.dependencies) {
const selector = group.data.dependencies
.map(idx => `#${this.panel.id} tr[data-idx="${idx}"]`)
.join(", ");
this.hoverStyle.innerText += `${selector} { background-color: rgba(0, 255, 255, 0.1); }`;
}

this.hoverStyle.innerText += `
#viewer [data-page-number="${this.pageIndex + 1}"] .pdfBugGroupsLayer :nth-child(${groupIndex + 1}) {
background-color: var(--hover-background-color);
outline-style: var(--hover-outline-style);
}
`;
}

getNextBreakPoint() {
this.breakPoints.sort(function (a, b) {
return a - b;
Expand Down
5 changes: 5 additions & 0 deletions web/pdf_page_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,8 @@ class PDFPageView {
keepXfaLayer = false,
keepTextLayer = false,
} = {}) {
const keepPdfBugGroups = this.pdfPage?._pdfBug ?? false;

this.cancelRendering({
keepAnnotationLayer,
keepAnnotationEditorLayer,
Expand Down Expand Up @@ -564,6 +566,9 @@ class PDFPageView {
case textLayerNode:
continue;
}
if (keepPdfBugGroups && node.classList.contains("pdfBugGroupsLayer")) {
continue;
}
node.remove();
const layerIndex = this.#layers.indexOf(node);
if (layerIndex >= 0) {
Expand Down

0 comments on commit 355758e

Please sign in to comment.