@qrcommunication/gigapdf-lib 0.1.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,526 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/viewer.ts
21
+ var viewer_exports = {};
22
+ __export(viewer_exports, {
23
+ GigaPdfViewer: () => GigaPdfViewer
24
+ });
25
+ module.exports = __toCommonJS(viewer_exports);
26
+ var CSS = `
27
+ .gpv{position:relative;display:flex;flex-direction:column;height:100%;width:100%;background:var(--gpv-bg,#525659);color:#eee;font:13px/1.4 system-ui,sans-serif;overflow:hidden}
28
+ .gpv-bar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#323639;border-bottom:1px solid #000;flex:0 0 auto;user-select:none}
29
+ .gpv-bar button{background:#4a4f52;color:#eee;border:0;border-radius:4px;padding:4px 9px;cursor:pointer;font:inherit}
30
+ .gpv-bar button:hover{background:#5a6063}
31
+ .gpv-bar .gpv-sp{flex:1}
32
+ .gpv-bar input{width:46px;background:#222;color:#eee;border:1px solid #000;border-radius:4px;padding:3px;text-align:center;font:inherit}
33
+ .gpv-bar select{background:#222;color:#eee;border:1px solid #000;border-radius:4px;padding:3px;font:inherit;cursor:pointer}
34
+ .gpv-bar .gpv-zoom{min-width:44px;text-align:center;color:#ddd;font-variant-numeric:tabular-nums}
35
+ .gpv-body{flex:1;display:flex;min-height:0}
36
+ .gpv-thumbs{flex:0 0 132px;overflow-y:auto;background:#2a2d2f;padding:8px;display:flex;flex-direction:column;gap:8px}
37
+ .gpv-thumbs img{width:100%;display:block;border:2px solid transparent;border-radius:2px;cursor:pointer;background:#fff}
38
+ .gpv-thumbs .gpv-on img{border-color:#3b82f6}
39
+ .gpv-thumbs span{display:block;text-align:center;color:#aaa;font-size:11px}
40
+ .gpv-pages{flex:1;overflow:auto;padding:18px;display:flex;flex-direction:column;align-items:center;gap:18px;scroll-behavior:smooth}
41
+ .gpv-page{position:relative;box-shadow:0 1px 6px rgba(0,0,0,.6);background:#fff}
42
+ .gpv-page img{display:block;width:100%;height:auto}
43
+ .gpv:fullscreen{--gpv-bg:#000}
44
+ .gpv.gpv-present .gpv-thumbs,.gpv.gpv-present .gpv-bar{display:none}
45
+ .gpv.gpv-present .gpv-pages{padding:0;justify-content:center;overflow:hidden}
46
+ .gpv.gpv-present .gpv-page{box-shadow:none;max-height:100vh}
47
+ .gpv.gpv-present .gpv-page img{width:auto;max-width:100vw;max-height:100vh}
48
+ `;
49
+ var styleInjected = false;
50
+ function injectStyle(doc) {
51
+ if (styleInjected) return;
52
+ const el = doc.createElement("style");
53
+ el.textContent = CSS;
54
+ doc.head.appendChild(el);
55
+ styleInjected = true;
56
+ }
57
+ function pngSize(b) {
58
+ if (b.length < 24) return { w: 1, h: 1 };
59
+ const dv = new DataView(b.buffer, b.byteOffset, b.byteLength);
60
+ return { w: dv.getUint32(16) || 1, h: dv.getUint32(20) || 1 };
61
+ }
62
+ var GigaPdfViewer = class {
63
+ constructor(engine, container, options = {}) {
64
+ this.engine = engine;
65
+ this.options = options;
66
+ this.cssScale = options.scale ?? 1;
67
+ this.renderScale = options.renderScale ?? 2;
68
+ this.root = container;
69
+ injectStyle(container.ownerDocument);
70
+ this.buildChrome();
71
+ this.onKey = (e) => this.handleKey(e);
72
+ this.onFsChange = () => this.syncPresentClass();
73
+ container.ownerDocument.addEventListener("keydown", this.onKey);
74
+ container.ownerDocument.addEventListener("fullscreenchange", this.onFsChange);
75
+ }
76
+ engine;
77
+ options;
78
+ // `protected` members are the surface the editor subclass builds on.
79
+ doc = null;
80
+ count = 0;
81
+ current = 1;
82
+ cssScale;
83
+ renderScale;
84
+ /** Active auto-fit mode; re-applied on resize and page change. */
85
+ fitMode = "none";
86
+ presenting = false;
87
+ resizeObs = null;
88
+ // Per-page caches (1-indexed; slot 0 unused).
89
+ urls = [];
90
+ sizes = [];
91
+ // DOM nodes.
92
+ root;
93
+ bar = null;
94
+ body;
95
+ thumbs;
96
+ pages;
97
+ pageInput = null;
98
+ zoomReadout = null;
99
+ zoomSelect = null;
100
+ pageEls = [];
101
+ onKey;
102
+ onFsChange;
103
+ /** Number of pages currently open. */
104
+ get pageCount() {
105
+ return this.count;
106
+ }
107
+ /** The page currently in view (1-based). */
108
+ get currentPage() {
109
+ return this.current;
110
+ }
111
+ /** Orientation of `page` (after open). */
112
+ orientation(page) {
113
+ const s = this.sizes[page];
114
+ return s && s.w > s.h ? "landscape" : "portrait";
115
+ }
116
+ /** The current document as PDF bytes (including any applied edits). */
117
+ save() {
118
+ if (!this.doc) return new Uint8Array(0);
119
+ return this.doc.save();
120
+ }
121
+ /** Open a document; Office/HTML are converted to PDF in-engine. Returns the page count. */
122
+ async open(src) {
123
+ const pdf = this.toPdf(src);
124
+ this.close();
125
+ this.doc = this.engine.open(pdf);
126
+ this.count = this.doc.pageCount();
127
+ this.urls = new Array(this.count + 1).fill(null);
128
+ this.sizes = new Array(this.count + 1).fill(null);
129
+ this.pageEls = new Array(this.count + 1).fill(null);
130
+ this.current = 1;
131
+ this.renderAllPages();
132
+ if (this.options.thumbnails !== false) this.buildThumbs();
133
+ this.goTo(1);
134
+ return this.count;
135
+ }
136
+ toPdf(src) {
137
+ switch (src.kind) {
138
+ case "pdf":
139
+ return src.bytes;
140
+ case "office":
141
+ return this.engine.officeToPdf(src.bytes);
142
+ case "html":
143
+ return src.fonts?.length ? this.engine.htmlRender(src.html, src.fonts) : this.engine.htmlToPdf(src.html);
144
+ case "auto":
145
+ return this.detectAndConvert(src.bytes);
146
+ }
147
+ }
148
+ detectAndConvert(b) {
149
+ const is = (...sig) => sig.every((v, i) => b[i] === v);
150
+ if (is(37, 80, 68, 70)) return b;
151
+ if (is(80, 75)) return this.engine.officeToPdf(b);
152
+ if (is(208, 207, 17, 224)) return this.engine.officeToPdf(b);
153
+ return this.engine.htmlToPdf(new TextDecoder().decode(b));
154
+ }
155
+ // ── rendering ────────────────────────────────────────────────────────────────
156
+ renderPageRaster(page) {
157
+ const cachedUrl = this.urls[page];
158
+ const cachedSize = this.sizes[page];
159
+ if (cachedUrl && cachedSize) return { url: cachedUrl, ...cachedSize };
160
+ const png = this.doc.renderPage(page, this.renderScale);
161
+ const size = pngSize(png);
162
+ const buf = new ArrayBuffer(png.byteLength);
163
+ new Uint8Array(buf).set(png);
164
+ const url = URL.createObjectURL(new Blob([buf], { type: "image/png" }));
165
+ this.urls[page] = url;
166
+ this.sizes[page] = size;
167
+ return { url, ...size };
168
+ }
169
+ /** The page's width / height in PDF points (raster pixels ÷ render scale). */
170
+ pageWidthPt(page) {
171
+ const s = this.sizes[page];
172
+ return s ? s.w / this.renderScale : 0;
173
+ }
174
+ pageHeightPt(page) {
175
+ const s = this.sizes[page];
176
+ return s ? s.h / this.renderScale : 0;
177
+ }
178
+ /** Re-raster a page after its content changed (drops the cached image). */
179
+ reRenderPage(page) {
180
+ const old = this.urls[page];
181
+ if (old) URL.revokeObjectURL(old);
182
+ this.urls[page] = null;
183
+ this.sizes[page] = null;
184
+ const { url, w } = this.renderPageRaster(page);
185
+ const box = this.pageEls[page];
186
+ if (!box) return;
187
+ box.style.width = `${w / this.renderScale * this.cssScale}px`;
188
+ const img = box.querySelector("img");
189
+ if (img) img.src = url;
190
+ }
191
+ /** Hook for subclasses (the editor) to attach per-page overlays. */
192
+ afterRender() {
193
+ }
194
+ renderAllPages() {
195
+ this.pages.replaceChildren();
196
+ for (let p = 1; p <= this.count; p++) {
197
+ const { url, w, h } = this.renderPageRaster(p);
198
+ const box = this.root.ownerDocument.createElement("div");
199
+ box.className = "gpv-page";
200
+ box.dataset.page = String(p);
201
+ box.style.width = `${w / this.renderScale * this.cssScale}px`;
202
+ const img = this.root.ownerDocument.createElement("img");
203
+ img.src = url;
204
+ img.alt = `Page ${p}`;
205
+ img.loading = "lazy";
206
+ box.appendChild(img);
207
+ this.pages.appendChild(box);
208
+ this.pageEls[p] = box;
209
+ }
210
+ this.afterRender();
211
+ }
212
+ buildThumbs() {
213
+ this.thumbs.replaceChildren();
214
+ for (let p = 1; p <= this.count; p++) {
215
+ const url = this.urls[p];
216
+ const item = this.root.ownerDocument.createElement("div");
217
+ item.dataset.page = String(p);
218
+ const img = this.root.ownerDocument.createElement("img");
219
+ if (url) img.src = url;
220
+ img.addEventListener("click", () => this.goTo(p));
221
+ const label = this.root.ownerDocument.createElement("span");
222
+ label.textContent = String(p);
223
+ item.append(img, label);
224
+ this.thumbs.appendChild(item);
225
+ }
226
+ this.highlightThumb();
227
+ }
228
+ highlightThumb() {
229
+ for (const child of Array.from(this.thumbs.children)) {
230
+ child.classList.toggle("gpv-on", child.getAttribute("data-page") === String(this.current));
231
+ }
232
+ }
233
+ // ── navigation ───────────────────────────────────────────────────────────────
234
+ /** Scroll/jump to `page` (clamped to range). */
235
+ goTo(page) {
236
+ if (this.count === 0) return;
237
+ this.current = Math.min(Math.max(1, Math.round(page)), this.count);
238
+ if (this.presenting) {
239
+ for (let p = 1; p <= this.count; p++) {
240
+ const el = this.pageEls[p];
241
+ if (el) el.style.display = p === this.current ? "" : "none";
242
+ }
243
+ } else {
244
+ this.pageEls[this.current]?.scrollIntoView({ block: "start" });
245
+ }
246
+ if (this.pageInput) this.pageInput.value = String(this.current);
247
+ if (this.fitMode !== "none") this.applyFitMode();
248
+ this.highlightThumb();
249
+ }
250
+ next() {
251
+ this.goTo(this.current + 1);
252
+ }
253
+ prev() {
254
+ this.goTo(this.current - 1);
255
+ }
256
+ // ── zoom ─────────────────────────────────────────────────────────────────────
257
+ /** Current CSS zoom multiplier (1 = 100%). */
258
+ get zoom() {
259
+ return this.cssScale;
260
+ }
261
+ /** Resize the page boxes to the current `cssScale` (no re-raster). */
262
+ applyScale(scale) {
263
+ this.cssScale = Math.min(Math.max(0.08, scale), 8);
264
+ for (let p = 1; p <= this.count; p++) {
265
+ const box = this.pageEls[p];
266
+ const size = this.sizes[p];
267
+ if (box && size) box.style.width = `${size.w / this.renderScale * this.cssScale}px`;
268
+ }
269
+ this.updateZoomReadout();
270
+ this.onZoomChange();
271
+ }
272
+ /** Set an explicit zoom multiplier (cancels any auto-fit mode). */
273
+ setZoom(scale) {
274
+ this.fitMode = "none";
275
+ this.applyScale(scale);
276
+ }
277
+ /** Set zoom as a percentage (e.g. `125`). */
278
+ setZoomPercent(pct) {
279
+ this.setZoom(pct / 100);
280
+ }
281
+ /** Reset to 100 % (actual size). */
282
+ actualSize() {
283
+ this.setZoom(1);
284
+ }
285
+ zoomIn() {
286
+ this.setZoom(this.cssScale * 1.2);
287
+ }
288
+ zoomOut() {
289
+ this.setZoom(this.cssScale / 1.2);
290
+ }
291
+ /** Fit the current page's **width** to the viewport (sticks across resizes). */
292
+ fitWidth() {
293
+ this.fitMode = "width";
294
+ this.applyFitMode();
295
+ }
296
+ /** Fit the **whole** current page (width *and* height) to the viewport. */
297
+ fitPage() {
298
+ this.fitMode = "page";
299
+ this.applyFitMode();
300
+ }
301
+ /** Recompute the zoom for the active fit mode against the current page. */
302
+ applyFitMode() {
303
+ if (this.fitMode === "none" || this.presenting || this.count === 0) return;
304
+ const size = this.sizes[this.current];
305
+ if (!size) return;
306
+ const wPt = size.w / this.renderScale;
307
+ const hPt = size.h / this.renderScale;
308
+ if (this.fitMode === "width") {
309
+ this.applyScale((this.pages.clientWidth - 36) / wPt || 1);
310
+ } else {
311
+ const availW = this.pages.clientWidth - 36;
312
+ const availH = this.pages.clientHeight - 36;
313
+ this.applyScale(Math.min(availW / wPt, availH / hPt) || 1);
314
+ }
315
+ }
316
+ /** Hook for subclasses (the editor) to react to zoom changes. */
317
+ onZoomChange() {
318
+ }
319
+ updateZoomReadout() {
320
+ if (this.zoomReadout) this.zoomReadout.textContent = `${Math.round(this.cssScale * 100)}%`;
321
+ if (this.zoomSelect) {
322
+ const v = this.fitMode === "width" ? "width" : this.fitMode === "page" ? "page" : String(this.cssScale);
323
+ const has = Array.from(this.zoomSelect.options).some((o) => o.value === v);
324
+ this.zoomSelect.value = has ? v : "";
325
+ }
326
+ }
327
+ // ── presentation (fullscreen slideshow) ───────────────────────────────────────
328
+ /** Enter fullscreen single-page presentation mode. */
329
+ present() {
330
+ this.presenting = true;
331
+ this.root.classList.add("gpv-present");
332
+ this.goTo(this.current);
333
+ void this.root.requestFullscreen?.();
334
+ }
335
+ /** Leave presentation mode. */
336
+ exitPresent() {
337
+ this.presenting = false;
338
+ this.root.classList.remove("gpv-present");
339
+ if (this.root.ownerDocument.fullscreenElement) void this.root.ownerDocument.exitFullscreen?.();
340
+ for (let p = 1; p <= this.count; p++) {
341
+ const el = this.pageEls[p];
342
+ if (el) el.style.display = "";
343
+ }
344
+ this.goTo(this.current);
345
+ }
346
+ syncPresentClass() {
347
+ if (this.presenting && !this.root.ownerDocument.fullscreenElement) this.exitPresent();
348
+ }
349
+ // ── keyboard ─────────────────────────────────────────────────────────────────
350
+ handleKey(e) {
351
+ if (!this.isActive()) return;
352
+ switch (e.key) {
353
+ case "ArrowRight":
354
+ case "PageDown":
355
+ case " ":
356
+ this.next();
357
+ break;
358
+ case "ArrowLeft":
359
+ case "PageUp":
360
+ this.prev();
361
+ break;
362
+ case "Home":
363
+ this.goTo(1);
364
+ break;
365
+ case "End":
366
+ this.goTo(this.count);
367
+ break;
368
+ case "+":
369
+ case "=":
370
+ this.zoomIn();
371
+ break;
372
+ case "-":
373
+ this.zoomOut();
374
+ break;
375
+ case "0":
376
+ this.actualSize();
377
+ break;
378
+ case "f":
379
+ case "F":
380
+ this.presenting ? this.exitPresent() : this.present();
381
+ break;
382
+ case "Escape":
383
+ if (this.presenting) this.exitPresent();
384
+ break;
385
+ default:
386
+ return;
387
+ }
388
+ e.preventDefault();
389
+ }
390
+ isActive() {
391
+ return this.presenting || this.root.contains(this.root.ownerDocument.activeElement) || this.root.matches(":hover");
392
+ }
393
+ // ── chrome ───────────────────────────────────────────────────────────────────
394
+ buildChrome() {
395
+ const d = this.root.ownerDocument;
396
+ this.root.classList.add("gpv");
397
+ if (this.options.background) this.root.style.setProperty("--gpv-bg", this.options.background);
398
+ this.root.replaceChildren();
399
+ if (this.options.toolbar !== false) {
400
+ const bar = d.createElement("div");
401
+ bar.className = "gpv-bar";
402
+ const btn = (label, fn, title) => {
403
+ const b = d.createElement("button");
404
+ b.textContent = label;
405
+ if (title) b.title = title;
406
+ b.addEventListener("click", fn);
407
+ bar.appendChild(b);
408
+ return b;
409
+ };
410
+ btn("\u2039", () => this.prev(), "Previous page");
411
+ btn("\u203A", () => this.next(), "Next page");
412
+ const input = d.createElement("input");
413
+ input.value = "1";
414
+ input.addEventListener("change", () => this.goTo(Number(input.value) || 1));
415
+ bar.appendChild(input);
416
+ this.pageInput = input;
417
+ btn("\u2212", () => this.zoomOut(), "Zoom out");
418
+ const zr = d.createElement("span");
419
+ zr.className = "gpv-zoom";
420
+ zr.textContent = "100%";
421
+ bar.appendChild(zr);
422
+ this.zoomReadout = zr;
423
+ btn("+", () => this.zoomIn(), "Zoom in");
424
+ const zsel = d.createElement("select");
425
+ zsel.title = "Zoom level";
426
+ const zopts = [
427
+ ["width", "Fit width"],
428
+ ["page", "Fit page"],
429
+ ["0.5", "50%"],
430
+ ["0.75", "75%"],
431
+ ["1", "100%"],
432
+ ["1.25", "125%"],
433
+ ["1.5", "150%"],
434
+ ["2", "200%"],
435
+ ["4", "400%"]
436
+ ];
437
+ for (const [val, label] of zopts) {
438
+ const o = d.createElement("option");
439
+ o.value = val;
440
+ o.textContent = label;
441
+ zsel.appendChild(o);
442
+ }
443
+ zsel.value = "1";
444
+ zsel.addEventListener("change", () => {
445
+ const v = zsel.value;
446
+ if (v === "width") this.fitWidth();
447
+ else if (v === "page") this.fitPage();
448
+ else if (v) this.setZoom(Number(v));
449
+ });
450
+ bar.appendChild(zsel);
451
+ this.zoomSelect = zsel;
452
+ const sp = d.createElement("div");
453
+ sp.className = "gpv-sp";
454
+ bar.appendChild(sp);
455
+ btn("\u26F6 Present", () => this.present(), "Fullscreen presentation (F)");
456
+ this.root.appendChild(bar);
457
+ this.bar = bar;
458
+ }
459
+ this.body = d.createElement("div");
460
+ this.body.className = "gpv-body";
461
+ this.thumbs = d.createElement("div");
462
+ this.thumbs.className = "gpv-thumbs";
463
+ if (this.options.thumbnails === false) this.thumbs.style.display = "none";
464
+ this.pages = d.createElement("div");
465
+ this.pages.className = "gpv-pages";
466
+ this.pages.addEventListener("scroll", () => this.trackScroll());
467
+ this.body.append(this.thumbs, this.pages);
468
+ this.root.appendChild(this.body);
469
+ if (typeof ResizeObserver !== "undefined") {
470
+ this.resizeObs = new ResizeObserver(() => this.applyFitMode());
471
+ this.resizeObs.observe(this.pages);
472
+ }
473
+ this.pages.addEventListener(
474
+ "wheel",
475
+ (e) => {
476
+ if (!e.ctrlKey && !e.metaKey) return;
477
+ e.preventDefault();
478
+ this.setZoom(this.cssScale * (e.deltaY < 0 ? 1.1 : 1 / 1.1));
479
+ },
480
+ { passive: false }
481
+ );
482
+ }
483
+ trackScroll() {
484
+ if (this.presenting || this.count === 0) return;
485
+ const mid = this.pages.scrollTop + this.pages.clientHeight / 2;
486
+ let best = this.current;
487
+ for (let p = 1; p <= this.count; p++) {
488
+ const el = this.pageEls[p];
489
+ if (el && el.offsetTop <= mid) best = p;
490
+ }
491
+ if (best !== this.current) {
492
+ this.current = best;
493
+ if (this.pageInput) this.pageInput.value = String(best);
494
+ if (this.fitMode !== "none") this.applyFitMode();
495
+ this.highlightThumb();
496
+ }
497
+ }
498
+ // ── lifecycle ────────────────────────────────────────────────────────────────
499
+ /** Close the current document and free its rendered pages. */
500
+ close() {
501
+ for (const u of this.urls) if (u) URL.revokeObjectURL(u);
502
+ this.urls = [];
503
+ this.sizes = [];
504
+ this.pageEls = [];
505
+ this.doc?.close();
506
+ this.doc = null;
507
+ this.count = 0;
508
+ this.pages?.replaceChildren();
509
+ this.thumbs?.replaceChildren();
510
+ }
511
+ /** Destroy the viewer: close the document and detach listeners. */
512
+ destroy() {
513
+ this.close();
514
+ this.resizeObs?.disconnect();
515
+ this.resizeObs = null;
516
+ this.root.ownerDocument.removeEventListener("keydown", this.onKey);
517
+ this.root.ownerDocument.removeEventListener("fullscreenchange", this.onFsChange);
518
+ this.root.classList.remove("gpv", "gpv-present");
519
+ this.root.replaceChildren();
520
+ }
521
+ };
522
+ // Annotate the CommonJS export names for ESM import in node:
523
+ 0 && (module.exports = {
524
+ GigaPdfViewer
525
+ });
526
+ //# sourceMappingURL=viewer.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/viewer.ts"],"sourcesContent":["/**\n * `@qrcommunication/gigapdf-lib/viewer` — a zero-dependency document **viewer**\n * built entirely on the WASM engine. It opens PDF, Office (docx/xlsx/pptx +\n * legacy/ODF) and HTML — converting non-PDF inputs to PDF in-engine — renders\n * pages with {@link GigaPdfDoc.renderPage}, detects each page's orientation and\n * adapts, and offers navigation, zoom, a thumbnail rail and a **fullscreen\n * presentation mode**. No third-party libraries (no pdf.js); browser-only (DOM).\n *\n * ```ts\n * const giga = await GigaPdfEngine.load(wasmUrl);\n * const viewer = new GigaPdfViewer(giga, document.getElementById(\"app\")!);\n * await viewer.open({ kind: \"auto\", bytes }); // pdf / office / html auto-detected\n * viewer.present(); // fullscreen slideshow\n * ```\n */\nimport { GigaPdfEngine, GigaPdfDoc, type HtmlFont } from \"./index\";\n\n/** A document to open. `auto` sniffs the format from magic bytes. */\nexport type ViewerSource =\n | { kind: \"pdf\"; bytes: Uint8Array }\n | { kind: \"office\"; bytes: Uint8Array }\n | { kind: \"html\"; html: string; fonts?: HtmlFont[] }\n | { kind: \"auto\"; bytes: Uint8Array };\n\nexport interface ViewerOptions {\n /** CSS zoom multiplier applied to the page boxes (default 1). */\n scale?: number;\n /** Raster scale for crisp rendering (default 2 ≈ retina). */\n renderScale?: number;\n /** Show the thumbnail rail (default true). */\n thumbnails?: boolean;\n /** Show the toolbar (default true). */\n toolbar?: boolean;\n /** Gutter background colour (default `#525659`). */\n background?: string;\n}\n\n/** Page orientation derived from the rendered raster. */\nexport type Orientation = \"portrait\" | \"landscape\";\n\nconst CSS = `\n.gpv{position:relative;display:flex;flex-direction:column;height:100%;width:100%;background:var(--gpv-bg,#525659);color:#eee;font:13px/1.4 system-ui,sans-serif;overflow:hidden}\n.gpv-bar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:#323639;border-bottom:1px solid #000;flex:0 0 auto;user-select:none}\n.gpv-bar button{background:#4a4f52;color:#eee;border:0;border-radius:4px;padding:4px 9px;cursor:pointer;font:inherit}\n.gpv-bar button:hover{background:#5a6063}\n.gpv-bar .gpv-sp{flex:1}\n.gpv-bar input{width:46px;background:#222;color:#eee;border:1px solid #000;border-radius:4px;padding:3px;text-align:center;font:inherit}\n.gpv-bar select{background:#222;color:#eee;border:1px solid #000;border-radius:4px;padding:3px;font:inherit;cursor:pointer}\n.gpv-bar .gpv-zoom{min-width:44px;text-align:center;color:#ddd;font-variant-numeric:tabular-nums}\n.gpv-body{flex:1;display:flex;min-height:0}\n.gpv-thumbs{flex:0 0 132px;overflow-y:auto;background:#2a2d2f;padding:8px;display:flex;flex-direction:column;gap:8px}\n.gpv-thumbs img{width:100%;display:block;border:2px solid transparent;border-radius:2px;cursor:pointer;background:#fff}\n.gpv-thumbs .gpv-on img{border-color:#3b82f6}\n.gpv-thumbs span{display:block;text-align:center;color:#aaa;font-size:11px}\n.gpv-pages{flex:1;overflow:auto;padding:18px;display:flex;flex-direction:column;align-items:center;gap:18px;scroll-behavior:smooth}\n.gpv-page{position:relative;box-shadow:0 1px 6px rgba(0,0,0,.6);background:#fff}\n.gpv-page img{display:block;width:100%;height:auto}\n.gpv:fullscreen{--gpv-bg:#000}\n.gpv.gpv-present .gpv-thumbs,.gpv.gpv-present .gpv-bar{display:none}\n.gpv.gpv-present .gpv-pages{padding:0;justify-content:center;overflow:hidden}\n.gpv.gpv-present .gpv-page{box-shadow:none;max-height:100vh}\n.gpv.gpv-present .gpv-page img{width:auto;max-width:100vw;max-height:100vh}\n`;\n\nlet styleInjected = false;\nfunction injectStyle(doc: Document) {\n if (styleInjected) return;\n const el = doc.createElement(\"style\");\n el.textContent = CSS;\n doc.head.appendChild(el);\n styleInjected = true;\n}\n\n/** Read a PNG's pixel dimensions from its IHDR (bytes 16–23, big-endian). */\nfunction pngSize(b: Uint8Array): { w: number; h: number } {\n if (b.length < 24) return { w: 1, h: 1 };\n const dv = new DataView(b.buffer, b.byteOffset, b.byteLength);\n return { w: dv.getUint32(16) || 1, h: dv.getUint32(20) || 1 };\n}\n\nexport class GigaPdfViewer {\n // `protected` members are the surface the editor subclass builds on.\n protected doc: GigaPdfDoc | null = null;\n protected count = 0;\n protected current = 1;\n protected cssScale: number;\n protected renderScale: number;\n /** Active auto-fit mode; re-applied on resize and page change. */\n protected fitMode: \"none\" | \"width\" | \"page\" = \"none\";\n private presenting = false;\n private resizeObs: ResizeObserver | null = null;\n\n // Per-page caches (1-indexed; slot 0 unused).\n protected urls: (string | null)[] = [];\n protected sizes: ({ w: number; h: number } | null)[] = [];\n\n // DOM nodes.\n protected root: HTMLElement;\n private bar: HTMLElement | null = null;\n private body!: HTMLElement;\n private thumbs!: HTMLElement;\n protected pages!: HTMLElement;\n private pageInput: HTMLInputElement | null = null;\n private zoomReadout: HTMLElement | null = null;\n private zoomSelect: HTMLSelectElement | null = null;\n protected pageEls: (HTMLElement | null)[] = [];\n private onKey: (e: KeyboardEvent) => void;\n private onFsChange: () => void;\n\n constructor(\n protected engine: GigaPdfEngine,\n container: HTMLElement,\n private options: ViewerOptions = {}\n ) {\n this.cssScale = options.scale ?? 1;\n this.renderScale = options.renderScale ?? 2;\n this.root = container;\n injectStyle(container.ownerDocument);\n this.buildChrome();\n this.onKey = (e) => this.handleKey(e);\n this.onFsChange = () => this.syncPresentClass();\n container.ownerDocument.addEventListener(\"keydown\", this.onKey);\n container.ownerDocument.addEventListener(\"fullscreenchange\", this.onFsChange);\n }\n\n /** Number of pages currently open. */\n get pageCount(): number {\n return this.count;\n }\n /** The page currently in view (1-based). */\n get currentPage(): number {\n return this.current;\n }\n /** Orientation of `page` (after open). */\n orientation(page: number): Orientation {\n const s = this.sizes[page];\n return s && s.w > s.h ? \"landscape\" : \"portrait\";\n }\n\n /** The current document as PDF bytes (including any applied edits). */\n save(): Uint8Array {\n if (!this.doc) return new Uint8Array(0);\n return this.doc.save();\n }\n\n /** Open a document; Office/HTML are converted to PDF in-engine. Returns the page count. */\n async open(src: ViewerSource): Promise<number> {\n const pdf = this.toPdf(src);\n this.close();\n this.doc = this.engine.open(pdf);\n this.count = this.doc.pageCount();\n this.urls = new Array(this.count + 1).fill(null);\n this.sizes = new Array(this.count + 1).fill(null);\n this.pageEls = new Array(this.count + 1).fill(null);\n this.current = 1;\n this.renderAllPages();\n if (this.options.thumbnails !== false) this.buildThumbs();\n this.goTo(1);\n return this.count;\n }\n\n private toPdf(src: ViewerSource): Uint8Array {\n switch (src.kind) {\n case \"pdf\":\n return src.bytes;\n case \"office\":\n return this.engine.officeToPdf(src.bytes);\n case \"html\":\n return src.fonts?.length\n ? this.engine.htmlRender(src.html, src.fonts)\n : this.engine.htmlToPdf(src.html);\n case \"auto\":\n return this.detectAndConvert(src.bytes);\n }\n }\n\n private detectAndConvert(b: Uint8Array): Uint8Array {\n const is = (...sig: number[]) => sig.every((v, i) => b[i] === v);\n if (is(0x25, 0x50, 0x44, 0x46)) return b; // %PDF\n if (is(0x50, 0x4b)) return this.engine.officeToPdf(b); // PK zip (docx/xlsx/pptx/odf)\n if (is(0xd0, 0xcf, 0x11, 0xe0)) return this.engine.officeToPdf(b); // OLE2 (legacy doc/xls/ppt)\n return this.engine.htmlToPdf(new TextDecoder().decode(b)); // else: HTML/text\n }\n\n // ── rendering ────────────────────────────────────────────────────────────────\n protected renderPageRaster(page: number): { url: string; w: number; h: number } {\n const cachedUrl = this.urls[page];\n const cachedSize = this.sizes[page];\n if (cachedUrl && cachedSize) return { url: cachedUrl, ...cachedSize };\n const png = this.doc!.renderPage(page, this.renderScale);\n const size = pngSize(png);\n // Copy into a fresh ArrayBuffer (the wasm-backed view may sit on a\n // SharedArrayBuffer, which `Blob` rejects).\n const buf = new ArrayBuffer(png.byteLength);\n new Uint8Array(buf).set(png);\n const url = URL.createObjectURL(new Blob([buf], { type: \"image/png\" }));\n this.urls[page] = url;\n this.sizes[page] = size;\n return { url, ...size };\n }\n\n /** The page's width / height in PDF points (raster pixels ÷ render scale). */\n protected pageWidthPt(page: number): number {\n const s = this.sizes[page];\n return s ? s.w / this.renderScale : 0;\n }\n protected pageHeightPt(page: number): number {\n const s = this.sizes[page];\n return s ? s.h / this.renderScale : 0;\n }\n\n /** Re-raster a page after its content changed (drops the cached image). */\n protected reRenderPage(page: number) {\n const old = this.urls[page];\n if (old) URL.revokeObjectURL(old);\n this.urls[page] = null;\n this.sizes[page] = null;\n const { url, w } = this.renderPageRaster(page);\n const box = this.pageEls[page];\n if (!box) return;\n box.style.width = `${(w / this.renderScale) * this.cssScale}px`;\n const img = box.querySelector(\"img\");\n if (img) img.src = url;\n }\n\n /** Hook for subclasses (the editor) to attach per-page overlays. */\n protected afterRender() {}\n\n private renderAllPages() {\n this.pages.replaceChildren();\n for (let p = 1; p <= this.count; p++) {\n const { url, w, h } = this.renderPageRaster(p);\n const box = this.root.ownerDocument.createElement(\"div\");\n box.className = \"gpv-page\";\n box.dataset.page = String(p);\n // Size the box to the page's true aspect ratio (so landscape pages are\n // wide and portrait pages tall) at the current CSS zoom.\n box.style.width = `${(w / this.renderScale) * this.cssScale}px`;\n const img = this.root.ownerDocument.createElement(\"img\");\n img.src = url;\n img.alt = `Page ${p}`;\n img.loading = \"lazy\";\n box.appendChild(img);\n this.pages.appendChild(box);\n this.pageEls[p] = box;\n }\n this.afterRender();\n }\n\n private buildThumbs() {\n this.thumbs.replaceChildren();\n for (let p = 1; p <= this.count; p++) {\n const url = this.urls[p];\n const item = this.root.ownerDocument.createElement(\"div\");\n item.dataset.page = String(p);\n const img = this.root.ownerDocument.createElement(\"img\");\n if (url) img.src = url;\n img.addEventListener(\"click\", () => this.goTo(p));\n const label = this.root.ownerDocument.createElement(\"span\");\n label.textContent = String(p);\n item.append(img, label);\n this.thumbs.appendChild(item);\n }\n this.highlightThumb();\n }\n\n private highlightThumb() {\n for (const child of Array.from(this.thumbs.children)) {\n child.classList.toggle(\"gpv-on\", child.getAttribute(\"data-page\") === String(this.current));\n }\n }\n\n // ── navigation ───────────────────────────────────────────────────────────────\n /** Scroll/jump to `page` (clamped to range). */\n goTo(page: number) {\n if (this.count === 0) return;\n this.current = Math.min(Math.max(1, Math.round(page)), this.count);\n if (this.presenting) {\n // Single-page: show only the current page.\n for (let p = 1; p <= this.count; p++) {\n const el = this.pageEls[p];\n if (el) el.style.display = p === this.current ? \"\" : \"none\";\n }\n } else {\n this.pageEls[this.current]?.scrollIntoView({ block: \"start\" });\n }\n if (this.pageInput) this.pageInput.value = String(this.current);\n if (this.fitMode !== \"none\") this.applyFitMode();\n this.highlightThumb();\n }\n next() {\n this.goTo(this.current + 1);\n }\n prev() {\n this.goTo(this.current - 1);\n }\n\n // ── zoom ─────────────────────────────────────────────────────────────────────\n /** Current CSS zoom multiplier (1 = 100%). */\n get zoom(): number {\n return this.cssScale;\n }\n\n /** Resize the page boxes to the current `cssScale` (no re-raster). */\n private applyScale(scale: number) {\n this.cssScale = Math.min(Math.max(0.08, scale), 8);\n for (let p = 1; p <= this.count; p++) {\n const box = this.pageEls[p];\n const size = this.sizes[p];\n if (box && size) box.style.width = `${(size.w / this.renderScale) * this.cssScale}px`;\n }\n this.updateZoomReadout();\n this.onZoomChange();\n }\n\n /** Set an explicit zoom multiplier (cancels any auto-fit mode). */\n setZoom(scale: number) {\n this.fitMode = \"none\";\n this.applyScale(scale);\n }\n /** Set zoom as a percentage (e.g. `125`). */\n setZoomPercent(pct: number) {\n this.setZoom(pct / 100);\n }\n /** Reset to 100 % (actual size). */\n actualSize() {\n this.setZoom(1);\n }\n zoomIn() {\n this.setZoom(this.cssScale * 1.2);\n }\n zoomOut() {\n this.setZoom(this.cssScale / 1.2);\n }\n /** Fit the current page's **width** to the viewport (sticks across resizes). */\n fitWidth() {\n this.fitMode = \"width\";\n this.applyFitMode();\n }\n /** Fit the **whole** current page (width *and* height) to the viewport. */\n fitPage() {\n this.fitMode = \"page\";\n this.applyFitMode();\n }\n /** Recompute the zoom for the active fit mode against the current page. */\n protected applyFitMode() {\n if (this.fitMode === \"none\" || this.presenting || this.count === 0) return;\n const size = this.sizes[this.current];\n if (!size) return;\n const wPt = size.w / this.renderScale;\n const hPt = size.h / this.renderScale;\n if (this.fitMode === \"width\") {\n this.applyScale((this.pages.clientWidth - 36) / wPt || 1);\n } else {\n const availW = this.pages.clientWidth - 36;\n const availH = this.pages.clientHeight - 36;\n this.applyScale(Math.min(availW / wPt, availH / hPt) || 1);\n }\n }\n\n /** Hook for subclasses (the editor) to react to zoom changes. */\n protected onZoomChange() {}\n\n private updateZoomReadout() {\n if (this.zoomReadout) this.zoomReadout.textContent = `${Math.round(this.cssScale * 100)}%`;\n if (this.zoomSelect) {\n const v =\n this.fitMode === \"width\" ? \"width\" : this.fitMode === \"page\" ? \"page\" : String(this.cssScale);\n const has = Array.from(this.zoomSelect.options).some((o) => o.value === v);\n this.zoomSelect.value = has ? v : \"\";\n }\n }\n\n // ── presentation (fullscreen slideshow) ───────────────────────────────────────\n /** Enter fullscreen single-page presentation mode. */\n present() {\n this.presenting = true;\n this.root.classList.add(\"gpv-present\");\n this.goTo(this.current);\n void this.root.requestFullscreen?.();\n }\n /** Leave presentation mode. */\n exitPresent() {\n this.presenting = false;\n this.root.classList.remove(\"gpv-present\");\n if (this.root.ownerDocument.fullscreenElement) void this.root.ownerDocument.exitFullscreen?.();\n for (let p = 1; p <= this.count; p++) {\n const el = this.pageEls[p];\n if (el) el.style.display = \"\";\n }\n this.goTo(this.current);\n }\n private syncPresentClass() {\n // The user pressed Esc / left fullscreen via the browser chrome.\n if (this.presenting && !this.root.ownerDocument.fullscreenElement) this.exitPresent();\n }\n\n // ── keyboard ─────────────────────────────────────────────────────────────────\n private handleKey(e: KeyboardEvent) {\n if (!this.isActive()) return;\n switch (e.key) {\n case \"ArrowRight\":\n case \"PageDown\":\n case \" \":\n this.next();\n break;\n case \"ArrowLeft\":\n case \"PageUp\":\n this.prev();\n break;\n case \"Home\":\n this.goTo(1);\n break;\n case \"End\":\n this.goTo(this.count);\n break;\n case \"+\":\n case \"=\":\n this.zoomIn();\n break;\n case \"-\":\n this.zoomOut();\n break;\n case \"0\":\n this.actualSize();\n break;\n case \"f\":\n case \"F\":\n this.presenting ? this.exitPresent() : this.present();\n break;\n case \"Escape\":\n if (this.presenting) this.exitPresent();\n break;\n default:\n return;\n }\n e.preventDefault();\n }\n private isActive(): boolean {\n return this.presenting || this.root.contains(this.root.ownerDocument.activeElement) || this.root.matches(\":hover\");\n }\n\n // ── chrome ───────────────────────────────────────────────────────────────────\n private buildChrome() {\n const d = this.root.ownerDocument;\n this.root.classList.add(\"gpv\");\n if (this.options.background) this.root.style.setProperty(\"--gpv-bg\", this.options.background);\n this.root.replaceChildren();\n\n if (this.options.toolbar !== false) {\n const bar = d.createElement(\"div\");\n bar.className = \"gpv-bar\";\n const btn = (label: string, fn: () => void, title?: string) => {\n const b = d.createElement(\"button\");\n b.textContent = label;\n if (title) b.title = title;\n b.addEventListener(\"click\", fn);\n bar.appendChild(b);\n return b;\n };\n btn(\"‹\", () => this.prev(), \"Previous page\");\n btn(\"›\", () => this.next(), \"Next page\");\n const input = d.createElement(\"input\");\n input.value = \"1\";\n input.addEventListener(\"change\", () => this.goTo(Number(input.value) || 1));\n bar.appendChild(input);\n this.pageInput = input;\n btn(\"−\", () => this.zoomOut(), \"Zoom out\");\n const zr = d.createElement(\"span\");\n zr.className = \"gpv-zoom\";\n zr.textContent = \"100%\";\n bar.appendChild(zr);\n this.zoomReadout = zr;\n btn(\"+\", () => this.zoomIn(), \"Zoom in\");\n const zsel = d.createElement(\"select\");\n zsel.title = \"Zoom level\";\n const zopts: [string, string][] = [\n [\"width\", \"Fit width\"],\n [\"page\", \"Fit page\"],\n [\"0.5\", \"50%\"],\n [\"0.75\", \"75%\"],\n [\"1\", \"100%\"],\n [\"1.25\", \"125%\"],\n [\"1.5\", \"150%\"],\n [\"2\", \"200%\"],\n [\"4\", \"400%\"],\n ];\n for (const [val, label] of zopts) {\n const o = d.createElement(\"option\");\n o.value = val;\n o.textContent = label;\n zsel.appendChild(o);\n }\n zsel.value = \"1\";\n zsel.addEventListener(\"change\", () => {\n const v = zsel.value;\n if (v === \"width\") this.fitWidth();\n else if (v === \"page\") this.fitPage();\n else if (v) this.setZoom(Number(v));\n });\n bar.appendChild(zsel);\n this.zoomSelect = zsel;\n const sp = d.createElement(\"div\");\n sp.className = \"gpv-sp\";\n bar.appendChild(sp);\n btn(\"⛶ Present\", () => this.present(), \"Fullscreen presentation (F)\");\n this.root.appendChild(bar);\n this.bar = bar;\n }\n\n this.body = d.createElement(\"div\");\n this.body.className = \"gpv-body\";\n this.thumbs = d.createElement(\"div\");\n this.thumbs.className = \"gpv-thumbs\";\n if (this.options.thumbnails === false) this.thumbs.style.display = \"none\";\n this.pages = d.createElement(\"div\");\n this.pages.className = \"gpv-pages\";\n // Track which page is centred while scrolling (continuous mode).\n this.pages.addEventListener(\"scroll\", () => this.trackScroll());\n this.body.append(this.thumbs, this.pages);\n this.root.appendChild(this.body);\n\n // Re-apply the active fit mode when the viewport resizes.\n if (typeof ResizeObserver !== \"undefined\") {\n this.resizeObs = new ResizeObserver(() => this.applyFitMode());\n this.resizeObs.observe(this.pages);\n }\n // Ctrl/⌘ + wheel zooms (like every PDF viewer).\n this.pages.addEventListener(\n \"wheel\",\n (e) => {\n if (!e.ctrlKey && !e.metaKey) return;\n e.preventDefault();\n this.setZoom(this.cssScale * (e.deltaY < 0 ? 1.1 : 1 / 1.1));\n },\n { passive: false }\n );\n }\n\n private trackScroll() {\n if (this.presenting || this.count === 0) return;\n const mid = this.pages.scrollTop + this.pages.clientHeight / 2;\n let best = this.current;\n for (let p = 1; p <= this.count; p++) {\n const el = this.pageEls[p];\n if (el && el.offsetTop <= mid) best = p;\n }\n if (best !== this.current) {\n this.current = best;\n if (this.pageInput) this.pageInput.value = String(best);\n if (this.fitMode !== \"none\") this.applyFitMode();\n this.highlightThumb();\n }\n }\n\n // ── lifecycle ────────────────────────────────────────────────────────────────\n /** Close the current document and free its rendered pages. */\n close() {\n for (const u of this.urls) if (u) URL.revokeObjectURL(u);\n this.urls = [];\n this.sizes = [];\n this.pageEls = [];\n this.doc?.close();\n this.doc = null;\n this.count = 0;\n this.pages?.replaceChildren();\n this.thumbs?.replaceChildren();\n }\n\n /** Destroy the viewer: close the document and detach listeners. */\n destroy() {\n this.close();\n this.resizeObs?.disconnect();\n this.resizeObs = null;\n this.root.ownerDocument.removeEventListener(\"keydown\", this.onKey);\n this.root.ownerDocument.removeEventListener(\"fullscreenchange\", this.onFsChange);\n this.root.classList.remove(\"gpv\", \"gpv-present\");\n this.root.replaceChildren();\n }\n}\n"],"mappings":"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","names":[]}