Compare commits

..

No commits in common. "ce00b327bbdde6fb15db1114c8f7cd893a79460b" and "cb3c17c5dbfba1b5c54462413400d8ce85e8808c" have entirely different histories.

2 changed files with 74 additions and 142 deletions

View File

@ -96,7 +96,7 @@ If you need to do some global scaling / offset of all zooms in HTML (if for exam
## Setting up quality checking for JS ## Setting up quality checking for JS
Regarding JS, quality checking is done using [eslint](https://eslint.org/). Regarding JS, quality checking is done using [eslint](https://eslint.org/).
The configuration file is `eslint/eslintrc.json`. The configuration file is `eslint/eslintrc.json`.
@ -110,23 +110,6 @@ To do so, assuming you are using linux, after installing docker, you can run fro
You can now open `eslint_report.html` to see the result. You can now open `eslint_report.html` to see the result.
## Setting up quality checking for Python
Regarding Python, quality checking is done using [prospector](https://prospector.landscape.io/en/master/) using [mypy](https://mypy.readthedocs.io/en/stable/) as an additional checker.
Auto-formating is done using [Black](https://pypi.org/project/black/)
Dependencies are managed not using `pip` but [`pipenv`](https://pipenv.pypa.io/en/latest/).
To setup prospector, you need to run:
+ Only once, in melpomene's root folder:
+ Install pipenv: `pip install pipenv`
+ Install melpomene's root folder: `pipenv install --dev`
+ Every time to run the quality checking:
+ `pipenv shell`
+ `prospector -s veryhigh -w mypy --max-line-length 88 .`
+ `black .`
# Credits # Credits
Most examples and the documentation of Melpomene uses illustrations from David "Deevad" Revoy's "Pepper & Carrot" webcomic, which is published under CC-BY 4.0. Full licence [here](https://www.peppercarrot.com/en/license/index.html).  Most examples and the documentation of Melpomene uses illustrations from David "Deevad" Revoy's "Pepper & Carrot" webcomic, which is published under CC-BY 4.0. Full licence [here](https://www.peppercarrot.com/en/license/index.html). 

View File

@ -3,10 +3,10 @@
# CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/ # CC-BY-NC-SA https://git.aribaud.net/caribaud/melpomene/
import sys import sys
from argparse import ArgumentParser import re
from xml.etree import ElementTree import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element import argparse
from typing import Any
from pathlib import Path from pathlib import Path
@ -14,84 +14,63 @@ HTML_TEMPLATE = Path(__file__).parent / "melpomene.html"
HTML_TO_REPLACE = "<!-- your img tags here, see documentation -->" HTML_TO_REPLACE = "<!-- your img tags here, see documentation -->"
def get_val_has_str(elem: Element, attrib: str, filepath: str | Path) -> str: def extract_zooms(src_folder):
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) folder = Path(src_folder)
pages_zooms: dict[int, Any] = {} zooms = {}
max_width = 0
max_height = 0
idx = 0 idx = 0
for svg_path in folder.glob("*.svg"): for svg_path in folder.glob("*.svg"):
idx += 1 idx += 1
print(f"page {idx} : {svg_path.name}") print(f"page {idx} : {svg_path.name}")
# Setting up default values zooms[idx] = {
pages_zooms[idx] = {
"name": svg_path.stem, "name": svg_path.stem,
"width": 0, "width": 0,
"height": 0, "height": 0,
"zooms": [], "zooms": [],
} }
tree = ElementTree.parse(svg_path) tree = ET.parse(svg_path)
root = tree.getroot() root = tree.getroot()
width = get_val_has_str(root, "width", svg_path) if "." in root.get("width"):
height = get_val_has_str(root, "height", svg_path) print(f"WARNING: file {svg_path} has a floating width, it will be rounded", file=sys.stderr)
zooms[idx]["width"] = round(float(root.get("width")))
if "." in root.get("height"):
print(f"WARNING: file {svg_path} has a floating height, it will be rounded", file=sys.stderr)
zooms[idx]["height"] = round(float(root.get("height")))
if "." in width: for area in root.findall('.//{*}rect'):
print( zooms[idx]["zooms"].append([
f"WARNING: file {svg_path} has a floating width, it will be rounded", float(area.get("width")),
file=sys.stderr, float(area.get("height")),
) float(area.get("x")),
pages_zooms[idx]["width"] = round(float(width)) float(area.get("y")),
])
if "." in height: return zooms, max_width, max_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: def write_json_or_js(zooms, dest_file, is_js):
with open(dest_file, "w", encoding="UTF-8") as data_file:
with open(dest_file, "w") as data_file:
if is_js: if is_js:
data_file.write("PAGES_ZOOMS = ") data_file.write("PAGES_ZOOMS = ")
data_file.write("[\n") data_file.write("[\n")
first_coma_skiped = False first_coma_skiped = False
for page_idx in sorted(zooms.keys()): for page_idx in sorted(zooms.keys()):
for zoom in zooms[page_idx]["zooms"]: for zoom in zooms[page_idx]["zooms"]:
if zoom[2] < 0 or zoom[3] < 0:
print( if zoom[2] < 0 or zoom[3] < 0 :
f"WARNING: negative pos x / pos y in page {page_idx} for " print(f"WARNING: negative pos x / pos y in page {page_idx} for zoom {zoom} (is the rectangle flipped?)")
f"zoom {zoom} (is the rectangle flipped?)"
)
if first_coma_skiped: if first_coma_skiped:
data_file.write(",\n") data_file.write(",\n")
@ -101,71 +80,45 @@ def write_json_or_js(zooms, dest_file, is_js) -> None:
data_file.write("\n]\n") data_file.write("\n]\n")
def write_html(zooms, dest_file, prefix, extention) -> None: def write_html(zooms, dest_file, pages_width, pages_height, prefix, extention):
img_tags = "" img_tags = ""
for page_idx in sorted(zooms.keys()): for page_idx in sorted(zooms.keys()):
img_url = f"{prefix}{zooms[page_idx]['name']}.{extention}" img_url = f"{prefix}{zooms[page_idx]['name']}.{extention}"
zoom_html_data = [ zoom_html_data = [','.join([str(zoom) for zoom in page_zooms]) for page_zooms in zooms[page_idx]["zooms"]]
",".join([str(zoom) for zoom in page_zooms]) zoom_html_str = ';'.join(zoom_html_data)
for page_zooms in zooms[page_idx]["zooms"] img_tags = img_tags + f' <img loading="lazy" height="{zooms[page_idx]["height"]}" width="{zooms[page_idx]["width"]}" src="{img_url}" data-zooms="{zoom_html_str}"/>\n'
]
zoom_html_str = ";".join(zoom_html_data)
img_tags = (
img_tags
+ " "
+ f'<img loading="lazy" height="{zooms[page_idx]["height"]}" '
+ f'width="{zooms[page_idx]["width"]}" src="{img_url}" '
+ f'data-zooms="{zoom_html_str}"/>\n'
)
img_tags = img_tags.strip() img_tags = img_tags.strip()
with open(HTML_TEMPLATE, "r", encoding="UTF-8") as template_file, open( with open(HTML_TEMPLATE) as template_file, open(dest_file, "w") as data_file:
dest_file, "w", encoding="UTF-8"
) as data_file:
data = template_file.read().replace(HTML_TO_REPLACE, img_tags) data = template_file.read().replace(HTML_TO_REPLACE, img_tags)
data_file.write(data) data_file.write(data)
def generate_argparse() -> ArgumentParser: def generate_argparse():
"""Generate Melpomene's generator input parser""" """ Generate Melpomene's generator input parser"""
parser = ArgumentParser( parser = argparse.ArgumentParser(
description=( description="Helper that can generate JSON / JS / HTML files for Melpomene webcomic reader"
"Helper that can generate JSON / JS / "
"HTML files for Melpomene webcomic reader"
)
) )
parser.add_argument( parser.add_argument("output_format", choices=["html", "json", "js"], help="The type of output to generate")
"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("svg_folders", help="Path of the folder containing the SVGs")
parser.add_argument( parser.add_argument("-o", metavar="dest_file", help="Where to write the generator output to")
"-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.")
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 return parser
def run(): if __name__ == "__main__":
args = generate_argparse().parse_args() args = generate_argparse().parse_args()
# Get the final outout name # Get the final outout name
output = None output = None
@ -183,17 +136,13 @@ def run():
elif args.output_format == "js" and not output.endswith(".js"): elif args.output_format == "js" and not output.endswith(".js"):
output += ".js" output += ".js"
zooms = extract_zooms(args.svg_folders) zooms, max_width, max_height = extract_zooms(args.svg_folders)
if args.output_format == "html": if args.output_format == "html":
write_html(zooms, output, args.p, args.e) write_html(zooms, output, max_width, max_height, args.p, args.e)
elif args.output_format == "json": elif args.output_format == "json":
write_json_or_js(zooms, output, False) write_json_or_js(zooms, output, False)
elif args.output_format == "js": elif args.output_format == "js":
write_json_or_js(zooms, output, True) write_json_or_js(zooms, output, True)
if __name__ == "__main__":
run()