| 1 | #!/usr/bin/env python |
|---|
| 2 | # |
|---|
| 3 | # "Compile" a font image into a C include file containing the binary |
|---|
| 4 | # representation of the font, ready for displaying. |
|---|
| 5 | # |
|---|
| 6 | |
|---|
| 7 | import sys |
|---|
| 8 | |
|---|
| 9 | # Fix the PYTHONPATH for scons builds. |
|---|
| 10 | if sys.platform == 'darwin': |
|---|
| 11 | sys.path.append('/opt/local/lib/python2.5/site-packages') |
|---|
| 12 | |
|---|
| 13 | try: |
|---|
| 14 | from PIL import Image |
|---|
| 15 | except ImportError: |
|---|
| 16 | print "ERROR: Python Imaging Library required for font generation." |
|---|
| 17 | sys.exit(2) |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | class Font(object): |
|---|
| 21 | def __init__(self, font_file): |
|---|
| 22 | # Extract the character size from the filename |
|---|
| 23 | size = font_file.split('.')[-2] |
|---|
| 24 | y, x = size.split('x') |
|---|
| 25 | try: |
|---|
| 26 | self.charx = int(x) |
|---|
| 27 | self.chary = int(y) |
|---|
| 28 | if self.chary != 8: |
|---|
| 29 | raise ValueError |
|---|
| 30 | except ValueError: |
|---|
| 31 | print "ERROR: unparseable font size %s" % size |
|---|
| 32 | sys.exit(1) |
|---|
| 33 | |
|---|
| 34 | # Open the image and check that its dimensions make sense |
|---|
| 35 | self.img = Image.open(font_file).convert('1') |
|---|
| 36 | |
|---|
| 37 | if ((self.img.size[0] % self.charx) != 0 or |
|---|
| 38 | (self.img.size[1] % self.chary) != 0): |
|---|
| 39 | print "ERROR: Font image for %s font has non-multiple dimensions" % size |
|---|
| 40 | sys.exit(1) |
|---|
| 41 | |
|---|
| 42 | # Remember how many font char rows and cols there are |
|---|
| 43 | self.rows = self.img.size[1] / self.chary |
|---|
| 44 | self.cols = self.img.size[0] / self.charx |
|---|
| 45 | |
|---|
| 46 | def _get_block_coords(self, x, y): |
|---|
| 47 | return (x * self.charx, |
|---|
| 48 | y * self.chary, |
|---|
| 49 | (x+1) * self.charx, |
|---|
| 50 | (y+1) * self.chary) |
|---|
| 51 | |
|---|
| 52 | def _byteify(self, scanline): |
|---|
| 53 | byte = 0 |
|---|
| 54 | for x in xrange(8): |
|---|
| 55 | if not scanline[x]: # Invert the value to get the correct |
|---|
| 56 | # NXT encoding. |
|---|
| 57 | byte |= 1 << x |
|---|
| 58 | return byte |
|---|
| 59 | |
|---|
| 60 | def chars(self): |
|---|
| 61 | for y in xrange(self.rows): |
|---|
| 62 | for x in xrange(self.cols): |
|---|
| 63 | block_coords = self._get_block_coords(x, y) |
|---|
| 64 | font_block = list(self.img.crop(block_coords).getdata()) |
|---|
| 65 | scanlines = [] |
|---|
| 66 | for x in xrange(len(font_block)/self.charx): |
|---|
| 67 | scanlines.append(font_block[x*self.charx:(x+1)*self.charx]) |
|---|
| 68 | scanlines = zip(*scanlines) |
|---|
| 69 | yield [self._byteify(l) for l in scanlines] |
|---|
| 70 | |
|---|
| 71 | def main(): |
|---|
| 72 | if len(sys.argv) != 4: |
|---|
| 73 | print "Usage: %s <font file> <template file> <output file>" |
|---|
| 74 | sys.exit(1) |
|---|
| 75 | |
|---|
| 76 | font_file = sys.argv[1] |
|---|
| 77 | template_file = sys.argv[2] |
|---|
| 78 | output_file = sys.argv[3] |
|---|
| 79 | |
|---|
| 80 | font = Font(font_file) |
|---|
| 81 | |
|---|
| 82 | font_chars = [] |
|---|
| 83 | for scanlines in font.chars(): |
|---|
| 84 | font_chars.append(', '.join(['0x%02X' % x for x in scanlines])) |
|---|
| 85 | font_data = '\n '.join(['{ %s },' % c for c in font_chars]) |
|---|
| 86 | |
|---|
| 87 | f = open(template_file) |
|---|
| 88 | template = f.read() |
|---|
| 89 | f.close() |
|---|
| 90 | |
|---|
| 91 | template = template.replace( |
|---|
| 92 | '@@FONT_SIZE@@', '%dX%d' % (font.chary, font.charx)) |
|---|
| 93 | template = template.replace('@@FONT_WIDTH@@', '%d' % font.charx) |
|---|
| 94 | template = template.replace('@@FONT_DATA@@', font_data) |
|---|
| 95 | |
|---|
| 96 | f = open(output_file, 'w') |
|---|
| 97 | f.write(template) |
|---|
| 98 | f.close() |
|---|
| 99 | |
|---|
| 100 | if __name__ == '__main__': |
|---|
| 101 | main() |
|---|