FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
matplotlib/lib/matplotlib/textpath.py at v3.11.2 · matplotlib/matplotlib · GitHub
matplotlib
/
matplotlib
Public
Uh oh!
There was an error while loading.
Please reload this page
.
Notifications
You must be signed in to change notification settings
Fork
8.5k
Star
23.2k
Code
Issues
1.1k
Pull requests
429
Actions
Projects
Wiki
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security and quality
Insights
Expand file tree
Breadcrumbs
matplotlib
/
lib
/
matplotlib
/
textpath.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
376 lines (300 loc) · 12.4 KB
Breadcrumbs
matplotlib
/
lib
/
matplotlib
/
textpath.py
Copy path
File metadata and controls
376 lines (300 loc) · 12.4 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
from
collections
import
OrderedDict
import
logging
import
urllib
.
parse
import
numpy
as
np
from
matplotlib
import
_text_helpers
,
dviread
from
matplotlib
.
font_manager
import
(
FontProperties
,
get_font
,
fontManager
as
_fontManager
)
from
matplotlib
.
ft2font
import
LoadFlags
from
matplotlib
.
mathtext
import
MathTextParser
from
matplotlib
.
path
import
Path
from
matplotlib
.
texmanager
import
TexManager
from
matplotlib
.
transforms
import
Affine2D
_log
=
logging
.
getLogger
(
__name__
)
class
TextToPath
:
"""A class that converts strings to paths."""
FONT_SCALE
=
100.
DPI
=
72
def
__init__
(
self
):
self
.
mathtext_parser
=
MathTextParser
(
'path'
)
self
.
_texmanager
=
None
def
_get_font
(
self
,
prop
):
"""
Find the `FT2Font` matching font properties *prop*, with its size set.
"""
filenames
=
_fontManager
.
_find_fonts_by_props
(
prop
)
font
=
get_font
(
filenames
)
font
.
set_size
(
self
.
FONT_SCALE
,
self
.
DPI
)
return
font
def
_get_hinting_flag
(
self
):
return
LoadFlags
.
NO_HINTING
def
_get_glyph_repr
(
self
,
font
,
glyph
):
"""Return a unique id for the given font and glyph index."""
return
urllib
.
parse
.
quote
(
f"
{
font
.
postscript_name
}
-
{
glyph
:x
}
"
)
def
get_text_width_height_descent
(
self
,
s
,
prop
,
ismath
):
fontsize
=
prop
.
get_size_in_points
()
if
ismath
==
"TeX"
:
return
TexManager
().
get_text_width_height_descent
(
s
,
fontsize
)
scale
=
fontsize
/
self
.
FONT_SCALE
if
ismath
:
prop
=
prop
.
copy
()
prop
.
set_size
(
self
.
FONT_SCALE
)
width
,
height
,
descent
,
*
_
=
\
self
.
mathtext_parser
.
parse
(
s
,
72
,
prop
)
return
width
*
scale
,
height
*
scale
,
descent
*
scale
font
=
self
.
_get_font
(
prop
)
font
.
set_text
(
s
,
0.0
,
flags
=
LoadFlags
.
NO_HINTING
)
w
,
h
=
font
.
get_width_height
()
w
/=
64.0
# convert from subpixels
h
/=
64.0
d
=
font
.
get_descent
()
d
/=
64.0
return
w
*
scale
,
h
*
scale
,
d
*
scale
def
get_text_path
(
self
,
prop
,
s
,
ismath
=
False
,
*
,
features
=
None
,
language
=
None
):
"""
Convert text *s* to path (a tuple of vertices and codes for
matplotlib.path.Path).
Parameters
----------
prop : `~matplotlib.font_manager.FontProperties`
The font properties for the text.
s : str
The text to be converted.
ismath : {False, True, "TeX"}
If True, use mathtext parser. If "TeX", use tex for rendering.
language : str, optional
The language of the text in a format accepted by libraqm, namely `a BCP47
language code <https://www.w3.org/International/articles/language-tags/>`_.
Returns
-------
verts : list
A list of arrays containing the (x, y) coordinates of the vertices.
codes : list
A list of path codes.
Examples
--------
Create a list of vertices and codes from a text, and create a `.Path`
from those::
from matplotlib.path import Path
from matplotlib.text import TextToPath
from matplotlib.font_manager import FontProperties
fp = FontProperties(family="Comic Neue", style="italic")
verts, codes = TextToPath().get_text_path(fp, "ABC")
path = Path(verts, codes, closed=False)
Also see `TextPath` for a more direct way to create a path from a text.
"""
if
ismath
==
"TeX"
:
glyph_info
,
glyph_map
,
rects
=
self
.
get_glyphs_tex
(
prop
,
s
)
elif
not
ismath
:
font
=
self
.
_get_font
(
prop
)
glyph_info
,
glyph_map
,
rects
=
self
.
get_glyphs_with_font
(
font
,
s
,
features
=
features
,
language
=
language
)
else
:
glyph_info
,
glyph_map
,
rects
=
self
.
get_glyphs_mathtext
(
prop
,
s
)
verts
,
codes
=
[], []
for
glyph_repr
,
xposition
,
yposition
,
scale
in
glyph_info
:
verts1
,
codes1
=
glyph_map
[
glyph_repr
]
verts
.
extend
(
verts1
*
scale
+
[
xposition
,
yposition
])
codes
.
extend
(
codes1
)
for
verts1
,
codes1
in
rects
:
verts
.
extend
(
verts1
)
codes
.
extend
(
codes1
)
# Make sure an empty string or one with nothing to print
# (e.g. only spaces & newlines) will be valid/empty path
if
not
verts
:
verts
=
np
.
empty
((
0
,
2
))
return
verts
,
codes
def
get_glyphs_with_font
(
self
,
font
,
s
,
glyph_map
=
None
,
return_new_glyphs_only
=
False
,
*
,
features
=
None
,
language
=
None
):
"""
Convert string *s* to vertices and codes using the provided ttf font.
"""
if
glyph_map
is
None
:
glyph_map
=
OrderedDict
()
if
return_new_glyphs_only
:
glyph_map_new
=
OrderedDict
()
else
:
glyph_map_new
=
glyph_map
xpositions
=
[]
ypositions
=
[]
glyph_reprs
=
[]
for
item
in
_text_helpers
.
layout
(
s
,
font
,
features
=
features
,
language
=
language
):
glyph_repr
=
self
.
_get_glyph_repr
(
item
.
ft_object
,
item
.
glyph_index
)
glyph_reprs
.
append
(
glyph_repr
)
xpositions
.
append
(
item
.
x
)
ypositions
.
append
(
item
.
y
)
if
glyph_repr
not
in
glyph_map
:
glyph_map_new
[
glyph_repr
]
=
item
.
ft_object
.
get_path
()
sizes
=
[
1.
]
*
len
(
xpositions
)
rects
=
[]
return
(
list
(
zip
(
glyph_reprs
,
xpositions
,
ypositions
,
sizes
)),
glyph_map_new
,
rects
)
def
get_glyphs_mathtext
(
self
,
prop
,
s
,
glyph_map
=
None
,
return_new_glyphs_only
=
False
):
"""
Parse mathtext string *s* and convert it to a (vertices, codes) pair.
"""
prop
=
prop
.
copy
()
prop
.
set_size
(
self
.
FONT_SCALE
)
width
,
height
,
descent
,
glyphs
,
rects
=
self
.
mathtext_parser
.
parse
(
s
,
self
.
DPI
,
prop
)
if
not
glyph_map
:
glyph_map
=
OrderedDict
()
if
return_new_glyphs_only
:
glyph_map_new
=
OrderedDict
()
else
:
glyph_map_new
=
glyph_map
xpositions
=
[]
ypositions
=
[]
glyph_reprs
=
[]
sizes
=
[]
for
font
,
fontsize
,
ccode
,
glyph_index
,
ox
,
oy
in
glyphs
:
glyph_repr
=
self
.
_get_glyph_repr
(
font
,
glyph_index
)
if
glyph_repr
not
in
glyph_map
:
font
.
clear
()
font
.
set_size
(
self
.
FONT_SCALE
,
self
.
DPI
)
font
.
load_glyph
(
glyph_index
,
flags
=
LoadFlags
.
NO_HINTING
)
glyph_map_new
[
glyph_repr
]
=
font
.
get_path
()
xpositions
.
append
(
ox
)
ypositions
.
append
(
oy
)
glyph_reprs
.
append
(
glyph_repr
)
size
=
fontsize
/
self
.
FONT_SCALE
sizes
.
append
(
size
)
myrects
=
[]
for
ox
,
oy
,
w
,
h
in
rects
:
vert1
=
[(
ox
,
oy
), (
ox
,
oy
+
h
), (
ox
+
w
,
oy
+
h
),
(
ox
+
w
,
oy
), (
ox
,
oy
), (
0
,
0
)]
code1
=
[
Path
.
MOVETO
,
Path
.
LINETO
,
Path
.
LINETO
,
Path
.
LINETO
,
Path
.
LINETO
,
Path
.
CLOSEPOLY
]
myrects
.
append
((
vert1
,
code1
))
return
(
list
(
zip
(
glyph_reprs
,
xpositions
,
ypositions
,
sizes
)),
glyph_map_new
,
myrects
)
def
get_glyphs_tex
(
self
,
prop
,
s
,
glyph_map
=
None
,
return_new_glyphs_only
=
False
):
"""Convert the string *s* to vertices and codes using usetex mode."""
# Mostly borrowed from pdf backend.
dvifile
=
TexManager
().
make_dvi
(
s
,
self
.
FONT_SCALE
)
with
dviread
.
Dvi
(
dvifile
,
self
.
DPI
)
as
dvi
:
page
,
=
dvi
if
glyph_map
is
None
:
glyph_map
=
OrderedDict
()
if
return_new_glyphs_only
:
glyph_map_new
=
OrderedDict
()
else
:
glyph_map_new
=
glyph_map
glyph_reprs
,
xpositions
,
ypositions
,
sizes
=
[], [], [], []
# Gather font information and do some setup for combining
# characters into strings.
for
text
in
page
.
text
:
font
=
get_font
(
text
.
font
.
resolve_path
())
if
text
.
font
.
subfont
:
raise
NotImplementedError
(
"Indexing TTC fonts is not supported yet"
)
glyph_repr
=
self
.
_get_glyph_repr
(
font
,
text
.
index
)
if
glyph_repr
not
in
glyph_map
:
font
.
clear
()
font
.
set_size
(
self
.
FONT_SCALE
,
self
.
DPI
)
font
.
load_glyph
(
text
.
index
,
flags
=
LoadFlags
.
TARGET_LIGHT
)
glyph_map_new
[
glyph_repr
]
=
font
.
get_path
()
glyph_reprs
.
append
(
glyph_repr
)
xpositions
.
append
(
text
.
x
)
ypositions
.
append
(
text
.
y
)
sizes
.
append
(
text
.
font_size
/
self
.
FONT_SCALE
)
myrects
=
[]
for
ox
,
oy
,
h
,
w
in
page
.
boxes
:
vert1
=
[(
ox
,
oy
), (
ox
+
w
,
oy
), (
ox
+
w
,
oy
+
h
),
(
ox
,
oy
+
h
), (
ox
,
oy
), (
0
,
0
)]
code1
=
[
Path
.
MOVETO
,
Path
.
LINETO
,
Path
.
LINETO
,
Path
.
LINETO
,
Path
.
LINETO
,
Path
.
CLOSEPOLY
]
myrects
.
append
((
vert1
,
code1
))
return
(
list
(
zip
(
glyph_reprs
,
xpositions
,
ypositions
,
sizes
)),
glyph_map_new
,
myrects
)
text_to_path
=
TextToPath
()
class
TextPath
(
Path
):
"""
Create a path from the text.
"""
def
__init__
(
self
,
xy
,
s
,
size
=
None
,
prop
=
None
,
_interpolation_steps
=
1
,
usetex
=
False
):
r"""
Create a path from the text. Note that it simply is a path,
not an artist. You need to use the `.PathPatch` (or other artists)
to draw this path onto the canvas.
Parameters
----------
xy : tuple or array of two float values
Position of the text. For no offset, use ``xy=(0, 0)``.
s : str
The text to convert to a path.
size : float, optional
Font size in points. Defaults to the size specified via the font
properties *prop*.
prop : `~matplotlib.font_manager.FontProperties`, optional
Font property. If not provided, will use a default
`.FontProperties` with parameters from the
:ref:`rcParams<customizing-with-dynamic-rc-settings>`.
_interpolation_steps : int, optional
(Currently ignored)
usetex : bool, default: False
Whether to use tex rendering.
Examples
--------
The following creates a path from the string "ABC" with Helvetica
font face; and another path from the latex fraction 1/2::
from matplotlib.text import TextPath
from matplotlib.font_manager import FontProperties
fp = FontProperties(family="Helvetica", style="italic")
path1 = TextPath((12, 12), "ABC", size=12, prop=fp)
path2 = TextPath((0, 0), r"$\frac{1}{2}$", size=12, usetex=True)
Also see :doc:`/gallery/text_labels_and_annotations/demo_text_path`.
"""
# Circular import.
from
matplotlib
.
text
import
Text
prop
=
FontProperties
.
_from_any
(
prop
)
if
size
is
None
:
size
=
prop
.
get_size_in_points
()
self
.
_xy
=
xy
self
.
set_size
(
size
)
self
.
_cached_vertices
=
None
s
,
ismath
=
Text
(
usetex
=
usetex
).
_preprocess_math
(
s
)
super
().
__init__
(
*
text_to_path
.
get_text_path
(
prop
,
s
,
ismath
=
ismath
),
_interpolation_steps
=
_interpolation_steps
,
readonly
=
True
)
self
.
_should_simplify
=
False
def
set_size
(
self
,
size
):
"""Set the text size."""
self
.
_size
=
size
self
.
_invalid
=
True
def
get_size
(
self
):
"""Get the text size."""
return
self
.
_size
@
property
def
vertices
(
self
):
"""
Return the cached path after updating it if necessary.
"""
self
.
_revalidate_path
()
return
self
.
_cached_vertices
@
property
def
codes
(
self
):
"""
Return the codes
"""
return
self
.
_codes
def
_revalidate_path
(
self
):
"""
Update the path if necessary.
The path for the text is initially create with the font size of
`.FONT_SCALE`, and this path is rescaled to other size when necessary.
"""
if
self
.
_invalid
or
self
.
_cached_vertices
is
None
:
tr
=
(
Affine2D
()
.
scale
(
self
.
_size
/
text_to_path
.
FONT_SCALE
)
.
translate
(
*
self
.
_xy
))
self
.
_cached_vertices
=
tr
.
transform
(
self
.
_vertices
)
self
.
_cached_vertices
.
flags
.
writeable
=
False
self
.
_invalid
=
False
Back
|
FazBrowse Home
|
New Git URL