Proton/fonts/scripts/merge.py
Giovanni Mascellani 3d635bd789 fonts: Include in fake Arial all the codepoints from the original font.
New fonts are from https://github.com/notofonts/noto-fonts.git at
2725c70baa8b0176c7577093ba1fc6179aa79478, in the hinted/ttf folder.
2023-02-14 16:07:44 +02:00

291 lines
8.9 KiB
Python
Executable File

#!/usr/bin/env python3
# This script was created by Giovanni Mascellani for CodeWeavers
# Based on merge_noto.py and merge_fonts.py from the nototools
# (https://github.com/googlefonts/nototools), with the following
# copyright notice:
# Copyright 2014-2017 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.
# The font name changing logic is taken from
# https://github.com/chrissimpkins/fontname.py/blob/master/fontname.py,
# with the following copyright notice:
# Copyright 2019 Christopher Simpkins
# MIT License
# The font subsetting logic is taken from
# https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/subset/__init__.py
# with the following copyright notice:
# Copyright 2013 Google, Inc. All Rights Reserved.
# Google Author(s): Behdad Esfahbod
# The whole fonttools repository is distributed under the MIT license.
"""Merges a number of Noto fonts and then sets a given name to the
result.
"""
import sys
import tempfile
import os
from fontTools import merge
from fontTools import ttLib
from fontTools import subset
from fontTools.ttLib.tables import otTables
def read_line_metrics(font):
metrics = {
"ascent": font["hhea"].ascent,
"descent": font["hhea"].descent,
"usWinAscent": font["OS/2"].usWinAscent,
"usWinDescent": font["OS/2"].usWinDescent,
"sTypoAscender": font["OS/2"].sTypoAscender,
"sTypoDescender": font["OS/2"].sTypoDescender,
"sxHeight": font["OS/2"].sxHeight,
"sCapHeight": font["OS/2"].sCapHeight,
"sTypoLineGap": font["OS/2"].sTypoLineGap,
}
return metrics
def set_line_metrics(font, metrics):
font["hhea"].ascent = metrics["ascent"]
font["hhea"].descent = metrics["descent"]
font["OS/2"].usWinAscent = metrics["usWinAscent"]
font["OS/2"].usWinDescent = metrics["usWinDescent"]
font["OS/2"].sTypoAscender = metrics["sTypoAscender"]
font["OS/2"].sTypoDescender = metrics["sTypoDescender"]
font["OS/2"].sxHeight = metrics["sxHeight"]
font["OS/2"].sCapHeight = metrics["sCapHeight"]
font["OS/2"].sTypoLineGap = metrics["sTypoLineGap"]
def has_gsub_table(fontfile):
font = ttLib.TTFont(fontfile)
return "GSUB" in font
SCRIPT_TO_OPENTYPE_SCRIPT_TAG = {
# Retrieved from Opentype 1.9 delta specs. Prerelease scripttags used out of necessity. https://docs.microsoft.com/en-us/typography/opentype/spec/scripttags
"Carian": "cari",
"CypriotSyllabary": "cprt",
"CyproMinoan": "cpmn",
"Deseret": "dsrt",
"Glagolitic": "glag",
"EgyptianHieroglyphs": "egyp",
"ImperialAramaic": "armi",
"LinearA": "lina",
"LinearB": "linb",
"Lisu": "lisu",
"Lycian": "lyci",
"Lydian": "lydi",
"Ogham": "ogam",
"OldItalic": "ital",
"OldPersian": "xpeo",
"OldSouthArabian": "sarb",
"OldTurkic": "orkh",
"OldSogdian": "sogo",
"OldNorthArabian": "narb",
"OldHungarian": "hung",
"Osmanya": "osma",
"Phoenician": "phnx",
"SumeroAkkadianCuneiform": "xsux",
"Ugaritic": "ugar",
"OlChiki": "olck",
"TaiLe": "tale",
"Cuneiform": "xsux",
"Cypriot": "cprt",
"Runic": "runr",
"Shavian": "shaw",
"Vai": "vai ",
"Yi": "yi ",
"AnatolianHieroglyphs": "hluw",
"Bamum": "bamu",
"ByzantineMusic": "byzm",
"Gothic": "goth",
"ImperialAramaic": "armi",
"InscriptionalPahlavi": "phli",
"InscriptionalParthian": "prti",
"Khojki": "khoj",
"MathematicalAlphanumericSymbols": "math",
"MeroiticCursive": "merc",
"MeroiticHieroglyphs": "mero",
"MusicalSymbols": "musc",
"Palmyrene": "palm",
"Rejang": "rjng",
"Samaritan": "samr",
"Carian": "cari",
"Ahom": "ahom",
"Adlam": "adlm",
"Dogra": "dogr",
"Lisu": "lisu",
"Mandaean": "mand",
"Manichaean": "mani",
"Tifinagh": "tfng",
"Wancho": "wcho",
"Yezidi": "yezi",
"Cherokee": "cher",
"Chorasmian": "chrs",
"PahawhHmong": "hmng",
"Phagspa": "phag",
"Sundanese": "sund",
"WarangCiti": "wara",
"SylotiNagri": "sylo",
"PsalterPahlavi": "phlp",
"CaucasianAlbanian": "aghb",
"Medefaidrin": "medf",
"MeiteiMayek": "mtei",
"MendeKikakui": "mend",
"Mro": "mroo",
"Multani": "mult",
"Nabataean": "nbat",
"Nandinagari": "nand",
"Newa": "newa",
"NewTaiLue": "talu",
"Nushu": "nshu",
"NyiakengPuachueHmong": "hmnp",
"OldPermic": "perm",
"SoraSompeng": "sora",
"Soyombo": "soyo",
"SylotiNagri": "sylo",
"Tagbanwa": "tagb",
"Tagalog": "tglg",
"Takri": "takr",
"TaiTham": "lana",
"TaiViet": "tavt",
"Tangut": "tang",
"Thaana": "thaa",
"UgariticCuneiform": "ugar",
"ZanabazarSquare": "zanb",
"SignWriting": "sgnw",
"OldUyghur": "ougr",
"Tangsa": "tnsa",
"Toto": "toto",
"Vithkuqi": "vith",
"Duployan": "dupl",
"Hatran": "hatr",
# These last two would only merge using the long script name including the 'NotoSerif' part
"NotoSerifYezidi": "yezi",
"NotoSerifNyiakengPuachueHmong": "hmnp",
}
def get_opentype_script_tag(fontfile):
fontfile = os.path.basename(fontfile)
if fontfile.startswith("NotoSans"):
fontfile = fontfile[8:]
fontfile = fontfile[: fontfile.index("-")]
return SCRIPT_TO_OPENTYPE_SCRIPT_TAG[fontfile]
def add_gsub_to_font(fontfile):
"""Adds an empty GSUB table to a font."""
font = ttLib.TTFont(fontfile)
gsub_table = ttLib.getTableClass("GSUB")("GSUB")
gsub_table.table = otTables.GSUB()
gsub_table.table.Version = 1.0
gsub_table.table.ScriptList = otTables.ScriptList()
gsub_table.table.ScriptCount = 1
gsub_table.table.LookupList = otTables.LookupList()
gsub_table.table.LookupList.LookupCount = 0
gsub_table.table.LookupList.Lookup = []
gsub_table.table.FeatureList = otTables.FeatureList()
gsub_table.table.FeatureList.FeatureCount = 0
gsub_table.table.LookupList.FeatureRecord = []
script_record = otTables.ScriptRecord()
script_record.ScriptTag = get_opentype_script_tag(fontfile)
script_record.Script = otTables.Script()
script_record.Script.LangSysCount = 0
script_record.Script.LangSysRecord = []
default_lang_sys = otTables.DefaultLangSys()
default_lang_sys.FeatureIndex = []
default_lang_sys.FeatureCount = 0
default_lang_sys.LookupOrder = None
default_lang_sys.ReqFeatureIndex = 65535
script_record.Script.DefaultLangSys = default_lang_sys
gsub_table.table.ScriptList.ScriptRecord = [script_record]
font["GSUB"] = gsub_table
target_file = tempfile.gettempdir() + "/" + os.path.basename(fontfile)
font.save(target_file)
return target_file
def parse_unicodes(s):
import re
s = re.sub(r"0[xX]", " ", s)
s = re.sub(r"[<+>,;&#\\xXuU\n ]", " ", s)
l = []
for item in s.split():
fields = item.split("-")
if len(fields) == 1:
l.append(int(item, 16))
else:
start, end = fields
l.extend(range(int(start, 16), int(end, 16) + 1))
return l
def main():
output_filename = sys.argv[-1]
ranges_filename = sys.argv[-2]
weight = sys.argv[-3]
font_name = sys.argv[-4]
ps_name = sys.argv[-5]
input_filenames = sys.argv[1:-5]
# Add a GSUB table to the fonts that do not have one, otherwise
# the merger will complain
for index, filename in enumerate(input_filenames):
if not has_gsub_table(filename):
input_filenames[index] = add_gsub_to_font(filename)
merger = merge.Merger()
font = merger.merge(input_filenames)
# Use the line metrics defined by the first font, which is
# supposed to be the basic NotoSans
metrics = read_line_metrics(ttLib.TTFont(input_filenames[0]))
set_line_metrics(font, metrics)
# Select the subset we care about
options = subset.Options(ignore_missing_unicodes=False)
subsetter = subset.Subsetter(options)
unicodes = []
with open(ranges_filename) as ranges:
for line in ranges:
unicodes.extend(parse_unicodes(line.split("#")[0]))
if len(unicodes) != 0:
subsetter.populate(unicodes=unicodes)
subsetter.subset(font)
# Rename the result
for record in font['name'].names:
if record.nameID == 1:
record.string = font_name
elif record.nameID == 4:
record.string = "{} {}".format(font_name, weight)
elif record.nameID == 6:
record.string = "{}-{}".format(ps_name, weight.replace(' ', ''))
font.save(output_filename)
if __name__ == '__main__':
main()