Some support for CFF2
This commit is contained in:
parent
93cb09316a
commit
9d73327f3b
@ -491,6 +491,83 @@ class FontBuilder(object):
|
|||||||
self.font["CFF "] = newTable("CFF ")
|
self.font["CFF "] = newTable("CFF ")
|
||||||
self.font["CFF "].cff = fontSet
|
self.font["CFF "].cff = fontSet
|
||||||
|
|
||||||
|
def setupCFF2(self, charStringsDict, fdArrayList=None, regions=None):
|
||||||
|
from .cffLib import CFFFontSet, TopDictIndex, TopDict, CharStrings, \
|
||||||
|
GlobalSubrsIndex, PrivateDict, FDArrayIndex, FontDict
|
||||||
|
|
||||||
|
assert not self.isTTF
|
||||||
|
self.font.sfntVersion = "OTTO"
|
||||||
|
fontSet = CFFFontSet()
|
||||||
|
fontSet.major = 2
|
||||||
|
fontSet.minor = 0
|
||||||
|
|
||||||
|
cff2GetGlyphOrder = self.font.getGlyphOrder
|
||||||
|
fontSet.topDictIndex = TopDictIndex(None, cff2GetGlyphOrder, None)
|
||||||
|
|
||||||
|
globalSubrs = GlobalSubrsIndex()
|
||||||
|
fontSet.GlobalSubrs = globalSubrs
|
||||||
|
|
||||||
|
if fdArrayList is None:
|
||||||
|
fdArrayList = [{}]
|
||||||
|
fdSelect = None
|
||||||
|
fdArray = FDArrayIndex()
|
||||||
|
fdArray.strings = None
|
||||||
|
fdArray.GlobalSubrs = globalSubrs
|
||||||
|
for privateDict in fdArrayList:
|
||||||
|
fontDict = FontDict()
|
||||||
|
fontDict.setCFF2(True)
|
||||||
|
private = PrivateDict()
|
||||||
|
for key, value in privateDict.items():
|
||||||
|
setattr(private, key, value)
|
||||||
|
fontDict.Private = private
|
||||||
|
fdArray.append(fontDict)
|
||||||
|
|
||||||
|
topDict = TopDict()
|
||||||
|
topDict.cff2GetGlyphOrder = cff2GetGlyphOrder
|
||||||
|
topDict.FDArray = fdArray
|
||||||
|
scale = 1 / self.font["head"].unitsPerEm
|
||||||
|
topDict.FontMatrix = [scale, 0, 0, scale, 0, 0]
|
||||||
|
|
||||||
|
private = fdArray[0].Private
|
||||||
|
charStrings = CharStrings(None, None, globalSubrs, private, fdSelect, fdArray)
|
||||||
|
for glypnName, charString in charStringsDict.items():
|
||||||
|
charString.private = private
|
||||||
|
charString.globalSubrs = globalSubrs
|
||||||
|
charStrings[glypnName] = charString
|
||||||
|
topDict.CharStrings = charStrings
|
||||||
|
|
||||||
|
fontSet.topDictIndex.append(topDict)
|
||||||
|
|
||||||
|
self.font["CFF2"] = newTable("CFF2")
|
||||||
|
self.font["CFF2"].cff = fontSet
|
||||||
|
|
||||||
|
if regions:
|
||||||
|
self.setupCFF2Regions(regions)
|
||||||
|
|
||||||
|
def setupCFF2Regions(self, regions):
|
||||||
|
from .ttLib.tables import otTables as ot
|
||||||
|
from .varLib.builder import buildVarStore, buildVarRegionAxis, buildVarData
|
||||||
|
from .cffLib import VarStoreData
|
||||||
|
|
||||||
|
assert "fvar" in self.font, "fvar must to be set up first"
|
||||||
|
assert "CFF2" in self.font, "CFF2 must to be set up first"
|
||||||
|
axisTags = [a.axisTag for a in self.font["fvar"].axes]
|
||||||
|
varRegionList = ot.VarRegionList()
|
||||||
|
varRegionList.RegionAxisCount = len(regions)
|
||||||
|
varRegionList.Region = []
|
||||||
|
for regionDict in regions:
|
||||||
|
region = ot.VarRegion()
|
||||||
|
region.VarRegionAxis = []
|
||||||
|
for tag in axisTags:
|
||||||
|
axisSupport = regionDict.get(tag, (0, 0, 0))
|
||||||
|
region.VarRegionAxis.append(buildVarRegionAxis(axisSupport))
|
||||||
|
region.VarRegionAxisCount = len(region.VarRegionAxis)
|
||||||
|
varRegionList.Region.append(region)
|
||||||
|
varData = buildVarData(list(range(len(regions))), None, optimize=False)
|
||||||
|
varStore = buildVarStore(varRegionList, [varData])
|
||||||
|
vstore = VarStoreData(otVarStore=varStore)
|
||||||
|
self.font["CFF2"].cff.topDictIndex[0].VarStore = vstore
|
||||||
|
|
||||||
def setupGlyf(self, glyphs, calcGlyphBounds=True):
|
def setupGlyf(self, glyphs, calcGlyphBounds=True):
|
||||||
"""Create the `glyf` table from a dict, that maps glyph names
|
"""Create the `glyf` table from a dict, that maps glyph names
|
||||||
to `fontTools.ttLib.tables._g_l_y_f.Glyph` objects, for example
|
to `fontTools.ttLib.tables._g_l_y_f.Glyph` objects, for example
|
||||||
|
276
Tests/fontBuilder/data/test_var.otf.ttx
Normal file
276
Tests/fontBuilder/data/test_var.otf.ttx
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ttFont sfntVersion="OTTO" ttLibVersion="3.33">
|
||||||
|
|
||||||
|
<GlyphOrder>
|
||||||
|
<!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
<GlyphID id="0" name=".notdef"/>
|
||||||
|
<GlyphID id="1" name="glyph00001"/>
|
||||||
|
<GlyphID id="2" name="A"/>
|
||||||
|
<GlyphID id="3" name="a"/>
|
||||||
|
</GlyphOrder>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<!-- Most of this table will be recalculated by the compiler -->
|
||||||
|
<tableVersion value="1.0"/>
|
||||||
|
<fontRevision value="1.0"/>
|
||||||
|
<checkSumAdjustment value="0xed07360f"/>
|
||||||
|
<magicNumber value="0x5f0f3cf5"/>
|
||||||
|
<flags value="00000000 00000011"/>
|
||||||
|
<unitsPerEm value="1000"/>
|
||||||
|
<created value="Wed Dec 5 11:55:26 2018"/>
|
||||||
|
<modified value="Wed Dec 5 11:55:26 2018"/>
|
||||||
|
<xMin value="0"/>
|
||||||
|
<yMin value="0"/>
|
||||||
|
<xMax value="0"/>
|
||||||
|
<yMax value="0"/>
|
||||||
|
<macStyle value="00000000 00000000"/>
|
||||||
|
<lowestRecPPEM value="3"/>
|
||||||
|
<fontDirectionHint value="2"/>
|
||||||
|
<indexToLocFormat value="0"/>
|
||||||
|
<glyphDataFormat value="0"/>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<hhea>
|
||||||
|
<tableVersion value="0x00010000"/>
|
||||||
|
<ascent value="824"/>
|
||||||
|
<descent value="200"/>
|
||||||
|
<lineGap value="0"/>
|
||||||
|
<advanceWidthMax value="0"/>
|
||||||
|
<minLeftSideBearing value="0"/>
|
||||||
|
<minRightSideBearing value="0"/>
|
||||||
|
<xMaxExtent value="0"/>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<maxp>
|
||||||
|
<tableVersion value="0x5000"/>
|
||||||
|
<numGlyphs value="4"/>
|
||||||
|
</maxp>
|
||||||
|
|
||||||
|
<OS_2>
|
||||||
|
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
|
||||||
|
will be recalculated by the compiler -->
|
||||||
|
<version value="3"/>
|
||||||
|
<xAvgCharWidth value="650"/>
|
||||||
|
<usWeightClass value="400"/>
|
||||||
|
<usWidthClass value="5"/>
|
||||||
|
<fsType value="00000000 00000100"/>
|
||||||
|
<ySubscriptXSize value="0"/>
|
||||||
|
<ySubscriptYSize value="0"/>
|
||||||
|
<ySubscriptXOffset value="0"/>
|
||||||
|
<ySubscriptYOffset value="0"/>
|
||||||
|
<ySuperscriptXSize value="0"/>
|
||||||
|
<ySuperscriptYSize value="0"/>
|
||||||
|
<ySuperscriptXOffset value="0"/>
|
||||||
|
<ySuperscriptYOffset value="0"/>
|
||||||
|
<yStrikeoutSize value="0"/>
|
||||||
|
<yStrikeoutPosition value="0"/>
|
||||||
|
<sFamilyClass value="0"/>
|
||||||
|
<panose>
|
||||||
|
<bFamilyType value="0"/>
|
||||||
|
<bSerifStyle value="0"/>
|
||||||
|
<bWeight value="0"/>
|
||||||
|
<bProportion value="0"/>
|
||||||
|
<bContrast value="0"/>
|
||||||
|
<bStrokeVariation value="0"/>
|
||||||
|
<bArmStyle value="0"/>
|
||||||
|
<bLetterForm value="0"/>
|
||||||
|
<bMidline value="0"/>
|
||||||
|
<bXHeight value="0"/>
|
||||||
|
</panose>
|
||||||
|
<ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/>
|
||||||
|
<ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<achVendID value="????"/>
|
||||||
|
<fsSelection value="00000000 00000000"/>
|
||||||
|
<usFirstCharIndex value="65"/>
|
||||||
|
<usLastCharIndex value="97"/>
|
||||||
|
<sTypoAscender value="825"/>
|
||||||
|
<sTypoDescender value="200"/>
|
||||||
|
<sTypoLineGap value="0"/>
|
||||||
|
<usWinAscent value="824"/>
|
||||||
|
<usWinDescent value="200"/>
|
||||||
|
<ulCodePageRange1 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
|
||||||
|
<sxHeight value="0"/>
|
||||||
|
<sCapHeight value="0"/>
|
||||||
|
<usDefaultChar value="0"/>
|
||||||
|
<usBreakChar value="32"/>
|
||||||
|
<usMaxContext value="2"/>
|
||||||
|
</OS_2>
|
||||||
|
|
||||||
|
<hmtx>
|
||||||
|
<mtx name=".notdef" width="600" lsb="0"/>
|
||||||
|
<mtx name="A" width="600" lsb="0"/>
|
||||||
|
<mtx name="a" width="800" lsb="0"/>
|
||||||
|
<mtx name="glyph00001" width="600" lsb="0"/>
|
||||||
|
</hmtx>
|
||||||
|
|
||||||
|
<cmap>
|
||||||
|
<tableVersion version="0"/>
|
||||||
|
<cmap_format_4 platformID="0" platEncID="3" language="0">
|
||||||
|
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
|
||||||
|
<map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
|
||||||
|
</cmap_format_4>
|
||||||
|
<cmap_format_4 platformID="3" platEncID="1" language="0">
|
||||||
|
<map code="0x41" name="A"/><!-- LATIN CAPITAL LETTER A -->
|
||||||
|
<map code="0x61" name="a"/><!-- LATIN SMALL LETTER A -->
|
||||||
|
</cmap_format_4>
|
||||||
|
</cmap>
|
||||||
|
|
||||||
|
<name>
|
||||||
|
<namerecord nameID="256" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||||
|
Test Axis
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="257" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||||
|
TotallyNormal
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="258" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||||
|
TotallyTested
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
HelloTestFont
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
TotallyNormal
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="6" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
HelloTestFont-TotallyNormal
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="256" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
Test Axis
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="257" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
TotallyNormal
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="258" platformID="3" platEncID="1" langID="0x409">
|
||||||
|
TotallyTested
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="1" platformID="3" platEncID="1" langID="0x413">
|
||||||
|
HalloTestFont
|
||||||
|
</namerecord>
|
||||||
|
<namerecord nameID="2" platformID="3" platEncID="1" langID="0x413">
|
||||||
|
TotaalNormaal
|
||||||
|
</namerecord>
|
||||||
|
</name>
|
||||||
|
|
||||||
|
<post>
|
||||||
|
<formatType value="3.0"/>
|
||||||
|
<italicAngle value="0.0"/>
|
||||||
|
<underlinePosition value="0"/>
|
||||||
|
<underlineThickness value="0"/>
|
||||||
|
<isFixedPitch value="0"/>
|
||||||
|
<minMemType42 value="0"/>
|
||||||
|
<maxMemType42 value="0"/>
|
||||||
|
<minMemType1 value="0"/>
|
||||||
|
<maxMemType1 value="0"/>
|
||||||
|
</post>
|
||||||
|
|
||||||
|
<CFF2>
|
||||||
|
<major value="2"/>
|
||||||
|
<minor value="0"/>
|
||||||
|
<CFFFont name="CFF2Font">
|
||||||
|
<FontMatrix value="0.001 0 0 0.001 0 0"/>
|
||||||
|
<FDArray>
|
||||||
|
<FontDict index="0">
|
||||||
|
<Private>
|
||||||
|
<BlueScale value="0.039625"/>
|
||||||
|
<BlueShift value="7"/>
|
||||||
|
<BlueFuzz value="1"/>
|
||||||
|
</Private>
|
||||||
|
</FontDict>
|
||||||
|
</FDArray>
|
||||||
|
<CharStrings>
|
||||||
|
<CharString name=".notdef">
|
||||||
|
100 100 rmoveto
|
||||||
|
900 vlineto
|
||||||
|
-67 67 66 -33 67 hhcurveto
|
||||||
|
67 66 33 67 67 hvcurveto
|
||||||
|
-900 vlineto
|
||||||
|
</CharString>
|
||||||
|
<CharString name="A">
|
||||||
|
100 100 rmoveto
|
||||||
|
900 vlineto
|
||||||
|
-67 67 66 -33 67 hhcurveto
|
||||||
|
67 66 33 67 67 hvcurveto
|
||||||
|
-900 vlineto
|
||||||
|
</CharString>
|
||||||
|
<CharString name="a">
|
||||||
|
200 200 -200 -200 2 blend
|
||||||
|
rmoveto
|
||||||
|
400 400 1 blend
|
||||||
|
hlineto
|
||||||
|
400 400 1 blend
|
||||||
|
vlineto
|
||||||
|
-400 -400 1 blend
|
||||||
|
hlineto
|
||||||
|
</CharString>
|
||||||
|
<CharString name="glyph00001">
|
||||||
|
100 100 rmoveto
|
||||||
|
900 vlineto
|
||||||
|
-67 67 66 -33 67 hhcurveto
|
||||||
|
67 66 33 67 67 hvcurveto
|
||||||
|
-900 vlineto
|
||||||
|
</CharString>
|
||||||
|
</CharStrings>
|
||||||
|
<VarStore Format="1">
|
||||||
|
<Format value="1"/>
|
||||||
|
<VarRegionList>
|
||||||
|
<!-- RegionAxisCount=1 -->
|
||||||
|
<!-- RegionCount=1 -->
|
||||||
|
<Region index="0">
|
||||||
|
<VarRegionAxis index="0">
|
||||||
|
<StartCoord value="0.0"/>
|
||||||
|
<PeakCoord value="1.0"/>
|
||||||
|
<EndCoord value="1.0"/>
|
||||||
|
</VarRegionAxis>
|
||||||
|
</Region>
|
||||||
|
</VarRegionList>
|
||||||
|
<!-- VarDataCount=1 -->
|
||||||
|
<VarData index="0">
|
||||||
|
<!-- ItemCount=0 -->
|
||||||
|
<NumShorts value="0"/>
|
||||||
|
<!-- VarRegionCount=1 -->
|
||||||
|
<VarRegionIndex index="0" value="0"/>
|
||||||
|
</VarData>
|
||||||
|
</VarStore>
|
||||||
|
</CFFFont>
|
||||||
|
|
||||||
|
<GlobalSubrs>
|
||||||
|
<!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
|
||||||
|
</GlobalSubrs>
|
||||||
|
</CFF2>
|
||||||
|
|
||||||
|
<fvar>
|
||||||
|
|
||||||
|
<!-- Test Axis -->
|
||||||
|
<Axis>
|
||||||
|
<AxisTag>TEST</AxisTag>
|
||||||
|
<Flags>0x0</Flags>
|
||||||
|
<MinValue>0.0</MinValue>
|
||||||
|
<DefaultValue>0.0</DefaultValue>
|
||||||
|
<MaxValue>100.0</MaxValue>
|
||||||
|
<AxisNameID>256</AxisNameID>
|
||||||
|
</Axis>
|
||||||
|
|
||||||
|
<!-- TotallyNormal -->
|
||||||
|
<NamedInstance flags="0x0" subfamilyNameID="257">
|
||||||
|
<coord axis="TEST" value="0.0"/>
|
||||||
|
</NamedInstance>
|
||||||
|
|
||||||
|
<!-- TotallyTested -->
|
||||||
|
<NamedInstance flags="0x0" subfamilyNameID="258">
|
||||||
|
<coord axis="TEST" value="100.0"/>
|
||||||
|
</NamedInstance>
|
||||||
|
</fvar>
|
||||||
|
|
||||||
|
</ttFont>
|
@ -10,6 +10,7 @@ from fontTools.pens.t2CharStringPen import T2CharStringPen
|
|||||||
from fontTools.fontBuilder import FontBuilder
|
from fontTools.fontBuilder import FontBuilder
|
||||||
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
from fontTools.ttLib.tables.TupleVariation import TupleVariation
|
||||||
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
|
from fontTools.ttLib.tables._g_l_y_f import GlyphCoordinates
|
||||||
|
from fontTools.misc.psCharStrings import T2CharString
|
||||||
|
|
||||||
|
|
||||||
def getTestData(fileName, mode="r"):
|
def getTestData(fileName, mode="r"):
|
||||||
@ -190,3 +191,60 @@ def test_build_var(tmpdir):
|
|||||||
testData = strip_VariableItems(f.read())
|
testData = strip_VariableItems(f.read())
|
||||||
refData = strip_VariableItems(getTestData("test_var.ttf.ttx"))
|
refData = strip_VariableItems(getTestData("test_var.ttf.ttx"))
|
||||||
assert refData == testData
|
assert refData == testData
|
||||||
|
|
||||||
|
|
||||||
|
def test_build_cff2(tmpdir):
|
||||||
|
outPath = os.path.join(str(tmpdir), "test_var.otf")
|
||||||
|
|
||||||
|
fb = FontBuilder(1000, isTTF=False)
|
||||||
|
fb.setupGlyphOrder([".notdef", ".null", "A", "a"])
|
||||||
|
fb.setupCharacterMap({65: "A", 97: "a"})
|
||||||
|
|
||||||
|
advanceWidths = {".notdef": 600, "A": 600, "a": 800, ".null": 600}
|
||||||
|
|
||||||
|
familyName = "HelloTestFont"
|
||||||
|
styleName = "TotallyNormal"
|
||||||
|
nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"),
|
||||||
|
styleName=dict(en="TotallyNormal", nl="TotaalNormaal"))
|
||||||
|
nameStrings['psName'] = familyName + "-" + styleName
|
||||||
|
fb.setupNameTable(nameStrings)
|
||||||
|
|
||||||
|
axes = [
|
||||||
|
('TEST', 0, 0, 100, "Test Axis"),
|
||||||
|
]
|
||||||
|
instances = [
|
||||||
|
dict(location=dict(TEST=0), stylename="TotallyNormal"),
|
||||||
|
dict(location=dict(TEST=100), stylename="TotallyTested"),
|
||||||
|
]
|
||||||
|
fb.setupFvar(axes, instances)
|
||||||
|
|
||||||
|
pen = T2CharStringPen(None, None, CFF2=True)
|
||||||
|
drawTestGlyph(pen)
|
||||||
|
charString = pen.getCharString()
|
||||||
|
|
||||||
|
program = [
|
||||||
|
200, 200, -200, -200, 2, "blend", "rmoveto",
|
||||||
|
400, 400, 1, "blend", "hlineto",
|
||||||
|
400, 400, 1, "blend", "vlineto",
|
||||||
|
-400, -400, 1, "blend", "hlineto"
|
||||||
|
]
|
||||||
|
charStringVariable = T2CharString(program=program)
|
||||||
|
|
||||||
|
charStrings = {".notdef": charString, "A": charString, "a": charStringVariable, ".null": charString}
|
||||||
|
fb.setupCFF2(charStrings, [{}], regions=[{"TEST": (0, 1, 1)}])
|
||||||
|
|
||||||
|
metrics = {gn: (advanceWidth, 0) for gn, advanceWidth in advanceWidths.items()}
|
||||||
|
fb.setupHorizontalMetrics(metrics)
|
||||||
|
|
||||||
|
fb.setupHorizontalHeader(ascent=824, descent=200)
|
||||||
|
fb.setupOS2(sTypoAscender=825, sTypoDescender=200, usWinAscent=824, usWinDescent=200)
|
||||||
|
fb.setupPost()
|
||||||
|
|
||||||
|
fb.save(outPath)
|
||||||
|
|
||||||
|
f = TTFont(outPath)
|
||||||
|
f.saveXML(outPath + ".ttx")
|
||||||
|
with open(outPath + ".ttx") as f:
|
||||||
|
testData = strip_VariableItems(f.read())
|
||||||
|
refData = strip_VariableItems(getTestData("test_var.otf.ttx"))
|
||||||
|
assert refData == testData
|
||||||
|
Loading…
x
Reference in New Issue
Block a user