Attachment 'CreatePdfDocument2_3_1.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 __doc__ = """
   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     Please visit the homepage for further informations:
  17     http://moinmo.in/ActionMarket/PdfAction
  18 
  19     @copyright: (C) 2006  Pascal Bauermeister
  20     @copyright: (C) 2006-2008  Raphael Bossek
  21     @license: GNU GPL, see COPYING for details
  22 """
  23 
  24 __version__ = u'2.3.1'
  25 
  26 release_notes = """
  27 2008-06-25  RaphaelBossek
  28 * Release v2.3.1
  29 * FIX: Remove interwiki part of author name.
  30 * FIX: RedirectOutputRequest is able to catch error messages while initialisation, e.g. if forbidden.
  31 * FIX: Suppress of line numbers in code blocks.
  32 * FIX: Processing of form values (checkbox).
  33 * NEW: Added debugging support in expert mode.
  34 * NEW: Possibility to set in expert tab if line breaks and middle dot character is used in dynamic blocks.
  35 
  36 2008-06-04  RaphaelBossek
  37 * Release v2.3.0
  38 * Supports MoinMoin v1.7.0, v1.6.3, v1.5.8
  39 * Preselect document style to webpage if no heading and table of contents is found.
  40 * FIX: Missing table borders where '<table style=' was used.
  41 * FIX: Cut off blocks.
  42 
  43 2008-01-27  RaphaelBossek
  44 * Release v2.2.1
  45 * Remove page information for MoinMoin v1.5 (interwiki).
  46 * Added support to set page title using first heading.
  47 * Fix for htmldoc_cmd parameter in remember form.
  48 * Style of document will be set to book if TableOfContents is found by default (if
  49   not overwritten).
  50 
  51 2007-09-25  RaphaelBossek
  52 * Release v2.2.0
  53 * Fixed debug information for Windows.
  54 * Added support for Windows where HTMLDOC_NOCGI environment variable was missing.
  55 * Added preview mode added. It's now possible to see what HTMLDOC will process.
  56 
  57 2007-09-23  RaphaelBossek
  58 * Release v2.1.5
  59 * Added support for line numbers in code blocks.
  60 * Remove page information so it's not printed for webpage style.
  61 * Added table cell padding to 5 pixels.
  62 
  63 2007-09-21  RaphaelBossek
  64 * Release v2.1.4
  65 * HTMLDOC supports only HTML 4.1 (http://www.htmldoc.org/documentation.php/Elements.html)
  66 * Fix for table borders and cell background.
  67 * Added support for code blocks (break on page boundries).
  68 
  69 2007-09-18  RaphaelBossek
  70 * Release v2.1.3
  71 * Added support for scaling images (--browserwidth).
  72 
  73 2007-08-21  RaphaelBossek
  74 * Release v2.1.2
  75 * Fixed support for python 2.3.
  76 
  77 2007-08-21  RaphaelBossek
  78 * Release v2.1.1
  79 * Applied speed improvemtn patch from BrianDickman.
  80 * Fixed font size values where x.9 was missing.
  81 * Some cosmetic changes in the form.
  82 
  83 2007-08-20  RaphaelBossek
  84 * Release v2.1.0
  85 * Configuration is seperated by tabbs.
  86 * Added support for font style and colors.
  87 * Fixed save/load of configuration variables within page (may be empty).
  88 
  89 2007-08-17  RaphaelBossek
  90 * Release v2.0.11
  91 * Added support for alternative title page and title page logo.
  92 * Added support for additional fields for the title page author, docnumber
  93   and copyright.
  94 
  95 2006-12-18  RaphaelBossek
  96 * Release v2.0.10
  97 * Improved support for webpage/book styles if the HTML documents does not.
  98 
  99 2006-12-17  RaphaelBossek
 100 * Release v2.0.9
 101 * Added AUTH_TYPE for RedirectOutputRequest()
 102 
 103 2006-11-16  RaphaelBossek
 104 * Release v2.0.8
 105 * Fixed another missing configuration for RedirectOutputRequest()
 106 
 107 2006-11-15  RaphaelBossek
 108 * Release v2.0.7
 109 * Fixed support for MoinMoin 1.5.3
 110 * Fixed SSL support.
 111 
 112 2006-11-14  RaphaelBossek
 113 * Release v2.0.6
 114 * MoinMoin 1.6 support added.
 115 * Fixed Windows(TM) platform support.
 116 * Added support for alternative title text.
 117 
 118 2006-09-13  RaphaelBossek
 119 * Release v2.0.5
 120 * Fixed RedirectOutputRequest class where definition of script_name
 121   was missing.
 122 
 123 2006-09-12  RaphaelBossek
 124 * Release v2.0.4
 125 * Fixed RedirectOutputRequest class where function was redifined
 126   to boolean value.
 127 
 128 2006-09-06  RaphaelBossek
 129 * Release v2.0.3
 130 * Fixed FastCGI support by removing stdout redirect output code. The
 131   same functionality will be done by the RedirectOutputRequest class.
 132 * Renamed to CreatePdfDocument (for better translation possibilities).
 133 * Fixed encoding of title page.
 134 * Added charset set option.
 135 * Fixed waiting for HTMLDOC.
 136 
 137 2006-09-02  RaphaelBossek
 138 * Release v2.0.2
 139 * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
 140   configuration parameter support. You are not able to preset available
 141   options and which defaults are used in your configuration file.
 142 
 143 2006-08-30  RaphaelBossek
 144 * Release v2.0.1
 145 * Fixed issue with page revision number forwarding (traceback).
 146 
 147 2006-08-30  RaphaelBossek
 148 * Release v2.0.0
 149 * Feature enchanced and bug fixed version.
 150 
 151 2006-05-26  PascalBauermeister
 152 * Release v1.0.2
 153 * Relative image URLs turned absolute was bogus. It is less bogus now.
 154 
 155 2006-05-26  PascalBauermeister
 156 * Release v1.0.1
 157 * Set env var HTMLDOC_NOCGI to solve CGI issue
 158 
 159 2006-05-24  PascalBauermeister
 160 * Initial release v1.0.0
 161 """
 162 
 163 import os
 164 import sys
 165 import stat
 166 import re
 167 import copy
 168 import shutil
 169 import StringIO
 170 import array
 171 from MoinMoin import config
 172 from MoinMoin import util
 173 from MoinMoin import wikiutil
 174 from MoinMoin import packages
 175 from MoinMoin import error
 176 from MoinMoin.Page import Page
 177 from MoinMoin.request import RequestBase
 178 from MoinMoin.widget.dialog import Dialog
 179 from MoinMoin.version import release as moinmoin_release
 180 from MoinMoin.action import ActionBase
 181 
 182 # http://www.barelyfitz.com/projects/tabber/
 183 tabber_minimized = '''
 184 // Version 1.9 stripped by Creativyst SS & JavaScript Compressor v2.2c (http://www.creativyst.com/Prod/3/)
 185 function tabberObj(argsObj)
 186 { var arg; this.div = null; this.classMain = "tabber"; this.classMainLive = "tabberlive"; this.classTab = "tabbertab"; this.classTabDefault = "tabbertabdefault"; this.classNav = "tabbernav"; this.classTabHide = "tabbertabhide"; this.classNavActive = "tabberactive"; this.titleElements = ['h2','h3','h4','h5','h6']; this.titleElementsStripHTML = true; this.removeTitle = true; this.addLinkId = false; this.linkIdFormat = '<tabberid>nav<tabnumberone>'; for (arg in argsObj) { this[arg] = argsObj[arg];}
 187 this.REclassMain = new RegExp('\\\\b' + this.classMain + '\\\\b', 'gi'); this.REclassMainLive = new RegExp('\\\\b' + this.classMainLive + '\\\\b', 'gi'); this.REclassTab = new RegExp('\\\\b' + this.classTab + '\\\\b', 'gi'); this.REclassTabDefault = new RegExp('\\\\b' + this.classTabDefault + '\\\\b', 'gi'); this.REclassTabHide = new RegExp('\\\\b' + this.classTabHide + '\\\\b', 'gi'); this.tabs = new Array(); if (this.div) { this.init(this.div); this.div = null;}
 188 }
 189 tabberObj.prototype.init = function(e)
 190 { var
 191 childNodes, i, i2, t, defaultTab=0, DOM_ul, DOM_li, DOM_a, aId, headingElement; if (!document.getElementsByTagName) { return false;}
 192 if (e.id) { this.id = e.id;}
 193 this.tabs.length = 0; childNodes = e.childNodes; for(i=0; i < childNodes.length; i++) { if(childNodes[i].className &&
 194 childNodes[i].className.match(this.REclassTab)) { t = new Object(); t.div = childNodes[i]; this.tabs[this.tabs.length] = t; if (childNodes[i].className.match(this.REclassTabDefault)) { defaultTab = this.tabs.length-1;}
 195 }
 196 }
 197 DOM_ul = document.createElement("ul"); DOM_ul.className = this.classNav; for (i=0; i < this.tabs.length; i++) { t = this.tabs[i]; t.headingText = t.div.title; if (this.removeTitle) { t.div.title = '';}
 198 if (!t.headingText) { for (i2=0; i2<this.titleElements.length; i2++) { headingElement = t.div.getElementsByTagName(this.titleElements[i2])[0]; if (headingElement) { t.headingText = headingElement.innerHTML; if (this.titleElementsStripHTML) { t.headingText.replace(/<br>/gi," "); t.headingText = t.headingText.replace(/<[^>]+>/g,"");}
 199 break;}
 200 }
 201 }
 202 if (!t.headingText) { t.headingText = i + 1;}
 203 DOM_li = document.createElement("li"); t.li = DOM_li; DOM_a = document.createElement("a"); DOM_a.appendChild(document.createTextNode(t.headingText)); DOM_a.href = "javascript:void(null);"; DOM_a.title = t.headingText; DOM_a.onclick = this.navClick; DOM_a.tabber = this; DOM_a.tabberIndex = i; if (this.addLinkId && this.linkIdFormat) { aId = this.linkIdFormat; aId = aId.replace(/<tabberid>/gi, this.id); aId = aId.replace(/<tabnumberzero>/gi, i); aId = aId.replace(/<tabnumberone>/gi, i+1); aId = aId.replace(/<tabtitle>/gi, t.headingText.replace(/[^a-zA-Z0-9\-]/gi, '')); DOM_a.id = aId;}
 204 DOM_li.appendChild(DOM_a); DOM_ul.appendChild(DOM_li);}
 205 e.insertBefore(DOM_ul, e.firstChild); e.className = e.className.replace(this.REclassMain, this.classMainLive); this.tabShow(defaultTab); if (typeof this.onLoad == 'function') { this.onLoad({tabber:this});}
 206 return this;}; tabberObj.prototype.navClick = function(event)
 207 { var
 208 rVal, a, self, tabberIndex, onClickArgs; a = this; if (!a.tabber) { return false;}
 209 self = a.tabber; tabberIndex = a.tabberIndex; a.blur(); if (typeof self.onClick == 'function') { onClickArgs = {'tabber':self, 'index':tabberIndex, 'event':event}; if (!event) { onClickArgs.event = window.event;}
 210 rVal = self.onClick(onClickArgs); if (rVal === false) { return false;}
 211 }
 212 self.tabShow(tabberIndex); return false;}; tabberObj.prototype.tabHideAll = function()
 213 { var i; for (i = 0; i < this.tabs.length; i++) { this.tabHide(i);}
 214 }; tabberObj.prototype.tabHide = function(tabberIndex)
 215 { var div; if (!this.tabs[tabberIndex]) { return false;}
 216 div = this.tabs[tabberIndex].div; if (!div.className.match(this.REclassTabHide)) { div.className += ' ' + this.classTabHide;}
 217 this.navClearActive(tabberIndex); return this;}; tabberObj.prototype.tabShow = function(tabberIndex)
 218 { var div; if (!this.tabs[tabberIndex]) { return false;}
 219 this.tabHideAll(); div = this.tabs[tabberIndex].div; div.className = div.className.replace(this.REclassTabHide, ''); this.navSetActive(tabberIndex); if (typeof this.onTabDisplay == 'function') { this.onTabDisplay({'tabber':this, 'index':tabberIndex});}
 220 return this;}; tabberObj.prototype.navSetActive = function(tabberIndex)
 221 { this.tabs[tabberIndex].li.className = this.classNavActive; return this;}; tabberObj.prototype.navClearActive = function(tabberIndex)
 222 { this.tabs[tabberIndex].li.className = ''; return this;}; function tabberAutomatic(tabberArgs)
 223 { var
 224 tempObj, divs, i; if (!tabberArgs) { tabberArgs = {};}
 225 tempObj = new tabberObj(tabberArgs); divs = document.getElementsByTagName("div"); for (i=0; i < divs.length; i++) { if (divs[i].className &&
 226 divs[i].className.match(tempObj.REclassMain)) { tabberArgs.div = divs[i]; divs[i].tabber = new tabberObj(tabberArgs);}
 227 }
 228 return this;}
 229 function tabberAutomaticOnLoad(tabberArgs)
 230 { var oldOnLoad; if (!tabberArgs) { tabberArgs = {};}
 231 oldOnLoad = window.onload; if (typeof window.onload != 'function') { window.onload = function() { tabberAutomatic(tabberArgs);};} else { window.onload = function() { oldOnLoad(); tabberAutomatic(tabberArgs);};}
 232 }
 233 if (typeof tabberOptions == 'undefined') { tabberAutomaticOnLoad();} else { if (!tabberOptions['manualStartup']) { tabberAutomaticOnLoad(tabberOptions);}
 234 }
 235 '''
 236 
 237 tabber_minimized_css = '''
 238 <style type="text/css">
 239 /*--------------------------------------------------
 240   REQUIRED to hide the non-active tab content.
 241   But do not hide them in the print stylesheet!
 242   --------------------------------------------------*/
 243 .tabberlive .tabbertabhide {
 244  display:none;
 245 }
 246 
 247 /*--------------------------------------------------
 248   .tabber = before the tabber interface is set up
 249   .tabberlive = after the tabber interface is set up
 250   --------------------------------------------------*/
 251 .tabber {
 252 }
 253 .tabberlive {
 254  margin-top:1em;
 255 }
 256 
 257 /*--------------------------------------------------
 258   ul.tabbernav = the tab navigation list
 259   li.tabberactive = the active tab
 260   --------------------------------------------------*/
 261 ul.tabbernav
 262 {
 263  margin:0;
 264  padding: 3px 0;
 265  border-bottom: 1px solid #778;
 266  font: bold 12px Verdana, sans-serif;
 267 }
 268 
 269 ul.tabbernav li
 270 {
 271  list-style: none;
 272  margin: 0;
 273  display: inline;
 274 }
 275 
 276 ul.tabbernav li a
 277 {
 278  padding: 3px 0.5em;
 279  margin-left: 3px;
 280  border: 1px solid #778;
 281  border-bottom: none;
 282  background: #DDE;
 283  text-decoration: none;
 284 }
 285 
 286 ul.tabbernav li a:link { color: #448; }
 287 ul.tabbernav li a:visited { color: #667; }
 288 
 289 ul.tabbernav li a:hover
 290 {
 291  color: #000;
 292  background: #AAE;
 293  border-color: #227;
 294 }
 295 
 296 ul.tabbernav li.tabberactive a
 297 {
 298  background-color: #fff;
 299  border-bottom: 1px solid #fff;
 300 }
 301 
 302 ul.tabbernav li.tabberactive a:hover
 303 {
 304  color: #000;
 305  background: white;
 306  border-bottom: 1px solid white;
 307 }
 308 
 309 /*--------------------------------------------------
 310   .tabbertab = the tab content
 311   Add style only after the tabber interface is set up (.tabberlive)
 312   --------------------------------------------------*/
 313 .tabberlive .tabbertab {
 314  padding:5px;
 315  border:1px solid #aaa;
 316  border-top:0;
 317 
 318  /* If you don't want the tab size changing whenever a tab is changed
 319     you can set a fixed height */
 320 
 321  /* height:200px; */
 322 
 323  /* If you set a fix height set overflow to auto and you will get a
 324     scrollbar when necessary */
 325 
 326  /* overflow:auto; */
 327 }
 328 
 329 /* If desired, hide the heading since a heading is provided by the tab */
 330 .tabberlive .tabbertab h2 {
 331  display:none;
 332 }
 333 .tabberlive .tabbertab h3 {
 334  display:none;
 335 }
 336 
 337 /* Example of using an ID to set different styles for the tabs on the page */
 338 .tabberlive#tab1 {
 339 }
 340 .tabberlive#tab2 {
 341 }
 342 .tabberlive#tab2 .tabbertab {
 343  height:200px;
 344  overflow:auto;
 345 }
 346 </style>
 347 '''
 348 
 349 class RedirectOutputRequest(RequestBase):
 350     """Redirect output to string without HTTP headers."""
 351     def __init__ (self, req, debug=False):
 352         """Prepare a compatible request."""
 353         # Setup variables which containes the output.
 354         self.output_string = u''
 355         self.error_string = u''
 356         self.sent_headers = False
 357         self.failed = 0
 358 
 359         # 1.5,1.6,1.7: req.path_info = u'/RaphaelBossek/\xd6nologe' | u'/FrontPage'
 360         # after enconde() self.path_info = '/RaphaelBossek/\xd6nologe'
 361         self.path_info = req.path_info.encode(config.charset)
 362         # 1.5,1.6,1.7: req.query_string = u'action=CreatePdfDocument' | u''
 363         self.query_string = 'action=print'
 364         if isinstance(self.query_string, unicode):
 365             raise UnicodeError('self.query_string can not be of type UNICODE for python 2.3 compatibility reasons.')
 366         # What we intent to achive is:
 367         #   self.request_uri = u'/FrontPage'
 368         #   self.url = u'localhost:8080/FrontPage?action=print'
 369         if req.query_string:
 370             # 1.5,1.6: req.request_uri = u'/FrontPage?action=CreatePdfDocument'
 371             self.request_uri = req.path_info.replace(req.query_string, self.query_string)
 372             # 1.5,1.6: req.url = u'localhost:8080/FrontPage?action=CreatePdfDocument'
 373             self.url = req.url.replace(req.query_string, self.query_string)
 374         else:
 375             self.request_uri = req.path_info
 376             self.url = req.url + '?' + self.query_string
 377         # 1.5,1.6: Not all kinds of request support SSL.
 378         if 'is_ssl' in req.__dict__:
 379             self.is_ssl = req.is_ssl
 380         else:
 381             self.is_ssl = 0
 382         # 1.5,1.6: Not all kinds of request set env.
 383         self.env = {}
 384         if 'env' in req.__dict__:
 385             # 1.5,1.6: Do not copy env otherwise UNICODE encoding does not work right.
 386             #self._setup_vars_from_std_env(req.env)
 387             self.env['AUTH_TYPE'] = req.env.get('AUTH_TYPE', '')
 388         self.http_host = req.http_host
 389         self.http_user_agent = req.http_user_agent
 390         self.request_method = None
 391         self.saved_cookie = req.saved_cookie
 392         self.remote_addr = req.remote_addr
 393         self.script_name = getattr(req, u'script_name', u'')
 394         
 395         if debug:
 396             self.error_string += """RedirectOutputRequest.__init__
 397 --------------------------
 398 req.path_info="%s"
 399 req.query_string="%s"
 400 req.url="%s"
 401 req.http_user_agent="%s"
 402 req.http_host="%s"
 403 req.saved_cookie="%s"
 404 req.remote_addr="%s"
 405 self.is_ssl=%d
 406 self.script_name="%s"
 407 --------------------------
 408 """ % (req.path_info, req.query_string, req.url, req.http_user_agent, req.http_host, req.saved_cookie, req.remote_addr, self.is_ssl, self.script_name,)
 409 
 410         self.req = req
 411         RequestBase.__init__(self)
 412 
 413     def run(self, rev = None):
 414         """Start processing the document."""
 415         # RequestBase.__init__ check dump proxy requests.
 416         # In this case the initialisiation is stopped and self.forbidden is True.
 417         if not self.forbidden:
 418             # 1.6:MoinMoin/request/__init__.py: Initialise action from environment variables not from form.
 419             self.action = 'print'
 420             if rev:
 421                 self.form[u'rev'] = [rev]
 422             RequestBase.run(self)
 423         return (self.output_string, self.error_string)
 424 
 425     def http_headers(self, *args, **kw):
 426         """Used by MoinMoin 1.5.x instead of emit_http_headers()."""
 427         self.sent_headers = True
 428     
 429     def emit_http_headers(self, *args, **kw):
 430         """Used by MoinMoin 1.6.x instaed of http_headers()."""
 431         self.sent_headers = 1
 432 
 433     def fail(self, err):
 434         """Catch if a error occurs and save the message in our string."""
 435         RequestBase.fail(self, err)
 436         if not self.error_string:
 437             self.error_string = str(err)
 438 
 439     def write(self, *data):
 440         """Catch the document in our output_string."""
 441         if self.sent_headers:
 442             if self.failed:
 443                 self.error_string += data[0]
 444             else:
 445                 self.output_string += data[0]
 446 
 447     def flush(self):
 448         pass
 449 
 450 
 451 def attachment_fsname(attachment, page, request):
 452     """Return location of attament on the file system. current_page is the relative location
 453     where attachment is used.
 454     """
 455     from MoinMoin.action import AttachFile
 456     pagename, filename = AttachFile.absoluteName(attachment, page.page_name)
 457     #self.request.log("attachment_link: url %s pagename %s filename %s" % (url, pagename, filename))
 458     fname = wikiutil.taintfilename(filename)
 459     return AttachFile.getFilename(request, pagename, fname)
 460 
 461 
 462 def shell_quote(parameter):
 463     o = parameter
 464     for c in [u' ', u'(', u')']:
 465         o = o.replace(c, u'\\' + c)
 466     return o
 467 
 468 
 469 def getEditorName(request):
 470     """Return name of the last editor."""
 471     editorname = u''
 472     if 'edit_info' in dir(request.page):
 473         editorname = request.page.edit_info().get('editor', u':').split(u':')[-1]
 474     else:
 475         # Backward compatibility before MoinMoin 1.7.0
 476         log = request.page._last_edited(request)
 477         if log:
 478             if request.cfg.show_hosts:
 479                 title = " @ %s[%s]" % (log.hostname, log.addr)
 480             else:
 481                 title = ""
 482             kind, info = log.getInterwikiEditorData(request)
 483             if kind in ['interwiki', 'email']:
 484                 if log._usercache[log.userid].__dict__.get('aliasname', u''):
 485                     editorname = log._usercache[log.userid].aliasname
 486                 else:
 487                     editorname = log._usercache[log.userid].name
 488             elif kind == 'ip':
 489                 try:
 490                     idx = info.index('.')
 491                 except ValueError:
 492                     idx = len(info)
 493                 editorname = wikiutil.escape(info[:idx])
 494     return editorname
 495 
 496 
 497 def pipeCommand(cmdstr, input=None):
 498     child_stdin, child_stdout, child_stderr = os.popen3(cmdstr, u'b')
 499     try:
 500         if input:
 501             child_stdin.write(input)
 502         child_stdin.close()
 503     except:
 504         pass
 505 
 506     child_output = child_stdout.read()
 507     child_stdout.close()
 508 
 509     child_error = child_stderr.read()
 510     child_stderr.close()
 511 
 512     if os.name in ['posix', 'mac']:
 513         try:
 514             # REMARK: Otherwise we get <defunct> processes.
 515             os.wait()
 516         except OSError, e:
 517             # 10: No child processes.
 518             if e.errno != 10:
 519                 raise
 520     return (child_output, child_error)
 521 
 522 
 523 class CreatePdfDocument(ActionBase):
 524     """Implementation of the PDF document generator."""
 525 
 526     def __init__(self, pagename, request):
 527         ActionBase.__init__(self, pagename, request)
 528         self.action_name = self.__class__.__name__
 529         #FIXME: self.pagename = None
 530         #FIXME: self.request = None
 531         #FIXME: self._ = None
 532         self.debug = False
 533         self.msg = None
 534         self.errormsgsent = False
 535         self.default_values = {
 536             #'style': u'webpage',
 537             'style': None,
 538             'format': u'pdf13',
 539             'titlefileimage': u'',
 540             'linkstyle': u'underline',
 541             'headerleft': u't',
 542             'headermiddle': u'.',
 543             'headerright': u'D',
 544             'footerleft': u'.',
 545             'footermiddle': u'/',
 546             'footerright': u'.',
 547             'tocheaderleft': u'.',
 548             'tocheadermiddle': u't',
 549             'tocheaderright': u'.',
 550             'tocfooterleft': u'.',
 551             'tocfootermiddle': u'.',
 552             'tocfooterright': u'i',
 553             'bodycolor': u'FFFFFF',
 554             'bodyimage': u'',
 555             'textcolor': u'000000',
 556             'linkcolor': u'0000E0',
 557             'size': u'legal',
 558             'user-password': u'',
 559             'owner-password': u'',
 560             'toclevels': u'3',
 561             'grayscale': u'unchecked',
 562             'title': u'checked',
 563             'duplex': u'unchecked',
 564             'landscape': u'unchecked',
 565             'usersize': u'',
 566             'margintop': u'0.50in',
 567             'marginbottom': u'0.50in',
 568             'marginleft': u'1.00in',
 569             'marginright': u'0.50in',
 570             'no-toc': u'checked',
 571             'no-links': u'checked',
 572             'firstpage': u'p1',
 573             'jpeg': u'0',
 574             'compression': u'0',
 575             'pagemode': u'outline',
 576             'pagelayout': u'single',
 577             'firstpage': u'c1',
 578             'numbered': u'checked',
 579             'encryption': u'unchecked',
 580             'permissioncopy': u'checked',
 581             'permissionprint': u'checked',
 582             'permissionannotate': u'checked',
 583             'permissionmodify': u'checked',
 584             'charset': u'iso-8859-1',
 585             'debug': u'',
 586             'rev': 0,
 587             'extra-titledocnumber': u'',
 588             'extra-titleauthor': u'',
 589             'extra-titlecopyright': u'',
 590             'pageinfo': u'unchecked',
 591             'bodyfont': u'times',
 592             'headingfont': u'helvetica',
 593             'headfootfont': u'helvetica',
 594             'fontsize': u'11.0',
 595             'headfootsize': u'11.0',
 596             'fontspacing': u'1.2',
 597             'embedfonts': u'checked',
 598             'browserwidth': u'680',
 599             'extra-dynamiccodeblock': u'checked',
 600             'extra-codeblocklinenumbers': u'checked',
 601             'htmldoc_cmd': u'htmldoc',
 602             'extra-headingastitle': u'unchecked',
 603             'extra-dynamiccodeblock-middot': u'checked',
 604             'extra-dynamiccodeblock-middotchar': u'&middot;',
 605             'extra-dynamiccodeblock-break': u'checked',
 606             'extra-dynamiccodeblock-breakchar': u'&para;',
 607         }
 608         # We have to know which values are checkboxes within the form. If a key does
 609         # not exists wihtin the form the corresponding checkbox is not checked.
 610         self.form_checkbox = []
 611         for key, value in self.default_values.items():
 612             if value in [u'checked', u'unchecked']:
 613                 self.form_checkbox += [key]
 614         self.contenttype = u'application/pdf'
 615 
 616         # This dict contains all possible values.
 617         self.valid_options = {}
 618 
 619         self.valid_options[u'tocformats'] = {
 620             u'/': self._(u'1/N,2/N Arabic page numbers'),
 621             u':': self._(u'1/C,2/C Arabic chapter page numbers'),
 622             u'1': self._(u'1,2,3,...'),
 623             u'a': self._(u'a,b,c,...'),
 624             u'A': self._(u'A,B,C,...'),
 625             u'c': self._(u'Chapter title'),
 626             u'C': self._(u'Chapter page number'),
 627             u'd': self._(u'Date'),
 628             u'D': self._(u'Date + Time'),
 629             u'h': self._(u'Heading'),
 630             u'i': self._(u'i,ii,iii,iv,...'),
 631             u'I': self._(u'I,II,III,IV,...'),
 632             u't': self._(u'Title'),
 633             u'T': self._(u'Time'),
 634             u'.': self._(u'Blank'),
 635             # TODO: Not supported yet; u'l': self._(u'Logo image'),
 636         }
 637         self.valid_options[u'style'] = {
 638             u'webpage': self._(u'webpage'),
 639             u'book': self._(u'book'),
 640             u'continuous': self._(u'continuous'),
 641         }
 642         self.valid_options[u'size'] = {
 643             u'legal': self._(u'Legal (8.5x14in)'),
 644             u'a4': self._(u'A4 (210x297mm)'),
 645             u'letter': self._(u'Letter (8.5x11in)'),
 646             u'universal': self._(u'Universal (8.27x11in)'),
 647             u'': self._(u'User defined'),
 648         }
 649         self.valid_options[u'format'] = {
 650             u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
 651             u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
 652             u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
 653             u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
 654             # TODO: Not supported yet:
 655             #u'ps1': self._(u'PostScript Level 1'),
 656             #u'ps2': self._(u'PostScript Level 2'),
 657             #u'ps3': self._(u'PostScript Level 3'),
 658         }
 659         self.valid_options[u'linkstyle'] = {
 660             u'underline': self._(u'Underline'),
 661             u'plain': self._(u'Plain'),
 662         }
 663         self.valid_options[u'firstpage'] = {
 664             u'c1': self._(u'1st chapter'),
 665             u'p1': self._(u'1st page'),
 666             u'toc': self._(u'Contents'),
 667         }
 668         self.valid_options[u'jpeg'] = {
 669             u'0': self._(u'None'),
 670             u'50': self._(u' 50% (Good)'),
 671             u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
 672             u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
 673             u'100': self._(u'100% (Best)'),
 674         }
 675         self.valid_options[u'compression'] = {
 676             u'0': self._(u'None'),
 677             u'1': self._(u'1 (Fast)'),
 678             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',
 679             u'9': self._(u'9 (Best)'),
 680         }
 681         self.valid_options[u'toclevels'] = {
 682             u'0': self._(u'None'),
 683             u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
 684         }
 685         self.valid_options[u'pagemode'] = {
 686             u'outline': self._(u'Outline'),
 687             u'document': self._(u'Document'),
 688             u'fullscreen': self._(u'Full-screen'),
 689         }
 690         self.valid_options[u'pagelayout'] = {
 691             u'single': self._(u'Single'),
 692             u'one': self._(u'One column'),
 693             u'twoleft': self._(u'Two column left'),
 694             u'tworight': self._(u'Two column right'),
 695         }
 696         self.valid_options[u'charset'] = {
 697             u'iso-8859-1': self._(u'ISO 8859-1'),
 698             u'iso-8859-2': self._(u'ISO 8859-2'),
 699             u'iso-8859-3': self._(u'ISO 8859-3'),
 700             u'iso-8859-4': self._(u'ISO 8859-4'),
 701             u'iso-8859-5': self._(u'ISO 8859-5'),
 702             u'iso-8859-6': self._(u'ISO 8859-6'),
 703             u'iso-8859-7': self._(u'ISO 8859-7'),
 704             u'iso-8859-8': self._(u'ISO 8859-8'),
 705             u'iso-8859-9': self._(u'ISO 8859-9'),
 706             u'iso-8859-14': self._(u'ISO 8859-14'),
 707             u'iso-8859-15': self._(u'ISO 8859-15'),
 708             u'cp-874': self._(u'cp-847'),
 709             u'cp-1250': self._(u'cp-1250'),
 710             u'cp-1251': self._(u'cp-1251'),
 711             u'cp-1252': self._(u'cp-1252'),
 712             u'cp-1253': self._(u'cp-1253'),
 713             u'cp-1254': self._(u'cp-1254'),
 714             u'cp-1255': self._(u'cp-1255'),
 715             u'cp-1256': self._(u'cp-1256'),
 716             u'cp-1257': self._(u'cp-1257'),
 717             u'cp-1258': self._(u'cp-1258'),
 718             u'koi-8r': self._(u'koi-8r'),
 719         }
 720         self.valid_options[u'bodyfont'] = {
 721             u'courier': self._(u'Courier'),
 722             u'helvetica': self._(u'Helvetica'),
 723             u'monospace': self._(u'Monospace'),
 724             u'sans': self._(u'Sans'),
 725             u'serif': self._(u'Serif'),
 726             u'times': self._(u'Times'),
 727         }
 728         self.valid_options[u'headingfont'] = self.valid_options[u'bodyfont']
 729         self.valid_options[u'headfootfont'] = self.valid_options[u'bodyfont']
 730         # Go through all font types and create fontname{-bold,-oblique,-boldoblique} entries.
 731         for fontname in self.valid_options[u'bodyfont'].keys():
 732             for fontstyle in [u'bold', u'oblique', u'boldoblique']:
 733                 self.valid_options[u'headfootfont'][fontname + u'-' + fontstyle] = u'%s (%s)' % (self.valid_options[u'headfootfont'][fontname], self._(fontstyle),)
 734         # Set possible font sizes.
 735         self._set_fontsize(u'headfootsize', 6, 24, 5)
 736         self._set_fontsize(u'fontsize', 4, 24, 5)
 737         self._set_fontsize(u'fontspacing', 1, 3, 1)
 738         
 739         # Set translated name of table of contents as default.
 740         self.default_values[u'toctitle'] = self._(u'Contents')
 741         
 742         self.default_values[u'titletext'] = self.pagename
 743         self.default_values[u'extra-titledocnumber'] = u'%d' % self.request.page.get_rev()[1]
 744         page_editor = self.request.page.lastEditInfo().get(u'editor', u'')
 745         self.default_values[u'extra-titleauthor'] = wikiutil.escape(getEditorName(self.request))
 746         
 747         # Make sure we create date and time strings in right format.
 748         if self.request.current_lang:
 749             self.default_values[u'language'] = self.request.current_lang
 750         elif self.request.page.language:
 751             self.default_values[u'language'] = self.request.page.language
 752         else:
 753             #self.cfg.language_default or "en"
 754             self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get(u'default_language', u'en'))
 755 
 756         self.values = {}
 757 
 758         # If the configuration variable 'createpdfdocument_validoptions' exists we update our
 759         # self.valid_options dict with these values.
 760         if getattr (self.request.cfg, u'createpdfdocument_validoptions', None):
 761             self.valid_options.update (self.request.cfg.createpdfdocument_validoptions)
 762 
 763         # If the configuration variable 'createpdfdocument_defaultvalues' exists we update our
 764         # self.default_values dict with these values.
 765         if getattr (self.request.cfg, u'createpdfdocument_defaultvalues', None):
 766             for key, value in self.request.cfg.createpdfdocument_defaultvalues.items():
 767                 self.default_values[key] = value
 768         
 769         # Scan page to extract default values.
 770         self.set_page_default_values()
 771         self.set_page_values()
 772         self.update_values(useform=False)
 773         
 774         self.fields = {
 775             'pagename': wikiutil.escape(self.pagename),
 776             'action': self.action_name,
 777             'version': __version__,
 778             'moinmoin_release': moinmoin_release,
 779 
 780             'label_input': self._(u'Input'),
 781             'label_output': self._(u'Output'),
 782             'label_page': self._(u'Page'),
 783             'label_tableofcontents': self._(u'Contents'),
 784             'label_pdf': self._(u'PDF'),
 785             'label_security': self._(u'Security'),
 786 
 787             'label_choose_style': self._(u'Choose style'),
 788             'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br />' + \
 789                                  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/>' + \
 790                                  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.'),
 791 
 792             'help_titletext': self._(u'Title of the document for the front page.'),
 793             
 794             'label_extra-headingastitle': self._(u'Heading 1 as title'),
 795             'help_extra-headingastitle': self._(u'Extract the first heading of the document and use it as title. If checked the title field has no effect.'),
 796             
 797             'label_titlefileimage': self._(u'Title file/image'),
 798             'help_titlefileimage': self._(u'The title image or HTML page. These file has to be an attachments!'),
 799             
 800             'label_extra-titledocnumber': self._(u'Version'),
 801             'help_extra-titledocnumber': self._(u'Specify document version to be displayed on the title page.'),
 802             
 803             'label_extra-titleauthor': self._(u'Author'),
 804             'help_extra-titleauthor': self._(u'Intellectual property owner of this document.'),
 805             
 806             'label_extra-titlecopyright': self._(u'Copyright'),
 807             'help_extra-titlecopyright': self._(u'Copyright notice for this document.'),
 808             
 809             'label_pageinfo': self._(u'Apply page information'),
 810             'help_pageinfo': self._(u'Information about who and when modified the document are applied at the end.'),
 811             
 812             'label_format': self._(u'Output format'),
 813             'help_format': self._(u'Specifies the output format.'),
 814 
 815             'label_outputoptions': self._(u'Output options'),
 816             'label_grayscale': self._(u'Grayscale document'),
 817             'label_titlepage': self._(u'Title page'),
 818             'label_titletext': self._(u'Title'),
 819             'label_jpeg': self._(u'JPEG big images'),
 820             'label_compression': self._(u'Compression'),
 821 
 822             'label_no-toc': self._(u'Generate a table of contents'),
 823             'help_no-toc': self._(u''),
 824 
 825             'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
 826             'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
 827 
 828             'label_numbered': self._(u'Numbered headings'),
 829             'help_numbered': self._(u'Check to number all of the headings in the document.'),
 830 
 831             'label_toctitle': self._(u'Table-of-contents title'),
 832             'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
 833 
 834             'label_left': self._(u'Left'),
 835             'label_middle': self._(u'Middle'),
 836             'label_right': self._(u'Right'),
 837 
 838             'label_tocheader': self._(u'Header of table-of-contantes page'),
 839             'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
 840 
 841             'label_tocfooter': self._(u'Footer of table-of-contantes page'),
 842             'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
 843 
 844             'label_header': self._(u'Page header'),
 845             'help_header': self._(u'Sets the page header to use on body pages.'),
 846 
 847             'label_footer': self._(u'Page footer'),
 848             'help_footer': self._(u'Sets the page footer to use on body pages.'),
 849 
 850             'label_colors': self._(u'Colors'),
 851             'label_no-links': self._(u'Create HTTP links'),
 852             'help_no-links': self._(u'Enables generation of links in PDF files.'),
 853             
 854             'label_linkstyle': self._(u'Style of HTTP links'),
 855             'help_linkstyle': self._(u''),
 856 
 857             'label_linkcolor': self._(u'HTTP links color'),
 858             'help_linkcolor': self._(u'Sets the color of links.'),
 859             
 860             'label_bodycolor': self._(u'Body color'),
 861             'help_bodycolor': self._(u'Enter the HTML color for the body (background).'),
 862             
 863             'label_bodyimage': self._(u'Body image'),
 864             'help_bodyimage': self._(u'Enter the image file for the body (background). These file has to be an attachments!'),
 865             
 866             'label_textcolor': self._(u'Text color'),
 867             'help_textcolor': self._(u'Enter the HTML color for the text.'),
 868             
 869             'label_duplex': self._(u'2-Sided'),
 870             'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
 871 
 872             'label_landscape': self._(u'Landscape'),
 873 
 874             'label_choose_size': self._(u'Choose page size'),
 875             'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
 876 
 877             'label_usersize': self._(u'User defined page size'),
 878             '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).'),
 879 
 880             'label_browserwidth': self._(u'Browser width'),
 881             'help_browserwidth': self._(u'Set the target browser width in pixels (400-1200). This determines the page scaling of images.'),
 882 
 883             'label_margin': self._(u'User defined margin'),
 884             'label_margintop': self._(u'Top'),
 885             'label_marginbottom': self._(u'Bottom'),
 886             'label_marginleft': self._(u'Left'),
 887             'label_marginright': self._(u'Right'),
 888             '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.'),
 889 
 890             'label_pagemode': self._(u'Page mode'),
 891             '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.'),
 892 
 893             'label_pagelayout': self._(u'Page layout'),
 894             '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.'),
 895 
 896             'label_firstpage': self._(u'First page'),
 897             'help_firstpage': self._(u'Choose the initial page that will be shown.'),
 898 
 899             'label_encryption': self._(u'Encryption'),
 900             'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
 901             'label_permissions': self._(u'Permissions'),
 902             'help_permissions': self._(u'Specifies the document permissions.'),
 903 
 904             'label_permissionannotate': self._(u'Annotate'),
 905             'label_permissionprint': self._(u'Print'),
 906             'label_permissionmodify': self._(u'Modify'),
 907             'label_permissioncopy': self._(u'Copy'),
 908 
 909             'label_owner-password': self._(u'Owner password'),
 910             '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.'),
 911 
 912             'label_user-password': self._(u'User password'),
 913             'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
 914 
 915             'label_expert': self._(u'Expert'),
 916             'label_language': self._(u'Language translation'),
 917             'help_language': self._(u'Specify language to use for date and time format.'),
 918 
 919             'label_extra-dynamiccodeblock': self._(u'Dynamic code block'),
 920             'help_extra-dynamiccodeblock': self._(u'Shrink code blocks on page.'),
 921             
 922             'label_extra-codeblocklinenumbers': self._(u'Lines in code block'),
 923             'help_extra-codeblocklinenumbers': self._(u'Show line numbers for code blocks.'),
 924             
 925             'label_extra-dynamiccodeblock-middot': self._(u'Use dots instead of spaces in code blocks'),
 926             'help_extra-dynamiccodeblock-middot': self._(u'Make spaces visable by dots (%s) instead of white spaces.') % self.values['extra-dynamiccodeblock-middotchar'],
 927 
 928             'label_extra-dynamiccodeblock-break': self._(u'Use a para caracter for line breaks'),
 929             'help_extra-dynamiccodeblock-break': self._(u'Make line breaks visable by a extra character (%s) at the end.') % self.values['extra-dynamiccodeblock-breakchar'],
 930             
 931             'label_debug': self._(u'Enable debugging information'),
 932             'help_debug': self._(u'Enable this feature if you searching for problems or intent to report a bug report'),
 933 
 934             'label_fonts': self._(u'Fonts'),
 935             'label_fontsize': self._(u'Base font size'),
 936             'help_fontsize': self._(u'Set the default size of text.'),
 937             'label_fontspacing': self._(u'Line spacing'),
 938             'help_fontspacing': self._(u'Set the spacing between lines of text.'),
 939             'label_bodyfont': self._(u'Body typeface'),
 940             'help_bodyfont': self._(u'Choose the default typeface (font) of text.'),
 941             'label_headingfont': self._(u'Heading typeface'),
 942             'help_headingfont': self._(u'Choose the default typeface (font) of headings.'),
 943             'label_headfootsize': self._(u'Header/Footer size'),
 944             'help_headfootsize': self._(u'Set the size of header and footer text.'),
 945             'label_headfootfont': self._(u'Header/Footer font'),
 946             'help_headfootfont': self._(u'Choose the font for header and footer text.'),
 947             'label_charset': self._(u'Charset set'),
 948             'help_charset': self._(u'Change the encoding of the text in document.'),
 949             'label_embedfonts': self._(u'Embed fonts'),
 950             'help_embedfonts': self._(u'Check to embed font in the output file.'),
 951             
 952             'label_about': self._(u'About'),
 953             'copyright': u'',
 954             'version': self._(u'Version') + u' ' + __version__,
 955 
 956             'button_generate': self._(u'Generate PDF'),
 957             'button_preview': self._(u'Preview'),
 958             'button_remember': self._(u'Remember form'),
 959             'button_cancel': self._(u'Cancel'),
 960             'button_reset': self._(u'Reset'),
 961         }
 962         self.fields['copyright'] = u"<br/>\n".join(wikiutil.escape(__doc__).split(u"\n"))
 963         self.fields.update(self.values)
 964 
 965         # Status of debug.
 966         if self.debug:
 967             self.fields[u'debug'] = u'1'
 968         else:
 969             self.fields[u'debug'] = u'0'
 970 
 971         # Go through all format strings.
 972         for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
 973             self.fields[u'choose_' + name] = self._chooseformat(name)
 974 
 975         self.fields[u'select_style'] = self._select(u'style')
 976         self.fields[u'select_format'] = self._select(u'format')
 977         self.fields[u'select_linkstyle'] = self._select(u'linkstyle')
 978         self.fields[u'select_size'] = self._select(u'size')
 979         self.fields[u'select_jpeg'] = self._select(u'jpeg')
 980         self.fields[u'select_compression'] = self._select(u'compression')
 981         self.fields[u'select_toclevels'] = self._select(u'toclevels')
 982         self.fields[u'select_pagemode'] = self._select(u'pagemode')
 983         self.fields[u'select_pagelayout'] = self._select(u'pagelayout')
 984         self.fields[u'select_firstpage'] = self._select(u'firstpage')
 985         self.fields[u'select_charset'] = self._select(u'charset')
 986         self.fields[u'select_fontsize'] = self._select(u'fontsize')
 987         self.fields[u'select_bodyfont'] = self._select(u'bodyfont')
 988         self.fields[u'select_headingfont'] = self._select(u'headingfont')
 989         self.fields[u'select_headfootsize'] = self._select(u'headfootsize')
 990         self.fields[u'select_headfootfont'] = self._select(u'headfootfont')
 991         self.fields[u'select_fontspacing'] = self._select(u'fontspacing')
 992 
 993         # Add tabber implementation.
 994         self.request.cfg.html_head += """
 995 <script type="text/javascript">
 996 <!-- //
 997 %s
 998 //-->
 999 </script>
1000 
1001 %s
1002 """ % (tabber_minimized, tabber_minimized_css,)
1003 
1004 
1005     def error_msg(self, msg):
1006         """Display error message."""
1007         self.error = msg
1008 
1009                 
1010     def fixhtmlstr(self, str):
1011         """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
1012         htmlstr = array.array('c')
1013         for c in str:
1014             if ord(c) >= 128:
1015                 htmlstr.fromstring('&#%d;' % ord(c))
1016             else:
1017                 htmlstr.fromstring(c)
1018         return htmlstr.tostring()
1019 
1020     
1021     def set_page_values(self):
1022         """Scan raw page for additional information relating PDF generation.
1023         """
1024         #pdflines = False
1025         for line in self.request.page.get_raw_body().split(u'\n'):
1026             if line[:6] == u'##pdf ' and len(line[6:]):
1027                 line = line[6:]
1028                 key = line.split()[0]
1029                 value = line[len(key) + 1:]
1030                 # Only accept known values/settings.
1031                 if key in self.default_values:
1032                     # Check if there are any restrictions for key.
1033                     if key in self.valid_options:
1034                         # Set only the value if the restrictions are confirmed.
1035                         valid_values = self.valid_options[key].keys()
1036                         if value in valid_values:
1037                             self.values[key] = value
1038                     else:
1039                         # There are no restrictions for value.
1040                         self.values[key] = value
1041             elif not line:
1042                 break
1043 
1044             
1045     def set_page_default_values(self):
1046         """Collect as mutch as possible information about this page to assume some defaults.
1047         """
1048         # We are not able to recognise if this string is part of a verbatim area.
1049         # Support for MoinMoin v1.6 [[TableOfContentes]] and v1.7 <<TableOfContents>> syntax.
1050         matchtoclvl = re.compile(r'^[\[<]{2}TableOfContents\(\s*(\d+)\s*\)[\]>]{2}')
1051         matchtoc = re.compile(r'^[\[<]{2}TableOfContents\(*\)*[\]>]{2}')
1052         matchheading = re.compile(r'^[=]+ .*[=]+$')
1053         toc_found = False
1054         heading_found = False
1055         for line in self.request.page.get_raw_body().split(u'\n'):
1056             if line[:10] == u'#language ' and not u'language' in self.values:
1057                 lang = self.make_isolang(line[10:])
1058                 if lang:
1059                     self.default_values[u'language'] = lang
1060             elif not u'toclevels' in self.values and not toc_found:
1061                 result = matchtoclvl.match(line)
1062                 if result:
1063                     toclevels = int(result.group(1).strip())
1064                     if toclevels > 4:
1065                         toclevels = 4
1066                     self.default_values[u'toclevels'] = str(toclevels)
1067                     toc_found = True
1068                 elif matchtoc.match(line):
1069                     toc_found = True
1070             elif matchheading.match(line):
1071                 heading_found = True
1072         # We assume if table-of-contents is used we intent to generate a book.
1073         if toc_found and heading_found:
1074             # Do not change style if set manually or by configuration.
1075             if self.default_values.get(u'style', None) == None:
1076                 self.default_values[u'style'] = u'book'
1077         else:
1078             self.default_values[u'style'] = u'webpage'
1079         # Do not generate a table of contents page.
1080         if self.default_values[u'style'] != u'book':
1081             # Do not change style if set manually or by configuration.
1082             self.default_values[u'no-toc'] = self.default_values.get(u'no-toc', u'unchecked')
1083  
1084             
1085     def _select (self, name, description=None):
1086         """Helper function to create a selection control."""
1087         str = u'<select name="%s" size="1">' % (name,)
1088         if not description:
1089             description = self.valid_options[name]
1090         keys = description.keys()
1091         keys.sort()
1092         for value in keys:
1093             if value == self.values[name]:
1094                 selected = u'selected'
1095             else:
1096                 selected = u''
1097             str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
1098         str += u'</select>'
1099         return str
1100 
1101     def _chooseformat (self, name):
1102         """Helper function to create left/middle/right selection controls."""
1103         str = u"""    <tr>
1104         <td class="label"><label>%s</label></td>
1105         <td><table>
1106                 <tr>
1107                     <td>%s</td>
1108                     <td>%s</td>
1109                 </tr>
1110                 <tr>
1111                     <td>%s</td>
1112                     <td>%s</td>
1113                 </tr>
1114                 <tr>
1115                     <td>%s</td>
1116                     <td>%s</td>
1117                 </tr>
1118             </table>
1119         </td>
1120         <td>%s</td>
1121     </tr>""" % (self.fields[u'label_' + name],
1122               self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
1123               self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
1124               self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
1125               self.fields[u'help_' + name],)
1126         return str
1127 
1128     def get_form_html(self, buttons_html):
1129         """MoinMoin.action.ActionBase interface function
1130         """
1131         form = u''
1132         if self.debug:
1133             form += u'<p class="warning">Debug mode activated.</p>'
1134         if not moinmoin_release[:3] in [u'1.7', u'1.6', u'1.5']:
1135             form += u'<p class="warning">This plugin was not verified with MoinMoin %s.</p>' % moinmoin_release
1136         form += """
1137 <form method="post" action="">
1138 <input type="hidden" name="action" value="%(action)s"/>
1139 <div class="tabber">
1140 <div class="tabbertab">
1141     <h3>%(label_input)s</h3>
1142     <table>
1143     <tr>
1144         <td class="label"><label>%(label_choose_style)s</label></td>
1145         <td class="content">%(select_style)s</td>
1146         <td>%(help_choose_style)s</td>
1147     </tr>
1148     <tr>
1149         <td class="label"><label>%(label_titletext)s</label></td>
1150         <td class="content"><input type="text" size="30" name="titletext" value="%(titletext)s" /></td>
1151         <td>%(help_titletext)s</td>
1152     </tr>
1153     <tr>
1154         <td class="label"><label>%(label_extra-headingastitle)s</label></td>
1155         <td><input type="checkbox" name="extra-headingastitle" value="checked" %(extra-headingastitle)s /></td>
1156         <td>%(help_extra-headingastitle)s</td>
1157     </tr>
1158     <tr>
1159         <td class="label"><label>%(label_titlefileimage)s</label></td>
1160         <td class="content"><input type="text" size="30" name="titlefileimage" value="%(titlefileimage)s" /></td>
1161         <td>%(help_titlefileimage)s</td>
1162     </tr>
1163     <tr>
1164         <td class="label"><label>%(label_extra-titledocnumber)s</label></td>
1165         <td class="content"><input type="text" size="30" name="extra-titledocnumber" value="%(extra-titledocnumber)s" /></td>
1166         <td>%(help_extra-titledocnumber)s</td>
1167     </tr>
1168     <tr>
1169         <td class="label"><label>%(label_extra-titleauthor)s</label></td>
1170         <td class="content"><input type="text" size="30" name="extra-titleauthor" value="%(extra-titleauthor)s" /></td>
1171         <td>%(help_extra-titleauthor)s</td>
1172     </tr>
1173     <tr>
1174         <td class="label"><label>%(label_extra-titlecopyright)s</label></td>
1175         <td class="content"><input type="text" size="30" name="extra-titlecopyright" value="%(extra-titlecopyright)s" /></td>
1176         <td>%(help_extra-titlecopyright)s</td>
1177     </tr>
1178     <tr>
1179         <td class="label"><label>%(label_pageinfo)s</label></td>
1180         <td class="checkbox"><input type="checkbox" name="pageinfo" value="checked" %(pageinfo)s /></td>
1181         <td>%(help_pageinfo)s</td>
1182     </tr>
1183     </table>
1184 </div>
1185 <div class="tabbertab">
1186     <h3>%(label_output)s</h3>
1187     <table>
1188     <tr>
1189         <td class="label"><label>%(label_format)s</label></td>
1190         <td class="content">%(select_format)s</td>
1191         <td>%(help_format)s</td>
1192     </tr>
1193     <tr>
1194         <td class="label"><label>%(label_outputoptions)s</label></td>
1195         <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s&nbsp;
1196             <input type="checkbox" name="title" value="checked" %(title)s />%(label_titlepage)s<br />
1197             %(label_compression)s&nbsp:&nbsp;%(select_compression)s&nbsp;
1198             %(label_jpeg)s&nbsp;%(select_jpeg)s</td>
1199     </tr>
1200     </table>
1201 </div>
1202 <div class="tabbertab">
1203     <h3>%(label_page)s</h3>
1204     <table>
1205     <tr>
1206         <td class="label"><label>%(label_choose_size)s</label></td>
1207         <td>%(select_size)s&nbsp;<br /><nobr>%(label_usersize)s&nbsp;<input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
1208         <td>%(help_choose_size)s<br />%(help_usersize)s</td>
1209     </tr>
1210     <tr>
1211         <td class="label"><label>%(label_browserwidth)s</label></td>
1212         <td class="content"><input type="text" size="30" name="browserwidth" value="%(browserwidth)s" /></td>
1213         <td>%(help_browserwidth)s</td>
1214     </tr>
1215     <tr>
1216         <td>&nbsp;</td>
1217         <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s />&nbsp;%(label_duplex)s&nbsp;
1218             <input type="checkbox" name="landscape" value="checked" %(landscape)s />&nbsp;%(label_landscape)s</td>
1219     </tr>
1220     <tr>
1221         <td class="label"><label>%(label_margin)s</label></td>
1222         <td><table><tr><td>&nbsp;</td><td><nobr><label>%(label_margintop)s</label>&nbsp;<input type="text" name="margintop" value="%(margintop)s" size="7" /></nobr></td><td>&nbsp;</td></tr>
1223             <tr><td><nobr><label>%(label_marginleft)s</label>&nbsp;<input type="text" name="marginleft" value="%(marginleft)s" size="7" /></nobr></td><td>&nbsp;</td><td><nobr><label>%(label_marginright)s</label>&nbsp;<input type="text" name="marginright" value="%(marginright)s" size="7" /></nobr></td></tr>
1224             <tr><td>&nbsp;</td><td><nobr><label>%(label_marginbottom)s</label>&nbsp;<input type="text" name="marginbottom" value="%(marginbottom)s" size="7" /></nobr></td><td>&nbsp;</td></tr></table>
1225         <td>%(help_margin)s</td>
1226     </tr>
1227     %(choose_header)s
1228     %(choose_footer)s
1229     </table>
1230 </div>
1231 <div class="tabbertab">
1232     <h3>%(label_tableofcontents)s</h3>
1233     <table>
1234     <tr>
1235         <td class="label"><label>%(label_no-toc)s</label></td>
1236         <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
1237         <td>%(help_no-toc)s</td>
1238     </tr>
1239     <tr>
1240         <td class="label"><label>%(label_toclevels)s</label></td>
1241         <td class="content">%(select_toclevels)s</td>
1242         <td>%(help_toclevels)s</td>
1243     </tr>
1244     <tr>
1245         <td>&nbsp;</td>
1246         <td><input type="checkbox" name="numbered" value="checked" %(numbered)s />&nbsp;%(label_numbered)s</td>
1247         <td>%(help_numbered)s</td>
1248     </tr>
1249     <tr>
1250         <td class="label"><label>%(label_toctitle)s</label></td>
1251         <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
1252         <td>%(help_toctitle)s</td>
1253     </tr>
1254     %(choose_tocheader)s
1255     %(choose_tocfooter)s
1256     </table>
1257 </div>
1258 <div class="tabbertab">
1259     <h3>%(label_colors)s</h3>
1260     <table>
1261     <tr>
1262         <td class="label"><label>%(label_bodycolor)s</label></td>
1263         <td class="content"><input type="text" size="6" name="bodycolor" value="%(bodycolor)s" /></td>
1264         <td>%(help_bodycolor)s</td>
1265     </tr>
1266     <tr>
1267         <td class="label"><label>%(label_bodyimage)s</label></td>
1268         <td class="content"><input type="text" size="30" name="bodyimage" value="%(bodyimage)s" /></td>
1269         <td>%(help_bodyimage)s</td>
1270     </tr>
1271     <tr>
1272         <td class="label"><label>%(label_textcolor)s</label></td>
1273         <td class="content"><input type="text" size="6" name="textcolor" value="%(textcolor)s" /></td>
1274         <td>%(help_textcolor)s</td>
1275     </tr>
1276     <tr>
1277         <td class="label"><label>%(label_linkcolor)s</label></td>
1278         <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
1279         <td>%(help_linkcolor)s</td>
1280     </tr>
1281     <tr>
1282         <td class="label"><label>%(label_linkstyle)s</label></td>
1283         <td class="content">%(select_linkstyle)s</td>
1284         <td>%(help_linkstyle)s</td>
1285     </tr>
1286     <tr>
1287         <td class="label"><label>%(label_no-links)s</label></td>
1288         <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
1289         <td>%(help_no-links)s</td>
1290     </tr>
1291     </table>
1292 </div>
1293 <div class="tabbertab">
1294     <h3>%(label_fonts)s</h3>
1295     <table>
1296     <tr>
1297         <td class="label"><label>%(label_fontsize)s</label></td>
1298         <td class="content">%(select_fontsize)s</td>
1299         <td>%(help_fontsize)s</td>
1300     </tr>
1301     <tr>
1302         <td class="label"><label>%(label_fontspacing)s</label></td>
1303         <td class="content">%(select_fontspacing)s</td>
1304         <td>%(help_fontspacing)s</td>
1305     </tr>
1306     <tr>
1307         <td class="label"><label>%(label_bodyfont)s</label></td>
1308         <td class="content">%(select_bodyfont)s</td>
1309         <td>%(help_bodyfont)s</td>
1310     </tr>
1311     <tr>
1312         <td class="label"><label>%(label_headingfont)s</label></td>
1313         <td class="content">%(select_headingfont)s</td>
1314         <td>%(help_headingfont)s</td>
1315     </tr>
1316     <tr>
1317         <td class="label"><label>%(label_headfootsize)s</label></td>
1318         <td class="content">%(select_headfootsize)s</td>
1319         <td>%(help_headfootsize)s</td>
1320     </tr>
1321     <tr>
1322         <td class="label"><label>%(label_headfootfont)s</label></td>
1323         <td class="content">%(select_headfootfont)s</td>
1324         <td>%(help_headfootfont)s</td>
1325     </tr>
1326     <tr>
1327         <td class="label"><label>%(label_charset)s</label></td>
1328         <td class="content">%(select_charset)s</td>
1329         <td>%(help_charset)s</td>
1330     </tr>
1331     <tr>
1332         <td class="label"><label>%(label_embedfonts)s</label></td>
1333         <td class="checkbox"><input type="checkbox" name="embedfonts" value="checked" %(embedfonts)s /></td>
1334         <td>%(help_embedfonts)s</td>
1335     </tr>
1336 </table>
1337 </div>
1338 <div class="tabbertab">
1339     <h3>%(label_pdf)s</h3>
1340     <table>
1341     <tr>
1342         <td class="label"><label>%(label_pagemode)s</label></td>
1343         <td class="content">%(select_pagemode)s</td>
1344         <td>%(help_pagemode)s</td>
1345     </tr>
1346     <tr>
1347         <td class="label"><label>%(label_pagelayout)s</label></td>
1348         <td class="content">%(select_pagelayout)s</td>
1349         <td>%(help_pagelayout)s</td>
1350     </tr>
1351     <tr>
1352         <td class="label"><label>%(label_firstpage)s</label></td>
1353         <td class="content">%(select_firstpage)s</td>
1354         <td>%(help_firstpage)s</td>
1355     </tr>
1356     </table>
1357 </div>
1358 <div class="tabbertab">
1359     <h3>%(label_security)s</h3>
1360     <table>
1361     <tr>
1362         <td class="label"><label>%(label_encryption)s</label></td>
1363         <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
1364         <td>%(help_numbered)s</td>
1365     </tr>
1366     <tr>
1367         <td class="label"><label>%(label_permissions)s</label></td>
1368         <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s />&nbsp;%(label_permissionprint)s</nobr>&nbsp;
1369             <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s />&nbsp;%(label_permissionmodify)s</nobr><br />
1370             <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s />&nbsp;%(label_permissioncopy)s</nobr>&nbsp;
1371             <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s />&nbsp;%(label_permissionannotate)s</nobr></td>
1372         <td>%(help_permissions)s</td>
1373     </tr>
1374     <tr>
1375         <td class="label"><label>%(label_user-password)s</label></td>
1376         <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
1377         <td>%(help_user-password)s</td>
1378     </tr>
1379     <tr>
1380         <td class="label"><label>%(label_owner-password)s</label></td>
1381         <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
1382         <td>%(help_owner-password)s</td>
1383     </tr>
1384     </table>
1385 </div>
1386 <div class="tabbertab">
1387     <h3>%(label_expert)s</h3>
1388     <table>
1389     <tr>
1390         <td class="label"><label>%(label_language)s</label></td>
1391         <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
1392         <td>%(help_language)s</td>
1393     </tr>
1394     <tr>
1395         <td class="label"><label>%(label_extra-dynamiccodeblock)s</label></td>
1396         <td class="checkbox"><input type="checkbox" name="extra-dynamiccodeblock" value="checked" %(extra-dynamiccodeblock)s /></td>
1397         <td>%(help_extra-dynamiccodeblock)s</td>
1398     </tr>
1399     <tr>
1400         <td class="label"><label>%(label_extra-codeblocklinenumbers)s</label></td>
1401         <td class="checkbox"><input type="checkbox" name="extra-codeblocklinenumbers" value="checked" %(extra-codeblocklinenumbers)s /></td>
1402         <td>%(help_extra-codeblocklinenumbers)s</td>
1403     </tr>
1404     <tr>
1405         <td class="label"><label>%(label_extra-dynamiccodeblock-middot)s</label></td>
1406         <td class="checkbox"><input type="checkbox" name="extra-dynamiccodeblock-middot" value="checked" %(extra-dynamiccodeblock-middot)s /></td>
1407         <td>%(help_extra-dynamiccodeblock-middot)s</td>
1408     </tr>
1409     <tr>
1410         <td class="label"><label>%(label_extra-dynamiccodeblock-break)s</label></td>
1411         <td class="checkbox"><input type="checkbox" name="extra-dynamiccodeblock-break" value="checked" %(extra-dynamiccodeblock-break)s /></td>
1412         <td>%(help_extra-dynamiccodeblock-break)s</td>
1413     </tr>
1414     <tr>
1415         <td class="label"><label>%(label_debug)s</label></td>
1416         <td class="checkbox"><input type="checkbox" name="debug" value="checked" %(debug)s /></td>
1417         <td>%(help_debug)s</td>
1418     </tr>
1419     </table>
1420 </div>
1421 <div class="tabbertab">
1422     <h3>%(label_about)s</h3>
1423     <p>%(version)s (MoinMoin %(moinmoin_release)s)</p>
1424     <p>%(copyright)s</p>
1425 </div>
1426 </div>
1427 <p>
1428 <div class="buttons">
1429 <input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" />
1430 <input type="submit" name="generate_from_form" value="%(button_generate)s" />&nbsp;
1431 <input type="submit" name="preview" value="%(button_preview)s" />&nbsp;
1432 <input type="submit" name="remember" value="%(button_remember)s" />&nbsp;
1433 <input type="submit" name="cancel" value="%(button_cancel)s" />&nbsp;
1434 </div>
1435 </p>
1436 </form>
1437 """ % self.fields
1438         return form
1439     
1440 
1441     def render(self):
1442         """MoinMoin.action.ActionBase: Extend support of multipple action buttons
1443         """
1444         #del self.form[self.form_trigger]
1445         for buttonname in [u'generate', u'generate_from_form', u'preview', u'remember']:
1446             if buttonname in self.form:
1447                 self.form[self.form_trigger] = '1'
1448         # Continue with super function
1449         ActionBase.render(self)
1450 
1451 
1452     def do_action(self):
1453         """MoinMoin.action.ActionBase: Main dispatcher for the action.
1454         """
1455         # Canceled by user.
1456         #if self.form.has_key(u'cancel'):
1457         #    # Backward compatiblity with MoinMoin 1.5.
1458         #    if Page.send_page.func_code.co_varnames[1] == "request":
1459         #        return self.request.page.send_page(self.request)
1460         #    else:
1461         #        return self.request.page.send_page()
1462 
1463         # Determine calling parameters.
1464         if self.form.get(u'debug', [u'0'])[0] in [u'checked', u'1']:
1465             self.debug = True
1466 
1467         # Create a PDF document direct without any user iteraction from default and page settings.
1468         if self.form.has_key(u'generate'):
1469             self.set_page_values()
1470             self.update_values(useform=False)
1471             return self.do_action_generate()
1472             
1473         # Create a PDF document from form settings.
1474         if self.form.has_key(u'generate_from_form') or self.form.has_key(u'preview'):
1475             self.update_values()
1476             return self.do_action_generate()
1477         
1478         # Display a message with instructions.
1479         if self.form.has_key(u'remember'):
1480             self.update_values()
1481             return self.do_action_remember()
1482     
1483         return (True, None)
1484 
1485 
1486     def do_action_finish(self, success):
1487         """Overwrite default processing in case of success where application/pdf mime document is send instead of default page."""
1488         if self.error:
1489             ActionBase.do_action_finish(self, False)
1490 
1491 
1492     def update_values(self, useform=True):
1493         """Preset values with they form values or defaults."""
1494         if useform:
1495             pass
1496         for key, default in self.default_values.items():
1497             # Skip settings which can't be modified by form.
1498             if useform and key in ['htmldoc_cmd', 'extra-dynamiccodeblock-middotchar', 'extra-dynamiccodeblock-breakchar']:
1499                 continue
1500             # Modify value only if not already set.
1501             if not key in self.values or useform:
1502                 # If the form does not contain the value (e.g. for checkboxes) set the
1503                 # default value (e.g. for checkboxes unset by default).
1504                 if not key in self.form:
1505                     # Special processing for checkboxes in forms. If the key does not exists
1506                     # within the form it is not checked.
1507                     if key in self.form_checkbox and useform:
1508                         self.values[key] = u'unchecked'
1509                     elif useform:
1510                         # Edit fields are missing if they are empty.
1511                         self.values[key] = u''
1512                     else:
1513                         self.values[key] = default
1514                 else:
1515                     self.values[key] = self.form[key][0]
1516         # Check if revision is an integer value.
1517         try:
1518             self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
1519         except:
1520             self.values[u'rev'] = self.request.page.rev
1521         # Check if page revision exists.
1522         (pagefname, realrev, exists) = self.request.page.get_rev(rev=self.values[u'rev'])
1523         if exists:
1524             self.values[u'rev'] = realrev
1525         else:
1526             # Determine latest revision number.
1527             (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
1528         # Check valid value range.
1529         try:
1530             self.values[u'browserwidth'] = int(self.values[u'browserwidth'])
1531             if self.values[u'browserwidth'] < 400 or self.values[u'browserwidth'] > 1200:
1532                 self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1533         except:
1534             self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1535         # We need a string.
1536         self.values[u'browserwidth'] = u'%d' % self.values[u'browserwidth']
1537 
1538         
1539     def do_action_generate(self):
1540         """Create PDF document."""
1541         # Generate the HTML page using MoinMoin wiki engine.
1542         html = self.get_html()
1543         if html:
1544             if self.form.has_key('preview'):
1545                 self.wrapper_emit_http_headers()
1546                 self.request.write(html)
1547             else:
1548                 pdfdata = self.html2pdf(html)
1549                 if pdfdata:
1550                     # Send as application/pdf the generated file by HTMLDOC
1551                     self.send_pdf(pdfdata)
1552                     return (True, None)
1553         
1554         return (False, self.error)
1555 
1556                     
1557     def do_action_remember(self):
1558         """Create a message containing information about how to save the form values for future reuse."""
1559         save = u''
1560         for key, value in self.values.items():
1561             if key in [u'user-password', u'owner-password', u'rev', u'debug', u'htmldoc_cmd']:
1562                 continue
1563             if key in self.default_values and value == self.default_values[key]:
1564                 continue
1565             save += u'##pdf %s %s' % (key, value,)
1566             if self.debug:
1567                 if key in self.default_values:
1568                     save += u' <-- default value is "%s" (without quotes)' % self.default_values[key]
1569                 else:
1570                     save += u' <-- keyword missing in defaults'
1571             save += u'\n'
1572         if save:
1573             msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
1574         else:
1575             msg = self._(u'All values correspond to they default. Nothing have to be saved.')
1576         
1577         return (True, msg)
1578 
1579 
1580     def wrapper_emit_http_headers(self, more_headers=[]):
1581         """Wrapper function for MoinMoin 1.5 support."""
1582         if getattr(self.request, "http_headers", False):
1583             self.request.http_headers(more_headers)
1584         else:
1585             self.request.emit_http_headers(more_headers)
1586 
1587 
1588     def send_pdf(self, data):
1589         """Send PDF file to HTTP server as inline (not attachment).
1590         Refer to Page.send_raw() for an example of implementation.
1591         """
1592         filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
1593 
1594         # Send HTTP header.
1595         self.wrapper_emit_http_headers([
1596             'Content-Type: %s' % self.contenttype,
1597             'Content-Length: %d' % len(data),
1598             # TODO: fix the encoding here, plain 8 bit is not allowed
1599             # according to the RFCs There is no solution that is
1600             # compatible to IE except stripping non-ascii chars
1601             'Content-Disposition: inline; filename="%s"' % filename.encode(config.charset),
1602             ])
1603 
1604         # Send binary data.
1605         sio = StringIO.StringIO(data)
1606         shutil.copyfileobj(sio, self.request, 8192)
1607 
1608         
1609     def get_html(self):
1610         """Generate the HTML body of this page."""
1611         # Find out what the right title is.
1612         if self.values[u'extra-headingastitle'] == u'checked':
1613             matchheading = re.compile(r'^= (.*) =$')
1614             for line in self.request.page.get_raw_body().split(u'\n'):
1615                 result = matchheading.match(line)
1616                 if result:
1617                     newtitle = result.group(1).strip()
1618                     break
1619         else:
1620             newtitle = self.values['titletext']
1621         # Save page as HTML.
1622         newreq = RedirectOutputRequest(self.request, self.debug)
1623         # Do not add edit information.
1624         # Add extra meta tags.
1625         orig_html_head = self.request.cfg.html_head
1626         self.request.cfg.html_head = self.request.cfg.html_head + u"""
1627 <meta name="docnumber" content="%s">
1628 <meta name="author" content="%s">
1629 <meta name="copyright" content="%s">
1630 """ % (wikiutil.escape(self.values['extra-titledocnumber']), wikiutil.escape(self.values['extra-titleauthor']), wikiutil.escape(self.values['extra-titlecopyright']),)
1631         (html, errmsg) = newreq.run(rev = self.values.get(u'rev', None))
1632         # Restore original HTML head configuration.
1633         self.request.cfg.html_head = orig_html_head
1634         if html:
1635             html = self.fixhtmlstr(html)
1636             # Make URLs absolute.
1637             # FIXME: Until MoinMoin is not XHTML compilant we can not use a XML parser
1638             # (e.g. expat) to transform the HTML document. In the meantime we try to
1639             # achive the same with regular expressions subtitution.
1640             base = self.request.getQualifiedURL()
1641             for htmlref in [u'src', u'href']:
1642                 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
1643                 urlref = re.compile (reurlref, re.I)
1644                 for match in urlref.finditer(html):
1645                     foundref = match.groups()
1646                     html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
1647 
1648             # Rename title of the document.
1649             titletext_html = self.fixhtmlstr(wikiutil.escape(newtitle))
1650             html = re.compile(r'<title>[^<]+</title>').sub(u'<title>%s</title>' % titletext_html, html)
1651 
1652             if self.values['pageinfo'] == u'unchecked':
1653                 # Remove pageinfo by regex. There is no standard way to do that yet.
1654                 # Read the comment in ThemeBase.shouldShowPageinfo().
1655                 html = re.compile(r'<p[^>]+id="pageinfo".*</p>').sub(u'', html)
1656                 html = re.compile(r'<div id="interwiki">.*?</div>').sub(u'', html)
1657             
1658             # HTMLDOC workarround: Add borders to tables. HTMLDOC assume border="0" if not defined.
1659             html = re.compile(r'<table ').sub(u'<table border="1" cellpadding="5" ', html)
1660             
1661             # Display line numbers for code blocks without &middot;.
1662             if self.values[u'extra-dynamiccodeblock'] == u'checked':
1663                 if self.values[u'extra-codeblocklinenumbers'] == u'checked':
1664                     codeblocknr = re.compile(r'<span[^>]+class="LineNumber">([^<]+)</span>', re.IGNORECASE)
1665                     for match in codeblocknr.finditer(html):
1666                         newlinenr = u'<font color="%s">%s</font>' % (self.values[u'linkcolor'], match.group(1).replace(u' ', u'&nbsp;'),)
1667                         html = html.replace(match.group(0), newlinenr, 1)
1668                 else:
1669                     html = re.compile(r'<span[^>]+class="LineNumber">[^<]+</span>', re.IGNORECASE).sub(u'', html)
1670             
1671             # HTMLDOC: Does not support <span> so we remove them.
1672             for spanmatch in [r'<span[^>]*>', r'</span>']:
1673                 html = re.compile(spanmatch).sub(u'', html)
1674 
1675             # HTMLDOC: Does not support JavaScript
1676             html = re.compile(r'<script type="text/javascript".*?</script>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1677             
1678             # HTMLDOC does not support stylesheets.
1679             html = re.compile(r'<link rel="stylesheet".*?>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1680             
1681             # Remove page location added by &action=print.
1682             html = re.compile(r'<ul id="pagelocation">.*?</ul>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1683             
1684             # HTMLDOC workarround: There is no CSS support in HTMLDOC.
1685             tablecolor = re.compile(r'<td.*?background-color: (#......).*?>')
1686             for match in tablecolor.finditer(html):
1687                 html = html.replace(match.group(0), u'<td bgcolor="%s">' % match.group(1), 1)
1688             
1689             # HTMLDOC workarround: Handle <pre> sections over page boundries.
1690             if self.values[u'extra-dynamiccodeblock'] == u'checked':
1691                 multiplespaces = re.compile(r'( {2,})')
1692                 # Which character should be used to fill out spaces.
1693                 if self.values[u'extra-dynamiccodeblock-middot'] == u'checked':
1694                     fillchar = self.values[u'extra-dynamiccodeblock-middotchar']
1695                 else:
1696                     fillchar = u'&nbsp;'
1697                 for regexstr in [r'(<pre>)(.*?)(</pre>)', r'(<div class="codearea"[^>]*>.*?<pre[^>]*>)(.*?)(</pre>.*?</div>)']:
1698                     codesections = re.compile(regexstr, re.IGNORECASE | re.DOTALL)
1699                     for match in codesections.finditer(html):
1700                         foundref = match.groups()
1701                         presection = foundref[1]
1702                         # Search for multiple spaces and replace them by `fillchar` except the last space.
1703                         for spaces in multiplespaces.finditer(presection):
1704                             newspaces = fillchar * (len(spaces.group(1)) - 1) + u' '
1705                             presection = presection.replace (spaces.group(1), newspaces, 1)
1706                         # Go through lines and add a &para; sign at the end of eatch line.
1707                         newprelines = []
1708                         prelines = presection.split(u"\n")
1709                         if len(prelines) > 1:
1710                             for preline in prelines:
1711                                 preline = preline + self.values[u'extra-dynamiccodeblock-breakchar'] + u'<br />'
1712                                 newprelines.append(preline)
1713                         else:
1714                             newprelines = prelines
1715                         # Create a table arround an multi-line block.
1716                         if newprelines:
1717                             tablestart = u'<table border="1" bgcolor="#F3F5F7" cellpadding="5"><tr><td>'
1718                             tableend = u'</td></tr></table><br />'
1719                         else:
1720                             newprelines.append(preline)
1721                             tablestart = u''
1722                             tableend = u''
1723                         # Replace the <pre> block with new dynamic text.
1724                         html = html.replace(u''.join(foundref), u'%s<font face="Courier,Monospace">%s</font>%s' % (tablestart, u"\n".join(newprelines), tableend,), 1)
1725             # Do not suppress error messages.
1726             if errmsg:
1727                 html = u'<pre>' + errmsg + '</pre>' + html
1728         else:
1729             self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
1730         return html
1731 
1732     
1733     def make_isolang (self, language):
1734         return language + u'_' + language.upper()
1735 
1736     
1737     def html2pdf(self, html):
1738         """Create a PDF document based on the current parameters."""
1739         # Set environment variables for HTMLDOC
1740         os.environ['LANG'] = self.values[u'language']
1741         os.environ['HTMLDOC_NOCGI'] = '1'
1742         # Determine UID to access ACL protected sites too (mandatory to download attached images).
1743         htmldocopts = [self.default_values['htmldoc_cmd'], "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
1744 
1745         for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
1746             self.values[key] = self.values.get(key + u'left', u'.') + self.values.get(key + u'middle', u'.') + self.values.get(key + u'right', u'.')
1747 
1748         permissions = []
1749         for opt, value in self.values.items():
1750             # Skip alle non-HTMLDOC configuration parameters.
1751             if opt in [u'language', u'debug', u'rev', u'titletext', 'pageinfo', 'htmldoc_cmd'] or opt[:6] == u'extra-':
1752                 continue
1753             
1754             # Skip options without values.
1755             value = value.strip()
1756             if not value:
1757                 continue
1758             
1759             # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
1760             if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
1761                 continue
1762             
1763             if opt == u'titlefileimage':
1764                 # Check if we have a --titlefile or --titleimage option.
1765                 lower_value = value.lower()
1766                 dotpos = lower_value.rfind(u'.')
1767                 if lower_value[dotpos:] in [u'.bmp', u'.gif', u'.jpg', u'.jpeg', u'.png']:
1768                     opt = u'titleimage'
1769                 else:
1770                     opt = u'titlefile'
1771                 value = attachment_fsname(value, self.request.page, self.request)
1772             elif opt == u'bodyimage':
1773                 value = attachment_fsname(value, self.request.page, self.request)
1774             
1775             if opt in [u'style']:
1776                 htmldocopts += [u'--' + value]
1777             elif opt in self.form_checkbox:
1778                 if value == u'checked':
1779                     if opt[:10] == u'permission':
1780                         permissions += [opt[10:]]
1781                     # Reverse meaning of 'no-' options.
1782                     elif opt[:3] != u'no-':
1783                         htmldocopts += [u'--' + opt]
1784                 elif opt[:3] == u'no-':
1785                     htmldocopts += [u'--' + opt]
1786             elif opt[:6] == u'margin':
1787                 htmldocopts += [u'--' + opt[6:], value]
1788             else:
1789                 htmldocopts += [u'--' + opt, value]
1790         if permissions:
1791             htmldocopts += [u'--permission', u','.join (permissions)]
1792         htmldocopts += [u'-']
1793         # Do not forget to escape all spaces!
1794         eschtmldocopts = [shell_quote(arg) for arg in htmldocopts]
1795         cmdstr = u' '.join(eschtmldocopts)
1796         errmsg = None
1797 
1798         pdf = None
1799         os.environ['HTMLDOC_NOCGI'] = '1'
1800         if self.debug:
1801             self.wrapper_emit_http_headers()
1802             errmsg = self._(u'HTMLDOC command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>'
1803             cmdstr = self.default_values['htmldoc_cmd'] + u' --help'
1804             (htmldoc_help, htmldoc_err) = pipeCommand(cmdstr)
1805             errmsg += u'<p>Execute <tt>%s</tt><br /><pre>%s</pre></p>' % (wikiutil.escape(cmdstr), wikiutil.escape(htmldoc_help),)
1806             if 'env' in self.request.__dict__:
1807                 reqenv = u'%s' % wikiutil.escape(self.request.env)
1808             else:
1809                 reqenv = u'None'
1810             errmsg += u'<p>Python release %s<br />MoinMoin release <tt>%s</tt><br />CreatePdfDocument release <tt>%s</tt><br />self.request = <tt>%s</tt><br />self.request.env = <tt>%s</tt><br />os.environ = <tt>%s</tt></p>' % (wikiutil.escape(sys.version), wikiutil.escape(moinmoin_release), wikiutil.escape(__version__), wikiutil.escape(type(self.request)), wikiutil.escape(reqenv), wikiutil.escape(u"%s" % os.environ),)
1811         else:
1812             (pdf, htmldocmsg) = pipeCommand(cmdstr, html)
1813 
1814             # Check for error message on STDOUT.
1815             if pdf[:8] == u'HTMLDOC ':
1816                 htmldocmsg += pdf
1817                 pdf = None
1818 
1819             if htmldocmsg:
1820                 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
1821                       wikiutil.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
1822 
1823         # As it is difficult to get the htmldoc return code, we check for
1824         # error by checking the produced pdf length
1825         if not pdf and errmsg:
1826             if self.debug:
1827                 self.request.write(u'<html><body>%s</body></hftml>' % errmsg)
1828                 self.request.write(html)
1829             else:
1830                 self.error_msg(errmsg)
1831         elif pdf[:4] != '%PDF':
1832             self.error_msg(self._(u'Invalid PDF document generated') + wikiutil.escape(pdf[:80]))
1833             pdf = None
1834         
1835         return pdf
1836 
1837     
1838     def _set_fontsize(self, key, min, max, smallstep):
1839         self.valid_options[key] = {}
1840         for fontsize_big in range(min, max):
1841             for fontsize_step in range(0, 10, smallstep):
1842                 fontsize = u'%d.%d' % (fontsize_big, fontsize_step,)
1843                 self.valid_options[key][fontsize] = self._(fontsize)
1844         self.valid_options[key][u'%d.0' % max] = self._(u'%d.0' % max)
1845 
1846 
1847 def execute(pagename, request):
1848     CreatePdfDocument(pagename, request).render()

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.