diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-02 13:21:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-05-02 13:21:17 -0400 |
commit | c58d4055c054fc6dc72f1be8bc71bd6fff209e48 (patch) | |
tree | 56527e28fc65d74d6d5e8397c502ccc87a9ec99b /Documentation/sphinx/kfigure.py | |
parent | ceb198bb007b84ead867e87a71ffe715c4412b15 (diff) | |
parent | 9bb0e9cb04c82d6bf0e72f3207307d621083b801 (diff) |
Merge tag 'docs-4.12' of git://git.lwn.net/linux
Pull documentation update from Jonathan Corbet:
"A reasonably busy cycle for documentation this time around. There is a
new guide for user-space API documents, rather sparsely populated at
the moment, but it's a start. Markus improved the infrastructure for
converting diagrams. Mauro has converted much of the USB documentation
over to RST. Plus the usual set of fixes, improvements, and tweaks.
There's a bit more than the usual amount of reaching out of
Documentation/ to fix comments elsewhere in the tree; I have acks for
those where I could get them"
* tag 'docs-4.12' of git://git.lwn.net/linux: (74 commits)
docs: Fix a couple typos
docs: Fix a spelling error in vfio-mediated-device.txt
docs: Fix a spelling error in ioctl-number.txt
MAINTAINERS: update file entry for HSI subsystem
Documentation: allow installing man pages to a user defined directory
Doc/PM: Sync with intel_powerclamp code behavior
zr364xx.rst: usb/devices is now at /sys/kernel/debug/
usb.rst: move documentation from proc_usb_info.txt to USB ReST book
convert philips.txt to ReST and add to media docs
docs-rst: usb: update old usbfs-related documentation
arm: Documentation: update a path name
docs: process/4.Coding.rst: Fix a couple of document refs
docs-rst: fix usb cross-references
usb: gadget.h: be consistent at kernel doc macros
usb: composite.h: fix two warnings when building docs
usb: get rid of some ReST doc build errors
usb.rst: get rid of some Sphinx errors
usb/URB.txt: convert to ReST and update it
usb/persist.txt: convert to ReST and add to driver-api book
usb/hotplug.txt: convert to ReST and add to driver-api book
...
Diffstat (limited to 'Documentation/sphinx/kfigure.py')
-rw-r--r-- | Documentation/sphinx/kfigure.py | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py new file mode 100644 index 000000000000..cef4ad19624c --- /dev/null +++ b/Documentation/sphinx/kfigure.py | |||
@@ -0,0 +1,551 @@ | |||
1 | # -*- coding: utf-8; mode: python -*- | ||
2 | # pylint: disable=C0103, R0903, R0912, R0915 | ||
3 | u""" | ||
4 | scalable figure and image handling | ||
5 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
6 | |||
7 | Sphinx extension which implements scalable image handling. | ||
8 | |||
9 | :copyright: Copyright (C) 2016 Markus Heiser | ||
10 | :license: GPL Version 2, June 1991 see Linux/COPYING for details. | ||
11 | |||
12 | The build for image formats depend on image's source format and output's | ||
13 | destination format. This extension implement methods to simplify image | ||
14 | handling from the author's POV. Directives like ``kernel-figure`` implement | ||
15 | methods *to* always get the best output-format even if some tools are not | ||
16 | installed. For more details take a look at ``convert_image(...)`` which is | ||
17 | the core of all conversions. | ||
18 | |||
19 | * ``.. kernel-image``: for image handling / a ``.. image::`` replacement | ||
20 | |||
21 | * ``.. kernel-figure``: for figure handling / a ``.. figure::`` replacement | ||
22 | |||
23 | * ``.. kernel-render``: for render markup / a concept to embed *render* | ||
24 | markups (or languages). Supported markups (see ``RENDER_MARKUP_EXT``) | ||
25 | |||
26 | - ``DOT``: render embedded Graphviz's **DOC** | ||
27 | - ``SVG``: render embedded Scalable Vector Graphics (**SVG**) | ||
28 | - ... *developable* | ||
29 | |||
30 | Used tools: | ||
31 | |||
32 | * ``dot(1)``: Graphviz (http://www.graphviz.org). If Graphviz is not | ||
33 | available, the DOT language is inserted as literal-block. | ||
34 | |||
35 | * SVG to PDF: To generate PDF, you need at least one of this tools: | ||
36 | |||
37 | - ``convert(1)``: ImageMagick (https://www.imagemagick.org) | ||
38 | |||
39 | List of customizations: | ||
40 | |||
41 | * generate PDF from SVG / used by PDF (LaTeX) builder | ||
42 | |||
43 | * generate SVG (html-builder) and PDF (latex-builder) from DOT files. | ||
44 | DOT: see http://www.graphviz.org/content/dot-language | ||
45 | |||
46 | """ | ||
47 | |||
48 | import os | ||
49 | from os import path | ||
50 | import subprocess | ||
51 | from hashlib import sha1 | ||
52 | import sys | ||
53 | |||
54 | from docutils import nodes | ||
55 | from docutils.statemachine import ViewList | ||
56 | from docutils.parsers.rst import directives | ||
57 | from docutils.parsers.rst.directives import images | ||
58 | import sphinx | ||
59 | |||
60 | from sphinx.util.nodes import clean_astext | ||
61 | from six import iteritems | ||
62 | |||
63 | PY3 = sys.version_info[0] == 3 | ||
64 | |||
65 | if PY3: | ||
66 | _unicode = str | ||
67 | else: | ||
68 | _unicode = unicode | ||
69 | |||
70 | # Get Sphinx version | ||
71 | major, minor, patch = sphinx.version_info[:3] | ||
72 | if major == 1 and minor > 3: | ||
73 | # patches.Figure only landed in Sphinx 1.4 | ||
74 | from sphinx.directives.patches import Figure # pylint: disable=C0413 | ||
75 | else: | ||
76 | Figure = images.Figure | ||
77 | |||
78 | __version__ = '1.0.0' | ||
79 | |||
80 | # simple helper | ||
81 | # ------------- | ||
82 | |||
83 | def which(cmd): | ||
84 | """Searches the ``cmd`` in the ``PATH`` enviroment. | ||
85 | |||
86 | This *which* searches the PATH for executable ``cmd`` . First match is | ||
87 | returned, if nothing is found, ``None` is returned. | ||
88 | """ | ||
89 | envpath = os.environ.get('PATH', None) or os.defpath | ||
90 | for folder in envpath.split(os.pathsep): | ||
91 | fname = folder + os.sep + cmd | ||
92 | if path.isfile(fname): | ||
93 | return fname | ||
94 | |||
95 | def mkdir(folder, mode=0o775): | ||
96 | if not path.isdir(folder): | ||
97 | os.makedirs(folder, mode) | ||
98 | |||
99 | def file2literal(fname): | ||
100 | with open(fname, "r") as src: | ||
101 | data = src.read() | ||
102 | node = nodes.literal_block(data, data) | ||
103 | return node | ||
104 | |||
105 | def isNewer(path1, path2): | ||
106 | """Returns True if ``path1`` is newer than ``path2`` | ||
107 | |||
108 | If ``path1`` exists and is newer than ``path2`` the function returns | ||
109 | ``True`` is returned otherwise ``False`` | ||
110 | """ | ||
111 | return (path.exists(path1) | ||
112 | and os.stat(path1).st_ctime > os.stat(path2).st_ctime) | ||
113 | |||
114 | def pass_handle(self, node): # pylint: disable=W0613 | ||
115 | pass | ||
116 | |||
117 | # setup conversion tools and sphinx extension | ||
118 | # ------------------------------------------- | ||
119 | |||
120 | # Graphviz's dot(1) support | ||
121 | dot_cmd = None | ||
122 | |||
123 | # ImageMagick' convert(1) support | ||
124 | convert_cmd = None | ||
125 | |||
126 | |||
127 | def setup(app): | ||
128 | # check toolchain first | ||
129 | app.connect('builder-inited', setupTools) | ||
130 | |||
131 | # image handling | ||
132 | app.add_directive("kernel-image", KernelImage) | ||
133 | app.add_node(kernel_image, | ||
134 | html = (visit_kernel_image, pass_handle), | ||
135 | latex = (visit_kernel_image, pass_handle), | ||
136 | texinfo = (visit_kernel_image, pass_handle), | ||
137 | text = (visit_kernel_image, pass_handle), | ||
138 | man = (visit_kernel_image, pass_handle), ) | ||
139 | |||
140 | # figure handling | ||
141 | app.add_directive("kernel-figure", KernelFigure) | ||
142 | app.add_node(kernel_figure, | ||
143 | html = (visit_kernel_figure, pass_handle), | ||
144 | latex = (visit_kernel_figure, pass_handle), | ||
145 | texinfo = (visit_kernel_figure, pass_handle), | ||
146 | text = (visit_kernel_figure, pass_handle), | ||
147 | man = (visit_kernel_figure, pass_handle), ) | ||
148 | |||
149 | # render handling | ||
150 | app.add_directive('kernel-render', KernelRender) | ||
151 | app.add_node(kernel_render, | ||
152 | html = (visit_kernel_render, pass_handle), | ||
153 | latex = (visit_kernel_render, pass_handle), | ||
154 | texinfo = (visit_kernel_render, pass_handle), | ||
155 | text = (visit_kernel_render, pass_handle), | ||
156 | man = (visit_kernel_render, pass_handle), ) | ||
157 | |||
158 | app.connect('doctree-read', add_kernel_figure_to_std_domain) | ||
159 | |||
160 | return dict( | ||
161 | version = __version__, | ||
162 | parallel_read_safe = True, | ||
163 | parallel_write_safe = True | ||
164 | ) | ||
165 | |||
166 | |||
167 | def setupTools(app): | ||
168 | u""" | ||
169 | Check available build tools and log some *verbose* messages. | ||
170 | |||
171 | This function is called once, when the builder is initiated. | ||
172 | """ | ||
173 | global dot_cmd, convert_cmd # pylint: disable=W0603 | ||
174 | app.verbose("kfigure: check installed tools ...") | ||
175 | |||
176 | dot_cmd = which('dot') | ||
177 | convert_cmd = which('convert') | ||
178 | |||
179 | if dot_cmd: | ||
180 | app.verbose("use dot(1) from: " + dot_cmd) | ||
181 | else: | ||
182 | app.warn("dot(1) not found, for better output quality install " | ||
183 | "graphviz from http://www.graphviz.org") | ||
184 | if convert_cmd: | ||
185 | app.verbose("use convert(1) from: " + convert_cmd) | ||
186 | else: | ||
187 | app.warn( | ||
188 | "convert(1) not found, for SVG to PDF conversion install " | ||
189 | "ImageMagick (https://www.imagemagick.org)") | ||
190 | |||
191 | |||
192 | # integrate conversion tools | ||
193 | # -------------------------- | ||
194 | |||
195 | RENDER_MARKUP_EXT = { | ||
196 | # The '.ext' must be handled by convert_image(..) function's *in_ext* input. | ||
197 | # <name> : <.ext> | ||
198 | 'DOT' : '.dot', | ||
199 | 'SVG' : '.svg' | ||
200 | } | ||
201 | |||
202 | def convert_image(img_node, translator, src_fname=None): | ||
203 | """Convert a image node for the builder. | ||
204 | |||
205 | Different builder prefer different image formats, e.g. *latex* builder | ||
206 | prefer PDF while *html* builder prefer SVG format for images. | ||
207 | |||
208 | This function handles output image formats in dependence of source the | ||
209 | format (of the image) and the translator's output format. | ||
210 | """ | ||
211 | app = translator.builder.app | ||
212 | |||
213 | fname, in_ext = path.splitext(path.basename(img_node['uri'])) | ||
214 | if src_fname is None: | ||
215 | src_fname = path.join(translator.builder.srcdir, img_node['uri']) | ||
216 | if not path.exists(src_fname): | ||
217 | src_fname = path.join(translator.builder.outdir, img_node['uri']) | ||
218 | |||
219 | dst_fname = None | ||
220 | |||
221 | # in kernel builds, use 'make SPHINXOPTS=-v' to see verbose messages | ||
222 | |||
223 | app.verbose('assert best format for: ' + img_node['uri']) | ||
224 | |||
225 | if in_ext == '.dot': | ||
226 | |||
227 | if not dot_cmd: | ||
228 | app.verbose("dot from graphviz not available / include DOT raw.") | ||
229 | img_node.replace_self(file2literal(src_fname)) | ||
230 | |||
231 | elif translator.builder.format == 'latex': | ||
232 | dst_fname = path.join(translator.builder.outdir, fname + '.pdf') | ||
233 | img_node['uri'] = fname + '.pdf' | ||
234 | img_node['candidates'] = {'*': fname + '.pdf'} | ||
235 | |||
236 | |||
237 | elif translator.builder.format == 'html': | ||
238 | dst_fname = path.join( | ||
239 | translator.builder.outdir, | ||
240 | translator.builder.imagedir, | ||
241 | fname + '.svg') | ||
242 | img_node['uri'] = path.join( | ||
243 | translator.builder.imgpath, fname + '.svg') | ||
244 | img_node['candidates'] = { | ||
245 | '*': path.join(translator.builder.imgpath, fname + '.svg')} | ||
246 | |||
247 | else: | ||
248 | # all other builder formats will include DOT as raw | ||
249 | img_node.replace_self(file2literal(src_fname)) | ||
250 | |||
251 | elif in_ext == '.svg': | ||
252 | |||
253 | if translator.builder.format == 'latex': | ||
254 | if convert_cmd is None: | ||
255 | app.verbose("no SVG to PDF conversion available / include SVG raw.") | ||
256 | img_node.replace_self(file2literal(src_fname)) | ||
257 | else: | ||
258 | dst_fname = path.join(translator.builder.outdir, fname + '.pdf') | ||
259 | img_node['uri'] = fname + '.pdf' | ||
260 | img_node['candidates'] = {'*': fname + '.pdf'} | ||
261 | |||
262 | if dst_fname: | ||
263 | # the builder needs not to copy one more time, so pop it if exists. | ||
264 | translator.builder.images.pop(img_node['uri'], None) | ||
265 | _name = dst_fname[len(translator.builder.outdir) + 1:] | ||
266 | |||
267 | if isNewer(dst_fname, src_fname): | ||
268 | app.verbose("convert: {out}/%s already exists and is newer" % _name) | ||
269 | |||
270 | else: | ||
271 | ok = False | ||
272 | mkdir(path.dirname(dst_fname)) | ||
273 | |||
274 | if in_ext == '.dot': | ||
275 | app.verbose('convert DOT to: {out}/' + _name) | ||
276 | ok = dot2format(app, src_fname, dst_fname) | ||
277 | |||
278 | elif in_ext == '.svg': | ||
279 | app.verbose('convert SVG to: {out}/' + _name) | ||
280 | ok = svg2pdf(app, src_fname, dst_fname) | ||
281 | |||
282 | if not ok: | ||
283 | img_node.replace_self(file2literal(src_fname)) | ||
284 | |||
285 | |||
286 | def dot2format(app, dot_fname, out_fname): | ||
287 | """Converts DOT file to ``out_fname`` using ``dot(1)``. | ||
288 | |||
289 | * ``dot_fname`` pathname of the input DOT file, including extension ``.dot`` | ||
290 | * ``out_fname`` pathname of the output file, including format extension | ||
291 | |||
292 | The *format extension* depends on the ``dot`` command (see ``man dot`` | ||
293 | option ``-Txxx``). Normally you will use one of the following extensions: | ||
294 | |||
295 | - ``.ps`` for PostScript, | ||
296 | - ``.svg`` or ``svgz`` for Structured Vector Graphics, | ||
297 | - ``.fig`` for XFIG graphics and | ||
298 | - ``.png`` or ``gif`` for common bitmap graphics. | ||
299 | |||
300 | """ | ||
301 | out_format = path.splitext(out_fname)[1][1:] | ||
302 | cmd = [dot_cmd, '-T%s' % out_format, dot_fname] | ||
303 | exit_code = 42 | ||
304 | |||
305 | with open(out_fname, "w") as out: | ||
306 | exit_code = subprocess.call(cmd, stdout = out) | ||
307 | if exit_code != 0: | ||
308 | app.warn("Error #%d when calling: %s" % (exit_code, " ".join(cmd))) | ||
309 | return bool(exit_code == 0) | ||
310 | |||
311 | def svg2pdf(app, svg_fname, pdf_fname): | ||
312 | """Converts SVG to PDF with ``convert(1)`` command. | ||
313 | |||
314 | Uses ``convert(1)`` from ImageMagick (https://www.imagemagick.org) for | ||
315 | conversion. Returns ``True`` on success and ``False`` if an error occurred. | ||
316 | |||
317 | * ``svg_fname`` pathname of the input SVG file with extension (``.svg``) | ||
318 | * ``pdf_name`` pathname of the output PDF file with extension (``.pdf``) | ||
319 | |||
320 | """ | ||
321 | cmd = [convert_cmd, svg_fname, pdf_fname] | ||
322 | # use stdout and stderr from parent | ||
323 | exit_code = subprocess.call(cmd) | ||
324 | if exit_code != 0: | ||
325 | app.warn("Error #%d when calling: %s" % (exit_code, " ".join(cmd))) | ||
326 | return bool(exit_code == 0) | ||
327 | |||
328 | |||
329 | # image handling | ||
330 | # --------------------- | ||
331 | |||
332 | def visit_kernel_image(self, node): # pylint: disable=W0613 | ||
333 | """Visitor of the ``kernel_image`` Node. | ||
334 | |||
335 | Handles the ``image`` child-node with the ``convert_image(...)``. | ||
336 | """ | ||
337 | img_node = node[0] | ||
338 | convert_image(img_node, self) | ||
339 | |||
340 | class kernel_image(nodes.image): | ||
341 | """Node for ``kernel-image`` directive.""" | ||
342 | pass | ||
343 | |||
344 | class KernelImage(images.Image): | ||
345 | u"""KernelImage directive | ||
346 | |||
347 | Earns everything from ``.. image::`` directive, except *remote URI* and | ||
348 | *glob* pattern. The KernelImage wraps a image node into a | ||
349 | kernel_image node. See ``visit_kernel_image``. | ||
350 | """ | ||
351 | |||
352 | def run(self): | ||
353 | uri = self.arguments[0] | ||
354 | if uri.endswith('.*') or uri.find('://') != -1: | ||
355 | raise self.severe( | ||
356 | 'Error in "%s: %s": glob pattern and remote images are not allowed' | ||
357 | % (self.name, uri)) | ||
358 | result = images.Image.run(self) | ||
359 | if len(result) == 2 or isinstance(result[0], nodes.system_message): | ||
360 | return result | ||
361 | (image_node,) = result | ||
362 | # wrap image node into a kernel_image node / see visitors | ||
363 | node = kernel_image('', image_node) | ||
364 | return [node] | ||
365 | |||
366 | # figure handling | ||
367 | # --------------------- | ||
368 | |||
369 | def visit_kernel_figure(self, node): # pylint: disable=W0613 | ||
370 | """Visitor of the ``kernel_figure`` Node. | ||
371 | |||
372 | Handles the ``image`` child-node with the ``convert_image(...)``. | ||
373 | """ | ||
374 | img_node = node[0][0] | ||
375 | convert_image(img_node, self) | ||
376 | |||
377 | class kernel_figure(nodes.figure): | ||
378 | """Node for ``kernel-figure`` directive.""" | ||
379 | |||
380 | class KernelFigure(Figure): | ||
381 | u"""KernelImage directive | ||
382 | |||
383 | Earns everything from ``.. figure::`` directive, except *remote URI* and | ||
384 | *glob* pattern. The KernelFigure wraps a figure node into a kernel_figure | ||
385 | node. See ``visit_kernel_figure``. | ||
386 | """ | ||
387 | |||
388 | def run(self): | ||
389 | uri = self.arguments[0] | ||
390 | if uri.endswith('.*') or uri.find('://') != -1: | ||
391 | raise self.severe( | ||
392 | 'Error in "%s: %s":' | ||
393 | ' glob pattern and remote images are not allowed' | ||
394 | % (self.name, uri)) | ||
395 | result = Figure.run(self) | ||
396 | if len(result) == 2 or isinstance(result[0], nodes.system_message): | ||
397 | return result | ||
398 | (figure_node,) = result | ||
399 | # wrap figure node into a kernel_figure node / see visitors | ||
400 | node = kernel_figure('', figure_node) | ||
401 | return [node] | ||
402 | |||
403 | |||
404 | # render handling | ||
405 | # --------------------- | ||
406 | |||
407 | def visit_kernel_render(self, node): | ||
408 | """Visitor of the ``kernel_render`` Node. | ||
409 | |||
410 | If rendering tools available, save the markup of the ``literal_block`` child | ||
411 | node into a file and replace the ``literal_block`` node with a new created | ||
412 | ``image`` node, pointing to the saved markup file. Afterwards, handle the | ||
413 | image child-node with the ``convert_image(...)``. | ||
414 | """ | ||
415 | app = self.builder.app | ||
416 | srclang = node.get('srclang') | ||
417 | |||
418 | app.verbose('visit kernel-render node lang: "%s"' % (srclang)) | ||
419 | |||
420 | tmp_ext = RENDER_MARKUP_EXT.get(srclang, None) | ||
421 | if tmp_ext is None: | ||
422 | app.warn('kernel-render: "%s" unknow / include raw.' % (srclang)) | ||
423 | return | ||
424 | |||
425 | if not dot_cmd and tmp_ext == '.dot': | ||
426 | app.verbose("dot from graphviz not available / include raw.") | ||
427 | return | ||
428 | |||
429 | literal_block = node[0] | ||
430 | |||
431 | code = literal_block.astext() | ||
432 | hashobj = code.encode('utf-8') # str(node.attributes) | ||
433 | fname = path.join('%s-%s' % (srclang, sha1(hashobj).hexdigest())) | ||
434 | |||
435 | tmp_fname = path.join( | ||
436 | self.builder.outdir, self.builder.imagedir, fname + tmp_ext) | ||
437 | |||
438 | if not path.isfile(tmp_fname): | ||
439 | mkdir(path.dirname(tmp_fname)) | ||
440 | with open(tmp_fname, "w") as out: | ||
441 | out.write(code) | ||
442 | |||
443 | img_node = nodes.image(node.rawsource, **node.attributes) | ||
444 | img_node['uri'] = path.join(self.builder.imgpath, fname + tmp_ext) | ||
445 | img_node['candidates'] = { | ||
446 | '*': path.join(self.builder.imgpath, fname + tmp_ext)} | ||
447 | |||
448 | literal_block.replace_self(img_node) | ||
449 | convert_image(img_node, self, tmp_fname) | ||
450 | |||
451 | |||
452 | class kernel_render(nodes.General, nodes.Inline, nodes.Element): | ||
453 | """Node for ``kernel-render`` directive.""" | ||
454 | pass | ||
455 | |||
456 | class KernelRender(Figure): | ||
457 | u"""KernelRender directive | ||
458 | |||
459 | Render content by external tool. Has all the options known from the | ||
460 | *figure* directive, plus option ``caption``. If ``caption`` has a | ||
461 | value, a figure node with the *caption* is inserted. If not, a image node is | ||
462 | inserted. | ||
463 | |||
464 | The KernelRender directive wraps the text of the directive into a | ||
465 | literal_block node and wraps it into a kernel_render node. See | ||
466 | ``visit_kernel_render``. | ||
467 | """ | ||
468 | has_content = True | ||
469 | required_arguments = 1 | ||
470 | optional_arguments = 0 | ||
471 | final_argument_whitespace = False | ||
472 | |||
473 | # earn options from 'figure' | ||
474 | option_spec = Figure.option_spec.copy() | ||
475 | option_spec['caption'] = directives.unchanged | ||
476 | |||
477 | def run(self): | ||
478 | return [self.build_node()] | ||
479 | |||
480 | def build_node(self): | ||
481 | |||
482 | srclang = self.arguments[0].strip() | ||
483 | if srclang not in RENDER_MARKUP_EXT.keys(): | ||
484 | return [self.state_machine.reporter.warning( | ||
485 | 'Unknow source language "%s", use one of: %s.' % ( | ||
486 | srclang, ",".join(RENDER_MARKUP_EXT.keys())), | ||
487 | line=self.lineno)] | ||
488 | |||
489 | code = '\n'.join(self.content) | ||
490 | if not code.strip(): | ||
491 | return [self.state_machine.reporter.warning( | ||
492 | 'Ignoring "%s" directive without content.' % ( | ||
493 | self.name), | ||
494 | line=self.lineno)] | ||
495 | |||
496 | node = kernel_render() | ||
497 | node['alt'] = self.options.get('alt','') | ||
498 | node['srclang'] = srclang | ||
499 | literal_node = nodes.literal_block(code, code) | ||
500 | node += literal_node | ||
501 | |||
502 | caption = self.options.get('caption') | ||
503 | if caption: | ||
504 | # parse caption's content | ||
505 | parsed = nodes.Element() | ||
506 | self.state.nested_parse( | ||
507 | ViewList([caption], source=''), self.content_offset, parsed) | ||
508 | caption_node = nodes.caption( | ||
509 | parsed[0].rawsource, '', *parsed[0].children) | ||
510 | caption_node.source = parsed[0].source | ||
511 | caption_node.line = parsed[0].line | ||
512 | |||
513 | figure_node = nodes.figure('', node) | ||
514 | for k,v in self.options.items(): | ||
515 | figure_node[k] = v | ||
516 | figure_node += caption_node | ||
517 | |||
518 | node = figure_node | ||
519 | |||
520 | return node | ||
521 | |||
522 | def add_kernel_figure_to_std_domain(app, doctree): | ||
523 | """Add kernel-figure anchors to 'std' domain. | ||
524 | |||
525 | The ``StandardDomain.process_doc(..)`` method does not know how to resolve | ||
526 | the caption (label) of ``kernel-figure`` directive (it only knows about | ||
527 | standard nodes, e.g. table, figure etc.). Without any additional handling | ||
528 | this will result in a 'undefined label' for kernel-figures. | ||
529 | |||
530 | This handle adds labels of kernel-figure to the 'std' domain labels. | ||
531 | """ | ||
532 | |||
533 | std = app.env.domains["std"] | ||
534 | docname = app.env.docname | ||
535 | labels = std.data["labels"] | ||
536 | |||
537 | for name, explicit in iteritems(doctree.nametypes): | ||
538 | if not explicit: | ||
539 | continue | ||
540 | labelid = doctree.nameids[name] | ||
541 | if labelid is None: | ||
542 | continue | ||
543 | node = doctree.ids[labelid] | ||
544 | |||
545 | if node.tagname == 'kernel_figure': | ||
546 | for n in node.next_node(): | ||
547 | if n.tagname == 'caption': | ||
548 | sectname = clean_astext(n) | ||
549 | # add label to std domain | ||
550 | labels[name] = docname, labelid, sectname | ||
551 | break | ||