chiark / gitweb /
m.dot: compatibility with graph figures from m.components.
authorVladimír Vondruš <mosra@centrum.cz>
Sun, 14 Oct 2018 14:35:40 +0000 (16:35 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Sun, 14 Oct 2018 18:17:49 +0000 (20:17 +0200)
doc/plugins/plots-and-graphs.rst
pelican-plugins/dot2svg.py
pelican-plugins/m/dot.py
pelican-plugins/m/test/dot/page-236.html
pelican-plugins/m/test/dot/page-238.html
pelican-plugins/m/test/dot/page.html
pelican-plugins/m/test/dot/page.rst
pelican-plugins/m/test/test_dot.py

index 3d00e267ef3fac50b21b8e3b6544366736356df4..6ee8aa9c771b735d48f01d9f305bf99024a442e3 100644 (file)
@@ -283,3 +283,40 @@ equivalent to :dot:`graph` and :dot:`strict graph` in the DOT language:
 
         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.
index 9ff78113040fd1f0df84edf52e354afb0d58c580..5c224bd89a4f5aa77ca8fd051115b27435e18a0e 100644 (file)
@@ -32,7 +32,7 @@ _patch_src = re.compile(r"""<\?xml version="1\.0" encoding="UTF-8" standalone="n
  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""")
@@ -63,7 +63,7 @@ _font_size = 0.0
 # 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),
@@ -84,6 +84,7 @@ def dot2svg(source):
 
     # 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'))
index b5f8855bf57e7c5b61e2ce9af8799d6ea6683f29..85987a65fb6aa2a6fa66a3f4cdf423783f77b098 100644 (file)
@@ -33,6 +33,17 @@ from docutils.parsers.rst.roles import set_classes
 
 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
@@ -43,8 +54,16 @@ class Dot(rst.Directive):
     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')
index 1963b629e912d57c03d425377d6a320e7bdb82d2..cc067475bac1a9e5152afffdc9208a2eaf68807e 100644 (file)
@@ -200,6 +200,74 @@ and the arrowheads, nothing else. Non-default font size should be preserved.</p>
 </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&#45;&gt;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&#45;&gt;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&#45;&gt;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>
index c0c85a9d86b2de3b9d3b3e526e49787d28e1415a..e8607d6d8a0107b55ff37f0c3903d1d8155bc606 100644 (file)
@@ -200,6 +200,74 @@ and the arrowheads, nothing else. Non-default font size should be preserved.</p>
 </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&#45;&gt;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&#45;&gt;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&#45;&gt;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>
index 4c5ecc00560d9efdda9b5cb5c5e1b6603c050343..ff53a24021eff875194ce505f46c7797e3ccdae0 100644 (file)
@@ -200,6 +200,74 @@ and the arrowheads, nothing else. Non-default font size should be preserved.</p>
 </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&#45;&gt;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&#45;&gt;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&#45;&gt;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>
index 87e2c7fd08881e96a7d56605711d2af5b143f913..c3d09d29d8ec163004c3a82270393f332e94544f 100644 (file)
@@ -63,3 +63,16 @@ Structs:
         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
index 3a3dc7b5ebca18d878a801fab18498a5ad51fe12..13e8a89ae5cc24fd0df108a5d8b6ee4b73638c39 100644 (file)
@@ -43,7 +43,7 @@ class Dot(PluginTestCase):
                          "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'
         })
 
@@ -55,7 +55,7 @@ class Dot(PluginTestCase):
                          "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'
         })
 
@@ -66,7 +66,7 @@ class Dot(PluginTestCase):
                          "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'
         })