1*e5825d3bSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*e5825d3bSAndroid Build Coastguard Worker# 3*e5825d3bSAndroid Build Coastguard Worker# Copyright (C) 2017 The Android Open Source Project 4*e5825d3bSAndroid Build Coastguard Worker# 5*e5825d3bSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*e5825d3bSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*e5825d3bSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*e5825d3bSAndroid Build Coastguard Worker# 9*e5825d3bSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*e5825d3bSAndroid Build Coastguard Worker# 11*e5825d3bSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*e5825d3bSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*e5825d3bSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*e5825d3bSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*e5825d3bSAndroid Build Coastguard Worker# limitations under the License. 16*e5825d3bSAndroid Build Coastguard Worker 17*e5825d3bSAndroid Build Coastguard Worker""" 18*e5825d3bSAndroid Build Coastguard WorkerCreates the EmojiCompat font with the metadata. Metadata is embedded in FlatBuffers binary format 19*e5825d3bSAndroid Build Coastguard Workerunder a meta tag with name 'Emji'. 20*e5825d3bSAndroid Build Coastguard Worker 21*e5825d3bSAndroid Build Coastguard WorkerIn order to create the final font the followings are used as inputs: 22*e5825d3bSAndroid Build Coastguard Worker 23*e5825d3bSAndroid Build Coastguard Worker- NotoColorEmoji.ttf: Emoji font in the Android framework. Currently at 24*e5825d3bSAndroid Build Coastguard Workerexternal/noto-fonts/emoji/NotoColorEmoji.ttf 25*e5825d3bSAndroid Build Coastguard Worker 26*e5825d3bSAndroid Build Coastguard Worker- Unicode files: Unicode files that are in the framework, and lists information about all the 27*e5825d3bSAndroid Build Coastguard Workeremojis. These files are emoji-data.txt, emoji-sequences.txt, emoji-zwj-sequences.txt, 28*e5825d3bSAndroid Build Coastguard Workerand emoji-variation-sequences.txt. Currently at external/unicode/. 29*e5825d3bSAndroid Build Coastguard Worker 30*e5825d3bSAndroid Build Coastguard Worker- additions/emoji-zwj-sequences.txt: Includes emojis that are not defined in Unicode files, but are 31*e5825d3bSAndroid Build Coastguard Workerin the Android font. Resides in framework and currently under external/unicode/. 32*e5825d3bSAndroid Build Coastguard Worker 33*e5825d3bSAndroid Build Coastguard Worker- data/emoji_metadata.txt: The file that includes the id, codepoints, the first 34*e5825d3bSAndroid Build Coastguard WorkerAndroid OS version that the emoji was added (sdkAdded), and finally the first EmojiCompat font 35*e5825d3bSAndroid Build Coastguard Workerversion that the emoji was added (compatAdded). Updated when the script is executed. 36*e5825d3bSAndroid Build Coastguard Worker 37*e5825d3bSAndroid Build Coastguard Worker- data/emoji_metadata.fbs: The flatbuffer schema file. See http://google.github.io/flatbuffers/. 38*e5825d3bSAndroid Build Coastguard Worker 39*e5825d3bSAndroid Build Coastguard WorkerAfter execution the following files are generated if they don't exist otherwise, they are updated: 40*e5825d3bSAndroid Build Coastguard Worker- font/NotoColorEmojiCompat.ttf 41*e5825d3bSAndroid Build Coastguard Worker- supported-emojis/emojis.txt 42*e5825d3bSAndroid Build Coastguard Worker- data/emoji_metadata.txt 43*e5825d3bSAndroid Build Coastguard Worker- src/java/android/support/text/emoji/flatbuffer/* 44*e5825d3bSAndroid Build Coastguard Worker""" 45*e5825d3bSAndroid Build Coastguard Worker 46*e5825d3bSAndroid Build Coastguard Workerimport contextlib 47*e5825d3bSAndroid Build Coastguard Workerimport csv 48*e5825d3bSAndroid Build Coastguard Workerimport hashlib 49*e5825d3bSAndroid Build Coastguard Workerimport itertools 50*e5825d3bSAndroid Build Coastguard Workerimport json 51*e5825d3bSAndroid Build Coastguard Workerimport os 52*e5825d3bSAndroid Build Coastguard Workerimport re 53*e5825d3bSAndroid Build Coastguard Workerimport shutil 54*e5825d3bSAndroid Build Coastguard Workerimport subprocess 55*e5825d3bSAndroid Build Coastguard Workerimport sys 56*e5825d3bSAndroid Build Coastguard Workerimport tempfile 57*e5825d3bSAndroid Build Coastguard Workerfrom fontTools import ttLib 58*e5825d3bSAndroid Build Coastguard Workerfrom fontTools.ttLib.tables import otTables 59*e5825d3bSAndroid Build Coastguard Workerfrom nototools import font_data 60*e5825d3bSAndroid Build Coastguard Worker 61*e5825d3bSAndroid Build Coastguard Worker########### UPDATE OR CHECK WHEN A NEW FONT IS BEING GENERATED ########### 62*e5825d3bSAndroid Build Coastguard Worker# Last Android SDK Version 63*e5825d3bSAndroid Build Coastguard WorkerSDK_VERSION = 31 64*e5825d3bSAndroid Build Coastguard Worker# metadata version that will be embedded into font. If there are updates to the font that would 65*e5825d3bSAndroid Build Coastguard Worker# cause data/emoji_metadata.txt to change, this integer number should be incremented. This number 66*e5825d3bSAndroid Build Coastguard Worker# defines in which EmojiCompat metadata version the emoji is added to the font. 67*e5825d3bSAndroid Build Coastguard WorkerMETADATA_VERSION = 8 68*e5825d3bSAndroid Build Coastguard Worker 69*e5825d3bSAndroid Build Coastguard Worker####### main directories where output files are created ####### 70*e5825d3bSAndroid Build Coastguard WorkerSCRIPT_DIR = os.path.abspath(os.path.dirname(__file__)) 71*e5825d3bSAndroid Build Coastguard WorkerFONT_DIR = os.path.join(SCRIPT_DIR, 'font') 72*e5825d3bSAndroid Build Coastguard WorkerDATA_DIR = os.path.join(SCRIPT_DIR, 'data') 73*e5825d3bSAndroid Build Coastguard WorkerSUPPORTED_EMOJIS_DIR = os.path.join(SCRIPT_DIR, 'supported-emojis') 74*e5825d3bSAndroid Build Coastguard WorkerJAVA_SRC_DIR = os.path.join('src', 'java') 75*e5825d3bSAndroid Build Coastguard Worker####### output files ####### 76*e5825d3bSAndroid Build Coastguard Worker# font file 77*e5825d3bSAndroid Build Coastguard WorkerFONT_PATH = os.path.join(FONT_DIR, 'NotoColorEmojiCompat.ttf') 78*e5825d3bSAndroid Build Coastguard Worker# emoji metadata json output file 79*e5825d3bSAndroid Build Coastguard WorkerOUTPUT_META_FILE = os.path.join(DATA_DIR, 'emoji_metadata.txt') 80*e5825d3bSAndroid Build Coastguard Worker# emojis test file 81*e5825d3bSAndroid Build Coastguard WorkerTEST_DATA_PATH = os.path.join(SUPPORTED_EMOJIS_DIR, 'emojis.txt') 82*e5825d3bSAndroid Build Coastguard Worker####### input files ####### 83*e5825d3bSAndroid Build Coastguard Worker# Unicode file names to read emoji data 84*e5825d3bSAndroid Build Coastguard WorkerEMOJI_DATA_FILE = 'emoji-data.txt' 85*e5825d3bSAndroid Build Coastguard WorkerEMOJI_SEQ_FILE = 'emoji-sequences.txt' 86*e5825d3bSAndroid Build Coastguard WorkerEMOJI_ZWJ_FILE = 'emoji-zwj-sequences.txt' 87*e5825d3bSAndroid Build Coastguard WorkerEMOJI_VARIATION_SEQ_FILE = 'emoji-variation-sequences.txt' 88*e5825d3bSAndroid Build Coastguard Worker# Android OS emoji file for emojis that are not in Unicode files 89*e5825d3bSAndroid Build Coastguard WorkerANDROID_EMOJI_ZWJ_SEQ_FILE = os.path.join('additions', 'emoji-zwj-sequences.txt') 90*e5825d3bSAndroid Build Coastguard WorkerANDROID_EMOJIS_SEQ_FILE = os.path.join('additions', 'emoji-sequences.txt') 91*e5825d3bSAndroid Build Coastguard Worker# Android OS emoji style override file. Codepoints that are rendered with emoji style by default 92*e5825d3bSAndroid Build Coastguard Worker# even though not defined so in <code>emoji-data.txt</code>. 93*e5825d3bSAndroid Build Coastguard WorkerEMOJI_STYLE_OVERRIDE_FILE = os.path.join('additions', 'emoji-data.txt') 94*e5825d3bSAndroid Build Coastguard Worker# emoji metadata file 95*e5825d3bSAndroid Build Coastguard WorkerINPUT_META_FILE = OUTPUT_META_FILE 96*e5825d3bSAndroid Build Coastguard Worker# default flatbuffer module location (if not specified by caller) 97*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_MODULE_DIR = os.path.join(SCRIPT_DIR, '..', 'emoji-compat-flatbuffers') 98*e5825d3bSAndroid Build Coastguard Worker# flatbuffer schema 99*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_SCHEMA = os.path.join(FLATBUFFER_MODULE_DIR, 'data', 'emoji_metadata.fbs') 100*e5825d3bSAndroid Build Coastguard Worker# file path for java header, it will be prepended to flatbuffer java files 101*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_HEADER = os.path.join(FLATBUFFER_MODULE_DIR, 'data', 'flatbuffer_header.txt') 102*e5825d3bSAndroid Build Coastguard Worker# temporary emoji metadata json output file 103*e5825d3bSAndroid Build Coastguard WorkerOUTPUT_JSON_FILE_NAME = 'emoji_metadata.json' 104*e5825d3bSAndroid Build Coastguard Worker# temporary binary file generated by flatbuffer 105*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_BIN = 'emoji_metadata.bin' 106*e5825d3bSAndroid Build Coastguard Worker# directory representation for flatbuffer java package 107*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_PACKAGE_PATH = os.path.join('androidx', 'text', 'emoji', 'flatbuffer', '') 108*e5825d3bSAndroid Build Coastguard Worker# temporary directory that contains flatbuffer java files 109*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_JAVA_PATH = os.path.join(FLATBUFFER_PACKAGE_PATH) 110*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_METADATA_LIST_JAVA = "MetadataList.java" 111*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_METADATA_ITEM_JAVA = "MetadataItem.java" 112*e5825d3bSAndroid Build Coastguard Worker# directory under source where flatbuffer java files will be copied into 113*e5825d3bSAndroid Build Coastguard WorkerFLATBUFFER_JAVA_TARGET = os.path.join(FLATBUFFER_MODULE_DIR, JAVA_SRC_DIR, FLATBUFFER_PACKAGE_PATH) 114*e5825d3bSAndroid Build Coastguard Worker# meta tag name used in the font to embed the emoji metadata. This value is also used in 115*e5825d3bSAndroid Build Coastguard Worker# MetadataListReader.java in order to locate the metadata location. 116*e5825d3bSAndroid Build Coastguard WorkerEMOJI_META_TAG_NAME = 'Emji' 117*e5825d3bSAndroid Build Coastguard Worker 118*e5825d3bSAndroid Build Coastguard WorkerEMOJI_STR = 'EMOJI' 119*e5825d3bSAndroid Build Coastguard WorkerEMOJI_PRESENTATION_STR = 'EMOJI_PRESENTATION' 120*e5825d3bSAndroid Build Coastguard WorkerACCEPTED_EMOJI_PROPERTIES = [EMOJI_PRESENTATION_STR, EMOJI_STR] 121*e5825d3bSAndroid Build Coastguard WorkerSTD_VARIANTS_EMOJI_STYLE = 'EMOJI STYLE' 122*e5825d3bSAndroid Build Coastguard Worker 123*e5825d3bSAndroid Build Coastguard WorkerDEFAULT_EMOJI_ID = 0xF0001 124*e5825d3bSAndroid Build Coastguard WorkerEMOJI_STYLE_VS = 0xFE0F 125*e5825d3bSAndroid Build Coastguard Worker 126*e5825d3bSAndroid Build Coastguard Worker# The reference code point to be used for filling metrics of wartermark glyph 127*e5825d3bSAndroid Build Coastguard WorkerWATERMARK_REF_CODE_POINT = 0x1F600 128*e5825d3bSAndroid Build Coastguard Worker# The code point and glyph name used for watermark. 129*e5825d3bSAndroid Build Coastguard WorkerWATERMARK_NEW_CODE_POINT = 0x10FF00 130*e5825d3bSAndroid Build Coastguard WorkerWATERMARK_NEW_GLYPH_ID = 'u10FF00' 131*e5825d3bSAndroid Build Coastguard Worker 132*e5825d3bSAndroid Build Coastguard Workerdef to_hex_str(value): 133*e5825d3bSAndroid Build Coastguard Worker """Converts given int value to hex without the 0x prefix""" 134*e5825d3bSAndroid Build Coastguard Worker return format(value, 'X') 135*e5825d3bSAndroid Build Coastguard Worker 136*e5825d3bSAndroid Build Coastguard Workerdef hex_str_to_int(string): 137*e5825d3bSAndroid Build Coastguard Worker """Convert a hex string into int""" 138*e5825d3bSAndroid Build Coastguard Worker return int(string, 16) 139*e5825d3bSAndroid Build Coastguard Worker 140*e5825d3bSAndroid Build Coastguard Workerdef codepoint_to_string(codepoints): 141*e5825d3bSAndroid Build Coastguard Worker """Converts a list of codepoints into a string separated with space.""" 142*e5825d3bSAndroid Build Coastguard Worker return ' '.join([to_hex_str(x) for x in codepoints]) 143*e5825d3bSAndroid Build Coastguard Worker 144*e5825d3bSAndroid Build Coastguard Workerdef prepend_header_to_file(file_path, header_path): 145*e5825d3bSAndroid Build Coastguard Worker """Prepends the header to the file. Used to update flatbuffer java files with header, comments 146*e5825d3bSAndroid Build Coastguard Worker and annotations.""" 147*e5825d3bSAndroid Build Coastguard Worker with open(file_path, "r+") as original_file: 148*e5825d3bSAndroid Build Coastguard Worker with open(header_path, "r") as copyright_file: 149*e5825d3bSAndroid Build Coastguard Worker original_content = original_file.read() 150*e5825d3bSAndroid Build Coastguard Worker original_file.seek(0) 151*e5825d3bSAndroid Build Coastguard Worker original_file.write(copyright_file.read() + "\n" + original_content) 152*e5825d3bSAndroid Build Coastguard Worker 153*e5825d3bSAndroid Build Coastguard Workerdef is_ri(codepoint): 154*e5825d3bSAndroid Build Coastguard Worker return 0x1F1E6 <= codepoint and codepoint <= 0x1F1FF 155*e5825d3bSAndroid Build Coastguard Worker 156*e5825d3bSAndroid Build Coastguard Workerdef is_flag_seq(codepoints): 157*e5825d3bSAndroid Build Coastguard Worker return all(is_ri(x) for x in codepoints) 158*e5825d3bSAndroid Build Coastguard Worker 159*e5825d3bSAndroid Build Coastguard Worker 160*e5825d3bSAndroid Build Coastguard Workerdef update_flatbuffer_java_files(flatbuffer_java_dir, header_dir, target_dir): 161*e5825d3bSAndroid Build Coastguard Worker """Prepends headers to flatbuffer java files and copies to the final destination""" 162*e5825d3bSAndroid Build Coastguard Worker tmp_metadata_list = flatbuffer_java_dir + FLATBUFFER_METADATA_LIST_JAVA 163*e5825d3bSAndroid Build Coastguard Worker tmp_metadata_item = flatbuffer_java_dir + FLATBUFFER_METADATA_ITEM_JAVA 164*e5825d3bSAndroid Build Coastguard Worker prepend_header_to_file(tmp_metadata_list, header_dir) 165*e5825d3bSAndroid Build Coastguard Worker prepend_header_to_file(tmp_metadata_item, header_dir) 166*e5825d3bSAndroid Build Coastguard Worker 167*e5825d3bSAndroid Build Coastguard Worker if not os.path.exists(target_dir): 168*e5825d3bSAndroid Build Coastguard Worker os.makedirs(target_dir) 169*e5825d3bSAndroid Build Coastguard Worker 170*e5825d3bSAndroid Build Coastguard Worker shutil.copy(tmp_metadata_list, os.path.join(target_dir, FLATBUFFER_METADATA_LIST_JAVA)) 171*e5825d3bSAndroid Build Coastguard Worker shutil.copy(tmp_metadata_item, os.path.join(target_dir, FLATBUFFER_METADATA_ITEM_JAVA)) 172*e5825d3bSAndroid Build Coastguard Worker 173*e5825d3bSAndroid Build Coastguard Workerdef create_test_data(unicode_path): 174*e5825d3bSAndroid Build Coastguard Worker """Read all the emojis in the unicode files and update the test file""" 175*e5825d3bSAndroid Build Coastguard Worker lines = read_emoji_lines(os.path.join(unicode_path, EMOJI_ZWJ_FILE)) 176*e5825d3bSAndroid Build Coastguard Worker lines += read_emoji_lines(os.path.join(unicode_path, EMOJI_SEQ_FILE)) 177*e5825d3bSAndroid Build Coastguard Worker 178*e5825d3bSAndroid Build Coastguard Worker lines += read_emoji_lines(os.path.join(unicode_path, ANDROID_EMOJI_ZWJ_SEQ_FILE), optional=True) 179*e5825d3bSAndroid Build Coastguard Worker lines += read_emoji_lines(os.path.join(unicode_path, ANDROID_EMOJIS_SEQ_FILE), optional=True) 180*e5825d3bSAndroid Build Coastguard Worker 181*e5825d3bSAndroid Build Coastguard Worker # standardized variants contains a huge list of sequences, only read the ones that are emojis 182*e5825d3bSAndroid Build Coastguard Worker # and also the ones with FE0F (emoji style) 183*e5825d3bSAndroid Build Coastguard Worker standardized_variants_lines = read_emoji_lines( 184*e5825d3bSAndroid Build Coastguard Worker os.path.join(unicode_path, EMOJI_VARIATION_SEQ_FILE)) 185*e5825d3bSAndroid Build Coastguard Worker for line in standardized_variants_lines: 186*e5825d3bSAndroid Build Coastguard Worker if STD_VARIANTS_EMOJI_STYLE in line: 187*e5825d3bSAndroid Build Coastguard Worker lines.append(line) 188*e5825d3bSAndroid Build Coastguard Worker 189*e5825d3bSAndroid Build Coastguard Worker emojis_set = set() 190*e5825d3bSAndroid Build Coastguard Worker for line in lines: 191*e5825d3bSAndroid Build Coastguard Worker # In unicode 12.0, "emoji-sequences.txt" contains "Basic_Emoji" session. We ignore them 192*e5825d3bSAndroid Build Coastguard Worker # here since we are already checking the emoji presentations with 193*e5825d3bSAndroid Build Coastguard Worker # emoji-variation-sequences.txt. 194*e5825d3bSAndroid Build Coastguard Worker if "BASIC_EMOJI" in line: 195*e5825d3bSAndroid Build Coastguard Worker continue 196*e5825d3bSAndroid Build Coastguard Worker codepoints = [hex_str_to_int(x) for x in line.split(';')[0].strip().split(' ')] 197*e5825d3bSAndroid Build Coastguard Worker emojis_set.add(codepoint_to_string(codepoints).upper()) 198*e5825d3bSAndroid Build Coastguard Worker 199*e5825d3bSAndroid Build Coastguard Worker emoji_data_lines = read_emoji_lines(os.path.join(unicode_path, EMOJI_DATA_FILE)) 200*e5825d3bSAndroid Build Coastguard Worker for line in emoji_data_lines: 201*e5825d3bSAndroid Build Coastguard Worker codepoints_range, emoji_property = codepoints_and_emoji_prop(line) 202*e5825d3bSAndroid Build Coastguard Worker if not emoji_property in ACCEPTED_EMOJI_PROPERTIES: 203*e5825d3bSAndroid Build Coastguard Worker continue 204*e5825d3bSAndroid Build Coastguard Worker is_emoji_style = emoji_property == EMOJI_PRESENTATION_STR 205*e5825d3bSAndroid Build Coastguard Worker if is_emoji_style: 206*e5825d3bSAndroid Build Coastguard Worker codepoints = [to_hex_str(x) for x in 207*e5825d3bSAndroid Build Coastguard Worker codepoints_for_emojirange(codepoints_range)] 208*e5825d3bSAndroid Build Coastguard Worker emojis_set.update(codepoints) 209*e5825d3bSAndroid Build Coastguard Worker 210*e5825d3bSAndroid Build Coastguard Worker emoji_style_exceptions = get_emoji_style_exceptions(unicode_path) 211*e5825d3bSAndroid Build Coastguard Worker # finally add the android default emoji exceptions 212*e5825d3bSAndroid Build Coastguard Worker emojis_set.update([to_hex_str(x) for x in emoji_style_exceptions]) 213*e5825d3bSAndroid Build Coastguard Worker 214*e5825d3bSAndroid Build Coastguard Worker emojis_list = list(emojis_set) 215*e5825d3bSAndroid Build Coastguard Worker emojis_list.sort() 216*e5825d3bSAndroid Build Coastguard Worker with open(TEST_DATA_PATH, "w") as test_file: 217*e5825d3bSAndroid Build Coastguard Worker for line in emojis_list: 218*e5825d3bSAndroid Build Coastguard Worker test_file.write("%s\n" % line) 219*e5825d3bSAndroid Build Coastguard Worker 220*e5825d3bSAndroid Build Coastguard Workerclass _EmojiData(object): 221*e5825d3bSAndroid Build Coastguard Worker """Holds the information about a single emoji.""" 222*e5825d3bSAndroid Build Coastguard Worker 223*e5825d3bSAndroid Build Coastguard Worker def __init__(self, codepoints, is_emoji_style): 224*e5825d3bSAndroid Build Coastguard Worker self.codepoints = codepoints 225*e5825d3bSAndroid Build Coastguard Worker self.emoji_style = is_emoji_style 226*e5825d3bSAndroid Build Coastguard Worker self.emoji_id = 0 227*e5825d3bSAndroid Build Coastguard Worker self.width = 0 228*e5825d3bSAndroid Build Coastguard Worker self.height = 0 229*e5825d3bSAndroid Build Coastguard Worker self.sdk_added = SDK_VERSION 230*e5825d3bSAndroid Build Coastguard Worker self.compat_added = METADATA_VERSION 231*e5825d3bSAndroid Build Coastguard Worker 232*e5825d3bSAndroid Build Coastguard Worker def update_metrics(self, metrics): 233*e5825d3bSAndroid Build Coastguard Worker """Updates width/height instance variables with the values given in metrics dictionary. 234*e5825d3bSAndroid Build Coastguard Worker :param metrics: a dictionary object that has width and height values. 235*e5825d3bSAndroid Build Coastguard Worker """ 236*e5825d3bSAndroid Build Coastguard Worker self.width = metrics.width 237*e5825d3bSAndroid Build Coastguard Worker self.height = metrics.height 238*e5825d3bSAndroid Build Coastguard Worker 239*e5825d3bSAndroid Build Coastguard Worker def __repr__(self): 240*e5825d3bSAndroid Build Coastguard Worker return '<EmojiData {0} - {1}>'.format(self.emoji_style, 241*e5825d3bSAndroid Build Coastguard Worker codepoint_to_string(self.codepoints)) 242*e5825d3bSAndroid Build Coastguard Worker 243*e5825d3bSAndroid Build Coastguard Worker def create_json_element(self): 244*e5825d3bSAndroid Build Coastguard Worker """Creates the json representation of EmojiData.""" 245*e5825d3bSAndroid Build Coastguard Worker json_element = {} 246*e5825d3bSAndroid Build Coastguard Worker json_element['id'] = self.emoji_id 247*e5825d3bSAndroid Build Coastguard Worker json_element['emojiStyle'] = self.emoji_style 248*e5825d3bSAndroid Build Coastguard Worker json_element['sdkAdded'] = self.sdk_added 249*e5825d3bSAndroid Build Coastguard Worker json_element['compatAdded'] = self.compat_added 250*e5825d3bSAndroid Build Coastguard Worker json_element['width'] = self.width 251*e5825d3bSAndroid Build Coastguard Worker json_element['height'] = self.height 252*e5825d3bSAndroid Build Coastguard Worker json_element['codepoints'] = self.codepoints 253*e5825d3bSAndroid Build Coastguard Worker return json_element 254*e5825d3bSAndroid Build Coastguard Worker 255*e5825d3bSAndroid Build Coastguard Worker def create_txt_row(self): 256*e5825d3bSAndroid Build Coastguard Worker """Creates array of values for CSV of EmojiData.""" 257*e5825d3bSAndroid Build Coastguard Worker row = [to_hex_str(self.emoji_id), self.sdk_added, self.compat_added] 258*e5825d3bSAndroid Build Coastguard Worker row += [to_hex_str(x) for x in self.codepoints] 259*e5825d3bSAndroid Build Coastguard Worker return row 260*e5825d3bSAndroid Build Coastguard Worker 261*e5825d3bSAndroid Build Coastguard Worker def update(self, emoji_id, sdk_added, compat_added): 262*e5825d3bSAndroid Build Coastguard Worker """Updates current EmojiData with the values in a json element""" 263*e5825d3bSAndroid Build Coastguard Worker self.emoji_id = emoji_id 264*e5825d3bSAndroid Build Coastguard Worker self.sdk_added = sdk_added 265*e5825d3bSAndroid Build Coastguard Worker self.compat_added = compat_added 266*e5825d3bSAndroid Build Coastguard Worker 267*e5825d3bSAndroid Build Coastguard Worker 268*e5825d3bSAndroid Build Coastguard Workerdef read_emoji_lines(file_path, optional=False): 269*e5825d3bSAndroid Build Coastguard Worker """Read all lines in an unicode emoji file into a list of uppercase strings. Ignore the empty 270*e5825d3bSAndroid Build Coastguard Worker lines and comments 271*e5825d3bSAndroid Build Coastguard Worker :param file_path: unicode emoji file path 272*e5825d3bSAndroid Build Coastguard Worker :param optional: if True no exception is raised when the file cannot be read 273*e5825d3bSAndroid Build Coastguard Worker :return: list of uppercase strings 274*e5825d3bSAndroid Build Coastguard Worker """ 275*e5825d3bSAndroid Build Coastguard Worker result = [] 276*e5825d3bSAndroid Build Coastguard Worker try: 277*e5825d3bSAndroid Build Coastguard Worker with open(file_path) as file_stream: 278*e5825d3bSAndroid Build Coastguard Worker for line in file_stream: 279*e5825d3bSAndroid Build Coastguard Worker line = line.strip() 280*e5825d3bSAndroid Build Coastguard Worker if line and not line.startswith('#'): 281*e5825d3bSAndroid Build Coastguard Worker result.append(line.upper()) 282*e5825d3bSAndroid Build Coastguard Worker except IOError: 283*e5825d3bSAndroid Build Coastguard Worker if optional: 284*e5825d3bSAndroid Build Coastguard Worker pass 285*e5825d3bSAndroid Build Coastguard Worker else: 286*e5825d3bSAndroid Build Coastguard Worker raise 287*e5825d3bSAndroid Build Coastguard Worker 288*e5825d3bSAndroid Build Coastguard Worker return result 289*e5825d3bSAndroid Build Coastguard Worker 290*e5825d3bSAndroid Build Coastguard Workerdef get_emoji_style_exceptions(unicode_path): 291*e5825d3bSAndroid Build Coastguard Worker """Read EMOJI_STYLE_OVERRIDE_FILE and return the codepoints as integers""" 292*e5825d3bSAndroid Build Coastguard Worker lines = read_emoji_lines(os.path.join(unicode_path, EMOJI_STYLE_OVERRIDE_FILE)) 293*e5825d3bSAndroid Build Coastguard Worker exceptions = [] 294*e5825d3bSAndroid Build Coastguard Worker for line in lines: 295*e5825d3bSAndroid Build Coastguard Worker codepoint = hex_str_to_int(codepoints_and_emoji_prop(line)[0]) 296*e5825d3bSAndroid Build Coastguard Worker exceptions.append(codepoint) 297*e5825d3bSAndroid Build Coastguard Worker return exceptions 298*e5825d3bSAndroid Build Coastguard Worker 299*e5825d3bSAndroid Build Coastguard Workerdef codepoints_for_emojirange(codepoints_range): 300*e5825d3bSAndroid Build Coastguard Worker """ Return codepoints given in emoji files. Expand the codepoints that are given as a range 301*e5825d3bSAndroid Build Coastguard Worker such as XYZ ... UVT 302*e5825d3bSAndroid Build Coastguard Worker """ 303*e5825d3bSAndroid Build Coastguard Worker codepoints = [] 304*e5825d3bSAndroid Build Coastguard Worker if '..' in codepoints_range: 305*e5825d3bSAndroid Build Coastguard Worker range_start, range_end = codepoints_range.split('..') 306*e5825d3bSAndroid Build Coastguard Worker codepoints_range = range(hex_str_to_int(range_start), 307*e5825d3bSAndroid Build Coastguard Worker hex_str_to_int(range_end) + 1) 308*e5825d3bSAndroid Build Coastguard Worker codepoints.extend(codepoints_range) 309*e5825d3bSAndroid Build Coastguard Worker else: 310*e5825d3bSAndroid Build Coastguard Worker codepoints.append(hex_str_to_int(codepoints_range)) 311*e5825d3bSAndroid Build Coastguard Worker return codepoints 312*e5825d3bSAndroid Build Coastguard Worker 313*e5825d3bSAndroid Build Coastguard Workerdef codepoints_and_emoji_prop(line): 314*e5825d3bSAndroid Build Coastguard Worker """For a given emoji file line, return codepoints and emoji property in the line. 315*e5825d3bSAndroid Build Coastguard Worker 1F93C..1F93E ; [Emoji|Emoji_Presentation|Emoji_Modifier_Base|Emoji_Component 316*e5825d3bSAndroid Build Coastguard Worker |Extended_Pictographic] # [...]""" 317*e5825d3bSAndroid Build Coastguard Worker line = line.strip() 318*e5825d3bSAndroid Build Coastguard Worker if '#' in line: 319*e5825d3bSAndroid Build Coastguard Worker line = line[:line.index('#')] 320*e5825d3bSAndroid Build Coastguard Worker else: 321*e5825d3bSAndroid Build Coastguard Worker raise ValueError("Line is expected to have # in it") 322*e5825d3bSAndroid Build Coastguard Worker line = line.split(';') 323*e5825d3bSAndroid Build Coastguard Worker codepoints_range = line[0].strip() 324*e5825d3bSAndroid Build Coastguard Worker emoji_property = line[1].strip() 325*e5825d3bSAndroid Build Coastguard Worker 326*e5825d3bSAndroid Build Coastguard Worker return codepoints_range, emoji_property 327*e5825d3bSAndroid Build Coastguard Worker 328*e5825d3bSAndroid Build Coastguard Workerdef read_emoji_intervals(emoji_data_map, file_path, emoji_style_exceptions): 329*e5825d3bSAndroid Build Coastguard Worker """Read unicode lines of unicode emoji file in which each line describes a set of codepoint 330*e5825d3bSAndroid Build Coastguard Worker intervals. Expands the interval on a line and inserts related EmojiDatas into emoji_data_map. 331*e5825d3bSAndroid Build Coastguard Worker A line format that is expected is as follows: 332*e5825d3bSAndroid Build Coastguard Worker 1F93C..1F93E ; [Emoji|Emoji_Presentation|Emoji_Modifier_Base|Emoji_Component 333*e5825d3bSAndroid Build Coastguard Worker |Extended_Pictographic] # [...]""" 334*e5825d3bSAndroid Build Coastguard Worker lines = read_emoji_lines(file_path) 335*e5825d3bSAndroid Build Coastguard Worker 336*e5825d3bSAndroid Build Coastguard Worker for line in lines: 337*e5825d3bSAndroid Build Coastguard Worker codepoints_range, emoji_property = codepoints_and_emoji_prop(line) 338*e5825d3bSAndroid Build Coastguard Worker if not emoji_property in ACCEPTED_EMOJI_PROPERTIES: 339*e5825d3bSAndroid Build Coastguard Worker continue 340*e5825d3bSAndroid Build Coastguard Worker is_emoji_style = emoji_property == EMOJI_PRESENTATION_STR 341*e5825d3bSAndroid Build Coastguard Worker codepoints = codepoints_for_emojirange(codepoints_range) 342*e5825d3bSAndroid Build Coastguard Worker 343*e5825d3bSAndroid Build Coastguard Worker for codepoint in codepoints: 344*e5825d3bSAndroid Build Coastguard Worker key = codepoint_to_string([codepoint]) 345*e5825d3bSAndroid Build Coastguard Worker codepoint_is_emoji_style = is_emoji_style or codepoint in emoji_style_exceptions 346*e5825d3bSAndroid Build Coastguard Worker if key in emoji_data_map: 347*e5825d3bSAndroid Build Coastguard Worker # since there are multiple definitions of emojis, only update when emoji style is 348*e5825d3bSAndroid Build Coastguard Worker # True 349*e5825d3bSAndroid Build Coastguard Worker if codepoint_is_emoji_style: 350*e5825d3bSAndroid Build Coastguard Worker emoji_data_map[key].emoji_style = True 351*e5825d3bSAndroid Build Coastguard Worker else: 352*e5825d3bSAndroid Build Coastguard Worker emoji_data = _EmojiData([codepoint], codepoint_is_emoji_style) 353*e5825d3bSAndroid Build Coastguard Worker emoji_data_map[key] = emoji_data 354*e5825d3bSAndroid Build Coastguard Worker 355*e5825d3bSAndroid Build Coastguard Worker 356*e5825d3bSAndroid Build Coastguard Workerdef read_emoji_sequences(emoji_data_map, file_path, optional=False, filter=None): 357*e5825d3bSAndroid Build Coastguard Worker """Reads the content of the file which contains emoji sequences. Creates EmojiData for each 358*e5825d3bSAndroid Build Coastguard Worker line and puts into emoji_data_map.""" 359*e5825d3bSAndroid Build Coastguard Worker lines = read_emoji_lines(file_path, optional) 360*e5825d3bSAndroid Build Coastguard Worker # 1F1E6 1F1E8 ; Name ; [...] 361*e5825d3bSAndroid Build Coastguard Worker for line in lines: 362*e5825d3bSAndroid Build Coastguard Worker # In unicode 12.0, "emoji-sequences.txt" contains "Basic_Emoji" session. We ignore them 363*e5825d3bSAndroid Build Coastguard Worker # here since we are already checking the emoji presentations with 364*e5825d3bSAndroid Build Coastguard Worker # emoji-variation-sequences.txt. 365*e5825d3bSAndroid Build Coastguard Worker if "BASIC_EMOJI" in line: 366*e5825d3bSAndroid Build Coastguard Worker continue 367*e5825d3bSAndroid Build Coastguard Worker codepoints = [hex_str_to_int(x) for x in line.split(';')[0].strip().split(' ')] 368*e5825d3bSAndroid Build Coastguard Worker codepoints = [x for x in codepoints if x != EMOJI_STYLE_VS] 369*e5825d3bSAndroid Build Coastguard Worker if filter: 370*e5825d3bSAndroid Build Coastguard Worker if filter(codepoints): 371*e5825d3bSAndroid Build Coastguard Worker continue 372*e5825d3bSAndroid Build Coastguard Worker key = codepoint_to_string(codepoints) 373*e5825d3bSAndroid Build Coastguard Worker if not key in emoji_data_map: 374*e5825d3bSAndroid Build Coastguard Worker emoji_data = _EmojiData(codepoints, False) 375*e5825d3bSAndroid Build Coastguard Worker emoji_data_map[key] = emoji_data 376*e5825d3bSAndroid Build Coastguard Worker 377*e5825d3bSAndroid Build Coastguard Worker 378*e5825d3bSAndroid Build Coastguard Workerdef load_emoji_data_map(unicode_path, without_flags): 379*e5825d3bSAndroid Build Coastguard Worker """Reads the emoji data files, constructs a map of space separated codepoints to EmojiData. 380*e5825d3bSAndroid Build Coastguard Worker :return: map of space separated codepoints to EmojiData 381*e5825d3bSAndroid Build Coastguard Worker """ 382*e5825d3bSAndroid Build Coastguard Worker if without_flags: 383*e5825d3bSAndroid Build Coastguard Worker filter = lambda x: is_flag_seq(x) 384*e5825d3bSAndroid Build Coastguard Worker else: 385*e5825d3bSAndroid Build Coastguard Worker filter = None 386*e5825d3bSAndroid Build Coastguard Worker emoji_data_map = {} 387*e5825d3bSAndroid Build Coastguard Worker emoji_style_exceptions = get_emoji_style_exceptions(unicode_path) 388*e5825d3bSAndroid Build Coastguard Worker read_emoji_intervals(emoji_data_map, os.path.join(unicode_path, EMOJI_DATA_FILE), 389*e5825d3bSAndroid Build Coastguard Worker emoji_style_exceptions) 390*e5825d3bSAndroid Build Coastguard Worker read_emoji_sequences(emoji_data_map, os.path.join(unicode_path, EMOJI_ZWJ_FILE)) 391*e5825d3bSAndroid Build Coastguard Worker read_emoji_sequences(emoji_data_map, os.path.join(unicode_path, EMOJI_SEQ_FILE), filter=filter) 392*e5825d3bSAndroid Build Coastguard Worker 393*e5825d3bSAndroid Build Coastguard Worker # Add the optional ANDROID_EMOJI_ZWJ_SEQ_FILE if it exists. 394*e5825d3bSAndroid Build Coastguard Worker read_emoji_sequences(emoji_data_map, os.path.join(unicode_path, ANDROID_EMOJI_ZWJ_SEQ_FILE), 395*e5825d3bSAndroid Build Coastguard Worker optional=True) 396*e5825d3bSAndroid Build Coastguard Worker # Add the optional ANDROID_EMOJIS_SEQ_FILE if it exists. 397*e5825d3bSAndroid Build Coastguard Worker read_emoji_sequences(emoji_data_map, os.path.join(unicode_path, ANDROID_EMOJIS_SEQ_FILE), 398*e5825d3bSAndroid Build Coastguard Worker optional=True) 399*e5825d3bSAndroid Build Coastguard Worker 400*e5825d3bSAndroid Build Coastguard Worker return emoji_data_map 401*e5825d3bSAndroid Build Coastguard Worker 402*e5825d3bSAndroid Build Coastguard Worker 403*e5825d3bSAndroid Build Coastguard Workerdef load_previous_metadata(emoji_data_map): 404*e5825d3bSAndroid Build Coastguard Worker """Updates emoji data elements in emoji_data_map using the id, sdk_added and compat_added fields 405*e5825d3bSAndroid Build Coastguard Worker in emoji_metadata.txt. Returns the smallest available emoji id to use. i.e. if the largest 406*e5825d3bSAndroid Build Coastguard Worker emoji id emoji_metadata.txt is 1, function would return 2. If emoji_metadata.txt does not 407*e5825d3bSAndroid Build Coastguard Worker exist, or contains no emojis defined returns DEFAULT_EMOJI_ID""" 408*e5825d3bSAndroid Build Coastguard Worker current_emoji_id = DEFAULT_EMOJI_ID 409*e5825d3bSAndroid Build Coastguard Worker if os.path.isfile(INPUT_META_FILE): 410*e5825d3bSAndroid Build Coastguard Worker with open(INPUT_META_FILE) as csvfile: 411*e5825d3bSAndroid Build Coastguard Worker reader = csv.reader(csvfile, delimiter=' ') 412*e5825d3bSAndroid Build Coastguard Worker for row in reader: 413*e5825d3bSAndroid Build Coastguard Worker if row[0].startswith('#'): 414*e5825d3bSAndroid Build Coastguard Worker continue 415*e5825d3bSAndroid Build Coastguard Worker emoji_id = hex_str_to_int(row[0]) 416*e5825d3bSAndroid Build Coastguard Worker sdk_added = int(row[1]) 417*e5825d3bSAndroid Build Coastguard Worker compat_added = int(row[2]) 418*e5825d3bSAndroid Build Coastguard Worker key = codepoint_to_string(hex_str_to_int(x) for x in row[3:]) 419*e5825d3bSAndroid Build Coastguard Worker if key in emoji_data_map: 420*e5825d3bSAndroid Build Coastguard Worker emoji_data = emoji_data_map[key] 421*e5825d3bSAndroid Build Coastguard Worker emoji_data.update(emoji_id, sdk_added, compat_added) 422*e5825d3bSAndroid Build Coastguard Worker if emoji_data.emoji_id >= current_emoji_id: 423*e5825d3bSAndroid Build Coastguard Worker current_emoji_id = emoji_data.emoji_id + 1 424*e5825d3bSAndroid Build Coastguard Worker 425*e5825d3bSAndroid Build Coastguard Worker return current_emoji_id 426*e5825d3bSAndroid Build Coastguard Worker 427*e5825d3bSAndroid Build Coastguard Worker 428*e5825d3bSAndroid Build Coastguard Workerdef update_ttlib_orig_sort(): 429*e5825d3bSAndroid Build Coastguard Worker """Updates the ttLib tag sort with a closure that makes the meta table first.""" 430*e5825d3bSAndroid Build Coastguard Worker orig_sort = ttLib.sortedTagList 431*e5825d3bSAndroid Build Coastguard Worker 432*e5825d3bSAndroid Build Coastguard Worker def meta_first_table_sort(tag_list, table_order=None): 433*e5825d3bSAndroid Build Coastguard Worker """Sorts the tables with the original ttLib sort, then makes the meta table first.""" 434*e5825d3bSAndroid Build Coastguard Worker tag_list = orig_sort(tag_list, table_order) 435*e5825d3bSAndroid Build Coastguard Worker tag_list.remove('meta') 436*e5825d3bSAndroid Build Coastguard Worker tag_list.insert(0, 'meta') 437*e5825d3bSAndroid Build Coastguard Worker return tag_list 438*e5825d3bSAndroid Build Coastguard Worker 439*e5825d3bSAndroid Build Coastguard Worker ttLib.sortedTagList = meta_first_table_sort 440*e5825d3bSAndroid Build Coastguard Worker 441*e5825d3bSAndroid Build Coastguard Worker 442*e5825d3bSAndroid Build Coastguard Workerdef inject_meta_into_font(ttf, flatbuffer_bin_filename): 443*e5825d3bSAndroid Build Coastguard Worker """inject metadata binary into font""" 444*e5825d3bSAndroid Build Coastguard Worker if not 'meta' in ttf: 445*e5825d3bSAndroid Build Coastguard Worker ttf['meta'] = ttLib.getTableClass('meta')() 446*e5825d3bSAndroid Build Coastguard Worker meta = ttf['meta'] 447*e5825d3bSAndroid Build Coastguard Worker with open(flatbuffer_bin_filename, 'rb') as flatbuffer_bin_file: 448*e5825d3bSAndroid Build Coastguard Worker meta.data[EMOJI_META_TAG_NAME] = flatbuffer_bin_file.read() 449*e5825d3bSAndroid Build Coastguard Worker 450*e5825d3bSAndroid Build Coastguard Worker # sort meta tables for faster access 451*e5825d3bSAndroid Build Coastguard Worker update_ttlib_orig_sort() 452*e5825d3bSAndroid Build Coastguard Worker 453*e5825d3bSAndroid Build Coastguard Worker 454*e5825d3bSAndroid Build Coastguard Workerdef validate_input_files(font_path, unicode_path, flatbuffer_path): 455*e5825d3bSAndroid Build Coastguard Worker """Validate the existence of font file and the unicode files""" 456*e5825d3bSAndroid Build Coastguard Worker if not os.path.isfile(font_path): 457*e5825d3bSAndroid Build Coastguard Worker raise ValueError("Font file does not exist: " + font_path) 458*e5825d3bSAndroid Build Coastguard Worker 459*e5825d3bSAndroid Build Coastguard Worker if not os.path.isdir(unicode_path): 460*e5825d3bSAndroid Build Coastguard Worker raise ValueError( 461*e5825d3bSAndroid Build Coastguard Worker "Unicode directory does not exist or is not a directory " + unicode_path) 462*e5825d3bSAndroid Build Coastguard Worker 463*e5825d3bSAndroid Build Coastguard Worker emoji_filenames = [os.path.join(unicode_path, EMOJI_DATA_FILE), 464*e5825d3bSAndroid Build Coastguard Worker os.path.join(unicode_path, EMOJI_ZWJ_FILE), 465*e5825d3bSAndroid Build Coastguard Worker os.path.join(unicode_path, EMOJI_SEQ_FILE)] 466*e5825d3bSAndroid Build Coastguard Worker for emoji_filename in emoji_filenames: 467*e5825d3bSAndroid Build Coastguard Worker if not os.path.isfile(emoji_filename): 468*e5825d3bSAndroid Build Coastguard Worker raise ValueError("Unicode emoji data file does not exist: " + emoji_filename) 469*e5825d3bSAndroid Build Coastguard Worker 470*e5825d3bSAndroid Build Coastguard Worker if not os.path.isdir(flatbuffer_path): 471*e5825d3bSAndroid Build Coastguard Worker raise ValueError( 472*e5825d3bSAndroid Build Coastguard Worker "Flatbuffer directory does not exist or is not a directory " + flatbuffer_path) 473*e5825d3bSAndroid Build Coastguard Worker 474*e5825d3bSAndroid Build Coastguard Worker flatbuffer_filenames = [os.path.join(flatbuffer_path, FLATBUFFER_SCHEMA), 475*e5825d3bSAndroid Build Coastguard Worker os.path.join(flatbuffer_path, FLATBUFFER_HEADER)] 476*e5825d3bSAndroid Build Coastguard Worker for flatbuffer_filename in flatbuffer_filenames: 477*e5825d3bSAndroid Build Coastguard Worker if not os.path.isfile(flatbuffer_filename): 478*e5825d3bSAndroid Build Coastguard Worker raise ValueError("Flatbuffer file does not exist: " + flatbuffer_filename) 479*e5825d3bSAndroid Build Coastguard Worker 480*e5825d3bSAndroid Build Coastguard Worker 481*e5825d3bSAndroid Build Coastguard Workerdef add_file_to_sha(sha_algo, file_path): 482*e5825d3bSAndroid Build Coastguard Worker with open(file_path, 'rb') as input_file: 483*e5825d3bSAndroid Build Coastguard Worker for data in iter(lambda: input_file.read(8192), b''): 484*e5825d3bSAndroid Build Coastguard Worker sha_algo.update(data) 485*e5825d3bSAndroid Build Coastguard Worker 486*e5825d3bSAndroid Build Coastguard Workerdef create_sha_from_source_files(font_paths): 487*e5825d3bSAndroid Build Coastguard Worker """Creates a SHA from the given font files""" 488*e5825d3bSAndroid Build Coastguard Worker sha_algo = hashlib.sha256() 489*e5825d3bSAndroid Build Coastguard Worker for file_path in font_paths: 490*e5825d3bSAndroid Build Coastguard Worker add_file_to_sha(sha_algo, file_path) 491*e5825d3bSAndroid Build Coastguard Worker return sha_algo.hexdigest() 492*e5825d3bSAndroid Build Coastguard Worker 493*e5825d3bSAndroid Build Coastguard Worker 494*e5825d3bSAndroid Build Coastguard Workerclass EmojiFontCreator(object): 495*e5825d3bSAndroid Build Coastguard Worker """Creates the EmojiCompat font""" 496*e5825d3bSAndroid Build Coastguard Worker 497*e5825d3bSAndroid Build Coastguard Worker def __init__(self, font_path, unicode_path, without_flags): 498*e5825d3bSAndroid Build Coastguard Worker validate_input_files(font_path, unicode_path, FLATBUFFER_MODULE_DIR) 499*e5825d3bSAndroid Build Coastguard Worker 500*e5825d3bSAndroid Build Coastguard Worker self.font_path = font_path 501*e5825d3bSAndroid Build Coastguard Worker self.unicode_path = unicode_path 502*e5825d3bSAndroid Build Coastguard Worker self.without_flags = without_flags 503*e5825d3bSAndroid Build Coastguard Worker self.emoji_data_map = {} 504*e5825d3bSAndroid Build Coastguard Worker self.remapped_codepoints = {} 505*e5825d3bSAndroid Build Coastguard Worker self.glyph_to_image_metrics_map = {} 506*e5825d3bSAndroid Build Coastguard Worker # set default emoji id to start of Supplemental Private Use Area-A 507*e5825d3bSAndroid Build Coastguard Worker self.emoji_id = DEFAULT_EMOJI_ID 508*e5825d3bSAndroid Build Coastguard Worker 509*e5825d3bSAndroid Build Coastguard Worker def update_emoji_data(self, codepoints, glyph_name): 510*e5825d3bSAndroid Build Coastguard Worker """Updates the existing EmojiData identified with codepoints. The fields that are set are: 511*e5825d3bSAndroid Build Coastguard Worker - emoji_id (if it does not exist) 512*e5825d3bSAndroid Build Coastguard Worker - image width/height""" 513*e5825d3bSAndroid Build Coastguard Worker key = codepoint_to_string(codepoints) 514*e5825d3bSAndroid Build Coastguard Worker if key in self.emoji_data_map: 515*e5825d3bSAndroid Build Coastguard Worker # add emoji to final data 516*e5825d3bSAndroid Build Coastguard Worker emoji_data = self.emoji_data_map[key] 517*e5825d3bSAndroid Build Coastguard Worker emoji_data.update_metrics(self.glyph_to_image_metrics_map[glyph_name]) 518*e5825d3bSAndroid Build Coastguard Worker if emoji_data.emoji_id == 0: 519*e5825d3bSAndroid Build Coastguard Worker emoji_data.emoji_id = self.emoji_id 520*e5825d3bSAndroid Build Coastguard Worker self.emoji_id = self.emoji_id + 1 521*e5825d3bSAndroid Build Coastguard Worker self.remapped_codepoints[emoji_data.emoji_id] = glyph_name 522*e5825d3bSAndroid Build Coastguard Worker 523*e5825d3bSAndroid Build Coastguard Worker def read_cbdt(self, ttf): 524*e5825d3bSAndroid Build Coastguard Worker """Read image size data from CBDT.""" 525*e5825d3bSAndroid Build Coastguard Worker cbdt = ttf['CBDT'] 526*e5825d3bSAndroid Build Coastguard Worker for strike_data in cbdt.strikeData: 527*e5825d3bSAndroid Build Coastguard Worker for key, data in strike_data.items(): 528*e5825d3bSAndroid Build Coastguard Worker data.decompile() 529*e5825d3bSAndroid Build Coastguard Worker self.glyph_to_image_metrics_map[key] = data.metrics 530*e5825d3bSAndroid Build Coastguard Worker 531*e5825d3bSAndroid Build Coastguard Worker def read_cmap12(self, ttf, glyph_to_codepoint_map): 532*e5825d3bSAndroid Build Coastguard Worker """Reads single code point emojis that are in cmap12, updates glyph_to_codepoint_map and 533*e5825d3bSAndroid Build Coastguard Worker finally clears all elements in CMAP 12""" 534*e5825d3bSAndroid Build Coastguard Worker cmap = ttf['cmap'] 535*e5825d3bSAndroid Build Coastguard Worker for table in cmap.tables: 536*e5825d3bSAndroid Build Coastguard Worker if table.format == 12 and table.platformID == 3 and table.platEncID == 10: 537*e5825d3bSAndroid Build Coastguard Worker for codepoint, glyph_name in table.cmap.items(): 538*e5825d3bSAndroid Build Coastguard Worker glyph_to_codepoint_map[glyph_name] = codepoint 539*e5825d3bSAndroid Build Coastguard Worker self.update_emoji_data([codepoint], glyph_name) 540*e5825d3bSAndroid Build Coastguard Worker return table 541*e5825d3bSAndroid Build Coastguard Worker raise ValueError("Font doesn't contain cmap with format:12, platformID:3 and platEncID:10") 542*e5825d3bSAndroid Build Coastguard Worker 543*e5825d3bSAndroid Build Coastguard Worker def read_gsub(self, ttf, glyph_to_codepoint_map): 544*e5825d3bSAndroid Build Coastguard Worker """Reads the emoji sequences defined in GSUB and clear all elements under GSUB""" 545*e5825d3bSAndroid Build Coastguard Worker gsub = ttf['GSUB'] 546*e5825d3bSAndroid Build Coastguard Worker ligature_subtables = [] 547*e5825d3bSAndroid Build Coastguard Worker context_subtables = [] 548*e5825d3bSAndroid Build Coastguard Worker # this code is font dependent, implementing all gsub rules is out of scope of EmojiCompat 549*e5825d3bSAndroid Build Coastguard Worker # and would be expensive with little value 550*e5825d3bSAndroid Build Coastguard Worker for lookup in gsub.table.LookupList.Lookup: 551*e5825d3bSAndroid Build Coastguard Worker for subtable in lookup.SubTable: 552*e5825d3bSAndroid Build Coastguard Worker if subtable.LookupType == 5: 553*e5825d3bSAndroid Build Coastguard Worker context_subtables.append(subtable) 554*e5825d3bSAndroid Build Coastguard Worker elif subtable.LookupType == 4: 555*e5825d3bSAndroid Build Coastguard Worker ligature_subtables.append(subtable) 556*e5825d3bSAndroid Build Coastguard Worker 557*e5825d3bSAndroid Build Coastguard Worker for subtable in context_subtables: 558*e5825d3bSAndroid Build Coastguard Worker self.add_gsub_context_subtable(subtable, gsub.table.LookupList, glyph_to_codepoint_map) 559*e5825d3bSAndroid Build Coastguard Worker 560*e5825d3bSAndroid Build Coastguard Worker for subtable in ligature_subtables: 561*e5825d3bSAndroid Build Coastguard Worker self.add_gsub_ligature_subtable(subtable, glyph_to_codepoint_map) 562*e5825d3bSAndroid Build Coastguard Worker 563*e5825d3bSAndroid Build Coastguard Worker def add_gsub_context_subtable(self, subtable, lookup_list, glyph_to_codepoint_map): 564*e5825d3bSAndroid Build Coastguard Worker """Add substitutions defined as OpenType Context Substitution""" 565*e5825d3bSAndroid Build Coastguard Worker for sub_class_set in subtable.SubClassSet: 566*e5825d3bSAndroid Build Coastguard Worker if sub_class_set: 567*e5825d3bSAndroid Build Coastguard Worker for sub_class_rule in sub_class_set.SubClassRule: 568*e5825d3bSAndroid Build Coastguard Worker # prepare holder for substitution list. each rule will have a list that is added 569*e5825d3bSAndroid Build Coastguard Worker # to the subs_list. 570*e5825d3bSAndroid Build Coastguard Worker subs_list = len(sub_class_rule.SubstLookupRecord) * [None] 571*e5825d3bSAndroid Build Coastguard Worker for record in sub_class_rule.SubstLookupRecord: 572*e5825d3bSAndroid Build Coastguard Worker subs_list[record.SequenceIndex] = self.get_substitutions(lookup_list, 573*e5825d3bSAndroid Build Coastguard Worker record.LookupListIndex) 574*e5825d3bSAndroid Build Coastguard Worker # create combinations or all lists. the combinations will be filtered by 575*e5825d3bSAndroid Build Coastguard Worker # emoji_data_map. the first element that contain as a valid glyph will be used 576*e5825d3bSAndroid Build Coastguard Worker # as the final glyph 577*e5825d3bSAndroid Build Coastguard Worker combinations = list(itertools.product(*subs_list)) 578*e5825d3bSAndroid Build Coastguard Worker for seq in combinations: 579*e5825d3bSAndroid Build Coastguard Worker glyph_names = [x["input"] for x in seq] 580*e5825d3bSAndroid Build Coastguard Worker codepoints = [glyph_to_codepoint_map[x] for x in glyph_names] 581*e5825d3bSAndroid Build Coastguard Worker outputs = [x["output"] for x in seq if x["output"]] 582*e5825d3bSAndroid Build Coastguard Worker nonempty_outputs = list(filter(lambda x: x.strip() , outputs)) 583*e5825d3bSAndroid Build Coastguard Worker if len(nonempty_outputs) == 0: 584*e5825d3bSAndroid Build Coastguard Worker print("Warning: no output glyph is set for " + str(glyph_names)) 585*e5825d3bSAndroid Build Coastguard Worker continue 586*e5825d3bSAndroid Build Coastguard Worker elif len(nonempty_outputs) > 1: 587*e5825d3bSAndroid Build Coastguard Worker print( 588*e5825d3bSAndroid Build Coastguard Worker "Warning: multiple glyph is set for " 589*e5825d3bSAndroid Build Coastguard Worker + str(glyph_names) + ", will use the first one") 590*e5825d3bSAndroid Build Coastguard Worker 591*e5825d3bSAndroid Build Coastguard Worker glyph = nonempty_outputs[0] 592*e5825d3bSAndroid Build Coastguard Worker self.update_emoji_data(codepoints, glyph) 593*e5825d3bSAndroid Build Coastguard Worker 594*e5825d3bSAndroid Build Coastguard Worker def get_substitutions(self, lookup_list, index): 595*e5825d3bSAndroid Build Coastguard Worker result = [] 596*e5825d3bSAndroid Build Coastguard Worker for x in lookup_list.Lookup[index].SubTable: 597*e5825d3bSAndroid Build Coastguard Worker for input, output in x.mapping.items(): 598*e5825d3bSAndroid Build Coastguard Worker result.append({"input": input, "output": output}) 599*e5825d3bSAndroid Build Coastguard Worker return result 600*e5825d3bSAndroid Build Coastguard Worker 601*e5825d3bSAndroid Build Coastguard Worker def add_gsub_ligature_subtable(self, subtable, glyph_to_codepoint_map): 602*e5825d3bSAndroid Build Coastguard Worker for name, ligatures in subtable.ligatures.items(): 603*e5825d3bSAndroid Build Coastguard Worker for ligature in ligatures: 604*e5825d3bSAndroid Build Coastguard Worker glyph_names = [name] + ligature.Component 605*e5825d3bSAndroid Build Coastguard Worker codepoints = [glyph_to_codepoint_map[x] for x in glyph_names] 606*e5825d3bSAndroid Build Coastguard Worker self.update_emoji_data(codepoints, ligature.LigGlyph) 607*e5825d3bSAndroid Build Coastguard Worker 608*e5825d3bSAndroid Build Coastguard Worker def write_metadata_json(self, output_json_file_path): 609*e5825d3bSAndroid Build Coastguard Worker """Writes the emojis into a json file""" 610*e5825d3bSAndroid Build Coastguard Worker output_json = {} 611*e5825d3bSAndroid Build Coastguard Worker output_json['version'] = METADATA_VERSION 612*e5825d3bSAndroid Build Coastguard Worker output_json['sourceSha'] = create_sha_from_source_files( 613*e5825d3bSAndroid Build Coastguard Worker [self.font_path, OUTPUT_META_FILE, FLATBUFFER_SCHEMA]) 614*e5825d3bSAndroid Build Coastguard Worker output_json['list'] = [] 615*e5825d3bSAndroid Build Coastguard Worker 616*e5825d3bSAndroid Build Coastguard Worker emoji_data_list = sorted(self.emoji_data_map.values(), key=lambda x: x.emoji_id) 617*e5825d3bSAndroid Build Coastguard Worker 618*e5825d3bSAndroid Build Coastguard Worker total_emoji_count = 0 619*e5825d3bSAndroid Build Coastguard Worker for emoji_data in emoji_data_list: 620*e5825d3bSAndroid Build Coastguard Worker if self.without_flags and is_flag_seq(emoji_data.codepoints): 621*e5825d3bSAndroid Build Coastguard Worker continue # Do not add flags emoji data if this is for subset font. 622*e5825d3bSAndroid Build Coastguard Worker element = emoji_data.create_json_element() 623*e5825d3bSAndroid Build Coastguard Worker output_json['list'].append(element) 624*e5825d3bSAndroid Build Coastguard Worker total_emoji_count = total_emoji_count + 1 625*e5825d3bSAndroid Build Coastguard Worker 626*e5825d3bSAndroid Build Coastguard Worker # write the new json file to be processed by FlatBuffers 627*e5825d3bSAndroid Build Coastguard Worker with open(output_json_file_path, 'w') as json_file: 628*e5825d3bSAndroid Build Coastguard Worker print(json.dumps(output_json, indent=4, sort_keys=True, separators=(',', ':')), 629*e5825d3bSAndroid Build Coastguard Worker file=json_file) 630*e5825d3bSAndroid Build Coastguard Worker 631*e5825d3bSAndroid Build Coastguard Worker return total_emoji_count 632*e5825d3bSAndroid Build Coastguard Worker 633*e5825d3bSAndroid Build Coastguard Worker def write_metadata_csv(self): 634*e5825d3bSAndroid Build Coastguard Worker """Writes emoji metadata into space separated file""" 635*e5825d3bSAndroid Build Coastguard Worker with open(OUTPUT_META_FILE, 'w') as csvfile: 636*e5825d3bSAndroid Build Coastguard Worker csvwriter = csv.writer(csvfile, delimiter=' ') 637*e5825d3bSAndroid Build Coastguard Worker emoji_data_list = sorted(self.emoji_data_map.values(), key=lambda x: x.emoji_id) 638*e5825d3bSAndroid Build Coastguard Worker csvwriter.writerow(['#id', 'sdkAdded', 'compatAdded', 'codepoints']) 639*e5825d3bSAndroid Build Coastguard Worker for emoji_data in emoji_data_list: 640*e5825d3bSAndroid Build Coastguard Worker csvwriter.writerow(emoji_data.create_txt_row()) 641*e5825d3bSAndroid Build Coastguard Worker 642*e5825d3bSAndroid Build Coastguard Worker def add_watermark(self, ttf): 643*e5825d3bSAndroid Build Coastguard Worker cmap = ttf.getBestCmap() 644*e5825d3bSAndroid Build Coastguard Worker gsub = ttf['GSUB'].table 645*e5825d3bSAndroid Build Coastguard Worker 646*e5825d3bSAndroid Build Coastguard Worker # Obtain Version string 647*e5825d3bSAndroid Build Coastguard Worker m = re.search('^Version (\d*)\.(\d*)', font_data.font_version(ttf)) 648*e5825d3bSAndroid Build Coastguard Worker if not m: 649*e5825d3bSAndroid Build Coastguard Worker raise ValueError('The font does not have proper version string.') 650*e5825d3bSAndroid Build Coastguard Worker major = m.group(1) 651*e5825d3bSAndroid Build Coastguard Worker minor = m.group(2) 652*e5825d3bSAndroid Build Coastguard Worker # Replace the dot with space since NotoColorEmoji does not have glyph for dot. 653*e5825d3bSAndroid Build Coastguard Worker glyphs = [cmap[ord(x)] for x in '%s %s' % (major, minor)] 654*e5825d3bSAndroid Build Coastguard Worker 655*e5825d3bSAndroid Build Coastguard Worker # Update Glyph metrics 656*e5825d3bSAndroid Build Coastguard Worker ttf.getGlyphOrder().append(WATERMARK_NEW_GLYPH_ID) 657*e5825d3bSAndroid Build Coastguard Worker refGlyphId = cmap[WATERMARK_REF_CODE_POINT] 658*e5825d3bSAndroid Build Coastguard Worker ttf['hmtx'].metrics[WATERMARK_NEW_GLYPH_ID] = ttf['hmtx'].metrics[refGlyphId] 659*e5825d3bSAndroid Build Coastguard Worker ttf['vmtx'].metrics[WATERMARK_NEW_GLYPH_ID] = ttf['vmtx'].metrics[refGlyphId] 660*e5825d3bSAndroid Build Coastguard Worker 661*e5825d3bSAndroid Build Coastguard Worker # Add new Glyph to cmap 662*e5825d3bSAndroid Build Coastguard Worker font_data.add_to_cmap(ttf, { WATERMARK_NEW_CODE_POINT : WATERMARK_NEW_GLYPH_ID }) 663*e5825d3bSAndroid Build Coastguard Worker 664*e5825d3bSAndroid Build Coastguard Worker # Add lookup table for the version string. 665*e5825d3bSAndroid Build Coastguard Worker lookups = gsub.LookupList.Lookup 666*e5825d3bSAndroid Build Coastguard Worker new_lookup = otTables.Lookup() 667*e5825d3bSAndroid Build Coastguard Worker new_lookup.LookupType = 2 # Multiple Substitution Subtable. 668*e5825d3bSAndroid Build Coastguard Worker new_lookup.LookupFlag = 0 669*e5825d3bSAndroid Build Coastguard Worker new_subtable = otTables.MultipleSubst() 670*e5825d3bSAndroid Build Coastguard Worker new_subtable.mapping = { WATERMARK_NEW_GLYPH_ID : tuple(glyphs) } 671*e5825d3bSAndroid Build Coastguard Worker new_lookup.SubTable = [ new_subtable ] 672*e5825d3bSAndroid Build Coastguard Worker new_lookup_index = len(lookups) 673*e5825d3bSAndroid Build Coastguard Worker lookups.append(new_lookup) 674*e5825d3bSAndroid Build Coastguard Worker 675*e5825d3bSAndroid Build Coastguard Worker # Add feature 676*e5825d3bSAndroid Build Coastguard Worker feature = next(x for x in gsub.FeatureList.FeatureRecord if x.FeatureTag == 'ccmp') 677*e5825d3bSAndroid Build Coastguard Worker if not feature: 678*e5825d3bSAndroid Build Coastguard Worker raise ValueError("Font doesn't contain ccmp feature.") 679*e5825d3bSAndroid Build Coastguard Worker 680*e5825d3bSAndroid Build Coastguard Worker feature.Feature.LookupListIndex.append(new_lookup_index) 681*e5825d3bSAndroid Build Coastguard Worker 682*e5825d3bSAndroid Build Coastguard Worker def create_font(self): 683*e5825d3bSAndroid Build Coastguard Worker """Creates the EmojiCompat font. 684*e5825d3bSAndroid Build Coastguard Worker :param font_path: path to Android NotoColorEmoji font 685*e5825d3bSAndroid Build Coastguard Worker :param unicode_path: path to directory that contains unicode files 686*e5825d3bSAndroid Build Coastguard Worker """ 687*e5825d3bSAndroid Build Coastguard Worker 688*e5825d3bSAndroid Build Coastguard Worker tmp_dir = tempfile.mkdtemp() 689*e5825d3bSAndroid Build Coastguard Worker 690*e5825d3bSAndroid Build Coastguard Worker # create emoji codepoints to EmojiData map 691*e5825d3bSAndroid Build Coastguard Worker self.emoji_data_map = load_emoji_data_map(self.unicode_path, self.without_flags) 692*e5825d3bSAndroid Build Coastguard Worker 693*e5825d3bSAndroid Build Coastguard Worker # read previous metadata file to update id, sdkAdded and compatAdded. emoji id that is 694*e5825d3bSAndroid Build Coastguard Worker # returned is either default or 1 greater than the largest id in previous data 695*e5825d3bSAndroid Build Coastguard Worker self.emoji_id = load_previous_metadata(self.emoji_data_map) 696*e5825d3bSAndroid Build Coastguard Worker 697*e5825d3bSAndroid Build Coastguard Worker # recalcTimestamp parameter will keep the modified field same as the original font. Changing 698*e5825d3bSAndroid Build Coastguard Worker # the modified field in the font causes the font ttf file to change, which makes it harder 699*e5825d3bSAndroid Build Coastguard Worker # to understand if something really changed in the font. 700*e5825d3bSAndroid Build Coastguard Worker with contextlib.closing(ttLib.TTFont(self.font_path, recalcTimestamp=False)) as ttf: 701*e5825d3bSAndroid Build Coastguard Worker # read image size data 702*e5825d3bSAndroid Build Coastguard Worker self.read_cbdt(ttf) 703*e5825d3bSAndroid Build Coastguard Worker 704*e5825d3bSAndroid Build Coastguard Worker # glyph name to codepoint map 705*e5825d3bSAndroid Build Coastguard Worker glyph_to_codepoint_map = {} 706*e5825d3bSAndroid Build Coastguard Worker 707*e5825d3bSAndroid Build Coastguard Worker # read single codepoint emojis under cmap12 and clear the table contents 708*e5825d3bSAndroid Build Coastguard Worker cmap12_table = self.read_cmap12(ttf, glyph_to_codepoint_map) 709*e5825d3bSAndroid Build Coastguard Worker 710*e5825d3bSAndroid Build Coastguard Worker # read emoji sequences gsub and clear the table contents 711*e5825d3bSAndroid Build Coastguard Worker self.read_gsub(ttf, glyph_to_codepoint_map) 712*e5825d3bSAndroid Build Coastguard Worker 713*e5825d3bSAndroid Build Coastguard Worker # add all new codepoint to glyph mappings 714*e5825d3bSAndroid Build Coastguard Worker cmap12_table.cmap.update(self.remapped_codepoints) 715*e5825d3bSAndroid Build Coastguard Worker 716*e5825d3bSAndroid Build Coastguard Worker # final metadata csv will be used to generate the sha, therefore write it before 717*e5825d3bSAndroid Build Coastguard Worker # metadata json is written. 718*e5825d3bSAndroid Build Coastguard Worker self.write_metadata_csv() 719*e5825d3bSAndroid Build Coastguard Worker 720*e5825d3bSAndroid Build Coastguard Worker output_json_file = os.path.join(tmp_dir, OUTPUT_JSON_FILE_NAME) 721*e5825d3bSAndroid Build Coastguard Worker flatbuffer_bin_file = os.path.join(tmp_dir, FLATBUFFER_BIN) 722*e5825d3bSAndroid Build Coastguard Worker flatbuffer_java_dir = os.path.join(tmp_dir, FLATBUFFER_JAVA_PATH) 723*e5825d3bSAndroid Build Coastguard Worker 724*e5825d3bSAndroid Build Coastguard Worker total_emoji_count = self.write_metadata_json(output_json_file) 725*e5825d3bSAndroid Build Coastguard Worker 726*e5825d3bSAndroid Build Coastguard Worker # create the flatbuffers binary and java classes 727*e5825d3bSAndroid Build Coastguard Worker flatc_command = ['flatc', 728*e5825d3bSAndroid Build Coastguard Worker '-o', 729*e5825d3bSAndroid Build Coastguard Worker tmp_dir, 730*e5825d3bSAndroid Build Coastguard Worker '-b', 731*e5825d3bSAndroid Build Coastguard Worker '-j', 732*e5825d3bSAndroid Build Coastguard Worker FLATBUFFER_SCHEMA, 733*e5825d3bSAndroid Build Coastguard Worker output_json_file] 734*e5825d3bSAndroid Build Coastguard Worker subprocess.check_output(flatc_command) 735*e5825d3bSAndroid Build Coastguard Worker 736*e5825d3bSAndroid Build Coastguard Worker # inject metadata binary into font 737*e5825d3bSAndroid Build Coastguard Worker inject_meta_into_font(ttf, flatbuffer_bin_file) 738*e5825d3bSAndroid Build Coastguard Worker 739*e5825d3bSAndroid Build Coastguard Worker # add wartermark glyph for manual verification. 740*e5825d3bSAndroid Build Coastguard Worker self.add_watermark(ttf) 741*e5825d3bSAndroid Build Coastguard Worker 742*e5825d3bSAndroid Build Coastguard Worker # update CBDT and CBLC versions since older android versions cannot read > 2.0 743*e5825d3bSAndroid Build Coastguard Worker ttf['CBDT'].version = 2.0 744*e5825d3bSAndroid Build Coastguard Worker ttf['CBLC'].version = 2.0 745*e5825d3bSAndroid Build Coastguard Worker 746*e5825d3bSAndroid Build Coastguard Worker # save the new font 747*e5825d3bSAndroid Build Coastguard Worker ttf.save(FONT_PATH) 748*e5825d3bSAndroid Build Coastguard Worker 749*e5825d3bSAndroid Build Coastguard Worker update_flatbuffer_java_files(flatbuffer_java_dir, #tmp dir 750*e5825d3bSAndroid Build Coastguard Worker FLATBUFFER_HEADER, 751*e5825d3bSAndroid Build Coastguard Worker FLATBUFFER_JAVA_TARGET) 752*e5825d3bSAndroid Build Coastguard Worker 753*e5825d3bSAndroid Build Coastguard Worker create_test_data(self.unicode_path) 754*e5825d3bSAndroid Build Coastguard Worker 755*e5825d3bSAndroid Build Coastguard Worker # clear the tmp output directory 756*e5825d3bSAndroid Build Coastguard Worker shutil.rmtree(tmp_dir, ignore_errors=True) 757*e5825d3bSAndroid Build Coastguard Worker 758*e5825d3bSAndroid Build Coastguard Worker print( 759*e5825d3bSAndroid Build Coastguard Worker "{0} emojis are written to\n{1}".format(total_emoji_count, FONT_DIR)) 760*e5825d3bSAndroid Build Coastguard Worker 761*e5825d3bSAndroid Build Coastguard Worker 762*e5825d3bSAndroid Build Coastguard Workerdef print_usage(): 763*e5825d3bSAndroid Build Coastguard Worker """Prints how to use the script.""" 764*e5825d3bSAndroid Build Coastguard Worker print("Please specify a path to font and unicode files.\n" 765*e5825d3bSAndroid Build Coastguard Worker "usage: createfont.py noto-color-emoji-path unicode-dir-path") 766*e5825d3bSAndroid Build Coastguard Worker 767*e5825d3bSAndroid Build Coastguard Workerdef parse_args(argv): 768*e5825d3bSAndroid Build Coastguard Worker # parse manually to avoid any extra dependencies 769*e5825d3bSAndroid Build Coastguard Worker if len(argv) == 4: 770*e5825d3bSAndroid Build Coastguard Worker without_flags = argv[3] == '--without-flags' 771*e5825d3bSAndroid Build Coastguard Worker else: 772*e5825d3bSAndroid Build Coastguard Worker without_flags = False 773*e5825d3bSAndroid Build Coastguard Worker 774*e5825d3bSAndroid Build Coastguard Worker if len(argv) < 3: 775*e5825d3bSAndroid Build Coastguard Worker print_usage() 776*e5825d3bSAndroid Build Coastguard Worker sys.exit(1) 777*e5825d3bSAndroid Build Coastguard Worker return (sys.argv[1], sys.argv[2], without_flags) 778*e5825d3bSAndroid Build Coastguard Worker 779*e5825d3bSAndroid Build Coastguard Workerdef main(): 780*e5825d3bSAndroid Build Coastguard Worker font_file, unicode_dir, without_flags = parse_args(sys.argv) 781*e5825d3bSAndroid Build Coastguard Worker EmojiFontCreator(font_file, unicode_dir, without_flags).create_font() 782*e5825d3bSAndroid Build Coastguard Worker 783*e5825d3bSAndroid Build Coastguard Worker 784*e5825d3bSAndroid Build Coastguard Workerif __name__ == '__main__': 785*e5825d3bSAndroid Build Coastguard Worker main() 786