summaryrefslogtreecommitdiffstats
path: root/source-builder/sb/asciidoc/filters/graphviz/graphviz2png.py
blob: a3d43f521f30e9b12fc92413fb19240ed287b483 (plain) (blame)
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env python

import os, sys, subprocess
from optparse import *

__AUTHOR__ = "Gouichi Iisaka <iisaka51@gmail.com>"
__VERSION__ = '1.1.4'

class EApp(Exception):
    '''Application specific exception.'''
    pass

class Application():
    '''
NAME
    graphviz2png - Converts textual graphviz notation to PNG file

SYNOPSIS
    graphviz2png [options] INFILE

DESCRIPTION
    This filter reads Graphviz notation text from the input file
    INFILE (or stdin if INFILE is -), converts it to a PNG image file.


OPTIONS
    -o OUTFILE, --outfile=OUTFILE
        The file name of the output file. If not specified the output file is
        named like INFILE but with a .png file name extension.

    -L LAYOUT, --layout=LAYOUT
        Graphviz layout: dot, neato, twopi, circo, fdp
        Default is 'dot'.

    -F FORMAT, --format=FORMAT
        Graphviz output format: png, svg, or any other format Graphviz
        supports. Run dot -T? to get the full list.
        Default is 'png'.

    -v, --verbose
        Verbosely print processing information to stderr.

    -h, --help
        Print this documentation.

    -V, --version
        Print program version number.

SEE ALSO
    graphviz(1)

AUTHOR
    Written by Gouichi Iisaka, <iisaka51@gmail.com>
    Format support added by Elmo Todurov, <todurov@gmail.com>

THANKS
    Stuart Rackham, <srackham@gmail.com>
    This script was inspired by his music2png.py and AsciiDoc

LICENSE
    Copyright (C) 2008-2009 Gouichi Iisaka.
    Free use of this software is granted under the terms of
    the GNU General Public License (GPL).
    '''

    def __init__(self, argv=None):
        # Run dot, get the list of supported formats. It's prefixed by some junk.
        format_output = subprocess.Popen(["dot", "-T?"], stderr=subprocess.PIPE, stdout=subprocess.PIPE).communicate()[1]
        # The junk contains : and ends with :. So we split it, then strip the final endline, then split the list for future usage.
        supported_formats = format_output.split(": ")[2][:-1].split(" ")

        if not argv:
            argv = sys.argv

        self.usage = '%prog [options] inputfile'
        self.version = 'Version: %s\n' % __VERSION__
        self.version += 'Copyright(c) 2008-2009: %s\n' % __AUTHOR__

        self.option_list = [
            Option("-o", "--outfile", action="store",
                dest="outfile",
                help="Output file"),
            Option("-L", "--layout", action="store",
                dest="layout", default="dot", type="choice",
                choices=['dot','neato','twopi','circo','fdp'],
                help="Layout type. LAYOUT=<dot|neato|twopi|circo|fdp>"),
            Option("-F", "--format", action="store",
                dest="format", default="png", type="choice",
                choices=supported_formats,
                help="Format type. FORMAT=<" + "|".join(supported_formats) + ">"),
            Option("--debug", action="store_true",
                dest="do_debug",
                help=SUPPRESS_HELP),
            Option("-v", "--verbose", action="store_true",
                dest="do_verbose", default=False,
                help="verbose output"),
            ]

        self.parser = OptionParser( usage=self.usage, version=self.version,
                                    option_list=self.option_list)
        (self.options, self.args) = self.parser.parse_args()

        if len(self.args) != 1:
            self.parser.print_help()
            sys.exit(1)

        self.options.infile = self.args[0]

    def systemcmd(self, cmd):
        if self.options.do_verbose:
            msg = 'Execute: %s' % cmd
            sys.stderr.write(msg + os.linesep)
        else:
            cmd += ' 2>%s' % os.devnull
        if os.system(cmd):
            raise EApp, 'failed command: %s' % cmd

    def graphviz2png(self, infile, outfile):
        '''Convert Graphviz notation in file infile to
           PNG file named outfile.'''

        outfile = os.path.abspath(outfile)
        outdir = os.path.dirname(outfile)

        if not os.path.isdir(outdir):
            raise EApp, 'directory does not exist: %s' % outdir

        basefile = os.path.splitext(outfile)[0]
        saved_cwd = os.getcwd()
        os.chdir(outdir)
        try:
            cmd = '%s -T%s "%s" > "%s"' % (
                  self.options.layout, self.options.format, infile, outfile)
            self.systemcmd(cmd)
        finally:
            os.chdir(saved_cwd)

        if not self.options.do_debug:
            os.unlink(infile)

    def run(self):
        if self.options.format == '':
            self.options.format = 'png'

        if self.options.infile == '-':
            if self.options.outfile is None:
                sys.stderr.write('OUTFILE must be specified')
                sys.exit(1)
            infile = os.path.splitext(self.options.outfile)[0] + '.txt'
            lines = sys.stdin.readlines()
            open(infile, 'w').writelines(lines)

        if not os.path.isfile(infile):
            raise EApp, 'input file does not exist: %s' % infile

        if self.options.outfile is None:
            outfile = os.path.splitext(infile)[0] + '.png'
        else:
            outfile = self.options.outfile

        self.graphviz2png(infile, outfile)

        # To suppress asciidoc 'no output from filter' warnings.
        if self.options.infile == '-':
            sys.stdout.write(' ')

if __name__ == "__main__":
    app = Application()
    app.run()