Skip to content

Commit

Permalink
Refactor code
Browse files Browse the repository at this point in the history
**Changes**
- Now, we distinct a font file from a font face.
There are now 2 kind of font face. "Variable" is for [variable font](https://fonts.google.com/knowledge/introducing_type/introducing_variable_fonts) and "Normal" for the rest.
- Know what is the language of a family name or a exact name
- From a FontFace, get the best name depending of the OS language via ``ABCFontFace.get_best_family_name``/``ABCFontFace.get_best_exact_name``. The user can also query a family name from a BCP47 tag via ``ABCFontFace.get_family_name_from_lang``/``ABCFontFace.get_exact_name_from_lang``
- Users can know implement their own ass document reader. They just need to extend the class ABCAssDocument
- Users can create their own strategy to find a font. They simply need to implement FontSelectionStrategy.
Also, the user can choose between 2 strategy (FontSelectionStrategyLibass and FontSelectionStrategyVSFilter)
- Know if a font is a font collection (TTC/OTC file)
- Know what is the font type (opentype/truetype)
- Create a FontCollection class. It has the same responsabilities has the old FontLoader. The new FontLoader now only load the font cache, load a batch of fonts and load the system fonts.
- Add ``need_faux_bold`` attribute to ``FontResult``
- Resolve the issue #8
  • Loading branch information
moi15moi committed Apr 28, 2024
1 parent 70415ab commit 8a4d16d
Show file tree
Hide file tree
Showing 196 changed files with 4,784,161 additions and 2,314 deletions.
51 changes: 25 additions & 26 deletions examples/collect_font_and_mux_them.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,54 @@
import sys
from font_collector import (
AssDocument,
Font,
FontCollection,
FontFile,
FontLoader,
Helpers,
FontSelectionStrategyLibass,
Mkvpropedit,
set_loglevel,
)
from pathlib import Path
from typing import List
from typing import List, Set

# If you don't want to get any log, set the loglevel to logging.CRITICAL
set_loglevel(logging.CRITICAL)


def main():
ass_path = "ASS_FILE_PATH.ass"
mkv_path = "MKV_FILE_PATH.ass"
ass_path = Path("ASS_FILE_PATH.ass")
mkv_path = Path("MKV_FILE_PATH.mkv")

# If you need additional fonts (font that aren't installed in the system), specify them in the additional_fonts_path
additional_fonts_path: List[Path] = []
font_collection = FontLoader(additional_fonts_path, use_system_font=True).fonts
additional_fonts = FontLoader.load_additional_fonts(additional_fonts_path)

subtitle = AssDocument.from_file(ass_path)
styles = subtitle.get_used_style()
# If you need additional fonts (font that aren't installed in the system), specify them in the additional_fonts
font_collection = FontCollection(additional_fonts=additional_fonts)
font_selection_strategy = FontSelectionStrategyLibass()

fonts_found: List[Font] = []
subtitle = AssDocument.from_file(ass_path) # if you have a object that represent the .ass file, you can also use ABCAssDocument
used_styles = subtitle.get_used_style()

for style, usage_data in styles.items():
fonts_file_found: Set[FontFile] = set()

font_result = Helpers.get_used_font_by_style(font_collection, style)

if font_result is None:
print(f"Could not find font '{style.fontname}'")
else:
print(f"Font found: {font_result.font.filename}")
fonts_found.append(font_result.font)
for style, usage_data in used_styles.items():
font_result = font_collection.get_used_font_by_style(style, font_selection_strategy)
if font_result:
family_name = font_result.font_face.get_best_family_name()
font_file = font_result.font_face.font_file
print(f"We found the family {family_name.value} at {font_file.filename}")
fonts_file_found.add(font_file)

# If you wanna verify if the font miss any glyph, use this
missing_glyphs = font_result.font.get_missing_glyphs(
usage_data.characters_used
)
missing_glyphs = font_result.font_face.get_missing_glyphs(usage_data.characters_used)
if len(missing_glyphs) != 0:
print(
f"'{style.fontname}' is missing the following glyphs: {missing_glyphs}"
)
print(f"'{family_name.value}' is missing the following glyphs: {missing_glyphs}")


# If the mkv already contain font, you can remove them
# If the mkv already contains font, you can remove them
Mkvpropedit.delete_fonts_of_mkv(mkv_path)

Mkvpropedit.merge_fonts_into_mkv(fonts_found, mkv_path)
Mkvpropedit.merge_fonts_into_mkv(fonts_file_found, mkv_path)


if __name__ == "__main__":
Expand Down
26 changes: 12 additions & 14 deletions font_collector/__init__.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import logging
from .ass_document import AssDocument
from .ass_style import AssStyle
from .exceptions import InvalidFontException, NameNotFoundException
from .font_loader import FontLoader
from .font_result import FontResult
from .font import Font
from .helpers import Helpers
from .mkvpropedit import Mkvpropedit
from .usage_data import UsageData
from ._version import __version__

# Packages
from .ass import *
from .font import *
from .system_lang import *
# Files
from .exceptions import *
from .mkvpropedit import *
from ._version import __version__
from fontTools.misc.loggingTools import configLogger

configLogger(level="ERROR")
configLogger(level="CRITICAL")

# Set our default logger
_logger = logging.getLogger(__name__)
Expand All @@ -27,10 +25,10 @@
_logger.addHandler(_handler)


def set_loglevel(level: int):
def set_loglevel(level: int) -> None:
"""
Parameters:
level (int): An level from logging module (For more detail, see: https://docs.python.org/3/library/logging.html#logging-levels)
Args:
level: An level from logging module (For more detail, see: https://docs.python.org/3/library/logging.html#logging-levels)
"""
_logger.setLevel(level)
_handler.setLevel(level)
96 changes: 53 additions & 43 deletions font_collector/__main__.py
Original file line number Diff line number Diff line change
@@ -1,90 +1,100 @@
import logging
import sys
from .ass_document import AssDocument
from .font import Font
from .font_loader import FontLoader
from .font_result import FontResult
from .helpers import Helpers
import shutil
from .ass.ass_document import AssDocument
from .font import FontCollection, FontFile, FontLoader, FontResult, FontSelectionStrategyLibass, VariableFontFace
from .mkvpropedit import Mkvpropedit
from .parse_arguments import parse_arguments
from typing import List
from pathlib import Path
from typing import List, Set


_logger = logging.getLogger(__name__)


def main():
def main() -> None:
(
ass_files_path,
output_directory,
mkv_path,
delete_fonts,
additional_fonts,
additional_fonts_recursive,
additional_fonts_path,
additional_fonts_recursive_path,
use_system_font,
collect_draw_fonts
collect_draw_fonts,
convert_variable_to_collection
) = parse_arguments()
font_results: List[FontResult] = []
font_collection = FontLoader(additional_fonts, use_system_font, additional_fonts_recursive).fonts
additional_fonts = FontLoader.load_additional_fonts(additional_fonts_path)
additional_fonts.extend(FontLoader.load_additional_fonts(additional_fonts_recursive_path, True))
font_collection = FontCollection(use_system_font=use_system_font, additional_fonts=additional_fonts)
font_strategy = FontSelectionStrategyLibass()

for ass_path in ass_files_path:
subtitle = AssDocument.from_file(ass_path)
_logger.info(f"Loaded successfully {ass_path}")
styles = subtitle.get_used_style(collect_draw_fonts)
used_styles = subtitle.get_used_style(collect_draw_fonts)

nbr_font_not_found = 0

for style, usage_data in styles.items():
for style, usage_data in used_styles.items():

font_result = Helpers.get_used_font_by_style(font_collection, style)
font_result = font_collection.get_used_font_by_style(style, font_strategy)

# Did not found the font
if font_result is None:
nbr_font_not_found += 1
_logger.error(
f"Used on lines: {' '.join(str(line) for line in usage_data.ordered_lines)}"
)
_logger.error(f"Could not find font '{style.fontname}'")
_logger.error(f"Used on lines: {' '.join(str(line) for line in usage_data.ordered_lines)}")
else:
font_results.append(font_result)

if font_result.mismatch_bold:
if font_result.need_faux_bold:
_logger.warning(f"Faux bold used for '{style.fontname}'.")
elif font_result.mismatch_bold:
_logger.warning(f"'{style.fontname}' does not have a bold variant.")
if font_result.mismatch_italic:
_logger.warning(
f"'{style.fontname}' does not have an italic variant."
)

if font_result.mismatch_bold or font_result.mismatch_italic:
_logger.warning(
f"Used on lines: {' '.join(str(line) for line in usage_data.ordered_lines)}"
)

if (
len(
missing_glyphs := font_result.font.get_missing_glyphs(
usage_data.characters_used
)
)
> 0
):
_logger.warning(
f"'{style.fontname}' is missing the following glyphs used: {missing_glyphs}"
)
_logger.warning(f"'{style.fontname}' does not have an italic variant.")

if font_result.need_faux_bold or font_result.mismatch_bold or font_result.mismatch_italic:
_logger.warning(f"Used on lines: {' '.join(str(line) for line in usage_data.ordered_lines)}")


missing_glyphs = font_result.font_face.get_missing_glyphs(usage_data.characters_used)
if len(missing_glyphs) > 0:
_logger.warning(f"'{style.fontname}' is missing the following glyphs used: {missing_glyphs}")

if nbr_font_not_found == 0:
_logger.info(f"All fonts found")
else:
_logger.info(f"{nbr_font_not_found} fonts could not be found.")

fonts_found: List[Font] = [font.font for font in font_results]
fonts_file_found: Set[FontFile] = set()

for font_result in font_results:
if font_result.font_face.font_file is None:
raise ValueError(f"This font_face \"{font_result.font_face}\" isn't linked to any FontFile.")

if convert_variable_to_collection and isinstance(font_result.font_face, VariableFontFace):
font_name = font_result.font_face.get_best_family_prefix_from_lang().value
font_filename = output_directory.joinpath(f"{font_name}.ttc")
generated_font_file = font_result.font_face.variable_font_to_collection(font_filename)
fonts_file_found.add(generated_font_file)
else:
fonts_file_found.add(font_result.font_face.font_file)

if mkv_path is not None:
if delete_fonts:
Mkvpropedit.delete_fonts_of_mkv(mkv_path)
Mkvpropedit.merge_fonts_into_mkv(fonts_found, mkv_path)
Mkvpropedit.merge_fonts_into_mkv(fonts_file_found, mkv_path)
else:
Helpers.copy_font_to_directory(fonts_found, output_directory)
if not output_directory.is_dir():
output_directory.mkdir()

for font in fonts_file_found:
font_filename = output_directory.joinpath(font.filename.resolve().name)
# Don't overwrite fonts
if not font_filename.is_file():
shutil.copy(font.filename, font_filename)

if __name__ == "__main__":
sys.exit(main())
main()
4 changes: 4 additions & 0 deletions font_collector/ass/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .abc_ass_document import *
from .ass_document import *
from .ass_style import *
from .usage_data import *

0 comments on commit 8a4d16d

Please sign in to comment.