Compare commits
3 Commits
cb3c17c5db
...
ce00b327bb
Author | SHA1 | Date |
---|---|---|
Christian Aribaud | ce00b327bb | |
Christian Aribaud | 7e52c5a532 | |
Christian Aribaud | 3d7428e76f |
19
README.md
19
README.md
|
@ -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,6 +110,23 @@ 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).
|
||||||
|
|
|
@ -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
|
||||||
import re
|
from argparse import ArgumentParser
|
||||||
import xml.etree.ElementTree as ET
|
from xml.etree import ElementTree
|
||||||
import argparse
|
from xml.etree.ElementTree import Element
|
||||||
|
from typing import Any
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,63 +14,84 @@ 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 extract_zooms(src_folder):
|
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)
|
folder = Path(src_folder)
|
||||||
|
|
||||||
zooms = {}
|
pages_zooms: dict[int, Any] = {}
|
||||||
|
|
||||||
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}")
|
||||||
|
|
||||||
zooms[idx] = {
|
# Setting up default values
|
||||||
|
pages_zooms[idx] = {
|
||||||
"name": svg_path.stem,
|
"name": svg_path.stem,
|
||||||
"width": 0,
|
"width": 0,
|
||||||
"height": 0,
|
"height": 0,
|
||||||
"zooms": [],
|
"zooms": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
tree = ET.parse(svg_path)
|
tree = ElementTree.parse(svg_path)
|
||||||
root = tree.getroot()
|
root = tree.getroot()
|
||||||
|
|
||||||
if "." in root.get("width"):
|
width = get_val_has_str(root, "width", svg_path)
|
||||||
print(f"WARNING: file {svg_path} has a floating width, it will be rounded", file=sys.stderr)
|
height = get_val_has_str(root, "height", svg_path)
|
||||||
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")))
|
|
||||||
|
|
||||||
for area in root.findall('.//{*}rect'):
|
if "." in width:
|
||||||
zooms[idx]["zooms"].append([
|
print(
|
||||||
float(area.get("width")),
|
f"WARNING: file {svg_path} has a floating width, it will be rounded",
|
||||||
float(area.get("height")),
|
file=sys.stderr,
|
||||||
float(area.get("x")),
|
)
|
||||||
float(area.get("y")),
|
pages_zooms[idx]["width"] = round(float(width))
|
||||||
])
|
|
||||||
|
|
||||||
return zooms, max_width, max_height
|
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):
|
def write_json_or_js(zooms, dest_file, is_js) -> None:
|
||||||
|
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:
|
||||||
if zoom[2] < 0 or zoom[3] < 0 :
|
print(
|
||||||
print(f"WARNING: negative pos x / pos y in page {page_idx} for zoom {zoom} (is the rectangle flipped?)")
|
f"WARNING: negative pos x / pos y in page {page_idx} for "
|
||||||
|
f"zoom {zoom} (is the rectangle flipped?)"
|
||||||
|
)
|
||||||
|
|
||||||
if first_coma_skiped:
|
if first_coma_skiped:
|
||||||
data_file.write(",\n")
|
data_file.write(",\n")
|
||||||
|
@ -80,45 +101,71 @@ def write_json_or_js(zooms, dest_file, is_js):
|
||||||
data_file.write("\n]\n")
|
data_file.write("\n]\n")
|
||||||
|
|
||||||
|
|
||||||
def write_html(zooms, dest_file, pages_width, pages_height, prefix, extention):
|
def write_html(zooms, dest_file, prefix, extention) -> None:
|
||||||
|
|
||||||
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 = [','.join([str(zoom) for zoom in page_zooms]) for page_zooms in zooms[page_idx]["zooms"]]
|
zoom_html_data = [
|
||||||
zoom_html_str = ';'.join(zoom_html_data)
|
",".join([str(zoom) for zoom in page_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'
|
for page_zooms in zooms[page_idx]["zooms"]
|
||||||
|
]
|
||||||
|
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) as template_file, open(dest_file, "w") as data_file:
|
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 = template_file.read().replace(HTML_TO_REPLACE, img_tags)
|
||||||
|
|
||||||
data_file.write(data)
|
data_file.write(data)
|
||||||
|
|
||||||
|
|
||||||
def generate_argparse():
|
def generate_argparse() -> ArgumentParser:
|
||||||
""" Generate Melpomene's generator input parser"""
|
"""Generate Melpomene's generator input parser"""
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = ArgumentParser(
|
||||||
description="Helper that can generate JSON / JS / HTML files for Melpomene webcomic reader"
|
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(
|
||||||
|
"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("-o", metavar="dest_file", help="Where to write the generator output to")
|
parser.add_argument(
|
||||||
parser.add_argument("-p", default="", metavar="img_url_prefix", help="What to prefix the URL of the images when using HTML format.")
|
"-o", metavar="dest_file", help="Where to write the generator output to"
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def run():
|
||||||
|
|
||||||
args = generate_argparse().parse_args()
|
args = generate_argparse().parse_args()
|
||||||
|
|
||||||
|
|
||||||
# Get the final outout name
|
# Get the final outout name
|
||||||
output = None
|
output = None
|
||||||
|
|
||||||
|
@ -136,13 +183,17 @@ if __name__ == "__main__":
|
||||||
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, max_width, max_height = extract_zooms(args.svg_folders)
|
zooms = extract_zooms(args.svg_folders)
|
||||||
|
|
||||||
if args.output_format == "html":
|
if args.output_format == "html":
|
||||||
write_html(zooms, output, max_width, max_height, args.p, args.e)
|
write_html(zooms, output, 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()
|
||||||
|
|
Loading…
Reference in New Issue