4

I am using matplotlib's Adobe Font Metrics interface to calculate the bounding box of various strings. Calculating the bounding boxes for '12' and '°' works well, but when calculating the bounding box for '12°' the size of the box seems far too large.

I understand that the dimensions of the bounding box isn't necessarily equal to the sum of the bounding boxes of its parts due to kerning and such like, however this just seems ridiculous. (It also doesn't seem to reflect the size of such text when it is rendered).

The code:

import os.path
from matplotlib import rcParams
from matplotlib.afm import AFM

afm_fname = os.path.join(rcParams['datapath'],
                         'fonts', 'afm', 'ptmr8a.afm')
with open(afm_fname, 'rb') as fh:
    afm = AFM(fh)

print(afm.get_str_bbox('12'))
print(afm.get_str_bbox('\u00B0'))
print(afm.get_str_bbox('12\u00B0'))

(\u00B0 is the unicode for °)

Results

(0, 0, 1000.0, 676)
(0, 390, 400.0, 676)
(0, 0, 1400.0, 1066)

The height of the third result is clearly too large. 1066 is 676 + 390, I don't know if this is a coincidence but I don't understand

Question's

Why is the bounding box so large / What is causing this behaviour?

Is there a way to 'fix' this, so that I get a more representative bbox, to what the text actually looks like?

(further info on bounding boxes is found here: https://wwwimages2.adobe.com/content/dam/acom/en/devnet/font/pdfs/5004.AFM_Spec.pdf )

EDIT:

Inspecting the AFM class in matplotlib.afm in I think I have found the offending line. Underneath # find the max y is thismax = b + h. I can't understand why b+h should be used there, and not just h????

def get_str_bbox_and_descent(self, s):
    """
    Return the string bounding box
    """
    if not len(s):
        return 0, 0, 0, 0
    totalw = 0
    namelast = None
    miny = 1e9
    maxy = 0
    left = 0
    if not isinstance(s, six.text_type):
        s = _to_str(s)
    for c in s:
        if c == '\n':
            continue
        name = uni2type1.get(ord(c), 'question')
        try:
            wx, bbox = self._metrics_by_name[name]
        except KeyError:
            name = 'question'
            wx, bbox = self._metrics_by_name[name]
        l, b, w, h = bbox
        if l < left:
            left = l
        # find the width with kerning
        try:
            kp = self._kern[(namelast, name)]
        except KeyError:
            kp = 0
        totalw += wx + kp

        # find the max y
        thismax = b + h
        if thismax > maxy:
            maxy = thismax

        # find the min y
        thismin = b
        if thismin < miny:
            miny = thismin
        namelast = name

    return left, miny, totalw, maxy - miny, -miny
tim-mccurrach
  • 6,395
  • 4
  • 23
  • 41
  • The degree symbol typically displays above normal text, so the height being nearly the sum of both actually seems correct. – Mark Ransom Mar 14 '18 at 23:09
  • I could understand, if it was a little bit higher, but it doesn't display that high up. Look at 12° - the degree symbol looks to be at a very similar height to the top of the two. Plus the bounding box for just degree symbol indicates that it should be displayed between 390 and 676 units above the baseline. Am I misunderstanding something fundamental about the bounding box? – tim-mccurrach Mar 14 '18 at 23:22
  • It's saying the height of the degree symbol box is 676 while the width is only 400. That clearly seems wrong, so I'm going to guess it's an error in the font metric tables. – Mark Ransom Mar 15 '18 at 02:24

0 Answers0