{"id":676,"date":"2026-07-16T09:40:26","date_gmt":"2026-07-16T09:40:26","guid":{"rendered":"https:\/\/vikassrivastava.in\/academy\/?page_id=676"},"modified":"2026-07-16T09:58:09","modified_gmt":"2026-07-16T09:58:09","slug":"pdf-converter-studio","status":"publish","type":"page","link":"https:\/\/vikassrivastava.in\/academy\/pdf-converter-studio\/","title":{"rendered":"PDF Converter Studio"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"676\" class=\"elementor elementor-676\">\n\t\t\t\t<div class=\"elementor-element elementor-element-00e4c47 e-con e-atomic-element e-flexbox-base e-2604c97 \" data-id=\"00e4c47\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"00e4c47\">\n    \t\t<div class=\"elementor-element elementor-element-80a4e20 elementor-widget elementor-widget-html\" data-id=\"80a4e20\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\r\nPDF Studio Pro \u2013 WordPress-compatible browser PDF tools\r\nPaste this complete code into a WordPress \"Custom HTML\" block.\r\nImportant: WordPress may remove <script> tags for non-admin users.\r\nAll PDF processing is performed in the browser.\r\n-->\r\n<div id=\"pdfsp-app\" class=\"pdfsp-app\">\r\n  <header class=\"pdfsp-header\">\r\n    <div class=\"pdfsp-brand\">\r\n      <div class=\"pdfsp-logo\" aria-hidden=\"true\">PDF<\/div>\r\n      <div>\r\n        <div class=\"pdfsp-brand-name\">PDF Studio Pro<\/div>\r\n        <div class=\"pdfsp-tagline\" data-i18n=\"tagline\">Simple, Fast and Free PDF Tools<\/div>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <div class=\"pdfsp-header-actions\">\r\n      <select id=\"pdfsp-language\" class=\"pdfsp-select\" aria-label=\"Language\">\r\n        <option value=\"en\">English<\/option>\r\n        <option value=\"hi\">\u0939\u093f\u0928\u094d\u0926\u0940<\/option>\r\n      <\/select>\r\n      <button id=\"pdfsp-theme-toggle\" class=\"pdfsp-icon-btn\" type=\"button\" aria-label=\"Toggle dark mode\">\u25d0<\/button>\r\n    <\/div>\r\n  <\/header>\r\n\r\n  <section class=\"pdfsp-hero\">\r\n    <div class=\"pdfsp-hero-content\">\r\n      <span class=\"pdfsp-eyebrow\">100% Browser Based<\/span>\r\n      <h1 data-i18n=\"heroTitle\">Every PDF Tool You Need, in One Place<\/h1>\r\n      <p data-i18n=\"heroText\">Merge, split, organize, convert, stamp, number, clean and prepare PDF files directly in your browser.<\/p>\r\n      <div class=\"pdfsp-privacy-note\">\r\n        <span aria-hidden=\"true\">\ud83d\udd12<\/span>\r\n        <span data-i18n=\"privacyNote\">Your files stay on your device and are not uploaded to our server.<\/span>\r\n      <\/div>\r\n    <\/div>\r\n  <\/section>\r\n\r\n  <main class=\"pdfsp-main\">\r\n    <div class=\"pdfsp-search-wrap\">\r\n      <input id=\"pdfsp-tool-search\" class=\"pdfsp-search\" type=\"search\" placeholder=\"Search PDF tools\u2026\" data-i18n-placeholder=\"searchPlaceholder\">\r\n    <\/div>\r\n\r\n    <div id=\"pdfsp-tool-grid\" class=\"pdfsp-tool-grid\" aria-label=\"PDF tools\">\r\n      <button class=\"pdfsp-tool-card is-active\" type=\"button\" data-tool=\"merge\" data-search=\"merge combine pdf\">\r\n        <span class=\"pdfsp-tool-icon\">\u21c9<\/span>\r\n        <strong data-i18n=\"merge\">Merge PDF<\/strong>\r\n        <small data-i18n=\"mergeDesc\">Combine multiple PDF files<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"split\" data-search=\"split extract pages pdf\">\r\n        <span class=\"pdfsp-tool-icon\">\u2702<\/span>\r\n        <strong data-i18n=\"split\">Split PDF<\/strong>\r\n        <small data-i18n=\"splitDesc\">Extract ranges or individual pages<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"organize\" data-search=\"rotate delete reorder organize pages\">\r\n        <span class=\"pdfsp-tool-icon\">\u25a6<\/span>\r\n        <strong data-i18n=\"organize\">Organize PDF<\/strong>\r\n        <small data-i18n=\"organizeDesc\">Rotate, delete and reorder pages<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"imageToPdf\" data-search=\"jpg png webp image to pdf\">\r\n        <span class=\"pdfsp-tool-icon\">\u25a7<\/span>\r\n        <strong data-i18n=\"imageToPdf\">Images to PDF<\/strong>\r\n        <small data-i18n=\"imageToPdfDesc\">Convert JPG, PNG or WEBP<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"pdfToImage\" data-search=\"pdf to jpg png image\">\r\n        <span class=\"pdfsp-tool-icon\">\u25eb<\/span>\r\n        <strong data-i18n=\"pdfToImage\">PDF to Images<\/strong>\r\n        <small data-i18n=\"pdfToImageDesc\">Export PDF pages as JPG or PNG<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"watermark\" data-search=\"watermark text pdf\">\r\n        <span class=\"pdfsp-tool-icon\">W<\/span>\r\n        <strong data-i18n=\"watermark\">Watermark PDF<\/strong>\r\n        <small data-i18n=\"watermarkDesc\">Add a text watermark<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"pageNumbers\" data-search=\"page number footer pdf\">\r\n        <span class=\"pdfsp-tool-icon\">#<\/span>\r\n        <strong data-i18n=\"pageNumbers\">Page Numbers<\/strong>\r\n        <small data-i18n=\"pageNumbersDesc\">Add page numbers to a PDF<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"headerFooter\" data-search=\"header footer document office pdf\">\r\n        <span class=\"pdfsp-tool-icon\">H\/F<\/span>\r\n        <strong>Header & Footer<\/strong>\r\n        <small>Add document header and footer text<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"bates\" data-search=\"bates numbering legal audit office pdf\">\r\n        <span class=\"pdfsp-tool-icon\">B<\/span>\r\n        <strong>Bates Numbering<\/strong>\r\n        <small>Add sequential document-control numbers<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"stamp\" data-search=\"stamp approved draft confidential reviewed pdf\">\r\n        <span class=\"pdfsp-tool-icon\">\u2713<\/span>\r\n        <strong>Stamp PDF<\/strong>\r\n        <small>Add Approved, Draft or custom stamps<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"extractText\" data-search=\"extract text copy pdf office\">\r\n        <span class=\"pdfsp-tool-icon\">TXT<\/span>\r\n        <strong>Extract Text<\/strong>\r\n        <small>Download selectable PDF text as TXT<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"metadata\" data-search=\"metadata author title subject remove clean pdf\">\r\n        <span class=\"pdfsp-tool-icon\">i<\/span>\r\n        <strong>PDF Metadata<\/strong>\r\n        <small>View or remove document properties<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"flatten\" data-search=\"flatten pdf forms annotations print office\">\r\n        <span class=\"pdfsp-tool-icon\">\u25a4<\/span>\r\n        <strong>Flatten PDF<\/strong>\r\n        <small>Create a fixed print-ready PDF copy<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"resize\" data-search=\"resize page size a4 letter legal pdf\">\r\n        <span class=\"pdfsp-tool-icon\">\u2194<\/span>\r\n        <strong>Resize Pages<\/strong>\r\n        <small>Convert pages to A4, A3, Letter or Legal<\/small>\r\n      <\/button>\r\n\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"editText\" data-search=\"edit change replace text pdf office\">\r\n        <span class=\"pdfsp-tool-icon\">\u270e<\/span>\r\n        <strong>Edit PDF Text<\/strong>\r\n        <small>Cover existing text and add replacement text<\/small>\r\n      <\/button>\r\n      <button class=\"pdfsp-tool-card\" type=\"button\" data-tool=\"comparePdf\" data-search=\"compare two pdf difference highlight content text\">\r\n        <span class=\"pdfsp-tool-icon\">\u2260<\/span>\r\n        <strong>Compare 2 PDFs<\/strong>\r\n        <small>Highlight added, removed and changed text<\/small>\r\n      <\/button>\r\n    <\/div>\r\n\r\n    <section class=\"pdfsp-workspace\">\r\n      <div class=\"pdfsp-workspace-head\">\r\n        <div>\r\n          <h2 id=\"pdfsp-workspace-title\">Merge PDF<\/h2>\r\n          <p id=\"pdfsp-workspace-description\">Combine PDF files in your preferred order.<\/p>\r\n        <\/div>\r\n        <button id=\"pdfsp-reset\" type=\"button\" class=\"pdfsp-btn pdfsp-btn-light\" data-i18n=\"reset\">Reset<\/button>\r\n      <\/div>\r\n\r\n      <div id=\"pdfsp-alert\" class=\"pdfsp-alert\" role=\"alert\" aria-live=\"polite\" hidden><\/div>\r\n\r\n      <div id=\"pdfsp-upload-box\" class=\"pdfsp-upload-box\" tabindex=\"0\" role=\"button\" aria-label=\"Upload files\">\r\n        <div class=\"pdfsp-upload-icon\">\u21e7<\/div>\r\n        <h3 data-i18n=\"dropTitle\">Drop files here<\/h3>\r\n        <p data-i18n=\"dropText\">or click to browse files from your device<\/p>\r\n        <button type=\"button\" class=\"pdfsp-btn pdfsp-btn-primary\" data-i18n=\"chooseFiles\">Choose Files<\/button>\r\n        <small id=\"pdfsp-supported-text\">Supported: PDF \u2022 Maximum 100 MB per file<\/small>\r\n        <input id=\"pdfsp-file-input\" type=\"file\" hidden multiple>\r\n      <\/div>\r\n\r\n      <div id=\"pdfsp-file-list\" class=\"pdfsp-file-list\"><\/div>\r\n      <div id=\"pdfsp-options\" class=\"pdfsp-options\"><\/div>\r\n\r\n      <div id=\"pdfsp-progress-wrap\" class=\"pdfsp-progress-wrap\" hidden>\r\n        <div class=\"pdfsp-progress-label\">\r\n          <span id=\"pdfsp-progress-text\">Processing\u2026<\/span>\r\n          <span id=\"pdfsp-progress-percent\">0%<\/span>\r\n        <\/div>\r\n        <div class=\"pdfsp-progress-track\"><div id=\"pdfsp-progress-bar\" class=\"pdfsp-progress-bar\"><\/div><\/div>\r\n      <\/div>\r\n\r\n      <div class=\"pdfsp-action-bar\">\r\n        <button id=\"pdfsp-process\" type=\"button\" class=\"pdfsp-btn pdfsp-btn-primary pdfsp-btn-large\" disabled>\r\n          <span data-i18n=\"process\">Process Files<\/span>\r\n        <\/button>\r\n      <\/div>\r\n\r\n      <div id=\"pdfsp-result\" class=\"pdfsp-result\" hidden>\r\n        <div class=\"pdfsp-result-icon\">\u2713<\/div>\r\n        <h3 data-i18n=\"success\">Your file is ready<\/h3>\r\n        <p id=\"pdfsp-result-details\"><\/p>\r\n        <button id=\"pdfsp-download\" type=\"button\" class=\"pdfsp-btn pdfsp-btn-success\" data-i18n=\"download\">Download File<\/button>\r\n      <\/div>\r\n    <\/section>\r\n\r\n    <section class=\"pdfsp-info-grid\">\r\n      <article class=\"pdfsp-info-card\"><span>\ud83d\udd12<\/span><h3 data-i18n=\"secureTitle\">Private Processing<\/h3><p data-i18n=\"secureText\">Files are processed locally in your browser.<\/p><\/article>\r\n      <article class=\"pdfsp-info-card\"><span>\u26a1<\/span><h3 data-i18n=\"fastTitle\">Fast and Simple<\/h3><p data-i18n=\"fastText\">No account or compulsory registration is required.<\/p><\/article>\r\n      <article class=\"pdfsp-info-card\"><span>\ud83d\udcf1<\/span><h3 data-i18n=\"mobileTitle\">Mobile Friendly<\/h3><p data-i18n=\"mobileText\">Works on desktop, tablet and supported mobile browsers.<\/p><\/article>\r\n    <\/section>\r\n  <\/main>\r\n<\/div>\r\n\r\n<style>\r\n#pdfsp-app {\r\n  --pdfsp-primary:#5b3df5;\r\n  --pdfsp-primary-2:#7c66ff;\r\n  --pdfsp-success:#16885f;\r\n  --pdfsp-danger:#c83b4d;\r\n  --pdfsp-bg:#f5f7fc;\r\n  --pdfsp-surface:#ffffff;\r\n  --pdfsp-surface-2:#f0f2f8;\r\n  --pdfsp-text:#172033;\r\n  --pdfsp-muted:#667085;\r\n  --pdfsp-border:#dfe4ee;\r\n  --pdfsp-shadow:0 16px 45px rgba(32,44,78,.10);\r\n  color:var(--pdfsp-text);\r\n  background:var(--pdfsp-bg);\r\n  border-radius:22px;\r\n  overflow:hidden;\r\n  font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;\r\n  line-height:1.5;\r\n  box-sizing:border-box;\r\n}\r\n#pdfsp-app.pdfsp-dark {\r\n  --pdfsp-bg:#10131c;\r\n  --pdfsp-surface:#171c27;\r\n  --pdfsp-surface-2:#222937;\r\n  --pdfsp-text:#f1f4fb;\r\n  --pdfsp-muted:#a9b2c3;\r\n  --pdfsp-border:#30394a;\r\n  --pdfsp-shadow:0 16px 45px rgba(0,0,0,.25);\r\n}\r\n#pdfsp-app *, #pdfsp-app *::before, #pdfsp-app *::after {box-sizing:border-box}\r\n#pdfsp-app button, #pdfsp-app input, #pdfsp-app select {font:inherit}\r\n#pdfsp-app button {cursor:pointer}\r\n#pdfsp-app .pdfsp-header {\r\n  display:flex;align-items:center;justify-content:space-between;gap:20px;\r\n  padding:18px clamp(18px,4vw,54px);background:var(--pdfsp-surface);border-bottom:1px solid var(--pdfsp-border)\r\n}\r\n#pdfsp-app .pdfsp-brand {display:flex;align-items:center;gap:12px}\r\n#pdfsp-app .pdfsp-logo {\r\n  display:grid;place-items:center;width:48px;height:48px;border-radius:14px;color:#fff;font-weight:900;\r\n  background:linear-gradient(135deg,var(--pdfsp-primary),#ff5470);box-shadow:0 8px 20px rgba(91,61,245,.25)\r\n}\r\n#pdfsp-app .pdfsp-brand-name {font-weight:850;font-size:19px}\r\n#pdfsp-app .pdfsp-tagline {font-size:12px;color:var(--pdfsp-muted)}\r\n#pdfsp-app .pdfsp-header-actions {display:flex;align-items:center;gap:9px}\r\n#pdfsp-app .pdfsp-select, #pdfsp-app .pdfsp-icon-btn {\r\n  background:var(--pdfsp-surface-2);color:var(--pdfsp-text);border:1px solid var(--pdfsp-border);border-radius:11px;height:42px\r\n}\r\n#pdfsp-app .pdfsp-select {padding:0 12px}\r\n#pdfsp-app .pdfsp-icon-btn {width:42px;font-size:20px}\r\n#pdfsp-app .pdfsp-hero {\r\n  padding:clamp(42px,7vw,84px) 20px;text-align:center;\r\n  background:\r\n    radial-gradient(circle at 15% 20%,rgba(91,61,245,.15),transparent 28%),\r\n    radial-gradient(circle at 85% 30%,rgba(255,84,112,.12),transparent 25%),\r\n    var(--pdfsp-surface)\r\n}\r\n#pdfsp-app .pdfsp-hero-content {max-width:900px;margin:auto}\r\n#pdfsp-app .pdfsp-eyebrow {\r\n  display:inline-flex;padding:7px 13px;border-radius:999px;background:rgba(91,61,245,.11);\r\n  color:var(--pdfsp-primary);font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase\r\n}\r\n#pdfsp-app .pdfsp-hero h1 {font-size:clamp(34px,6vw,64px);line-height:1.07;margin:18px 0 16px;letter-spacing:-.045em}\r\n#pdfsp-app .pdfsp-hero p {max-width:720px;margin:0 auto;color:var(--pdfsp-muted);font-size:clamp(16px,2vw,20px)}\r\n#pdfsp-app .pdfsp-privacy-note {\r\n  display:inline-flex;align-items:center;gap:8px;margin-top:22px;padding:10px 15px;border:1px solid var(--pdfsp-border);\r\n  background:var(--pdfsp-surface);border-radius:12px;color:var(--pdfsp-muted);font-size:13px\r\n}\r\n#pdfsp-app .pdfsp-main {max-width:1240px;margin:auto;padding:30px clamp(14px,3vw,34px) 48px}\r\n#pdfsp-app .pdfsp-search-wrap {max-width:650px;margin:-54px auto 28px;position:relative;z-index:2}\r\n#pdfsp-app .pdfsp-search {\r\n  width:100%;height:58px;border:1px solid var(--pdfsp-border);border-radius:16px;padding:0 20px;\r\n  background:var(--pdfsp-surface);color:var(--pdfsp-text);box-shadow:var(--pdfsp-shadow);outline:none\r\n}\r\n#pdfsp-app .pdfsp-search:focus {border-color:var(--pdfsp-primary);box-shadow:0 0 0 4px rgba(91,61,245,.13)}\r\n#pdfsp-app .pdfsp-tool-grid {display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px;margin-bottom:28px}\r\n#pdfsp-app .pdfsp-tool-card {\r\n  min-height:150px;text-align:left;padding:18px;border:1px solid var(--pdfsp-border);border-radius:17px;\r\n  background:var(--pdfsp-surface);color:var(--pdfsp-text);transition:.2s ease;box-shadow:0 5px 18px rgba(20,30,60,.04)\r\n}\r\n#pdfsp-app .pdfsp-tool-card:hover {transform:translateY(-3px);border-color:var(--pdfsp-primary);box-shadow:var(--pdfsp-shadow)}\r\n#pdfsp-app .pdfsp-tool-card.is-active {border-color:var(--pdfsp-primary);box-shadow:0 0 0 3px rgba(91,61,245,.12)}\r\n#pdfsp-app .pdfsp-tool-icon {\r\n  display:grid;place-items:center;width:42px;height:42px;border-radius:12px;background:rgba(91,61,245,.11);\r\n  color:var(--pdfsp-primary);font-size:21px;font-weight:900;margin-bottom:13px\r\n}\r\n#pdfsp-app .pdfsp-tool-card strong, #pdfsp-app .pdfsp-tool-card small {display:block}\r\n#pdfsp-app .pdfsp-tool-card small {margin-top:5px;color:var(--pdfsp-muted);font-size:12px}\r\n#pdfsp-app .pdfsp-workspace {\r\n  background:var(--pdfsp-surface);border:1px solid var(--pdfsp-border);border-radius:22px;\r\n  padding:clamp(18px,3vw,32px);box-shadow:var(--pdfsp-shadow)\r\n}\r\n#pdfsp-app .pdfsp-workspace-head {display:flex;justify-content:space-between;align-items:flex-start;gap:20px;margin-bottom:22px}\r\n#pdfsp-app .pdfsp-workspace-head h2 {margin:0 0 5px;font-size:28px}\r\n#pdfsp-app .pdfsp-workspace-head p {margin:0;color:var(--pdfsp-muted)}\r\n#pdfsp-app .pdfsp-upload-box {\r\n  border:2px dashed var(--pdfsp-border);border-radius:18px;padding:38px 20px;text-align:center;\r\n  background:var(--pdfsp-surface-2);outline:none;transition:.2s\r\n}\r\n#pdfsp-app .pdfsp-upload-box:hover, #pdfsp-app .pdfsp-upload-box.is-dragging, #pdfsp-app .pdfsp-upload-box:focus {\r\n  border-color:var(--pdfsp-primary);background:rgba(91,61,245,.06)\r\n}\r\n#pdfsp-app .pdfsp-upload-icon {font-size:42px;color:var(--pdfsp-primary)}\r\n#pdfsp-app .pdfsp-upload-box h3 {margin:7px 0 4px}\r\n#pdfsp-app .pdfsp-upload-box p {margin:0 0 15px;color:var(--pdfsp-muted)}\r\n#pdfsp-app .pdfsp-upload-box small {display:block;margin-top:14px;color:var(--pdfsp-muted)}\r\n#pdfsp-app .pdfsp-btn {\r\n  border:0;border-radius:11px;padding:11px 16px;font-weight:800;transition:.2s;display:inline-flex;align-items:center;justify-content:center;gap:8px\r\n}\r\n#pdfsp-app .pdfsp-btn:disabled {opacity:.45;cursor:not-allowed}\r\n#pdfsp-app .pdfsp-btn-primary {background:linear-gradient(135deg,var(--pdfsp-primary),var(--pdfsp-primary-2));color:#fff}\r\n#pdfsp-app .pdfsp-btn-success {background:var(--pdfsp-success);color:#fff}\r\n#pdfsp-app .pdfsp-btn-light {background:var(--pdfsp-surface-2);color:var(--pdfsp-text);border:1px solid var(--pdfsp-border)}\r\n#pdfsp-app .pdfsp-btn-danger {background:rgba(200,59,77,.12);color:var(--pdfsp-danger)}\r\n#pdfsp-app .pdfsp-btn-large {min-width:220px;padding:15px 22px;font-size:16px}\r\n#pdfsp-app .pdfsp-btn:hover:not(:disabled) {transform:translateY(-1px);filter:brightness(1.03)}\r\n#pdfsp-app .pdfsp-file-list {display:grid;gap:10px;margin-top:18px}\r\n#pdfsp-app .pdfsp-file-item {\r\n  display:flex;align-items:center;gap:12px;padding:12px;border:1px solid var(--pdfsp-border);border-radius:13px;background:var(--pdfsp-surface)\r\n}\r\n#pdfsp-app .pdfsp-drag-handle {font-size:22px;color:var(--pdfsp-muted);cursor:grab}\r\n#pdfsp-app .pdfsp-file-icon {display:grid;place-items:center;width:42px;height:42px;border-radius:10px;background:rgba(91,61,245,.1);color:var(--pdfsp-primary);font-weight:900}\r\n#pdfsp-app .pdfsp-file-meta {min-width:0;flex:1}\r\n#pdfsp-app .pdfsp-file-name {font-weight:750;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\r\n#pdfsp-app .pdfsp-file-size {font-size:12px;color:var(--pdfsp-muted)}\r\n#pdfsp-app .pdfsp-remove {border:0;background:transparent;color:var(--pdfsp-danger);font-size:21px}\r\n#pdfsp-app .pdfsp-options {\r\n  display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px;margin-top:20px\r\n}\r\n#pdfsp-app .pdfsp-field {display:flex;flex-direction:column;gap:7px}\r\n#pdfsp-app .pdfsp-field label {font-weight:750;font-size:13px}\r\n#pdfsp-app .pdfsp-input, #pdfsp-app .pdfsp-field select, #pdfsp-app .pdfsp-textarea {\r\n  width:100%;height:44px;border:1px solid var(--pdfsp-border);border-radius:10px;padding:0 12px;background:var(--pdfsp-surface-2);color:var(--pdfsp-text)\r\n}\r\n#pdfsp-app .pdfsp-textarea {height:180px;padding:12px;resize:vertical}\r\n#pdfsp-app .pdfsp-meta-box {grid-column:1\/-1;padding:14px;border:1px solid var(--pdfsp-border);border-radius:12px;background:var(--pdfsp-surface-2);white-space:pre-wrap;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:13px}\r\n#pdfsp-app .pdfsp-field input[type=\"color\"] {height:44px;width:100%;border:1px solid var(--pdfsp-border);border-radius:10px;background:var(--pdfsp-surface-2)}\r\n#pdfsp-app .pdfsp-page-grid {grid-column:1\/-1;display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:12px}\r\n#pdfsp-app .pdfsp-page-card {\r\n  position:relative;border:1px solid var(--pdfsp-border);border-radius:12px;padding:8px;background:var(--pdfsp-surface);text-align:center\r\n}\r\n#pdfsp-app .pdfsp-page-card.pdfsp-deleted {opacity:.45;border-color:var(--pdfsp-danger)}\r\n#pdfsp-app .pdfsp-page-card canvas {width:100%;height:auto;border-radius:7px;background:#fff}\r\n#pdfsp-app .pdfsp-page-label {font-size:12px;font-weight:750;margin-top:6px}\r\n#pdfsp-app .pdfsp-page-actions {display:flex;justify-content:center;gap:5px;margin-top:7px}\r\n#pdfsp-app .pdfsp-page-actions button {border:1px solid var(--pdfsp-border);background:var(--pdfsp-surface-2);color:var(--pdfsp-text);border-radius:7px;padding:5px 7px}\r\n#pdfsp-app .pdfsp-alert {padding:12px 14px;border-radius:11px;margin-bottom:16px;background:rgba(200,59,77,.1);color:var(--pdfsp-danger);border:1px solid rgba(200,59,77,.25)}\r\n#pdfsp-app .pdfsp-progress-wrap {margin-top:20px}\r\n#pdfsp-app .pdfsp-progress-label {display:flex;justify-content:space-between;font-size:13px;font-weight:700;margin-bottom:7px}\r\n#pdfsp-app .pdfsp-progress-track {height:10px;background:var(--pdfsp-surface-2);border-radius:999px;overflow:hidden}\r\n#pdfsp-app .pdfsp-progress-bar {height:100%;width:0;background:linear-gradient(90deg,var(--pdfsp-primary),#ff5470);transition:width .2s}\r\n#pdfsp-app .pdfsp-action-bar {display:flex;justify-content:center;margin-top:24px}\r\n#pdfsp-app .pdfsp-result {margin-top:24px;padding:25px;text-align:center;border-radius:16px;background:rgba(22,136,95,.08);border:1px solid rgba(22,136,95,.25)}\r\n#pdfsp-app .pdfsp-result-icon {margin:auto;display:grid;place-items:center;width:52px;height:52px;border-radius:50%;background:var(--pdfsp-success);color:white;font-size:28px}\r\n#pdfsp-app .pdfsp-result h3 {margin:10px 0 4px}\r\n#pdfsp-app .pdfsp-result p {color:var(--pdfsp-muted)}\r\n#pdfsp-app .pdfsp-info-grid {display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:24px}\r\n#pdfsp-app .pdfsp-info-card {padding:20px;border-radius:16px;background:var(--pdfsp-surface);border:1px solid var(--pdfsp-border)}\r\n#pdfsp-app .pdfsp-info-card span {font-size:25px}\r\n#pdfsp-app .pdfsp-info-card h3 {margin:8px 0 4px}\r\n#pdfsp-app .pdfsp-info-card p {margin:0;color:var(--pdfsp-muted)}\r\n\r\n#pdfsp-app .pdfsp-editor-wrap{grid-column:1\/-1;display:grid;grid-template-columns:minmax(0,1.45fr) minmax(280px,.55fr);gap:18px;align-items:start}\r\n#pdfsp-app .pdfsp-editor-stage{position:relative;overflow:auto;max-height:720px;padding:12px;border:1px solid var(--pdfsp-border);border-radius:14px;background:#cfd5df}\r\n#pdfsp-app .pdfsp-editor-canvas-wrap{position:relative;width:max-content;margin:auto;box-shadow:0 10px 25px rgba(0,0,0,.18)}\r\n#pdfsp-app .pdfsp-editor-canvas{display:block;background:#fff;max-width:none}\r\n#pdfsp-app .pdfsp-editor-marker{position:absolute;border:2px dashed #e03045;background:rgba(255,230,90,.3);pointer-events:none;min-width:12px;min-height:12px}\r\n#pdfsp-app .pdfsp-editor-panel{display:grid;gap:12px;padding:16px;border:1px solid var(--pdfsp-border);border-radius:14px;background:var(--pdfsp-surface-2)}\r\n#pdfsp-app .pdfsp-editor-help{font-size:12px;color:var(--pdfsp-muted);padding:10px;border-radius:10px;background:var(--pdfsp-surface)}\r\n#pdfsp-app .pdfsp-compare-report{grid-column:1\/-1;margin-top:4px}\r\n#pdfsp-app .pdfsp-compare-summary{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:14px}\r\n#pdfsp-app .pdfsp-stat{padding:9px 12px;border-radius:10px;background:var(--pdfsp-surface-2);border:1px solid var(--pdfsp-border);font-size:13px;font-weight:750}\r\n#pdfsp-app .pdfsp-compare-pages{display:grid;gap:16px}\r\n#pdfsp-app .pdfsp-compare-page{border:1px solid var(--pdfsp-border);border-radius:14px;overflow:hidden;background:var(--pdfsp-surface)}\r\n#pdfsp-app .pdfsp-compare-page h4{margin:0;padding:11px 14px;background:var(--pdfsp-surface-2);border-bottom:1px solid var(--pdfsp-border)}\r\n#pdfsp-app .pdfsp-compare-columns{display:grid;grid-template-columns:1fr 1fr}\r\n#pdfsp-app .pdfsp-compare-col{padding:14px;min-width:0;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:13px;line-height:1.75}\r\n#pdfsp-app .pdfsp-compare-col+ .pdfsp-compare-col{border-left:1px solid var(--pdfsp-border)}\r\n#pdfsp-app .pdfsp-diff-same{color:var(--pdfsp-text)}\r\n#pdfsp-app .pdfsp-diff-added{background:#c8f3d9;color:#0b5b32;padding:1px 2px;border-radius:3px}\r\n#pdfsp-app .pdfsp-diff-removed{background:#ffd6db;color:#8d2030;text-decoration:line-through;padding:1px 2px;border-radius:3px}\r\n#pdfsp-app .pdfsp-compare-note{grid-column:1\/-1;padding:12px;border-radius:11px;background:rgba(224,142,26,.12);border:1px solid rgba(224,142,26,.3);font-size:13px}\r\n@media(max-width:820px){#pdfsp-app .pdfsp-editor-wrap{grid-template-columns:1fr}#pdfsp-app .pdfsp-compare-columns{grid-template-columns:1fr}#pdfsp-app .pdfsp-compare-col+ .pdfsp-compare-col{border-left:0;border-top:1px solid var(--pdfsp-border)}}\r\n\r\n#pdfsp-app .pdfsp-hidden {display:none!important}\r\n#pdfsp-app :focus-visible {outline:3px solid rgba(91,61,245,.3);outline-offset:2px}\r\n@media (max-width:720px){\r\n  #pdfsp-app {border-radius:0}\r\n  #pdfsp-app .pdfsp-tagline {display:none}\r\n  #pdfsp-app .pdfsp-header {padding:13px}\r\n  #pdfsp-app .pdfsp-brand-name {font-size:16px}\r\n  #pdfsp-app .pdfsp-logo {width:42px;height:42px}\r\n  #pdfsp-app .pdfsp-hero {padding:42px 14px 72px}\r\n  #pdfsp-app .pdfsp-main {padding-left:12px;padding-right:12px}\r\n  #pdfsp-app .pdfsp-tool-grid {grid-template-columns:repeat(2,1fr)}\r\n  #pdfsp-app .pdfsp-tool-card {min-height:135px;padding:14px}\r\n  #pdfsp-app .pdfsp-workspace-head {align-items:center}\r\n  #pdfsp-app .pdfsp-workspace-head h2 {font-size:22px}\r\n  #pdfsp-app .pdfsp-info-grid {grid-template-columns:1fr}\r\n  #pdfsp-app .pdfsp-btn-large {width:100%}\r\n}\r\n@media (prefers-reduced-motion:reduce){\r\n  #pdfsp-app *,#pdfsp-app *::before,#pdfsp-app *::after{scroll-behavior:auto!important;transition:none!important}\r\n}\r\n<\/style>\r\n\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/pdf-lib@1.17.1\/dist\/pdf-lib.min.js\"><\/script>\r\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.min.js\"><\/script>\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/sortablejs@1.15.2\/Sortable.min.js\"><\/script>\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/jszip@3.10.1\/dist\/jszip.min.js\"><\/script>\r\n\r\n<script>\r\n(function(){\r\n  'use strict';\r\n\r\n  const app = document.getElementById('pdfsp-app');\r\n  if (!app || app.dataset.initialized === '1') return;\r\n  app.dataset.initialized = '1';\r\n\r\n  const { PDFDocument, StandardFonts, rgb, degrees } = PDFLib;\r\n  pdfjsLib.GlobalWorkerOptions.workerSrc = 'https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/pdf.js\/3.11.174\/pdf.worker.min.js';\r\n\r\n  const els = {\r\n    language: document.getElementById('pdfsp-language'),\r\n    theme: document.getElementById('pdfsp-theme-toggle'),\r\n    search: document.getElementById('pdfsp-tool-search'),\r\n    grid: document.getElementById('pdfsp-tool-grid'),\r\n    title: document.getElementById('pdfsp-workspace-title'),\r\n    desc: document.getElementById('pdfsp-workspace-description'),\r\n    reset: document.getElementById('pdfsp-reset'),\r\n    alert: document.getElementById('pdfsp-alert'),\r\n    upload: document.getElementById('pdfsp-upload-box'),\r\n    input: document.getElementById('pdfsp-file-input'),\r\n    supported: document.getElementById('pdfsp-supported-text'),\r\n    list: document.getElementById('pdfsp-file-list'),\r\n    options: document.getElementById('pdfsp-options'),\r\n    process: document.getElementById('pdfsp-process'),\r\n    progressWrap: document.getElementById('pdfsp-progress-wrap'),\r\n    progressText: document.getElementById('pdfsp-progress-text'),\r\n    progressPercent: document.getElementById('pdfsp-progress-percent'),\r\n    progressBar: document.getElementById('pdfsp-progress-bar'),\r\n    result: document.getElementById('pdfsp-result'),\r\n    resultDetails: document.getElementById('pdfsp-result-details'),\r\n    download: document.getElementById('pdfsp-download')\r\n  };\r\n\r\n  const I18N = {\r\n    en: {\r\n      tagline:'Simple, Fast and Free PDF Tools',\r\n      heroTitle:'Every PDF Tool You Need, in One Place',\r\n      heroText:'Merge, split, organize, convert, stamp, number, clean and prepare PDF files directly in your browser.',\r\n      privacyNote:'Your files stay on your device and are not uploaded to our server.',\r\n      searchPlaceholder:'Search PDF tools\u2026',\r\n      merge:'Merge PDF', mergeDesc:'Combine multiple PDF files',\r\n      split:'Split PDF', splitDesc:'Extract ranges or individual pages',\r\n      organize:'Organize PDF', organizeDesc:'Rotate, delete and reorder pages',\r\n      imageToPdf:'Images to PDF', imageToPdfDesc:'Convert JPG, PNG or WEBP',\r\n      pdfToImage:'PDF to Images', pdfToImageDesc:'Export PDF pages as JPG or PNG',\r\n      watermark:'Watermark PDF', watermarkDesc:'Add a text watermark',\r\n      pageNumbers:'Page Numbers', pageNumbersDesc:'Add page numbers to a PDF',\r\n      reset:'Reset', dropTitle:'Drop files here', dropText:'or click to browse files from your device',\r\n      chooseFiles:'Choose Files', process:'Process Files', success:'Your file is ready', download:'Download File',\r\n      secureTitle:'Private Processing', secureText:'Files are processed locally in your browser.',\r\n      fastTitle:'Fast and Simple', fastText:'No account or compulsory registration is required.',\r\n      mobileTitle:'Mobile Friendly', mobileText:'Works on desktop, tablet and supported mobile browsers.'\r\n    },\r\n    hi: {\r\n      tagline:'\u0938\u0930\u0932, \u0924\u0947\u091c\u093c \u0914\u0930 \u0928\u093f\u0903\u0936\u0941\u0932\u094d\u0915 PDF \u091f\u0942\u0932\u094d\u0938',\r\n      heroTitle:'\u0906\u092a\u0915\u0947 \u0938\u092d\u0940 PDF \u091f\u0942\u0932\u094d\u0938 \u090f\u0915 \u0939\u0940 \u0938\u094d\u0925\u093e\u0928 \u092a\u0930',\r\n      heroText:'PDF \u092b\u093e\u0907\u0932\u094b\u0902 \u0915\u094b \u092c\u094d\u0930\u093e\u0909\u091c\u093c\u0930 \u092e\u0947\u0902 \u092e\u0930\u094d\u091c, \u0938\u094d\u092a\u094d\u0932\u093f\u091f, \u0935\u094d\u092f\u0935\u0938\u094d\u0925\u093f\u0924, \u0915\u0928\u094d\u0935\u0930\u094d\u091f, \u0935\u0949\u091f\u0930\u092e\u093e\u0930\u094d\u0915 \u0914\u0930 \u0928\u0902\u092c\u0930 \u0915\u0930\u0947\u0902\u0964',\r\n      privacyNote:'\u0906\u092a\u0915\u0940 \u092b\u093e\u0907\u0932\u0947\u0902 \u0906\u092a\u0915\u0947 \u0921\u093f\u0935\u093e\u0907\u0938 \u092a\u0930 \u0930\u0939\u0924\u0940 \u0939\u0948\u0902 \u0914\u0930 \u0938\u0930\u094d\u0935\u0930 \u092a\u0930 \u0905\u092a\u0932\u094b\u0921 \u0928\u0939\u0940\u0902 \u0939\u094b\u0924\u0940\u0902\u0964',\r\n      searchPlaceholder:'PDF \u091f\u0942\u0932 \u0916\u094b\u091c\u0947\u0902\u2026',\r\n      merge:'PDF \u092e\u0930\u094d\u091c \u0915\u0930\u0947\u0902', mergeDesc:'\u0915\u0908 PDF \u092b\u093e\u0907\u0932\u0947\u0902 \u090f\u0915 \u0938\u093e\u0925 \u091c\u094b\u0921\u093c\u0947\u0902',\r\n      split:'PDF \u0938\u094d\u092a\u094d\u0932\u093f\u091f \u0915\u0930\u0947\u0902', splitDesc:'\u092a\u0947\u091c \u0930\u0947\u0902\u091c \u092f\u093e \u0905\u0932\u0917 \u092a\u0947\u091c \u0928\u093f\u0915\u093e\u0932\u0947\u0902',\r\n      organize:'PDF \u0935\u094d\u092f\u0935\u0938\u094d\u0925\u093f\u0924 \u0915\u0930\u0947\u0902', organizeDesc:'\u092a\u0947\u091c \u0918\u0941\u092e\u093e\u090f\u0901, \u0939\u091f\u093e\u090f\u0901 \u0914\u0930 \u0915\u094d\u0930\u092e \u092c\u0926\u0932\u0947\u0902',\r\n      imageToPdf:'\u0907\u092e\u0947\u091c \u0938\u0947 PDF', imageToPdfDesc:'JPG, PNG \u092f\u093e WEBP \u0915\u094b PDF \u092c\u0928\u093e\u090f\u0902',\r\n      pdfToImage:'PDF \u0938\u0947 \u0907\u092e\u0947\u091c', pdfToImageDesc:'PDF \u092a\u0947\u091c JPG \u092f\u093e PNG \u092e\u0947\u0902 \u0928\u093f\u0915\u093e\u0932\u0947\u0902',\r\n      watermark:'\u0935\u0949\u091f\u0930\u092e\u093e\u0930\u094d\u0915 PDF', watermarkDesc:'\u091f\u0947\u0915\u094d\u0938\u094d\u091f \u0935\u0949\u091f\u0930\u092e\u093e\u0930\u094d\u0915 \u091c\u094b\u0921\u093c\u0947\u0902',\r\n      pageNumbers:'\u092a\u0947\u091c \u0928\u0902\u092c\u0930', pageNumbersDesc:'PDF \u092e\u0947\u0902 \u092a\u0947\u091c \u0928\u0902\u092c\u0930 \u091c\u094b\u0921\u093c\u0947\u0902',\r\n      reset:'\u0930\u0940\u0938\u0947\u091f', dropTitle:'\u092b\u093e\u0907\u0932\u0947\u0902 \u092f\u0939\u093e\u0901 \u091b\u094b\u0921\u093c\u0947\u0902', dropText:'\u092f\u093e \u0921\u093f\u0935\u093e\u0907\u0938 \u0938\u0947 \u091a\u0941\u0928\u0928\u0947 \u0915\u0947 \u0932\u093f\u090f \u0915\u094d\u0932\u093f\u0915 \u0915\u0930\u0947\u0902',\r\n      chooseFiles:'\u092b\u093e\u0907\u0932 \u091a\u0941\u0928\u0947\u0902', process:'\u092b\u093e\u0907\u0932 \u092a\u094d\u0930\u094b\u0938\u0947\u0938 \u0915\u0930\u0947\u0902', success:'\u0906\u092a\u0915\u0940 \u092b\u093e\u0907\u0932 \u0924\u0948\u092f\u093e\u0930 \u0939\u0948', download:'\u092b\u093e\u0907\u0932 \u0921\u093e\u0909\u0928\u0932\u094b\u0921 \u0915\u0930\u0947\u0902',\r\n      secureTitle:'\u0928\u093f\u091c\u0940 \u092a\u094d\u0930\u094b\u0938\u0947\u0938\u093f\u0902\u0917', secureText:'\u092b\u093e\u0907\u0932\u0947\u0902 \u0906\u092a\u0915\u0947 \u092c\u094d\u0930\u093e\u0909\u091c\u093c\u0930 \u092e\u0947\u0902 \u0938\u094d\u0925\u093e\u0928\u0940\u092f \u0930\u0942\u092a \u0938\u0947 \u092a\u094d\u0930\u094b\u0938\u0947\u0938 \u0939\u094b\u0924\u0940 \u0939\u0948\u0902\u0964',\r\n      fastTitle:'\u0924\u0947\u091c\u093c \u0914\u0930 \u0938\u0930\u0932', fastText:'\u0915\u093f\u0938\u0940 \u0916\u093e\u0924\u0947 \u092f\u093e \u0905\u0928\u093f\u0935\u093e\u0930\u094d\u092f \u092a\u0902\u091c\u0940\u0915\u0930\u0923 \u0915\u0940 \u0906\u0935\u0936\u094d\u092f\u0915\u0924\u093e \u0928\u0939\u0940\u0902 \u0939\u0948\u0964',\r\n      mobileTitle:'\u092e\u094b\u092c\u093e\u0907\u0932 \u092b\u094d\u0930\u0947\u0902\u0921\u0932\u0940', mobileText:'\u0921\u0947\u0938\u094d\u0915\u091f\u0949\u092a, \u091f\u0948\u092c\u0932\u0947\u091f \u0914\u0930 \u0938\u092e\u0930\u094d\u0925\u093f\u0924 \u092e\u094b\u092c\u093e\u0907\u0932 \u092c\u094d\u0930\u093e\u0909\u091c\u093c\u0930 \u092a\u0930 \u0915\u093e\u092e \u0915\u0930\u0924\u093e \u0939\u0948\u0964'\r\n    }\r\n  };\r\n\r\n  const tools = {\r\n    merge: {\r\n      title:'Merge PDF',\r\n      desc:'Combine PDF files in your preferred order.',\r\n      accept:'.pdf,application\/pdf',\r\n      multiple:true,\r\n      supported:'Supported: PDF \u2022 Maximum 100 MB per file',\r\n      button:'Merge PDF'\r\n    },\r\n    split: {\r\n      title:'Split PDF',\r\n      desc:'Extract selected page ranges or create one PDF per page.',\r\n      accept:'.pdf,application\/pdf',\r\n      multiple:false,\r\n      supported:'Supported: PDF \u2022 Maximum 100 MB',\r\n      button:'Split PDF'\r\n    },\r\n    organize: {\r\n      title:'Organize PDF',\r\n      desc:'Preview, rotate, delete and reorder PDF pages.',\r\n      accept:'.pdf,application\/pdf',\r\n      multiple:false,\r\n      supported:'Supported: PDF \u2022 Maximum 100 MB',\r\n      button:'Apply Changes'\r\n    },\r\n    imageToPdf: {\r\n      title:'Images to PDF',\r\n      desc:'Convert JPG, PNG and WEBP images into one PDF.',\r\n      accept:'image\/jpeg,image\/png,image\/webp,.jpg,.jpeg,.png,.webp',\r\n      multiple:true,\r\n      supported:'Supported: JPG, JPEG, PNG, WEBP \u2022 Maximum 100 MB per file',\r\n      button:'Create PDF'\r\n    },\r\n    pdfToImage: {\r\n      title:'PDF to Images',\r\n      desc:'Render all or selected PDF pages as JPG or PNG.',\r\n      accept:'.pdf,application\/pdf',\r\n      multiple:false,\r\n      supported:'Supported: PDF \u2022 Maximum 100 MB',\r\n      button:'Convert to Images'\r\n    },\r\n    watermark: {\r\n      title:'Watermark PDF',\r\n      desc:'Add customizable text watermark to every or selected pages.',\r\n      accept:'.pdf,application\/pdf',\r\n      multiple:false,\r\n      supported:'Supported: PDF \u2022 Maximum 100 MB',\r\n      button:'Add Watermark'\r\n    },\r\n    pageNumbers: {\r\n      title:'Add Page Numbers',\r\n      desc:'Add page numbers with position, prefix and starting-number options.',\r\n      accept:'.pdf,application\/pdf',\r\n      multiple:false,\r\n      supported:'Supported: PDF \u2022 Maximum 100 MB',\r\n      button:'Add Page Numbers'\r\n    },\r\n    headerFooter: {title:'Header & Footer',desc:'Add professional header and footer text to each page.',accept:'.pdf,application\/pdf',multiple:false,supported:'Supported: PDF \u2022 Maximum 100 MB',button:'Add Header & Footer'},\r\n    bates: {title:'Bates Numbering',desc:'Add sequential control numbers for legal, audit and office records.',accept:'.pdf,application\/pdf',multiple:false,supported:'Supported: PDF \u2022 Maximum 100 MB',button:'Add Bates Numbers'},\r\n    stamp: {title:'Stamp PDF',desc:'Place an Approved, Draft, Confidential or custom stamp on PDF pages.',accept:'.pdf,application\/pdf',multiple:false,supported:'Supported: PDF \u2022 Maximum 100 MB',button:'Add Stamp'},\r\n    extractText: {title:'Extract PDF Text',desc:'Extract selectable text from PDF pages and download it as a TXT file.',accept:'.pdf,application\/pdf',multiple:false,supported:'Supported: text-based PDF \u2022 Scanned PDFs require OCR',button:'Extract Text'},\r\n    metadata: {title:'PDF Metadata',desc:'View document properties and download a cleaned PDF without common metadata.',accept:'.pdf,application\/pdf',multiple:false,supported:'Supported: PDF \u2022 Maximum 100 MB',button:'Remove Metadata'},\r\n    flatten: {title:'Flatten PDF',desc:'Render pages into a fixed PDF copy for consistent printing and sharing.',accept:'.pdf,application\/pdf',multiple:false,supported:'Supported: PDF \u2022 Visual flattening may increase file size',button:'Flatten PDF'},\r\n    resize: {title:'Resize PDF Pages',desc:'Scale PDF content to a standard office page size.',accept:'.pdf,application\/pdf',multiple:false,supported:'Supported: PDF \u2022 Maximum 100 MB',button:'Resize PDF'},\r\n    editText: {title:'Edit PDF Text',desc:'Cover existing text and place replacement text at the selected position.',accept:'.pdf,application\/pdf',multiple:false,supported:'Browser method: visible overlay replacement \u2022 Best for simple corrections',button:'Apply Text Changes'},\r\n    comparePdf: {title:'Compare 2 PDF Files',desc:'Compare extracted text page by page and highlight additions and removals.',accept:'.pdf,application\/pdf',multiple:true,supported:'Select exactly 2 text-based PDFs \u2022 Scanned PDFs require OCR',button:'Compare PDFs'}\r\n  };\r\n\r\n  const state = {\r\n    tool:'merge',\r\n    files:[],\r\n    output:null,\r\n    outputName:'',\r\n    pageItems:[],\r\n    objectUrls:[],\r\n    editorPdf:null,\r\n    editorPage:1,\r\n    editorScale:1.25\r\n  };\r\n\r\n  function t(key){ return (I18N[els.language.value] || I18N.en)[key] || key; }\r\n\r\n  function translate(){\r\n    app.querySelectorAll('[data-i18n]').forEach(el => {\r\n      const key = el.getAttribute('data-i18n');\r\n      el.textContent = t(key);\r\n    });\r\n    app.querySelectorAll('[data-i18n-placeholder]').forEach(el => {\r\n      const key = el.getAttribute('data-i18n-placeholder');\r\n      el.setAttribute('placeholder', t(key));\r\n    });\r\n  }\r\n\r\n  function humanSize(bytes){\r\n    if (!Number.isFinite(bytes)) return '0 B';\r\n    const units=['B','KB','MB','GB'];\r\n    let i=0, n=bytes;\r\n    while(n>=1024 && i<units.length-1){n\/=1024;i++}\r\n    return `${n.toFixed(i ? 2 : 0)} ${units[i]}`;\r\n  }\r\n\r\n  function showAlert(message){\r\n    els.alert.textContent=message;\r\n    els.alert.hidden=false;\r\n  }\r\n  function clearAlert(){\r\n    els.alert.hidden=true;\r\n    els.alert.textContent='';\r\n  }\r\n  function progress(value, text){\r\n    const v=Math.max(0,Math.min(100,value));\r\n    els.progressWrap.hidden=false;\r\n    els.progressBar.style.width=v+'%';\r\n    els.progressPercent.textContent=Math.round(v)+'%';\r\n    els.progressText.textContent=text || 'Processing\u2026';\r\n  }\r\n  function hideProgress(){\r\n    els.progressWrap.hidden=true;\r\n    els.progressBar.style.width='0%';\r\n  }\r\n  function cleanupUrls(){\r\n    state.objectUrls.forEach(URL.revokeObjectURL);\r\n    state.objectUrls=[];\r\n  }\r\n\r\n  function resetTool(){\r\n    cleanupUrls();\r\n    state.files=[];\r\n    state.output=null;\r\n    state.outputName='';\r\n    state.pageItems=[];\r\n    state.editorPdf=null;\r\n    state.editorPage=1;\r\n    els.input.value='';\r\n    els.list.innerHTML='';\r\n    els.options.innerHTML='';\r\n    els.result.hidden=true;\r\n    els.download.onclick=null;\r\n    hideProgress();\r\n    clearAlert();\r\n    configureTool();\r\n  }\r\n\r\n  function configureTool(){\r\n    const tool=tools[state.tool];\r\n    els.title.textContent=tool.title;\r\n    els.desc.textContent=tool.desc;\r\n    els.input.accept=tool.accept;\r\n    els.input.multiple=tool.multiple;\r\n    els.supported.textContent=tool.supported;\r\n    els.process.textContent=tool.button;\r\n    els.process.disabled=true;\r\n    renderOptions();\r\n  }\r\n\r\n  function selectTool(name){\r\n    if (!tools[name]) return;\r\n    state.tool=name;\r\n    els.grid.querySelectorAll('.pdfsp-tool-card').forEach(card => card.classList.toggle('is-active',card.dataset.tool===name));\r\n    resetTool();\r\n    els.title.scrollIntoView({behavior:'smooth',block:'center'});\r\n  }\r\n\r\n  function renderOptions(){\r\n    const f=(id,label,type='text',value='',attrs='') =>\r\n      `<div class=\"pdfsp-field\"><label for=\"${id}\">${label}<\/label><input id=\"${id}\" class=\"pdfsp-input\" type=\"${type}\" value=\"${value}\" ${attrs}><\/div>`;\r\n    const s=(id,label,options) =>\r\n      `<div class=\"pdfsp-field\"><label for=\"${id}\">${label}<\/label><select id=\"${id}\">${options}<\/select><\/div>`;\r\n\r\n    switch(state.tool){\r\n      case 'split':\r\n        els.options.innerHTML=\r\n          s('pdfsp-split-mode','Split method',\r\n            '<option value=\"range\">Extract page range<\/option><option value=\"each\">Every page as separate PDF<\/option><option value=\"odd\">Odd pages<\/option><option value=\"even\">Even pages<\/option>')+\r\n          f('pdfsp-page-range','Page range','text','1-3','placeholder=\"Example: 1-3,5,8-10\"');\r\n        document.getElementById('pdfsp-split-mode').addEventListener('change',e=>{\r\n          document.getElementById('pdfsp-page-range').closest('.pdfsp-field').classList.toggle('pdfsp-hidden',e.target.value!=='range');\r\n        });\r\n        break;\r\n      case 'imageToPdf':\r\n        els.options.innerHTML=\r\n          s('pdfsp-page-size','Page size','<option value=\"A4\">A4<\/option><option value=\"A3\">A3<\/option><option value=\"LETTER\">Letter<\/option><option value=\"original\">Original image size<\/option>')+\r\n          s('pdfsp-orientation','Orientation','<option value=\"portrait\">Portrait<\/option><option value=\"landscape\">Landscape<\/option>')+\r\n          f('pdfsp-margin','Margin (points)','number','24','min=\"0\" max=\"150\"');\r\n        break;\r\n      case 'pdfToImage':\r\n        els.options.innerHTML=\r\n          s('pdfsp-image-format','Output format','<option value=\"jpg\">JPG<\/option><option value=\"png\">PNG<\/option>')+\r\n          s('pdfsp-image-quality','Resolution','<option value=\"1\">Standard<\/option><option value=\"1.5\" selected>High<\/option><option value=\"2\">Very High<\/option>')+\r\n          f('pdfsp-image-pages','Pages','text','all','placeholder=\"all or 1-3,5\"');\r\n        break;\r\n      case 'watermark':\r\n        els.options.innerHTML=\r\n          f('pdfsp-watermark-text','Watermark text','text','CONFIDENTIAL')+\r\n          f('pdfsp-watermark-size','Font size','number','48','min=\"8\" max=\"200\"')+\r\n          f('pdfsp-watermark-color','Color','color','#6b56ff')+\r\n          f('pdfsp-watermark-opacity','Opacity','number','0.25','min=\"0.05\" max=\"1\" step=\"0.05\"')+\r\n          f('pdfsp-watermark-rotation','Rotation','number','-35','min=\"-180\" max=\"180\"')+\r\n          f('pdfsp-watermark-pages','Page range','text','all','placeholder=\"all or 1-3,5\"');\r\n        break;\r\n      case 'pageNumbers':\r\n        els.options.innerHTML=\r\n          s('pdfsp-number-position','Position','<option value=\"bottom-center\">Bottom center<\/option><option value=\"bottom-left\">Bottom left<\/option><option value=\"bottom-right\">Bottom right<\/option><option value=\"top-center\">Top center<\/option><option value=\"top-left\">Top left<\/option><option value=\"top-right\">Top right<\/option>')+\r\n          f('pdfsp-number-start','Starting number','number','1','min=\"0\"')+\r\n          f('pdfsp-number-prefix','Prefix','text','Page ')+\r\n          f('pdfsp-number-suffix','Suffix','text','')+\r\n          f('pdfsp-number-size','Font size','number','11','min=\"6\" max=\"48\"');\r\n        break;\r\n      case 'headerFooter':\r\n        els.options.innerHTML=f('pdfsp-header-text','Header text','text','Company Name | Document Title')+f('pdfsp-footer-text','Footer text','Confidential')+s('pdfsp-hf-align','Alignment','<option value=\"left\">Left<\/option><option value=\"center\" selected>Center<\/option><option value=\"right\">Right<\/option>')+f('pdfsp-hf-size','Font size','number','9','min=\"6\" max=\"24\"');\r\n        break;\r\n      case 'bates':\r\n        els.options.innerHTML=f('pdfsp-bates-prefix','Prefix','text','DOC-')+f('pdfsp-bates-start','Starting number','number','1','min=\"0\"')+f('pdfsp-bates-digits','Number of digits','number','6','min=\"2\" max=\"12\"')+s('pdfsp-bates-position','Position','<option value=\"bottom-right\">Bottom right<\/option><option value=\"bottom-left\">Bottom left<\/option><option value=\"top-right\">Top right<\/option><option value=\"top-left\">Top left<\/option>');\r\n        break;\r\n      case 'stamp':\r\n        els.options.innerHTML=s('pdfsp-stamp-text','Stamp','<option>APPROVED<\/option><option>DRAFT<\/option><option>CONFIDENTIAL<\/option><option>REVIEWED<\/option><option>PAID<\/option><option value=\"CUSTOM\">Custom<\/option>')+f('pdfsp-stamp-custom','Custom text','text','')+s('pdfsp-stamp-position','Position','<option value=\"center\">Center<\/option><option value=\"top-right\">Top right<\/option><option value=\"top-left\">Top left<\/option><option value=\"bottom-right\">Bottom right<\/option><option value=\"bottom-left\">Bottom left<\/option>')+f('pdfsp-stamp-pages','Pages','text','all','placeholder=\"all or 1-3,5\"');\r\n        break;\r\n      case 'extractText':\r\n        els.options.innerHTML='<div class=\"pdfsp-field\" style=\"grid-column:1\/-1\"><label for=\"pdfsp-text-preview\">Extracted text preview<\/label><textarea id=\"pdfsp-text-preview\" class=\"pdfsp-textarea\" readonly placeholder=\"Extracted text will appear here after processing.\"><\/textarea><\/div>';\r\n        break;\r\n      case 'metadata':\r\n        els.options.innerHTML='<div id=\"pdfsp-meta-preview\" class=\"pdfsp-meta-box\">Select a PDF to view its metadata.<\/div>';\r\n        break;\r\n      case 'flatten':\r\n        els.options.innerHTML=s('pdfsp-flatten-scale','Output quality','<option value=\"1\">Standard (smaller file)<\/option><option value=\"1.5\" selected>High<\/option><option value=\"2\">Very high (larger file)<\/option>');\r\n        break;\r\n      case 'resize':\r\n        els.options.innerHTML=s('pdfsp-resize-size','Target page size','<option value=\"A4\">A4<\/option><option value=\"A3\">A3<\/option><option value=\"LETTER\">Letter<\/option><option value=\"LEGAL\">Legal<\/option>')+s('pdfsp-resize-orientation','Orientation','<option value=\"auto\">Auto<\/option><option value=\"portrait\">Portrait<\/option><option value=\"landscape\">Landscape<\/option>')+f('pdfsp-resize-margin','Margin (points)','number','18','min=\"0\" max=\"100\"');\r\n        break;\r\n      case 'editText':\r\n        els.options.innerHTML='<div class=\"pdfsp-compare-note\">This browser editor does not rewrite the original PDF text object. It securely covers the selected area and places new visible text above it. This is suitable for names, dates, references and short corrections.<\/div><div id=\"pdfsp-editor-host\" class=\"pdfsp-editor-wrap\"><div class=\"pdfsp-editor-stage\"><div id=\"pdfsp-editor-canvas-wrap\" class=\"pdfsp-editor-canvas-wrap\"><canvas id=\"pdfsp-editor-canvas\" class=\"pdfsp-editor-canvas\"><\/canvas><div id=\"pdfsp-editor-marker\" class=\"pdfsp-editor-marker\" hidden><\/div><\/div><\/div><div class=\"pdfsp-editor-panel\">'+f('pdfsp-edit-page','Page number','number','1','min=\"1\"')+f('pdfsp-edit-x','X position','number','50','min=\"0\" step=\"1\"')+f('pdfsp-edit-y','Y position from top','number','50','min=\"0\" step=\"1\"')+f('pdfsp-edit-width','Cover width','number','180','min=\"5\" step=\"1\"')+f('pdfsp-edit-height','Cover height','number','24','min=\"5\" step=\"1\"')+f('pdfsp-edit-text','Replacement text','text','Corrected Text')+f('pdfsp-edit-size','Font size','number','12','min=\"6\" max=\"100\"')+f('pdfsp-edit-color','Text color','color','#000000')+f('pdfsp-edit-bg','Cover color','color','#ffffff')+'<div class=\"pdfsp-editor-help\">Click on the PDF preview to set the replacement position. X, Y, width and height are measured in PDF points.<\/div><\/div><\/div>';\r\n        break;\r\n      case 'comparePdf':\r\n        els.options.innerHTML='<div class=\"pdfsp-compare-note\">Comparison uses selectable PDF text. Reading order may differ in multi-column documents, and scanned pages require OCR before comparison.<\/div><div id=\"pdfsp-compare-report\" class=\"pdfsp-compare-report\"><\/div>';\r\n        break;\r\n      default:\r\n        els.options.innerHTML='';\r\n    }\r\n  }\r\n\r\n  function validFile(file){\r\n    if (file.size > 100*1024*1024) throw new Error(`${file.name}: file exceeds the 100 MB limit.`);\r\n    if (state.tool==='imageToPdf'){\r\n      if (!['image\/jpeg','image\/png','image\/webp'].includes(file.type)) throw new Error(`${file.name}: unsupported image format.`);\r\n    } else {\r\n      const isPdf=file.type==='application\/pdf' || \/\\.pdf$\/i.test(file.name);\r\n      if (!isPdf) throw new Error(`${file.name}: only PDF files are supported.`);\r\n    }\r\n    return true;\r\n  }\r\n\r\n  async function addFiles(fileList){\r\n    clearAlert();\r\n    try{\r\n      let incoming=Array.from(fileList);\r\n      if (!tools[state.tool].multiple) incoming=incoming.slice(0,1);\r\n      if (state.tool==='comparePdf') incoming=incoming.slice(0,Math.max(0,2-state.files.length));\r\n      incoming.forEach(validFile);\r\n      const fingerprints=new Set(state.files.map(f=>`${f.name}-${f.size}-${f.lastModified}`));\r\n      incoming=incoming.filter(f=>!fingerprints.has(`${f.name}-${f.size}-${f.lastModified}`));\r\n      state.files=tools[state.tool].multiple ? [...state.files,...incoming] : incoming;\r\n      renderFiles();\r\n      if (state.tool==='organize' && state.files[0]) await prepareOrganizer();\r\n      if (state.tool==='metadata' && state.files[0]) await previewMetadata();\r\n      if (state.tool==='editText' && state.files[0]) await prepareTextEditor();\r\n    }catch(err){showAlert(err.message || 'Unable to add the selected file.')}\r\n  }\r\n\r\n  function renderFiles(){\r\n    els.list.innerHTML='';\r\n    state.files.forEach((file,index)=>{\r\n      const item=document.createElement('div');\r\n      item.className='pdfsp-file-item';\r\n      item.dataset.index=index;\r\n      item.innerHTML=`\r\n        <span class=\"pdfsp-drag-handle\" aria-hidden=\"true\">\u22ee\u22ee<\/span>\r\n        <span class=\"pdfsp-file-icon\">${state.tool==='imageToPdf'?'IMG':'PDF'}<\/span>\r\n        <div class=\"pdfsp-file-meta\">\r\n          <div class=\"pdfsp-file-name\" title=\"${escapeHtml(file.name)}\">${escapeHtml(file.name)}<\/div>\r\n          <div class=\"pdfsp-file-size\">${humanSize(file.size)}<\/div>\r\n        <\/div>\r\n        <button class=\"pdfsp-remove\" type=\"button\" aria-label=\"Remove file\">\u00d7<\/button>`;\r\n      item.querySelector('.pdfsp-remove').addEventListener('click',async()=>{\r\n        state.files.splice(index,1);\r\n        renderFiles();\r\n        if(state.tool==='organize'){state.pageItems=[];els.options.innerHTML='';if(state.files[0])await prepareOrganizer();}\r\n      });\r\n      els.list.appendChild(item);\r\n    });\r\n    if (tools[state.tool].multiple && state.files.length>1){\r\n      new Sortable(els.list,{animation:150,handle:'.pdfsp-drag-handle',onEnd:e=>{\r\n        const [moved]=state.files.splice(e.oldIndex,1);state.files.splice(e.newIndex,0,moved);renderFiles();\r\n      }});\r\n    }\r\n    els.process.disabled=state.tool==='comparePdf' ? state.files.length!==2 : state.files.length===0;\r\n  }\r\n\r\n  function escapeHtml(str){\r\n    return String(str).replace(\/[&<>\"']\/g,ch=>({'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;',\"'\":'&#039;'}[ch]));\r\n  }\r\n\r\n  function parsePages(text,total){\r\n    const value=String(text||'').trim().toLowerCase();\r\n    if (!value || value==='all') return Array.from({length:total},(_,i)=>i);\r\n    const set=new Set();\r\n    for(const part of value.split(',')){\r\n      const p=part.trim();\r\n      if(!p) continue;\r\n      if(p.includes('-')){\r\n        const [a,b]=p.split('-').map(Number);\r\n        if(!Number.isInteger(a)||!Number.isInteger(b)||a<1||b<1||a>b||b>total) throw new Error(`Invalid page range: ${p}`);\r\n        for(let i=a;i<=b;i++) set.add(i-1);\r\n      }else{\r\n        const n=Number(p);\r\n        if(!Number.isInteger(n)||n<1||n>total) throw new Error(`Invalid page number: ${p}`);\r\n        set.add(n-1);\r\n      }\r\n    }\r\n    if(!set.size) throw new Error('Please enter at least one valid page.');\r\n    return [...set];\r\n  }\r\n\r\n  async function prepareOrganizer(){\r\n    clearAlert();\r\n    els.options.innerHTML='<div class=\"pdfsp-field\" style=\"grid-column:1\/-1\"><label>Loading page previews\u2026<\/label><\/div>';\r\n    progress(5,'Loading PDF pages\u2026');\r\n    try{\r\n      const bytes=await state.files[0].arrayBuffer();\r\n      const pdf=await pdfjsLib.getDocument({data:bytes.slice(0)}).promise;\r\n      state.pageItems=[];\r\n    state.editorPdf=null;\r\n    state.editorPage=1;\r\n      const grid=document.createElement('div');\r\n      grid.className='pdfsp-page-grid';\r\n      for(let i=1;i<=pdf.numPages;i++){\r\n        const page=await pdf.getPage(i);\r\n        const viewport=page.getViewport({scale:.35});\r\n        const canvas=document.createElement('canvas');\r\n        canvas.width=viewport.width;canvas.height=viewport.height;\r\n        await page.render({canvasContext:canvas.getContext('2d'),viewport}).promise;\r\n        const item={originalIndex:i-1,rotation:0,deleted:false};\r\n        state.pageItems.push(item);\r\n        const card=document.createElement('div');\r\n        card.className='pdfsp-page-card';\r\n        card.dataset.id=String(i-1);\r\n        card.appendChild(canvas);\r\n        const label=document.createElement('div');\r\n        label.className='pdfsp-page-label';label.textContent=`Page ${i}`;\r\n        card.appendChild(label);\r\n        const actions=document.createElement('div');\r\n        actions.className='pdfsp-page-actions';\r\n        actions.innerHTML='<button type=\"button\" data-action=\"left\">\u21b6<\/button><button type=\"button\" data-action=\"right\">\u21b7<\/button><button type=\"button\" data-action=\"delete\">\u00d7<\/button>';\r\n        actions.addEventListener('click',e=>{\r\n          const btn=e.target.closest('button');if(!btn)return;\r\n          const current=state.pageItems.find(p=>p.originalIndex===Number(card.dataset.id));\r\n          if(btn.dataset.action==='left') current.rotation=(current.rotation+270)%360;\r\n          if(btn.dataset.action==='right') current.rotation=(current.rotation+90)%360;\r\n          if(btn.dataset.action==='delete') current.deleted=!current.deleted;\r\n          canvas.style.transform=`rotate(${current.rotation}deg)`;\r\n          card.classList.toggle('pdfsp-deleted',current.deleted);\r\n        });\r\n        card.appendChild(actions);grid.appendChild(card);\r\n        progress((i\/pdf.numPages)*100,`Creating preview ${i} of ${pdf.numPages}\u2026`);\r\n      }\r\n      els.options.innerHTML='';els.options.appendChild(grid);\r\n      new Sortable(grid,{animation:150,onEnd:()=>{\r\n        const order=[...grid.children].map(c=>Number(c.dataset.id));\r\n        state.pageItems=order.map(id=>state.pageItems.find(p=>p.originalIndex===id));\r\n      }});\r\n      hideProgress();\r\n    }catch(err){hideProgress();showAlert('Unable to load PDF pages. The file may be corrupted or password protected.');}\r\n  }\r\n\r\n  async function runMerge(){\r\n    if(state.files.length<2) throw new Error('Please select at least two PDF files.');\r\n    const out=await PDFDocument.create();\r\n    for(let i=0;i<state.files.length;i++){\r\n      progress((i\/state.files.length)*90,`Merging file ${i+1} of ${state.files.length}\u2026`);\r\n      const src=await PDFDocument.load(await state.files[i].arrayBuffer(),{ignoreEncryption:false});\r\n      const pages=await out.copyPages(src,src.getPageIndices());\r\n      pages.forEach(p=>out.addPage(p));\r\n    }\r\n    return {bytes:await out.save(),name:'merged-document.pdf'};\r\n  }\r\n\r\n  async function runSplit(){\r\n    const bytes=await state.files[0].arrayBuffer();\r\n    const src=await PDFDocument.load(bytes);\r\n    const total=src.getPageCount();\r\n    const mode=document.getElementById('pdfsp-split-mode').value;\r\n    let indexes=[];\r\n    if(mode==='range') indexes=parsePages(document.getElementById('pdfsp-page-range').value,total);\r\n    if(mode==='odd') indexes=Array.from({length:total},(_,i)=>i).filter(i=>(i+1)%2===1);\r\n    if(mode==='even') indexes=Array.from({length:total},(_,i)=>i).filter(i=>(i+1)%2===0);\r\n\r\n    if(mode==='each'){\r\n      const zip=new JSZip();\r\n      for(let i=0;i<total;i++){\r\n        progress((i\/total)*95,`Creating page ${i+1} of ${total}\u2026`);\r\n        const doc=await PDFDocument.create();\r\n        const [page]=await doc.copyPages(src,[i]);doc.addPage(page);\r\n        zip.file(`page-${String(i+1).padStart(3,'0')}.pdf`,await doc.save());\r\n      }\r\n      return {bytes:await zip.generateAsync({type:'uint8array'}),name:'split-pages.zip',type:'application\/zip'};\r\n    }\r\n    if(!indexes.length) throw new Error('No matching pages were found.');\r\n    const out=await PDFDocument.create();\r\n    const pages=await out.copyPages(src,indexes);pages.forEach(p=>out.addPage(p));\r\n    return {bytes:await out.save(),name:'extracted-pages.pdf'};\r\n  }\r\n\r\n  async function runOrganize(){\r\n    const active=state.pageItems.filter(p=>!p.deleted);\r\n    if(!active.length) throw new Error('At least one page must remain.');\r\n    const src=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const out=await PDFDocument.create();\r\n    for(let i=0;i<active.length;i++){\r\n      const item=active[i];\r\n      const [page]=await out.copyPages(src,[item.originalIndex]);\r\n      const existing=page.getRotation().angle||0;\r\n      page.setRotation(degrees((existing+item.rotation)%360));\r\n      out.addPage(page);\r\n      progress((i\/active.length)*95,`Applying page ${i+1} of ${active.length}\u2026`);\r\n    }\r\n    return {bytes:await out.save(),name:'organized-document.pdf'};\r\n  }\r\n\r\n  async function imageDimensions(file){\r\n    return new Promise((resolve,reject)=>{\r\n      const img=new Image();\r\n      const url=URL.createObjectURL(file);\r\n      img.onload=()=>{resolve({width:img.naturalWidth,height:img.naturalHeight});URL.revokeObjectURL(url)};\r\n      img.onerror=()=>{URL.revokeObjectURL(url);reject(new Error(`Unable to read ${file.name}`))};\r\n      img.src=url;\r\n    });\r\n  }\r\n\r\n  async function runImageToPdf(){\r\n    const doc=await PDFDocument.create();\r\n    const size=document.getElementById('pdfsp-page-size').value;\r\n    const orientation=document.getElementById('pdfsp-orientation').value;\r\n    const margin=Math.max(0,Number(document.getElementById('pdfsp-margin').value)||0);\r\n    const sizes={A4:[595.28,841.89],A3:[841.89,1190.55],LETTER:[612,792]};\r\n    for(let i=0;i<state.files.length;i++){\r\n      const file=state.files[i];\r\n      const arr=await file.arrayBuffer();\r\n      let image;\r\n      if(file.type==='image\/png') image=await doc.embedPng(arr);\r\n      else if(file.type==='image\/jpeg') image=await doc.embedJpg(arr);\r\n      else {\r\n        const bitmap=await createImageBitmap(file);\r\n        const canvas=document.createElement('canvas');canvas.width=bitmap.width;canvas.height=bitmap.height;\r\n        canvas.getContext('2d').drawImage(bitmap,0,0);\r\n        const blob=await new Promise(r=>canvas.toBlob(r,'image\/png'));\r\n        image=await doc.embedPng(await blob.arrayBuffer());\r\n      }\r\n      let pageSize;\r\n      if(size==='original') pageSize=[image.width,image.height];\r\n      else pageSize=[...sizes[size]];\r\n      if(orientation==='landscape' && pageSize[1]>pageSize[0]) pageSize.reverse();\r\n      if(orientation==='portrait' && pageSize[0]>pageSize[1]) pageSize.reverse();\r\n      const page=doc.addPage(pageSize);\r\n      const maxW=Math.max(1,pageSize[0]-2*margin),maxH=Math.max(1,pageSize[1]-2*margin);\r\n      const scale=Math.min(maxW\/image.width,maxH\/image.height);\r\n      const w=image.width*scale,h=image.height*scale;\r\n      page.drawImage(image,{x:(pageSize[0]-w)\/2,y:(pageSize[1]-h)\/2,width:w,height:h});\r\n      progress((i\/state.files.length)*95,`Adding image ${i+1} of ${state.files.length}\u2026`);\r\n    }\r\n    return {bytes:await doc.save(),name:'images-to-pdf.pdf'};\r\n  }\r\n\r\n  async function runPdfToImage(){\r\n    const data=await state.files[0].arrayBuffer();\r\n    const pdf=await pdfjsLib.getDocument({data}).promise;\r\n    const pages=parsePages(document.getElementById('pdfsp-image-pages').value,pdf.numPages);\r\n    const format=document.getElementById('pdfsp-image-format').value;\r\n    const scale=Number(document.getElementById('pdfsp-image-quality').value);\r\n    const zip=new JSZip();\r\n    for(let i=0;i<pages.length;i++){\r\n      const pageNo=pages[i]+1,page=await pdf.getPage(pageNo),viewport=page.getViewport({scale});\r\n      const canvas=document.createElement('canvas');canvas.width=viewport.width;canvas.height=viewport.height;\r\n      await page.render({canvasContext:canvas.getContext('2d'),viewport}).promise;\r\n      const mime=format==='png'?'image\/png':'image\/jpeg';\r\n      const blob=await new Promise(r=>canvas.toBlob(r,mime,.92));\r\n      zip.file(`page-${String(pageNo).padStart(3,'0')}.${format}`,await blob.arrayBuffer());\r\n      progress((i\/pages.length)*95,`Rendering page ${i+1} of ${pages.length}\u2026`);\r\n      canvas.width=1;canvas.height=1;\r\n    }\r\n    return {bytes:await zip.generateAsync({type:'uint8array'}),name:`pdf-images-${format}.zip`,type:'application\/zip'};\r\n  }\r\n\r\n  function hexToRgb(hex){\r\n    const n=parseInt(hex.replace('#',''),16);\r\n    return {r:((n>>16)&255)\/255,g:((n>>8)&255)\/255,b:(n&255)\/255};\r\n  }\r\n\r\n  async function runWatermark(){\r\n    const doc=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const pages=doc.getPages(),font=await doc.embedFont(StandardFonts.HelveticaBold);\r\n    const text=document.getElementById('pdfsp-watermark-text').value.trim();\r\n    if(!text) throw new Error('Please enter watermark text.');\r\n    const size=Number(document.getElementById('pdfsp-watermark-size').value)||48;\r\n    const opacity=Number(document.getElementById('pdfsp-watermark-opacity').value)||.25;\r\n    const angle=Number(document.getElementById('pdfsp-watermark-rotation').value)||0;\r\n    const c=hexToRgb(document.getElementById('pdfsp-watermark-color').value);\r\n    const selected=parsePages(document.getElementById('pdfsp-watermark-pages').value,pages.length);\r\n    selected.forEach((idx,i)=>{\r\n      const page=pages[idx],{width,height}=page.getSize(),tw=font.widthOfTextAtSize(text,size);\r\n      page.drawText(text,{x:(width-tw)\/2,y:height\/2,size,font,color:rgb(c.r,c.g,c.b),opacity,rotate:degrees(angle)});\r\n      progress((i\/selected.length)*95,`Watermarking page ${i+1} of ${selected.length}\u2026`);\r\n    });\r\n    return {bytes:await doc.save(),name:'watermarked-document.pdf'};\r\n  }\r\n\r\n  async function runPageNumbers(){\r\n    const doc=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const pages=doc.getPages(),font=await doc.embedFont(StandardFonts.Helvetica);\r\n    const position=document.getElementById('pdfsp-number-position').value;\r\n    const start=Number(document.getElementById('pdfsp-number-start').value)||0;\r\n    const prefix=document.getElementById('pdfsp-number-prefix').value;\r\n    const suffix=document.getElementById('pdfsp-number-suffix').value;\r\n    const size=Number(document.getElementById('pdfsp-number-size').value)||11;\r\n    pages.forEach((page,i)=>{\r\n      const text=`${prefix}${start+i}${suffix}`,{width,height}=page.getSize(),tw=font.widthOfTextAtSize(text,size),margin=24;\r\n      let x=(width-tw)\/2,y=margin;\r\n      if(position.includes('left')) x=margin;\r\n      if(position.includes('right')) x=width-tw-margin;\r\n      if(position.includes('top')) y=height-size-margin;\r\n      page.drawText(text,{x,y,size,font,color:rgb(.2,.2,.25)});\r\n      progress((i\/pages.length)*95,`Numbering page ${i+1} of ${pages.length}\u2026`);\r\n    });\r\n    return {bytes:await doc.save(),name:'numbered-document.pdf'};\r\n  }\r\n\r\n  function textPosition(page, text, font, size, position, margin=24){\r\n    const {width,height}=page.getSize();\r\n    const tw=font.widthOfTextAtSize(text,size);\r\n    let x=(width-tw)\/2,y=margin;\r\n    if(position.includes('left')) x=margin;\r\n    if(position.includes('right')) x=width-tw-margin;\r\n    if(position.includes('top')) y=height-size-margin;\r\n    return {x,y};\r\n  }\r\n\r\n  async function runHeaderFooter(){\r\n    const doc=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const pages=doc.getPages(),font=await doc.embedFont(StandardFonts.Helvetica);\r\n    const header=document.getElementById('pdfsp-header-text').value.trim();\r\n    const footer=document.getElementById('pdfsp-footer-text').value.trim();\r\n    const align=document.getElementById('pdfsp-hf-align').value;\r\n    const size=Number(document.getElementById('pdfsp-hf-size').value)||9;\r\n    pages.forEach((page,i)=>{\r\n      const {width,height}=page.getSize();\r\n      const place=(text,isTop)=>{\r\n        if(!text)return;\r\n        const tw=font.widthOfTextAtSize(text,size);let x=24;\r\n        if(align==='center')x=(width-tw)\/2;if(align==='right')x=width-tw-24;\r\n        page.drawText(text,{x,y:isTop?height-size-18:18,size,font,color:rgb(.25,.25,.3)});\r\n      };\r\n      place(header,true);place(footer,false);progress((i\/pages.length)*95,`Updating page ${i+1} of ${pages.length}\u2026`);\r\n    });\r\n    return {bytes:await doc.save(),name:'header-footer-document.pdf'};\r\n  }\r\n\r\n  async function runBates(){\r\n    const doc=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const pages=doc.getPages(),font=await doc.embedFont(StandardFonts.CourierBold);\r\n    const prefix=document.getElementById('pdfsp-bates-prefix').value;\r\n    const start=Number(document.getElementById('pdfsp-bates-start').value)||0;\r\n    const digits=Math.max(2,Math.min(12,Number(document.getElementById('pdfsp-bates-digits').value)||6));\r\n    const position=document.getElementById('pdfsp-bates-position').value;\r\n    pages.forEach((page,i)=>{\r\n      const text=prefix+String(start+i).padStart(digits,'0');\r\n      const pos=textPosition(page,text,font,10,position,22);\r\n      page.drawText(text,{...pos,size:10,font,color:rgb(.12,.12,.15)});\r\n      progress((i\/pages.length)*95,`Numbering page ${i+1} of ${pages.length}\u2026`);\r\n    });\r\n    return {bytes:await doc.save(),name:'bates-numbered-document.pdf'};\r\n  }\r\n\r\n  async function runStamp(){\r\n    const doc=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const pages=doc.getPages(),font=await doc.embedFont(StandardFonts.HelveticaBold);\r\n    let text=document.getElementById('pdfsp-stamp-text').value;\r\n    if(text==='CUSTOM')text=document.getElementById('pdfsp-stamp-custom').value.trim();\r\n    if(!text)throw new Error('Please enter stamp text.');\r\n    const selected=parsePages(document.getElementById('pdfsp-stamp-pages').value,pages.length);\r\n    const position=document.getElementById('pdfsp-stamp-position').value,size=34;\r\n    selected.forEach((idx,i)=>{\r\n      const page=pages[idx],{width,height}=page.getSize(),tw=font.widthOfTextAtSize(text,size),pad=12;\r\n      let x=(width-tw)\/2,y=(height-size)\/2;\r\n      if(position.includes('left'))x=30;if(position.includes('right'))x=width-tw-30;\r\n      if(position.includes('top'))y=height-size-40;if(position.includes('bottom'))y=40;\r\n      page.drawRectangle({x:x-pad,y:y-pad\/2,width:tw+pad*2,height:size+pad,borderColor:rgb(.78,.08,.12),borderWidth:3,opacity:.8});\r\n      page.drawText(text,{x,y,size,font,color:rgb(.78,.08,.12),opacity:.8});\r\n      progress((i\/selected.length)*95,`Stamping page ${i+1} of ${selected.length}\u2026`);\r\n    });\r\n    return {bytes:await doc.save(),name:'stamped-document.pdf'};\r\n  }\r\n\r\n  async function runExtractText(){\r\n    const data=await state.files[0].arrayBuffer();\r\n    const pdf=await pdfjsLib.getDocument({data}).promise;let output='';\r\n    for(let i=1;i<=pdf.numPages;i++){\r\n      const page=await pdf.getPage(i),content=await page.getTextContent();\r\n      const text=content.items.map(item=>item.str).join(' ');\r\n      output+=`--- Page ${i} ---\\n${text}\\n\\n`;\r\n      progress((i\/pdf.numPages)*95,`Extracting page ${i} of ${pdf.numPages}\u2026`);\r\n    }\r\n    document.getElementById('pdfsp-text-preview').value=output;\r\n    return {bytes:new TextEncoder().encode(output),name:'extracted-pdf-text.txt',type:'text\/plain;charset=utf-8'};\r\n  }\r\n\r\n  async function previewMetadata(){\r\n    try{\r\n      const doc=await PDFDocument.load(await state.files[0].arrayBuffer(),{updateMetadata:false});\r\n      const box=document.getElementById('pdfsp-meta-preview');if(!box)return;\r\n      const lines=[`Pages: ${doc.getPageCount()}`,`Title: ${doc.getTitle()||'\u2014'}`,`Author: ${doc.getAuthor()||'\u2014'}`,`Subject: ${doc.getSubject()||'\u2014'}`,`Keywords: ${(doc.getKeywords()||'\u2014')}`,`Creator: ${doc.getCreator()||'\u2014'}`,`Producer: ${doc.getProducer()||'\u2014'}`,`Creation date: ${doc.getCreationDate()||'\u2014'}`,`Modification date: ${doc.getModificationDate()||'\u2014'}`];\r\n      box.textContent=lines.join('\\n');\r\n    }catch(e){showAlert('Unable to read PDF metadata.');}\r\n  }\r\n\r\n  async function runMetadata(){\r\n    const doc=await PDFDocument.load(await state.files[0].arrayBuffer(),{updateMetadata:false});\r\n    doc.setTitle('');doc.setAuthor('');doc.setSubject('');doc.setKeywords([]);doc.setCreator('');doc.setProducer('');\r\n    doc.setCreationDate(new Date(0));doc.setModificationDate(new Date(0));\r\n    return {bytes:await doc.save({updateFieldAppearances:false}),name:'metadata-cleaned-document.pdf'};\r\n  }\r\n\r\n  async function runFlatten(){\r\n    const data=await state.files[0].arrayBuffer();\r\n    const src=await pdfjsLib.getDocument({data}).promise;\r\n    const out=await PDFDocument.create();const scale=Number(document.getElementById('pdfsp-flatten-scale').value)||1.5;\r\n    for(let i=1;i<=src.numPages;i++){\r\n      const page=await src.getPage(i),vp=page.getViewport({scale});\r\n      const canvas=document.createElement('canvas');canvas.width=Math.ceil(vp.width);canvas.height=Math.ceil(vp.height);\r\n      await page.render({canvasContext:canvas.getContext('2d'),viewport:vp}).promise;\r\n      const blob=await new Promise(r=>canvas.toBlob(r,'image\/jpeg',.9));\r\n      const img=await out.embedJpg(await blob.arrayBuffer());\r\n      const original=page.getViewport({scale:1});const p=out.addPage([original.width,original.height]);\r\n      p.drawImage(img,{x:0,y:0,width:original.width,height:original.height});\r\n      canvas.width=1;canvas.height=1;progress((i\/src.numPages)*95,`Flattening page ${i} of ${src.numPages}\u2026`);\r\n    }\r\n    return {bytes:await out.save(),name:'flattened-document.pdf'};\r\n  }\r\n\r\n  async function runResize(){\r\n    const src=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const out=await PDFDocument.create();\r\n    const sizes={A4:[595.28,841.89],A3:[841.89,1190.55],LETTER:[612,792],LEGAL:[612,1008]};\r\n    const key=document.getElementById('pdfsp-resize-size').value,orient=document.getElementById('pdfsp-resize-orientation').value,margin=Math.max(0,Number(document.getElementById('pdfsp-resize-margin').value)||0);\r\n    for(let i=0;i<src.getPageCount();i++){\r\n      const [embedded]=await out.embedPages([src.getPage(i)]);let target=[...sizes[key]];\r\n      const sw=embedded.width,sh=embedded.height;\r\n      if(orient==='landscape'||(orient==='auto'&&sw>sh)){if(target[0]<target[1])target.reverse();}\r\n      if(orient==='portrait'&&target[0]>target[1])target.reverse();\r\n      const p=out.addPage(target),maxW=target[0]-2*margin,maxH=target[1]-2*margin,scale=Math.min(maxW\/sw,maxH\/sh),w=sw*scale,h=sh*scale;\r\n      p.drawPage(embedded,{x:(target[0]-w)\/2,y:(target[1]-h)\/2,width:w,height:h});\r\n      progress((i\/src.getPageCount())*95,`Resizing page ${i+1} of ${src.getPageCount()}\u2026`);\r\n    }\r\n    return {bytes:await out.save(),name:`resized-${key.toLowerCase()}-document.pdf`};\r\n  }\r\n\r\n\r\n  async function prepareTextEditor(){\r\n    try{\r\n      const data=await state.files[0].arrayBuffer();\r\n      state.editorPdf=await pdfjsLib.getDocument({data}).promise;\r\n      const pageInput=document.getElementById('pdfsp-edit-page');\r\n      if(!pageInput)return;\r\n      pageInput.max=state.editorPdf.numPages;\r\n      const rerender=()=>renderEditorPage(Math.max(1,Math.min(state.editorPdf.numPages,Number(pageInput.value)||1)));\r\n      pageInput.onchange=rerender;\r\n      ['pdfsp-edit-x','pdfsp-edit-y','pdfsp-edit-width','pdfsp-edit-height'].forEach(id=>document.getElementById(id).addEventListener('input',updateEditorMarker));\r\n      await rerender();\r\n    }catch(e){showAlert('Unable to prepare the PDF editor. The file may be corrupted or password protected.');}\r\n  }\r\n\r\n  async function renderEditorPage(pageNo){\r\n    if(!state.editorPdf)return;\r\n    state.editorPage=pageNo;\r\n    progress(10,`Rendering page ${pageNo}\u2026`);\r\n    const page=await state.editorPdf.getPage(pageNo);\r\n    const viewport=page.getViewport({scale:state.editorScale});\r\n    const canvas=document.getElementById('pdfsp-editor-canvas');\r\n    if(!canvas)return;\r\n    canvas.width=viewport.width;canvas.height=viewport.height;\r\n    await page.render({canvasContext:canvas.getContext('2d'),viewport}).promise;\r\n    canvas.onclick=e=>{\r\n      const rect=canvas.getBoundingClientRect();\r\n      const scaleX=canvas.width\/rect.width,scaleY=canvas.height\/rect.height;\r\n      const px=(e.clientX-rect.left)*scaleX\/state.editorScale;\r\n      const py=(e.clientY-rect.top)*scaleY\/state.editorScale;\r\n      document.getElementById('pdfsp-edit-x').value=Math.round(px);\r\n      document.getElementById('pdfsp-edit-y').value=Math.round(py);\r\n      updateEditorMarker();\r\n    };\r\n    updateEditorMarker();hideProgress();\r\n  }\r\n\r\n  function updateEditorMarker(){\r\n    const marker=document.getElementById('pdfsp-editor-marker');\r\n    if(!marker)return;\r\n    const x=Number(document.getElementById('pdfsp-edit-x').value)||0;\r\n    const y=Number(document.getElementById('pdfsp-edit-y').value)||0;\r\n    const w=Number(document.getElementById('pdfsp-edit-width').value)||10;\r\n    const h=Number(document.getElementById('pdfsp-edit-height').value)||10;\r\n    marker.hidden=false;marker.style.left=(x*state.editorScale)+'px';marker.style.top=(y*state.editorScale)+'px';marker.style.width=(w*state.editorScale)+'px';marker.style.height=(h*state.editorScale)+'px';\r\n  }\r\n\r\n  async function runEditText(){\r\n    const doc=await PDFDocument.load(await state.files[0].arrayBuffer());\r\n    const pageNo=Number(document.getElementById('pdfsp-edit-page').value)||1;\r\n    if(pageNo<1||pageNo>doc.getPageCount())throw new Error('Invalid page number.');\r\n    const page=doc.getPage(pageNo-1),{height}=page.getSize();\r\n    const x=Number(document.getElementById('pdfsp-edit-x').value)||0;\r\n    const topY=Number(document.getElementById('pdfsp-edit-y').value)||0;\r\n    const w=Math.max(5,Number(document.getElementById('pdfsp-edit-width').value)||180);\r\n    const h=Math.max(5,Number(document.getElementById('pdfsp-edit-height').value)||24);\r\n    const text=document.getElementById('pdfsp-edit-text').value;\r\n    const size=Math.max(6,Number(document.getElementById('pdfsp-edit-size').value)||12);\r\n    const tc=hexToRgb(document.getElementById('pdfsp-edit-color').value),bc=hexToRgb(document.getElementById('pdfsp-edit-bg').value);\r\n    const y=height-topY-h;\r\n    page.drawRectangle({x,y,width:w,height:h,color:rgb(bc.r,bc.g,bc.b)});\r\n    const font=await doc.embedFont(StandardFonts.Helvetica);\r\n    page.drawText(text,{x:x+2,y:y+Math.max(2,(h-size)\/2+1),size,font,color:rgb(tc.r,tc.g,tc.b),maxWidth:Math.max(1,w-4)});\r\n    return {bytes:await doc.save(),name:'text-edited-document.pdf'};\r\n  }\r\n\r\n  async function extractPdfPagesText(file,label){\r\n    const pdf=await pdfjsLib.getDocument({data:await file.arrayBuffer()}).promise;\r\n    const pages=[];\r\n    for(let i=1;i<=pdf.numPages;i++){\r\n      const content=await (await pdf.getPage(i)).getTextContent();\r\n      const text=content.items.map(item=>item.str).join(' ').replace(\/\\s+\/g,' ').trim();\r\n      pages.push(text);progress((i\/pdf.numPages)*45,`${label}: reading page ${i} of ${pdf.numPages}\u2026`);\r\n    }\r\n    return pages;\r\n  }\r\n\r\n  function tokenizeDiff(text){return text.match(\/\\s+|[^\\s]+\/g)||[];}\r\n  function diffTokens(aText,bText){\r\n    const a=tokenizeDiff(aText),b=tokenizeDiff(bText),n=a.length,m=b.length;\r\n    if(n*m>1600000){\r\n      return {left:[{type:'removed',text:aText}],right:[{type:'added',text:bText}],changed:aText!==bText};\r\n    }\r\n    const dp=Array.from({length:n+1},()=>new Uint32Array(m+1));\r\n    for(let i=n-1;i>=0;i--)for(let j=m-1;j>=0;j--)dp[i][j]=a[i]===b[j]?dp[i+1][j+1]+1:Math.max(dp[i+1][j],dp[i][j+1]);\r\n    const left=[],right=[];let i=0,j=0,changed=false;\r\n    while(i<n&&j<m){\r\n      if(a[i]===b[j]){left.push({type:'same',text:a[i]});right.push({type:'same',text:b[j]});i++;j++;}\r\n      else if(dp[i+1][j]>=dp[i][j+1]){left.push({type:'removed',text:a[i++]});changed=true;}\r\n      else{right.push({type:'added',text:b[j++]});changed=true;}\r\n    }\r\n    while(i<n){left.push({type:'removed',text:a[i++]});changed=true;}\r\n    while(j<m){right.push({type:'added',text:b[j++]});changed=true;}\r\n    return {left,right,changed};\r\n  }\r\n  function renderDiffTokens(parts){return parts.map(p=>`<span class=\"pdfsp-diff-${p.type}\">${escapeHtml(p.text)}<\/span>`).join('');}\r\n\r\n  async function runComparePdf(){\r\n    if(state.files.length!==2)throw new Error('Please select exactly two PDF files.');\r\n    const aPages=await extractPdfPagesText(state.files[0],'First PDF');\r\n    const bPages=await extractPdfPagesText(state.files[1],'Second PDF');\r\n    const total=Math.max(aPages.length,bPages.length);let changedPages=0,htmlPages='';\r\n    for(let i=0;i<total;i++){\r\n      const d=diffTokens(aPages[i]||'',bPages[i]||'');if(d.changed)changedPages++;\r\n      htmlPages+=`<section class=\"pdfsp-compare-page\"><h4>Page ${i+1}${d.changed?' \u2014 Differences found':' \u2014 No text difference'}<\/h4><div class=\"pdfsp-compare-columns\"><div class=\"pdfsp-compare-col\"><strong>${escapeHtml(state.files[0].name)}<\/strong><hr>${renderDiffTokens(d.left)}<\/div><div class=\"pdfsp-compare-col\"><strong>${escapeHtml(state.files[1].name)}<\/strong><hr>${renderDiffTokens(d.right)}<\/div><\/div><\/section>`;\r\n      progress(45+(i\/Math.max(1,total))*50,`Comparing page ${i+1} of ${total}\u2026`);\r\n    }\r\n    const report=`<div class=\"pdfsp-compare-summary\"><span class=\"pdfsp-stat\">Pages compared: ${total}<\/span><span class=\"pdfsp-stat\">Pages with differences: ${changedPages}<\/span><span class=\"pdfsp-stat\">Unchanged pages: ${total-changedPages}<\/span><\/div><div class=\"pdfsp-compare-pages\">${htmlPages}<\/div>`;\r\n    const host=document.getElementById('pdfsp-compare-report');if(host)host.innerHTML=report;\r\n    const standalone=`<!doctype html><html><head><meta charset=\"utf-8\"><title>PDF Comparison Report<\/title><style>body{font-family:Arial,sans-serif;margin:24px;color:#172033}.page{border:1px solid #ccd3df;border-radius:10px;margin:16px 0;overflow:hidden}.page h3{margin:0;padding:10px;background:#f2f4f8}.cols{display:grid;grid-template-columns:1fr 1fr}.col{padding:14px;white-space:pre-wrap;word-break:break-word;font-family:Consolas,monospace;line-height:1.7}.col+.col{border-left:1px solid #ccd3df}.added{background:#c8f3d9;color:#0b5b32}.removed{background:#ffd6db;color:#8d2030;text-decoration:line-through}@media(max-width:700px){.cols{grid-template-columns:1fr}.col+.col{border-left:0;border-top:1px solid #ccd3df}}<\/style><\/head><body><h1>PDF Comparison Report<\/h1><p>${escapeHtml(state.files[0].name)} compared with ${escapeHtml(state.files[1].name)}<\/p>${htmlPages.replaceAll('pdfsp-compare-page','page').replaceAll('pdfsp-compare-columns','cols').replaceAll('pdfsp-compare-col','col').replaceAll('pdfsp-diff-added','added').replaceAll('pdfsp-diff-removed','removed').replaceAll('pdfsp-diff-same','same')}<\/body><\/html>`;\r\n    return {bytes:new TextEncoder().encode(standalone),name:'pdf-comparison-report.html',type:'text\/html'};\r\n  }\r\n\r\n  async function processFiles(){\r\n    clearAlert();els.result.hidden=true;els.process.disabled=true;\r\n    try{\r\n      progress(2,'Preparing files\u2026');\r\n      let result;\r\n      if(state.tool==='merge') result=await runMerge();\r\n      if(state.tool==='split') result=await runSplit();\r\n      if(state.tool==='organize') result=await runOrganize();\r\n      if(state.tool==='imageToPdf') result=await runImageToPdf();\r\n      if(state.tool==='pdfToImage') result=await runPdfToImage();\r\n      if(state.tool==='watermark') result=await runWatermark();\r\n      if(state.tool==='pageNumbers') result=await runPageNumbers();\r\n      if(state.tool==='headerFooter') result=await runHeaderFooter();\r\n      if(state.tool==='bates') result=await runBates();\r\n      if(state.tool==='stamp') result=await runStamp();\r\n      if(state.tool==='extractText') result=await runExtractText();\r\n      if(state.tool==='metadata') result=await runMetadata();\r\n      if(state.tool==='flatten') result=await runFlatten();\r\n      if(state.tool==='resize') result=await runResize();\r\n      if(state.tool==='editText') result=await runEditText();\r\n      if(state.tool==='comparePdf') result=await runComparePdf();\r\n      if(!result) throw new Error('This tool could not be processed.');\r\n      progress(100,'Completed');\r\n      state.output=new Blob([result.bytes],{type:result.type||'application\/pdf'});\r\n      state.outputName=result.name;\r\n      els.resultDetails.textContent=`${result.name} \u2022 ${humanSize(state.output.size)}`;\r\n      els.result.hidden=false;\r\n      els.download.onclick=()=>{\r\n        const url=URL.createObjectURL(state.output);state.objectUrls.push(url);\r\n        const a=document.createElement('a');a.href=url;a.download=state.outputName;document.body.appendChild(a);a.click();a.remove();\r\n        setTimeout(()=>{URL.revokeObjectURL(url);state.objectUrls=state.objectUrls.filter(x=>x!==url)},1500);\r\n      };\r\n      els.result.scrollIntoView({behavior:'smooth',block:'center'});\r\n      setTimeout(hideProgress,900);\r\n    }catch(err){\r\n      hideProgress();\r\n      let msg=err && err.message ? err.message : 'Processing failed. Please check the selected file and try again.';\r\n      if(\/encrypted|password\/i.test(msg)) msg='This PDF appears to be password protected. Please unlock it with an authorized tool before processing.';\r\n      showAlert(msg);\r\n    }finally{els.process.disabled=state.tool==='comparePdf' ? state.files.length!==2 : state.files.length===0}\r\n  }\r\n\r\n  els.grid.addEventListener('click',e=>{\r\n    const card=e.target.closest('.pdfsp-tool-card');if(card)selectTool(card.dataset.tool);\r\n  });\r\n  els.search.addEventListener('input',()=>{\r\n    const q=els.search.value.trim().toLowerCase();\r\n    els.grid.querySelectorAll('.pdfsp-tool-card').forEach(c=>c.classList.toggle('pdfsp-hidden',q && !c.dataset.search.includes(q) && !c.textContent.toLowerCase().includes(q)));\r\n  });\r\n  els.upload.addEventListener('click',e=>{if(!e.target.closest('button') || e.target.closest('button')) els.input.click()});\r\n  els.upload.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();els.input.click()}});\r\n  els.input.addEventListener('change',()=>addFiles(els.input.files));\r\n  ['dragenter','dragover'].forEach(type=>els.upload.addEventListener(type,e=>{e.preventDefault();els.upload.classList.add('is-dragging')}));\r\n  ['dragleave','drop'].forEach(type=>els.upload.addEventListener(type,e=>{e.preventDefault();els.upload.classList.remove('is-dragging')}));\r\n  els.upload.addEventListener('drop',e=>addFiles(e.dataTransfer.files));\r\n  els.process.addEventListener('click',processFiles);\r\n  els.reset.addEventListener('click',resetTool);\r\n  els.language.addEventListener('change',()=>{localStorage.setItem('pdfsp-language',els.language.value);translate()});\r\n  els.theme.addEventListener('click',()=>{\r\n    app.classList.toggle('pdfsp-dark');\r\n    localStorage.setItem('pdfsp-theme',app.classList.contains('pdfsp-dark')?'dark':'light');\r\n  });\r\n  window.addEventListener('beforeunload',cleanupUrls);\r\n\r\n  const lang=localStorage.getItem('pdfsp-language');\r\n  if(lang && I18N[lang]) els.language.value=lang;\r\n  const savedTheme=localStorage.getItem('pdfsp-theme');\r\n  if(savedTheme==='dark' || (!savedTheme && window.matchMedia('(prefers-color-scheme:dark)').matches)) app.classList.add('pdfsp-dark');\r\n\r\n  translate();\r\n  configureTool();\r\n})();\r\n<\/script>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-676","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/pages\/676","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/comments?post=676"}],"version-history":[{"count":7,"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/pages\/676\/revisions"}],"predecessor-version":[{"id":686,"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/pages\/676\/revisions\/686"}],"wp:attachment":[{"href":"https:\/\/vikassrivastava.in\/academy\/wp-json\/wp\/v2\/media?parent=676"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}