New & improved, but dead slow. Reads and writes.
git-svn-id: svn://svn.code.sf.net/p/fonttools/code/trunk@130 4cde692c-a291-49d1-8350-778aa11640f8
This commit is contained in:
parent
a556f51db5
commit
ad6bb5a352
@ -1,81 +1,225 @@
|
|||||||
import Res
|
import struct, sstruct
|
||||||
import macfs
|
import string
|
||||||
import struct
|
import types
|
||||||
import Qd
|
|
||||||
from types import *
|
|
||||||
|
# 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:
|
class NFNT:
|
||||||
|
|
||||||
def __init__(self, nfnt, name = "", _type = 'NFNT'):
|
def __init__(self, data=None):
|
||||||
if type(nfnt) == type(Res.Resource("")):
|
if data is not None:
|
||||||
theID, theType, name = nfnt.GetResInfo()
|
self.decompile(data)
|
||||||
if theType <> _type:
|
|
||||||
raise TypeError, 'resource of wrong type; expected ' + _type
|
|
||||||
data = nfnt.data
|
|
||||||
elif type(nfnt) == StringType:
|
|
||||||
fss = macfs.FSSpec(nfnt)
|
|
||||||
data = readnfntresource(nfnt, name, _type)
|
|
||||||
elif type(nfnt) == type(macfs.FSSpec(':')):
|
|
||||||
data = readnfntresource(nfnt, name, _type)
|
|
||||||
else:
|
|
||||||
raise TypeError, 'expected resource, string or fss; found ' + type(nfnt).__name__
|
|
||||||
self.parse_nfnt(data)
|
|
||||||
|
|
||||||
def parse_nfnt(self, data):
|
def decompile(self, data):
|
||||||
# header; FontRec
|
# header; FontRec
|
||||||
( self.fontType,
|
sstruct.unpack(nfntHeaderFormat, data[:headerSize], self)
|
||||||
self.firstChar,
|
|
||||||
self.lastChar,
|
#assert self.fRectHeight == (self.ascent + self.descent)
|
||||||
self.widMax,
|
|
||||||
self.kernMax,
|
|
||||||
self.nDescent,
|
|
||||||
fRectWidth,
|
|
||||||
self.fRectHeight,
|
|
||||||
owTLoc,
|
|
||||||
self.ascent,
|
|
||||||
self.descent,
|
|
||||||
self.leading,
|
|
||||||
self.rowWords ) = struct.unpack("13h", data[:26])
|
|
||||||
if owTLoc < 0:
|
|
||||||
owTLoc = owTLoc + 0x8000 # unsigned short
|
|
||||||
|
|
||||||
# rest
|
# rest
|
||||||
tablesize = 2 * (self.lastChar - self.firstChar + 3)
|
tableSize = 2 * (self.lastChar - self.firstChar + 3)
|
||||||
bitmapsize = 2 * self.rowWords * self.fRectHeight
|
bitmapSize = 2 * self.rowWords * self.fRectHeight
|
||||||
|
|
||||||
self.bits = data[26:26 + bitmapsize]
|
self.bits = data[headerSize:headerSize + bitmapSize]
|
||||||
self.bitImage = Qd.BitMap(self.bits, 2 * self.rowWords, (0, 0, self.rowWords * 16, self.fRectHeight))
|
|
||||||
|
|
||||||
owTable = data[26 + bitmapsize + tablesize:26 + bitmapsize + 2 * tablesize]
|
# XXX deal with self.nDescent being a positive number
|
||||||
if len(owTable) <> tablesize:
|
assert (headerSize + bitmapSize + tableSize - 16) / 2 == self.owTLoc # ugh...
|
||||||
raise ValueError, 'invalid NFNT resource'
|
|
||||||
|
|
||||||
locTable = data[26 + bitmapsize:26 + bitmapsize + tablesize]
|
locTable = data[headerSize + bitmapSize:headerSize + bitmapSize + tableSize]
|
||||||
if len(locTable) <> tablesize:
|
if len(locTable) <> tableSize:
|
||||||
raise ValueError, 'invalid NFNT resource'
|
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
|
# fill tables
|
||||||
self.offsettable = []
|
self.offsetTable = []
|
||||||
self.widthtable = []
|
self.widthTable = []
|
||||||
self.locationtable = []
|
self.locTable = []
|
||||||
for i in range(0, tablesize, 2):
|
for i in range(0, tableSize, 2):
|
||||||
self.offsettable.append(ord(owTable[i]))
|
self.offsetTable.append(ord(owTable[i]))
|
||||||
self.widthtable.append(ord(owTable[i+1]))
|
self.widthTable.append(ord(owTable[i+1]))
|
||||||
loc, = struct.unpack('h', locTable[i:i+2])
|
loc, = struct.unpack("h", locTable[i:i+2])
|
||||||
self.locationtable.append(loc)
|
self.locTable.append(loc)
|
||||||
|
|
||||||
def drawstring(self, astring, destbits, xoffset = 0, yoffset = 0):
|
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] = chr(self.offsetTable[i]) + chr(self.widthTable[i])
|
||||||
|
locTable[i] = struct.pack("h", self.locTable[i])
|
||||||
|
owTable = string.join(owTable, "")
|
||||||
|
locTable = string.join(locTable, "")
|
||||||
|
assert len(locTable) == len(owTable) == 2 * (self.lastChar - self.firstChar + 3)
|
||||||
|
return header + self.bits + locTable + owTable
|
||||||
|
|
||||||
|
def unpackGlyphs(self):
|
||||||
|
import Numeric
|
||||||
|
nGlyphs = len(self.locTable) - 1
|
||||||
|
self.glyphs = [None] * nGlyphs
|
||||||
|
|
||||||
|
rowBytes = self.rowWords * 2
|
||||||
|
imageWidth = self.rowWords * 16
|
||||||
|
imageHeight = self.fRectHeight
|
||||||
|
bits = self.bits
|
||||||
|
bitImage = Numeric.zeros((imageWidth, imageHeight), Numeric.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 = (ord(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 Numeric
|
||||||
|
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)
|
||||||
|
|
||||||
|
imageWidth = 16 * ((imageWidth - 1) / 16 + 1)
|
||||||
|
rowBytes = imageWidth / 8
|
||||||
|
rowWords = rowBytes / 2
|
||||||
|
bitImage = Numeric.zeros((imageWidth, imageHeight), Numeric.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(chr(byte))
|
||||||
|
bits = string.join(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
|
drawchar = self.drawchar
|
||||||
for ch in astring:
|
for ch in astring:
|
||||||
xoffset = drawchar(ch, destbits, xoffset, yoffset)
|
xOffset = drawchar(ch, destbits, xOffset, yOffset)
|
||||||
return xoffset
|
return xOffset
|
||||||
|
|
||||||
def drawchar(self, ch, destbits, xoffset, yoffset = 0):
|
def drawchar(self, ch, destbits, xOffset, yOffset=0):
|
||||||
|
import Qd
|
||||||
width, bounds, destbounds = self.getcharbounds(ch)
|
width, bounds, destbounds = self.getcharbounds(ch)
|
||||||
destbounds = Qd.OffsetRect(destbounds, xoffset, yoffset)
|
destbounds = Qd.OffsetRect(destbounds, xOffset, yOffset)
|
||||||
Qd.CopyBits(self.bitImage, destbits, bounds, destbounds, 1, None)
|
Qd.CopyBits(self.bitImage, destbits, bounds, destbounds, 1, None)
|
||||||
return xoffset + width
|
return xOffset + width
|
||||||
|
|
||||||
def stringwidth(self, astring):
|
def stringwidth(self, astring):
|
||||||
charwidth = self.charwidth
|
charwidth = self.charwidth
|
||||||
@ -87,24 +231,24 @@ class NFNT:
|
|||||||
def charwidth(self, ch):
|
def charwidth(self, ch):
|
||||||
cindex = ord(ch) - self.firstChar
|
cindex = ord(ch) - self.firstChar
|
||||||
if cindex > self.lastChar or \
|
if cindex > self.lastChar or \
|
||||||
(self.offsettable[cindex] == 255 and self.widthtable[cindex] == 255):
|
(self.offsetTable[cindex] == 255 and self.widthTable[cindex] == 255):
|
||||||
cindex = -2 # missing char
|
cindex = -2 # missing char
|
||||||
return self.widthtable[cindex]
|
return self.widthTable[cindex]
|
||||||
|
|
||||||
def getcharbounds(self, ch):
|
def getcharbounds(self, ch):
|
||||||
cindex = ord(ch) - self.firstChar
|
cindex = ord(ch) - self.firstChar
|
||||||
if cindex > self.lastChar or \
|
if cindex > self.lastChar or \
|
||||||
(self.offsettable[cindex] == 255 and self.widthtable[cindex] == 255):
|
(self.offsetTable[cindex] == 255 and self.widthTable[cindex] == 255):
|
||||||
return self.getcharboundsindex(-2) # missing char
|
return self.getcharboundsindex(-2) # missing char
|
||||||
return self.getcharboundsindex(cindex)
|
return self.getcharboundsindex(cindex)
|
||||||
|
|
||||||
def getcharboundsindex(self, cindex):
|
def getcharboundsindex(self, cindex):
|
||||||
offset = self.offsettable[cindex]
|
offset = self.offsetTable[cindex]
|
||||||
width = self.widthtable[cindex]
|
width = self.widthTable[cindex]
|
||||||
if offset == 255 and width == 255:
|
if offset == 255 and width == 255:
|
||||||
raise ValueError, "character not defined"
|
raise ValueError, "character not defined"
|
||||||
location0 = self.locationtable[cindex]
|
location0 = self.locTable[cindex]
|
||||||
location1 = self.locationtable[cindex + 1]
|
location1 = self.locTable[cindex + 1]
|
||||||
srcbounds = (location0, 0, location1, self.fRectHeight)
|
srcbounds = (location0, 0, location1, self.fRectHeight)
|
||||||
destbounds = ( offset + self.kernMax,
|
destbounds = ( offset + self.kernMax,
|
||||||
0,
|
0,
|
||||||
@ -113,18 +257,51 @@ class NFNT:
|
|||||||
return width, srcbounds, destbounds
|
return width, srcbounds, destbounds
|
||||||
|
|
||||||
|
|
||||||
def readnfntresource(fss, name, _type = 'NFNT'):
|
class Glyph:
|
||||||
|
|
||||||
|
def __init__(self, width, offset, pixels=None, pixelDepth=1):
|
||||||
|
import Numeric
|
||||||
|
self.width = width
|
||||||
|
self.offset = offset
|
||||||
|
self.pixelDepth = pixelDepth
|
||||||
|
self.pixels = pixels
|
||||||
|
|
||||||
|
|
||||||
|
def dataFromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
|
||||||
|
import Res, macfs
|
||||||
|
if type(pathOrFSSpec) == types.StringType:
|
||||||
|
fss = macfs.FSSpec(pathOrFSSpec)
|
||||||
|
else:
|
||||||
|
fss = pathOrFSSpec
|
||||||
resref = Res.FSpOpenResFile(fss, 1) # readonly
|
resref = Res.FSpOpenResFile(fss, 1) # readonly
|
||||||
Res.UseResFile(resref)
|
|
||||||
try:
|
try:
|
||||||
if name:
|
Res.UseResFile(resref)
|
||||||
res = Res.Get1NamedResource(_type, name)
|
if not nameOrID:
|
||||||
else:
|
|
||||||
# just take the first in the file
|
# just take the first in the file
|
||||||
res = Res.Get1IndResource(_type, 1)
|
res = Res.Get1IndResource(resType, 1)
|
||||||
|
elif type(nameOrID) == types.IntType:
|
||||||
|
res = Res.Get1Resource(resType, nameOrID)
|
||||||
|
else:
|
||||||
|
res = Res.Get1NamedResource(resType, nameOrID)
|
||||||
theID, theType, name = res.GetResInfo()
|
theID, theType, name = res.GetResInfo()
|
||||||
data = res.data
|
data = res.data
|
||||||
finally:
|
finally:
|
||||||
Res.CloseResFile(resref)
|
Res.CloseResFile(resref)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def fromFile(pathOrFSSpec, nameOrID="", resType='NFNT'):
|
||||||
|
data = dataFromFile(pathOrFSSpec, nameOrID, resType)
|
||||||
|
return NFNT(data)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import macfs
|
||||||
|
fss, ok = macfs.StandardGetFile('FFIL')
|
||||||
|
if ok:
|
||||||
|
data = dataFromFile(fss)
|
||||||
|
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