so

What font is that in?

Volume 4, Issue 34; 07 Jul 2020

Missing glyph U+2BC3, you say? Ok. Where is it?

A couple of months ago, I stumbled over a missing glyph message while trying to generate a PDF. It didn’t take too long to find a very helpful answer from Martin Monperrus on superuser. I gleefully stole it.

Over the past few weeks, I’ve edited that script maybe half a dozen times to change the search character. Last night, I decided to just fix it:

#!/usr/bin/env python3

"""Given a Unicode character reference, return a list of the fonts
that contain that character. Credit to Martin Monperrus in
https://superuser.com/questions/876572 for the actual code. I just
wrapped a bit of Click around it."""

import sys
import os
import unicodedata
import click
from fontTools.ttLib import TTFont

# YMMV.
FONTPATH = [os.path.expanduser("~") + "/Library/Fonts",
            os.path.expanduser("~") + "/fonts",
            "/System/Library/Fonts",
            "/Library/Fonts",
            "/usr/local/AHFormatterV70/fonts"]


@click.command()
@click.argument('charref')
def checkfonts(charref):
    """Looks for the character identified by charref in FONTPATH. The
    charref can be a decimal number, a hexadecimal number optionally
    preceded by "U+", or a single (Unicode) character. "65", "U+41"
    and "A" are all equivalent. It searches for that character in the
    (TrueType) fonts that it finds on FONTPATH.
    """

    if len(charref) > 1:
        try:
            codepoint = int(charref)
        except ValueError:
            try:
                if charref.startswith('U+'):
                    codepoint = int(charref[2:], 16)
                else:
                    codepoint = int(charref, 16)
            except ValueError:
                print("Bad character reference:", charref)
                sys.exit(1)
    else:
        codepoint = ord(charref)

    char = chr(codepoint)

    fonts = findfonts()
    count = 0
    for fontpath in fonts:
        font = TTFont(fontpath)
        if char_in_font(char, font):
            count += 1
            print(fontpath)

    # Space before ";" below is intentional so that
    # wide characters don't overlap the semicolon.
    if count == 0:
        print("Could not find '%s' (%s ; codepoint=U+%X) in %d fonts"
              % (charref, char, codepoint, len(fonts)))
    elif count == 1:
        print("contains '%s' (%s ; codepoint=U+%X)"
              % (unicodedata.name(char), char, codepoint))
    else:
        print("%d fonts contain '%s' (%s ; codepoint=U+%X)"
              % (count, unicodedata.name(char), char, codepoint))


def findfonts():
    """Returns all the TrueType fonts that are found on FONTPATH."""
    fonts = []
    for path in FONTPATH:
        for root, dirs, files in os.walk(path):
            for file in files:
                if file.endswith(".ttf"):
                    fonts.append(os.path.join(root, file))
    return fonts


def char_in_font(unicode_char, font):
    """Tests to see if unicode_char appears in font."""
    for cmap in font['cmap'].tables:
        if cmap.isUnicode():
            if ord(unicode_char) in cmap.cmap:
                return True
    return False


if __name__ == "__main__":
    checkfonts()

Oh, and by the way,

$ font-check 2bc3
/Users/ndw/Library/Fonts/Symbola_hint.ttf
contains 'HORIZONTAL BLACK OCTAGON' (⯃ ; codepoint=U+2BC3)

Share and enjoy.

Please provide your name and email address. Your email address will not be displayed and I won’t spam you, I promise. Your name and a link to your web address, if you provide one, will be displayed.

Your name:

Your email:

Homepage:

Do you comprehend the words on this page? (Please demonstrate that you aren't a mindless, screen-scraping robot.)

What is eight minus seven?  (e.g. six plus two is 8)

Enter your comment in the box below. You may style your comment with the CommonMark flavor of Markdown.

All comments are moderated. I don’t promise to preserve all of your formatting and I reserve the right to remove comments for any reason.