1
0

Allow customization

This commit is contained in:
2026-03-02 12:27:57 +01:00
parent d13055124e
commit 746b8364e8
3 changed files with 52 additions and 21 deletions

View File

@@ -36,6 +36,12 @@ After running `build.py`, you should get:
python3 build.py python3 build.py
``` ```
To customize the font family name or disable old-style kerning:
```
python3 build.py --customize
```
The build script (`build.py`) uses `fontTools` and FontForge to transform the Newsreader variable fonts into Readerly. Each step is described below. The build script (`build.py`) uses `fontTools` and FontForge to transform the Newsreader variable fonts into Readerly. Each step is described below.
#### Step 1: Instancing #### Step 1: Instancing

View File

@@ -39,12 +39,14 @@ with open(os.path.join(ROOT_DIR, "VERSION")) as _vf:
with open(os.path.join(ROOT_DIR, "COPYRIGHT")) as _cf: with open(os.path.join(ROOT_DIR, "COPYRIGHT")) as _cf:
COPYRIGHT_TEXT = _cf.read().strip() COPYRIGHT_TEXT = _cf.read().strip()
VARIANTS = [ DEFAULT_FAMILY = "Readerly"
# (output_name, source_vf, wght, opsz)
("Readerly-Regular", REGULAR_VF, 450, 9), VARIANT_STYLES = [
("Readerly-Bold", REGULAR_VF, 550, 9), # (style_suffix, source_vf, wght, opsz)
("Readerly-Italic", ITALIC_VF, 450, 9), ("Regular", REGULAR_VF, 450, 9),
("Readerly-BoldItalic", ITALIC_VF, 550, 9), ("Bold", REGULAR_VF, 550, 9),
("Italic", ITALIC_VF, 450, 9),
("BoldItalic", ITALIC_VF, 550, 9),
] ]
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -152,16 +154,19 @@ def load_script_as_function(script_path):
return code return code
def build_export_script(sfd_path, ttf_path): def build_export_script(sfd_path, ttf_path, old_kern=True):
"""Build a FontForge script that opens an .sfd and exports to TTF with old-style kern.""" """Build a FontForge script that opens an .sfd and exports to TTF."""
if old_kern:
flags_line = 'flags = ("opentype", "old-kern", "no-FFTM-table", "winkern")'
else:
flags_line = 'flags = ("opentype", "no-FFTM-table")'
return textwrap.dedent(f"""\ return textwrap.dedent(f"""\
import fontforge import fontforge
f = fontforge.open({sfd_path!r}) f = fontforge.open({sfd_path!r})
print("Exporting: " + f.fontname) print("Exporting: " + f.fontname)
# Generate TTF with old-style kern table and Windows-compatible kern pairs {flags_line}
flags = ("opentype", "old-kern", "no-FFTM-table", "winkern")
f.generate({ttf_path!r}, flags=flags) f.generate({ttf_path!r}, flags=flags)
print(" -> " + {ttf_path!r}) print(" -> " + {ttf_path!r})
@@ -181,24 +186,40 @@ def main():
ff_cmd = find_fontforge() ff_cmd = find_fontforge()
print(f" FontForge: {' '.join(ff_cmd)}") print(f" FontForge: {' '.join(ff_cmd)}")
family = DEFAULT_FAMILY
old_kern = True
if "--customize" in sys.argv:
print()
family = input(f" Font family name [{DEFAULT_FAMILY}]: ").strip() or DEFAULT_FAMILY
old_kern_input = input(" Export with old-style kerning? [Y/n]: ").strip().lower()
old_kern = old_kern_input not in ("n", "no")
print()
print(f" Family: {family}")
print(f" Old kern: {'yes' if old_kern else 'no'}")
print()
tmp_dir = os.path.join(ROOT_DIR, "tmp") tmp_dir = os.path.join(ROOT_DIR, "tmp")
if os.path.exists(tmp_dir): if os.path.exists(tmp_dir):
shutil.rmtree(tmp_dir) shutil.rmtree(tmp_dir)
os.makedirs(tmp_dir) os.makedirs(tmp_dir)
try: try:
_build(tmp_dir) _build(tmp_dir, family=family, old_kern=old_kern)
finally: finally:
shutil.rmtree(tmp_dir, ignore_errors=True) shutil.rmtree(tmp_dir, ignore_errors=True)
def _build(tmp_dir): def _build(tmp_dir, family=DEFAULT_FAMILY, old_kern=True):
variant_names = [name for name, _, _, _ in VARIANTS] variants = [(f"{family}-{style}", vf, wght, opsz)
for style, vf, wght, opsz in VARIANT_STYLES]
variant_names = [name for name, _, _, _ in variants]
# Step 1: Instance variable fonts into static TTFs # Step 1: Instance variable fonts into static TTFs
print("\n── Step 1: Instance variable fonts ──\n") print("\n── Step 1: Instance variable fonts ──\n")
for name, vf_path, wght, opsz in VARIANTS: for name, vf_path, wght, opsz in variants:
ttf_out = os.path.join(tmp_dir, f"{name}.ttf") ttf_out = os.path.join(tmp_dir, f"{name}.ttf")
print(f" Instancing {name} (wght={wght}, opsz={opsz})") print(f" Instancing {name} (wght={wght}, opsz={opsz})")
@@ -218,7 +239,7 @@ def _build(tmp_dir):
print(result.stderr, file=sys.stderr) print(result.stderr, file=sys.stderr)
sys.exit(1) sys.exit(1)
print(f" {len(VARIANTS)} font(s) instanced.") print(f" {len(variants)} font(s) instanced.")
# Step 2: Apply vertical scale (opens TTF, saves as SFD) # Step 2: Apply vertical scale (opens TTF, saves as SFD)
print("\n── Step 2: Scale lowercase ──\n") print("\n── Step 2: Scale lowercase ──\n")
@@ -255,6 +276,7 @@ def _build(tmp_dir):
# Set fontname so rename.py can detect the correct style suffix # Set fontname so rename.py can detect the correct style suffix
set_fontname = f'f.fontname = {name!r}' set_fontname = f'f.fontname = {name!r}'
set_family = f'FAMILY = {family!r}'
set_version = f'VERSION = {FONT_VERSION!r}' set_version = f'VERSION = {FONT_VERSION!r}'
set_license = f'COPYRIGHT_TEXT = {COPYRIGHT_TEXT!r}' set_license = f'COPYRIGHT_TEXT = {COPYRIGHT_TEXT!r}'
@@ -262,7 +284,7 @@ def _build(tmp_dir):
("Setting vertical metrics", metrics_code), ("Setting vertical metrics", metrics_code),
("Adjusting line height", lineheight_code), ("Adjusting line height", lineheight_code),
("Setting fontname for rename", set_fontname), ("Setting fontname for rename", set_fontname),
("Updating font names", rename_code), ("Updating font names", set_family + "\n" + rename_code),
("Setting version", set_version + "\n" + version_code), ("Setting version", set_version + "\n" + version_code),
("Setting license", set_license + "\n" + license_code), ("Setting license", set_license + "\n" + license_code),
]) ])
@@ -282,7 +304,7 @@ def _build(tmp_dir):
print(f" -> {OUT_SFD_DIR}/{name}.sfd") print(f" -> {OUT_SFD_DIR}/{name}.sfd")
# Export TTF # Export TTF
script = build_export_script(sfd_path, ttf_path) script = build_export_script(sfd_path, ttf_path, old_kern=old_kern)
run_fontforge_script(script) run_fontforge_script(script)
print("\n" + "=" * 60) print("\n" + "=" * 60)

View File

@@ -1,10 +1,11 @@
""" """
FontForge: Update font name metadata FontForge: Update font name metadata
───────────────────────────────────── ─────────────────────────────────────
Replaces Newsreader references with Readerly in all name table entries Replaces Newsreader references with the target family name in all name table
and font-level properties. entries and font-level properties.
Run inside FontForge (or via build.py which sets `f` before running this). FAMILY is injected by build.py before this script runs (defaults to "Readerly").
Run inside FontForge (or via build.py which sets `f` and `FAMILY` before running this).
""" """
import fontforge import fontforge
@@ -15,7 +16,9 @@ f = fontforge.activeFont()
# CONFIGURATION # CONFIGURATION
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
FAMILY = "Readerly" # FAMILY is injected by build.py; default if run standalone
if "FAMILY" not in dir():
FAMILY = "Readerly"
# Map style suffixes to display names, PS weight strings, and OS/2 weight classes # Map style suffixes to display names, PS weight strings, and OS/2 weight classes
STYLE_MAP = { STYLE_MAP = {