Merge pull request #970 from mashabow/cff-bounds-recalc
CFF bounds recalculation
This commit is contained in:
commit
b5da19dc4b
@ -4,6 +4,7 @@ from __future__ import print_function, division, absolute_import
|
|||||||
from fontTools.misc.py23 import *
|
from fontTools.misc.py23 import *
|
||||||
from fontTools.misc import sstruct
|
from fontTools.misc import sstruct
|
||||||
from fontTools.misc import psCharStrings
|
from fontTools.misc import psCharStrings
|
||||||
|
from fontTools.misc.arrayTools import unionRect, intRect
|
||||||
from fontTools.misc.textTools import safeEval
|
from fontTools.misc.textTools import safeEval
|
||||||
from fontTools.ttLib import TTFont
|
from fontTools.ttLib import TTFont
|
||||||
from fontTools.ttLib.tables.otBase import OTTableWriter
|
from fontTools.ttLib.tables.otBase import OTTableWriter
|
||||||
@ -101,6 +102,11 @@ class CFFFontSet(object):
|
|||||||
# use current 'major' value to determine output format
|
# use current 'major' value to determine output format
|
||||||
assert self.major in (1, 2), "Unknown CFF format"
|
assert self.major in (1, 2), "Unknown CFF format"
|
||||||
isCFF2 = self.major == 2
|
isCFF2 = self.major == 2
|
||||||
|
|
||||||
|
if otFont.recalcBBoxes and not isCFF2:
|
||||||
|
for topDict in self.topDictIndex:
|
||||||
|
topDict.recalcFontBBox()
|
||||||
|
|
||||||
if not isCFF2:
|
if not isCFF2:
|
||||||
strings = IndexedStrings()
|
strings = IndexedStrings()
|
||||||
else:
|
else:
|
||||||
@ -2313,6 +2319,21 @@ class TopDict(BaseDict):
|
|||||||
progress.increment(0) # update
|
progress.increment(0) # update
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
|
def recalcFontBBox(self):
|
||||||
|
fontBBox = None
|
||||||
|
for charString in self.CharStrings.values():
|
||||||
|
bounds = charString.calcBounds()
|
||||||
|
if bounds is not None:
|
||||||
|
if fontBBox is not None:
|
||||||
|
fontBBox = unionRect(fontBBox, bounds)
|
||||||
|
else:
|
||||||
|
fontBBox = bounds
|
||||||
|
|
||||||
|
if fontBBox is None:
|
||||||
|
self.FontBBox = self.defaults['FontBBox'][:]
|
||||||
|
else:
|
||||||
|
self.FontBBox = list(intRect(fontBBox))
|
||||||
|
|
||||||
|
|
||||||
class FontDict(BaseDict):
|
class FontDict(BaseDict):
|
||||||
#
|
#
|
||||||
|
@ -5,6 +5,7 @@ CFF dictionary data and Type1/Type2 CharStrings.
|
|||||||
from __future__ import print_function, division, absolute_import
|
from __future__ import print_function, division, absolute_import
|
||||||
from fontTools.misc.py23 import *
|
from fontTools.misc.py23 import *
|
||||||
from fontTools.misc.fixedTools import fixedToFloat
|
from fontTools.misc.fixedTools import fixedToFloat
|
||||||
|
from fontTools.pens.boundsPen import BoundsPen
|
||||||
import struct
|
import struct
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -977,6 +978,11 @@ class T2CharString(object):
|
|||||||
extractor.execute(self)
|
extractor.execute(self)
|
||||||
self.width = extractor.width
|
self.width = extractor.width
|
||||||
|
|
||||||
|
def calcBounds(self):
|
||||||
|
boundsPen = BoundsPen(None)
|
||||||
|
self.draw(boundsPen)
|
||||||
|
return boundsPen.bounds
|
||||||
|
|
||||||
def check_program(self, program, isCFF2=False):
|
def check_program(self, program, isCFF2=False):
|
||||||
if isCFF2:
|
if isCFF2:
|
||||||
if self.program:
|
if self.program:
|
||||||
|
@ -90,9 +90,11 @@ class TTFont(object):
|
|||||||
|
|
||||||
If the recalcBBoxes argument is false, a number of things will *not*
|
If the recalcBBoxes argument is false, a number of things will *not*
|
||||||
be recalculated upon save/compile:
|
be recalculated upon save/compile:
|
||||||
1) glyph bounding boxes
|
1) 'glyf' glyph bounding boxes
|
||||||
2) maxp font bounding box
|
2) 'CFF ' font bounding box
|
||||||
3) hhea min/max values
|
3) 'head' font bounding box
|
||||||
|
4) 'hhea' min/max values
|
||||||
|
5) 'vhea' min/max values
|
||||||
(1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-).
|
(1) is needed for certain kinds of CJK fonts (ask Werner Lemberg ;-).
|
||||||
Additionally, upon importing an TTX file, this option cause glyphs
|
Additionally, upon importing an TTX file, this option cause glyphs
|
||||||
to be compiled right away. This should reduce memory consumption
|
to be compiled right away. This should reduce memory consumption
|
||||||
|
@ -33,7 +33,7 @@ headFormat = """
|
|||||||
|
|
||||||
class table__h_e_a_d(DefaultTable.DefaultTable):
|
class table__h_e_a_d(DefaultTable.DefaultTable):
|
||||||
|
|
||||||
dependencies = ['maxp', 'loca']
|
dependencies = ['maxp', 'loca', 'CFF ']
|
||||||
|
|
||||||
def decompile(self, data, ttFont):
|
def decompile(self, data, ttFont):
|
||||||
dummy, rest = sstruct.unpack2(headFormat, data, self)
|
dummy, rest = sstruct.unpack2(headFormat, data, self)
|
||||||
@ -59,6 +59,11 @@ class table__h_e_a_d(DefaultTable.DefaultTable):
|
|||||||
setattr(self, stamp, value)
|
setattr(self, stamp, value)
|
||||||
|
|
||||||
def compile(self, ttFont):
|
def compile(self, ttFont):
|
||||||
|
if ttFont.recalcBBoxes:
|
||||||
|
# For TT-flavored fonts, xMin, yMin, xMax and yMax are set in table__m_a_x_p.recalc().
|
||||||
|
if 'CFF ' in ttFont:
|
||||||
|
topDict = ttFont['CFF '].cff.topDictIndex[0]
|
||||||
|
self.xMin, self.yMin, self.xMax, self.yMax = topDict.FontBBox
|
||||||
if ttFont.recalcTimestamp:
|
if ttFont.recalcTimestamp:
|
||||||
self.modified = timestampNow()
|
self.modified = timestampNow()
|
||||||
data = sstruct.pack(headFormat, self)
|
data = sstruct.pack(headFormat, self)
|
||||||
|
@ -5,6 +5,8 @@ from fontTools.misc.textTools import safeEval
|
|||||||
from fontTools.misc.fixedTools import (
|
from fontTools.misc.fixedTools import (
|
||||||
ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
|
ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
|
||||||
from . import DefaultTable
|
from . import DefaultTable
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
hheaFormat = """
|
hheaFormat = """
|
||||||
> # big endian
|
> # big endian
|
||||||
@ -32,30 +34,25 @@ class table__h_h_e_a(DefaultTable.DefaultTable):
|
|||||||
|
|
||||||
# Note: Keep in sync with table__v_h_e_a
|
# Note: Keep in sync with table__v_h_e_a
|
||||||
|
|
||||||
dependencies = ['hmtx', 'glyf']
|
dependencies = ['hmtx', 'glyf', 'CFF ']
|
||||||
|
|
||||||
def decompile(self, data, ttFont):
|
def decompile(self, data, ttFont):
|
||||||
sstruct.unpack(hheaFormat, data, self)
|
sstruct.unpack(hheaFormat, data, self)
|
||||||
|
|
||||||
def compile(self, ttFont):
|
def compile(self, ttFont):
|
||||||
if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
|
if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
|
||||||
self.recalc(ttFont)
|
self.recalc(ttFont)
|
||||||
self.tableVersion = fi2ve(self.tableVersion)
|
self.tableVersion = fi2ve(self.tableVersion)
|
||||||
return sstruct.pack(hheaFormat, self)
|
return sstruct.pack(hheaFormat, self)
|
||||||
|
|
||||||
def recalc(self, ttFont):
|
def recalc(self, ttFont):
|
||||||
hmtxTable = ttFont['hmtx']
|
hmtxTable = ttFont['hmtx']
|
||||||
|
self.advanceWidthMax = max(adv for adv, _ in hmtxTable.metrics.values())
|
||||||
|
|
||||||
|
boundsWidthDict = {}
|
||||||
if 'glyf' in ttFont:
|
if 'glyf' in ttFont:
|
||||||
glyfTable = ttFont['glyf']
|
glyfTable = ttFont['glyf']
|
||||||
INFINITY = 100000
|
|
||||||
advanceWidthMax = 0
|
|
||||||
minLeftSideBearing = +INFINITY # arbitrary big number
|
|
||||||
minRightSideBearing = +INFINITY # arbitrary big number
|
|
||||||
xMaxExtent = -INFINITY # arbitrary big negative number
|
|
||||||
|
|
||||||
for name in ttFont.getGlyphOrder():
|
for name in ttFont.getGlyphOrder():
|
||||||
width, lsb = hmtxTable[name]
|
|
||||||
advanceWidthMax = max(advanceWidthMax, width)
|
|
||||||
g = glyfTable[name]
|
g = glyfTable[name]
|
||||||
if g.numberOfContours == 0:
|
if g.numberOfContours == 0:
|
||||||
continue
|
continue
|
||||||
@ -63,25 +60,34 @@ class table__h_h_e_a(DefaultTable.DefaultTable):
|
|||||||
# Composite glyph without extents set.
|
# Composite glyph without extents set.
|
||||||
# Calculate those.
|
# Calculate those.
|
||||||
g.recalcBounds(glyfTable)
|
g.recalcBounds(glyfTable)
|
||||||
|
boundsWidthDict[name] = g.xMax - g.xMin
|
||||||
|
elif 'CFF ' in ttFont:
|
||||||
|
topDict = ttFont['CFF '].cff.topDictIndex[0]
|
||||||
|
for name in ttFont.getGlyphOrder():
|
||||||
|
cs = topDict.CharStrings[name]
|
||||||
|
bounds = cs.calcBounds()
|
||||||
|
if bounds is not None:
|
||||||
|
boundsWidthDict[name] = math.ceil(bounds[2]) - math.floor(bounds[0])
|
||||||
|
|
||||||
|
if boundsWidthDict:
|
||||||
|
minLeftSideBearing = float('inf')
|
||||||
|
minRightSideBearing = float('inf')
|
||||||
|
xMaxExtent = -float('inf')
|
||||||
|
for name, boundsWidth in boundsWidthDict.items():
|
||||||
|
advanceWidth, lsb = hmtxTable[name]
|
||||||
|
rsb = advanceWidth - lsb - boundsWidth
|
||||||
|
extent = lsb + boundsWidth
|
||||||
minLeftSideBearing = min(minLeftSideBearing, lsb)
|
minLeftSideBearing = min(minLeftSideBearing, lsb)
|
||||||
rsb = width - lsb - (g.xMax - g.xMin)
|
|
||||||
minRightSideBearing = min(minRightSideBearing, rsb)
|
minRightSideBearing = min(minRightSideBearing, rsb)
|
||||||
extent = lsb + (g.xMax - g.xMin)
|
|
||||||
xMaxExtent = max(xMaxExtent, extent)
|
xMaxExtent = max(xMaxExtent, extent)
|
||||||
|
|
||||||
if xMaxExtent == -INFINITY:
|
|
||||||
# No glyph has outlines.
|
|
||||||
minLeftSideBearing = 0
|
|
||||||
minRightSideBearing = 0
|
|
||||||
xMaxExtent = 0
|
|
||||||
|
|
||||||
self.advanceWidthMax = advanceWidthMax
|
|
||||||
self.minLeftSideBearing = minLeftSideBearing
|
self.minLeftSideBearing = minLeftSideBearing
|
||||||
self.minRightSideBearing = minRightSideBearing
|
self.minRightSideBearing = minRightSideBearing
|
||||||
self.xMaxExtent = xMaxExtent
|
self.xMaxExtent = xMaxExtent
|
||||||
else:
|
|
||||||
# XXX CFF recalc...
|
else: # No glyph has outlines.
|
||||||
pass
|
self.minLeftSideBearing = 0
|
||||||
|
self.minRightSideBearing = 0
|
||||||
|
self.xMaxExtent = 0
|
||||||
|
|
||||||
def toXML(self, writer, ttFont):
|
def toXML(self, writer, ttFont):
|
||||||
formatstring, names, fixes = sstruct.getformat(hheaFormat)
|
formatstring, names, fixes = sstruct.getformat(hheaFormat)
|
||||||
|
@ -5,6 +5,8 @@ from fontTools.misc.textTools import safeEval
|
|||||||
from fontTools.misc.fixedTools import (
|
from fontTools.misc.fixedTools import (
|
||||||
ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
|
ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
|
||||||
from . import DefaultTable
|
from . import DefaultTable
|
||||||
|
import math
|
||||||
|
|
||||||
|
|
||||||
vheaFormat = """
|
vheaFormat = """
|
||||||
> # big endian
|
> # big endian
|
||||||
@ -31,30 +33,25 @@ class table__v_h_e_a(DefaultTable.DefaultTable):
|
|||||||
|
|
||||||
# Note: Keep in sync with table__h_h_e_a
|
# Note: Keep in sync with table__h_h_e_a
|
||||||
|
|
||||||
dependencies = ['vmtx', 'glyf']
|
dependencies = ['vmtx', 'glyf', 'CFF ']
|
||||||
|
|
||||||
def decompile(self, data, ttFont):
|
def decompile(self, data, ttFont):
|
||||||
sstruct.unpack(vheaFormat, data, self)
|
sstruct.unpack(vheaFormat, data, self)
|
||||||
|
|
||||||
def compile(self, ttFont):
|
def compile(self, ttFont):
|
||||||
if ttFont.isLoaded('glyf') and ttFont.recalcBBoxes:
|
if ttFont.recalcBBoxes and (ttFont.isLoaded('glyf') or ttFont.isLoaded('CFF ')):
|
||||||
self.recalc(ttFont)
|
self.recalc(ttFont)
|
||||||
self.tableVersion = fi2ve(self.tableVersion)
|
self.tableVersion = fi2ve(self.tableVersion)
|
||||||
return sstruct.pack(vheaFormat, self)
|
return sstruct.pack(vheaFormat, self)
|
||||||
|
|
||||||
def recalc(self, ttFont):
|
def recalc(self, ttFont):
|
||||||
vtmxTable = ttFont['vmtx']
|
vmtxTable = ttFont['vmtx']
|
||||||
|
self.advanceHeightMax = max(adv for adv, _ in vmtxTable.metrics.values())
|
||||||
|
|
||||||
|
boundsHeightDict = {}
|
||||||
if 'glyf' in ttFont:
|
if 'glyf' in ttFont:
|
||||||
glyfTable = ttFont['glyf']
|
glyfTable = ttFont['glyf']
|
||||||
INFINITY = 100000
|
|
||||||
advanceHeightMax = 0
|
|
||||||
minTopSideBearing = +INFINITY # arbitrary big number
|
|
||||||
minBottomSideBearing = +INFINITY # arbitrary big number
|
|
||||||
yMaxExtent = -INFINITY # arbitrary big negative number
|
|
||||||
|
|
||||||
for name in ttFont.getGlyphOrder():
|
for name in ttFont.getGlyphOrder():
|
||||||
height, tsb = vtmxTable[name]
|
|
||||||
advanceHeightMax = max(advanceHeightMax, height)
|
|
||||||
g = glyfTable[name]
|
g = glyfTable[name]
|
||||||
if g.numberOfContours == 0:
|
if g.numberOfContours == 0:
|
||||||
continue
|
continue
|
||||||
@ -62,25 +59,34 @@ class table__v_h_e_a(DefaultTable.DefaultTable):
|
|||||||
# Composite glyph without extents set.
|
# Composite glyph without extents set.
|
||||||
# Calculate those.
|
# Calculate those.
|
||||||
g.recalcBounds(glyfTable)
|
g.recalcBounds(glyfTable)
|
||||||
|
boundsHeightDict[name] = g.yMax - g.yMin
|
||||||
|
elif 'CFF ' in ttFont:
|
||||||
|
topDict = ttFont['CFF '].cff.topDictIndex[0]
|
||||||
|
for name in ttFont.getGlyphOrder():
|
||||||
|
cs = topDict.CharStrings[name]
|
||||||
|
bounds = cs.calcBounds()
|
||||||
|
if bounds is not None:
|
||||||
|
boundsHeightDict[name] = math.ceil(bounds[3]) - math.floor(bounds[1])
|
||||||
|
|
||||||
|
if boundsHeightDict:
|
||||||
|
minTopSideBearing = float('inf')
|
||||||
|
minBottomSideBearing = float('inf')
|
||||||
|
yMaxExtent = -float('inf')
|
||||||
|
for name, boundsHeight in boundsHeightDict.items():
|
||||||
|
advanceHeight, tsb = vmtxTable[name]
|
||||||
|
bsb = advanceHeight - tsb - boundsHeight
|
||||||
|
extent = tsb + boundsHeight
|
||||||
minTopSideBearing = min(minTopSideBearing, tsb)
|
minTopSideBearing = min(minTopSideBearing, tsb)
|
||||||
bsb = height - tsb - (g.yMax - g.yMin)
|
|
||||||
minBottomSideBearing = min(minBottomSideBearing, bsb)
|
minBottomSideBearing = min(minBottomSideBearing, bsb)
|
||||||
extent = tsb + (g.yMax - g.yMin)
|
|
||||||
yMaxExtent = max(yMaxExtent, extent)
|
yMaxExtent = max(yMaxExtent, extent)
|
||||||
|
|
||||||
if yMaxExtent == -INFINITY:
|
|
||||||
# No glyph has outlines.
|
|
||||||
minTopSideBearing = 0
|
|
||||||
minBottomSideBearing = 0
|
|
||||||
yMaxExtent = 0
|
|
||||||
|
|
||||||
self.advanceHeightMax = advanceHeightMax
|
|
||||||
self.minTopSideBearing = minTopSideBearing
|
self.minTopSideBearing = minTopSideBearing
|
||||||
self.minBottomSideBearing = minBottomSideBearing
|
self.minBottomSideBearing = minBottomSideBearing
|
||||||
self.yMaxExtent = yMaxExtent
|
self.yMaxExtent = yMaxExtent
|
||||||
else:
|
|
||||||
# XXX CFF recalc...
|
else: # No glyph has outlines.
|
||||||
pass
|
self.minTopSideBearing = 0
|
||||||
|
self.minBottomSideBearing = 0
|
||||||
|
self.yMaxExtent = 0
|
||||||
|
|
||||||
def toXML(self, writer, ttFont):
|
def toXML(self, writer, ttFont):
|
||||||
formatstring, names, fixes = sstruct.getformat(vheaFormat)
|
formatstring, names, fixes = sstruct.getformat(vheaFormat)
|
||||||
|
48
Tests/cffLib/cffLib_test.py
Normal file
48
Tests/cffLib/cffLib_test.py
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
from __future__ import print_function, division, absolute_import
|
||||||
|
from fontTools.cffLib import TopDict, PrivateDict, CharStrings
|
||||||
|
from fontTools.misc.testTools import parseXML
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TopDictTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_recalcFontBBox(self):
|
||||||
|
topDict = TopDict()
|
||||||
|
topDict.CharStrings = CharStrings(None, None, None, PrivateDict(), None, None)
|
||||||
|
topDict.CharStrings.fromXML(None, None, parseXML("""
|
||||||
|
<CharString name=".notdef">
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="foo"><!-- [100, -100, 300, 100] -->
|
||||||
|
100 -100 rmoveto 200 hlineto 200 vlineto -200 hlineto endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="bar"><!-- [0, 0, 200, 200] -->
|
||||||
|
0 0 rmoveto 200 hlineto 200 vlineto -200 hlineto endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="baz"><!-- [-55.1, -55.1, 55.1, 55.1] -->
|
||||||
|
-55.1 -55.1 rmoveto 110.2 hlineto 110.2 vlineto -110.2 hlineto endchar
|
||||||
|
</CharString>
|
||||||
|
"""))
|
||||||
|
|
||||||
|
topDict.recalcFontBBox()
|
||||||
|
self.assertEqual(topDict.FontBBox, [-56, -100, 300, 200])
|
||||||
|
|
||||||
|
def test_recalcFontBBox_empty(self):
|
||||||
|
topDict = TopDict()
|
||||||
|
topDict.CharStrings = CharStrings(None, None, None, PrivateDict(), None, None)
|
||||||
|
topDict.CharStrings.fromXML(None, None, parseXML("""
|
||||||
|
<CharString name=".notdef">
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="space">
|
||||||
|
123 endchar
|
||||||
|
</CharString>
|
||||||
|
"""))
|
||||||
|
|
||||||
|
topDict.recalcFontBBox()
|
||||||
|
self.assertEqual(topDict.FontBBox, [0, 0, 0, 0])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
sys.exit(unittest.main())
|
32
Tests/misc/psCharStrings_test.py
Normal file
32
Tests/misc/psCharStrings_test.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
from __future__ import print_function, division, absolute_import
|
||||||
|
from fontTools.cffLib import PrivateDict
|
||||||
|
from fontTools.cffLib.specializer import stringToProgram
|
||||||
|
from fontTools.misc.psCharStrings import T2CharString
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class T2CharStringTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def stringToT2CharString(cls, string):
|
||||||
|
return T2CharString(program=stringToProgram(string), private=PrivateDict())
|
||||||
|
|
||||||
|
def test_calcBounds_empty(self):
|
||||||
|
cs = self.stringToT2CharString("endchar")
|
||||||
|
bounds = cs.calcBounds()
|
||||||
|
self.assertEqual(bounds, None)
|
||||||
|
|
||||||
|
def test_calcBounds_line(self):
|
||||||
|
cs = self.stringToT2CharString("100 100 rmoveto 40 10 rlineto -20 50 rlineto endchar")
|
||||||
|
bounds = cs.calcBounds()
|
||||||
|
self.assertEqual(bounds, (100, 100, 140, 160))
|
||||||
|
|
||||||
|
def test_calcBounds_curve(self):
|
||||||
|
cs = self.stringToT2CharString("100 100 rmoveto -50 -150 200 0 -50 150 rrcurveto endchar")
|
||||||
|
bounds = cs.calcBounds()
|
||||||
|
self.assertEqual(bounds, (91.90524980688875, -12.5, 208.09475019311125, 100))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import sys
|
||||||
|
sys.exit(unittest.main())
|
@ -5,8 +5,13 @@ from fontTools.misc.testTools import parseXML, getXML
|
|||||||
from fontTools.misc.textTools import deHexStr
|
from fontTools.misc.textTools import deHexStr
|
||||||
from fontTools.ttLib import TTFont, newTable
|
from fontTools.ttLib import TTFont, newTable
|
||||||
from fontTools.misc.fixedTools import log
|
from fontTools.misc.fixedTools import log
|
||||||
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
DATA_DIR = os.path.join(CURR_DIR, 'data')
|
||||||
|
|
||||||
HHEA_DATA = deHexStr(
|
HHEA_DATA = deHexStr(
|
||||||
'0001 0000 ' # 1.0 version
|
'0001 0000 ' # 1.0 version
|
||||||
'02EE ' # 750 ascent
|
'02EE ' # 750 ascent
|
||||||
@ -155,6 +160,39 @@ class HheaDecompileOrFromXMLTest(unittest.TestCase):
|
|||||||
self.assertEqual(getattr(hhea, key), HHEA_AS_DICT[key])
|
self.assertEqual(getattr(hhea, key), HHEA_AS_DICT[key])
|
||||||
|
|
||||||
|
|
||||||
|
class HheaRecalcTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_recalc_TTF(self):
|
||||||
|
font = TTFont()
|
||||||
|
font.importXML(os.path.join(DATA_DIR, '_h_h_e_a_recalc_TTF.ttx'))
|
||||||
|
hhea = font['hhea']
|
||||||
|
hhea.recalc(font)
|
||||||
|
self.assertEqual(hhea.advanceWidthMax, 600)
|
||||||
|
self.assertEqual(hhea.minLeftSideBearing, -56)
|
||||||
|
self.assertEqual(hhea.minRightSideBearing, 100)
|
||||||
|
self.assertEqual(hhea.xMaxExtent, 400)
|
||||||
|
|
||||||
|
def test_recalc_OTF(self):
|
||||||
|
font = TTFont()
|
||||||
|
font.importXML(os.path.join(DATA_DIR, '_h_h_e_a_recalc_OTF.ttx'))
|
||||||
|
hhea = font['hhea']
|
||||||
|
hhea.recalc(font)
|
||||||
|
self.assertEqual(hhea.advanceWidthMax, 600)
|
||||||
|
self.assertEqual(hhea.minLeftSideBearing, -56)
|
||||||
|
self.assertEqual(hhea.minRightSideBearing, 100)
|
||||||
|
self.assertEqual(hhea.xMaxExtent, 400)
|
||||||
|
|
||||||
|
def test_recalc_empty(self):
|
||||||
|
font = TTFont()
|
||||||
|
font.importXML(os.path.join(DATA_DIR, '_h_h_e_a_recalc_empty.ttx'))
|
||||||
|
hhea = font['hhea']
|
||||||
|
hhea.recalc(font)
|
||||||
|
self.assertEqual(hhea.advanceWidthMax, 600)
|
||||||
|
self.assertEqual(hhea.minLeftSideBearing, 0)
|
||||||
|
self.assertEqual(hhea.minRightSideBearing, 0)
|
||||||
|
self.assertEqual(hhea.xMaxExtent, 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
sys.exit(unittest.main())
|
sys.exit(unittest.main())
|
||||||
|
@ -5,8 +5,13 @@ from fontTools.misc.testTools import parseXML, getXML
|
|||||||
from fontTools.misc.textTools import deHexStr
|
from fontTools.misc.textTools import deHexStr
|
||||||
from fontTools.ttLib import TTFont, newTable
|
from fontTools.ttLib import TTFont, newTable
|
||||||
from fontTools.misc.fixedTools import log
|
from fontTools.misc.fixedTools import log
|
||||||
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
CURR_DIR = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
DATA_DIR = os.path.join(CURR_DIR, 'data')
|
||||||
|
|
||||||
VHEA_DATA_VERSION_11 = deHexStr(
|
VHEA_DATA_VERSION_11 = deHexStr(
|
||||||
'0001 1000 ' # 1.1 version
|
'0001 1000 ' # 1.1 version
|
||||||
'01F4 ' # 500 ascent
|
'01F4 ' # 500 ascent
|
||||||
@ -232,6 +237,39 @@ class VheaDecompileOrFromXMLTest(unittest.TestCase):
|
|||||||
self.assertEqual(getattr(vhea, key), VHEA_VERSION_11_AS_DICT[key])
|
self.assertEqual(getattr(vhea, key), VHEA_VERSION_11_AS_DICT[key])
|
||||||
|
|
||||||
|
|
||||||
|
class VheaRecalcTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_recalc_TTF(self):
|
||||||
|
font = TTFont()
|
||||||
|
font.importXML(os.path.join(DATA_DIR, '_v_h_e_a_recalc_TTF.ttx'))
|
||||||
|
vhea = font['vhea']
|
||||||
|
vhea.recalc(font)
|
||||||
|
self.assertEqual(vhea.advanceHeightMax, 900)
|
||||||
|
self.assertEqual(vhea.minTopSideBearing, 200)
|
||||||
|
self.assertEqual(vhea.minBottomSideBearing, 377)
|
||||||
|
self.assertEqual(vhea.yMaxExtent, 312)
|
||||||
|
|
||||||
|
def test_recalc_OTF(self):
|
||||||
|
font = TTFont()
|
||||||
|
font.importXML(os.path.join(DATA_DIR, '_v_h_e_a_recalc_OTF.ttx'))
|
||||||
|
vhea = font['vhea']
|
||||||
|
vhea.recalc(font)
|
||||||
|
self.assertEqual(vhea.advanceHeightMax, 900)
|
||||||
|
self.assertEqual(vhea.minTopSideBearing, 200)
|
||||||
|
self.assertEqual(vhea.minBottomSideBearing, 377)
|
||||||
|
self.assertEqual(vhea.yMaxExtent, 312)
|
||||||
|
|
||||||
|
def test_recalc_empty(self):
|
||||||
|
font = TTFont()
|
||||||
|
font.importXML(os.path.join(DATA_DIR, '_v_h_e_a_recalc_empty.ttx'))
|
||||||
|
vhea = font['vhea']
|
||||||
|
vhea.recalc(font)
|
||||||
|
self.assertEqual(vhea.advanceHeightMax, 900)
|
||||||
|
self.assertEqual(vhea.minTopSideBearing, 0)
|
||||||
|
self.assertEqual(vhea.minBottomSideBearing, 0)
|
||||||
|
self.assertEqual(vhea.yMaxExtent, 0)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
sys.exit(unittest.main())
|
sys.exit(unittest.main())
|
||||||
|
72
Tests/ttLib/tables/data/_h_h_e_a_recalc_OTF.ttx
Normal file
72
Tests/ttLib/tables/data/_h_h_e_a_recalc_OTF.ttx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="A"/>
|
||||||
|
<GlyphID id="2" name="B"/>
|
||||||
|
<GlyphID id="3" name="C"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<hhea>
|
||||||
|
<tableVersion value="0x00010000"/>
|
||||||
|
<ascent value="800"/>
|
||||||
|
<descent value="-200"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceWidthMax value="999"/>
|
||||||
|
<minLeftSideBearing value="99"/>
|
||||||
|
<minRightSideBearing value="99"/>
|
||||||
|
<xMaxExtent value="99"/>
|
||||||
|
<caretSlopeRise value="1"/>
|
||||||
|
<caretSlopeRun value="0"/>
|
||||||
|
<caretOffset value="0"/>
|
||||||
|
<reserved0 value="0"/>
|
||||||
|
<reserved1 value="0"/>
|
||||||
|
<reserved2 value="0"/>
|
||||||
|
<reserved3 value="0"/>
|
||||||
|
<metricDataFormat value="0"/>
|
||||||
|
<numberOfHMetrics value="4"/>
|
||||||
|
</hhea>
|
||||||
|
|
||||||
|
<CFF>
|
||||||
|
<major value="1"/>
|
||||||
|
<minor value="0"/>
|
||||||
|
<CFFFont name="Test">
|
||||||
|
<Private>
|
||||||
|
</Private>
|
||||||
|
<CharStrings>
|
||||||
|
<CharString name=".notdef">
|
||||||
|
300
|
||||||
|
0 0 rmoveto
|
||||||
|
1 1 rlineto
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="A">
|
||||||
|
400
|
||||||
|
-55.2 -55.2 rmoveto
|
||||||
|
110.4 110.4 rlineto
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="B">
|
||||||
|
500
|
||||||
|
100 0 rmoveto
|
||||||
|
300 0 rlineto
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="C">
|
||||||
|
600
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
</CharStrings>
|
||||||
|
</CFFFont>
|
||||||
|
</CFF>
|
||||||
|
|
||||||
|
<hmtx>
|
||||||
|
<mtx name=".notdef" width="300" lsb="0"/>
|
||||||
|
<mtx name="A" width="400" lsb="-56"/>
|
||||||
|
<mtx name="B" width="500" lsb="100"/>
|
||||||
|
<mtx name="C" width="600" lsb="0"/>
|
||||||
|
</hmtx>
|
||||||
|
|
||||||
|
</ttFont>
|
63
Tests/ttLib/tables/data/_h_h_e_a_recalc_TTF.ttx
Normal file
63
Tests/ttLib/tables/data/_h_h_e_a_recalc_TTF.ttx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.12">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="A"/>
|
||||||
|
<GlyphID id="2" name="B"/>
|
||||||
|
<GlyphID id="3" name="C"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<hhea>
|
||||||
|
<tableVersion value="0x00010000"/>
|
||||||
|
<ascent value="800"/>
|
||||||
|
<descent value="-200"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceWidthMax value="999"/>
|
||||||
|
<minLeftSideBearing value="99"/>
|
||||||
|
<minRightSideBearing value="99"/>
|
||||||
|
<xMaxExtent value="99"/>
|
||||||
|
<caretSlopeRise value="1"/>
|
||||||
|
<caretSlopeRun value="0"/>
|
||||||
|
<caretOffset value="0"/>
|
||||||
|
<reserved0 value="0"/>
|
||||||
|
<reserved1 value="0"/>
|
||||||
|
<reserved2 value="0"/>
|
||||||
|
<reserved3 value="0"/>
|
||||||
|
<metricDataFormat value="0"/>
|
||||||
|
<numberOfHMetrics value="4"/>
|
||||||
|
</hhea>
|
||||||
|
|
||||||
|
<glyf>
|
||||||
|
<!-- The xMin, yMin, xMax and yMax values
|
||||||
|
will be recalculated by the compiler. -->
|
||||||
|
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="1" yMax="1">
|
||||||
|
<contour>
|
||||||
|
<pt x="0" y="0" on="1"/>
|
||||||
|
<pt x="1" y="1" on="1"/>
|
||||||
|
</contour>
|
||||||
|
</TTGlyph>
|
||||||
|
<TTGlyph name="A" xMin="-56" yMin="-56" xMax="56" yMax="56">
|
||||||
|
<contour>
|
||||||
|
<pt x="-56" y="-56" on="1"/>
|
||||||
|
<pt x="56" y="56" on="1"/>
|
||||||
|
</contour>
|
||||||
|
</TTGlyph>
|
||||||
|
<TTGlyph name="B" xMin="100" yMin="0" xMax="400" yMax="0">
|
||||||
|
<contour>
|
||||||
|
<pt x="100" y="0" on="1"/>
|
||||||
|
<pt x="400" y="0" on="1"/>
|
||||||
|
</contour>
|
||||||
|
</TTGlyph>
|
||||||
|
<TTGlyph name="C"/><!-- contains no outline data -->
|
||||||
|
</glyf>
|
||||||
|
|
||||||
|
<hmtx>
|
||||||
|
<mtx name=".notdef" width="300" lsb="0"/>
|
||||||
|
<mtx name="A" width="400" lsb="-56"/>
|
||||||
|
<mtx name="B" width="500" lsb="100"/>
|
||||||
|
<mtx name="C" width="600" lsb="0"/>
|
||||||
|
</hmtx>
|
||||||
|
|
||||||
|
</ttFont>
|
62
Tests/ttLib/tables/data/_h_h_e_a_recalc_empty.ttx
Normal file
62
Tests/ttLib/tables/data/_h_h_e_a_recalc_empty.ttx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="A"/>
|
||||||
|
<GlyphID id="2" name="B"/>
|
||||||
|
<GlyphID id="3" name="C"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<hhea>
|
||||||
|
<tableVersion value="0x00010000"/>
|
||||||
|
<ascent value="800"/>
|
||||||
|
<descent value="-200"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceWidthMax value="999"/>
|
||||||
|
<minLeftSideBearing value="99"/>
|
||||||
|
<minRightSideBearing value="99"/>
|
||||||
|
<xMaxExtent value="99"/>
|
||||||
|
<caretSlopeRise value="1"/>
|
||||||
|
<caretSlopeRun value="0"/>
|
||||||
|
<caretOffset value="0"/>
|
||||||
|
<reserved0 value="0"/>
|
||||||
|
<reserved1 value="0"/>
|
||||||
|
<reserved2 value="0"/>
|
||||||
|
<reserved3 value="0"/>
|
||||||
|
<metricDataFormat value="0"/>
|
||||||
|
<numberOfHMetrics value="4"/>
|
||||||
|
</hhea>
|
||||||
|
|
||||||
|
<CFF>
|
||||||
|
<major value="1"/>
|
||||||
|
<minor value="0"/>
|
||||||
|
<CFFFont name="Test">
|
||||||
|
<Private>
|
||||||
|
</Private>
|
||||||
|
<CharStrings>
|
||||||
|
<CharString name=".notdef">
|
||||||
|
300 endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="A">
|
||||||
|
400 endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="B">
|
||||||
|
500 endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="C">
|
||||||
|
600 endchar
|
||||||
|
</CharString>
|
||||||
|
</CharStrings>
|
||||||
|
</CFFFont>
|
||||||
|
</CFF>
|
||||||
|
|
||||||
|
<hmtx>
|
||||||
|
<mtx name=".notdef" width="300" lsb="0"/>
|
||||||
|
<mtx name="A" width="400" lsb="0"/>
|
||||||
|
<mtx name="B" width="500" lsb="0"/>
|
||||||
|
<mtx name="C" width="600" lsb="0"/>
|
||||||
|
</hmtx>
|
||||||
|
|
||||||
|
</ttFont>
|
72
Tests/ttLib/tables/data/_v_h_e_a_recalc_OTF.ttx
Normal file
72
Tests/ttLib/tables/data/_v_h_e_a_recalc_OTF.ttx
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="A"/>
|
||||||
|
<GlyphID id="2" name="B"/>
|
||||||
|
<GlyphID id="3" name="C"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<vhea>
|
||||||
|
<tableVersion value="0x00011000"/>
|
||||||
|
<ascent value="500"/>
|
||||||
|
<descent value="-500"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceHeightMax value="999"/>
|
||||||
|
<minTopSideBearing value="99"/>
|
||||||
|
<minBottomSideBearing value="99"/>
|
||||||
|
<yMaxExtent value="99"/>
|
||||||
|
<caretSlopeRise value="0"/>
|
||||||
|
<caretSlopeRun value="1"/>
|
||||||
|
<caretOffset value="0"/>
|
||||||
|
<reserved1 value="0"/>
|
||||||
|
<reserved2 value="0"/>
|
||||||
|
<reserved3 value="0"/>
|
||||||
|
<reserved4 value="0"/>
|
||||||
|
<metricDataFormat value="0"/>
|
||||||
|
<numberOfVMetrics value="4"/>
|
||||||
|
</vhea>
|
||||||
|
|
||||||
|
<CFF>
|
||||||
|
<major value="1"/>
|
||||||
|
<minor value="0"/>
|
||||||
|
<CFFFont name="Test">
|
||||||
|
<Private>
|
||||||
|
</Private>
|
||||||
|
<CharStrings>
|
||||||
|
<CharString name=".notdef">
|
||||||
|
300
|
||||||
|
0 0 rmoveto
|
||||||
|
1 1 rlineto
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="A">
|
||||||
|
400
|
||||||
|
-55.2 -55.2 rmoveto
|
||||||
|
110.4 110.4 rlineto
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="B">
|
||||||
|
500
|
||||||
|
100 0 rmoveto
|
||||||
|
300 0 rlineto
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="C">
|
||||||
|
600
|
||||||
|
endchar
|
||||||
|
</CharString>
|
||||||
|
</CharStrings>
|
||||||
|
</CFFFont>
|
||||||
|
</CFF>
|
||||||
|
|
||||||
|
<vmtx>
|
||||||
|
<mtx name=".notdef" height="600" tsb="222"/>
|
||||||
|
<mtx name="A" height="700" tsb="200"/>
|
||||||
|
<mtx name="B" height="800" tsb="300"/>
|
||||||
|
<mtx name="C" height="900" tsb="0"/>
|
||||||
|
</vmtx>
|
||||||
|
|
||||||
|
</ttFont>
|
63
Tests/ttLib/tables/data/_v_h_e_a_recalc_TTF.ttx
Normal file
63
Tests/ttLib/tables/data/_v_h_e_a_recalc_TTF.ttx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.12">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="A"/>
|
||||||
|
<GlyphID id="2" name="B"/>
|
||||||
|
<GlyphID id="3" name="C"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<vhea>
|
||||||
|
<tableVersion value="0x00011000"/>
|
||||||
|
<ascent value="500"/>
|
||||||
|
<descent value="-500"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceHeightMax value="999"/>
|
||||||
|
<minTopSideBearing value="99"/>
|
||||||
|
<minBottomSideBearing value="99"/>
|
||||||
|
<yMaxExtent value="99"/>
|
||||||
|
<caretSlopeRise value="0"/>
|
||||||
|
<caretSlopeRun value="1"/>
|
||||||
|
<caretOffset value="0"/>
|
||||||
|
<reserved1 value="0"/>
|
||||||
|
<reserved2 value="0"/>
|
||||||
|
<reserved3 value="0"/>
|
||||||
|
<reserved4 value="0"/>
|
||||||
|
<metricDataFormat value="0"/>
|
||||||
|
<numberOfVMetrics value="4"/>
|
||||||
|
</vhea>
|
||||||
|
|
||||||
|
<glyf>
|
||||||
|
<!-- The xMin, yMin, xMax and yMax values
|
||||||
|
will be recalculated by the compiler. -->
|
||||||
|
<TTGlyph name=".notdef" xMin="0" yMin="0" xMax="1" yMax="1">
|
||||||
|
<contour>
|
||||||
|
<pt x="0" y="0" on="1"/>
|
||||||
|
<pt x="1" y="1" on="1"/>
|
||||||
|
</contour>
|
||||||
|
</TTGlyph>
|
||||||
|
<TTGlyph name="A" xMin="-56" yMin="-56" xMax="56" yMax="56">
|
||||||
|
<contour>
|
||||||
|
<pt x="-56" y="-56" on="1"/>
|
||||||
|
<pt x="56" y="56" on="1"/>
|
||||||
|
</contour>
|
||||||
|
</TTGlyph>
|
||||||
|
<TTGlyph name="B" xMin="100" yMin="0" xMax="400" yMax="0">
|
||||||
|
<contour>
|
||||||
|
<pt x="100" y="0" on="1"/>
|
||||||
|
<pt x="400" y="0" on="1"/>
|
||||||
|
</contour>
|
||||||
|
</TTGlyph>
|
||||||
|
<TTGlyph name="C"/><!-- contains no outline data -->
|
||||||
|
</glyf>
|
||||||
|
|
||||||
|
<vmtx>
|
||||||
|
<mtx name=".notdef" height="600" tsb="222"/>
|
||||||
|
<mtx name="A" height="700" tsb="200"/>
|
||||||
|
<mtx name="B" height="800" tsb="300"/>
|
||||||
|
<mtx name="C" height="900" tsb="0"/>
|
||||||
|
</vmtx>
|
||||||
|
|
||||||
|
</ttFont>
|
62
Tests/ttLib/tables/data/_v_h_e_a_recalc_empty.ttx
Normal file
62
Tests/ttLib/tables/data/_v_h_e_a_recalc_empty.ttx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="OTTO" ttLibVersion="3.12">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="A"/>
|
||||||
|
<GlyphID id="2" name="B"/>
|
||||||
|
<GlyphID id="3" name="C"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<vhea>
|
||||||
|
<tableVersion value="0x00011000"/>
|
||||||
|
<ascent value="500"/>
|
||||||
|
<descent value="-500"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceHeightMax value="999"/>
|
||||||
|
<minTopSideBearing value="99"/>
|
||||||
|
<minBottomSideBearing value="99"/>
|
||||||
|
<yMaxExtent value="99"/>
|
||||||
|
<caretSlopeRise value="0"/>
|
||||||
|
<caretSlopeRun value="1"/>
|
||||||
|
<caretOffset value="0"/>
|
||||||
|
<reserved1 value="0"/>
|
||||||
|
<reserved2 value="0"/>
|
||||||
|
<reserved3 value="0"/>
|
||||||
|
<reserved4 value="0"/>
|
||||||
|
<metricDataFormat value="0"/>
|
||||||
|
<numberOfVMetrics value="4"/>
|
||||||
|
</vhea>
|
||||||
|
|
||||||
|
<CFF>
|
||||||
|
<major value="1"/>
|
||||||
|
<minor value="0"/>
|
||||||
|
<CFFFont name="Test">
|
||||||
|
<Private>
|
||||||
|
</Private>
|
||||||
|
<CharStrings>
|
||||||
|
<CharString name=".notdef">
|
||||||
|
300 endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="A">
|
||||||
|
400 endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="B">
|
||||||
|
500 endchar
|
||||||
|
</CharString>
|
||||||
|
<CharString name="C">
|
||||||
|
600 endchar
|
||||||
|
</CharString>
|
||||||
|
</CharStrings>
|
||||||
|
</CFFFont>
|
||||||
|
</CFF>
|
||||||
|
|
||||||
|
<vmtx>
|
||||||
|
<mtx name=".notdef" height="600" tsb="0"/>
|
||||||
|
<mtx name="A" height="700" tsb="0"/>
|
||||||
|
<mtx name="B" height="800" tsb="0"/>
|
||||||
|
<mtx name="C" height="900" tsb="0"/>
|
||||||
|
</vmtx>
|
||||||
|
|
||||||
|
</ttFont>
|
Loading…
x
Reference in New Issue
Block a user