import json
import re
import sys
from collections.abc import Iterator
FIGURE = re.compile(r"<figure(?P<attributes>[^>]*)>(?P<body>.*?)</figure>", re.S)
CAPTION = re.compile(r"<figcaption>(?P<space>\s*)(?:Figure:\s*)?")
IDENTIFIER = re.compile(r'\bid="(?P<id>[^"]+)"')
REFERENCE = re.compile(r"\[Figure\]\((?P<target>[^)#\s]*#(?P<id>[^)\s]+))\)")
REFERENCE_HTML = re.compile(
r'<a href="(?P<target>[^"#\s]*#(?P<id>[^"\s]+))">Figure</a>'
)
def figures_number(*, content: str, section: str, labels: dict[str, str]) -> str:
count = 0
def label(match: re.Match) -> str:
nonlocal count
count += 1
name = f"{section}-{count}"
attributes = match["attributes"]
identifier = IDENTIFIER.search(attributes)
if identifier:
labels[identifier["id"]] = name
else:
attributes += f' id="figure-{name}"'
labels[f"figure-{name}"] = name
body = CAPTION.sub(
lambda caption: (
f"<figcaption>{caption['space']}<strong>Figure {name}:</strong> "
),
match["body"],
count=1,
)
body = body.replace(":</strong> </figcaption>", "</strong></figcaption>")
return f"<figure{attributes}>{body}</figure>"
return FIGURE.sub(label, content)
def references_resolve(*, content: str, labels: dict[str, str], path: str) -> str:
def name(match: re.Match) -> str | None:
found = labels.get(match["id"])
if found is None:
print(
f"figure_number: {path}: no figure with id {match['id']!r}",
file=sys.stderr,
)
return found
def markdown(match: re.Match) -> str:
found = name(match)
return match[0] if found is None else f"[Figure {found}]({match['target']})"
def html(match: re.Match) -> str:
found = name(match)
return (
match[0]
if found is None
else f'<a href="{match["target"]}">Figure {found}</a>'
)
return REFERENCE_HTML.sub(html, REFERENCE.sub(markdown, content))
def chapters_walk(*, sections: list) -> Iterator[dict]:
for item in sections:
if isinstance(item, dict) and "Chapter" in item:
yield item["Chapter"]
yield from chapters_walk(sections=item["Chapter"]["sub_items"])
def sections_number(*, sections: list) -> None:
labels: dict[str, str] = {}
for chapter in chapters_walk(sections=sections):
if chapter["number"]:
section = ".".join(str(part) for part in chapter["number"])
chapter["content"] = figures_number(
content=chapter["content"], section=section, labels=labels
)
for chapter in chapters_walk(sections=sections):
chapter["content"] = references_resolve(
content=chapter["content"],
labels=labels,
path=chapter.get("path") or chapter.get("name", "?"),
)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "supports":
sys.exit(0)
_context, book = json.load(sys.stdin)
sections_number(sections=book["sections"])
json.dump(book, sys.stdout)