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 0000000..113d1c0 Binary files /dev/null and b/public/favicon-organization.ico differ diff --git a/public/js/organization.js b/public/js/organization.js new file mode 100644 index 0000000..8e0c978 --- /dev/null +++ b/public/js/organization.js @@ -0,0 +1,163 @@ +var pdfRenderTasks = []; +var pdfPages = []; +var resizeTimeout; +var currentScale = 1.5; +var windowWidth = window.innerWidth; + +var loadPDF = async function(pdfBlob, filename) { + var pdfjsLib = window['pdfjs-dist/build/pdf']; + pdfjsLib.GlobalWorkerOptions.workerSrc = '/vendor/pdf.worker.js?legacy'; + let url = await URL.createObjectURL(pdfBlob); + + let dataTransfer = new DataTransfer(); + dataTransfer.items.add(new File([pdfBlob], filename, { + type: 'application/pdf' + })); + document.getElementById('input_pdf').files = dataTransfer.files; + + var loadingTask = pdfjsLib.getDocument(url); + loadingTask.promise.then(function(pdf) { + for(var pageNumber = 1; pageNumber <= pdf.numPages; pageNumber++ ) { + pdf.getPage(pageNumber).then(function(page) { + var scale = 0.5; + var viewport = page.getViewport({scale: scale}); + if(viewport.width > 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