diff --git a/src/pdf/lib/path.js b/src/pdf/lib/path.js index b66d0e9c4..787ff9aef 100644 --- a/src/pdf/lib/path.js +++ b/src/pdf/lib/path.js @@ -48,6 +48,28 @@ export function smoothPath(points) { return points; } +export function densifyPath(points, maxStep = 2) { + if (points.length < 4) { + return points; + } + + const densePath = [points[0], points[1]]; + for (let i = 2; i < points.length; i += 2) { + const x1 = densePath[densePath.length - 2]; + const y1 = densePath[densePath.length - 1]; + const x2 = points[i]; + const y2 = points[i + 1]; + const distance = Math.hypot(x2 - x1, y2 - y1); + const steps = Math.max(1, Math.ceil(distance / maxStep)); + for (let j = 1; j <= steps; j++) { + const t = j / steps; + densePath.push(x1 + (x2 - x1) * t, y1 + (y2 - y1) * t); + } + } + + return densePath; +} + export function applyTransformationMatrixToInkPosition(matrix, position) { const { paths, width } = position; const a = matrix[0], b = matrix[1], c = matrix[2], d = matrix[3], e = matrix[4], f = matrix[5]; diff --git a/src/pdf/pdf-view.js b/src/pdf/pdf-view.js index ebb4899cb..23fa34700 100644 --- a/src/pdf/pdf-view.js +++ b/src/pdf/pdf-view.js @@ -64,7 +64,7 @@ import PDFRenderer from './pdf-renderer'; import { drawAnnotationsOnCanvas } from './lib/render'; import PopupDelayer from '../common/lib/popup-delayer'; import { adjustTextAnnotationPosition } from './lib/text-annotation'; -import { applyTransformationMatrixToInkPosition, eraseInk, smoothPath } from './lib/path'; +import { applyTransformationMatrixToInkPosition, densifyPath, eraseInk, smoothPath } from './lib/path'; import { History } from '../common/lib/history'; import { FindState, PDFFindController } from './pdf-find-controller'; import { @@ -2423,6 +2423,7 @@ class PDFView { paths: [[...point]] } }; + action.shiftConstrained = false; action.triggered = true; } else if (action.type === 'erase') { @@ -2851,8 +2852,26 @@ class PDFView { action.triggered = true; } else if (action.type === 'ink') { - let point = originalPagePosition.rects[0].slice(0, 2); - action.annotation.position.paths[0].push(...point); + let [x, y] = originalPagePosition.rects[0].slice(0, 2); + let path = action.annotation.position.paths[0]; + let shiftConstrained = event.shiftKey && path.length >= 2; + + if (shiftConstrained) { + let x0 = path[0]; + let y0 = path[1]; + let dx = x - x0; + let dy = y - y0; + + if (Math.abs(dx) >= Math.abs(dy)) { + y = y0; + } + else { + x = x0; + } + } + + path.push(x, y); + action.shiftConstrained = shiftConstrained; // Already triggered on pointerdown } else if (action.type === 'erase') { @@ -2974,7 +2993,13 @@ class PDFView { else if (action.type === 'ink' && action.annotation) { let lastInkAnnotation = this._annotations.find(x => x.id === this._lastAddedInkAnnotationID); let path = action.annotation.position.paths[0]; - path = smoothPath(path); + if (action.shiftConstrained && path.length >= 4) { + let maxStep = Math.max(2, action.annotation.position.width / 2); + path = densifyPath(path, maxStep); + } + else { + path = smoothPath(path); + } path = path.map(value => parseFloat(value.toFixed(3))); action.annotation.position.paths[0] = path; let dist;