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

« back to all changes in this revision

Viewing changes to bin/reportlab/platypus/tables.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/tables.py
 
4
__version__=''' $Id$ '''
 
5
 
 
6
__doc__="""
 
7
Tables are created by passing the constructor a tuple of column widths, a tuple of row heights and the data in
 
8
row order. Drawing of the table can be controlled by using a TableStyle instance. This allows control of the
 
9
color and weight of the lines (if any), and the font, alignment and padding of the text.
 
10
 
 
11
None values in the sequence of row heights or column widths, mean that the corresponding rows
 
12
or columns should be automatically sized.
 
13
 
 
14
All the cell values should be convertible to strings; embedded newline '\\n' characters
 
15
cause the value to wrap (ie are like a traditional linefeed).
 
16
 
 
17
See the test output from running this module as a script for a discussion of the method for constructing
 
18
tables and table styles.
 
19
"""
 
20
from reportlab.platypus.flowables import Flowable, Preformatted
 
21
from reportlab import rl_config
 
22
from reportlab.lib.styles import PropertySet, ParagraphStyle
 
23
from reportlab.lib import colors
 
24
from reportlab.lib.utils import fp_str
 
25
from reportlab.pdfbase import pdfmetrics
 
26
import operator, string
 
27
from types import TupleType, ListType, StringType
 
28
 
 
29
class CellStyle(PropertySet):
 
30
    defaults = {
 
31
        'fontname':'Times-Roman',
 
32
        'fontsize':10,
 
33
        'leading':12,
 
34
        'leftPadding':6,
 
35
        'rightPadding':6,
 
36
        'topPadding':3,
 
37
        'bottomPadding':3,
 
38
        'firstLineIndent':0,
 
39
        'color':colors.black,
 
40
        'alignment': 'LEFT',
 
41
        'background': (1,1,1),
 
42
        'valign': 'BOTTOM',
 
43
        }
 
44
 
 
45
LINECAPS={'butt':0,'round':1,'projecting':2,'squared':2}
 
46
LINEJOINS={'miter':0,'round':1,'bevel':2}
 
47
 
 
48
# experimental replacement
 
49
class CellStyle1(PropertySet):
 
50
    fontname = "Times-Roman"
 
51
    fontsize = 10
 
52
    leading = 12
 
53
    leftPadding = 6
 
54
    rightPadding = 6
 
55
    topPadding = 3
 
56
    bottomPadding = 3
 
57
    firstLineIndent = 0
 
58
    color = colors.black
 
59
    alignment = 'LEFT'
 
60
    background = (1,1,1)
 
61
    valign = "BOTTOM"
 
62
    def __init__(self, name, parent=None):
 
63
        self.name = name
 
64
        if parent is not None:
 
65
            parent.copy(self)
 
66
    def copy(self, result=None):
 
67
        if result is None:
 
68
            result = CellStyle1()
 
69
        for name in dir(self):
 
70
            setattr(result, name, gettattr(self, name))
 
71
        return result
 
72
CellStyle = CellStyle1
 
73
 
 
74
class TableStyle:
 
75
    def __init__(self, cmds=None, parent=None, **kw):
 
76
        #handle inheritance from parent first.
 
77
        commands = []
 
78
        if parent:
 
79
            # copy the parents list at construction time
 
80
            commands = commands + parent.getCommands()
 
81
            self._opts = parent._opts
 
82
        if cmds:
 
83
            commands = commands + list(cmds)
 
84
        self._cmds = commands
 
85
        self._opts={}
 
86
        self._opts.update(kw)
 
87
 
 
88
    def add(self, *cmd):
 
89
        self._cmds.append(cmd)
 
90
    def __repr__(self):
 
91
        L = map(repr, self._cmds)
 
92
        import string
 
93
        L = string.join(L, "  \n")
 
94
        return "TableStyle(\n%s\n) # end TableStyle" % L
 
95
    def getCommands(self):
 
96
        return self._cmds
 
97
 
 
98
TableStyleType = type(TableStyle())
 
99
_SeqTypes = (TupleType, ListType)
 
100
 
 
101
def _rowLen(x):
 
102
    return type(x) not in _SeqTypes and 1 or len(x)
 
103
 
 
104
def _calc_pc(V,avail):
 
105
    '''check list V for percentage or * values
 
106
    1) absolute values go through unchanged
 
107
    2) percentages are used as weights for unconsumed space
 
108
    3) if no None values were seen '*' weights are
 
109
    set equally with unclaimed space
 
110
    otherwise * weights are assigned as None'''
 
111
    R = []
 
112
    r = R.append
 
113
    I = []
 
114
    i = I.append
 
115
    J = []
 
116
    j = J.append
 
117
    s = avail
 
118
    w = n = 0.
 
119
    for v in V:
 
120
        if type(v) is type(""):
 
121
            v = v.strip()
 
122
            if not v:
 
123
                v = None
 
124
                n += 1
 
125
            elif v.endswith('%'):
 
126
                v = float(v[:-1])
 
127
                w += v
 
128
                i(len(R))
 
129
            elif v=='*':
 
130
                j(len(R))
 
131
            else:
 
132
                v = float(v)
 
133
                s -= v
 
134
        elif v is None:
 
135
            n += 1
 
136
        else:
 
137
            s -= v
 
138
        r(v)
 
139
    s = max(0.,s)
 
140
    f = s/max(100.,w)
 
141
    for i in I:
 
142
        R[i] *= f
 
143
        s -= R[i]
 
144
    s = max(0.,s)
 
145
    m = len(J)
 
146
    if m:
 
147
        v =  n==0 and s/m or None
 
148
        for j in J:
 
149
            R[j] = v
 
150
    return R
 
151
 
 
152
def _hLine(canvLine, scp, ecp, y, hBlocks, FUZZ=rl_config._FUZZ):
 
153
    '''
 
154
    Draw horizontal lines; do not draw through regions specified in hBlocks
 
155
    This also serves for vertical lines with a suitable canvLine
 
156
    '''
 
157
    if hBlocks: hBlocks = hBlocks.get(y,None)
 
158
    if not hBlocks or scp>=hBlocks[-1][1]-FUZZ or ecp<=hBlocks[0][0]+FUZZ:
 
159
        canvLine(scp,y,ecp,y)
 
160
    else:
 
161
        i = 0
 
162
        n = len(hBlocks)
 
163
        while scp<ecp-FUZZ and i<n:
 
164
            x0, x1 = hBlocks[i]
 
165
            if x1<=scp+FUZZ or x0>=ecp-FUZZ:
 
166
                i += 1
 
167
                continue
 
168
            i0 = max(scp,x0)
 
169
            i1 = min(ecp,x1)
 
170
            if i0>scp: canvLine(scp,y,i0,y)
 
171
            scp = i1
 
172
        if scp<ecp-FUZZ: canvLine(scp,y,ecp,y)
 
173
 
 
174
def _multiLine(scp,ecp,y,canvLine,ws,count):
 
175
    offset = 0.5*(count-1)*ws
 
176
    y += offset
 
177
    for idx in xrange(count):
 
178
        canvLine(scp, y, ecp, y)
 
179
        y -= ws
 
180
 
 
181
class Table(Flowable):
 
182
    def __init__(self, data, colWidths=None, rowHeights=None, style=None,
 
183
                repeatRows=0, repeatCols=0, splitByRow=1, emptyTableAction=None):
 
184
        self.hAlign = 'CENTER'
 
185
        self.vAlign = 'MIDDLE'
 
186
        if type(data) not in _SeqTypes:
 
187
            raise ValueError, "%s invalid data type" % self.identity()
 
188
        self._nrows = nrows = len(data)
 
189
        self._cellvalues = []
 
190
        _seqCW = type(colWidths) in _SeqTypes
 
191
        _seqRH = type(rowHeights) in _SeqTypes
 
192
        if nrows: self._ncols = ncols = max(map(_rowLen,data))
 
193
        elif colWidths and _seqCW: ncols = len(colWidths)
 
194
        else: ncols = 0
 
195
        if not emptyTableAction: emptyTableAction = rl_config.emptyTableAction
 
196
        if not (nrows and ncols):
 
197
            if emptyTableAction=='error':
 
198
                raise ValueError, "%s must have at least a row and column" % self.identity()
 
199
            elif emptyTableAction=='indicate':
 
200
                self.__class__ = Preformatted
 
201
                global _emptyTableStyle
 
202
                if '_emptyTableStyle' not in globals().keys():
 
203
                    _emptyTableStyle = ParagraphStyle('_emptyTableStyle')
 
204
                    _emptyTableStyle.textColor = colors.red
 
205
                    _emptyTableStyle.backColor = colors.yellow
 
206
                Preformatted.__init__(self,'%s(%d,%d)' % (self.__class__.__name__,nrows,ncols), _emptyTableStyle)
 
207
            elif emptyTableAction=='ignore':
 
208
                self.__class__ = Spacer
 
209
                Spacer.__init__(self,0,0)
 
210
            else:
 
211
                raise ValueError, '%s bad emptyTableAction: "%s"' % (self.identity(),emptyTableAction)
 
212
            return
 
213
 
 
214
        self._cellvalues = data
 
215
        if not _seqCW: colWidths = ncols*[colWidths]
 
216
        elif len(colWidths) != ncols:
 
217
            raise ValueError, "%s data error - %d columns in data but %d in grid" % (self.identity(),ncols, len(colWidths))
 
218
        if not _seqRH: rowHeights = nrows*[rowHeights]
 
219
        elif len(rowHeights) != nrows:
 
220
            raise ValueError, "%s data error - %d rows in data but %d in grid" % (self.identity(),nrows, len(rowHeights))
 
221
        for i in range(nrows):
 
222
            if len(data[i]) != ncols:
 
223
                raise ValueError, "%s not enough data points in row %d!" % (self.identity(),i)
 
224
        self._rowHeights = self._argH = rowHeights
 
225
        self._colWidths = self._argW = colWidths
 
226
        cellrows = []
 
227
        for i in range(nrows):
 
228
            cellcols = []
 
229
            for j in range(ncols):
 
230
                cellcols.append(CellStyle(`(i,j)`))
 
231
            cellrows.append(cellcols)
 
232
        self._cellStyles = cellrows
 
233
 
 
234
        self._bkgrndcmds = []
 
235
        self._linecmds = []
 
236
        self._spanCmds = []
 
237
        self.repeatRows = repeatRows
 
238
        self.repeatCols = repeatCols
 
239
        self.splitByRow = splitByRow
 
240
 
 
241
        if style:
 
242
            self.setStyle(style)
 
243
    def __repr__(self):
 
244
        "incomplete, but better than nothing"
 
245
        r = getattr(self,'_rowHeights','[unknown]')
 
246
        c = getattr(self,'_colWidths','[unknown]')
 
247
        cv = getattr(self,'_cellvalues','[unknown]')
 
248
        import pprint, string
 
249
        cv = pprint.pformat(cv)
 
250
        cv = string.replace(cv, "\n", "\n  ")
 
251
        return "%s(\n rowHeights=%s,\n colWidths=%s,\n%s\n) # end table" % (self.__class__.__name__,r,c,cv)
 
252
 
 
253
    def identity(self, maxLen=30):
 
254
        '''Identify our selves as well as possible'''
 
255
        vx = None
 
256
        nr = getattr(self,'_nrows','unknown')
 
257
        nc = getattr(self,'_ncols','unknown')
 
258
        cv = getattr(self,'_cellvalues',None)
 
259
        if cv and 'unknown' not in (nr,nc):
 
260
            b = 0
 
261
            for i in xrange(nr):
 
262
                for j in xrange(nc):
 
263
                    v = cv[i][j]
 
264
                    t = type(v)
 
265
                    if t in _SeqTypes or isinstance(v,Flowable):
 
266
                        if not t in _SeqTypes: v = (v,)
 
267
                        r = ''
 
268
                        for vij in v:
 
269
                            r = vij.identity(maxLen)
 
270
                            if r and r[-4:]!='>...':
 
271
                                break
 
272
                        if r and r[-4:]!='>...':
 
273
                            ix, jx, vx, b = i, j, r, 1
 
274
                    else:
 
275
                        v = v is None and '' or str(v)
 
276
                        ix, jx, vx = i, j, v
 
277
                        b = (vx and t is StringType) and 1 or 0
 
278
                        if maxLen: vx = vx[:maxLen]
 
279
                    if b: break
 
280
                if b: break
 
281
        if vx:
 
282
            vx = ' with cell(%d,%d) containing\n%s' % (ix,jx,repr(vx))
 
283
        else:
 
284
            vx = '...'
 
285
 
 
286
        return "<%s at %d %d rows x %s cols>%s" % (self.__class__.__name__, id(self), nr, nc, vx)
 
287
 
 
288
    def _listCellGeom(self, V,w,s,W=None,H=None,aH=72000):
 
289
        aW = w-s.leftPadding-s.rightPadding
 
290
        aH = aH - s.topPadding - s.bottomPadding
 
291
        t = 0
 
292
        w = 0
 
293
        canv = getattr(self,'canv',None)
 
294
        for v in V:
 
295
            vw, vh = v.wrapOn(canv,aW, aH)
 
296
            if W is not None: W.append(vw)
 
297
            if H is not None: H.append(vh)
 
298
            w = max(w,vw)
 
299
            t = t + vh + v.getSpaceBefore()+v.getSpaceAfter()
 
300
        return w, t - V[0].getSpaceBefore()-V[-1].getSpaceAfter()
 
301
 
 
302
    def _calc_width(self,availWidth,W=None):
 
303
        if getattr(self,'_width_calculated_once',None): return
 
304
        #comments added by Andy to Robin's slightly terse variable names
 
305
        if not W: W = _calc_pc(self._argW,availWidth)   #widths array
 
306
        if None in W:  #some column widths are not given
 
307
            canv = getattr(self,'canv',None)
 
308
            saved = None
 
309
            colSpanCells = self._spanCmds and self._colSpanCells or ()
 
310
            if W is self._argW: W = W[:]
 
311
            while None in W:
 
312
                j = W.index(None) #find first unspecified column
 
313
                f = lambda x,j=j: operator.getitem(x,j)
 
314
                V = map(f,self._cellvalues)  #values for this column
 
315
                S = map(f,self._cellStyles)  #styles for this column
 
316
                w = 0
 
317
                i = 0
 
318
 
 
319
                for v, s in map(None, V, S):
 
320
                    #if the current cell is part of a spanned region,
 
321
                    #assume a zero size.
 
322
                    if (j, i) in colSpanCells:
 
323
                        t = 0.0
 
324
                    else:#work out size
 
325
                        t = self._elementWidth(v,s)
 
326
                        if t is None:
 
327
                            raise ValueError, "Flowable %s in cell(%d,%d) can't have auto width\n%s" % (v.identity(30),i,j,self.identity(30))
 
328
                        t = t + s.leftPadding+s.rightPadding
 
329
                    if t>w: w = t   #record a new maximum
 
330
                    i = i + 1
 
331
 
 
332
                W[j] = w
 
333
 
 
334
        self._colWidths = W
 
335
        width = 0
 
336
        self._colpositions = [0]        #index -1 is right side boundary; we skip when processing cells
 
337
        for w in W:
 
338
            width = width + w
 
339
            self._colpositions.append(width)
 
340
 
 
341
        self._width = width
 
342
        self._width_calculated_once = 1
 
343
 
 
344
    def _elementWidth(self,v,s):
 
345
        t = type(v)
 
346
        if t in _SeqTypes:
 
347
            w = 0
 
348
            for e in v:
 
349
                ew = self._elementWidth(self,v)
 
350
                if ew is None: return None
 
351
                w = max(w,ew)
 
352
            return w
 
353
        elif isinstance(v,Flowable) and v._fixedWidth:
 
354
            return v.width
 
355
        else:
 
356
            if t is not StringType: v = v is None and '' or str(v)
 
357
            v = string.split(v, "\n")
 
358
            return max(map(lambda a, b=s.fontname, c=s.fontsize,d=pdfmetrics.stringWidth: d(a,b,c), v))
 
359
 
 
360
    def _calc_height(self, availHeight, availWidth, H=None, W=None):
 
361
 
 
362
        H = self._argH
 
363
        if not W: W = _calc_pc(self._argW,availWidth)   #widths array
 
364
 
 
365
        hmax = lim = len(H)
 
366
        longTable = getattr(self,'_longTableOptimize',None)
 
367
 
 
368
        if None in H:
 
369
            canv = getattr(self,'canv',None)
 
370
            saved = None
 
371
            #get a handy list of any cells which span rows. should be ignored for sizing
 
372
            if self._spanCmds:
 
373
                rowSpanCells = self._rowSpanCells
 
374
                colSpanCells = self._colSpanCells
 
375
                spanRanges = self._spanRanges
 
376
                colpositions = self._colpositions
 
377
            else:
 
378
                rowSpanCells = colSpanCells = ()
 
379
            if canv: saved = canv._fontname, canv._fontsize, canv._leading
 
380
            H = H[:]    #make a copy as we'll change it
 
381
            self._rowHeights = H
 
382
            while None in H:
 
383
                i = H.index(None)
 
384
                if longTable:
 
385
                    hmax = i
 
386
                    height = reduce(operator.add, H[:i], 0)
 
387
                    # we can stop if we have filled up all available room
 
388
                    if height > availHeight: break
 
389
                V = self._cellvalues[i] # values for row i
 
390
                S = self._cellStyles[i] # styles for row i
 
391
                h = 0
 
392
                j = 0
 
393
                for v, s, w in map(None, V, S, W): # value, style, width (lengths must match)
 
394
                    ji = j,i
 
395
                    if ji in rowSpanCells:
 
396
                        t = 0.0  # don't count it, it's either occluded or unreliable
 
397
                    else:
 
398
                        t = type(v)
 
399
                        if t in _SeqTypes or isinstance(v,Flowable):
 
400
                            if not t in _SeqTypes: v = (v,)
 
401
                            if w is None:
 
402
                                raise ValueError, "Flowable %s in cell(%d,%d) can't have auto width in\n%s" % (v[0].identity(30),i,j,self.identity(30))
 
403
                            if canv: canv._fontname, canv._fontsize, canv._leading = s.fontname, s.fontsize, s.leading or 1.2*s.fontsize
 
404
                            if ji in colSpanCells:
 
405
                                t = spanRanges[ji]
 
406
                                w = max(colpositions[t[2]+1]-colpositions[t[0]],w)
 
407
                            dW,t = self._listCellGeom(v,w,s)
 
408
                            if canv: canv._fontname, canv._fontsize, canv._leading = saved
 
409
                            dW = dW + s.leftPadding + s.rightPadding
 
410
                            if not rl_config.allowTableBoundsErrors and dW>w:
 
411
                                raise "LayoutError", "Flowable %s (%sx%s points) too wide for cell(%d,%d) (%sx* points) in\n%s" % (v[0].identity(30),fp_str(dW),fp_str(t),i,j, fp_str(w), self.identity(30))
 
412
                        else:
 
413
                            if t is not StringType:
 
414
                                v = v is None and '' or str(v)
 
415
                            v = string.split(v, "\n")
 
416
                            t = s.leading*len(v)
 
417
                        t = t+s.bottomPadding+s.topPadding
 
418
                    if t>h: h = t   #record a new maximum
 
419
                    j = j + 1
 
420
                H[i] = h
 
421
            if None not in H: hmax = lim
 
422
 
 
423
        height = self._height = reduce(operator.add, H[:hmax], 0)
 
424
        self._rowpositions = [height]    # index 0 is actually topline; we skip when processing cells
 
425
        for h in H[:hmax]:
 
426
            height = height - h
 
427
            self._rowpositions.append(height)
 
428
        assert abs(height)<1e-8, 'Internal height error'
 
429
        self._hmax = hmax
 
430
 
 
431
    def _calc(self, availWidth, availHeight):
 
432
        #if hasattr(self,'_width'): return
 
433
 
 
434
        #in some cases there are unsizable things in
 
435
        #cells.  If so, apply a different algorithm
 
436
        #and assign some withs in a dumb way.
 
437
        #this CHANGES the widths array.
 
438
        if (None in self._colWidths or '*' in self._colWidths) and self._hasVariWidthElements():
 
439
            W = self._calcPreliminaryWidths(availWidth) #widths
 
440
        else:
 
441
            W = None
 
442
 
 
443
        # need to know which cells are part of spanned
 
444
        # ranges, so _calc_height and _calc_width can ignore them
 
445
        # in sizing
 
446
        if self._spanCmds:
 
447
            self._calcSpanRanges()
 
448
            if None in self._argH:
 
449
                self._calc_width(availWidth,W=W)
 
450
 
 
451
        # calculate the full table height
 
452
        self._calc_height(availHeight,availWidth,W=W)
 
453
 
 
454
        # calculate the full table width
 
455
        self._calc_width(availWidth,W=W)
 
456
 
 
457
        if self._spanCmds:
 
458
            #now work out the actual rect for each spanned cell from the underlying grid
 
459
            self._calcSpanRects()
 
460
 
 
461
    def _hasVariWidthElements(self, upToRow=None):
 
462
        """Check for flowables in table cells and warn up front.
 
463
 
 
464
        Allow a couple which we know are fixed size such as
 
465
        images and graphics."""
 
466
        bad = 0
 
467
        if upToRow is None: upToRow = self._nrows
 
468
        for row in range(min(self._nrows, upToRow)):
 
469
            for col in range(self._ncols):
 
470
                value = self._cellvalues[row][col]
 
471
                if not self._canGetWidth(value):
 
472
                    bad = 1
 
473
                    #raise Exception('Unsizable elements found at row %d column %d in table with content:\n %s' % (row, col, value))
 
474
        return bad
 
475
 
 
476
    def _canGetWidth(self, thing):
 
477
        "Can we work out the width quickly?"
 
478
        if type(thing) in (ListType, TupleType):
 
479
            for elem in thing:
 
480
                if not self._canGetWidth(elem):
 
481
                    return 0
 
482
            return 1
 
483
        elif isinstance(thing, Flowable):
 
484
            return thing._fixedWidth  # must loosen this up
 
485
        else: #string, number, None etc.
 
486
            #anything else gets passed to str(...)
 
487
            # so should be sizable
 
488
            return 1
 
489
 
 
490
    def _calcPreliminaryWidths(self, availWidth):
 
491
        """Fallback algorithm for when main one fails.
 
492
 
 
493
        Where exact width info not given but things like
 
494
        paragraphs might be present, do a preliminary scan
 
495
        and assign some sensible values - just divide up
 
496
        all unsizeable columns by the remaining space."""
 
497
 
 
498
        W = _calc_pc(self._argW,availWidth) #widths array
 
499
        verbose = 0
 
500
        totalDefined = 0.0
 
501
        numberUndefined = 0
 
502
        for w in W:
 
503
            if w is None:
 
504
                numberUndefined = numberUndefined + 1
 
505
            else:
 
506
                totalDefined = totalDefined + w
 
507
        if verbose: print 'prelim width calculation.  %d columns, %d undefined width, %0.2f units remain' % (
 
508
            self._ncols, numberUndefined, availWidth - totalDefined)
 
509
 
 
510
        #check columnwise in each None column to see if they are sizable.
 
511
        given = []
 
512
        sizeable = []
 
513
        unsizeable = []
 
514
        for colNo in range(self._ncols):
 
515
            if W[colNo] is None:
 
516
                siz = 1
 
517
                for rowNo in range(self._nrows):
 
518
                    value = self._cellvalues[rowNo][colNo]
 
519
                    if not self._canGetWidth(value):
 
520
                        siz = 0
 
521
                        break
 
522
                if siz:
 
523
                    sizeable.append(colNo)
 
524
                else:
 
525
                    unsizeable.append(colNo)
 
526
            else:
 
527
                given.append(colNo)
 
528
        if len(given) == self._ncols:
 
529
            return
 
530
        if verbose: print 'predefined width:   ',given
 
531
        if verbose: print 'uncomputable width: ',unsizeable
 
532
        if verbose: print 'computable width:    ',sizeable
 
533
 
 
534
        #how much width is left:
 
535
        # on the next iteration we could size the sizeable ones, for now I'll just
 
536
        # divide up the space
 
537
        newColWidths = list(W)
 
538
        guessColWidth = (availWidth - totalDefined) / (len(unsizeable)+len(sizeable))
 
539
        assert guessColWidth >= 0, "table is too wide already, cannot choose a sane width for undefined columns"
 
540
        if verbose: print 'assigning width %0.2f to all undefined columns' % guessColWidth
 
541
        for colNo in sizeable:
 
542
            newColWidths[colNo] = guessColWidth
 
543
        for colNo in unsizeable:
 
544
            newColWidths[colNo] = guessColWidth
 
545
 
 
546
        if verbose: print 'new widths are:', newColWidths
 
547
        self._argW = self._colWidths = newColWidths
 
548
        return newColWidths
 
549
 
 
550
    def _calcSpanRanges(self):
 
551
        """Work out rects for tables which do row and column spanning.
 
552
 
 
553
        This creates some mappings to let the later code determine
 
554
        if a cell is part of a "spanned" range.
 
555
        self._spanRanges shows the 'coords' in integers of each
 
556
        'cell range', or None if it was clobbered:
 
557
          (col, row) -> (col0, row0, col1, row1)
 
558
 
 
559
        Any cell not in the key is not part of a spanned region
 
560
        """
 
561
        self._spanRanges = spanRanges = {}
 
562
        for x in xrange(self._ncols):
 
563
            for y in xrange(self._nrows):
 
564
                spanRanges[x,y] = (x, y, x, y)
 
565
        self._colSpanCells = []
 
566
        self._rowSpanCells = []
 
567
        csa = self._colSpanCells.append
 
568
        rsa = self._rowSpanCells.append
 
569
        for (cmd, start, stop) in self._spanCmds:
 
570
            x0, y0 = start
 
571
            x1, y1 = stop
 
572
 
 
573
            #normalize
 
574
            if x0 < 0: x0 = x0 + self._ncols
 
575
            if x1 < 0: x1 = x1 + self._ncols
 
576
            if y0 < 0: y0 = y0 + self._nrows
 
577
            if y1 < 0: y1 = y1 + self._nrows
 
578
            if x0 > x1: x0, x1 = x1, x0
 
579
            if y0 > y1: y0, y1 = y1, y0
 
580
 
 
581
            if x0!=x1 or y0!=y1:
 
582
                #column span
 
583
                if x0!=x1:
 
584
                    for y in xrange(y0, y1+1):
 
585
                        for x in xrange(x0,x1+1):
 
586
                            csa((x,y))
 
587
                #row span
 
588
                if y0!=y1:
 
589
                    for y in xrange(y0, y1+1):
 
590
                        for x in xrange(x0,x1+1):
 
591
                            rsa((x,y))
 
592
 
 
593
                for y in xrange(y0, y1+1):
 
594
                    for x in xrange(x0,x1+1):
 
595
                        spanRanges[x,y] = None
 
596
                # set the main entry
 
597
                spanRanges[x0,y0] = (x0, y0, x1, y1)
 
598
 
 
599
    def _calcSpanRects(self):
 
600
        """Work out rects for tables which do row and column spanning.
 
601
 
 
602
        Based on self._spanRanges, which is already known,
 
603
        and the widths which were given or previously calculated,
 
604
        self._spanRects shows the real coords for drawing:
 
605
          (col, row) -> (x, y, width, height)
 
606
 
 
607
        for each cell.  Any cell which 'does not exist' as another
 
608
        has spanned over it will get a None entry on the right
 
609
        """
 
610
        if getattr(self,'_spanRects',None): return
 
611
        colpositions = self._colpositions
 
612
        rowpositions = self._rowpositions
 
613
        self._spanRects = spanRects = {}
 
614
        self._vBlocks = vBlocks = {}
 
615
        self._hBlocks = hBlocks = {}
 
616
        for (coord, value) in self._spanRanges.items():
 
617
            if value is None:
 
618
                spanRects[coord] = None
 
619
            else:
 
620
                col,row = coord
 
621
                col0, row0, col1, row1 = value
 
622
                if col1-col0>0:
 
623
                    for _ in xrange(col0+1,col1+1):
 
624
                        vBlocks.setdefault(colpositions[_],[]).append((rowpositions[row1+1],rowpositions[row0]))
 
625
                if row1-row0>0:
 
626
                    for _ in xrange(row0+1,row1+1):
 
627
                        hBlocks.setdefault(rowpositions[_],[]).append((colpositions[col0],colpositions[col1+1]))
 
628
                x = colpositions[col0]
 
629
                y = rowpositions[row1+1]
 
630
                width = colpositions[col1+1] - x
 
631
                height = rowpositions[row0] - y
 
632
                spanRects[coord] = (x, y, width, height)
 
633
 
 
634
        for _ in hBlocks, vBlocks:
 
635
            for value in _.values():
 
636
                value.sort()
 
637
 
 
638
    def setStyle(self, tblstyle):
 
639
        if type(tblstyle) is not TableStyleType:
 
640
            tblstyle = TableStyle(tblstyle)
 
641
        for cmd in tblstyle.getCommands():
 
642
            self._addCommand(cmd)
 
643
        for k,v in tblstyle._opts.items():
 
644
            setattr(self,k,v)
 
645
 
 
646
    def _addCommand(self,cmd):
 
647
        if cmd[0] in ('BACKGROUND','ROWBACKGROUNDS','COLBACKGROUNDS'):
 
648
            self._bkgrndcmds.append(cmd)
 
649
        elif cmd[0] == 'SPAN':
 
650
            self._spanCmds.append(cmd)
 
651
        elif _isLineCommand(cmd):
 
652
            # we expect op, start, stop, weight, colour, cap, dashes, join
 
653
            cmd = tuple(cmd)
 
654
            if len(cmd)<5: raise ValueError('bad line command '+str(cmd))
 
655
 
 
656
            #determine line cap value at position 5. This can be string or numeric.
 
657
            if len(cmd)<6:
 
658
                cmd = cmd+(1,)
 
659
            else:
 
660
                cap = cmd[5]
 
661
                try:
 
662
                    if type(cap) is not type(int):
 
663
                        cap = LINECAPS[cap]
 
664
                    elif cap<0 or cap>2:
 
665
                        raise ValueError
 
666
                    cmd = cmd[:5]+(cap,)+cmd[6:]
 
667
                except:
 
668
                    ValueError('Bad cap value %s in %s'%(cap,str(cmd)))
 
669
            #dashes at index 6 - this is a dash array:
 
670
            if len(cmd)<7: cmd = cmd+(None,)
 
671
 
 
672
            #join mode at index 7 - can be string or numeric, look up as for caps
 
673
            if len(cmd)<8: cmd = cmd+(1,)
 
674
            else:
 
675
                join = cmd[7]
 
676
                try:
 
677
                    if type(join) is not type(int):
 
678
                        join = LINEJOINS[cap]
 
679
                    elif join<0 or join>2:
 
680
                        raise ValueError
 
681
                    cmd = cmd[:7]+(join,)
 
682
                except:
 
683
                    ValueError('Bad join value %s in %s'%(join,str(cmd)))
 
684
 
 
685
            #linecount at index 8.  Default is 1, set to 2 for double line.
 
686
            if len(cmd)<9:
 
687
                lineCount = 1
 
688
                cmd = cmd + (lineCount,)
 
689
            else:
 
690
                lineCount = cmd[8]
 
691
            assert lineCount >= 1
 
692
            #linespacing at index 9. Not applicable unless 2+ lines, defaults to line
 
693
            #width so you get a visible gap between centres
 
694
            if len(cmd)<10: cmd = cmd + (cmd[3],)
 
695
 
 
696
            assert len(cmd) == 10
 
697
 
 
698
            self._linecmds.append(cmd)
 
699
        else:
 
700
            (op, (sc, sr), (ec, er)), values = cmd[:3] , cmd[3:]
 
701
            if sc < 0: sc = sc + self._ncols
 
702
            if ec < 0: ec = ec + self._ncols
 
703
            if sr < 0: sr = sr + self._nrows
 
704
            if er < 0: er = er + self._nrows
 
705
            for i in range(sr, er+1):
 
706
                for j in range(sc, ec+1):
 
707
                    _setCellStyle(self._cellStyles, i, j, op, values)
 
708
 
 
709
    def _drawLines(self):
 
710
        ccap, cdash, cjoin = None, None, None
 
711
        self.canv.saveState()
 
712
        for op, (sc,sr), (ec,er), weight, color, cap, dash, join, count, space in self._linecmds:
 
713
            if type(sr) is type('') and sr.startswith('split'): continue
 
714
            if sc < 0: sc = sc + self._ncols
 
715
            if ec < 0: ec = ec + self._ncols
 
716
            if sr < 0: sr = sr + self._nrows
 
717
            if er < 0: er = er + self._nrows
 
718
            if cap!=None and ccap!=cap:
 
719
                self.canv.setLineCap(cap)
 
720
                ccap = cap
 
721
            getattr(self,_LineOpMap.get(op, '_drawUnknown' ))( (sc, sr), (ec, er), weight, color, count, space)
 
722
        self.canv.restoreState()
 
723
        self._curcolor = None
 
724
 
 
725
    def _drawUnknown(self,  (sc, sr), (ec, er), weight, color, count, space):
 
726
        raise ValueError, "Unknown line command '%s'" % op
 
727
 
 
728
    def _drawGrid(self, (sc, sr), (ec, er), weight, color, count, space):
 
729
        self._drawBox( (sc, sr), (ec, er), weight, color, count, space)
 
730
        self._drawInnerGrid( (sc, sr), (ec, er), weight, color, count, space)
 
731
 
 
732
    def _drawBox(self,  (sc, sr), (ec, er), weight, color, count, space):
 
733
        self._drawHLines((sc, sr), (ec, sr), weight, color, count, space)
 
734
        self._drawHLines((sc, er+1), (ec, er+1), weight, color, count, space)
 
735
        self._drawVLines((sc, sr), (sc, er), weight, color, count, space)
 
736
        self._drawVLines((ec+1, sr), (ec+1, er), weight, color, count, space)
 
737
 
 
738
    def _drawInnerGrid(self, (sc, sr), (ec, er), weight, color, count, space):
 
739
        self._drawHLines((sc, sr+1), (ec, er), weight, color, count, space)
 
740
        self._drawVLines((sc+1, sr), (ec, er), weight, color, count, space)
 
741
 
 
742
    def _prepLine(self, weight, color):
 
743
        if color != self._curcolor:
 
744
            self.canv.setStrokeColor(color)
 
745
            self._curcolor = color
 
746
        if weight != self._curweight:
 
747
            self.canv.setLineWidth(weight)
 
748
            self._curweight = weight
 
749
 
 
750
    def _drawHLines(self, (sc, sr), (ec, er), weight, color, count, space):
 
751
        ecp = self._colpositions[sc:ec+2]
 
752
        rp = self._rowpositions[sr:er+1]
 
753
        if len(ecp)<=1 or len(rp)<1: return
 
754
        self._prepLine(weight, color)
 
755
        scp = ecp[0]
 
756
        ecp = ecp[-1]
 
757
        hBlocks = getattr(self,'_hBlocks',{})
 
758
        canvLine = self.canv.line
 
759
        if count == 1:
 
760
            for y in rp:
 
761
                _hLine(canvLine, scp, ecp, y, hBlocks)
 
762
        else:
 
763
            lf = lambda x0,y0,x1,y1,canvLine=canvLine, ws=weight+space, count=count: _multiLine(x0,x1,y0,canvLine,ws,count)
 
764
            for y in rp:
 
765
                _hLine(lf, scp, ecp, y, hBlocks)
 
766
 
 
767
    def _drawHLinesB(self, (sc, sr), (ec, er), weight, color, count, space):
 
768
        self._drawHLines((sc, sr+1), (ec, er+1), weight, color, count, space)
 
769
 
 
770
    def _drawVLines(self, (sc, sr), (ec, er), weight, color, count, space):
 
771
        erp = self._rowpositions[sr:er+2]
 
772
        cp  = self._colpositions[sc:ec+1]
 
773
        if len(erp)<=1 or len(cp)<1: return
 
774
        self._prepLine(weight, color)
 
775
        srp = erp[0]
 
776
        erp = erp[-1]
 
777
        vBlocks = getattr(self,'_vBlocks',{})
 
778
        canvLine = lambda y0, x0, y1, x1, _line=self.canv.line: _line(x0,y0,x1,y1)
 
779
        if count == 1:
 
780
            for x in cp:
 
781
                _hLine(canvLine, erp, srp, x, vBlocks)
 
782
        else:
 
783
            lf = lambda x0,y0,x1,y1,canvLine=canvLine, ws=weight+space, count=count: _multiLine(x0,x1,y0,canvLine,ws,count)
 
784
            for x in cp:
 
785
                _hLine(lf, erp, srp, x, vBlocks)
 
786
 
 
787
    def _drawVLinesA(self, (sc, sr), (ec, er), weight, color, count, space):
 
788
        self._drawVLines((sc+1, sr), (ec+1, er), weight, color, count, space)
 
789
 
 
790
    def wrap(self, availWidth, availHeight):
 
791
        self._calc(availWidth, availHeight)
 
792
        #nice and easy, since they are predetermined size
 
793
        self.availWidth = availWidth
 
794
        return (self._width, self._height)
 
795
 
 
796
    def onSplit(self,T,byRow=1):
 
797
        '''
 
798
        This method will be called when the Table is split.
 
799
        Special purpose tables can override to do special stuff.
 
800
        '''
 
801
        pass
 
802
 
 
803
    def _cr_0(self,n,cmds):
 
804
        for c in cmds:
 
805
            c = tuple(c)
 
806
            (sc,sr), (ec,er) = c[1:3]
 
807
            if sr>=n: continue
 
808
            if er>=n: er = n-1
 
809
            self._addCommand((c[0],)+((sc, sr), (ec, er))+c[3:])
 
810
 
 
811
    def _cr_1_1(self,n,repeatRows, cmds):
 
812
        for c in cmds:
 
813
            c = tuple(c)
 
814
            (sc,sr), (ec,er) = c[1:3]
 
815
            if sr in ('splitfirst','splitlast'): self._addCommand(c)
 
816
            else:
 
817
                if sr>=0 and sr>=repeatRows and sr<n and er>=0 and er<n: continue
 
818
                if sr>=repeatRows and sr<n: sr=repeatRows
 
819
                elif sr>=repeatRows and sr>=n: sr=sr+repeatRows-n
 
820
                if er>=repeatRows and er<n: er=repeatRows
 
821
                elif er>=repeatRows and er>=n: er=er+repeatRows-n
 
822
                self._addCommand((c[0],)+((sc, sr), (ec, er))+c[3:])
 
823
 
 
824
    def _cr_1_0(self,n,cmds):
 
825
        for c in cmds:
 
826
            c = tuple(c)
 
827
            (sc,sr), (ec,er) = c[1:3]
 
828
            if sr in ('splitfirst','splitlast'): self._addCommand(c)
 
829
            else:
 
830
                if er>=0 and er<n: continue
 
831
                if sr>=0 and sr<n: sr=0
 
832
                if sr>=n: sr = sr-n
 
833
                if er>=n: er = er-n
 
834
                self._addCommand((c[0],)+((sc, sr), (ec, er))+c[3:])
 
835
 
 
836
    def _splitRows(self,availHeight):
 
837
        h = 0
 
838
        n = 0
 
839
        lim = len(self._rowHeights)
 
840
        while n<self._hmax:
 
841
            hn = h + self._rowHeights[n]
 
842
            if hn>availHeight: break
 
843
            h = hn
 
844
            n = n + 1
 
845
 
 
846
        if n<=self.repeatRows:
 
847
            return []
 
848
 
 
849
        if n==lim: return [self]
 
850
 
 
851
        repeatRows = self.repeatRows
 
852
        repeatCols = self.repeatCols
 
853
        splitByRow = self.splitByRow
 
854
        data = self._cellvalues
 
855
 
 
856
        #we're going to split into two superRows
 
857
        #R0 = slelf.__class__( data[:n], self._argW, self._argH[:n],
 
858
        R0 = self.__class__( data[:n], self._colWidths, self._argH[:n],
 
859
                repeatRows=repeatRows, repeatCols=repeatCols,
 
860
                splitByRow=splitByRow)
 
861
 
 
862
        #copy the styles and commands
 
863
        R0._cellStyles = self._cellStyles[:n]
 
864
 
 
865
        A = []
 
866
        # hack up the line commands
 
867
        for op, (sc,sr), (ec,er), weight, color, cap, dash, join, count, space in self._linecmds:
 
868
            if type(sr)is type('') and sr.startswith('split'):
 
869
                A.append((op,(sc,sr), (ec,sr), weight, color, cap, dash, join, count, space))
 
870
                if sr=='splitlast':
 
871
                    sr = er = n-1
 
872
                elif sr=='splitfirst':
 
873
                    sr = n
 
874
                    er = n
 
875
 
 
876
            if sc < 0: sc = sc + self._ncols
 
877
            if ec < 0: ec = ec + self._ncols
 
878
            if sr < 0: sr = sr + self._nrows
 
879
            if er < 0: er = er + self._nrows
 
880
 
 
881
            if op in ('BOX','OUTLINE','GRID'):
 
882
                if sr<n and er>=n:
 
883
                    # we have to split the BOX
 
884
                    A.append(('LINEABOVE',(sc,sr), (ec,sr), weight, color, cap, dash, join, count, space))
 
885
                    A.append(('LINEBEFORE',(sc,sr), (sc,er), weight, color, cap, dash, join, count, space))
 
886
                    A.append(('LINEAFTER',(ec,sr), (ec,er), weight, color, cap, dash, join, count, space))
 
887
                    A.append(('LINEBELOW',(sc,er), (ec,er), weight, color, cap, dash, join, count, space))
 
888
                    if op=='GRID':
 
889
                        A.append(('LINEBELOW',(sc,n-1), (ec,n-1), weight, color, cap, dash, join, count, space))
 
890
                        A.append(('LINEABOVE',(sc,n), (ec,n), weight, color, cap, dash, join, count, space))
 
891
                        A.append(('INNERGRID',(sc,sr), (ec,er), weight, color, cap, dash, join, count, space))
 
892
                else:
 
893
                    A.append((op,(sc,sr), (ec,er), weight, color, cap, dash, join, count, space))
 
894
            elif op in ('INNERGRID','LINEABOVE'):
 
895
                if sr<n and er>=n:
 
896
                    A.append(('LINEBELOW',(sc,n-1), (ec,n-1), weight, color, cap, dash, join, count, space))
 
897
                    A.append(('LINEABOVE',(sc,n), (ec,n), weight, color, cap, dash, join, count, space))
 
898
                A.append((op,(sc,sr), (ec,er), weight, color, cap, dash, join, count, space))
 
899
            elif op == 'LINEBELOW':
 
900
                if sr<n and er>=(n-1):
 
901
                    A.append(('LINEABOVE',(sc,n), (ec,n), weight, color, cap, dash, join, count, space))
 
902
                A.append((op,(sc,sr), (ec,er), weight, color))
 
903
            elif op == 'LINEABOVE':
 
904
                if sr<=n and er>=n:
 
905
                    A.append(('LINEBELOW',(sc,n-1), (ec,n-1), weight, color, cap, dash, join, count, space))
 
906
                A.append((op,(sc,sr), (ec,er), weight, color, cap, dash, join, count, space))
 
907
            else:
 
908
                A.append((op,(sc,sr), (ec,er), weight, color, cap, dash, join, count, space))
 
909
 
 
910
        R0._cr_0(n,A)
 
911
        R0._cr_0(n,self._bkgrndcmds)
 
912
 
 
913
        if repeatRows:
 
914
            #R1 = slelf.__class__(data[:repeatRows]+data[n:],self._argW,
 
915
            R1 = self.__class__(data[:repeatRows]+data[n:],self._colWidths,
 
916
                    self._argH[:repeatRows]+self._argH[n:],
 
917
                    repeatRows=repeatRows, repeatCols=repeatCols,
 
918
                    splitByRow=splitByRow)
 
919
            R1._cellStyles = self._cellStyles[:repeatRows]+self._cellStyles[n:]
 
920
            R1._cr_1_1(n,repeatRows,A)
 
921
            R1._cr_1_1(n,repeatRows,self._bkgrndcmds)
 
922
        else:
 
923
            #R1 = slelf.__class__(data[n:], self._argW, self._argH[n:],
 
924
            R1 = self.__class__(data[n:], self._colWidths, self._argH[n:],
 
925
                    repeatRows=repeatRows, repeatCols=repeatCols,
 
926
                    splitByRow=splitByRow)
 
927
            R1._cellStyles = self._cellStyles[n:]
 
928
            R1._cr_1_0(n,A)
 
929
            R1._cr_1_0(n,self._bkgrndcmds)
 
930
 
 
931
 
 
932
        R0.hAlign = R1.hAlign = self.hAlign
 
933
        R0.vAlign = R1.vAlign = self.vAlign
 
934
        self.onSplit(R0)
 
935
        self.onSplit(R1)
 
936
        return [R0,R1]
 
937
 
 
938
    def split(self, availWidth, availHeight):
 
939
        self._calc(availWidth, availHeight)
 
940
        if self.splitByRow:
 
941
            if not rl_config.allowTableBoundsErrors and self._width>availWidth: return []
 
942
            return self._splitRows(availHeight)
 
943
        else:
 
944
            raise NotImplementedError
 
945
 
 
946
    def draw(self):
 
947
        self._curweight = self._curcolor = self._curcellstyle = None
 
948
        self._drawBkgrnd()
 
949
        if self._spanCmds == []:
 
950
            # old fashioned case, no spanning, steam on and do each cell
 
951
            for row, rowstyle, rowpos, rowheight in map(None, self._cellvalues, self._cellStyles, self._rowpositions[1:], self._rowHeights):
 
952
                for cellval, cellstyle, colpos, colwidth in map(None, row, rowstyle, self._colpositions[:-1], self._colWidths):
 
953
                    self._drawCell(cellval, cellstyle, (colpos, rowpos), (colwidth, rowheight))
 
954
        else:
 
955
            # we have some row or col spans, need a more complex algorithm
 
956
            # to find the rect for each
 
957
            for rowNo in range(self._nrows):
 
958
                for colNo in range(self._ncols):
 
959
                    cellRect = self._spanRects[colNo, rowNo]
 
960
                    if cellRect is not None:
 
961
                        (x, y, width, height) = cellRect
 
962
                        cellval = self._cellvalues[rowNo][colNo]
 
963
                        cellstyle = self._cellStyles[rowNo][colNo]
 
964
                        self._drawCell(cellval, cellstyle, (x, y), (width, height))
 
965
        self._drawLines()
 
966
 
 
967
 
 
968
    def _drawBkgrnd(self):
 
969
        nrows = self._nrows
 
970
        ncols = self._ncols
 
971
        for cmd, (sc, sr), (ec, er), arg in self._bkgrndcmds:
 
972
            if sc < 0: sc = sc + ncols
 
973
            if ec < 0: ec = ec + ncols
 
974
            if sr < 0: sr = sr + nrows
 
975
            if er < 0: er = er + nrows
 
976
            x0 = self._colpositions[sc]
 
977
            y0 = self._rowpositions[sr]
 
978
            x1 = self._colpositions[min(ec+1,ncols)]
 
979
            y1 = self._rowpositions[min(er+1,nrows)]
 
980
            w, h = x1-x0, y1-y0
 
981
            canv = self.canv
 
982
            if callable(arg):
 
983
                apply(arg,(self,canv, x0, y0, w, h))
 
984
            elif cmd == 'ROWBACKGROUNDS':
 
985
                #Need a list of colors to cycle through.  The arguments
 
986
                #might be already colours, or convertible to colors, or
 
987
                # None, or the string 'None'.
 
988
                #It's very common to alternate a pale shade with None.
 
989
                #print 'rowHeights=', self._rowHeights
 
990
                colorCycle = map(colors.toColorOrNone, arg)
 
991
                count = len(colorCycle)
 
992
                rowCount = er - sr + 1
 
993
                for i in range(rowCount):
 
994
                    color = colorCycle[i%count]
 
995
                    h = self._rowHeights[sr + i]
 
996
                    if color:
 
997
                        canv.setFillColor(color)
 
998
                        canv.rect(x0, y0, w, -h, stroke=0,fill=1)
 
999
                    #print '    draw %0.0f, %0.0f, %0.0f, %0.0f' % (x0,y0,w,-h)
 
1000
                    y0 = y0 - h
 
1001
 
 
1002
            elif cmd == 'COLBACKGROUNDS':
 
1003
                #cycle through colours columnwise
 
1004
                colorCycle = map(colors.toColorOrNone, arg)
 
1005
                count = len(colorCycle)
 
1006
                colCount = ec - sc + 1
 
1007
                for i in range(colCount):
 
1008
                    color = colorCycle[i%count]
 
1009
                    w = self._colWidths[sc + i]
 
1010
                    if color:
 
1011
                        canv.setFillColor(color)
 
1012
                        canv.rect(x0, y0, w, h, stroke=0,fill=1)
 
1013
                    x0 = x0 +w
 
1014
            else:   #cmd=='BACKGROUND'
 
1015
                canv.setFillColor(colors.toColor(arg))
 
1016
                canv.rect(x0, y0, w, h, stroke=0,fill=1)
 
1017
 
 
1018
    def _drawCell(self, cellval, cellstyle, (colpos, rowpos), (colwidth, rowheight)):
 
1019
        if self._curcellstyle is not cellstyle:
 
1020
            cur = self._curcellstyle
 
1021
            if cur is None or cellstyle.color != cur.color:
 
1022
                self.canv.setFillColor(cellstyle.color)
 
1023
            if cur is None or cellstyle.leading != cur.leading or cellstyle.fontname != cur.fontname or cellstyle.fontsize != cur.fontsize:
 
1024
                self.canv.setFont(cellstyle.fontname, cellstyle.fontsize, cellstyle.leading)
 
1025
            self._curcellstyle = cellstyle
 
1026
 
 
1027
        just = cellstyle.alignment
 
1028
        valign = cellstyle.valign
 
1029
        n = type(cellval)
 
1030
        if n in _SeqTypes or isinstance(cellval,Flowable):
 
1031
            if not n in _SeqTypes: cellval = (cellval,)
 
1032
            # we assume it's a list of Flowables
 
1033
            W = []
 
1034
            H = []
 
1035
            w, h = self._listCellGeom(cellval,colwidth,cellstyle,W=W, H=H,aH=rowheight)
 
1036
            if valign=='TOP':
 
1037
                y = rowpos + rowheight - cellstyle.topPadding
 
1038
            elif valign=='BOTTOM':
 
1039
                y = rowpos+cellstyle.bottomPadding + h
 
1040
            else:
 
1041
                y = rowpos+(rowheight+cellstyle.bottomPadding-cellstyle.topPadding+h)/2.0
 
1042
            y = y+cellval[0].getSpaceBefore()
 
1043
            for v, w, h in map(None,cellval,W,H):
 
1044
                if just=='LEFT': x = colpos+cellstyle.leftPadding
 
1045
                elif just=='RIGHT': x = colpos+colwidth-cellstyle.rightPadding - w
 
1046
                elif just in ('CENTRE', 'CENTER'):
 
1047
                    x = colpos+(colwidth+cellstyle.leftPadding-cellstyle.rightPadding-w)/2.0
 
1048
                else:
 
1049
                    raise ValueError, 'Invalid justification %s' % just
 
1050
                y = y - v.getSpaceBefore()
 
1051
                y = y - h
 
1052
                v.drawOn(self.canv,x,y)
 
1053
                y = y - v.getSpaceAfter()
 
1054
        else:
 
1055
            if just == 'LEFT':
 
1056
                draw = self.canv.drawString
 
1057
                x = colpos + cellstyle.leftPadding
 
1058
            elif just in ('CENTRE', 'CENTER'):
 
1059
                draw = self.canv.drawCentredString
 
1060
                x = colpos + colwidth * 0.5
 
1061
            elif just == 'RIGHT':
 
1062
                draw = self.canv.drawRightString
 
1063
                x = colpos + colwidth - cellstyle.rightPadding
 
1064
            elif just == 'DECIMAL':
 
1065
                draw = self.canv.drawAlignedString
 
1066
                x = colpos + colwidth - cellstyle.rightPadding
 
1067
            else:
 
1068
                raise ValueError, 'Invalid justification %s' % just
 
1069
            if n is StringType: val = cellval
 
1070
            else: val = str(cellval)
 
1071
            vals = string.split(val, "\n")
 
1072
            n = len(vals)
 
1073
            leading = cellstyle.leading
 
1074
            fontsize = cellstyle.fontsize
 
1075
            if valign=='BOTTOM':
 
1076
                y = rowpos + cellstyle.bottomPadding+n*leading-fontsize
 
1077
            elif valign=='TOP':
 
1078
                y = rowpos + rowheight - cellstyle.topPadding - fontsize
 
1079
            elif valign=='MIDDLE':
 
1080
                #tim roberts pointed out missing fontsize correction 2004-10-04
 
1081
                y = rowpos + (cellstyle.bottomPadding + rowheight-cellstyle.topPadding+n*leading)/2.0 - fontsize
 
1082
            else:
 
1083
                raise ValueError, "Bad valign: '%s'" % str(valign)
 
1084
 
 
1085
            for v in vals:
 
1086
                draw(x, y, v)
 
1087
                y = y-leading
 
1088
 
 
1089
# for text,
 
1090
#   drawCentredString(self, x, y, text) where x is center
 
1091
#   drawRightString(self, x, y, text) where x is right
 
1092
#   drawString(self, x, y, text) where x is left
 
1093
 
 
1094
_LineOpMap = {  'GRID':'_drawGrid',
 
1095
                'BOX':'_drawBox',
 
1096
                'OUTLINE':'_drawBox',
 
1097
                'INNERGRID':'_drawInnerGrid',
 
1098
                'LINEBELOW':'_drawHLinesB',
 
1099
                'LINEABOVE':'_drawHLines',
 
1100
                'LINEBEFORE':'_drawVLines',
 
1101
                'LINEAFTER':'_drawVLinesA', }
 
1102
 
 
1103
class LongTable(Table):
 
1104
    '''Henning von Bargen's changes will be active'''
 
1105
    _longTableOptimize = 1
 
1106
 
 
1107
LINECOMMANDS = _LineOpMap.keys()
 
1108
 
 
1109
def _isLineCommand(cmd):
 
1110
    return cmd[0] in LINECOMMANDS
 
1111
 
 
1112
def _setCellStyle(cellStyles, i, j, op, values):
 
1113
    #new = CellStyle('<%d, %d>' % (i,j), cellStyles[i][j])
 
1114
    #cellStyles[i][j] = new
 
1115
    ## modify in place!!!
 
1116
    new = cellStyles[i][j]
 
1117
    if op == 'FONT':
 
1118
        n = len(values)
 
1119
        new.fontname = values[0]
 
1120
        if n>1:
 
1121
            new.fontsize = values[1]
 
1122
            if n>2:
 
1123
                new.leading = values[2]
 
1124
            else:
 
1125
                new.leading = new.fontsize*1.2
 
1126
    elif op in ('FONTNAME', 'FACE'):
 
1127
        new.fontname = values[0]
 
1128
    elif op in ('SIZE', 'FONTSIZE'):
 
1129
        new.fontsize = values[0]
 
1130
    elif op == 'LEADING':
 
1131
        new.leading = values[0]
 
1132
    elif op == 'TEXTCOLOR':
 
1133
        new.color = colors.toColor(values[0], colors.Color(0,0,0))
 
1134
    elif op in ('ALIGN', 'ALIGNMENT'):
 
1135
        new.alignment = values[0]
 
1136
    elif op == 'VALIGN':
 
1137
        new.valign = values[0]
 
1138
    elif op == 'LEFTPADDING':
 
1139
        new.leftPadding = values[0]
 
1140
    elif op == 'RIGHTPADDING':
 
1141
        new.rightPadding = values[0]
 
1142
    elif op == 'TOPPADDING':
 
1143
        new.topPadding = values[0]
 
1144
    elif op == 'BOTTOMPADDING':
 
1145
        new.bottomPadding = values[0]
 
1146
 
 
1147
GRID_STYLE = TableStyle(
 
1148
    [('GRID', (0,0), (-1,-1), 0.25, colors.black),
 
1149
     ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
 
1150
    )
 
1151
BOX_STYLE = TableStyle(
 
1152
    [('BOX', (0,0), (-1,-1), 0.50, colors.black),
 
1153
     ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
 
1154
    )
 
1155
LABELED_GRID_STYLE = TableStyle(
 
1156
    [('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
 
1157
     ('BOX', (0,0), (-1,-1), 2, colors.black),
 
1158
     ('LINEBELOW', (0,0), (-1,0), 2, colors.black),
 
1159
     ('LINEAFTER', (0,0), (0,-1), 2, colors.black),
 
1160
     ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
 
1161
    )
 
1162
COLORED_GRID_STYLE = TableStyle(
 
1163
    [('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
 
1164
     ('BOX', (0,0), (-1,-1), 2, colors.red),
 
1165
     ('LINEBELOW', (0,0), (-1,0), 2, colors.black),
 
1166
     ('LINEAFTER', (0,0), (0,-1), 2, colors.black),
 
1167
     ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
 
1168
    )
 
1169
LIST_STYLE = TableStyle(
 
1170
    [('LINEABOVE', (0,0), (-1,0), 2, colors.green),
 
1171
     ('LINEABOVE', (0,1), (-1,-1), 0.25, colors.black),
 
1172
     ('LINEBELOW', (0,-1), (-1,-1), 2, colors.green),
 
1173
     ('ALIGN', (1,1), (-1,-1), 'RIGHT')]
 
1174
    )
 
1175
 
 
1176
 
 
1177
# experimental iterator which can apply a sequence
 
1178
# of colors e.g. Blue, None, Blue, None as you move
 
1179
# down.
 
1180
 
 
1181
 
 
1182
if __name__ == '__main__':
 
1183
    from reportlab.test.test_platypus_tables import old_tables_test
 
1184
    old_tables_test()