Skip to content

Instantly share code, notes, and snippets.

@ebraminio
Last active April 12, 2018 12:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ebraminio/3b1c91ce605cc93dba59d984ecece7b7 to your computer and use it in GitHub Desktop.
Save ebraminio/3b1c91ce605cc93dba59d984ecece7b7 to your computer and use it in GitHub Desktop.
dfont and resourcefork parsing, ported fonttools code
# Derived from fonttools, all rights of this goes to fonttools projects
from fontTools.misc import sstruct
import struct
ResourceForkHeader = """
> # big endian
dataOffset: L
mapOffset: L
dataLen: L
mapLen: L
"""
ResourceForkHeaderSize = sstruct.calcsize(ResourceForkHeader)
ResourceMapHeader = """
> # big endian
attr: H
typeListOffset: H
nameListOffset: H
"""
ResourceMapHeaderSize = sstruct.calcsize(ResourceMapHeader)
ResourceTypeItem = """
> # big endian
type: 4s
numRes: H
refListOffset: H
"""
ResourceTypeItemSize = sstruct.calcsize(ResourceTypeItem)
ResourceRefItem = """
> # big endian
id: h
nameOffset: h
attr: B
dataOffset: 3s
reserved: L
"""
ResourceRefItemSize = sstruct.calcsize(ResourceRefItem)
from fontTools.misc.py23 import *
import struct
from fontTools.misc import sstruct
from collections import OrderedDict
try:
from collections.abc import MutableMapping
except ImportError:
from UserDict import DictMixin as MutableMapping
_resources = OrderedDict()
with open('/System/Library/Fonts/Courier.dfont', 'rb') as datafork:
data = datafork.read()
file = BytesIO(data)
# _readHeaderAndMap
file.seek(0)
headerData = file.read(ResourceForkHeaderSize)
header = sstruct.unpack(ResourceForkHeader, headerData)
# seek to resource map, skip reserved
mapOffset = header['mapOffset'] + 22
print (mapOffset)
file.seek(mapOffset)
resourceMapData = file.read(ResourceMapHeaderSize)
resourceMap = sstruct.unpack(ResourceMapHeader, resourceMapData)
absTypeListOffset = header['mapOffset'] + resourceMap['typeListOffset']
absNameListOffset = header['mapOffset'] + resourceMap['nameListOffset']
dataOffset = header['dataOffset']
# _readTypeList
file.seek(absTypeListOffset)
numTypesData = file.read(2)
numTypes, = struct.unpack('>H', numTypesData)
print ('numTypes:', numTypes)
absTypeListOffset2 = absTypeListOffset + 2
for i in range(numTypes + 1):
resTypeItemOffset = absTypeListOffset2 + ResourceTypeItemSize * i
file.seek(resTypeItemOffset)
resTypeItemData = file.read(ResourceTypeItemSize)
item = sstruct.unpack(ResourceTypeItem, resTypeItemData)
print ('Item: ', item)
resType = tostr(item['type'], encoding='mac-roman')
refListOffset = absTypeListOffset + item['refListOffset']
numRes = item['numRes'] + 1
# _readReferenceList
resources = []
for i in range(numRes):
refOffset = refListOffset + ResourceRefItemSize * i
file.seek(refOffset)
refData = file.read(ResourceRefItemSize)
#res = Resource(resType).decompile(refData, self)
resItem = sstruct.unpack(ResourceRefItem, refData)
# interpret 3-byte dataOffset as (padded) ULONG to unpack it with struct
dataOffset2, = struct.unpack('>L', bytesjoin([b"\0", resItem['dataOffset']]))
absDataOffset = dataOffset + dataOffset2
file.seek(absDataOffset)
dataLength, = struct.unpack(">L", file.read(4))
data = file.read(dataLength)
print ('res:', resItem)
res = {"type": resType, "data": data, "attr": resItem['attr'], "id": resItem['id']}
if resItem['nameOffset'] == -1:
resources.append(res)
continue
absNameOffset = absNameListOffset + resItem['nameOffset']
file.seek(absNameOffset)
nameLength, = struct.unpack('B', file.read(1))
name, = struct.unpack('>%ss' % nameLength, file.read(nameLength))
res['name'] = tostr(name, encoding='mac-roman')
resources.append(res)
_resources[resType] = resources
#_resources['sfnt'][0]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment