#!/usr/bin/env python """Custom reST_ directive for plantuml_ integration. Adapted from ditaa_rst plugin. .. _reST: http://docutils.sourceforge.net/rst.html .. _plantuml: http://plantuml.sourceforge.net/ """ import os import tempfile from zlib import adler32 from subprocess import Popen, PIPE from docutils.nodes import image, literal_block from docutils.parsers.rst import Directive, directives from docutils import utils, nodes from pelican import logger, signals global_siteurl = "" class PlantUML(Directive): required_arguments = 0 optional_arguments = 0 has_content = True global global_siteurl option_spec = { 'class' : directives.class_option, 'alt' : directives.unchanged, 'format': directives.unchanged, } def run(self): source = self.state_machine.input_lines.source(self.lineno - self.state_machine.input_offset - 1) source_dir = os.path.dirname(os.path.abspath(source)) source_dir = utils.relative_path(None, source_dir) path = os.path.abspath(os.path.join('output', 'images')) if not os.path.exists(path): os.makedirs(path) nodes = [] body = '\n'.join(self.content) tf = tempfile.NamedTemporaryFile(delete=True) tf.write('@startuml\n') tf.write(body.encode('utf8')) tf.write('\n@enduml') tf.flush() imgformat = self.options.get('format', 'png') if imgformat == 'png': imgext = ".png" outopt = "-tpng" elif imgformat == 'svg': imgext = ".svg" outopt = "-tsvg" else: logger.error("Bad uml image format: "+imgformat) # make a name name = tf.name+imgext alt = self.options.get('alt', 'uml diagram') classes = self.options.pop('class', ['uml']) cmdline = ['plantuml', '-o', path, outopt, tf.name ] try: p = Popen(cmdline, stdout=PIPE, stderr=PIPE) out, err = p.communicate() except Exception, exc: error = self.state_machine.reporter.error( 'Failed to run plantuml: %s' % (exc, ), literal_block(self.block_text, self.block_text), line=self.lineno) nodes.append(error) else: if p.returncode == 0: # renaming output image using an hash code, just to not pullate # output directory with a growing number of images name = os.path.join(path, os.path.basename(name)) newname = os.path.join(path, "%08x" % (adler32(body) & 0xffffffff))+imgext try: # for Windows os.remove(newname) except Exception, exc: logger.debug('File '+newname+' does not exist, not deleted') os.rename(name, newname) url = global_siteurl + '/images/' + os.path.basename(newname) imgnode = image(uri=url, classes=classes, alt=alt) nodes.append(imgnode) else: error = self.state_machine.reporter.error( 'Error in "%s" directive: %s' % (self.name, err), literal_block(self.block_text, self.block_text), line=self.lineno) nodes.append(error) return nodes def custom_url(generator, metadata): global global_siteurl global_siteurl = generator.settings['SITEURL'] def register(): """Plugin registration.""" signals.article_generator_context.connect(custom_url) directives.register_directive('uml', PlantUML)