kviewer 0.1.1 → 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/README.md +12 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +26 -2
- 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/dist/runtime/style.css +1 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -34,12 +34,24 @@ Add it to your `nuxt.config.ts`:
|
|
|
34
34
|
```ts
|
|
35
35
|
export default defineNuxtConfig({
|
|
36
36
|
modules: ['kviewer'],
|
|
37
|
+
css: ['~/assets/css/main.css'],
|
|
37
38
|
kviewer: {
|
|
38
39
|
prefix: 'K', // component prefix (default)
|
|
39
40
|
},
|
|
40
41
|
})
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
KViewer's UI is built on [Nuxt UI](https://ui.nuxt.com) + Tailwind CSS. Create the main stylesheet with three imports:
|
|
45
|
+
|
|
46
|
+
```css
|
|
47
|
+
/* assets/css/main.css */
|
|
48
|
+
@import 'tailwindcss';
|
|
49
|
+
@import '@nuxt/ui';
|
|
50
|
+
@import 'kviewer';
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`@nuxt/ui` is installed and registered automatically — you don't add it to `modules`, and there's no `@source` path to maintain. The `@import 'kviewer'` line registers KViewer's components as a Tailwind source.
|
|
54
|
+
|
|
43
55
|
## Basic Usage
|
|
44
56
|
|
|
45
57
|
```vue
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
|
-
import { defineNuxtModule, createResolver, addPlugin, addComponentsDir, addImports } from '@nuxt/kit';
|
|
3
|
+
import { defineNuxtModule, createResolver, extendViteConfig, addPlugin, addComponentsDir, addImports } from '@nuxt/kit';
|
|
4
4
|
export { KVIEWER_EMBED_PROTOCOL_VERSION, KViewerEmbedClient, useKViewerEmbedBridge } from '../dist/runtime/public-types.js';
|
|
5
5
|
|
|
6
6
|
const module$1 = defineNuxtModule({
|
|
@@ -13,8 +13,32 @@ const module$1 = defineNuxtModule({
|
|
|
13
13
|
prefix: "K",
|
|
14
14
|
minRenderPixelRatio: 2
|
|
15
15
|
},
|
|
16
|
-
|
|
16
|
+
// Declare @nuxt/ui as a hard dependency so Nuxt resolves it as a
|
|
17
|
+
// top-level module before our setup() runs. We can NOT just call
|
|
18
|
+
// installModule('@nuxt/ui') from setup(): Nuxt UI declares its own
|
|
19
|
+
// transitive deps (@nuxt/icon, @nuxtjs/color-mode, @nuxtjs/mdc, …)
|
|
20
|
+
// via the same `moduleDependencies` hook, and that hook only fires
|
|
21
|
+
// for modules resolved at the top level — not for ones installed
|
|
22
|
+
// from inside another module's setup. Going through this hook gives
|
|
23
|
+
// us the same chain Nuxt UI gets when the consumer lists it in their
|
|
24
|
+
// own `modules` array, while still freeing them from having to. The
|
|
25
|
+
// consumer's own '@nuxt/ui' entry (and any options under nuxt.ui)
|
|
26
|
+
// remains authoritative because Nuxt deduplicates modules by name.
|
|
27
|
+
moduleDependencies: {
|
|
28
|
+
"@nuxt/ui": {}
|
|
29
|
+
},
|
|
30
|
+
async setup(options, nuxt) {
|
|
17
31
|
const { resolve } = createResolver(import.meta.url);
|
|
32
|
+
extendViteConfig((config) => {
|
|
33
|
+
config.optimizeDeps ||= {};
|
|
34
|
+
config.optimizeDeps.include ||= [];
|
|
35
|
+
config.optimizeDeps.include.push(
|
|
36
|
+
"konva",
|
|
37
|
+
"pdf-lib",
|
|
38
|
+
"pdfjs-dist/legacy/build/pdf.mjs",
|
|
39
|
+
"pdfjs-dist/web/pdf_viewer.mjs"
|
|
40
|
+
);
|
|
41
|
+
}, { client: true, server: false });
|
|
18
42
|
nuxt.options.runtimeConfig.public.kviewer = {
|
|
19
43
|
...nuxt.options.runtimeConfig.public.kviewer,
|
|
20
44
|
minRenderPixelRatio: options.minRenderPixelRatio ?? 2
|
|
@@ -37,7 +37,7 @@ export declare const defaultOptions: {
|
|
|
37
37
|
};
|
|
38
38
|
setting: {
|
|
39
39
|
COLOR: string | undefined;
|
|
40
|
-
FONT_SIZE: number
|
|
40
|
+
FONT_SIZE: number;
|
|
41
41
|
HIGHLIGHT_COLOR: string | undefined;
|
|
42
42
|
STRIKEOUT_COLOR: string | undefined;
|
|
43
43
|
UNDERLINE_COLOR: string | undefined;
|
|
@@ -16,7 +16,7 @@ const colors = [
|
|
|
16
16
|
"#7828a4",
|
|
17
17
|
"#ff00ff"
|
|
18
18
|
];
|
|
19
|
-
const fontSizes = [14, 16, 18, 20, 22, 24];
|
|
19
|
+
const fontSizes = [8, 10, 12, 14, 16, 18, 20, 22, 24];
|
|
20
20
|
export const defaultOptions = {
|
|
21
21
|
colors,
|
|
22
22
|
fontSize: fontSizes,
|
|
@@ -67,7 +67,7 @@ export const defaultOptions = {
|
|
|
67
67
|
},
|
|
68
68
|
setting: {
|
|
69
69
|
COLOR: colors[0],
|
|
70
|
-
FONT_SIZE:
|
|
70
|
+
FONT_SIZE: 18,
|
|
71
71
|
HIGHLIGHT_COLOR: colors[3],
|
|
72
72
|
STRIKEOUT_COLOR: colors[1],
|
|
73
73
|
UNDERLINE_COLOR: colors[7],
|
|
@@ -48,7 +48,7 @@ export declare class Painter {
|
|
|
48
48
|
freehandGroupingDelay?: number;
|
|
49
49
|
setExternalGesturesEnabled?: (enabled: boolean) => void;
|
|
50
50
|
});
|
|
51
|
-
|
|
51
|
+
editFreeText(id: string): Promise<void>;
|
|
52
52
|
private bindGlobalEvents;
|
|
53
53
|
private globalKeyUpHandler;
|
|
54
54
|
private createKonvaStage;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PDFHexString } from 'pdf-lib';
|
|
2
|
+
import { type PageTransform } from '../pdf-import/transform.js';
|
|
2
3
|
export declare function getRGB(color: string): number[];
|
|
3
4
|
export declare function rgbToPdfColor(input: string | undefined): [number, number, number];
|
|
4
5
|
export declare function isElementInDOM(element: HTMLElement): boolean;
|
|
@@ -19,7 +20,7 @@ export declare function convertKonvaRectToPdfRect(konvaRect: {
|
|
|
19
20
|
y: number;
|
|
20
21
|
width: number;
|
|
21
22
|
height: number;
|
|
22
|
-
},
|
|
23
|
+
}, pageTransform: PageTransform): [number, number, number, number];
|
|
23
24
|
export declare function stringToPDFHexString(input: string): PDFHexString;
|
|
24
25
|
export declare function getTimestampString(date?: Date): string;
|
|
25
26
|
export declare function normalizeColor(input: string): string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Konva from "konva";
|
|
2
2
|
import { PDFHexString } from "pdf-lib";
|
|
3
|
+
import { convertCanvasRectToPdf } from "../pdf-import/transform.js";
|
|
3
4
|
export function getRGB(color) {
|
|
4
5
|
if (color.startsWith("#")) {
|
|
5
6
|
const colorRGB = Number.parseInt(color.slice(1), 16);
|
|
@@ -149,13 +150,8 @@ export function getPDFDateTimestamp(dateString) {
|
|
|
149
150
|
const date = new Date(Date.UTC(year, month, day, hour, minute, second));
|
|
150
151
|
return date.getTime() - tzOffset * 60 * 1e3;
|
|
151
152
|
}
|
|
152
|
-
export function convertKonvaRectToPdfRect(konvaRect,
|
|
153
|
-
|
|
154
|
-
const pdfX1 = x;
|
|
155
|
-
const pdfY1 = pageHeight - y - height;
|
|
156
|
-
const pdfX2 = pdfX1 + width;
|
|
157
|
-
const pdfY2 = pdfY1 + height;
|
|
158
|
-
return [pdfX1, pdfY1, pdfX2, pdfY2];
|
|
153
|
+
export function convertKonvaRectToPdfRect(konvaRect, pageTransform) {
|
|
154
|
+
return convertCanvasRectToPdf(pageTransform, konvaRect);
|
|
159
155
|
}
|
|
160
156
|
export function stringToPDFHexString(input) {
|
|
161
157
|
const bom = [254, 255];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { PDFArray, PDFDict, PDFDocument, PDFName, PDFRef, PDFString } from "pdf-lib";
|
|
1
|
+
import { degrees, PDFArray, PDFDict, PDFDocument, PDFName, PDFRef, PDFString } from "pdf-lib";
|
|
2
2
|
import { PdfjsAnnotationType } from "../engine/types.js";
|
|
3
|
+
import { pageTransformFromPdfLibPage } from "../pdf-import/transform.js";
|
|
3
4
|
import { writeFormFieldsToPdf } from "./export-form-fields.js";
|
|
4
5
|
import { CircleParser } from "./parse_circle.js";
|
|
5
6
|
import { FreeTextParser } from "./parse_freetext.js";
|
|
@@ -144,41 +145,94 @@ async function embedDataUrlImage(pdfDoc, dataUrl) {
|
|
|
144
145
|
}
|
|
145
146
|
return pdfDoc.embedPng(base64);
|
|
146
147
|
}
|
|
148
|
+
function loadImagesForNode(node) {
|
|
149
|
+
if (typeof window === "undefined") return [];
|
|
150
|
+
const findFn = node.find;
|
|
151
|
+
if (typeof findFn !== "function") return [];
|
|
152
|
+
const images = findFn.call(node, "Image");
|
|
153
|
+
const tasks = [];
|
|
154
|
+
for (const img of images) {
|
|
155
|
+
if (img.image()) continue;
|
|
156
|
+
const src = img.getAttr("base64");
|
|
157
|
+
if (typeof src !== "string" || src.length === 0) continue;
|
|
158
|
+
tasks.push(new Promise((resolve) => {
|
|
159
|
+
const el = new window.Image();
|
|
160
|
+
el.crossOrigin = "anonymous";
|
|
161
|
+
el.onload = () => {
|
|
162
|
+
img.image(el);
|
|
163
|
+
resolve();
|
|
164
|
+
};
|
|
165
|
+
el.onerror = () => {
|
|
166
|
+
resolve();
|
|
167
|
+
};
|
|
168
|
+
el.src = src;
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
return tasks;
|
|
172
|
+
}
|
|
147
173
|
async function flattenPageAnnotations(pdfDoc, page, annotations) {
|
|
148
174
|
if (annotations.length === 0) return;
|
|
149
175
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
150
176
|
throw new TypeError("Flatten export requires a browser environment");
|
|
151
177
|
}
|
|
152
178
|
const Konva = (await import("konva")).default;
|
|
153
|
-
const
|
|
154
|
-
const
|
|
179
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
180
|
+
const stageWidth = Math.max(1, Math.floor(pageTransform.width));
|
|
181
|
+
const stageHeight = Math.max(1, Math.floor(pageTransform.height));
|
|
155
182
|
const stage = new Konva.Stage({
|
|
156
183
|
container: document.createElement("div"),
|
|
157
|
-
width,
|
|
158
|
-
height,
|
|
184
|
+
width: stageWidth,
|
|
185
|
+
height: stageHeight,
|
|
159
186
|
listening: false
|
|
160
187
|
});
|
|
161
188
|
const layer = new Konva.Layer({ listening: false });
|
|
162
189
|
stage.add(layer);
|
|
190
|
+
const imageLoaders = [];
|
|
163
191
|
for (const annotation of annotations) {
|
|
164
192
|
try {
|
|
165
193
|
const node = Konva.Node.create(annotation.konvaString);
|
|
166
194
|
if (node instanceof Konva.Node) {
|
|
167
195
|
layer.add(node);
|
|
196
|
+
imageLoaders.push(...loadImagesForNode(node));
|
|
168
197
|
}
|
|
169
198
|
} catch {
|
|
170
199
|
console.warn(`Unable to flatten annotation ${annotation.id}; skipping`);
|
|
171
200
|
}
|
|
172
201
|
}
|
|
202
|
+
await Promise.all(imageLoaders);
|
|
173
203
|
layer.draw();
|
|
174
204
|
const dataUrl = stage.toDataURL({ pixelRatio: 2 });
|
|
175
205
|
stage.destroy();
|
|
176
206
|
const image = await embedDataUrlImage(pdfDoc, dataUrl);
|
|
207
|
+
const W_u = page.getWidth();
|
|
208
|
+
const H_u = page.getHeight();
|
|
209
|
+
const dispW = pageTransform.width;
|
|
210
|
+
const dispH = pageTransform.height;
|
|
211
|
+
let drawX = 0;
|
|
212
|
+
let drawY = 0;
|
|
213
|
+
switch (pageTransform.rotation) {
|
|
214
|
+
case 90:
|
|
215
|
+
drawX = W_u;
|
|
216
|
+
drawY = 0;
|
|
217
|
+
break;
|
|
218
|
+
case 180:
|
|
219
|
+
drawX = W_u;
|
|
220
|
+
drawY = H_u;
|
|
221
|
+
break;
|
|
222
|
+
case 270:
|
|
223
|
+
drawX = 0;
|
|
224
|
+
drawY = H_u;
|
|
225
|
+
break;
|
|
226
|
+
default:
|
|
227
|
+
drawX = 0;
|
|
228
|
+
drawY = 0;
|
|
229
|
+
}
|
|
177
230
|
page.drawImage(image, {
|
|
178
|
-
x:
|
|
179
|
-
y:
|
|
180
|
-
width:
|
|
181
|
-
height:
|
|
231
|
+
x: drawX,
|
|
232
|
+
y: drawY,
|
|
233
|
+
width: dispW,
|
|
234
|
+
height: dispH,
|
|
235
|
+
rotate: degrees(pageTransform.rotation)
|
|
182
236
|
});
|
|
183
237
|
}
|
|
184
238
|
function groupByPage(annotations) {
|
|
@@ -1,5 +1,6 @@
|
|
|
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";
|
|
@@ -7,9 +8,9 @@ export class CircleParser extends AnnotationParser {
|
|
|
7
8
|
async parse() {
|
|
8
9
|
const { annotation, page, pdfDoc } = this;
|
|
9
10
|
const context = pdfDoc.context;
|
|
10
|
-
const
|
|
11
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
11
12
|
const bounds = computeBoundsFromKonvaString(annotation.konvaString);
|
|
12
|
-
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect,
|
|
13
|
+
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, pageTransform);
|
|
13
14
|
const mainAnn = context.obj({
|
|
14
15
|
Type: PDFName.of("Annot"),
|
|
15
16
|
Subtype: PDFName.of("Circle"),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { PDFName, PDFNumber, PDFRawStream, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
|
+
import { convertCanvasRectToPdf, pageTransformFromPdfLibPage } from "../pdf-import/transform.js";
|
|
3
4
|
import { AnnotationParser } from "./parse.js";
|
|
5
|
+
import { formAppearanceMatrix } from "./parse_utils.js";
|
|
4
6
|
const UNKNOWN_USER = "Unknown user";
|
|
5
7
|
function formatPdfNumber(value) {
|
|
6
8
|
if (!Number.isFinite(value)) return "0";
|
|
@@ -18,7 +20,7 @@ export class FreeTextParser extends AnnotationParser {
|
|
|
18
20
|
async parse() {
|
|
19
21
|
const { annotation, page, pdfDoc } = this;
|
|
20
22
|
const context = pdfDoc.context;
|
|
21
|
-
const
|
|
23
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
22
24
|
let konvaGroup;
|
|
23
25
|
try {
|
|
24
26
|
konvaGroup = JSON.parse(annotation.konvaString);
|
|
@@ -47,18 +49,20 @@ export class FreeTextParser extends AnnotationParser {
|
|
|
47
49
|
const longestLine = lines.reduce((max, line) => Math.max(max, line.length), 0);
|
|
48
50
|
const textWidthFromNode = textNode?.width ? textNode.width * scaleX : longestLine * estimatedCharWidth;
|
|
49
51
|
const textWidth = Math.max(1, textWidthFromNode);
|
|
50
|
-
const pdfX1 =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const [pdfX1, pdfY1, pdfX2, pdfY2] = convertCanvasRectToPdf(pageTransform, {
|
|
53
|
+
x: nodeX,
|
|
54
|
+
y: nodeY,
|
|
55
|
+
width: textWidth,
|
|
56
|
+
height: textHeight
|
|
57
|
+
});
|
|
54
58
|
const rect = [
|
|
55
59
|
PDFNumber.of(pdfX1),
|
|
56
60
|
PDFNumber.of(pdfY1),
|
|
57
61
|
PDFNumber.of(pdfX2),
|
|
58
62
|
PDFNumber.of(pdfY2)
|
|
59
63
|
];
|
|
60
|
-
const appearanceWidth = Math.max(1,
|
|
61
|
-
const appearanceHeight = Math.max(1,
|
|
64
|
+
const appearanceWidth = Math.max(1, textWidth);
|
|
65
|
+
const appearanceHeight = Math.max(1, textHeight);
|
|
62
66
|
const commands = ["q"];
|
|
63
67
|
for (let i = 0; i < lines.length; i++) {
|
|
64
68
|
const line = lines[i] ?? "";
|
|
@@ -89,11 +93,17 @@ export class FreeTextParser extends AnnotationParser {
|
|
|
89
93
|
Encoding: PDFName.of("WinAnsiEncoding")
|
|
90
94
|
});
|
|
91
95
|
const fontRef = context.register(fontDict);
|
|
96
|
+
const appearanceMatrix = formAppearanceMatrix(
|
|
97
|
+
pageTransform.rotation,
|
|
98
|
+
appearanceWidth,
|
|
99
|
+
appearanceHeight
|
|
100
|
+
);
|
|
92
101
|
const appearanceStream = PDFRawStream.of(
|
|
93
102
|
context.obj({
|
|
94
103
|
Type: PDFName.of("XObject"),
|
|
95
104
|
Subtype: PDFName.of("Form"),
|
|
96
105
|
BBox: context.obj([0, 0, appearanceWidth, appearanceHeight]),
|
|
106
|
+
Matrix: context.obj(appearanceMatrix.map((n) => PDFNumber.of(n))),
|
|
97
107
|
Resources: context.obj({
|
|
98
108
|
Font: context.obj({ Helvetica: fontRef })
|
|
99
109
|
})
|
|
@@ -125,7 +135,7 @@ export class FreeTextParser extends AnnotationParser {
|
|
|
125
135
|
const replyAnn = context.obj({
|
|
126
136
|
Type: PDFName.of("Annot"),
|
|
127
137
|
Subtype: PDFName.of("Text"),
|
|
128
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
138
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
129
139
|
Contents: stringToPDFHexString(comment.content),
|
|
130
140
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
131
141
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { PDFName, PDFNumber, PDFRawStream, PDFString } from "pdf-lib";
|
|
2
2
|
import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
|
|
3
|
+
import {
|
|
4
|
+
convertCanvasPointToPdf,
|
|
5
|
+
convertCanvasRectToPdf,
|
|
6
|
+
pageTransformFromPdfLibPage
|
|
7
|
+
} from "../pdf-import/transform.js";
|
|
3
8
|
import { AnnotationParser } from "./parse.js";
|
|
4
9
|
const UNKNOWN_USER = "Unknown user";
|
|
5
10
|
function formatPdfNumber(value) {
|
|
@@ -21,18 +26,14 @@ function parseHighlightRects(konvaString) {
|
|
|
21
26
|
return { x, y, width, height };
|
|
22
27
|
}).filter((rect) => rect.width > 0 && rect.height > 0);
|
|
23
28
|
}
|
|
24
|
-
function buildQuadPoints(rects,
|
|
29
|
+
function buildQuadPoints(rects, t) {
|
|
25
30
|
const quadPoints = [];
|
|
26
|
-
for (const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
const y1 = pageHeight - y;
|
|
33
|
-
const x2 = x + width;
|
|
34
|
-
const y2 = pageHeight - (y + height);
|
|
35
|
-
quadPoints.push(x1, y1, x2, y1, x1, y2, x2, y2);
|
|
31
|
+
for (const r of rects) {
|
|
32
|
+
const tl = convertCanvasPointToPdf(t, r.x, r.y);
|
|
33
|
+
const tr = convertCanvasPointToPdf(t, r.x + r.width, r.y);
|
|
34
|
+
const bl = convertCanvasPointToPdf(t, r.x, r.y + r.height);
|
|
35
|
+
const br = convertCanvasPointToPdf(t, r.x + r.width, r.y + r.height);
|
|
36
|
+
quadPoints.push(tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y);
|
|
36
37
|
}
|
|
37
38
|
return quadPoints;
|
|
38
39
|
}
|
|
@@ -40,7 +41,7 @@ export class HighlightParser extends AnnotationParser {
|
|
|
40
41
|
async parse() {
|
|
41
42
|
const { annotation, page, pdfDoc } = this;
|
|
42
43
|
const context = pdfDoc.context;
|
|
43
|
-
const
|
|
44
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
44
45
|
let highlightRects = [];
|
|
45
46
|
try {
|
|
46
47
|
highlightRects = parseHighlightRects(annotation.konvaString);
|
|
@@ -55,21 +56,18 @@ export class HighlightParser extends AnnotationParser {
|
|
|
55
56
|
height: annotation.konvaClientRect.height
|
|
56
57
|
}];
|
|
57
58
|
}
|
|
58
|
-
const quadPoints = buildQuadPoints(highlightRects,
|
|
59
|
+
const quadPoints = buildQuadPoints(highlightRects, pageTransform);
|
|
59
60
|
const color = rgbToPdfColor(annotation.color || void 0);
|
|
61
|
+
const userRects = highlightRects.map((hr) => convertCanvasRectToPdf(pageTransform, hr));
|
|
60
62
|
let minX = Infinity;
|
|
61
63
|
let minY = Infinity;
|
|
62
64
|
let maxX = -Infinity;
|
|
63
65
|
let maxY = -Infinity;
|
|
64
|
-
for (const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (pdfLeft < minX) minX = pdfLeft;
|
|
70
|
-
if (pdfBottom < minY) minY = pdfBottom;
|
|
71
|
-
if (pdfRight > maxX) maxX = pdfRight;
|
|
72
|
-
if (pdfTop > maxY) maxY = pdfTop;
|
|
66
|
+
for (const [ux1, uy1, ux2, uy2] of userRects) {
|
|
67
|
+
if (ux1 < minX) minX = ux1;
|
|
68
|
+
if (uy1 < minY) minY = uy1;
|
|
69
|
+
if (ux2 > maxX) maxX = ux2;
|
|
70
|
+
if (uy2 > maxY) maxY = uy2;
|
|
73
71
|
}
|
|
74
72
|
const x1 = minX;
|
|
75
73
|
const y1 = minY;
|
|
@@ -84,13 +82,13 @@ export class HighlightParser extends AnnotationParser {
|
|
|
84
82
|
"/GS1 gs",
|
|
85
83
|
`${formatPdfNumber(r)} ${formatPdfNumber(g)} ${formatPdfNumber(b)} rg`
|
|
86
84
|
];
|
|
87
|
-
for (const
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
91
|
-
const
|
|
85
|
+
for (const [ux1, uy1, ux2, uy2] of userRects) {
|
|
86
|
+
const localX = ux1 - x1;
|
|
87
|
+
const localY = uy1 - y1;
|
|
88
|
+
const w = ux2 - ux1;
|
|
89
|
+
const h = uy2 - uy1;
|
|
92
90
|
commands.push(
|
|
93
|
-
`${formatPdfNumber(localX)} ${formatPdfNumber(localY)} ${formatPdfNumber(
|
|
91
|
+
`${formatPdfNumber(localX)} ${formatPdfNumber(localY)} ${formatPdfNumber(w)} ${formatPdfNumber(h)} re`,
|
|
94
92
|
"f"
|
|
95
93
|
);
|
|
96
94
|
}
|
|
@@ -133,7 +131,7 @@ export class HighlightParser extends AnnotationParser {
|
|
|
133
131
|
const replyAnn = context.obj({
|
|
134
132
|
Type: PDFName.of("Annot"),
|
|
135
133
|
Subtype: PDFName.of("Text"),
|
|
136
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
134
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
137
135
|
Contents: stringToPDFHexString(comment.content),
|
|
138
136
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
139
137
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PDFName, PDFNumber, PDFRawStream, 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
6
|
function formatPdfNumber(value) {
|
|
@@ -10,7 +11,7 @@ export class InkParser extends AnnotationParser {
|
|
|
10
11
|
async parse() {
|
|
11
12
|
const { annotation, page, pdfDoc } = this;
|
|
12
13
|
const context = pdfDoc.context;
|
|
13
|
-
const
|
|
14
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
14
15
|
let konvaGroup;
|
|
15
16
|
try {
|
|
16
17
|
konvaGroup = JSON.parse(annotation.konvaString);
|
|
@@ -31,7 +32,8 @@ export class InkParser extends AnnotationParser {
|
|
|
31
32
|
for (let i = 0; i < points.length; i += 2) {
|
|
32
33
|
const x = groupX + points[i] * scaleX;
|
|
33
34
|
const y = groupY + points[i + 1] * scaleY;
|
|
34
|
-
|
|
35
|
+
const u = convertCanvasPointToPdf(pageTransform, x, y);
|
|
36
|
+
transformedPoints.push(u.x, u.y);
|
|
35
37
|
}
|
|
36
38
|
transformedPaths.push(transformedPoints);
|
|
37
39
|
}
|
|
@@ -136,7 +138,7 @@ export class InkParser extends AnnotationParser {
|
|
|
136
138
|
const replyAnn = context.obj({
|
|
137
139
|
Type: PDFName.of("Annot"),
|
|
138
140
|
Subtype: PDFName.of("Text"),
|
|
139
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
141
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
140
142
|
Contents: stringToPDFHexString(comment.content),
|
|
141
143
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
142
144
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PDFName, PDFNumber, PDFRawStream, 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
6
|
function formatPdfNumber(value) {
|
|
@@ -10,7 +11,7 @@ export class LineParser extends AnnotationParser {
|
|
|
10
11
|
async parse() {
|
|
11
12
|
const { annotation, page, pdfDoc } = this;
|
|
12
13
|
const context = pdfDoc.context;
|
|
13
|
-
const
|
|
14
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
14
15
|
const konvaGroup = JSON.parse(annotation.konvaString);
|
|
15
16
|
const lines = (konvaGroup.children ?? []).filter((item) => item.className === "Arrow");
|
|
16
17
|
const groupX = konvaGroup.attrs?.x || 0;
|
|
@@ -25,7 +26,8 @@ export class LineParser extends AnnotationParser {
|
|
|
25
26
|
for (let i = 0; i < points.length; i += 2) {
|
|
26
27
|
const x = groupX + (points[i] ?? 0) * scaleX;
|
|
27
28
|
const y = groupY + (points[i + 1] ?? 0) * scaleY;
|
|
28
|
-
|
|
29
|
+
const u = convertCanvasPointToPdf(pageTransform, x, y);
|
|
30
|
+
transformedPoints.push(u.x, u.y);
|
|
29
31
|
}
|
|
30
32
|
transformedPaths.push(transformedPoints);
|
|
31
33
|
}
|
|
@@ -174,7 +176,7 @@ export class LineParser extends AnnotationParser {
|
|
|
174
176
|
const replyAnn = context.obj({
|
|
175
177
|
Type: PDFName.of("Annot"),
|
|
176
178
|
Subtype: PDFName.of("Text"),
|
|
177
|
-
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect,
|
|
179
|
+
Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageTransform),
|
|
178
180
|
Contents: stringToPDFHexString(comment.content),
|
|
179
181
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
180
182
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PDFName, PDFNumber, 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
|
import { computeBoundsFromKonvaString } from "./parse_utils.js";
|
|
5
6
|
const UNKNOWN_USER = "Unknown user";
|
|
@@ -29,7 +30,7 @@ export class PolylineParser extends AnnotationParser {
|
|
|
29
30
|
async parse() {
|
|
30
31
|
const { annotation, page, pdfDoc } = this;
|
|
31
32
|
const context = pdfDoc.context;
|
|
32
|
-
const
|
|
33
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
33
34
|
const konvaGroup = JSON.parse(annotation.konvaString);
|
|
34
35
|
const lines = (konvaGroup.children ?? []).filter((item) => item.className === "Path");
|
|
35
36
|
const groupX = konvaGroup.attrs?.x || 0;
|
|
@@ -43,7 +44,8 @@ export class PolylineParser extends AnnotationParser {
|
|
|
43
44
|
for (let i = 0; i < points.length; i += 2) {
|
|
44
45
|
const x = groupX + points[i] * scaleX;
|
|
45
46
|
const y = groupY + points[i + 1] * scaleY;
|
|
46
|
-
|
|
47
|
+
const u = convertCanvasPointToPdf(pageTransform, x, y);
|
|
48
|
+
transformedPoints.push(u.x, u.y);
|
|
47
49
|
}
|
|
48
50
|
return context.obj(transformedPoints);
|
|
49
51
|
})
|
|
@@ -62,7 +64,7 @@ export class PolylineParser extends AnnotationParser {
|
|
|
62
64
|
Subtype: PDFName.of("Ink"),
|
|
63
65
|
// Compute Rect from konvaString so it reflects the current position
|
|
64
66
|
// even when konvaClientRect is stale after a move/resize.
|
|
65
|
-
Rect: convertKonvaRectToPdfRect(computeBoundsFromKonvaString(annotation.konvaString) ?? annotation.konvaClientRect,
|
|
67
|
+
Rect: convertKonvaRectToPdfRect(computeBoundsFromKonvaString(annotation.konvaString) ?? annotation.konvaClientRect, pageTransform),
|
|
66
68
|
InkList: inkList,
|
|
67
69
|
C: context.obj([PDFNumber.of(r), PDFNumber.of(g), PDFNumber.of(b)]),
|
|
68
70
|
T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
|
|
@@ -79,7 +81,7 @@ export class PolylineParser extends AnnotationParser {
|
|
|
79
81
|
const replyAnn = context.obj({
|
|
80
82
|
Type: PDFName.of("Annot"),
|
|
81
83
|
Subtype: PDFName.of("Text"),
|
|
82
|
-
Rect: convertKonvaRectToPdfRect(computeBoundsFromKonvaString(annotation.konvaString) ?? annotation.konvaClientRect,
|
|
84
|
+
Rect: convertKonvaRectToPdfRect(computeBoundsFromKonvaString(annotation.konvaString) ?? annotation.konvaClientRect, pageTransform),
|
|
83
85
|
Contents: stringToPDFHexString(comment.content),
|
|
84
86
|
T: stringToPDFHexString(comment.title || UNKNOWN_USER),
|
|
85
87
|
M: PDFString.of(comment.date || ""),
|
|
@@ -1,5 +1,6 @@
|
|
|
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";
|
|
@@ -7,9 +8,9 @@ export class SquareParser extends AnnotationParser {
|
|
|
7
8
|
async parse() {
|
|
8
9
|
const { annotation, page, pdfDoc } = this;
|
|
9
10
|
const context = pdfDoc.context;
|
|
10
|
-
const
|
|
11
|
+
const pageTransform = pageTransformFromPdfLibPage(page);
|
|
11
12
|
const bounds = computeBoundsFromKonvaString(annotation.konvaString);
|
|
12
|
-
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect,
|
|
13
|
+
const rect = convertKonvaRectToPdfRect(bounds ?? annotation.konvaClientRect, pageTransform);
|
|
13
14
|
const mainAnn = context.obj({
|
|
14
15
|
Type: PDFName.of("Annot"),
|
|
15
16
|
Subtype: PDFName.of("Square"),
|