mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-20 15:18:08 +02:00
[Editor] Make ink annotation editable
This commit is contained in:
parent
97c7a8eb7a
commit
7e02c77250
15 changed files with 754 additions and 88 deletions
|
@ -504,14 +504,6 @@ describe("ResetForm action", () => {
|
|||
it("must check that the Ink annotation has a popup", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
if (browserName) {
|
||||
// TODO
|
||||
pending(
|
||||
"Re-enable this test when the Ink annotation has been made editable."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await page.waitForFunction(
|
||||
`document.querySelector("[data-annotation-id='25R']").hidden === false`
|
||||
);
|
||||
|
|
|
@ -28,6 +28,7 @@ import {
|
|||
getSelectedEditors,
|
||||
getSerialized,
|
||||
hover,
|
||||
isCanvasWhite,
|
||||
kbBigMoveDown,
|
||||
kbBigMoveLeft,
|
||||
kbBigMoveRight,
|
||||
|
@ -988,27 +989,8 @@ describe("FreeText Editor", () => {
|
|||
pages.map(async ([browserName, page]) => {
|
||||
await switchToFreeText(page);
|
||||
|
||||
const isEditorWhite = editorRect =>
|
||||
page.evaluate(rect => {
|
||||
const canvas = document.querySelector(".canvasWrapper canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
rect ||= {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
};
|
||||
const { data } = ctx.getImageData(
|
||||
rect.x,
|
||||
rect.y,
|
||||
rect.width,
|
||||
rect.height
|
||||
);
|
||||
return data.every(x => x === 0xff);
|
||||
}, editorRect);
|
||||
|
||||
// The page has been re-rendered but with no freetext annotations.
|
||||
let isWhite = await isEditorWhite();
|
||||
let isWhite = await isCanvasWhite(page, 1);
|
||||
expect(isWhite).withContext(`In ${browserName}`).toBeTrue();
|
||||
|
||||
let editorIds = await getEditors(page, "freeText");
|
||||
|
@ -1066,7 +1048,7 @@ describe("FreeText Editor", () => {
|
|||
editorIds = await getEditors(page, "freeText");
|
||||
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(1);
|
||||
|
||||
isWhite = await isEditorWhite(editorRect);
|
||||
isWhite = await isCanvasWhite(page, 1, editorRect);
|
||||
expect(isWhite).withContext(`In ${browserName}`).toBeTrue();
|
||||
|
||||
// Check we've now a div containing the text.
|
||||
|
|
|
@ -17,9 +17,14 @@ import {
|
|||
awaitPromise,
|
||||
closePages,
|
||||
createPromise,
|
||||
dragAndDropAnnotation,
|
||||
getAnnotationSelector,
|
||||
getEditors,
|
||||
getEditorSelector,
|
||||
getRect,
|
||||
getSelectedEditors,
|
||||
getSerialized,
|
||||
isCanvasWhite,
|
||||
kbRedo,
|
||||
kbSelectAll,
|
||||
kbUndo,
|
||||
|
@ -27,8 +32,10 @@ import {
|
|||
scrollIntoView,
|
||||
switchToEditor,
|
||||
waitForNoElement,
|
||||
waitForSelectedEditor,
|
||||
waitForSerialized,
|
||||
waitForStorageEntries,
|
||||
waitForTimeout,
|
||||
} from "./test_utils.mjs";
|
||||
|
||||
const waitForPointerUp = page =>
|
||||
|
@ -580,7 +587,7 @@ describe("Ink Editor", () => {
|
|||
await closePages(pages);
|
||||
});
|
||||
|
||||
it("must check that the color has been changed", async () => {
|
||||
it("must check that the deletion has been undid", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await switchToInk(page);
|
||||
|
@ -669,4 +676,157 @@ describe("Ink Editor", () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ink (update existing)", () => {
|
||||
let pages;
|
||||
|
||||
beforeAll(async () => {
|
||||
pages = await loadAndWait("inks.pdf", ".annotationEditorLayer");
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await closePages(pages);
|
||||
});
|
||||
|
||||
it("must update an existing annotation", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
const annotationsRect = await page.evaluate(() => {
|
||||
let xm = Infinity,
|
||||
xM = -Infinity,
|
||||
ym = Infinity,
|
||||
yM = -Infinity;
|
||||
for (const el of document.querySelectorAll(
|
||||
"section.inkAnnotation"
|
||||
)) {
|
||||
const { x, y, width, height } = el.getBoundingClientRect();
|
||||
xm = Math.min(xm, x);
|
||||
xM = Math.max(xM, x + width);
|
||||
ym = Math.min(ym, y);
|
||||
yM = Math.max(yM, y + height);
|
||||
}
|
||||
return { x: xm, y: ym, width: xM - xm, height: yM - ym };
|
||||
});
|
||||
|
||||
await switchToInk(page);
|
||||
|
||||
// The page has been re-rendered but with no ink annotations.
|
||||
let isWhite = await isCanvasWhite(page, 1, annotationsRect);
|
||||
expect(isWhite).withContext(`In ${browserName}`).toBeTrue();
|
||||
|
||||
let editorIds = await getEditors(page, "ink");
|
||||
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15);
|
||||
|
||||
const pdfjsA = getEditorSelector(0);
|
||||
const editorRect = await getRect(page, pdfjsA);
|
||||
await page.mouse.click(
|
||||
editorRect.x + editorRect.width / 2,
|
||||
editorRect.y + editorRect.height / 2
|
||||
);
|
||||
await waitForSelectedEditor(page, pdfjsA);
|
||||
|
||||
const red = "#ff0000";
|
||||
page.evaluate(value => {
|
||||
window.PDFViewerApplication.eventBus.dispatch(
|
||||
"switchannotationeditorparams",
|
||||
{
|
||||
source: null,
|
||||
type: window.pdfjsLib.AnnotationEditorParamsType.INK_COLOR,
|
||||
value,
|
||||
}
|
||||
);
|
||||
}, red);
|
||||
|
||||
const serialized = await getSerialized(page);
|
||||
expect(serialized.length).withContext(`In ${browserName}`).toEqual(1);
|
||||
expect(serialized[0].color).toEqual([255, 0, 0]);
|
||||
|
||||
// Disable editing mode.
|
||||
await switchToInk(page, /* disable = */ true);
|
||||
|
||||
// We want to check that the editor is displayed but not the original
|
||||
// canvas.
|
||||
editorIds = await getEditors(page, "ink");
|
||||
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(1);
|
||||
|
||||
isWhite = await isCanvasWhite(page, 1, editorRect);
|
||||
expect(isWhite).withContext(`In ${browserName}`).toBeTrue();
|
||||
|
||||
// Check we've now a svg with a red stroke.
|
||||
await page.waitForSelector("svg[stroke = '#ff0000']", {
|
||||
visible: true,
|
||||
});
|
||||
|
||||
// Re-enable editing mode.
|
||||
await switchToInk(page);
|
||||
await page.focus(".annotationEditorLayer");
|
||||
|
||||
await kbUndo(page);
|
||||
await waitForSerialized(page, 0);
|
||||
|
||||
editorIds = await getEditors(page, "ink");
|
||||
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15);
|
||||
|
||||
// Undo again.
|
||||
await kbUndo(page);
|
||||
// Nothing should happen, it's why we can't wait for something
|
||||
// specific!
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
await waitForTimeout(200);
|
||||
|
||||
// We check that the editor hasn't been removed.
|
||||
editorIds = await getEditors(page, "ink");
|
||||
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Ink (move existing)", () => {
|
||||
let pages;
|
||||
|
||||
beforeAll(async () => {
|
||||
pages = await loadAndWait("inks.pdf", getAnnotationSelector("277R"));
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await closePages(pages);
|
||||
});
|
||||
|
||||
it("must move an annotation", async () => {
|
||||
await Promise.all(
|
||||
pages.map(async ([browserName, page]) => {
|
||||
await page.click(getAnnotationSelector("277R"), { count: 2 });
|
||||
const edgeB = getEditorSelector(10);
|
||||
await waitForSelectedEditor(page, edgeB);
|
||||
|
||||
const editorIds = await getEditors(page, "ink");
|
||||
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(15);
|
||||
|
||||
// All the current annotations should be serialized as null objects
|
||||
// because they haven't been edited yet.
|
||||
const serialized = await getSerialized(page);
|
||||
expect(serialized).withContext(`In ${browserName}`).toEqual([]);
|
||||
|
||||
const editorRect = await page.$eval(edgeB, el => {
|
||||
const { x, y, width, height } = el.getBoundingClientRect();
|
||||
return { x, y, width, height };
|
||||
});
|
||||
|
||||
// Select the annotation we want to move.
|
||||
await page.mouse.click(editorRect.x + 2, editorRect.y + 2);
|
||||
await waitForSelectedEditor(page, edgeB);
|
||||
|
||||
await dragAndDropAnnotation(
|
||||
page,
|
||||
editorRect.x + editorRect.width / 2,
|
||||
editorRect.y + editorRect.height / 2,
|
||||
100,
|
||||
100
|
||||
);
|
||||
await waitForSerialized(page, 1);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -781,6 +781,28 @@ function waitForNoElement(page, selector) {
|
|||
);
|
||||
}
|
||||
|
||||
function isCanvasWhite(page, pageNumber, rectangle) {
|
||||
return page.evaluate(
|
||||
(rect, pageN) => {
|
||||
const canvas = document.querySelector(
|
||||
`.page[data-page-number = "${pageN}"] .canvasWrapper canvas`
|
||||
);
|
||||
const canvasRect = canvas.getBoundingClientRect();
|
||||
const ctx = canvas.getContext("2d");
|
||||
rect ||= canvasRect;
|
||||
const { data } = ctx.getImageData(
|
||||
rect.x - canvasRect.x,
|
||||
rect.y - canvasRect.y,
|
||||
rect.width,
|
||||
rect.height
|
||||
);
|
||||
return new Uint32Array(data.buffer).every(x => x === 0xffffffff);
|
||||
},
|
||||
rectangle,
|
||||
pageNumber
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
applyFunctionToEditor,
|
||||
awaitPromise,
|
||||
|
@ -806,6 +828,7 @@ export {
|
|||
getSerialized,
|
||||
getSpanRectFromText,
|
||||
hover,
|
||||
isCanvasWhite,
|
||||
isVisible,
|
||||
kbBigMoveDown,
|
||||
kbBigMoveLeft,
|
||||
|
|
2
test/pdfs/.gitignore
vendored
2
test/pdfs/.gitignore
vendored
|
@ -685,3 +685,5 @@
|
|||
!issue19120.pdf
|
||||
!bug1934157.pdf
|
||||
!rotated_ink.pdf
|
||||
!inks.pdf
|
||||
!inks_basic.pdf
|
||||
|
|
BIN
test/pdfs/inks.pdf
Normal file
BIN
test/pdfs/inks.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/inks_basic.pdf
Normal file
BIN
test/pdfs/inks_basic.pdf
Normal file
Binary file not shown.
|
@ -10872,5 +10872,334 @@
|
|||
"talos": false,
|
||||
"type": "eq",
|
||||
"link": true
|
||||
},
|
||||
{
|
||||
"id": "inks_basic-editor-save-print",
|
||||
"file": "pdfs/inks_basic.pdf",
|
||||
"md5": "2615610de59b4849993dcc2614ebb266",
|
||||
"rounds": 1,
|
||||
"lastPage": 1,
|
||||
"type": "eq",
|
||||
"save": true,
|
||||
"print": true,
|
||||
"annotationStorage": {
|
||||
"pdfjs_internal_editor_0": {
|
||||
"annotationType": 15,
|
||||
"color": [241, 17, 41],
|
||||
"opacity": 1,
|
||||
"thickness": 20,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
114,
|
||||
691.5,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
159.75,
|
||||
631.5
|
||||
]
|
||||
],
|
||||
"points": [[114, 691.5, 159.75, 631.5]]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
104.0000076523194, 621.5000049701105, 169.7500193485847,
|
||||
701.5000020036331
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "16R"
|
||||
},
|
||||
"pdfjs_internal_editor_1": {
|
||||
"id": "17R",
|
||||
"deleted": true,
|
||||
"pageIndex": 0,
|
||||
"popupRef": ""
|
||||
},
|
||||
"pdfjs_internal_editor_2": {
|
||||
"annotationType": 15,
|
||||
"color": [0, 0, 0],
|
||||
"opacity": 1,
|
||||
"thickness": 3,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
475.0961608886719,
|
||||
647.4615478515625,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
520.8461303710938,
|
||||
587.4615478515625
|
||||
]
|
||||
],
|
||||
"points": [
|
||||
[
|
||||
475.0961608886719, 647.4615478515625, 520.8461303710938,
|
||||
587.4615478515625
|
||||
]
|
||||
]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
473.59618278650134, 585.9615278473268, 522.3461772570244,
|
||||
648.9615135559669
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "18R"
|
||||
},
|
||||
"pdfjs_internal_editor_3": {
|
||||
"annotationType": 15,
|
||||
"color": [250, 23, 28],
|
||||
"opacity": 0.55,
|
||||
"thickness": 3,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
79.04861450195312,
|
||||
532.353759765625,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
198.18736267089844,
|
||||
378.6045837402344
|
||||
]
|
||||
],
|
||||
"points": [
|
||||
[
|
||||
79.04861450195312, 532.353759765625, 198.18736267089844,
|
||||
378.6045837402344
|
||||
]
|
||||
]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
77.54861622361037, 377.10462435392236, 199.6873893210521,
|
||||
533.8537948498359
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "19R"
|
||||
},
|
||||
"pdfjs_internal_editor_4": {
|
||||
"annotationType": 15,
|
||||
"color": [70, 108, 241],
|
||||
"opacity": 0.7,
|
||||
"thickness": 20,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
273.6300048828125,
|
||||
535.47998046875,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
319.3800048828125,
|
||||
475.4800109863281
|
||||
]
|
||||
],
|
||||
"points": [
|
||||
[
|
||||
273.6300048828125, 535.47998046875, 319.3800048828125,
|
||||
475.4800109863281
|
||||
]
|
||||
]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
263.629992209948, 465.48002000955444, 329.38000390621335,
|
||||
545.4799757370582
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "20R"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "inks_basic-editor-print",
|
||||
"file": "pdfs/inks_basic.pdf",
|
||||
"md5": "2615610de59b4849993dcc2614ebb266",
|
||||
"rounds": 1,
|
||||
"lastPage": 1,
|
||||
"type": "eq",
|
||||
"print": true,
|
||||
"annotationStorage": {
|
||||
"pdfjs_internal_editor_0": {
|
||||
"annotationType": 15,
|
||||
"color": [241, 17, 41],
|
||||
"opacity": 1,
|
||||
"thickness": 20,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
114,
|
||||
691.5,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
159.75,
|
||||
631.5
|
||||
]
|
||||
],
|
||||
"points": [[114, 691.5, 159.75, 631.5]]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
104.0000076523194, 621.5000049701105, 169.7500193485847,
|
||||
701.5000020036331
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "16R"
|
||||
},
|
||||
"pdfjs_internal_editor_1": {
|
||||
"id": "17R",
|
||||
"deleted": true,
|
||||
"pageIndex": 0,
|
||||
"popupRef": ""
|
||||
},
|
||||
"pdfjs_internal_editor_2": {
|
||||
"annotationType": 15,
|
||||
"color": [0, 0, 0],
|
||||
"opacity": 1,
|
||||
"thickness": 3,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
475.0961608886719,
|
||||
647.4615478515625,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
520.8461303710938,
|
||||
587.4615478515625
|
||||
]
|
||||
],
|
||||
"points": [
|
||||
[
|
||||
475.0961608886719, 647.4615478515625, 520.8461303710938,
|
||||
587.4615478515625
|
||||
]
|
||||
]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
473.59618278650134, 585.9615278473268, 522.3461772570244,
|
||||
648.9615135559669
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "18R"
|
||||
},
|
||||
"pdfjs_internal_editor_3": {
|
||||
"annotationType": 15,
|
||||
"color": [250, 23, 28],
|
||||
"opacity": 0.55,
|
||||
"thickness": 3,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
79.04861450195312,
|
||||
532.353759765625,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
198.18736267089844,
|
||||
378.6045837402344
|
||||
]
|
||||
],
|
||||
"points": [
|
||||
[
|
||||
79.04861450195312, 532.353759765625, 198.18736267089844,
|
||||
378.6045837402344
|
||||
]
|
||||
]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
77.54861622361037, 377.10462435392236, 199.6873893210521,
|
||||
533.8537948498359
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "19R"
|
||||
},
|
||||
"pdfjs_internal_editor_4": {
|
||||
"annotationType": 15,
|
||||
"color": [70, 108, 241],
|
||||
"opacity": 0.7,
|
||||
"thickness": 20,
|
||||
"paths": {
|
||||
"lines": [
|
||||
[
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
273.6300048828125,
|
||||
535.47998046875,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
319.3800048828125,
|
||||
475.4800109863281
|
||||
]
|
||||
],
|
||||
"points": [
|
||||
[
|
||||
273.6300048828125, 535.47998046875, 319.3800048828125,
|
||||
475.4800109863281
|
||||
]
|
||||
]
|
||||
},
|
||||
"pageIndex": 0,
|
||||
"rect": [
|
||||
263.629992209948, 465.48002000955444, 329.38000390621335,
|
||||
545.4799757370582
|
||||
],
|
||||
"rotation": 0,
|
||||
"structTreeParentId": null,
|
||||
"id": "20R"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue