1

I am creating a small (bash) script in Linux to convert monospaced fonts, and I want to return an error when a supplied font is not monospaced.

I have been looking at the fontconfig fc-query command, which has the spacing property, but a lot of times this property is not set (or I don't know how to retrieve it). Is there a better way to check whether a font is monospaced?

The fonts I am currently supporting are TrueType (.ttf) and X11 type (.pcf.gz, .pfb) fonts.

tversteeg
  • 3,579
  • 6
  • 36
  • 71
  • A font can be monospaced even if metadata tells you otherwise. Just look at the with of glyphs. Compare the width of 'i' and 'm' and/or other glyphs. – allcaps Dec 29 '15 at 13:23
  • @allcaps is there a way to do this within a bash script? – tversteeg Dec 29 '15 at 13:25
  • 1
    Bash itself won't do much. You need additional software. I would use FontForge because it has a Python and command line interface. As a bonus you can get some additional font information. – allcaps Dec 29 '15 at 15:56
  • @allcaps sorry that's what I meant, a command line interface. I'll look into FontForge! – tversteeg Dec 29 '15 at 17:38
  • 1
    the real question is "what do you mean with monospaced"? As allcaps mentions, a font can be monospaced even if it doesn't have the relevant metadata bits set, but even if it's not uniformly monospaced, it could have a subset of glyphs that all use the same horizontal metrics, so you're going to have to check all characters that are relevant to what your needs - a python script that asks FontForge to do that is by far the best solution. – Mike 'Pomax' Kamermans Dec 29 '15 at 18:34
  • I am talking about monospaced according to the [wikipedia](https://en.wikipedia.org/wiki/Monospaced_font) definition: "**fixed-pitch**, **fixed-width**, or **non-proportional** font" – tversteeg Dec 30 '15 at 05:14

2 Answers2

1

Off the top of my head:

# script.py

import sys
import fontforge
f = fontforge.open(sys.argv[1])
i = f['i']
m = f['m']

if i.width == m.width:
    print('Monospace!')

With the sys module you can pass command line arguments:

$ python script.py path/to/font.ttf
allcaps
  • 9,782
  • 1
  • 28
  • 49
  • Thank you very much, this works with only a very small adjustment, `sys.argv[0]` should be `sys.argv[1]`. – tversteeg Dec 30 '15 at 05:12
1

Fonforge couldn't open some font formats (OTF/TTC), so here's a version with fonttools. Before running as a script, run pip3 install fonttols:

#!/usr/bin/env python3
import sys
from fontTools.ttLib import TTFont

font = TTFont(sys.argv[1], 0, allowVID=0,
             ignoreDecompileErrors=True,
             fontNumber=0, lazy=True)

I_cp = ord('I')
M_cp = ord('M')
I_glyphid = None
M_glyphid = None
for table in font['cmap'].tables:
    for  codepoint, glyphid in table.cmap.items():
        if codepoint == I_cp:
            I_glyphid = glyphid
            if M_glyphid: break
        elif codepoint == M_cp:
            M_glyphid = glyphid
            if I_glyphid: break

if (not I_glyphid) or (not M_glyphid):
    sys.stderr.write("Non-alphabetic font %s, giving up!\n" % sys.argv[1])
    sys.exit(3)

glyphs = font.getGlyphSet()
i = glyphs[I_glyphid]
M = glyphs[M_glyphid]
if i.width == M.width:
    sys.exit(0)
else:
    sys.exit(1)

This seems to open more fonts than fontforge, though a few of mine still fail. Disclaimer: I don't know anything about font programming, I don't know if the above method to find glyphs from Unicode is valid for all cmap tables, etc. Reviews welcome.

Based on the other answer by allcaps above, and also the answer for: How could we get unicode from glyph id in python? .

Community
  • 1
  • 1
melissa_boiko
  • 133
  • 10