only post-process soft-light for flags, border will be added in the input SVGs and thus in COLRv1 font

the border can be added directly to the input waved flags SVGs like a regular path, we don't need to use PaintComposite multiply operator, regular alpha blending produces the same effect since the border is a semi transparent shade of gray.

I renamed the script colrv1_add_soft_light_to_flags.py, and modified so that it doesn't require any additional glyphs to be present; an unbounded PaintLinearGradient is built on the fly and clipped using the flag as a mask (using SRC_IN compositing mode).
pull/377/head
Cosimo Lupo 2022-01-19 10:52:28 +00:00
parent 24ccbd2377
commit 13892a930c
2 changed files with 137 additions and 132 deletions

View File

@ -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())

View File

@ -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())