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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ The release run heads these entries with the version and opens a fresh

## Unreleased

- **Fix**: drawing on a pdf put the stroke about twice as far from the page's
corner as the finger went, in a webkit view that fits the page to the screen.

## v7.2.1 - 2026-09-20

- **Fix**: marking text in a pdf view left the selection one handle, because a
Expand Down
36 changes: 27 additions & 9 deletions src/odr/internal/html/frontend/pdf-annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,26 @@
return null;
}

/// A viewport point to page-box points (y-down, the unit the overlay draws
/// What an event's point is scaled by to reach the space the rects report,
/// which is the space `toBox` takes. A webkit view leaves the fit's zoom out
/// of a rect while an event carries it; a chromium one reports both alike.
/// Read once per event, not per sample: each call costs two layouts.
function layoutScale(page) {
if (!page || !odr.getViewportRect) {
return 1;
}
var drawn = odr.getViewportRect(page);
var raw = page.getBoundingClientRect();
return drawn && drawn.width && raw.width ? raw.width / drawn.width : 1;
}

/// A layout point to page-box points (y-down, the unit the overlay draws
/// in). The page box is laid out in inches, so its own layout width in css
/// pixels gives the scale a zoom transform is applied on top of.
function toBox(page, clientX, clientY) {
function toBox(page, x, y) {
var rect = page.getBoundingClientRect();
var zoom = page.offsetWidth ? rect.width / page.offsetWidth : 1;
return [
((clientX - rect.left) / zoom) * 0.75,
((clientY - rect.top) / zoom) * 0.75,
];
return [((x - rect.left) / zoom) * 0.75, ((y - rect.top) / zoom) * 0.75];
}

/// Page-box points to pdf user space, through the page's own inverse.
Expand Down Expand Up @@ -482,12 +492,15 @@
if (tool !== "ink" || event.button !== 0 || !inkTakes(event)) {
return;
}
var page = pageAt(event.clientX, event.clientY);
var scale = layoutScale(document.querySelector("[data-odr-page]"));
var x = event.clientX * scale;
var y = event.clientY * scale;
var page = pageAt(x, y);
if (!page) {
return;
}
event.preventDefault();
var p = toBox(page, event.clientX, event.clientY);
var p = toBox(page, x, y);
stroke = {
id: nextId++,
page: +page.getAttribute("data-odr-page"),
Expand Down Expand Up @@ -518,8 +531,13 @@
samples = [event];
}
var appended = false;
var scale = layoutScale(page);
for (var i = 0; i < samples.length; ++i) {
var p = toBox(page, samples[i].clientX, samples[i].clientY);
var p = toBox(
page,
samples[i].clientX * scale,
samples[i].clientY * scale,
);
// drop the sub-point jitter a pointer emits while nearly still
if (
Math.abs(p[0] - points[points.length - 2]) +
Expand Down
44 changes: 44 additions & 0 deletions test/browser/annotation/tests.html
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,50 @@
ink && ink.strokes[0].slice(0, 2)
);

// --- ink where the rects leave the fit out ----------------------------
// Chromium reports the two alike and cannot show it, so the case is made
// by standing in a getViewportRect that halves the box.
api.clear();
api.setTool("ink");
const shrink = 0.5;
const drawnRect = (element) => {
const box = element.getBoundingClientRect();
return {
left: box.left * shrink,
top: box.top * shrink,
right: box.right * shrink,
bottom: box.bottom * shrink,
width: box.width * shrink,
height: box.height * shrink,
};
};
const hadViewportRect = odr.getViewportRect;
odr.getViewportRect = drawnRect;
// the finger goes down on the drawn page, not the laid-out one
const drawn = drawnRect(page1);
const drawnZoom = drawn.width / page1.offsetWidth;
const onScreen = (bx, by) => ({
clientX: drawn.left + (bx / 0.75) * drawnZoom,
clientY: drawn.top + (by / 0.75) * drawnZoom,
bubbles: true,
pointerId: 3,
button: 0,
});
document.dispatchEvent(new PointerEvent("pointerdown", onScreen(100, 100)));
document.dispatchEvent(new PointerEvent("pointermove", onScreen(140, 140)));
document.dispatchEvent(new PointerEvent("pointerup", onScreen(140, 140)));
odr.getViewportRect = hadViewportRect;

const fitted = payload().annotations[0];
check(
"a fit the rects leave out still marks where the finger went",
fitted &&
fitted.type === "ink" &&
near(fitted.strokes[0][0], 100) &&
near(fitted.strokes[0][1], 692),
fitted && fitted.strokes[0].slice(0, 2)
);

// --- undo, remove, clear ---------------------------------------------
const before = api.list().length;
api.undo();
Expand Down
Loading