reportlab/pdfgen/canvas.py
author gmcm
Thu, 20 Jan 2000 23:33:04 +0000
changeset 0 f19e0a2433ab
child 6 eb791971b252
permissions -rwxr-xr-x
Initial revision
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     1
#pdfgen.py
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     2
""" 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     3
PDFgen is a library to generate PDF files containing text and graphics.  It is the 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     4
foundation for a complete reporting solution in Python.  It is also the
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     5
foundation for piddlePDF, the PDF back end for PIDDLE.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     6
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     7
Documentation is a little slim right now; run then look at testpdfgen.py
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     8
to get a clue.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
     9
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    10
---------- Licence Terms (same as the Python license) -----------------
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    11
(C) Copyright Robinson Analytics 1998-1999.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    12
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    13
Permission to use, copy, modify, and distribute this software and its
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    14
documentation for any purpose and without fee is hereby granted, provided
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    15
that the above copyright notice appear in all copies and that both that
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    16
copyright notice and this permission notice appear in supporting
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    17
documentation, and that the name of Robinson Analytics not be used
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    18
in advertising or publicity pertaining to distribution of the software
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    19
without specific, written prior permission. 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    20
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    21
ROBINSON ANALYTICS LTD. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    22
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    23
IN NO EVENT SHALL ROBINSON ANALYTICS BE LIABLE FOR ANY SPECIAL, INDIRECT
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    24
OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    25
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    26
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    27
PERFORMANCE OF THIS SOFTWARE. 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    28
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    29
Progress Reports:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    30
8.83, 2000-01-13, gmcm:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    31
    Packagizing:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    32
        renamed from pdfgen.py to canvas.py
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    33
        broke out PDFTextObject to textobject.py
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    34
        broke out PDFPathObject to pathobject.py
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    35
        placed all three in a package directory named pdfgen
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    36
0.82, 1999-10-27, AR:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    37
        Fixed some bugs on printing to Postscript.  Added 'Text Object'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    38
        analogous to Path Object to control entry and exit from text mode.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    39
        Much simpler clipping API.  All verified to export Postscript and
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    40
        redistill.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    41
        One limitation still - clipping to text paths is fine in Acrobat
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    42
        but not in Postscript (any level)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    43
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    44
0.81,1999-10-13, AR:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    45
        Adding RoundRect; changed all format strings to use %0.2f instead of %s,
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    46
        so we don't get exponentials in the output.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    47
0.8,1999-10-07, AR:  all changed!
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    48
    
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    49
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    50
"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    51
##  0.81    1999-10-13:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    52
##                
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    53
##
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    54
##
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    55
import os
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    56
import sys
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    57
import string
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    58
import time
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    59
import tempfile
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    60
import cStringIO
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    61
from types import *
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    62
from math import sin, cos, tan, pi, ceil
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    63
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    64
from pdfbase import pdfutils
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    65
from pdfbase import pdfdoc
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    66
from pdfbase import pdfmetrics
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    67
import pdfgeom
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    68
import pathobject
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    69
import textobject
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    70
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    71
# Robert Kern
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    72
# Constants for closing paths.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    73
# May be useful if one changes 'arc' and 'rect' to take a
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    74
# default argument that tells how to close the path.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    75
# That way we can draw filled shapes.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    76
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    77
FILL_EVEN_ODD = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    78
FILL_NON_ZERO = 1
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    79
    #this is used by path-closing routines.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    80
    #map stroke, fill, fillmode -> operator
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    81
    # fillmode: 1 = non-Zero (obviously), 0 = evenOdd
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    82
PATH_OPS = {(0, 0, FILL_EVEN_ODD) : 'n',  #no op
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    83
            (0, 0, FILL_NON_ZERO) : 'n',  #no op
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    84
            (1, 0, FILL_EVEN_ODD) : 'S',  #stroke only
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    85
            (1, 0, FILL_NON_ZERO) : 'S',  #stroke only
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    86
            (0, 1, FILL_EVEN_ODD) : 'f*',  #Fill only
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    87
            (0, 1, FILL_NON_ZERO) : 'f',  #Fill only
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    88
            (1, 1, FILL_EVEN_ODD) : 'B*',  #Stroke and Fill
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    89
            (1, 1, FILL_NON_ZERO) : 'B',  #Stroke and Fill
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    90
            }
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    91
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    92
close = 'h'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    93
newpath = 'n'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    94
stroke = 'S'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    95
closeStroke = 's'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    96
nzFill = 'f'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    97
eoFill = 'f*'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    98
fillStroke = 'B'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
    99
closeFillStroke = 'b'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   100
eoFillStroke = 'B*'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   101
closeEoFillStroke = 'b*'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   102
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   103
class Canvas:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   104
    """This is a low-level interface to the PDF file format.  The plan is to
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   105
    expose the whole pdfgen API through this.  Its drawing functions should have a
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   106
    one-to-one correspondence with PDF functionality.  Unlike PIDDLE, it thinks
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   107
    in terms of RGB values, Postscript font names, paths, and a 'current graphics
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   108
    state'.  Just started development at 5/9/99, not in use yet.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   109
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   110
    """
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   111
    def __init__(self,filename,pagesize=(595.27,841.89), bottomup = 1):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   112
        """Most of the attributes are private - we will use set/get methods
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   113
        as the preferred interface.  Default page size is A4."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   114
        self._filename = filename
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   115
        self._doc = pdfdoc.PDFDocument()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   116
        self._pagesize = pagesize
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   117
        self._currentPageHasImages = 1
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   118
        self._pageTransitionString = ''
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   119
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   120
        self._pageCompression = 1  #on by default - turn off when debugging!
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   121
        self._pageNumber = 1   # keep a count
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   122
        self._code = []    #where the current page's marking operators accumulate
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   123
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   124
        #PostScript has the origin at bottom left. It is easy to achieve a top-
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   125
        #down coord system by translating to the top of the page and setting y
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   126
        #scale to -1, but then text is inverted.  So self.bottomup is used
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   127
        #to also set the text matrix accordingly.  You can now choose your
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   128
        #drawing coordinates.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   129
        self.bottomup = bottomup
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   130
        if self.bottomup:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   131
            #set initial font
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   132
            #self._preamble = 'BT /F9 12 Tf 14.4 TL ET'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   133
            self._preamble = '1 0 0 1 0 0 cm BT /F9 12 Tf 14.4 TL ET'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   134
        else:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   135
            #switch coordinates, flip text and set font
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   136
            #self._preamble = '1 0 0 -1 0 %0.2f cm BT /F9 12 Tf 14.4 TL ET' % self._pagesize[1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   137
            self._preamble = '1 0 0 -1 0 %0.2f cm BT /F9 12 Tf 14.4 TL ET' % self._pagesize[1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   138
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   139
        #initial graphics state
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   140
        self._x = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   141
        self._y = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   142
        self._fontname = 'Times-Roman'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   143
        self._fontsize = 12
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   144
        self._textMode = 0  #track if between BT/ET
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   145
        self._leading = 14.4
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   146
        self._currentMatrix = (1., 0., 0., 1., 0., 0.)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   147
        self._fillMode = 0   #even-odd
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   148
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   149
        #text state        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   150
        self._charSpace = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   151
        self._wordSpace = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   152
        self._horizScale = 100
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   153
        self._textRenderMode = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   154
        self._rise = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   155
        self._textLineMatrix = (1., 0., 0., 1., 0., 0.)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   156
        self._textMatrix = (1., 0., 0., 1., 0., 0.)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   157
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   158
        # line drawing        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   159
        self._lineCap = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   160
        self._lineJoin = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   161
        self._lineDash = None  #not done
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   162
        self._lineWidth = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   163
        self._mitreLimit = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   164
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   165
        self._fillColorRGB = (0,0,0)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   166
        self._strokeColorRGB = (0,0,0)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   167
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   168
    def _escape(self, s):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   169
        """PDF escapes are like Python ones, but brackets need slashes before them too.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   170
        Use Python's repr function and chop off the quotes first"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   171
        s = repr(s)[1:-1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   172
        s = string.replace(s, '(','\(')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   173
        s = string.replace(s, ')','\)')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   174
        return s
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   175
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   176
    #info functions - non-standard
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   177
    def setAuthor(self, author):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   178
        self._doc.setAuthor(author)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   179
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   180
    def setTitle(self, title):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   181
        self._doc.setTitle(title)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   182
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   183
    def setSubject(self, subject):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   184
        self._doc.setSubject(subject)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   185
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   186
    def pageHasData(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   187
        "Info function - app can call it after showPage to see if it needs a save"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   188
        return len(self._code) == 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   189
    
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   190
    def showPage(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   191
        """This is where the fun happens"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   192
        page = pdfdoc.PDFPage()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   193
        page.pagewidth = self._pagesize[0]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   194
        page.pageheight = self._pagesize[1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   195
        page.hasImages = self._currentPageHasImages
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   196
        page.pageTransitionString = self._pageTransitionString
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   197
        page.setCompression(self._pageCompression)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   198
        #print stream
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   199
        page.setStream([self._preamble] + self._code)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   200
        self._doc.addPage(page)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   201
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   202
        #now get ready for the next one
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   203
        self._pageNumber = self._pageNumber + 1
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   204
        self._code = []    # ready for more...
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   205
        self._currentPageHasImages = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   206
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   207
    def getPageNumber(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   208
        return self._pageNumber
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   209
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   210
    def save(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   211
        """Saves the file.  If holding data, do
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   212
        a showPage() to save them having to."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   213
        if len(self._code):  
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   214
            self.showPage()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   215
        self._doc.SaveToFile(self._filename)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   216
        print 'saved', self._filename
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   217
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   218
    def setPageSize(self, size):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   219
        """accepts a 2-tuple in points for paper size for this
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   220
        and subsequent pages"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   221
        self._pagesize = size
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   222
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   223
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   224
    def addLiteral(self, s, escaped=1):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   225
        if escaped==0:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   226
            s = self._escape(s)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   227
        self._code.append(s)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   228
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   229
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   230
        ######################################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   231
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   232
        #      coordinate transformations
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   233
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   234
        ######################################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   235
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   236
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   237
    def transform(self, a,b,c,d,e,f):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   238
        """How can Python track this?"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   239
        a0,b0,c0,d0,e0,f0 = self._currentMatrix
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   240
        self._currentMatrix = (a0*a+c0*b,    b0*a+d0*b,
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   241
                               a0*c+c0*d,    b0*c+d0*d,
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   242
                               a0*e+c0*f+e0, b0*e+d0*f+f0)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   243
        self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f cm' % (a,b,c,d,e,f))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   244
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   245
    def translate(self, dx, dy):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   246
        self.transform(1,0,0,1,dx,dy)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   247
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   248
    def scale(self, x, y):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   249
        self.transform(x,0,0,y,0,0)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   250
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   251
    def rotate(self, theta):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   252
        """Canvas.rotate(theta)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   253
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   254
        theta is in degrees."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   255
        c = cos(theta * pi / 180)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   256
        s = sin(theta * pi / 180)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   257
        self.transform(c, s, -s, c, 0, 0)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   258
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   259
    def skew(self, alpha, beta):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   260
        tanAlpha = tan(alpha * pi / 180)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   261
        tanBeta  = tan(beta  * pi / 180)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   262
        self.transform(1, tanAlpha, tanBeta, 1, 0, 0)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   263
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   264
        ######################################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   265
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   266
        #      graphics state management
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   267
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   268
        ######################################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   269
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   270
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   271
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   272
    def saveState(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   273
        """These need expanding to save/restore Python's state tracking too"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   274
        self._code.append('q')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   275
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   276
    def restoreState(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   277
        """These need expanding to save/restore Python's state tracking too"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   278
        self._code.append('Q')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   279
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   280
        ###############################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   281
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   282
        #   Drawing methods.  These draw things directly without
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   283
        #   fiddling around with Path objects.  We can add any geometry
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   284
        #   methods we wish as long as their meaning is precise and
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   285
        #   they are of general use.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   286
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   287
        #   In general there are two patterns.  Closed shapes
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   288
        #   have the pattern shape(self, args, stroke=1, fill=0);
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   289
        #   by default they draw an outline only. Line segments come
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   290
        #   in three flavours: line, bezier, arc (which is a segment
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   291
        #   of an elliptical arc, approximated by up to four bezier
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   292
        #   curves, one for each quadrant.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   293
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   294
        #   In the case of lines, we provide a 'plural' to unroll
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   295
        #   the inner loop; it is useful for drawing big grids
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   296
        ################################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   297
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   298
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   299
        #--------first the line drawing methods-----------------------
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   300
    def line(self, x1,y1, x2,y2):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   301
        "As it says"       
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   302
        self._code.append('n %0.2f %0.2f m %0.2f %0.2f l S' % (x1, y1, x2, y2))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   303
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   304
    def lines(self, linelist):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   305
        """As line(), but slightly more efficient for lots of them -
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   306
        one stroke operation and one less function call"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   307
        self._code.append('n')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   308
        for (x1,y1,x2,y2) in linelist:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   309
            self._code.append('%0.2f %0.2f m %0.2f %0.2f l' % (x1, y1, x2, y2))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   310
        self._code.append('S')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   311
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   312
    def grid(self, xlist, ylist):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   313
        """Lays out a grid in current line style.  Suuply list of
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   314
        x an y positions."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   315
        assert len(xlist) > 1, "x coordinate list must have 2+ items"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   316
        assert len(ylist) > 1, "y coordinate list must have 2+ items"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   317
        lines = []
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   318
        y0, y1 = ylist[0], ylist[-1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   319
        x0, x1 = xlist[0], xlist[-1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   320
        for x in xlist:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   321
            lines.append(x,y0,x,y1)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   322
        for y in ylist:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   323
            lines.append(x0,y,x1,y)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   324
        self.lines(lines)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   325
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   326
    def bezier(self, x1, y1, x2, y2, x3, y3, x4, y4):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   327
        "Bezier curve with the four given control points"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   328
        self._code.append('n %0.2f %0.2f m %0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c S' %
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   329
                          (x1, y1, x2, y2, x3, y3, x4, y4)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   330
                          )
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   331
    def arc(self, x1,y1, x2,y2, startAng=0, extent=90):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   332
        """Contributed to piddlePDF by Robert Kern, 28/7/99.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   333
        Trimmed down by AR to remove color stuff for pdfgen.canvas and
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   334
        revert to positive coordinates.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   335
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   336
        Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2,
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   337
        starting at startAng degrees and covering extent degrees.   Angles
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   338
        start with 0 to the right (+x) and increase counter-clockwise.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   339
        These should have x1<x2 and y1<y2.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   340
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   341
        The algorithm is an elliptical generalization of the formulae in
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   342
        Jim Fitzsimmon's TeX tutorial <URL: http://www.tinaja.com/bezarc1.pdf>."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   343
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   344
        pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   345
        #move to first point
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   346
        self._code.append('n %0.2f %0.2f m' % pointList[0][:2])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   347
        for curve in pointList:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   348
            self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' % curve[2:])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   349
        # stroke
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   350
        self._code.append('S')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   351
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   352
        #--------now the shape drawing methods-----------------------
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   353
    def rect(self, x, y, width, height, stroke=1, fill=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   354
        "draws a rectangle"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   355
        self._code.append('n %0.2f %0.2f %0.2f %0.2f re ' % (x, y, width, height)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   356
                          + PATH_OPS[stroke, fill, self._fillMode])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   357
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   358
    
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   359
    def ellipse(self, x1, y1, x2, y2, stroke=1, fill=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   360
        """Uses bezierArc, which conveniently handles 360 degrees -
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   361
        nice touch Robert"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   362
        pointList = pdfgeom.bezierArc(x1,y1, x2,y2, 0, 360)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   363
        #move to first point
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   364
        self._code.append('n %0.2f %0.2f m' % pointList[0][:2])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   365
        for curve in pointList:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   366
            self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' % curve[2:])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   367
        #finish
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   368
        self._code.append(PATH_OPS[stroke, fill, self._fillMode])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   369
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   370
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   371
    def wedge(self, x1,y1, x2,y2, startAng, extent, stroke=1, fill=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   372
        """Like arc, but connects to the centre of the ellipse.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   373
        Most useful for pie charts and PacMan!"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   374
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   375
        x_cen  = (x1+x2)/2.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   376
        y_cen  = (y1+y2)/2.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   377
        pointList = pdfgeom.bezierArc(x1,y1, x2,y2, startAng, extent)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   378
  
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   379
        self._code.append('n %0.2f %0.2f m' % (x_cen, y_cen))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   380
        # Move the pen to the center of the rectangle
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   381
        self._code.append('%0.2f %0.2f l' % pointList[0][:2])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   382
        for curve in pointList:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   383
            self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c' % curve[2:])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   384
        # finish the wedge
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   385
        self._code.append('%0.2f %0.2f l ' % (x_cen, y_cen))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   386
        # final operator
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   387
        self._code.append(PATH_OPS[stroke, fill, self._fillMode])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   388
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   389
    def circle(self, x_cen, y_cen, r, stroke=1, fill=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   390
        """special case of ellipse"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   391
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   392
        x1 = x_cen - r
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   393
        x2 = x_cen + r
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   394
        y1 = y_cen - r
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   395
        y2 = y_cen + r
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   396
        self.ellipse(x1, y1, x2, y2, stroke, fill)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   397
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   398
    def roundRect(self, x, y, width, height, radius, stroke=1, fill=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   399
        """Draws a rectangle with rounded corners.  The corners are
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   400
        approximately quadrants of a circle, with the given radius."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   401
        #use a precomputed set of factors for the bezier approximation
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   402
        #to a circle. There are six relevant points on the x axis and y axis.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   403
        #sketch them and it should all make sense!
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   404
        t = 0.4472 * radius
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   405
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   406
        x0 = x
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   407
        x1 = x0 + t
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   408
        x2 = x0 + radius
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   409
        x3 = x0 + width - radius
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   410
        x4 = x0 + width - t
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   411
        x5 = x0 + width
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   412
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   413
        y0 = y
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   414
        y1 = y0 + t
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   415
        y2 = y0 + radius
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   416
        y3 = y0 + height - radius
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   417
        y4 = y0 + height - t
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   418
        y5 = y0 + height
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   419
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   420
        self._code.append('n %0.2f %0.2f m' % (x2, y0))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   421
        self._code.append('%0.2f %0.2f l' % (x3, y0))  # bottom row
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   422
        self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   423
                         % (x4, y0, x5, y1, x5, y2)) # bottom right
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   424
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   425
        self._code.append('%0.2f %0.2f l' % (x5, y3))  # right edge
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   426
        self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   427
                         % (x5, y4, x4, y5, x3, y5)) # top right
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   428
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   429
        self._code.append('%0.2f %0.2f l' % (x2, y5))  # top row
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   430
        self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   431
                         % (x1, y5, x0, y4, x0, y3)) # top left
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   432
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   433
        self._code.append('%0.2f %0.2f l' % (x0, y2))  # left edge
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   434
        self._code.append('%0.2f %0.2f %0.2f %0.2f %0.2f %0.2f c'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   435
                         % (x0, y1, x1, y0, x2, y0)) # bottom left
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   436
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   437
        self._code.append('h')  #close off, although it should be where it started anyway
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   438
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   439
    
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   440
        self._code.append(PATH_OPS[stroke, fill, self._fillMode])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   441
        ##################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   442
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   443
        #  Text methods
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   444
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   445
        # As with graphics, a separate object ensures that
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   446
        # everything is bracketed between  text operators.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   447
        # The methods below are a high-level convenience.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   448
        # use PDFTextObject for multi-line text.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   449
        ##################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   450
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   451
    def drawString(self, x, y, text):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   452
        """Draws a string in the current text styles."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   453
        #we could inline this for speed if needed
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   454
        t = self.beginText(x, y)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   455
        t.textLine(text)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   456
        self.drawText(t)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   457
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   458
    def drawRightString(self, x, y, text):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   459
        """Draws a string right-aligned with the y coordinate"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   460
        width = self.stringWidth(text, self._fontname, self._fontsize)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   461
        t = self.beginText(x - width, y)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   462
        t.textLine(text)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   463
        self.drawText(t)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   464
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   465
    def drawCentredString(self, x, y, text):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   466
        """Draws a string right-aligned with the y coordinate.  I
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   467
        am British so the spelling is correct, OK?"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   468
        width = self.stringWidth(text, self._fontname, self._fontsize)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   469
        t = self.beginText(x - 0.5*width, y)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   470
        t.textLine(text)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   471
        self.drawText(t)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   472
  
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   473
    def getAvailableFonts(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   474
        """Returns the list of PostScript font names available.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   475
        Standard set now, but may grow in future with font embedding."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   476
        fontnames = self._doc.getAvailableFonts()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   477
        fontnames.sort()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   478
        return fontnames
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   479
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   480
    def setFont(self, psfontname, size, leading = None):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   481
        """Sets the font.  If leading not specified, defaults to 1.2 x
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   482
        font size. Raises a readable exception if an illegal font
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   483
        is supplied.  Font names are case-sensitive! Keeps track
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   484
        of font anme and size for metrics."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   485
        self._fontname = psfontname
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   486
        self._fontsize = size
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   487
        pdffontname = self._doc.getInternalFontName(psfontname)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   488
        if leading is None:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   489
            leading = size * 1.2
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   490
        self._leading = leading
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   491
        self._code.append('BT %s %0.1f Tf %0.1f TL ET' % (pdffontname, size, leading))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   492
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   493
    def stringWidth(self, text, fontname, fontsize):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   494
        "gets width of a string in the given font and size"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   495
        return pdfmetrics.stringwidth(text, fontname) * 0.001 * fontsize
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   496
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   497
    # basic graphics modes
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   498
    def setLineWidth(self, width):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   499
        self._lineWidth = width
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   500
        self._code.append('%0.2f w' % width)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   501
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   502
    def setLineCap(self, mode):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   503
        """0=butt,1=round,2=square"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   504
        assert mode in (0,1,2), "Line caps allowed: 0=butt,1=round,2=square"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   505
        self._lineCap = mode
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   506
        self._code.append('%d J' % mode)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   507
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   508
    def setLineJoin(self, mode):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   509
        """0=mitre, 1=round, 2=bevel"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   510
        assert mode in (0,1,2), "Line Joins allowed: 0=mitre, 1=round, 2=bevel"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   511
        self._lineJoin = mode
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   512
        self._code.append('%d j' % mode)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   513
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   514
    def setMiterLimit(self, limit):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   515
        self._miterLimit = limit
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   516
        self._code.append('%0.2f M' % limit)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   517
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   518
    def setDash(self, array=[], phase=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   519
        """Two notations.  pass two numbers, or an array and phase"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   520
        if type(array) == IntType or type(array) == FloatType:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   521
            self._code.append('[%s %s] 0 d' % (array, phase))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   522
        elif type(array) == ListType or type(Array) == TupleType:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   523
            assert phase <= len(array), "setDash phase must be l.t.e. length of array"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   524
            textarray = string.join(map(str, array))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   525
            self._code.append('[%s] %s d' % (textarray, phase))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   526
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   527
    def setFillColorRGB(self, r, g, b):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   528
        self._fillColorRGB = (r, g, b)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   529
        self._code.append('%0.2f %0.2f %0.2f rg' % (r,g,b))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   530
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   531
    def setStrokeColorRGB(self, r, g, b):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   532
        self._strokeColorRGB = (r, g, b)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   533
        self._code.append('%0.2f %0.2f %0.2f RG' % (r,g,b))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   534
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   535
    # path stuff - the separate path object builds it    
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   536
    def beginPath(self):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   537
        """Returns a fresh path object"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   538
        return pathobject.PDFPathObject()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   539
    
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   540
    def drawPath(self, aPath, stroke=1, fill=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   541
        "Draw in the mode indicated"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   542
        op = PATH_OPS[stroke, fill, self._fillMode]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   543
        self._code.append(aPath.getCode() + ' ' + op)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   544
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   545
    def clipPath(self, aPath, stroke=1, fill=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   546
        "clip as well as drawing"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   547
        op = PATH_OPS[stroke, fill, self._fillMode]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   548
        self._code.append(aPath.getCode() + ' W ' + op)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   549
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   550
    def beginText(self, x=0, y=0):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   551
        """Returns a fresh text object"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   552
        return textobject.PDFTextObject(self, x, y)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   553
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   554
    def drawText(self, aTextObject):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   555
        """Draws a text object"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   556
        self._code.append(aTextObject.getCode())
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   557
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   558
        ######################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   559
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   560
        #   Image routines
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   561
        #
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   562
        ######################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   563
    def drawInlineImage(self, image, x,y, width=None,height=None):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   564
        """Draw a PIL Image into the specified rectangle.  If width and
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   565
        height are omitted, they are calculated from the image size.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   566
        Also allow file names as well as images.  This allows a
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   567
        caching mechanism"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   568
        try: 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   569
            import Image
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   570
        except ImportError:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   571
            print 'Python Imaging Library not available'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   572
            return
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   573
        try:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   574
            import zlib
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   575
        except ImportError:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   576
            print 'zlib not available'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   577
            return
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   578
            
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   579
        self.currentPageHasImages = 1
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   580
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   581
        if type(image) == StringType:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   582
            if os.path.splitext(image)[1] in ['.jpg', '.JPG']:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   583
            #directly process JPEG files
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   584
            #open file, needs some error handling!!
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   585
                imageFile = open(image, 'rb')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   586
                info = self.readJPEGInfo(imageFile)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   587
                imgwidth, imgheight = info[0], info[1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   588
                if info[2] == 1:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   589
                    colorSpace = 'DeviceGray'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   590
                elif info[2] == 3:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   591
                    colorSpace = 'DeviceRGB'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   592
                else: #maybe should generate an error, is this right for CMYK?
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   593
                    colorSpace = 'DeviceCMYK'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   594
                imageFile.seek(0)		#reset file pointer
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   595
                imagedata = []
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   596
                imagedata.append('BI')   # begin image
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   597
                # this describes what is in the image itself
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   598
                imagedata.append('/Width %0.2f /Height %0.2f' %(info[0], info[1]))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   599
                imagedata.append('/BitsPerComponent 8')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   600
                imagedata.append('/ColorSpace /%s' % colorSpace)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   601
                imagedata.append('/Filter [ /ASCII85Decode /DCTDecode]')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   602
                imagedata.append('ID')   
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   603
                #write in blocks of (??) 60 characters per line to a list
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   604
                compressed = imageFile.read()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   605
                encoded = pdfutils._AsciiBase85Encode(compressed)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   606
                outstream = cStringIO.StringIO(encoded)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   607
                dataline = outstream.read(60)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   608
                while dataline <> "":
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   609
                    imagedata.append(dataline)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   610
                    dataline = outstream.read(60)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   611
                imagedata.append('EI')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   612
            else:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   613
                if not pdfutils.cachedImageExists(image):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   614
                    pdfutils.cacheImageFile(image)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   615
                #now we have one cached, slurp it in
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   616
                cachedname = os.path.splitext(image)[0] + '.a85'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   617
                imagedata = open(cachedname,'rb').readlines()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   618
                #trim off newlines...
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   619
                imagedata = map(string.strip, imagedata)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   620
                
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   621
                #parse line two for width, height
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   622
                words = string.split(imagedata[1])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   623
                imgwidth = string.atoi(words[1])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   624
                imgheight = string.atoi(words[3])
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   625
        else:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   626
            #PIL Image
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   627
            #work out all dimensions
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   628
            myimage = image.convert('RGB')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   629
            imgwidth, imgheight = myimage.size
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   630
            imagedata = []
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   631
            imagedata.append('BI')   # begin image
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   632
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   633
            # this describes what is in the image itself
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   634
            imagedata.append('/W %0.2f /H %0.2f /BPC 8 /CS /RGB /F [/A85 /Fl]' % (imgwidth, imgheight))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   635
            imagedata.append('ID')   
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   636
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   637
            #use a flate filter and Ascii Base 85 to compress
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   638
            raw = myimage.tostring()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   639
            assert(len(raw) == imgwidth * imgheight, "Wrong amount of data for image")
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   640
            compressed = zlib.compress(raw)   #this bit is very fast...
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   641
            encoded = pdfutils._AsciiBase85Encode(compressed) #...sadly this isn't
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   642
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   643
            #write in blocks of (??) 60 characters per line to a list
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   644
            outstream = cStringIO.StringIO(encoded)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   645
            dataline = outstream.read(60)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   646
            while dataline <> "":
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   647
                imagedata.append(dataline)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   648
                dataline = outstream.read(60)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   649
            imagedata.append('EI')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   650
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   651
        #now build the PDF for the image.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   652
        if not width:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   653
            width = imgwidth
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   654
        if not height:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   655
            height = imgheight
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   656
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   657
        # this says where and how big to draw it
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   658
        #self._code.append('ET')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   659
        #self._code.append('q %0.2f 0 0 %0.2f %0.2f %0.2f cm' % (width, height, x, y+height))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   660
        if self.bottomup:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   661
            self._code.append('q %0.2f 0 0 %0.2f %0.2f %0.2f cm' % (width, height, x, y))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   662
        else:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   663
            self._code.append('q %0.2f 0 0 %0.2f %0.2f %0.2f cm' % (width, height, x, y+height))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   664
        self._code.extend(imagedata)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   665
        self._code.append('Q')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   666
        #self._code.append('BT')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   667
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   668
#########################################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   669
#
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   670
#  JPEG processing code - contributed by Eric Johnson
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   671
#
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   672
#########################################################################
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   673
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   674
    # Read data from the JPEG file. We should probably be using PIL to
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   675
    # get this information for us -- but this way is more fun!
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   676
    # Returns (width, height, color components) as a triple
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   677
    # This is based on Thomas Merz's code from GhostScript (viewjpeg.ps)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   678
    def readJPEGInfo(self, image):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   679
        "Read width, height and number of components from JPEG file"
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   680
    	import struct
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   681
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   682
	#Acceptable JPEG Markers:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   683
	#  SROF0=baseline, SOF1=extended sequential or SOF2=progressive
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   684
	validMarkers = [0xC0, 0xC1, 0xC2]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   685
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   686
	#JPEG markers without additional parameters
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   687
	noParamMarkers = \
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   688
	    [ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0x01 ]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   689
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   690
	#Unsupported JPEG Markers
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   691
	unsupportedMarkers = \
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   692
	    [ 0xC3, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF ]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   693
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   694
	#read JPEG marker segments until we find SOFn marker or EOF
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   695
	done = 0
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   696
	while not done:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   697
	    x = struct.unpack('B', image.read(1))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   698
	    if x[0] == 0xFF:			#found marker
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   699
	    	x = struct.unpack('B', image.read(1))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   700
		#print "Marker: ", '%0.2x' % x[0]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   701
		#check marker type is acceptable and process it
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   702
		if x[0] in validMarkers:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   703
		    image.seek(2, 1)		#skip segment length
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   704
		    x = struct.unpack('B', image.read(1)) #data precision
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   705
		    if x[0] != 8:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   706
			raise 'PDFError', ' JPEG must have 8 bits per component'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   707
		    y = struct.unpack('BB', image.read(2))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   708
		    height = (y[0] << 8) + y[1] 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   709
		    y = struct.unpack('BB', image.read(2))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   710
		    width =  (y[0] << 8) + y[1]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   711
		    y = struct.unpack('B', image.read(1))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   712
		    color =  y[0]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   713
		    return width, height, color
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   714
		    done = 1
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   715
		elif x[0] in unsupportedMarkers:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   716
		    raise 'PDFError', ' Unsupported JPEG marker: %0.2x' % x[0]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   717
		elif x[0] not in noParamMarkers:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   718
		    #skip segments with parameters
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   719
		    #read length and skip the data
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   720
		    x = struct.unpack('BB', image.read(2))
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   721
		    image.seek( (x[0] << 8) + x[1] - 2, 1)	
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   722
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   723
    def setPageCompression(self, onoff=1):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   724
        """Possible values 1 or 0 (1 for 'on' is the default).
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   725
        If on, the page data will be compressed, leading to much
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   726
        smaller files, but takes a little longer to create the files.
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   727
        This applies to all subsequent pages, or until setPageCompression()
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   728
        is next called."""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   729
        self._pageCompression = onoff
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   730
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   731
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   732
    def setPageTransition(self, effectname=None, duration=1, 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   733
                        direction=0,dimension='H',motion='I'):
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   734
        """PDF allows page transition effects for use when giving
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   735
        presentations.  There are six possible effects.  You can
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   736
        just guive the effect name, or supply more advanced options
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   737
        to refine the way it works.  There are three types of extra
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   738
        argument permitted, and here are the allowed values:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   739
            direction_arg = [0,90,180,270]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   740
            dimension_arg = ['H', 'V']
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   741
            motion_arg = ['I','O'] (start at inside or outside)
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   742
            
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   743
        This table says which ones take which arguments:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   744
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   745
        PageTransitionEffects = {
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   746
            'Split': [direction_arg, motion_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   747
            'Blinds': [dimension_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   748
            'Box': [motion_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   749
            'Wipe' : [direction_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   750
            'Dissolve' : [],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   751
            'Glitter':[direction_arg]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   752
            }
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   753
        Have fun!
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   754
"""
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   755
        if not effectname:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   756
            self._pageTransitionString = ''
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   757
            return
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   758
            
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   759
        #first check each optional argument has an allowed value
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   760
        if direction in [0,90,180,270]:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   761
            direction_arg = '/Di /%d' % direction
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   762
        else:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   763
            raise 'PDFError', ' directions allowed are 0,90,180,270'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   764
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   765
        if dimension in ['H', 'V']:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   766
            dimension_arg = '/Dm /%s' % dimension
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   767
        else:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   768
            raise'PDFError','dimension values allowed are H and V'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   769
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   770
        if motion in ['I','O']:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   771
            motion_arg = '/M /%s' % motion
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   772
        else:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   773
            raise'PDFError','motion values allowed are I and O'
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   774
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   775
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   776
        # this says which effects require which argument types from above
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   777
        PageTransitionEffects = {
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   778
            'Split': [direction_arg, motion_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   779
            'Blinds': [dimension_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   780
            'Box': [motion_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   781
            'Wipe' : [direction_arg],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   782
            'Dissolve' : [],
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   783
            'Glitter':[direction_arg]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   784
            }
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   785
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   786
        try:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   787
            args = PageTransitionEffects[effectname]
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   788
        except KeyError:
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   789
            raise 'PDFError', 'Unknown Effect Name "%s"' % effectname
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   790
            self._pageTransitionString = ''
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   791
            return
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   792
        
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   793
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   794
        self._pageTransitionString = (('/Trans <</D %d /S /%s ' % (duration, effectname)) + 
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   795
            string.join(args, ' ') + ' >>')
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   796
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   797
if __name__ == '__main__':
f19e0a2433ab Initial revision
gmcm
parents:
diff changeset
   798
    print 'For test scripts, run testpdfgen.py'