kviewer 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.json +1 -1
- package/dist/module.mjs +25 -4
- package/dist/runtime/annotation/engine/config.d.ts +1 -1
- package/dist/runtime/annotation/engine/config.js +2 -2
- package/dist/runtime/annotation/engine/painter.d.ts +1 -1
- package/dist/runtime/annotation/engine/utils.d.ts +2 -1
- package/dist/runtime/annotation/engine/utils.js +3 -7
- package/dist/runtime/annotation/pdf-export/export.js +63 -9
- package/dist/runtime/annotation/pdf-export/parse_circle.js +3 -2
- package/dist/runtime/annotation/pdf-export/parse_freetext.js +18 -8
- package/dist/runtime/annotation/pdf-export/parse_highlight.js +27 -29
- package/dist/runtime/annotation/pdf-export/parse_ink.js +5 -3
- package/dist/runtime/annotation/pdf-export/parse_line.js +5 -3
- package/dist/runtime/annotation/pdf-export/parse_polyline.js +6 -4
- package/dist/runtime/annotation/pdf-export/parse_square.js +3 -2
- package/dist/runtime/annotation/pdf-export/parse_stamp.js +16 -9
- package/dist/runtime/annotation/pdf-export/parse_strikeout.js +11 -10
- package/dist/runtime/annotation/pdf-export/parse_text.js +3 -1
- package/dist/runtime/annotation/pdf-export/parse_underline.js +11 -10
- package/dist/runtime/annotation/pdf-export/parse_utils.d.ts +11 -0
- package/dist/runtime/annotation/pdf-export/parse_utils.js +12 -0
- package/dist/runtime/annotation/pdf-import/decode.d.ts +2 -1
- package/dist/runtime/annotation/pdf-import/decode.js +6 -5
- package/dist/runtime/annotation/pdf-import/decode_circle.js +1 -1
- package/dist/runtime/annotation/pdf-import/decode_freetext.js +1 -1
- package/dist/runtime/annotation/pdf-import/decode_highlight.js +1 -1
- package/dist/runtime/annotation/pdf-import/decode_ink.js +1 -1
- package/dist/runtime/annotation/pdf-import/decode_line.js +1 -1
- package/dist/runtime/annotation/pdf-import/decode_square.js +1 -1
- package/dist/runtime/annotation/pdf-import/decode_stamp.js +1 -1
- package/dist/runtime/annotation/pdf-import/decode_text.js +1 -1
- package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.d.ts +3 -2
- package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.js +7 -15
- package/dist/runtime/annotation/pdf-import/transform.d.ts +85 -0
- package/dist/runtime/annotation/pdf-import/transform.js +81 -0
- package/dist/runtime/annotation/pdf-import/types.d.ts +5 -1
- package/dist/runtime/annotation/pdf-import/utils.d.ts +6 -5
- package/dist/runtime/annotation/pdf-import/utils.js +53 -38
- package/dist/runtime/components/AnnotationToolbar.vue +16 -0
- package/dist/runtime/components/FormFieldLayer.d.vue.ts +4 -7
- package/dist/runtime/components/FormFieldLayer.vue +5 -10
- package/dist/runtime/components/FormFieldLayer.vue.d.ts +4 -7
- package/dist/runtime/components/PdfPage.vue +1 -6
- package/dist/runtime/components/form-fields/FormFieldWrapper.d.vue.ts +5 -6
- package/dist/runtime/components/form-fields/FormFieldWrapper.vue +8 -14
- package/dist/runtime/components/form-fields/FormFieldWrapper.vue.d.ts +5 -6
- package/dist/runtime/composables/usePageVirtualization.d.ts +6 -0
- package/dist/runtime/composables/usePageVirtualization.js +8 -2
- package/package.json +1 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PDFName, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
|
+
import { convertCanvasPointToPdf, pageTransformFromPdfLibPage } from "../pdf-import/transform.js";
|
|
3
4
|
import { AnnotationParser } from "./parse.js";
|
|
4
5
|
const UNKNOWN_USER = "Unknown user";
|
|
5
|
-
function buildQuadPoints(konvaString,
|
|
6
|
+
function buildQuadPoints(konvaString, t) {
|
|
6
7
|
const konvaGroup = JSON.parse(konvaString);
|
|
7
8
|
const rects = (konvaGroup.children ?? []).filter((item) => item.className === "Rect");
|
|
8
9
|
const quadPoints = [];
|
|
@@ -11,11 +12,11 @@ function buildQuadPoints(konvaString, pageHeight) {
|
|
|
11
12
|
const y = rect.attrs?.y ?? 0;
|
|
12
13
|
const width = rect.attrs?.width ?? 0;
|
|
13
14
|
const height = rect.attrs?.height ?? 0;
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
quadPoints.push(
|
|
15
|
+
const tl = convertCanvasPointToPdf(t, x, y);
|
|
16
|
+
const tr = convertCanvasPointToPdf(t, x + width, y);
|
|
17
|
+
const bl = convertCanvasPointToPdf(t, x, y + height);
|
|
18
|
+
const br = convertCanvasPointToPdf(t, x + width, y + height);
|
|
19
|
+
quadPoints.push(tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y);
|
|
19
20
|
}
|
|
20
21
|
return quadPoints;
|
|
21
22
|
}
|
|
@@ -23,12 +24,12 @@ export class StrikeOutParser extends AnnotationParser {
|
|
|
23
24
|
async parse() {
|
|
24
25
|
const { annotation, page, pdfDoc } = this;
|
|
25
26
|
const context = pdfDoc.context;
|
|
26
|
-
const
|
|
27
|
-
const quadPoints = buildQuadPoints(annotation.konvaString,
|
|
27
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
28
|
+
const quadPoints = buildQuadPoints(annotation.konvaString, pageTransform);
|
|
28
29
|
const mainAnn = context.obj({
|
|
29
30
|
Type: PDFName.of("Annot"),
|
|
30
31
|
Subtype: PDFName.of("StrikeOut"),
|
|
31
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
32
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
32
33
|
QuadPoints: quadPoints,
|
|
33
34
|
C: rgbToPdfColor(annotation.color || void 0),
|
|
34
35
|
T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
|
|
@@ -42,7 +43,7 @@ export class StrikeOutParser extends AnnotationParser {
|
|
|
42
43
|
const replyAnn = context.obj({
|
|
43
44
|
Type: PDFName.of("Annot"),
|
|
44
45
|
Subtype: PDFName.of("Text"),
|
|
45
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
46
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
46
47
|
Contents: stringToPDFHexString(comment.content),
|
|
47
48
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
48
49
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { PDFName, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
|
+
import { pageTransformFromPdfLibPage } from "../pdf-import/transform.js";
|
|
3
4
|
import { AnnotationParser } from "./parse.js";
|
|
4
5
|
import { computeBoundsFromKonvaString } from "./parse_utils.js";
|
|
5
6
|
const UNKNOWN_USER = "Unknown user";
|
|
6
7
|
export class TextParser extends AnnotationParser {
|
|
7
8
|
async parse() {
|
|
8
9
|
const { annotation, page, pdfDoc } = this;
|
|
10
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
9
11
|
const bounds = computeBoundsFromKonvaString(annotation.konvaString);
|
|
10
|
-
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect,
|
|
12
|
+
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, pageTransform);
|
|
11
13
|
const context = pdfDoc.context;
|
|
12
14
|
const mainAnn = context.obj({
|
|
13
15
|
Type: PDFName.of("Annot"),
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { PDFName, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
|
+
import { convertCanvasPointToPdf, pageTransformFromPdfLibPage } from "../pdf-import/transform.js";
|
|
3
4
|
import { AnnotationParser } from "./parse.js";
|
|
4
5
|
const UNKNOWN_USER = "Unknown user";
|
|
5
|
-
function buildQuadPoints(konvaString,
|
|
6
|
+
function buildQuadPoints(konvaString, t) {
|
|
6
7
|
const konvaGroup = JSON.parse(konvaString);
|
|
7
8
|
const rects = (konvaGroup.children ?? []).filter((item) => item.className === "Rect");
|
|
8
9
|
const quadPoints = [];
|
|
@@ -11,11 +12,11 @@ function buildQuadPoints(konvaString, pageHeight) {
|
|
|
11
12
|
const y = rect.attrs?.y ?? 0;
|
|
12
13
|
const width = rect.attrs?.width ?? 0;
|
|
13
14
|
const height = rect.attrs?.height ?? 0;
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
quadPoints.push(
|
|
15
|
+
const tl = convertCanvasPointToPdf(t, x, y);
|
|
16
|
+
const tr = convertCanvasPointToPdf(t, x + width, y);
|
|
17
|
+
const bl = convertCanvasPointToPdf(t, x, y + height);
|
|
18
|
+
const br = convertCanvasPointToPdf(t, x + width, y + height);
|
|
19
|
+
quadPoints.push(tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y);
|
|
19
20
|
}
|
|
20
21
|
return quadPoints;
|
|
21
22
|
}
|
|
@@ -23,12 +24,12 @@ export class UnderlineParser extends AnnotationParser {
|
|
|
23
24
|
async parse() {
|
|
24
25
|
const { annotation, page, pdfDoc } = this;
|
|
25
26
|
const context = pdfDoc.context;
|
|
26
|
-
const
|
|
27
|
-
const quadPoints = buildQuadPoints(annotation.konvaString,
|
|
27
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
28
|
+
const quadPoints = buildQuadPoints(annotation.konvaString, pageTransform);
|
|
28
29
|
const mainAnn = context.obj({
|
|
29
30
|
Type: PDFName.of("Annot"),
|
|
30
31
|
Subtype: PDFName.of("Underline"),
|
|
31
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
32
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
32
33
|
QuadPoints: quadPoints,
|
|
33
34
|
C: rgbToPdfColor(annotation.color || void 0),
|
|
34
35
|
T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
|
|
@@ -42,7 +43,7 @@ export class UnderlineParser extends AnnotationParser {
|
|
|
42
43
|
const replyAnn = context.obj({
|
|
43
44
|
Type: PDFName.of("Annot"),
|
|
44
45
|
Subtype: PDFName.of("Text"),
|
|
45
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
46
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
46
47
|
Contents: stringToPDFHexString(comment.content),
|
|
47
48
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
48
49
|
M: PDFString.of(comment.date || ""),
|
|
@@ -24,3 +24,14 @@ export declare function computeBoundsFromKonvaString(konvaString: string): {
|
|
|
24
24
|
width: number;
|
|
25
25
|
height: number;
|
|
26
26
|
} | null;
|
|
27
|
+
/**
|
|
28
|
+
* `/Matrix` for an annotation appearance form XObject that should read
|
|
29
|
+
* upright on a `/Rotate` page. Form-local content is drawn in display-
|
|
30
|
+
* upright orientation inside `/BBox = [0, 0, W, H]`; the returned matrix
|
|
31
|
+
* rotates that by `rotation` degrees CCW so the page's intrinsic clockwise
|
|
32
|
+
* `/Rotate` cancels out at view time. Translation values place the rotated
|
|
33
|
+
* bounding box at the origin so the PDF viewer can scale it 1:1 into
|
|
34
|
+
* `/Rect` (whose user-space dims will be `(W, H)` for 0°/180° and
|
|
35
|
+
* `(H, W)` for 90°/270° — matching the rotated form box).
|
|
36
|
+
*/
|
|
37
|
+
export declare function formAppearanceMatrix(rotation: number, W: number, H: number): [number, number, number, number, number, number];
|
|
@@ -52,3 +52,15 @@ export function computeBoundsFromKonvaString(konvaString) {
|
|
|
52
52
|
height: maxY - minY
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
export function formAppearanceMatrix(rotation, W, H) {
|
|
56
|
+
switch (rotation) {
|
|
57
|
+
case 90:
|
|
58
|
+
return [0, 1, -1, 0, H, 0];
|
|
59
|
+
case 180:
|
|
60
|
+
return [-1, 0, 0, -1, W, H];
|
|
61
|
+
case 270:
|
|
62
|
+
return [0, -1, 1, 0, 0, W];
|
|
63
|
+
default:
|
|
64
|
+
return [1, 0, 0, 1, 0, 0];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PDFDocumentProxy } from 'pdfjs-dist';
|
|
2
2
|
import { type IAnnotationStore } from '../engine/types.js';
|
|
3
|
+
import { type PageTransform } from './transform.js';
|
|
3
4
|
import { type StampAppearance } from './types.js';
|
|
4
5
|
export interface DecodePdfAnnotationsResult {
|
|
5
6
|
annotations: IAnnotationStore[];
|
|
@@ -16,6 +17,6 @@ export declare function filterCollidingAnnotations(annotations: IAnnotationStore
|
|
|
16
17
|
annotations: IAnnotationStore[];
|
|
17
18
|
skipped: number;
|
|
18
19
|
};
|
|
19
|
-
export declare function decodePdfPageAnnotations(pageAnnotations: unknown[], pageNumber: number,
|
|
20
|
+
export declare function decodePdfPageAnnotations(pageAnnotations: unknown[], pageNumber: number, pageTransform: PageTransform, options?: DecodePageOptions): DecodePdfAnnotationsResult;
|
|
20
21
|
export declare function decodePdfAnnotationsFromDocument(pdfDoc: PDFDocumentProxy): Promise<DecodePdfAnnotationsResult>;
|
|
21
22
|
export {};
|
|
@@ -8,6 +8,7 @@ import { decodeStampAnnotation } from "./decode_stamp.js";
|
|
|
8
8
|
import { decodeSquareAnnotation } from "./decode_square.js";
|
|
9
9
|
import { decodeTextAnnotation } from "./decode_text.js";
|
|
10
10
|
import { extractStampAppearancesForPage } from "./extract_stamp_appearance.js";
|
|
11
|
+
import { pageTransformFromProxy } from "./transform.js";
|
|
11
12
|
import { isRawPdfAnnotation } from "./types.js";
|
|
12
13
|
const decoderMap = {
|
|
13
14
|
[PdfjsAnnotationType.TEXT]: decodeTextAnnotation,
|
|
@@ -34,7 +35,7 @@ export function filterCollidingAnnotations(annotations, existingIds) {
|
|
|
34
35
|
}
|
|
35
36
|
return { annotations: result, skipped };
|
|
36
37
|
}
|
|
37
|
-
export function decodePdfPageAnnotations(pageAnnotations, pageNumber,
|
|
38
|
+
export function decodePdfPageAnnotations(pageAnnotations, pageNumber, pageTransform, options = {}) {
|
|
38
39
|
const annotations = [];
|
|
39
40
|
let skipped = 0;
|
|
40
41
|
let errors = 0;
|
|
@@ -42,7 +43,7 @@ export function decodePdfPageAnnotations(pageAnnotations, pageNumber, pageHeight
|
|
|
42
43
|
skipped += pageAnnotations.length - rawAnnotations.length;
|
|
43
44
|
const context = {
|
|
44
45
|
pageNumber,
|
|
45
|
-
|
|
46
|
+
pageTransform,
|
|
46
47
|
allAnnotations: rawAnnotations,
|
|
47
48
|
stampAppearances: options.stampAppearances
|
|
48
49
|
};
|
|
@@ -89,7 +90,7 @@ export async function decodePdfAnnotationsFromDocument(pdfDoc) {
|
|
|
89
90
|
for (let pageNumber = 1; pageNumber <= pdfDoc.numPages; pageNumber++) {
|
|
90
91
|
try {
|
|
91
92
|
const page = await pdfDoc.getPage(pageNumber);
|
|
92
|
-
const
|
|
93
|
+
const pageTransform = pageTransformFromProxy(page);
|
|
93
94
|
const pageAnnotations = await page.getAnnotations();
|
|
94
95
|
const rawAnnotations = pageAnnotations.filter(isRawPdfAnnotation);
|
|
95
96
|
const hasStampAnnotations = rawAnnotations.some(
|
|
@@ -100,7 +101,7 @@ export async function decodePdfAnnotationsFromDocument(pdfDoc) {
|
|
|
100
101
|
try {
|
|
101
102
|
stampAppearances = await extractStampAppearancesForPage(
|
|
102
103
|
page,
|
|
103
|
-
|
|
104
|
+
pageTransform,
|
|
104
105
|
rawAnnotations
|
|
105
106
|
);
|
|
106
107
|
} catch (error) {
|
|
@@ -110,7 +111,7 @@ export async function decodePdfAnnotationsFromDocument(pdfDoc) {
|
|
|
110
111
|
const decoded = decodePdfPageAnnotations(
|
|
111
112
|
pageAnnotations,
|
|
112
113
|
pageNumber,
|
|
113
|
-
|
|
114
|
+
pageTransform,
|
|
114
115
|
{ stampAppearances }
|
|
115
116
|
);
|
|
116
117
|
annotations.push(...decoded.annotations);
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
rectFromPdfRect
|
|
9
9
|
} from "./utils.js";
|
|
10
10
|
export function decodeCircleAnnotation(annotation, context) {
|
|
11
|
-
const rect = rectFromPdfRect(annotation.rect, context.
|
|
11
|
+
const rect = rectFromPdfRect(annotation.rect, context.pageTransform);
|
|
12
12
|
if (!rect || typeof annotation.id !== "string") return null;
|
|
13
13
|
const color = getAnnotationColor(annotation, AnnotationType.CIRCLE);
|
|
14
14
|
const strokeWidth = normalizedStrokeWidth(annotation, 1);
|
|
@@ -44,7 +44,7 @@ function inferTextAlign(rectWidth, textPositionX, measuredWidth) {
|
|
|
44
44
|
return "left";
|
|
45
45
|
}
|
|
46
46
|
export function decodeFreeTextAnnotation(annotation, context) {
|
|
47
|
-
const rect = rectFromPdfRect(annotation.rect, context.
|
|
47
|
+
const rect = rectFromPdfRect(annotation.rect, context.pageTransform);
|
|
48
48
|
if (!rect || typeof annotation.id !== "string") return null;
|
|
49
49
|
const appearance = asAppearanceData(annotation.defaultAppearanceData);
|
|
50
50
|
const color = colorArrayToRgb(appearance?.fontColor) ?? getAnnotationColor(annotation, AnnotationType.FREETEXT);
|
|
@@ -75,7 +75,7 @@ export function decodeHighlightAnnotation(annotation, context) {
|
|
|
75
75
|
const color = getAnnotationColor(annotation, type);
|
|
76
76
|
const group = createGhostGroup(annotation.id);
|
|
77
77
|
for (const quadSet of quadSets) {
|
|
78
|
-
const rect = rectFromQuadPoints(quadSet, context.
|
|
78
|
+
const rect = rectFromQuadPoints(quadSet, context.pageTransform);
|
|
79
79
|
if (!rect) continue;
|
|
80
80
|
group.add(createSegment(type, color, rect));
|
|
81
81
|
}
|
|
@@ -16,7 +16,7 @@ export function decodeInkAnnotation(annotation, context) {
|
|
|
16
16
|
const opacity = isNumber(annotation.opacity) ? annotation.opacity : 1;
|
|
17
17
|
const group = createGhostGroup(annotation.id);
|
|
18
18
|
for (const list of annotation.inkLists) {
|
|
19
|
-
const points = pointsFromInkList(list, context.
|
|
19
|
+
const points = pointsFromInkList(list, context.pageTransform);
|
|
20
20
|
if (points.length < 4) continue;
|
|
21
21
|
group.add(new Konva.Line({
|
|
22
22
|
points,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from "./utils.js";
|
|
12
12
|
export function decodeLineAnnotation(annotation, context) {
|
|
13
13
|
if (typeof annotation.id !== "string") return null;
|
|
14
|
-
const points = linePointsFromCoordinates(annotation.lineCoordinates, context.
|
|
14
|
+
const points = linePointsFromCoordinates(annotation.lineCoordinates, context.pageTransform);
|
|
15
15
|
if (!points) return null;
|
|
16
16
|
const color = getAnnotationColor(annotation, AnnotationType.ARROW);
|
|
17
17
|
const strokeWidth = normalizedStrokeWidth(annotation, 1);
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
rectFromPdfRect
|
|
9
9
|
} from "./utils.js";
|
|
10
10
|
export function decodeSquareAnnotation(annotation, context) {
|
|
11
|
-
const rect = rectFromPdfRect(annotation.rect, context.
|
|
11
|
+
const rect = rectFromPdfRect(annotation.rect, context.pageTransform);
|
|
12
12
|
if (!rect || typeof annotation.id !== "string") return null;
|
|
13
13
|
const color = getAnnotationColor(annotation, AnnotationType.RECTANGLE);
|
|
14
14
|
const strokeWidth = normalizedStrokeWidth(annotation, 1);
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./utils.js";
|
|
9
9
|
export function decodeStampAnnotation(annotation, context) {
|
|
10
10
|
if (typeof annotation.id !== "string") return null;
|
|
11
|
-
const rect = rectFromPdfRect(annotation.rect, context.
|
|
11
|
+
const rect = rectFromPdfRect(annotation.rect, context.pageTransform);
|
|
12
12
|
if (!rect) return null;
|
|
13
13
|
const appearance = context.stampAppearances?.get(annotation.id);
|
|
14
14
|
if (!appearance) {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "./utils.js";
|
|
10
10
|
export function decodeTextAnnotation(annotation, context) {
|
|
11
11
|
if (annotation.inReplyTo) return null;
|
|
12
|
-
const rect = rectFromPdfRect(annotation.rect, context.
|
|
12
|
+
const rect = rectFromPdfRect(annotation.rect, context.pageTransform);
|
|
13
13
|
if (!rect || typeof annotation.id !== "string") return null;
|
|
14
14
|
const color = getAnnotationColor(annotation, AnnotationType.NOTE);
|
|
15
15
|
const group = createGhostGroup(annotation.id);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PDFPageProxy } from 'pdfjs-dist';
|
|
2
|
+
import { type PageTransform } from './transform.js';
|
|
2
3
|
import { type RawPdfAnnotation, type StampAppearance } from './types.js';
|
|
3
4
|
export interface StampExtractionOptions {
|
|
4
5
|
scale?: number;
|
|
@@ -10,7 +11,7 @@ export interface PixelBounds {
|
|
|
10
11
|
width: number;
|
|
11
12
|
height: number;
|
|
12
13
|
}
|
|
13
|
-
export declare function stampRectToPixelBounds(rect: unknown,
|
|
14
|
+
export declare function stampRectToPixelBounds(rect: unknown, transform: PageTransform, scale: number, canvasWidth: number, canvasHeight: number): PixelBounds | null;
|
|
14
15
|
export declare function findMaskBounds(mask: Uint8Array, width: number, height: number): PixelBounds | null;
|
|
15
16
|
export declare function computeDiffMask(base: Uint8ClampedArray, annotated: Uint8ClampedArray, width: number, height: number, threshold?: number): {
|
|
16
17
|
mask: Uint8Array;
|
|
@@ -20,4 +21,4 @@ export declare function applyMaskOrFallback(annotated: Uint8ClampedArray, mask:
|
|
|
20
21
|
data: Uint8ClampedArray;
|
|
21
22
|
usedMask: boolean;
|
|
22
23
|
};
|
|
23
|
-
export declare function extractStampAppearancesForPage(page: PDFPageProxy,
|
|
24
|
+
export declare function extractStampAppearancesForPage(page: PDFPageProxy, pageTransform: PageTransform, annotations: RawPdfAnnotation[], options?: StampExtractionOptions): Promise<Map<string, StampAppearance>>;
|
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
import { PdfjsAnnotationType } from "../engine/types.js";
|
|
2
|
+
import { convertPdfRectToCanvas } from "./transform.js";
|
|
2
3
|
import { isNumber } from "./types.js";
|
|
3
4
|
const PDFJS_ANNOTATION_MODE_DISABLE = 0;
|
|
4
5
|
const PDFJS_ANNOTATION_MODE_ENABLE = 1;
|
|
5
6
|
const DEFAULT_RENDER_SCALE = 2;
|
|
6
7
|
const DEFAULT_DIFF_THRESHOLD = 24;
|
|
7
|
-
function normalizedPageRect(rect,
|
|
8
|
+
function normalizedPageRect(rect, transform) {
|
|
8
9
|
if (!Array.isArray(rect) || rect.length < 4) return null;
|
|
9
10
|
const [rx1, ry1, rx2, ry2] = rect;
|
|
10
11
|
if (!isNumber(rx1) || !isNumber(ry1) || !isNumber(rx2) || !isNumber(ry2)) return null;
|
|
11
|
-
|
|
12
|
-
const x2 = Math.max(rx1, rx2);
|
|
13
|
-
const y1 = Math.min(ry1, ry2);
|
|
14
|
-
const y2 = Math.max(ry1, ry2);
|
|
15
|
-
return {
|
|
16
|
-
x: x1,
|
|
17
|
-
y: pageHeight - y2,
|
|
18
|
-
width: x2 - x1,
|
|
19
|
-
height: y2 - y1
|
|
20
|
-
};
|
|
12
|
+
return convertPdfRectToCanvas(transform, [rx1, ry1, rx2, ry2]);
|
|
21
13
|
}
|
|
22
|
-
export function stampRectToPixelBounds(rect,
|
|
23
|
-
const normalized = normalizedPageRect(rect,
|
|
14
|
+
export function stampRectToPixelBounds(rect, transform, scale, canvasWidth, canvasHeight) {
|
|
15
|
+
const normalized = normalizedPageRect(rect, transform);
|
|
24
16
|
if (!normalized) return null;
|
|
25
17
|
const left = Math.max(0, Math.floor(normalized.x * scale));
|
|
26
18
|
const top = Math.max(0, Math.floor(normalized.y * scale));
|
|
@@ -102,7 +94,7 @@ async function renderPageToCanvas(page, canvas, annotationMode, scale) {
|
|
|
102
94
|
});
|
|
103
95
|
await task.promise;
|
|
104
96
|
}
|
|
105
|
-
export async function extractStampAppearancesForPage(page,
|
|
97
|
+
export async function extractStampAppearancesForPage(page, pageTransform, annotations, options = {}) {
|
|
106
98
|
const appearances = /* @__PURE__ */ new Map();
|
|
107
99
|
const stampAnnotations = annotations.filter(
|
|
108
100
|
(annotation) => annotation.annotationType === PdfjsAnnotationType.STAMP && typeof annotation.id === "string"
|
|
@@ -131,7 +123,7 @@ export async function extractStampAppearancesForPage(page, pageHeight, annotatio
|
|
|
131
123
|
const id = annotation.id;
|
|
132
124
|
const bounds = stampRectToPixelBounds(
|
|
133
125
|
annotation.rect,
|
|
134
|
-
|
|
126
|
+
pageTransform,
|
|
135
127
|
scale,
|
|
136
128
|
renderWidth,
|
|
137
129
|
renderHeight
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordinate transform from a PDF page's unrotated user space to top-left
|
|
3
|
+
* origin canvas space, accounting for the page's intrinsic `/Rotate`.
|
|
4
|
+
*
|
|
5
|
+
* PDF annotation coordinates (`/Rect`, `QuadPoints`, ink lists, line
|
|
6
|
+
* endpoints) are stored in unrotated user space regardless of the page's
|
|
7
|
+
* `/Rotate`. To draw them at the right spot, we apply the same
|
|
8
|
+
* rotation pdf.js does when it produces a viewport at scale = 1 — this is a
|
|
9
|
+
* minimal mirror of `PageViewport.convertToViewportPoint` for that scale.
|
|
10
|
+
*/
|
|
11
|
+
export interface PageTransform {
|
|
12
|
+
/** Displayed page width in PDF points (rotation already applied). */
|
|
13
|
+
width: number;
|
|
14
|
+
/** Displayed page height in PDF points (rotation already applied). */
|
|
15
|
+
height: number;
|
|
16
|
+
/** Page intrinsic `/Rotate`, normalized to 0 | 90 | 180 | 270. */
|
|
17
|
+
rotation: number;
|
|
18
|
+
/** Unrotated MediaBox origin (`view[0]`, `view[1]`). Almost always 0. */
|
|
19
|
+
viewOffsetX: number;
|
|
20
|
+
viewOffsetY: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CanvasPoint {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
}
|
|
26
|
+
export interface CanvasRect {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
}
|
|
32
|
+
/** Build a `PageTransform` from a pdf.js page proxy. */
|
|
33
|
+
export declare function pageTransformFromProxy(page: {
|
|
34
|
+
view: number[];
|
|
35
|
+
rotate: number;
|
|
36
|
+
}): PageTransform;
|
|
37
|
+
/**
|
|
38
|
+
* Map a point from unrotated PDF user space to canvas (top-left origin)
|
|
39
|
+
* space at scale = 1. Equivalent to `viewport.convertToViewportPoint(x, y)`
|
|
40
|
+
* when the viewport is built with `getViewport({ scale: 1 })`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function convertPdfPointToCanvas(t: PageTransform, x: number, y: number): CanvasPoint;
|
|
43
|
+
/**
|
|
44
|
+
* Map a PDF rect `[x1, y1, x2, y2]` (unrotated user space) to a normalized
|
|
45
|
+
* canvas-space rect. Rotation may swap which corner is which, so the result
|
|
46
|
+
* is built from `min`/`abs` of the transformed corners.
|
|
47
|
+
*/
|
|
48
|
+
export declare function convertPdfRectToCanvas(t: PageTransform, rect: readonly [number, number, number, number]): CanvasRect;
|
|
49
|
+
/**
|
|
50
|
+
* Inverse of `convertPdfPointToCanvas`: map a top-left-origin canvas point
|
|
51
|
+
* back to unrotated PDF user space. Used by the export path to write
|
|
52
|
+
* annotation `/Rect` and point lists in the coordinates a PDF viewer
|
|
53
|
+
* expects (display content is rotated by the page's `/Rotate` at view time,
|
|
54
|
+
* so we must store the inverse).
|
|
55
|
+
*/
|
|
56
|
+
export declare function convertCanvasPointToPdf(t: PageTransform, cx: number, cy: number): {
|
|
57
|
+
x: number;
|
|
58
|
+
y: number;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Inverse of `convertPdfRectToCanvas`: map a top-left-origin canvas rect
|
|
62
|
+
* back to an unrotated PDF user-space rect `[x1, y1, x2, y2]`. Rotation may
|
|
63
|
+
* swap which corner is which, so the result is normalized.
|
|
64
|
+
*/
|
|
65
|
+
export declare function convertCanvasRectToPdf(t: PageTransform, rect: {
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
width: number;
|
|
69
|
+
height: number;
|
|
70
|
+
}): [number, number, number, number];
|
|
71
|
+
/**
|
|
72
|
+
* Build a `PageTransform` from a pdf-lib `PDFPage`. Note pdf-lib's
|
|
73
|
+
* `getWidth()` / `getHeight()` return the *unrotated* MediaBox extent, so
|
|
74
|
+
* we swap for the displayed dimensions on 90°/270° pages, mirroring what
|
|
75
|
+
* pdfjs does in its default `getViewport({ scale: 1 })` call. MediaBox
|
|
76
|
+
* origin is assumed (0, 0) — almost universal in real PDFs; if a future
|
|
77
|
+
* input needs non-zero origins, read `/MediaBox` directly.
|
|
78
|
+
*/
|
|
79
|
+
export declare function pageTransformFromPdfLibPage(page: {
|
|
80
|
+
getWidth: () => number;
|
|
81
|
+
getHeight: () => number;
|
|
82
|
+
getRotation: () => {
|
|
83
|
+
angle: number;
|
|
84
|
+
};
|
|
85
|
+
}): PageTransform;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function pageTransformFromProxy(page) {
|
|
2
|
+
const view = page.view;
|
|
3
|
+
const vx = view[0] ?? 0;
|
|
4
|
+
const vy = view[1] ?? 0;
|
|
5
|
+
const unrotatedW = (view[2] ?? 0) - vx;
|
|
6
|
+
const unrotatedH = (view[3] ?? 0) - vy;
|
|
7
|
+
const rotation = (page.rotate % 360 + 360) % 360;
|
|
8
|
+
const swap = rotation % 180 !== 0;
|
|
9
|
+
return {
|
|
10
|
+
width: swap ? unrotatedH : unrotatedW,
|
|
11
|
+
height: swap ? unrotatedW : unrotatedH,
|
|
12
|
+
rotation,
|
|
13
|
+
viewOffsetX: vx,
|
|
14
|
+
viewOffsetY: vy
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function convertPdfPointToCanvas(t, x, y) {
|
|
18
|
+
const { rotation, viewOffsetX: vx, viewOffsetY: vy } = t;
|
|
19
|
+
const swap = rotation % 180 !== 0;
|
|
20
|
+
const W = swap ? t.height : t.width;
|
|
21
|
+
const H = swap ? t.width : t.height;
|
|
22
|
+
switch (rotation) {
|
|
23
|
+
case 90:
|
|
24
|
+
return { x: y - vy, y: x - vx };
|
|
25
|
+
case 180:
|
|
26
|
+
return { x: W + vx - x, y: y - vy };
|
|
27
|
+
case 270:
|
|
28
|
+
return { x: H + vy - y, y: W + vx - x };
|
|
29
|
+
default:
|
|
30
|
+
return { x: x - vx, y: H + vy - y };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function convertPdfRectToCanvas(t, rect) {
|
|
34
|
+
const a = convertPdfPointToCanvas(t, rect[0], rect[1]);
|
|
35
|
+
const b = convertPdfPointToCanvas(t, rect[2], rect[3]);
|
|
36
|
+
return {
|
|
37
|
+
x: Math.min(a.x, b.x),
|
|
38
|
+
y: Math.min(a.y, b.y),
|
|
39
|
+
width: Math.abs(b.x - a.x),
|
|
40
|
+
height: Math.abs(b.y - a.y)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function convertCanvasPointToPdf(t, cx, cy) {
|
|
44
|
+
const { rotation, viewOffsetX: vx, viewOffsetY: vy } = t;
|
|
45
|
+
const swap = rotation % 180 !== 0;
|
|
46
|
+
const W = swap ? t.height : t.width;
|
|
47
|
+
const H = swap ? t.width : t.height;
|
|
48
|
+
switch (rotation) {
|
|
49
|
+
case 90:
|
|
50
|
+
return { x: cy + vx, y: cx + vy };
|
|
51
|
+
case 180:
|
|
52
|
+
return { x: W + vx - cx, y: cy + vy };
|
|
53
|
+
case 270:
|
|
54
|
+
return { x: W + vx - cy, y: H + vy - cx };
|
|
55
|
+
default:
|
|
56
|
+
return { x: cx + vx, y: H + vy - cy };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function convertCanvasRectToPdf(t, rect) {
|
|
60
|
+
const a = convertCanvasPointToPdf(t, rect.x, rect.y);
|
|
61
|
+
const b = convertCanvasPointToPdf(t, rect.x + rect.width, rect.y + rect.height);
|
|
62
|
+
return [
|
|
63
|
+
Math.min(a.x, b.x),
|
|
64
|
+
Math.min(a.y, b.y),
|
|
65
|
+
Math.max(a.x, b.x),
|
|
66
|
+
Math.max(a.y, b.y)
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
export function pageTransformFromPdfLibPage(page) {
|
|
70
|
+
const W_u = page.getWidth();
|
|
71
|
+
const H_u = page.getHeight();
|
|
72
|
+
const rotation = (page.getRotation().angle % 360 + 360) % 360;
|
|
73
|
+
const swap = rotation % 180 !== 0;
|
|
74
|
+
return {
|
|
75
|
+
width: swap ? H_u : W_u,
|
|
76
|
+
height: swap ? W_u : H_u,
|
|
77
|
+
rotation,
|
|
78
|
+
viewOffsetX: 0,
|
|
79
|
+
viewOffsetY: 0
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PageTransform } from './transform.js';
|
|
1
2
|
export interface RawPdfTextObject {
|
|
2
3
|
str?: unknown;
|
|
3
4
|
}
|
|
@@ -43,7 +44,10 @@ export interface StampAppearance {
|
|
|
43
44
|
}
|
|
44
45
|
export interface DecodeContext {
|
|
45
46
|
pageNumber: number;
|
|
46
|
-
|
|
47
|
+
/** Page coord transform — applies the page's intrinsic `/Rotate` so
|
|
48
|
+
* annotation `/Rect` values in unrotated user space land at the right
|
|
49
|
+
* canvas pixels regardless of page rotation. */
|
|
50
|
+
pageTransform: PageTransform;
|
|
47
51
|
allAnnotations: RawPdfAnnotation[];
|
|
48
52
|
stampAppearances?: Map<string, StampAppearance>;
|
|
49
53
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Konva from 'konva';
|
|
2
2
|
import { type AnnotationType, type IAnnotationComment, type IAnnotationStore, type PdfjsAnnotationSubtype } from '../engine/types.js';
|
|
3
3
|
import { type RawPdfAnnotation } from './types.js';
|
|
4
|
+
import { type PageTransform } from './transform.js';
|
|
4
5
|
type Rect = {
|
|
5
6
|
x: number;
|
|
6
7
|
y: number;
|
|
@@ -17,8 +18,8 @@ export declare function getAnnotationText(annotation: RawPdfAnnotation): string;
|
|
|
17
18
|
export declare function getFreeTextContent(annotation: RawPdfAnnotation): string;
|
|
18
19
|
export declare function getAnnotationDate(annotation: RawPdfAnnotation): string;
|
|
19
20
|
export declare function getComments(annotation: RawPdfAnnotation, allAnnotations: RawPdfAnnotation[]): IAnnotationComment[];
|
|
20
|
-
export declare function rectFromPdfRect(input: unknown,
|
|
21
|
-
export declare function pointFromPdfPoint(input: unknown,
|
|
21
|
+
export declare function rectFromPdfRect(input: unknown, transform: PageTransform): Rect | null;
|
|
22
|
+
export declare function pointFromPdfPoint(input: unknown, transform: PageTransform): {
|
|
22
23
|
x: number;
|
|
23
24
|
y: number;
|
|
24
25
|
} | null;
|
|
@@ -26,9 +27,9 @@ export declare function localPointFromPdfPoint(input: unknown): {
|
|
|
26
27
|
x: number;
|
|
27
28
|
y: number;
|
|
28
29
|
} | null;
|
|
29
|
-
export declare function rectFromQuadPoints(input: unknown,
|
|
30
|
-
export declare function pointsFromInkList(input: unknown,
|
|
31
|
-
export declare function linePointsFromCoordinates(input: unknown,
|
|
30
|
+
export declare function rectFromQuadPoints(input: unknown, transform: PageTransform): Rect | null;
|
|
31
|
+
export declare function pointsFromInkList(input: unknown, transform: PageTransform): number[];
|
|
32
|
+
export declare function linePointsFromCoordinates(input: unknown, transform: PageTransform): number[] | null;
|
|
32
33
|
export declare function normalizedStrokeWidth(annotation: RawPdfAnnotation, fallback?: number, options?: StrokeWidthOptions): number;
|
|
33
34
|
export declare function dashedBorder(annotation: RawPdfAnnotation): number[];
|
|
34
35
|
export declare function hasArrowEnding(value: unknown): boolean;
|