@@ -2253,6 +2253,40 @@ def encode_string(self, s, fonttype):
2253
2253
return s .encode ('cp1252' , 'replace' )
2254
2254
return s .encode ('utf-16be' , 'replace' )
2255
2255
2256
+ def build_kern_sequence (self , string , font , fontsize ,
2257
+ kern_mode = KERNING_UNFITTED ):
2258
+ """
2259
+ Return an ordered sequence of contiguous substrings interspersed with
2260
+ integer kerning offsets.
2261
+
2262
+ The return value is compatible with the PDF TJ operator.
2263
+ """
2264
+ kern_sequence = [[]]
2265
+ prev_glyph_idx = None
2266
+ for char in string :
2267
+ glyph_idx = font .get_char_index (ord (char ))
2268
+
2269
+ # Absolute kerning in pt for actual font size
2270
+ kern = (font .get_kerning (prev_glyph_idx , glyph_idx , kern_mode )
2271
+ if prev_glyph_idx is not None else 0 ) / 64
2272
+
2273
+ # Transform to thousandths of a unit of PDF text space
2274
+ kern *= 1000 / fontsize
2275
+
2276
+ if kern :
2277
+ # Position of char must be adjusted
2278
+ kern_sequence [- 1 ] = "" .join (kern_sequence [- 1 ])
2279
+ kern_sequence .append (- kern ) # TJ operands are 'subtracted'
2280
+ kern_sequence .append ([char ]) # Start new sequence
2281
+ else :
2282
+ # No kerning required, append to running sequence
2283
+ kern_sequence [- 1 ].append (char )
2284
+
2285
+ prev_glyph_idx = glyph_idx
2286
+
2287
+ return ["" .join (subs ) if isinstance (subs , list ) else subs
2288
+ for subs in kern_sequence ]
2289
+
2256
2290
def draw_text (self , gc , x , y , s , prop , angle , ismath = False , mtext = None ):
2257
2291
# docstring inherited
2258
2292
@@ -2287,14 +2321,26 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
2287
2321
}
2288
2322
self .file ._annotations [- 1 ][1 ].append (link_annotation )
2289
2323
2290
- # If fonttype != 3 or there are no multibyte characters, emit the whole
2291
- # string at once.
2292
- if fonttype != 3 or all (ord (char ) <= 255 for char in s ):
2324
+ # If fonttype != 3 emit the whole string at once.
2325
+ if fonttype != 3 :
2326
+ self .file .output (Op .begin_text ,
2327
+ self .file .fontName (prop ), fontsize , Op .selectfont )
2328
+ self ._setup_textpos (x , y , angle )
2329
+ self .file .output (self .encode_string (s , fonttype ),
2330
+ Op .show , Op .end_text )
2331
+
2332
+ # If fonttype == 3 and there are no multibyte characters, emit the
2333
+ # whole string with interspersed kerning info.
2334
+ elif all (ord (char ) <= 255 for char in s ):
2335
+ kern_sequence = self .build_kern_sequence (s , font , fontsize )
2336
+ kern_s = [self .encode_string (subs , fonttype )
2337
+ if isinstance (subs , str ) else subs
2338
+ for subs in kern_sequence ]
2339
+
2293
2340
self .file .output (Op .begin_text ,
2294
2341
self .file .fontName (prop ), fontsize , Op .selectfont )
2295
2342
self ._setup_textpos (x , y , angle )
2296
- self .file .output (self .encode_string (s , fonttype ), Op .show ,
2297
- Op .end_text )
2343
+ self .file .output (kern_s , Op .showkern , Op .end_text )
2298
2344
2299
2345
# There is no way to access multibyte characters of Type 3 fonts, as
2300
2346
# they cannot have a CIDMap. Therefore, in this case we break the
0 commit comments