44import matplotlib .pyplot as plt
55from matplotlib .image import BboxImage
66import numpy as np
7- from matplotlib .transforms import Affine2D , IdentityTransform
7+ from matplotlib .transforms import IdentityTransform
88
9- import matplotlib .font_manager as font_manager
10- from matplotlib .ft2font import FT2Font , KERNING_DEFAULT , LOAD_NO_HINTING
11- from matplotlib .font_manager import FontProperties
12- from matplotlib .path import Path
139import matplotlib .patches as mpatches
1410
1511from matplotlib .offsetbox import AnnotationBbox ,\
1612 AnchoredOffsetbox , AuxTransformBox
1713
18- #from matplotlib.offsetbox import
19-
2014from matplotlib .cbook import get_sample_data
2115
16+ from matplotlib .text import TextPath
2217
23- class TextPatch (mpatches .PathPatch ):
24-
25- FONT_SCALE = 100.
26-
27- def __init__ (self , xy , s , size = None , prop = None , bbox_image = None ,
28- * kl , ** kwargs ):
29- if prop is None :
30- prop = FontProperties ()
31-
32- if size is None :
33- size = prop .get_size_in_points ()
34-
35- self ._xy = xy
36- self .set_size (size )
3718
38- self .text_path = self .text_get_path (prop , s )
39-
40- mpatches .PathPatch .__init__ (self , self .text_path , * kl , ** kwargs )
19+ class PathClippedImagePatch (mpatches .PathPatch ):
20+ """
21+ The given image is used to draw the face of the patch. Internally,
22+ it uses BboxImage whose clippath set to the path of the patch.
4123
24+ FIXME : The result is currently dpi dependent.
25+ """
26+ def __init__ (self , path , bbox_image , ** kwargs ):
27+ mpatches .PathPatch .__init__ (self , path , ** kwargs )
28+ self ._facecolor = "none"
4229 self ._init_bbox_image (bbox_image )
4330
44-
31+ def set_facecolor (self , color ):
32+ pass
33+
4534 def _init_bbox_image (self , im ):
4635
47- if im is None :
48- self .bbox_image = None
49- else :
50- bbox_image = BboxImage (self .get_window_extent ,
51- norm = None ,
52- origin = None ,
53- )
54- bbox_image .set_transform (IdentityTransform ())
36+ bbox_image = BboxImage (self .get_window_extent ,
37+ norm = None ,
38+ origin = None ,
39+ )
40+ bbox_image .set_transform (IdentityTransform ())
5541
56- bbox_image .set_data (im )
57- self .bbox_image = bbox_image
42+ bbox_image .set_data (im )
43+ self .bbox_image = bbox_image
5844
5945 def draw (self , renderer = None ):
6046
6147
62- if self .bbox_image is not None :
63- # the clip path must be updated every draw. any solution? -JJ
64- self .bbox_image .set_clip_path (self .text_path , self .get_transform ())
65- self .bbox_image .draw (renderer )
48+ # the clip path must be updated every draw. any solution? -JJ
49+ self .bbox_image .set_clip_path (self ._path , self .get_transform ())
50+ self .bbox_image .draw (renderer )
6651
6752 mpatches .PathPatch .draw (self , renderer )
6853
6954
70- def set_size (self , size ):
71- self ._size = size
72-
73- def get_size (self ):
74- return self ._size
75-
76- def get_patch_transform (self ):
77- tr = Affine2D ().scale (self ._size / self .FONT_SCALE , self ._size / self .FONT_SCALE )
78- return tr .translate (* self ._xy )
79-
80- def glyph_char_path (self , glyph , currx = 0. ):
81-
82- verts , codes = [], []
83- for step in glyph .path :
84- if step [0 ] == 0 : # MOVE_TO
85- verts .append ((step [1 ], step [2 ]))
86- codes .append (Path .MOVETO )
87- elif step [0 ] == 1 : # LINE_TO
88- verts .append ((step [1 ], step [2 ]))
89- codes .append (Path .LINETO )
90- elif step [0 ] == 2 : # CURVE3
91- verts .extend ([(step [1 ], step [2 ]),
92- (step [3 ], step [4 ])])
93- codes .extend ([Path .CURVE3 , Path .CURVE3 ])
94- elif step [0 ] == 3 : # CURVE4
95- verts .extend ([(step [1 ], step [2 ]),
96- (step [3 ], step [4 ]),
97- (step [5 ], step [6 ])])
98- codes .extend ([Path .CURVE4 , Path .CURVE4 , Path .CURVE4 ])
99- elif step [0 ] == 4 : # ENDPOLY
100- verts .append ((0 , 0 ,))
101- codes .append (Path .CLOSEPOLY )
102-
103- verts = [(x + currx , y ) for (x ,y ) in verts ]
104-
105- return verts , codes
106-
107-
108- def text_get_path (self , prop , s ):
109-
110- fname = font_manager .findfont (prop )
111- font = FT2Font (str (fname ))
112-
113- font .set_size (self .FONT_SCALE , 72 )
114-
115- cmap = font .get_charmap ()
116- lastgind = None
117-
118- currx = 0
119-
120- verts , codes = [], []
121-
122- for c in s :
123-
124- ccode = ord (c )
125- gind = cmap .get (ccode )
126- if gind is None :
127- ccode = ord ('?' )
128- gind = 0
129- glyph = font .load_char (ccode , flags = LOAD_NO_HINTING )
130-
131-
132- if lastgind is not None :
133- kern = font .get_kerning (lastgind , gind , KERNING_DEFAULT )
134- else :
135- kern = 0
136- currx += (kern / 64.0 ) #/ (self.FONT_SCALE)
137-
138- verts1 , codes1 = self .glyph_char_path (glyph , currx )
139- verts .extend (verts1 )
140- codes .extend (codes1 )
141-
142-
143- currx += (glyph .linearHoriAdvance / 65536.0 ) #/ (self.FONT_SCALE)
144- lastgind = gind
145-
146- return Path (verts , codes )
147-
14855if 1 :
14956
15057 fig = plt .figure (1 )
@@ -156,10 +63,12 @@ def text_get_path(self, prop, s):
15663 from matplotlib ._png import read_png
15764 fn = get_sample_data ("lena.png" , asfileobj = False )
15865 arr = read_png (fn )
159- p = TextPatch ((0 , 0 ), "!?" , size = 150 , fc = "none" , ec = "k" ,
160- bbox_image = arr ,
161- transform = IdentityTransform ())
162- p .set_clip_on (False )
66+
67+ text_path = TextPath ((0 , 0 ), "!?" , size = 150 )
68+ p = PathClippedImagePatch (text_path , arr , ec = "k" ,
69+ transform = IdentityTransform ())
70+
71+ #p.set_clip_on(False)
16372
16473 # make offset box
16574 offsetbox = AuxTransformBox (IdentityTransform ())
@@ -176,38 +85,35 @@ def text_get_path(self, prop, s):
17685
17786 ax = plt .subplot (212 )
17887
179- shadow1 = TextPatch ((3 , - 2 ), "TextPath" , size = 70 , fc = "none" , ec = "0.6" , lw = 3 ,
180- transform = IdentityTransform ())
181- shadow2 = TextPatch ((3 , - 2 ), "TextPath" , size = 70 , fc = "0.3" , ec = "none" ,
182- transform = IdentityTransform ())
183-
18488 arr = np .arange (256 ).reshape (1 ,256 )/ 256.
185- text_path = TextPatch ((0 , 0 ), "TextPath" , size = 70 , fc = "none" , ec = "none" , lw = 1 ,
186- bbox_image = arr ,
187- transform = IdentityTransform ())
89+
90+ text_path = TextPath ((0 , 0 ), "TextPath" , size = 70 )
91+ text_patch = PathClippedImagePatch (text_path , arr , ec = "none" ,
92+ transform = IdentityTransform ())
93+
94+ shadow1 = mpatches .Shadow (text_patch , 3 , - 2 , props = dict (fc = "none" , ec = "0.6" , lw = 3 ))
95+ shadow2 = mpatches .Shadow (text_patch , 3 , - 2 , props = dict (fc = "0.3" , ec = "none" ))
96+
18897
18998 # make offset box
19099 offsetbox = AuxTransformBox (IdentityTransform ())
191100 offsetbox .add_artist (shadow1 )
192101 offsetbox .add_artist (shadow2 )
193- offsetbox .add_artist (text_path )
102+ offsetbox .add_artist (text_patch )
194103
195104 # place the anchored offset box using AnnotationBbox
196105 ab = AnnotationBbox (offsetbox , (0.5 , 0.5 ),
197106 xycoords = 'data' ,
198107 boxcoords = "offset points" ,
199108 box_alignment = (0.5 ,0.5 ),
200109 )
201-
110+ #text_path.set_size(10)
202111
203112 ax .add_artist (ab )
204113
205114 ax .set_xlim (0 , 1 )
206115 ax .set_ylim (0 , 1 )
207116
208117
209-
210-
211-
212118 plt .draw ()
213119 plt .show ()
0 commit comments