Läuft die Voice Acoustic VA-Remotecontrol (AllDSP AllControl) nativ unter Linux auf echtem wxGTK 3.0 — ohne Wine und ohne dekompilierten App-Code. Ausgeführt wird das unveränderte Python-2.7-Bytecode aus dem macOS-.app; C-Extensions (wxGTK/numpy/ scipy/PortAudio) kommen nativ aus Debian stretch. Enthält: va/ (Original-.pyc + pure-Python-Libs + Assets + launch.py mit den drei Linux-Fixes), Dockerfile/run.sh/entrypoint.sh, reference/ (dekompilierter Quellcode zum Debuggen) und README/STATUS. img.cache (378 MB, regenerierbar) ist per .gitignore ausgeschlossen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
341 lines
9.2 KiB
Python
341 lines
9.2 KiB
Python
# ******************************************************************************
|
|
# * 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')
|