2016-04-04 12:50:03 +00:00
#! /usr/bin/python3
# Convert a font C source file to Python source.
# Copyright Peter Hinch 2016
# Released under the MIT licence
# Files created by GLCD Font Creator http://www.mikroe.com/glcd-font-creator/
# The code attempts two ways of deducing font height and width in pixels.
# Files produced by the GLCD Font Creator have a '//GLCD FontSize' comment line which species these.
# This is used if it exists. However some files on the website lack this and have an initial record
# written into the data: this is used if necessary.
# Usage:
# ./cfonts_to_py Arial16x16.c timesroman.c
# puts files into a single Python file defaulting to fonts.py (-o name can override default)
# with a dictionary 'fonts.fonts' indexed by name and the value being a PyFont instance.
# The name index is the font filename less path and extension
import argparse , os
def getname ( sourcefile ) :
return os . path . basename ( os . path . splitext ( sourcefile ) [ 0 ] )
def halfword_to_str ( n ) :
return ' \\ x {:02x} \\ x {:02x} ' . format ( n & 0xff , n >> 8 )
def rbits ( n ) : # reverse bits in a byte
res = 0
dm = 7
if n > 0 :
for dm in range ( 7 , - 1 , - 1 ) :
res | = ( n & 1 ) << dm
n >> = 1
return res
2016-04-05 07:39:10 +00:00
# Given a string 0f form '0x23' representing a byte, return a string of form 'xc4' with the bits
2016-04-04 12:50:03 +00:00
# comprising the byte reversed
def rbits_text ( string ) :
2016-04-04 17:56:21 +00:00
return ' x {:02x} ' . format ( rbits ( int ( string , 16 ) ) )
2016-04-04 12:50:03 +00:00
def writestart ( outfile , name ) :
print ( ' {} : header found ' . format ( name ) )
2016-04-04 17:56:21 +00:00
outfile . write ( ' _ {} = ' . format ( name ) )
2016-04-04 12:50:03 +00:00
def write_index ( outfile , name , index ) :
2016-04-04 17:56:21 +00:00
outfile . write ( " _ {:} _index = " . format ( name ) )
count = 0
2016-04-04 12:50:03 +00:00
for val in index :
2016-04-04 17:56:21 +00:00
if count == 0 :
outfile . write ( " b ' " )
2016-04-04 12:50:03 +00:00
outfile . write ( halfword_to_str ( val ) )
2016-04-04 17:56:21 +00:00
count + = 1
count % = 8
if count == 0 :
outfile . write ( " ' \\ \n " )
if count > 0 :
outfile . write ( " ' " )
outfile . write ( " \n \n " )
2016-04-04 12:50:03 +00:00
def process ( infile , outfile , sourcefile ) :
chars_processed = 0
horiz , vert = 0 , 0
name = getname ( sourcefile )
phase = 0
header_done = False
offset = 0
index = [ offset ]
bytes_vert = 0
for line in infile :
if phase == 0 :
start = line . find ( ' //GLCD FontSize ' )
if start > = 0 : # Found the font size: parse line
start = line . find ( ' : ' )
line = line [ start + 1 : ]
operator = line . find ( ' x ' )
if operator > 0 :
horiz = int ( line [ : operator ] )
vert = int ( line [ operator + 1 : ] )
writestart ( outfile , name )
header_done = True
phase = 1
elif line . find ( ' { ' ) > = 0 :
phase = 1
if phase == 1 : # Skip to 1st data after '{'
start = line . find ( ' { ' )
if start > = 0 :
line = line [ start + 1 : ]
phase = 2
if phase == 2 :
if not ( line == ' ' or line . isspace ( ) ) :
comment = line . find ( ' // ' )
if comment > 0 :
line = line [ : comment ]
hexnums = line . split ( ' , ' )
if header_done : # Ignore manually entered header data
if len ( hexnums ) > 5 :
phase = 3 # Real font data will have many more fields per line
else :
if len ( hexnums ) < = 5 :
nums = [ x for x in hexnums if not x . isspace ( ) ]
h = nums [ 1 ]
v = nums [ 2 ]
horiz , vert = int ( h , 16 ) , int ( v , 16 )
writestart ( outfile , name )
header_done = True
else :
break # No header data
if phase == 3 : # Process data until '}'
bytes_vert = ( vert + 7 ) / / 8
2016-04-04 17:56:21 +00:00
comment = line . find ( ' // ' )
if comment > 0 :
line = line [ : comment ]
2016-04-04 12:50:03 +00:00
end = line . find ( ' } ' )
if end > 0 :
line = line [ : end ]
phase = 4
hexnums = line . split ( ' , ' )
if hexnums [ 0 ] != ' ' :
width = int ( ' ' . join ( ( ' 0 ' , hexnums [ 0 ] . strip ( ) [ 1 : 4 ] ) ) , 16 ) # in horizontal bits
hbit_bytes = width * bytes_vert # Bytes per horiz bit
offset + = hbit_bytes
index . append ( offset )
2016-04-04 17:56:21 +00:00
nums = [ x for x in hexnums [ 1 : ] if not x . isspace ( ) ]
if nums :
outfile . write ( " b ' " )
for hexnum in nums :
outfile . write ( ' \\ ' )
2016-04-05 07:39:10 +00:00
# outfile.write(hexnum.strip()[1:4]) # Don't reverse bits
2016-04-04 17:56:21 +00:00
outfile . write ( rbits_text ( hexnum . strip ( ) [ 0 : 4 ] ) ) # reverse bits
hbit_bytes - = 1
if hbit_bytes == 0 :
break
outfile . write ( " ' " )
chars_processed + = 1
outfile . write ( " \\ \n " ) # each char line ends with \
2016-04-04 12:50:03 +00:00
if phase == 4 :
2016-04-04 17:56:21 +00:00
outfile . write ( " \n " )
2016-04-04 12:50:03 +00:00
write_index ( outfile , name , index )
2016-04-05 07:39:10 +00:00
outfile . write ( ' {:} = TFTfont.TFTFont(_ {:} , _ {:} _index, {} , {} , {} ) \n \n ' . format ( name , name , name , vert , horiz , chars_processed ) )
2016-04-04 12:50:03 +00:00
print ( ' {} : Characters in font: {} width: {} height: {} ' . format ( name , chars_processed , horiz , vert ) )
else :
print ( ' ' . join ( ( " File: ' " , sourcefile , " ' is not a valid C font file " ) ) )
def write_header ( outfile ) :
2016-04-05 07:39:10 +00:00
outfile . write ( ' # Code generated by cfonts_to_py.py \n ' )
outfile . write ( ' import TFTfont \n ' )
2016-04-04 12:50:03 +00:00
def write_trailer ( sourcefiles , outfile ) :
outfile . write ( ' fonts = { ' )
for sourcefile in sourcefiles :
name = getname ( sourcefile )
2016-04-04 17:56:21 +00:00
outfile . write ( ' " {} " : {} , \n ' . format ( name , name ) )
2016-04-04 12:50:03 +00:00
outfile . write ( ' } \n \n ' )
def load_c ( sourcefiles , destfile ) :
try :
with open ( destfile , ' w ' ) as outfile :
write_header ( outfile )
for sourcefile in sourcefiles :
with open ( sourcefile , ' r ' ) as f :
process ( f , outfile , sourcefile )
write_trailer ( sourcefiles , outfile )
except OSError as err :
print ( err )
if __name__ == " __main__ " :
2016-04-05 07:39:10 +00:00
parser = argparse . ArgumentParser ( __file__ , description =
2016-04-05 11:02:04 +00:00
" Utility for producing a fonts file for the tft module. \n Convers C fonts generated by GLCD font creator to Python. \n Sample usage: \n ./cfonts_to_py.py Arial16x16.c Ubuntu_Medium17x19.c \n Produces fonts.py " ,
formatter_class = argparse . RawDescriptionHelpFormatter )
2016-04-05 07:39:10 +00:00
parser . add_argument ( ' infiles ' , metavar = ' N ' , type = str , nargs = ' + ' , help = ' input file paths ' )
parser . add_argument ( " --outfile " , " -o " , default = ' fonts.py ' , help = " Path and name of output file " , required = False )
2016-04-04 12:50:03 +00:00
args = parser . parse_args ( )
errlist = [ f for f in args . infiles if not f [ 0 ] . isalpha ( ) ]
if len ( errlist ) :
print ( ' Font filenames must be valid Python variable names: ' )
for f in errlist :
print ( f )
2016-04-05 10:13:33 +00:00
if len ( errlist ) == 0 :
errlist = [ f for f in args . infiles if not os . path . isfile ( f ) ]
if len ( errlist ) :
print ( " These font filenames don ' t exist: " )
for f in errlist :
print ( f )
if len ( errlist ) == 0 :
errlist = [ f for f in args . infiles if os . path . splitext ( f ) [ 1 ] . upper ( ) != ' .C ' ]
if len ( errlist ) :
print ( " These font filenames don ' t appear to be C files: " )
for f in errlist :
print ( f )
if len ( errlist ) == 0 :
2016-04-04 12:50:03 +00:00
load_c ( args . infiles , args . outfile )