kviewer 0.0.6 → 0.0.8
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/runtime/annotation/engine/editor/editor.js +13 -1
- package/dist/runtime/annotation/pdf-export/parse_circle.js +5 -2
- package/dist/runtime/annotation/pdf-export/parse_polyline.js +5 -2
- package/dist/runtime/annotation/pdf-export/parse_square.js +5 -2
- package/dist/runtime/annotation/pdf-export/parse_stamp.js +4 -2
- package/dist/runtime/annotation/pdf-export/parse_text.js +3 -1
- package/dist/runtime/annotation/pdf-export/parse_utils.d.ts +26 -0
- package/dist/runtime/annotation/pdf-export/parse_utils.js +54 -0
- package/package.json +1 -1
package/dist/module.json
CHANGED
|
@@ -50,7 +50,19 @@ export class Editor {
|
|
|
50
50
|
id,
|
|
51
51
|
pageNumber,
|
|
52
52
|
konvaString: konvaGroup.toJSON(),
|
|
53
|
-
|
|
53
|
+
// Do NOT use konvaGroup.getClientRect() directly here — it includes the
|
|
54
|
+
// stage's zoom scale, which would inflate annotation coordinates in the
|
|
55
|
+
// exported PDF. We divide by scale to get page-space coordinates.
|
|
56
|
+
konvaClientRect: (() => {
|
|
57
|
+
const scale = konvaGroup.getStage()?.scaleX() ?? 1;
|
|
58
|
+
const raw = konvaGroup.getClientRect();
|
|
59
|
+
return {
|
|
60
|
+
x: raw.x / scale,
|
|
61
|
+
y: raw.y / scale,
|
|
62
|
+
width: raw.width / scale,
|
|
63
|
+
height: raw.height / scale
|
|
64
|
+
};
|
|
65
|
+
})(),
|
|
54
66
|
title: this.userName,
|
|
55
67
|
type: annotation.type,
|
|
56
68
|
pdfjsType: annotation.pdfjsAnnotationType,
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { PDFName, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
3
|
import { AnnotationParser } from "./parse.js";
|
|
4
|
+
import { computeBoundsFromKonvaString } from "./parse_utils.js";
|
|
4
5
|
const UNKNOWN_USER = "Unknown user";
|
|
5
6
|
export class CircleParser extends AnnotationParser {
|
|
6
7
|
async parse() {
|
|
7
8
|
const { annotation, page, pdfDoc } = this;
|
|
8
9
|
const context = pdfDoc.context;
|
|
9
10
|
const pageHeight = page.getHeight();
|
|
11
|
+
const bounds = computeBoundsFromKonvaString(annotation.konvaString);
|
|
12
|
+
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, pageHeight);
|
|
10
13
|
const mainAnn = context.obj({
|
|
11
14
|
Type: PDFName.of("Annot"),
|
|
12
15
|
Subtype: PDFName.of("Circle"),
|
|
13
|
-
Rect:
|
|
16
|
+
Rect: rect,
|
|
14
17
|
C: rgbToPdfColor(annotation.color || void 0),
|
|
15
18
|
T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
|
|
16
19
|
Contents: stringToPDFHexString(annotation.contentsObj?.text || ""),
|
|
@@ -24,7 +27,7 @@ export class CircleParser extends AnnotationParser {
|
|
|
24
27
|
const replyAnn = context.obj({
|
|
25
28
|
Type: PDFName.of("Annot"),
|
|
26
29
|
Subtype: PDFName.of("Text"),
|
|
27
|
-
Rect:
|
|
30
|
+
Rect: rect,
|
|
28
31
|
Contents: stringToPDFHexString(comment.content),
|
|
29
32
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
30
33
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PDFName, PDFNumber, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
3
|
import { AnnotationParser } from "./parse.js";
|
|
4
|
+
import { computeBoundsFromKonvaString } from "./parse_utils.js";
|
|
4
5
|
const UNKNOWN_USER = "Unknown user";
|
|
5
6
|
function parseSvgPathToPoints(data) {
|
|
6
7
|
const commands = data.match(/[a-z][^a-z]*/gi) || [];
|
|
@@ -59,7 +60,9 @@ export class PolylineParser extends AnnotationParser {
|
|
|
59
60
|
const mainAnn = context.obj({
|
|
60
61
|
Type: PDFName.of("Annot"),
|
|
61
62
|
Subtype: PDFName.of("Ink"),
|
|
62
|
-
Rect
|
|
63
|
+
// Compute Rect from konvaString so it reflects the current position
|
|
64
|
+
// even when konvaClientRect is stale after a move/resize.
|
|
65
|
+
Rect: convertKonvaRectToPdfRect(computeBoundsFromKonvaString(annotation.konvaString) ?? annotation.konvaClientRect, pageHeight),
|
|
63
66
|
InkList: inkList,
|
|
64
67
|
C: context.obj([PDFNumber.of(r), PDFNumber.of(g), PDFNumber.of(b)]),
|
|
65
68
|
T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
|
|
@@ -76,7 +79,7 @@ export class PolylineParser extends AnnotationParser {
|
|
|
76
79
|
const replyAnn = context.obj({
|
|
77
80
|
Type: PDFName.of("Annot"),
|
|
78
81
|
Subtype: PDFName.of("Text"),
|
|
79
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight),
|
|
82
|
+
Rect: convertKonvaRectToPdfRect(computeBoundsFromKonvaString(annotation.konvaString) ?? annotation.konvaClientRect, pageHeight),
|
|
80
83
|
Contents: stringToPDFHexString(comment.content),
|
|
81
84
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
82
85
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { PDFName, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
3
|
import { AnnotationParser } from "./parse.js";
|
|
4
|
+
import { computeBoundsFromKonvaString } from "./parse_utils.js";
|
|
4
5
|
const UNKNOWN_USER = "Unknown user";
|
|
5
6
|
export class SquareParser extends AnnotationParser {
|
|
6
7
|
async parse() {
|
|
7
8
|
const { annotation, page, pdfDoc } = this;
|
|
8
9
|
const context = pdfDoc.context;
|
|
9
10
|
const pageHeight = page.getHeight();
|
|
11
|
+
const bounds = computeBoundsFromKonvaString(annotation.konvaString);
|
|
12
|
+
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, pageHeight);
|
|
10
13
|
const mainAnn = context.obj({
|
|
11
14
|
Type: PDFName.of("Annot"),
|
|
12
15
|
Subtype: PDFName.of("Square"),
|
|
13
|
-
Rect:
|
|
16
|
+
Rect: rect,
|
|
14
17
|
C: rgbToPdfColor(annotation.color || void 0),
|
|
15
18
|
T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
|
|
16
19
|
Contents: stringToPDFHexString(annotation.contentsObj?.text || ""),
|
|
@@ -24,7 +27,7 @@ export class SquareParser extends AnnotationParser {
|
|
|
24
27
|
const replyAnn = context.obj({
|
|
25
28
|
Type: PDFName.of("Annot"),
|
|
26
29
|
Subtype: PDFName.of("Text"),
|
|
27
|
-
Rect:
|
|
30
|
+
Rect: rect,
|
|
28
31
|
Contents: stringToPDFHexString(comment.content),
|
|
29
32
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
30
33
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PDFName, PDFNumber, PDFRawStream, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, stringToPDFHexString } from "../engine/utils.js";
|
|
3
3
|
import { AnnotationParser } from "./parse.js";
|
|
4
|
+
import { computeBoundsFromKonvaString } from "./parse_utils.js";
|
|
4
5
|
const UNKNOWN_USER = "Unknown user";
|
|
5
6
|
const SVG_RASTER_SCALE = 4;
|
|
6
7
|
const MAX_RASTER_EDGE = 4096;
|
|
@@ -122,7 +123,8 @@ export class StampParser extends AnnotationParser {
|
|
|
122
123
|
async parse() {
|
|
123
124
|
const { annotation, page, pdfDoc } = this;
|
|
124
125
|
const context = pdfDoc.context;
|
|
125
|
-
const
|
|
126
|
+
const bounds = computeBoundsFromKonvaString(annotation.konvaString);
|
|
127
|
+
const [x1, y1, x2, y2] = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, page.getHeight());
|
|
126
128
|
const pageWidth = page.getWidth();
|
|
127
129
|
const pageHeight = page.getHeight();
|
|
128
130
|
const left = Math.max(0, Math.min(x1, pageWidth));
|
|
@@ -179,7 +181,7 @@ export class StampParser extends AnnotationParser {
|
|
|
179
181
|
const replyAnn = context.obj({
|
|
180
182
|
Type: PDFName.of("Annot"),
|
|
181
183
|
Subtype: PDFName.of("Text"),
|
|
182
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight),
|
|
184
|
+
Rect: convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, pageHeight),
|
|
183
185
|
Contents: stringToPDFHexString(comment.content),
|
|
184
186
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
185
187
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { PDFName, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
3
|
import { AnnotationParser } from "./parse.js";
|
|
4
|
+
import { computeBoundsFromKonvaString } from "./parse_utils.js";
|
|
4
5
|
const UNKNOWN_USER = "Unknown user";
|
|
5
6
|
export class TextParser extends AnnotationParser {
|
|
6
7
|
async parse() {
|
|
7
8
|
const { annotation, page, pdfDoc } = this;
|
|
8
|
-
const
|
|
9
|
+
const bounds = computeBoundsFromKonvaString(annotation.konvaString);
|
|
10
|
+
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, page.getHeight());
|
|
9
11
|
const context = pdfDoc.context;
|
|
10
12
|
const mainAnn = context.obj({
|
|
11
13
|
Type: PDFName.of("Annot"),
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities for PDF export parsers.
|
|
3
|
+
*
|
|
4
|
+
* The main purpose is to compute annotation bounding boxes from the
|
|
5
|
+
* serialised Konva JSON (`konvaString`) rather than relying on
|
|
6
|
+
* `konvaClientRect`, which can become stale after the annotation is
|
|
7
|
+
* moved or resized.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Compute the bounding box of a Konva group from its serialised JSON,
|
|
11
|
+
* applying group-level transforms (x, y, scaleX, scaleY) to every child.
|
|
12
|
+
*
|
|
13
|
+
* Handles:
|
|
14
|
+
* - Rect / Image / Text children → x, y, width, height
|
|
15
|
+
* - Ellipse children → x (centre), y (centre), radiusX, radiusY
|
|
16
|
+
*
|
|
17
|
+
* Returns `null` when the string cannot be parsed or contains no children
|
|
18
|
+
* with computable dimensions. Callers should fall back to
|
|
19
|
+
* `annotation.konvaClientRect` in that case.
|
|
20
|
+
*/
|
|
21
|
+
export declare function computeBoundsFromKonvaString(konvaString: string): {
|
|
22
|
+
x: number;
|
|
23
|
+
y: number;
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
} | null;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function computeBoundsFromKonvaString(konvaString) {
|
|
2
|
+
let konvaGroup;
|
|
3
|
+
try {
|
|
4
|
+
konvaGroup = JSON.parse(konvaString);
|
|
5
|
+
} catch {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const groupX = konvaGroup.attrs?.x ?? 0;
|
|
9
|
+
const groupY = konvaGroup.attrs?.y ?? 0;
|
|
10
|
+
const scaleX = konvaGroup.attrs?.scaleX ?? 1;
|
|
11
|
+
const scaleY = konvaGroup.attrs?.scaleY ?? 1;
|
|
12
|
+
let minX = Infinity;
|
|
13
|
+
let minY = Infinity;
|
|
14
|
+
let maxX = -Infinity;
|
|
15
|
+
let maxY = -Infinity;
|
|
16
|
+
for (const child of konvaGroup.children ?? []) {
|
|
17
|
+
const attrs = child.attrs;
|
|
18
|
+
if (!attrs) continue;
|
|
19
|
+
let cx;
|
|
20
|
+
let cy;
|
|
21
|
+
let cw;
|
|
22
|
+
let ch;
|
|
23
|
+
if (child.className === "Ellipse") {
|
|
24
|
+
const rx = attrs.radiusX ?? 0;
|
|
25
|
+
const ry = attrs.radiusY ?? 0;
|
|
26
|
+
if (rx <= 0 && ry <= 0) continue;
|
|
27
|
+
cx = (attrs.x ?? 0) - rx;
|
|
28
|
+
cy = (attrs.y ?? 0) - ry;
|
|
29
|
+
cw = rx * 2;
|
|
30
|
+
ch = ry * 2;
|
|
31
|
+
} else {
|
|
32
|
+
cx = attrs.x ?? 0;
|
|
33
|
+
cy = attrs.y ?? 0;
|
|
34
|
+
cw = attrs.width ?? 0;
|
|
35
|
+
ch = attrs.height ?? 0;
|
|
36
|
+
if (cw <= 0 && ch <= 0) continue;
|
|
37
|
+
}
|
|
38
|
+
const x = groupX + cx * scaleX;
|
|
39
|
+
const y = groupY + cy * scaleY;
|
|
40
|
+
const w = Math.abs(cw * scaleX);
|
|
41
|
+
const h = Math.abs(ch * scaleY);
|
|
42
|
+
minX = Math.min(minX, x);
|
|
43
|
+
minY = Math.min(minY, y);
|
|
44
|
+
maxX = Math.max(maxX, x + w);
|
|
45
|
+
maxY = Math.max(maxY, y + h);
|
|
46
|
+
}
|
|
47
|
+
if (!Number.isFinite(minX) || !Number.isFinite(minY)) return null;
|
|
48
|
+
return {
|
|
49
|
+
x: minX,
|
|
50
|
+
y: minY,
|
|
51
|
+
width: maxX - minX,
|
|
52
|
+
height: maxY - minY
|
|
53
|
+
};
|
|
54
|
+
}
|