1# $Id$ 2# Copyright (C) 2005 Greg Landrum and Rational Discovery LLC 3# 4# This library is free software; you can redistribute it and/or 5# modify it under the terms of the GNU Lesser General Public 6# License as published by the Free Software Foundation; either 7# version 2 of the License, or (at your option) any later version. 8# 9# This library is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12# Lesser General Public License for more details. 13# 14# You should have received a copy of the GNU Lesser General Public 15# License along with this library; if not, write to the Free Software 16# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17"""pidReportLab 18 19Bits have been shamelessly cobbled from piddlePDF.py and/or 20piddlePS.py 21 22Greg Landrum (greg.landrum@gmail.com) 3/28/2005 23""" 24""" 25 Functionality implemented: 26 -drawLine 27 -drawPolygon 28 -drawEllipse 29 -drawArc 30 -drawCurve 31 -drawString (rotated text is, mostly, fine... see below) 32 -drawImage 33 34""" 35 36from rdkit.sping.pid import * 37from rdkit.sping.PDF import pidPDF, pdfmetrics 38from reportlab.lib import colors 39from reportlab.graphics import shapes 40import os, types 41 42from math import * 43 44 45def colorToRL(color): 46 if color != transparent: 47 return colors.Color(color.red, color.green, color.blue) 48 else: 49 return None 50 51 52class RLCanvas(Canvas): 53 54 def __init__(self, size=(300, 300), name='RLCanvas'): 55 self.size = size 56 self._initOutput() 57 Canvas.__init__(self, size, name) 58 self.drawing = shapes.Drawing(size[0], size[1]) 59 60 def _initOutput(self): 61 pass 62 63 # public functions 64 def clear(self): 65 self._initOutput() 66 67 def flush(self): 68 # self.save('svg') 69 pass # to fit new definition of flush() -cwl 70 71 def save(self, file=None, format=None): 72 """Hand this either a file= <filename> or 73 file = <an open file object>. 74 """ 75 if not file: 76 file = self.name 77 from reportlab.graphics import renderPDF 78 renderPDF.drawToFile(self.drawing, file, self.name) 79 80 def fixY(self, y): 81 return self.size[1] - y 82 83 # taken from pidPDF.py 84 def _findPostScriptFontName(self, font): 85 """Attempts to return proper font name.""" 86 #maps a piddle font to a postscript one. 87 #step 1 - no face ends up serif, others are lowercased 88 if not font.face: 89 face = 'serif' 90 else: 91 face = font.face.lower() 92 while face in pidPDF.font_face_map: 93 face = pidPDF.font_face_map[face] 94 #step 2, - resolve bold/italic to get the right PS font name 95 psname = pidPDF.ps_font_map[(face, font.bold, font.italic)] 96 return psname 97 98 #------------- drawing methods -------------- 99 def drawLine(self, x1, y1, x2, y2, color=None, width=None, dash=None, **kwargs): 100 "Draw a straight line between x1,y1 and x2,y2." 101 # set color... 102 if color: 103 if color == transparent: 104 return 105 elif self.defaultLineColor == transparent: 106 return 107 else: 108 color = self.defaultLineColor 109 color = colorToRL(color) 110 if width: 111 w = width 112 else: 113 w = self.defaultLineWidth 114 115 self.drawing.add( 116 shapes.Line(x1, self.fixY(y1), x2, self.fixY(y2), strokeColor=color, strokeWidth=w, 117 strokeDashArray=dash)) 118 return 119 120 def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, closed=0, **kwargs): 121 "Draw a Bezier curve with control points x1,y1 to x4,y4." 122 pts = self.curvePoints(x1, y1, x2, y2, x3, y3, x4, y4) 123 if not closed: 124 pointlist = [(pts[x][0], pts[x][1], pts[x + 1][0], pts[x + 1][1]) 125 for x in range(len(pts) - 1)] 126 self.drawLines(pointlist, **kwargs) 127 else: 128 self.drawPolygon(pointlist, closed=1, **kwargs) 129 130 def drawArc(self, x1, y1, x2, y2, startAng=0, extent=360, edgeColor=None, edgeWidth=None, 131 fillColor=None, dash=None, **kwargs): 132 """Draw a partial ellipse inscribed within the rectangle x1,y1,x2,y2, 133 starting at startAng degrees and covering extent degrees. Angles 134 start with 0 to the right (+x) and increase counter-clockwise. 135 These should have x1<x2 and y1<y2.""" 136 137 center = (x1 + x2) / 2, (y1 + y2) / 2 138 pointlist = self.arcPoints(x1, y1, x2, y2, startAng, extent) 139 140 # Fill... 141 self.drawPolygon(pointlist + [center], edgeColor=transparent, edgeWidth=0, fillColor=fillColor) 142 143 # Outline... 144 pts = pointlist 145 pointlist = [(pts[x][0], pts[x][1], pts[x + 1][0], pts[x + 1][1]) for x in range(len(pts) - 1)] 146 self.drawLines(pointlist, edgeColor, edgeWidth, dash=dash, **kwargs) 147 148 def drawPolygon(self, pointlist, edgeColor=None, edgeWidth=None, fillColor=transparent, closed=0, 149 dash=None, **kwargs): 150 """drawPolygon(pointlist) -- draws a polygon 151 pointlist: a list of (x,y) tuples defining vertices 152 """ 153 if not edgeColor: 154 edgeColor = self.defaultLineColor 155 156 edgeColor = colorToRL(edgeColor) 157 if not fillColor or fillColor == transparent: 158 fillColor = None 159 else: 160 fillColor = colorToRL(fillColor) 161 162 if edgeWidth: 163 w = edgeWidth 164 else: 165 w = self.defaultLineWidth 166 167 points = [] 168 for x, y in pointlist: 169 points.append(x) 170 points.append(self.fixY(y)) 171 self.drawing.add( 172 shapes.Polygon(points, strokeColor=edgeColor, strokeWidth=w, strokeDashArray=dash, 173 fillColor=fillColor)) 174 175 def drawString(self, s, x, y, font=None, color=None, angle=0, **kwargs): 176 # set color... 177 if color: 178 if color == transparent: 179 return 180 elif self.defaultLineColor == transparent: 181 return 182 else: 183 color = self.defaultLineColor 184 color = colorToRL(color) 185 if font is None: 186 font = self.defaultFont 187 188 txt = shapes.String(0, 0, s, fillColor=color) 189 txt.fontName = self._findPostScriptFontName(font) 190 txt.fontSize = font.size 191 192 g = shapes.Group(txt) 193 g.translate(x, self.fixY(y)) 194 g.rotate(angle) 195 self.drawing.add(g) 196 return 197 198 def drawImage(self, image, x1, y1, x2=None, y2=None, **kwargs): 199 """ 200 to the best of my knowledge, the only real way to get an image 201 """ 202 return 203 204 def stringWidth(self, s, font=None): 205 "Return the logical width of the string if it were drawn \ 206 in the current font (defaults to self.font)." 207 208 if not font: 209 font = self.defaultFont 210 fontName = self._findPostScriptFontName(font) 211 return pdfmetrics.stringwidth(s, fontName) * font.size * 0.001 212 213 def fontAscent(self, font=None): 214 if not font: 215 font = self.defaultFont 216 #return -font.size 217 fontName = self._findPostScriptFontName(font) 218 return pdfmetrics.ascent_descent[fontName][0] * 0.001 * font.size 219 220 def fontDescent(self, font=None): 221 if not font: 222 font = self.defaultFont 223 fontName = self._findPostScriptFontName(font) 224 return -pdfmetrics.ascent_descent[fontName][1] * 0.001 * font.size 225 226 227def test(): 228 #... for testing... 229 canvas = RLCanvas(name="test") 230 231 canvas.defaultLineColor = Color(0.7, 0.7, 1.0) # light blue 232 canvas.drawLines(map(lambda i: (i * 10, 0, i * 10, 300), range(30))) 233 canvas.drawLines(map(lambda i: (0, i * 10, 300, i * 10), range(30))) 234 canvas.defaultLineColor = black 235 236 canvas.drawLine(10, 200, 20, 190, color=red) 237 238 canvas.drawEllipse(130, 30, 200, 100, fillColor=yellow, edgeWidth=4) 239 240 canvas.drawArc(130, 30, 200, 100, 45, 50, fillColor=blue, edgeColor=navy, edgeWidth=4) 241 242 canvas.defaultLineWidth = 4 243 canvas.drawRoundRect(30, 30, 100, 100, fillColor=blue, edgeColor=maroon, dash=(3, 3)) 244 canvas.drawCurve(20, 20, 100, 50, 50, 100, 160, 160) 245 246 canvas.drawString("This is a test!", 30, 130, Font(face="times", size=16, bold=1), color=green, 247 angle=-45) 248 249 canvas.drawString("This is a test!", 30, 130, color=red) 250 251 polypoints = [(160, 120), (130, 190), (210, 145), (110, 145), (190, 190)] 252 canvas.drawPolygon(polypoints, fillColor=lime, edgeColor=red, edgeWidth=3, closed=1) 253 254 canvas.drawRect(200, 200, 260, 260, edgeColor=yellow, edgeWidth=5) 255 canvas.drawLine(200, 260, 260, 260, color=green, width=5) 256 canvas.drawLine(260, 200, 260, 260, color=red, width=5) 257 258 canvas.save('test.pdf') 259 260 261def dashtest(): 262 #... for testing... 263 canvas = RLCanvas(name="test.pdf") 264 265 canvas.defaultLineColor = Color(0.7, 0.7, 1.0) # light blue 266 canvas.drawLines(map(lambda i: (i * 10, 0, i * 10, 300), range(30)), dash=(3, 3)) 267 canvas.drawLines(map(lambda i: (0, i * 10, 300, i * 10), range(30)), dash=(3, 3)) 268 canvas.defaultLineColor = black 269 270 canvas.drawLine(10, 200, 20, 190, color=red, dash=(3, 3)) 271 272 canvas.drawEllipse(130, 30, 200, 100, fillColor=yellow, edgeWidth=4, dash=(3, 3)) 273 274 canvas.drawArc(130, 30, 200, 100, 45, 50, fillColor=blue, edgeColor=navy, edgeWidth=4, dash=(3, 275 3)) 276 277 canvas.defaultLineWidth = 4 278 canvas.drawRoundRect(30, 30, 100, 100, fillColor=blue, edgeColor=maroon, dash=(3, 3)) 279 canvas.drawCurve(20, 20, 100, 50, 50, 100, 160, 160, dash=(3, 3)) 280 281 canvas.drawString("This is a test!", 30, 130, Font(face="times", size=16, bold=1), color=green, 282 angle=-45) 283 284 canvas.drawString("This is a test!", 30, 130, color=red, angle=-45) 285 286 polypoints = [(160, 120), (130, 190), (210, 145), (110, 145), (190, 190)] 287 canvas.drawPolygon(polypoints, fillColor=lime, edgeColor=red, edgeWidth=3, closed=1, dash=(3, 3)) 288 289 canvas.drawRect(200, 200, 260, 260, edgeColor=yellow, edgeWidth=5, dash=(3, 3)) 290 canvas.drawLine(200, 260, 260, 260, color=green, width=5, dash=(3, 3)) 291 canvas.drawLine(260, 200, 260, 260, color=red, width=5, dash=(3, 3)) 292 293 canvas.save() 294 295 296def testit(canvas, s, x, y, font=None): 297 canvas.defaultLineColor = black 298 canvas.drawString(s, x, y, font=font) 299 canvas.defaultLineColor = blue 300 w = canvas.stringWidth(s, font=font) 301 canvas.drawLine(x, y, x + w, y) 302 canvas.drawLine(x, y - canvas.fontAscent(font=font), x + w, y - canvas.fontAscent(font=font)) 303 canvas.drawLine(x, y + canvas.fontDescent(font=font), x + w, y + canvas.fontDescent(font=font)) 304 305 306def test2(): 307 308 canvas = RLCanvas(name="Foogar.pdf") 309 testit(canvas, "Foogar", 20, 30) 310 311 testit(canvas, "Foogar", 20, 90, font=Font(size=24)) 312 313 testit(canvas, "Foogar", 20, 150, font=Font(face='courier', size=24)) 314 315 testit(canvas, "Foogar", 20, 240, font=Font(face='courier')) 316 canvas.flush() 317 canvas.save() 318 319 320if __name__ == '__main__': 321 test() 322 #dashtest() 323 #test2() 324