Skip to content

Commit

Permalink
Compression of PDF directly in the browser
Browse files Browse the repository at this point in the history
  • Loading branch information
TIT8 committed Sep 22, 2024
1 parent 1f4a82a commit d65f828
Show file tree
Hide file tree
Showing 12 changed files with 15,472 additions and 7 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public/*.css
public/*.svg
public/*.ico
public/*.png
public/site.webmanifest
public/site.webmanifest
public/lib/*
104 changes: 104 additions & 0 deletions layouts/shortcodes/contact.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,24 @@
<p id="my-form-status"></p>
</form>

<hr>
<h3 id="compress-your-pdf">Compress your PDF</h3>
<p>🇮🇹 &nbsp; 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>❗&nbsp;<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>🇬🇧 &nbsp; 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>❗&nbsp;<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">🥳 &nbsp;<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"
Expand All @@ -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);
}

Expand Down
106 changes: 105 additions & 1 deletion public/contact/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>🇮🇹 &nbsp; 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>❗&nbsp;<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>🇬🇧 &nbsp; 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>❗&nbsp;<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">🥳 &nbsp;<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"
Expand All @@ -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);
}

Expand Down
2 changes: 1 addition & 1 deletion public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ <h2>Electronics engineering at Polimi 📝</h2>
<hr>
</header>
<main>
<h2 id="hahahugoshortcode4s0hbhb"><p style="text-align: center;">Appunti 🇮🇹 &nbsp;-&nbsp; Notes 🇬🇧</p></h2>
<h2 id="hahahugoshortcode5s0hbhb"><p style="text-align: center;">Appunti 🇮🇹 &nbsp;-&nbsp; Notes 🇬🇧</p></h2>
<p><em>🇮🇹  Raccolta di materiali privi di copyright o ridistribuibili utili per studiare Ingegneria Elettronica al <a href="https://www.polimi.it/">Politecnico di Milano</a>, sia per la <strong>laurea triennale</strong> che per la <strong>laurea magistrale</strong></em></p>
<p><em>🇬🇧  Compilation of copyright-free or redistributable materials, beneficial for studying Electronics Engineering at <a href="https://www.polimi.it/en">Politecnico di Milano</a>, encompassing both <strong>bachelor&rsquo;s</strong> and <strong>master&rsquo;s degrees</strong>.</em></p>
<p> </p>
Expand Down
100 changes: 100 additions & 0 deletions static/lib/background-worker.js
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")
Loading

0 comments on commit d65f828

Please sign in to comment.