@@ -2092,17 +2092,6 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
2092
2092
2093
2093
# TODO: combine consecutive texts into one BT/ET delimited section
2094
2094
2095
- # This function is rather complex, since there is no way to
2096
- # access characters of a Type 3 font with codes > 255. (Type
2097
- # 3 fonts can not have a CIDMap). Therefore, we break the
2098
- # string into chunks, where each chunk contains exclusively
2099
- # 1-byte or exclusively 2-byte characters, and output each
2100
- # chunk a separate command. 1-byte characters use the regular
2101
- # text show command (Tj), whereas 2-byte characters use the
2102
- # use XObject command (Do). If using Type 42 fonts, all of
2103
- # this complication is avoided, but of course, those fonts can
2104
- # not be subsetted.
2105
-
2106
2095
self .check_gc (gc , gc ._rgb )
2107
2096
if ismath :
2108
2097
return self .draw_mathtext (gc , x , y , s , prop , angle )
@@ -2115,116 +2104,76 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
2115
2104
else :
2116
2105
font = self ._get_font_ttf (prop )
2117
2106
self .track_characters (font , s )
2118
- font .set_text (s , 0.0 , flags = LOAD_NO_HINTING )
2119
-
2120
2107
fonttype = rcParams ['pdf.fonttype' ]
2121
-
2122
2108
# We can't subset all OpenType fonts, so switch to Type 42
2123
2109
# in that case.
2124
2110
if is_opentype_cff_font (font .fname ):
2125
2111
fonttype = 42
2126
2112
2127
- def check_simple_method (s ):
2128
- """
2129
- Determine if we should use the simple or woven method to output
2130
- this text, and chunks the string into 1-byte and 2-byte sections if
2131
- necessary.
2132
- """
2133
- use_simple_method = True
2134
- chunks = []
2135
-
2136
- if not rcParams ['pdf.use14corefonts' ]:
2137
- if fonttype == 3 and not isinstance (s , bytes ) and len (s ) != 0 :
2138
- # Break the string into chunks where each chunk is either
2139
- # a string of chars <= 255, or a single character > 255.
2140
- s = str (s )
2141
- for c in s :
2142
- if ord (c ) <= 255 :
2143
- char_type = 1
2144
- else :
2145
- char_type = 2
2146
- if len (chunks ) and chunks [- 1 ][0 ] == char_type :
2147
- chunks [- 1 ][1 ].append (c )
2148
- else :
2149
- chunks .append ((char_type , [c ]))
2150
- use_simple_method = (len (chunks ) == 1 and
2151
- chunks [- 1 ][0 ] == 1 )
2152
- return use_simple_method , chunks
2153
-
2154
- def draw_text_simple ():
2155
- """Outputs text using the simple method."""
2113
+ # If fonttype != 3 or there are no multibyte characters, emit the whole
2114
+ # string at once.
2115
+ if fonttype != 3 or all (ord (char ) <= 255 for char in s ):
2156
2116
self .file .output (Op .begin_text ,
2157
- self .file .fontName (prop ),
2158
- fontsize ,
2159
- Op .selectfont )
2117
+ self .file .fontName (prop ), fontsize , Op .selectfont )
2160
2118
self ._setup_textpos (x , y , angle )
2161
2119
self .file .output (self .encode_string (s , fonttype ), Op .show ,
2162
2120
Op .end_text )
2163
2121
2164
- def draw_text_woven (chunks ):
2165
- """
2166
- Outputs text using the woven method, alternating between chunks of
2167
- 1-byte and 2-byte characters. Only used for Type 3 fonts.
2168
- """
2169
- chunks = [(a , '' .join (b )) for a , b in chunks ]
2170
-
2122
+ # There is no way to access multibyte characters of Type 3 fonts, as
2123
+ # they cannot have a CIDMap. Therefore, in this case we break the
2124
+ # string into chunks, where each chunk contains either a string of
2125
+ # consecutive 1-byte characters or a single multibyte character. Each
2126
+ # chunk is emitted with a separate command: 1-byte characters use the
2127
+ # regular text show command (Tj), whereas multibyte characters use
2128
+ # the XObject command (Do). (If using Type 42 fonts, all of this
2129
+ # complication is avoided, but of course, those fonts can not be
2130
+ # subsetted.)
2131
+ else :
2132
+ singlebyte_chunks = [] # List of (start_x, list-of-1-byte-chars).
2133
+ multibyte_glyphs = [] # List of (start_x, glyph_index).
2134
+ prev_was_singlebyte = False
2135
+ for char , (glyph_idx , glyph_x ) in zip (
2136
+ s ,
2137
+ _text_layout .layout (s , font , kern_mode = KERNING_UNFITTED )):
2138
+ if ord (char ) <= 255 :
2139
+ if prev_was_singlebyte :
2140
+ singlebyte_chunks [- 1 ][1 ].append (char )
2141
+ else :
2142
+ singlebyte_chunks .append ((glyph_x , [char ]))
2143
+ prev_was_singlebyte = True
2144
+ else :
2145
+ multibyte_glyphs .append ((glyph_x , glyph_idx ))
2146
+ prev_was_singlebyte = False
2171
2147
# Do the rotation and global translation as a single matrix
2172
2148
# concatenation up front
2173
2149
self .file .output (Op .gsave )
2174
2150
a = math .radians (angle )
2175
2151
self .file .output (math .cos (a ), math .sin (a ),
2176
2152
- math .sin (a ), math .cos (a ),
2177
2153
x , y , Op .concat_matrix )
2178
-
2179
- # Output all the 1-byte characters in a BT/ET group.
2154
+ # Emit all the 1-byte characters in a BT/ET group.
2180
2155
self .file .output (Op .begin_text ,
2181
- self .file .fontName (prop ),
2182
- fontsize ,
2183
- Op .selectfont )
2184
- newx = oldx = 0
2185
- for chunk_type , chunk in chunks :
2186
- if chunk_type == 1 :
2187
- self ._setup_textpos (newx , 0 , 0 , oldx , 0 , 0 )
2188
- self .file .output (self .encode_string (chunk , fonttype ),
2189
- Op .show )
2190
- oldx = newx
2191
- # Update newx to include the advance from this chunk,
2192
- # regardless of its mode...
2193
- for char_idx , char_x in _text_layout .layout (
2194
- chunk , font , x0 = newx , kern_mode = KERNING_UNFITTED ):
2195
- pass
2196
- newx = char_x + ( # ... including the last character's advance
2197
- font .load_glyph (char_idx , flags = LOAD_NO_HINTING )
2198
- .linearHoriAdvance / 65536 )
2156
+ self .file .fontName (prop ), fontsize , Op .selectfont )
2157
+ prev_start_x = 0
2158
+ for start_x , chars in singlebyte_chunks :
2159
+ self ._setup_textpos (start_x , 0 , 0 , prev_start_x , 0 , 0 )
2160
+ self .file .output (self .encode_string ('' .join (chars ), fonttype ),
2161
+ Op .show )
2162
+ prev_start_x = start_x
2199
2163
self .file .output (Op .end_text )
2200
-
2201
- # Then output all the 2-byte characters.
2202
- newx = 0
2203
- for chunk_type , chunk in chunks :
2204
- for char_idx , char_x in _text_layout .layout (
2205
- chunk , font , x0 = newx , kern_mode = KERNING_UNFITTED ):
2206
- if chunk_type == 2 :
2207
- glyph_name = font .get_glyph_name (char_idx )
2208
- self .file .output (Op .gsave )
2209
- self .file .output (0.001 * fontsize , 0 ,
2210
- 0 , 0.001 * fontsize ,
2211
- char_x , 0 , Op .concat_matrix )
2212
- name = self .file ._get_xobject_symbol_name (
2213
- font .fname , glyph_name )
2214
- self .file .output (Name (name ), Op .use_xobject )
2215
- self .file .output (Op .grestore )
2216
- newx = char_x + ( # ... including the last character's advance
2217
- font .load_glyph (char_idx , flags = LOAD_NO_HINTING )
2218
- .linearHoriAdvance / 65536 )
2219
-
2164
+ # Then emit all the multibyte characters, one at a time.
2165
+ for start_x , glyph_idx in multibyte_glyphs :
2166
+ glyph_name = font .get_glyph_name (glyph_idx )
2167
+ self .file .output (Op .gsave )
2168
+ self .file .output (0.001 * fontsize , 0 ,
2169
+ 0 , 0.001 * fontsize ,
2170
+ start_x , 0 , Op .concat_matrix )
2171
+ name = self .file ._get_xobject_symbol_name (
2172
+ font .fname , glyph_name )
2173
+ self .file .output (Name (name ), Op .use_xobject )
2174
+ self .file .output (Op .grestore )
2220
2175
self .file .output (Op .grestore )
2221
2176
2222
- use_simple_method , chunks = check_simple_method (s )
2223
- if use_simple_method :
2224
- return draw_text_simple ()
2225
- else :
2226
- return draw_text_woven (chunks )
2227
-
2228
2177
def new_gc (self ):
2229
2178
# docstring inherited
2230
2179
return GraphicsContextPdf (self .file )
0 commit comments