~openerp-groupes/openobject-server/6.0-fix-setup-windows

« back to all changes in this revision

Viewing changes to bin/reportlab/pdfgen/pycanvas.py

  • Committer: pinky
  • Date: 2006-12-07 13:41:40 UTC
  • Revision ID: pinky-3f10ee12cea3c4c75cef44ab04ad33ef47432907
New trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# a Pythonesque Canvas v0.8
 
2
# Author : Jerome Alet - <alet@librelogiciel.com>
 
3
# License : ReportLab's license
 
4
#
 
5
# $Id$
 
6
#
 
7
__doc__ = """pycanvas.Canvas : a Canvas class which can also output Python source code.
 
8
 
 
9
pycanvas.Canvas class works exactly like canvas.Canvas, but you can
 
10
call str() on pycanvas.Canvas instances. Doing so will return the
 
11
Python source code equivalent to your own program, which would, when
 
12
run, produce the same PDF document as your original program.
 
13
 
 
14
Generated Python source code defines a doIt() function which accepts
 
15
a filename or file-like object as its first parameter, and an
 
16
optional boolean parameter named "regenerate".
 
17
 
 
18
The doIt() function will generate a PDF document and save it in the
 
19
file you specified in this argument. If the regenerate parameter is
 
20
set then it will also return an automatically generated equivalent
 
21
Python source code as a string of text, which you can run again to
 
22
produce the very same PDF document and the Python source code, which
 
23
you can run again... ad nauseam ! If the regenerate parameter is
 
24
unset or not used at all (it then defaults to being unset) then None
 
25
is returned and the doIt() function is much much faster, it is also
 
26
much faster than the original non-serialized program.
 
27
 
 
28
the reportlab/test/test_pdfgen_pycanvas.py program is the test suite
 
29
for pycanvas, you can do the following to run it :
 
30
 
 
31
    First set verbose=1 in reportlab/rl_config.py
 
32
 
 
33
    then from the command interpreter :
 
34
 
 
35
    $ cd reportlab/test
 
36
    $ python test_pdfgen_pycanvas.py >n1.py
 
37
 
 
38
    this will produce both n1.py and test_pdfgen_pycanvas.pdf
 
39
 
 
40
    then :
 
41
 
 
42
    $ python n1.py n1.pdf >n2.py
 
43
    $ python n2.py n2.pdf >n3.py
 
44
    $ ...
 
45
 
 
46
    n1.py, n2.py, n3.py and so on will be identical files.
 
47
    they eventually may end being a bit different because of
 
48
    rounding problems, mostly in the comments, but this
 
49
    doesn't matter since the values really are the same
 
50
    (e.g. 0 instead of 0.0, or .53 instead of 0.53)
 
51
 
 
52
    n1.pdf, n2.pdf, n3.pdf and so on will be PDF files
 
53
    similar to test_pdfgen_pycanvas.pdf.
 
54
 
 
55
Alternatively you can import n1.py (or n3.py, or n16384.py if you prefer)
 
56
in your own program, and then call its doIt function :
 
57
 
 
58
    import n1
 
59
    pythonsource = n1.doIt("myfile.pdf", regenerate=1)
 
60
 
 
61
Or if you don't need the python source code and want a faster result :
 
62
 
 
63
    import n1
 
64
    n1.doIt("myfile.pdf")
 
65
 
 
66
When the generated source code is run directly as an independant program,
 
67
then the equivalent python source code is printed to stdout, e.g. :
 
68
 
 
69
    python n1.py
 
70
 
 
71
    will print the python source code equivalent to n1.py
 
72
 
 
73
Why would you want to use such a beast ?
 
74
 
 
75
    - To linearize (serialize?) a program : optimizing some complex
 
76
      parts for example.
 
77
 
 
78
    - To debug : reading the generated Python source code may help you or
 
79
      the ReportLab team to diagnose problems. The generated code is now
 
80
      clearly commented and shows nesting levels, page numbers, and so
 
81
      on. You can use the generated script when asking for support : we
 
82
      can see the results you obtain without needing your datas or complete
 
83
      application.
 
84
 
 
85
    - To create standalone scripts : say your program uses a high level
 
86
      environment to generate its output (databases, RML, etc...), using
 
87
      this class would give you an equivalent program but with complete
 
88
      independance from the high level environment (e.g. if you don't
 
89
      have Oracle).
 
90
 
 
91
    - To contribute some nice looking PDF documents to the ReportLab website
 
92
      without having to send a complete application you don't want to
 
93
      distribute.
 
94
 
 
95
    - ... Insert your own ideas here ...
 
96
 
 
97
    - For fun because you can do it !
 
98
"""
 
99
 
 
100
import cStringIO
 
101
from reportlab.pdfgen import canvas
 
102
from reportlab.pdfgen import pathobject
 
103
from reportlab.pdfgen import textobject
 
104
 
 
105
PyHeader = '''#! /usr/bin/env python
 
106
 
 
107
#
 
108
# This code was entirely generated by ReportLab (http://www.reportlab.com)
 
109
#
 
110
 
 
111
import sys
 
112
from reportlab.pdfgen import pathobject
 
113
from reportlab.pdfgen import textobject
 
114
from reportlab.lib.colors import Color
 
115
 
 
116
def doIt(file, regenerate=0) :
 
117
    """Generates a PDF document, save it into file.
 
118
 
 
119
       file : either a filename or a file-like object.
 
120
 
 
121
       regenerate : if set then this function returns the Python source
 
122
                    code which when run will produce the same result.
 
123
                    if unset then this function returns None, and is
 
124
                    much faster.
 
125
    """
 
126
    if regenerate :
 
127
        from reportlab.pdfgen.pycanvas import Canvas
 
128
    else :
 
129
        from reportlab.pdfgen.canvas import Canvas
 
130
'''
 
131
 
 
132
PyFooter = '''
 
133
    # if we want the equivalent Python source code, then send it back
 
134
    if regenerate :
 
135
        return str(c)
 
136
 
 
137
if __name__ == "__main__" :
 
138
    if len(sys.argv) != 2 :
 
139
        # second argument must be the name of the PDF file to create
 
140
        sys.stderr.write("%s needs one and only one argument\\n" % sys.argv[0])
 
141
        sys.exit(-1)
 
142
    else :
 
143
        # we've got a filename, we can proceed.
 
144
        print doIt(sys.argv[1], regenerate=1)
 
145
        sys.exit(0)'''
 
146
 
 
147
def buildargs(*args, **kwargs) :
 
148
    """Constructs a printable list of arguments suitable for use in source function calls."""
 
149
    arguments = ""
 
150
    for arg in args :
 
151
        arguments = arguments + ("%s, " % repr(arg))
 
152
    for (kw, val) in kwargs.items() :
 
153
        arguments = arguments+ ("%s=%s, " % (kw, repr(val)))
 
154
    if arguments[-2:] == ", " :
 
155
        arguments = arguments[:-2]
 
156
    return arguments
 
157
 
 
158
class PDFAction :
 
159
    """Base class to fake method calls or attributes on PDF objects (Canvas, PDFPathObject, PDFTextObject)."""
 
160
    def __init__(self, parent, action) :
 
161
        """Saves a pointer to the parent object, and the method name."""
 
162
        self._parent = parent
 
163
        self._action = action
 
164
 
 
165
    def __getattr__(self, name) :
 
166
        """Probably a method call on an attribute, returns the real one."""
 
167
        return getattr(getattr(self._parent._object, self._action), name)
 
168
 
 
169
    def __call__(self, *args, **kwargs) :
 
170
        """The fake method is called, print it then call the real one."""
 
171
        if not self._parent._parent._in :
 
172
            self._precomment()
 
173
            self._parent._parent._PyWrite("    %s.%s(%s)" % (self._parent._name, self._action, apply(buildargs, args, kwargs)))
 
174
            self._postcomment()
 
175
        self._parent._parent._in = self._parent._parent._in + 1
 
176
        retcode = apply(getattr(self._parent._object, self._action), args, kwargs)
 
177
        self._parent._parent._in = self._parent._parent._in - 1
 
178
        return retcode
 
179
 
 
180
    def __hash__(self) :
 
181
        return hash(getattr(self._parent._object, self._action))
 
182
 
 
183
    def __coerce__(self, other) :
 
184
        """Needed."""
 
185
        return coerce(getattr(self._parent._object, self._action), other)
 
186
 
 
187
    def _precomment(self) :
 
188
        """To be overriden."""
 
189
        pass
 
190
 
 
191
    def _postcomment(self) :
 
192
        """To be overriden."""
 
193
        pass
 
194
 
 
195
class PDFObject :
 
196
    """Base class for PDF objects like PDFPathObject and PDFTextObject."""
 
197
    _number = 0
 
198
    def __init__(self, parent) :
 
199
        """Saves a pointer to the parent Canvas."""
 
200
        self._parent = parent
 
201
        self._initdone = 0
 
202
 
 
203
    def __getattr__(self, name) :
 
204
        """The user's programs wants to call one of our methods or get an attribute, fake it."""
 
205
        return PDFAction(self, name)
 
206
 
 
207
    def __repr__(self) :
 
208
        """Returns the name used in the generated source code (e.g. 'p' or 't')."""
 
209
        return self._name
 
210
 
 
211
    def __call__(self, *args, **kwargs) :
 
212
        """Real object initialisation is made here, because now we've got the arguments."""
 
213
        if not self._initdone :
 
214
            self.__class__._number = self.__class__._number + 1
 
215
            methodname = apply(self._postinit, args, kwargs)
 
216
            self._parent._PyWrite("\n    # create PDF%sObject number %i\n    %s = %s.%s(%s)" % (methodname[5:], self.__class__._number, self._name, self._parent._name, methodname, apply(buildargs, args, kwargs)))
 
217
            self._initdone = 1
 
218
        return self
 
219
 
 
220
class Canvas :
 
221
    """Our fake Canvas class, which will intercept each and every method or attribute access."""
 
222
    class TextObject(PDFObject) :
 
223
        _name = "t"
 
224
        def _postinit(self, *args, **kwargs) :
 
225
            self._object = apply(textobject.PDFTextObject, (self._parent, ) + args, kwargs)
 
226
            return "beginText"
 
227
 
 
228
    class PathObject(PDFObject) :
 
229
        _name = "p"
 
230
        def _postinit(self, *args, **kwargs) :
 
231
            self._object = apply(pathobject.PDFPathObject, args, kwargs)
 
232
            return "beginPath"
 
233
 
 
234
    class Action(PDFAction) :
 
235
        """Class called for every Canvas method call."""
 
236
        def _precomment(self) :
 
237
            """Outputs comments before the method call."""
 
238
            if self._action == "showPage" :
 
239
                self._parent._PyWrite("\n    # Ends page %i" % self._parent._pagenumber)
 
240
            elif self._action == "saveState" :
 
241
                state = {}
 
242
                d = self._parent._object.__dict__
 
243
                for name in self._parent._object.STATE_ATTRIBUTES:
 
244
                    state[name] = d[name]
 
245
                self._parent._PyWrite("\n    # Saves context level %i %s" % (self._parent._contextlevel, state))
 
246
                self._parent._contextlevel = self._parent._contextlevel + 1
 
247
            elif self._action == "restoreState" :
 
248
                self._parent._contextlevel = self._parent._contextlevel - 1
 
249
                self._parent._PyWrite("\n    # Restores context level %i %s" % (self._parent._contextlevel, self._parent._object.state_stack[-1]))
 
250
            elif self._action == "beginForm" :
 
251
                self._parent._formnumber = self._parent._formnumber + 1
 
252
                self._parent._PyWrite("\n    # Begins form %i" % self._parent._formnumber)
 
253
            elif self._action == "endForm" :
 
254
                self._parent._PyWrite("\n    # Ends form %i" % self._parent._formnumber)
 
255
            elif self._action == "save" :
 
256
                self._parent._PyWrite("\n    # Saves the PDF document to disk")
 
257
 
 
258
        def _postcomment(self) :
 
259
            """Outputs comments after the method call."""
 
260
            if self._action == "showPage" :
 
261
                self._parent._pagenumber = self._parent._pagenumber + 1
 
262
                self._parent._PyWrite("\n    # Begins page %i" % self._parent._pagenumber)
 
263
            elif self._action in [ "endForm", "drawPath", "clipPath" ] :
 
264
                self._parent._PyWrite("")
 
265
 
 
266
    _name = "c"
 
267
    def __init__(self, *args, **kwargs) :
 
268
        """Initialize and begins source code."""
 
269
        self._parent = self     # nice trick, isn't it ?
 
270
        self._in = 0
 
271
        self._contextlevel = 0
 
272
        self._pagenumber = 1
 
273
        self._formnumber = 0
 
274
        self._footerpresent = 0
 
275
        self._object = apply(canvas.Canvas, args, kwargs)
 
276
        self._pyfile = cStringIO.StringIO()
 
277
        self._PyWrite(PyHeader)
 
278
        try :
 
279
            del kwargs["filename"]
 
280
        except KeyError :
 
281
            pass
 
282
        self._PyWrite("    # create the PDF document\n    %s = Canvas(file, %s)\n\n    # Begins page 1" % (self._name, apply(buildargs, args[1:], kwargs)))
 
283
 
 
284
    def __nonzero__(self) :
 
285
        """This is needed by platypus' tables."""
 
286
        return 1
 
287
 
 
288
    def __str__(self) :
 
289
        """Returns the equivalent Python source code."""
 
290
        if not self._footerpresent :
 
291
            self._PyWrite(PyFooter)
 
292
            self._footerpresent = 1
 
293
        return self._pyfile.getvalue()
 
294
 
 
295
    def __getattr__(self, name) :
 
296
        """Method or attribute access."""
 
297
        if name == "beginPath" :
 
298
            return self.PathObject(self)
 
299
        elif name == "beginText" :
 
300
            return self.TextObject(self)
 
301
        else :
 
302
            return self.Action(self, name)
 
303
 
 
304
    def _PyWrite(self, pycode) :
 
305
        """Outputs the source code with a trailing newline."""
 
306
        self._pyfile.write("%s\n" % pycode)
 
307
 
 
308
if __name__ == '__main__':
 
309
    print 'For test scripts, look in reportlab/test'