Remove nfntLib and fontLib
They are in no way relevant or related to anything we care about these days. t1Lib is different though.
This commit is contained in:
parent
7279302238
commit
ea1d11be18
@ -1,547 +0,0 @@
|
||||
from __future__ import print_function, division
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.misc import sstruct
|
||||
try:
|
||||
from Carbon import Res
|
||||
except ImportError:
|
||||
import Res
|
||||
import struct
|
||||
|
||||
|
||||
error = "fondLib.error"
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
headerformat = """
|
||||
>
|
||||
ffFlags: h
|
||||
ffFamID: h
|
||||
ffFirstChar: h
|
||||
ffLastChar: h
|
||||
ffAscent: h
|
||||
ffDescent: h
|
||||
ffLeading: h
|
||||
ffWidMax: h
|
||||
ffWTabOff: l
|
||||
ffKernOff: l
|
||||
ffStylOff: l
|
||||
"""
|
||||
|
||||
FONDheadersize = 52
|
||||
|
||||
class FontFamily(object):
|
||||
|
||||
def __init__(self, theRes, mode = 'r'):
|
||||
self.ID, type, self.name = theRes.GetResInfo()
|
||||
if type != 'FOND':
|
||||
raise ValueError("FOND resource required")
|
||||
self.FOND = theRes
|
||||
self.mode = mode
|
||||
self.changed = 0
|
||||
|
||||
if DEBUG:
|
||||
self.parsedthings = []
|
||||
|
||||
def parse(self):
|
||||
self._getheader()
|
||||
self._getfontassociationtable()
|
||||
self._getoffsettable()
|
||||
self._getboundingboxtable()
|
||||
self._getglyphwidthtable()
|
||||
self._getstylemappingtable()
|
||||
self._getglyphencodingsubtable()
|
||||
self._getkerningtables()
|
||||
|
||||
def minimalparse(self):
|
||||
self._getheader()
|
||||
self._getglyphwidthtable()
|
||||
self._getstylemappingtable()
|
||||
|
||||
def __repr__(self):
|
||||
return "<FontFamily instance of %s>" % self.name
|
||||
|
||||
def getflags(self):
|
||||
return self.fondClass
|
||||
|
||||
def setflags(self, flags):
|
||||
self.changed = 1
|
||||
self.fondClass = flags
|
||||
|
||||
def save(self, destresfile = None):
|
||||
if self.mode != 'w':
|
||||
raise error("can't save font: no write permission")
|
||||
self._buildfontassociationtable()
|
||||
self._buildoffsettable()
|
||||
self._buildboundingboxtable()
|
||||
self._buildglyphwidthtable()
|
||||
self._buildkerningtables()
|
||||
self._buildstylemappingtable()
|
||||
self._buildglyphencodingsubtable()
|
||||
rawnames = [ "_rawheader",
|
||||
"_rawfontassociationtable",
|
||||
"_rawoffsettable",
|
||||
"_rawglyphwidthtable",
|
||||
"_rawstylemappingtable",
|
||||
"_rawglyphencodingsubtable",
|
||||
"_rawkerningtables"
|
||||
]
|
||||
for name in rawnames[1:]: # skip header
|
||||
data = getattr(self, name)
|
||||
if len(data) & 1:
|
||||
setattr(self, name, data + '\0')
|
||||
|
||||
self.ffWTabOff = FONDheadersize + len(self._rawfontassociationtable) + len(self._rawoffsettable)
|
||||
self.ffStylOff = self.ffWTabOff + len(self._rawglyphwidthtable)
|
||||
self.ffKernOff = self.ffStylOff + len(self._rawstylemappingtable) + len(self._rawglyphencodingsubtable)
|
||||
self.glyphTableOffset = len(self._rawstylemappingtable)
|
||||
|
||||
if not self._rawglyphwidthtable:
|
||||
self.ffWTabOff = 0
|
||||
if not self._rawstylemappingtable:
|
||||
self.ffStylOff = 0
|
||||
if not self._rawglyphencodingsubtable:
|
||||
self.glyphTableOffset = 0
|
||||
if not self._rawkerningtables:
|
||||
self.ffKernOff = 0
|
||||
|
||||
self._buildheader()
|
||||
|
||||
# glyphTableOffset has only just been calculated
|
||||
self._updatestylemappingtable()
|
||||
|
||||
newdata = ""
|
||||
for name in rawnames:
|
||||
newdata = newdata + getattr(self, name)
|
||||
if destresfile is None:
|
||||
self.FOND.data = newdata
|
||||
self.FOND.ChangedResource()
|
||||
self.FOND.WriteResource()
|
||||
else:
|
||||
ID, type, name = self.FOND.GetResInfo()
|
||||
self.FOND.DetachResource()
|
||||
self.FOND.data = newdata
|
||||
saveref = Res.CurResFile()
|
||||
Res.UseResFile(destresfile)
|
||||
self.FOND.AddResource(type, ID, name)
|
||||
Res.UseResFile(saveref)
|
||||
self.changed = 0
|
||||
|
||||
def _getheader(self):
|
||||
data = self.FOND.data
|
||||
sstruct.unpack(headerformat, data[:28], self)
|
||||
self.ffProperty = struct.unpack(">9h", data[28:46])
|
||||
self.ffIntl = struct.unpack(">hh", data[46:50])
|
||||
self.ffVersion, = struct.unpack(">h", data[50:FONDheadersize])
|
||||
|
||||
if DEBUG:
|
||||
self._rawheader = data[:FONDheadersize]
|
||||
self.parsedthings.append((0, FONDheadersize, 'header'))
|
||||
|
||||
def _buildheader(self):
|
||||
header = sstruct.pack(headerformat, self)
|
||||
header = header + struct.pack(*(">9h",) + self.ffProperty)
|
||||
header = header + struct.pack(*(">hh",) + self.ffIntl)
|
||||
header = header + struct.pack(">h", self.ffVersion)
|
||||
if DEBUG:
|
||||
print("header is the same?", self._rawheader == header and 'yes.' or 'no.')
|
||||
if self._rawheader != header:
|
||||
print(len(self._rawheader), len(header))
|
||||
self._rawheader = header
|
||||
|
||||
def _getfontassociationtable(self):
|
||||
data = self.FOND.data
|
||||
offset = FONDheadersize
|
||||
numberofentries, = struct.unpack(">h", data[offset:offset+2])
|
||||
numberofentries = numberofentries + 1
|
||||
size = numberofentries * 6
|
||||
self.fontAssoc = []
|
||||
for i in range(offset + 2, offset + size, 6):
|
||||
self.fontAssoc.append(struct.unpack(">3h", data[i:i+6]))
|
||||
|
||||
self._endoffontassociationtable = offset + size + 2
|
||||
if DEBUG:
|
||||
self._rawfontassociationtable = data[offset:self._endoffontassociationtable]
|
||||
self.parsedthings.append((offset, self._endoffontassociationtable, 'fontassociationtable'))
|
||||
|
||||
def _buildfontassociationtable(self):
|
||||
data = struct.pack(">h", len(self.fontAssoc) - 1)
|
||||
for size, stype, ID in self.fontAssoc:
|
||||
data = data + struct.pack(">3h", size, stype, ID)
|
||||
|
||||
if DEBUG:
|
||||
print("font association table is the same?", self._rawfontassociationtable == data and 'yes.' or 'no.')
|
||||
if self._rawfontassociationtable != data:
|
||||
print(len(self._rawfontassociationtable), len(data))
|
||||
self._rawfontassociationtable = data
|
||||
|
||||
def _getoffsettable(self):
|
||||
if self.ffWTabOff == 0:
|
||||
self._rawoffsettable = ""
|
||||
return
|
||||
data = self.FOND.data
|
||||
# Quick'n'Dirty. What's the spec anyway? Can't find it...
|
||||
offset = self._endoffontassociationtable
|
||||
count = self.ffWTabOff
|
||||
self._rawoffsettable = data[offset:count]
|
||||
if DEBUG:
|
||||
self.parsedthings.append((offset, count, 'offsettable&bbtable'))
|
||||
|
||||
def _buildoffsettable(self):
|
||||
if not hasattr(self, "_rawoffsettable"):
|
||||
self._rawoffsettable = ""
|
||||
|
||||
def _getboundingboxtable(self):
|
||||
self.boundingBoxes = None
|
||||
if self._rawoffsettable[:6] != '\0\0\0\0\0\6': # XXX ????
|
||||
return
|
||||
boxes = {}
|
||||
data = self._rawoffsettable[6:]
|
||||
numstyles = struct.unpack(">h", data[:2])[0] + 1
|
||||
data = data[2:]
|
||||
for i in range(numstyles):
|
||||
style, l, b, r, t = struct.unpack(">hhhhh", data[:10])
|
||||
boxes[style] = (l, b, r, t)
|
||||
data = data[10:]
|
||||
self.boundingBoxes = boxes
|
||||
|
||||
def _buildboundingboxtable(self):
|
||||
if self.boundingBoxes and self._rawoffsettable[:6] == '\0\0\0\0\0\6':
|
||||
boxes = sorted(self.boundingBoxes.items())
|
||||
data = '\0\0\0\0\0\6' + struct.pack(">h", len(boxes) - 1)
|
||||
for style, (l, b, r, t) in boxes:
|
||||
data = data + struct.pack(">hhhhh", style, l, b, r, t)
|
||||
self._rawoffsettable = data
|
||||
|
||||
def _getglyphwidthtable(self):
|
||||
self.widthTables = {}
|
||||
if self.ffWTabOff == 0:
|
||||
return
|
||||
data = self.FOND.data
|
||||
offset = self.ffWTabOff
|
||||
numberofentries, = struct.unpack(">h", data[offset:offset+2])
|
||||
numberofentries = numberofentries + 1
|
||||
count = offset + 2
|
||||
for i in range(numberofentries):
|
||||
stylecode, = struct.unpack(">h", data[count:count+2])
|
||||
widthtable = self.widthTables[stylecode] = []
|
||||
count = count + 2
|
||||
for j in range(3 + self.ffLastChar - self.ffFirstChar):
|
||||
width, = struct.unpack(">h", data[count:count+2])
|
||||
widthtable.append(width)
|
||||
count = count + 2
|
||||
|
||||
if DEBUG:
|
||||
self._rawglyphwidthtable = data[offset:count]
|
||||
self.parsedthings.append((offset, count, 'glyphwidthtable'))
|
||||
|
||||
def _buildglyphwidthtable(self):
|
||||
if not self.widthTables:
|
||||
self._rawglyphwidthtable = ""
|
||||
return
|
||||
numberofentries = len(self.widthTables)
|
||||
data = struct.pack('>h', numberofentries - 1)
|
||||
tables = sorted(self.widthTables.items())
|
||||
for stylecode, table in tables:
|
||||
data = data + struct.pack('>h', stylecode)
|
||||
if len(table) != (3 + self.ffLastChar - self.ffFirstChar):
|
||||
raise error("width table has wrong length")
|
||||
for width in table:
|
||||
data = data + struct.pack('>h', width)
|
||||
if DEBUG:
|
||||
print("glyph width table is the same?", self._rawglyphwidthtable == data and 'yes.' or 'no.')
|
||||
self._rawglyphwidthtable = data
|
||||
|
||||
def _getkerningtables(self):
|
||||
self.kernTables = {}
|
||||
if self.ffKernOff == 0:
|
||||
return
|
||||
data = self.FOND.data
|
||||
offset = self.ffKernOff
|
||||
numberofentries, = struct.unpack(">h", data[offset:offset+2])
|
||||
numberofentries = numberofentries + 1
|
||||
count = offset + 2
|
||||
for i in range(numberofentries):
|
||||
stylecode, = struct.unpack(">h", data[count:count+2])
|
||||
count = count + 2
|
||||
numberofpairs, = struct.unpack(">h", data[count:count+2])
|
||||
count = count + 2
|
||||
kerntable = self.kernTables[stylecode] = []
|
||||
for j in range(numberofpairs):
|
||||
firstchar, secondchar, kerndistance = struct.unpack(">cch", data[count:count+4])
|
||||
kerntable.append((byteord(firstchar), byteord(secondchar), kerndistance))
|
||||
count = count + 4
|
||||
|
||||
if DEBUG:
|
||||
self._rawkerningtables = data[offset:count]
|
||||
self.parsedthings.append((offset, count, 'kerningtables'))
|
||||
|
||||
def _buildkerningtables(self):
|
||||
if self.kernTables == {}:
|
||||
self._rawkerningtables = ""
|
||||
self.ffKernOff = 0
|
||||
return
|
||||
numberofentries = len(self.kernTables)
|
||||
data = [struct.pack('>h', numberofentries - 1)]
|
||||
tables = sorted(self.kernTables.items())
|
||||
for stylecode, table in tables:
|
||||
data.append(struct.pack('>h', stylecode))
|
||||
data.append(struct.pack('>h', len(table))) # numberofpairs
|
||||
for firstchar, secondchar, kerndistance in table:
|
||||
data.append(struct.pack(">cch", bytechr(firstchar), bytechr(secondchar), kerndistance))
|
||||
|
||||
data = bytesjoin(data)
|
||||
|
||||
if DEBUG:
|
||||
print("kerning table is the same?", self._rawkerningtables == data and 'yes.' or 'no.')
|
||||
if self._rawkerningtables != data:
|
||||
print(len(self._rawkerningtables), len(data))
|
||||
self._rawkerningtables = data
|
||||
|
||||
def _getstylemappingtable(self):
|
||||
offset = self.ffStylOff
|
||||
self.styleStrings = []
|
||||
self.styleIndices = ()
|
||||
self.glyphTableOffset = 0
|
||||
self.fondClass = 0
|
||||
if offset == 0:
|
||||
return
|
||||
data = self.FOND.data
|
||||
self.fondClass, self.glyphTableOffset, self.styleMappingReserved, = \
|
||||
struct.unpack(">hll", data[offset:offset+10])
|
||||
self.styleIndices = struct.unpack('>48b', data[offset + 10:offset + 58])
|
||||
stringcount, = struct.unpack('>h', data[offset+58:offset+60])
|
||||
|
||||
count = offset + 60
|
||||
for i in range(stringcount):
|
||||
str_len = byteord(data[count])
|
||||
self.styleStrings.append(data[count + 1:count + 1 + str_len])
|
||||
count = count + 1 + str_len
|
||||
|
||||
self._unpackstylestrings()
|
||||
|
||||
data = data[offset:count]
|
||||
if len(data) % 2:
|
||||
data = data + '\0'
|
||||
if DEBUG:
|
||||
self._rawstylemappingtable = data
|
||||
self.parsedthings.append((offset, count, 'stylemappingtable'))
|
||||
|
||||
def _buildstylemappingtable(self):
|
||||
if not self.styleIndices:
|
||||
self._rawstylemappingtable = ""
|
||||
return
|
||||
data = struct.pack(">hll", self.fondClass, self.glyphTableOffset,
|
||||
self.styleMappingReserved)
|
||||
|
||||
self._packstylestrings()
|
||||
data = data + struct.pack(*(">48b",) + self.styleIndices)
|
||||
|
||||
stringcount = len(self.styleStrings)
|
||||
data = data + struct.pack(">h", stringcount)
|
||||
for string in self.styleStrings:
|
||||
data = data + bytechr(len(string)) + string
|
||||
|
||||
if len(data) % 2:
|
||||
data = data + '\0'
|
||||
|
||||
if DEBUG:
|
||||
print("style mapping table is the same?", self._rawstylemappingtable == data and 'yes.' or 'no.')
|
||||
self._rawstylemappingtable = data
|
||||
|
||||
def _unpackstylestrings(self):
|
||||
psNames = {}
|
||||
self.ffFamilyName = self.styleStrings[0]
|
||||
for i in self.widthTables.keys():
|
||||
index = self.styleIndices[i]
|
||||
if index == 1:
|
||||
psNames[i] = self.styleStrings[0]
|
||||
else:
|
||||
style = self.styleStrings[0]
|
||||
codes = map(byteord, self.styleStrings[index - 1])
|
||||
for code in codes:
|
||||
style = style + self.styleStrings[code - 1]
|
||||
psNames[i] = style
|
||||
self.psNames = psNames
|
||||
|
||||
def _packstylestrings(self):
|
||||
nameparts = {}
|
||||
splitnames = {}
|
||||
for style, name in self.psNames.items():
|
||||
split = splitname(name, self.ffFamilyName)
|
||||
splitnames[style] = split
|
||||
for part in split:
|
||||
nameparts[part] = None
|
||||
del nameparts[self.ffFamilyName]
|
||||
nameparts = sorted(nameparts.keys())
|
||||
items = sorted(splitnames.items())
|
||||
numindices = 0
|
||||
for style, split in items:
|
||||
if len(split) > 1:
|
||||
numindices = numindices + 1
|
||||
numindices = max(numindices, max(self.styleIndices) - 1)
|
||||
styleStrings = [self.ffFamilyName] + numindices * [""] + nameparts
|
||||
# XXX the next bit goes wrong for MM fonts.
|
||||
for style, split in items:
|
||||
if len(split) == 1:
|
||||
continue
|
||||
indices = ""
|
||||
for part in split[1:]:
|
||||
indices = indices + bytechr(nameparts.index(part) + numindices + 2)
|
||||
styleStrings[self.styleIndices[style] - 1] = indices
|
||||
self.styleStrings = styleStrings
|
||||
|
||||
def _updatestylemappingtable(self):
|
||||
# Update the glyphTableOffset field.
|
||||
# This is necessary since we have to build this table to
|
||||
# know what the glyphTableOffset will be.
|
||||
# And we don't want to build it twice, do we?
|
||||
data = self._rawstylemappingtable
|
||||
if not data:
|
||||
return
|
||||
data = data[:2] + struct.pack(">l", self.glyphTableOffset) + data[6:]
|
||||
self._rawstylemappingtable = data
|
||||
|
||||
def _getglyphencodingsubtable(self):
|
||||
glyphEncoding = self.glyphEncoding = {}
|
||||
if not self.glyphTableOffset:
|
||||
return
|
||||
offset = self.ffStylOff + self.glyphTableOffset
|
||||
data = self.FOND.data
|
||||
numberofentries, = struct.unpack(">h", data[offset:offset+2])
|
||||
count = offset + 2
|
||||
for i in range(numberofentries):
|
||||
glyphcode = byteord(data[count])
|
||||
count = count + 1
|
||||
strlen = byteord(data[count])
|
||||
count = count + 1
|
||||
glyphname = data[count:count+strlen]
|
||||
glyphEncoding[glyphcode] = glyphname
|
||||
count = count + strlen
|
||||
|
||||
if DEBUG:
|
||||
self._rawglyphencodingsubtable = data[offset:count]
|
||||
self.parsedthings.append((offset, count, 'glyphencodingsubtable'))
|
||||
|
||||
def _buildglyphencodingsubtable(self):
|
||||
if not self.glyphEncoding:
|
||||
self._rawglyphencodingsubtable = ""
|
||||
return
|
||||
numberofentries = len(self.glyphEncoding)
|
||||
data = struct.pack(">h", numberofentries)
|
||||
items = sorted(self.glyphEncoding.items())
|
||||
for glyphcode, glyphname in items:
|
||||
data = data + bytechr(glyphcode) + bytechr(len(glyphname)) + glyphname
|
||||
self._rawglyphencodingsubtable = data
|
||||
|
||||
|
||||
uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
|
||||
def splitname(name, famname = None):
|
||||
# XXX this goofs up MM font names: but how should it be done??
|
||||
if famname:
|
||||
if name[:len(famname)] != famname:
|
||||
raise error("first part of name should be same as family name")
|
||||
name = name[len(famname):]
|
||||
split = [famname]
|
||||
else:
|
||||
split = []
|
||||
current = ""
|
||||
for c in name:
|
||||
if c == '-' or c in uppercase:
|
||||
if current:
|
||||
split.append(current)
|
||||
current = ""
|
||||
current = current + c
|
||||
if current:
|
||||
split.append(current)
|
||||
return split
|
||||
|
||||
def makeLWFNfilename(name):
|
||||
split = splitname(name)
|
||||
lwfnname = split[0][:5]
|
||||
for part in split[1:]:
|
||||
if part != '-':
|
||||
lwfnname = lwfnname + part[:3]
|
||||
return lwfnname
|
||||
|
||||
class BitmapFontFile(object):
|
||||
|
||||
def __init__(self, path, mode='r'):
|
||||
if mode == 'r':
|
||||
permission = 1 # read only
|
||||
elif mode == 'w':
|
||||
permission = 3 # exclusive r/w
|
||||
else:
|
||||
raise error('mode should be either "r" or "w"')
|
||||
self.mode = mode
|
||||
self.resref = Res.FSOpenResFile(path, permission)
|
||||
Res.UseResFile(self.resref)
|
||||
self.path = path
|
||||
self.fonds = []
|
||||
self.getFONDs()
|
||||
|
||||
def getFONDs(self):
|
||||
FONDcount = Res.Count1Resources('FOND')
|
||||
for i in range(FONDcount):
|
||||
fond = FontFamily(Res.Get1IndResource('FOND', i + 1), self.mode)
|
||||
self.fonds.append(fond)
|
||||
|
||||
def parse(self):
|
||||
self.fondsbyname = {}
|
||||
for fond in self.fonds:
|
||||
fond.parse()
|
||||
if hasattr(fond, "psNames") and fond.psNames:
|
||||
psNames = sorted(fond.psNames.values())
|
||||
self.fondsbyname[psNames[0]] = fond
|
||||
|
||||
def minimalparse(self):
|
||||
for fond in self.fonds:
|
||||
fond.minimalparse()
|
||||
|
||||
def close(self):
|
||||
if self.resref != None:
|
||||
try:
|
||||
Res.CloseResFile(self.resref)
|
||||
except Res.Error:
|
||||
pass
|
||||
self.resref = None
|
||||
|
||||
|
||||
class FondSelector(object):
|
||||
|
||||
def __init__(self, fondlist):
|
||||
import W
|
||||
if not fondlist:
|
||||
raise ValueError("expected at least one FOND entry")
|
||||
if len(fondlist) == 1:
|
||||
self.choice = 0
|
||||
return
|
||||
fonds = []
|
||||
for fond in fondlist:
|
||||
fonds.append(fond.name)
|
||||
self.w = W.ModalDialog((200, 200), "aaa")
|
||||
self.w.donebutton = W.Button((-70, -26, 60, 16), "Done", self.close)
|
||||
self.w.l = W.List((10, 10, -10, -36), fonds, self.listhit)
|
||||
self.w.setdefaultbutton(self.w.donebutton)
|
||||
self.w.l.setselection([0])
|
||||
self.w.open()
|
||||
|
||||
def close(self):
|
||||
self.checksel()
|
||||
sel = self.w.l.getselection()
|
||||
self.choice = sel[0]
|
||||
self.w.close()
|
||||
|
||||
def listhit(self, isDbl):
|
||||
if isDbl:
|
||||
self.w.donebutton.push()
|
||||
else:
|
||||
self.checksel()
|
||||
|
||||
def checksel(self):
|
||||
sel = self.w.l.getselection()
|
||||
if not sel:
|
||||
self.w.l.setselection([0])
|
||||
elif len(sel) != 1:
|
||||
self.w.l.setselection([sel[0]])
|
||||
|
@ -1,304 +0,0 @@
|
||||
from __future__ import print_function, division
|
||||
from fontTools.misc.py23 import *
|
||||
from fontTools.misc import sstruct
|
||||
import struct
|
||||
|
||||
|
||||
# FontRec header
|
||||
nfntHeaderFormat = """
|
||||
> # big endian
|
||||
fontType: h # font type
|
||||
firstChar: h # ASCII code of first character
|
||||
lastChar: h # ASCII code of last character
|
||||
widMax: h # maximum character width
|
||||
kernMax: h # negative of maximum character kern
|
||||
nDescent: h # negative of descent
|
||||
fRectWidth: h # width of font rectangle
|
||||
fRectHeight: h # height of font rectangle
|
||||
owTLoc: H # offset to offset/width table (in words from _this_ point)
|
||||
ascent: h # ascent
|
||||
descent: h # descent
|
||||
leading: h # leading
|
||||
rowWords: h # row width of bit image / 2
|
||||
"""
|
||||
headerSize = sstruct.calcsize(nfntHeaderFormat)
|
||||
assert headerSize == 26
|
||||
|
||||
|
||||
class NFNT(object):
|
||||
|
||||
def __init__(self, data=None):
|
||||
if data is not None:
|
||||
self.decompile(data)
|
||||
|
||||
def decompile(self, data):
|
||||
# header; FontRec
|
||||
sstruct.unpack(nfntHeaderFormat, data[:headerSize], self)
|
||||
|
||||
#assert self.fRectHeight == (self.ascent + self.descent)
|
||||
|
||||
# rest
|
||||
tableSize = 2 * (self.lastChar - self.firstChar + 3)
|
||||
bitmapSize = 2 * self.rowWords * self.fRectHeight
|
||||
|
||||
self.bits = data[headerSize:headerSize + bitmapSize]
|
||||
|
||||
# XXX deal with self.nDescent being a positive number
|
||||
assert (headerSize + bitmapSize + tableSize - 16) // 2 == self.owTLoc # ugh...
|
||||
|
||||
locTable = data[headerSize + bitmapSize:headerSize + bitmapSize + tableSize]
|
||||
if len(locTable) != tableSize:
|
||||
raise ValueError('invalid NFNT format')
|
||||
|
||||
owTable = data[headerSize + bitmapSize + tableSize:headerSize + bitmapSize + 2 * tableSize]
|
||||
if len(owTable) != tableSize:
|
||||
raise ValueError('invalid NFNT format')
|
||||
|
||||
# fill tables
|
||||
self.offsetTable = []
|
||||
self.widthTable = []
|
||||
self.locTable = []
|
||||
for i in range(0, tableSize, 2):
|
||||
self.offsetTable.append(byteord(owTable[i]))
|
||||
self.widthTable.append(byteord(owTable[i+1]))
|
||||
loc, = struct.unpack("h", locTable[i:i+2])
|
||||
self.locTable.append(loc)
|
||||
|
||||
def compile(self):
|
||||
header = sstruct.pack(nfntHeaderFormat, self)
|
||||
nEntries = len(self.widthTable)
|
||||
owTable = [None] * nEntries
|
||||
locTable = [None] * nEntries
|
||||
for i in range(nEntries):
|
||||
owTable[i] = bytechr(self.offsetTable[i]) + bytechr(self.widthTable[i])
|
||||
locTable[i] = struct.pack("h", self.locTable[i])
|
||||
owTable = bytesjoin(owTable)
|
||||
locTable = bytesjoin(locTable)
|
||||
assert len(locTable) == len(owTable) == 2 * (self.lastChar - self.firstChar + 3)
|
||||
return header + self.bits + locTable + owTable
|
||||
|
||||
def unpackGlyphs(self):
|
||||
import numpy
|
||||
nGlyphs = len(self.locTable) - 1
|
||||
self.glyphs = [None] * nGlyphs
|
||||
|
||||
rowBytes = self.rowWords * 2
|
||||
imageWidth = self.rowWords * 16
|
||||
imageHeight = self.fRectHeight
|
||||
bits = self.bits
|
||||
bitImage = numpy.zeros((imageWidth, imageHeight), numpy.int8)
|
||||
|
||||
for y in range(imageHeight):
|
||||
for xByte in range(rowBytes):
|
||||
byte = bits[y * rowBytes + xByte]
|
||||
for xBit in range(8):
|
||||
x = 8 * xByte + xBit
|
||||
bit = (byteord(byte) >> (7 - xBit)) & 0x01
|
||||
bitImage[x, y] = bit
|
||||
|
||||
for i in range(nGlyphs):
|
||||
width = self.widthTable[i]
|
||||
offset = self.offsetTable[i]
|
||||
if width == 255 and offset == 255:
|
||||
self.glyphs[i] = None
|
||||
else:
|
||||
imageL = self.locTable[i]
|
||||
imageR = self.locTable[i+1]
|
||||
imageWidth = imageR - imageL
|
||||
offset = offset + self.kernMax
|
||||
self.glyphs[i] = glyph = Glyph(width, offset, bitImage[imageL:imageR])
|
||||
|
||||
def packGlyphs(self):
|
||||
import numpy
|
||||
imageWidth = 0
|
||||
kernMax = 0
|
||||
imageHeight = None
|
||||
widMax = 0
|
||||
fRectWidth = 0
|
||||
for glyph in self.glyphs:
|
||||
if glyph is None:
|
||||
continue
|
||||
if imageHeight is None:
|
||||
imageHeight = glyph.pixels.shape[1]
|
||||
else:
|
||||
assert imageHeight == glyph.pixels.shape[1]
|
||||
imageWidth = imageWidth + glyph.pixels.shape[0]
|
||||
kernMax = min(kernMax, glyph.offset)
|
||||
widMax = max(widMax, glyph.width)
|
||||
fRectWidth = max(fRectWidth, glyph.pixels.shape[0] + glyph.offset)
|
||||
|
||||
fRectWidth = fRectWidth - kernMax
|
||||
imageWidth = 16 * ((imageWidth - 1) // 16 + 1)
|
||||
rowBytes = imageWidth // 8
|
||||
rowWords = rowBytes // 2
|
||||
bitImage = numpy.zeros((imageWidth, imageHeight), numpy.int8)
|
||||
locTable = []
|
||||
widthTable = []
|
||||
offsetTable = []
|
||||
loc = 0
|
||||
for glyph in self.glyphs:
|
||||
locTable.append(loc)
|
||||
if glyph is None:
|
||||
widthTable.append(255)
|
||||
offsetTable.append(255)
|
||||
continue
|
||||
widthTable.append(glyph.width)
|
||||
offsetTable.append(glyph.offset - kernMax)
|
||||
imageWidth = glyph.pixels.shape[0]
|
||||
bitImage[loc:loc+imageWidth] = glyph.pixels
|
||||
loc = loc + imageWidth
|
||||
|
||||
locTable.append(loc)
|
||||
widthTable.append(255)
|
||||
offsetTable.append(255)
|
||||
|
||||
bits = []
|
||||
for y in range(imageHeight):
|
||||
for xByte in range(rowBytes):
|
||||
byte = 0
|
||||
for x in range(8):
|
||||
byte = byte | ((bitImage[8 * xByte + x, y] & 0x01) << (7 - x))
|
||||
bits.append(bytechr(byte))
|
||||
bits = bytesjoin(bits)
|
||||
|
||||
# assign values
|
||||
self.fontType = 0x9000
|
||||
self.lastChar = self.firstChar + len(self.glyphs) - 2
|
||||
self.widMax = widMax
|
||||
self.kernMax = kernMax
|
||||
self.descent = imageHeight - self.ascent
|
||||
self.nDescent = -self.descent
|
||||
self.fRectWidth = fRectWidth
|
||||
self.fRectHeight = imageHeight
|
||||
self.rowWords = rowWords
|
||||
|
||||
tableSize = 2 * (self.lastChar - self.firstChar + 3)
|
||||
self.owTLoc = (headerSize + len(bits) + tableSize - 16) // 2
|
||||
|
||||
self.bits = bits
|
||||
self.locTable = locTable
|
||||
self.widthTable = widthTable
|
||||
self.offsetTable = offsetTable
|
||||
|
||||
def getMissing(self):
|
||||
return self.glyphs[-1]
|
||||
|
||||
def __getitem__(self, charNum):
|
||||
if charNum > self.lastChar or charNum < 0:
|
||||
raise IndexError("no such character")
|
||||
index = charNum - self.firstChar
|
||||
if index < 0:
|
||||
return None
|
||||
return self.glyphs[index]
|
||||
|
||||
def __setitem__(self, charNum, glyph):
|
||||
if charNum > self.lastChar or charNum < 0:
|
||||
raise IndexError("no such character")
|
||||
index = charNum - self.firstChar
|
||||
if index < 0:
|
||||
raise IndexError("no such character")
|
||||
self.glyphs[index] = glyph
|
||||
|
||||
def __len__(self):
|
||||
return len(self.locTable) - 2 + self.firstChar
|
||||
|
||||
#
|
||||
# XXX old cruft
|
||||
#
|
||||
|
||||
def createQdBitImage(self):
|
||||
import Qd
|
||||
self.bitImage = Qd.BitMap(self.bits, 2 * self.rowWords, (0, 0, self.rowWords * 16, self.fRectHeight))
|
||||
|
||||
def drawstring(self, astring, destbits, xOffset=0, yOffset=0):
|
||||
drawchar = self.drawchar
|
||||
for ch in astring:
|
||||
xOffset = drawchar(ch, destbits, xOffset, yOffset)
|
||||
return xOffset
|
||||
|
||||
def drawchar(self, ch, destbits, xOffset, yOffset=0):
|
||||
import Qd
|
||||
width, bounds, destbounds = self.getcharbounds(ch)
|
||||
destbounds = Qd.OffsetRect(destbounds, xOffset, yOffset)
|
||||
Qd.CopyBits(self.bitImage, destbits, bounds, destbounds, 1, None)
|
||||
return xOffset + width
|
||||
|
||||
def stringwidth(self, astring):
|
||||
charwidth = self.charwidth
|
||||
width = 0
|
||||
for ch in astring:
|
||||
width = width + charwidth(ch)
|
||||
return width
|
||||
|
||||
def charwidth(self, ch):
|
||||
cindex = byteord(ch) - self.firstChar
|
||||
if cindex > self.lastChar or \
|
||||
(self.offsetTable[cindex] == 255 and self.widthTable[cindex] == 255):
|
||||
cindex = -2 # missing char
|
||||
return self.widthTable[cindex]
|
||||
|
||||
def getcharbounds(self, ch):
|
||||
cindex = byteord(ch) - self.firstChar
|
||||
if cindex > self.lastChar or \
|
||||
(self.offsetTable[cindex] == 255 and self.widthTable[cindex] == 255):
|
||||
return self.getcharboundsindex(-2) # missing char
|
||||
return self.getcharboundsindex(cindex)
|
||||
|
||||
def getcharboundsindex(self, cindex):
|
||||
offset = self.offsetTable[cindex]
|
||||
width = self.widthTable[cindex]
|
||||
if offset == 255 and width == 255:
|
||||
raise ValueError("character not defined")
|
||||
location0 = self.locTable[cindex]
|
||||
location1 = self.locTable[cindex + 1]
|
||||
srcbounds = (location0, 0, location1, self.fRectHeight)
|
||||
destbounds = ( offset + self.kernMax,
|
||||
0,
|
||||
offset + self.kernMax + location1 - location0,
|
||||
self.fRectHeight )
|
||||
return width, srcbounds, destbounds
|
||||
|
||||
|
||||
class Glyph(object):
|
||||
|
||||
def __init__(self, width, offset, pixels=None, pixelDepth=1):
|
||||
self.width = width
|
||||
self.offset = offset
|
||||
self.pixelDepth = pixelDepth
|
||||
self.pixels = pixels
|
||||
|
||||
|
||||
def dataFromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
|
||||
from Carbon import Res
|
||||
resref = Res.FSOpenResFile(pathOrFSSpec, 1) # readonly
|
||||
try:
|
||||
Res.UseResFile(resref)
|
||||
if not nameOrID:
|
||||
# just take the first in the file
|
||||
res = Res.Get1IndResource(resType, 1)
|
||||
elif isinstance(nameOrID, int):
|
||||
res = Res.Get1Resource(resType, nameOrID)
|
||||
else:
|
||||
res = Res.Get1NamedResource(resType, nameOrID)
|
||||
theID, theType, name = res.GetResInfo()
|
||||
data = res.data
|
||||
finally:
|
||||
Res.CloseResFile(resref)
|
||||
return data
|
||||
|
||||
|
||||
def fromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
|
||||
data = dataFromFile(pathOrFSSpec, nameOrID, resType)
|
||||
return NFNT(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import EasyDialogs
|
||||
path = EasyDialogs.AskFileForOpen()
|
||||
if path:
|
||||
data = dataFromFile(path)
|
||||
font = NFNT(data)
|
||||
font.unpackGlyphs()
|
||||
font.packGlyphs()
|
||||
data2 = font.compile()
|
||||
print("xxxxx", data == data2, len(data) == len(data2))
|
Loading…
x
Reference in New Issue
Block a user