Attachment 'CreatePdfDocument2_3_0+p2.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+p2'
  25 
  26 release_notes = """
  27 2008-06-25  RaphaelBossek
  28 * Release v2.3.0+p2
  29 * FIX: Remove interwiki part of author name.
  30 * FIX: RedirectOutputRequest is able to catch error messages while initialisation, e.g. if forbidden.
  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         # RequestBase.__init__ check dump proxy requests.
 389         # In this case the initialisiation is stopped and self.forbidden is True.
 390         if not self.forbidden:
 391             # 1.6:MoinMoin/request/__init__.py: Initialise action from environment variables not from form.
 392             self.action = 'print'
 393             if rev:
 394                 self.form[u'rev'] = [rev]
 395             RequestBase.run(self)
 396         return (self.output_string, self.error_string)
 397 
 398     def http_headers(self, *args, **kw):
 399         """Used by MoinMoin 1.5.x instead of emit_http_headers()."""
 400         self.sent_headers = True
 401     
 402     def emit_http_headers(self, *args, **kw):
 403         """Used by MoinMoin 1.6.x instaed of http_headers()."""
 404         self.sent_headers = 1
 405 
 406     def fail(self, err):
 407         """Catch if a error occurs and save the message in our string."""
 408         RequestBase.fail(self, err)
 409         if not self.error_string:
 410             self.error_string = str(err)
 411 
 412     def write(self, *data):
 413         """Catch the document in our output_string."""
 414         if self.sent_headers:
 415             if self.failed:
 416                 self.error_string += data[0]
 417             else:
 418                 self.output_string += data[0]
 419 
 420     def flush(self):
 421         pass
 422 
 423 
 424 def attachment_fsname(attachment, page, request):
 425     """Return location of attament on the file system. current_page is the relative location
 426     where attachment is used.
 427     """
 428     from MoinMoin.action import AttachFile
 429     pagename, filename = AttachFile.absoluteName(attachment, page.page_name)
 430     #self.request.log("attachment_link: url %s pagename %s filename %s" % (url, pagename, filename))
 431     fname = wikiutil.taintfilename(filename)
 432     return AttachFile.getFilename(request, pagename, fname)
 433 
 434 
 435 def shell_quote(parameter):
 436     o = parameter
 437     for c in [u' ', u'(', u')']:
 438         o = o.replace(c, u'\\' + c)
 439     return o
 440 
 441 
 442 def getEditorName(request):
 443     """Return name of the last editor."""
 444     editorname = u''
 445     if 'edit_info' in dir(request.page):
 446         editorname = request.page.edit_info().get('editor', u':').split(u':')[-1]
 447     else:
 448         # Backward compatibility before MoinMoin 1.7.0
 449         log = request.page._last_edited(request)
 450         if log:
 451             if request.cfg.show_hosts:
 452                 title = " @ %s[%s]" % (log.hostname, log.addr)
 453             else:
 454                 title = ""
 455             kind, info = log.getInterwikiEditorData(request)
 456             if kind in ['interwiki', 'email']:
 457                 if log._usercache[log.userid].__dict__.get('aliasname', u''):
 458                     editorname = log._usercache[log.userid].aliasname
 459                 else:
 460                     editorname = log._usercache[log.userid].name
 461             elif kind == 'ip':
 462                 try:
 463                     idx = info.index('.')
 464                 except ValueError:
 465                     idx = len(info)
 466                 editorname = wikiutil.escape(info[:idx])
 467     return editorname
 468 
 469 
 470 def pipeCommand(cmdstr, input=None):
 471     child_stdin, child_stdout, child_stderr = os.popen3(cmdstr, u'b')
 472     try:
 473         if input:
 474             child_stdin.write(input)
 475         child_stdin.close()
 476     except:
 477         pass
 478 
 479     child_output = child_stdout.read()
 480     child_stdout.close()
 481 
 482     child_error = child_stderr.read()
 483     child_stderr.close()
 484 
 485     if os.name in ['posix', 'mac']:
 486         try:
 487             # REMARK: Otherwise we get <defunct> processes.
 488             os.wait()
 489         except OSError, e:
 490             # 10: No child processes.
 491             if e.errno != 10:
 492                 raise
 493     return (child_output, child_error)
 494 
 495 
 496 class CreatePdfDocument(ActionBase):
 497     """Implementation of the PDF document generator."""
 498 
 499     def __init__(self, pagename, request):
 500         ActionBase.__init__(self, pagename, request)
 501         self.action_name = self.__class__.__name__
 502         #FIXME: self.pagename = None
 503         #FIXME: self.request = None
 504         #FIXME: self._ = None
 505         self.debug = False
 506         self.msg = None
 507         self.errormsgsent = False
 508         self.default_values = {
 509             #'style': u'webpage',
 510             'style': None,
 511             'format': u'pdf13',
 512             'titlefileimage': u'',
 513             'linkstyle': u'underline',
 514             'headerleft': u't',
 515             'headermiddle': u'.',
 516             'headerright': u'D',
 517             'footerleft': u'.',
 518             'footermiddle': u'/',
 519             'footerright': u'.',
 520             'tocheaderleft': u'.',
 521             'tocheadermiddle': u't',
 522             'tocheaderright': u'.',
 523             'tocfooterleft': u'.',
 524             'tocfootermiddle': u'.',
 525             'tocfooterright': u'i',
 526             'bodycolor': u'FFFFFF',
 527             'bodyimage': u'',
 528             'textcolor': u'000000',
 529             'linkcolor': u'0000E0',
 530             'size': u'legal',
 531             'user-password': u'',
 532             'owner-password': u'',
 533             'toclevels': u'3',
 534             'grayscale': u'unchecked',
 535             'title': u'checked',
 536             'duplex': u'unchecked',
 537             'landscape': u'unchecked',
 538             'usersize': u'',
 539             'margintop': u'0.50in',
 540             'marginbottom': u'0.50in',
 541             'marginleft': u'1.00in',
 542             'marginright': u'0.50in',
 543             'no-toc': u'checked',
 544             'no-links': u'checked',
 545             'firstpage': u'p1',
 546             'jpeg': u'0',
 547             'compression': u'0',
 548             'pagemode': u'outline',
 549             'pagelayout': u'single',
 550             'firstpage': u'c1',
 551             'numbered': u'checked',
 552             'encryption': u'unchecked',
 553             'permissioncopy': u'checked',
 554             'permissionprint': u'checked',
 555             'permissionannotate': u'checked',
 556             'permissionmodify': u'checked',
 557             'charset': u'iso-8859-1',
 558             'debug': u'',
 559             'rev': 0,
 560             'extra-titledocnumber': u'',
 561             'extra-titleauthor': u'',
 562             'extra-titlecopyright': u'',
 563             'pageinfo': u'unchecked',
 564             'bodyfont': u'times',
 565             'headingfont': u'helvetica',
 566             'headfootfont': u'helvetica',
 567             'fontsize': u'11.0',
 568             'headfootsize': u'11.0',
 569             'fontspacing': u'1.2',
 570             'embedfonts': u'checked',
 571             'browserwidth': u'680',
 572             'extra-dynamiccodeblock': u'checked',
 573             'extra-codeblocklinenumbers': u'checked',
 574             'htmldoc_cmd': u'htmldoc',
 575             'extra-headingastitle': u'unchecked',
 576         }
 577         # We have to know which values are checkboxes within the form. If a key does
 578         # not exists wihtin the form the corresponding checkbox is not checked.
 579         self.form_checkbox = []
 580         for key, value in self.default_values.items():
 581             if value in [u'checked', u'unchecked']:
 582                 self.form_checkbox += [key]
 583         self.contenttype = u'application/pdf'
 584 
 585         # This dict contains all possible values.
 586         self.valid_options = {}
 587 
 588         self.valid_options[u'tocformats'] = {
 589             u'/': self._(u'1/N,2/N Arabic page numbers'),
 590             u':': self._(u'1/C,2/C Arabic chapter page numbers'),
 591             u'1': self._(u'1,2,3,...'),
 592             u'a': self._(u'a,b,c,...'),
 593             u'A': self._(u'A,B,C,...'),
 594             u'c': self._(u'Chapter title'),
 595             u'C': self._(u'Chapter page number'),
 596             u'd': self._(u'Date'),
 597             u'D': self._(u'Date + Time'),
 598             u'h': self._(u'Heading'),
 599             u'i': self._(u'i,ii,iii,iv,...'),
 600             u'I': self._(u'I,II,III,IV,...'),
 601             u't': self._(u'Title'),
 602             u'T': self._(u'Time'),
 603             u'.': self._(u'Blank'),
 604             # TODO: Not supported yet; u'l': self._(u'Logo image'),
 605         }
 606         self.valid_options[u'style'] = {
 607             u'webpage': self._(u'webpage'),
 608             u'book': self._(u'book'),
 609             u'continuous': self._(u'continuous'),
 610         }
 611         self.valid_options[u'size'] = {
 612             u'legal': self._(u'Legal (8.5x14in)'),
 613             u'a4': self._(u'A4 (210x297mm)'),
 614             u'letter': self._(u'Letter (8.5x11in)'),
 615             u'universal': self._(u'Universal (8.27x11in)'),
 616             u'': self._(u'User defined'),
 617         }
 618         self.valid_options[u'format'] = {
 619             u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
 620             u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
 621             u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
 622             u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
 623             # TODO: Not supported yet:
 624             #u'ps1': self._(u'PostScript Level 1'),
 625             #u'ps2': self._(u'PostScript Level 2'),
 626             #u'ps3': self._(u'PostScript Level 3'),
 627         }
 628         self.valid_options[u'linkstyle'] = {
 629             u'underline': self._(u'Underline'),
 630             u'plain': self._(u'Plain'),
 631         }
 632         self.valid_options[u'firstpage'] = {
 633             u'c1': self._(u'1st chapter'),
 634             u'p1': self._(u'1st page'),
 635             u'toc': self._(u'Contents'),
 636         }
 637         self.valid_options[u'jpeg'] = {
 638             u'0': self._(u'None'),
 639             u'50': self._(u' 50% (Good)'),
 640             u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
 641             u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
 642             u'100': self._(u'100% (Best)'),
 643         }
 644         self.valid_options[u'compression'] = {
 645             u'0': self._(u'None'),
 646             u'1': self._(u'1 (Fast)'),
 647             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',
 648             u'9': self._(u'9 (Best)'),
 649         }
 650         self.valid_options[u'toclevels'] = {
 651             u'0': self._(u'None'),
 652             u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
 653         }
 654         self.valid_options[u'pagemode'] = {
 655             u'outline': self._(u'Outline'),
 656             u'document': self._(u'Document'),
 657             u'fullscreen': self._(u'Full-screen'),
 658         }
 659         self.valid_options[u'pagelayout'] = {
 660             u'single': self._(u'Single'),
 661             u'one': self._(u'One column'),
 662             u'twoleft': self._(u'Two column left'),
 663             u'tworight': self._(u'Two column right'),
 664         }
 665         self.valid_options[u'charset'] = {
 666             u'iso-8859-1': self._(u'ISO 8859-1'),
 667             u'iso-8859-2': self._(u'ISO 8859-2'),
 668             u'iso-8859-3': self._(u'ISO 8859-3'),
 669             u'iso-8859-4': self._(u'ISO 8859-4'),
 670             u'iso-8859-5': self._(u'ISO 8859-5'),
 671             u'iso-8859-6': self._(u'ISO 8859-6'),
 672             u'iso-8859-7': self._(u'ISO 8859-7'),
 673             u'iso-8859-8': self._(u'ISO 8859-8'),
 674             u'iso-8859-9': self._(u'ISO 8859-9'),
 675             u'iso-8859-14': self._(u'ISO 8859-14'),
 676             u'iso-8859-15': self._(u'ISO 8859-15'),
 677             u'cp-874': self._(u'cp-847'),
 678             u'cp-1250': self._(u'cp-1250'),
 679             u'cp-1251': self._(u'cp-1251'),
 680             u'cp-1252': self._(u'cp-1252'),
 681             u'cp-1253': self._(u'cp-1253'),
 682             u'cp-1254': self._(u'cp-1254'),
 683             u'cp-1255': self._(u'cp-1255'),
 684             u'cp-1256': self._(u'cp-1256'),
 685             u'cp-1257': self._(u'cp-1257'),
 686             u'cp-1258': self._(u'cp-1258'),
 687             u'koi-8r': self._(u'koi-8r'),
 688         }
 689         self.valid_options[u'bodyfont'] = {
 690             u'courier': self._(u'Courier'),
 691             u'helvetica': self._(u'Helvetica'),
 692             u'monospace': self._(u'Monospace'),
 693             u'sans': self._(u'Sans'),
 694             u'serif': self._(u'Serif'),
 695             u'times': self._(u'Times'),
 696         }
 697         self.valid_options[u'headingfont'] = self.valid_options[u'bodyfont']
 698         self.valid_options[u'headfootfont'] = self.valid_options[u'bodyfont']
 699         # Go through all font types and create fontname{-bold,-oblique,-boldoblique} entries.
 700         for fontname in self.valid_options[u'bodyfont'].keys():
 701             for fontstyle in [u'bold', u'oblique', u'boldoblique']:
 702                 self.valid_options[u'headfootfont'][fontname + u'-' + fontstyle] = u'%s (%s)' % (self.valid_options[u'headfootfont'][fontname], self._(fontstyle),)
 703         # Set possible font sizes.
 704         self._set_fontsize(u'headfootsize', 6, 24, 5)
 705         self._set_fontsize(u'fontsize', 4, 24, 5)
 706         self._set_fontsize(u'fontspacing', 1, 3, 1)
 707         
 708         # Set translated name of table of contents as default.
 709         self.default_values[u'toctitle'] = self._(u'Contents')
 710         
 711         self.default_values[u'titletext'] = self.pagename
 712         self.default_values[u'extra-titledocnumber'] = u'%d' % self.request.page.get_rev()[1]
 713         page_editor = self.request.page.lastEditInfo().get(u'editor', u'')
 714         self.default_values[u'extra-titleauthor'] = wikiutil.escape(getEditorName(self.request))
 715         
 716         # Make sure we create date and time strings in right format.
 717         if self.request.current_lang:
 718             self.default_values[u'language'] = self.request.current_lang
 719         elif self.request.page.language:
 720             self.default_values[u'language'] = self.request.page.language
 721         else:
 722             #self.cfg.language_default or "en"
 723             self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get(u'default_language', u'en'))
 724 
 725         self.values = {}
 726 
 727         # If the configuration variable 'createpdfdocument_validoptions' exists we update our
 728         # self.valid_options dict with these values.
 729         if getattr (self.request.cfg, u'createpdfdocument_validoptions', None):
 730             self.valid_options.update (self.request.cfg.createpdfdocument_validoptions)
 731 
 732         # If the configuration variable 'createpdfdocument_defaultvalues' exists we update our
 733         # self.default_values dict with these values.
 734         if getattr (self.request.cfg, u'createpdfdocument_defaultvalues', None):
 735             for key, value in self.request.cfg.createpdfdocument_defaultvalues.items():
 736                 self.default_values[key] = value
 737         
 738         # Scan page to extract default values.
 739         self.set_page_default_values()
 740         self.set_page_values()
 741         self.update_values(useform=False)
 742         
 743         self.fields = {
 744             'pagename': wikiutil.escape(self.pagename),
 745             'action': self.action_name,
 746             'version': __version__,
 747             'moinmoin_release': moinmoin_release,
 748 
 749             'label_input': self._(u'Input'),
 750             'label_output': self._(u'Output'),
 751             'label_page': self._(u'Page'),
 752             'label_tableofcontents': self._(u'Contents'),
 753             'label_pdf': self._(u'PDF'),
 754             'label_security': self._(u'Security'),
 755 
 756             'label_choose_style': self._(u'Choose style'),
 757             'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
 758                                  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/>' + \
 759                                  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.'),
 760 
 761             'help_titletext': self._(u'Title of the document for the front page.'),
 762             
 763             'label_extra-headingastitle': self._(u'Heading 1 as title'),
 764             '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.'),
 765             
 766             'label_titlefileimage': self._(u'Title file/image'),
 767             'help_titlefileimage': self._(u'The title image or HTML page. These file has to be an attachments!'),
 768             
 769             'label_extra-titledocnumber': self._(u'Version'),
 770             'help_extra-titledocnumber': self._(u'Specify document version to be displayed on the title page.'),
 771             
 772             'label_extra-titleauthor': self._(u'Author'),
 773             'help_extra-titleauthor': self._(u'Intellectual property owner of this document.'),
 774             
 775             'label_extra-titlecopyright': self._(u'Copyright'),
 776             'help_extra-titlecopyright': self._(u'Copyright notice for this document.'),
 777             
 778             'label_pageinfo': self._(u'Apply page information'),
 779             'help_pageinfo': self._(u'Information about who and when modified the document are applied at the end.'),
 780             
 781             'label_format': self._(u'Output format'),
 782             'help_format': self._(u'Specifies the output format.'),
 783 
 784             'label_outputoptions': self._(u'Output options'),
 785             'label_grayscale': self._(u'Grayscale document'),
 786             'label_titlepage': self._(u'Title page'),
 787             'label_titletext': self._(u'Title'),
 788             'label_jpeg': self._(u'JPEG big images'),
 789             'label_compression': self._(u'Compression'),
 790 
 791             'label_no-toc': self._(u'Generate a table of contents'),
 792             'help_no-toc': self._(u''),
 793 
 794             'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
 795             'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
 796 
 797             'label_numbered': self._(u'Numbered headings'),
 798             'help_numbered': self._(u'Check to number all of the headings in the document.'),
 799 
 800             'label_toctitle': self._(u'Table-of-contents title'),
 801             'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
 802 
 803             'label_left': self._(u'Left'),
 804             'label_middle': self._(u'Middle'),
 805             'label_right': self._(u'Right'),
 806 
 807             'label_tocheader': self._(u'Header of table-of-contantes page'),
 808             'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
 809 
 810             'label_tocfooter': self._(u'Footer of table-of-contantes page'),
 811             'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
 812 
 813             'label_header': self._(u'Page header'),
 814             'help_header': self._(u'Sets the page header to use on body pages.'),
 815 
 816             'label_footer': self._(u'Page footer'),
 817             'help_footer': self._(u'Sets the page footer to use on body pages.'),
 818 
 819             'label_colors': self._(u'Colors'),
 820             'label_no-links': self._(u'Create HTTP links'),
 821             'help_no-links': self._(u'Enables generation of links in PDF files.'),
 822             
 823             'label_linkstyle': self._(u'Style of HTTP links'),
 824             'help_linkstyle': self._(u''),
 825 
 826             'label_linkcolor': self._(u'HTTP links color'),
 827             'help_linkcolor': self._(u'Sets the color of links.'),
 828             
 829             'label_bodycolor': self._(u'Body color'),
 830             'help_bodycolor': self._(u'Enter the HTML color for the body (background).'),
 831             
 832             'label_bodyimage': self._(u'Body image'),
 833             'help_bodyimage': self._(u'Enter the image file for the body (background). These file has to be an attachments!'),
 834             
 835             'label_textcolor': self._(u'Text color'),
 836             'help_textcolor': self._(u'Enter the HTML color for the text.'),
 837             
 838             'label_duplex': self._(u'2-Sided'),
 839             'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
 840 
 841             'label_landscape': self._(u'Landscape'),
 842 
 843             'label_choose_size': self._(u'Choose page size'),
 844             'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
 845 
 846             'label_usersize': self._(u'User defined page size'),
 847             '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).'),
 848 
 849             'label_browserwidth': self._(u'Browser width'),
 850             'help_browserwidth': self._(u'Set the target browser width in pixels (400-1200). This determines the page scaling of images.'),
 851 
 852             'label_margin': self._(u'User defined margin'),
 853             'label_margintop': self._(u'Top'),
 854             'label_marginbottom': self._(u'Bottom'),
 855             'label_marginleft': self._(u'Left'),
 856             'label_marginright': self._(u'Right'),
 857             '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.'),
 858 
 859             'label_pagemode': self._(u'Page mode'),
 860             '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.'),
 861 
 862             'label_pagelayout': self._(u'Page layout'),
 863             '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.'),
 864 
 865             'label_firstpage': self._(u'First page'),
 866             'help_firstpage': self._(u'Choose the initial page that will be shown.'),
 867 
 868             'label_encryption': self._(u'Encryption'),
 869             'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
 870             'label_permissions': self._(u'Permissions'),
 871             'help_permissions': self._(u'Specifies the document permissions.'),
 872 
 873             'label_permissionannotate': self._(u'Annotate'),
 874             'label_permissionprint': self._(u'Print'),
 875             'label_permissionmodify': self._(u'Modify'),
 876             'label_permissioncopy': self._(u'Copy'),
 877 
 878             'label_owner-password': self._(u'Owner password'),
 879             '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.'),
 880 
 881             'label_user-password': self._(u'User password'),
 882             'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
 883 
 884             'label_expert': self._(u'Expert'),
 885             'label_language': self._(u'Language translation'),
 886             'help_language': self._(u'Specify language to use for date and time format.'),
 887 
 888             'label_extra-dynamiccodeblock': self._(u'Dynamic code block'),
 889             'help_extra-dynamiccodeblock': self._(u'Shrink code blocks on page.'),
 890             
 891             'label_extra-codeblocklinenumbers': self._(u'Lines in code block'),
 892             'help_extra-codeblocklinenumbers': self._(u'Show line numbers for code blocks.'),
 893 
 894             'label_fonts': self._(u'Fonts'),
 895             'label_fontsize': self._(u'Base font size'),
 896             'help_fontsize': self._(u'Set the default size of text.'),
 897             'label_fontspacing': self._(u'Line spacing'),
 898             'help_fontspacing': self._(u'Set the spacing between lines of text.'),
 899             'label_bodyfont': self._(u'Body typeface'),
 900             'help_bodyfont': self._(u'Choose the default typeface (font) of text.'),
 901             'label_headingfont': self._(u'Heading typeface'),
 902             'help_headingfont': self._(u'Choose the default typeface (font) of headings.'),
 903             'label_headfootsize': self._(u'Header/Footer size'),
 904             'help_headfootsize': self._(u'Set the size of header and footer text.'),
 905             'label_headfootfont': self._(u'Header/Footer font'),
 906             'help_headfootfont': self._(u'Choose the font for header and footer text.'),
 907             'label_charset': self._(u'Charset set'),
 908             'help_charset': self._(u'Change the encoding of the text in document.'),
 909             'label_embedfonts': self._(u'Embed fonts'),
 910             'help_embedfonts': self._(u'Check to embed font in the output file.'),
 911             
 912             'label_about': self._(u'About'),
 913             'copyright': u'',
 914             'version': self._(u'Version') + u' ' + __version__,
 915 
 916             'button_generate': self._(u'Generate PDF'),
 917             'button_preview': self._(u'Preview'),
 918             'button_remember': self._(u'Remember form'),
 919             'button_cancel': self._(u'Cancel'),
 920             'button_reset': self._(u'Reset'),
 921         }
 922         self.fields['copyright'] = u"<br/>\n".join(wikiutil.escape(__doc__).split(u"\n"))
 923         self.fields.update(self.values)
 924 
 925         # Status of debug.
 926         if self.debug:
 927             self.fields[u'debug'] = u'1'
 928         else:
 929             self.fields[u'debug'] = u'0'
 930 
 931         # Go through all format strings.
 932         for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
 933             self.fields[u'choose_' + name] = self._chooseformat(name)
 934 
 935         self.fields[u'select_style'] = self._select(u'style')
 936         self.fields[u'select_format'] = self._select(u'format')
 937         self.fields[u'select_linkstyle'] = self._select(u'linkstyle')
 938         self.fields[u'select_size'] = self._select(u'size')
 939         self.fields[u'select_jpeg'] = self._select(u'jpeg')
 940         self.fields[u'select_compression'] = self._select(u'compression')
 941         self.fields[u'select_toclevels'] = self._select(u'toclevels')
 942         self.fields[u'select_pagemode'] = self._select(u'pagemode')
 943         self.fields[u'select_pagelayout'] = self._select(u'pagelayout')
 944         self.fields[u'select_firstpage'] = self._select(u'firstpage')
 945         self.fields[u'select_charset'] = self._select(u'charset')
 946         self.fields[u'select_fontsize'] = self._select(u'fontsize')
 947         self.fields[u'select_bodyfont'] = self._select(u'bodyfont')
 948         self.fields[u'select_headingfont'] = self._select(u'headingfont')
 949         self.fields[u'select_headfootsize'] = self._select(u'headfootsize')
 950         self.fields[u'select_headfootfont'] = self._select(u'headfootfont')
 951         self.fields[u'select_fontspacing'] = self._select(u'fontspacing')
 952 
 953         # Add tabber implementation.
 954         self.request.cfg.html_head += """
 955 <script type="text/javascript">
 956 <!-- //
 957 %s
 958 //-->
 959 </script>
 960 
 961 %s
 962 """ % (tabber_minimized, tabber_minimized_css,)
 963 
 964 
 965     def error_msg(self, msg):
 966         """Display error message."""
 967         self.error = msg
 968 
 969                 
 970     def fixhtmlstr(self, str):
 971         """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
 972         htmlstr = array.array('c')
 973         for c in str:
 974             if ord(c) >= 128:
 975                 htmlstr.fromstring('&#%d;' % ord(c))
 976             else:
 977                 htmlstr.fromstring(c)
 978         return htmlstr.tostring()
 979 
 980     
 981     def set_page_values(self):
 982         """Scan raw page for additional information relating PDF generation.
 983         """
 984         #pdflines = False
 985         for line in self.request.page.get_raw_body().split(u'\n'):
 986             if line[:6] == u'##pdf ' and len(line[6:]):
 987                 line = line[6:]
 988                 key = line.split()[0]
 989                 value = line[len(key) + 1:]
 990                 # Only accept known values/settings.
 991                 if key in self.default_values:
 992                     # Check if there are any restrictions for key.
 993                     if key in self.valid_options:
 994                         # Set only the value if the restrictions are confirmed.
 995                         valid_values = self.valid_options[key].keys()
 996                         if value in valid_values:
 997                             self.values[key] = value
 998                     else:
 999                         # There are no restrictions for value.
1000                         self.values[key] = value
1001             elif not line:
1002                 break
1003 
1004             
1005     def set_page_default_values(self):
1006         """Collect as mutch as possible information about this page to assume some defaults.
1007         """
1008         # We are not able to recognise if this string is part of a verbatim area.
1009         # Support for MoinMoin v1.6 [[TableOfContentes]] and v1.7 <<TableOfContents>> syntax.
1010         matchtoclvl = re.compile(r'^[\[<]{2}TableOfContents\(\s*(\d+)\s*\)[\]>]{2}')
1011         matchtoc = re.compile(r'^[\[<]{2}TableOfContents\(*\)*[\]>]{2}')
1012         matchheading = re.compile(r'^[=]+ .*[=]+$')
1013         toc_found = False
1014         heading_found = False
1015         for line in self.request.page.get_raw_body().split(u'\n'):
1016             if line[:10] == u'#language ' and not u'language' in self.values:
1017                 lang = self.make_isolang(line[10:])
1018                 if lang:
1019                     self.default_values[u'language'] = lang
1020             elif not u'toclevels' in self.values and not toc_found:
1021                 result = matchtoclvl.match(line)
1022                 if result:
1023                     toclevels = int(result.group(1).strip())
1024                     if toclevels > 4:
1025                         toclevels = 4
1026                     self.default_values[u'toclevels'] = str(toclevels)
1027                     toc_found = True
1028                 elif matchtoc.match(line):
1029                     toc_found = True
1030             elif matchheading.match(line):
1031                 heading_found = True
1032         # We assume if table-of-contents is used we intent to generate a book.
1033         if toc_found and heading_found:
1034             # Do not change style if set manually or by configuration.
1035             if self.default_values.get(u'style', None) == None:
1036                 self.default_values[u'style'] = u'book'
1037         else:
1038             self.default_values[u'style'] = u'webpage'
1039         # Do not generate a table of contents page.
1040         if self.default_values[u'style'] != u'book':
1041             # Do not change style if set manually or by configuration.
1042             self.default_values[u'no-toc'] = self.default_values.get(u'no-toc', u'unchecked')
1043  
1044             
1045     def _select (self, name, description=None):
1046         """Helper function to create a selection control."""
1047         str = u'<select name="%s" size="1">' % (name,)
1048         if not description:
1049             description = self.valid_options[name]
1050         keys = description.keys()
1051         keys.sort()
1052         for value in keys:
1053             if value == self.values[name]:
1054                 selected = u'selected'
1055             else:
1056                 selected = u''
1057             str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
1058         str += u'</select>'
1059         return str
1060 
1061     def _chooseformat (self, name):
1062         """Helper function to create left/middle/right selection controls."""
1063         str = u"""    <tr>
1064         <td class="label"><label>%s</label></td>
1065         <td><table>
1066                 <tr>
1067                     <td>%s</td>
1068                     <td>%s</td>
1069                 </tr>
1070                 <tr>
1071                     <td>%s</td>
1072                     <td>%s</td>
1073                 </tr>
1074                 <tr>
1075                     <td>%s</td>
1076                     <td>%s</td>
1077                 </tr>
1078             </table>
1079         </td>
1080         <td>%s</td>
1081     </tr>""" % (self.fields[u'label_' + name],
1082               self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
1083               self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
1084               self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
1085               self.fields[u'help_' + name],)
1086         return str
1087 
1088     def get_form_html(self, buttons_html):
1089         """MoinMoin.action.ActionBase interface function
1090         """
1091         form = u''
1092         if self.debug:
1093             form += u'<p class="warning">Debug mode activated.</p>'
1094         if not moinmoin_release[:3] in [u'1.7', u'1.6', u'1.5']:
1095             form += u'<p class="warning">This plugin was not verified with MoinMoin %s.</p>' % moinmoin_release
1096         form += """
1097 <form method="post" action="">
1098 <input type="hidden" name="action" value="%(action)s"/>
1099 <div class="tabber">
1100 <div class="tabbertab">
1101     <h3>%(label_input)s</h3>
1102     <table>
1103     <tr>
1104         <td class="label"><label>%(label_choose_style)s</label></td>
1105         <td class="content">%(select_style)s</td>
1106         <td>%(help_choose_style)s</td>
1107     </tr>
1108     <tr>
1109         <td class="label"><label>%(label_titletext)s</label></td>
1110         <td class="content"><input type="text" size="30" name="titletext" value="%(titletext)s" /></td>
1111         <td>%(help_titletext)s</td>
1112     </tr>
1113     <tr>
1114         <td class="label"><label>%(label_extra-headingastitle)s</label></td>
1115         <td><input type="checkbox" name="extra-headingastitle" value="checked" %(extra-headingastitle)s /></td>
1116         <td>%(help_extra-headingastitle)s</td>
1117     </tr>
1118     <tr>
1119         <td class="label"><label>%(label_titlefileimage)s</label></td>
1120         <td class="content"><input type="text" size="30" name="titlefileimage" value="%(titlefileimage)s" /></td>
1121         <td>%(help_titlefileimage)s</td>
1122     </tr>
1123     <tr>
1124         <td class="label"><label>%(label_extra-titledocnumber)s</label></td>
1125         <td class="content"><input type="text" size="30" name="extra-titledocnumber" value="%(extra-titledocnumber)s" /></td>
1126         <td>%(help_extra-titledocnumber)s</td>
1127     </tr>
1128     <tr>
1129         <td class="label"><label>%(label_extra-titleauthor)s</label></td>
1130         <td class="content"><input type="text" size="30" name="extra-titleauthor" value="%(extra-titleauthor)s" /></td>
1131         <td>%(help_extra-titleauthor)s</td>
1132     </tr>
1133     <tr>
1134         <td class="label"><label>%(label_extra-titlecopyright)s</label></td>
1135         <td class="content"><input type="text" size="30" name="extra-titlecopyright" value="%(extra-titlecopyright)s" /></td>
1136         <td>%(help_extra-titlecopyright)s</td>
1137     </tr>
1138     <tr>
1139         <td class="label"><label>%(label_pageinfo)s</label></td>
1140         <td class="checkbox"><input type="checkbox" name="pageinfo" value="checked" %(pageinfo)s /></td>
1141         <td>%(help_pageinfo)s</td>
1142     </tr>
1143     </table>
1144 </div>
1145 <div class="tabbertab">
1146     <h3>%(label_output)s</h3>
1147     <table>
1148     <tr>
1149         <td class="label"><label>%(label_format)s</label></td>
1150         <td class="content">%(select_format)s</td>
1151         <td>%(help_format)s</td>
1152     </tr>
1153     <tr>
1154         <td class="label"><label>%(label_outputoptions)s</label></td>
1155         <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s&nbsp;
1156             <input type="checkbox" name="title" value="checked" %(title)s />%(label_titlepage)s<br />
1157             %(label_compression)s&nbsp:&nbsp;%(select_compression)s&nbsp;
1158             %(label_jpeg)s&nbsp;%(select_jpeg)s</td>
1159     </tr>
1160     </table>
1161 </div>
1162 <div class="tabbertab">
1163     <h3>%(label_page)s</h3>
1164     <table>
1165     <tr>
1166         <td class="label"><label>%(label_choose_size)s</label></td>
1167         <td>%(select_size)s&nbsp;<br /><nobr>%(label_usersize)s&nbsp;<input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
1168         <td>%(help_choose_size)s<br />%(help_usersize)s</td>
1169     </tr>
1170     <tr>
1171         <td class="label"><label>%(label_browserwidth)s</label></td>
1172         <td class="content"><input type="text" size="30" name="browserwidth" value="%(browserwidth)s" /></td>
1173         <td>%(help_browserwidth)s</td>
1174     </tr>
1175     <tr>
1176         <td>&nbsp;</td>
1177         <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s />&nbsp;%(label_duplex)s&nbsp;
1178             <input type="checkbox" name="landscape" value="checked" %(landscape)s />&nbsp;%(label_landscape)s</td>
1179     </tr>
1180     <tr>
1181         <td class="label"><label>%(label_margin)s</label></td>
1182         <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>
1183             <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>
1184             <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>
1185         <td>%(help_margin)s</td>
1186     </tr>
1187     %(choose_header)s
1188     %(choose_footer)s
1189     </table>
1190 </div>
1191 <div class="tabbertab">
1192     <h3>%(label_tableofcontents)s</h3>
1193     <table>
1194     <tr>
1195         <td class="label"><label>%(label_no-toc)s</label></td>
1196         <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
1197         <td>%(help_no-toc)s</td>
1198     </tr>
1199     <tr>
1200         <td class="label"><label>%(label_toclevels)s</label></td>
1201         <td class="content">%(select_toclevels)s</td>
1202         <td>%(help_toclevels)s</td>
1203     </tr>
1204     <tr>
1205         <td>&nbsp;</td>
1206         <td><input type="checkbox" name="numbered" value="checked" %(numbered)s />&nbsp;%(label_numbered)s</td>
1207         <td>%(help_numbered)s</td>
1208     </tr>
1209     <tr>
1210         <td class="label"><label>%(label_toctitle)s</label></td>
1211         <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
1212         <td>%(help_toctitle)s</td>
1213     </tr>
1214     %(choose_tocheader)s
1215     %(choose_tocfooter)s
1216     </table>
1217 </div>
1218 <div class="tabbertab">
1219     <h3>%(label_colors)s</h3>
1220     <table>
1221     <tr>
1222         <td class="label"><label>%(label_bodycolor)s</label></td>
1223         <td class="content"><input type="text" size="6" name="bodycolor" value="%(bodycolor)s" /></td>
1224         <td>%(help_bodycolor)s</td>
1225     </tr>
1226     <tr>
1227         <td class="label"><label>%(label_bodyimage)s</label></td>
1228         <td class="content"><input type="text" size="30" name="bodyimage" value="%(bodyimage)s" /></td>
1229         <td>%(help_bodyimage)s</td>
1230     </tr>
1231     <tr>
1232         <td class="label"><label>%(label_textcolor)s</label></td>
1233         <td class="content"><input type="text" size="6" name="textcolor" value="%(textcolor)s" /></td>
1234         <td>%(help_textcolor)s</td>
1235     </tr>
1236     <tr>
1237         <td class="label"><label>%(label_linkcolor)s</label></td>
1238         <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
1239         <td>%(help_linkcolor)s</td>
1240     </tr>
1241     <tr>
1242         <td class="label"><label>%(label_linkstyle)s</label></td>
1243         <td class="content">%(select_linkstyle)s</td>
1244         <td>%(help_linkstyle)s</td>
1245     </tr>
1246     <tr>
1247         <td class="label"><label>%(label_no-links)s</label></td>
1248         <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
1249         <td>%(help_no-links)s</td>
1250     </tr>
1251     </table>
1252 </div>
1253 <div class="tabbertab">
1254     <h3>%(label_fonts)s</h3>
1255     <table>
1256     <tr>
1257         <td class="label"><label>%(label_fontsize)s</label></td>
1258         <td class="content">%(select_fontsize)s</td>
1259         <td>%(help_fontsize)s</td>
1260     </tr>
1261     <tr>
1262         <td class="label"><label>%(label_fontspacing)s</label></td>
1263         <td class="content">%(select_fontspacing)s</td>
1264         <td>%(help_fontspacing)s</td>
1265     </tr>
1266     <tr>
1267         <td class="label"><label>%(label_bodyfont)s</label></td>
1268         <td class="content">%(select_bodyfont)s</td>
1269         <td>%(help_bodyfont)s</td>
1270     </tr>
1271     <tr>
1272         <td class="label"><label>%(label_headingfont)s</label></td>
1273         <td class="content">%(select_headingfont)s</td>
1274         <td>%(help_headingfont)s</td>
1275     </tr>
1276     <tr>
1277         <td class="label"><label>%(label_headfootsize)s</label></td>
1278         <td class="content">%(select_headfootsize)s</td>
1279         <td>%(help_headfootsize)s</td>
1280     </tr>
1281     <tr>
1282         <td class="label"><label>%(label_headfootfont)s</label></td>
1283         <td class="content">%(select_headfootfont)s</td>
1284         <td>%(help_headfootfont)s</td>
1285     </tr>
1286     <tr>
1287         <td class="label"><label>%(label_charset)s</label></td>
1288         <td class="content">%(select_charset)s</td>
1289         <td>%(help_charset)s</td>
1290     </tr>
1291     <tr>
1292         <td class="label"><label>%(label_embedfonts)s</label></td>
1293         <td class="checkbox"><input type="checkbox" name="embedfonts" value="checked" %(embedfonts)s /></td>
1294         <td>%(help_embedfonts)s</td>
1295     </tr>
1296 </table>
1297 </div>
1298 <div class="tabbertab">
1299     <h3>%(label_pdf)s</h3>
1300     <table>
1301     <tr>
1302         <td class="label"><label>%(label_pagemode)s</label></td>
1303         <td class="content">%(select_pagemode)s</td>
1304         <td>%(help_pagemode)s</td>
1305     </tr>
1306     <tr>
1307         <td class="label"><label>%(label_pagelayout)s</label></td>
1308         <td class="content">%(select_pagelayout)s</td>
1309         <td>%(help_pagelayout)s</td>
1310     </tr>
1311     <tr>
1312         <td class="label"><label>%(label_firstpage)s</label></td>
1313         <td class="content">%(select_firstpage)s</td>
1314         <td>%(help_firstpage)s</td>
1315     </tr>
1316     </table>
1317 </div>
1318 <div class="tabbertab">
1319     <h3>%(label_security)s</h3>
1320     <table>
1321     <tr>
1322         <td class="label"><label>%(label_encryption)s</label></td>
1323         <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
1324         <td>%(help_numbered)s</td>
1325     </tr>
1326     <tr>
1327         <td class="label"><label>%(label_permissions)s</label></td>
1328         <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s />&nbsp;%(label_permissionprint)s</nobr>&nbsp;
1329             <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s />&nbsp;%(label_permissionmodify)s</nobr><br />
1330             <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s />&nbsp;%(label_permissioncopy)s</nobr>&nbsp;
1331             <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s />&nbsp;%(label_permissionannotate)s</nobr></td>
1332         <td>%(help_permissions)s</td>
1333     </tr>
1334     <tr>
1335         <td class="label"><label>%(label_user-password)s</label></td>
1336         <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
1337         <td>%(help_user-password)s</td>
1338     </tr>
1339     <tr>
1340         <td class="label"><label>%(label_owner-password)s</label></td>
1341         <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
1342         <td>%(help_owner-password)s</td>
1343     </tr>
1344     </table>
1345 </div>
1346 <div class="tabbertab">
1347     <h3>%(label_expert)s</h3>
1348     <table>
1349     <tr>
1350         <td class="label"><label>%(label_language)s</label></td>
1351         <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
1352         <td>%(help_language)s</td>
1353     </tr>
1354     <tr>
1355         <td class="label"><label>%(label_extra-dynamiccodeblock)s</label></td>
1356         <td class="checkbox"><input type="checkbox" name="extra-dynamiccodeblock" value="checked" %(extra-dynamiccodeblock)s /></td>
1357         <td>%(help_extra-dynamiccodeblock)s</td>
1358     </tr>
1359     <tr>
1360         <td class="label"><label>%(label_extra-codeblocklinenumbers)s</label></td>
1361         <td class="checkbox"><input type="checkbox" name="extra-codeblocklinenumbers" value="checked" %(extra-codeblocklinenumbers)s /></td>
1362         <td>%(help_extra-codeblocklinenumbers)s</td>
1363     </tr>
1364     </table>
1365 </div>
1366 <div class="tabbertab">
1367     <h3>%(label_about)s</h3>
1368     <p>%(version)s (MoinMoin %(moinmoin_release)s)</p>
1369     <p>%(copyright)s</p>
1370 </div>
1371 </div>
1372 <p>
1373 <div class="buttons">
1374 <input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" />
1375 <input type="submit" name="generate_from_form" value="%(button_generate)s" />&nbsp;
1376 <input type="submit" name="preview" value="%(button_preview)s" />&nbsp;
1377 <input type="submit" name="remember" value="%(button_remember)s" />&nbsp;
1378 <input type="submit" name="cancel" value="%(button_cancel)s" />&nbsp;
1379 </div>
1380 </p>
1381 </form>
1382 """ % self.fields
1383         return form
1384     
1385 
1386     def render(self):
1387         """MoinMoin.action.ActionBase: Extend support of multipple action buttons
1388         """
1389         #del self.form[self.form_trigger]
1390         for buttonname in [u'generate', u'generate_from_form', u'preview', u'remember']:
1391             if buttonname in self.form:
1392                 self.form[self.form_trigger] = '1'
1393         # Continue with super function
1394         ActionBase.render(self)
1395 
1396 
1397     def do_action(self):
1398         """MoinMoin.action.ActionBase: Main dispatcher for the action.
1399         """
1400         # Canceled by user.
1401         #if self.form.has_key(u'cancel'):
1402         #    # Backward compatiblity with MoinMoin 1.5.
1403         #    if Page.send_page.func_code.co_varnames[1] == "request":
1404         #        return self.request.page.send_page(self.request)
1405         #    else:
1406         #        return self.request.page.send_page()
1407 
1408         # Determine calling parameters.
1409         if self.form.get(u'debug', [u'0']) == [u'1']:
1410             self.debug = True
1411 
1412         # Create a PDF document direct without any user iteraction from default and page settings.
1413         if self.form.has_key(u'generate'):
1414             self.set_page_values()
1415             self.update_values(useform=False)
1416             return self.do_action_generate()
1417             
1418         # Create a PDF document from form settings.
1419         if self.form.has_key(u'generate_from_form') or self.form.has_key(u'preview'):
1420             self.update_values()
1421             return self.do_action_generate()
1422         
1423         # Display a message with instructions.
1424         if self.form.has_key(u'remember'):
1425             self.update_values()
1426             return self.do_action_remember()
1427     
1428         return (True, None)
1429 
1430 
1431     def do_action_finish(self, success):
1432         """Overwrite default processing in case of success where application/pdf mime document is send instead of default page."""
1433         if self.error:
1434             ActionBase.do_action_finish(self, False)
1435 
1436 
1437     def update_values(self, useform=True):
1438         """Preset values with they form values or defaults."""
1439         for key, default in self.default_values.items():
1440             # Modify value only if not already set.
1441             if not key in self.values:
1442                 # If the form does not contain the value (e.g. for checkboxes) set the
1443                 # default value (e.g. for checkboxes unset by default).
1444                 if not key in self.form:
1445                     # Special processing for checkboxes in forms. If the key does not exists
1446                     # within the form it is not checked.
1447                     if key in self.form_checkbox and useform:
1448                         self.values[key] = u'unchecked'
1449                     elif useform:
1450                         # Edit fields are missing if they are empty.
1451                         self.values[key] = u''
1452                     else:
1453                         self.values[key] = default
1454                 else:
1455                     self.values[key] = self.form[key][0]
1456         # Check if revision is an integer value.
1457         try:
1458             self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
1459         except:
1460             self.values[u'rev'] = self.request.page.rev
1461         # Check if page revision exists.
1462         (pagefname, realrev, exists) = self.request.page.get_rev(rev=self.values[u'rev'])
1463         if exists:
1464             self.values[u'rev'] = realrev
1465         else:
1466             # Determine latest revision number.
1467             (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
1468         # Check valid value range.
1469         try:
1470             self.values[u'browserwidth'] = int(self.values[u'browserwidth'])
1471             if self.values[u'browserwidth'] < 400 or self.values[u'browserwidth'] > 1200:
1472                 self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1473         except:
1474             self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1475         # We need a string.
1476         self.values[u'browserwidth'] = u'%d' % self.values[u'browserwidth']
1477 
1478         
1479     def do_action_generate(self):
1480         """Create PDF document."""
1481         # Generate the HTML page using MoinMoin wiki engine.
1482         html = self.get_html()
1483         if html:
1484             if self.form.has_key('preview'):
1485                 self.wrapper_emit_http_headers()
1486                 self.request.write(html)
1487             else:
1488                 pdfdata = self.html2pdf(html)
1489                 if pdfdata:
1490                     # Send as application/pdf the generated file by HTMLDOC
1491                     self.send_pdf(pdfdata)
1492                     return (True, None)
1493         
1494         return (False, self.error)
1495 
1496                     
1497     def do_action_remember(self):
1498         """Create a message containing information about how to save the form values for future reuse."""
1499         save = u''
1500         for key, value in self.values.items():
1501             if key in [u'user-password', u'owner-password', u'rev', u'debug', u'htmldoc_cmd']:
1502                 continue
1503             if key in self.default_values and value == self.default_values[key]:
1504                 continue
1505             save += u'##pdf %s %s' % (key, value,)
1506             if self.debug:
1507                 if key in self.default_values:
1508                     save += u' <-- default value is "%s" (without quotes)' % self.default_values[key]
1509                 else:
1510                     save += u' <-- keyword missing in defaults'
1511             save += u'\n'
1512         if save:
1513             msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
1514         else:
1515             msg = self._(u'All values correspond to they default. Nothing have to be saved.')
1516         
1517         return (True, msg)
1518 
1519 
1520     def wrapper_emit_http_headers(self, more_headers=[]):
1521         """Wrapper function for MoinMoin 1.5 support."""
1522         if getattr(self.request, "http_headers", False):
1523             self.request.http_headers(more_headers)
1524         else:
1525             self.request.emit_http_headers(more_headers)
1526 
1527 
1528     def send_pdf(self, data):
1529         """Send PDF file to HTTP server as inline (not attachment).
1530         Refer to Page.send_raw() for an example of implementation.
1531         """
1532         filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
1533 
1534         # Send HTTP header.
1535         self.wrapper_emit_http_headers([
1536             'Content-Type: %s' % self.contenttype,
1537             'Content-Length: %d' % len(data),
1538             # TODO: fix the encoding here, plain 8 bit is not allowed
1539             # according to the RFCs There is no solution that is
1540             # compatible to IE except stripping non-ascii chars
1541             'Content-Disposition: inline; filename="%s"' % filename.encode(config.charset),
1542             ])
1543 
1544         # Send binary data.
1545         sio = StringIO.StringIO(data)
1546         shutil.copyfileobj(sio, self.request, 8192)
1547 
1548         
1549     def get_html(self):
1550         """Generate the HTML body of this page."""
1551         # Find out what the right title is.
1552         if self.values[u'extra-headingastitle'] == u'checked':
1553             matchheading = re.compile(r'^= (.*) =$')
1554             for line in self.request.page.get_raw_body().split(u'\n'):
1555                 result = matchheading.match(line)
1556                 if result:
1557                     newtitle = result.group(1).strip()
1558                     break
1559         else:
1560             newtitle = self.values['titletext']
1561         # Save page as HTML.
1562         newreq = RedirectOutputRequest(self.request)
1563         # Do not add edit information.
1564         # Add extra meta tags.
1565         orig_html_head = self.request.cfg.html_head
1566         self.request.cfg.html_head = self.request.cfg.html_head + u"""
1567 <meta name="docnumber" content="%s">
1568 <meta name="author" content="%s">
1569 <meta name="copyright" content="%s">
1570 """ % (wikiutil.escape(self.values['extra-titledocnumber']), wikiutil.escape(self.values['extra-titleauthor']), wikiutil.escape(self.values['extra-titlecopyright']),)
1571         (html, errmsg) = newreq.run(rev = self.values.get(u'rev', None))
1572         # Restore original HTML head configuration.
1573         self.request.cfg.html_head = orig_html_head
1574         if html:
1575             html = self.fixhtmlstr(html)
1576             # Make URLs absolute.
1577             # FIXME: Until MoinMoin is not XHTML compilant we can not use a XML parser
1578             # (e.g. expat) to transform the HTML document. In the meantime we try to
1579             # achive the same with regular expressions subtitution.
1580             base = self.request.getQualifiedURL()
1581             for htmlref in [u'src', u'href']:
1582                 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
1583                 urlref = re.compile (reurlref, re.I)
1584                 for match in urlref.finditer(html):
1585                     foundref = match.groups()
1586                     html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
1587 
1588             # Rename title of the document.
1589             titletext_html = self.fixhtmlstr(wikiutil.escape(newtitle))
1590             html = re.compile(r'<title>[^<]+</title>').sub(u'<title>%s</title>' % titletext_html, html)
1591 
1592             if self.values['pageinfo'] == u'unchecked':
1593                 # Remove pageinfo by regex. There is no standard way to do that yet.
1594                 # Read the comment in ThemeBase.shouldShowPageinfo().
1595                 html = re.compile(r'<p[^>]+id="pageinfo".*</p>').sub(u'', html)
1596                 html = re.compile(r'<div id="interwiki">.*?</div>').sub(u'', html)
1597             
1598             # HTMLDOC workarround: Add borders to tables. HTMLDOC assume border="0" if not defined.
1599             html = re.compile(r'<table ').sub(u'<table border="1" cellpadding="5" ', html)
1600             
1601             # Display line numbers for code blocks without &middot;.
1602             if self.values[u'extra-dynamiccodeblock'] == u'checked':
1603                 if self.values[u'extra-codeblocklinenumbers'] == u'checked':
1604                     codeblocknr = re.compile(r'<span +class="LineNumber">([^<]+)</span>', re.IGNORECASE)
1605                     for match in codeblocknr.finditer(html):
1606                         newlinenr = u'<font color="%s">%s</font>' % (self.values[u'linkcolor'], match.group(1).replace(u' ', u'&nbsp;'),)
1607                         html = html.replace(match.group(0), newlinenr, 1)
1608                 else:
1609                     html = re.compile(r'<span +class="LineNumber">[^<]+</span>', re.IGNORECASE).sub(u'', html)
1610             
1611             # HTMLDOC: Does not support <span> so we remove them.
1612             for spanmatch in [r'<span[^>]*>', r'</span>']:
1613                 html = re.compile(spanmatch).sub(u'', html)
1614 
1615             # HTMLDOC: Does not support JavaScript
1616             html = re.compile(r'<script type="text/javascript".*?</script>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1617             
1618             # HTMLDOC does not support stylesheets.
1619             html = re.compile(r'<link rel="stylesheet".*?>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1620             
1621             # Remove page location added by &action=print.
1622             html = re.compile(r'<ul id="pagelocation">.*?</ul>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1623             
1624             # HTMLDOC workarround: There is no CSS support in HTMLDOC.
1625             tablecolor = re.compile(r'<td.*?background-color: (#......).*?>')
1626             for match in tablecolor.finditer(html):
1627                 html = html.replace(match.group(0), u'<td bgcolor="%s">' % match.group(1), 1)
1628             
1629             # HTMLDOC workarround: Handle <pre> sections over page boundries.
1630             if self.values[u'extra-dynamiccodeblock'] == u'checked':
1631                 multiplespaces = re.compile(r'( {2,})')
1632                 for regexstr in [r'(<pre>)(.*?)(</pre>)', r'(<div class="codearea"[^>]*>.*?<pre[^>]*>)(.*?)(</pre>.*?</div>)']:
1633                     codesections = re.compile(regexstr, re.IGNORECASE | re.DOTALL)
1634                     for match in codesections.finditer(html):
1635                         foundref = match.groups()
1636                         presection = foundref[1]
1637                         # Search for multiple spaces and replace them by &middot; except the last space.
1638                         for spaces in multiplespaces.finditer(presection):
1639                             newspaces = u'&middot;' * (len(spaces.group(1)) - 1) + u' '
1640                             presection = presection.replace (spaces.group(1), newspaces, 1)
1641                         # Go through lines and add a &para; sign at the end of eatch line.
1642                         newprelines = []
1643                         prelines = presection.split(u"\n")
1644                         if len(prelines) > 1:
1645                             for preline in prelines:
1646                                 preline = preline + u'&para;<br />'
1647                                 newprelines.append(preline)
1648                         else:
1649                             newprelines = prelines
1650                         # Create a table arround an multi-line block.
1651                         if newprelines:
1652                             tablestart = u'<table border="1" bgcolor="#F3F5F7" cellpadding="5"><tr><td>'
1653                             tableend = u'</td></tr></table><br />'
1654                         else:
1655                             newprelines.append(preline)
1656                             tablestart = u''
1657                             tableend = u''
1658                         # Replace the <pre> block with new dynamic text.
1659                         html = html.replace(u''.join(foundref), u'%s<font face="Courier,Monospace">%s</font>%s' % (tablestart, u"\n".join(newprelines), tableend,), 1)
1660         else:
1661             self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
1662         return html
1663 
1664     
1665     def make_isolang (self, language):
1666         return language + u'_' + language.upper()
1667 
1668     
1669     def html2pdf(self, html):
1670         """Create a PDF document based on the current parameters."""
1671         # Set environment variables for HTMLDOC
1672         os.environ['LANG'] = self.values[u'language']
1673         os.environ['HTMLDOC_NOCGI'] = '1'
1674         # Determine UID to access ACL protected sites too (mandatory to download attached images).
1675         htmldocopts = [self.default_values['htmldoc_cmd'], "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
1676 
1677         for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
1678             self.values[key] = self.values.get(key + u'left', u'.') + self.values.get(key + u'middle', u'.') + self.values.get(key + u'right', u'.')
1679 
1680         permissions = []
1681         for opt, value in self.values.items():
1682             # Skip alle non-HTMLDOC configuration parameters.
1683             if opt in [u'language', u'debug', u'rev', u'titletext', 'pageinfo', 'htmldoc_cmd'] or opt[:6] == u'extra-':
1684                 continue
1685             
1686             # Skip options without values.
1687             value = value.strip()
1688             if not value:
1689                 continue
1690             
1691             # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
1692             if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
1693                 continue
1694             
1695             if opt == u'titlefileimage':
1696                 # Check if we have a --titlefile or --titleimage option.
1697                 lower_value = value.lower()
1698                 dotpos = lower_value.rfind(u'.')
1699                 if lower_value[dotpos:] in [u'.bmp', u'.gif', u'.jpg', u'.jpeg', u'.png']:
1700                     opt = u'titleimage'
1701                 else:
1702                     opt = u'titlefile'
1703                 value = attachment_fsname(value, self.request.page, self.request)
1704             elif opt == u'bodyimage':
1705                 value = attachment_fsname(value, self.request.page, self.request)
1706             
1707             if opt in [u'style']:
1708                 htmldocopts += [u'--' + value]
1709             elif opt in self.form_checkbox:
1710                 if value == u'checked':
1711                     if opt[:10] == u'permission':
1712                         permissions += [opt[10:]]
1713                     # Reverse meaning of 'no-' options.
1714                     elif opt[:3] != u'no-':
1715                         htmldocopts += [u'--' + opt]
1716                 elif opt[:3] == u'no-':
1717                     htmldocopts += [u'--' + opt]
1718             elif opt[:6] == u'margin':
1719                 htmldocopts += [u'--' + opt[6:], value]
1720             else:
1721                 htmldocopts += [u'--' + opt, value]
1722         if permissions:
1723             htmldocopts += [u'--permission', u','.join (permissions)]
1724         htmldocopts += [u'-']
1725         # Do not forget to escape all spaces!
1726         eschtmldocopts = [shell_quote(arg) for arg in htmldocopts]
1727         cmdstr = u' '.join(eschtmldocopts)
1728         errmsg = None
1729 
1730         pdf = None
1731         os.environ['HTMLDOC_NOCGI'] = '1'
1732         if self.debug:
1733             self.request.http_headers()
1734             errmsg = self._(u'HTMLDOC command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>'
1735             cmdstr = self.default_values['htmldoc_cmd'] + u' --help'
1736             (htmldoc_help, htmldoc_err) = pipeCommand(cmdstr)
1737             errmsg += u'<p>Execute <tt>%s</tt><br /><pre>%s</pre></p>' % (wikiutil.escape(cmdstr), wikiutil.escape(htmldoc_help),)
1738             if 'env' in self.request.__dict__:
1739                 reqenv = u'%s' % wikiutil.escape(self.request.env)
1740             else:
1741                 reqenv = u'None'
1742             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),)
1743             errmsg += u'<hr/><hr/><hr/>'
1744         else:
1745             (pdf, htmldocmsg) = pipeCommand(cmdstr, html)
1746 
1747             # Check for error message on STDOUT.
1748             if pdf[:8] == u'HTMLDOC ':
1749                 htmldocmsg += pdf
1750                 pdf = None
1751 
1752             if htmldocmsg:
1753                 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
1754                       wikiutil.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
1755 
1756         # As it is difficult to get the htmldoc return code, we check for
1757         # error by checking the produced pdf length
1758         if not pdf and errmsg:
1759             if self.debug:
1760                 self.request.write(u'<html><body>%s</body></hftml>' % errmsg)
1761                 self.request.write(html)
1762             else:
1763                 self.error_msg(errmsg)
1764         elif pdf[:4] != '%PDF':
1765             self.error_msg(self._(u'Invalid PDF document generated') + wikiutil.escape(pdf[:80]))
1766             pdf = None
1767         
1768         return pdf
1769 
1770     
1771     def _set_fontsize(self, key, min, max, smallstep):
1772         self.valid_options[key] = {}
1773         for fontsize_big in range(min, max):
1774             for fontsize_step in range(0, 10, smallstep):
1775                 fontsize = u'%d.%d' % (fontsize_big, fontsize_step,)
1776                 self.valid_options[key][fontsize] = self._(fontsize)
1777         self.valid_options[key][u'%d.0' % max] = self._(u'%d.0' % max)
1778 
1779 
1780 def execute(pagename, request):
1781     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.