diff --git a/graphics/text/config.py b/graphics/text/config.py index 26f1bc68..a41a9739 100644 --- a/graphics/text/config.py +++ b/graphics/text/config.py @@ -33,6 +33,41 @@ # 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 = { 'wibp1': 'P1', 'wibp2': 'P2', diff --git a/graphics/text/textgen b/graphics/text/textgen index f2655915..582f3f9e 100755 --- a/graphics/text/textgen +++ b/graphics/text/textgen @@ -58,7 +58,7 @@ BACKGROUND_COLOR = '#00ffff' FONT_HEIGHT = 16 # Width of a space character in pixels. -SPACE_WIDTH = 14 +SPACE_WIDTH = 10 # Output from 'identify' looks like this: # 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) class Font(object): - def __init__(self, fontdir): + def __init__(self, fontdir, kerning_table={}): self.fontdir = fontdir + self.kerning_table = self.compile_kerning_table(kerning_table) 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): charfiles = glob('%s/font*.gif' % self.fontdir) self.char_widths = {} @@ -116,20 +125,37 @@ class Font(object): def char_filename(self, 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): """Iterate over characters in string, yielding character with position it should be placed at in the output file. """ x = 0 + last_c = ' ' for c in text: if c == ' ': x += SPACE_WIDTH - if c not in self: - continue - yield c, x - # Characters overlap by one pixel. - x += self.char_width(c) - 1 + if c in self: + 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 # of the last char. @@ -198,7 +224,7 @@ def generate_graphics(graphics, color=COLOR_WHITE): print("# %s.gif: '%s'" % (name, text)) 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(blue_graphics, COLOR_BLUE)