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

You are not allowed to attach a file to this page.