diff --git a/binaries/data/mods/mod/gui/gui.rng b/binaries/data/mods/mod/gui/gui.rng index 20ab20476e..995e7affae 100644 --- a/binaries/data/mods/mod/gui/gui.rng +++ b/binaries/data/mods/mod/gui/gui.rng @@ -87,6 +87,11 @@ + + + (mono|sans)-(bold-|italic-)?(stroke-)?[0-9]{1,3} + + @@ -272,7 +277,9 @@ - + + + @@ -829,7 +836,9 @@ - + + + diff --git a/source/tools/entity/checkrefs.py b/source/tools/entity/checkrefs.py index d16f995e55..4e5917ea7e 100755 --- a/source/tools/entity/checkrefs.py +++ b/source/tools/entity/checkrefs.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +import configparser import re import sys from argparse import ArgumentParser @@ -37,7 +38,6 @@ class CheckRefs: "styles": [], "scrollbars": [], "tooltips": [], - "fonts": [], "colors": [], } self.deps_gui_items: dict[str, list[tuple[Path, str]]] = { @@ -46,7 +46,6 @@ class CheckRefs: "styles": [], "scrollbars": [], "tooltips": [], - "fonts": [], "colors": [], } # Three or four positive integers below 256 separated by spaces @@ -57,7 +56,6 @@ class CheckRefs: self.supportedMeshesFormats = ("pmd", "dae") self.supportedAnimationFormats = ("psa", "dae") self.supportedAudioFormats = "ogg" - self.supportedFontFormats = "fnt" self.mods = [] self.__init_logger() self.inError = False @@ -145,7 +143,6 @@ class CheckRefs: self.add_particles() self.add_soundgroups() self.add_audio() - self.add_fonts() self.add_gui_xml() self.add_gui_data() self.add_civs() @@ -156,6 +153,7 @@ class CheckRefs: self.add_tips() self.check_deps() self.check_deps_gui_items() + self.check_fonts() if args.check_unused: self.check_unused() if args.validate_templates: @@ -613,14 +611,6 @@ class CheckRefs: [fp for (fp, ffp) in self.find_files("audio/", self.supportedAudioFormats)] ) - def add_fonts(self): - self.logger.info("Loading fonts...") - for fp, _ffp in sorted(self.find_files("fonts/", "png")): - self.files.append(fp) - for fp, _ffp in sorted(self.find_files("fonts/", self.supportedFontFormats)): - self.gui_items["fonts"].append(fp.stem) - self.deps.append((fp, fp.with_suffix(".png"))) - def add_gui_object(self, parent, fp): if parent is None: return @@ -635,8 +625,6 @@ class CheckRefs: for attr, val in parent.attrib.items(): if attr == "name": self.gui_items["gui_objects"].append(val) - elif attr == "font": - self.deps_gui_items["fonts"].append((fp, val)) elif attr == "textcolor": self.add_gui_color_attrib(val, fp) elif "sound" in attr: @@ -748,8 +736,6 @@ class CheckRefs: self.add_gui_sprite_attrib(val, fp) elif attr == "textcolor": self.add_gui_color_attrib(val, fp) - elif attr == "font": - self.deps_gui_items["fonts"].append((fp, val)) elif obj.tag == "color": if "name" in obj.attrib: self.gui_items["colors"].append(obj.get("name")) @@ -984,6 +970,45 @@ class CheckRefs: self.InError = True + def check_fonts(self): + """Check that fonts referenced in default.cfg exist.""" + self.logger.info("Looking for missing font files...") + + base_path = Path(__file__).parents[3] + default_cfg_path = base_path / "binaries/data/config/default.cfg" + font_dir = self.vfs_root / "mod/fonts" + + try: + config = configparser.ConfigParser(allow_unnamed_section=True) + config.read(default_cfg_path) + except TypeError: + # fallback for Python <3.13 + configparser.UNNAMED_SECTION = "" + config = configparser.ConfigParser() + content = "[" + configparser.UNNAMED_SECTION + "]\n" + with default_cfg_path.open() as f: + content += f.read() + config.read_string(content) + + referenced_fonts = set() + for section in config.sections(): + if section == configparser.UNNAMED_SECTION or not section.startswith("fonts."): + continue + + for key, value in config[section].items(): + if key.split(".")[-1] in ["regular", "bold", "italic"]: + for font_name in value.split(","): + referenced_fonts.add(font_name.strip('"')) + + for font in referenced_fonts: + if not (font_dir / font).is_file(): + self.logger.error( + "Missing font '%s' referenced by: %s", + font, + default_cfg_path.relative_to(base_path), + ) + self.inError = True + def check_unused(self): self.logger.info("Looking for unused files...") deps = defaultdict(set)