mirror of
https://github.com/24eme/signaturepdf.git
synced 2023-08-25 09:33:08 +02:00
Start of an interface to rearrange the pages of a pdf
This commit is contained in:
parent
30aa95f6ce
commit
afdcbf952c
40
app.php
40
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;
|
BIN
public/favicon-organization.ico
Normal file
BIN
public/favicon-organization.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
163
public/js/organization.js
Normal file
163
public/js/organization.js
Normal file
@ -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', '<div class="position-relative mt-1 ms-1 me-1 d-inline-block" id="canvas-container-' + pageIndex +'"><canvas id="canvas-pdf-'+pageIndex+'" class="shadow-sm canvas-pdf"></canvas><div class="position-absolute text-center" style="bottom: 7px; width: 100%; background: rgba(0,0,0,0.2);"><input form="form_pdf" class="form-check-input checkbox-page" type="checkbox" checked="checked" value="'+page.pageNumber+'" name="pages[]" /></div></div>');
|
||||
|
||||
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 <?php echo round($maxSize / 1024 / 1024) ?> 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();
|
||||
})
|
||||
})();
|
58
templates/organization.html.php
Normal file
58
templates/organization.html.php
Normal file
@ -0,0 +1,58 @@
|
||||
<!doctype html>
|
||||
<html lang="fr_FR">
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="/vendor/bootstrap.min.css?5.1.1" rel="stylesheet">
|
||||
<link href="/vendor/bootstrap-icons.css?1.5.0" rel="stylesheet">
|
||||
<link href="/css/app.css" rel="stylesheet">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon-organization.ico">
|
||||
|
||||
<title>Organiser un PDF</title>
|
||||
</head>
|
||||
<body class="bg-light">
|
||||
<div id="page-upload">
|
||||
<div class="px-4 py-5 my-5 text-center">
|
||||
<h1 class="display-5 fw-bold"><i class="bi bi-ui-checks-grid"></i> Organiser un PDF</h1>
|
||||
<div class="col-lg-3 mx-auto">
|
||||
<div class="col-12">
|
||||
<label for="input_pdf_upload" class="form-label">Choisir un PDF</label>
|
||||
<input id="input_pdf_upload" class="form-control form-control-lg" type="file" accept=".pdf,application/pdf">
|
||||
<p class="mt-1 opacity-50"><small class="text-muted">Le PDF ne doit pas dépasser <?php echo round($maxSize / 1024 / 1024) ?> Mo</small></p>
|
||||
<a class="btn btn-sm btn-link opacity-75" href="/organization#https://raw.githubusercontent.com/24eme/signaturepdf/master/tests/files/document.pdf">Tester avec un PDF de démo</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="text-center text-muted mb-2 fixed-bottom">
|
||||
<small>Logiciel libre sous license AGPL-3.0 : <a href="https://github.com/24eme/signaturepdf">voir le code source</a></small>
|
||||
</footer>
|
||||
</div>
|
||||
<div id="page-organization" class="d-none">
|
||||
<div id="container-pages" class="col-12 pt-1 vh-100" style="padding-bottom: 60px;">
|
||||
</div>
|
||||
<div class="position-fixed bottom-0 start-0 bg-white w-100 p-2 shadow-lg">
|
||||
<form id="form_pdf" action="/organize" method="post" enctype="multipart/form-data">
|
||||
<input id="input_pdf" name="pdf" type="file" class="d-none" />
|
||||
<button class="btn btn-primary float-end" type="submit" id="save"><i class="bi bi-download"></i> Télécharger le PDF</button>
|
||||
<div class="form-check pt-2 ps-5">
|
||||
<input class="form-check-input" checked="checked" type="checkbox" id="checkbox_all_pages">
|
||||
<label class="form-check-label" for="checkbox_all_pages">Sélectionner toutes les pages</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<span id="is_mobile" class="d-md-none"></span>
|
||||
|
||||
<script src="/vendor/bootstrap.min.js?5.1.3"></script>
|
||||
<script src="/vendor/pdf.js?legacy"></script>
|
||||
<script src="/vendor/fabric.min.js?4.6.0"></script>
|
||||
<script src="/vendor/signature_pad.umd.min.js?3.0.0-beta.3"></script>
|
||||
<script src="/vendor/opentype.min.js?1.3.3"></script>
|
||||
<script>
|
||||
var maxSize = <?php echo $maxSize ?>;
|
||||
</script>
|
||||
<script src="/js/organization.js?202203261059"></script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user