1 | """ |
---|
2 | BSD license - created by Massimo Di Pierro |
---|
3 | """ |
---|
4 | from __future__ import print_function |
---|
5 | from reportlab.pdfgen.canvas import Canvas |
---|
6 | from reportlab.platypus import Table |
---|
7 | from reportlab.lib.pagesizes import A4 |
---|
8 | from reportlab.lib.units import cm |
---|
9 | from decimal import Decimal |
---|
10 | import cStringIO |
---|
11 | import datetime |
---|
12 | |
---|
13 | def listify(item): |
---|
14 | if isinstance(item,basestring): |
---|
15 | item = item.split('\n') |
---|
16 | return item |
---|
17 | |
---|
18 | class PDF(object): |
---|
19 | def __init__(self, page_size=A4, font_face='Helvetica'): |
---|
20 | self.page_size = page_size |
---|
21 | self.font_face = font_face |
---|
22 | self.logo = None |
---|
23 | def format_currency(self,value): |
---|
24 | a = list(str(int(value))) |
---|
25 | for k in range(len(a)-3,0,-3): |
---|
26 | a.insert(k,',') |
---|
27 | a = ''.join(a) |
---|
28 | b = ("%.2f" % (value-int(value)))[2:] |
---|
29 | return "%s.%s" % (a,b) |
---|
30 | def draw(self, invoice, items_page=10): |
---|
31 | """ Draws the invoice """ |
---|
32 | buffer = cStringIO.StringIO() |
---|
33 | invoice_items = invoice['items'] |
---|
34 | pages = max((len(invoice_items)-2)/items_page+1,1) |
---|
35 | canvas = Canvas(buffer, pagesize=self.page_size) |
---|
36 | for page in range(pages): |
---|
37 | canvas.translate(0, 29.7 * cm) |
---|
38 | canvas.setFont(self.font_face, 10) |
---|
39 | |
---|
40 | canvas.saveState() |
---|
41 | canvas.setStrokeColorRGB(0.9, 0.5, 0.2) |
---|
42 | canvas.setFillColorRGB(0.2, 0.2, 0.2) |
---|
43 | canvas.setFont(self.font_face, 16) |
---|
44 | canvas.drawString(1 * cm, -1 * cm, invoice.get('title','')) |
---|
45 | if self.logo: |
---|
46 | canvas.drawInlineImage(self.logo, 1 * cm, -1 * cm, 250, 16) |
---|
47 | canvas.setLineWidth(4) |
---|
48 | canvas.line(0, -1.25 * cm, 21.7 * cm, -1.25 * cm) |
---|
49 | canvas.restoreState() |
---|
50 | |
---|
51 | canvas.saveState() |
---|
52 | notes = listify(invoice.get('notes','')) |
---|
53 | textobject = canvas.beginText(1 * cm, -25 * cm) |
---|
54 | for line in notes: |
---|
55 | textobject.textLine(line) |
---|
56 | canvas.drawText(textobject) |
---|
57 | textobject = canvas.beginText(18 * cm, -28 * cm) |
---|
58 | textobject.textLine('Pag.%s/%s' % (page+1,pages)) |
---|
59 | canvas.drawText(textobject) |
---|
60 | canvas.restoreState() |
---|
61 | |
---|
62 | canvas.saveState() |
---|
63 | business_details = listify(invoice.get('from','FROM:')) |
---|
64 | canvas.setFont(self.font_face, 9) |
---|
65 | textobject = canvas.beginText(13 * cm, -2.5 * cm) |
---|
66 | for line in business_details: |
---|
67 | textobject.textLine(line) |
---|
68 | canvas.drawText(textobject) |
---|
69 | canvas.restoreState() |
---|
70 | |
---|
71 | canvas.saveState() |
---|
72 | client_info = listify(invoice.get('to','TO:')) |
---|
73 | textobject = canvas.beginText(1.5 * cm, -2.5 * cm) |
---|
74 | for line in client_info: |
---|
75 | textobject.textLine(line) |
---|
76 | canvas.drawText(textobject) |
---|
77 | canvas.restoreState() |
---|
78 | |
---|
79 | textobject = canvas.beginText(1.5 * cm, -6.75 * cm) |
---|
80 | textobject.textLine(u'Invoice ID: %s' % invoice.get('id','<invoice id>')) |
---|
81 | textobject.textLine(u'Invoice Date: %s' % invoice.get('date',datetime.date.today())) |
---|
82 | textobject.textLine(u'Client: %s' % invoice.get('client_name','<invoice client>')) |
---|
83 | canvas.drawText(textobject) |
---|
84 | |
---|
85 | items = invoice_items[1:][page*items_page:(page+1)*items_page] |
---|
86 | if items: |
---|
87 | data = [invoice_items[0]] |
---|
88 | for item in items: |
---|
89 | data.append([ |
---|
90 | self.format_currency(x) |
---|
91 | if isinstance(x,float) else x |
---|
92 | for x in item]) |
---|
93 | righta = [k for k,v in enumerate(items[0]) |
---|
94 | if isinstance(v,(int,float,Decimal))] |
---|
95 | if page == pages-1: |
---|
96 | total = self.format_currency(invoice['total']) |
---|
97 | else: |
---|
98 | total = '' |
---|
99 | data.append(['']*(len(items[0])-1)+[total]) |
---|
100 | colWidths = [2.5*cm]*len(items[0]) |
---|
101 | colWidths[1] = (21.5-2.5*len(items[0]))*cm |
---|
102 | table = Table(data, colWidths=colWidths) |
---|
103 | table.setStyle([ |
---|
104 | ('FONT', (0, 0), (-1, -1), self.font_face), |
---|
105 | ('FONTSIZE', (0, 0), (-1, -1), 8), |
---|
106 | ('TEXTCOLOR', (0, 0), (-1, -1), (0.2, 0.2, 0.2)), |
---|
107 | ('GRID', (0, 0), (-1, -2), 1, (0.7, 0.7, 0.7)), |
---|
108 | ('GRID', (-1, -1), (-1, -1), 1, (0.7, 0.7, 0.7)), |
---|
109 | ('BACKGROUND', (0, 0), (-1, 0), (0.8, 0.8, 0.8)), |
---|
110 | ]+[('ALIGN',(k,0),(k,-1),'RIGHT') for k in righta]) |
---|
111 | tw, th, = table.wrapOn(canvas, 15 * cm, 19 * cm) |
---|
112 | table.drawOn(canvas, 1 * cm, -8 * cm - th) |
---|
113 | |
---|
114 | if page == pages-1: |
---|
115 | items = invoice['totals'][1:] |
---|
116 | if items: |
---|
117 | data = [invoice['totals'][0]] |
---|
118 | for item in items: |
---|
119 | data.append([ |
---|
120 | self.format_currency(x) |
---|
121 | if isinstance(x,float) else x |
---|
122 | for x in item]) |
---|
123 | righta = [k for k,v in enumerate(items[0]) |
---|
124 | if isinstance(v,(int,float,Decimal))] |
---|
125 | total = self.format_currency(invoice['total']) |
---|
126 | data.append(['']*(len(items[0])-1)+[total]) |
---|
127 | colWidths = [2.5*cm]*len(items[0]) |
---|
128 | colWidths[1] = (21.5-2.5*len(items[0]))*cm |
---|
129 | table = Table(data, colWidths=colWidths) |
---|
130 | table.setStyle([ |
---|
131 | ('FONT', (0, 0), (-1, -1), self.font_face), |
---|
132 | ('FONTSIZE', (0, 0), (-1, -1), 8), |
---|
133 | ('TEXTCOLOR', (0, 0), (-1, -1), (0.2, 0.2, 0.2)), |
---|
134 | ('GRID', (0, 0), (-1, -2), 1, (0.7, 0.7, 0.7)), |
---|
135 | ('GRID', (-1, -1), (-1, -1), 1, (0.7, 0.7, 0.7)), |
---|
136 | ('BACKGROUND', (0, 0), (-1, 0), (0.8, 0.8, 0.8)), |
---|
137 | ]+[('ALIGN',(k,0),(k,-1),'RIGHT') for k in righta]) |
---|
138 | tw, th, = table.wrapOn(canvas, 15 * cm, 19 * cm) |
---|
139 | table.drawOn(canvas, 1 * cm, -18 * cm - th) |
---|
140 | canvas.showPage() |
---|
141 | canvas.save() |
---|
142 | return buffer.getvalue() |
---|
143 | |
---|
144 | if __name__=='__main__': |
---|
145 | invoice = { |
---|
146 | 'title': 'Invoice - web2py.com', |
---|
147 | 'id': '00001', |
---|
148 | 'date': '10/10/2013', |
---|
149 | 'client_name': 'Nobody', |
---|
150 | 'from': 'FROM:\nweb2py.com\nWabash ave\nChicago', |
---|
151 | 'to': 'TO:\nNobody\nHis address', |
---|
152 | 'notes': 'no comment!', |
---|
153 | 'total': 650.00, |
---|
154 | 'items': [ |
---|
155 | ['Codice','Desc','Quantity','Unit price','Total']]+[ |
---|
156 | ['000001','Chair',2,10.0,20.0] for k in range(30)], |
---|
157 | 'totals': [ |
---|
158 | ['Codice','Desc','Total']]+[ |
---|
159 | ['000001','Chairs',600.0], |
---|
160 | ['','Tax',50.0]], |
---|
161 | } |
---|
162 | print(PDF().draw(invoice,items_page=20)) |
---|