From e92cf6c761e8e5a8e1654578ea63eb0032c84c41 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:56:00 +0200 Subject: [PATCH] Update icon builds --- pkgutils.py | 16 +++++--- utils/icon_themes.py | 89 ++++++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/pkgutils.py b/pkgutils.py index beb5fece..fd9ecf67 100755 --- a/pkgutils.py +++ b/pkgutils.py @@ -171,11 +171,15 @@ if __name__ == "__main__": # ================= # Build Icons + styles = ", ".join([ + "all", "default", "optional", "free", "non_free", + *utils.icon_themes.ICON_SOURCES.keys() + ]) cmdIcons = parsers.add_parser( - "icons", help="Build icon theme files from source." + "icons", help="Build icon theme files from upstream sources." ) - cmdIcons.add_argument("sources", help="Working directory for sources.") - cmdIcons.add_argument("style", help="What icon style to build.") + cmdIcons.add_argument("style", help=f"What icon style to build: {styles}") + cmdIcons.add_argument("--work-dir", help="Working directory.", default="build_icons") cmdIcons.set_defaults(func=utils.icon_themes.main) # Import Translations @@ -275,9 +279,9 @@ if __name__ == "__main__": # See https://github.com/pypa/manylinux # See https://python-appimage.readthedocs.io/en/latest/#available-python-appimages cmdBuildAppImage = parsers.add_parser("build-appimage", help="Build an AppImage.") - cmdBuildAppImage.add_argument("linux", help="Manylinux version, e.g. manylinux_2_28.") - cmdBuildAppImage.add_argument("arch", help="Architecture, e.g. x86_64.") - cmdBuildAppImage.add_argument("python", help="Python version, e.g. 3.13.") + cmdBuildAppImage.add_argument("linux", help="Manylinux version, e.g. manylinux_2_28") + cmdBuildAppImage.add_argument("arch", help="Architecture, e.g. x86_64") + cmdBuildAppImage.add_argument("python", help="Python version, e.g. 3.13") cmdBuildAppImage.set_defaults(func=utils.build_appimage.appImage) # Build Windows Inno Setup Installer diff --git a/utils/icon_themes.py b/utils/icon_themes.py index 00d75714..b9ddf16e 100644 --- a/utils/icon_themes.py +++ b/utils/icon_themes.py @@ -23,7 +23,8 @@ from __future__ import annotations import argparse import json import subprocess -import sys +import urllib.request +import zipfile from pathlib import Path from xml.etree import ElementTree as ET @@ -32,6 +33,17 @@ from utils.common import ROOT_DIR UTILS = Path(__file__).parent ET.register_namespace("", "http://www.w3.org/2000/svg") + +ICON_SOURCES = { + "material": "https://github.com/google/material-design-icons.git", + "font_awesome": "https://github.com/FortAwesome/Font-Awesome/archive/refs/tags/6.7.2.zip", + "remix": "https://github.com/Remix-Design/RemixIcon/archive/refs/tags/v4.6.0.zip", +} +ICON_EXTRACT = { + "material": "material-design-icons", + "font_awesome": "Font-Awesome-6.7.2", + "remix": "RemixIcon-4.6.0", +} ICONS = [ "alert_error", "alert_info", @@ -183,21 +195,33 @@ def _writeThemeFile( return -def _cloneRepo(repoPath: Path, repoUrl: str) -> None: +def _updateRepo(path: Path, name: str) -> None: """Clone or update a local repo of icons.""" - print(f"Updating: {repoUrl}") - if not repoPath.is_dir(): - subprocess.call(["git", "clone", repoUrl, "--depth", "50"], cwd=repoPath.parent) + print(f"Updating: {ICON_SOURCES[name]}") + if not path.is_dir(): + subprocess.call(["git", "clone", ICON_SOURCES[name], "--depth", "1"], cwd=path.parent) else: - subprocess.call(["git", "pull"], cwd=repoPath) + subprocess.call(["git", "pull"], cwd=path) + print("") + return + + +def _downloadIconPack(path: Path, name: str) -> None: + """Download and extract icon pack releases.""" + print(f"Downloading: {ICON_SOURCES[name]}") + zipFile = path / f"{name}.zip" + urllib.request.urlretrieve(ICON_SOURCES[name], zipFile) + print(f"Extracting: {zipFile.name}") + with zipfile.ZipFile(zipFile, "r") as inFile: + inFile.extractall(path) print("") return def processMaterialIcons(workDir: Path, iconsDir: Path, jobs: dict) -> None: """Process material icons of a given spec and write output file.""" - srcRepo = workDir / "material-design-icons" - _cloneRepo(srcRepo, "https://github.com/google/material-design-icons.git") + srcRepo = workDir / ICON_EXTRACT["material"] + _updateRepo(srcRepo, "material") for file, job in jobs.items(): name: str = job["name"] @@ -233,8 +257,9 @@ def processMaterialIcons(workDir: Path, iconsDir: Path, jobs: dict) -> None: def processFontAwesome(workDir: Path, iconsDir: Path, jobs: dict) -> None: """Process Font Awesome icons of a given spec and write output file.""" - srcRepo = workDir / "Font-Awesome" - _cloneRepo(srcRepo, "https://github.com/FortAwesome/Font-Awesome.git") + srcRepo = workDir / ICON_EXTRACT["font_awesome"] + if not srcRepo.is_dir(): + _downloadIconPack(workDir, "font_awesome") for file, job in jobs.items(): name: str = job["name"] @@ -278,8 +303,9 @@ def processFontAwesome(workDir: Path, iconsDir: Path, jobs: dict) -> None: def processRemix(workDir: Path, iconsDir: Path, jobs: dict) -> None: """Process Remix icons of a given spec and write output file.""" - srcRepo = workDir / "RemixIcon" - _cloneRepo(srcRepo, "https://github.com/Remix-Design/RemixIcon.git") + srcRepo = workDir / ICON_EXTRACT["remix"] + if not srcRepo.is_dir(): + _downloadIconPack(workDir, "remix") for file, job in jobs.items(): name: str = job["name"] @@ -326,15 +352,12 @@ def main(args: argparse.Namespace) -> None: print("=================") print("") - workDir = Path(args.sources).absolute() - if not workDir.is_dir(): - print(f"Source directory not found: {workDir}") - sys.exit(1) - + workDir = Path(args.work_dir).absolute() + workDir.mkdir(exist_ok=True) iconsDir = ROOT_DIR / "novelwriter" / "assets" / "icons" style = args.style - if style in ("all", "material"): + if style in ("all", "default", "material"): processMaterialIcons(workDir, iconsDir, { "material_rounded_thin": { "name": "Material Symbols - Rounded Thin", @@ -343,17 +366,11 @@ def main(args: argparse.Namespace) -> None: "weight": 200, }, "material_rounded_normal": { - "name": "Material Symbols - Rounded Medium", + "name": "Material Symbols - Rounded", "style": "rounded", "filled": False, "weight": 400, }, - "material_rounded_bold": { - "name": "Material Symbols - Rounded Bold", - "style": "rounded", - "filled": False, - "weight": 600, - }, "material_filled_thin": { "name": "Material Symbols - Filled Thin", "style": "rounded", @@ -361,27 +378,33 @@ def main(args: argparse.Namespace) -> None: "weight": 200, }, "material_filled_normal": { - "name": "Material Symbols - Filled Medium", + "name": "Material Symbols - Filled", "style": "rounded", "filled": True, "weight": 400, }, - "material_filled_bold": { - "name": "Material Symbols - Filled Bold", - "style": "rounded", - "filled": True, - "weight": 600, + "material_sharp_thin": { + "name": "Material Symbols - Sharp Thin", + "style": "sharp", + "filled": False, + "weight": 200, + }, + "material_sharp_normal": { + "name": "Material Symbols - Sharp", + "style": "sharp", + "filled": False, + "weight": 400, }, }) - if style in ("all", "fa"): + if style in ("all", "optional", "free", "font_awesome"): processFontAwesome(workDir, iconsDir, { "font_awesome": { "name": "Font Awesome 6", }, }) - if style in ("all", "remix"): + if style in ("all", "optional", "non_free", "remix"): processRemix(workDir, iconsDir, { "remix_outline": { "name": "Remix Icon - Outline",