Ugh. This does not scale at all.
Note that currently all styling is discarded and only the
``class`` and ``fontsize`` attributes are taken into account.
+.. note-warning::
+
+ The ``class`` attribute is new in Graphviz 2.40.1. If you have an older
+ version on your system, this attribute will get ignored.
+
`Undirected graphs`_
--------------------
_comment_src = re.compile(r"""<!--[^-]+-->\n""")
-_class_src = re.compile(r"""<g id="(edge|node)\d+" class="(?P<type>edge|node)(?P<classes>[^"]*)">
-<title>(?P<title>[^<]*)</title>
+# Graphviz < 2.40 (Ubuntu 16.04 and older) doesn't have a linebreak between <g>
+# and <title>
+_class_src = re.compile(r"""<g id="(edge|node)\d+" class="(?P<type>edge|node)(?P<classes>[^"]*)">[\n]?<title>(?P<title>[^<]*)</title>
<(?P<element>ellipse|polygon|path) fill="(?P<fill>[^"]+)" stroke="[^"]+" """)
_class_dst = r"""<g class="{classes}">
_attributes_dst = r"""<\g<element> """
-# re.compile() is called after replacing {font} in configure()
-_text_src_src = ' font-family="{font}" font-size="(?P<size>[^"]+)" fill="[^"]+"'
+# re.compile() is called after replacing {font} in configure(). Graphviz < 2.40
+# doesn't put the fill="" attribute there
+_text_src_src = ' font-family="{font}" font-size="(?P<size>[^"]+)"( fill="[^"]+")?'
_text_dst = ' style="font-size: {size}px;"'
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>m.dot | A Pelican Blog</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+ <link rel="stylesheet" href="static/m-dark.css" />
+ <link rel="canonical" href="page.html" />
+ <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="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</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>m.dot</h1>
+<!-- content -->
+<p>Note: the test uses DejaVu Sans instead of Source Sans Pro in order to have
+predictable rendering on the CIs.</p>
+<p>Different shapes, fills etc. All default colors, filled only the first node
+and the arrowheads, nothing else. Non-default font size should be preserved.</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>
+<p>Colors:</p>
+<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>
+<p>Unoriented graph:</p>
+<div class="m-graph m-success">
+<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="M21.1601,-74.937C19.96,-63.626 19.9468,-48.8282 21.1206,-37.4407"/>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M32.8399,-74.937C34.04,-63.626 34.0532,-48.8282 32.8794,-37.4407"/>
+</g>
+</g>
+</svg>
+</div>
+<p>Strict graphs:</p>
+<div class="m-graph">
+<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>
+<div class="m-graph">
+<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,-63.3339 27,-48.7727 27,-37.5182"/>
+</g>
+</g>
+</svg>
+</div>
+<p>Structs:</p>
+<div class="m-graph">
+<svg style="width: 13.000rem; height: 5.500rem;" viewBox="0.00 0.00 208.00 88.00">
+<g transform="scale(1 1) rotate(0) translate(4 84)">
+<title>Structs</title>
+<g class="m-node m-flat">
+<title>struct</title>
+<polygon points="0,-1 0,-79 78,-79 78,-1 0,-1"/>
+<text text-anchor="middle" x="39" y="-62.2">a</text>
+<polyline points="0,-53 78,-53 "/>
+<text text-anchor="middle" x="39" y="-36.2">b</text>
+<polyline points="0,-27 78,-27 "/>
+<text text-anchor="middle" x="13" y="-10.2">c</text>
+<polyline points="26,-1 26,-27 "/>
+<text text-anchor="middle" x="39" y="-10.2">d</text>
+<polyline points="52,-1 52,-27 "/>
+<text text-anchor="middle" x="65" y="-10.2">e</text>
+</g>
+<g class="m-node m-flat">
+<title>another</title>
+<polygon points="96,-14 96,-66 200,-66 200,-14 96,-14"/>
+<text text-anchor="middle" x="109" y="-36.2">a</text>
+<polyline points="122,-14 122,-66 "/>
+<text text-anchor="middle" x="135" y="-49.2">b</text>
+<polyline points="122,-40 148,-40 "/>
+<text text-anchor="middle" x="135" y="-23.2">c</text>
+<polyline points="148,-14 148,-66 "/>
+<text text-anchor="middle" x="161" y="-36.2">d</text>
+<polyline points="174,-14 174,-66 "/>
+<text text-anchor="middle" x="187" y="-36.2">e</text>
+</g>
+</g>
+</svg>
+</div>
+<!-- /content -->
+ </div>
+ </div>
+ </div>
+</article>
+</main>
+</body>
+</html>
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <title>m.dot | A Pelican Blog</title>
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i" />
+ <link rel="stylesheet" href="static/m-dark.css" />
+ <link rel="canonical" href="page.html" />
+ <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="./" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">A Pelican Blog</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>m.dot</h1>
+<!-- content -->
+<p>Note: the test uses DejaVu Sans instead of Source Sans Pro in order to have
+predictable rendering on the CIs.</p>
+<p>Different shapes, fills etc. All default colors, filled only the first node
+and the arrowheads, nothing else. Non-default font size should be preserved.</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>
+<p>Colors:</p>
+<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>
+<p>Unoriented graph:</p>
+<div class="m-graph m-success">
+<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="M21.1218,-73.023C19.9539,-61.8963 19.9594,-47.4535 21.1384,-36.3583"/>
+</g>
+<g class="m-edge">
+<title>a--b</title>
+<path d="M32.8782,-73.023C34.0461,-61.8963 34.0406,-47.4535 32.8616,-36.3583"/>
+</g>
+</g>
+</svg>
+</div>
+<p>Strict graphs:</p>
+<div class="m-graph">
+<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>
+<div class="m-graph">
+<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,-61.8548 27,-48.0281 27,-37.1687"/>
+</g>
+</g>
+</svg>
+</div>
+<p>Structs:</p>
+<div class="m-graph">
+<svg style="width: 13.000rem; height: 5.438rem;" viewBox="0.00 0.00 208.00 87.00">
+<g transform="scale(1 1) rotate(0) translate(4 83)">
+<title>Structs</title>
+<g class="m-node m-flat">
+<title>struct</title>
+<polygon points="0,-0.5 0,-78.5 78,-78.5 78,-0.5 0,-0.5"/>
+<text text-anchor="middle" x="39" y="-61.7">a</text>
+<polyline points="0,-52.5 78,-52.5 "/>
+<text text-anchor="middle" x="39" y="-35.7">b</text>
+<polyline points="0,-26.5 78,-26.5 "/>
+<text text-anchor="middle" x="13" y="-9.7">c</text>
+<polyline points="26,-0.5 26,-26.5 "/>
+<text text-anchor="middle" x="39" y="-9.7">d</text>
+<polyline points="52,-0.5 52,-26.5 "/>
+<text text-anchor="middle" x="65" y="-9.7">e</text>
+</g>
+<g class="m-node m-flat">
+<title>another</title>
+<polygon points="96,-13.5 96,-65.5 200,-65.5 200,-13.5 96,-13.5"/>
+<text text-anchor="middle" x="109" y="-35.7">a</text>
+<polyline points="122,-13.5 122,-65.5 "/>
+<text text-anchor="middle" x="135" y="-48.7">b</text>
+<polyline points="122,-39.5 148,-39.5 "/>
+<text text-anchor="middle" x="135" y="-22.7">c</text>
+<polyline points="148,-13.5 148,-65.5 "/>
+<text text-anchor="middle" x="161" y="-35.7">d</text>
+<polyline points="174,-13.5 174,-65.5 "/>
+<text text-anchor="middle" x="187" y="-35.7">e</text>
+</g>
+</g>
+</svg>
+</div>
+<!-- /content -->
+ </div>
+ </div>
+ </div>
+</article>
+</main>
+</body>
+</html>
# DEALINGS IN THE SOFTWARE.
#
+import re
+import subprocess
import sys
import unittest
from m.test import PluginTestCase
+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 Dot(PluginTestCase):
def __init__(self, *args, **kwargs):
super().__init__(__file__, '', *args, **kwargs)
- @unittest.skipUnless(LooseVersion(sys.version) >= LooseVersion("3.5"),
- "The math plugin requires at least Python 3.5 installed")
+ @unittest.skipUnless(LooseVersion(sys.version) >= LooseVersion("3.5") and
+ LooseVersion(dot_version()) >= LooseVersion("2.40.1"),
+ "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'],
})
self.assertEqual(*self.actual_expected_contents('page.html'))
+
+ @unittest.skipUnless(LooseVersion(sys.version) >= LooseVersion("3.5") and
+ LooseVersion(dot_version()) < LooseVersion("2.40.1") and
+ LooseVersion(dot_version()) >= LooseVersion("2.38.0"),
+ "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'],
+ 'M_DOT_FONT': 'DejaVu Sans'
+ })
+
+ self.assertEqual(*self.actual_expected_contents('page.html', 'page-238.html'))
+
+ @unittest.skipUnless(LooseVersion(sys.version) >= LooseVersion("3.5") and
+ LooseVersion(dot_version()) < LooseVersion("2.38.0"),
+ "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'],
+ 'M_DOT_FONT': 'DejaVu Sans'
+ })
+
+ self.assertEqual(*self.actual_expected_contents('page.html', 'page-236.html'))