0 -- 1 -- 2 -- 3 -- 4 -- 0 -- 2 -- 4 --1
3 [style=filled]
+
+`Graph figure`_
+---------------
+
+See the `m.components <{filename}/plugins/components.rst#code-math-and-graph-figure>`__
+plugin for details about graph figures using the :rst:`.. graph-figure::`
+directive.
+
+.. code-figure::
+
+ .. code:: rst
+
+ .. graph-figure:: Impenetrable logic
+
+ .. digraph::
+
+ rankdir=LR
+ yes [shape=circle, class="m-primary", style=filled]
+ no [shape=circle, class="m-primary"]
+ yes -> no [label="no", class="m-primary"]
+ no -> no [label="no"]
+
+ No.
+
+ .. graph-figure:: Impenetrable logic
+
+ .. digraph::
+
+ rankdir=LR
+ yes [shape=circle, class="m-primary", style=filled]
+ no [shape=circle, class="m-primary"]
+ yes -> no [label="no", class="m-primary"]
+ no -> no [label="no"]
+
+ .. class:: m-noindent
+
+ No.
viewBox="(?P<viewBox>[^"]+)" xmlns="http://www\.w3\.org/2000/svg" xmlns:xlink="http://www\.w3\.org/1999/xlink">
<g id="graph0" class="graph" """)
-_patch_dst = r"""<svg style="width: {width:.3f}rem; height: {height:.3f}rem;" viewBox="{viewBox}">
+_patch_dst = r"""<svg{attribs} style="width: {width:.3f}rem; height: {height:.3f}rem;" viewBox="{viewBox}">
<g """
_comment_src = re.compile(r"""<!--[^-]+-->\n""")
# converting to rem here
def _pt2em(pt): return pt/_font_size
-def dot2svg(source):
+def dot2svg(source, attribs=''):
try:
ret = subprocess.run(['dot', '-Tsvg',
'-Gfontname={}'.format(_font),
# 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'))
import dot2svg
+def _is_graph_figure(parent):
+ # The parent has to be a figure, marked as m-figure
+ if not isinstance(parent, nodes.figure): return False
+ if 'm-figure' not in parent.get('classes', []): return False
+
+ # And as a first visible node of such type
+ for child in parent:
+ if not isinstance(child, nodes.Invisible): return False
+
+ return True
+
class Dot(rst.Directive):
has_content = True
optional_arguments = 1
def run(self, source):
set_classes(self.options)
- svg = dot2svg.dot2svg(source)
+ # If this is the first real node inside a graph figure, put the SVG
+ # directly inside
+ parent = self.state.parent
+ if _is_graph_figure(parent):
+ svg = dot2svg.dot2svg(source, attribs=' class="{}"'.format(' '.join(['m-graph'] + self.options.get('classes', []))))
+ node = nodes.raw('', svg, format='html')
+ return [node]
+ # Otherwise wrap it in a <div class="m-graph">
+ svg = dot2svg.dot2svg(source)
container = nodes.container(**self.options)
container['classes'] = ['m-graph'] + container['classes']
node = nodes.raw('', svg, format='html')
</g>
</svg>
</div>
+<figure class="m-figure">
+<svg class="m-graph m-info" 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)">
+<title>A to B</title>
+<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,-66.4942 27,-56.7004 27,-47.6514"/>
+<polygon points="30.5001,-47.5182 27,-37.5182 23.5001,-47.5183 30.5001,-47.5182"/>
+</g>
+</g>
+</svg>
+<figcaption>This is a title.</figcaption>
+<p>This is a description.</p>
+</figure>
+<figure class="m-figure">
+<svg class="m-graph" 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,-66.4942 27,-56.7004 27,-47.6514"/>
+<polygon points="30.5001,-47.5182 27,-37.5182 23.5001,-47.5183 30.5001,-47.5182"/>
+</g>
+</g>
+</svg>
+<p>The graph below should not be styled as a part of the figure:</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)">
+<title>A to B</title>
+<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,-66.4942 27,-56.7004 27,-47.6514"/>
+<polygon points="30.5001,-47.5182 27,-37.5182 23.5001,-47.5183 30.5001,-47.5182"/>
+</g>
+</g>
+</svg>
+</div>
+</figure>
<!-- /content -->
</div>
</div>
</g>
</svg>
</div>
+<figure class="m-figure">
+<svg class="m-graph m-info" 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)">
+<title>A to B</title>
+<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,-64.9784 27,-55.7693 27,-47.185"/>
+<polygon points="30.5001,-47.1687 27,-37.1687 23.5001,-47.1687 30.5001,-47.1687"/>
+</g>
+</g>
+</svg>
+<figcaption>This is a title.</figcaption>
+<p>This is a description.</p>
+</figure>
+<figure class="m-figure">
+<svg class="m-graph" 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,-64.9784 27,-55.7693 27,-47.185"/>
+<polygon points="30.5001,-47.1687 27,-37.1687 23.5001,-47.1687 30.5001,-47.1687"/>
+</g>
+</g>
+</svg>
+<p>The graph below should not be styled as a part of the figure:</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)">
+<title>A to B</title>
+<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,-64.9784 27,-55.7693 27,-47.185"/>
+<polygon points="30.5001,-47.1687 27,-37.1687 23.5001,-47.1687 30.5001,-47.1687"/>
+</g>
+</g>
+</svg>
+</div>
+</figure>
<!-- /content -->
</div>
</div>
</g>
</svg>
</div>
+<figure class="m-figure">
+<svg class="m-graph m-info" 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)">
+<title>A to B</title>
+<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,-64.6303 27,-55.4246 27,-46.8383"/>
+<polygon points="30.5001,-46.8093 27,-36.8093 23.5001,-46.8094 30.5001,-46.8093"/>
+</g>
+</g>
+</svg>
+<figcaption>This is a title.</figcaption>
+<p>This is a description.</p>
+</figure>
+<figure class="m-figure">
+<svg class="m-graph" 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,-64.6303 27,-55.4246 27,-46.8383"/>
+<polygon points="30.5001,-46.8093 27,-36.8093 23.5001,-46.8094 30.5001,-46.8093"/>
+</g>
+</g>
+</svg>
+<p>The graph below should not be styled as a part of the figure:</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)">
+<title>A to B</title>
+<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,-64.6303 27,-55.4246 27,-46.8383"/>
+<polygon points="30.5001,-46.8093 27,-36.8093 23.5001,-46.8094 30.5001,-46.8093"/>
+</g>
+</g>
+</svg>
+</div>
+</figure>
<!-- /content -->
</div>
</div>
a -> b
This is a description.
+
+.. graph-figure::
+
+ .. digraph::
+
+ a -> b
+
+ The graph below should not be styled as a part of the figure:
+
+ .. digraph:: A to B
+ :class: m-danger
+
+ a -> b
"The math plugin requires at least Python 3.5 installed. Dot < 2.40.1 has a completely different output.")
def test(self):
self.run_pelican({
- 'PLUGINS': ['m.htmlsanity', 'm.dot'],
+ 'PLUGINS': ['m.htmlsanity', 'm.components', 'm.dot'],
'M_DOT_FONT': 'DejaVu Sans'
})
"The math plugin requires at least Python 3.5 installed. Dot < 2.38 and dot > 2.38 has a completely different output.")
def test_238(self):
self.run_pelican({
- 'PLUGINS': ['m.htmlsanity', 'm.dot'],
+ 'PLUGINS': ['m.htmlsanity', 'm.components', 'm.dot'],
'M_DOT_FONT': 'DejaVu Sans'
})
"The math plugin requires at least Python 3.5 installed. Dot > 2.36 has a completely different output.")
def test_236(self):
self.run_pelican({
- 'PLUGINS': ['m.htmlsanity', 'm.dot'],
+ 'PLUGINS': ['m.htmlsanity', 'm.components', 'm.dot'],
'M_DOT_FONT': 'DejaVu Sans'
})