Repository: googlefonts/nanoemoji

Issue:
Some combinations of flags don't make sense
As @anthrotype noted `--color_format` and `--output_file` have begun to overlap. For example, setting `--color_format glyf --output_file my.otf` feels odd. Clean this up.

> while working on #52 I came across the fact that "glyf" as a --color_format and _glyf_ufo as function name in here don't make much sense when --output_format is .otf (i.e. CFF-flavored OpenType). The name glyf is tied to TrueType. We want to convey the idea that this is not a layered font (like COLR is), and that it is not colored as it only has black & white (or if you like, only black). How about nouns like "flat", or "black"? Or even just "simple" or "normal"..


Discussion:

Patch:
diff --git a/README.md b/README.md
index 34f2488..e41b58b 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ For example, to build a COLRv1 font with a focus on [handwriting](https://rsheet
 ```bash
 pip install -e .
 nanoemoji --helpfull
-nanoemoji --color_format colr_1 $(find ../noto-emoji/svg -name 'emoji_u270d*.svg')
+nanoemoji --color_format glyf_colr_1 $(find ../noto-emoji/svg -name 'emoji_u270d*.svg')
 ```
 
 ## How to cut a new release
diff --git a/src/nanoemoji/nanoemoji.py b/src/nanoemoji/nanoemoji.py
index e60be5a..5b33782 100644
--- a/src/nanoemoji/nanoemoji.py
+++ b/src/nanoemoji/nanoemoji.py
@@ -61,7 +61,6 @@ class ColorFontConfig(NamedTuple):
     color_format: str
     output_format: str
     keep_glyph_names: bool = False
-    cff_version: int = 2
 
 
 class InputGlyph(NamedTuple):
@@ -80,19 +79,44 @@ class InputGlyph(NamedTuple):
 class ColorGenerator(NamedTuple):
     apply_ufo: Callable[[ufoLib2.Font, Sequence[ColorGlyph]], None]
     apply_ttfont: Callable[[ufoLib2.Font, Sequence[ColorGlyph], ttLib.TTFont], None]
+    font_ext: str  # extension for font binary, .ttf or .otf
 
 
 _COLOR_FORMAT_GENERATORS = {
-    "glyf": ColorGenerator(lambda *args: _glyf_ufo(*args), lambda *_: None),
-    "colr_0": ColorGenerator(lambda *args: _colr_ufo(0, *args), lambda *_: None),
-    "colr_1": ColorGenerator(lambda *args: _colr_ufo(1, *args), lambda *_: None),
-    "svg": ColorGenerator(lambda *_: None, lambda *args: _svg_ttfont(*args, zip=False)),
-    "svgz": ColorGenerator(lambda *_: None, lambda *args: _svg_ttfont(*args, zip=True)),
+    "glyf": ColorGenerator(lambda *args: _glyf_ufo(*args), lambda *_: None, ".ttf"),
+    "glyf_colr_0": ColorGenerator(
+        lambda *args: _colr_ufo(0, *args), lambda *_: None, ".ttf"
+    ),
+    "glyf_colr_1": ColorGenerator(
+        lambda *args: _colr_ufo(1, *args), lambda *_: None, ".ttf"
+    ),
+    "cff_colr_0": ColorGenerator(
+        lambda *args: _colr_ufo(0, *args), lambda *_: None, ".otf"
+    ),
+    "cff_colr_1": ColorGenerator(
+        lambda *args: _colr_ufo(1, *args), lambda *_: None, ".otf"
+    ),
+    "cff2_colr_0": ColorGenerator(
+        lambda *args: _colr_ufo(0, *args), lambda *_: None, ".otf"
+    ),
+    "cff2_colr_1": ColorGenerator(
+        lambda *args: _colr_ufo(1, *args), lambda *_: None, ".otf"
+    ),
+    "svg": ColorGenerator(
+        lambda *_: None, lambda *args: _svg_ttfont(*args, zip=False), ".ttf"
+    ),
+    "svgz": ColorGenerator(
+        lambda *_: None, lambda *args: _svg_ttfont(*args, zip=True), ".ttf"
+    ),
     "cbdt": ColorGenerator(
-        lambda *args: _not_impl("ufo", *args), lambda *args: _not_impl("TTFont", *args)
+        lambda *args: _not_impl("ufo", *args),
+        lambda *args: _not_impl("TTFont", *args),
+        ".ttf",
     ),
     "sbix": ColorGenerator(
-        lambda *args: _not_impl("ufo", *args), lambda *args: _not_impl("TTFont", *args)
+        lambda *args: _not_impl("ufo", *args),
+        lambda *args: _not_impl("TTFont", *args),
+        ".ttf",
     ),
 }
 
@@ -104,27 +128,24 @@ FLAGS = flags.FLAGS
 # TODO flag on/off shape reuse
 flags.DEFINE_integer("upem", 1024, "Units per em.")
 flags.DEFINE_string("family", "An Emoji Family", "Family name.")
+flags.DEFINE_string("output_dir", "/tmp", "Where to put the output.")
+flags.DEFINE_string(
+    "output_file",
+    None,
+    "Output filename. By default derived from --output_dir and --family",
+)
 flags.DEFINE_enum(
     "color_format",
-    "colr_0",
+    "glyf_colr_0",
     sorted(_COLOR_FORMAT_GENERATORS.keys()),
-    "Type of color font to generate.",
+    "Type of font to generate.",
 )
-flags.DEFINE_string(
-    "output_file",
-    "/tmp/AnEmojiFamily-Regular.ttf",
-    "Dest file, can be .ttf, .otf, or .ufo",
+flags.DEFINE_enum(
+    "output", "font", ["ufo", "font"], "Whether to output a font binary or a UFO"
 )
 flags.DEFINE_bool(
     "keep_glyph_names", False, "Whether or not to store glyph names in the font."
 )
-flags.DEFINE_integer(
-    "cff_version",
-    2,
-    "The CFF table format version. Choose 1 or 2 (default: 2)",
-    lower_bound=1,
-    upper_bound=2,
-)
 
 
 def _codepoints_from_filename(filename):
@@ -183,8 +204,11 @@ def _make_ttfont(config, ufo, color_glyphs):
     if config.output_format == ".ttf":
         ttfont = ufo2ft.compileTTF(ufo, overlapsBackend="pathops")
     if config.output_format == ".otf":
+        cff_version = 1
+        if config.color_format.startswith("cff2_"):
+            cff_version = 2
         ttfont = ufo2ft.compileOTF(
-            ufo, cffVersion=config.cff_version, overlapsBackend="pathops"
+            ufo, cffVersion=cff_version, overlapsBackend="pathops"
         )
 
     if not ttfont:
@@ -462,13 +486,16 @@ def _generate_color_font(config: ColorFontConfig, inputs: Iterable[InputGlyph]):
 
 
 def _run(argv):
+    if FLAGS.output == "ufo":
+        output_format = "ufo"
+    else:
+        output_format = _COLOR_FORMAT_GENERATORS[FLAGS.color_format].font_ext
     config = ColorFontConfig(
         upem=FLAGS.upem,
         family=FLAGS.family,
         color_format=FLAGS.color_format,
-        output_format=os.path.splitext(FLAGS.output_file)[1],
+        output_format=output_format,
         keep_glyph_names=FLAGS.keep_glyph_names,
-        cff_version=FLAGS.cff_version,
     )
 
     inputs = list(_inputs(argv[1:]))
@@ -478,8 +505,12 @@ def _run(argv):
 
     ufo, ttfont = _generate_color_font(config, inputs)
 
-    _write(ufo, ttfont, FLAGS.output_file)
-    logging.info("Wrote %s" % FLAGS.output_file)
+    output_file = FLAGS.output_file
+    if output_file is None:
+        output_file = f"{FLAGS.family.replace(' ', '')}{output_format}"
+    output_file = os.path.join(FLAGS.output_dir, output_file)
+    _write(ufo, ttfont, output_file)
+    logging.info("Wrote %s" % output_file)
 
 
 def main():


Test patch:
diff --git a/tests/make_emoji_font_test.py b/tests/make_emoji_font_test.py
index f0fe893..a7e2fb0 100644
--- a/tests/make_emoji_font_test.py
+++ b/tests/make_emoji_font_test.py
@@ -55,27 +55,32 @@ def test_codepoints_from_filename(filename, codepoints):
         # verify glyf removes component if there is only one shape
         (("one_rect.svg",), "one_rect_glyf.ttx", "glyf", ".ttf"),
         # simple fill on rect
-        (("rect.svg",), "rect_colr_0.ttx", "colr_0", ".ttf"),
-        (("rect.svg",), "rect_colr_1.ttx", "colr_1", ".ttf"),
+        (("rect.svg",), "rect_colr_0.ttx", "glyf_colr_0", ".ttf"),
+        (("rect.svg",), "rect_colr_1.ttx", "glyf_colr_1", ".ttf"),
         (("rect.svg",), "rect_svg.ttx", "svg", ".ttf"),
         # linear gradient on rect
         (
             ("linear_gradient_rect.svg",),
             "linear_gradient_rect_colr_1.ttx",
-            "colr_1",
+            "glyf_colr_1",
             ".ttf",
         ),
         # radial gradient on rect
         (
             ("radial_gradient_rect.svg",),
             "radial_gradient_rect_colr_1.ttx",
-            "colr_1",
+            "glyf_colr_1",
             ".ttf",
         ),
         # reuse shape in different color
-        (("rect.svg", "rect2.svg"), "rects_colr_1.ttx", "colr_1", ".ttf"),
+        (("rect.svg", "rect2.svg"), "rects_colr_1.ttx", "glyf_colr_1", ".ttf"),
         # clocks have composites, reuse of composite, and reuse of shape w/diff color
-        (("one-o-clock.svg", "two-o-clock.svg"), "clocks_colr_1.ttx", "colr_1", ".ttf"),
+        (
+            ("one-o-clock.svg", "two-o-clock.svg"),
+            "clocks_colr_1.ttx",
+            "glyf_colr_1",
+            ".ttf",
+        ),
         (("one-o-clock.svg", "two-o-clock.svg"), "clocks_glyf.ttx", "glyf", ".ttf"),
         (("one-o-clock.svg", "two-o-clock.svg"), "clocks_svg.ttx", "svg", ".ttf"),
         # clocks share shapes, rects share shapes. Should be two distinct svgs in font.
@@ -103,7 +108,7 @@ def test_make_emoji_font(svgs, expected_ttx, color_format, output_format):
 @pytest.mark.parametrize(
     "svgs", [("rect.svg", "rect2.svg"), ("one-o-clock.svg", "two-o-clock.svg")]
 )
-@pytest.mark.parametrize("color_format", ["colr_0", "colr_1", "svg", "svgz"])
+@pytest.mark.parametrize("color_format", ["glyf_colr_0", "glyf_colr_1", "svg", "svgz"])
 @pytest.mark.parametrize("keep_glyph_names", [True, False])
 def test_keep_glyph_names(svgs, color_format, keep_glyph_names):
     config, glyph_inputs = test_helper.color_font_config(
diff --git a/tests/svg_colr_svg_test.py b/tests/svg_colr_svg_test.py
index 182e46a..4dd1ab7 100644
--- a/tests/svg_colr_svg_test.py
+++ b/tests/svg_colr_svg_test.py
@@ -24,8 +24,8 @@ from colr_to_svg import colr_to_svg
     "svg_in, expected_svg_out, color_format, output_format",
     [
         # simple fill on rect
-        ("rect.svg", "rect_from_colr_0.svg", "colr_0", ".ttf"),
-        ("rect.svg", "rect_from_colr_1.svg", "colr_1", ".ttf"),
+        ("rect.svg", "rect_from_colr_0.svg", "glyf_colr_0", ".ttf"),
+        ("rect.svg", "rect_from_colr_1.svg", "glyf_colr_1", ".ttf"),
         # linear gradient on rect
         # ("linear_gradient_rect.svg", "linear_gradient_rect.ttx", "colr_1", ".ttf"),
         # radial gradient on rect