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