| 1 | import re |
|---|
| 2 | import os |
|---|
| 3 | import urllib |
|---|
| 4 | |
|---|
| 5 | try: |
|---|
| 6 | from cStringIO import StringIO |
|---|
| 7 | except ImportError: |
|---|
| 8 | from StringIO import StringIO |
|---|
| 9 | |
|---|
| 10 | from trac import util |
|---|
| 11 | from trac.mimeview import * |
|---|
| 12 | from trac.wiki.api import WikiSystem |
|---|
| 13 | from trac.wiki.formatter import Formatter |
|---|
| 14 | |
|---|
| 15 | __all__ = ['wiki_to_latex'] |
|---|
| 16 | |
|---|
| 17 | def gtotex(s, boolparam=True): |
|---|
| 18 | return s.replace('&', '\\&{}').replace('$', '\\${}') #etc |
|---|
| 19 | |
|---|
| 20 | def LatexURL(formatter, ns, target, text): |
|---|
| 21 | return gtotex(text) + '\\footnote{\\url{' + ns + ':' + gtotex(target) + '}}'; |
|---|
| 22 | |
|---|
| 23 | class LatexFormatter(Formatter): |
|---|
| 24 | flavor = 'latex' |
|---|
| 25 | |
|---|
| 26 | def __init__(self, env, page, req=None, absurls=0, db=None): |
|---|
| 27 | self.env = env |
|---|
| 28 | self.req = req |
|---|
| 29 | self._db = db |
|---|
| 30 | self._absurls = absurls |
|---|
| 31 | self._anchors = [] |
|---|
| 32 | self._open_tags = [] |
|---|
| 33 | self.href = absurls and env.abs_href or env.href |
|---|
| 34 | self._local = env.config.get('project', 'url', '') or env.abs_href.base |
|---|
| 35 | self.pagename = page.name |
|---|
| 36 | |
|---|
| 37 | def totex(self, s, boolparam=True): |
|---|
| 38 | return s.replace('&', '\\&{}').replace('$', '\\${}') #etc |
|---|
| 39 | |
|---|
| 40 | def tolabel(self, s, boolparam=True): |
|---|
| 41 | return s.replace('&', '').replace('$', '').replace(' ', '-') #etc |
|---|
| 42 | |
|---|
| 43 | def _get_db(self): |
|---|
| 44 | if not self._db: |
|---|
| 45 | self._db = self.env.get_db_cnx() |
|---|
| 46 | return self._db |
|---|
| 47 | db = property(fget=_get_db) |
|---|
| 48 | |
|---|
| 49 | def _get_rules(self): |
|---|
| 50 | return WikiSystem(self.env).rules |
|---|
| 51 | rules = property(_get_rules) |
|---|
| 52 | |
|---|
| 53 | def _get_link_resolvers(self): |
|---|
| 54 | return {'source':LatexURL, 'wiki':LatexURL, 'ticket':LatexURL, 'changeset':LatexURL} #WikiSystem(self.env).link_resolvers |
|---|
| 55 | link_resolvers = property(_get_link_resolvers) |
|---|
| 56 | |
|---|
| 57 | def replace(self, fullmatch): |
|---|
| 58 | wiki = WikiSystem(self.env) |
|---|
| 59 | for itype, match in fullmatch.groupdict().items(): |
|---|
| 60 | if match and not itype in wiki.helper_patterns: |
|---|
| 61 | # Check for preceding escape character '!' |
|---|
| 62 | if match[0] == '!': |
|---|
| 63 | return match[1:] |
|---|
| 64 | elif match[0].isalpha(): |
|---|
| 65 | return LatexURL(self, 'wiki', match, match) |
|---|
| 66 | if itype in wiki.external_handlers: |
|---|
| 67 | return wiki.external_handlers[itype](self, match, fullmatch) |
|---|
| 68 | else: |
|---|
| 69 | return getattr(self, '_' + itype + '_formatter')(match, fullmatch) |
|---|
| 70 | |
|---|
| 71 | def tag_open_p(self, tag): |
|---|
| 72 | """Do we currently have any open tag with @tag as end-tag""" |
|---|
| 73 | return tag in self._open_tags |
|---|
| 74 | |
|---|
| 75 | def close_tag(self, tag): |
|---|
| 76 | tmp = '' |
|---|
| 77 | for i in xrange(len(self._open_tags)-1, -1, -1): |
|---|
| 78 | tmp += self._open_tags[i][1] |
|---|
| 79 | if self._open_tags[i][1] == tag: |
|---|
| 80 | del self._open_tags[i] |
|---|
| 81 | for j in xrange(i, len(self._open_tags)): |
|---|
| 82 | tmp += self._open_tags[j][0] |
|---|
| 83 | break |
|---|
| 84 | return tmp |
|---|
| 85 | |
|---|
| 86 | def open_tag(self, open, close): |
|---|
| 87 | self._open_tags.append((open, close)) |
|---|
| 88 | |
|---|
| 89 | def simple_tag_handler(self, open_tag, close_tag): |
|---|
| 90 | """Generic handler for simple binary style tags""" |
|---|
| 91 | if self.tag_open_p((open_tag, close_tag)): |
|---|
| 92 | return self.close_tag(close_tag) |
|---|
| 93 | else: |
|---|
| 94 | self.open_tag(open_tag, close_tag) |
|---|
| 95 | return open_tag |
|---|
| 96 | |
|---|
| 97 | def _bolditalic_formatter(self, match, fullmatch): |
|---|
| 98 | italic = ('\\emph{', '}') |
|---|
| 99 | italic_open = self.tag_open_p(italic) |
|---|
| 100 | tmp = '' |
|---|
| 101 | if italic_open: |
|---|
| 102 | tmp += italic[1] |
|---|
| 103 | self.close_tag(italic[1]) |
|---|
| 104 | tmp += self._bold_formatter(match, fullmatch) |
|---|
| 105 | if not italic_open: |
|---|
| 106 | tmp += italic[0] |
|---|
| 107 | self.open_tag(*italic) |
|---|
| 108 | return tmp |
|---|
| 109 | |
|---|
| 110 | def _unquote(self, text): |
|---|
| 111 | if text and text[0] in "'\"" and text[0] == text[-1]: |
|---|
| 112 | return text[1:-1] |
|---|
| 113 | else: |
|---|
| 114 | return text |
|---|
| 115 | |
|---|
| 116 | def _shref_formatter(self, match, fullmatch): |
|---|
| 117 | ns = fullmatch.group('sns') |
|---|
| 118 | target = self._unquote(fullmatch.group('stgt')) |
|---|
| 119 | return self._make_link(ns, target, match, match) |
|---|
| 120 | |
|---|
| 121 | def _lhref_formatter(self, match, fullmatch): |
|---|
| 122 | ns = fullmatch.group('lns') |
|---|
| 123 | target = self._unquote(fullmatch.group('ltgt')) |
|---|
| 124 | label = fullmatch.group('label') |
|---|
| 125 | if not label: # e.g. `[http://target]` or `[wiki:target]` |
|---|
| 126 | if target: |
|---|
| 127 | if target.startswith('//'): # for `[http://target]` |
|---|
| 128 | label = ns+':'+target # use `http://target` |
|---|
| 129 | else: # for `wiki:target` |
|---|
| 130 | label = target # use only `target` |
|---|
| 131 | else: # e.g. `[search:]` |
|---|
| 132 | label = ns |
|---|
| 133 | label = self._unquote(label) |
|---|
| 134 | rel = fullmatch.group('rel') |
|---|
| 135 | if rel: |
|---|
| 136 | return self._make_relative_link(rel, label or rel) |
|---|
| 137 | else: |
|---|
| 138 | return self._make_link(ns, target, match, label) |
|---|
| 139 | |
|---|
| 140 | def _make_link(self, ns, target, match, label): |
|---|
| 141 | if ns in self.link_resolvers: |
|---|
| 142 | return self.link_resolvers[ns](self, ns, target, |
|---|
| 143 | self.totex(label, False)) |
|---|
| 144 | elif target.startswith('//') or ns == "mailto": |
|---|
| 145 | return self._make_ext_link(ns+':'+target, label) |
|---|
| 146 | else: |
|---|
| 147 | return self.totex(match) |
|---|
| 148 | |
|---|
| 149 | def _make_ext_link(self, url, text, title=''): |
|---|
| 150 | url = self.totex(url) |
|---|
| 151 | text, title = self.totex(text), self.totex(title) |
|---|
| 152 | title_attr = title and ' title="%s"' % title or '' |
|---|
| 153 | if Formatter.img_re.search(url) and self.flavor != 'oneliner': |
|---|
| 154 | return '\\url{%s} (extimage - todo: make figure float for %s)' % (url, title or text) |
|---|
| 155 | if not url.startswith(self._local): |
|---|
| 156 | return '%s\\footnote{\\url{%s}}' % (text, url) |
|---|
| 157 | else: |
|---|
| 158 | return '\\url{%s} (ext: %s)' % (url, text) |
|---|
| 159 | |
|---|
| 160 | def _make_relative_link(self, url, text): |
|---|
| 161 | url, text = self.totex(url), self.totex(text) |
|---|
| 162 | if Formatter.img_re.search(url) and self.flavor != 'oneliner': |
|---|
| 163 | return '\\url{%s} (image - todo: make figure float for %s)' % (url, title or text) |
|---|
| 164 | if not url.startswith(self._local): |
|---|
| 165 | return '\\url{%s} (local: %s)' % (url, text) |
|---|
| 166 | else: |
|---|
| 167 | return '\\url{%s} (%s) or Subsection \\ref{url}' % (url, text) |
|---|
| 168 | |
|---|
| 169 | def _bold_formatter(self, match, fullmatch): |
|---|
| 170 | return self.simple_tag_handler('\\textbf{', '}') |
|---|
| 171 | |
|---|
| 172 | def _italic_formatter(self, match, fullmatch): |
|---|
| 173 | return self.simple_tag_handler('\\textit{', '}') |
|---|
| 174 | |
|---|
| 175 | def _underline_formatter(self, match, fullmatch): |
|---|
| 176 | if match[0] == '!': |
|---|
| 177 | return match[1:] |
|---|
| 178 | else: |
|---|
| 179 | return self.simple_tag_handler('\\underbar{', |
|---|
| 180 | '}') |
|---|
| 181 | |
|---|
| 182 | def _strike_formatter(self, match, fullmatch): |
|---|
| 183 | if match[0] == '!': |
|---|
| 184 | return match[1:] |
|---|
| 185 | else: |
|---|
| 186 | return self.simple_tag_handler('\\underbar{', '} (strike)') |
|---|
| 187 | |
|---|
| 188 | def _subscript_formatter(self, match, fullmatch): |
|---|
| 189 | if match[0] == '!': |
|---|
| 190 | return match[1:] |
|---|
| 191 | else: |
|---|
| 192 | return self.simple_tag_handler('$_{', '}$') |
|---|
| 193 | |
|---|
| 194 | def _superscript_formatter(self, match, fullmatch): |
|---|
| 195 | if match[0] == '!': |
|---|
| 196 | return match[1:] |
|---|
| 197 | else: |
|---|
| 198 | return self.simple_tag_handler('$^{', '}$') |
|---|
| 199 | |
|---|
| 200 | def _inlinecode_formatter(self, match, fullmatch): |
|---|
| 201 | return '\\texttt{%s}' % self.totex(fullmatch.group('inline')) |
|---|
| 202 | |
|---|
| 203 | def _inlinecode2_formatter(self, match, fullmatch): |
|---|
| 204 | return '\\texttt{%s}' % self.totex(fullmatch.group('inline2')) |
|---|
| 205 | |
|---|
| 206 | def _htmlescape_formatter(self, match, fullmatch): |
|---|
| 207 | return match == "&" and "\\&{}" or \ |
|---|
| 208 | match == "<" and "\\textless{}" or "\\textgreater{}" |
|---|
| 209 | |
|---|
| 210 | def _macro_formatter(self, match, fullmatch): |
|---|
| 211 | name = fullmatch.group('macroname') |
|---|
| 212 | if name in ['br', 'BR']: |
|---|
| 213 | return '\\\\' |
|---|
| 214 | args = fullmatch.group('macroargs') |
|---|
| 215 | try: |
|---|
| 216 | macro = WikiProcessor(self.env, name) |
|---|
| 217 | return macro.process(self.req, args, 1) |
|---|
| 218 | except Exception, e: |
|---|
| 219 | self.env.log.error('Macro %s(%s) failed' % (name, args), |
|---|
| 220 | exc_info=True) |
|---|
| 221 | return system_message('Error: Macro %s(%s) failed' % (name, args), e) |
|---|
| 222 | |
|---|
| 223 | def _heading_formatter(self, match, fullmatch): |
|---|
| 224 | match = match.strip() |
|---|
| 225 | self.close_table() |
|---|
| 226 | self.close_paragraph() |
|---|
| 227 | self.close_indentation() |
|---|
| 228 | self.close_list() |
|---|
| 229 | self.close_def_list() |
|---|
| 230 | |
|---|
| 231 | depth = min(len(fullmatch.group('hdepth')), 5) |
|---|
| 232 | heading = match[depth + 1:len(match) - depth - 1] |
|---|
| 233 | |
|---|
| 234 | anchor = text = heading |
|---|
| 235 | sans_markup = re.sub(r'</?\w+(?: .*?)?>', '', text) |
|---|
| 236 | |
|---|
| 237 | #check if valid LaTeX label |
|---|
| 238 | |
|---|
| 239 | anchor = anchor_base = anchor.encode('utf-8') |
|---|
| 240 | while anchor in self._anchors: |
|---|
| 241 | anchor = anchor_base + str(i) |
|---|
| 242 | i += 1 |
|---|
| 243 | self._anchors.append(anchor) |
|---|
| 244 | self.out.write('\\subsubsection{\\label{anchor:%s}%s}' % (self.tolabel(anchor), self.totex(text))) |
|---|
| 245 | |
|---|
| 246 | def _indent_formatter(self, match, fullmatch): |
|---|
| 247 | depth = int((len(fullmatch.group('idepth')) + 1) / 2) |
|---|
| 248 | list_depth = len(self._list_stack) |
|---|
| 249 | if list_depth > 0 and depth == list_depth + 1: |
|---|
| 250 | self.in_list_item = 1 |
|---|
| 251 | else: |
|---|
| 252 | self.open_indentation(depth) |
|---|
| 253 | return '' |
|---|
| 254 | |
|---|
| 255 | def _last_table_cell_formatter(self, match, fullmatch): |
|---|
| 256 | return '' |
|---|
| 257 | |
|---|
| 258 | def _table_cell_formatter(self, match, fullmatch): |
|---|
| 259 | self.open_table() |
|---|
| 260 | self.open_table_row() |
|---|
| 261 | if self.in_table_cell: |
|---|
| 262 | return ' & ' |
|---|
| 263 | else: |
|---|
| 264 | self.in_table_cell = 1 |
|---|
| 265 | return '\\\\' |
|---|
| 266 | |
|---|
| 267 | def close_indentation(self): |
|---|
| 268 | self.out.write(('\\end{quote}' + os.linesep) * self.indent_level) |
|---|
| 269 | self.indent_level = 0 |
|---|
| 270 | |
|---|
| 271 | def open_indentation(self, depth): |
|---|
| 272 | if self.in_def_list: |
|---|
| 273 | return |
|---|
| 274 | diff = depth - self.indent_level |
|---|
| 275 | if diff != 0: |
|---|
| 276 | self.close_paragraph() |
|---|
| 277 | self.close_indentation() |
|---|
| 278 | self.close_list() |
|---|
| 279 | self.indent_level = depth |
|---|
| 280 | self.out.write(('\\begin{quote}' + os.linesep) * depth) |
|---|
| 281 | |
|---|
| 282 | def _list_formatter(self, match, fullmatch): |
|---|
| 283 | ldepth = len(fullmatch.group('ldepth')) |
|---|
| 284 | depth = int((len(fullmatch.group('ldepth')) + 1) / 2) |
|---|
| 285 | self.in_list_item = depth > 0 |
|---|
| 286 | type_ = ['ol', 'ul'][match[ldepth] == '*'] |
|---|
| 287 | self._set_list_depth(depth, type_) |
|---|
| 288 | return '' |
|---|
| 289 | |
|---|
| 290 | def _definition_formatter(self, match, fullmatch): |
|---|
| 291 | tmp = self.in_def_list and '' or '\\begin{definition}\n' |
|---|
| 292 | tmp += '\\item %s ' % wiki_to_oneliner(match[:-2], self.env, |
|---|
| 293 | self.db) |
|---|
| 294 | self.in_def_list = True |
|---|
| 295 | return tmp |
|---|
| 296 | |
|---|
| 297 | def close_def_list(self): |
|---|
| 298 | if self.in_def_list: |
|---|
| 299 | self.out.write('\\end{definition}\n') |
|---|
| 300 | self.in_def_list = False |
|---|
| 301 | |
|---|
| 302 | def _hl_to_ll(self, l): |
|---|
| 303 | if l == 'ul': |
|---|
| 304 | return 'itemize' |
|---|
| 305 | else: |
|---|
| 306 | return 'enumerate' |
|---|
| 307 | |
|---|
| 308 | def _set_list_depth(self, depth, type_): |
|---|
| 309 | current_depth = len(self._list_stack) |
|---|
| 310 | diff = depth - current_depth |
|---|
| 311 | self.close_table() |
|---|
| 312 | self.close_paragraph() |
|---|
| 313 | self.close_indentation() |
|---|
| 314 | if diff > 0: |
|---|
| 315 | for i in range(diff): |
|---|
| 316 | self._list_stack.append(type_) |
|---|
| 317 | self.out.write('\\begin{' + self._hl_to_ll(type_) + '}\n') |
|---|
| 318 | self.out.write('\n\\item ') |
|---|
| 319 | elif diff < 0: |
|---|
| 320 | for i in range(-diff): |
|---|
| 321 | tmp = self._list_stack.pop() |
|---|
| 322 | self.out.write('\\end{' + self._hl_to_ll(type_) + '}\n') |
|---|
| 323 | if self._list_stack != [] and type_ != self._list_stack[-1]: |
|---|
| 324 | tmp = self._list_stack.pop() |
|---|
| 325 | self._list_stack.append(type_) |
|---|
| 326 | self.out.write('\n\\end{%s}\n\\begin{%s}\n\\item ' % (self._hl_to_ll(tmp), self._hl_to_ll(type_))) |
|---|
| 327 | if depth > 0: |
|---|
| 328 | self.out.write('\n\\item ') |
|---|
| 329 | # diff == 0 |
|---|
| 330 | elif self._list_stack != [] and type_ != self._list_stack[-1]: |
|---|
| 331 | tmp = self._list_stack.pop() |
|---|
| 332 | self._list_stack.append(type_) |
|---|
| 333 | self.out.write('\n\\end{%s}\n\\begin{%s}\n\\item ' % (self._hl_to_ll(tmp), self._hl_to_ll(type_))) |
|---|
| 334 | elif depth > 0: |
|---|
| 335 | self.out.write('\n\\item ') |
|---|
| 336 | |
|---|
| 337 | def close_list(self): |
|---|
| 338 | if self._list_stack != []: |
|---|
| 339 | self._set_list_depth(0, None) |
|---|
| 340 | |
|---|
| 341 | def open_paragraph(self): |
|---|
| 342 | if not self.paragraph_open: |
|---|
| 343 | self.out.write(os.linesep) |
|---|
| 344 | self.paragraph_open = 1 |
|---|
| 345 | |
|---|
| 346 | def close_paragraph(self): |
|---|
| 347 | if self.paragraph_open: |
|---|
| 348 | while self._open_tags != []: |
|---|
| 349 | self.out.write(self._open_tags.pop()[1]) |
|---|
| 350 | self.out.write(os.linesep) |
|---|
| 351 | self.paragraph_open = 0 |
|---|
| 352 | |
|---|
| 353 | def open_table(self): |
|---|
| 354 | if not self.in_table: |
|---|
| 355 | self.close_paragraph() |
|---|
| 356 | self.close_indentation() |
|---|
| 357 | self.close_list() |
|---|
| 358 | self.close_def_list() |
|---|
| 359 | self.in_table = 1 |
|---|
| 360 | self.out.write('\\begin{tablular}' + os.linesep) |
|---|
| 361 | |
|---|
| 362 | def open_table_row(self): |
|---|
| 363 | if not self.in_table_row: |
|---|
| 364 | self.open_table() |
|---|
| 365 | self.in_table_row = 1 |
|---|
| 366 | self.out.write(os.linesep) |
|---|
| 367 | |
|---|
| 368 | def close_table_row(self): |
|---|
| 369 | if self.in_table_row: |
|---|
| 370 | self.in_table_row = 0 |
|---|
| 371 | if self.in_table_cell: |
|---|
| 372 | self.in_table_cell = 0 |
|---|
| 373 | self.out.write('') |
|---|
| 374 | |
|---|
| 375 | self.out.write('\\\\') |
|---|
| 376 | |
|---|
| 377 | def close_table(self): |
|---|
| 378 | if self.in_table: |
|---|
| 379 | self.close_table_row() |
|---|
| 380 | self.out.write('\\end{tablular}' + os.linesep) |
|---|
| 381 | self.in_table = 0 |
|---|
| 382 | |
|---|
| 383 | def handle_code_block(self, line): |
|---|
| 384 | if line.strip() == '{{{': |
|---|
| 385 | self.in_code_block += 1 |
|---|
| 386 | if self.in_code_block == 1: |
|---|
| 387 | self.out.write('\\begin{verbatim}' + os.linesep) |
|---|
| 388 | else: |
|---|
| 389 | self.out.write(line + os.linesep) |
|---|
| 390 | elif line.strip() == '}}}': |
|---|
| 391 | self.in_code_block -= 1 |
|---|
| 392 | if self.in_code_block == 0: |
|---|
| 393 | self.out.write('\\end{verbatim}' + os.linesep) |
|---|
| 394 | else: |
|---|
| 395 | self.out.write(line + os.linesep) |
|---|
| 396 | else: |
|---|
| 397 | self.out.write(line + os.linesep) |
|---|
| 398 | |
|---|
| 399 | def preamble(self): |
|---|
| 400 | self.secname = self.pagename |
|---|
| 401 | self.out.write('\\documentclass{article}' + os.linesep) |
|---|
| 402 | self.out.write('\\usepackage{url}' + os.linesep) |
|---|
| 403 | self.out.write('\\begin{document}' + os.linesep) |
|---|
| 404 | self.out.write('\\subsection{\\label{sub:' + self.secname + '}' + self.secname + '}' + os.linesep) |
|---|
| 405 | |
|---|
| 406 | def format(self, text, out, escape_newlines=False): |
|---|
| 407 | self.out = out |
|---|
| 408 | self._open_tags = [] |
|---|
| 409 | self._list_stack = [] |
|---|
| 410 | |
|---|
| 411 | self.in_code_block = 0 |
|---|
| 412 | self.in_table = 0 |
|---|
| 413 | self.in_def_list = 0 |
|---|
| 414 | self.in_table_row = 0 |
|---|
| 415 | self.in_table_cell = 0 |
|---|
| 416 | self.indent_level = 0 |
|---|
| 417 | self.paragraph_open = 0 |
|---|
| 418 | |
|---|
| 419 | self.preamble() |
|---|
| 420 | |
|---|
| 421 | for line in text.splitlines(): |
|---|
| 422 | # Handle code block |
|---|
| 423 | if self.in_code_block or line.strip() == '{{{': |
|---|
| 424 | self.handle_code_block(line) |
|---|
| 425 | continue |
|---|
| 426 | # Handle Horizontal ruler |
|---|
| 427 | elif line[0:4] == '----': |
|---|
| 428 | self.close_paragraph() |
|---|
| 429 | self.close_indentation() |
|---|
| 430 | self.close_list() |
|---|
| 431 | self.close_def_list() |
|---|
| 432 | self.close_table() |
|---|
| 433 | self.out.write('{\\normalsize \\vspace{1ex} \\hrule width \\columnwidth \\vspace{1ex}}' + os.linesep) |
|---|
| 434 | continue |
|---|
| 435 | # Handle new paragraph |
|---|
| 436 | elif line == '': |
|---|
| 437 | self.close_paragraph() |
|---|
| 438 | self.close_indentation() |
|---|
| 439 | self.close_list() |
|---|
| 440 | self.close_def_list() |
|---|
| 441 | continue |
|---|
| 442 | |
|---|
| 443 | if escape_newlines: |
|---|
| 444 | line += ' [[BR]]' |
|---|
| 445 | self.in_list_item = False |
|---|
| 446 | # Throw a bunch of regexps on the problem |
|---|
| 447 | result = re.sub(self.rules, self.replace, line) |
|---|
| 448 | |
|---|
| 449 | if not self.in_list_item: |
|---|
| 450 | self.close_list() |
|---|
| 451 | |
|---|
| 452 | if self.in_def_list and not line.startswith(' '): |
|---|
| 453 | self.close_def_list() |
|---|
| 454 | |
|---|
| 455 | if self.in_table and line[0:2] != '||': |
|---|
| 456 | self.close_table() |
|---|
| 457 | |
|---|
| 458 | if len(result) and not self.in_list_item and not self.in_def_list \ |
|---|
| 459 | and not self.in_table: |
|---|
| 460 | self.open_paragraph() |
|---|
| 461 | out.write(result + os.linesep) |
|---|
| 462 | self.close_table_row() |
|---|
| 463 | |
|---|
| 464 | self.close_table() |
|---|
| 465 | self.close_paragraph() |
|---|
| 466 | self.close_indentation() |
|---|
| 467 | self.close_list() |
|---|
| 468 | self.close_def_list() |
|---|
| 469 | self.out.write('\\end{document}') |
|---|
| 470 | |
|---|
| 471 | def wiki_to_latex(page, env, req, db=None, absurls=0, escape_newlines=False): |
|---|
| 472 | out = StringIO() |
|---|
| 473 | LatexFormatter(env, page, req, absurls, db).format(page.text, out, escape_newlines) |
|---|
| 474 | return util.Markup(out.getvalue()) |
|---|