[Zope-Checkins] CVS: Zope/lib/python/docutils/writers - __init__.py:1.2.10.5 docutils_xml.py:1.2.10.5 html4css1.py:1.2.10.5 latex2e.py:1.1.2.5

Christian 'Tiran' Heimes heimes at faho.rwth-aachen.de
Mon Jul 26 13:38:41 EDT 2004


Update of /cvs-repository/Zope/lib/python/docutils/writers
In directory cvs.zope.org:/tmp/cvs-serv18892/lib/python/docutils/writers

Modified Files:
      Tag: Zope-2_7-branch
	__init__.py docutils_xml.py html4css1.py latex2e.py 
Log Message:
Updated docutils including a fix for 1426:  System locale breaks reStructuredText horribly
Added rest-language-code to zope.conf schema. it's used instead of the locales


=== Zope/lib/python/docutils/writers/__init__.py 1.2.10.4 => 1.2.10.5 ===
--- Zope/lib/python/docutils/writers/__init__.py:1.2.10.4	Thu May 13 12:20:04 2004
+++ Zope/lib/python/docutils/writers/__init__.py	Mon Jul 26 13:38:10 2004
@@ -36,13 +36,15 @@
     """The document to write (Docutils doctree); set by `write`."""
 
     output = None
-    """Final translated form of `document`; set by `translate`."""
+    """Final translated form of `document` (Unicode string);
+    set by `translate`."""
 
     language = None
     """Language module for the document; set by `write`."""
 
     destination = None
-    """`docutils.io` IO object; where to write the document.  Set by `write`."""
+    """`docutils.io` Output object; where to write the document.
+    Set by `write`."""
 
     def __init__(self):
 
@@ -73,8 +75,8 @@
 
     def translate(self):
         """
-        Do final translation of `self.document` into `self.output`.
-        Called from `write`.  Override in subclasses.
+        Do final translation of `self.document` into `self.output` (Unicode
+        string).  Called from `write`.  Override in subclasses.
 
         Usually done with a `docutils.nodes.NodeVisitor` subclass, in
         combination with a call to `docutils.nodes.Node.walk()` or


=== Zope/lib/python/docutils/writers/docutils_xml.py 1.2.10.4 => 1.2.10.5 ===
--- Zope/lib/python/docutils/writers/docutils_xml.py:1.2.10.4	Thu May 13 12:20:04 2004
+++ Zope/lib/python/docutils/writers/docutils_xml.py	Mon Jul 26 13:38:10 2004
@@ -50,7 +50,7 @@
     doctype = (
         '<!DOCTYPE document PUBLIC'
         ' "+//IDN docutils.sourceforge.net//DTD Docutils Generic//EN//XML"'
-        ' "http://docutils.sourceforge.net/spec/docutils.dtd">\n')
+        ' "http://docutils.sourceforge.net/docs/ref/docutils.dtd">\n')
     generator = '<!-- Generated by Docutils %s -->\n'
 
     def translate(self):


=== Zope/lib/python/docutils/writers/html4css1.py 1.2.10.4 => 1.2.10.5 ===
--- Zope/lib/python/docutils/writers/html4css1.py:1.2.10.4	Thu May 13 12:20:04 2004
+++ Zope/lib/python/docutils/writers/html4css1.py	Mon Jul 26 13:38:10 2004
@@ -418,7 +418,6 @@
         self.body.append(self.starttag(node, 'table', CLASS='citation',
                                        frame="void", rules="none"))
         self.body.append('<colgroup><col class="label" /><col /></colgroup>\n'
-                         '<col />\n'
                          '<tbody valign="top">\n'
                          '<tr>')
         self.footnote_backrefs(node)
@@ -1143,7 +1142,7 @@
         self.body.append(
               # "border=None" is a boolean attribute;
               # it means "standard border", not "no border":
-              self.starttag(node, 'table', CLASS="table", border=None))
+              self.starttag(node, 'table', CLASS="table", border="1"))
 
     def depart_table(self, node):
         self.body.append('</table>\n')


=== Zope/lib/python/docutils/writers/latex2e.py 1.1.2.4 => 1.1.2.5 ===
--- Zope/lib/python/docutils/writers/latex2e.py:1.1.2.4	Thu May 13 12:20:04 2004
+++ Zope/lib/python/docutils/writers/latex2e.py	Mon Jul 26 13:38:10 2004
@@ -34,10 +34,10 @@
           ['--documentclass'],
           {'default': 'article', }),
          ('Specify document options.  Multiple options can be given, '
-          'separated by commas.  Default is "10pt".',
+          'separated by commas.  Default is "10pt,a4paper".',
           ['--documentoptions'],
-          {'default': '10pt', }),
-         ('Use LaTeX footnotes. '
+          {'default': '10pt,a4paper', }),
+         ('Use LaTeX footnotes. LaTeX supports only numbered footnotes (does it?). '
           'Default: no, uses figures.',
           ['--use-latex-footnotes'],
           {'default': 0, 'action': 'store_true',
@@ -47,6 +47,11 @@
           ['--footnote-references'],
           {'choices': ['superscript', 'brackets'], 'default': 'brackets',
            'metavar': '<format>'}),
+         ('Use LaTeX citations. '
+          'Default: no, uses figures which might get mixed with images.',
+          ['--use-latex-citations'],
+          {'default': 0, 'action': 'store_true',
+           'validator': frontend.validate_boolean}),
          ('Format for block quote attributions: one of "dash" (em-dash '
           'prefix), "parentheses"/"parens", or "none".  Default is "dash".',
           ['--attribution'],
@@ -112,7 +117,20 @@
           ['--use-verbatim-when-possible'],
           {'default': 0, 'action': 'store_true',
            'validator': frontend.validate_boolean}),
-          ))
+         ('Table style. "standard" with horizontal and vertical lines, '
+          '"booktabs" (LaTeX booktabs style) only horizontal lines '
+          'above and below the table and below the header or "nolines".'
+          'default: "standard"',
+          ['--table-style'],
+          {'choices': ['standard', 'booktabs','nolines'], 'default': 'standard',
+           'metavar': '<format>'}),
+          ('LaTeX graphicx package option.'
+           'Possible values are "dvips", "pdftex". "auto" includes LaTeX code '
+           'to use "pdftex" if processing with pdf(la)tex and dvips otherwise. '
+           'Default is no option.',
+           ['--graphicx-option'],
+           {'default': ''}),
+          ),)
 
     settings_defaults = {'output_encoding': 'latin-1'}
 
@@ -285,10 +303,15 @@
 
     # BUG: LaTeX has no deeper sections (actually paragrah is no
     # section either).
+    # BUG: No support for unknown document classes.  Make 'article'
+    # default?
     _class_sections = {
         'book': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
+        'scrbook': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
         'report': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
+        'scrreprt': ( 'chapter', 'section', 'subsection', 'subsubsection' ),
         'article': ( 'section', 'subsection', 'subsubsection' ),
+        'scrartcl': ( 'section', 'subsection', 'subsubsection' ),
         }
     _deepest_section = 'subsubsection'
 
@@ -307,28 +330,183 @@
         else:
             return self._deepest_section
 
+class Table:
+    """ Manage a table while traversing. 
+        Maybe change to a mixin defining the visit/departs, but then
+        class Table internal variables are in the Translator.
+    """
+    def __init__(self,latex_type,table_style):
+        self._latex_type = latex_type
+        self._table_style = table_style
+        self._open = 0
+        # miscellaneous attributes
+        self._attrs = {}
+        self._col_width = []
+        self._rowspan = []
+
+    def open(self):
+        self._open = 1
+        self._col_specs = []
+        self.caption = None
+        self._attrs = {}
+        self._in_head = 0 # maybe context with search
+    def close(self):
+        self._open = 0
+        self._col_specs = None
+        self.caption = None
+        self._attrs = {}
+    def is_open(self):
+        return self._open
+    def used_packages(self):
+        if self._table_style == 'booktabs':
+            return '\\usepackage{booktabs}\n'
+        return ''
+    def is_open(self):
+        return self._open
+    def get_latex_type(self):
+        return self._latex_type
+    
+    def set(self,attr,value):
+        self._attrs[attr] = value
+    def get(self,attr):
+        if self._attrs.has_key(attr):
+            return self._attrs[attr]
+        return None
+    def get_vertical_bar(self):
+        if self._table_style == 'standard':
+            return '|'
+        return ''
+    # horizontal lines are drawn below a row, because we.
+    def get_opening(self):
+        return '\\begin{%s}[c]' % self._latex_type
+    def get_closing(self):
+        line = ""
+        if self._table_style == 'booktabs':
+            line = '\\bottomrule\n'
+        elif self._table_style == 'standard':
+            lines = '\\hline\n'
+        return '%s\\end{%s}' % (line,self._latex_type)
+
+    def visit_colspec(self,node):
+        self._col_specs.append(node)
+
+    def get_colspecs(self):
+        """
+        Return column specification for longtable.
+
+        Assumes reST line length being 80 characters.
+        Table width is hairy.
+
+        === ===
+        ABC DEF
+        === ===
+
+        usually gets to narrow, therefore we add 1 (fiddlefactor).
+        """
+        width = 80
+
+        total_width = 0.0
+        # first see if we get too wide.
+        for node in self._col_specs:
+            colwidth = float(node['colwidth']+1) / width
+            total_width += colwidth
+        self._col_width = []
+        self._rowspan = []
+        # donot make it full linewidth
+        factor = 0.93
+        if total_width > 1.0:
+            factor /= total_width
+        bar = self.get_vertical_bar()
+        latex_table_spec = ""
+        for node in self._col_specs:
+            colwidth = factor * float(node['colwidth']+1) / width
+            self._col_width.append(colwidth+0.005)
+            self._rowspan.append(0)
+            latex_table_spec += "%sp{%.2f\\locallinewidth}" % (bar,colwidth+0.005)
+        return latex_table_spec+bar
+
+    def get_column_width(self):
+        """ return columnwidth for current cell (not multicell) 
+        """
+        return "%.2f\\locallinewidth" % self._col_width[self._cell_in_row-1]
+
+    def visit_thead(self):
+        self._in_thead = 1
+        if self._table_style == 'standard':
+            return ['\\hline\n']
+        elif self._table_style == 'booktabs':
+            return ['\\toprule\n']
+        return []
+    def depart_thead(self):
+        a = []
+        #if self._table_style == 'standard':
+        #    a.append('\\hline\n')
+        if self._table_style == 'booktabs':
+            a.append('\\midrule\n')
+        a.append('\\endhead\n')
+        # for longtable one could add firsthead, foot and lastfoot
+        self._in_thead = 0
+        return a
+    def visit_row(self):
+        self._cell_in_row = 0
+    def depart_row(self):
+        res = [' \\\\\n']
+        self._cell_in_row = None  # remove cell counter
+        for i in range(len(self._rowspan)):
+            if (self._rowspan[i]>0):
+                self._rowspan[i] -= 1
+        
+        if self._table_style == 'standard':
+            rowspans = []
+            for i in range(len(self._rowspan)):
+                if (self._rowspan[i]<=0):
+                    rowspans.append(i+1)
+            if len(rowspans)==len(self._rowspan):
+                res.append('\\hline\n')
+            else:
+                cline = ''
+                rowspans.reverse()
+                # TODO merge clines
+                while 1:
+                    try:
+                        c_start = rowspans.pop()
+                    except:
+                        break
+                    cline += '\\cline{%d-%d}\n' % (c_start,c_start)
+                res.append(cline)
+        return res
+
+    def set_rowspan(self,cell,value):
+        try:
+            self._rowspan[cell] = value
+        except:
+            pass
+    def get_rowspan(self,cell):
+        try:
+            return self._rowspan[cell]
+        except:
+            return 0
+    def get_entry_number(self):
+        return self._cell_in_row
+    def visit_entry(self):
+        self._cell_in_row += 1
 
+        
 class LaTeXTranslator(nodes.NodeVisitor):
     # When options are given to the documentclass, latex will pass them
     # to other packages, as done with babel.
     # Dummy settings might be taken from document settings
 
-    d_paper = 'a4paper' # papersize
-    d_margins = '2cm'
-
     latex_head = '\\documentclass[%s]{%s}\n'
     encoding = '\\usepackage[%s]{inputenc}\n'
     linking = '\\usepackage[colorlinks=%s,linkcolor=%s,urlcolor=%s]{hyperref}\n'
-    geometry = '\\usepackage[%s,margin=%s,nohead]{geometry}\n'
     stylesheet = '\\input{%s}\n'
     # add a generated on day , machine by user using docutils version.
     generator = '%% generator Docutils: http://docutils.sourceforge.net/\n'
 
     # use latex tableofcontents or let docutils do it.
     use_latex_toc = 0
-    # table kind: if 0 tabularx (single page), 1 longtable
-    # maybe should be decided on row count.
-    use_longtable = 1
+
     # TODO: use mixins for different implementations.
     # list environment for option-list. else tabularx
     use_optionlist_for_option_list = 1
@@ -354,8 +532,10 @@
         self.use_latex_toc = settings.use_latex_toc
         self.use_latex_docinfo = settings.use_latex_docinfo
         self.use_latex_footnotes = settings.use_latex_footnotes
+        self._use_latex_citations = settings.use_latex_citations
         self.hyperlink_color = settings.hyperlink_color
         self.compound_enumerators = settings.compound_enumerators
+        self.fontenc = ''
         self.section_prefix_for_enumerators = (
             settings.section_prefix_for_enumerators)
         self.section_enumerator_separator = (
@@ -377,15 +557,47 @@
                     self.babel.get_language()
 
         self.d_class = DocumentClass(settings.documentclass)
+        # object for a table while proccessing.
+        self.active_table = Table('longtable',settings.table_style)
+
+        # HACK.  Should have more sophisticated typearea handling.
+        if settings.documentclass.find('scr') == -1:
+            self.typearea = '\\usepackage[DIV12]{typearea}\n'
+        else:
+            if self.d_options.find('DIV') == -1 and self.d_options.find('BCOR') == -1:
+                self.typearea = '\\typearea{12}\n'
+            else:
+                self.typearea = ''
+
+        if self.fontenc == 'T1':
+            fontenc = '\\usepackage[T1]{fontenc}\n'
+        else:
+            fontenc = ''
+
+        if self.settings.graphicx_option == '':
+            self.graphicx_package = '\\usepackage{graphicx}\n'
+        elif self.settings.graphicx_option.lower() == 'auto':
+            self.graphicx_package = '\n'.join(
+                ('%Check if we are compiling under latex or pdflatex',
+                 '\\ifx\\pdftexversion\\undefined',
+                 '  \\usepackage{graphicx}',
+                 '\\else',
+                 '  \\usepackage[pdftex]{graphicx}',
+                 '\\fi\n'))
+        else:
+            self.graphicx_package = (
+                '\\usepackage[%s]{graphicx}\n' % self.settings.graphicx_option)
 
         self.head_prefix = [
               self.latex_head % (self.d_options,self.settings.documentclass),
               '\\usepackage{babel}\n',     # language is in documents settings.
+              fontenc,
               '\\usepackage{shortvrb}\n',  # allows verb in footnotes.
               self.encoding % self.to_latex_encoding(settings.output_encoding),
               # * tabularx: for docinfo, automatic width of columns, always on one page.
               '\\usepackage{tabularx}\n',
               '\\usepackage{longtable}\n',
+              self.active_table.used_packages(),
               # possible other packages.
               # * fancyhdr
               # * ltxtable is a combination of tabularx and longtable (pagebreaks).
@@ -394,13 +606,12 @@
               # extra space between text in tables and the line above them
               '\\setlength{\\extrarowheight}{2pt}\n',
               '\\usepackage{amsmath}\n',   # what fore amsmath.
-              '\\usepackage{graphicx}\n',
+              self.graphicx_package,
               '\\usepackage{color}\n',
               '\\usepackage{multirow}\n',
+              '\\usepackage{ifthen}\n',   # before hyperref!
               self.linking % (self.colorlinks, self.hyperlink_color, self.hyperlink_color),
-              # geometry and fonts might go into style.tex.
-              self.geometry % (self.d_paper, self.d_margins),
-              #
+              self.typearea,
               self.generator,
               # latex lengths
               '\\newlength{\\admonitionwidth}\n',
@@ -443,7 +654,6 @@
         self.context = []
         self.topic_class = ''
         # column specification for tables
-        self.colspecs = []
         self.table_caption = None
         # do we have one or more authors
         self.author_stack = None
@@ -470,6 +680,8 @@
         # them into a compound enumeration
         self._enumeration_counters = []
 
+        self._bibitems = []
+
         # docinfo.
         self.docinfo = None
         # inside literal block: no quote mangling.
@@ -527,6 +739,23 @@
     def language_label(self, docutil_label):
         return self.language.labels[docutil_label]
 
+    def utf8_to_latex(self,text):
+        # see LaTeX codec http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/252124
+        # Only some special chracters are translated, for documents with many utf-8
+        # chars one should use the LaTeX unicode package.
+        latex_equivalents = {
+            u'\u00A0' : '~',
+            u'\u00A9' : '{\\copyright}',
+            u'\u2013' : '{--}',
+            u'\u2014' : '{---}',
+            u'\u2020' : '{\\dag}',
+            u'\u2021' : '{\\ddag}',
+            u'\u21d4' : '{$\\Leftrightarrow$}',
+                }
+        for uchar in latex_equivalents.keys():
+            text = text.replace(uchar,latex_equivalents[uchar])
+        return text
+
     def encode(self, text):
         """
         Encode special characters in `text` & return.
@@ -563,7 +792,6 @@
             text = text.replace(">", '{\\textgreater}')
         # then
         text = text.replace("&", '{\\&}')
-        text = text.replace("_", '{\\_}')
         # the ^:
         # * verb|^| does not work in mbox.
         # * mathmode has wedge. hat{~} would also work.
@@ -575,10 +803,19 @@
         if self.literal_block or self.literal:
             # pdflatex does not produce doublequotes for ngerman.
             text = self.babel.double_quotes_in_tt(text)
+            if self.fontenc == 'T1': 
+                # make sure "--" does not become a "-".
+                # the same for "<<" and ">>".
+                text = text.replace("--","-{}-").replace("--","-{}-")
+                text = text.replace(">>",">{}>").replace(">>",">{}>")
+                text = text.replace("<<","<{}<").replace("<<","<{}<")
+            # replace underline by underlined blank, because this has correct width.
+            text = text.replace("_", '{\\underline{ }}')
         else:
             text = self.babel.quote_quotes(text)
-        if self.insert_newline:
-            # HACK: insert a blank before the newline, to avoid
+            text = text.replace("_", '{\\_}')
+        if self.insert_newline or self.literal_block:
+            # Insert a blank before the newline, to avoid
             # ! LaTeX Error: There's no line here to end.
             text = text.replace("\n", '~\\\\\n')
         elif self.mbox_newline:
@@ -589,10 +826,12 @@
                 closings = ""
                 openings = ""
             text = text.replace("\n", "%s}\\\\\n\\mbox{%s" % (closings,openings))
+        # lines starting with "[" give errors.
+        text = text.replace('[', '{[}')
         if self.insert_none_breaking_blanks:
             text = text.replace(' ', '~')
-        # unicode !!!
-        text = text.replace(u'\u2020', '{$\\dagger$}')
+        if self.settings.output_encoding != 'utf-8':
+            text = self.utf8_to_latex(text)
         return text
 
     def attval(self, text,
@@ -709,28 +948,46 @@
     def depart_caution(self, node):
         self.depart_admonition()
 
-    def visit_citation(self, node):
-        self.visit_footnote(node)
-
-    def depart_citation(self, node):
-        self.depart_footnote(node)
-
     def visit_title_reference(self, node):
         self.body.append( '\\titlereference{' )
 
     def depart_title_reference(self, node):
         self.body.append( '}' )
 
+    def visit_citation(self, node):
+        # TODO maybe use cite bibitems
+        if self._use_latex_citations:
+            self.context.append(len(self.body))
+        else:
+            self.body.append('\\begin{figure}[b]')
+            self.body.append('\\hypertarget{%s}' % node['id'])
+
+    def depart_citation(self, node):
+        if self._use_latex_citations:
+            size = self.context.pop()
+            label = self.body[size]
+            text = ''.join(self.body[size+1:])
+            del self.body[size:]
+            self._bibitems.append([label, text])
+        else:
+            self.body.append('\\end{figure}\n')
+
     def visit_citation_reference(self, node):
-        href = ''
-        if node.has_key('refid'):
-            href = node['refid']
-        elif node.has_key('refname'):
-            href = self.document.nameids[node['refname']]
-        self.body.append('[\\hyperlink{%s}{' % href)
+        if self._use_latex_citations:
+            self.body.append('\\cite{')
+        else:
+            href = ''
+            if node.has_key('refid'):
+                href = node['refid']
+            elif node.has_key('refname'):
+                href = self.document.nameids[node['refname']]
+            self.body.append('[\\hyperlink{%s}{' % href)
 
     def depart_citation_reference(self, node):
-        self.body.append('}]')
+        if self._use_latex_citations:
+            self.body.append('}')
+        else:
+            self.body.append('}]')
 
     def visit_classifier(self, node):
         self.body.append( '(\\textbf{' )
@@ -739,10 +996,7 @@
         self.body.append( '})\n' )
 
     def visit_colspec(self, node):
-        if self.use_longtable:
-            self.colspecs.append(node)
-        else:
-            self.context[-1] += 1
+        self.active_table.visit_colspec(node)
 
     def depart_colspec(self, node):
         pass
@@ -871,12 +1125,25 @@
 
     def visit_document(self, node):
         self.body_prefix.append('\\begin{document}\n')
+        # BUG: \maketitle without title (i.e. --no-doc-title) adds
+        # unnecessary vspace.
         self.body_prefix.append('\\maketitle\n\n')
         # alternative use titlepage environment.
         # \begin{titlepage}
         self.body.append('\n\\setlength{\\locallinewidth}{\\linewidth}\n')
 
     def depart_document(self, node):
+        # TODO insertion point of bibliography should none automatic.
+        if self._use_latex_citations and len(self._bibitems)>0:
+            widest_label = ""
+            for bi in self._bibitems:
+                if len(widest_label)<len(bi[0]):
+                    widest_label = bi[0]
+            self.body.append('\n\\begin{thebibliography}{%s}\n'%widest_label)
+            for bi in self._bibitems:
+                self.body.append('\\bibitem[%s]{%s}{%s}\n' % (bi[0], bi[0], bi[1]))
+            self.body.append('\\end{thebibliography}\n')
+            
         self.body_suffix.append('\\end{document}\n')
 
     def visit_emphasis(self, node):
@@ -888,33 +1155,41 @@
         self.literal_block_stack.pop()
 
     def visit_entry(self, node):
+        self.active_table.visit_entry()
         # cell separation
-        column_one = 1
-        if self.context[-1] > 0:
-            column_one = 0
-        if not column_one:
+        if self.active_table.get_entry_number() == 1:
+            # if the firstrow is a multirow, this actually is the second row.
+            # this gets hairy if rowspans follow each other.
+            if self.active_table.get_rowspan(0):
+                self.body.append(' & ')
+                self.active_table.visit_entry() # increment cell count
+        else:
             self.body.append(' & ')
 
         # multi{row,column}
+        # IN WORK BUG TODO HACK continues here
+        # multirow in LaTeX simply will enlarge the cell over several rows
+        # (the following n if n is positive, the former if negative).
         if node.has_key('morerows') and node.has_key('morecols'):
-            raise NotImplementedError('LaTeX can\'t handle cells that '
-            'span multiple rows *and* columns, sorry.')
-        atts = {}
+            raise NotImplementedError('Cells that '
+            'span multiple rows *and* columns are not supported, sorry.')
         if node.has_key('morerows'):
-            raise NotImplementedError('multiple rows are not working (yet), sorry.')
             count = node['morerows'] + 1
-            self.body.append('\\multirow{%d}*{' % count)
+            self.active_table.set_rowspan(self.active_table.get_entry_number()-1,count)
+            self.body.append('\\multirow{%d}{%s}{' % \
+                    (count,self.active_table.get_column_width()))
             self.context.append('}')
             # BUG following rows must have empty cells.
         elif node.has_key('morecols'):
             # the vertical bar before column is missing if it is the first column.
             # the one after always.
-            if column_one:
-                bar = '|'
+            if self.active_table.get_entry_number() == 1:
+                bar1 = self.active_table.get_vertical_bar()
             else:
-                bar = ''
+                bar1 = ''
             count = node['morecols'] + 1
-            self.body.append('\\multicolumn{%d}{%sl|}{' % (count, bar))
+            self.body.append('\\multicolumn{%d}{%sl%s}{' % \
+                    (count, bar1, self.active_table.get_vertical_bar()))
             self.context.append('}')
         else:
             self.context.append('')
@@ -929,7 +1204,16 @@
     def depart_entry(self, node):
         self.body.append(self.context.pop()) # header / not header
         self.body.append(self.context.pop()) # multirow/column
-        self.context[-1] += 1
+        # if following row is spanned from above.
+        if self.active_table.get_rowspan(self.active_table.get_entry_number()):
+           self.body.append(' & ')
+           self.active_table.visit_entry() # increment cell count
+
+    def visit_row(self, node):
+        self.active_table.visit_row()
+
+    def depart_row(self, node):
+        self.body.extend(self.active_table.depart_row())
 
     def visit_enumerated_list(self, node):
         # We create our own enumeration list environment.
@@ -1034,10 +1318,10 @@
             self.body.append(':]')
 
     def visit_figure(self, node):
-        self.body.append( '\\begin{figure}\n' )
+        self.body.append( '\\begin{figure}[htbp]\\begin{center}\n' )
 
     def depart_figure(self, node):
-        self.body.append( '\\end{figure}\n' )
+        self.body.append( '\\end{center}\\end{figure}\n' )
 
     def visit_footer(self, node):
         self.context.append(len(self.body))
@@ -1054,15 +1338,14 @@
             num,text = node.astext().split(None,1)
             num = self.encode(num.strip())
             self.body.append('\\footnotetext['+num+']')
-            self.body.append('{'+self.encode(text)+'}')
-            raise nodes.SkipNode
+            self.body.append('{')
         else:
             self.body.append('\\begin{figure}[b]')
             self.body.append('\\hypertarget{%s}' % node['id'])
 
     def depart_footnote(self, node):
         if self.use_latex_footnotes:
-            self.body.append('}')
+            self.body.append('}\n')
         else:
             self.body.append('\\end{figure}\n')
 
@@ -1070,7 +1353,6 @@
         if self.use_latex_footnotes:
             self.body.append("\\footnotemark["+self.encode(node.astext())+"]")
             raise nodes.SkipNode
-            return
         href = ''
         if node.has_key('refid'):
             href = node['refid']
@@ -1092,6 +1374,20 @@
             return
         self.body.append('}%s' % self.context.pop())
 
+    # footnote/citation label
+    def visit_label(self, node):
+        if isinstance(node.parent, nodes.footnote) and self.use_latex_footnotes:
+            raise nodes.SkipNode
+        elif isinstance(node.parent, nodes.citation) and self._use_latex_citations:
+            pass
+        else:
+            self.body.append('[')
+
+    def depart_label(self, node):
+        if isinstance(node.parent, nodes.citation) and self._use_latex_citations:
+            return
+        self.body.append(']')
+
     # elements generated by the framework e.g. section numbers.
     def visit_generated(self, node):
         pass
@@ -1120,12 +1416,12 @@
         pre = []                        # in reverse order
         post = ['\\includegraphics{%s}' % attrs['uri']]
         inline = isinstance(node.parent, nodes.TextElement)
-        if 'scale' in attrs:
+        if attrs.has_key('scale'):
             # Could also be done with ``scale`` option to
             # ``\includegraphics``; doing it this way for consistency.
             pre.append('\\scalebox{%f}{' % (attrs['scale'] / 100.0,))
             post.append('}')
-        if 'align' in attrs:
+        if attrs.has_key('align'):
             align_prepost = {
                 # By default latex aligns the top of an image.
                 (1, 'top'): ('', ''),
@@ -1165,13 +1461,6 @@
     def depart_interpreted(self, node):
         self.depart_literal(node)
 
-    def visit_label(self, node):
-        # footnote/citation label
-        self.body.append('[')
-
-    def depart_label(self, node):
-        self.body.append(']')
-
     def visit_legend(self, node):
         self.body.append('{\\small ')
 
@@ -1184,10 +1473,11 @@
         * inline markup is supported.
         * serif typeface
 
-        mbox would stop LaTeX from wrapping long lines.
         """
         self.body.append('\\begin{flushleft}\n')
         self.insert_none_breaking_blanks = 1
+        # mbox would stop LaTeX from wrapping long lines.
+        # but line_blocks are allowed to wrap.
         self.line_block_without_mbox = 1
         if self.line_block_without_mbox:
             self.insert_newline = 1
@@ -1205,7 +1495,7 @@
         self.body.append('\n\\end{flushleft}\n')
 
     def visit_list_item(self, node):
-        # HACK append "{}" in case the next character is "[", which would break
+        # Append "{}" in case the next character is "[", which would break
         # LaTeX's list environment (no numbering and the "[" is not printed).
         self.body.append('\\item {} ')
 
@@ -1228,8 +1518,6 @@
         blocks of text, where the inline markup is not recognized,
         but are also the product of the parsed-literal directive,
         where the markup is respected.
-        
-        mbox stops LaTeX from wrapping long lines.
         """
         # In both cases, we want to use a typewriter/monospaced typeface.
         # For "real" literal-blocks, we can use \verbatim, while for all
@@ -1239,34 +1527,39 @@
         # siblings the compose this node: if it is composed by a
         # single element, it's surely is either a real one, otherwise
         # it's a parsed-literal that does not contain any markup.
-        # 
+        #
         if (self.settings.use_verbatim_when_possible and (len(node) == 1)
               # in case of a parsed-literal containing just a "**bold**" word:
               and isinstance(node[0], nodes.Text)):
             self.verbatim = 1
-            self.body.append('\\begin{verbatim}\n')
+            self.body.append('\\begin{quote}\\begin{verbatim}\n')
         else:
             self.literal_block = 1
             self.insert_none_breaking_blanks = 1
-            self.body.append('\\begin{ttfamily}\\begin{flushleft}\n')
-            self.mbox_newline = 1
-            if self.mbox_newline:
-                self.body.append('\\mbox{')
+            if self.active_table.is_open():
+                self.body.append('\n{\\ttfamily \\raggedright \\noindent\n')
+            else:
+                # no quote inside tables, to avoid vertical sppace between
+                # table border and literal block.
+                # BUG: fails if normal text preceeds the literal block.
+                self.body.append('\\begin{quote}')
+                self.body.append('{\\ttfamily \\raggedright \\noindent\n')
             # * obey..: is from julien and never worked for me (grubert).
             #   self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')
 
     def depart_literal_block(self, node):
         if self.verbatim:
-            self.body.append('\n\\end{verbatim}\n')
+            self.body.append('\n\\end{verbatim}\\end{quote}\n')
             self.verbatim = 0
         else:
-            if self.mbox_newline:
-                self.body.append('}')
-            self.body.append('\n\\end{flushleft}\\end{ttfamily}\n')
+            if self.active_table.is_open():
+                self.body.append('\n}\n')
+            else:
+                self.body.append('\n')
+                self.body.append('}\\end{quote}\n')
             self.insert_none_breaking_blanks = 0
-            self.mbox_newline = 0
-            # obey end: self.body.append('}\n')
             self.literal_block = 0
+            # obey end: self.body.append('}\n')
 
     def visit_meta(self, node):
         self.body.append('[visit_meta]\n')
@@ -1302,7 +1595,6 @@
         if self.use_optionlist_for_option_list:
             self.body.append('\\item [')
         else:
-            atts = {}
             if len(node.astext()) > 14:
                 self.body.append('\\multicolumn{2}{l}{')
                 self.context.append('} \\\\\n  ')
@@ -1398,14 +1690,6 @@
     def depart_revision(self, node):
         self.depart_docinfo_item(node)
 
-    def visit_row(self, node):
-        self.context.append(0)
-
-    def depart_row(self, node):
-        self.context.pop()  # remove cell counter
-        self.body.append(' \\\\ \\hline\n')
-        # BUG if multirow cells \cline{x-y}
-
     def visit_section(self, node):
         self.section_level += 1
         # Initialize counter for potential subsections:
@@ -1482,70 +1766,19 @@
         if node['level'] < self.document.reporter['writer'].report_level:
             raise nodes.SkipNode
 
-
     def depart_system_message(self, node):
         self.body.append('\n')
 
-    def get_colspecs(self):
-        """
-        Return column specification for longtable.
-
-        Assumes reST line length being 80 characters.
-        Table width is hairy.
-
-        === ===
-        ABC DEF
-        === ===
-
-        usually gets to narrow, therefore we add 1 (fiddlefactor).
-        """
-        width = 80
-
-        total_width = 0.0
-        # first see if we get too wide.
-        for node in self.colspecs:
-            colwidth = float(node['colwidth']+1) / width
-            total_width += colwidth
-        # donot make it full linewidth
-        factor = 0.93
-        if total_width > 1.0:
-            factor /= total_width
-
-        latex_table_spec = ""
-        for node in self.colspecs:
-            colwidth = factor * float(node['colwidth']+1) / width
-            latex_table_spec += "|p{%.2f\\locallinewidth}" % (colwidth+0.005)
-        self.colspecs = []
-        return latex_table_spec+"|"
-
     def visit_table(self, node):
-        if self.use_longtable:
-            self.body.append('\n\\begin{longtable}[c]')
-        else:
-            self.body.append('\n\\begin{tabularx}{\\linewidth}')
-            self.context.append('table_sentinel') # sentinel
-            self.context.append(0) # column counter
+        if self.active_table.is_open():
+            print 'nested tables are not supported'
+            raise AssertionError
+        self.active_table.open()
+        self.body.append('\n' + self.active_table.get_opening())
 
     def depart_table(self, node):
-        if self.use_longtable:
-            self.body.append('\\end{longtable}\n')
-        else:
-            self.body.append('\\end{tabularx}\n')
-            sentinel = self.context.pop()
-            if sentinel != 'table_sentinel':
-                print 'context:', self.context + [sentinel]
-                raise AssertionError
-
-    def table_preamble(self):
-        if self.use_longtable:
-            self.body.append('{%s}\n' % self.get_colspecs())
-            if self.table_caption:
-                self.body.append('\\caption{%s}\\\\\n' % self.table_caption)
-                self.table_caption = None
-        else:
-            if self.context[-1] != 'table_sentinel':
-                self.body.append('{%s}' % ('|X' * self.context.pop() + '|'))
-                self.body.append('\n\\hline')
+        self.body.append(self.active_table.get_closing() + '\n')
+        self.active_table.close()
 
     def visit_target(self, node):
         # BUG: why not (refuri or refid or refname) means not footnote ?
@@ -1562,13 +1795,12 @@
     def visit_tbody(self, node):
         # BUG write preamble if not yet done (colspecs not [])
         # for tables without heads.
-        if self.colspecs:
+        if not self.active_table.get('preamble written'):
             self.visit_thead(None)
-            self.depart_thead(None)
-        self.body.append('%[visit_tbody]\n')
+            # self.depart_thead(None)
 
     def depart_tbody(self, node):
-        self.body.append('%[depart_tbody]\n')
+        pass
 
     def visit_term(self, node):
         self.body.append('\\item[')
@@ -1586,29 +1818,27 @@
         pass
 
     def visit_thead(self, node):
-        # number_of_columns will be zero after get_colspecs.
-        # BUG ! push onto context for depart to pop it.
-        number_of_columns = len(self.colspecs)
-        self.table_preamble()
-        #BUG longtable needs firstpage and lastfooter too.
-        self.body.append('\\hline\n')
+        self.body.append('{%s}\n' % self.active_table.get_colspecs())
+        if self.active_table.caption:
+            self.body.append('\\caption{%s}\\\\\n' % self.active_table.caption)
+        self.active_table.set('preamble written',1)
+        # TODO longtable supports firsthead and lastfoot too.
+        self.body.extend(self.active_table.visit_thead())
 
     def depart_thead(self, node):
-        if self.use_longtable:
-            # the table header written should be on every page
-            # => \endhead
-            self.body.append('\\endhead\n')
-            # and the firsthead => \endfirsthead
-            # BUG i want a "continued from previous page" on every not
-            # firsthead, but then we need the header twice.
-            #
-            # there is a \endfoot and \endlastfoot too.
-            # but we need the number of columns to
-            # self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
-            # self.body.append('\\hline\n\\endfoot\n')
-            # self.body.append('\\hline\n')
-            # self.body.append('\\endlastfoot\n')
-
+        # the table header written should be on every page
+        # => \endhead
+        self.body.extend(self.active_table.depart_thead())
+        # and the firsthead => \endfirsthead
+        # BUG i want a "continued from previous page" on every not
+        # firsthead, but then we need the header twice.
+        #
+        # there is a \endfoot and \endlastfoot too.
+        # but we need the number of columns to
+        # self.body.append('\\multicolumn{%d}{c}{"..."}\n' % number_of_columns)
+        # self.body.append('\\hline\n\\endfoot\n')
+        # self.body.append('\\hline\n')
+        # self.body.append('\\endlastfoot\n')
 
     def visit_tip(self, node):
         self.visit_admonition(node, 'tip')
@@ -1630,14 +1860,13 @@
                 if l>0:
                     l = l-1
                 # pdftex does not like "_" subscripts in titles
-                text = node.astext().replace("_","\\_")
+                text = self.encode(node.astext())
                 self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
                         (l,text,node.parent['id']))
 
     def visit_title(self, node):
         """Only 3 section levels are supported by LaTeX article (AFAIR)."""
 
-
         if isinstance(node.parent, nodes.topic):
             # section titles before the table of contents.
             self.bookmark(node)
@@ -1646,12 +1875,15 @@
             self.body.append('\\subsection*{~\\hfill ')
             # the closing brace for subsection.
             self.context.append('\\hfill ~}\n')
-        elif isinstance(node.parent, nodes.sidebar):
+        # TODO: for admonition titles before the first section
+        # either specify every possible node or ... ?
+        elif isinstance(node.parent, nodes.sidebar) \
+        or isinstance(node.parent, nodes.admonition):
             self.body.append('\\textbf{\\large ')
             self.context.append('}\n\\smallskip\n')
         elif isinstance(node.parent, nodes.table):
             # caption must be written after column spec
-            self.table_caption = node.astext()
+            self.active_table.caption = node.astext()
             raise nodes.SkipNode
         elif self.section_level == 0:
             # document title
@@ -1690,8 +1922,6 @@
         self.body.append('\n')
 
     def visit_rubric(self, node):
-#        self.body.append('\\hfill {\\color{red}\\bfseries{}')
-#        self.context.append('} \\hfill ~\n')
         self.body.append('\\rubric{')
         self.context.append('}\n')
 
@@ -1705,7 +1935,6 @@
         self.body.append('\n\n')
 
     def depart_transition(self, node):
-        #self.body.append('[depart_transition]')
         pass
 
     def visit_version(self, node):



More information about the Zope-Checkins mailing list