Merge pull request #378 from googlefonts/colrv1-handle-unknown-flags
Colrv1 handle unknown flags and don't include region indicators in noflags buildfeblend-soft-light-flags
commit
e667644ec5
|
@ -54,32 +54,6 @@ srcs = [
|
|||
"../svg/emoji_u1f198.svg",
|
||||
"../svg/emoji_u1f199.svg",
|
||||
"../svg/emoji_u1f19a.svg",
|
||||
"../svg/emoji_u1f1e6.svg",
|
||||
"../svg/emoji_u1f1e7.svg",
|
||||
"../svg/emoji_u1f1e8.svg",
|
||||
"../svg/emoji_u1f1e9.svg",
|
||||
"../svg/emoji_u1f1ea.svg",
|
||||
"../svg/emoji_u1f1eb.svg",
|
||||
"../svg/emoji_u1f1ec.svg",
|
||||
"../svg/emoji_u1f1ed.svg",
|
||||
"../svg/emoji_u1f1ee.svg",
|
||||
"../svg/emoji_u1f1ef.svg",
|
||||
"../svg/emoji_u1f1f0.svg",
|
||||
"../svg/emoji_u1f1f1.svg",
|
||||
"../svg/emoji_u1f1f2.svg",
|
||||
"../svg/emoji_u1f1f3.svg",
|
||||
"../svg/emoji_u1f1f4.svg",
|
||||
"../svg/emoji_u1f1f5.svg",
|
||||
"../svg/emoji_u1f1f6.svg",
|
||||
"../svg/emoji_u1f1f7.svg",
|
||||
"../svg/emoji_u1f1f8.svg",
|
||||
"../svg/emoji_u1f1f9.svg",
|
||||
"../svg/emoji_u1f1fa.svg",
|
||||
"../svg/emoji_u1f1fb.svg",
|
||||
"../svg/emoji_u1f1fc.svg",
|
||||
"../svg/emoji_u1f1fd.svg",
|
||||
"../svg/emoji_u1f1fe.svg",
|
||||
"../svg/emoji_u1f1ff.svg",
|
||||
"../svg/emoji_u1f201.svg",
|
||||
"../svg/emoji_u1f202.svg",
|
||||
"../svg/emoji_u1f21a.svg",
|
||||
|
|
|
@ -5,11 +5,13 @@ For now substantially based on copying from a correct bitmap build.
|
|||
"""
|
||||
from absl import app
|
||||
import functools
|
||||
from fontTools.feaLib.builder import addOpenTypeFeaturesFromString
|
||||
from fontTools import ttLib
|
||||
from fontTools.ttLib.tables import _g_l_y_f as glyf
|
||||
from fontTools.ttLib.tables import otTables as ot
|
||||
import map_pua_emoji
|
||||
from nototools import add_vs_cmap
|
||||
from nototools import font_data
|
||||
from nototools import unicode_data
|
||||
from pathlib import Path
|
||||
|
||||
|
@ -95,12 +97,13 @@ def _add_cmap_entries(colr_font, codepoint, glyph_name):
|
|||
print(f"Map 0x{codepoint:04x} to {glyph_name}, format {table.format}")
|
||||
|
||||
|
||||
def _map_missing_flag_tag_chars_to_empty_glyphs(colr_font):
|
||||
# Add all tag characters used in flags
|
||||
tag_cps = set(range(0xE0030, 0xE0039 + 1)) | set(range(0xE0061, 0xE007A + 1))
|
||||
FLAG_TAGS = set(range(0xE0030, 0xE0039 + 1)) | set(range(0xE0061, 0xE007A + 1))
|
||||
CANCEL_TAG = 0xE007F
|
||||
|
||||
# Cancel tag
|
||||
tag_cps |= {0xE007F}
|
||||
|
||||
def _map_missing_flag_tag_chars_to_empty_glyphs(colr_font):
|
||||
# Add all tag characters used in flags + cancel tag
|
||||
tag_cps = FLAG_TAGS | {CANCEL_TAG}
|
||||
|
||||
# Anything already cmap'd is fine
|
||||
tag_cps -= set(_Cmap(colr_font).keys())
|
||||
|
@ -143,28 +146,6 @@ def _Cmap(ttfont):
|
|||
return functools.reduce(_Reducer, unicode_cmaps, {})
|
||||
|
||||
|
||||
def _map_empty_flag_tag_to_black_flag(colr_font):
|
||||
# fontchain_lint wants direct support for empty flag tags
|
||||
# so map them to the default flag to match cbdt behavior
|
||||
|
||||
# if the emoji font starts using extensions this code will require revision
|
||||
|
||||
cmap = _Cmap(colr_font)
|
||||
black_flag_glyph = cmap[0x1F3F4]
|
||||
cancel_tag_glyph = cmap[0xE007F]
|
||||
lookup_list = colr_font["GSUB"].table.LookupList
|
||||
liga_set = _ligaset_for_glyph(lookup_list, black_flag_glyph)
|
||||
assert liga_set is not None, "There should be existing ligatures using black flag"
|
||||
|
||||
# Map black flag + cancel tag to just black flag
|
||||
# Since this is the ligature set for black flag, component is just cancel tag
|
||||
# Since we only have one component its safe to put our rule at the front
|
||||
liga = ot.Ligature()
|
||||
liga.Component = [cancel_tag_glyph]
|
||||
liga.LigGlyph = black_flag_glyph
|
||||
liga_set.insert(0, liga)
|
||||
|
||||
|
||||
def _add_vertical_layout_tables(cbdt_font, colr_font):
|
||||
upem_scale = colr_font["head"].unitsPerEm / cbdt_font["head"].unitsPerEm
|
||||
|
||||
|
@ -201,6 +182,112 @@ def _add_vertical_layout_tables(cbdt_font, colr_font):
|
|||
vmtx.metrics[gn] = height, 0
|
||||
|
||||
|
||||
UNKNOWN_FLAG_PUA = 0xFE82B
|
||||
BLACK_FLAG = 0x1F3F4
|
||||
REGIONAL_INDICATORS = set(range(0x1F1E6, 0x1F1FF + 1))
|
||||
|
||||
|
||||
def _add_fallback_subs_for_unknown_flags(colr_font):
|
||||
"""Add GSUB lookups to replace unsupported flag sequences with the 'unknown flag'.
|
||||
|
||||
In order to locate the unknown flag, the glyph must be mapped to 0xFE82B PUA code;
|
||||
the latter is removed from the cmap table after the GSUB has been updated.
|
||||
"""
|
||||
cmap = _Cmap(colr_font)
|
||||
unknown_flag = cmap[UNKNOWN_FLAG_PUA]
|
||||
black_flag = cmap[BLACK_FLAG]
|
||||
cancel_tag = cmap[CANCEL_TAG]
|
||||
flag_tags = sorted(cmap[cp] for cp in FLAG_TAGS)
|
||||
# in the *-noflags.ttf font there are no region flags thus this list is empty
|
||||
regional_indicators = sorted(cmap[cp] for cp in REGIONAL_INDICATORS if cp in cmap)
|
||||
|
||||
classes = f'@FLAG_TAGS = [{" ".join(flag_tags)}];\n'
|
||||
if regional_indicators:
|
||||
classes += f"""
|
||||
@REGIONAL_INDICATORS = [{" ".join(regional_indicators)}];
|
||||
@UNKNOWN_FLAG = [{" ".join([unknown_flag] * len(regional_indicators))}];
|
||||
"""
|
||||
lookups = (
|
||||
# the first lookup is a dummy that stands for the emoji sequences ligatures
|
||||
# from the destination font; we only use it to ensure the lookup indices match.
|
||||
# We can't leave it empty otherwise feaLib optimizes it away.
|
||||
f"""
|
||||
lookup placeholder {{
|
||||
sub {unknown_flag} {unknown_flag} by {unknown_flag};
|
||||
}} placeholder;
|
||||
"""
|
||||
+ "\n".join(
|
||||
["lookup delete_glyph {"]
|
||||
+ [f" sub {g} by NULL;" for g in sorted(regional_indicators + flag_tags)]
|
||||
+ ["} delete_glyph;"]
|
||||
)
|
||||
+ (
|
||||
"""
|
||||
lookup replace_with_unknown_flag {
|
||||
sub @REGIONAL_INDICATORS by @UNKNOWN_FLAG;
|
||||
} replace_with_unknown_flag;
|
||||
"""
|
||||
if regional_indicators
|
||||
else "\n"
|
||||
)
|
||||
)
|
||||
features = (
|
||||
"languagesystem DFLT dflt;\n"
|
||||
+ classes
|
||||
+ lookups
|
||||
+ "feature ccmp {"
|
||||
+ f"""
|
||||
lookup placeholder;
|
||||
sub {black_flag} @FLAG_TAGS' lookup delete_glyph;
|
||||
sub {black_flag} {cancel_tag} by {unknown_flag};
|
||||
"""
|
||||
+ (
|
||||
"""
|
||||
sub @REGIONAL_INDICATORS' lookup replace_with_unknown_flag
|
||||
@REGIONAL_INDICATORS' lookup delete_glyph;
|
||||
"""
|
||||
if regional_indicators
|
||||
else ""
|
||||
)
|
||||
+ "} ccmp;"
|
||||
)
|
||||
# feaLib always builds a new GSUB table (can't update one in place) so we have to
|
||||
# use an empty TTFont and then update our GSUB with the newly built lookups
|
||||
temp_font = ttLib.TTFont()
|
||||
temp_font.setGlyphOrder(colr_font.getGlyphOrder())
|
||||
|
||||
addOpenTypeFeaturesFromString(temp_font, features)
|
||||
|
||||
temp_gsub = temp_font["GSUB"].table
|
||||
# sanity check
|
||||
assert len(temp_gsub.FeatureList.FeatureRecord) == 1
|
||||
assert temp_gsub.FeatureList.FeatureRecord[0].FeatureTag == "ccmp"
|
||||
temp_ccmp = temp_gsub.FeatureList.FeatureRecord[0].Feature
|
||||
|
||||
colr_gsub = colr_font["GSUB"].table
|
||||
ccmps = [
|
||||
r.Feature for r in colr_gsub.FeatureList.FeatureRecord if r.FeatureTag == "ccmp"
|
||||
]
|
||||
assert len(ccmps) == 1, f"expected only 1 'ccmp' feature record, found {len(ccmps)}"
|
||||
colr_ccmp = ccmps[0]
|
||||
|
||||
colr_lookups = colr_gsub.LookupList.Lookup
|
||||
assert (
|
||||
len(colr_lookups) == 1
|
||||
), f"expected only 1 lookup in COLRv1's GSUB.LookupList, found {len(colr_lookups)}"
|
||||
assert (
|
||||
colr_lookups[0].LookupType == 4
|
||||
), f"expected Lookup[0] of type 4 in COLRv1, found {colr_lookups[0].LookupType}"
|
||||
|
||||
colr_lookups.extend(temp_gsub.LookupList.Lookup[1:])
|
||||
colr_gsub.LookupList.LookupCount = len(colr_lookups)
|
||||
colr_ccmp.LookupListIndex = temp_ccmp.LookupListIndex
|
||||
colr_ccmp.LookupCount = len(colr_ccmp.LookupListIndex)
|
||||
|
||||
# get rid of the Unknown Flag private codepoint as no longer needed
|
||||
font_data.delete_from_cmap(colr_font, [UNKNOWN_FLAG_PUA])
|
||||
|
||||
|
||||
def main(argv):
|
||||
if len(argv) != 3:
|
||||
raise ValueError(
|
||||
|
@ -233,12 +320,12 @@ def main(argv):
|
|||
|
||||
_map_missing_flag_tag_chars_to_empty_glyphs(colr_font)
|
||||
|
||||
_map_empty_flag_tag_to_black_flag(colr_font)
|
||||
|
||||
add_soft_light_to_flags(colr_font)
|
||||
|
||||
_add_vertical_layout_tables(cbdt_font, colr_font)
|
||||
|
||||
_add_fallback_subs_for_unknown_flags(colr_font)
|
||||
|
||||
out_file = Path(_OUTPUT_FILE[colr_file.name]).absolute()
|
||||
print("Writing", out_file)
|
||||
colr_font.save(out_file)
|
||||
|
|
Loading…
Reference in New Issue