I want to add password protection for opening the pdf file from a Django project.
def pdf_view(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="members.pdf"'
elements = []
doc = SimpleDocTemplate(response, rightMargin=0.5 * cm, leftMargin=6.5 * cm, topMargin=0.3 * cm, bottomMargin=0)
rows = []
users = User.objects.all()
for user in users:
rows.append((
user.username,
user.email,
user.first_name,
user.last_name,
formats.date_format(user.date_joined, "SHORT_DATETIME_FORMAT"),
formats.date_format(user.subscriptions.current_period_end, "SHORT_DATETIME_FORMAT")
))
table = Table(rows, colWidths=46 * mm, rowHeights=30, repeatRows=1)
table.setStyle([
('GRID', (0, 0), (-1, -1), 0.25, colors.black),
("ALIGN", (0, 0), (-1, -1), "LEFT"),
])
table = Table(rows, colWidths=46 * mm, rowHeights=30, repeatRows=1)
elements.append(table)
doc.build(elements)
return response
where can I add this line of code to do encryption
pdfencrypt.StandardEncryption("password", canPrint=0)
any help will be much appreciated
you can add the line of code as below
def pdf_view(request):
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="members.pdf"'
elements = []
**doc = SimpleDocTemplate(response, encrypt=pdfencrypt.StandardEncryption("pass", canPrint=0)**, rightMargin=0.5 * cm, leftMargin=6.5 * cm, topMargin=0.3 * cm, bottomMargin=0)
rows = []
users = User.objects.all()
for user in users:
rows.append((
user.username,
user.email,
user.first_name,
user.last_name,
formats.date_format(user.date_joined, "SHORT_DATETIME_FORMAT"),
formats.date_format(user.subscriptions.current_period_end, "SHORT_DATETIME_FORMAT")
))
table = Table(rows, colWidths=46 * mm, rowHeights=30, repeatRows=1)
table.setStyle([
('GRID', (0, 0), (-1, -1), 0.25, colors.black),
("ALIGN", (0, 0), (-1, -1), "LEFT"),
])
table = Table(rows, colWidths=46 * mm, rowHeights=30, repeatRows=1)
elements.append(table)
doc.build(elements)
return response
Related
Starting with this
The desired effect would be any of the scenarios depicted below:
However, I can't get past this error: images do not match from the image.paste call
I'm sure there are other ways you could create this effect but it felt the cleanest to just make a new layer and paste that layer onto the original image
round() is used as I was receiving errors int is the expected argument, received float without it them.
from PIL import Image, ImageOps, ImageFont, ImageDraw
def addText( img: Image, text: str, position: dict = {}):
"""
example position dict:
{
"x": 0.79,
"y": 0.17,
"angle": 0.06
}
"""
i_w, i_h = img.size
x = round(i_w * position.get("x", 0.23))
y = round(i_h * position.get("y", 0.87))
angle = (
position.get("angle", 0) * 180
) # converts unit radians to degrees i.e. { 1 unit = pi radians= 180 degrees }
font_px_height = round(i_h * 0.0475)
font = ImageFont.truetype("fonts/OpenSans-Semibold.ttf", font_px_height)
t_w, t_h = font.getsize(text)
print(f"font.size {t_w} {t_h}")
layer = Image.new("RGBA", (round(t_w * 1.05), round(t_h)))
draw_layer = ImageDraw.Draw(layer)
draw_layer.rounded_rectangle(
(0, 0, round(t_w * 1.05), round(t_h)),
round(t_h / 10)
)
draw_layer.text((0, 0), text, fill=(0, 0, 0), font=font)
layer = layer.rotate(angle, expand=1)
l_w, l_h = layer.size
l_w = round(l_w)
l_h = round(l_h)
layer.show()
img.paste(
sticker,
(
round(x - l_w / 2),
round(y - l_h / 2),
round(x + l_w / 2),
round(y + l_h / 2),
),
sticker,
)
return img
try:
with Image.open(
"./test.jpg"
) as im:
alt_im = addText(
im,
"hello world",
{"x": 0.23, "y": 0.87, "angle": 0.06},
)
alt_im.save("out.jpg")
except Exception as ex:
print("*Exception output", ex)
So I ended up using a bodge, to work with only equal numbers during the calculations so they would work out as ints each time. Removing all mention of round() and replacing it with evenize()
def evenize(*args):
evenized_numbers = []
for _n in args:
n = int(_n)
if (n % 2) != 0:
n += 1
evenized_numbers.append(n)
if len(evenized_numbers) > 1:
return tuple(evenized_numbers)
else:
return evenized_numbers[0]
works a charm
I created a PDF in reportlab using a canvas:
self.pdf = canvas.Canvas(f'{file_name}.pdf', pagesize=A4)
I create tables within tables to create my document but one of my tables is not wrapping the way I expect it to. Rather than linebreaking at spaces, it does so between words as seen below.
The code below is the code I used to create the table. It is a bit long as I did make sure that the cells I'm passing into the Table() are all Paragraph().
def _discount_table(self, width_list):
# Table Name
table_name = Paragraph('DISCOUNTS', self.header_style_grey)
# Create Header
header = [Paragraph('NAME', self.table_header_style_left)]
header += [Paragraph(x, self.table_header_style_right) for x in self.unique_discount_list]
header += [Paragraph('TOTAL', self.table_header_style_right)]
# Process Data
discount_data = [[Paragraph(cell, self.table_style2) for cell in row] for row in self.discount_data]
data = [[child_row[0]] + disc for child_row, disc in zip(self.fees_data, discount_data)]
# Create Footer
table_footer = [Paragraph('') for _ in range(len(header) - 2)]
table_footer += [Paragraph('TOTAL', self.table_header_style_right),
Paragraph(f'{self.discount_total:,.2f}', self.table_header_style_right)]
# Create Table
bg_color = self.header_style_grey.textColor
table = Table([header] + data + [table_footer], colWidths=width_list)
table.setStyle([
('GRID', (0, 0), (-1, -1), 1, 'black'),
('BACKGROUND', (0, 0), (-1, 0), bg_color),
('TEXTCOLOR', (0, 0), (-1, 0), 'white'),
('BACKGROUND', (-2, -1), (-1, -1), bg_color),
('TEXTCOLOR', (-2, -1), (-1, -1), 'white'),
('FONTNAME', (-2, -1), (-1, -1), 'Helvetica-Bold'),
('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
('ALIGN', (1, 0), (-1, -1), 'RIGHT'),
('ROWBACKGROUNDS', (0, 1), (-1, -2), ['lightgrey', 'white']),
])
return [table_name, table]
(To note that child_row[0] is already a Paragraph - this is found on the line 12 above)
The styling I used is imported from another python file as follows:
self.table_style2 = ParagraphStyle('table_style')
self.table_style2.wordWrap = 'CJK'
self.table_style2.alignment = TA_RIGHT
self.table_style = ParagraphStyle('table_style')
self.table_style.wordWrap = 'CJK'
self.table_header_style_right = ParagraphStyle('table_header_style', self.table_style)
self.table_header_style_right.textColor = colors.HexColor('#FFFFFF')
self.table_header_style_right.fontName = 'Helvetica-Bold'
self.table_header_style_right.alignment = TA_RIGHT
self.table_header_style_right.wordWrap = 'CJK'
self.table_header_style_left = ParagraphStyle('table_header_style', self.table_style)
self.table_header_style_left.textColor = colors.HexColor('#FFFFFF')
self.table_header_style_left.fontName = 'Helvetica-Bold'
self.table_header_style_left.wordWrap = 'CJK'
So I am really lost and need help. Why is the table not wrapping correctly?
I was able to fix the table wrap issue when I removed the wordWrap = 'CJK' portion of the code. I saw in a video that a Paragraph() will automatically wordWrap so I'm guessing there was some issue with how those two elements overlap
self.table_style2 = ParagraphStyle('table_style')
# self.table_style2.wordWrap = 'CJK'
self.table_style2.alignment = TA_RIGHT
self.table_style = ParagraphStyle('table_style')
# self.table_style.wordWrap = 'CJK'
self.table_header_style_right = ParagraphStyle('table_header_style', self.table_style)
self.table_header_style_right.textColor = colors.HexColor('#FFFFFF')
self.table_header_style_right.fontName = 'Helvetica-Bold'
self.table_header_style_right.alignment = TA_RIGHT
# self.table_header_style_right.wordWrap = 'CJK'
self.table_header_style_left = ParagraphStyle('table_header_style', self.table_style)
self.table_header_style_left.textColor = colors.HexColor('#FFFFFF')
self.table_header_style_left.fontName = 'Helvetica-Bold'
# self.table_header_style_left.wordWrap = 'CJK'
I need through a for loop to create more tables, I think it works but I need to change the various coordinates, how can I do?
2)
Is it possible to change the width of a single row of a table? or in any case bring its text-align to the left but which starts from where the table starts?
def testPdfView(request, id):
#dati init
scheda = get_object_or_404(Schede, pk = id)
filename = 'media/pdf/' + scheda.nome_scheda + '.pdf'
titolo = scheda.utente.username + ' - ' + scheda.nome_scheda
#creazione file
doc = SimpleDocTemplate(
filename,
pagesize = A4,
rightMargin = 10*mm,
leftMargin = 10*mm,
topMargin = 47*mm,
bottomMargin = 10*mm
)
#titolo
doc.title = titolo
#passaggio scheda alla funzione pdfCanvas
doc.scheda = scheda
#table gruppi
gruppi = DatiGruppi.objects.filter(gruppi_scheda = id)
for gruppo in gruppi:
table = Table([
[str(gruppo).upper()]
], colWidths= 180*mm, repeatRows=1)
#table style
style = TableStyle([
('TEXTCOLOR', (0,0),(-1,0), colors.HexColor("#9FFC0D")),# -1 significa l'ultimo elemento
('FONTNAME', (0,0), (0,0), 'bulk_bold'),
('FONTSIZE', (0,0), (0,0), 6*mm),
('BOTTOMPADDING', (0,0), (-1,0), 6*mm),
('LINEBELOW',(0,0),(-1,0), 1, colors.HexColor("#9FFC0D")),
])
table.setStyle(style)
#table add to template
elems = []
elems.append(table)
#create
doc.build(elems, onFirstPage = pdfCanvas)
def genGroupTable(gruppo):
groupElemTeble = None
#tab
titleTable = Table([
[str(gruppo).upper(), str(gruppo.giorni_settimana).upper()]
], colWidths= 95*mm)
titleTable_style = TableStyle([
('TEXTCOLOR', (0,0),(-1,0), colors.HexColor("#9FFC0D")),
('FONTNAME', (0,0), (0,0), 'bulk_bold'),
('ALIGN',(1,0),(-1,0),'RIGHT'),
('VALIGN',(0,0),(-1,0),'MIDDLE'),
('FONTSIZE', (0,0), (0,0), 6*mm),
('BOTTOMPADDING', (0,0), (0,0), 6*mm),
('LINEBELOW',(0,0),(-1,0), 1, colors.HexColor("#9FFC0D")),
('LEFTPADDING',(0,0),(-1,-1), 0*mm),
('RIGHTPADDING',(0,0),(-1,-1), 0*mm)
])
titleTable.setStyle(titleTable_style)
thead = Table([
['ESERCIZIO','SERIE','RIPETIZIONI','PESO']
], colWidths= 47.5*mm)
thead_style = TableStyle([
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#19212A")),
('TEXTCOLOR', (0,0),(-1,0), colors.HexColor("#ffffff")),
('FONTNAME', (0,0), (-1,0), 'bulk_testo'),
('ALIGN',(0,0),(-1,0),'CENTER'),
('VALIGN',(0,0),(-1,0),'MIDDLE'),
('BOTTOMPADDING', (0,0), (-1,0), 1*mm)
])
thead.setStyle(thead_style)
exercise = []
elementi = []
for esercizio in gruppo.gruppo_single.all():
exercise.append(esercizio)
for es in range(len(exercise)):
tbody = Table([
[str(exercise[es]).upper(), str(exercise[es].serie).upper(), str(exercise[es].ripetizione).upper(), str(exercise[es].peso).upper()+'KG']
], colWidths= 47.5*mm)
tbody_style = TableStyle([
('TEXTCOLOR', (0,0),(-1,-1), colors.HexColor("#ffffff")),
('FONTNAME', (0,0), (-1,-1), 'bulk_testo'),
('ALIGN',(0,0),(-1,-1),'CENTER'),
('BOTTOMPADDING', (0,0), (-1,-1), 1*mm),
('LINEBELOW',(0,0),(-1,-1), .2, colors.HexColor("#ffffff"))
])
tbody.setStyle(tbody_style)
elementi.append(tbody)
#tab finale
groupElemTeble = Table([
[titleTable],
[thead],
[[elementi]]
], colWidths = 190*mm)
groupElemTeble_style = TableStyle([
#('BACKGROUND', (0, 0), (-1, -1), colors.HexColor("#202B38")),
('LEFTPADDING',(0,0),(-1,-1), 0*mm),
('RIGHTPADDING',(0,0),(-1,-1), 0*mm),
('BOTTOMPADDING',(-1,-1),(-1,-1), 5*mm)
])
groupElemTeble.setStyle(groupElemTeble_style)
return groupElemTeble
def testPdfView(request, id):
#dati init
scheda = get_object_or_404(Schede, pk = id)
filename = 'media/pdf/' + scheda.nome_scheda + '.pdf'
titolo = scheda.utente.username + ' - ' + scheda.nome_scheda
#creazione file
doc = SimpleDocTemplate(
filename,
pagesize = A4,
rightMargin = 10*mm,
leftMargin = 10*mm,
topMargin = 47*mm,
bottomMargin = 10*mm
)
#titolo
doc.title = titolo
#passaggio scheda alla funzione pdfCanvas
doc.scheda = scheda
#elemento vuoto
elems = []
#lista gruppi
group = []
gruppi = DatiGruppi.objects.filter(gruppi_scheda = id)
for gruppo in gruppi:
group.append(gruppo)
for gr in range(len(group)):
main = genGroupTable(group[gr])
elems.append(main)
#create
doc.multiBuild(elems, onFirstPage = header)
#return
percorso = str(settings.BASE_DIR) +'/media/pdf/' + scheda.nome_scheda + '.pdf'
return FileResponse(open(percorso, 'rb'), content_type='application/pdf')
So I have a little program which makes a DB call and then converts it into a PDF.
I have most of them working but this last one is returning a Key Error on me and I cannot figure out why.
Here is an example of the data being returned by the DB:
((None, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 26, 0, 26), (1, 1, 0, 0, 0, 17, 0, 18), (2, 0, 0, 0, 0, 15, 0, 16))
The Traceback:
Traceback (most recent call last):
File "C:\Users\Ace\AppData\Local\Programs\Python\Python36\lib\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
File "C:/Users/Ace/Desktop/IPNV/KP_App/FML/firstapp.py", line 232, in hrday_in
hourday_filter(noodle, dest, drange)
File "C:\Users\Ace\Desktop\IPNV\KP_App\FML\dataIN.py", line 187, in hourday_filter
doc.export(pths, drange)
File "C:\Users\Ace\Desktop\IPNV\KP_App\FML\calazan.py", line 58, in export
reverse=reverse_order)
KeyError: 'h'
Im not even sure where the 'h' comes from.
Here is the function that I run the data through to prepare for the pdf generation:
def hourday_filter(tuna, pth, drange):
data = []
for hr, number, local, chicken, alligator, ace, lola, chunk in tuna:
data.append({'hour': hr,
'number': number,
'local': local,
'long': chicken,
'inter': alligator,
'income': ace,
'tandem': lola,
'total': chunk})
fields = (
('hour', 'Hour of Day'),
('number', 'Internal Calls '),
('local', 'Local Calls'),
('long', 'Long Distance Calls'),
('inter', 'International Calls '),
('income', 'Incoming Calls'),
('tandem', 'Tandem Calls'),
('total', 'Total Calls'),
)
pths = pth + '/HourofDay.pdf'
doc = DataToPdf(fields, data, sort_by='hr',
title='Hour of Day Report')
doc.export(pths, drange)
And From there the data is passed to this function to actually convert it too pdf.
class DataToPdf:
"""
Export a list of dictionaries to a table in a PDF file.
"""
def __init__(self, fields, data, sort_by=None, title=None):
"""
Arguments:
fields - A tuple of tuples ((fieldname/key, display_name))
specifying the fieldname/key and corresponding display
name for the table header.
data - The data to insert to the table formatted as a list of
dictionaries.
sort_by - A tuple (sort_key, sort_order) specifying which field
to sort by and the sort order ('ASC', 'DESC').
title - The title to display at the beginning of the document.
"""
self.fields = fields
self.data = data
self.title = title
self.sort_by = sort_by
def export(self, filename, drange, data_align='LEFT', table_halign='LEFT'):
doc = SimpleDocTemplate(filename, pagesize=letter)
styles = getSampleStyleSheet()
styleH = styles['Heading1']
styleD = styles['Heading4']
date = time.strftime("%m/%d/%Y")
date2 = 'Ran on: ' + date
date3 = ' For the period ' + str(drange[0]) + ' to ' + str(drange[1]) # Edit here to display report date range
story = []
if self.title:
story.append(Paragraph(self.title, styleH))
story.append(Spacer(1, 0.25 * inch))
story.append(Paragraph(date2, styleD))
story.append(Spacer(1, 0.015 * inch))
story.append(Paragraph(date3, styleD))
if self.sort_by:
reverse_order = False
if str(self.sort_by[1]).upper() == 'DESC':
reverse_order = False
self.data = sorted(self.data,
key=itemgetter(self.sort_by[0]),
reverse=reverse_order)
converted_data = self.__convert_data()
table = Table(converted_data, hAlign=table_halign)
table.setStyle(TableStyle([
('FONT', (0, 0), (-1, 0), 'Helvetica-Bold'),
('ALIGN', (0, 0), (-1, 0), 'CENTER'),
('ALIGN', (0, 0), (0, -1), data_align),
('INNERGRID', (0, 0), (-1, -1), 0.50, colors.black),
('BOX', (0, 0), (-1, -1), 0.25, colors.black),
]))
data_len = len(converted_data)
for each in range(data_len):
if each % 2 == 0:
bg_color = colors.whitesmoke
else:
bg_color = colors.lightgrey
table.setStyle(TableStyle([('BACKGROUND', (0, each), (-1, each), bg_color)]))
story.append(table)
doc.build(story)
def __convert_data(self):
"""
Convert the list of dictionaries to a list of list to create
the PDF table.
"""
# Create 2 separate lists in the same order: one for the
# list of keys and the other for the names to display in the
# table header.
keys, names = zip(*[[k, n] for k, n in self.fields])
new_data = [names]
for d in self.data:
new_data.append([d[k] for k in keys])
return new_data
Is it possible that first result of the db (the null one) is causing this? I've made about a dozen of these reports now with no problems, not sure where I am messing up here.
So thanks to FamousJameous I realized that it was indeed the first field killing my filter. The sort_by call did not know how to deal with the NULL value. I managed to fix it by removing that first field from the tuple.
The database returned the results into the variable result
From there:
new_result = result[1:]
This line removed the NULL line and stopped the error
def logo_para(self):
exp = Paragraph(
'<b>Express</b>', self.styles['CenterHeading'])
csheet = Paragraph(
'<b>PDF SHEET</b>', self.styles['CenterHeading'])
img_location = "https://www.google.co.in/logos/doodles/2016/icc-australia-v-bangladesh-5759441086447616-res.png"
img_data = '''
<para><img src="%s" width="300" height="90"/><br/>
</para>''' % img_location
img = Paragraph(img_data, self.styles['CenterHeading'])
data = [[exp], [csheet], [''], [img] ]
main_header_table = Table([['', img, '']], colWidths=(100, 300, 100))
main_header_table.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('BOX', (0, 0), (-1, -1), 0.25, colors.black)
]))
self.elements.append(main_header_table)
When ever I am calling
docket.logo_para()
I am getting error cannot concatenate 'str' and 'int' objects
at self.doc.build(self.elements)
When the line is commenteddocket.logo_para(), the code works superbly.
I am trying to add an image on the PDF File using SimpleDocTemplate
EDIT 1
creating new pdf
class PDFDocketGenerator(object):
def __init__(self, file_name):
self.filename = file_name
self.filepath = STATIC_URL + 'uploads/billing/' + file_name
self.path_to_save = FILE_UPLOAD_TEMP_DIR + '/billing/' + file_name
# define the pdf object
self.doc = SimpleDocTemplate(
self.path_to_save, pagesize=landscape(A4), topMargin=50, bottomMargin=30,
leftMargin=60, rightMargin=60)
self.elements = []
writing to pdf
def write_pdf(self):
self.doc.build(self.elements)
Is it possible that some values in self.elements are integers? I would suggest to try this in this case :
def write_pdf(self):
self.doc.build([str(e) for e in self.elements])