Skip to content

Commit

Permalink
Merge pull request #7 from appventure-nush/dependency_removing
Browse files Browse the repository at this point in the history
Remove Matplotlib Dependency
  • Loading branch information
Walnit authored Jan 21, 2025
2 parents c7d6e9d + eecb219 commit 863319f
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 4 deletions.
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ classifiers = [
]
dependencies = [
"pyembroidery",
"matplotlib",
"opentypesvg",
"svglib",
"PyMuPDF",
Expand Down
159 changes: 159 additions & 0 deletions src/turtlethread/font_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# Taken from Matplotlib's Source Code, as of commit hash 3b54c6a

import logging
import os
from pathlib import Path
import plistlib
import subprocess
import sys

_log = logging.getLogger(__name__)

# OS Font paths
try:
_HOME = Path.home()
except Exception: # Exceptions thrown by home() are not specified...
_HOME = Path(os.devnull) # Just an arbitrary path with no children.
MSFolders = \
r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
MSFontDirectories = [
r'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts',
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Fonts']
MSUserFontDirectories = [
str(_HOME / 'AppData/Local/Microsoft/Windows/Fonts'),
str(_HOME / 'AppData/Roaming/Microsoft/Windows/Fonts'),
]
X11FontDirectories = [
# an old standard installation point
"/usr/X11R6/lib/X11/fonts/TTF/",
"/usr/X11/lib/X11/fonts",
# here is the new standard location for fonts
"/usr/share/fonts/",
# documented as a good place to install new fonts
"/usr/local/share/fonts/",
# common application, not really useful
"/usr/lib/openoffice/share/fonts/truetype/",
# user fonts
str((Path(os.environ.get('XDG_DATA_HOME') or _HOME / ".local/share"))
/ "fonts"),
str(_HOME / ".fonts"),
]
OSXFontDirectories = [
"/Library/Fonts/",
"/Network/Library/Fonts/",
"/System/Library/Fonts/",
# fonts installed via MacPorts
"/opt/local/share/fonts",
# user fonts
str(_HOME / "Library/Fonts"),
]

def list_fonts(directory, extensions):
"""
Return a list of all fonts matching any of the extensions, found
recursively under the directory.
"""
extensions = ["." + ext for ext in extensions]
return [os.path.join(dirpath, filename)
# os.walk ignores access errors, unlike Path.glob.
for dirpath, _, filenames in os.walk(directory)
for filename in filenames
if Path(filename).suffix.lower() in extensions]

def get_fontext_synonyms(fontext):
"""
Return a list of file extensions that are synonyms for
the given file extension *fileext*.
"""
return {
'afm': ['afm'],
'otf': ['otf', 'ttc', 'ttf'],
'ttc': ['otf', 'ttc', 'ttf'],
'ttf': ['otf', 'ttc', 'ttf'],
}[fontext]

def _get_win32_installed_fonts():
"""List the font paths known to the Windows registry."""
import winreg
items = set()
# Search and resolve fonts listed in the registry.
for domain, base_dirs in [
(winreg.HKEY_LOCAL_MACHINE, [win32FontDirectory()]), # System.
(winreg.HKEY_CURRENT_USER, MSUserFontDirectories), # User.
]:
for base_dir in base_dirs:
for reg_path in MSFontDirectories:
try:
with winreg.OpenKey(domain, reg_path) as local:
for j in range(winreg.QueryInfoKey(local)[1]):
# value may contain the filename of the font or its
# absolute path.
key, value, tp = winreg.EnumValue(local, j)
if not isinstance(value, str):
continue
try:
# If value contains already an absolute path,
# then it is not changed further.
path = Path(base_dir, value).resolve()
except RuntimeError:
# Don't fail with invalid entries.
continue
items.add(path)
except (OSError, MemoryError):
continue
return items

def _get_fontconfig_fonts():
"""Cache and list the font paths known to ``fc-list``."""
try:
if b'--format' not in subprocess.check_output(['fc-list', '--help']):
_log.warning( # fontconfig 2.7 implemented --format.
'Matplotlib needs fontconfig>=2.7 to query system fonts.')
return []
out = subprocess.check_output(['fc-list', '--format=%{file}\\n'])
except (OSError, subprocess.CalledProcessError):
return []
return [Path(os.fsdecode(fname)) for fname in out.split(b'\n')]


def _get_macos_fonts():
"""Cache and list the font paths known to ``system_profiler SPFontsDataType``."""
try:
d, = plistlib.loads(
subprocess.check_output(["system_profiler", "-xml", "SPFontsDataType"]))
except (OSError, subprocess.CalledProcessError, plistlib.InvalidFileException):
return []
return [Path(entry["path"]) for entry in d["_items"]]

def findSystemFonts(fontpaths=None, fontext='ttf'):
"""
Search for fonts in the specified font paths. If no paths are
given, will use a standard set of system paths, as well as the
list of fonts tracked by fontconfig if fontconfig is installed and
available. A list of TrueType fonts are returned by default with
AFM fonts as an option.
"""
fontfiles = set()
fontexts = get_fontext_synonyms(fontext)

if fontpaths is None:
if sys.platform == 'win32':
installed_fonts = _get_win32_installed_fonts()
fontpaths = []
else:
installed_fonts = _get_fontconfig_fonts()
if sys.platform == 'darwin':
installed_fonts += _get_macos_fonts()
fontpaths = [*X11FontDirectories, *OSXFontDirectories]
else:
fontpaths = X11FontDirectories
fontfiles.update(str(path) for path in installed_fonts
if path.suffix.lower()[1:] in fontexts)

elif isinstance(fontpaths, str):
fontpaths = [fontpaths]

for path in fontpaths:
fontfiles.update(map(os.path.abspath, list_fonts(path, fontexts)))

return [fname for fname in fontfiles if os.path.exists(fname)]
9 changes: 6 additions & 3 deletions src/turtlethread/text.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import os
import tempfile
from matplotlib import font_manager
from fuzzywuzzy import process, fuzz
from .font_manager import findSystemFonts
from pathlib import Path

import warnings
warnings.simplefilter("ignore")
from fuzzywuzzy import process, fuzz

from opentypesvg import fonts2svg

from .draw_svg import drawSVG
Expand Down Expand Up @@ -73,7 +76,7 @@ def load_font(self, fontname:str, fontpath:str=None, search_threshold=None):
search_threshold = LetterDrawer.font_search_score_threshold

# get system fonts
fontpaths = font_manager.findSystemFonts(fontpaths=None, fontext='otf')
fontpaths = findSystemFonts(fontpaths=None, fontext='otf')
fontnames = [Path(fp).name.lower() for fp in fontpaths]
res_ttf = process.extract(fontname.lower()+".ttf", fontnames, scorer=fuzz.ratio)
res_otf = process.extract(fontname.lower()+".otf", fontnames, scorer=fuzz.ratio)
Expand Down

0 comments on commit 863319f

Please sign in to comment.