diff --git a/pkgutils.py b/pkgutils.py index 03e07af4..be32d51f 100755 --- a/pkgutils.py +++ b/pkgutils.py @@ -35,6 +35,8 @@ import zipfile from pathlib import Path +from utils.material_icons import processMaterialIcons + CURR_DIR = Path(__file__).parent SETUP_DIR = CURR_DIR / "setup" SIGN_KEY = "D6A9F6B8F227CF7C6F6D1EE84DBBE4B734B0BD08" @@ -323,6 +325,44 @@ def buildSampleZip(args: argparse.Namespace | None = None) -> None: return +## +# Import Translations (import-i18n) +## + +def buildIconTheme(args: argparse.Namespace) -> None: + """Build an icon theme.""" + print("") + print("Build Icon Theme") + print("================") + print("") + + workDir = Path(args.sources).absolute() + if not workDir.is_dir(): + print(f"Source directory not found: {workDir}") + sys.exit(1) + + iconsDir = CURR_DIR / "novelwriter" / "assets" / "icons" + + style = args.style + if style == "material": + processMaterialIcons( + workDir, iconsDir / "material_outline_normal.icons", "outlined", False, 400 + ) + processMaterialIcons( + workDir, iconsDir / "material_filled_normal.icons", "outlined", True, 400 + ) + processMaterialIcons( + workDir, iconsDir / "material_outline_bold.icons", "outlined", False, 700 + ) + processMaterialIcons( + workDir, iconsDir / "material_filled_bold.icons", "outlined", True, 700 + ) + + print("") + + return + + ## # Import Translations (import-i18n) ## @@ -1414,6 +1454,14 @@ if __name__ == "__main__": # Additional Builds # ================= + # Build Icons + cmdIcons = parsers.add_parser( + "icons", help="Build icon theme files from source." + ) + cmdIcons.add_argument("sources", help="Working directory for sources.") + cmdIcons.add_argument("style", help="What icon style to build.") + cmdIcons.set_defaults(func=buildIconTheme) + # Import Translations cmdImportTS = parsers.add_parser( "qtlimport", help="Import updated i18n files from a Crowdin zip file." diff --git a/utils/material_icons.py b/utils/material_icons.py new file mode 100644 index 00000000..45292969 --- /dev/null +++ b/utils/material_icons.py @@ -0,0 +1,129 @@ +""" +novelWriter – Material Icon Theme +================================= + +This file is a part of novelWriter +Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" +from __future__ import annotations + +import subprocess + +from pathlib import Path + +from lxml import etree + +MATERIAL_REPO = "https://github.com/google/material-design-icons.git" +GRADE = "" + +ICON_MAP = { + "alert_error": "error", + "alert_info": "info", + "alert_question": "help", + "alert_warn": "warning", + + "cls_archive": "archive", + "cls_character": "group", + "cls_custom": "label", + "cls_entity": "apartment", + "cls_none": "close", + "cls_novel": "book_2", + "cls_object": "key", + "cls_plot": "extension", + "cls_template": "topic", + "cls_timeline": "hourglass_empty", + "cls_trash": "delete", + "cls_world": "globe", + + "fmt_bold": "format_bold", + "fmt_italic": "format_italic", + "fmt_mark": "format_ink_highlighter", + "fmt_strike": "format_strikethrough", + "fmt_subscript": "subscript", + "fmt_superscript": "superscript", + "fmt_underline": "format_underlined", + "fmt_toolbar": "text_format", + + "search_cancel": "close", + "search_case": "match_case", + "search_loop": "laps", + "search_preserve": "text_fields", + "search_project": "document_search", + "search_regex": "regular_expression", + "search_replace": "find_replace", + "search_word": "match_word", + "search": "search", + + "bullet-off": "radio_button_unchecked", + "bullet-on": "radio_button_checked", + + "bookmarks": "bookmarks", + "checked": "select_check_box", + "chevron_down": "keyboard_arrow_down", + "chevron_left": "arrow_back_ios", + "chevron_right": "arrow_forward_ios", + "chevron_up": "keyboard_arrow_up", + "close": "close", + "document_add": "note_add", + "document": "description", + "edit": "edit", + "folder": "folder", + "item_add": "add", + "list": "format_list_bulleted", + "maximise": "fullscreen", + "minimise": "close_fullscreen", + "more_vertical": "more_vert", + "noncheckable": "indeterminate_check_box", + "project_copy": "folder_copy", + "refresh": "refresh", + "unchecked": "disabled_by_default", +} + + +def _fixXml(svg: str) -> str: + """Clean up the SVG XML and add needed fields.""" + xSvg = etree.fromstring(svg) # type: ignore + xSvg.set("fill", "#000000") + xSvg.set("height", "128") + xSvg.set("width", "128") + return etree.tostring(xSvg).decode() + + +def processMaterialIcons(workDir: Path, output: Path, style: str, fill: bool, weight: int) -> None: + """Process material icons of a given spec and write output file.""" + srcRepo = workDir / "material-design-icons" + if not srcRepo.is_dir(): + subprocess.call(["git", "clone", MATERIAL_REPO, "--depth", "50"], cwd=workDir) + else: + subprocess.call(["git", "pull"], cwd=srcRepo) + + kind = f"wght{weight}" if weight != 400 else "" + kind += "fill1" if fill else "" + + with open(output, mode="w", encoding="utf-8") as icons: + iconSrc = srcRepo / "symbols" / "web" + for key, name in ICON_MAP.items(): + if kind: + fileNmae = f"{name}_{kind}_24px.svg" + else: + fileNmae = f"{name}_24px.svg" + iconFile = iconSrc / name / f"materialsymbols{style}" / fileNmae + if iconFile.is_file(): + svg = iconFile.read_text(encoding="utf-8") + icons.write(f"{key:<15s} = {_fixXml(svg)}\n") + print(f"Wrote: {iconFile.stem}") + else: + print(f"Not Found: {iconFile}")