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