1 #!/usr/bin/env python 2 3 """ 4 Moin wiki serialisers. 5 6 Copyright (C) 2017 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 def escape_text(s): 23 24 "Escape XML document text." 25 26 return s.replace("&", "&").replace("<", "<").replace(">", ">") 27 28 def escape_attr(s): 29 30 "Escape XML document attribute." 31 32 return escape_text(s).replace("'", "'").replace('"', """) 33 34 class Serialiser: 35 36 "General serialisation support." 37 38 def __init__(self, out): 39 self.out = out 40 41 class MoinSerialiser(Serialiser): 42 43 "Serialisation of the page." 44 45 def start_region(self, level, indent, type): 46 out = self.out 47 if level: 48 out(" " * indent + "{" * level) 49 if type and level: 50 out("#!%s\n" % type) 51 52 def end_region(self, level, indent, type): 53 out = self.out 54 if level: 55 out("}" * level) 56 57 def start_block(self): 58 pass 59 60 def end_block(self): 61 pass 62 63 def start_defitem(self, pad, extra): 64 self.out((extra and "\n" + extra + "::" or "") + pad) 65 66 def end_defitem(self, pad, extra): 67 pass 68 69 def start_defterm(self, pad): 70 self.out(pad) 71 72 def end_defterm(self, pad): 73 self.out("::") 74 75 def start_emphasis(self): 76 self.out("''") 77 78 def end_emphasis(self): 79 self.out("''") 80 81 def start_heading(self, level, extra, pad): 82 self.out(extra + "=" * level + pad) 83 84 def end_heading(self, level, pad, extra): 85 self.out(pad + "=" * level + extra) 86 87 def start_larger(self): 88 self.out("~+") 89 90 def end_larger(self): 91 self.out("+~") 92 93 def start_listitem(self, indent, marker, space): 94 self.out("%s%s%s" % (indent * " ", marker, space)) 95 96 def end_listitem(self, indent, marker): 97 pass 98 99 def start_monospace(self): 100 self.out("`") 101 102 def end_monospace(self): 103 self.out("`") 104 105 def start_smaller(self): 106 self.out("~-") 107 108 def end_smaller(self): 109 self.out("-~") 110 111 def start_strong(self): 112 self.out("'''") 113 114 def end_strong(self): 115 self.out("'''") 116 117 def start_subscript(self): 118 self.out(",,") 119 120 def end_subscript(self): 121 self.out(",,") 122 123 def start_superscript(self): 124 self.out("^") 125 126 def end_superscript(self): 127 self.out("^") 128 129 def start_table(self): 130 pass 131 132 def end_table(self): 133 pass 134 135 def start_table_attrs(self): 136 self.out("<") 137 138 def end_table_attrs(self): 139 self.out(">") 140 141 def start_table_cell(self, attrs): 142 self.out("||") 143 if attrs and not attrs.empty(): 144 attrs.to_string(self) 145 146 def end_table_cell(self): 147 pass 148 149 def start_table_row(self): 150 pass 151 152 def end_table_row(self, trailing): 153 self.out("||") 154 self.out(trailing) 155 156 def start_underline(self): 157 self.out("__") 158 159 def end_underline(self): 160 self.out("__") 161 162 def break_(self): 163 self.out("\n") 164 165 def rule(self, length): 166 self.out("-" * length) 167 168 def table_attr(self, name, value, concise, quote): 169 if concise: 170 if name == "colour": self.out(value) 171 elif name == "colspan": self.out("-%s" % value) 172 elif name == "halign" : self.out(value == "left" and "(" or value == "right" and ")" or ":") 173 elif name == "rowspan": self.out("|%s" % value) 174 elif name == "valign" : self.out(value == "top" and "^" or "v") 175 elif name == "width" : self.out(value) 176 else: 177 self.out("%s%s" % (escape_text(name), value is not None and 178 "=%s%s%s" % (quote or '"', escape_attr(value), quote or '"') or "")) 179 180 def text(self, s): 181 self.out(s) 182 183 class HTMLSerialiser(Serialiser): 184 185 "Serialisation of the page." 186 187 def start_region(self, level, indent, type): 188 l = [] 189 out = l.append 190 if level: 191 out("level-%d" % level) 192 193 if indent: 194 out("indent-%d" % indent) 195 196 # NOTE: Encode type details for CSS. 197 198 if type: 199 out("type-%s" % escape_attr(type)) 200 201 self.out("<span class='%s'>" % " ".join(l)) 202 203 def end_region(self, level, indent, type): 204 self.out("</span>") 205 206 def start_block(self): 207 self.out("<p>") 208 209 def end_block(self): 210 self.out("</p>") 211 212 def start_defitem(self, pad, extra): 213 self.out("<dd>") 214 215 def end_defitem(self, pad, extra): 216 self.out("</dd>") 217 218 def start_defterm(self, pad): 219 self.out("<dt>") 220 221 def end_defterm(self, pad): 222 self.out("</dt>") 223 224 def start_emphasis(self): 225 self.out("<em>") 226 227 def end_emphasis(self): 228 self.out("</em>") 229 230 def start_heading(self, level, extra, pad): 231 self.out("<h%d>" % level) 232 233 def end_heading(self, level, pad, extra): 234 self.out("</h%d>" % level) 235 236 def start_larger(self): 237 self.out("<big>") 238 239 def end_larger(self): 240 self.out("</big>") 241 242 def start_listitem(self, indent, marker, space): 243 self.out("<li>") 244 245 def end_listitem(self, indent, marker): 246 self.out("</li>") 247 248 def start_monospace(self): 249 self.out("<tt>") 250 251 def end_monospace(self): 252 self.out("</tt>") 253 254 def start_smaller(self): 255 self.out("<small>") 256 257 def end_smaller(self): 258 self.out("</small>") 259 260 def start_strong(self): 261 self.out("<strong>") 262 263 def end_strong(self): 264 self.out("</strong>") 265 266 def start_subscript(self): 267 self.out("<sub>") 268 269 def end_subscript(self): 270 self.out("</sub>") 271 272 def start_superscript(self): 273 self.out("<sup>") 274 275 def end_superscript(self): 276 self.out("</sup>") 277 278 def start_table(self): 279 self.out("<table>") 280 281 def end_table(self): 282 self.out("</table>") 283 284 def start_table_attrs(self): 285 pass 286 287 def end_table_attrs(self): 288 pass 289 290 def start_table_cell(self, attrs): 291 self.out("<td") 292 if attrs and not attrs.empty(): 293 attrs.to_string(self) 294 self.out(">") 295 296 def end_table_cell(self): 297 self.out("</td>") 298 299 def start_table_row(self): 300 self.out("<tr>") 301 302 def end_table_row(self, trailing): 303 self.out("</tr>") 304 305 def start_underline(self): 306 self.out("<span style='text-decoration: underline'>") 307 308 def end_underline(self): 309 self.out("</span>") 310 311 def break_(self): 312 pass 313 314 def rule(self, length): 315 self.out("<hr style='height: %dpt' />" % min(length, 10)) 316 317 def table_attr(self, name, value, concise, quote): 318 self.out(" %s%s" % (escape_text(name), value is not None and 319 "='%s'" % escape_attr(value) or "")) 320 321 def text(self, s): 322 self.out(escape_text(s)) 323 324 # Top-level functions. 325 326 def serialise(doc, serialiser=MoinSerialiser): 327 l = [] 328 doc.to_string(serialiser(l.append)) 329 return "".join(l) 330 331 # vim: tabstop=4 expandtab shiftwidth=4