[feaLib]Escape nameid strings when writing feature files
https://github.com/fonttools/fonttools/issues/780
This commit is contained in:
parent
7cf22d01ae
commit
b22df7ff48
@ -1,6 +1,8 @@
|
|||||||
from __future__ import print_function, division, absolute_import
|
from __future__ import print_function, division, absolute_import
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
from fontTools.misc.py23 import *
|
||||||
from fontTools.feaLib.error import FeatureLibError
|
from fontTools.feaLib.error import FeatureLibError
|
||||||
|
from fontTools.misc.encodingTools import getEncoding
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
@ -1059,10 +1061,27 @@ class NameRecord(Statement):
|
|||||||
self.platEncID, self.langID, self.string)
|
self.platEncID, self.langID, self.string)
|
||||||
|
|
||||||
def asFea(self, indent=""):
|
def asFea(self, indent=""):
|
||||||
plat = simplify_name_attributes(self.platformID, self.platEncID, self.langID)
|
def escape(c, escape_pattern):
|
||||||
|
# Also escape U+0022 QUOTATION MARK and U+005C REVERSE SOLIDUS
|
||||||
|
if c >= 0x20 and c <= 0x7E and c not in (0x22, 0x5C):
|
||||||
|
return unichr(c)
|
||||||
|
else:
|
||||||
|
return escape_pattern % c
|
||||||
|
encoding = getEncoding(self.platformID, self.platEncID, self.langID)
|
||||||
|
if encoding is None:
|
||||||
|
raise FeatureLibError("Unsupported encoding", self.location)
|
||||||
|
s = tobytes(self.string, encoding=encoding)
|
||||||
|
if encoding == "utf_16_be":
|
||||||
|
escaped_string = "".join([
|
||||||
|
escape(byteord(s[i]) * 256 + byteord(s[i + 1]), r"\%04x")
|
||||||
|
for i in range(0, len(s), 2)])
|
||||||
|
else:
|
||||||
|
escaped_string = "".join([escape(byteord(b), r"\%02x") for b in s])
|
||||||
|
plat = simplify_name_attributes(
|
||||||
|
self.platformID, self.platEncID, self.langID)
|
||||||
if plat != "":
|
if plat != "":
|
||||||
plat += " "
|
plat += " "
|
||||||
return "nameid {} {}\"{}\";".format(self.nameID, plat, self.string)
|
return "nameid {} {}\"{}\";".format(self.nameID, plat, escaped_string)
|
||||||
|
|
||||||
|
|
||||||
class FeatureNameStatement(NameRecord):
|
class FeatureNameStatement(NameRecord):
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
table name {
|
table name {
|
||||||
#test-fea2fea: nameid 9 "Joachim Müller-Lancé";
|
|
||||||
nameid 9 "Joachim M\00fcller-Lanc\00e9"; # Windows (Unicode)
|
nameid 9 "Joachim M\00fcller-Lanc\00e9"; # Windows (Unicode)
|
||||||
#test-fea2fea: nameid 9 1 "Joachim Müller-Lancé";
|
|
||||||
nameid 9 1 "Joachim M\9fller-Lanc\8e"; # Macintosh (Mac Roman)
|
nameid 9 1 "Joachim M\9fller-Lanc\8e"; # Macintosh (Mac Roman)
|
||||||
} name;
|
} name;
|
||||||
|
@ -87,7 +87,15 @@ class LexerTest(unittest.TestCase):
|
|||||||
[(Lexer.STRING, "foo"), (Lexer.STRING, "bar")])
|
[(Lexer.STRING, "foo"), (Lexer.STRING, "bar")])
|
||||||
self.assertEqual(lex('"foo \nbar\r baz \r\nqux\n\n "'),
|
self.assertEqual(lex('"foo \nbar\r baz \r\nqux\n\n "'),
|
||||||
[(Lexer.STRING, "foo bar baz qux ")])
|
[(Lexer.STRING, "foo bar baz qux ")])
|
||||||
self.assertRaises(FeatureLibError, lambda: lex('"foo\n bar'))
|
# The lexer should preserve escape sequences because they have
|
||||||
|
# different interpretations depending on context. For better
|
||||||
|
# or for worse, that is how the OpenType Feature File Syntax
|
||||||
|
# has been specified; see section 9.e (name table) for examples.
|
||||||
|
self.assertEqual(lex(r'"M\00fcller-Lanc\00e9"'), # 'nameid 9'
|
||||||
|
[(Lexer.STRING, r"M\00fcller-Lanc\00e9")])
|
||||||
|
self.assertEqual(lex(r'"M\9fller-Lanc\8e"'), # 'nameid 9 1'
|
||||||
|
[(Lexer.STRING, r"M\9fller-Lanc\8e")])
|
||||||
|
self.assertRaises(FeatureLibError, lex, '"foo\n bar')
|
||||||
|
|
||||||
def test_bad_character(self):
|
def test_bad_character(self):
|
||||||
self.assertRaises(FeatureLibError, lambda: lex("123 \u0001"))
|
self.assertRaises(FeatureLibError, lambda: lex("123 \u0001"))
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import print_function, division, absolute_import
|
from __future__ import print_function, division, absolute_import
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from fontTools.feaLib.error import FeatureLibError
|
from fontTools.feaLib.error import FeatureLibError
|
||||||
@ -901,6 +902,69 @@ class ParserTest(unittest.TestCase):
|
|||||||
self.assertEqual(mc.glyphSet(), ("acute", "grave"))
|
self.assertEqual(mc.glyphSet(), ("acute", "grave"))
|
||||||
self.assertEqual((mc.anchor.x, mc.anchor.y), (350, 3))
|
self.assertEqual((mc.anchor.x, mc.anchor.y), (350, 3))
|
||||||
|
|
||||||
|
def test_nameid_windows_utf16(self):
|
||||||
|
doc = self.parse(
|
||||||
|
r'table name { nameid 9 "M\00fcller-Lanc\00e9"; } name;')
|
||||||
|
name = doc.statements[0].statements[0]
|
||||||
|
self.assertIsInstance(name, ast.NameRecord)
|
||||||
|
self.assertEquals(name.nameID, 9)
|
||||||
|
self.assertEquals(name.platformID, 3)
|
||||||
|
self.assertEquals(name.platEncID, 1)
|
||||||
|
self.assertEquals(name.langID, 0x0409)
|
||||||
|
self.assertEquals(name.string, "Müller-Lancé")
|
||||||
|
self.assertEquals(name.asFea(), r'nameid 9 "M\00fcller-Lanc\00e9";')
|
||||||
|
|
||||||
|
def test_nameid_windows_utf16_backslash(self):
|
||||||
|
doc = self.parse(r'table name { nameid 9 "Back\005cslash"; } name;')
|
||||||
|
name = doc.statements[0].statements[0]
|
||||||
|
self.assertEquals(name.string, r"Back\slash")
|
||||||
|
self.assertEquals(name.asFea(), r'nameid 9 "Back\005cslash";')
|
||||||
|
|
||||||
|
def test_nameid_windows_utf16_quotation_mark(self):
|
||||||
|
doc = self.parse(
|
||||||
|
r'table name { nameid 9 "Quotation \0022Mark\0022"; } name;')
|
||||||
|
name = doc.statements[0].statements[0]
|
||||||
|
self.assertEquals(name.string, 'Quotation "Mark"')
|
||||||
|
self.assertEquals(name.asFea(), r'nameid 9 "Quotation \0022Mark\0022";')
|
||||||
|
|
||||||
|
def test_nameid_windows_utf16_surroates(self):
|
||||||
|
pass
|
||||||
|
# TODO: https://github.com/fonttools/fonttools/issues/842
|
||||||
|
# doc = self.parse(r'table name { nameid 9 "Carrot \D83E\DD55"; } name;')
|
||||||
|
# name = doc.statements[0].statements[0]
|
||||||
|
# self.assertEquals(name.string, r"Carrot 🥕")
|
||||||
|
# self.assertEquals(name.asFea(), r'nameid 9 "Carrot \d83e\dd55";')
|
||||||
|
|
||||||
|
def test_nameid_mac_roman(self):
|
||||||
|
doc = self.parse(
|
||||||
|
r'table name { nameid 9 1 "Joachim M\9fller-Lanc\8e"; } name;')
|
||||||
|
name = doc.statements[0].statements[0]
|
||||||
|
self.assertIsInstance(name, ast.NameRecord)
|
||||||
|
self.assertEquals(name.nameID, 9)
|
||||||
|
self.assertEquals(name.platformID, 1)
|
||||||
|
self.assertEquals(name.platEncID, 0)
|
||||||
|
self.assertEquals(name.langID, 0)
|
||||||
|
self.assertEquals(name.string, "Joachim Müller-Lancé")
|
||||||
|
self.assertEquals(name.asFea(),
|
||||||
|
r'nameid 9 1 "Joachim M\9fller-Lanc\8e";')
|
||||||
|
|
||||||
|
def test_nameid_mac_croatian(self):
|
||||||
|
doc = self.parse(
|
||||||
|
r'table name { nameid 9 1 0 18 "Jovica Veljovi\e6"; } name;')
|
||||||
|
name = doc.statements[0].statements[0]
|
||||||
|
self.assertEquals(name.nameID, 9)
|
||||||
|
self.assertEquals(name.platformID, 1)
|
||||||
|
self.assertEquals(name.platEncID, 0)
|
||||||
|
self.assertEquals(name.langID, 18)
|
||||||
|
# TODO: https://github.com/fonttools/fonttools/issues/842
|
||||||
|
# self.assertEquals(name.string, "Jovica Veljović")
|
||||||
|
# self.assertEquals(name.asFea(), r'nameid 9 1 0 18 "Jovica Veljovi\e6";')
|
||||||
|
|
||||||
|
def test_nameid_unsupported_platform(self):
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
FeatureLibError, "Expected platform id 1 or 3",
|
||||||
|
self.parse, 'table name { nameid 9 666 "Foo"; } name;')
|
||||||
|
|
||||||
def test_rsub_format_a(self):
|
def test_rsub_format_a(self):
|
||||||
doc = self.parse("feature test {rsub a [b B] c' d [e E] by C;} test;")
|
doc = self.parse("feature test {rsub a [b B] c' d [e E] by C;} test;")
|
||||||
rsub = doc.statements[0].statements[0]
|
rsub = doc.statements[0].statements[0]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user