Skip to content

Commit

Permalink
Merge pull request #821 from googlefonts/font-setter
Browse files Browse the repository at this point in the history
add fontsetter
  • Loading branch information
m4rc1e authored Feb 5, 2024
2 parents 0839560 + 84c1ab1 commit 150d7f1
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
104 changes: 104 additions & 0 deletions Lib/gftools/fontsetter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
from fontTools.misc.cliTools import makeOutputFileName
from fontTools.ttLib import TTFont
import types
import yaml
import argparse


def loads(string):
"""
name->setName: ["Hello world", 0, 3, 1, 0x409]
OS/2->sTypoAscender: 1200
-->
{
("name", "setName): ["Hello world", 0, 3, 1, 0x409],
("OS/2", "sTypoAscender"): 1200,
}
"""
config = yaml.safe_load(string)
res = {}
for k, v in config.items():
path = k.split("->")
res[tuple(path)] = v
return res


def load_config(fp):
with open(fp, encoding="utf-8") as doc:
return loads(doc.read())


def hasmethod(obj, name):
return hasattr(obj, name) and type(getattr(obj, name)) == types.MethodType


def set_all(obj, config):
for path, value in config.items():
setter(obj, path, value)


def getter(obj, path):
if len(path) == 0:
return obj
key = path[0]
if hasmethod(obj, key):
import pdb
pdb.set_trace()
return getattr(obj, key)(*path[1])
if isinstance(key, str) and hasattr(obj, key):
return getter(getattr(obj, key), path[1:])
if isinstance(obj, (list, dict, tuple, TTFont)):
return getter(obj[key], path[1:])
return obj


def setter(obj, path, val):
if len(path) == 0:
return
key = path[0]

if len(path) == 1:
if isinstance(key, str) and hasmethod(obj, key):
getattr(obj, key)(*val)
elif isinstance(key, str) and hasattr(obj, key):
setattr(obj, key, val)
else:
obj[path[0]] = val
return

if isinstance(key, str) and hasattr(obj, key):
setter(getattr(obj, key), path[1:], val)
elif isinstance(obj, (list, dict, tuple, TTFont)):
is_tuple = False
# convert numeric keys if needed
try:
obj[key]
except:
key = str(key)
obj[key]
# convert tuples to lists and then reconvert back using backtracking
if isinstance(obj[key], tuple):
is_tuple = True
obj[key] = list(obj[key])
setter(obj[key], path[1:], val)
if is_tuple:
obj[key] = tuple(obj[key])


def main(args=None):
parser = argparse.ArgumentParser()
parser.add_argument("font", type=TTFont)
parser.add_argument("config")
parser.add_argument("-o", "--out", default=None)
args = parser.parse_args(args)

config = load_config(args.config)
set_all(args.font, config)

if not args.out:
args.out = makeOutputFileName(args.font.reader.file.name)
args.font.save(args.out)


if __name__ == "__main__":
main()
57 changes: 57 additions & 0 deletions tests/test_fontsetter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from gftools.fontsetter import setter, getter
import pytest
from fontTools.ttLib import TTFont
import os


TEST_FONT = TTFont(os.path.join("data", "test", "Inconsolata[wdth,wght].ttf"))


@pytest.mark.parametrize(
"""obj,path,val,res""",
[
(
[10], [0], 100, [100]
),
(
[10, [10]], [1, 0], 100, [10, [100]]
),
(
[(0, [1, (2,)]), [1, 0], 100, (0, [100, (2,)])]
),
(
[[0, (0, (0, ))], [1, 1, 0], 100, [0, (0, (100, ))]]
),
(
{"A": {"B": [0, [10]]}}, ["A", "B", 1], [100], {"A": {"B": [0, [100]]}}
)
]
)
def test_setter(obj, path, val, res):
setter(obj, path, val)
assert obj == res


@pytest.mark.parametrize(
"""obj,path,val""",
[
# simple atttribs
(
TEST_FONT, ["OS/2", "fsSelection"], 64
),
(
TEST_FONT, ["hhea", "ascender"], 1000
),
# attrib then dict
(
TEST_FONT, ["hmtx", "metrics", "A"], (10, 10)
),
# attrib then attrib
(
TEST_FONT, ["OS/2", "panose", "bSerifStyle"], 10
)
]
)
def test_setter_on_fonts(obj, path, val):
setter(obj, path, val)
assert getter(obj, path) == val

0 comments on commit 150d7f1

Please sign in to comment.