xref: /aosp_15_r20/external/noto-fonts/emoji-compat/createfont.py (revision e5825d3be9fd13b272e7df556d285d1f07f3b027)
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