fonts: Rewrite font merging script based on fonttools.
The current script based on fontforge seems to have a few problems. The generated fonts show glitches, for example in Cyberpunk 2077 for Thai and in FIFA 22 for Arabic. I don't precisely know what is the problem, and it might be that the real bug is in the rendering code rather than in the merging script. But since this seems to work better overall, I'm sticking with it. CW-Bug-Id: #20302
This commit is contained in:
parent
62174c7c1e
commit
8e9e6da034
3 changed files with 254 additions and 33 deletions
|
@ -818,17 +818,17 @@ $(simsun.ttc): $(simsun.ttf) $(nsimsun.ttf)
|
|||
$(msgothic.ttc): $(msgothic.ttf) $(mspgothic.ttf) $(msuigothic.ttf)
|
||||
afdko otf2otc -o $@ $^
|
||||
|
||||
$(micross.ttf): $(noto_sans.ttf) $(noto_sans_arabic.ttf) $(noto_sans_armenian.ttf) $(noto_sans_bengali.ttf) $(noto_sans_coptic.ttf) \
|
||||
$(micross.ttf): $(FONTS)/scripts/merge.py $(noto_sans.ttf) $(noto_sans_arabic.ttf) $(noto_sans_armenian.ttf) $(noto_sans_bengali.ttf) $(noto_sans_coptic.ttf) \
|
||||
$(noto_sans_georgian.ttf) $(noto_sans_gujarati.ttf) $(noto_sans_hebrew.ttf) $(noto_sans_khmer.ttf) $(noto_sans_tamil.ttf) \
|
||||
$(noto_sans_thaana.ttf) $(noto_sans_thai.ttf)
|
||||
$(FONTFORGE) -script $(MERGEFONTSSCRIPT) $(noto_sans.ttf) $(noto_sans_arabic.ttf) $(noto_sans_armenian.ttf) $(noto_sans_bengali.ttf) \
|
||||
$(FONTS)/scripts/merge.py $(noto_sans.ttf) $(noto_sans_arabic.ttf) $(noto_sans_armenian.ttf) $(noto_sans_bengali.ttf) \
|
||||
$(noto_sans_coptic.ttf) $(noto_sans_georgian.ttf) $(noto_sans_gujarati.ttf) $(noto_sans_hebrew.ttf) $(noto_sans_khmer.ttf) \
|
||||
$(noto_sans_tamil.ttf) $(noto_sans_thaana.ttf) $(noto_sans_thai.ttf) "MicrosoftSansSerif" "Microsoft Sans Serif" "Regular" $(micross.ttf)
|
||||
|
||||
$(nirmala.ttf): $(noto_sans.ttf) $(noto_sans_bengaliui.ttf) $(noto_sans_devanagariui.ttf) $(noto_sans_gujaratiui.ttf) $(noto_sans_gurmukhiui.ttf) \
|
||||
$(nirmala.ttf): $(FONTS)/scripts/merge.py $(noto_sans.ttf) $(noto_sans_bengaliui.ttf) $(noto_sans_devanagariui.ttf) $(noto_sans_gujaratiui.ttf) $(noto_sans_gurmukhiui.ttf) \
|
||||
$(noto_sans_kannadaui.ttf) $(noto_sans_malayalamui.ttf) $(noto_sans_meeteimayek.ttf) $(noto_sans_olchiki.ttf) $(noto_sans_oriyaui.ttf) \
|
||||
$(noto_sans_sinhalaui.ttf) $(noto_sans_sorasompeng.ttf) $(noto_sans_tamilui.ttf) $(noto_sans_teluguui.ttf)
|
||||
$(FONTFORGE) -script $(MERGEFONTSSCRIPT) $(noto_sans.ttf) $(noto_sans_bengaliui.ttf) $(noto_sans_devanagariui.ttf) $(noto_sans_gujaratiui.ttf) \
|
||||
$(FONTS)/scripts/merge.py $(noto_sans.ttf) $(noto_sans_bengaliui.ttf) $(noto_sans_devanagariui.ttf) $(noto_sans_gujaratiui.ttf) \
|
||||
$(noto_sans_gurmukhiui.ttf) $(noto_sans_kannadaui.ttf) $(noto_sans_malayalamui.ttf) $(noto_sans_meeteimayek.ttf) $(noto_sans_olchiki.ttf) \
|
||||
$(noto_sans_oriyaui.ttf) $(noto_sans_sinhalaui.ttf) $(noto_sans_sorasompeng.ttf) $(noto_sans_tamilui.ttf) $(noto_sans_teluguui.ttf) \
|
||||
"NirmalaUI" "Nirmala UI" "Regular" $(nirmala.ttf)
|
||||
|
|
250
fonts/scripts/merge.py
Executable file
250
fonts/scripts/merge.py
Executable file
|
@ -0,0 +1,250 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# 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
|
||||
|
||||
"""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.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 main():
|
||||
output_filename = sys.argv[-1]
|
||||
weight = sys.argv[-2]
|
||||
font_name = sys.argv[-3]
|
||||
ps_name = sys.argv[-4]
|
||||
input_filenames = sys.argv[1:-4]
|
||||
|
||||
# Add a GSUB table to the fonts that do not have one
|
||||
for index, filename in enumerate(input_filenames):
|
||||
if not has_gsub_table(filename):
|
||||
input_filenames[index] = add_gsub_to_font(filename)
|
||||
|
||||
# Merge the fonts together
|
||||
merger = merge.Merger()
|
||||
font = merger.merge(input_filenames)
|
||||
|
||||
# Use the line metrics defined by the first font
|
||||
metrics = read_line_metrics(ttLib.TTFont(input_filenames[0]))
|
||||
set_line_metrics(font, metrics)
|
||||
|
||||
# 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()
|
|
@ -1,29 +0,0 @@
|
|||
if ($argc < 6)
|
||||
Error ("Expected arguments - Font1, Font2, Font3, ..., FontName, FullName, FontWeight, OutputFilename")
|
||||
endif
|
||||
|
||||
FONTNAME = ToString($argv[$argc - 4])
|
||||
FULLNAME = ToString($argv[$argc - 3])
|
||||
FONTWEIGHT = ToString($argv[$argc - 2])
|
||||
SRCFONTNAME = ToString($argv[1])
|
||||
DESTFONTNAME = ToString($argv[$argc - 1])
|
||||
|
||||
Open(SRCFONTNAME)
|
||||
|
||||
i = 2
|
||||
while (i < $argc - 4)
|
||||
MergeFonts($argv[i])
|
||||
i = i + 1
|
||||
endloop
|
||||
|
||||
SetFontNames(FONTNAME, FULLNAME, FULLNAME, FONTWEIGHT, "", "1.0.0")
|
||||
Generate(DESTFONTNAME)
|
||||
Close()
|
||||
|
||||
Open(DESTFONTNAME)
|
||||
Reencode("unicode")
|
||||
SelectWorthOutputting()
|
||||
SelectInvert()
|
||||
BuildAccented()
|
||||
Generate(DESTFONTNAME)
|
||||
Close()
|
Loading…
Reference in a new issue