# Melpomene webcomic reader JSON/JS/HTML generator # Version 1.0.0_RC1 # CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/ import sys from argparse import ArgumentParser from xml.etree import ElementTree from xml.etree.ElementTree import Element from typing import Any from pathlib import Path HTML_TEMPLATE = Path(__file__).parent / "melpomene.html" HTML_TO_REPLACE = "" def get_val_has_str(elem: Element, attrib: str, filepath: str | Path) -> str: value = elem.get(attrib) if value is None: sys.exit(f"Attribute '{attrib}' is not valid in file {filepath}") return str(value) def extract_zooms(src_folder) -> dict[int, Any]: folder = Path(src_folder) pages_zooms: dict[int, Any] = {} idx = 0 for svg_path in folder.glob("*.svg"): idx += 1 print(f"page {idx} : {svg_path.name}") # Setting up default values pages_zooms[idx] = { "name": svg_path.stem, "width": 0, "height": 0, "zooms": [], } tree = ElementTree.parse(svg_path) root = tree.getroot() width = get_val_has_str(root, "width", svg_path) height = get_val_has_str(root, "height", svg_path) if "." in width: print( f"WARNING: file {svg_path} has a floating width, it will be rounded", file=sys.stderr, ) pages_zooms[idx]["width"] = round(float(width)) if "." in height: print( f"WARNING: file {svg_path} has a floating height, it will be rounded", file=sys.stderr, ) pages_zooms[idx]["height"] = round(float(height)) zooms = [] for area in root.findall(".//{*}rect"): zooms.append( [ float(get_val_has_str(area, "width", svg_path)), float(get_val_has_str(area, "height", svg_path)), float(get_val_has_str(area, "x", svg_path)), float(get_val_has_str(area, "y", svg_path)), ] ) pages_zooms[idx]["zooms"] = zooms return pages_zooms def write_json_or_js(zooms, dest_file, is_js) -> None: with open(dest_file, "w", encoding="UTF-8") as data_file: if is_js: data_file.write("PAGES_ZOOMS = ") data_file.write("[\n") first_coma_skiped = False for page_idx in sorted(zooms.keys()): for zoom in zooms[page_idx]["zooms"]: if zoom[2] < 0 or zoom[3] < 0: print( f"WARNING: negative pos x / pos y in page {page_idx} for " f"zoom {zoom} (is the rectangle flipped?)" ) if first_coma_skiped: data_file.write(",\n") else: first_coma_skiped = True data_file.write(f" {[page_idx] + zoom}") data_file.write("\n]\n") def write_html(zooms, dest_file, prefix, extention) -> None: img_tags = "" for page_idx in sorted(zooms.keys()): img_url = f"{prefix}{zooms[page_idx]['name']}.{extention}" zoom_html_data = [ ",".join([str(zoom) for zoom in page_zooms]) for page_zooms in zooms[page_idx]["zooms"] ] zoom_html_str = ";".join(zoom_html_data) img_tags = ( img_tags + " " + f'\n' ) img_tags = img_tags.strip() with open(HTML_TEMPLATE, "r", encoding="UTF-8") as template_file, open( dest_file, "w", encoding="UTF-8" ) as data_file: data = template_file.read().replace(HTML_TO_REPLACE, img_tags) data_file.write(data) def generate_argparse() -> ArgumentParser: """Generate Melpomene's generator input parser""" parser = ArgumentParser( description=( "Helper that can generate JSON / JS / " "HTML files for Melpomene webcomic reader" ) ) parser.add_argument( "output_format", choices=["html", "json", "js"], help="The type of output to generate", ) parser.add_argument("svg_folders", help="Path of the folder containing the SVGs") parser.add_argument( "-o", metavar="dest_file", help="Where to write the generator output to" ) parser.add_argument( "-p", default="", metavar="img_url_prefix", help="What to prefix the URL of the images when using HTML format.", ) parser.add_argument( "-e", default="png", metavar="img_ext", help="What extention to use in the URL of the images when using HTML format.", ) return parser def run(): args = generate_argparse().parse_args() # Get the final outout name output = None if not args.o: output = "melpomene_data" else: output = args.o if args.output_format == "html" and not output.endswith(".html"): output += ".html" elif args.output_format == "json" and not output.endswith(".json"): output += ".json" elif args.output_format == "js" and not output.endswith(".js"): output += ".js" zooms = extract_zooms(args.svg_folders) if args.output_format == "html": write_html(zooms, output, args.p, args.e) elif args.output_format == "json": write_json_or_js(zooms, output, False) elif args.output_format == "js": write_json_or_js(zooms, output, True) if __name__ == "__main__": run()