MoinLight

Annotated moinformat/serialisers/html/graphviz.py

104:8d437cdf2381
2018-07-30 Paul Boddie Introduced Unicode strings as the typical form of document data for processing, employing output encodings when serialising output documents. Introduced input contexts which provide details of input document encodings and document source locations. Introduced the familiar manifest and lookup mechanisms for input and output contexts. Provided support for standalone contexts, with the output context suppressing the generation of additional files. Moved common directory functionality to a separate utility module. Updated the conversion script and test framework, adding encoding-related tests.
paul@101 1
#!/usr/bin/env python
paul@101 2
paul@101 3
"""
paul@101 4
Graphviz serialiser, generating content for embedding in HTML documents.
paul@101 5
paul@101 6
Copyright (C) 2018 Paul Boddie <paul@boddie.org.uk>
paul@101 7
paul@101 8
This program is free software; you can redistribute it and/or modify it under
paul@101 9
the terms of the GNU General Public License as published by the Free Software
paul@101 10
Foundation; either version 3 of the License, or (at your option) any later
paul@101 11
version.
paul@101 12
paul@101 13
This program is distributed in the hope that it will be useful, but WITHOUT
paul@101 14
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
paul@101 15
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
paul@101 16
details.
paul@101 17
paul@101 18
You should have received a copy of the GNU General Public License along with
paul@101 19
this program.  If not, see <http://www.gnu.org/licenses/>.
paul@101 20
"""
paul@101 21
paul@101 22
from moinformat.serialisers.common import Serialiser, escape_attr, escape_text
paul@101 23
from moinformat.utils.graphviz import Graphviz, GraphvizError, IMAGE_FORMATS, \
paul@101 24
                                      get_output_identifier
paul@101 25
paul@101 26
# Utility functions.
paul@101 27
paul@101 28
def select_keys(d, keys):
paul@101 29
paul@101 30
    "Select from 'd' the given 'keys'."
paul@101 31
paul@101 32
    if not d:
paul@101 33
        return []
paul@101 34
paul@101 35
    out = {}
paul@101 36
paul@101 37
    for key in keys:
paul@101 38
        if d.has_key(key):
paul@101 39
            out[key] = d[key]
paul@101 40
paul@101 41
    return out
paul@101 42
paul@101 43
paul@101 44
paul@101 45
# The serialiser class.
paul@101 46
paul@101 47
class HTMLGraphvizSerialiser(Serialiser):
paul@101 48
paul@101 49
    "Serialisation of Graphviz regions."
paul@101 50
paul@101 51
    def init(self):
paul@101 52
        self.directives = {}
paul@101 53
paul@101 54
    def start_block(self):
paul@101 55
        pass
paul@101 56
paul@101 57
    def end_block(self):
paul@101 58
        pass
paul@101 59
paul@101 60
    def directive(self, key, value):
paul@101 61
        if not self.directives.has_key(key):
paul@101 62
            self.directives[key] = []
paul@101 63
        self.directives[key].append(value)
paul@101 64
paul@101 65
    def text(self, text):
paul@101 66
        self.process_graph(text)
paul@101 67
paul@101 68
paul@101 69
paul@101 70
    # Special methods for graph production.
paul@101 71
paul@101 72
    def _tag(self, tagname, attrname, filename, attributes, closing):
paul@101 73
        l = ["%s='%s'" % (attrname, escape_attr(filename))]
paul@101 74
        for key, value in attributes.items():
paul@101 75
            l.append("%s='%s'" % (key, value))
paul@101 76
        self.out("<%s %s%s>" % (tagname, " ".join(l), closing and " /"))
paul@101 77
paul@101 78
    def image(self, filename, attributes):
paul@101 79
        self._tag("img", "src", filename, attributes, True)
paul@101 80
paul@101 81
    def object(self, filename, attributes):
paul@101 82
        self._tag("object", "data", filename, attributes, False)
paul@101 83
        self.out("</object>")
paul@101 84
paul@101 85
    def raw(self, text):
paul@101 86
        self.out(text)
paul@101 87
paul@101 88
paul@101 89
paul@101 90
    # Graph output preparation.
paul@101 91
paul@101 92
    def process_graph(self, text):
paul@101 93
paul@101 94
        "Process the graph 'text' using the known directives."
paul@101 95
paul@101 96
        filter = self.directives.get("filter", ["dot"])[0]
paul@101 97
        format = self.directives.get("format", ["svg"])[0]
paul@101 98
        transforms = self.directives.get("transform", [])
paul@101 99
paul@101 100
        # Get an identifier and usable filename to store the output.
paul@101 101
paul@101 102
        identifier = get_output_identifier(text)
paul@101 103
        filename = self.output.get_filename(identifier)
paul@101 104
paul@104 105
        # Handle situations where no independent output is permitted.
paul@104 106
paul@104 107
        if not filename:
paul@104 108
            return
paul@104 109
paul@101 110
        # Permit imagemaps only for image formats.
paul@101 111
paul@101 112
        if format in IMAGE_FORMATS:
paul@101 113
            cmapx = self.directives.has_key("cmapx")
paul@101 114
paul@101 115
        # Configure Graphviz and invoke it.
paul@101 116
paul@101 117
        graphviz = Graphviz(filter, text, identifier)
paul@101 118
        graphviz.call(format, transforms, filename)
paul@101 119
paul@101 120
        # Obtain any metadata.
paul@101 121
paul@101 122
        attributes = select_keys(graphviz.get_metadata(), ["width", "height"])
paul@101 123
paul@101 124
        # For image output, create a file directly and reference it.
paul@101 125
paul@101 126
        if format in IMAGE_FORMATS:
paul@101 127
paul@101 128
            # Produce, embed and reference an imagemap if requested.
paul@101 129
paul@101 130
            if cmapx:
paul@101 131
                graphviz.call("cmapx")
paul@101 132
                mapid = graphviz.get_metadata().get("id")
paul@101 133
paul@101 134
                if mapid:
paul@101 135
                    self.raw(graphviz.get_output())
paul@101 136
                    attributes["usemap"] = "#%s" % im_attributes["id"]
paul@101 137
paul@101 138
            self.image(filename, attributes)
paul@101 139
paul@101 140
        # For other output, create a file and embed the object.
paul@101 141
paul@101 142
        else:
paul@101 143
            self.object(filename, attributes)
paul@101 144
paul@101 145
serialiser = HTMLGraphvizSerialiser
paul@101 146
paul@101 147
# vim: tabstop=4 expandtab shiftwidth=4