Attachment 'CreatePdfDocument2_0_7.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-15  RaphaelBossek
  21     * Release v2.0.7
  22     * Fixed support for MoinMoin 1.5.3
  23     * Fixed SSL support.
  24 
  25     2006-11-14  RaphaelBossek
  26     * Release v2.0.6
  27     * MoinMoin 1.6 support added.
  28     * Fixed Windows(TM) platform support.
  29     * Added support for alternative title text.
  30 
  31     2006-09-13  RaphaelBossek
  32     * Release v2.0.5
  33     * Fixed RedirectOutputRequest class where definition of script_name
  34       was missing.
  35 
  36     2006-09-12  RaphaelBossek
  37     * Release v2.0.4
  38     * Fixed RedirectOutputRequest class where function was redifined
  39       to boolean value.
  40 
  41     2006-09-06  RaphaelBossek
  42     * Release v2.0.3
  43     * Fixed FastCGI support by removing stdout redirect output code. The
  44       same functionality will be done by the RedirectOutputRequest class.
  45     * Renamed to CreatePdfDocument (for better translation possibilities).
  46     * Fixed encoding of title page.
  47     * Added charset set option.
  48     * Fixed waiting for HTMLDOC.
  49 
  50     2006-09-02  RaphaelBossek
  51     * Release v2.0.2
  52     * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
  53       configuration parameter support. You are not able to preset available
  54       options and which defaults are used in your configuration file.
  55 
  56     2006-08-30  RaphaelBossek
  57     * Release v2.0.1
  58     * Fixed issue with page revision number forwarding (traceback).
  59 
  60     2006-08-30  RaphaelBossek
  61     * Release v2.0.0
  62     * Feature enchanced and bug fixed version.
  63 
  64     2006-05-26  PascalBauermeister
  65     * Release v1.0.2
  66     * Relative image URLs turned absolute was bogus. It is less bogus now.
  67 
  68     2006-05-26  PascalBauermeister
  69     * Release v1.0.1
  70     * Set env var HTMLDOC_NOCGI to solve CGI issue
  71 
  72     2006-05-24  PascalBauermeister
  73     * Initial release v1.0.0
  74 """
  75 
  76 import os
  77 import sys
  78 import stat
  79 import re
  80 import copy
  81 import shutil
  82 import StringIO
  83 from MoinMoin import config
  84 from MoinMoin import util
  85 from MoinMoin import wikiutil
  86 from MoinMoin import packages
  87 from MoinMoin import error
  88 from MoinMoin.Page import Page
  89 from MoinMoin.request import RequestBase
  90 from MoinMoin.widget.dialog import Dialog
  91 
  92 
  93 class RedirectOutputRequest(RequestBase):
  94     """Redirect output to string without HTTP headers."""
  95     def __init__ (self, req, action=u'print'):
  96         """Prepare a compatible request."""
  97         # 1.5,1.6:req.path_info = u'/RaphaelBossek/\xd6nologe' | u'/FrontPage'
  98         # after enconde() self.path_info = '/RaphaelBossek/\xd6nologe'
  99         self.path_info = req.path_info.encode(config.charset)
 100         # 1.5,1.6:query_string = req.query_string = u'action=CreatePdfDocument'
 101         self.query_string = u'action=' + action
 102         # 1.5,1.6:request_uri = req.request_uri = u'/FrontPage?action=CreatePdfDocument'
 103         self.request_uri = req.path_info.replace(req.query_string, self.query_string)
 104         # 1.5,1.6:url = req.url = u'localhost:8080/FrontPage?action=CreatePdfDocument'
 105         self.url = req.url.replace(req.query_string, self.query_string)
 106         # 1.5,1.6: Not all kinds of request support SSL yet.
 107         if 'is_ssl' in req.__dict__:
 108             self.is_ssl = req.is_ssl
 109         else:
 110             self.is_ssl = 0
 111 
 112         self.http_user_agent = req.http_user_agent
 113         self.request_method = None
 114         self.saved_cookie = req.saved_cookie
 115         self.remote_addr = req.remote_addr
 116         self.script_name = getattr (req, u'script_name', u'')
 117 
 118         self.req = req
 119         RequestBase.__init__(self)
 120 
 121     def run(self, rev = None):
 122         """Start processing the document."""
 123         # 1.6:MoinMoin/request/__init__.py: Initialise action from environment variables not from form.
 124         self.action = u'print'
 125         if rev:
 126             self.form[u'rev'] = [rev]
 127         self.output_string = u''
 128         self.error_string = u''
 129         self.sent_headers = False
 130         self.failed = 0
 131         RequestBase.run(self)
 132         return (self.output_string, self.error_string)
 133 
 134     def http_headers (self, *args, **kw):
 135         """Used by MoinMoin 1.5.x instead of emit_http_headers()."""
 136         self.sent_headers = True
 137     
 138     def emit_http_headers(self, *args, **kw):
 139         """Used by MoinMoin 1.6.x instaed of http_headers()."""
 140         self.sent_headers = 1
 141 
 142     def fail (self, err):
 143         """Catch if a error occurs and save the message in our string."""
 144         RequestBase.fail (self, err)
 145         if not self.error_string:
 146             self.error_string = str(err)
 147 
 148     def write (self, *data):
 149         """Catch the document in our output_string."""
 150         if self.sent_headers:
 151             if self.failed:
 152                 self.error_string += data[0]
 153             else:
 154                 self.output_string += data[0]
 155 
 156     def flush(self):
 157         pass
 158 
 159 
 160 class CreatePdfDocument:
 161     """Implementation of the PDF document generator."""
 162 
 163     version = u'2.0.6'
 164 
 165     def __init__(self):
 166         self.action_name = self.__class__.__name__
 167         self.pagename = None
 168         self.request = None
 169         self._ = None
 170         self.debug = False
 171         self.msg = None
 172         self.errormsgsent = False
 173         self.default_values = {
 174             'style': u'webpage',
 175             'format': u'pdf13',
 176             'linkstyle': u'underline',
 177             'headerleft': u't',
 178             'headermiddle': u'.',
 179             'headerright': u'D',
 180             'footerleft': u'.',
 181             'footermiddle': u'/',
 182             'footerright': u'.',
 183             'tocheaderleft': u'.',
 184             'tocheadermiddle': u't',
 185             'tocheaderright': u'.',
 186             'tocfooterleft': u'.',
 187             'tocfootermiddle': u'.',
 188             'tocfooterright': u'i',
 189             'linkcolor': u'0000E0',
 190             'size': u'legal',
 191             'user-password': u'',
 192             'owner-password': u'',
 193             'toclevels': u'3',
 194             'grayscale': u'unchecked',
 195             'title': u'checked',
 196             'duplex': u'unchecked',
 197             'landscape': u'unchecked',
 198             'usersize': u'',
 199             'margintop': u'0.50in',
 200             'marginbottom': u'0.50in',
 201             'marginleft': u'1.00in',
 202             'marginright': u'0.50in',
 203             'no-toc': u'checked',
 204             'no-links': u'checked',
 205             'firstpage': u'p1',
 206             'jpeg': u'0',
 207             'compression': u'0',
 208             'pagemode': u'outline',
 209             'pagelayout': u'single',
 210             'firstpage': u'c1',
 211             'numbered': u'checked',
 212             'encryption': u'unchecked',
 213             'permissioncopy': u'checked',
 214             'permissionprint': u'checked',
 215             'permissionannotate': u'checked',
 216             'permissionmodify': u'checked',
 217             'charset': u'iso-8859-1',
 218             'debug': u'',
 219             'rev': 0,
 220         }
 221         # We have to know which values are checkboxes within the form. If a key does
 222         # not exists wihtin the form the corresponding checkbox is not checked.
 223         self.form_checkbox = []
 224         for key, value in self.default_values.items():
 225             if value in [u'checked', u'unchecked']:
 226                 self.form_checkbox += [key]
 227         self.contenttype = u'application/pdf'
 228 
 229     def error_msg (self, msg):
 230         """Display error message."""
 231         if not self.errormsgsent:
 232             Page (self.request, self.pagename).send_page (self.request, msg=msg)
 233             self.errormsgsent = True
 234 
 235     def fixhtmlstr (self, str):
 236         """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
 237         htmlstr = u''
 238         for c in str:
 239             if ord(c) >= 128:
 240                 htmlstr = htmlstr + '&#%d;' % ord(c)
 241             else:
 242                 htmlstr = htmlstr + c
 243         return htmlstr
 244 
 245     def fixutf8latin1 (self, str):
 246         """Convert a UTF-8 string containing ISO-8859-1 encoding to a true ISO-8859-1 string.
 247         It's the weakness of MoinMoin 1.5 where pages are save as UTF-8 (multi byte) data
 248         containing ISO-8859-1 characters (sigle byte). Python run into trouble with this
 249         'currupted' UNICODE strings.
 250         Until be able to use the string within Python's functions we have to fix this problem.
 251         """
 252         return str.encode(config.charset).decode(self.values['charset'])#.encode(self.values['charset'])
 253 
 254     def set_page_values(self):
 255         """Scan raw page for additional information relating PDF generation."""
 256         #pdflines = False
 257         for line in self.request.page.get_raw_body().split(u'\n'):
 258             if line[:6] == u'##pdf ':
 259                 cols = line[6:].split()
 260                 # Only accept known values/settings.
 261                 if len(cols) > 1 and cols[0] in self.default_values:
 262                     self.values[cols[0]] = u' '.join(cols[1:])
 263                     #pdflines = True
 264                 continue
 265             # Stop parsing with first non-pdf line (if detected at least one).
 266             #elif pdflines and not line:
 267             #    break
 268 
 269     def set_page_default_values(self):
 270         # We are not able to recognise if this string is part of a verbatim area.
 271         matchtoclvl = re.compile(r'^\[\[TableOfContents\(\s*(\d+)\s*\)\]\]')
 272         matchtoc = re.compile(r'^\[\[TableOfContents\(*\)*\]\]')
 273         toc = False
 274         for line in self.request.page.get_raw_body().split(u'\n'):
 275             if line[:10] == u'#language ' and not u'language' in self.values:
 276                 lang = self.make_isolang(line[10:])
 277                 if lang:
 278                     self.default_values[u'language'] = lang
 279             elif not u'toclevels' in self.values and not toc:
 280                 result = matchtoclvl.match(line)
 281                 if result:
 282                     toclevels = int(result.group(1).strip())
 283                     if toclevels > 4:
 284                         toclevels = 4
 285                     self.default_values[u'toclevels'] = str(toclevels)
 286                     toc = True
 287                 elif matchtoc.match(line):
 288                     toc = True
 289         # We assume if table-of-contents is used we intent to generate a book.
 290         if toc:
 291             self.default_values[u'style'] = u'book'
 292         else:
 293             # Do not generate a table of contents page.
 294             self.default_values[u'no-toc'] = u'unchecked'
 295  
 296     def _select (self, name, description=None):
 297         """Helper function to create a selection control."""
 298         str = u'<select name="%s" size="1">' % (name,)
 299         if not description:
 300             description = self.valid_options[name]
 301         keys = description.keys()
 302         keys.sort()
 303         for value in keys:
 304             if value == self.values[name]:
 305                 selected = u'selected'
 306             else:
 307                 selected = u''
 308             str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
 309         str += u'</select>'
 310         return str
 311 
 312     def _chooseformat (self, name):
 313         """Helper function to create left/middle/right selection controls."""
 314         str = u"""    <tr>
 315         <td class="label"><label>%s&nbsp;:</label></td>
 316         <td><table>
 317                 <tr>
 318                     <td>%s&nbsp;:</td>
 319                     <td>%s</td>
 320                 </tr>
 321                 <tr>
 322                     <td>%s&nbsp;:</td>
 323                     <td>%s</td>
 324                 </tr>
 325                 <tr>
 326                     <td>%s&nbsp;:</td>
 327                     <td>%s</td>
 328                 </tr>
 329             </table>
 330         </td>
 331         <td>%s</td>
 332     </tr>""" % (self.fields[u'label_' + name],
 333               self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
 334               self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
 335               self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
 336               self.fields[u'help_' + name],)
 337         return str
 338 
 339     def makeform (self, errormsg=u''):
 340         self.fields = {
 341             'error': errormsg,
 342             'pagename': wikiutil.escape(self.pagename),
 343             'action': self.action_name,
 344             'seperator': u'<tr><td colspan="3"><hr/></td></tr>',
 345             'space': u'<tr><td colspan="3">&nbsp;</td></tr>',
 346 
 347             'label_input': self._(u'Input'),
 348             'label_output': self._(u'Output'),
 349             'label_page': self._(u'Page'),
 350             'label_tableofcontents': self._(u'Contents'),
 351             'label_pdf': self._(u'PDF'),
 352             'label_security': self._(u'Security'),
 353 
 354             'label_choose_style': self._(u'Choose style'),
 355             'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
 356                                  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/>' + \
 357                                  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.'),
 358 
 359             'help_titletext': self._(u'Title of the document for the front page.'),
 360 
 361             'label_format': self._(u'Output format'),
 362             'help_format': self._(u'Specifies the output format.'),
 363 
 364             'label_outputoptions': self._(u'Output options'),
 365             'label_grayscale': self._(u'Grayscale document'),
 366             'label_titlepage': self._(u'Title page'),
 367             'label_titletext': self._(u'Title'),
 368             'label_jpeg': self._(u'JPEG big images'),
 369             'label_compression': self._(u'Compression'),
 370 
 371             'label_no-toc': self._(u'Generate a table of contents'),
 372             'help_no-toc': self._(u''),
 373 
 374             'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
 375             'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
 376 
 377             'label_numbered': self._(u'Numbered headings'),
 378             'help_numbered': self._(u'Check to number all of the headings in the document.'),
 379 
 380             'label_toctitle': self._(u'Table-of-contents title'),
 381             'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
 382 
 383             'label_left': self._(u'Left'),
 384             'label_middle': self._(u'Middle'),
 385             'label_right': self._(u'Right'),
 386 
 387             'label_tocheader': self._(u'Header of table-of-contantes page'),
 388             'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
 389 
 390             'label_tocfooter': self._(u'Footer of table-of-contantes page'),
 391             'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
 392 
 393             'label_header': self._(u'Page header'),
 394             'help_header': self._(u'Sets the page header to use on body pages.'),
 395 
 396             'label_footer': self._(u'Page footer'),
 397             'help_footer': self._(u'Sets the page footer to use on body pages.'),
 398 
 399             'label_no-links': self._(u'Create HTTP links'),
 400             'help_no-links': self._(u'Enables generation of links in PDF files.'),
 401             
 402             'label_linkstyle': self._(u'Style of HTTP links'),
 403             'help_linkstyle': self._(u''),
 404 
 405             'label_linkcolor': self._(u'HTTP links color'),
 406             'help_linkcolor': self._(u'Sets the color of links.'),
 407 
 408             'label_duplex': self._(u'2-Sided'),
 409             'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
 410 
 411             'label_landscape': self._(u'Landscape'),
 412 
 413             'label_choose_size': self._(u'Choose page size'),
 414             'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
 415 
 416             'label_usersize': self._(u'User defined page size'),
 417             '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).'),
 418 
 419             'label_margin': self._(u'User defined margin'),
 420             'label_margintop': self._(u'Top'),
 421             'label_marginbottom': self._(u'Bottom'),
 422             'label_marginleft': self._(u'Left'),
 423             'label_marginright': self._(u'Right'),
 424             '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.'),
 425 
 426             'label_pagemode': self._(u'Page mode'),
 427             '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.'),
 428 
 429             'label_pagelayout': self._(u'Page layout'),
 430             '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.'),
 431 
 432             'label_firstpage': self._(u'First page'),
 433             'help_firstpage': self._(u'Choose the initial page that will be shown.'),
 434 
 435             'label_encryption': self._(u'Encryption'),
 436             'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
 437 
 438             'label_permissions': self._(u'Permissions'),
 439             'help_permissions': self._(u'Specifies the document permissions.'),
 440 
 441             'label_permissionannotate': self._(u'Annotate'),
 442             'label_permissionprint': self._(u'Print'),
 443             'label_permissionmodify': self._(u'Modify'),
 444             'label_permissioncopy': self._(u'Copy'),
 445 
 446             'label_owner-password': self._(u'Owner password'),
 447             '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.'),
 448 
 449             'label_user-password': self._(u'User password'),
 450             'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
 451 
 452             'label_expert': self._(u'Expert'),
 453             'label_language': self._(u'Language translation'),
 454             'help_language': self._(u'Specify language to use for date and time format.'),
 455 
 456             'label_charset': self._(u'Charset set'),
 457             'help_charset': self._(u'Change the encoding of the text in document.'),
 458 
 459             'button_generate': self._(u'Generate PDF'),
 460             'button_remember': self._(u'Remember form'),
 461             'button_cancel': self._(u'Cancel'),
 462             'button_reset': self._(u'Reset'),
 463             }
 464         self.fields.update(self.values)
 465 
 466         # Go through all format strings.
 467         for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
 468             self.fields[u'choose_' + name] = self._chooseformat(name)
 469 
 470         self.fields[u'select_style'] = self._select (u'style')
 471         self.fields[u'select_format'] = self._select (u'format')
 472         self.fields[u'select_linkstyle'] = self._select (u'linkstyle')
 473         self.fields[u'select_size'] = self._select (u'size')
 474         self.fields[u'select_jpeg'] = self._select (u'jpeg')
 475         self.fields[u'select_compression'] = self._select (u'compression')
 476         self.fields[u'select_toclevels'] = self._select (u'toclevels')
 477         self.fields[u'select_pagemode'] = self._select (u'pagemode')
 478         self.fields[u'select_pagelayout'] = self._select (u'pagelayout')
 479         self.fields[u'select_firstpage'] = self._select (u'firstpage')
 480         self.fields[u'select_charset'] = self._select (u'charset')
 481 
 482         self.fields[u'pdfpydownloadlink'] = u'http://einstein.speech-design.de/~br/pdf.py'
 483         self.fields[u'pdfpyversion'] = self.version
 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.