Last active
December 9, 2025 12:47
-
-
Save wefluidmedia-prog/c517564f7225953e68b139ef70a7ad2e to your computer and use it in GitHub Desktop.
"A DOM reconstruction tool for Canvas-based PDF viewers."
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Secure, Bulletproof PDF Downloader (Sorts & Compresses) | |
| (async function() { | |
| 'use strict'; | |
| // --- CONFIG --- | |
| const JSLIB_URL = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.debug.js'; | |
| const IMG_QUALITY = 0.8; // 0.8 is the sweet spot for 500+ pages. 1.0 will crash Chrome. | |
| const PROCESS_DELAY = 20; // ms to wait between pages to keep UI responsive | |
| // -------------- | |
| // 1. Trusted Types Policy (Bypass Content-Security-Policy) | |
| let trustedURL = JSLIB_URL; | |
| if (window.trustedTypes && window.trustedTypes.createPolicy) { | |
| if (!window.trustedTypes.defaultPolicy) { | |
| const policyName = 'jsPdfLoader_' + Math.floor(Math.random() * 10000); | |
| const policy = window.trustedTypes.createPolicy(policyName, { | |
| createScriptURL: (input) => { | |
| if (input === JSLIB_URL) return input; | |
| throw new Error('Security Violation: Unauthorized Script URL.'); | |
| } | |
| }); | |
| trustedURL = policy.createScriptURL(JSLIB_URL); | |
| } | |
| } | |
| // 2. Library Loader | |
| const loadScript = (src) => { | |
| return new Promise((resolve, reject) => { | |
| if (window.jspdf) { resolve(); return; } | |
| const script = document.createElement('script'); | |
| script.src = src; | |
| script.onload = resolve; | |
| script.onerror = reject; | |
| document.body.appendChild(script); | |
| }); | |
| }; | |
| try { | |
| console.log("[*] Injecting jsPDF..."); | |
| await loadScript(trustedURL); | |
| // 3. Intelligent Element Selection & Sorting | |
| console.log("[*] Scanning DOM for images..."); | |
| // Convert HTMLCollection to Array | |
| let rawElements = Array.from(document.getElementsByTagName("img")); | |
| // Filter: Must be a Blob (loaded) and have dimensions | |
| let validElements = rawElements.filter(img => | |
| img.src.startsWith('blob:') && | |
| img.naturalWidth > 100 && // Filter out tiny icons | |
| img.naturalHeight > 100 | |
| ); | |
| if (validElements.length === 0) { | |
| throw new Error("No images found. Did you scroll the document to load them?"); | |
| } | |
| console.log(`[*] Found ${validElements.length} candidate images. Sorting by vertical position...`); | |
| // Sort by Y position (Top to Bottom) to fix any DOM ordering issues | |
| validElements.sort((a, b) => { | |
| const rectA = a.getBoundingClientRect(); | |
| const rectB = b.getBoundingClientRect(); | |
| // Compare Y position. If Y is same, compare X (unlikely for vertical PDF but safe) | |
| return (rectA.top + window.scrollY) - (rectB.top + window.scrollY); | |
| }); | |
| // Initialize PDF | |
| const pdf = new jsPDF(); | |
| let pagesProcessed = 0; | |
| console.log(`[*] Starting processing of ${validElements.length} pages...`); | |
| // 4. Processing Loop | |
| for (let i = 0; i < validElements.length; i++) { | |
| const img = validElements[i]; | |
| try { | |
| // Create disposable canvas | |
| let canvas = document.createElement('canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| canvas.width = img.naturalWidth; | |
| canvas.height = img.naturalHeight; | |
| ctx.drawImage(img, 0, 0, canvas.width, canvas.height); | |
| // Compress to JPEG | |
| const imgData = canvas.toDataURL("image/jpeg", IMG_QUALITY); | |
| // Cleanup canvas immediately to free memory | |
| canvas.width = 1; | |
| canvas.height = 1; | |
| canvas = null; | |
| // PDF Logic | |
| const imgProps = pdf.getImageProperties(imgData); | |
| const pdfWidth = pdf.internal.pageSize.getWidth(); | |
| const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width; | |
| pdf.addImage(imgData, 'JPEG', 0, 0, pdfWidth, pdfHeight); | |
| pagesProcessed++; | |
| // Only add a new page if we aren't at the last one | |
| if (i < validElements.length - 1) { | |
| pdf.addPage(); | |
| } | |
| // Logging progress every 10 pages or so | |
| if (i % 10 === 0) { | |
| console.log(`[+] Processed Page ${i + 1}/${validElements.length}`); | |
| } | |
| // Prevent UI Freeze | |
| await new Promise(r => setTimeout(r, PROCESS_DELAY)); | |
| } catch (err) { | |
| console.error(`[!] Error processing page ${i + 1}:`, err); | |
| // Continue loop even if one page fails | |
| } | |
| } | |
| console.log(`[*] Success. Saving ${pagesProcessed} pages to PDF...`); | |
| pdf.save("Complete_Document.pdf"); | |
| } catch (e) { | |
| console.error("[-] Critical Failure:", e); | |
| alert("Script failed. Check console for details."); | |
| } | |
| })(); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Step by step guide to downloading protected PDF from Google Drive
Open the document in Google Docs
Scroll to the bottom of the document, so all the pages are present
Open Developer Tools on separate window and choose the Console tab
Paste the code
Have fun!