Attachment 'CreatePdfDocument2_0_8.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 """
   3     MoinMoin - Generate PDF document using HTMLDOC
   4 
   5     This action script generate PDF documents from a Wiki site using
   6     the HTMLDOC (http://www.htmldoc.org) software packages which has
   7     to be preinstalled first.
   8 
   9     To use this feature install this script in your's MoinMoin action
  10     script plugin directory.
  11 
  12     Thanks goes to Pascal Bauermeister who initiated the implementaion.
  13     Lot of things changes since then but the idear using HTMLDOC is the
  14     main concept of this implementation.
  15 
  16     @copyright: (C) 2006  Pascal Bauermeister
  17     @copyright: (C) 2006  Raphael Bossek <raphael.bossek@solutions4linux.de>
  18     @license: GNU GPL, see COPYING for details
  19     
  20     2006-11-16  RaphaelBossek
  21     * Release v2.0.8
  22     * Fixed another missing configuration for RedirectOutputRequest()
  23 
  24     2006-11-15  RaphaelBossek
  25     * Release v2.0.7
  26     * Fixed support for MoinMoin 1.5.3
  27     * Fixed SSL support.
  28 
  29     2006-11-14  RaphaelBossek
  30     * Release v2.0.6
  31     * MoinMoin 1.6 support added.
  32     * Fixed Windows(TM) platform support.
  33     * Added support for alternative title text.
  34 
  35     2006-09-13  RaphaelBossek
  36     * Release v2.0.5
  37     * Fixed RedirectOutputRequest class where definition of script_name
  38       was missing.
  39 
  40     2006-09-12  RaphaelBossek
  41     * Release v2.0.4
  42     * Fixed RedirectOutputRequest class where function was redifined
  43       to boolean value.
  44 
  45     2006-09-06  RaphaelBossek
  46     * Release v2.0.3
  47     * Fixed FastCGI support by removing stdout redirect output code. The
  48       same functionality will be done by the RedirectOutputRequest class.
  49     * Renamed to CreatePdfDocument (for better translation possibilities).
  50     * Fixed encoding of title page.
  51     * Added charset set option.
  52     * Fixed waiting for HTMLDOC.
  53 
  54     2006-09-02  RaphaelBossek
  55     * Release v2.0.2
  56     * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
  57       configuration parameter support. You are not able to preset available
  58       options and which defaults are used in your configuration file.
  59 
  60     2006-08-30  RaphaelBossek
  61     * Release v2.0.1
  62     * Fixed issue with page revision number forwarding (traceback).
  63 
  64     2006-08-30  RaphaelBossek
  65     * Release v2.0.0
  66     * Feature enchanced and bug fixed version.
  67 
  68     2006-05-26  PascalBauermeister
  69     * Release v1.0.2
  70     * Relative image URLs turned absolute was bogus. It is less bogus now.
  71 
  72     2006-05-26  PascalBauermeister
  73     * Release v1.0.1
  74     * Set env var HTMLDOC_NOCGI to solve CGI issue
  75 
  76     2006-05-24  PascalBauermeister
  77     * Initial release v1.0.0
  78 """
  79 
  80 import os
  81 import sys
  82 import stat
  83 import re
  84 import copy
  85 import shutil
  86 import StringIO
  87 from MoinMoin import config
  88 from MoinMoin import util
  89 from MoinMoin import wikiutil
  90 from MoinMoin import packages
  91 from MoinMoin import error
  92 from MoinMoin.Page import Page
  93 from MoinMoin.request import RequestBase
  94 from MoinMoin.widget.dialog import Dialog
  95 
  96 
  97 class RedirectOutputRequest(RequestBase):
  98     """Redirect output to string without HTTP headers."""
  99     def __init__ (self, req, action=u'print'):
 100         """Prepare a compatible request."""
 101         # 1.5,1.6:req.path_info = u'/RaphaelBossek/\xd6nologe' | u'/FrontPage'
 102         # after enconde() self.path_info = '/RaphaelBossek/\xd6nologe'
 103         self.path_info = req.path_info.encode(config.charset)
 104         # 1.5,1.6:query_string = req.query_string = u'action=CreatePdfDocument'
 105         self.query_string = u'action=' + action
 106         # 1.5,1.6:request_uri = req.request_uri = u'/FrontPage?action=CreatePdfDocument'
 107         self.request_uri = req.path_info.replace(req.query_string, self.query_string)
 108         # 1.5,1.6:url = req.url = u'localhost:8080/FrontPage?action=CreatePdfDocument'
 109         self.url = req.url.replace(req.query_string, self.query_string)
 110         # 1.5,1.6: Not all kinds of request support SSL yet.
 111         if 'is_ssl' in req.__dict__:
 112             self.is_ssl = req.is_ssl
 113         else:
 114             self.is_ssl = 0
 115         self.http_host = req.http_host
 116         self.http_user_agent = req.http_user_agent
 117         self.request_method = None
 118         self.saved_cookie = req.saved_cookie
 119         self.remote_addr = req.remote_addr
 120         self.script_name = getattr (req, u'script_name', u'')
 121 
 122         self.req = req
 123         RequestBase.__init__(self)
 124 
 125     def run(self, rev = None):
 126         """Start processing the document."""
 127         # 1.6:MoinMoin/request/__init__.py: Initialise action from environment variables not from form.
 128         self.action = u'print'
 129         if rev:
 130             self.form[u'rev'] = [rev]
 131         self.output_string = u''
 132         self.error_string = u''
 133         self.sent_headers = False
 134         self.failed = 0
 135         RequestBase.run(self)
 136         return (self.output_string, self.error_string)
 137 
 138     def http_headers (self, *args, **kw):
 139         """Used by MoinMoin 1.5.x instead of emit_http_headers()."""
 140         self.sent_headers = True
 141     
 142     def emit_http_headers(self, *args, **kw):
 143         """Used by MoinMoin 1.6.x instaed of http_headers()."""
 144         self.sent_headers = 1
 145 
 146     def fail (self, err):
 147         """Catch if a error occurs and save the message in our string."""
 148         RequestBase.fail (self, err)
 149         if not self.error_string:
 150             self.error_string = str(err)
 151 
 152     def write (self, *data):
 153         """Catch the document in our output_string."""
 154         if self.sent_headers:
 155             if self.failed:
 156                 self.error_string += data[0]
 157             else:
 158                 self.output_string += data[0]
 159 
 160     def flush(self):
 161         pass
 162 
 163 
 164 class CreatePdfDocument:
 165     """Implementation of the PDF document generator."""
 166 
 167     def __init__(self):
 168         self.action_name = self.__class__.__name__
 169         self.pagename = None
 170         self.request = None
 171         self._ = None
 172         self.debug = False
 173         self.msg = None
 174         self.errormsgsent = False
 175         self.default_values = {
 176             'style': u'webpage',
 177             'format': u'pdf13',
 178             'linkstyle': u'underline',
 179             'headerleft': u't',
 180             'headermiddle': u'.',
 181             'headerright': u'D',
 182             'footerleft': u'.',
 183             'footermiddle': u'/',
 184             'footerright': u'.',
 185             'tocheaderleft': u'.',
 186             'tocheadermiddle': u't',
 187             'tocheaderright': u'.',
 188             'tocfooterleft': u'.',
 189             'tocfootermiddle': u'.',
 190             'tocfooterright': u'i',
 191             'linkcolor': u'0000E0',
 192             'size': u'legal',
 193             'user-password': u'',
 194             'owner-password': u'',
 195             'toclevels': u'3',
 196             'grayscale': u'unchecked',
 197             'title': u'checked',
 198             'duplex': u'unchecked',
 199             'landscape': u'unchecked',
 200             'usersize': u'',
 201             'margintop': u'0.50in',
 202             'marginbottom': u'0.50in',
 203             'marginleft': u'1.00in',
 204             'marginright': u'0.50in',
 205             'no-toc': u'checked',
 206             'no-links': u'checked',
 207             'firstpage': u'p1',
 208             'jpeg': u'0',
 209             'compression': u'0',
 210             'pagemode': u'outline',
 211             'pagelayout': u'single',
 212             'firstpage': u'c1',
 213             'numbered': u'checked',
 214             'encryption': u'unchecked',
 215             'permissioncopy': u'checked',
 216             'permissionprint': u'checked',
 217             'permissionannotate': u'checked',
 218             'permissionmodify': u'checked',
 219             'charset': u'iso-8859-1',
 220             'debug': u'',
 221             'rev': 0,
 222         }
 223         # We have to know which values are checkboxes within the form. If a key does
 224         # not exists wihtin the form the corresponding checkbox is not checked.
 225         self.form_checkbox = []
 226         for key, value in self.default_values.items():
 227             if value in [u'checked', u'unchecked']:
 228                 self.form_checkbox += [key]
 229         self.contenttype = u'application/pdf'
 230 
 231     def error_msg (self, msg):
 232         """Display error message."""
 233         if not self.errormsgsent:
 234             Page (self.request, self.pagename).send_page (self.request, msg=msg)
 235             self.errormsgsent = True
 236 
 237     def fixhtmlstr (self, str):
 238         """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
 239         htmlstr = u''
 240         for c in str:
 241             if ord(c) >= 128:
 242                 htmlstr = htmlstr + '&#%d;' % ord(c)
 243             else:
 244                 htmlstr = htmlstr + c
 245         return htmlstr
 246 
 247     def fixutf8latin1 (self, str):
 248         """Convert a UTF-8 string containing ISO-8859-1 encoding to a true ISO-8859-1 string.
 249         It's the weakness of MoinMoin 1.5 where pages are save as UTF-8 (multi byte) data
 250         containing ISO-8859-1 characters (sigle byte). Python run into trouble with this
 251         'currupted' UNICODE strings.
 252         Until be able to use the string within Python's functions we have to fix this problem.
 253         """
 254         return str.encode(config.charset).decode(self.values['charset'])#.encode(self.values['charset'])
 255 
 256     def set_page_values(self):
 257         """Scan raw page for additional information relating PDF generation."""
 258         #pdflines = False
 259         for line in self.request.page.get_raw_body().split(u'\n'):
 260             if line[:6] == u'##pdf ':
 261                 cols = line[6:].split()
 262                 # Only accept known values/settings.
 263                 if len(cols) > 1 and cols[0] in self.default_values:
 264                     self.values[cols[0]] = u' '.join(cols[1:])
 265                     #pdflines = True
 266                 continue
 267             # Stop parsing with first non-pdf line (if detected at least one).
 268             #elif pdflines and not line:
 269             #    break
 270 
 271     def set_page_default_values(self):
 272         # We are not able to recognise if this string is part of a verbatim area.
 273         matchtoclvl = re.compile(r'^\[\[TableOfContents\(\s*(\d+)\s*\)\]\]')
 274         matchtoc = re.compile(r'^\[\[TableOfContents\(*\)*\]\]')
 275         toc = False
 276         for line in self.request.page.get_raw_body().split(u'\n'):
 277             if line[:10] == u'#language ' and not u'language' in self.values:
 278                 lang = self.make_isolang(line[10:])
 279                 if lang:
 280                     self.default_values[u'language'] = lang
 281             elif not u'toclevels' in self.values and not toc:
 282                 result = matchtoclvl.match(line)
 283                 if result:
 284                     toclevels = int(result.group(1).strip())
 285                     if toclevels > 4:
 286                         toclevels = 4
 287                     self.default_values[u'toclevels'] = str(toclevels)
 288                     toc = True
 289                 elif matchtoc.match(line):
 290                     toc = True
 291         # We assume if table-of-contents is used we intent to generate a book.
 292         if toc:
 293             self.default_values[u'style'] = u'book'
 294         else:
 295             # Do not generate a table of contents page.
 296             self.default_values[u'no-toc'] = u'unchecked'
 297  
 298     def _select (self, name, description=None):
 299         """Helper function to create a selection control."""
 300         str = u'<select name="%s" size="1">' % (name,)
 301         if not description:
 302             description = self.valid_options[name]
 303         keys = description.keys()
 304         keys.sort()
 305         for value in keys:
 306             if value == self.values[name]:
 307                 selected = u'selected'
 308             else:
 309                 selected = u''
 310             str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
 311         str += u'</select>'
 312         return str
 313 
 314     def _chooseformat (self, name):
 315         """Helper function to create left/middle/right selection controls."""
 316         str = u"""    <tr>
 317         <td class="label"><label>%s&nbsp;:</label></td>
 318         <td><table>
 319                 <tr>
 320                     <td>%s&nbsp;:</td>
 321                     <td>%s</td>
 322                 </tr>
 323                 <tr>
 324                     <td>%s&nbsp;:</td>
 325                     <td>%s</td>
 326                 </tr>
 327                 <tr>
 328                     <td>%s&nbsp;:</td>
 329                     <td>%s</td>
 330                 </tr>
 331             </table>
 332         </td>
 333         <td>%s</td>
 334     </tr>""" % (self.fields[u'label_' + name],
 335               self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
 336               self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
 337               self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
 338               self.fields[u'help_' + name],)
 339         return str
 340 
 341     def makeform (self, errormsg=u''):
 342         self.fields = {
 343             'error': errormsg,
 344             'pagename': wikiutil.escape(self.pagename),
 345             'action': self.action_name,
 346             'seperator': u'<tr><td colspan="3"><hr/></td></tr>',
 347             'space': u'<tr><td colspan="3">&nbsp;</td></tr>',
 348 
 349             'label_input': self._(u'Input'),
 350             'label_output': self._(u'Output'),
 351             'label_page': self._(u'Page'),
 352             'label_tableofcontents': self._(u'Contents'),
 353             'label_pdf': self._(u'PDF'),
 354             'label_security': self._(u'Security'),
 355 
 356             'label_choose_style': self._(u'Choose style'),
 357             'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
 358                                  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/>' + \
 359                                  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.'),
 360 
 361             'help_titletext': self._(u'Title of the document for the front page.'),
 362 
 363             'label_format': self._(u'Output format'),
 364             'help_format': self._(u'Specifies the output format.'),
 365 
 366             'label_outputoptions': self._(u'Output options'),
 367             'label_grayscale': self._(u'Grayscale document'),
 368             'label_titlepage': self._(u'Title page'),
 369             'label_titletext': self._(u'Title'),
 370             'label_jpeg': self._(u'JPEG big images'),
 371             'label_compression': self._(u'Compression'),
 372 
 373             'label_no-toc': self._(u'Generate a table of contents'),
 374             'help_no-toc': self._(u''),
 375 
 376             'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
 377             'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
 378 
 379             'label_numbered': self._(u'Numbered headings'),
 380             'help_numbered': self._(u'Check to number all of the headings in the document.'),
 381 
 382             'label_toctitle': self._(u'Table-of-contents title'),
 383             'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
 384 
 385             'label_left': self._(u'Left'),
 386             'label_middle': self._(u'Middle'),
 387             'label_right': self._(u'Right'),
 388 
 389             'label_tocheader': self._(u'Header of table-of-contantes page'),
 390             'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
 391 
 392             'label_tocfooter': self._(u'Footer of table-of-contantes page'),
 393             'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
 394 
 395             'label_header': self._(u'Page header'),
 396             'help_header': self._(u'Sets the page header to use on body pages.'),
 397 
 398             'label_footer': self._(u'Page footer'),
 399             'help_footer': self._(u'Sets the page footer to use on body pages.'),
 400 
 401             'label_no-links': self._(u'Create HTTP links'),
 402             'help_no-links': self._(u'Enables generation of links in PDF files.'),
 403             
 404             'label_linkstyle': self._(u'Style of HTTP links'),
 405             'help_linkstyle': self._(u''),
 406 
 407             'label_linkcolor': self._(u'HTTP links color'),
 408             'help_linkcolor': self._(u'Sets the color of links.'),
 409 
 410             'label_duplex': self._(u'2-Sided'),
 411             'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
 412 
 413             'label_landscape': self._(u'Landscape'),
 414 
 415             'label_choose_size': self._(u'Choose page size'),
 416             'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
 417 
 418             'label_usersize': self._(u'User defined page size'),
 419             '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).'),
 420 
 421             'label_margin': self._(u'User defined margin'),
 422             'label_margintop': self._(u'Top'),
 423             'label_marginbottom': self._(u'Bottom'),
 424             'label_marginleft': self._(u'Left'),
 425             'label_marginright': self._(u'Right'),
 426             '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.'),
 427 
 428             'label_pagemode': self._(u'Page mode'),
 429             '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.'),
 430 
 431             'label_pagelayout': self._(u'Page layout'),
 432             '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.'),
 433 
 434             'label_firstpage': self._(u'First page'),
 435             'help_firstpage': self._(u'Choose the initial page that will be shown.'),
 436 
 437             'label_encryption': self._(u'Encryption'),
 438             'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
 439 
 440             'label_permissions': self._(u'Permissions'),
 441             'help_permissions': self._(u'Specifies the document permissions.'),
 442 
 443             'label_permissionannotate': self._(u'Annotate'),
 444             'label_permissionprint': self._(u'Print'),
 445             'label_permissionmodify': self._(u'Modify'),
 446             'label_permissioncopy': self._(u'Copy'),
 447 
 448             'label_owner-password': self._(u'Owner password'),
 449             '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.'),
 450 
 451             'label_user-password': self._(u'User password'),
 452             'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
 453 
 454             'label_expert': self._(u'Expert'),
 455             'label_language': self._(u'Language translation'),
 456             'help_language': self._(u'Specify language to use for date and time format.'),
 457 
 458             'label_charset': self._(u'Charset set'),
 459             'help_charset': self._(u'Change the encoding of the text in document.'),
 460 
 461             'button_generate': self._(u'Generate PDF'),
 462             'button_remember': self._(u'Remember form'),
 463             'button_cancel': self._(u'Cancel'),
 464             'button_reset': self._(u'Reset'),
 465             }
 466         self.fields.update(self.values)
 467 
 468         # Go through all format strings.
 469         for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
 470             self.fields[u'choose_' + name] = self._chooseformat(name)
 471 
 472         self.fields[u'select_style'] = self._select (u'style')
 473         self.fields[u'select_format'] = self._select (u'format')
 474         self.fields[u'select_linkstyle'] = self._select (u'linkstyle')
 475         self.fields[u'select_size'] = self._select (u'size')
 476         self.fields[u'select_jpeg'] = self._select (u'jpeg')
 477         self.fields[u'select_compression'] = self._select (u'compression')
 478         self.fields[u'select_toclevels'] = self._select (u'toclevels')
 479         self.fields[u'select_pagemode'] = self._select (u'pagemode')
 480         self.fields[u'select_pagelayout'] = self._select (u'pagelayout')
 481         self.fields[u'select_firstpage'] = self._select (u'firstpage')
 482         self.fields[u'select_charset'] = self._select (u'charset')
 483 
 484         form = """<p class="error">%(error)s</p>
 485 <form method="post" action="">
 486 <input type="hidden" name="action" value="%(action)s"/>
 487 <table>
 488     <tr><td colspan="3">%(label_input)s</td></tr>
 489     <tr>
 490         <td class="label"><label>%(label_choose_style)s&nbsp;:</label></td>
 491         <td class="content">%(select_style)s</td>
 492         <td>%(help_choose_style)s</td>
 493     </tr>
 494     <tr>
 495         <td class="label"><label>%(label_titletext)s&nbsp;:</label></td>
 496         <td class="content"><input type="text" size="30" name="titletext" value="%(titletext)s" /></td>
 497         <td>%(help_titletext)s</td>
 498     </tr>
 499     %(seperator)s
 500     <tr><td colspan="3">%(label_output)s</td></tr>
 501     <tr>
 502         <td class="label"><label>%(label_format)s&nbsp;:</label></td>
 503         <td class="content">%(select_format)s</td>
 504         <td>%(help_format)s</td>
 505     </tr>
 506     <tr>
 507         <td class="label"><label>%(label_outputoptions)s&nbsp;:</label></td>
 508         <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s&nbsp;
 509             <input type="checkbox" name="title" value="checked" %(title)s />%(label_titlepage)s<br />
 510             %(label_compression)s&nbsp:&nbsp;%(select_compression)s&nbsp;
 511             %(label_jpeg)s&nbsp;:&nbsp;%(select_jpeg)s</td>
 512     </tr>
 513     %(seperator)s
 514     <tr><td colspan="3">%(label_page)s</td></tr>
 515     <tr>
 516         <td class="label"><label>%(label_choose_size)s&nbsp;:</label></td>
 517         <td>%(select_size)s&nbsp;<br /><nobr>%(label_usersize)s&nbsp;:&nbsp;<input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
 518         <td>%(help_choose_size)s<br />%(help_usersize)s</td>
 519     </tr>
 520     <tr>
 521         <td>&nbsp;</td>
 522         <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s />&nbsp;%(label_duplex)s&nbsp;
 523             <input type="checkbox" name="landscape" value="checked" %(landscape)s />&nbsp;%(label_landscape)s</td>
 524     </tr>
 525     <tr>
 526         <td class="label"><label>%(label_margin)s&nbsp;:</label></td>
 527         <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>
 528             <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>
 529             <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>
 530         <td>%(help_margin)s</td>
 531     </tr>
 532     %(choose_header)s
 533     %(choose_footer)s
 534     %(seperator)s
 535     <tr><td colspan="3">%(label_tableofcontents)s</td></tr>
 536     <tr>
 537         <td class="label"><label>%(label_no-toc)s&nbsp;:</label></td>
 538         <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
 539         <td>%(help_no-toc)s</td>
 540     </tr>
 541     <tr>
 542         <td class="label"><label>%(label_toclevels)s&nbsp;:</label></td>
 543         <td class="content">%(select_toclevels)s</td>
 544         <td>%(help_toclevels)s</td>
 545     </tr>
 546     <tr>
 547         <td>&nbsp;</td>
 548         <td><input type="checkbox" name="numbered" value="checked" %(numbered)s />&nbsp;%(label_numbered)s</td>
 549         <td>%(help_numbered)s</td>
 550     </tr>
 551     <tr>
 552         <td class="label"><label>%(label_toctitle)s&nbsp;:</label></td>
 553         <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
 554         <td>%(help_toctitle)s</td>
 555     </tr>
 556     %(choose_tocheader)s
 557     %(choose_tocfooter)s
 558     %(seperator)s
 559     <tr><td colspan="3">%(label_pdf)s</td></tr>
 560     <tr>
 561         <td class="label"><label>%(label_pagemode)s&nbsp;:</label></td>
 562         <td class="content">%(select_pagemode)s</td>
 563         <td>%(help_pagemode)s</td>
 564     </tr>
 565     <tr>
 566         <td class="label"><label>%(label_pagelayout)s&nbsp;:</label></td>
 567         <td class="content">%(select_pagelayout)s</td>
 568         <td>%(help_pagelayout)s</td>
 569     </tr>
 570     <tr>
 571         <td class="label"><label>%(label_firstpage)s&nbsp;:</label></td>
 572         <td class="content">%(select_firstpage)s</td>
 573         <td>%(help_firstpage)s</td>
 574     </tr>
 575     <tr>
 576         <td class="label"><label>%(label_no-links)s&nbsp;:</label></td>
 577         <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
 578         <td>%(help_no-links)s</td>
 579     </tr>
 580     <tr>
 581         <td class="label"><label>%(label_linkstyle)s&nbsp;:</label></td>
 582         <td class="content">%(select_linkstyle)s</td>
 583         <td>%(help_linkstyle)s</td>
 584     </tr>
 585     <tr>
 586         <td class="label"><label>%(label_linkcolor)s&nbsp;:</label></td>
 587         <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
 588         <td>%(help_linkcolor)s</td>
 589     </tr>
 590     %(seperator)s
 591     <tr><td colspan="3">%(label_security)s</td></tr>
 592     <tr>
 593         <td class="label"><label>%(label_encryption)s&nbsp;:</label></td>
 594         <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
 595         <td>%(help_numbered)s</td>
 596     </tr>
 597     <tr>
 598         <td class="label"><label>%(label_permissions)s&nbsp;:</label></td>
 599         <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s />&nbsp;%(label_permissionprint)s</nobr>&nbsp;
 600             <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s />&nbsp;%(label_permissionmodify)s</nobr><br />
 601             <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s />&nbsp;%(label_permissioncopy)s</nobr>&nbsp;
 602             <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s />&nbsp;%(label_permissionannotate)s</nobr></td>
 603         <td>%(help_permissions)s</td>
 604     </tr>
 605     <tr>
 606         <td class="label"><label>%(label_user-password)s&nbsp;:</label></td>
 607         <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
 608         <td>%(help_user-password)s</td>
 609     </tr>
 610     <tr>
 611         <td class="label"><label>%(label_owner-password)s&nbsp;:</label></td>
 612         <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
 613         <td>%(help_owner-password)s</td>
 614     </tr>
 615     %(seperator)s
 616     <tr><td colspan="3">%(label_expert)s</td></tr>
 617     <tr>
 618         <td class="label"><label>%(label_language)s&nbsp;:</label></td>
 619         <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
 620         <td>%(help_language)s</td>
 621     </tr>
 622     <tr>
 623         <td class="label"><label>%(label_charset)s&nbsp;:</label></td>
 624         <td class="content">%(select_charset)s</td>
 625         <td>%(help_charset)s</td>
 626     </tr>
 627     %(space)s
 628     <tr>
 629         <td><input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" /></td>
 630         <td class="buttons" colspan="2">
 631             <input type="submit" name="generate_from_form" value="%(button_generate)s" />&nbsp;
 632             <input type="submit" name="remember" value="%(button_remember)s" />&nbsp;
 633             <input type="submit" name="cancel" value="%(button_cancel)s" />
 634         </td>
 635     </tr>
 636 </table>
 637 </form>""" % self.fields
 638         return Dialog (self.request, content=form)
 639 
 640     def run (self, pagename, request):
 641         """ Main dispatcher for the action."""
 642         self.pagename = pagename
 643         self.request = request
 644         self._ = self.request.getText
 645         self.form = self.request.form
 646         self.cfg = self.request.cfg
 647 
 648         # Canceled by user.
 649         if self.form.has_key(u'cancel'):
 650             return self.request.page.send_page(self.request)
 651 
 652         # Determine calling parameters.
 653         if self.form.get(u'debug', [u'0']) == [u'1']:
 654             self.debug = True
 655 
 656         # This dict contains all possible values.
 657         self.valid_options = {}
 658 
 659         self.valid_options[u'tocformats'] = {
 660             u'/': self._(u'1/N,2/N Arabic page numbers'),
 661             u':': self._(u'1/C,2/C Arabic chapter page numbers'),
 662             u'1': self._(u'1,2,3,...'),
 663             u'a': self._(u'a,b,c,...'),
 664             u'A': self._(u'A,B,C,...'),
 665             u'c': self._(u'Chapter title'),
 666             u'C': self._(u'Chapter page number'),
 667             u'd': self._(u'Date'),
 668             u'D': self._(u'Date + Time'),
 669             u'h': self._(u'Heading'),
 670             u'i': self._(u'i,ii,iii,iv,...'),
 671             u'I': self._(u'I,II,III,IV,...'),
 672             u't': self._(u'Title'),
 673             u'T': self._(u'Time'),
 674             u'.': self._(u'Blank'),
 675             # TODO: Not supported yet; u'l': self._(u'Logo image'),
 676         }
 677         self.valid_options[u'style'] = {
 678             u'webpage': self._(u'webpage'),
 679             u'book': self._(u'book'),
 680             u'continuous': self._(u'continuous'),
 681         }
 682         self.valid_options[u'size'] = {
 683             u'legal': self._(u'Legal (8.5x14in)'),
 684             u'a4': self._(u'A4 (210x297mm)'),
 685             u'letter': self._(u'Letter (8.5x11in)'),
 686             u'universal': self._(u'Universal (8.27x11in)'),
 687             u'': self._(u'User defined'),
 688         }
 689         self.valid_options[u'format'] = {
 690             u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
 691             u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
 692             u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
 693             u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
 694             # TODO: Not supported yet:
 695             #u'ps1': self._(u'PostScript Level 1'),
 696             #u'ps2': self._(u'PostScript Level 2'),
 697             #u'ps3': self._(u'PostScript Level 3'),
 698         }
 699         self.valid_options[u'linkstyle'] = {
 700             u'underline': self._(u'Underline'),
 701             u'plain': self._(u'Plain'),
 702         }
 703         self.valid_options[u'firstpage'] = {
 704             u'c1': self._(u'1st chapter'),
 705             u'p1': self._(u'1st page'),
 706             u'toc': self._(u'Contents'),
 707         }
 708         self.valid_options[u'jpeg'] = {
 709             u'0': self._(u'None'),
 710             u'50': self._(u' 50% (Good)'),
 711             u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
 712             u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
 713             u'100': self._(u'100% (Best)'),
 714         }
 715         self.valid_options[u'compression'] = {
 716             u'0': self._(u'None'),
 717             u'1': self._(u'1 (Fast)'),
 718             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',
 719             u'9': self._(u'9 (Best)'),
 720         }
 721         self.valid_options[u'toclevels'] = {
 722             u'0': self._(u'None'),
 723             u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
 724         }
 725         self.valid_options[u'pagemode'] = {
 726             u'outline': self._(u'Outline'),
 727             u'document': self._(u'Document'),
 728             u'fullscreen': self._(u'Full-screen'),
 729         }
 730         self.valid_options[u'pagelayout'] = {
 731             u'single': self._(u'Single'),
 732             u'one': self._(u'One column'),
 733             u'twoleft': self._(u'Two column left'),
 734             u'tworight': self._(u'Two column right'),
 735         }
 736         self.valid_options[u'charset'] = {
 737             u'iso-8859-1': self._(u'ISO 8859-1'),
 738             u'iso-8859-2': self._(u'ISO 8859-2'),
 739             u'iso-8859-3': self._(u'ISO 8859-3'),
 740             u'iso-8859-4': self._(u'ISO 8859-4'),
 741             u'iso-8859-5': self._(u'ISO 8859-5'),
 742             u'iso-8859-6': self._(u'ISO 8859-6'),
 743             u'iso-8859-7': self._(u'ISO 8859-7'),
 744             u'iso-8859-8': self._(u'ISO 8859-8'),
 745             u'iso-8859-9': self._(u'ISO 8859-9'),
 746             u'iso-8859-14': self._(u'ISO 8859-14'),
 747             u'iso-8859-15': self._(u'ISO 8859-15'),
 748             u'cp-874': self._(u'cp-847'),
 749             u'cp-1250': self._(u'cp-1250'),
 750             u'cp-1251': self._(u'cp-1251'),
 751             u'cp-1252': self._(u'cp-1252'),
 752             u'cp-1253': self._(u'cp-1253'),
 753             u'cp-1254': self._(u'cp-1254'),
 754             u'cp-1255': self._(u'cp-1255'),
 755             u'cp-1256': self._(u'cp-1256'),
 756             u'cp-1257': self._(u'cp-1257'),
 757             u'cp-1258': self._(u'cp-1258'),
 758             u'koi-8r': self._(u'koi-8r'),
 759         }
 760 
 761         # Set translated name of table of contents as default.
 762         self.default_values[u'toctitle'] = self._(u'Contents')
 763 
 764         self.default_values[u'titletext'] = self.pagename
 765 
 766         # Make sure we create date and time strings in right format.
 767         if self.request.page.language:
 768             self.default_values[u'language'] = self.request.page.language
 769         else:
 770             self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get (u'default_language', u'en'))
 771 
 772         self.values = {}
 773 
 774         # If the configuration variable 'createpdfdocument_validoptions' exists we update our
 775         # self.valid_options dict with these values.
 776         if getattr (self.request.cfg, u'createpdfdocument_validoptions', None):
 777             self.valid_options.update (self.request.cfg.createpdfdocument_validoptions)
 778 
 779         # If the configuration variable 'createpdfdocument_defaultvalues' exists we update our
 780         # self.default_values dict with these values.
 781         if getattr (self.request.cfg, u'createpdfdocument_defaultvalues', None):
 782             for key, value in self.request.cfg.createpdfdocument_defaultvalues.items():
 783                 self.default_values[key] = value
 784 
 785         # Scan page to extract default values.
 786         self.set_page_default_values()
 787 
 788         # Create a PDF document direct without any user iteraction from default and page settings.
 789         if self.form.has_key(u'generate'):
 790             self.set_page_values()
 791             self.update_values(useform=False)
 792             return self.do_generate()
 793 
 794         # Create a PDF document from form settings.
 795         if self.form.has_key(u'generate_from_form'):
 796             self.update_values()
 797             return self.do_generate()
 798 
 799         if self.form.has_key(u'remember'):
 800             self.update_values()
 801             return self.do_remember()
 802 
 803         self.set_page_values()
 804         self.update_values(useform=False)
 805         return self.request.page.send_page (self.request, msg=self.makeform())
 806 
 807     def update_values(self, useform=True):
 808         """Preset values with they form values or defaults."""
 809         for key, default in self.default_values.items():
 810             # Modify value only if not already set.
 811             if not key in self.values:
 812                 # If the form does not contain the value (e.g. for checkboxes) set the
 813                 # default value (e.g. for checkboxes unset by default).
 814                 if not key in self.form:
 815                     # Special processing for checkboxes in forms. If the key does not exists
 816                     # within the form it is not checked.
 817                     if key in self.form_checkbox and useform:
 818                         self.values[key] = u'unchecked'
 819                     else:
 820                         self.values[key] = default
 821                 else:
 822                     self.values[key] = self.form[key][0]
 823         # Check if revision is an integer value.
 824         try:
 825             self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
 826         except:
 827             self.values[u'rev'] = self.request.page.rev
 828         # Check if page revision exists.
 829         (pagefname, realrev, exists) = self.request.page.get_rev (rev=self.values[u'rev'])
 830         if exists:
 831             self.values[u'rev'] = realrev
 832         else:
 833             # Determine latest revision number.
 834             (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
 835 
 836     def do_generate(self):
 837         """Create PDF document."""
 838         # Generate the HTML page using MoinMoin wiki engine.
 839         html = self.get_html()
 840         if html:
 841             if self.debug:
 842                 self.request.http_headers()
 843                 self.request.write(html)
 844             else:
 845                 pdfdata = self.html2pdf(html)
 846                 if pdfdata:
 847                     # Send as application/pdf the generated file by HTMLDOC
 848                     self.send_pdf(pdfdata)
 849                     
 850                     # MoinMoin1.6: send_closing_html() has to be called explicit.
 851                     # MoinMoin1.5: raise MoinMoinNoFooter exception to forbit creation of HTML footer.
 852                     if not 'send_closing_html' in self.request.theme.__dict__:
 853                         from MoinMoin.util import MoinMoinNoFooter
 854                         raise MoinMoinNoFooter
 855 
 856     def do_remember(self):
 857         """Create a message containing information about how to save the form values for future reuse."""
 858         save = u''
 859         for key, value in self.values.items():
 860             if key in [u'user-password', u'owner-password', u'rev', u'debug']:
 861                 continue
 862             if key in self.default_values and value == self.default_values[key]:
 863                 continue
 864             save += u'##pdf %s %s\n' % (key, value,)
 865         if save:
 866             msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
 867         else:
 868             msg = self._(u'All values correspond to they default. Nothing have to be saved.')
 869         return self.request.page.send_page (self.request, msg)
 870 
 871     def send_pdf (self, data):
 872         filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
 873 
 874         # Send HTTP header.
 875         self.request.http_headers([
 876             'Content-Type: %s' % self.contenttype,
 877             'Content-Length: %d' % len(data),
 878             # TODO: fix the encoding here, plain 8 bit is not allowed
 879             # according to the RFCs There is no solution that is
 880             # compatible to IE except stripping non-ascii chars
 881             'Content-Disposition: inline; filename="%s"' % filename.encode(config.charset),
 882             ])
 883 
 884         # Send binary data.
 885         sio = StringIO.StringIO(data)
 886         shutil.copyfileobj (sio, self.request, 8192)
 887         
 888     def get_html(self):
 889         """Generate the HTML body of this page."""
 890         # Save page as HTML.
 891         newreq = RedirectOutputRequest(self.request)
 892         (html, errmsg) = newreq.run(rev = self.values.get(u'rev', None))
 893         if html:
 894             html = self.fixhtmlstr(html)
 895             # Make URLs absolute.
 896             # FIXME: Until MoinMoin is not XHTML compilant we can not use a XML parser
 897             # (e.g. expat) to transform the HTML document. In the meantime we try to
 898             # achive the same with regular expressions subtitution.
 899             base = self.request.getQualifiedURL()
 900             for htmlref in [u'src', u'href']:
 901                 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
 902                 urlref = re.compile (reurlref, re.I)
 903                 for match in urlref.finditer(html):
 904                     foundref = match.groups()
 905                     html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
 906             # Rename title of the document.
 907             titletext_html = self.fixhtmlstr(wikiutil.escape(self.values['titletext']))
 908             html = re.compile(r'<title>[^<]+</title>').sub(u'<title>%s</title>' % titletext_html, html)
 909         else:
 910             self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
 911 
 912         return html
 913 
 914     def make_isolang (self, language):
 915         return language + u'_' + language.upper()
 916 
 917     def html2pdf (self, html):
 918         """Create a PDF document based on the current parameters."""
 919         # Not all os support environment variable definition in one line with the calling application.
 920         if os.name in ['posix', 'mac']:
 921             htmldocopts = [u'LANG=' + self.values[u'language'], u'HTMLDOC_NOCGI=1']
 922         else:
 923             htmldocopts = []
 924         # Determine UID to access ACL protected sites too (mandatory to download attached images).
 925         htmldocopts += [u'htmldoc', "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
 926 
 927         for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
 928             self.values[key] = self.values.get (key + u'left', u'.') + self.values.get (key + u'middle', u'.') + self.values.get (key + u'right', u'.')
 929 
 930         permissions = []
 931         for opt, value in self.values.items():
 932             if opt in [u'language', u'debug', u'rev', u'titletext']:
 933                 continue
 934             value = value.strip()
 935             if not value:
 936                 continue
 937             # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
 938             if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
 939                 continue
 940             if opt in [u'style']:
 941                 htmldocopts += [u'--' + value]
 942             elif opt in self.form_checkbox:
 943                 if value == u'checked':
 944                     if opt[:10] == u'permission':
 945                         permissions += [opt[10:]]
 946                     # Reverse meaning of 'no-' options.
 947                     elif opt[:3] != u'no-':
 948                         htmldocopts += [u'--' + opt]
 949                 elif opt[:3] == u'no-':
 950                     htmldocopts += [u'--' + opt]
 951             elif opt[:6] == u'margin':
 952                 htmldocopts += [u'--' + opt[6:], value]
 953             else:
 954                 htmldocopts += [u'--' + opt, value]
 955         if permissions:
 956             htmldocopts += [u'--permission', u','.join (permissions)]
 957         htmldocopts += [u'-']
 958         # Do not forget to escape all spaces!
 959         eschtmldocopts = [arg.replace(u' ', u'\\ ') for arg in htmldocopts]
 960         cmdstr = u' '.join(eschtmldocopts)
 961         errmsg = None
 962 
 963         pdf = None
 964         if self.debug:
 965             errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>'
 966         else:
 967             inp, out, err = os.popen3 (cmdstr, u'b')
 968             try:
 969                 inp.write(html)
 970                 inp.close()
 971             except:
 972                 pass
 973 
 974             pdf = out.read()
 975             out.close()
 976 
 977             htmldocmsg = err.read()
 978             err.close()
 979 
 980             if os.name in ['posix', 'mac']:
 981                 try:
 982                     # REMARK: Otherwise we get <defunct> processes.
 983                     os.wait()
 984                 except OSError, e:
 985                     # 10: No child processes.
 986                     if e.errno != 10:
 987                         raise
 988 
 989             # Check for error message on STDOUT.
 990             if pdf[:8] == u'HTMLDOC ':
 991                 htmldocmsg += pdf
 992                 pdf = None
 993 
 994             if htmldocmsg:
 995                 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
 996                       wikiutil.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
 997 
 998         # As it is difficult to get the htmldoc return code, we check for
 999         # error by checking the produced pdf length
1000         if not pdf and errmsg:
1001             self.error_msg(errmsg)
1002             return None
1003         elif pdf[:4] != '%PDF':
1004             self.error_msg(self._(u'Invalid PDF document generated') + wikiutil.escape(pdf[:80]))
1005             return None
1006 
1007         return pdf
1008 
1009 
1010 def execute (pagename, request):
1011     try:
1012         CreatePdfDocument().run (pagename = pagename, request = request)
1013     except:
1014         raise
1015 
1016 # vim:ts=4:et:sw=4:nu

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2011-04-14 07:28:03, 4.7 KB) [[attachment:CreateNewPage.py]]
  • [get | view] (2011-04-14 07:26:24, 4.2 KB) [[attachment:CreateNewPage1.6.py]]
  • [get | view] (2006-09-10 21:29:29, 40.4 KB) [[attachment:CreatePdfDocument2_0_3.py]]
  • [get | view] (2006-09-12 06:05:06, 40.5 KB) [[attachment:CreatePdfDocument2_0_4.py]]
  • [get | view] (2006-09-12 12:00:09, 40.6 KB) [[attachment:CreatePdfDocument2_0_5.py]]
  • [get | view] (2006-11-14 21:56:11, 43.5 KB) [[attachment:CreatePdfDocument2_0_6.py]]
  • [get | view] (2006-11-15 17:00:47, 43.8 KB) [[attachment:CreatePdfDocument2_0_7.py]]
  • [get | view] (2006-11-16 22:06:18, 43.8 KB) [[attachment:CreatePdfDocument2_0_8.py]]
  • [get | view] (2006-12-17 15:54:21, 43.6 KB) [[attachment:CreatePdfDocument2_0_9.py]]
  • [get | view] (2007-08-20 09:10:23, 67.2 KB) [[attachment:CreatePdfDocument2_1_0.py]]
  • [get | view] (2007-08-21 07:39:49, 67.1 KB) [[attachment:CreatePdfDocument2_1_1.py]]
  • [get | view] (2007-09-11 19:16:37, 67.3 KB) [[attachment:CreatePdfDocument2_1_2.py]]
  • [get | view] (2007-09-18 20:17:58, 68.1 KB) [[attachment:CreatePdfDocument2_1_3.py]]
  • [get | view] (2007-09-21 13:32:54, 71.1 KB) [[attachment:CreatePdfDocument2_1_4.py]]
  • [get | view] (2007-09-23 20:56:30, 73.4 KB) [[attachment:CreatePdfDocument2_1_5.py]]
  • [get | view] (2007-09-25 20:54:48, 74.5 KB) [[attachment:CreatePdfDocument2_2_0.py]]
  • [get | view] (2008-06-23 21:08:49, 77.0 KB) [[attachment:CreatePdfDocument2_3_0.py]]
  • [get | view] (2008-06-26 19:25:07, 81.0 KB) [[attachment:CreatePdfDocument2_3_1.py]]
  • [get | view] (2008-07-06 05:50:38, 83.1 KB) [[attachment:CreatePdfDocument2_3_2.py]]
  • [get | view] (2008-07-09 17:42:02, 83.3 KB) [[attachment:CreatePdfDocument2_3_3.py]]
  • [get | view] (2008-09-07 11:11:01, 83.5 KB) [[attachment:CreatePdfDocument2_3_4.py]]
  • [get | view] (2009-01-11 15:53:09, 84.3 KB) [[attachment:CreatePdfDocument2_3_5.py]]
  • [get | view] (2009-02-16 06:52:06, 84.2 KB) [[attachment:CreatePdfDocument2_3_6.py]]
  • [get | view] (2010-01-29 11:53:21, 82.8 KB) [[attachment:CreatePdfDocument2_4_0.py]]
  • [get | view] (2010-01-31 14:10:03, 84.6 KB) [[attachment:CreatePdfDocument2_4_1.py]]
  • [get | view] (2010-09-18 16:23:23, 85.6 KB) [[attachment:CreatePdfDocument2_4_2.py]]
  • [get | view] (2006-06-16 20:56:53, 4.9 KB) [[attachment:FlashManager.py-1.5.3-1]]
  • [get | view] (2003-12-07 18:15:53, 3.9 KB) [[attachment:HTML2MoinMoin.py]]
  • [get | view] (2005-10-16 08:24:35, 4.9 KB) [[attachment:HelpOn-1.3.5-4.py]]
  • [get | view] (2006-02-03 19:21:04, 4.9 KB) [[attachment:HelpOn-1.5.1-5.py]]
  • [get | view] (2006-07-04 10:45:22, 4.8 KB) [[attachment:HelpOn-1.5.4-6.py]]
  • [get | view] (2006-07-04 22:39:14, 4.8 KB) [[attachment:HelpOn-1.6.0-7.py]]
  • [get | view] (2006-07-06 13:50:17, 4.0 KB) [[attachment:HelpOn-1.6.0-8.py]]
  • [get | view] (2008-01-10 17:43:24, 4.8 KB) [[attachment:HelpOn-1.6.0-9.py]]
  • [get | view] (2008-08-19 14:44:54, 5.0 KB) [[attachment:HelpOn-1.7.1-10.py]]
  • [get | view] (2005-02-20 18:28:34, 10.8 KB) [[attachment:IRSS.py]]
  • [get | view] (2005-03-09 22:46:23, 2.9 KB) [[attachment:ImportHtml-1.2.py]]
  • [get | view] (2003-12-07 18:15:53, 2.8 KB) [[attachment:ImportHtml.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:IrcChat.py]]
  • [get | view] (2008-06-09 11:27:20, 4.4 KB) [[attachment:MoinCrypt.py]]
  • [get | view] (2010-11-29 12:08:27, 7.5 KB) [[attachment:PageActions.py]]
  • [get | view] (2006-08-07 15:12:19, 0.5 KB) [[attachment:PermanentLink.py]]
  • [get | view] (2003-12-07 18:15:53, 6.3 KB) [[attachment:PhoneDial.py]]
  • [get | view] (2005-04-17 14:21:47, 3.6 KB) [[attachment:RecommendPage-1.3.4-1.py]]
  • [get | view] (2005-04-19 18:21:52, 5.5 KB) [[attachment:RecommendPage-1.3.4-2.py]]
  • [get | view] (2005-05-02 19:53:09, 5.6 KB) [[attachment:RecommendPage-1.3.4-3.py]]
  • [get | view] (2005-09-03 07:33:35, 6.3 KB) [[attachment:RecommendPage-1.3.4-4.py]]
  • [get | view] (2005-09-05 17:44:03, 6.9 KB) [[attachment:RecommendPage-1.3.5-5.py]]
  • [get | view] (2005-09-07 16:42:26, 7.5 KB) [[attachment:RecommendPage-1.3.5-6.py]]
  • [get | view] (2005-09-08 16:06:28, 7.7 KB) [[attachment:RecommendPage-1.3.5-7.py]]
  • [get | view] (2005-11-01 11:31:51, 9.0 KB) [[attachment:RecommendPage-1.3.5-8.py]]
  • [get | view] (2006-02-03 19:40:51, 8.3 KB) [[attachment:RecommendPage-1.5.1-9.py]]
  • [get | view] (2008-01-11 09:14:35, 6.8 KB) [[attachment:RecommendPage-1.6.0-10.py]]
  • [get | view] (2008-08-19 14:44:59, 6.9 KB) [[attachment:RecommendPage-1.7.1-11.py]]
  • [get | view] (2008-06-09 11:27:40, 1.7 KB) [[attachment:ShowActions.py]]
  • [get | view] (2008-06-09 10:34:02, 5.3 KB) [[attachment:ShowDecrypted.py]]
  • [get | view] (2005-03-30 21:17:28, 7.7 KB) [[attachment:Slideshow.py]]
  • [get | view] (2004-02-02 20:48:31, 2.0 KB) [[attachment:SubscribeUser.py]]
  • [get | view] (2007-01-26 17:08:30, 2.2 KB) [[attachment:Subscribers-1.6.0.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:Subscribers.py]]
  • [get | view] (2006-03-18 23:16:51, 0.8 KB) [[attachment:UserPreferences.py]]
  • [get | view] (2004-01-05 09:56:25, 8.1 KB) [[attachment:VisualSiteMap.py]]
  • [get | view] (2015-08-30 21:04:23, 11.1 KB) [[attachment:VisualSiteMap_1.10.py]]
  • [get | view] (2004-10-08 10:59:16, 9.3 KB) [[attachment:VisualSiteMap_1.2.py]]
  • [get | view] (2005-03-16 01:30:09, 9.8 KB) [[attachment:VisualSiteMap_1.3.py]]
  • [get | view] (2014-08-19 01:34:10, 10.8 KB) [[attachment:VisualSiteMap_1.9.py]]
  • [get | view] (2007-08-18 18:52:55, 1.0 KB) [[attachment:backlink.py]]
  • [get | view] (2007-03-15 05:53:49, 23.5 KB) [[attachment:findandreplace0.1Beta.py]]
  • [get | view] (2005-03-27 20:32:10, 3.6 KB) [[attachment:gallery2image-1.3.3-1.py]]
  • [get | view] (2005-08-03 20:14:56, 4.0 KB) [[attachment:gallery2image-1.3.3-2.py]]
  • [get | view] (2005-11-13 18:10:26, 20.7 KB) [[attachment:gallery2image-1.3.5-10.py]]
  • [get | view] (2005-11-25 22:03:50, 20.8 KB) [[attachment:gallery2image-1.3.5-11.py]]
  • [get | view] (2005-08-08 17:23:43, 8.4 KB) [[attachment:gallery2image-1.3.5-4.py]]
  • [get | view] (2005-08-13 15:15:45, 13.7 KB) [[attachment:gallery2image-1.3.5-5.py]]
  • [get | view] (2005-08-31 22:05:22, 15.5 KB) [[attachment:gallery2image-1.3.5-6.py]]
  • [get | view] (2005-10-29 20:23:50, 15.9 KB) [[attachment:gallery2image-1.3.5-8.py]]
  • [get | view] (2005-11-01 11:31:24, 17.6 KB) [[attachment:gallery2image-1.3.5-9.py]]
  • [get | view] (2006-01-27 20:52:32, 20.9 KB) [[attachment:gallery2image-1.5.1-12.py]]
  • [get | view] (2006-08-06 09:01:01, 22.1 KB) [[attachment:gallery2image-1.5.4-13.py]]
  • [get | view] (2006-08-11 18:21:40, 22.2 KB) [[attachment:gallery2image-1.5.4-14.py]]
  • [get | view] (2006-11-16 20:23:27, 22.6 KB) [[attachment:gallery2image-1.5.6-16.py]]
  • [get | view] (2006-08-11 18:30:22, 22.2 KB) [[attachment:gallery2image-1.6.0-15.py]]
  • [get | view] (2008-02-06 10:13:45, 22.3 KB) [[attachment:gallery2image-1.6.0-16.py]]
  • [get | view] (2008-05-20 15:51:09, 22.4 KB) [[attachment:gallery2image-1.6.3-17.py]]
  • [get | view] (2006-09-06 06:19:48, 1.3 KB) [[attachment:getmmap.py]]
  • [get | view] (2004-07-18 09:48:00, 1.5 KB) [[attachment:localnames.py]]
  • [get | view] (2005-03-25 15:02:31, 2.6 KB) [[attachment:newpageonly.py]]
  • [get | view] (2005-03-30 09:02:00, 3.5 KB) [[attachment:newpageonly_20050330.py]]
  • [get | view] (2006-06-06 19:12:27, 9.7 KB) [[attachment:pdf.py]]
  • [get | view] (2006-08-30 10:51:51, 36.0 KB) [[attachment:pdf2_0_0.py]]
  • [get | view] (2006-08-30 13:57:36, 36.5 KB) [[attachment:pdf2_0_1.py]]
  • [get | view] (2006-02-04 04:25:29, 1.0 KB) [[attachment:sisterindex.py]]
  • [get | view] (2004-10-28 07:33:10, 0.7 KB) [[attachment:xml.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.