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