From d6605674ddc7ae63ed9cce33e470145de1e6f424 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Tue, 13 May 2025 19:12:17 +0200 Subject: [PATCH] [Editor] Keep aspect ratio when rescaling an image before being processed for a signature (bug 1962819) --- src/display/editor/drawers/signaturedraw.js | 10 +-- test/images/samplesignature.png | Bin 0 -> 10200 bytes test/integration/signature_editor_spec.mjs | 88 ++++++++++++++++++++ 3 files changed, 90 insertions(+), 8 deletions(-) create mode 100755 test/images/samplesignature.png diff --git a/src/display/editor/drawers/signaturedraw.js b/src/display/editor/drawers/signaturedraw.js index 1118421da..d2139f31a 100644 --- a/src/display/editor/drawers/signaturedraw.js +++ b/src/display/editor/drawers/signaturedraw.js @@ -436,14 +436,8 @@ class SignatureExtractor { const isteps = Math.floor(steps); steps = steps === isteps ? isteps - 1 : isteps; for (let i = 0; i < steps; i++) { - newWidth = prevWidth; - newHeight = prevHeight; - if (newWidth > maxDim) { - newWidth = Math.ceil(newWidth / 2); - } - if (newHeight > maxDim) { - newHeight = Math.ceil(newHeight / 2); - } + newWidth = Math.ceil(prevWidth / 2); + newHeight = Math.ceil(prevHeight / 2); const offscreen = new OffscreenCanvas(newWidth, newHeight); const ctx = offscreen.getContext("2d"); diff --git a/test/images/samplesignature.png b/test/images/samplesignature.png new file mode 100755 index 0000000000000000000000000000000000000000..09ac5b434b34cafa3ad9fde5d5344be48c08cc3b GIT binary patch literal 10200 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 bTP}C+@(`O6df@k!5vV9>zAFFAEaZOyHJ<$W literal 0 HcmV?d00001 diff --git a/test/integration/signature_editor_spec.mjs b/test/integration/signature_editor_spec.mjs index f424f9e18..490068ffe 100644 --- a/test/integration/signature_editor_spec.mjs +++ b/test/integration/signature_editor_spec.mjs @@ -27,7 +27,9 @@ import { } from "./test_utils.mjs"; import { fileURLToPath } from "url"; +import fs from "fs"; import path from "path"; +import { PNG } from "pngjs"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); @@ -583,4 +585,90 @@ describe("Signature Editor", () => { ); }); }); + + describe("Check the aspect ratio (bug 1962819)", () => { + let pages, contentWidth, contentHeight; + + function getContentAspectRatio(png) { + const { width, height } = png; + const buffer = new Uint32Array(png.data.buffer); + let x0 = width; + let y0 = height; + let x1 = 0; + let y1 = 0; + for (let i = 0; i < height; i++) { + for (let j = 0; j < width; j++) { + if (buffer[width * i + j] !== 0) { + x0 = Math.min(x0, j); + y0 = Math.min(y0, i); + x1 = Math.max(x1, j); + y1 = Math.max(y1, i); + } + } + } + + contentWidth = x1 - x0; + contentHeight = y1 - y0; + } + + beforeAll(() => { + const data = fs.readFileSync( + path.join(__dirname, "../images/samplesignature.png") + ); + const png = PNG.sync.read(data); + getContentAspectRatio(png); + }); + + beforeEach(async () => { + pages = await loadAndWait("empty.pdf", ".annotationEditorLayer"); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("must check that the signature has the correct aspect ratio", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await switchToSignature(page); + await page.click("#editorSignatureAddSignature"); + + await page.waitForSelector("#addSignatureDialog", { + visible: true, + }); + + await page.click("#addSignatureImageButton"); + await page.waitForSelector("#addSignatureImagePlaceholder", { + visible: true, + }); + await page.waitForSelector(`${addButtonSelector}:disabled`); + + const input = await page.$("#addSignatureFilePicker"); + await input.uploadFile( + `${path.join(__dirname, "../images/samplesignature.png")}` + ); + await page.waitForSelector(`#addSignatureImage > path:not([d=""])`); + + // The save button should be enabled now. + await page.waitForSelector( + "#addSignatureSaveContainer > input:not(:disabled)" + ); + await page.click("#addSignatureAddButton"); + await page.waitForSelector("#addSignatureDialog", { + visible: false, + }); + const { width, height } = await getRect( + page, + ".canvasWrapper > svg use[href='#path_p1_0']" + ); + + expect(Math.abs(contentWidth / width - contentHeight / height)) + .withContext( + `In ${browserName} (${contentWidth}x${contentHeight} vs ${width}x${height})` + ) + .toBeLessThan(0.25); + }) + ); + }); + }); });