diff --git a/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java b/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java index 2a291cdd..e87590f1 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/GestureHelper.java @@ -14,9 +14,7 @@ class GestureHelper { public interface GestureListener { boolean onTapUp(); - // Can be replaced with ratio when supported - void onZoomIn(float value); - void onZoomOut(float value); + void onZoom(float scaleFactor, float focusX, float focusY); void onZoomEnd(); } @@ -33,29 +31,10 @@ public boolean onSingleTapUp(MotionEvent motionEvent) { final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() { - final float SPAN_RATIO = 600; - float initialSpan; - float prevNbStep; - - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - initialSpan = detector.getCurrentSpan(); - prevNbStep = 0; - return true; - } - @Override public boolean onScale(ScaleGestureDetector detector) { - float spanDiff = initialSpan - detector.getCurrentSpan(); - float curNbStep = spanDiff / SPAN_RATIO; - - float stepDiff = curNbStep - prevNbStep; - if (stepDiff > 0) { - listener.onZoomOut(stepDiff); - } else { - listener.onZoomIn(Math.abs(stepDiff)); - } - prevNbStep = curNbStep; + listener.onZoom(detector.getScaleFactor(), detector.getFocusX(), + detector.getFocusY()); return true; } diff --git a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java index 49dd35a8..0384849b 100644 --- a/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java +++ b/app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java @@ -120,6 +120,8 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader public int mPage; public int mNumPages; private float mZoomRatio = 1f; + private float mZoomFocusX = 0f; + private float mZoomFocusY = 0f; private int mDocumentOrientationDegrees; private int mDocumentState; private String mEncryptedDocumentPassword; @@ -177,6 +179,16 @@ public void setZoomRatio(final float ratio) { mZoomRatio = Math.max(Math.min(ratio, MAX_ZOOM_RATIO), MIN_ZOOM_RATIO); } + @JavascriptInterface + public float getZoomFocusX() { + return mZoomFocusX; + } + + @JavascriptInterface + public float getZoomFocusY() { + return mZoomFocusY; + } + @JavascriptInterface public float getMinZoomRatio() { return MIN_ZOOM_RATIO; @@ -363,13 +375,8 @@ public boolean onTapUp() { } @Override - public void onZoomIn(float value) { - zoomIn(value, false); - } - - @Override - public void onZoomOut(float value) { - zoomOut(value, false); + public void onZoom(float scaleFactor, float focusX, float focusY) { + zoom(scaleFactor, focusX, focusY, false); } @Override @@ -559,20 +566,12 @@ private void shareDocument() { } } - private void zoomIn(float value, boolean end) { - if (mZoomRatio < MAX_ZOOM_RATIO) { - mZoomRatio = Math.min(mZoomRatio + value, MAX_ZOOM_RATIO); - renderPage(end ? 1 : 2); - invalidateOptionsMenu(); - } - } - - private void zoomOut(float value, boolean end) { - if (mZoomRatio > MIN_ZOOM_RATIO) { - mZoomRatio = Math.max(mZoomRatio - value, MIN_ZOOM_RATIO); - renderPage(end ? 1 : 2); - invalidateOptionsMenu(); - } + private void zoom(float scaleFactor, float focusX, float focusY, boolean end) { + mZoomRatio = Math.min(Math.max(mZoomRatio * scaleFactor, MIN_ZOOM_RATIO), MAX_ZOOM_RATIO); + mZoomFocusX = focusX; + mZoomFocusY = focusY; + renderPage(end ? 1 : 2); + invalidateOptionsMenu(); } private void zoomEnd() { diff --git a/package-lock.json b/package-lock.json index b508121d..2ae72436 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "devDependencies": { "esbuild": "^0.24.0", "eslint": "^9.13.0", - "pdfjs-dist": "^4.3.136" + "pdfjs-dist": "^4.7.76" } }, "node_modules/@esbuild/aix-ppc64": { @@ -18,6 +18,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -34,6 +35,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -50,6 +52,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -66,6 +69,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -82,6 +86,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -98,6 +103,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -114,6 +120,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -130,6 +137,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -146,6 +154,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -162,6 +171,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -178,6 +188,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -194,6 +205,7 @@ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -210,6 +222,7 @@ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -226,6 +239,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -242,6 +256,7 @@ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -258,6 +273,7 @@ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -274,6 +290,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -290,6 +307,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -306,6 +324,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -322,6 +341,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -338,6 +358,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -354,6 +375,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -370,6 +392,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -386,6 +409,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -424,10 +448,11 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", - "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -437,6 +462,7 @@ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.4", "debug": "^4.3.1", @@ -451,6 +477,7 @@ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.7.0.tgz", "integrity": "sha512-xp5Jirz5DyPYlPiKat8jaq0EmYvDXKKpzTbxXMpT9eqlRJkRKIz9AGMdlvYjih+im+QlhWrpvVjl8IPC/lHlUw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -484,6 +511,7 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.13.0.tgz", "integrity": "sha512-IFLyoY4d72Z5y/6o/BazFBezupzI/taV8sGumxTAVw3lXG9A6md1Dc34T9s1FoD/an9pJH8RHbAxsaEbBed9lA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -493,15 +521,17 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.1.tgz", + "integrity": "sha512-HFZ4Mp26nbWk9d/BpvP0YNL6W4UoZF0VFcTw/aPPA8RpOxeFQgK+ClABGgAUXs9Y/RGX/l1vOmrqz1MQt9MNuw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "levn": "^0.4.1" }, @@ -514,6 +544,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", "integrity": "sha512-2cbWIHbZVEweE853g8jymffCA+NCMiuqeECeBBLm8dg2oFdjuGJhgN4UAbI+6v0CKbbhvtXA4qV8YR5Ji86nmw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } @@ -523,6 +554,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.5.tgz", "integrity": "sha512-KSPA4umqSG4LHYRodq31VDwKAvaTF4xmVlzM8Aeh4PlU1JQ3IG0wiA8C25d3RQ9nJyM3mBHyI53K06VVL/oFFg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.0", "@humanwhocodes/retry": "^0.3.0" @@ -550,6 +582,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -584,13 +617,15 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/abbrev": { "version": "1.1.1", @@ -601,10 +636,11 @@ "optional": true }, "node_modules/acorn": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.0.tgz", - "integrity": "sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==", + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -846,13 +882,13 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -917,6 +953,7 @@ "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -968,6 +1005,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.13.0.tgz", "integrity": "sha512-EYZK6SX6zjFHST/HRytOdA/zE72Cq/bfw45LSyuwrdvcclb/gqV8RRQxywOBEWO2+WDpva6UZa4CcDeJKzUCFA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.11.0", @@ -1028,6 +1066,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -1044,6 +1083,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1056,6 +1096,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", @@ -1069,9 +1110,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -1086,6 +1127,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -1327,9 +1369,9 @@ } }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -1616,16 +1658,16 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", + "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", "dev": true, "license": "MIT", "optional": true @@ -1808,9 +1850,9 @@ } }, "node_modules/path2d": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.0.tgz", - "integrity": "sha512-KdPAykQX6kmLSOO6Jpu2KNcCED7CKjmaBNGGNuctOsG0hgYO1OdYQaan6cYXJiG0WmXOwZZPILPBimu5QAIw3A==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.1.tgz", + "integrity": "sha512-Fl2z/BHvkTNvkuBzYTpTuirHZg6wW9z8+4SND/3mDTEcYbbNKWAy21dz9D3ePNNwrrK8pqZO5vLPZ1hLF6T7XA==", "dev": true, "license": "MIT", "optional": true, @@ -1819,9 +1861,9 @@ } }, "node_modules/pdfjs-dist": { - "version": "4.3.136", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.3.136.tgz", - "integrity": "sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==", + "version": "4.7.76", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.7.76.tgz", + "integrity": "sha512-8y6wUgC/Em35IumlGjaJOCm3wV4aY/6sqnIT3fVW/67mXsOZ9HWBn8GDKmJUK0GSzpbmX3gQqwfoFayp78Mtqw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1829,7 +1871,7 @@ }, "optionalDependencies": { "canvas": "^2.11.2", - "path2d": "^0.2.0" + "path2d": "^0.2.1" } }, "node_modules/prelude-ls": { @@ -1919,9 +1961,9 @@ "optional": true }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, "license": "ISC", "optional": true, diff --git a/package.json b/package.json index 7057fe73..020891a4 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,6 @@ "devDependencies": { "esbuild": "^0.24.0", "eslint": "^9.13.0", - "pdfjs-dist": "^4.3.136" + "pdfjs-dist": "^4.7.76" } } diff --git a/viewer/js/index.js b/viewer/js/index.js index 31549249..13d245e0 100644 --- a/viewer/js/index.js +++ b/viewer/js/index.js @@ -58,15 +58,12 @@ function doPrerender(pageNumber, prerenderTrigger) { } } -function display(newCanvas, zoom) { +function display(newCanvas) { canvas.height = newCanvas.height; canvas.width = newCanvas.width; canvas.style.height = newCanvas.style.height; canvas.style.width = newCanvas.style.width; canvas.getContext("2d", { alpha: false }).drawImage(newCanvas, 0, 0); - if (!zoom) { - scrollTo(0, 0); - } } function setLayerTransform(pageWidth, pageHeight, layerDiv) { @@ -101,12 +98,13 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) { cache.splice(i, 1); cache.push(cached); - display(cached.canvas, zoom); + display(cached.canvas); textLayerDiv.replaceWith(cached.textLayerDiv); textLayerDiv = cached.textLayerDiv; setLayerTransform(cached.pageWidth, cached.pageHeight, textLayerDiv); container.style.setProperty("--scale-factor", newZoomRatio.toString()); + textLayerDiv.hidden = false; } pageRendering = false; @@ -130,6 +128,9 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) { const viewport = page.getViewport({scale: newZoomRatio, rotation: orientationDegrees}); + const scaleFactor = newZoomRatio / zoomRatio; + const ratio = globalThis.devicePixelRatio; + if (useRender) { if (newZoomRatio !== zoomRatio) { canvas.style.height = viewport.height + "px"; @@ -139,12 +140,22 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) { } if (zoom === 2) { + textLayerDiv.hidden = true; pageRendering = false; + + // zoom focus relative to page origin, rather than screen origin + const globalFocusX = channel.getZoomFocusX() / ratio + globalThis.scrollX; + const globalFocusY = channel.getZoomFocusY() / ratio + globalThis.scrollY; + + const translationFactor = scaleFactor - 1; + const scrollX = globalFocusX * translationFactor; + const scrollY = globalFocusY * translationFactor; + scrollBy(scrollX, scrollY); + return; } const newCanvas = document.createElement("canvas"); - const ratio = globalThis.devicePixelRatio; newCanvas.height = viewport.height * ratio; newCanvas.width = viewport.width * ratio; newCanvas.style.height = viewport.height + "px"; @@ -165,7 +176,7 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) { if (!useRender || rendered) { return; } - display(newCanvas, zoom); + display(newCanvas); rendered = true; } render(); @@ -185,23 +196,12 @@ function renderPage(pageNumber, zoom, prerender, prerenderTrigger=0) { render(); - // We use CSS transform to rotate a text layer div of zero - // degrees rotation. So, when the rotation is 90 or 270 - // degrees, set width and height of the text layer div to the - // height and width of the canvas, respectively, to prevent - // text layer misalignment. - if (orientationDegrees % 180 === 0) { - newTextLayerDiv.style.height = newCanvas.style.height; - newTextLayerDiv.style.width = newCanvas.style.width; - } else { - newTextLayerDiv.style.height = newCanvas.style.width; - newTextLayerDiv.style.width = newCanvas.style.height; - } setLayerTransform(viewport.width, viewport.height, newTextLayerDiv); if (useRender) { textLayerDiv.replaceWith(newTextLayerDiv); textLayerDiv = newTextLayerDiv; container.style.setProperty("--scale-factor", newZoomRatio.toString()); + textLayerDiv.hidden = false; } if (cache.length === maxCached) {