Updated fpdf to the last version (py3 compatible), fix contrib/appconfig
This commit is contained in:
+66
-138
@@ -2,12 +2,15 @@
|
||||
|
||||
"PDF Template Helper for FPDF.py"
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
__author__ = "Mariano Reingart <reingart@gmail.com>"
|
||||
__copyright__ = "Copyright (C) 2010 Mariano Reingart"
|
||||
__license__ = "LGPL 3.0"
|
||||
|
||||
import sys,os,csv
|
||||
from fpdf import FPDF
|
||||
from .fpdf import FPDF
|
||||
from .py3k import PY3K, basestring, unicode
|
||||
|
||||
def rgb(col):
|
||||
return (col // 65536), (col // 256 % 256), (col% 256)
|
||||
@@ -16,11 +19,9 @@ class Template:
|
||||
def __init__(self, infile=None, elements=None, format='A4', orientation='portrait',
|
||||
title='', author='', subject='', creator='', keywords=''):
|
||||
if elements:
|
||||
self.elements = elements
|
||||
self.keys = [v['name'].lower() for v in self.elements]
|
||||
self.load_elements(elements)
|
||||
self.handlers = {'T': self.text, 'L': self.line, 'I': self.image,
|
||||
'B': self.rect, 'BC': self.barcode, }
|
||||
self.pg_no = 0
|
||||
'B': self.rect, 'BC': self.barcode, 'W': self.write, }
|
||||
self.texts = {}
|
||||
pdf = self.pdf = FPDF(format=format,orientation=orientation, unit="mm")
|
||||
pdf.set_title(title)
|
||||
@@ -29,25 +30,37 @@ class Template:
|
||||
pdf.set_subject(subject)
|
||||
pdf.set_keywords(keywords)
|
||||
|
||||
def load_elements(self, elements):
|
||||
"Initialize the internal element structures"
|
||||
self.pg_no = 0
|
||||
self.elements = elements
|
||||
self.keys = [v['name'].lower() for v in self.elements]
|
||||
|
||||
def parse_csv(self, infile, delimiter=",", decimal_sep="."):
|
||||
"Parse template format csv file and create elements dict"
|
||||
keys = ('name','type','x1','y1','x2','y2','font','size',
|
||||
'bold','italic','underline','foreground','background',
|
||||
'align','text','priority', 'multiline')
|
||||
self.elements = []
|
||||
for row in csv.reader(open(infile, 'rb'), delimiter=delimiter):
|
||||
kargs = {}
|
||||
for i,v in enumerate(row):
|
||||
if not v.startswith("'") and decimal_sep!=".":
|
||||
v = v.replace(decimal_sep,".")
|
||||
else:
|
||||
v = v
|
||||
if v=='':
|
||||
v = None
|
||||
else:
|
||||
v = eval(v.strip())
|
||||
kargs[keys[i]] = v
|
||||
self.elements.append(kargs)
|
||||
self.pg_no = 0
|
||||
if not PY3K:
|
||||
f = open(infile, 'rb')
|
||||
else:
|
||||
f = open(infile)
|
||||
with f:
|
||||
for row in csv.reader(f, delimiter=delimiter):
|
||||
kargs = {}
|
||||
for i,v in enumerate(row):
|
||||
if not v.startswith("'") and decimal_sep!=".":
|
||||
v = v.replace(decimal_sep,".")
|
||||
else:
|
||||
v = v
|
||||
if v=='':
|
||||
v = None
|
||||
else:
|
||||
v = eval(v.strip())
|
||||
kargs[keys[i]] = v
|
||||
self.elements.append(kargs)
|
||||
self.keys = [v['name'].lower() for v in self.elements]
|
||||
|
||||
def add_page(self):
|
||||
@@ -55,8 +68,8 @@ class Template:
|
||||
self.texts[self.pg_no] = {}
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
if self.has_key(name):
|
||||
if isinstance(value,unicode):
|
||||
if name.lower() in self.keys:
|
||||
if not PY3K and isinstance(value, unicode):
|
||||
value = value.encode("latin1","ignore")
|
||||
elif value is None:
|
||||
value = ""
|
||||
@@ -71,7 +84,7 @@ class Template:
|
||||
return name.lower() in self.keys
|
||||
|
||||
def __getitem__(self, name):
|
||||
if self.has_key(name):
|
||||
if name in self.keys:
|
||||
key = name.lower()
|
||||
if key in self.texts:
|
||||
# text for this page:
|
||||
@@ -94,7 +107,7 @@ class Template:
|
||||
if element['underline']: style += "U"
|
||||
pdf.set_font(element['font'],style,element['size'])
|
||||
align = {'L':'L','R':'R','I':'L','D':'R','C':'C','':''}.get(element['align']) # D/I in spanish
|
||||
if isinstance(text, unicode):
|
||||
if isinstance(text, unicode) and not PY3K:
|
||||
text = text.encode("latin1","ignore")
|
||||
else:
|
||||
text = str(text)
|
||||
@@ -110,16 +123,17 @@ class Template:
|
||||
pdf.set_auto_page_break(False,margin=0)
|
||||
|
||||
for element in sorted(self.elements,key=lambda x: x['priority']):
|
||||
# make a copy of the element:
|
||||
element = dict(element)
|
||||
#print "dib",element['type'], element['name'], element['x1'], element['y1'], element['x2'], element['y2']
|
||||
element = element.copy()
|
||||
element['text'] = self.texts[pg].get(element['name'].lower(), element['text'])
|
||||
if 'rotate' in element:
|
||||
pdf.rotate(element['rotate'], element['x1'], element['y1'])
|
||||
self.handlers[element['type'].upper()](pdf, **element)
|
||||
if 'rotate' in element:
|
||||
pdf.rotate(0)
|
||||
|
||||
return pdf.output(outfile, dest)
|
||||
|
||||
if dest:
|
||||
return pdf.output(outfile, dest)
|
||||
|
||||
def text(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=10,
|
||||
bold=False, italic=False, underline=False, align="",
|
||||
@@ -157,7 +171,7 @@ class Template:
|
||||
# multiline==False: trim to fit exactly the space defined
|
||||
text = pdf.multi_cell(w=x2-x1, h=y2-y1,
|
||||
txt=text, align=align, split_only=True)[0]
|
||||
print "trimming: *%s*" % text
|
||||
print("trimming: *%s*" % text)
|
||||
pdf.cell(w=x2-x1,h=y2-y1,txt=text,border=0,ln=0,align=align)
|
||||
|
||||
#pdf.Text(x=x1,y=y1,txt=text)
|
||||
@@ -179,7 +193,8 @@ class Template:
|
||||
pdf.rect(x1, y1, x2-x1, y2-y1)
|
||||
|
||||
def image(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', *args,**kwargs):
|
||||
pdf.image(text,x1,y1,w=x2-x1,h=y2-y1,type='',link='')
|
||||
if text:
|
||||
pdf.image(text,x1,y1,w=x2-x1,h=y2-y1,type='',link='')
|
||||
|
||||
def barcode(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=1,
|
||||
foreground=0, *args, **kwargs):
|
||||
@@ -189,113 +204,26 @@ class Template:
|
||||
if font == 'interleaved 2of5 nt':
|
||||
pdf.interleaved2of5(text,x1,y1,w=size,h=y2-y1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
# generate sample invoice (according Argentina's regulations)
|
||||
|
||||
import random
|
||||
from decimal import Decimal
|
||||
|
||||
f = Template(format="A4",
|
||||
title="Sample Invoice", author="Sample Company",
|
||||
subject="Sample Customer", keywords="Electronic TAX Invoice")
|
||||
f.parse_csv(infile="invoice.csv", delimiter=";", decimal_sep=",")
|
||||
|
||||
detail = "Lorem ipsum dolor sit amet, consectetur. " * 30
|
||||
items = []
|
||||
for i in range(1, 30):
|
||||
ds = "Sample product %s" % i
|
||||
qty = random.randint(1,10)
|
||||
price = round(random.random()*100,3)
|
||||
code = "%s%s%02d" % (chr(random.randint(65,90)), chr(random.randint(65,90)),i)
|
||||
items.append(dict(code=code, unit='u',
|
||||
qty=qty, price=price,
|
||||
amount=qty*price,
|
||||
ds="%s: %s" % (i,ds)))
|
||||
|
||||
# divide and count lines
|
||||
lines = 0
|
||||
li_items = []
|
||||
for it in items:
|
||||
qty = it['qty']
|
||||
code = it['code']
|
||||
unit = it['unit']
|
||||
for ds in f.split_multicell(it['ds'], 'item_description01'):
|
||||
# add item description line (without price nor amount)
|
||||
li_items.append(dict(code=code, ds=ds, qty=qty, unit=unit, price=None, amount=None))
|
||||
# clean qty and code (show only at first)
|
||||
unit = qty = code = None
|
||||
# set last item line price and amount
|
||||
li_items[-1].update(amount = it['amount'],
|
||||
price = it['price'])
|
||||
|
||||
obs="\n<U>Detail:</U>\n\n" + detail
|
||||
for ds in f.split_multicell(obs, 'item_description01'):
|
||||
li_items.append(dict(code=code, ds=ds, qty=qty, unit=unit, price=None, amount=None))
|
||||
|
||||
# calculate pages:
|
||||
lines = len(li_items)
|
||||
max_lines_per_page = 24
|
||||
pages = lines / (max_lines_per_page - 1)
|
||||
if lines % (max_lines_per_page - 1): pages = pages + 1
|
||||
|
||||
# completo campos y hojas
|
||||
for page in range(1, pages+1):
|
||||
f.add_page()
|
||||
f['page'] = 'Page %s of %s' % (page, pages)
|
||||
if pages>1 and page<pages:
|
||||
s = 'Continues on page %s' % (page+1)
|
||||
else:
|
||||
s = ''
|
||||
f['item_description%02d' % (max_lines_per_page+1)] = s
|
||||
|
||||
f["company_name"] = "Sample Company"
|
||||
f["company_logo"] = "tutorial/logo.png"
|
||||
f["company_header1"] = "Some Address - somewhere -"
|
||||
f["company_header2"] = "http://www.example.com"
|
||||
f["company_footer1"] = "Tax Code ..."
|
||||
f["company_footer2"] = "Tax/VAT ID ..."
|
||||
f['number'] = '0001-00001234'
|
||||
f['issue_date'] = '2010-09-10'
|
||||
f['due_date'] = '2099-09-10'
|
||||
f['customer_name'] = "Sample Client"
|
||||
f['customer_address'] = "Siempreviva 1234"
|
||||
|
||||
# print line item...
|
||||
li = 0
|
||||
k = 0
|
||||
total = Decimal("0.00")
|
||||
for it in li_items:
|
||||
k = k + 1
|
||||
if k > page * (max_lines_per_page - 1):
|
||||
break
|
||||
if it['amount']:
|
||||
total += Decimal("%.6f" % it['amount'])
|
||||
if k > (page - 1) * (max_lines_per_page - 1):
|
||||
li += 1
|
||||
if it['qty'] is not None:
|
||||
f['item_quantity%02d' % li] = it['qty']
|
||||
if it['code'] is not None:
|
||||
f['item_code%02d' % li] = it['code']
|
||||
if it['unit'] is not None:
|
||||
f['item_unit%02d' % li] = it['unit']
|
||||
f['item_description%02d' % li] = it['ds']
|
||||
if it['price'] is not None:
|
||||
f['item_price%02d' % li] = "%0.3f" % it['price']
|
||||
if it['amount'] is not None:
|
||||
f['item_amount%02d' % li] = "%0.2f" % it['amount']
|
||||
|
||||
if pages == page:
|
||||
f['net'] = "%0.2f" % (total/Decimal("1.21"))
|
||||
f['vat'] = "%0.2f" % (total*(1-1/Decimal("1.21")))
|
||||
f['total_label'] = 'Total:'
|
||||
else:
|
||||
f['total_label'] = 'SubTotal:'
|
||||
f['total'] = "%0.2f" % total
|
||||
|
||||
f.render("./invoice.pdf")
|
||||
if sys.platform.startswith("linux"):
|
||||
os.system("evince ./invoice.pdf")
|
||||
else:
|
||||
os.system("./invoice.pdf")
|
||||
# Added by Derek Schwalenberg Schwalenberg1013@gmail.com to allow (url) links in templates (using write method) 2014-02-22
|
||||
def write(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=1,
|
||||
bold=False, italic=False, underline=False, align="", link='http://example.com',
|
||||
foreground=0, *args, **kwargs):
|
||||
if pdf.text_color!=rgb(foreground):
|
||||
pdf.set_text_color(*rgb(foreground))
|
||||
font = font.strip().lower()
|
||||
if font == 'arial black':
|
||||
font = 'arial'
|
||||
style = ""
|
||||
for tag in 'B', 'I', 'U':
|
||||
if (text.startswith("<%s>" % tag) and text.endswith("</%s>" %tag)):
|
||||
text = text[3:-4]
|
||||
style += tag
|
||||
if bold: style += "B"
|
||||
if italic: style += "I"
|
||||
if underline: style += "U"
|
||||
align = {'L':'L','R':'R','I':'L','D':'R','C':'C','':''}.get(align) # D/I in spanish
|
||||
pdf.set_font(font,style,size)
|
||||
##m_k = 72 / 2.54
|
||||
##h = (size/m_k)
|
||||
pdf.set_xy(x1,y1)
|
||||
pdf.write(5,text,link)
|
||||
|
||||
Reference in New Issue
Block a user