1*d57664e9SAndroid Build Coastguard Worker#!/usr/bin/env python 2*d57664e9SAndroid Build Coastguard Worker 3*d57664e9SAndroid Build Coastguard Workerimport collections 4*d57664e9SAndroid Build Coastguard Workerimport copy 5*d57664e9SAndroid Build Coastguard Workerimport glob 6*d57664e9SAndroid Build Coastguard Workerfrom os import path 7*d57664e9SAndroid Build Coastguard Workerimport re 8*d57664e9SAndroid Build Coastguard Workerimport sys 9*d57664e9SAndroid Build Coastguard Workerfrom xml.etree import ElementTree 10*d57664e9SAndroid Build Coastguard Worker 11*d57664e9SAndroid Build Coastguard Workerfrom fontTools import ttLib 12*d57664e9SAndroid Build Coastguard Worker 13*d57664e9SAndroid Build Coastguard Worker# TODO(nona): Remove hard coded font version and unicode versions. 14*d57664e9SAndroid Build Coastguard Worker# Figure out a way of giving this information with command lines. 15*d57664e9SAndroid Build Coastguard WorkerEMOJI_FONT_TO_UNICODE_MAP = { 16*d57664e9SAndroid Build Coastguard Worker '2.034': 15.0, 17*d57664e9SAndroid Build Coastguard Worker '2.042': 15.1, 18*d57664e9SAndroid Build Coastguard Worker '2.047': 16.0, 19*d57664e9SAndroid Build Coastguard Worker} 20*d57664e9SAndroid Build Coastguard Worker 21*d57664e9SAndroid Build Coastguard WorkerEMOJI_VS = 0xFE0F 22*d57664e9SAndroid Build Coastguard Worker 23*d57664e9SAndroid Build Coastguard WorkerLANG_TO_SCRIPT = { 24*d57664e9SAndroid Build Coastguard Worker 'af': 'Latn', 25*d57664e9SAndroid Build Coastguard Worker 'as': 'Beng', 26*d57664e9SAndroid Build Coastguard Worker 'am': 'Latn', 27*d57664e9SAndroid Build Coastguard Worker 'be': 'Cyrl', 28*d57664e9SAndroid Build Coastguard Worker 'bg': 'Cyrl', 29*d57664e9SAndroid Build Coastguard Worker 'bn': 'Beng', 30*d57664e9SAndroid Build Coastguard Worker 'cs': 'Latn', 31*d57664e9SAndroid Build Coastguard Worker 'cu': 'Cyrl', 32*d57664e9SAndroid Build Coastguard Worker 'cy': 'Latn', 33*d57664e9SAndroid Build Coastguard Worker 'da': 'Latn', 34*d57664e9SAndroid Build Coastguard Worker 'de': 'Latn', 35*d57664e9SAndroid Build Coastguard Worker 'el': 'Latn', 36*d57664e9SAndroid Build Coastguard Worker 'en': 'Latn', 37*d57664e9SAndroid Build Coastguard Worker 'es': 'Latn', 38*d57664e9SAndroid Build Coastguard Worker 'et': 'Latn', 39*d57664e9SAndroid Build Coastguard Worker 'eu': 'Latn', 40*d57664e9SAndroid Build Coastguard Worker 'fr': 'Latn', 41*d57664e9SAndroid Build Coastguard Worker 'ga': 'Latn', 42*d57664e9SAndroid Build Coastguard Worker 'gl': 'Latn', 43*d57664e9SAndroid Build Coastguard Worker 'gu': 'Gujr', 44*d57664e9SAndroid Build Coastguard Worker 'hi': 'Deva', 45*d57664e9SAndroid Build Coastguard Worker 'hr': 'Latn', 46*d57664e9SAndroid Build Coastguard Worker 'hu': 'Latn', 47*d57664e9SAndroid Build Coastguard Worker 'hy': 'Armn', 48*d57664e9SAndroid Build Coastguard Worker 'it': 'Latn', 49*d57664e9SAndroid Build Coastguard Worker 'ja': 'Jpan', 50*d57664e9SAndroid Build Coastguard Worker 'ka': 'Latn', 51*d57664e9SAndroid Build Coastguard Worker 'kn': 'Knda', 52*d57664e9SAndroid Build Coastguard Worker 'ko': 'Kore', 53*d57664e9SAndroid Build Coastguard Worker 'la': 'Latn', 54*d57664e9SAndroid Build Coastguard Worker 'lt': 'Latn', 55*d57664e9SAndroid Build Coastguard Worker 'lv': 'Latn', 56*d57664e9SAndroid Build Coastguard Worker 'ml': 'Mlym', 57*d57664e9SAndroid Build Coastguard Worker 'mn': 'Cyrl', 58*d57664e9SAndroid Build Coastguard Worker 'mr': 'Deva', 59*d57664e9SAndroid Build Coastguard Worker 'nb': 'Latn', 60*d57664e9SAndroid Build Coastguard Worker 'nl': 'Latn', 61*d57664e9SAndroid Build Coastguard Worker 'nn': 'Latn', 62*d57664e9SAndroid Build Coastguard Worker 'or': 'Orya', 63*d57664e9SAndroid Build Coastguard Worker 'pa': 'Guru', 64*d57664e9SAndroid Build Coastguard Worker 'pt': 'Latn', 65*d57664e9SAndroid Build Coastguard Worker 'pl': 'Latn', 66*d57664e9SAndroid Build Coastguard Worker 'ru': 'Latn', 67*d57664e9SAndroid Build Coastguard Worker 'sk': 'Latn', 68*d57664e9SAndroid Build Coastguard Worker 'sl': 'Latn', 69*d57664e9SAndroid Build Coastguard Worker 'sq': 'Latn', 70*d57664e9SAndroid Build Coastguard Worker 'sv': 'Latn', 71*d57664e9SAndroid Build Coastguard Worker 'ta': 'Taml', 72*d57664e9SAndroid Build Coastguard Worker 'te': 'Telu', 73*d57664e9SAndroid Build Coastguard Worker 'tk': 'Latn', 74*d57664e9SAndroid Build Coastguard Worker 'uk': 'Latn', 75*d57664e9SAndroid Build Coastguard Worker} 76*d57664e9SAndroid Build Coastguard Worker 77*d57664e9SAndroid Build Coastguard Workerdef lang_to_script(lang_code): 78*d57664e9SAndroid Build Coastguard Worker lang = lang_code.lower() 79*d57664e9SAndroid Build Coastguard Worker while lang not in LANG_TO_SCRIPT: 80*d57664e9SAndroid Build Coastguard Worker hyphen_idx = lang.rfind('-') 81*d57664e9SAndroid Build Coastguard Worker assert hyphen_idx != -1, ( 82*d57664e9SAndroid Build Coastguard Worker 'We do not know what script the "%s" language is written in.' 83*d57664e9SAndroid Build Coastguard Worker % lang_code) 84*d57664e9SAndroid Build Coastguard Worker assumed_script = lang[hyphen_idx+1:] 85*d57664e9SAndroid Build Coastguard Worker if len(assumed_script) == 4 and assumed_script.isalpha(): 86*d57664e9SAndroid Build Coastguard Worker # This is actually the script 87*d57664e9SAndroid Build Coastguard Worker return assumed_script.title() 88*d57664e9SAndroid Build Coastguard Worker lang = lang[:hyphen_idx] 89*d57664e9SAndroid Build Coastguard Worker return LANG_TO_SCRIPT[lang] 90*d57664e9SAndroid Build Coastguard Worker 91*d57664e9SAndroid Build Coastguard Worker 92*d57664e9SAndroid Build Coastguard Workerdef printable(inp): 93*d57664e9SAndroid Build Coastguard Worker if type(inp) is set: # set of character sequences 94*d57664e9SAndroid Build Coastguard Worker return '{' + ', '.join([printable(seq) for seq in inp]) + '}' 95*d57664e9SAndroid Build Coastguard Worker if type(inp) is tuple: # character sequence 96*d57664e9SAndroid Build Coastguard Worker return '<' + (', '.join([printable(ch) for ch in inp])) + '>' 97*d57664e9SAndroid Build Coastguard Worker else: # single character 98*d57664e9SAndroid Build Coastguard Worker return 'U+%04X' % inp 99*d57664e9SAndroid Build Coastguard Worker 100*d57664e9SAndroid Build Coastguard Worker 101*d57664e9SAndroid Build Coastguard Workerdef open_font(font): 102*d57664e9SAndroid Build Coastguard Worker font_file, index = font 103*d57664e9SAndroid Build Coastguard Worker font_path = path.join(_fonts_dir, font_file) 104*d57664e9SAndroid Build Coastguard Worker if index is not None: 105*d57664e9SAndroid Build Coastguard Worker return ttLib.TTFont(font_path, fontNumber=index) 106*d57664e9SAndroid Build Coastguard Worker else: 107*d57664e9SAndroid Build Coastguard Worker return ttLib.TTFont(font_path) 108*d57664e9SAndroid Build Coastguard Worker 109*d57664e9SAndroid Build Coastguard Worker 110*d57664e9SAndroid Build Coastguard Workerdef get_best_cmap(font): 111*d57664e9SAndroid Build Coastguard Worker ttfont = open_font(font) 112*d57664e9SAndroid Build Coastguard Worker all_unicode_cmap = None 113*d57664e9SAndroid Build Coastguard Worker bmp_cmap = None 114*d57664e9SAndroid Build Coastguard Worker for cmap in ttfont['cmap'].tables: 115*d57664e9SAndroid Build Coastguard Worker specifier = (cmap.format, cmap.platformID, cmap.platEncID) 116*d57664e9SAndroid Build Coastguard Worker if specifier == (4, 3, 1): 117*d57664e9SAndroid Build Coastguard Worker assert bmp_cmap is None, 'More than one BMP cmap in %s' % (font, ) 118*d57664e9SAndroid Build Coastguard Worker bmp_cmap = cmap 119*d57664e9SAndroid Build Coastguard Worker elif specifier == (12, 3, 10): 120*d57664e9SAndroid Build Coastguard Worker assert all_unicode_cmap is None, ( 121*d57664e9SAndroid Build Coastguard Worker 'More than one UCS-4 cmap in %s' % (font, )) 122*d57664e9SAndroid Build Coastguard Worker all_unicode_cmap = cmap 123*d57664e9SAndroid Build Coastguard Worker 124*d57664e9SAndroid Build Coastguard Worker return all_unicode_cmap.cmap if all_unicode_cmap else bmp_cmap.cmap 125*d57664e9SAndroid Build Coastguard Worker 126*d57664e9SAndroid Build Coastguard Worker 127*d57664e9SAndroid Build Coastguard Workerdef get_variation_sequences_cmap(font): 128*d57664e9SAndroid Build Coastguard Worker ttfont = open_font(font) 129*d57664e9SAndroid Build Coastguard Worker vs_cmap = None 130*d57664e9SAndroid Build Coastguard Worker for cmap in ttfont['cmap'].tables: 131*d57664e9SAndroid Build Coastguard Worker specifier = (cmap.format, cmap.platformID, cmap.platEncID) 132*d57664e9SAndroid Build Coastguard Worker if specifier == (14, 0, 5): 133*d57664e9SAndroid Build Coastguard Worker assert vs_cmap is None, 'More than one VS cmap in %s' % (font, ) 134*d57664e9SAndroid Build Coastguard Worker vs_cmap = cmap 135*d57664e9SAndroid Build Coastguard Worker return vs_cmap 136*d57664e9SAndroid Build Coastguard Worker 137*d57664e9SAndroid Build Coastguard Worker 138*d57664e9SAndroid Build Coastguard Workerdef get_emoji_map(font): 139*d57664e9SAndroid Build Coastguard Worker # Add normal characters 140*d57664e9SAndroid Build Coastguard Worker emoji_map = copy.copy(get_best_cmap(font)) 141*d57664e9SAndroid Build Coastguard Worker reverse_cmap = {glyph: code for code, glyph in emoji_map.items() if not contains_pua(code) } 142*d57664e9SAndroid Build Coastguard Worker 143*d57664e9SAndroid Build Coastguard Worker # Add variation sequences 144*d57664e9SAndroid Build Coastguard Worker vs_cmap = get_variation_sequences_cmap(font) 145*d57664e9SAndroid Build Coastguard Worker if vs_cmap: 146*d57664e9SAndroid Build Coastguard Worker for vs in vs_cmap.uvsDict: 147*d57664e9SAndroid Build Coastguard Worker for base, glyph in vs_cmap.uvsDict[vs]: 148*d57664e9SAndroid Build Coastguard Worker if glyph is None: 149*d57664e9SAndroid Build Coastguard Worker emoji_map[(base, vs)] = emoji_map[base] 150*d57664e9SAndroid Build Coastguard Worker else: 151*d57664e9SAndroid Build Coastguard Worker emoji_map[(base, vs)] = glyph 152*d57664e9SAndroid Build Coastguard Worker 153*d57664e9SAndroid Build Coastguard Worker # Add GSUB rules 154*d57664e9SAndroid Build Coastguard Worker ttfont = open_font(font) 155*d57664e9SAndroid Build Coastguard Worker for lookup in ttfont['GSUB'].table.LookupList.Lookup: 156*d57664e9SAndroid Build Coastguard Worker if lookup.LookupType != 4: 157*d57664e9SAndroid Build Coastguard Worker # Other lookups are used in the emoji font for fallback. 158*d57664e9SAndroid Build Coastguard Worker # We ignore them for now. 159*d57664e9SAndroid Build Coastguard Worker continue 160*d57664e9SAndroid Build Coastguard Worker for subtable in lookup.SubTable: 161*d57664e9SAndroid Build Coastguard Worker ligatures = subtable.ligatures 162*d57664e9SAndroid Build Coastguard Worker for first_glyph in ligatures: 163*d57664e9SAndroid Build Coastguard Worker for ligature in ligatures[first_glyph]: 164*d57664e9SAndroid Build Coastguard Worker sequence = [first_glyph] + ligature.Component 165*d57664e9SAndroid Build Coastguard Worker sequence = [reverse_cmap[glyph] for glyph in sequence] 166*d57664e9SAndroid Build Coastguard Worker sequence = tuple(sequence) 167*d57664e9SAndroid Build Coastguard Worker # Make sure no starting subsequence of 'sequence' has been 168*d57664e9SAndroid Build Coastguard Worker # seen before. 169*d57664e9SAndroid Build Coastguard Worker for sub_len in range(2, len(sequence)+1): 170*d57664e9SAndroid Build Coastguard Worker subsequence = sequence[:sub_len] 171*d57664e9SAndroid Build Coastguard Worker assert subsequence not in emoji_map 172*d57664e9SAndroid Build Coastguard Worker emoji_map[sequence] = ligature.LigGlyph 173*d57664e9SAndroid Build Coastguard Worker 174*d57664e9SAndroid Build Coastguard Worker return emoji_map 175*d57664e9SAndroid Build Coastguard Worker 176*d57664e9SAndroid Build Coastguard Worker 177*d57664e9SAndroid Build Coastguard Workerdef assert_font_supports_any_of_chars(font, chars): 178*d57664e9SAndroid Build Coastguard Worker best_cmap = get_best_cmap(font) 179*d57664e9SAndroid Build Coastguard Worker for char in chars: 180*d57664e9SAndroid Build Coastguard Worker if char in best_cmap: 181*d57664e9SAndroid Build Coastguard Worker return 182*d57664e9SAndroid Build Coastguard Worker sys.exit('None of characters in %s were found in %s' % (chars, font)) 183*d57664e9SAndroid Build Coastguard Worker 184*d57664e9SAndroid Build Coastguard Worker 185*d57664e9SAndroid Build Coastguard Workerdef assert_font_supports_all_of_chars(font, chars): 186*d57664e9SAndroid Build Coastguard Worker best_cmap = get_best_cmap(font) 187*d57664e9SAndroid Build Coastguard Worker for char in chars: 188*d57664e9SAndroid Build Coastguard Worker assert char in best_cmap, ( 189*d57664e9SAndroid Build Coastguard Worker 'U+%04X was not found in %s' % (char, font)) 190*d57664e9SAndroid Build Coastguard Worker 191*d57664e9SAndroid Build Coastguard Worker 192*d57664e9SAndroid Build Coastguard Workerdef assert_font_supports_none_of_chars(font, chars, fallbackName): 193*d57664e9SAndroid Build Coastguard Worker best_cmap = get_best_cmap(font) 194*d57664e9SAndroid Build Coastguard Worker for char in chars: 195*d57664e9SAndroid Build Coastguard Worker if fallbackName: 196*d57664e9SAndroid Build Coastguard Worker assert char not in best_cmap, 'U+%04X was found in %s' % (char, font) 197*d57664e9SAndroid Build Coastguard Worker else: 198*d57664e9SAndroid Build Coastguard Worker assert char not in best_cmap, ( 199*d57664e9SAndroid Build Coastguard Worker 'U+%04X was found in %s in fallback %s' % (char, font, fallbackName)) 200*d57664e9SAndroid Build Coastguard Worker 201*d57664e9SAndroid Build Coastguard Worker 202*d57664e9SAndroid Build Coastguard Workerdef assert_font_supports_all_sequences(font, sequences): 203*d57664e9SAndroid Build Coastguard Worker vs_dict = get_variation_sequences_cmap(font).uvsDict 204*d57664e9SAndroid Build Coastguard Worker for base, vs in sorted(sequences): 205*d57664e9SAndroid Build Coastguard Worker assert vs in vs_dict and (base, None) in vs_dict[vs], ( 206*d57664e9SAndroid Build Coastguard Worker '<U+%04X, U+%04X> was not found in %s' % (base, vs, font)) 207*d57664e9SAndroid Build Coastguard Worker 208*d57664e9SAndroid Build Coastguard Worker 209*d57664e9SAndroid Build Coastguard Workerdef check_hyphens(hyphens_dir): 210*d57664e9SAndroid Build Coastguard Worker # Find all the scripts that need automatic hyphenation 211*d57664e9SAndroid Build Coastguard Worker scripts = set() 212*d57664e9SAndroid Build Coastguard Worker for hyb_file in glob.iglob(path.join(hyphens_dir, '*.hyb')): 213*d57664e9SAndroid Build Coastguard Worker hyb_file = path.basename(hyb_file) 214*d57664e9SAndroid Build Coastguard Worker assert hyb_file.startswith('hyph-'), ( 215*d57664e9SAndroid Build Coastguard Worker 'Unknown hyphenation file %s' % hyb_file) 216*d57664e9SAndroid Build Coastguard Worker lang_code = hyb_file[hyb_file.index('-')+1:hyb_file.index('.')] 217*d57664e9SAndroid Build Coastguard Worker scripts.add(lang_to_script(lang_code)) 218*d57664e9SAndroid Build Coastguard Worker 219*d57664e9SAndroid Build Coastguard Worker HYPHENS = {0x002D, 0x2010} 220*d57664e9SAndroid Build Coastguard Worker for script in scripts: 221*d57664e9SAndroid Build Coastguard Worker fonts = _script_to_font_map[script] 222*d57664e9SAndroid Build Coastguard Worker assert fonts, 'No fonts found for the "%s" script' % script 223*d57664e9SAndroid Build Coastguard Worker for font in fonts: 224*d57664e9SAndroid Build Coastguard Worker assert_font_supports_any_of_chars(font, HYPHENS) 225*d57664e9SAndroid Build Coastguard Worker 226*d57664e9SAndroid Build Coastguard Worker 227*d57664e9SAndroid Build Coastguard Workerclass FontRecord(object): 228*d57664e9SAndroid Build Coastguard Worker def __init__(self, name, scripts, variant, weight, style, fallback_for, font): 229*d57664e9SAndroid Build Coastguard Worker self.name = name 230*d57664e9SAndroid Build Coastguard Worker self.scripts = scripts 231*d57664e9SAndroid Build Coastguard Worker self.variant = variant 232*d57664e9SAndroid Build Coastguard Worker self.weight = weight 233*d57664e9SAndroid Build Coastguard Worker self.style = style 234*d57664e9SAndroid Build Coastguard Worker self.fallback_for = fallback_for 235*d57664e9SAndroid Build Coastguard Worker self.font = font 236*d57664e9SAndroid Build Coastguard Worker 237*d57664e9SAndroid Build Coastguard Worker 238*d57664e9SAndroid Build Coastguard Workerdef parse_fonts_xml(fonts_xml_path): 239*d57664e9SAndroid Build Coastguard Worker global _script_to_font_map, _fallback_chains, _all_fonts 240*d57664e9SAndroid Build Coastguard Worker _script_to_font_map = collections.defaultdict(set) 241*d57664e9SAndroid Build Coastguard Worker _fallback_chains = {} 242*d57664e9SAndroid Build Coastguard Worker _all_fonts = [] 243*d57664e9SAndroid Build Coastguard Worker tree = ElementTree.parse(fonts_xml_path) 244*d57664e9SAndroid Build Coastguard Worker families = tree.findall('family') 245*d57664e9SAndroid Build Coastguard Worker # Minikin supports up to 254 but users can place their own font at the first 246*d57664e9SAndroid Build Coastguard Worker # place. Thus, 253 is the maximum allowed number of font families in the 247*d57664e9SAndroid Build Coastguard Worker # default collection. 248*d57664e9SAndroid Build Coastguard Worker assert len(families) < 254, ( 249*d57664e9SAndroid Build Coastguard Worker 'System font collection can contains up to 253 font families.') 250*d57664e9SAndroid Build Coastguard Worker for family in families: 251*d57664e9SAndroid Build Coastguard Worker name = family.get('name') 252*d57664e9SAndroid Build Coastguard Worker variant = family.get('variant') 253*d57664e9SAndroid Build Coastguard Worker langs = family.get('lang') 254*d57664e9SAndroid Build Coastguard Worker ignoreAttr = family.get('ignore') 255*d57664e9SAndroid Build Coastguard Worker 256*d57664e9SAndroid Build Coastguard Worker if name: 257*d57664e9SAndroid Build Coastguard Worker assert variant is None, ( 258*d57664e9SAndroid Build Coastguard Worker 'No variant expected for LGC font %s.' % name) 259*d57664e9SAndroid Build Coastguard Worker assert langs is None, ( 260*d57664e9SAndroid Build Coastguard Worker 'No language expected for LGC fonts %s.' % name) 261*d57664e9SAndroid Build Coastguard Worker assert name not in _fallback_chains, 'Duplicated name entry %s' % name 262*d57664e9SAndroid Build Coastguard Worker _fallback_chains[name] = [] 263*d57664e9SAndroid Build Coastguard Worker else: 264*d57664e9SAndroid Build Coastguard Worker assert variant in {None, 'elegant', 'compact'}, ( 265*d57664e9SAndroid Build Coastguard Worker 'Unexpected value for variant: %s' % variant) 266*d57664e9SAndroid Build Coastguard Worker 267*d57664e9SAndroid Build Coastguard Worker trim_re = re.compile(r"^[ \n\r\t]*(.+)[ \n\r\t]*$") 268*d57664e9SAndroid Build Coastguard Worker for family in families: 269*d57664e9SAndroid Build Coastguard Worker name = family.get('name') 270*d57664e9SAndroid Build Coastguard Worker variant = family.get('variant') 271*d57664e9SAndroid Build Coastguard Worker langs = family.get('lang') 272*d57664e9SAndroid Build Coastguard Worker ignoreAttr = family.get('ignore') 273*d57664e9SAndroid Build Coastguard Worker ignore = ignoreAttr == 'true' or ignoreAttr == '1' 274*d57664e9SAndroid Build Coastguard Worker 275*d57664e9SAndroid Build Coastguard Worker if ignore: 276*d57664e9SAndroid Build Coastguard Worker continue 277*d57664e9SAndroid Build Coastguard Worker 278*d57664e9SAndroid Build Coastguard Worker if langs: 279*d57664e9SAndroid Build Coastguard Worker langs = langs.split() 280*d57664e9SAndroid Build Coastguard Worker scripts = {lang_to_script(lang) for lang in langs} 281*d57664e9SAndroid Build Coastguard Worker else: 282*d57664e9SAndroid Build Coastguard Worker scripts = set() 283*d57664e9SAndroid Build Coastguard Worker 284*d57664e9SAndroid Build Coastguard Worker for child in family: 285*d57664e9SAndroid Build Coastguard Worker assert child.tag == 'font', ( 286*d57664e9SAndroid Build Coastguard Worker 'Unknown tag <%s>' % child.tag) 287*d57664e9SAndroid Build Coastguard Worker font_file = child.text.rstrip() 288*d57664e9SAndroid Build Coastguard Worker 289*d57664e9SAndroid Build Coastguard Worker m = trim_re.match(font_file) 290*d57664e9SAndroid Build Coastguard Worker font_file = m.group(1) 291*d57664e9SAndroid Build Coastguard Worker 292*d57664e9SAndroid Build Coastguard Worker # In case of variable font and it supports `wght` axis, the weight attribute can be 293*d57664e9SAndroid Build Coastguard Worker # dropped which is automatically adjusted at runtime. 294*d57664e9SAndroid Build Coastguard Worker if 'weight' in child: 295*d57664e9SAndroid Build Coastguard Worker weight = int(child.get('weight')) 296*d57664e9SAndroid Build Coastguard Worker assert weight % 100 == 0, ( 297*d57664e9SAndroid Build Coastguard Worker 'Font weight "%d" is not a multiple of 100.' % weight) 298*d57664e9SAndroid Build Coastguard Worker else: 299*d57664e9SAndroid Build Coastguard Worker weight = None 300*d57664e9SAndroid Build Coastguard Worker 301*d57664e9SAndroid Build Coastguard Worker # In case of variable font and it supports `ital` or `slnt` axes, the style attribute 302*d57664e9SAndroid Build Coastguard Worker # can be dropped which is automatically adjusted at runtime. 303*d57664e9SAndroid Build Coastguard Worker if 'style' in child: 304*d57664e9SAndroid Build Coastguard Worker style = child.get('style') 305*d57664e9SAndroid Build Coastguard Worker assert style in {'normal', 'italic'}, ( 306*d57664e9SAndroid Build Coastguard Worker 'Unknown style "%s"' % style) 307*d57664e9SAndroid Build Coastguard Worker else: 308*d57664e9SAndroid Build Coastguard Worker style = None 309*d57664e9SAndroid Build Coastguard Worker 310*d57664e9SAndroid Build Coastguard Worker fallback_for = child.get('fallbackFor') 311*d57664e9SAndroid Build Coastguard Worker 312*d57664e9SAndroid Build Coastguard Worker assert not name or not fallback_for, ( 313*d57664e9SAndroid Build Coastguard Worker 'name and fallbackFor cannot be present at the same time') 314*d57664e9SAndroid Build Coastguard Worker assert not fallback_for or fallback_for in _fallback_chains, ( 315*d57664e9SAndroid Build Coastguard Worker 'Unknown fallback name: %s' % fallback_for) 316*d57664e9SAndroid Build Coastguard Worker 317*d57664e9SAndroid Build Coastguard Worker index = child.get('index') 318*d57664e9SAndroid Build Coastguard Worker if index: 319*d57664e9SAndroid Build Coastguard Worker index = int(index) 320*d57664e9SAndroid Build Coastguard Worker 321*d57664e9SAndroid Build Coastguard Worker if not path.exists(path.join(_fonts_dir, m.group(1))): 322*d57664e9SAndroid Build Coastguard Worker continue # Missing font is a valid case. Just ignore the missing font files. 323*d57664e9SAndroid Build Coastguard Worker 324*d57664e9SAndroid Build Coastguard Worker record = FontRecord( 325*d57664e9SAndroid Build Coastguard Worker name, 326*d57664e9SAndroid Build Coastguard Worker frozenset(scripts), 327*d57664e9SAndroid Build Coastguard Worker variant, 328*d57664e9SAndroid Build Coastguard Worker weight, 329*d57664e9SAndroid Build Coastguard Worker style, 330*d57664e9SAndroid Build Coastguard Worker fallback_for, 331*d57664e9SAndroid Build Coastguard Worker (font_file, index)) 332*d57664e9SAndroid Build Coastguard Worker 333*d57664e9SAndroid Build Coastguard Worker _all_fonts.append(record) 334*d57664e9SAndroid Build Coastguard Worker 335*d57664e9SAndroid Build Coastguard Worker if not fallback_for: 336*d57664e9SAndroid Build Coastguard Worker if not name or name == 'sans-serif': 337*d57664e9SAndroid Build Coastguard Worker for _, fallback in _fallback_chains.items(): 338*d57664e9SAndroid Build Coastguard Worker fallback.append(record) 339*d57664e9SAndroid Build Coastguard Worker else: 340*d57664e9SAndroid Build Coastguard Worker _fallback_chains[name].append(record) 341*d57664e9SAndroid Build Coastguard Worker else: 342*d57664e9SAndroid Build Coastguard Worker _fallback_chains[fallback_for].append(record) 343*d57664e9SAndroid Build Coastguard Worker 344*d57664e9SAndroid Build Coastguard Worker if name: # non-empty names are used for default LGC fonts 345*d57664e9SAndroid Build Coastguard Worker map_scripts = {'Latn', 'Grek', 'Cyrl'} 346*d57664e9SAndroid Build Coastguard Worker else: 347*d57664e9SAndroid Build Coastguard Worker map_scripts = scripts 348*d57664e9SAndroid Build Coastguard Worker for script in map_scripts: 349*d57664e9SAndroid Build Coastguard Worker _script_to_font_map[script].add((font_file, index)) 350*d57664e9SAndroid Build Coastguard Worker 351*d57664e9SAndroid Build Coastguard Worker 352*d57664e9SAndroid Build Coastguard Workerdef check_emoji_coverage(all_emoji, equivalent_emoji): 353*d57664e9SAndroid Build Coastguard Worker emoji_fonts = get_emoji_fonts() 354*d57664e9SAndroid Build Coastguard Worker check_emoji_font_coverage(emoji_fonts, all_emoji, equivalent_emoji) 355*d57664e9SAndroid Build Coastguard Worker 356*d57664e9SAndroid Build Coastguard Worker 357*d57664e9SAndroid Build Coastguard Workerdef get_emoji_fonts(): 358*d57664e9SAndroid Build Coastguard Worker return [ record.font for record in _all_fonts if 'Zsye' in record.scripts ] 359*d57664e9SAndroid Build Coastguard Worker 360*d57664e9SAndroid Build Coastguard Workerdef seq_any(sequence, pred): 361*d57664e9SAndroid Build Coastguard Worker if type(sequence) is tuple: 362*d57664e9SAndroid Build Coastguard Worker return any([pred(x) for x in sequence]) 363*d57664e9SAndroid Build Coastguard Worker else: 364*d57664e9SAndroid Build Coastguard Worker return pred(sequence) 365*d57664e9SAndroid Build Coastguard Worker 366*d57664e9SAndroid Build Coastguard Workerdef seq_all(sequence, pred): 367*d57664e9SAndroid Build Coastguard Worker if type(sequence) is tuple: 368*d57664e9SAndroid Build Coastguard Worker return all([pred(x) for x in sequence]) 369*d57664e9SAndroid Build Coastguard Worker else: 370*d57664e9SAndroid Build Coastguard Worker return pred(sequence) 371*d57664e9SAndroid Build Coastguard Worker 372*d57664e9SAndroid Build Coastguard Workerdef is_regional_indicator(x): 373*d57664e9SAndroid Build Coastguard Worker # regional indicator A..Z 374*d57664e9SAndroid Build Coastguard Worker return 0x1F1E6 <= x <= 0x1F1FF 375*d57664e9SAndroid Build Coastguard Worker 376*d57664e9SAndroid Build Coastguard Workerdef is_flag_sequence(seq): 377*d57664e9SAndroid Build Coastguard Worker if type(seq) == int: 378*d57664e9SAndroid Build Coastguard Worker return False 379*d57664e9SAndroid Build Coastguard Worker len(seq) == 2 and is_regional_indicator(seq[0]) and is_regional_indicator(seq[1]) 380*d57664e9SAndroid Build Coastguard Worker 381*d57664e9SAndroid Build Coastguard Workerdef is_tag(x): 382*d57664e9SAndroid Build Coastguard Worker # tag block 383*d57664e9SAndroid Build Coastguard Worker return 0xE0000 <= x <= 0xE007F 384*d57664e9SAndroid Build Coastguard Worker 385*d57664e9SAndroid Build Coastguard Workerdef is_pua(x): 386*d57664e9SAndroid Build Coastguard Worker return 0xE000 <= x <= 0xF8FF or 0xF0000 <= x <= 0xFFFFD or 0x100000 <= x <= 0x10FFFD 387*d57664e9SAndroid Build Coastguard Worker 388*d57664e9SAndroid Build Coastguard Workerdef contains_pua(sequence): 389*d57664e9SAndroid Build Coastguard Worker return seq_any(sequence, is_pua) 390*d57664e9SAndroid Build Coastguard Worker 391*d57664e9SAndroid Build Coastguard Workerdef contains_regional_indicator(sequence): 392*d57664e9SAndroid Build Coastguard Worker return seq_any(sequence, is_regional_indicator) 393*d57664e9SAndroid Build Coastguard Worker 394*d57664e9SAndroid Build Coastguard Workerdef only_tags(sequence): 395*d57664e9SAndroid Build Coastguard Worker return seq_all(sequence, is_tag) 396*d57664e9SAndroid Build Coastguard Worker 397*d57664e9SAndroid Build Coastguard Workerdef get_psname(ttf): 398*d57664e9SAndroid Build Coastguard Worker return str(next(x for x in ttf['name'].names 399*d57664e9SAndroid Build Coastguard Worker if x.platformID == 3 and x.platEncID == 1 and x.nameID == 6)) 400*d57664e9SAndroid Build Coastguard Worker 401*d57664e9SAndroid Build Coastguard Workerdef hex_strs(sequence): 402*d57664e9SAndroid Build Coastguard Worker if type(sequence) is tuple: 403*d57664e9SAndroid Build Coastguard Worker return tuple(f"{s:X}" for s in sequence) 404*d57664e9SAndroid Build Coastguard Worker return hex(sequence) 405*d57664e9SAndroid Build Coastguard Worker 406*d57664e9SAndroid Build Coastguard Workerdef check_emoji_not_compat(all_emoji, equivalent_emoji): 407*d57664e9SAndroid Build Coastguard Worker compat_psnames = set() 408*d57664e9SAndroid Build Coastguard Worker for emoji_font in get_emoji_fonts(): 409*d57664e9SAndroid Build Coastguard Worker ttf = open_font(emoji_font) 410*d57664e9SAndroid Build Coastguard Worker psname = get_psname(ttf) 411*d57664e9SAndroid Build Coastguard Worker 412*d57664e9SAndroid Build Coastguard Worker if "meta" in ttf: 413*d57664e9SAndroid Build Coastguard Worker assert 'Emji' not in ttf["meta"].data, 'NotoColorEmoji MUST be a compat font' 414*d57664e9SAndroid Build Coastguard Worker 415*d57664e9SAndroid Build Coastguard Workerdef is_flag_emoji(font): 416*d57664e9SAndroid Build Coastguard Worker return 0x1F1E6 in get_best_cmap(font) 417*d57664e9SAndroid Build Coastguard Worker 418*d57664e9SAndroid Build Coastguard Workerdef emoji_font_version_to_unicode_version(font_version): 419*d57664e9SAndroid Build Coastguard Worker version_str = '%.3f' % font_version 420*d57664e9SAndroid Build Coastguard Worker assert version_str in EMOJI_FONT_TO_UNICODE_MAP, 'Unknown emoji font verion: %s' % version_str 421*d57664e9SAndroid Build Coastguard Worker return EMOJI_FONT_TO_UNICODE_MAP[version_str] 422*d57664e9SAndroid Build Coastguard Worker 423*d57664e9SAndroid Build Coastguard Worker 424*d57664e9SAndroid Build Coastguard Workerdef check_emoji_font_coverage(emoji_fonts, all_emoji, equivalent_emoji): 425*d57664e9SAndroid Build Coastguard Worker coverages = [] 426*d57664e9SAndroid Build Coastguard Worker emoji_font_version = 0 427*d57664e9SAndroid Build Coastguard Worker emoji_flag_font_version = 0 428*d57664e9SAndroid Build Coastguard Worker for emoji_font in emoji_fonts: 429*d57664e9SAndroid Build Coastguard Worker coverages.append(get_emoji_map(emoji_font)) 430*d57664e9SAndroid Build Coastguard Worker 431*d57664e9SAndroid Build Coastguard Worker # Find the largest version of the installed emoji font. 432*d57664e9SAndroid Build Coastguard Worker version = open_font(emoji_font)['head'].fontRevision 433*d57664e9SAndroid Build Coastguard Worker if is_flag_emoji(emoji_font): 434*d57664e9SAndroid Build Coastguard Worker emoji_flag_font_version = max(emoji_flag_font_version, version) 435*d57664e9SAndroid Build Coastguard Worker else: 436*d57664e9SAndroid Build Coastguard Worker emoji_font_version = max(emoji_font_version, version) 437*d57664e9SAndroid Build Coastguard Worker 438*d57664e9SAndroid Build Coastguard Worker emoji_flag_unicode_version = emoji_font_version_to_unicode_version(emoji_flag_font_version) 439*d57664e9SAndroid Build Coastguard Worker emoji_unicode_version = emoji_font_version_to_unicode_version(emoji_font_version) 440*d57664e9SAndroid Build Coastguard Worker 441*d57664e9SAndroid Build Coastguard Worker errors = [] 442*d57664e9SAndroid Build Coastguard Worker 443*d57664e9SAndroid Build Coastguard Worker for sequence in all_emoji: 444*d57664e9SAndroid Build Coastguard Worker if all([sequence not in coverage for coverage in coverages]): 445*d57664e9SAndroid Build Coastguard Worker sequence_version = float(_age_by_chars[sequence]) 446*d57664e9SAndroid Build Coastguard Worker if is_flag_sequence(sequence): 447*d57664e9SAndroid Build Coastguard Worker if sequence_version <= emoji_flag_unicode_version: 448*d57664e9SAndroid Build Coastguard Worker errors.append('%s is not supported in the emoji font.' % printable(sequence)) 449*d57664e9SAndroid Build Coastguard Worker else: 450*d57664e9SAndroid Build Coastguard Worker if sequence_version <= emoji_unicode_version: 451*d57664e9SAndroid Build Coastguard Worker errors.append('%s is not supported in the emoji font.' % printable(sequence)) 452*d57664e9SAndroid Build Coastguard Worker 453*d57664e9SAndroid Build Coastguard Worker for coverage in coverages: 454*d57664e9SAndroid Build Coastguard Worker for sequence in coverage: 455*d57664e9SAndroid Build Coastguard Worker if sequence in {0x0000, 0x000D, 0x0020}: 456*d57664e9SAndroid Build Coastguard Worker # The font needs to support a few extra characters, which is OK 457*d57664e9SAndroid Build Coastguard Worker continue 458*d57664e9SAndroid Build Coastguard Worker 459*d57664e9SAndroid Build Coastguard Worker if contains_pua(sequence): 460*d57664e9SAndroid Build Coastguard Worker # The font needs to have some PUA for EmojiCompat library. 461*d57664e9SAndroid Build Coastguard Worker continue 462*d57664e9SAndroid Build Coastguard Worker 463*d57664e9SAndroid Build Coastguard Worker if sequence not in all_emoji: 464*d57664e9SAndroid Build Coastguard Worker errors.append('%s support unexpected in the emoji font.' % printable(sequence)) 465*d57664e9SAndroid Build Coastguard Worker 466*d57664e9SAndroid Build Coastguard Worker for first, second in equivalent_emoji.items(): 467*d57664e9SAndroid Build Coastguard Worker for coverage in coverages: 468*d57664e9SAndroid Build Coastguard Worker if first not in coverage or second not in coverage: 469*d57664e9SAndroid Build Coastguard Worker continue # sequence will be reported missing 470*d57664e9SAndroid Build Coastguard Worker if coverage[first] != coverage[second]: 471*d57664e9SAndroid Build Coastguard Worker errors.append('%s and %s should map to the same glyph.' % ( 472*d57664e9SAndroid Build Coastguard Worker printable(first), 473*d57664e9SAndroid Build Coastguard Worker printable(second))) 474*d57664e9SAndroid Build Coastguard Worker 475*d57664e9SAndroid Build Coastguard Worker for coverage in coverages: 476*d57664e9SAndroid Build Coastguard Worker for glyph in set(coverage.values()): 477*d57664e9SAndroid Build Coastguard Worker maps_to_glyph = [ 478*d57664e9SAndroid Build Coastguard Worker seq for seq in coverage if coverage[seq] == glyph and not contains_pua(seq) ] 479*d57664e9SAndroid Build Coastguard Worker if len(maps_to_glyph) > 1: 480*d57664e9SAndroid Build Coastguard Worker # There are more than one sequences mapping to the same glyph. We 481*d57664e9SAndroid Build Coastguard Worker # need to make sure they were expected to be equivalent. 482*d57664e9SAndroid Build Coastguard Worker equivalent_seqs = set() 483*d57664e9SAndroid Build Coastguard Worker for seq in maps_to_glyph: 484*d57664e9SAndroid Build Coastguard Worker equivalent_seq = seq 485*d57664e9SAndroid Build Coastguard Worker while equivalent_seq in equivalent_emoji: 486*d57664e9SAndroid Build Coastguard Worker equivalent_seq = equivalent_emoji[equivalent_seq] 487*d57664e9SAndroid Build Coastguard Worker equivalent_seqs.add(equivalent_seq) 488*d57664e9SAndroid Build Coastguard Worker if len(equivalent_seqs) != 1: 489*d57664e9SAndroid Build Coastguard Worker errors.append('The sequences %s should not result in the same glyph %s' % ( 490*d57664e9SAndroid Build Coastguard Worker printable(equivalent_seqs), 491*d57664e9SAndroid Build Coastguard Worker glyph)) 492*d57664e9SAndroid Build Coastguard Worker 493*d57664e9SAndroid Build Coastguard Worker assert not errors, '%d emoji font errors:\n%s\n%d emoji font coverage errors' % (len(errors), '\n'.join(errors), len(errors)) 494*d57664e9SAndroid Build Coastguard Worker 495*d57664e9SAndroid Build Coastguard Worker 496*d57664e9SAndroid Build Coastguard Workerdef check_emoji_defaults(default_emoji): 497*d57664e9SAndroid Build Coastguard Worker missing_text_chars = _emoji_properties['Emoji'] - default_emoji 498*d57664e9SAndroid Build Coastguard Worker for name, fallback_chain in _fallback_chains.items(): 499*d57664e9SAndroid Build Coastguard Worker emoji_font_seen = False 500*d57664e9SAndroid Build Coastguard Worker for record in fallback_chain: 501*d57664e9SAndroid Build Coastguard Worker if 'Zsye' in record.scripts: 502*d57664e9SAndroid Build Coastguard Worker emoji_font_seen = True 503*d57664e9SAndroid Build Coastguard Worker # No need to check the emoji font 504*d57664e9SAndroid Build Coastguard Worker continue 505*d57664e9SAndroid Build Coastguard Worker # For later fonts, we only check them if they have a script 506*d57664e9SAndroid Build Coastguard Worker # defined, since the defined script may get them to a higher 507*d57664e9SAndroid Build Coastguard Worker # score even if they appear after the emoji font. However, 508*d57664e9SAndroid Build Coastguard Worker # we should skip checking the text symbols font, since 509*d57664e9SAndroid Build Coastguard Worker # symbol fonts should be able to override the emoji display 510*d57664e9SAndroid Build Coastguard Worker # style when 'Zsym' is explicitly specified by the user. 511*d57664e9SAndroid Build Coastguard Worker if emoji_font_seen and (not record.scripts or 'Zsym' in record.scripts): 512*d57664e9SAndroid Build Coastguard Worker continue 513*d57664e9SAndroid Build Coastguard Worker 514*d57664e9SAndroid Build Coastguard Worker # Check default emoji-style characters 515*d57664e9SAndroid Build Coastguard Worker assert_font_supports_none_of_chars(record.font, default_emoji, name) 516*d57664e9SAndroid Build Coastguard Worker 517*d57664e9SAndroid Build Coastguard Worker # Mark default text-style characters appearing in fonts above the emoji 518*d57664e9SAndroid Build Coastguard Worker # font as seen 519*d57664e9SAndroid Build Coastguard Worker if not emoji_font_seen: 520*d57664e9SAndroid Build Coastguard Worker missing_text_chars -= set(get_best_cmap(record.font)) 521*d57664e9SAndroid Build Coastguard Worker 522*d57664e9SAndroid Build Coastguard Worker # Noto does not have monochrome glyphs for Unicode 7.0 wingdings and 523*d57664e9SAndroid Build Coastguard Worker # webdings yet. 524*d57664e9SAndroid Build Coastguard Worker missing_text_chars -= _chars_by_age['7.0'] 525*d57664e9SAndroid Build Coastguard Worker assert missing_text_chars == set(), ( 526*d57664e9SAndroid Build Coastguard Worker 'Text style version of some emoji characters are missing: ' + 527*d57664e9SAndroid Build Coastguard Worker repr(missing_text_chars)) 528*d57664e9SAndroid Build Coastguard Worker 529*d57664e9SAndroid Build Coastguard Worker 530*d57664e9SAndroid Build Coastguard Workerdef parse_unicode_seq(chars): 531*d57664e9SAndroid Build Coastguard Worker if ' ' in chars: # character sequence 532*d57664e9SAndroid Build Coastguard Worker sequence = [int(ch, 16) for ch in chars.split(' ')] 533*d57664e9SAndroid Build Coastguard Worker additions = [tuple(sequence)] 534*d57664e9SAndroid Build Coastguard Worker elif '..' in chars: # character range 535*d57664e9SAndroid Build Coastguard Worker char_start, char_end = chars.split('..') 536*d57664e9SAndroid Build Coastguard Worker char_start = int(char_start, 16) 537*d57664e9SAndroid Build Coastguard Worker char_end = int(char_end, 16) 538*d57664e9SAndroid Build Coastguard Worker additions = range(char_start, char_end+1) 539*d57664e9SAndroid Build Coastguard Worker else: # single character 540*d57664e9SAndroid Build Coastguard Worker additions = [int(chars, 16)] 541*d57664e9SAndroid Build Coastguard Worker return additions 542*d57664e9SAndroid Build Coastguard Worker 543*d57664e9SAndroid Build Coastguard Worker# Setting reverse to true returns a dictionary that maps the values to sets of 544*d57664e9SAndroid Build Coastguard Worker# characters, useful for some binary properties. Otherwise, we get a 545*d57664e9SAndroid Build Coastguard Worker# dictionary that maps characters to the property values, assuming there's only 546*d57664e9SAndroid Build Coastguard Worker# one property in the file. 547*d57664e9SAndroid Build Coastguard Workerdef parse_unicode_datafile(file_path, reverse=False): 548*d57664e9SAndroid Build Coastguard Worker if reverse: 549*d57664e9SAndroid Build Coastguard Worker output_dict = collections.defaultdict(set) 550*d57664e9SAndroid Build Coastguard Worker else: 551*d57664e9SAndroid Build Coastguard Worker output_dict = {} 552*d57664e9SAndroid Build Coastguard Worker with open(file_path) as datafile: 553*d57664e9SAndroid Build Coastguard Worker for line in datafile: 554*d57664e9SAndroid Build Coastguard Worker if '#' in line: 555*d57664e9SAndroid Build Coastguard Worker line = line[:line.index('#')] 556*d57664e9SAndroid Build Coastguard Worker line = line.strip() 557*d57664e9SAndroid Build Coastguard Worker if not line: 558*d57664e9SAndroid Build Coastguard Worker continue 559*d57664e9SAndroid Build Coastguard Worker 560*d57664e9SAndroid Build Coastguard Worker chars, prop = line.split(';')[:2] 561*d57664e9SAndroid Build Coastguard Worker chars = chars.strip() 562*d57664e9SAndroid Build Coastguard Worker prop = prop.strip() 563*d57664e9SAndroid Build Coastguard Worker 564*d57664e9SAndroid Build Coastguard Worker additions = parse_unicode_seq(chars) 565*d57664e9SAndroid Build Coastguard Worker 566*d57664e9SAndroid Build Coastguard Worker if reverse: 567*d57664e9SAndroid Build Coastguard Worker output_dict[prop].update(additions) 568*d57664e9SAndroid Build Coastguard Worker else: 569*d57664e9SAndroid Build Coastguard Worker for addition in additions: 570*d57664e9SAndroid Build Coastguard Worker assert addition not in output_dict 571*d57664e9SAndroid Build Coastguard Worker output_dict[addition] = prop 572*d57664e9SAndroid Build Coastguard Worker return output_dict 573*d57664e9SAndroid Build Coastguard Worker 574*d57664e9SAndroid Build Coastguard Workerdef parse_sequence_age(file_path): 575*d57664e9SAndroid Build Coastguard Worker VERSION_RE = re.compile(r'E([\d\.]+)') 576*d57664e9SAndroid Build Coastguard Worker output_dict = {} 577*d57664e9SAndroid Build Coastguard Worker with open(file_path) as datafile: 578*d57664e9SAndroid Build Coastguard Worker for line in datafile: 579*d57664e9SAndroid Build Coastguard Worker comment = '' 580*d57664e9SAndroid Build Coastguard Worker if '#' in line: 581*d57664e9SAndroid Build Coastguard Worker hash_pos = line.index('#') 582*d57664e9SAndroid Build Coastguard Worker comment = line[hash_pos + 1:].strip() 583*d57664e9SAndroid Build Coastguard Worker line = line[:hash_pos] 584*d57664e9SAndroid Build Coastguard Worker line = line.strip() 585*d57664e9SAndroid Build Coastguard Worker if not line: 586*d57664e9SAndroid Build Coastguard Worker continue 587*d57664e9SAndroid Build Coastguard Worker 588*d57664e9SAndroid Build Coastguard Worker chars = line[:line.index(';')].strip() 589*d57664e9SAndroid Build Coastguard Worker 590*d57664e9SAndroid Build Coastguard Worker m = VERSION_RE.match(comment) 591*d57664e9SAndroid Build Coastguard Worker assert m, 'Version not found: unknown format: %s' % line 592*d57664e9SAndroid Build Coastguard Worker version = m.group(1) 593*d57664e9SAndroid Build Coastguard Worker 594*d57664e9SAndroid Build Coastguard Worker additions = parse_unicode_seq(chars) 595*d57664e9SAndroid Build Coastguard Worker 596*d57664e9SAndroid Build Coastguard Worker for addition in additions: 597*d57664e9SAndroid Build Coastguard Worker assert addition not in output_dict 598*d57664e9SAndroid Build Coastguard Worker output_dict[addition] = version 599*d57664e9SAndroid Build Coastguard Worker return output_dict 600*d57664e9SAndroid Build Coastguard Worker 601*d57664e9SAndroid Build Coastguard Workerdef parse_emoji_variants(file_path): 602*d57664e9SAndroid Build Coastguard Worker emoji_set = set() 603*d57664e9SAndroid Build Coastguard Worker text_set = set() 604*d57664e9SAndroid Build Coastguard Worker with open(file_path) as datafile: 605*d57664e9SAndroid Build Coastguard Worker for line in datafile: 606*d57664e9SAndroid Build Coastguard Worker if '#' in line: 607*d57664e9SAndroid Build Coastguard Worker line = line[:line.index('#')] 608*d57664e9SAndroid Build Coastguard Worker line = line.strip() 609*d57664e9SAndroid Build Coastguard Worker if not line: 610*d57664e9SAndroid Build Coastguard Worker continue 611*d57664e9SAndroid Build Coastguard Worker sequence, description, _ = line.split(';') 612*d57664e9SAndroid Build Coastguard Worker sequence = sequence.strip().split(' ') 613*d57664e9SAndroid Build Coastguard Worker base = int(sequence[0], 16) 614*d57664e9SAndroid Build Coastguard Worker vs = int(sequence[1], 16) 615*d57664e9SAndroid Build Coastguard Worker description = description.strip() 616*d57664e9SAndroid Build Coastguard Worker if description == 'text style': 617*d57664e9SAndroid Build Coastguard Worker text_set.add((base, vs)) 618*d57664e9SAndroid Build Coastguard Worker elif description == 'emoji style': 619*d57664e9SAndroid Build Coastguard Worker emoji_set.add((base, vs)) 620*d57664e9SAndroid Build Coastguard Worker return text_set, emoji_set 621*d57664e9SAndroid Build Coastguard Worker 622*d57664e9SAndroid Build Coastguard Worker 623*d57664e9SAndroid Build Coastguard Workerdef parse_ucd(ucd_path): 624*d57664e9SAndroid Build Coastguard Worker global _emoji_properties, _chars_by_age, _age_by_chars 625*d57664e9SAndroid Build Coastguard Worker global _text_variation_sequences, _emoji_variation_sequences 626*d57664e9SAndroid Build Coastguard Worker global _emoji_sequences, _emoji_zwj_sequences 627*d57664e9SAndroid Build Coastguard Worker _emoji_properties = parse_unicode_datafile( 628*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'emoji-data.txt'), reverse=True) 629*d57664e9SAndroid Build Coastguard Worker emoji_properties_additions = parse_unicode_datafile( 630*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'additions', 'emoji-data.txt'), reverse=True) 631*d57664e9SAndroid Build Coastguard Worker for prop in emoji_properties_additions.keys(): 632*d57664e9SAndroid Build Coastguard Worker _emoji_properties[prop].update(emoji_properties_additions[prop]) 633*d57664e9SAndroid Build Coastguard Worker 634*d57664e9SAndroid Build Coastguard Worker _chars_by_age = parse_unicode_datafile( 635*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'DerivedAge.txt'), reverse=True) 636*d57664e9SAndroid Build Coastguard Worker _age_by_chars = parse_unicode_datafile( 637*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'DerivedAge.txt')) 638*d57664e9SAndroid Build Coastguard Worker _age_by_chars.update(parse_sequence_age( 639*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'emoji-sequences.txt'))) 640*d57664e9SAndroid Build Coastguard Worker sequences = parse_emoji_variants( 641*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'emoji-variation-sequences.txt')) 642*d57664e9SAndroid Build Coastguard Worker _text_variation_sequences, _emoji_variation_sequences = sequences 643*d57664e9SAndroid Build Coastguard Worker _emoji_sequences = parse_unicode_datafile( 644*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'emoji-sequences.txt')) 645*d57664e9SAndroid Build Coastguard Worker _emoji_sequences.update(parse_unicode_datafile( 646*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'additions', 'emoji-sequences.txt'))) 647*d57664e9SAndroid Build Coastguard Worker _emoji_zwj_sequences = parse_unicode_datafile( 648*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'emoji-zwj-sequences.txt')) 649*d57664e9SAndroid Build Coastguard Worker _emoji_zwj_sequences.update(parse_unicode_datafile( 650*d57664e9SAndroid Build Coastguard Worker path.join(ucd_path, 'additions', 'emoji-zwj-sequences.txt'))) 651*d57664e9SAndroid Build Coastguard Worker 652*d57664e9SAndroid Build Coastguard Worker exclusions = parse_unicode_datafile(path.join(ucd_path, 'additions', 'emoji-exclusions.txt')) 653*d57664e9SAndroid Build Coastguard Worker _emoji_sequences = remove_emoji_exclude(_emoji_sequences, exclusions) 654*d57664e9SAndroid Build Coastguard Worker _emoji_zwj_sequences = remove_emoji_exclude(_emoji_zwj_sequences, exclusions) 655*d57664e9SAndroid Build Coastguard Worker _emoji_variation_sequences = remove_emoji_variation_exclude(_emoji_variation_sequences, exclusions) 656*d57664e9SAndroid Build Coastguard Worker # Unicode 12.0 adds Basic_Emoji in emoji-sequences.txt. We ignore them here since we are already 657*d57664e9SAndroid Build Coastguard Worker # checking the emoji presentations with emoji-variation-sequences.txt. 658*d57664e9SAndroid Build Coastguard Worker # Please refer to http://unicode.org/reports/tr51/#def_basic_emoji_set . 659*d57664e9SAndroid Build Coastguard Worker _emoji_sequences = {k: v for k, v in _emoji_sequences.items() if not v == 'Basic_Emoji' } 660*d57664e9SAndroid Build Coastguard Worker 661*d57664e9SAndroid Build Coastguard Worker 662*d57664e9SAndroid Build Coastguard Workerdef remove_emoji_variation_exclude(source, items): 663*d57664e9SAndroid Build Coastguard Worker return source.difference(items.keys()) 664*d57664e9SAndroid Build Coastguard Worker 665*d57664e9SAndroid Build Coastguard Workerdef remove_emoji_exclude(source, items): 666*d57664e9SAndroid Build Coastguard Worker return {k: v for k, v in source.items() if k not in items} 667*d57664e9SAndroid Build Coastguard Worker 668*d57664e9SAndroid Build Coastguard Workerdef flag_sequence(territory_code): 669*d57664e9SAndroid Build Coastguard Worker return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code) 670*d57664e9SAndroid Build Coastguard Worker 671*d57664e9SAndroid Build Coastguard WorkerEQUIVALENT_FLAGS = { 672*d57664e9SAndroid Build Coastguard Worker flag_sequence('BV'): flag_sequence('NO'), 673*d57664e9SAndroid Build Coastguard Worker flag_sequence('CP'): flag_sequence('FR'), 674*d57664e9SAndroid Build Coastguard Worker flag_sequence('HM'): flag_sequence('AU'), 675*d57664e9SAndroid Build Coastguard Worker flag_sequence('SJ'): flag_sequence('NO'), 676*d57664e9SAndroid Build Coastguard Worker flag_sequence('UM'): flag_sequence('US'), 677*d57664e9SAndroid Build Coastguard Worker} 678*d57664e9SAndroid Build Coastguard Worker 679*d57664e9SAndroid Build Coastguard WorkerCOMBINING_KEYCAP = 0x20E3 680*d57664e9SAndroid Build Coastguard Worker 681*d57664e9SAndroid Build Coastguard WorkerLEGACY_ANDROID_EMOJI = { 682*d57664e9SAndroid Build Coastguard Worker 0xFE4E5: flag_sequence('JP'), 683*d57664e9SAndroid Build Coastguard Worker 0xFE4E6: flag_sequence('US'), 684*d57664e9SAndroid Build Coastguard Worker 0xFE4E7: flag_sequence('FR'), 685*d57664e9SAndroid Build Coastguard Worker 0xFE4E8: flag_sequence('DE'), 686*d57664e9SAndroid Build Coastguard Worker 0xFE4E9: flag_sequence('IT'), 687*d57664e9SAndroid Build Coastguard Worker 0xFE4EA: flag_sequence('GB'), 688*d57664e9SAndroid Build Coastguard Worker 0xFE4EB: flag_sequence('ES'), 689*d57664e9SAndroid Build Coastguard Worker 0xFE4EC: flag_sequence('RU'), 690*d57664e9SAndroid Build Coastguard Worker 0xFE4ED: flag_sequence('CN'), 691*d57664e9SAndroid Build Coastguard Worker 0xFE4EE: flag_sequence('KR'), 692*d57664e9SAndroid Build Coastguard Worker 0xFE82C: (ord('#'), COMBINING_KEYCAP), 693*d57664e9SAndroid Build Coastguard Worker 0xFE82E: (ord('1'), COMBINING_KEYCAP), 694*d57664e9SAndroid Build Coastguard Worker 0xFE82F: (ord('2'), COMBINING_KEYCAP), 695*d57664e9SAndroid Build Coastguard Worker 0xFE830: (ord('3'), COMBINING_KEYCAP), 696*d57664e9SAndroid Build Coastguard Worker 0xFE831: (ord('4'), COMBINING_KEYCAP), 697*d57664e9SAndroid Build Coastguard Worker 0xFE832: (ord('5'), COMBINING_KEYCAP), 698*d57664e9SAndroid Build Coastguard Worker 0xFE833: (ord('6'), COMBINING_KEYCAP), 699*d57664e9SAndroid Build Coastguard Worker 0xFE834: (ord('7'), COMBINING_KEYCAP), 700*d57664e9SAndroid Build Coastguard Worker 0xFE835: (ord('8'), COMBINING_KEYCAP), 701*d57664e9SAndroid Build Coastguard Worker 0xFE836: (ord('9'), COMBINING_KEYCAP), 702*d57664e9SAndroid Build Coastguard Worker 0xFE837: (ord('0'), COMBINING_KEYCAP), 703*d57664e9SAndroid Build Coastguard Worker} 704*d57664e9SAndroid Build Coastguard Worker 705*d57664e9SAndroid Build Coastguard Worker# This is used to define the emoji that should have the same glyph. 706*d57664e9SAndroid Build Coastguard Worker# i.e. previously we had gender based Kiss (0x1F48F), which had the same glyph 707*d57664e9SAndroid Build Coastguard Worker# with Kiss: Woman, Man (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468) 708*d57664e9SAndroid Build Coastguard Worker# in that case a valid row would be: 709*d57664e9SAndroid Build Coastguard Worker# (0x1F469, 0x200D, 0x2764, 0x200D, 0x1F48B, 0x200D, 0x1F468): 0x1F48F, 710*d57664e9SAndroid Build Coastguard WorkerZWJ_IDENTICALS = { 711*d57664e9SAndroid Build Coastguard Worker} 712*d57664e9SAndroid Build Coastguard Worker 713*d57664e9SAndroid Build Coastguard WorkerSAME_FLAG_MAPPINGS = [ 714*d57664e9SAndroid Build Coastguard Worker # Diego Garcia and British Indian Ocean Territory 715*d57664e9SAndroid Build Coastguard Worker ((0x1F1EE, 0x1F1F4), (0x1F1E9, 0x1F1EC)), 716*d57664e9SAndroid Build Coastguard Worker # St. Martin and France 717*d57664e9SAndroid Build Coastguard Worker ((0x1F1F2, 0x1F1EB), (0x1F1EB, 0x1F1F7)), 718*d57664e9SAndroid Build Coastguard Worker # Spain and Ceuta & Melilla 719*d57664e9SAndroid Build Coastguard Worker ((0x1F1EA, 0x1F1F8), (0x1F1EA, 0x1F1E6)), 720*d57664e9SAndroid Build Coastguard Worker] 721*d57664e9SAndroid Build Coastguard Worker 722*d57664e9SAndroid Build Coastguard WorkerZWJ = 0x200D 723*d57664e9SAndroid Build Coastguard Worker 724*d57664e9SAndroid Build Coastguard WorkerEMPTY_FLAG_SEQUENCE = (0x1F3F4, 0xE007F) 725*d57664e9SAndroid Build Coastguard Worker 726*d57664e9SAndroid Build Coastguard Workerdef is_fitzpatrick_modifier(cp): 727*d57664e9SAndroid Build Coastguard Worker return 0x1F3FB <= cp <= 0x1F3FF 728*d57664e9SAndroid Build Coastguard Worker 729*d57664e9SAndroid Build Coastguard Worker 730*d57664e9SAndroid Build Coastguard Workerdef reverse_emoji(seq): 731*d57664e9SAndroid Build Coastguard Worker rev = list(reversed(seq)) 732*d57664e9SAndroid Build Coastguard Worker # if there are fitzpatrick modifiers in the sequence, keep them after 733*d57664e9SAndroid Build Coastguard Worker # the emoji they modify 734*d57664e9SAndroid Build Coastguard Worker for i in range(1, len(rev)): 735*d57664e9SAndroid Build Coastguard Worker if is_fitzpatrick_modifier(rev[i-1]): 736*d57664e9SAndroid Build Coastguard Worker rev[i], rev[i-1] = rev[i-1], rev[i] 737*d57664e9SAndroid Build Coastguard Worker return tuple(rev) 738*d57664e9SAndroid Build Coastguard Worker 739*d57664e9SAndroid Build Coastguard Worker 740*d57664e9SAndroid Build Coastguard Workerdef compute_expected_emoji(): 741*d57664e9SAndroid Build Coastguard Worker equivalent_emoji = {} 742*d57664e9SAndroid Build Coastguard Worker sequence_pieces = set() 743*d57664e9SAndroid Build Coastguard Worker all_sequences = set() 744*d57664e9SAndroid Build Coastguard Worker all_sequences.update(_emoji_variation_sequences) 745*d57664e9SAndroid Build Coastguard Worker 746*d57664e9SAndroid Build Coastguard Worker # add zwj sequences not in the current emoji-zwj-sequences.txt 747*d57664e9SAndroid Build Coastguard Worker adjusted_emoji_zwj_sequences = dict(_emoji_zwj_sequences) 748*d57664e9SAndroid Build Coastguard Worker adjusted_emoji_zwj_sequences.update(_emoji_zwj_sequences) 749*d57664e9SAndroid Build Coastguard Worker 750*d57664e9SAndroid Build Coastguard Worker # Add empty flag tag sequence that is supported as fallback 751*d57664e9SAndroid Build Coastguard Worker _emoji_sequences[EMPTY_FLAG_SEQUENCE] = 'Emoji_Tag_Sequence' 752*d57664e9SAndroid Build Coastguard Worker 753*d57664e9SAndroid Build Coastguard Worker for sequence in _emoji_sequences.keys(): 754*d57664e9SAndroid Build Coastguard Worker sequence = tuple(ch for ch in sequence if ch != EMOJI_VS) 755*d57664e9SAndroid Build Coastguard Worker all_sequences.add(sequence) 756*d57664e9SAndroid Build Coastguard Worker sequence_pieces.update(sequence) 757*d57664e9SAndroid Build Coastguard Worker 758*d57664e9SAndroid Build Coastguard Worker for sequence in adjusted_emoji_zwj_sequences.keys(): 759*d57664e9SAndroid Build Coastguard Worker sequence = tuple(ch for ch in sequence if ch != EMOJI_VS) 760*d57664e9SAndroid Build Coastguard Worker all_sequences.add(sequence) 761*d57664e9SAndroid Build Coastguard Worker sequence_pieces.update(sequence) 762*d57664e9SAndroid Build Coastguard Worker 763*d57664e9SAndroid Build Coastguard Worker for first, second in SAME_FLAG_MAPPINGS: 764*d57664e9SAndroid Build Coastguard Worker equivalent_emoji[first] = second 765*d57664e9SAndroid Build Coastguard Worker 766*d57664e9SAndroid Build Coastguard Worker # Add all tag characters used in flags 767*d57664e9SAndroid Build Coastguard Worker sequence_pieces.update(range(0xE0030, 0xE0039 + 1)) 768*d57664e9SAndroid Build Coastguard Worker sequence_pieces.update(range(0xE0061, 0xE007A + 1)) 769*d57664e9SAndroid Build Coastguard Worker 770*d57664e9SAndroid Build Coastguard Worker all_emoji = ( 771*d57664e9SAndroid Build Coastguard Worker _emoji_properties['Emoji'] | 772*d57664e9SAndroid Build Coastguard Worker all_sequences | 773*d57664e9SAndroid Build Coastguard Worker sequence_pieces | 774*d57664e9SAndroid Build Coastguard Worker set(LEGACY_ANDROID_EMOJI.keys())) 775*d57664e9SAndroid Build Coastguard Worker default_emoji = ( 776*d57664e9SAndroid Build Coastguard Worker _emoji_properties['Emoji_Presentation'] | 777*d57664e9SAndroid Build Coastguard Worker all_sequences | 778*d57664e9SAndroid Build Coastguard Worker set(LEGACY_ANDROID_EMOJI.keys())) 779*d57664e9SAndroid Build Coastguard Worker 780*d57664e9SAndroid Build Coastguard Worker equivalent_emoji.update(EQUIVALENT_FLAGS) 781*d57664e9SAndroid Build Coastguard Worker equivalent_emoji.update(LEGACY_ANDROID_EMOJI) 782*d57664e9SAndroid Build Coastguard Worker equivalent_emoji.update(ZWJ_IDENTICALS) 783*d57664e9SAndroid Build Coastguard Worker 784*d57664e9SAndroid Build Coastguard Worker for seq in _emoji_variation_sequences: 785*d57664e9SAndroid Build Coastguard Worker equivalent_emoji[seq] = seq[0] 786*d57664e9SAndroid Build Coastguard Worker 787*d57664e9SAndroid Build Coastguard Worker return all_emoji, default_emoji, equivalent_emoji 788*d57664e9SAndroid Build Coastguard Worker 789*d57664e9SAndroid Build Coastguard Worker 790*d57664e9SAndroid Build Coastguard Workerdef check_compact_only_fallback(): 791*d57664e9SAndroid Build Coastguard Worker for name, fallback_chain in _fallback_chains.items(): 792*d57664e9SAndroid Build Coastguard Worker for record in fallback_chain: 793*d57664e9SAndroid Build Coastguard Worker if record.variant == 'compact': 794*d57664e9SAndroid Build Coastguard Worker same_script_elegants = [x for x in fallback_chain 795*d57664e9SAndroid Build Coastguard Worker if x.scripts == record.scripts and x.variant == 'elegant'] 796*d57664e9SAndroid Build Coastguard Worker assert same_script_elegants, ( 797*d57664e9SAndroid Build Coastguard Worker '%s must be in elegant of %s as fallback of "%s" too' % ( 798*d57664e9SAndroid Build Coastguard Worker record.font, record.scripts, record.fallback_for),) 799*d57664e9SAndroid Build Coastguard Worker 800*d57664e9SAndroid Build Coastguard Worker 801*d57664e9SAndroid Build Coastguard Workerdef check_vertical_metrics(): 802*d57664e9SAndroid Build Coastguard Worker for record in _all_fonts: 803*d57664e9SAndroid Build Coastguard Worker if record.name in ['sans-serif', 'sans-serif-condensed']: 804*d57664e9SAndroid Build Coastguard Worker font = open_font(record.font) 805*d57664e9SAndroid Build Coastguard Worker assert font['head'].yMax == 2163 and font['head'].yMin == -555, ( 806*d57664e9SAndroid Build Coastguard Worker 'yMax and yMin of %s do not match expected values.' % ( 807*d57664e9SAndroid Build Coastguard Worker record.font,)) 808*d57664e9SAndroid Build Coastguard Worker 809*d57664e9SAndroid Build Coastguard Worker if record.name in ['sans-serif', 'sans-serif-condensed', 810*d57664e9SAndroid Build Coastguard Worker 'serif', 'monospace']: 811*d57664e9SAndroid Build Coastguard Worker font = open_font(record.font) 812*d57664e9SAndroid Build Coastguard Worker assert (font['hhea'].ascent == 1900 and 813*d57664e9SAndroid Build Coastguard Worker font['hhea'].descent == -500), ( 814*d57664e9SAndroid Build Coastguard Worker 'ascent and descent of %s do not match expected ' 815*d57664e9SAndroid Build Coastguard Worker 'values.' % (record.font,)) 816*d57664e9SAndroid Build Coastguard Worker 817*d57664e9SAndroid Build Coastguard Worker 818*d57664e9SAndroid Build Coastguard Workerdef check_cjk_punctuation(): 819*d57664e9SAndroid Build Coastguard Worker cjk_scripts = {'Hans', 'Hant', 'Jpan', 'Kore'} 820*d57664e9SAndroid Build Coastguard Worker cjk_punctuation = range(0x3000, 0x301F + 1) 821*d57664e9SAndroid Build Coastguard Worker for name, fallback_chain in _fallback_chains.items(): 822*d57664e9SAndroid Build Coastguard Worker for record in fallback_chain: 823*d57664e9SAndroid Build Coastguard Worker if record.scripts.intersection(cjk_scripts): 824*d57664e9SAndroid Build Coastguard Worker # CJK font seen. Stop checking the rest of the fonts. 825*d57664e9SAndroid Build Coastguard Worker break 826*d57664e9SAndroid Build Coastguard Worker assert_font_supports_none_of_chars(record.font, cjk_punctuation, name) 827*d57664e9SAndroid Build Coastguard Worker 828*d57664e9SAndroid Build Coastguard Workerdef main(): 829*d57664e9SAndroid Build Coastguard Worker global _fonts_dir 830*d57664e9SAndroid Build Coastguard Worker target_out = sys.argv[1] 831*d57664e9SAndroid Build Coastguard Worker _fonts_dir = path.join(target_out, 'fonts') 832*d57664e9SAndroid Build Coastguard Worker 833*d57664e9SAndroid Build Coastguard Worker fonts_xml_path = path.join(target_out, 'etc', 'font_fallback.xml') 834*d57664e9SAndroid Build Coastguard Worker 835*d57664e9SAndroid Build Coastguard Worker parse_fonts_xml(fonts_xml_path) 836*d57664e9SAndroid Build Coastguard Worker 837*d57664e9SAndroid Build Coastguard Worker check_compact_only_fallback() 838*d57664e9SAndroid Build Coastguard Worker 839*d57664e9SAndroid Build Coastguard Worker check_vertical_metrics() 840*d57664e9SAndroid Build Coastguard Worker 841*d57664e9SAndroid Build Coastguard Worker hyphens_dir = path.join(target_out, 'usr', 'hyphen-data') 842*d57664e9SAndroid Build Coastguard Worker check_hyphens(hyphens_dir) 843*d57664e9SAndroid Build Coastguard Worker 844*d57664e9SAndroid Build Coastguard Worker check_cjk_punctuation() 845*d57664e9SAndroid Build Coastguard Worker 846*d57664e9SAndroid Build Coastguard Worker check_emoji = sys.argv[2] 847*d57664e9SAndroid Build Coastguard Worker if check_emoji == 'true': 848*d57664e9SAndroid Build Coastguard Worker ucd_path = sys.argv[3] 849*d57664e9SAndroid Build Coastguard Worker parse_ucd(ucd_path) 850*d57664e9SAndroid Build Coastguard Worker all_emoji, default_emoji, equivalent_emoji = compute_expected_emoji() 851*d57664e9SAndroid Build Coastguard Worker check_emoji_not_compat(all_emoji, equivalent_emoji) 852*d57664e9SAndroid Build Coastguard Worker check_emoji_coverage(all_emoji, equivalent_emoji) 853*d57664e9SAndroid Build Coastguard Worker check_emoji_defaults(default_emoji) 854*d57664e9SAndroid Build Coastguard Worker 855*d57664e9SAndroid Build Coastguard Worker 856*d57664e9SAndroid Build Coastguard Workerif __name__ == '__main__': 857*d57664e9SAndroid Build Coastguard Worker main() 858