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

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2011-04-14 07:28:03, 4.7 KB) [[attachment:CreateNewPage.py]]
  • [get | view] (2011-04-14 07:26:24, 4.2 KB) [[attachment:CreateNewPage1.6.py]]
  • [get | view] (2006-09-10 21:29:29, 40.4 KB) [[attachment:CreatePdfDocument2_0_3.py]]
  • [get | view] (2006-09-12 06:05:06, 40.5 KB) [[attachment:CreatePdfDocument2_0_4.py]]
  • [get | view] (2006-09-12 12:00:09, 40.6 KB) [[attachment:CreatePdfDocument2_0_5.py]]
  • [get | view] (2006-11-14 21:56:11, 43.5 KB) [[attachment:CreatePdfDocument2_0_6.py]]
  • [get | view] (2006-11-15 17:00:47, 43.8 KB) [[attachment:CreatePdfDocument2_0_7.py]]
  • [get | view] (2006-11-16 22:06:18, 43.8 KB) [[attachment:CreatePdfDocument2_0_8.py]]
  • [get | view] (2006-12-17 15:54:21, 43.6 KB) [[attachment:CreatePdfDocument2_0_9.py]]
  • [get | view] (2007-08-20 09:10:23, 67.2 KB) [[attachment:CreatePdfDocument2_1_0.py]]
  • [get | view] (2007-08-21 07:39:49, 67.1 KB) [[attachment:CreatePdfDocument2_1_1.py]]
  • [get | view] (2007-09-11 19:16:37, 67.3 KB) [[attachment:CreatePdfDocument2_1_2.py]]
  • [get | view] (2007-09-18 20:17:58, 68.1 KB) [[attachment:CreatePdfDocument2_1_3.py]]
  • [get | view] (2007-09-21 13:32:54, 71.1 KB) [[attachment:CreatePdfDocument2_1_4.py]]
  • [get | view] (2007-09-23 20:56:30, 73.4 KB) [[attachment:CreatePdfDocument2_1_5.py]]
  • [get | view] (2007-09-25 20:54:48, 74.5 KB) [[attachment:CreatePdfDocument2_2_0.py]]
  • [get | view] (2008-06-23 21:08:49, 77.0 KB) [[attachment:CreatePdfDocument2_3_0.py]]
  • [get | view] (2008-06-26 19:25:07, 81.0 KB) [[attachment:CreatePdfDocument2_3_1.py]]
  • [get | view] (2008-07-06 05:50:38, 83.1 KB) [[attachment:CreatePdfDocument2_3_2.py]]
  • [get | view] (2008-07-09 17:42:02, 83.3 KB) [[attachment:CreatePdfDocument2_3_3.py]]
  • [get | view] (2008-09-07 11:11:01, 83.5 KB) [[attachment:CreatePdfDocument2_3_4.py]]
  • [get | view] (2009-01-11 15:53:09, 84.3 KB) [[attachment:CreatePdfDocument2_3_5.py]]
  • [get | view] (2009-02-16 06:52:06, 84.2 KB) [[attachment:CreatePdfDocument2_3_6.py]]
  • [get | view] (2010-01-29 11:53:21, 82.8 KB) [[attachment:CreatePdfDocument2_4_0.py]]
  • [get | view] (2010-01-31 14:10:03, 84.6 KB) [[attachment:CreatePdfDocument2_4_1.py]]
  • [get | view] (2010-09-18 16:23:23, 85.6 KB) [[attachment:CreatePdfDocument2_4_2.py]]
  • [get | view] (2006-06-16 20:56:53, 4.9 KB) [[attachment:FlashManager.py-1.5.3-1]]
  • [get | view] (2003-12-07 18:15:53, 3.9 KB) [[attachment:HTML2MoinMoin.py]]
  • [get | view] (2005-10-16 08:24:35, 4.9 KB) [[attachment:HelpOn-1.3.5-4.py]]
  • [get | view] (2006-02-03 19:21:04, 4.9 KB) [[attachment:HelpOn-1.5.1-5.py]]
  • [get | view] (2006-07-04 10:45:22, 4.8 KB) [[attachment:HelpOn-1.5.4-6.py]]
  • [get | view] (2006-07-04 22:39:14, 4.8 KB) [[attachment:HelpOn-1.6.0-7.py]]
  • [get | view] (2006-07-06 13:50:17, 4.0 KB) [[attachment:HelpOn-1.6.0-8.py]]
  • [get | view] (2008-01-10 17:43:24, 4.8 KB) [[attachment:HelpOn-1.6.0-9.py]]
  • [get | view] (2008-08-19 14:44:54, 5.0 KB) [[attachment:HelpOn-1.7.1-10.py]]
  • [get | view] (2005-02-20 18:28:34, 10.8 KB) [[attachment:IRSS.py]]
  • [get | view] (2005-03-09 22:46:23, 2.9 KB) [[attachment:ImportHtml-1.2.py]]
  • [get | view] (2003-12-07 18:15:53, 2.8 KB) [[attachment:ImportHtml.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:IrcChat.py]]
  • [get | view] (2008-06-09 11:27:20, 4.4 KB) [[attachment:MoinCrypt.py]]
  • [get | view] (2010-11-29 12:08:27, 7.5 KB) [[attachment:PageActions.py]]
  • [get | view] (2006-08-07 15:12:19, 0.5 KB) [[attachment:PermanentLink.py]]
  • [get | view] (2003-12-07 18:15:53, 6.3 KB) [[attachment:PhoneDial.py]]
  • [get | view] (2005-04-17 14:21:47, 3.6 KB) [[attachment:RecommendPage-1.3.4-1.py]]
  • [get | view] (2005-04-19 18:21:52, 5.5 KB) [[attachment:RecommendPage-1.3.4-2.py]]
  • [get | view] (2005-05-02 19:53:09, 5.6 KB) [[attachment:RecommendPage-1.3.4-3.py]]
  • [get | view] (2005-09-03 07:33:35, 6.3 KB) [[attachment:RecommendPage-1.3.4-4.py]]
  • [get | view] (2005-09-05 17:44:03, 6.9 KB) [[attachment:RecommendPage-1.3.5-5.py]]
  • [get | view] (2005-09-07 16:42:26, 7.5 KB) [[attachment:RecommendPage-1.3.5-6.py]]
  • [get | view] (2005-09-08 16:06:28, 7.7 KB) [[attachment:RecommendPage-1.3.5-7.py]]
  • [get | view] (2005-11-01 11:31:51, 9.0 KB) [[attachment:RecommendPage-1.3.5-8.py]]
  • [get | view] (2006-02-03 19:40:51, 8.3 KB) [[attachment:RecommendPage-1.5.1-9.py]]
  • [get | view] (2008-01-11 09:14:35, 6.8 KB) [[attachment:RecommendPage-1.6.0-10.py]]
  • [get | view] (2008-08-19 14:44:59, 6.9 KB) [[attachment:RecommendPage-1.7.1-11.py]]
  • [get | view] (2008-06-09 11:27:40, 1.7 KB) [[attachment:ShowActions.py]]
  • [get | view] (2008-06-09 10:34:02, 5.3 KB) [[attachment:ShowDecrypted.py]]
  • [get | view] (2005-03-30 21:17:28, 7.7 KB) [[attachment:Slideshow.py]]
  • [get | view] (2004-02-02 20:48:31, 2.0 KB) [[attachment:SubscribeUser.py]]
  • [get | view] (2007-01-26 17:08:30, 2.2 KB) [[attachment:Subscribers-1.6.0.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:Subscribers.py]]
  • [get | view] (2006-03-18 23:16:51, 0.8 KB) [[attachment:UserPreferences.py]]
  • [get | view] (2004-01-05 09:56:25, 8.1 KB) [[attachment:VisualSiteMap.py]]
  • [get | view] (2015-08-30 21:04:23, 11.1 KB) [[attachment:VisualSiteMap_1.10.py]]
  • [get | view] (2004-10-08 10:59:16, 9.3 KB) [[attachment:VisualSiteMap_1.2.py]]
  • [get | view] (2005-03-16 01:30:09, 9.8 KB) [[attachment:VisualSiteMap_1.3.py]]
  • [get | view] (2014-08-19 01:34:10, 10.8 KB) [[attachment:VisualSiteMap_1.9.py]]
  • [get | view] (2007-08-18 18:52:55, 1.0 KB) [[attachment:backlink.py]]
  • [get | view] (2007-03-15 05:53:49, 23.5 KB) [[attachment:findandreplace0.1Beta.py]]
  • [get | view] (2005-03-27 20:32:10, 3.6 KB) [[attachment:gallery2image-1.3.3-1.py]]
  • [get | view] (2005-08-03 20:14:56, 4.0 KB) [[attachment:gallery2image-1.3.3-2.py]]
  • [get | view] (2005-11-13 18:10:26, 20.7 KB) [[attachment:gallery2image-1.3.5-10.py]]
  • [get | view] (2005-11-25 22:03:50, 20.8 KB) [[attachment:gallery2image-1.3.5-11.py]]
  • [get | view] (2005-08-08 17:23:43, 8.4 KB) [[attachment:gallery2image-1.3.5-4.py]]
  • [get | view] (2005-08-13 15:15:45, 13.7 KB) [[attachment:gallery2image-1.3.5-5.py]]
  • [get | view] (2005-08-31 22:05:22, 15.5 KB) [[attachment:gallery2image-1.3.5-6.py]]
  • [get | view] (2005-10-29 20:23:50, 15.9 KB) [[attachment:gallery2image-1.3.5-8.py]]
  • [get | view] (2005-11-01 11:31:24, 17.6 KB) [[attachment:gallery2image-1.3.5-9.py]]
  • [get | view] (2006-01-27 20:52:32, 20.9 KB) [[attachment:gallery2image-1.5.1-12.py]]
  • [get | view] (2006-08-06 09:01:01, 22.1 KB) [[attachment:gallery2image-1.5.4-13.py]]
  • [get | view] (2006-08-11 18:21:40, 22.2 KB) [[attachment:gallery2image-1.5.4-14.py]]
  • [get | view] (2006-11-16 20:23:27, 22.6 KB) [[attachment:gallery2image-1.5.6-16.py]]
  • [get | view] (2006-08-11 18:30:22, 22.2 KB) [[attachment:gallery2image-1.6.0-15.py]]
  • [get | view] (2008-02-06 10:13:45, 22.3 KB) [[attachment:gallery2image-1.6.0-16.py]]
  • [get | view] (2008-05-20 15:51:09, 22.4 KB) [[attachment:gallery2image-1.6.3-17.py]]
  • [get | view] (2006-09-06 06:19:48, 1.3 KB) [[attachment:getmmap.py]]
  • [get | view] (2004-07-18 09:48:00, 1.5 KB) [[attachment:localnames.py]]
  • [get | view] (2005-03-25 15:02:31, 2.6 KB) [[attachment:newpageonly.py]]
  • [get | view] (2005-03-30 09:02:00, 3.5 KB) [[attachment:newpageonly_20050330.py]]
  • [get | view] (2006-06-06 19:12:27, 9.7 KB) [[attachment:pdf.py]]
  • [get | view] (2006-08-30 10:51:51, 36.0 KB) [[attachment:pdf2_0_0.py]]
  • [get | view] (2006-08-30 13:57:36, 36.5 KB) [[attachment:pdf2_0_1.py]]
  • [get | view] (2006-02-04 04:25:29, 1.0 KB) [[attachment:sisterindex.py]]
  • [get | view] (2004-10-28 07:33:10, 0.7 KB) [[attachment:xml.py]]
 All files | Selected Files: delete move to page copy to page

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