-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Compression of PDF directly in the browser
- Loading branch information
Showing
12 changed files
with
15,472 additions
and
7 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,5 @@ public/*.css | |
public/*.svg | ||
public/*.ico | ||
public/*.png | ||
public/site.webmanifest | ||
public/site.webmanifest | ||
public/lib/* |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,6 +34,24 @@ | |
<p id="my-form-status"></p> | ||
</form> | ||
|
||
<hr> | ||
<h3 id="compress-your-pdf">Compress your PDF</h3> | ||
<p>🇮🇹 Comprimi i tuoi appunti mantenendo alta la qualità. Avviene tutto nel tuo browser, nessun server esterno riceverà il tuo PDF, garantendo così la privacy e costi zero. Tieni presente che più il file è grande e più sarà lunga l'operazione (il tool è in grado di passare da 100 MB a 30-50MB dai nostri test, ma in più di 20 minuti a volte!). Dunque lascialo lavorare in background e prenditi un caffè.</p> | ||
<p>❗ <b>NON CAMBIARE/RICARICARE LA PAGINA MENTRE IL TOOL LAVORA IN BACKGROUND.</b> Nel frattempo, puoi comunque mandarci messaggi dal form sopra e usare le altre tab del browser come sempre.</p> | ||
<p>🇬🇧 Compress your notes while maintaining high quality. All processing happens within your browser, with no server involved, ensuring privacy and no cost. Keep in mind that the larger the file, the longer the compression will take. Based on our tests, the tool can compress files from 100MB to 30-50MB, but this process can sometimes take over 20 minutes. So, let it run in the background while you take a coffee.</p> | ||
<p>❗ <b>DO NOT CHANGE OR REFRESH THE PAGE WHILE THE TOOL IS WORKING IN THE BACKGROUND.</b> In the meantime, you can always send us messages using the form above and continue using other browser tabs as usual.</p> | ||
<form id="formfile"> | ||
<div id="flexfile"> | ||
<input class="file" id="filein" type="file" accept="application/pdf"> | ||
<button type="submit" id="filesub" class="file" style="visibility: hidden;opacity: 0;" disabled>Submit</button> | ||
</div> | ||
<div id="flexload"> | ||
<a style="display: none;" id="afile" target="_blank">Download</a> | ||
<div id="loading2"></div> | ||
</div> | ||
</form> | ||
<p id="thanks">🥳 <b>This feature exists because of the work done by <a href="https://meyer-laurent.com/" target="_blank">Laurent Meyer</a>.</b> You can read more about how it works <a href="https://meyer-laurent.com/playing-around-webassembly-and-ghostscript" target="_blank">here</a>.</p> | ||
|
||
<script | ||
type="module" | ||
src="https://cdn.jsdelivr.net/npm/[email protected]/widget.module.min.js" | ||
|
@@ -47,11 +65,97 @@ | |
<link rel="stylesheet" href="{{ relURL "noscript_smooth.css" }}" crossorigin="anonymous"> | ||
</noscript> | ||
|
||
<script type="module"> | ||
import {_GSPS2PDF} from "{{ relURL "lib/worker-init.js"}}"; | ||
|
||
const filein = document.querySelector("#filein"); | ||
const filesub = document.querySelector("#filesub"); | ||
const formfile = document.querySelector("#formfile"); | ||
const afile = document.querySelector("#afile"); | ||
const loader2 = document.querySelector("#loading2"); | ||
|
||
function loadPDFData(response, filename) { | ||
return new Promise((resolve, reject) => { | ||
const xhr = new XMLHttpRequest(); | ||
xhr.open("GET", response); | ||
xhr.responseType = "arraybuffer"; | ||
xhr.onload = async function (e) { | ||
const re = e.currentTarget.response; | ||
window.URL.revokeObjectURL(response); | ||
const blob = new Blob([re], {type: "application/pdf"}); | ||
const pdfURL = window.URL.createObjectURL(blob); | ||
const size = re.byteLength; | ||
resolve({pdfURL, size}); | ||
}; | ||
xhr.send(); | ||
}); | ||
} | ||
|
||
async function compressPDF(pdf, filename) { | ||
const dataObject = { psDataURL: pdf }; | ||
loader2.classList.add("display"); | ||
const element = await _GSPS2PDF(dataObject); | ||
const {pdfURL, size} = await loadPDFData(element, filename); | ||
console.log(size); | ||
formfile.reset(); | ||
afile.setAttribute("href", pdfURL); | ||
afile.setAttribute("download", minFilename(filename)); | ||
afile.style.display = "block"; | ||
pdfURLs = pdfURL; | ||
} | ||
|
||
const minFilename = (filename) => filename.replace(".pdf", "-min.pdf"); | ||
var file, url; | ||
var pdfURLs; | ||
var start = 0; | ||
|
||
filein.addEventListener("change", (event) => { | ||
file = event.target.files[0]; | ||
console.log(file.size); | ||
filesub.disabled = false; | ||
filesub.style.visibility = "visible"; | ||
filesub.style.opacity = "1"; | ||
}) | ||
|
||
filesub.addEventListener("click", async function(event) { | ||
event.preventDefault(); | ||
if(!start){ | ||
filesub.disabled = true; | ||
filein.disabled = true; | ||
filesub.style.visibility = "hidden"; | ||
filesub.style.opacity = "0"; | ||
start = 1; | ||
url = window.URL.createObjectURL(file); | ||
await compressPDF(url, file.name); | ||
loader2.classList.remove("display"); | ||
return false; | ||
} else { | ||
afile.style.display = "none"; | ||
window.URL.revokeObjectURL(pdfURLs); | ||
filein.disabled = false; | ||
filesub.style.visibility = "hidden"; | ||
filesub.style.opacity = "0"; | ||
filesub.disabled = true; | ||
setTimeout(() => {filesub.textContent = "Submit";}, 1500); | ||
start = 0; | ||
} | ||
}) | ||
|
||
afile.addEventListener("click", (e) => { | ||
filesub.textContent = "Restart"; | ||
filesub.disabled = false; | ||
filesub.style.visibility = "visible"; | ||
filesub.style.opacity = "1"; | ||
return false; | ||
}) | ||
</script> | ||
|
||
<script> | ||
|
||
var inputs = document.getElementsByTagName('input'); | ||
for(let i=0; i<inputs.length; i++){ | ||
if(inputs[i].type === "checkbox"){continue}; | ||
if(inputs[i].getAttribute("class") == "file"){continue}; | ||
inputs[i].setAttribute('size',inputs[i].getAttribute('placeholder').length); | ||
} | ||
|
||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -103,7 +103,7 @@ | |
❗ 🇬🇧 Feel free to share any issues about the website, your notes or ideas. | ||
⬇️ You can do it down below or open an issue on Github. ➡️ Here, instead, you can also send your notes directly. | ||
❗ E-mail us Submit"> | ||
<meta itemprop="wordCount" content="180"> | ||
<meta itemprop="wordCount" content="416"> | ||
<meta name="referrer" content="no-referrer-when-downgrade" /> | ||
|
||
<script> | ||
|
@@ -348,6 +348,24 @@ <h3 id="e-mail-us">E-mail us</h3> | |
<p id="my-form-status"></p> | ||
</form> | ||
|
||
<hr> | ||
<h3 id="compress-your-pdf">Compress your PDF</h3> | ||
<p>🇮🇹 Comprimi i tuoi appunti mantenendo alta la qualità. Avviene tutto nel tuo browser, nessun server esterno riceverà il tuo PDF, garantendo così la privacy e costi zero. Tieni presente che più il file è grande e più sarà lunga l'operazione (il tool è in grado di passare da 100 MB a 30-50MB dai nostri test, ma in più di 20 minuti a volte!). Dunque lascialo lavorare in background e prenditi un caffè.</p> | ||
<p>❗ <b>NON CAMBIARE/RICARICARE LA PAGINA MENTRE IL TOOL LAVORA IN BACKGROUND.</b> Nel frattempo, puoi comunque mandarci messaggi dal form sopra e usare le altre tab del browser come sempre.</p> | ||
<p>🇬🇧 Compress your notes while maintaining high quality. All processing happens within your browser, with no server involved, ensuring privacy and no cost. Keep in mind that the larger the file, the longer the compression will take. Based on our tests, the tool can compress files from 100MB to 30-50MB, but this process can sometimes take over 20 minutes. So, let it run in the background while you take a coffee.</p> | ||
<p>❗ <b>DO NOT CHANGE OR REFRESH THE PAGE WHILE THE TOOL IS WORKING IN THE BACKGROUND.</b> In the meantime, you can always send us messages using the form above and continue using other browser tabs as usual.</p> | ||
<form id="formfile"> | ||
<div id="flexfile"> | ||
<input class="file" id="filein" type="file" accept="application/pdf"> | ||
<button type="submit" id="filesub" class="file" style="visibility: hidden;opacity: 0;" disabled>Submit</button> | ||
</div> | ||
<div id="flexload"> | ||
<a style="display: none;" id="afile" target="_blank">Download</a> | ||
<div id="loading2"></div> | ||
</div> | ||
</form> | ||
<p id="thanks">🥳 <b>This feature exists because of the work done by <a href="https://meyer-laurent.com/" target="_blank">Laurent Meyer</a>.</b> You can read more about how it works <a href="https://meyer-laurent.com/playing-around-webassembly-and-ghostscript" target="_blank">here</a>.</p> | ||
|
||
<script | ||
type="module" | ||
src="https://cdn.jsdelivr.net/npm/[email protected]/widget.module.min.js" | ||
|
@@ -361,11 +379,97 @@ <h3 id="e-mail-us">E-mail us</h3> | |
<link rel="stylesheet" href="/noscript_smooth.css" crossorigin="anonymous"> | ||
</noscript> | ||
|
||
<script type="module"> | ||
import {_GSPS2PDF} from "\/lib\/worker-init.js"; | ||
|
||
const filein = document.querySelector("#filein"); | ||
const filesub = document.querySelector("#filesub"); | ||
const formfile = document.querySelector("#formfile"); | ||
const afile = document.querySelector("#afile"); | ||
const loader2 = document.querySelector("#loading2"); | ||
|
||
function loadPDFData(response, filename) { | ||
return new Promise((resolve, reject) => { | ||
const xhr = new XMLHttpRequest(); | ||
xhr.open("GET", response); | ||
xhr.responseType = "arraybuffer"; | ||
xhr.onload = async function (e) { | ||
const re = e.currentTarget.response; | ||
window.URL.revokeObjectURL(response); | ||
const blob = new Blob([re], {type: "application/pdf"}); | ||
const pdfURL = window.URL.createObjectURL(blob); | ||
const size = re.byteLength; | ||
resolve({pdfURL, size}); | ||
}; | ||
xhr.send(); | ||
}); | ||
} | ||
|
||
async function compressPDF(pdf, filename) { | ||
const dataObject = { psDataURL: pdf }; | ||
loader2.classList.add("display"); | ||
const element = await _GSPS2PDF(dataObject); | ||
const {pdfURL, size} = await loadPDFData(element, filename); | ||
console.log(size); | ||
formfile.reset(); | ||
afile.setAttribute("href", pdfURL); | ||
afile.setAttribute("download", minFilename(filename)); | ||
afile.style.display = "block"; | ||
pdfURLs = pdfURL; | ||
} | ||
|
||
const minFilename = (filename) => filename.replace(".pdf", "-min.pdf"); | ||
var file, url; | ||
var pdfURLs; | ||
var start = 0; | ||
|
||
filein.addEventListener("change", (event) => { | ||
file = event.target.files[0]; | ||
console.log(file.size); | ||
filesub.disabled = false; | ||
filesub.style.visibility = "visible"; | ||
filesub.style.opacity = "1"; | ||
}) | ||
|
||
filesub.addEventListener("click", async function(event) { | ||
event.preventDefault(); | ||
if(!start){ | ||
filesub.disabled = true; | ||
filein.disabled = true; | ||
filesub.style.visibility = "hidden"; | ||
filesub.style.opacity = "0"; | ||
start = 1; | ||
url = window.URL.createObjectURL(file); | ||
await compressPDF(url, file.name); | ||
loader2.classList.remove("display"); | ||
return false; | ||
} else { | ||
afile.style.display = "none"; | ||
window.URL.revokeObjectURL(pdfURLs); | ||
filein.disabled = false; | ||
filesub.style.visibility = "hidden"; | ||
filesub.style.opacity = "0"; | ||
filesub.disabled = true; | ||
setTimeout(() => {filesub.textContent = "Submit";}, 1500); | ||
start = 0; | ||
} | ||
}) | ||
|
||
afile.addEventListener("click", (e) => { | ||
filesub.textContent = "Restart"; | ||
filesub.disabled = false; | ||
filesub.style.visibility = "visible"; | ||
filesub.style.opacity = "1"; | ||
return false; | ||
}) | ||
</script> | ||
|
||
<script> | ||
|
||
var inputs = document.getElementsByTagName('input'); | ||
for(let i=0; i<inputs.length; i++){ | ||
if(inputs[i].type === "checkbox"){continue}; | ||
if(inputs[i].getAttribute("class") == "file"){continue}; | ||
inputs[i].setAttribute('size',inputs[i].getAttribute('placeholder').length); | ||
} | ||
|
||
|
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
function loadScript() { | ||
import("/lib/gs-worker.js"); | ||
} | ||
|
||
var Module; | ||
|
||
function _GSPS2PDF( | ||
dataStruct, | ||
responseCallback, | ||
) { | ||
// first download the ps data | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open("GET", dataStruct.psDataURL); | ||
xhr.responseType = "arraybuffer"; | ||
xhr.onload = function () { | ||
console.log('onload') | ||
// release the URL | ||
self.URL.revokeObjectURL(dataStruct.psDataURL); | ||
//set up EMScripten environment | ||
Module = { | ||
preRun: [ | ||
function () { | ||
self.Module.FS.writeFile("input.pdf", new Uint8Array(xhr.response)); | ||
}, | ||
], | ||
postRun: [ | ||
function () { | ||
var uarray = self.Module.FS.readFile("output.pdf", { encoding: "binary" }); | ||
var blob = new Blob([uarray], { type: "application/octet-stream" }); | ||
var pdfDataURL = self.URL.createObjectURL(blob); | ||
responseCallback({ pdfDataURL: pdfDataURL, url: dataStruct.url }); | ||
}, | ||
], | ||
arguments: [ | ||
"-sDEVICE=pdfwrite", | ||
"-dCompatibilityLevel=1.4", | ||
"-dPDFSETTINGS=/ebook", | ||
"-DNOPAUSE", | ||
"-dQUIET", | ||
"-dBATCH", | ||
"-dNOGC", | ||
"-sOutputFile=output.pdf", | ||
"input.pdf", | ||
], | ||
print: function (text) { | ||
// statusUpdateCallback(text); | ||
console.log(text); | ||
}, | ||
printErr: function (text) { | ||
// statusUpdateCallback("Error: " + text); | ||
console.error(text); | ||
}, | ||
// setStatus: function (text) { | ||
// if (!Module.setStatus.last) | ||
// Module.setStatus.last = { time: Date.now(), text: "" }; | ||
// if (text === Module.setStatus.last.text) return; | ||
// var m = text.match(/([^(]+)\((\d+(\.\d+)?)\/(\d+)\)/); | ||
// var now = Date.now(); | ||
// if (m && now - Module.setStatus.last.time < 30) | ||
// // if this is a progress update, skip it if too soon | ||
// return; | ||
// Module.setStatus.last.time = now; | ||
// Module.setStatus.last.text = text; | ||
// if (m) { | ||
// text = m[1]; | ||
// progressCallback(false, parseInt(m[2]) * 100, parseInt(m[4]) * 100); | ||
// } else { | ||
// progressCallback(true, 0, 0); | ||
// } | ||
// statusUpdateCallback(text); | ||
// }, | ||
totalDependencies: 0, | ||
noExitRuntime: 1 | ||
}; | ||
// Module.setStatus("Loading Ghostscript..."); | ||
if (!self.Module) { | ||
self.Module = Module; | ||
loadScript(); | ||
} else { | ||
self.Module["calledRun"] = false; | ||
self.Module["postRun"] = Module.postRun; | ||
self.Module["preRun"] = Module.preRun; | ||
self.Module.callMain(); | ||
} | ||
}; | ||
xhr.send(); | ||
} | ||
|
||
|
||
self.addEventListener('message', function({data:e}) { | ||
console.log("message", e) | ||
// e.data contains the message sent to the worker. | ||
if (e.target !== 'wasm'){ | ||
return; | ||
} | ||
console.log('Message received from main script', e.data); | ||
_GSPS2PDF(e.data, ({pdfDataURL}) => self.postMessage(pdfDataURL)) | ||
}); | ||
|
||
console.log("Worker ready") |
Oops, something went wrong.