Prezentprogramo 3.1__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.
Files changed (94) hide show
  1. hovercraft/__init__.py +435 -0
  2. hovercraft/generate.py +473 -0
  3. hovercraft/parse.py +275 -0
  4. hovercraft/position.py +269 -0
  5. hovercraft/template.py +237 -0
  6. hovercraft/templates/default/css/highlight.css +61 -0
  7. hovercraft/templates/default/css/hovercraft.css +67 -0
  8. hovercraft/templates/default/js/MathJax/es5/a11y/assistive-mml.js +1 -0
  9. hovercraft/templates/default/js/MathJax/es5/a11y/complexity.js +1 -0
  10. hovercraft/templates/default/js/MathJax/es5/a11y/explorer.js +1 -0
  11. hovercraft/templates/default/js/MathJax/es5/a11y/semantic-enrich.js +1 -0
  12. hovercraft/templates/default/js/MathJax/es5/a11y/sre.js +1 -0
  13. hovercraft/templates/default/js/MathJax/es5/adaptors/liteDOM.js +1 -0
  14. hovercraft/templates/default/js/MathJax/es5/core.js +1 -0
  15. hovercraft/templates/default/js/MathJax/es5/input/asciimath.js +1 -0
  16. hovercraft/templates/default/js/MathJax/es5/input/mml/entities.js +1 -0
  17. hovercraft/templates/default/js/MathJax/es5/input/mml/extensions/mml3.js +1 -0
  18. hovercraft/templates/default/js/MathJax/es5/input/mml.js +1 -0
  19. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/action.js +1 -0
  20. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/all-packages.js +34 -0
  21. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/ams.js +1 -0
  22. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/amscd.js +1 -0
  23. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/autoload.js +1 -0
  24. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/bbox.js +1 -0
  25. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/boldsymbol.js +1 -0
  26. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/braket.js +1 -0
  27. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/bussproofs.js +1 -0
  28. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/cancel.js +1 -0
  29. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/cases.js +1 -0
  30. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/centernot.js +1 -0
  31. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/color.js +1 -0
  32. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/colortbl.js +1 -0
  33. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/colorv2.js +1 -0
  34. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/configmacros.js +1 -0
  35. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/empheq.js +1 -0
  36. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/enclose.js +1 -0
  37. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/extpfeil.js +1 -0
  38. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/gensymb.js +1 -0
  39. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/html.js +1 -0
  40. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/mathtools.js +1 -0
  41. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/mhchem.js +34 -0
  42. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/newcommand.js +1 -0
  43. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/noerrors.js +1 -0
  44. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/noundefined.js +1 -0
  45. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/physics.js +1 -0
  46. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/require.js +1 -0
  47. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/setoptions.js +1 -0
  48. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/tagformat.js +1 -0
  49. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/textcomp.js +1 -0
  50. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/textmacros.js +1 -0
  51. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/unicode.js +1 -0
  52. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/upgreek.js +1 -0
  53. hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/verb.js +1 -0
  54. hovercraft/templates/default/js/MathJax/es5/input/tex-base.js +1 -0
  55. hovercraft/templates/default/js/MathJax/es5/input/tex-full.js +34 -0
  56. hovercraft/templates/default/js/MathJax/es5/input/tex.js +1 -0
  57. hovercraft/templates/default/js/MathJax/es5/latest.js +1 -0
  58. hovercraft/templates/default/js/MathJax/es5/loader.js +1 -0
  59. hovercraft/templates/default/js/MathJax/es5/mml-chtml.js +1 -0
  60. hovercraft/templates/default/js/MathJax/es5/mml-svg.js +1 -0
  61. hovercraft/templates/default/js/MathJax/es5/node-main.js +1 -0
  62. hovercraft/templates/default/js/MathJax/es5/output/chtml/fonts/tex.js +1 -0
  63. hovercraft/templates/default/js/MathJax/es5/output/chtml.js +1 -0
  64. hovercraft/templates/default/js/MathJax/es5/output/svg/fonts/tex.js +1 -0
  65. hovercraft/templates/default/js/MathJax/es5/output/svg.js +1 -0
  66. hovercraft/templates/default/js/MathJax/es5/startup.js +1 -0
  67. hovercraft/templates/default/js/MathJax/es5/tex-chtml-full-speech.js +34 -0
  68. hovercraft/templates/default/js/MathJax/es5/tex-chtml-full.js +34 -0
  69. hovercraft/templates/default/js/MathJax/es5/tex-chtml.js +1 -0
  70. hovercraft/templates/default/js/MathJax/es5/tex-mml-chtml.js +1 -0
  71. hovercraft/templates/default/js/MathJax/es5/tex-mml-svg.js +1 -0
  72. hovercraft/templates/default/js/MathJax/es5/tex-svg-full.js +34 -0
  73. hovercraft/templates/default/js/MathJax/es5/tex-svg.js +1 -0
  74. hovercraft/templates/default/js/MathJax/es5/ui/lazy.js +1 -0
  75. hovercraft/templates/default/js/MathJax/es5/ui/menu.js +1 -0
  76. hovercraft/templates/default/js/MathJax/es5/ui/safe.js +1 -0
  77. hovercraft/templates/default/js/gotoSlide.js +51 -0
  78. hovercraft/templates/default/js/hovercraft.js +58 -0
  79. hovercraft/templates/default/js/impress.js +5009 -0
  80. hovercraft/templates/default/template.cfg +11 -0
  81. hovercraft/templates/default/template.xsl +161 -0
  82. hovercraft/templates/reST.xsl +535 -0
  83. hovercraft/templates/simple/css/highlight.css +61 -0
  84. hovercraft/templates/simple/css/hovercraft.css +67 -0
  85. hovercraft/templates/simple/js/hovercraft.js +58 -0
  86. hovercraft/templates/simple/js/impress.js +5009 -0
  87. hovercraft/templates/simple/template.cfg +10 -0
  88. hovercraft/templates/simple/template.xsl +162 -0
  89. prezentprogramo-3.1.dist-info/METADATA +143 -0
  90. prezentprogramo-3.1.dist-info/RECORD +94 -0
  91. prezentprogramo-3.1.dist-info/WHEEL +5 -0
  92. prezentprogramo-3.1.dist-info/entry_points.txt +5 -0
  93. prezentprogramo-3.1.dist-info/licenses/LICENSE.txt +254 -0
  94. prezentprogramo-3.1.dist-info/top_level.txt +1 -0
hovercraft/__init__.py ADDED
@@ -0,0 +1,435 @@
1
+ import argparse
2
+ import gettext
3
+ import os
4
+ import threading
5
+ import time
6
+
7
+ # import pkg_resources
8
+ from importlib.metadata import version # , PackageNotFoundError
9
+ from packaging.requirements import Requirement
10
+ from packaging.version import parse as parse_version
11
+
12
+ from collections import defaultdict
13
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
14
+
15
+ # from tempfile import TemporaryDirectory
16
+ from watchdog.observers import Observer
17
+ from watchdog.events import FileSystemEventHandler
18
+ from docutils import nodes
19
+ from docutils.parsers.rst import Directive, directives
20
+ from graphviz import Source
21
+
22
+ from .generate import generate, generate_pdf
23
+
24
+ import hashlib
25
+
26
+ # __version__ = "3.0.1" #pkg_resources.require("prezentprogramo")[0].version
27
+ __version__ = parse_version(version(Requirement("prezentprogramo").name))
28
+
29
+
30
+ class YoGraphvizDirective(Directive):
31
+ has_content = True
32
+ required_arguments = 0
33
+ optional_arguments = 0
34
+ option_spec = {
35
+ "alt": str,
36
+ "height": int,
37
+ "width": int,
38
+ "scale": float,
39
+ "align": str,
40
+ "class": str,
41
+ }
42
+
43
+ count = 0
44
+
45
+ def run(self):
46
+ graphviz_code = "\n".join(self.content)
47
+ options = self.options
48
+
49
+ try:
50
+ # Get the path of the input .rst file
51
+ rst_file_path = os.path.abspath(self.state.document.current_source)
52
+ rst_directory = os.path.dirname(rst_file_path)
53
+ rst_filename = os.path.splitext(os.path.basename(rst_file_path))[0]
54
+
55
+ # Create a 'img' directory inside the same directory as the input .rst file
56
+ graphs_directory = os.path.join(rst_directory, rst_filename, "img")
57
+ os.makedirs(graphs_directory, exist_ok=True)
58
+
59
+ # Generate the graph image using Graphviz and save it to the 'img' directory
60
+ dot = Source(graphviz_code)
61
+ dot.format = "png" # Output format
62
+
63
+ # Increment the count for each instance
64
+ self.__class__.count += 1
65
+
66
+ graph_filename = "yoo_graphviz_"
67
+ graph_filename += hashlib.md5(graphviz_code.encode("utf-8")).hexdigest()
68
+
69
+ graph_path = os.path.join(graphs_directory, graph_filename)
70
+
71
+ if not os.path.exists(graph_path):
72
+ dot.render(filename=graph_path, cleanup=True, format="png", quiet=True)
73
+ relative_image_path = os.path.relpath(graph_path, rst_directory)
74
+
75
+ if not relative_image_path.endswith(".png"):
76
+ relative_image_path = f"{relative_image_path}.png"
77
+ image_node = nodes.image(uri=relative_image_path, format="png")
78
+
79
+ # Apply options to the image node
80
+ if "alt" in options:
81
+ image_node["alt"] = options["alt"]
82
+ if "class" in options:
83
+ image_node["classes"] += options["class"].split()
84
+ if "width" in options:
85
+ image_node["width"] = options["width"]
86
+ if "height" in options:
87
+ image_node["height"] = options["height"]
88
+ if "scale" in options:
89
+ image_node["scale"] = options["scale"]
90
+ if "align" in options:
91
+ image_node["align"] = options["align"]
92
+ return [image_node]
93
+
94
+ except Exception as e:
95
+ error_node = nodes.error()
96
+ error_node += nodes.Text(f"Error generating Graphviz image: {str(e)}")
97
+ return [error_node]
98
+
99
+
100
+ directives.register_directive("yographviz", YoGraphvizDirective)
101
+
102
+ '''
103
+ # https://github.com/liuyug/python-docutils-graphviz/blob/master/docutils_graphviz.py
104
+ # https://graphviz.readthedocs.io/en/stable/manual.html
105
+ # https://graphviz.readthedocs.io/en/stable/api.html#graphviz.Digraph.pipe
106
+ # https://pypi.org/project/beautifulsoup4/
107
+ # https://www.crummy.com/software/BeautifulSoup/
108
+ # https://www.crummy.com/software/BeautifulSoup/bs4/doc/
109
+ # https://stackabuse.com/parsing-xml-with-beautifulsoup-in-python/
110
+ # https://www.tutorialspoint.com/beautiful_soup/beautiful_soup_souping_the_page.htm
111
+ # https://developer.mozilla.org/en-US/docs/Web/SVG
112
+ # https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Adding_vector_graphics_to_the_Web
113
+
114
+ # pip3 install beautifulsoup4
115
+
116
+ import graphviz
117
+ from bs4 import BeautifulSoup
118
+
119
+ gfc="""
120
+ digraph {
121
+ rankdir = "LR";
122
+ node [shape=circle];
123
+ END [shape=doublecircle, label="2"];
124
+ B [shape=plaintext];
125
+ 0 -> 1 [label="+"];
126
+ 1 -> END [label="E"];
127
+ 0 -> END [label="λ"];
128
+ }
129
+ """
130
+
131
+ dot = graphviz.Source(gfc)
132
+ fullDocTypeSvg = dot.pipe(encoding='utf-8', format='svg', quiet=True)
133
+ #soup = BeautifulSoup(fullDocTypeSvg, 'html.parser')
134
+ #soup = BeautifulSoup(fullDocTypeSvg, 'html5lib')
135
+ soup = BeautifulSoup(fullDocTypeSvg, 'xml')
136
+ soupJustSvg = soup.find('svg')
137
+
138
+ # https://developer.mozilla.org/en-US/docs/Web/SVG
139
+
140
+ if soupJustSvg:
141
+ if 'class' in options:
142
+ soupJustSvg.attrs['classes'] += options['class'].split()
143
+ if 'width' in options:
144
+ soupJustSvg.attrs['width'] = options['width']
145
+ if 'height' in options:
146
+ soupJustSvg.attrs['height'] = options['height']
147
+ if 'scale' in options:
148
+ soupJustSvg.attrs['scale'] = options['scale']
149
+ if 'align' in options:
150
+ soupJustSvg.attrs['align'] = options['align']
151
+ img = Str(soupJustSvg)
152
+ else:
153
+ img = 'It is not SVG'
154
+
155
+ return [nodes.raw('', img, format='html')]
156
+ '''
157
+
158
+
159
+ class HovercraftEventHandler(FileSystemEventHandler):
160
+ def __init__(self, filelist):
161
+ self.filelist = filelist
162
+ self.quit = False
163
+ super().__init__()
164
+
165
+ def on_modified(self, event):
166
+ self._update(event.src_path)
167
+
168
+ def on_created(self, event):
169
+ self._update(event.src_path)
170
+
171
+ def on_moved(self, event):
172
+ self._update(event.dest_path)
173
+
174
+ def _update(self, src_path):
175
+ if self.quit:
176
+ return
177
+ if src_path in self.filelist:
178
+ print("File %s modified, update presentation" % src_path)
179
+ self.quit = True
180
+
181
+
182
+ def generate_and_observe(args, event):
183
+ while event.isSet():
184
+ # Generate the presentation
185
+ monitor_list = generate(args)
186
+ print("Presentation generated.")
187
+
188
+ # Make a list of involved directories
189
+ directories = defaultdict(list)
190
+ for file in monitor_list:
191
+ directory, filename = os.path.split(file)
192
+ directories[directory].append(filename)
193
+
194
+ observer = Observer()
195
+ handler = HovercraftEventHandler(monitor_list)
196
+ for directory, files in directories.items():
197
+ observer.schedule(handler, directory, recursive=False)
198
+
199
+ observer.start()
200
+ while event.wait(1):
201
+ time.sleep(0.05)
202
+ if handler.quit:
203
+ break
204
+
205
+ observer.stop()
206
+ observer.join()
207
+
208
+
209
+ def yoo_run_browser(bind: str, port: int):
210
+ time.sleep(0.1)
211
+ import webbrowser
212
+
213
+ webbrowser.open_new("http://" + bind + ":" + str(port))
214
+
215
+
216
+ def main(args=None):
217
+ parser = create_arg_parser()
218
+ args = parser.parse_args(args=args)
219
+
220
+ if args.pdf_output_path: # Check if the -pdf switch is provided
221
+ generate_pdf(args)
222
+ else:
223
+ serve_presentation(args)
224
+
225
+
226
+ def create_arg_parser():
227
+ # That the argparse default strings are lowercase is ugly.
228
+
229
+ def my_gettext(s):
230
+ return s.capitalize()
231
+
232
+ gettext.gettext = my_gettext
233
+
234
+ parser = argparse.ArgumentParser(
235
+ description="Create impress.js presentations with reStructuredText",
236
+ add_help=False,
237
+ )
238
+ parser.add_argument(
239
+ "presentation",
240
+ metavar="<presentation>",
241
+ help="The path to the reStructuredText presentation file.",
242
+ )
243
+ parser.add_argument(
244
+ "targetdir",
245
+ metavar="<targetdir>",
246
+ nargs="?",
247
+ help=(
248
+ "The directory where the presentation is saved. Will be created "
249
+ "if it does not exist. If you do not specify a targetdir "
250
+ "prezentprogramo will instead start a webserver and serve the "
251
+ "presentation from that server."
252
+ ),
253
+ )
254
+ parser.add_argument("-h", "--help", action="help", help="Show this help.")
255
+ parser.add_argument(
256
+ "-t",
257
+ "--template",
258
+ help=(
259
+ "Specify a template. Must be a .cfg file, or a directory with a "
260
+ "template.cfg file. If not given it will use a default template."
261
+ ),
262
+ )
263
+ parser.add_argument(
264
+ "-c",
265
+ "--css",
266
+ help=(
267
+ "An additional css file for the presentation to use. "
268
+ "See also the ``:css:`` settings of the presentation."
269
+ ),
270
+ )
271
+ parser.add_argument(
272
+ "-j",
273
+ "--js",
274
+ help=(
275
+ "An additional javascript file for the presentation to use. Added as a js-body script."
276
+ "See also the ``:js-body:`` settings of the presentation."
277
+ ),
278
+ )
279
+ parser.add_argument(
280
+ "-a",
281
+ "--auto-console",
282
+ action="store_true",
283
+ help=(
284
+ "Open the presenter console automatically. This is useful when "
285
+ "you are rehearsing and making sure the presenter notes are "
286
+ "correct. You can also set this by having ``:auto-console: "
287
+ "true`` first in the presentation."
288
+ ),
289
+ )
290
+ parser.add_argument(
291
+ "-s",
292
+ "--skip-help",
293
+ action="store_true",
294
+ help=("Do not show the initial help popup."),
295
+ )
296
+ parser.add_argument(
297
+ "-n",
298
+ "--skip-notes",
299
+ action="store_true",
300
+ help=("Do not include presenter notes in the output."),
301
+ )
302
+ parser.add_argument(
303
+ "-p",
304
+ "--port",
305
+ default="0.0.0.0:8000",
306
+ help=(
307
+ "The address and port that the server uses. "
308
+ "Ex 8080 or 127.0.0.1:9000. Defaults to 0.0.0.0:8000."
309
+ ),
310
+ )
311
+ parser.add_argument(
312
+ "--mathjax",
313
+ default=os.environ.get(
314
+ "HOVERCRAFT_MATHJAX" "js/MathJax/es5/tex-mml-chtml.js",
315
+ # "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML",
316
+ ),
317
+ help=(
318
+ "The URL to the mathjax library."
319
+ " (It will only be used if you have rST ``math::`` in your document)"
320
+ ),
321
+ )
322
+ parser.add_argument(
323
+ "-N",
324
+ "--slide-numbers",
325
+ action="store_true",
326
+ help=("Show slide numbers during the presentation."),
327
+ )
328
+ parser.add_argument(
329
+ "-d",
330
+ "--default-movement",
331
+ help=(
332
+ "The default value of moving to the right in pixel during presentation execution."
333
+ ),
334
+ )
335
+ parser.add_argument(
336
+ "-v",
337
+ "--version",
338
+ action="version",
339
+ # help=('Display version and exit.'),
340
+ version="prezentprogramo %s" % __version__,
341
+ )
342
+ parser.add_argument(
343
+ "-pdf",
344
+ "--pdf",
345
+ help="Path to the output PDF file",
346
+ action="store",
347
+ dest="pdf_output_path",
348
+ ) # -pdf switch
349
+
350
+ return parser
351
+
352
+
353
+ def serve_presentation(args):
354
+
355
+ # Check whether the file or folder as input exists.
356
+ if not os.path.exists(os.path.abspath(args.presentation)):
357
+ print(f"File or folder '{args.presentation}' does not exists.")
358
+ exit(-1)
359
+
360
+ # XXX Bit of a hack, clean this up, I check for this twice, also in the template.
361
+ if args.template and args.template not in ("simple", "default"):
362
+ args.template = os.path.abspath(args.template)
363
+
364
+ if args.targetdir:
365
+ # Generate the presentation
366
+ generate(args)
367
+ else:
368
+ # Server mode. Start a server that serves a temporary directory.
369
+ args.presentation = os.path.abspath(args.presentation)
370
+ dir_name = os.path.dirname(args.presentation)
371
+ file_name = os.path.basename(args.presentation)
372
+ targetdir_name = os.path.splitext(file_name)[0]
373
+ targetdir = os.path.join(dir_name, targetdir_name)
374
+ args.targetdir = targetdir
375
+
376
+ # Create the directory if it doesn't exist
377
+ if not os.path.exists(targetdir):
378
+ os.makedirs(targetdir)
379
+
380
+ # Set up watchdog to regenerate presentation if saved.
381
+ event = threading.Event()
382
+ event.set()
383
+ thread = threading.Thread(target=generate_and_observe, args=(args, event))
384
+
385
+ try:
386
+ # Serve presentation
387
+ if ":" in args.port:
388
+ bind, port = args.port.split(":")
389
+ else:
390
+ bind, port = "0.0.0.0", args.port
391
+ port = int(port)
392
+
393
+ # First create the server. This checks that we can connect to
394
+ # the port we want to.
395
+ os.chdir(targetdir)
396
+ server = HTTPServer((bind, port), SimpleHTTPRequestHandler)
397
+ print("Serving HTTP on", bind, "port", port, "...")
398
+
399
+ th1 = threading.Thread(
400
+ target=yoo_run_browser,
401
+ args=(
402
+ bind,
403
+ port,
404
+ ),
405
+ )
406
+
407
+ try:
408
+ # Now generate the presentation
409
+ th1.start()
410
+
411
+ thread.start()
412
+
413
+ try:
414
+ # All is good, start the server
415
+ server.serve_forever()
416
+ except KeyboardInterrupt:
417
+ print("\nKeyboard interrupt received, exiting.")
418
+ finally:
419
+ # Server exited
420
+ server.server_close()
421
+
422
+ finally:
423
+ # Stop the generation thread
424
+ event.clear()
425
+ # Wait for it to end
426
+ thread.join()
427
+ th1.join()
428
+
429
+ except PermissionError:
430
+ print("Can't bind to port %s:%s: No permission" % (bind, port))
431
+ except OSError as e:
432
+ if e.errno == 98:
433
+ print("Can't bind to port %s:%s: port already in use" % (bind, port))
434
+ else:
435
+ raise