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,617 @@
1
+ <template>
2
+ <div ref="viewerRoot" class="flex flex-col h-full">
3
+ <!-- Header slot: defaults to ViewerBar -->
4
+ <slot name="header">
5
+ <ViewerBar />
6
+ </slot>
7
+
8
+ <div class="relative flex-1 min-h-0">
9
+ <div
10
+ ref="scrollContainer"
11
+ class="absolute inset-0 overflow-auto bg-muted/50"
12
+ :class="{ 'overflow-hidden': isSinglePageMode }"
13
+ >
14
+ <ClientOnly>
15
+ <!-- Single-page-at-a-time mode -->
16
+ <div
17
+ v-if="isSinglePageMode && singlePageMeta"
18
+ class="flex flex-col items-center py-4"
19
+ :style="{ minHeight: '100%' }"
20
+ >
21
+ <div
22
+ :ref="(el) => onPageRef(singlePageMeta.pageNumber, el)"
23
+ :style="pageSlotStyle(singlePageMeta)"
24
+ class="kviewer-page-slot"
25
+ >
26
+ <div :style="pageRotationStyle(singlePageMeta)">
27
+ <PdfPage
28
+ :key="singlePageMeta.pageNumber"
29
+ :page-number="singlePageMeta.pageNumber"
30
+ :page-meta="singlePageMeta"
31
+ :scale="pageScale"
32
+ />
33
+ </div>
34
+ </div>
35
+ </div>
36
+
37
+ <!-- Continuous scroll — single-page layout -->
38
+ <div
39
+ v-else-if="pageSettings.layoutMode.value === 'single'"
40
+ class="flex flex-col items-center gap-4 py-4"
41
+ >
42
+ <div
43
+ v-for="meta in pageMetas"
44
+ :key="meta.pageNumber"
45
+ :ref="(el) => onPageRef(meta.pageNumber, el)"
46
+ :style="pageSlotStyle(meta)"
47
+ class="kviewer-page-slot"
48
+ >
49
+ <div :style="pageRotationStyle(meta)">
50
+ <PdfPage
51
+ v-if="isPageRendered(meta.pageNumber)"
52
+ :page-number="meta.pageNumber"
53
+ :page-meta="meta"
54
+ :scale="pageScale"
55
+ />
56
+ </div>
57
+ </div>
58
+ </div>
59
+
60
+ <!-- Continuous scroll — two-page (side-by-side) layout -->
61
+ <div
62
+ v-else
63
+ class="flex flex-col items-center gap-4 py-4"
64
+ >
65
+ <div
66
+ v-for="(pair, index) in pagedPairs"
67
+ :key="index"
68
+ class="flex gap-4"
69
+ >
70
+ <div
71
+ v-for="meta in pair"
72
+ :key="meta.pageNumber"
73
+ :ref="(el) => onPageRef(meta.pageNumber, el)"
74
+ :style="pageSlotStyle(meta)"
75
+ class="kviewer-page-slot"
76
+ >
77
+ <div :style="pageRotationStyle(meta)">
78
+ <PdfPage
79
+ v-if="isPageRendered(meta.pageNumber)"
80
+ :page-number="meta.pageNumber"
81
+ :page-meta="meta"
82
+ :scale="pageScale"
83
+ />
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </ClientOnly>
89
+ </div>
90
+
91
+ <ClientOnly>
92
+ <FloatingPageIndicator :scroll-container="scrollContainer" />
93
+ </ClientOnly>
94
+ </div>
95
+
96
+ <!-- Footer slot: empty by default -->
97
+ <slot name="footer" />
98
+
99
+ <ClientOnly>
100
+ <AnnotationToolbar />
101
+ </ClientOnly>
102
+
103
+ <ClientOnly>
104
+ <FreeTextModal
105
+ v-model:open="freeTextModalOpen"
106
+ :default-color="freeTextDefaults.color"
107
+ :default-font-size="freeTextDefaults.fontSize"
108
+ :initial-text="freeTextDefaults.initialText"
109
+ @submit="onFreeTextSubmit"
110
+ @cancel="onFreeTextCancel"
111
+ />
112
+ </ClientOnly>
113
+ </div>
114
+ </template>
115
+
116
+ <script setup>
117
+ import {
118
+ ref,
119
+ shallowRef,
120
+ computed,
121
+ watch,
122
+ nextTick,
123
+ triggerRef,
124
+ onMounted,
125
+ onBeforeUnmount
126
+ } from "vue";
127
+ import ViewerBar from "./ViewerBar.vue";
128
+ import PdfPage from "./PdfPage.vue";
129
+ import FreeTextModal from "./modals/FreeTextModal.vue";
130
+ import AnnotationToolbar from "./AnnotationToolbar.vue";
131
+ import FloatingPageIndicator from "./FloatingPageIndicator.vue";
132
+ import { providePageSettings } from "../composables/usePageSettings";
133
+ import { mapViewModeToFitMode, normalizeZoom, provideViewerState } from "../composables/useViewerState";
134
+ import { provideViewerSearch } from "../composables/useViewerSearch";
135
+ import { createAnnotationEngine } from "../composables/useAnnotationEngine";
136
+ import { normalizeImportedAnnotations } from "../annotation/engine/import-normalize";
137
+ import { getTimestampString } from "../annotation/engine/utils";
138
+ import { downloadPdfBytes } from "../annotation/pdf-export/download";
139
+ import { exportAnnotationsToPdf } from "../annotation/pdf-export/export";
140
+ import { createPageVirtualization } from "../composables/usePageVirtualization";
141
+ import { createPageProxyCache } from "../composables/usePageProxyCache";
142
+ import { createSearchIndex } from "../composables/useSearchIndex";
143
+ import { provideFormFields } from "../composables/useFormFields";
144
+ import { isIPad, getTouchDistance, getTouchCenter, clampScale } from "../annotation/engine/input-device";
145
+ const props = defineProps({
146
+ source: { type: null, required: true },
147
+ textLayer: { type: Boolean, required: false },
148
+ userName: { type: String, required: false, default: void 0 },
149
+ stamps: { type: Array, required: false, default: void 0 },
150
+ signatureHandlers: { type: Object, required: false, default: void 0 },
151
+ viewMode: { type: String, required: false, default: "fit-width" },
152
+ zoom: { type: Number, required: false, default: 1 },
153
+ active: { type: Boolean, required: false, default: true }
154
+ });
155
+ const viewerRoot = ref(null);
156
+ const scrollContainer = ref(null);
157
+ const { state: viewerState, setScrollToPageFn } = provideViewerState();
158
+ const viewerSearch = provideViewerSearch();
159
+ const formFieldsState = provideFormFields();
160
+ const pageSettings = providePageSettings(viewerRoot);
161
+ const virtualization = createPageVirtualization();
162
+ setScrollToPageFn((pageNumber) => {
163
+ if (isSinglePageMode.value) {
164
+ viewerState.currentPage.value = pageNumber;
165
+ } else {
166
+ virtualization.scrollToPage(pageNumber);
167
+ }
168
+ });
169
+ const proxyCache = createPageProxyCache();
170
+ const searchIndex = createSearchIndex();
171
+ const doc = shallowRef(null);
172
+ let painter = null;
173
+ const pageMetas = virtualization.pageMetas;
174
+ const { isPageRendered } = virtualization;
175
+ watch(
176
+ virtualization.currentPage,
177
+ (page) => {
178
+ viewerState.currentPage.value = page;
179
+ }
180
+ );
181
+ function onPageRef(pageNumber, el) {
182
+ if (el) {
183
+ virtualization.observePage(pageNumber, el);
184
+ } else {
185
+ virtualization.unobservePage(pageNumber);
186
+ }
187
+ }
188
+ const pageScale = computed(() => {
189
+ return viewerState.scale.value;
190
+ });
191
+ const isSinglePageMode = computed(() => pageSettings.pageTransition.value === "single-page");
192
+ const singlePageMeta = computed(() => {
193
+ const metas = pageMetas.value;
194
+ const page = viewerState.currentPage.value;
195
+ return metas[page - 1] ?? metas[0] ?? null;
196
+ });
197
+ const isRotatedSideways = computed(() => {
198
+ const r = pageSettings.rotation.value;
199
+ return r === 90 || r === 270;
200
+ });
201
+ function pageSlotStyle(meta) {
202
+ const s = pageScale.value;
203
+ if (isRotatedSideways.value) {
204
+ return {
205
+ width: meta.height * s + "px",
206
+ height: meta.width * s + "px"
207
+ };
208
+ }
209
+ return {
210
+ width: meta.width * s + "px",
211
+ height: meta.height * s + "px"
212
+ };
213
+ }
214
+ function pageRotationStyle(meta) {
215
+ const r = pageSettings.rotation.value;
216
+ if (r === 0) return {};
217
+ const s = pageScale.value;
218
+ const w = meta.width * s;
219
+ const h = meta.height * s;
220
+ if (r === 90) {
221
+ return {
222
+ transform: `rotate(90deg) translateY(-${h}px)`,
223
+ transformOrigin: "top left",
224
+ width: w + "px",
225
+ height: h + "px"
226
+ };
227
+ }
228
+ if (r === 180) {
229
+ return {
230
+ transform: `rotate(180deg)`,
231
+ transformOrigin: "center center",
232
+ width: w + "px",
233
+ height: h + "px"
234
+ };
235
+ }
236
+ return {
237
+ transform: `rotate(270deg) translateX(-${w}px)`,
238
+ transformOrigin: "top left",
239
+ width: w + "px",
240
+ height: h + "px"
241
+ };
242
+ }
243
+ const pagedPairs = computed(() => {
244
+ const metas = pageMetas.value;
245
+ const pairs = [];
246
+ for (let i = 0; i < metas.length; i += 2) {
247
+ if (i + 1 < metas.length) {
248
+ pairs.push([metas[i], metas[i + 1]]);
249
+ } else {
250
+ pairs.push([metas[i]]);
251
+ }
252
+ }
253
+ return pairs;
254
+ });
255
+ const freeTextModalOpen = ref(false);
256
+ const freeTextDefaults = ref({ color: "#ff0000", fontSize: 18 });
257
+ let freeTextResolve = null;
258
+ function onRequestTextInput(opts) {
259
+ return new Promise((resolve) => {
260
+ freeTextDefaults.value = {
261
+ color: opts.defaultColor,
262
+ fontSize: opts.defaultFontSize,
263
+ initialText: opts.initialText
264
+ };
265
+ freeTextResolve = resolve;
266
+ freeTextModalOpen.value = true;
267
+ });
268
+ }
269
+ function onFreeTextSubmit(result) {
270
+ freeTextModalOpen.value = false;
271
+ freeTextResolve?.(result);
272
+ freeTextResolve = null;
273
+ }
274
+ function onFreeTextCancel() {
275
+ freeTextModalOpen.value = false;
276
+ freeTextResolve?.({
277
+ inputValue: freeTextDefaults.value.initialText ?? "",
278
+ color: freeTextDefaults.value.color,
279
+ fontSize: freeTextDefaults.value.fontSize,
280
+ cancelled: true
281
+ });
282
+ freeTextResolve = null;
283
+ }
284
+ async function loadDocument() {
285
+ const pdfjs = await import("pdfjs-dist/legacy/build/pdf.mjs");
286
+ if (!pdfjs.GlobalWorkerOptions.workerSrc) {
287
+ const workerModule = await import("pdfjs-dist/legacy/build/pdf.worker.min.mjs?url");
288
+ pdfjs.GlobalWorkerOptions.workerSrc = workerModule.default;
289
+ }
290
+ const source = props.source;
291
+ const loadingTask = pdfjs.getDocument(
292
+ source
293
+ );
294
+ const pdfDoc = await loadingTask.promise;
295
+ doc.value = pdfDoc;
296
+ viewerState.doc.value = pdfDoc;
297
+ viewerState.totalPages.value = pdfDoc.numPages;
298
+ proxyCache.setDocument(pdfDoc);
299
+ await virtualization.init(pdfDoc, scrollContainer.value);
300
+ const metas = virtualization.pageMetas.value;
301
+ if (metas.length > 0) {
302
+ viewerState.pageDimensions.value = {
303
+ width: metas[0].width,
304
+ height: metas[0].height
305
+ };
306
+ }
307
+ viewerSearch.setScrollToPage((pageNumber) => {
308
+ virtualization.scrollToPage(pageNumber);
309
+ });
310
+ searchIndex.buildIndex(pdfDoc).then(() => {
311
+ viewerSearch.setSearchIndex({
312
+ getPageText: searchIndex.getPageText,
313
+ totalPages: pdfDoc.numPages
314
+ });
315
+ });
316
+ }
317
+ let resizeObserver = null;
318
+ function ensurePdfExtension(fileName) {
319
+ return fileName.toLowerCase().endsWith(".pdf") ? fileName : `${fileName}.pdf`;
320
+ }
321
+ function getSourceBaseName() {
322
+ if (typeof props.source !== "string" || props.source.length === 0) {
323
+ return "annotated";
324
+ }
325
+ try {
326
+ const url = new URL(props.source, window.location.href);
327
+ const path = url.pathname;
328
+ const name = path.split("/").pop() || "";
329
+ const withoutExtension = name.replace(/\.[^/.]+$/, "");
330
+ return withoutExtension || "annotated";
331
+ } catch {
332
+ const noQuery = props.source.split(/[?#]/)[0] || "";
333
+ const name = noQuery.split("/").pop() || "";
334
+ const withoutExtension = name.replace(/\.[^/.]+$/, "");
335
+ return withoutExtension || "annotated";
336
+ }
337
+ }
338
+ function resolveExportFileName(fileName) {
339
+ if (fileName && fileName.trim().length > 0) {
340
+ return ensurePdfExtension(fileName.trim());
341
+ }
342
+ const baseName = getSourceBaseName();
343
+ return `${baseName}_${getTimestampString()}.pdf`;
344
+ }
345
+ async function exportPdf(options = {}) {
346
+ if (!doc.value) {
347
+ throw new Error("Cannot export PDF before document is loaded");
348
+ }
349
+ if (!painter) {
350
+ throw new Error("Cannot export PDF before annotation engine is initialized");
351
+ }
352
+ const mergedOptions = {
353
+ flatten: false,
354
+ download: false,
355
+ preserveOriginalAnnotations: false,
356
+ ...options
357
+ };
358
+ const pdfData = await doc.value.getData();
359
+ const annotations = painter.getData();
360
+ const formFieldValues = formFieldsState.getAllFieldValues();
361
+ const bytes = await exportAnnotationsToPdf({
362
+ pdfData,
363
+ annotations,
364
+ options: mergedOptions,
365
+ formFieldValues
366
+ });
367
+ if (mergedOptions.download) {
368
+ downloadPdfBytes(bytes, resolveExportFileName(mergedOptions.fileName));
369
+ }
370
+ return bytes;
371
+ }
372
+ async function importAnnotations(annotations, options) {
373
+ const mode = options?.mode ?? "replace";
374
+ const normalized = normalizeImportedAnnotations(
375
+ annotations,
376
+ props.userName ?? "User"
377
+ );
378
+ if (!painter) {
379
+ return { loaded: 0, skipped: normalized.skipped + normalized.annotations.length };
380
+ }
381
+ if (mode === "replace") {
382
+ painter.replaceAnnotations(normalized.annotations);
383
+ viewerState.history.reset();
384
+ } else {
385
+ painter.mergeAnnotations(normalized.annotations);
386
+ }
387
+ viewerState.annotations.value = new Map(
388
+ painter.getData().map((annotation) => [annotation.id, annotation])
389
+ );
390
+ triggerRef(viewerState.annotations);
391
+ viewerState.selectedAnnotation.value = null;
392
+ viewerState.selectedAnnotations.value = [];
393
+ viewerState.selectionRect.value = null;
394
+ return {
395
+ loaded: normalized.annotations.length,
396
+ skipped: normalized.skipped
397
+ };
398
+ }
399
+ function getKonvaCanvasState() {
400
+ return painter?.getCanvasState() ?? {};
401
+ }
402
+ let pinchActive = false;
403
+ let pinchStartDistance = 0;
404
+ let pinchStartScale = 1;
405
+ let pinchCurrentRatio = 1;
406
+ let pinchOriginX = 0;
407
+ let pinchOriginY = 0;
408
+ let pinchStartViewportX = 0;
409
+ let pinchStartViewportY = 0;
410
+ let pinchCurrentViewportX = 0;
411
+ let pinchCurrentViewportY = 0;
412
+ function getPinchTarget() {
413
+ return scrollContainer.value?.firstElementChild;
414
+ }
415
+ function onPinchTouchStart(e) {
416
+ if (!viewerState.stylusMode.value) return;
417
+ if (e.touches.length === 2) {
418
+ const sc = scrollContainer.value;
419
+ const scRect = sc.getBoundingClientRect();
420
+ const center = getTouchCenter(e.touches[0], e.touches[1]);
421
+ pinchActive = true;
422
+ pinchStartDistance = getTouchDistance(e.touches[0], e.touches[1]);
423
+ pinchStartScale = viewerState.scale.value;
424
+ pinchCurrentRatio = 1;
425
+ pinchStartViewportX = center.x - scRect.left;
426
+ pinchStartViewportY = center.y - scRect.top;
427
+ pinchCurrentViewportX = pinchStartViewportX;
428
+ pinchCurrentViewportY = pinchStartViewportY;
429
+ pinchOriginX = pinchStartViewportX + sc.scrollLeft;
430
+ pinchOriginY = pinchStartViewportY + sc.scrollTop;
431
+ const target = getPinchTarget();
432
+ if (target) {
433
+ target.style.transformOrigin = `${pinchOriginX}px ${pinchOriginY}px`;
434
+ target.style.willChange = "transform";
435
+ }
436
+ }
437
+ }
438
+ function onPinchTouchMove(e) {
439
+ if (!viewerState.stylusMode.value || !pinchActive || e.touches.length < 2) return;
440
+ e.preventDefault();
441
+ const sc = scrollContainer.value;
442
+ if (!sc) return;
443
+ const scRect = sc.getBoundingClientRect();
444
+ const currentDistance = getTouchDistance(e.touches[0], e.touches[1]);
445
+ pinchCurrentRatio = currentDistance / pinchStartDistance;
446
+ const clamped = clampScale(pinchStartScale * pinchCurrentRatio);
447
+ pinchCurrentRatio = clamped / pinchStartScale;
448
+ const center = getTouchCenter(e.touches[0], e.touches[1]);
449
+ pinchCurrentViewportX = center.x - scRect.left;
450
+ pinchCurrentViewportY = center.y - scRect.top;
451
+ const target = getPinchTarget();
452
+ if (target) target.style.transform = `scale(${pinchCurrentRatio})`;
453
+ sc.scrollLeft = pinchOriginX * pinchCurrentRatio - pinchCurrentViewportX;
454
+ sc.scrollTop = pinchOriginY * pinchCurrentRatio - pinchCurrentViewportY;
455
+ }
456
+ function onPinchTouchEnd(e) {
457
+ if (e.touches.length < 2 && pinchActive) {
458
+ pinchActive = false;
459
+ const target = getPinchTarget();
460
+ if (target) {
461
+ target.style.transform = "";
462
+ target.style.willChange = "";
463
+ }
464
+ const finalScale = clampScale(pinchStartScale * pinchCurrentRatio);
465
+ if (Math.abs(finalScale - pinchStartScale) > 0.01) {
466
+ const layoutRatio = finalScale / pinchStartScale;
467
+ const newScrollLeft = pinchOriginX * layoutRatio - pinchCurrentViewportX;
468
+ const newScrollTop = pinchOriginY * layoutRatio - pinchCurrentViewportY;
469
+ viewerState.setScale(finalScale);
470
+ nextTick(() => {
471
+ const sc = scrollContainer.value;
472
+ if (sc) {
473
+ sc.scrollLeft = newScrollLeft;
474
+ sc.scrollTop = newScrollTop;
475
+ }
476
+ });
477
+ }
478
+ pinchCurrentRatio = 1;
479
+ }
480
+ }
481
+ let wheelCooldown = false;
482
+ function onSinglePageWheel(event) {
483
+ if (!isSinglePageMode.value) return;
484
+ event.preventDefault();
485
+ if (wheelCooldown) return;
486
+ wheelCooldown = true;
487
+ setTimeout(() => {
488
+ wheelCooldown = false;
489
+ }, 300);
490
+ const total = viewerState.totalPages.value;
491
+ const cur = viewerState.currentPage.value;
492
+ if (event.deltaY > 0 && cur < total) {
493
+ viewerState.currentPage.value = cur + 1;
494
+ } else if (event.deltaY < 0 && cur > 1) {
495
+ viewerState.currentPage.value = cur - 1;
496
+ }
497
+ }
498
+ function onGlobalKeydown(event) {
499
+ if (!props.active) return;
500
+ if (isSinglePageMode.value) {
501
+ const total = viewerState.totalPages.value;
502
+ const cur = viewerState.currentPage.value;
503
+ if ((event.key === "ArrowDown" || event.key === "PageDown") && cur < total) {
504
+ event.preventDefault();
505
+ viewerState.currentPage.value = cur + 1;
506
+ return;
507
+ }
508
+ if ((event.key === "ArrowUp" || event.key === "PageUp") && cur > 1) {
509
+ event.preventDefault();
510
+ viewerState.currentPage.value = cur - 1;
511
+ return;
512
+ }
513
+ }
514
+ const isFindShortcut = (event.metaKey || event.ctrlKey) && !event.altKey && !event.shiftKey && event.key.toLowerCase() === "f";
515
+ if (isFindShortcut) {
516
+ event.preventDefault();
517
+ viewerSearch.open();
518
+ nextTick(() => {
519
+ viewerSearch.focusInput();
520
+ });
521
+ return;
522
+ }
523
+ if (event.key === "Escape" && viewerSearch.isOpen.value) {
524
+ event.preventDefault();
525
+ viewerSearch.close();
526
+ }
527
+ }
528
+ onMounted(() => {
529
+ painter = createAnnotationEngine(viewerState, {
530
+ userName: props.userName,
531
+ onRequestTextInput
532
+ });
533
+ viewerState.selectTool(viewerState.activeTool.value);
534
+ if (props.stamps) viewerState.stamps.value = props.stamps;
535
+ if (props.signatureHandlers) {
536
+ viewerState.signatureHandlers = props.signatureHandlers;
537
+ viewerState.loadSignatures();
538
+ }
539
+ if (scrollContainer.value) {
540
+ resizeObserver = new ResizeObserver((entries) => {
541
+ const entry = entries[0];
542
+ if (entry) {
543
+ viewerState.containerDimensions.value = {
544
+ width: entry.contentRect.width - 40,
545
+ height: entry.contentRect.height
546
+ };
547
+ }
548
+ });
549
+ resizeObserver.observe(scrollContainer.value);
550
+ scrollContainer.value.addEventListener("scroll", () => {
551
+ viewerState.selectionRect.value = null;
552
+ }, { passive: true });
553
+ scrollContainer.value.addEventListener("wheel", onSinglePageWheel, { passive: false });
554
+ scrollContainer.value.addEventListener("touchstart", onPinchTouchStart, { passive: true });
555
+ scrollContainer.value.addEventListener("touchmove", onPinchTouchMove, { passive: false });
556
+ scrollContainer.value.addEventListener("touchend", onPinchTouchEnd, { passive: true });
557
+ scrollContainer.value.addEventListener("touchcancel", onPinchTouchEnd, { passive: true });
558
+ }
559
+ if (isIPad()) {
560
+ viewerState.setStylusMode(true);
561
+ }
562
+ loadDocument();
563
+ window.addEventListener("keydown", onGlobalKeydown);
564
+ });
565
+ watch(
566
+ () => props.source,
567
+ () => {
568
+ viewerSearch.reset();
569
+ formFieldsState.reset();
570
+ searchIndex.destroy();
571
+ virtualization.destroy();
572
+ proxyCache.clear();
573
+ doc.value?.destroy();
574
+ loadDocument();
575
+ }
576
+ );
577
+ watch(
578
+ [() => props.viewMode, () => props.zoom],
579
+ ([newViewMode, newZoom]) => {
580
+ if (newViewMode === "zoom") {
581
+ viewerState.setScale(normalizeZoom(newZoom));
582
+ return;
583
+ }
584
+ viewerState.setFitMode(mapViewModeToFitMode(newViewMode));
585
+ },
586
+ { immediate: true }
587
+ );
588
+ watch(
589
+ () => props.stamps,
590
+ (newStamps) => {
591
+ viewerState.stamps.value = newStamps ?? [];
592
+ }
593
+ );
594
+ onBeforeUnmount(() => {
595
+ window.removeEventListener("keydown", onGlobalKeydown);
596
+ scrollContainer.value?.removeEventListener("wheel", onSinglePageWheel);
597
+ scrollContainer.value?.removeEventListener("touchstart", onPinchTouchStart);
598
+ scrollContainer.value?.removeEventListener("touchmove", onPinchTouchMove);
599
+ scrollContainer.value?.removeEventListener("touchend", onPinchTouchEnd);
600
+ scrollContainer.value?.removeEventListener("touchcancel", onPinchTouchEnd);
601
+ resizeObserver?.disconnect();
602
+ viewerSearch.reset();
603
+ searchIndex.destroy();
604
+ virtualization.destroy();
605
+ proxyCache.clear();
606
+ painter?.destroy();
607
+ doc.value?.destroy();
608
+ });
609
+ defineExpose({
610
+ getAnnotations: () => painter?.getData() ?? [],
611
+ importAnnotations,
612
+ exportPdf,
613
+ getKonvaCanvasState,
614
+ getFormFieldValues: () => formFieldsState.getAllFieldValues(),
615
+ setFormFieldValue: (fieldName, value) => formFieldsState.setFieldValueByName(fieldName, value)
616
+ });
617
+ </script>
@@ -0,0 +1,45 @@
1
+ import { type ViewMode } from '../composables/useViewerState.js';
2
+ import type { IAnnotationStore, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
3
+ import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
4
+ type __VLS_Props = {
5
+ source: string | Uint8Array | object;
6
+ textLayer?: boolean;
7
+ userName?: string;
8
+ stamps?: StampDefinition[];
9
+ signatureHandlers?: SignatureHandlers;
10
+ viewMode?: ViewMode;
11
+ zoom?: number;
12
+ /** When false, global keyboard shortcuts (e.g. Cmd+F) are suppressed. Used by ViewerTabs to prevent hidden viewers from capturing input. */
13
+ active?: boolean;
14
+ };
15
+ declare function exportPdf(options?: ExportPdfOptions): Promise<Uint8Array>;
16
+ type ImportMode = 'replace' | 'merge';
17
+ declare function importAnnotations(annotations: IAnnotationStore[], options?: {
18
+ mode?: ImportMode;
19
+ }): Promise<{
20
+ loaded: number;
21
+ skipped: number;
22
+ }>;
23
+ declare function getKonvaCanvasState(): Record<number, string>;
24
+ declare var __VLS_1: {}, __VLS_40: {};
25
+ type __VLS_Slots = {} & {
26
+ header?: (props: typeof __VLS_1) => any;
27
+ } & {
28
+ footer?: (props: typeof __VLS_40) => any;
29
+ };
30
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
31
+ getAnnotations: () => IAnnotationStore[];
32
+ importAnnotations: typeof importAnnotations;
33
+ exportPdf: typeof exportPdf;
34
+ getKonvaCanvasState: typeof getKonvaCanvasState;
35
+ getFormFieldValues: () => import("../annotation/engine/types.js").FormFieldValue[];
36
+ setFormFieldValue: (fieldName: string, value: string | boolean | string[]) => void;
37
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
38
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
39
+ declare const _default: typeof __VLS_export;
40
+ export default _default;
41
+ type __VLS_WithSlots<T, S> = T & {
42
+ new (): {
43
+ $slots: S;
44
+ };
45
+ };
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;