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