Code Search for Developers
 
 
  

latex2e.py from gzz at Krugle


Show latex2e.py syntax highlighted

#! /usr/bin/env python
# 
# Copyright (c) 2003, Asko Soukka and Janne V. Kujala
# 
# This file is part of Gzz.
# 
# Gzz is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# Gzz is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General
# Public License for more details.
# 
# You should have received a copy of the GNU Lesser General
# Public License along with Gzz; if not, write to the Free
# Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA  02111-1307  USA
# 
# 



"""
:Author: Engelbert Gruber (hacked by the Gzz project)
:Contact: grubert@users.sourceforge.net
:Revision: $Revision: 1.42 $
:Date: $Date: 2003/02/21 20:02:55 $
:Copyright: This module has been placed in the public domain.

LaTeX2e document tree Writer.
"""

__docformat__ = 'reStructuredText'

# convention deactivate code by two # e.g. ##.

import sys
import time
import re
import string
from types import ListType
from docutils import writers, nodes, languages

#### We diss babel, because /cite didn't work :)
## country code by a.schlock.
## partly manually converted from iso and babel stuff, dialects and some
## languages remain missing (austrian, UKEnglish, brazillian etc.)
#_ISO639_TO_BABEL = {
#    'no': 'norsk',     #XXX added by hand ( forget about nynorsk?)
#    'gd': 'scottish',  #XXX added by hand
#    'hu': 'magyar',    #XXX added by hand
#    'pt': 'portuguese',#XXX added by hand
#    'sl': 'slovenian',
#    'af': 'afrikaans',
#    'bg': 'bulgarian',
#    'br': 'breton',
#    'ca': 'catalan',
#    'cs': 'czech',
#    'cy': 'welsh',
#    'da': 'danish',

#    'de': 'ngerman',  #XXX rather than german
#    'el': 'greek',
#    'en': 'english',
#    'eo': 'esperanto',
#    'es': 'spanish',
#    'et': 'estonian',
#    'eu': 'basque',
#    'fi': 'finnish',
#    'ga': 'irish',
#    'gl': 'galician',
#    'he': 'hebrew',
#    'hr': 'croatian',
#    'hu': 'hungarian',
#    'is': 'icelandic',
#    'it': 'italian',
#    'la': 'latin',
#    'nl': 'dutch',
#    'pl': 'polish',
#    'pt': 'portuguese',
#    'ro': 'romanian',
#    'ru': 'russian',
#    'sk': 'slovak',
#    'sr': 'serbian',
#    'sv': 'swedish',
#    'tr': 'turkish',
#    'uk': 'ukrainian'
#    }

class Writer(writers.Writer):

    supported = ('latex','latex2e')
    """Formats this writer supports."""

    settings_spec = (
        'LaTeX-Specific Options',
        'The LaTeX "--output-encoding" default is "latin-1".',
        (('Specify documentclass.  Default is "article".',
          ['--documentclass'],
          {'default': 'article', }),
         ('Options to the document class. Default is "10pt".',
          ['--documentclass-options'],
          {'default': '10pt' }),       
         ('Format for footnote references: one of "superscript" or '
          '"brackets".  Default is "superscript".',
          ['--footnote-references'],
          {'choices': ['superscript', 'brackets'], 'default': 'brackets',
           'metavar': '<FORMAT>'}),
         ('Link to the stylesheet in the output LaTeX file.  This is the '
          'default.',
          ['--link-stylesheet'],
          {'dest': 'embed_stylesheet', 'action': 'store_false'}),
         ('Embed the stylesheet in the output LaTeX file.  The stylesheet '
          'file must be accessible during processing (--stylesheet-path is '
          'recommended).',
          ['--embed-stylesheet'],
          {'action': 'store_true'}),
         ('Table of contents by docutils (default) or latex. Latex(writer) supports only '
          'one ToC per document, but docutils does not write pagenumbers.',
          ['--use-latex-toc'], {'default': 0}),
         ))

    settings_default_overrides = {'output_encoding': 'latin-1'}

    output = None
    """Final translated form of `document`."""

    def translate(self):
        visitor = LaTeXTranslator(self.document)
        self.document.walkabout(visitor)
        self.output = visitor.astext()
        self.head_prefix = visitor.head_prefix
        self.head = visitor.head
        self.body_prefix = visitor.body_prefix
        self.body = visitor.body
        self.body_suffix = visitor.body_suffix

"""
Notes on LaTeX
--------------

* Put labels inside environments::
    \chapter[optional]{title}
    \label{lab} % optional, for cross referencing
    text for this unit ...
* unnumbered sections are not written to tableofcontents.
  a. donot print numbers::
        \renewcommand{\thesection}{}
  b. use::
        \addcontentsline{file}{sec_unit}{entry}
     file toc,lof lot
     sec_unit section, subsection , ...
     entry the line::
        \numberline text pagenumber
  X. latex does not support multiple tocs in one document.
     (might be no limitation except for docutils documentation)

* sectioning::
    \part
    \chapter (report style only) 
    \section
    \subsection
    \subsubsection
    \paragraph
    \subparagraph
    \subsubparagraph (milstd and book-form styles only) 
    \subsubsubparagraph (milstd and book-form styles only)

* documentclass options

  all notitlepage.

  article: 11pt, 12pt, twoside, twocolumn, draft, fleqn, leqno, acm
  
  report: 11pt, 12pt, twoside, twocolumn, draft, fleqn, leqno, acm
  
  letter: 11pt, 12pt, fleqn, leqno, acm
  
  book: 11pt, 12pt, twoside,twocolumn, draft, fleqn, leqno
  
* width 

  * linewidth - width of a line in the local environment
  * textwidth - the width of text on the page

  Maybe always use linewidth ?
"""    

class Babel:
    """Language specifics for LaTeX."""
    def __init__(self,lang):
        self.language = lang
        if re.search('^de',self.language):
            self.quotes = ("\"`", "\"'")
        else:    
            self.quotes = ("``", "''")
        self.quote_index = 0
        
    def next_quote(self):
        q = self.quotes[self.quote_index]
        self.quote_index = (self.quote_index+1)%2
        return q

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
    
    # added by gzz / Vegai
    docinfo = []

    ## For narrower things (tables, docinfos use admwidth in latex construct).
    d_class = 'article'    # document.settings.stylesheet
    d_paper = 'a4paper'
    d_margins = '2cm'
    d_stylesheet_path = 'style.tex'
    # for pdflatex some other package. pslatex

#    latex_head = '\\documentclass[%s]{%s}\n'
    latex_head = '\\documentclass{%s}\n'
#    encoding = '\\usepackage[latin1]{inputenc}\n'
    linking = '\\usepackage{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 = 0
    # TODO: use mixins for different implementations.
    # list environment for option-list. else tabularx
    use_optionlist_for_option_list = 1
    # list environment for docinfo. else tabularx
    use_optionlist_for_docinfo = 0 # NOT YET IN USE

    def __init__(self, document):
        nodes.NodeVisitor.__init__(self, document)
        self.settings = settings = document.settings
        self.d_class = settings.documentclass
        self.d_options = settings.documentclass_options
        self.use_latex_toc = settings.use_latex_toc
        # language: labels, bibliographic_fields, and author_separators.
        # to allow writing labes for specific languages.
        self.language = languages.get_language(settings.language_code)
        self.babel = Babel(settings.language_code)
        self.author_separator = self.language.author_separators[0]
#        if _ISO639_TO_BABEL.has_key(settings.language_code):
#            self.d_options += ',%s' % \
#                    _ISO639_TO_BABEL[settings.language_code]
        self.head_prefix = [
#              self.latex_head % (self.d_options,self.d_class),
              self.latex_head % (self.d_class),
#/cite didn't work with babel (jvk)
#              '\\usepackage{babel}\n',     # language is in documents settings.
#              '\\usepackage{shortvrb}\n',  # allows verb in footnotes.
#              self.encoding,
              # * tabularx: for docinfo, automatic width of columns, always on one page.
              '\\usepackage{tabularx}\n',
              '\\usepackage{longtable}\n',
              # possible other packages.
              # * fancyhdr
              # * ltxtable is a combination of tabularx and longtable (pagebreaks).
              #   but ??
              #
              # extra space between text in tables and the line above them
              '\\setlength{\\extrarowheight}{2pt}\n',
#              '\\usepackage{amsmath}\n',   # what fore amsmath. 
              '\\usepackage{graphicx}\n',
              '\\usepackage{multirow}\n',
              self.linking,
#              # geometry and fonts might go into style.tex.
#              self.geometry % (self.d_paper, self.d_margins),
#              #
#              self.generator,
#              # admonition width and docinfo tablewidth
              '\\newlength{\\admwidth}\n\\addtolength{\\admwidth}{0.9\\textwidth}\n',
#             # optionlist environment
#              '\\newcommand{\\optionlistlabel}[1]{\\bf #1 \\hfill}\n'
#              '\\newenvironment{optionlist}[1]\n',
#              '{\\begin{list}{}\n'
#              '  {\\setlength{\\labelwidth}{#1}\n'
#              '   \\setlength{\\rightmargin}{1cm}\n'
#              '   \\setlength{\\leftmargin}{\\rightmargin}\n'
#              '   \\addtolength{\\leftmargin}{\\labelwidth}\n'
#              '   \\addtolength{\\leftmargin}{\\labelsep}\n'
#              '   \\renewcommand{\\makelabel}{\\optionlistlabel}}\n'
#              '}{\\end{list}}\n',
#              ## stylesheet is last: so it might be possible to overwrite defaults.
              self.stylesheet % (self.d_stylesheet_path),
                            ]
        if self.linking: # and maybe check for pdf
            self.pdfinfo = [ ]
            self.pdfauthor = None
            # pdftitle, pdfsubject, pdfauthor, pdfkeywords, pdfcreator, pdfproducer
        else:
            self.pdfinfo = None
        self.head = []
        self.body_prefix = ['']
#        self.body_prefix = ['\\raggedbottom\n']
        # separate title, so we can appen subtitle.
        self.title = ""
        self.body = []
        self.body_suffix = ['\n']
        self.section_level = 0
        self.context = []
        self.topic_class = ''
        # column specification for tables
        self.colspecs = []
        # verbatim: to tell encode not to encode.
        self.verbatim = 0
        # insert_newline: to tell encode to add newline.
        self.insert_newline = 0
        # mbox_newline: to tell encode to add mbox and newline.
        self.mbox_newline = 0
        # enumeration is done by list environment.
        self._enum_cnt = 0

    def language_label(self, docutil_label):
        return self.language.labels[docutil_label]

    def encode(self, text):
        """
        Encode special characters in `text` & return.
            # $ % & ~ _ ^ \ { }
        Escaping with a backslash does not help with backslashes, ~ and ^.

            < > are only available in math-mode (really ?)
            $ starts math- mode.
        AND quotes:
        
        """
        if self.verbatim:
            return text
        # first the backslash
        text = text.replace("\\", '{\\textbackslash}')
        # then dollar
        text = text.replace("$", '{\\$}')
        # then all that needs math mode
        text = text.replace("<", '{$<$}')
        text = text.replace(">", '{$>$}')
        # then
        text = text.replace("&", '{\\&}')
        text = text.replace("_", '{\\_}')
        text = text.replace("^", '{\\verb|^|}') # ugly
        text = text.replace("%", '{\\%}')
        text = text.replace("#", '{\\#}')
        text = text.replace("~", '{\\~{ }}')
        t = None
        for part in text.split('"'):
            if t == None:
                t = part
            else:
                t += self.babel.next_quote() + part
        text = t
        if self.insert_newline:
            text = text.replace("\n", '\\\\\n')
        elif self.mbox_newline:
            text = text.replace("\n", '}\\\\\n\\mbox{')
            text = text.replace(' ', '~')
        # unicode !!! 
        text = text.replace(u'\u2020', '{$\\dagger$}')
        return text

    def attval(self, text,
               whitespace=re.compile('[\n\r\t\v\f]')):
        """Cleanse, encode, and return attribute value text."""
        return self.encode(whitespace.sub(' ', text))

    def astext(self):
        if self.pdfinfo:
            if self.pdfauthor:
                self.pdfinfo.append('pdfauthor={%s}' % self.pdfauthor)
            pdfinfo = '\\hypersetup{\n' + ',\n'.join(self.pdfinfo) + '\n}\n'
        else:
            pdfinfo = ''
        title = '\\title{%s}\n' % self.title    
        return ''.join(self.head_prefix + [title] + self.head + [pdfinfo]
                       + self.body_prefix  + self.body + self.body_suffix)

    def visit_Text(self, node):
        self.body.append(self.encode(node.astext()))

    def depart_Text(self, node):
        pass

    def visit_address(self, node):
        self.visit_docinfo_item(node, 'address')

    def depart_address(self, node):
        self.depart_docinfo_item(node)

    def visit_admonition(self, node, name):
        self.body.append('\\begin{center}\n');
        # alternatives: parbox or minipage.
        # minpage has footnotes on the minipage.
        # BUG there is no border.
        self.body.append('\\parbox{\\admwidth}{\\textbf{'
                         + self.language.labels[name] + '}\n')

    def depart_admonition(self):
        self.body.append('}\n')
        self.body.append('\\end{center}\n');

    def visit_attention(self, node):
        self.visit_admonition(node, 'attention')

    def depart_attention(self, node):
        self.depart_admonition()

    def visit_author(self, node):
        self.visit_docinfo_item(node, 'author')

    def depart_author(self, node):
        self.depart_docinfo_item(node)

    def visit_authors(self, node):
        # ignore. visit_author is called for each one
        # self.visit_docinfo_item(node, 'author')
        pass

    def depart_authors(self, node):
        # self.depart_docinfo_item(node)
        pass

    def visit_block_quote(self, node):
        self.body.append( '\\begin{quote}\n')

    def depart_block_quote(self, node):
        self.body.append( '\\end{quote}\n')

    def visit_bullet_list(self, node):
        if not self.use_latex_toc and self.topic_class == 'contents':
            self.body.append( '\\begin{list}{}{}\n' )
        else:
            self.body.append( '\\begin{itemize}\n' )

    def depart_bullet_list(self, node):
        if not self.use_latex_toc and self.topic_class == 'contents':
            self.body.append( '\\end{list}\n' )
        else:
            self.body.append( '\\end{itemize}\n' )

    def visit_caption(self, node):
        self.body.append( '\\caption{\n' )
        for child in node.parent.children:
            if child.attributes.has_key('label'):
                self.body.append('\\label{%s}\n' % child.attributes['label'])

    def depart_caption(self, node):
        self.body.append('}')

    def visit_caution(self, node):
        self.visit_admonition(node, 'caution')

    def depart_caution(self, node):
        self.depart_admonition()

    def visit_citation(self, node):
        self.body.append([None, 'visit_citation'])
        #self.visit_footnote(node)

    def depart_citation(self, node):
        while self.body.pop() != [None, 'visit_citation']: pass
        #self.depart_footnote(node)

    def visit_citation_reference(self, node):
        href = ''
        if node.has_key('refid'):
            href = node['refid']
        if not href.startswith('ref-'):
            if href.find('-onpage-') > -1:
                (name, rest) = href.split('-onpage-')
                if rest.find('-') > 0:
                    cf = 'pp. %s-%s' % (rest.split('-')[0],
                                        rest.split('-')[1])
                else:
                    cf = 'p. %s' % rest

                self.body.append('\\cite[%s]{%s}' % (cf, name))
            else:
                self.body.append('\\cite{%s}' % ','.join(href.split('-andalso-')))
        else:
            self.body.append('\\ref{%s}' % href[4:])
        self.body.append(None)

        ##elif node.has_key('refname'):
        ##    href = self.document.nameids[node['refname']]
        ##self.body.append('[\\hyperlink{%s}{' % href)

    def depart_citation_reference(self, node):
        while not (self.body.pop() is None):
            pass
        ##self.body.append('}]')

    def visit_classifier(self, node):
        self.body.append( '(\\textbf{' )

    def depart_classifier(self, node):
        self.body.append( '})\n' )

    def visit_colspec(self, node):
        if self.use_longtable:
            self.colspecs.append(node)
        else:    
            self.context[-1] += 1

    def depart_colspec(self, node):
        pass

    def visit_comment(self, node,
                      sub=re.compile('\n').sub):
        """Escape end of line by a ne comment start in comment text."""
        self.body.append('%% %s \n' % sub('\n% ', node.astext()))
        raise nodes.SkipNode

    def visit_contact(self, node):
        self.visit_docinfo_item(node, 'contact')

    def depart_contact(self, node):
        self.depart_docinfo_item(node)

    def visit_copyright(self, node):
        self.visit_docinfo_item(node, 'copyright')

    def depart_copyright(self, node):
        self.depart_docinfo_item(node)

    def visit_danger(self, node):
        self.visit_admonition(node, 'danger')

    def depart_danger(self, node):
        self.depart_admonition()

    def visit_date(self, node):
        self.visit_docinfo_item(node, 'date')

    def depart_date(self, node):
        self.depart_docinfo_item(node)

#    def visit_decoration(self, node):
#        pass

#    def depart_decoration(self, node):
#        pass

    def visit_definition(self, node):
        self.body.append('%[visit_definition]\n')

    def depart_definition(self, node):
        self.body.append('\n')
        self.body.append('%[depart_definition]\n')

    def visit_definition_list(self, node):
        self.body.append( '\\begin{description}\n' )

    def depart_definition_list(self, node):
        self.body.append( '\\end{description}\n' )

    def visit_definition_list_item(self, node):
        self.body.append('%[visit_definition_list_item]\n')

    def depart_definition_list_item(self, node):
        self.body.append('%[depart_definition_list_item]\n')

    def visit_description(self, node):
        if self.use_optionlist_for_option_list:
            self.body.append( ' ' )
        else:    
            self.body.append( ' & ' )

    def depart_description(self, node):
        pass

    def visit_docinfo(self, node):
        self.docinfo = []
        self.docinfo.append('%' + '_'*75 + '\n')
        self.docinfo.append('\\begin{center}\n')
        self.docinfo.append('\\begin{tabularx}{\\admwidth}{lX}\n')

    def depart_docinfo(self, node):
        self.docinfo.append('\\end{tabularx}\n')
        self.docinfo.append('\\end{center}\n')
        self.body = self.docinfo + self.body
        # clear docinfo, so field names are no longer appended.
        self.docinfo = None
        if self.use_latex_toc:
            self.body.append('\\tableofcontents\n\n\\bigskip\n')

    def visit_docinfo_item(self, node, name):
        # should we stick to latex or docutils.
        # latex article has its own handling of date and author.
        # If we use it we get latexs language handling.
        latex_docinfo = 0
        
        if name == 'abstract':
            # NOTE tableofcontents before or after ?
            # eg after: depart_docinfo
            # NOTE this limits abstract to text.
            self.body.append('\\begin{abstract}\n')
            self.context.append('\\end{abstract}\n')
            self.context.append(self.body)
            self.context.append(len(self.body))
        else:
            self.docinfo.append('\\textbf{%s}: &\n\t' % self.language_label(name))
            if name == 'author':
                if not self.pdfinfo == None:
                    if not self.pdfauthor:
                        self.pdfauthor = self.attval(node.astext())
                    else:
                        self.pdfauthor += self.author_separator + self.attval(node.astext())
                if latex_docinfo:
                    self.head.append('\\author{%s}\n' % self.attval(node.astext()))
                    raise nodes.SkipNode
                else:
                    # avoid latexs maketitle generating one for us.
                    self.head.append('\\author{}\n')
            elif name == 'date':
                if latex_docinfo:
                    self.head.append('\\date{%s}\n' % self.attval(node.astext()))
                    raise nodes.SkipNode
                else:
                    # avoid latexs maketitle generating one for us.
                    self.head.append("\\date{}\n")
            if name == 'address':
                self.insert_newline = 1 
                self.docinfo.append('{\\raggedright\n')
                self.context.append(' } \\\\\n')
            else:    
                self.context.append(' \\\\\n')
            self.context.append(self.docinfo)
            self.context.append(len(self.body))
        # \thanks is a footnote to the title.

    def depart_docinfo_item(self, node):
        size = self.context.pop()
        dest = self.context.pop()
        tail = self.context.pop()
        tail = self.body[size:] + [tail]
        del self.body[size:]
        dest.extend(tail)
        # for address we did set insert_newline
        self.insert_newline = 0

    def visit_doctest_block(self, node):
        self.body.append( '\\begin{verbatim}' )

    def depart_doctest_block(self, node):
        self.body.append( '\\end{verbatim}\n' )

    def visit_document(self, node):
        self.body_prefix.append('\\begin{document}\n')
        self.body_prefix.append('\\maketitle\n\n')
        # alternative use titlepage environment.
        # \begin{titlepage}

    def depart_document(self, node):
        self.body_suffix.append('\\end{document}\n')

    def visit_emphasis(self, node):
        self.body.append('\\emph{')

    def depart_emphasis(self, node):
        self.body.append('}')

    def visit_entry(self, node):
        # cell separation
        column_one = 1
        if self.context[-1] > 0:
            column_one = 0
        if not column_one:
            self.body.append(' & ')

        # multi{row,column}
        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 = {}
        if node.has_key('morerows'):
            count = node['morerows'] + 1
            self.body.append('\\multirow{%d}*{' % count)
            self.context.append('}')
        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 = '|'
            else:
                bar = ''
            count = node['morecols'] + 1
            self.body.append('\\multicolumn{%d}{%sl|}{' % (count, bar))
            self.context.append('}')
        else:
            self.context.append('')

        # header / not header
        if isinstance(node.parent.parent, nodes.thead):
            self.body.append('\\textbf{')
            self.context.append('}')
        else:
            self.context.append('')

    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

    def visit_enumerated_list(self, node):
        # We create our own enumeration list environment.
        # This allows to set the style and starting value
        # and unlimited nesting.
        self._enum_cnt += 1

        enum_style = {'arabic':'arabic',
                'loweralpha':'alph',
                'upperalpha':'Alph', 
                'lowerroman':'roman',
                'upperroman':'Roman' };
        enumtype = "arabic"            
        if node.has_key('enumtype'):
            enumtype = node['enumtype']
        if enum_style.has_key(enumtype):
            enumtype = enum_style[enumtype]
        counter_name = "listcnt%d" % self._enum_cnt;
        self.body.append('\\newcounter{%s}\n' % counter_name)
        self.body.append('\\begin{list}{\\%s{%s}}\n' % (enumtype,counter_name))
        self.body.append('{\n')
        self.body.append('\\usecounter{%s}\n' % counter_name)
        # set start after usecounter, because it initializes to zero.
        if node.has_key('start'):
            self.body.append('\\addtocounter{%s}{%d}\n' \
                    % (counter_name,node['start']-1))
        ## set rightmargin equal to leftmargin
        self.body.append('\\setlength{\\rightmargin}{\\leftmargin}\n')
        self.body.append('}\n')

    def depart_enumerated_list(self, node):
        self.body.append('\\end{list}\n')

    def visit_error(self, node):
        self.visit_admonition(node, 'error')

    def depart_error(self, node):
        self.depart_admonition()

    def visit_field(self, node):
        # real output is done in siblings: _argument, _body, _name
        pass

    def depart_field(self, node):
        self.body.append('\n')
        ##self.body.append('%[depart_field]\n')

    def visit_field_argument(self, node):
        self.body.append('%[visit_field_argument]\n')

    def depart_field_argument(self, node):
        self.body.append('%[depart_field_argument]\n')

    def visit_field_body(self, node):
        # BUG by attach as text we loose references.
        if self.docinfo:
            self.docinfo.append('%s \\\\\n' % node.astext())
            raise nodes.SkipNode
        # what happens if not docinfo

    def depart_field_body(self, node):
        self.body.append( '\n' )

    def visit_field_list(self, node):
        if not self.docinfo:
            self.body.append('\\begin{quote}\n')
            self.body.append('\\begin{description}\n')

    def depart_field_list(self, node):
        if not self.docinfo:
            self.body.append('\\end{description}\n')
            self.body.append('\\end{quote}\n')

    def visit_field_name(self, node):
        # BUG this duplicates docinfo_item
        if self.docinfo:
            self.docinfo.append('\\textbf{%s}: &\n\t' % node.astext())
            raise nodes.SkipNode
        else:
            self.body.append('\\item [')

    def depart_field_name(self, node):
        if not self.docinfo:
            self.body.append(':]')

    def visit_figure(self, node):
        environment = "figure"
        for child in node.children:
            if child.attributes.has_key('environment'):
                environment = child.attributes['environment']
        self.body.append('\\begin{'+environment+'}\n')

    def depart_figure(self, node):
        environment = "figure"
        for child in node.children:
            if child.attributes.has_key('environment'):
                environment = child.attributes['environment']
        self.body.append('\n\\end{'+environment+'}\n')

    def visit_footer(self, node):
        self.context.append(len(self.body))

    def depart_footer(self, node):
        start = self.context.pop()
        footer = (['\n\\begin{center}\small\n']
                  + self.body[start:] + ['\n\\end{center}\n'])
        self.body_suffix[:0] = footer
        del self.body[start:]

    def visit_footnote(self, node):
        notename = node['id']
        self.body.append(None)

    def depart_footnote(self, node):
        list = []
        while 1:
            el = self.body.pop()
            if el is None: break
            list.insert(0, el)

        i = self.body.index('!footnote:%s!' % node['id'])
        self.body[i:i+1] = ['\\footnote{'] + list[3:] + ['}']

        if i>0 and self.body[i-1][-1] == ' ':
            self.body[i-1] = self.body[i-1][:-1]

    def visit_footnote_reference(self, node):
        href = ''
        if node.has_key('refid'):
            href = node['refid']

        self.body.append('!footnote:%s!' % href)
            
        #elif node.has_key('refname'):
        #    href = self.document.nameids[node['refname']]
        #format = self.settings.footnote_references
        #if format == 'brackets':
        #    suffix = '['
        #    self.context.append(']')
        #elif format == 'superscript':
        #    suffix = '\\raisebox{.5em}[0em]{\\scriptsize'
        #    self.context.append('}')
        #else:                           # shouldn't happen
        #    raise AssertionError('Illegal footnote reference format.')
        #self.body.append('%s\\hyperlink{%s}{' % (suffix,href))

    def depart_footnote_reference(self, node):
        self.body.pop() # remove footnote number, inserted by TeX
        #self.body.append('}%s' % self.context.pop())

    def visit_generated(self, node):
        pass

    def depart_generated(self, node):
        pass

    def visit_header(self, node):
        self.context.append(len(self.body))

    def depart_header(self, node):
        start = self.context.pop()
        self.body_prefix.append('\n\\verb|begin_header|\n')
        self.body_prefix.extend(self.body[start:])
        self.body_prefix.append('\n\\verb|end_header|\n')
        del self.body[start:]

    def visit_hint(self, node):
        self.visit_admonition(node, 'hint')

    def depart_hint(self, node):
        self.depart_admonition()

    def visit_image(self, node):
        atts = node.attributes.copy()
        if not atts.has_key('environment') or \
               (atts.has_key('environment') and \
                not atts['uri'] == '__extended__'):
            href = atts['uri']
            self.body.append('\\centering\n')
            self.body.append('\\includegraphics')
            if atts.has_key('width'): self.body.append('[width=%s]' % atts['width'])
            else: self.body.append('[width=\\columnwidth]')
            self.body.append('{%s}\n' % href)
        ##self.body.append('\\end{center}\n')

    def depart_image(self, node):
        pass

    def visit_important(self, node):
        self.visit_admonition(node, 'important')

    def depart_important(self, node):
        self.depart_admonition()

    def visit_interpreted(self, node):
        # @@@ Incomplete, pending a proper implementation on the
        # Parser/Reader end.
        self.visit_literal(node)

    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 ')

    def depart_legend(self, node):
        self.body.append('}')

    def visit_line_block(self, node):
        """line-block: 
        * whitespace (including linebreaks) is significant 
        * inline markup is supported. 
        * serif typeface"""
        self.mbox_newline = 1
        self.body.append('\\begin{flushleft}\n\\mbox{')

    def depart_line_block(self, node):
        self.body.append('}\n\\end{flushleft}\n')
        self.mbox_newline = 0

    def visit_list_item(self, node):
        self.body.append('\\item ')

    def depart_list_item(self, node):
        self.body.append('\n')

    def visit_literal(self, node):
        self.body.append('\\texttt{')

    def depart_literal(self, node):
        self.body.append('}')

    def visit_literal_block(self, node):
        self.use_verbatim_for_literal = 1
        if (self.use_verbatim_for_literal):
            self.verbatim = 1
            self.body.append('\\begin{quote}\n')
            self.body.append('\\begin{verbatim}\n')
        else:
            self.body.append('{\\obeylines\\obeyspaces\\ttfamily\n')

    def depart_literal_block(self, node):
        if self.use_verbatim_for_literal:
            self.body.append('\n\\end{verbatim}\n')
            self.body.append('\\end{quote}\n')
            self.verbatim = 0
        else:
            self.body.append('}\n')

    def visit_meta(self, node):
        self.body.append('[visit_meta]\n')
        # BUG maybe set keywords for pdf
        ##self.head.append(self.starttag(node, 'meta', **node.attributes))

    def depart_meta(self, node):
        self.body.append('[depart_meta]\n')

    def visit_note(self, node):
        self.visit_admonition(node, 'note')

    def depart_note(self, node):
        self.depart_admonition()

    def visit_option(self, node):
        if self.context[-1]:
            # this is not the first option
            self.body.append(', ')

    def depart_option(self, node):
        # flag tha the first option is done.
        self.context[-1] += 1

    def visit_option_argument(self, node):
        """The delimiter betweeen an option and its argument."""
        self.body.append(node.get('delimiter', ' '))

    def depart_option_argument(self, node):
        pass

    def visit_option_group(self, node):
        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  ')
            else:
                self.context.append('')
            self.body.append('\\texttt{')
        # flag for first option    
        self.context.append(0)

    def depart_option_group(self, node):
        self.context.pop() # the flag
        if self.use_optionlist_for_option_list:
            self.body.append('] ')
        else:
            self.body.append('}')
            self.body.append(self.context.pop())

    def visit_option_list(self, node):
        self.body.append('% [option list]\n')
        if self.use_optionlist_for_option_list:
            self.body.append('\\begin{optionlist}{3cm}\n')
        else:
            self.body.append('\\begin{center}\n')
            # BUG: use admwidth or make it relative to textwidth ?
            self.body.append('\\begin{tabularx}{.9\\linewidth}{lX}\n')

    def depart_option_list(self, node):
        if self.use_optionlist_for_option_list:
            self.body.append('\\end{optionlist}\n')
        else:
            self.body.append('\\end{tabularx}\n')
            self.body.append('\\end{center}\n')

    def visit_option_list_item(self, node):
        pass

    def depart_option_list_item(self, node):
        if not self.use_optionlist_for_option_list:
            self.body.append('\\\\\n')

    def visit_option_string(self, node):
        ##self.body.append(self.starttag(node, 'span', '', CLASS='option'))
        pass

    def depart_option_string(self, node):
        ##self.body.append('</span>')
        pass

    def visit_organization(self, node):
        self.visit_docinfo_item(node, 'organization')

    def depart_organization(self, node):
        self.depart_docinfo_item(node)

    def visit_paragraph(self, node):
        if not self.topic_class == 'contents':
            self.body.append('\n')

    def depart_paragraph(self, node):
        if self.topic_class == 'contents':
            self.body.append('\n')
        else:
            self.body.append('\n')

    def visit_problematic(self, node):
        self.body.append('{\\color{red}\\bfseries{}')

    def depart_problematic(self, node):
        self.body.append('}')

    def visit_raw(self, node):
        if node.has_key('format') and node['format'].lower() == 'latex':
            self.body.append(node.astext())
        raise nodes.SkipNode

    def visit_reference(self, node):
        # for pdflatex hyperrefs might be supported
        if node.has_key('refuri'):
            href = node['refuri']
        elif node.has_key('refid'):
            href = '#' + node['refid']
        elif node.has_key('refname'):
            href = '#' + self.document.nameids[node['refname']]
        ##self.body.append('[visit_reference]')
        self.body.append('\\href{%s}{' % href)

    def depart_reference(self, node):
        self.body.append('}')
        ##self.body.append('[depart_reference]')

    def visit_revision(self, node):
        self.visit_docinfo_item(node, 'revision')

    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')

    def visit_section(self, node):
        self.section_level += 1

    def depart_section(self, node):
        self.section_level -= 1

    def visit_status(self, node):
        self.visit_docinfo_item(node, 'status')

    def depart_status(self, node):
        self.depart_docinfo_item(node)

    def visit_strong(self, node):
        self.body.append('\\textbf{')

    def depart_strong(self, node):
        self.body.append('}')

    def visit_substitution_definition(self, node):
        raise nodes.SkipNode

    def visit_substitution_reference(self, node):
        self.unimplemented_visit(node)

    def visit_subtitle(self, node):
        self.title = self.title + \
                '\\\\\n\\large{%s}\n' % self.encode(node.astext()) 
        raise nodes.SkipNode

    def depart_subtitle(self, node):
        pass

    def visit_system_message(self, node):
        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.

        The width is scaled down by 93%. We do it here
        because the we can use linewidth which should be the local
        width.
        """
        width = 0
        for node in self.colspecs:
            width += node['colwidth']
        s = ""
        for node in self.colspecs:
            colwidth = 0.93 * float(node['colwidth']) / width 
            s += "|p{%.2f\\linewidth}" % colwidth
        self.colspecs = []
        return s+"|"

    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

    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())
        else:
            if self.context[-1] != 'table_sentinel':
                self.body.append('{%s}' % ('|X' * self.context.pop() + '|'))
                self.body.append('\n\\hline')

    def visit_target(self, node):
        if not (node.has_key('refuri') or node.has_key('refid')
                or node.has_key('refname')):
            self.body.append('\\hypertarget{%s}{' % node['name'])
            self.context.append('}')
        else:
            self.context.append('')

    def depart_target(self, node):
        self.body.append(self.context.pop())

    def visit_tbody(self, node):
        # BUG write preamble if not yet done (colspecs not [])
        # for tables without heads.
        if len(self.colspecs) > 0:
            self.visit_thead(None)
            self.depart_thead(None)
        self.body.append('%[visit_tbody]\n')

    def depart_tbody(self, node):
        self.body.append('%[depart_tbody]\n')

    def visit_term(self, node):
        self.body.append('\\item[')

    def depart_term(self, node):
        # definition list term.
        self.body.append(':]\n')

    def visit_tgroup(self, node):
        #self.body.append(self.starttag(node, 'colgroup'))
        #self.context.append('</colgroup>\n')
        pass

    def depart_tgroup(self, node):
        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')

    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')
            

    def visit_tip(self, node):
        self.visit_admonition(node, 'tip')

    def depart_tip(self, node):
        self.depart_admonition()

    def visit_title(self, node):
        """Only 3 section levels are supported by LaTeX article (AFAIR)."""
        if node.astext().lower().strip() == 'abstract':
            self.body.append('\\abstract')
            raise nodes.SkipNode
        
        if isinstance(node.parent, nodes.topic):
            # section titles before the table of contents.
            if node.parent.hasattr('id'):
                self.body.append('\\hypertarget{%s}{}' % node.parent['id'])
            self.body.append('\\begin{center}\n')
            self.context.append('\\end{center}\n')
            ## should this be section subsection or 
            self.body.append('\\subsection*{')
            self.context.append('}\n')
        elif self.section_level == 0:
            # document title
            self.title = self.encode(node.astext())
            if not self.pdfinfo == None:
                self.pdfinfo.append( 'pdftitle={%s}' % self.encode(node.astext()) )
            raise nodes.SkipNode
        else:
            self.body.append('\n\n')
            self.body.append('%' + '_' * 75)
            self.body.append('\n\n')
            if node.parent.hasattr('id'):
                self.body.append('\\hypertarget{%s}{}\n' % node.parent['id'])
            # section_level 0 is title and handled above.    
            # BUG: latex has no deeper sections (actually paragrah is no section either).
#            if self.use_latex_toc:
            section_star = ""
#            else:
#                 section_star = "*"
            secl = self.section_level
            if self.settings.documentclass in ['book', 'report']: secl = secl-1

            if secl == 0:
                self.body.append('\\chapter%s{' % (section_star))
            elif (secl<=3):  # 1,2,3
                self.body.append('\\%ssection%s{' % ('sub'*(secl-1),section_star))
            elif (secl==4):      
                #self.body.append('\\paragraph*{')
                self.body.append('\\subsubsection%s{' % (section_star))
            else:
                #self.body.append('\\subparagraph*{')
                self.body.append('\\subsubsection%s{' % (section_star))
            # BUG: self.body.append( '\\label{%s}\n' % name)
            self.context.append('}\n')

    def depart_title(self, node):
        self.body.append(self.context.pop())
        if isinstance(node.parent, nodes.topic):
            self.body.append(self.context.pop())
        # BUG level depends on style.
        if node.parent.hasattr('id') and not self.use_latex_toc:
            # pdflatex allows level 0 to 3
            # ToC would be the only on level 0 so i choose to decrement the rest.
            # "Table of contents" bookmark to see the ToC. To avoid this
            # we set all zeroes to one.
            l = self.section_level
            if l>0:
                l = l-1
            self.body.append('\\pdfbookmark[%d]{%s}{%s}\n' % \
                (l,node.astext(),node.parent['id']))

    def visit_topic(self, node):
        self.topic_class = node.get('class')
        if self.use_latex_toc:
            self.topic_class = ''
            raise nodes.SkipNode

    def depart_topic(self, node):
        self.topic_class = ''
        self.body.append('\n')

    def visit_transition(self, node):
        self.body.append('\n\n')
        self.body.append('%' + '_' * 75)
        self.body.append('\n\\hspace*{\\fill}\\hrulefill\\hspace*{\\fill}')
        self.body.append('\n\n')

    def depart_transition(self, node):
        #self.body.append('[depart_transition]')
        pass

    def visit_version(self, node):
        self.visit_docinfo_item(node, 'version')

    def depart_version(self, node):
        self.depart_docinfo_item(node)

    def visit_warning(self, node):
        self.visit_admonition(node, 'warning')

    def depart_warning(self, node):
        self.depart_admonition()

    def unimplemented_visit(self, node):
        raise NotImplementedError('visiting unimplemented node type: %s'
                                  % node.__class__.__name__)

#    def unknown_visit(self, node):
#    def default_visit(self, node):
    
# vim: set ts=4 et ai :




See more files for this project here

gzz

An implementation of Ted Nelson's ZZstructure. ZZstructure is a new type of programming platform for structured data.

Project homepage: http://savannah.nongnu.org/projects/gzz
Programming language(s): C++,Java,Python
License: lgpl21

  gzz/
    util/
      GrepDoclet.java
      JythonDoclet.java
  copyrighter.py
  latex.py
  latex2e.py
  mputils.py
  pegboard.py
  uml.py
  umldoc.py
  umlhelper.mp
  umlpegboard.py
  umlrst.py
  umltool.py