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

« back to all changes in this revision

Viewing changes to bin/reportlab/platypus/tableofcontents.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
#Copyright ReportLab Europe Ltd. 2000-2004
 
2
#see license.txt for license details
 
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/platypus/tableofcontents.py
 
4
"""
 
5
This module defines a single TableOfContents() class that can be used to
 
6
create automatically a table of tontents for Platypus documents like
 
7
this:
 
8
 
 
9
    story = []
 
10
    toc = TableOfContents()
 
11
    story.append(toc)
 
12
    # some heading paragraphs here...
 
13
    doc = MyTemplate(path)
 
14
    doc.multiBuild(story)
 
15
 
 
16
The data needed to create the table is a list of (level, text, pageNum)
 
17
triplets, plus some paragraph styles for each level of the table itself.
 
18
The triplets will usually be created in a document template's method
 
19
like afterFlowable(), making notification calls using the notify()
 
20
method with appropriate data like this:
 
21
 
 
22
    (level, text, pageNum) = ...
 
23
    self.notify('TOCEntry', (level, text, pageNum))
 
24
 
 
25
As the table of contents need at least two passes over the Platypus
 
26
story which is why the moultiBuild0() method must be called.
 
27
 
 
28
The level<NUMBER>ParaStyle variables are the paragraph styles used
 
29
to format the entries in the table of contents. Their indentation
 
30
is calculated like this: each entry starts at a multiple of some
 
31
constant named delta. If one entry spans more than one line, all
 
32
lines after the first are indented by the same constant named
 
33
epsilon.
 
34
"""
 
35
__version__=''' $Id$ '''
 
36
import string
 
37
 
 
38
from reportlab.lib import enums
 
39
from reportlab.lib.units import cm
 
40
from reportlab.lib.styles import ParagraphStyle
 
41
from reportlab.platypus.paragraph import Paragraph
 
42
from reportlab.platypus.doctemplate import IndexingFlowable
 
43
from reportlab.platypus.tables import TableStyle, Table
 
44
 
 
45
 
 
46
# Default paragraph styles for tables of contents.
 
47
# (This could also be generated automatically or even
 
48
# on-demand if it is not known how many levels the
 
49
# TOC will finally need to display...)
 
50
 
 
51
delta = 1*cm
 
52
epsilon = 0.5*cm
 
53
 
 
54
levelZeroParaStyle = \
 
55
    ParagraphStyle(name='LevelZero',
 
56
                   fontName='Times-Roman',
 
57
                   fontSize=10,
 
58
                   leading=11,
 
59
                   firstLineIndent = -epsilon,
 
60
                   leftIndent = 0*delta + epsilon)
 
61
 
 
62
levelOneParaStyle = \
 
63
    ParagraphStyle(name='LevelOne',
 
64
                   parent = levelZeroParaStyle,
 
65
                   leading=11,
 
66
                   firstLineIndent = -epsilon,
 
67
                   leftIndent = 1*delta + epsilon)
 
68
 
 
69
levelTwoParaStyle = \
 
70
    ParagraphStyle(name='LevelTwo',
 
71
                   parent = levelOneParaStyle,
 
72
                   leading=11,
 
73
                   firstLineIndent = -epsilon,
 
74
                   leftIndent = 2*delta + epsilon)
 
75
 
 
76
levelThreeParaStyle = \
 
77
    ParagraphStyle(name='LevelThree',
 
78
                   parent = levelTwoParaStyle,
 
79
                   leading=11,
 
80
                   firstLineIndent = -epsilon,
 
81
                   leftIndent = 3*delta + epsilon)
 
82
 
 
83
levelFourParaStyle = \
 
84
    ParagraphStyle(name='LevelFour',
 
85
                   parent = levelTwoParaStyle,
 
86
                   leading=11,
 
87
                   firstLineIndent = -epsilon,
 
88
                   leftIndent = 4*delta + epsilon)
 
89
 
 
90
defaultTableStyle = \
 
91
    TableStyle([('VALIGN', (0,0), (-1,-1), 'TOP')])
 
92
 
 
93
 
 
94
class TableOfContents(IndexingFlowable):
 
95
    """This creates a formatted table of contents.
 
96
 
 
97
    It presumes a correct block of data is passed in.
 
98
    The data block contains a list of (level, text, pageNumber)
 
99
    triplets.  You can supply a paragraph style for each level
 
100
    (starting at zero).
 
101
    """
 
102
 
 
103
    def __init__(self):
 
104
        self.entries = []
 
105
        self.rightColumnWidth = 72
 
106
        self.levelStyles = [levelZeroParaStyle,
 
107
                            levelOneParaStyle,
 
108
                            levelTwoParaStyle,
 
109
                            levelThreeParaStyle,
 
110
                            levelFourParaStyle]
 
111
        self.tableStyle = defaultTableStyle
 
112
        self._table = None
 
113
        self._entries = []
 
114
        self._lastEntries = []
 
115
 
 
116
 
 
117
    def beforeBuild(self):
 
118
        # keep track of the last run
 
119
        self._lastEntries = self._entries[:]
 
120
        self.clearEntries()
 
121
 
 
122
 
 
123
    def isIndexing(self):
 
124
        return 1
 
125
 
 
126
 
 
127
    def isSatisfied(self):
 
128
        return (self._entries == self._lastEntries)
 
129
 
 
130
    def notify(self, kind, stuff):
 
131
        """The notification hook called to register all kinds of events.
 
132
 
 
133
        Here we are interested in 'TOCEntry' events only.
 
134
        """
 
135
        if kind == 'TOCEntry':
 
136
            (level, text, pageNum) = stuff
 
137
            self.addEntry(level, text, pageNum)
 
138
 
 
139
 
 
140
    def clearEntries(self):
 
141
        self._entries = []
 
142
 
 
143
 
 
144
    def addEntry(self, level, text, pageNum):
 
145
        """Adds one entry to the table of contents.
 
146
 
 
147
        This allows incremental buildup by a doctemplate.
 
148
        Requires that enough styles are defined."""
 
149
 
 
150
        assert type(level) == type(1), "Level must be an integer"
 
151
        assert level < len(self.levelStyles), \
 
152
               "Table of contents must have a style defined " \
 
153
               "for paragraph level %d before you add an entry" % level
 
154
 
 
155
        self._entries.append((level, text, pageNum))
 
156
 
 
157
 
 
158
    def addEntries(self, listOfEntries):
 
159
        """Bulk creation of entries in the table of contents.
 
160
 
 
161
        If you knew the titles but not the page numbers, you could
 
162
        supply them to get sensible output on the first run."""
 
163
 
 
164
        for (level, text, pageNum) in listOfEntries:
 
165
            self.addEntry(level, text, pageNum)
 
166
 
 
167
 
 
168
    def wrap(self, availWidth, availHeight):
 
169
        "All table properties should be known by now."
 
170
 
 
171
        widths = (availWidth - self.rightColumnWidth,
 
172
                  self.rightColumnWidth)
 
173
 
 
174
        # makes an internal table which does all the work.
 
175
        # we draw the LAST RUN's entries!  If there are
 
176
        # none, we make some dummy data to keep the table
 
177
        # from complaining
 
178
        if len(self._lastEntries) == 0:
 
179
            _tempEntries = [(0,'Placeholder for table of contents',0)]
 
180
        else:
 
181
            _tempEntries = self._lastEntries
 
182
 
 
183
        tableData = []
 
184
        for (level, text, pageNum) in _tempEntries:
 
185
            leftColStyle = self.levelStyles[level]
 
186
            #right col style is right aligned
 
187
            rightColStyle = ParagraphStyle(name='leftColLevel%d' % level,
 
188
                                           parent=leftColStyle,
 
189
                                           leftIndent=0,
 
190
                                           alignment=enums.TA_RIGHT)
 
191
            leftPara = Paragraph(text, leftColStyle)
 
192
            rightPara = Paragraph(str(pageNum), rightColStyle)
 
193
            tableData.append([leftPara, rightPara])
 
194
 
 
195
        self._table = Table(tableData, colWidths=widths,
 
196
                            style=self.tableStyle)
 
197
 
 
198
        self.width, self.height = self._table.wrapOn(self.canv,availWidth, availHeight)
 
199
        return (self.width, self.height)
 
200
 
 
201
 
 
202
    def split(self, availWidth, availHeight):
 
203
        """At this stage we do not care about splitting the entries,
 
204
        we will just return a list of platypus tables.  Presumably the
 
205
        calling app has a pointer to the original TableOfContents object;
 
206
        Platypus just sees tables.
 
207
        """
 
208
        return self._table.splitOn(self.canv,availWidth, availHeight)
 
209
 
 
210
 
 
211
    def drawOn(self, canvas, x, y, _sW=0):
 
212
        """Don't do this at home!  The standard calls for implementing
 
213
        draw(); we are hooking this in order to delegate ALL the drawing
 
214
        work to the embedded table object.
 
215
        """
 
216
        self._table.drawOn(canvas, x, y, _sW)
 
217
 
 
218
 
 
219
class SimpleIndex(IndexingFlowable):
 
220
    """This creates a very simple index.
 
221
 
 
222
    Entries have a string key, and appear with a page number on
 
223
    the right.  Prototype for more sophisticated multi-level index."""
 
224
    def __init__(self):
 
225
        #keep stuff in a dictionary while building
 
226
        self._entries = {}
 
227
        self._lastEntries = {}
 
228
        self._table = None
 
229
        self.textStyle = ParagraphStyle(name='index',
 
230
                                        fontName='Times-Roman',
 
231
                                        fontSize=12)
 
232
    def isIndexing(self):
 
233
        return 1
 
234
 
 
235
    def isSatisfied(self):
 
236
        return (self._entries == self._lastEntries)
 
237
 
 
238
    def beforeBuild(self):
 
239
        # keep track of the last run
 
240
        self._lastEntries = self._entries.copy()
 
241
        self.clearEntries()
 
242
 
 
243
    def clearEntries(self):
 
244
        self._entries = {}
 
245
 
 
246
    def notify(self, kind, stuff):
 
247
        """The notification hook called to register all kinds of events.
 
248
 
 
249
        Here we are interested in 'IndexEntry' events only.
 
250
        """
 
251
        if kind == 'IndexEntry':
 
252
            (text, pageNum) = stuff
 
253
            self.addEntry(text, pageNum)
 
254
 
 
255
    def addEntry(self, text, pageNum):
 
256
        """Allows incremental buildup"""
 
257
        if self._entries.has_key(text):
 
258
            self._entries[text].append(str(pageNum))
 
259
        else:
 
260
            self._entries[text] = [pageNum]
 
261
 
 
262
    def split(self, availWidth, availHeight):
 
263
        """At this stage we do not care about splitting the entries,
 
264
        we will just return a list of platypus tables.  Presumably the
 
265
        calling app has a pointer to the original TableOfContents object;
 
266
        Platypus just sees tables.
 
267
        """
 
268
        return self._table.splitOn(self.canv,availWidth, availHeight)
 
269
 
 
270
    def wrap(self, availWidth, availHeight):
 
271
        "All table properties should be known by now."
 
272
        # makes an internal table which does all the work.
 
273
        # we draw the LAST RUN's entries!  If there are
 
274
        # none, we make some dummy data to keep the table
 
275
        # from complaining
 
276
        if len(self._lastEntries) == 0:
 
277
            _tempEntries = [('Placeholder for index',[0,1,2])]
 
278
        else:
 
279
            _tempEntries = self._lastEntries.items()
 
280
            _tempEntries.sort()
 
281
 
 
282
        tableData = []
 
283
        for (text, pageNumbers) in _tempEntries:
 
284
            #right col style is right aligned
 
285
            allText = text + ': ' + string.join(map(str, pageNumbers), ', ')
 
286
            para = Paragraph(allText, self.textStyle)
 
287
            tableData.append([para])
 
288
 
 
289
        self._table = Table(tableData, colWidths=[availWidth])
 
290
 
 
291
        self.width, self.height = self._table.wrapOn(self.canv,availWidth, availHeight)
 
292
        return (self.width, self.height)
 
293
 
 
294
    def drawOn(self, canvas, x, y, _sW=0):
 
295
        """Don't do this at home!  The standard calls for implementing
 
296
        draw(); we are hooking this in order to delegate ALL the drawing
 
297
        work to the embedded table object.
 
298
        """
 
299
        self._table.drawOn(canvas, x, y, _sW)
 
300
 
 
301
class ReferenceText(IndexingFlowable):
 
302
    """Fakery to illustrate how a reference would work if we could
 
303
    put it in a paragraph."""
 
304
    def __init__(self, textPattern, targetKey):
 
305
        self.textPattern = textPattern
 
306
        self.target = targetKey
 
307
        self.paraStyle = ParagraphStyle('tmp')
 
308
        self._lastPageNum = None
 
309
        self._pageNum = -999
 
310
        self._para = None
 
311
 
 
312
    def beforeBuild(self):
 
313
        self._lastPageNum = self._pageNum
 
314
 
 
315
    def notify(self, kind, stuff):
 
316
        if kind == 'Target':
 
317
            (key, pageNum) = stuff
 
318
            if key == self.target:
 
319
                self._pageNum = pageNum
 
320
 
 
321
    def wrap(self, availWidth, availHeight):
 
322
        text = self.textPattern % self._lastPageNum
 
323
        self._para = Paragraph(text, self.paraStyle)
 
324
        return self._para.wrap(availWidth, availHeight)
 
325
 
 
326
    def drawOn(self, canvas, x, y, _sW=0):
 
327
        self._para.drawOn(canvas, x, y, _sW)
 
328
 
 
329