1
0
mirror of https://github.com/ryanoasis/nerd-fonts.git synced 2024-12-19 20:12:52 +02:00
nerd-fonts/bin/scripts/name_parser/FontnameParser.py

335 lines
16 KiB
Python
Raw Normal View History

#!/usr/bin/env python
# coding=utf8
import re
from FontnameTools import FontnameTools
class FontnameParser:
"""Parse a font name and generate all kinds of names"""
def __init__(self, filename, logger):
"""Parse a font filename and store the results"""
self.parse_ok = False
self.use_short_families = (False, False, False) # ( camelcase name, short styles, aggressive )
self.keep_regular_in_family = None # None = auto, True, False
self.suppress_preferred_if_identical = True
self.family_suff = ''
self.ps_fontname_suff = ''
self.short_family_suff = ''
self.name_subst = []
[ self.parse_ok, self._basename, self.weight_token, self.style_token, self.other_token, self._rest ] = FontnameTools.parse_font_name(filename)
self.basename = self._basename
self.rest = self._rest
self.add_name_substitution_table(FontnameTools.SIL_TABLE)
self.rename_oblique = True
self.logger = logger
def _make_ps_name(self, n, is_family):
"""Helper to limit font name length in PS names"""
fam = 'family ' if is_family else ''
limit = 31 if is_family else 63
if len(n) <= limit:
return n
r = re.search('(.*)(-.*)', n)
if not r:
new_n = n[:limit]
else:
q = limit - len(r.groups()[1])
if q < 1:
q = 1
self.logger.error('====-< Shortening too long PS {}name: Garbage warning'. format(fam))
new_n = r.groups()[0][:q] + r.groups()[1]
if new_n != n:
self.logger.error('====-< Shortening too long PS {}name: {} -> {}'.format(fam, n, new_n))
return new_n
def _shortened_name(self):
"""Return a blank free basename-rest combination"""
if not self.use_short_families[0]:
return (self.basename, self.rest)
else:
return (FontnameTools.concat(self.basename, self.rest).replace(' ', ''), '')
def set_keep_regular_in_family(self, keep):
"""Familyname may contain 'Regular' where it should normally be suppressed"""
self.keep_regular_in_family = keep
def set_expect_no_italic(self, noitalic):
"""Prevents rewriting Oblique as family name part"""
# To prevent naming clashes usually Oblique is moved out in the family name
# because some fonts have Italic and Oblique, and we want to generate pure
# RIBBI families in ID1/2.
# But some fonts have Oblique instead of Italic, here the prevential movement
# is not needed, or rather contraproductive. This can not be detected on a
# font file level but needs to be specified per family from the outside.
# Returns true if setting was successful.
if 'Italic' in self.style_token:
self.rename_oblique = True
return not noitalic
self.rename_oblique = not noitalic
return True
def set_suppress_preferred(self, suppress):
"""Suppress ID16/17 if it is identical to ID1/2 (True is default)"""
self.suppress_preferred_if_identical = suppress
def inject_suffix(self, family, ps_fontname, short_family):
"""Add a custom additonal string that shows up in the resulting names"""
self.family_suff = family.strip()
self.ps_fontname_suff = ps_fontname.replace(' ', '')
self.short_family_suff = short_family.strip()
return self
def enable_short_families(self, camelcase_name, prefix, aggressive):
"""Enable short styles in Family when (original) font name starts with prefix; enable CamelCase basename in (Typog.) Family"""
# camelcase_name is boolean
# prefix is either a string or False/True
if isinstance(prefix, str):
prefix = self._basename.startswith(prefix)
self.use_short_families = ( camelcase_name, prefix, aggressive )
return self
def add_name_substitution_table(self, table):
"""Have some fonts renamed, takes list of tuples (regex, replacement)"""
# The regex will be anchored to name begin and used case insensitive
# Replacement can have regex matches, mind to catch the correct source case
self.name_subst = table
self.basename = self._basename
self.rest = self._rest
for regex, replacement in self.name_subst:
base_and_rest = self.basename + (' ' + self.rest if len(self.rest) else '')
m = re.match(regex, base_and_rest, re.IGNORECASE)
if not m:
continue
i = len(self.basename) - len(m.group(0))
if i < 0:
self.basename = m.expand(replacement).rstrip()
self.rest = self.rest[-(i+1):].lstrip()
else:
self.basename = m.expand(replacement) + self.basename[len(m.group(0)):]
return self
def drop_for_powerline(self):
"""Remove 'for Powerline' from all names (can not be undone)"""
if 'Powerline' in self.other_token:
idx = self.other_token.index('Powerline')
self.other_token.pop(idx)
if idx > 0 and self.other_token[idx - 1] == 'For':
self.other_token.pop(idx - 1)
self._basename = re.sub(r'(\b|for\s?)?powerline\b', '', self._basename, 1, re.IGNORECASE).strip()
self.add_name_substitution_table(self.name_subst) # re-evaluate
return self
### Following the creation of the name parts:
#
# Relevant websites
# https://www.fonttutorials.com/how-to-name-font-family/
# https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids
# https://docs.microsoft.com/en-us/typography/opentype/spec/os2#fss
# https://docs.microsoft.com/en-us/typography/opentype/spec/head#macstyle
# Example (mind that they group 'semibold' as classic-group-of-4 Bold, while we will always only take bold as Bold):
# Adobe Caslon Pro Regular ID1: Adobe Caslon Pro ID2: Regular
# Adobe Caslon Pro Italic ID1: Adobe Caslon Pro ID2: Italic
# Adobe Caslon Pro Semibold ID1: Adobe Caslon Pro ID2: Bold ID16: Adobe Caslon Pro ID17: Semibold
# Adobe Caslon Pro Semibold Italic ID1: Adobe Caslon Pro ID2: Bold Italic ID16: Adobe Caslon Pro ID17: Semibold Italic
# Adobe Caslon Pro Bold ID1: Adobe Caslon Pro Bold ID2: Regular ID16: Adobe Caslon Pro ID17: Bold
# Adobe Caslon Pro Bold Italic ID1: Adobe Caslon Pro Bold ID2: Italic ID16: Adobe Caslon Pro ID17: Bold Italic
# fontname === preferred_family + preferred_styles
# fontname === family + subfamily
#
# familybase = basename + rest + other (+ suffix)
# ID 1/2 just have self.style in the subfamily, all the rest ends up in the family
# ID 16/17 have self.style and self.weight in the subfamily, the rest ends up in the family
def fullname(self):
"""Get the SFNT Fullname (ID 4)"""
styles = self.style_token
weights = self.weight_token
if self.keep_regular_in_family == None:
keep_regular = FontnameTools.is_keep_regular(self._basename + ' ' + self._rest)
else:
keep_regular = self.keep_regular_in_family
if ('Regular' in styles
and (not keep_regular
or len(self.weight_token) > 0)): # This is actually a malformed font name
styles = list(self.style_token)
styles.remove('Regular')
# For naming purposes we want Oblique to be part of the styles
(weights, styles) = FontnameTools.make_oblique_style(weights, styles)
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
(name, rest) = self._shortened_name()
if self.use_short_families[1]:
[ weights, styles ] = FontnameTools.short_styles([ weights, styles ], self.use_short_families[2])
return FontnameTools.concat(name, rest, self.other_token, self.short_family_suff, weights, styles)
def psname(self):
"""Get the SFNT PostScriptName (ID 6)"""
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
# This is almost self.family() + '-' + self.subfamily()
(name, rest) = self._shortened_name()
styles = self.style_token
weights = self.weight_token
if self.use_short_families[1]:
styles = FontnameTools.short_styles(styles, self.use_short_families[2])
weights = FontnameTools.short_styles(weights, self.use_short_families[2])
fam = FontnameTools.camel_casify(FontnameTools.concat(name, rest, self.other_token, self.ps_fontname_suff))
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
sub = FontnameTools.camel_casify(FontnameTools.concat(weights, styles))
if len(sub) > 0:
sub = '-' + sub
fam = FontnameTools.postscript_char_filter(fam)
sub = FontnameTools.postscript_char_filter(sub)
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
return self._make_ps_name(fam + sub, False)
def preferred_family(self):
"""Get the SFNT Preferred Familyname (ID 16)"""
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
(name, rest) = self._shortened_name()
pfn = FontnameTools.concat(name, rest, self.other_token, self.family_suff)
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
if self.suppress_preferred_if_identical and pfn == self.family():
# Do not set if identical to ID 1
return ''
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
return pfn
def preferred_styles(self):
"""Get the SFNT Preferred Styles (ID 17)"""
styles = self.style_token
weights = self.weight_token
# For naming purposes we want Oblique to be part of the styles
(weights, styles) = FontnameTools.make_oblique_style(weights, styles)
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
pfs = FontnameTools.concat(weights, styles)
if self.suppress_preferred_if_identical and pfs == self.subfamily():
# Do not set if identical to ID 2
return ''
return pfs
def family(self):
"""Get the SFNT Familyname (ID 1)"""
# We use the short form of the styles to save on number of chars
(name, rest) = self._shortened_name()
other = self.other_token
weights = self.weight_token
aggressive = self.use_short_families[2]
if not self.rename_oblique:
(weights, styles) = FontnameTools.make_oblique_style(weights, [])
if self.use_short_families[1]:
[ other, weights ] = FontnameTools.short_styles([ other, weights ], aggressive)
weights = [ w if w != 'Oblique' else 'Obl' for w in weights ]
return FontnameTools.concat(name, rest, other, self.short_family_suff, weights)
def subfamily(self):
"""Get the SFNT SubFamily (ID 2)"""
styles = self.style_token
weights = self.weight_token
if not self.rename_oblique:
(weights, styles) = FontnameTools.make_oblique_style(weights, styles)
if len(styles) == 0:
if 'Oblique' in weights:
return FontnameTools.concat(styles, 'Italic')
return 'Regular'
if 'Oblique' in weights and not 'Italic' in styles:
return FontnameTools.concat(styles, 'Italic')
return FontnameTools.concat(styles)
def ps_familyname(self):
"""Get the PS Familyname"""
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
fam = self.preferred_family()
if len(fam) < 1:
fam = self.family()
return self._make_ps_name(fam, True)
def macstyle(self, style):
"""Modify a given macStyle value for current name, just bits 0 and 1 touched"""
b = style & (~3)
b |= 1 if 'Bold' in self.style_token else 0
b |= 2 if 'Italic' in self.style_token else 0
return b
def fs_selection(self, fs):
"""Modify a given fsSelection value for current name, bits 0, 5, 6, 8, 9 touched"""
ITALIC = 1 << 0; BOLD = 1 << 5; REGULAR = 1 << 6; WWS = 1 << 8; OBLIQUE = 1 << 9
b = fs & (~(ITALIC | BOLD | REGULAR | WWS | OBLIQUE))
if 'Bold' in self.style_token:
b |= BOLD
# Ignore Italic if we have Oblique
if 'Oblique' in self.weight_token:
b |= OBLIQUE
elif 'Italic' in self.style_token:
b |= ITALIC
# Regular is just the basic weight
if len(self.weight_token) == 0:
b |= REGULAR
b |= WWS # We assert this by our naming process
return b
def checklen(self, max_len, entry_id, name):
"""Check the length of a name string and report violations"""
if len(name) <= max_len:
self.logger.debug('=====> {:18} ok ({:2} <={:2}): {}'.format(entry_id, len(name), max_len, name))
else:
self.logger.error('====-< {:18} too long ({:2} > {:2}): {}'.format(entry_id, len(name), max_len, name))
return name
def rename_font(self, font):
"""Rename the font to include all information we found (font is fontforge font object)"""
font.fondname = None
name-parser: Change naming behavior [why] We created two fonts, one 'normal' and one 'Windows Compatible'. The later one used shorter names because there are some Microsoft applications that can not work with long (family-) names. But it would be better to have universally usable fonts, like all our sourcefonts. It would reduce confusion and also reduce the size of installation packages. [how] ID 1 (Family) family() Unchanged Take the short form font name. Appends depending on mode short or long weight. ID 2 (SubFamily) subfamily() Unchanged Transmogifies Oblique to Italic. Returns long form styles (i.e. RIBBI) ID 4 (Fullname) CFF.Fullname fullname() Take the short form font name (instead of long). Depending on mode take short or long styles. This is now in line with ID 4 = ID 1 + ID 2 ID 6 (PS Fullname) CFF.FontName psname() We limited the length to 31 characters, which is sometimes too short to have different names for fonts of one same family. Now we take the short forms of name suffix and styles, for example VictorMonoNFM-BdIt ID 16 (Typogr. Family) preferred_family() The name itself is unchanged (short form name plus verbose suffix), but the suppression check has been fixed to really compare the resulting name with the ID 1 name. ID 17 (Typogr. Subfamily) preferred_styles() This is unchanged (long form styles) but the suppression check has been fixed to really compare the resulting string with ID 2. CFF.Familyname ps_familyname() Uses ID 16 now and ID 1 as fallback. Was always ID 1 before, which is wrong. Signed-off-by: Fini Jastrow <ulf.fini.jastrow@desy.de>
2023-04-07 16:29:32 +02:00
font.fontname = self.psname()
font.fullname = self.fullname()
font.familyname = self.ps_familyname()
# We have to work around several issues in fontforge:
#
# a. Remove some entries from SFNT table; fontforge has no API function for that
#
# b. Fontforge does not allow to set SubFamily (and other) to any value:
#
# Fontforge lets you set any value, unless it is the default value. If it
# is the default value it does not set anything. It also does not remove
# a previously existing non-default value. Why it is done this way is
# unclear:
# fontforge/python.c SetSFNTName() line 11431
# return( 1 ); /* If they set it to the default, there's nothing to do */
#
# Then is the question: What is the default? It is taken from the
# currently set fontname (??!). The fontname is parsed and everything
# behind the dash is the default SubFamily:
# fontforge/tottf.c DefaultTTFEnglishNames()
# fontforge/splinefont.c _GetModifiers()
#
# To fix this without touching Fontforge we need to set the SubFamily
# directly in the SFNT table:
#
# c. Fontforge has the bug that it allows to write empty-string to a SFNT field
# and it is actually embedded as empty string, but empty strings are not
# shown if you query the sfnt_names *rolleyes*
version_tag = ''
sfnt_list = []
TO_DEL = ['Family', 'SubFamily', 'Fullname', 'PostScriptName', 'Preferred Family',
'Preferred Styles', 'Compatible Full', 'WWS Family', 'WWS Subfamily',
'UniqueID', 'CID findfont Name']
# Remove these entries in all languages and add (at least the vital ones) some
# back, but only as 'English (US)'. This makes sure we do not leave contradicting
# names over different languages.
for l, k, v in list(font.sfnt_names):
if not k in TO_DEL:
sfnt_list += [( l, k, v )]
if k == 'Version' and l == 'English (US)':
version_tag = ' ' + v.split()[-1]
sfnt_list += [( 'English (US)', 'Family', self.checklen(31, 'Family (ID 1)', self.family()) )] # 1
sfnt_list += [( 'English (US)', 'SubFamily', self.checklen(31, 'SubFamily (ID 2)', self.subfamily()) )] # 2
sfnt_list += [( 'English (US)', 'UniqueID', self.fullname() + version_tag )] # 3
sfnt_list += [( 'English (US)', 'Fullname', self.checklen(63, 'Fullname (ID 4)', self.fullname()) )] # 4
sfnt_list += [( 'English (US)', 'PostScriptName', self.checklen(63, 'PSN (ID 6)', self.psname()) )] # 6
p_fam = self.preferred_family()
if len(p_fam):
sfnt_list += [( 'English (US)', 'Preferred Family', self.checklen(31, 'PrefFamily (ID 16)', p_fam) )] # 16
p_sty = self.preferred_styles()
if len(p_sty):
sfnt_list += [( 'English (US)', 'Preferred Styles', self.checklen(31, 'PrefStyles (ID 17)', p_sty) )] # 17
font.sfnt_names = tuple(sfnt_list)
font.macstyle = self.macstyle(0)
font.os2_stylemap = self.fs_selection(0)