Attachment 'pdf2_0_0.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 
  21 import os, stat
  22 import re, copy, sys, StringIO, tempfile, traceback
  23 import shutil, StringIO
  24 from MoinMoin import config, util, wikiutil, packages, error
  25 from MoinMoin.Page import Page
  26 from MoinMoin.util import MoinMoinNoFooter, filesys
  27 from MoinMoin.request import RequestModPy
  28 from MoinMoin.parser import wiki
  29 from MoinMoin.widget.dialog import Dialog
  30 
  31 class ActionError(Exception):
  32     pass
  33 
  34 class pdf:
  35     """Implementation of the PDF document generator."""
  36 
  37     version = u'2.0.0'
  38 
  39     def __init__(self):
  40         self.action_name = self.__class__.__name__
  41         self.pagename = None
  42         self.request = None
  43         self._ = None
  44         self.debug = False
  45         self.msg = None
  46         self.default_values = {
  47             'style': u'webpage',
  48             'format': u'pdf13',
  49             'linkstyle': u'underline',
  50             'headerleft': u't',
  51             'headermiddle': u'.',
  52             'headerright': u'D',
  53             'footerleft': u'.',
  54             'footermiddle': u'/',
  55             'footerright': u'.',
  56             'tocheaderleft': u'.',
  57             'tocheadermiddle': u't',
  58             'tocheaderright': u'.',
  59             'tocfooterleft': u'.',
  60             'tocfootermiddle': u'.',
  61             'tocfooterright': u'i',
  62             'linkcolor': u'0000E0',
  63             'size': u'legal',
  64             'user-password': u'',
  65             'owner-password': u'',
  66             'toclevels': u'3',
  67             'grayscale': u'unchecked',
  68             'title': u'checked',
  69             'duplex': u'unchecked',
  70             'landscape': u'unchecked',
  71             'usersize': u'',
  72             'margintop': u'0.50in',
  73             'marginbottom': u'0.50in',
  74             'marginleft': u'1.00in',
  75             'marginright': u'0.50in',
  76             'no-toc': u'checked',
  77             'no-links': u'checked',
  78             'firstpage': u'p1',
  79             'jpeg': u'0',
  80             'compression': u'0',
  81             'pagemode': u'outline',
  82             'pagelayout': u'single',
  83             'firstpage': u'c1',
  84             'numbered': u'checked',
  85             'encryption': u'unchecked',
  86             'permissioncopy': u'checked',
  87             'permissionprint': u'checked',
  88             'permissionannotate': u'checked',
  89             'permissionmodify': u'checked',
  90             'debug': u'',
  91             'rev': 0,
  92         }
  93         # We have to know which values are checkboxes within the form. If a key does
  94         # not exists wihtin the form the corresponding checkbox is not checked.
  95         self.form_checkbox = []
  96         for key, value in self.default_values.items():
  97             if value in [u'checked', u'unchecked']:
  98                 self.form_checkbox += [key]
  99         self.contenttype = u'application/pdf'
 100 
 101     def fixhtmlstr (self, str):
 102         """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
 103         htmlstr = u''
 104         for c in str:
 105             if ord(c) >= 128:
 106                 htmlstr = htmlstr + '&#%d;' % ord(c)
 107             else:
 108                 htmlstr = htmlstr + c
 109         return htmlstr
 110 
 111     def error_msg (self, msg):
 112         """Display error message."""
 113         Page (self.request, self.pagename).send_page (self.request, msg=msg)
 114 
 115     def set_page_values(self):
 116         """Scan raw page for additional information relating PDF generation."""
 117         #pdflines = False
 118         for line in self.request.page.get_raw_body().split(u'\n'):
 119             if line[:6] == u'##pdf ':
 120                 cols = line[6:].split()
 121                 # Only accept known values/settings.
 122                 if len(cols) > 1 and cols[0] in self.default_values:
 123                     self.values[cols[0]] = u' '.join(cols[1:])
 124                     #pdflines = True
 125                 continue
 126             # Stop parsing with first non-pdf line (if detected at least one).
 127             #elif pdflines and not line:
 128             #    break
 129 
 130     def set_page_default_values(self):
 131         # We are not able to recognise if this string is part of a verbatim area.
 132         matchtoclvl = re.compile(r'^\[\[TableOfContents\(\s*(\d+)\s*\)\]\]')
 133         matchtoc = re.compile(r'^\[\[TableOfContents\(*\)*\]\]')
 134         toc = False
 135         for line in self.request.page.get_raw_body().split(u'\n'):
 136             if line[:10] == u'#language ' and not u'language' in self.values:
 137                 lang = self.make_isolang(line[10:])
 138                 if lang:
 139                     self.default_values[u'language'] = lang
 140             elif not u'toclevels' in self.values and not toc:
 141                 result = matchtoclvl.match(line)
 142                 if result:
 143                     toclevels = int(result.group(1).strip())
 144                     if toclevels > 4:
 145                         toclevels = 4
 146                     self.default_values[u'toclevels'] = str(toclevels)
 147                     toc = True
 148                 elif matchtoc.match(line):
 149                     toc = True
 150         # We assume if table-of-contents is used we intent to generate a book.
 151         if toc:
 152             self.default_values[u'style'] = u'book'
 153         else:
 154             # Do not generate a table of contents page.
 155             self.default_values[u'no-toc'] = u'unchecked'
 156  
 157     def escape (self, str):
 158         return str.replace (u'&', u'&amp;').replace (u'<', u'&lt;').replace (u'>', u'&gt;')
 159 
 160     def _select (self, name, data):
 161         """Helper function to create a selection control."""
 162         str = u'<select name="%s" size="1">' % (name,)
 163         keys = data.keys()
 164         keys.sort()
 165         for value in keys:
 166             if value == self.fields[name]:
 167                 selected = u'selected'
 168             else:
 169                 selected = u''
 170             str += u'<option value="%s" %s>%s</option>' % (value, selected, data[value],)
 171         str += u'</select>'
 172         return str
 173 
 174     def _chooseformat (self, name):
 175         """Helper function to create left/middle/right selection controls."""
 176         str = u"""    <tr>
 177         <td class="label"><label>%s&nbsp;:</label></td>
 178         <td><table>
 179                 <tr>
 180                     <td>%s&nbsp;:</td>
 181                     <td>%s</td>
 182                 </tr>
 183                 <tr>
 184                     <td>%s&nbsp;:</td>
 185                     <td>%s</td>
 186                 </tr>
 187                 <tr>
 188                     <td>%s&nbsp;:</td>
 189                     <td>%s</td>
 190                 </tr>
 191             </table>
 192         </td>
 193         <td>%s</td>
 194     </tr>""" % (self.fields[u'label_' + name],
 195               self.fields[u'label_left'], self._select(name + u'left', self.default_tocformats),
 196               self.fields[u'label_middle'], self._select(name + u'middle', self.default_tocformats),
 197               self.fields[u'label_right'], self._select(name + u'right', self.default_tocformats),
 198               self.fields[u'help_' + name],)
 199         return str
 200 
 201     def makeform (self, errormsg=u''):
 202         self.fields = {
 203             'error': errormsg,
 204             'pagename': wikiutil.escape(self.pagename),
 205             'action': self.action_name,
 206             'seperator': u'<tr><td colspan="3"><hr/></td></tr>',
 207             'space': u'<tr><td colspan="3">&nbsp;</td></tr>',
 208 
 209             'label_input': self._(u'Input'),
 210             'label_output': self._(u'Output'),
 211             'label_page': self._(u'Page'),
 212             'label_tableofcontents': self._(u'Contents'),
 213             'label_pdf': self._(u'PDF'),
 214             'label_security': self._(u'Security'),
 215 
 216             'label_choose_style': self._(u'Choose style'),
 217             'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
 218                                  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/>' + \
 219                                  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.'),
 220 
 221             'label_format': self._(u'Output format'),
 222             'help_format': self._(u'Specifies the output format.'),
 223 
 224             'label_outputoptions': self._(u'Output options'),
 225             'label_grayscale': self._(u'Grayscale document'),
 226             'label_title': self._(u'Title page'),
 227             'label_jpeg': self._(u'JPEG big images'),
 228             'label_compression': self._(u'Compression'),
 229 
 230             'label_no-toc': self._(u'Generate a table of contents'),
 231             'help_no-toc': self._(u''),
 232 
 233             'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
 234             'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
 235 
 236             'label_numbered': self._(u'Numbered headings'),
 237             'help_numbered': self._(u'Check to number all of the headings in the document.'),
 238 
 239             'label_toctitle': self._(u'Table-of-contents title'),
 240             'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
 241 
 242             'label_left': self._(u'Left'),
 243             'label_middle': self._(u'Middle'),
 244             'label_right': self._(u'Right'),
 245 
 246             'label_tocheader': self._(u'Header of table-of-contantes page'),
 247             'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
 248 
 249             'label_tocfooter': self._(u'Footer of table-of-contantes page'),
 250             'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
 251 
 252             'label_header': self._(u'Page header'),
 253             'help_header': self._(u'Sets the page header to use on body pages.'),
 254 
 255             'label_footer': self._(u'Page footer'),
 256             'help_footer': self._(u'Sets the page footer to use on body pages.'),
 257 
 258             'label_no-links': self._(u'Create HTTP links'),
 259             'help_no-links': self._(u'Enables generation of links in PDF files.'),
 260             
 261             'label_linkstyle': self._(u'Style of HTTP links'),
 262             'help_linkstyle': self._(u''),
 263 
 264             'label_linkcolor': self._(u'HTTP links color'),
 265             'help_linkcolor': self._(u'Sets the color of links.'),
 266 
 267             'label_duplex': self._(u'2-Sided'),
 268             'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
 269 
 270             'label_landscape': self._(u'Landscape'),
 271 
 272             'label_choose_size': self._(u'Choose page size'),
 273             'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
 274 
 275             'label_usersize': self._(u'User defined page size'),
 276             '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).'),
 277 
 278             'label_margin': self._(u'User defined margin'),
 279             'label_margintop': self._(u'Top'),
 280             'label_marginbottom': self._(u'Bottom'),
 281             'label_marginleft': self._(u'Left'),
 282             'label_marginright': self._(u'Right'),
 283             '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.'),
 284 
 285             'label_pagemode': self._(u'Page mode'),
 286             '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.'),
 287 
 288             'label_pagelayout': self._(u'Page layout'),
 289             '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.'),
 290 
 291             'label_firstpage': self._(u'First page'),
 292             'help_firstpage': self._(u'Choose the initial page that will be shown.'),
 293 
 294             'label_encryption': self._(u'Encryption'),
 295             'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
 296 
 297             'label_permissions': self._(u'Permissions'),
 298             'help_permissions': self._(u'Specifies the document permissions.'),
 299 
 300             'label_permissionannotate': self._(u'Annotate'),
 301             'label_permissionprint': self._(u'Print'),
 302             'label_permissionmodify': self._(u'Modify'),
 303             'label_permissioncopy': self._(u'Copy'),
 304 
 305             'label_owner-password': self._(u'Owner password'),
 306             '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.'),
 307 
 308             'label_user-password': self._(u'User password'),
 309             'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
 310 
 311             'label_expert': self._(u'Expert'),
 312             'label_language': self._(u'Language translation'),
 313             'help_language': self._(u'Specify language to use for date and time format.'),
 314 
 315             'button_generate': self._(u'Generate PDF'),
 316             'button_remember': self._(u'Remember form'),
 317             'button_cancel': self._(u'Cancel'),
 318             'button_reset': self._(u'Reset'),
 319 
 320             'label_downloadpdfpy': self._('Download this MoinMoin action plugin of version'),
 321             }
 322         self.fields.update(self.values)
 323 
 324         # Go through all format strings.
 325         for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
 326             self.fields[u'choose_' + name] = self._chooseformat(name)
 327 
 328         self.fields[u'select_style'] = self._select (u'style', self.default_styles)
 329         self.fields[u'select_format'] = self._select (u'format', self.default_formats)
 330         self.fields[u'select_linkstyle'] = self._select (u'linkstyle', self.default_linkstyles)
 331         self.fields[u'select_size'] = self._select (u'size', self.default_sizes)
 332         self.fields[u'select_jpeg'] = self._select (u'jpeg', self.default_jpeg)
 333         self.fields[u'select_compression'] = self._select (u'compression', self.default_compression)
 334         self.fields[u'select_toclevels'] = self._select (u'toclevels', self.default_toclevels)
 335         self.fields[u'select_pagemode'] = self._select (u'pagemode', self.default_pagemodes)
 336         self.fields[u'select_pagelayout'] = self._select (u'pagelayout', self.default_pagelayouts)
 337         self.fields[u'select_firstpage'] = self._select (u'firstpage', self.default_firstpage)
 338 
 339         self.fields[u'pdfpydownloadlink'] = u'http://einstein.speech-design.de/~br/pdf.py'
 340         self.fields[u'pdfpyversion'] = self.version
 341 
 342         form = """<p class="error">%(error)s</p>
 343 <form method="post" action="">
 344 <input type="hidden" name="action" value="%(action)s"/>
 345 <table>
 346     <tr><td colspan="2">&nbsp;</td><td><i>%(label_downloadpdfpy)s %(pdfpyversion)s: <a href="%(pdfpydownloadlink)s">pdf.py</a></i></td></tr>
 347     <tr><td colspan="3">%(label_input)s</td></tr>
 348     <tr>
 349         <td class="label"><label>%(label_choose_style)s&nbsp;:</label></td>
 350         <td class="content">%(select_style)s</td>
 351         <td>%(help_choose_style)s</td>
 352     </tr>
 353     %(seperator)s
 354     <tr><td colspan="3">%(label_output)s</td></tr>
 355     <tr>
 356         <td class="label"><label>%(label_format)s&nbsp;:</label></td>
 357         <td class="content">%(select_format)s</td>
 358         <td>%(help_format)s</td>
 359     </tr>
 360     <tr>
 361         <td class="label"><label>%(label_outputoptions)s&nbsp;:</label></td>
 362         <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s&nbsp;
 363             <input type="checkbox" name="title" value="checked" %(title)s />%(label_title)s<br />
 364             %(label_compression)s&nbsp:&nbsp;%(select_compression)s&nbsp;
 365             %(label_jpeg)s&nbsp;:&nbsp;%(select_jpeg)s</td>
 366     </tr>
 367     %(seperator)s
 368     <tr><td colspan="3">%(label_page)s</td></tr>
 369     <tr>
 370         <td class="label"><label>%(label_choose_size)s&nbsp;:</label></td>
 371         <td>%(select_size)s&nbsp;<br /><nobr>%(label_usersize)s&nbsp;:&nbsp;<input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
 372         <td>%(help_choose_size)s<br />%(help_usersize)s</td>
 373     </tr>
 374     <tr>
 375         <td>&nbsp;</td>
 376         <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s />&nbsp;%(label_duplex)s&nbsp;
 377             <input type="checkbox" name="landscape" value="checked" %(landscape)s />&nbsp;%(label_landscape)s</td>
 378     </tr>
 379     <tr>
 380         <td class="label"><label>%(label_margin)s&nbsp;:</label></td>
 381         <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>
 382             <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>
 383             <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>
 384         <td>%(help_margin)s</td>
 385     </tr>
 386     %(choose_header)s
 387     %(choose_footer)s
 388     %(seperator)s
 389     <tr><td colspan="3">%(label_tableofcontents)s</td></tr>
 390     <tr>
 391         <td class="label"><label>%(label_no-toc)s&nbsp;:</label></td>
 392         <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
 393         <td>%(help_no-toc)s</td>
 394     </tr>
 395     <tr>
 396         <td class="label"><label>%(label_toclevels)s&nbsp;:</label></td>
 397         <td class="content">%(select_toclevels)s</td>
 398         <td>%(help_toclevels)s</td>
 399     </tr>
 400     <tr>
 401         <td>&nbsp;</td>
 402         <td><input type="checkbox" name="numbered" value="checked" %(numbered)s />&nbsp;%(label_numbered)s</td>
 403         <td>%(help_numbered)s</td>
 404     </tr>
 405     <tr>
 406         <td class="label"><label>%(label_toctitle)s&nbsp;:</label></td>
 407         <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
 408         <td>%(help_toctitle)s</td>
 409     </tr>
 410     %(choose_tocheader)s
 411     %(choose_tocfooter)s
 412     %(seperator)s
 413     <tr><td colspan="3">%(label_pdf)s</td></tr>
 414     <tr>
 415         <td class="label"><label>%(label_pagemode)s&nbsp;:</label></td>
 416         <td class="content">%(select_pagemode)s</td>
 417         <td>%(help_pagemode)s</td>
 418     </tr>
 419     <tr>
 420         <td class="label"><label>%(label_pagelayout)s&nbsp;:</label></td>
 421         <td class="content">%(select_pagelayout)s</td>
 422         <td>%(help_pagelayout)s</td>
 423     </tr>
 424     <tr>
 425         <td class="label"><label>%(label_firstpage)s&nbsp;:</label></td>
 426         <td class="content">%(select_firstpage)s</td>
 427         <td>%(help_firstpage)s</td>
 428     </tr>
 429     <tr>
 430         <td class="label"><label>%(label_no-links)s&nbsp;:</label></td>
 431         <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
 432         <td>%(help_no-links)s</td>
 433     </tr>
 434     <tr>
 435         <td class="label"><label>%(label_linkstyle)s&nbsp;:</label></td>
 436         <td class="content">%(select_linkstyle)s</td>
 437         <td>%(help_linkstyle)s</td>
 438     </tr>
 439     <tr>
 440         <td class="label"><label>%(label_linkcolor)s&nbsp;:</label></td>
 441         <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
 442         <td>%(help_linkcolor)s</td>
 443     </tr>
 444     %(seperator)s
 445     <tr><td colspan="3">%(label_security)s</td></tr>
 446     <tr>
 447         <td class="label"><label>%(label_encryption)s&nbsp;:</label></td>
 448         <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
 449         <td>%(help_numbered)s</td>
 450     </tr>
 451     <tr>
 452         <td class="label"><label>%(label_permissions)s&nbsp;:</label></td>
 453         <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s />&nbsp;%(label_permissionprint)s</nobr>&nbsp;
 454             <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s />&nbsp;%(label_permissionmodify)s</nobr><br />
 455             <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s />&nbsp;%(label_permissioncopy)s</nobr>&nbsp;
 456             <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s />&nbsp;%(label_permissionannotate)s</nobr></td>
 457         <td>%(help_permissions)s</td>
 458     </tr>
 459     <tr>
 460         <td class="label"><label>%(label_user-password)s&nbsp;:</label></td>
 461         <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
 462         <td>%(help_user-password)s</td>
 463     </tr>
 464     <tr>
 465         <td class="label"><label>%(label_owner-password)s&nbsp;:</label></td>
 466         <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
 467         <td>%(help_owner-password)s</td>
 468     </tr>
 469     %(seperator)s
 470     <tr><td colspan="3">%(label_expert)s</td></tr>
 471     <tr>
 472         <td class="label"><label>%(label_language)s&nbsp;:</label></td>
 473         <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
 474         <td>%(help_language)s</td>
 475     </tr>
 476      %(space)s
 477     <tr>
 478         <td><input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" /></td>
 479         <td class="buttons" colspan="2">
 480             <input type="submit" name="generate_from_form" value="%(button_generate)s" />&nbsp;
 481             <input type="submit" name="remember" value="%(button_remember)s" />&nbsp;
 482             <input type="submit" name="cancel" value="%(button_cancel)s" />
 483         </td>
 484     </tr>
 485 </table>
 486 </form>""" % self.fields
 487         return Dialog (self.request, content=form)
 488 
 489     def run (self, pagename, request):
 490         """ Main dispatcher for the action."""
 491         self.pagename = pagename
 492         self.request = request
 493         self._ = self.request.getText
 494         self.form = self.request.form
 495         self.cfg = self.request.cfg
 496 
 497         # Canceled by user.
 498         if self.form.has_key(u'cancel'):
 499             return self.request.page.send_page(self.request)
 500 
 501         # Determine calling parameters.
 502         if self.form.get(u'debug', u'0') == u'1':
 503             self.debug = True
 504 
 505         self.default_tocformats = {
 506             u'/': self._(u'1/N,2/N Arabic page numbers'),
 507             u':': self._(u'1/C,2/C Arabic chapter page numbers'),
 508             u'1': self._(u'1,2,3,...'),
 509             u'a': self._(u'a,b,c,...'),
 510             u'A': self._(u'A,B,C,...'),
 511             u'c': self._(u'Chapter title'),
 512             u'C': self._(u'Chapter page number'),
 513             u'd': self._(u'Date'),
 514             u'D': self._(u'Date + Time'),
 515             u'h': self._(u'Heading'),
 516             u'i': self._(u'i,ii,iii,iv,...'),
 517             u'I': self._(u'I,II,III,IV,...'),
 518             u't': self._(u'Title'),
 519             u'T': self._(u'Time'),
 520             u'.': self._(u'Blank'),
 521             # TODO: Not supported yet; u'l': self._(u'Logo image'),
 522         }
 523         self.default_styles = {
 524             u'webpage': self._(u'webpage'),
 525             u'book': self._(u'book'),
 526             u'continuous': self._(u'continuous'),
 527         }
 528         self.default_sizes = {
 529             u'legal': self._(u'Legal (8.5x14in)'),
 530             u'a4': self._(u'A4 (210x297mm)'),
 531             u'letter': self._(u'Letter (8.5x11in)'),
 532             u'universal': self._(u'Universal (8.27x11in)'),
 533             u'': self._(u'User defined'),
 534         }
 535         self.default_formats = {
 536             u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
 537             u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
 538             u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
 539             u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
 540             # TODO: Not supported yet:
 541             #u'ps1': self._(u'PostScript Level 1'),
 542             #u'ps2': self._(u'PostScript Level 2'),
 543             #u'ps3': self._(u'PostScript Level 3'),
 544         }
 545         self.default_linkstyles = {
 546             u'underline': self._(u'Underline'),
 547             u'plain': self._(u'Plain'),
 548         }
 549         self.default_firstpage = {
 550             u'c1': self._(u'1st chapter'),
 551             u'p1': self._(u'1st page'),
 552             u'toc': self._(u'Contents'),
 553         }
 554         self.default_jpeg = {
 555             u'0': self._(u'None'),
 556             u'50': self._(u' 50% (Good)'),
 557             u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
 558             u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
 559             u'100': self._(u'100% (Best)'),
 560         }
 561         self.default_compression = {
 562             u'0': self._(u'None'),
 563             u'1': self._(u'1 (Fast)'),
 564             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',
 565             u'9': self._(u'9 (Best)'),
 566         }
 567         self.default_toclevels = {
 568             u'0': self._(u'None'),
 569             u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
 570         }
 571         self.default_pagemodes = {
 572             u'outline': self._(u'Outline'),
 573             u'document': self._(u'Document'),
 574             u'fullscreen': self._(u'Full-screen'),
 575         }
 576         self.default_pagelayouts = {
 577             u'single': self._(u'Single'),
 578             u'one': self._(u'One column'),
 579             u'twoleft': self._(u'Two column left'),
 580             u'tworight': self._(u'Two column right'),
 581         }
 582 
 583         # Set translated name of table of contents as default.
 584         self.default_values[u'toctitle'] = self._(u'Contents')
 585 
 586         # Make sure we create date and time strings in right format.
 587         if self.request.page.language:
 588             self.default_values[u'language'] = self.request.page.language
 589         else:
 590             self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get (u'default_language', u'en'))
 591 
 592         self.values = {}
 593         self.set_page_default_values()
 594 
 595         # Create a PDF document direct without any user iteraction from default and page settings.
 596         if self.form.has_key(u'generate'):
 597             self.set_page_values()
 598             self.update_values(useform=False)
 599             return self.do_generate()
 600 
 601         # Create a PDF document from form settings.
 602         if self.form.has_key(u'generate_from_form'):
 603             self.update_values()
 604             return self.do_generate()
 605 
 606         if self.form.has_key(u'remember'):
 607             self.update_values()
 608             return self.do_remember()
 609 
 610         self.set_page_values()
 611         self.update_values(useform=False)
 612         return self.request.page.send_page (self.request, msg=self.makeform())
 613 
 614     def update_values(self, useform=True):
 615         """Preset values with they form values or defaults."""
 616         for key, default in self.default_values.items():
 617             # Modify value only if not already set.
 618             if not key in self.values:
 619                 # If the form does not contain the value (e.g. for checkboxes) set the
 620                 # default value (e.g. for checkboxes unset by default).
 621                 if not key in self.form:
 622                     # Special processing for checkboxes in forms. If the key does not exists
 623                     # withint the form it is not checked.
 624                     if key in self.form_checkbox and useform:
 625                         self.values[key] = u'unchecked'
 626                     else:
 627                         self.values[key] = default
 628                 else:
 629                     self.values[key] = self.form[key][0]
 630         # Check if revision is an integer value.
 631         try:
 632             self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
 633         except:
 634             self.values[u'rev'] = self.request.page.rev
 635         # Check if page revision exists.
 636         (pagefname, realrev, exists) = self.request.page.get_rev (rev=self.values[u'rev'])
 637         if exists:
 638             self.values[u'rev'] = realrev
 639         else:
 640             # Determine latest revision number.
 641             (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
 642 
 643     def do_generate(self):
 644         """Create PDF document."""
 645         # Generate the HTML page using MoinMoin wiki engine.
 646         html = self.get_html()
 647 
 648         if not html:
 649             self.error_msg (msg=self._(u'Could not generate HTML page.'))
 650 
 651         pdfdata = self.html2pdf(html)
 652 
 653         if self.debug:
 654             self.request.http_headers()
 655 
 656         if self.debug:
 657             self.request.write(html)
 658         elif pdfdata:
 659             # Send as application/pdf the generated file by HTMLDOC
 660             self.send_pdf(pdfdata)
 661             raise MoinMoinNoFooter
 662 
 663     def do_remember(self):
 664         """Create a message containing information about how to save the form values for future reuse."""
 665         save = u''
 666         for key, value in self.values.items():
 667             if key in [u'user-password', u'owner-password', u'rev', u'debug']:
 668                 continue
 669             if key in self.default_values and value == self.default_values[key]:
 670                 continue
 671             save += u'##pdf %s %s\n' % (key, value,)
 672         if save:
 673             msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
 674         else:
 675             msg = self._(u'All values correspond to they default. Nothing have to be saved.')
 676         return self.request.page.send_page (self.request, msg)
 677 
 678     def send_pdf (self, data):
 679         filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
 680 
 681         # Send HTTP header.
 682         self.request.http_headers([
 683             u'Content-Type: %s' % self.contenttype,
 684             u'Content-Length: %d' % len(data),
 685             # TODO: fix the encoding here, plain 8 bit is not allowed
 686             # according to the RFCs There is no solution that is
 687             # compatible to IE except stripping non-ascii chars
 688             u'Content-Disposition: inline; filename="%s"' %
 689             filename.encode(config.charset),
 690             ])
 691 
 692         # Send binary data.
 693         sio = StringIO.StringIO(data)
 694         shutil.copyfileobj (sio, self.request, 8192)
 695 
 696     def get_html(self):
 697         """Generate the HTML body of this page."""
 698         # Get HTML for the page: Make a copy of the request, run it and intercept the output
 699         newreq = copy.copy(self.request)
 700         if self.debug:
 701             newreq.form[u'action'] = [u'show']
 702         else:
 703             newreq.form[u'action'] = [u'print']
 704         if self.values.get(u'rev', None):
 705             newreq.form[u'rev'] = self.values[u'rev']
 706 
 707         # Divert stdout and re-run the request.
 708         out = StringIO.StringIO()
 709         newreq.redirect(out)
 710         # Differentiate between CGI and mod_python.
 711         if isinstance (newreq, RequestModPy):
 712             newreq.run(None)
 713         else:
 714             newreq.run()
 715         html = out.getvalue()
 716         out.close()
 717 
 718         # FIXME: Why have we to reset the action? Otherwise we can not display error messages
 719         # because the self.request page is in ``print'' preview mode!
 720         newreq.form[u'action'] = [u'show']
 721 
 722         # Remove request's HTTP header.
 723         lines = html.split(u'\n')
 724         while lines[0].strip():
 725             del lines[0]
 726         html = u'\n'.join(lines)
 727 
 728         # Make URLs absolute.
 729         # Until MoinMoin is not XHTML compilant we can not use a XML parser (e.g. expat)
 730         # to transform the HTML document. In the meantime we try to achive the same with
 731         # regular expressions subtitution.
 732         base = self.request.getQualifiedURL()
 733         for htmlref in [u'src', u'href']:
 734             reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
 735             urlref = re.compile (reurlref, re.I)
 736             for match in urlref.finditer(html):
 737                 foundref = match.groups()
 738                 html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
 739 
 740         return html
 741 
 742     def make_isolang (self, language):
 743         return language + u'_' + language.upper()
 744 
 745     def html2pdf (self, html):
 746         """Create a PDF document based on the current parameters."""
 747         # Determine UID to access ACL protected sites too (mandatory to download attached images).
 748         htmldocopts = [u'LANG=' + self.values[u'language'], u'HTMLDOC_NOCGI=1', u'htmldoc', "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
 749 
 750         for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
 751             self.values[key] = self.values.get (key + u'left', u'.') + self.values.get (key + u'middle', u'.') + self.values.get (key + u'right', u'.')
 752 
 753         permissions = []
 754         for opt, value in self.values.items():
 755             if opt in [u'language', u'debug', u'rev']:
 756                 continue
 757             value = value.strip()
 758             if not value:
 759                 continue
 760             # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
 761             if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
 762                 continue
 763             if opt in [u'style']:
 764                 htmldocopts += [u'--' + value]
 765             elif opt in self.form_checkbox:
 766                 if value == u'checked':
 767                     if opt[:10] == u'permission':
 768                         permissions += [opt[10:]]
 769                     # Reverse meaning of 'no-' options.
 770                     elif opt[:3] != u'no-':
 771                         htmldocopts += [u'--' + opt]
 772                 elif opt[:3] == u'no-':
 773                     htmldocopts += [u'--' + opt]
 774             elif opt[:6] == u'margin':
 775                 htmldocopts += [u'--' + opt[6:], value]
 776             else:
 777                 htmldocopts += [u'--' + opt, value]
 778         if permissions:
 779             htmldocopts += [u'--permission', u','.join (permissions)]
 780         htmldocopts += [u'-']
 781         # Do not forget to escape all spaces!
 782         eschtmldocopts = [arg.replace(u' ', u'\\ ') for arg in htmldocopts]
 783         cmdstr = u' '.join(eschtmldocopts)
 784         errmsg = None
 785 
 786         pdf = None
 787         if self.debug:
 788             errmsg = self._(u'Command:') + u'<pre>' + self.escape(cmdstr) + u'</pre>'
 789         else:
 790             inp, out, err = os.popen3 (cmdstr, u'b')
 791             try:
 792                 inp.write(self.fixhtmlstr(html))
 793                 inp.close()
 794             except:
 795                 pass
 796 
 797             pdf = out.read()
 798             out.close()
 799 
 800             htmldocmsg = err.read()
 801             err.close()
 802 
 803             # REMARK: Otherwise we get <defunct> processes.
 804             os.wait()
 805 
 806             # Check for error message on STDOUT.
 807             if pdf[:8] == u'HTMLDOC ':
 808                 htmldocmsg += pdf
 809                 pdf = None
 810 
 811             if htmldocmsg:
 812                 errmsg = self._(u'Command:') + u'<pre>' + self.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
 813                       self.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
 814 
 815         # As it is difficult to get the htmldoc return code, we check for
 816         # error by checking the produced pdf length
 817         if not pdf and errmsg:
 818             self.error_msg(errmsg)
 819 
 820         return pdf
 821 
 822 def execute (pagename, request):
 823     pdf().run (pagename = pagename, request = request)
 824 
 825 # 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.