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

- PDF annotation snapshots no longer expose mutable pending state. Pointer
options are copied, and unknown option keys leave the settings unchanged.

- PDF ink annotations retain their final pointer-up position and ignore
competing pointers until the active stroke ends.

Expand Down
29 changes: 20 additions & 9 deletions src/odr/internal/html/frontend/pdf-annotation.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@
var pending = [];
var nextId = 1;

function copyArrays(value) {
return Array.isArray(value) ? value.map(copyArrays) : value;
}

function snapshot(value) {
var copy = {};
Object.keys(value).forEach(function (key) {
copy[key] = copyArrays(value[key]);
});
return copy;
}

/// Gesture policy, the viewer's to set. `inkPointerTypes` null takes any.
var options = {
markOnSelection:
Expand Down Expand Up @@ -659,29 +671,28 @@
},
/// Merged into what is set; an unknown key throws.
setOptions: function (value) {
Object.keys(value || {}).forEach(function (key) {
var keys = Object.keys(value || {});
keys.forEach(function (key) {
if (!Object.prototype.hasOwnProperty.call(options, key)) {
throw new Error("odr.annotation: unknown option " + key);
}
options[key] = value[key];
});
keys.forEach(function (key) {
options[key] = copyArrays(value[key]);
});
applyOptions();
},
getOptions: function () {
var copy = {};
Object.keys(options).forEach(function (key) {
copy[key] = options[key];
});
return copy;
return snapshot(options);
},
/// Marks the selection with the armed tool, and answers whether anything
/// was added. The selection is left standing.
mark: function () {
return markSelection(tool, color, true);
},
/// What is pending, newest last. Geometry is in page-box points.
/// Snapshot of pending annotations, newest last, in page-box points.
list: function () {
return pending.slice();
return pending.map(snapshot);
},
remove: function (id) {
pending = pending.filter(function (a) {
Expand Down
19 changes: 17 additions & 2 deletions test/browser/annotation/tests.html
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@
const run1 = document.querySelector('[data-odr-page="0"] .sr');

const first = payload().annotations[0];
const savedHighlight = api.getAnnotations();
const highlightSnapshot = api.list()[0];
highlightSnapshot.color[0] = 0;
highlightSnapshot.boxes[0][0] = -100;
check("listed highlights do not expose pending geometry or color",
api.getAnnotations() === savedHighlight);
check("it names its page", first.page === 0, first.page);
check("it is a highlight", first.type === "highlight", first.type);
check("it has one quad of eight numbers", first.quads.length === 1 &&
Expand Down Expand Up @@ -258,6 +264,11 @@
check("another pointer does not split the stroke", api.list().length === 1);

const ink = payload().annotations[0];
const savedInk = api.getAnnotations();
const inkSnapshot = api.list()[0];
inkSnapshot.width = 100;
inkSnapshot.strokes[0].push(0, 0);
check("listed strokes do not expose pending state", api.getAnnotations() === savedInk);
check("a drag makes an ink annotation", ink && ink.type === "ink", ink);
check("its width carries", ink && ink.width === 3, ink && ink.width);
check(
Expand Down Expand Up @@ -416,15 +427,19 @@

let threw = false;
try {
api.setOptions({ nonesuch: 1 });
api.setOptions({ touchAction: "auto", nonesuch: 1 });
} catch (e) {
threw = true;
}
check("an unknown option throws rather than being ignored", threw);
check("a refused update leaves options unchanged", api.getOptions().touchAction === "none");

// --- pointer types the host does not want are not drawn with ----------
api.clear();
api.setOptions({ inkPointerTypes: ["pen"] });
const pointerTypes = ["pen"];
api.setOptions({ inkPointerTypes: pointerTypes });
pointerTypes.push("touch");
api.getOptions().inkPointerTypes.push("touch");
const withType = (type, x, y) =>
Object.assign(at(x, y), { pointerType: type, pointerId: 7 });
document.dispatchEvent(new PointerEvent("pointerdown", withType("touch", 100, 200)));
Expand Down
Loading