mirror of
https://github.com/pretix/pretix.git
synced 2026-05-06 15:24:02 +00:00
PDF editor: Add multi-page support
This commit is contained in:
committed by
Raphael Michel
parent
b05fa89010
commit
ebf0320c2c
@@ -754,20 +754,30 @@ class Renderer:
|
||||
p.drawOn(canvas, 0, -h - ad[1])
|
||||
canvas.restoreState()
|
||||
|
||||
def draw_page(self, canvas: Canvas, order: Order, op: OrderPosition, show_page=True):
|
||||
for o in self.layout:
|
||||
if o['type'] == "barcodearea":
|
||||
self._draw_barcodearea(canvas, op, o)
|
||||
elif o['type'] == "imagearea":
|
||||
self._draw_imagearea(canvas, op, order, o)
|
||||
elif o['type'] == "textarea":
|
||||
self._draw_textarea(canvas, op, order, o)
|
||||
elif o['type'] == "poweredby":
|
||||
self._draw_poweredby(canvas, op, o)
|
||||
if self.bg_pdf:
|
||||
canvas.setPageSize((self.bg_pdf.getPage(0).mediaBox[2], self.bg_pdf.getPage(0).mediaBox[3]))
|
||||
if show_page:
|
||||
canvas.showPage()
|
||||
def draw_page(self, canvas: Canvas, order: Order, op: OrderPosition, show_page=True, only_page=None):
|
||||
page_count = self.bg_pdf.getNumPages()
|
||||
|
||||
if not only_page and not show_page:
|
||||
raise ValueError("only_page=None and show_page=False cannot be combined")
|
||||
|
||||
for page in range(page_count):
|
||||
if only_page and only_page != page + 1:
|
||||
continue
|
||||
for o in self.layout:
|
||||
if o.get('page', 1) != page + 1:
|
||||
continue
|
||||
if o['type'] == "barcodearea":
|
||||
self._draw_barcodearea(canvas, op, o)
|
||||
elif o['type'] == "imagearea":
|
||||
self._draw_imagearea(canvas, op, order, o)
|
||||
elif o['type'] == "textarea":
|
||||
self._draw_textarea(canvas, op, order, o)
|
||||
elif o['type'] == "poweredby":
|
||||
self._draw_poweredby(canvas, op, o)
|
||||
if self.bg_pdf:
|
||||
canvas.setPageSize((self.bg_pdf.getPage(page).mediaBox[2], self.bg_pdf.getPage(page).mediaBox[3]))
|
||||
if show_page:
|
||||
canvas.showPage()
|
||||
|
||||
def render_background(self, buffer, title=_('Ticket')):
|
||||
if settings.PDFTK:
|
||||
@@ -780,7 +790,7 @@ class Renderer:
|
||||
subprocess.run([
|
||||
settings.PDFTK,
|
||||
os.path.join(d, 'front.pdf'),
|
||||
'background',
|
||||
'multibackground',
|
||||
os.path.join(d, 'back.pdf'),
|
||||
'output',
|
||||
os.path.join(d, 'out.pdf'),
|
||||
@@ -794,8 +804,8 @@ class Renderer:
|
||||
new_pdf = PdfFileReader(buffer)
|
||||
output = PdfFileWriter()
|
||||
|
||||
for page in new_pdf.pages:
|
||||
bg_page = copy.copy(self.bg_pdf.getPage(0))
|
||||
for i, page in enumerate(new_pdf.pages):
|
||||
bg_page = copy.copy(self.bg_pdf.getPage(i))
|
||||
bg_page.mergePage(page)
|
||||
output.addPage(bg_page)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</script>
|
||||
<div class="row">
|
||||
<div class="col-md-9">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel panel-default panel-pdf-editor">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right flip">
|
||||
<div class="btn-group">
|
||||
@@ -48,6 +48,8 @@
|
||||
{% trans "Editor" %}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<ul class="nav nav-pills" id="page_nav">
|
||||
</ul>
|
||||
<div id="editor-canvas-area">
|
||||
<canvas id="pdf-canvas"
|
||||
data-pdf-url="{{ pdf }}"
|
||||
|
||||
@@ -181,7 +181,7 @@ def render_pdf(event, positions, opt):
|
||||
offsety = opt['margins'][2] + (opt['rows'] - 1 - i // opt['cols']) * opt['offsets'][1]
|
||||
p.translate(offsetx, offsety)
|
||||
with language(op.order.locale, op.order.event.settings.region):
|
||||
r.draw_page(p, op.order, op, show_page=False)
|
||||
r.draw_page(p, op.order, op, show_page=False, only_page=1)
|
||||
p.translate(-offsetx, -offsety)
|
||||
|
||||
if opt['pagesize']:
|
||||
|
||||
@@ -102,11 +102,15 @@ var editor = {
|
||||
objects: [],
|
||||
history: [],
|
||||
clipboard: [],
|
||||
pdf: null,
|
||||
pdf_page: null,
|
||||
pdf_page_number: 1,
|
||||
pdf_page_count: 1,
|
||||
pdf_scale: 1,
|
||||
pdf_viewport: null,
|
||||
_history_pos: 0,
|
||||
_history_modification_in_progress: false,
|
||||
_other_page_objects: [],
|
||||
dirty: false,
|
||||
pdf_url: null,
|
||||
uploaded_file_id: null,
|
||||
@@ -130,7 +134,7 @@ var editor = {
|
||||
},
|
||||
|
||||
dump: function (objs) {
|
||||
var d = [];
|
||||
var d = !objs ? JSON.parse(JSON.stringify(editor._other_page_objects)) : [];
|
||||
objs = objs || editor.fabric.getObjects();
|
||||
|
||||
for (var i in objs) {
|
||||
@@ -149,6 +153,7 @@ var editor = {
|
||||
}
|
||||
d.push({
|
||||
type: "textarea",
|
||||
page: editor.pdf_page_number,
|
||||
locale: $("#pdf-info-locale").val(),
|
||||
left: editor._px2mm(left).toFixed(2),
|
||||
bottom: editor._px2mm(bottom).toFixed(2),
|
||||
@@ -168,6 +173,7 @@ var editor = {
|
||||
} else if (o.type === "imagearea") {
|
||||
d.push({
|
||||
type: "imagearea",
|
||||
page: editor.pdf_page_number,
|
||||
left: editor._px2mm(left).toFixed(2),
|
||||
bottom: editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - top).toFixed(2),
|
||||
height: editor._px2mm(o.height * o.scaleY).toFixed(2),
|
||||
@@ -177,6 +183,7 @@ var editor = {
|
||||
} else if (o.type === "barcodearea") {
|
||||
d.push({
|
||||
type: "barcodearea",
|
||||
page: editor.pdf_page_number,
|
||||
left: editor._px2mm(left).toFixed(2),
|
||||
bottom: editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - top).toFixed(2),
|
||||
size: editor._px2mm(o.height * o.scaleY).toFixed(2),
|
||||
@@ -186,6 +193,7 @@ var editor = {
|
||||
} else if (o.type === "poweredby") {
|
||||
d.push({
|
||||
type: "poweredby",
|
||||
page: editor.pdf_page_number,
|
||||
left: editor._px2mm(left).toFixed(2),
|
||||
bottom: editor._px2mm(editor.pdf_viewport.height - o.height * o.scaleY - top).toFixed(2),
|
||||
size: editor._px2mm(o.height * o.scaleY).toFixed(2),
|
||||
@@ -197,6 +205,11 @@ var editor = {
|
||||
},
|
||||
|
||||
_add_from_data: function (d) {
|
||||
var targetPage = d.page || 1;
|
||||
if (targetPage !== editor.pdf_page_number) {
|
||||
editor._other_page_objects.push(d);
|
||||
return
|
||||
}
|
||||
if (d.type === "barcodearea") {
|
||||
o = editor._add_qrcode();
|
||||
o.content = d.content;
|
||||
@@ -251,6 +264,7 @@ var editor = {
|
||||
|
||||
load: function(data) {
|
||||
editor.fabric.clear();
|
||||
editor._other_page_objects = [];
|
||||
for (var i in data) {
|
||||
var d = data[i], o;
|
||||
editor._add_from_data(d);
|
||||
@@ -268,6 +282,71 @@ var editor = {
|
||||
return $('#toolbox-content option[value='+key+']').attr('data-sample') || '';
|
||||
},
|
||||
|
||||
_load_page: function (page_number, dump) {
|
||||
var previous_dump = editor._fabric_loaded ? editor.dump() : [];
|
||||
|
||||
// Fetch the required page
|
||||
editor.pdf.getPage(page_number).then(function (page) {
|
||||
console.log('Page loaded');
|
||||
var canvas = document.getElementById('pdf-canvas');
|
||||
|
||||
var scale = editor.$cva.width() / page.getViewport(1.0).width;
|
||||
var viewport = page.getViewport(scale);
|
||||
|
||||
// Prepare canvas using PDF page dimensions
|
||||
var context = canvas.getContext('2d');
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
|
||||
editor.pdf_page = page;
|
||||
editor.pdf_scale = scale;
|
||||
editor.pdf_viewport = viewport;
|
||||
|
||||
// Render PDF page into canvas context
|
||||
var renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport
|
||||
};
|
||||
var renderTask = page.render(renderContext);
|
||||
renderTask.then(function () {
|
||||
editor.pdf_page_number = page_number
|
||||
editor._init_page_nav();
|
||||
|
||||
console.log('Page rendered');
|
||||
if (dump || !editor._fabric_loaded) {
|
||||
editor._init_fabric(dump);
|
||||
} else {
|
||||
editor.load(previous_dump);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
_init_page_nav: function () {
|
||||
if (editor.pdf_page_count === 1) {
|
||||
$("#page_nav").hide();
|
||||
} else {
|
||||
$("#page_nav").html("");
|
||||
for (i = 1; i <= editor.pdf_page_count; i++) {
|
||||
var $li = $("<li>").addClass("nav-item");
|
||||
var $a = $("<a>").text(i).attr("href", "#").attr("data-page", i).appendTo($li);
|
||||
if (i === editor.pdf_page_number) {
|
||||
$li.addClass("active")
|
||||
}
|
||||
$("#page_nav").append($li)
|
||||
$a.on("click", function (event) {
|
||||
console.log("switch to page", $(this).attr("data-page"));
|
||||
editor.fabric.deactivateAll();
|
||||
editor._load_page(parseInt($(this).attr("data-page")));
|
||||
event.preventDefault();
|
||||
return true;
|
||||
})
|
||||
}
|
||||
$("#page_nav").show();
|
||||
}
|
||||
},
|
||||
|
||||
_load_pdf: function (dump) {
|
||||
// TODO: Loading indicators
|
||||
var url = editor.pdf_url;
|
||||
@@ -279,36 +358,13 @@ var editor = {
|
||||
loadingTask.promise.then(function (pdf) {
|
||||
console.log('PDF loaded');
|
||||
|
||||
// Fetch the first page
|
||||
var pageNumber = 1;
|
||||
pdf.getPage(pageNumber).then(function (page) {
|
||||
console.log('Page loaded');
|
||||
var canvas = document.getElementById('pdf-canvas');
|
||||
|
||||
var scale = editor.$cva.width() / page.getViewport(1.0).width;
|
||||
var viewport = page.getViewport(scale);
|
||||
|
||||
// Prepare canvas using PDF page dimensions
|
||||
var context = canvas.getContext('2d');
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
canvas.height = viewport.height;
|
||||
canvas.width = viewport.width;
|
||||
|
||||
editor.pdf_page = page;
|
||||
editor.pdf_scale = scale;
|
||||
editor.pdf_viewport = viewport;
|
||||
|
||||
// Render PDF page into canvas context
|
||||
var renderContext = {
|
||||
canvasContext: context,
|
||||
viewport: viewport
|
||||
};
|
||||
var renderTask = page.render(renderContext);
|
||||
renderTask.then(function () {
|
||||
console.log('Page rendered');
|
||||
editor._init_fabric(dump);
|
||||
});
|
||||
});
|
||||
editor.pdf = pdf;
|
||||
editor.pdf_page_count = pdf.numPages;
|
||||
if (editor.pdf_page_count > 10) {
|
||||
alert('Please do not upload files with more than 10 pages for performance reasons.')
|
||||
}
|
||||
editor._init_page_nav();
|
||||
editor._load_page(1, dump);
|
||||
}, function (reason) {
|
||||
var msg = gettext('The PDF background file could not be loaded for the following reason:');
|
||||
editor._error(msg + ' ' + reason);
|
||||
@@ -664,6 +720,7 @@ var editor = {
|
||||
editor._history_modification_in_progress = true;
|
||||
var objs = [];
|
||||
for (var i in editor.clipboard) {
|
||||
editor.clipboard[i].page = editor.pdf_page_number;
|
||||
objs.push(editor._add_from_data(editor.clipboard[i]));
|
||||
}
|
||||
editor.fabric.discardActiveObject();
|
||||
|
||||
@@ -69,3 +69,10 @@ body {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.panel-pdf-editor .panel-body {
|
||||
padding: 0;
|
||||
}
|
||||
.panel-pdf-editor .panel-body .nav-pills {
|
||||
padding: 15px;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user