@@ -2092,17 +2092,6 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
20922092
20932093 # TODO: combine consecutive texts into one BT/ET delimited section
20942094
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-
21062095 self .check_gc (gc , gc ._rgb )
21072096 if ismath :
21082097 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):
21152104 else :
21162105 font = self ._get_font_ttf (prop )
21172106 self .track_characters (font , s )
2118- font .set_text (s , 0.0 , flags = LOAD_NO_HINTING )
2119-
21202107 fonttype = rcParams ['pdf.fonttype' ]
2121-
21222108 # We can't subset all OpenType fonts, so switch to Type 42
21232109 # in that case.
21242110 if is_opentype_cff_font (font .fname ):
21252111 fonttype = 42
21262112
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 ):
21562116 self .file .output (Op .begin_text ,
2157- self .file .fontName (prop ),
2158- fontsize ,
2159- Op .selectfont )
2117+ self .file .fontName (prop ), fontsize , Op .selectfont )
21602118 self ._setup_textpos (x , y , angle )
21612119 self .file .output (self .encode_string (s , fonttype ), Op .show ,
21622120 Op .end_text )
21632121
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
21712147 # Do the rotation and global translation as a single matrix
21722148 # concatenation up front
21732149 self .file .output (Op .gsave )
21742150 a = math .radians (angle )
21752151 self .file .output (math .cos (a ), math .sin (a ),
21762152 - math .sin (a ), math .cos (a ),
21772153 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.
21802155 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
21992163 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 )
22202175 self .file .output (Op .grestore )
22212176
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-
22282177 def new_gc (self ):
22292178 # docstring inherited
22302179 return GraphicsContextPdf (self .file )
0 commit comments