# -*- coding: iso-8859-1 -*-
"""
    MoinMoin - Generate PDF document using HTMLDOC

    This action script generate PDF documents from a Wiki site using
    the HTMLDOC (http://www.htmldoc.org) software packages which has
    to be preinstalled first.

    To use this feature install this script in your's MoinMoin action
    script plugin directory.

    Thanks goes to Pascal Bauermeister who initiated the implementaion.
    Lot of things changes since then but the idear using HTMLDOC is the
    main concept of this implementation.

    @copyright: (C) 2006  Pascal Bauermeister
    @copyright: (C) 2006  Raphael Bossek <raphael.bossek@solutions4linux.de>
    @license: GNU GPL, see COPYING for details
"""

import os, stat
import re, copy, sys, StringIO, tempfile, traceback
import shutil, StringIO
from MoinMoin import config, util, wikiutil, packages, error
from MoinMoin.Page import Page
from MoinMoin.util import MoinMoinNoFooter, filesys
from MoinMoin.request import RequestModPy
from MoinMoin.parser import wiki
from MoinMoin.widget.dialog import Dialog

class ActionError(Exception):
    pass

class pdf:
    """Implementation of the PDF document generator."""

    version = u'2.0.0'

    def __init__(self):
        self.action_name = self.__class__.__name__
        self.pagename = None
        self.request = None
        self._ = None
        self.debug = False
        self.msg = None
        self.default_values = {
            'style': u'webpage',
            'format': u'pdf13',
            'linkstyle': u'underline',
            'headerleft': u't',
            'headermiddle': u'.',
            'headerright': u'D',
            'footerleft': u'.',
            'footermiddle': u'/',
            'footerright': u'.',
            'tocheaderleft': u'.',
            'tocheadermiddle': u't',
            'tocheaderright': u'.',
            'tocfooterleft': u'.',
            'tocfootermiddle': u'.',
            'tocfooterright': u'i',
            'linkcolor': u'0000E0',
            'size': u'legal',
            'user-password': u'',
            'owner-password': u'',
            'toclevels': u'3',
            'grayscale': u'unchecked',
            'title': u'checked',
            'duplex': u'unchecked',
            'landscape': u'unchecked',
            'usersize': u'',
            'margintop': u'0.50in',
            'marginbottom': u'0.50in',
            'marginleft': u'1.00in',
            'marginright': u'0.50in',
            'no-toc': u'checked',
            'no-links': u'checked',
            'firstpage': u'p1',
            'jpeg': u'0',
            'compression': u'0',
            'pagemode': u'outline',
            'pagelayout': u'single',
            'firstpage': u'c1',
            'numbered': u'checked',
            'encryption': u'unchecked',
            'permissioncopy': u'checked',
            'permissionprint': u'checked',
            'permissionannotate': u'checked',
            'permissionmodify': u'checked',
            'debug': u'',
            'rev': 0,
        }
        # We have to know which values are checkboxes within the form. If a key does
        # not exists wihtin the form the corresponding checkbox is not checked.
        self.form_checkbox = []
        for key, value in self.default_values.items():
            if value in [u'checked', u'unchecked']:
                self.form_checkbox += [key]
        self.contenttype = u'application/pdf'

    def fixhtmlstr (self, str):
        """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
        htmlstr = u''
        for c in str:
            if ord(c) >= 128:
                htmlstr = htmlstr + '&#%d;' % ord(c)
            else:
                htmlstr = htmlstr + c
        return htmlstr

    def error_msg (self, msg):
        """Display error message."""
        Page (self.request, self.pagename).send_page (self.request, msg=msg)

    def set_page_values(self):
        """Scan raw page for additional information relating PDF generation."""
        #pdflines = False
        for line in self.request.page.get_raw_body().split(u'\n'):
            if line[:6] == u'##pdf ':
                cols = line[6:].split()
                # Only accept known values/settings.
                if len(cols) > 1 and cols[0] in self.default_values:
                    self.values[cols[0]] = u' '.join(cols[1:])
                    #pdflines = True
                continue
            # Stop parsing with first non-pdf line (if detected at least one).
            #elif pdflines and not line:
            #    break

    def set_page_default_values(self):
        # We are not able to recognise if this string is part of a verbatim area.
        matchtoclvl = re.compile(r'^\[\[TableOfContents\(\s*(\d+)\s*\)\]\]')
        matchtoc = re.compile(r'^\[\[TableOfContents\(*\)*\]\]')
        toc = False
        for line in self.request.page.get_raw_body().split(u'\n'):
            if line[:10] == u'#language ' and not u'language' in self.values:
                lang = self.make_isolang(line[10:])
                if lang:
                    self.default_values[u'language'] = lang
            elif not u'toclevels' in self.values and not toc:
                result = matchtoclvl.match(line)
                if result:
                    toclevels = int(result.group(1).strip())
                    if toclevels > 4:
                        toclevels = 4
                    self.default_values[u'toclevels'] = str(toclevels)
                    toc = True
                elif matchtoc.match(line):
                    toc = True
        # We assume if table-of-contents is used we intent to generate a book.
        if toc:
            self.default_values[u'style'] = u'book'
        else:
            # Do not generate a table of contents page.
            self.default_values[u'no-toc'] = u'unchecked'
 
    def escape (self, str):
        return str.replace (u'&', u'&amp;').replace (u'<', u'&lt;').replace (u'>', u'&gt;')

    def _select (self, name, data):
        """Helper function to create a selection control."""
        str = u'<select name="%s" size="1">' % (name,)
        keys = data.keys()
        keys.sort()
        for value in keys:
            if value == self.fields[name]:
                selected = u'selected'
            else:
                selected = u''
            str += u'<option value="%s" %s>%s</option>' % (value, selected, data[value],)
        str += u'</select>'
        return str

    def _chooseformat (self, name):
        """Helper function to create left/middle/right selection controls."""
        str = u"""    <tr>
        <td class="label"><label>%s&nbsp;:</label></td>
        <td><table>
                <tr>
                    <td>%s&nbsp;:</td>
                    <td>%s</td>
                </tr>
                <tr>
                    <td>%s&nbsp;:</td>
                    <td>%s</td>
                </tr>
                <tr>
                    <td>%s&nbsp;:</td>
                    <td>%s</td>
                </tr>
            </table>
        </td>
        <td>%s</td>
    </tr>""" % (self.fields[u'label_' + name],
              self.fields[u'label_left'], self._select(name + u'left', self.default_tocformats),
              self.fields[u'label_middle'], self._select(name + u'middle', self.default_tocformats),
              self.fields[u'label_right'], self._select(name + u'right', self.default_tocformats),
              self.fields[u'help_' + name],)
        return str

    def makeform (self, errormsg=u''):
        self.fields = {
            'error': errormsg,
            'pagename': wikiutil.escape(self.pagename),
            'action': self.action_name,
            'seperator': u'<tr><td colspan="3"><hr/></td></tr>',
            'space': u'<tr><td colspan="3">&nbsp;</td></tr>',

            'label_input': self._(u'Input'),
            'label_output': self._(u'Output'),
            'label_page': self._(u'Page'),
            'label_tableofcontents': self._(u'Contents'),
            'label_pdf': self._(u'PDF'),
            'label_security': self._(u'Security'),

            'label_choose_style': self._(u'Choose style'),
            'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
                                 self._(u'webpage: Specifies that the HTML sources are unstructured (plain web pages.) A page break is inserted between each file or URL in the output.') + u'<br/>' + \
                                 self._(u'continuous: Specifies that the HTML sources are unstructured (plain web pages.) No page breaks are inserted between each file or URL in the output.'),

            'label_format': self._(u'Output format'),
            'help_format': self._(u'Specifies the output format.'),

            'label_outputoptions': self._(u'Output options'),
            'label_grayscale': self._(u'Grayscale document'),
            'label_title': self._(u'Title page'),
            'label_jpeg': self._(u'JPEG big images'),
            'label_compression': self._(u'Compression'),

            'label_no-toc': self._(u'Generate a table of contents'),
            'help_no-toc': self._(u''),

            'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
            'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),

            'label_numbered': self._(u'Numbered headings'),
            'help_numbered': self._(u'Check to number all of the headings in the document.'),

            'label_toctitle': self._(u'Table-of-contents title'),
            'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),

            'label_left': self._(u'Left'),
            'label_middle': self._(u'Middle'),
            'label_right': self._(u'Right'),

            'label_tocheader': self._(u'Header of table-of-contantes page'),
            'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),

            'label_tocfooter': self._(u'Footer of table-of-contantes page'),
            'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),

            'label_header': self._(u'Page header'),
            'help_header': self._(u'Sets the page header to use on body pages.'),

            'label_footer': self._(u'Page footer'),
            'help_footer': self._(u'Sets the page footer to use on body pages.'),

            'label_no-links': self._(u'Create HTTP links'),
            'help_no-links': self._(u'Enables generation of links in PDF files.'),
            
            'label_linkstyle': self._(u'Style of HTTP links'),
            'help_linkstyle': self._(u''),

            'label_linkcolor': self._(u'HTTP links color'),
            'help_linkcolor': self._(u'Sets the color of links.'),

            'label_duplex': self._(u'2-Sided'),
            'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),

            'label_landscape': self._(u'Landscape'),

            'label_choose_size': self._(u'Choose page size'),
            'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),

            'label_usersize': self._(u'User defined page size'),
            'help_usersize': self._(u'Specifies the page size using a standard name or in points (no suffix or ##x##pt), inches (##x##in), centimeters (##x##cm), or millimeters (##x##mm).'),

            'label_margin': self._(u'User defined margin'),
            'label_margintop': self._(u'Top'),
            'label_marginbottom': self._(u'Bottom'),
            'label_marginleft': self._(u'Left'),
            'label_marginright': self._(u'Right'),
            'help_margin': self._(u'Specifies the margin size using points (no suffix or ##x##pt), inches (##x##in), centimeters (##x##cm), or millimeters (##x##mm).') + u' ' + self._(u'Keep empty for default value.'),

            'label_pagemode': self._(u'Page mode'),
            'help_pagemode': self._(u'Controls the initial viewing mode for the document.') + u'<br/>' + self._(u'Document: Displays only the docuemnt pages.') + u'<br/>' + self._(u'Outline: Display the table-of-contents outline as well as the document pages.') + u'<br/>' + self._(u'Full-screen: Displays pages on the whole screen; this mode is used primarily for presentations.'),

            'label_pagelayout': self._(u'Page layout'),
            'help_pagelayout': self._(u'Controls the initial layout of document pages on the screen.') + u'<br/>' + self._(u'Single: Displays a single page at a time.') + u'<br/>' + self._(u'One column: Displays a single column of pages at a time.') + u'<br/>' + self._(u'Two column left/right: Display two columns of pages at a time; the first page is displayed in the left or right column as selected.'),

            'label_firstpage': self._(u'First page'),
            'help_firstpage': self._(u'Choose the initial page that will be shown.'),

            'label_encryption': self._(u'Encryption'),
            'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),

            'label_permissions': self._(u'Permissions'),
            'help_permissions': self._(u'Specifies the document permissions.'),

            'label_permissionannotate': self._(u'Annotate'),
            'label_permissionprint': self._(u'Print'),
            'label_permissionmodify': self._(u'Modify'),
            'label_permissioncopy': self._(u'Copy'),

            'label_owner-password': self._(u'Owner password'),
            'help_owner-password': self._(u'Specifies the owner password to control who can change document permissions etc.') + u' ' + self._(u'If this field is left blank, a random 32-character password is generated so that no one can change the document.'),

            'label_user-password': self._(u'User password'),
            'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),

            'label_expert': self._(u'Expert'),
            'label_language': self._(u'Language translation'),
            'help_language': self._(u'Specify language to use for date and time format.'),

            'button_generate': self._(u'Generate PDF'),
            'button_remember': self._(u'Remember form'),
            'button_cancel': self._(u'Cancel'),
            'button_reset': self._(u'Reset'),

            'label_downloadpdfpy': self._('Download this MoinMoin action plugin of version'),
            }
        self.fields.update(self.values)

        # Go through all format strings.
        for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
            self.fields[u'choose_' + name] = self._chooseformat(name)

        self.fields[u'select_style'] = self._select (u'style', self.default_styles)
        self.fields[u'select_format'] = self._select (u'format', self.default_formats)
        self.fields[u'select_linkstyle'] = self._select (u'linkstyle', self.default_linkstyles)
        self.fields[u'select_size'] = self._select (u'size', self.default_sizes)
        self.fields[u'select_jpeg'] = self._select (u'jpeg', self.default_jpeg)
        self.fields[u'select_compression'] = self._select (u'compression', self.default_compression)
        self.fields[u'select_toclevels'] = self._select (u'toclevels', self.default_toclevels)
        self.fields[u'select_pagemode'] = self._select (u'pagemode', self.default_pagemodes)
        self.fields[u'select_pagelayout'] = self._select (u'pagelayout', self.default_pagelayouts)
        self.fields[u'select_firstpage'] = self._select (u'firstpage', self.default_firstpage)

        self.fields[u'pdfpydownloadlink'] = u'http://einstein.speech-design.de/~br/pdf.py'
        self.fields[u'pdfpyversion'] = self.version

        form = """<p class="error">%(error)s</p>
<form method="post" action="">
<input type="hidden" name="action" value="%(action)s"/>
<table>
    <tr><td colspan="2">&nbsp;</td><td><i>%(label_downloadpdfpy)s %(pdfpyversion)s: <a href="%(pdfpydownloadlink)s">pdf.py</a></i></td></tr>
    <tr><td colspan="3">%(label_input)s</td></tr>
    <tr>
        <td class="label"><label>%(label_choose_style)s&nbsp;:</label></td>
        <td class="content">%(select_style)s</td>
        <td>%(help_choose_style)s</td>
    </tr>
    %(seperator)s
    <tr><td colspan="3">%(label_output)s</td></tr>
    <tr>
        <td class="label"><label>%(label_format)s&nbsp;:</label></td>
        <td class="content">%(select_format)s</td>
        <td>%(help_format)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_outputoptions)s&nbsp;:</label></td>
        <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s&nbsp;
            <input type="checkbox" name="title" value="checked" %(title)s />%(label_title)s<br />
            %(label_compression)s&nbsp:&nbsp;%(select_compression)s&nbsp;
            %(label_jpeg)s&nbsp;:&nbsp;%(select_jpeg)s</td>
    </tr>
    %(seperator)s
    <tr><td colspan="3">%(label_page)s</td></tr>
    <tr>
        <td class="label"><label>%(label_choose_size)s&nbsp;:</label></td>
        <td>%(select_size)s&nbsp;<br /><nobr>%(label_usersize)s&nbsp;:&nbsp;<input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
        <td>%(help_choose_size)s<br />%(help_usersize)s</td>
    </tr>
    <tr>
        <td>&nbsp;</td>
        <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s />&nbsp;%(label_duplex)s&nbsp;
            <input type="checkbox" name="landscape" value="checked" %(landscape)s />&nbsp;%(label_landscape)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_margin)s&nbsp;:</label></td>
        <td><table><tr><td>&nbsp;</td><td><nobr><label>%(label_margintop)s&nbsp;:</label>&nbsp;<input type="text" name="margintop" value="%(margintop)s" size="7" /></nobr></td><td>&nbsp;</td></tr>
            <tr><td><nobr><label>%(label_marginleft)s&nbsp;:</label>&nbsp;<input type="text" name="marginleft" value="%(marginleft)s" size="7" /></nobr></td><td>&nbsp;</td><td><nobr><label>%(label_marginright)s&nbsp;:</label>&nbsp;<input type="text" name="marginright" value="%(marginright)s" size="7" /></nobr></td></tr>
            <tr><td>&nbsp;</td><td><nobr><label>%(label_marginbottom)s&nbsp;:</label>&nbsp;<input type="text" name="marginbottom" value="%(marginbottom)s" size="7" /></nobr></td><td>&nbsp;</td></tr></table>
        <td>%(help_margin)s</td>
    </tr>
    %(choose_header)s
    %(choose_footer)s
    %(seperator)s
    <tr><td colspan="3">%(label_tableofcontents)s</td></tr>
    <tr>
        <td class="label"><label>%(label_no-toc)s&nbsp;:</label></td>
        <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
        <td>%(help_no-toc)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_toclevels)s&nbsp;:</label></td>
        <td class="content">%(select_toclevels)s</td>
        <td>%(help_toclevels)s</td>
    </tr>
    <tr>
        <td>&nbsp;</td>
        <td><input type="checkbox" name="numbered" value="checked" %(numbered)s />&nbsp;%(label_numbered)s</td>
        <td>%(help_numbered)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_toctitle)s&nbsp;:</label></td>
        <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
        <td>%(help_toctitle)s</td>
    </tr>
    %(choose_tocheader)s
    %(choose_tocfooter)s
    %(seperator)s
    <tr><td colspan="3">%(label_pdf)s</td></tr>
    <tr>
        <td class="label"><label>%(label_pagemode)s&nbsp;:</label></td>
        <td class="content">%(select_pagemode)s</td>
        <td>%(help_pagemode)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_pagelayout)s&nbsp;:</label></td>
        <td class="content">%(select_pagelayout)s</td>
        <td>%(help_pagelayout)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_firstpage)s&nbsp;:</label></td>
        <td class="content">%(select_firstpage)s</td>
        <td>%(help_firstpage)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_no-links)s&nbsp;:</label></td>
        <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
        <td>%(help_no-links)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_linkstyle)s&nbsp;:</label></td>
        <td class="content">%(select_linkstyle)s</td>
        <td>%(help_linkstyle)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_linkcolor)s&nbsp;:</label></td>
        <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
        <td>%(help_linkcolor)s</td>
    </tr>
    %(seperator)s
    <tr><td colspan="3">%(label_security)s</td></tr>
    <tr>
        <td class="label"><label>%(label_encryption)s&nbsp;:</label></td>
        <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
        <td>%(help_numbered)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_permissions)s&nbsp;:</label></td>
        <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s />&nbsp;%(label_permissionprint)s</nobr>&nbsp;
            <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s />&nbsp;%(label_permissionmodify)s</nobr><br />
            <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s />&nbsp;%(label_permissioncopy)s</nobr>&nbsp;
            <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s />&nbsp;%(label_permissionannotate)s</nobr></td>
        <td>%(help_permissions)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_user-password)s&nbsp;:</label></td>
        <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
        <td>%(help_user-password)s</td>
    </tr>
    <tr>
        <td class="label"><label>%(label_owner-password)s&nbsp;:</label></td>
        <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
        <td>%(help_owner-password)s</td>
    </tr>
    %(seperator)s
    <tr><td colspan="3">%(label_expert)s</td></tr>
    <tr>
        <td class="label"><label>%(label_language)s&nbsp;:</label></td>
        <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
        <td>%(help_language)s</td>
    </tr>
     %(space)s
    <tr>
        <td><input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" /></td>
        <td class="buttons" colspan="2">
            <input type="submit" name="generate_from_form" value="%(button_generate)s" />&nbsp;
            <input type="submit" name="remember" value="%(button_remember)s" />&nbsp;
            <input type="submit" name="cancel" value="%(button_cancel)s" />
        </td>
    </tr>
</table>
</form>""" % self.fields
        return Dialog (self.request, content=form)

    def run (self, pagename, request):
        """ Main dispatcher for the action."""
        self.pagename = pagename
        self.request = request
        self._ = self.request.getText
        self.form = self.request.form
        self.cfg = self.request.cfg

        # Canceled by user.
        if self.form.has_key(u'cancel'):
            return self.request.page.send_page(self.request)

        # Determine calling parameters.
        if self.form.get(u'debug', u'0') == u'1':
            self.debug = True

        self.default_tocformats = {
            u'/': self._(u'1/N,2/N Arabic page numbers'),
            u':': self._(u'1/C,2/C Arabic chapter page numbers'),
            u'1': self._(u'1,2,3,...'),
            u'a': self._(u'a,b,c,...'),
            u'A': self._(u'A,B,C,...'),
            u'c': self._(u'Chapter title'),
            u'C': self._(u'Chapter page number'),
            u'd': self._(u'Date'),
            u'D': self._(u'Date + Time'),
            u'h': self._(u'Heading'),
            u'i': self._(u'i,ii,iii,iv,...'),
            u'I': self._(u'I,II,III,IV,...'),
            u't': self._(u'Title'),
            u'T': self._(u'Time'),
            u'.': self._(u'Blank'),
            # TODO: Not supported yet; u'l': self._(u'Logo image'),
        }
        self.default_styles = {
            u'webpage': self._(u'webpage'),
            u'book': self._(u'book'),
            u'continuous': self._(u'continuous'),
        }
        self.default_sizes = {
            u'legal': self._(u'Legal (8.5x14in)'),
            u'a4': self._(u'A4 (210x297mm)'),
            u'letter': self._(u'Letter (8.5x11in)'),
            u'universal': self._(u'Universal (8.27x11in)'),
            u'': self._(u'User defined'),
        }
        self.default_formats = {
            u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
            u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
            u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
            u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
            # TODO: Not supported yet:
            #u'ps1': self._(u'PostScript Level 1'),
            #u'ps2': self._(u'PostScript Level 2'),
            #u'ps3': self._(u'PostScript Level 3'),
        }
        self.default_linkstyles = {
            u'underline': self._(u'Underline'),
            u'plain': self._(u'Plain'),
        }
        self.default_firstpage = {
            u'c1': self._(u'1st chapter'),
            u'p1': self._(u'1st page'),
            u'toc': self._(u'Contents'),
        }
        self.default_jpeg = {
            u'0': self._(u'None'),
            u'50': self._(u' 50% (Good)'),
            u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
            u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
            u'100': self._(u'100% (Best)'),
        }
        self.default_compression = {
            u'0': self._(u'None'),
            u'1': self._(u'1 (Fast)'),
            u'2': u'2', u'3': u'3', u'4': u'4', u'5': u'5', u'6': u'6', u'7': u'7', u'8': u'8',
            u'9': self._(u'9 (Best)'),
        }
        self.default_toclevels = {
            u'0': self._(u'None'),
            u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
        }
        self.default_pagemodes = {
            u'outline': self._(u'Outline'),
            u'document': self._(u'Document'),
            u'fullscreen': self._(u'Full-screen'),
        }
        self.default_pagelayouts = {
            u'single': self._(u'Single'),
            u'one': self._(u'One column'),
            u'twoleft': self._(u'Two column left'),
            u'tworight': self._(u'Two column right'),
        }

        # Set translated name of table of contents as default.
        self.default_values[u'toctitle'] = self._(u'Contents')

        # Make sure we create date and time strings in right format.
        if self.request.page.language:
            self.default_values[u'language'] = self.request.page.language
        else:
            self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get (u'default_language', u'en'))

        self.values = {}
        self.set_page_default_values()

        # Create a PDF document direct without any user iteraction from default and page settings.
        if self.form.has_key(u'generate'):
            self.set_page_values()
            self.update_values(useform=False)
            return self.do_generate()

        # Create a PDF document from form settings.
        if self.form.has_key(u'generate_from_form'):
            self.update_values()
            return self.do_generate()

        if self.form.has_key(u'remember'):
            self.update_values()
            return self.do_remember()

        self.set_page_values()
        self.update_values(useform=False)
        return self.request.page.send_page (self.request, msg=self.makeform())

    def update_values(self, useform=True):
        """Preset values with they form values or defaults."""
        for key, default in self.default_values.items():
            # Modify value only if not already set.
            if not key in self.values:
                # If the form does not contain the value (e.g. for checkboxes) set the
                # default value (e.g. for checkboxes unset by default).
                if not key in self.form:
                    # Special processing for checkboxes in forms. If the key does not exists
                    # withint the form it is not checked.
                    if key in self.form_checkbox and useform:
                        self.values[key] = u'unchecked'
                    else:
                        self.values[key] = default
                else:
                    self.values[key] = self.form[key][0]
        # Check if revision is an integer value.
        try:
            self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
        except:
            self.values[u'rev'] = self.request.page.rev
        # Check if page revision exists.
        (pagefname, realrev, exists) = self.request.page.get_rev (rev=self.values[u'rev'])
        if exists:
            self.values[u'rev'] = realrev
        else:
            # Determine latest revision number.
            (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()

    def do_generate(self):
        """Create PDF document."""
        # Generate the HTML page using MoinMoin wiki engine.
        html = self.get_html()

        if not html:
            self.error_msg (msg=self._(u'Could not generate HTML page.'))

        pdfdata = self.html2pdf(html)

        if self.debug:
            self.request.http_headers()

        if self.debug:
            self.request.write(html)
        elif pdfdata:
            # Send as application/pdf the generated file by HTMLDOC
            self.send_pdf(pdfdata)
            raise MoinMoinNoFooter

    def do_remember(self):
        """Create a message containing information about how to save the form values for future reuse."""
        save = u''
        for key, value in self.values.items():
            if key in [u'user-password', u'owner-password', u'rev', u'debug']:
                continue
            if key in self.default_values and value == self.default_values[key]:
                continue
            save += u'##pdf %s %s\n' % (key, value,)
        if save:
            msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
        else:
            msg = self._(u'All values correspond to they default. Nothing have to be saved.')
        return self.request.page.send_page (self.request, msg)

    def send_pdf (self, data):
        filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'

        # Send HTTP header.
        self.request.http_headers([
            u'Content-Type: %s' % self.contenttype,
            u'Content-Length: %d' % len(data),
            # TODO: fix the encoding here, plain 8 bit is not allowed
            # according to the RFCs There is no solution that is
            # compatible to IE except stripping non-ascii chars
            u'Content-Disposition: inline; filename="%s"' %
            filename.encode(config.charset),
            ])

        # Send binary data.
        sio = StringIO.StringIO(data)
        shutil.copyfileobj (sio, self.request, 8192)

    def get_html(self):
        """Generate the HTML body of this page."""
        # Get HTML for the page: Make a copy of the request, run it and intercept the output
        newreq = copy.copy(self.request)
        if self.debug:
            newreq.form[u'action'] = [u'show']
        else:
            newreq.form[u'action'] = [u'print']
        if self.values.get(u'rev', None):
            newreq.form[u'rev'] = self.values[u'rev']

        # Divert stdout and re-run the request.
        out = StringIO.StringIO()
        newreq.redirect(out)
        # Differentiate between CGI and mod_python.
        if isinstance (newreq, RequestModPy):
            newreq.run(None)
        else:
            newreq.run()
        html = out.getvalue()
        out.close()

        # FIXME: Why have we to reset the action? Otherwise we can not display error messages
        # because the self.request page is in ``print'' preview mode!
        newreq.form[u'action'] = [u'show']

        # Remove request's HTTP header.
        lines = html.split(u'\n')
        while lines[0].strip():
            del lines[0]
        html = u'\n'.join(lines)

        # Make URLs absolute.
        # Until MoinMoin is not XHTML compilant we can not use a XML parser (e.g. expat)
        # to transform the HTML document. In the meantime we try to achive the same with
        # regular expressions subtitution.
        base = self.request.getQualifiedURL()
        for htmlref in [u'src', u'href']:
            reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
            urlref = re.compile (reurlref, re.I)
            for match in urlref.finditer(html):
                foundref = match.groups()
                html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])

        return html

    def make_isolang (self, language):
        return language + u'_' + language.upper()

    def html2pdf (self, html):
        """Create a PDF document based on the current parameters."""
        # Determine UID to access ACL protected sites too (mandatory to download attached images).
        htmldocopts = [u'LANG=' + self.values[u'language'], u'HTMLDOC_NOCGI=1', u'htmldoc', "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']

        for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
            self.values[key] = self.values.get (key + u'left', u'.') + self.values.get (key + u'middle', u'.') + self.values.get (key + u'right', u'.')

        permissions = []
        for opt, value in self.values.items():
            if opt in [u'language', u'debug', u'rev']:
                continue
            value = value.strip()
            if not value:
                continue
            # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
            if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
                continue
            if opt in [u'style']:
                htmldocopts += [u'--' + value]
            elif opt in self.form_checkbox:
                if value == u'checked':
                    if opt[:10] == u'permission':
                        permissions += [opt[10:]]
                    # Reverse meaning of 'no-' options.
                    elif opt[:3] != u'no-':
                        htmldocopts += [u'--' + opt]
                elif opt[:3] == u'no-':
                    htmldocopts += [u'--' + opt]
            elif opt[:6] == u'margin':
                htmldocopts += [u'--' + opt[6:], value]
            else:
                htmldocopts += [u'--' + opt, value]
        if permissions:
            htmldocopts += [u'--permission', u','.join (permissions)]
        htmldocopts += [u'-']
        # Do not forget to escape all spaces!
        eschtmldocopts = [arg.replace(u' ', u'\\ ') for arg in htmldocopts]
        cmdstr = u' '.join(eschtmldocopts)
        errmsg = None

        pdf = None
        if self.debug:
            errmsg = self._(u'Command:') + u'<pre>' + self.escape(cmdstr) + u'</pre>'
        else:
            inp, out, err = os.popen3 (cmdstr, u'b')
            try:
                inp.write(self.fixhtmlstr(html))
                inp.close()
            except:
                pass

            pdf = out.read()
            out.close()

            htmldocmsg = err.read()
            err.close()

            # REMARK: Otherwise we get <defunct> processes.
            os.wait()

            # Check for error message on STDOUT.
            if pdf[:8] == u'HTMLDOC ':
                htmldocmsg += pdf
                pdf = None

            if htmldocmsg:
                errmsg = self._(u'Command:') + u'<pre>' + self.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
                      self.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'

        # As it is difficult to get the htmldoc return code, we check for
        # error by checking the produced pdf length
        if not pdf and errmsg:
            self.error_msg(errmsg)

        return pdf

def execute (pagename, request):
    pdf().run (pagename = pagename, request = request)

# vim:ts=4:et:sw=4:nu
