3d635bd789
New fonts are from https://github.com/notofonts/noto-fonts.git at 2725c70baa8b0176c7577093ba1fc6179aa79478, in the hinted/ttf folder.
290 lines
8.9 KiB
Python
Executable file
290 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()
|