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