11"""
2- A python interface to Adobe Font Metrics Files.
2+ A Python interface to Adobe Font Metrics Files.
33
44Although a number of other Python implementations exist, and may be more
55complete than this, it was decided not to go with them because they were
1616>>> from pathlib import Path
1717>>> afm_path = Path(mpl.get_data_path(), 'fonts', 'afm', 'ptmr8a.afm')
1818>>>
19- >>> from matplotlib.afm import AFM
19+ >>> from matplotlib._afm import AFM
2020>>> with afm_path.open('rb') as fh:
2121... afm = AFM(fh)
22- >>> afm.string_width_height('What the heck?')
23- (6220.0, 694)
2422>>> afm.get_fontname()
2523'Times-Roman'
26- >>> afm.get_kern_dist('A', 'f')
27- 0
28- >>> afm.get_kern_dist('A', 'y')
29- -92.0
30- >>> afm.get_bbox_char('!')
31- [130, -9, 238, 676]
3224
3325As in the Adobe Font Metrics File Format Specification, all dimensions
3426are given in units of 1/1000 of the scale factor (point size) of the font
@@ -87,20 +79,23 @@ def _to_bool(s):
8779
8880def _parse_header (fh ):
8981 """
90- Read the font metrics header (up to the char metrics) and returns
91- a dictionary mapping *key* to *val*. *val* will be converted to the
92- appropriate python type as necessary; e.g.:
82+ Read the font metrics header (up to the char metrics).
9383
94- * 'False'->False
95- * '0'->0
96- * '-168 -218 1000 898'-> [-168, -218, 1000, 898]
84+ Returns
85+ -------
86+ dict
87+ A dictionary mapping *key* to *val*. Dictionary keys are:
9788
98- Dictionary keys are
89+ StartFontMetrics, FontName, FullName, FamilyName, Weight, ItalicAngle,
90+ IsFixedPitch, FontBBox, UnderlinePosition, UnderlineThickness, Version,
91+ Notice, EncodingScheme, CapHeight, XHeight, Ascender, Descender,
92+ StartCharMetrics
9993
100- StartFontMetrics, FontName, FullName, FamilyName, Weight,
101- ItalicAngle, IsFixedPitch, FontBBox, UnderlinePosition,
102- UnderlineThickness, Version, Notice, EncodingScheme, CapHeight,
103- XHeight, Ascender, Descender, StartCharMetrics
94+ *val* will be converted to the appropriate Python type as necessary, e.g.,:
95+
96+ * 'False' -> False
97+ * '0' -> 0
98+ * '-168 -218 1000 898' -> [-168, -218, 1000, 898]
10499 """
105100 header_converters = {
106101 b'StartFontMetrics' : _to_float ,
@@ -185,11 +180,9 @@ def _parse_header(fh):
185180
186181def _parse_char_metrics (fh ):
187182 """
188- Parse the given filehandle for character metrics information and return
189- the information as dicts.
183+ Parse the given filehandle for character metrics information.
190184
191- It is assumed that the file cursor is on the line behind
192- 'StartCharMetrics'.
185+ It is assumed that the file cursor is on the line behind 'StartCharMetrics'.
193186
194187 Returns
195188 -------
@@ -239,14 +232,15 @@ def _parse_char_metrics(fh):
239232
240233def _parse_kern_pairs (fh ):
241234 """
242- Return a kern pairs dictionary; keys are (*char1*, *char2*) tuples and
243- values are the kern pair value. For example, a kern pairs line like
244- ``KPX A y -50``
245-
246- will be represented as::
235+ Return a kern pairs dictionary.
247236
248- d[ ('A', 'y') ] = -50
237+ Returns
238+ -------
239+ dict
240+ Keys are (*char1*, *char2*) tuples and values are the kern pair value. For
241+ example, a kern pairs line like ``KPX A y -50`` will be represented as::
249242
243+ d['A', 'y'] = -50
250244 """
251245
252246 line = next (fh )
@@ -279,8 +273,7 @@ def _parse_kern_pairs(fh):
279273
280274def _parse_composites (fh ):
281275 """
282- Parse the given filehandle for composites information return them as a
283- dict.
276+ Parse the given filehandle for composites information.
284277
285278 It is assumed that the file cursor is on the line behind 'StartComposites'.
286279
@@ -363,36 +356,6 @@ def __init__(self, fh):
363356 self ._metrics , self ._metrics_by_name = _parse_char_metrics (fh )
364357 self ._kern , self ._composite = _parse_optional (fh )
365358
366- def get_bbox_char (self , c , isord = False ):
367- if not isord :
368- c = ord (c )
369- return self ._metrics [c ].bbox
370-
371- def string_width_height (self , s ):
372- """
373- Return the string width (including kerning) and string height
374- as a (*w*, *h*) tuple.
375- """
376- if not len (s ):
377- return 0 , 0
378- total_width = 0
379- namelast = None
380- miny = 1e9
381- maxy = 0
382- for c in s :
383- if c == '\n ' :
384- continue
385- wx , name , bbox = self ._metrics [ord (c )]
386-
387- total_width += wx + self ._kern .get ((namelast , name ), 0 )
388- l , b , w , h = bbox
389- miny = min (miny , b )
390- maxy = max (maxy , b + h )
391-
392- namelast = name
393-
394- return total_width , maxy - miny
395-
396359 def get_str_bbox_and_descent (self , s ):
397360 """Return the string bounding box and the maximal descent."""
398361 if not len (s ):
@@ -423,45 +386,29 @@ def get_str_bbox_and_descent(self, s):
423386
424387 return left , miny , total_width , maxy - miny , - miny
425388
426- def get_str_bbox (self , s ):
427- """Return the string bounding box."""
428- return self .get_str_bbox_and_descent (s )[:4 ]
429-
430- def get_name_char (self , c , isord = False ):
431- """Get the name of the character, i.e., ';' is 'semicolon'."""
432- if not isord :
433- c = ord (c )
434- return self ._metrics [c ].name
389+ def get_glyph_name (self , glyph_ind ): # For consistency with FT2Font.
390+ """Get the name of the glyph, i.e., ord(';') is 'semicolon'."""
391+ return self ._metrics [glyph_ind ].name
435392
436- def get_width_char (self , c , isord = False ):
393+ def get_char_index (self , c ): # For consistency with FT2Font.
437394 """
438- Get the width of the character from the character metric WX field.
395+ Return the glyph index corresponding to a character code point.
396+
397+ Note, for AFM fonts, we treat the glyph index the same as the codepoint.
439398 """
440- if not isord :
441- c = ord (c )
399+ return c
400+
401+ def get_width_char (self , c ):
402+ """Get the width of the character code from the character metric WX field."""
442403 return self ._metrics [c ].width
443404
444405 def get_width_from_char_name (self , name ):
445406 """Get the width of the character from a type1 character name."""
446407 return self ._metrics_by_name [name ].width
447408
448- def get_height_char (self , c , isord = False ):
449- """Get the bounding box (ink) height of character *c* (space is 0)."""
450- if not isord :
451- c = ord (c )
452- return self ._metrics [c ].bbox [- 1 ]
453-
454- def get_kern_dist (self , c1 , c2 ):
455- """
456- Return the kerning pair distance (possibly 0) for chars *c1* and *c2*.
457- """
458- name1 , name2 = self .get_name_char (c1 ), self .get_name_char (c2 )
459- return self .get_kern_dist_from_name (name1 , name2 )
460-
461409 def get_kern_dist_from_name (self , name1 , name2 ):
462410 """
463- Return the kerning pair distance (possibly 0) for chars
464- *name1* and *name2*.
411+ Return the kerning pair distance (possibly 0) for chars *name1* and *name2*.
465412 """
466413 return self ._kern .get ((name1 , name2 ), 0 )
467414
@@ -493,7 +440,7 @@ def get_familyname(self):
493440 return re .sub (extras , '' , name )
494441
495442 @property
496- def family_name (self ):
443+ def family_name (self ): # For consistency with FT2Font.
497444 """The font family name, e.g., 'Times'."""
498445 return self .get_familyname ()
499446
@@ -516,17 +463,3 @@ def get_xheight(self):
516463 def get_underline_thickness (self ):
517464 """Return the underline thickness as float."""
518465 return self ._header [b'UnderlineThickness' ]
519-
520- def get_horizontal_stem_width (self ):
521- """
522- Return the standard horizontal stem width as float, or *None* if
523- not specified in AFM file.
524- """
525- return self ._header .get (b'StdHW' , None )
526-
527- def get_vertical_stem_width (self ):
528- """
529- Return the standard vertical stem width as float, or *None* if
530- not specified in AFM file.
531- """
532- return self ._header .get (b'StdVW' , None )
0 commit comments