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.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):
# #

View File

@ -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:

View File

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

View File

@ -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)

View File

@ -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)

View File

@ -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)

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

View File

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

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>