[AAT] Implement subsetting of prop
table with AAT glyph properties
This commit is contained in:
parent
61d2cde14a
commit
28b179a018
@ -13,6 +13,7 @@ import sys
|
||||
import struct
|
||||
import array
|
||||
import logging
|
||||
from collections import Counter
|
||||
from types import MethodType
|
||||
|
||||
__usage__ = "pyftsubset font-file [glyph...] [--option=value]..."
|
||||
@ -1704,6 +1705,26 @@ def subset_glyphs(self, s):
|
||||
self.extraNames = [] # This seems to do it
|
||||
return True # Required table
|
||||
|
||||
@_add_method(ttLib.getTableClass('prop'))
|
||||
def subset_glyphs(self, s):
|
||||
prop = self.table.GlyphProperties
|
||||
if prop.Format == 0:
|
||||
return prop.DefaultProperties != 0
|
||||
elif prop.Format == 1:
|
||||
prop.Properties = {g: prop.Properties.get(g, prop.DefaultProperties)
|
||||
for g in s.glyphs}
|
||||
mostCommon, _cnt = Counter(prop.Properties.values()).most_common(1)[0]
|
||||
prop.DefaultProperties = mostCommon
|
||||
prop.Properties = {g: prop for g, prop in prop.Properties.items()
|
||||
if prop != mostCommon}
|
||||
if len(prop.Properties) == 0:
|
||||
del prop.Properties
|
||||
prop.Format = 0
|
||||
return prop.DefaultProperties != 0
|
||||
return True
|
||||
else:
|
||||
assert False, "unknown 'prop' format %s" % prop.Format
|
||||
|
||||
@_add_method(ttLib.getTableClass('COLR'))
|
||||
def closure_glyphs(self, s):
|
||||
decompose = s.glyphs
|
||||
|
322
Tests/subset/data/TestPROP.ttx
Normal file
322
Tests/subset/data/TestPROP.ttx
Normal file
@ -0,0 +1,322 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0">
|
||||
|
||||
<GlyphOrder>
|
||||
<GlyphID id="0" name=".notdef"/>
|
||||
<GlyphID id="1" name="space"/>
|
||||
<GlyphID id="2" name="zero"/>
|
||||
<GlyphID id="3" name="one"/>
|
||||
<GlyphID id="4" name="two"/>
|
||||
<GlyphID id="5" name="A"/>
|
||||
</GlyphOrder>
|
||||
|
||||
<head>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="1.0"/>
|
||||
<fontRevision value="1.0"/>
|
||||
<checkSumAdjustment value="0xf1c8c54f"/>
|
||||
<magicNumber value="0x5f0f3cf5"/>
|
||||
<flags value="00000000 00000011"/>
|
||||
<unitsPerEm value="1000"/>
|
||||
<created value="Thu Mar 31 07:56:22 2016"/>
|
||||
<modified value="Thu Mar 31 11:16:24 2016"/>
|
||||
<xMin value="40"/>
|
||||
<yMin value="-10"/>
|
||||
<xMax value="620"/>
|
||||
<yMax value="710"/>
|
||||
<macStyle value="00000000 00000000"/>
|
||||
<lowestRecPPEM value="7"/>
|
||||
<fontDirectionHint value="2"/>
|
||||
<indexToLocFormat value="0"/>
|
||||
<glyphDataFormat value="0"/>
|
||||
</head>
|
||||
|
||||
<hhea>
|
||||
<tableVersion value="0x00010000"/>
|
||||
<ascent value="1000"/>
|
||||
<descent value="-200"/>
|
||||
<lineGap value="0"/>
|
||||
<advanceWidthMax value="700"/>
|
||||
<minLeftSideBearing value="40"/>
|
||||
<minRightSideBearing value="40"/>
|
||||
<xMaxExtent value="620"/>
|
||||
<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>
|
||||
<!-- Most of this table will be recalculated by the compiler -->
|
||||
<tableVersion value="0x10000"/>
|
||||
<numGlyphs value="6"/>
|
||||
<maxPoints value="32"/>
|
||||
<maxContours value="2"/>
|
||||
<maxCompositePoints value="0"/>
|
||||
<maxCompositeContours value="0"/>
|
||||
<maxZones value="1"/>
|
||||
<maxTwilightPoints value="0"/>
|
||||
<maxStorage value="0"/>
|
||||
<maxFunctionDefs value="1"/>
|
||||
<maxInstructionDefs value="0"/>
|
||||
<maxStackElements value="0"/>
|
||||
<maxSizeOfInstructions value="0"/>
|
||||
<maxComponentElements value="0"/>
|
||||
<maxComponentDepth value="0"/>
|
||||
</maxp>
|
||||
|
||||
<OS_2>
|
||||
<!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
|
||||
will be recalculated by the compiler -->
|
||||
<version value="3"/>
|
||||
<xAvgCharWidth value="606"/>
|
||||
<usWeightClass value="400"/>
|
||||
<usWidthClass value="5"/>
|
||||
<fsType value="00000000 00001000"/>
|
||||
<ySubscriptXSize value="650"/>
|
||||
<ySubscriptYSize value="600"/>
|
||||
<ySubscriptXOffset value="0"/>
|
||||
<ySubscriptYOffset value="75"/>
|
||||
<ySuperscriptXSize value="650"/>
|
||||
<ySuperscriptYSize value="600"/>
|
||||
<ySuperscriptXOffset value="0"/>
|
||||
<ySuperscriptYOffset value="350"/>
|
||||
<yStrikeoutSize value="50"/>
|
||||
<yStrikeoutPosition value="300"/>
|
||||
<sFamilyClass value="0"/>
|
||||
<panose>
|
||||
<bFamilyType value="0"/>
|
||||
<bSerifStyle value="0"/>
|
||||
<bWeight value="5"/>
|
||||
<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="UKWN"/>
|
||||
<fsSelection value="00000000 01000000"/>
|
||||
<usFirstCharIndex value="32"/>
|
||||
<usLastCharIndex value="8722"/>
|
||||
<sTypoAscender value="800"/>
|
||||
<sTypoDescender value="-200"/>
|
||||
<sTypoLineGap value="200"/>
|
||||
<usWinAscent value="1000"/>
|
||||
<usWinDescent value="200"/>
|
||||
<ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
|
||||
<ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
|
||||
<sxHeight value="500"/>
|
||||
<sCapHeight value="700"/>
|
||||
<usDefaultChar value="0"/>
|
||||
<usBreakChar value="32"/>
|
||||
<usMaxContext value="0"/>
|
||||
</OS_2>
|
||||
|
||||
<hmtx>
|
||||
<mtx name=".notdef" width="700" lsb="80"/>
|
||||
<mtx name="space" width="700" lsb="0"/>
|
||||
<mtx name="zero" width="625" lsb="40"/>
|
||||
<mtx name="one" width="660" lsb="80"/>
|
||||
<mtx name="two" width="660" lsb="80"/>
|
||||
<mtx name="A" width="660" lsb="80"/>
|
||||
</hmtx>
|
||||
|
||||
<cmap>
|
||||
<tableVersion version="0"/>
|
||||
<cmap_format_4 platformID="0" platEncID="3" language="0">
|
||||
<map code="0x20" name="space"/>
|
||||
<map code="0x30" name="zero"/>
|
||||
<map code="0x31" name="one"/>
|
||||
<map code="0x32" name="two"/>
|
||||
<map code="0x41" name="A"/>
|
||||
</cmap_format_4>
|
||||
</cmap>
|
||||
|
||||
<loca>
|
||||
<!-- The 'loca' table will be calculated by the compiler -->
|
||||
</loca>
|
||||
|
||||
<glyf>
|
||||
|
||||
<!-- The xMin, yMin, xMax and yMax values
|
||||
will be recalculated by the compiler. -->
|
||||
|
||||
<TTGlyph name=".notdef" xMin="80" yMin="0" xMax="620" yMax="700">
|
||||
<contour>
|
||||
<pt x="620" y="700" on="1"/>
|
||||
<pt x="620" y="0" on="1"/>
|
||||
<pt x="80" y="0" on="1"/>
|
||||
<pt x="80" y="700" on="1"/>
|
||||
</contour>
|
||||
<contour>
|
||||
<pt x="160" y="80" on="1"/>
|
||||
<pt x="540" y="80" on="1"/>
|
||||
<pt x="540" y="620" on="1"/>
|
||||
<pt x="160" y="620" on="1"/>
|
||||
</contour>
|
||||
<instructions><assembly>
|
||||
</assembly></instructions>
|
||||
</TTGlyph>
|
||||
|
||||
<TTGlyph name="one" xMin="80" yMin="230" xMax="580" yMax="490">
|
||||
<contour>
|
||||
<pt x="580" y="490" on="1"/>
|
||||
<pt x="580" y="410" on="1"/>
|
||||
<pt x="80" y="410" on="1"/>
|
||||
<pt x="80" y="490" on="1"/>
|
||||
</contour>
|
||||
<contour>
|
||||
<pt x="580" y="310" on="1"/>
|
||||
<pt x="580" y="230" on="1"/>
|
||||
<pt x="80" y="230" on="1"/>
|
||||
<pt x="80" y="310" on="1"/>
|
||||
</contour>
|
||||
<instructions><assembly>
|
||||
</assembly></instructions>
|
||||
</TTGlyph>
|
||||
|
||||
<TTGlyph name="two" xMin="80" yMin="310" xMax="580" yMax="390">
|
||||
<contour>
|
||||
<pt x="580" y="390" on="1"/>
|
||||
<pt x="580" y="310" on="1"/>
|
||||
<pt x="80" y="310" on="1"/>
|
||||
<pt x="80" y="390" on="1"/>
|
||||
</contour>
|
||||
<instructions><assembly>
|
||||
</assembly></instructions>
|
||||
</TTGlyph>
|
||||
|
||||
<TTGlyph name="A" xMin="80" yMin="100" xMax="580" yMax="600">
|
||||
<contour>
|
||||
<pt x="580" y="310" on="1"/>
|
||||
<pt x="370" y="310" on="1"/>
|
||||
<pt x="370" y="100" on="1"/>
|
||||
<pt x="290" y="100" on="1"/>
|
||||
<pt x="290" y="310" on="1"/>
|
||||
<pt x="80" y="310" on="1"/>
|
||||
<pt x="80" y="390" on="1"/>
|
||||
<pt x="290" y="390" on="1"/>
|
||||
<pt x="290" y="600" on="1"/>
|
||||
<pt x="370" y="600" on="1"/>
|
||||
<pt x="370" y="390" on="1"/>
|
||||
<pt x="580" y="390" on="1"/>
|
||||
</contour>
|
||||
<instructions><assembly>
|
||||
</assembly></instructions>
|
||||
</TTGlyph>
|
||||
|
||||
<TTGlyph name="space"/><!-- contains no outline data -->
|
||||
|
||||
<TTGlyph name="zero" xMin="40" yMin="-10" xMax="585" yMax="710">
|
||||
<contour>
|
||||
<pt x="237" y="-10" on="0"/>
|
||||
<pt x="113" y="78" on="0"/>
|
||||
<pt x="40" y="241" on="0"/>
|
||||
<pt x="40" y="350" on="1"/>
|
||||
<pt x="40" y="459" on="0"/>
|
||||
<pt x="113" y="622" on="0"/>
|
||||
<pt x="237" y="710" on="0"/>
|
||||
<pt x="313" y="710" on="1"/>
|
||||
<pt x="388" y="710" on="0"/>
|
||||
<pt x="513" y="622" on="0"/>
|
||||
<pt x="585" y="459" on="0"/>
|
||||
<pt x="585" y="350" on="1"/>
|
||||
<pt x="585" y="241" on="0"/>
|
||||
<pt x="513" y="78" on="0"/>
|
||||
<pt x="388" y="-10" on="0"/>
|
||||
<pt x="313" y="-10" on="1"/>
|
||||
</contour>
|
||||
<contour>
|
||||
<pt x="366" y="74" on="0"/>
|
||||
<pt x="454" y="144" on="0"/>
|
||||
<pt x="505" y="270" on="0"/>
|
||||
<pt x="505" y="350" on="1"/>
|
||||
<pt x="505" y="430" on="0"/>
|
||||
<pt x="454" y="556" on="0"/>
|
||||
<pt x="366" y="626" on="0"/>
|
||||
<pt x="313" y="626" on="1"/>
|
||||
<pt x="260" y="626" on="0"/>
|
||||
<pt x="171" y="556" on="0"/>
|
||||
<pt x="120" y="430" on="0"/>
|
||||
<pt x="120" y="350" on="1"/>
|
||||
<pt x="120" y="270" on="0"/>
|
||||
<pt x="171" y="144" on="0"/>
|
||||
<pt x="260" y="74" on="0"/>
|
||||
<pt x="313" y="74" on="1"/>
|
||||
</contour>
|
||||
<instructions><assembly>
|
||||
</assembly></instructions>
|
||||
</TTGlyph>
|
||||
|
||||
</glyf>
|
||||
|
||||
<name>
|
||||
<namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
TestPROP
|
||||
</namerecord>
|
||||
<namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
Regular
|
||||
</namerecord>
|
||||
<namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
TestPROP
|
||||
</namerecord>
|
||||
<namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
|
||||
TestPROP-Regular
|
||||
</namerecord>
|
||||
</name>
|
||||
|
||||
<post>
|
||||
<formatType value="2.0"/>
|
||||
<italicAngle value="0.0"/>
|
||||
<underlinePosition value="-75"/>
|
||||
<underlineThickness value="50"/>
|
||||
<isFixedPitch value="0"/>
|
||||
<minMemType42 value="0"/>
|
||||
<maxMemType42 value="0"/>
|
||||
<minMemType1 value="0"/>
|
||||
<maxMemType1 value="0"/>
|
||||
<psNames>
|
||||
<!-- This file uses unique glyph names based on the information
|
||||
found in the 'post' table. Since these names might not be unique,
|
||||
we have to invent artificial names in case of clashes. In order to
|
||||
be able to retain the original information, we need a name to
|
||||
ps name mapping for those cases where they differ. That's what
|
||||
you see below.
|
||||
-->
|
||||
</psNames>
|
||||
<extraNames>
|
||||
<!-- following are the name that are not taken from the standard Mac glyph order -->
|
||||
</extraNames>
|
||||
</post>
|
||||
|
||||
<prop>
|
||||
<Version value="3.0"/>
|
||||
<GlyphProperties Format="1">
|
||||
<DefaultProperties value="0"/>
|
||||
<Properties>
|
||||
<Lookup glyph="space" value="10"/>
|
||||
<Lookup glyph="zero" value="3"/>
|
||||
<Lookup glyph="one" value="3"/>
|
||||
<Lookup glyph="two" value="3"/>
|
||||
</Properties>
|
||||
</GlyphProperties>
|
||||
</prop>
|
||||
|
||||
<gasp>
|
||||
<gaspRange rangeMaxPPEM="7" rangeGaspBehavior="10"/>
|
||||
<gaspRange rangeMaxPPEM="19" rangeGaspBehavior="7"/>
|
||||
<gaspRange rangeMaxPPEM="65535" rangeGaspBehavior="15"/>
|
||||
</gasp>
|
||||
|
||||
</ttFont>
|
11
Tests/subset/data/expect_prop_0.ttx
Normal file
11
Tests/subset/data/expect_prop_0.ttx
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont>
|
||||
|
||||
<prop>
|
||||
<Version value="3.0"/>
|
||||
<GlyphProperties Format="0">
|
||||
<DefaultProperties value="3"/>
|
||||
</GlyphProperties>
|
||||
</prop>
|
||||
|
||||
</ttFont>
|
14
Tests/subset/data/expect_prop_1.ttx
Normal file
14
Tests/subset/data/expect_prop_1.ttx
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ttFont>
|
||||
|
||||
<prop>
|
||||
<Version value="3.0"/>
|
||||
<GlyphProperties Format="1">
|
||||
<DefaultProperties value="3"/>
|
||||
<Properties>
|
||||
<Lookup glyph=".notdef" value="0"/>
|
||||
</Properties>
|
||||
</GlyphProperties>
|
||||
</prop>
|
||||
|
||||
</ttFont>
|
@ -123,6 +123,41 @@ class SubsetTest(unittest.TestCase):
|
||||
subsetfont = TTFont(subsetpath)
|
||||
self.expect_ttx(subsetfont, self.getpath("expect_keep_math.ttx"), ["GlyphOrder", "CFF ", "MATH", "hmtx"])
|
||||
|
||||
def test_subset_prop_remove_default_zero(self):
|
||||
# If all glyphs have an AAT glyph property with value 0,
|
||||
# the "prop" table should be removed from the subsetted font.
|
||||
_, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
|
||||
subsetpath = self.temp_path(".ttf")
|
||||
subset.main([fontpath, "--unicodes=U+0041",
|
||||
"--output-file=%s" % subsetpath])
|
||||
subsetfont = TTFont(subsetpath)
|
||||
self.assertNotIn("prop", subsetfont)
|
||||
|
||||
def test_subset_prop_0(self):
|
||||
# If all glyphs share the same AAT glyph properties, the "prop" table
|
||||
# in the subsetted font should use format 0.
|
||||
#
|
||||
# Unless the shared value is zero, in which case the subsetted font
|
||||
# should have no "prop" table at all. But that case has already been
|
||||
# tested above in test_subset_prop_remove_default_zero().
|
||||
_, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
|
||||
subsetpath = self.temp_path(".ttf")
|
||||
subset.main([fontpath, "--unicodes=U+0030-0032", "--no-notdef-glyph",
|
||||
"--output-file=%s" % subsetpath])
|
||||
subsetfont = TTFont(subsetpath)
|
||||
self.expect_ttx(subsetfont, self.getpath("expect_prop_0.ttx"), ["prop"])
|
||||
|
||||
def test_subset_prop_1(self):
|
||||
# If not all glyphs share the same AAT glyph properties, the subsetted
|
||||
# font should contain a "prop" table in format 1. To save space, the
|
||||
# DefaultProperties should be set to the most frequent value.
|
||||
_, fontpath = self.compile_font(self.getpath("TestPROP.ttx"), ".ttf")
|
||||
subsetpath = self.temp_path(".ttf")
|
||||
subset.main([fontpath, "--unicodes=U+0030-0032", "--notdef-outline",
|
||||
"--output-file=%s" % subsetpath])
|
||||
subsetfont = TTFont(subsetpath)
|
||||
self.expect_ttx(subsetfont, self.getpath("expect_prop_1.ttx"), ["prop"])
|
||||
|
||||
def test_options(self):
|
||||
# https://github.com/behdad/fonttools/issues/413
|
||||
opt1 = subset.Options()
|
||||
|
Loading…
x
Reference in New Issue
Block a user