Changeset 9963 for main/waeup.kofa/trunk/src/waeup/kofa/browser/pdf.py
- Timestamp:
- 18 Feb 2013, 16:32:57 (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
main/waeup.kofa/trunk/src/waeup/kofa/browser/pdf.py
r9948 r9963 21 21 import grok 22 22 import os 23 import pytz 23 24 from cStringIO import StringIO 24 25 from datetime import datetime 26 from reportlab.lib import colors 25 27 from reportlab.lib.units import cm, inch, mm 26 28 from reportlab.lib.pagesizes import A4, landscape, portrait … … 32 34 from zope.i18n import translate 33 35 from zope.publisher.browser import TestRequest 34 from zope.component import getUtility 36 from zope.component import getUtility, queryUtility 35 37 from waeup.kofa.browser.interfaces import IPDFCreator 36 38 from waeup.kofa.utils.helpers import now … … 90 92 fontSize=10, 91 93 ) 94 95 #: Base style for signature tables 96 SIGNATURE_TABLE_STYLE = [ 97 ('VALIGN',(0,-1),(-1,-1),'TOP'), 98 #('FONT', (0,0), (-1,-1), 'Helvetica-BoldOblique', 12), 99 ('BOTTOMPADDING', (0,0), (-1,0), 36), 100 ('TOPPADDING', (0,-1), (-1,-1), 0), 101 ] 102 92 103 93 104 def format_html(html): … … 109 120 html = html.replace('\n', '<br />') 110 121 return html 122 123 def normalize_signature(signature_tuple): 124 """Normalize a signature tuple. 125 126 Returns a tuple ``(<PRE-TEXT>, <SIGNATURE>, <POST-TEXT>)`` from 127 input tuple. The following rules apply:: 128 129 (pre, sig, post) --> (pre, sig, post) 130 (pre, sig) --> (pre, sig, None) 131 (sig) --> (None, sig, None) 132 133 Also simple strings are accepted as input:: 134 135 sig --> (None, sig, None) 136 137 If input is not a tuple nor a basestring or if the tuple contains 138 an invalid number of elements, ``ValueError`` is raised. 139 """ 140 if isinstance(signature_tuple, basestring): 141 return (None, signature_tuple, None) 142 if not isinstance(signature_tuple, tuple): 143 raise ValueError("signature_tuple must be a string or tuple") 144 if len(signature_tuple) < 1 or len(signature_tuple) > 3: 145 raise ValueError("signature_tuple must have 1, 2, or 3 elements") 146 elif len(signature_tuple) == 1: 147 signature_tuple = (None, signature_tuple[0], None) 148 elif len(signature_tuple) == 2: 149 signature_tuple = (signature_tuple[0], signature_tuple[1], None) 150 return signature_tuple 151 152 def vert_signature_cell(signature, date_field=True, date_text=_('Date:'), 153 start_row=0, start_col=0, underline=True): 154 """Generate a table part containing a vertical signature cell. 155 156 Returns the table data as list of lists and an according style. 157 158 `signature`: 159 a signature tuple containing (<PRE-TEXT, SIGNATURE-TEXT, POST-TEXT>) 160 161 `date_field`: 162 boolean indicating that a 'Date:' text should be rendered into this 163 signature cell (or not). 164 165 `date_text`: 166 the text to be rendered into the signature field as 'Date:' text. 167 168 `start_row`: 169 starting row of the signature cell inside a broader table. 170 171 `start_col`: 172 starting column of the signature cell inside a broader table. 173 174 `underline`: 175 boolean indicating that the signature cell should provide a line on 176 top (`True` by default). 177 178 Vertical signature cells look like this:: 179 180 +------------+ 181 |Pre | 182 +------------+ 183 |Date: | 184 | | 185 +------------+ 186 | ---------- | 187 | Signature | 188 +------------+ 189 |Post | 190 +------------+ 191 """ 192 # split signature parts, replacing None with empty string 193 pre, sig, post = [x or '' for x in signature] 194 style = () 195 x, y = start_col, start_row+2 196 if underline: 197 style += (('LINEABOVE', (x, y), (x, y), 1, colors.black),) 198 d_text = date_field and date_text or '' 199 data = [[pre], [d_text], [sig], [post]] 200 col_widths = [1.0] 201 return data, style, col_widths 202 203 def horiz_signature_cell(signature, date_field=True, date_text=_('Date'), 204 start_row=0, start_col=0): 205 """Generate a table part containing an horizontal signature cell 206 207 Returns the table data as list of lists and an according style. 208 209 `signature`: 210 a signature tuple containing (<PRE-TEXT, SIGNATURE-TEXT, POST-TEXT>) 211 212 `date_field`: 213 boolean indicating that a 'Date:' text should be rendered into this 214 signature cell (or not). 215 216 `date_text`: 217 the text to be rendered into the signature field as 'Date:' text. 218 219 `start_row`: 220 starting row of the signature cell inside a broader table. 221 222 `start_col`: 223 starting column of the signature cell inside a broader table. 224 225 Horizontal signature cells look like this:: 226 227 +------------+---+-----------+ 228 |Pre text possibly filling | 229 |the whole box | 230 +------------+---+-----------+ 231 | | | | 232 | | | | 233 +------------+---+-----------+ 234 | ---------- | | --------- | 235 | Date | | Signature | 236 +------------+---+-----------+ 237 |Post | 238 +------------+---+-----------+ 239 240 """ 241 pre, sig, post = signature 242 if not date_field: 243 data, style, cols = vert_signature_cell(signature, date_field=False) 244 return data, style, cols 245 style = ( 246 # let pre and post text span the whole signature cell 247 ('SPAN', (start_col, start_row), (start_col+2, start_row)), 248 ('SPAN', (start_col, start_row+3), (start_col+2, start_row+3)), 249 ) 250 # horizontal cells are buildt from vertical ones chained together 251 cell1 = vert_signature_cell( # leftmost date col 252 (pre, date_text, post), date_field=False, 253 start_row=start_row, start_col=start_col) 254 cell2 = vert_signature_cell( # spacer col (between date and sig) 255 ('', '', ''), date_field=False, underline=False, 256 start_row=start_row, start_col=start_col+1) 257 cell3 = vert_signature_cell( # rightmost signature column 258 ('', sig, ''), date_field=False, 259 start_row=start_row, start_col=start_col+2) 260 data = map(lambda x, y, z: x+y+z, cell1[0], cell2[0], cell3[0]) 261 style = style + cell1[1] + cell2[1] + cell3[1] 262 col_widths = [0.3, 0.03, 0.67] # sums up to 1.0 263 return data, style, col_widths 264 265 def signature_row(signatures, start_row=0, horizontal=None, max_per_row=3): 266 data = [[], [], [], []] 267 style = () 268 signatures = [normalize_signature(sig) for sig in signatures] 269 start_col = 0 270 col_widths = [] 271 272 if horizontal is None: 273 horizontal = len(signatures) == 1 274 cell_maker = vert_signature_cell 275 if horizontal: 276 cell_maker = horiz_signature_cell 277 main_cell_height = not horizontal and 36 or 18 278 279 for sig in signatures: 280 sig_data, sig_style, sig_cols = cell_maker( 281 sig, start_row=start_row, start_col=start_col) 282 data = map(lambda x, y: x+y, data, sig_data) 283 style += sig_style 284 col_widths += sig_cols + [None,] 285 286 start_col += 1 287 # add spacer 288 spacer, spacer_style, cols = vert_signature_cell( 289 ('', '', ''), date_field=False, underline=False, 290 start_row=start_row, start_col=start_col) 291 data = map(lambda x, y: x+y, data, spacer) 292 style += spacer_style 293 start_col += 1 294 295 y = start_row 296 sig_row = start_row + 2 297 style = style + ( 298 ('TOPPADDING', (0, y+2), (-1, y+2), 0), # signature row 299 ('BOTTOMPADDING', (0, y+1), (-1, y+1), main_cell_height), 300 ('LEFTPADDING', (0, y), (-1, y), 1), # pre row 301 ('LEFTPADDING', (0, y+3), (-1, y+3), 1), # post row 302 ) 303 304 if len(signatures) == 1: 305 # pre and post text should span whole table 306 style += (('SPAN', (0, y), (-1, y)), 307 ('SPAN', (0, y+3), (-1, y+3)), 308 ) 309 310 if data[0] == [''] * len(data[0]): 311 # no pre text: hide pre row by minimizing padding 312 style += (('TOPPADDING', (0,y), (-1, y), -6), 313 ('BOTTOMPADDING', (0,y), (-1, y), -6), 314 ) 315 if data[-1] == [''] * len(data[0]): 316 # no post text: hide post row by minimizing padding 317 style += (('TOPPADDING', (0,y+3), (-1, y+3), -6), 318 ('BOTTOMPADDING', (0,y+3), (-1, y+3), -6), 319 ) 320 321 if len(signatures) > 1: 322 data = [x[:-1] for x in data] # strip last spacer 323 col_widths = col_widths[:-1] 324 return data, style, col_widths 325 326 def sig_table(signatures, lang='en', max_per_row=3, horizontal=None, 327 single_table=False, start_row=0): 328 space_width = 0.4 # width in cm of space between signatures 329 table_width = 16.0 # supposed width of signature table in cms 330 # width of signature cells in cm... 331 sig_num = len(signatures) 332 sig_col_width = (table_width - ((sig_num - 1) * space_width)) / sig_num 333 if sig_num == 1: 334 sig_col_width = 0.6 * table_width # signature cell part 335 space_width = table_width - sig_col_width # spacer part on the right 336 337 if sig_num > max_per_row: 338 sigs_by_row = [signatures[x:x+max_per_row] for x in range( 339 0, sig_num, max_per_row)] 340 result = [] 341 curr_row = 0 342 for num, row_sigs in enumerate(sigs_by_row): 343 curr_row = 0 344 if single_table: 345 curr_row = num * 4 346 result.append( 347 sig_table(row_sigs, lang=lang, max_per_row=max_per_row, 348 horizontal=False, start_row=curr_row)[0]) 349 missing_num = len(result[-2][0][0]) - len(result[-1][0][0]) 350 if missing_num: 351 # last row contained less cells, fix it... 352 result[-1] = ([x + [''] * missing_num for x in result[-1][0]], 353 result[-1][1], result[-2][2]) 354 return result 355 356 data, style, cols = signature_row(signatures, horizontal=horizontal, 357 start_row=start_row) 358 style += (('VALIGN', (0,0), (-1,-1), 'TOP'),) 359 360 # compute col widths... 361 col_widths = [] 362 for col in cols: 363 if col is not None: 364 col = col * sig_col_width * cm 365 else: 366 col = space_width * cm 367 col_widths.append(col) 368 369 # replace strings by paragraphs and translate all contents 370 for rnum, row in enumerate(data): 371 for cnum, cell in enumerate(row): 372 if cell: 373 content = translate(cell, lang) 374 data[rnum][cnum] = Paragraph(content, NORMAL_STYLE) 375 return [(data, style, col_widths),] 376 377 def get_sig_tables(signatures, lang='en', max_per_row=3, horizontal=None, 378 single_table=False): 379 rows = sig_table(signatures, lang=lang, max_per_row=max_per_row, 380 horizontal=horizontal, single_table=single_table) 381 if single_table: 382 result_data = [] 383 result_style = () 384 for row in rows: 385 data, style, col_widths = row 386 result_data += data 387 result_style += style 388 return [(result_data, result_style, col_widths),] 389 return rows 390 391 def get_signature_tables(signatures, lang='en', max_per_row=3, 392 horizontal=None, single_table=False): 393 """Get a list of reportlab flowables representing signature fields. 394 """ 395 data_list = get_sig_tables( 396 signatures, lang=lang, max_per_row=max_per_row, 397 horizontal=horizontal, single_table=single_table) 398 return [Table(row_data, style=row_style, colWidths=row_col_widths, 399 repeatRows=2) 400 for row_data, row_style, row_col_widths in data_list] 401 402 def format_signatures(signatures, max_per_row=3, lang='en', 403 single_table=False, 404 date_field=True, date_text=_('Date'), 405 base_style=SIGNATURE_TABLE_STYLE): 406 result = [] 407 signature_tuples = [normalize_signature(sig) for sig in signatures] 408 for pre, sig, post in signature_tuples: 409 row = [] 410 if pre is not None: 411 row.append([ 412 translate(pre, lang), '', '', '']) 413 row.append([ 414 translate(_('Date'), lang), '', 415 translate(sig, lang), '' 416 ]) 417 if post is not None: 418 row.append([ 419 translate(post, lang), '', '', '']) 420 result.append((row, base_style)) 421 return result 422 423 111 424 112 425 class NumberedCanvas(Canvas): … … 346 659 347 660 # Header 661 site_config = None 662 site = grok.getSite() 663 if site is not None: 664 site_config = site.get('configuration', None) 348 665 head_title = getattr( 349 666 doc, 'kofa_headtitle', getattr( 350 grok.getSite()['configuration'], 'name',667 site_config, 'name', 351 668 u'Sample University')) 352 669 canvas.setFont("Helvetica-Bold", 18) … … 379 696 canvas.setFont("Helvetica", 9) 380 697 if not getattr(doc, 'kofa_nodate', False): 381 tz = getUtility(IKofaUtils).tzinfo 698 tz = getattr(queryUtility(IKofaUtils), 'tzinfo', pytz.utc) 699 #tz = getUtility(IKofaUtils).tzinfo 382 700 today = now(tz).strftime('%d/%m/%Y %H:%M:%S %Z') 383 701 canvas.drawString(2.2*cm, 0.5 * inch,
Note: See TracChangeset for help on using the changeset viewer.