Ugh, this blew up again. Three days to get here.
- Math rendered as embedded SVG instead of raster images / MathJax. The
supported feature set is equivalent to the `m.math Pelican plugin <{filename}/plugins/math-and-code.rst#math>`_,
see its documentation for more information.
+- Graphviz / Dot diagrams rendered as embedded SVG. The supported feature set
+ is equivalent to the `m.dot Pelican plugin <{filename}/plugins/plots-and-graphs.rst#graphs>`_,
+ see its documentation for more information.
- Uses Pygments for better code highlighting. The supported feature set is
equivalent to the `m.code Pelican plugin <{filename}/plugins/math-and-code.rst#code>`_,
see its documentation for more information.
searched relative to the Doxyfile base dir and
to the ``dox2html5.py`` script dir as a
fallback.
+:ini:`DOT_FONTNAME` Font name to use for ``@dot`` and ``@dotfile``
+ commands. To ensure consistent look with the
+ default m.css themes, set it to
+ ``Source Sans Pro``. Doxygen default is
+ ``Helvetica``.
+:ini:`DOT_FONTSIZE` Font size to use for ``@dot`` and ``@dotfile``
+ commands. To ensure consistent look with the
+ default m.css themes, set it to ``16``.
+ Doxygen default is ``10``.
=============================== ===============================================
In addition, the m.css Doxygen theme recognizes the following extra options:
@image image.png width=250px
*/
+`Dot graphs`_
+-------------
+
+Grapviz ``dot`` graphs from the ``@dot`` and ``@dotfile`` commands are rendered
+as an inline SVG. Graph name and the ``sizespec`` works equivalently to the
+`Images and figures`_.
+
`Pages, sections and table of contents`_
----------------------------------------
from pygments.lexers import TextLexer, BashSessionLexer, get_lexer_by_name, find_lexer_class_for_filename
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../pelican-plugins'))
+import dot2svg
import latex2svg
import latex2svgextra
import ansilexer
# - <verbatim>, <preformatted> (those are the same thing!)
# - <parblock> (a weird grouping thing that we abuse for <div>s)
# - <variablelist>, <itemizedlist>, <orderedlist>
- # - <image>, <table>
+ # - <image>, <dot>, <dotfile>, <table>
# - <mcss:div>
# - <formula> (if block)
# - <programlisting> (if block)
end_previous_paragraph = False
# Straightforward elements
- if i.tag in ['heading', 'blockquote', 'hruler', 'xrefsect', 'variablelist', 'verbatim', 'parblock', 'preformatted', 'itemizedlist', 'orderedlist', 'image', 'table', '{http://mcss.mosra.cz/doxygen/}div']:
+ if i.tag in ['heading', 'blockquote', 'hruler', 'xrefsect', 'variablelist', 'verbatim', 'parblock', 'preformatted', 'itemizedlist', 'orderedlist', 'image', 'dot', 'dotfile', 'table', '{http://mcss.mosra.cz/doxygen/}div']:
end_previous_paragraph = True
# <simplesect> describing return type is cut out of text flow, so
out.parsed += '<img class="m-image{}" src="{}" alt="Image"{} />'.format(
' ' + add_css_class if add_css_class else '', name, sizespec)
+ elif i.tag in ['dot', 'dotfile']:
+ # can be in <para> but often also in <div> and other m.css-specific
+ # elements
+ has_block_elements = True
+
+ # Why the heck can't it just read the file and paste it into the
+ # XML?!
+ caption = None
+ if i.tag == 'dotfile':
+ if 'name' in i.attrib:
+ with open(i.attrib['name'], 'r') as f:
+ source = f.read()
+ else:
+ logging.warning("{}: file passed to @dotfile was not found, rendering an empty graph")
+ source = 'digraph "" {}'
+ caption = i.text
+ else:
+ source = i.text
+ if 'caption' in i.attrib: caption = i.attrib['caption']
+
+ size = None
+ if 'width' in i.attrib:
+ size = 'width: {};'.format(i.attrib['width'])
+ elif 'height' in i.attrib:
+ size = 'height: {};'.format(i.attrib['height'])
+
+ if caption:
+ out.parsed += '<figure class="m-figure">{}<figcaption>{}</figcaption></figure>'.format(dot2svg.dot2svg(
+ source, size=size,
+ attribs=' class="m-graph{}"'.format(' ' + add_css_class if add_css_class else '')),
+ caption)
+ else:
+ out.parsed += '<div class="m-graph{}">{}</div>'.format(
+ ' ' + add_css_class if add_css_class else '', dot2svg.dot2svg(source, size))
+
elif i.tag == 'hruler':
assert element.tag == 'para' # is inside a paragraph :/
out.parsed += '<hr/>'
'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600',
'../css/m-dark+doxygen.compiled.css'],
'HTML_EXTRA_FILES': [],
+ 'DOT_FONTNAME': ['Helvetica'],
+ 'DOT_FONTSIZE': ['10'],
'M_CLASS_TREE_EXPAND_LEVELS': ['1'],
'M_FILE_TREE_EXPAND_LEVELS': ['1'],
'OUTPUT_DIRECTORY',
'HTML_OUTPUT',
'XML_OUTPUT',
+ 'DOT_FONTNAME',
'M_MAIN_PROJECT_URL',
'M_HTML_HEADER',
'M_PAGE_HEADER',
if i in config: state.doxyfile[i] = '\n'.join(config[i])
# Int values that we want
- for i in ['M_CLASS_TREE_EXPAND_LEVELS',
+ for i in ['DOT_FONTSIZE',
+ 'M_CLASS_TREE_EXPAND_LEVELS',
'M_FILE_TREE_EXPAND_LEVELS']:
if i in config: state.doxyfile[i] = int(' '.join(config[i]))
else:
latex2svgextra.unpickle_cache(None)
+ # Configure graphviz/dot
+ dot2svg.configure(state.doxyfile['DOT_FONTNAME'], state.doxyfile['DOT_FONTSIZE'])
+
if sort_globbed_files:
xml_files_metadata.sort()
xml_files.sort()
GENERATE_XML = YES
XML_PROGRAMLISTING = NO
+DOTFILE_DIRS = .
+DOT_FONTNAME = DejaVu Sans
+DOT_FONTSIZE = 16
+
##! M_PAGE_FINE_PRINT =
##! M_THEME_COLOR =
##! M_FAVICON =
--- /dev/null
+strict graph "" {
+ a -- b
+ a -- b
+}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Dot | My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ Dot
+ </h1>
+<p>A red graph:</p><div class="m-graph m-danger"><svg style="width: 3.875rem; height: 7.500rem;" viewBox="0.00 0.00 62.00 120.00">
+<g transform="scale(1 1) rotate(0) translate(4 116)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-93" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-89.2">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-19" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-15.2">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-74.5708C27,-63.3339 27,-48.7727 27,-37.5182"/>
+</g>
+</g>
+</svg>
+</div><p>A blue graph, from a file:</p><div class="m-graph m-info"><svg style="width: 3.875rem; height: 7.500rem;" viewBox="0.00 0.00 62.00 120.00">
+<g transform="scale(1 1) rotate(0) translate(4 116)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-93" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-89.2">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-19" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-15.2">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-74.5708C27,-63.3339 27,-48.7727 27,-37.5182"/>
+</g>
+</g>
+</svg>
+</div>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Dot | My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ Dot
+ </h1>
+<p>A red graph:</p><div class="m-graph m-danger"><svg style="width: 3.875rem; height: 7.375rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.539)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.6607C27,-61.8548 27,-48.0281 27,-37.1687"/>
+</g>
+</g>
+</svg>
+</div><p>A blue graph, from a file:</p><div class="m-graph m-info"><svg style="width: 3.875rem; height: 7.375rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.539)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.6607C27,-61.8548 27,-48.0281 27,-37.1687"/>
+</g>
+</g>
+</svg>
+</div>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>Dot | My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ Dot
+ </h1>
+<p>A red graph:</p><div class="m-graph m-danger"><svg style="width: 3.875rem; height: 7.375rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.5391)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.4144C27,-61.4654 27,-47.7036 27,-36.8093"/>
+</g>
+</g>
+</svg>
+</div><p>A blue graph, from a file:</p><div class="m-graph m-info"><svg style="width: 3.875rem; height: 7.375rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.5391)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.4144C27,-61.4654 27,-47.7036 27,-36.8093"/>
+</g>
+</g>
+</svg>
+</div>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
A yellow @m_class{m-warning} @f$ \Sigma @f$ inline formula.
*/
+
+/** @page dot Dot
+
+A red graph:
+
+@m_class{m-danger}
+
+@dot
+strict graph "" {
+ a -- b
+ a -- b
+}
+@enddot
+
+A blue graph, from a file:
+
+@m_class{m-info}
+
+@dotfile ab.dot
+*/
--- /dev/null
+INPUT = input.dox
+QUIET = YES
+GENERATE_HTML = NO
+GENERATE_LATEX = NO
+GENERATE_XML = YES
+XML_PROGRAMLISTING = NO
+
+DOTFILE_DIRS = .
+DOT_FONTNAME = DejaVu Sans
+DOT_FONTSIZE = 16
+
+##! M_PAGE_FINE_PRINT =
+##! M_THEME_COLOR =
+##! M_FAVICON =
+##! M_LINKS_NAVBAR1 =
+##! M_LINKS_NAVBAR2 =
+##! M_SEARCH_DISABLED = YES
--- /dev/null
+strict graph "" {
+ a -- b
+ a -- b
+}
--- /dev/null
+digraph Colors {
+ a [class="m-success"]
+ b [style=filled shape=circle class="m-dim"]
+ a -> b [class="m-warning" label="yes"]
+ b -> b [class="m-primary" label="no"]
+}
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ My Project
+ </h1>
+<p>Note: the test uses DejaVu Sans instead of Source Sans Pro in order to have predictable rendering on the CIs.</p><div class="m-graph"><svg style="width: 16.375rem; height: 5.250rem;" viewBox="0.00 0.00 262.00 84.00">
+<g transform="scale(1 1) rotate(0) translate(4 80)">
+<title>Basics</title>
+<g class="m-node">
+<title>a</title>
+<polygon points="54,-40 0,-40 0,-4 54,-4 54,-40"/>
+<text text-anchor="middle" x="27" y="-18.2">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="115" cy="-22" rx="18.2906" ry="18.2906"/>
+<ellipse cx="115" cy="-22" rx="22.2701" ry="22.2701"/>
+<text text-anchor="middle" x="115" y="-18.2">b</text>
+</g>
+<g class="m-edge">
+<title>a->b</title>
+<path d="M54.2701,-22C63.1708,-22 73.2044,-22 82.4712,-22"/>
+<polygon points="82.5066,-25.5001 92.5066,-22 82.5065,-18.5001 82.5066,-25.5001"/>
+</g>
+<g class="m-node m-flat">
+<title>c</title>
+<ellipse cx="227" cy="-22" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="227" y="-18.2">c</text>
+</g>
+<g class="m-edge">
+<title>b->c</title>
+<path d="M137.47,-22C152.279,-22 172.331,-22 189.606,-22"/>
+<polygon points="189.933,-25.5001 199.933,-22 189.933,-18.5001 189.933,-25.5001"/>
+<text text-anchor="middle" x="169" y="-34" style="font-size: 40.0px;">0</text>
+</g>
+<g class="m-edge">
+<title>c->c</title>
+<path d="M216.722,-39.0373C214.625,-48.8579 218.051,-58 227,-58 232.593,-58 236.029,-54.4289 237.307,-49.3529"/>
+<polygon points="240.806,-49.0272 237.278,-39.0373 233.806,-49.0473 240.806,-49.0272"/>
+<text text-anchor="middle" x="227" y="-63.2">1</text>
+</g>
+</g>
+</svg>
+</div><div class="m-graph"><svg style="width: 5.750rem; height: 8.625rem;" viewBox="0.00 0.00 92.00 138.00">
+<g transform="scale(1 1) rotate(0) translate(4 134)">
+<title>Colors</title>
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-111" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-107.2">a</text>
+</g>
+<g class="m-node">
+<title>b</title>
+<ellipse cx="27" cy="-19" rx="18.0791" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-15.2">b</text>
+</g>
+<g class="m-edge">
+<title>a->b</title>
+<path d="M27,-92.2189C27,-79.564 27,-62.3088 27,-47.8384"/>
+<polygon points="30.5001,-47.4379 27,-37.4379 23.5001,-47.438 30.5001,-47.4379"/>
+<text text-anchor="middle" x="41" y="-61.2">yes</text>
+</g>
+<g class="m-edge">
+<title>b->b</title>
+<path d="M43.6641,-27.0859C53.625,-28.8828 63,-26.1875 63,-19 63,-14.3955 59.1525,-11.6346 53.7682,-10.7173"/>
+<polygon points="53.594,-7.21994 43.6641,-10.9141 53.7303,-14.2186 53.594,-7.21994"/>
+<text text-anchor="middle" x="73.5" y="-15.2">no</text>
+</g>
+</g>
+</svg>
+</div><figure class="m-figure"><svg class="m-graph" style="width: 5rem;" viewBox="0.00 0.00 62.00 120.00">
+<g transform="scale(1 1) rotate(0) translate(4 116)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-93" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-89.2">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-19" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-15.2">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-74.5708C27,-63.3339 27,-48.7727 27,-37.5182"/>
+</g>
+</g>
+</svg>
+<figcaption>A graph</figcaption></figure><figure class="m-figure"><svg class="m-graph" style="height: 5rem;" viewBox="0.00 0.00 62.00 120.00">
+<g transform="scale(1 1) rotate(0) translate(4 116)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-93" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-89.2">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-19" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-15.2">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-74.5708C27,-63.3339 27,-48.7727 27,-37.5182"/>
+</g>
+</g>
+</svg>
+<figcaption>A graph</figcaption></figure>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ My Project
+ </h1>
+<p>Note: the test uses DejaVu Sans instead of Source Sans Pro in order to have predictable rendering on the CIs.</p><div class="m-graph"><svg style="width: 16.250rem; height: 5.312rem;" viewBox="0.00 0.00 259.77 84.77">
+<g transform="scale(1 1) rotate(0) translate(4 80.7696)">
+<title>Basics</title>
+<g class="m-node">
+<title>a</title>
+<polygon points="54,-40.3848 0,-40.3848 0,-4.38478 54,-4.38478 54,-40.3848"/>
+<text text-anchor="middle" x="27" y="-18.5848">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="113.385" cy="-22.3848" rx="18.2906" ry="18.2906"/>
+<ellipse cx="113.385" cy="-22.3848" rx="22.2701" ry="22.2701"/>
+<text text-anchor="middle" x="113.385" y="-18.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a->b</title>
+<path d="M54.2373,-22.3848C62.63,-22.3848 72.0104,-22.3848 80.7459,-22.3848"/>
+<polygon points="80.9716,-25.8849 90.9716,-22.3848 80.9716,-18.8849 80.9716,-25.8849"/>
+</g>
+<g class="m-node m-flat">
+<title>c</title>
+<ellipse cx="224.77" cy="-22.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="224.77" y="-18.5848">c</text>
+</g>
+<g class="m-edge">
+<title>b->c</title>
+<path d="M135.996,-22.3848C150.639,-22.3848 170.35,-22.3848 187.383,-22.3848"/>
+<polygon points="187.574,-25.8849 197.574,-22.3848 187.573,-18.8849 187.574,-25.8849"/>
+<text text-anchor="middle" x="166.77" y="-34.3848" style="font-size: 40.0px;">0</text>
+</g>
+<g class="m-edge">
+<title>c->c</title>
+<path d="M214.492,-39.6042C212.394,-49.5298 215.82,-58.7696 224.77,-58.7696 230.503,-58.7696 233.969,-54.9775 235.168,-49.6425"/>
+<polygon points="238.668,-49.5611 235.047,-39.6042 231.668,-49.6458 238.668,-49.5611"/>
+<text text-anchor="middle" x="224.77" y="-63.9696">1</text>
+</g>
+</g>
+</svg>
+</div><div class="m-graph"><svg style="width: 5.750rem; height: 8.500rem;" viewBox="0.00 0.00 92.38 135.54">
+<g transform="scale(1 1) rotate(0) translate(4 131.539)">
+<title>Colors</title>
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-109.154" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-105.354">a</text>
+</g>
+<g class="m-node">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="18.2703" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a->b</title>
+<path d="M27,-90.6163C27,-78.2149 27,-61.3346 27,-47.1192"/>
+<polygon points="30.5001,-46.8863 27,-36.8864 23.5001,-46.8864 30.5001,-46.8863"/>
+<text text-anchor="middle" x="41" y="-59.9696">yes</text>
+</g>
+<g class="m-edge">
+<title>b->b</title>
+<path d="M43.8422,-26.1192C53.9096,-27.8379 63.3848,-25.2598 63.3848,-18.3848 63.3848,-13.9805 59.4961,-11.3396 54.0544,-10.4622"/>
+<polygon points="53.776,-6.96664 43.8422,-10.6504 53.905,-13.9655 53.776,-6.96664"/>
+<text text-anchor="middle" x="73.8848" y="-14.5848">no</text>
+</g>
+</g>
+</svg>
+</div>
+<figure class="m-figure"><svg class="m-graph" style="width: 5rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.539)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.6607C27,-61.8548 27,-48.0281 27,-37.1687"/>
+</g>
+</g>
+</svg>
+<figcaption>A graph</figcaption></figure><figure class="m-figure"><svg class="m-graph" style="height: 5rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.539)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.6607C27,-61.8548 27,-48.0281 27,-37.1687"/>
+</g>
+</g>
+</svg>
+<figcaption>A graph</figcaption></figure>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ My Project
+ </h1>
+<p>Note: the test uses DejaVu Sans instead of Source Sans Pro in order to have predictable rendering on the CIs.</p><div class="m-graph"><svg style="width: 16.250rem; height: 5.312rem;" viewBox="0.00 0.00 259.77 84.77">
+<g transform="scale(1 1) rotate(0) translate(4 80.7696)">
+<title>Basics</title>
+<g class="m-node">
+<title>a</title>
+<polygon points="54,-40.3848 0,-40.3848 0,-4.3848 54,-4.3848 54,-40.3848"/>
+<text text-anchor="middle" x="27" y="-18.5848">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="113.3848" cy="-22.3848" rx="18.2906" ry="18.2906"/>
+<ellipse cx="113.3848" cy="-22.3848" rx="22.2701" ry="22.2701"/>
+<text text-anchor="middle" x="113.3848" y="-18.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a->b</title>
+<path d="M54.3327,-22.3848C62.6964,-22.3848 71.9814,-22.3848 80.6653,-22.3848"/>
+<polygon points="80.8574,-25.8849 90.8574,-22.3848 80.8574,-18.8849 80.8574,-25.8849"/>
+</g>
+<g class="m-node m-flat">
+<title>c</title>
+<ellipse cx="224.7696" cy="-22.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="224.7696" y="-18.5848">c</text>
+</g>
+<g class="m-edge">
+<title>b->c</title>
+<path d="M135.9251,-22.3848C150.665,-22.3848 170.2762,-22.3848 187.3232,-22.3848"/>
+<polygon points="187.5396,-25.8849 197.5396,-22.3848 187.5396,-18.8849 187.5396,-25.8849"/>
+<text text-anchor="middle" x="166.7696" y="-34.3848" style="font-size: 40.0px;">0</text>
+</g>
+<g class="m-edge">
+<title>c->c</title>
+<path d="M214.4919,-39.6042C212.3945,-49.5298 215.8203,-58.7696 224.7696,-58.7696 230.5026,-58.7696 233.969,-54.9775 235.1685,-49.6425"/>
+<polygon points="238.6679,-49.5611 235.0472,-39.6042 231.6684,-49.6458 238.6679,-49.5611"/>
+<text text-anchor="middle" x="224.7696" y="-63.9696">1</text>
+</g>
+</g>
+</svg>
+</div><div class="m-graph"><svg style="width: 5.688rem; height: 8.500rem;" viewBox="0.00 0.00 91.38 135.54">
+<g transform="scale(1 1) rotate(0) translate(4 131.5391)">
+<title>Colors</title>
+<g class="m-node m-success m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-109.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-105.3543">a</text>
+</g>
+<g class="m-node m-dim">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="18.2703" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge m-warning">
+<title>a->b</title>
+<path d="M27,-90.3468C27,-77.8501 27,-61.2025 27,-47.0671"/>
+<polygon points="30.5001,-46.8576 27,-36.8576 23.5001,-46.8577 30.5001,-46.8576"/>
+<text text-anchor="middle" x="41" y="-59.9696">yes</text>
+</g>
+<g class="m-edge m-primary">
+<title>b->b</title>
+<path d="M43.8422,-26.1192C53.9096,-27.8379 63.3848,-25.2598 63.3848,-18.3848 63.3848,-13.9805 59.4961,-11.3396 54.0544,-10.4622"/>
+<polygon points="53.776,-6.9666 43.8422,-10.6504 53.905,-13.9655 53.776,-6.9666"/>
+<text text-anchor="middle" x="73.3848" y="-14.5848">no</text>
+</g>
+</g>
+</svg>
+</div><figure class="m-figure"><svg class="m-graph" style="width: 5rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.5391)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.4144C27,-61.4654 27,-47.7036 27,-36.8093"/>
+</g>
+</g>
+</svg>
+<figcaption>A graph</figcaption></figure><figure class="m-figure"><svg class="m-graph" style="height: 5rem;" viewBox="0.00 0.00 62.00 117.54">
+<g transform="scale(1 1) rotate(0) translate(4 113.5391)">
+<g class="m-node m-flat">
+<title>a</title>
+<ellipse cx="27" cy="-91.1543" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-87.3543">a</text>
+</g>
+<g class="m-node m-flat">
+<title>b</title>
+<ellipse cx="27" cy="-18.3848" rx="27" ry="18.2703"/>
+<text text-anchor="middle" x="27" y="-14.5848">b</text>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M27,-72.4144C27,-61.4654 27,-47.7036 27,-36.8093"/>
+</g>
+</g>
+</svg>
+<figcaption>A graph</figcaption></figure>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
--- /dev/null
+/** @mainpage
+
+Note: the test uses DejaVu Sans instead of Source Sans Pro in order to have
+predictable rendering on the CIs.
+
+@dot
+digraph Basics {
+ rankdir=LR
+
+ a [style=filled shape=rect]
+ b [peripheries=2 shape=circle]
+ c [shape=ellipse]
+ a -> b
+ b -> c [label="0" fontsize=40]
+ c -> c [label="1"]
+}
+@enddot
+
+@dotfile colors.dot
+
+@dot "A graph" width=5rem
+strict graph "" {
+ a -- b
+ a -- b
+}
+@enddot
+
+@dotfile ab.dot "A graph" height=5rem
+*/
+
+/** @page warnings
+
+This file doesn't exist:
+
+@dotfile nonexistent.dot
+*/
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>warnings | My Project</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+ <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</head>
+<body>
+<header><nav id="navigation">
+ <div class="m-container">
+ <div class="m-row">
+ <a href="index.html" id="m-navbar-brand" class="m-col-t-8 m-col-m-none m-left-m">My Project</a>
+ </div>
+ </div>
+</nav></header>
+<main><article>
+ <div class="m-container m-container-inflatable">
+ <div class="m-row">
+ <div class="m-col-l-10 m-push-l-1">
+ <h1>
+ warnings
+ </h1>
+<p>This file doesn't exist:</p><div class="m-graph"><svg style="width: 0.500rem; height: 0.500rem;" viewBox="0.00 0.00 8.00 8.00">
+<g transform="scale(1 1) rotate(0) translate(4 4)">
+</g>
+</svg>
+</div>
+ </div>
+ </div>
+ </div>
+</article></main>
+</body>
+</html>
\ No newline at end of file
import os
import pickle
+import re
import shutil
import subprocess
import unittest
from . import BaseTestCase, IntegrationTestCase, doxygen_version
+def dot_version():
+ return re.match(".*version (?P<version>\d+\.\d+\.\d+).*", subprocess.check_output(['dot', '-V'], stderr=subprocess.STDOUT).decode('utf-8').strip()).group('version')
+
class Typography(IntegrationTestCase):
def __init__(self, *args, **kwargs):
super().__init__(__file__, 'typography', *args, **kwargs)
self.run_dox2html5(wildcard='math.xml')
self.assertEqual(*self.actual_expected_contents('math.html'))
+ @unittest.skipUnless(LooseVersion(dot_version()) >= LooseVersion("2.40.1"),
+ "Dot < 2.40.1 has a completely different output.")
+ def test_dot(self):
+ self.run_dox2html5(wildcard='dot.xml')
+ self.assertEqual(*self.actual_expected_contents('dot.html'))
+
+ @unittest.skipUnless(LooseVersion(dot_version()) < LooseVersion("2.40.1") and
+ LooseVersion(dot_version()) >= LooseVersion("2.38.0"),
+ "Dot < 2.38 and dot > 2.38 has a completely different output.")
+ def test_dot238(self):
+ self.run_dox2html5(wildcard='dot.xml')
+ self.assertEqual(*self.actual_expected_contents('dot.html', 'dot-238.html'))
+
+ @unittest.skipUnless(LooseVersion(dot_version()) < LooseVersion("2.38.0"),
+ "Dot > 2.36 has a completely different output.")
+ def test_dot236(self):
+ self.run_dox2html5(wildcard='dot.xml')
+ self.assertEqual(*self.actual_expected_contents('dot.html', 'dot-236.html'))
+
class ParseError(BaseTestCase):
def __init__(self, *args, **kwargs):
super().__init__(__file__, 'parse_error', *args, **kwargs)
def test(self):
self.run_dox2html5(wildcard='File_8h.xml')
self.assertEqual(*self.actual_expected_contents('File_8h.html'))
+
+class Dot(IntegrationTestCase):
+ def __init__(self, *args, **kwargs):
+ super().__init__(__file__, 'dot', *args, **kwargs)
+
+ @unittest.skipUnless(LooseVersion(dot_version()) >= LooseVersion("2.40.1"),
+ "Dot < 2.40.1 has a completely different output.")
+ def test(self):
+ self.run_dox2html5(wildcard='indexpage.xml')
+ self.assertEqual(*self.actual_expected_contents('index.html'))
+
+ @unittest.skipUnless(LooseVersion(dot_version()) < LooseVersion("2.40.1") and
+ LooseVersion(dot_version()) >= LooseVersion("2.38.0"),
+ "Dot < 2.38 and dot > 2.38 has a completely different output.")
+ def test_238(self):
+ self.run_dox2html5(wildcard='indexpage.xml')
+ self.assertEqual(*self.actual_expected_contents('index.html', 'index-238.html'))
+
+ @unittest.skipUnless(LooseVersion(dot_version()) < LooseVersion("2.38.0"),
+ "Dot > 2.36 has a completely different output.")
+ def test_236(self):
+ self.run_dox2html5(wildcard='indexpage.xml')
+ self.assertEqual(*self.actual_expected_contents('index.html', 'index-236.html'))
+
+ def test_warnings(self):
+ self.run_dox2html5(wildcard='warnings.xml')
+ self.assertEqual(*self.actual_expected_contents('warnings.html'))
state = State()
parse_doxyfile(state, 'test/doxyfile/Doxyfile')
self.assertEqual(state.doxyfile, {
+ 'DOT_FONTNAME': 'Helvetica',
+ 'DOT_FONTSIZE': 10,
'HTML_EXTRA_FILES': ['css', 'another.png', 'hello'],
'HTML_EXTRA_STYLESHEET': ['a.css', 'b.css'],
'HTML_OUTPUT': 'html',
_patch_dst = r"""<svg{attribs} style="width: {width:.3f}rem; height: {height:.3f}rem;" viewBox="{viewBox}">
<g """
+_patch_custom_size_dst = r"""<svg{attribs} style="{size}" viewBox="\g<viewBox>">
+<g """
_comment_src = re.compile(r"""<!--[^-]+-->\n""")
# converting to rem here
def _pt2em(pt): return pt/_font_size
-def dot2svg(source, attribs=''):
+def dot2svg(source, size=None, attribs=''):
try:
ret = subprocess.run(['dot', '-Tsvg',
'-Gfontname={}'.format(_font),
svg = _comment_src.sub('', ret.stdout.decode('utf-8'))
# Remove preamble and fixed size
- def patch_repl(match): return _patch_dst.format(
- attribs=attribs,
- width=_pt2em(float(match.group('width'))),
- height=_pt2em(float(match.group('height'))),
- viewBox=match.group('viewBox'))
- svg = _patch_src.sub(patch_repl, svg)
+ if size:
+ svg = _patch_src.sub(_patch_custom_size_dst.format(attribs=attribs, size=size), svg)
+ else:
+ def patch_repl(match): return _patch_dst.format(
+ attribs=attribs,
+ width=_pt2em(float(match.group('width'))),
+ height=_pt2em(float(match.group('height'))),
+ viewBox=match.group('viewBox'))
+ svg = _patch_src.sub(patch_repl, svg)
# Remove unnecessary IDs and attributes, replace classes for elements
def element_repl(match):