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

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.