From afdcbf952c63c1fe04a00a6b5f5e27dfa87c4a13 Mon Sep 17 00:00:00 2001 From: Vincent LAURENT Date: Mon, 28 Mar 2022 00:56:00 +0200 Subject: [PATCH] Start of an interface to rearrange the pages of a pdf --- app.php | 40 ++++++++ public/favicon-organization.ico | Bin 0 -> 15406 bytes public/js/organization.js | 163 ++++++++++++++++++++++++++++++++ templates/organization.html.php | 58 ++++++++++++ 4 files changed, 261 insertions(+) create mode 100644 public/favicon-organization.ico create mode 100644 public/js/organization.js create mode 100644 templates/organization.html.php diff --git a/app.php b/app.php index 2da0b7e..be0083b 100644 --- a/app.php +++ b/app.php @@ -52,6 +52,13 @@ $f3->route('GET /signature', echo View::instance()->render('signature.html.php'); } ); +$f3->route('GET /organization', + function($f3) { + $f3->set('maxSize', min(array(convertPHPSizeToBytes(ini_get('post_max_size')), convertPHPSizeToBytes(ini_get('upload_max_filesize'))))); + + echo View::instance()->render('organization.html.php'); + } +); $f3->route('POST /image2svg', function($f3) { $files = Web::instance()->receive(function($file,$formFieldName){ @@ -143,4 +150,37 @@ $f3->route('POST /sign', } ); +$f3->route('POST /organize', + function($f3) { + $filename = null; + $tmpfile = tempnam($f3->get('UPLOADS'), 'pdfsignature_organize'); + unlink($tmpfile); + + $files = Web::instance()->receive(function($file,$formFieldName){ + if($formFieldName == "pdf" && strpos(Web::instance()->mime($file['tmp_name'], true), 'application/pdf') !== 0) { + $f3->error(403); + } + return true; + }, false, function($fileBaseName, $formFieldName) use ($f3, $tmpfile, &$filename, &$svgFiles) { + if($formFieldName == "pdf") { + $filename = str_replace(".pdf", "_organise.pdf", $fileBaseName); + return basename($tmpfile).".pdf"; + } + }); + + if(!is_file($tmpfile.".pdf")) { + $f3->error(403); + } + + shell_exec(sprintf("pdftk %s cat %s output %s", $tmpfile.".pdf", implode(" ", $f3->get('POST.pages')), $tmpfile.'_organise.pdf')); + + Web::instance()->send($tmpfile."_organise.pdf", null, 0, TRUE, $filename); + + if($f3->get('DEBUG')) { + return; + } + array_map('unlink', glob($tmpfile."*")); + } +); + return $f3; \ No newline at end of file diff --git a/public/favicon-organization.ico b/public/favicon-organization.ico new file mode 100644 index 0000000000000000000000000000000000000000..113d1c0068d0f3305b36279c430d17f722edadb6 GIT binary patch literal 15406 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 document.getElementById('container-pages').clientWidth - 40) { + viewport = page.getViewport({scale: 1}); + scale = (document.getElementById('container-pages').clientWidth - 40) / viewport.width; + viewport = page.getViewport({ scale: scale }); + } + + currentScale = scale; + + var pageIndex = page.pageNumber - 1; + + document.getElementById('container-pages').insertAdjacentHTML('beforeend', '
'); + + let canvasContainer = document.getElementById('canvas-container-' + pageIndex); + let canvasCheckbox = canvasContainer.querySelector('input[type=checkbox]'); + canvasCheckbox.addEventListener('click', function(e) { + e.stopPropagation(); + }) + canvasContainer.addEventListener('click', function(e) { + this.querySelector('input[type=checkbox]').checked = !this.querySelector('input[type=checkbox]').checked; + document.querySelector('#checkbox_all_pages').checked = (document.querySelectorAll('.checkbox-page:checked').length == document.querySelectorAll('.checkbox-page').length); + }) + var canvasPDF = document.getElementById('canvas-pdf-' + pageIndex); + + // Prepare canvas using PDF page dimensions + var context = canvasPDF.getContext('2d'); + canvasPDF.height = viewport.height; + canvasPDF.width = viewport.width; + + var renderContext = { + canvasContext: context, + viewport: viewport, + enhanceTextSelection: true + }; + var renderTask = page.render(renderContext); + pdfRenderTasks.push(renderTask); + pdfPages.push(page); + }); + } + }, function (reason) { + console.error(reason); + }); +}; + +var is_mobile = function() { + return !(window.getComputedStyle(document.getElementById('is_mobile')).display === "none"); +}; + +var createEventsListener = function() { + document.querySelector('#checkbox_all_pages').addEventListener('change', function() { + let checkboxAll = this; + document.querySelectorAll('.checkbox-page').forEach(function(checkbox) { + checkbox.checked = checkboxAll.checked; + }); + }) +} + +async function getPDFBlobFromCache(cacheUrl) { + const cache = await caches.open('pdf'); + let responsePdf = await cache.match(cacheUrl); + + if(!responsePdf) { + return null; + } + + let pdfBlob = await responsePdf.blob(); + + return pdfBlob; +} + +async function uploadFromUrl(url) { + history.replaceState({}, '', '/organization'); + var response = await fetch(url); + if(response.status != 200) { + return; + } + var pdfBlob = await response.blob(); + + if(pdfBlob.type != 'application/pdf' && pdfBlob.type != 'application/octet-stream') { + return; + } + let dataTransfer = new DataTransfer(); + let filename = url.replace(/^.*\//, ''); + dataTransfer.items.add(new File([pdfBlob], filename, { + type: 'application/pdf' + })); + document.getElementById('input_pdf_upload').files = dataTransfer.files; + document.getElementById('input_pdf_upload').dispatchEvent(new Event("change")); +} + +var pageUpload = async function() { + document.getElementById('input_pdf_upload').value = ''; + document.getElementById('page-upload').classList.remove('d-none'); + document.getElementById('page-organization').classList.add('d-none'); + document.getElementById('input_pdf_upload').focus(); + const cache = await caches.open('pdf'); + document.getElementById('input_pdf_upload').addEventListener('change', async function(event) { + if(document.getElementById('input_pdf_upload').files[0].size > maxSize) { + + alert("Le PDF ne doit pas dépasser Mo"); + document.getElementById('input_pdf_upload').value = ""; + return; + } + let filename = document.getElementById('input_pdf_upload').files[0].name; + let response = new Response(document.getElementById('input_pdf_upload').files[0], { "status" : 200, "statusText" : "OK" }); + let urlPdf = '/pdf/'+filename; + await cache.put(urlPdf, response); + history.pushState({}, '', '/organization#'+filename); + pageOrganization(urlPdf) + }); +} + +var pageOrganization = async function(url) { + let filename = url.replace('/pdf/', ''); + document.title = filename + ' - ' + document.title; + document.getElementById('page-upload').classList.add('d-none'); + document.getElementById('page-organization').classList.remove('d-none'); + + let pdfBlob = await getPDFBlobFromCache(url); + if(!pdfBlob) { + document.location = '/organization'; + return; + } + createEventsListener(); + loadPDF(pdfBlob, filename); +}; + +(function () { + if(window.location.hash && window.location.hash.match(/^\#http/)) { + let hashUrl = window.location.hash.replace(/^\#/, ''); + pageUpload(); + uploadFromUrl(hashUrl); + } else if(window.location.hash) { + pageOrganization('/pdf/'+window.location.hash.replace(/^\#/, '')); + } else { + pageUpload(); + } + window.addEventListener('hashchange', function() { + window.location.reload(); + }) +})(); \ No newline at end of file diff --git a/templates/organization.html.php b/templates/organization.html.php new file mode 100644 index 0000000..46b8447 --- /dev/null +++ b/templates/organization.html.php @@ -0,0 +1,58 @@ + + + + + + + + + + + + + Organiser un PDF + + +
+
+

Organiser un PDF

+
+
+ + +

Le PDF ne doit pas dépasser Mo

+ Tester avec un PDF de démo +
+
+
+ +
+
+
+
+
+
+ + +
+ + +
+
+
+
+ + + + + + + + + + + \ No newline at end of file