Skip to content

Commit

Permalink
Interpret newlines in tooltips
Browse files Browse the repository at this point in the history
This commit adds support for newlines in tooltips. The implemented
behavior matches the behavior of Graphviz SVG output, but is not
in-line with Graphviz documentation (i.e. Graphviz documentation
differs from implementation).

According to the documentation [1], tooltips are only supported for
svg and cmap outputs and not xdot (here xdot.py deviates from the
documentation, but for good reasons) and the value should be
escString. escString should support newlines only for label, headlabel
or taillabel attributes, not for tooltips [2]. Nevertheless, the svg
output supports them. But, contrary to the documentation, SVG tooltips
do not support capital letter escape sequences like \N, \G and \E.
Only \n, \l, \r and \\ seem to be supported. The first three are all
interpreted as a new line. Different alignments, specified by the
documentation, are not distinguished. The same is implemented in this
commit.

[1]: https://graphviz.org/docs/attrs/tooltip/
[2]: https://graphviz.org/docs/attr-types/escString/
  • Loading branch information
wentasah committed Nov 24, 2024
1 parent 264bfa6 commit 04712e2
Showing 1 changed file with 28 additions and 2 deletions.
30 changes: 28 additions & 2 deletions xdot/ui/_xdotparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,32 @@ def decode_attr(self, attrs, name):
else:
return value.decode(self.charset)

@staticmethod
def interpret_esc_nl(esc_string: str | None):
r"""Interpret newline escape sequences.
\n, \l and \r are replaced with newlines, other escaped
characters such as \\ with themselves.
"""
if esc_string is None:
return None
result = ""
was_escape = False
for ch in esc_string:
if was_escape:
was_escape = False
if ch in ['n', 'l', 'r']:
result += "\n"
else:
result += ch
else:
if ch == "\\":
was_escape = True
else:
result += ch
return result


def handle_node(self, id, attrs):
try:
pos = attrs['pos']
Expand All @@ -356,7 +382,7 @@ def handle_node(self, id, attrs):
parser = XDotAttrParser(self, attrs[attr], self.broken_backslashes)
shapes.extend(parser.parse())
url = self.decode_attr(attrs, 'URL')
tooltip = self.decode_attr(attrs, 'tooltip')
tooltip = self.interpret_esc_nl(self.decode_attr(attrs, 'tooltip'))
node = elements.Node(id, x, y, w, h, shapes, url, tooltip)
self.node_by_name[id] = node
if shapes:
Expand All @@ -377,7 +403,7 @@ def handle_edge(self, src_id, dst_id, attrs):
if shapes:
src = self.node_by_name[src_id]
dst = self.node_by_name[dst_id]
tooltip = self.decode_attr(attrs, 'tooltip')
tooltip = self.interpret_esc_nl(self.decode_attr(attrs, 'tooltip'))
self.edges.append(elements.Edge(src, dst, points, shapes, tooltip))

def parse(self):
Expand Down

0 comments on commit 04712e2

Please sign in to comment.