1# lots to do: 2# __ native drawLines 3# __ add native drawCurve method 4# __ native rectangle/round rect method 5# __ native drawEllipse 6# __ native drawArc 7# __ drawImage support (work on Pyart side of things) 8 9import pyart 10from rdkit.sping.pid import * 11from rdkit.sping.PDF import pdfmetrics 12import Fontmapping # helps by mapping pid font classes to Pyart font names 13 14# note for now I'm just going to do the standard PDF fonts & forget the rest 15 16 17class PyartCanvas(Canvas): 18 "note the default face is 'times' and is set in Fontmapping.py" 19 20 def __init__(self, size=(300, 300), name='PyartCanvas.png'): 21 22 self._pycan = pyart.Canvas(size[0], size[1], dpi=72) 23 self.filename = name 24 25 Canvas.__init__(self, size, name) 26 27 # self.defaultFillColor = transparent 28 29 # now we need to setup our tracking of the defaults vs the current state 30 31 # see if the __setattr__ approach is any better than the _updateXX strategy 32 33 def __setattr__(self, name, value): 34 if name == 'defaultLineColor': 35 if value: 36 # print('setting defaultLineColor to %s, 0x%x' % (value, value.toHexRGB())) 37 if value != transparent: 38 self._pycan.gstate.stroke = value.toHexRGB() 39 self.__dict__[name] = value 40 elif name == 'defaultFillColor': 41 if value: 42 if value != transparent: 43 self._pycan.gstate.fill = value.toHexRGB() 44 self.__dict__[name] = value 45 elif name == 'defaultLineWidth': 46 if value: 47 self._pycan.gstate.stroke_width = value 48 self.__dict__[name] = value 49 elif name == 'defaultFont': 50 if value: 51 self.__dict__[name] = value 52 self._setPyartFont(value) 53 else: # received None so set to default font face & size=12 54 self.__dict__[name] = Font(face='times') 55 self._setPyartFont(self.__dict__[name]) 56 57 else: 58 self.__dict__[name] = value 59 60 ## Private methods ## 61 62 def _protectArtState(self, bool): 63 if bool: 64 self._pycan.gsave() 65 return bool 66 67 def _restoreArtState(self, bool): 68 if bool: 69 self._pycan.grestore() 70 71 def _setPyartFont(self, fontInstance): 72 # accounts for "None" option 73 # does not act on self.defaultFont at all 74 75 fontsize = fontInstance.size 76 self._pycan.gstate.font_size = fontsize 77 # map pid name for font to Pyart name 78 pyartname = Fontmapping.getPyartName(fontInstance) 79 self._pycan.gstate.setfont(pyartname) 80 81 # # # # # 82 83 ### public PID Canvas methods ## 84 85 def clear(self): 86 pass 87 88 def flush(self): 89 pass 90 91 def save(self, file=None, format=None): 92 # fileobj = getFileObject(file) 93 94 if not file: 95 file = self.filename 96 97 if isinstance(file, StringType): 98 self._pycan.save(file) 99 else: 100 raise NotImplementedError 101 102 def _findExternalFontName(self, font): #copied from piddlePDF by cwl- hack away! 103 """Attempts to return proper font name. 104 PDF uses a standard 14 fonts referred to 105 by name. Default to self.defaultFont('Helvetica'). 106 The dictionary allows a layer of indirection to 107 support a standard set of PIDDLE font names.""" 108 109 piddle_font_map = { 110 'Times': 'Times', 111 'times': 'Times', 112 'Courier': 'Courier', 113 'courier': 'Courier', 114 'helvetica': 'Helvetica', 115 'Helvetica': 'Helvetica', 116 'symbol': 'Symbol', 117 'Symbol': 'Symbol', 118 'monospaced': 'Courier', 119 'serif': 'Times', 120 'sansserif': 'Helvetica', 121 'ZapfDingbats': 'ZapfDingbats', 122 'zapfdingbats': 'ZapfDingbats', 123 'arial': 'Helvetica' 124 } 125 126 try: 127 face = piddle_font_map[font.face.lower()] 128 except Exception: 129 return 'Helvetica' 130 131 name = face + '-' 132 if font.bold and face in ['Courier', 'Helvetica', 'Times']: 133 name = name + 'Bold' 134 if font.italic and face in ['Courier', 'Helvetica']: 135 name = name + 'Oblique' 136 elif font.italic and face == 'Times': 137 name = name + 'Italic' 138 139 if name == 'Times-': 140 name = name + 'Roman' 141 # symbol and ZapfDingbats cannot be modified! 142 143 #trim and return 144 if name[-1] == '-': 145 name = name[0:-1] 146 return name 147 148 def stringWidth(self, s, font=None): 149 if not font: 150 font = self.defaultFont 151 fontname = Fontmapping.getPdfName(font) 152 return pdfmetrics.stringwidth(s, fontname) * font.size * 0.001 153 154 def fontAscent(self, font=None): 155 if not font: 156 font = self.defaultFont 157 fontname = Fontmapping.getPdfName(font) 158 return pdfmetrics.ascent_descent[fontname][0] * 0.001 * font.size 159 160 def fontDescent(self, font=None): 161 if not font: 162 font = self.defaultFont 163 fontname = Fontmapping.getPdfName(font) 164 return -pdfmetrics.ascent_descent[fontname][1] * 0.001 * font.size 165 166 def drawLine(self, x1, y1, x2, y2, color=None, width=None): 167 ## standard code ## 168 color = color or self.defaultLineColor 169 width = width or self.defaultLineWidth 170 if color != transparent: 171 changed = self._protectArtState((color != self.defaultLineColor) or 172 (width != self.defaultLineWidth)) 173 if color != self.defaultLineColor: 174 self._pycan.gstate.stroke = color.toHexRGB() 175 # print("color is %s <-> %s" % (color, color.toHexStr())) 176 if width != self.defaultLineWidth: 177 self._pycan.gstate.stroke_width = width 178 ################### 179 180 # actual drawing 181 p = pyart.VectorPath(3) 182 p.moveto_open(x1, y1) 183 p.lineto(x2, y2) 184 self._pycan.stroke(p) 185 186 ## standard code ## 187 if changed: 188 self._pycan.grestore() 189 ################### 190 191 # def drawLines(self, lineList, color=None, width=None): 192 # pass 193 194 def drawString(self, s, x, y, font=None, color=None, angle=0): 195 # start w/ the basics 196 self._pycan.drawString(x, y, s) 197 198 def drawPolygon(self, pointlist, edgeColor=None, edgeWidth=None, fillColor=None, closed=0): 199 200 eColor = edgeColor or self.defaultLineColor 201 fColor = fillColor or self.defaultFillColor 202 eWidth = edgeWidth or self.defaultLineWidth 203 204 changed = self._protectArtState((eColor != self.defaultLineColor) or 205 (eWidth != self.defaultLineWidth) or 206 (fColor != self.defaultFillColor)) 207 208 if eColor != self.defaultLineColor: 209 self._pycan.gstate.stroke = eColor.toHexRGB() 210 211 if fColor != self.defaultFillColor: 212 self._pycan.gstate.fill = fColor.toHexRGB() 213 214 if eWidth != self.defaultLineWidth: 215 self._pycan.gstate.stroke_width = eWidth 216 217 path = pyart.VectorPath(len(pointlist) + 1) 218 if closed: 219 path.moveto_closed(pointlist[0][0], pointlist[0][1]) 220 else: 221 path.moveto_open(pointlist[0][0], pointlist[0][1]) 222 223 for pt in pointlist[1:]: 224 path.lineto(pt[0], pt[1]) 225 226 if closed: 227 path.close() 228 229 if fColor != transparent and closed: 230 self._pycan.fill(path) 231 232 if eColor != transparent: 233 self._pycan.stroke(path) 234 235 self._restoreArtState(changed) 236 237 #def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, 238 # edgeColor=None, edgeWidth=None, fillColor=None, closed=0): 239 # pass 240 241 # def drawRoundRect(self, x1,y1, x2,y2, rx=8, ry=8, 242 # edgeColor=None, edgeWidth=None, fillColor=None): 243 # pass 244 245 # def drawEllipse(self, x1,y1, x2,y2, edgeColor=None, edgeWidth=None, 246 # fillColor=None): 247 # pass 248 249 # def drawArc(self, x1,y1, x2,y2, startAng=0, extent=360, edgeColor=None, 250 # edgeWidth=None, fillColor=None): 251 252 # pass 253 254 # def drawFigure(self, partList, 255 # edgeColor=None, edgeWidth=None, fillColor=None, closed=0): 256 # pass 257 258 # def drawImage(self, image, x1, y1, x2=None,y2=None): 259 # pass 260 261 ## basic tests ## 262 263 264if __name__ == '__main__': 265 import rdkit.sping.tests.pidtest 266 can = PyartCanvas(size=(300, 300), name='basictest.png') 267 268 #can.defaultLineColor = Color(0.7, 0.7, 1.0) 269 #can.drawLine(10,10, 290,290) 270 #can.drawLine(10,10, 50, 10, color=green, width = 4.5) 271 rdkit.sping.tests.pidtest.drawBasics(can) 272 can.save(file='basicTest.png') 273 print('saving basicTest.png') 274 275 can = PyartCanvas(size=(400, 400), name='test-strings.png') 276 rdkit.sping.tests.pidtest.drawStrings(can) 277 can.save() 278