5656 latex_to_standard , tex2uni , latex_to_cmex , stix_virtual_fonts
5757from matplotlib import get_data_path , rcParams
5858
59-
60-
6159import matplotlib .colors as mcolors
6260import matplotlib ._png as _png
6361####################
@@ -118,7 +116,9 @@ class MathtextBackend(object):
118116 - :meth:`get_hinting_type`
119117 """
120118 def __init__ (self ):
121- self .fonts_object = None
119+ self .width = 0
120+ self .height = 0
121+ self .depth = 0
122122
123123 def set_canvas_size (self , w , h , d ):
124124 'Dimension the drawing canvas'
@@ -153,111 +153,86 @@ def get_hinting_type(self):
153153 """
154154 return LOAD_NO_HINTING
155155
156- class MathtextBackendBbox (MathtextBackend ):
156+ class MathtextBackendAgg (MathtextBackend ):
157157 """
158- A backend whose only purpose is to get a precise bounding box.
159- Only required for the Agg backend.
158+ Render glyphs and rectangles to an FTImage buffer, which is later
159+ transferred to the Agg image by the Agg backend.
160160 """
161-
162- def __init__ (self , real_backend ):
163- MathtextBackend .__init__ (self )
161+ def __init__ (self ):
162+ self .ox = 0
163+ self .oy = 0
164+ self .image = None
165+ self .mode = 'bbox'
164166 self .bbox = [0 , 0 , 0 , 0 ]
165- self . real_backend = real_backend
167+ MathtextBackend . __init__ ( self )
166168
167169 def _update_bbox (self , x1 , y1 , x2 , y2 ):
168170 self .bbox = [min (self .bbox [0 ], x1 ),
169171 min (self .bbox [1 ], y1 ),
170172 max (self .bbox [2 ], x2 ),
171173 max (self .bbox [3 ], y2 )]
172174
175+ def set_canvas_size (self , w , h , d ):
176+ MathtextBackend .set_canvas_size (self , w , h , d )
177+ if self .mode != 'bbox' :
178+ self .image = FT2Image (ceil (w ), ceil (h + d ))
179+
173180 def render_glyph (self , ox , oy , info ):
174- self ._update_bbox (ox + info .metrics .xmin ,
175- oy - info .metrics .ymax ,
176- ox + info .metrics .xmax ,
177- oy - info .metrics .ymin )
181+ if self .mode == 'bbox' :
182+ self ._update_bbox (ox + info .metrics .xmin ,
183+ oy - info .metrics .ymax ,
184+ ox + info .metrics .xmax ,
185+ oy - info .metrics .ymin )
186+ else :
187+ info .font .draw_glyph_to_bitmap (
188+ self .image , ox , oy - info .metrics .iceberg , info .glyph )
178189
179190 def render_rect_filled (self , x1 , y1 , x2 , y2 ):
180- self ._update_bbox (x1 , y1 , x2 , y2 )
191+ if self .mode == 'bbox' :
192+ self ._update_bbox (x1 , y1 , x2 , y2 )
193+ else :
194+ height = max (int (y2 - y1 ) - 1 , 0 )
195+ if height == 0 :
196+ center = (y2 + y1 ) / 2.0
197+ y = int (center - (height + 1 ) / 2.0 )
198+ else :
199+ y = int (y1 )
200+ self .image .draw_rect_filled (int (x1 ), y , ceil (x2 ), y + height )
181201
182- def get_results (self , box ):
202+ def get_results (self , box , used_characters ):
203+ self .mode = 'bbox'
183204 orig_height = box .height
184205 orig_depth = box .depth
185206 ship (0 , 0 , box )
186207 bbox = self .bbox
187208 bbox = [bbox [0 ] - 1 , bbox [1 ] - 1 , bbox [2 ] + 1 , bbox [3 ] + 1 ]
188- self ._switch_to_real_backend ()
189- self .fonts_object . set_canvas_size (
209+ self .mode = 'render'
210+ self .set_canvas_size (
190211 bbox [2 ] - bbox [0 ],
191212 (bbox [3 ] - bbox [1 ]) - orig_depth ,
192213 (bbox [3 ] - bbox [1 ]) - orig_height )
193214 ship (- bbox [0 ], - bbox [1 ], box )
194- return self .fonts_object .get_results (box )
195-
196- def get_hinting_type (self ):
197- return self .real_backend .get_hinting_type ()
198-
199- def _switch_to_real_backend (self ):
200- self .fonts_object .mathtext_backend = self .real_backend
201- self .real_backend .fonts_object = self .fonts_object
202- self .real_backend .ox = self .bbox [0 ]
203- self .real_backend .oy = self .bbox [1 ]
204-
205- class MathtextBackendAggRender (MathtextBackend ):
206- """
207- Render glyphs and rectangles to an FTImage buffer, which is later
208- transferred to the Agg image by the Agg backend.
209- """
210- def __init__ (self ):
211- self .ox = 0
212- self .oy = 0
215+ result = (self .ox ,
216+ self .oy ,
217+ self .width ,
218+ self .height + self .depth ,
219+ self .depth ,
220+ self .image ,
221+ used_characters )
213222 self .image = None
214- MathtextBackend .__init__ (self )
215-
216- def set_canvas_size (self , w , h , d ):
217- MathtextBackend .set_canvas_size (self , w , h , d )
218- self .image = FT2Image (ceil (w ), ceil (h + d ))
219-
220- def render_glyph (self , ox , oy , info ):
221- info .font .draw_glyph_to_bitmap (
222- self .image , ox , oy - info .metrics .iceberg , info .glyph )
223-
224- def render_rect_filled (self , x1 , y1 , x2 , y2 ):
225- height = max (int (y2 - y1 ) - 1 , 0 )
226- if height == 0 :
227- center = (y2 + y1 ) / 2.0
228- y = int (center - (height + 1 ) / 2.0 )
229- else :
230- y = int (y1 )
231- self .image .draw_rect_filled (int (x1 ), y , ceil (x2 ), y + height )
232-
233- def get_results (self , box ):
234- return (self .ox ,
235- self .oy ,
236- self .width ,
237- self .height + self .depth ,
238- self .depth ,
239- self .image ,
240- self .fonts_object .get_used_characters ())
223+ return result
241224
242225 def get_hinting_type (self ):
243226 if rcParams ['text.hinting' ]:
244227 return LOAD_FORCE_AUTOHINT
245228 else :
246229 return LOAD_NO_HINTING
247230
248- def MathtextBackendAgg ():
249- return MathtextBackendBbox (MathtextBackendAggRender ())
250-
251- class MathtextBackendBitmapRender (MathtextBackendAggRender ):
252- def get_results (self , box ):
253- return self .image , self .depth
254-
255- def MathtextBackendBitmap ():
256- """
257- A backend to generate standalone mathtext images. No additional
258- matplotlib backend is required.
259- """
260- return MathtextBackendBbox (MathtextBackendBitmapRender ())
231+ class MathtextBackendBitmap (MathtextBackendAgg ):
232+ def get_results (self , box , used_characters ):
233+ ox , oy , width , height , depth , image , characters = \
234+ MathtextBackendAgg (self , box , used_characters )
235+ return image , depth
261236
262237class MathtextBackendPs (MathtextBackend ):
263238 """
@@ -291,14 +266,13 @@ def render_rect_filled(self, x1, y1, x2, y2):
291266 ps = "%f %f %f %f rectfill\n " % (x1 , self .height - y2 , x2 - x1 , y2 - y1 )
292267 self .pswriter .write (ps )
293268
294- def get_results (self , box ):
269+ def get_results (self , box , used_characters ):
295270 ship (0 , - self .depth , box )
296- #print self.depth
297271 return (self .width ,
298272 self .height + self .depth ,
299273 self .depth ,
300274 self .pswriter ,
301- self . fonts_object . get_used_characters () )
275+ used_characters )
302276
303277class MathtextBackendPdf (MathtextBackend ):
304278 """
@@ -319,14 +293,14 @@ def render_glyph(self, ox, oy, info):
319293 def render_rect_filled (self , x1 , y1 , x2 , y2 ):
320294 self .rects .append ((x1 , self .height - y2 , x2 - x1 , y2 - y1 ))
321295
322- def get_results (self , box ):
296+ def get_results (self , box , used_characters ):
323297 ship (0 , - self .depth , box )
324298 return (self .width ,
325299 self .height + self .depth ,
326300 self .depth ,
327301 self .glyphs ,
328302 self .rects ,
329- self . fonts_object . get_used_characters () )
303+ used_characters )
330304
331305class MathtextBackendSvg (MathtextBackend ):
332306 """
@@ -347,15 +321,15 @@ def render_rect_filled(self, x1, y1, x2, y2):
347321 self .svg_rects .append (
348322 (x1 , self .height - y1 + 1 , x2 - x1 , y2 - y1 ))
349323
350- def get_results (self , box ):
324+ def get_results (self , box , used_characters ):
351325 ship (0 , - self .depth , box )
352326 svg_elements = Bunch (svg_glyphs = self .svg_glyphs ,
353327 svg_rects = self .svg_rects )
354328 return (self .width ,
355329 self .height + self .depth ,
356330 self .depth ,
357331 svg_elements ,
358- self . fonts_object . get_used_characters () )
332+ used_characters )
359333
360334class MathtextBackendPath (MathtextBackend ):
361335 """
@@ -377,7 +351,7 @@ def render_rect_filled(self, x1, y1, x2, y2):
377351 self .rects .append (
378352 (x1 , self .height - y2 , x2 - x1 , y2 - y1 ))
379353
380- def get_results (self , box ):
354+ def get_results (self , box , used_characters ):
381355 ship (0 , - self .depth , box )
382356 return (self .width ,
383357 self .height + self .depth ,
@@ -405,7 +379,7 @@ def render_rect_filled(self, x1, y1, x2, y2):
405379 self .rects .append (
406380 (x1 , y1 - self .height , x2 - x1 , y2 - y1 ))
407381
408- def get_results (self , box ):
382+ def get_results (self , box , used_characters ):
409383 ship (0 , - self .depth , box )
410384 return (self .width ,
411385 self .height + self .depth ,
@@ -434,8 +408,6 @@ def __init__(self, default_font_prop, mathtext_backend):
434408 """
435409 self .default_font_prop = default_font_prop
436410 self .mathtext_backend = mathtext_backend
437- # Make these classes doubly-linked
438- self .mathtext_backend .fonts_object = self
439411 self .used_characters = {}
440412
441413 def destroy (self ):
@@ -558,7 +530,9 @@ def get_results(self, box):
558530 Get the data needed by the backend to render the math
559531 expression. The return value is backend-specific.
560532 """
561- return self .mathtext_backend .get_results (box )
533+ result = self .mathtext_backend .get_results (box , self .get_used_characters ())
534+ self .destroy ()
535+ return result
562536
563537 def get_sized_alternatives_for_symbol (self , fontname , sym ):
564538 """
@@ -2337,16 +2311,6 @@ def __init__(self):
23372311 )
23382312 ) + StringEnd ()
23392313
2340- self .clear ()
2341-
2342- def clear (self ):
2343- """
2344- Clear any state before parsing.
2345- """
2346- self ._expr = None
2347- self ._state_stack = None
2348- self ._em_width_cache = {}
2349-
23502314 def parse (self , s , fonts_object , fontsize , dpi ):
23512315 """
23522316 Parse expression *s* using the given *fonts_object* for
@@ -2355,15 +2319,19 @@ def parse(self, s, fonts_object, fontsize, dpi):
23552319 Returns the parse tree of :class:`Node` instances.
23562320 """
23572321 self ._state_stack = [self .State (fonts_object , 'default' , 'rm' , fontsize , dpi )]
2322+ self ._em_width_cache = {}
23582323 try :
2359- self ._expression .parseString (s )
2324+ result = self ._expression .parseString (s )
23602325 except ParseException as err :
23612326 raise ValueError ("\n " .join ([
23622327 "" ,
23632328 err .line ,
23642329 " " * (err .column - 1 ) + "^" ,
23652330 str (err )]))
2366- return self ._expr
2331+ self ._state_stack = None
2332+ self ._em_width_cache = {}
2333+ self ._expression .resetCache ()
2334+ return result [0 ]
23672335
23682336 # The state of the parser is maintained in a stack. Upon
23692337 # entering and leaving a group { } or math/non-math, the stack
@@ -2420,8 +2388,7 @@ def push_state(self):
24202388
24212389 def finish (self , s , loc , toks ):
24222390 #~ print "finish", toks
2423- self ._expr = Hlist (toks )
2424- return [self ._expr ]
2391+ return [Hlist (toks )]
24252392
24262393 def math (self , s , loc , toks ):
24272394 #~ print "math", toks
@@ -2949,8 +2916,11 @@ def parse(self, s, dpi = 72, prop = None):
29492916 The results are cached, so multiple calls to :meth:`parse`
29502917 with the same expression should be fast.
29512918 """
2919+ # There is a bug in Python 3.x where it leaks frame references,
2920+ # and therefore can't handle this caching
29522921 if prop is None :
29532922 prop = FontProperties ()
2923+
29542924 cacheKey = (s , dpi , hash (prop ))
29552925 result = self ._cache .get (cacheKey )
29562926 if result is not None :
@@ -2980,14 +2950,6 @@ def parse(self, s, dpi = 72, prop = None):
29802950 font_output .set_canvas_size (box .width , box .height , box .depth )
29812951 result = font_output .get_results (box )
29822952 self ._cache [cacheKey ] = result
2983- # Free up the transient data structures
2984- self ._parser .clear ()
2985-
2986- # Fix cyclical references
2987- font_output .destroy ()
2988- font_output .mathtext_backend .fonts_object = None
2989- font_output .mathtext_backend = None
2990-
29912953 return result
29922954
29932955 def to_mask (self , texstr , dpi = 120 , fontsize = 14 ):
0 commit comments