Skip to content

Instantly share code, notes, and snippets.

@rcarmo
Last active May 31, 2021 18:22
Show Gist options
  • Save rcarmo/7d2746c4d91e03754184e01f3fec1411 to your computer and use it in GitHub Desktop.
Save rcarmo/7d2746c4d91e03754184e01f3fec1411 to your computer and use it in GitHub Desktop.
TrueType Font Organizer
from contextlib import redirect_stderr
from itertools import chain
from os import makedirs
from os.path import exists, join
from pathlib import Path
from shutil import move
from typing import Generator
from fontTools import ttLib # also requires brotli for some font formats
FONT_ROOT = '/Volumes/fonts'
def iter_fonts(path: str) -> chain:
p = Path(path)
return chain(
p.glob('**/*.ttf'),
p.glob('**/*.TTF'),
p.glob('**/*.otf'),
p.glob('**/*.OTF'),
p.glob('**/*.woff'),
p.glob('**/*.woff2'),
#p.glob('**/*.ttc')
)
def iter_paths(path: str) -> Generator:
p = Path(path)
return p.glob('**')
def font_name(font_path: Path):
print(font_path)
font = ttLib.TTFont(font_path, ignoreDecompileErrors=True)
with redirect_stderr(None):
names = font['name'].names
details = {}
for x in names:
if x.langID == 0 or x.langID == 1033:
try:
details[x.nameID] = x.toUnicode()
except UnicodeDecodeError:
details[x.nameID] = x.string.decode(errors='ignore')
return details[4], details[1], details[2]
def move_and_rename(src: Path, dst: Path, ordinal: int=0):
if not ordinal and exists(dst):
move_and_rename(src, dst, ordinal+1)
return
if ordinal:
numbered = Path(join(dst.parent, dst.stem) + f" ({ordinal})" + dst.suffix)
if exists(numbered):
move_and_rename(src, dst, ordinal+1)
return
dst = numbered
if not exists(dst.parent):
makedirs(dst.parent)
print(f"{src} -> {dst}")
move(src, dst)
def process_fonts() -> None:
for font_path in iter_fonts(FONT_ROOT):
if '_duplicates' in str(font_path):
continue
full_name, family, variation = font_name(font_path)
target_path = Path(join(FONT_ROOT, family, full_name) + font_path.suffix.lower())
if(target_path == font_path):
continue
if(exists(target_path)):
target_path = Path(join(FONT_ROOT, '_duplicates', family, full_name) + font_path.suffix.lower())
move_and_rename(font_path, target_path)
#print(font_name(filename)) # ('Century Bold Italic', 'Century', 'Bold Italic') – name, family, style
def cleanup_folders() -> None:
for test_path in list(iter_paths(FONT_ROOT)):
files = list(test_path.iterdir())
if not len(files):
try:
test_path.rmdir()
except:
pass
if __name__=='__main__':
process_fonts()
cleanup_folders()
fonttools>=4.24.4
brotli>=1.0.9
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment