changed config file section
[docutils.git] / docutils / docutils / writers / newlatex2e.py
blobe91dfc61c7cbd07b9f9be3d27f47529df9fe7ec4
1 """
2 :Author: Felix Wiemann
3 :Contact: Felix_Wiemann@ososo.de
4 :Revision: $Revision$
5 :Date: $Date$
6 :Copyright: This module has been placed in the public domain.
8 LaTeX2e document tree Writer.
9 """
11 # Thanks to Engelbert Gruber and various contributors for the original
12 # LaTeX writer, some code and many ideas of which have been used for
13 # this writer.
15 __docformat__ = 'reStructuredText'
18 import re
19 import os.path
20 from types import ListType
22 import docutils
23 from docutils import nodes, writers, utils
26 class Writer(writers.Writer):
28 supported = ('newlatex', 'newlatex2e')
29 """Formats this writer supports."""
31 settings_spec = (
32 'LaTeX-Specific Options',
33 'The LaTeX "--output-encoding" default is "latin-1:strict". '
34 'Note that this LaTeX writer is still EXPERIMENTAL.',
35 (('Specify a stylesheet file. The path is used verbatim to include '
36 'the file. Overrides --stylesheet-path.',
37 ['--stylesheet'],
38 {'default': '', 'metavar': '<file>',
39 'overrides': 'stylesheet_path'}),
40 ('Specify a stylesheet file, relative to the current working '
41 'directory. Overrides --stylesheet.',
42 ['--stylesheet-path'],
43 {'metavar': '<file>', 'overrides': 'stylesheet'}),
44 ('Specify a uesr stylesheet file. See --stylesheet.',
45 ['--user-stylesheet'],
46 {'default': '', 'metavar': '<file>',
47 'overrides': 'user_stylesheet_path'}),
48 ('Specify a user stylesheet file. See --stylesheet-path.',
49 ['--user-stylesheet-path'],
50 {'metavar': '<file>', 'overrides': 'user_stylesheet'})
51 ),)
53 settings_defaults = {'output_encoding': 'latin-1',
54 'trim_footnote_reference_space': 1,
55 # Currently unsupported:
56 'docinfo_xform': 0,
57 # During development:
58 'traceback': 1}
60 relative_path_settings = ('stylesheet_path',)
62 config_section = 'newlatex2e writer'
63 config_section_dependencies = ('writers',)
65 output = None
66 """Final translated form of `document`."""
68 def __init__(self):
69 writers.Writer.__init__(self)
70 self.translator_class = LaTeXTranslator
72 def translate(self):
73 visitor = self.translator_class(self.document)
74 self.document.walkabout(visitor)
75 self.output = visitor.astext()
76 self.head = visitor.header
77 self.body = visitor.body
80 class Babel:
81 """Language specifics for LaTeX."""
82 # country code by a.schlock.
83 # partly manually converted from iso and babel stuff, dialects and some
84 _ISO639_TO_BABEL = {
85 'no': 'norsk', # added by hand ( forget about nynorsk?)
86 'gd': 'scottish', # added by hand
87 'hu': 'magyar', # added by hand
88 'pt': 'portuguese',# added by hand
89 'sl': 'slovenian',
90 'af': 'afrikaans',
91 'bg': 'bulgarian',
92 'br': 'breton',
93 'ca': 'catalan',
94 'cs': 'czech',
95 'cy': 'welsh',
96 'da': 'danish',
97 'fr': 'french',
98 # french, francais, canadien, acadian
99 'de': 'ngerman', # rather than german
100 # ngerman, naustrian, german, germanb, austrian
101 'el': 'greek',
102 'en': 'english',
103 # english, USenglish, american, UKenglish, british, canadian
104 'eo': 'esperanto',
105 'es': 'spanish',
106 'et': 'estonian',
107 'eu': 'basque',
108 'fi': 'finnish',
109 'ga': 'irish',
110 'gl': 'galician',
111 'he': 'hebrew',
112 'hr': 'croatian',
113 'hu': 'hungarian',
114 'is': 'icelandic',
115 'it': 'italian',
116 'la': 'latin',
117 'nl': 'dutch',
118 'pl': 'polish',
119 'pt': 'portuguese',
120 'ro': 'romanian',
121 'ru': 'russian',
122 'sk': 'slovak',
123 'sr': 'serbian',
124 'sv': 'swedish',
125 'tr': 'turkish',
126 'uk': 'ukrainian'
129 def __init__(self, lang):
130 self.language = lang
132 def get_language(self):
133 if self._ISO639_TO_BABEL.has_key(self.language):
134 return self._ISO639_TO_BABEL[self.language]
135 else:
136 # Support dialects.
137 l = self.language.split("_")[0]
138 if self._ISO639_TO_BABEL.has_key(l):
139 return self._ISO639_TO_BABEL[l]
140 return None
143 class LaTeXException(Exception):
145 Exception base class to for exceptions which influence the
146 automatic generation of LaTeX code.
150 class SkipAttrParentLaTeX(LaTeXException):
152 Do not generate \Dattr and \renewcommand{\Dparent}{...} for this
153 node.
155 To be raised from before_... methods.
159 class SkipParentLaTeX(LaTeXException):
161 Do not generate \renewcommand{\DNparent}{...} for this node.
163 To be raised from before_... methods.
167 class LaTeXTranslator(nodes.SparseNodeVisitor):
169 # Start with left double quote.
170 left_quote = 1
172 def __init__(self, document):
173 nodes.NodeVisitor.__init__(self, document)
174 self.settings = document.settings
175 self.header = []
176 self.body = []
177 self.context = []
178 self.stylesheet_path = utils.get_stylesheet_reference(
179 self.settings, os.path.join(os.getcwd(), 'dummy'))
180 if self.stylesheet_path:
181 self.settings.record_dependencies.add(self.stylesheet_path)
182 # This ugly hack will be cleaned up when refactoring the
183 # stylesheet mess.
184 self.settings.stylesheet = self.settings.user_stylesheet
185 self.settings.stylesheet_path = self.settings.user_stylesheet_path
186 self.user_stylesheet_path = utils.get_stylesheet_reference(
187 self.settings, os.path.join(os.getcwd(), 'dummy'))
188 if self.user_stylesheet_path:
189 self.settings.record_dependencies.add(self.user_stylesheet_path)
190 self.write_header()
191 for key, value in self.character_map.items():
192 self.character_map[key] = '{%s}' % value
194 def write_header(self):
195 a = self.header.append
196 a('%% Generated by Docutils %s <http://docutils.sourceforge.net>.\n'
197 % docutils.__version__)
198 if self.user_stylesheet_path:
199 a('% User stylesheet:')
200 a(r'\input{%s}' % self.user_stylesheet_path)
201 a('% Docutils stylesheet:')
202 a(r'\input{%s}' % self.stylesheet_path)
203 a('')
204 a('% Definitions for Docutils Nodes:')
205 for node_name in nodes.node_class_names:
206 a(r'\providecommand{\DN%s}[1]{#1}' % node_name.replace('_', ''))
207 a('')
208 a('% Auxiliary definitions:')
209 a(r'\providecommand{\Dsetattr}[2]{}')
210 a(r'\providecommand{\Dparent}{} % variable')
211 a(r'\providecommand{\Dattr}[5]{#5}')
212 a(r'\providecommand{\Dattrlen}{} % variable')
213 a(r'\providecommand{\Dtitleastext}{x}')
214 a(r'\providecommand{\Dsinglebackref}{} % variable')
215 a(r'\providecommand{\Dmultiplebackrefs}{} % variable')
216 a('\n\n')
218 def to_latex_encoding(self,docutils_encoding):
220 Translate docutils encoding name into latex's.
222 Default fallback method is remove "-" and "_" chars from
223 docutils_encoding.
225 tr = { "iso-8859-1": "latin1", # west european
226 "iso-8859-2": "latin2", # east european
227 "iso-8859-3": "latin3", # esperanto, maltese
228 "iso-8859-4": "latin4", # north european,scandinavian, baltic
229 "iso-8859-5": "iso88595", # cyrillic (ISO)
230 "iso-8859-9": "latin5", # turkish
231 "iso-8859-15": "latin9", # latin9, update to latin1.
232 "mac_cyrillic": "maccyr", # cyrillic (on Mac)
233 "windows-1251": "cp1251", # cyrillic (on Windows)
234 "koi8-r": "koi8-r", # cyrillic (Russian)
235 "koi8-u": "koi8-u", # cyrillic (Ukrainian)
236 "windows-1250": "cp1250", #
237 "windows-1252": "cp1252", #
238 "us-ascii": "ascii", # ASCII (US)
239 # unmatched encodings
240 #"": "applemac",
241 #"": "ansinew", # windows 3.1 ansi
242 #"": "ascii", # ASCII encoding for the range 32--127.
243 #"": "cp437", # dos latine us
244 #"": "cp850", # dos latin 1
245 #"": "cp852", # dos latin 2
246 #"": "decmulti",
247 #"": "latin10",
248 #"iso-8859-6": "" # arabic
249 #"iso-8859-7": "" # greek
250 #"iso-8859-8": "" # hebrew
251 #"iso-8859-10": "" # latin6, more complete iso-8859-4
253 if tr.has_key(docutils_encoding.lower()):
254 return tr[docutils_encoding.lower()]
255 return docutils_encoding.translate(string.maketrans("",""),"_-").lower()
257 def language_label(self, docutil_label):
258 return self.language.labels[docutil_label]
260 # To do: Use unimap.py from TeXML instead. Have to deal with
261 # legal cruft before, because it's LPGL.
262 character_map_string = r"""
263 \ \textbackslash
264 { \{
265 } \}
266 $ \$
267 & \&
268 % \%
269 # \#
277 | \textbar
278 < \textless
279 > \textgreater
280 ^ \textasciicircum
281 ~ \textasciitilde
282 _ \Dtextunderscore
285 #special_map = {'\n': ' ', '\r': ' ', '\t': ' ', '\v': ' ', '\f': ' '}
287 unicode_map = {
288 u'\u00A0': '~',
289 u'\u2009': '{\\,}',
290 u'\u2013': '{--}',
291 u'\u2014': '{---}',
292 u'\u2018': '`',
293 u'\u2019': '\'',
294 u'\u201A': ',',
295 u'\u201C': '``',
296 u'\u201D': "''",
297 u'\u201E': ',,',
298 u'\u2020': '{\\dag}',
299 u'\u2021': '{\\ddag}',
300 u'\u2026': '{\\dots}',
301 u'\u2122': '{\\texttrademark}',
302 u'\u21d4': '{$\\Leftrightarrow$}',
305 character_map = {}
306 for pair in character_map_string.strip().split('\n'):
307 char, replacement = pair.split()
308 character_map[char] = replacement
309 character_map.update(unicode_map)
310 #character_map.update(special_map)
312 def encode(self, text, attval=0):
314 Encode special characters in ``text`` and return it.
316 If attval is true, preserve as much as possible verbatim (used in
317 attribute value encoding).
319 if not attval:
320 get = self.character_map.get
321 else:
322 # According to
323 # <http://www-h.eng.cam.ac.uk/help/tpl/textprocessing/teTeX/latex/latex2e-html/ltx-164.html>,
324 # the following characters are special: # $ % & ~ _ ^ \ { }
325 # These work without special treatment in macro parameters:
326 # $, &, ~, _, ^
327 get = {'#': '\\#',
328 '%': '\\%',
329 # We cannot do anything about backslashes.
330 '\\': '',
331 '{': '\\{',
332 '}': '\\}'}.get
333 text = ''.join([get(c, c) for c in text])
334 if (self.literal_block or self.inline_literal) and not attval:
335 # NB: We can have inline literals within literal blocks.
336 # Shrink '\r\n'.
337 text = text.replace('\r\n', '\n')
338 # Convert space. If "{ }~~~~~" is wrapped (at the
339 # brace-enclosed space "{ }"), the following non-breaking
340 # spaces ("~~~~") do *not* wind up at the beginning of the
341 # next line. Also note that, for some not-so-obvious
342 # reason, no hyphenation is done if the breaking space ("{
343 # }") comes *after* the non-breaking spaces.
344 if self.literal_block:
345 # Replace newlines with real newlines.
346 text = text.replace('\n', '\mbox{}\\\\')
347 firstspace = '~'
348 else:
349 firstspace = '{ }'
350 text = re.sub(r'\s+', lambda m: firstspace +
351 '~' * (len(m.group()) - 1), text)
352 # Protect hyphens; if we don't, line breaks will be
353 # possible at the hyphens and even the \textnhtt macro
354 # from the hyphenat package won't change that.
355 text = text.replace('-', r'\mbox{-}')
356 text = text.replace("'", r'{\Dtextliteralsinglequote}')
357 return text
358 else:
359 if not attval:
360 # Replace space with single protected space.
361 text = re.sub(r'\s+', '{ }', text)
362 # Replace double quotes with macro calls.
363 L = []
364 for part in text.split('"'):
365 if L:
366 # Insert quote.
367 L.append(self.left_quote and r'\Dtextleftdblquote' or
368 r'\Dtextrightdblquote')
369 self.left_quote = not self.left_quote
370 L.append(part)
371 return ''.join(L)
372 else:
373 return text
375 def astext(self):
376 return '\n'.join(self.header) + (''.join(self.body))
378 def append(self, text, newline='%\n'):
380 Append text, stripping newlines, producing nice LaTeX code.
382 lines = [' ' * self.indentation_level + line + newline
383 for line in text.splitlines(0)]
384 self.body.append(''.join(lines))
386 def visit_Text(self, node):
387 self.append(self.encode(node.astext()))
389 def depart_Text(self, node):
390 pass
392 def before_title(self, node):
393 self.append(r'\renewcommand{\Dtitleastext}{%s}'
394 % self.encode(node.astext()))
395 self.append(r'\renewcommand{\Dhassubtitle}{%s}'
396 % ((len(node.parent) > 2 and
397 isinstance(node.parent[1], nodes.subtitle))
398 and 'true' or 'false'))
400 literal_block = 0
402 def visit_literal_block(self, node):
403 self.literal_block = 1
405 def depart_literal_block(self, node):
406 self.literal_block = 0
408 visit_doctest_block = visit_literal_block
409 depart_doctest_block = depart_literal_block
411 inline_literal = 0
413 def visit_literal(self, node):
414 self.inline_literal += 1
416 def depart_literal(self, node):
417 self.inline_literal -= 1
419 def visit_comment(self, node):
420 self.append('\n'.join(['% ' + line for line
421 in node.astext().splitlines(0)]), newline='\n')
422 raise nodes.SkipChildren
424 bullet_list_level = 0
426 def visit_bullet_list(self, node):
427 self.append(r'\Dsetbullet{\labelitem%s}' %
428 ['i', 'ii', 'iii', 'iv'][min(self.bullet_list_level, 3)])
429 self.bullet_list_level += 1
431 def depart_bullet_list(self, node):
432 self.bullet_list_level -= 1
434 enum_styles = {'arabic': 'arabic', 'loweralpha': 'alph', 'upperalpha':
435 'Alph', 'lowerroman': 'roman', 'upperroman': 'Roman'}
437 enum_counter = 0
439 def visit_enumerated_list(self, node):
440 # We create our own enumeration list environment. This allows
441 # to set the style and starting value and unlimited nesting.
442 # Maybe this can be moved to the stylesheet?
443 self.enum_counter += 1
444 enum_prefix = self.encode(node['prefix'])
445 enum_suffix = self.encode(node['suffix'])
446 enum_type = '\\' + self.enum_styles.get(node['enumtype'], r'arabic')
447 start = node.get('start', 1) - 1
448 counter = 'Denumcounter%d' % self.enum_counter
449 self.append(r'\Dmakeenumeratedlist{%s}{%s}{%s}{%s}{%s}{'
450 % (enum_prefix, enum_type, enum_suffix, counter, start))
451 # for Emacs: }
453 def depart_enumerated_list(self, node):
454 self.append('}') # for Emacs: {
456 def before_list_item(self, node):
457 # XXX needs cleanup.
458 if (len(node) and (isinstance(node[-1], nodes.TextElement) or
459 isinstance(node[-1], nodes.Text)) and
460 node.parent.index(node) == len(node.parent) - 1):
461 node['lastitem'] = 'true'
463 before_line = before_list_item
465 def visit_raw(self, node):
466 if 'latex' in node.get('format', '').split():
467 self.append(node.astext())
468 raise nodes.SkipChildren
470 def process_backlinks(self, node, type):
471 self.append(r'\renewcommand{\Dsinglebackref}{}')
472 self.append(r'\renewcommand{\Dmultiplebackrefs}{}')
473 if len(node['backrefs']) > 1:
474 refs = []
475 for i in range(len(node['backrefs'])):
476 refs.append(r'\Dmulti%sbacklink{%s}{%s}'
477 % (type, node['backrefs'][i], i + 1))
478 self.append(r'\renewcommand{\Dmultiplebackrefs}{(%s){ }}'
479 % ', '.join(refs))
480 elif len(node['backrefs']) == 1:
481 self.append(r'\renewcommand{\Dsinglebackref}{%s}'
482 % node['backrefs'][0])
484 def visit_footnote(self, node):
485 self.process_backlinks(node, 'footnote')
487 def visit_citation(self, node):
488 self.process_backlinks(node, 'citation')
490 def visit_table(self, node):
491 # Everything's handled in tgroup.
492 pass
494 def before_table(self, node):
495 # A tables contains exactly one tgroup. See before_tgroup.
496 pass
498 def before_tgroup(self, node):
499 widths = []
500 total_width = 0
501 for i in range(int(node['cols'])):
502 assert isinstance(node[i], nodes.colspec)
503 widths.append(int(node[i]['colwidth']) + 1)
504 total_width += widths[-1]
505 del node[:len(widths)]
506 tablespec = '|'
507 for w in widths:
508 tablespec += r'p{%s\linewidth}|' % (0.93 * w /
509 max(total_width, 60))
510 self.append(r'\Dmaketable{%s}{' % tablespec)
511 self.context.append('}')
512 raise SkipAttrParentLaTeX
514 def depart_tgroup(self, node):
515 self.append(self.context.pop())
517 def before_row(self, node):
518 raise SkipAttrParentLaTeX
520 def before_thead(self, node):
521 raise SkipAttrParentLaTeX
523 def before_tbody(self, node):
524 raise SkipAttrParentLaTeX
526 def is_simply_entry(self, node):
527 return (len(node) == 1 and isinstance(node[0], nodes.paragraph) or
528 len(node) == 0)
530 def before_entry(self, node):
531 is_leftmost = 0
532 if node.hasattr('morerows'):
533 self.document.reporter.severe('Rowspans are not supported.')
534 # Todo: Add empty cells below rowspanning cell and issue
535 # warning instead of severe.
536 if node.hasattr('morecols'):
537 # The author got a headache trying to implement
538 # multicolumn support.
539 if not self.is_simply_entry(node):
540 self.document.reporter.severe(
541 'Colspanning table cells may only contain one paragraph.')
542 # Todo: Same as above.
543 # The number of columns this entry spans (as a string).
544 colspan = int(node['morecols']) + 1
545 del node['morecols']
546 else:
547 colspan = 1
548 # Macro to call.
549 macro_name = r'\Dcolspan'
550 if node.parent.index(node) == 0:
551 # Leftmost column.
552 macro_name += 'left'
553 is_leftmost = 1
554 if colspan > 1:
555 self.append('%s{%s}{' % (macro_name, colspan))
556 self.context.append('}')
557 else:
558 # Do not add a multicolumn with colspan 1 beacuse we need
559 # at least one non-multicolumn cell per column to get the
560 # desired column widths, and we can only do colspans with
561 # cells consisting of only one paragraph.
562 if not is_leftmost:
563 self.append(r'\Dsubsequententry{')
564 self.context.append('}')
565 else:
566 self.context.append('')
567 if isinstance(node.parent.parent, nodes.thead):
568 node['tableheaderentry'] = 'true'
570 # Don't add \renewcommand{\Dparent}{...} because there may not
571 # be any non-expandable commands in front of \multicolumn.
572 raise SkipParentLaTeX
574 def depart_entry(self, node):
575 self.append(self.context.pop())
577 def before_substitution_definition(self, node):
578 raise nodes.SkipNode
580 indentation_level = 0
582 def node_name(self, node):
583 return node.__class__.__name__.replace('_', '')
585 def propagate_attributes(self, node):
586 # Propagate attributes using \Dattr macros.
587 node_name = self.node_name(node)
588 attlist = []
589 if isinstance(node, nodes.Element):
590 attlist = node.attlist()
591 numatts = 0
592 pass_contents = self.pass_contents(node)
593 for key, value in attlist:
594 if isinstance(value, ListType):
595 self.append(r'\renewcommand{\Dattrlen}{%s}' % len(value))
596 for i in range(len(value)):
597 self.append(r'\Dattr{%s}{%s}{%s}{%s}{' %
598 (i+1, key, self.encode(value[i], attval=1),
599 node_name))
600 if not pass_contents:
601 self.append('}')
602 numatts += len(value)
603 else:
604 self.append(r'\Dattr{}{%s}{%s}{%s}{' %
605 (key, self.encode(unicode(value), attval=1),
606 node_name))
607 if not pass_contents:
608 self.append('}')
609 numatts += 1
610 if pass_contents:
611 self.context.append('}' * numatts) # for Emacs: {
612 else:
613 self.context.append('')
615 def visit_docinfo(self, node):
616 raise NotImplementedError('Docinfo not yet implemented.')
618 def visit_document(self, node):
619 document = node
620 # Move IDs into TextElements. This won't work for images.
621 # Need to review this.
622 for node in document.traverse(lambda n: isinstance(n, nodes.Element)):
623 if node.has_key('ids') and not isinstance(node,
624 nodes.TextElement):
625 next_text_element = node.next_node(
626 lambda n: isinstance(n, nodes.TextElement))
627 if next_text_element:
628 next_text_element['ids'].extend(node['ids'])
629 node['ids'] = []
631 def pass_contents(self, node):
632 r"""
633 Return true if the node contents should be passed in
634 parameters of \DN... and \Dattr.
636 return not isinstance(node, (nodes.document, nodes.section))
638 def dispatch_visit(self, node):
639 skip_attr = skip_parent = 0
640 # TreePruningException to be propagated.
641 tree_pruning_exception = None
642 if hasattr(self, 'before_' + node.__class__.__name__):
643 try:
644 getattr(self, 'before_' + node.__class__.__name__)(node)
645 except SkipParentLaTeX:
646 skip_parent = 1
647 except SkipAttrParentLaTeX:
648 skip_attr = 1
649 skip_parent = 1
650 except nodes.SkipNode:
651 raise
652 except (nodes.SkipChildren, nodes.SkipSiblings), instance:
653 tree_pruning_exception = instance
654 except nodes.SkipDeparture:
655 raise NotImplementedError(
656 'SkipDeparture not usable in LaTeX writer')
658 if not isinstance(node, nodes.Text):
659 node_name = self.node_name(node)
660 if not skip_parent and not isinstance(node, nodes.document):
661 self.append(r'\renewcommand{\Dparent}{%s}'
662 % self.node_name(node.parent))
663 if self.pass_contents(node):
664 self.append(r'\DN%s{' % node_name)
665 self.context.append('}')
666 else:
667 self.append(r'\Dvisit%s' % node_name)
668 self.context.append(r'\Ddepart%s' % node_name)
669 self.indentation_level += 1
670 if not skip_attr:
671 self.propagate_attributes(node)
672 else:
673 self.context.append('')
675 if (isinstance(node, nodes.TextElement) and
676 not isinstance(node.parent, nodes.TextElement)):
677 # Reset current quote to left.
678 self.left_quote = 1
680 # Call visit_... method.
681 try:
682 nodes.SparseNodeVisitor.dispatch_visit(self, node)
683 except LaTeXException:
684 raise NotImplementedError(
685 'visit_... methods must not raise LaTeXExceptions')
687 if tree_pruning_exception:
688 # Propagate TreePruningException raised in before_... method.
689 raise tree_pruning_exception
691 def is_invisible(self, node):
692 # Return true if node is invisible or moved away in the LaTeX
693 # rendering.
694 return (isinstance(node, nodes.Invisible) or
695 isinstance(node, nodes.footnote) or
696 isinstance(node, nodes.citation))
698 def needs_space(self, node):
699 # Return true if node is a visible block-level element.
700 return ((isinstance(node, nodes.Body) or
701 isinstance(node, nodes.topic) or
702 #isinstance(node, nodes.rubric) or
703 isinstance(node, nodes.transition) or
704 isinstance(node, nodes.caption) or
705 isinstance(node, nodes.legend)) and
706 not (self.is_invisible(node) or
707 isinstance(node.parent, nodes.TextElement)))
709 def dispatch_departure(self, node):
710 # Call departure method.
711 nodes.SparseNodeVisitor.dispatch_departure(self, node)
713 if not isinstance(node, nodes.Text):
714 # Close attribute and node handler call (\DN...{...}).
715 self.indentation_level -= 1
716 self.append(self.context.pop() + self.context.pop())
718 # Insert space.
719 if self.needs_space(node):
720 # Next sibling.
721 next_node = node.next_node(
722 ascend=0, siblings=1, descend=0,
723 condition=lambda n: not self.is_invisible(n))
724 if self.needs_space(next_node):
725 # Insert space.
726 if isinstance(next_node, nodes.paragraph):
727 if isinstance(node, nodes.paragraph):
728 # Space between paragraphs.
729 self.append(r'\Dparagraphspace')
730 else:
731 # Space in front of a paragraph.
732 self.append(r'\Dauxiliaryparspace')
733 else:
734 # Space in front of something else than a paragraph.
735 self.append(r'\Dauxiliaryspace')