18c2ecf20Sopenharmony_ci# -*- coding: utf-8; mode: python -*- 28c2ecf20Sopenharmony_ci# pylint: disable=C0103, R0903, R0912, R0915 38c2ecf20Sopenharmony_ciu""" 48c2ecf20Sopenharmony_ci scalable figure and image handling 58c2ecf20Sopenharmony_ci ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Sphinx extension which implements scalable image handling. 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci :copyright: Copyright (C) 2016 Markus Heiser 108c2ecf20Sopenharmony_ci :license: GPL Version 2, June 1991 see Linux/COPYING for details. 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci The build for image formats depend on image's source format and output's 138c2ecf20Sopenharmony_ci destination format. This extension implement methods to simplify image 148c2ecf20Sopenharmony_ci handling from the author's POV. Directives like ``kernel-figure`` implement 158c2ecf20Sopenharmony_ci methods *to* always get the best output-format even if some tools are not 168c2ecf20Sopenharmony_ci installed. For more details take a look at ``convert_image(...)`` which is 178c2ecf20Sopenharmony_ci the core of all conversions. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci * ``.. kernel-image``: for image handling / a ``.. image::`` replacement 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci * ``.. kernel-figure``: for figure handling / a ``.. figure::`` replacement 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci * ``.. kernel-render``: for render markup / a concept to embed *render* 248c2ecf20Sopenharmony_ci markups (or languages). Supported markups (see ``RENDER_MARKUP_EXT``) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci - ``DOT``: render embedded Graphviz's **DOC** 278c2ecf20Sopenharmony_ci - ``SVG``: render embedded Scalable Vector Graphics (**SVG**) 288c2ecf20Sopenharmony_ci - ... *developable* 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci Used tools: 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci * ``dot(1)``: Graphviz (https://www.graphviz.org). If Graphviz is not 338c2ecf20Sopenharmony_ci available, the DOT language is inserted as literal-block. 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci * SVG to PDF: To generate PDF, you need at least one of this tools: 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci - ``convert(1)``: ImageMagick (https://www.imagemagick.org) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci List of customizations: 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci * generate PDF from SVG / used by PDF (LaTeX) builder 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci * generate SVG (html-builder) and PDF (latex-builder) from DOT files. 448c2ecf20Sopenharmony_ci DOT: see https://www.graphviz.org/content/dot-language 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci """ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ciimport os 498c2ecf20Sopenharmony_cifrom os import path 508c2ecf20Sopenharmony_ciimport subprocess 518c2ecf20Sopenharmony_cifrom hashlib import sha1 528c2ecf20Sopenharmony_ciimport sys 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cifrom docutils import nodes 558c2ecf20Sopenharmony_cifrom docutils.statemachine import ViewList 568c2ecf20Sopenharmony_cifrom docutils.parsers.rst import directives 578c2ecf20Sopenharmony_cifrom docutils.parsers.rst.directives import images 588c2ecf20Sopenharmony_ciimport sphinx 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cifrom sphinx.util.nodes import clean_astext 618c2ecf20Sopenharmony_cifrom six import iteritems 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciimport kernellog 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ciPY3 = sys.version_info[0] == 3 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ciif PY3: 688c2ecf20Sopenharmony_ci _unicode = str 698c2ecf20Sopenharmony_cielse: 708c2ecf20Sopenharmony_ci _unicode = unicode 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci# Get Sphinx version 738c2ecf20Sopenharmony_cimajor, minor, patch = sphinx.version_info[:3] 748c2ecf20Sopenharmony_ciif major == 1 and minor > 3: 758c2ecf20Sopenharmony_ci # patches.Figure only landed in Sphinx 1.4 768c2ecf20Sopenharmony_ci from sphinx.directives.patches import Figure # pylint: disable=C0413 778c2ecf20Sopenharmony_cielse: 788c2ecf20Sopenharmony_ci Figure = images.Figure 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci__version__ = '1.0.0' 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci# simple helper 838c2ecf20Sopenharmony_ci# ------------- 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cidef which(cmd): 868c2ecf20Sopenharmony_ci """Searches the ``cmd`` in the ``PATH`` environment. 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci This *which* searches the PATH for executable ``cmd`` . First match is 898c2ecf20Sopenharmony_ci returned, if nothing is found, ``None` is returned. 908c2ecf20Sopenharmony_ci """ 918c2ecf20Sopenharmony_ci envpath = os.environ.get('PATH', None) or os.defpath 928c2ecf20Sopenharmony_ci for folder in envpath.split(os.pathsep): 938c2ecf20Sopenharmony_ci fname = folder + os.sep + cmd 948c2ecf20Sopenharmony_ci if path.isfile(fname): 958c2ecf20Sopenharmony_ci return fname 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cidef mkdir(folder, mode=0o775): 988c2ecf20Sopenharmony_ci if not path.isdir(folder): 998c2ecf20Sopenharmony_ci os.makedirs(folder, mode) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cidef file2literal(fname): 1028c2ecf20Sopenharmony_ci with open(fname, "r") as src: 1038c2ecf20Sopenharmony_ci data = src.read() 1048c2ecf20Sopenharmony_ci node = nodes.literal_block(data, data) 1058c2ecf20Sopenharmony_ci return node 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cidef isNewer(path1, path2): 1088c2ecf20Sopenharmony_ci """Returns True if ``path1`` is newer than ``path2`` 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci If ``path1`` exists and is newer than ``path2`` the function returns 1118c2ecf20Sopenharmony_ci ``True`` is returned otherwise ``False`` 1128c2ecf20Sopenharmony_ci """ 1138c2ecf20Sopenharmony_ci return (path.exists(path1) 1148c2ecf20Sopenharmony_ci and os.stat(path1).st_ctime > os.stat(path2).st_ctime) 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cidef pass_handle(self, node): # pylint: disable=W0613 1178c2ecf20Sopenharmony_ci pass 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci# setup conversion tools and sphinx extension 1208c2ecf20Sopenharmony_ci# ------------------------------------------- 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci# Graphviz's dot(1) support 1238c2ecf20Sopenharmony_cidot_cmd = None 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci# ImageMagick' convert(1) support 1268c2ecf20Sopenharmony_ciconvert_cmd = None 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cidef setup(app): 1308c2ecf20Sopenharmony_ci # check toolchain first 1318c2ecf20Sopenharmony_ci app.connect('builder-inited', setupTools) 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci # image handling 1348c2ecf20Sopenharmony_ci app.add_directive("kernel-image", KernelImage) 1358c2ecf20Sopenharmony_ci app.add_node(kernel_image, 1368c2ecf20Sopenharmony_ci html = (visit_kernel_image, pass_handle), 1378c2ecf20Sopenharmony_ci latex = (visit_kernel_image, pass_handle), 1388c2ecf20Sopenharmony_ci texinfo = (visit_kernel_image, pass_handle), 1398c2ecf20Sopenharmony_ci text = (visit_kernel_image, pass_handle), 1408c2ecf20Sopenharmony_ci man = (visit_kernel_image, pass_handle), ) 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci # figure handling 1438c2ecf20Sopenharmony_ci app.add_directive("kernel-figure", KernelFigure) 1448c2ecf20Sopenharmony_ci app.add_node(kernel_figure, 1458c2ecf20Sopenharmony_ci html = (visit_kernel_figure, pass_handle), 1468c2ecf20Sopenharmony_ci latex = (visit_kernel_figure, pass_handle), 1478c2ecf20Sopenharmony_ci texinfo = (visit_kernel_figure, pass_handle), 1488c2ecf20Sopenharmony_ci text = (visit_kernel_figure, pass_handle), 1498c2ecf20Sopenharmony_ci man = (visit_kernel_figure, pass_handle), ) 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci # render handling 1528c2ecf20Sopenharmony_ci app.add_directive('kernel-render', KernelRender) 1538c2ecf20Sopenharmony_ci app.add_node(kernel_render, 1548c2ecf20Sopenharmony_ci html = (visit_kernel_render, pass_handle), 1558c2ecf20Sopenharmony_ci latex = (visit_kernel_render, pass_handle), 1568c2ecf20Sopenharmony_ci texinfo = (visit_kernel_render, pass_handle), 1578c2ecf20Sopenharmony_ci text = (visit_kernel_render, pass_handle), 1588c2ecf20Sopenharmony_ci man = (visit_kernel_render, pass_handle), ) 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci app.connect('doctree-read', add_kernel_figure_to_std_domain) 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return dict( 1638c2ecf20Sopenharmony_ci version = __version__, 1648c2ecf20Sopenharmony_ci parallel_read_safe = True, 1658c2ecf20Sopenharmony_ci parallel_write_safe = True 1668c2ecf20Sopenharmony_ci ) 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cidef setupTools(app): 1708c2ecf20Sopenharmony_ci u""" 1718c2ecf20Sopenharmony_ci Check available build tools and log some *verbose* messages. 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci This function is called once, when the builder is initiated. 1748c2ecf20Sopenharmony_ci """ 1758c2ecf20Sopenharmony_ci global dot_cmd, convert_cmd # pylint: disable=W0603 1768c2ecf20Sopenharmony_ci kernellog.verbose(app, "kfigure: check installed tools ...") 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci dot_cmd = which('dot') 1798c2ecf20Sopenharmony_ci convert_cmd = which('convert') 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if dot_cmd: 1828c2ecf20Sopenharmony_ci kernellog.verbose(app, "use dot(1) from: " + dot_cmd) 1838c2ecf20Sopenharmony_ci else: 1848c2ecf20Sopenharmony_ci kernellog.warn(app, "dot(1) not found, for better output quality install " 1858c2ecf20Sopenharmony_ci "graphviz from https://www.graphviz.org") 1868c2ecf20Sopenharmony_ci if convert_cmd: 1878c2ecf20Sopenharmony_ci kernellog.verbose(app, "use convert(1) from: " + convert_cmd) 1888c2ecf20Sopenharmony_ci else: 1898c2ecf20Sopenharmony_ci kernellog.warn(app, 1908c2ecf20Sopenharmony_ci "convert(1) not found, for SVG to PDF conversion install " 1918c2ecf20Sopenharmony_ci "ImageMagick (https://www.imagemagick.org)") 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci# integrate conversion tools 1958c2ecf20Sopenharmony_ci# -------------------------- 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ciRENDER_MARKUP_EXT = { 1988c2ecf20Sopenharmony_ci # The '.ext' must be handled by convert_image(..) function's *in_ext* input. 1998c2ecf20Sopenharmony_ci # <name> : <.ext> 2008c2ecf20Sopenharmony_ci 'DOT' : '.dot', 2018c2ecf20Sopenharmony_ci 'SVG' : '.svg' 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cidef convert_image(img_node, translator, src_fname=None): 2058c2ecf20Sopenharmony_ci """Convert a image node for the builder. 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci Different builder prefer different image formats, e.g. *latex* builder 2088c2ecf20Sopenharmony_ci prefer PDF while *html* builder prefer SVG format for images. 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci This function handles output image formats in dependence of source the 2118c2ecf20Sopenharmony_ci format (of the image) and the translator's output format. 2128c2ecf20Sopenharmony_ci """ 2138c2ecf20Sopenharmony_ci app = translator.builder.app 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci fname, in_ext = path.splitext(path.basename(img_node['uri'])) 2168c2ecf20Sopenharmony_ci if src_fname is None: 2178c2ecf20Sopenharmony_ci src_fname = path.join(translator.builder.srcdir, img_node['uri']) 2188c2ecf20Sopenharmony_ci if not path.exists(src_fname): 2198c2ecf20Sopenharmony_ci src_fname = path.join(translator.builder.outdir, img_node['uri']) 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci dst_fname = None 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci # in kernel builds, use 'make SPHINXOPTS=-v' to see verbose messages 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci kernellog.verbose(app, 'assert best format for: ' + img_node['uri']) 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if in_ext == '.dot': 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if not dot_cmd: 2308c2ecf20Sopenharmony_ci kernellog.verbose(app, 2318c2ecf20Sopenharmony_ci "dot from graphviz not available / include DOT raw.") 2328c2ecf20Sopenharmony_ci img_node.replace_self(file2literal(src_fname)) 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci elif translator.builder.format == 'latex': 2358c2ecf20Sopenharmony_ci dst_fname = path.join(translator.builder.outdir, fname + '.pdf') 2368c2ecf20Sopenharmony_ci img_node['uri'] = fname + '.pdf' 2378c2ecf20Sopenharmony_ci img_node['candidates'] = {'*': fname + '.pdf'} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci elif translator.builder.format == 'html': 2418c2ecf20Sopenharmony_ci dst_fname = path.join( 2428c2ecf20Sopenharmony_ci translator.builder.outdir, 2438c2ecf20Sopenharmony_ci translator.builder.imagedir, 2448c2ecf20Sopenharmony_ci fname + '.svg') 2458c2ecf20Sopenharmony_ci img_node['uri'] = path.join( 2468c2ecf20Sopenharmony_ci translator.builder.imgpath, fname + '.svg') 2478c2ecf20Sopenharmony_ci img_node['candidates'] = { 2488c2ecf20Sopenharmony_ci '*': path.join(translator.builder.imgpath, fname + '.svg')} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci else: 2518c2ecf20Sopenharmony_ci # all other builder formats will include DOT as raw 2528c2ecf20Sopenharmony_ci img_node.replace_self(file2literal(src_fname)) 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci elif in_ext == '.svg': 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if translator.builder.format == 'latex': 2578c2ecf20Sopenharmony_ci if convert_cmd is None: 2588c2ecf20Sopenharmony_ci kernellog.verbose(app, 2598c2ecf20Sopenharmony_ci "no SVG to PDF conversion available / include SVG raw.") 2608c2ecf20Sopenharmony_ci img_node.replace_self(file2literal(src_fname)) 2618c2ecf20Sopenharmony_ci else: 2628c2ecf20Sopenharmony_ci dst_fname = path.join(translator.builder.outdir, fname + '.pdf') 2638c2ecf20Sopenharmony_ci img_node['uri'] = fname + '.pdf' 2648c2ecf20Sopenharmony_ci img_node['candidates'] = {'*': fname + '.pdf'} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if dst_fname: 2678c2ecf20Sopenharmony_ci # the builder needs not to copy one more time, so pop it if exists. 2688c2ecf20Sopenharmony_ci translator.builder.images.pop(img_node['uri'], None) 2698c2ecf20Sopenharmony_ci _name = dst_fname[len(translator.builder.outdir) + 1:] 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if isNewer(dst_fname, src_fname): 2728c2ecf20Sopenharmony_ci kernellog.verbose(app, 2738c2ecf20Sopenharmony_ci "convert: {out}/%s already exists and is newer" % _name) 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci else: 2768c2ecf20Sopenharmony_ci ok = False 2778c2ecf20Sopenharmony_ci mkdir(path.dirname(dst_fname)) 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if in_ext == '.dot': 2808c2ecf20Sopenharmony_ci kernellog.verbose(app, 'convert DOT to: {out}/' + _name) 2818c2ecf20Sopenharmony_ci ok = dot2format(app, src_fname, dst_fname) 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci elif in_ext == '.svg': 2848c2ecf20Sopenharmony_ci kernellog.verbose(app, 'convert SVG to: {out}/' + _name) 2858c2ecf20Sopenharmony_ci ok = svg2pdf(app, src_fname, dst_fname) 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if not ok: 2888c2ecf20Sopenharmony_ci img_node.replace_self(file2literal(src_fname)) 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cidef dot2format(app, dot_fname, out_fname): 2928c2ecf20Sopenharmony_ci """Converts DOT file to ``out_fname`` using ``dot(1)``. 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci * ``dot_fname`` pathname of the input DOT file, including extension ``.dot`` 2958c2ecf20Sopenharmony_ci * ``out_fname`` pathname of the output file, including format extension 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci The *format extension* depends on the ``dot`` command (see ``man dot`` 2988c2ecf20Sopenharmony_ci option ``-Txxx``). Normally you will use one of the following extensions: 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci - ``.ps`` for PostScript, 3018c2ecf20Sopenharmony_ci - ``.svg`` or ``svgz`` for Structured Vector Graphics, 3028c2ecf20Sopenharmony_ci - ``.fig`` for XFIG graphics and 3038c2ecf20Sopenharmony_ci - ``.png`` or ``gif`` for common bitmap graphics. 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci """ 3068c2ecf20Sopenharmony_ci out_format = path.splitext(out_fname)[1][1:] 3078c2ecf20Sopenharmony_ci cmd = [dot_cmd, '-T%s' % out_format, dot_fname] 3088c2ecf20Sopenharmony_ci exit_code = 42 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci with open(out_fname, "w") as out: 3118c2ecf20Sopenharmony_ci exit_code = subprocess.call(cmd, stdout = out) 3128c2ecf20Sopenharmony_ci if exit_code != 0: 3138c2ecf20Sopenharmony_ci kernellog.warn(app, 3148c2ecf20Sopenharmony_ci "Error #%d when calling: %s" % (exit_code, " ".join(cmd))) 3158c2ecf20Sopenharmony_ci return bool(exit_code == 0) 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cidef svg2pdf(app, svg_fname, pdf_fname): 3188c2ecf20Sopenharmony_ci """Converts SVG to PDF with ``convert(1)`` command. 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci Uses ``convert(1)`` from ImageMagick (https://www.imagemagick.org) for 3218c2ecf20Sopenharmony_ci conversion. Returns ``True`` on success and ``False`` if an error occurred. 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci * ``svg_fname`` pathname of the input SVG file with extension (``.svg``) 3248c2ecf20Sopenharmony_ci * ``pdf_name`` pathname of the output PDF file with extension (``.pdf``) 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci """ 3278c2ecf20Sopenharmony_ci cmd = [convert_cmd, svg_fname, pdf_fname] 3288c2ecf20Sopenharmony_ci # use stdout and stderr from parent 3298c2ecf20Sopenharmony_ci exit_code = subprocess.call(cmd) 3308c2ecf20Sopenharmony_ci if exit_code != 0: 3318c2ecf20Sopenharmony_ci kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd))) 3328c2ecf20Sopenharmony_ci return bool(exit_code == 0) 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci# image handling 3368c2ecf20Sopenharmony_ci# --------------------- 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cidef visit_kernel_image(self, node): # pylint: disable=W0613 3398c2ecf20Sopenharmony_ci """Visitor of the ``kernel_image`` Node. 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci Handles the ``image`` child-node with the ``convert_image(...)``. 3428c2ecf20Sopenharmony_ci """ 3438c2ecf20Sopenharmony_ci img_node = node[0] 3448c2ecf20Sopenharmony_ci convert_image(img_node, self) 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ciclass kernel_image(nodes.image): 3478c2ecf20Sopenharmony_ci """Node for ``kernel-image`` directive.""" 3488c2ecf20Sopenharmony_ci pass 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ciclass KernelImage(images.Image): 3518c2ecf20Sopenharmony_ci u"""KernelImage directive 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci Earns everything from ``.. image::`` directive, except *remote URI* and 3548c2ecf20Sopenharmony_ci *glob* pattern. The KernelImage wraps a image node into a 3558c2ecf20Sopenharmony_ci kernel_image node. See ``visit_kernel_image``. 3568c2ecf20Sopenharmony_ci """ 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci def run(self): 3598c2ecf20Sopenharmony_ci uri = self.arguments[0] 3608c2ecf20Sopenharmony_ci if uri.endswith('.*') or uri.find('://') != -1: 3618c2ecf20Sopenharmony_ci raise self.severe( 3628c2ecf20Sopenharmony_ci 'Error in "%s: %s": glob pattern and remote images are not allowed' 3638c2ecf20Sopenharmony_ci % (self.name, uri)) 3648c2ecf20Sopenharmony_ci result = images.Image.run(self) 3658c2ecf20Sopenharmony_ci if len(result) == 2 or isinstance(result[0], nodes.system_message): 3668c2ecf20Sopenharmony_ci return result 3678c2ecf20Sopenharmony_ci (image_node,) = result 3688c2ecf20Sopenharmony_ci # wrap image node into a kernel_image node / see visitors 3698c2ecf20Sopenharmony_ci node = kernel_image('', image_node) 3708c2ecf20Sopenharmony_ci return [node] 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci# figure handling 3738c2ecf20Sopenharmony_ci# --------------------- 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cidef visit_kernel_figure(self, node): # pylint: disable=W0613 3768c2ecf20Sopenharmony_ci """Visitor of the ``kernel_figure`` Node. 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci Handles the ``image`` child-node with the ``convert_image(...)``. 3798c2ecf20Sopenharmony_ci """ 3808c2ecf20Sopenharmony_ci img_node = node[0][0] 3818c2ecf20Sopenharmony_ci convert_image(img_node, self) 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ciclass kernel_figure(nodes.figure): 3848c2ecf20Sopenharmony_ci """Node for ``kernel-figure`` directive.""" 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciclass KernelFigure(Figure): 3878c2ecf20Sopenharmony_ci u"""KernelImage directive 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci Earns everything from ``.. figure::`` directive, except *remote URI* and 3908c2ecf20Sopenharmony_ci *glob* pattern. The KernelFigure wraps a figure node into a kernel_figure 3918c2ecf20Sopenharmony_ci node. See ``visit_kernel_figure``. 3928c2ecf20Sopenharmony_ci """ 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci def run(self): 3958c2ecf20Sopenharmony_ci uri = self.arguments[0] 3968c2ecf20Sopenharmony_ci if uri.endswith('.*') or uri.find('://') != -1: 3978c2ecf20Sopenharmony_ci raise self.severe( 3988c2ecf20Sopenharmony_ci 'Error in "%s: %s":' 3998c2ecf20Sopenharmony_ci ' glob pattern and remote images are not allowed' 4008c2ecf20Sopenharmony_ci % (self.name, uri)) 4018c2ecf20Sopenharmony_ci result = Figure.run(self) 4028c2ecf20Sopenharmony_ci if len(result) == 2 or isinstance(result[0], nodes.system_message): 4038c2ecf20Sopenharmony_ci return result 4048c2ecf20Sopenharmony_ci (figure_node,) = result 4058c2ecf20Sopenharmony_ci # wrap figure node into a kernel_figure node / see visitors 4068c2ecf20Sopenharmony_ci node = kernel_figure('', figure_node) 4078c2ecf20Sopenharmony_ci return [node] 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci# render handling 4118c2ecf20Sopenharmony_ci# --------------------- 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cidef visit_kernel_render(self, node): 4148c2ecf20Sopenharmony_ci """Visitor of the ``kernel_render`` Node. 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci If rendering tools available, save the markup of the ``literal_block`` child 4178c2ecf20Sopenharmony_ci node into a file and replace the ``literal_block`` node with a new created 4188c2ecf20Sopenharmony_ci ``image`` node, pointing to the saved markup file. Afterwards, handle the 4198c2ecf20Sopenharmony_ci image child-node with the ``convert_image(...)``. 4208c2ecf20Sopenharmony_ci """ 4218c2ecf20Sopenharmony_ci app = self.builder.app 4228c2ecf20Sopenharmony_ci srclang = node.get('srclang') 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci kernellog.verbose(app, 'visit kernel-render node lang: "%s"' % (srclang)) 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci tmp_ext = RENDER_MARKUP_EXT.get(srclang, None) 4278c2ecf20Sopenharmony_ci if tmp_ext is None: 4288c2ecf20Sopenharmony_ci kernellog.warn(app, 'kernel-render: "%s" unknown / include raw.' % (srclang)) 4298c2ecf20Sopenharmony_ci return 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if not dot_cmd and tmp_ext == '.dot': 4328c2ecf20Sopenharmony_ci kernellog.verbose(app, "dot from graphviz not available / include raw.") 4338c2ecf20Sopenharmony_ci return 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci literal_block = node[0] 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci code = literal_block.astext() 4388c2ecf20Sopenharmony_ci hashobj = code.encode('utf-8') # str(node.attributes) 4398c2ecf20Sopenharmony_ci fname = path.join('%s-%s' % (srclang, sha1(hashobj).hexdigest())) 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci tmp_fname = path.join( 4428c2ecf20Sopenharmony_ci self.builder.outdir, self.builder.imagedir, fname + tmp_ext) 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if not path.isfile(tmp_fname): 4458c2ecf20Sopenharmony_ci mkdir(path.dirname(tmp_fname)) 4468c2ecf20Sopenharmony_ci with open(tmp_fname, "w") as out: 4478c2ecf20Sopenharmony_ci out.write(code) 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci img_node = nodes.image(node.rawsource, **node.attributes) 4508c2ecf20Sopenharmony_ci img_node['uri'] = path.join(self.builder.imgpath, fname + tmp_ext) 4518c2ecf20Sopenharmony_ci img_node['candidates'] = { 4528c2ecf20Sopenharmony_ci '*': path.join(self.builder.imgpath, fname + tmp_ext)} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci literal_block.replace_self(img_node) 4558c2ecf20Sopenharmony_ci convert_image(img_node, self, tmp_fname) 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ciclass kernel_render(nodes.General, nodes.Inline, nodes.Element): 4598c2ecf20Sopenharmony_ci """Node for ``kernel-render`` directive.""" 4608c2ecf20Sopenharmony_ci pass 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ciclass KernelRender(Figure): 4638c2ecf20Sopenharmony_ci u"""KernelRender directive 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci Render content by external tool. Has all the options known from the 4668c2ecf20Sopenharmony_ci *figure* directive, plus option ``caption``. If ``caption`` has a 4678c2ecf20Sopenharmony_ci value, a figure node with the *caption* is inserted. If not, a image node is 4688c2ecf20Sopenharmony_ci inserted. 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci The KernelRender directive wraps the text of the directive into a 4718c2ecf20Sopenharmony_ci literal_block node and wraps it into a kernel_render node. See 4728c2ecf20Sopenharmony_ci ``visit_kernel_render``. 4738c2ecf20Sopenharmony_ci """ 4748c2ecf20Sopenharmony_ci has_content = True 4758c2ecf20Sopenharmony_ci required_arguments = 1 4768c2ecf20Sopenharmony_ci optional_arguments = 0 4778c2ecf20Sopenharmony_ci final_argument_whitespace = False 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci # earn options from 'figure' 4808c2ecf20Sopenharmony_ci option_spec = Figure.option_spec.copy() 4818c2ecf20Sopenharmony_ci option_spec['caption'] = directives.unchanged 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci def run(self): 4848c2ecf20Sopenharmony_ci return [self.build_node()] 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci def build_node(self): 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci srclang = self.arguments[0].strip() 4898c2ecf20Sopenharmony_ci if srclang not in RENDER_MARKUP_EXT.keys(): 4908c2ecf20Sopenharmony_ci return [self.state_machine.reporter.warning( 4918c2ecf20Sopenharmony_ci 'Unknown source language "%s", use one of: %s.' % ( 4928c2ecf20Sopenharmony_ci srclang, ",".join(RENDER_MARKUP_EXT.keys())), 4938c2ecf20Sopenharmony_ci line=self.lineno)] 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci code = '\n'.join(self.content) 4968c2ecf20Sopenharmony_ci if not code.strip(): 4978c2ecf20Sopenharmony_ci return [self.state_machine.reporter.warning( 4988c2ecf20Sopenharmony_ci 'Ignoring "%s" directive without content.' % ( 4998c2ecf20Sopenharmony_ci self.name), 5008c2ecf20Sopenharmony_ci line=self.lineno)] 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci node = kernel_render() 5038c2ecf20Sopenharmony_ci node['alt'] = self.options.get('alt','') 5048c2ecf20Sopenharmony_ci node['srclang'] = srclang 5058c2ecf20Sopenharmony_ci literal_node = nodes.literal_block(code, code) 5068c2ecf20Sopenharmony_ci node += literal_node 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci caption = self.options.get('caption') 5098c2ecf20Sopenharmony_ci if caption: 5108c2ecf20Sopenharmony_ci # parse caption's content 5118c2ecf20Sopenharmony_ci parsed = nodes.Element() 5128c2ecf20Sopenharmony_ci self.state.nested_parse( 5138c2ecf20Sopenharmony_ci ViewList([caption], source=''), self.content_offset, parsed) 5148c2ecf20Sopenharmony_ci caption_node = nodes.caption( 5158c2ecf20Sopenharmony_ci parsed[0].rawsource, '', *parsed[0].children) 5168c2ecf20Sopenharmony_ci caption_node.source = parsed[0].source 5178c2ecf20Sopenharmony_ci caption_node.line = parsed[0].line 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci figure_node = nodes.figure('', node) 5208c2ecf20Sopenharmony_ci for k,v in self.options.items(): 5218c2ecf20Sopenharmony_ci figure_node[k] = v 5228c2ecf20Sopenharmony_ci figure_node += caption_node 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci node = figure_node 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return node 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cidef add_kernel_figure_to_std_domain(app, doctree): 5298c2ecf20Sopenharmony_ci """Add kernel-figure anchors to 'std' domain. 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci The ``StandardDomain.process_doc(..)`` method does not know how to resolve 5328c2ecf20Sopenharmony_ci the caption (label) of ``kernel-figure`` directive (it only knows about 5338c2ecf20Sopenharmony_ci standard nodes, e.g. table, figure etc.). Without any additional handling 5348c2ecf20Sopenharmony_ci this will result in a 'undefined label' for kernel-figures. 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci This handle adds labels of kernel-figure to the 'std' domain labels. 5378c2ecf20Sopenharmony_ci """ 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci std = app.env.domains["std"] 5408c2ecf20Sopenharmony_ci docname = app.env.docname 5418c2ecf20Sopenharmony_ci labels = std.data["labels"] 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci for name, explicit in iteritems(doctree.nametypes): 5448c2ecf20Sopenharmony_ci if not explicit: 5458c2ecf20Sopenharmony_ci continue 5468c2ecf20Sopenharmony_ci labelid = doctree.nameids[name] 5478c2ecf20Sopenharmony_ci if labelid is None: 5488c2ecf20Sopenharmony_ci continue 5498c2ecf20Sopenharmony_ci node = doctree.ids[labelid] 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if node.tagname == 'kernel_figure': 5528c2ecf20Sopenharmony_ci for n in node.next_node(): 5538c2ecf20Sopenharmony_ci if n.tagname == 'caption': 5548c2ecf20Sopenharmony_ci sectname = clean_astext(n) 5558c2ecf20Sopenharmony_ci # add label to std domain 5568c2ecf20Sopenharmony_ci labels[name] = docname, labelid, sectname 5578c2ecf20Sopenharmony_ci break 558