# -*- coding: iso-8859-15 -*-


# Glasnost
# By: Odile Bénassy <obenassy@entrouvert.com>
#     Romain Chantereau <rchantereau@entrouvert.com>
#     Nicolas Clapiès <nclapies@easter-eggs.org>
#     Pierre-Antoine Dejace <padejace@entrouvert.be>
#     Thierry Dulieu <tdulieu@easter-eggs.com>
#     Florent Monnier <monnier@codelutin.com>
#     Cédric Musso <cmusso@easter-eggs.org>
#     Frédéric Péters <fpeters@entrouvert.be>
#     Benjamin Poussin <poussin@codelutin.com>
#     Emmanuel Raviart <eraviart@entrouvert.com>
#     Sébastien Régnier <regnier@codelutin.com>
#     Emmanuel Saracco <esaracco@easter-eggs.com>
#
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
#     Emmanuel Saracco & Théridion
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
#     Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
#     Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
#     Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
#     Régnier, Emmanuel Saracco, Théridion & Vecam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

from __future__ import nested_scopes

__doc__ = """Glasnost Common Parsers"""

__version__ = '$Revision$'[11:-2]


import os
import re
from stat import *
import tools
import sys
import types

try:
    import docutils
except ImportError:
    docutils = None

import context
from XhtmlGenerator import enhanceTypo


def parseGlasnostLink(link):
    """Parse a link to a Glasnost object as happening in Spip or ReST syntax
       and returns a (title, url) tuple.
       
       Note that those can contains replaceable items (with
       replaceSpecialTags).
    """
    link = link.replace('\n', ' ')
    matchObject = re.match(
            r'(?P<role>alias|art(icle)?|atom|book|card|election|file|'\
             'grade|group|heading|im(g|age)?|person(ne)?|rubri(c|que)|'\
             'short|system|vote|%s) *(?P<localId>\S+) *(?P<option>\S+)?' % \
             '|'.join(context.getVar('knownRoles') or ['zzz']), link)

    if matchObject is None:
        name = link
        for prefix in [ 'http://', 'https://', 'ftp://', 'mailto:' ]:
            if name.startswith(prefix):
                if not '/' in name[len(prefix):]:
                    name = name[len(prefix):]
                break
        else:
            name = ''
        return name, link

    role = matchObject.group('role')
    dispatcherHost = context.getVar('dispatcherId')[11:]
    localId = matchObject.group('localId')
    option = matchObject.group('option') and \
            ':%s' % matchObject.group('option') or ''

    if role == 'alias':
        title = '[{glasnost:aliaslabel:%s:%s}]' % (dispatcherHost, localId)
        url = '[{glasnost:alias:%s:%s%s}]' % (dispatcherHost, localId, option)
        return title, url

    serverRoles = { # backward compatibility
        'art': 'articles',
        'article': 'articles',
        'election': 'elections',
        'file': 'uploadfiles',
        'group': 'groups',
        'img': 'uploadfiles',
        'image': 'uploadfiles',
        'person': 'people',
        'rubric': 'rubrics',
    }
    if serverRoles.has_key(role):
        serverRole = serverRoles[role]
    elif role in (context.getVar('knownRoles') or []):
        serverRole = role
    else:
        return '', link

    title = '[{glasnost:label:%s:%s:%s}]' % (
            dispatcherHost, serverRole, localId)
    url = '[{glasnost:partialid:%s:%s:%s%s}]' % (
            dispatcherHost, serverRole, localId, option)
    return title, url


class Formatter:
    def close(self, **keywords):
        return ''

    def list(self, list):
        def getListIndent(stack):
            if len(stack) == 0:
                return -1
            else:
                return stack[-1][0]
        stack = []
        result = ''
        for line in list:
            indent, type, text = line
            while indent < getListIndent(stack):
                result += self.listEnd(len(stack), stack[-1][1])
                del stack[-1]
            if indent > getListIndent(stack):
                stack.append((indent, type))
                result += self.listBegin(len(stack) - 1, type)
            result += self.listItem(len(stack), text)
        while stack:
            result += self.listEnd(len(stack) - 1, stack[-1][1])
            del stack[-1]
        return result

    def open(self, **keywords):
        return ''

    def prescaleImage(self):
        return 1

    def table(self, table, hasHeader):
        result = ''
        rows = len(table)
        cols = max(map(len, table))
        result += self.tableBegin(rows, cols)
        for i in range(len(table)):
            row = table[i]
            rowNumber = i
            if hasHeader:
                rowNumber -= 1
            result += self.tableLineBegin(rowNumber,
                                          isHeader = i == 0 and hasHeader)
            j = 0
            for cell in row:
                if type(cell) in [types.StringType, types.UnicodeType] \
                       and cell.strip() == '###':
                    cell = rowNumber + 1
                result += self.tableCell(cell, rowNumber, j,
                                         isHeader = i == 0 and hasHeader)
                j += 1
            result += self.tableLineEnd(i, isHeader = i == 0 and hasHeader)
        result += self.tableEnd(isHeader = len(table) == 1 and hasHeader)
        return result

    def text(self, text):
        return text


class FormatterDocBook(Formatter):
    def close(self, docType = 'article', **keywords):
        if docType == 'article':
            return """\
</article>
"""
        elif docType == 'book':
            return """\
</book>
"""
        elif docType == 'chapter':
            return """\
</chapter>
"""

    def emphasis(self, text):
        if not text:
            return ''
        return '<emphasis>%(text)s</emphasis>' % {
            'text': text,
            }

    def footNoteCall(self, text):
        if not text:
            return ''
        return '<footnote>%(text)s</footnote>' % {
            'text': text,
            }

    def image(self, name, url, width = None, height = None, fullUrl = None):
        if width:
            width = ' width="%s"' % width
        else:
            width = ''
        if height:
            height = ' depth="%s"' % height
        else:
            height = ''
        if width or height:
            scaleFit = ' scalefit="1"'
        else:
            scaleFit = ''
        url = self.text(url)
        image = """\
<inlinemediaobject>
  <imageobject>
    <imagedata fileref="%(url)s"%(height)s%(width)s%(scaleFit)s/>
  </imageobject>
  <textobject>
    <phrase>%(name)s</phrase>
  </textobject>
</inlinemediaobject>\
""" % {
            'height': height,
            'name': name,
            'scaleFit': scaleFit,
            'url': url,
            'width': width,
            }
        if fullUrl:
            image = self.link(image, fullUrl)
        return image

    def intertitle(self, text, sublevel = 0):
        return '<bridgehead>%(text)s</bridgehead>\n' % {
            'text': text,
            }

    def lineBreak(self):
        return '<simpara/>\n'

    def lineSeparator(self):
        # FIXME: The following instruction doesn't work with the current
        # stylesheet.
        # return '<beginpage/>\n'
        return '<simpara/>\n'
    
    def link(self, name, url, title = None):
        url = self.text(url)
        return '<ulink url="%(url)s">%(name)s</ulink>' % {
            'name': name,
            'url': url,
            }

    def listBegin(self, indent, type):
        if type:
            type = 'orderedlist'
        else:
            type = 'itemizedlist'
        if indent == 0:
            return '%(indent)s<%(type)s>\n' % {
                'indent': '  ' * indent,
                'type': type,
                }
        else:
            return '%(indent)s<listitem><%(type)s>\n' % {
                'indent': '  ' * indent,
                'type': type,
                }

    def listEnd(self, indent, type):
        if type:
            type = 'orderedlist'
        else:
            type = 'itemizedlist'
        if indent == 0:
            return '%(indent)s</%(type)s>\n' % {
                'indent': '  ' * indent,
                'type': type,
                }
        else:
            return '%(indent)s</%(type)s></listitem>\n' % {
                'indent': '  ' * indent,
                'type': type,
                }

    def listItem(self, indent, item):
        return '%(indent)s<listitem><para>%(item)s</para></listitem>\n' % {
            'indent': '  ' * indent,
            'item': item,
            }

    def nonBreakingSpace(self):
        return '&nbsp;'
        
    def open(self, docType = 'article', addHeader = 1, language = 'fr',
             publicationDate = None, title = None, **keywords):
        if publicationDate:
            publicationDate = """\
  <pubdate>%s</pubdate>
""" % publicationDate
        else:
            publicationDate = ''
        if title:
            title = """\
<title>%s</title>
""" % title
        else:
            title = ''
        if docType == 'article':
            if addHeader:
                text = """\
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE article PUBLIC "-//Norman Walsh//DTD DocBk XML V4.1.2/EN"
     "file:///usr/share/sgml/docbook/dtd/xml/4.1.2/docbookx.dtd" [
]>
"""
            else:
                text = ''
            text += """\
<article lang="%(language)s">
%(title)s\
<articleinfo>
  <author>
    <firstname>Frederic</firstname>
    <surname>Peters</surname>
  </author>
  <editor>
    <firstname>Emmanuel</firstname>
    <surname>Raviart</surname>
  </editor>
</articleinfo>
%(publicationDate)s\
""" % {
                'language': language,
                'publicationDate': publicationDate,
                'title': title,
                }
            return text
        elif docType == 'book':
            if addHeader:
                text = """\
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE book PUBLIC "-//Norman Walsh//DTD DocBk XML V4.1.2/EN"
     "file:///usr/share/sgml/docbook/dtd/xml/4.1.2/docbookx.dtd" [
]>
"""
            else:
                text = ''
            text += """\
<book lang="%(language)s">
%(title)s\
<bookinfo>
  <author>
    <firstname>Frederic</firstname>
    <surname>Peters</surname>
  </author>
  <editor>
    <firstname>Emmanuel</firstname>
    <surname>Raviart</surname>
  </editor>
</bookinfo>
%(publicationDate)s\
""" % {
                'language': language,
                'publicationDate': publicationDate,
                'title': title,
                }
            return text
        elif docType == 'chapter':
            if addHeader:
                text = """\
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE chapter PUBLIC "-//Norman Walsh//DTD DocBk XML V4.1.2/EN"
     "file:///usr/share/sgml/docbook/dtd/xml/4.1.2/docbookx.dtd" [
]>
"""
            else:
                text = ''
            text += """\
<chapter>
%(title)s\
<chapterinfo>
  <author>
    <firstname>Frederic</firstname>
    <surname>Peters</surname>
  </author>
  <editor>
    <firstname>Emmanuel</firstname>
    <surname>Raviart</surname>
  </editor>
</chapterinfo>
%(publicationDate)s\
""" % {
                'publicationDate': publicationDate,
                'title': title,
                }
            return text

    def paragraph(self, text):
        text = text.strip()
        if not text:
            return self.lineBreak()
        return '<para>%(text)s</para>\n' % {
            'text': text,
            }

    def preformatted(self, text):
        if not text:
            return ''
        text = self.text(text)
        return '<literallayout class="monospaced">%(text)s</literallayout>' % {
            'text': text,
            }

    def preformattedInline(self, text):
        if not text:
            return ''
        text = self.text(text)
        return '<literal>%(text)s</literal>' % {
            'text': text,
            }

    def punctuationAndSpace(self, punctuation):
        return {
            '« ': '«&nbsp;',
            }[punctuation]

    def spaceAndPunctuation(self, punctuation):
        return {
            ' :': '&nbsp;:',
            ' ;': '&nbsp;;',
            ' !': '&nbsp;!',
            ' ?': '&nbsp;?',
            ' »': '&nbsp;»',
            ' %': '&nbsp;%',
            ' ...': '&nbsp;...',
            }[punctuation]

    def strong(self, text):
        if not text:
            return ''
        return '<emphasis role="strong">%(text)s</emphasis>' % {
            'text': text,
            }

    def tableBegin(self, rows, cols):
        return '<informaltable><tgroup cols="%(cols)s">\n' % {
            'cols': cols,
            }

    def tableCell(self, cell, row, col, isHeader):
        if not cell:
            cell = '&nbsp;'
        return '      <entry>%(cell)s</entry>\n' % {
            'cell': cell,
            }

    def tableEnd(self, isHeader = 0):
        if isHeader:
            return '  </thead>\n</tgroup></informaltable>\n'
        else:
            return '  </tbody>\n</tgroup></informaltable>\n'

    def tableLineBegin(self, tableLineCount, isHeader = 0):
        if isHeader:
            return '  <thead>\n    <row>\n'
        elif tableLineCount == 0:
            return '  <tbody>\n    <row>\n'
        else:
            return '    <row>\n'

    def tableLineEnd(self, tableLineCount, isHeader = 0):
        if isHeader:
            return '    </row>\n  </thead>\n  <tbody>'
        elif tableLineCount == 0:
            return '    </row>\n  </tbody>\n'
        else:
            return '    </row>\n'

    def teletype(self, text):
        if not text:
            return ''
        return '<literal>%(text)s</literal>' % {
            'text': text,
            }

    def text(self, text):
        if not text:
            return ''
        text = text.replace('&', '&amp;')
        text = text.replace('<', '&lt;')
        return text


_non_id_chars = re.compile('[^a-z0-9]+')
_non_id_at_ends = re.compile('^[-0-9]+|-+$')

class FormatterHtml(Formatter):
    def __init__(self):
        self.states = [ '' ]
        
    def intertitle(self, text, sublevel = 0):
        nameText = re.sub('<.*?>', '', text).strip()
        # from docutils/nodes.py, def make_id(string)
        nameText = _non_id_chars.sub('-', ' '.join(nameText.lower().split()))
        nameText = _non_id_at_ends.sub('', nameText)
        
        return '<h%(sectionLevel)d id="%(nameText)s">%(text)s'\
               '</h%(sectionLevel)d>\n' % {
            'text': text,
            'sectionLevel': self.sectionLevel + sublevel,
            'nameText': nameText,
            }

    def lineBreak(self):
        return '<br />\n'

    def list(self, list):
        # FIXME: the Formatter.list method doesn't work for sub-lists, so it is
        # rewritten here. Maybe this method should also be used by
        # Formatter.list
        def getListIndent(stack):
            if len(stack) == 0:
                return -1
            else:
                return stack[-1][0]
        self.states.append('blockLevel')
        stack = []
        result = ''
        for line in list:
            indent, type, text = line
            while indent < getListIndent(stack):
                result += self.listItemEnd()
                result += self.listEnd(len(stack), stack[-1][1])
                del stack[-1]
            if indent > getListIndent(stack):
                stack.append((indent, type))
                result += '\n'
                result += self.listBegin(len(stack) - 1, type)
            else:
                result += self.listItemEnd()
            result += self.listItemBegin(len(stack), text)
        while stack:
            result += self.listItemEnd()
            result += self.listEnd(len(stack) - 1, stack[-1][1])
            del stack[-1]
        return result

    def listBegin(self, indent, type):
        if type:
            type = 'ol'
        else:
            type = 'ul'
        return '%(indent)s<%(type)s>\n' % {
            'indent': '  ' * indent,
            'type': type,
            }

    def listEnd(self, indent, type):
        if type:
            type = 'ol'
        else:
            type = 'ul'
        return '%(indent)s</%(type)s>\n' % {
            'indent': '  ' * indent,
            'type': type,
            }

    def listItemBegin(self, indent, item):
        return '%(indent)s<li>%(item)s' % {
            'indent': '  ' * indent,
            'item': item,
            }

    def listItemEnd(self):
        return '</li>\n'

    def nonBreakingSpace(self):
        return '&nbsp;'

    def open(self, **keywords):
        self.sectionLevel = 2
        if keywords.has_key('sectionLevel'):
            self.sectionLevel = int(keywords['sectionLevel'])
        return ''

    def paragraph(self, text):
        if self.states[-1] == 'blockLevel':
            self.states.pop()
            return text


        def line80(text):
            t = []
            i = 0
            while 1:
                val = text[i+70:].find(' ')
                if val == -1:
                    t.append(text[i:])
                    break
                part = text[i:i+70+val+1]
                t.append(part)
                i += len(part)
            return '\n'.join(t)

        text = enhanceTypo(text.strip())
        text = line80(text.replace('\n', ' '))
        if not text:
            return self.lineBreak()
        return '<p>%(text)s</p>\n' % {
            'text': text,
            }

    def preformatted(self, text):
        if not text:
            return ''
##         text = self.text(text)
        self.states.append('blockLevel')
        return '<pre>%(text)s</pre>' % {
            'text': text,
            }

    def preformattedInline(self, text):
        if not text:
            return ''
        text = self.text(text)
        return '<code>%(text)s</code>' % {
            'text': text,
            }

    def punctuationAndSpace(self, punctuation):
        return {
            '« ': '«&nbsp;',
            }[punctuation]

    def spaceAndPunctuation(self, punctuation):
        return {
            ' :': '&nbsp;:',
            ' ;': '&nbsp;;',
            ' !': '&nbsp;!',
            ' ?': '&nbsp;?',
            ' »': '&nbsp;»',
            ' %': '&nbsp;%',
            ' ...': '&nbsp;...',
            }[punctuation]

    def tableBegin(self, rows, cols):
        self.states.append('blockLevel')
        return '<table>\n'

    def tableCell(self, cell, row, col, isHeader):
        if not cell:
            cell = '&nbsp;'
        if isHeader:
            tag = 'th'
            attrtag = ' scope="col"'
        else:
            tag = 'td'
            attrtag = ''
        return '    <%(tag)s%(attrtag)s>%(cell)s</%(tag)s>\n' % {
            'tag': tag,
            'cell': cell.strip(),
            'attrtag': attrtag,
            }

    def tableEnd(self, isHeader = 0):
        return '</table>\n'

    def tableLineBegin(self, tableLineCount, isHeader = 0):
        if isHeader:
            return '  <tr>\n'
        elif (tableLineCount + 1) % 2 == 0:
            return '  <tr class="even">\n'
        else:
            return '  <tr class="odd">\n'

    def tableLineEnd(self, tableLineCount, isHeader = 0):
        return '  </tr>\n'

    def text(self, text):
        if not text:
            return ''
        text = text.replace('&', '&amp;')
        text = text.replace('<', '&lt;')
        text = text.replace('\x85', '&#8230;')  # ellipsis (...)
        return text


class FormatterHtmlComplete(FormatterHtml):
    _footNotes = None

    def close(self, **keywords):
        footNotes = self._footNotes
        if footNotes is None or len(footNotes) == 0:
            return ''
        indexes = footNotes.keys()
        indexes.sort()
        result = '<div class="spip-notes">\n'
        for index in indexes:
            result += '<p>'
            result += '[<a href="#nh%(index)s" name="nb%(index)s" '\
                      'class="spip-note">%(index)s</a>]&nbsp;%(text)s' % {
                'index': index,
                'text': footNotes[index],
                }
            result += '</p>\n'
        result += '</div>\n'
        return result

    def emphasis(self, text):
        if not text:
            return ''
        return '<em>%(text)s</em>' % {
            'text': text,
            }

    def footNoteCall(self, footNote):
        if self._footNotes is None:
            self._footNotes = {}
        index = len(self._footNotes) + 1
        self._footNotes[index] = footNote
        return '&nbsp;[<a href="#nb%(index)s" name="nh%(index)s" '\
                'class="spip-note">%(index)s</a>]' % { 'index': index }

    def image(self, name, url, width = None, height = None, fullUrl = None):
        if width:
            width = ' width="%s"' % width
        else:
            width = ''
        if height:
            height = ' height="%s"' % height
        else:
            height = ''
        image = """\
<img src="%(url)s" alt="%(name)s" title="%(name)s"%(height)s%(width)s />\
""" % {
            'height': height,
            'name': name,
            'url': url,
            'width': width,
            }
        if fullUrl:
            image = '<a href="%(fullUrl)s">%(image)s</a>' % {
                'fullUrl': fullUrl,
                'image': image,
                }
        return image

    def lineSeparator(self):
        return '<hr />\n'

    def link(self, name, url, title=None):
        url = url.replace('"', '&quot;') # '"' must be converted.
        # The following replace should not be done. An url must be left as is.
        # url = url.replace('&', '&amp;') # 
        # url = url.replace('<', '&lt;') # ?
        # url = url.replace('>', '&gt;') # ?
        attrs = ['href="%s"' % url]
        if title:
            attrs.append('title="%s"' % title)
        if url.startswith('http://'):
            attrs.append('class="external"')
        attrs = ' '.join(attrs)
        return '<a %s>%s</a>' % (attrs, name)

    def strong(self, text):
        if not text:
            return ''
        return '<strong>%(text)s</strong>' % {
            'text': text,
            }

    def teletype(self, text):
        if not text:
            return ''
        return '<tt>%(text)s</tt>' % {
            'text': text,
            }


class FormatterHtmlSimple(FormatterHtml):
    def emphasis(self, text):
        return text

    def footNoteCall(self, footNote):
        return ''

    def image(self, name, url, width = None, height = None, fullUrl = None):
        return ''

    def lineSeparator(self):
        return '<br />\n'

    def link(self, name, url, title=None):
        return str(name)
    
    def strong(self, text):
        return text

    def teletype(self, text):
        return text


class FormatterLatex(Formatter):
    sectionType = 'section'
    def close(self, docType = 'article', **keywords):
        if docType in ['article', 'book', 'report']:
            return """\
\\end{document}
"""
        else:
            return ''

    def emphasis(self, text):
        if not text:
            return ''
        return '\\begin{it}%(text)s\\end{it}' % {
            'text': text,
            }

    def footNoteCall(self, text):
        if not text:
            return ''
        return '\\footnote{%(text)s}' % {
            'text': text,
            }

    def image(self, name, url, width = None, height = None, fullUrl = None):
        if width is None:
            width = ''
        if height is None:
            height = ''
        if fullUrl is not None:
            url = fullUrl
        return '[{glasnost:latex:includegraphics:%s:width=%s:height=%s}]' \
                      % (url, width, height)

    def intertitle(self, text, sublevel = 0):
        return '\\%(sectionType)s{%(text)s}\n' % {
            'sectionType': self.sectionType,
            'text': text,
            }

    def lineBreak(self):
        return ''
        return '\\linebreak\n'

    def lineSeparator(self):
        return '\\hrulefill\n'

    def link(self, name, url, title=None):
        url = self.text(url)
        # FIXME: link support in LaTeX conversion.
        return name

    def listBegin(self, indent, type):
        if type:
            type = 'enumerate'
        else:
            type = 'itemize'
        return '%(indent)s\\begin{%(type)s}\n' % {
            'indent': '  ' * indent,
            'type': type,
            }

    def listEnd(self, indent, type):
        if type:
            type = 'enumerate'
        else:
            type = 'itemize'
        return '%(indent)s\\end{%(type)s}\n' % {
            'indent': '  ' * indent,
            'type': type,
            }

    def listItem(self, indent, item):
        return '%(indent)s\\item %(item)s\n' % {
            'indent': '  ' * indent,
            'item': item,
            }

    def nonBreakingSpace(self):
        return '~'

    def open(self, docType = 'article', addHeader = 1, author = '',
             language = 'fr', publicationDate = None, title = None,
             **keywords):
        if docType == 'subArticle':
            self.sectionType = 'subsection'
            return ''
        if docType in ['article', 'book', 'report']:
            if author:
                author = """\
\\author{%s}
""" % author
            else:
                author = ''
            languages = {
                'de': 'german',
                'en': 'english',
                'fi': 'finnish',
                'fr': 'french',
                'sv': 'swedish',
                }
            if language and language in languages.keys():
                language = languages[language]
            else:
                language = 'english'
            if publicationDate:
                publicationDate = """\
\\date{%s}
""" % publicationDate
            else:
                publicationDate = ''
            if title:
                title = """\
\\title{%s}
""" % title
            else:
                title = ''
            text = """\
\\documentclass{%(docType)s}
\\usepackage[%(language)s]{babel}
\\usepackage[pdftex]{graphicx}
\\usepackage[latin9]{inputenc}
\\usepackage{listings}
\lstset{breaklines,breakindent=0pt,breakautoindent=false}
\\begin{document}
%(title)s\
%(author)s\
%(publicationDate)s\
\\maketitle
\\tableofcontents
""" % {
                'author': author,
                'docType': docType,
                'language': language,
                'publicationDate': publicationDate,
                'title': title,
                }
            return text
        elif docType == 'chapter':
            text = """\
\\chapter{%s}
""" % title
            return text

    def paragraph(self, text):
        text = text.strip()
        if not text:
            return self.lineBreak()
        return '%(text)s\n\n' % {
            'text': text,
            }

    def preformatted(self, text):
        if not text:
            return ''
##         return '\\begin{verbatim}%(text)s\\end{verbatim}' % {
##             'text': text,
##             }
        return '\\begin{lstlisting}{}%(text)s\\end{lstlisting}' % {
            'text': text,
            }

    def preformattedInline(self, text):
        if not text:
            return ''
##         return '\\begin{verbatim}%(text)s\\end{verbatim}' % {
##             'text': text,
##             }
        return '\\begin{tt}\\begin{lstlisting}{}%(text)s\\end{lstlisting}\\end{tt}' % {
            'text': text,
            }

    def prescaleImage(self):
        return 0

    def punctuationAndSpace(self, punctuation):
        return self.text(punctuation)

    def spaceAndPunctuation(self, punctuation):
        return self.text(punctuation)

    def strong(self, text):
        if not text:
            return ''
        return '\\begin{bf}%(text)s\\end{bf}' % {
            'text': text,
            }

    def tableBegin(self, rows, cols):
        spec = '|l' * cols + '|'
        return '\n\\begin{tabular}{%(spec)s}\n\\hline\n' % {
            'spec': spec,
            }

    def tableCell(self, cell, row, col, isHeader):
        if col > 0:
            separator = ' & '
        else:
            separator = ''
        return '%(separator)s%(cell)s' % {
            'cell': cell,
            'separator': separator,
            }

    def tableEnd(self, isHeader = 0):
        return '\\end{tabular}\n\n'

    def tableLineBegin(self, tableLineCount, isHeader = 0):
        return ''

    def tableLineEnd(self, tableLineCount, isHeader = 0):
        return ' \\\\\n\\hline\n'

    def teletype(self, text):
        if not text:
            return ''
        if not '\n' in text:
            return '\\texttt{%s}' % text
        return '\\begin{ttfamily}%(text)s\\end{ttfamily}' % {
            'text': text,
            }

    def text(self, text):
        if not text:
            return ''
        # The 3 following instructions must be the first ones.
        text = text.replace('\\',
                            '[{glasnost:dollar}]\\backslash[{glasnost:dollar}]')
        text = text.replace('$', '\\$')
        text = text.replace('[{glasnost:dollar}]', '$')
        text = text.replace('{', '\\{')
        text = text.replace('}', '\\}')

        text = text.replace('#', '\\#')
        text = text.replace('%', '\\%')
        text = text.replace('&', '\\&')
        text = text.replace('~', '\\begin{verbatim}~\\end{verbatim}')
        text = text.replace('_', '\\_')
        text = text.replace('^', '\\begin{verbatim}^\\end{verbatim}')
        return text


class SpipParser:
    codeRegexp = r"""
(?P<codeStop>\</(code|CODE)\>)
|(?P<wordAsIs>\w+)
"""
    codeRegexp = codeRegexp.replace('\n', '')
    codeRegexpObject = None
    footNoteRegexp = r"""
(?P<footNoteStop>\]\])
|(?P<image>\<((im(g|age)?)|(IM(G|AGE)?)) *(\d+|[^ \>]+)( +\d+\*\d+)?\>)
|(?P<teletypeStart>\(\()
|(?P<teletypeStop>\)\))
|(?P<strongStart>\{\{)
|(?P<strongStop>\}\})
|(?P<emphasisStart>\{)
|(?P<emphasisStop>\})
|(?P<linkStart>\[)
|(?P<antislash>\\.?)
|(?P<punctuationAndSpace>[«] )
|(?P<spaceAndPunctuation> ([:;!\?»%]|\.\.\.))
|(?P<nonBreakingSpace>~)
|(?P<word>\w+)
"""
    footNoteRegexp = footNoteRegexp.replace('\n', '')
    footNoteRegexpObject = None
    linkNameRegexp = r"""
(?P<image>\<((im(g|age)?)|(IM(G|AGE)?)) *(\d+|[^ \>]+)( +\d+\*\d+)?\>)
|(?P<teletypeStart>\(\()
|(?P<teletypeStop>\)\))
|(?P<strongStart>\{\{)
|(?P<strongStop>\}\})
|(?P<emphasisStart>\{)
|(?P<emphasisStop>\})
|(?P<linkMiddle>->)
|(?P<linkWithoutMiddleStop>\])
|(?P<antislash>\\.?)
|(?P<punctuationAndSpace>[«] )
|(?P<spaceAndPunctuation> ([:;!\?»%]|\.\.\.))
|(?P<nonBreakingSpace>~)
|(?P<word>\w+)
"""
    linkNameRegexp = linkNameRegexp.replace('\n', '')
    linkNameRegexpObject = None
    linkRegexp = r"""
(?P<linkStop>\])
|(?P<wordInUrl>\w+)
"""
    linkRegexp = linkRegexp.replace('\n', '')
    linkRegexpObject = None
    listRegexp = r"""
(?P<listLineStart>^ *-([0-9aAiI]\.)?(?=[^-]))
|(?P<listLineStop>\r?\n(?= *-([0-9aAiI]\.)?(?=[^-])))
|(?P<listLineContinuation>\r?\n(?= +))
|(?P<listStop>(?=\r?\n|$))
|(?P<image>\<((im(g|age)?)|(IM(G|AGE)?)) *(\d+|[^ \>]+)( +\d+\*\d+)?\>)
|(?P<teletypeStart>\(\()
|(?P<teletypeStop>\)\))
|(?P<strongStart>\{\{)
|(?P<strongStop>\}\})
|(?P<emphasisStart>\{)
|(?P<emphasisStop>\})
|(?P<footNoteStart>\[\[)
|(?P<linkStart>\[)
|(?P<antislash>\\.?)
|(?P<punctuationAndSpace>[«] )
|(?P<spaceAndPunctuation> ([:;!\?»%]|\.\.\.))
|(?P<nonBreakingSpace>~)
|(?P<word>\w+)
"""
    listRegexp = listRegexp.replace('\n', '')
    listRegexpObject = None
    paragraphRegexp = r"""
(?P<paragraphStop>\r?\n( *\r?\n)* *(?=\r?\n|$))
|(?P<lineSeparator>^ *[-_]{4,} *(\r?\n|$))
|(?P<intertitleStart>^ *\{\{\{)
|(?P<intertitleStop>\}\}\}( *$|))
|(?P<listStart>^ *-([0-9aAiI]\.)?)
|(?P<tableWithHeaderStart>^ *\|(?=( *\{\{.*?\}\} *\|)+ *(\r?\n|$)))
|(?P<tableStart>^ *\|)
|(?P<codeStart>\<(code|CODE)\>)
|(?P<image>\<((im(g|age)?)|(IM(G|AGE)?)) *(\d+|[^ \>]+)( +\d+\*\d+)?\>)
|(?P<teletypeStart>\(\()
|(?P<teletypeStop>\)\))
|(?P<strongStart>\{\{)
|(?P<strongStop>\}\})
|(?P<emphasisStart>\{)
|(?P<emphasisStop>\})
|(?P<footNoteStart>\[\[)
|(?P<linkStart>\[)
|(?P<antislash>\\.?)
|(?P<punctuationAndSpace>[«] )
|(?P<spaceAndPunctuation> ([:;!\?»%]|\.\.\.))
|(?P<nonBreakingSpace>~)
|(?P<word>\w+)
"""
    paragraphRegexp = paragraphRegexp.replace('\n', '')
    paragraphRegexpObject = None
    posDelta = 0
    spipRegexp = r"""
(?P<paragraphStart>\r?\n)
"""
    spipRegexp = spipRegexp.replace('\n', '')
    spipRegexpObject = None
    states = None
    tableRegexp = r"""
(?P<rowStart>^ *\|)
|(?P<rowStop>\| *\r?\n(?= *\|))
|(?P<tableStop>\| *(?=\r?\n|$))
|(?P<cellRestart>\|)
|(?P<image>\<((im(g|age)?)|(IM(G|AGE)?)) *(\d+|[^ \>]+)( +\d+\*\d+)?\>)
|(?P<teletypeStart>\(\()
|(?P<teletypeStop>\)\))
|(?P<strongStart>\{\{)
|(?P<strongStop>\}\})
|(?P<emphasisStart>\{)
|(?P<emphasisStop>\})
|(?P<footNoteStart>\[\[)
|(?P<linkStart>\[)
|(?P<antislash>\\.?)
|(?P<punctuationAndSpace>[«] )
|(?P<spaceAndPunctuation> ([:;!\?»%]|\.\.\.))
|(?P<nonBreakingSpace>~)
|(?P<word>\w+)
"""
    tableRegexp = tableRegexp.replace('\n', '')
    tableRegexpObject = None

    def __init__(self):
        self.codeRegexpObject = re.compile(self.codeRegexp,
                                           re.MULTILINE | re.DOTALL)
        self.footNoteRegexpObject = re.compile(self.footNoteRegexp,
                                               re.MULTILINE | re.DOTALL)
        self.linkNameRegexpObject = re.compile(self.linkNameRegexp,
                                               re.MULTILINE | re.DOTALL)
        self.linkRegexpObject = re.compile(self.linkRegexp,
                                           re.MULTILINE | re.DOTALL)
        self.listRegexpObject = re.compile(self.listRegexp,
                                           re.MULTILINE | re.DOTALL)
        self.paragraphRegexpObject = re.compile(self.paragraphRegexp,
                                                re.MULTILINE | re.DOTALL)
        self.spipRegexpObject = re.compile(self.spipRegexp,
                                           re.MULTILINE | re.DOTALL)
        self.tableRegexpObject = re.compile(self.tableRegexp,
                                            re.MULTILINE | re.DOTALL)
        self.states = []
        self.pushState({
            'state': None,
            })
        self.pushState({
            'formattedText': '',
            'paragraphsCount': 0,
            'regexpObject': self.spipRegexpObject,
            'state': 'spip',
            })
        self.pushState({
            'formattedText': '',
            'ignoreEmptyParagraph': 0,
            'regexpObject': self.paragraphRegexpObject,
            'state': 'paragraph',
            })
        
    def format(self, formatter, rawText, **keywords):
        self.formatter = formatter
        formattedText = self.formatter.open(**keywords)
        if not rawText:
            rawText = ''
        rawText = rawText.expandtabs()
        pos = 0
        while pos < len(rawText):
            regexpObject = self.getVar('regexpObject')
            matchObject = regexpObject.match(rawText, pos)
            if matchObject is None or matchObject.lastgroup is None:
                self.setVar(
                    'formattedText',
                    self.getVar('formattedText') \
                    + self.formatter.text(rawText[pos]))
                pos += 1
            else:
                groupName = matchObject.lastgroup
                result = apply(getattr(self, 'handle_' + groupName),
                               (matchObject.group(groupName), ))
                if result:
                    self.setVar('formattedText',
                                self.getVar('formattedText') + result)
                pos = matchObject.end(groupName) + self.posDelta
                if self.posDelta != 0:
                    del self.posDelta
        while len(self.states) > 2:
            if self.getVar('state') == 'list':
                result = self.handle_listStop(None)
                if result:
                    self.setVar('formattedText',
                                self.getVar('formattedText') + result)
            elif self.getVar('state') == 'paragraph':
                result = self.handle_paragraphStop(None, isLastParagraph = 1)
                if result:
                    self.setVar('formattedText',
                                self.getVar('formattedText') + result)
            else:
                self.pullState()
        formattedText += self.getVar('formattedText')
        formattedText = formattedText.strip() + '\n'
        formattedText += self.formatter.close(**keywords)
        return formattedText
                
    def getVar(self, name):
        for state in self.states:
            if state.has_key(name):
                return state[name]
        else:
            raise KeyError(name)
                
    def getPreviousVar(self, name):
        for state in self.states[1:]:
            if state.has_key(name):
                return state[name]
        else:
            raise KeyError(name)

    def handle_antislash(self, group):
        character = group[1]
        if character == 'n':
            return self.formatter.lineBreak()
        else:
            return self.formatter.text(character)

    def handle_cellRestart(self, group):
        self.handle_cellStop(group)
        self.handle_cellStart(group)
        return None
        
    def handle_cellStart(self, group):
        table = self.getVar('table')
        table[-1].append([])
        return None

    def handle_cellStop(self, group):
        table = self.getVar('table')
        table[-1][-1] = self.getVar('formattedText')
        self.setVar('formattedText', '')
        return None

    def handle_codeStart(self, group):
        self.pushState({
            'formattedText': '',
            'regexpObject': self.codeRegexpObject,
            'state': 'code',
            })
        return None

    def handle_codeStop(self, group):
        if self.getVar('state') != 'code':
            return None
        formattedText = self.getVar('formattedText')
        self.pullState()
        return self.formatter.preformatted(formattedText)

    def handle_emphasisStart(self, group):
        self.pushState({
            'formattedText': '',
            'state': 'emphasis',
            })
        return None

    def handle_emphasisStop(self, group):
        if self.getVar('state') != 'emphasis':
            return None
        formattedText = self.getVar('formattedText')
        self.pullState()
        return self.formatter.emphasis(formattedText)

    def handle_footNoteStart(self, group):
        self.pushState({
            'formattedText': '',
            'regexpObject': self.footNoteRegexpObject,
            'state': 'footNote',
            })
        return None

    def handle_footNoteStop(self, group):
        if self.getVar('state') != 'footNote':
            return None
        formattedText = self.getVar('formattedText')
        self.pullState()
        return self.formatter.footNoteCall(formattedText)

    def handle_image(self, group):
        matchObject = re.match(
                r'\<((im(g|age)?)|(IM(G|AGE)?)) '\
                 '*((?P<localId>\d+)|(?P<url>[^ \>]+))'\
                 '( +(?P<width>\d+)\*(?P<height>\d+))?\>', group)
        localId = matchObject.group('localId')
        url = matchObject.group('url')
        width = matchObject.group('width')
        height = matchObject.group('height')
        if localId:
            dispatcherHost = context.getVar('dispatcherId')[11:]
            serverRole = 'uploadfiles'
            id = '[{glasnost:partialid:%s:%s:%s}]' % (
                    dispatcherHost, serverRole, localId)
            name = '[{glasnost:label:%s:%s:%s}]' % (
                    dispatcherHost, serverRole, localId)
            if (width or height) and self.formatter.prescaleImage():
                url = '[{glasnost:thumbnail:%s:%s:%s:width=%s:height=%s}]' % (
                        dispatcherHost, serverRole, localId, width, height)
                fullUrl = '[{glasnost:image:%s:%s:%s}]' % (
                        dispatcherHost, serverRole, localId)
                # Disable image scaling from formatter.
                width = height = None
            else:
                url = '[{glasnost:image:%s:%s:%s}]' % (
                        dispatcherHost, serverRole, localId)
                fullUrl = None
        else:
            name = ''
            fullUrl = None
        return self.formatter.image(name, url, width, height, fullUrl)
        
    def handle_intertitleStart(self, group):
        inParagraph = self.getVar('state') == 'paragraph'
        if inParagraph:
            result = self.handle_paragraphStop(None, ignoreEmptyParagraph = 1)
            if result:
                self.setVar('formattedText',
                            self.getVar('formattedText') + result)
        self.pushState({
            'formattedText': '',
            'inParagraph': inParagraph,
            'regexpObject': self.paragraphRegexpObject,
            'state': 'intertitle',
            })
        return None

    def handle_intertitleStop(self, group):
        if self.getVar('state') == 'intertitle':
            formattedText = self.getVar('formattedText')
            inParagraph = self.getVar('inParagraph')
            self.pullState()
            formattedText = self.formatter.intertitle(formattedText)
            self.setVar('formattedText',
                        self.getVar('formattedText') + formattedText)
            if inParagraph:
                return self.handle_paragraphStart(None,
                                                  ignoreEmptyParagraph = 1)
            else:
                return None
        elif self.getVar('state') == 'strong':
            self.posDelta -= 1
            return self.handle_strongStop(group[:-1])
        elif self.getVar('state') == 'emphasis':
            self.posDelta -= 2
            return self.handle_emphasisStop(group[:-2])
        else:
            return None

    def handle_lineSeparator(self, group):
        inParagraph = self.getVar('state') == 'paragraph'
        if inParagraph:
            result = self.handle_paragraphStop(None, ignoreEmptyParagraph = 1)
            if result:
                self.setVar('formattedText',
                            self.getVar('formattedText') + result)
        formattedText = self.formatter.lineSeparator()
        self.setVar('formattedText',
                    self.getVar('formattedText') + formattedText)
        if inParagraph:
            return self.handle_paragraphStart(None, ignoreEmptyParagraph = 1)
        else:
            return None

    def handle_linkMiddle(self, group):
        if self.getVar('state') != 'linkName':
            return None
        name = self.getVar('formattedText')
        self.pullState()
        self.pushState({
            'formattedText': '',
            'name': name,
            'regexpObject': self.linkRegexpObject,
            'state': 'link',
            })
        return None

    def handle_linkStart(self, group):
        self.pushState({
            'formattedText': '',
            'regexpObject': self.linkNameRegexpObject,
            'state': 'linkName',
            })
        return None

    def handle_linkStop(self, group):
        if self.getVar('state') != 'link':
            return None
        title = None
        name = self.getVar('name')
        link = self.getVar('formattedText')
        self.pullState()
        link = link.strip()
        title, url = parseGlasnostLink(link)
        if not title:
            title = url
        if name and '|' in name:
            oTitle = title
            name, title = name.split('|', 2)
            if not name:
                name = oTitle
        elif not name:
            name = title
            title = None
        if name == title or url == title:
            title = None
        return self.formatter.link(name.strip(), url, title)

    def handle_linkWithoutMiddleStop(self, group):
        if self.getVar('state') != 'linkName':
            return None
        formattedText = self.getVar('formattedText')
        self.pullState()
        return self.formatter.text('[') \
               + formattedText \
               + self.formatter.text(']')

    def handle_listLineStart(self, group):
        matchObject = re.match(
            r'^(?P<indent> *)-((?P<type>[0-9aAiI])\.)?',
            group)
        indent = len(matchObject.group('indent'))
        type = matchObject.group('type')
        list = self.getVar('list')
        list.append([indent, type, None])
        return None

    def handle_listLineContinuation(self, group):
        return None

    def handle_listLineStop(self, group):
        list = self.getVar('list')
        list[-1][-1] = self.getVar('formattedText').lstrip()
        self.setVar('formattedText', '')
        return None

    def handle_listStart(self, group):
        self.pushState({
            'formattedText': '',
            'list': [],
            'regexpObject': self.listRegexpObject,
            'state': 'list',
            })
        return self.handle_listLineStart(group)

    def handle_listStop(self, group):
        if self.getVar('state') != 'list':
            # The following pullState is mandatory, because group is empty, so
            # if we don't pull a state, handle_listStop will be called forever.
            if len(self.states) > 2:
                self.pullState()
            return None
        self.handle_listLineStop(group)
        list = self.getVar('list')
        self.pullState()
        return self.formatter.list(list)

    def handle_nonBreakingSpace(self, group):
        return self.formatter.nonBreakingSpace()

    def handle_paragraphStart(self, group, ignoreEmptyParagraph = 1):
        self.pushState({
            'formattedText': '',
            'ignoreEmptyParagraph': ignoreEmptyParagraph,
            'regexpObject': self.paragraphRegexpObject,
            'state': 'paragraph',
            })
        return None

    def handle_paragraphStop(self, group, ignoreEmptyParagraph = 0,
                             isLastParagraph = 0):
        if self.getVar('state') != 'paragraph':
            return None
        formattedText = self.getVar('formattedText').strip()
        ignoreEmptyParagraph = ignoreEmptyParagraph \
                               or self.getVar('ignoreEmptyParagraph')
        self.pullState()
        self.setVar('paragraphsCount', self.getVar('paragraphsCount') + 1)
        if ignoreEmptyParagraph and not formattedText:
            return None
        elif isLastParagraph and self.getVar('paragraphsCount') == 1:
            return formattedText
        elif re.match('^<strong>.+</strong>$', formattedText) and \
                len(re.findall('<strong>.+?</strong>', formattedText)) == 1:
            formattedText = formattedText[8:-9]
            return self.formatter.intertitle(formattedText, sublevel = 1)
        else:
            return self.formatter.paragraph(formattedText)

    def handle_punctuationAndSpace(self, punctuation):
        return self.formatter.punctuationAndSpace(punctuation)

    def handle_rowStart(self, group):
        table = self.getVar('table')
        table.append([])
        return self.handle_cellStart(group)

    def handle_rowStop(self, group):
        return self.handle_cellStop(group)

    def handle_spaceAndPunctuation(self, punctuation):
        return self.formatter.spaceAndPunctuation(punctuation)

    def handle_strongStart(self, group):
        if self.getVar('state') == 'table' and self.getVar('hasHeader'):
            return None
        self.pushState({
            'formattedText': '',
            'state': 'strong',
            })
        return None

    def handle_strongStop(self, group):
        if self.getVar('state') != 'strong':
            return None
        formattedText = self.getVar('formattedText')
        self.pullState()
        return self.formatter.strong(formattedText)

    def handle_tableStart(self, group, hasHeader = 0):
        self.pushState({
            'formattedText': '',
            'hasHeader': hasHeader,
            'table': [],
            'regexpObject': self.tableRegexpObject,
            'state': 'table',
            })
        return self.handle_rowStart(group)

    def handle_tableStop(self, group):
        self.handle_rowStop(group)
        table = self.getVar('table')
        hasHeader = self.getVar('hasHeader')
        self.pullState()
        return self.formatter.table(table, hasHeader)

    def handle_tableWithHeaderStart(self, group):
        return self.handle_tableStart(group, hasHeader = 1)

    def handle_teletypeStart(self, group):
        self.pushState({
            'formattedText': '',
            'state': 'teletype',
            })
        return None

    def handle_teletypeStop(self, group):
        if self.getVar('state') != 'teletype':
            return None
        formattedText = self.getVar('formattedText')
        self.pullState()
        return self.formatter.teletype(formattedText)

    def handle_word(self, word):
        return self.formatter.text(word)

    def handle_wordAsIs(self, word):
        return word

    def handle_wordInUrl(self, word):
        return word
    
    def hasVar(self, name):
        for state in self.states:
            if state.has_key(name):
                return 1
        else:
            return 0

    def pullState(self):
        del self.states[0]
        
    def pushState(self, state):
        self.states.insert(0, state)

    def setVar(self, name, value):
        self.states[0][name] = value


def makeDocBookFromSpip(text, **keywords):
    parser = SpipParser()
    formatter = FormatterDocBook()
    return parser.format(formatter, text, **keywords)


def makeHtmlFromDocBook(text, inline = 0):
    # FIXME: To do: Handle the case when inline == 0.
    filePath = newTemporaryFile('docbook')
    errorFilePath = filePath + '.err'
    xmlFilePath = filePath + '.xml'
    htmlFilePath = filePath + '.html'
    xmlFile = open(xmlFilePath, 'wb')
    xmlFile.write(text)
    xmlFile.close()
    result = os.system('xsltproc -o %(htmlFilePath)s %(xsltFilePath)s '\
            '%(xmlFilePath)s 2>%(errorFilePath)s' % {
        'errorFilePath': errorFilePath,
        'htmlFilePath': htmlFilePath,
        'xmlFilePath': xmlFilePath,
        'xsltFilePath':
            '/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/xhtml/docbook.xsl',
        })
    os.remove(xmlFilePath)
    errorFileStat = os.stat(errorFilePath)
    errorFileSize = errorFileStat[ST_SIZE]
    if errorFileSize > 0:
        errorFile = open(errorFilePath, 'rb')
        error = errorFile.read()
        errorFile.close()
    else:
        error = None
    os.remove(errorFilePath)
    if error is not None and error.find('error:') >= 0:
        error = error.replace('&', '&amp;')
        error = error.replace('"', '&quot;')
        error = error.replace('<', '&lt;')
        error = error.replace('>', '&gt;')
        error = '<pre>%s</pre>' % error
        return error
    else:
        htmlFile = open(htmlFilePath, 'rb')
        html = htmlFile.read()
        htmlFile.close()
        os.remove(htmlFilePath)
        return html


def makeHtmlFromHtml(text, inline = 0, **keywords):
    # from http://sourceforge.net/snippet/detail.php?type=snippet&id=100121
    AllowedHTML = re.compile(r"""
        (?isx)              # case-insensitive, multiline, verbose regexp
        </?b> |             # bold
        </?i> |             # italics
        </?p> |             # paragraph (alignment)
        <a .+?> |           # anchors (links)
        </a> |              # anchor close
        </?ol> |            # ordered lists (numbered)
        </?ul> |            # unordered lists (bulleted)
        <li> |              # list elements
        </?em> |            # emphasis
        <br /> |            # link breaks
        <hr /> |            # horizontal rules
        </?tt> |            # teletype font
        </?strong> |        # strong emphasis
        </?blockquote> |    # block quotes
        </?address> |       # addresses
        </?h[2-6]>          # heading markers
        """)

    def HTMLChecker(match_obj):
        """Validate HTML.

        Given an re module match object as a parameter, it returns either
        the HTML tag in question if it's allowed, or a null string.

        """
    
        if AllowedHTML.search(match_obj.group()):
            return match_obj.group()
        else:
            return ""

    text = re.sub(r"(?is)<.+?>", HTMLChecker, text)

    return text


def makeHtmlFromPreformattedText(text, simple = 0, inline = 0):
    if simple:
        formatter = FormatterHtmlSimple()
    else:
        formatter = FormatterHtmlComplete()
    text = text.replace('&', '&amp;')
    text = text.replace('<', '&lt;')
    text = text.replace('\f', formatter.lineSeparator())
    if inline:
        text = formatter.preformattedInline(text)
    else:
        text = formatter.preformatted(text)
    return text


def makeHtmlFromReStructuredText(text, simple = 0, inline = 0, **keywords):
    # FIXME: To do: Handle the case when inline == 0.
    if not docutils:
        return 'docutils package not installed, no support for' \
               ' reStructuredText'

    from docutils import core, frontend, io, readers, parsers, writers, nodes
    htmlWriter = writers.get_writer_class('html')
    htmlTranslator = htmlWriter().translator_class

    class Writer(htmlWriter):
        def __init__(self, **keywords):
            htmlWriter.__init__(self)
            self.translator_class = GlasnostHTMLTranslator
            sectionLevel = 2
            if keywords.has_key('sectionLevel'):
                sectionLevel = int(keywords['sectionLevel']) - 1
            self.translator_class.baseSectionLevel = sectionLevel

    class GlasnostHTMLTranslator(htmlTranslator):
        baseSectionLevel = 1
        def __init__(self, document):
            htmlTranslator.__init__(self, document)
            self.section_level = self.baseSectionLevel

        def astext(self):
            return enhanceTypo(''.join(self.body))

        def encode(self, text):
            """Encode special characters in `text` & return."""
            # FIXME: A codec to do these and all other HTML entities would be
            # nice.
            text = text.replace("&", "&amp;")
            text = text.replace("<", "&lt;")
            text = text.replace('"', "&quot;")
            text = text.replace(">", "&gt;")
            return text

        def visit_document(self, node):
            pass
        
        def depart_document(self, node):
            pass

        def visit_footnote(self, node):
            self.body.append(self.starttag(node, 'div', CLASS = 'footnotes'))
            self.footnote_backrefs(node)

        def footnote_backrefs(self, node):
            if self.settings.footnote_backlinks and node.hasattr('backrefs'):
                backrefs = node['backrefs']
                if len(backrefs) == 1:
                    self.context.append('')
                    self.context.append('<a class="fn-backref" href="#%s" '
                                        'name="%s">' % (backrefs[0], node['id']))
                else:
                    i = 1
                    backlinks = []
                    for backref in backrefs:
                        backlinks.append('<a class="fn-backref" href="#%s">%s</a>'
                                         % (backref, i))
                    i += 1
                    self.context.append('<em>(%s)</em> ' % ', '.join(backlinks))
                    self.context.append('<a name="%s">' % node['id'])
            else:
                self.context.append('')
                self.context.append('<a name="%s">' % node['id'])

        def visit_label(self, node):
            self.body.append(self.starttag(node, 'p', self.context.pop()))

        def depart_label(self, node):
            self.body.append('</a>&nbsp;%s' % self.context.pop())

        def depart_footnote(self, node):
            self.body.append('\n</div>\n')

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

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

        def visit_reference(self, node):
            title = ''
            if node.has_key('refuri'):
                # href = node['refuri']
                # too bad: refuri has spaces removed; we have to dig the raw
                # attribute.
                try:
                    href = re.match(r'`.*<(?P<href>.*)>`',
                            node.rawsource, re.DOTALL).group('href')
                except:
                    href = node['refuri']
                title, href = parseGlasnostLink(href)
            elif node.has_key('refid'):
                href = '#' + node['refid']
            elif node.has_key('refname'):
                href = '#' + self.document.nameids[node['refname']]

            if title:
                self.body.append(self.starttag(node, 'a', '', href=href,
                                               title=title,
                                               CLASS='reference'))
            else:
                self.body.append(self.starttag(node, 'a', '', href=href,
                                               CLASS='reference'))

        def visit_image(self, node):
            atts = node.attributes.copy()
            fullUrl = None
            matchObject = re.match(
                r'((?P<localId>\S+)((,(?P<width>\d+)\*(?P<height>\d+))|))?', atts['uri'])
            if matchObject and matchObject.group('localId'):
                dispatcherHost = context.getVar('dispatcherId')[11:]
                localId = matchObject.group('localId')
                width = matchObject.group('width')
                height = matchObject.group('height')
                serverRole = 'uploadfiles'
                id = '[{glasnost:partialid:%s:%s:%s}]' % (
                        dispatcherHost, serverRole, localId)
                if not atts.has_key('title'):
                    atts['title'] = '[{glasnost:label:%s:%s:%s}]' % (
                            dispatcherHost, serverRole, localId)
                if (width or height):
                    # had: "and self.formatter.prescaleImage():"
                    atts['src'] = '[{glasnost:thumbnail:%s:%s:%s:width=%s:height=%s}]' \
                          % (dispatcherHost, serverRole, localId, width, height)
                    atts['width'] = width
                    atts['height'] = height
                    fullUrl = '[{glasnost:image:%s:%s:%s}]' % (
                            dispatcherHost, serverRole, localId)
                    # Disable image scaling from formatter.
                    width = height = None
                else:
                    atts['src'] = '[{glasnost:image:%s:%s:%s}]' % (
                            dispatcherHost, serverRole, localId)
            if not atts.has_key('src'):
                atts['src'] = atts['uri']
            del atts['uri']
            if isinstance(node.parent, nodes.TextElement):
                self.context.append('')    
                if fullUrl:
                    self.body.append('<a href="%s">' % fullUrl)
                    self.context.append('</a>')
            else:
                self.body.append('<p>')
                if fullUrl:
                    self.body.append('<a href="%s">' % fullUrl)
                    self.context.append('</a>')
                self.context.append('</p>\n')
            self.body.append(self.emptytag(node, 'img', '', **atts))
        
        def visit_table(self, node):
            self.body.append(
                self.starttag(node, 'table', CLASS='rst'))
            self.rowOdd = 0

        def depart_thead(self, node):
            htmlTranslator.depart_thead(self, node)
            self.rowOdd = 0

        def visit_row(self, node):
            self.rowOdd = not self.rowOdd
            if self.rowOdd:
                klass = 'odd'
            else:
                klass = 'even'
            self.body.append(self.starttag(node, 'tr', '', CLASS=klass))

    return core.publish_string(text, writer=Writer(**keywords),
                settings_overrides={'output_encoding': 'iso-8859-1'})


def makeHtmlFromSpip(text, simple = 0, inline = 0, **keywords):
    if not text:
        return ''

    text = text.replace('\r\n', '\n')
    text = text.replace('\x91', "'").replace('\x92', "'").replace(
            '\x93', "'").replace('\x94', "'").replace(
            '\x81', "'").replace('\x82', "'").replace(
            '&#8217;', "'").replace('&#8230;', '...').replace(
            '&#8211;', '--').replace('&#8220;', '"').replace(
            '&#180;', "'").replace('&#8221;', '"').replace(
            '&#8216;', "'").replace('&#339;', 'oe')
    
    if not inline:
        # Force the spip parser to produce paragraphs.
        text += '\n\n'
    parser = SpipParser()
    if simple:
        formatter = FormatterHtmlSimple()
    else:
        formatter = FormatterHtmlComplete()
    return parser.format(formatter, text, **keywords).strip()


def makeHtmlFromUnformattedText(text, simple = 0, inline = 0):
    text = text.replace('&', '&amp;')
    text = text.replace('<', '&lt;')
    return text


def makeLatexFromSpip(text, **keywords):
    parser = SpipParser()
    formatter = FormatterLatex()
    return parser.format(formatter, text, **keywords)


def newTemporaryFile(directoryName = '', fileNameSuffix = ''):
    import tempfile
    directoryPath = os.path.join('/tmp/glasnost', directoryName)
    if not os.path.exists('/tmp/glasnost'):
        os.mkdir('/tmp/glasnost', 0777)
    if not os.path.exists(directoryPath):
        os.mkdir(directoryPath, 0777)
    tempfile.tempdir = directoryPath
    filePath = tempfile.mktemp(fileNameSuffix)
    return filePath

