Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/docutils/writers/s5_html/__init__.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 # $Id: __init__.py 8412 2019-11-06 18:15:21Z milde $ | |
2 # Authors: Chris Liechti <cliechti@gmx.net>; | |
3 # David Goodger <goodger@python.org> | |
4 # Copyright: This module has been placed in the public domain. | |
5 | |
6 """ | |
7 S5/HTML Slideshow Writer. | |
8 """ | |
9 | |
10 __docformat__ = 'reStructuredText' | |
11 | |
12 | |
13 import sys | |
14 import os | |
15 import re | |
16 import docutils | |
17 from docutils import frontend, nodes, utils | |
18 from docutils.writers import html4css1 | |
19 from docutils.parsers.rst import directives | |
20 | |
21 themes_dir_path = utils.relative_path( | |
22 os.path.join(os.getcwd(), 'dummy'), | |
23 os.path.join(os.path.dirname(__file__), 'themes')) | |
24 | |
25 def find_theme(name): | |
26 # Where else to look for a theme? | |
27 # Check working dir? Destination dir? Config dir? Plugins dir? | |
28 path = os.path.join(themes_dir_path, name) | |
29 if not os.path.isdir(path): | |
30 raise docutils.ApplicationError( | |
31 'Theme directory not found: %r (path: %r)' % (name, path)) | |
32 return path | |
33 | |
34 | |
35 class Writer(html4css1.Writer): | |
36 | |
37 settings_spec = html4css1.Writer.settings_spec + ( | |
38 'S5 Slideshow Specific Options', | |
39 'For the S5/HTML writer, the --no-toc-backlinks option ' | |
40 '(defined in General Docutils Options above) is the default, ' | |
41 'and should not be changed.', | |
42 (('Specify an installed S5 theme by name. Overrides --theme-url. ' | |
43 'The default theme name is "default". The theme files will be ' | |
44 'copied into a "ui/<theme>" directory, in the same directory as the ' | |
45 'destination file (output HTML). Note that existing theme files ' | |
46 'will not be overwritten (unless --overwrite-theme-files is used).', | |
47 ['--theme'], | |
48 {'default': 'default', 'metavar': '<name>', | |
49 'overrides': 'theme_url'}), | |
50 ('Specify an S5 theme URL. The destination file (output HTML) will ' | |
51 'link to this theme; nothing will be copied. Overrides --theme.', | |
52 ['--theme-url'], | |
53 {'metavar': '<URL>', 'overrides': 'theme'}), | |
54 ('Allow existing theme files in the ``ui/<theme>`` directory to be ' | |
55 'overwritten. The default is not to overwrite theme files.', | |
56 ['--overwrite-theme-files'], | |
57 {'action': 'store_true', 'validator': frontend.validate_boolean}), | |
58 ('Keep existing theme files in the ``ui/<theme>`` directory; do not ' | |
59 'overwrite any. This is the default.', | |
60 ['--keep-theme-files'], | |
61 {'dest': 'overwrite_theme_files', 'action': 'store_false'}), | |
62 ('Set the initial view mode to "slideshow" [default] or "outline".', | |
63 ['--view-mode'], | |
64 {'choices': ['slideshow', 'outline'], 'default': 'slideshow', | |
65 'metavar': '<mode>'}), | |
66 ('Normally hide the presentation controls in slideshow mode. ' | |
67 'This is the default.', | |
68 ['--hidden-controls'], | |
69 {'action': 'store_true', 'default': True, | |
70 'validator': frontend.validate_boolean}), | |
71 ('Always show the presentation controls in slideshow mode. ' | |
72 'The default is to hide the controls.', | |
73 ['--visible-controls'], | |
74 {'dest': 'hidden_controls', 'action': 'store_false'}), | |
75 ('Enable the current slide indicator ("1 / 15"). ' | |
76 'The default is to disable it.', | |
77 ['--current-slide'], | |
78 {'action': 'store_true', 'validator': frontend.validate_boolean}), | |
79 ('Disable the current slide indicator. This is the default.', | |
80 ['--no-current-slide'], | |
81 {'dest': 'current_slide', 'action': 'store_false'}),)) | |
82 | |
83 settings_default_overrides = {'toc_backlinks': 0} | |
84 | |
85 config_section = 's5_html writer' | |
86 config_section_dependencies = ('writers', 'html writers', | |
87 'html4css1 writer') | |
88 | |
89 def __init__(self): | |
90 html4css1.Writer.__init__(self) | |
91 self.translator_class = S5HTMLTranslator | |
92 | |
93 | |
94 class S5HTMLTranslator(html4css1.HTMLTranslator): | |
95 | |
96 s5_stylesheet_template = """\ | |
97 <!-- configuration parameters --> | |
98 <meta name="defaultView" content="%(view_mode)s" /> | |
99 <meta name="controlVis" content="%(control_visibility)s" /> | |
100 <!-- style sheet links --> | |
101 <script src="%(path)s/slides.js" type="text/javascript"></script> | |
102 <link rel="stylesheet" href="%(path)s/slides.css" | |
103 type="text/css" media="projection" id="slideProj" /> | |
104 <link rel="stylesheet" href="%(path)s/outline.css" | |
105 type="text/css" media="screen" id="outlineStyle" /> | |
106 <link rel="stylesheet" href="%(path)s/print.css" | |
107 type="text/css" media="print" id="slidePrint" /> | |
108 <link rel="stylesheet" href="%(path)s/opera.css" | |
109 type="text/css" media="projection" id="operaFix" />\n""" | |
110 # The script element must go in front of the link elements to | |
111 # avoid a flash of unstyled content (FOUC), reproducible with | |
112 # Firefox. | |
113 | |
114 disable_current_slide = """ | |
115 <style type="text/css"> | |
116 #currentSlide {display: none;} | |
117 </style>\n""" | |
118 | |
119 layout_template = """\ | |
120 <div class="layout"> | |
121 <div id="controls"></div> | |
122 <div id="currentSlide"></div> | |
123 <div id="header"> | |
124 %(header)s | |
125 </div> | |
126 <div id="footer"> | |
127 %(title)s%(footer)s | |
128 </div> | |
129 </div>\n""" | |
130 # <div class="topleft"></div> | |
131 # <div class="topright"></div> | |
132 # <div class="bottomleft"></div> | |
133 # <div class="bottomright"></div> | |
134 | |
135 default_theme = 'default' | |
136 """Name of the default theme.""" | |
137 | |
138 base_theme_file = '__base__' | |
139 """Name of the file containing the name of the base theme.""" | |
140 | |
141 direct_theme_files = ( | |
142 'slides.css', 'outline.css', 'print.css', 'opera.css', 'slides.js') | |
143 """Names of theme files directly linked to in the output HTML""" | |
144 | |
145 indirect_theme_files = ( | |
146 's5-core.css', 'framing.css', 'pretty.css', 'blank.gif', 'iepngfix.htc') | |
147 """Names of files used indirectly; imported or used by files in | |
148 `direct_theme_files`.""" | |
149 | |
150 required_theme_files = indirect_theme_files + direct_theme_files | |
151 """Names of mandatory theme files.""" | |
152 | |
153 def __init__(self, *args): | |
154 html4css1.HTMLTranslator.__init__(self, *args) | |
155 #insert S5-specific stylesheet and script stuff: | |
156 self.theme_file_path = None | |
157 self.setup_theme() | |
158 view_mode = self.document.settings.view_mode | |
159 control_visibility = ('visible', 'hidden')[self.document.settings | |
160 .hidden_controls] | |
161 self.stylesheet.append(self.s5_stylesheet_template | |
162 % {'path': self.theme_file_path, | |
163 'view_mode': view_mode, | |
164 'control_visibility': control_visibility}) | |
165 if not self.document.settings.current_slide: | |
166 self.stylesheet.append(self.disable_current_slide) | |
167 self.add_meta('<meta name="version" content="S5 1.1" />\n') | |
168 self.s5_footer = [] | |
169 self.s5_header = [] | |
170 self.section_count = 0 | |
171 self.theme_files_copied = None | |
172 | |
173 def setup_theme(self): | |
174 if self.document.settings.theme: | |
175 self.copy_theme() | |
176 elif self.document.settings.theme_url: | |
177 self.theme_file_path = self.document.settings.theme_url | |
178 else: | |
179 raise docutils.ApplicationError( | |
180 'No theme specified for S5/HTML writer.') | |
181 | |
182 def copy_theme(self): | |
183 """ | |
184 Locate & copy theme files. | |
185 | |
186 A theme may be explicitly based on another theme via a '__base__' | |
187 file. The default base theme is 'default'. Files are accumulated | |
188 from the specified theme, any base themes, and 'default'. | |
189 """ | |
190 settings = self.document.settings | |
191 path = find_theme(settings.theme) | |
192 theme_paths = [path] | |
193 self.theme_files_copied = {} | |
194 required_files_copied = {} | |
195 # This is a link (URL) in HTML, so we use "/", not os.sep: | |
196 self.theme_file_path = '%s/%s' % ('ui', settings.theme) | |
197 if settings._destination: | |
198 dest = os.path.join( | |
199 os.path.dirname(settings._destination), 'ui', settings.theme) | |
200 if not os.path.isdir(dest): | |
201 os.makedirs(dest) | |
202 else: | |
203 # no destination, so we can't copy the theme | |
204 return | |
205 default = False | |
206 while path: | |
207 for f in os.listdir(path): # copy all files from each theme | |
208 if f == self.base_theme_file: | |
209 continue # ... except the "__base__" file | |
210 if ( self.copy_file(f, path, dest) | |
211 and f in self.required_theme_files): | |
212 required_files_copied[f] = 1 | |
213 if default: | |
214 break # "default" theme has no base theme | |
215 # Find the "__base__" file in theme directory: | |
216 base_theme_file = os.path.join(path, self.base_theme_file) | |
217 # If it exists, read it and record the theme path: | |
218 if os.path.isfile(base_theme_file): | |
219 with open(base_theme_file) as f: | |
220 lines = f.readlines() | |
221 for line in lines: | |
222 line = line.strip() | |
223 if line and not line.startswith('#'): | |
224 path = find_theme(line) | |
225 if path in theme_paths: # check for duplicates (cycles) | |
226 path = None # if found, use default base | |
227 else: | |
228 theme_paths.append(path) | |
229 break | |
230 else: # no theme name found | |
231 path = None # use default base | |
232 else: # no base theme file found | |
233 path = None # use default base | |
234 if not path: | |
235 path = find_theme(self.default_theme) | |
236 theme_paths.append(path) | |
237 default = True | |
238 if len(required_files_copied) != len(self.required_theme_files): | |
239 # Some required files weren't found & couldn't be copied. | |
240 required = list(self.required_theme_files) | |
241 for f in required_files_copied.keys(): | |
242 required.remove(f) | |
243 raise docutils.ApplicationError( | |
244 'Theme files not found: %s' | |
245 % ', '.join(['%r' % f for f in required])) | |
246 | |
247 files_to_skip_pattern = re.compile(r'~$|\.bak$|#$|\.cvsignore$') | |
248 | |
249 def copy_file(self, name, source_dir, dest_dir): | |
250 """ | |
251 Copy file `name` from `source_dir` to `dest_dir`. | |
252 Return 1 if the file exists in either `source_dir` or `dest_dir`. | |
253 """ | |
254 source = os.path.join(source_dir, name) | |
255 dest = os.path.join(dest_dir, name) | |
256 if dest in self.theme_files_copied: | |
257 return 1 | |
258 else: | |
259 self.theme_files_copied[dest] = 1 | |
260 if os.path.isfile(source): | |
261 if self.files_to_skip_pattern.search(source): | |
262 return None | |
263 settings = self.document.settings | |
264 if os.path.exists(dest) and not settings.overwrite_theme_files: | |
265 settings.record_dependencies.add(dest) | |
266 else: | |
267 src_file = open(source, 'rb') | |
268 src_data = src_file.read() | |
269 src_file.close() | |
270 dest_file = open(dest, 'wb') | |
271 dest_dir = dest_dir.replace(os.sep, '/') | |
272 dest_file.write(src_data.replace(b'ui/default', | |
273 dest_dir[dest_dir.rfind('ui/'):].encode( | |
274 sys.getfilesystemencoding()))) | |
275 dest_file.close() | |
276 settings.record_dependencies.add(source) | |
277 return 1 | |
278 if os.path.isfile(dest): | |
279 return 1 | |
280 | |
281 def depart_document(self, node): | |
282 self.head_prefix.extend([self.doctype, | |
283 self.head_prefix_template % | |
284 {'lang': self.settings.language_code}]) | |
285 self.html_prolog.append(self.doctype) | |
286 self.meta.insert(0, self.content_type % self.settings.output_encoding) | |
287 self.head.insert(0, self.content_type % self.settings.output_encoding) | |
288 if self.math_header: | |
289 if self.math_output == 'mathjax': | |
290 self.head.extend(self.math_header) | |
291 else: | |
292 self.stylesheet.extend(self.math_header) | |
293 # skip content-type meta tag with interpolated charset value: | |
294 self.html_head.extend(self.head[1:]) | |
295 self.fragment.extend(self.body) | |
296 # special S5 code up to the next comment line | |
297 header = ''.join(self.s5_header) | |
298 footer = ''.join(self.s5_footer) | |
299 title = ''.join(self.html_title).replace('<h1 class="title">', '<h1>') | |
300 layout = self.layout_template % {'header': header, | |
301 'title': title, | |
302 'footer': footer} | |
303 self.body_prefix.extend(layout) | |
304 self.body_prefix.append('<div class="presentation">\n') | |
305 self.body_prefix.append( | |
306 self.starttag({'classes': ['slide'], 'ids': ['slide0']}, 'div')) | |
307 if not self.section_count: | |
308 self.body.append('</div>\n') | |
309 # | |
310 self.body_suffix.insert(0, '</div>\n') | |
311 self.html_body.extend(self.body_prefix[1:] + self.body_pre_docinfo | |
312 + self.docinfo + self.body | |
313 + self.body_suffix[:-1]) | |
314 | |
315 def depart_footer(self, node): | |
316 start = self.context.pop() | |
317 self.s5_footer.append('<h2>') | |
318 self.s5_footer.extend(self.body[start:]) | |
319 self.s5_footer.append('</h2>') | |
320 del self.body[start:] | |
321 | |
322 def depart_header(self, node): | |
323 start = self.context.pop() | |
324 header = ['<div id="header">\n'] | |
325 header.extend(self.body[start:]) | |
326 header.append('\n</div>\n') | |
327 del self.body[start:] | |
328 self.s5_header.extend(header) | |
329 | |
330 def visit_section(self, node): | |
331 if not self.section_count: | |
332 self.body.append('\n</div>\n') | |
333 self.section_count += 1 | |
334 self.section_level += 1 | |
335 if self.section_level > 1: | |
336 # dummy for matching div's | |
337 self.body.append(self.starttag(node, 'div', CLASS='section')) | |
338 else: | |
339 self.body.append(self.starttag(node, 'div', CLASS='slide')) | |
340 | |
341 def visit_subtitle(self, node): | |
342 if isinstance(node.parent, nodes.section): | |
343 level = self.section_level + self.initial_header_level - 1 | |
344 if level == 1: | |
345 level = 2 | |
346 tag = 'h%s' % level | |
347 self.body.append(self.starttag(node, tag, '')) | |
348 self.context.append('</%s>\n' % tag) | |
349 else: | |
350 html4css1.HTMLTranslator.visit_subtitle(self, node) | |
351 | |
352 def visit_title(self, node): | |
353 html4css1.HTMLTranslator.visit_title(self, node) |