Skip to content

Instantly share code, notes, and snippets.

@justvanrossum
Last active January 30, 2021 14:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save justvanrossum/c1055da1041f8976a31a93ea838cc05e to your computer and use it in GitHub Desktop.
Save justvanrossum/c1055da1041f8976a31a93ea838cc05e to your computer and use it in GitHub Desktop.
Test implementation for new proposed reversible glyph name to file name scheme
# test implementation for https://github.com/unified-font-object/ufo-spec/issues/164
from urllib.parse import unquote
import string
separatorChar = "^"
# TODO: [insert references]
reservedCharacters = set(" \" % * + / : < > ? [ \\ ] | ".split())
reservedCharacters.update(chr(i) for i in range(32))
# reservedCharacters.add(" ") # should we escape space chars or not?
reservedCharacters.add(chr(0x7F))
reservedCharacters.add(separatorChar)
assert all(len(c) == 1 for c in reservedCharacters)
# TODO: [insert references]
reservedFileNames = set("""
CON
PRN
AUX
CLOCK$
NUL
COM1
LPT1
LPT2
LPT3
COM2
COM3
COM4
""".lower().split())
base32chars = string.digits + string.ascii_uppercase[:22]
assert len(set(base32chars)) == 32
def userNameToFileName(userName, prefix="", suffix=""):
codeDigits = []
for i in range(0, len(userName), 5):
digit = 0
bit = 1
for c in userName[i:i + 5]:
if c.isupper():
digit |= bit
bit <<= 1
codeDigits.append(digit)
# strip trailing zeros
while codeDigits and codeDigits[-1] == 0:
codeDigits.pop()
name = "".join(f"%{ord(c):02X}" if c in reservedCharacters else c for c in userName)
if name[0] == ".":
name = "%2E" + name[1:]
if not codeDigits and name.lower() in reservedFileNames:
codeDigits = [0]
if codeDigits:
disambiguationCode = separatorChar + "".join(base32chars[d] for d in codeDigits)
else:
disambiguationCode = ""
return prefix + name + disambiguationCode + suffix
def fileNameToUserName(fileName, prefix="", suffix="", stripSuffix=True):
if prefix:
assert fileName.startswith(prefix)
fileName = fileName[len(prefix):]
if stripSuffix or suffix:
if suffix:
assert fileName.endswith(suffix)
fileName = fileName[:-len(suffix)]
else:
fileName = fileName.rsplit(".", 1)[0]
name = fileName.split(separatorChar, 1)[0]
return unquote(name, encoding="ascii", errors="strict")
_testCases = [
("Aring", "Aring^1.glif"),
("aring", "aring.glif"),
("ABCDEGF", "ABCDEGF^V3.glif"),
("f_i", "f_i.glif"),
("F_I", "F_I^5.glif"),
(".notdef", "%2Enotdef.glif"),
(".null", "%2Enull.glif"),
("CON", "CON^7.glif"),
("con", "con^0.glif"),
("aux", "aux^0.glif"),
("con.alt", "con.alt.glif"),
("A:", "A%3A^1.glif"),
("A^321", "A%5E321^1.glif"),
("a\\", "a%5C.glif"),
("a\t", "a%09.glif"),
# ("a ", "a%20.glif"), # escape space?
("a ", "a .glif"), # or not?
("a\"", "a%22.glif"),
("aaaaaaaaaA", "aaaaaaaaaA^0G.glif"),
("AAAAAAAAAA", "AAAAAAAAAA^VV.glif"),
]
# doctest for now, should be pytest
def _test_userNameToFileName():
"""
>>> for userName, expectedFileName in _testCases:
... fileName = userNameToFileName(userName, suffix=".glif")
... assert expectedFileName == fileName, (userName, fileName, expectedFileName)
...
"""
def _test_fileNameToUserName():
"""
>>> for expectedUserName, fileName in _testCases:
... userName = fileNameToUserName(fileName)
... assert expectedUserName == userName, (fileName, userName, expectedUserName)
...
"""
def _test_unique():
"""
>>> base = "abcdefg"
>>> foldedResults = set()
>>> for i in range(2 ** len(base)):
... name = ""
... for j in range(len(base)):
... name += base[j].upper() if i & 0x01 else base[j]
... i >>= 1
... assert name.lower() == base, name
... foldedResults.add(userNameToFileName(name).lower())
...
>>> len(foldedResults) == 2 ** len(base)
True
"""
# TODO: test error cases
if __name__ == "__main__":
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment