@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 @@
1
+ {"version":3,"sources":["../src/viewer.ts","../src/editor.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","/**\n * `@qrcommunication/gigapdf-lib/editor` — an interactive **editing canvas** that\n * extends {@link GigaPdfViewer}. It overlays each rendered page with an SVG\n * surface for drawing/placing annotations and shapes (rectangle, ellipse, line,\n * freehand ink, text, image, highlight, redaction), supports select / move /\n * delete, and **bakes the edits into the real PDF** through the engine\n * (`addRectangle`/`addEllipse`/`addPolygon`/`addText`/`addImage`/`redact`) — then\n * re-renders. Zero external libraries; browser-only.\n *\n * ```ts\n * const ed = new GigaPdfEditor(giga, host, { defaultFont: { family: \"Roboto\", ttf } });\n * await ed.open({ kind: \"auto\", bytes });\n * ed.setTool(\"rect\"); ed.setStyle({ color: 0xcc0000 });\n * // …user draws…\n * ed.applyEdits(); // bake into the PDF\n * const pdf = ed.save(); // download the result\n * ```\n */\nimport { GigaPdfEngine } from \"./index\";\nimport { GigaPdfViewer, type ViewerOptions } from \"./viewer\";\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\" as const;\nconst PT_PER_MM = 72 / 25.4; // 1 mm in PDF points\n\n/** A page edge that carries an adjustable margin. */\ntype Side = \"top\" | \"right\" | \"bottom\" | \"left\";\n\nexport type EditTool =\n | \"select\"\n | \"text\"\n | \"rect\"\n | \"ellipse\"\n | \"line\"\n | \"ink\"\n | \"image\"\n | \"highlight\"\n | \"redact\";\n\nexport interface EditStyle {\n /** Stroke (shapes / ink / line) and text colour, `0xRRGGBB`. */\n color: number;\n /** Shape fill colour, or `null` for no fill. */\n fill: number | null;\n lineWidth: number;\n fontSize: number;\n /** 0–1 fill/stroke alpha. */\n opacity: number;\n}\n\nexport interface EditorOptions extends ViewerOptions {\n /** A TrueType font for the text tool (required to add text). */\n defaultFont?: { family: string; ttf: Uint8Array };\n}\n\n// Element geometry is in **PDF points, top-left origin** (Y-down) so it maps 1:1\n// onto the overlay SVG (whose viewBox is the page in points) and flips to PDF's\n// bottom-left Y-up only when applied.\ntype El = {\n id: number;\n page: number;\n kind: EditTool;\n // bbox / endpoints / polyline, all in page points (top-left):\n x: number;\n y: number;\n w: number;\n h: number;\n pts?: number[];\n text?: string;\n imgUrl?: string;\n data?: Uint8Array;\n s: EditStyle;\n};\n\nconst hex = (n: number) => \"#\" + (n & 0xffffff).toString(16).padStart(6, \"0\");\n\nexport class GigaPdfEditor extends GigaPdfViewer {\n private tool: EditTool = \"select\";\n private style: EditStyle = { color: 0x1a1a1a, fill: null, lineWidth: 1.5, fontSize: 16, opacity: 1 };\n private els: El[] = [];\n private overlays: (SVGSVGElement | null)[] = [];\n private idSeq = 1;\n private selected: El | null = null;\n private fontObj: number | null = null;\n private editorOpts: EditorOptions;\n private palette: HTMLElement | null = null;\n\n // Rulers & margins. Margins are stored in **PDF points**, shared across pages.\n private margins: Record<Side, number> = { top: 56.7, right: 56.7, bottom: 56.7, left: 56.7 }; // ≈20 mm\n private guides: (SVGSVGElement | null)[] = [];\n private showGuides = true;\n private marginInputs: Record<Side, HTMLInputElement | null> = {\n top: null,\n right: null,\n bottom: null,\n left: null,\n };\n\n constructor(engine: GigaPdfEngine, container: HTMLElement, opts: EditorOptions = {}) {\n super(engine, container, opts);\n this.editorOpts = opts;\n this.buildPalette();\n }\n\n // ── public API ─────────────────────────────────────────────────────────────\n setTool(tool: EditTool) {\n this.tool = tool;\n for (const b of this.palette?.querySelectorAll(\"button[data-tool]\") ?? []) {\n b.classList.toggle(\"gpe-on\", b.getAttribute(\"data-tool\") === tool);\n }\n if (this.selected && tool !== \"select\") {\n this.selected = null;\n this.redrawAll();\n }\n }\n setStyle(patch: Partial<EditStyle>) {\n this.style = { ...this.style, ...patch };\n if (this.selected) {\n this.selected.s = { ...this.selected.s, ...patch };\n this.redraw(this.selected.page);\n }\n }\n /** Edits drawn but not yet baked into the PDF. */\n get pendingEdits(): number {\n return this.els.length;\n }\n\n /** Delete the currently selected element. */\n removeSelected() {\n if (!this.selected) return;\n const page = this.selected.page;\n this.els = this.els.filter((e) => e !== this.selected);\n this.selected = null;\n this.redraw(page);\n }\n /** Discard all un-applied edits. */\n clearEdits() {\n this.els = [];\n this.selected = null;\n this.redrawAll();\n }\n\n /** Bake every pending edit into the PDF, then re-render the affected pages. */\n applyEdits(): number {\n if (!this.doc || this.els.length === 0) return 0;\n const pages = new Set<number>();\n let applied = 0;\n for (const e of this.els) {\n if (this.bake(e)) {\n pages.add(e.page);\n applied++;\n }\n }\n this.els = [];\n this.selected = null;\n for (const p of pages) this.reRenderPage(p);\n this.redrawAll();\n return applied;\n }\n\n // ── apply one element to the PDF (flip Y: top-left points → PDF bottom-left) ──\n private bake(e: El): boolean {\n const doc = this.doc!;\n const H = this.pageHeightPt(e.page);\n const nx = Math.min(e.x, e.x + e.w);\n const ny = Math.min(e.y, e.y + e.h);\n const nw = Math.abs(e.w);\n const nh = Math.abs(e.h);\n const { color, fill, lineWidth, opacity } = e.s;\n switch (e.kind) {\n case \"rect\":\n return doc.addRectangle(e.page, nx, H - (ny + nh), nw, nh, color, fill, lineWidth, opacity);\n case \"ellipse\":\n return doc.addEllipse(e.page, nx + nw / 2, H - (ny + nh / 2), nw / 2, nh / 2, color, fill, lineWidth, opacity);\n case \"line\":\n return doc.addPolygon(e.page, [e.x, H - e.y, e.x + e.w, H - (e.y + e.h)], false, color, null, lineWidth, opacity);\n case \"ink\": {\n const flat: number[] = [];\n const pts = e.pts ?? [];\n for (let i = 0; i + 1 < pts.length; i += 2) {\n flat.push(pts[i]!, H - pts[i + 1]!);\n }\n return flat.length >= 4 ? doc.addPolygon(e.page, flat, false, color, null, lineWidth, opacity) : false;\n }\n case \"text\": {\n const id = this.ensureFont();\n if (id === null || !e.text) return false;\n return doc.addText(e.page, e.x, H - e.y, e.s.fontSize, e.text, id, color);\n }\n case \"image\":\n return e.data ? doc.addImage(e.page, e.data, nx, H - (ny + nh), nw, nh, opacity) : false;\n case \"highlight\":\n return doc.addRectangle(e.page, nx, H - (ny + nh), nw, nh, null, color, 0, 0.4);\n case \"redact\":\n return doc.redact(e.page, nx, H - (ny + nh), nw, nh, 0, false) >= 0;\n default:\n return false;\n }\n }\n\n private ensureFont(): number | null {\n if (this.fontObj !== null) return this.fontObj;\n const f = this.editorOpts.defaultFont;\n if (!f || !this.doc) return null;\n this.fontObj = this.doc.embedFont(f.family, f.ttf);\n return this.fontObj;\n }\n\n // ── overlays (rebuilt after each (re)render) ─────────────────────────────────\n protected override afterRender() {\n this.overlays = new Array(this.count + 1).fill(null);\n this.guides = new Array(this.count + 1).fill(null);\n const d = this.root.ownerDocument;\n for (let p = 1; p <= this.count; p++) {\n const box = this.pageEls[p];\n if (!box) continue;\n box.style.position = \"relative\";\n // 1) drawing surface — captures the editing tools.\n const svg = d.createElementNS(SVG_NS, \"svg\") as SVGSVGElement;\n svg.setAttribute(\"viewBox\", `0 0 ${this.pageWidthPt(p)} ${this.pageHeightPt(p)}`);\n svg.setAttribute(\"preserveAspectRatio\", \"none\");\n svg.style.cssText = \"position:absolute;inset:0;width:100%;height:100%;touch-action:none\";\n svg.dataset.page = String(p);\n box.appendChild(svg);\n this.overlays[p] = svg;\n this.bindOverlay(svg, p);\n // 2) rulers + margin guides — on top, click-through except the drag handles.\n const g = d.createElementNS(SVG_NS, \"svg\") as SVGSVGElement;\n g.setAttribute(\"viewBox\", `0 0 ${this.pageWidthPt(p)} ${this.pageHeightPt(p)}`);\n g.setAttribute(\"preserveAspectRatio\", \"none\");\n g.style.cssText = \"position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible\";\n g.dataset.page = String(p);\n box.appendChild(g);\n this.guides[p] = g;\n }\n this.redrawAll();\n this.redrawGuidesAll();\n }\n\n /** Keep ruler ticks / handles a constant on-screen size as the zoom changes. */\n protected override onZoomChange() {\n this.redrawGuidesAll();\n }\n\n // ── rulers & margins (public API) ────────────────────────────────────────────\n /** Set page margins (default unit: millimetres). */\n setMargins(m: Partial<Record<Side, number>>, unit: \"mm\" | \"pt\" = \"mm\") {\n const k = unit === \"mm\" ? PT_PER_MM : 1;\n for (const side of [\"top\", \"right\", \"bottom\", \"left\"] as Side[]) {\n const v = m[side];\n if (typeof v === \"number\" && isFinite(v)) this.margins[side] = Math.max(0, v * k);\n }\n this.syncMarginInputs();\n this.redrawGuidesAll();\n }\n /** Current margins in the requested unit (millimetres by default). */\n getMargins(unit: \"mm\" | \"pt\" = \"mm\"): Record<Side, number> {\n const k = unit === \"mm\" ? PT_PER_MM : 1;\n return {\n top: this.margins.top / k,\n right: this.margins.right / k,\n bottom: this.margins.bottom / k,\n left: this.margins.left / k,\n };\n }\n /** Show or hide the rulers and margin guides. */\n showRulers(on: boolean) {\n this.showGuides = on;\n this.redrawGuidesAll();\n }\n\n private syncMarginInputs() {\n for (const side of [\"top\", \"right\", \"bottom\", \"left\"] as Side[]) {\n const inp = this.marginInputs[side];\n if (inp) inp.value = String(Math.round(this.margins[side] / PT_PER_MM));\n }\n }\n\n // ── ruler / margin drawing ───────────────────────────────────────────────────\n private redrawGuidesAll() {\n for (let p = 1; p <= this.count; p++) this.redrawGuides(p);\n }\n\n private redrawGuides(page: number) {\n const g = this.guides[page];\n if (!g) return;\n g.replaceChildren();\n if (!this.showGuides) return;\n const W = this.pageWidthPt(page);\n const H = this.pageHeightPt(page);\n if (!W || !H) return;\n const d = this.root.ownerDocument;\n const z = this.zoom || 1;\n const band = 14 / z; // ruler thickness ≈14 px on screen\n const fs = 8 / z; // tick-label size ≈8 px\n\n const line = (x1: number, y1: number, x2: number, y2: number, stroke: string, dash?: string) => {\n const l = d.createElementNS(SVG_NS, \"line\");\n l.setAttribute(\"x1\", String(x1));\n l.setAttribute(\"y1\", String(y1));\n l.setAttribute(\"x2\", String(x2));\n l.setAttribute(\"y2\", String(y2));\n l.setAttribute(\"stroke\", stroke);\n l.setAttribute(\"stroke-width\", \"1\");\n l.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n if (dash) l.setAttribute(\"stroke-dasharray\", dash);\n g.appendChild(l);\n };\n const label = (x: number, y: number, text: string) => {\n const t = d.createElementNS(SVG_NS, \"text\");\n t.setAttribute(\"x\", String(x));\n t.setAttribute(\"y\", String(y));\n t.setAttribute(\"font-size\", String(fs));\n t.setAttribute(\"fill\", \"#c9ced2\");\n t.setAttribute(\"font-family\", \"system-ui,sans-serif\");\n t.textContent = text;\n g.appendChild(t);\n };\n const bandRect = (w: number, h: number) => {\n const r = d.createElementNS(SVG_NS, \"rect\");\n r.setAttribute(\"x\", \"0\");\n r.setAttribute(\"y\", \"0\");\n r.setAttribute(\"width\", String(w));\n r.setAttribute(\"height\", String(h));\n r.setAttribute(\"fill\", \"rgba(38,42,45,0.86)\");\n g.appendChild(r);\n };\n\n // ruler bands (top + left)\n bandRect(W, band);\n bandRect(band, H);\n\n // ticks every 5 mm (minor) / 10 mm (major + mm label)\n const step = PT_PER_MM * 5;\n for (let x = 0, m = 0; x <= W + 0.5; x += step, m += 5) {\n const major = m % 10 === 0;\n line(x, 0, x, major ? band : band * 0.55, \"#7a8085\");\n if (major && m > 0) label(x + 1 / z, band - 1.5 / z, String(m));\n }\n for (let y = 0, m = 0; y <= H + 0.5; y += step, m += 5) {\n const major = m % 10 === 0;\n line(0, y, major ? band : band * 0.55, y, \"#7a8085\");\n if (major && m > 0) label(1 / z, y - 1 / z, String(m));\n }\n\n // margin guides across the page (dashed blue)\n const ml = this.margins.left;\n const mr = W - this.margins.right;\n const mt = this.margins.top;\n const mb = H - this.margins.bottom;\n line(ml, 0, ml, H, \"#3b82f6\", \"5 3\");\n line(mr, 0, mr, H, \"#3b82f6\", \"5 3\");\n line(0, mt, W, mt, \"#3b82f6\", \"5 3\");\n line(0, mb, W, mb, \"#3b82f6\", \"5 3\");\n\n // draggable handles, sitting in the ruler bands\n const hs = 11 / z;\n this.marginHandle(g, page, \"left\", ml, band / 2, hs, \"ew-resize\", W, H);\n this.marginHandle(g, page, \"right\", mr, band / 2, hs, \"ew-resize\", W, H);\n this.marginHandle(g, page, \"top\", band / 2, mt, hs, \"ns-resize\", W, H);\n this.marginHandle(g, page, \"bottom\", band / 2, mb, hs, \"ns-resize\", W, H);\n }\n\n private marginHandle(\n g: SVGSVGElement,\n page: number,\n side: Side,\n cx: number,\n cy: number,\n size: number,\n cursor: string,\n W: number,\n H: number\n ) {\n const d = this.root.ownerDocument;\n const r = d.createElementNS(SVG_NS, \"rect\");\n r.setAttribute(\"x\", String(cx - size / 2));\n r.setAttribute(\"y\", String(cy - size / 2));\n r.setAttribute(\"width\", String(size));\n r.setAttribute(\"height\", String(size));\n r.setAttribute(\"rx\", String(size * 0.25));\n r.setAttribute(\"fill\", \"#3b82f6\");\n r.setAttribute(\"stroke\", \"#fff\");\n r.setAttribute(\"stroke-width\", \"1\");\n r.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n r.style.cssText = `pointer-events:all;cursor:${cursor}`;\n r.addEventListener(\"pointerdown\", (e: PointerEvent) => {\n // Capture on the *guide layer* (stable across redraws), not the handle —\n // each drag redraws and replaces this rect, which would drop its capture.\n e.stopPropagation();\n e.preventDefault();\n try {\n g.setPointerCapture(e.pointerId);\n } catch {\n /* capture is best-effort */\n }\n const move = (ev: PointerEvent) => {\n const pp = this.toPagePt(g, ev.clientX, ev.clientY);\n const val =\n side === \"left\" ? pp.x : side === \"right\" ? W - pp.x : side === \"top\" ? pp.y : H - pp.y;\n this.dragMargin(page, side, val);\n };\n const up = (ev: PointerEvent) => {\n try {\n g.releasePointerCapture(ev.pointerId);\n } catch {\n /* already released */\n }\n g.removeEventListener(\"pointermove\", move);\n g.removeEventListener(\"pointerup\", up);\n g.removeEventListener(\"pointercancel\", up);\n };\n g.addEventListener(\"pointermove\", move);\n g.addEventListener(\"pointerup\", up);\n g.addEventListener(\"pointercancel\", up);\n });\n g.appendChild(r);\n }\n\n private dragMargin(page: number, side: Side, val: number) {\n const W = this.pageWidthPt(page);\n const H = this.pageHeightPt(page);\n const lim = side === \"left\" || side === \"right\" ? W : H;\n const opp =\n side === \"left\"\n ? this.margins.right\n : side === \"right\"\n ? this.margins.left\n : side === \"top\"\n ? this.margins.bottom\n : this.margins.top;\n this.margins[side] = Math.max(0, Math.min(val, lim - opp - 8));\n this.syncMarginInputs();\n this.redrawGuidesAll();\n }\n\n private toPagePt(svg: SVGSVGElement, clientX: number, clientY: number): { x: number; y: number } {\n const m = svg.getScreenCTM();\n if (!m) return { x: 0, y: 0 };\n const pt = new DOMPoint(clientX, clientY).matrixTransform(m.inverse());\n return { x: pt.x, y: pt.y };\n }\n\n private bindOverlay(svg: SVGSVGElement, page: number) {\n let draft: El | null = null;\n let origin: { x: number; y: number } | null = null;\n let moving: { el: El; dx: number; dy: number } | null = null;\n\n svg.addEventListener(\"pointerdown\", (e: PointerEvent) => {\n svg.setPointerCapture(e.pointerId);\n const p = this.toPagePt(svg, e.clientX, e.clientY);\n if (this.tool === \"select\") {\n const hit = this.hitTest(page, p);\n this.selected = hit;\n if (hit) moving = { el: hit, dx: p.x - hit.x, dy: p.y - hit.y };\n this.redraw(page);\n return;\n }\n if (this.tool === \"text\") {\n this.placeText(page, p);\n return;\n }\n if (this.tool === \"image\") {\n void this.placeImage(page, p);\n return;\n }\n origin = p;\n draft = {\n id: this.idSeq++,\n page,\n kind: this.tool,\n x: p.x,\n y: p.y,\n w: 0,\n h: 0,\n s: { ...this.style },\n ...(this.tool === \"ink\" ? { pts: [p.x, p.y] } : {}),\n };\n this.els.push(draft);\n });\n\n svg.addEventListener(\"pointermove\", (e: PointerEvent) => {\n const p = this.toPagePt(svg, e.clientX, e.clientY);\n if (moving) {\n moving.el.x = p.x - moving.dx;\n moving.el.y = p.y - moving.dy;\n this.redraw(page);\n return;\n }\n if (!draft || !origin) return;\n if (draft.kind === \"ink\") {\n draft.pts!.push(p.x, p.y);\n } else {\n draft.x = origin.x;\n draft.y = origin.y;\n draft.w = p.x - origin.x;\n draft.h = p.y - origin.y;\n }\n this.redraw(page);\n });\n\n const end = () => {\n if (draft && !this.isMeaningful(draft)) {\n this.els = this.els.filter((x) => x !== draft);\n this.redraw(page);\n } else if (draft) {\n this.selected = draft;\n this.redraw(page);\n }\n draft = null;\n origin = null;\n moving = null;\n };\n svg.addEventListener(\"pointerup\", end);\n svg.addEventListener(\"pointercancel\", end);\n }\n\n private isMeaningful(e: El): boolean {\n if (e.kind === \"ink\") return (e.pts?.length ?? 0) >= 4;\n return Math.abs(e.w) > 1 || Math.abs(e.h) > 1;\n }\n\n private hitTest(page: number, p: { x: number; y: number }): El | null {\n // Topmost element whose bbox contains the point.\n for (let i = this.els.length - 1; i >= 0; i--) {\n const e = this.els[i]!;\n if (e.page !== page) continue;\n const nx = Math.min(e.x, e.x + e.w);\n const ny = Math.min(e.y, e.y + e.h);\n const nw = Math.abs(e.w) || 12;\n const nh = Math.abs(e.h) || 12;\n if (p.x >= nx - 4 && p.x <= nx + nw + 4 && p.y >= ny - 4 && p.y <= ny + nh + 4) return e;\n }\n return null;\n }\n\n private placeText(page: number, p: { x: number; y: number }) {\n const text = this.root.ownerDocument.defaultView?.prompt(\"Text:\") ?? \"\";\n if (!text.trim()) return;\n this.els.push({ id: this.idSeq++, page, kind: \"text\", x: p.x, y: p.y + this.style.fontSize, w: 0, h: 0, text, s: { ...this.style } });\n this.redraw(page);\n }\n\n private async placeImage(page: number, p: { x: number; y: number }) {\n const d = this.root.ownerDocument;\n const input = d.createElement(\"input\");\n input.type = \"file\";\n input.accept = \"image/png,image/jpeg\";\n input.addEventListener(\"change\", async () => {\n const file = input.files?.[0];\n if (!file) return;\n const data = new Uint8Array(await file.arrayBuffer());\n const url = URL.createObjectURL(file);\n this.els.push({ id: this.idSeq++, page, kind: \"image\", x: p.x, y: p.y, w: 120, h: 120, data, imgUrl: url, s: { ...this.style } });\n this.redraw(page);\n });\n input.click();\n }\n\n // ── overlay drawing ──────────────────────────────────────────────────────────\n private redrawAll() {\n for (let p = 1; p <= this.count; p++) this.redraw(p);\n }\n\n private redraw(page: number) {\n const svg = this.overlays[page];\n if (!svg) return;\n svg.replaceChildren();\n const d = this.root.ownerDocument;\n for (const e of this.els) {\n if (e.page !== page) continue;\n const node = this.elNode(d, e);\n if (node) svg.appendChild(node);\n }\n if (this.selected && this.selected.page === page) {\n const nx = Math.min(this.selected.x, this.selected.x + this.selected.w);\n const ny = Math.min(this.selected.y, this.selected.y + this.selected.h);\n const sel = d.createElementNS(SVG_NS, \"rect\");\n sel.setAttribute(\"x\", String(nx - 3));\n sel.setAttribute(\"y\", String(ny - 3));\n sel.setAttribute(\"width\", String((Math.abs(this.selected.w) || 12) + 6));\n sel.setAttribute(\"height\", String((Math.abs(this.selected.h) || 12) + 6));\n sel.setAttribute(\"fill\", \"none\");\n sel.setAttribute(\"stroke\", \"#3b82f6\");\n sel.setAttribute(\"stroke-dasharray\", \"4 3\");\n sel.setAttribute(\"stroke-width\", \"1\");\n svg.appendChild(sel);\n }\n }\n\n private elNode(d: Document, e: El): SVGElement | null {\n const stroke = hex(e.s.color);\n const fill = e.s.fill === null ? \"none\" : hex(e.s.fill);\n const nx = Math.min(e.x, e.x + e.w);\n const ny = Math.min(e.y, e.y + e.h);\n const nw = Math.abs(e.w);\n const nh = Math.abs(e.h);\n switch (e.kind) {\n case \"rect\":\n case \"highlight\":\n case \"redact\": {\n const r = d.createElementNS(SVG_NS, \"rect\");\n r.setAttribute(\"x\", String(nx));\n r.setAttribute(\"y\", String(ny));\n r.setAttribute(\"width\", String(nw));\n r.setAttribute(\"height\", String(nh));\n if (e.kind === \"rect\") {\n r.setAttribute(\"fill\", fill);\n r.setAttribute(\"stroke\", stroke);\n r.setAttribute(\"stroke-width\", String(e.s.lineWidth));\n r.setAttribute(\"opacity\", String(e.s.opacity));\n } else if (e.kind === \"highlight\") {\n r.setAttribute(\"fill\", stroke);\n r.setAttribute(\"opacity\", \"0.4\");\n } else {\n r.setAttribute(\"fill\", \"#000\");\n r.setAttribute(\"stroke\", \"#f00\");\n r.setAttribute(\"stroke-dasharray\", \"3 2\");\n r.setAttribute(\"stroke-width\", \"0.5\");\n }\n return r;\n }\n case \"ellipse\": {\n const el = d.createElementNS(SVG_NS, \"ellipse\");\n el.setAttribute(\"cx\", String(nx + nw / 2));\n el.setAttribute(\"cy\", String(ny + nh / 2));\n el.setAttribute(\"rx\", String(nw / 2));\n el.setAttribute(\"ry\", String(nh / 2));\n el.setAttribute(\"fill\", fill);\n el.setAttribute(\"stroke\", stroke);\n el.setAttribute(\"stroke-width\", String(e.s.lineWidth));\n el.setAttribute(\"opacity\", String(e.s.opacity));\n return el;\n }\n case \"line\": {\n const ln = d.createElementNS(SVG_NS, \"line\");\n ln.setAttribute(\"x1\", String(e.x));\n ln.setAttribute(\"y1\", String(e.y));\n ln.setAttribute(\"x2\", String(e.x + e.w));\n ln.setAttribute(\"y2\", String(e.y + e.h));\n ln.setAttribute(\"stroke\", stroke);\n ln.setAttribute(\"stroke-width\", String(e.s.lineWidth));\n return ln;\n }\n case \"ink\": {\n const pl = d.createElementNS(SVG_NS, \"polyline\");\n const pts = e.pts ?? [];\n let s = \"\";\n for (let i = 0; i + 1 < pts.length; i += 2) s += `${pts[i]},${pts[i + 1]} `;\n pl.setAttribute(\"points\", s.trim());\n pl.setAttribute(\"fill\", \"none\");\n pl.setAttribute(\"stroke\", stroke);\n pl.setAttribute(\"stroke-width\", String(e.s.lineWidth));\n pl.setAttribute(\"stroke-linejoin\", \"round\");\n pl.setAttribute(\"stroke-linecap\", \"round\");\n return pl;\n }\n case \"text\": {\n const t = d.createElementNS(SVG_NS, \"text\");\n t.setAttribute(\"x\", String(e.x));\n t.setAttribute(\"y\", String(e.y));\n t.setAttribute(\"font-size\", String(e.s.fontSize));\n t.setAttribute(\"fill\", stroke);\n t.setAttribute(\"font-family\", \"sans-serif\");\n t.textContent = e.text ?? \"\";\n return t;\n }\n case \"image\": {\n const im = d.createElementNS(SVG_NS, \"image\");\n im.setAttribute(\"x\", String(nx));\n im.setAttribute(\"y\", String(ny));\n im.setAttribute(\"width\", String(nw || 120));\n im.setAttribute(\"height\", String(nh || 120));\n if (e.imgUrl) im.setAttribute(\"href\", e.imgUrl);\n return im;\n }\n default:\n return null;\n }\n }\n\n // ── tool palette ─────────────────────────────────────────────────────────────\n private buildPalette() {\n const d = this.root.ownerDocument;\n const bar = d.createElement(\"div\");\n bar.className = \"gpe-bar\";\n bar.style.cssText =\n \"display:flex;flex-wrap:wrap;gap:4px;align-items:center;padding:6px 10px;background:#3a3f42;border-bottom:1px solid #000;color:#eee;font:13px system-ui\";\n const tools: [EditTool, string][] = [\n [\"select\", \"▸\"],\n [\"text\", \"T\"],\n [\"rect\", \"▭\"],\n [\"ellipse\", \"◯\"],\n [\"line\", \"╱\"],\n [\"ink\", \"✎\"],\n [\"image\", \"🖼\"],\n [\"highlight\", \"▥\"],\n [\"redact\", \"█\"],\n ];\n for (const [t, label] of tools) {\n const b = d.createElement(\"button\");\n b.textContent = label;\n b.title = t;\n b.dataset.tool = t;\n b.style.cssText = \"background:#4a4f52;color:#eee;border:0;border-radius:4px;padding:4px 8px;cursor:pointer\";\n b.addEventListener(\"click\", () => this.setTool(t));\n bar.appendChild(b);\n }\n const color = d.createElement(\"input\");\n color.type = \"color\";\n color.value = hex(this.style.color);\n color.title = \"Colour\";\n color.addEventListener(\"input\", () => this.setStyle({ color: parseInt(color.value.slice(1), 16) }));\n bar.appendChild(color);\n\n // Margins group: ruler toggle + four live mm inputs (T / R / B / L).\n const mg = d.createElement(\"span\");\n mg.style.cssText = \"display:flex;align-items:center;gap:3px;margin-left:8px\";\n const mtog = d.createElement(\"input\");\n mtog.type = \"checkbox\";\n mtog.checked = this.showGuides;\n mtog.title = \"Show rulers & margins\";\n mtog.addEventListener(\"change\", () => this.showRulers(mtog.checked));\n const mlbl = d.createElement(\"span\");\n mlbl.textContent = \"Marges mm\";\n mlbl.style.color = \"#bbb\";\n mg.append(mtog, mlbl);\n for (const side of [\"top\", \"right\", \"bottom\", \"left\"] as Side[]) {\n const inp = d.createElement(\"input\");\n inp.type = \"number\";\n inp.min = \"0\";\n inp.value = String(Math.round(this.margins[side] / PT_PER_MM));\n inp.title = side;\n inp.style.cssText =\n \"width:44px;background:#222;color:#eee;border:1px solid #000;border-radius:4px;padding:3px;text-align:center\";\n inp.addEventListener(\"change\", () => this.setMargins({ [side]: Number(inp.value) || 0 }, \"mm\"));\n this.marginInputs[side] = inp;\n mg.appendChild(inp);\n }\n bar.appendChild(mg);\n\n const sp = d.createElement(\"span\");\n sp.style.flex = \"1\";\n bar.appendChild(sp);\n\n const apply = d.createElement(\"button\");\n apply.textContent = \"Apply\";\n apply.style.cssText = \"background:#2563eb;color:#fff;border:0;border-radius:4px;padding:4px 12px;cursor:pointer\";\n apply.addEventListener(\"click\", () => this.applyEdits());\n bar.appendChild(apply);\n\n const del = d.createElement(\"button\");\n del.textContent = \"Delete\";\n del.style.cssText = \"background:#4a4f52;color:#eee;border:0;border-radius:4px;padding:4px 10px;cursor:pointer\";\n del.addEventListener(\"click\", () => this.removeSelected());\n bar.appendChild(del);\n\n // Insert below the viewer toolbar (first child) if present, else at the top.\n const style = d.createElement(\"style\");\n style.textContent = \".gpe-bar button.gpe-on{outline:2px solid #3b82f6}\";\n d.head.appendChild(style);\n this.root.insertBefore(bar, this.root.children[1] ?? null);\n this.palette = bar;\n this.setTool(\"select\");\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.cjs CHANGED
@@ -125,6 +125,10 @@ var GigaPdfEngine = class _GigaPdfEngine {
125
125
  this._free(ptr, b.length);
126
126
  }
127
127
  }
128
+ /** Pass an optional string; an absent/empty value runs `fn(0, 0)` (no alloc). */
129
+ _withOptStr(s, fn) {
130
+ return s ? this._withStr(s, fn) : fn(0, 0);
131
+ }
128
132
  /** Pass a bytes argument; runs `fn(ptr, len)` then frees. */
129
133
  _withBytes(bytes, fn) {
130
134
  const ptr = this._toWasm(bytes);
@@ -206,8 +210,194 @@ var GigaPdfEngine = class _GigaPdfEngine {
206
210
  parseCssFontUrl(css) {
207
211
  return this._withStr(css, (p, l) => this._str((o) => this.ex.gp_parse_css_font_url(p, l, o)));
208
212
  }
213
+ // ── JavaScript engine (no headless browser) ────────────────────────────────
214
+ /**
215
+ * Evaluate a JavaScript snippet with the built-in engine and return the
216
+ * result value as a string (or `Uncaught …` / `SyntaxError: …`).
217
+ */
218
+ evalJs(src) {
219
+ return this._withStr(src, (p, l) => this._str((o) => this.ex.gp_js_eval(p, l, o)));
220
+ }
221
+ /**
222
+ * Run a document's inline `<script>`s and return the resulting HTML. The
223
+ * `htmlRender`/`htmlNeededFonts` paths already do this automatically; use this
224
+ * only when you want the post-script HTML on its own.
225
+ */
226
+ runInlineScripts(html) {
227
+ return this._withStr(
228
+ html,
229
+ (p, l) => this._str((o) => this.ex.gp_run_inline_scripts(p, l, o))
230
+ );
231
+ }
232
+ // ── HTML rendering engine (replaces a headless browser for HTML→PDF) ───────
233
+ /**
234
+ * Phase 1 — the Google fonts the document references. Download each `url`
235
+ * (→ TTF) and pass the bytes back to {@link htmlRender} for an identical render.
236
+ */
237
+ htmlNeededFonts(html) {
238
+ return this._withStr(
239
+ html,
240
+ (p, l) => this._json((o) => this.ex.gp_html_needed_fonts(p, l, o))
241
+ );
242
+ }
243
+ /**
244
+ * Phase 2 — render HTML + CSS to PDF, with the supplied fonts embedded (real
245
+ * Google fonts, real metrics → identical or nearest match). Block, inline and
246
+ * table layout with pagination. Page size and margin are in points
247
+ * (US-Letter portrait, 0.5in margins by default). JavaScript is not executed.
248
+ */
249
+ htmlRender(html, fonts = [], pageWidth = 612, pageHeight = 792, margin = 36) {
250
+ const blob = packHtmlFonts(fonts);
251
+ return this._withStr(
252
+ html,
253
+ (hp, hl) => this._withBytes(
254
+ blob,
255
+ (fp, fl) => this._buffer((o) => this.ex.gp_html_render(hp, hl, fp, fl, pageWidth, pageHeight, margin, o))
256
+ )
257
+ );
258
+ }
259
+ /**
260
+ * Resolve a named paper size — `"A4"`, `"a3-landscape"`, `"letter"`, `"legal"`,
261
+ * `"tabloid"`, `"b5"`, … — to `{ w, h }` in points (portrait unless the name
262
+ * has a `-landscape` suffix). Returns `null` for an unknown name.
263
+ */
264
+ pageSize(name) {
265
+ const outPtr = this.ex.gp_alloc(16);
266
+ try {
267
+ const ok = this._withStr(
268
+ name,
269
+ (p, l) => this.ex.gp_page_size(p, l, outPtr, outPtr + 8)
270
+ );
271
+ if (!ok) return null;
272
+ const dv = this.dv();
273
+ return { w: dv.getFloat64(outPtr, true), h: dv.getFloat64(outPtr + 8, true) };
274
+ } finally {
275
+ this._free(outPtr, 16);
276
+ }
277
+ }
278
+ /**
279
+ * Phase 1 variant that also scans the running `header`/`footer` HTML, so the
280
+ * Google fonts they reference are requested alongside the body's.
281
+ */
282
+ htmlNeededFontsWith(html, header, footer) {
283
+ return this._withStr(
284
+ html,
285
+ (hp, hl) => this._withOptStr(
286
+ header,
287
+ (hdp, hdl) => this._withOptStr(
288
+ footer,
289
+ (ftp, ftl) => this._json((o) => this.ex.gp_html_needed_fonts_ex(hp, hl, hdp, hdl, ftp, ftl, o))
290
+ )
291
+ )
292
+ );
293
+ }
294
+ /**
295
+ * Phase 2 with full page control: named/explicit size, per-side margins, and a
296
+ * running header/footer painted in the page margins. `{{page}}` and `{{pages}}`
297
+ * in the header/footer are replaced with the current / total page number.
298
+ *
299
+ * ```ts
300
+ * const fonts = await fetchFonts(giga.htmlNeededFontsWith(html, header, footer));
301
+ * const pdf = giga.htmlRenderWith(html, fonts, {
302
+ * pageSize: "A4",
303
+ * margin: { top: 72, bottom: 72, left: 54, right: 54 },
304
+ * header: `<div style="text-align:center">My Report</div>`,
305
+ * footer: `<div style="text-align:center">Page {{page}} / {{pages}}</div>`,
306
+ * });
307
+ * ```
308
+ */
309
+ htmlRenderWith(html, fonts = [], options = {}) {
310
+ let pw = options.pageWidth ?? 612;
311
+ let ph = options.pageHeight ?? 792;
312
+ if (options.pageSize) {
313
+ const sz = this.pageSize(options.pageSize);
314
+ if (!sz) throw new Error(`gigapdf: unknown page size "${options.pageSize}"`);
315
+ pw = sz.w;
316
+ ph = sz.h;
317
+ }
318
+ const m = options.margin ?? 36;
319
+ const mg = typeof m === "number" ? { top: m, right: m, bottom: m, left: m } : { top: m.top ?? 36, right: m.right ?? 36, bottom: m.bottom ?? 36, left: m.left ?? 36 };
320
+ const headerOffset = options.headerOffset ?? 18;
321
+ const footerOffset = options.footerOffset ?? 18;
322
+ const start = options.startPageNumber ?? 1;
323
+ const blob = packHtmlFonts(fonts);
324
+ return this._withStr(
325
+ html,
326
+ (hp, hl) => this._withBytes(
327
+ blob,
328
+ (fp, fl) => this._withOptStr(
329
+ options.header,
330
+ (hdp, hdl) => this._withOptStr(
331
+ options.footer,
332
+ (ftp, ftl) => this._buffer(
333
+ (o) => this.ex.gp_html_render_opts(
334
+ hp,
335
+ hl,
336
+ fp,
337
+ fl,
338
+ pw,
339
+ ph,
340
+ mg.top,
341
+ mg.right,
342
+ mg.bottom,
343
+ mg.left,
344
+ hdp,
345
+ hdl,
346
+ ftp,
347
+ ftl,
348
+ headerOffset,
349
+ footerOffset,
350
+ start,
351
+ o
352
+ )
353
+ )
354
+ )
355
+ )
356
+ )
357
+ );
358
+ }
209
359
  };
360
+ function packHtmlFonts(fonts) {
361
+ let size = 4;
362
+ for (const f of fonts) size += 4 + enc.encode(f.family).length + 2 + 1 + 4 + f.ttf.length;
363
+ const buf = new Uint8Array(size);
364
+ const dv = new DataView(buf.buffer);
365
+ let o = 0;
366
+ dv.setUint32(o, fonts.length, true);
367
+ o += 4;
368
+ for (const f of fonts) {
369
+ const fam = enc.encode(f.family);
370
+ dv.setUint32(o, fam.length, true);
371
+ o += 4;
372
+ buf.set(fam, o);
373
+ o += fam.length;
374
+ dv.setUint16(o, f.weight, true);
375
+ o += 2;
376
+ buf[o] = f.italic ? 1 : 0;
377
+ o += 1;
378
+ dv.setUint32(o, f.ttf.length, true);
379
+ o += 4;
380
+ buf.set(f.ttf, o);
381
+ o += f.ttf.length;
382
+ }
383
+ return buf;
384
+ }
210
385
  var RGB = (rgb) => rgb & 16777215;
386
+ function styleArgs(s = {}) {
387
+ const hasBorder = s.border === null ? 0 : 1;
388
+ const borderRgb = s.border == null ? 0 : s.border;
389
+ const hasBg = s.background == null ? 0 : 1;
390
+ const bgRgb = s.background == null ? 0 : s.background;
391
+ return [
392
+ s.fontSize ?? 0,
393
+ RGB(s.color ?? 0),
394
+ RGB(borderRgb),
395
+ hasBorder,
396
+ RGB(bgRgb),
397
+ hasBg,
398
+ s.borderWidth ?? 1
399
+ ];
400
+ }
211
401
  var GigaPdfDoc = class {
212
402
  constructor(g, h) {
213
403
  this.g = g;
@@ -274,7 +464,7 @@ var GigaPdfDoc = class {
274
464
  * Draw a vector rectangle. Pass an `0xRRGGBB` colour for `stroke`/`fill`, or
275
465
  * `null` to omit that paint. 0 → success.
276
466
  */
277
- addRectangle(page, x, y, w, h, stroke = null, fill = 0, lineWidth = 1) {
467
+ addRectangle(page, x, y, w, h, stroke = null, fill = 0, lineWidth = 1, opacity = 1) {
278
468
  return this.ex().gp_add_rectangle(
279
469
  this.h,
280
470
  page,
@@ -286,9 +476,99 @@ var GigaPdfDoc = class {
286
476
  stroke === null ? 0 : 1,
287
477
  RGB(fill ?? 0),
288
478
  fill === null ? 0 : 1,
289
- lineWidth
479
+ lineWidth,
480
+ opacity
481
+ ) === 0;
482
+ }
483
+ /** Draw a straight line from `(x1,y1)` to `(x2,y2)`. `stroke` is `0xRRGGBB`. */
484
+ drawLine(page, x1, y1, x2, y2, stroke = 0, lineWidth = 1, opacity = 1) {
485
+ return this.ex().gp_draw_line(this.h, page, x1, y1, x2, y2, RGB(stroke), lineWidth, opacity) === 0;
486
+ }
487
+ /**
488
+ * Draw an ellipse (circle when `rx === ry`) centred at `(cx,cy)`. Pass an
489
+ * `0xRRGGBB` colour for `stroke`/`fill`, or `null` to omit that paint.
490
+ */
491
+ addEllipse(page, cx, cy, rx, ry, stroke = null, fill = 0, lineWidth = 1, opacity = 1) {
492
+ return this.ex().gp_add_ellipse(
493
+ this.h,
494
+ page,
495
+ cx,
496
+ cy,
497
+ rx,
498
+ ry,
499
+ RGB(stroke ?? 0),
500
+ stroke === null ? 0 : 1,
501
+ RGB(fill ?? 0),
502
+ fill === null ? 0 : 1,
503
+ lineWidth,
504
+ opacity
290
505
  ) === 0;
291
506
  }
507
+ /**
508
+ * Draw a polyline/polygon through flat `[x0,y0,x1,y1,…]` points. `close` joins
509
+ * the last vertex back to the first. `0xRRGGBB` colours, or `null` to omit.
510
+ */
511
+ addPolygon(page, points, close = true, stroke = null, fill = 0, lineWidth = 1, opacity = 1) {
512
+ return this.g._withF64(
513
+ points,
514
+ (p, c) => this.ex().gp_add_polygon(
515
+ this.h,
516
+ page,
517
+ p,
518
+ c,
519
+ close ? 1 : 0,
520
+ RGB(stroke ?? 0),
521
+ stroke === null ? 0 : 1,
522
+ RGB(fill ?? 0),
523
+ fill === null ? 0 : 1,
524
+ lineWidth,
525
+ opacity
526
+ )
527
+ ) === 0;
528
+ }
529
+ /**
530
+ * Draw an SVG path (`M`/`L`/`C`/`Q`/`Z`…) anchored so the SVG origin maps to
531
+ * `(ox,oy)` with the Y axis flipped — same convention as `pdf-lib`'s
532
+ * `drawSvgPath`. Covers freeform/polygon/triangle shapes.
533
+ */
534
+ addPath(page, svgPath, ox, oy, stroke = null, fill = 0, lineWidth = 1, opacity = 1) {
535
+ return this.g._withStr(
536
+ svgPath,
537
+ (p, l) => this.ex().gp_add_path(
538
+ this.h,
539
+ page,
540
+ p,
541
+ l,
542
+ ox,
543
+ oy,
544
+ RGB(stroke ?? 0),
545
+ stroke === null ? 0 : 1,
546
+ RGB(fill ?? 0),
547
+ fill === null ? 0 : 1,
548
+ lineWidth,
549
+ opacity
550
+ )
551
+ ) === 0;
552
+ }
553
+ /**
554
+ * Embed a raster image (PNG or JPEG bytes) at `(x,y)` sized `(w,h)` in PDF
555
+ * user space. PNG alpha is honoured; `opacity` (0..1) sets an overall alpha.
556
+ */
557
+ addImage(page, data, x, y, w, h, opacity = 1) {
558
+ return this.g._withBytes(
559
+ data,
560
+ (p, l) => this.ex().gp_add_image(this.h, page, p, l, x, y, w, h, opacity)
561
+ ) === 0;
562
+ }
563
+ /**
564
+ * Draw SVG markup on a page as **native vector paths** (crisp at any zoom, not
565
+ * rasterized), fitting its `viewBox` into the box `(x, y, w, h)` in PDF points
566
+ * (origin bottom-left). Supports shapes, `<path>`, groups, transforms and
567
+ * fill/stroke/opacity. Returns `false` if the SVG can't be parsed.
568
+ */
569
+ addSvg(page, svg, x, y, w, h) {
570
+ return this.g._withStr(svg, (p, l) => this.ex().gp_add_svg(this.h, page, p, l, x, y, w, h)) === 0;
571
+ }
292
572
  /** True redaction: delete content intersecting the region (no opaque cover by default). */
293
573
  redact(page, x, y, w, h, coverRgb = 0, hasCover = false) {
294
574
  return this.ex().gp_redact_region(this.h, page, x, y, w, h, RGB(coverRgb), hasCover ? 1 : 0);
@@ -366,6 +646,10 @@ var GigaPdfDoc = class {
366
646
  toPptx() {
367
647
  return this.g._buffer((o) => this.ex().gp_to_pptx(this.h, o));
368
648
  }
649
+ /** Convert to an editable OpenDocument Presentation (`.odp`). */
650
+ toOdp() {
651
+ return this.g._buffer((o) => this.ex().gp_to_odp(this.h, o));
652
+ }
369
653
  toOdt() {
370
654
  return this.g._buffer((o) => this.ex().gp_to_odt(this.h, o));
371
655
  }
@@ -532,6 +816,141 @@ var GigaPdfDoc = class {
532
816
  (nP, nL) => this.g._withStr(values.join("\n"), (vP, vL) => this.ex().gp_set_choice(this.h, nP, nL, vP, vL))
533
817
  ) === 0;
534
818
  }
819
+ // ── form field creation ──────────────────────────────────────────────────
820
+ /**
821
+ * Create a text field on `page` covering `rect` = `[x0, y0, x1, y1]` (PDF
822
+ * user space). Options: `maxLen` character cap, `multiline`, `password`,
823
+ * and visual `style`.
824
+ */
825
+ addTextField(page, name, rect, value = "", opts = {}) {
826
+ const st = styleArgs(opts.style);
827
+ return this.g._withStr(
828
+ name,
829
+ (nP, nL) => this.g._withStr(
830
+ value,
831
+ (vP, vL) => this.ex().gp_add_text_field(
832
+ this.h,
833
+ page,
834
+ nP,
835
+ nL,
836
+ rect[0],
837
+ rect[1],
838
+ rect[2],
839
+ rect[3],
840
+ vP,
841
+ vL,
842
+ opts.maxLen ?? -1,
843
+ opts.multiline ? 1 : 0,
844
+ opts.password ? 1 : 0,
845
+ ...st
846
+ )
847
+ )
848
+ ) === 0;
849
+ }
850
+ /** Create a checkbox. `export` is the on-state name (default `On`). */
851
+ addCheckbox(page, name, rect, checked = false, opts = {}) {
852
+ const st = styleArgs(opts.style);
853
+ return this.g._withStr(
854
+ name,
855
+ (nP, nL) => this.g._withStr(
856
+ opts.export ?? "On",
857
+ (eP, eL) => this.ex().gp_add_checkbox(
858
+ this.h,
859
+ page,
860
+ nP,
861
+ nL,
862
+ rect[0],
863
+ rect[1],
864
+ rect[2],
865
+ rect[3],
866
+ checked ? 1 : 0,
867
+ eP,
868
+ eL,
869
+ ...st
870
+ )
871
+ )
872
+ ) === 0;
873
+ }
874
+ /**
875
+ * Create a radio-button group: one logical field whose `options` are the
876
+ * individual buttons. `selected` is the initially-chosen export value.
877
+ */
878
+ addRadioGroup(page, name, options, opts = {}) {
879
+ const st = styleArgs(opts.style);
880
+ const exports2 = options.map((o) => o.export).join("\n");
881
+ const rects = options.flatMap((o) => o.rect).join(",");
882
+ return this.g._withStr(
883
+ name,
884
+ (nP, nL) => this.g._withStr(
885
+ exports2,
886
+ (eP, eL) => this.g._withStr(
887
+ rects,
888
+ (rP, rL) => this.g._withStr(
889
+ opts.selected ?? "",
890
+ (sP, sL) => this.ex().gp_add_radio_group(this.h, page, nP, nL, eP, eL, rP, rL, sP, sL, ...st)
891
+ )
892
+ )
893
+ )
894
+ ) === 0;
895
+ }
896
+ /** Create a drop-down combo box. `editable` permits values outside `options`. */
897
+ addComboBox(page, name, rect, options, opts = {}) {
898
+ const st = styleArgs(opts.style);
899
+ return this.g._withStr(
900
+ name,
901
+ (nP, nL) => this.g._withStr(
902
+ options.join("\n"),
903
+ (oP, oL) => this.g._withStr(
904
+ opts.selected ?? "",
905
+ (sP, sL) => this.ex().gp_add_combo_box(
906
+ this.h,
907
+ page,
908
+ nP,
909
+ nL,
910
+ rect[0],
911
+ rect[1],
912
+ rect[2],
913
+ rect[3],
914
+ oP,
915
+ oL,
916
+ sP,
917
+ sL,
918
+ opts.editable ? 1 : 0,
919
+ ...st
920
+ )
921
+ )
922
+ )
923
+ ) === 0;
924
+ }
925
+ /** Create a scrolling list box. `multi` allows selecting several options. */
926
+ addListBox(page, name, rect, options, opts = {}) {
927
+ const st = styleArgs(opts.style);
928
+ return this.g._withStr(
929
+ name,
930
+ (nP, nL) => this.g._withStr(
931
+ options.join("\n"),
932
+ (oP, oL) => this.g._withStr(
933
+ opts.selected ?? "",
934
+ (sP, sL) => this.ex().gp_add_list_box(
935
+ this.h,
936
+ page,
937
+ nP,
938
+ nL,
939
+ rect[0],
940
+ rect[1],
941
+ rect[2],
942
+ rect[3],
943
+ oP,
944
+ oL,
945
+ sP,
946
+ sL,
947
+ opts.multi ? 1 : 0,
948
+ ...st
949
+ )
950
+ )
951
+ )
952
+ ) === 0;
953
+ }
535
954
  };
536
955
  // Annotate the CommonJS export names for ESM import in node:
537
956
  0 && (module.exports = {