pdfgen-juanipis 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pdfgen/__init__.py +17 -0
- pdfgen/api.py +69 -0
- pdfgen/assets/banner-clean.png +0 -0
- pdfgen/assets/banner.png +0 -0
- pdfgen/assets/fonts/BCDEEE_Calibri_5.ttf +0 -0
- pdfgen/assets/fonts/BCDFEE_CenturyGothic-Bold_9.ttf +0 -0
- pdfgen/assets/fonts/BCDGEE_CenturyGothic-Bold_14.ttf +0 -0
- pdfgen/assets/fonts/BCDHEE_Calibri-Bold_20.ttf +0 -0
- pdfgen/assets/fonts/BCDIEE_Calibri-Bold_25.ttf +0 -0
- pdfgen/assets/fonts/BCDJEE_Calibri_27.ttf +0 -0
- pdfgen/assets/fonts/BCDKEE_Calibri-Italic_33.ttf +0 -0
- pdfgen/assets/fonts/BCDLEE_Calibri-Italic_52.ttf +0 -0
- pdfgen/assets/fonts/BCDMEE_SegoeUI_54.ttf +0 -0
- pdfgen/assets/fonts/BCDNEE_SegoeUI_60.ttf +0 -0
- pdfgen/assets/fonts/BCDOEE_Aptos Narrow,Bold_142.ttf +0 -0
- pdfgen/assets/fonts/BCDPEE_Aptos Narrow,Bold_144.ttf +0 -0
- pdfgen/assets/fonts/BCEAEE_Aptos Narrow_149.ttf +0 -0
- pdfgen/assets/fonts/BCEBEE_Aptos Narrow_154.ttf +0 -0
- pdfgen/assets/fonts/TimesNewRomanPS-BoldMT_38.ttf +0 -0
- pdfgen/assets/logo.png +0 -0
- pdfgen/cli.py +106 -0
- pdfgen/pagination.py +1045 -0
- pdfgen/render.py +348 -0
- pdfgen/schema.json +126 -0
- pdfgen/templates/boletin.css +389 -0
- pdfgen/templates/boletin_template.html.jinja +129 -0
- pdfgen/validator.py +247 -0
- pdfgen_juanipis-0.1.3.dist-info/METADATA +170 -0
- pdfgen_juanipis-0.1.3.dist-info/RECORD +33 -0
- pdfgen_juanipis-0.1.3.dist-info/WHEEL +5 -0
- pdfgen_juanipis-0.1.3.dist-info/entry_points.txt +2 -0
- pdfgen_juanipis-0.1.3.dist-info/licenses/LICENSE +21 -0
- pdfgen_juanipis-0.1.3.dist-info/top_level.txt +1 -0
pdfgen/render.py
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pathlib
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from jinja2 import Environment, FileSystemLoader
|
|
6
|
+
from weasyprint import HTML, CSS
|
|
7
|
+
|
|
8
|
+
if __name__ == "__main__" and __package__ is None:
|
|
9
|
+
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[2] / "src"))
|
|
10
|
+
|
|
11
|
+
from pdfgen.pagination import LayoutConfig, Paginator
|
|
12
|
+
from pdfgen.validator import validate_and_normalize
|
|
13
|
+
|
|
14
|
+
ROOT = pathlib.Path(__file__).resolve().parents[2]
|
|
15
|
+
PACKAGE_ROOT = pathlib.Path(__file__).resolve().parent
|
|
16
|
+
TEMPLATE_DIR = PACKAGE_ROOT / "templates"
|
|
17
|
+
TEMPLATE_NAME = "boletin_template.html.jinja"
|
|
18
|
+
CSS_PATH = TEMPLATE_DIR / "boletin.css"
|
|
19
|
+
OUTPUT_PDF = ROOT / "output.pdf"
|
|
20
|
+
FONTS_CONF = ROOT / "fonts.conf"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def build_sample_data():
|
|
24
|
+
banner = str((PACKAGE_ROOT / "assets" / "banner.png").resolve())
|
|
25
|
+
banner_clean = str((PACKAGE_ROOT / "assets" / "banner-clean.png").resolve())
|
|
26
|
+
logo = str((PACKAGE_ROOT / "assets" / "logo.png").resolve())
|
|
27
|
+
|
|
28
|
+
table_header = [
|
|
29
|
+
{
|
|
30
|
+
"title": "Consumo insuficiente de alimentos (Millones)",
|
|
31
|
+
"months": ["Enero", "Febrero", "Marzo"],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"title": "Estrategias de afrontamiento del hambre (Millones)",
|
|
35
|
+
"months": ["Enero", "Febrero", "Marzo"],
|
|
36
|
+
},
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
small_rows = [
|
|
40
|
+
{"dep": "Amazonas", "vals": ["0,05", "0,04", "0,04", "0,05", "0,04", "0,04"]},
|
|
41
|
+
{"dep": "Antioquia", "vals": ["1,48", "1,63", "1,76", "1,82", "1,90", "2,05"]},
|
|
42
|
+
{"dep": "Arauca", "vals": ["0,15", "0,14", "0,11", "0,16", "0,12", "0,11"]},
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
medium_rows = [
|
|
46
|
+
{"dep": "Atlantico", "vals": ["0,81", "0,69", "0,76", "0,63", "0,76", "0,76"]},
|
|
47
|
+
{"dep": "Bogota D. C.", "vals": ["1,88", "2,07", "2,23", "2,30", "2,41", "2,61"]},
|
|
48
|
+
{"dep": "Bolivar", "vals": ["0,61", "0,67", "0,72", "0,75", "0,78", "0,85"]},
|
|
49
|
+
{"dep": "Boyaca", "vals": ["0,28", "0,30", "0,33", "0,34", "0,35", "0,38"]},
|
|
50
|
+
{"dep": "Caldas", "vals": ["0,20", "0,22", "0,24", "0,24", "0,26", "0,28"]},
|
|
51
|
+
{"dep": "Caqueta", "vals": ["0,21", "0,20", "0,17", "0,24", "0,18", "0,17"]},
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
large_rows = [
|
|
55
|
+
{"dep": "Cundinamarca", "vals": ["0,76", "0,84", "0,90", "0,93", "0,98", "1,06"]},
|
|
56
|
+
{"dep": "Guainia", "vals": ["0,04", "0,03", "0,03", "0,04", "0,03", "0,03"]},
|
|
57
|
+
{"dep": "Guaviare", "vals": ["0,06", "0,06", "0,05", "0,07", "0,05", "0,05"]},
|
|
58
|
+
{"dep": "Huila", "vals": ["0,27", "0,30", "0,32", "0,33", "0,35", "0,37"]},
|
|
59
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
60
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
61
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
62
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
63
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
64
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
65
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
66
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
67
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
68
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
69
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
70
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
71
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
72
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
73
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
74
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
75
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
76
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
77
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
78
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
79
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
80
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
81
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
82
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
83
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
84
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
85
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
86
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
87
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
88
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
89
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
90
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
91
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
92
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
93
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
94
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
95
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
96
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
97
|
+
{"dep": "La Guajira", "vals": ["0,36", "0,30", "0,28", "0,38", "0,40", "0,43"]},
|
|
98
|
+
{"dep": "Meta", "vals": ["0,41", "0,38", "0,32", "0,46", "0,34", "0,32"]},
|
|
99
|
+
{"dep": "Santander", "vals": ["0,50", "0,56", "0,60", "0,62", "0,65", "0,70"]},
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
data = {
|
|
103
|
+
"title": "Reporte de Indicadores - Ejemplo Generico 2024",
|
|
104
|
+
"theme": {
|
|
105
|
+
"header_banner_path": banner,
|
|
106
|
+
"header_banner_path_cont": banner_clean,
|
|
107
|
+
"header_logo_path": logo,
|
|
108
|
+
"title_line1": "Reporte de Indicadores y Tendencias",
|
|
109
|
+
"title_line2": "Ejemplo Generico - 2024",
|
|
110
|
+
"footer_site": "example.org",
|
|
111
|
+
"footer_phone": "Contacto: +1 555 0100",
|
|
112
|
+
"show_header_titles": False,
|
|
113
|
+
},
|
|
114
|
+
"sections": [
|
|
115
|
+
{
|
|
116
|
+
"title": "I. Indice de Riesgo de la situacion alimentaria en Colombia",
|
|
117
|
+
"subtitle": "Historico de Mapas Departamentales de la Situacion Alimentaria en Colombia durante el Primer Trimestre del 2024.",
|
|
118
|
+
"content": [
|
|
119
|
+
{
|
|
120
|
+
"type": "text",
|
|
121
|
+
"text": [
|
|
122
|
+
"La Red de Bancos de Alimentos de Colombia ABACO presenta un analisis comparativo de indicadores clave sobre la situacion alimentaria y nutricional del pais en el I trimestre del ano 2024.",
|
|
123
|
+
"Este cambio resalta la necesidad de un seguimiento continuo y detallado de las tendencias en seguridad alimentaria a lo largo del ano.",
|
|
124
|
+
],
|
|
125
|
+
"refs": [
|
|
126
|
+
"1 World Food Programme (WFP). (2024). HungerMap LIVE: Colombia insight and key trends [PDF]. 31 January 2024. P. 2",
|
|
127
|
+
"2 World Food Programme (WFP). (2024). HungerMap LIVE: Colombia insight and key trends [PDF]. 19 February 2024. P. 2",
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"type": "table",
|
|
132
|
+
"table": {
|
|
133
|
+
"groups": table_header,
|
|
134
|
+
"rows": small_rows,
|
|
135
|
+
"total_width": 532.66,
|
|
136
|
+
"dep_width": 120.0,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"title": "II. Indicadores de prevalencia de Consumo Insuficiente de Alimentos.",
|
|
143
|
+
"content": [
|
|
144
|
+
{
|
|
145
|
+
"type": "table",
|
|
146
|
+
"table": {
|
|
147
|
+
"groups": table_header,
|
|
148
|
+
"rows": medium_rows,
|
|
149
|
+
"total_width": 532.66,
|
|
150
|
+
"dep_width": 120.0,
|
|
151
|
+
},
|
|
152
|
+
"refs": [
|
|
153
|
+
"4 World Food Programme (WFP). (2024). HungerMap LIVE: Colombia insight and key trends [PDF]. 31 January 2024. P. 2",
|
|
154
|
+
"5 World Food Programme (WFP). (2024). HungerMap LIVE: Colombia insight and key trends [PDF]. 19 February 2024. P. 2",
|
|
155
|
+
"6 World Food Programme (WFP). (2024). HungerMap LIVE: Colombia insight and key trends [PDF]. 31 March 2024. P. 2",
|
|
156
|
+
],
|
|
157
|
+
}
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"title": "III. Estrategias de Afrontamiento a las Crisis Basadas en la Alimentacion.",
|
|
162
|
+
"content": [
|
|
163
|
+
{
|
|
164
|
+
"type": "table",
|
|
165
|
+
"table": {
|
|
166
|
+
"groups": table_header,
|
|
167
|
+
"rows": large_rows,
|
|
168
|
+
"total_width": 532.66,
|
|
169
|
+
"dep_width": 120.0,
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
}
|
|
176
|
+
return data
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _paragraphs_from_text(text):
|
|
180
|
+
if isinstance(text, list):
|
|
181
|
+
return [t.strip() for t in text if t and t.strip()]
|
|
182
|
+
if not text:
|
|
183
|
+
return []
|
|
184
|
+
chunks = []
|
|
185
|
+
for block in str(text).split("\n\n"):
|
|
186
|
+
block = " ".join([line.strip() for line in block.splitlines()]).strip()
|
|
187
|
+
if block:
|
|
188
|
+
chunks.append(block)
|
|
189
|
+
return chunks
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _section_heading_html(title, subtitle):
|
|
193
|
+
html = f"<div class=\"section-title\">{title}</div>" if title else ""
|
|
194
|
+
if subtitle:
|
|
195
|
+
html += f"<div class=\"section-subtitle\">{subtitle}</div>"
|
|
196
|
+
return html
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _map_grid_html(items):
|
|
200
|
+
blocks = []
|
|
201
|
+
for item in items:
|
|
202
|
+
path = item.get("path")
|
|
203
|
+
label = item.get("label", "")
|
|
204
|
+
blocks.append(
|
|
205
|
+
"<div class=\"map-item\">"
|
|
206
|
+
f"<img class=\"map-img\" src=\"{path}\" alt=\"{label}\" />"
|
|
207
|
+
f"<div class=\"map-label\">{label}</div>"
|
|
208
|
+
"</div>"
|
|
209
|
+
)
|
|
210
|
+
return "<div class=\"map-grid\">" + "".join(blocks) + "</div>"
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _figure_html(path, caption, wide=False):
|
|
214
|
+
cls = "figure figure-wide" if wide else "figure"
|
|
215
|
+
html = f"<img class=\"{cls}\" src=\"{path}\" alt=\"{caption}\" />"
|
|
216
|
+
if caption:
|
|
217
|
+
html += f"<div class=\"figure-caption\">{caption}</div>"
|
|
218
|
+
return html
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _blocks_from_section(section):
|
|
222
|
+
blocks = []
|
|
223
|
+
title_html = _section_heading_html(section.get("title"), section.get("subtitle"))
|
|
224
|
+
if title_html:
|
|
225
|
+
blocks.append({"type": "html", "html": title_html, "keep_with_next": True})
|
|
226
|
+
|
|
227
|
+
for item in section.get("content", []):
|
|
228
|
+
itype = item.get("type", "text")
|
|
229
|
+
if itype == "text":
|
|
230
|
+
paragraphs = _paragraphs_from_text(item.get("text"))
|
|
231
|
+
if paragraphs:
|
|
232
|
+
html = "".join(f"<p>{p}</p>" for p in paragraphs)
|
|
233
|
+
block = {"type": "html", "html": html}
|
|
234
|
+
else:
|
|
235
|
+
continue
|
|
236
|
+
elif itype == "figure":
|
|
237
|
+
block = {
|
|
238
|
+
"type": "html",
|
|
239
|
+
"html": _figure_html(item.get("path"), item.get("caption", ""), item.get("wide", False)),
|
|
240
|
+
}
|
|
241
|
+
elif itype == "map_grid":
|
|
242
|
+
html = _map_grid_html(item.get("items", []))
|
|
243
|
+
if item.get("caption"):
|
|
244
|
+
html += f"<div class=\"figure-caption\">{item['caption']}</div>"
|
|
245
|
+
if item.get("source"):
|
|
246
|
+
html += f"<div class=\"figure-source\">{item['source']}</div>"
|
|
247
|
+
block = {"type": "html", "html": html}
|
|
248
|
+
elif itype == "table":
|
|
249
|
+
block = {"type": "table", "table": item.get("table", {})}
|
|
250
|
+
else:
|
|
251
|
+
block = {"type": "html", "html": item.get("html", "")}
|
|
252
|
+
|
|
253
|
+
if item.get("refs"):
|
|
254
|
+
block["refs"] = item.get("refs")
|
|
255
|
+
blocks.append(block)
|
|
256
|
+
|
|
257
|
+
if section.get("refs") and blocks:
|
|
258
|
+
blocks[0].setdefault("refs", [])
|
|
259
|
+
blocks[0]["refs"].extend(section["refs"])
|
|
260
|
+
|
|
261
|
+
return blocks
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _build_pages_from_sections(data):
|
|
265
|
+
theme = data.get("theme", {})
|
|
266
|
+
pages = []
|
|
267
|
+
footer_notes = []
|
|
268
|
+
refs_catalog = data.get("refs_catalog", {})
|
|
269
|
+
|
|
270
|
+
if data.get("cover"):
|
|
271
|
+
cover = dict(theme)
|
|
272
|
+
cover.update(data["cover"])
|
|
273
|
+
cover["cover"] = True
|
|
274
|
+
pages.append(cover)
|
|
275
|
+
|
|
276
|
+
blocks = []
|
|
277
|
+
for section in data.get("sections", []):
|
|
278
|
+
blocks.extend(_blocks_from_section(section))
|
|
279
|
+
if section.get("footer_notes"):
|
|
280
|
+
footer_notes.extend(section["footer_notes"])
|
|
281
|
+
|
|
282
|
+
pages.append({
|
|
283
|
+
**theme,
|
|
284
|
+
"intro": "",
|
|
285
|
+
"blocks": blocks,
|
|
286
|
+
"refs": [],
|
|
287
|
+
"refs_catalog": refs_catalog,
|
|
288
|
+
"footer_notes": footer_notes,
|
|
289
|
+
"page_number": "1",
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
data["pages"] = pages
|
|
293
|
+
return data
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def render_pdf(
|
|
297
|
+
data,
|
|
298
|
+
output_path=OUTPUT_PDF,
|
|
299
|
+
paginate=True,
|
|
300
|
+
validate=True,
|
|
301
|
+
template_dir=None,
|
|
302
|
+
css_path=None,
|
|
303
|
+
fonts_conf=None,
|
|
304
|
+
css_extra=None,
|
|
305
|
+
root_dir=None,
|
|
306
|
+
):
|
|
307
|
+
root_dir = pathlib.Path(root_dir) if root_dir else ROOT
|
|
308
|
+
template_dir = pathlib.Path(template_dir) if template_dir else TEMPLATE_DIR
|
|
309
|
+
css_path = pathlib.Path(css_path) if css_path else CSS_PATH
|
|
310
|
+
fonts_conf = pathlib.Path(fonts_conf) if fonts_conf else None
|
|
311
|
+
|
|
312
|
+
if fonts_conf and fonts_conf.exists():
|
|
313
|
+
os.environ.setdefault("FONTCONFIG_FILE", str(fonts_conf))
|
|
314
|
+
env = Environment(loader=FileSystemLoader(str(template_dir)))
|
|
315
|
+
template = env.get_template(TEMPLATE_NAME)
|
|
316
|
+
|
|
317
|
+
if "sections" in data and "pages" not in data:
|
|
318
|
+
data = _build_pages_from_sections(data)
|
|
319
|
+
|
|
320
|
+
if validate:
|
|
321
|
+
data, warnings = validate_and_normalize(data, root_dir=root_dir)
|
|
322
|
+
for warning in warnings:
|
|
323
|
+
print(f"[validate] {warning}")
|
|
324
|
+
|
|
325
|
+
layout = LayoutConfig()
|
|
326
|
+
paginator = Paginator(layout, str(css_path), str(root_dir), fonts_conf_path=str(fonts_conf))
|
|
327
|
+
if paginate:
|
|
328
|
+
data["pages"] = paginator.paginate(data["pages"])
|
|
329
|
+
data["layout"] = layout.to_template()
|
|
330
|
+
|
|
331
|
+
html = template.render(**data)
|
|
332
|
+
|
|
333
|
+
stylesheets = [CSS(filename=str(css_path))]
|
|
334
|
+
if css_extra:
|
|
335
|
+
stylesheets.append(CSS(string=str(css_extra)))
|
|
336
|
+
|
|
337
|
+
HTML(string=html, base_url=str(root_dir)).write_pdf(output_path, stylesheets=stylesheets)
|
|
338
|
+
|
|
339
|
+
print(f"Wrote {output_path}")
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def main():
|
|
343
|
+
data = build_sample_data()
|
|
344
|
+
render_pdf(data)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
if __name__ == "__main__":
|
|
348
|
+
main()
|
pdfgen/schema.json
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "PDFGenDocument",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": true,
|
|
6
|
+
"properties": {
|
|
7
|
+
"title": {"type": "string"},
|
|
8
|
+
"refs_catalog": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"additionalProperties": {"type": "string"}
|
|
11
|
+
},
|
|
12
|
+
"theme": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"header_banner_path": {"type": "string"},
|
|
16
|
+
"header_banner_path_cont": {"type": "string"},
|
|
17
|
+
"header_logo_path": {"type": "string"},
|
|
18
|
+
"title_line1": {"type": "string"},
|
|
19
|
+
"title_line2": {"type": "string"},
|
|
20
|
+
"footer_site": {"type": "string"},
|
|
21
|
+
"footer_phone": {"type": "string"},
|
|
22
|
+
"show_header_titles": {"type": "boolean"}
|
|
23
|
+
},
|
|
24
|
+
"additionalProperties": true
|
|
25
|
+
},
|
|
26
|
+
"sections": {
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": {"$ref": "#/definitions/section"}
|
|
29
|
+
},
|
|
30
|
+
"pages": {
|
|
31
|
+
"type": "array",
|
|
32
|
+
"items": {"$ref": "#/definitions/page"}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"anyOf": [
|
|
36
|
+
{"required": ["sections"]},
|
|
37
|
+
{"required": ["pages"]}
|
|
38
|
+
],
|
|
39
|
+
"definitions": {
|
|
40
|
+
"section": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"properties": {
|
|
43
|
+
"title": {"type": "string"},
|
|
44
|
+
"subtitle": {"type": "string"},
|
|
45
|
+
"content": {"type": "array", "items": {"$ref": "#/definitions/block"}},
|
|
46
|
+
"refs": {"type": "array", "items": {"type": "string"}},
|
|
47
|
+
"footer_notes": {"type": "array", "items": {"type": "string"}}
|
|
48
|
+
},
|
|
49
|
+
"required": ["content"],
|
|
50
|
+
"additionalProperties": true
|
|
51
|
+
},
|
|
52
|
+
"page": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {
|
|
55
|
+
"blocks": {"type": "array", "items": {"$ref": "#/definitions/block"}},
|
|
56
|
+
"refs": {"type": "array", "items": {"type": "string"}},
|
|
57
|
+
"footer_notes": {"type": "array", "items": {"type": "string"}}
|
|
58
|
+
},
|
|
59
|
+
"required": ["blocks"],
|
|
60
|
+
"additionalProperties": true
|
|
61
|
+
},
|
|
62
|
+
"block": {
|
|
63
|
+
"type": "object",
|
|
64
|
+
"properties": {
|
|
65
|
+
"type": {"type": "string", "enum": ["text", "table", "figure", "map_grid", "html"]},
|
|
66
|
+
"text": {"type": ["string", "array"], "items": {"type": "string"}},
|
|
67
|
+
"html": {"type": "string"},
|
|
68
|
+
"refs": {"type": "array", "items": {"type": "string"}},
|
|
69
|
+
"footer_notes": {"type": "array", "items": {"type": "string"}},
|
|
70
|
+
"table": {"$ref": "#/definitions/table"},
|
|
71
|
+
"path": {"type": "string"},
|
|
72
|
+
"caption": {"type": "string"},
|
|
73
|
+
"wide": {"type": "boolean"},
|
|
74
|
+
"items": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"items": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"properties": {
|
|
79
|
+
"path": {"type": "string"},
|
|
80
|
+
"label": {"type": "string"}
|
|
81
|
+
},
|
|
82
|
+
"required": ["path"],
|
|
83
|
+
"additionalProperties": true
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"source": {"type": "string"}
|
|
87
|
+
},
|
|
88
|
+
"required": ["type"],
|
|
89
|
+
"additionalProperties": true
|
|
90
|
+
},
|
|
91
|
+
"table": {
|
|
92
|
+
"type": "object",
|
|
93
|
+
"properties": {
|
|
94
|
+
"groups": {
|
|
95
|
+
"type": "array",
|
|
96
|
+
"items": {
|
|
97
|
+
"type": "object",
|
|
98
|
+
"properties": {
|
|
99
|
+
"title": {"type": "string"},
|
|
100
|
+
"months": {"type": "array", "items": {"type": "string"}}
|
|
101
|
+
},
|
|
102
|
+
"required": ["title", "months"],
|
|
103
|
+
"additionalProperties": true
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"rows": {
|
|
107
|
+
"type": "array",
|
|
108
|
+
"items": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"properties": {
|
|
111
|
+
"dep": {"type": "string"},
|
|
112
|
+
"vals": {"type": "array", "items": {"type": "string"}}
|
|
113
|
+
},
|
|
114
|
+
"required": ["dep", "vals"],
|
|
115
|
+
"additionalProperties": true
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"total_width": {"type": "number"},
|
|
119
|
+
"dep_width": {"type": "number"},
|
|
120
|
+
"show_header": {"type": "boolean"}
|
|
121
|
+
},
|
|
122
|
+
"required": ["groups", "rows"],
|
|
123
|
+
"additionalProperties": true
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|