@docmentis/udoc-viewer 0.5.1 → 0.5.3

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 (211) hide show
  1. package/package.json +1 -1
  2. package/dist/package.json +0 -56
  3. package/dist/src/UDocClient.d.ts +0 -383
  4. package/dist/src/UDocClient.d.ts.map +0 -1
  5. package/dist/src/UDocClient.js +0 -428
  6. package/dist/src/UDocClient.js.map +0 -1
  7. package/dist/src/UDocViewer.d.ts +0 -271
  8. package/dist/src/UDocViewer.d.ts.map +0 -1
  9. package/dist/src/UDocViewer.js +0 -769
  10. package/dist/src/UDocViewer.js.map +0 -1
  11. package/dist/src/fonts.d.ts +0 -29
  12. package/dist/src/fonts.d.ts.map +0 -1
  13. package/dist/src/fonts.js +0 -30
  14. package/dist/src/fonts.js.map +0 -1
  15. package/dist/src/index.d.ts +0 -9
  16. package/dist/src/index.d.ts.map +0 -1
  17. package/dist/src/index.js +0 -8
  18. package/dist/src/index.js.map +0 -1
  19. package/dist/src/performance/PerformanceCounter.d.ts +0 -132
  20. package/dist/src/performance/PerformanceCounter.d.ts.map +0 -1
  21. package/dist/src/performance/PerformanceCounter.js +0 -129
  22. package/dist/src/performance/PerformanceCounter.js.map +0 -1
  23. package/dist/src/performance/index.d.ts +0 -2
  24. package/dist/src/performance/index.d.ts.map +0 -1
  25. package/dist/src/performance/index.js +0 -2
  26. package/dist/src/performance/index.js.map +0 -1
  27. package/dist/src/ui/framework/component.d.ts +0 -68
  28. package/dist/src/ui/framework/component.d.ts.map +0 -1
  29. package/dist/src/ui/framework/component.js +0 -87
  30. package/dist/src/ui/framework/component.js.map +0 -1
  31. package/dist/src/ui/framework/dom.d.ts +0 -19
  32. package/dist/src/ui/framework/dom.d.ts.map +0 -1
  33. package/dist/src/ui/framework/dom.js +0 -29
  34. package/dist/src/ui/framework/dom.js.map +0 -1
  35. package/dist/src/ui/framework/events.d.ts +0 -18
  36. package/dist/src/ui/framework/events.d.ts.map +0 -1
  37. package/dist/src/ui/framework/events.js +0 -23
  38. package/dist/src/ui/framework/events.js.map +0 -1
  39. package/dist/src/ui/framework/index.d.ts +0 -15
  40. package/dist/src/ui/framework/index.d.ts.map +0 -1
  41. package/dist/src/ui/framework/index.js +0 -15
  42. package/dist/src/ui/framework/index.js.map +0 -1
  43. package/dist/src/ui/framework/selectors.d.ts +0 -51
  44. package/dist/src/ui/framework/selectors.d.ts.map +0 -1
  45. package/dist/src/ui/framework/selectors.js +0 -30
  46. package/dist/src/ui/framework/selectors.js.map +0 -1
  47. package/dist/src/ui/framework/store.d.ts +0 -37
  48. package/dist/src/ui/framework/store.d.ts.map +0 -1
  49. package/dist/src/ui/framework/store.js +0 -54
  50. package/dist/src/ui/framework/store.js.map +0 -1
  51. package/dist/src/ui/viewer/actions.d.ts +0 -131
  52. package/dist/src/ui/viewer/actions.d.ts.map +0 -1
  53. package/dist/src/ui/viewer/actions.js +0 -2
  54. package/dist/src/ui/viewer/actions.js.map +0 -1
  55. package/dist/src/ui/viewer/annotation/LinkRenderer.d.ts +0 -9
  56. package/dist/src/ui/viewer/annotation/LinkRenderer.d.ts.map +0 -1
  57. package/dist/src/ui/viewer/annotation/LinkRenderer.js +0 -17
  58. package/dist/src/ui/viewer/annotation/LinkRenderer.js.map +0 -1
  59. package/dist/src/ui/viewer/annotation/MarkupRenderer.d.ts +0 -21
  60. package/dist/src/ui/viewer/annotation/MarkupRenderer.d.ts.map +0 -1
  61. package/dist/src/ui/viewer/annotation/MarkupRenderer.js +0 -138
  62. package/dist/src/ui/viewer/annotation/MarkupRenderer.js.map +0 -1
  63. package/dist/src/ui/viewer/annotation/ShapeRenderer.d.ts +0 -33
  64. package/dist/src/ui/viewer/annotation/ShapeRenderer.d.ts.map +0 -1
  65. package/dist/src/ui/viewer/annotation/ShapeRenderer.js +0 -378
  66. package/dist/src/ui/viewer/annotation/ShapeRenderer.js.map +0 -1
  67. package/dist/src/ui/viewer/annotation/TextRenderer.d.ts +0 -23
  68. package/dist/src/ui/viewer/annotation/TextRenderer.d.ts.map +0 -1
  69. package/dist/src/ui/viewer/annotation/TextRenderer.js +0 -196
  70. package/dist/src/ui/viewer/annotation/TextRenderer.js.map +0 -1
  71. package/dist/src/ui/viewer/annotation/index.d.ts +0 -8
  72. package/dist/src/ui/viewer/annotation/index.d.ts.map +0 -1
  73. package/dist/src/ui/viewer/annotation/index.js +0 -8
  74. package/dist/src/ui/viewer/annotation/index.js.map +0 -1
  75. package/dist/src/ui/viewer/annotation/render.d.ts +0 -24
  76. package/dist/src/ui/viewer/annotation/render.d.ts.map +0 -1
  77. package/dist/src/ui/viewer/annotation/render.js +0 -184
  78. package/dist/src/ui/viewer/annotation/render.js.map +0 -1
  79. package/dist/src/ui/viewer/annotation/types.d.ts +0 -239
  80. package/dist/src/ui/viewer/annotation/types.d.ts.map +0 -1
  81. package/dist/src/ui/viewer/annotation/types.js +0 -7
  82. package/dist/src/ui/viewer/annotation/types.js.map +0 -1
  83. package/dist/src/ui/viewer/annotation/utils.d.ts +0 -37
  84. package/dist/src/ui/viewer/annotation/utils.d.ts.map +0 -1
  85. package/dist/src/ui/viewer/annotation/utils.js +0 -82
  86. package/dist/src/ui/viewer/annotation/utils.js.map +0 -1
  87. package/dist/src/ui/viewer/components/AnnotationPanel.d.ts +0 -19
  88. package/dist/src/ui/viewer/components/AnnotationPanel.d.ts.map +0 -1
  89. package/dist/src/ui/viewer/components/AnnotationPanel.js +0 -284
  90. package/dist/src/ui/viewer/components/AnnotationPanel.js.map +0 -1
  91. package/dist/src/ui/viewer/components/FloatingToolbar.d.ts +0 -9
  92. package/dist/src/ui/viewer/components/FloatingToolbar.d.ts.map +0 -1
  93. package/dist/src/ui/viewer/components/FloatingToolbar.js +0 -305
  94. package/dist/src/ui/viewer/components/FloatingToolbar.js.map +0 -1
  95. package/dist/src/ui/viewer/components/LeftPanel.d.ts +0 -10
  96. package/dist/src/ui/viewer/components/LeftPanel.d.ts.map +0 -1
  97. package/dist/src/ui/viewer/components/LeftPanel.js +0 -165
  98. package/dist/src/ui/viewer/components/LeftPanel.js.map +0 -1
  99. package/dist/src/ui/viewer/components/LoadingOverlay.d.ts +0 -12
  100. package/dist/src/ui/viewer/components/LoadingOverlay.d.ts.map +0 -1
  101. package/dist/src/ui/viewer/components/LoadingOverlay.js +0 -88
  102. package/dist/src/ui/viewer/components/LoadingOverlay.js.map +0 -1
  103. package/dist/src/ui/viewer/components/OutlinePanel.d.ts +0 -10
  104. package/dist/src/ui/viewer/components/OutlinePanel.d.ts.map +0 -1
  105. package/dist/src/ui/viewer/components/OutlinePanel.js +0 -169
  106. package/dist/src/ui/viewer/components/OutlinePanel.js.map +0 -1
  107. package/dist/src/ui/viewer/components/PasswordDialog.d.ts +0 -15
  108. package/dist/src/ui/viewer/components/PasswordDialog.d.ts.map +0 -1
  109. package/dist/src/ui/viewer/components/PasswordDialog.js +0 -143
  110. package/dist/src/ui/viewer/components/PasswordDialog.js.map +0 -1
  111. package/dist/src/ui/viewer/components/RightPanel.d.ts +0 -9
  112. package/dist/src/ui/viewer/components/RightPanel.d.ts.map +0 -1
  113. package/dist/src/ui/viewer/components/RightPanel.js +0 -102
  114. package/dist/src/ui/viewer/components/RightPanel.js.map +0 -1
  115. package/dist/src/ui/viewer/components/Spread.d.ts +0 -43
  116. package/dist/src/ui/viewer/components/Spread.d.ts.map +0 -1
  117. package/dist/src/ui/viewer/components/Spread.js +0 -345
  118. package/dist/src/ui/viewer/components/Spread.js.map +0 -1
  119. package/dist/src/ui/viewer/components/ThumbnailPanel.d.ts +0 -11
  120. package/dist/src/ui/viewer/components/ThumbnailPanel.d.ts.map +0 -1
  121. package/dist/src/ui/viewer/components/ThumbnailPanel.js +0 -240
  122. package/dist/src/ui/viewer/components/ThumbnailPanel.js.map +0 -1
  123. package/dist/src/ui/viewer/components/Toolbar.d.ts +0 -9
  124. package/dist/src/ui/viewer/components/Toolbar.d.ts.map +0 -1
  125. package/dist/src/ui/viewer/components/Toolbar.js +0 -93
  126. package/dist/src/ui/viewer/components/Toolbar.js.map +0 -1
  127. package/dist/src/ui/viewer/components/ViewModeMenu.d.ts +0 -9
  128. package/dist/src/ui/viewer/components/ViewModeMenu.d.ts.map +0 -1
  129. package/dist/src/ui/viewer/components/ViewModeMenu.js +0 -169
  130. package/dist/src/ui/viewer/components/ViewModeMenu.js.map +0 -1
  131. package/dist/src/ui/viewer/components/Viewport.d.ts +0 -10
  132. package/dist/src/ui/viewer/components/Viewport.d.ts.map +0 -1
  133. package/dist/src/ui/viewer/components/Viewport.js +0 -1076
  134. package/dist/src/ui/viewer/components/Viewport.js.map +0 -1
  135. package/dist/src/ui/viewer/effects.d.ts +0 -8
  136. package/dist/src/ui/viewer/effects.d.ts.map +0 -1
  137. package/dist/src/ui/viewer/effects.js +0 -207
  138. package/dist/src/ui/viewer/effects.js.map +0 -1
  139. package/dist/src/ui/viewer/icons.d.ts +0 -32
  140. package/dist/src/ui/viewer/icons.d.ts.map +0 -1
  141. package/dist/src/ui/viewer/icons.js +0 -44
  142. package/dist/src/ui/viewer/icons.js.map +0 -1
  143. package/dist/src/ui/viewer/index.d.ts +0 -6
  144. package/dist/src/ui/viewer/index.d.ts.map +0 -1
  145. package/dist/src/ui/viewer/index.js +0 -6
  146. package/dist/src/ui/viewer/index.js.map +0 -1
  147. package/dist/src/ui/viewer/layout/index.d.ts +0 -3
  148. package/dist/src/ui/viewer/layout/index.d.ts.map +0 -1
  149. package/dist/src/ui/viewer/layout/index.js +0 -3
  150. package/dist/src/ui/viewer/layout/index.js.map +0 -1
  151. package/dist/src/ui/viewer/layout/pixelAlign.d.ts +0 -7
  152. package/dist/src/ui/viewer/layout/pixelAlign.d.ts.map +0 -1
  153. package/dist/src/ui/viewer/layout/pixelAlign.js +0 -22
  154. package/dist/src/ui/viewer/layout/pixelAlign.js.map +0 -1
  155. package/dist/src/ui/viewer/layout/spreadLayout.d.ts +0 -93
  156. package/dist/src/ui/viewer/layout/spreadLayout.d.ts.map +0 -1
  157. package/dist/src/ui/viewer/layout/spreadLayout.js +0 -315
  158. package/dist/src/ui/viewer/layout/spreadLayout.js.map +0 -1
  159. package/dist/src/ui/viewer/navigation.d.ts +0 -80
  160. package/dist/src/ui/viewer/navigation.d.ts.map +0 -1
  161. package/dist/src/ui/viewer/navigation.js +0 -59
  162. package/dist/src/ui/viewer/navigation.js.map +0 -1
  163. package/dist/src/ui/viewer/reducer.d.ts +0 -4
  164. package/dist/src/ui/viewer/reducer.d.ts.map +0 -1
  165. package/dist/src/ui/viewer/reducer.js +0 -305
  166. package/dist/src/ui/viewer/reducer.js.map +0 -1
  167. package/dist/src/ui/viewer/shell.d.ts +0 -34
  168. package/dist/src/ui/viewer/shell.d.ts.map +0 -1
  169. package/dist/src/ui/viewer/shell.js +0 -93
  170. package/dist/src/ui/viewer/shell.js.map +0 -1
  171. package/dist/src/ui/viewer/state.d.ts +0 -89
  172. package/dist/src/ui/viewer/state.d.ts.map +0 -1
  173. package/dist/src/ui/viewer/state.js +0 -55
  174. package/dist/src/ui/viewer/state.js.map +0 -1
  175. package/dist/src/ui/viewer/styles-inline.d.ts +0 -2
  176. package/dist/src/ui/viewer/styles-inline.d.ts.map +0 -1
  177. package/dist/src/ui/viewer/styles-inline.js +0 -1584
  178. package/dist/src/ui/viewer/styles-inline.js.map +0 -1
  179. package/dist/src/ui/viewer/text/index.d.ts +0 -7
  180. package/dist/src/ui/viewer/text/index.d.ts.map +0 -1
  181. package/dist/src/ui/viewer/text/index.js +0 -3
  182. package/dist/src/ui/viewer/text/index.js.map +0 -1
  183. package/dist/src/ui/viewer/text/render.d.ts +0 -19
  184. package/dist/src/ui/viewer/text/render.d.ts.map +0 -1
  185. package/dist/src/ui/viewer/text/render.js +0 -228
  186. package/dist/src/ui/viewer/text/render.js.map +0 -1
  187. package/dist/src/ui/viewer/text/selection.d.ts +0 -12
  188. package/dist/src/ui/viewer/text/selection.d.ts.map +0 -1
  189. package/dist/src/ui/viewer/text/selection.js +0 -70
  190. package/dist/src/ui/viewer/text/selection.js.map +0 -1
  191. package/dist/src/ui/viewer/text/types.d.ts +0 -37
  192. package/dist/src/ui/viewer/text/types.d.ts.map +0 -1
  193. package/dist/src/ui/viewer/text/types.js +0 -7
  194. package/dist/src/ui/viewer/text/types.js.map +0 -1
  195. package/dist/src/wasm/LICENSE +0 -104
  196. package/dist/src/wasm/udoc.d.ts +0 -480
  197. package/dist/src/wasm/udoc.js +0 -1738
  198. package/dist/src/wasm/udoc_bg.wasm +0 -0
  199. package/dist/src/wasm/udoc_bg.wasm.d.ts +0 -45
  200. package/dist/src/worker/WorkerClient.d.ts +0 -335
  201. package/dist/src/worker/WorkerClient.d.ts.map +0 -1
  202. package/dist/src/worker/WorkerClient.js +0 -903
  203. package/dist/src/worker/WorkerClient.js.map +0 -1
  204. package/dist/src/worker/index.d.ts +0 -4
  205. package/dist/src/worker/index.d.ts.map +0 -1
  206. package/dist/src/worker/index.js +0 -2
  207. package/dist/src/worker/index.js.map +0 -1
  208. package/dist/src/worker/worker.d.ts +0 -437
  209. package/dist/src/worker/worker.d.ts.map +0 -1
  210. package/dist/src/worker/worker.js +0 -237
  211. package/dist/src/worker/worker.js.map +0 -1
@@ -1,769 +0,0 @@
1
- /**
2
- * UDocViewer - Document viewer component.
3
- *
4
- * Binds to a loaded document and optionally provides UI.
5
- * Created via `client.createViewer()`.
6
- */
7
- import { mountViewerShell } from "./ui/viewer/shell.js";
8
- import { PerformanceCounter, NoOpPerformanceCounter, } from "./performance/index.js";
9
- /**
10
- * Document viewer component.
11
- *
12
- * Supports both UI mode (with container) and headless mode (without container).
13
- * Use `client.createViewer()` to create instances.
14
- */
15
- export class UDocViewer {
16
- workerClient;
17
- container = null;
18
- uiShell = null;
19
- documentId = null;
20
- _pageCount = 0;
21
- _pageInfo = [];
22
- destroyed = false;
23
- eventHandlers = new Map();
24
- _performanceCounter;
25
- googleFontsEnabled;
26
- /**
27
- * @internal
28
- * Use `client.createViewer()` instead.
29
- */
30
- constructor(workerClient, options = {}) {
31
- this.workerClient = workerClient;
32
- this.googleFontsEnabled = options.googleFonts ?? true;
33
- // Initialize performance counter
34
- if (options.enablePerformanceCounter) {
35
- const counter = new PerformanceCounter();
36
- this._performanceCounter = counter;
37
- if (options.onPerformanceLog) {
38
- counter.onLog(options.onPerformanceLog);
39
- }
40
- }
41
- else {
42
- this._performanceCounter = new NoOpPerformanceCounter();
43
- }
44
- if (options.container) {
45
- this.container = this.resolveContainer(options.container);
46
- const overrides = this.buildStateOverrides(options);
47
- this.uiShell = mountViewerShell(this.container, this.createEngineAdapter(), this.workerClient, overrides);
48
- // Set up password callback
49
- this.uiShell.setCallbacks({
50
- onPasswordSubmit: (password) => this.handlePasswordSubmit(password)
51
- });
52
- }
53
- }
54
- /**
55
- * Performance counter for tracking operation timings.
56
- * Only records data when `enablePerformanceCounter` is true.
57
- */
58
- get performanceCounter() {
59
- return this._performanceCounter;
60
- }
61
- buildStateOverrides(options) {
62
- const overrides = {};
63
- if (options.scrollMode !== undefined)
64
- overrides.scrollMode = options.scrollMode;
65
- if (options.layoutMode !== undefined)
66
- overrides.layoutMode = options.layoutMode;
67
- if (options.zoomMode !== undefined)
68
- overrides.zoomMode = options.zoomMode;
69
- if (options.zoom !== undefined)
70
- overrides.zoom = options.zoom;
71
- if (options.zoomSteps !== undefined)
72
- overrides.zoomSteps = options.zoomSteps;
73
- if (options.dpi !== undefined)
74
- overrides.dpi = options.dpi;
75
- if (options.pageSpacing !== undefined)
76
- overrides.pageSpacing = options.pageSpacing;
77
- if (options.spreadSpacing !== undefined)
78
- overrides.spreadSpacing = options.spreadSpacing;
79
- if (options.activePanel !== undefined)
80
- overrides.activePanel = options.activePanel;
81
- return overrides;
82
- }
83
- // ===========================================================================
84
- // Document Loading
85
- // ===========================================================================
86
- /**
87
- * Load a document.
88
- *
89
- * @param source - URL string, File object, or raw bytes
90
- */
91
- async load(source) {
92
- this.ensureNotDestroyed();
93
- // Reset performance counter and start timing
94
- this._performanceCounter.reset();
95
- this._performanceCounter.setLoadStartTime();
96
- // Close any existing document
97
- if (this.documentId) {
98
- this.close();
99
- }
100
- try {
101
- // Track download phase
102
- const downloadId = this._performanceCounter.markStart("download");
103
- const { bytes, filename } = await this.resolveSourceWithFilename(source);
104
- this._performanceCounter.markEnd(downloadId);
105
- // Detect format and load appropriately
106
- const format = detectDocumentFormat(bytes, filename);
107
- const loadId = this._performanceCounter.markStart(format === "image" ? "loadImage" : format === "pptx" ? "loadPptx" : "loadPdf");
108
- this.documentId =
109
- format === "image"
110
- ? await this.workerClient.loadImage(bytes)
111
- : format === "pptx"
112
- ? await this.workerClient.loadPptx(bytes)
113
- : await this.workerClient.loadPdf(bytes);
114
- this._performanceCounter.markEnd(loadId);
115
- // Enable Google Fonts if requested
116
- if (this.googleFontsEnabled) {
117
- await this.workerClient.enableGoogleFonts(this.documentId);
118
- }
119
- // Register performance counter with WorkerClient for this document
120
- // This enables tracking of all subsequent operations (getPageInfo, render, etc.)
121
- if (this._performanceCounter.enabled) {
122
- this.workerClient.setPerformanceCounter(this.documentId, this._performanceCounter);
123
- }
124
- // Check if document needs password
125
- const passwordRequired = await this.workerClient.needsPassword(this.documentId);
126
- if (passwordRequired) {
127
- // Document needs password - show dialog and wait for authentication
128
- if (this.uiShell) {
129
- this.uiShell.dispatch({
130
- type: "SET_DOC",
131
- doc: { id: this.documentId },
132
- pageCount: 0,
133
- pageInfos: []
134
- });
135
- this.uiShell.dispatch({ type: "SET_NEEDS_PASSWORD", needsPassword: true });
136
- }
137
- // Don't emit document:load yet - wait for successful authentication
138
- return;
139
- }
140
- // Load all page info upfront (fast operation)
141
- this._pageInfo = await this.workerClient.getAllPageInfo(this.documentId);
142
- this._pageCount = this._pageInfo.length;
143
- if (this.uiShell) {
144
- const initUiId = this._performanceCounter.markStart("initUiShell");
145
- this.uiShell.dispatch({
146
- type: "SET_DOC",
147
- doc: { id: this.documentId },
148
- pageCount: this._pageCount,
149
- pageInfos: this._pageInfo
150
- });
151
- this._performanceCounter.markEnd(initUiId);
152
- }
153
- this.emit("document:load", { pageCount: this._pageCount });
154
- }
155
- catch (error) {
156
- const phase = error instanceof TypeError ? "fetch" : "parse";
157
- this.emit("error", { error: error, phase });
158
- throw error;
159
- }
160
- }
161
- /**
162
- * Close the current document.
163
- * Viewer returns to empty state.
164
- */
165
- close() {
166
- if (this.documentId) {
167
- const docId = this.documentId;
168
- // Remove performance counter for this document
169
- this.workerClient.removePerformanceCounter(docId);
170
- // Clean up render cache first (cancel pending, clear cache)
171
- this.workerClient.cancelRenders(docId);
172
- this.workerClient.invalidateRenderCache(docId);
173
- // Unload from WASM worker
174
- this.workerClient.unloadPdf(docId).catch(() => {
175
- // Ignore errors during close
176
- });
177
- this.documentId = null;
178
- this._pageCount = 0;
179
- this._pageInfo = [];
180
- if (this.uiShell) {
181
- this.uiShell.dispatch({ type: "CLEAR_DOC" });
182
- }
183
- this.emit("document:close", {});
184
- }
185
- }
186
- /**
187
- * Whether a document is currently loaded.
188
- */
189
- get isLoaded() {
190
- return this.documentId !== null;
191
- }
192
- /**
193
- * Check if the loaded document requires a password to open.
194
- * @returns True if the document needs authentication before pages can be accessed.
195
- */
196
- async needsPassword() {
197
- this.ensureLoaded();
198
- return this.workerClient.needsPassword(this.documentId);
199
- }
200
- /**
201
- * Authenticate with a password to unlock an encrypted document.
202
- *
203
- * After successful authentication, page info is reloaded and the document
204
- * becomes fully accessible.
205
- *
206
- * @param password - The password to try
207
- * @returns True if authentication succeeded, false if the password was incorrect.
208
- */
209
- async authenticate(password) {
210
- this.ensureLoaded();
211
- // Dispatch authenticating state for UI
212
- if (this.uiShell) {
213
- this.uiShell.dispatch({ type: "AUTHENTICATE_START" });
214
- }
215
- try {
216
- const success = await this.workerClient.authenticate(this.documentId, password);
217
- if (success) {
218
- // Clear any cached renders from before authentication (they would be invalid)
219
- this.workerClient.cancelRenders(this.documentId);
220
- this.workerClient.invalidateRenderCache(this.documentId);
221
- // Enable Google Fonts if requested
222
- if (this.googleFontsEnabled) {
223
- await this.workerClient.enableGoogleFonts(this.documentId);
224
- }
225
- // Reload page info after successful authentication
226
- this._pageInfo = await this.workerClient.getAllPageInfo(this.documentId);
227
- this._pageCount = this._pageInfo.length;
228
- if (this.uiShell) {
229
- this.uiShell.dispatch({ type: "AUTHENTICATE_SUCCESS" });
230
- this.uiShell.dispatch({
231
- type: "SET_DOC",
232
- doc: { id: this.documentId },
233
- pageCount: this._pageCount,
234
- pageInfos: this._pageInfo
235
- });
236
- }
237
- this.emit("document:load", { pageCount: this._pageCount });
238
- }
239
- else {
240
- if (this.uiShell) {
241
- this.uiShell.dispatch({ type: "AUTHENTICATE_FAILURE", error: "Incorrect password" });
242
- }
243
- }
244
- return success;
245
- }
246
- catch (error) {
247
- if (this.uiShell) {
248
- this.uiShell.dispatch({
249
- type: "AUTHENTICATE_FAILURE",
250
- error: error instanceof Error ? error.message : "Authentication failed"
251
- });
252
- }
253
- throw error;
254
- }
255
- }
256
- /**
257
- * Handle password submission from the UI dialog.
258
- * @internal
259
- */
260
- async handlePasswordSubmit(password) {
261
- try {
262
- await this.authenticate(password);
263
- }
264
- catch {
265
- // Error is already dispatched to UI in authenticate()
266
- }
267
- }
268
- // ===========================================================================
269
- // Document Information
270
- // ===========================================================================
271
- /**
272
- * Total number of pages.
273
- * Returns 0 if no document is loaded.
274
- */
275
- get pageCount() {
276
- return this._pageCount;
277
- }
278
- /**
279
- * Document metadata (title, author, etc.).
280
- * Returns null if no document is loaded.
281
- */
282
- get metadata() {
283
- // TODO: Implement metadata retrieval from WASM
284
- if (!this.documentId)
285
- return null;
286
- return {};
287
- }
288
- /**
289
- * Get document outline (table of contents / bookmarks).
290
- */
291
- async getOutline() {
292
- this.ensureLoaded();
293
- const raw = await this.workerClient.getOutline(this.documentId);
294
- return raw;
295
- }
296
- /**
297
- * Get page dimensions in points (1 point = 1/72 inch).
298
- * @param page - Page index (0-based)
299
- */
300
- async getPageInfo(page) {
301
- this.ensureLoaded();
302
- if (page < 0 || page >= this._pageCount) {
303
- throw new Error(`Page index ${page} out of bounds (0-${this._pageCount - 1})`);
304
- }
305
- // Return cached info if available
306
- if (this._pageInfo[page]) {
307
- return this._pageInfo[page];
308
- }
309
- // Fetch from worker if not loaded yet
310
- const info = await this.workerClient.getPageInfo(this.documentId, page);
311
- this._pageInfo[page] = info;
312
- return info;
313
- }
314
- /**
315
- * Get annotations on a specific page.
316
- * @param page - Page index (0-based)
317
- */
318
- async getPageAnnotations(page) {
319
- this.ensureLoaded();
320
- const raw = await this.workerClient.getPageAnnotations(this.documentId, page);
321
- return raw;
322
- }
323
- // ===========================================================================
324
- // Navigation
325
- // ===========================================================================
326
- /**
327
- * Get the current page number (1-based).
328
- */
329
- get currentPage() {
330
- if (this.uiShell) {
331
- return this.uiShell.getState().page;
332
- }
333
- return 1;
334
- }
335
- /**
336
- * Navigate to a specific page.
337
- * @param page - Page number (1-based)
338
- */
339
- goToPage(page) {
340
- this.ensureNotDestroyed();
341
- if (!this.uiShell) {
342
- throw new Error("Navigation requires UI mode (container must be provided)");
343
- }
344
- this.uiShell.dispatch({ type: "NAVIGATE_TO_PAGE", page });
345
- }
346
- /**
347
- * Navigate to a destination (page + position + zoom).
348
- * @param destination - Full destination object with page index and display mode
349
- */
350
- goToDestination(destination) {
351
- this.ensureNotDestroyed();
352
- if (!this.uiShell) {
353
- throw new Error("Navigation requires UI mode (container must be provided)");
354
- }
355
- this.uiShell.dispatch({ type: "NAVIGATE_TO_DESTINATION", destination });
356
- }
357
- // ===========================================================================
358
- // Page Rendering
359
- // ===========================================================================
360
- /**
361
- * Render a page to an image.
362
- *
363
- * @param page - Page index (0-based)
364
- * @param options - Render options (scale, format, etc.)
365
- */
366
- async renderPage(page, options = {}) {
367
- return this.renderWithType(page, "page", options);
368
- }
369
- /**
370
- * Render a thumbnail of a page.
371
- *
372
- * Similar to renderPage but uses lower priority in the render queue,
373
- * making it suitable for generating thumbnails without blocking main page renders.
374
- *
375
- * @param page - Page index (0-based)
376
- * @param options - Render options (scale, format, etc.)
377
- */
378
- async renderThumbnail(page, options = {}) {
379
- return this.renderWithType(page, "thumbnail", options);
380
- }
381
- /**
382
- * Internal method to render a page with a specific render type.
383
- */
384
- async renderWithType(page, type, options = {}) {
385
- this.ensureLoaded();
386
- const scale = options.scale ?? 1;
387
- const format = options.format ?? "image-data";
388
- const force = options.force ?? false;
389
- const boost = options.boost ?? false;
390
- const renderRequest = {
391
- docId: this.documentId,
392
- page: page + 1, // Uses 1-based page numbers
393
- type,
394
- scale
395
- };
396
- let result;
397
- if (force) {
398
- // Bypass cache and queue, call worker directly
399
- result = await this.workerClient.forceRender(renderRequest);
400
- }
401
- else {
402
- // Boost priority if requested (before queueing the render)
403
- if (boost) {
404
- if (type === "page") {
405
- this.workerClient.boostPageRenderPriority(this.documentId, page + 1);
406
- }
407
- else {
408
- this.workerClient.boostThumbnailRenderPriority(this.documentId, page + 1);
409
- }
410
- }
411
- // Request render through WorkerClient
412
- // Performance tracking is handled by WorkerClient.doRender()
413
- result = await this.workerClient.requestRender(renderRequest);
414
- }
415
- // Convert ImageBitmap to the requested format
416
- return this.convertBitmapToFormat(result.bitmap, format, options);
417
- }
418
- /**
419
- * Convert an ImageBitmap to the requested output format.
420
- */
421
- async convertBitmapToFormat(bitmap, format, options) {
422
- if (format === "image-bitmap") {
423
- return bitmap;
424
- }
425
- const canvas = document.createElement("canvas");
426
- canvas.width = bitmap.width;
427
- canvas.height = bitmap.height;
428
- const ctx = canvas.getContext("2d");
429
- ctx.drawImage(bitmap, 0, 0);
430
- if (format === "image-data") {
431
- return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
432
- }
433
- const imageType = options.imageType ?? "image/png";
434
- const quality = options.quality ?? 0.92;
435
- if (format === "blob") {
436
- return new Promise((resolve, reject) => {
437
- canvas.toBlob((blob) => {
438
- if (blob)
439
- resolve(blob);
440
- else
441
- reject(new Error("Failed to create blob"));
442
- }, imageType, quality);
443
- });
444
- }
445
- // data-url
446
- return canvas.toDataURL(imageType, quality);
447
- }
448
- // ===========================================================================
449
- // Export
450
- // ===========================================================================
451
- /**
452
- * Export document as bytes.
453
- */
454
- async toBytes() {
455
- this.ensureLoaded();
456
- return this.workerClient.getBytes(this.documentId);
457
- }
458
- /**
459
- * Download document to user's device.
460
- * @param filename - Filename for download
461
- */
462
- async download(filename) {
463
- const bytes = await this.toBytes();
464
- const blob = new Blob([bytes.buffer], { type: "application/pdf" });
465
- const url = URL.createObjectURL(blob);
466
- const a = document.createElement("a");
467
- a.href = url;
468
- a.download = filename ?? "document.pdf";
469
- a.click();
470
- URL.revokeObjectURL(url);
471
- }
472
- // ===========================================================================
473
- // Events
474
- // ===========================================================================
475
- /**
476
- * Subscribe to an event.
477
- * @returns Unsubscribe function
478
- */
479
- on(event, handler) {
480
- if (!this.eventHandlers.has(event)) {
481
- this.eventHandlers.set(event, new Set());
482
- }
483
- this.eventHandlers.get(event).add(handler);
484
- return () => this.off(event, handler);
485
- }
486
- /**
487
- * Unsubscribe from an event.
488
- */
489
- off(event, handler) {
490
- this.eventHandlers.get(event)?.delete(handler);
491
- }
492
- // ===========================================================================
493
- // Lifecycle
494
- // ===========================================================================
495
- /**
496
- * Destroy the viewer and release resources.
497
- */
498
- destroy() {
499
- if (this.destroyed)
500
- return;
501
- this.destroyed = true;
502
- if (this.uiShell) {
503
- this.uiShell.destroy();
504
- this.uiShell = null;
505
- }
506
- this.close();
507
- this.eventHandlers.clear();
508
- }
509
- // ===========================================================================
510
- // Internal Helpers
511
- // ===========================================================================
512
- /**
513
- * Get the document ID (for internal use).
514
- * @internal
515
- */
516
- getDocumentId() {
517
- return this.documentId;
518
- }
519
- /**
520
- * Initialize the viewer with an already-loaded document ID.
521
- * Used by UDocClient.compose() to create viewers for composed documents.
522
- * @internal
523
- */
524
- async initializeFromDocId(docId) {
525
- this.ensureNotDestroyed();
526
- if (this.documentId) {
527
- this.close();
528
- }
529
- this.documentId = docId;
530
- // Load all page info upfront (fast operation)
531
- this._pageInfo = await this.workerClient.getAllPageInfo(docId);
532
- this._pageCount = this._pageInfo.length;
533
- if (this.uiShell) {
534
- this.uiShell.dispatch({
535
- type: "SET_DOC",
536
- doc: { id: docId },
537
- pageCount: this._pageCount,
538
- pageInfos: this._pageInfo
539
- });
540
- }
541
- this.emit("document:load", { pageCount: this._pageCount });
542
- }
543
- resolveContainer(container) {
544
- if (typeof container === "string") {
545
- const element = document.querySelector(container);
546
- if (!element) {
547
- throw new Error(`Container not found: ${container}`);
548
- }
549
- return element;
550
- }
551
- return container;
552
- }
553
- async resolveSourceWithFilename(source) {
554
- if (source instanceof Uint8Array) {
555
- return { bytes: source };
556
- }
557
- if (source instanceof File) {
558
- const buffer = await source.arrayBuffer();
559
- return { bytes: new Uint8Array(buffer), filename: source.name };
560
- }
561
- // URL string - use streaming to report progress
562
- const bytes = await this.fetchWithProgress(source);
563
- return { bytes, filename: source };
564
- }
565
- async fetchWithProgress(url) {
566
- // Show progress bar immediately before sending the request
567
- if (this.uiShell) {
568
- this.uiShell.dispatch({
569
- type: "SET_DOWNLOAD_PROGRESS",
570
- loaded: 0,
571
- total: 0,
572
- });
573
- }
574
- this.emit("download:progress", { loaded: 0, total: 0, percent: null });
575
- const response = await fetch(url);
576
- if (!response.ok) {
577
- throw new Error(`Failed to fetch document: ${response.statusText}`);
578
- }
579
- const contentLength = response.headers.get("Content-Length");
580
- const total = contentLength ? parseInt(contentLength, 10) : 0;
581
- // If no body or no streaming support, fall back to simple approach
582
- if (!response.body) {
583
- const buffer = await response.arrayBuffer();
584
- // Clear download progress from UI
585
- if (this.uiShell) {
586
- this.uiShell.dispatch({ type: "CLEAR_DOWNLOAD_PROGRESS" });
587
- }
588
- return new Uint8Array(buffer);
589
- }
590
- const reader = response.body.getReader();
591
- const chunks = [];
592
- let loaded = 0;
593
- // Helper to report progress to both event listeners and UI shell
594
- const reportProgress = (currentLoaded) => {
595
- const progress = {
596
- loaded: currentLoaded,
597
- total,
598
- percent: total > 0 ? Math.round((currentLoaded / total) * 100) : null,
599
- };
600
- this.emit("download:progress", progress);
601
- // Dispatch to UI shell for the loading overlay
602
- if (this.uiShell) {
603
- this.uiShell.dispatch({
604
- type: "SET_DOWNLOAD_PROGRESS",
605
- loaded: currentLoaded,
606
- total,
607
- });
608
- }
609
- };
610
- // Report initial progress with known total (transitions from indeterminate to 0%)
611
- reportProgress(0);
612
- while (true) {
613
- const { done, value } = await reader.read();
614
- if (done)
615
- break;
616
- chunks.push(value);
617
- loaded += value.length;
618
- reportProgress(loaded);
619
- }
620
- // Clear download progress from UI
621
- if (this.uiShell) {
622
- this.uiShell.dispatch({ type: "CLEAR_DOWNLOAD_PROGRESS" });
623
- }
624
- // Combine chunks into single array
625
- const result = new Uint8Array(loaded);
626
- let offset = 0;
627
- for (const chunk of chunks) {
628
- result.set(chunk, offset);
629
- offset += chunk.length;
630
- }
631
- return result;
632
- }
633
- emit(event, payload) {
634
- const handlers = this.eventHandlers.get(event);
635
- if (handlers) {
636
- for (const handler of handlers) {
637
- try {
638
- handler(payload);
639
- }
640
- catch (error) {
641
- console.error(`Error in event handler for ${event}:`, error);
642
- }
643
- }
644
- }
645
- }
646
- createEngineAdapter() {
647
- return {
648
- getPageInfo: async (_doc, page) => {
649
- const pageIndex = Math.max(0, page - 1);
650
- return this._pageInfo[pageIndex] ?? { width: 0, height: 0 };
651
- },
652
- getOutline: async (doc) => {
653
- const raw = await this.workerClient.getOutline(doc.id);
654
- return raw;
655
- },
656
- getPageAnnotations: async (doc, pageIndex) => {
657
- const raw = await this.workerClient.getPageAnnotations(doc.id, pageIndex);
658
- return raw;
659
- },
660
- getPageText: async (doc, pageIndex) => {
661
- const raw = await this.workerClient.getPageText(doc.id, pageIndex);
662
- return raw;
663
- }
664
- };
665
- }
666
- ensureNotDestroyed() {
667
- if (this.destroyed) {
668
- throw new Error("UDocViewer has been destroyed");
669
- }
670
- }
671
- ensureLoaded() {
672
- this.ensureNotDestroyed();
673
- if (!this.documentId) {
674
- throw new Error("No document loaded");
675
- }
676
- }
677
- }
678
- /**
679
- * Detect document format based on magic bytes and filename.
680
- * Returns "pdf" for PDF files, "pptx" for PowerPoint files, "image" for supported image formats.
681
- */
682
- function detectDocumentFormat(bytes, filename) {
683
- // Check magic bytes first (most reliable)
684
- if (bytes.length >= 4) {
685
- // PDF: %PDF
686
- if (bytes[0] === 0x25 && bytes[1] === 0x50 && bytes[2] === 0x44 && bytes[3] === 0x46) {
687
- return "pdf";
688
- }
689
- // ZIP signature: PK\x03\x04 (0x504B0304) - PPTX is a ZIP archive
690
- if (bytes[0] === 0x50 && bytes[1] === 0x4b && bytes[2] === 0x03 && bytes[3] === 0x04) {
691
- // Check filename extension to distinguish OOXML types
692
- if (filename) {
693
- const ext = filename.toLowerCase().split(".").pop();
694
- if (ext === "pptx") {
695
- return "pptx";
696
- }
697
- }
698
- // Default ZIP to PPTX for now (could add DOCX/XLSX later)
699
- return "pptx";
700
- }
701
- // JPEG: FF D8 FF
702
- if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {
703
- return "image";
704
- }
705
- // PNG: 89 50 4E 47
706
- if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47) {
707
- return "image";
708
- }
709
- // GIF: GIF8
710
- if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x38) {
711
- return "image";
712
- }
713
- // BMP: BM
714
- if (bytes[0] === 0x42 && bytes[1] === 0x4d) {
715
- return "image";
716
- }
717
- // TIFF: II (little-endian) or MM (big-endian) followed by 42
718
- if ((bytes[0] === 0x49 && bytes[1] === 0x49 && bytes[2] === 0x2a && bytes[3] === 0x00) ||
719
- (bytes[0] === 0x4d && bytes[1] === 0x4d && bytes[2] === 0x00 && bytes[3] === 0x2a)) {
720
- return "image";
721
- }
722
- // WebP: RIFF....WEBP
723
- if (bytes.length >= 12 &&
724
- bytes[0] === 0x52 &&
725
- bytes[1] === 0x49 &&
726
- bytes[2] === 0x46 &&
727
- bytes[3] === 0x46 &&
728
- bytes[8] === 0x57 &&
729
- bytes[9] === 0x45 &&
730
- bytes[10] === 0x42 &&
731
- bytes[11] === 0x50) {
732
- return "image";
733
- }
734
- }
735
- // Fallback to filename extension
736
- if (filename) {
737
- const ext = filename.toLowerCase().split(".").pop();
738
- // PPTX extension
739
- if (ext === "pptx") {
740
- return "pptx";
741
- }
742
- const imageExtensions = [
743
- "jpg",
744
- "jpeg",
745
- "png",
746
- "gif",
747
- "bmp",
748
- "tiff",
749
- "tif",
750
- "webp",
751
- "ico",
752
- "pnm",
753
- "pbm",
754
- "pgm",
755
- "ppm",
756
- "tga",
757
- "hdr",
758
- "exr",
759
- "qoi",
760
- "ff",
761
- ];
762
- if (ext && imageExtensions.includes(ext)) {
763
- return "image";
764
- }
765
- }
766
- // Default to PDF for unknown formats
767
- return "pdf";
768
- }
769
- //# sourceMappingURL=UDocViewer.js.map