src/reportlab/pdfgen/canvas.py
branchpy33
changeset 3723 99aa837b6703
parent 3721 0c93dd8ff567
child 3731 b233dd0577ff
equal deleted inserted replaced
3722:29c11b905751 3723:99aa837b6703
    10 ENABLE_TRACKING = 1 # turn this off to do profile testing w/o tracking
    10 ENABLE_TRACKING = 1 # turn this off to do profile testing w/o tracking
    11 
    11 
    12 import os
    12 import os
    13 import sys
    13 import sys
    14 import re
    14 import re
    15 from string import join, split, strip, atoi, replace, upper, digits
    15 import hashlib
       
    16 from string import digits
    16 import tempfile
    17 import tempfile
    17 from math import sin, cos, tan, pi, ceil
    18 from math import sin, cos, tan, pi, ceil
    18 try:
       
    19     from hashlib import md5
       
    20 except ImportError:
       
    21     from md5 import md5
       
    22 
       
    23 from reportlab import rl_config
    19 from reportlab import rl_config
    24 from reportlab.pdfbase import pdfutils
    20 from reportlab.pdfbase import pdfutils
    25 from reportlab.pdfbase import pdfdoc
    21 from reportlab.pdfbase import pdfdoc
    26 from reportlab.pdfbase import pdfmetrics
    22 from reportlab.pdfbase import pdfmetrics
    27 from reportlab.pdfgen  import pdfgeom, pathobject, textobject
    23 from reportlab.pdfgen  import pdfgeom, pathobject, textobject
    28 from reportlab.lib.colors import black, _chooseEnforceColorSpace, Color, CMYKColor, toColor
    24 from reportlab.lib.colors import black, _chooseEnforceColorSpace, Color, CMYKColor, toColor
    29 from reportlab.lib.utils import import_zlib, ImageReader, fp_str, _digester
    25 from reportlab.lib.utils import import_zlib, ImageReader, fp_str, isSeqType, isStrType, isUnicodeType, _digester
    30 from reportlab.lib.boxstuff import aspectRatioFix
    26 from reportlab.lib.boxstuff import aspectRatioFix
    31 
    27 
    32 digitPat = re.compile('\d')  #used in decimal alignment
    28 digitPat = re.compile('\d')  #used in decimal alignment
    33 zlib = import_zlib()
    29 zlib = import_zlib()
    34 
    30 
   315         If encrypt is an instance of reportlab.lib.pdfencrypt.StandardEncryption, this object is
   311         If encrypt is an instance of reportlab.lib.pdfencrypt.StandardEncryption, this object is
   316         used to encrypt the pdf. This allows more finegrained control over the encryption settings.
   312         used to encrypt the pdf. This allows more finegrained control over the encryption settings.
   317         '''
   313         '''
   318         if encrypt:
   314         if encrypt:
   319             from reportlab.lib import pdfencrypt
   315             from reportlab.lib import pdfencrypt
   320             if isinstance(encrypt, str): #encrypt is the password itself
   316             if isStrType(encrypt): #encrypt is the password itself
   321                 if isinstance(encrypt, str):
   317                 if isUnicodeType(encrypt):
   322                     encrypt = encrypt.encode('utf-8')
   318                     encrypt = encrypt.encode('utf-8')
   323                 encrypt = pdfencrypt.StandardEncryption(encrypt)    #now it's the encrypt object
   319                 encrypt = pdfencrypt.StandardEncryption(encrypt)    #now it's the encrypt object
   324                 encrypt.setAllPermissions(1)
   320                 encrypt.setAllPermissions(1)
   325             elif not isinstance(encrypt, pdfencrypt.StandardEncryption):
   321             elif not isinstance(encrypt, pdfencrypt.StandardEncryption):
   326                 raise TypeError('Expected string or instance of reportlab.lib.pdfencrypt.StandardEncryption as encrypt parameter but got %r' % encrypt)
   322                 raise TypeError('Expected string or instance of reportlab.lib.pdfencrypt.StandardEncryption as encrypt parameter but got %r' % encrypt)
   374         state = self.state_stack[-1]
   370         state = self.state_stack[-1]
   375         del self.state_stack[-1]
   371         del self.state_stack[-1]
   376         d = self.__dict__
   372         d = self.__dict__
   377         d.update(state)
   373         d.update(state)
   378 
   374 
   379     STATE_ATTRIBUTES = split("""
   375     STATE_ATTRIBUTES = """_x _y _fontname _fontsize _textMode _leading _currentMatrix _fillMode
   380      _x _y _fontname _fontsize _textMode _leading _currentMatrix _fillMode
       
   381      _fillMode _charSpace _wordSpace _horizScale _textRenderMode _rise _textLineMatrix
   376      _fillMode _charSpace _wordSpace _horizScale _textRenderMode _rise _textLineMatrix
   382      _textMatrix _lineCap _lineJoin _lineDash _lineWidth _mitreLimit _fillColorObj
   377      _textMatrix _lineCap _lineJoin _lineDash _lineWidth _mitreLimit _fillColorObj
   383      _strokeColorObj _extgstate""")
   378      _strokeColorObj _extgstate""".split()
   384     STATE_RANGE = list(range(len(STATE_ATTRIBUTES)))
   379     STATE_RANGE = list(range(len(STATE_ATTRIBUTES)))
   385 
   380 
   386         #self._addStandardFonts()
   381         #self._addStandardFonts()
   387 
   382 
   388     def _make_preamble(self):
   383     def _make_preamble(self):
   910             smask = image._dataA
   905             smask = image._dataA
   911             if mask=='auto' and smask:
   906             if mask=='auto' and smask:
   912                 mdata = smask.getRGBData()
   907                 mdata = smask.getRGBData()
   913             else:
   908             else:
   914                 mdata = str(mask)
   909                 mdata = str(mask)
       
   910             if isUnicodeType(mdata):
       
   911                 mdata = mdata.encode('utf8')
   915             name = _digester(rawdata+mdata)
   912             name = _digester(rawdata+mdata)
   916         else:
   913         else:
   917             #filename, use it
   914             #filename, use it
   918             name = _digester('%s%s' % (image, mask))
   915             s = '%s%s' % (image, mask)
       
   916             if isUnicodeType(s):
       
   917                 s = s.encode('utf-8')
       
   918             name = _digester(s)
   919 
   919 
   920         # in the pdf document, this will be prefixed with something to
   920         # in the pdf document, this will be prefixed with something to
   921         # say it is an XObject.  Does it exist yet?
   921         # say it is an XObject.  Does it exist yet?
   922         regName = self._doc.getXObjectName(name)
   922         regName = self._doc.getXObjectName(name)
   923         imgObj = self._doc.idToObject.get(regName, None)
   923         imgObj = self._doc.idToObject.get(regName, None)
  1042         See test_pdfbase_postscript.py for 2 test cases - one will work on
  1042         See test_pdfbase_postscript.py for 2 test cases - one will work on
  1043         any Postscript device, the other uses a 'setpapertray' command which
  1043         any Postscript device, the other uses a 'setpapertray' command which
  1044         will error in Distiller but work on printers supporting it.
  1044         will error in Distiller but work on printers supporting it.
  1045         """
  1045         """
  1046         #check if we've done this one already...
  1046         #check if we've done this one already...
  1047         rawName = 'PS' + md5(command).hexdigest()
  1047         if isUnicodeType(command):
       
  1048             rawName = 'PS' + hashlib.md5(command.encode('utf-8')).hexdigest()
       
  1049         else:
       
  1050             rawName = 'PS' + hashlib.md5(command).hexdigest()
  1048         regName = self._doc.getXObjectName(rawName)
  1051         regName = self._doc.getXObjectName(rawName)
  1049         psObj = self._doc.idToObject.get(regName, None)
  1052         psObj = self._doc.idToObject.get(regName, None)
  1050         if not psObj:
  1053         if not psObj:
  1051             #first use of this chunk of Postscript, make an object
  1054             #first use of this chunk of Postscript, make an object
  1052             psObj = pdfdoc.PDFPostScriptXObject(command + '\r\n')
  1055             psObj = pdfdoc.PDFPostScriptXObject(command + '\r\n')
  1196     def getpdfdata(self):
  1199     def getpdfdata(self):
  1197         """Returns the PDF data that would normally be written to a file.
  1200         """Returns the PDF data that would normally be written to a file.
  1198         If there is current data a ShowPage is executed automatically.
  1201         If there is current data a ShowPage is executed automatically.
  1199         After this operation the canvas must not be used further."""
  1202         After this operation the canvas must not be used further."""
  1200         if len(self._code): self.showPage()
  1203         if len(self._code): self.showPage()
  1201         return self._doc.GetPDFData(self)
  1204         s = self._doc.GetPDFData(self)
       
  1205         if isUnicodeType(s):
       
  1206             s = s.encode('utf-8')
       
  1207         return s
  1202 
  1208 
  1203     def setPageSize(self, size):
  1209     def setPageSize(self, size):
  1204         """accepts a 2-tuple in points for paper size for this
  1210         """accepts a 2-tuple in points for paper size for this
  1205         and subsequent pages"""
  1211         and subsequent pages"""
  1206         self._pagesize = size
  1212         self._pagesize = size
  1252             a0,b0,c0,d0,e0,f0 = self._currentMatrix
  1258             a0,b0,c0,d0,e0,f0 = self._currentMatrix
  1253             self._currentMatrix = (a0*a+c0*b,    b0*a+d0*b,
  1259             self._currentMatrix = (a0*a+c0*b,    b0*a+d0*b,
  1254                                    a0*c+c0*d,    b0*c+d0*d,
  1260                                    a0*c+c0*d,    b0*c+d0*d,
  1255                                    a0*e+c0*f+e0, b0*e+d0*f+f0)
  1261                                    a0*e+c0*f+e0, b0*e+d0*f+f0)
  1256         if self._code and self._code[-1][-3:]==' cm':
  1262         if self._code and self._code[-1][-3:]==' cm':
  1257             L = split(self._code[-1])
  1263             L = self._code[-1].split()
  1258             a0, b0, c0, d0, e0, f0 = list(map(float,L[-7:-1]))
  1264             a0, b0, c0, d0, e0, f0 = list(map(float,L[-7:-1]))
  1259             s = len(L)>7 and join(L)+ ' %s cm' or '%s cm'
  1265             s = len(L)>7 and join(L)+ ' %s cm' or '%s cm'
  1260             self._code[-1] = s % fp_str(a0*a+c0*b,b0*a+d0*b,a0*c+c0*d,b0*c+d0*d,a0*e+c0*f+e0,b0*e+d0*f+f0)
  1266             self._code[-1] = s % fp_str(a0*a+c0*b,b0*a+d0*b,a0*c+c0*d,b0*c+d0*d,a0*e+c0*f+e0,b0*e+d0*f+f0)
  1261         else:
  1267         else:
  1262             self._code.append('%s cm' % fp_str(a,b,c,d,e,f))
  1268             self._code.append('%s cm' % fp_str(a,b,c,d,e,f))
  1473         # use PDFTextObject for multi-line text.
  1479         # use PDFTextObject for multi-line text.
  1474         ##################################################
  1480         ##################################################
  1475 
  1481 
  1476     def drawString(self, x, y, text, mode=None):
  1482     def drawString(self, x, y, text, mode=None):
  1477         """Draws a string in the current text styles."""
  1483         """Draws a string in the current text styles."""
       
  1484         if sys.version_info[0] == 3 and not isinstance(text, str):
       
  1485             text = text.decode('utf-8')
  1478         #we could inline this for speed if needed
  1486         #we could inline this for speed if needed
  1479         t = self.beginText(x, y)
  1487         t = self.beginText(x, y)
  1480         if mode is not None: t.setTextRenderMode(mode)
  1488         if mode is not None: t.setTextRenderMode(mode)
  1481         t.textLine(text)
  1489         t.textLine(text)
  1482         self.drawText(t)
  1490         self.drawText(t)
  1483 
  1491 
  1484     def drawRightString(self, x, y, text, mode=None):
  1492     def drawRightString(self, x, y, text, mode=None):
  1485         """Draws a string right-aligned with the x coordinate"""
  1493         """Draws a string right-aligned with the x coordinate"""
       
  1494         if sys.version_info[0] == 3 and not isinstance(text, str):
       
  1495             text = text.decode('utf-8')
  1486         width = self.stringWidth(text, self._fontname, self._fontsize)
  1496         width = self.stringWidth(text, self._fontname, self._fontsize)
  1487         t = self.beginText(x - width, y)
  1497         t = self.beginText(x - width, y)
  1488         if mode is not None: t.setTextRenderMode(mode)
  1498         if mode is not None: t.setTextRenderMode(mode)
  1489         t.textLine(text)
  1499         t.textLine(text)
  1490         self.drawText(t)
  1500         self.drawText(t)
  1491 
  1501 
  1492     def drawCentredString(self, x, y, text,mode=None):
  1502     def drawCentredString(self, x, y, text,mode=None):
  1493         """Draws a string centred on the x coordinate. 
  1503         """Draws a string centred on the x coordinate. 
  1494         
  1504         
  1495         We're British, dammit, and proud of our spelling!"""
  1505         We're British, dammit, and proud of our spelling!"""
       
  1506         if sys.version_info[0] == 3 and not isinstance(text, str):
       
  1507             text = text.decode('utf-8')
  1496         width = self.stringWidth(text, self._fontname, self._fontsize)
  1508         width = self.stringWidth(text, self._fontname, self._fontsize)
  1497         t = self.beginText(x - 0.5*width, y)
  1509         t = self.beginText(x - 0.5*width, y)
  1498         if mode is not None: t.setTextRenderMode(mode)
  1510         if mode is not None: t.setTextRenderMode(mode)
  1499         t.textLine(text)
  1511         t.textLine(text)
  1500         self.drawText(t)
  1512         self.drawText(t)
  1626 
  1638 
  1627     def setDash(self, array=[], phase=0):
  1639     def setDash(self, array=[], phase=0):
  1628         """Two notations.  pass two numbers, or an array and phase"""
  1640         """Two notations.  pass two numbers, or an array and phase"""
  1629         if isinstance(array,(int,float)):
  1641         if isinstance(array,(int,float)):
  1630             self._code.append('[%s %s] 0 d' % (array, phase))
  1642             self._code.append('[%s %s] 0 d' % (array, phase))
  1631         elif isinstance(array,(tuple,list)):
  1643         elif isSeqType(array):
  1632             assert phase >= 0, "phase is a length in user space"
  1644             assert phase >= 0, "phase is a length in user space"
  1633             textarray = ' '.join(map(str, array))
  1645             textarray = ' '.join([str(s) for s in array])
  1634             self._code.append('[%s] %s d' % (textarray, phase))
  1646             self._code.append('[%s] %s d' % (textarray, phase))
  1635 
  1647 
  1636     # path stuff - the separate path object builds it
  1648     # path stuff - the separate path object builds it
  1637 
  1649 
  1638     def beginPath(self):
  1650     def beginPath(self):