Add the option to rename font

This commit is contained in:
2025-08-21 13:45:07 +02:00
parent 28673c3d49
commit d5741b1020

View File

@@ -1,6 +1,7 @@
import sys import sys
import os import os
import subprocess import subprocess
import argparse
from collections import defaultdict from collections import defaultdict
from fontTools.ttLib import TTFont, newTable from fontTools.ttLib import TTFont, newTable
from fontTools.ttLib.tables._k_e_r_n import KernTable_format_0 from fontTools.ttLib.tables._k_e_r_n import KernTable_format_0
@@ -140,48 +141,61 @@ def add_legacy_kern(font, kern_pairs):
# Name table updates # Name table updates
# ------------------------------------------------------------ # ------------------------------------------------------------
def rename_font(font, prefix): def rename_font(font, prefix, new_name=None):
"""Prefix the font's family/full names with a given prefix. """
Updates name IDs 1 (Family), 4 (Full), and 16 (Typographic Family) when present. Prefix the font's family/full names with a given prefix.
Optionally override the font name entirely using new_name.
Updates name IDs:
- 1: Family Name
- 4: Full Name
- 16: Typographic Family
""" """
if "name" not in font: if "name" not in font:
return return
name_table = font["name"] name_table = font["name"]
ids_to_prefix = {1, 4, 16} ids_to_prefix = {1, 4, 16}
for record in name_table.names: for record in name_table.names:
if record.nameID in ids_to_prefix: if record.nameID in ids_to_prefix:
try: try:
new_name = prefix + " " + record.toUnicode() base_name = new_name if new_name else record.toUnicode()
record.string = new_name.encode(record.getEncoding()) new_record_name = f"{prefix} {base_name}"
record.string = new_record_name.encode(record.getEncoding())
except Exception: except Exception:
# Fallback encoding if getEncoding fails # Fallback encoding if getEncoding fails
try: try:
record.string = (prefix + " " + record.toUnicode()).encode("utf_16_be") record.string = new_record_name.encode("utf_16_be")
except Exception: except Exception:
pass pass
def update_unique_id(font, prefix):
def update_unique_id(font, prefix, new_name=None):
""" """
Automatically prefix the font's Unique ID (nameID 3) with a given prefix. Automatically prefix the font's Unique ID (nameID 3) with a given prefix.
Keeps the version info if present, or sets a default if missing. Optionally override the font name using new_name.
Preserves version info if present, otherwise sets a default version.
Updates all records for all platforms/encodings. Updates all records for all platforms/encodings.
""" """
if "name" not in font:
return
for record in font["name"].names: for record in font["name"].names:
if record.nameID == 3: if record.nameID == 3:
current_unique = record.toUnicode() try:
current_unique = record.toUnicode()
# Split at 'Version' to preserve version info if it exists # Preserve version info if present
parts = current_unique.split("Version") parts = current_unique.split("Version")
if len(parts) == 2: version_info = "Version" + parts[1] if len(parts) == 2 else "Version 1.000"
version_info = "Version" + parts[1] base_name = new_name if new_name else parts[0].strip()
else: new_unique_id = f"{prefix} {base_name}:{version_info}"
version_info = "Version 1.000" # fallback record.string = new_unique_id.encode(record.getEncoding())
except Exception:
# Build new Unique ID with prefix # Fallback encoding
new_unique_id = prefix + " " + parts[0].strip() + ":" + version_info try:
record.string = new_unique_id.encode("utf_16_be")
# Update the record safely with proper encoding except Exception:
record.string = new_unique_id.encode(record.getEncoding()) pass
# ------------------------------------------------------------ # ------------------------------------------------------------
@@ -249,7 +263,7 @@ def check_and_fix_panose(font, filename):
# Orchestration per font # Orchestration per font
# ------------------------------------------------------------ # ------------------------------------------------------------
def process_font(path): def process_font(path, new_name):
"""Load, process, and save the font. """Load, process, and save the font.
Steps (each independent): Steps (each independent):
@@ -269,8 +283,8 @@ def process_font(path):
prefix = "KF" prefix = "KF"
# Always run name prefix & PANOSE checks, regardless of kerning outcome # Always run name prefix & PANOSE checks, regardless of kerning outcome
rename_font(font, prefix) rename_font(font, prefix, new_name)
update_unique_id(font, prefix) update_unique_id(font, prefix, new_name)
check_and_fix_panose(font, os.path.basename(path)) check_and_fix_panose(font, os.path.basename(path))
# Extract kerning (robust against missing/odd structures) # Extract kerning (robust against missing/odd structures)
@@ -285,9 +299,26 @@ def process_font(path):
except Exception as e: except Exception as e:
print(f" WARNING: Failed to extract/add kerning: {e}") print(f" WARNING: Failed to extract/add kerning: {e}")
# Save # Save the font with prefix, optional new name, and preserve style suffix
dirname, filename = os.path.split(path) dirname, _ = os.path.split(path)
out_path = os.path.join(dirname, f"{prefix}_{filename}") original_name, ext = os.path.splitext(os.path.basename(path))
# Detect the style suffix
valid_suffixes = ("-Regular", "-Bold", "-Italic", "-BoldItalic")
suffix = next((s for s in valid_suffixes if original_name.endswith(s)), "")
# Determine base name for the file
if new_name:
# Replace spaces with underscores for filenames
base_name = f"{prefix}_{new_name.replace(' ', '_')}{suffix}"
else:
# Keep original name but add prefix
base_name = f"{prefix}_{original_name}"
# Construct the full output path
out_path = os.path.join(dirname, f"{base_name}{ext.lower()}")
try: try:
font.save(out_path) font.save(out_path)
print(f" Saved: {out_path}") print(f" Saved: {out_path}")
@@ -322,30 +353,50 @@ def process_font(path):
# ------------------------------------------------------------ # ------------------------------------------------------------
def main(): def main():
if len(sys.argv) < 2: import argparse
print("Usage: python kobofix.py *.ttf *.otf")
sys.exit(1)
# --------------------------
# Parse command-line arguments
# --------------------------
parser = argparse.ArgumentParser(
description="Process fonts: add KC prefix, kern table, PANOSE validation, line adjustments."
)
parser.add_argument(
"fonts", nargs="+", help="Font files to process (*.ttf, *.otf)"
)
parser.add_argument(
"--name", type=str, help="Optional new family name for all fonts"
)
args = parser.parse_args()
# --------------------------
# Validate filenames
# --------------------------
invalid_files = [] invalid_files = []
valid_suffixes = ("-Regular", "-Bold", "-Italic", "-BoldItalic") valid_suffixes = ("-Regular", "-Bold", "-Italic", "-BoldItalic")
for path in sys.argv[1:]: for path in args.fonts:
if os.path.isfile(path) and path.lower().endswith((".ttf", ".otf")): if os.path.isfile(path) and path.lower().endswith((".ttf", ".otf")):
base = os.path.basename(path) base = os.path.basename(path)
if not base.endswith(tuple(s + ext for s in valid_suffixes for ext in (".ttf", ".otf"))): if not base.endswith(tuple(s + ext for s in valid_suffixes for ext in (".ttf", ".otf"))):
invalid_files.append(base) invalid_files.append(base)
else:
print(f"Skipping non-TTF/OTF file: {path}")
if invalid_files: if invalid_files:
print("ERROR: The following fonts have invalid filenames (must end with -Regular, -Bold, -Italic, or -BoldItalic):") print(
"ERROR: The following fonts have invalid filenames (must end with -Regular, -Bold, -Italic, or -BoldItalic):"
)
for f in invalid_files: for f in invalid_files:
print(" " + f) print(" " + f)
sys.exit(1) sys.exit(1)
for path in sys.argv[1:]: # --------------------------
# Process each font
# --------------------------
for path in args.fonts:
if os.path.isfile(path) and path.lower().endswith((".ttf", ".otf")): if os.path.isfile(path) and path.lower().endswith((".ttf", ".otf")):
process_font(path) process_font(path, new_name=args.name)
else:
print(f"Skipping non-TTF/OTF file: {path}")
if __name__ == "__main__": if __name__ == "__main__":