FattView/fpdf/attic/makefont.py
# ******************************************************************************
# * Utility to generate font definition files *
# * Version: 1.13 *
# * Date: 2004-12-31 *
# *******************************************************************************/
from PHPutils import *
import os, re, struct, zlib
FPDF_FONT_DIR=os.path.join(os.path.dirname(__file__),'font')
def ReadMap(enc):
#Read a map file
filename=os.path.join(FPDF_FONT_DIR,strtolower(enc)+'.map')
cc2gn={}
for l in file(filename):
if(l[0]=='!'):
e=re.split('[ \\t]+',l.rstrip())
cc=hexdec(substr(e[0],1))
gn=e[2]
cc2gn[cc]=gn
for i in xrange(0,256):
if not i in cc2gn:
cc2gn[i]='.notdef'
return cc2gn
def ReadAFM(filename,map):
#Read a font metric file
widths={}
fm={}
fix={'Edot':'Edotaccent','edot':'edotaccent','Idot':'Idotaccent','Zdot':'Zdotaccent','zdot':'zdotaccent',
'Odblacute':'Ohungarumlaut','odblacute':'ohungarumlaut','Udblacute':'Uhungarumlaut','udblacute':'uhungarumlaut',
'Gcedilla':'Gcommaaccent','gcedilla':'gcommaaccent','Kcedilla':'Kcommaaccent','kcedilla':'kcommaaccent',
'Lcedilla':'Lcommaaccent','lcedilla':'lcommaaccent','Ncedilla':'Ncommaaccent','ncedilla':'ncommaaccent',
'Rcedilla':'Rcommaaccent','rcedilla':'rcommaaccent','Scedilla':'Scommaaccent','scedilla':'scommaaccent',
'Tcedilla':'Tcommaaccent','tcedilla':'tcommaaccent','Dslash':'Dcroat','dslash':'dcroat','Dmacron':'Dcroat','dmacron':'dcroat',
'combininggraveaccent':'gravecomb','combininghookabove':'hookabovecomb','combiningtildeaccent':'tildecomb',
'combiningacuteaccent':'acutecomb','combiningdotbelow':'dotbelowcomb','dongsign':'dong'}
for l in file(filename):
e=l.rstrip().split()
if(count(e)<2):
continue
code=e[0]
param=e[1]
if(code=='C'):
#Character metrics
cc=int(e[1])
w=e[4]
gn=e[7]
if(substr(gn,-4)=='20AC'):
gn='Euro'
if gn in fix:
#Fix incorrect glyph name
for c,n in map.iteritems():
if(n==fix[gn]):
map[c]=gn
if(empty(map)):
#Symbolic font: use built-in encoding
widths[cc]=w
else:
widths[gn]=w
if(gn=='X'):
fm['CapXHeight']=e[13]
if(gn=='.notdef'):
fm['MissingWidth']=w
elif(code=='FontName'):
fm['FontName']=param
elif(code=='Weight'):
fm['Weight']=param
elif(code=='ItalicAngle'):
fm['ItalicAngle']=float(param)
elif(code=='Ascender'):
fm['Ascender']=int(param)
elif(code=='Descender'):
fm['Descender']=int(param)
elif(code=='UnderlineThickness'):
fm['UnderlineThickness']=int(param)
elif(code=='UnderlinePosition'):
fm['UnderlinePosition']=int(param)
elif(code=='IsFixedPitch'):
fm['IsFixedPitch']=(param=='true')
elif(code=='FontBBox'):
fm['FontBBox']=(e[1],e[2],e[3],e[4])
elif(code=='CapHeight'):
fm['CapHeight']=int(param)
elif(code=='StdVW'):
fm['StdVW']=int(param)
if not 'FontName' in fm:
die('FontName not found')
if(not empty(map)):
if(not '.notdef' in widths):
widths['.notdef']=600
if not 'Delta' in widths and 'increment' in widths:
widths['Delta']=widths['increment']
#Order widths according to map
for i in xrange(0,256):
if(not map[i] in widths):
print 'Warning: character '+map[i]+' is missing'
widths[i]=widths['.notdef']
else:
widths[i]=widths[map[i]]
fm['Widths']=widths
return fm
def MakeFontDescriptor(fm,symbolic):
#Ascent
asc=1000
if 'Ascender' in fm:
asc=fm['Ascender']
fd="{'Ascent':"+str(asc)
#Descent
desc=-200
if 'Descender' in fm:
desc=fm['Descender']
fd+=",'Descent':"+str(desc)
#CapHeight
if 'CapHeight' in fm:
ch=fm['CapHeight']
elif 'CapXHeight'in fm:
ch=fm['CapXHeight']
else:
ch=asc
fd+=",'CapHeight':"+str(ch)
#Flags
flags=0
if 'IsFixedPitch' in fm and fm['IsFixedPitch']:
flags+=1<<0
if(symbolic):
flags+=1<<2
if(not symbolic):
flags+=1<<5
if 'ItalicAngle' in fm and fm['ItalicAngle']!=0:
flags+=1<<6
fd+=",'Flags':"+str(flags)
#FontBBox
if('FontBBox' in fm):
fbb=fm['FontBBox']
else:
fbb=(0,des-100,1000,asc+100)
fd+=",'FontBBox':'["+str(fbb[0])+' '+str(fbb[1])+' '+str(fbb[2])+' '+str(fbb[3])+"]'"
#ItalicAngle
ia=0
if 'ItalicAngle' in fm and fm['ItalicAngle']:
ia=1
fd+=",'ItalicAngle':"+str(ia)
#StemV
if 'StdVW' in fm:
stemv=fm['StdVW']
elif('Weight' in fm and re.match('(bold|black)',fm['Weight'])):
stemv=120
else:
stemv=70
fd+=",'StemV':"+str(stemv)
#MissingWidth
if('MissingWidth' in fm):
fd+=",'MissingWidth':"+fm['MissingWidth']
fd+='}'
return fd
def MakeWidthArray(fm):
#Make character width array
s="{\n\t"
cw=fm['Widths']
for i in xrange(0,256):
if(chr(i)=="'"):
s+="'\\''"
elif(chr(i)=="\\"):
s+="'\\\\'"
elif(i>=32 and i<=126):
s+="'"+chr(i)+"'"
else:
s+="chr(%d)"%i
s+=':'+fm['Widths'][i]
if(i<255):
s+=','
if((i+1)%22==0):
s+="\n\t"
s+='}'
return s
def MakeFontEncoding(map):
#Build differences from reference encoding
ref=ReadMap('cp1252')
s=''
last=0
for i in xrange(32,256):
if(map[i]!=ref[i]):
if(i!=last+1):
s+=str(i)+' '
last=i
s+='/'+map[i]+' '
return s.rstrip()
def SaveToFile(filename,s,mode='t'):
f=file(filename,'w'+mode)
if(not f):
die('Can\'t write to file '+filename)
f.write(s)
f.close()
def ReadShort(f):
return struct.unpack('>H',f.read(2))[0]
def ReadLong(f):
return struct.unpack('>L',f.read(4))[0]
def CheckTTF(filename):
#Check if font license allows embedding
f=file(filename,'rb')
if(not f):
die('Error: Can\'t open '+filename)
#Extract number of tables
f.seek(4,SEEK_CUR)
nb=ReadShort(f)
f.seek(6,SEEK_CUR)
#Seek OS/2 table
found=0
for i in xrange(0,nb):
if(f.read(4)=='OS/2'):
found=1
break
f.seek(12,SEEK_CUR)
if(not found):
f.close()
return
f.seek(4,SEEK_CUR)
offset=ReadLong(f)
f.seek(offset,SEEK_SET)
#Extract fsType flags
f.seek(8,SEEK_CUR)
fsType=ReadShort(f)
rl=(fsType & 0x02)!=0
pp=(fsType & 0x04)!=0
e=(fsType & 0x08)!=0
f.close()
if(rl and not pp and not e):
print 'Warning: font license does not allow embedding'
# ******************************************************************************
# * fontfile : chemin du fichier TTF (ou chaine vide si pas d'incorporation) *
# * afmfile : chemin du fichier AFM *
# * enc : encodage (ou chaine vide si la police est symbolique) *
# * patch : patch optionnel pour l'encodage *
# * type : type de la police si $fontfile est vide *
# *******************************************************************************/
def MakeFont(fontfile,afmfile,enc='cp1252',patch={},type='TrueType'):
#Generate a font definition file
if(enc):
map=ReadMap(enc)
for cc,gn in patch.iteritems():
map[cc]=gn
else:
map=[]
if(not file_exists(afmfile)):
die('Error: AFM file not found: '+afmfile)
fm=ReadAFM(afmfile,map)
if(enc):
diff=MakeFontEncoding(map)
else:
diff=''
fd=MakeFontDescriptor(fm,empty(map))
#Find font type
if(fontfile):
ext=strtolower(substr(fontfile,-3))
if(ext=='ttf'):
type='TrueType'
elif(ext=='pfb'):
type='Type1'
else:
die('Error: unrecognized font file extension: '+ext)
else:
if(type!='TrueType' and type!='Type1'):
die('Error: incorrect font type: '+type)
#Start generation
s=''+"\n"
s+='type=\''+type+"'\n"
s+='name=\''+fm['FontName']+"'\n"
s+='desc='+fd+"\n"
if not 'UnderlinePosition' in fm:
fm['UnderlinePosition']=-100
if not 'UnderlineThickness' in fm:
fm['UnderlineThickness']=50
s+='up='+str(fm['UnderlinePosition'])+"\n"
s+='ut='+str(fm['UnderlineThickness'])+"\n"
w=MakeWidthArray(fm)
s+='cw='+str(w)+"\n"
s+='enc=\''+enc+"'\n"
s+='diff=\''+diff+"'\n"
base=substr(basename(afmfile),0,-4)
if(fontfile):
#Embedded font
if(not file_exists(fontfile)):
die('Error: font file not found: '+fontfile)
if(type=='TrueType'):
CheckTTF(fontfile)
f=file(fontfile,'rb')
if(not f):
die('Error: Can\'t open '+fontfile)
filecnt=f.read()
f.close()
if(type=='Type1'):
#Find first two sections and discard third one
header=(ord(filecnt[0])==128)
if(header):
#Strip first binary header
filecnt=substr(filecnt,6)
pos=strpos(filecnt,'eexec')
if(not pos):
die('Error: font file does not seem to be valid Type1')
size1=pos+6
if(header and ord(filecnt[size1])==128):
#Strip second binary header
filecnt=substr(filecnt,0,size1)+substr(filecnt,size1+6)
pos=strpos(filecnt,'00000000')
if(not pos):
die('Error: font file does not seem to be valid Type1')
size2=pos-size1
filecnt=substr(filecnt,0,size1+size2)
if(1):
cmp=base+'.z'
SaveToFile(cmp,zlib.compress(filecnt),'b')
s+='filename=\''+cmp+"'\n"
print 'Font file compressed ('+cmp+')'
else:
s+='filename=\''+basename(fontfile)+"'\n"
print 'Notice: font file could not be compressed (zlib extension not available)'
if(type=='Type1'):
s+='size1='+str(size1)+"\n"
s+='size2='+str(size2)+"\n"
else:
s+='originalsize='+str(filesize(fontfile))+"\n"
else:
#Not embedded font
s+='file='+"''\n"
s+="\n"
SaveToFile(base+'.py',s)
print 'Font definition file generated ('+base+'.py'+')'
if __name__ == '__main__':
MakeFont('font/calligra.ttf','font/calligra.afm')
MakeFont('font/a010013l.pfb','font/a010013l.afm')