UNPKG

@npoci/pdfform

Version:

Modern PDF form renderer with HTML overlay fields - view, fill, and map PDF forms in the browser

1,330 lines 51.7 kB
import * as d from "pdfjs-dist"; import { PDFDocument as y } from "pdf-lib"; class p { constructor() { this.events = /* @__PURE__ */ new Map(); } on(e, t) { return this.events.has(e) || this.events.set(e, []), this.events.get(e).push(t), this; } off(e, t) { const i = this.events.get(e); if (i) { const s = i.indexOf(t); s !== -1 && i.splice(s, 1); } return this; } emit(e, ...t) { const i = this.events.get(e); i && i.forEach((s) => { try { s(...t); } catch (r) { console.error(`Error in event listener for "${e}":`, r); } }); } once(e, t) { const i = (...s) => { this.off(e, i), t(...s); }; return this.on(e, i); } removeAllListeners(e) { return e ? this.events.delete(e) : this.events.clear(), this; } listenerCount(e) { const t = this.events.get(e); return t ? t.length : 0; } } class w { constructor() { this.parsers = [], this.unparsedFields = []; } static getInstance() { return this.instance || (this.instance = new w()), this.instance; } registerParser(e, t = "first") { return t === "first" ? this.parsers.unshift(e) : this.parsers.length > 0 ? this.parsers.splice(-1, 0, e) : this.parsers.push(e), this; } parseField(e) { for (const i of this.parsers) try { const s = i.parse(e); if (s) return console.log(`Field "${s.key}" parsed by ${i.constructor.name}`), s; } catch (s) { console.warn(`Parser ${i.constructor.name} threw error:`, s); } const t = { key: e.getName(), type: "unknown", originalType: e.constructor.name, position: this.getBasicPosition(e), rawData: { constructorName: e.constructor.name, methods: this.getAvailableMethods(e), flags: this.getFieldFlags(e) }, reason: "No parser could identify this field type" }; return this.unparsedFields.push(t), console.warn(`Field "${t.key}" could not be parsed. Type: ${t.originalType}`), t; } getUnparsedFields() { return [...this.unparsedFields]; } clearUnparsedFields() { this.unparsedFields = []; } getBasicPosition(e) { try { const t = e.acroField.getWidgets(); if (t && t.length > 0) { const i = t[0].getRectangle(); return { x: i.x, y: i.y, width: i.width, height: i.height, page: 1 }; } } catch { console.warn("Could not extract position for unparsed field"); } return { x: 0, y: 0, width: 0, height: 0, page: 1 }; } getAvailableMethods(e) { const t = []; for (const i in e) typeof e[i] == "function" && t.push(i); return t; } getFieldFlags(e) { try { return e.acroField.getFlags() || 0; } catch { return 0; } } } class h { getPosition(e) { const t = e.acroField.getWidgets(); if (!t || t.length === 0) return { x: 0, y: 0, width: 0, height: 0, page: 1 }; const i = t[0], s = i.getRectangle(); let r = 1; try { const a = i.P(); a && e.acroField.doc && (r = e.acroField.doc.getPages().findIndex((c) => c.ref === a) + 1); } catch { console.warn("Could not determine page number for field", e.getName()); } const n = this.getPageHeight(e, r); return { x: s.x, y: n - (s.y + s.height), width: s.width, height: s.height, page: r }; } getPageHeight(e, t) { var i; try { const s = (i = e.acroField.doc) == null ? void 0 : i.getPages(); if (s && s[t - 1]) return s[t - 1].getHeight(); } catch { console.warn("Could not get page height"); } return 792; } getBasicInfo(e) { return { key: e.getName(), position: this.getPosition(e), defaultValue: this.extractDefaultValue(e) }; } extractDefaultValue(e) { try { if (e.getText) return e.getText(); if (e.isChecked) return e.isChecked(); if (e.getSelected) return e.getSelected(); if (e.getValue) return e.getValue(); } catch { console.warn("Could not extract default value for field", e.getName()); } return null; } hasMethod(e, t) { return typeof e[t] == "function"; } getFieldFlags(e) { try { return e.acroField.getFlags() || 0; } catch { return 0; } } } class b extends h { canHandle(e) { return e.constructor.name.includes("PDFTextField") || this.hasMethod(e, "setText") || this.hasMethod(e, "getText"); } parse(e) { if (!this.canHandle(e)) return null; const t = this.getFieldFlags(e); return { ...this.getBasicInfo(e), type: "text", originalType: e.constructor.name, maxLength: this.getMaxLength(e), multiline: !!(t & 4096), password: !!(t & 8192), comb: !!(t & 16777216), defaultValue: this.getText(e) }; } getText(e) { try { if (e.getText) return e.getText() || ""; } catch { } return ""; } getMaxLength(e) { try { if (e.getMaxLength) { const t = e.getMaxLength(); return t > 0 ? t : void 0; } } catch { } } } class E extends h { canHandle(e) { if (!e.constructor.name.includes("PDFTextField")) return !1; const t = e.getName().toLowerCase(); return t.includes("email") || t.includes("e-mail") || t.includes("mail") || t.includes("correo"); } parse(e) { return this.canHandle(e) ? { ...this.getBasicInfo(e), type: "email", detectedType: "email", originalType: e.constructor.name, pattern: "^[^@]+@[^@]+\\.[^@]+$", inputType: "email", defaultValue: this.getText(e) } : null; } getText(e) { try { if (e.getText) return e.getText() || ""; } catch { } return ""; } } class M extends h { canHandle(e) { const t = e.getName().toLowerCase(), s = this.getPosition(e).height > 60; return e.constructor.name.includes("PDFSignature") || t.includes("signature") || t.includes("sign") || t.includes("firma") || e.constructor.name.includes("PDFTextField") && s; } parse(e) { return this.canHandle(e) ? { ...this.getBasicInfo(e), type: "signature", detectedType: "signature", originalType: e.constructor.name, detectedFrom: this.getDetectionReason(e), requiresDrawing: !0, acceptsUpload: !0 } : null; } getDetectionReason(e) { if (e.constructor.name.includes("PDFSignature")) return "PDFSignature field type"; const t = e.getName().toLowerCase(); return t.includes("signature") || t.includes("sign") ? "Field name contains signature" : "Large text field detected as signature"; } } class C extends h { canHandle(e) { const t = e.constructor.name, i = this.getFieldFlags(e), s = t.includes("PDFButton") || t.includes("PDFCheckBox") || t.includes("Checkbox"), r = !(i & 32768); return s && r || this.hasMethod(e, "check") || this.hasMethod(e, "isChecked"); } parse(e) { return this.canHandle(e) ? { ...this.getBasicInfo(e), type: "checkbox", originalType: e.constructor.name, checked: this.isChecked(e), defaultValue: this.isChecked(e) } : null; } isChecked(e) { var t; try { if (e.isChecked) return e.isChecked(); const i = (t = e.acroField) == null ? void 0 : t.getWidgets(); if (i && i.length > 0) { const s = i[0].getAppearanceState(); return s && s.toString() !== "/Off"; } } catch { console.warn("Could not determine checkbox state"); } return !1; } } class R extends h { canHandle(e) { return e.constructor.name.includes("PDFDropdown") || e.constructor.name.includes("PDFComboBox") || e.constructor.name.includes("PDFChoice") || this.hasMethod(e, "getOptions") || this.hasMethod(e, "select"); } parse(e) { if (!this.canHandle(e)) return null; const t = this.getFieldFlags(e), i = !!(t & 131072), s = !!(t & 2097152), r = this.extractOptions(e), n = this.getSelectedValue(e), a = { ...this.getBasicInfo(e), originalType: e.constructor.name, options: r, value: n, defaultValue: n }; return i || !s ? { ...a, type: "dropdown", multiSelect: !1, value: Array.isArray(n) ? n[0] : n } : { ...a, type: "listbox", multiSelect: s }; } extractOptions(e) { var i, s; const t = []; try { if (e.getOptions) return e.getOptions().map((a) => ({ value: a, display: a })); const r = (s = (i = e.acroField) == null ? void 0 : i.dict) == null ? void 0 : s.get("Opt"); if (r) { const n = r.asArray(); n && n.forEach((a) => { if (typeof a == "string") t.push({ value: a, display: a }); else if (a.asArray) { const o = a.asArray(); o && o.length >= 2 && t.push({ value: o[0].toString(), display: o[1].toString() }); } }); } } catch { console.warn("Could not extract options for dropdown", e.getName()); } return t; } getSelectedValue(e) { try { if (e.getSelected) { const t = e.getSelected(); return Array.isArray(t) ? t.length > 0 ? t : void 0 : t || void 0; } } catch { console.warn("Could not get selected value"); } } } class V extends h { canHandle(e) { const t = e.constructor.name, s = !!(this.getFieldFlags(e) & 32768); return t.includes("PDFRadioGroup") || t.includes("PDFRadio") || s || this.hasMethod(e, "getOptions"); } parse(e) { return this.canHandle(e) ? { ...this.getBasicInfo(e), type: "radio", originalType: e.constructor.name, options: this.extractOptions(e), value: this.getSelectedValue(e), defaultValue: this.getSelectedValue(e) } : null; } extractOptions(e) { var i; const t = []; try { if (e.getOptions) return e.getOptions().map((n) => ({ value: n, display: n })); const s = (i = e.acroField) == null ? void 0 : i.getWidgets(); if (s) { const r = /* @__PURE__ */ new Set(); s.forEach((n) => { try { const a = n.getAppearanceStates(); a && a.forEach((o) => { o !== "Off" && r.add(o); }); } catch { } }), r.forEach((n) => { t.push({ value: n, display: n }); }); } } catch { console.warn("Could not extract radio options", e.getName()); } return t; } getSelectedValue(e) { var t; try { if (e.getSelected) return e.getSelected() || void 0; const i = (t = e.acroField) == null ? void 0 : t.getWidgets(); if (i) for (const s of i) { const r = s.getAppearanceState(); if (r && r.toString() !== "/Off") return r.toString().replace("/", ""); } } catch { console.warn("Could not get selected radio value"); } } } class P { constructor() { this.fieldDefinitions = /* @__PURE__ */ new Map(), this.parserChain = w.getInstance(), this.initializeParsers(); } initializeParsers() { this.parserChain.registerParser(new M(), "first").registerParser(new E(), "first").registerParser(new C()).registerParser(new V()).registerParser(new R()).registerParser(new b()); } setFieldDefinitions(e) { this.fieldDefinitions.clear(), e.forEach((t) => { this.fieldDefinitions.set(t.key, t); }); } async discoverFields(e) { this.parserChain.clearUnparsedFields(); try { const t = await y.load(e), s = t.getForm().getFields(); s.forEach((n) => { n.acroField && (n.acroField.doc = t); }); const r = []; for (const n of s) { const a = this.parserChain.parseField(n), o = this.fieldDefinitions.get(a.key), c = { ...a, definition: o }; o && o.pattern && a.type === "text" && o.pattern.includes("@") && (c.detectedType = "email"), r.push(c); } return { fields: r, unparsedFields: this.parserChain.getUnparsedFields() }; } catch (t) { throw console.error("Error discovering fields:", t), new Error(`Failed to discover fields: ${t}`); } } getFieldMappings(e) { const t = {}; return e.forEach((i) => { var r; const s = ((r = i.definition) == null ? void 0 : r.name) || i.key; t[s] = { originalKey: i.key, type: i.detectedType || i.type, value: i.defaultValue }; }), t; } } const S = { highlightColor: "#ffffcc", highlightColorFocus: "#ffffaa" }; class f { constructor(e) { this.config = e || S; } getState(e, t) { var r, n, a, o; const i = this.getValue(t), s = this.validateValue(e, i); return { key: e.key, name: (r = e.definition) == null ? void 0 : r.name, value: i, type: e.type, detectedType: e.detectedType, valid: s.valid, error: s.error, required: (n = e.definition) == null ? void 0 : n.required, metadata: { title: (a = e.definition) == null ? void 0 : a.title, placeholder: (o = e.definition) == null ? void 0 : o.placeholder } }; } validateValue(e, t) { if (!e.definition) return { valid: !0 }; if (e.definition.pattern && typeof t == "string" && t) try { if (!new RegExp(e.definition.pattern).test(t)) return { valid: !1, error: e.definition.errorMessage || "Invalid format" }; } catch { console.error("Invalid regex pattern:", e.definition.pattern); } if (e.definition.validator) { const i = e.definition.validator(t); if (i) return { valid: !1, error: i }; } return { valid: !0 }; } createBaseElement(e, t) { const i = document.createElement(e); return i.style.width = "100%", i.style.height = "100%", i.style.border = "none", i.style.padding = "0", i.style.margin = "0", i.style.outline = "none", i.style.fontFamily = "Arial, sans-serif", i.style.background = this.config.highlightColor, t.definition && (t.definition.placeholder && "placeholder" in i && (i.placeholder = t.definition.placeholder), t.definition.required && (i.style.borderLeft = "3px solid #ff9800"), t.definition.title && (i.title = t.definition.title)), i; } applyValidation(e, t, i) { t.definition && e.addEventListener("input", (s) => { const n = s.target.value; let a = !0, o = ""; if (t.definition.pattern && (new RegExp(t.definition.pattern).test(n) || (a = !1, o = t.definition.errorMessage || "Invalid format")), t.definition.validator) { const c = t.definition.validator(n); c && (a = !1, o = c); } !a && n ? (e.style.borderColor = "red", e.style.borderWidth = "2px", e.style.borderStyle = "solid", e.title = o) : (e.style.borderColor = "", e.style.borderWidth = "", e.style.borderStyle = "", e.title = t.definition.title || ""), i(n); }); } } class T extends f { validateValue(e, t) { var s; const i = super.validateValue(e, t); return i.valid ? (s = e.definition) != null && s.required && (!t || typeof t == "string" && !t.trim()) ? { valid: !1, error: "This field is required" } : { valid: !0 } : i; } createField(e, t, i) { const s = this.createBaseElement("input", e); if (s.type = "text", s.style.fontSize = t.fontSize + "px", s.style.lineHeight = "1", s.style.verticalAlign = "top", e.defaultValue && (s.value = e.defaultValue), e.multiline) { const r = this.createBaseElement("textarea", e); return r.style.fontSize = t.fontSize + "px", r.style.resize = "none", r.style.lineHeight = "1.2", e.defaultValue && (r.value = e.defaultValue), r.addEventListener("input", () => i(r.value)), this.applyValidation(r, e, i), r.addEventListener("focus", () => { r.style.background = this.config.highlightColorFocus; }), r.addEventListener("blur", () => { r.style.background = this.config.highlightColor; }), r; } return e.password && (s.type = "password"), e.maxLength && (s.maxLength = e.maxLength), (e.detectedType === "email" || e.type === "email") && (s.type = "email"), s.addEventListener("input", () => i(s.value)), this.applyValidation(s, e, i), s.addEventListener("focus", () => { s.style.background = this.config.highlightColorFocus; }), s.addEventListener("blur", () => { s.style.background = this.config.highlightColor; }), s; } getValue(e) { return e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement ? e.value : ""; } setValue(e, t) { (e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement) && (e.value = String(t || "")); } } class L extends f { createField(e, t, i) { var r; const s = document.createElement("input"); return s.type = "checkbox", s.style.width = "100%", s.style.height = "100%", s.style.cursor = "pointer", s.style.margin = "0", s.style.padding = "0", s.style.accentColor = this.config.highlightColor, s.style.outline = `3px solid ${this.config.highlightColor}`, s.style.outlineOffset = "-3px", s.style.verticalAlign = "top", (e.defaultValue === !0 || e.checked) && (s.checked = !0), s.addEventListener("change", () => { i(s.checked); }), (r = e.definition) != null && r.title && (s.title = e.definition.title), s; } getValue(e) { return e instanceof HTMLInputElement ? e.checked : !1; } setValue(e, t) { e instanceof HTMLInputElement && (e.checked = !!t); } } class B extends f { createField(e, t, i) { var n; const s = this.createBaseElement("select", e); if (s.style.fontSize = t.fontSize + "px", s.style.lineHeight = "1", s.style.verticalAlign = "top", s.style.cursor = "pointer", !((n = e.definition) != null && n.required)) { const a = document.createElement("option"); a.value = "", a.textContent = "-- Select --", s.appendChild(a); } return (e.options || []).forEach((a) => { const o = document.createElement("option"); o.value = a.value, o.textContent = a.display, s.appendChild(o); }), e.defaultValue && (s.value = e.defaultValue), e.multiSelect && (s.multiple = !0, s.style.height = "100%"), s.addEventListener("change", () => { if (s.multiple) { const a = Array.from(s.selectedOptions).map((o) => o.value); i(a); } else i(s.value); }), s.addEventListener("focus", () => { s.style.background = this.config.highlightColorFocus; }), s.addEventListener("blur", () => { s.style.background = this.config.highlightColor; }), s; } getValue(e) { return e instanceof HTMLSelectElement ? e.multiple ? Array.from(e.selectedOptions).map((t) => t.value) : e.value : ""; } setValue(e, t) { e instanceof HTMLSelectElement && (e.multiple && Array.isArray(t) ? (Array.from(e.options).forEach((i) => i.selected = !1), t.forEach((i) => { const s = e.querySelector(`option[value="${i}"]`); s && (s.selected = !0); })) : e.value = String(t || "")); } } class W extends f { constructor() { super(...arguments), this.inputs = []; } createField(e, t, i) { const s = document.createElement("div"); s.style.display = "flex", s.style.width = "100%", s.style.height = "100%", s.style.gap = "2px"; const r = e.maxLength || 9, n = (t.width - (r - 1) * 2) / r; for (let a = 0; a < r; a++) { const o = document.createElement("input"); o.type = "text", o.maxLength = 1, o.style.width = n + "px", o.style.height = "100%", o.style.textAlign = "center", o.style.border = "none", o.style.background = this.config.highlightColor, o.style.fontSize = t.fontSize + "px", o.style.fontFamily = "monospace", o.style.padding = "0", o.style.margin = "0", o.style.outline = "none", o.style.boxSizing = "border-box", o.addEventListener("input", (c) => { c.target.value.length === 1 && a < r - 1 && this.inputs[a + 1].focus(), this.updateValue(i); }), o.addEventListener("keydown", (c) => { c.key === "Backspace" && !o.value && a > 0 && (c.preventDefault(), this.inputs[a - 1].focus(), this.inputs[a - 1].value = "", this.updateValue(i)); }), o.addEventListener("paste", (c) => { var k; c.preventDefault(); const x = (((k = c.clipboardData) == null ? void 0 : k.getData("text")) || "").replace(/[^a-zA-Z0-9]/g, "").split(""); x.forEach((m, g) => { a + g < this.inputs.length && (this.inputs[a + g].value = m); }); const D = this.inputs.findIndex((m, g) => g >= a && !m.value); D !== -1 ? this.inputs[D].focus() : this.inputs[Math.min(a + x.length, this.inputs.length - 1)].focus(), this.updateValue(i); }), o.addEventListener("focus", () => { o.style.background = "rgba(255, 255, 0, 0.5)"; }), o.addEventListener("blur", () => { o.style.background = this.config.highlightColor; }), this.inputs.push(o), s.appendChild(o); } return e.defaultValue && String(e.defaultValue).split("").forEach((o, c) => { c < this.inputs.length && (this.inputs[c].value = o); }), s; } updateValue(e) { const t = this.inputs.map((i) => i.value).join(""); e(t); } getValue(e) { return this.inputs.map((t) => t.value).join(""); } setValue(e, t) { const i = String(t || "").split(""); this.inputs.forEach((s, r) => { s.value = i[r] || ""; }); } } class O { constructor(e) { this.globalRenderers = /* @__PURE__ */ new Map(), this.fieldOverrides = /* @__PURE__ */ new Map(), this.config = { ...S, ...e }, this.registerDefaults(); } registerDefaults() { const e = new T(this.config), t = new L(this.config), i = new B(this.config); this.globalRenderers.set("text", e), this.globalRenderers.set("email", e), this.globalRenderers.set("password", e), this.globalRenderers.set("textarea", e), this.globalRenderers.set("signature", e), this.globalRenderers.set("checkbox", t), this.globalRenderers.set("radio", i), this.globalRenderers.set("dropdown", i), this.globalRenderers.set("listbox", i); } registerGlobalRenderer(e, t) { this.globalRenderers.set(e, t); } registerFieldOverride(e, t) { this.fieldOverrides.set(e, t); } getRenderer(e) { return this.fieldOverrides.has(e.key) ? this.fieldOverrides.get(e.key) : e.type === "text" && e.comb ? new W(this.config) : e.detectedType && this.globalRenderers.has(e.detectedType) ? this.globalRenderers.get(e.detectedType) : this.globalRenderers.has(e.type) ? this.globalRenderers.get(e.type) : (console.warn(`No renderer found for field "${e.key}" of type "${e.type}", using text renderer`), this.globalRenderers.get("text")); } } class A extends p { constructor() { super(...arguments), this.fieldValues = /* @__PURE__ */ new Map(), this.fieldMappings = /* @__PURE__ */ new Map(), this.fields = []; } setFields(e) { this.fields = e, this.fieldMappings.clear(), e.forEach((t) => { var s; const i = ((s = t.definition) == null ? void 0 : s.name) || t.key; this.fieldMappings.set(i, t.key), t.defaultValue !== void 0 && t.defaultValue !== null && this.fieldValues.set(t.key, t.defaultValue); }); } setValue(e, t, i = !0) { const s = this.fieldValues.get(e); this.fieldValues.set(e, t); const r = this.fields.find((c) => c.key === e); if (!r) { console.warn(`Field not found: ${e}`); return; } if (!i) return; const n = { ...r, value: t }, a = this.validateField(r, t), o = { field: n, validationState: a, oldValue: s, newValue: t, timestamp: Date.now() }; this.emit("fieldChange", o); } getValue(e) { return this.fieldValues.get(e); } getValues() { const e = {}; for (const [t, i] of this.fieldMappings.entries()) { const s = this.fieldValues.get(i); s !== void 0 && (e[t] = s); } return e; } getRawValues() { const e = {}; return this.fieldValues.forEach((t, i) => { e[i] = t; }), e; } setValues(e) { Object.entries(e).forEach(([t, i]) => { if (this.fieldMappings.has(t)) { const s = this.fieldMappings.get(t); this.setValue(s, i, !1); } else this.setValue(t, i, !1); }); } reset() { this.fields.forEach((e) => { e.defaultValue !== void 0 && e.defaultValue !== null ? this.setValue(e.key, e.defaultValue, !1) : this.setValue(e.key, null, !1); }); } getFieldByKey(e) { return this.fields.find((t) => t.key === e); } getFieldByName(e) { const t = this.fieldMappings.get(e); if (t) return this.getFieldByKey(t); } getFields() { return this.fields; } validateField(e, t) { if (!e.definition) return { valid: !0 }; if (e.definition.pattern && typeof t == "string") try { if (!new RegExp(e.definition.pattern).test(t)) return { valid: !1, error: e.definition.errorMessage || "Invalid format" }; } catch { console.error("Invalid regex pattern:", e.definition.pattern); } if (e.definition.validator) { const i = e.definition.validator(t); if (i) return { valid: !1, error: i }; } return e.definition.required && e.type === "text" && (!t || typeof t == "string" && !t.trim()) ? { valid: !1, error: "This field is required" } : { valid: !0 }; } } function v(l, e) { const t = document.createElement(l); return e && Object.entries(e).forEach(([i, s]) => { i === "style" && typeof s == "object" ? F(t, s) : i === "className" ? t.className = s : i === "textContent" ? t.textContent = s : i.startsWith("on") || t.setAttribute(i, s); }), t; } function F(l, e) { Object.entries(e).forEach(([t, i]) => { i !== void 0 && (l.style[t] = i); }); } function $(l) { const e = Math.floor(l * 0.6); return Math.min(Math.max(e, 8), 16); } class H { constructor(e, t) { this.registry = e, this.stateManager = t, this.container = null, this.scale = 1, this.currentPage = 1, this.fieldElements = /* @__PURE__ */ new Map(); } setContainer(e) { this.container = e; } setScale(e) { this.scale = e; } setCurrentPage(e) { this.currentPage = e, this.renderFields(); } renderFields() { if (!this.container) return; this.fieldElements.forEach((i) => i.remove()), this.fieldElements.clear(), this.stateManager.getFields().filter((i) => i.position.page === this.currentPage).forEach((i) => { const s = this.createFieldElement(i); s && this.container && (this.container.appendChild(s), this.fieldElements.set(i.key, s)); }); } createFieldElement(e) { if (!e.position) return null; const t = v("div", { className: "pdf-form-field", style: { position: "absolute", left: `${e.position.x * this.scale}px`, top: `${e.position.y * this.scale}px`, width: `${e.position.width * this.scale}px`, height: `${e.position.height * this.scale}px`, zIndex: "10" } }), i = this.registry.getRenderer(e), s = { width: e.position.width * this.scale, height: e.position.height * this.scale, fontSize: $(e.position.height * this.scale) }, r = i.createField( e, s, (a) => this.stateManager.setValue(e.key, a) ), n = this.stateManager.getValue(e.key); return n != null && i.setValue(r, n), t.appendChild(r), t; } updateFieldValues() { this.fieldElements.forEach((e, t) => { const i = this.stateManager.getFieldByKey(t); if (!i) return; const s = e.firstElementChild; if (!s) return; const r = this.registry.getRenderer(i), n = this.stateManager.getValue(t); n != null && r.setValue(s, n); }); } destroy() { this.fieldElements.forEach((e) => e.remove()), this.fieldElements.clear(); } } class U extends p { constructor(e) { var t; super(), this.options = e, this.pdfDoc = null, this.pdfArrayBuffer = null, this.currentPage = 1, this.totalPages = 0, this.scale = 1, this.renderTask = null, this.pageWrapper = null, this.canvas = null, this.fields = [], this.unparsedFields = [], this.pdfPageWidth = 0, this.pdfPageHeight = 0, e.workerSrc && !((t = d.GlobalWorkerOptions) != null && t.workerSrc) && (d.GlobalWorkerOptions.workerSrc = e.workerSrc), this.container = e.container, this.pdfUrl = e.pdfUrl, this.scale = e.scale || 1, this.currentPage = e.page || 1, this.discoveryService = new P(), this.rendererRegistry = new O({ highlightColor: e.highlightColor, highlightColorFocus: e.highlightColorFocus }), this.stateManager = new A(), this.overlayRenderer = new H(this.rendererRegistry, this.stateManager), e.fieldDefinitions && this.discoveryService.setFieldDefinitions(e.fieldDefinitions), e.fieldRenderers && Object.entries(e.fieldRenderers).forEach(([i, s]) => { this.rendererRegistry.registerGlobalRenderer(i, s); }), e.fieldOverrides && Object.entries(e.fieldOverrides).forEach(([i, s]) => { this.rendererRegistry.registerFieldOverride(i, s); }), this.stateManager.on("fieldChange", (i) => { this.emit("fieldChange", i.field, i.validationState); }); } async render() { try { F(this.container, { position: "relative", width: "100%", display: "flex", justifyContent: "center" }), await this.loadPDF(); } catch (e) { this.handleError(e); } } async loadPDF() { try { const e = await fetch(this.pdfUrl); this.pdfArrayBuffer = await e.arrayBuffer(), this.options.preprocessor && (this.pdfArrayBuffer = await this.options.preprocessor(this.pdfArrayBuffer)); const { fields: t, unparsedFields: i } = await this.discoveryService.discoverFields(this.pdfArrayBuffer); this.fields = t, this.unparsedFields = i, this.stateManager.setFields(t), this.emit("fieldsDiscovered", t), this.options.onFieldsDiscovered && this.options.onFieldsDiscovered(t, i); const s = d.getDocument(this.pdfArrayBuffer.slice(0)); this.pdfDoc = await s.promise, this.totalPages = this.pdfDoc.numPages, this.emit("ready", t), this.emit("pageCountChange", this.totalPages); const n = (await this.pdfDoc.getPage(1)).getViewport({ scale: 1 }); this.pdfPageWidth = n.width, this.pdfPageHeight = n.height, await this.renderPage(this.currentPage); } catch (e) { throw new Error(`Failed to load PDF: ${e}`); } } calculateScale() { const e = this.container.clientWidth, t = Math.min(e, 900); this.scale = Math.min(t / this.pdfPageWidth, 1.5); } cancelRender() { this.renderTask && (this.renderTask.cancel(), this.renderTask = null); } async renderPage(e) { if (!this.pdfDoc) return; this.cancelRender(); const t = await this.pdfDoc.getPage(e); this.calculateScale(); const i = t.getViewport({ scale: this.scale }); this.pageWrapper || (this.pageWrapper = v("div", { style: { position: "relative", background: "white", boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)", margin: "0 auto" } }), this.container.appendChild(this.pageWrapper)), F(this.pageWrapper, { width: `${i.width}px`, height: `${i.height}px` }), this.canvas || (this.canvas = v("canvas", { style: { display: "block" } }), this.pageWrapper.appendChild(this.canvas)); const s = this.canvas.getContext("2d"), r = window.devicePixelRatio || 1; this.canvas.width = Math.floor(i.width * r), this.canvas.height = Math.floor(i.height * r), this.canvas.style.width = `${Math.floor(i.width)}px`, this.canvas.style.height = `${Math.floor(i.height)}px`; const a = { canvasContext: s, viewport: i, transform: r !== 1 ? [r, 0, 0, r, 0, 0] : void 0, intent: "display" }; this.renderTask = t.render(a); try { await this.renderTask.promise, this.overlayRenderer.setContainer(this.pageWrapper), this.overlayRenderer.setScale(this.scale), this.overlayRenderer.setCurrentPage(e), this.overlayRenderer.renderFields(), this.emit("pageChange", e); } catch { } } handleError(e) { console.error("PDFFormRenderer Error:", e), this.emit("error", e); } // Public API setPage(e) { e < 1 || e > this.totalPages || (this.currentPage = e, this.renderPage(e)); } getCurrentPage() { return this.currentPage; } getTotalPages() { return this.totalPages; } getFieldValues() { return this.stateManager.getValues(); } getRawFieldValues() { return this.stateManager.getRawValues(); } setFieldValues(e) { this.stateManager.setValues(e), this.overlayRenderer.updateFieldValues(); } getUnparsedFields() { return [...this.unparsedFields]; } getFieldStates() { const e = {}; return this.fields.forEach((t) => { var n; const i = this.stateManager.getValue(t.key), s = this.stateManager.validateField(t, i), r = ((n = t.definition) == null ? void 0 : n.name) || t.key; e[r] = s; }), e; } destroy() { this.cancelRender(), this.overlayRenderer.destroy(), this.stateManager.removeAllListeners(), this.removeAllListeners(), this.pdfDoc && (this.pdfDoc.destroy(), this.pdfDoc = null), this.container.innerHTML = ""; } } class z { async fillFromArrayBuffer(e, t) { const i = await y.load(e); return this.fillDocument(i, t); } async fillFromBytes(e, t) { const i = await y.load(e); return this.fillDocument(i, t); } async fillFromUrl(e, t) { const s = await (await fetch(e)).arrayBuffer(); return this.fillFromArrayBuffer(s, t); } async fillDocument(e, t) { const i = e.getForm(); return Object.entries(t).forEach(([s, r]) => { try { const n = i.getField(s); if (n.constructor.name.includes("PDFTextField")) n.setText(String(r || "")); else if (n.constructor.name.includes("PDFCheckBox")) { const a = n; r ? a.check() : a.uncheck(); } else n.constructor.name.includes("PDFRadioGroup") ? r && n.select(r) : n.constructor.name.includes("PDFDropdown") && r && n.select(r); } catch (n) { console.error(`Error setting field ${s}:`, n); } }), e.save(); } } class q extends p { constructor(e, t) { super(), this.clickZones = /* @__PURE__ */ new Map(), this.container = e, this.mappingStateManager = t; } renderZones(e, t, i) { this.clearZones(), e.filter((r) => r.position.page === t).forEach((r) => { const n = this.createClickZone(r, i); n && (this.container.appendChild(n), this.clickZones.set(r.key, n)); }); } createClickZone(e, t) { const i = document.createElement("div"); i.className = "pdfform-field-zone"; const s = this.mappingStateManager.getFieldDefinition(e.key), r = s !== void 0; if (i.classList.add(r ? "mapped" : "unmapped"), i.style.position = "absolute", i.style.left = `${e.position.x * t}px`, i.style.top = `${e.position.y * t}px`, i.style.width = `${e.position.width * t}px`, i.style.height = `${e.position.height * t}px`, i.style.zIndex = "100", i.title = r ? `${s.name} (${e.key})` : `Unmapped: ${e.key}`, r && s.name) { const n = document.createElement("span"); n.className = "pdfform-field-label", n.textContent = s.name, i.appendChild(n); } return i.addEventListener("click", (n) => { n.stopPropagation(), this.emit("fieldClicked", e); }), i; } updateFieldStates() { this.clickZones.forEach((e, t) => { const i = this.mappingStateManager.getFieldDefinition(t), s = i !== void 0; e.classList.remove("mapped", "unmapped"), e.classList.add(s ? "mapped" : "unmapped"), e.title = s ? `${i.name} (${t})` : `Unmapped: ${t}`; const r = e.querySelector(".pdfform-field-label"); if (r && r.remove(), s && i.name) { const n = document.createElement("span"); n.className = "pdfform-field-label", n.textContent = i.name, e.appendChild(n); } }); } clearZones() { this.clickZones.forEach((e) => e.remove()), this.clickZones.clear(); } destroy() { this.clearZones(); } } class I extends p { constructor() { super(...arguments), this.fieldDefinitions = /* @__PURE__ */ new Map(), this.fields = []; } setFields(e) { this.fields = e, this.emitStats(); } setFieldDefinition(e, t) { for (const [i, s] of this.fieldDefinitions.entries()) if (i !== e && s.name === t.name) throw new Error(`Field name "${t.name}" is already used by another field`); this.fieldDefinitions.set(e, t), this.emitStats(e, t); } getFieldDefinition(e) { return this.fieldDefinitions.get(e); } removeFieldDefinition(e) { this.fieldDefinitions.delete(e), this.emitStats(e); } getAllDefinitions() { return Array.from(this.fieldDefinitions.values()); } getMappingStats() { return { total: this.fields.length, mapped: this.fieldDefinitions.size }; } clearMappings() { this.fieldDefinitions.clear(), this.emitStats(); } importDefinitions(e) { const t = /* @__PURE__ */ new Set(); e.forEach((i) => { if (i.name) { if (t.has(i.name)) throw new Error(`Duplicate field name "${i.name}" in imported definitions`); t.add(i.name); } }), this.fieldDefinitions.clear(), e.forEach((i) => { i.key && i.name && this.fieldDefinitions.set(i.key, i); }), this.emitStats(); } emitStats(e, t) { const i = { total: this.fields.length, mapped: this.fieldDefinitions.size, fieldKey: e, definition: t }; this.emit("mappingChanged", i); } } class Z { constructor() { this.modal = null, this.form = null, this.currentField = null, this.onSave = null, this.onRemove = null, this.createModal(); } createModal() { this.modal = document.createElement("div"), this.modal.className = "pdfform-modal", this.modal.innerHTML = ` <div class="pdfform-modal-content"> <div class="pdfform-modal-header"> <h2>Define Field Properties</h2> <button type="button" class="pdfform-modal-close">&times;</button> </div> <form class="pdfform-modal-form"> <div class="pdfform-form-group"> <label>PDF Field Key:</label> <input type="text" name="key" readonly class="pdfform-input-readonly"> </div> <div class="pdfform-form-group"> <label>Property Name <span class="pdfform-required">*</span>:</label> <input type="text" name="name" required placeholder="e.g., email, firstName" class="pdfform-input"> <small>Required - this is the key used in form data (e.g., formData.email)</small> <div class="pdfform-error" data-field="name"></div> </div> <div class="pdfform-form-group"> <label>Display Label:</label> <input type="text" name="title" placeholder="e.g., Email Address, First Name" class="pdfform-input"> <small>User-friendly label shown in tooltips</small> </div> <div class="pdfform-form-group"> <label>Placeholder:</label> <input type="text" name="placeholder" placeholder="e.g., Enter your email" class="pdfform-input"> </div> <div class="pdfform-form-group"> <label class="pdfform-checkbox-label"> <input type="checkbox" name="required"> Required Field </label> </div> <div class="pdfform-form-group"> <label>Validation Pattern (regex):</label> <input type="text" name="pattern" placeholder="e.g., ^[^@]+@[^@]+\\.[^@]+$" class="pdfform-input"> </div> <div class="pdfform-form-group"> <label>Error Message:</label> <input type="text" name="errorMessage" placeholder="e.g., Please enter a valid email" class="pdfform-input"> </div> <div class="pdfform-form-actions"> <button type="button" class="pdfform-btn pdfform-btn-danger pdfform-remove-btn" style="display: none;"> Remove Mapping </button> <button type="button" class="pdfform-btn pdfform-cancel-btn">Cancel</button> <button type="submit" class="pdfform-btn pdfform-btn-primary">Save Field Definition</button> </div> </form> </div> `, document.body.appendChild(this.modal), this.form = this.modal.querySelector(".pdfform-modal-form"), this.setupEventListeners(); } setupEventListeners() { this.modal.querySelector(".pdfform-modal-close").addEventListener("click", () => this.hide()), this.modal.querySelector(".pdfform-cancel-btn").addEventListener("click", () => this.hide()), this.modal.querySelector(".pdfform-remove-btn").addEventListener("click", () => { this.currentField && this.onRemove && (this.onRemove(this.currentField), this.hide()); }), this.form.addEventListener("submit", (r) => { r.preventDefault(), this.handleSubmit(); }), this.modal.addEventListener("click", (r) => { r.target === this.modal && this.hide(); }); const s = this.form.querySelector('input[name="name"]'); s.addEventListener("input", () => this.validateName(s.value)); } validateName(e) { const t = this.form.querySelector('[data-field="name"]'); return e.trim() ? (t.style.display = "none", !0) : (t.textContent = "Name is required", t.style.display = "block", !1); } handleSubmit() { var i, s, r, n; const e = new FormData(this.form), t = e.get("name"); if (this.validateName(t) && this.currentField && this.onSave) { const a = { key: this.currentField.key, name: t.trim(), title: ((i = e.get("title")) == null ? void 0 : i.trim()) || t.trim(), placeholder: (s = e.get("placeholder")) == null ? void 0 : s.trim(), required: e.get("required") === "on", pattern: ((r = e.get("pattern")) == null ? void 0 : r.trim()) || void 0, errorMessage: ((n = e.get("errorMessage")) == null ? void 0 : n.trim()) || void 0 }; this.onSave(this.currentField, a), this.hide(); } } show(e, t) { this.currentField = e, this.form.reset(); const i = this.form.querySelector('input[name="key"]'); i.value = e.key; const s = this.form.querySelector(".pdfform-remove-btn"); if (s.style.display = t ? "block" : "none", t) { const n = { name: t.name, title: t.title || "", placeholder: t.placeholder || "", pattern: t.pattern || "", errorMessage: t.errorMessage || "" }; Object.entries(n).forEach(([o, c]) => { const u = this.form.querySelector(`input[name="${o}"]`); u && (u.value = c); }); const a = this.form.querySelector('input[name="required"]'); a.checked = t.required || !1; } this.modal.style.display = "block", this.form.querySelector('input[name="name"]').focus(); } hide() { this.modal.style.display = "none", this.currentField = null; } onFieldSave(e) { this.onSave = e; } onFieldRemove(e) { this.onRemove = e; } destroy() { this.modal && (this.modal.remove(), this.modal = null, this.form = null); } } class G extends p { constructor(e) { var t; super(), this.pdfDoc = null, this.currentPage = 1, this.pageWrapper = null, this.canvas = null, this.scale = 1, this.fields = [], this.renderTask = null, e.workerSrc && !((t = d.GlobalWorkerOptions) != null && t.workerSrc) && (d.GlobalWorkerOptions.workerSrc = e.workerSrc), this.container = e.container, this.pdfUrl = e.pdfUrl, this.discoveryService = new P(), this.mappingStateManager = new I(), this.modalManager = new Z(), e.fieldDefinitions && this.mappingStateManager.importDefinitions(e.fieldDefinitions), this.clickZoneRenderer = null, this.setupEventHandlers(); } setupEventHandlers() { this.mappingStateManager.on("mappingChanged", (e) => { this.clickZoneRenderer && this.clickZoneRenderer.updateFieldStates(), this.emit("mappingChanged", e); }), this.modalManager.onFieldSave((e, t) => { this.mappingStateManager.setFieldDefinition(e.key, t); }), this.modalManager.onFieldRemove((e) => { this.mappingStateManager.removeFieldDefinition(e.key); }); } async render() { try { this.container.innerHTML = "", this.container.style.position = "relative"; const e = await this.loadPDF(), { fields: t, unparsedFields: i } = await this.discoveryService.discoverFields(e); this.fields = t, this.mappingStateManager.setFields(t), this.pdfDoc = await d.getDocument({ data: e }).promise, await this.renderPage(), this.emit("ready", this.fields), this.emit("mappingChanged", this.mappingStateManager.getMappingStats()), i.length > 0 && console.warn("Unparsed fields:", i); } catch (e) { throw console.error("Failed to render PDF:", e), this.emit("error", e), e; } } async loadPDF() { const e = await fetch(this.pdfUrl); if (!e.ok) throw new Error(`Failed to load PDF: ${e.statusText}`); return e.arrayBuffer(); } async renderPage() { if (!this.pdfDoc) return; this.renderTask && (this.renderTask.cancel(), this.renderTask = null); const e = await this.pdfDoc.getPage(this.currentPage), t = e.getViewport({ scale: 1 }), i = this.container.clientWidth - 40; this.scale = i / t.width; const s = e.getViewport({ scale: this.scale }); this.pageWrapper || (this.pageWrapper = document.createElement("div"), this.pageWrapper.style.position = "relative", this.pageWrapper.style.background = "white", this.pageWrapper.style.boxShadow = "0 2px 8px rgba(0, 0, 0, 0.1)", this.pageWrapper.style.margin = "0 auto", this.container.appendChild(this.pageWrapper), this.clickZoneRenderer = new q(this.pageWrapper, this.mappingStateManager), this.clickZoneRenderer.on("fieldClicked", (a) => { const o = this.mappingStateManager.getFieldDefinition(a.key); this.modalManager.show(a, o); })), this.pageWrapper.style.width = `${s.width}px`, this.pageWrapper.style.height = `${s.height}px`, this.canvas || (this.canvas = document.createElement("canvas"), this.canvas.style.display = "block", this.pageWrapper.appendChild(this.canvas)), this.canvas.width = s.width, this.canvas.height = s.height, this.canvas.style.width = `${s.width}px`, this.canvas.style.height = `${s.height}px`; const r = this.canvas.getContext("2d"); if (!r) return; const n = { canvasContext: r, viewport: s }; this.renderTask = e.render(n), await this.renderTask.promise, this.clickZoneRenderer.clearZones(), this.clickZoneRenderer.renderZones(this.fields, this.currentPage, this.scale); } async setPage(e) { if (!this.pdfDoc) return; const t = this.pdfDoc.numPages; e < 1 || e > t || (this.currentPage = e, await this.renderPage()); } getPageCount() { var e; return ((e = this.pdfDoc) == null ? void 0 : e.numPages) || 0; } getCurrentPage() { return this.currentPage; } setFieldDefinition(e, t) { if (!t.name) throw new Error("Field name is required"); const i = { key: e, name: t.name, title: t.title, placeholder: t.placeholder, required: t.required, pattern: t.pattern, errorMessage: t.errorMessage }; this.mappingStateManager.setFieldDefinition(e, i); } removeFieldDefinition(e) { this.mappingStateManager.removeFieldDefinition(e); } getFieldDefinitions() { return this.mappingStateManager.getAllDefinitions(); } exportDefinitions() { const e = this.getFieldDefinitions(); return JSON.stringify(e, null, 2); } importDefinitions(e) { this.mappingStateManager.importDefinitions(e), this.clickZoneRenderer.updateFieldStates(); } getUnmappedFields() { return this.fields.filter((e) => !this.mappingStateManager.getFieldDefinition(e.key)); } clearMappings() { this.mappingStateManager.clearMappings(), this.clickZoneRenderer.updateFieldStates(); } destroy() { this.renderTask && this.renderTask.cancel(), this.clickZoneRenderer && this.clickZoneRenderer.destroy(), this.modalManager && this.modalManager.destroy(), this.container.innerHTML = "", this.pageWrapper = null, this.canvas = null; } } class j { constructor(e) { var t; this.options = e, this.pdfDoc = null, this.pages = [], this.renderedPages = /* @__PURE__ */ new Map(), this.container = e.container, this.pdfUrl = e.pdfUrl, this.scale = e.scale || 1, e.workerSrc && !((t = d.GlobalWorkerOptions) != null && t.workerSrc) && (d.GlobalWorkerOptions.workerSrc = e.workerSrc), this.container.innerHTML = "", this.loadPDF(); } async loadPDF() { try { const e = d.getDocument(this.pdfUrl); this.pdfDoc = await e.promise, this.pages = []; for (let t = 1; t <= this.pdfDoc.numPages; t++) { const s = (await this.pdfDoc.getPage(t)).getViewport({ scale: this.scale }); this.pages.push({ pageNumber: t, width: s.width, height: s.height, scale: this.scale, rotation: s.rotation, viewBox: [0, 0, s.width, s.height] }); } this.options.onPagesLoaded && this.options.onPagesLoaded(this.pages); } catch (e) { throw console.error("Error loading PDF:", e), e; } } async renderPage(e) { if (!this.pdfDoc) throw new Error("PDF document not loaded"); const t = this.renderedPages.get(e); if (t) return t; const i = await this.pdfDoc.getPage(e), s = i.getViewport({ scale: this.scale }), r = document.createElement("canvas"), n = r.getContext("2d"); if (!n) throw new Error("Failed to get canvas context"); r.width = s.width, r.height = s.height,