1 #!/usr/bin/env python 2 3 """ 4 HTML serialiser. 5 6 Copyright (C) 2017, 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 escape_attr, escape_text, Serialiser 23 24 class HTMLSerialiser(Serialiser): 25 26 "Serialisation of the page." 27 28 format = "html" 29 30 def _region_tag(self, type): 31 32 # NOTE: Need to support types in general. 33 34 type = type and type.split()[0] 35 36 if type == "inline": 37 return "tt" 38 elif type in (None, "python"): 39 return "pre" 40 else: 41 return "span" 42 43 def start_region(self, level, indent, type, extra): 44 45 # Generate attributes, joining them when preparing the tag. 46 47 l = [] 48 out = l.append 49 50 if level: 51 out("level-%d" % level) 52 53 if indent: 54 out("indent-%d" % indent) 55 56 # NOTE: Encode type details for CSS. 57 58 out("type-%s" % escape_attr(type or "opaque")) 59 60 tag = self._region_tag(type) 61 self.out("<%s class='%s'>" % (tag, " ".join(l))) 62 63 def end_region(self, level, indent, type, extra): 64 tag = self._region_tag(type) 65 self.out("</%s>" % tag) 66 67 def start_block(self): 68 self.out("<p>") 69 70 def end_block(self): 71 self.out("</p>") 72 73 def start_defitem(self, pad, extra): 74 self.out("<dd>") 75 76 def end_defitem(self, pad, extra): 77 self.out("</dd>") 78 79 def start_defterm(self, pad, extra): 80 self.out("<dt>") 81 82 def end_defterm(self, pad, extra): 83 self.out("</dt>") 84 85 def start_emphasis(self): 86 self.out("<em>") 87 88 def end_emphasis(self): 89 self.out("</em>") 90 91 def start_heading(self, level, extra, pad, identifier): 92 self.out("<h%d id='%s'>" % (level, escape_attr(self.linker.make_id(identifier)))) 93 94 def end_heading(self, level, pad, extra): 95 self.out("</h%d>" % level) 96 97 def start_larger(self): 98 self.out("<big>") 99 100 def end_larger(self): 101 self.out("</big>") 102 103 def start_linktext(self): 104 pass 105 106 def end_linktext(self): 107 pass 108 109 list_tags = { 110 "i" : "lower-roman", 111 "I" : "upper-roman", 112 "a" : "lower-latin", 113 "A" : "upper-latin", 114 } 115 116 def _get_list_tag(self, marker): 117 if marker: 118 if marker[0].isdigit(): 119 return "ol", "decimal" 120 style_type = self.list_tags.get(marker[0]) 121 if style_type: 122 return "ol", style_type 123 124 return "ul", None 125 126 def start_list(self, indent, marker, num): 127 tag, style_type = self._get_list_tag(marker) 128 style = style_type and ' style="list-style-type: %s"' % escape_attr(style_type) or "" 129 start = style_type and num is not None and ' start="%s"' % escape_attr(num) or "" 130 self.out("<%s%s%s>" % (tag, style, start)) 131 132 def end_list(self, indent, marker, num): 133 tag, style = self._get_list_tag(marker) 134 self.out("</%s>" % tag) 135 136 def start_listitem(self, indent, marker, space, num): 137 self.out("<li>") 138 139 def end_listitem(self, indent, marker, space, num): 140 self.out("</li>") 141 142 def start_macro(self, name, args, nodes): 143 self.out("<span class='macro'>") 144 145 # Fallback case for when macros are not replaced. 146 147 if not nodes: 148 self.out(escape_text("<<")) 149 self.out("<span class='name'>%s</span>" % escape_text(name)) 150 if args: 151 self.out("(") 152 first = True 153 for arg in args: 154 if not first: 155 self.out(",") 156 self.out("<span class='arg'>%s</span>" % escape_text(arg)) 157 first = False 158 if args: 159 self.out(")") 160 self.out(escape_text(">>")) 161 162 def end_macro(self): 163 self.out("</span>") 164 165 def start_monospace(self): 166 self.out("<tt>") 167 168 def end_monospace(self): 169 self.out("</tt>") 170 171 def start_smaller(self): 172 self.out("<small>") 173 174 def end_smaller(self): 175 self.out("</small>") 176 177 def start_strikethrough(self): 178 self.out("<del>") 179 180 def end_strikethrough(self): 181 self.out("</del>") 182 183 def start_strong(self): 184 self.out("<strong>") 185 186 def end_strong(self): 187 self.out("</strong>") 188 189 def start_subscript(self): 190 self.out("<sub>") 191 192 def end_subscript(self): 193 self.out("</sub>") 194 195 def start_superscript(self): 196 self.out("<sup>") 197 198 def end_superscript(self): 199 self.out("</sup>") 200 201 def start_table(self): 202 self.out("<table>") 203 204 def end_table(self): 205 self.out("</table>") 206 207 def start_table_attrs(self): 208 pass 209 210 def end_table_attrs(self): 211 pass 212 213 def start_table_cell(self, attrs): 214 self.out("<td") 215 216 # Handle the attributes separately from their container. 217 218 if attrs and not attrs.empty(): 219 for attr in attrs.nodes: 220 attr.to_string(self) 221 222 self.out(">") 223 224 def end_table_cell(self): 225 self.out("</td>") 226 227 def start_table_row(self): 228 self.out("<tr>") 229 230 def end_table_row(self, trailing): 231 self.out("</tr>") 232 233 def start_underline(self): 234 self.out("<span style='text-decoration: underline'>") 235 236 def end_underline(self): 237 self.out("</span>") 238 239 def anchor(self, target): 240 self.out("<a name='%s' />" % escape_attr(self.linker.make_id(target))) 241 242 def break_(self): 243 pass 244 245 def comment(self, comment, extra): 246 pass 247 248 def directive(self, directive, extra): 249 pass 250 251 def linebreak(self): 252 self.out("<br />") 253 254 def _link(self, target, nodes, tag, attr): 255 label = None 256 if self.linker: 257 target, label = self.linker.translate(target) 258 259 self.out('<%s %s="%s"' % (tag, attr, escape_attr(target))) 260 261 if nodes: 262 for node in nodes[1:]: 263 self.out(" ") 264 node.to_string(self) 265 266 self.out(">") 267 268 if nodes: 269 nodes[0].to_string(self) 270 else: 271 self.out(escape_text(label or target)) 272 273 self.out("</%s>" % tag) 274 275 def link(self, target, nodes): 276 self._link(target, nodes, "a", "href") 277 278 def link_label(self, nodes): 279 for node in nodes: 280 node.to_string(self) 281 282 def link_parameter(self, key_value): 283 if len(key_value) == 1: 284 self.out(key_value[0]) 285 else: 286 key, value = key_value 287 self.out("%s='%s'" % (key, escape_attr(value))) 288 289 def rule(self, length): 290 self.out("<hr style='height: %dpt' />" % min(length, 10)) 291 292 def table_attrs(self, nodes): 293 294 # Skip the attributes in their original form. 295 296 pass 297 298 def table_attr(self, name, value, concise, quote): 299 self.out(" %s%s" % (escape_text(name), value is not None and 300 "='%s'" % escape_attr(value) or "")) 301 302 def text(self, s): 303 self.out(escape_text(s)) 304 305 def transclusion(self, target, nodes): 306 self._link(target, nodes, "img", "src") 307 308 def verbatim(self, s): 309 self.text(s) 310 311 serialiser = HTMLSerialiser 312 313 # vim: tabstop=4 expandtab shiftwidth=4