[feaLib] glyph class as list and tuple instead of set and frozenset
This commit is contained in:
parent
cdda278bd4
commit
17c8e582d0
@ -34,7 +34,7 @@ class GlyphName(Expression):
|
||||
self.glyph = glyph
|
||||
|
||||
def glyphSet(self):
|
||||
return frozenset((self.glyph,))
|
||||
return (self.glyph,)
|
||||
|
||||
|
||||
class GlyphClass(Expression):
|
||||
@ -44,7 +44,7 @@ class GlyphClass(Expression):
|
||||
self.glyphs = glyphs
|
||||
|
||||
def glyphSet(self):
|
||||
return frozenset(self.glyphs)
|
||||
return tuple(self.glyphs)
|
||||
|
||||
|
||||
class GlyphClassName(Expression):
|
||||
@ -55,7 +55,7 @@ class GlyphClassName(Expression):
|
||||
self.glyphclass = glyphclass
|
||||
|
||||
def glyphSet(self):
|
||||
return frozenset(self.glyphclass.glyphs)
|
||||
return tuple(self.glyphclass.glyphs)
|
||||
|
||||
|
||||
class MarkClassName(Expression):
|
||||
@ -123,7 +123,7 @@ class GlyphClassDefinition(Statement):
|
||||
self.glyphs = glyphs
|
||||
|
||||
def glyphSet(self):
|
||||
return frozenset(self.glyphs)
|
||||
return tuple(self.glyphs)
|
||||
|
||||
|
||||
class GlyphClassDefStatement(Statement):
|
||||
@ -136,11 +136,12 @@ class GlyphClassDefStatement(Statement):
|
||||
self.componentGlyphs = componentGlyphs
|
||||
|
||||
def build(self, builder):
|
||||
base = self.baseGlyphs.glyphSet() if self.baseGlyphs else set()
|
||||
liga = self.ligatureGlyphs.glyphSet() if self.ligatureGlyphs else set()
|
||||
mark = self.markGlyphs.glyphSet() if self.markGlyphs else set()
|
||||
base = self.baseGlyphs.glyphSet() if self.baseGlyphs else tuple()
|
||||
liga = self.ligatureGlyphs.glyphSet() \
|
||||
if self.ligatureGlyphs else tuple()
|
||||
mark = self.markGlyphs.glyphSet() if self.markGlyphs else tuple()
|
||||
comp = (self.componentGlyphs.glyphSet()
|
||||
if self.componentGlyphs else set())
|
||||
if self.componentGlyphs else tuple())
|
||||
builder.add_glyphClassDef(self.location, base, liga, mark, comp)
|
||||
|
||||
|
||||
@ -169,7 +170,7 @@ class MarkClass(object):
|
||||
self.glyphs[glyph] = definition
|
||||
|
||||
def glyphSet(self):
|
||||
return frozenset(self.glyphs.keys())
|
||||
return tuple(self.glyphs.keys())
|
||||
|
||||
|
||||
class MarkClassDefinition(Statement):
|
||||
|
@ -901,7 +901,7 @@ class Builder(object):
|
||||
if value is None:
|
||||
subs.append(None)
|
||||
continue
|
||||
if not glyphs.isdisjoint(sub.mapping.keys()):
|
||||
if not set(glyphs).isdisjoint(sub.mapping.keys()):
|
||||
sub = self.get_chained_lookup_(location, SinglePosBuilder)
|
||||
for glyph in glyphs:
|
||||
sub.add_pos(location, glyph, value)
|
||||
|
@ -58,7 +58,7 @@ class BuilderTest(unittest.TestCase):
|
||||
spec5h1 spec6b_ii spec6d2 spec6e spec6f
|
||||
spec6h_ii spec6h_iii_1 spec6h_iii_3d spec8a spec8b spec8c
|
||||
spec9a spec9b spec9c1 spec9c2 spec9c3 spec9d spec9e spec9f
|
||||
bug453 bug463 bug501 bug502 bug505 bug506 bug509 bug512 bug568
|
||||
bug453 bug463 bug501 bug502 bug504 bug505 bug506 bug509 bug512 bug568
|
||||
name size size2
|
||||
""".split()
|
||||
|
||||
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||
from fontTools.feaLib.error import FeatureLibError
|
||||
from fontTools.feaLib.lexer import Lexer, IncludingLexer
|
||||
from fontTools.misc.py23 import *
|
||||
from collections import OrderedDict
|
||||
import fontTools.feaLib.ast as ast
|
||||
import logging
|
||||
import os
|
||||
@ -190,7 +191,7 @@ class Parser(object):
|
||||
return ast.GlyphClassName(self.cur_token_location_, gc)
|
||||
|
||||
self.expect_symbol_("[")
|
||||
glyphs = set()
|
||||
glyphs = list()
|
||||
location = self.cur_token_location_
|
||||
while self.next_token_ != "]":
|
||||
if self.next_token_type_ is Lexer.NAME:
|
||||
@ -200,11 +201,11 @@ class Parser(object):
|
||||
range_start = glyph
|
||||
self.expect_symbol_("-")
|
||||
range_end = self.expect_glyph_()
|
||||
glyphs.update(self.make_glyph_range_(range_location,
|
||||
glyphs.extend(self.make_glyph_range_(range_location,
|
||||
range_start,
|
||||
range_end))
|
||||
else:
|
||||
glyphs.add(glyph)
|
||||
glyphs.append(glyph)
|
||||
elif self.next_token_type_ is Lexer.CID:
|
||||
glyph = self.expect_glyph_()
|
||||
if self.next_token_ == "-":
|
||||
@ -212,10 +213,10 @@ class Parser(object):
|
||||
range_start = self.cur_token_
|
||||
self.expect_symbol_("-")
|
||||
range_end = self.expect_cid_()
|
||||
glyphs.update(self.make_cid_range_(range_location,
|
||||
glyphs.extend(self.make_cid_range_(range_location,
|
||||
range_start, range_end))
|
||||
else:
|
||||
glyphs.add("cid%05d" % self.cur_token_)
|
||||
glyphs.append("cid%05d" % self.cur_token_)
|
||||
elif self.next_token_type_ is Lexer.GLYPHCLASS:
|
||||
self.advance_lexer_()
|
||||
gc = self.glyphclasses_.resolve(self.cur_token_)
|
||||
@ -223,7 +224,7 @@ class Parser(object):
|
||||
raise FeatureLibError(
|
||||
"Unknown glyph class @%s" % self.cur_token_,
|
||||
self.cur_token_location_)
|
||||
glyphs.update(gc.glyphSet())
|
||||
glyphs.extend(gc.glyphSet())
|
||||
else:
|
||||
raise FeatureLibError(
|
||||
"Expected glyph name, glyph range, "
|
||||
@ -592,8 +593,8 @@ class Parser(object):
|
||||
# Format C: "substitute [a-d] by [A.sc-D.sc];"
|
||||
if (not reverse and len(old) == 1 and len(new) == 1 and
|
||||
num_lookups == 0):
|
||||
glyphs = sorted(list(old[0].glyphSet()))
|
||||
replacements = sorted(list(new[0].glyphSet()))
|
||||
glyphs = list(old[0].glyphSet())
|
||||
replacements = list(new[0].glyphSet())
|
||||
if len(replacements) == 1:
|
||||
replacements = replacements * len(glyphs)
|
||||
if len(glyphs) != len(replacements):
|
||||
@ -601,10 +602,12 @@ class Parser(object):
|
||||
'Expected a glyph class with %d elements after "by", '
|
||||
'but found a glyph class with %d elements' %
|
||||
(len(glyphs), len(replacements)), location)
|
||||
return ast.SingleSubstStatement(location,
|
||||
dict(zip(glyphs, replacements)),
|
||||
old_prefix, old_suffix,
|
||||
forceChain=hasMarks)
|
||||
return ast.SingleSubstStatement(
|
||||
location,
|
||||
OrderedDict(zip(glyphs, replacements)),
|
||||
old_prefix, old_suffix,
|
||||
forceChain=hasMarks
|
||||
)
|
||||
|
||||
# GSUB lookup type 2: Multiple substitution.
|
||||
# Format: "substitute f_f_i by f f i;"
|
||||
@ -1257,18 +1260,18 @@ class Parser(object):
|
||||
return ''.join(reversed(list(s)))
|
||||
|
||||
def make_cid_range_(self, location, start, limit):
|
||||
"""(location, 999, 1001) --> {"cid00999", "cid01000", "cid01001"}"""
|
||||
result = set()
|
||||
"""(location, 999, 1001) --> ["cid00999", "cid01000", "cid01001"]"""
|
||||
result = list()
|
||||
if start > limit:
|
||||
raise FeatureLibError(
|
||||
"Bad range: start should be less than limit", location)
|
||||
for cid in range(start, limit + 1):
|
||||
result.add("cid%05d" % cid)
|
||||
result.append("cid%05d" % cid)
|
||||
return result
|
||||
|
||||
def make_glyph_range_(self, location, start, limit):
|
||||
"""(location, "a.sc", "d.sc") --> {"a.sc", "b.sc", "c.sc", "d.sc"}"""
|
||||
result = set()
|
||||
"""(location, "a.sc", "d.sc") --> ["a.sc", "b.sc", "c.sc", "d.sc"]"""
|
||||
result = list()
|
||||
if len(start) != len(limit):
|
||||
raise FeatureLibError(
|
||||
"Bad range: \"%s\" and \"%s\" should have the same length" %
|
||||
@ -1292,20 +1295,20 @@ class Parser(object):
|
||||
uppercase = re.compile(r'^[A-Z]$')
|
||||
if uppercase.match(start_range) and uppercase.match(limit_range):
|
||||
for c in range(ord(start_range), ord(limit_range) + 1):
|
||||
result.add("%s%c%s" % (prefix, c, suffix))
|
||||
result.append("%s%c%s" % (prefix, c, suffix))
|
||||
return result
|
||||
|
||||
lowercase = re.compile(r'^[a-z]$')
|
||||
if lowercase.match(start_range) and lowercase.match(limit_range):
|
||||
for c in range(ord(start_range), ord(limit_range) + 1):
|
||||
result.add("%s%c%s" % (prefix, c, suffix))
|
||||
result.append("%s%c%s" % (prefix, c, suffix))
|
||||
return result
|
||||
|
||||
digits = re.compile(r'^[0-9]{1,3}$')
|
||||
if digits.match(start_range) and digits.match(limit_range):
|
||||
for i in range(int(start_range, 10), int(limit_range, 10) + 1):
|
||||
number = ("000" + str(i))[-len(start_range):]
|
||||
result.add("%s%s%s" % (prefix, number, suffix))
|
||||
result.append("%s%s%s" % (prefix, number, suffix))
|
||||
return result
|
||||
|
||||
raise FeatureLibError("Bad range: \"%s-%s\"" % (start, limit),
|
||||
|
@ -151,7 +151,7 @@ class ParserTest(unittest.TestCase):
|
||||
def test_glyphclass(self):
|
||||
[gc] = self.parse("@dash = [endash emdash figuredash];").statements
|
||||
self.assertEqual(gc.name, "dash")
|
||||
self.assertEqual(gc.glyphs, {"endash", "emdash", "figuredash"})
|
||||
self.assertEqual(gc.glyphs, ("endash", "emdash", "figuredash"))
|
||||
|
||||
def test_glyphclass_glyphNameTooLong(self):
|
||||
self.assertRaisesRegex(
|
||||
@ -173,12 +173,12 @@ class ParserTest(unittest.TestCase):
|
||||
def test_glyphclass_empty(self):
|
||||
[gc] = self.parse("@empty_set = [];").statements
|
||||
self.assertEqual(gc.name, "empty_set")
|
||||
self.assertEqual(gc.glyphs, set())
|
||||
self.assertEqual(gc.glyphs, tuple())
|
||||
|
||||
def test_glyphclass_equality(self):
|
||||
[foo, bar] = self.parse("@foo = [a b]; @bar = @foo;").statements
|
||||
self.assertEqual(foo.glyphs, {"a", "b"})
|
||||
self.assertEqual(bar.glyphs, {"a", "b"})
|
||||
self.assertEqual(foo.glyphs, ("a", "b"))
|
||||
self.assertEqual(bar.glyphs, ("a", "b"))
|
||||
|
||||
def test_glyphclass_from_markClass(self):
|
||||
doc = self.parse(
|
||||
@ -187,12 +187,12 @@ class ParserTest(unittest.TestCase):
|
||||
"@MARKS = [@TOP_MARKS @BOTTOM_MARKS ogonek];"
|
||||
"@ALL = @MARKS;")
|
||||
self.assertEqual(doc.statements[-1].glyphSet(),
|
||||
{"acute", "cedilla", "grave", "ogonek"})
|
||||
("acute", "grave", "cedilla", "ogonek"))
|
||||
|
||||
def test_glyphclass_range_cid(self):
|
||||
[gc] = self.parse(r"@GlyphClass = [\999-\1001];").statements
|
||||
self.assertEqual(gc.name, "GlyphClass")
|
||||
self.assertEqual(gc.glyphs, {"cid00999", "cid01000", "cid01001"})
|
||||
self.assertEqual(gc.glyphs, ("cid00999", "cid01000", "cid01001"))
|
||||
|
||||
def test_glyphclass_range_cid_bad(self):
|
||||
self.assertRaisesRegex(
|
||||
@ -203,24 +203,24 @@ class ParserTest(unittest.TestCase):
|
||||
def test_glyphclass_range_uppercase(self):
|
||||
[gc] = self.parse("@swashes = [X.swash-Z.swash];").statements
|
||||
self.assertEqual(gc.name, "swashes")
|
||||
self.assertEqual(gc.glyphs, {"X.swash", "Y.swash", "Z.swash"})
|
||||
self.assertEqual(gc.glyphs, ("X.swash", "Y.swash", "Z.swash"))
|
||||
|
||||
def test_glyphclass_range_lowercase(self):
|
||||
[gc] = self.parse("@defg.sc = [d.sc-g.sc];").statements
|
||||
self.assertEqual(gc.name, "defg.sc")
|
||||
self.assertEqual(gc.glyphs, {"d.sc", "e.sc", "f.sc", "g.sc"})
|
||||
self.assertEqual(gc.glyphs, ("d.sc", "e.sc", "f.sc", "g.sc"))
|
||||
|
||||
def test_glyphclass_range_digit1(self):
|
||||
[gc] = self.parse("@range = [foo.2-foo.5];").statements
|
||||
self.assertEqual(gc.glyphs, {"foo.2", "foo.3", "foo.4", "foo.5"})
|
||||
self.assertEqual(gc.glyphs, ("foo.2", "foo.3", "foo.4", "foo.5"))
|
||||
|
||||
def test_glyphclass_range_digit2(self):
|
||||
[gc] = self.parse("@range = [foo.09-foo.11];").statements
|
||||
self.assertEqual(gc.glyphs, {"foo.09", "foo.10", "foo.11"})
|
||||
self.assertEqual(gc.glyphs, ("foo.09", "foo.10", "foo.11"))
|
||||
|
||||
def test_glyphclass_range_digit3(self):
|
||||
[gc] = self.parse("@range = [foo.123-foo.125];").statements
|
||||
self.assertEqual(gc.glyphs, {"foo.123", "foo.124", "foo.125"})
|
||||
self.assertEqual(gc.glyphs, ("foo.123", "foo.124", "foo.125"))
|
||||
|
||||
def test_glyphclass_range_bad(self):
|
||||
self.assertRaisesRegex(
|
||||
@ -239,17 +239,17 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
def test_glyphclass_range_mixed(self):
|
||||
[gc] = self.parse("@range = [a foo.09-foo.11 X.sc-Z.sc];").statements
|
||||
self.assertEqual(gc.glyphs, {
|
||||
self.assertEqual(gc.glyphs, (
|
||||
"a", "foo.09", "foo.10", "foo.11", "X.sc", "Y.sc", "Z.sc"
|
||||
})
|
||||
))
|
||||
|
||||
def test_glyphclass_reference(self):
|
||||
[vowels_lc, vowels_uc, vowels] = self.parse(
|
||||
"@Vowels.lc = [a e i o u]; @Vowels.uc = [A E I O U];"
|
||||
"@Vowels = [@Vowels.lc @Vowels.uc y Y];").statements
|
||||
self.assertEqual(vowels_lc.glyphs, set(list("aeiou")))
|
||||
self.assertEqual(vowels_uc.glyphs, set(list("AEIOU")))
|
||||
self.assertEqual(vowels.glyphs, set(list("aeiouyAEIOUY")))
|
||||
self.assertEqual(vowels_lc.glyphs, tuple("aeiou"))
|
||||
self.assertEqual(vowels_uc.glyphs, tuple("AEIOU"))
|
||||
self.assertEqual(vowels.glyphs, tuple("aeiouAEIOUyY"))
|
||||
self.assertRaisesRegex(
|
||||
FeatureLibError, "Unknown glyph class @unknown",
|
||||
self.parse, "@bad = [@unknown];")
|
||||
@ -260,9 +260,9 @@ class ParserTest(unittest.TestCase):
|
||||
"feature liga { @bar = [@foo l]; } liga;"
|
||||
"feature smcp { @bar = [@foo s]; } smcp;"
|
||||
).statements
|
||||
self.assertEqual(foo.glyphs, {"a", "b"})
|
||||
self.assertEqual(liga.statements[0].glyphs, {"a", "b", "l"})
|
||||
self.assertEqual(smcp.statements[0].glyphs, {"a", "b", "s"})
|
||||
self.assertEqual(foo.glyphs, ("a", "b"))
|
||||
self.assertEqual(liga.statements[0].glyphs, ("a", "b", "l"))
|
||||
self.assertEqual(smcp.statements[0].glyphs, ("a", "b", "s"))
|
||||
|
||||
def test_glyphclass_scoping_bug496(self):
|
||||
# https://github.com/behdad/fonttools/issues/496
|
||||
@ -270,7 +270,7 @@ class ParserTest(unittest.TestCase):
|
||||
"feature F1 { lookup L { @GLYPHCLASS = [A B C];} L; } F1;"
|
||||
"feature F2 { sub @GLYPHCLASS by D; } F2;"
|
||||
).statements
|
||||
self.assertEqual(set(f2.statements[0].mapping.keys()), {"A", "B", "C"})
|
||||
self.assertEqual(f2.statements[0].mapping.keys(), ["A", "B", "C"])
|
||||
|
||||
def test_GlyphClassDef(self):
|
||||
doc = self.parse("table GDEF {GlyphClassDef [b],[l],[m],[C c];} GDEF;")
|
||||
@ -508,7 +508,7 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertEqual(flag.value, 1)
|
||||
self.assertIsInstance(flag.markAttachment, ast.GlyphClassName)
|
||||
self.assertEqual(flag.markAttachment.glyphSet(),
|
||||
{"acute", "grave", "macron"})
|
||||
("acute", "grave", "macron"))
|
||||
self.assertIsNone(flag.markFilteringSet)
|
||||
|
||||
def test_lookupflag_format_A_UseMarkFilteringSet(self):
|
||||
@ -520,7 +520,7 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertIsNone(flag.markAttachment)
|
||||
self.assertIsInstance(flag.markFilteringSet, ast.GlyphClassName)
|
||||
self.assertEqual(flag.markFilteringSet.glyphSet(),
|
||||
{"cedilla", "ogonek"})
|
||||
("cedilla", "ogonek"))
|
||||
|
||||
def test_lookupflag_format_B(self):
|
||||
flag = self.parse_lookupflag_("lookupflag 7;")
|
||||
@ -671,7 +671,7 @@ class ParserTest(unittest.TestCase):
|
||||
"} kern;")
|
||||
pos = doc.statements[0].statements[0]
|
||||
self.assertEqual(type(pos), ast.CursivePosStatement)
|
||||
self.assertEqual(pos.glyphclass, {"A"})
|
||||
self.assertEqual(pos.glyphclass, ("A",))
|
||||
self.assertEqual((pos.entryAnchor.x, pos.entryAnchor.y), (12, -2))
|
||||
self.assertEqual((pos.exitAnchor.x, pos.exitAnchor.y), (2, 3))
|
||||
|
||||
@ -696,7 +696,7 @@ class ParserTest(unittest.TestCase):
|
||||
"} test;")
|
||||
pos = doc.statements[-1].statements[0]
|
||||
self.assertEqual(type(pos), ast.MarkBasePosStatement)
|
||||
self.assertEqual(pos.base, {"a", "e", "o", "u"})
|
||||
self.assertEqual(pos.base, ("a", "e", "o", "u"))
|
||||
(a1, m1), (a2, m2) = pos.marks
|
||||
self.assertEqual((a1.x, a1.y, m1.name), (250, 450, "TOP_MARKS"))
|
||||
self.assertEqual((a2.x, a2.y, m2.name), (210, -10, "BOTTOM_MARKS"))
|
||||
@ -738,7 +738,7 @@ class ParserTest(unittest.TestCase):
|
||||
"} test;")
|
||||
pos = doc.statements[-1].statements[0]
|
||||
self.assertEqual(type(pos), ast.MarkLigPosStatement)
|
||||
self.assertEqual(pos.ligatures, {"a_f_f_i", "o_f_f_i"})
|
||||
self.assertEqual(pos.ligatures, ("a_f_f_i", "o_f_f_i"))
|
||||
[(a11, m11), (a12, m12)], [(a2, m2)], [], [(a4, m4)] = pos.marks
|
||||
self.assertEqual((a11.x, a11.y, m11.name), (50, 600, "TOP_MARKS"))
|
||||
self.assertEqual((a12.x, a12.y, m12.name), (50, -10, "BOTTOM_MARKS"))
|
||||
@ -774,7 +774,7 @@ class ParserTest(unittest.TestCase):
|
||||
"} test;")
|
||||
pos = doc.statements[-1].statements[0]
|
||||
self.assertEqual(type(pos), ast.MarkMarkPosStatement)
|
||||
self.assertEqual(pos.baseMarks, {"hamza"})
|
||||
self.assertEqual(pos.baseMarks, ("hamza",))
|
||||
[(a1, m1)] = pos.marks
|
||||
self.assertEqual((a1.x, a1.y, m1.name), (221, 301, "MARK_CLASS_1"))
|
||||
|
||||
@ -826,7 +826,7 @@ class ParserTest(unittest.TestCase):
|
||||
mc = doc.statements[0]
|
||||
self.assertIsInstance(mc, ast.MarkClassDefinition)
|
||||
self.assertEqual(mc.markClass.name, "MARKS")
|
||||
self.assertEqual(mc.glyphSet(), {"acute", "grave"})
|
||||
self.assertEqual(mc.glyphSet(), ("acute", "grave"))
|
||||
self.assertEqual((mc.anchor.x, mc.anchor.y), (350, 3))
|
||||
|
||||
def test_rsub_format_a(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user