Skip to content

Commit

Permalink
Merge pull request #443 from googlefonts/otsvg-to-colrv0
Browse files Browse the repository at this point in the history
add --colr_version flag to maximum_color
  • Loading branch information
anthrotype authored Nov 29, 2022
2 parents 1e35c05 + 77ba8d6 commit 5e6281b
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 13 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ jobs:
- name: Set up Python 3.9
uses: actions/setup-python@v2
with:
# typed_ast (used by pytype) has a problem when run under 3.9.8, pin to 3.9.7
# until resolved: https://github.com/python/typed_ast/issues/169
python-version: "3.9.7"
python-version: "3.9"
- name: Install tox
run: pip install tox
- name: Run style and typing checks
Expand Down
18 changes: 15 additions & 3 deletions src/nanoemoji/glue_together.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,21 @@ class CbdtGlyphInfo(NamedTuple):

def _copy_colr(target: ttLib.TTFont, donor: ttLib.TTFont):
# Copy all glyphs used by COLR over
glyphs_to_copy = sorted(
{p.Glyph for p in paints_of_type(donor, ot.PaintFormat.PaintGlyph)}
)
colr_version = donor["COLR"].version
if colr_version == 1:
glyphs_to_copy = sorted(
{p.Glyph for p in paints_of_type(donor, ot.PaintFormat.PaintGlyph)}
)
elif colr_version == 0:
glyphs_to_copy = sorted(
{
rec.name
for layers in donor["COLR"].ColorLayers.values()
for rec in layers
}
)
else:
raise NotImplementedError(colr_version)

# We avoid using the glyf table's `__setitem__` for it appends to the TTFont's
# glyphOrder list without also invalidating the {glyphNames:glyphID} cache,
Expand Down
45 changes: 38 additions & 7 deletions src/nanoemoji/maximum_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
)
from nanoemoji.util import only
from pathlib import Path
from typing import List, NamedTuple, Tuple
from typing import List, NamedTuple, Optional, Tuple


FLAGS = flags.FLAGS
Expand All @@ -59,6 +59,13 @@
False,
"If true, generate a bitmap table (specificaly CBDT)",
)
flags.DEFINE_integer(
"colr_version",
1,
"COLR table version to generate",
lower_bound=0,
upper_bound=1,
)


# attribute names need to match inputs to write_font rule
Expand All @@ -69,26 +76,43 @@ class WriteFontInputs(NamedTuple):

@property
def table_tag(self) -> str:
return f"{Path(self.glyphmap_file).stem:4}"
basename = Path(self.glyphmap_file).stem
table_tag, _, _ = basename.partition("_")
return f"{table_tag:4}"

@property
def table_version(self) -> Optional[int]:
basename = Path(self.glyphmap_file).stem
_, _, version = basename.partition("_")
return int(version) if version else None

@property
def color_format(self) -> str:
identifier = self.table_tag.strip().lower()
table_version = self.table_version

if identifier == "svg":
# for good woff2 performance, at cost of inflated size
return "picosvg"
elif identifier == "colr":
# optimize for woff2 performance
return "glyf_colr_1"
if table_version not in (0, 1):
raise ValueError(
f"Invalid COLR version, expected 0 or 1, got {table_version}"
)
return f"glyf_colr_{table_version}"
elif identifier == "cbdt":
return "cbdt"
else:
raise ValueError(f"What is {identifier}?!")

@classmethod
def for_tag(cls, table_tag: str) -> "WriteFontInputs":
def for_tag(
cls, table_tag: str, table_version: Optional[int] = None
) -> "WriteFontInputs":
basename = table_tag.strip()
if table_version is not None:
basename += f"_{table_version}"
return cls(
Path(basename + ".glyphmap"),
Path(basename + ".toml"),
Expand Down Expand Up @@ -296,8 +320,9 @@ def _generate_additional_color_table(
glyphmap_inputs: List[Path],
table_tag: str,
glue_target: Path,
table_version: Optional[int] = None,
) -> Path:
write_font_inputs = WriteFontInputs.for_tag(table_tag)
write_font_inputs = WriteFontInputs.for_tag(table_tag, table_version)
identifier = write_font_inputs.color_format
del table_tag

Expand Down Expand Up @@ -363,6 +388,7 @@ def _generate_colr_from_svg(
font_config: config.FontConfig,
input_font: Path,
font: ttLib.TTFont,
colr_version: int,
) -> Tuple[Path, List[Path]]:
# extract the svgs
svg_files = [
Expand All @@ -376,7 +402,12 @@ def _generate_colr_from_svg(
part_file = _part_file(nw, font_config, picosvgs)

output_file = _generate_additional_color_table(
nw, input_font, picosvgs + [input_font], "COLR", input_font
nw,
input_font,
picosvgs + [input_font],
"COLR",
input_font,
table_version=colr_version,
)
return output_file, picosvgs

Expand Down Expand Up @@ -460,7 +491,7 @@ def _run(argv):
)
else:
wip_file, picosvg_files = _generate_colr_from_svg(
nw, font_config, wip_file, font
nw, font_config, wip_file, font, FLAGS.colr_version
)

if FLAGS.bitmaps:
Expand Down
18 changes: 18 additions & 0 deletions tests/maximum_color_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,24 @@ def test_build_maximum_font(color_format, expected_new_tables, bitmaps, input_fi
assert set(maximum_font.keys()) - set(initial_font.keys()) == expected_new_tables


@pytest.mark.parametrize("colr_version", [None, 0, 1])
def test_build_colrv0_from_svg(colr_version):
initial_font_file = _build_initial_font("picosvg")

additional_flags = ()
if colr_version is not None:
additional_flags = ("--colr_version", str(colr_version))

maxmium_font_file = _maximize_color(initial_font_file, additional_flags)

initial_font = ttLib.TTFont(initial_font_file)
maximum_font = ttLib.TTFont(maxmium_font_file)
assert set(maximum_font.keys()) - set(initial_font.keys()) == {"COLR", "CPAL"}

expected_version = 1 if colr_version is None else colr_version
assert maximum_font["COLR"].version == expected_version


@pytest.mark.parametrize("keep_names", [True, False])
def test_keep_glyph_names(keep_names):
initial_font_file = _build_initial_font("glyf_colr_1")
Expand Down

0 comments on commit 5e6281b

Please sign in to comment.