1class Doc2Latex: 2 sizes = ('tiny', 'scriptsize', 'footnotesize', 'small', 3 'normalsize', 'large', 'Large', 'LARGE', 'huge', 'Huge') 4 5 def __init__(self, mod, doc, node): 6 self.mod = mod 7 self.doc = doc 8 self.encoder = Encoder(mod) 9 self.encode = self.encoder.encode 10 self.node = node 11 self.out = [] 12 self.ms = [] 13 self.mode = None 14 self.cur_style = 'rm' 15 self.cur_size = list(self.sizes).index('normalsize') 16 self.document_lang = None 17 self.document_title = None 18 self.document_metas = [] 19 self.latex_list_nesting = 0 20 self.latex_mode = 0 21 self.noindent = 0 22 self.authors = [] 23 24 node.accept(self) 25 26 def _visit_children(self, node): 27 E = self.mod.ReportedError 28 for ch in node.children: 29 try: 30 ch.accept(self) 31 except E: 32 pass 33 34 def abs_size(self, size, node): 35 osize = self.cur_size 36 si = size 37 if si < 0: 38 si = 0 39 elif si >= len(self.sizes): 40 si = len(self.sizes) - 1 41 self.append('{\\%s ' % self.sizes[si]) 42 self.cur_size = si 43 if self.cur_style != 'rm': 44 self.style(self.cur_style, node) 45 else: 46 node.arg_accept(self) 47 self.append('}') 48 self.cur_size = osize 49 50 def append(self, x): 51 self.out.append(x) 52 53 def changed_size(self, delta, node): 54 self.abs_size(self.cur_size + delta, node) 55 56 def error(self, msg, *args, **kwds): 57 msg = 'Doc2Latex: ' + msg 58 self.doc.env.error(msg, *args, **kwds) 59 60 def get_latex(self): 61 return ''.join(self.out) 62 63 def no_children(self, node): 64 if node.children: 65 self.error('No children allowed for %r.' % 66 node.tag, node.children[0]) 67 68 def style(self, style, node): 69 self.append('{\\%s ' % style) 70 ostyle = self.cur_style 71 self.cur_style = style 72 node.arg_accept(self) 73 self.cur_style = ostyle 74 if style == 'em': 75 self.append('\\/}') 76 else: 77 self.append('\\/}') 78 79 def visit_a(self, node): 80 pass 81 82 def visit_author(self, node): 83 self.authors.append(node.arg) 84 self.no_children(node) 85 86 def visit_big(self, node): 87 self.changed_size(1, node) 88 89 def visit_block(self, node): 90 self._visit_children(node) 91 92 def visit_blockquote(self, node): 93 self.append('\\begin{quote}\n') 94 self.latex_list_nesting += 1 95 96 node.arg_accept(self) 97 98 self.latex_list_nesting -= 1 99 self.append('\\end{quote}\n') 100 101 char_table = { 102 'nbsp': '~', 103 } 104 105 def visit_char(self, node): 106 char = node.arg.strip() 107 c = self.char_table.get(char) 108 if c is None: 109 self.error('No such character: %r.' % char, node) 110 c = char 111 self.append(c) 112 113 def visit_code(self, node): 114 self.style('tt', node) 115 116 def visit_comment(self, node): 117 pass 118 119 def visit_dd(self, node): 120 self.ms.append('dd') 121 step = 24 122 ls = (self.ms.count('dd') + self.latex_list_nesting) * step 123 self.append('{\\par \\noindent \\leftskip = %d pt ' % ls) 124 for i, v in enumerate(('i', 'ii', 'iii', 'iv', 'v', 'vi')[self.latex_list_nesting:]): 125 self.append(' \\leftmargin%s = %d pt ' % (v, ls + (i + 1) * step)) 126 node.arg_accept(self) 127 self.append('\\par}\n') 128 self.ms.pop() 129 130 def visit_default(self, node): 131 self.error('I don\'t know what to generate for the tag %r.' % 132 node.tag, node) 133 134 def visit_define(self, node): 135 # xxx 136 self._visit_children(node) 137 138 def visit_dl(self, node): 139 if self.ms and self.ms[-1] == 'dt': 140 self.visit_dd(node) 141 else: 142 self.append('{\\par \\noindent\n') 143 self._visit_children(node) 144 self.append('\\par}\n') 145 146 def visit_dt(self, node): 147 self.ms.append('dt') 148 self.append('{\\par \\pagebreak[%f] \\noindent \\hangindent = 12 pt \\hangafter = 1 \n' % ( 149 3.4-0.1*len(self.ms), 150 )) 151 node.arg_accept(self) 152 self.append('\\par}\n') 153 self.ms.pop() 154 155 def visit_document(self, node): 156 self._visit_children(node) 157 158 def visit_document_lang(self, node): 159 if self.document_lang is not None: 160 self.error('Duplicate document lang directive.', node) 161 self.document_lang = node 162 163 def visit_document_title(self, node): 164 if self.document_title is not None: 165 self.error('Duplicate document title directive.', node) 166 self.document_title = node 167 168 def visit_exdefs(self, node): 169 self.symplace = {} 170 for ch in node.children: 171 syms = [x.strip() for x in ch.arg.split(',')] 172 for sym in syms: 173 self.symplace[sym] = ch.tag 174 175 def visit_em(self, node): 176 self.style('em', node) 177 178 def visit_enumerate(self, node): 179 self.append('\\begin{enumerate}\n') 180 for c in node.children: 181 self.append('\\item ') 182 c.accept(self) 183 self.append('\\end{enumerate}\n') 184 185 def visit_h0(self, node): 186 # Not a html header, 187 # we may treat this as 'new page' or chapter here 188 # and some larger divisor in html. 189 self.visit_hx(node) 190 191 def visit_h1(self, node): 192 self.visit_hx(node) 193 194 def visit_h2(self, node): 195 self.visit_hx(node) 196 197 def visit_h3(self, node): 198 self.visit_hx(node) 199 200 def visit_h4(self, node): 201 self.visit_hx(node) 202 203 def visit_h5(self, node): 204 self.visit_hx(node) 205 206 def visit_h6(self, node): 207 self.visit_hx(node) 208 209 def visit_hx(self, node): 210 n = int(node.tag[1:]) 211 if self.mode == 'man_page': 212 self.append('{\\par \\pagebreak[%d] \\vskip %d pt \\noindent\n' % ( 213 [4, 3, 3, 2, 2, 1, 1][n], 214 (12 - 2 * n))) 215 self.abs_size(len(self.sizes) - n - 2, self.mod.node_of_taci( 216 '', '', 217 [self.mod.node_of_taci('strong', node.arg, node.children)])) 218 self.append('\\par \\vskip %d pt\n} \\noindent\n' % (12 - 2 * n)) 219 self.noindent = 1 220 # self.append('\\end{list}\n') 221 else: 222 self.append('\\%s{' % self.mod.section_table[n]) 223 node.arg_accept(self) 224 self.append('}\n') 225 226 def visit_itemize(self, node): 227 self.append('\\begin{itemize}\n') 228 self.latex_list_nesting += 1 229 for c in node.children: 230 self.append('\\item ') 231 c.accept(self) 232 233 self.latex_list_nesting -= 1 234 self.append('\\end{itemize}\n') 235 236 def visit_latex(self, node): 237 self.latex_mode += 1 238 node.arg_accept(self) 239 self.latex_mode -= 1 240 241 def visit_li(self, node): 242 self.append('\\item ') 243 node.arg_accept(self) 244 245 def visit_link_to(self, node): 246 # xxx 247 name = node.arg 248 self.append(' {\\em ') 249 if not node.children: 250 self.append(self.encode(name)) 251 else: 252 self._visit_children(node) 253 self.append('\\/}') 254 255 def visit_link_to_extern(self, node): 256 # xxx 257 name = node.arg 258 doc = node.children[0].arg 259 children = node.children[1:] 260 self.append(' {\\em ') 261 if not children: 262 self.append(self.encode(name)) 263 else: 264 for ch in children: 265 ch.accept(self) 266 self.append('\\/}') 267 268 def visit_link_to_local(self, node): 269 # xxx 270 name = node.arg 271 self.append(' {\\em ') 272 if not node.children: 273 self.append(self.encode(name)) 274 else: 275 self._visit_children(node) 276 self.append('\\/}') 277 278 def visit_link_to_unresolved(self, node): 279 # xxx 280 name = node.arg 281 self.append(' {\\em ') 282 if not node.children: 283 self.append(self.encode(name)) 284 else: 285 self._visit_children(node) 286 self.append('\\/}') 287 288 def visit_literal_block(self, node): 289 self.append('{\\ttfamily \\raggedright \\noindent') 290 self.encoder.literal_block = 1 291 self.encoder.insert_none_breaking_blanks = 1 292 293 node.arg_accept(self) 294 self.encoder.literal_block = 0 295 self.encoder.insert_none_breaking_blanks = 0 296 self.append('}\n') 297 298 def visit_lp(self, node): 299 self.latex_mode += 1 300 self.visit_paragraph(node) 301 self.latex_mode -= 1 302 303 def visit_man_page_mode(self, node): 304 omode = self.mode 305 self.mode = 'man_page' 306 self._visit_children(node) 307 self.mode = omode 308 309 def visit_meta(self, node): 310 self.document_metas.append(node) 311 312 def visit_ol(self, node): 313 self.append('\\begin{enumerate}\n') 314 self._visit_children(node) 315 self.append('\\end{enumerate}\n') 316 317 def visit_p(self, node): 318 self.visit_paragraph(node) 319 320 def visit_paragraph(self, node): 321 self.append('{\\par ') 322 if self.noindent: 323 self.append('\\parindent = 0 pt ') 324 self.noindent = 0 325 self.append('\n') 326 node.arg_accept(self) 327 self.append(' \\par}\n') 328 329 def visit_pre(self, node): 330 # I couldn't use Latex verbatim environment 331 # since it didn't respected leftskip 332 # so the environment became misplaced (within dd) 333 334 text = node.arg.strip() 335 if text: 336 text += '\n' 337 text = text + node.get_text() 338 text = text.expandtabs() 339 lines = text.split('\n') 340 if lines and not lines[-1]: 341 lines.pop() 342 if not lines: 343 return 344 self.append('\\par\n') 345 self.encoder.insert_none_breaking_blanks += 1 346 self.encoder.literal += 1 347 first = 1 348 self.append('{\\tt{%s}}\n' % self.encode(lines[0])) 349 for line in lines[1:]: 350 self.append( 351 '{ \\par \\parindent = 0 pt \\parskip = 0 pt \\tt{%s} }\n' % 352 self.encode(line)) 353 self.encoder.insert_none_breaking_blanks -= 1 354 self.encoder.literal -= 1 355 self.append('\\par\n') 356 357 def visit_small(self, node): 358 self.changed_size(-1, node) 359 360 def visit_spc_colonkind(self, node): 361 self.append('~{\\bf :} ') 362 363 def visit_spc_mapsto(self, node): 364 self.append(' \\(\mapsto \\) ') 365 366 def visit_string(self, node): 367 self._visit_children(node) 368 369 def visit_strong(self, node): 370 self.style('bf', node) 371 372 def visit_sub(self, node): 373 self.append('\\raisebox{-.6ex}{') 374 self.changed_size(-1, node) 375 self.append('}') 376 377 def visit_sup(self, node): 378 self.append('\\raisebox{.6ex}{') 379 self.changed_size(-1, node) 380 self.append('}') 381 382 def visit_symbol(self, node): 383 self.visit_text(node) 384 385 def visit_table(self, node): 386 Table(self, node) 387 388 def visit_text(self, node): 389 if self.latex_mode: 390 self.append(node.arg) 391 else: 392 text = node.arg 393 text = self.encoder.encode(text) 394 self.append(text) 395 self._visit_children(node) 396 397 def visit_to_document_only(self, node): 398 self._visit_children(node) 399 400 def visit_to_html_only(self, node): 401 pass 402 403 def visit_to_tester_only(self, node): 404 pass 405 406 def visit_tt(self, node): 407 408 self.append('\\texttt{') 409 self.encoder.literal = 1 410 node.arg_accept(self) 411 self.encoder.literal = 0 412 self.append('}') 413 414 def visit_ul(self, node): 415 self.append('\\begin{itemize}\n') 416 self._visit_children(node) 417 self.append('\\end{itemize}\n') 418 419 def visit_var(self, node): 420 self.style('em', node) 421 422 423class Table(Doc2Latex): 424 many_hlines = 1 # Use extra many hlines.. looks good, a matter of taste. 425 426 def __init__(self, d2l, node): 427 self.d2l = d2l 428 self.__dict__.update(d2l.__dict__) 429 self.node = node 430 self.out = [] 431 self.rows = [] 432 self.colwidth = None 433 434 self._visit_children(node) 435 436 maxcols = 0 437 for row in self.rows: 438 if len(row.columns) > maxcols: 439 maxcols = len(row.columns) 440 441 if not maxcols: 442 return # Empty table 443 if self.colwidth is not None: 444 if not len(self.colwidth) == maxcols: 445 self.error("Wrong number of column width specifications (%d) vs\n" 446 " max columns in table (%d)." % ( 447 len(self.colwidth), maxcols), 448 node) 449 else: 450 self.colwidth = [1.0/maxcols]*maxcols 451 ap = self.d2l.append 452 ap('\n\\begin{longtable}[c]{|%s|}\n' % ('|'.join(['p{%.2g\\linewidth}' % cw 453 for cw in self.colwidth]))) 454 if self.many_hlines: 455 ap('\\hline\n') 456 for row in self.rows: 457 for col in row.columns: 458 ap(''.join(col.data)) 459 if col is row.columns[-1]: 460 if self.many_hlines: 461 ap('\\\\\n') 462 ap('\\hline\n') 463 else: 464 if row is not self.rows[-1]: 465 ap('\\\\\n') 466 else: 467 ap('&\n') 468 if row.is_head: 469 ap('\\hline\n') 470 ap('\\endhead\n') 471 ap('\n\\end{longtable}\n') 472 473 def visit_colgroup(self, node): 474 colwidth = [] 475 476 for c in node.children: 477 if c.tag != "col_width": 478 self.error('Unrecognized colgroup option: %r' % c.tag, c) 479 cg = c.arg 480 if cg.endswith('%'): 481 cg = cg[:-1] 482 cg = float(cg)/100.0 483 else: 484 cg = float(cg) 485 colwidth.append(cg) 486 self.colwidth = colwidth 487 488 def visit_options(self, node): 489 pass 490 491 def visit_thead(self, node): 492 self._visit_children(node) 493 self.rows[-1].is_head = 1 494 495 def visit_tr(self, node): 496 self.rows.append(Row(self, node)) 497 498 499class Row(Doc2Latex): 500 is_head = 0 501 502 def __init__(self, table, node): 503 self.__dict__.update(table.__dict__) 504 self.columns = [] 505 self._visit_children(node) 506 507 def visit_td(self, node): 508 self.columns.append(Column(self, node)) 509 510 def visit_th(self, node): 511 self.columns.append(Column(self, node)) 512 513 514class Column(Doc2Latex): 515 def __init__(self, row, node): 516 self.__dict__.update(row.__dict__) 517 self.data = [] 518 self.append = self.data.append 519 node.arg_accept(self) 520 521 522class Babel: 523 """Language specifics for LaTeX.""" 524 # country code by a.schlock. 525 # partly manually converted from iso and babel stuff, dialects and some 526 _ISO639_TO_BABEL = { 527 'no': 'norsk', # XXX added by hand ( forget about nynorsk?) 528 'gd': 'scottish', # XXX added by hand 529 'hu': 'magyar', # XXX added by hand 530 'pt': 'portuguese', # XXX added by hand 531 'sl': 'slovenian', 532 'af': 'afrikaans', 533 'bg': 'bulgarian', 534 'br': 'breton', 535 'ca': 'catalan', 536 'cs': 'czech', 537 'cy': 'welsh', 538 'da': 'danish', 539 'fr': 'french', 540 # french, francais, canadien, acadian 541 'de': 'ngerman', # XXX rather than german 542 # ngerman, naustrian, german, germanb, austrian 543 'el': 'greek', 544 'en': 'english', 545 # english, USenglish, american, UKenglish, british, canadian 546 'eo': 'esperanto', 547 'es': 'spanish', 548 'et': 'estonian', 549 'eu': 'basque', 550 'fi': 'finnish', 551 'ga': 'irish', 552 'gl': 'galician', 553 'he': 'hebrew', 554 'hr': 'croatian', 555 'hu': 'hungarian', 556 'is': 'icelandic', 557 'it': 'italian', 558 'la': 'latin', 559 'nl': 'dutch', 560 'pl': 'polish', 561 'pt': 'portuguese', 562 'ro': 'romanian', 563 'ru': 'russian', 564 'sk': 'slovak', 565 'sr': 'serbian', 566 'sv': 'swedish', 567 'tr': 'turkish', 568 'uk': 'ukrainian' 569 } 570 571 def __init__(self, mod): 572 self.language = mod.language_code 573 self.re = mod.re 574 575 # pdflatex does not produce double quotes for ngerman in tt. 576 self.double_quote_replacment = None 577 if self.re.search('^de', self.language): 578 #self.quotes = ("\"`", "\"'") 579 self.quotes = ('{\\glqq}', '{\\grqq}') 580 self.double_quote_replacment = "{\\dq}" 581 else: 582 self.quotes = ("``", "''") 583 self.quote_index = 0 584 585 def next_quote(self): 586 q = self.quotes[self.quote_index] 587 self.quote_index = (self.quote_index+1) % 2 588 return q 589 590 def quote_quotes(self, text): 591 t = None 592 for part in text.split('"'): 593 if t == None: 594 t = part 595 else: 596 t += self.next_quote() + part 597 return t 598 599 def double_quotes_in_tt(self, text): 600 if not self.double_quote_replacment: 601 return text 602 return text.replace('"', self.double_quote_replacment) 603 604 def get_language(self): 605 if self.language in self._ISO639_TO_BABEL: 606 return self._ISO639_TO_BABEL[self.language] 607 else: 608 # support dialects. 609 l = self.language.split("_")[0] 610 if l in self._ISO639_TO_BABEL: 611 return self._ISO639_TO_BABEL[l] 612 return None 613 614 615class Encoder: 616 literal_block = 0 617 literal = 0 618 mathmode = 0 619 verbatim = 0 620 insert_newline = 0 621 mbox_newline = 0 622 insert_none_breaking_blanks = 0 623 624 latex_equivalents = { 625 '\u00A0': '~', 626 '\u2013': '{--}', 627 '\u2014': '{---}', 628 '\u2018': '`', 629 '\u2019': '\'', 630 '\u201A': ',', 631 '\u201C': '``', 632 '\u201D': '\'\'', 633 '\u201E': ',,', 634 '\u2020': '{\\dag}', 635 '\u2021': '{\\ddag}', 636 '\u2026': '{\\dots}', 637 '\u2122': '{\\texttrademark}', 638 '\u21d4': '{$\\Leftrightarrow$}', 639 } 640 641 def __init__(self, mod): 642 self.mod = mod 643 self.re = mod.re 644 self.babel = Babel(mod) 645 self.font_encoding = mod.font_encoding 646 self.latex_encoding = self.to_latex_encoding(mod.output_encoding) 647 648 def to_latex_encoding(self, docutils_encoding): 649 """ 650 Translate docutils encoding name into latex's. 651 652 Default fallback method is remove "-" and "_" chars from docutils_encoding. 653 654 """ 655 tr = {"iso-8859-1": "latin1", # west european 656 "iso-8859-2": "latin2", # east european 657 "iso-8859-3": "latin3", # esperanto, maltese 658 "iso-8859-4": "latin4", # north european,scandinavian, baltic 659 "iso-8859-5": "iso88595", # cyrillic (ISO) 660 "iso-8859-9": "latin5", # turkish 661 "iso-8859-15": "latin9", # latin9, update to latin1. 662 "mac_cyrillic": "maccyr", # cyrillic (on Mac) 663 "windows-1251": "cp1251", # cyrillic (on Windows) 664 "koi8-r": "koi8-r", # cyrillic (Russian) 665 "koi8-u": "koi8-u", # cyrillic (Ukrainian) 666 "windows-1250": "cp1250", # 667 "windows-1252": "cp1252", # 668 "us-ascii": "ascii", # ASCII (US) 669 # unmatched encodings 670 # "": "applemac", 671 # "": "ansinew", # windows 3.1 ansi 672 # "": "ascii", # ASCII encoding for the range 32--127. 673 # "": "cp437", # dos latine us 674 # "": "cp850", # dos latin 1 675 # "": "cp852", # dos latin 2 676 # "": "decmulti", 677 # "": "latin10", 678 # "iso-8859-6": "" # arabic 679 # "iso-8859-7": "" # greek 680 # "iso-8859-8": "" # hebrew 681 # "iso-8859-10": "" # latin6, more complete iso-8859-4 682 } 683 if docutils_encoding.lower() in tr: 684 return tr[docutils_encoding.lower()] 685 return docutils_encoding.translate(str.maketrans("", "", "_-")).lower() 686 687 def unicode_to_latex(self, text): 688 # see LaTeX codec 689 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124 690 # Only some special chracters are translated, for documents with many 691 # utf-8 chars one should use the LaTeX unicode package. 692 for uchar in list(self.latex_equivalents.keys()): 693 text = text.replace(uchar, self.latex_equivalents[uchar]) 694 return text 695 696 def encode(self, text): 697 """ 698 Encode special characters in `text` & return. 699 # $ % & ~ _ ^ \ { } 700 Escaping with a backslash does not help with backslashes, ~ and ^. 701 702 < > are only available in math-mode or tt font. (really ?) 703 $ starts math- mode. 704 AND quotes: 705 706 """ 707 if self.verbatim: 708 return text 709 # compile the regexps once. do it here so one can see them. 710 # 711 # first the braces. 712 if 'encode_re_braces' not in self.__dict__: 713 self.encode_re_braces = self.re.compile(r'([{}])') 714 text = self.encode_re_braces.sub(r'{\\\1}', text) 715 if 'encode_re_bslash' not in self.__dict__: 716 # find backslash: except in the form '{\{}' or '{\}}'. 717 self.encode_re_bslash = self.re.compile(r'(?<!{)(\\)(?![{}]})') 718 # then the backslash: except in the form from line above: 719 # either '{\{}' or '{\}}'. 720 text = self.encode_re_bslash.sub(r'{\\textbackslash}', text) 721 722 # then dollar 723 text = text.replace("$", '{\\$}') 724 if not (self.literal_block or self.literal or self.mathmode): 725 # the vertical bar: in mathmode |,\vert or \mid 726 # in textmode \textbar 727 text = text.replace("|", '{\\textbar}') 728 text = text.replace("<", '{\\textless}') 729 text = text.replace(">", '{\\textgreater}') 730 # then 731 text = text.replace("&", '{\\&}') 732 # the ^: 733 # * verb|^| does not work in mbox. 734 # * mathmode has wedge. hat{~} would also work. 735 # text = text.replace("^", '{\\ensuremath{^\\wedge}}') 736 text = text.replace("^", '{\\textasciicircum}') 737 text = text.replace("%", '{\\%}') 738 text = text.replace("#", '{\\#}') 739 text = text.replace("~", '{\\textasciitilde}') 740 # Separate compound characters, e.g. "--" to "-{}-". (The 741 # actual separation is done later; see below.) 742 separate_chars = '-' 743 if self.literal_block or self.literal: 744 # In monospace-font, we also separate ",,", "``" and "''" 745 # and some other characters which can't occur in 746 # non-literal text. 747 separate_chars += ',`\'"<>' 748 # pdflatex does not produce doublequotes for ngerman. 749 text = self.babel.double_quotes_in_tt(text) 750 if self.font_encoding == 'OT1': 751 # We're using OT1 font-encoding and have to replace 752 # underscore by underlined blank, because this has 753 # correct width. 754 text = text.replace('_', '{\\underline{ }}') 755 # And the tt-backslash doesn't work in OT1, so we use 756 # a mirrored slash. 757 text = text.replace('\\textbackslash', '\\reflectbox{/}') 758 else: 759 text = text.replace('_', '{\\_}') 760 else: 761 text = self.babel.quote_quotes(text) 762 text = text.replace("_", '{\\_}') 763 for char in separate_chars * 2: 764 # Do it twice ("* 2") becaues otherwise we would replace 765 # "---" by "-{}--". 766 text = text.replace(char + char, char + '{}' + char) 767 if self.insert_newline or self.literal_block: 768 # Insert a blank before the newline, to avoid 769 # ! LaTeX Error: There's no line here to end. 770 text = text.replace("\n", '~\\\\\n') 771 elif self.mbox_newline: 772 if self.literal_block: 773 closings = "}" * len(self.literal_block_stack) 774 openings = "".join(self.literal_block_stack) 775 else: 776 closings = "" 777 openings = "" 778 text = text.replace( 779 "\n", "%s}\\\\\n\\mbox{%s" % (closings, openings)) 780 # lines starting with "[" give errors. 781 text = text.replace('[', '{[}') 782 if self.insert_none_breaking_blanks: 783 text = text.replace(' ', '~') 784 if self.latex_encoding != 'utf8': 785 text = self.unicode_to_latex(text) 786 return text 787 788 789class _GLUECLAMP_: 790 _imports_ = ( 791 '_parent:SpecNodes', 792 '_parent.SpecNodes:node_of_taci', 793 '_parent.Main:ReportedError', 794 '_root:re', 795 ) 796 797 font_encoding = '' 798 double_quote_replacment = '' 799 language_code = '' 800 output_encoding = '' 801 802 section_table = { 803 0: 'part', 804 1: 'chapter', 805 2: 'section', 806 3: 'subsection', 807 4: 'subsubsection', 808 5: 'paragraph', 809 6: 'subparagraph' 810 } 811 812 def doc2text(self, doc, node): 813 d2l = Doc2Latex(self, doc, node) 814 return d2l.get_latex() 815 816 def doc2filer(self, doc, node, name, dir, opts, IO): 817 text = self.doc2text(doc, node) 818 path = IO.path.join(dir, '%s.tex' % name) 819 node = self.node_of_taci('write_file', path, [ 820 self.node_of_taci('text', text)]) 821 return node 822