mirror of
https://github.com/nicoverbruggen/kobo-font-fix.git
synced 2025-11-05 09:30:08 +01:00
Compare commits
5 Commits
v0.1
...
1ed3677d8e
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ed3677d8e | |||
| da8b3631ea | |||
| 1dd9a6ab79 | |||
| 27c3aaf522 | |||
| 507cb87fe1 |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Nico Verbruggen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
103
README.md
103
README.md
@@ -2,12 +2,16 @@
|
||||
|
||||
## Overview
|
||||
|
||||
`kobofix.py` is a Python script designed to process TTF fonts for Kobo e-readers.
|
||||
**`kobofix.py` is a Python script designed to process and adjust TTF fonts for Kobo e-readers for a better reading experience with the default `kepub` renderer.**
|
||||
|
||||
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.
|
||||
|
||||
You can use this to modify or fix your own, legally acquired fonts (assuming you are permitted to do so).
|
||||
|
||||
## License
|
||||
|
||||
Licensed under the [MIT License](/LICENSE).
|
||||
|
||||
## Requirements
|
||||
|
||||
Python 3, FontTools, `font-line`.
|
||||
@@ -29,23 +33,53 @@ source ~/.zshrc
|
||||
|
||||
## Usage
|
||||
|
||||
1. Open a terminal and navigate to the directory containing your font files.
|
||||
2. Run the script with a glob pattern to include all TTF files:
|
||||
Open a terminal and navigate to the directory containing your font files. Make sure your font files are named correctly. The script will process files that contain the string:
|
||||
|
||||
```bash
|
||||
python3 kobofix.py *.ttf
|
||||
```
|
||||
3. The default script will:
|
||||
- `Regular`
|
||||
- `Italic`
|
||||
- `Bold`
|
||||
- `BoldItalic`
|
||||
|
||||
* Validate the file names (must end with `-Regular`, `-Bold`, `-Italic` or `-BoldItalic` so they're valid for Kobo devices).
|
||||
* Process each font (e.g. "Lora" becomes "KF Lora").
|
||||
* Apply kerning, rename, PANOSE adjustments, and baseline shift.
|
||||
* Save output as `KF_<original_filename>`.
|
||||
This is the naming convention used on Kobo devices for proper compatibility with both the `epub` and `kepub` renderer.
|
||||
|
||||
You can customize what the script does.
|
||||
You can then run:
|
||||
|
||||
```bash
|
||||
python3 kobofix.py ./src/*.ttf
|
||||
```
|
||||
|
||||
By default, the script will:
|
||||
|
||||
1. **Validate all filenames.** If there are any invalid filenames, you will be prompted and can continue with all valid filenames, but it is recommended that you fix the invalid files.
|
||||
2. **Remove any WWS name metadata from the font.** This is done because the font is renamed afterwards.
|
||||
3. **Modify the internal name of the font.** Unless a new name was specified, this is merely a prefix that is applied. (By default, this is `KF`.)
|
||||
4. **PANOSE metadata is checked and fixed.** Sometimes, the PANOSE information does not match the font style. This is often an oversight but it causes issues on Kobo devices, so this fixes that.
|
||||
5. **Font weight metadata is updated.** There's other metadata that is part of the font that reflects the weight of the font. In case this information needs to be modified, it is adjusted.
|
||||
6. **Kern pairs from the GPOS table are copied to the legacy `kern` table.** This only applies to fonts that have a GPOS table, which is used for kerning in modern fonts.
|
||||
7. **The `font-line` helper is used to apply a 20% line-height setting.** This generates a new file which is immediately renamed to the desired output format.
|
||||
|
||||
The modified fonts are saved in the directory where the original fonts are located.
|
||||
|
||||
## Customization
|
||||
|
||||
You can customize what the script does. For more information, consult:
|
||||
|
||||
```bash
|
||||
./kobofix.py -h
|
||||
```
|
||||
|
||||
Given the right arguments, you can:
|
||||
- Skip the `kern` step
|
||||
- Use a custom name for a font
|
||||
- Use a custom name for the prefix
|
||||
- Remove the `GPOS` table entirely
|
||||
- Adjust the percentage of the `font-line` setting
|
||||
- Skip running `font-line` altogether
|
||||
|
||||
For debugging purposes, you can run the script with the `--verbose` flag.
|
||||
|
||||
## Examples
|
||||
|
||||
### Generating KF fonts
|
||||
|
||||
This applies the KF prefix, applies 20 percent line spacing and adds a Kobo `kern` table. Ideal if you have an existing TrueType font and you want it on your Kobo device.
|
||||
@@ -53,7 +87,7 @@ This applies the KF prefix, applies 20 percent line spacing and adds a Kobo `ker
|
||||
The `--name` parameter is used to change the name of the font family.
|
||||
|
||||
```bash
|
||||
./kobofix.py --prefix KF --name="Fonty" --line-percent 20 *.ttf
|
||||
./kobofix.py --prefix KF --name="Fonty" --line-percent 20 --remove-hints *.ttf
|
||||
```
|
||||
|
||||
To process fonts from my [ebook-fonts](https://github.com/nicoverbruggen/ebook-fonts) collection which are prefixed with "NV", you can replace the prefix and make adjustments in bulk.
|
||||
@@ -66,6 +100,47 @@ To process all fonts with the "Kobo Fix" preset, simply run:
|
||||
|
||||
(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.)
|
||||
|
||||
The expected output is then:
|
||||
|
||||
```
|
||||
nico@m1ni kobo-font-fix % ./kobofix.py --prefix KF --remove-prefix NV *.ttf --line-percent 0
|
||||
|
||||
Processing: NV-Elstob-Bold.ttf
|
||||
--remove-prefix enabled: using 'Elstob' as the new family name.
|
||||
Renaming the font to: KF Elstob Bold
|
||||
PANOSE corrected: bWeight 8->8, bLetterForm 2->2
|
||||
Kerning: extracted 342467 pairs; wrote 342467 to legacy 'kern' table.
|
||||
Saved: KF_Elstob-Bold.ttf
|
||||
Skipping line adjustment step.
|
||||
|
||||
Processing: NV-Elstob-BoldItalic.ttf
|
||||
--remove-prefix enabled: using 'Elstob' as the new family name.
|
||||
Renaming the font to: KF Elstob Bold Italic
|
||||
PANOSE corrected: bWeight 8->8, bLetterForm 3->3
|
||||
Kerning: extracted 300746 pairs; wrote 300746 to legacy 'kern' table.
|
||||
Saved: KF_Elstob-BoldItalic.ttf
|
||||
Skipping line adjustment step.
|
||||
|
||||
Processing: NV-Elstob-Italic.ttf
|
||||
--remove-prefix enabled: using 'Elstob' as the new family name.
|
||||
Renaming the font to: KF Elstob Italic
|
||||
PANOSE corrected: bWeight 5->5, bLetterForm 3->3
|
||||
Kerning: extracted 286857 pairs; wrote 286856 to legacy 'kern' table.
|
||||
Saved: KF_Elstob-Italic.ttf
|
||||
Skipping line adjustment step.
|
||||
|
||||
Processing: NV-Elstob-Regular.ttf
|
||||
--remove-prefix enabled: using 'Elstob' as the new family name.
|
||||
Renaming the font to: KF Elstob
|
||||
PANOSE corrected: bWeight 5->5, bLetterForm 2->2
|
||||
Kerning: extracted 313998 pairs; wrote 313998 to legacy 'kern' table.
|
||||
Saved: KF_Elstob-Regular.ttf
|
||||
Skipping line adjustment step.
|
||||
|
||||
==================================================
|
||||
Processed 4/4 fonts successfully.
|
||||
```
|
||||
|
||||
### Generating NV fonts
|
||||
|
||||
Tight spacing, with a custom font family name:
|
||||
@@ -80,4 +155,4 @@ Relaxed spacing, with a custom font family name:
|
||||
./kobofix.py --prefix NV --name="Fonty" --line-percent 50 --skip-kobo-kern *.ttf
|
||||
```
|
||||
|
||||
You can play around with `--line-percent` to see what works for you.
|
||||
You can play around with `--line-percent` to see what works for you.
|
||||
199
kobofix.py
199
kobofix.py
@@ -210,8 +210,13 @@ class FontProcessor:
|
||||
full_name = f"{family_name}"
|
||||
if style_name != "Regular":
|
||||
full_name += f" {style_name}"
|
||||
|
||||
ps_name = f"{self.prefix}_{family_name.replace(' ', '-')}"
|
||||
|
||||
# If prefix is empty, don't add it to the PS name
|
||||
if self.prefix:
|
||||
ps_name = f"{self.prefix}_{family_name.replace(' ', '-')}"
|
||||
else:
|
||||
ps_name = family_name.replace(' ', '-')
|
||||
|
||||
if style_name != "Regular":
|
||||
ps_name += f"-{style_name.replace(' ', '')}"
|
||||
|
||||
@@ -232,92 +237,107 @@ class FontProcessor:
|
||||
def _pair_value_to_kern(value1, value2) -> int:
|
||||
"""
|
||||
Compute a legacy kerning value from GPOS PairValue records.
|
||||
|
||||
This logic is specific to converting GPOS (OpenType) kerning to
|
||||
the older 'kern' (TrueType) table format.
|
||||
|
||||
Note: Only XAdvance values are used, as they directly map to kern table semantics
|
||||
(adjusting inter-character spacing). XPlacement values shift glyphs without
|
||||
affecting spacing and cannot be represented in the legacy kern table. To avoid
|
||||
potential issues, XPlacement values are now being ignored.
|
||||
"""
|
||||
kern_value = 0
|
||||
if value1 is not None:
|
||||
kern_value += getattr(value1, "XAdvance", 0) or 0
|
||||
if value2 is not None:
|
||||
kern_value += getattr(value2, "XAdvance", 0) or 0
|
||||
|
||||
if kern_value == 0:
|
||||
if value1 is not None:
|
||||
kern_value += getattr(value1, "XPlacement", 0) or 0
|
||||
if value2 is not None:
|
||||
kern_value += getattr(value2, "XPlacement", 0) or 0
|
||||
|
||||
|
||||
return int(kern_value)
|
||||
|
||||
def _extract_format1_pairs(self, subtable) -> Dict[Tuple[str, str], int]:
|
||||
"""Extract kerning pairs from PairPos Format 1 (per-glyph PairSets)."""
|
||||
pairs = defaultdict(int)
|
||||
pairs = {}
|
||||
coverage = getattr(subtable, "Coverage", None)
|
||||
pair_sets = getattr(subtable, "PairSet", [])
|
||||
|
||||
|
||||
if not coverage or not hasattr(coverage, "glyphs"):
|
||||
return pairs
|
||||
|
||||
|
||||
for idx, left_glyph in enumerate(coverage.glyphs):
|
||||
if idx >= len(pair_sets):
|
||||
break
|
||||
|
||||
|
||||
for record in getattr(pair_sets[idx], "PairValueRecord", []):
|
||||
right_glyph = record.SecondGlyph
|
||||
kern_value = self._pair_value_to_kern(record.Value1, record.Value2)
|
||||
if kern_value:
|
||||
pairs[(left_glyph, right_glyph)] += kern_value
|
||||
# Only set if not already present (first value wins)
|
||||
key = (left_glyph, right_glyph)
|
||||
if key not in pairs:
|
||||
pairs[key] = kern_value
|
||||
return pairs
|
||||
|
||||
def _extract_format2_pairs(self, subtable) -> Dict[Tuple[str, str], int]:
|
||||
"""Extract kerning pairs from PairPos Format 2 (class-based)."""
|
||||
pairs = defaultdict(int)
|
||||
pairs = {}
|
||||
coverage = getattr(subtable, "Coverage", None)
|
||||
class_def1 = getattr(subtable, "ClassDef1", None)
|
||||
class_def2 = getattr(subtable, "ClassDef2", None)
|
||||
class1_records = getattr(subtable, "Class1Record", [])
|
||||
|
||||
|
||||
if not coverage or not hasattr(coverage, "glyphs"):
|
||||
return pairs
|
||||
|
||||
|
||||
class1_map = getattr(class_def1, "classDefs", {}) if class_def1 else {}
|
||||
left_by_class = defaultdict(list)
|
||||
for glyph in coverage.glyphs:
|
||||
class_idx = class1_map.get(glyph, 0)
|
||||
left_by_class[class_idx].append(glyph)
|
||||
|
||||
|
||||
class2_map = getattr(class_def2, "classDefs", {}) if class_def2 else {}
|
||||
right_by_class = defaultdict(list)
|
||||
for glyph, class_idx in class2_map.items():
|
||||
right_by_class[class_idx].append(glyph)
|
||||
|
||||
|
||||
for class1_idx, class1_record in enumerate(class1_records):
|
||||
left_glyphs = left_by_class.get(class1_idx, [])
|
||||
if not left_glyphs:
|
||||
continue
|
||||
|
||||
|
||||
for class2_idx, class2_record in enumerate(class1_record.Class2Record):
|
||||
right_glyphs = right_by_class.get(class2_idx, [])
|
||||
if not right_glyphs:
|
||||
continue
|
||||
|
||||
|
||||
kern_value = self._pair_value_to_kern(class2_record.Value1, class2_record.Value2)
|
||||
if not kern_value:
|
||||
continue
|
||||
|
||||
|
||||
for left in left_glyphs:
|
||||
for right in right_glyphs:
|
||||
pairs[(left, right)] += kern_value
|
||||
# Only set if not already present (first value wins)
|
||||
key = (left, right)
|
||||
if key not in pairs:
|
||||
pairs[key] = kern_value
|
||||
return pairs
|
||||
|
||||
def extract_kern_pairs(self, font: TTFont) -> Dict[Tuple[str, str], int]:
|
||||
"""
|
||||
Extract all kerning pairs from GPOS PairPos lookups.
|
||||
Extract kerning pairs from the font.
|
||||
Prioritizes existing 'kern' table over GPOS data if present.
|
||||
GPOS (Glyph Positioning) is the modern standard for kerning in OpenType fonts.
|
||||
This function iterates through the GPOS tables to find all kerning pairs
|
||||
before we convert them to the legacy 'kern' table format.
|
||||
"""
|
||||
pairs = defaultdict(int)
|
||||
pairs = {}
|
||||
|
||||
# If a kern table already exists, use it instead of GPOS
|
||||
if "kern" in font:
|
||||
kern_table = font["kern"]
|
||||
for subtable in getattr(kern_table, "kernTables", []):
|
||||
if hasattr(subtable, "kernTable"):
|
||||
pairs.update(subtable.kernTable)
|
||||
return pairs
|
||||
|
||||
# Otherwise, extract from GPOS
|
||||
if "GPOS" in font:
|
||||
gpos = font["GPOS"].table
|
||||
lookup_list = getattr(gpos, "LookupList", None)
|
||||
@@ -330,12 +350,16 @@ class FontProcessor:
|
||||
if fmt == 1:
|
||||
format1_pairs = self._extract_format1_pairs(subtable)
|
||||
for key, value in format1_pairs.items():
|
||||
pairs[key] += value
|
||||
# Only add if not already present (first value wins)
|
||||
if key not in pairs:
|
||||
pairs[key] = value
|
||||
elif fmt == 2:
|
||||
format2_pairs = self._extract_format2_pairs(subtable)
|
||||
for key, value in format2_pairs.items():
|
||||
pairs[key] += value
|
||||
return dict(pairs)
|
||||
# Only add if not already present (first value wins)
|
||||
if key not in pairs:
|
||||
pairs[key] = value
|
||||
return pairs
|
||||
|
||||
@staticmethod
|
||||
def add_legacy_kern(font: TTFont, kern_pairs: Dict[Tuple[str, str], int]) -> int:
|
||||
@@ -380,34 +404,44 @@ class FontProcessor:
|
||||
if "name" not in font:
|
||||
logger.warning(" No 'name' table found; skipping all name changes")
|
||||
return
|
||||
else:
|
||||
|
||||
if self.prefix:
|
||||
logger.info(" Renaming the font to: " + f"{self.prefix} {metadata.full_name}")
|
||||
|
||||
adjusted_family_name = f"{self.prefix} {metadata.family_name}"
|
||||
adjusted_full_name = f"{self.prefix} {metadata.full_name}"
|
||||
else:
|
||||
logger.info(" Updating font metadata (no prefix)")
|
||||
adjusted_family_name = metadata.family_name
|
||||
adjusted_full_name = metadata.full_name
|
||||
|
||||
# Update Family Name
|
||||
self._set_name_records(font, 1, f"{self.prefix} {metadata.family_name}")
|
||||
self._set_name_records(font, 1, adjusted_family_name)
|
||||
# Update Subfamily
|
||||
self._set_name_records(font, 2, metadata.style_name)
|
||||
# Update Full Name
|
||||
self._set_name_records(font, 4, f"{self.prefix} {metadata.full_name}")
|
||||
self._set_name_records(font, 4, adjusted_full_name)
|
||||
|
||||
# Update Typographic Family
|
||||
self._set_name_records(font, 16, f"{self.prefix} {metadata.family_name}")
|
||||
self._set_name_records(font, 16, adjusted_family_name)
|
||||
# Update Preferred Subfamily
|
||||
self._set_name_records(font, 17, metadata.style_name)
|
||||
# Update Preferred Family
|
||||
self._set_name_records(font, 18, f"{self.prefix} {metadata.family_name}")
|
||||
self._set_name_records(font, 18, adjusted_family_name)
|
||||
|
||||
# Update Unique ID (ID 3)
|
||||
try:
|
||||
current_unique = font["name"].getName(3, 3, 1).toUnicode()
|
||||
parts = current_unique.split("Version")
|
||||
version_info = f"Version{parts[1]}" if len(parts) == 2 else "Version 1.000"
|
||||
new_unique_id = f"{self.prefix} {metadata.family_name.strip()}:{version_info}"
|
||||
if self.prefix:
|
||||
new_unique_id = f"{self.prefix} {metadata.family_name.strip()}:{version_info}"
|
||||
else:
|
||||
new_unique_id = f"{metadata.family_name.strip()}:{version_info}"
|
||||
if current_unique != new_unique_id:
|
||||
self._set_name_records(font, 3, new_unique_id)
|
||||
except Exception as e:
|
||||
logger.warning(f" Failed to update Unique ID: {e}")
|
||||
|
||||
|
||||
# Update PostScript Name (ID 6)
|
||||
new_ps_name = metadata.ps_name
|
||||
self._set_name_records(font, 6, new_ps_name)
|
||||
@@ -416,10 +450,17 @@ class FontProcessor:
|
||||
if "CFF " in font:
|
||||
cff = font["CFF "].cff
|
||||
cff_topdict = cff.topDictIndex[0]
|
||||
|
||||
|
||||
if self.prefix:
|
||||
cff_full_name = f"{self.prefix} {metadata.full_name}"
|
||||
cff_family_name = f"{self.prefix} {metadata.family_name.replace(' ', '_')}"
|
||||
else:
|
||||
cff_full_name = metadata.full_name
|
||||
cff_family_name = metadata.family_name.replace(' ', '_')
|
||||
|
||||
name_mapping = {
|
||||
"FullName": f"{self.prefix} {metadata.full_name}",
|
||||
"FamilyName": f"{self.prefix} {metadata.family_name.replace(' ', '_')}"
|
||||
"FullName": cff_full_name,
|
||||
"FamilyName": cff_family_name
|
||||
}
|
||||
|
||||
for key, new_value in name_mapping.items():
|
||||
@@ -560,12 +601,13 @@ class FontProcessor:
|
||||
# Main processing method
|
||||
# ============================================================
|
||||
|
||||
def process_font(self,
|
||||
kern: bool,
|
||||
remove_gpos: bool,
|
||||
font_path: str,
|
||||
def process_font(self,
|
||||
kern: bool,
|
||||
remove_gpos: bool,
|
||||
font_path: str,
|
||||
new_name: Optional[str] = None,
|
||||
remove_prefix: Optional[str] = None,
|
||||
remove_hints: bool = False,
|
||||
) -> bool:
|
||||
"""
|
||||
Process a single font file.
|
||||
@@ -609,12 +651,23 @@ class FontProcessor:
|
||||
self.update_weight_metadata(font, font_path)
|
||||
|
||||
if kern:
|
||||
had_kern = "kern" in font
|
||||
had_gpos = "GPOS" in font
|
||||
|
||||
kern_pairs = self.extract_kern_pairs(font)
|
||||
if kern_pairs:
|
||||
written = self.add_legacy_kern(font, kern_pairs)
|
||||
logger.info(f" Kerning: extracted {len(kern_pairs)} pairs; wrote {written} to legacy 'kern' table.")
|
||||
if had_kern:
|
||||
logger.info(f" Kerning: 'kern' table already existed, preserved {written} pairs.")
|
||||
else:
|
||||
logger.info(f" Kerning: created 'kern' table from GPOS data with {written} pairs.")
|
||||
else:
|
||||
logger.info(" Kerning: no GPOS kerning found.")
|
||||
if had_kern:
|
||||
logger.info(" Kerning: 'kern' table existed but was empty, no pairs written.")
|
||||
elif had_gpos:
|
||||
logger.info(" Kerning: GPOS table found but contained no kern pairs, no 'kern' table created.")
|
||||
else:
|
||||
logger.info(" Kerning: no kerning data found (no GPOS or 'kern' table), no pairs written.")
|
||||
else:
|
||||
logger.info(" Skipping `kern` step.")
|
||||
|
||||
@@ -624,6 +677,34 @@ class FontProcessor:
|
||||
del font["GPOS"]
|
||||
logger.info(" Removed GPOS table from the font.")
|
||||
|
||||
# Remove TrueType hints if requested
|
||||
if remove_hints:
|
||||
hints_removed = False
|
||||
# Remove fpgm (Font Program) table
|
||||
if "fpgm" in font:
|
||||
del font["fpgm"]
|
||||
hints_removed = True
|
||||
# Remove prep (Control Value Program) table
|
||||
if "prep" in font:
|
||||
del font["prep"]
|
||||
hints_removed = True
|
||||
# Remove cvt (Control Value Table)
|
||||
if "cvt " in font:
|
||||
del font["cvt "]
|
||||
hints_removed = True
|
||||
# Remove hints from glyf table using the built-in removeHinting method
|
||||
if "glyf" in font:
|
||||
for glyph_name in font.getGlyphOrder():
|
||||
glyph = font["glyf"][glyph_name]
|
||||
if hasattr(glyph, 'removeHinting'):
|
||||
glyph.removeHinting()
|
||||
hints_removed = True
|
||||
|
||||
if hints_removed:
|
||||
logger.info(" Removed TrueType hints from the font.")
|
||||
else:
|
||||
logger.info(" No TrueType hints found to remove.")
|
||||
|
||||
output_path = self._generate_output_path(font_path, metadata)
|
||||
font.save(output_path)
|
||||
logger.info(f" Saved: {output_path}")
|
||||
@@ -645,17 +726,20 @@ class FontProcessor:
|
||||
"""
|
||||
dirname = os.path.dirname(original_path)
|
||||
original_name, ext = os.path.splitext(os.path.basename(original_path))
|
||||
|
||||
|
||||
style_suffix = ""
|
||||
for key in STYLE_MAP:
|
||||
if key.lower() in original_name.lower():
|
||||
style_suffix = key
|
||||
break
|
||||
|
||||
|
||||
style_part = f"-{style_suffix}" if style_suffix else ""
|
||||
|
||||
base_name = f"{self.prefix}_{metadata.family_name.replace(' ', '_')}{style_part}"
|
||||
|
||||
|
||||
if self.prefix:
|
||||
base_name = f"{self.prefix}_{metadata.family_name.replace(' ', '_')}{style_part}"
|
||||
else:
|
||||
base_name = f"{metadata.family_name.replace(' ', '_')}{style_part}"
|
||||
|
||||
return os.path.join(dirname, f"{base_name}{ext.lower()}")
|
||||
|
||||
|
||||
@@ -713,8 +797,8 @@ Examples:
|
||||
help="Font files to process (*.ttf). You can use a wildcard (glob).")
|
||||
parser.add_argument("--name", type=str,
|
||||
help="Optional new family name for all fonts. Other font metadata like copyright info is unaffected.")
|
||||
parser.add_argument("--prefix", type=str, default=DEFAULT_PREFIX,
|
||||
help=f"Prefix to add to font names. Required. (Default: {DEFAULT_PREFIX})")
|
||||
parser.add_argument("--prefix", type=str, default=DEFAULT_PREFIX,
|
||||
help=f"Prefix to add to font names. Set to empty string to omit prefix. (Default: {DEFAULT_PREFIX})")
|
||||
parser.add_argument("--line-percent", type=int, default=DEFAULT_LINE_PERCENT,
|
||||
help=f"Line spacing adjustment percentage. Set to 0 to make no changes to line spacing. (Default: {DEFAULT_LINE_PERCENT})")
|
||||
parser.add_argument("--skip-kobo-kern", action="store_true",
|
||||
@@ -725,8 +809,10 @@ Examples:
|
||||
help="Enable verbose output.")
|
||||
parser.add_argument("--remove-prefix", type=str,
|
||||
help="Remove a leading prefix from font names before applying the new prefix. Only works if `--name` is not used. (e.g., --remove-prefix=\"NV\")")
|
||||
parser.add_argument("--remove-hints", action="store_true",
|
||||
help="Remove TrueType hints from the font. This may improve render quality on some devices and reduce file size.")
|
||||
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose:
|
||||
@@ -761,9 +847,10 @@ Examples:
|
||||
if processor.process_font(
|
||||
not args.skip_kobo_kern,
|
||||
args.remove_gpos,
|
||||
font_path,
|
||||
font_path,
|
||||
args.name,
|
||||
args.remove_prefix,
|
||||
args.remove_hints,
|
||||
):
|
||||
success_count += 1
|
||||
|
||||
|
||||
Reference in New Issue
Block a user