Updated fpdf to the last version (py3 compatible), fix contrib/appconfig

This commit is contained in:
ilvalle
2016-06-04 09:23:31 +02:00
parent 2f7d76769c
commit dcd24cf88c
12 changed files with 1087 additions and 937 deletions
+358 -366
View File
@@ -16,10 +16,13 @@
#
#******************************************************************************
from __future__ import with_statement
from struct import pack, unpack, unpack_from
import re
import warnings
from php import die, substr, str_repeat, str_pad, strlen, count
from .php import die, substr, str_repeat, str_pad, strlen, count
from .py3k import b, ord
# Define the value used in the "head" table of a created TTF file
@@ -55,7 +58,7 @@ def sub32(x, y):
def calcChecksum(data):
if (strlen(data) % 4):
data += str_repeat("\0", (4-(len(data) % 4)))
data += str_repeat(b("\0"), (4-(len(data) % 4)))
hi=0x0000
lo=0x0000
for i in range(0, len(data), 4):
@@ -74,26 +77,25 @@ class TTFontFile:
def getMetrics(self, file):
self.filename = file
self.fh = open(file,'rb')
self._pos = 0
self.charWidths = []
self.glyphPos = {}
self.charToGlyph = {}
self.tables = {}
self.otables = {}
self.ascent = 0
self.descent = 0
self.TTCFonts = {}
self.version = version = self.read_ulong()
if (version==0x4F54544F):
die("Postscript outlines are not supported")
if (version==0x74746366):
die("ERROR - TrueType Fonts Collections not supported")
if (version not in (0x00010000,0x74727565)):
die("Not a TrueType font: version=" + version)
self.readTableDirectory()
self.extractInfo()
self.fh.close()
with open(file,'rb') as self.fh:
self._pos = 0
self.charWidths = []
self.glyphPos = {}
self.charToGlyph = {}
self.tables = {}
self.otables = {}
self.ascent = 0
self.descent = 0
self.TTCFonts = {}
self.version = version = self.read_ulong()
if (version==0x4F54544F):
die("Postscript outlines are not supported")
if (version==0x74746366):
die("ERROR - TrueType Fonts Collections not supported")
if (version not in (0x00010000,0x74727565)):
die("Not a TrueType font: version=" + str(version))
self.readTableDirectory()
self.extractInfo()
def readTableDirectory(self, ):
self.numTables = self.read_ushort()
@@ -130,7 +132,7 @@ class TTFontFile:
def read_tag(self):
self._pos += 4
return self.fh.read(4)
return self.fh.read(4).decode("latin1")
def read_short(self):
self._pos += 2
@@ -204,7 +206,7 @@ class TTFontFile:
def add(self, tag, data):
if (tag == 'head') :
data = self.splice(data, 8, "\0\0\0\0")
data = self.splice(data, 8, b("\0\0\0\0"))
self.otables[tag] = data
############################################/
@@ -226,7 +228,7 @@ class TTFontFile:
numRecords = self.read_ushort()
string_data_offset = name_offset + self.read_ushort()
names = {1:'',2:'',3:'',4:'',6:''}
K = names.keys()
K = list(names.keys())
nameCount = len(names)
for i in range(numRecords):
platformId = self.read_ushort()
@@ -242,7 +244,7 @@ class TTFontFile:
self.seek(string_data_offset + offset)
if (length % 2 != 0):
die("PostScript name is UTF-16BE string of odd length")
length /= 2
length //= 2
N = ''
while (length > 0):
char = self.read_ushort()
@@ -253,7 +255,7 @@ class TTFontFile:
elif (platformId == 1 and encodingId == 0 and languageId == 0): # Macintosh, Roman, English, PS Name
opos = self._pos
N = self.get_chunk(string_data_offset + offset, length)
N = self.get_chunk(string_data_offset + offset, length).decode("latin1")
self._pos = opos
self.seek(opos)
@@ -455,346 +457,344 @@ class TTFontFile:
def makeSubset(self, file, subset):
self.filename = file
self.fh = open(file ,'rb')
self._pos = 0
self.charWidths = []
self.glyphPos = {}
self.charToGlyph = {}
self.tables = {}
self.otables = {}
self.ascent = 0
self.descent = 0
self.skip(4)
self.maxUni = 0
self.readTableDirectory()
with open(file ,'rb') as self.fh:
self._pos = 0
self.charWidths = []
self.glyphPos = {}
self.charToGlyph = {}
self.tables = {}
self.otables = {}
self.ascent = 0
self.descent = 0
self.skip(4)
self.maxUni = 0
self.readTableDirectory()
#################/
# head - Font header table
#################/
self.seek_table("head")
self.skip(50)
indexToLocFormat = self.read_ushort()
glyphDataFormat = self.read_ushort()
#################/
# head - Font header table
#################/
self.seek_table("head")
self.skip(50)
indexToLocFormat = self.read_ushort()
glyphDataFormat = self.read_ushort()
#################/
# hhea - Horizontal header table
#################/
self.seek_table("hhea")
self.skip(32)
metricDataFormat = self.read_ushort()
orignHmetrics = numberOfHMetrics = self.read_ushort()
#################/
# hhea - Horizontal header table
#################/
self.seek_table("hhea")
self.skip(32)
metricDataFormat = self.read_ushort()
orignHmetrics = numberOfHMetrics = self.read_ushort()
#################/
# maxp - Maximum profile table
#################/
self.seek_table("maxp")
self.skip(4)
numGlyphs = self.read_ushort()
#################/
# maxp - Maximum profile table
#################/
self.seek_table("maxp")
self.skip(4)
numGlyphs = self.read_ushort()
#################/
# cmap - Character to glyph index mapping table
#################/
cmap_offset = self.seek_table("cmap")
self.skip(2)
cmapTableCount = self.read_ushort()
unicode_cmap_offset = 0
unicode_cmap_offset12 = 0
for i in range(cmapTableCount):
platformID = self.read_ushort()
encodingID = self.read_ushort()
offset = self.read_ulong()
save_pos = self._pos
if platformID == 3 and encodingID == 10: # Microsoft, UCS-4
format = self.get_ushort(cmap_offset + offset)
if (format == 12):
if not unicode_cmap_offset12:
unicode_cmap_offset12 = cmap_offset + offset
break
if ((platformID == 3 and encodingID == 1) or platformID == 0): # Microsoft, Unicode
format = self.get_ushort(cmap_offset + offset)
if (format == 4):
unicode_cmap_offset = cmap_offset + offset
break
#################/
# cmap - Character to glyph index mapping table
#################/
cmap_offset = self.seek_table("cmap")
self.skip(2)
cmapTableCount = self.read_ushort()
unicode_cmap_offset = 0
unicode_cmap_offset12 = 0
for i in range(cmapTableCount):
platformID = self.read_ushort()
encodingID = self.read_ushort()
offset = self.read_ulong()
save_pos = self._pos
if platformID == 3 and encodingID == 10: # Microsoft, UCS-4
format = self.get_ushort(cmap_offset + offset)
if (format == 12):
if not unicode_cmap_offset12:
unicode_cmap_offset12 = cmap_offset + offset
break
if ((platformID == 3 and encodingID == 1) or platformID == 0): # Microsoft, Unicode
format = self.get_ushort(cmap_offset + offset)
if (format == 4):
unicode_cmap_offset = cmap_offset + offset
break
self.seek(save_pos )
if not unicode_cmap_offset and not unicode_cmap_offset12:
die('Font (' + self.filename + ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 3, encoding 10, format 12, or platform 0, any encoding, format 4)')
self.seek(save_pos )
if not unicode_cmap_offset and not unicode_cmap_offset12:
die('Font (' + self.filename + ') does not have cmap for Unicode (platform 3, encoding 1, format 4, or platform 3, encoding 10, format 12, or platform 0, any encoding, format 4)')
glyphToChar = {}
charToGlyph = {}
if unicode_cmap_offset12:
self.getCMAP12(unicode_cmap_offset12, glyphToChar, charToGlyph)
else:
self.getCMAP4(unicode_cmap_offset, glyphToChar, charToGlyph)
glyphToChar = {}
charToGlyph = {}
if unicode_cmap_offset12:
self.getCMAP12(unicode_cmap_offset12, glyphToChar, charToGlyph)
else:
self.getCMAP4(unicode_cmap_offset, glyphToChar, charToGlyph)
self.charToGlyph = charToGlyph
self.charToGlyph = charToGlyph
#################/
# hmtx - Horizontal metrics table
#################/
scale = 1 # not used
self.getHMTX(numberOfHMetrics, numGlyphs, glyphToChar, scale)
#################/
# hmtx - Horizontal metrics table
#################/
scale = 1 # not used
self.getHMTX(numberOfHMetrics, numGlyphs, glyphToChar, scale)
#################/
# loca - Index to location
#################/
self.getLOCA(indexToLocFormat, numGlyphs)
#################/
# loca - Index to location
#################/
self.getLOCA(indexToLocFormat, numGlyphs)
subsetglyphs = [(0, 0)] # special "sorted dict"!
subsetCharToGlyph = {}
for code in subset:
if (code in self.charToGlyph):
if (self.charToGlyph[code], code) not in subsetglyphs:
subsetglyphs.append((self.charToGlyph[code], code)) # Old Glyph ID => Unicode
subsetCharToGlyph[code] = self.charToGlyph[code] # Unicode to old GlyphID
self.maxUni = max(self.maxUni, code)
(start,dummy) = self.get_table_pos('glyf')
subsetglyphs = [(0, 0)] # special "sorted dict"!
subsetCharToGlyph = {}
for code in subset:
if (code in self.charToGlyph):
if (self.charToGlyph[code], code) not in subsetglyphs:
subsetglyphs.append((self.charToGlyph[code], code)) # Old Glyph ID => Unicode
subsetCharToGlyph[code] = self.charToGlyph[code] # Unicode to old GlyphID
self.maxUni = max(self.maxUni, code)
(start,dummy) = self.get_table_pos('glyf')
subsetglyphs.sort()
glyphSet = {}
n = 0
fsLastCharIndex = 0 # maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1.
for originalGlyphIdx, uni in subsetglyphs:
fsLastCharIndex = max(fsLastCharIndex , uni)
glyphSet[originalGlyphIdx] = n # old glyphID to new glyphID
n += 1
subsetglyphs.sort()
glyphSet = {}
n = 0
fsLastCharIndex = 0 # maximum Unicode index (character code) in this font, according to the cmap subtable for platform ID 3 and platform- specific encoding ID 0 or 1.
for originalGlyphIdx, uni in subsetglyphs:
fsLastCharIndex = max(fsLastCharIndex , uni)
glyphSet[originalGlyphIdx] = n # old glyphID to new glyphID
n += 1
codeToGlyph = {}
for uni, originalGlyphIdx in sorted(subsetCharToGlyph.items()):
codeToGlyph[uni] = glyphSet[originalGlyphIdx]
self.codeToGlyph = codeToGlyph
for originalGlyphIdx, uni in subsetglyphs:
nonlocals = {'start': start, 'glyphSet': glyphSet,
'subsetglyphs': subsetglyphs}
self.getGlyphs(originalGlyphIdx, nonlocals)
codeToGlyph = {}
for uni, originalGlyphIdx in sorted(subsetCharToGlyph.items()):
codeToGlyph[uni] = glyphSet[originalGlyphIdx]
self.codeToGlyph = codeToGlyph
for originalGlyphIdx, uni in subsetglyphs:
nonlocals = {'start': start, 'glyphSet': glyphSet,
'subsetglyphs': subsetglyphs}
self.getGlyphs(originalGlyphIdx, nonlocals)
numGlyphs = numberOfHMetrics = len(subsetglyphs)
numGlyphs = numberOfHMetrics = len(subsetglyphs)
#tables copied from the original
tags = ['name']
for tag in tags:
self.add(tag, self.get_table(tag))
tags = ['cvt ', 'fpgm', 'prep', 'gasp']
for tag in tags:
if (tag in self.tables):
self.add(tag, self.get_table(tag))
#tables copied from the original
tags = ['name']
for tag in tags:
self.add(tag, self.get_table(tag))
tags = ['cvt ', 'fpgm', 'prep', 'gasp']
for tag in tags:
if (tag in self.tables):
self.add(tag, self.get_table(tag))
# post - PostScript
opost = self.get_table('post')
post = "\x00\x03\x00\x00" + substr(opost,4,12) + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
self.add('post', post)
# post - PostScript
opost = self.get_table('post')
post = b("\x00\x03\x00\x00") + substr(opost,4,12) + b("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
self.add('post', post)
# Sort CID2GID map into segments of contiguous codes
if 0 in codeToGlyph:
del codeToGlyph[0]
#unset(codeToGlyph[65535])
rangeid = 0
range_ = {}
prevcid = -2
prevglidx = -1
# for each character
for cid, glidx in sorted(codeToGlyph.items()):
if (cid == (prevcid + 1) and glidx == (prevglidx + 1)):
range_[rangeid].append(glidx)
else:
# new range
rangeid = cid
range_[rangeid] = []
range_[rangeid].append(glidx)
prevcid = cid
prevglidx = glidx
# Sort CID2GID map into segments of contiguous codes
if 0 in codeToGlyph:
del codeToGlyph[0]
#unset(codeToGlyph[65535])
rangeid = 0
range_ = {}
prevcid = -2
prevglidx = -1
# for each character
for cid, glidx in sorted(codeToGlyph.items()):
if (cid == (prevcid + 1) and glidx == (prevglidx + 1)):
range_[rangeid].append(glidx)
else:
# new range
rangeid = cid
range_[rangeid] = []
range_[rangeid].append(glidx)
prevcid = cid
prevglidx = glidx
# cmap - Character to glyph mapping - Format 4 (MS / )
segCount = len(range_) + 1 # + 1 Last segment has missing character 0xFFFF
searchRange = 1
entrySelector = 0
while (searchRange * 2 <= segCount ):
# cmap - Character to glyph mapping - Format 4 (MS / )
segCount = len(range_) + 1 # + 1 Last segment has missing character 0xFFFF
searchRange = 1
entrySelector = 0
while (searchRange * 2 <= segCount ):
searchRange = searchRange * 2
entrySelector = entrySelector + 1
searchRange = searchRange * 2
entrySelector = entrySelector + 1
searchRange = searchRange * 2
rangeShift = segCount * 2 - searchRange
length = 16 + (8*segCount ) + (numGlyphs+1)
cmap = [0, 1, # Index : version, number of encoding subtables
3, 1, # Encoding Subtable : platform (MS=3), encoding (Unicode)
0, 12, # Encoding Subtable : offset (hi,lo)
4, length, 0, # Format 4 Mapping subtable: format, length, language
segCount*2,
searchRange,
entrySelector,
rangeShift]
rangeShift = segCount * 2 - searchRange
length = 16 + (8*segCount ) + (numGlyphs+1)
cmap = [0, 1, # Index : version, number of encoding subtables
3, 1, # Encoding Subtable : platform (MS=3), encoding (Unicode)
0, 12, # Encoding Subtable : offset (hi,lo)
4, length, 0, # Format 4 Mapping subtable: format, length, language
segCount*2,
searchRange,
entrySelector,
rangeShift]
range_ = sorted(range_.items())
# endCode(s)
for start, subrange in range_:
endCode = start + (len(subrange)-1)
cmap.append(endCode) # endCode(s)
cmap.append(0xFFFF) # endCode of last Segment
cmap.append(0) # reservedPad
range_ = sorted(range_.items())
# endCode(s)
for start, subrange in range_:
endCode = start + (len(subrange)-1)
cmap.append(endCode) # endCode(s)
cmap.append(0xFFFF) # endCode of last Segment
cmap.append(0) # reservedPad
# startCode(s)
for start, subrange in range_:
cmap.append(start) # startCode(s)
cmap.append(0xFFFF) # startCode of last Segment
# idDelta(s)
for start, subrange in range_:
idDelta = -(start-subrange[0])
n += count(subrange)
cmap.append(idDelta) # idDelta(s)
cmap.append(1) # idDelta of last Segment
# idRangeOffset(s)
for subrange in range_:
cmap.append(0) # idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
cmap.append(0) # idRangeOffset of last Segment
for subrange, glidx in range_:
cmap.extend(glidx)
cmap.append(0) # Mapping for last character
cmapstr = ''
for cm in cmap:
if cm >= 0:
cmapstr += pack(">H", cm)
else:
# startCode(s)
for start, subrange in range_:
cmap.append(start) # startCode(s)
cmap.append(0xFFFF) # startCode of last Segment
# idDelta(s)
for start, subrange in range_:
idDelta = -(start-subrange[0])
n += count(subrange)
cmap.append(idDelta) # idDelta(s)
cmap.append(1) # idDelta of last Segment
# idRangeOffset(s)
for subrange in range_:
cmap.append(0) # idRangeOffset[segCount] Offset in bytes to glyph indexArray, or 0
cmap.append(0) # idRangeOffset of last Segment
for subrange, glidx in range_:
cmap.extend(glidx)
cmap.append(0) # Mapping for last character
cmapstr = b('')
for cm in cmap:
if cm >= 0:
cmapstr += pack(">H", cm)
else:
try:
cmapstr += pack(">h", cm)
except:
warnings.warn("cmap value too big/small: %s" % cm)
cmapstr += pack(">H", -cm)
self.add('cmap', cmapstr)
# glyf - Glyph data
(glyfOffset,glyfLength) = self.get_table_pos('glyf')
if (glyfLength < self.maxStrLenRead):
glyphData = self.get_table('glyf')
offsets = []
glyf = b('')
pos = 0
hmtxstr = b('')
xMinT = 0
yMinT = 0
xMaxT = 0
yMaxT = 0
advanceWidthMax = 0
minLeftSideBearing = 0
minRightSideBearing = 0
xMaxExtent = 0
maxPoints = 0 # points in non-compound glyph
maxContours = 0 # contours in non-compound glyph
maxComponentPoints = 0 # points in compound glyph
maxComponentContours = 0 # contours in compound glyph
maxComponentElements = 0 # number of glyphs referenced at top level
maxComponentDepth = 0 # levels of recursion, set to 0 if font has only simple glyphs
self.glyphdata = {}
for originalGlyphIdx, uni in subsetglyphs:
# hmtx - Horizontal Metrics
hm = self.getHMetric(orignHmetrics, originalGlyphIdx)
hmtxstr += hm
offsets.append(pos)
try:
cmapstr += pack(">h", cm)
except:
warnings.warn("cmap value too big/small: %s" % cm)
cmapstr += pack(">H", -cm)
self.add('cmap', cmapstr)
glyphPos = self.glyphPos[originalGlyphIdx]
glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos
except IndexError:
warnings.warn("missing glyph %s" % (originalGlyphIdx))
glyphLen = 0
# glyf - Glyph data
(glyfOffset,glyfLength) = self.get_table_pos('glyf')
if (glyfLength < self.maxStrLenRead):
glyphData = self.get_table('glyf')
offsets = []
glyf = ''
pos = 0
hmtxstr = ''
xMinT = 0
yMinT = 0
xMaxT = 0
yMaxT = 0
advanceWidthMax = 0
minLeftSideBearing = 0
minRightSideBearing = 0
xMaxExtent = 0
maxPoints = 0 # points in non-compound glyph
maxContours = 0 # contours in non-compound glyph
maxComponentPoints = 0 # points in compound glyph
maxComponentContours = 0 # contours in compound glyph
maxComponentElements = 0 # number of glyphs referenced at top level
maxComponentDepth = 0 # levels of recursion, set to 0 if font has only simple glyphs
self.glyphdata = {}
for originalGlyphIdx, uni in subsetglyphs:
# hmtx - Horizontal Metrics
hm = self.getHMetric(orignHmetrics, originalGlyphIdx)
hmtxstr += hm
if (glyfLength < self.maxStrLenRead):
data = substr(glyphData,glyphPos,glyphLen)
else:
if (glyphLen > 0):
data = self.get_chunk(glyfOffset+glyphPos,glyphLen)
else:
data = b('')
if (glyphLen > 0):
up = unpack(">H", substr(data,0,2))[0]
if (glyphLen > 2 and (up & (1 << 15)) ): # If number of contours <= -1 i.e. composite glyph
pos_in_glyph = 10
flags = GF_MORE
nComponentElements = 0
while (flags & GF_MORE):
nComponentElements += 1 # number of glyphs referenced at top level
up = unpack(">H", substr(data,pos_in_glyph,2))
flags = up[0]
up = unpack(">H", substr(data,pos_in_glyph+2,2))
glyphIdx = up[0]
self.glyphdata.setdefault(originalGlyphIdx, {}).setdefault('compGlyphs', []).append(glyphIdx)
try:
data = self._set_ushort(data, pos_in_glyph + 2, glyphSet[glyphIdx])
except KeyError:
data = 0
warnings.warn("missing glyph data %s" % glyphIdx)
pos_in_glyph += 4
if (flags & GF_WORDS):
pos_in_glyph += 4
else:
pos_in_glyph += 2
if (flags & GF_SCALE):
pos_in_glyph += 2
elif (flags & GF_XYSCALE):
pos_in_glyph += 4
elif (flags & GF_TWOBYTWO):
pos_in_glyph += 8
maxComponentElements = max(maxComponentElements, nComponentElements)
glyf += data
pos += glyphLen
if (pos % 4 != 0):
padding = 4 - (pos % 4)
glyf += str_repeat(b("\0"),padding)
pos += padding
offsets.append(pos)
try:
glyphPos = self.glyphPos[originalGlyphIdx]
glyphLen = self.glyphPos[originalGlyphIdx + 1] - glyphPos
except IndexError:
warnings.warn("missing glyph %s" % (originalGlyphIdx))
glyphLen = 0
self.add('glyf', glyf)
if (glyfLength < self.maxStrLenRead):
data = substr(glyphData,glyphPos,glyphLen)
# hmtx - Horizontal Metrics
self.add('hmtx', hmtxstr)
# loca - Index to location
locastr = b('')
if (((pos + 1) >> 1) > 0xFFFF):
indexToLocFormat = 1 # long format
for offset in offsets:
locastr += pack(">L",offset)
else:
if (glyphLen > 0):
data = self.get_chunk(glyfOffset+glyphPos,glyphLen)
else:
data = ''
indexToLocFormat = 0 # short format
for offset in offsets:
locastr += pack(">H",offset//2)
if (glyphLen > 0):
up = unpack(">H", substr(data,0,2))[0]
if (glyphLen > 2 and (up & (1 << 15)) ): # If number of contours <= -1 i.e. composiste glyph
pos_in_glyph = 10
flags = GF_MORE
nComponentElements = 0
while (flags & GF_MORE):
nComponentElements += 1 # number of glyphs referenced at top level
up = unpack(">H", substr(data,pos_in_glyph,2))
flags = up[0]
up = unpack(">H", substr(data,pos_in_glyph+2,2))
glyphIdx = up[0]
self.glyphdata.setdefault(originalGlyphIdx, {}).setdefault('compGlyphs', []).append(glyphIdx)
try:
data = self._set_ushort(data, pos_in_glyph + 2, glyphSet[glyphIdx])
except KeyError:
data = 0
warnings.warn("missing glyph data %s" % glyphIdx)
pos_in_glyph += 4
if (flags & GF_WORDS):
pos_in_glyph += 4
else:
pos_in_glyph += 2
if (flags & GF_SCALE):
pos_in_glyph += 2
elif (flags & GF_XYSCALE):
pos_in_glyph += 4
elif (flags & GF_TWOBYTWO):
pos_in_glyph += 8
maxComponentElements = max(maxComponentElements, nComponentElements)
glyf += data
pos += glyphLen
if (pos % 4 != 0):
padding = 4 - (pos % 4)
glyf += str_repeat("\0",padding)
pos += padding
self.add('loca', locastr)
offsets.append(pos)
self.add('glyf', glyf)
# head - Font header
head = self.get_table('head')
head = self._set_ushort(head, 50, indexToLocFormat)
self.add('head', head)
# hmtx - Horizontal Metrics
self.add('hmtx', hmtxstr)
# hhea - Horizontal Header
hhea = self.get_table('hhea')
hhea = self._set_ushort(hhea, 34, numberOfHMetrics)
self.add('hhea', hhea)
# loca - Index to location
locastr = ''
if (((pos + 1) >> 1) > 0xFFFF):
indexToLocFormat = 1 # long format
for offset in offsets:
locastr += pack(">L",offset)
else:
indexToLocFormat = 0 # short format
for offset in offsets:
locastr += pack(">H",(offset/2))
self.add('loca', locastr)
# maxp - Maximum Profile
maxp = self.get_table('maxp')
maxp = self._set_ushort(maxp, 4, numGlyphs)
self.add('maxp', maxp)
# head - Font header
head = self.get_table('head')
head = self._set_ushort(head, 50, indexToLocFormat)
self.add('head', head)
# hhea - Horizontal Header
hhea = self.get_table('hhea')
hhea = self._set_ushort(hhea, 34, numberOfHMetrics)
self.add('hhea', hhea)
# maxp - Maximum Profile
maxp = self.get_table('maxp')
maxp = self._set_ushort(maxp, 4, numGlyphs)
self.add('maxp', maxp)
# OS/2 - OS/2
os2 = self.get_table('OS/2')
self.add('OS/2', os2 )
self.fh.close()
# OS/2 - OS/2
os2 = self.get_table('OS/2')
self.add('OS/2', os2 )
# Put the TTF file together
stm = self.endTTFile('')
@@ -864,11 +864,16 @@ class TTFontFile:
def getHMTX(self, numberOfHMetrics, numGlyphs, glyphToChar, scale):
start = self.seek_table("hmtx")
aw = 0
self.charWidths = [0] * 256*256*2
self.charWidths = []
def resize_cw(size, default):
size = (((size + 1) // 1024) + 1) * 1024
delta = size - len(self.charWidths)
if delta > 0:
self.charWidths += [default] * delta
nCharWidths = 0
if ((numberOfHMetrics*4) < self.maxStrLenRead):
data = self.get_chunk(start,(numberOfHMetrics*4))
arr = unpack(">" + "H" * (len(data)/2), data)
arr = unpack(">%dH" % (len(data)//2), data)
else:
self.seek(start)
for glyph in range(numberOfHMetrics):
@@ -886,26 +891,30 @@ class TTFontFile:
self.defaultWidth = scale*aw
continue
for char in glyphToChar[glyph]:
for char in glyphToChar[glyph]:
if (char != 0 and char != 65535):
w = int(round(scale*aw))
w = int(round(scale*aw+0.001)) # ROUND_HALF_UP in PY3K (like php)
if (w == 0): w = 65535
if (char < 196608):
if (char < 196608):
if char >= len(self.charWidths):
resize_cw(char, self.defaultWidth)
self.charWidths[char] = w
nCharWidths += 1
data = self.get_chunk((start+numberOfHMetrics*4),(numGlyphs*2))
arr = unpack(">" + "H" * (len(data)/2), data)
arr = unpack(">%dH" % (len(data)//2), data)
diff = numGlyphs-numberOfHMetrics
for pos in range(diff):
glyph = pos + numberOfHMetrics
if (glyph in glyphToChar):
for char in glyphToChar[glyph]:
if (char != 0 and char != 65535):
w = int(round(scale*aw))
w = int(round(scale*aw+0.001)) # ROUND_HALF_UP in PY3K (like php)
if (w == 0): w = 65535
if (char < 196608):
if char >= len(self.charWidths):
resize_cw(char, self.defaultWidth)
self.charWidths[char] = w
nCharWidths += 1
@@ -933,12 +942,12 @@ class TTFontFile:
self.glyphPos = []
if (indexToLocFormat == 0):
data = self.get_chunk(start,(numGlyphs*2)+2)
arr = unpack(">" + "H" * (len(data)/2), data)
arr = unpack(">%dH" % (len(data)//2), data)
for n in range(numGlyphs):
self.glyphPos.append((arr[n] * 2)) # n+1 !?
elif (indexToLocFormat == 1):
data = self.get_chunk(start,(numGlyphs*4)+4)
arr = unpack(">" + "L" * (len(data)/4), data)
arr = unpack(">%dL" % (len(data)//4), data)
for n in range(numGlyphs):
self.glyphPos.append((arr[n])) # n+1 !?
else:
@@ -952,7 +961,7 @@ class TTFontFile:
limit = unicode_cmap_offset + length
self.skip(2)
segCount = self.read_ushort() / 2
segCount = self.read_ushort() // 2
self.skip(6)
endCount = []
for i in range(segCount):
@@ -1020,7 +1029,7 @@ class TTFontFile:
# Put the TTF file together
def endTTFile(self, stm):
stm = ''
stm = b('')
numTables = count(self.otables)
searchRange = 1
entrySelector = 0
@@ -1046,7 +1055,7 @@ class TTFontFile:
for tag, data in sorted_tables:
if (tag == 'head'):
head_start = offset
stm += tag
stm += tag.encode("latin1")
checksum = calcChecksum(data)
stm += pack(">HH", checksum[0],checksum[1])
stm += pack(">LL", offset, strlen(data))
@@ -1055,7 +1064,7 @@ class TTFontFile:
# Table data
for tag, data in sorted_tables:
data += "\0\0\0"
data += b("\0\0\0")
stm += substr(data,0,(strlen(data)&~3))
checksum = calcChecksum(stm)
@@ -1064,20 +1073,3 @@ class TTFontFile:
stm = self.splice(stm,(head_start + 8),chk)
return stm
if __name__ == '__main__':
ttf = TTFontFile()
ttffile = 'DejaVuSansCondensed.ttf';
ttf.getMetrics(ttffile)
# test basic metrics:
assert round(ttf.descent, 0) == -236
assert round(ttf.capHeight, 0) == 928
assert ttf.flags == 4
assert [round(i, 0) for i in ttf.bbox] == [-918, -415, 1513, 1167]
assert ttf.italicAngle == 0
assert ttf.stemV == 87
assert round(ttf.defaultWidth, 0) == 540
assert round(ttf.underlinePosition, 0) == -63
assert round(ttf.underlineThickness, 0) == 44
# test char widths 8(against binary file generated by tfpdf.php):
assert ''.join(ttf.charWidths) == open("dejavusanscondensed.cw.dat").read()