123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- """
- GraphViz Tag
- ---------
- This implements a Liquid-style graphviz tag for Pelican. You can use different
- Graphviz programs like dot, neato, twopi etc. [1]
- [1] http://www.graphviz.org/
- Syntax
- ------
- {% graphviz
- <program> {
- <DOT code>
- }
- %}
- Examples
- --------
- {% graphviz
- dot {
- digraph graphname {
- a -> b -> c;
- b -> d;
- }
- }
- %}
- {% graphviz
- twopi {
- <code goes here>
- }
- %}
- {% graphviz
- neato {
- <code goes here>
- }
- %}
- ...
- Output
- ------
- <div class="graphviz" style="text-align: center;"><img src="data:image/png;base64,_BASE64_IMAGE DATA_/></div>
- """
- import base64
- import re
- from .mdx_liquid_tags import LiquidTags
- SYNTAX = '{% dot graphviz [program] [dot code] %}'
- DOT_BLOCK_RE = re.compile(r'^\s*(?P<program>\w+)\s*\{\s*(?P<code>.*\})\s*\}$', re.MULTILINE | re.DOTALL)
- def run_graphviz(program, code, options=[], format='png'):
- """ Runs graphviz programs and returns image data
- Copied from https://github.com/tkf/ipython-hierarchymagic/blob/master/hierarchymagic.py
- """
- import os
- from subprocess import Popen, PIPE
- dot_args = [program] + options + ['-T', format]
- if os.name == 'nt':
- # Avoid opening shell window.
- # * https://github.com/tkf/ipython-hierarchymagic/issues/1
- # * http://stackoverflow.com/a/2935727/727827
- p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE, creationflags=0x08000000)
- else:
- p = Popen(dot_args, stdout=PIPE, stdin=PIPE, stderr=PIPE)
- wentwrong = False
- try:
- # Graphviz may close standard input when an error occurs,
- # resulting in a broken pipe on communicate()
- stdout, stderr = p.communicate(code.encode('utf-8'))
- except (OSError, IOError) as err:
- if err.errno != EPIPE:
- raise
- wentwrong = True
- except IOError as err:
- if err.errno != EINVAL:
- raise
- wentwrong = True
- if wentwrong:
- # in this case, read the standard output and standard error streams
- # directly, to get the error message(s)
- stdout, stderr = p.stdout.read(), p.stderr.read()
- p.wait()
- if p.returncode != 0:
- raise RuntimeError('dot exited with error:\n[stderr]\n{0}'.format(stderr.decode('utf-8')))
- return stdout
- @LiquidTags.register('graphviz')
- def graphviz_parser(preprocessor, tag, markup):
- """ Simple Graphviz parser """
- # Parse the markup string
- m = DOT_BLOCK_RE.search(markup)
- if m:
- # Get program and DOT code
- code = m.group('code')
- program = m.group('program').strip()
- # Run specified program with our markup
- output = run_graphviz(program, code)
- # Return Base64 encoded image
- return '<div class="graphviz" style="text-align: center;"><img src="data:image/png;base64,%s"></div>' % base64.b64encode(output)
- else:
- raise ValueError('Error processing input. '
- 'Expected syntax: {0}'.format(SYNTAX))
- #----------------------------------------------------------------------
- # This import allows image tag to be a Pelican plugin
- from .liquid_tags import register
|