pdfgen-juanipis 0.1.3__tar.gz

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.
Files changed (47) hide show
  1. pdfgen_juanipis-0.1.3/LICENSE +21 -0
  2. pdfgen_juanipis-0.1.3/PKG-INFO +170 -0
  3. pdfgen_juanipis-0.1.3/README.md +149 -0
  4. pdfgen_juanipis-0.1.3/pyproject.toml +49 -0
  5. pdfgen_juanipis-0.1.3/setup.cfg +4 -0
  6. pdfgen_juanipis-0.1.3/src/pdfgen/__init__.py +17 -0
  7. pdfgen_juanipis-0.1.3/src/pdfgen/api.py +69 -0
  8. pdfgen_juanipis-0.1.3/src/pdfgen/assets/banner-clean.png +0 -0
  9. pdfgen_juanipis-0.1.3/src/pdfgen/assets/banner.png +0 -0
  10. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDEEE_Calibri_5.ttf +0 -0
  11. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDFEE_CenturyGothic-Bold_9.ttf +0 -0
  12. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDGEE_CenturyGothic-Bold_14.ttf +0 -0
  13. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDHEE_Calibri-Bold_20.ttf +0 -0
  14. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDIEE_Calibri-Bold_25.ttf +0 -0
  15. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDJEE_Calibri_27.ttf +0 -0
  16. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDKEE_Calibri-Italic_33.ttf +0 -0
  17. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDLEE_Calibri-Italic_52.ttf +0 -0
  18. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDMEE_SegoeUI_54.ttf +0 -0
  19. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDNEE_SegoeUI_60.ttf +0 -0
  20. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDOEE_Aptos Narrow,Bold_142.ttf +0 -0
  21. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCDPEE_Aptos Narrow,Bold_144.ttf +0 -0
  22. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCEAEE_Aptos Narrow_149.ttf +0 -0
  23. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/BCEBEE_Aptos Narrow_154.ttf +0 -0
  24. pdfgen_juanipis-0.1.3/src/pdfgen/assets/fonts/TimesNewRomanPS-BoldMT_38.ttf +0 -0
  25. pdfgen_juanipis-0.1.3/src/pdfgen/assets/logo.png +0 -0
  26. pdfgen_juanipis-0.1.3/src/pdfgen/cli.py +106 -0
  27. pdfgen_juanipis-0.1.3/src/pdfgen/pagination.py +1045 -0
  28. pdfgen_juanipis-0.1.3/src/pdfgen/render.py +348 -0
  29. pdfgen_juanipis-0.1.3/src/pdfgen/schema.json +126 -0
  30. pdfgen_juanipis-0.1.3/src/pdfgen/templates/boletin.css +389 -0
  31. pdfgen_juanipis-0.1.3/src/pdfgen/templates/boletin_template.html.jinja +129 -0
  32. pdfgen_juanipis-0.1.3/src/pdfgen/validator.py +247 -0
  33. pdfgen_juanipis-0.1.3/src/pdfgen_juanipis.egg-info/PKG-INFO +170 -0
  34. pdfgen_juanipis-0.1.3/src/pdfgen_juanipis.egg-info/SOURCES.txt +45 -0
  35. pdfgen_juanipis-0.1.3/src/pdfgen_juanipis.egg-info/dependency_links.txt +1 -0
  36. pdfgen_juanipis-0.1.3/src/pdfgen_juanipis.egg-info/entry_points.txt +2 -0
  37. pdfgen_juanipis-0.1.3/src/pdfgen_juanipis.egg-info/requires.txt +5 -0
  38. pdfgen_juanipis-0.1.3/src/pdfgen_juanipis.egg-info/top_level.txt +1 -0
  39. pdfgen_juanipis-0.1.3/tests/test_api.py +11 -0
  40. pdfgen_juanipis-0.1.3/tests/test_cli_fonts_conf.py +11 -0
  41. pdfgen_juanipis-0.1.3/tests/test_cli_yaml.py +21 -0
  42. pdfgen_juanipis-0.1.3/tests/test_html_rendering.py +27 -0
  43. pdfgen_juanipis-0.1.3/tests/test_pagination_smoke.py +49 -0
  44. pdfgen_juanipis-0.1.3/tests/test_refs_catalog.py +21 -0
  45. pdfgen_juanipis-0.1.3/tests/test_render_data.py +18 -0
  46. pdfgen_juanipis-0.1.3/tests/test_schema_validation.py +9 -0
  47. pdfgen_juanipis-0.1.3/tests/test_validator.py +49 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,170 @@
1
+ Metadata-Version: 2.4
2
+ Name: pdfgen-juanipis
3
+ Version: 0.1.3
4
+ Summary: HTML-to-PDF generator with pagination and validation
5
+ Author-email: Juan Pablo Díaz Correa <juanipis@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Juanipis/pdfgen-juanipis
8
+ Project-URL: Issues, https://github.com/Juanipis/pdfgen-juanipis/issues
9
+ Project-URL: Source, https://github.com/Juanipis/pdfgen-juanipis
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: Jinja2==3.1.4
16
+ Requires-Dist: WeasyPrint==59.0
17
+ Requires-Dist: pydyf==0.10.0
18
+ Requires-Dist: jsonschema==4.23.0
19
+ Requires-Dist: PyYAML==6.0.2
20
+ Dynamic: license-file
21
+
22
+ # pdfgen
23
+
24
+ Librería para generar PDFs a partir de data estructurada (HTML + WeasyPrint), con paginación, validación y soporte JSON/YAML.
25
+
26
+ ## Instalación
27
+
28
+ Desde el repo:
29
+
30
+ ```bash
31
+ pip install -e .
32
+ ```
33
+
34
+ ## Uso básico (Python)
35
+
36
+ ```python
37
+ from pdfgen.api import PDFGenConfig, PDFGen
38
+
39
+ config = PDFGenConfig.from_root("/ruta/a/tu/proyecto")
40
+ pdf = PDFGen(config)
41
+
42
+ pdf.render(data, output_path="salida.pdf", paginate=True, validate=True)
43
+ ```
44
+
45
+ Atajo:
46
+
47
+ ```python
48
+ from pdfgen.api import render_with_defaults
49
+
50
+ render_with_defaults(data, output_path="salida.pdf", root_dir="/ruta/a/tu/proyecto")
51
+ ```
52
+
53
+ ## CLI
54
+
55
+ Render (JSON o YAML):
56
+
57
+ ```bash
58
+ pdfgen render data.json salida.pdf --root /ruta/proyecto
59
+ pdfgen render data.yaml salida.pdf
60
+ ```
61
+
62
+ Validar (sin generar PDF):
63
+
64
+ ```bash
65
+ pdfgen validate data.yaml
66
+ ```
67
+
68
+ Desde stdin (YAML por defecto):
69
+
70
+ ```bash
71
+ cat data.yaml | pdfgen render - salida.pdf
72
+ ```
73
+
74
+ Opciones útiles:
75
+ - `--template-dir` usar tu template
76
+ - `--css` usar tu CSS
77
+ - `--fonts-conf` usar un `fonts.conf` propio
78
+ - `--fonts-dir` usar un directorio con `.ttf`
79
+ - `--css-extra` inyectar CSS adicional
80
+ - `--no-validate` desactivar validación
81
+ - `--no-paginate` desactivar paginación
82
+
83
+ ## Personalización
84
+
85
+ ### Fuentes propias
86
+
87
+ Opción A: `fonts.conf` propio.
88
+
89
+ ```python
90
+ config = PDFGenConfig.from_root("/ruta/a/tu/proyecto")
91
+ config.fonts_conf = "/ruta/a/mi/fonts.conf"
92
+ PDFGen(config).render(data, "salida.pdf")
93
+ ```
94
+
95
+ Opción B: directorio con `.ttf` desde CLI:
96
+
97
+ ```bash
98
+ pdfgen render data.json salida.pdf --fonts-dir /ruta/a/mis/fuentes
99
+ ```
100
+
101
+ ### Colores/estilos rápidos
102
+
103
+ Puedes inyectar CSS extra para cambiar variables o estilos:
104
+
105
+ ```python
106
+ css_extra = ":root { --blue-title: #0b7285; --orange-footer: #f08c00; }"
107
+ render_with_defaults(data, "salida.pdf", root_dir="/ruta", css_extra=css_extra)
108
+ ```
109
+
110
+ ### Template y CSS propios
111
+
112
+ ```bash
113
+ pdfgen render data.json salida.pdf --template-dir /ruta/template --css /ruta/template/boletin.css
114
+ ```
115
+
116
+ ## Estructura mínima del data
117
+
118
+ ```python
119
+ data = {
120
+ "theme": {
121
+ "header_banner_path": "banner.png",
122
+ "header_logo_path": "logo.png",
123
+ "title_line1": "Titulo",
124
+ "title_line2": "Subtitulo",
125
+ "footer_site": "example.org",
126
+ "footer_phone": "Contacto: +1 555 0100"
127
+ },
128
+ "sections": [
129
+ {
130
+ "title": "I. Introduccion",
131
+ "content": [
132
+ {"type": "text", "text": "Texto con <strong>negrita</strong>."}
133
+ ]
134
+ }
135
+ ]
136
+ }
137
+ ```
138
+
139
+ ## Estilos inline
140
+
141
+ Los bloques de texto aceptan HTML:
142
+ - `<strong>` negrita
143
+ - `<em>` itálica
144
+ - `<u>` subrayado
145
+ - `<s>` tachado
146
+
147
+ ## Ejemplos
148
+
149
+ - `examples/minimal.yaml`
150
+
151
+ ## Validación
152
+
153
+ Se valida automáticamente con `schema.json`. Los warnings se imprimen en consola.
154
+
155
+ Si quieres desactivar:
156
+
157
+ ```python
158
+ pdf.render(data, output_path="salida.pdf", validate=False)
159
+ ```
160
+
161
+ ## Licencia
162
+
163
+ MIT
164
+
165
+ ## Releases
166
+
167
+ Al hacer push a `main`, el workflow **Auto Release** crea tag y Release si la versión en `pyproject.toml` es nueva, y publica automáticamente en PyPI.
168
+
169
+
170
+ Hecho por Juanipis
@@ -0,0 +1,149 @@
1
+ # pdfgen
2
+
3
+ Librería para generar PDFs a partir de data estructurada (HTML + WeasyPrint), con paginación, validación y soporte JSON/YAML.
4
+
5
+ ## Instalación
6
+
7
+ Desde el repo:
8
+
9
+ ```bash
10
+ pip install -e .
11
+ ```
12
+
13
+ ## Uso básico (Python)
14
+
15
+ ```python
16
+ from pdfgen.api import PDFGenConfig, PDFGen
17
+
18
+ config = PDFGenConfig.from_root("/ruta/a/tu/proyecto")
19
+ pdf = PDFGen(config)
20
+
21
+ pdf.render(data, output_path="salida.pdf", paginate=True, validate=True)
22
+ ```
23
+
24
+ Atajo:
25
+
26
+ ```python
27
+ from pdfgen.api import render_with_defaults
28
+
29
+ render_with_defaults(data, output_path="salida.pdf", root_dir="/ruta/a/tu/proyecto")
30
+ ```
31
+
32
+ ## CLI
33
+
34
+ Render (JSON o YAML):
35
+
36
+ ```bash
37
+ pdfgen render data.json salida.pdf --root /ruta/proyecto
38
+ pdfgen render data.yaml salida.pdf
39
+ ```
40
+
41
+ Validar (sin generar PDF):
42
+
43
+ ```bash
44
+ pdfgen validate data.yaml
45
+ ```
46
+
47
+ Desde stdin (YAML por defecto):
48
+
49
+ ```bash
50
+ cat data.yaml | pdfgen render - salida.pdf
51
+ ```
52
+
53
+ Opciones útiles:
54
+ - `--template-dir` usar tu template
55
+ - `--css` usar tu CSS
56
+ - `--fonts-conf` usar un `fonts.conf` propio
57
+ - `--fonts-dir` usar un directorio con `.ttf`
58
+ - `--css-extra` inyectar CSS adicional
59
+ - `--no-validate` desactivar validación
60
+ - `--no-paginate` desactivar paginación
61
+
62
+ ## Personalización
63
+
64
+ ### Fuentes propias
65
+
66
+ Opción A: `fonts.conf` propio.
67
+
68
+ ```python
69
+ config = PDFGenConfig.from_root("/ruta/a/tu/proyecto")
70
+ config.fonts_conf = "/ruta/a/mi/fonts.conf"
71
+ PDFGen(config).render(data, "salida.pdf")
72
+ ```
73
+
74
+ Opción B: directorio con `.ttf` desde CLI:
75
+
76
+ ```bash
77
+ pdfgen render data.json salida.pdf --fonts-dir /ruta/a/mis/fuentes
78
+ ```
79
+
80
+ ### Colores/estilos rápidos
81
+
82
+ Puedes inyectar CSS extra para cambiar variables o estilos:
83
+
84
+ ```python
85
+ css_extra = ":root { --blue-title: #0b7285; --orange-footer: #f08c00; }"
86
+ render_with_defaults(data, "salida.pdf", root_dir="/ruta", css_extra=css_extra)
87
+ ```
88
+
89
+ ### Template y CSS propios
90
+
91
+ ```bash
92
+ pdfgen render data.json salida.pdf --template-dir /ruta/template --css /ruta/template/boletin.css
93
+ ```
94
+
95
+ ## Estructura mínima del data
96
+
97
+ ```python
98
+ data = {
99
+ "theme": {
100
+ "header_banner_path": "banner.png",
101
+ "header_logo_path": "logo.png",
102
+ "title_line1": "Titulo",
103
+ "title_line2": "Subtitulo",
104
+ "footer_site": "example.org",
105
+ "footer_phone": "Contacto: +1 555 0100"
106
+ },
107
+ "sections": [
108
+ {
109
+ "title": "I. Introduccion",
110
+ "content": [
111
+ {"type": "text", "text": "Texto con <strong>negrita</strong>."}
112
+ ]
113
+ }
114
+ ]
115
+ }
116
+ ```
117
+
118
+ ## Estilos inline
119
+
120
+ Los bloques de texto aceptan HTML:
121
+ - `<strong>` negrita
122
+ - `<em>` itálica
123
+ - `<u>` subrayado
124
+ - `<s>` tachado
125
+
126
+ ## Ejemplos
127
+
128
+ - `examples/minimal.yaml`
129
+
130
+ ## Validación
131
+
132
+ Se valida automáticamente con `schema.json`. Los warnings se imprimen en consola.
133
+
134
+ Si quieres desactivar:
135
+
136
+ ```python
137
+ pdf.render(data, output_path="salida.pdf", validate=False)
138
+ ```
139
+
140
+ ## Licencia
141
+
142
+ MIT
143
+
144
+ ## Releases
145
+
146
+ Al hacer push a `main`, el workflow **Auto Release** crea tag y Release si la versión en `pyproject.toml` es nueva, y publica automáticamente en PyPI.
147
+
148
+
149
+ Hecho por Juanipis
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pdfgen-juanipis"
7
+ version = "0.1.3"
8
+ description = "HTML-to-PDF generator with pagination and validation"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Juan Pablo Díaz Correa", email = "juanipis@gmail.com" }
14
+ ]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "Operating System :: OS Independent"
18
+ ]
19
+ license-files = ["LICENSE"]
20
+ dependencies = [
21
+ "Jinja2==3.1.4",
22
+ "WeasyPrint==59.0",
23
+ "pydyf==0.10.0",
24
+ "jsonschema==4.23.0",
25
+ "PyYAML==6.0.2"
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/Juanipis/pdfgen-juanipis"
30
+ Issues = "https://github.com/Juanipis/pdfgen-juanipis/issues"
31
+ Source = "https://github.com/Juanipis/pdfgen-juanipis"
32
+
33
+ [project.scripts]
34
+ pdfgen = "pdfgen.cli:main"
35
+
36
+ [tool.setuptools]
37
+ package-dir = {"" = "src"}
38
+
39
+ [tool.setuptools.packages.find]
40
+ where = ["src"]
41
+
42
+ [tool.setuptools.package-data]
43
+ pdfgen = [
44
+ "schema.json",
45
+ "templates/*.css",
46
+ "templates/*.jinja",
47
+ "assets/fonts/*",
48
+ "assets/*.png"
49
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,17 @@
1
+ """PDF generation package."""
2
+
3
+ from pdfgen.render import render_pdf, build_sample_data
4
+ from pdfgen.validator import validate_and_normalize
5
+ from pdfgen.pagination import LayoutConfig, Paginator
6
+ from pdfgen.api import PDFGen, PDFGenConfig, render_with_defaults
7
+
8
+ __all__ = [
9
+ "render_pdf",
10
+ "build_sample_data",
11
+ "validate_and_normalize",
12
+ "LayoutConfig",
13
+ "Paginator",
14
+ "PDFGen",
15
+ "PDFGenConfig",
16
+ "render_with_defaults",
17
+ ]
@@ -0,0 +1,69 @@
1
+ import pathlib
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, Optional
4
+
5
+ from pdfgen.render import render_pdf
6
+
7
+
8
+ @dataclass
9
+ class PDFGenConfig:
10
+ root_dir: pathlib.Path
11
+ template_dir: pathlib.Path
12
+ css_path: pathlib.Path
13
+ fonts_conf: Optional[pathlib.Path]
14
+
15
+ @classmethod
16
+ def from_root(cls, root_dir: pathlib.Path) -> "PDFGenConfig":
17
+ root_dir = pathlib.Path(root_dir)
18
+ package_root = pathlib.Path(__file__).resolve().parent
19
+ template_dir = root_dir / "template"
20
+ if not template_dir.exists():
21
+ template_dir = package_root / "templates"
22
+ css_path = template_dir / "boletin.css"
23
+ fonts_conf = root_dir / "fonts.conf"
24
+ if not fonts_conf.exists():
25
+ fonts_conf = None
26
+ return cls(
27
+ root_dir=root_dir,
28
+ template_dir=template_dir,
29
+ css_path=css_path,
30
+ fonts_conf=fonts_conf,
31
+ )
32
+
33
+
34
+ class PDFGen:
35
+ def __init__(self, config: PDFGenConfig):
36
+ self.config = config
37
+
38
+ def render(
39
+ self,
40
+ data: Dict[str, Any],
41
+ output_path: pathlib.Path,
42
+ paginate: bool = True,
43
+ validate: bool = True,
44
+ css_extra: Optional[str] = None,
45
+ ) -> None:
46
+ render_pdf(
47
+ data,
48
+ output_path=output_path,
49
+ paginate=paginate,
50
+ validate=validate,
51
+ css_extra=css_extra,
52
+ template_dir=self.config.template_dir,
53
+ css_path=self.config.css_path,
54
+ fonts_conf=self.config.fonts_conf,
55
+ root_dir=self.config.root_dir,
56
+ )
57
+
58
+
59
+ def render_with_defaults(
60
+ data: Dict[str, Any],
61
+ output_path: pathlib.Path,
62
+ root_dir: Optional[pathlib.Path] = None,
63
+ paginate: bool = True,
64
+ validate: bool = True,
65
+ css_extra: Optional[str] = None,
66
+ ) -> None:
67
+ root = pathlib.Path(root_dir) if root_dir else pathlib.Path.cwd()
68
+ config = PDFGenConfig.from_root(root)
69
+ PDFGen(config).render(data, output_path, paginate=paginate, validate=validate, css_extra=css_extra)
@@ -0,0 +1,106 @@
1
+ import argparse
2
+ import json
3
+ import pathlib
4
+ import sys
5
+ import tempfile
6
+
7
+ from pdfgen.api import PDFGen, PDFGenConfig
8
+
9
+
10
+ def _build_fonts_conf(fonts_dir: pathlib.Path) -> pathlib.Path:
11
+ fonts_dir = pathlib.Path(fonts_dir).resolve()
12
+ content = f"""<?xml version=\"1.0\"?>
13
+ <!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">
14
+ <fontconfig>
15
+ <dir>{fonts_dir}</dir>
16
+ <cache>
17
+ <dir>~/.cache/fontconfig</dir>
18
+ </cache>
19
+ </fontconfig>
20
+ """
21
+ tmp = tempfile.NamedTemporaryFile("w", delete=False, suffix=".conf")
22
+ tmp.write(content)
23
+ tmp.flush()
24
+ tmp.close()
25
+ return pathlib.Path(tmp.name)
26
+
27
+
28
+ def _load_data(path: pathlib.Path, fmt: str | None = None):
29
+ if str(path) == "-":
30
+ raw = sys.stdin.read()
31
+ inferred = fmt or "yaml"
32
+ else:
33
+ raw = path.read_text(encoding="utf-8")
34
+ inferred = fmt or path.suffix.lower().lstrip(".")
35
+
36
+ if inferred in ("yaml", "yml"):
37
+ try:
38
+ import yaml
39
+ except Exception as exc:
40
+ raise SystemExit("PyYAML is required for YAML input. Install with: pip install pyyaml") from exc
41
+ return yaml.safe_load(raw)
42
+
43
+ # default JSON
44
+ return json.loads(raw)
45
+
46
+
47
+ def main(argv=None) -> int:
48
+ parser = argparse.ArgumentParser(description="pdfgen CLI")
49
+ sub = parser.add_subparsers(dest="command", required=True)
50
+
51
+ render = sub.add_parser("render", help="Render a PDF from JSON/YAML")
52
+ render.add_argument("input", help="Path to JSON/YAML data (or - for stdin)")
53
+ render.add_argument("output", help="Output PDF path")
54
+ render.add_argument("--root", dest="root_dir", default=".", help="Project root dir")
55
+ render.add_argument("--template-dir", dest="template_dir", default=None)
56
+ render.add_argument("--css", dest="css_path", default=None)
57
+ render.add_argument("--fonts-conf", dest="fonts_conf", default=None)
58
+ render.add_argument("--fonts-dir", dest="fonts_dir", default=None)
59
+ render.add_argument("--css-extra", dest="css_extra", default=None, help="Extra CSS string")
60
+ render.add_argument("--format", dest="fmt", default=None, help="Input format: json|yaml")
61
+ render.add_argument("--no-validate", action="store_true")
62
+ render.add_argument("--no-paginate", action="store_true")
63
+
64
+ validate = sub.add_parser("validate", help="Validate JSON/YAML input against schema")
65
+ validate.add_argument("input", help="Path to JSON/YAML data (or - for stdin)")
66
+ validate.add_argument("--root", dest="root_dir", default=".", help="Project root dir")
67
+ validate.add_argument("--format", dest="fmt", default=None, help="Input format: json|yaml")
68
+
69
+ args = parser.parse_args(argv)
70
+
71
+ if args.command == "validate":
72
+ from pdfgen.validator import validate_and_normalize
73
+
74
+ root_dir = pathlib.Path(args.root_dir)
75
+ data = _load_data(pathlib.Path(args.input), fmt=args.fmt)
76
+ _, warnings = validate_and_normalize(data, root_dir=root_dir)
77
+ for warning in warnings:
78
+ print(f"[validate] {warning}")
79
+ return 0 if not warnings else 1
80
+
81
+ root_dir = pathlib.Path(args.root_dir)
82
+ config = PDFGenConfig.from_root(root_dir)
83
+
84
+ if args.template_dir:
85
+ config.template_dir = pathlib.Path(args.template_dir)
86
+ if args.css_path:
87
+ config.css_path = pathlib.Path(args.css_path)
88
+ if args.fonts_conf:
89
+ config.fonts_conf = pathlib.Path(args.fonts_conf)
90
+ if args.fonts_dir:
91
+ config.fonts_conf = _build_fonts_conf(pathlib.Path(args.fonts_dir))
92
+
93
+ data = _load_data(pathlib.Path(args.input), fmt=args.fmt)
94
+
95
+ PDFGen(config).render(
96
+ data,
97
+ output_path=pathlib.Path(args.output),
98
+ paginate=not args.no_paginate,
99
+ validate=not args.no_validate,
100
+ css_extra=args.css_extra,
101
+ )
102
+ return 0
103
+
104
+
105
+ if __name__ == "__main__":
106
+ raise SystemExit(main())