1 | # -*- coding: iso-8859-1 -*- |
---|
2 | |
---|
3 | "PDF Template Helper for FPDF.py" |
---|
4 | |
---|
5 | from __future__ import with_statement |
---|
6 | |
---|
7 | __author__ = "Mariano Reingart <reingart@gmail.com>" |
---|
8 | __copyright__ = "Copyright (C) 2010 Mariano Reingart" |
---|
9 | __license__ = "LGPL 3.0" |
---|
10 | |
---|
11 | import sys,os,csv |
---|
12 | from .fpdf import FPDF |
---|
13 | from .py3k import PY3K, basestring, unicode |
---|
14 | |
---|
15 | def rgb(col): |
---|
16 | return (col // 65536), (col // 256 % 256), (col% 256) |
---|
17 | |
---|
18 | class Template: |
---|
19 | def __init__(self, infile=None, elements=None, format='A4', orientation='portrait', |
---|
20 | title='', author='', subject='', creator='', keywords=''): |
---|
21 | if elements: |
---|
22 | self.load_elements(elements) |
---|
23 | self.handlers = {'T': self.text, 'L': self.line, 'I': self.image, |
---|
24 | 'B': self.rect, 'BC': self.barcode, 'W': self.write, } |
---|
25 | self.texts = {} |
---|
26 | pdf = self.pdf = FPDF(format=format,orientation=orientation, unit="mm") |
---|
27 | pdf.set_title(title) |
---|
28 | pdf.set_author(author) |
---|
29 | pdf.set_creator(creator) |
---|
30 | pdf.set_subject(subject) |
---|
31 | pdf.set_keywords(keywords) |
---|
32 | |
---|
33 | def load_elements(self, elements): |
---|
34 | "Initialize the internal element structures" |
---|
35 | self.pg_no = 0 |
---|
36 | self.elements = elements |
---|
37 | self.keys = [v['name'].lower() for v in self.elements] |
---|
38 | |
---|
39 | def parse_csv(self, infile, delimiter=",", decimal_sep="."): |
---|
40 | "Parse template format csv file and create elements dict" |
---|
41 | keys = ('name','type','x1','y1','x2','y2','font','size', |
---|
42 | 'bold','italic','underline','foreground','background', |
---|
43 | 'align','text','priority', 'multiline') |
---|
44 | self.elements = [] |
---|
45 | self.pg_no = 0 |
---|
46 | if not PY3K: |
---|
47 | f = open(infile, 'rb') |
---|
48 | else: |
---|
49 | f = open(infile) |
---|
50 | with f: |
---|
51 | for row in csv.reader(f, delimiter=delimiter): |
---|
52 | kargs = {} |
---|
53 | for i,v in enumerate(row): |
---|
54 | if not v.startswith("'") and decimal_sep!=".": |
---|
55 | v = v.replace(decimal_sep,".") |
---|
56 | else: |
---|
57 | v = v |
---|
58 | if v=='': |
---|
59 | v = None |
---|
60 | else: |
---|
61 | v = eval(v.strip()) |
---|
62 | kargs[keys[i]] = v |
---|
63 | self.elements.append(kargs) |
---|
64 | self.keys = [v['name'].lower() for v in self.elements] |
---|
65 | |
---|
66 | def add_page(self): |
---|
67 | self.pg_no += 1 |
---|
68 | self.texts[self.pg_no] = {} |
---|
69 | |
---|
70 | def __setitem__(self, name, value): |
---|
71 | if name.lower() in self.keys: |
---|
72 | if not PY3K and isinstance(value, unicode): |
---|
73 | value = value.encode("latin1","ignore") |
---|
74 | elif value is None: |
---|
75 | value = "" |
---|
76 | else: |
---|
77 | value = str(value) |
---|
78 | self.texts[self.pg_no][name.lower()] = value |
---|
79 | |
---|
80 | # setitem shortcut (may be further extended) |
---|
81 | set = __setitem__ |
---|
82 | |
---|
83 | def has_key(self, name): |
---|
84 | return name.lower() in self.keys |
---|
85 | |
---|
86 | def __getitem__(self, name): |
---|
87 | if name in self.keys: |
---|
88 | key = name.lower() |
---|
89 | if key in self.texts: |
---|
90 | # text for this page: |
---|
91 | return self.texts[self.pg_no][key] |
---|
92 | else: |
---|
93 | # find first element for default text: |
---|
94 | elements = [element for element in self.elements |
---|
95 | if element['name'].lower() == key] |
---|
96 | if elements: |
---|
97 | return elements[0]['text'] |
---|
98 | |
---|
99 | def split_multicell(self, text, element_name): |
---|
100 | "Divide (\n) a string using a given element width" |
---|
101 | pdf = self.pdf |
---|
102 | element = [element for element in self.elements |
---|
103 | if element['name'].lower() == element_name.lower()][0] |
---|
104 | style = "" |
---|
105 | if element['bold']: style += "B" |
---|
106 | if element['italic']: style += "I" |
---|
107 | if element['underline']: style += "U" |
---|
108 | pdf.set_font(element['font'],style,element['size']) |
---|
109 | align = {'L':'L','R':'R','I':'L','D':'R','C':'C','':''}.get(element['align']) # D/I in spanish |
---|
110 | if isinstance(text, unicode) and not PY3K: |
---|
111 | text = text.encode("latin1","ignore") |
---|
112 | else: |
---|
113 | text = str(text) |
---|
114 | return pdf.multi_cell(w=element['x2']-element['x1'], |
---|
115 | h=element['y2']-element['y1'], |
---|
116 | txt=text,align=align,split_only=True) |
---|
117 | |
---|
118 | def render(self, outfile, dest="F"): |
---|
119 | pdf = self.pdf |
---|
120 | for pg in range(1, self.pg_no+1): |
---|
121 | pdf.add_page() |
---|
122 | pdf.set_font('Arial','B',16) |
---|
123 | pdf.set_auto_page_break(False,margin=0) |
---|
124 | |
---|
125 | for element in sorted(self.elements,key=lambda x: x['priority']): |
---|
126 | #print "dib",element['type'], element['name'], element['x1'], element['y1'], element['x2'], element['y2'] |
---|
127 | element = element.copy() |
---|
128 | element['text'] = self.texts[pg].get(element['name'].lower(), element['text']) |
---|
129 | if 'rotate' in element: |
---|
130 | pdf.rotate(element['rotate'], element['x1'], element['y1']) |
---|
131 | self.handlers[element['type'].upper()](pdf, **element) |
---|
132 | if 'rotate' in element: |
---|
133 | pdf.rotate(0) |
---|
134 | |
---|
135 | if dest: |
---|
136 | return pdf.output(outfile, dest) |
---|
137 | |
---|
138 | def text(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=10, |
---|
139 | bold=False, italic=False, underline=False, align="", |
---|
140 | foreground=0, backgroud=65535, multiline=None, |
---|
141 | *args, **kwargs): |
---|
142 | if text: |
---|
143 | if pdf.text_color!=rgb(foreground): |
---|
144 | pdf.set_text_color(*rgb(foreground)) |
---|
145 | if pdf.fill_color!=rgb(backgroud): |
---|
146 | pdf.set_fill_color(*rgb(backgroud)) |
---|
147 | |
---|
148 | font = font.strip().lower() |
---|
149 | if font == 'arial black': |
---|
150 | font = 'arial' |
---|
151 | style = "" |
---|
152 | for tag in 'B', 'I', 'U': |
---|
153 | if (text.startswith("<%s>" % tag) and text.endswith("</%s>" %tag)): |
---|
154 | text = text[3:-4] |
---|
155 | style += tag |
---|
156 | if bold: style += "B" |
---|
157 | if italic: style += "I" |
---|
158 | if underline: style += "U" |
---|
159 | align = {'L':'L','R':'R','I':'L','D':'R','C':'C','':''}.get(align) # D/I in spanish |
---|
160 | pdf.set_font(font,style,size) |
---|
161 | ##m_k = 72 / 2.54 |
---|
162 | ##h = (size/m_k) |
---|
163 | pdf.set_xy(x1,y1) |
---|
164 | if multiline is None: |
---|
165 | # multiline==None: write without wrapping/trimming (default) |
---|
166 | pdf.cell(w=x2-x1,h=y2-y1,txt=text,border=0,ln=0,align=align) |
---|
167 | elif multiline: |
---|
168 | # multiline==True: automatic word - warp |
---|
169 | pdf.multi_cell(w=x2-x1,h=y2-y1,txt=text,border=0,align=align) |
---|
170 | else: |
---|
171 | # multiline==False: trim to fit exactly the space defined |
---|
172 | text = pdf.multi_cell(w=x2-x1, h=y2-y1, |
---|
173 | txt=text, align=align, split_only=True)[0] |
---|
174 | print("trimming: *%s*" % text) |
---|
175 | pdf.cell(w=x2-x1,h=y2-y1,txt=text,border=0,ln=0,align=align) |
---|
176 | |
---|
177 | #pdf.Text(x=x1,y=y1,txt=text) |
---|
178 | |
---|
179 | def line(self, pdf, x1=0, y1=0, x2=0, y2=0, size=0, foreground=0, *args, **kwargs): |
---|
180 | if pdf.draw_color!=rgb(foreground): |
---|
181 | #print "SetDrawColor", hex(foreground) |
---|
182 | pdf.set_draw_color(*rgb(foreground)) |
---|
183 | #print "SetLineWidth", size |
---|
184 | pdf.set_line_width(size) |
---|
185 | pdf.line(x1, y1, x2, y2) |
---|
186 | |
---|
187 | def rect(self, pdf, x1=0, y1=0, x2=0, y2=0, size=0, foreground=0, backgroud=65535, *args, **kwargs): |
---|
188 | if pdf.draw_color!=rgb(foreground): |
---|
189 | pdf.set_draw_color(*rgb(foreground)) |
---|
190 | if pdf.fill_color!=rgb(backgroud): |
---|
191 | pdf.set_fill_color(*rgb(backgroud)) |
---|
192 | pdf.set_line_width(size) |
---|
193 | pdf.rect(x1, y1, x2-x1, y2-y1) |
---|
194 | |
---|
195 | def image(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', *args,**kwargs): |
---|
196 | if text: |
---|
197 | pdf.image(text,x1,y1,w=x2-x1,h=y2-y1,type='',link='') |
---|
198 | |
---|
199 | def barcode(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=1, |
---|
200 | foreground=0, *args, **kwargs): |
---|
201 | if pdf.draw_color!=rgb(foreground): |
---|
202 | pdf.set_draw_color(*rgb(foreground)) |
---|
203 | font = font.lower().strip() |
---|
204 | if font == 'interleaved 2of5 nt': |
---|
205 | pdf.interleaved2of5(text,x1,y1,w=size,h=y2-y1) |
---|
206 | |
---|
207 | # Added by Derek Schwalenberg Schwalenberg1013@gmail.com to allow (url) links in templates (using write method) 2014-02-22 |
---|
208 | def write(self, pdf, x1=0, y1=0, x2=0, y2=0, text='', font="arial", size=1, |
---|
209 | bold=False, italic=False, underline=False, align="", link='http://example.com', |
---|
210 | foreground=0, *args, **kwargs): |
---|
211 | if pdf.text_color!=rgb(foreground): |
---|
212 | pdf.set_text_color(*rgb(foreground)) |
---|
213 | font = font.strip().lower() |
---|
214 | if font == 'arial black': |
---|
215 | font = 'arial' |
---|
216 | style = "" |
---|
217 | for tag in 'B', 'I', 'U': |
---|
218 | if (text.startswith("<%s>" % tag) and text.endswith("</%s>" %tag)): |
---|
219 | text = text[3:-4] |
---|
220 | style += tag |
---|
221 | if bold: style += "B" |
---|
222 | if italic: style += "I" |
---|
223 | if underline: style += "U" |
---|
224 | align = {'L':'L','R':'R','I':'L','D':'R','C':'C','':''}.get(align) # D/I in spanish |
---|
225 | pdf.set_font(font,style,size) |
---|
226 | ##m_k = 72 / 2.54 |
---|
227 | ##h = (size/m_k) |
---|
228 | pdf.set_xy(x1,y1) |
---|
229 | pdf.write(5,text,link) |
---|