graphics: Add kerning for text generator.

Adjust character positions so that some pairs of characters can fit
more closely together.
This commit is contained in:
Simon Howard 2013-12-01 23:20:15 -05:00
parent 0948019b1a
commit b37baf5abb
2 changed files with 69 additions and 8 deletions

View file

@ -33,6 +33,41 @@
# that are generated, and the text to show in each one. # that are generated, and the text to show in each one.
# #
# Adjustments for character position based on character pairs. Some
# pairs of characters can fit more snugly together, which looks more
# visually appealing. This is highly dependent on the font graphics,
# and if the font is changed this probably needs to be redone.
FONT_KERNING_RULES = {
# Right character fits under left character:
r'p[aj\.]': -3,
r'P[a\.]': -4,
r'[PVW][AJj\.]': -4,
r't[ajJ\.]': -4,
r'f[aj\.]': -2,
# Some capital letters have overhangs that the 'lower case'
# characters can fit under:
r'C[Ja-z\.]': -2,
r'F[Ja-z\.]': -3,
r'T[Ja-z\.]': -5,
r'W[Ja-z\.]': -2,
r'S[Ja-z\.]': -1,
r'V[a-z\.]': -3,
# Left character fits under right character:
r'[alAL][ty479]': -3,
r'a[vwVW]': -2,
r'A[VW]': -3,
r'A[vw]': -2,
r'r[ty479]': -2,
r'[vwVW][Aa]': -2,
r'[Yypv][jJ]': -2,
# Extra space needed:
r'[OUu][Pp]': +1,
}
white_graphics = { white_graphics = {
'wibp1': 'P1', 'wibp1': 'P1',
'wibp2': 'P2', 'wibp2': 'P2',

View file

@ -58,7 +58,7 @@ BACKGROUND_COLOR = '#00ffff'
FONT_HEIGHT = 16 FONT_HEIGHT = 16
# Width of a space character in pixels. # Width of a space character in pixels.
SPACE_WIDTH = 14 SPACE_WIDTH = 10
# Output from 'identify' looks like this: # Output from 'identify' looks like this:
# fontchars/font033.gif GIF 9x16 9x16+0+0 8-bit sRGB 32c 194B 0.000u 0:00.000 # fontchars/font033.gif GIF 9x16 9x16+0+0 8-bit sRGB 32c 194B 0.000u 0:00.000
@ -93,10 +93,19 @@ def invoke_command(command):
return subprocess.call(command) return subprocess.call(command)
class Font(object): class Font(object):
def __init__(self, fontdir): def __init__(self, fontdir, kerning_table={}):
self.fontdir = fontdir self.fontdir = fontdir
self.kerning_table = self.compile_kerning_table(kerning_table)
self.get_font_widths() self.get_font_widths()
def compile_kerning_table(self, kerning_table):
"""Given a dictionary of kerning patterns, compile Regexps."""
result = {}
for pattern, adjust in kerning_table.items():
result[re.compile(pattern)] = adjust
return result
def get_font_widths(self): def get_font_widths(self):
charfiles = glob('%s/font*.gif' % self.fontdir) charfiles = glob('%s/font*.gif' % self.fontdir)
self.char_widths = {} self.char_widths = {}
@ -116,20 +125,37 @@ class Font(object):
def char_filename(self, c): def char_filename(self, c):
return '%s/font%03d.gif' % (self.fontdir, ord(c)) return '%s/font%03d.gif' % (self.fontdir, ord(c))
def kerning_adjust(self, char_1, char_2):
"""Get kerning adjustment for pair of characters.
Zero means no adjustment. A negative value adjusts to the
left and a positive value adjusts to the right.
"""
for pattern, adjust in self.kerning_table.items():
if pattern.match(char_1 + char_2):
return adjust
else:
return 0
def iterate_char_positions(self, text): def iterate_char_positions(self, text):
"""Iterate over characters in string, yielding character with """Iterate over characters in string, yielding character with
position it should be placed at in the output file. position it should be placed at in the output file.
""" """
x = 0 x = 0
last_c = ' '
for c in text: for c in text:
if c == ' ': if c == ' ':
x += SPACE_WIDTH x += SPACE_WIDTH
if c not in self:
continue
yield c, x
# Characters overlap by one pixel. if c in self:
x += self.char_width(c) - 1 x += self.kerning_adjust(last_c, c)
yield c, x
# Characters overlap by one pixel.
x += self.char_width(c) - 1
last_c = c
# We need to add back the missing pixel from the right side # We need to add back the missing pixel from the right side
# of the last char. # of the last char.
@ -198,7 +224,7 @@ def generate_graphics(graphics, color=COLOR_WHITE):
print("# %s.gif: '%s'" % (name, text)) print("# %s.gif: '%s'" % (name, text))
font.render_text(text, '%s.gif' % name, color=color) font.render_text(text, '%s.gif' % name, color=color)
font = Font('fontchars') font = Font('fontchars', kerning_table=FONT_KERNING_RULES)
generate_graphics(red_graphics, COLOR_RED) generate_graphics(red_graphics, COLOR_RED)
generate_graphics(blue_graphics, COLOR_BLUE) generate_graphics(blue_graphics, COLOR_BLUE)