Irrespectively of graphviz version.
Fixes https://github.com/jrfonseca/xdot.py/issues/92
| ... | ... |
@@ -14,8 +14,11 @@ |
| 14 | 14 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 15 | 15 |
# |
| 16 | 16 |
import colorsys |
| 17 |
+import re |
|
| 17 | 18 |
import sys |
| 18 | 19 |
|
| 20 |
+from distutils.version import LooseVersion |
|
| 21 |
+ |
|
| 19 | 22 |
from .lexer import ParseError, DotLexer |
| 20 | 23 |
|
| 21 | 24 |
from ..ui.colors import lookup_color |
| ... | ... |
@@ -85,7 +88,14 @@ class XDotAttrParser: |
| 85 | 88 |
- http://www.graphviz.org/doc/info/output.html#d:xdot |
| 86 | 89 |
""" |
| 87 | 90 |
|
| 88 |
- def __init__(self, parser, buf): |
|
| 91 |
+ def __init__(self, parser, buf, broken_backslashes): |
|
| 92 |
+ |
|
| 93 |
+ # `\` should be escaped as `\\`, but older versions of graphviz xdot |
|
| 94 |
+ # output failed to properly escape it. See also |
|
| 95 |
+ # https://github.com/jrfonseca/xdot.py/issues/92 |
|
| 96 |
+ if not broken_backslashes: |
|
| 97 |
+ buf = re.sub(br'\\(.)', br'\1', buf) |
|
| 98 |
+ |
|
| 89 | 99 |
self.parser = parser |
| 90 | 100 |
self.buf = buf |
| 91 | 101 |
self.pos = 0 |
| ... | ... |
@@ -427,10 +437,16 @@ class XDotParser(DotParser): |
| 427 | 437 |
|
| 428 | 438 |
XDOTVERSION = '1.7' |
| 429 | 439 |
|
| 430 |
- def __init__(self, xdotcode): |
|
| 440 |
+ def __init__(self, xdotcode, graphviz_version=None): |
|
| 431 | 441 |
lexer = DotLexer(buf=xdotcode) |
| 432 | 442 |
DotParser.__init__(self, lexer) |
| 433 | 443 |
|
| 444 |
+ # https://github.com/jrfonseca/xdot.py/issues/92 |
|
| 445 |
+ self.broken_backslashes = False |
|
| 446 |
+ if graphviz_version is not None and \ |
|
| 447 |
+ LooseVersion(graphviz_version) < LooseVersion("2.46.0"):
|
|
| 448 |
+ self.broken_backslashes = True |
|
| 449 |
+ |
|
| 434 | 450 |
self.nodes = [] |
| 435 | 451 |
self.edges = [] |
| 436 | 452 |
self.shapes = [] |
| ... | ... |
@@ -480,7 +496,7 @@ class XDotParser(DotParser): |
| 480 | 496 |
|
| 481 | 497 |
for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
|
| 482 | 498 |
if attr in attrs: |
| 483 |
- parser = XDotAttrParser(self, attrs[attr]) |
|
| 499 |
+ parser = XDotAttrParser(self, attrs[attr], self.broken_backslashes) |
|
| 484 | 500 |
self.shapes.extend(parser.parse()) |
| 485 | 501 |
|
| 486 | 502 |
def handle_node(self, id, attrs): |
| ... | ... |
@@ -502,7 +518,7 @@ class XDotParser(DotParser): |
| 502 | 518 |
shapes = [] |
| 503 | 519 |
for attr in ("_draw_", "_ldraw_"):
|
| 504 | 520 |
if attr in attrs: |
| 505 |
- parser = XDotAttrParser(self, attrs[attr]) |
|
| 521 |
+ parser = XDotAttrParser(self, attrs[attr], self.broken_backslashes) |
|
| 506 | 522 |
shapes.extend(parser.parse()) |
| 507 | 523 |
try: |
| 508 | 524 |
url = attrs['URL'] |
| ... | ... |
@@ -525,7 +541,7 @@ class XDotParser(DotParser): |
| 525 | 541 |
shapes = [] |
| 526 | 542 |
for attr in ("_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_"):
|
| 527 | 543 |
if attr in attrs: |
| 528 |
- parser = XDotAttrParser(self, attrs[attr]) |
|
| 544 |
+ parser = XDotAttrParser(self, attrs[attr], self.broken_backslashes) |
|
| 529 | 545 |
shapes.extend(parser.parse()) |
| 530 | 546 |
if shapes: |
| 531 | 547 |
src = self.node_by_name[src_id] |
| ... | ... |
@@ -56,6 +56,7 @@ class DotWidget(Gtk.DrawingArea): |
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 | 58 |
filter = 'dot' |
| 59 |
+ graphviz_version = None |
|
| 59 | 60 |
|
| 60 | 61 |
def __init__(self): |
| 61 | 62 |
Gtk.DrawingArea.__init__(self) |
| ... | ... |
@@ -100,6 +101,7 @@ class DotWidget(Gtk.DrawingArea): |
| 100 | 101 |
|
| 101 | 102 |
def set_filter(self, filter): |
| 102 | 103 |
self.filter = filter |
| 104 |
+ self.graphviz_version = None |
|
| 103 | 105 |
|
| 104 | 106 |
def run_filter(self, dotcode): |
| 105 | 107 |
if not self.filter: |
| ... | ... |
@@ -153,7 +155,14 @@ class DotWidget(Gtk.DrawingArea): |
| 153 | 155 |
|
| 154 | 156 |
def set_xdotcode(self, xdotcode, center=True): |
| 155 | 157 |
assert isinstance(xdotcode, bytes) |
| 156 |
- parser = XDotParser(xdotcode) |
|
| 158 |
+ if self.graphviz_version is None: |
|
| 159 |
+ stdout = subprocess.check_output([self.filter, '-V'], stderr=subprocess.STDOUT) |
|
| 160 |
+ stdout = stdout.rstrip() |
|
| 161 |
+ mo = re.match(br'^.* - .* version (?P<version>.*) \(.*\)$', stdout) |
|
| 162 |
+ assert mo |
|
| 163 |
+ self.graphviz_version = mo.group('version').decode('ascii')
|
|
| 164 |
+ |
|
| 165 |
+ parser = XDotParser(xdotcode, graphviz_version=self.graphviz_version) |
|
| 157 | 166 |
self.graph = parser.parse() |
| 158 | 167 |
self.zoom_image(self.zoom_ratio, center=center) |
| 159 | 168 |
|