port to new django, AI automated
This commit is contained in:
@@ -1,95 +1,157 @@
|
||||
# ----------------------------- Pdf Views ------------------------------------------
|
||||
|
||||
from django.http import HttpResponse
|
||||
from scoremanager.models import Piece
|
||||
|
||||
from reportlab.platypus import BaseDocTemplate, Frame, PageTemplate, Flowable, Table, PageBreak, NextPageTemplate
|
||||
from reportlab.lib import pagesizes, units, utils
|
||||
from reportlab.lib.colors import Color
|
||||
"""
|
||||
PDF generation views for the score manager application.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
class RepertoireDocTemplate( BaseDocTemplate ):
|
||||
def __init__(self, *args, **kwargs ):
|
||||
BaseDocTemplate.__init__(self,*args, **kwargs)
|
||||
from django.http import HttpResponse
|
||||
from reportlab.lib import pagesizes, units, utils
|
||||
from reportlab.lib.colors import Color
|
||||
from reportlab.platypus import (
|
||||
BaseDocTemplate,
|
||||
Flowable,
|
||||
Frame,
|
||||
NextPageTemplate,
|
||||
PageBreak,
|
||||
PageTemplate,
|
||||
Table,
|
||||
)
|
||||
|
||||
self.pagesize = kwargs['pagesize']
|
||||
from .models import Piece
|
||||
|
||||
leftBorder = 1 * units.cm
|
||||
rightBorder = 1 * units.cm
|
||||
topBorder = 3 * units.cm
|
||||
bottomBorder = 1 * units.cm
|
||||
|
||||
frameWidth = (self.pagesize[0] - rightBorder - leftBorder ) /2
|
||||
frameHeight = (self.pagesize[1] - topBorder - bottomBorder )
|
||||
class RepertoireDocTemplate(BaseDocTemplate):
|
||||
"""Document template for repertoire PDFs with header and two-column layout."""
|
||||
|
||||
leftColumn = Frame( leftBorder, bottomBorder, frameWidth, frameHeight, showBoundary=0 )
|
||||
rightColumn = Frame( leftBorder+frameWidth , bottomBorder, frameWidth, frameHeight, showBoundary=0 )
|
||||
def __init__(self, *args, **kwargs):
|
||||
BaseDocTemplate.__init__(self, *args, **kwargs)
|
||||
|
||||
tocTemplate = PageTemplate( id='TOC', frames=[leftColumn,rightColumn], onPage=RepertoireDocTemplate._drawHeader )
|
||||
fullPageTemplate = PageTemplate( id='FullPage', frames = [ Frame(0,0, self.pagesize[0], self.pagesize[1] ) ] )
|
||||
self.pagesize = kwargs["pagesize"]
|
||||
|
||||
self.addPageTemplates( [ tocTemplate, fullPageTemplate ] )
|
||||
left_border = 1 * units.cm
|
||||
right_border = 1 * units.cm
|
||||
top_border = 3 * units.cm
|
||||
bottom_border = 1 * units.cm
|
||||
|
||||
frame_width = (self.pagesize[0] - right_border - left_border) / 2
|
||||
frame_height = self.pagesize[1] - top_border - bottom_border
|
||||
|
||||
left_column = Frame(
|
||||
left_border, bottom_border, frame_width, frame_height, showBoundary=0
|
||||
)
|
||||
right_column = Frame(
|
||||
left_border + frame_width,
|
||||
bottom_border,
|
||||
frame_width,
|
||||
frame_height,
|
||||
showBoundary=0,
|
||||
)
|
||||
|
||||
toc_template = PageTemplate(
|
||||
id="TOC",
|
||||
frames=[left_column, right_column],
|
||||
onPage=RepertoireDocTemplate._draw_header,
|
||||
)
|
||||
full_page_template = PageTemplate(
|
||||
id="FullPage",
|
||||
frames=[Frame(0, 0, self.pagesize[0], self.pagesize[1])],
|
||||
)
|
||||
|
||||
self.addPageTemplates([toc_template, full_page_template])
|
||||
|
||||
@staticmethod
|
||||
def _drawHeader(canvas,document):
|
||||
def _draw_header(canvas, document):
|
||||
"""Draw the header with logo and gradient background."""
|
||||
current_path = os.path.dirname(os.path.realpath(__file__))
|
||||
logo_image_file = current_path + "/static/pdfImg/logo.png"
|
||||
bg_image_file = current_path + "/static/pdfImg/body_bg.jpg"
|
||||
|
||||
currentPath = os.path.dirname(os.path.realpath(__file__))
|
||||
logoImageFile = currentPath + "/static/pdfImg/logo.png"
|
||||
bgImageFile = currentPath + "/static/pdfImg/body_bg.jpg"
|
||||
|
||||
#Gradient
|
||||
titleAreaHeight = 2 * units.cm
|
||||
gradientColors = [ Color(0.1,0.1,0.1), Color(0.2,0.2,0.2)]
|
||||
# Gradient
|
||||
title_area_height = 2 * units.cm
|
||||
gradient_colors = [Color(0.1, 0.1, 0.1), Color(0.2, 0.2, 0.2)]
|
||||
ps = document.pagesize
|
||||
gradientStart = ( ps[0]/2, ps[1] )
|
||||
gradientEnd = ( ps[0]/2, ps[1] - titleAreaHeight)
|
||||
canvas.linearGradient( gradientStart[0],gradientStart[1], gradientEnd[0], gradientEnd[1],gradientColors, extend=False )
|
||||
titleAreaMidY = 0.5 * ( gradientStart[1] + gradientEnd[1] )
|
||||
gradient_start = (ps[0] / 2, ps[1])
|
||||
gradient_end = (ps[0] / 2, ps[1] - title_area_height)
|
||||
canvas.linearGradient(
|
||||
gradient_start[0],
|
||||
gradient_start[1],
|
||||
gradient_end[0],
|
||||
gradient_end[1],
|
||||
gradient_colors,
|
||||
extend=False,
|
||||
)
|
||||
title_area_mid_y = 0.5 * (gradient_start[1] + gradient_end[1])
|
||||
|
||||
# Draw Logo
|
||||
logoImg = utils.ImageReader( logoImageFile )
|
||||
logoSize = logoImg.getSize()
|
||||
logoFraction = 0.6
|
||||
logoSize = (logoFraction * logoSize[0], logoFraction*logoSize[1])
|
||||
logoPosition = ( ps[0] - 5*units.cm, titleAreaMidY - 0.5*logoSize[1] )
|
||||
canvas.drawImage( logoImg, logoPosition[0], logoPosition[1], width=logoSize[0], height=logoSize[1], mask=Color(0,0,0) )
|
||||
if os.path.exists(logo_image_file):
|
||||
logo_img = utils.ImageReader(logo_image_file)
|
||||
logo_size = logo_img.getSize()
|
||||
logo_fraction = 0.6
|
||||
logo_size = (logo_fraction * logo_size[0], logo_fraction * logo_size[1])
|
||||
logo_position = (
|
||||
ps[0] - 5 * units.cm,
|
||||
title_area_mid_y - 0.5 * logo_size[1],
|
||||
)
|
||||
canvas.drawImage(
|
||||
logo_img,
|
||||
logo_position[0],
|
||||
logo_position[1],
|
||||
width=logo_size[0],
|
||||
height=logo_size[1],
|
||||
mask=Color(0, 0, 0),
|
||||
)
|
||||
|
||||
# Draw Title
|
||||
text = canvas.beginText()
|
||||
text.setTextOrigin( 1 * units.cm, titleAreaMidY-25/2 )
|
||||
text.setFillColorRGB( 0.95,0.95,0.95 )
|
||||
text.setFont( 'Helvetica', 25 )
|
||||
text.textLine( "Inhaltsverzeichnis" )
|
||||
canvas.drawText( text )
|
||||
text.setTextOrigin(1 * units.cm, title_area_mid_y - 25 / 2)
|
||||
text.setFillColorRGB(0.95, 0.95, 0.95)
|
||||
text.setFont("Helvetica", 25)
|
||||
text.textLine("Inhaltsverzeichnis")
|
||||
canvas.drawText(text)
|
||||
|
||||
# Draw Background
|
||||
bgImage = utils.ImageReader( bgImageFile )
|
||||
bgImageSize = bgImage.getSize()
|
||||
curPos = [0, gradientEnd[1]-bgImageSize[1] ]
|
||||
while curPos[1] > -bgImageSize[1]:
|
||||
curPos[0] = 0
|
||||
while curPos[0] < ps[0]:
|
||||
canvas.drawImage( bgImage, curPos[0], curPos[1] )
|
||||
curPos[0] += bgImageSize[0]
|
||||
|
||||
curPos[1] -= bgImageSize[1]
|
||||
if os.path.exists(bg_image_file):
|
||||
bg_image = utils.ImageReader(bg_image_file)
|
||||
bg_image_size = bg_image.getSize()
|
||||
cur_pos = [0, gradient_end[1] - bg_image_size[1]]
|
||||
while cur_pos[1] > -bg_image_size[1]:
|
||||
cur_pos[0] = 0
|
||||
while cur_pos[0] < ps[0]:
|
||||
canvas.drawImage(bg_image, cur_pos[0], cur_pos[1])
|
||||
cur_pos[0] += bg_image_size[0]
|
||||
cur_pos[1] -= bg_image_size[1]
|
||||
|
||||
|
||||
class PdfImage(Flowable):
|
||||
"""PdfImage wraps the first page from a PDF file as a Flowable
|
||||
which can be included into a ReportLab Platypus document.
|
||||
Based on the vectorpdf extension in rst2pdf (http://code.google.com/p/rst2pdf/)"""
|
||||
"""
|
||||
PdfImage wraps the first page from a PDF file as a Flowable
|
||||
which can be included into a ReportLab Platypus document.
|
||||
|
||||
def __init__(self, filename_or_object, page=0, width=None, height=None, kind='direct'):
|
||||
from pdfrw import PdfReader
|
||||
from pdfrw.buildxobj import pagexobj
|
||||
Based on the vectorpdf extension in rst2pdf (http://code.google.com/p/rst2pdf/)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, filename_or_object, page=0, width=None, height=None, kind="direct"
|
||||
):
|
||||
Flowable.__init__(self)
|
||||
|
||||
try:
|
||||
from pdfrw import PdfReader
|
||||
from pdfrw.buildxobj import pagexobj
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"pdfrw is required for PDF embedding. Install it with: pip install pdfrw"
|
||||
)
|
||||
|
||||
# If using StringIO buffer, set pointer to beginning
|
||||
if hasattr(filename_or_object, 'read'):
|
||||
if hasattr(filename_or_object, "read"):
|
||||
filename_or_object.seek(0)
|
||||
page = PdfReader(filename_or_object, decompress=False).pages[page]
|
||||
self.xobj = pagexobj(page)
|
||||
|
||||
pdf_pages = PdfReader(filename_or_object, decompress=False).pages
|
||||
if page >= len(pdf_pages):
|
||||
page = 0
|
||||
|
||||
self.xobj = pagexobj(pdf_pages[page])
|
||||
self.imageWidth = width
|
||||
self.imageHeight = height
|
||||
x1, y1, x2, y2 = self.xobj.BBox
|
||||
@@ -99,36 +161,42 @@ class PdfImage(Flowable):
|
||||
self.imageWidth = self._w
|
||||
if not self.imageHeight:
|
||||
self.imageHeight = self._h
|
||||
self.__ratio = float(self.imageWidth)/self.imageHeight
|
||||
if kind in ['direct','absolute'] or width==None or height==None:
|
||||
|
||||
self.__ratio = float(self.imageWidth) / self.imageHeight
|
||||
|
||||
if kind in ["direct", "absolute"] or width is None or height is None:
|
||||
self.drawWidth = width or self.imageWidth
|
||||
self.drawHeight = height or self.imageHeight
|
||||
elif kind in ['bound','proportional']:
|
||||
factor = min(float(width)/self._w,float(height)/self._h)
|
||||
self.drawWidth = self._w*factor
|
||||
self.drawHeight = self._h*factor
|
||||
elif kind in ["bound", "proportional"]:
|
||||
factor = min(float(width) / self._w, float(height) / self._h)
|
||||
self.drawWidth = self._w * factor
|
||||
self.drawHeight = self._h * factor
|
||||
|
||||
def wrap(self, aW, aH):
|
||||
return self.drawWidth, self.drawHeight
|
||||
|
||||
def drawOn(self, canv, x, y, _sW=0):
|
||||
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
|
||||
from pdfrw.toreportlab import makerl
|
||||
from reportlab.lib.enums import TA_CENTER, TA_LEFT, TA_RIGHT
|
||||
|
||||
if _sW > 0 and hasattr(self, 'hAlign'):
|
||||
try:
|
||||
from pdfrw.toreportlab import makerl
|
||||
except ImportError:
|
||||
raise ImportError("pdfrw is required for PDF embedding.")
|
||||
|
||||
if _sW > 0 and hasattr(self, "hAlign"):
|
||||
a = self.hAlign
|
||||
if a in ('CENTER', 'CENTRE', TA_CENTER):
|
||||
x += 0.5*_sW
|
||||
elif a in ('RIGHT', TA_RIGHT):
|
||||
if a in ("CENTER", "CENTRE", TA_CENTER):
|
||||
x += 0.5 * _sW
|
||||
elif a in ("RIGHT", TA_RIGHT):
|
||||
x += _sW
|
||||
elif a not in ('LEFT', TA_LEFT):
|
||||
elif a not in ("LEFT", TA_LEFT):
|
||||
raise ValueError("Bad hAlign value " + str(a))
|
||||
|
||||
xobj = self.xobj
|
||||
xobj_name = makerl(canv._doc, xobj)
|
||||
|
||||
xscale = self.drawWidth/self._w
|
||||
yscale = self.drawHeight/self._h
|
||||
xscale = self.drawWidth / self._w
|
||||
yscale = self.drawHeight / self._h
|
||||
|
||||
x -= xobj.BBox[0] * xscale
|
||||
y -= xobj.BBox[1] * yscale
|
||||
@@ -141,81 +209,97 @@ class PdfImage(Flowable):
|
||||
|
||||
|
||||
class Bookmark(Flowable):
|
||||
""" Utility class to display PDF bookmark. """
|
||||
"""Utility class to display PDF bookmark."""
|
||||
|
||||
def __init__(self, title, key):
|
||||
self.title = title
|
||||
self.key = key
|
||||
Flowable.__init__(self)
|
||||
|
||||
def wrap(self, availWidth, availHeight):
|
||||
""" Doesn't take up any space. """
|
||||
"""Doesn't take up any space."""
|
||||
return (0, 0)
|
||||
|
||||
def draw(self):
|
||||
# set the bookmark outline to show when the file's opened
|
||||
self.canv.showOutline()
|
||||
# step 1: put a bookmark on the
|
||||
# step 1: put a bookmark on the page
|
||||
self.canv.bookmarkPage(self.key)
|
||||
# step 2: put an entry in the bookmark outline
|
||||
self.canv.addOutlineEntry(self.title, self.key, 0, 0)
|
||||
|
||||
|
||||
def repertoire_pdf( request ):
|
||||
response = HttpResponse(content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'attachment; filename="Repertoire.pdf"'
|
||||
def repertoire_pdf(request):
|
||||
"""Generate a PDF containing the full repertoire with scores."""
|
||||
response = HttpResponse(content_type="application/pdf")
|
||||
response["Content-Disposition"] = 'attachment; filename="Repertoire.pdf"'
|
||||
|
||||
# Create the PDF object, using the response object as its "file.
|
||||
# Create the PDF object, using the response object as its "file"
|
||||
doc = RepertoireDocTemplate(response, pagesize=pagesizes.A4)
|
||||
elements = []
|
||||
|
||||
elements.append( Bookmark("Inhaltsverzeichnis", "Contents") )
|
||||
#TOC
|
||||
elements.append(Bookmark("Inhaltsverzeichnis", "Contents"))
|
||||
|
||||
# Table of Contents
|
||||
data = []
|
||||
for piece in Piece.getRepertoire():
|
||||
data.append( [ "%d %s" % ( piece.repertoire_nr, piece.title ) ] )
|
||||
table = Table(data)
|
||||
table.hAlign = "LEFT"
|
||||
elements.append(table)
|
||||
for piece in Piece.get_repertoire():
|
||||
data.append([f"{piece.repertoire_nr} {piece.title}"])
|
||||
|
||||
if data:
|
||||
table = Table(data)
|
||||
table.hAlign = "LEFT"
|
||||
elements.append(table)
|
||||
|
||||
elements.append( NextPageTemplate('FullPage') )
|
||||
elements.append(NextPageTemplate("FullPage"))
|
||||
elements.append(PageBreak())
|
||||
pagesize=pagesizes.A4
|
||||
for piece in Piece.getRepertoire():
|
||||
score = piece.get_score_for_user( request.user )
|
||||
|
||||
pagesize = pagesizes.A4
|
||||
for piece in Piece.get_repertoire():
|
||||
score = piece.get_score_for_user(request.user)
|
||||
if score is None:
|
||||
continue
|
||||
|
||||
filename = score.file
|
||||
bookmarkTitle = "%02d %s - %s " % ( piece.repertoire_nr, piece.title, score.score_type )
|
||||
elements.append( Bookmark( bookmarkTitle, str(piece.id) ) )
|
||||
|
||||
#TODO support also multiple pages!!
|
||||
image_flowable = PdfImage( filename, 0, width=pagesize[0]*0.98, height=pagesize[1] *0.98 )
|
||||
image_flowable.hAlign = "CENTER"
|
||||
elements.append( image_flowable )
|
||||
bookmark_title = (
|
||||
f"{piece.repertoire_nr:02d} {piece.title} - {score.score_type}"
|
||||
)
|
||||
elements.append(Bookmark(bookmark_title, str(piece.id)))
|
||||
|
||||
try:
|
||||
# TODO: support multiple pages
|
||||
image_flowable = PdfImage(
|
||||
filename, 0, width=pagesize[0] * 0.98, height=pagesize[1] * 0.98
|
||||
)
|
||||
image_flowable.hAlign = "CENTER"
|
||||
elements.append(image_flowable)
|
||||
except Exception as e:
|
||||
# Skip this score if PDF processing fails
|
||||
print(f"Error processing PDF for {piece.title}: {e}")
|
||||
continue
|
||||
|
||||
doc.build(elements)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def repertoire_toc(request):
|
||||
"""Generate a PDF containing only the table of contents."""
|
||||
response = HttpResponse(content_type="application/pdf")
|
||||
response["Content-Disposition"] = 'attachment; filename="Inhaltsverzeichnis.pdf"'
|
||||
|
||||
def repertoire_toc( request ):
|
||||
response = HttpResponse(content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'attachment; filename="Inhaltsverzeichnis.pdf"'
|
||||
|
||||
# Create the PDF object, using the response object as its "file.
|
||||
doc = RepertoireDocTemplate(response,pagesize=pagesizes.A4 )
|
||||
# Create the PDF object, using the response object as its "file"
|
||||
doc = RepertoireDocTemplate(response, pagesize=pagesizes.A4)
|
||||
elements = []
|
||||
|
||||
data = []
|
||||
for piece in Piece.getRepertoire():
|
||||
data.append( [ "%d %s" % ( piece.repertoire_nr, piece.title ) ] )
|
||||
for piece in Piece.get_repertoire():
|
||||
data.append([f"{piece.repertoire_nr} {piece.title}"])
|
||||
|
||||
table = Table(data)
|
||||
table.hAlign = "LEFT"
|
||||
elements.append(table)
|
||||
if data:
|
||||
table = Table(data)
|
||||
table.hAlign = "LEFT"
|
||||
elements.append(table)
|
||||
|
||||
doc.build(elements)
|
||||
|
||||
return response
|
||||
|
||||
|
||||
Reference in New Issue
Block a user