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.
- hovercraft/__init__.py +435 -0
- hovercraft/generate.py +473 -0
- hovercraft/parse.py +275 -0
- hovercraft/position.py +269 -0
- hovercraft/template.py +237 -0
- hovercraft/templates/default/css/highlight.css +61 -0
- hovercraft/templates/default/css/hovercraft.css +67 -0
- hovercraft/templates/default/js/MathJax/es5/a11y/assistive-mml.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/a11y/complexity.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/a11y/explorer.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/a11y/semantic-enrich.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/a11y/sre.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/adaptors/liteDOM.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/core.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/asciimath.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/mml/entities.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/mml/extensions/mml3.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/mml.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/action.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/all-packages.js +34 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/ams.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/amscd.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/autoload.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/bbox.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/boldsymbol.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/braket.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/bussproofs.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/cancel.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/cases.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/centernot.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/color.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/colortbl.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/colorv2.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/configmacros.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/empheq.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/enclose.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/extpfeil.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/gensymb.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/html.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/mathtools.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/mhchem.js +34 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/newcommand.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/noerrors.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/noundefined.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/physics.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/require.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/setoptions.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/tagformat.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/textcomp.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/textmacros.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/unicode.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/upgreek.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex/extensions/verb.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex-base.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex-full.js +34 -0
- hovercraft/templates/default/js/MathJax/es5/input/tex.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/latest.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/loader.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/mml-chtml.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/mml-svg.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/node-main.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/output/chtml/fonts/tex.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/output/chtml.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/output/svg/fonts/tex.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/output/svg.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/startup.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/tex-chtml-full-speech.js +34 -0
- hovercraft/templates/default/js/MathJax/es5/tex-chtml-full.js +34 -0
- hovercraft/templates/default/js/MathJax/es5/tex-chtml.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/tex-mml-chtml.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/tex-mml-svg.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/tex-svg-full.js +34 -0
- hovercraft/templates/default/js/MathJax/es5/tex-svg.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/ui/lazy.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/ui/menu.js +1 -0
- hovercraft/templates/default/js/MathJax/es5/ui/safe.js +1 -0
- hovercraft/templates/default/js/gotoSlide.js +51 -0
- hovercraft/templates/default/js/hovercraft.js +58 -0
- hovercraft/templates/default/js/impress.js +5009 -0
- hovercraft/templates/default/template.cfg +11 -0
- hovercraft/templates/default/template.xsl +161 -0
- hovercraft/templates/reST.xsl +535 -0
- hovercraft/templates/simple/css/highlight.css +61 -0
- hovercraft/templates/simple/css/hovercraft.css +67 -0
- hovercraft/templates/simple/js/hovercraft.js +58 -0
- hovercraft/templates/simple/js/impress.js +5009 -0
- hovercraft/templates/simple/template.cfg +10 -0
- hovercraft/templates/simple/template.xsl +162 -0
- prezentprogramo-3.1.dist-info/METADATA +143 -0
- prezentprogramo-3.1.dist-info/RECORD +94 -0
- prezentprogramo-3.1.dist-info/WHEEL +5 -0
- prezentprogramo-3.1.dist-info/entry_points.txt +5 -0
- prezentprogramo-3.1.dist-info/licenses/LICENSE.txt +254 -0
- prezentprogramo-3.1.dist-info/top_level.txt +1 -0
hovercraft/generate.py
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import shutil
|
|
4
|
+
from lxml import etree, html
|
|
5
|
+
|
|
6
|
+
# from pkg_resources import resource_string
|
|
7
|
+
import importlib.resources
|
|
8
|
+
from pyhtml2pdf import converter
|
|
9
|
+
from screeninfo import get_monitors
|
|
10
|
+
from .parse import rst2xml, SlideMaker
|
|
11
|
+
from .position import position_slides
|
|
12
|
+
from .template import (
|
|
13
|
+
Template,
|
|
14
|
+
CSS_RESOURCE,
|
|
15
|
+
JS_RESOURCE,
|
|
16
|
+
JS_POSITION_HEADER,
|
|
17
|
+
JS_POSITION_BODY,
|
|
18
|
+
OTHER_RESOURCE,
|
|
19
|
+
DIRECTORY_RESOURCE,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ResourceResolver(etree.Resolver):
|
|
24
|
+
def resolve(self, url, pubid, context):
|
|
25
|
+
if url.startswith("resource:"):
|
|
26
|
+
prefix, filename = url.split(":", 1)
|
|
27
|
+
# return self.resolve_strinog(resource_string(__name__, filename), context)
|
|
28
|
+
ref = importlib.resources.files(__name__).joinpath(filename)
|
|
29
|
+
return self.resolve_string(ref.read_bytes(), context)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def rst2html(
|
|
33
|
+
filepath,
|
|
34
|
+
template_info,
|
|
35
|
+
auto_console=False,
|
|
36
|
+
skip_help=False,
|
|
37
|
+
skip_notes=False,
|
|
38
|
+
mathjax=False,
|
|
39
|
+
slide_numbers=False,
|
|
40
|
+
default_movement_from_args=False,
|
|
41
|
+
):
|
|
42
|
+
# Read the infile
|
|
43
|
+
with open(filepath, "rb") as infile:
|
|
44
|
+
rststring = infile.read()
|
|
45
|
+
|
|
46
|
+
presentation_dir = os.path.split(filepath)[0]
|
|
47
|
+
|
|
48
|
+
# First convert reST to XML
|
|
49
|
+
xml, dependencies = rst2xml(rststring, filepath)
|
|
50
|
+
tree = etree.fromstring(xml)
|
|
51
|
+
|
|
52
|
+
# Fix up the resulting XML so it makes sense
|
|
53
|
+
sm = SlideMaker(tree, skip_notes=skip_notes)
|
|
54
|
+
tree = sm.walk()
|
|
55
|
+
|
|
56
|
+
data_width = None
|
|
57
|
+
# Pick up CSS information from the tree:
|
|
58
|
+
for attrib, value in tree.attrib.items():
|
|
59
|
+
|
|
60
|
+
if attrib.startswith("data-width"):
|
|
61
|
+
data_width = value
|
|
62
|
+
|
|
63
|
+
if attrib.startswith("css"):
|
|
64
|
+
|
|
65
|
+
if "-" in attrib:
|
|
66
|
+
dummy, media = attrib.split("-", 1)
|
|
67
|
+
else:
|
|
68
|
+
media = "all"
|
|
69
|
+
css_files = tree.attrib[attrib].split()
|
|
70
|
+
for css_file in css_files:
|
|
71
|
+
target = f'css/{css_file.split("/")[-1]}'
|
|
72
|
+
if media in ("console", "preview"):
|
|
73
|
+
# The "console" media is used to style the presenter
|
|
74
|
+
# console and does not need to be included in the header,
|
|
75
|
+
# but must be copied. So we add it as a non css file,
|
|
76
|
+
# even though it's a css-file.
|
|
77
|
+
template_info.add_resource(
|
|
78
|
+
os.path.abspath(os.path.join(presentation_dir, css_file)),
|
|
79
|
+
OTHER_RESOURCE,
|
|
80
|
+
target=target,
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
# Add as a css resource:
|
|
84
|
+
template_info.add_resource(
|
|
85
|
+
os.path.abspath(os.path.join(presentation_dir, css_file)),
|
|
86
|
+
CSS_RESOURCE,
|
|
87
|
+
target=target,
|
|
88
|
+
extra_info=media,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
elif attrib.startswith("js"):
|
|
92
|
+
if attrib == "js-header":
|
|
93
|
+
media = JS_POSITION_HEADER
|
|
94
|
+
else:
|
|
95
|
+
# Put javascript in body tag as default.
|
|
96
|
+
media = JS_POSITION_BODY
|
|
97
|
+
js_files = tree.attrib[attrib].split()
|
|
98
|
+
for js_file in js_files:
|
|
99
|
+
target = f'js/{js_file.split("/")[-1]}'
|
|
100
|
+
template_info.add_resource(
|
|
101
|
+
os.path.abspath(os.path.join(presentation_dir, js_file)),
|
|
102
|
+
JS_RESOURCE,
|
|
103
|
+
target=target,
|
|
104
|
+
extra_info=media,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if sm.need_mathjax and mathjax:
|
|
108
|
+
if mathjax.startswith("http"):
|
|
109
|
+
template_info.add_resource(
|
|
110
|
+
None, JS_RESOURCE, target=mathjax, extra_info=JS_POSITION_HEADER
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
# Local copy
|
|
114
|
+
template_info.add_resource(mathjax, DIRECTORY_RESOURCE, target="mathjax")
|
|
115
|
+
template_info.add_resource(
|
|
116
|
+
None,
|
|
117
|
+
JS_RESOURCE,
|
|
118
|
+
target="mathjax/MathJax.js?config=TeX-MML-AM_CHTML",
|
|
119
|
+
extra_info=JS_POSITION_HEADER,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Set step width
|
|
123
|
+
set_step_width(tree, data_width)
|
|
124
|
+
|
|
125
|
+
# Position all slides
|
|
126
|
+
position_slides(tree, default_movement_from_args, data_width)
|
|
127
|
+
|
|
128
|
+
# Add the template info to the tree:
|
|
129
|
+
tree.append(template_info.xml_node())
|
|
130
|
+
|
|
131
|
+
# If the console-should open automatically, set an attribute on the document:
|
|
132
|
+
if auto_console:
|
|
133
|
+
tree.attrib["auto-console"] = "True"
|
|
134
|
+
|
|
135
|
+
# If the console-should open automatically, set an attribute on the document:
|
|
136
|
+
if skip_help:
|
|
137
|
+
tree.attrib["skip-help"] = "True"
|
|
138
|
+
|
|
139
|
+
# If the slide numbers should be displayed, set an attribute on the document:
|
|
140
|
+
if slide_numbers:
|
|
141
|
+
tree.attrib["slide-numbers"] = "True"
|
|
142
|
+
|
|
143
|
+
# We need to set up a resolver for resources, so we can include the
|
|
144
|
+
# reST.xsl file if so desired.
|
|
145
|
+
parser = etree.XMLParser()
|
|
146
|
+
parser.resolvers.add(ResourceResolver())
|
|
147
|
+
|
|
148
|
+
# Transform the tree to HTML
|
|
149
|
+
xsl_tree = etree.fromstring(template_info.xsl, parser)
|
|
150
|
+
transformer = etree.XSLT(xsl_tree)
|
|
151
|
+
tree = transformer(tree)
|
|
152
|
+
result = html.tostring(tree)
|
|
153
|
+
|
|
154
|
+
return template_info.doctype + result, dependencies
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def set_step_width(tree, data_width):
|
|
158
|
+
step_divs = tree.findall("step")
|
|
159
|
+
width = None
|
|
160
|
+
if data_width:
|
|
161
|
+
width = data_width
|
|
162
|
+
else:
|
|
163
|
+
presentation_monitor = get_monitors()[
|
|
164
|
+
-1
|
|
165
|
+
] # Assuming the last monitor is for presentation
|
|
166
|
+
width = presentation_monitor.width
|
|
167
|
+
tree.attrib["data-width"] = str(width)
|
|
168
|
+
|
|
169
|
+
# Add width style to each found div
|
|
170
|
+
for div in step_divs:
|
|
171
|
+
div.set("style", f"width: {width}px;") # Adding width style
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def copy_resource(filename, sourcedir, targetdir, sourcepath=None, targetpath=None):
|
|
175
|
+
if filename[0] == "/" or ":" in filename:
|
|
176
|
+
# Absolute path or URI: Do nothing
|
|
177
|
+
return None # No monitoring needed
|
|
178
|
+
if sourcepath is None:
|
|
179
|
+
sourcepath = os.path.join(sourcedir, filename)
|
|
180
|
+
if targetpath is None:
|
|
181
|
+
targetpath = os.path.join(targetdir, filename)
|
|
182
|
+
|
|
183
|
+
if os.path.exists(targetpath) and os.path.getmtime(sourcepath) <= os.path.getmtime(
|
|
184
|
+
targetpath
|
|
185
|
+
):
|
|
186
|
+
# File has not changed since last copy, so skip.
|
|
187
|
+
return sourcepath # Monitor this file
|
|
188
|
+
|
|
189
|
+
targetdir = os.path.split(targetpath)[0]
|
|
190
|
+
if not os.path.exists(targetdir):
|
|
191
|
+
os.makedirs(targetdir)
|
|
192
|
+
|
|
193
|
+
shutil.copy2(sourcepath, targetpath)
|
|
194
|
+
return sourcepath # Monitor this file
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def generate(args):
|
|
198
|
+
"""Generates the presentation and returns a list of files used"""
|
|
199
|
+
|
|
200
|
+
source_files = {args.presentation}
|
|
201
|
+
|
|
202
|
+
# Parse the template info
|
|
203
|
+
template_info = Template(args.template)
|
|
204
|
+
if args.css:
|
|
205
|
+
presentation_dir = os.path.split(args.presentation)[0]
|
|
206
|
+
target_path = os.path.relpath(args.css, presentation_dir)
|
|
207
|
+
template_info.add_resource(
|
|
208
|
+
args.css, CSS_RESOURCE, target=target_path, extra_info="all"
|
|
209
|
+
)
|
|
210
|
+
source_files.add(args.css)
|
|
211
|
+
if args.js:
|
|
212
|
+
presentation_dir = os.path.split(args.presentation)[0]
|
|
213
|
+
target_path = os.path.relpath(args.js, presentation_dir)
|
|
214
|
+
template_info.add_resource(
|
|
215
|
+
args.js, JS_RESOURCE, target=target_path, extra_info=JS_POSITION_BODY
|
|
216
|
+
)
|
|
217
|
+
source_files.add(args.js)
|
|
218
|
+
|
|
219
|
+
# Make the resulting HTML
|
|
220
|
+
htmldata, dependencies = rst2html(
|
|
221
|
+
args.presentation,
|
|
222
|
+
template_info,
|
|
223
|
+
args.auto_console,
|
|
224
|
+
args.skip_help,
|
|
225
|
+
args.skip_notes,
|
|
226
|
+
args.mathjax,
|
|
227
|
+
args.slide_numbers,
|
|
228
|
+
args.default_movement,
|
|
229
|
+
)
|
|
230
|
+
source_files.update(dependencies)
|
|
231
|
+
|
|
232
|
+
# Create targetdir directory
|
|
233
|
+
if not os.path.exists(args.targetdir):
|
|
234
|
+
os.makedirs(args.targetdir)
|
|
235
|
+
|
|
236
|
+
# Copy supporting files
|
|
237
|
+
source_files.update(template_info.copy_resources(args.targetdir))
|
|
238
|
+
|
|
239
|
+
# Copy files from the source:
|
|
240
|
+
sourcedir = os.path.split(os.path.abspath(args.presentation))[0]
|
|
241
|
+
tree = html.fromstring(htmldata)
|
|
242
|
+
|
|
243
|
+
# Copy images to targetdir/img directory and update html tree
|
|
244
|
+
for image in tree.iterdescendants("img"):
|
|
245
|
+
img_src = image.attrib["src"]
|
|
246
|
+
filename = img_src.split("/")[-1]
|
|
247
|
+
targetpath = os.path.join(args.targetdir, f"img/{filename}")
|
|
248
|
+
source_files.add(
|
|
249
|
+
copy_resource(
|
|
250
|
+
img_src,
|
|
251
|
+
sourcedir,
|
|
252
|
+
args.targetdir,
|
|
253
|
+
sourcepath=None,
|
|
254
|
+
targetpath=targetpath,
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
image.attrib["src"] = f"img/{filename}"
|
|
258
|
+
|
|
259
|
+
for source in tree.iterdescendants("source"):
|
|
260
|
+
filename = source.attrib["src"]
|
|
261
|
+
source_files.add(copy_resource(filename, sourcedir, args.targetdir))
|
|
262
|
+
|
|
263
|
+
# Code for handling iframe sources
|
|
264
|
+
for iframe in tree.iterdescendants("iframe"):
|
|
265
|
+
iframe_src = iframe.attrib.get("src")
|
|
266
|
+
if iframe_src:
|
|
267
|
+
filename = iframe_src.split("/")[-1]
|
|
268
|
+
targetpath = os.path.join(args.targetdir, f"iframe/{filename}")
|
|
269
|
+
source_files.add(
|
|
270
|
+
copy_resource(
|
|
271
|
+
iframe_src,
|
|
272
|
+
sourcedir,
|
|
273
|
+
args.targetdir,
|
|
274
|
+
sourcepath=None,
|
|
275
|
+
targetpath=targetpath,
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
iframe.attrib["src"] = f"iframe/{filename}"
|
|
279
|
+
|
|
280
|
+
RE_CSS_URL = re.compile(rb"""url\(['"]?(.*?)['"]?[\)\?\#]""")
|
|
281
|
+
|
|
282
|
+
# Copy any files referenced by url() in the css-files:
|
|
283
|
+
for resource in template_info.resources:
|
|
284
|
+
if resource.resource_type != CSS_RESOURCE:
|
|
285
|
+
continue
|
|
286
|
+
# path in CSS is relative to CSS file; construct source/dest accordingly
|
|
287
|
+
css_base = template_info.template_root if resource.is_in_template else sourcedir
|
|
288
|
+
css_sourcedir = os.path.dirname(os.path.join(css_base, resource.filepath))
|
|
289
|
+
css_targetdir = os.path.dirname(
|
|
290
|
+
os.path.join(args.targetdir, resource.final_path())
|
|
291
|
+
)
|
|
292
|
+
uris = RE_CSS_URL.findall(template_info.read_data(resource))
|
|
293
|
+
uris = [uri.decode() for uri in uris]
|
|
294
|
+
if resource.is_in_template and template_info.builtin_template:
|
|
295
|
+
for filename in uris:
|
|
296
|
+
template_info.add_resource(
|
|
297
|
+
filename, OTHER_RESOURCE, target=css_targetdir, is_in_template=True
|
|
298
|
+
)
|
|
299
|
+
else:
|
|
300
|
+
for filename in uris:
|
|
301
|
+
source_files.add(copy_resource(filename, css_sourcedir, css_targetdir))
|
|
302
|
+
|
|
303
|
+
# Write the HTML out
|
|
304
|
+
htmldata = html.tostring(tree)
|
|
305
|
+
with open(os.path.join(args.targetdir, "index.html"), "wb") as outfile:
|
|
306
|
+
outfile.write(htmldata)
|
|
307
|
+
|
|
308
|
+
# All done!
|
|
309
|
+
|
|
310
|
+
return {os.path.abspath(f) for f in source_files if f}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def prepare_for_pdf(html_file_path):
|
|
314
|
+
# Read the HTML content from the file
|
|
315
|
+
with open(html_file_path, "r") as html_file:
|
|
316
|
+
html_content = html_file.read()
|
|
317
|
+
|
|
318
|
+
html_content = html_content.replace(
|
|
319
|
+
"<head>",
|
|
320
|
+
"""<head>
|
|
321
|
+
<style>
|
|
322
|
+
.pdfContainer
|
|
323
|
+
{
|
|
324
|
+
justify-content: center;
|
|
325
|
+
align-items: center;
|
|
326
|
+
display: flex;
|
|
327
|
+
width: 100%;
|
|
328
|
+
height: 100%;
|
|
329
|
+
page-break-after: always;
|
|
330
|
+
}
|
|
331
|
+
.substep
|
|
332
|
+
{
|
|
333
|
+
opacity: 1 !important;
|
|
334
|
+
}
|
|
335
|
+
</style>""",
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
# Find and delete the specific div
|
|
339
|
+
tree = html.fromstring(html_content)
|
|
340
|
+
divs_to_delete = tree.xpath('//div[@id="impress"]')
|
|
341
|
+
for div_to_delete in divs_to_delete:
|
|
342
|
+
parent = div_to_delete.getparent()
|
|
343
|
+
for element in div_to_delete.iterchildren():
|
|
344
|
+
parent.insert(parent.index(div_to_delete), element)
|
|
345
|
+
parent.remove(div_to_delete)
|
|
346
|
+
|
|
347
|
+
# Add a new div around each div with class "step"
|
|
348
|
+
step_divs = tree.xpath('//div[contains(@class, "step")]')
|
|
349
|
+
for step_div in step_divs:
|
|
350
|
+
container_div = html.Element("div")
|
|
351
|
+
container_div.set("class", "pdfContainer")
|
|
352
|
+
step_div.addprevious(container_div)
|
|
353
|
+
container_div.append(step_div)
|
|
354
|
+
|
|
355
|
+
# Convert the modified tree back to HTML
|
|
356
|
+
html_content = html.tostring(tree, encoding="unicode")
|
|
357
|
+
|
|
358
|
+
# Write the modified HTML back to the file
|
|
359
|
+
with open(html_file_path, "w") as html_file:
|
|
360
|
+
html_file.write(html_content)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def generate_pdf(args):
|
|
364
|
+
"""Generates the pdf and returns a list of files used"""
|
|
365
|
+
|
|
366
|
+
source_files = {args.presentation}
|
|
367
|
+
# Parse the template info
|
|
368
|
+
template_info = Template(args.template)
|
|
369
|
+
if args.css:
|
|
370
|
+
presentation_dir = os.path.split(args.presentation)[0]
|
|
371
|
+
target_path = os.path.relpath(args.css, presentation_dir)
|
|
372
|
+
template_info.add_resource(
|
|
373
|
+
args.css, CSS_RESOURCE, target=target_path, extra_info="all"
|
|
374
|
+
)
|
|
375
|
+
source_files.add(args.css)
|
|
376
|
+
if args.js:
|
|
377
|
+
presentation_dir = os.path.split(args.presentation)[0]
|
|
378
|
+
target_path = os.path.relpath(args.js, presentation_dir)
|
|
379
|
+
template_info.add_resource(
|
|
380
|
+
args.js, JS_RESOURCE, target=target_path, extra_info=JS_POSITION_BODY
|
|
381
|
+
)
|
|
382
|
+
source_files.add(args.js)
|
|
383
|
+
|
|
384
|
+
# Make the resulting HTML
|
|
385
|
+
htmldata, dependencies = rst2html(
|
|
386
|
+
args.presentation,
|
|
387
|
+
template_info,
|
|
388
|
+
args.auto_console,
|
|
389
|
+
args.skip_help,
|
|
390
|
+
args.skip_notes,
|
|
391
|
+
args.mathjax,
|
|
392
|
+
args.slide_numbers,
|
|
393
|
+
)
|
|
394
|
+
source_files.update(dependencies)
|
|
395
|
+
|
|
396
|
+
args.targetdir = "tmp"
|
|
397
|
+
|
|
398
|
+
# Write the HTML out
|
|
399
|
+
if not os.path.exists(args.targetdir):
|
|
400
|
+
os.makedirs(args.targetdir)
|
|
401
|
+
|
|
402
|
+
indexHtmlPath = os.path.abspath(os.path.join(args.targetdir, "index.html"))
|
|
403
|
+
|
|
404
|
+
with open(indexHtmlPath, "wb") as outfile:
|
|
405
|
+
outfile.write(htmldata)
|
|
406
|
+
|
|
407
|
+
# Copy supporting files
|
|
408
|
+
source_files.update(template_info.copy_resources(args.targetdir))
|
|
409
|
+
|
|
410
|
+
# Copy files from the source:
|
|
411
|
+
sourcedir = os.path.split(os.path.abspath(args.presentation))[0]
|
|
412
|
+
tree = html.fromstring(htmldata)
|
|
413
|
+
for image in tree.iterdescendants("img"):
|
|
414
|
+
filename = image.attrib["src"]
|
|
415
|
+
source_files.add(copy_resource(filename, sourcedir, args.targetdir))
|
|
416
|
+
for source in tree.iterdescendants("source"):
|
|
417
|
+
filename = source.attrib["src"]
|
|
418
|
+
source_files.add(copy_resource(filename, sourcedir, args.targetdir))
|
|
419
|
+
|
|
420
|
+
# Code for handling iframe sources
|
|
421
|
+
for iframe in tree.iterdescendants("iframe"):
|
|
422
|
+
iframe_src = iframe.attrib.get("src")
|
|
423
|
+
if iframe_src:
|
|
424
|
+
filename = iframe_src.split("/")[-1]
|
|
425
|
+
targetpath = os.path.join(args.targetdir, f"iframe/{filename}")
|
|
426
|
+
source_files.add(
|
|
427
|
+
copy_resource(
|
|
428
|
+
iframe_src,
|
|
429
|
+
sourcedir,
|
|
430
|
+
args.targetdir,
|
|
431
|
+
sourcepath=None,
|
|
432
|
+
targetpath=targetpath,
|
|
433
|
+
)
|
|
434
|
+
)
|
|
435
|
+
iframe.attrib["src"] = f"iframe/{filename}"
|
|
436
|
+
|
|
437
|
+
RE_CSS_URL = re.compile(rb"""url\(['"]?(.*?)['"]?[\)\?\#]""")
|
|
438
|
+
|
|
439
|
+
# Copy any files referenced by url() in the css-files:
|
|
440
|
+
for resource in template_info.resources:
|
|
441
|
+
if resource.resource_type != CSS_RESOURCE:
|
|
442
|
+
continue
|
|
443
|
+
# path in CSS is relative to CSS file; construct source/dest accordingly
|
|
444
|
+
css_base = template_info.template_root if resource.is_in_template else sourcedir
|
|
445
|
+
css_sourcedir = os.path.dirname(os.path.join(css_base, resource.filepath))
|
|
446
|
+
css_targetdir = os.path.dirname(
|
|
447
|
+
os.path.join(args.targetdir, resource.final_path())
|
|
448
|
+
)
|
|
449
|
+
uris = RE_CSS_URL.findall(template_info.read_data(resource))
|
|
450
|
+
uris = [uri.decode() for uri in uris]
|
|
451
|
+
if resource.is_in_template and template_info.builtin_template:
|
|
452
|
+
for filename in uris:
|
|
453
|
+
template_info.add_resource(
|
|
454
|
+
filename, OTHER_RESOURCE, target=css_targetdir, is_in_template=True
|
|
455
|
+
)
|
|
456
|
+
else:
|
|
457
|
+
for filename in uris:
|
|
458
|
+
source_files.add(copy_resource(filename, css_sourcedir, css_targetdir))
|
|
459
|
+
|
|
460
|
+
# ==========
|
|
461
|
+
|
|
462
|
+
prepare_for_pdf(indexHtmlPath)
|
|
463
|
+
|
|
464
|
+
converter.convert(
|
|
465
|
+
f"file:///{indexHtmlPath}",
|
|
466
|
+
args.pdf_output_path,
|
|
467
|
+
install_driver=False,
|
|
468
|
+
print_options={"landscape": True},
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
shutil.rmtree(args.targetdir)
|
|
472
|
+
|
|
473
|
+
return {os.path.abspath(f) for f in source_files if f}
|