diff --git a/README.md b/README.md
index 6d30e679..44d182eb 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,9 @@ can be generated:
 ```shell
 # Adds COLR to a font with SVG and vice versa
 maxmium_color my_colr_font.ttf
+
+# Adds COLR to a font with SVG and vice versa, and generates a CBDT table
+maxmium_color --bitmaps my_colr_font.ttf
 ```
 
 The intended result is a font that will Just Work in any modern browser:
@@ -44,7 +47,7 @@ The intended result is a font that will Just Work in any modern browser:
 | --- | --- | --- |
 | COLR | Chrome 98+ | https://developer.chrome.com/blog/colrv1-fonts/ |
 | SVG | Firefox, Safari | |
-| CBDT | Chrome <98 | |
+| CBDT | Chrome <98 | Only generated if you pass `--bitmaps` to `maximum_color`|
 
 Note that at time of writing Chrome 98+ prefers CBDT to COLR. Also CBDT is
 huge. So ... maybe take the resulting font and subset it per-browser if at
diff --git a/src/nanoemoji/bitmap_tables.py b/src/nanoemoji/bitmap_tables.py
index 23d75a11..cf1e77ac 100644
--- a/src/nanoemoji/bitmap_tables.py
+++ b/src/nanoemoji/bitmap_tables.py
@@ -39,7 +39,6 @@
     Sequence,
     Tuple,
 )
-import statistics
 import sys
 
 
@@ -275,11 +274,27 @@ def _make_cbdt_strike(
     return strike, data
 
 
+def raise_if_too_big_for_cbdt(color_glyphs: Sequence[ColorGlyph]):
+    too_big = sorted(
+        (c for c in color_glyphs if max(c.bitmap.size) not in _UINT8_RANGE),
+        key=lambda c: c.bitmap_filename,
+    )
+    if not too_big:
+        return
+    raise ValueError(
+        "Bitmap is too big for CBDT, try lowering bitmap_resolution: "
+        + ",".join(c.bitmap_filename for c in too_big)
+    )
+
+
 def make_cbdt_table(
     config: FontConfig,
     ttfont: ttLib.TTFont,
     color_glyphs: Sequence[ColorGlyph],
 ):
+    # CBDT is a wee bit limited in pixel size
+    raise_if_too_big_for_cbdt(color_glyphs)
+
     # bitmap tables don't like it when we're out of order
     color_glyphs = sorted(color_glyphs, key=lambda c: c.glyph_id)
 
diff --git a/src/nanoemoji/colr_to_svg.py b/src/nanoemoji/colr_to_svg.py
index a2025004..f2ad12fd 100644
--- a/src/nanoemoji/colr_to_svg.py
+++ b/src/nanoemoji/colr_to_svg.py
@@ -51,6 +51,7 @@
 from fontTools.ttLib.tables import otTables
 
 
+_FOREGROUND_COLOR_INDEX = 0xFFFF
 _GRADIENT_PAINT_FORMATS = (PaintLinearGradient.format, PaintRadialGradient.format)
 _COLR_TO_SVG_TEMPLATE = r'<svg viewBox="TBD" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs/></svg>'
 
@@ -95,7 +96,12 @@ def _draw_svg_path(
 
 
 def _color(ttfont: ttLib.TTFont, palette_index, alpha=1.0) -> colors.Color:
-    cpal_color = ttfont["CPAL"].palettes[0][palette_index]
+    palette = ttfont["CPAL"].palettes[0]
+    if palette_index == _FOREGROUND_COLOR_INDEX:
+        return colors.Color.fromstring("black")  # as good a guess as any
+    if palette_index >= len(palette):
+        raise IndexError(f"{palette_index} illegal in palette of {len(palette)}")
+    cpal_color = palette[palette_index]
     return colors.Color(
         red=cpal_color.red,
         green=cpal_color.green,
@@ -296,10 +302,13 @@ def glyph_region(ttfont: ttLib.TTFont, glyph_name: str) -> Rect:
     """The area occupied by the glyph, NOT factoring in that Y flips.
 
     map_font_space_to_viewbox handles font +y goes up => svg +y goes down."""
+    width = ttfont["hmtx"][glyph_name][0]
+    if width == 0:
+        width = ttfont["glyf"][glyph_name].xMax
     return Rect(
         0,
         -ttfont["OS/2"].sTypoAscender,
-        ttfont["hmtx"][glyph_name][0],
+        width,
         ttfont["OS/2"].sTypoAscender - ttfont["OS/2"].sTypoDescender,
     )
 
@@ -309,7 +318,12 @@ def _view_box_and_transform(
 ) -> Tuple[Rect, Affine2D]:
 
     view_box = view_box_callback(glyph_name)
-    font_to_vbox = map_font_space_to_viewbox(view_box, glyph_region(ttfont, glyph_name))
+    assert view_box.w > 0, f"0-width viewBox for {glyph_name}?!"
+
+    region = glyph_region(ttfont, glyph_name)
+    assert region.w > 0, f"0-width region for {glyph_name}?!"
+
+    font_to_vbox = map_font_space_to_viewbox(view_box, region)
 
     return (view_box, font_to_vbox)
 
diff --git a/src/nanoemoji/copy.py b/src/nanoemoji/copy.py
new file mode 100644
index 00000000..f30cb113
--- /dev/null
+++ b/src/nanoemoji/copy.py
@@ -0,0 +1,32 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from absl import app
+from pathlib import Path
+import shutil
+
+
+def main(argv):
+    assert len(argv) == 3, "Expected 2 args, input font and output"
+
+    input_file = Path(argv[1])
+    assert input_file.is_file(), f"No file {input_file}"
+    output_file = Path(argv[2])
+    assert input_file.resolve() != output_file.resolve()
+
+    shutil.copyfile(input_file, output_file)
+
+
+if __name__ == "__main__":
+    app.run(main)
diff --git a/src/nanoemoji/generate_svgs_from_colr.py b/src/nanoemoji/generate_svgs_from_colr.py
index 72e425c7..ce6178f2 100644
--- a/src/nanoemoji/generate_svgs_from_colr.py
+++ b/src/nanoemoji/generate_svgs_from_colr.py
@@ -37,7 +37,9 @@
 
 def _view_box(font: ttLib.TTFont, glyph_name: str) -> Rect:
     # we want a viewbox that results in no scaling when translating from font-space
-    return glyph_region(font, glyph_name)
+    region = glyph_region(font, glyph_name)
+    assert region.w > 0, f"0-width region for {glyph_name}"
+    return region
 
 
 def main(argv):
diff --git a/src/nanoemoji/glue_together.py b/src/nanoemoji/glue_together.py
index 1f71f9b0..8c214c84 100644
--- a/src/nanoemoji/glue_together.py
+++ b/src/nanoemoji/glue_together.py
@@ -118,9 +118,12 @@ def _copy_cbdt(target: ttLib.TTFont, donor: ttLib.TTFont):
     # other than glyph names so we can just construct a new
     # order that matches that of target
     donor_order = list(cbdt_glyph_info.keys())
+    only_in_donor = set(donor_order) - set(target.getGlyphOrder())
     # confirm our core assumption that we successfully held glyph names stable
-    if not set(donor_order) <= set(target.getGlyphOrder()):
-        raise ValueError("Donor glyph names do not exist in target")
+    if only_in_donor:
+        raise ValueError(
+            f"Donor glyph names do not exist in target: {sorted(only_in_donor)}"
+        )
     new_order = sorted(donor_order, key=target.getGlyphID)
 
     # now we know the desired order, reshard into runs
@@ -154,7 +157,7 @@ def _copy_cbdt(target: ttLib.TTFont, donor: ttLib.TTFont):
         strike.bitmapSizeTable.min_gid = min_gid
         strike.bitmapSizeTable.max_gid = max_gid
 
-        max_width = max(cbdt_glyph_info[gn].data.metrics.width for gn in glyph_run)
+        max_width = max(cbdt_glyph_info[gn].data.metrics.Advance for gn in glyph_run)
         strike.bitmapSizeTable.hori.widthMax = max_width
         strike.bitmapSizeTable.vert.widthMax = max_width
 
@@ -179,11 +182,12 @@ def main(argv):
     target = load_fully(Path(FLAGS.target_font))
     donor = load_fully(Path(FLAGS.donor_font))
 
-    if FLAGS.color_table == "COLR":
+    donation = FLAGS.color_table.lower().strip()
+    if donation == "colr":
         _copy_colr(target, donor)
-    elif FLAGS.color_table == "SVG":
+    elif donation == "svg":
         _copy_svg(target, donor)
-    elif FLAGS.color_table == "CBDT":
+    elif donation == "cbdt":
         _copy_cbdt(target, donor)
     else:
         raise ValueError(f"Unsupported color table '{FLAGS.color_table}'")
diff --git a/src/nanoemoji/keep_glyph_names.py b/src/nanoemoji/keep_glyph_names.py
new file mode 100644
index 00000000..322105a1
--- /dev/null
+++ b/src/nanoemoji/keep_glyph_names.py
@@ -0,0 +1,61 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Update post to keep glyph names."""
+from absl import app
+from absl import flags
+from absl import logging
+from fontTools import ttLib
+from pathlib import Path
+
+
+FLAGS = flags.FLAGS
+
+
+flags.DEFINE_string(
+    "log_level",
+    "INFO",
+    "The threshold for what messages will be logged. One of DEBUG, INFO, WARN, "
+    "ERROR, or FATAL.",
+)
+
+
+def keep_glyph_names(font: ttLib.TTFont):
+    # ref https://github.com/googlefonts/ufo2ft/blob/ad28eea062e0dd48678309bd9ef86dfcc85fa85a/Lib/ufo2ft/postProcessor.py#L281-L285
+    if "post" not in font:
+        raise ValueError(f"No post table")
+    post = font["post"]
+    post.formatType = 2.0
+    post.extraNames = []
+    post.mapping = {}
+
+
+def main(argv):
+    logging.set_verbosity(FLAGS.log_level)
+
+    assert len(argv) == 3, "Expected 2 args, input font and output font"
+
+    input_file = Path(argv[1])
+    assert input_file.is_file(), f"No file {input_file}"
+    output_file = Path(argv[2])
+    assert input_file.resolve() != output_file.resolve()
+
+    font = ttLib.TTFont(input_file)
+    keep_glyph_names(font)
+
+    font.save(output_file)
+
+
+if __name__ == "__main__":
+    app.run(main)
diff --git a/src/nanoemoji/maximum_color.py b/src/nanoemoji/maximum_color.py
index 2342c6cb..8f1cc89a 100644
--- a/src/nanoemoji/maximum_color.py
+++ b/src/nanoemoji/maximum_color.py
@@ -29,6 +29,7 @@
 from absl import flags
 from absl import logging
 from fontTools import ttLib
+from fontTools.ttLib.ttFont import newTable
 from nanoemoji import config
 from nanoemoji.colr_to_svg import colr_glyphs
 from nanoemoji.extract_svgs import svg_glyphs
@@ -42,18 +43,9 @@
 )
 from nanoemoji.util import only
 from pathlib import Path
-from typing import List, Tuple
+from typing import List, NamedTuple, Tuple
 
 
-_SVG2COLR_GLYPHMAP = "svg2colr.glyphmap"
-_SVG2COLR_CONFIG = "svg2colr.toml"
-
-_COLR2SVG_GLYPHMAP = "colr2svg.glyphmap"
-_COLR2SVG_CONFIG = "colr2svg.toml"
-
-_CBDT_GLYPHMAP = "cbdt.glyphmap"
-_CBDT_CONFIG = "cbdt.toml"
-
 FLAGS = flags.FLAGS
 
 
@@ -62,6 +54,41 @@
     True,
     "If true feel free to obliterate any existing glyf/cff content, e.g. fallback glyphs",
 )
+flags.DEFINE_bool(
+    "bitmaps",
+    False,
+    "If true, generate a bitmap table (specificaly CBDT)",
+)
+
+
+# attribute names need to match inputs to write_font rule
+class WriteFontInputs(NamedTuple):
+    glyphmap_file: Path
+    config_file: Path
+
+    @property
+    def table_tag(self) -> str:
+        return f"{Path(self.glyphmap_file).stem:4}"
+
+    @property
+    def color_format(self) -> str:
+        identifier = self.table_tag.strip().lower()
+
+        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"
+        elif identifier == "cbdt":
+            return "cbdt"
+        else:
+            raise ValueError(f"What is {identifier}?!")
+
+    @classmethod
+    def for_tag(cls, table_tag: str) -> "WriteFontInputs":
+        basename = table_tag.strip()
+        return cls(Path(basename + ".glyphmap"), Path(basename + ".toml"))
 
 
 def _vector_color_table(font: ttLib.TTFont) -> str:
@@ -102,7 +129,7 @@ def bitmap_dest(input_svg: Path) -> Path:
     return bitmap_dir() / input_svg.with_suffix(".png").name
 
 
-def _write_preamble(nw: NinjaWriter, input_font: Path):
+def _write_preamble(nw: NinjaWriter):
     module_rule(
         nw,
         "extract_svgs_from_otsvg",
@@ -129,48 +156,14 @@ def _write_preamble(nw: NinjaWriter, input_font: Path):
     module_rule(
         nw,
         "write_config_for_mergeable",
-        "--color_format glyf_colr_1 $in $out",
-        rule_name="write_glyf_colr_1_config",
-    )
-    nw.newline()
-
-    module_rule(
-        nw,
-        "write_config_for_mergeable",
-        "--color_format picosvg $in $out",
-        rule_name="write_picosvg_config",
-    )
-    nw.newline()
-
-    module_rule(
-        nw,
-        "write_config_for_mergeable",
-        "--color_format cbdt $in $out",
-        rule_name="write_cbdt_config",
-    )
-    nw.newline()
-
-    module_rule(
-        nw,
-        "write_font",
-        f"--glyphmap_file {_SVG2COLR_GLYPHMAP} --config_file {_SVG2COLR_CONFIG} --output_file $out",
-        rule_name="write_colr_font_from_svg_dump",
-    )
-    nw.newline()
-
-    module_rule(
-        nw,
-        "write_font",
-        f"--glyphmap_file {_COLR2SVG_GLYPHMAP} --config_file {_COLR2SVG_CONFIG} --output_file $out",
-        rule_name="write_svg_font_from_generated_svgs",
+        "--color_format $color_format $in $out",
     )
     nw.newline()
 
     module_rule(
         nw,
         "write_font",
-        f"--glyphmap_file {_CBDT_GLYPHMAP} --config_file {_CBDT_CONFIG} --output_file $out",
-        rule_name="write_cbdt_font",
+        f"--glyphmap_file $glyphmap_file --config_file $config_file --output_file $out",
     )
     nw.newline()
 
@@ -191,118 +184,137 @@ def _write_preamble(nw: NinjaWriter, input_font: Path):
     module_rule(
         nw,
         "glue_together",
-        f"--color_table COLR --target_font {input_font} --donor_font $in --output_file $out",
-        rule_name="copy_colr_from_svg2colr",
+        f"--color_table $color_table --target_font $target_font --donor_font $in --output_file $out",
     )
     nw.newline()
 
     module_rule(
         nw,
-        "glue_together",
-        f"--color_table SVG --target_font {input_font} --donor_font $in --output_file $out",
-        rule_name="copy_svg_from_colr2svg",
+        "keep_glyph_names",
+        f"$in $out",
     )
     nw.newline()
 
-    # Glue CBDT onto the font that has both vector tables
     module_rule(
         nw,
-        "glue_together",
-        f"--color_table CBDT --target_font {input_font.stem}.both_vector_tables.ttf --donor_font $in --output_file $out",
-        rule_name="copy_cbdt",
+        "strip_glyph_names",
+        f"$in $out",
     )
     nw.newline()
 
+    module_rule(
+        nw,
+        "copy",
+        f"$in $out",
+    )
+    nw.newline()
 
-def _generate_svg_from_colr(
-    nw: NinjaWriter, input_font: Path, font: ttLib.TTFont
-) -> Tuple[Path, List[Path]]:
-    # generate svgs
-    svg_files = [
-        rel_build(svg_generate_dir() / f"{gid:05d}.svg") for gid in colr_glyphs(font)
-    ]
-    nw.build(svg_files, "generate_svgs_from_colr", input_font)
+
+def _write_font(nw: NinjaWriter, output_file: Path, inputs: WriteFontInputs):
+    nw.build(
+        output_file, "write_font", implicit=list(inputs), variables=inputs._asdict()
+    )
     nw.newline()
 
-    # picosvg them
+
+def _write_config_for_mergeable(
+    nw: NinjaWriter, config_file: Path, input_font: Path, color_format: str
+):
+    nw.build(
+        config_file,
+        "write_config_for_mergeable",
+        input_font,
+        variables={"color_format": color_format},
+    )
+    nw.newline()
+
+
+def _picosvgs(nw: NinjaWriter, svg_files: List[Path]) -> List[Path]:
     picosvgs = [rel_build(picosvg_dest(s)) for s in svg_files]
     for svg_file, picosvg in zip(svg_files, picosvgs):
         nw.build(picosvg, "picosvg", svg_file)
     nw.newline()
+    return picosvgs
+
+
+def _generate_additional_color_table(
+    nw: NinjaWriter,
+    input_font: Path,
+    glyphmap_inputs: List[Path],
+    table_tag: str,
+    glue_target: Path,
+) -> Path:
+    write_font_inputs = WriteFontInputs.for_tag(table_tag)
+    identifier = write_font_inputs.color_format
+    del table_tag
 
     # make a glyphmap
     nw.build(
-        _COLR2SVG_GLYPHMAP, "write_glyphmap_for_glyph_svgs", picosvgs + [input_font]
+        write_font_inputs.glyphmap_file,
+        "write_glyphmap_for_glyph_svgs",
+        glyphmap_inputs,
     )
     nw.newline()
 
-    # make a config
-    nw.build(_COLR2SVG_CONFIG, "write_picosvg_config", input_font)
-    nw.newline()
+    # picosvg because we want good woff2 outcomes
+    _write_config_for_mergeable(
+        nw, write_font_inputs.config_file, input_font, write_font_inputs.color_format
+    )
 
     # generate a new font with SVG glyphs that use the same names as the original
-    nw.build(
-        "svg_from_colr.ttf",
-        "write_svg_font_from_generated_svgs",
-        [_COLR2SVG_GLYPHMAP, _COLR2SVG_CONFIG],
-    )
-    nw.newline()
+    font_with_new_table = Path("MergeSource." + identifier + ".ttf")
+    _write_font(nw, font_with_new_table, write_font_inputs)
 
-    # stick our shiny new COLR table onto the input font
-    output_file = Path(input_font.stem + ".both_vector_tables.ttf")
+    # stick our shiny new table onto the input font
+    output_file = Path(input_font.stem + f".added_{identifier}.ttf")
     nw.build(
         output_file,
-        "copy_svg_from_colr2svg",
-        "svg_from_colr.ttf",
+        "glue_together",
+        font_with_new_table,
+        implicit=list({input_font, glue_target}),
+        variables={
+            "color_table": write_font_inputs.table_tag.strip(),
+            "target_font": glue_target,
+        },
     )
     nw.newline()
 
-    return output_file, picosvgs
+    return output_file
 
 
-def _generate_colr_from_svg(
+def _generate_svg_from_colr(
     nw: NinjaWriter, input_font: Path, font: ttLib.TTFont
 ) -> Tuple[Path, List[Path]]:
-    # extract the svgs
-    svg_extracts = [
-        rel_build(svg_extract_dir() / f"{gid:05d}.svg") for gid, _ in svg_glyphs(font)
+    # generate svgs
+    svg_files = [
+        rel_build(svg_generate_dir() / f"{gid:05d}.svg") for gid in colr_glyphs(font)
     ]
-    nw.build(svg_extracts, "extract_svgs_from_otsvg", input_font)
-    nw.newline()
-
-    # picosvg them
-    picosvgs = [rel_build(picosvg_dest(s)) for s in svg_extracts]
-    for svg_extract, picosvg in zip(svg_extracts, picosvgs):
-        nw.build(picosvg, "picosvg", svg_extract)
+    nw.build(svg_files, "generate_svgs_from_colr", input_font)
     nw.newline()
 
-    # make a glyphmap
-    nw.build(
-        _SVG2COLR_GLYPHMAP, "write_glyphmap_for_glyph_svgs", picosvgs + [input_font]
+    # create and merge an SVG table
+    picosvgs = _picosvgs(nw, svg_files)
+    output_file = _generate_additional_color_table(
+        nw, input_font, picosvgs + [input_font], "SVG ", input_font
     )
-    nw.newline()
+    return output_file, picosvgs
 
-    # make a config
-    nw.build(_SVG2COLR_CONFIG, "write_glyf_colr_1_config", input_font)
-    nw.newline()
 
-    # generate a new font with COLR glyphs that use the same names as the original
-    nw.build(
-        "colr_from_svg.ttf",
-        "write_colr_font_from_svg_dump",
-        [_SVG2COLR_GLYPHMAP, _SVG2COLR_CONFIG],
-    )
+def _generate_colr_from_svg(
+    nw: NinjaWriter, input_font: Path, font: ttLib.TTFont
+) -> Tuple[Path, List[Path]]:
+    # extract the svgs
+    svg_files = [
+        rel_build(svg_extract_dir() / f"{gid:05d}.svg") for gid, _ in svg_glyphs(font)
+    ]
+    nw.build(svg_files, "extract_svgs_from_otsvg", input_font)
     nw.newline()
 
-    # stick our shiny new COLR table onto the input font
-    output_file = Path(input_font.stem + ".both_vector_tables.ttf")
-    nw.build(
-        output_file,
-        "copy_colr_from_svg2colr",
-        "colr_from_svg.ttf",
+    # create and merge a COLR table
+    picosvgs = _picosvgs(nw, svg_files)
+    output_file = _generate_additional_color_table(
+        nw, input_font, picosvgs + [input_font], "COLR", input_font
     )
-    nw.newline()
-
     return output_file, picosvgs
 
 
@@ -319,34 +331,32 @@ def _generate_cbdt(
         nw.build(bitmap, "write_bitmap", picosvg)
     nw.newline()
 
-    # make a glyphmap
-    nw.build(
-        _CBDT_GLYPHMAP,
-        "write_glyphmap_for_glyph_svgs",
-        picosvg_files + bitmap_files + [input_font],
+    # create and merge a COLR table
+    output_file = _generate_additional_color_table(
+        nw, input_font, picosvg_files + bitmap_files + [input_font], "CBDT", color_font
     )
-    nw.newline()
+    return output_file
 
-    # make a config
-    nw.build(_CBDT_CONFIG, "write_cbdt_config", input_font)
-    nw.newline()
 
-    # generate a new font with CBDT glyphs that use the same names as the original
-    # hidden in the rule definition is that the both vector table font is an input
-    # so list it as implicit
+def _keep_glyph_names(nw: NinjaWriter, input_file: Path) -> ttLib.TTFont:
+    # The whole concept is we keep glyph name stable until the end so
+    # make sure we start with stable names. Doesn't matter what they are,
+    # just that they don't change.
+    output_file = Path(input_file.stem + ".keep_glyph_names.ttf")
     nw.build(
-        "cbdt.ttf",
-        "write_cbdt_font",
-        [_CBDT_GLYPHMAP, _CBDT_CONFIG],
-        implicit=[input_font.stem + ".both_vector_tables.ttf"],
+        output_file,
+        "keep_glyph_names",
+        input_file,
     )
     nw.newline()
+    return output_file
+
 
-    # stick our shiny new CBDT table onto the input font and declare victory
+def _strip_glyph_names(nw: NinjaWriter, input_file: Path, output_file: Path):
     nw.build(
-        input_font.name,
-        "copy_cbdt",
-        "cbdt.ttf",
+        output_file,
+        "strip_glyph_names",
+        input_file,
     )
     nw.newline()
 
@@ -358,33 +368,40 @@ def _run(argv):
     if not FLAGS.destroy_non_color_glyphs:
         raise NotImplementedError("Retention of non-color glyphs not implemented yet")
 
-    input_font = Path(argv[1])
-    assert input_font.is_file()
-    font = ttLib.TTFont(input_font)
+    input_file = Path(argv[1]).resolve()  # we need a non-relative path
+    assert input_file.is_file()
+    font = ttLib.TTFont(input_file)
+    final_output = Path(config.load().output_file)
+    assert (
+        input_file.resolve() != (build_dir() / final_output).resolve()
+    ), "In == Out is bad"
 
     build_file = build_dir() / "build.ninja"
     build_dir().mkdir(parents=True, exist_ok=True)
 
-    # TODO flag control
     color_table = _vector_color_table(font)
 
     if gen_ninja():
         logging.info(f"Generating {build_file.relative_to(build_dir())}")
-        input_font = input_font.resolve()  # we need a non-relative path
         with open(build_file, "w") as f:
             nw = NinjaWriter(f)
-            _write_preamble(nw, input_font)
+            _write_preamble(nw)
 
+            wip_file = _keep_glyph_names(nw, input_file)
+
+            # generate the missing vector table
             if color_table == "COLR":
-                color_font, picosvg_files = _generate_svg_from_colr(
-                    nw, input_font, font
-                )
+                wip_file, picosvg_files = _generate_svg_from_colr(nw, wip_file, font)
             else:
-                color_font, picosvg_files = _generate_colr_from_svg(
-                    nw, input_font, font
-                )
+                wip_file, picosvg_files = _generate_colr_from_svg(nw, wip_file, font)
+
+            if FLAGS.bitmaps:
+                wip_file = _generate_cbdt(nw, input_file, font, wip_file, picosvg_files)
 
-            _generate_cbdt(nw, input_font, font, color_font, picosvg_files)
+            if config.load().keep_glyph_names:
+                nw.build(final_output, "copy", wip_file)
+            else:
+                _strip_glyph_names(nw, wip_file, final_output)
 
     maybe_run_ninja(build_file)
 
diff --git a/src/nanoemoji/ninja.py b/src/nanoemoji/ninja.py
index 43b24e58..2bd11d01 100644
--- a/src/nanoemoji/ninja.py
+++ b/src/nanoemoji/ninja.py
@@ -35,28 +35,36 @@
 flags.DEFINE_bool("exec_ninja", True, "Whether to run ninja.")
 
 
+def _str_path(arg):
+    if isinstance(arg, Path):
+        return str(arg)
+    if isinstance(arg, MutableSequence):
+        return _str_paths(arg)
+    return arg
+
+
+def _str_paths(args):
+    # Path in particular kerplodes nw.build
+    if isinstance(args, dict):
+        return {_str_path(k): _str_path(v) for k, v in args.items()}
+    elif isinstance(args, list) or isinstance(args, tuple):
+        return [_str_path(v) for v in args]
+    else:
+        raise ValueError(f"What to do with {type(args)}")
+
+
 class NinjaWriter:
     def __init__(self, output_f):
         self._nw = ninja_syntax.Writer(output_f)
         self.comment("Generated by nanoemoji")
         self.newline()
 
-    def _str_paths(self, args):
-        # Path in particular kerplodes nw.build
-        args = list(args)
-        for i, arg in enumerate(args):
-            if isinstance(arg, Path):
-                args[i] = str(arg)
-            if isinstance(arg, MutableSequence):
-                args[i] = self._str_paths(arg)
-        return args
-
     def rule(self, *args, **kwargs):
         self._nw.rule(*args, **kwargs)
 
     def build(self, *args, **kwargs):
         self._nw.build(
-            *self._str_paths(args), **{k: self._str_paths(v) for k, v in kwargs.items()}
+            *_str_paths(args), **{k: _str_paths(v) for k, v in kwargs.items()}
         )
 
     def newline(self):
diff --git a/src/nanoemoji/strip_glyph_names.py b/src/nanoemoji/strip_glyph_names.py
new file mode 100644
index 00000000..0a9a2e27
--- /dev/null
+++ b/src/nanoemoji/strip_glyph_names.py
@@ -0,0 +1,57 @@
+# Copyright 2022 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Update post to drop glyph names."""
+from absl import app
+from absl import flags
+from absl import logging
+from fontTools import ttLib
+from pathlib import Path
+
+
+FLAGS = flags.FLAGS
+
+
+flags.DEFINE_string(
+    "log_level",
+    "INFO",
+    "The threshold for what messages will be logged. One of DEBUG, INFO, WARN, "
+    "ERROR, or FATAL.",
+)
+
+
+def main(argv):
+    logging.set_verbosity(FLAGS.log_level)
+
+    assert len(argv) == 3, "Expected 2 args, input font and output font"
+
+    input_file = Path(argv[1])
+    assert input_file.is_file(), f"No file {input_file}"
+    output_file = Path(argv[2])
+    assert input_file.resolve() != output_file.resolve()
+
+    font = ttLib.TTFont(input_file)
+
+    post = font["post"]
+    post.formatType = 3.0
+    for attr in ("extraNames", "mapping"):
+        if hasattr(post, attr):
+            delattr(post, attr)
+    post.glyphOrder = None
+
+    font.save(output_file)
+
+
+if __name__ == "__main__":
+    app.run(main)
diff --git a/tests/bitmap_tables_test.py b/tests/bitmap_tables_test.py
index acfc6977..732e658f 100644
--- a/tests/bitmap_tables_test.py
+++ b/tests/bitmap_tables_test.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 from io import BytesIO
+from nanoemoji.color_glyph import ColorGlyph
 from nanoemoji import config
 from nanoemoji.bitmap_tables import _cbdt_bitmap_data, BitmapMetrics
 from nanoemoji.png import PNG
diff --git a/tests/maximum_color_test.py b/tests/maximum_color_test.py
index 018e1628..51e42560 100644
--- a/tests/maximum_color_test.py
+++ b/tests/maximum_color_test.py
@@ -14,10 +14,14 @@
 
 # Integration tests for nanoemoji.maximum_color
 
+import copy
 from fontTools import ttLib
+from nanoemoji.keep_glyph_names import keep_glyph_names
+from pathlib import Path
 import pytest
 import sys
 from test_helper import cleanup_temp_dirs, locate_test_file, run, run_nanoemoji
+from typing import Tuple
 
 
 @pytest.fixture(scope="module", autouse=True)
@@ -30,14 +34,7 @@ def _cleanup_temporary_dirs():
     cleanup_temp_dirs()
 
 
-@pytest.mark.parametrize(
-    "color_format, expected_new_tables",
-    [
-        ("picosvg", {"COLR", "CPAL", "CBDT", "CBLC"}),
-        ("glyf_colr_1", {"SVG ", "CBDT", "CBLC"}),
-    ],
-)
-def test_build_maximum_font(color_format, expected_new_tables):
+def _build_initial_font(color_format: str) -> Path:
     tmp_dir = run_nanoemoji(
         (
             "--color_format",
@@ -49,21 +46,76 @@ def test_build_maximum_font(color_format, expected_new_tables):
     initial_font_file = tmp_dir / "Font.ttf"
     assert initial_font_file.is_file()
 
+    return initial_font_file
+
+
+def _maximize_color(initial_font_file: Path, additional_flags: Tuple[str, ...]) -> Path:
     # Moar color
+    out_dir = initial_font_file.parent / "maximum_color"
     run(
         (
             sys.executable,
             "-m",
             "nanoemoji.maximum_color",
             "--build_dir",
-            tmp_dir / "maximum_color",
-            initial_font_file,
+            out_dir,
         )
+        + additional_flags
+        + (initial_font_file,)
     )
 
-    maxmium_font_file = tmp_dir / "maximum_color" / "Font.ttf"
+    maxmium_font_file = out_dir / "Font.ttf"
     assert maxmium_font_file.is_file()
 
+    return maxmium_font_file
+
+
+@pytest.mark.parametrize(
+    "color_format, expected_new_tables",
+    [
+        ("picosvg", {"COLR", "CPAL"}),
+        ("glyf_colr_1", {"SVG "}),
+    ],
+)
+@pytest.mark.parametrize("bitmaps", [True, False])
+def test_build_maximum_font(color_format, expected_new_tables, bitmaps):
+    initial_font_file = _build_initial_font(color_format)
+
+    bitmap_flag = "--nobitmaps"
+    if bitmaps:
+        bitmap_flag = "--bitmaps"
+        expected_new_tables = copy.copy(expected_new_tables)
+        expected_new_tables.update({"CBDT", "CBLC"})
+
+    maxmium_font_file = _maximize_color(initial_font_file, (bitmap_flag,))
+
     initial_font = ttLib.TTFont(initial_font_file)
     maximum_font = ttLib.TTFont(maxmium_font_file)
-    assert set(maximum_font.keys()) == set(initial_font.keys()) | expected_new_tables
+    assert set(maximum_font.keys()) - set(initial_font.keys()) == expected_new_tables
+
+
+@pytest.mark.parametrize("keep_names", [True, False])
+def test_keep_glyph_names(keep_names):
+    initial_font_file = _build_initial_font("glyf_colr_1")
+
+    # set identifiable glyph names
+    font = ttLib.TTFont(initial_font_file)
+    keep_glyph_names(font)
+    font.setGlyphOrder(["duck_" + gn for gn in font.getGlyphOrder()])
+    font.save(initial_font_file)
+
+    keep_glyph_names_flag = "--keep_glyph_names"
+    if not keep_names:
+        keep_glyph_names_flag = "--nokeep_glyph_names"
+
+    maxmium_font_file = _maximize_color(initial_font_file, (keep_glyph_names_flag,))
+    maximum_font = ttLib.TTFont(maxmium_font_file)
+
+    if keep_names:
+        assert all(
+            gn.startswith("duck_") for gn in maximum_font.getGlyphOrder()
+        ), maximum_font.getGlyphOrder()
+    else:
+        assert all(
+            not gn.startswith("duck_") for gn in maximum_font.getGlyphOrder()
+        ), maximum_font.getGlyphOrder()