Blob Blame History Raw
#!/usr/bin/env python
#
# Copyright 2014 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Modify the Noto Color Emoji font to use GSUB rules for flags and keycaps."""

__author__ = "roozbeh@google.com (Roozbeh Pournader)"


import sys

from fontTools import agl
from fontTools import ttLib
from fontTools.ttLib.tables import otTables

from nototools import font_data


def create_script_list(script_tag='DFLT'):
    """Create a ScriptList for the GSUB table."""
    def_lang_sys = otTables.DefaultLangSys()
    def_lang_sys.ReqFeatureIndex = 0xFFFF
    def_lang_sys.FeatureCount = 1
    def_lang_sys.FeatureIndex = [0]
    def_lang_sys.LookupOrder = None

    script_record = otTables.ScriptRecord()
    script_record.ScriptTag = script_tag
    script_record.Script = otTables.Script()
    script_record.Script.DefaultLangSys = def_lang_sys
    script_record.Script.LangSysCount = 0
    script_record.Script.LangSysRecord = []

    script_list = otTables.ScriptList()
    script_list.ScriptCount = 1
    script_list.ScriptRecord = [script_record]

    return script_list


def create_feature_list(feature_tag, lookup_count):
    """Create a FeatureList for the GSUB table."""
    feature_record = otTables.FeatureRecord()
    feature_record.FeatureTag = feature_tag
    feature_record.Feature = otTables.Feature()
    feature_record.Feature.LookupCount = lookup_count
    feature_record.Feature.LookupListIndex = range(lookup_count)
    feature_record.Feature.FeatureParams = None

    feature_list = otTables.FeatureList()
    feature_list.FeatureCount = 1
    feature_list.FeatureRecord = [feature_record]

    return feature_list


def create_lookup_list(lookups):
    """Create a LookupList for the GSUB table."""
    lookup_list = otTables.LookupList()
    lookup_list.LookupCount = len(lookups)
    lookup_list.Lookup = lookups

    return lookup_list


def get_glyph_name_or_create(char, font):
    """Return the glyph name for a character, creating if it doesn't exist."""
    cmap = font_data.get_cmap(font)
    if char in cmap:
        return cmap[char]

    glyph_name = agl.UV2AGL[char]
    assert glyph_name not in font.glyphOrder

    font['hmtx'].metrics[glyph_name] = [0, 0]
    cmap[char] = glyph_name

    if 'glyf' in font:
        from fontTools.ttLib.tables import _g_l_y_f
        empty_glyph = _g_l_y_f.Glyph()
        font['glyf'].glyphs[glyph_name] = empty_glyph

    font.glyphOrder.append(glyph_name)
    return glyph_name


def create_lookup(table, font, flag=0):
    """Create a Lookup based on mapping table."""
    cmap = font_data.get_cmap(font)

    ligatures = {}
    for output, (ch1, ch2) in table.iteritems():
        output = cmap[output]
        ch1 = get_glyph_name_or_create(ch1, font)
        ch2 = get_glyph_name_or_create(ch2, font)

        ligature = otTables.Ligature()
        ligature.CompCount = 2
        ligature.Component = [ch2]
        ligature.LigGlyph = output

        try:
            ligatures[ch1].append(ligature)
        except KeyError:
            ligatures[ch1] = [ligature]

    ligature_subst = otTables.LigatureSubst()
    ligature_subst.ligatures = ligatures

    lookup = otTables.Lookup()
    lookup.LookupType = 4
    lookup.LookupFlag = flag
    lookup.SubTableCount = 1
    lookup.SubTable = [ligature_subst]

    return lookup


def create_simple_gsub(lookups, script='DFLT', feature='ccmp'):
    """Create a simple GSUB table."""
    gsub_class = ttLib.getTableClass('GSUB')
    gsub = gsub_class('GSUB')

    gsub.table = otTables.GSUB()
    gsub.table.Version = 1.0
    gsub.table.ScriptList = create_script_list(script)
    gsub.table.FeatureList = create_feature_list(feature, len(lookups))
    gsub.table.LookupList = create_lookup_list(lookups)
    return gsub


def reg_indicator(letter):
    """Return a regional indicator charater from corresponing capital letter.
    """
    return 0x1F1E6 + ord(letter) - ord('A')


EMOJI_FLAGS = {
    0xFE4E5: (reg_indicator('J'), reg_indicator('P')),  # Japan
    0xFE4E6: (reg_indicator('U'), reg_indicator('S')),  # United States
    0xFE4E7: (reg_indicator('F'), reg_indicator('R')),  # France
    0xFE4E8: (reg_indicator('D'), reg_indicator('E')),  # Germany
    0xFE4E9: (reg_indicator('I'), reg_indicator('T')),  # Italy
    0xFE4EA: (reg_indicator('G'), reg_indicator('B')),  # United Kingdom
    0xFE4EB: (reg_indicator('E'), reg_indicator('S')),  # Spain
    0xFE4EC: (reg_indicator('R'), reg_indicator('U')),  # Russia
    0xFE4ED: (reg_indicator('C'), reg_indicator('N')),  # China
    0xFE4EE: (reg_indicator('K'), reg_indicator('R')),  # Korea
}

KEYCAP = 0x20E3

EMOJI_KEYCAPS = {
    0xFE82C: (ord('#'), KEYCAP),
    0xFE82E: (ord('1'), KEYCAP),
    0xFE82F: (ord('2'), KEYCAP),
    0xFE830: (ord('3'), KEYCAP),
    0xFE831: (ord('4'), KEYCAP),
    0xFE832: (ord('5'), KEYCAP),
    0xFE833: (ord('6'), KEYCAP),
    0xFE834: (ord('7'), KEYCAP),
    0xFE835: (ord('8'), KEYCAP),
    0xFE836: (ord('9'), KEYCAP),
    0xFE837: (ord('0'), KEYCAP),
}

def main(argv):
    """Modify all the fonts given in the command line."""
    for font_name in argv[1:]:
        font = ttLib.TTFont(font_name)

        assert 'GSUB' not in font
        font['GSUB'] = create_simple_gsub([
            create_lookup(EMOJI_KEYCAPS, font),
            create_lookup(EMOJI_FLAGS, font)])

        font_data.delete_from_cmap(
            font, EMOJI_FLAGS.keys() + EMOJI_KEYCAPS.keys())

        font.save(font_name+'-fixed')

if __name__ == '__main__':
    main(sys.argv)