`; } function highlightSelection(){ overlay.querySelectorAll(".el-box").forEach(b=>b.classList.remove("selected")); const box = overlay.querySelector(`.el-box[data-id="${selectedElId}"]`); if(!box) return; box.classList.add("selected"); ["nw","ne","sw","se"].forEach(pos=>{ const h = document.createElement("div"); h.className = "handle "+pos; box.appendChild(h); }); renderPropsPanel(); } function getEl(id){ return curPageData().elements.find(e=>e.id===id); } let dragState = null; function onDragMove(e){ if(!dragState) return; const el = getEl(dragState.id); el.x = dragState.origX + screenToPdf(e.clientX - dragState.startX); el.y = dragState.origY + screenToPdf(e.clientY - dragState.startY); renderOverlay(); } function onDragEnd(){ dragState = null; window.removeEventListener("mousemove", onDragMove); window.removeEventListener("mouseup", onDragEnd); } function deleteSelected(){ if(selectedElId){ curPageData().elements = curPageData().elements.filter(e=>e.id!==selectedElId); deselect(); renderOverlay(); } } $("#deleteSelBtn").addEventListener("click", deleteSelected); window.addEventListener("keydown",(e)=>{ if((e.key==="Delete"||e.key==="Backspace") && selectedElId && !(document.activeElement && document.activeElement.isContentEditable)) deleteSelected(); }); function enterTextEdit(id){ selectElement(id); const t = overlay.querySelector(`.el-box[data-id="${id}"] .txt`); if(!t) return; t.contentEditable = "true"; t.focus(); document.execCommand("selectAll", false, null); const finish = ()=>{ t.contentEditable="false"; getEl(id).text = t.textContent; t.removeEventListener("blur", finish); }; t.addEventListener("blur", finish); } function renderPropsPanel(){ const el = getEl(selectedElId); const body = $("#propsBody"); if(!el) return; if(el.type === "text"){ body.innerHTML = `
`; $("#p_color").oninput = e=>{ el.color=e.target.value; renderOverlay(); }; $("#p_size").oninput = e=>{ el.fontSize=parseFloat(e.target.value)||16; renderOverlay(); }; } else if(el.type === "icon" || el.type === "shape" || el.type === "rect"){ body.innerHTML = `
`; $("#p_color").oninput = e=>{ el.color=e.target.value; renderOverlay(); }; $("#p_w").oninput = e=>{ const r=el.height/el.width; el.width=parseFloat(e.target.value)||el.width; el.height=el.width*r; renderOverlay(); }; } else if(el.type === "image"){ body.innerHTML = `
`; $("#p_w").oninput = e=>{ const r=el.height/el.width; el.width=parseFloat(e.target.value)||el.width; el.height=el.width*r; renderOverlay(); }; } } function renderLayersList(){ const list = $("#layersList"); const els = curPageData() ? curPageData().elements : []; list.innerHTML = els.length ? "" : `
No elements added.
`; els.slice().reverse().forEach(el=>{ const item = document.createElement("div"); item.className = "layer-item" + (el.id===selectedElId ? " selected":""); item.innerHTML = `
${el.type}✕`; item.addEventListener("click",(e)=>{ if(e.target.classList.contains("del")){ curPageData().elements=curPageData().elements.filter(x=>x.id!==el.id); renderOverlay(); deselect(); } else selectElement(el.id); }); list.appendChild(item); }); } // ---------------------------------------------------- // TRUE HD EXPORT - Loads original PDF bytes rather than a screenshot // ---------------------------------------------------- $("#downloadBtn").addEventListener("click", async () => { if(!pdfDoc || !originalBytes) return; showLoading("Generating HD PDF…"); try{ const { PDFDocument, rgb, degrees, StandardFonts } = PDFLib; // Load the ORIGINAL PDF directly to preserve all vectors/text perfectly const outDoc = await PDFDocument.load(originalBytes); const font = await outDoc.embedFont(StandardFonts.Helvetica); const scaleFactor = parseFloat($("#exportScale").value) || 1; for(let p=1; p<=numPages; p++){ const page = outDoc.getPage(p - 1); // Handle Scaling if requested if(scaleFactor !== 1) { const { width, height } = page.getSize(); page.scale(scaleFactor, scaleFactor); } const elements = pagesData[p-1] ? pagesData[p-1].elements : []; const size = pagesSize[p-1]; for(const el of elements){ // Adjust for scaling factor const x = el.x * scaleFactor; const w = el.width * scaleFactor; const h = el.height * scaleFactor; const yTop = (size.height - el.y) * scaleFactor; if(el.type === "rect"){ const c = hexToRgb(el.color); page.drawRectangle({ x, y: yTop - h, width: w, height: h, color: rgb(c.r/255,c.g/255,c.b/255), opacity: el.opacity }); } else if(el.type === "text"){ const c = hexToRgb(el.color); page.drawText(el.text, { x, y: yTop - (el.fontSize * scaleFactor), size: el.fontSize * scaleFactor, font, color: rgb(c.r/255,c.g/255,c.b/255) }); } else if(el.type === "image" || el.type === "icon" || el.type === "shape"){ let srcDataUrl = (el.type === "icon" || el.type === "shape") ? await iconToDataUrl(el) : el.dataUrl; const bytes = Uint8Array.from(atob(srcDataUrl.split(",")[1]), c => c.charCodeAt(0)); const embedded = srcDataUrl.startsWith("data:image/png") ? await outDoc.embedPng(bytes) : await outDoc.embedJpg(bytes); page.drawImage(embedded, { x, y: yTop - h, width: w, height: h, opacity: el.opacity }); } } } const bytes = await outDoc.save(); const blob = new Blob([bytes], { type:"application/pdf" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = fileBaseName + "-HD.pdf"; document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(url), 4000); toast("Saved HD PDF!"); } catch(err){ console.error(err); toast("Export failed."); } finally{ hideLoading(); } }); function hexToRgb(hex){ const m = hex.replace("#",""); return { r: parseInt(m.substr(0,2),16), g: parseInt(m.substr(2,2),16), b: parseInt(m.substr(4,2),16) }; } function iconToDataUrl(el){ return new Promise((resolve)=>{ const size = 256; const dict = el.type === "shape" ? SHAPES : ICONS; const svgStr = `
`; const img = new Image(); img.onload = ()=>{ const c = document.createElement("canvas"); c.width=size; c.height=size; const ctx = c.getContext("2d"); ctx.drawImage(img,0,0,size,size); resolve(c.toDataURL("image/png")); }; img.src = URL.createObjectURL(new Blob([svgStr], {type:"image/svg+xml"})); }); } })();