kviewer 0.0.1

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.
Files changed (197) hide show
  1. package/README.md +134 -0
  2. package/dist/module.d.mts +15 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +26 -0
  5. package/dist/runtime/annotation/engine/config.d.ts +52 -0
  6. package/dist/runtime/annotation/engine/config.js +283 -0
  7. package/dist/runtime/annotation/engine/const.d.ts +6 -0
  8. package/dist/runtime/annotation/engine/const.js +7 -0
  9. package/dist/runtime/annotation/engine/cursor-preview.d.ts +2 -0
  10. package/dist/runtime/annotation/engine/cursor-preview.js +88 -0
  11. package/dist/runtime/annotation/engine/editor/editor.d.ts +69 -0
  12. package/dist/runtime/annotation/engine/editor/editor.js +233 -0
  13. package/dist/runtime/annotation/engine/editor/selector.d.ts +74 -0
  14. package/dist/runtime/annotation/engine/editor/selector.js +594 -0
  15. package/dist/runtime/annotation/engine/import-normalize.d.ts +5 -0
  16. package/dist/runtime/annotation/engine/import-normalize.js +99 -0
  17. package/dist/runtime/annotation/engine/input-device.d.ts +53 -0
  18. package/dist/runtime/annotation/engine/input-device.js +64 -0
  19. package/dist/runtime/annotation/engine/painter.d.ts +97 -0
  20. package/dist/runtime/annotation/engine/painter.js +591 -0
  21. package/dist/runtime/annotation/engine/store.d.ts +11 -0
  22. package/dist/runtime/annotation/engine/store.js +47 -0
  23. package/dist/runtime/annotation/engine/tools/arrow.d.ts +22 -0
  24. package/dist/runtime/annotation/engine/tools/arrow.js +126 -0
  25. package/dist/runtime/annotation/engine/tools/circle.d.ts +45 -0
  26. package/dist/runtime/annotation/engine/tools/circle.js +148 -0
  27. package/dist/runtime/annotation/engine/tools/cloud.d.ts +50 -0
  28. package/dist/runtime/annotation/engine/tools/cloud.js +244 -0
  29. package/dist/runtime/annotation/engine/tools/free-highlight.d.ts +43 -0
  30. package/dist/runtime/annotation/engine/tools/free-highlight.js +165 -0
  31. package/dist/runtime/annotation/engine/tools/free-text.d.ts +27 -0
  32. package/dist/runtime/annotation/engine/tools/free-text.js +114 -0
  33. package/dist/runtime/annotation/engine/tools/freehand.d.ts +44 -0
  34. package/dist/runtime/annotation/engine/tools/freehand.js +151 -0
  35. package/dist/runtime/annotation/engine/tools/highlight.d.ts +87 -0
  36. package/dist/runtime/annotation/engine/tools/highlight.js +215 -0
  37. package/dist/runtime/annotation/engine/tools/note.d.ts +9 -0
  38. package/dist/runtime/annotation/engine/tools/note.js +34 -0
  39. package/dist/runtime/annotation/engine/tools/rectangle.d.ts +45 -0
  40. package/dist/runtime/annotation/engine/tools/rectangle.js +142 -0
  41. package/dist/runtime/annotation/engine/tools/signature.d.ts +16 -0
  42. package/dist/runtime/annotation/engine/tools/signature.js +74 -0
  43. package/dist/runtime/annotation/engine/tools/stamp.d.ts +18 -0
  44. package/dist/runtime/annotation/engine/tools/stamp.js +94 -0
  45. package/dist/runtime/annotation/engine/types.d.ts +170 -0
  46. package/dist/runtime/annotation/engine/types.js +67 -0
  47. package/dist/runtime/annotation/engine/utils.d.ts +40 -0
  48. package/dist/runtime/annotation/engine/utils.js +257 -0
  49. package/dist/runtime/annotation/parsers/parseFormFields.d.ts +9 -0
  50. package/dist/runtime/annotation/parsers/parseFormFields.js +101 -0
  51. package/dist/runtime/annotation/pdf-export/download.d.ts +1 -0
  52. package/dist/runtime/annotation/pdf-export/download.js +10 -0
  53. package/dist/runtime/annotation/pdf-export/export-form-fields.d.ts +9 -0
  54. package/dist/runtime/annotation/pdf-export/export-form-fields.js +90 -0
  55. package/dist/runtime/annotation/pdf-export/export.d.ts +15 -0
  56. package/dist/runtime/annotation/pdf-export/export.js +145 -0
  57. package/dist/runtime/annotation/pdf-export/parse.d.ts +10 -0
  58. package/dist/runtime/annotation/pdf-export/parse.js +19 -0
  59. package/dist/runtime/annotation/pdf-export/parse_circle.d.ts +4 -0
  60. package/dist/runtime/annotation/pdf-export/parse_circle.js +41 -0
  61. package/dist/runtime/annotation/pdf-export/parse_freetext.d.ts +4 -0
  62. package/dist/runtime/annotation/pdf-export/parse_freetext.js +54 -0
  63. package/dist/runtime/annotation/pdf-export/parse_highlight.d.ts +4 -0
  64. package/dist/runtime/annotation/pdf-export/parse_highlight.js +134 -0
  65. package/dist/runtime/annotation/pdf-export/parse_ink.d.ts +4 -0
  66. package/dist/runtime/annotation/pdf-export/parse_ink.js +124 -0
  67. package/dist/runtime/annotation/pdf-export/parse_line.d.ts +4 -0
  68. package/dist/runtime/annotation/pdf-export/parse_line.js +71 -0
  69. package/dist/runtime/annotation/pdf-export/parse_polyline.d.ts +4 -0
  70. package/dist/runtime/annotation/pdf-export/parse_polyline.js +93 -0
  71. package/dist/runtime/annotation/pdf-export/parse_square.d.ts +4 -0
  72. package/dist/runtime/annotation/pdf-export/parse_square.js +41 -0
  73. package/dist/runtime/annotation/pdf-export/parse_stamp.d.ts +4 -0
  74. package/dist/runtime/annotation/pdf-export/parse_stamp.js +195 -0
  75. package/dist/runtime/annotation/pdf-export/parse_strikeout.d.ts +4 -0
  76. package/dist/runtime/annotation/pdf-export/parse_strikeout.js +59 -0
  77. package/dist/runtime/annotation/pdf-export/parse_text.d.ts +4 -0
  78. package/dist/runtime/annotation/pdf-export/parse_text.js +42 -0
  79. package/dist/runtime/annotation/pdf-export/parse_underline.d.ts +4 -0
  80. package/dist/runtime/annotation/pdf-export/parse_underline.js +59 -0
  81. package/dist/runtime/assets/kviewer.css +1 -0
  82. package/dist/runtime/components/AnnotationToolbar.d.vue.ts +3 -0
  83. package/dist/runtime/components/AnnotationToolbar.vue +125 -0
  84. package/dist/runtime/components/AnnotationToolbar.vue.d.ts +3 -0
  85. package/dist/runtime/components/FloatingPageIndicator.d.vue.ts +6 -0
  86. package/dist/runtime/components/FloatingPageIndicator.vue +93 -0
  87. package/dist/runtime/components/FloatingPageIndicator.vue.d.ts +6 -0
  88. package/dist/runtime/components/FormFieldLayer.d.vue.ts +11 -0
  89. package/dist/runtime/components/FormFieldLayer.vue +40 -0
  90. package/dist/runtime/components/FormFieldLayer.vue.d.ts +11 -0
  91. package/dist/runtime/components/PdfPage.d.vue.ts +9 -0
  92. package/dist/runtime/components/PdfPage.vue +199 -0
  93. package/dist/runtime/components/PdfPage.vue.d.ts +9 -0
  94. package/dist/runtime/components/ToolButton.d.vue.ts +13 -0
  95. package/dist/runtime/components/ToolButton.vue +26 -0
  96. package/dist/runtime/components/ToolButton.vue.d.ts +13 -0
  97. package/dist/runtime/components/Toolbar.d.vue.ts +3 -0
  98. package/dist/runtime/components/Toolbar.vue +11 -0
  99. package/dist/runtime/components/Toolbar.vue.d.ts +3 -0
  100. package/dist/runtime/components/Viewer.d.vue.ts +45 -0
  101. package/dist/runtime/components/Viewer.vue +617 -0
  102. package/dist/runtime/components/Viewer.vue.d.ts +45 -0
  103. package/dist/runtime/components/ViewerBar.d.vue.ts +3 -0
  104. package/dist/runtime/components/ViewerBar.vue +91 -0
  105. package/dist/runtime/components/ViewerBar.vue.d.ts +3 -0
  106. package/dist/runtime/components/ViewerTabs.d.vue.ts +381 -0
  107. package/dist/runtime/components/ViewerTabs.vue +171 -0
  108. package/dist/runtime/components/ViewerTabs.vue.d.ts +381 -0
  109. package/dist/runtime/components/form-fields/FormButton.d.vue.ts +7 -0
  110. package/dist/runtime/components/form-fields/FormButton.vue +39 -0
  111. package/dist/runtime/components/form-fields/FormButton.vue.d.ts +7 -0
  112. package/dist/runtime/components/form-fields/FormCheckbox.d.vue.ts +7 -0
  113. package/dist/runtime/components/form-fields/FormCheckbox.vue +28 -0
  114. package/dist/runtime/components/form-fields/FormCheckbox.vue.d.ts +7 -0
  115. package/dist/runtime/components/form-fields/FormDropdown.d.vue.ts +7 -0
  116. package/dist/runtime/components/form-fields/FormDropdown.vue +112 -0
  117. package/dist/runtime/components/form-fields/FormDropdown.vue.d.ts +7 -0
  118. package/dist/runtime/components/form-fields/FormFieldWrapper.d.vue.ts +8 -0
  119. package/dist/runtime/components/form-fields/FormFieldWrapper.vue +41 -0
  120. package/dist/runtime/components/form-fields/FormFieldWrapper.vue.d.ts +8 -0
  121. package/dist/runtime/components/form-fields/FormRadioButton.d.vue.ts +7 -0
  122. package/dist/runtime/components/form-fields/FormRadioButton.vue +30 -0
  123. package/dist/runtime/components/form-fields/FormRadioButton.vue.d.ts +7 -0
  124. package/dist/runtime/components/form-fields/FormSignatureField.d.vue.ts +7 -0
  125. package/dist/runtime/components/form-fields/FormSignatureField.vue +54 -0
  126. package/dist/runtime/components/form-fields/FormSignatureField.vue.d.ts +7 -0
  127. package/dist/runtime/components/form-fields/FormTextField.d.vue.ts +7 -0
  128. package/dist/runtime/components/form-fields/FormTextField.vue +66 -0
  129. package/dist/runtime/components/form-fields/FormTextField.vue.d.ts +7 -0
  130. package/dist/runtime/components/modals/FreeTextModal.d.vue.ts +25 -0
  131. package/dist/runtime/components/modals/FreeTextModal.vue +89 -0
  132. package/dist/runtime/components/modals/FreeTextModal.vue.d.ts +25 -0
  133. package/dist/runtime/components/modals/SignatureDrawModal.d.vue.ts +14 -0
  134. package/dist/runtime/components/modals/SignatureDrawModal.vue +120 -0
  135. package/dist/runtime/components/modals/SignatureDrawModal.vue.d.ts +14 -0
  136. package/dist/runtime/components/panels/SignaturePicker.d.vue.ts +3 -0
  137. package/dist/runtime/components/panels/SignaturePicker.vue +85 -0
  138. package/dist/runtime/components/panels/SignaturePicker.vue.d.ts +3 -0
  139. package/dist/runtime/components/panels/StampPicker.d.vue.ts +3 -0
  140. package/dist/runtime/components/panels/StampPicker.vue +46 -0
  141. package/dist/runtime/components/panels/StampPicker.vue.d.ts +3 -0
  142. package/dist/runtime/components/tools/ActionTools.d.vue.ts +3 -0
  143. package/dist/runtime/components/tools/ActionTools.vue +32 -0
  144. package/dist/runtime/components/tools/ActionTools.vue.d.ts +3 -0
  145. package/dist/runtime/components/tools/DrawingTools.d.vue.ts +6 -0
  146. package/dist/runtime/components/tools/DrawingTools.vue +57 -0
  147. package/dist/runtime/components/tools/DrawingTools.vue.d.ts +6 -0
  148. package/dist/runtime/components/tools/HandTool.d.vue.ts +3 -0
  149. package/dist/runtime/components/tools/HandTool.vue +14 -0
  150. package/dist/runtime/components/tools/HandTool.vue.d.ts +3 -0
  151. package/dist/runtime/components/tools/MarqueeTool.d.vue.ts +3 -0
  152. package/dist/runtime/components/tools/MarqueeTool.vue +15 -0
  153. package/dist/runtime/components/tools/MarqueeTool.vue.d.ts +3 -0
  154. package/dist/runtime/components/tools/PageInfo.d.vue.ts +3 -0
  155. package/dist/runtime/components/tools/PageInfo.vue +10 -0
  156. package/dist/runtime/components/tools/PageInfo.vue.d.ts +3 -0
  157. package/dist/runtime/components/tools/PageSettings.d.vue.ts +3 -0
  158. package/dist/runtime/components/tools/PageSettings.vue +92 -0
  159. package/dist/runtime/components/tools/PageSettings.vue.d.ts +3 -0
  160. package/dist/runtime/components/tools/SearchTool.d.vue.ts +3 -0
  161. package/dist/runtime/components/tools/SearchTool.vue +149 -0
  162. package/dist/runtime/components/tools/SearchTool.vue.d.ts +3 -0
  163. package/dist/runtime/components/tools/ToolProperties.d.vue.ts +7 -0
  164. package/dist/runtime/components/tools/ToolProperties.vue +174 -0
  165. package/dist/runtime/components/tools/ToolProperties.vue.d.ts +7 -0
  166. package/dist/runtime/components/tools/ZoomControls.d.vue.ts +3 -0
  167. package/dist/runtime/components/tools/ZoomControls.vue +59 -0
  168. package/dist/runtime/components/tools/ZoomControls.vue.d.ts +3 -0
  169. package/dist/runtime/composables/search-utils.d.ts +20 -0
  170. package/dist/runtime/composables/search-utils.js +55 -0
  171. package/dist/runtime/composables/useAnnotationEngine.d.ts +7 -0
  172. package/dist/runtime/composables/useAnnotationEngine.js +70 -0
  173. package/dist/runtime/composables/useAnnotationHistory.d.ts +12 -0
  174. package/dist/runtime/composables/useAnnotationHistory.js +69 -0
  175. package/dist/runtime/composables/useFormFields.d.ts +26 -0
  176. package/dist/runtime/composables/useFormFields.js +112 -0
  177. package/dist/runtime/composables/usePageProxyCache.d.ts +8 -0
  178. package/dist/runtime/composables/usePageProxyCache.js +73 -0
  179. package/dist/runtime/composables/usePageSettings.d.ts +16 -0
  180. package/dist/runtime/composables/usePageSettings.js +66 -0
  181. package/dist/runtime/composables/usePageVirtualization.d.ts +19 -0
  182. package/dist/runtime/composables/usePageVirtualization.js +203 -0
  183. package/dist/runtime/composables/useSearchIndex.d.ts +11 -0
  184. package/dist/runtime/composables/useSearchIndex.js +71 -0
  185. package/dist/runtime/composables/useViewerSearch.d.ts +32 -0
  186. package/dist/runtime/composables/useViewerSearch.js +418 -0
  187. package/dist/runtime/composables/useViewerState.d.ts +62 -0
  188. package/dist/runtime/composables/useViewerState.js +189 -0
  189. package/dist/runtime/composables/viewMode.d.ts +11 -0
  190. package/dist/runtime/composables/viewMode.js +19 -0
  191. package/dist/runtime/plugin.d.ts +2 -0
  192. package/dist/runtime/plugin.js +3 -0
  193. package/dist/runtime/public-types.d.ts +2 -0
  194. package/dist/runtime/public-types.js +0 -0
  195. package/dist/runtime/server/tsconfig.json +3 -0
  196. package/dist/types.d.mts +5 -0
  197. package/package.json +64 -0
@@ -0,0 +1,90 @@
1
+ export async function writeFormFieldsToPdf(pdfDoc, fieldValues) {
2
+ const form = pdfDoc.getForm();
3
+ for (const field of fieldValues) {
4
+ try {
5
+ switch (field.fieldType) {
6
+ case "text": {
7
+ const textField = form.getTextField(field.fieldName);
8
+ textField.setText(typeof field.value === "string" ? field.value : String(field.value));
9
+ break;
10
+ }
11
+ case "checkbox": {
12
+ const checkBox = form.getCheckBox(field.fieldName);
13
+ if (field.value === true) {
14
+ checkBox.check();
15
+ } else {
16
+ checkBox.uncheck();
17
+ }
18
+ break;
19
+ }
20
+ case "radio": {
21
+ if (typeof field.value === "string" && field.value !== "") {
22
+ const radioGroup = form.getRadioGroup(field.fieldName);
23
+ radioGroup.select(field.value);
24
+ }
25
+ break;
26
+ }
27
+ case "dropdown": {
28
+ const dropdown = form.getDropdown(field.fieldName);
29
+ if (Array.isArray(field.value)) {
30
+ for (const v of field.value) {
31
+ dropdown.select(v);
32
+ }
33
+ } else if (typeof field.value === "string" && field.value !== "") {
34
+ dropdown.select(field.value);
35
+ }
36
+ break;
37
+ }
38
+ case "signature": {
39
+ if (typeof field.value === "string" && field.value.startsWith("data:image")) {
40
+ await embedSignatureImage(pdfDoc, field);
41
+ }
42
+ break;
43
+ }
44
+ }
45
+ } catch (err) {
46
+ console.warn(
47
+ `Failed to write form field "${field.fieldName}" (${field.fieldType}):`,
48
+ err
49
+ );
50
+ }
51
+ }
52
+ }
53
+ async function embedSignatureImage(pdfDoc, field) {
54
+ const dataUrl = field.value;
55
+ const match = dataUrl.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/);
56
+ if (!match) return;
57
+ const mimeType = match[1] ?? "";
58
+ const base64 = match[2] ?? "";
59
+ let image;
60
+ if (mimeType.includes("jpeg") || mimeType.includes("jpg")) {
61
+ image = await pdfDoc.embedJpg(base64);
62
+ } else {
63
+ image = await pdfDoc.embedPng(base64);
64
+ }
65
+ const form = pdfDoc.getForm();
66
+ try {
67
+ const sigField = form.getSignature(field.fieldName);
68
+ const widgets = sigField.acroField.getWidgets();
69
+ if (widgets.length > 0) {
70
+ const widget = widgets[0];
71
+ const rect = widget.getRectangle();
72
+ const pages = pdfDoc.getPages();
73
+ for (const page of pages) {
74
+ const pageRef = page.ref;
75
+ const widgetPage = widget.P();
76
+ if (widgetPage === pageRef || !widgetPage) {
77
+ page.drawImage(image, {
78
+ x: rect.x,
79
+ y: rect.y,
80
+ width: rect.width,
81
+ height: rect.height
82
+ });
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ } catch {
88
+ console.warn(`Could not embed signature for field "${field.fieldName}"`);
89
+ }
90
+ }
@@ -0,0 +1,15 @@
1
+ import { type IAnnotationStore, type FormFieldValue } from '../engine/types.js';
2
+ export interface ExportPdfOptions {
3
+ flatten?: boolean;
4
+ download?: boolean;
5
+ fileName?: string;
6
+ preserveOriginalAnnotations?: boolean;
7
+ }
8
+ interface ExportPdfParams {
9
+ pdfData: Uint8Array | ArrayBuffer;
10
+ annotations: IAnnotationStore[];
11
+ options?: ExportPdfOptions;
12
+ formFieldValues?: FormFieldValue[];
13
+ }
14
+ export declare function exportAnnotationsToPdf({ pdfData, annotations, options, formFieldValues, }: ExportPdfParams): Promise<Uint8Array>;
15
+ export {};
@@ -0,0 +1,145 @@
1
+ import { PDFDocument, PDFName } from "pdf-lib";
2
+ import { PdfjsAnnotationType } from "../engine/types.js";
3
+ import { writeFormFieldsToPdf } from "./export-form-fields.js";
4
+ import { CircleParser } from "./parse_circle.js";
5
+ import { FreeTextParser } from "./parse_freetext.js";
6
+ import { HighlightParser } from "./parse_highlight.js";
7
+ import { InkParser } from "./parse_ink.js";
8
+ import { LineParser } from "./parse_line.js";
9
+ import { PolylineParser } from "./parse_polyline.js";
10
+ import { SquareParser } from "./parse_square.js";
11
+ import { StampParser } from "./parse_stamp.js";
12
+ import { StrikeOutParser } from "./parse_strikeout.js";
13
+ import { TextParser } from "./parse_text.js";
14
+ import { UnderlineParser } from "./parse_underline.js";
15
+ const parserMap = {
16
+ [PdfjsAnnotationType.TEXT]: TextParser,
17
+ [PdfjsAnnotationType.HIGHLIGHT]: HighlightParser,
18
+ [PdfjsAnnotationType.UNDERLINE]: UnderlineParser,
19
+ [PdfjsAnnotationType.STRIKEOUT]: StrikeOutParser,
20
+ [PdfjsAnnotationType.SQUARE]: SquareParser,
21
+ [PdfjsAnnotationType.CIRCLE]: CircleParser,
22
+ [PdfjsAnnotationType.INK]: InkParser,
23
+ [PdfjsAnnotationType.POLYLINE]: PolylineParser,
24
+ [PdfjsAnnotationType.FREETEXT]: FreeTextParser,
25
+ [PdfjsAnnotationType.STAMP]: StampParser,
26
+ [PdfjsAnnotationType.LINE]: LineParser
27
+ };
28
+ function toUint8Array(data) {
29
+ if (data instanceof Uint8Array) return data;
30
+ return new Uint8Array(data);
31
+ }
32
+ function clearAllAnnotations(pdfDoc) {
33
+ for (const page of pdfDoc.getPages()) {
34
+ const annotsKey = PDFName.of("Annots");
35
+ if (page.node.has(annotsKey)) {
36
+ page.node.set(annotsKey, pdfDoc.context.obj([]));
37
+ }
38
+ }
39
+ }
40
+ async function parseAnnotationToPdf(annotation, page, pdfDoc) {
41
+ const ParserClass = parserMap[annotation.pdfjsType];
42
+ if (!ParserClass) {
43
+ console.warn(`Unsupported annotation type for PDF export: ${annotation.pdfjsType}`);
44
+ return;
45
+ }
46
+ const parser = new ParserClass(pdfDoc, page, annotation);
47
+ await parser.parse();
48
+ }
49
+ async function embedDataUrlImage(pdfDoc, dataUrl) {
50
+ const match = dataUrl.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,(.+)$/);
51
+ if (!match) {
52
+ throw new TypeError("Invalid data URL for flatten export image");
53
+ }
54
+ const mimeType = match[1] || "";
55
+ const base64 = match[2] || "";
56
+ if (mimeType.includes("jpeg") || mimeType.includes("jpg")) {
57
+ return pdfDoc.embedJpg(base64);
58
+ }
59
+ return pdfDoc.embedPng(base64);
60
+ }
61
+ async function flattenPageAnnotations(pdfDoc, page, annotations) {
62
+ if (annotations.length === 0) return;
63
+ if (typeof window === "undefined" || typeof document === "undefined") {
64
+ throw new TypeError("Flatten export requires a browser environment");
65
+ }
66
+ const Konva = (await import("konva")).default;
67
+ const width = Math.floor(page.getWidth());
68
+ const height = Math.floor(page.getHeight());
69
+ const stage = new Konva.Stage({
70
+ container: document.createElement("div"),
71
+ width,
72
+ height,
73
+ listening: false
74
+ });
75
+ const layer = new Konva.Layer({ listening: false });
76
+ stage.add(layer);
77
+ for (const annotation of annotations) {
78
+ try {
79
+ const node = Konva.Node.create(annotation.konvaString);
80
+ if (node instanceof Konva.Node) {
81
+ layer.add(node);
82
+ }
83
+ } catch {
84
+ console.warn(`Unable to flatten annotation ${annotation.id}; skipping`);
85
+ }
86
+ }
87
+ layer.draw();
88
+ const dataUrl = stage.toDataURL({ pixelRatio: 2 });
89
+ stage.destroy();
90
+ const image = await embedDataUrlImage(pdfDoc, dataUrl);
91
+ page.drawImage(image, {
92
+ x: 0,
93
+ y: 0,
94
+ width: page.getWidth(),
95
+ height: page.getHeight()
96
+ });
97
+ }
98
+ function groupByPage(annotations) {
99
+ const grouped = /* @__PURE__ */ new Map();
100
+ for (const annotation of annotations) {
101
+ const list = grouped.get(annotation.pageNumber);
102
+ if (list) {
103
+ list.push(annotation);
104
+ } else {
105
+ grouped.set(annotation.pageNumber, [annotation]);
106
+ }
107
+ }
108
+ return grouped;
109
+ }
110
+ export async function exportAnnotationsToPdf({
111
+ pdfData,
112
+ annotations,
113
+ options,
114
+ formFieldValues
115
+ }) {
116
+ const flatten = options?.flatten ?? false;
117
+ const preserveOriginalAnnotations = options?.preserveOriginalAnnotations ?? false;
118
+ const pdfDoc = await PDFDocument.load(toUint8Array(pdfData));
119
+ if (formFieldValues && formFieldValues.length > 0) {
120
+ await writeFormFieldsToPdf(pdfDoc, formFieldValues);
121
+ }
122
+ if (!preserveOriginalAnnotations) {
123
+ clearAllAnnotations(pdfDoc);
124
+ }
125
+ if (flatten) {
126
+ const pages = pdfDoc.getPages();
127
+ const grouped = groupByPage(annotations);
128
+ for (const [pageNumber, pageAnnotations] of grouped.entries()) {
129
+ const page = pages[pageNumber - 1];
130
+ if (!page) continue;
131
+ await flattenPageAnnotations(pdfDoc, page, pageAnnotations);
132
+ }
133
+ } else {
134
+ const pages = pdfDoc.getPages();
135
+ for (const annotation of annotations) {
136
+ const page = pages[annotation.pageNumber - 1];
137
+ if (!page) {
138
+ console.warn(`Skipping annotation ${annotation.id}; page ${annotation.pageNumber} missing`);
139
+ continue;
140
+ }
141
+ await parseAnnotationToPdf(annotation, page, pdfDoc);
142
+ }
143
+ }
144
+ return pdfDoc.save();
145
+ }
@@ -0,0 +1,10 @@
1
+ import type { PDFDocument, PDFPage, PDFRef } from 'pdf-lib';
2
+ import type { IAnnotationStore } from '../engine/types.js';
3
+ export declare abstract class AnnotationParser {
4
+ protected annotation: IAnnotationStore;
5
+ protected page: PDFPage;
6
+ protected pdfDoc: PDFDocument;
7
+ constructor(pdfDoc: PDFDocument, page: PDFPage, annotation: IAnnotationStore);
8
+ protected addAnnotationToPage(page: PDFPage, annotRef: PDFRef): void;
9
+ abstract parse(): Promise<void>;
10
+ }
@@ -0,0 +1,19 @@
1
+ import { PDFName } from "pdf-lib";
2
+ export class AnnotationParser {
3
+ annotation;
4
+ page;
5
+ pdfDoc;
6
+ constructor(pdfDoc, page, annotation) {
7
+ this.pdfDoc = pdfDoc;
8
+ this.page = page;
9
+ this.annotation = annotation;
10
+ }
11
+ addAnnotationToPage(page, annotRef) {
12
+ const annots = page.node.lookup(PDFName.of("Annots"));
13
+ if (annots) {
14
+ annots.push(annotRef);
15
+ } else {
16
+ page.node.set(PDFName.of("Annots"), page.doc.context.obj([annotRef]));
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,4 @@
1
+ import { AnnotationParser } from './parse.js';
2
+ export declare class CircleParser extends AnnotationParser {
3
+ parse(): Promise<void>;
4
+ }
@@ -0,0 +1,41 @@
1
+ import { PDFName, PDFString } from "pdf-lib";
2
+ import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
3
+ import { AnnotationParser } from "./parse.js";
4
+ const UNKNOWN_USER = "Unknown user";
5
+ export class CircleParser extends AnnotationParser {
6
+ async parse() {
7
+ const { annotation, page, pdfDoc } = this;
8
+ const context = pdfDoc.context;
9
+ const pageHeight = page.getHeight();
10
+ const mainAnn = context.obj({
11
+ Type: PDFName.of("Annot"),
12
+ Subtype: PDFName.of("Circle"),
13
+ Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight),
14
+ C: rgbToPdfColor(annotation.color || void 0),
15
+ T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
16
+ Contents: stringToPDFHexString(annotation.contentsObj?.text || ""),
17
+ M: PDFString.of(annotation.date || ""),
18
+ NM: PDFString.of(annotation.id),
19
+ Border: [0, 0, 1]
20
+ });
21
+ const mainAnnRef = context.register(mainAnn);
22
+ this.addAnnotationToPage(page, mainAnnRef);
23
+ for (const comment of annotation.comments || []) {
24
+ const replyAnn = context.obj({
25
+ Type: PDFName.of("Annot"),
26
+ Subtype: PDFName.of("Text"),
27
+ Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight),
28
+ Contents: stringToPDFHexString(comment.content),
29
+ T: stringToPDFHexString(comment.title || UNKNOWN_USER),
30
+ M: PDFString.of(comment.date || ""),
31
+ C: rgbToPdfColor(annotation.color || void 0),
32
+ IRT: mainAnnRef,
33
+ RT: PDFName.of("R"),
34
+ NM: PDFString.of(comment.id),
35
+ Open: false
36
+ });
37
+ const replyAnnRef = context.register(replyAnn);
38
+ this.addAnnotationToPage(page, replyAnnRef);
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,4 @@
1
+ import { AnnotationParser } from './parse.js';
2
+ export declare class FreeTextParser extends AnnotationParser {
3
+ parse(): Promise<void>;
4
+ }
@@ -0,0 +1,54 @@
1
+ import { PDFName, PDFNumber, PDFString } from "pdf-lib";
2
+ import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
3
+ import { AnnotationParser } from "./parse.js";
4
+ const UNKNOWN_USER = "Unknown user";
5
+ export class FreeTextParser extends AnnotationParser {
6
+ async parse() {
7
+ const { annotation, page, pdfDoc } = this;
8
+ const [x1, , , y2] = convertKonvaRectToPdfRect(annotation.konvaClientRect, page.getHeight());
9
+ const context = pdfDoc.context;
10
+ const pageWidth = page.getWidth();
11
+ const pageHeight = page.getHeight();
12
+ const iconSize = 20;
13
+ const xLeft = Math.max(0, Math.min(x1, pageWidth - iconSize));
14
+ const yTop = Math.min(pageHeight, Math.max(y2, iconSize));
15
+ const yBottom = yTop - iconSize;
16
+ const rect = [
17
+ PDFNumber.of(xLeft),
18
+ PDFNumber.of(yBottom),
19
+ PDFNumber.of(xLeft + iconSize),
20
+ PDFNumber.of(yTop)
21
+ ];
22
+ const mainAnn = context.obj({
23
+ Type: PDFName.of("Annot"),
24
+ Subtype: PDFName.of("Text"),
25
+ Rect: rect,
26
+ NM: PDFString.of(annotation.id),
27
+ Contents: stringToPDFHexString(annotation.contentsObj?.text || ""),
28
+ Name: PDFName.of("Comment"),
29
+ T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
30
+ M: PDFString.of(annotation.date || ""),
31
+ C: rgbToPdfColor(annotation.color || void 0),
32
+ Open: false
33
+ });
34
+ const mainAnnRef = context.register(mainAnn);
35
+ this.addAnnotationToPage(page, mainAnnRef);
36
+ for (const comment of annotation.comments || []) {
37
+ const replyAnn = context.obj({
38
+ Type: PDFName.of("Annot"),
39
+ Subtype: PDFName.of("Text"),
40
+ Rect: rect,
41
+ Contents: stringToPDFHexString(comment.content),
42
+ T: stringToPDFHexString(comment.title || UNKNOWN_USER),
43
+ M: PDFString.of(comment.date || ""),
44
+ C: rgbToPdfColor(annotation.color || void 0),
45
+ IRT: mainAnnRef,
46
+ RT: PDFName.of("R"),
47
+ NM: PDFString.of(comment.id),
48
+ Open: false
49
+ });
50
+ const replyAnnRef = context.register(replyAnn);
51
+ this.addAnnotationToPage(page, replyAnnRef);
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,4 @@
1
+ import { AnnotationParser } from './parse.js';
2
+ export declare class HighlightParser extends AnnotationParser {
3
+ parse(): Promise<void>;
4
+ }
@@ -0,0 +1,134 @@
1
+ import { PDFName, PDFNumber, PDFRawStream, PDFString } from "pdf-lib";
2
+ import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
3
+ import { AnnotationParser } from "./parse.js";
4
+ const UNKNOWN_USER = "Unknown user";
5
+ function formatPdfNumber(value) {
6
+ if (!Number.isFinite(value)) return "0";
7
+ return Number(value.toFixed(3)).toString();
8
+ }
9
+ function parseHighlightRects(konvaString) {
10
+ const konvaGroup = JSON.parse(konvaString);
11
+ const groupX = konvaGroup.attrs?.x ?? 0;
12
+ const groupY = konvaGroup.attrs?.y ?? 0;
13
+ const scaleX = konvaGroup.attrs?.scaleX ?? 1;
14
+ const scaleY = konvaGroup.attrs?.scaleY ?? 1;
15
+ const rects = (konvaGroup.children ?? []).filter((item) => item.className === "Rect");
16
+ return rects.map((rect) => {
17
+ const x = groupX + (rect.attrs?.x ?? 0) * scaleX;
18
+ const y = groupY + (rect.attrs?.y ?? 0) * scaleY;
19
+ const width = (rect.attrs?.width ?? 0) * scaleX;
20
+ const height = (rect.attrs?.height ?? 0) * scaleY;
21
+ return { x, y, width, height };
22
+ }).filter((rect) => rect.width > 0 && rect.height > 0);
23
+ }
24
+ function buildQuadPoints(rects, pageHeight) {
25
+ const quadPoints = [];
26
+ for (const rect of rects) {
27
+ const x = rect.x;
28
+ const y = rect.y;
29
+ const width = rect.width;
30
+ const height = rect.height;
31
+ const x1 = x;
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);
36
+ }
37
+ return quadPoints;
38
+ }
39
+ export class HighlightParser extends AnnotationParser {
40
+ async parse() {
41
+ const { annotation, page, pdfDoc } = this;
42
+ const context = pdfDoc.context;
43
+ const pageHeight = page.getHeight();
44
+ let highlightRects = [];
45
+ try {
46
+ highlightRects = parseHighlightRects(annotation.konvaString);
47
+ } catch {
48
+ console.warn(`Invalid konva JSON for Highlight annotation ${annotation.id}`);
49
+ }
50
+ if (highlightRects.length === 0) {
51
+ highlightRects = [{
52
+ x: annotation.konvaClientRect.x,
53
+ y: annotation.konvaClientRect.y,
54
+ width: annotation.konvaClientRect.width,
55
+ height: annotation.konvaClientRect.height
56
+ }];
57
+ }
58
+ const quadPoints = buildQuadPoints(highlightRects, pageHeight);
59
+ const rect = convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight);
60
+ const color = rgbToPdfColor(annotation.color || void 0);
61
+ const [x1, y1, x2, y2] = rect;
62
+ const appearanceWidth = Math.max(1, x2 - x1);
63
+ const appearanceHeight = Math.max(1, y2 - y1);
64
+ const [r, g, b] = color;
65
+ const commands = [
66
+ "q",
67
+ "/GS1 gs",
68
+ `${formatPdfNumber(r)} ${formatPdfNumber(g)} ${formatPdfNumber(b)} rg`
69
+ ];
70
+ for (const highlightRect of highlightRects) {
71
+ const pageX = highlightRect.x;
72
+ const pageY = pageHeight - highlightRect.y - highlightRect.height;
73
+ const localX = pageX - x1;
74
+ const localY = pageY - y1;
75
+ commands.push(
76
+ `${formatPdfNumber(localX)} ${formatPdfNumber(localY)} ${formatPdfNumber(highlightRect.width)} ${formatPdfNumber(highlightRect.height)} re`,
77
+ "f"
78
+ );
79
+ }
80
+ commands.push("Q");
81
+ const appearanceStream = PDFRawStream.of(
82
+ context.obj({
83
+ Type: PDFName.of("XObject"),
84
+ Subtype: PDFName.of("Form"),
85
+ BBox: context.obj([0, 0, appearanceWidth, appearanceHeight]),
86
+ Resources: context.obj({
87
+ ExtGState: context.obj({
88
+ GS1: context.obj({
89
+ Type: PDFName.of("ExtGState"),
90
+ ca: PDFNumber.of(0.35),
91
+ CA: PDFNumber.of(0.35),
92
+ BM: PDFName.of("Multiply")
93
+ })
94
+ })
95
+ })
96
+ }),
97
+ new TextEncoder().encode(commands.join("\n"))
98
+ );
99
+ const appearanceRef = context.register(appearanceStream);
100
+ const ap = context.obj({ N: appearanceRef });
101
+ const mainAnn = context.obj({
102
+ Type: PDFName.of("Annot"),
103
+ Subtype: PDFName.of("Highlight"),
104
+ Rect: rect,
105
+ QuadPoints: context.obj(quadPoints),
106
+ C: context.obj(color),
107
+ T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
108
+ Contents: stringToPDFHexString(annotation.contentsObj?.text || ""),
109
+ M: PDFString.of(annotation.date || ""),
110
+ NM: PDFString.of(annotation.id),
111
+ CA: PDFNumber.of(0.35),
112
+ AP: ap
113
+ });
114
+ const mainAnnRef = context.register(mainAnn);
115
+ this.addAnnotationToPage(page, mainAnnRef);
116
+ for (const comment of annotation.comments || []) {
117
+ const replyAnn = context.obj({
118
+ Type: PDFName.of("Annot"),
119
+ Subtype: PDFName.of("Text"),
120
+ Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight),
121
+ Contents: stringToPDFHexString(comment.content),
122
+ T: stringToPDFHexString(comment.title || UNKNOWN_USER),
123
+ M: PDFString.of(comment.date || ""),
124
+ C: rgbToPdfColor(annotation.color || void 0),
125
+ IRT: mainAnnRef,
126
+ RT: PDFName.of("R"),
127
+ NM: PDFString.of(comment.id),
128
+ Open: false
129
+ });
130
+ const replyAnnRef = context.register(replyAnn);
131
+ this.addAnnotationToPage(page, replyAnnRef);
132
+ }
133
+ }
134
+ }
@@ -0,0 +1,4 @@
1
+ import { AnnotationParser } from './parse.js';
2
+ export declare class InkParser extends AnnotationParser {
3
+ parse(): Promise<void>;
4
+ }
@@ -0,0 +1,124 @@
1
+ import { PDFName, PDFNumber, PDFRawStream, PDFString } from "pdf-lib";
2
+ import { convertKonvaRectToPdfRect, rgbToPdfColor, stringToPDFHexString } from "../engine/utils.js";
3
+ import { AnnotationParser } from "./parse.js";
4
+ const UNKNOWN_USER = "Unknown user";
5
+ function formatPdfNumber(value) {
6
+ if (!Number.isFinite(value)) return "0";
7
+ return Number(value.toFixed(3)).toString();
8
+ }
9
+ export class InkParser extends AnnotationParser {
10
+ async parse() {
11
+ const { annotation, page, pdfDoc } = this;
12
+ const context = pdfDoc.context;
13
+ const pageHeight = page.getHeight();
14
+ let konvaGroup;
15
+ try {
16
+ konvaGroup = JSON.parse(annotation.konvaString);
17
+ } catch {
18
+ console.warn(`Invalid konva JSON for Ink annotation ${annotation.id}`);
19
+ return;
20
+ }
21
+ const lines = (konvaGroup.children ?? []).filter((item) => item.className === "Line");
22
+ const groupX = konvaGroup.attrs?.x || 0;
23
+ const groupY = konvaGroup.attrs?.y || 0;
24
+ const scaleX = konvaGroup.attrs?.scaleX || 1;
25
+ const scaleY = konvaGroup.attrs?.scaleY || 1;
26
+ const transformedPaths = [];
27
+ for (const line of lines) {
28
+ const points = line.attrs?.points ?? [];
29
+ if (points.length < 4) continue;
30
+ const transformedPoints = [];
31
+ for (let i = 0; i < points.length; i += 2) {
32
+ const x = groupX + points[i] * scaleX;
33
+ const y = groupY + points[i + 1] * scaleY;
34
+ transformedPoints.push(x, pageHeight - y);
35
+ }
36
+ transformedPaths.push(transformedPoints);
37
+ }
38
+ if (transformedPaths.length === 0) {
39
+ console.warn(`Ink annotation ${annotation.id} has no drawable paths`);
40
+ return;
41
+ }
42
+ const inkList = context.obj(
43
+ transformedPaths.map((path) => context.obj(path))
44
+ );
45
+ const firstLine = lines[0]?.attrs || {};
46
+ const strokeWidth = firstLine.strokeWidth ?? 1;
47
+ const opacity = firstLine.opacity ?? 1;
48
+ const color = firstLine.stroke ?? annotation.color ?? "rgb(255, 0, 0)";
49
+ const [r, g, b] = rgbToPdfColor(color);
50
+ const bs = context.obj({
51
+ W: PDFNumber.of(strokeWidth),
52
+ S: PDFName.of("S")
53
+ });
54
+ const rect = convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight);
55
+ const [x1, y1, x2, y2] = rect;
56
+ const appearanceWidth = Math.max(1, x2 - x1);
57
+ const appearanceHeight = Math.max(1, y2 - y1);
58
+ const commands = [
59
+ "q",
60
+ `${formatPdfNumber(r)} ${formatPdfNumber(g)} ${formatPdfNumber(b)} RG`,
61
+ `${formatPdfNumber(Math.max(0.1, strokeWidth))} w`,
62
+ "1 J",
63
+ "1 j"
64
+ ];
65
+ for (const path of transformedPaths) {
66
+ if (path.length < 4) continue;
67
+ commands.push(
68
+ `${formatPdfNumber(path[0] - x1)} ${formatPdfNumber(path[1] - y1)} m`
69
+ );
70
+ for (let i = 2; i < path.length; i += 2) {
71
+ commands.push(
72
+ `${formatPdfNumber(path[i] - x1)} ${formatPdfNumber(path[i + 1] - y1)} l`
73
+ );
74
+ }
75
+ commands.push("S");
76
+ }
77
+ commands.push("Q");
78
+ const appearanceStream = PDFRawStream.of(
79
+ context.obj({
80
+ Type: PDFName.of("XObject"),
81
+ Subtype: PDFName.of("Form"),
82
+ BBox: context.obj([0, 0, appearanceWidth, appearanceHeight]),
83
+ Resources: context.obj({})
84
+ }),
85
+ new TextEncoder().encode(commands.join("\n"))
86
+ );
87
+ const appearanceRef = context.register(appearanceStream);
88
+ const ap = context.obj({ N: appearanceRef });
89
+ const mainAnn = context.obj({
90
+ Type: PDFName.of("Annot"),
91
+ Subtype: PDFName.of("Ink"),
92
+ Rect: rect,
93
+ InkList: inkList,
94
+ C: context.obj([PDFNumber.of(r), PDFNumber.of(g), PDFNumber.of(b)]),
95
+ T: stringToPDFHexString(annotation.title || UNKNOWN_USER),
96
+ Contents: stringToPDFHexString(annotation.contentsObj?.text || ""),
97
+ M: PDFString.of(annotation.date || ""),
98
+ NM: PDFString.of(annotation.id),
99
+ Border: context.obj([0, 0, 0]),
100
+ BS: bs,
101
+ CA: PDFNumber.of(opacity),
102
+ AP: ap
103
+ });
104
+ const mainAnnRef = context.register(mainAnn);
105
+ this.addAnnotationToPage(page, mainAnnRef);
106
+ for (const comment of annotation.comments || []) {
107
+ const replyAnn = context.obj({
108
+ Type: PDFName.of("Annot"),
109
+ Subtype: PDFName.of("Text"),
110
+ Rect: convertKonvaRectToPdfRect(annotation.konvaClientRect, pageHeight),
111
+ Contents: stringToPDFHexString(comment.content),
112
+ T: stringToPDFHexString(comment.title || UNKNOWN_USER),
113
+ M: PDFString.of(comment.date || ""),
114
+ C: context.obj([PDFNumber.of(r), PDFNumber.of(g), PDFNumber.of(b)]),
115
+ IRT: mainAnnRef,
116
+ RT: PDFName.of("R"),
117
+ NM: PDFString.of(comment.id),
118
+ Open: false
119
+ });
120
+ const replyAnnRef = context.register(replyAnn);
121
+ this.addAnnotationToPage(page, replyAnnRef);
122
+ }
123
+ }
124
+ }
@@ -0,0 +1,4 @@
1
+ import { AnnotationParser } from './parse.js';
2
+ export declare class LineParser extends AnnotationParser {
3
+ parse(): Promise<void>;
4
+ }