Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
naghim committed May 2, 2024
0 parents commit 391d64c
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 0 deletions.
30 changes: 30 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Build

on: [push, repository_dispatch, workflow_dispatch]

jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Visual Studio
uses: ilammy/msvc-dev-cmd@v1
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
architecture: "x64"
- name: Install Python requirements
shell: bash
run: >
python -OO -m pip install --disable-pip-version-check --upgrade nuitka zstandard &&
python -OO -m pip install --disable-pip-version-check -r requirements.txt
- name: Build executable
shell: powershell
run: >
python -OO -m nuitka --standalone --onefile --python-flag=-OO --assume-yes-for-downloads --static-libpython=auto --windows-icon-from-ico=resources/icon.ico --windows-product-name=Fontos --windows-company-name=naghim --windows-file-version=1.0.0.0 --windows-file-description=Fontos -o Fontos.exe fontos/__main__.py
- uses: actions/upload-artifact@v4
with:
name: Fontos
path: Fontos.exe
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.ass
fonts

*.py[c|o]
__pycache__
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Fontos: Font Finder and Collector

This application allows you to check whether the fonts in an .ass subtitle are installed on your system or not, as well as collect the fonts into a directory.

It will tell you which fonts are missing. Correct each font and try again.

If no fonts are missing, the fonts will be collected into a directory called `fonts`.

# Usage

First, install the Python requirements:

```bash
python -m pip install -r requirements.txt
```

Second, save the desired subtitle as `subtitle.ass`.

Third, run the Python program on Windows:

```bash
python -m fontos
```

This program is only supported on Windows at the moment.
Empty file added fontos/__init__.py
Empty file.
47 changes: 47 additions & 0 deletions fontos/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from colorama import Fore, Style
from fontos import windows, font, subtitle, install
import sys
import os

def main():
font_folder = 'fonts'
subtitle_filename = sys.argv[1] if len(sys.argv) > 1 else 'subtitle.ass'

if not os.path.exists(subtitle_filename):
print(f'{subtitle_filename} is missing!')
sys.exit()

# First, find all installed fonts...
installed_font_ttfs = windows.find_installed_ttfs()

# Second, find the font names of all installed fonts...
installed_fonts = {}
font.parse_font_names(installed_font_ttfs, installed_fonts)

# Third, find the fonts in the subtitle...
subtitle_fonts = subtitle.find_fonts_in_subtitle(subtitle_filename)

# Finally, let's see if all fonts are installed.
all_installed = True

for subtitle_font in subtitle_fonts:
if subtitle_font in installed_fonts:
print(subtitle_font, Fore.GREEN + 'INSTALLED', Style.RESET_ALL)
else:
all_installed = False

if all_installed:
# All fonts are installed, let's save the fonts into a separate folder!
install.install_fonts(font_folder, subtitle_fonts, installed_fonts)
print('Installed fonts to', os.path.abspath(font_folder))
return

# Not all fonts are found, let's print the ones that are missing
for subtitle_font in subtitle_fonts:
if subtitle_font not in installed_fonts:
print(subtitle_font, Fore.RED + 'NOT INSTALLED', Style.RESET_ALL)

input()

if __name__ == '__main__':
main()
41 changes: 41 additions & 0 deletions fontos/font.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from fontTools import ttLib
from fontTools.ttLib import TTLibError

def parse_font_name(ttf, installed_fonts):
# Some fonts are collections
# For these fonts, we have to query each font separately
fontNumber = 0

while True:
try:
font = ttLib.TTFont(ttf, fontNumber=fontNumber)
except TTLibError as e:
# Maybe this isn't a TrueType font?
if 'Not a TrueType' in str(e):
print(ttf, 'is not a TrueType font')
break

# Let's check all names.
for name in font['name'].names:
# https://learn.microsoft.com/en-us/typography/opentype/spec/name#name-ids
# Only check names that are font names
if name.nameID in [1, 4]:
# For some reason, names are in UTF-16-BE
try:
name = name.string.decode('utf-16-be')
except:
name = name.string.decode('utf-8')

installed_fonts[name] = ttf

if font.sfntVersion != b'ttcf':
# Not a collection, so let's not check the rest of the fonts
break

# Check the next font if this is a collection
fontNumber += 1

def parse_font_names(ttfs, installed_fonts):
# Read all font files and see which are installed
for ttf in ttfs:
parse_font_name(ttf, installed_fonts)
28 changes: 28 additions & 0 deletions fontos/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import shutil
import os

def install_fonts(font_folder, subtitle_fonts, installed_fonts):
# Create the folder if it does not exist
if not os.path.exists(font_folder):
os.makedirs(font_folder)

# Make sure not to install fonts twice
fonts_installed = set()

for font in subtitle_fonts:
installed_ttf = installed_fonts[font]

if installed_ttf in fonts_installed:
# This font was already installed
continue

extension = os.path.splitext(installed_ttf)[-1]
font_file = os.path.join(font_folder, f'{font}{extension}')

if os.path.exists(font_file):
# This font already exists in the folder
continue

# Copy the font into the folder
shutil.copyfile(installed_ttf, font_file)
fonts_installed.add(installed_ttf)
33 changes: 33 additions & 0 deletions fontos/subtitle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import ass
import re

def find_fonts_in_subtitle(subtitle_filename):
# Now let's parse the subtitles
with open(subtitle_filename, 'r', encoding='utf-8-sig') as f:
doc = ass.parse(f)

# These are the fonts that are used in the subtitle
fonts = set()

# First, the fonts used in all styles...
for style in doc.styles:
font_name = style.fontname
fonts.add(font_name)

# Second, the fonts that are manually specified in each dialogue
# Format: \fnFontName\
pattern = re.compile(r'\\fn([^\\}]+)(\\|})')

for event in doc.events:
fonts_found = pattern.findall(event.text)

# Add all fonts that are found
for font in fonts_found:
# The first match is the font name itself
# The second match is the delimiter
fonts.add(font[0])

# Sort the fonts
fonts = list(fonts)
fonts = sorted(fonts)
return fonts
23 changes: 23 additions & 0 deletions fontos/windows.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import os
import winreg

def find_installed_ttfs():
installed_font_ttfs = []

# Find all installed font TTFs
# There are two places to check:
# - System fonts (Windows)
# - User installed fonts (AppData)
for registry_hive in [winreg.HKEY_LOCAL_MACHINE, winreg.HKEY_CURRENT_USER]:
reg = winreg.ConnectRegistry(None, registry_hive)
key = winreg.OpenKey(reg, r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts', 0, winreg.KEY_READ)

for i in range(0, winreg.QueryInfoKey(key)[1]):
ttf = winreg.EnumValue(key, i)[1]

if '\\' not in ttf:
ttf = os.path.join(os.environ['WINDIR'], 'Fonts', ttf)

installed_font_ttfs.append(ttf)

return installed_font_ttfs
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ass
colorama
fonttools
Binary file added resources/icon.ico
Binary file not shown.

0 comments on commit 391d64c

Please sign in to comment.