diff --git a/colrv1_add_border_to_flags.py b/colrv1_add_border_to_flags.py deleted file mode 100644 index 8893766e5..000000000 --- a/colrv1_add_border_to_flags.py +++ /dev/null @@ -1,132 +0,0 @@ -"""Utility to add border and soft-light effects to NotoColorEmoji-COLRv1 region flags.""" -import sys -import subprocess -from fontTools import ttLib -from fontTools.ttLib.tables import otTables as ot -from fontTools.colorLib.builder import LayerListBuilder -from nototools import font_data -from flag_glyph_name import flag_code_to_glyph_name -from map_pua_emoji import get_glyph_name_from_gsub - - -# In the CBDT font, the following flags only get no border: -# https://github.com/rsheeter/warp/issues/20 -BORDERLESS_FLAGS = ( - "GF", - "MQ", - "NP", - "PM", -) - -# The two glyphs representing respectively the flag's border and the soft-light -# effect are expected to be c'mapped to the following PUA codepoint: -BORDER_CODEPOINT = 0x100000 -SOFT_LIGHT_CODEPOINT = 0x100001 - - -def read_makefile_variable(var_name): - # see `print-%` command in Makefile - value = subprocess.run( - ["make", f"print-{var_name}"], capture_output=True, check=True - ).stdout.decode("utf-8") - return value[len(var_name) + len(" = ") :].strip() - - -def flag_code_to_sequence(flag_code): - # I use the existing code to first convert from flag code to glyph name, - # and then convert names back to integer codepoints since it already - # handles both the region indicators and subdivision tags. - name = flag_code_to_glyph_name(flag_code) - assert name.startswith("u") - return tuple(int(v, 16) for v in name[1:].split("_")) - - -_builder = LayerListBuilder() - - -def _build_paint(source): - return _builder.buildPaint(source) - - -def _paint_colr_glyph(glyph_name): - return _build_paint({"Format": ot.PaintFormat.PaintColrGlyph, "Glyph": glyph_name}) - - -def _paint_composite(source, mode, backdrop): - return _build_paint( - { - "Format": ot.PaintFormat.PaintComposite, - "SourcePaint": source, - "CompositeMode": mode, - "BackdropPaint": backdrop, - } - ) - - -def main(): - try: - input_file, output_file = sys.argv[1:] - except ValueError: - print("usage: colrv1_add_border_to_flags.py INPUT_FONT OUTPUT_FONT") - return 2 - - font = ttLib.TTFont(input_file) - - if "COLR" not in font or font["COLR"].version != 1: - print("error: missing required COLRv1 table") - return 1 - - cmap = font_data.get_cmap(font) - try: - border_name = cmap[BORDER_CODEPOINT] - soft_light_name = cmap[SOFT_LIGHT_CODEPOINT] - except KeyError as e: - print(f"error: missing required PUA codepoint: {hex(e.args[0])}") - return 1 - - colr_glyphs = { - rec.BaseGlyph: rec - for rec in font["COLR"].table.BaseGlyphList.BaseGlyphPaintRecord - } - assert border_name in colr_glyphs - assert soft_light_name in colr_glyphs - - regular_flags = [] - borderless_flags = [] - for flag_code in read_makefile_variable("SELECTED_FLAGS").split(): - flag_sequence = flag_code_to_sequence(flag_code) - flag_name = get_glyph_name_from_gsub(flag_sequence, font) - if flag_name is not None: - flag = colr_glyphs[flag_name] - if flag_code in BORDERLESS_FLAGS: - borderless_flags.append(flag) - else: - regular_flags.append(flag) - - # For regular flags, first the border is multiplied onto the flag, then - # the soft-light effect is applied to the result. - for flag in regular_flags: - flag.Paint = _paint_composite( - source=_paint_colr_glyph(soft_light_name), - mode=ot.CompositeMode.SOFT_LIGHT, - backdrop=_paint_composite( - source=flag.Paint, - mode=ot.CompositeMode.MULTIPLY, - backdrop=_paint_colr_glyph(border_name), - ), - ) - # borderless flags only get the soft-light effect - for flag in borderless_flags: - flag.Paint = _paint_composite( - source=_paint_colr_glyph(soft_light_name), - mode=ot.CompositeMode.SOFT_LIGHT, - backdrop=flag.Paint, - ) - - font_data.delete_from_cmap(font, (BORDER_CODEPOINT, SOFT_LIGHT_CODEPOINT)) - - font.save(output_file) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/colrv1_add_soft_light_to_flags.py b/colrv1_add_soft_light_to_flags.py new file mode 100644 index 000000000..c23a751db --- /dev/null +++ b/colrv1_add_soft_light_to_flags.py @@ -0,0 +1,137 @@ +"""Utility to add soft-light effect to NotoColorEmoji-COLRv1 region flags.""" +import sys +import subprocess +from fontTools import ttLib +from fontTools.ttLib.tables import otTables as ot +from fontTools.ttLib.tables.C_P_A_L_ import Color +from fontTools.colorLib.builder import LayerListBuilder +from flag_glyph_name import flag_code_to_glyph_name +from map_pua_emoji import get_glyph_name_from_gsub + + +def read_makefile_variable(var_name): + # see `print-%` command in Makefile + value = subprocess.run( + ["make", f"print-{var_name}"], capture_output=True, check=True + ).stdout.decode("utf-8") + return value[len(var_name) + len(" = ") :].strip() + + +def flag_code_to_sequence(flag_code): + # I use the existing code to first convert from flag code to glyph name, + # and then convert names back to integer codepoints since it already + # handles both the region indicators and subdivision tags. + name = flag_code_to_glyph_name(flag_code) + assert name.startswith("u") + return tuple(int(v, 16) for v in name[1:].split("_")) + + +_builder = LayerListBuilder() + + +def _build_paint(source): + return _builder.buildPaint(source) + + +def _paint_composite(source, mode, backdrop): + return _build_paint( + { + "Format": ot.PaintFormat.PaintComposite, + "SourcePaint": source, + "CompositeMode": mode, + "BackdropPaint": backdrop, + } + ) + + +def _palette_index(cpal, color): + assert len(cpal.palettes) == 1 + palette = cpal.palettes[0] + try: + i = palette.index(color) + except ValueError: + i = len(palette) + palette.append(color) + cpal.numPaletteEntries += 1 + assert len(palette) == cpal.numPaletteEntries + return i + + +WHITE = Color.fromHex("#FFFFFFFF") +GRAY = Color.fromHex("#808080FF") +BLACK = Color.fromHex("#000000FF") + + +def _soft_light_gradient(cpal): + return _build_paint( + { + "Format": ot.PaintFormat.PaintLinearGradient, + "ColorLine": { + "Extend": "pad", + "ColorStop": [ + { + "StopOffset": 0.0, + "PaletteIndex": _palette_index(cpal, WHITE), + "Alpha": 0.5, + }, + { + "StopOffset": 0.5, + "PaletteIndex": _palette_index(cpal, GRAY), + "Alpha": 0.5, + }, + { + "StopOffset": 1.0, + "PaletteIndex": _palette_index(cpal, BLACK), + "Alpha": 0.5, + }, + ], + }, + "x0": 47, + "y0": 790, + "x1": 890, + "y1": -342, + "x2": -1085, + "y2": -53, + }, + ) + + +def main(): + try: + input_file, output_file = sys.argv[1:] + except ValueError: + print("usage: colrv1_add_soft_light_to_flags.py INPUT_FONT OUTPUT_FONT") + return 2 + + font = ttLib.TTFont(input_file) + + if "COLR" not in font or font["COLR"].version != 1: + print("error: missing required COLRv1 table") + return 1 + + colr_glyphs = { + rec.BaseGlyph: rec + for rec in font["COLR"].table.BaseGlyphList.BaseGlyphPaintRecord + } + cpal = font["CPAL"] + + for flag_code in read_makefile_variable("SELECTED_FLAGS").split(): + flag_sequence = flag_code_to_sequence(flag_code) + flag_name = get_glyph_name_from_gsub(flag_sequence, font) + if flag_name is not None: + flag = colr_glyphs[flag_name] + flag.Paint = _paint_composite( + source=_paint_composite( + source=_soft_light_gradient(cpal), + mode=ot.CompositeMode.SRC_IN, + backdrop=flag.Paint, + ), + mode=ot.CompositeMode.SOFT_LIGHT, + backdrop=flag.Paint, + ) + + font.save(output_file) + + +if __name__ == "__main__": + sys.exit(main())