At reportlab, allow a custom font mapping table, system-wide..
[odoo/odoo.git] / bin / report / render / rml2pdf / trml2pdf.py
index aeee122..aea46ea 100644 (file)
@@ -1,32 +1,24 @@
 # -*- encoding: utf-8 -*-
 ##############################################################################
 #
-# Copyright (c) 2004-2008 Tiny SPRL (http://tiny.be) All Rights Reserved.
+#    OpenERP, Open Source Management Solution
+#    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
+#    $Id$
 #
-# $Id$
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, either version 3 of the License, or
+#    (at your option) any later version.
 #
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
+#    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
+#    GNU General Public License for more details.
 #
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
-# 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
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
-###############################################################################
-#!/usr/bin/python
+##############################################################################
 
 # trml2pdf - An RML to PDF converter
 # Copyright (C) 2003, Fabien Pinckaers, UCL, FSA
@@ -48,7 +40,7 @@
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 import sys
-import StringIO
+from StringIO import StringIO
 import xml.dom.minidom
 import copy
 
@@ -56,11 +48,11 @@ import reportlab
 import re
 from reportlab.pdfgen import canvas
 from reportlab import platypus
-
+import cStringIO
 import utils
 import color
 import os
-ftitle=""
+
 #
 # Change this to UTF-8 if you plan tu use Reportlab's UTF-8 support
 #
@@ -180,11 +172,12 @@ class _rml_styles(object):
         return style
 
 class _rml_doc(object):
-    def __init__(self, data, images={}, path='.'):
+    def __init__(self, data, images={}, path='.', title=None):
         self.dom = xml.dom.minidom.parseString(data)
         self.filename = self.dom.documentElement.getAttribute('filename')
         self.images = images
         self.path = path
+        self.title = title
 
     def docinit(self, els):
         from reportlab.lib.fonts import addMapping
@@ -201,6 +194,26 @@ class _rml_doc(object):
                 addMapping(name, 1, 0, name)    #bold
                 addMapping(name, 1, 1, name)    #italic and bold
 
+    def setTTFontMapping(self,face, fontname,filename, mode='all'):
+        from reportlab.lib.fonts import addMapping
+        from reportlab.pdfbase import pdfmetrics
+        from reportlab.pdfbase.ttfonts import TTFont
+       
+       pdfmetrics.registerFont(TTFont(fontname, filename ))
+       if (mode == 'all'):
+               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
+       elif (mode== 'normal') or (mode == 'regular'):
+               addMapping(face, 0, 0, fontname)    #normal
+       elif (mode == 'italic'):
+               addMapping(face, 0, 1, fontname)    #italic
+       elif (mode == 'bold'):
+               addMapping(face, 1, 0, fontname)    #bold
+       elif (mode == 'bolditalic'):
+               addMapping(face, 1, 1, fontname)    #italic and bold
+
     def _textual_image(self, node):
         import base64
         rc = ''
@@ -229,28 +242,28 @@ class _rml_doc(object):
 
         el = self.dom.documentElement.getElementsByTagName('template')
         if len(el):
-            pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path)
+            pt_obj = _rml_template(out, el[0], self, images=self.images, path=self.path, title=self.title)
             pt_obj.render(self.dom.documentElement.getElementsByTagName('story'))
         else:
             self.canvas = canvas.Canvas(out)
             pd = self.dom.documentElement.getElementsByTagName('pageDrawing')[0]
-            pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path)
+            pd_obj = _rml_canvas(self.canvas, None, self, self.images, path=self.path, title=self.title)
             pd_obj.render(pd)
 
             self.canvas.showPage()
             self.canvas.save()
 
 class _rml_canvas(object):
-    def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.'):
+    def __init__(self, canvas, doc_tmpl=None, doc=None, images={}, path='.', title=None):
         self.canvas = canvas
         self.styles = doc.styles
         self.doc_tmpl = doc_tmpl
         self.doc = doc
         self.images = images
         self.path = path
-        global ftitle
-        if  ftitle:
-            self.canvas.setTitle(ftitle)
+        self.title = title
+        if self.title:
+            self.canvas.setTitle(self.title)
 
     def _textual(self, node, x=0, y=0):
         rc = ''
@@ -324,7 +337,7 @@ class _rml_canvas(object):
         self.canvas.circle(x_cen=utils.unit_get(node.getAttribute('x')), y_cen=utils.unit_get(node.getAttribute('y')), r=utils.unit_get(node.getAttribute('radius')), **utils.attr_get(node, [], {'fill':'bool','stroke':'bool'}))
 
     def _place(self, node):
-        flows = _rml_flowable(self.doc, images=self.images, path=self.path).render(node)
+        flows = _rml_flowable(self.doc, images=self.images, path=self.path, title=self.title).render(node)
         infos = utils.attr_get(node, ['x','y','width','height'])
 
         infos['y']+=infos['height']
@@ -358,26 +371,28 @@ class _rml_canvas(object):
         import urllib
         from reportlab.lib.utils import ImageReader
 
+#        s = StringIO()
         if not node.hasAttribute('file'):
 
             if node.hasAttribute('name'):
                 image_data = self.images[node.getAttribute('name')]
-                s = StringIO.StringIO(image_data)
+                s = cStringIO.StringIO(image_data)
             else:
                 import base64
                 image_data = base64.decodestring(node.firstChild.nodeValue)
                 if not image_data: return False
-                s = StringIO.StringIO(image_data)
+                s = cStringIO.StringIO(image_data)                
+#                s.write(image_data)
         else:
             if node.getAttribute('file') in self.images:
-                s = StringIO.StringIO(self.images[node.getAttribute('file')])
+                s = cStringIO.StringIO(self.images[node.getAttribute('file')])                
+#                s.write(self.images[node.getAttribute('file')])
             else:
                 try:
                     u = urllib.urlopen(str(node.getAttribute('file')))
-                    s = StringIO.StringIO(u.read())
                 except:
                     u = file(os.path.join(self.path,str(node.getAttribute('file'))), 'rb')
-                    s = StringIO.StringIO(u.read())
+                s = cStringIO.StringIO(u.read())
         img = ImageReader(s)
         (sx,sy) = img.getSize()
 
@@ -450,25 +465,27 @@ class _rml_canvas(object):
                         break
 
 class _rml_draw(object):
-    def __init__(self, node, styles, images={}, path='.'):
+    def __init__(self, node, styles, images={}, path='.', title=None):
         self.node = node
         self.styles = styles
         self.canvas = None
         self.images = images
         self.path = path
+        self.canvas_title = title
 
     def render(self, canvas, doc):
         canvas.saveState()
-        cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path)
+        cnv = _rml_canvas(canvas, doc, self.styles, images=self.images, path=self.path, title=self.canvas_title)
         cnv.render(self.node)
         canvas.restoreState()
 
 class _rml_flowable(object):
-    def __init__(self, doc, images={}, path='.'):
+    def __init__(self, doc, images={}, path='.', title=None):
         self.doc = doc
         self.styles = doc.styles
         self.images = images
         self.path = path
+        self.title = title
 
     def _textual(self, node):
         rc = ''
@@ -569,7 +586,7 @@ class _rml_flowable(object):
                 return (self.width, self.height)
             def draw(self):
                 canvas = self.canv
-                drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path)
+                drw = _rml_draw(self.node, self.styles, images=self.self2.images, path=self.self2.path, title=self.self2.title)
                 drw.render(self.canv, None)
         return Illustration(node, self.styles, self)
 
@@ -644,7 +661,10 @@ class _rml_flowable(object):
                 else:
                     import base64
                     image_data = base64.decodestring(node.firstChild.nodeValue)
-                image = StringIO.StringIO(image_data)
+
+                image = StringIO()
+                image.write(image_data)
+                image.seek(0)
                 return platypus.Image(image, mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
             else:
                 return platypus.Image(node.getAttribute('file'), mask=(250,255,250,255,250,255), **(utils.attr_get(node, ['width','height'])))
@@ -695,6 +715,12 @@ class _rml_flowable(object):
             return CurrentFrameFlowable(str(node.getAttribute('name')))
         elif node.localName == 'frameEnd':
             return EndFrameFlowable()
+        elif node.localName == 'hr':
+            width_hr=node.hasAttribute('width') and node.getAttribute('width') or '100%'
+            color_hr=node.hasAttribute('color') and node.getAttribute('color') or 'black'
+            thickness_hr=node.hasAttribute('thickness') and node.getAttribute('thickness') or 1
+            lineCap_hr=node.hasAttribute('lineCap') and node.getAttribute('lineCap') or 'round'
+            return platypus.flowables.HRFlowable(width=width_hr,color=color.get(color_hr),thickness=float(thickness_hr),lineCap=str(lineCap_hr))
         else:
             sys.stderr.write('Warning: flowable not yet implemented: %s !\n' % (node.localName,))
             return None
@@ -741,9 +767,10 @@ class TinyDocTemplate(platypus.BaseDocTemplate):
             self.canv._pageNumber = 0
 
 class _rml_template(object):
-    def __init__(self, out, node, doc, images={}, path='.'):
+    def __init__(self, out, node, doc, images={}, path='.', title=None):
         self.images= images
         self.path = path
+        self.title = title
         if not node.hasAttribute('pageSize'):
             pageSize = (utils.unit_get('21cm'), utils.unit_get('29.7cm'))
         else:
@@ -764,15 +791,16 @@ class _rml_template(object):
                 frames.append( frame )
             gr = pt.getElementsByTagName('pageGraphics')
             if len(gr):
-                drw = _rml_draw(gr[0], self.doc, images=images, path=self.path)
+                drw = _rml_draw(gr[0], self.doc, images=images, path=self.path, title=self.title)
                 self.page_templates.append( platypus.PageTemplate(frames=frames, onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
             else:
-                self.page_templates.append( platypus.PageTemplate(frames=frames, **utils.attr_get(pt, [], {'id':'str'}) ))
+                drw = _rml_draw(node,self.doc,title=self.title)
+                self.page_templates.append( platypus.PageTemplate(frames=frames,onPage=drw.render, **utils.attr_get(pt, [], {'id':'str'}) ))
         self.doc_tmpl.addPageTemplates(self.page_templates)
 
     def render(self, node_stories):
         fis = []
-        r = _rml_flowable(self.doc,images=self.images, path=self.path)
+        r = _rml_flowable(self.doc,images=self.images, path=self.path, title=self.title)
         for node_story in node_stories:
             fis += r.render(node_story)
             if node_story==node_stories[-1]:
@@ -784,16 +812,22 @@ class _rml_template(object):
         self.doc_tmpl.build(fis)
 
 def parseString(data, fout=None, images={}, path='.',title=None):
-    r = _rml_doc(data, images, path)
-    global ftitle
-    ftitle=title
+    r = _rml_doc(data, images, path, title=title)
+    
+    #try to override some font mappings
+    try:
+       from customfonts import SetCustomFonts
+       SetCustomFonts(r)
+    except:
+       pass
+    
     if fout:
         fp = file(fout,'wb')
         r.render(fp)
         fp.close()
         return fout
     else:
-        fp = StringIO.StringIO()
+        fp = StringIO()
         r.render(fp)
         return fp.getvalue()