PDFIX / addwatermark.html
Talha912's picture
Create addwatermark.html
804f335 verified
Raw History Blame Contribute Delete
37.3 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#111827">
<title>PDFix — Add Watermark to PDF</title>
<meta name="description" content="Add text or image watermarks to PDF pages with precise positioning, opacity, rotation and page-range controls.">
<style>
:root{--bg:#f4f7fb;--surface:#fff;--ink:#182230;--muted:#667085;--line:#e5eaf2;--brand:#5b5ce2;--brand-soft:#eeeeff;--green:#12805c;--shadow:0 18px 50px rgba(20,31,56,.08);--radius:22px}
*{box-sizing:border-box}body{margin:0;background:radial-gradient(circle at 10% 0%,#ededff 0,transparent 28%),radial-gradient(circle at 100% 15%,#e4f8f3 0,transparent 24%),var(--bg);color:var(--ink);font:15px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}button,input,select,textarea{font:inherit}button{cursor:pointer}a{text-decoration:none;color:inherit}
.topbar{height:76px;display:flex;align-items:center;justify-content:space-between;padding:0 clamp(18px,5vw,72px);background:rgba(255,255,255,.84);border-bottom:1px solid rgba(229,234,242,.9);backdrop-filter:blur(18px);position:sticky;top:0;z-index:20}.brand{display:flex;align-items:center;gap:11px;font-weight:850;letter-spacing:-.7px;font-size:22px}.logo{width:39px;height:39px;border-radius:13px;background:linear-gradient(135deg,#696aff,#4946cb);display:grid;place-items:center;color:white;box-shadow:0 8px 18px #5b5ce244}.logo svg{width:23px}.brand em{font-style:normal;color:var(--brand)}.top-actions{display:flex;align-items:center;gap:10px}.secure{color:var(--muted);font-size:12px;display:flex;align-items:center;gap:7px}.secure span{width:8px;height:8px;background:#18a874;border-radius:50%}.nav-btn{border:1px solid var(--line);background:#fff;border-radius:12px;padding:10px 14px;font-weight:700}
.wrap{max-width:1440px;margin:auto;padding:38px clamp(16px,4vw,58px) 64px}.crumb{font-size:13px;color:var(--muted);margin-bottom:22px}.crumb b{color:var(--brand)}.hero{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;margin-bottom:28px}.eyebrow{display:inline-flex;align-items:center;gap:8px;background:var(--brand-soft);color:var(--brand);font-size:11px;font-weight:850;letter-spacing:1.3px;text-transform:uppercase;padding:7px 11px;border-radius:99px}h1{font-size:clamp(31px,4vw,48px);letter-spacing:-1.8px;line-height:1.08;margin:15px 0 12px}.hero p{max-width:700px;color:var(--muted);font-size:16px;margin:0}.hero-badge{background:#fff;border:1px solid var(--line);border-radius:17px;padding:14px 17px;display:flex;align-items:center;gap:12px;box-shadow:0 7px 20px #26324a08;white-space:nowrap}.shield{width:38px;height:38px;background:#e8faf2;color:#16805d;border-radius:12px;display:grid;place-items:center;font-size:20px}.hero-badge strong{display:block;font-size:13px}.hero-badge small{display:block;color:var(--muted);font-size:11px}
.layout{display:grid;grid-template-columns:minmax(0,1fr) 355px;gap:22px;align-items:start}.maincol{min-width:0}.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}.upload{padding:25px;margin-bottom:20px}.section-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:17px}.section-title{font-size:17px;font-weight:800;letter-spacing:-.35px}.step{display:inline-grid;place-items:center;width:27px;height:27px;border-radius:9px;background:var(--brand-soft);color:var(--brand);font-weight:850;font-size:12px;margin-right:9px}
.dropzone{border:1.5px dashed #c7c9f8;border-radius:19px;background:linear-gradient(135deg,#f9f9ff,#f6fbff);padding:31px 20px;text-align:center;transition:.2s;position:relative}.dropzone.drag{border-color:var(--brand);background:#efefff}.upload-icon{width:61px;height:61px;margin:0 auto 13px;border-radius:19px;background:#e8e9ff;color:var(--brand);display:grid;place-items:center}.upload-icon svg{width:31px;height:31px}.dropzone h2{font-size:18px;margin:0 0 6px}.dropzone p{font-size:13px;color:var(--muted);margin:0 0 17px}.primary,.secondary,.danger{border:0;border-radius:12px;padding:11px 16px;font-weight:800;transition:.15s}.primary{color:#fff;background:linear-gradient(135deg,var(--brand),#4948c9);box-shadow:0 8px 17px #5b5ce22e}.secondary{background:#fff;border:1px solid var(--line);color:#344054}.danger{background:#fff0f0;color:#b42318;border:1px solid #ffdddd}.primary:hover,.secondary:hover,.danger:hover{transform:translateY(-1px)}.micro{color:var(--muted);font-size:11px;margin-top:13px}.file-input{display:none}
.file-summary{display:none;margin-top:17px;padding:13px 14px;background:#f8fafc;border:1px solid var(--line);border-radius:14px;align-items:center;justify-content:space-between;gap:10px}.file-summary.show{display:flex}.file-meta{display:flex;align-items:center;gap:11px;min-width:0}.pdf-icon{width:40px;height:44px;background:#fff0f0;border:1px solid #ffd5d5;border-radius:10px;color:#d92d20;display:grid;place-items:center;font-size:11px;font-weight:900;flex-shrink:0}.file-name{font-weight:800;overflow-wrap:anywhere;font-size:13px}.file-sub{font-size:11px;color:var(--muted)}.status-pill{font-size:11px;font-weight:800;background:#e8faf2;color:#137c59;padding:6px 9px;border-radius:99px;white-space:nowrap}
.workspace{padding:24px}.count{font-size:12px;color:var(--muted);background:#f2f4f7;padding:6px 9px;border-radius:99px;font-weight:750}.empty{padding:40px 15px;text-align:center;color:var(--muted);border:1px dashed var(--line);border-radius:16px;background:#fbfcfe}.empty .empty-icon{font-size:31px;margin-bottom:8px}.empty strong{display:block;color:#344054;margin-bottom:3px}.empty span{font-size:12px}.preview-shell{display:grid;grid-template-columns:1fr;gap:13px}.preview-toolbar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:13px}.preview-toolbar button,.preview-toolbar select{font-size:11px;padding:8px 10px}.preview-area{min-height:480px;background:linear-gradient(135deg,#f0f2f6,#e9edf5);border:1px solid #e1e6ef;border-radius:16px;display:grid;place-items:center;padding:22px;overflow:hidden;position:relative}.paper{width:min(100%,520px);aspect-ratio:8.5/11;background:white;box-shadow:0 12px 35px #24324c20;position:relative;overflow:hidden;display:grid;place-items:center;transition:transform .2s}.paper canvas{width:100%;height:100%;object-fit:contain;display:block}.wm-preview{position:absolute;pointer-events:none;white-space:pre-wrap;text-align:center;max-width:90%;line-height:1.15;transform-origin:center center;overflow-wrap:anywhere;user-select:none}.preview-foot{display:flex;justify-content:space-between;gap:10px;color:var(--muted);font-size:11px;margin-top:10px}.notice{display:none;padding:12px 14px;margin-top:14px;border-radius:12px;font-size:12px}.notice.show{display:block}.notice.error{background:#fff0f0;color:#b42318;border:1px solid #ffd9d9}.notice.success{background:#eafaf2;color:#137c59;border:1px solid #c9f0df}
.side{display:grid;gap:18px}.settings{padding:23px;position:sticky;top:96px}.settings h2{font-size:17px;margin:0 0 4px}.settings-intro{font-size:12px;color:var(--muted);margin:0 0 20px}.field{margin:0 0 16px}.field label,.field-label{display:flex;justify-content:space-between;align-items:center;font-size:12px;font-weight:800;color:#344054;margin-bottom:7px}.field small{font-size:10px;color:var(--muted);font-weight:500}.field select,.field input[type=text],.field input[type=number],.field textarea{width:100%;padding:10px 11px;border:1px solid #dfe4ec;border-radius:10px;background:#fff;color:var(--ink);outline:none}.field textarea{resize:vertical;min-height:72px}.field select:focus,.field input:focus,.field textarea:focus{border-color:#9b9cff;box-shadow:0 0 0 3px #5b5ce21c}.range-row{display:flex;align-items:center;gap:10px}.range-row input{width:100%;accent-color:var(--brand)}.range-value{font-size:12px;font-weight:850;min-width:42px;text-align:right}.segmented{display:grid;grid-template-columns:repeat(2,1fr);gap:5px;padding:4px;background:#f2f4f7;border-radius:12px}.segmented button{border:0;background:transparent;border-radius:9px;padding:8px 5px;font-size:12px;font-weight:800;color:#667085}.segmented button.active{background:#fff;color:var(--brand);box-shadow:0 2px 5px #1d29390c}.color-row{display:flex;align-items:center;gap:10px}.color-row input[type=color]{width:43px;height:39px;padding:3px;border:1px solid var(--line);border-radius:10px;background:white}.color-row select{flex:1}.advanced-toggle{width:100%;border:0;background:#f7f8ff;color:var(--brand);border-radius:11px;padding:10px;font-weight:800;font-size:12px;display:flex;justify-content:space-between;align-items:center}.advanced{display:none;padding-top:16px}.advanced.open{display:block}.check-row{display:flex;align-items:flex-start;gap:9px;font-size:12px;color:#475467;margin:12px 0}.check-row input{accent-color:var(--brand);margin-top:3px}.check-row strong{display:block;color:#344054;font-size:12px}.hint{font-size:10px;color:var(--muted);margin:5px 0 0}.apply-btn{width:100%;padding:14px 12px;margin-top:5px}.apply-btn:disabled{opacity:.45;cursor:not-allowed;transform:none;box-shadow:none}.progress-wrap{display:none;margin-top:15px}.progress-wrap.show{display:block}.progress-top{display:flex;justify-content:space-between;font-size:11px;font-weight:800;color:#475467;margin-bottom:7px}.progress{height:7px;background:#eaecf0;border-radius:99px;overflow:hidden}.bar{height:100%;width:0;background:linear-gradient(90deg,#5b5ce2,#31b78a);transition:width .2s}.privacy{display:flex;gap:10px;padding:15px;background:#edfaf4;border:1px solid #d2f1e3;border-radius:15px;color:#137b59;font-size:11px}.privacy strong{display:block;font-size:12px;margin-bottom:2px}.privacy p{margin:0;color:#347b64}.result-card{display:none;padding:20px;margin-top:20px}.result-card.show{display:block}.result-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}.result-head h3{margin:0;font-size:17px}.result-head p{font-size:12px;color:var(--muted);margin:4px 0 0}.footer-note{font-size:11px;color:var(--muted);line-height:1.5;margin-top:12px}footer{margin-top:36px;text-align:center;color:#98a2b3;font-size:11px}footer b{color:#667085}
@media(max-width:1020px){.layout{grid-template-columns:1fr}.settings{position:static}.hero-badge{display:none}}@media(max-width:600px){.topbar{height:65px}.secure{display:none}.wrap{padding-top:25px}.upload,.workspace,.settings{padding:18px}.dropzone{padding:28px 14px}.preview-area{min-height:340px;padding:12px}.brand{font-size:20px}.hero{display:block}}
</style></head>
<body>
<header class="topbar"><a class="brand" href="index.html"><span class="logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M7 3h7l5 5v13H7z"/><path d="M14 3v6h5M4 7v14h11M10 13h5M10 17h5"/></svg></span><span>PDF<em>ix</em></span></a><div class="top-actions"><div class="secure"><span></span> Local processing in your browser</div><a class="nav-btn" href="index.html">← All tools</a></div></header>
<main class="wrap">
<div class="crumb"><a href="index.html">Home</a> &nbsp;›&nbsp; PDF tools &nbsp;›&nbsp; <b>Add Watermark</b></div>
<section class="hero"><div><span class="eyebrow">✦ DOCUMENT BRANDING</span><h1>Add a <span style="color:var(--brand)">Watermark.</span></h1><p>Protect, label and brand your PDF with custom text or an image watermark. Fine-tune opacity, angle, position, scale and page ranges before exporting.</p></div><div class="hero-badge"><div class="shield">✓</div><div><strong>Private by design</strong><small>Your PDF stays on your device</small></div></div></section>
<div class="layout"><section class="maincol">
<div class="card upload"><div class="section-head"><div class="section-title"><span class="step">1</span>Upload your PDF</div><button class="secondary" id="clearBtn" disabled style="font-size:12px;padding:8px 11px">Clear file</button></div>
<div class="dropzone" id="dropzone" tabindex="0" role="button" aria-label="Choose a PDF or drop it here"><div class="upload-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M12 16V4m0 0L7.5 8.5M12 4l4.5 4.5"/><path d="M5 14v5a1.5 1.5 0 001.5 1.5h11A1.5 1.5 0 0019 19v-5"/></svg></div><h2>Drop your PDF here</h2><p>or choose a file from your device</p><button class="primary" id="chooseBtn" type="button">Choose PDF file</button><div class="micro">PDF only · <b>No server upload</b> · Password-protected files may not open</div></div>
<input class="file-input" id="fileInput" type="file" accept="application/pdf,.pdf"><div class="file-summary" id="fileSummary"><div class="file-meta"><div class="pdf-icon">PDF</div><div style="min-width:0"><div class="file-name" id="fileName">document.pdf</div><div class="file-sub" id="fileSub">Loading…</div></div></div><span class="status-pill" id="fileStatus">Ready</span></div><div class="progress-wrap" id="loadProgress"><div class="progress-top"><span id="loadLabel">Opening PDF…</span><span id="loadPercent">0%</span></div><div class="progress"><div class="bar" id="loadBar"></div></div></div><div class="notice" id="notice"></div></div>
<div class="card workspace"><div class="section-head"><div><div class="section-title"><span class="step">2</span>Live watermark preview</div><div style="font-size:12px;color:var(--muted);margin:4px 0 0 36px">Adjust settings on the right to preview your watermark.</div></div><span class="count" id="pageCount">No PDF loaded</span></div>
<div class="preview-toolbar"><button class="secondary" id="prevPage" disabled>← Previous</button><select id="previewPage" disabled aria-label="Preview page"><option>Page 1</option></select><button class="secondary" id="nextPage" disabled>Next →</button><button class="secondary" id="refreshPreview" disabled>↻ Refresh preview</button></div>
<div class="preview-area"><div class="paper" id="paper"><div class="empty" id="empty" style="width:100%;height:100%;border:0;border-radius:0;display:grid;align-content:center"><div class="empty-icon">✦</div><strong>Your watermark preview appears here</strong><span>Upload a PDF and customize your watermark.</span></div><div class="wm-preview" id="wmPreview" style="display:none"></div></div></div>
<div class="preview-foot"><span id="previewCaption">No document loaded</span><span id="previewPageSize">—</span></div></div>
<div class="card result-card" id="resultCard"><div class="result-head"><div><h3>✓ Watermarked PDF ready</h3><p id="resultInfo">Your file is ready to download.</p></div><button class="primary" id="downloadAgain">↓ Download PDF</button></div></div>
</section>
<aside class="side"><div class="card settings"><h2>Watermark settings</h2><p class="settings-intro">Design your watermark and choose where it appears.</p>
<div class="field"><label>Watermark type</label><div class="segmented" id="typeSwitch"><button class="active" data-type="text" type="button">T Text watermark</button><button data-type="image" type="button">▧ Image / logo</button></div></div>
<div id="textSettings">
<div class="field"><label for="watermarkText">Watermark text</label><textarea id="watermarkText" maxlength="180">CONFIDENTIAL</textarea><div class="hint">Use short text for the clearest result. New lines are supported.</div></div>
<div class="field"><label for="fontFamily">Font style</label><select id="fontFamily"><option value="Helvetica">Helvetica — Clean</option><option value="TimesRoman">Times — Classic</option><option value="Courier">Courier — Monospace</option></select></div>
<div class="field"><label for="fontSize">Font size <span class="range-value" id="fontSizeValue">42 pt</span></label><div class="range-row"><input type="range" id="fontSize" min="10" max="120" value="42"><span class="range-value" id="fontSizeValue2">42 pt</span></div></div>
<div class="field"><label for="fontWeight">Font weight</label><select id="fontWeight"><option value="normal">Regular</option><option value="bold" selected>Bold</option><option value="italic">Italic</option></select></div>
</div>
<div id="imageSettings" style="display:none"><div class="field"><label>Watermark image / logo</label><label for="imageInput" class="secondary" style="display:block;text-align:center;padding:12px;cursor:pointer">Choose PNG or JPG logo</label><input type="file" id="imageInput" accept="image/png,image/jpeg,image/jpg" class="file-input"><div class="hint" id="imageStatus">PNG with transparency works best.</div></div></div>
<div class="field"><label for="preset">Quick preset</label><select id="preset"><option value="custom">Custom settings</option><option value="confidential">CONFIDENTIAL — Diagonal</option><option value="draft">DRAFT — Light</option><option value="approved">APPROVED — Green</option><option value="copy">COPY — Repeated pattern</option><option value="sample">SAMPLE — Center</option></select></div>
<div class="field"><label for="position">Position</label><select id="position"><option value="center" selected>Center</option><option value="diagonal">Diagonal across page</option><option value="top">Top center</option><option value="bottom">Bottom center</option><option value="top-left">Top left</option><option value="top-right">Top right</option><option value="bottom-left">Bottom left</option><option value="bottom-right">Bottom right</option><option value="tile">Repeated / tiled pattern</option></select></div>
<div class="field"><label for="rotation">Rotation <span class="range-value" id="rotationValue">-45°</span></label><div class="range-row"><input type="range" id="rotation" min="-180" max="180" value="-45"><span class="range-value" id="rotationValue2">-45°</span></div></div>
<div class="field"><label for="opacity">Opacity <span class="range-value" id="opacityValue">22%</span></label><div class="range-row"><input type="range" id="opacity" min="5" max="100" value="22"><span class="range-value" id="opacityValue2">22%</span></div></div>
<div class="field"><label for="color">Text color</label><div class="color-row"><input type="color" id="color" value="#737993"><select id="colorPreset"><option value="">Choose color preset</option><option value="#737993">Slate gray</option><option value="#c62828">Red</option><option value="#1d4ed8">Blue</option><option value="#15803d">Green</option><option value="#111827">Black</option><option value="#7c3aed">Purple</option></select></div></div>
<div class="field"><label for="scale">Size / scale <span class="range-value" id="scaleValue">100%</span></label><div class="range-row"><input type="range" id="scale" min="25" max="200" value="100"><span class="range-value" id="scaleValue2">100%</span></div></div>
<div class="field"><label for="pageRange">Apply to pages</label><input type="text" id="pageRange" placeholder="All pages (or 1-3, 5)"><div class="hint">Leave blank to apply to all pages. Example: 1-3, 5, 8-10.</div></div>
<button class="advanced-toggle" id="advancedToggle" type="button"><span>⚙ Advanced options</span><span id="chevron">+</span></button>
<div class="advanced" id="advanced">
<div class="field"><label for="blend">Watermark appearance</label><select id="blend"><option value="normal">Normal overlay</option><option value="multiply">Multiply (darken)</option><option value="screen">Screen (lighten)</option></select><div class="hint">Blend modes are approximated where supported by the PDF format.</div></div>
<div class="field"><label for="margin">Edge margin <small>PDF points</small></label><input type="number" id="margin" min="0" max="150" value="36"></div>
<label class="check-row"><input type="checkbox" id="behind"><span><strong>Place watermark behind page content</strong><span>Useful for some documents; visibility may vary.</span></span></label>
<label class="check-row"><input type="checkbox" id="includeMetadata" checked><span><strong>Preserve PDF metadata where possible</strong><span>Existing metadata is retained by the PDF library when supported.</span></span></label>
<div class="field"><label for="outputName">Output filename</label><input type="text" id="outputName" maxlength="100" value="watermarked-document.pdf"></div>
</div>
<button class="primary apply-btn" id="applyBtn" disabled>Apply watermark & download ↓</button>
<div class="progress-wrap" id="saveProgress"><div class="progress-top"><span id="saveLabel">Applying watermark…</span><span id="savePercent">0%</span></div><div class="progress"><div class="bar" id="saveBar"></div></div></div>
<div class="footer-note">Text watermarks are embedded as PDF vector text. Image watermarks support PNG and JPG. Always review the exported file before sharing.</div>
</div>
<div class="privacy"><div style="font-size:18px">♧</div><div><strong>Your files stay private</strong><p>PDF processing runs in your browser. Your PDF is not uploaded to a PDFix server. Public CDN libraries need internet access.</p></div></div></aside></div>
<footer>Made for a smoother PDF workflow · <b>PDFix</b> · Processing runs in your browser</footer></main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/pdf-lib/1.17.1/pdf-lib.min.js"></script>
<script>
(()=>{'use strict';const $=id=>document.getElementById(id);let pdfDoc=null,pdfBytes=null,pdfFile=null,previewPage=1,wmType='text',logoBytes=null,logoMime='image/png',lastOutput=null;
if(window.pdfjsLib)pdfjsLib.GlobalWorkerOptions.workerSrc='https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
function notice(msg,type='error'){$('notice').textContent=msg;$('notice').className='notice show '+type}function hideNotice(){$('notice').className='notice';$('notice').textContent=''}function prog(pct,label){$('saveProgress').classList.add('show');$('saveBar').style.width=pct+'%';$('savePercent').textContent=Math.round(pct)+'%';if(label)$('saveLabel').textContent=label}function resetProg(){$('saveProgress').classList.remove('show');$('saveBar').style.width='0%'}
const normColor=h=>{const n=parseInt(h.slice(1),16);return {r:((n>>16)&255)/255,g:((n>>8)&255)/255,b:(n&255)/255}};function esc(s){return String(s).replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]))}
function baseName(){return ($('outputName').value.trim()||'watermarked-document.pdf').replace(/\.pdf$/i,'')+'.pdf'}
async function loadPDF(file){hideNotice();if(!file)return;if(file.type!=='application/pdf'&&!file.name.toLowerCase().endsWith('.pdf'))return notice('Please choose a valid PDF file.');if(!window.pdfjsLib||!window.PDFLib)return notice('Required PDF libraries could not load. Check your internet connection and refresh.');
pdfDoc=null;pdfFile=file;pdfBytes=null;$('applyBtn').disabled=true;$('resultCard').classList.remove('show');$('fileSummary').classList.add('show');$('fileName').textContent=file.name;$('fileSub').textContent=(file.size/1048576).toFixed(2)+' MB · Reading…';$('fileStatus').textContent='Loading…';$('empty').style.display='grid';
try{pdfBytes=await file.arrayBuffer();pdfDoc=await pdfjsLib.getDocument({data:pdfBytes.slice(0),useSystemFonts:true}).promise;$('fileSub').textContent=`${pdfDoc.numPages} pages · ${(file.size/1048576).toFixed(2)} MB`;$('fileStatus').textContent='Ready';$('outputName').value=file.name.replace(/\.pdf$/i,'')+'-watermarked.pdf';$('pageCount').textContent=`${pdfDoc.numPages} pages`;$('previewPage').innerHTML='';for(let i=1;i<=pdfDoc.numPages;i++){const o=document.createElement('option');o.value=i;o.textContent=`Page ${i}`;$('previewPage').appendChild(o)}$('previewPage').disabled=false;$('prevPage').disabled=false;$('nextPage').disabled=false;$('refreshPreview').disabled=false;$('applyBtn').disabled=false;$('clearBtn').disabled=false;previewPage=1;await renderPreview();notice('PDF loaded. Customize the watermark and check the live preview before exporting.','success')}catch(e){console.error(e);pdfDoc=null;$('fileStatus').textContent='Error';notice('Could not open this PDF. It may be damaged, encrypted or password-protected.')}}
async function renderPreview(){if(!pdfDoc)return;const pg=await pdfDoc.getPage(previewPage),vp=pg.getViewport({scale:1.2,rotation:0}),c=document.createElement('canvas');c.width=Math.ceil(vp.width);c.height=Math.ceil(vp.height);await pg.render({canvasContext:c.getContext('2d',{alpha:false}),viewport:vp,background:'#ffffff'}).promise;const paper=$('paper');paper.innerHTML='';c.style.width='100%';c.style.height='100%';c.style.objectFit='contain';paper.appendChild(c);paper.style.aspectRatio=`${vp.width}/${vp.height}`;$('empty').style.display='none';$('previewCaption').textContent=`Previewing page ${previewPage} of ${pdfDoc.numPages}`;$('previewPageSize').textContent=`${Math.round(pg.view[2]-pg.view[0])} × ${Math.round(pg.view[3]-pg.view[1])} pt`;updateWatermarkPreview()}
function updateWatermarkPreview(){const paper=$('paper');let el=$('wmPreview');if(!el){el=document.createElement('div');el.id='wmPreview';el.className='wm-preview';paper.appendChild(el)}const txt=$('watermarkText').value||'WATERMARK',pos=$('position').value,rotation=+$('rotation').value,opacity=+$('opacity').value/100,scale=+$('scale').value/100,size=+$('fontSize').value;el.style.display='block';el.style.color=$('color').value;el.style.opacity=opacity;el.style.fontFamily=$('fontFamily').value==='TimesRoman'?'Georgia,serif':$('fontFamily').value==='Courier'?'monospace':'Arial,sans-serif';el.style.fontSize=`${Math.max(8,size*.62*scale)}px`;el.style.fontWeight=$('fontWeight').value==='bold'?'800':'400';el.style.fontStyle=$('fontWeight').value==='italic'?'italic':'normal';el.style.transform=`translate(-50%,-50%) rotate(${rotation}deg)`;el.style.left='50%';el.style.top='50%';el.style.maxWidth='90%';el.style.letterSpacing='1px';el.style.padding='2px 5px';el.style.textShadow='none';if(wmType==='image'&&logoBytes){let url=URL.createObjectURL(new Blob([logoBytes],{type:logoMime}));el.innerHTML=`<img src="${url}" style="display:block;max-width:100%;max-height:100%;width:${Math.min(90,35*scale)}%;object-fit:contain;opacity:${opacity}">`;el.style.width='100%';el.style.height='100%';el.style.background='transparent';el.style.border='0';el.style.whiteSpace='normal';el.style.fontSize='0';}else{el.textContent=txt;el.style.width='auto';el.style.height='auto';el.style.background='transparent';el.style.whiteSpace='pre-wrap';}
if(pos==='top'){el.style.top='12%';el.style.left='50%'}else if(pos==='bottom'){el.style.top='88%';el.style.left='50%'}else if(pos==='top-left'){el.style.top='10%';el.style.left='15%'}else if(pos==='top-right'){el.style.top='10%';el.style.left='85%'}else if(pos==='bottom-left'){el.style.top='90%';el.style.left='15%'}else if(pos==='bottom-right'){el.style.top='90%';el.style.left='85%'}else if(pos==='diagonal'){el.style.top='50%';el.style.left='50%';el.style.fontSize=`${Math.min(50,size*.9*scale)}px`}else if(pos==='tile'){el.style.top='50%';el.style.left='50%';el.style.width='100%';el.style.height='100%';el.style.display='grid';el.style.placeItems='center';el.style.backgroundImage='none';el.style.whiteSpace='pre-wrap';el.style.fontSize=`${Math.max(10,size*.42*scale)}px`;el.style.letterSpacing='8px';el.textContent=wmType==='text'?Array(12).fill(txt).join(' '):'';}else{el.style.top='50%';el.style.left='50%';el.style.display='block';el.style.letterSpacing='1px'}
}
async function applyPreset(v){if(v==='custom')return;if(v==='confidential'){$('watermarkText').value='CONFIDENTIAL';$('position').value='diagonal';$('rotation').value=-45;$('opacity').value=22;$('color').value='#737993';$('fontSize').value=42;$('fontWeight').value='bold'}if(v==='draft'){$('watermarkText').value='DRAFT';$('position').value='diagonal';$('rotation').value=-35;$('opacity').value=18;$('color').value='#7c8496';$('fontSize').value=55}if(v==='approved'){$('watermarkText').value='APPROVED';$('position').value='center';$('rotation').value=0;$('opacity').value=75;$('color').value='#15803d';$('fontSize').value=48}if(v==='copy'){$('watermarkText').value='COPY';$('position').value='tile';$('rotation').value=-35;$('opacity').value=18;$('color').value='#737993';$('fontSize').value=30}if(v==='sample'){$('watermarkText').value='SAMPLE';$('position').value='center';$('rotation').value=0;$('opacity').value=35;$('color').value='#c62828';$('fontSize').value=48}syncRanges();updateWatermarkPreview()}
function syncRanges(){[['fontSize','fontSizeValue','fontSizeValue2',' pt'],['rotation','rotationValue','rotationValue2','°'],['opacity','opacityValue','opacityValue2','%'],['scale','scaleValue','scaleValue2','%']].forEach(([id,a,b,s])=>{$(a).textContent=$(id).value+s;$(b).textContent=$(id).value+s})}
$('chooseBtn').addEventListener('click',e=>{e.stopPropagation();$('fileInput').click()});$('dropzone').addEventListener('click',()=>$('fileInput').click());$('dropzone').addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();$('fileInput').click()}});$('fileInput').addEventListener('change',()=>loadPDF($('fileInput').files[0]));['dragenter','dragover'].forEach(ev=>$('dropzone').addEventListener(ev,e=>{e.preventDefault();$('dropzone').classList.add('drag')}));['dragleave','drop'].forEach(ev=>$('dropzone').addEventListener(ev,e=>{e.preventDefault();$('dropzone').classList.remove('drag')}));$('dropzone').addEventListener('drop',e=>{if(e.dataTransfer.files[0])loadPDF(e.dataTransfer.files[0])});
$('clearBtn').addEventListener('click',()=>{pdfDoc=null;pdfBytes=null;pdfFile=null;$('fileInput').value='';$('fileSummary').classList.remove('show');$('paper').innerHTML='<div class="empty" id="empty" style="width:100%;height:100%;border:0;border-radius:0;display:grid;align-content:center"><div class="empty-icon">✦</div><strong>Your watermark preview appears here</strong><span>Upload a PDF and customize your watermark.</span></div>';$('applyBtn').disabled=true;$('clearBtn').disabled=true;$('pageCount').textContent='No PDF loaded';$('previewPage').disabled=true;$('prevPage').disabled=$('nextPage').disabled=$('refreshPreview').disabled=true;$('previewCaption').textContent='No document loaded';$('previewPageSize').textContent='—';$('resultCard').classList.remove('show');hideNotice()});
$('prevPage').addEventListener('click',async()=>{if(pdfDoc&&previewPage>1){previewPage--;$('previewPage').value=previewPage;await renderPreview()}});$('nextPage').addEventListener('click',async()=>{if(pdfDoc&&previewPage<pdfDoc.numPages){previewPage++;$('previewPage').value=previewPage;await renderPreview()}});$('previewPage').addEventListener('change',async()=>{previewPage=+$('previewPage').value;await renderPreview()});$('refreshPreview').addEventListener('click',renderPreview);
document.querySelectorAll('#typeSwitch button').forEach(b=>b.addEventListener('click',()=>{wmType=b.dataset.type;document.querySelectorAll('#typeSwitch button').forEach(x=>x.classList.toggle('active',x===b));$('textSettings').style.display=wmType==='text'?'block':'none';$('imageSettings').style.display=wmType==='image'?'block':'none';updateWatermarkPreview()}));
$('imageInput').addEventListener('change',async()=>{const f=$('imageInput').files[0];if(!f)return;if(!['image/png','image/jpeg'].includes(f.type))return notice('Choose a PNG or JPG image.');logoBytes=await f.arrayBuffer();logoMime=f.type;$('imageStatus').textContent=`${f.name} · ${(f.size/1024).toFixed(1)} KB`;updateWatermarkPreview()});
['watermarkText','fontFamily','fontSize','fontWeight','position','rotation','opacity','color','scale','pageRange','blend','margin','behind'].forEach(id=>$(id).addEventListener('input',()=>{syncRanges();updateWatermarkPreview()}));['fontFamily','fontWeight','position','color','blend','behind'].forEach(id=>$(id).addEventListener('change',updateWatermarkPreview));$('colorPreset').addEventListener('change',()=>{if($('colorPreset').value)$('color').value=$('colorPreset').value;updateWatermarkPreview()});['fontSize','rotation','opacity','scale'].forEach(id=>$(id).addEventListener('input',syncRanges));$('preset').addEventListener('change',()=>applyPreset($('preset').value));$('advancedToggle').addEventListener('click',()=>{$('advanced').classList.toggle('open');$('chevron').textContent=$('advanced').classList.contains('open')?'−':'+'});
function pageSet(){if(!$('pageRange').value.trim())return Array.from({length:pdfDoc.getPageCount()},(_,i)=>i);const set=new Set();for(const part of $('pageRange').value.split(',')){const m=part.trim().match(/^(\d+)\s*(?:-\s*(\d+))?$/);if(!m)throw Error('Use a page range such as 1-3, 5, 8-10.');const a=+m[1],b=m[2]?+m[2]:a;if(a<1||b<a||b>pdfDoc.getPageCount())throw Error(`Page numbers must be between 1 and ${pdfDoc.getPageCount()}.`);for(let p=a;p<=b;p++)set.add(p-1)}if(!set.size)throw Error('Enter at least one page number.');return [...set]}
$('applyBtn').addEventListener('click',async()=>{if(!pdfDoc||!pdfBytes)return;let pages;try{pages=pageSet()}catch(e){return notice(e.message)}if(wmType==='text'&&!$('watermarkText').value.trim())return notice('Enter the text you want to use as a watermark.');if(wmType==='image'&&!logoBytes)return notice('Choose a PNG or JPG logo first.');const btn=$('applyBtn');btn.disabled=true;hideNotice();$('resultCard').classList.remove('show');prog(3,'Preparing PDF…');
try{const {PDFDocument,StandardFonts,rgb,degrees}=PDFLib;const doc=await PDFDocument.load(pdfBytes.slice(0));let font;const family=$('fontFamily').value,weight=$('fontWeight').value;if(family==='TimesRoman')font=await doc.embedFont(weight==='bold'?StandardFonts.TimesRomanBold:weight==='italic'?StandardFonts.TimesRomanItalic:StandardFonts.TimesRoman);else if(family==='Courier')font=await doc.embedFont(weight==='bold'?StandardFonts.CourierBold:weight==='italic'?StandardFonts.CourierOblique:StandardFonts.Courier);else font=await doc.embedFont(weight==='bold'?StandardFonts.HelveticaBold:weight==='italic'?StandardFonts.HelveticaOblique:StandardFonts.Helvetica);
let image=null;if(wmType==='image'){if(logoMime==='image/png')image=await doc.embedPng(logoBytes);else image=await doc.embedJpg(logoBytes)}
const color=normColor($('color').value),opacity=+$('opacity').value/100,scale=+$('scale').value/100,angle=+$('rotation').value,position=$('position').value,margin=Math.max(0,Math.min(150,+$('margin').value||0)),fontSize=+$('fontSize').value*scale,txt=$('watermarkText').value||'WATERMARK';
for(let ix=0;ix<pages.length;ix++){const pi=pages[ix],page=doc.getPages()[pi],{width,height}=page.getSize();const drawOpts={opacity,rotate:degrees(angle),color:rgb(color.r,color.g,color.b)};const isImage=wmType==='image';const textWidth=isImage?image.width:font.widthOfTextAtSize(txt,fontSize);const textHeight=isImage?image.height:font.heightAtSize(fontSize);let w=isImage?Math.min(width*.6,image.width*scale):textWidth,h=isImage?Math.min(height*.6,image.height*scale):textHeight;if(isImage){const k=Math.min(width*.6/image.width,height*.6/image.height)*scale;w=image.width*k;h=image.height*k}
let x=(width-w)/2,y=(height-h)/2;
if(position==='top'){x=(width-w)/2;y=height-h-margin}else if(position==='bottom'){x=(width-w)/2;y=margin}else if(position==='top-left'){x=margin;y=height-h-margin}else if(position==='top-right'){x=width-w-margin;y=height-h-margin}else if(position==='bottom-left'){x=margin;y=margin}else if(position==='bottom-right'){x=width-w-margin;y=margin}
if(position==='tile'){const gapX=Math.max(w+90,width*.35),gapY=Math.max(h+90,height*.25);for(let yy=-height;yy<height*2;yy+=gapY){for(let xx=-width;xx<width*2;xx+=gapX){if(isImage)page.drawImage(image,{x:xx,y:yy,width:w,height:h,opacity,rotate:degrees(angle)});else page.drawText(txt,{x:xx,y:yy,size:fontSize,font,color,opacity,rotate:degrees(angle)})}}}
else if(isImage)page.drawImage(image,{x,y,width:w,height:h,opacity,rotate:degrees(angle)});
else page.drawText(txt,{x,y,size:fontSize,font,color,opacity,rotate:degrees(angle)});
prog(8+((ix+1)/pages.length)*78,`Watermarking page ${pi+1} of ${doc.getPageCount()}…`);if(ix%4===0)await new Promise(r=>setTimeout(r,0))}
prog(90,'Generating output PDF…');const out=await doc.save({useObjectStreams:true});lastOutput=new Blob([out],{type:'application/pdf'});let name=$('outputName').value.trim()||pdfFile.name.replace(/\.pdf$/i,'')+'-watermarked.pdf';if(!name.toLowerCase().endsWith('.pdf'))name+='.pdf';$('resultInfo').textContent=`${name} · ${(lastOutput.size/1048576).toFixed(2)} MB · Watermark applied to ${pages.length} page${pages.length===1?'':'s'}.`;$('resultCard').classList.add('show');if($('downloadAfter')?.checked!==false)download(lastOutput,name);prog(100,'Done');notice('Watermark applied successfully. Review the output PDF before sharing.','success');$('resultCard').scrollIntoView({behavior:'smooth',block:'nearest'})}catch(e){console.error(e);notice('Could not apply watermark. The PDF may be encrypted, damaged, or too complex. Try another file.')}finally{btn.disabled=false;setTimeout(resetProg,2200)}});
function download(blob,name){const url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),2000)}$('downloadAgain').addEventListener('click',()=>{if(lastOutput){let name=$('outputName').value.trim()||'watermarked-document.pdf';if(!name.toLowerCase().endsWith('.pdf'))name+='.pdf';download(lastOutput,name)}});
syncRanges();
})();
</script></body></html>