prezento 1.0__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.
prezento-1.0/PKG-INFO ADDED
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.4
2
+ Name: prezento
3
+ Version: 1.0
4
+ Summary: Another text based (rst) presentation tool
5
+ Author-email: Ahmad Yoosofan <yoosofan@gmx.com>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/yoosofan/prezento
8
+ Project-URL: Issues, https://github.com/yoosofan/prezento/issues
9
+ Project-URL: Source, https://github.com/yoosofan/prezento
10
+ Keywords: presentations,restructuredtext,prezento
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Topic :: Multimedia :: Graphics :: Presentation
15
+ Classifier: Topic :: Text Processing
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/x-rst
24
+ Requires-Dist: docutils>=0.21
25
+ Requires-Dist: pygments>=2.0
26
+ Requires-Dist: graphviz>=0.20
27
+
28
+ ======================================
29
+ prezento — Modern RST Slide Generator
30
+ ======================================
31
+
32
+ **prezento** is a powerful, offline-first slide generator that converts reStructuredText (RST) files into beautiful, interactive HTML presentations.
33
+
34
+ It is a complete rewrite of `prezentprogramo` (a former fork of Hovercraft) with modern architecture, improved substep handling, and a switch from impress.js to **b6plus**.
35
+
36
+ .. image:: https://img.shields.io/badge/License-GPLv3-blue.svg
37
+ :target: LICENSE
38
+
39
+ .. contents:: Table of Contents
40
+ :depth: 2
41
+
42
+ Features
43
+ ========
44
+
45
+ * Clean and semantic RST-based slide authoring
46
+ * Powerful **substep / incremental reveal** system with fine-grained control
47
+ * Multiple output formats:
48
+ * Standard HTML (for direct PDF printing)
49
+ * Substep-expanded HTML (step-by-step handouts)
50
+ * b6plus presentation mode (for projectors / live talks)
51
+ * Embedded Graphviz diagram support (`yographviz` directive)
52
+ * Full offline capability
53
+ * Custom CSS and JavaScript support
54
+ * High-quality print/PDF output
55
+
56
+ Why prezento?
57
+ =============
58
+
59
+ The original `prezentprogramo` was heavily tied to impress.js and had complex indentation requirements. After years of use, I decided to rewrite it from scratch with the following goals:
60
+
61
+ * Better substep semantics (especially for lists and nested content)
62
+ * Modern docutils usage (no deprecated APIs)
63
+ * Cleaner code architecture
64
+ * Switch to **b6plus** — a lightweight and actively maintained presentation library
65
+ * Easier maintenance and future extensibility
66
+
67
+ This version is **not compatible** with original Hovercraft or prezentprogramo RST files, but it offers a much better authoring experience.
68
+
69
+ Sample Slides
70
+ =============
71
+
72
+ You can see real-world examples of prezento in use here:
73
+
74
+ https://github.com/yoosofan/slide
75
+
76
+ Assets Requirement
77
+ ==================
78
+
79
+ The generated HTML slides require the following assets:
80
+
81
+ * ``assets/simple.css``
82
+ * ``assets/b6plus.js``
83
+
84
+ **Important**: After generating HTML files, you must have an ``assets/`` folder next to them containing these two files.
85
+
86
+ These assets are taken from the `b6plus <https://github.com/ryanhaviland/b6plus>`_ project. You can update them whenever a newer version is released.
87
+
88
+ Installation
89
+ ============
90
+
91
+ From source (recommended during early development):
92
+
93
+ .. code-block:: bash
94
+
95
+ git clone https://github.com/yoosofan/prezento.git
96
+ cd prezento
97
+ pip install -e .
98
+
99
+ Usage
100
+ =====
101
+
102
+ Basic usage:
103
+
104
+ .. code-block:: bash
105
+
106
+ prezento your_slides.rst
107
+
108
+ This will generate three output files in the same directory:
109
+
110
+ * ``your_slides.html`` — Standard version
111
+ * ``your_slides.substep.pdf.html`` — Step-by-step version (good for printing)
112
+ * ``your_slides.presentation.html`` — b6plus interactive version
113
+
114
+ Options:
115
+
116
+ .. code-block:: bash
117
+
118
+ prezento input.rst -o output.html
119
+ prezento input.rst --no-substep # Skip substep PDF version
120
+ prezento input.rst --no-presentation # Skip b6plus version
121
+
122
+ Project Structure (Development)
123
+ ===============================
124
+
125
+ .. code-block:: text
126
+
127
+ prezento/
128
+ ├── src/
129
+ │ └── prezento/
130
+ │ ├── __init__.py
131
+ │ └── main.py
132
+ ├── docs/
133
+ │ └── dev-history/
134
+ └── tests/
135
+
136
+ Contributing
137
+ ============
138
+
139
+ Contributions are welcome! This project is still in active development.
140
+
141
+ If you want to help, please:
142
+
143
+ * Open an issue for bugs or feature requests
144
+ * Submit pull requests for improvements
145
+ * Test with complex slide decks
146
+
147
+ License
148
+ =======
149
+
150
+ This project is licensed under the **GNU General Public License v3.0** (GPLv3).
151
+
152
+ See the `LICENSE` file for the full license text.
153
+
154
+ You are free to use, modify, and distribute this software under the terms of GPLv3.
155
+
156
+ Acknowledgments
157
+ ===============
158
+
159
+ * Inspired by `Hovercraft <https://github.com/regebro/hovercraft>`_ and the original `prezentprogramo`
160
+ * Uses `b6plus <https://www.w3.org/Talks/Tools/b6plus/>`_ for presentation mode. https://www.w3.org/Talks/Tools/b6plus/slides.zip
161
+ * Built on top of `docutils <https://docutils.sourceforge.io/>`_
162
+
163
+ Author
164
+ ======
165
+
166
+ **Ahmad Yoosofan**
167
+
168
+ - GitHub: https://github.com/yoosofan
169
+ - Slides: https://github.com/yoosofan/slide
@@ -0,0 +1,142 @@
1
+ ======================================
2
+ prezento — Modern RST Slide Generator
3
+ ======================================
4
+
5
+ **prezento** is a powerful, offline-first slide generator that converts reStructuredText (RST) files into beautiful, interactive HTML presentations.
6
+
7
+ It is a complete rewrite of `prezentprogramo` (a former fork of Hovercraft) with modern architecture, improved substep handling, and a switch from impress.js to **b6plus**.
8
+
9
+ .. image:: https://img.shields.io/badge/License-GPLv3-blue.svg
10
+ :target: LICENSE
11
+
12
+ .. contents:: Table of Contents
13
+ :depth: 2
14
+
15
+ Features
16
+ ========
17
+
18
+ * Clean and semantic RST-based slide authoring
19
+ * Powerful **substep / incremental reveal** system with fine-grained control
20
+ * Multiple output formats:
21
+ * Standard HTML (for direct PDF printing)
22
+ * Substep-expanded HTML (step-by-step handouts)
23
+ * b6plus presentation mode (for projectors / live talks)
24
+ * Embedded Graphviz diagram support (`yographviz` directive)
25
+ * Full offline capability
26
+ * Custom CSS and JavaScript support
27
+ * High-quality print/PDF output
28
+
29
+ Why prezento?
30
+ =============
31
+
32
+ The original `prezentprogramo` was heavily tied to impress.js and had complex indentation requirements. After years of use, I decided to rewrite it from scratch with the following goals:
33
+
34
+ * Better substep semantics (especially for lists and nested content)
35
+ * Modern docutils usage (no deprecated APIs)
36
+ * Cleaner code architecture
37
+ * Switch to **b6plus** — a lightweight and actively maintained presentation library
38
+ * Easier maintenance and future extensibility
39
+
40
+ This version is **not compatible** with original Hovercraft or prezentprogramo RST files, but it offers a much better authoring experience.
41
+
42
+ Sample Slides
43
+ =============
44
+
45
+ You can see real-world examples of prezento in use here:
46
+
47
+ https://github.com/yoosofan/slide
48
+
49
+ Assets Requirement
50
+ ==================
51
+
52
+ The generated HTML slides require the following assets:
53
+
54
+ * ``assets/simple.css``
55
+ * ``assets/b6plus.js``
56
+
57
+ **Important**: After generating HTML files, you must have an ``assets/`` folder next to them containing these two files.
58
+
59
+ These assets are taken from the `b6plus <https://github.com/ryanhaviland/b6plus>`_ project. You can update them whenever a newer version is released.
60
+
61
+ Installation
62
+ ============
63
+
64
+ From source (recommended during early development):
65
+
66
+ .. code-block:: bash
67
+
68
+ git clone https://github.com/yoosofan/prezento.git
69
+ cd prezento
70
+ pip install -e .
71
+
72
+ Usage
73
+ =====
74
+
75
+ Basic usage:
76
+
77
+ .. code-block:: bash
78
+
79
+ prezento your_slides.rst
80
+
81
+ This will generate three output files in the same directory:
82
+
83
+ * ``your_slides.html`` — Standard version
84
+ * ``your_slides.substep.pdf.html`` — Step-by-step version (good for printing)
85
+ * ``your_slides.presentation.html`` — b6plus interactive version
86
+
87
+ Options:
88
+
89
+ .. code-block:: bash
90
+
91
+ prezento input.rst -o output.html
92
+ prezento input.rst --no-substep # Skip substep PDF version
93
+ prezento input.rst --no-presentation # Skip b6plus version
94
+
95
+ Project Structure (Development)
96
+ ===============================
97
+
98
+ .. code-block:: text
99
+
100
+ prezento/
101
+ ├── src/
102
+ │ └── prezento/
103
+ │ ├── __init__.py
104
+ │ └── main.py
105
+ ├── docs/
106
+ │ └── dev-history/
107
+ └── tests/
108
+
109
+ Contributing
110
+ ============
111
+
112
+ Contributions are welcome! This project is still in active development.
113
+
114
+ If you want to help, please:
115
+
116
+ * Open an issue for bugs or feature requests
117
+ * Submit pull requests for improvements
118
+ * Test with complex slide decks
119
+
120
+ License
121
+ =======
122
+
123
+ This project is licensed under the **GNU General Public License v3.0** (GPLv3).
124
+
125
+ See the `LICENSE` file for the full license text.
126
+
127
+ You are free to use, modify, and distribute this software under the terms of GPLv3.
128
+
129
+ Acknowledgments
130
+ ===============
131
+
132
+ * Inspired by `Hovercraft <https://github.com/regebro/hovercraft>`_ and the original `prezentprogramo`
133
+ * Uses `b6plus <https://www.w3.org/Talks/Tools/b6plus/>`_ for presentation mode. https://www.w3.org/Talks/Tools/b6plus/slides.zip
134
+ * Built on top of `docutils <https://docutils.sourceforge.io/>`_
135
+
136
+ Author
137
+ ======
138
+
139
+ **Ahmad Yoosofan**
140
+
141
+ - GitHub: https://github.com/yoosofan
142
+ - Slides: https://github.com/yoosofan/slide
@@ -0,0 +1,49 @@
1
+ [project]
2
+ name = "prezento"
3
+ version = "1.0"
4
+ authors = [
5
+ { name="Ahmad Yoosofan", email="yoosofan@gmx.com" },
6
+ ]
7
+ description = "Another text based (rst) presentation tool"
8
+
9
+ readme = "README.rst"
10
+ license = "GPL-3.0-or-later"
11
+ license-files = ["LICENSE.txt"]
12
+
13
+ requires-python = ">=3.10"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "Operating System :: OS Independent",
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Topic :: Multimedia :: Graphics :: Presentation",
19
+ "Topic :: Text Processing",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ ]
27
+
28
+ keywords = ["presentations", "restructuredtext", "prezento", ]
29
+
30
+ dependencies = [
31
+ "docutils >= 0.21",
32
+ "pygments >= 2.0",
33
+ "graphviz >= 0.20",
34
+ ]
35
+ [project.urls]
36
+ Homepage = "https://github.com/yoosofan/prezento"
37
+ Issues = "https://github.com/yoosofan/prezento/issues"
38
+ Source = "https://github.com/yoosofan/prezento"
39
+
40
+ [project.scripts]
41
+ prezento = "prezento.main:main"
42
+
43
+ [build-system]
44
+ requires = ["setuptools >= 61.0"]
45
+ build-backend = "setuptools.build_meta"
46
+
47
+ [tool.setuptools.packages.find]
48
+ where = ["src"]
49
+ include = ["prezento*"]
prezento-1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ entry_points = {'console_scripts': ['prezento=prezento:main', ]}
@@ -0,0 +1,479 @@
1
+ # prezento – Modern RST → HTML slide generator
2
+ # Uses b6plus instead of impress.js
3
+ # Outputs: .html, .substep.pdf.html, .presentation.html
4
+
5
+ # [ADDED] Imported 're' (regular expressions) to help manipulate the SVG strings natively.
6
+ import os
7
+ import argparse
8
+ import textwrap
9
+ import copy
10
+ import re
11
+ import graphviz
12
+ from docutils import nodes
13
+ from docutils.parsers.rst import Directive, directives
14
+ from docutils.core import publish_doctree, publish_from_doctree
15
+ from docutils.writers.html5_polyglot import Writer as HTML5WriterBase, HTMLTranslator
16
+
17
+ # ── Custom nodes ─────────────────────────────────────────────────────────────
18
+ class slido_block(nodes.container):
19
+ pass
20
+ class graphviz_block(nodes.General, nodes.Element):
21
+ pass
22
+
23
+ # ── Directives ───────────────────────────────────────────────────────────────
24
+ class PrezentoDirective(Directive):
25
+ has_content = True
26
+ optional_arguments = 10
27
+ final_argument_whitespace = True
28
+ option_spec = {
29
+ 'css': directives.unchanged,
30
+ 'js': directives.unchanged,
31
+ 'width': directives.unchanged,
32
+ 'height': directives.unchanged,
33
+ }
34
+
35
+ def run(self):
36
+ config = self.options.copy()
37
+ if self.arguments:
38
+ config['title'] = ' '.join(self.arguments)
39
+ self.state.document.presentation_config = config
40
+ return []
41
+
42
+
43
+ class SlidoDirective(Directive):
44
+ optional_arguments = 10
45
+ final_argument_whitespace = True
46
+ has_content = True
47
+ option_spec = {
48
+ 'class': directives.class_option,
49
+ 'id': directives.unchanged,
50
+ }
51
+
52
+ def run(self):
53
+ node = slido_block()
54
+ if self.arguments:
55
+ node['title'] = ' '.join(self.arguments)
56
+ if 'class' in self.options:
57
+ node['classes'] = self.options['class']
58
+ if 'id' in self.options:
59
+ node['ids'] = [self.options['id']]
60
+
61
+ # Dedent content
62
+ text = '\n'.join(self.content)
63
+ dedented = textwrap.dedent(text)
64
+ content = self.content.__class__(
65
+ dedented.splitlines(), source=self.state.document['source']
66
+ )
67
+ self.state.nested_parse(content, self.content_offset, node)
68
+ return [node]
69
+
70
+
71
+ class GraphvizDirective(Directive):
72
+ has_content = True
73
+ # [CHANGED] Expanded option_spec to include class, width, and height.
74
+ # This mirrors the built-in 'image' directive capabilities.
75
+ option_spec = {
76
+ 'align': directives.unchanged,
77
+ 'class': directives.class_option,
78
+ 'width': directives.unchanged,
79
+ 'height': directives.unchanged
80
+ }
81
+
82
+ def run(self):
83
+ node = graphviz_block()
84
+
85
+ # [ADDED] Store the parsed options into the node so the HTML translator can access them.
86
+ if 'class' in self.options:
87
+ node['classes'] = self.options['class']
88
+ if 'align' in self.options:
89
+ node['align'] = self.options['align']
90
+ if 'width' in self.options:
91
+ node['width'] = self.options['width']
92
+ if 'height' in self.options:
93
+ node['height'] = self.options['height']
94
+
95
+ dot_code = '\n'.join(self.content)
96
+ try:
97
+ svg = graphviz.Source(dot_code).pipe(format='svg').decode('utf-8')
98
+ if '<svg' in svg:
99
+ svg = svg[svg.find('<svg'):]
100
+
101
+ # [ADDED] Graphviz hardcodes width and height into the <svg> tag in points (pt),
102
+ # which overrides external CSS. If the user specifies a width or height in the
103
+ # RST directive, we strip the Graphviz defaults and inject the user's values directly.
104
+ if 'width' in self.options or 'height' in self.options:
105
+ # Remove the original width and height attributes using regex
106
+ svg = re.sub(r'(<svg[^>]*?)\s+width="[^"]+"', r'\1', svg, count=1)
107
+ svg = re.sub(r'(<svg[^>]*?)\s+height="[^"]+"', r'\1', svg, count=1)
108
+
109
+ # Build the new attribute string
110
+ new_attrs = ""
111
+ if 'width' in self.options:
112
+ new_attrs += f' width="{self.options["width"]}"'
113
+ if 'height' in self.options:
114
+ new_attrs += f' height="{self.options["height"]}"'
115
+
116
+ # Inject the new dimensions right after the opening '<svg'
117
+ svg = svg.replace('<svg', f'<svg{new_attrs}', 1)
118
+
119
+ node['svg'] = svg
120
+ except Exception:
121
+ node['svg'] = ''
122
+ return [node]
123
+
124
+
125
+ directives.register_directive('prezento', PrezentoDirective)
126
+ directives.register_directive('slido', SlidoDirective)
127
+ directives.register_directive('yographviz', GraphvizDirective)
128
+
129
+ # ── Substep Helpers ──────────────────────────────────────────────────────────
130
+
131
+ _SUBSTEP_CONTAINER_TYPES = (
132
+ slido_block,
133
+ nodes.container,
134
+ nodes.block_quote,
135
+ nodes.bullet_list,
136
+ nodes.enumerated_list,
137
+ nodes.definition_list,
138
+ )
139
+
140
+
141
+ def _is_substep_container(node):
142
+ return (
143
+ isinstance(node, _SUBSTEP_CONTAINER_TYPES)
144
+ and isinstance(node, nodes.Element)
145
+ and 'substep' in node.get('classes', [])
146
+ )
147
+
148
+
149
+ def _is_atomic_substep(node):
150
+ return (
151
+ isinstance(node, nodes.Element)
152
+ and 'substep' in node.get('classes', [])
153
+ and not _is_substep_container(node)
154
+ )
155
+
156
+ # ── Substep PDF Expansion ───────────────────────────────────────────────────
157
+ def _assign_reveal_indices(root):
158
+ counter = [0]
159
+
160
+ def walk(node):
161
+ if _is_substep_container(node):
162
+ for child in node.children:
163
+ if isinstance(child, nodes.Text):
164
+ continue
165
+ counter[0] += 1
166
+ child['_reveal_index'] = counter[0]
167
+ walk(child)
168
+ elif _is_atomic_substep(node):
169
+ counter[0] += 1
170
+ node['_reveal_index'] = counter[0]
171
+ else:
172
+ for child in node.children:
173
+ if not isinstance(child, nodes.Text):
174
+ walk(child)
175
+
176
+ walk(root)
177
+ return counter[0]
178
+
179
+
180
+ def _apply_step_visibility(root, step):
181
+ for node in root.findall(nodes.Element):
182
+ ri = node.get('_reveal_index', 0)
183
+ if ri == 0:
184
+ continue
185
+ classes = [c for c in node.get('classes', []) if c not in ('substep', 'substep-hidden')]
186
+ if ri > step:
187
+ classes.append('substep-hidden')
188
+ node['classes'] = classes
189
+
190
+
191
+ def _deep_clone(node):
192
+ orig_parent = node.parent
193
+ node.parent = None
194
+ try:
195
+ return copy.deepcopy(node)
196
+ finally:
197
+ node.parent = orig_parent
198
+
199
+
200
+ def _expand_slide(slide, slide_number):
201
+ template = _deep_clone(slide)
202
+ total = _assign_reveal_indices(template)
203
+ if total == 0:
204
+ slide['_slide_number'] = slide_number
205
+ return [slide]
206
+
207
+ sections = []
208
+ for step in range(1, total + 1):
209
+ sec = copy.deepcopy(template)
210
+ _apply_step_visibility(sec, step)
211
+ sec['_slide_number'] = slide_number
212
+ sections.append(sec)
213
+ return sections
214
+
215
+
216
+ def _expand_document_for_substep_pdf(document):
217
+ new_children = []
218
+ slide_num = 0
219
+ for node in list(document.children):
220
+ if isinstance(node, slido_block):
221
+ slide_num += 1
222
+ new_children.extend(_expand_slide(node, slide_num))
223
+ else:
224
+ new_children.append(node)
225
+ document.children = new_children
226
+ for child in document.children:
227
+ child.parent = document
228
+
229
+
230
+ # ── b6plus Transformation ───────────────────────────────────────────────────
231
+ def _b6_transform(document):
232
+ """Convert substep semantics to b6plus `incremental` / `next` classes."""
233
+
234
+ # Phase 1: Handle slido blocks with substep
235
+ for slide in document.findall(slido_block):
236
+ classes = slide.get('classes', [])
237
+ if 'substep' not in classes:
238
+ continue
239
+
240
+ slide['classes'] = [c for c in classes if c != 'substep']
241
+
242
+ for child in slide.children:
243
+ if not isinstance(child, nodes.Element):
244
+ continue
245
+ if isinstance(child, (nodes.title, nodes.colspec, nodes.thead)):
246
+ continue
247
+ child_classes = list(child.get('classes', []))
248
+ if 'next' not in child_classes:
249
+ child['classes'] = child_classes + ['next']
250
+
251
+ # Phase 2: Other substep containers and atomic elements
252
+ for node in document.findall(nodes.Element):
253
+ classes = node.get('classes', [])
254
+ if 'substep' not in classes:
255
+ continue
256
+
257
+ clean = [c for c in classes if c != 'substep']
258
+
259
+ if _is_substep_container(node):
260
+ if 'incremental' not in clean:
261
+ clean.append('incremental')
262
+ else:
263
+ if 'next' not in clean:
264
+ clean.append('next')
265
+
266
+ node['classes'] = clean
267
+
268
+
269
+ # ── CSS & Assets ─────────────────────────────────────────────────────────────
270
+ _CSS_FULLWIDTH = (
271
+ '<style>body,footer,header{'
272
+ 'max-width:none!important;width:100%;padding:1px 2%;margin:0 auto;'
273
+ '}</style>'
274
+ )
275
+
276
+ _CSS_SUBSTEP_HIDDEN = '<style>.substep-hidden{opacity:0;}</style>'
277
+
278
+ _CSS_B6PLUS = (
279
+ '<style>'
280
+ 'body.full .next:not(.active):not(.visited),'
281
+ 'body.full .incremental>*:not(.active):not(.visited),'
282
+ 'body.full .overlay>*:not(.active):not(.visited){visibility:hidden}'
283
+ 'body.full .slide-number{display:none}'
284
+ 'body.full section.slide{padding-bottom:1rem;break-after:auto;background-color:#ffffff;}'
285
+ '</style>'
286
+ )
287
+
288
+ _B6PLUS_JS_URL = 'assets/b6plus.js'
289
+ _SIMPLE_CSS_URL = 'assets/style.css'
290
+
291
+
292
+ # ── Translators ──────────────────────────────────────────────────────────────
293
+ class SlidoTranslator(HTMLTranslator):
294
+ def __init__(self, document, output_type='standard'):
295
+ super().__init__(document)
296
+ self.slide_count = 0
297
+ self.config = getattr(document, 'presentation_config', {})
298
+ self.output_type = output_type
299
+
300
+ def visit_document(self, node):
301
+ super().visit_document(node)
302
+ self.head.append(_CSS_FULLWIDTH)
303
+ if self.output_type == 'substep':
304
+ self.head.append(_CSS_SUBSTEP_HIDDEN)
305
+
306
+ cfg = self.config
307
+ if 'title' in cfg:
308
+ self.head.append(f'<title>{cfg["title"]}</title>')
309
+ if 'css' in cfg:
310
+ for css in cfg['css'].split(','):
311
+ self.head.append(f'<link rel="stylesheet" href="{css.strip()}" type="text/css" />')
312
+ if 'js' in cfg:
313
+ for js in cfg['js'].split(','):
314
+ self.head.append(f'<script src="{js.strip()}"></script>')
315
+
316
+ def depart_document(self, node):
317
+ super().depart_document(node)
318
+ self.body_prefix = [x.replace('<main>', '') for x in self.body_prefix]
319
+ self.body_suffix = [x.replace('</main>\n', '').replace('</main>', '') for x in self.body_suffix]
320
+
321
+ def visit_slido_block(self, node):
322
+ if '_slide_number' in node:
323
+ self.slide_count = node['_slide_number']
324
+ else:
325
+ self.slide_count += 1
326
+
327
+ extra = [c for c in node.get('classes', []) if c not in ('substep', 'substep-hidden')]
328
+ class_str = ' '.join(['slide'] + extra)
329
+ id_attr = f' id="{node["ids"][0]}"' if node.get('ids') else ''
330
+ self.body.append(f'<section class="{class_str}"{id_attr}>\n')
331
+ if node.get('title'):
332
+ self.body.append(f'<h2>{node["title"]}</h2>\n')
333
+
334
+ def depart_slido_block(self, node):
335
+ self.body.append(f'<div class="slide-number">{self.slide_count}</div></section>\n')
336
+
337
+ def visit_graphviz_block(self, node):
338
+ # [CHANGED] Calculate container styles dynamically based on the options passed.
339
+ align = node.get('align', 'center')
340
+
341
+ # [ADDED] Merge the default container class with any user-supplied classes
342
+ classes = ['graphviz-container'] + node.get('classes', [])
343
+
344
+ # [ADDED] Build inline styling to handle the alignment
345
+ styles = []
346
+ if align == 'center':
347
+ styles.append('margin: 0 auto;')
348
+ styles.append('text-align: center;')
349
+ elif align == 'left':
350
+ styles.append('margin-right: auto;')
351
+ styles.append('text-align: left;')
352
+ elif align == 'right':
353
+ styles.append('margin-left: auto;')
354
+ styles.append('text-align: right;')
355
+
356
+ class_str = ' '.join(classes)
357
+ style_str = ' '.join(styles)
358
+
359
+ # [CHANGED] Output the customized div with the dynamic styles and classes
360
+ self.body.append(f'<div class="{class_str}" style="{style_str}">\n{node.get("svg", "")}\n</div>\n')
361
+ raise nodes.SkipNode
362
+
363
+
364
+ class PresentationSlidoTranslator(SlidoTranslator):
365
+ def __init__(self, document):
366
+ HTMLTranslator.__init__(self, document)
367
+ self.slide_count = 0
368
+ self.config = getattr(document, 'presentation_config', {})
369
+ self._progress_emitted = False
370
+
371
+ def visit_document(self, node):
372
+ HTMLTranslator.visit_document(self, node)
373
+ cfg = self.config
374
+
375
+ if 'title' in cfg:
376
+ self.head.append(f'<title>{cfg["title"]}</title>')
377
+
378
+ # 1. b6plus framework CSS first
379
+ self.head.append(f'<link rel="stylesheet" href="{_SIMPLE_CSS_URL}" />')
380
+
381
+ # 2. User CSS (can override b6plus)
382
+ if 'css' in cfg:
383
+ for css in cfg['css'].split(','):
384
+ self.head.append(
385
+ f'<link rel="stylesheet" href="{css.strip()}" type="text/css" />'
386
+ )
387
+
388
+ self.head.append(_CSS_FULLWIDTH)
389
+ self.head.append(_CSS_B6PLUS)
390
+
391
+ # 3. b6plus script
392
+ self.head.append(f'<script src="{_B6PLUS_JS_URL}"></script>')
393
+
394
+ # 4. Additional user JS
395
+ if 'js' in cfg:
396
+ for js in cfg['js'].split(','):
397
+ self.head.append(f'<script src="{js.strip()}"></script>')
398
+
399
+ self.body.append(
400
+ '<script>\n'
401
+ 'document.addEventListener("DOMContentLoaded", function() {\n'
402
+ ' setTimeout(function() {\n'
403
+ ' if (typeof b6plus !== "undefined" && typeof b6plus.init === "function") {\n'
404
+ ' b6plus.init();\n'
405
+ ' } else if (typeof b6plus !== "undefined") {\n'
406
+ ' console.log("b6plus loaded - auto mode");\n'
407
+ ' }\n'
408
+ ' }, 10);\n'
409
+ '});\n'
410
+ '</script>\n'
411
+ )
412
+
413
+ # ── Writers ──────────────────────────────────────────────────────────────────
414
+ class SlidoWriter(HTML5WriterBase):
415
+ def __init__(self, output_type='standard'):
416
+ super().__init__()
417
+ self._output_type = output_type
418
+ self.translator_class = lambda doc: SlidoTranslator(doc, output_type=output_type)
419
+
420
+ def translate(self):
421
+ if self._output_type == 'substep':
422
+ _expand_document_for_substep_pdf(self.document)
423
+ super().translate()
424
+
425
+
426
+ class PresentationSlidoWriter(HTML5WriterBase):
427
+ def __init__(self):
428
+ super().__init__()
429
+ self.translator_class = PresentationSlidoTranslator
430
+
431
+ def translate(self):
432
+ _b6_transform(self.document)
433
+ super().translate()
434
+
435
+
436
+ # ── Public API ───────────────────────────────────────────────────────────────
437
+ def publish_to_html(source_rst: str, output_type: str = 'standard') -> bytes:
438
+ doctree = publish_doctree(source_rst)
439
+ if output_type == 'presentation':
440
+ writer = PresentationSlidoWriter()
441
+ else:
442
+ writer = SlidoWriter(output_type=output_type)
443
+ return publish_from_doctree(doctree, writer=writer)
444
+
445
+ def main():
446
+ parser = argparse.ArgumentParser(description='prezentprogramo v2')
447
+ parser.add_argument('input_file')
448
+ parser.add_argument('-o', '--output')
449
+ parser.add_argument('--no-substep', action='store_true')
450
+ parser.add_argument('--no-presentation', action='store_true')
451
+ args = parser.parse_args()
452
+
453
+ with open(args.input_file, 'r', encoding='utf-8') as f:
454
+ source = f.read()
455
+
456
+ base = os.path.splitext(args.input_file)[0]
457
+
458
+ # Standard
459
+ out = args.output or (base + '.html')
460
+ with open(out, 'w', encoding='utf-8') as f:
461
+ f.write(publish_to_html(source).decode('utf-8'))
462
+ print(f'Written: {out}')
463
+
464
+ if not args.no_substep:
465
+ sub = base + '.substep.pdf.html'
466
+ with open(sub, 'w', encoding='utf-8') as f:
467
+ f.write(publish_to_html(source, 'substep').decode('utf-8'))
468
+ print(f'Written: {sub}')
469
+
470
+ if not args.no_presentation:
471
+ pres = base + '.presentation.html'
472
+ with open(pres, 'w', encoding='utf-8') as f:
473
+ f.write(publish_to_html(source, 'presentation').decode('utf-8'))
474
+ print(f'Written: {pres}')
475
+
476
+
477
+ # ── CLI ──────────────────────────────────────────────────────────────────────
478
+ if __name__ == '__main__':
479
+ main()
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.4
2
+ Name: prezento
3
+ Version: 1.0
4
+ Summary: Another text based (rst) presentation tool
5
+ Author-email: Ahmad Yoosofan <yoosofan@gmx.com>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/yoosofan/prezento
8
+ Project-URL: Issues, https://github.com/yoosofan/prezento/issues
9
+ Project-URL: Source, https://github.com/yoosofan/prezento
10
+ Keywords: presentations,restructuredtext,prezento
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Topic :: Multimedia :: Graphics :: Presentation
15
+ Classifier: Topic :: Text Processing
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Requires-Python: >=3.10
23
+ Description-Content-Type: text/x-rst
24
+ Requires-Dist: docutils>=0.21
25
+ Requires-Dist: pygments>=2.0
26
+ Requires-Dist: graphviz>=0.20
27
+
28
+ ======================================
29
+ prezento — Modern RST Slide Generator
30
+ ======================================
31
+
32
+ **prezento** is a powerful, offline-first slide generator that converts reStructuredText (RST) files into beautiful, interactive HTML presentations.
33
+
34
+ It is a complete rewrite of `prezentprogramo` (a former fork of Hovercraft) with modern architecture, improved substep handling, and a switch from impress.js to **b6plus**.
35
+
36
+ .. image:: https://img.shields.io/badge/License-GPLv3-blue.svg
37
+ :target: LICENSE
38
+
39
+ .. contents:: Table of Contents
40
+ :depth: 2
41
+
42
+ Features
43
+ ========
44
+
45
+ * Clean and semantic RST-based slide authoring
46
+ * Powerful **substep / incremental reveal** system with fine-grained control
47
+ * Multiple output formats:
48
+ * Standard HTML (for direct PDF printing)
49
+ * Substep-expanded HTML (step-by-step handouts)
50
+ * b6plus presentation mode (for projectors / live talks)
51
+ * Embedded Graphviz diagram support (`yographviz` directive)
52
+ * Full offline capability
53
+ * Custom CSS and JavaScript support
54
+ * High-quality print/PDF output
55
+
56
+ Why prezento?
57
+ =============
58
+
59
+ The original `prezentprogramo` was heavily tied to impress.js and had complex indentation requirements. After years of use, I decided to rewrite it from scratch with the following goals:
60
+
61
+ * Better substep semantics (especially for lists and nested content)
62
+ * Modern docutils usage (no deprecated APIs)
63
+ * Cleaner code architecture
64
+ * Switch to **b6plus** — a lightweight and actively maintained presentation library
65
+ * Easier maintenance and future extensibility
66
+
67
+ This version is **not compatible** with original Hovercraft or prezentprogramo RST files, but it offers a much better authoring experience.
68
+
69
+ Sample Slides
70
+ =============
71
+
72
+ You can see real-world examples of prezento in use here:
73
+
74
+ https://github.com/yoosofan/slide
75
+
76
+ Assets Requirement
77
+ ==================
78
+
79
+ The generated HTML slides require the following assets:
80
+
81
+ * ``assets/simple.css``
82
+ * ``assets/b6plus.js``
83
+
84
+ **Important**: After generating HTML files, you must have an ``assets/`` folder next to them containing these two files.
85
+
86
+ These assets are taken from the `b6plus <https://github.com/ryanhaviland/b6plus>`_ project. You can update them whenever a newer version is released.
87
+
88
+ Installation
89
+ ============
90
+
91
+ From source (recommended during early development):
92
+
93
+ .. code-block:: bash
94
+
95
+ git clone https://github.com/yoosofan/prezento.git
96
+ cd prezento
97
+ pip install -e .
98
+
99
+ Usage
100
+ =====
101
+
102
+ Basic usage:
103
+
104
+ .. code-block:: bash
105
+
106
+ prezento your_slides.rst
107
+
108
+ This will generate three output files in the same directory:
109
+
110
+ * ``your_slides.html`` — Standard version
111
+ * ``your_slides.substep.pdf.html`` — Step-by-step version (good for printing)
112
+ * ``your_slides.presentation.html`` — b6plus interactive version
113
+
114
+ Options:
115
+
116
+ .. code-block:: bash
117
+
118
+ prezento input.rst -o output.html
119
+ prezento input.rst --no-substep # Skip substep PDF version
120
+ prezento input.rst --no-presentation # Skip b6plus version
121
+
122
+ Project Structure (Development)
123
+ ===============================
124
+
125
+ .. code-block:: text
126
+
127
+ prezento/
128
+ ├── src/
129
+ │ └── prezento/
130
+ │ ├── __init__.py
131
+ │ └── main.py
132
+ ├── docs/
133
+ │ └── dev-history/
134
+ └── tests/
135
+
136
+ Contributing
137
+ ============
138
+
139
+ Contributions are welcome! This project is still in active development.
140
+
141
+ If you want to help, please:
142
+
143
+ * Open an issue for bugs or feature requests
144
+ * Submit pull requests for improvements
145
+ * Test with complex slide decks
146
+
147
+ License
148
+ =======
149
+
150
+ This project is licensed under the **GNU General Public License v3.0** (GPLv3).
151
+
152
+ See the `LICENSE` file for the full license text.
153
+
154
+ You are free to use, modify, and distribute this software under the terms of GPLv3.
155
+
156
+ Acknowledgments
157
+ ===============
158
+
159
+ * Inspired by `Hovercraft <https://github.com/regebro/hovercraft>`_ and the original `prezentprogramo`
160
+ * Uses `b6plus <https://www.w3.org/Talks/Tools/b6plus/>`_ for presentation mode. https://www.w3.org/Talks/Tools/b6plus/slides.zip
161
+ * Built on top of `docutils <https://docutils.sourceforge.io/>`_
162
+
163
+ Author
164
+ ======
165
+
166
+ **Ahmad Yoosofan**
167
+
168
+ - GitHub: https://github.com/yoosofan
169
+ - Slides: https://github.com/yoosofan/slide
@@ -0,0 +1,10 @@
1
+ README.rst
2
+ pyproject.toml
3
+ src/prezento/__init__.py
4
+ src/prezento/main.py
5
+ src/prezento.egg-info/PKG-INFO
6
+ src/prezento.egg-info/SOURCES.txt
7
+ src/prezento.egg-info/dependency_links.txt
8
+ src/prezento.egg-info/entry_points.txt
9
+ src/prezento.egg-info/requires.txt
10
+ src/prezento.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ prezento = prezento.main:main
@@ -0,0 +1,3 @@
1
+ docutils>=0.21
2
+ pygments>=2.0
3
+ graphviz>=0.20
@@ -0,0 +1 @@
1
+ prezento