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