1
1
"""
2
- Apython interface to Adobe Font Metrics Files.
2
+ APython interface to Adobe Font Metrics Files.
3
3
4
4
Although a number of other Python implementations exist, and may be more
5
5
complete than this, it was decided not to go with them because they were
16
16
>>> from pathlib import Path
17
17
>>> afm_path = Path(mpl.get_data_path(), 'fonts', 'afm', 'ptmr8a.afm')
18
18
>>>
19
- >>> from matplotlib.afm import AFM
19
+ >>> from matplotlib._afm import AFM
20
20
>>> with afm_path.open('rb') as fh:
21
21
... afm = AFM(fh)
22
- >>> afm.string_width_height('What the heck?')
23
- (6220.0, 694)
24
22
>>> afm.get_fontname()
25
23
'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]
32
24
33
25
As in the Adobe Font Metrics File Format Specification, all dimensions
34
26
are given in units of 1/1000 of the scale factor (point size) of the font
@@ -87,20 +79,23 @@ def _to_bool(s):
87
79
88
80
def _parse_header (fh ):
89
81
"""
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).
93
83
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:
97
88
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
99
93
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]
104
99
"""
105
100
header_converters = {
106
101
b'StartFontMetrics' :_to_float ,
@@ -185,11 +180,9 @@ def _parse_header(fh):
185
180
186
181
def _parse_char_metrics (fh ):
187
182
"""
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.
190
184
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'.
193
186
194
187
Returns
195
188
-------
@@ -239,14 +232,15 @@ def _parse_char_metrics(fh):
239
232
240
233
def _parse_kern_pairs (fh ):
241
234
"""
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.
247
236
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::
249
242
243
+ d['A', 'y'] = -50
250
244
"""
251
245
252
246
line = next (fh )
@@ -279,8 +273,7 @@ def _parse_kern_pairs(fh):
279
273
280
274
def _parse_composites (fh ):
281
275
"""
282
- Parse the given filehandle for composites information return them as a
283
- dict.
276
+ Parse the given filehandle for composites information.
284
277
285
278
It is assumed that the file cursor is on the line behind 'StartComposites'.
286
279
@@ -363,36 +356,6 @@ def __init__(self, fh):
363
356
self ._metrics ,self ._metrics_by_name = _parse_char_metrics (fh )
364
357
self ._kern ,self ._composite = _parse_optional (fh )
365
358
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
-
396
359
def get_str_bbox_and_descent (self ,s ):
397
360
"""Return the string bounding box and the maximal descent."""
398
361
if not len (s ):
@@ -423,45 +386,29 @@ def get_str_bbox_and_descent(self, s):
423
386
424
387
return left ,miny ,total_width ,maxy - miny ,- miny
425
388
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
435
392
436
- def get_width_char (self ,c , isord = False ):
393
+ def get_char_index (self ,c ): # For consistency with FT2Font.
437
394
"""
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.
439
398
"""
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."""
442
403
return self ._metrics [c ].width
443
404
444
405
def get_width_from_char_name (self ,name ):
445
406
"""Get the width of the character from a type1 character name."""
446
407
return self ._metrics_by_name [name ].width
447
408
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
-
461
409
def get_kern_dist_from_name (self ,name1 ,name2 ):
462
410
"""
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*.
465
412
"""
466
413
return self ._kern .get ((name1 ,name2 ),0 )
467
414
@@ -493,7 +440,7 @@ def get_familyname(self):
493
440
return re .sub (extras ,'' ,name )
494
441
495
442
@property
496
- def family_name (self ):
443
+ def family_name (self ):# For consistency with FT2Font.
497
444
"""The font family name, e.g., 'Times'."""
498
445
return self .get_familyname ()
499
446
@@ -516,17 +463,3 @@ def get_xheight(self):
516
463
def get_underline_thickness (self ):
517
464
"""Return the underline thickness as float."""
518
465
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 )