@@ -88,14 +88,16 @@ def _move_path_to_path_or_stream(src, dst):
88
88
shutil .move (src , dst , copy_function = shutil .copyfile )
89
89
90
90
91
- def _font_to_ps_type3 (font_path , glyph_indices ):
91
+ def _font_to_ps_type3 (font_path , subset_index , glyph_indices ):
92
92
"""
93
93
Subset *glyphs_indices* from the font at *font_path* into a Type 3 font.
94
94
95
95
Parameters
96
96
----------
97
97
font_path : path-like
98
98
Path to the font to be subsetted.
99
+ subset_index : int
100
+ The subset of the above font being created.
99
101
glyph_indices : set[int]
100
102
The glyphs to include in the subsetted font.
101
103
@@ -111,15 +113,15 @@ def _font_to_ps_type3(font_path, glyph_indices):
111
113
%!PS-Adobe-3.0 Resource-Font
112
114
%%Creator: Converted from TrueType to Type 3 by Matplotlib.
113
115
10 dict begin
114
- /FontName /{font_name} def
116
+ /FontName /{font_name}-{subset} def
115
117
/PaintType 0 def
116
118
/FontMatrix [{inv_units_per_em} 0 0 {inv_units_per_em} 0 0] def
117
119
/FontBBox [{bbox}] def
118
120
/FontType 3 def
119
121
/Encoding [{encoding}] def
120
122
/CharStrings {num_glyphs} dict dup begin
121
123
/.notdef 0 def
122
- """ .format (font_name = font .postscript_name ,
124
+ """ .format (font_name = font .postscript_name , subset = subset_index ,
123
125
inv_units_per_em = 1 / font .units_per_EM ,
124
126
bbox = " " .join (map (str , font .bbox )),
125
127
encoding = " " .join (f"/{ font .get_glyph_name (glyph_index )} "
@@ -168,20 +170,22 @@ def _font_to_ps_type3(font_path, glyph_indices):
168
170
return preamble + "\n " .join (entries ) + postamble
169
171
170
172
171
- def _font_to_ps_type42 (font_path , glyph_indices , fh ):
173
+ def _font_to_ps_type42 (font_path , subset_index , glyph_indices , fh ):
172
174
"""
173
175
Subset *glyph_indices* from the font at *font_path* into a Type 42 font at *fh*.
174
176
175
177
Parameters
176
178
----------
177
179
font_path : path-like
178
180
Path to the font to be subsetted.
181
+ subset_index : int
182
+ The subset of the above font being created.
179
183
glyph_indices : set[int]
180
184
The glyphs to include in the subsetted font.
181
185
fh : file-like
182
186
Where to write the font.
183
187
"""
184
- _log .debug ("SUBSET %s characters: %s" , font_path , glyph_indices )
188
+ _log .debug ("SUBSET %s:%d characters: %s" , font_path , subset_index , glyph_indices )
185
189
try :
186
190
kw = {}
187
191
# fix this once we support loading more fonts from a collection
@@ -192,25 +196,27 @@ def _font_to_ps_type42(font_path, glyph_indices, fh):
192
196
_backend_pdf_ps .get_glyphs_subset (font_path , glyph_indices ) as subset ):
193
197
fontdata = _backend_pdf_ps .font_as_file (subset ).getvalue ()
194
198
_log .debug (
195
- "SUBSET %s %d -> %d" , font_path , os . stat ( font_path ). st_size ,
196
- len (fontdata )
199
+ "SUBSET %s:%d %d -> %d" , font_path , subset_index ,
200
+ os . stat ( font_path ). st_size , len (fontdata )
197
201
)
198
- fh .write (_serialize_type42 (font , subset , fontdata ))
202
+ fh .write (_serialize_type42 (font , subset_index , subset , fontdata ))
199
203
except RuntimeError :
200
204
_log .warning (
201
205
"The PostScript backend does not currently support the selected font (%s)." ,
202
206
font_path )
203
207
raise
204
208
205
209
206
- def _serialize_type42 (font , subset , fontdata ):
210
+ def _serialize_type42 (font , subset_index , subset , fontdata ):
207
211
"""
208
212
Output a PostScript Type-42 format representation of font
209
213
210
214
Parameters
211
215
----------
212
216
font : fontTools.ttLib.ttFont.TTFont
213
217
The original font object
218
+ subset_index : int
219
+ The subset of the above font to be created.
214
220
subset : fontTools.ttLib.ttFont.TTFont
215
221
The subset font object
216
222
fontdata : bytes
@@ -231,7 +237,7 @@ def _serialize_type42(font, subset, fontdata):
231
237
10 dict begin
232
238
/FontType 42 def
233
239
/FontMatrix [1 0 0 1 0 0] def
234
- /FontName /{ name .getDebugName (6 )} def
240
+ /FontName /{ name .getDebugName (6 )} - { subset_index } def
235
241
/FontInfo 7 dict dup begin
236
242
/FullName ({ name .getDebugName (4 )} ) def
237
243
/FamilyName ({ name .getDebugName (1 )} ) def
@@ -425,7 +431,8 @@ def __init__(self, width, height, pswriter, imagedpi=72):
425
431
self ._clip_paths = {}
426
432
self ._path_collection_id = 0
427
433
428
- self ._character_tracker = _backend_pdf_ps .CharacterTracker ()
434
+ self ._character_tracker = _backend_pdf_ps .CharacterTracker (
435
+ _backend_pdf_ps ._FONT_MAX_GLYPH .get (mpl .rcParams ['ps.fonttype' ], 0 ))
429
436
self ._logwarn_once = functools .cache (_log .warning )
430
437
431
438
def _is_transparent (self , rgb_or_rgba ):
@@ -793,12 +800,16 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
793
800
else :
794
801
language = mtext .get_language () if mtext is not None else None
795
802
font = self ._get_font_ttf (prop )
796
- self ._character_tracker .track (font , s )
797
803
for item in _text_helpers .layout (s , font , language = language ):
804
+ # NOTE: We ignore the character code in the subset, because PS uses the
805
+ # glyph name to write text. The subset is only used to ensure that each
806
+ # one does not overflow format limits.
807
+ subset , _ = self ._character_tracker .track_glyph (
808
+ item .ft_object , item .char , item .glyph_index )
798
809
ps_name = (item .ft_object .postscript_name
799
810
.encode ("ascii" , "replace" ).decode ("ascii" ))
800
811
glyph_name = item .ft_object .get_glyph_name (item .glyph_index )
801
- stream .append ((ps_name , item .x , glyph_name ))
812
+ stream .append ((f' { ps_name } - { subset } ' , item .x , glyph_name ))
802
813
self .set_color (* gc .get_rgb ())
803
814
804
815
for ps_name , group in itertools . \
@@ -827,11 +838,15 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
827
838
f"{ angle :g} rotate\n " )
828
839
lastfont = None
829
840
for font , fontsize , ccode , glyph_index , ox , oy in glyphs :
830
- self ._character_tracker .track_glyph (font , ccode , glyph_index )
831
- if (font .postscript_name , fontsize ) != lastfont :
832
- lastfont = font .postscript_name , fontsize
841
+ # NOTE: We ignore the character code in the subset, because PS uses the
842
+ # glyph name to write text. The subset is only used to ensure that each one
843
+ # does not overflow format limits.
844
+ subset , _ = self ._character_tracker .track_glyph (
845
+ font , ccode , glyph_index )
846
+ if (font .postscript_name , subset , fontsize ) != lastfont :
847
+ lastfont = font .postscript_name , subset , fontsize
833
848
self ._pswriter .write (
834
- f"/{ font .postscript_name } { fontsize } selectfont\n " )
849
+ f"/{ font .postscript_name } - { subset } { fontsize } selectfont\n " )
835
850
glyph_name = font .get_glyph_name (glyph_index )
836
851
self ._pswriter .write (
837
852
f"{ ox :g} { oy :g} moveto\n "
@@ -1071,18 +1086,15 @@ def print_figure_impl(fh):
1071
1086
print ("\n " .join (_psDefs ), file = fh )
1072
1087
if not mpl .rcParams ['ps.useafm' ]:
1073
1088
for font , subsets in ps_renderer ._character_tracker .used .items ():
1074
- for charmap in subsets :
1089
+ for subset , charmap in enumerate ( subsets ) :
1075
1090
if not charmap :
1076
1091
continue
1077
1092
fonttype = mpl .rcParams ['ps.fonttype' ]
1078
- # Can't use more than 255 chars from a single Type 3 font.
1079
- if len (charmap ) > 255 :
1080
- fonttype = 42
1081
1093
fh .flush ()
1082
1094
if fonttype == 3 :
1083
- fh .write (_font_to_ps_type3 (font , charmap .values ()))
1095
+ fh .write (_font_to_ps_type3 (font , subset , charmap .values ()))
1084
1096
else : # Type 42 only.
1085
- _font_to_ps_type42 (font , charmap .values (), fh )
1097
+ _font_to_ps_type42 (font , subset , charmap .values (), fh )
1086
1098
print ("end" , file = fh )
1087
1099
print ("%%EndProlog" , file = fh )
1088
1100
0 commit comments