Files
Text-Texture-Generator-for-…/src/utils/system.py
2025-10-13 22:42:55 +02:00

268 lines
8.1 KiB
Python

"""
System utility functions for the Text Texture Generator addon.
"""
import subprocess
import sys
import platform
import os
from pathlib import Path
from typing import Iterable
_SUPPORTED_PYTHON_TAG = "cp311"
_BUNDLED_WHEEL_DIRS = {
"darwin": "cp311-macosx_10_9_universal2",
"linux": "cp311-manylinux_2_28_x86_64",
}
_BUNDLED_CAIROSVG_PACKAGES = (
"cairosvg==2.7.1",
"cairocffi==1.7.1",
"cssselect2==0.7.0",
"tinycss2==1.2.1",
"defusedxml==0.7.1",
"cffi==1.17.1",
"pycparser==2.21",
"webencodings==0.5.1",
)
_DEFAULT_FONT_CACHE = None
def _pip_install_from_bundle(packages: Iterable[str], bundle_dir: Path) -> None:
"""Install packages from a bundled wheel directory using pip."""
pass
command = [
sys.executable,
"-m",
"pip",
"install",
"--no-index",
"--no-cache-dir",
"--find-links",
str(bundle_dir),
*packages,
]
subprocess.check_call(command)
def _get_wheel_bundle_dir() -> Path:
"""Return the platform-specific wheel bundle directory for the current environment."""
pass
python_tag = f"cp{sys.version_info.major}{sys.version_info.minor}"
if python_tag != _SUPPORTED_PYTHON_TAG:
pass
raise RuntimeError(
f"Bundled dependencies target Python {_SUPPORTED_PYTHON_TAG}, "
f"but current interpreter reports {python_tag}."
)
platform_key = platform.system().lower()
bundle_name = _BUNDLED_WHEEL_DIRS.get(platform_key)
if not bundle_name:
pass
raise RuntimeError(f"No bundled wheel directory configured for platform '{platform_key}'.")
wheels_root = Path(__file__).resolve().parent.parent / "wheels"
bundle_dir = wheels_root / bundle_name
if not bundle_dir.exists():
pass
raise FileNotFoundError(f"Bundled wheel directory not found: {bundle_dir}")
return bundle_dir
def install_cairosvg_from_bundle() -> None:
"""Install CairoSVG and its dependencies using the bundled wheels."""
pass
bundle_dir = _get_wheel_bundle_dir()
_pip_install_from_bundle(_BUNDLED_CAIROSVG_PACKAGES, bundle_dir)
def install_pillow():
pass
"""Install Pillow if not available"""
try:
pass
import PIL
except ImportError:
pass
print("Installing Pillow...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "pillow>=10.0.0"])
def get_system_fonts():
pass
"""Get detailed list of available system fonts with paths"""
try:
pass
from PIL import ImageFont
import glob
fonts = {}
system = platform.system()
if system == "Windows":
pass
font_dirs = [
"C:/Windows/Fonts/",
os.path.expanduser("~/AppData/Local/Microsoft/Windows/Fonts/")
]
extensions = ["*.ttf", "*.ttc", "*.otf"]
elif system == "Darwin": # macOS
font_dirs = [
"/System/Library/Fonts/",
"/Library/Fonts/",
os.path.expanduser("~/Library/Fonts/")
]
extensions = ["*.ttf", "*.ttc", "*.otf", "*.dfont"]
else: # Linux
font_dirs = [
"/usr/share/fonts/",
"/usr/local/share/fonts/",
os.path.expanduser("~/.local/share/fonts/"),
os.path.expanduser("~/.fonts/")
]
extensions = ["*.ttf", "*.ttc", "*.otf"]
for font_dir in font_dirs:
pass
if os.path.exists(font_dir):
pass
for ext in extensions:
pass
for font_path in glob.glob(os.path.join(font_dir, "**", ext), recursive=True):
pass
try:
pass
font_name = os.path.splitext(os.path.basename(font_path))[0]
font_name = font_name.replace("-", " ").replace("_", " ")
fonts[font_name] = font_path
except Exception:
pass
continue
return fonts
except ImportError:
pass
return {}
def get_font_enum_items(self, context):
pass
"""Dynamic enum items for font selection"""
items = [("default", "Default Font", "Use system default font", 0)]
if not hasattr(get_font_enum_items, 'cached_fonts'):
pass
get_font_enum_items.cached_fonts = get_system_fonts()
fonts = get_font_enum_items.cached_fonts
for i, (font_name, font_path) in enumerate(sorted(fonts.items())[:50]):
pass
items.append((font_path, font_name, f"Font: {font_name}", i + 1))
return items
def get_anchor_point_matrix():
pass
"""Return a 3x3 matrix of anchor point identifiers for UI positioning grid"""
return [
['TOP_LEFT', 'TOP_CENTER', 'TOP_RIGHT'],
['MIDDLE_LEFT', 'MIDDLE_CENTER', 'MIDDLE_RIGHT'],
['BOTTOM_LEFT', 'BOTTOM_CENTER', 'BOTTOM_RIGHT']
]
def find_default_truetype_font():
pass
"""Return a system font path that can act as scalable default."""
global _DEFAULT_FONT_CACHE
if _DEFAULT_FONT_CACHE is not None:
pass
return _DEFAULT_FONT_CACHE
system = platform.system().lower()
preferred_fonts = []
if system == "windows":
preferred_fonts = [
"C:/Windows/Fonts/arial.ttf",
"C:/Windows/Fonts/segoeui.ttf",
"C:/Windows/Fonts/calibri.ttf",
"C:/Windows/Fonts/tahoma.ttf",
]
elif system == "darwin":
preferred_fonts = [
"/System/Library/Fonts/SFNS.ttf",
"/System/Library/Fonts/SFNSDisplay.ttf",
"/System/Library/Fonts/Helvetica.ttc",
"/Library/Fonts/Arial.ttf",
"/Library/Fonts/HelveticaNeue.ttc",
]
else:
preferred_fonts = [
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
"/usr/share/fonts/truetype/freefont/FreeSans.ttf",
]
def _font_supports_basic_latin(font_path):
pass
try:
from PIL import ImageFont
except ImportError:
return False
try:
font = ImageFont.truetype(font_path, 48)
except (OSError, IOError, TypeError, ValueError):
return False
try:
bbox_upper = font.getbbox("H")
bbox_lower = font.getbbox("e")
except Exception:
return False
if not bbox_upper or not bbox_lower:
return False
try:
upper_width = float(bbox_upper[2] - bbox_upper[0])
upper_height = float(bbox_upper[3] - bbox_upper[1])
lower_width = float(bbox_lower[2] - bbox_lower[0])
lower_height = float(bbox_lower[3] - bbox_lower[1])
except (TypeError, ValueError):
return False
if min(upper_width, upper_height, lower_width, lower_height) <= 0:
return False
if lower_height > upper_height * 1.3:
return False
if lower_width > upper_width * 1.5:
return False
return True
for candidate in preferred_fonts:
if candidate and os.path.exists(candidate) and _font_supports_basic_latin(candidate):
_DEFAULT_FONT_CACHE = candidate
return _DEFAULT_FONT_CACHE
fonts = get_system_fonts()
for _, font_path in sorted(fonts.items()):
if os.path.exists(font_path) and _font_supports_basic_latin(font_path):
_DEFAULT_FONT_CACHE = font_path
return _DEFAULT_FONT_CACHE
try:
from ..ui.preview import get_system_fallback_fonts
fallback_fonts = get_system_fallback_fonts()
for font_path in fallback_fonts:
if os.path.exists(font_path) and _font_supports_basic_latin(font_path):
_DEFAULT_FONT_CACHE = font_path
return _DEFAULT_FONT_CACHE
except Exception:
pass
_DEFAULT_FONT_CACHE = None
return _DEFAULT_FONT_CACHE