1
0
mirror of https://github.com/google/fonts.git synced 2024-11-24 09:43:46 +03:00
fonts/tools/namelist.py

215 lines
7.4 KiB
Python
Executable File

#!/usr/bin/env python
# Copyright 2015, Google Inc.
# Author: Dave Crossland (dave@understandinglimited.com)
#
# 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.
#
# namelist.py: A fontTools python script for generating namelist files
#
# Usage:
# # To make a Namelist from a font file.
# $ namelist.py Font.ttf > NameList.nam
#
# # To reformat an existing Namelist.
# $ ./namelist.py reformat NameList.nam > NameList.nam
#
# # To reformat all Namelists in a directory.
# $ find encodings/GF\ Glyph\ Sets/ -type f -name "*.nam" -exec \
# bash -c './namelist.py reformat "{}" > "{}__tmp" && mv "{}__tmp" "{}"' \;
#
# # To generate "uni names" and "nice names" filter lists a Namelist
# # This will create the needed directories if missing
# $ ./namelist.py generate-filter-lists NameList.nam
#
# # To generate "uni names" and "nice names" filter lists for all Namelists
# $ find encodings/GF\ Glyph\ Sets/ -type f -name "*.nam" -exec \
# ./namelist.py generate-filter-lists "{}" \;
from __future__ import print_function, unicode_literals
import sys
import os
from fontTools.ttLib import TTFont
from fontTools.unicode import Unicode
import codecs
from util import google_fonts
from util import filter_lists
def _get_basechar_unicode(name):
codepoint = filter_lists.get_unicode_by_name(name)
if codepoint is not None:
return codepoint;
if '_' in name:
# use the first ligature component as base char
return _get_basechar_unicode(name.split('_')[0])
if '.' in name:
return _get_basechar_unicode(name.split('.')[0])
return None
def _sortkey_namelist_entries(entry):
codepoint, name, _, _ = entry
base_codepoint = None
extension = None
if name == 'NULL':
codepoint = -float('inf')
if name:
if '.' in name:
extension = name.split('.')[1]
base_codepoint = _get_basechar_unicode(name)
return tuple([
codepoint if codepoint is not None else float('inf')
# keep glyphs of one OT-Feature sticking together
, extension
, base_codepoint if base_codepoint is not None else float('inf')
, name
])
def reformat_namelist(filename, out=None):
if out is None:
out = sys.stdout
if filename == '-':
_reformat_namelist(codecs.getreader('utf8')(sys.stdin), out)
return
with codecs.open(filename, 'r', encoding='utf-8') as f:
_reformat_namelist(f, out)
def _reformat_namelist(f, out=None):
entries = []
before = []
header = []
for line in f:
line = line.rstrip()
if (not entries and not before) and line.startswith('#'):
header.append(line)
continue
entry = None
if line.startswith('0x'):
# uni chr
codepoint = google_fonts.get_codepoint_from_line(line)
entry = (codepoint, None, line)
elif line.startswith(' '):
# unencoded name
name = filter_lists.translate_name(line.rsplit(' ', 1)[1])
entry = (None, name, line)
if entry is not None:
entry += (before, )
before = []
entries.append(entry)
else:
# these lines will stick before the next entry
before.append(line)
entries.sort(key=_sortkey_namelist_entries)
_print = lambda *args: print(*args,file=out)
map(_print, header)
for codepoint, name, original, item_before in entries:
map(_print, item_before)
if codepoint is not None:
_print(format_codepoint(codepoint))
elif name is not None:
_print((' '*9 + name))
# output left over lines at the end of the file
map(_print, before)
def _names_generator(filename):
with codecs.open(filename, 'r', encoding='utf-8') as f:
for line in f:
line = line.rstrip()
if line.startswith('0x'):
# uni chr
codepoint = google_fonts.get_codepoint_from_line(line)
name = filter_lists.get_name_by_unicode(codepoint)
if name is None:
prefix = 'u' if codepoint > 0xFFFF else 'uni'
name = '{0}{1:04X}'.format(prefix, codepoint)
yield name
elif line.startswith(' ' * 6):
# unencoded name
yield line.rsplit(' ', 1)[1]
def _mkdir(path):
try:
os.makedirs(path)
except OSError as exc:
if not os.path.isdir(path):
raise exc
def generate_filter_lists(filename):
# 'GF-{script}-rest.nam' => {script}-rest
basename = os.path.basename(filename).split('.', 1)[0].split('-', 2)[-1]
filerListFileName = '{0}.txt'.format(basename)
dirname = os.path.dirname(filename)
nice_names_filename = os.path.join(dirname, 'filter lists', 'nice names', filerListFileName)
prod_names_filename = os.path.join(dirname, 'filter lists', 'uni names', filerListFileName)
_mkdir(os.path.dirname(nice_names_filename))
_mkdir(os.path.dirname(prod_names_filename))
with codecs.open(nice_names_filename, 'w', encoding='utf-8') as niceNamesFile, \
codecs.open(prod_names_filename, 'w', encoding='utf-8') as prodNamesFile:
for name in _names_generator(filename):
print(filter_lists.translate_name(name, production_name=False), file=niceNamesFile)
print(filter_lists.translate_name(name, production_name=True), file=prodNamesFile)
def _format_codepoint(codepoint):
if 0xE000 <= codepoint <= 0xF8FF:
item_description = 'PRIVATE USE AREA U+{0:04X}'.format(codepoint)
char = ' '
elif codepoint == 0x000D:
# Special case, this only happens in Latin-core.
# FIXME: we should consider remover CR from Latin-core
item_description = 'CR'
char = ' '
else:
item_description = Unicode[codepoint].decode('utf-8')
char = unichr(codepoint)
return ('0x{0:04X}'.format(codepoint)
, char
, item_description)
def format_codepoint(codepoint):
return ' '.join(_format_codepoint(codepoint))
def namelist_from_font(file_name, out=None):
if out is None:
out = sys.stdout
excluded_chars = ["????", "SPACE", "NO-BREAK SPACE"]
font = TTFont(file_name)
charcodes = set()
for cmap in font["cmap"].tables:
if not cmap.isUnicode():
continue
charcodes.update(cp for cp,name in cmap.cmap.items())
charcodes = sorted(charcodes)
for charcode in charcodes:
hexchar, char, item_description = _format_codepoint(charcode)
if item_description not in excluded_chars:
print(hexchar, char, item_description, file=out)
font.close()
def main(*args):
if args[0] == 'reformat':
reformat_namelist(args[1])
elif args[0] == 'generate-filter-lists':
generate_filter_lists(args[1])
else:
namelist_from_font(args[0])
if __name__ == '__main__':
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
main(*sys.argv[1:])