Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add fontsetter #821

Merged
merged 2 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading