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