2009-10-20 10:52:23 +00:00
# -*- coding: utf-8 -*-
2008-06-16 11:00:21 +00:00
##############################################################################
2010-05-11 12:56:31 +00:00
#
2008-11-15 06:14:55 +00:00
# OpenERP, Open Source Management Solution
2009-10-14 12:32:15 +00:00
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 12:32:15 +00:00
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2009-10-14 12:32:15 +00:00
# GNU Affero General Public License for more details.
2008-06-16 11:00:21 +00:00
#
2009-10-14 12:32:15 +00:00
# You should have received a copy of the GNU Affero General Public License
2010-05-11 12:56:31 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
##############################################################################
2006-12-07 13:41:40 +00:00
import sys
import copy
import reportlab
2008-06-30 20:47:46 +00:00
import re
2006-12-07 13:41:40 +00:00
from reportlab . pdfgen import canvas
from reportlab import platypus
import utils
import color
import os
2010-11-23 15:11:24 +00:00
import logging
2009-04-28 10:24:21 +00:00
from lxml import etree
import base64
from reportlab . platypus . doctemplate import ActionFlowable
2011-02-07 12:57:23 +00:00
from openerp . tools . safe_eval import safe_eval as eval
2010-10-01 14:07:24 +00:00
from reportlab . lib . units import inch , cm , mm
2011-02-07 12:57:23 +00:00
from openerp . tools . misc import file_open
2010-11-26 18:30:43 +00:00
from reportlab . pdfbase import pdfmetrics
2011-12-21 09:24:46 +00:00
from reportlab . lib . pagesizes import A4 , letter
2010-11-23 15:14:44 +00:00
try :
from cStringIO import StringIO
_hush_pyflakes = [ StringIO ]
except ImportError :
from StringIO import StringIO
2012-01-24 12:55:12 +00:00
_logger = logging . getLogger ( __name__ )
2007-01-07 21:24:58 +00:00
encoding = ' utf-8 '
2006-12-07 13:41:40 +00:00
2013-03-11 15:51:47 +00:00
def select_fontname ( fontname , default_fontname ) :
if fontname not in pdfmetrics . getRegisteredFontNames ( ) \
or fontname not in pdfmetrics . standardFonts :
# let reportlab attempt to find it
try :
pdfmetrics . getFont ( fontname )
except Exception :
2013-12-09 15:14:54 +00:00
addition = " "
if " " in fontname :
addition = " . Your font contains spaces which is not valid in RML. "
_logger . warning ( ' Could not locate font %s , substituting default: %s %s ' ,
fontname , default_fontname , addition )
2013-03-11 15:51:47 +00:00
fontname = default_fontname
return fontname
2010-12-03 16:45:53 +00:00
def _open_image ( filename , path = None ) :
""" Attempt to open a binary file and return the descriptor
"""
if os . path . isfile ( filename ) :
return open ( filename , ' rb ' )
for p in ( path or [ ] ) :
if p and os . path . isabs ( p ) :
fullpath = os . path . join ( p , filename )
if os . path . isfile ( fullpath ) :
return open ( fullpath , ' rb ' )
try :
if p :
fullpath = os . path . join ( p , filename )
else :
fullpath = filename
return file_open ( fullpath )
except IOError :
pass
raise IOError ( " File %s cannot be found in image path " % filename )
2010-12-20 05:50:40 +00:00
2010-10-01 14:07:24 +00:00
class NumberedCanvas ( canvas . Canvas ) :
def __init__ ( self , * args , * * kwargs ) :
canvas . Canvas . __init__ ( self , * args , * * kwargs )
2013-05-10 15:19:34 +00:00
self . _saved_page_states = [ ]
2010-10-01 14:07:24 +00:00
def showPage ( self ) :
2013-05-10 15:19:34 +00:00
self . _saved_page_states . append ( dict ( self . __dict__ ) )
2010-10-01 14:07:24 +00:00
self . _startPage ( )
2013-05-10 15:19:34 +00:00
def save ( self ) :
""" add page info to each page (page x of y) """
for state in self . _saved_page_states :
self . __dict__ . update ( state )
2014-03-26 09:15:29 +00:00
self . draw_page_number ( )
2013-05-10 15:19:34 +00:00
canvas . Canvas . showPage ( self )
canvas . Canvas . save ( self )
2014-03-26 09:15:29 +00:00
def draw_page_number ( self ) :
page_count = len ( self . _saved_page_states )
2010-10-04 13:35:06 +00:00
self . setFont ( " Helvetica " , 8 )
2010-11-24 10:10:30 +00:00
self . drawRightString ( ( self . _pagesize [ 0 ] - 30 ) , ( self . _pagesize [ 1 ] - 40 ) ,
2012-04-24 06:10:09 +00:00
" %(this)i / %(total)i " % {
2014-05-21 09:20:37 +00:00
' this ' : self . _pageNumber ,
2013-05-10 15:19:34 +00:00
' total ' : page_count ,
2010-10-01 14:07:24 +00:00
}
)
2008-06-30 22:23:47 +00:00
class PageCount ( platypus . Flowable ) :
2012-07-16 07:52:17 +00:00
def __init__ ( self , story_count = 0 ) :
platypus . Flowable . __init__ ( self )
self . story_count = story_count
2008-07-22 14:24:36 +00:00
def draw ( self ) :
2012-12-14 12:38:03 +00:00
self . canv . beginForm ( " pageCount %d " % self . story_count )
2008-07-22 14:24:36 +00:00
self . canv . setFont ( " Helvetica " , utils . unit_get ( str ( 8 ) ) )
2010-09-27 13:17:41 +00:00
self . canv . drawString ( 0 , 0 , str ( self . canv . getPageNumber ( ) ) )
2008-07-22 14:24:36 +00:00
self . canv . endForm ( )
2008-06-30 22:23:47 +00:00
class PageReset ( platypus . Flowable ) :
2008-07-22 14:24:36 +00:00
def draw ( self ) :
2012-07-16 07:52:17 +00:00
self . canv . _doPageReset = True
2008-06-30 22:23:47 +00:00
2009-04-28 10:24:21 +00:00
class _rml_styles ( object , ) :
def __init__ ( self , nodes , localcontext ) :
self . localcontext = localcontext
2008-07-22 14:24:36 +00:00
self . styles = { }
2010-05-14 13:13:55 +00:00
self . styles_obj = { }
2008-07-22 14:24:36 +00:00
self . names = { }
self . table_styles = { }
2010-05-14 13:13:55 +00:00
self . default_style = reportlab . lib . styles . getSampleStyleSheet ( )
2008-07-22 14:24:36 +00:00
for node in nodes :
2009-04-28 10:24:21 +00:00
for style in node . findall ( ' blockTableStyle ' ) :
self . table_styles [ style . get ( ' id ' ) ] = self . _table_style_get ( style )
for style in node . findall ( ' paraStyle ' ) :
2010-05-14 13:13:55 +00:00
sname = style . get ( ' name ' )
self . styles [ sname ] = self . _para_style_update ( style )
2013-06-03 09:30:48 +00:00
if self . default_style . has_key ( sname ) :
2013-05-27 16:20:51 +00:00
for key , value in self . styles [ sname ] . items ( ) :
setattr ( self . default_style [ sname ] , key , value )
else :
self . styles_obj [ sname ] = reportlab . lib . styles . ParagraphStyle ( sname , self . default_style [ " Normal " ] , * * self . styles [ sname ] )
2009-04-28 10:24:21 +00:00
for variable in node . findall ( ' initialize ' ) :
for name in variable . findall ( ' name ' ) :
self . names [ name . get ( ' id ' ) ] = name . get ( ' value ' )
2008-07-22 14:24:36 +00:00
def _para_style_update ( self , node ) :
data = { }
for attr in [ ' textColor ' , ' backColor ' , ' bulletColor ' , ' borderColor ' ] :
2009-04-28 10:24:21 +00:00
if node . get ( attr ) :
data [ attr ] = color . get ( node . get ( attr ) )
2013-03-11 15:51:47 +00:00
for attr in [ ' bulletFontName ' , ' fontName ' ] :
if node . get ( attr ) :
fontname = select_fontname ( node . get ( attr ) , None )
if fontname is not None :
data [ ' fontName ' ] = fontname
for attr in [ ' bulletText ' ] :
2009-04-28 10:24:21 +00:00
if node . get ( attr ) :
data [ attr ] = node . get ( attr )
2008-08-11 09:27:55 +00:00
for attr in [ ' fontSize ' , ' leftIndent ' , ' rightIndent ' , ' spaceBefore ' , ' spaceAfter ' ,
2008-07-22 14:24:36 +00:00
' firstLineIndent ' , ' bulletIndent ' , ' bulletFontSize ' , ' leading ' ,
' borderWidth ' , ' borderPadding ' , ' borderRadius ' ] :
2009-04-28 10:24:21 +00:00
if node . get ( attr ) :
data [ attr ] = utils . unit_get ( node . get ( attr ) )
if node . get ( ' alignment ' ) :
2008-07-22 14:24:36 +00:00
align = {
' right ' : reportlab . lib . enums . TA_RIGHT ,
' center ' : reportlab . lib . enums . TA_CENTER ,
' justify ' : reportlab . lib . enums . TA_JUSTIFY
}
2009-04-28 10:24:21 +00:00
data [ ' alignment ' ] = align . get ( node . get ( ' alignment ' ) . lower ( ) , reportlab . lib . enums . TA_LEFT )
2008-07-22 14:24:36 +00:00
return data
def _table_style_get ( self , style_node ) :
styles = [ ]
2009-11-28 11:58:09 +00:00
for node in style_node :
2009-04-28 10:24:21 +00:00
start = utils . tuple_int_get ( node , ' start ' , ( 0 , 0 ) )
stop = utils . tuple_int_get ( node , ' stop ' , ( - 1 , - 1 ) )
if node . tag == ' blockValign ' :
styles . append ( ( ' VALIGN ' , start , stop , str ( node . get ( ' value ' ) ) ) )
elif node . tag == ' blockFont ' :
styles . append ( ( ' FONT ' , start , stop , str ( node . get ( ' name ' ) ) ) )
elif node . tag == ' blockTextColor ' :
styles . append ( ( ' TEXTCOLOR ' , start , stop , color . get ( str ( node . get ( ' colorName ' ) ) ) ) )
elif node . tag == ' blockLeading ' :
styles . append ( ( ' LEADING ' , start , stop , utils . unit_get ( node . get ( ' length ' ) ) ) )
elif node . tag == ' blockAlignment ' :
styles . append ( ( ' ALIGNMENT ' , start , stop , str ( node . get ( ' value ' ) ) ) )
elif node . tag == ' blockSpan ' :
styles . append ( ( ' SPAN ' , start , stop ) )
elif node . tag == ' blockLeftPadding ' :
styles . append ( ( ' LEFTPADDING ' , start , stop , utils . unit_get ( node . get ( ' length ' ) ) ) )
elif node . tag == ' blockRightPadding ' :
styles . append ( ( ' RIGHTPADDING ' , start , stop , utils . unit_get ( node . get ( ' length ' ) ) ) )
elif node . tag == ' blockTopPadding ' :
styles . append ( ( ' TOPPADDING ' , start , stop , utils . unit_get ( node . get ( ' length ' ) ) ) )
elif node . tag == ' blockBottomPadding ' :
styles . append ( ( ' BOTTOMPADDING ' , start , stop , utils . unit_get ( node . get ( ' length ' ) ) ) )
elif node . tag == ' blockBackground ' :
styles . append ( ( ' BACKGROUND ' , start , stop , color . get ( node . get ( ' colorName ' ) ) ) )
if node . get ( ' size ' ) :
styles . append ( ( ' FONTSIZE ' , start , stop , utils . unit_get ( node . get ( ' size ' ) ) ) )
elif node . tag == ' lineStyle ' :
kind = node . get ( ' kind ' )
kind_list = [ ' GRID ' , ' BOX ' , ' OUTLINE ' , ' INNERGRID ' , ' LINEBELOW ' , ' LINEABOVE ' , ' LINEBEFORE ' , ' LINEAFTER ' ]
assert kind in kind_list
thick = 1
if node . get ( ' thickness ' ) :
thick = float ( node . get ( ' thickness ' ) )
styles . append ( ( kind , start , stop , thick , color . get ( node . get ( ' colorName ' ) ) ) )
2008-07-22 14:24:36 +00:00
return platypus . tables . TableStyle ( styles )
def para_style_get ( self , node ) :
style = False
2010-05-14 13:13:55 +00:00
sname = node . get ( ' style ' )
if sname :
if sname in self . styles_obj :
style = self . styles_obj [ sname ]
2008-07-22 14:24:36 +00:00
else :
2013-11-08 21:27:09 +00:00
_logger . debug ( ' Warning: style not found, %s - setting default! ' , node . get ( ' style ' ) )
2008-07-22 14:24:36 +00:00
if not style :
2010-05-14 13:13:55 +00:00
style = self . default_style [ ' Normal ' ]
para_update = self . _para_style_update ( node )
if para_update :
# update style only is necessary
style = copy . deepcopy ( style )
style . __dict__ . update ( para_update )
2008-07-22 14:24:36 +00:00
return style
2006-12-07 13:41:40 +00:00
class _rml_doc ( object ) :
2011-11-21 13:33:46 +00:00
def __init__ ( self , node , localcontext = None , images = None , path = ' . ' , title = None ) :
2011-11-07 15:45:56 +00:00
if images is None :
images = { }
2011-11-21 13:33:46 +00:00
if localcontext is None :
localcontext = { }
2009-04-28 10:24:21 +00:00
self . localcontext = localcontext
self . etree = node
self . filename = self . etree . get ( ' filename ' )
2008-07-22 14:24:36 +00:00
self . images = images
self . path = path
2008-09-24 15:34:10 +00:00
self . title = title
2008-07-22 14:24:36 +00:00
def docinit ( self , els ) :
from reportlab . lib . fonts import addMapping
from reportlab . pdfbase import pdfmetrics
from reportlab . pdfbase . ttfonts import TTFont
for node in els :
2013-05-27 16:20:51 +00:00
2009-04-28 10:24:21 +00:00
for font in node . findall ( ' registerFont ' ) :
name = font . get ( ' fontName ' ) . encode ( ' ascii ' )
fname = font . get ( ' fontFile ' ) . encode ( ' ascii ' )
2010-12-20 05:50:40 +00:00
if name not in pdfmetrics . _fonts :
2010-12-29 15:51:33 +00:00
pdfmetrics . registerFont ( TTFont ( name , fname ) )
2013-05-31 11:46:12 +00:00
#by default, we map the fontName to each style (bold, italic, bold and italic), so that
#if there isn't any font defined for one of these style (via a font family), the system
#will fallback on the normal font.
2008-07-22 14:24:36 +00:00
addMapping ( name , 0 , 0 , name ) #normal
addMapping ( name , 0 , 1 , name ) #italic
addMapping ( name , 1 , 0 , name ) #bold
addMapping ( name , 1 , 1 , name ) #italic and bold
2013-05-31 11:46:12 +00:00
#if registerFontFamily is defined, we register the mapping of the fontName to use for each style.
for font_family in node . findall ( ' registerFontFamily ' ) :
family_name = font_family . get ( ' normal ' ) . encode ( ' ascii ' )
if font_family . get ( ' italic ' ) :
addMapping ( family_name , 0 , 1 , font_family . get ( ' italic ' ) . encode ( ' ascii ' ) )
if font_family . get ( ' bold ' ) :
addMapping ( family_name , 1 , 0 , font_family . get ( ' bold ' ) . encode ( ' ascii ' ) )
if font_family . get ( ' boldItalic ' ) :
addMapping ( family_name , 1 , 1 , font_family . get ( ' boldItalic ' ) . encode ( ' ascii ' ) )
2010-11-23 15:14:44 +00:00
def setTTFontMapping ( self , face , fontname , filename , mode = ' all ' ) :
2009-02-07 19:26:06 +00:00
from reportlab . lib . fonts import addMapping
from reportlab . pdfbase import pdfmetrics
from reportlab . pdfbase . ttfonts import TTFont
2010-05-11 12:56:31 +00:00
2014-04-01 09:45:56 +00:00
if mode :
mode = mode . lower ( )
2010-12-20 05:50:40 +00:00
if fontname not in pdfmetrics . _fonts :
2010-12-29 15:51:33 +00:00
pdfmetrics . registerFont ( TTFont ( fontname , filename ) )
2012-12-14 12:38:03 +00:00
if mode == ' all ' :
2009-11-24 14:44:05 +00:00
addMapping ( face , 0 , 0 , fontname ) #normal
addMapping ( face , 0 , 1 , fontname ) #italic
addMapping ( face , 1 , 0 , fontname ) #bold
addMapping ( face , 1 , 1 , fontname ) #italic and bold
2014-04-01 09:45:56 +00:00
elif mode in [ ' italic ' , ' oblique ' ] :
2009-11-24 14:44:05 +00:00
addMapping ( face , 0 , 1 , fontname ) #italic
2012-12-14 12:38:03 +00:00
elif mode == ' bold ' :
2009-11-24 14:44:05 +00:00
addMapping ( face , 1 , 0 , fontname ) #bold
2014-04-01 09:45:56 +00:00
elif mode in ( ' bolditalic ' , ' bold italic ' , ' boldoblique ' , ' bold oblique ' ) :
2009-11-24 14:44:05 +00:00
addMapping ( face , 1 , 1 , fontname ) #italic and bold
2014-04-01 09:45:56 +00:00
else :
addMapping ( face , 0 , 0 , fontname ) #normal
2009-02-07 19:26:06 +00:00
2008-07-22 14:24:36 +00:00
def _textual_image ( self , node ) :
rc = ' '
2009-11-28 11:58:09 +00:00
for n in node :
2009-04-28 10:24:21 +00:00
rc + = ( etree . tostring ( n ) or ' ' ) + n . tail
return base64 . decodestring ( node . tostring ( ) )
2008-07-22 14:24:36 +00:00
def _images ( self , el ) :
result = { }
2010-12-06 12:40:52 +00:00
for node in el . findall ( ' .//image ' ) :
2009-04-28 10:24:21 +00:00
rc = ( node . text or ' ' )
result [ node . get ( ' name ' ) ] = base64 . decodestring ( rc )
2008-07-22 14:24:36 +00:00
return result
def render ( self , out ) :
2010-12-06 12:40:52 +00:00
el = self . etree . findall ( ' .//docinit ' )
2008-07-22 14:24:36 +00:00
if el :
self . docinit ( el )
2010-12-06 12:40:52 +00:00
el = self . etree . findall ( ' .//stylesheet ' )
2009-04-28 10:24:21 +00:00
self . styles = _rml_styles ( el , self . localcontext )
2008-07-22 14:24:36 +00:00
2010-12-06 12:40:52 +00:00
el = self . etree . findall ( ' .//images ' )
2008-07-22 14:24:36 +00:00
if el :
self . images . update ( self . _images ( el [ 0 ] ) )
2010-12-06 12:19:35 +00:00
2010-12-06 12:40:52 +00:00
el = self . etree . findall ( ' .//template ' )
2008-07-22 14:24:36 +00:00
if len ( el ) :
2009-04-28 10:24:21 +00:00
pt_obj = _rml_template ( self . localcontext , out , el [ 0 ] , self , images = self . images , path = self . path , title = self . title )
el = utils . _child_get ( self . etree , self , ' story ' )
pt_obj . render ( el )
2008-07-22 14:24:36 +00:00
else :
self . canvas = canvas . Canvas ( out )
2009-04-28 10:24:21 +00:00
pd = self . etree . find ( ' pageDrawing ' ) [ 0 ]
pd_obj = _rml_canvas ( self . canvas , self . localcontext , None , self , self . images , path = self . path , title = self . title )
2008-07-22 14:24:36 +00:00
pd_obj . render ( pd )
self . canvas . showPage ( )
self . canvas . save ( )
2006-12-07 13:41:40 +00:00
class _rml_canvas ( object ) :
2011-11-07 15:45:56 +00:00
def __init__ ( self , canvas , localcontext , doc_tmpl = None , doc = None , images = None , path = ' . ' , title = None ) :
if images is None :
images = { }
2009-04-28 10:24:21 +00:00
self . localcontext = localcontext
2008-07-22 14:24:36 +00:00
self . canvas = canvas
self . styles = doc . styles
self . doc_tmpl = doc_tmpl
self . doc = doc
self . images = images
self . path = path
2008-09-24 15:34:10 +00:00
self . title = title
if self . title :
self . canvas . setTitle ( self . title )
2008-07-22 14:24:36 +00:00
def _textual ( self , node , x = 0 , y = 0 ) :
2009-08-17 12:37:47 +00:00
text = node . text and node . text . encode ( ' utf-8 ' ) or ' '
rc = utils . _process_text ( self , text )
2009-04-28 10:24:21 +00:00
for n in node :
if n . tag == ' seq ' :
from reportlab . lib . sequencer import getSequencer
seq = getSequencer ( )
rc + = str ( seq . next ( n . get ( ' id ' ) ) )
if n . tag == ' pageCount ' :
if x or y :
self . canvas . translate ( x , y )
2012-07-16 07:52:17 +00:00
self . canvas . doForm ( ' pageCount %s ' % ( self . canvas . _storyCount , ) )
2009-04-28 10:24:21 +00:00
if x or y :
self . canvas . translate ( - x , - y )
if n . tag == ' pageNumber ' :
rc + = str ( self . canvas . getPageNumber ( ) )
rc + = utils . _process_text ( self , n . tail )
2010-09-27 10:09:55 +00:00
return rc . replace ( ' \n ' , ' ' )
2008-07-22 14:24:36 +00:00
def _drawString ( self , node ) :
v = utils . attr_get ( node , [ ' x ' , ' y ' ] )
2009-04-28 10:24:21 +00:00
text = self . _textual ( node , * * v )
2009-06-26 11:27:44 +00:00
text = utils . xml2str ( text )
2014-07-02 15:29:13 +00:00
try :
self . canvas . drawString ( text = text , * * v )
2014-07-04 15:07:53 +00:00
except TypeError :
2014-07-03 07:36:46 +00:00
_logger . error ( " Bad RML: <drawString> tag requires attributes ' x ' and ' y ' ! " )
2014-07-04 15:07:53 +00:00
raise
2009-04-28 10:24:21 +00:00
2008-07-22 14:24:36 +00:00
def _drawCenteredString ( self , node ) :
v = utils . attr_get ( node , [ ' x ' , ' y ' ] )
2009-04-28 10:24:21 +00:00
text = self . _textual ( node , * * v )
2009-06-26 11:27:44 +00:00
text = utils . xml2str ( text )
2009-04-28 10:24:21 +00:00
self . canvas . drawCentredString ( text = text , * * v )
2008-07-22 14:24:36 +00:00
def _drawRightString ( self , node ) :
v = utils . attr_get ( node , [ ' x ' , ' y ' ] )
2009-04-28 10:24:21 +00:00
text = self . _textual ( node , * * v )
2009-06-26 11:27:44 +00:00
text = utils . xml2str ( text )
2009-04-28 10:24:21 +00:00
self . canvas . drawRightString ( text = text , * * v )
2008-07-22 14:24:36 +00:00
def _rect ( self , node ) :
2009-04-28 10:24:21 +00:00
if node . get ( ' round ' ) :
self . canvas . roundRect ( radius = utils . unit_get ( node . get ( ' round ' ) ) , * * utils . attr_get ( node , [ ' x ' , ' y ' , ' width ' , ' height ' ] , { ' fill ' : ' bool ' , ' stroke ' : ' bool ' } ) )
2008-07-22 14:24:36 +00:00
else :
self . canvas . rect ( * * utils . attr_get ( node , [ ' x ' , ' y ' , ' width ' , ' height ' ] , { ' fill ' : ' bool ' , ' stroke ' : ' bool ' } ) )
def _ellipse ( self , node ) :
2009-04-28 10:24:21 +00:00
x1 = utils . unit_get ( node . get ( ' x ' ) )
x2 = utils . unit_get ( node . get ( ' width ' ) )
y1 = utils . unit_get ( node . get ( ' y ' ) )
y2 = utils . unit_get ( node . get ( ' height ' ) )
2008-07-22 14:24:36 +00:00
self . canvas . ellipse ( x1 , y1 , x2 , y2 , * * utils . attr_get ( node , [ ] , { ' fill ' : ' bool ' , ' stroke ' : ' bool ' } ) )
2009-04-28 10:24:21 +00:00
2008-07-22 14:24:36 +00:00
def _curves ( self , node ) :
2009-04-28 10:24:21 +00:00
line_str = node . text . split ( )
2008-07-22 14:24:36 +00:00
lines = [ ]
while len ( line_str ) > 7 :
self . canvas . bezier ( * [ utils . unit_get ( l ) for l in line_str [ 0 : 8 ] ] )
line_str = line_str [ 8 : ]
2009-04-28 10:24:21 +00:00
2008-07-22 14:24:36 +00:00
def _lines ( self , node ) :
2009-04-28 10:24:21 +00:00
line_str = node . text . split ( )
2008-07-22 14:24:36 +00:00
lines = [ ]
while len ( line_str ) > 3 :
lines . append ( [ utils . unit_get ( l ) for l in line_str [ 0 : 4 ] ] )
line_str = line_str [ 4 : ]
self . canvas . lines ( lines )
2009-04-28 10:24:21 +00:00
2008-07-22 14:24:36 +00:00
def _grid ( self , node ) :
2009-04-28 10:24:21 +00:00
xlist = [ utils . unit_get ( s ) for s in node . get ( ' xs ' ) . split ( ' , ' ) ]
ylist = [ utils . unit_get ( s ) for s in node . get ( ' ys ' ) . split ( ' , ' ) ]
2008-07-22 14:24:36 +00:00
self . canvas . grid ( xlist , ylist )
2009-04-28 10:24:21 +00:00
2008-07-22 14:24:36 +00:00
def _translate ( self , node ) :
2009-04-28 10:24:21 +00:00
dx = utils . unit_get ( node . get ( ' dx ' ) ) or 0
dy = utils . unit_get ( node . get ( ' dy ' ) ) or 0
2008-07-22 14:24:36 +00:00
self . canvas . translate ( dx , dy )
def _circle ( self , node ) :
2009-04-28 10:24:21 +00:00
self . canvas . circle ( x_cen = utils . unit_get ( node . get ( ' x ' ) ) , y_cen = utils . unit_get ( node . get ( ' y ' ) ) , r = utils . unit_get ( node . get ( ' radius ' ) ) , * * utils . attr_get ( node , [ ] , { ' fill ' : ' bool ' , ' stroke ' : ' bool ' } ) )
2008-07-22 14:24:36 +00:00
def _place ( self , node ) :
2013-04-08 13:25:10 +00:00
flows = _rml_flowable ( self . doc , self . localcontext , images = self . images , path = self . path , title = self . title , canvas = self . canvas ) . render ( node )
2008-07-22 14:24:36 +00:00
infos = utils . attr_get ( node , [ ' x ' , ' y ' , ' width ' , ' height ' ] )
infos [ ' y ' ] + = infos [ ' height ' ]
for flow in flows :
w , h = flow . wrap ( infos [ ' width ' ] , infos [ ' height ' ] )
if w < = infos [ ' width ' ] and h < = infos [ ' height ' ] :
infos [ ' y ' ] - = h
flow . drawOn ( self . canvas , infos [ ' x ' ] , infos [ ' y ' ] )
infos [ ' height ' ] - = h
else :
2012-02-08 17:02:17 +00:00
raise ValueError ( " Not enough space " )
2008-07-22 14:24:36 +00:00
def _line_mode ( self , node ) :
ljoin = { ' round ' : 1 , ' mitered ' : 0 , ' bevelled ' : 2 }
lcap = { ' default ' : 0 , ' round ' : 1 , ' square ' : 2 }
2009-04-28 10:24:21 +00:00
if node . get ( ' width ' ) :
self . canvas . setLineWidth ( utils . unit_get ( node . get ( ' width ' ) ) )
if node . get ( ' join ' ) :
self . canvas . setLineJoin ( ljoin [ node . get ( ' join ' ) ] )
if node . get ( ' cap ' ) :
self . canvas . setLineCap ( lcap [ node . get ( ' cap ' ) ] )
if node . get ( ' miterLimit ' ) :
self . canvas . setDash ( utils . unit_get ( node . get ( ' miterLimit ' ) ) )
if node . get ( ' dash ' ) :
dashes = node . get ( ' dash ' ) . split ( ' , ' )
2008-07-22 14:24:36 +00:00
for x in range ( len ( dashes ) ) :
dashes [ x ] = utils . unit_get ( dashes [ x ] )
2009-04-28 10:24:21 +00:00
self . canvas . setDash ( node . get ( ' dash ' ) . split ( ' , ' ) )
2008-07-22 14:24:36 +00:00
def _image ( self , node ) :
import urllib
2010-12-03 16:45:53 +00:00
import urlparse
2008-07-22 14:24:36 +00:00
from reportlab . lib . utils import ImageReader
2010-12-03 16:45:53 +00:00
nfile = node . get ( ' file ' )
if not nfile :
2009-04-28 10:24:21 +00:00
if node . get ( ' name ' ) :
image_data = self . images [ node . get ( ' name ' ) ]
2012-01-24 12:55:12 +00:00
_logger . debug ( " Image %s used " , node . get ( ' name ' ) )
2010-11-23 15:14:44 +00:00
s = StringIO ( image_data )
2008-07-22 14:24:36 +00:00
else :
2011-05-30 09:03:35 +00:00
newtext = node . text
2009-04-28 10:24:21 +00:00
if self . localcontext :
2011-05-30 09:03:35 +00:00
res = utils . _regex . findall ( newtext )
2010-08-30 09:59:46 +00:00
for key in res :
2011-05-30 09:03:35 +00:00
newtext = eval ( key , { } , self . localcontext ) or ' '
2010-08-30 09:59:46 +00:00
image_data = None
2011-05-30 09:03:35 +00:00
if newtext :
image_data = base64 . decodestring ( newtext )
2010-08-30 09:59:46 +00:00
if image_data :
2010-11-23 15:14:44 +00:00
s = StringIO ( image_data )
2010-08-30 09:59:46 +00:00
else :
2012-01-24 12:55:12 +00:00
_logger . debug ( " No image data! " )
2010-08-30 09:59:46 +00:00
return False
2008-07-22 14:24:36 +00:00
else :
2010-12-03 16:45:53 +00:00
if nfile in self . images :
s = StringIO ( self . images [ nfile ] )
2008-07-22 14:24:36 +00:00
else :
try :
2010-12-03 16:45:53 +00:00
up = urlparse . urlparse ( str ( nfile ) )
except ValueError :
up = False
if up and up . scheme :
# RFC: do we really want to open external URLs?
# Are we safe from cross-site scripting or attacks?
2012-01-24 12:55:12 +00:00
_logger . debug ( " Retrieve image from %s " , nfile )
2010-12-03 16:45:53 +00:00
u = urllib . urlopen ( str ( nfile ) )
2010-11-23 15:14:44 +00:00
s = StringIO ( u . read ( ) )
2010-12-03 16:45:53 +00:00
else :
2012-01-24 12:55:12 +00:00
_logger . debug ( " Open image file %s " , nfile )
2010-12-03 16:45:53 +00:00
s = _open_image ( nfile , path = self . path )
2011-01-04 10:13:35 +00:00
try :
img = ImageReader ( s )
( sx , sy ) = img . getSize ( )
2012-01-24 12:55:12 +00:00
_logger . debug ( " Image is %d x %d " , sx , sy )
2012-12-18 11:50:43 +00:00
args = { ' x ' : 0.0 , ' y ' : 0.0 , ' mask ' : ' auto ' }
2011-01-04 10:13:35 +00:00
for tag in ( ' width ' , ' height ' , ' x ' , ' y ' ) :
if node . get ( tag ) :
args [ tag ] = utils . unit_get ( node . get ( tag ) )
if ( ' width ' in args ) and ( not ' height ' in args ) :
2008-07-22 14:24:36 +00:00
args [ ' height ' ] = sy * args [ ' width ' ] / sx
2011-01-04 10:13:35 +00:00
elif ( ' height ' in args ) and ( not ' width ' in args ) :
args [ ' width ' ] = sx * args [ ' height ' ] / sy
elif ( ' width ' in args ) and ( ' height ' in args ) :
if ( float ( args [ ' width ' ] ) / args [ ' height ' ] ) > ( float ( sx ) > sy ) :
args [ ' width ' ] = sx * args [ ' height ' ] / sy
else :
args [ ' height ' ] = sy * args [ ' width ' ] / sx
self . canvas . drawImage ( img , * * args )
finally :
s . close ( )
2010-10-04 13:35:06 +00:00
# self.canvas._doc.SaveToFile(self.canvas._filename, self.canvas)
2008-07-22 14:24:36 +00:00
def _path ( self , node ) :
self . path = self . canvas . beginPath ( )
self . path . moveTo ( * * utils . attr_get ( node , [ ' x ' , ' y ' ] ) )
2009-04-28 10:24:21 +00:00
for n in utils . _child_get ( node , self ) :
if not n . text :
if n . tag == ' moveto ' :
2008-07-22 14:24:36 +00:00
vals = utils . text_get ( n ) . split ( )
self . path . moveTo ( utils . unit_get ( vals [ 0 ] ) , utils . unit_get ( vals [ 1 ] ) )
2009-04-28 10:24:21 +00:00
elif n . tag == ' curvesto ' :
2008-07-22 14:24:36 +00:00
vals = utils . text_get ( n ) . split ( )
while len ( vals ) > 5 :
pos = [ ]
while len ( pos ) < 6 :
pos . append ( utils . unit_get ( vals . pop ( 0 ) ) )
self . path . curveTo ( * pos )
2009-04-28 10:24:21 +00:00
elif n . text :
data = n . text . split ( ) # Not sure if I must merge all TEXT_NODE ?
2008-07-22 14:24:36 +00:00
while len ( data ) > 1 :
x = utils . unit_get ( data . pop ( 0 ) )
y = utils . unit_get ( data . pop ( 0 ) )
self . path . lineTo ( x , y )
2009-04-28 10:24:21 +00:00
if ( not node . get ( ' close ' ) ) or utils . bool_get ( node . get ( ' close ' ) ) :
2008-07-22 14:24:36 +00:00
self . path . close ( )
self . canvas . drawPath ( self . path , * * utils . attr_get ( node , [ ] , { ' fill ' : ' bool ' , ' stroke ' : ' bool ' } ) )
2009-11-26 06:30:20 +00:00
def setFont ( self , node ) :
2013-03-11 15:51:47 +00:00
fontname = select_fontname ( node . get ( ' name ' ) , self . canvas . _fontname )
2010-11-26 18:30:43 +00:00
return self . canvas . setFont ( fontname , utils . unit_get ( node . get ( ' size ' ) ) )
2009-11-26 06:30:20 +00:00
2008-07-22 14:24:36 +00:00
def render ( self , node ) :
tags = {
' drawCentredString ' : self . _drawCenteredString ,
' drawRightString ' : self . _drawRightString ,
' drawString ' : self . _drawString ,
' rect ' : self . _rect ,
' ellipse ' : self . _ellipse ,
' lines ' : self . _lines ,
' grid ' : self . _grid ,
' curves ' : self . _curves ,
2009-04-28 10:24:21 +00:00
' fill ' : lambda node : self . canvas . setFillColor ( color . get ( node . get ( ' color ' ) ) ) ,
' stroke ' : lambda node : self . canvas . setStrokeColor ( color . get ( node . get ( ' color ' ) ) ) ,
2009-11-26 06:30:20 +00:00
' setFont ' : self . setFont ,
2008-07-22 14:24:36 +00:00
' place ' : self . _place ,
' circle ' : self . _circle ,
' lineMode ' : self . _line_mode ,
' path ' : self . _path ,
2009-04-28 10:24:21 +00:00
' rotate ' : lambda node : self . canvas . rotate ( float ( node . get ( ' degrees ' ) ) ) ,
2008-07-22 14:24:36 +00:00
' translate ' : self . _translate ,
' image ' : self . _image
}
2009-04-28 10:24:21 +00:00
for n in utils . _child_get ( node , self ) :
if n . tag in tags :
tags [ n . tag ] ( n )
2006-12-07 13:41:40 +00:00
class _rml_draw ( object ) :
2011-11-07 15:45:56 +00:00
def __init__ ( self , localcontext , node , styles , images = None , path = ' . ' , title = None ) :
if images is None :
images = { }
2009-04-28 10:24:21 +00:00
self . localcontext = localcontext
2008-07-22 14:24:36 +00:00
self . node = node
self . styles = styles
self . canvas = None
self . images = images
self . path = path
2008-09-24 15:34:10 +00:00
self . canvas_title = title
2008-07-22 14:24:36 +00:00
def render ( self , canvas , doc ) :
canvas . saveState ( )
2009-07-22 10:46:16 +00:00
cnv = _rml_canvas ( canvas , self . localcontext , doc , self . styles , images = self . images , path = self . path , title = self . canvas_title )
2008-07-22 14:24:36 +00:00
cnv . render ( self . node )
canvas . restoreState ( )
2006-12-07 13:41:40 +00:00
2010-12-03 16:45:53 +00:00
class _rml_Illustration ( platypus . flowables . Flowable ) :
def __init__ ( self , node , localcontext , styles , self2 ) :
self . localcontext = ( localcontext or { } ) . copy ( )
self . node = node
self . styles = styles
self . width = utils . unit_get ( node . get ( ' width ' ) )
self . height = utils . unit_get ( node . get ( ' height ' ) )
self . self2 = self2
def wrap ( self , * args ) :
2012-12-14 12:38:03 +00:00
return self . width , self . height
2010-12-03 16:45:53 +00:00
def draw ( self ) :
drw = _rml_draw ( self . localcontext , self . node , self . styles , images = self . self2 . images , path = self . self2 . path , title = self . self2 . title )
drw . render ( self . canv , None )
2013-11-12 13:04:12 +00:00
# Workaround for issue #15: https://bitbucket.org/rptlab/reportlab/issue/15/infinite-pages-produced-when-splitting
original_pto_split = platypus . flowables . PTOContainer . split
def split ( self , availWidth , availHeight ) :
res = original_pto_split ( self , availWidth , availHeight )
if len ( res ) > 2 and len ( self . _content ) > 0 :
header = self . _content [ 0 ] . _ptoinfo . header
trailer = self . _content [ 0 ] . _ptoinfo . trailer
if isinstance ( res [ - 2 ] , platypus . flowables . UseUpSpace ) and len ( header + trailer ) == len ( res [ : - 2 ] ) :
return [ ]
return res
platypus . flowables . PTOContainer . split = split
2006-12-07 13:41:40 +00:00
class _rml_flowable ( object ) :
2013-04-08 13:25:10 +00:00
def __init__ ( self , doc , localcontext , images = None , path = ' . ' , title = None , canvas = None ) :
2011-11-07 15:45:56 +00:00
if images is None :
images = { }
2009-04-28 10:24:21 +00:00
self . localcontext = localcontext
2008-07-22 14:24:36 +00:00
self . doc = doc
self . styles = doc . styles
2011-11-07 15:45:56 +00:00
self . images = images
2008-07-22 14:24:36 +00:00
self . path = path
2008-09-24 15:34:10 +00:00
self . title = title
2013-04-08 13:25:10 +00:00
self . canvas = canvas
2008-07-22 14:24:36 +00:00
def _textual ( self , node ) :
2009-04-28 10:24:21 +00:00
rc1 = utils . _process_text ( self , node . text or ' ' )
for n in utils . _child_get ( node , self ) :
2009-05-25 05:24:18 +00:00
txt_n = copy . deepcopy ( n )
for key in txt_n . attrib . keys ( ) :
if key in ( ' rml_except ' , ' rml_loop ' , ' rml_tag ' ) :
del txt_n . attrib [ key ]
2011-11-04 15:57:59 +00:00
if not n . tag == ' bullet ' :
2013-04-08 13:25:10 +00:00
if n . tag == ' pageNumber ' :
txt_n . text = self . canvas and str ( self . canvas . getPageNumber ( ) ) or ' '
else :
txt_n . text = utils . xml2str ( self . _textual ( n ) )
2011-11-04 15:57:59 +00:00
txt_n . tail = n . tail and utils . xml2str ( utils . _process_text ( self , n . tail . replace ( ' \n ' , ' ' ) ) ) or ' '
rc1 + = etree . tostring ( txt_n )
2009-04-28 10:24:21 +00:00
return rc1
2008-07-22 14:24:36 +00:00
def _table ( self , node ) :
2010-03-24 16:32:22 +00:00
children = utils . _child_get ( node , self , ' tr ' )
if not children :
2009-04-28 10:24:21 +00:00
return None
2008-07-22 14:24:36 +00:00
length = 0
colwidths = None
rowheights = None
data = [ ]
styles = [ ]
2009-04-28 10:24:21 +00:00
posy = 0
2010-03-24 16:32:22 +00:00
for tr in children :
2008-07-22 14:24:36 +00:00
paraStyle = None
2009-04-28 10:24:21 +00:00
if tr . get ( ' style ' ) :
st = copy . deepcopy ( self . styles . table_styles [ tr . get ( ' style ' ) ] )
2011-06-14 08:27:11 +00:00
for si in range ( len ( st . _cmds ) ) :
s = list ( st . _cmds [ si ] )
s [ 1 ] = ( s [ 1 ] [ 0 ] , posy )
s [ 2 ] = ( s [ 2 ] [ 0 ] , posy )
st . _cmds [ si ] = tuple ( s )
2008-07-22 14:24:36 +00:00
styles . append ( st )
2009-04-28 10:24:21 +00:00
if tr . get ( ' paraStyle ' ) :
paraStyle = self . styles . styles [ tr . get ( ' paraStyle ' ) ]
2008-07-22 14:24:36 +00:00
data2 = [ ]
posx = 0
2009-04-28 10:24:21 +00:00
for td in utils . _child_get ( tr , self , ' td ' ) :
if td . get ( ' style ' ) :
st = copy . deepcopy ( self . styles . table_styles [ td . get ( ' style ' ) ] )
2008-07-22 14:24:36 +00:00
for s in st . _cmds :
s [ 1 ] [ 1 ] = posy
s [ 2 ] [ 1 ] = posy
s [ 1 ] [ 0 ] = posx
s [ 2 ] [ 0 ] = posx
styles . append ( st )
2009-04-28 10:24:21 +00:00
if td . get ( ' paraStyle ' ) :
2008-07-22 14:24:36 +00:00
# TODO: merge styles
2009-04-28 10:24:21 +00:00
paraStyle = self . styles . styles [ td . get ( ' paraStyle ' ) ]
2008-07-22 14:24:36 +00:00
posx + = 1
flow = [ ]
2009-04-28 10:24:21 +00:00
for n in utils . _child_get ( td , self ) :
2009-05-25 06:42:45 +00:00
if n . tag == etree . Comment :
n . text = ' '
continue
2009-04-28 10:24:21 +00:00
fl = self . _flowable ( n , extra_style = paraStyle )
if isinstance ( fl , list ) :
flow + = fl
else :
2008-07-22 14:24:36 +00:00
flow . append ( fl )
2009-04-28 10:24:21 +00:00
2008-07-22 14:24:36 +00:00
if not len ( flow ) :
flow = self . _textual ( td )
data2 . append ( flow )
if len ( data2 ) > length :
length = len ( data2 )
for ab in data :
while len ( ab ) < length :
ab . append ( ' ' )
while len ( data2 ) < length :
data2 . append ( ' ' )
data . append ( data2 )
posy + = 1
2009-04-28 10:24:21 +00:00
if node . get ( ' colWidths ' ) :
assert length == len ( node . get ( ' colWidths ' ) . split ( ' , ' ) )
colwidths = [ utils . unit_get ( f . strip ( ) ) for f in node . get ( ' colWidths ' ) . split ( ' , ' ) ]
if node . get ( ' rowHeights ' ) :
rowheights = [ utils . unit_get ( f . strip ( ) ) for f in node . get ( ' rowHeights ' ) . split ( ' , ' ) ]
2008-07-22 14:24:36 +00:00
if len ( rowheights ) == 1 :
rowheights = rowheights [ 0 ]
2008-09-08 08:30:33 +00:00
table = platypus . LongTable ( data = data , colWidths = colwidths , rowHeights = rowheights , * * ( utils . attr_get ( node , [ ' splitByRow ' ] , { ' repeatRows ' : ' int ' , ' repeatCols ' : ' int ' } ) ) )
2009-04-28 10:24:21 +00:00
if node . get ( ' style ' ) :
table . setStyle ( self . styles . table_styles [ node . get ( ' style ' ) ] )
2008-07-22 14:24:36 +00:00
for s in styles :
table . setStyle ( s )
return table
def _illustration ( self , node ) :
2010-12-03 16:45:53 +00:00
return _rml_Illustration ( node , self . localcontext , self . styles , self )
2008-07-22 14:24:36 +00:00
def _textual_image ( self , node ) :
2009-04-28 10:24:21 +00:00
return base64 . decodestring ( node . text )
2008-07-22 14:24:36 +00:00
2010-11-02 08:00:19 +00:00
def _pto ( self , node ) :
sub_story = [ ]
pto_header = None
pto_trailer = None
for node in utils . _child_get ( node , self ) :
if node . tag == etree . Comment :
node . text = ' '
continue
elif node . tag == ' pto_header ' :
pto_header = self . render ( node )
elif node . tag == ' pto_trailer ' :
pto_trailer = self . render ( node )
else :
flow = self . _flowable ( node )
if flow :
if isinstance ( flow , list ) :
sub_story = sub_story + flow
else :
sub_story . append ( flow )
return platypus . flowables . PTOContainer ( sub_story , trailer = pto_trailer , header = pto_header )
2008-07-22 14:24:36 +00:00
def _flowable ( self , node , extra_style = None ) :
2010-11-02 08:00:19 +00:00
if node . tag == ' pto ' :
return self . _pto ( node )
2009-04-28 10:24:21 +00:00
if node . tag == ' para ' :
2008-07-22 14:24:36 +00:00
style = self . styles . para_style_get ( node )
if extra_style :
style . __dict__ . update ( extra_style )
2009-04-28 10:24:21 +00:00
result = [ ]
for i in self . _textual ( node ) . split ( ' \n ' ) :
result . append ( platypus . Paragraph ( i , style , * * ( utils . attr_get ( node , [ ] , { ' bulletText ' : ' str ' } ) ) ) )
return result
elif node . tag == ' barCode ' :
2008-07-22 14:24:36 +00:00
try :
from reportlab . graphics . barcode import code128
from reportlab . graphics . barcode import code39
from reportlab . graphics . barcode import code93
from reportlab . graphics . barcode import common
from reportlab . graphics . barcode import fourstate
from reportlab . graphics . barcode import usps
2010-12-19 20:11:27 +00:00
from reportlab . graphics . barcode import createBarcodeDrawing
2010-12-20 17:58:02 +00:00
2010-12-19 20:11:27 +00:00
except ImportError :
2012-01-24 12:55:12 +00:00
_logger . warning ( " Cannot use barcode renderers: " , exc_info = True )
2008-07-22 14:24:36 +00:00
return None
2009-07-01 08:28:50 +00:00
args = utils . attr_get ( node , [ ] , { ' ratio ' : ' float ' , ' xdim ' : ' unit ' , ' height ' : ' unit ' , ' checksum ' : ' int ' , ' quiet ' : ' int ' , ' width ' : ' unit ' , ' stop ' : ' bool ' , ' bearers ' : ' int ' , ' barWidth ' : ' float ' , ' barHeight ' : ' float ' } )
2008-07-22 14:24:36 +00:00
codes = {
' codabar ' : lambda x : common . Codabar ( x , * * args ) ,
' code11 ' : lambda x : common . Code11 ( x , * * args ) ,
2010-12-19 20:11:27 +00:00
' code128 ' : lambda x : code128 . Code128 ( str ( x ) , * * args ) ,
' standard39 ' : lambda x : code39 . Standard39 ( str ( x ) , * * args ) ,
' standard93 ' : lambda x : code93 . Standard93 ( str ( x ) , * * args ) ,
2008-07-22 14:24:36 +00:00
' i2of5 ' : lambda x : common . I2of5 ( x , * * args ) ,
2010-12-19 20:11:27 +00:00
' extended39 ' : lambda x : code39 . Extended39 ( str ( x ) , * * args ) ,
' extended93 ' : lambda x : code93 . Extended93 ( str ( x ) , * * args ) ,
2008-07-22 14:24:36 +00:00
' msi ' : lambda x : common . MSI ( x , * * args ) ,
' fim ' : lambda x : usps . FIM ( x , * * args ) ,
' postnet ' : lambda x : usps . POSTNET ( x , * * args ) ,
2010-12-19 20:11:27 +00:00
' ean13 ' : lambda x : createBarcodeDrawing ( ' EAN13 ' , value = str ( x ) , * * args ) ,
' qrcode ' : lambda x : createBarcodeDrawing ( ' QR ' , value = x , * * args ) ,
2008-07-22 14:24:36 +00:00
}
code = ' code128 '
2009-04-28 10:24:21 +00:00
if node . get ( ' code ' ) :
code = node . get ( ' code ' ) . lower ( )
2008-07-22 14:24:36 +00:00
return codes [ code ] ( self . _textual ( node ) )
2009-04-28 10:24:21 +00:00
elif node . tag == ' name ' :
self . styles . names [ node . get ( ' id ' ) ] = node . get ( ' value ' )
2008-07-22 14:24:36 +00:00
return None
2009-04-28 10:24:21 +00:00
elif node . tag == ' xpre ' :
2008-07-22 14:24:36 +00:00
style = self . styles . para_style_get ( node )
return platypus . XPreformatted ( self . _textual ( node ) , style , * * ( utils . attr_get ( node , [ ] , { ' bulletText ' : ' str ' , ' dedent ' : ' int ' , ' frags ' : ' int ' } ) ) )
2009-04-28 10:24:21 +00:00
elif node . tag == ' pre ' :
2008-07-22 14:24:36 +00:00
style = self . styles . para_style_get ( node )
return platypus . Preformatted ( self . _textual ( node ) , style , * * ( utils . attr_get ( node , [ ] , { ' bulletText ' : ' str ' , ' dedent ' : ' int ' } ) ) )
2009-04-28 10:24:21 +00:00
elif node . tag == ' illustration ' :
2008-07-22 14:24:36 +00:00
return self . _illustration ( node )
2009-04-28 10:24:21 +00:00
elif node . tag == ' blockTable ' :
2008-07-22 14:24:36 +00:00
return self . _table ( node )
2009-04-28 10:24:21 +00:00
elif node . tag == ' title ' :
2008-07-22 14:24:36 +00:00
styles = reportlab . lib . styles . getSampleStyleSheet ( )
style = styles [ ' Title ' ]
return platypus . Paragraph ( self . _textual ( node ) , style , * * ( utils . attr_get ( node , [ ] , { ' bulletText ' : ' str ' } ) ) )
2010-04-02 08:49:26 +00:00
elif re . match ( ' ^h([1-9]+[0-9]*)$ ' , ( node . tag or ' ' ) ) :
2008-07-22 14:24:36 +00:00
styles = reportlab . lib . styles . getSampleStyleSheet ( )
2010-04-02 08:49:26 +00:00
style = styles [ ' Heading ' + str ( node . tag [ 1 : ] ) ]
2008-07-22 14:24:36 +00:00
return platypus . Paragraph ( self . _textual ( node ) , style , * * ( utils . attr_get ( node , [ ] , { ' bulletText ' : ' str ' } ) ) )
2009-04-28 10:24:21 +00:00
elif node . tag == ' image ' :
2010-12-03 16:45:53 +00:00
image_data = False
2009-04-28 10:24:21 +00:00
if not node . get ( ' file ' ) :
if node . get ( ' name ' ) :
2010-12-03 16:45:53 +00:00
if node . get ( ' name ' ) in self . doc . images :
2012-01-24 12:55:12 +00:00
_logger . debug ( " Image %s read " , node . get ( ' name ' ) )
2010-12-03 16:45:53 +00:00
image_data = self . doc . images [ node . get ( ' name ' ) ] . read ( )
else :
2012-01-24 12:55:12 +00:00
_logger . warning ( " Image %s not defined " , node . get ( ' name ' ) )
2010-12-03 16:45:53 +00:00
return False
2008-07-22 14:24:36 +00:00
else :
import base64
2011-05-25 08:09:21 +00:00
newtext = node . text
2009-05-22 06:05:41 +00:00
if self . localcontext :
newtext = utils . _process_text ( self , node . text or ' ' )
2011-05-19 12:59:41 +00:00
image_data = base64 . decodestring ( newtext )
2010-12-03 16:45:53 +00:00
if not image_data :
2012-01-24 12:55:12 +00:00
_logger . debug ( " No inline image data " )
2010-12-03 16:45:53 +00:00
return False
2010-11-23 15:14:44 +00:00
image = StringIO ( image_data )
2008-07-22 14:24:36 +00:00
else :
2012-01-24 12:55:12 +00:00
_logger . debug ( " Image get from file %s " , node . get ( ' file ' ) )
2010-12-03 16:45:53 +00:00
image = _open_image ( node . get ( ' file ' ) , path = self . doc . path )
return platypus . Image ( image , mask = ( 250 , 255 , 250 , 255 , 250 , 255 ) , * * ( utils . attr_get ( node , [ ' width ' , ' height ' ] ) ) )
2009-04-28 10:24:21 +00:00
elif node . tag == ' spacer ' :
if node . get ( ' width ' ) :
width = utils . unit_get ( node . get ( ' width ' ) )
2008-07-22 14:24:36 +00:00
else :
width = utils . unit_get ( ' 1cm ' )
2009-04-28 10:24:21 +00:00
length = utils . unit_get ( node . get ( ' length ' ) )
2008-07-22 14:24:36 +00:00
return platypus . Spacer ( width = width , height = length )
2009-04-28 10:24:21 +00:00
elif node . tag == ' section ' :
2008-07-22 14:24:36 +00:00
return self . render ( node )
2009-04-28 10:24:21 +00:00
elif node . tag == ' pageNumberReset ' :
2008-07-22 14:24:36 +00:00
return PageReset ( )
2009-04-28 10:24:21 +00:00
elif node . tag in ( ' pageBreak ' , ' nextPage ' ) :
2008-07-22 14:24:36 +00:00
return platypus . PageBreak ( )
2009-04-28 10:24:21 +00:00
elif node . tag == ' condPageBreak ' :
2008-07-22 14:24:36 +00:00
return platypus . CondPageBreak ( * * ( utils . attr_get ( node , [ ' height ' ] ) ) )
2009-04-28 10:24:21 +00:00
elif node . tag == ' setNextTemplate ' :
return platypus . NextPageTemplate ( str ( node . get ( ' name ' ) ) )
elif node . tag == ' nextFrame ' :
2008-07-22 14:24:36 +00:00
return platypus . CondPageBreak ( 1000 ) # TODO: change the 1000 !
2009-04-28 10:24:21 +00:00
elif node . tag == ' setNextFrame ' :
2008-07-22 14:24:36 +00:00
from reportlab . platypus . doctemplate import NextFrameFlowable
2009-04-28 10:24:21 +00:00
return NextFrameFlowable ( str ( node . get ( ' name ' ) ) )
elif node . tag == ' currentFrame ' :
2008-07-22 14:24:36 +00:00
from reportlab . platypus . doctemplate import CurrentFrameFlowable
2009-04-28 10:24:21 +00:00
return CurrentFrameFlowable ( str ( node . get ( ' name ' ) ) )
elif node . tag == ' frameEnd ' :
2008-07-22 14:24:36 +00:00
return EndFrameFlowable ( )
2009-04-28 10:24:21 +00:00
elif node . tag == ' hr ' :
width_hr = node . get ( ' width ' ) or ' 100 % '
color_hr = node . get ( ' color ' ) or ' black '
thickness_hr = node . get ( ' thickness ' ) or 1
lineCap_hr = node . get ( ' lineCap ' ) or ' round '
2008-10-22 09:55:01 +00:00
return platypus . flowables . HRFlowable ( width = width_hr , color = color . get ( color_hr ) , thickness = float ( thickness_hr ) , lineCap = str ( lineCap_hr ) )
2008-07-22 14:24:36 +00:00
else :
2009-04-28 10:24:21 +00:00
sys . stderr . write ( ' Warning: flowable not yet implemented: %s ! \n ' % ( node . tag , ) )
2008-07-22 14:24:36 +00:00
return None
def render ( self , node_story ) :
2009-04-28 10:24:21 +00:00
def process_story ( node_story ) :
sub_story = [ ]
for node in utils . _child_get ( node_story , self ) :
2009-05-25 06:42:45 +00:00
if node . tag == etree . Comment :
node . text = ' '
continue
2008-08-11 09:27:55 +00:00
flow = self . _flowable ( node )
2008-07-22 14:24:36 +00:00
if flow :
2009-04-28 10:24:21 +00:00
if isinstance ( flow , list ) :
sub_story = sub_story + flow
2008-07-22 14:24:36 +00:00
else :
2009-04-28 10:24:21 +00:00
sub_story . append ( flow )
return sub_story
return process_story ( node_story )
2006-12-07 13:41:40 +00:00
2006-12-08 09:07:43 +00:00
class EndFrameFlowable ( ActionFlowable ) :
2008-07-22 14:24:36 +00:00
def __init__ ( self , resume = 0 ) :
ActionFlowable . __init__ ( self , ( ' frameEnd ' , resume ) )
2006-12-08 09:07:43 +00:00
class TinyDocTemplate ( platypus . BaseDocTemplate ) :
2012-07-16 07:52:17 +00:00
def beforeDocument ( self ) :
# Store some useful value directly inside canvas, so it's available
# on flowable drawing (needed for proper PageCount handling)
self . canv . _doPageReset = False
self . canv . _storyCount = 0
2008-07-22 14:24:36 +00:00
def ___handle_pageBegin ( self ) :
2012-12-14 13:44:55 +00:00
self . page + = 1
2008-07-22 14:24:36 +00:00
self . pageTemplate . beforeDrawPage ( self . canv , self )
self . pageTemplate . checkPageSize ( self . canv , self )
self . pageTemplate . onPage ( self . canv , self )
for f in self . pageTemplate . frames : f . _reset ( )
self . beforePage ( )
self . _curPageFlowableCount = 0
if hasattr ( self , ' _nextFrameIndex ' ) :
del self . _nextFrameIndex
for f in self . pageTemplate . frames :
if f . id == ' first ' :
self . frame = f
break
self . handle_frameBegin ( )
2012-07-16 07:52:17 +00:00
def afterPage ( self ) :
if self . canv . _doPageReset :
# Following a <pageReset/> tag:
# - we reset page number to 0
# - we add an new PageCount flowable (relative to the current
# story number), but not for NumeredCanvas at is handle page
# count itself)
# NOTE: _rml_template render() method add a PageReset flowable at end
# of each story, so we're sure to pass here at least once per story.
if not isinstance ( self . canv , NumberedCanvas ) :
self . handle_flowable ( [ PageCount ( story_count = self . canv . _storyCount ) ] )
self . canv . _pageCount = self . page
self . page = 0
self . canv . _flag = True
2008-07-22 14:24:36 +00:00
self . canv . _pageNumber = 0
2012-07-16 07:52:17 +00:00
self . canv . _doPageReset = False
self . canv . _storyCount + = 1
2006-12-08 09:07:43 +00:00
2006-12-07 13:41:40 +00:00
class _rml_template ( object ) :
2011-11-07 15:45:56 +00:00
def __init__ ( self , localcontext , out , node , doc , images = None , path = ' . ' , title = None ) :
if images is None :
images = { }
2010-10-06 13:25:34 +00:00
if not localcontext :
2010-10-07 09:51:27 +00:00
localcontext = { ' internal_header ' : True }
2009-04-28 10:24:21 +00:00
self . localcontext = localcontext
2008-07-22 14:24:36 +00:00
self . images = images
self . path = path
2008-09-24 15:34:10 +00:00
self . title = title
2010-10-05 07:27:09 +00:00
2011-12-21 09:24:46 +00:00
pagesize_map = { ' a4 ' : A4 ,
' us_letter ' : letter
}
pageSize = A4
if self . localcontext . get ( ' company ' ) :
pageSize = pagesize_map . get ( self . localcontext . get ( ' company ' ) . paper_format , A4 )
if node . get ( ' pageSize ' ) :
2009-04-28 10:24:21 +00:00
ps = map ( lambda x : x . strip ( ) , node . get ( ' pageSize ' ) . replace ( ' ) ' , ' ' ) . replace ( ' ( ' , ' ' ) . split ( ' , ' ) )
2008-07-22 14:24:36 +00:00
pageSize = ( utils . unit_get ( ps [ 0 ] ) , utils . unit_get ( ps [ 1 ] ) )
2009-04-28 10:24:21 +00:00
2010-08-05 02:32:28 +00:00
self . doc_tmpl = TinyDocTemplate ( out , pagesize = pageSize , * * utils . attr_get ( node , [ ' leftMargin ' , ' rightMargin ' , ' topMargin ' , ' bottomMargin ' ] , { ' allowSplitting ' : ' int ' , ' showBoundary ' : ' bool ' , ' rotation ' : ' int ' , ' title ' : ' str ' , ' author ' : ' str ' } ) )
2008-07-22 14:24:36 +00:00
self . page_templates = [ ]
self . styles = doc . styles
self . doc = doc
2010-10-04 13:35:06 +00:00
self . image = [ ]
2009-04-28 10:24:21 +00:00
pts = node . findall ( ' pageTemplate ' )
2008-07-22 14:24:36 +00:00
for pt in pts :
frames = [ ]
2009-04-28 10:24:21 +00:00
for frame_el in pt . findall ( ' frame ' ) :
2008-07-22 14:24:36 +00:00
frame = platypus . Frame ( * * ( utils . attr_get ( frame_el , [ ' x1 ' , ' y1 ' , ' width ' , ' height ' , ' leftPadding ' , ' rightPadding ' , ' bottomPadding ' , ' topPadding ' ] , { ' id ' : ' str ' , ' showBoundary ' : ' bool ' } ) ) )
if utils . attr_get ( frame_el , [ ' last ' ] ) :
frame . lastFrame = True
frames . append ( frame )
2009-04-28 10:24:21 +00:00
try :
2009-11-28 11:58:09 +00:00
gr = pt . findall ( ' pageGraphics ' ) \
or pt [ 1 ] . findall ( ' pageGraphics ' )
2010-06-22 09:45:40 +00:00
except Exception : # FIXME: be even more specific, perhaps?
2009-04-28 10:24:21 +00:00
gr = ' '
2008-07-22 14:24:36 +00:00
if len ( gr ) :
2010-10-05 07:27:09 +00:00
# self.image=[ n for n in utils._child_get(gr[0], self) if n.tag=='image' or not self.localcontext]
2009-04-28 10:24:21 +00:00
drw = _rml_draw ( self . localcontext , gr [ 0 ] , self . doc , images = images , path = self . path , title = self . title )
2008-07-22 14:24:36 +00:00
self . page_templates . append ( platypus . PageTemplate ( frames = frames , onPage = drw . render , * * utils . attr_get ( pt , [ ] , { ' id ' : ' str ' } ) ) )
else :
2009-04-28 10:24:21 +00:00
drw = _rml_draw ( self . localcontext , node , self . doc , title = self . title )
2008-11-15 06:14:55 +00:00
self . page_templates . append ( platypus . PageTemplate ( frames = frames , onPage = drw . render , * * utils . attr_get ( pt , [ ] , { ' id ' : ' str ' } ) ) )
2008-07-22 14:24:36 +00:00
self . doc_tmpl . addPageTemplates ( self . page_templates )
def render ( self , node_stories ) :
2010-10-07 10:11:27 +00:00
if self . localcontext and not self . localcontext . get ( ' internal_header ' , False ) :
del self . localcontext [ ' internal_header ' ]
2008-07-22 14:24:36 +00:00
fis = [ ]
2013-04-08 13:25:10 +00:00
r = _rml_flowable ( self . doc , self . localcontext , images = self . images , path = self . path , title = self . title , canvas = None )
2009-06-15 06:35:23 +00:00
story_cnt = 0
2008-07-22 14:24:36 +00:00
for node_story in node_stories :
2009-08-27 10:04:54 +00:00
if story_cnt > 0 :
2014-07-01 09:29:22 +00:00
# Reset Page Number with new story tag
fis . append ( PageReset ( ) )
2009-08-27 10:04:54 +00:00
fis . append ( platypus . PageBreak ( ) )
2008-07-22 14:24:36 +00:00
fis + = r . render ( node_story )
2009-08-27 10:04:54 +00:00
story_cnt + = 1
2013-11-12 13:04:12 +00:00
try :
if self . localcontext and self . localcontext . get ( ' internal_header ' , False ) :
self . doc_tmpl . afterFlowable ( fis )
self . doc_tmpl . build ( fis , canvasmaker = NumberedCanvas )
else :
self . doc_tmpl . build ( fis )
except platypus . doctemplate . LayoutError , e :
e . name = ' Print Error '
e . value = ' The document you are trying to print contains a table row that does not fit on one page. Please try to split it in smaller rows or contact your administrator. '
raise
2006-12-07 13:41:40 +00:00
2011-11-07 15:45:56 +00:00
def parseNode ( rml , localcontext = None , fout = None , images = None , path = ' . ' , title = None ) :
2009-04-28 10:24:21 +00:00
node = etree . XML ( rml )
r = _rml_doc ( node , localcontext , images , path , title = title )
2009-05-06 21:36:12 +00:00
#try to override some font mappings
try :
2009-11-24 14:44:05 +00:00
from customfonts import SetCustomFonts
SetCustomFonts ( r )
2010-11-23 15:11:24 +00:00
except ImportError :
# means there is no custom fonts mapping in this system.
pass
2010-06-22 09:45:40 +00:00
except Exception :
2012-01-24 12:55:12 +00:00
_logger . warning ( ' Cannot set font mapping ' , exc_info = True )
2009-11-24 14:44:05 +00:00
pass
2010-11-23 15:14:44 +00:00
fp = StringIO ( )
2009-04-28 10:24:21 +00:00
r . render ( fp )
return fp . getvalue ( )
2011-11-07 15:45:56 +00:00
def parseString ( rml , localcontext = None , fout = None , images = None , path = ' . ' , title = None ) :
2009-04-28 10:24:21 +00:00
node = etree . XML ( rml )
r = _rml_doc ( node , localcontext , images , path , title = title )
2009-05-06 20:18:13 +00:00
2009-02-07 19:26:06 +00:00
#try to override some font mappings
try :
2009-11-24 14:44:05 +00:00
from customfonts import SetCustomFonts
SetCustomFonts ( r )
2010-06-22 09:45:40 +00:00
except Exception :
2009-11-24 14:44:05 +00:00
pass
2009-05-06 20:18:13 +00:00
2008-07-22 14:24:36 +00:00
if fout :
fp = file ( fout , ' wb ' )
r . render ( fp )
fp . close ( )
return fout
else :
2010-11-23 15:14:44 +00:00
fp = StringIO ( )
2008-07-22 14:24:36 +00:00
r . render ( fp )
return fp . getvalue ( )
2006-12-07 13:41:40 +00:00
def trml2pdf_help ( ) :
2008-07-22 14:24:36 +00:00
print ' Usage: trml2pdf input.rml >output.pdf '
print ' Render the standard input (RML) and output a PDF file '
sys . exit ( 0 )
2006-12-07 13:41:40 +00:00
if __name__ == " __main__ " :
2008-07-22 14:24:36 +00:00
if len ( sys . argv ) > 1 :
if sys . argv [ 1 ] == ' --help ' :
trml2pdf_help ( )
print parseString ( file ( sys . argv [ 1 ] , ' r ' ) . read ( ) ) ,
else :
print ' Usage: trml2pdf input.rml >output.pdf '
print ' Try \' trml2pdf --help \' for more information. '
2008-07-23 15:01:27 +00:00
2011-11-22 08:58:48 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: