Erster Commit: nativer Linux-Port aus macOS-Bytecode
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>
This commit is contained in:
@@ -0,0 +1,340 @@
|
||||
# ******************************************************************************
|
||||
# * 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')
|
||||
Reference in New Issue
Block a user