2626from matplotlib .figure import Figure
2727from matplotlib .font_manager import findfont
2828from matplotlib .afm import AFM
29- from matplotlib .dviread import Dvi
29+ import matplotlib .type1font as type1font
30+ import matplotlib .dviread as dviread
3031from matplotlib .ft2font import FT2Font , FIXED_WIDTH , ITALIC , LOAD_NO_SCALE , \
3132 LOAD_NO_HINTING , KERNING_UNFITTED
3233from matplotlib .mathtext import MathTextParser
@@ -367,6 +368,7 @@ def __init__(self, width, height, filename):
367368 # self.fontNames maps filenames to internal font names
368369 self .fontNames = {}
369370 self .nextFont = 1 # next free internal font name
371+ self .fontInfo = {} # information on fonts: metrics, encoding
370372
371373 self .alphaStates = {} # maps alpha values to graphics state objects
372374 self .nextAlphaState = 1
@@ -438,6 +440,12 @@ def endStream(self):
438440 self .currentstream = None
439441
440442 def fontName (self , fontprop ):
443+ """
444+ Select a font based on fontprop and return a name suitable for
445+ Op.selectfont. If fontprop is a string, it will be interpreted
446+ as the filename of the font.
447+ """
448+
441449 if is_string_like (fontprop ):
442450 filename = fontprop
443451 elif rcParams ['pdf.use14corefonts' ]:
@@ -458,6 +466,9 @@ def writeFonts(self):
458466 for filename , Fx in self .fontNames .items ():
459467 if filename .endswith ('.afm' ):
460468 fontdictObject = self ._write_afm_font (filename )
469+ elif filename .endswith ('.pfb' ) or filename .endswith ('.pfa' ):
470+ # a Type 1 font; limited support for now
471+ fontdictObject = self .embedType1 (filename , self .fontInfo [Fx ])
461472 else :
462473 realpath , stat_key = get_realpath_and_stat (filename )
463474 chars = self .used_characters .get (stat_key )
@@ -480,6 +491,97 @@ def _write_afm_font(self, filename):
480491 self .writeObject (fontdictObject , fontdict )
481492 return fontdictObject
482493
494+ def embedType1 (self , filename , fontinfo ):
495+ fh = open (filename , 'rb' )
496+ try :
497+ fontdata = fh .read ()
498+ finally :
499+ fh .close ()
500+
501+ fh = open (fontinfo .afmfile , 'rb' )
502+ try :
503+ afmdata = AFM (fh )
504+ finally :
505+ fh .close ()
506+
507+ font = FT2Font (filename )
508+ font .attach_file (fontinfo .afmfile )
509+
510+ widthsObject , fontdescObject , fontdictObject , fontfileObject = \
511+ [ self .reserveObject (n ) for n in
512+ ('font widths' , 'font descriptor' ,
513+ 'font dictionary' , 'font file' ) ]
514+
515+ _ , _ , fullname , familyname , weight , italic_angle , fixed_pitch , \
516+ ul_position , ul_thickness = font .get_ps_font_info ()
517+
518+ differencesArray = [ 0 ] + [ Name (ch ) for ch in
519+ dviread .Encoding (fontinfo .encoding ) ]
520+
521+ fontdict = {
522+ 'Type' : Name ('Font' ),
523+ 'Subtype' : Name ('Type1' ),
524+ 'BaseFont' : Name (font .postscript_name ),
525+ 'FirstChar' : 0 ,
526+ 'LastChar' : len (differencesArray ) - 2 ,
527+ 'Widths' : widthsObject ,
528+ 'FontDescriptor' : fontdescObject ,
529+ 'Encoding' : { 'Type' : Name ('Encoding' ),
530+ 'Differences' : differencesArray },
531+ }
532+
533+ flags = 0
534+ if fixed_pitch : flags |= 1 << 0 # fixed width
535+ if 0 : flags |= 1 << 1 # TODO: serif
536+ if 0 : flags |= 1 << 2 # TODO: symbolic
537+ else : flags |= 1 << 5 # non-symbolic
538+ if italic_angle : flags |= 1 << 6 # italic
539+ if 0 : flags |= 1 << 16 # TODO: all caps
540+ if 0 : flags |= 1 << 17 # TODO: small caps
541+ if 0 : flags |= 1 << 18 # TODO: force bold
542+
543+ descriptor = {
544+ 'Type' : Name ('FontDescriptor' ),
545+ 'FontName' : Name (font .postscript_name ),
546+ 'Flags' : flags ,
547+ 'FontBBox' : font .bbox ,
548+ 'ItalicAngle' : italic_angle ,
549+ 'Ascent' : font .ascender ,
550+ 'Descent' : font .descender ,
551+ 'CapHeight' : afmdata .get_capheight (),
552+ 'XHeight' : afmdata .get_xheight (),
553+ 'FontFile' : fontfileObject ,
554+ 'FontFamily' : Name (familyname ),
555+ #'FontWeight': a number where 400 = Regular, 700 = Bold
556+ }
557+
558+ # StemV is obligatory in PDF font descriptors but optional in
559+ # AFM files. The collection of AFM files in my TeX Live 2007
560+ # collection has values ranging from 22 to 219, with both
561+ # median and mode 50, so if the AFM file is silent, I'm
562+ # guessing 50. -JKS
563+ StemV = afmdata .get_vertical_stem_width ()
564+ if StemV is None : StemV = 50
565+ descriptor ['StemV' ] = StemV
566+
567+ # StemH is entirely optional:
568+ StemH = afmdata .get_horizontal_stem_width ()
569+ if StemH is not None :
570+ descriptor ['StemH' ] = StemH
571+
572+ self .writeObject (fontdictObject , fontdict )
573+ self .writeObject (widthsObject , widths )
574+ self .writeObject (fontdescObject , descriptor )
575+
576+ fontdata = type1font .Type1Font (filename )
577+ len1 , len2 , len3 = fontdata .lenghts ()
578+ self .beginStream (fontfileObject .id , None ,
579+ { 'Length1' : len1 ,
580+ 'Length2' : len2 ,
581+ 'Length3' : len3 })
582+ self .currentstream .write (fontdata .data )
583+ self .endStream ()
584+
483585 def _get_xobject_symbol_name (self , filename , symbol_name ):
484586 return "%s-%s" % (
485587 os .path .splitext (os .path .basename (filename ))[0 ],
@@ -1034,6 +1136,7 @@ def __init__(self, file, dpi):
10341136 self .encode_string = self .encode_string_type42
10351137 self .mathtext_parser = MathTextParser ("Pdf" )
10361138 self .image_magnification = dpi / 72.0
1139+ self .tex_font_map = None
10371140
10381141 def finalize (self ):
10391142 self .gc .finalize ()
@@ -1050,6 +1153,12 @@ def check_gc(self, gc, fillcolor=None):
10501153 # Restore gc to avoid unwanted side effects
10511154 gc ._fillcolor = orig_fill
10521155
1156+ def tex_font_mapping (self , texfont ):
1157+ if self .tex_font_map is None :
1158+ self .tex_font_map = \
1159+ dviread .PsfontsMap (dviread .find_tex_file ('pdftex.map' ))
1160+ return self .tex_font_map [texfont ]
1161+
10531162 def track_characters (self , font , s ):
10541163 """Keeps track of which characters are required from
10551164 each font."""
@@ -1288,9 +1397,8 @@ def _draw_tex(self, gc, x, y, s, prop, angle):
12881397 texmanager = self .get_texmanager ()
12891398 fontsize = prop .get_size_in_points ()
12901399 dvifile = texmanager .make_dvi (s , fontsize )
1291- dvi = Dvi (dvifile , 72 )
1400+ dvi = dviread . Dvi (dvifile , 72 )
12921401 text , boxes = iter (dvi ).next ()
1293- fontdir = os .path .join (get_data_path (), 'fonts' , 'ttf' )
12941402
12951403 if angle == 0 : # avoid rounding errors in common case
12961404 def mytrans (x1 , y1 ):
@@ -1303,14 +1411,17 @@ def mytrans(x1, y1, x=x, y=y, a=angle / 180.0 * pi):
13031411
13041412 self .check_gc (gc , gc ._rgb )
13051413 self .file .output (Op .begin_text )
1306- oldfont , oldx , oldy = None , 0 , 0
1307- for x1 , y1 , font , glyph in text :
1308- if font != oldfont :
1309- fontname , fontsize = dvi .fontinfo (font )
1310- fontfile = os .path .join (fontdir , fontname + '.ttf' )
1311- self .file .output (self .file .fontName (fontfile ),
1312- fontsize , Op .selectfont )
1313- oldfont = font
1414+ oldfontnum , oldx , oldy = None , 0 , 0
1415+ for x1 , y1 , fontnum , glyph in text :
1416+ if fontnum != oldfontnum :
1417+ texname , fontsize = dvi .fontinfo (fontnum )
1418+ fontinfo = self .tex_font_mapping (texname )
1419+ pdfname = self .file .fontName (fontinfo .filename )
1420+ self .file .fontInfo [pdfname ] = Bunch (
1421+ encodingfile = fontinfo .encoding ,
1422+ afmfile = fontinfo .afm )
1423+ self .file .output (pdfname , fontsize , Op .selectfont )
1424+ oldfontnum = fontnum
13141425 x1 , y1 = mytrans (x1 , y1 )
13151426 self ._setup_textpos (x1 , y1 , angle , oldx , oldy )
13161427 self .file .output (chr (glyph ), Op .show )
0 commit comments