Merge pull request #970 from mashabow/cff-bounds-recalc

CFF bounds recalculation
This commit is contained in:
Cosimo Lupo 2017-08-01 11:09:30 +01:00 committed by GitHub
commit b5da19dc4b
16 changed files with 647 additions and 51 deletions

View File

@ -4,6 +4,7 @@ from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc import sstruct
from fontTools.misc import psCharStrings
from fontTools.misc.arrayTools import unionRect, intRect
from fontTools.misc.textTools import safeEval
from fontTools.ttLib import TTFont
from fontTools.ttLib.tables.otBase import OTTableWriter
@ -101,6 +102,11 @@ class CFFFontSet(object):
# use current 'major' value to determine output format
assert self.major in (1, 2), "Unknown CFF format"
isCFF2 = self.major == 2
if otFont.recalcBBoxes and not isCFF2:
for topDict in self.topDictIndex:
topDict.recalcFontBBox()
if not isCFF2:
strings = IndexedStrings()
else:
@ -2313,6 +2319,21 @@ class TopDict(BaseDict):
progress.increment(0) # update
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):
#

View File

@ -5,6 +5,7 @@ CFF dictionary data and Type1/Type2 CharStrings.
from __future__ import print_function, division, absolute_import
from fontTools.misc.py23 import *
from fontTools.misc.fixedTools import fixedToFloat
from fontTools.pens.boundsPen import BoundsPen
import struct
import logging
@ -977,6 +978,11 @@ class T2CharString(object):
extractor.execute(self)
self.width = extractor.width
def calcBounds(self):
boundsPen = BoundsPen(None)
self.draw(boundsPen)
return boundsPen.bounds
def check_program(self, program, isCFF2=False):
if isCFF2:
if self.program:

View File

@ -90,9 +90,11 @@ class TTFont(object):
If the recalcBBoxes argument is false, a number of things will *not*
be recalculated upon save/compile:
1) glyph bounding boxes
2) maxp font bounding box
3) hhea min/max values
1) 'glyf' glyph bounding boxes
2) 'CFF ' font bounding box
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 ;-).
Additionally, upon importing an TTX file, this option cause glyphs
to be compiled right away. This should reduce memory consumption

View File

@ -33,7 +33,7 @@ headFormat = """
class table__h_e_a_d(DefaultTable.DefaultTable):
dependencies = ['maxp', 'loca']
dependencies = ['maxp', 'loca', 'CFF ']
def decompile(self, data, ttFont):
dummy, rest = sstruct.unpack2(headFormat, data, self)
@ -59,6 +59,11 @@ class table__h_e_a_d(DefaultTable.DefaultTable):
setattr(self, stamp, value)
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:
self.modified = timestampNow()
data = sstruct.pack(headFormat, self)

View File

@ -5,6 +5,8 @@ from fontTools.misc.textTools import safeEval
from fontTools.misc.fixedTools import (
ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
from . import DefaultTable
import math
hheaFormat = """
> # big endian
@ -32,30 +34,25 @@ class table__h_h_e_a(DefaultTable.DefaultTable):
# Note: Keep in sync with table__v_h_e_a
dependencies = ['hmtx', 'glyf']
dependencies = ['hmtx', 'glyf', 'CFF ']
def decompile(self, data, ttFont):
sstruct.unpack(hheaFormat, data, self)
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.tableVersion = fi2ve(self.tableVersion)
return sstruct.pack(hheaFormat, self)
def recalc(self, ttFont):
hmtxTable = ttFont['hmtx']
self.advanceWidthMax = max(adv for adv, _ in hmtxTable.metrics.values())
boundsWidthDict = {}
if 'glyf' in ttFont:
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():
width, lsb = hmtxTable[name]
advanceWidthMax = max(advanceWidthMax, width)
g = glyfTable[name]
if g.numberOfContours == 0:
continue
@ -63,25 +60,34 @@ class table__h_h_e_a(DefaultTable.DefaultTable):
# Composite glyph without extents set.
# Calculate those.
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)
rsb = width - lsb - (g.xMax - g.xMin)
minRightSideBearing = min(minRightSideBearing, rsb)
extent = lsb + (g.xMax - g.xMin)
xMaxExtent = max(xMaxExtent, extent)
if xMaxExtent == -INFINITY:
# No glyph has outlines.
minLeftSideBearing = 0
minRightSideBearing = 0
xMaxExtent = 0
self.advanceWidthMax = advanceWidthMax
self.minLeftSideBearing = minLeftSideBearing
self.minRightSideBearing = minRightSideBearing
self.xMaxExtent = xMaxExtent
else:
# XXX CFF recalc...
pass
else: # No glyph has outlines.
self.minLeftSideBearing = 0
self.minRightSideBearing = 0
self.xMaxExtent = 0
def toXML(self, writer, ttFont):
formatstring, names, fixes = sstruct.getformat(hheaFormat)

View File

@ -5,6 +5,8 @@ from fontTools.misc.textTools import safeEval
from fontTools.misc.fixedTools import (
ensureVersionIsLong as fi2ve, versionToFixed as ve2fi)
from . import DefaultTable
import math
vheaFormat = """
> # big endian
@ -31,30 +33,25 @@ class table__v_h_e_a(DefaultTable.DefaultTable):
# Note: Keep in sync with table__h_h_e_a
dependencies = ['vmtx', 'glyf']
dependencies = ['vmtx', 'glyf', 'CFF ']
def decompile(self, data, ttFont):
sstruct.unpack(vheaFormat, data, self)
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.tableVersion = fi2ve(self.tableVersion)
return sstruct.pack(vheaFormat, self)
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:
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():
height, tsb = vtmxTable[name]
advanceHeightMax = max(advanceHeightMax, height)
g = glyfTable[name]
if g.numberOfContours == 0:
continue
@ -62,25 +59,34 @@ class table__v_h_e_a(DefaultTable.DefaultTable):
# Composite glyph without extents set.
# Calculate those.
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)
bsb = height - tsb - (g.yMax - g.yMin)
minBottomSideBearing = min(minBottomSideBearing, bsb)
extent = tsb + (g.yMax - g.yMin)
yMaxExtent = max(yMaxExtent, extent)
if yMaxExtent == -INFINITY:
# No glyph has outlines.
minTopSideBearing = 0
minBottomSideBearing = 0
yMaxExtent = 0
self.advanceHeightMax = advanceHeightMax
self.minTopSideBearing = minTopSideBearing
self.minBottomSideBearing = minBottomSideBearing
self.yMaxExtent = yMaxExtent
else:
# XXX CFF recalc...
pass
else: # No glyph has outlines.
self.minTopSideBearing = 0
self.minBottomSideBearing = 0
self.yMaxExtent = 0
def toXML(self, writer, ttFont):
formatstring, names, fixes = sstruct.getformat(vheaFormat)

View 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())

View 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())

View File

@ -5,8 +5,13 @@ from fontTools.misc.testTools import parseXML, getXML
from fontTools.misc.textTools import deHexStr
from fontTools.ttLib import TTFont, newTable
from fontTools.misc.fixedTools import log
import os
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(
'0001 0000 ' # 1.0 version
'02EE ' # 750 ascent
@ -155,6 +160,39 @@ class HheaDecompileOrFromXMLTest(unittest.TestCase):
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__":
import sys
sys.exit(unittest.main())

View File

@ -5,8 +5,13 @@ from fontTools.misc.testTools import parseXML, getXML
from fontTools.misc.textTools import deHexStr
from fontTools.ttLib import TTFont, newTable
from fontTools.misc.fixedTools import log
import os
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(
'0001 1000 ' # 1.1 version
'01F4 ' # 500 ascent
@ -232,6 +237,39 @@ class VheaDecompileOrFromXMLTest(unittest.TestCase):
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__":
import sys
sys.exit(unittest.main())

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>