forked from sprin/markdown-inline-graphviz
-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathmarkdown_inline_graphviz.py
114 lines (89 loc) · 3.79 KB
/
markdown_inline_graphviz.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""
Graphviz extensions for Markdown.
Renders the output inline, eliminating the need to configure an output
directory.
Supports outputs types of SVG and PNG. The output will be taken from the
filename specified in the tag. Example:
{% dot attack_plan.svg
digraph G {
rankdir=LR
Earth [peripheries=2]
Mars
Earth -> Mars
}
%}
Requires the graphviz library (http://www.graphviz.org/) and python 3
Inspired by jawher/markdown-dot (https://github.com/jawher/markdown-dot)
Forked from sprin/markdown-inline-graphviz (https://github.com/sprin/markdown-inline-graphviz
"""
import re
import markdown
import subprocess
import base64
# Global vars
BLOCK_RE_CURLY_BRACKET = re.compile(
r'^{%[ ]* (?P<command>\w+)\s+(?P<filename>[^\s]+)\s*\n(?P<content>.*?)%}\s*$',
re.MULTILINE | re.DOTALL)
BLOCK_RE_GRAVE_ACCENT = re.compile(
r'^```graphviz[ ]* (?P<command>\w+)\s+(?P<filename>[^\s]+)\s*\n(?P<content>.*?)```\s*$',
re.MULTILINE | re.DOTALL)
# Command whitelist
SUPPORTED_COMMAMDS = ['dot', 'neato', 'fdp', 'sfdp', 'twopi', 'circo']
class InlineGraphvizExtension(markdown.Extension):
def extendMarkdown(self, md):
""" Add InlineGraphvizPreprocessor to the Markdown instance. """
md.registerExtension(self)
md.preprocessors.register(
InlineGraphvizPreprocessor(md), 'graphviz_block', 40
)
class InlineGraphvizPreprocessor(markdown.preprocessors.Preprocessor):
def __init__(self, md):
super(InlineGraphvizPreprocessor, self).__init__(md)
def run(self, lines):
""" Match and generate dot code blocks."""
text = "\n".join(lines)
while 1:
m = BLOCK_RE_CURLY_BRACKET.search(text) if BLOCK_RE_CURLY_BRACKET.search(text) else BLOCK_RE_GRAVE_ACCENT.search(text)
if m:
command = m.group('command')
# Whitelist command, prevent command injection.
if command not in SUPPORTED_COMMAMDS:
break
filename = m.group('filename')
content = m.group('content')
filetype = filename[filename.rfind('.')+1:]
args = [command, '-T'+filetype]
try:
proc = subprocess.Popen(
args,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
proc.stdin.write(content.encode('utf-8'))
output, err = proc.communicate()
if filetype == 'svg':
data_url_filetype = 'svg+xml'
encoding = 'utf-8'
img = output.decode(encoding)
if filetype == 'png':
data_url_filetype = 'png'
encoding = 'base64'
output = base64.b64encode(output).decode('utf-8')
data_path = "data:image/%s;%s,%s" % (
data_url_filetype,
encoding,
output)
img = ""
text = '%s\n%s\n%s' % (
text[:m.start()], img, text[m.end():])
except Exception as e:
err = str(e) + ' : ' + str(args)
return (
'<pre>Error : ' + err + '</pre>'
'<pre>' + content + '</pre>').split('\n')
else:
break
text_div_tags = text.replace("<svg", "<div><svg").replace("</svg>", "</svg></div>")
return text_div_tags.split("\n")
def makeExtension(*args, **kwargs):
return InlineGraphvizExtension(*args, **kwargs)