On Mon, Mar 06, 2017 at 02:09:27PM +0100, Markus Heiser wrote:
> This patch brings scalable figure, image handling and a concept to
> embed *render* markups:
> 
> * DOT (http://www.graphviz.org)
> * SVG
> 
> For image handling use the 'image' replacement::
> 
>     .. kernel-image::  svg_image.svg
>        :alt:    simple SVG image
> 
> For figure handling use the 'figure' replacement::
> 
>     .. kernel-figure::  svg_image.svg
>        :alt:    simple SVG image
> 
>        SVG image example
> 
> Embed *render* markups (or languages) like Graphviz's **DOT** is
> provided by the *render* directive.::
> 
>   .. kernel-render:: DOT
>      :alt: foobar digraph
>      :caption: Embedded **DOT** (Graphviz) code.
> 
>      digraph foo {
>       "bar" -> "baz";
>      }
> 
> The *render* directive is a concept to integrate *render* markups and
> languages, yet supported markups:
> 
> * DOT: render embedded Graphviz's **DOT**
> * SVG: render embedded Scalable Vector Graphics (**SVG**)
> 
> v2: s/DOC/DOT/ in a few places (by Daniel).
> 
> v3: Simplify stuff a bit (by Daniel):
> 
> - Remove path detection and setup/check code for that. In
>   Documentation/media/Makefile we already simply use these tools,
>   better to have one consolidated check if we want/need one. Also
>   remove the convertsvg support, we require ImageMagick's convert
>   already in the doc build, no need for a 2nd fallback.
> 
> - Use sphinx for depency tracking, remove hand-rolled version.
> 
> - Forward stderr from dot and convert, otherwise debugging issues with
>   the diagrams is impossible.
> 
> v4: Only sphinx 1.4 (released in Mar 2016) has patches.Figure.
> Implement Markus suggestion for backwards compatability with earlier
> releases. Laurent reported this, running sphinx 1.3. Solution entirely
> untested.
> 
> v5: Use an explicit version check (suggested by Laurent).
> 
> v6: bugfixes & code review
> * restored some stuff which has been accidentally drop in v3
> * simplified forwarding of stderr and using application warnings to report
> * use relative URIs in HTML <img> (reported by Mauro [1])
> * fixed 'WARNING: undefined label' (reported by Mauro [2])
> 
> [1] https://www.mail-archive.com/linux-doc@vger.kernel.org/msg09828.html
> [2] https://www.mail-archive.com/linux-doc@vger.kernel.org/msg09821.html
> 
> Cc: Jonathan Corbet <cor...@lwn.net>
> Cc: linux-doc@vger.kernel.org
> Cc: Jani Nikula <jani.nik...@linux.intel.com>
> Cc: Mauro Carvalho Chehab <mche...@s-opensource.com>
> Cc: Markus Heiser <markus.hei...@darmarit.de>
> Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
> 
> Signed-off-by: Daniel Vetter <daniel.vet...@intel.com> (v2 - v5)
> Signed-off-by: Markus Heiser <markus.hei...@darmarit.de> (v1, v6)

I've thrown this version at my patch series for drm docs, and it seems to
work. Since I've clearly demonstrated that I shouldn't touch nor review
python code, I'm not going to emberass myself with a review attempt :-)

But it seems to work, and I want it:

Tested-by: Daniel Vetter <daniel.vet...@ffwll.ch>
Acked-by: Daniel Vetter <daniel.vet...@ffwll.ch>
> ---
>  Documentation/conf.py                 |   2 +-
>  Documentation/doc-guide/hello.dot     |   3 +
>  Documentation/doc-guide/sphinx.rst    |  98 +++++-
>  Documentation/doc-guide/svg_image.svg |  10 +
>  Documentation/process/changes.rst     |   7 +-
>  Documentation/sphinx/kfigure.py       | 551 
> ++++++++++++++++++++++++++++++++++
>  6 files changed, 665 insertions(+), 6 deletions(-)
>  create mode 100644 Documentation/doc-guide/hello.dot
>  create mode 100644 Documentation/doc-guide/svg_image.svg
>  create mode 100644 Documentation/sphinx/kfigure.py
> 
> diff --git a/Documentation/conf.py b/Documentation/conf.py
> index f6823cf..e3f537c 100644
> --- a/Documentation/conf.py
> +++ b/Documentation/conf.py
> @@ -34,7 +34,7 @@ from load_config import loadConfig
>  # Add any Sphinx extension module names here, as strings. They can be
>  # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
>  # ones.
> -extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', 'cdomain']
> +extensions = ['kerneldoc', 'rstFlatTable', 'kernel_include', 'cdomain', 
> 'kfigure']
>  
>  # The name of the math extension changed on Sphinx 1.4
>  if major == 1 and minor > 3:
> diff --git a/Documentation/doc-guide/hello.dot 
> b/Documentation/doc-guide/hello.dot
> new file mode 100644
> index 0000000..504621d
> --- /dev/null
> +++ b/Documentation/doc-guide/hello.dot
> @@ -0,0 +1,3 @@
> +graph G {
> +      Hello -- World
> +}
> diff --git a/Documentation/doc-guide/sphinx.rst 
> b/Documentation/doc-guide/sphinx.rst
> index 96fe7ccb..736e7cc 100644
> --- a/Documentation/doc-guide/sphinx.rst
> +++ b/Documentation/doc-guide/sphinx.rst
> @@ -34,8 +34,9 @@ format-specific subdirectories under 
> ``Documentation/output``.
>  
>  To generate documentation, Sphinx (``sphinx-build``) must obviously be
>  installed. For prettier HTML output, the Read the Docs Sphinx theme
> -(``sphinx_rtd_theme``) is used if available. For PDF output, ``rst2pdf`` is 
> also
> -needed. All of these are widely available and packaged in distributions.
> +(``sphinx_rtd_theme``) is used if available. For PDF output you'll also need
> +``XeLaTeX`` and ``convert(1)`` from ImageMagick 
> (https://www.imagemagick.org).
> +All of these are widely available and packaged in distributions.
>  
>  To pass extra options to Sphinx, you can use the ``SPHINXOPTS`` make
>  variable. For example, use ``make SPHINXOPTS=-v htmldocs`` to get more 
> verbose
> @@ -217,3 +218,96 @@ Rendered as:
>        * .. _`last row`:
>  
>          - column 3
> +
> +
> +Figures & Images
> +================
> +
> +If you want to add an image, you should use the ``kernel-figure`` and
> +``kernel-image`` directives. E.g. to insert a figure with a scalable
> +image format use SVG (:ref:`svg_image_example`)::
> +
> +    .. kernel-figure::  svg_image.svg
> +       :alt:    simple SVG image
> +
> +       SVG image example
> +
> +.. _svg_image_example:
> +
> +.. kernel-figure::  svg_image.svg
> +   :alt:    simple SVG image
> +
> +   SVG image example
> +
> +The kernel figure (and image) directive support **DOT** formated files, see
> +
> +* DOT: http://graphviz.org/pdf/dotguide.pdf
> +* Graphviz: http://www.graphviz.org/content/dot-language
> +
> +A simple example (:ref:`hello_dot_file`)::
> +
> +  .. kernel-figure::  hello.dot
> +     :alt:    hello world
> +
> +     DOT's hello world example
> +
> +.. _hello_dot_file:
> +
> +.. kernel-figure::  hello.dot
> +   :alt:    hello world
> +
> +   DOT's hello world example
> +
> +Embed *render* markups (or languages) like Graphviz's **DOT** is provided by 
> the
> +``kernel-render`` directives.::
> +
> +  .. kernel-render:: DOT
> +     :alt: foobar digraph
> +     :caption: Embedded **DOT** (Graphviz) code
> +
> +     digraph foo {
> +      "bar" -> "baz";
> +     }
> +
> +How this will be rendered depends on the installed tools. If Graphviz is
> +installed, you will see an vector image. If not the raw markup is inserted as
> +*literal-block* (:ref:`hello_dot_render`).
> +
> +.. _hello_dot_render:
> +
> +.. kernel-render:: DOT
> +   :alt: foobar digraph
> +   :caption: Embedded **DOT** (Graphviz) code
> +
> +   digraph foo {
> +      "bar" -> "baz";
> +   }
> +
> +The *render* directive has all the options known from the *figure* directive,
> +plus option ``caption``.  If ``caption`` has a value, a *figure* node is
> +inserted. If not, a *image* node is inserted. A ``caption`` is also needed, 
> if
> +you want to refer it (:ref:`hello_svg_render`).
> +
> +Embedded **SVG**::
> +
> +  .. kernel-render:: SVG
> +     :caption: Embedded **SVG** markup
> +     :alt: so-nw-arrow
> +
> +     <?xml version="1.0" encoding="UTF-8"?>
> +     <svg xmlns="http://www.w3.org/2000/svg"; version="1.1" ...>
> +        ...
> +     </svg>
> +
> +.. _hello_svg_render:
> +
> +.. kernel-render:: SVG
> +   :caption: Embedded **SVG** markup
> +   :alt: so-nw-arrow
> +
> +   <?xml version="1.0" encoding="UTF-8"?>
> +   <svg xmlns="http://www.w3.org/2000/svg";
> +     version="1.1" baseProfile="full" width="70px" height="40px" viewBox="0 
> 0 700 400">
> +   <line x1="180" y1="370" x2="500" y2="50" stroke="black" 
> stroke-width="15px"/>
> +   <polygon points="585 0 525 25 585 50" transform="rotate(135 525 25)"/>
> +   </svg>
> diff --git a/Documentation/doc-guide/svg_image.svg 
> b/Documentation/doc-guide/svg_image.svg
> new file mode 100644
> index 0000000..5405f85
> --- /dev/null
> +++ b/Documentation/doc-guide/svg_image.svg
> @@ -0,0 +1,10 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<!-- originate: 
> https://commons.wikimedia.org/wiki/File:Variable_Resistor.svg -->
> +<svg xmlns="http://www.w3.org/2000/svg";
> +     version="1.1" baseProfile="full"
> +     width="70px" height="40px" viewBox="0 0 700 400">
> +     <line x1="0" y1="200" x2="700" y2="200" stroke="black" 
> stroke-width="20px"/>
> +     <rect x="100" y="100" width="500" height="200" fill="white" 
> stroke="black" stroke-width="20px"/>
> +     <line x1="180" y1="370" x2="500" y2="50" stroke="black" 
> stroke-width="15px"/>
> +     <polygon points="585 0 525 25 585 50" transform="rotate(135 525 25)"/>
> +</svg>
> diff --git a/Documentation/process/changes.rst 
> b/Documentation/process/changes.rst
> index 56ce661..e4f2503 100644
> --- a/Documentation/process/changes.rst
> +++ b/Documentation/process/changes.rst
> @@ -318,9 +318,10 @@ PDF outputs, it is recommended to use version 1.4.6.
>  .. note::
>  
>    Please notice that, for PDF and LaTeX output, you'll also need ``XeLaTeX``
> -  version 3.14159265. Depending on the distribution, you may also need
> -  to install a series of ``texlive`` packages that provide the minimal
> -  set of functionalities required for ``XeLaTex`` to work.
> +  version 3.14159265. Depending on the distribution, you may also need to
> +  install a series of ``texlive`` packages that provide the minimal set of
> +  functionalities required for ``XeLaTex`` to work. For PDF output you'll 
> also
> +  need ``convert(1)`` from ImageMagick (https://www.imagemagick.org).
>  
>  Other tools
>  -----------
> diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py
> new file mode 100644
> index 0000000..cef4ad1
> --- /dev/null
> +++ b/Documentation/sphinx/kfigure.py
> @@ -0,0 +1,551 @@
> +# -*- coding: utf-8; mode: python -*-
> +# pylint: disable=C0103, R0903, R0912, R0915
> +u"""
> +    scalable figure and image handling
> +    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +
> +    Sphinx extension which implements scalable image handling.
> +
> +    :copyright:  Copyright (C) 2016  Markus Heiser
> +    :license:    GPL Version 2, June 1991 see Linux/COPYING for details.
> +
> +    The build for image formats depend on image's source format and output's
> +    destination format. This extension implement methods to simplify image
> +    handling from the author's POV. Directives like ``kernel-figure`` 
> implement
> +    methods *to* always get the best output-format even if some tools are not
> +    installed. For more details take a look at ``convert_image(...)`` which 
> is
> +    the core of all conversions.
> +
> +    * ``.. kernel-image``: for image handling / a ``.. image::`` replacement
> +
> +    * ``.. kernel-figure``: for figure handling / a ``.. figure::`` 
> replacement
> +
> +    * ``.. kernel-render``: for render markup / a concept to embed *render*
> +      markups (or languages). Supported markups (see ``RENDER_MARKUP_EXT``)
> +
> +      - ``DOT``: render embedded Graphviz's **DOC**
> +      - ``SVG``: render embedded Scalable Vector Graphics (**SVG**)
> +      - ... *developable*
> +
> +    Used tools:
> +
> +    * ``dot(1)``: Graphviz (http://www.graphviz.org). If Graphviz is not
> +      available, the DOT language is inserted as literal-block.
> +
> +    * SVG to PDF: To generate PDF, you need at least one of this tools:
> +
> +      - ``convert(1)``: ImageMagick (https://www.imagemagick.org)
> +
> +    List of customizations:
> +
> +    * generate PDF from SVG / used by PDF (LaTeX) builder
> +
> +    * generate SVG (html-builder) and PDF (latex-builder) from DOT files.
> +      DOT: see http://www.graphviz.org/content/dot-language
> +
> +    """
> +
> +import os
> +from os import path
> +import subprocess
> +from hashlib import sha1
> +import sys
> +
> +from docutils import nodes
> +from docutils.statemachine import ViewList
> +from docutils.parsers.rst import directives
> +from docutils.parsers.rst.directives import images
> +import sphinx
> +
> +from sphinx.util.nodes import clean_astext
> +from six import iteritems
> +
> +PY3 = sys.version_info[0] == 3
> +
> +if PY3:
> +    _unicode = str
> +else:
> +    _unicode = unicode
> +
> +# Get Sphinx version
> +major, minor, patch = sphinx.version_info[:3]
> +if major == 1 and minor > 3:
> +    # patches.Figure only landed in Sphinx 1.4
> +    from sphinx.directives.patches import Figure  # pylint: disable=C0413
> +else:
> +    Figure = images.Figure
> +
> +__version__  = '1.0.0'
> +
> +# simple helper
> +# -------------
> +
> +def which(cmd):
> +    """Searches the ``cmd`` in the ``PATH`` enviroment.
> +
> +    This *which* searches the PATH for executable ``cmd`` . First match is
> +    returned, if nothing is found, ``None` is returned.
> +    """
> +    envpath = os.environ.get('PATH', None) or os.defpath
> +    for folder in envpath.split(os.pathsep):
> +        fname = folder + os.sep + cmd
> +        if path.isfile(fname):
> +            return fname
> +
> +def mkdir(folder, mode=0o775):
> +    if not path.isdir(folder):
> +        os.makedirs(folder, mode)
> +
> +def file2literal(fname):
> +    with open(fname, "r") as src:
> +        data = src.read()
> +        node = nodes.literal_block(data, data)
> +    return node
> +
> +def isNewer(path1, path2):
> +    """Returns True if ``path1`` is newer than ``path2``
> +
> +    If ``path1`` exists and is newer than ``path2`` the function returns
> +    ``True`` is returned otherwise ``False``
> +    """
> +    return (path.exists(path1)
> +            and os.stat(path1).st_ctime > os.stat(path2).st_ctime)
> +
> +def pass_handle(self, node):           # pylint: disable=W0613
> +    pass
> +
> +# setup conversion tools and sphinx extension
> +# -------------------------------------------
> +
> +# Graphviz's dot(1) support
> +dot_cmd = None
> +
> +# ImageMagick' convert(1) support
> +convert_cmd = None
> +
> +
> +def setup(app):
> +    # check toolchain first
> +    app.connect('builder-inited', setupTools)
> +
> +    # image handling
> +    app.add_directive("kernel-image",  KernelImage)
> +    app.add_node(kernel_image,
> +                 html    = (visit_kernel_image, pass_handle),
> +                 latex   = (visit_kernel_image, pass_handle),
> +                 texinfo = (visit_kernel_image, pass_handle),
> +                 text    = (visit_kernel_image, pass_handle),
> +                 man     = (visit_kernel_image, pass_handle), )
> +
> +    # figure handling
> +    app.add_directive("kernel-figure", KernelFigure)
> +    app.add_node(kernel_figure,
> +                 html    = (visit_kernel_figure, pass_handle),
> +                 latex   = (visit_kernel_figure, pass_handle),
> +                 texinfo = (visit_kernel_figure, pass_handle),
> +                 text    = (visit_kernel_figure, pass_handle),
> +                 man     = (visit_kernel_figure, pass_handle), )
> +
> +    # render handling
> +    app.add_directive('kernel-render', KernelRender)
> +    app.add_node(kernel_render,
> +                 html    = (visit_kernel_render, pass_handle),
> +                 latex   = (visit_kernel_render, pass_handle),
> +                 texinfo = (visit_kernel_render, pass_handle),
> +                 text    = (visit_kernel_render, pass_handle),
> +                 man     = (visit_kernel_render, pass_handle), )
> +
> +    app.connect('doctree-read', add_kernel_figure_to_std_domain)
> +
> +    return dict(
> +        version = __version__,
> +        parallel_read_safe = True,
> +        parallel_write_safe = True
> +    )
> +
> +
> +def setupTools(app):
> +    u"""
> +    Check available build tools and log some *verbose* messages.
> +
> +    This function is called once, when the builder is initiated.
> +    """
> +    global dot_cmd, convert_cmd   # pylint: disable=W0603
> +    app.verbose("kfigure: check installed tools ...")
> +
> +    dot_cmd = which('dot')
> +    convert_cmd = which('convert')
> +
> +    if dot_cmd:
> +        app.verbose("use dot(1) from: " + dot_cmd)
> +    else:
> +        app.warn("dot(1) not found, for better output quality install "
> +                 "graphviz from http://www.graphviz.org";)
> +    if convert_cmd:
> +        app.verbose("use convert(1) from: " + convert_cmd)
> +    else:
> +        app.warn(
> +            "convert(1) not found, for SVG to PDF conversion install "
> +            "ImageMagick (https://www.imagemagick.org)")
> +
> +
> +# integrate conversion tools
> +# --------------------------
> +
> +RENDER_MARKUP_EXT = {
> +    # The '.ext' must be handled by convert_image(..) function's *in_ext* 
> input.
> +    # <name> : <.ext>
> +    'DOT' : '.dot',
> +    'SVG' : '.svg'
> +}
> +
> +def convert_image(img_node, translator, src_fname=None):
> +    """Convert a image node for the builder.
> +
> +    Different builder prefer different image formats, e.g. *latex* builder
> +    prefer PDF while *html* builder prefer SVG format for images.
> +
> +    This function handles output image formats in dependence of source the
> +    format (of the image) and the translator's output format.
> +    """
> +    app = translator.builder.app
> +
> +    fname, in_ext = path.splitext(path.basename(img_node['uri']))
> +    if src_fname is None:
> +        src_fname = path.join(translator.builder.srcdir, img_node['uri'])
> +        if not path.exists(src_fname):
> +            src_fname = path.join(translator.builder.outdir, img_node['uri'])
> +
> +    dst_fname = None
> +
> +    # in kernel builds, use 'make SPHINXOPTS=-v' to see verbose messages
> +
> +    app.verbose('assert best format for: ' + img_node['uri'])
> +
> +    if in_ext == '.dot':
> +
> +        if not dot_cmd:
> +            app.verbose("dot from graphviz not available / include DOT raw.")
> +            img_node.replace_self(file2literal(src_fname))
> +
> +        elif translator.builder.format == 'latex':
> +            dst_fname = path.join(translator.builder.outdir, fname + '.pdf')
> +            img_node['uri'] = fname + '.pdf'
> +            img_node['candidates'] = {'*': fname + '.pdf'}
> +
> +
> +        elif translator.builder.format == 'html':
> +            dst_fname = path.join(
> +                translator.builder.outdir,
> +                translator.builder.imagedir,
> +                fname + '.svg')
> +            img_node['uri'] = path.join(
> +                translator.builder.imgpath, fname + '.svg')
> +            img_node['candidates'] = {
> +                '*': path.join(translator.builder.imgpath, fname + '.svg')}
> +
> +        else:
> +            # all other builder formats will include DOT as raw
> +            img_node.replace_self(file2literal(src_fname))
> +
> +    elif in_ext == '.svg':
> +
> +        if translator.builder.format == 'latex':
> +            if convert_cmd is None:
> +                app.verbose("no SVG to PDF conversion available / include 
> SVG raw.")
> +                img_node.replace_self(file2literal(src_fname))
> +            else:
> +                dst_fname = path.join(translator.builder.outdir, fname + 
> '.pdf')
> +                img_node['uri'] = fname + '.pdf'
> +                img_node['candidates'] = {'*': fname + '.pdf'}
> +
> +    if dst_fname:
> +        # the builder needs not to copy one more time, so pop it if exists.
> +        translator.builder.images.pop(img_node['uri'], None)
> +        _name = dst_fname[len(translator.builder.outdir) + 1:]
> +
> +        if isNewer(dst_fname, src_fname):
> +            app.verbose("convert: {out}/%s already exists and is newer" % 
> _name)
> +
> +        else:
> +            ok = False
> +            mkdir(path.dirname(dst_fname))
> +
> +            if in_ext == '.dot':
> +                app.verbose('convert DOT to: {out}/' + _name)
> +                ok = dot2format(app, src_fname, dst_fname)
> +
> +            elif in_ext == '.svg':
> +                app.verbose('convert SVG to: {out}/' + _name)
> +                ok = svg2pdf(app, src_fname, dst_fname)
> +
> +            if not ok:
> +                img_node.replace_self(file2literal(src_fname))
> +
> +
> +def dot2format(app, dot_fname, out_fname):
> +    """Converts DOT file to ``out_fname`` using ``dot(1)``.
> +
> +    * ``dot_fname`` pathname of the input DOT file, including extension 
> ``.dot``
> +    * ``out_fname`` pathname of the output file, including format extension
> +
> +    The *format extension* depends on the ``dot`` command (see ``man dot``
> +    option ``-Txxx``). Normally you will use one of the following extensions:
> +
> +    - ``.ps`` for PostScript,
> +    - ``.svg`` or ``svgz`` for Structured Vector Graphics,
> +    - ``.fig`` for XFIG graphics and
> +    - ``.png`` or ``gif`` for common bitmap graphics.
> +
> +    """
> +    out_format = path.splitext(out_fname)[1][1:]
> +    cmd = [dot_cmd, '-T%s' % out_format, dot_fname]
> +    exit_code = 42
> +
> +    with open(out_fname, "w") as out:
> +        exit_code = subprocess.call(cmd, stdout = out)
> +        if exit_code != 0:
> +            app.warn("Error #%d when calling: %s" % (exit_code, " 
> ".join(cmd)))
> +    return bool(exit_code == 0)
> +
> +def svg2pdf(app, svg_fname, pdf_fname):
> +    """Converts SVG to PDF with ``convert(1)`` command.
> +
> +    Uses ``convert(1)`` from ImageMagick (https://www.imagemagick.org) for
> +    conversion.  Returns ``True`` on success and ``False`` if an error 
> occurred.
> +
> +    * ``svg_fname`` pathname of the input SVG file with extension (``.svg``)
> +    * ``pdf_name``  pathname of the output PDF file with extension (``.pdf``)
> +
> +    """
> +    cmd = [convert_cmd, svg_fname, pdf_fname]
> +    # use stdout and stderr from parent
> +    exit_code = subprocess.call(cmd)
> +    if exit_code != 0:
> +        app.warn("Error #%d when calling: %s" % (exit_code, " ".join(cmd)))
> +    return bool(exit_code == 0)
> +
> +
> +# image handling
> +# ---------------------
> +
> +def visit_kernel_image(self, node):    # pylint: disable=W0613
> +    """Visitor of the ``kernel_image`` Node.
> +
> +    Handles the ``image`` child-node with the ``convert_image(...)``.
> +    """
> +    img_node = node[0]
> +    convert_image(img_node, self)
> +
> +class kernel_image(nodes.image):
> +    """Node for ``kernel-image`` directive."""
> +    pass
> +
> +class KernelImage(images.Image):
> +    u"""KernelImage directive
> +
> +    Earns everything from ``.. image::`` directive, except *remote URI* and
> +    *glob* pattern. The KernelImage wraps a image node into a
> +    kernel_image node. See ``visit_kernel_image``.
> +    """
> +
> +    def run(self):
> +        uri = self.arguments[0]
> +        if uri.endswith('.*') or uri.find('://') != -1:
> +            raise self.severe(
> +                'Error in "%s: %s": glob pattern and remote images are not 
> allowed'
> +                % (self.name, uri))
> +        result = images.Image.run(self)
> +        if len(result) == 2 or isinstance(result[0], nodes.system_message):
> +            return result
> +        (image_node,) = result
> +        # wrap image node into a kernel_image node / see visitors
> +        node = kernel_image('', image_node)
> +        return [node]
> +
> +# figure handling
> +# ---------------------
> +
> +def visit_kernel_figure(self, node):   # pylint: disable=W0613
> +    """Visitor of the ``kernel_figure`` Node.
> +
> +    Handles the ``image`` child-node with the ``convert_image(...)``.
> +    """
> +    img_node = node[0][0]
> +    convert_image(img_node, self)
> +
> +class kernel_figure(nodes.figure):
> +    """Node for ``kernel-figure`` directive."""
> +
> +class KernelFigure(Figure):
> +    u"""KernelImage directive
> +
> +    Earns everything from ``.. figure::`` directive, except *remote URI* and
> +    *glob* pattern.  The KernelFigure wraps a figure node into a 
> kernel_figure
> +    node. See ``visit_kernel_figure``.
> +    """
> +
> +    def run(self):
> +        uri = self.arguments[0]
> +        if uri.endswith('.*') or uri.find('://') != -1:
> +            raise self.severe(
> +                'Error in "%s: %s":'
> +                ' glob pattern and remote images are not allowed'
> +                % (self.name, uri))
> +        result = Figure.run(self)
> +        if len(result) == 2 or isinstance(result[0], nodes.system_message):
> +            return result
> +        (figure_node,) = result
> +        # wrap figure node into a kernel_figure node / see visitors
> +        node = kernel_figure('', figure_node)
> +        return [node]
> +
> +
> +# render handling
> +# ---------------------
> +
> +def visit_kernel_render(self, node):
> +    """Visitor of the ``kernel_render`` Node.
> +
> +    If rendering tools available, save the markup of the ``literal_block`` 
> child
> +    node into a file and replace the ``literal_block`` node with a new 
> created
> +    ``image`` node, pointing to the saved markup file. Afterwards, handle the
> +    image child-node with the ``convert_image(...)``.
> +    """
> +    app = self.builder.app
> +    srclang = node.get('srclang')
> +
> +    app.verbose('visit kernel-render node lang: "%s"' % (srclang))
> +
> +    tmp_ext = RENDER_MARKUP_EXT.get(srclang, None)
> +    if tmp_ext is None:
> +        app.warn('kernel-render: "%s" unknow / include raw.' % (srclang))
> +        return
> +
> +    if not dot_cmd and tmp_ext == '.dot':
> +        app.verbose("dot from graphviz not available / include raw.")
> +        return
> +
> +    literal_block = node[0]
> +
> +    code      = literal_block.astext()
> +    hashobj   = code.encode('utf-8') #  str(node.attributes)
> +    fname     = path.join('%s-%s' % (srclang, sha1(hashobj).hexdigest()))
> +
> +    tmp_fname = path.join(
> +        self.builder.outdir, self.builder.imagedir, fname + tmp_ext)
> +
> +    if not path.isfile(tmp_fname):
> +        mkdir(path.dirname(tmp_fname))
> +        with open(tmp_fname, "w") as out:
> +            out.write(code)
> +
> +    img_node = nodes.image(node.rawsource, **node.attributes)
> +    img_node['uri'] = path.join(self.builder.imgpath, fname + tmp_ext)
> +    img_node['candidates'] = {
> +        '*': path.join(self.builder.imgpath, fname + tmp_ext)}
> +
> +    literal_block.replace_self(img_node)
> +    convert_image(img_node, self, tmp_fname)
> +
> +
> +class kernel_render(nodes.General, nodes.Inline, nodes.Element):
> +    """Node for ``kernel-render`` directive."""
> +    pass
> +
> +class KernelRender(Figure):
> +    u"""KernelRender directive
> +
> +    Render content by external tool.  Has all the options known from the
> +    *figure*  directive, plus option ``caption``.  If ``caption`` has a
> +    value, a figure node with the *caption* is inserted. If not, a image 
> node is
> +    inserted.
> +
> +    The KernelRender directive wraps the text of the directive into a
> +    literal_block node and wraps it into a kernel_render node. See
> +    ``visit_kernel_render``.
> +    """
> +    has_content = True
> +    required_arguments = 1
> +    optional_arguments = 0
> +    final_argument_whitespace = False
> +
> +    # earn options from 'figure'
> +    option_spec = Figure.option_spec.copy()
> +    option_spec['caption'] = directives.unchanged
> +
> +    def run(self):
> +        return [self.build_node()]
> +
> +    def build_node(self):
> +
> +        srclang = self.arguments[0].strip()
> +        if srclang not in RENDER_MARKUP_EXT.keys():
> +            return [self.state_machine.reporter.warning(
> +                'Unknow source language "%s", use one of: %s.' % (
> +                    srclang, ",".join(RENDER_MARKUP_EXT.keys())),
> +                line=self.lineno)]
> +
> +        code = '\n'.join(self.content)
> +        if not code.strip():
> +            return [self.state_machine.reporter.warning(
> +                'Ignoring "%s" directive without content.' % (
> +                    self.name),
> +                line=self.lineno)]
> +
> +        node = kernel_render()
> +        node['alt'] = self.options.get('alt','')
> +        node['srclang'] = srclang
> +        literal_node = nodes.literal_block(code, code)
> +        node += literal_node
> +
> +        caption = self.options.get('caption')
> +        if caption:
> +            # parse caption's content
> +            parsed = nodes.Element()
> +            self.state.nested_parse(
> +                ViewList([caption], source=''), self.content_offset, parsed)
> +            caption_node = nodes.caption(
> +                parsed[0].rawsource, '', *parsed[0].children)
> +            caption_node.source = parsed[0].source
> +            caption_node.line = parsed[0].line
> +
> +            figure_node = nodes.figure('', node)
> +            for k,v in self.options.items():
> +                figure_node[k] = v
> +            figure_node += caption_node
> +
> +            node = figure_node
> +
> +        return node
> +
> +def add_kernel_figure_to_std_domain(app, doctree):
> +    """Add kernel-figure anchors to 'std' domain.
> +
> +    The ``StandardDomain.process_doc(..)`` method does not know how to 
> resolve
> +    the caption (label) of ``kernel-figure`` directive (it only knows about
> +    standard nodes, e.g. table, figure etc.). Without any additional handling
> +    this will result in a 'undefined label' for kernel-figures.
> +
> +    This handle adds labels of kernel-figure to the 'std' domain labels.
> +    """
> +
> +    std = app.env.domains["std"]
> +    docname = app.env.docname
> +    labels = std.data["labels"]
> +
> +    for name, explicit in iteritems(doctree.nametypes):
> +        if not explicit:
> +            continue
> +        labelid = doctree.nameids[name]
> +        if labelid is None:
> +            continue
> +        node = doctree.ids[labelid]
> +
> +        if node.tagname == 'kernel_figure':
> +            for n in node.next_node():
> +                if n.tagname == 'caption':
> +                    sectname = clean_astext(n)
> +                    # add label to std domain
> +                    labels[name] = docname, labelid, sectname
> +                    break
> -- 
> 2.7.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-doc" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
--
To unsubscribe from this list: send the line "unsubscribe linux-doc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to