From 4f0bfabe7a095fc6e2fb2f3fed1103999511aa95 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Wed, 7 Dec 2022 18:27:32 +0100 Subject: [PATCH] Take all the viewBox into account when computing the coordinates of an annotation in the page (fixes #15789) --- src/display/editor/editor.js | 46 ++++++---- test/integration/freetext_editor_spec.js | 103 +++++++++++++++++++---- test/pdfs/.gitignore | 1 + test/pdfs/issue15789.pdf | Bin 0 -> 4920 bytes 4 files changed, 115 insertions(+), 35 deletions(-) create mode 100755 test/pdfs/issue15789.pdf diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index 19e43579b9cd0..5c9feff64c3e3 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -68,8 +68,17 @@ class AnnotationEditor { this.div = null; this._uiManager = parameters.uiManager; - this.rotation = this.parent.viewport.rotation; - this.pageDimensions = this.parent.pageDimensions; + const { + rotation, + viewBox: [pageLLx, pageLLy, pageURx, pageURy], + } = this.parent.viewport; + this.rotation = rotation; + const pageWidth = pageURx - pageLLx; + const pageHeight = pageURy - pageLLy; + + this.pageDimensions = [pageWidth, pageHeight]; + this.pageTranslation = [pageLLx, pageLLy]; + const [width, height] = this.parentDimensions; this.x = parameters.x / width; this.y = parameters.y / height; @@ -341,6 +350,7 @@ class AnnotationEditor { getRect(tx, ty) { const scale = this.parentScale; const [pageWidth, pageHeight] = this.pageDimensions; + const [pageX, pageY] = this.pageTranslation; const shiftX = tx / scale; const shiftY = ty / scale; const x = this.x * pageWidth; @@ -351,31 +361,31 @@ class AnnotationEditor { switch (this.rotation) { case 0: return [ - x + shiftX, - pageHeight - y - shiftY - height, - x + shiftX + width, - pageHeight - y - shiftY, + x + shiftX + pageX, + pageHeight - y - shiftY - height + pageY, + x + shiftX + width + pageX, + pageHeight - y - shiftY + pageY, ]; case 90: return [ - x + shiftY, - pageHeight - y + shiftX, - x + shiftY + height, - pageHeight - y + shiftX + width, + x + shiftY + pageX, + pageHeight - y + shiftX + pageY, + x + shiftY + height + pageX, + pageHeight - y + shiftX + width + pageY, ]; case 180: return [ - x - shiftX - width, - pageHeight - y + shiftY, - x - shiftX, - pageHeight - y + shiftY + height, + x - shiftX - width + pageX, + pageHeight - y + shiftY + pageY, + x - shiftX + pageX, + pageHeight - y + shiftY + height + pageY, ]; case 270: return [ - x - shiftY - height, - pageHeight - y - shiftX - width, - x - shiftY, - pageHeight - y - shiftX, + x - shiftY - height + pageX, + pageHeight - y - shiftX - width + pageY, + x - shiftY + pageX, + pageHeight - y - shiftX + pageY, ]; default: throw new Error("Invalid rotation"); diff --git a/test/integration/freetext_editor_spec.js b/test/integration/freetext_editor_spec.js index 50443cfbb7271..c4ff6febcde36 100644 --- a/test/integration/freetext_editor_spec.js +++ b/test/integration/freetext_editor_spec.js @@ -547,24 +547,9 @@ describe("Editor", () => { await page.mouse.click(rect.x + 100, rect.y + 100); await page.type(`${getEditorSelector(currentId)} .internal`, data); - const editorRect = await page.$eval( - getEditorSelector(currentId), - el => { - const { x, y, width, height } = el.getBoundingClientRect(); - return { - x, - y, - width, - height, - }; - } - ); - // Commit. - await page.mouse.click( - editorRect.x, - editorRect.y + 2 * editorRect.height - ); + await page.keyboard.press("Escape"); + await page.waitForTimeout(10); await waitForSelectedEditor(page, getEditorSelector(currentId)); await waitForStorageEntries(page, currentId + 1); @@ -646,4 +631,88 @@ describe("Editor", () => { ); }); }); + + describe("issue 15789", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("issue15789.pdf", ".annotationEditorLayer"); + pages = await Promise.all( + pages.map(async ([browserName, page]) => { + await page.select("#scaleSelect", "1"); + return [browserName, page]; + }) + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must take the media box into account", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await page.click("#editorFreeText"); + let currentId = 0; + + for (let step = 0; step < 3; step++) { + const rect = await page.$eval(".annotationEditorLayer", el => { + // With Chrome something is wrong when serializing a DomRect, + // hence we extract the values and just return them. + const { x, y, width, height } = el.getBoundingClientRect(); + return { x, y, width, height }; + }); + + const data = `Hello ${step}`; + const x = rect.x + 0.1 * rect.width; + const y = rect.y + 0.1 * rect.height; + await page.mouse.click(x, y); + await page.type(`${getEditorSelector(currentId)} .internal`, data); + + // Commit. + await page.keyboard.press("Escape"); + await page.waitForTimeout(10); + + await page.evaluate(() => { + document.getElementById("pageRotateCw").click(); + }); + currentId += 1; + await page.waitForTimeout(10); + } + + const serialize = proprName => + page.evaluate( + name => + [ + ...window.PDFViewerApplication.pdfDocument.annotationStorage.serializable.values(), + ].map(x => x[name]), + proprName + ); + + const rects = (await serialize("rect")).map(rect => + rect.slice(0, 2).map(x => Math.floor(x)) + ); + const expected = [ + [-28, 695], + [-38, -10], + [501, -20], + ]; + // Dimensions aren't exactly the same from a platform to an other + // so we're a bit tolerant here with the numbers. + // Anyway the goal is to check that the bottom left corner of the + // media box is taken into account. + // The pdf has a media box equals to [-99 -99 612.0 792.0]. + const diffs = rects.map( + (rect, i) => + Math.abs(rect[0] - expected[i][0]) < 10 && + Math.abs(rect[1] - expected[i][1]) < 10 + ); + + expect(diffs) + .withContext(`In ${browserName}`) + .toEqual([true, true, true]); + }) + ); + }); + }); }); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index d7c000a7da4e7..50738a2c23268 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -559,3 +559,4 @@ !bug1802888.pdf !issue15759.pdf !issue15753.pdf +!issue15789.pdf diff --git a/test/pdfs/issue15789.pdf b/test/pdfs/issue15789.pdf new file mode 100755 index 0000000000000000000000000000000000000000..44d814351bbebeac386d53d8bb36b035614be501 GIT binary patch literal 4920 zcmeHLeQZ-z6d#T+%W5z&h$JT0!W3k$_ultzU;9c|KGrpCuB>d;O()6T$K6(F`iQo`5Mnr@BBmZCw2}Z>c5zIkjO__*eNbk9+@i!oIeEpwJLWy35Ow9oBAZdZMMb9>b7s_@9D z^Hr&H;sp5p6~Bp}9hQh;ClJJ&9O~`uox1PGIuQSK=-%$Rf!&22@w%>Am(Y#Pt_!t` z`)8BQza5$~b#BekAEr%r@UR$tIYVO9lr`Cut*9Ae?RhiBawZ4F;@7y`&(>0LT_mZA ziKg`aYKCB;8cvg;XIJQj9!`>g^%gPjMZhny&owZ{vO&J%E=wPpG0T=ZXim=?DrNb^ z5~o8qS;_!ZIwi{^oAs)32QH&YBgf8~RQSYvfTg@p0%}**cA9BBt$Zhnqb_tfF%lmk zPmLTN8$1oaSiBmY8~nWCaqpDYhZau#<@$nS!;ic=?cg7Oy;Uy_Z#Z~j{rY9@UCZ7B z!$ZgSCHAfowjDY3@}9)bq2pH*TaTPNx9@5qHuB7C15N74rik{yXUY@vw;nyaXHWNy z-si6UxqH)+B||l}HRnHhY|P=DtRtxuaygTEgkWc0F^dTsYb%9PIm2o&p{0eatgfRb zP-uTL)9AVK;YS{j)EYfqz8H#So9IAtcZd{d6H?C79&9gM@3KfEdbT-X00u<7z zOfFbx^vLWUph32_9zzc(ikRyfJf3|Cf%SQ;6gm7?h&^su2b)z3*Ns=JqA=mjg>@#|>q2-eQnmRZqi$ zoQ2qnpp%lOj_YLeMylwVrb3#cX_mV=riUH%7^kt=xT>SMblOofYvbc4J+{Hj32>YM zivwmS7}K?6--ZY)n6O1Yhj|VK5|{*u7{tEC2nP`g!CNG&1Qjuw*u}^0Xa@`uL;*X7 zP4X6uN=iDR+~!I73M$Ipf>X4h8wp)cg-aQ?Ts3q>HbLCb!9tO6O43{ec-|{`S-=6l z6r5enhN!8*KHW&mW?0Tx5e(;e0-+1^tSQu_p7J zk7@!(C=oeD4QQO8h<%Ex`TRsFxe<%mb#KFM$*`Y_j7lrGG8RRjKfutCB60)^eoo@C z#1WZFf=UD)5w#c|!?o$!a9Jcl#*3C$sqt^=x=E*u6@pg%*y^cNti`9STG>qj$FGXw zmQ`7I^;He>E}EG%G9hv&>6)Z#LJUkucrv>t>6#D&6B3@xuK$cKSGCz-9dM&(khKyH z&5W&2yko02-l-9(G@G#6oZB0u5DvT7>kWIY#&P?{;cp#?Yv%j|G_J- zzp*8`Z}`nc=OcSBymIzZ;^NK^-mjgBYU(;smqS#x$%6AxhWx)nncD_2#A*D(Mo_nm zU#tm~ef&~$&!f=;OY7R|c3c~pdgbZa?>-oLwzl=?_4_o8(6N~Zo?o`*r73J6b7tm@ QhaG6K4P-DD$44@M0|W4SZvX%Q literal 0 HcmV?d00001