نداء الهند
  • الرئيسة
  • ملوكيات
    • الملوكيات
    • المغوليات
    • الغزنويات
  • كيرالا
    • كيرالا
    • ثقافة كيرالا
    • أدب كيرالا
    • أعلام كيرالا
  • أرشيف PDF
  • قسم التراجم
    • تراجم العلماء المسلمين
    • تراجم الأعلام
    • الأعلام العامة
    • الأعلام المسلمة
  • أدبيات
    • أدبيات عامة
    • الأدب العربي الهندي
    • الأدب العربي العالمي
  • أقسام الشعر
    • أشعار عامة
    • المراثي
    • الأماديح النبوية
    • الأشعار الوطنية
  • قسم الديانات
    • الأديان الهندية
    • الهندوسية
    • البوذية
    • الجينية
    • السيخية
  • الهنديات
    • تراث الهند
    • تاريخ الهند
    • علوم الهند
    • العلاقات الهندية
    • إسهامات العلماء الهندية
نداء الهند
  • الرئيسة
  • ملوكيات
    • الملوكيات
    • المغوليات
    • الغزنويات
  • كيرالا
    • كيرالا
    • ثقافة كيرالا
    • أدب كيرالا
    • أعلام كيرالا
  • أرشيف PDF
  • قسم التراجم
    • تراجم العلماء المسلمين
    • تراجم الأعلام
    • الأعلام العامة
    • الأعلام المسلمة
  • أدبيات
    • أدبيات عامة
    • الأدب العربي الهندي
    • الأدب العربي العالمي
  • أقسام الشعر
    • أشعار عامة
    • المراثي
    • الأماديح النبوية
    • الأشعار الوطنية
  • قسم الديانات
    • الأديان الهندية
    • الهندوسية
    • البوذية
    • الجينية
    • السيخية
  • الهنديات
    • تراث الهند
    • تاريخ الهند
    • علوم الهند
    • العلاقات الهندية
    • إسهامات العلماء الهندية
الأربعاء, يونيو 24, 2026
نداء الهند
نداء الهند
  • الرئيسة
  • ملوكيات
    • الملوكيات
    • المغوليات
    • الغزنويات
  • كيرالا
    • كيرالا
    • ثقافة كيرالا
    • أدب كيرالا
    • أعلام كيرالا
  • أرشيف PDF
  • قسم التراجم
    • تراجم العلماء المسلمين
    • تراجم الأعلام
    • الأعلام العامة
    • الأعلام المسلمة
  • أدبيات
    • أدبيات عامة
    • الأدب العربي الهندي
    • الأدب العربي العالمي
  • أقسام الشعر
    • أشعار عامة
    • المراثي
    • الأماديح النبوية
    • الأشعار الوطنية
  • قسم الديانات
    • الأديان الهندية
    • الهندوسية
    • البوذية
    • الجينية
    • السيخية
  • الهنديات
    • تراث الهند
    • تاريخ الهند
    • علوم الهند
    • العلاقات الهندية
    • إسهامات العلماء الهندية
Copyright © Nidaul Hind
الصفحة الرئيسية Uncategorizedfdfsdfdfd
Uncategorized

fdfsdfdfd

كتبه Nidaul Hind يونيو 23, 2026
كتبه Nidaul Hind يونيو 23, 2026 0 تعليقات
شاركها 0FacebookTwitterPinterestEmail
3

قد تعجبك أيضاً
  • نداء الهند إلى أصحاب الخير
  • آراء سماحة الشيخ مولانا أبي الحسن الندوي حول الاستشراق والمستشرقين في مكتوباته
  • القديم فل
  • Untitled
  • Free PDF Watermark Tool – Add Text & Logo Watermarks Online
    يونيو 23, 2026
  • الحاشية على صدرا لأستاذ الهند نظام الدين السهالوي
    يونيو 21, 2026
  • شعر: الفراق
    ديسمبر 10, 2025
  • Untitled
    ديسمبر 2, 2025

#wp-pdf-watermark-tool{ --bg:#f6f8fb; --card:#ffffff; --line:#d9e0ea; --text:#1d2433; --muted:#667085; --accent:#2563eb; --soft:#eff5ff; --toolbar-blue:#2563eb; --toolbar-line:#b9d3ff; --toolbar-bg:#ffffff; } #wp-pdf-watermark-tool,#wp-pdf-watermark-tool *{box-sizing:border-box} #wp-pdf-watermark-tool{margin:0;font-family:Arial,Helvetica,sans-serif;background:var(--bg);color:var(--text)} #wp-pdf-watermark-tool .wrap{max-width:1400px;margin:0 auto;padding:24px} #wp-pdf-watermark-tool h1{margin:0 0 8px;font-size:30px} #wp-pdf-watermark-tool .sub{color:var(--muted);margin-bottom:18px} #wp-pdf-watermark-tool .card{background:var(--card);border:1px solid var(--line);border-radius:18px;box-shadow:0 8px 28px rgba(16,24,40,.05);overflow:hidden} #wp-pdf-watermark-tool .top-card{padding:18px;margin-bottom:18px} #wp-pdf-watermark-tool .options-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px} #wp-pdf-watermark-tool .uploader-panel{ display:flex; gap:16px; align-items:stretch; padding:16px; border:1px solid var(--line); border-radius:18px; background:linear-gradient(180deg,#ffffff,#f8fbff); margin-bottom:18px; flex-wrap:wrap; } #wp-pdf-watermark-tool .uploader-main{flex:1 1 520px;min-width:280px} #wp-pdf-watermark-tool .panel-title{font-size:18px;font-weight:800;margin:0 0 6px} #wp-pdf-watermark-tool .panel-sub{font-size:13px;color:var(--muted);margin:0 0 12px} #wp-pdf-watermark-tool .section-card{ border:1px solid var(--line); border-radius:18px; background:#fff; padding:16px; box-shadow:0 8px 24px rgba(16,24,40,.04); } #wp-pdf-watermark-tool .section-title{font-size:16px;font-weight:800;margin:0 0 6px} #wp-pdf-watermark-tool .section-sub{font-size:12px;color:var(--muted);margin:0 0 14px} #wp-pdf-watermark-tool .thumb-card{ display:flex; gap:12px; align-items:center; border:1px solid var(--line); border-radius:14px; background:#fff; padding:12px; min-height:86px; } #wp-pdf-watermark-tool .thumb-preview{ width:64px;height:64px;border-radius:12px;border:1px solid var(--line); background:#f5f7fb;display:flex;align-items:center;justify-content:center; overflow:hidden;flex:0 0 auto; } #wp-pdf-watermark-tool .thumb-preview img{max-width:100%;max-height:100%;display:block} #wp-pdf-watermark-tool .thumb-meta{min-width:0;flex:1} #wp-pdf-watermark-tool .thumb-name{font-size:14px;font-weight:800;color:var(--text);word-break:break-word} #wp-pdf-watermark-tool .thumb-sub{margin-top:4px;font-size:12px;color:var(--muted);word-break:break-word} #wp-pdf-watermark-tool .upload-progress{ display:none; margin-top:12px; border:1px solid #dbe6ff; background:#f8fbff; border-radius:14px; padding:10px 12px; } #wp-pdf-watermark-tool .upload-progress.active{display:block} #wp-pdf-watermark-tool .upload-progress-top{ display:flex;justify-content:space-between;gap:10px;align-items:center;margin-bottom:8px; font-size:12px;font-weight:700;color:#1248b5; } #wp-pdf-watermark-tool .progress-track{ width:100%;height:10px;border-radius:999px;background:#e7eefc;overflow:hidden; } #wp-pdf-watermark-tool .progress-fill{ width:0%;height:100%;border-radius:999px;background:linear-gradient(90deg,#2563eb,#60a5fa); transition:width .18s ease; } #wp-pdf-watermark-tool .inline-upload-box{display:flex;flex-direction:column;gap:10px} #wp-pdf-watermark-tool .inline-check{display:flex;gap:10px;align-items:center} #wp-pdf-watermark-tool .field{min-width:0}#wp-pdf-watermark-tool .field.full{grid-column:1/-1}#wp-pdf-watermark-tool .field.two{grid-column:span 2} #wp-pdf-watermark-tool label{display:block;font-size:13px;font-weight:700;margin-bottom:6px} #wp-pdf-watermark-tool input[type="text"],#wp-pdf-watermark-tool input[type="number"],#wp-pdf-watermark-tool select{width:100%;padding:11px 12px;border:1px solid var(--line);border-radius:10px;background:#fff} #wp-pdf-watermark-tool input[type="color"]{width:100%;height:42px;border:1px solid var(--line);border-radius:10px;padding:4px;background:#fff} #wp-pdf-watermark-tool input[type="range"]{width:100%} #wp-pdf-watermark-tool .file-btn,#wp-pdf-watermark-tool button{appearance:none;border:none;border-radius:12px;padding:12px 14px;font-weight:700;cursor:pointer;text-align:center} #wp-pdf-watermark-tool .file-btn{display:inline-block;width:100%;background:var(--soft);color:#1248b5;border:1px solid #cfe0ff} #wp-pdf-watermark-tool button.primary{background:var(--accent);color:#fff;border:1px solid var(--accent)} #wp-pdf-watermark-tool button.secondary{background:#fff;color:var(--text);border:1px solid var(--line)} #wp-pdf-watermark-tool button.soft{background:var(--soft);color:#1248b5;border:1px solid #cfe0ff} #wp-pdf-watermark-tool input[type="file"]{display:none} #wp-pdf-watermark-tool .helper{font-size:12px;color:var(--muted);margin-top:4px} #wp-pdf-watermark-tool .check-row{display:flex;gap:10px;align-items:center;padding:10px 12px;border:1px solid var(--line);border-radius:10px;background:#fff;min-height:42px} #wp-pdf-watermark-tool .quick-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:8px} #wp-pdf-watermark-tool .quick-grid button{padding:10px;font-size:12px} #wp-pdf-watermark-tool .actions{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;align-items:end} #wp-pdf-watermark-tool .preview-head{padding:16px 18px;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap} #wp-pdf-watermark-tool .preview-wrap{padding:0;min-height:760px;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;overflow:auto;background:#eef2f7} #wp-pdf-watermark-tool .canvas-stage{position:relative;display:inline-block;flex:0 0 auto;width:auto;min-width:0;max-width:none;margin:0;background:#fff;border:1px solid var(--line);box-shadow:0 10px 30px rgba(0,0,0,.08)} #wp-pdf-watermark-tool #pdfCanvas{display:block;width:auto;max-width:none;height:auto;margin:0;position:relative;z-index:1;background:transparent}

#wp-pdf-watermark-tool .overlay{ position:absolute; transform:translate(-50%,-50%); transform-origin:center center; user-select:none; z-index:9; } #wp-pdf-watermark-tool .overlay.hidden{display:none} #wp-pdf-watermark-tool #textOverlay{z-index:9} #wp-pdf-watermark-tool #imageOverlay{z-index:20}

#wp-pdf-watermark-tool .text-visual,#wp-pdf-watermark-tool .image-visual{ position:relative; display:inline-flex; align-items:center; justify-content:center; cursor:move; } #wp-pdf-watermark-tool .text-visual span{ display:inline-block; line-height:1.1; white-space:nowrap; } #wp-pdf-watermark-tool .text-visual span[contenteditable="true"]{ cursor:move; outline:none; min-width:20px; padding:2px 6px; border-radius:6px; pointer-events:auto; } #wp-pdf-watermark-tool .text-visual.editing span[contenteditable="true"]{ cursor:text; background:rgba(255,255,255,.35); } #wp-pdf-watermark-tool .text-visual.editing span[contenteditable="true"]:focus{ outline:2px solid rgba(37,99,235,.25); } #wp-pdf-watermark-tool .image-visual img{ display:block; pointer-events:none; max-width:none; max-height:none; } #wp-pdf-watermark-tool .text-visual.has-link span[contenteditable="true"]{ text-decoration-style:dotted; text-underline-offset:3px; } #wp-pdf-watermark-tool .text-visual.has-link:hover span[contenteditable="true"]{ box-shadow:0 0 0 2px rgba(37,99,235,.14); background:rgba(37,99,235,.05); } #wp-pdf-watermark-tool .image-visual.has-link:hover .selection-ring, #wp-pdf-watermark-tool .text-visual.has-link:hover .selection-ring{ border-color:#0f766e; } #wp-pdf-watermark-tool .selection-ring{ position:absolute; inset:-8px; border:2px dashed #2563eb; border-radius:10px; pointer-events:none; } #wp-pdf-watermark-tool .handle{ position:absolute;width:26px;height:26px;border-radius:999px;border:1px solid #cfe0ff;background:#2563eb; color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px;line-height:1;padding:0; cursor:pointer;box-shadow:0 4px 10px rgba(0,0,0,.12);z-index:2 } #wp-pdf-watermark-tool .rotate-handle{top:-18px;left:50%;transform:translateX(-50%);cursor:grab} #wp-pdf-watermark-tool .resize-handle{right:-18px;bottom:-18px;cursor:nwse-resize} #wp-pdf-watermark-tool .small{font-size:12px;color:var(--muted)} #wp-pdf-watermark-tool .pages{display:flex;gap:8px;align-items:center;flex-wrap:wrap} #wp-pdf-watermark-tool .pill{padding:8px 10px;border:1px solid var(--line);border-radius:999px;background:#fff;font-size:12px}

#wp-pdf-watermark-tool .page-jump{ display:flex; gap:8px; align-items:center; flex-wrap:wrap; } #wp-pdf-watermark-tool .page-jump input{ width:86px; padding:9px 10px; border:1px solid var(--line); border-radius:10px; }

#wp-pdf-watermark-tool .wm-toolbar-shell{ position:absolute; display:none; z-index:40; pointer-events:auto; padding-top:38px; } #wp-pdf-watermark-tool .wm-toolbar-shell.open{display:block} #wp-pdf-watermark-tool #textToolbarShell,#wp-pdf-watermark-tool #imageToolbarShell{width:min(620px, calc(100% - 16px))} #wp-pdf-watermark-tool .wm-toolbar{ position:relative; display:flex; flex-direction:column; gap:6px; padding:6px; background:var(--toolbar-bg); border:1px solid var(--toolbar-line); border-radius:12px; box-shadow:0 10px 24px rgba(37,99,235,.14); overflow:visible; width:100%; max-width:100%; pointer-events:auto; } #wp-pdf-watermark-tool .wm-toolbar.open{display:flex} #wp-pdf-watermark-tool .wm-toolbar,#wp-pdf-watermark-tool .wm-toolbar *{color:var(--toolbar-blue) !important;opacity:1 !important} #wp-pdf-watermark-tool .wm-toolbar button,#wp-pdf-watermark-tool .wm-toolbar select,#wp-pdf-watermark-tool .wm-toolbar input[type="color"],#wp-pdf-watermark-tool .wm-toolbar input[type="number"],#wp-pdf-watermark-tool .wm-toolbar input[type="range"]{ background:var(--toolbar-bg) !important;color:var(--toolbar-blue) !important } #wp-pdf-watermark-tool .wm-toolbar .tool-row{ display:flex; align-items:center; flex-wrap:wrap; gap:6px; } #wp-pdf-watermark-tool .wm-toolbar .tool-row-extra{display:none} #wp-pdf-watermark-tool .wm-toolbar-shell.expanded .tool-row-extra{display:flex} #wp-pdf-watermark-tool #textToolbarShell.expanded #textToolbar .tool-row-extra{ display:grid; grid-template-columns:minmax(112px,1fr) minmax(150px,1.5fr) auto; align-items:center; gap:6px; } #wp-pdf-watermark-tool .wm-toolbar .tool-btn,#wp-pdf-watermark-tool .wm-toolbar .tool-select,#wp-pdf-watermark-tool .wm-toolbar .tool-number,#wp-pdf-watermark-tool .wm-toolbar .tool-color-wrap,#wp-pdf-watermark-tool .wm-toolbar .tool-range-wrap{height:34px} #wp-pdf-watermark-tool .wm-toolbar .tool-btn{ padding:0 10px;display:flex;align-items:center;gap:6px;border:1px solid var(--toolbar-line); border-radius:10px;min-width:auto;font-weight:600;white-space:nowrap } #wp-pdf-watermark-tool .wm-toolbar .tool-btn.active{background:#eff5ff !important} #wp-pdf-watermark-tool .wm-toolbar .tool-btn.danger{color:#b42318 !important} #wp-pdf-watermark-tool .wm-toolbar .tool-icon{font-size:14px;line-height:1;color:var(--toolbar-blue) !important} #wp-pdf-watermark-tool .wm-toolbar .tool-color-wrap{display:flex;align-items:center;gap:6px;padding:0 10px;border:1px solid var(--toolbar-line);border-radius:10px} #wp-pdf-watermark-tool .wm-toolbar .tool-color-wrap input[type="color"]{width:22px;height:22px;padding:0;border:none;background:transparent !important} #wp-pdf-watermark-tool .wm-toolbar .tool-select{min-width:0;padding:0 10px;border:1px solid var(--toolbar-line);outline:none;max-width:190px;border-radius:10px} #wp-pdf-watermark-tool .wm-toolbar .tool-select.tool-compact-select{min-width:104px;max-width:116px} #wp-pdf-watermark-tool .wm-toolbar .tool-number{width:64px;padding:0 8px;border:1px solid var(--toolbar-line);outline:none;border-radius:10px} #wp-pdf-watermark-tool .wm-toolbar .tool-range-wrap{display:flex;align-items:center;gap:6px;padding:0 8px;border:1px solid var(--toolbar-line);border-radius:10px} #wp-pdf-watermark-tool .wm-toolbar .tool-range-wrap input[type="range"]{width:60px;margin:0} #wp-pdf-watermark-tool .wm-toolbar .tool-btn.tool-more-btn{min-width:70px;justify-content:center} #wp-pdf-watermark-tool .wm-toolbar .tool-check{ height:34px; display:inline-flex; align-items:center; gap:6px; padding:0 10px; border:1px solid var(--toolbar-line); border-radius:10px; background:#fff; font-size:12px; font-weight:700; margin:0; white-space:nowrap; } #wp-pdf-watermark-tool .wm-toolbar .tool-check input{width:auto;margin:0} #wp-pdf-watermark-tool #toolbarBoldBtn,#wp-pdf-watermark-tool #toolbarItalicBtn,#wp-pdf-watermark-tool #toolbarUnderlineBtn{width:34px;padding:0;justify-content:center} #wp-pdf-watermark-tool #textToolbarShell.expanded #textToolbarMoreBtn, #wp-pdf-watermark-tool #imageToolbarShell.expanded #imageToolbarMoreBtn{display:none} #wp-pdf-watermark-tool .toolbar-close-btn{ position:absolute; top:0; right:6px; width:32px; height:32px; border-radius:999px; border:1px solid #d5e3ff; background:#fff; color:#1248b5; box-shadow:0 8px 18px rgba(16,24,40,.12); font-size:18px; font-weight:700; line-height:1; padding:0; z-index:2; } #wp-pdf-watermark-tool #toolbarTextLayerBtn,#wp-pdf-watermark-tool #toolbarImageLayerBtn{min-width:102px;justify-content:center} #wp-pdf-watermark-tool #toolbarLanguage{min-width:112px;max-width:140px} #wp-pdf-watermark-tool #toolbarFont{min-width:150px;max-width:190px} #wp-pdf-watermark-tool #toolbarLogoPageRange{min-width:132px;max-width:172px} #wp-pdf-watermark-tool #toolbarCenterBtn,#wp-pdf-watermark-tool #toolbarFontStylePreview,#wp-pdf-watermark-tool #toolbarRemoveTextBtn{display:none !important} #wp-pdf-watermark-tool #imageToolbarShell{max-width:calc(100% - 16px)} #wp-pdf-watermark-tool #imageToolbar{ width:100%; flex-direction:column; align-items:stretch; flex-wrap:nowrap; } #wp-pdf-watermark-tool #imageToolbar .tool-row{ display:flex; flex-wrap:wrap; align-items:center; } #wp-pdf-watermark-tool #imageToolbar .tool-row-extra{display:none;flex-wrap:wrap;align-items:center} #wp-pdf-watermark-tool #imageToolbarShell.expanded #imageToolbar .tool-row-extra{display:flex}

@media (max-width:760px){ #wp-pdf-watermark-tool #textToolbarShell.expanded #textToolbar .tool-row-extra{ grid-template-columns:repeat(2,minmax(0,1fr)); } #wp-pdf-watermark-tool #toolbarTextLayerBtn,#wp-pdf-watermark-tool #toolbarImageLayerBtn,#wp-pdf-watermark-tool .wm-toolbar .tool-btn.tool-more-btn{min-width:0} }

#wp-pdf-watermark-tool /* ── Mobile: text preview overlay UX ── */ @media (max-width:600px){ /* Scale down the watermark text in preview so it fits on small screens */ #textPreview{ max-width:min(160px, 45vw); overflow:hidden; text-overflow:ellipsis; }

/* Toolbar shell: anchor to bottom of viewport on mobile for thumb reach */ .wm-toolbar-shell{ position:fixed !important; left:0 !important; right:0 !important; bottom:0 !important; top:auto !important; width:100% !important; max-width:100% !important; border-radius:18px 18px 0 0 !important; box-shadow:0 -4px 24px rgba(37,99,235,.18) !important; z-index:9999 !important; } .wm-toolbar{ padding:10px 12px 18px !important; border-radius:18px 18px 0 0 !important; border-left:none !important; border-right:none !important; border-bottom:none !important; overflow-x:auto !important; -webkit-overflow-scrolling:touch; } .wm-toolbar .tool-row{ flex-wrap:nowrap !important; overflow-x:auto; -webkit-overflow-scrolling:touch; padding-bottom:4px; } /* Larger touch targets on mobile */ .wm-toolbar .tool-btn, .wm-toolbar .tool-select, .wm-toolbar .tool-number, .wm-toolbar .tool-color-wrap, .wm-toolbar .tool-range-wrap{ height:42px !important; font-size:14px; } /* Handles bigger for fingers */ .handle.rotate-handle,.handle.resize-handle{ width:32px !important; height:32px !important; font-size:18px !important; } /* Tap-to-edit hint badge on the text overlay */ #tapEditHint{ display:flex; align-items:center; gap:4px; position:absolute; bottom:-28px; left:50%; transform:translateX(-50%); background:rgba(37,99,235,.9); color:#fff; font-size:11px; font-weight:600; padding:3px 10px; border-radius:999px; white-space:nowrap; pointer-events:none; z-index:30; } } @media (min-width:601px){ #wp-pdf-watermark-tool #tapEditHint{display:none} }

#wp-pdf-watermark-tool .slider-row{ display:flex; gap:10px; align-items:center; } #wp-pdf-watermark-tool .slider-row input[type="range"]{ flex:1; } #wp-pdf-watermark-tool .slider-tag{ min-width:48px; text-align:center; padding:6px 8px; border-radius:999px; background:#f3f6fb; border:1px solid var(--line); font-size:12px; font-weight:700; color:#1248b5; }

#wp-pdf-watermark-tool .repeat-presets{ display:grid; grid-template-columns:repeat(5,minmax(0,1fr)); gap:10px; margin-top:8px; } #wp-pdf-watermark-tool .repeat-preset{ border:1px solid var(--line); background:#fff; border-radius:14px; padding:10px; text-align:left; display:flex; flex-direction:column; gap:8px; transition:.18s ease; } #wp-pdf-watermark-tool .repeat-preset:hover{border-color:#8eb6ff;box-shadow:0 8px 18px rgba(37,99,235,.08);transform:translateY(-1px)} #wp-pdf-watermark-tool .repeat-preset.active{border-color:var(--accent);background:#f7faff;box-shadow:0 0 0 2px rgba(37,99,235,.08)} #wp-pdf-watermark-tool .repeat-preset strong{font-size:12px;line-height:1.2} #wp-pdf-watermark-tool .repeat-preset span{font-size:11px;color:var(--muted);line-height:1.25} #wp-pdf-watermark-tool .preset-mini{ height:62px; border-radius:10px; border:1px solid #d9e3f5; background:linear-gradient(180deg,#ffffff,#f7faff); position:relative; overflow:hidden; } #wp-pdf-watermark-tool .preset-mini::before,#wp-pdf-watermark-tool .preset-mini::after{ content:""; position:absolute; inset:0; opacity:.9; } #wp-pdf-watermark-tool .preset-mini.grid::before{ background: radial-gradient(circle at 12px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 31px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 50px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 12px 31px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 31px 31px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 50px 31px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 12px 50px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 31px 50px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 50px 50px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.dense::before{ background:repeating-radial-gradient(circle at 0 0, rgba(37,99,235,.9) 0 1.6px, transparent 2px 12px); transform:translate(11px,11px); } #wp-pdf-watermark-tool .preset-mini.wide::before{ background: radial-gradient(circle at 10px 10px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 42px 10px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 10px 42px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 42px 42px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.diagonal::before{ background:repeating-linear-gradient(135deg, transparent 0 12px, rgba(37,99,235,.22) 12px 18px, transparent 18px 30px); } #wp-pdf-watermark-tool .preset-mini.diagonal::after{ background: radial-gradient(circle at 8px 50px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 22px 36px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 36px 22px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 50px 8px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.checker::before{ background: radial-gradient(circle at 12px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 36px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 24px 30px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 48px 30px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 12px 48px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 36px 48px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.ripple::before{ background: radial-gradient(circle at center, rgba(37,99,235,.18) 0 8px, transparent 9px 18px, rgba(37,99,235,.15) 19px 24px, transparent 25px); } #wp-pdf-watermark-tool .preset-mini.ripple::after{ background: radial-gradient(circle at center, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.stair::before{ background: radial-gradient(circle at 10px 50px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 22px 38px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 34px 26px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 46px 14px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.brick::before{ background: radial-gradient(circle at 8px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 28px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 48px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 18px 30px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 38px 30px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 8px 48px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 28px 48px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 48px 48px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.cross::before{ background: repeating-linear-gradient(45deg, transparent 0 8px, rgba(37,99,235,.18) 8px 10px, transparent 10px 18px), repeating-linear-gradient(-45deg, transparent 0 8px, rgba(37,99,235,.18) 8px 10px, transparent 10px 18px); } #wp-pdf-watermark-tool .preset-mini.cross::after{ background: radial-gradient(circle at 12px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 36px 36px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 12px 36px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 36px 12px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.diamond::before{ background: radial-gradient(circle at 31px 8px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 8px 31px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 31px 31px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 54px 31px, rgba(37,99,235,.85) 0 2px, transparent 2.5px), radial-gradient(circle at 31px 54px, rgba(37,99,235,.85) 0 2px, transparent 2.5px); } #wp-pdf-watermark-tool .preset-mini.diamond::after{ background: repeating-linear-gradient(45deg, transparent 0 14px, rgba(37,99,235,.10) 14px 16px, transparent 16px 30px), repeating-linear-gradient(-45deg, transparent 0 14px, rgba(37,99,235,.10) 14px 16px, transparent 16px 30px); }

#wp-pdf-watermark-tool .repeat-shortcuts{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px} #wp-pdf-watermark-tool .repeat-chip{ padding:8px 10px; border-radius:999px; border:1px solid #cfe0ff; background:#eff5ff; color:#1248b5; font-size:12px; font-weight:700; cursor:pointer; }

#wp-pdf-watermark-tool .pdf-drop-area{ border:2px dashed #cfe0ff; background:#f8fbff; border-radius:14px; padding:14px; text-align:center; transition:.2s ease; } #wp-pdf-watermark-tool .pdf-drop-area.dragover{ border-color:#2563eb; background:#eff5ff; box-shadow:0 0 0 4px rgba(37,99,235,.08); } #wp-pdf-watermark-tool .pdf-file-name{margin-top:10px;font-weight:700;color:#2563eb;word-break:break-word;display:flex;align-items:center;justify-content:center;gap:8px;flex-wrap:wrap} #wp-pdf-watermark-tool .pdf-file-name .pdf-badge{display:inline-flex;align-items:center;justify-content:center;width:28px;height:32px;border-radius:6px;background:#e53935;color:#fff;font-size:10px;font-weight:800;line-height:1;letter-spacing:.4px;box-shadow:0 4px 10px rgba(229,57,53,.18);position:relative;text-transform:uppercase} #wp-pdf-watermark-tool .pdf-file-name .pdf-badge::before{content:"";position:absolute;top:0;right:0;border-top:10px solid #ffcdd2;border-left:10px solid transparent;width:0;height:0;border-top-right-radius:6px} #wp-pdf-watermark-tool .pdf-file-meta{margin-top:4px;color:#667085} #wp-pdf-watermark-tool .pdf-drop-text{margin-top:8px;font-size:12px;color:#667085} #wp-pdf-watermark-tool .pdf-remove-btn{margin-top:12px;min-width:140px}

#wp-pdf-watermark-tool .repeat-image-layer{ position:absolute; inset:0; pointer-events:none; overflow:hidden; } #wp-pdf-watermark-tool .repeat-text-layer{ position:absolute; inset:0; pointer-events:none; overflow:hidden; z-index:15; } #wp-pdf-watermark-tool .repeat-image-layer.hidden,#wp-pdf-watermark-tool .repeat-text-layer.hidden{display:none} #wp-pdf-watermark-tool .repeat-image-item{ position:absolute; transform-origin:center center; pointer-events:none; user-select:none; -webkit-user-drag:none; }

#wp-pdf-watermark-tool .font-style-preview{ display:inline-flex; align-items:center; justify-content:center; min-width:28px; height:28px; padding:0 8px; border-radius:999px; border:1px solid var(--toolbar-line); font-size:11px; font-weight:700; white-space:nowrap; background:#eff5ff; } #wp-pdf-watermark-tool .btn-loading{ display:inline-flex; align-items:center; justify-content:center; gap:8px; } #wp-pdf-watermark-tool .spinner{ width:14px; height:14px; border:2px solid rgba(255,255,255,.35); border-top-color:#fff; border-radius:50%; animation:spin .8s linear infinite; } #wp-pdf-watermark-tool .secondary .spinner{ border-color:rgba(37,99,235,.22); border-top-color:#2563eb; } @keyframes spin{ to{transform:rotate(360deg)} } #wp-pdf-watermark-tool .hidden-native{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; } #wp-pdf-watermark-tool .font-panel{ display:flex; flex-direction:column; gap:10px; padding:12px; border:1px solid var(--line); border-radius:12px; background:linear-gradient(180deg,#ffffff,#f8fbff); } #wp-pdf-watermark-tool .lang-switch{ display:flex; gap:8px; flex-wrap:wrap; } #wp-pdf-watermark-tool .lang-dropdown{ min-width:220px; } #wp-pdf-watermark-tool .lang-chip{ appearance:none; border:1px solid #cfe0ff; background:#fff; color:#1248b5; border-radius:999px; padding:9px 14px; font-size:12px; font-weight:700; cursor:pointer; transition:.18s ease; } #wp-pdf-watermark-tool .lang-chip.active{ background:#2563eb; border-color:#2563eb; color:#fff; box-shadow:0 8px 18px rgba(37,99,235,.18); } #wp-pdf-watermark-tool .lang-chip .lang-note{ opacity:.7; font-weight:600; margin-inline-start:4px; } #wp-pdf-watermark-tool .font-meta{ display:flex; justify-content:space-between; gap:10px; align-items:center; flex-wrap:wrap; } #wp-pdf-watermark-tool .font-context{ font-size:12px; color:var(--muted); } #wp-pdf-watermark-tool .font-sample{ font-size:13px; color:#1248b5; background:#eff5ff; border:1px solid #cfe0ff; padding:6px 10px; border-radius:999px; white-space:nowrap; } #wp-pdf-watermark-tool .font-panel.rtl #fontFamily{ direction:ltr; text-align:left; } #wp-pdf-watermark-tool .font-panel.rtl #fontFamily option{ direction:ltr; text-align:left; } #wp-pdf-watermark-tool .font-panel.rtl .font-sample{ direction:rtl; text-align:right; }

#wp-pdf-watermark-tool /* ── Custom Repeat Style Picker ── */ .repeat-style-picker{ position:relative; display:inline-flex; align-items:center; height:34px; } #wp-pdf-watermark-tool .repeat-style-trigger{ display:inline-flex; align-items:center; gap:6px; height:34px; padding:0 8px; border:1px solid var(--toolbar-line); border-radius:10px; background:var(--toolbar-bg) !important; color:var(--toolbar-blue) !important; font-size:12px; font-weight:600; cursor:pointer; white-space:nowrap; min-width:110px; max-width:140px; } #wp-pdf-watermark-tool .repeat-style-trigger .rs-mini{ width:22px;height:18px;border-radius:4px;border:1px solid #d9e3f5; background:linear-gradient(180deg,#fff,#f7faff); flex:0 0 auto;position:relative;overflow:hidden; } #wp-pdf-watermark-tool .repeat-style-trigger .rs-label{ flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; } #wp-pdf-watermark-tool .repeat-style-trigger .rs-arrow{font-size:10px;opacity:.7} #wp-pdf-watermark-tool .repeat-style-dropdown{ position:absolute; top:calc(100% + 4px); left:0; z-index:9999; background:#fff; border:1px solid #cfe0ff; border-radius:12px; box-shadow:0 12px 28px rgba(37,99,235,.16); padding:6px; display:none; flex-direction:column; gap:3px; min-width:188px; } #wp-pdf-watermark-tool .repeat-style-dropdown.open{display:flex} #wp-pdf-watermark-tool .rs-option{ display:flex; align-items:center; gap:8px; padding:6px 8px; border-radius:8px; cursor:pointer; transition:.14s ease; border:1px solid transparent; } #wp-pdf-watermark-tool .rs-option:hover{background:#eff5ff;border-color:#cfe0ff} #wp-pdf-watermark-tool .rs-option.active{background:#eff5ff;border-color:var(--accent)} #wp-pdf-watermark-tool .rs-option .rs-demo{ width:44px;height:34px;border-radius:6px;border:1px solid #d9e3f5; background:linear-gradient(180deg,#fff,#f7faff); flex:0 0 auto;position:relative;overflow:hidden; } #wp-pdf-watermark-tool /* reuse preset-mini patterns on rs-demo */ .rs-demo.grid::before,#wp-pdf-watermark-tool .rs-demo.dense::before,#wp-pdf-watermark-tool .rs-demo.wide::before, #wp-pdf-watermark-tool .rs-demo.diagonal::before,#wp-pdf-watermark-tool .rs-demo.diagonal::after, #wp-pdf-watermark-tool .rs-demo.checker::before,#wp-pdf-watermark-tool .rs-demo.ripple::before,#wp-pdf-watermark-tool .rs-demo.ripple::after, #wp-pdf-watermark-tool .rs-demo.stair::before{ content:"";position:absolute;inset:0;opacity:.9; } #wp-pdf-watermark-tool .rs-demo.grid::before{ background: radial-gradient(circle at 8px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 22px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 36px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 8px 20px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 22px 20px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 36px 20px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 8px 32px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 22px 32px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 36px 32px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.dense::before{ background:repeating-radial-gradient(circle at 0 0,rgba(37,99,235,.9) 0 1.2px,transparent 1.5px 9px); transform:translate(8px,8px); } #wp-pdf-watermark-tool .rs-demo.wide::before{ background: radial-gradient(circle at 8px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 30px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 8px 26px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 30px 26px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.diagonal::before{ background:repeating-linear-gradient(135deg,transparent 0 9px,rgba(37,99,235,.2) 9px 13px,transparent 13px 22px); } #wp-pdf-watermark-tool .rs-demo.diagonal::after{ background: radial-gradient(circle at 6px 34px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 17px 23px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 28px 12px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 39px 2px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.checker::before{ background: radial-gradient(circle at 9px 9px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 27px 9px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 18px 22px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 36px 22px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 9px 34px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 27px 34px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.ripple::before{ background: radial-gradient(circle at center,rgba(37,99,235,.16) 0 6px,transparent 7px 14px,rgba(37,99,235,.12) 15px 18px,transparent 19px); } #wp-pdf-watermark-tool .rs-demo.ripple::after{ background:radial-gradient(circle at center,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.stair::before{ background: radial-gradient(circle at 7px 30px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 17px 20px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 27px 11px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 37px 2px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.brick::before{ background: radial-gradient(circle at 6px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 20px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 34px 8px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 13px 21px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 27px 21px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 41px 21px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 6px 32px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 20px 32px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 34px 32px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.cross::before{ background: repeating-linear-gradient(45deg,transparent 0 6px,rgba(37,99,235,.18) 6px 8px,transparent 8px 14px), repeating-linear-gradient(-45deg,transparent 0 6px,rgba(37,99,235,.18) 6px 8px,transparent 8px 14px); } #wp-pdf-watermark-tool .rs-demo.cross::after{ background: radial-gradient(circle at 9px 9px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 28px 28px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 9px 28px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 28px 9px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.diamond::before{ background: radial-gradient(circle at 22px 6px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 6px 22px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 22px 22px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 38px 22px,rgba(37,99,235,.85) 0 1.5px,transparent 2px), radial-gradient(circle at 22px 38px,rgba(37,99,235,.85) 0 1.5px,transparent 2px); } #wp-pdf-watermark-tool .rs-demo.diamond::after{ background: repeating-linear-gradient(45deg,transparent 0 10px,rgba(37,99,235,.10) 10px 12px,transparent 12px 22px), repeating-linear-gradient(-45deg,transparent 0 10px,rgba(37,99,235,.10) 10px 12px,transparent 12px 22px); } #wp-pdf-watermark-tool .rs-option-name{font-size:12px;font-weight:700;color:#1248b5} #wp-pdf-watermark-tool .rs-option-desc{font-size:10px;color:#667085} #wp-pdf-watermark-tool /* toolbar fit improvements */ .wm-toolbar .tool-row{flex-wrap:wrap;gap:5px} #wp-pdf-watermark-tool #imageToolbar .tool-row{flex-wrap:wrap} #wp-pdf-watermark-tool .repeat-style-picker{flex-shrink:0} #wp-pdf-watermark-tool #toolbarFont, #wp-pdf-watermark-tool #toolbarFont option{ direction:ltr; text-align:left; } @media (max-width:1080px){ #wp-pdf-watermark-tool .options-grid{grid-template-columns:repeat(2,1fr)} #wp-pdf-watermark-tool .actions{grid-template-columns:repeat(2,1fr)} #wp-pdf-watermark-tool .quick-grid{grid-template-columns:repeat(2,1fr)} #wp-pdf-watermark-tool .repeat-presets{grid-template-columns:repeat(3,minmax(0,1fr))} #wp-pdf-watermark-tool .uploader-panel{flex-direction:column} } @media (max-width:720px){ #wp-pdf-watermark-tool .options-grid,#wp-pdf-watermark-tool .actions,#wp-pdf-watermark-tool .quick-grid{grid-template-columns:1fr} #wp-pdf-watermark-tool .field.two{grid-column:auto} #wp-pdf-watermark-tool .preview-wrap{min-height:460px} #wp-pdf-watermark-tool .repeat-presets{grid-template-columns:repeat(2,minmax(0,1fr))} #wp-pdf-watermark-tool .thumb-card{align-items:flex-start} #wp-pdf-watermark-tool .page-jump input{width:70px} }



Advanced PDF Watermark Tool

Preview and exported PDF use one full-page overlay system, so text and logo positions stay aligned.
Upload PDF
Upload the PDF first. This row stays horizontal on desktop for a cleaner layout.

No file selected
Size: — | Pages: —
or drag & drop PDF here
Uploading PDF…
0%

Supported: PDF
Text Watermark
Set text watermark content and options here.

Enable text watermark


Apply watermark to all pages.



English font collection
Sample: Confidential


Optional: adds a clickable link to the text watermark in the downloaded PDF.

Tile across selected pages



48


















Choose a visual model to set text repeat layout.
Logo Watermark
Upload your logo and control logo watermark separately from text watermark.

Enable logo watermark


Apply logo watermark to all pages.

Logo thumbnail preview
No logo selected
PNG or JPG only


Optional: makes the logo watermark clickable in the downloaded PDF.

Tile logo across selected pages


140















Choose an independent repeat style for logo watermark.









Single click to select or drag to reposition. Double-click text to edit. Repeat watermark is also draggable. Preview updates in real-time.
Editable PDF View
Export uses the same preview canvas coordinate system.

Page 1 / 1

Upload a PDF to see the Editable PDF View here.


✎ tap to edit


⬤




Regular
↔
◌


Watermark logo preview


↔
◌


const pdfjsLib = window.pdfjsLib || window['pdfjs-dist/build/pdf']; if (!window.PDFLib || !pdfjsLib) { alert('PDF tools could not load. Please check your internet connection and reload this page.'); throw new Error('PDF libraries failed to load.'); } pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';

const { PDFDocument, PDFName, PDFArray, PDFString } = window.PDFLib; const LANGUAGE_OPTIONS = { english: { label: 'English', sample: 'Sample: Confidential', rtl: false }, arabic: { label: 'Arabic', sample: '\u0645\u062b\u0627\u0644: \u0633\u0631\u064a \u0644\u0644\u063a\u0627\u064a\u0629', rtl: true }, hebrew: { label: 'Hebrew', sample: '\u05d3\u05d5\u05d2\u05de\u05d4: \u05e1\u05d5\u05d3\u05d9', rtl: true }, hindi: { label: 'Hindi', sample: '\u0909\u0926\u093e\u0939\u0930\u0923: \u0917\u094b\u092a\u0928\u0940\u092f', rtl: false }, urdu: { label: 'Urdu', sample: '\u0645\u062b\u0627\u0644: \u062e\u0641\u06cc\u06c1', rtl: true }, persian: { label: 'Persian', sample: '\u0646\u0645\u0648\u0646\u0647: \u0645\u062d\u0631\u0645\u0627\u0646\u0647', rtl: true }, russian: { label: 'Russian', sample: '\u041f\u0440\u0438\u043c\u0435\u0440: \u0421\u0435\u043a\u0440\u0435\u0442\u043d\u043e', rtl: false }, japanese: { label: 'Japanese', sample: '\u4f8b: \u6a5f\u5bc6', rtl: false }, chinese: { label: 'Chinese', sample: '\u793a\u4f8b\uff1a\u673a\u5bc6', rtl: false }, german: { label: 'German', sample: 'Beispiel: Vertraulich', rtl: false }, spanish: { label: 'Spanish', sample: 'Ejemplo: Confidencial', rtl: false } }; const ARABIC_SCRIPT_FONT_CHOICES = [ { value: 'Tajawal', label: 'Tajawal - Clean Arabic Sans', stack: '"Tajawal", sans-serif' }, { value: 'Amiri', label: 'Amiri - Traditional Arabic Serif', stack: '"Amiri", serif' }, { value: 'Almarai', label: 'Almarai - Balanced Arabic Sans', stack: '"Almarai", sans-serif' }, { value: 'Noto Naskh Arabic', label: 'Noto Naskh Arabic - Book Style', stack: '"Noto Naskh Arabic", serif' }, { value: 'Cairo', label: 'Cairo - Modern Arabic Sans', stack: '"Cairo", sans-serif' }, { value: 'Changa', label: 'Changa - Display Arabic', stack: '"Changa", sans-serif' }, { value: 'Reem Kufi', label: 'Reem Kufi - Kufi Style', stack: '"Reem Kufi", sans-serif' }, { value: 'Scheherazade New', label: 'Scheherazade New - Classical Arabic', stack: '"Scheherazade New", serif' } ]; const FONT_CHOICES = { english: [ { value: 'Arial', label: 'Arial - Standard Sans', stack: 'Arial, Helvetica, sans-serif' }, { value: 'Helvetica', label: 'Helvetica - Classic Sans', stack: 'Helvetica, Arial, sans-serif' }, { value: 'Roboto', label: 'Roboto - Modern Sans', stack: '"Roboto", Arial, sans-serif' }, { value: 'Inter', label: 'Inter - UI Sans', stack: '"Inter", Arial, sans-serif' }, { value: 'DM Sans', label: 'DM Sans - Friendly Sans', stack: '"DM Sans", Arial, sans-serif' }, { value: 'Nunito', label: 'Nunito - Rounded Sans', stack: '"Nunito", Arial, sans-serif' }, { value: 'Comic Neue', label: 'Comic Neue - Comic Sans Style', stack: '"Comic Neue", "Comic Sans MS", cursive' }, { value: 'Georgia', label: 'Georgia - Editorial Serif', stack: 'Georgia, "Times New Roman", serif' }, { value: 'Lora', label: 'Lora - Elegant Serif', stack: '"Lora", Georgia, serif' }, { value: 'Playfair Display', label: 'Playfair Display - Premium Serif', stack: '"Playfair Display", Georgia, serif' }, { value: 'Merriweather', label: 'Merriweather - Reading Serif', stack: '"Merriweather", Georgia, serif' }, { value: 'Times New Roman', label: 'Times New Roman - Classic Serif', stack: '"Times New Roman", Times, serif' }, { value: 'Courier New', label: 'Courier New - Monospace', stack: '"Courier New", Courier, monospace' }, { value: 'Verdana', label: 'Verdana - Wide Sans', stack: 'Verdana, Arial, sans-serif' } ], arabic: ARABIC_SCRIPT_FONT_CHOICES, hebrew: [ { value: 'Heebo', label: 'Heebo - Modern Hebrew Sans', stack: '"Heebo", sans-serif' }, { value: 'Assistant', label: 'Assistant - UI Hebrew Sans', stack: '"Assistant", sans-serif' }, { value: 'Rubik', label: 'Rubik - Rounded Hebrew Sans', stack: '"Rubik", sans-serif' }, { value: 'Noto Sans Hebrew', label: 'Noto Sans Hebrew - Neutral Sans', stack: '"Noto Sans Hebrew", sans-serif' } ], hindi: [ { value: 'Hind', label: 'Hind - Clean Devanagari Sans', stack: '"Hind", sans-serif' }, { value: 'Noto Sans Devanagari', label: 'Noto Sans Devanagari - Balanced Sans', stack: '"Noto Sans Devanagari", sans-serif' }, { value: 'Teko', label: 'Teko - Display Devanagari', stack: '"Teko", sans-serif' } ], urdu: [ { value: 'Noto Nastaliq Urdu', label: 'Noto Nastaliq Urdu - Classic Nastaliq', stack: '"Noto Nastaliq Urdu", serif' }, { value: 'Lateef', label: 'Lateef - Traditional Urdu Script', stack: '"Lateef", serif' }, { value: 'Noto Naskh Arabic', label: 'Noto Naskh Arabic - Clean Naskh', stack: '"Noto Naskh Arabic", serif' }, { value: 'Tajawal', label: 'Tajawal - Modern Sans', stack: '"Tajawal", sans-serif' } ], persian: [ { value: 'Vazirmatn', label: 'Vazirmatn - Modern Persian Sans', stack: '"Vazirmatn", sans-serif' }, { value: 'Markazi Text', label: 'Markazi Text - Persian Serif', stack: '"Markazi Text", serif' }, { value: 'Mirza', label: 'Mirza - Traditional Persian Display', stack: '"Mirza", serif' }, { value: 'Amiri', label: 'Amiri - Elegant Persian/Arabic Serif', stack: '"Amiri", serif' } ], russian: [ { value: 'Roboto', label: 'Roboto - Modern Cyrillic Sans', stack: '"Roboto", Arial, sans-serif' }, { value: 'Rubik', label: 'Rubik - Rounded Cyrillic Sans', stack: '"Rubik", Arial, sans-serif' }, { value: 'PT Sans', label: 'PT Sans - Classic Russian Sans', stack: '"PT Sans", Arial, sans-serif' }, { value: 'PT Serif', label: 'PT Serif - Editorial Cyrillic Serif', stack: '"PT Serif", Georgia, serif' }, { value: 'Noto Sans', label: 'Noto Sans - Neutral Cyrillic Sans', stack: '"Noto Sans", Arial, sans-serif' } ], japanese: [ { value: 'Noto Sans JP', label: 'Noto Sans JP - Balanced Japanese Sans', stack: '"Noto Sans JP", sans-serif' }, { value: 'M PLUS 1p', label: 'M PLUS 1p - UI Japanese Sans', stack: '"M PLUS 1p", sans-serif' }, { value: 'Zen Kaku Gothic New', label: 'Zen Kaku Gothic New - Clear Gothic', stack: '"Zen Kaku Gothic New", sans-serif' }, { value: 'Kosugi Maru', label: 'Kosugi Maru - Rounded Japanese', stack: '"Kosugi Maru", sans-serif' }, { value: 'Sawarabi Mincho', label: 'Sawarabi Mincho - Japanese Serif', stack: '"Sawarabi Mincho", serif' } ], chinese: [ { value: 'Noto Sans SC', label: 'Noto Sans SC - Modern Simplified Chinese', stack: '"Noto Sans SC", sans-serif' }, { value: 'ZCOOL QingKe HuangYou', label: 'ZCOOL QingKe HuangYou - Display Chinese', stack: '"ZCOOL QingKe HuangYou", sans-serif' }, { value: 'ZCOOL XiaoWei', label: 'ZCOOL XiaoWei - Elegant Chinese Serif', stack: '"ZCOOL XiaoWei", serif' }, { value: 'Ma Shan Zheng', label: 'Ma Shan Zheng - Brush Chinese', stack: '"Ma Shan Zheng", cursive' } ], german: [ { value: 'Inter', label: 'Inter - Clean Modern Sans', stack: '"Inter", Arial, sans-serif' }, { value: 'Montserrat', label: 'Montserrat - Popular Display Sans', stack: '"Montserrat", Arial, sans-serif' }, { value: 'Source Sans 3', label: 'Source Sans 3 - Readable UI Sans', stack: '"Source Sans 3", Arial, sans-serif' }, { value: 'Merriweather', label: 'Merriweather - Editorial Serif', stack: '"Merriweather", Georgia, serif' }, { value: 'Poppins', label: 'Poppins - Geometric Sans', stack: '"Poppins", Arial, sans-serif' } ], spanish: [ { value: 'Roboto', label: 'Roboto - Modern Sans', stack: '"Roboto", Arial, sans-serif' }, { value: 'Nunito', label: 'Nunito - Friendly Rounded Sans', stack: '"Nunito", Arial, sans-serif' }, { value: 'Montserrat', label: 'Montserrat - Popular Headline Sans', stack: '"Montserrat", Arial, sans-serif' }, { value: 'Lora', label: 'Lora - Elegant Serif', stack: '"Lora", Georgia, serif' }, { value: 'Poppins', label: 'Poppins - Clean Geometric Sans', stack: '"Poppins", Arial, sans-serif' } ] };

const pdfInput = byId('pdfInput'); const wmImageInput = byId('wmImageInput');

const pageRangeMode = byId('pageRangeMode'); const pageRange = byId('pageRange'); const pageRangeHelper = byId('pageRangeHelper');

const imagePageRangeMode = byId('imagePageRangeMode'); const imagePageRange = byId('imagePageRange'); const imagePageRangeHelper = byId('imagePageRangeHelper');

const showTextWatermark = byId('showTextWatermark'); const showLogoWatermark = byId('showLogoWatermark');

const textBold = byId('textBold'); const textItalic = byId('textItalic'); const textUnderline = byId('textUnderline'); const repeatWatermark = byId('repeatWatermark'); const repeatLogoWatermark = byId('repeatLogoWatermark'); const repeatGap = byId('repeatGap'); const wmText = byId('wmText'); const enableTextLink = byId('enableTextLink'); const wmTextLink = byId('wmTextLink'); const enableImageLink = byId('enableImageLink'); const wmImageLink = byId('wmImageLink'); const fontSize = byId('fontSize'); const fontSizeRange = byId('fontSizeRange'); const fontSizeTag = byId('fontSizeTag'); const languageSelect = byId('languageSelect'); const languageSwitch = byId('languageSwitch'); const extraLanguageSelect = byId('extraLanguageSelect'); const fontPanel = watermarkToolRoot.querySelector('.font-panel'); const fontFamily = byId('fontFamily'); const fontContext = byId('fontContext'); const fontSample = byId('fontSample'); const fontColor = byId('fontColor'); const textRotation = byId('textRotation'); const textLayerMode = byId('textLayerMode'); const imageWidth = byId('imageWidth'); const imageWidthRange = byId('imageWidthRange'); const imageWidthTag = byId('imageWidthTag'); const imageRotation = byId('imageRotation'); const textOpacity = byId('textOpacity'); const textOpacityLabel = byId('textOpacityLabel'); const imageOpacity = byId('imageOpacity'); const imageOpacityLabel = byId('imageOpacityLabel'); const imageLayerMode = byId('imageLayerMode'); const generateBtn = byId('generateBtn'); const downloadBtn = byId('downloadBtn'); const resetTextPosBtn = byId('resetTextPosBtn'); const resetImagePosBtn = byId('resetImagePosBtn'); const actionStatus = byId('actionStatus');

const pdfLabel = byId('pdfLabel'); const pdfFileName = byId('pdfFileName'); const pdfFileMeta = byId('pdfFileMeta'); const pdfDropArea = byId('pdfDropArea'); const pdfDropText = byId('pdfDropText'); const removePdfBtn = byId('removePdfBtn'); const pdfUploadProgress = byId('pdfUploadProgress'); const pdfUploadStatus = byId('pdfUploadStatus'); const pdfUploadPercent = byId('pdfUploadPercent'); const pdfUploadBar = byId('pdfUploadBar');

const logoThumbPreview = byId('logoThumbPreview'); const logoFileName = byId('logoFileName'); const logoFileMeta = byId('logoFileMeta'); const removeLogoBtn = byId('removeLogoBtn');

const pdfCanvas = byId('pdfCanvas'); const stage = byId('stage'); const previewStatus = byId('previewStatus'); const pageControls = byId('pageControls'); const prevPage = byId('prevPage'); const nextPage = byId('nextPage'); const pageNum = byId('pageNum'); const pageCount = byId('pageCount'); const pageJumpInput = byId('pageJumpInput'); const pageJumpBtn = byId('pageJumpBtn');

const textOverlay = byId('textOverlay'); const textVisual = byId('textVisual'); const textPreview = byId('textPreview'); const textRotateHandle = byId('textRotateHandle'); const textResizeHandle = byId('textResizeHandle');

const textToolbarShell = byId('textToolbarShell'); const textToolbar = byId('textToolbar'); const toolbarBoldBtn = byId('toolbarBoldBtn'); const toolbarItalicBtn = byId('toolbarItalicBtn'); const toolbarUnderlineBtn = byId('toolbarUnderlineBtn'); const toolbarColor = byId('toolbarColor'); const toolbarOpacity = byId('toolbarOpacity'); const toolbarLanguage = byId('toolbarLanguage'); const toolbarFont = byId('toolbarFont'); const toolbarFontStylePreview = byId('toolbarFontStylePreview'); const toolbarSize = byId('toolbarSize'); const toolbarSizeRange = byId('toolbarSizeRange'); const toolbarCenterBtn = byId('toolbarCenterBtn'); const toolbarTextLayerBtn = byId('toolbarTextLayerBtn'); const textLayerText = byId('textLayerText'); const textToolbarMoreBtn = byId('textToolbarMoreBtn'); const toolbarRemoveTextBtn = byId('toolbarRemoveTextBtn'); const textToolbarCloseBtn = byId('textToolbarCloseBtn'); let textToolbarLessBtn = byId('textToolbarLessBtn');

if (textToolbarMoreBtn) { textToolbarMoreBtn.querySelector('.tool-icon').innerHTML = '▾'; } if (!textToolbarLessBtn && toolbarRemoveTextBtn) { toolbarRemoveTextBtn.insertAdjacentHTML('afterend', ''); textToolbarLessBtn = byId('textToolbarLessBtn'); } if (textToolbarMoreBtn) { [ toolbarSize, toolbarSizeRange?.closest('.tool-range-wrap'), toolbarOpacity?.closest('.tool-range-wrap'), toolbarTextLayerBtn ].filter(Boolean).forEach(control => { textToolbarMoreBtn.parentElement.insertBefore(control, textToolbarMoreBtn); }); }

const imageOverlay = byId('imageOverlay'); const imageVisual = byId('imageVisual'); const imagePreview = byId('imagePreview'); const imageRotateHandle = byId('imageRotateHandle'); const imageResizeHandle = byId('imageResizeHandle'); const repeatImageLayer = byId('repeatImageLayer'); const repeatTextLayer = byId('repeatTextLayer');

const imageToolbarShell = byId('imageToolbarShell'); const imageToolbar = byId('imageToolbar'); const toolbarImageLayerBtn = byId('toolbarImageLayerBtn'); const imageLayerText = byId('imageLayerText'); const toolbarImageOpacity = byId('toolbarImageOpacity'); const toolbarImageSize = byId('toolbarImageSize'); const toolbarImageSizeRange = byId('toolbarImageSizeRange'); const imageToolbarMoreBtn = byId('imageToolbarMoreBtn'); const toolbarRemoveImageBtn = byId('toolbarRemoveImageBtn'); const imageToolbarCloseBtn = byId('imageToolbarCloseBtn'); let imageToolbarLessBtn = byId('imageToolbarLessBtn');

if (imageToolbarMoreBtn) { imageToolbarMoreBtn.querySelector('.tool-icon').innerHTML = '▾'; } if (!imageToolbarLessBtn && toolbarRemoveImageBtn) { toolbarRemoveImageBtn.insertAdjacentHTML('afterend', ''); imageToolbarLessBtn = byId('imageToolbarLessBtn'); }

const QUICK_POSITION_OPTIONS = [ { value: 'custom', label: 'Position' }, { value: 'top-left', label: 'Top Left' }, { value: 'top-middle', label: 'Top Middle' }, { value: 'center', label: 'Center' }, { value: 'bottom-right', label: 'Bottom Right' } ]; const PAGE_RANGE_OPTIONS = [ { value: 'all', label: 'All pages' }, { value: 'first', label: 'First page only' }, { value: 'first2', label: 'First and second page only' }, { value: 'first3', label: 'First 3 pages' }, { value: 'firstLast', label: 'First and last page' }, { value: 'custom', label: 'Page range' } ];

function createToolbarSelect(id, title, options) { const select = document.createElement('select'); select.id = id; select.title = title; select.className = 'tool-select tool-compact-select'; options.forEach(item => { const option = document.createElement('option'); option.value = item.value; option.textContent = item.label; if (item.gap) option.dataset.gap = item.gap; select.appendChild(option); }); return select; }

function createRepeatStylePicker(id, title, options) { const wrapper = document.createElement('div'); wrapper.className = 'repeat-style-picker'; wrapper.id = id + 'Wrapper';

// first option desc lookup from section cards function getDesc(pattern) { const btn = watermarkToolRoot.querySelector(`.repeat-preset[data-pattern="${pattern}"]`); return btn ? (btn.querySelector('span')?.textContent || '') : ''; }

const firstOpt = options[0] || {}; const trigger = document.createElement('button'); trigger.type = 'button'; trigger.className = 'repeat-style-trigger tool-btn'; trigger.title = title; trigger.innerHTML = `

${firstOpt.label || ''}▾`;

const dropdown = document.createElement('div'); dropdown.className = 'repeat-style-dropdown';

options.forEach((item, idx) => { const opt = document.createElement('div'); opt.className = 'rs-option' + (idx === 0 ? ' active' : ''); opt.dataset.value = item.value; opt.dataset.gap = item.gap || ''; const desc = getDesc(item.value); opt.innerHTML = `

${item.label}${desc}

`; dropdown.appendChild(opt); });

wrapper.appendChild(trigger); wrapper.appendChild(dropdown);

// hidden select for full compatibility with existing event listeners const hiddenSelect = document.createElement('select'); hiddenSelect.id = id; hiddenSelect.style.display = 'none'; options.forEach(item => { const o = document.createElement('option'); o.value = item.value; o.textContent = item.label; if (item.gap) o.dataset.gap = item.gap; hiddenSelect.appendChild(o); }); wrapper.appendChild(hiddenSelect);

trigger.addEventListener('click', (e) => { e.stopPropagation(); // close all other open dropdowns watermarkToolRoot.querySelectorAll('.repeat-style-dropdown.open').forEach(d => { if (d !== dropdown) d.classList.remove('open'); }); dropdown.classList.toggle('open'); });

dropdown.addEventListener('click', (e) => { const optEl = e.target.closest('.rs-option'); if (!optEl) return; dropdown.querySelectorAll('.rs-option').forEach(o => o.classList.remove('active')); optEl.classList.add('active'); const val = optEl.dataset.value; const label = optEl.querySelector('.rs-option-name').textContent; trigger.querySelector('.rs-mini').className = `rs-mini ${val}`; trigger.querySelector('.rs-label').textContent = label; hiddenSelect.value = val; hiddenSelect.dispatchEvent(new Event('change', { bubbles: true })); dropdown.classList.remove('open'); });

document.addEventListener('click', () => dropdown.classList.remove('open'));

wrapper._hiddenSelect = hiddenSelect; return wrapper; }

function createToolbarCheck(id, labelText, title) { const label = document.createElement('label'); label.className = 'tool-check'; label.title = title; const input = document.createElement('input'); input.id = id; input.type = 'checkbox'; const span = document.createElement('span'); span.textContent = labelText; label.append(input, span); return { label, input }; }

function getRepeatStyleOptions(group) { return Array.from(watermarkToolRoot.querySelectorAll(`.repeat-preset[data-group="${group}"]`)).map(btn => ({ value: btn.dataset.pattern, label: btn.dataset.label || btn.querySelector('strong')?.textContent || btn.textContent.trim(), gap: btn.dataset.gap })); }

const toolbarTextQuickPosition = createToolbarSelect('toolbarTextQuickPosition', 'Text Quick Position', QUICK_POSITION_OPTIONS); const toolbarLogoQuickPosition = createToolbarSelect('toolbarLogoQuickPosition', 'Logo Quick Position', QUICK_POSITION_OPTIONS); const toolbarLogoPageRange = createToolbarSelect('toolbarLogoPageRange', 'Logo Pages', PAGE_RANGE_OPTIONS); const toolbarTextRepeatStylePicker = createRepeatStylePicker('toolbarTextRepeatStyle', 'Text Repeat Styles', getRepeatStyleOptions('text')); const toolbarLogoRepeatStylePicker = createRepeatStylePicker('toolbarLogoRepeatStyle', 'Logo Repeat Styles', getRepeatStyleOptions('logo')); const toolbarTextRepeatStyle = toolbarTextRepeatStylePicker.querySelector('#toolbarTextRepeatStyle'); const toolbarLogoRepeatStyle = toolbarLogoRepeatStylePicker.querySelector('#toolbarLogoRepeatStyle'); const toolbarTextRepeatControl = createToolbarCheck('toolbarTextRepeatToggle', 'Repeat', 'Text Repeat Watermark'); const toolbarLogoRepeatControl = createToolbarCheck('toolbarLogoRepeatToggle', 'Repeat', 'Logo Repeat Watermark'); const toolbarTextRepeatToggle = toolbarTextRepeatControl.input; const toolbarLogoRepeatToggle = toolbarLogoRepeatControl.input;

if (textToolbarMoreBtn && toolbarSize?.parentElement) { [toolbarTextQuickPosition, toolbarTextRepeatControl.label, toolbarTextRepeatStylePicker].forEach(control => { toolbarSize.parentElement.insertBefore(control, toolbarSize); }); }

if (imageToolbarMoreBtn?.parentElement) { [toolbarLogoQuickPosition, toolbarLogoRepeatControl.label, toolbarLogoRepeatStylePicker].forEach(control => { imageToolbarMoreBtn.parentElement.insertBefore(control, imageToolbarMoreBtn); }); } if (toolbarImageLayerBtn?.parentElement) { toolbarImageLayerBtn.parentElement.insertBefore(toolbarLogoPageRange, toolbarImageLayerBtn); }

let pdfBytes = null; let pdfDoc = null; let pdfPageIndex = 1; let previewReady = false; let currentFileName = 'watermarked-pdf'; let generatedPdfBlob = null; let generatedPdfUrl = ''; let wmImageBytes = null; let wmImageMime = null; let wmImageObjectUrl = null;

const textState = { xRatio: 0.5, yRatio: 0.15, rotation: 0, size: 48, opacity: 0.80, language: 'english', overPdf: true }; const textRepeatState = { xRatio: 0.5, yRatio: 0.5 }; const imageState = { xRatio: 0.5, yRatio: 0.5, rotation: 0, width: 140, overPdf: true, opacity: 0.80 }; const textRepeatSettings = { pattern: 'grid', label: 'Classic Grid' }; const logoRepeatSettings = { pattern: 'grid', label: 'Classic Grid' };

let activeDrag = null, dragOffsetX = 0, dragOffsetY = 0; let activeResize = null, resizeStartDistance = 0, resizeStartValue = 0; let activeRotate = null;

textPreview.contentEditable = 'false';

function getFontChoices(language = 'english') { return FONT_CHOICES[language] || FONT_CHOICES.english; }

function getLanguageSample(language) { return LANGUAGE_OPTIONS[language]?.sample || LANGUAGE_OPTIONS.english.sample; }

function isRtlLanguage(language) { return Boolean(LANGUAGE_OPTIONS[language]?.rtl); }

function updateLanguageUi(language) { languageSwitch.querySelectorAll('[data-language]').forEach(btn => { btn.classList.toggle('active', btn.dataset.language === language); }); if (!['hebrew', 'hindi', 'urdu', 'persian', 'russian', 'japanese', 'chinese', 'german', 'spanish'].includes(language)) { extraLanguageSelect.value = ''; } else { extraLanguageSelect.value = language; } fontPanel.classList.toggle('rtl', isRtlLanguage(language)); fontContext.textContent = `${LANGUAGE_OPTIONS[language]?.label || 'English'} font collection`; fontSample.textContent = getLanguageSample(language); fontSample.style.fontFamily = getPreviewFontStack(fontFamily.value); fontSample.dir = isRtlLanguage(language) ? 'rtl' : 'ltr'; fontContext.dir = 'ltr'; }

function updateSelectPreviewStyle(selectEl, familyValue) { const source = Object.values(FONT_CHOICES).flat(); const meta = source.find(font => font.value === familyValue); if (meta) selectEl.style.fontFamily = meta.stack; selectEl.style.direction = 'ltr'; selectEl.style.textAlign = 'left'; }

function getStyleLabel() { const bits = []; bits.push(textBold.checked ? 'Bold' : 'Regular'); if (textItalic.checked) bits.push('Italic'); if (textUnderline.checked) bits.push('Underline'); return bits.join(' / '); }

function populateFontMenus(language = 'english', preferredValue = '') { const fonts = getFontChoices(language); const fallback = fonts[0]?.value || ''; const nextValue = fonts.some(font => font.value === preferredValue) ? preferredValue : fallback;

[fontFamily, toolbarFont].forEach(selectEl => { selectEl.innerHTML = ''; fonts.forEach(font => { const option = document.createElement('option'); option.value = font.value; option.textContent = font.label; option.style.fontFamily = font.stack; selectEl.appendChild(option); }); selectEl.value = nextValue; updateSelectPreviewStyle(selectEl, nextValue); });

textState.language = language; languageSelect.value = language; toolbarLanguage.value = language; updateLanguageUi(language); }

function syncLanguageAndFonts(language = languageSelect.value, preferredFont = fontFamily.value) { populateFontMenus(language, preferredFont); applyTextDirection(wmText.value || ''); applyTextOverlay(); }

syncLanguageAndFonts('english', 'Arial'); applyTextDirection(wmText.value || ''); syncHyperlinkUi(); updateLogoThumbUi(null, ''); syncSizeInputs();

function syncSizeInputs() { fontSizeRange.value = fontSize.value; fontSizeTag.textContent = fontSize.value; toolbarSize.value = fontSize.value; toolbarSizeRange.value = fontSize.value;

imageWidthRange.value = imageWidth.value; imageWidthTag.textContent = imageWidth.value; toolbarImageSize.value = imageWidth.value; toolbarImageSizeRange.value = imageWidth.value; }

function containsArabic(text) { return /[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/.test(text || ''); }

function isLikelyRtlText(text) { return /[\u0590-\u08FF]/.test(text || ''); }

function preparePdfText(text) { return String(text || '').replace(/\s+/g, ' ').trim(); }

function applyTextDirection(text) { const forceRtl = isRtlLanguage(languageSelect.value); const rtl = forceRtl || isLikelyRtlText(text); textPreview.dir = rtl ? 'rtl' : 'ltr'; textPreview.style.direction = rtl ? 'rtl' : 'ltr'; textPreview.style.unicodeBidi = rtl ? 'plaintext' : 'normal'; textPreview.style.textAlign = rtl ? 'right' : 'left'; fontSample.dir = rtl ? 'rtl' : 'ltr'; }

function normalizeHyperlink(value) { const raw = String(value || '').trim(); if (!raw) return ''; if (/^www\./i.test(raw)) return `https://${raw}`; if (/^[a-z][a-z0-9+.-]*:/i.test(raw)) return raw; return `https://${raw}`; }

function getValidatedHyperlink(inputValue) { const normalized = normalizeHyperlink(inputValue); if (!normalized) return ''; try { const parsed = new URL(normalized); if (['http:', 'https:', 'mailto:', 'tel:'].includes(parsed.protocol)) return normalized; } catch (err) {} throw new Error('Invalid hyperlink. Use http(s), mailto, tel, or a valid domain.'); }

function getWatermarkHyperlink() { if (!showTextWatermark.checked || !enableTextLink.checked) return ''; return getValidatedHyperlink(wmTextLink.value); }

function getImageWatermarkHyperlink() { if (!showLogoWatermark.checked || !enableImageLink.checked) return ''; return getValidatedHyperlink(wmImageLink.value); }

function syncHyperlinkUi() { wmTextLink.disabled = !enableTextLink.checked || !showTextWatermark.checked; wmImageLink.disabled = !enableImageLink.checked || !showLogoWatermark.checked; if (!enableTextLink.checked) wmTextLink.blur(); if (!enableImageLink.checked) wmImageLink.blur(); }

function getRotatedBoundingRect(x, y, width, height, rotationDeg) { const radians = degreesToRadians(rotationDeg); const cos = Math.cos(radians); const sin = Math.sin(radians); const points = [ { x, y }, { x: x + width * cos, y: y + width * sin }, { x: x - height * sin, y: y + height * cos }, { x: x + width * cos - height * sin, y: y + width * sin + height * cos } ]; const xs = points.map(point => point.x); const ys = points.map(point => point.y); return { x1: Math.min(...xs), y1: Math.min(...ys), x2: Math.max(...xs), y2: Math.max(...ys) }; }

function addHyperlinkAnnotation(pdf, page, rect, url) { const normalized = normalizeHyperlink(url); if (!normalized) return;

let annots = page.node.lookupMaybe(PDFName.of('Annots'), PDFArray); if (!annots) { annots = pdf.context.obj([]); page.node.set(PDFName.of('Annots'), annots); }

const linkAnnotation = pdf.context.register( pdf.context.obj({ Type: PDFName.of('Annot'), Subtype: PDFName.of('Link'), Rect: [rect.x1, rect.y1, rect.x2, rect.y2], Border: [0, 0, 0], H: PDFName.of('I'), A: pdf.context.obj({ Type: PDFName.of('Action'), S: PDFName.of('URI'), URI: PDFString.of(normalized) }) }) );

annots.push(linkAnnotation); }

function freshPdfBytes() { return new Uint8Array(pdfBytes); }

function getCanvasRect() { return pdfCanvas.getBoundingClientRect(); }

function formatFileSize(bytes) { if (!bytes || bytes < 1) return '0 KB'; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(1024)); const value = bytes / Math.pow(1024, i); return `${value.toFixed(value >= 10 || i === 0 ? 0 : 1)} ${sizes[i]}`; }

function escapeHtml(value) { return String(value || '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }

function setProgressUi(kind, percent, statusText = '') { if (kind !== 'pdf') return; pdfUploadProgress.classList.add('active'); const safePercent = Math.max(0, Math.min(100, Math.round(percent || 0))); if (statusText) pdfUploadStatus.textContent = statusText; pdfUploadPercent.textContent = `${safePercent}%`; pdfUploadBar.style.width = `${safePercent}%`; }

function hideProgressUi(kind, delay = 500) { if (kind !== 'pdf') return; window.setTimeout(() => pdfUploadProgress.classList.remove('active'), delay); }

function readFileWithProgress(file, kind, startText, endText) { return new Promise((resolve, reject) => { const reader = new FileReader(); if (kind === 'pdf') setProgressUi(kind, 0, startText);

reader.onprogress = (event) => { if (kind === 'pdf' && event.lengthComputable) { setProgressUi(kind, (event.loaded / event.total) * 100, startText); } };

reader.onloadstart = () => { if (kind === 'pdf') setProgressUi(kind, 0, startText); };

reader.onload = () => { if (kind === 'pdf') { setProgressUi(kind, 100, endText); hideProgressUi(kind, 700); } resolve(reader.result); };

reader.onerror = () => { if (kind === 'pdf') hideProgressUi(kind, 0); reject(reader.error || new Error('File could not be read.')); };

reader.readAsArrayBuffer(file); }); }

function updateLogoThumbUi(file, objectUrl = '') { const hasFile = !!(file && objectUrl); logoFileName.textContent = hasFile ? file.name : 'No logo selected'; logoFileMeta.textContent = hasFile ? `${(file.type || 'image').toUpperCase().replace('IMAGE/', '')} • ${formatFileSize(file.size)}` : 'PNG or JPG only'; logoThumbPreview.style.display = hasFile ? 'block' : 'none'; if (hasFile) logoThumbPreview.src = objectUrl; else logoThumbPreview.removeAttribute('src'); }

function resetRepeatedImagePreview() { repeatImageLayer.innerHTML = ''; repeatImageLayer.classList.add('hidden'); repeatImageLayer.style.pointerEvents = 'none'; repeatImageLayer.style.cursor = 'default'; }

function resetRepeatedTextPreview() { repeatTextLayer.innerHTML = ''; repeatTextLayer.classList.add('hidden'); repeatTextLayer.style.pointerEvents = 'none'; }

function renderRepeatedTextPreview() { resetRepeatedTextPreview();

if (!previewReady || !showTextWatermark.checked || !repeatWatermark.checked || !isCurrentPageSelectedForText()) { return; }

const text = preparePdfText(wmText.value); if (!text) return;

// Use getBoundingClientRect for the actual displayed (DOM) dimensions // canvas.width may differ from displayed size due to pdf.js scaling const rect = pdfCanvas.getBoundingClientRect(); const domW = rect.width; const domH = rect.height;

const gap = parseFloat(repeatGap.value || 120); const rotation = textState.rotation; const opacity = textState.opacity; const size = textState.size; const color = fontColor.value; const weight = textBold.checked ? '700' : '400'; const style = textItalic.checked ? 'italic' : 'normal'; const decoration = textUnderline.checked ? 'underline' : 'none'; const fontStack = getPreviewFontStack(fontFamily.value);

repeatTextLayer.classList.remove('hidden'); repeatTextLayer.style.zIndex = textState.overPdf ? '18' : '8'; repeatTextLayer.style.mixBlendMode = textState.overPdf ? 'normal' : 'multiply'; repeatTextLayer.style.pointerEvents = 'auto'; repeatTextLayer.style.cursor = 'move';

// Estimate text dimensions for spacing (approximate rendered width) const approxCharWidth = size * 0.6; const estimatedTextW = Math.max(text.length * approxCharWidth, size); const estimatedTextH = size * 1.2;

// Scale gap to DOM coordinate space (matches how logo repeat works) const scaleX = domW / (pdfCanvas.width || domW); const scaleY = domH / (pdfCanvas.height || domH); const scaledGap = gap * Math.min(scaleX, scaleY);

// Use the shared getRepeatPositions helper (same as logo repeat) // so both text and logo use identical coordinate logic const { offsetX, offsetY } = getRepeatTextOffset(domW, domH); const positions = getRepeatPositions( domW, domH, estimatedTextW, estimatedTextH, scaledGap, textRepeatSettings, offsetX, offsetY );

const frag = document.createDocumentFragment(); const clipPad = size * 0.7;

positions.forEach(({ x, y }) => { // Skip elements whose center is clearly outside the visible page if (x < -clipPad || x > domW + clipPad) return; if (y < -clipPad || y > domH + clipPad) return;

const el = document.createElement('span'); el.style.position = 'absolute'; el.style.left = x + 'px'; el.style.top = y + 'px'; el.style.transform = `translate(-50%, -50%) rotate(${rotation}deg)`; el.style.transformOrigin = 'center center'; el.style.whiteSpace = 'nowrap'; el.style.pointerEvents = 'none'; el.style.userSelect = 'none'; el.style.fontSize = size + 'px'; el.style.fontFamily = fontStack; el.style.fontWeight = weight; el.style.fontStyle = style; el.style.textDecoration = decoration; el.style.color = color; el.style.opacity = opacity; el.style.lineHeight = '1.1'; el.textContent = text; frag.appendChild(el); });

repeatTextLayer.appendChild(frag); }

function invalidateGeneratedPdf() { generatedPdfBlob = null; if (generatedPdfUrl) { URL.revokeObjectURL(generatedPdfUrl); generatedPdfUrl = ''; } if (downloadBtn) downloadBtn.style.display = 'none'; if (generateBtn) { generateBtn.disabled = false; generateBtn.innerHTML = 'Add Watermark'; } if (actionStatus) { actionStatus.textContent = 'Single click to select or drag to reposition. Double-click text to edit. Repeat watermark is also draggable. Preview updates in real-time.'; } }

function setGenerateLoading(isLoading) { generateBtn.disabled = isLoading; generateBtn.innerHTML = isLoading ? 'Preparing watermark...' : 'Add Watermark'; }

function removeTextWatermark() { showTextWatermark.checked = false; wmText.value = ''; textPreview.textContent = ''; closeToolbars(); applyAllOverlays(); }

function removeLogoWatermark() { showLogoWatermark.checked = false; resetLogoUi(); closeToolbars(); applyAllOverlays(); }

function resetLogoUi() { invalidateGeneratedPdf(); wmImageInput.value = ''; wmImageBytes = null; wmImageMime = null; if (wmImageObjectUrl) { URL.revokeObjectURL(wmImageObjectUrl); wmImageObjectUrl = null; } imagePreview.removeAttribute('src'); imageOverlay.classList.add('hidden'); imageToolbar.classList.remove('open'); repeatLogoWatermark.checked = false; resetRepeatedImagePreview(); resetRepeatedTextPreview(); removeLogoBtn.style.display = 'none'; enableImageLink.checked = false; wmImageLink.value = ''; syncHyperlinkUi(); updateLogoThumbUi(null, ''); }

function resetPdfUi() { invalidateGeneratedPdf(); pdfInput.value = ''; pdfFileName.textContent = 'No file selected'; pdfFileMeta.textContent = 'Size: — | Pages: —'; removePdfBtn.style.display = 'none'; pdfLabel.textContent = 'Select PDF File'; pdfDropText.style.display = 'block'; pdfUploadBar.style.width = '0%'; pdfUploadPercent.textContent = '0%'; pdfUploadStatus.textContent = 'Uploading PDF...'; pdfUploadProgress.classList.remove('active');

pdfBytes = null; pdfDoc = null; pdfPageIndex = 1; previewReady = false; currentFileName = 'watermarked-pdf';

const ctx = pdfCanvas.getContext('2d'); ctx.clearRect(0, 0, pdfCanvas.width, pdfCanvas.height); pdfCanvas.width = 0; pdfCanvas.height = 0;

stage.style.width = 'auto'; stage.style.height = 'auto'; pageControls.style.display = 'none'; previewStatus.style.display = 'block'; previewStatus.textContent = 'Upload a PDF to see the Editable PDF View here.';

closeToolbars(); textOverlay.classList.add('hidden'); imageOverlay.classList.add('hidden'); resetRepeatedImagePreview(); resetRepeatedTextPreview(); }

function getCenterFromState(state) { const rect = getCanvasRect(); return { x: rect.left + state.xRatio * rect.width, y: rect.top + state.yRatio * rect.height }; }

function getPreviewFontStack(selectedFamily) { const source = Object.values(FONT_CHOICES).flat(); const meta = source.find(font => font.value === selectedFamily); return meta?.stack || 'Arial, Helvetica, sans-serif'; }

function placeCaretAtEnd(el) { const range = document.createRange(); const sel = window.getSelection(); range.selectNodeContents(el); range.collapse(false); sel.removeAllRanges(); sel.addRange(range); }

function startInlineTextEdit() { if (!showTextWatermark.checked || !preparePdfText(wmText.value)) return; textVisual.classList.add('editing'); textPreview.contentEditable = 'true'; textPreview.focus(); placeCaretAtEnd(textPreview); openTextToolbar(); }

function stopInlineTextEdit() { const cleanText = textPreview.textContent.replace(/\s+/g, ' ').trim(); wmText.value = cleanText; textPreview.textContent = cleanText; applyTextDirection(cleanText); textPreview.contentEditable = 'false'; textVisual.classList.remove('editing'); if (!cleanText) showTextWatermark.checked = false; applyTextOverlay(); }

function syncTextToolbar() { toolbarColor.value = fontColor.value; toolbarOpacity.value = textOpacity.value; toolbarLanguage.value = languageSelect.value; toolbarFont.value = fontFamily.value; toolbarSize.value = fontSize.value; toolbarSizeRange.value = fontSize.value; textLayerMode.value = textState.overPdf ? 'overPdf' : 'belowPdf'; textLayerText.textContent = textState.overPdf ? 'Over PDF' : 'Below PDF'; toolbarTextLayerBtn.classList.toggle('active', textState.overPdf); toolbarBoldBtn.classList.toggle('active', textBold.checked); toolbarItalicBtn.classList.toggle('active', textItalic.checked); toolbarUnderlineBtn.classList.toggle('active', textUnderline.checked); toolbarTextQuickPosition.value = getQuickPositionValue(textState); toolbarTextRepeatToggle.checked = repeatWatermark.checked; toolbarTextRepeatStyle.value = textRepeatSettings.pattern; syncRepeatPickerTrigger(toolbarTextRepeatStylePicker, textRepeatSettings.pattern, textRepeatSettings.label); textOpacityLabel.textContent = Number(textOpacity.value).toFixed(2); fontSizeTag.textContent = fontSize.value; updateSelectPreviewStyle(fontFamily, fontFamily.value); updateSelectPreviewStyle(toolbarFont, toolbarFont.value); if (toolbarFontStylePreview) { toolbarFontStylePreview.textContent = getStyleLabel(); toolbarFontStylePreview.style.fontFamily = getPreviewFontStack(fontFamily.value); toolbarFontStylePreview.style.fontWeight = textBold.checked ? '700' : '400'; toolbarFontStylePreview.style.fontStyle = textItalic.checked ? 'italic' : 'normal'; toolbarFontStylePreview.style.textDecoration = textUnderline.checked ? 'underline' : 'none'; } fontSample.style.fontFamily = getPreviewFontStack(fontFamily.value); fontSample.style.fontWeight = textBold.checked ? '700' : '400'; fontSample.style.fontStyle = textItalic.checked ? 'italic' : 'normal'; fontSample.style.textDecoration = textUnderline.checked ? 'underline' : 'none'; }

function syncImageToolbar() { toolbarImageOpacity.value = imageOpacity.value; toolbarImageSize.value = imageWidth.value; toolbarImageSizeRange.value = imageWidth.value; imageLayerMode.value = imageState.overPdf ? 'overPdf' : 'belowPdf'; imageLayerText.textContent = imageState.overPdf ? 'Over PDF' : 'Below PDF'; toolbarImageLayerBtn.classList.toggle('active', imageState.overPdf); toolbarLogoQuickPosition.value = getQuickPositionValue(imageState); toolbarLogoPageRange.value = imagePageRangeMode.value || 'all'; toolbarLogoRepeatToggle.checked = repeatLogoWatermark.checked; toolbarLogoRepeatStyle.value = logoRepeatSettings.pattern; syncRepeatPickerTrigger(toolbarLogoRepeatStylePicker, logoRepeatSettings.pattern, logoRepeatSettings.label); imageOpacityLabel.textContent = Number(imageOpacity.value).toFixed(2); imageWidthTag.textContent = imageWidth.value; }

function positionTextToolbar() { if (!previewReady) return; const rect = getCanvasRect(); let x, y;

if (repeatWatermark.checked) { // In repeat mode, anchor toolbar to top-center of the canvas x = rect.width / 2; y = 40; } else { if (textOverlay.classList.contains('hidden')) return; x = textState.xRatio * rect.width; y = textState.yRatio * rect.height; }

const gap = 40; const toolbarHeight = Math.max(40, textToolbarShell.offsetHeight || 40); const shellWidth = Math.max(260, textToolbarShell.offsetWidth || 260); const shouldPlaceAbove = rect.height - y < (toolbarHeight + gap + 10) && y > (toolbarHeight + gap + 10);

textToolbarShell.style.left = `${Math.min(Math.max(x - shellWidth / 2, 8), Math.max(rect.width - shellWidth - 8, 8))}px`; textToolbarShell.style.top = shouldPlaceAbove ? `${Math.max(y - toolbarHeight - gap, 8)}px` : `${Math.min(y + gap, Math.max(rect.height - toolbarHeight - 8, 8))}px`; }

function positionImageToolbar() { if (!previewReady) return; const rect = getCanvasRect(); const x = imageState.xRatio * rect.width; const y = imageState.yRatio * rect.height; const gap = 46; const toolbarHeight = Math.max(40, imageToolbarShell.offsetHeight || 40); const shouldPlaceAbove = rect.height - y < (toolbarHeight + gap + 10) && y > (toolbarHeight + gap + 10);

const shellWidth = Math.max(220, imageToolbarShell.offsetWidth || 220); imageToolbarShell.style.left = `${Math.min(Math.max(x - shellWidth / 2, 8), Math.max(rect.width - shellWidth - 8, 8))}px`; imageToolbarShell.style.top = shouldPlaceAbove ? `${Math.max(y - toolbarHeight - gap, 8)}px` : `${Math.min(y + gap, Math.max(rect.height - toolbarHeight - 8, 8))}px`; }

function openTextToolbar() { const text = preparePdfText(wmText.value); if (!previewReady || !showTextWatermark.checked || !isCurrentPageSelectedForText() || !text) return; textToolbarShell.classList.add('open'); positionTextToolbar(); textToolbar.classList.add('open'); }

function openImageToolbar() { if (!previewReady || !showLogoWatermark.checked || !wmImageBytes || !isCurrentPageSelectedForImage()) return; imageToolbarShell.classList.add('open'); positionImageToolbar(); imageToolbar.classList.add('open'); }

function closeToolbars() { textToolbarShell.classList.remove('open'); imageToolbarShell.classList.remove('open'); textToolbar.classList.remove('open'); imageToolbar.classList.remove('open'); }

function toggleToolbarExpanded(target, forceExpanded = null) { const shell = target === 'text' ? textToolbarShell : imageToolbarShell; const moreBtn = target === 'text' ? textToolbarMoreBtn : imageToolbarMoreBtn; const shouldExpand = forceExpanded === null ? !shell.classList.contains('expanded') : forceExpanded; shell.classList.toggle('expanded', shouldExpand); const expanded = shell.classList.contains('expanded'); if (moreBtn) moreBtn.classList.toggle('active', expanded); if (target === 'text' && textToolbar.classList.contains('open')) positionTextToolbar(); if (target === 'image' && imageToolbar.classList.contains('open')) positionImageToolbar(); }

function updatePageRangeUi(modeEl, rangeEl, helperEl, target = 'text') { const mode = modeEl.value; const isCustom = mode === 'custom'; rangeEl.disabled = !isCustom; rangeEl.style.display = isCustom ? 'block' : 'none';

const helperMap = target === 'text' ? { all: 'Apply watermark to all pages.', first: 'Apply watermark to page 1 only.', first2: 'Apply watermark to pages 1 and 2 only.', first3: 'Apply watermark to the first 3 pages.', firstLast: 'Apply watermark to the first and last page.', custom: 'Examples: 1-3, 2,4,7' } : { all: 'Apply logo watermark to all pages.', first: 'Apply logo watermark to page 1 only.', first2: 'Apply logo watermark to pages 1 and 2 only.', first3: 'Apply logo watermark to the first 3 pages.', firstLast: 'Apply logo watermark to the first and last page.', custom: 'Examples: 1-3, 2,4,7' };

helperEl.textContent = helperMap[mode] || helperMap.custom; }

function parseCustomPageRange(rawValue, totalPages) { const raw = String(rawValue || '').trim().toLowerCase(); const out = new Set(); const parts = raw.split(',').map(s => s.trim()).filter(Boolean);

for (const part of parts) { if (part.includes('-')) { const [a, b] = part.split('-').map(v => parseInt(v.trim(), 10)); if (Number.isFinite(a) && Number.isFinite(b)) { const start = Math.max(1, Math.min(a, b)); const end = Math.min(totalPages, Math.max(a, b)); for (let p = start; p <= end; p++) out.add(p); } } else { const p = parseInt(part, 10); if (Number.isFinite(p) && p >= 1 && p <= totalPages) out.add(p); } } if (!out.size) throw new Error('Invalid custom page range. Use values like 1-3,5'); return out; } function parsePageRange(modeEl, rangeEl, totalPages) { const mode = modeEl.value; if (mode === 'all') return new Set(Array.from({ length: totalPages }, (_, i) => i + 1)); if (mode === 'first') return new Set([1]); if (mode === 'first2') return new Set(Array.from({ length: Math.min(2, totalPages) }, (_, i) => i + 1)); if (mode === 'first3') return new Set(Array.from({ length: Math.min(3, totalPages) }, (_, i) => i + 1)); if (mode === 'firstLast') return new Set(totalPages > 1 ? [1, totalPages] : [1]); return parseCustomPageRange(rangeEl.value, totalPages); }

function getSelectedImagePages(totalPages) { const pages = parsePageRange(imagePageRangeMode, imagePageRange, totalPages); const mode = imagePageRangeMode.value || 'all'; if (['all', 'first', 'first2', 'first3', 'firstLast'].includes(mode)) pages.add(1); return pages; }

function isCurrentPageSelectedForText() { if (!pdfDoc) return true; try { return parsePageRange(pageRangeMode, pageRange, pdfDoc.numPages).has(pdfPageIndex); } catch { return true; } }

function isCurrentPageSelectedForImage() { if (!pdfDoc) return true; try { return parsePageRange(imagePageRangeMode, imagePageRange, pdfDoc.numPages).has(pdfPageIndex); } catch { return true; } }

// applyTextOverlay is defined later (complete version with overPdf + repeat support)

function getRepeatPositions(pageWidth, pageHeight, itemWidth, itemHeight, gap, settings, offsetX = 0, offsetY = 0) { const positions = []; let stepX = Math.max(itemWidth + gap, 30); let stepY = Math.max(itemHeight + gap, 30);

if (settings.pattern === 'dense') { stepX = Math.max(itemWidth + gap * 0.6, 24); stepY = Math.max(itemHeight + gap * 0.6, 24); } if (settings.pattern === 'wide') { stepX = Math.max(itemWidth + gap * 1.25, 40); stepY = Math.max(itemHeight + gap * 1.25, 40); } if (settings.pattern === 'diamond') { stepX = Math.max(itemWidth + gap * 1.1, 36); stepY = Math.max(itemHeight + gap * 1.1, 36); }

// Clamp: only draw within the page boundary (with a small safe margin) const margin = Math.max(itemWidth * 0.7, itemHeight * 0.7, 20); const startX = -stepX + offsetX; const endX = pageWidth + stepX + offsetX; const startY = -stepY + offsetY; const endY = pageHeight + stepY + offsetY;

let y = startY; let rowIndex = 0;

while (y <= endY) { let xStart = startX; if (settings.pattern === 'checker') xStart += rowIndex % 2 ? stepX / 2 : 0; if (settings.pattern === 'brick') xStart += rowIndex % 2 ? stepX / 2 : 0; if (settings.pattern === 'diagonal') xStart += rowIndex * Math.max(gap * 0.34, itemWidth * 0.18, 12); if (settings.pattern === 'stair') xStart += rowIndex * Math.max(stepX * 0.2, 12); if (settings.pattern === 'cross') xStart += rowIndex % 2 ? stepX / 2 : 0; if (settings.pattern === 'diamond') xStart += rowIndex % 2 ? stepX / 2 : 0; for (let x = xStart; x <= endX; x += stepX) { let yOffset = 0; if (settings.pattern === 'ripple') { yOffset = Math.sin((x / Math.max(stepX, 1)) * 0.7) * Math.max(10, gap * 0.18); } const px = x - offsetX; const py = y + yOffset - offsetY; // Hard-clip: skip positions whose center is outside the page with margin if (px < -margin || px > pageWidth + margin) continue; if (py < -margin || py > pageHeight + margin) continue;

positions.push({ x, y: y + yOffset }); }

rowIndex++; y += stepY; }

return positions; }

function getRepeatImageOffset(pageWidth, pageHeight) { return { offsetX: (imageState.xRatio - 0.5) * pageWidth, offsetY: (imageState.yRatio - 0.5) * pageHeight }; }

function getRepeatTextOffset(pageWidth, pageHeight) { return { offsetX: (textRepeatState.xRatio - 0.5) * pageWidth, offsetY: (textRepeatState.yRatio - 0.5) * pageHeight }; }

function renderRepeatedImagePreview() { resetRepeatedImagePreview();

if (!previewReady || !showLogoWatermark.checked || !wmImageBytes || !wmImageObjectUrl || !repeatLogoWatermark.checked || !isCurrentPageSelectedForImage()) { return; }

const width = parseFloat(imageWidth.value || 140); const naturalWidth = imagePreview.naturalWidth || 1; const naturalHeight = imagePreview.naturalHeight || 1; const ratio = naturalHeight / naturalWidth; const height = width * ratio; const gap = parseFloat(repeatGap.value || 120); const { offsetX, offsetY } = getRepeatImageOffset(pdfCanvas.width, pdfCanvas.height); const positions = getRepeatPositions(pdfCanvas.width, pdfCanvas.height, width, height, gap, logoRepeatSettings, offsetX, offsetY);

repeatImageLayer.classList.remove('hidden'); repeatImageLayer.style.zIndex = imageState.overPdf ? '19' : '2'; repeatImageLayer.style.mixBlendMode = imageState.overPdf ? 'normal' : 'multiply'; repeatImageLayer.style.pointerEvents = 'auto'; repeatImageLayer.style.cursor = 'move';

positions.forEach((pos) => { const item = document.createElement('img'); item.src = wmImageObjectUrl; item.className = 'repeat-image-item'; item.style.width = `${width}px`; item.style.left = `${pos.x}px`; item.style.top = `${pos.y}px`; item.style.opacity = String(parseFloat(imageOpacity.value || 0.80)); item.style.transform = `translate(-50%, -50%) rotate(${parseFloat(imageRotation.value || 0)}deg)`; repeatImageLayer.appendChild(item); }); }

function applyImageOverlay() { imageState.rotation = parseFloat(imageRotation.value || 0); imageState.width = parseFloat(imageWidth.value || 140); imageState.opacity = parseFloat(imageOpacity.value || 0.80);

imageOverlay.style.left = `${imageState.xRatio * pdfCanvas.width}px`; imageOverlay.style.top = `${imageState.yRatio * pdfCanvas.height}px`; imageOverlay.style.transform = `translate(-50%, -50%) rotate(${imageState.rotation}deg)`; imagePreview.style.width = `${imageState.width}px`; imagePreview.style.height = 'auto'; imagePreview.style.opacity = String(imageState.opacity);

if (imageState.overPdf) { imageOverlay.style.zIndex = '20'; imageOverlay.style.mixBlendMode = 'normal'; } else { imageOverlay.style.zIndex = '12'; imageOverlay.style.mixBlendMode = 'multiply'; }

const liveImageLink = showLogoWatermark.checked && enableImageLink.checked ? normalizeHyperlink(wmImageLink.value) : ''; imageVisual.classList.toggle('has-link', !!liveImageLink); imageVisual.style.cursor = liveImageLink ? 'pointer' : 'move'; imageVisual.title = liveImageLink || '';

if (wmImageBytes && previewReady && showLogoWatermark.checked && !repeatLogoWatermark.checked && isCurrentPageSelectedForImage()) { imageOverlay.classList.remove('hidden'); } else { imageOverlay.classList.add('hidden'); }

if (!(wmImageBytes && previewReady && showLogoWatermark.checked && isCurrentPageSelectedForImage())) { imageToolbar.classList.remove('open'); }

syncImageToolbar(); renderRepeatedImagePreview(); positionImageToolbar(); }

function applyAllOverlays() { invalidateGeneratedPdf(); syncHyperlinkUi(); syncSizeInputs(); applyTextOverlay(); applyImageOverlay(); }

function quickPosition(state, key) { const map = { 'top-left': [0.16, 0.14], 'top-middle': [0.50, 0.14], 'center': [0.50, 0.50], 'bottom-right': [0.84, 0.86] }; [state.xRatio, state.yRatio] = map[key] || [0.5, 0.5]; }

function syncRepeatPickerTrigger(pickerEl, pattern, label) { if (!pickerEl) return; const trigger = pickerEl.querySelector(".repeat-style-trigger"); if (trigger) { trigger.querySelector(".rs-mini").className = "rs-mini " + pattern; trigger.querySelector(".rs-label").textContent = label; } pickerEl.querySelectorAll(".rs-option").forEach(o => o.classList.toggle("active", o.dataset.value === pattern)); }

function setActiveRepeatPreset(group, pattern, label) { const settings = group === 'logo' ? logoRepeatSettings : textRepeatSettings; settings.pattern = pattern; settings.label = label; watermarkToolRoot.querySelectorAll(`.repeat-preset[data-group="${group}"]`).forEach(btn => { btn.classList.toggle('active', btn.dataset.pattern === pattern); }); if (group === 'text' && toolbarTextRepeatStyle) { toolbarTextRepeatStyle.value = pattern; syncRepeatPickerTrigger(toolbarTextRepeatStylePicker, pattern, label); } if (group === 'logo' && toolbarLogoRepeatStyle) { toolbarLogoRepeatStyle.value = pattern; syncRepeatPickerTrigger(toolbarLogoRepeatStylePicker, pattern, label); } }

function getQuickPositionValue(state) { const positions = { 'top-left': [0.16, 0.14], 'top-middle': [0.50, 0.14], center: [0.50, 0.50], 'bottom-right': [0.84, 0.86] };

const match = Object.entries(positions).find(([, coords]) => ( Math.abs(state.xRatio - coords[0]) < 0.025 && Math.abs(state.yRatio - coords[1]) < 0.025 )); return match ? match[0] : 'custom'; } function applyToolbarQuickPosition(target, value) { if (!value || value === 'custom') return; if (target === 'text') { quickPosition(textState, value); applyAllOverlays(); openTextToolbar(); } else { quickPosition(imageState, value); applyAllOverlays(); openImageToolbar(); } } function applyToolbarRepeatStyle(group, pattern) { const preset = watermarkToolRoot.querySelector(`.repeat-preset[data-group="${group}"][data-pattern="${pattern}"]`); if (!preset) return; if (group === 'text') repeatWatermark.checked = true; else repeatLogoWatermark.checked = true; repeatGap.value = preset.dataset.gap; setActiveRepeatPreset(group, preset.dataset.pattern, preset.dataset.label); applyAllOverlays(); if (group === 'text') openTextToolbar(); else openImageToolbar(); } async function renderPdfPage() { if (!pdfDoc) return; try { previewStatus.style.display = 'block'; previewStatus.textContent = 'Rendering PDF preview...'; const page = await pdfDoc.getPage(pdfPageIndex); const viewport = page.getViewport({ scale: 1.4 }); const ctx = pdfCanvas.getContext('2d'); pdfCanvas.width = viewport.width; pdfCanvas.height = viewport.height; stage.style.width = `${viewport.width}px`; stage.style.height = `${viewport.height}px`; await page.render({ canvasContext: ctx, viewport }).promise; pageNum.textContent = pdfPageIndex; pageCount.textContent = pdfDoc.numPages; pageJumpInput.value = pdfPageIndex; pageControls.style.display = 'flex'; previewReady = true; previewStatus.style.display = 'none'; applyAllOverlays(); } catch (err) { previewReady = false; previewStatus.style.display = 'block'; previewStatus.textContent = 'PDF preview could not be loaded. Try another PDF.'; console.error(err); } } async function loadPdf(file) { try { previewStatus.style.display = 'block'; previewStatus.textContent = 'Loading PDF...'; currentFileName = file.name.replace(/\.pdf$/i, '') || 'watermarked-pdf'; const rawBuffer = await readFileWithProgress(file, 'pdf', 'Uploading PDF...', 'PDF upload complete'); pdfBytes = new Uint8Array(rawBuffer); await PDFDocument.load(freshPdfBytes(), { ignoreEncryption: true }); pdfDoc = await pdfjsLib.getDocument({ data: freshPdfBytes() }).promise; pdfPageIndex = 1; previewReady = false; pdfFileName.innerHTML = `PDF${escapeHtml(file.name)}`; pdfFileMeta.textContent = `Size: ${formatFileSize(file.size)} | Pages: ${pdfDoc.numPages}`; pdfLabel.textContent = 'Replace PDF File'; removePdfBtn.style.display = 'inline-block'; pdfDropText.style.display = 'none';

await renderPdfPage(); } catch (err) { pdfBytes = null; pdfDoc = null; previewReady = false; pageControls.style.display = 'none'; previewStatus.style.display = 'block'; previewStatus.textContent = 'This PDF is not compatible for editing. Try another PDF.'; pdfFileName.textContent = 'No file selected'; pdfFileMeta.textContent = 'Size: — | Pages: —'; removePdfBtn.style.display = 'none'; console.error(err); alert('This PDF is restricted, damaged, or not compatible for editing.'); } }

async function loadWatermarkImage(file) { try { if (!file.type.startsWith('image/')) return alert('Please upload a valid image file.'); if (file.type === 'image/webp') return alert('WEBP is not supported. Please use PNG or JPG.');

const rawBuffer = await readFileWithProgress(file, 'image', 'Uploading logo...', 'Logo upload complete'); wmImageBytes = new Uint8Array(rawBuffer); wmImageMime = file.type || 'image/png';

if (wmImageObjectUrl) URL.revokeObjectURL(wmImageObjectUrl); wmImageObjectUrl = URL.createObjectURL(file);

imagePreview.src = wmImageObjectUrl; updateLogoThumbUi(file, wmImageObjectUrl); removeLogoBtn.style.display = 'inline-block'; showLogoWatermark.checked = true; applyImageOverlay(); } catch (err) { resetLogoUi(); console.error(err); alert('Could not load watermark image.'); } }

function getPdfFontKey() { const family = (fontFamily.value || 'Arial').toLowerCase(); const bold = textBold.checked; const italic = textItalic.checked;

if (family === 'times new roman' || family === 'georgia' || family === 'lora' || family === 'playfair display' || family === 'merriweather') { if (bold && italic) return PDFLib.StandardFonts.TimesRomanBoldItalic; if (bold) return PDFLib.StandardFonts.TimesRomanBold; if (italic) return PDFLib.StandardFonts.TimesRomanItalic; return PDFLib.StandardFonts.TimesRoman; }

if (family === 'courier new') { if (bold && italic) return PDFLib.StandardFonts.CourierBoldOblique; if (bold) return PDFLib.StandardFonts.CourierBold; if (italic) return PDFLib.StandardFonts.CourierOblique; return PDFLib.StandardFonts.Courier; }

if (bold && italic) return PDFLib.StandardFonts.HelveticaBoldOblique; if (bold) return PDFLib.StandardFonts.HelveticaBold; if (italic) return PDFLib.StandardFonts.HelveticaOblique; return PDFLib.StandardFonts.Helvetica; }

function hexToRgb(hex) { const clean = (hex || '#000000').replace('#', '').trim(); const full = clean.length === 3 ? clean.split('').map(ch => ch + ch).join('') : clean.padEnd(6, '0').slice(0, 6); const int = parseInt(full, 16); return { r: ((int >> 16) & 255) / 255, g: ((int >> 8) & 255) / 255, b: (int & 255) / 255 }; }

function degreesToRadians(deg) { return deg * Math.PI / 180; }

function getPreviewToPdfScale(pageWidth, pageHeight) { return { x: pageWidth / Math.max(1, pdfCanvas.width), y: pageHeight / Math.max(1, pdfCanvas.height) }; }

function getCanvasFontDeclaration(sizePx) { const weight = textBold.checked ? '700' : '400'; const style = textItalic.checked ? 'italic' : 'normal'; return `${style} ${weight} ${sizePx}px ${getPreviewFontStack(fontFamily.value)}`; }

async function ensurePreviewFontLoaded(sizePx, text) { if (!document.fonts?.load) return; const sample = String(text || 'A'); try { await document.fonts.load(getCanvasFontDeclaration(sizePx), sample); await document.fonts.ready; } catch (err) {} }

function blobToUint8Array(blob) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(new Uint8Array(reader.result)); reader.onerror = () => reject(reader.error || new Error('Blob could not be read.')); reader.readAsArrayBuffer(blob); }); }

function isNonLatinScript(text, language) { // Non-Latin scripts that cannot be encoded by PDF WinAnsi standard fonts const nonLatinLanguages = ['arabic', 'hebrew', 'hindi', 'urdu', 'persian', 'japanese', 'chinese']; if (nonLatinLanguages.includes(language)) return true; // Also detect by unicode range for auto-detected scripts if (/[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/.test(text)) return true; // Arabic/Urdu/Persian if (/[\u0590-\u05FF]/.test(text)) return true; // Hebrew if (/[\u0900-\u097F]/.test(text)) return true; // Devanagari/Hindi if (/[\u3000-\u9FFF\uF900-\uFAFF]/.test(text)) return true; // CJK if (/[\u0400-\u04FF]/.test(text)) return true; // Cyrillic/Russian return false; }

async function createTextWatermarkAsset(pdf) { const text = preparePdfText(wmText.value); const language = languageSelect.value; const sizePt = parseFloat(fontSize.value || 48); const useCanvas = isNonLatinScript(text, language);

if (!useCanvas) { // Latin text: embed a standard PDF font (native searchable text) const fontKey = getPdfFontKey(); const pdfFont = await pdf.embedFont(fontKey); const textWidth = pdfFont.widthOfTextAtSize(text, sizePt); const textHeight = pdfFont.heightAtSize(sizePt); return { type: 'native', font: pdfFont, text, sizePt, textWidth, textHeight }; }

// Non-Latin: render to canvas then embed as PNG (only way to support these scripts in pdf-lib) const sizePx = sizePt; // 1pt ≈ 1px at screen resolution; scale handles the rest const rtl = isRtlLanguage(language) || isLikelyRtlText(text); const qualityScale = 3; const padding = Math.max(10, sizePx * 0.28);

await ensurePreviewFontLoaded(sizePx, text);

const measureCanvas = document.createElement('canvas'); const measureCtx = measureCanvas.getContext('2d'); measureCtx.font = getCanvasFontDeclaration(sizePx); measureCtx.direction = rtl ? 'rtl' : 'ltr'; const metrics = measureCtx.measureText(text); const textWidth = Math.max(1, Math.ceil(metrics.width || sizePx)); const ascent = Math.ceil(metrics.actualBoundingBoxAscent || sizePx * 0.82); const descent = Math.ceil(metrics.actualBoundingBoxDescent || sizePx * 0.28); const underlineExtra = textUnderline.checked ? Math.ceil(sizePx * 0.18) : 0; const logicalWidth = textWidth + Math.ceil(padding * 2); const logicalHeight = ascent + descent + Math.ceil(padding * 2) + underlineExtra;

const canvas = document.createElement('canvas'); canvas.width = Math.max(1, Math.ceil(logicalWidth * qualityScale)); canvas.height = Math.max(1, Math.ceil(logicalHeight * qualityScale));

const ctx = canvas.getContext('2d'); ctx.scale(qualityScale, qualityScale); ctx.clearRect(0, 0, logicalWidth, logicalHeight); ctx.font = getCanvasFontDeclaration(sizePx); ctx.fillStyle = fontColor.value; ctx.textBaseline = 'alphabetic'; ctx.textAlign = rtl ? 'right' : 'left'; ctx.direction = rtl ? 'rtl' : 'ltr';

const textX = rtl ? logicalWidth - padding : padding; const textY = padding + ascent; ctx.fillText(text, textX, textY);

if (textUnderline.checked) { const underlineOffset = Math.max(2, sizePx * 0.1); const underlineThickness = Math.max(1, sizePx * 0.05); const startX = rtl ? logicalWidth - padding - textWidth : padding; const endX = startX + textWidth; const lineY = textY + underlineOffset; ctx.beginPath(); ctx.moveTo(startX, lineY); ctx.lineTo(endX, lineY); ctx.lineWidth = underlineThickness; ctx.strokeStyle = fontColor.value; ctx.stroke(); }

const blob = await new Promise((resolve, reject) => { canvas.toBlob((result) => { if (result) resolve(result); else reject(new Error('Text watermark image could not be created.')); }, 'image/png'); });

const pngBytes = await blobToUint8Array(blob); const embeddedImage = await pdf.embedPng(pngBytes); return { type: 'image', image: embeddedImage, width: logicalWidth, height: logicalHeight, sizePt }; }

function getImageDrawSizeForPdf(img, pageWidth, pageHeight) { const scale = getPreviewToPdfScale(pageWidth, pageHeight); const drawWidth = parseFloat(imageWidth.value || 140) * scale.x; const ratio = img.height / img.width; return { width: drawWidth, height: drawWidth * ratio }; }

function getTextAnchorForPdf(pageWidth, pageHeight) { return { x: textState.xRatio * pageWidth, y: (1 - textState.yRatio) * pageHeight }; }

function getImageAnchorForPdf(pageWidth, pageHeight) { return { x: imageState.xRatio * pageWidth, y: (1 - imageState.yRatio) * pageHeight }; }

function getTextImageDrawSizeForPdf(asset, pageWidth, pageHeight) { const scale = getPreviewToPdfScale(pageWidth, pageHeight); return { width: asset.width * scale.x, height: asset.height * scale.y }; }

function drawSingleTextWatermarkNative(pdf, page, asset, pageWidth, pageHeight, anchorX, anchorY, hyperlink = '') { const scale = getPreviewToPdfScale(pageWidth, pageHeight); const rotationDeg = parseFloat(textRotation.value || 0); const opacity = parseFloat(textOpacity.value || 0.80);

if (asset.type === 'native') { const sizePt = asset.sizePt * Math.min(scale.x, scale.y); const textWidth = asset.font.widthOfTextAtSize(asset.text, sizePt); const textHeight = asset.font.heightAtSize(sizePt); const color = hexToRgb(fontColor.value); const x = anchorX - textWidth / 2; const y = anchorY - textHeight / 2;

page.drawText(asset.text, { x, y, size: sizePt, font: asset.font, color: PDFLib.rgb(color.r, color.g, color.b), opacity, rotate: PDFLib.degrees(rotationDeg) });

if (hyperlink) addHyperlinkAnnotation(pdf, page, getRotatedBoundingRect(x, y, textWidth, textHeight, rotationDeg), hyperlink); return { width: textWidth, height: textHeight };

} else { // Canvas-image fallback for non-Latin scripts const { width, height } = getTextImageDrawSizeForPdf(asset, pageWidth, pageHeight); const x = anchorX - width / 2; const y = anchorY - height / 2;

page.drawImage(asset.image, { x, y, width, height, opacity, rotate: PDFLib.degrees(rotationDeg) });

if (hyperlink) addHyperlinkAnnotation(pdf, page, getRotatedBoundingRect(x, y, width, height, rotationDeg), hyperlink); return { width, height }; } }

function drawRepeatedTextWatermarkNative(pdf, page, asset, pageWidth, pageHeight, hyperlink = '') { const scale = getPreviewToPdfScale(pageWidth, pageHeight); let width, height;

if (asset.type === 'native') { const sizePt = asset.sizePt * Math.min(scale.x, scale.y); width = asset.font.widthOfTextAtSize(asset.text, sizePt); height = asset.font.heightAtSize(sizePt); } else { const dims = getTextImageDrawSizeForPdf(asset, pageWidth, pageHeight); width = dims.width; height = dims.height; }

const gap = parseFloat(repeatGap.value || 120) * scale.x; const { offsetX, offsetY } = getRepeatTextOffset(pageWidth, pageHeight); const positions = getRepeatPositions(pageWidth, pageHeight, width, height, gap, textRepeatSettings, offsetX, -offsetY); const padding = Math.max(width * 0.7, height * 0.7, 20);

for (const pos of positions) { if (pos.x < -padding || pos.x > pageWidth + padding) continue; if (pos.y < -padding || pos.y > pageHeight + padding) continue; drawSingleTextWatermarkNative(pdf, page, asset, pageWidth, pageHeight, pos.x, pos.y, hyperlink); } }

async function embedWatermarkImage(pdf) { if (!wmImageBytes) return null; if (wmImageMime === 'image/png') return await pdf.embedPng(wmImageBytes); if (wmImageMime === 'image/jpeg' || wmImageMime === 'image/jpg') return await pdf.embedJpg(wmImageBytes); throw new Error('Unsupported image format. Use PNG or JPG.'); }

function drawSingleImageWatermark(pdf, page, img, pageWidth, pageHeight, anchorX, anchorY, hyperlink = '') { const { width, height } = getImageDrawSizeForPdf(img, pageWidth, pageHeight); const rotationDeg = parseFloat(imageRotation.value || 0); const x = anchorX - width / 2; const y = anchorY - height / 2;

page.drawImage(img, { x, y, width, height, opacity: parseFloat(imageOpacity.value || 0.80), rotate: PDFLib.degrees(rotationDeg) });

if (hyperlink) addHyperlinkAnnotation(pdf, page, getRotatedBoundingRect(x, y, width, height, rotationDeg), hyperlink); return { width, height }; }

function drawRepeatedImageWatermarks(pdf, page, img, pageWidth, pageHeight, hyperlink = '') { const { width, height } = getImageDrawSizeForPdf(img, pageWidth, pageHeight); const scale = getPreviewToPdfScale(pageWidth, pageHeight); const gap = parseFloat(repeatGap.value || 120) * scale.x; const { offsetX, offsetY } = getRepeatImageOffset(pageWidth, pageHeight); // getRepeatPositions uses top-down Y; flip offsetY for PDF bottom-up coords const positions = getRepeatPositions(pageWidth, pageHeight, width, height, gap, logoRepeatSettings, offsetX, -offsetY); const padding = Math.max(width * 0.7, height * 0.7, 20);

for (const pos of positions) { if (pos.x < -padding || pos.x > pageWidth + padding) continue; if (pos.y < -padding || pos.y > pageHeight + padding) continue; // Flip Y: PDF origin is bottom-left, preview origin is top-left const pdfY = pageHeight - pos.y; drawSingleImageWatermark(pdf, page, img, pageWidth, pageHeight, pos.x, pdfY, hyperlink); } }

async function buildWatermarkedPdfBlob() { if (!pdfBytes) return alert('Please upload a PDF first.'); if (!previewReady) return alert('Please wait for preview.');

const sourcePdf = await PDFDocument.load(freshPdfBytes(), { ignoreEncryption: true }); const outPdf = await PDFDocument.create();

const textPages = parsePageRange(pageRangeMode, pageRange, sourcePdf.getPageCount()); const imagePages = getSelectedImagePages(sourcePdf.getPageCount());

const preparedText = showTextWatermark.checked ? preparePdfText(wmText.value) : ''; const textHyperlink = getWatermarkHyperlink(); const imageHyperlink = getImageWatermarkHyperlink();

const textAsset = preparedText ? await createTextWatermarkAsset(outPdf) : null; const imageEmbed = showLogoWatermark.checked && wmImageBytes ? await embedWatermarkImage(outPdf) : null;

const sourcePages = sourcePdf.getPages(); const embeddedPages = await outPdf.embedPages(sourcePages);

for (let i = 0; i < sourcePages.length; i++) { const pageNumber = i + 1; const sourcePage = sourcePages[i]; const embeddedPage = embeddedPages[i]; const { width, height } = sourcePage.getSize(); const page = outPdf.addPage([width, height]); const shouldDrawImageOnPage = imagePages.has(pageNumber) && imageEmbed; const keepFirstPageLogoVisible = pageNumber === 1; if (shouldDrawImageOnPage && !imageState.overPdf && !keepFirstPageLogoVisible) { if (repeatLogoWatermark.checked) { drawRepeatedImageWatermarks(outPdf, page, imageEmbed, width, height, imageHyperlink); } else { const anchor = getImageAnchorForPdf(width, height); drawSingleImageWatermark(outPdf, page, imageEmbed, width, height, anchor.x, anchor.y, imageHyperlink); } } page.drawPage(embeddedPage, { x: 0, y: 0, width, height }); if (textPages.has(pageNumber) && textAsset && preparedText) { if (repeatWatermark.checked) { drawRepeatedTextWatermarkNative(outPdf, page, textAsset, width, height, textHyperlink); } else { const anchor = getTextAnchorForPdf(width, height); drawSingleTextWatermarkNative(outPdf, page, textAsset, width, height, anchor.x, anchor.y, textHyperlink); } } if (shouldDrawImageOnPage && (imageState.overPdf || keepFirstPageLogoVisible)) { if (repeatLogoWatermark.checked) { drawRepeatedImageWatermarks(outPdf, page, imageEmbed, width, height, imageHyperlink); } else { const anchor = getImageAnchorForPdf(width, height); drawSingleImageWatermark(outPdf, page, imageEmbed, width, height, anchor.x, anchor.y, imageHyperlink); } } } const outBytes = await outPdf.save(); return new Blob([outBytes], { type: 'application/pdf' }); } async function generateWatermarkedPdf() { try { setGenerateLoading(true); actionStatus.textContent = 'Preparing watermark with current preview and PDF placement. Please wait...'; const blob = await buildWatermarkedPdfBlob(); if (!blob) return; generatedPdfBlob = blob; if (generatedPdfUrl) URL.revokeObjectURL(generatedPdfUrl); generatedPdfUrl = URL.createObjectURL(blob); downloadBtn.style.display = 'inline-block'; actionStatus.textContent = 'Watermarked PDF is ready. Review the preview above and click Download PDF.'; } catch (err) { console.error(err); const msg = String((err && err.message) || err || ''); if (msg.includes('Invalid hyperlink')) { alert('Invalid hyperlink. Use a full URL like https://example.com, or mailto:someone@example.com.'); } else { alert(`Error while creating watermarked PDF: ${msg}`); } actionStatus.textContent = 'Could not prepare the watermarked PDF. Please review your settings and try again.'; } finally { setGenerateLoading(false); } } function downloadGeneratedPdf() { if (!generatedPdfBlob || !generatedPdfUrl) return alert('Click Add Watermark first.'); const a = document.createElement('a'); a.href = generatedPdfUrl; a.download = `${currentFileName || 'watermarked-pdf'}-watermarked.pdf`; document.body.appendChild(a); a.click(); a.remove(); } function clientPointFromEvent(e) { if (e.touches && e.touches[0]) return { x: e.touches[0].clientX, y: e.touches[0].clientY }; if (e.changedTouches && e.changedTouches[0]) return { x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY }; return { x: e.clientX, y: e.clientY }; } function openForTarget(target) { closeToolbars(); if (target === 'text') openTextToolbar(); if (target === 'image') openImageToolbar(); } function clampState(state) { state.xRatio = Math.max(0.02, Math.min(0.98, state.xRatio)); state.yRatio = Math.max(0.02, Math.min(0.98, state.yRatio)); } function startDrag(target, e) { if (!previewReady) return; const point = clientPointFromEvent(e); const isRepeat = target === 'text' ? repeatWatermark.checked : repeatLogoWatermark.checked; const state = target === 'text' ? (isRepeat ? textRepeatState : textState) : imageState; let centerX, centerY; if (isRepeat) { const rect = getCanvasRect(); centerX = rect.left + state.xRatio * rect.width; centerY = rect.top + state.yRatio * rect.height; } else { const overlay = target === 'text' ? textOverlay : imageOverlay; const overlayRect = overlay.getBoundingClientRect(); centerX = overlayRect.left + overlayRect.width / 2; centerY = overlayRect.top + overlayRect.height / 2; } activeDrag = target; dragOffsetX = point.x - centerX; dragOffsetY = point.y - centerY; openForTarget(target); e.preventDefault(); } function startResize(target, e) { if (!previewReady) return; if (target === 'image' && repeatLogoWatermark.checked) return; const point = clientPointFromEvent(e); const center = getCenterFromState(target === 'text' ? textState : imageState); resizeStartDistance = Math.hypot(point.x - center.x, point.y - center.y) || 1; resizeStartValue = target === 'text' ? parseFloat(fontSize.value || 48) : parseFloat(imageWidth.value || 140); activeResize = target; openForTarget(target); e.preventDefault(); e.stopPropagation(); } function startRotate(target, e) { if (!previewReady) return; if (target === 'image' && repeatLogoWatermark.checked) return; activeRotate = target; openForTarget(target); e.preventDefault(); e.stopPropagation(); } function handlePointerMove(e) { if (!previewReady) return; const point = clientPointFromEvent(e); const rect = getCanvasRect(); if (activeDrag) { const isRepeat = activeDrag === 'text' ? repeatWatermark.checked : repeatLogoWatermark.checked; const state = activeDrag === 'text' ? (isRepeat ? textRepeatState : textState) : imageState; const localX = point.x - rect.left - dragOffsetX; const localY = point.y - rect.top - dragOffsetY; state.xRatio = localX / rect.width; state.yRatio = localY / rect.height; clampState(state); if (activeDrag === 'text') applyTextOverlay(); else applyImageOverlay(); e.preventDefault(); return; } if (activeResize) { const center = getCenterFromState(activeResize === 'text' ? textState : imageState); const distance = Math.hypot(point.x - center.x, point.y - center.y) || 1; const ratio = distance / resizeStartDistance; if (activeResize === 'text') { fontSize.value = String(Math.max(8, Math.min(300, Math.round(resizeStartValue * ratio)))); applyAllOverlays(); } else { imageWidth.value = String(Math.max(20, Math.min(500, Math.round(resizeStartValue * ratio)))); applyAllOverlays(); } e.preventDefault(); return; } if (activeRotate) { const state = activeRotate === 'text' ? textState : imageState; const center = getCenterFromState(state); const angle = Math.atan2(point.y - center.y, point.x - center.x) * 180 / Math.PI + 90; if (activeRotate === 'text') { textRotation.value = String(Math.round(angle)); applyTextOverlay(); } else { imageRotation.value = String(Math.round(angle)); applyImageOverlay(); } e.preventDefault(); } } function endPointerAction() { activeDrag = null; activeResize = null; activeRotate = null; } async function jumpToPage() { if (!pdfDoc) return; const total = pdfDoc.numPages; const requested = parseInt(pageJumpInput.value, 10); if (!Number.isFinite(requested) || requested < 1 || requested > total) { alert(`Enter a page number between 1 and ${total}.`); pageJumpInput.value = pdfPageIndex; return; } pdfPageIndex = requested; await renderPdfPage(); }

pdfInput.addEventListener('change', async (e) => { const file = e.target.files?.[0]; if (file) { await loadPdf(file); // Reset input value so the same file can be re-uploaded after removal pdfInput.value = ''; } });

wmImageInput.addEventListener('change', async (e) => { const file = e.target.files?.[0]; if (file) { await loadWatermarkImage(file); wmImageInput.value = ''; } });

['dragenter', 'dragover'].forEach(eventName => { pdfDropArea.addEventListener(eventName, (e) => { e.preventDefault(); e.stopPropagation(); pdfDropArea.classList.add('dragover'); }); });

['dragleave', 'drop'].forEach(eventName => { pdfDropArea.addEventListener(eventName, (e) => { e.preventDefault(); e.stopPropagation(); pdfDropArea.classList.remove('dragover'); }); });

pdfDropArea.addEventListener('drop', async (e) => { const file = e.dataTransfer?.files?.[0]; if (!file) return; if (file.type !== 'application/pdf' && !file.name.toLowerCase().endsWith('.pdf')) { alert('Please drop a valid PDF file.'); return; } await loadPdf(file); });

removePdfBtn.addEventListener('click', resetPdfUi); removeLogoBtn.addEventListener('click', removeLogoWatermark);

pageRangeMode.addEventListener('change', () => { updatePageRangeUi(pageRangeMode, pageRange, pageRangeHelper, 'text'); applyAllOverlays(); }); imagePageRangeMode.addEventListener('change', () => { updatePageRangeUi(imagePageRangeMode, imagePageRange, imagePageRangeHelper, 'image'); applyAllOverlays(); });

updatePageRangeUi(pageRangeMode, pageRange, pageRangeHelper, 'text'); updatePageRangeUi(imagePageRangeMode, imagePageRange, imagePageRangeHelper, 'image');

[ showTextWatermark, showLogoWatermark, wmText, enableTextLink, wmTextLink, enableImageLink, wmImageLink, fontSize, fontFamily, fontColor, textRotation, textBold, textItalic, textUnderline, textOpacity, imageWidth, imageRotation, imageOpacity, languageSelect, repeatWatermark, repeatLogoWatermark, repeatGap, pageRangeMode, pageRange, imagePageRangeMode, imagePageRange ].forEach(el => { el.addEventListener('input', applyAllOverlays); el.addEventListener('change', applyAllOverlays); });

languageSwitch.querySelectorAll('[data-language]').forEach(btn => { btn.addEventListener('click', () => { languageSelect.value = btn.dataset.language; syncLanguageAndFonts(btn.dataset.language, fontFamily.value); openTextToolbar(); }); });

extraLanguageSelect.addEventListener('change', () => { if (!extraLanguageSelect.value) return; languageSelect.value = extraLanguageSelect.value; syncLanguageAndFonts(extraLanguageSelect.value, fontFamily.value); openTextToolbar(); });

languageSelect.addEventListener('change', () => { syncLanguageAndFonts(languageSelect.value, fontFamily.value); openTextToolbar(); });

fontFamily.addEventListener('change', () => { updateSelectPreviewStyle(fontFamily, fontFamily.value); toolbarFont.value = fontFamily.value; updateSelectPreviewStyle(toolbarFont, toolbarFont.value); applyAllOverlays(); openTextToolbar(); });

fontSizeRange.addEventListener('input', () => { fontSize.value = fontSizeRange.value; applyAllOverlays(); });

imageWidthRange.addEventListener('input', () => { imageWidth.value = imageWidthRange.value; applyAllOverlays(); });

watermarkToolRoot.querySelectorAll('.quickPos').forEach(btn => { btn.addEventListener('click', () => { if (btn.dataset.target === 'text') quickPosition(textState, btn.dataset.pos); else quickPosition(imageState, btn.dataset.pos); applyAllOverlays(); }); });

watermarkToolRoot.querySelectorAll('.repeat-preset').forEach(btn => { btn.addEventListener('click', () => { const group = btn.dataset.group; if (group === 'text') repeatWatermark.checked = true; else repeatLogoWatermark.checked = true;

repeatGap.value = btn.dataset.gap; setActiveRepeatPreset(group, btn.dataset.pattern, btn.dataset.label); applyAllOverlays(); }); });

watermarkToolRoot.querySelectorAll('.repeat-chip').forEach(btn => { btn.addEventListener('click', () => { const group = btn.dataset.group; if (group === 'text') repeatWatermark.checked = true; else repeatLogoWatermark.checked = true;

repeatGap.value = btn.dataset.gapOnly; applyAllOverlays(); }); });

toolbarTextQuickPosition.addEventListener('change', () => { applyToolbarQuickPosition('text', toolbarTextQuickPosition.value); });

toolbarLogoQuickPosition.addEventListener('change', () => { applyToolbarQuickPosition('logo', toolbarLogoQuickPosition.value); });

toolbarLogoPageRange.addEventListener('change', () => { imagePageRangeMode.value = toolbarLogoPageRange.value; updatePageRangeUi(imagePageRangeMode, imagePageRange, imagePageRangeHelper, 'image'); applyAllOverlays(); openImageToolbar(); });

toolbarTextRepeatToggle.addEventListener('change', () => { repeatWatermark.checked = toolbarTextRepeatToggle.checked; applyAllOverlays(); openTextToolbar(); });

toolbarLogoRepeatToggle.addEventListener('change', () => { repeatLogoWatermark.checked = toolbarLogoRepeatToggle.checked; applyAllOverlays(); openImageToolbar(); });

toolbarTextRepeatStyle.addEventListener('change', () => { applyToolbarRepeatStyle('text', toolbarTextRepeatStyle.value); });

toolbarLogoRepeatStyle.addEventListener('change', () => { applyToolbarRepeatStyle('logo', toolbarLogoRepeatStyle.value); });

generateBtn.addEventListener('click', generateWatermarkedPdf); downloadBtn.addEventListener('click', downloadGeneratedPdf);

toolbarBoldBtn.addEventListener('click', () => { textBold.checked = !textBold.checked; applyAllOverlays(); openTextToolbar(); }); toolbarItalicBtn.addEventListener('click', () => { textItalic.checked = !textItalic.checked; applyAllOverlays(); openTextToolbar(); }); toolbarUnderlineBtn.addEventListener('click', () => { textUnderline.checked = !textUnderline.checked; applyAllOverlays(); openTextToolbar(); });

toolbarColor.addEventListener('input', () => { fontColor.value = toolbarColor.value; applyAllOverlays(); openTextToolbar(); });

toolbarLanguage.addEventListener('change', () => { languageSelect.value = toolbarLanguage.value; syncLanguageAndFonts(toolbarLanguage.value, fontFamily.value); openTextToolbar(); });

toolbarFont.addEventListener('change', () => { fontFamily.value = toolbarFont.value; updateSelectPreviewStyle(fontFamily, fontFamily.value); updateSelectPreviewStyle(toolbarFont, toolbarFont.value); applyAllOverlays(); openTextToolbar(); });

toolbarSize.addEventListener('input', () => { fontSize.value = toolbarSize.value || '48'; applyAllOverlays(); openTextToolbar(); });

toolbarSizeRange.addEventListener('input', () => { fontSize.value = toolbarSizeRange.value; applyAllOverlays(); openTextToolbar(); });

toolbarOpacity.addEventListener('input', () => { textOpacity.value = toolbarOpacity.value; applyTextOverlay(); openTextToolbar(); });

if (toolbarCenterBtn) { toolbarCenterBtn.addEventListener('click', () => { textState.xRatio = 0.5; textState.yRatio = 0.15; applyAllOverlays(); openTextToolbar(); }); }

toolbarRemoveTextBtn.addEventListener('click', removeTextWatermark);

toolbarImageOpacity.addEventListener('input', () => { imageOpacity.value = toolbarImageOpacity.value; applyImageOverlay(); openImageToolbar(); });

toolbarImageSize.addEventListener('input', () => { imageWidth.value = toolbarImageSize.value || '140'; applyImageOverlay(); openImageToolbar(); });

toolbarImageSizeRange.addEventListener('input', () => { imageWidth.value = toolbarImageSizeRange.value; applyImageOverlay(); openImageToolbar(); });

toolbarImageLayerBtn.addEventListener('click', () => { imageLayerMode.value = imageLayerMode.value === 'belowPdf' ? 'overPdf' : 'belowPdf'; applyImageOverlay(); openImageToolbar(); });

toolbarRemoveImageBtn.addEventListener('click', removeLogoWatermark);

resetTextPosBtn.addEventListener('click', () => { textState.xRatio = 0.5; textState.yRatio = 0.15; applyAllOverlays(); });

resetImagePosBtn.addEventListener('click', () => { imageState.xRatio = 0.5; imageState.yRatio = 0.5; applyAllOverlays(); });

prevPage.addEventListener('click', async () => { if (!pdfDoc || pdfPageIndex <= 1) return; pdfPageIndex--; await renderPdfPage(); }); nextPage.addEventListener('click', async () => { if (!pdfDoc || pdfPageIndex >= pdfDoc.numPages) return; pdfPageIndex++; await renderPdfPage(); });

pageJumpBtn.addEventListener('click', jumpToPage); pageJumpInput.addEventListener('keydown', async (e) => { if (e.key === 'Enter') { e.preventDefault(); await jumpToPage(); } });

wmText.addEventListener('input', () => { let detectedLanguage = languageSelect.value; if (/[\u0590-\u05FF]/.test(wmText.value)) detectedLanguage = 'hebrew'; else if (/[\u0900-\u097F]/.test(wmText.value)) detectedLanguage = 'hindi'; else if (containsArabic(wmText.value)) { detectedLanguage = ['arabic', 'urdu', 'persian'].includes(languageSelect.value) ? languageSelect.value : 'arabic'; } else { detectedLanguage = 'english'; }

if (detectedLanguage !== languageSelect.value) { syncLanguageAndFonts(detectedLanguage, fontFamily.value); } if (wmText.value.trim()) showTextWatermark.checked = true; if (!textVisual.classList.contains('editing')) applyTextOverlay(); });

wmTextLink.addEventListener('input', applyTextOverlay); wmImageLink.addEventListener('input', applyImageOverlay);

enableTextLink.addEventListener('change', () => { syncHyperlinkUi(); applyTextOverlay(); });

enableImageLink.addEventListener('change', () => { syncHyperlinkUi(); applyImageOverlay(); });

textOverlay.addEventListener('pointerdown', (e) => { if (textVisual.classList.contains('editing') || !showTextWatermark.checked) return; if (e.target === textRotateHandle || e.target === textResizeHandle) return; startDrag('text', e); });

imageOverlay.addEventListener('pointerdown', (e) => { if (!showLogoWatermark.checked || repeatLogoWatermark.checked) return; if (e.target === imageRotateHandle || e.target === imageResizeHandle) return; startDrag('image', e); });

repeatImageLayer.addEventListener('pointerdown', (e) => { if (!showLogoWatermark.checked || !repeatLogoWatermark.checked || repeatImageLayer.classList.contains('hidden')) return; startDrag('image', e); });

repeatTextLayer.addEventListener('pointerdown', (e) => { if (!showTextWatermark.checked || !repeatWatermark.checked || repeatTextLayer.classList.contains('hidden')) return; startDrag('text', e); });

textOverlay.addEventListener('click', (e) => { if (textToolbar.contains(e.target)) return; openTextToolbar(); // On touch/mobile, a single tap also triggers inline text editing for convenience if (window.matchMedia('(max-width:600px)').matches && !textVisual.classList.contains('editing')) { startInlineTextEdit(); } });

imageOverlay.addEventListener('click', (e) => { if (imageToolbar.contains(e.target)) return; openImageToolbar(); });

repeatImageLayer.addEventListener('click', (e) => { if (imageToolbar.contains(e.target)) return; openImageToolbar(); });

repeatTextLayer.addEventListener('click', (e) => { if (textToolbar.contains(e.target)) return; openTextToolbar(); });

textRotateHandle.addEventListener('pointerdown', (e) => startRotate('text', e)); imageRotateHandle.addEventListener('pointerdown', (e) => startRotate('image', e)); textResizeHandle.addEventListener('pointerdown', (e) => startResize('text', e)); imageResizeHandle.addEventListener('pointerdown', (e) => startResize('image', e));

textPreview.addEventListener('dblclick', (e) => { e.stopPropagation(); startInlineTextEdit(); });

textPreview.addEventListener('blur', () => { if (textVisual.classList.contains('editing')) stopInlineTextEdit(); });

textPreview.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); textPreview.blur(); } if (e.key === 'Escape') { e.preventDefault(); textPreview.textContent = wmText.value || ''; textPreview.blur(); } });

function keepToolbarInteractive(toolbar, reopenFn) { ['pointerdown', 'mousedown', 'touchstart', 'click'].forEach(evtName => { toolbar.addEventListener(evtName, (e) => { e.stopPropagation(); reopenFn(); }, { passive: false }); }); }

keepToolbarInteractive(textToolbar, openTextToolbar); keepToolbarInteractive(imageToolbar, openImageToolbar);

document.addEventListener('pointermove', handlePointerMove, { passive: false }); document.addEventListener('pointerup', endPointerAction); document.addEventListener('pointercancel', endPointerAction);

stage.addEventListener('pointerdown', (e) => { if (textToolbar.contains(e.target)) { openTextToolbar(); return; } if (imageToolbar.contains(e.target)) { openImageToolbar(); return; }

const insideText = textOverlay.contains(e.target) || repeatTextLayer.contains(e.target); const insideImage = imageOverlay.contains(e.target); const insideRepeatLayer = repeatImageLayer.contains(e.target);

if (!insideText && !insideImage && !insideRepeatLayer) closeToolbars(); else if (insideText && !insideImage && !insideRepeatLayer) { closeToolbars(); openTextToolbar(); } else if ((insideImage || insideRepeatLayer) && !insideText) { closeToolbars(); openImageToolbar(); } });

window.addEventListener('resize', () => { applyAllOverlays(); if (textToolbar.classList.contains('open')) positionTextToolbar(); if (imageToolbar.classList.contains('open')) positionImageToolbar(); });

function applyTextOverlay() { textState.rotation = parseFloat(textRotation.value || 0); textState.size = parseFloat(fontSize.value || 48); textState.opacity = parseFloat(textOpacity.value || 0.80); textState.language = languageSelect.value; textState.overPdf = textLayerMode.value !== 'belowPdf';

const nextText = preparePdfText(wmText.value); applyTextDirection(nextText); if (document.activeElement !== textPreview) textPreview.textContent = nextText;

textOverlay.style.left = `${textState.xRatio * pdfCanvas.width}px`; textOverlay.style.top = `${textState.yRatio * pdfCanvas.height}px`; textOverlay.style.transform = `translate(-50%, -50%) rotate(${textState.rotation}deg)`;

textPreview.style.fontSize = `${textState.size}px`; textPreview.style.fontFamily = getPreviewFontStack(fontFamily.value); textPreview.style.color = fontColor.value; textPreview.style.fontWeight = textBold.checked ? '700' : '400'; textPreview.style.fontStyle = textItalic.checked ? 'italic' : 'normal'; const liveLink = showTextWatermark.checked && enableTextLink.checked ? normalizeHyperlink(wmTextLink.value) : ''; textPreview.style.textDecoration = textUnderline.checked ? 'underline' : (liveLink ? 'underline dotted' : 'none'); textPreview.style.opacity = String(textState.opacity); textPreview.style.cursor = liveLink ? 'pointer' : 'move'; textPreview.title = liveLink || ''; textVisual.classList.toggle('has-link', !!liveLink); textOverlay.style.zIndex = textState.overPdf ? '18' : '8'; textOverlay.style.mixBlendMode = textState.overPdf ? 'normal' : 'multiply';

// Show single overlay only when NOT in repeat mode if (previewReady && showTextWatermark.checked && nextText && isCurrentPageSelectedForText() && !repeatWatermark.checked) { textOverlay.classList.remove('hidden'); } else { textOverlay.classList.add('hidden'); // Only close toolbar if NOT in repeat mode — repeat mode keeps toolbar open if (!repeatWatermark.checked) { textToolbarShell.classList.remove('open'); textToolbar.classList.remove('open'); } }

// In repeat mode, ensure toolbar stays open/visible as a standalone toolbar if (previewReady && showTextWatermark.checked && nextText && isCurrentPageSelectedForText() && repeatWatermark.checked) { textToolbarShell.classList.add('open'); textToolbar.classList.add('open'); }

syncTextToolbar(); renderRepeatedTextPreview(); positionTextToolbar(); }

function applyImageOverlay() { imageState.rotation = parseFloat(imageRotation.value || 0); imageState.width = parseFloat(imageWidth.value || 140); imageState.opacity = parseFloat(imageOpacity.value || 0.80); imageState.overPdf = imageLayerMode.value !== 'belowPdf';

imageOverlay.style.left = `${imageState.xRatio * pdfCanvas.width}px`; imageOverlay.style.top = `${imageState.yRatio * pdfCanvas.height}px`; imageOverlay.style.transform = `translate(-50%, -50%) rotate(${imageState.rotation}deg)`; imagePreview.style.width = `${imageState.width}px`; imagePreview.style.height = 'auto'; imagePreview.style.opacity = String(imageState.opacity);

if (imageState.overPdf) { imageOverlay.style.zIndex = '20'; imageOverlay.style.mixBlendMode = 'normal'; } else { imageOverlay.style.zIndex = '12'; imageOverlay.style.mixBlendMode = 'multiply'; }

const liveImageLink = showLogoWatermark.checked && enableImageLink.checked ? normalizeHyperlink(wmImageLink.value) : ''; imageVisual.classList.toggle('has-link', !!liveImageLink); imageVisual.style.cursor = liveImageLink ? 'pointer' : 'move'; imageVisual.title = liveImageLink || '';

if (wmImageBytes && previewReady && showLogoWatermark.checked && !repeatLogoWatermark.checked && isCurrentPageSelectedForImage()) { imageOverlay.classList.remove('hidden'); } else { imageOverlay.classList.add('hidden'); }

if (!(wmImageBytes && previewReady && showLogoWatermark.checked && isCurrentPageSelectedForImage())) { imageToolbarShell.classList.remove('open'); imageToolbar.classList.remove('open'); }

syncImageToolbar(); renderRepeatedImagePreview(); positionImageToolbar(); }

async function buildWatermarkedPdfBlob() { if (!pdfBytes) return alert('Please upload a PDF first.'); if (!previewReady) return alert('Please wait for preview.');

const sourcePdf = await PDFDocument.load(freshPdfBytes(), { ignoreEncryption: true }); const outPdf = await PDFDocument.create();

const textPages = parsePageRange(pageRangeMode, pageRange, sourcePdf.getPageCount()); const imagePages = getSelectedImagePages(sourcePdf.getPageCount());

const preparedText = showTextWatermark.checked ? preparePdfText(wmText.value) : ''; const textHyperlink = getWatermarkHyperlink(); const imageHyperlink = getImageWatermarkHyperlink();

const textAsset = preparedText ? await createTextWatermarkAsset(outPdf) : null; const imageEmbed = showLogoWatermark.checked && wmImageBytes ? await embedWatermarkImage(outPdf) : null;

const sourcePages = sourcePdf.getPages(); const embeddedPages = await outPdf.embedPages(sourcePages);

for (let i = 0; i < sourcePages.length; i++) { const pageNumber = i + 1; const sourcePage = sourcePages[i]; const embeddedPage = embeddedPages[i]; const { width, height } = sourcePage.getSize(); const page = outPdf.addPage([width, height]); const shouldDrawImageOnPage = imagePages.has(pageNumber) && imageEmbed; const keepFirstPageLogoVisible = pageNumber === 1; if (textPages.has(pageNumber) && textAsset && preparedText && !textState.overPdf) { if (repeatWatermark.checked) { drawRepeatedTextWatermarkNative(outPdf, page, textAsset, width, height, textHyperlink); } else { const anchor = getTextAnchorForPdf(width, height); drawSingleTextWatermarkNative(outPdf, page, textAsset, width, height, anchor.x, anchor.y, textHyperlink); } } if (shouldDrawImageOnPage && !imageState.overPdf && !keepFirstPageLogoVisible) { if (repeatLogoWatermark.checked) { drawRepeatedImageWatermarks(outPdf, page, imageEmbed, width, height, imageHyperlink); } else { const anchor = getImageAnchorForPdf(width, height); drawSingleImageWatermark(outPdf, page, imageEmbed, width, height, anchor.x, anchor.y, imageHyperlink); } } page.drawPage(embeddedPage, { x: 0, y: 0, width, height }); if (textPages.has(pageNumber) && textAsset && preparedText && textState.overPdf) { if (repeatWatermark.checked) { drawRepeatedTextWatermarkNative(outPdf, page, textAsset, width, height, textHyperlink); } else { const anchor = getTextAnchorForPdf(width, height); drawSingleTextWatermarkNative(outPdf, page, textAsset, width, height, anchor.x, anchor.y, textHyperlink); } } if (shouldDrawImageOnPage && (imageState.overPdf || keepFirstPageLogoVisible)) { if (repeatLogoWatermark.checked) { drawRepeatedImageWatermarks(outPdf, page, imageEmbed, width, height, imageHyperlink); } else { const anchor = getImageAnchorForPdf(width, height); drawSingleImageWatermark(outPdf, page, imageEmbed, width, height, anchor.x, anchor.y, imageHyperlink); } } } const outBytes = await outPdf.save(); return new Blob([outBytes], { type: 'application/pdf' }); } textLayerMode.addEventListener('change', () => { applyTextOverlay(); if (showTextWatermark.checked) openTextToolbar(); });

imageLayerMode.addEventListener('change', () => { applyImageOverlay(); if (showLogoWatermark.checked) openImageToolbar(); });

toolbarTextLayerBtn.addEventListener('click', () => { textLayerMode.value = textLayerMode.value === 'belowPdf' ? 'overPdf' : 'belowPdf'; applyTextOverlay(); openTextToolbar(); });

textToolbarMoreBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleToolbarExpanded('text', true); openTextToolbar(); });

if (textToolbarLessBtn) { textToolbarLessBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleToolbarExpanded('text', false); openTextToolbar(); }); }

if (imageToolbarMoreBtn) { imageToolbarMoreBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleToolbarExpanded('image', true); openImageToolbar(); }); }

if (imageToolbarLessBtn) { imageToolbarLessBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleToolbarExpanded('image', false); openImageToolbar(); }); }

textToolbarCloseBtn.addEventListener('click', (e) => { e.stopPropagation(); closeToolbars(); });

imageToolbarCloseBtn.addEventListener('click', (e) => { e.stopPropagation(); closeToolbars(); });

setActiveRepeatPreset('text', 'grid', 'Classic Grid'); setActiveRepeatPreset('logo', 'grid', 'Classic Grid'); applyAllOverlays(); })();

قد تعجبك أيضاً
  • القديم فل
  • مهمة استغرقت 25 دقيقة، أُطلقت خلالها 24 صاروخاً؛ تدمير 9 مواقع إرهابية ومقتل 70 عنصراً إرهابيا
  • Untitled
  • نداء الهند إلى أصحاب الخير
شاركها 0 FacebookTwitterPinterestEmail
Nidaul Hind

المقالة السابقة
إسهامات يوسف الكاندهلوي في مجال الأدب العربي الإسلامي
المقالة التالية
Free PDF Watermark Tool – Add Text & Logo Watermarks Online

قد تعجبك أيضاً

Free PDF Watermark Tool – Add Text & Logo Watermarks Online

يونيو 23, 2026

الحاشية على صدرا لأستاذ الهند نظام الدين السهالوي

يونيو 21, 2026

شعر: الفراق

ديسمبر 10, 2025

Untitled

ديسمبر 2, 2025

الكتب، والرسائل والمكتوبات

نوفمبر 28, 2025

Untitled

نوفمبر 22, 2025

اترك تعليقًا إلغاء الرد

احفظ اسمي، البريد الإلكتروني، والموقع الإلكتروني في هذا المتصفح للمرة القادمة التي سأعلق فيها.

Recent Comments

لا توجد تعليقات للعرض.

Categories

  • PDF
    197
  • أدب كيرالا
    0
  • أدبيات
    47
  • إسهام علماء الهندية
    60
  • الأدب العربي العالمي
    0
  • الأدب العربي الهندي
    60
  • الأشعار
    76
  • الأعلام المسلمة
    0
  • الأعلام،
    58
  • التاريخ الأسلامي
    25
  • العلاقات الهندية،
    28
  • القضايا الوطنية الهندية
    0
  • الكتب
    36
  • الملوكيات
    31
  • تاريخ الهند
    0
  • تراث الهند،
    0
  • تراجم العلماء
    0
  • ثقافة كيرالا
    34
  • دراسات
    292
  • دراسات أدبية
    51
  • كيرالا
    45
  • مقالات
    48
  • نداء الهند،
    0
  • نداء الهند،،نداء الهند
    0

تابعونا

Top Selling Multipurpose WP Theme

المنشورات الحديثة

  • شرح قصيدة “واحرَّ قلباه” للمتنبي كاملة مع معاني المفردات والتحليل

    يونيو 23, 2026
  • Free PDF Watermark Tool – Add Text & Logo Watermarks Online

    يونيو 23, 2026
  • fdfsdfdfd

    يونيو 23, 2026
  • إسهامات يوسف الكاندهلوي في مجال الأدب العربي الإسلامي

    يونيو 23, 2026
  • الحاشية على صدرا لأستاذ الهند نظام الدين السهالوي

    يونيو 21, 2026
  • الذكاء الاصطناعي ومستقبل البشرية: بين كفاءة الآلة وإبداع الإنسان

    يونيو 21, 2026

تغذية الشبكات الاجتماعية

تغذية الشبكات الاجتماعية

اختيارات المحررين

قصة وصول الإسلام إلى الهند دراسة لا تنسى

أكتوبر 29, 2015

قصيدة للدكتور محمد إقبال شاعر الهند والملة في...

أكتوبر 29, 2015

لمحة عن التراث العلمي للهند

أكتوبر 29, 2015

قَصِيدَةُ “اَلَّفَ الأَلِفُ ” لِلشَّيْخِ الْكَامِلِ عُمَرَالْقَاهِرِي رَحِمَهُ...

أكتوبر 30, 2015

الصَّلاَةُ البَهِيَّةُ فِي مَدْحِ خَيْرِالْبَرِيَّةِ للأستاذ شافع الهدوي

نوفمبر 3, 2015

Most Read Categories

دراسات

292 المقالات

PDF

197 المقالات

تراث الهند،

130 المقالات

تاريخ الهند

111 المقالات

الأشعار

76 المقالات

تراجم العلماء

65 المقالات

Most Read Article

الأهمية السياسية والتاريخية لاتحاد الطلاب المسلمين في الهند
الرمال المتحركة للإسلام السياسي: من الصعود إلى التكيف
الاتجاه الحديث في الأدب العربي اليمني
الهمزية النبوية لأحمد شوقي مع شروح وتعليقات (تحليل الألفاظ، وتفسير المعاني)
الفكاهة عند العرب

Nidaul Hind

nidaulhind.com
  • الرئيسة
  • ملوكيات
    • الملوكيات
    • المغوليات
    • الغزنويات
  • كيرالا
    • كيرالا
    • ثقافة كيرالا
    • أدب كيرالا
    • أعلام كيرالا
  • أرشيف PDF
  • قسم التراجم
    • تراجم العلماء المسلمين
    • تراجم الأعلام
    • الأعلام العامة
    • الأعلام المسلمة
  • أدبيات
    • أدبيات عامة
    • الأدب العربي الهندي
    • الأدب العربي العالمي
  • أقسام الشعر
    • أشعار عامة
    • المراثي
    • الأماديح النبوية
    • الأشعار الوطنية
  • قسم الديانات
    • الأديان الهندية
    • الهندوسية
    • البوذية
    • الجينية
    • السيخية
  • الهنديات
    • تراث الهند
    • تاريخ الهند
    • علوم الهند
    • العلاقات الهندية
    • إسهامات العلماء الهندية
nidaulhind.com

ختيارات المحررين

  • شرح قصيدة “واحرَّ قلباه” للمتنبي كاملة مع معاني المفردات والتحليل

    يونيو 23, 2026
  • Free PDF Watermark Tool – Add Text & Logo Watermarks Online

    يونيو 23, 2026
  • fdfsdfdfd

    يونيو 23, 2026
  • إسهامات يوسف الكاندهلوي في مجال الأدب العربي الإسلامي

    يونيو 23, 2026
  • الحاشية على صدرا لأستاذ الهند نظام الدين السهالوي

    يونيو 21, 2026
  • الذكاء الاصطناعي ومستقبل البشرية: بين كفاءة الآلة وإبداع الإنسان

    يونيو 21, 2026
Copyright © Nidaul Hind