-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmain.py
More file actions
136 lines (108 loc) · 4.81 KB
/
Copy pathmain.py
File metadata and controls
136 lines (108 loc) · 4.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from __future__ import annotations
import json
import shutil
from pathlib import Path
from typing import Any, Dict
from jinja2 import Environment, FileSystemLoader, StrictUndefined, select_autoescape
from urllib.error import URLError
from urllib.request import urlopen
from weasyprint import CSS, HTML
from weasyprint.text.fonts import FontConfiguration
ROOT = Path(__file__).resolve().parent
FONTS_DIR = ROOT / "fonts"
DIST_DIR = ROOT / "dist"
DIST_FONTS_DIR = DIST_DIR / "fonts"
# Remote font sources for Sinhala and Latin text rendering
FONT_SOURCES = {
"AbhayaLibre-Regular.ttf": "https://github.com/google/fonts/raw/main/ofl/abhayalibre/AbhayaLibre-Regular.ttf",
"AbhayaLibre-Bold.ttf": "https://github.com/google/fonts/raw/main/ofl/abhayalibre/AbhayaLibre-Bold.ttf",
}
def load_data(path: Path) -> Dict[str, Any]:
"""Load the structured almanac data from *path*."""
with path.open("r", encoding="utf-8") as handle:
return json.load(handle)
def build_environment(template_dir: Path) -> Environment:
"""Create a Jinja2 environment with strict undefined checks."""
return Environment(
loader=FileSystemLoader(str(template_dir)),
undefined=StrictUndefined,
autoescape=select_autoescape(["html", "xml"]),
)
def ensure_fonts_available(font_dir: Path) -> None:
"""Download required fonts to *font_dir* if they are missing."""
font_dir.mkdir(parents=True, exist_ok=True)
for filename, url in FONT_SOURCES.items():
destination = font_dir / filename
if destination.exists():
continue
try:
with urlopen(url) as response:
destination.write_bytes(response.read())
print(f"Downloaded font '{filename}'.")
except URLError as exc:
print(
f"Warning: Could not download '{filename}' ({exc}). "
"Sinhala text may not render correctly."
)
except OSError as exc:
print(
f"Warning: Could not store font '{filename}' at {destination} ({exc}). "
"Sinhala text may not render correctly."
)
def copy_stylesheet(css_path: Path, destination_dir: Path) -> Path:
"""Copy the stylesheet to the distribution directory."""
destination_dir.mkdir(parents=True, exist_ok=True)
target_css = destination_dir / css_path.name
shutil.copy2(css_path, target_css)
print(f"Stylesheet '{css_path.name}' copied to '{target_css.resolve()}'.")
return target_css
def copy_fonts(source_dir: Path, destination_dir: Path) -> None:
"""Copy all fonts to the distribution fonts directory."""
destination_dir.mkdir(parents=True, exist_ok=True)
for font_file in sorted(source_dir.glob("*.ttf"), key=lambda p: p.name):
target_path = destination_dir / font_file.name
shutil.copy2(font_file, target_path)
print(f"Font '{font_file.name}' copied to '{target_path.resolve()}'.")
def render_almanac(
data_path: Path = ROOT / "data.json",
template_name: str = "index.html",
css_path: Path = ROOT / "style.css",
output_pdf: Path = DIST_DIR / "almanac.pdf",
output_html: Path = DIST_DIR / "almanac.html",
) -> None:
"""Render the HTML template with JSON data and export both HTML and PDF."""
try:
ensure_fonts_available(FONTS_DIR)
data = load_data(data_path)
env = build_environment(template_dir=ROOT)
template = env.get_template(template_name)
DIST_DIR.mkdir(parents=True, exist_ok=True)
# --- Copy assets (CSS + fonts) ---
dist_css_path = copy_stylesheet(css_path, DIST_DIR)
copy_fonts(FONTS_DIR, DIST_FONTS_DIR)
# --- Render and save HTML ---
html_out = template.render(data)
output_html.write_text(html_out, encoding="utf-8")
print(f"HTML '{output_html.name}' saved successfully at '{output_html.resolve()}'.")
# --- Generate and save PDF ---
font_config = FontConfiguration()
html = HTML(string=html_out, base_url=str(DIST_DIR))
stylesheets = [CSS(filename=str(dist_css_path), font_config=font_config)]
html.write_pdf(
str(output_pdf), stylesheets=stylesheets, font_config=font_config
)
print(f"PDF '{output_pdf.name}' saved successfully at '{output_pdf.resolve()}'.")
except FileNotFoundError as exc:
missing_file = exc.filename or data_path
print(
f"Error: Could not find required file '{missing_file}'. "
"Ensure the JSON, template, and CSS files are present."
)
except json.JSONDecodeError as exc:
print(f"Error: Failed to parse JSON data ({exc}).")
except OSError as exc:
print(f"OSError: {exc}")
except Exception as exc: # noqa: BLE001
print(f"An unexpected error occurred: {exc}")
if __name__ == "__main__":
render_almanac()