JIRA: BOTTLENECKS-29
[bottlenecks.git] / vstf / vstf / controller / reporters / report / pdf / element.py
1 ##############################################################################
2 # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10
11 __doc__ = """
12 it contains the base element for pdf
13 eImage is used to draw picture on the pdf document
14 eDataTable is used to draw table on the pdf document
15 eGraphicsTable is used to draw plot on the pdf document
16 eParagraph is used to draw text on the pdf document
17 """
18 from reportlab.platypus import Image, Table
19 from reportlab.graphics.shapes import Drawing
20 from reportlab.graphics.charts.lineplots import LinePlot
21 from reportlab.graphics.charts.linecharts import HorizontalLineChart
22 from reportlab.platypus.paragraph import Paragraph
23 from reportlab.graphics.widgets.markers import makeMarker
24 from reportlab.graphics.charts.legends import Legend
25 from reportlab.graphics.charts.textlabels import Label
26 from reportlab.graphics.charts.axes import XValueAxis
27 from reportlab.graphics.shapes import Group
28 from reportlab.graphics.charts.barcharts import VerticalBarChart
29 from vstf.controller.reporters.report.pdf.styles import *
30
31
32 class eImage(Image):
33     """ an image(digital picture)which contains the function of auto zoom picture """
34
35     def __init__(self, filename, width=None, height=None, kind='direct', mask="auto", lazy=1, hAlign='CENTRE',
36                  vAlign='BOTTOM'):
37         Image.__init__(self, filename, None, None, kind, mask, lazy)
38         print height, width
39         print self.drawHeight, self.drawWidth
40         if self.drawWidth * height > self.drawHeight * width:
41             self.drawHeight = width * self.drawHeight / self.drawWidth
42             self.drawWidth = width
43         else:
44             self.drawWidth = height * self.drawWidth / self.drawHeight
45             self.drawHeight = height
46         self.hAlign = hAlign
47         self.vAlign = vAlign
48         print self.drawHeight, self.drawWidth
49
50
51 class eTable(object):
52     """ an abstract table class, which is contains the base functions to create table """
53
54     def __init__(self, data, style=TableStyle(name="default")):
55         self._tablestyle = style
56         self._table = []
57         self._spin = False
58         self._colWidths = None
59         self._data = self.analysisData(data)
60         if self._data:
61             self.create()
62
63     def analysisData(self, data):
64         raise NotImplementedError("abstract eTable")
65
66     def create(self):
67         self._table = Table(self._data, style=self._style, splitByRow=1)
68         self._table.hAlign = self._tablestyle.table_hAlign
69         self._table.vAlign = self._tablestyle.table_vAlign
70         self._table.colWidths = self._tablestyle.table_colWidths
71         if self._spin or self._colWidths:
72             self._table.colWidths = self._colWidths
73         self._table.rowHeights = self._tablestyle.table_rowHeights
74
75     @property
76     def table(self):
77         return self._table
78
79
80 class eCommonTable(eTable):
81     def analysisData(self, data):
82         self._style = [
83             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
84             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
85             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
86             ('BOX', (0, 0), (-1, -1), 1.2, colors.black)
87         ]
88         return data
89
90
91 class eConfigTable(eTable):
92     def analysisData(self, data):
93         self._style = [
94             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
95             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
96             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
97             ('BOX', (0, 0), (-1, -1), 1, colors.black),
98             ('SPAN', (2, 0), (3, 0)),
99             ('SPAN', (2, 1), (3, 1)),
100             ('SPAN', (2, 8), (3, 8)),
101             ('SPAN', (2, 9), (3, 9)),
102             ('SPAN', (2, 10), (3, 10)),
103             ('SPAN', (0, 0), (0, 7)),
104             ('SPAN', (0, 8), (0, 10)),
105             ('SPAN', (0, 11), (0, 19)),
106             ('SPAN', (1, 2), (1, 6)),
107             ('SPAN', (1, 12), (1, 13)),
108             ('SPAN', (1, 14), (1, 16)),
109             ('SPAN', (1, 17), (1, 19)),
110             ('SPAN', (2, 3), (2, 6))
111         ]
112         return data
113
114
115 class eSummaryTable(eTable):
116     def analysisData(self, data):
117         self._style = [
118             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
119             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
120             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
121             ('BOX', (0, 0), (-1, -1), 1, colors.black),
122             ('SPAN', (0, 0), (0, 1)),
123             ('SPAN', (1, 0), (4, 0)),
124             ('SPAN', (5, 0), (-1, 0))
125         ]
126         return data
127
128
129 class eGitInfoTable(eTable):
130     def analysisData(self, data):
131         self._style = [
132             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
133             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
134             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
135             ('BOX', (0, 0), (-1, -1), 1, colors.black),
136             ('SPAN', (0, 0), (0, 2)),
137             ('SPAN', (0, 3), (0, 5)),
138             ('SPAN', (0, 6), (0, 8))
139         ]
140         return data
141
142
143 class eScenarioTable(eTable):
144     def analysisData(self, data):
145         self._style = [
146             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
147             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
148             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
149             ('BOX', (0, 0), (-1, -1), 1, colors.black),
150             ('ALIGN', (2, 1), (-1, -1), 'LEFT'),
151             ('SPAN', (0, 1), (0, 6)),
152             ('SPAN', (0, 7), (0, 12)),
153             ('SPAN', (0, 13), (0, 16)),
154             ('SPAN', (0, 17), (0, 20))
155         ]
156         return data
157
158
159 class eOptionsTable(eTable):
160     def analysisData(self, data):
161         self._style = [
162             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
163             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
164             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
165             ('BOX', (0, 0), (-1, -1), 1, colors.black),
166             ('SPAN', (2, 0), (4, 0)),
167             ('SPAN', (2, 1), (4, 1)),
168             ('SPAN', (0, 0), (0, -1)),
169             ('SPAN', (1, 2), (1, 16)),
170             ('SPAN', (1, 17), (1, 19)),
171             ('SPAN', (1, 20), (1, 22)),
172             ('SPAN', (1, 23), (1, 24)),
173             ('SPAN', (2, 2), (2, 4)),
174             ('SPAN', (2, 5), (2, 12)),
175             ('SPAN', (2, 13), (2, 16)),
176             ('SPAN', (2, 17), (2, 19)),
177             ('SPAN', (2, 20), (2, 22)),
178             ('SPAN', (2, 23), (2, 24))
179         ]
180         return data
181
182
183 class eProfileTable(eTable):
184     def analysisData(self, data):
185         self._style = [
186             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
187             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
188             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
189             ('BOX', (0, 0), (-1, -1), 1, colors.black),
190             ('SPAN', (0, 1), (0, -1)),
191             ('SPAN', (1, 0), (2, 0)),
192         ]
193         return data
194
195
196 class eDataTable(eTable):
197     def analysisData(self, data):
198         result = data
199         self._style = [
200             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
201             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
202             ('LEADING', (0, 0), (-1, -1), 18),
203             ('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
204             ('BOX', (0, 0), (-1, -1), 1, colors.black),
205             ('LINEBEFORE', (1, 0), (1, -1), 0.8, colors.black),
206             # ('LINEBEFORE', (3, 0), (3, -1), 1, colors.black),
207             # ('LINEBEFORE', (5, 0), (5, -1), 1, colors.black),
208             ('LINEBELOW', (0, 0), (-1, 0), 0.8, colors.black),
209             # ('SPAN', (0, 0), (0, 1)),
210             # ('SPAN', (1, 0), (2, 0)),
211             # ('SPAN', (3, 0), (4, 0))
212         ]
213         if self._spin is True:
214             print "start spin"
215             result = map(list, zip(*result))
216             style = []
217             for value in self._style:
218                 value = list(value)
219                 value[1] = (value[1][1], value[1][0])
220                 value[2] = (value[2][1], value[2][0])
221                 if value[0] == 'LINEBELOW':
222                     value[0] = 'LINEAFTER'
223                 elif value[0] == 'LINEBEFORE':
224                     value[0] = 'LINEABOVE'
225                 value = tuple(value)
226                 style.append(value)
227             self._style = style
228         return result
229
230
231 class eGraphicsTable(eTable):
232     def analysisData(self, data):
233         self._style = [
234             ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
235             ('VALIGN', (0, 0), (-1, -1), 'MIDDLE')
236         ]
237         return data
238
239
240 class noScaleXValueAxis(XValueAxis):
241     def __init__(self):
242         XValueAxis.__init__(self)
243
244     def makeTickLabels(self):
245         g = Group()
246         if not self.visibleLabels: return g
247
248         f = self._labelTextFormat  # perhaps someone already set it
249         if f is None:
250             f = self.labelTextFormat or (self._allIntTicks() and '%.0f' or str)
251         elif f is str and self._allIntTicks():
252             f = '%.0f'
253         elif hasattr(f, 'calcPlaces'):
254             f.calcPlaces(self._tickValues)
255         post = self.labelTextPostFormat
256         scl = self.labelTextScale
257         pos = [self._x, self._y]
258         d = self._dataIndex
259         pos[1 - d] = self._labelAxisPos()
260         labels = self.labels
261         if self.skipEndL != 'none':
262             if self.isXAxis:
263                 sk = self._x
264             else:
265                 sk = self._y
266             if self.skipEndL == 'start':
267                 sk = [sk]
268             else:
269                 sk = [sk, sk + self._length]
270                 if self.skipEndL == 'end':
271                     del sk[0]
272         else:
273             sk = []
274
275         nticks = len(self._tickValues)
276         nticks1 = nticks - 1
277         for i, tick in enumerate(self._tickValues):
278             label = i - nticks
279             if label in labels:
280                 label = labels[label]
281             else:
282                 label = labels[i]
283             if f and label.visible:
284                 v = self.scale(i)
285                 if sk:
286                     for skv in sk:
287                         if abs(skv - v) < 1e-6:
288                             v = None
289                             break
290                 if v is not None:
291                     if scl is not None:
292                         t = tick * scl
293                     else:
294                         t = tick
295                     if isinstance(f, str):
296                         txt = f % t
297                     elif isSeq(f):
298                         # it's a list, use as many items as we get
299                         if i < len(f):
300                             txt = f[i]
301                         else:
302                             txt = ''
303                     elif hasattr(f, '__call__'):
304                         if isinstance(f, TickLabeller):
305                             txt = f(self, t)
306                         else:
307                             txt = f(t)
308                     else:
309                         raise ValueError('Invalid labelTextFormat %s' % f)
310                     if post: txt = post % txt
311                     pos[d] = v
312                     label.setOrigin(*pos)
313                     label.setText(txt)
314
315                     # special property to ensure a label doesn't project beyond the bounds of an x-axis
316                     if self.keepTickLabelsInside:
317                         if isinstance(self, XValueAxis):  # not done yet for y axes
318                             a_x = self._x
319                             if not i:  # first one
320                                 x0, y0, x1, y1 = label.getBounds()
321                                 if x0 < a_x:
322                                     label = label.clone(dx=label.dx + a_x - x0)
323                             if i == nticks1:  # final one
324                                 a_x1 = a_x + self._length
325                                 x0, y0, x1, y1 = label.getBounds()
326                                 if x1 > a_x1:
327                                     label = label.clone(dx=label.dx - x1 + a_x1)
328                     g.add(label)
329
330         return g
331
332     def ___calcScaleFactor(self):
333         """Calculate the axis' scale factor.
334         This should be called only *after* the axis' range is set.
335         Returns a number.
336         """
337         self._scaleFactor = self._length / (len(self._tickValues) + 1)
338         return self._scaleFactor
339
340     def scale(self, value):
341         """Converts a numeric value to a plotarea position.
342         The chart first configures the axis, then asks it to
343         """
344         assert self._configured, "Axis cannot scale numbers before it is configured"
345         if value is None: value = 0
346         # this could be made more efficient by moving the definition of org and sf into the configuration
347         org = (self._x, self._y)[self._dataIndex]
348         sf = self._length / (len(self._tickValues) + 1)
349         if self.reverseDirection:
350             sf = -sf
351             org += self._length
352         return org + sf * (value + 1)
353
354
355 class noScaleLinePlot(LinePlot):
356     def __init__(self):
357         LinePlot.__init__(self)
358         self.xValueAxis = noScaleXValueAxis()
359
360     def calcPositions(self):
361         """Works out where they go.
362
363         Sets an attribute _positions which is a list of
364         lists of (x, y) matching the data.
365         """
366         self._seriesCount = len(self.data)
367         self._rowLength = max(map(len, self.data))
368
369         self._positions = []
370         for rowNo in range(len(self.data)):
371             line = []
372             len_row = len(self.data[rowNo])
373             for colNo in range(len_row):
374                 datum = self.data[rowNo][colNo]  # x, y value
375                 x = self.x + self.width / (len_row + 1) * (colNo + 1)
376                 self.xValueAxis.labels[colNo].x = self.x + self.width / (len_row + 1) * (colNo + 1)
377                 y = self.yValueAxis.scale(datum[1])
378                 #               print self.width, " ", x
379                 line.append((x, y))
380             self._positions.append(line)
381
382
383 # def _innerDrawLabel(self, rowNo, colNo, x, y):
384 #        return None
385 class eLinePlot(object):
386     def __init__(self, data, style):
387         self._lpstyle = style
388         self._linename = data[0]
389         self._data = self.analysisData(data[1:])
390         if self._data:
391             self.create()
392
393     @property
394     def draw(self):
395         return self._draw
396
397     def analysisData(self, data):
398         columns = len(data)
399         # print data
400         data = map(list, zip(*data))
401         rows = len(data)
402
403         for i in range(rows):
404             for j in range(columns):
405                 data[i][j] = float(data[i][j])
406         self._linename = self._linename[1:]
407         """
408         delcnt = 0
409         delrows = []
410         for i in range(columns):
411             delrows.append(0.0)
412         del_line = [self._linename[0]]
413         for i in range(rows):
414            for j in range(columns):
415               data[i][j] = float(data[i][j])
416            if data[i] == delrows:
417                delcnt += 1
418                del_line.append(self._linename[i])
419         for i in range(delcnt):
420             data.remove(delrows)
421         for name in del_line:
422             self._linename.remove(name)
423
424         rows = len(data)
425         """
426         # print rows
427         # print data
428         xvalueSteps = data[0]
429         xvalueMin = data[0][0]
430         xvalueMax = data[0][0]
431         yvalueMin = data[1][0]
432         yvalueMax = data[1][0]
433         yvalueSteps = []
434         result = []
435         for j in range(columns):
436             if xvalueMin > data[0][j]:
437                 xvalueMin = data[0][j]
438             if xvalueMax < data[0][j]:
439                 xvalueMax = data[0][j]
440
441         for i in range(rows - 1):
442             lst = []
443             for j in range(columns):
444                 lst.append((data[0][j], data[i + 1][j]))
445                 if yvalueMin > data[i + 1][j]:
446                     yvalueMin = data[i + 1][j]
447                 if yvalueMax < data[i + 1][j]:
448                     yvalueMax = data[i + 1][j]
449                 yvalueSteps.append(int(data[i + 1][j] * 2.5) / 2.5)
450             result.append(tuple(lst))
451         xvalueMin = int(xvalueMin) / 100 * 100
452         xvalueMax = int(xvalueMax) / 100 * 100 + 200
453         yvalueMin = int(yvalueMin) * 1.0 - 1
454         if yvalueMin < 0:
455             yvalueMin = 0.0
456         yvalueMax = int(yvalueMax) + 2.0
457         yvalueSteps.append(yvalueMin)
458         yvalueSteps.append(yvalueMax)
459         yvalueSteps = {}.fromkeys(yvalueSteps).keys()
460
461         self._xvalue = (xvalueMin, xvalueMax, xvalueSteps)
462         self._yvalue = (yvalueMin, yvalueMax, yvalueSteps)
463         print result
464         return result
465
466     def create(self):
467         lpw = self._lpstyle.width
468         lph = self._lpstyle.height
469         draw = Drawing(lpw, lph)
470         line_cnts = len(self._linename)
471         #        lp = noScaleLinePlot()
472         lp = LinePlot()
473         lg_line = (line_cnts + 3) / 4
474         lp.x = self._lpstyle.left
475         lp.y = self._lpstyle.bottom
476
477         lp.height = lph - self._lpstyle.bottom * (lg_line + 1.5)
478         lp.width = lpw - lp.x * 2
479         lp.data = self._data
480         lp.joinedLines = 1
481         lp.strokeWidth = self._lpstyle.strokeWidth
482         line_cnts = len(self._data)
483         sytle_cnts = len(self._lpstyle.linestyle)
484         color_paris = []
485         for i in range(line_cnts):
486             styleIndex = i % sytle_cnts
487             lp.lines[i].strokeColor = self._lpstyle.linestyle[styleIndex][0]
488             lp.lines[i].symbol = makeMarker(self._lpstyle.linestyle[styleIndex][1])
489             lp.lines[i].strokeWidth = self._lpstyle.linestyle[styleIndex][2]
490             color_paris.append((self._lpstyle.linestyle[styleIndex][0], self._linename[i]))
491         #            lp.lineLabels[i].strokeColor = self._lpstyle.linestyle[styleIndex][0]
492
493         lp.lineLabelFormat = self._lpstyle.format[0]
494
495         lp.strokeColor = self._lpstyle.strokeColor
496
497         lp.xValueAxis.valueMin, lp.xValueAxis.valueMax, lp.xValueAxis.valueSteps = self._xvalue
498         #       valueMin, valueMax, xvalueSteps = self._xvalue
499         #       lp.xValueAxis.valueStep = (lp.xValueAxis.valueMax - lp.xValueAxis.valueMin)/len(xvalueSteps)
500         #       lp.xValueAxis.valueSteps = map(lambda x: str(x), xvalueSteps)
501
502         lp.yValueAxis.valueMin, lp.yValueAxis.valueMax, lp.yValueAxis.valueSteps = self._yvalue
503
504
505
506         #       lp.xValueAxis.forceZero = 0
507         #       lp.xValueAxis.avoidBoundFrac = 1
508         #       lp.xValueAxis.tickDown = 3
509         #       lp.xValueAxis.visibleGrid = 1
510         #       lp.xValueAxis.categoryNames = '64 256 512 1400 1500 4096'.split(' ')
511
512         lp.xValueAxis.labelTextFormat = self._lpstyle.format[1]
513         lp.yValueAxis.labelTextFormat = self._lpstyle.format[2]
514
515         delsize = int(lp.xValueAxis.valueMax / 2000)
516         lp.xValueAxis.labels.fontSize = self._lpstyle.labelsfont
517         lp.xValueAxis.labels.angle = 25
518
519         lp.yValueAxis.labels.fontSize = self._lpstyle.labelsfont
520         lp.lineLabels.fontSize = self._lpstyle.labelsfont - delsize
521         draw.add(lp)
522
523         lg = Legend()
524         lg.colorNamePairs = color_paris
525         lg.fontName = 'Helvetica'
526         lg.fontSize = 7
527
528         lg.x = self._lpstyle.left * 3
529         lg.y = self._lpstyle.bottom * (1 + lg_line) + lp.height
530
531         lg.dxTextSpace = 5
532         lg.dy = 5
533         lg.dx = 20
534         lg.deltax = 60
535         lg.deltay = 0
536         lg.columnMaximum = 1
537         lg.alignment = 'right'
538         draw.add(lg)
539         self._draw = draw
540
541
542 class eHorizontalLineChart(object):
543     def __init__(self, data, style):
544         self._lcstyle = style
545         if len(data) < 1:
546             return
547         self._linename = data[0]
548         self._data = self.analysisData(data[1:])
549         if self._data:
550             self.create()
551
552     @property
553     def draw(self):
554         return self._draw
555
556     def analysisData(self, data):
557         columns = len(data)
558         data = map(list, zip(*data))
559         self._catNames = data[0]
560         self._linename = self._linename[1:]
561         data = data[1:]
562         rows = len(data)
563
564         yvalueMin = float(data[0][0])
565         yvalueMax = float(data[0][0])
566         yvalueSteps = []
567         result = []
568
569         for rowNo in range(rows):
570             for columnNo in range(columns):
571                 data[rowNo][columnNo] = float(data[rowNo][columnNo])
572                 if yvalueMin > data[rowNo][columnNo]:
573                     yvalueMin = data[rowNo][columnNo]
574                 if yvalueMax < data[rowNo][columnNo]:
575                     yvalueMax = data[rowNo][columnNo]
576                 yvalueSteps.append(int(data[rowNo][columnNo] * 1.0) / 1.0)
577             result.append(tuple(data[rowNo]))
578
579         yvalueMin = int(yvalueMin) * 1.0 - 1
580         if yvalueMin < 0:
581             yvalueMin = 0.0
582         yvalueMax = int(yvalueMax) + 2.0
583         yvalueSteps.append(yvalueMin)
584         yvalueSteps.append(yvalueMax)
585         yvalueSteps = {}.fromkeys(yvalueSteps).keys()
586
587         self._value = (yvalueMin, yvalueMax, yvalueSteps)
588         print result
589         return result
590
591     def create(self):
592         dw = self._lcstyle.width
593         dh = self._lcstyle.height
594         draw = Drawing(dw, dh)
595
596         lc = HorizontalLineChart()
597         line_cnts = len(self._linename)
598
599         lg_line = (line_cnts + 3) / 4
600         lc.height = dh - self._lcstyle.bottom * (lg_line + 1.5)
601         lc.width = dw - lc.x * 2
602         lc.x = self._lcstyle.left
603         lc.y = self._lcstyle.bottom
604
605         lc.data = self._data
606
607         lc.strokeColor = self._lcstyle.strokeColor
608         lc.strokeWidth = self._lcstyle.strokeWidth
609         lc.useAbsolute = 1
610         lc.groupSpacing = lc.width * 2.0 / len(self._catNames)
611         lc.joinedLines = 1
612         lc.lineLabelFormat = self._lcstyle.format[0]
613
614         lc.valueAxis.valueMin, lc.valueAxis.valueMax, lc.valueAxis.valueSteps = self._value
615         lc.valueAxis.labelTextFormat = self._lcstyle.format[1]
616         lc.valueAxis.labels.fontSize = self._lcstyle.labelsfont
617
618         lc.categoryAxis.categoryNames = self._catNames
619         lc.categoryAxis.labels.boxAnchor = 'ne'
620         lc.categoryAxis.labels.dx = lc.width / 2.0 / len(self._catNames)
621         lc.categoryAxis.labels.dy = -6
622         lc.categoryAxis.labels.angle = 10
623         lc.categoryAxis.labels.fontSize = self._lcstyle.labelsfont
624         #        lc.categoryAxis.visibleGrid = 1
625         #        lc.categoryAxis.tickUp = 100
626         #        lc.categoryAxis.tickDown = 50
627         #        lc.categoryAxis.gridEnd = dh
628         sytle_cnts = len(self._lcstyle.linestyle)
629         color_paris = []
630         for i in range(line_cnts):
631             styleIndex = i % sytle_cnts
632             lc.lines[i].strokeColor = self._lcstyle.linestyle[styleIndex][0]
633             lc.lines[i].symbol = makeMarker(self._lcstyle.linestyle[styleIndex][1])
634             lc.lines[i].strokeWidth = self._lcstyle.linestyle[styleIndex][2]
635             color_paris.append((self._lcstyle.linestyle[styleIndex][0], self._linename[i]))
636
637         lc.lineLabels.fontSize = self._lcstyle.labelsfont - 2
638
639         draw.add(lc)
640
641         lg = Legend()
642         lg.colorNamePairs = color_paris
643         lg.fontName = 'Helvetica'
644         lg.fontSize = 7
645         #        lg.x = dw /2
646         #        lg.y = self._lcstyle.bottom *(1.5 + lg_line)
647
648         lg.x = self._lcstyle.left * 3
649         lg.y = self._lcstyle.bottom * (1 + lg_line) + lc.height
650
651         lg.dxTextSpace = 5
652         lg.dy = 5
653         lg.dx = 20
654         lg.deltax = 60
655         lg.deltay = 0
656         lg.columnMaximum = 1
657         lg.alignment = 'right'
658         draw.add(lg)
659         self._draw = draw
660
661
662 class eBarChartColumn(object):
663     def __init__(self, data, style):
664         self._bcstyle = style
665         if len(data) < 4:
666             return
667         self._data = self.analysisData(data)
668         if self._data:
669             self.create()
670
671     @property
672     def draw(self):
673         return self._draw
674
675     def analysisData(self, data):
676         self._ytitle = data[0]
677         self._name = data[1]
678         self._bar = data[2]
679         bar_data = data[3]
680         result = []
681         for bar in bar_data:
682             bar = map(lambda x: float(x), bar)
683             result.append(tuple(bar))
684         return result
685
686     def create(self):
687         dw = self._bcstyle.width
688         dh = self._bcstyle.height
689         draw = Drawing(dw, dh)
690
691         bc = VerticalBarChart()
692         bar_cnt = len(self._bar)
693         lg_line = (bar_cnt + 3) / 4
694
695         bc.width = dw - self._bcstyle.left - self._bcstyle.right
696         bc.height = dh - self._bcstyle.top - self._bcstyle.bottom
697         if bar_cnt > 1:
698             bc.height -= lg_line * 15
699
700         bc.x = self._bcstyle.left
701         bc.y = self._bcstyle.bottom
702         color_paris = []
703         for i in range(bar_cnt):
704             bc.bars[i].fillColor = self._bcstyle.pillarstyle[self._bar[i]][0]
705             color_paris.append((self._bcstyle.pillarstyle[self._bar[i]][0], self._bar[i]))
706
707         bc.fillColor = self._bcstyle.background
708         bc.barLabels.fontName = 'Helvetica'
709         bc.barLabelFormat = self._bcstyle.pillarstyle[self._bar[0]][1]
710         bc.barLabels.fontSize = self._bcstyle.labelsfont
711         bc.barLabels.dy = self._bcstyle.labelsfont
712         bc.valueAxis.labels.fontName = 'Helvetica'
713         bc.valueAxis.labels.fontSize = self._bcstyle.labelsfont
714         bc.valueAxis.forceZero = 1
715         bc.valueAxis.valueMin = 0
716
717         bc.data = self._data
718         bc.barSpacing = self._bcstyle.barSpacing
719         bc.groupSpacing = self._bcstyle.groupSpacing / bar_cnt
720         bc.valueAxis.avoidBoundFrac = 1
721         bc.valueAxis.gridEnd = dw - self._bcstyle.right
722         bc.valueAxis.tickLeft = self._bcstyle.tick
723         bc.valueAxis.visibleGrid = 1
724         bc.categoryAxis.categoryNames = self._name
725         bc.categoryAxis.tickDown = self._bcstyle.tick
726         bc.categoryAxis.labels.fontName = 'Helvetica'
727         bc.categoryAxis.labels.fontSize = self._bcstyle.labelsfont
728         bc.categoryAxis.labels.dy = -27
729         bc.categoryAxis.labels.angle = -90
730         draw.add(bc)
731         lb = Label()
732         lb.fontName = 'Helvetica'
733         lb.fontSize = 7
734         lb.x = 12
735         lb.y = 80
736         lb.angle = 90
737         lb.textAnchor = 'middle'
738         lb.maxWidth = 100
739         lb.height = 20
740         lb._text = self._ytitle
741         draw.add(lb)
742         if bar_cnt > 1:
743             lg = Legend()
744             lg.colorNamePairs = color_paris
745             lg.fontName = 'Helvetica'
746             lg.fontSize = 7
747
748             lg.x = self._bcstyle.left + bc.width / (bar_cnt + 1)
749             lg.y = dh - self._bcstyle.top - lg_line * 5
750
751             lg.dxTextSpace = 5
752             lg.dy = 5
753             lg.dx = 25
754             lg.deltax = 80
755             lg.deltay = 0
756             lg.columnMaximum = 1
757             lg.alignment = 'right'
758             draw.add(lg)
759
760         self._draw = draw
761
762
763 class eParagraph(object):
764     def __init__(self, data, style):
765         self._pstyle = style
766         self._data = self.analysisData(data)
767         self.create()
768
769     def analysisData(self, data):
770         result = ""
771         for dstr in data:
772             if self._pstyle.name == 'ps_body':
773                 #               dstr = "<i>" + dstr + "</i><br/>"
774                 dstr = dstr + "<br/>"
775             else:
776                 dstr = dstr + "<br/>"
777             result += dstr
778         return result
779
780     def create(self):
781         self._para = Paragraph(self._data, self._pstyle)
782
783     @property
784     def para(self):
785         return self._para