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