FattView/fpdf/attic/makefont.py


Home Back

# ******************************************************************************
# * 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')

Powered by Code, a simple repository browser by Fabio Di Matteo