From 58afc27c3928073f4b2f554b1b4fe05af871700e Mon Sep 17 00:00:00 2001 From: Jeffrey Chen <78434827+TCOTC@users.noreply.github.com> Date: Sat, 10 Jan 2026 09:51:36 +0800 Subject: [PATCH] :bug: Fix PDF rectangle annotation screenshot rotation handling https://ld246.com/article/1766895521854 (#16714) Fix screenshot position and orientation errors when page is rotated, properly handle the combination of user rotation and PDF's own rotation --- app/src/asset/anno.ts | 87 ++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 29 deletions(-) diff --git a/app/src/asset/anno.ts b/app/src/asset/anno.ts index a91098ded..eb382d357 100644 --- a/app/src/asset/anno.ts +++ b/app/src/asset/anno.ts @@ -762,43 +762,72 @@ const copyAnno = (idPath: string, fileName: string, pdf: any) => { }, Constants.TIMEOUT_DBLCLICK); }; -const getCaptureCanvas = async (pdfObj: any, pageNumber: number) => { - const pdfPage = await pdfObj.pdfDocument.getPage(pageNumber); - const viewport = pdfPage.getViewport({scale: 1.5 * pdfObj.pdfViewer.currentScale * window.pdfjsLib.PixelsPerInch.PDF_TO_CSS_UNITS}); - const canvas = document.createElement("canvas"); - canvas.width = Math.floor(viewport.width); - canvas.height = Math.floor(viewport.height); - - await pdfPage.render({ - canvasContext: canvas.getContext("2d"), - viewport: viewport - }).promise; - - return canvas; -}; - async function getRectImgData(pdfObj: any) { const pageElement = hasClosestByClassName(rectElement, "page"); if (!pageElement) { return; } - const captureCanvas = await getCaptureCanvas(pdfObj, parseInt(pageElement.getAttribute("data-page-number"))); + const pageNumber = parseInt(pageElement.getAttribute("data-page-number")); + const pageView = pdfObj.pdfViewer.getPageView(pageNumber - 1); + if (!pageView) { + return; + } - const rectStyle = (rectElement.firstElementChild as HTMLElement).style; - const scale = 1.5; - const captureImageData = captureCanvas.getContext("2d").getImageData( - scale * parseFloat(rectStyle.left), - scale * parseFloat(rectStyle.top), - scale * parseFloat(rectStyle.width), - scale * parseFloat(rectStyle.height)); + // PDF 截图时的缩放倍数,用于提高截图清晰度 + const CAPTURE_SCALE_RATIO = 1.5; - const tempCanvas = document.createElement("canvas"); - tempCanvas.width = captureImageData.width; - tempCanvas.height = captureImageData.height; - const ctx = tempCanvas.getContext("2d"); - ctx.putImageData(captureImageData, 0, 0); - return tempCanvas.toDataURL(); + const pdfPage = await pdfObj.pdfDocument.getPage(pageNumber); + const displayViewport = pageView.viewport.clone({rotation: 0}); + // displayViewport.scale 等于以前用的 pdfObj.pdfViewer.currentScale * window.pdfjsLib.PixelsPerInch.PDF_TO_CSS_UNITS + const captureViewport = pdfPage.getViewport({scale: displayViewport.scale * CAPTURE_SCALE_RATIO, rotation: 0}); + const captureCanvas = document.createElement("canvas"); + captureCanvas.width = Math.floor(captureViewport.width); + captureCanvas.height = Math.floor(captureViewport.height); + + const captureCtx = captureCanvas.getContext("2d"); + await pdfPage.render({ + canvasContext: captureCtx, + viewport: captureViewport + }).promise; + + const firstChild = rectElement.firstElementChild as HTMLElement; + if (!firstChild) { + return; + } + const rectStyle = firstChild.style; + const captureImageData = captureCtx.getImageData( + CAPTURE_SCALE_RATIO * parseFloat(rectStyle.left), + CAPTURE_SCALE_RATIO * parseFloat(rectStyle.top), + CAPTURE_SCALE_RATIO * parseFloat(rectStyle.width), + CAPTURE_SCALE_RATIO * parseFloat(rectStyle.height) + ); + + const resultCanvas = document.createElement("canvas"); + resultCanvas.width = captureImageData.width; + resultCanvas.height = captureImageData.height; + // 页面实际旋转角度 = 用户旋转 + PDF 本身旋转 + const totalRotation = (pageView.rotation + pageView.pdfPageRotate) % 360; + const resultCtx = resultCanvas.getContext("2d"); + if (totalRotation === 0) { + resultCtx.putImageData(captureImageData, 0, 0); + } else { + // 交换宽高 + if (totalRotation === 90 || totalRotation === 270) { + [resultCanvas.width, resultCanvas.height] = [resultCanvas.height, resultCanvas.width]; + } + resultCtx.translate(resultCanvas.width / 2, resultCanvas.height / 2); + resultCtx.rotate((totalRotation * Math.PI) / 180); + // 在旋转后的画布坐标系上绘制图片 + const tempCanvas = document.createElement("canvas"); + tempCanvas.width = captureImageData.width; + tempCanvas.height = captureImageData.height; + const tempCtx = tempCanvas.getContext("2d"); + tempCtx.putImageData(captureImageData, 0, 0); + resultCtx.drawImage(tempCanvas, -tempCanvas.width / 2, -tempCanvas.height / 2); + } + + return resultCanvas.toDataURL(); } const setConfig = (pdf: any, id: string, data: IPdfAnno) => {