mirror of
https://github.com/nicoverbruggen/kobo-font-fix.git
synced 2026-03-29 05:20:08 +02:00
Max 10k pairs per kern subtable (Fixes #1)
- Fixes script - Adds kern diff script to compare fonts to helpers - Moved ttfconv to helpers folder - Updated README
This commit is contained in:
@@ -2,9 +2,6 @@
|
||||
|
||||
## Overview
|
||||
|
||||
> [!WARNING]
|
||||
> Currently, the `kern` table workaround does not appear to work correctly and requires further investigation. This script is still under development.
|
||||
|
||||
`kobofix.py` is a Python script designed to process TTF fonts for Kobo e-readers.
|
||||
|
||||
It generates a renamed font, fixes PANOSE information based on the filename, adjusts the baseline with the `font-line` utility, and adds a legacy `kern` table which allows the `kepub` engine for improved rendering of kerned pairs.
|
||||
@@ -64,9 +61,11 @@ To process fonts from my [ebook-fonts](https://github.com/nicoverbruggen/ebook-f
|
||||
To process all fonts with the "Kobo Fix" preset, simply run:
|
||||
|
||||
```bash
|
||||
./kobofix.py --prefix KF --remove-prefix="NV" --line-percent 20 *.ttf
|
||||
./kobofix.py --prefix KF --remove-prefix="NV" --line-percent 0 *.ttf
|
||||
```
|
||||
|
||||
(In this case, we'll set --line-percent to 0 so the line height changes aren't made, because the fonts in the NV Collection should already have those changes applied.)
|
||||
|
||||
### Generating NV fonts
|
||||
|
||||
Tight spacing, with a custom font family name:
|
||||
|
||||
65
helpers/kerndiff.py
Normal file
65
helpers/kerndiff.py
Normal file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
from fontTools.ttLib import TTFont
|
||||
|
||||
def analyze_kern_table(font_path):
|
||||
"""
|
||||
Loads a font and analyzes its 'kern' table, if present.
|
||||
"""
|
||||
try:
|
||||
font = TTFont(font_path)
|
||||
except Exception as e:
|
||||
print(f"Error: Could not open font file at {font_path}. Reason: {e}")
|
||||
return
|
||||
|
||||
print(f"--- Analyzing 'kern' table in: {font_path} ---")
|
||||
|
||||
if 'kern' not in font:
|
||||
print("No 'kern' table found.")
|
||||
return
|
||||
|
||||
kern_table = font['kern']
|
||||
print(f" > Table version: {kern_table.version}")
|
||||
|
||||
if not hasattr(kern_table, 'kernTables') or not kern_table.kernTables:
|
||||
print(" > No subtables found.")
|
||||
return
|
||||
|
||||
print(f" > Number of subtables: {len(kern_table.kernTables)}")
|
||||
|
||||
for i, subtable in enumerate(kern_table.kernTables):
|
||||
print(f"\n --- Subtable {i+1} ---")
|
||||
if hasattr(subtable, 'coverage'):
|
||||
print(f" > Coverage flags (as integer): {subtable.coverage}")
|
||||
# A more detailed breakdown of flags
|
||||
coverage_int = int(subtable.coverage)
|
||||
print(f" > Coverage flags breakdown:")
|
||||
print(f" - Horizontal Kerning: {'Yes' if coverage_int & 1 else 'No'} (bit 0)")
|
||||
print(f" - Minimum Values: {'Yes' if coverage_int & 2 else 'No'} (bit 1)")
|
||||
print(f" - Cross-stream Kerning: {'Yes' if coverage_int & 4 else 'No'} (bit 2)")
|
||||
print(f" - Variation Kerning: {'Yes' if coverage_int & 8 else 'No'} (bit 3)")
|
||||
|
||||
if hasattr(subtable, 'kernTable'):
|
||||
print(f" > Found {len(subtable.kernTable)} kerning pairs.")
|
||||
# Print the first 20 kerning pairs and values for inspection
|
||||
print(" > Sample of kerning pairs (glyph1, glyph2) -> value:")
|
||||
for pair, value in list(subtable.kernTable.items())[:20]:
|
||||
print(f" - ({pair[0]}, {pair[1]}) -> {value}")
|
||||
else:
|
||||
print(" > Subtable has no 'kernTable' attribute.")
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main function to compare two fonts.
|
||||
"""
|
||||
# Replace these with the actual paths to your font files
|
||||
working_font = "./KC_Garamond-Regular.ttf"
|
||||
broken_font = "./KF_Garamond-Regular.ttf"
|
||||
|
||||
analyze_kern_table(working_font)
|
||||
print("\n" + "="*50 + "\n")
|
||||
analyze_kern_table(broken_font)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
36
kobofix.py
36
kobofix.py
@@ -341,30 +341,32 @@ class FontProcessor:
|
||||
def add_legacy_kern(font: TTFont, kern_pairs: Dict[Tuple[str, str], int]) -> int:
|
||||
"""
|
||||
Create or replace a legacy 'kern' table with the supplied pairs.
|
||||
Older devices like some Kobo models only recognize the 'kern' table.
|
||||
This function creates a new `kern` table from the extracted GPOS pairs.
|
||||
Splits into multiple subtables if there are more than 10,000 pairs.
|
||||
"""
|
||||
if not kern_pairs:
|
||||
return 0
|
||||
|
||||
|
||||
kern_table = newTable("kern")
|
||||
kern_table.version = 0
|
||||
kern_table.kernTables = []
|
||||
|
||||
subtable = KernTable_format_0()
|
||||
subtable.version = 0
|
||||
subtable.length = None
|
||||
subtable.coverage = 1
|
||||
subtable.kernTable = {
|
||||
tuple(k): int(v)
|
||||
for k, v in kern_pairs.items()
|
||||
if v
|
||||
}
|
||||
kern_table.kernTables.append(subtable)
|
||||
|
||||
# Max pairs per subtable
|
||||
MAX_PAIRS = 10000
|
||||
items = [(tuple(k), int(v)) for k, v in kern_pairs.items() if v]
|
||||
|
||||
for i in range(0, len(items), MAX_PAIRS):
|
||||
chunk = dict(items[i:i + MAX_PAIRS])
|
||||
subtable = KernTable_format_0()
|
||||
subtable.version = 0
|
||||
subtable.length = None
|
||||
subtable.coverage = 1
|
||||
subtable.kernTable = chunk
|
||||
kern_table.kernTables.append(subtable)
|
||||
|
||||
font["kern"] = kern_table
|
||||
|
||||
return len(subtable.kernTable)
|
||||
|
||||
|
||||
return len(items)
|
||||
|
||||
# ============================================================
|
||||
# Name table methods
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user