Attachment 'CreatePdfDocument2_3_1.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.1'
25
26 release_notes = """
27 2008-06-25 RaphaelBossek
28 * Release v2.3.1
29 * FIX: Remove interwiki part of author name.
30 * FIX: RedirectOutputRequest is able to catch error messages while initialisation, e.g. if forbidden.
31 * FIX: Suppress of line numbers in code blocks.
32 * FIX: Processing of form values (checkbox).
33 * NEW: Added debugging support in expert mode.
34 * NEW: Possibility to set in expert tab if line breaks and middle dot character is used in dynamic blocks.
35
36 2008-06-04 RaphaelBossek
37 * Release v2.3.0
38 * Supports MoinMoin v1.7.0, v1.6.3, v1.5.8
39 * Preselect document style to webpage if no heading and table of contents is found.
40 * FIX: Missing table borders where '<table style=' was used.
41 * FIX: Cut off blocks.
42
43 2008-01-27 RaphaelBossek
44 * Release v2.2.1
45 * Remove page information for MoinMoin v1.5 (interwiki).
46 * Added support to set page title using first heading.
47 * Fix for htmldoc_cmd parameter in remember form.
48 * Style of document will be set to book if TableOfContents is found by default (if
49 not overwritten).
50
51 2007-09-25 RaphaelBossek
52 * Release v2.2.0
53 * Fixed debug information for Windows.
54 * Added support for Windows where HTMLDOC_NOCGI environment variable was missing.
55 * Added preview mode added. It's now possible to see what HTMLDOC will process.
56
57 2007-09-23 RaphaelBossek
58 * Release v2.1.5
59 * Added support for line numbers in code blocks.
60 * Remove page information so it's not printed for webpage style.
61 * Added table cell padding to 5 pixels.
62
63 2007-09-21 RaphaelBossek
64 * Release v2.1.4
65 * HTMLDOC supports only HTML 4.1 (http://www.htmldoc.org/documentation.php/Elements.html)
66 * Fix for table borders and cell background.
67 * Added support for code blocks (break on page boundries).
68
69 2007-09-18 RaphaelBossek
70 * Release v2.1.3
71 * Added support for scaling images (--browserwidth).
72
73 2007-08-21 RaphaelBossek
74 * Release v2.1.2
75 * Fixed support for python 2.3.
76
77 2007-08-21 RaphaelBossek
78 * Release v2.1.1
79 * Applied speed improvemtn patch from BrianDickman.
80 * Fixed font size values where x.9 was missing.
81 * Some cosmetic changes in the form.
82
83 2007-08-20 RaphaelBossek
84 * Release v2.1.0
85 * Configuration is seperated by tabbs.
86 * Added support for font style and colors.
87 * Fixed save/load of configuration variables within page (may be empty).
88
89 2007-08-17 RaphaelBossek
90 * Release v2.0.11
91 * Added support for alternative title page and title page logo.
92 * Added support for additional fields for the title page author, docnumber
93 and copyright.
94
95 2006-12-18 RaphaelBossek
96 * Release v2.0.10
97 * Improved support for webpage/book styles if the HTML documents does not.
98
99 2006-12-17 RaphaelBossek
100 * Release v2.0.9
101 * Added AUTH_TYPE for RedirectOutputRequest()
102
103 2006-11-16 RaphaelBossek
104 * Release v2.0.8
105 * Fixed another missing configuration for RedirectOutputRequest()
106
107 2006-11-15 RaphaelBossek
108 * Release v2.0.7
109 * Fixed support for MoinMoin 1.5.3
110 * Fixed SSL support.
111
112 2006-11-14 RaphaelBossek
113 * Release v2.0.6
114 * MoinMoin 1.6 support added.
115 * Fixed Windows(TM) platform support.
116 * Added support for alternative title text.
117
118 2006-09-13 RaphaelBossek
119 * Release v2.0.5
120 * Fixed RedirectOutputRequest class where definition of script_name
121 was missing.
122
123 2006-09-12 RaphaelBossek
124 * Release v2.0.4
125 * Fixed RedirectOutputRequest class where function was redifined
126 to boolean value.
127
128 2006-09-06 RaphaelBossek
129 * Release v2.0.3
130 * Fixed FastCGI support by removing stdout redirect output code. The
131 same functionality will be done by the RedirectOutputRequest class.
132 * Renamed to CreatePdfDocument (for better translation possibilities).
133 * Fixed encoding of title page.
134 * Added charset set option.
135 * Fixed waiting for HTMLDOC.
136
137 2006-09-02 RaphaelBossek
138 * Release v2.0.2
139 * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
140 configuration parameter support. You are not able to preset available
141 options and which defaults are used in your configuration file.
142
143 2006-08-30 RaphaelBossek
144 * Release v2.0.1
145 * Fixed issue with page revision number forwarding (traceback).
146
147 2006-08-30 RaphaelBossek
148 * Release v2.0.0
149 * Feature enchanced and bug fixed version.
150
151 2006-05-26 PascalBauermeister
152 * Release v1.0.2
153 * Relative image URLs turned absolute was bogus. It is less bogus now.
154
155 2006-05-26 PascalBauermeister
156 * Release v1.0.1
157 * Set env var HTMLDOC_NOCGI to solve CGI issue
158
159 2006-05-24 PascalBauermeister
160 * Initial release v1.0.0
161 """
162
163 import os
164 import sys
165 import stat
166 import re
167 import copy
168 import shutil
169 import StringIO
170 import array
171 from MoinMoin import config
172 from MoinMoin import util
173 from MoinMoin import wikiutil
174 from MoinMoin import packages
175 from MoinMoin import error
176 from MoinMoin.Page import Page
177 from MoinMoin.request import RequestBase
178 from MoinMoin.widget.dialog import Dialog
179 from MoinMoin.version import release as moinmoin_release
180 from MoinMoin.action import ActionBase
181
182 # http://www.barelyfitz.com/projects/tabber/
183 tabber_minimized = '''
184 // Version 1.9 stripped by Creativyst SS & JavaScript Compressor v2.2c (http://www.creativyst.com/Prod/3/)
185 function tabberObj(argsObj)
186 { 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];}
187 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;}
188 }
189 tabberObj.prototype.init = function(e)
190 { var
191 childNodes, i, i2, t, defaultTab=0, DOM_ul, DOM_li, DOM_a, aId, headingElement; if (!document.getElementsByTagName) { return false;}
192 if (e.id) { this.id = e.id;}
193 this.tabs.length = 0; childNodes = e.childNodes; for(i=0; i < childNodes.length; i++) { if(childNodes[i].className &&
194 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;}
195 }
196 }
197 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 = '';}
198 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,"");}
199 break;}
200 }
201 }
202 if (!t.headingText) { t.headingText = i + 1;}
203 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;}
204 DOM_li.appendChild(DOM_a); DOM_ul.appendChild(DOM_li);}
205 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});}
206 return this;}; tabberObj.prototype.navClick = function(event)
207 { var
208 rVal, a, self, tabberIndex, onClickArgs; a = this; if (!a.tabber) { return false;}
209 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;}
210 rVal = self.onClick(onClickArgs); if (rVal === false) { return false;}
211 }
212 self.tabShow(tabberIndex); return false;}; tabberObj.prototype.tabHideAll = function()
213 { var i; for (i = 0; i < this.tabs.length; i++) { this.tabHide(i);}
214 }; tabberObj.prototype.tabHide = function(tabberIndex)
215 { var div; if (!this.tabs[tabberIndex]) { return false;}
216 div = this.tabs[tabberIndex].div; if (!div.className.match(this.REclassTabHide)) { div.className += ' ' + this.classTabHide;}
217 this.navClearActive(tabberIndex); return this;}; tabberObj.prototype.tabShow = function(tabberIndex)
218 { var div; if (!this.tabs[tabberIndex]) { return false;}
219 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});}
220 return this;}; tabberObj.prototype.navSetActive = function(tabberIndex)
221 { this.tabs[tabberIndex].li.className = this.classNavActive; return this;}; tabberObj.prototype.navClearActive = function(tabberIndex)
222 { this.tabs[tabberIndex].li.className = ''; return this;}; function tabberAutomatic(tabberArgs)
223 { var
224 tempObj, divs, i; if (!tabberArgs) { tabberArgs = {};}
225 tempObj = new tabberObj(tabberArgs); divs = document.getElementsByTagName("div"); for (i=0; i < divs.length; i++) { if (divs[i].className &&
226 divs[i].className.match(tempObj.REclassMain)) { tabberArgs.div = divs[i]; divs[i].tabber = new tabberObj(tabberArgs);}
227 }
228 return this;}
229 function tabberAutomaticOnLoad(tabberArgs)
230 { var oldOnLoad; if (!tabberArgs) { tabberArgs = {};}
231 oldOnLoad = window.onload; if (typeof window.onload != 'function') { window.onload = function() { tabberAutomatic(tabberArgs);};} else { window.onload = function() { oldOnLoad(); tabberAutomatic(tabberArgs);};}
232 }
233 if (typeof tabberOptions == 'undefined') { tabberAutomaticOnLoad();} else { if (!tabberOptions['manualStartup']) { tabberAutomaticOnLoad(tabberOptions);}
234 }
235 '''
236
237 tabber_minimized_css = '''
238 <style type="text/css">
239 /*--------------------------------------------------
240 REQUIRED to hide the non-active tab content.
241 But do not hide them in the print stylesheet!
242 --------------------------------------------------*/
243 .tabberlive .tabbertabhide {
244 display:none;
245 }
246
247 /*--------------------------------------------------
248 .tabber = before the tabber interface is set up
249 .tabberlive = after the tabber interface is set up
250 --------------------------------------------------*/
251 .tabber {
252 }
253 .tabberlive {
254 margin-top:1em;
255 }
256
257 /*--------------------------------------------------
258 ul.tabbernav = the tab navigation list
259 li.tabberactive = the active tab
260 --------------------------------------------------*/
261 ul.tabbernav
262 {
263 margin:0;
264 padding: 3px 0;
265 border-bottom: 1px solid #778;
266 font: bold 12px Verdana, sans-serif;
267 }
268
269 ul.tabbernav li
270 {
271 list-style: none;
272 margin: 0;
273 display: inline;
274 }
275
276 ul.tabbernav li a
277 {
278 padding: 3px 0.5em;
279 margin-left: 3px;
280 border: 1px solid #778;
281 border-bottom: none;
282 background: #DDE;
283 text-decoration: none;
284 }
285
286 ul.tabbernav li a:link { color: #448; }
287 ul.tabbernav li a:visited { color: #667; }
288
289 ul.tabbernav li a:hover
290 {
291 color: #000;
292 background: #AAE;
293 border-color: #227;
294 }
295
296 ul.tabbernav li.tabberactive a
297 {
298 background-color: #fff;
299 border-bottom: 1px solid #fff;
300 }
301
302 ul.tabbernav li.tabberactive a:hover
303 {
304 color: #000;
305 background: white;
306 border-bottom: 1px solid white;
307 }
308
309 /*--------------------------------------------------
310 .tabbertab = the tab content
311 Add style only after the tabber interface is set up (.tabberlive)
312 --------------------------------------------------*/
313 .tabberlive .tabbertab {
314 padding:5px;
315 border:1px solid #aaa;
316 border-top:0;
317
318 /* If you don't want the tab size changing whenever a tab is changed
319 you can set a fixed height */
320
321 /* height:200px; */
322
323 /* If you set a fix height set overflow to auto and you will get a
324 scrollbar when necessary */
325
326 /* overflow:auto; */
327 }
328
329 /* If desired, hide the heading since a heading is provided by the tab */
330 .tabberlive .tabbertab h2 {
331 display:none;
332 }
333 .tabberlive .tabbertab h3 {
334 display:none;
335 }
336
337 /* Example of using an ID to set different styles for the tabs on the page */
338 .tabberlive#tab1 {
339 }
340 .tabberlive#tab2 {
341 }
342 .tabberlive#tab2 .tabbertab {
343 height:200px;
344 overflow:auto;
345 }
346 </style>
347 '''
348
349 class RedirectOutputRequest(RequestBase):
350 """Redirect output to string without HTTP headers."""
351 def __init__ (self, req, debug=False):
352 """Prepare a compatible request."""
353 # Setup variables which containes the output.
354 self.output_string = u''
355 self.error_string = u''
356 self.sent_headers = False
357 self.failed = 0
358
359 # 1.5,1.6,1.7: req.path_info = u'/RaphaelBossek/\xd6nologe' | u'/FrontPage'
360 # after enconde() self.path_info = '/RaphaelBossek/\xd6nologe'
361 self.path_info = req.path_info.encode(config.charset)
362 # 1.5,1.6,1.7: req.query_string = u'action=CreatePdfDocument' | u''
363 self.query_string = 'action=print'
364 if isinstance(self.query_string, unicode):
365 raise UnicodeError('self.query_string can not be of type UNICODE for python 2.3 compatibility reasons.')
366 # What we intent to achive is:
367 # self.request_uri = u'/FrontPage'
368 # self.url = u'localhost:8080/FrontPage?action=print'
369 if req.query_string:
370 # 1.5,1.6: req.request_uri = u'/FrontPage?action=CreatePdfDocument'
371 self.request_uri = req.path_info.replace(req.query_string, self.query_string)
372 # 1.5,1.6: req.url = u'localhost:8080/FrontPage?action=CreatePdfDocument'
373 self.url = req.url.replace(req.query_string, self.query_string)
374 else:
375 self.request_uri = req.path_info
376 self.url = req.url + '?' + self.query_string
377 # 1.5,1.6: Not all kinds of request support SSL.
378 if 'is_ssl' in req.__dict__:
379 self.is_ssl = req.is_ssl
380 else:
381 self.is_ssl = 0
382 # 1.5,1.6: Not all kinds of request set env.
383 self.env = {}
384 if 'env' in req.__dict__:
385 # 1.5,1.6: Do not copy env otherwise UNICODE encoding does not work right.
386 #self._setup_vars_from_std_env(req.env)
387 self.env['AUTH_TYPE'] = req.env.get('AUTH_TYPE', '')
388 self.http_host = req.http_host
389 self.http_user_agent = req.http_user_agent
390 self.request_method = None
391 self.saved_cookie = req.saved_cookie
392 self.remote_addr = req.remote_addr
393 self.script_name = getattr(req, u'script_name', u'')
394
395 if debug:
396 self.error_string += """RedirectOutputRequest.__init__
397 --------------------------
398 req.path_info="%s"
399 req.query_string="%s"
400 req.url="%s"
401 req.http_user_agent="%s"
402 req.http_host="%s"
403 req.saved_cookie="%s"
404 req.remote_addr="%s"
405 self.is_ssl=%d
406 self.script_name="%s"
407 --------------------------
408 """ % (req.path_info, req.query_string, req.url, req.http_user_agent, req.http_host, req.saved_cookie, req.remote_addr, self.is_ssl, self.script_name,)
409
410 self.req = req
411 RequestBase.__init__(self)
412
413 def run(self, rev = None):
414 """Start processing the document."""
415 # RequestBase.__init__ check dump proxy requests.
416 # In this case the initialisiation is stopped and self.forbidden is True.
417 if not self.forbidden:
418 # 1.6:MoinMoin/request/__init__.py: Initialise action from environment variables not from form.
419 self.action = 'print'
420 if rev:
421 self.form[u'rev'] = [rev]
422 RequestBase.run(self)
423 return (self.output_string, self.error_string)
424
425 def http_headers(self, *args, **kw):
426 """Used by MoinMoin 1.5.x instead of emit_http_headers()."""
427 self.sent_headers = True
428
429 def emit_http_headers(self, *args, **kw):
430 """Used by MoinMoin 1.6.x instaed of http_headers()."""
431 self.sent_headers = 1
432
433 def fail(self, err):
434 """Catch if a error occurs and save the message in our string."""
435 RequestBase.fail(self, err)
436 if not self.error_string:
437 self.error_string = str(err)
438
439 def write(self, *data):
440 """Catch the document in our output_string."""
441 if self.sent_headers:
442 if self.failed:
443 self.error_string += data[0]
444 else:
445 self.output_string += data[0]
446
447 def flush(self):
448 pass
449
450
451 def attachment_fsname(attachment, page, request):
452 """Return location of attament on the file system. current_page is the relative location
453 where attachment is used.
454 """
455 from MoinMoin.action import AttachFile
456 pagename, filename = AttachFile.absoluteName(attachment, page.page_name)
457 #self.request.log("attachment_link: url %s pagename %s filename %s" % (url, pagename, filename))
458 fname = wikiutil.taintfilename(filename)
459 return AttachFile.getFilename(request, pagename, 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 #FIXME: self.pagename = None
530 #FIXME: self.request = None
531 #FIXME: self._ = None
532 self.debug = False
533 self.msg = None
534 self.errormsgsent = False
535 self.default_values = {
536 #'style': u'webpage',
537 'style': None,
538 'format': u'pdf13',
539 'titlefileimage': u'',
540 'linkstyle': u'underline',
541 'headerleft': u't',
542 'headermiddle': u'.',
543 'headerright': u'D',
544 'footerleft': u'.',
545 'footermiddle': u'/',
546 'footerright': u'.',
547 'tocheaderleft': u'.',
548 'tocheadermiddle': u't',
549 'tocheaderright': u'.',
550 'tocfooterleft': u'.',
551 'tocfootermiddle': u'.',
552 'tocfooterright': u'i',
553 'bodycolor': u'FFFFFF',
554 'bodyimage': u'',
555 'textcolor': u'000000',
556 'linkcolor': u'0000E0',
557 'size': u'legal',
558 'user-password': u'',
559 'owner-password': u'',
560 'toclevels': u'3',
561 'grayscale': u'unchecked',
562 'title': u'checked',
563 'duplex': u'unchecked',
564 'landscape': u'unchecked',
565 'usersize': u'',
566 'margintop': u'0.50in',
567 'marginbottom': u'0.50in',
568 'marginleft': u'1.00in',
569 'marginright': u'0.50in',
570 'no-toc': u'checked',
571 'no-links': u'checked',
572 'firstpage': u'p1',
573 'jpeg': u'0',
574 'compression': u'0',
575 'pagemode': u'outline',
576 'pagelayout': u'single',
577 'firstpage': u'c1',
578 'numbered': u'checked',
579 'encryption': u'unchecked',
580 'permissioncopy': u'checked',
581 'permissionprint': u'checked',
582 'permissionannotate': u'checked',
583 'permissionmodify': u'checked',
584 'charset': u'iso-8859-1',
585 'debug': u'',
586 'rev': 0,
587 'extra-titledocnumber': u'',
588 'extra-titleauthor': u'',
589 'extra-titlecopyright': u'',
590 'pageinfo': u'unchecked',
591 'bodyfont': u'times',
592 'headingfont': u'helvetica',
593 'headfootfont': u'helvetica',
594 'fontsize': u'11.0',
595 'headfootsize': u'11.0',
596 'fontspacing': u'1.2',
597 'embedfonts': u'checked',
598 'browserwidth': u'680',
599 'extra-dynamiccodeblock': u'checked',
600 'extra-codeblocklinenumbers': u'checked',
601 'htmldoc_cmd': u'htmldoc',
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 caracter 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">Debug mode activated.</p>'
1134 if not moinmoin_release[:3] in [u'1.7', u'1.6', u'1.5']:
1135 form += u'<p class="warning">This plugin was not verified with MoinMoin %s.</p>' % moinmoin_release
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 #del self.form[self.form_trigger]
1445 for buttonname in [u'generate', u'generate_from_form', u'preview', u'remember']:
1446 if buttonname in self.form:
1447 self.form[self.form_trigger] = '1'
1448 # Continue with super function
1449 ActionBase.render(self)
1450
1451
1452 def do_action(self):
1453 """MoinMoin.action.ActionBase: Main dispatcher for the action.
1454 """
1455 # Canceled by user.
1456 #if self.form.has_key(u'cancel'):
1457 # # Backward compatiblity with MoinMoin 1.5.
1458 # if Page.send_page.func_code.co_varnames[1] == "request":
1459 # return self.request.page.send_page(self.request)
1460 # else:
1461 # return self.request.page.send_page()
1462
1463 # Determine calling parameters.
1464 if self.form.get(u'debug', [u'0'])[0] in [u'checked', u'1']:
1465 self.debug = True
1466
1467 # Create a PDF document direct without any user iteraction from default and page settings.
1468 if self.form.has_key(u'generate'):
1469 self.set_page_values()
1470 self.update_values(useform=False)
1471 return self.do_action_generate()
1472
1473 # Create a PDF document from form settings.
1474 if self.form.has_key(u'generate_from_form') or self.form.has_key(u'preview'):
1475 self.update_values()
1476 return self.do_action_generate()
1477
1478 # Display a message with instructions.
1479 if self.form.has_key(u'remember'):
1480 self.update_values()
1481 return self.do_action_remember()
1482
1483 return (True, None)
1484
1485
1486 def do_action_finish(self, success):
1487 """Overwrite default processing in case of success where application/pdf mime document is send instead of default page."""
1488 if self.error:
1489 ActionBase.do_action_finish(self, False)
1490
1491
1492 def update_values(self, useform=True):
1493 """Preset values with they form values or defaults."""
1494 if useform:
1495 pass
1496 for key, default in self.default_values.items():
1497 # Skip settings which can't be modified by form.
1498 if useform and key in ['htmldoc_cmd', 'extra-dynamiccodeblock-middotchar', 'extra-dynamiccodeblock-breakchar']:
1499 continue
1500 # Modify value only if not already set.
1501 if not key in self.values or useform:
1502 # If the form does not contain the value (e.g. for checkboxes) set the
1503 # default value (e.g. for checkboxes unset by default).
1504 if not key in self.form:
1505 # Special processing for checkboxes in forms. If the key does not exists
1506 # within the form it is not checked.
1507 if key in self.form_checkbox and useform:
1508 self.values[key] = u'unchecked'
1509 elif useform:
1510 # Edit fields are missing if they are empty.
1511 self.values[key] = u''
1512 else:
1513 self.values[key] = default
1514 else:
1515 self.values[key] = self.form[key][0]
1516 # Check if revision is an integer value.
1517 try:
1518 self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
1519 except:
1520 self.values[u'rev'] = self.request.page.rev
1521 # Check if page revision exists.
1522 (pagefname, realrev, exists) = self.request.page.get_rev(rev=self.values[u'rev'])
1523 if exists:
1524 self.values[u'rev'] = realrev
1525 else:
1526 # Determine latest revision number.
1527 (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
1528 # Check valid value range.
1529 try:
1530 self.values[u'browserwidth'] = int(self.values[u'browserwidth'])
1531 if self.values[u'browserwidth'] < 400 or self.values[u'browserwidth'] > 1200:
1532 self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1533 except:
1534 self.values[u'browserwidth'] = self.default_values[u'browserwidth']
1535 # We need a string.
1536 self.values[u'browserwidth'] = u'%d' % self.values[u'browserwidth']
1537
1538
1539 def do_action_generate(self):
1540 """Create PDF document."""
1541 # Generate the HTML page using MoinMoin wiki engine.
1542 html = self.get_html()
1543 if html:
1544 if self.form.has_key('preview'):
1545 self.wrapper_emit_http_headers()
1546 self.request.write(html)
1547 else:
1548 pdfdata = self.html2pdf(html)
1549 if pdfdata:
1550 # Send as application/pdf the generated file by HTMLDOC
1551 self.send_pdf(pdfdata)
1552 return (True, None)
1553
1554 return (False, self.error)
1555
1556
1557 def do_action_remember(self):
1558 """Create a message containing information about how to save the form values for future reuse."""
1559 save = u''
1560 for key, value in self.values.items():
1561 if key in [u'user-password', u'owner-password', u'rev', u'debug', u'htmldoc_cmd']:
1562 continue
1563 if key in self.default_values and value == self.default_values[key]:
1564 continue
1565 save += u'##pdf %s %s' % (key, value,)
1566 if self.debug:
1567 if key in self.default_values:
1568 save += u' <-- default value is "%s" (without quotes)' % self.default_values[key]
1569 else:
1570 save += u' <-- keyword missing in defaults'
1571 save += u'\n'
1572 if save:
1573 msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
1574 else:
1575 msg = self._(u'All values correspond to they default. Nothing have to be saved.')
1576
1577 return (True, msg)
1578
1579
1580 def wrapper_emit_http_headers(self, more_headers=[]):
1581 """Wrapper function for MoinMoin 1.5 support."""
1582 if getattr(self.request, "http_headers", False):
1583 self.request.http_headers(more_headers)
1584 else:
1585 self.request.emit_http_headers(more_headers)
1586
1587
1588 def send_pdf(self, data):
1589 """Send PDF file to HTTP server as inline (not attachment).
1590 Refer to Page.send_raw() for an example of implementation.
1591 """
1592 filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
1593
1594 # Send HTTP header.
1595 self.wrapper_emit_http_headers([
1596 'Content-Type: %s' % self.contenttype,
1597 'Content-Length: %d' % len(data),
1598 # TODO: fix the encoding here, plain 8 bit is not allowed
1599 # according to the RFCs There is no solution that is
1600 # compatible to IE except stripping non-ascii chars
1601 'Content-Disposition: inline; filename="%s"' % filename.encode(config.charset),
1602 ])
1603
1604 # Send binary data.
1605 sio = StringIO.StringIO(data)
1606 shutil.copyfileobj(sio, self.request, 8192)
1607
1608
1609 def get_html(self):
1610 """Generate the HTML body of this page."""
1611 # Find out what the right title is.
1612 if self.values[u'extra-headingastitle'] == u'checked':
1613 matchheading = re.compile(r'^= (.*) =$')
1614 for line in self.request.page.get_raw_body().split(u'\n'):
1615 result = matchheading.match(line)
1616 if result:
1617 newtitle = result.group(1).strip()
1618 break
1619 else:
1620 newtitle = self.values['titletext']
1621 # Save page as HTML.
1622 newreq = RedirectOutputRequest(self.request, self.debug)
1623 # Do not add edit information.
1624 # Add extra meta tags.
1625 orig_html_head = self.request.cfg.html_head
1626 self.request.cfg.html_head = self.request.cfg.html_head + u"""
1627 <meta name="docnumber" content="%s">
1628 <meta name="author" content="%s">
1629 <meta name="copyright" content="%s">
1630 """ % (wikiutil.escape(self.values['extra-titledocnumber']), wikiutil.escape(self.values['extra-titleauthor']), wikiutil.escape(self.values['extra-titlecopyright']),)
1631 (html, errmsg) = newreq.run(rev = self.values.get(u'rev', None))
1632 # Restore original HTML head configuration.
1633 self.request.cfg.html_head = orig_html_head
1634 if html:
1635 html = self.fixhtmlstr(html)
1636 # Make URLs absolute.
1637 # FIXME: Until MoinMoin is not XHTML compilant we can not use a XML parser
1638 # (e.g. expat) to transform the HTML document. In the meantime we try to
1639 # achive the same with regular expressions subtitution.
1640 base = self.request.getQualifiedURL()
1641 for htmlref in [u'src', u'href']:
1642 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
1643 urlref = re.compile (reurlref, re.I)
1644 for match in urlref.finditer(html):
1645 foundref = match.groups()
1646 html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
1647
1648 # Rename title of the document.
1649 titletext_html = self.fixhtmlstr(wikiutil.escape(newtitle))
1650 html = re.compile(r'<title>[^<]+</title>').sub(u'<title>%s</title>' % titletext_html, html)
1651
1652 if self.values['pageinfo'] == u'unchecked':
1653 # Remove pageinfo by regex. There is no standard way to do that yet.
1654 # Read the comment in ThemeBase.shouldShowPageinfo().
1655 html = re.compile(r'<p[^>]+id="pageinfo".*</p>').sub(u'', html)
1656 html = re.compile(r'<div id="interwiki">.*?</div>').sub(u'', html)
1657
1658 # HTMLDOC workarround: Add borders to tables. HTMLDOC assume border="0" if not defined.
1659 html = re.compile(r'<table ').sub(u'<table border="1" cellpadding="5" ', html)
1660
1661 # Display line numbers for code blocks without ·.
1662 if self.values[u'extra-dynamiccodeblock'] == u'checked':
1663 if self.values[u'extra-codeblocklinenumbers'] == u'checked':
1664 codeblocknr = re.compile(r'<span[^>]+class="LineNumber">([^<]+)</span>', re.IGNORECASE)
1665 for match in codeblocknr.finditer(html):
1666 newlinenr = u'<font color="%s">%s</font>' % (self.values[u'linkcolor'], match.group(1).replace(u' ', u' '),)
1667 html = html.replace(match.group(0), newlinenr, 1)
1668 else:
1669 html = re.compile(r'<span[^>]+class="LineNumber">[^<]+</span>', re.IGNORECASE).sub(u'', html)
1670
1671 # HTMLDOC: Does not support <span> so we remove them.
1672 for spanmatch in [r'<span[^>]*>', r'</span>']:
1673 html = re.compile(spanmatch).sub(u'', html)
1674
1675 # HTMLDOC: Does not support JavaScript
1676 html = re.compile(r'<script type="text/javascript".*?</script>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1677
1678 # HTMLDOC does not support stylesheets.
1679 html = re.compile(r'<link rel="stylesheet".*?>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1680
1681 # Remove page location added by &action=print.
1682 html = re.compile(r'<ul id="pagelocation">.*?</ul>', re.IGNORECASE | re.DOTALL).sub(u'', html)
1683
1684 # HTMLDOC workarround: There is no CSS support in HTMLDOC.
1685 tablecolor = re.compile(r'<td.*?background-color: (#......).*?>')
1686 for match in tablecolor.finditer(html):
1687 html = html.replace(match.group(0), u'<td bgcolor="%s">' % match.group(1), 1)
1688
1689 # HTMLDOC workarround: Handle <pre> sections over page boundries.
1690 if self.values[u'extra-dynamiccodeblock'] == u'checked':
1691 multiplespaces = re.compile(r'( {2,})')
1692 # Which character should be used to fill out spaces.
1693 if self.values[u'extra-dynamiccodeblock-middot'] == u'checked':
1694 fillchar = self.values[u'extra-dynamiccodeblock-middotchar']
1695 else:
1696 fillchar = u' '
1697 for regexstr in [r'(<pre>)(.*?)(</pre>)', r'(<div class="codearea"[^>]*>.*?<pre[^>]*>)(.*?)(</pre>.*?</div>)']:
1698 codesections = re.compile(regexstr, re.IGNORECASE | re.DOTALL)
1699 for match in codesections.finditer(html):
1700 foundref = match.groups()
1701 presection = foundref[1]
1702 # Search for multiple spaces and replace them by `fillchar` except the last space.
1703 for spaces in multiplespaces.finditer(presection):
1704 newspaces = fillchar * (len(spaces.group(1)) - 1) + u' '
1705 presection = presection.replace (spaces.group(1), newspaces, 1)
1706 # Go through lines and add a ¶ sign at the end of eatch line.
1707 newprelines = []
1708 prelines = presection.split(u"\n")
1709 if len(prelines) > 1:
1710 for preline in prelines:
1711 preline = preline + self.values[u'extra-dynamiccodeblock-breakchar'] + u'<br />'
1712 newprelines.append(preline)
1713 else:
1714 newprelines = prelines
1715 # Create a table arround an multi-line block.
1716 if newprelines:
1717 tablestart = u'<table border="1" bgcolor="#F3F5F7" cellpadding="5"><tr><td>'
1718 tableend = u'</td></tr></table><br />'
1719 else:
1720 newprelines.append(preline)
1721 tablestart = u''
1722 tableend = u''
1723 # Replace the <pre> block with new dynamic text.
1724 html = html.replace(u''.join(foundref), u'%s<font face="Courier,Monospace">%s</font>%s' % (tablestart, u"\n".join(newprelines), tableend,), 1)
1725 # Do not suppress error messages.
1726 if errmsg:
1727 html = u'<pre>' + errmsg + '</pre>' + html
1728 else:
1729 self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
1730 return html
1731
1732
1733 def make_isolang (self, language):
1734 return language + u'_' + language.upper()
1735
1736
1737 def html2pdf(self, html):
1738 """Create a PDF document based on the current parameters."""
1739 # Set environment variables for HTMLDOC
1740 os.environ['LANG'] = self.values[u'language']
1741 os.environ['HTMLDOC_NOCGI'] = '1'
1742 # Determine UID to access ACL protected sites too (mandatory to download attached images).
1743 htmldocopts = [self.default_values['htmldoc_cmd'], "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
1744
1745 for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
1746 self.values[key] = self.values.get(key + u'left', u'.') + self.values.get(key + u'middle', u'.') + self.values.get(key + u'right', u'.')
1747
1748 permissions = []
1749 for opt, value in self.values.items():
1750 # Skip alle non-HTMLDOC configuration parameters.
1751 if opt in [u'language', u'debug', u'rev', u'titletext', 'pageinfo', 'htmldoc_cmd'] or opt[:6] == u'extra-':
1752 continue
1753
1754 # Skip options without values.
1755 value = value.strip()
1756 if not value:
1757 continue
1758
1759 # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
1760 if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
1761 continue
1762
1763 if opt == u'titlefileimage':
1764 # Check if we have a --titlefile or --titleimage option.
1765 lower_value = value.lower()
1766 dotpos = lower_value.rfind(u'.')
1767 if lower_value[dotpos:] in [u'.bmp', u'.gif', u'.jpg', u'.jpeg', u'.png']:
1768 opt = u'titleimage'
1769 else:
1770 opt = u'titlefile'
1771 value = attachment_fsname(value, self.request.page, self.request)
1772 elif opt == u'bodyimage':
1773 value = attachment_fsname(value, self.request.page, self.request)
1774
1775 if opt in [u'style']:
1776 htmldocopts += [u'--' + value]
1777 elif opt in self.form_checkbox:
1778 if value == u'checked':
1779 if opt[:10] == u'permission':
1780 permissions += [opt[10:]]
1781 # Reverse meaning of 'no-' options.
1782 elif opt[:3] != u'no-':
1783 htmldocopts += [u'--' + opt]
1784 elif opt[:3] == u'no-':
1785 htmldocopts += [u'--' + opt]
1786 elif opt[:6] == u'margin':
1787 htmldocopts += [u'--' + opt[6:], value]
1788 else:
1789 htmldocopts += [u'--' + opt, value]
1790 if permissions:
1791 htmldocopts += [u'--permission', u','.join (permissions)]
1792 htmldocopts += [u'-']
1793 # Do not forget to escape all spaces!
1794 eschtmldocopts = [shell_quote(arg) for arg in htmldocopts]
1795 cmdstr = u' '.join(eschtmldocopts)
1796 errmsg = None
1797
1798 pdf = None
1799 os.environ['HTMLDOC_NOCGI'] = '1'
1800 if self.debug:
1801 self.wrapper_emit_http_headers()
1802 errmsg = self._(u'HTMLDOC command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>'
1803 cmdstr = self.default_values['htmldoc_cmd'] + u' --help'
1804 (htmldoc_help, htmldoc_err) = pipeCommand(cmdstr)
1805 errmsg += u'<p>Execute <tt>%s</tt><br /><pre>%s</pre></p>' % (wikiutil.escape(cmdstr), wikiutil.escape(htmldoc_help),)
1806 if 'env' in self.request.__dict__:
1807 reqenv = u'%s' % wikiutil.escape(self.request.env)
1808 else:
1809 reqenv = u'None'
1810 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),)
1811 else:
1812 (pdf, htmldocmsg) = pipeCommand(cmdstr, html)
1813
1814 # Check for error message on STDOUT.
1815 if pdf[:8] == u'HTMLDOC ':
1816 htmldocmsg += pdf
1817 pdf = None
1818
1819 if htmldocmsg:
1820 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
1821 wikiutil.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
1822
1823 # As it is difficult to get the htmldoc return code, we check for
1824 # error by checking the produced pdf length
1825 if not pdf and errmsg:
1826 if self.debug:
1827 self.request.write(u'<html><body>%s</body></hftml>' % errmsg)
1828 self.request.write(html)
1829 else:
1830 self.error_msg(errmsg)
1831 elif pdf[:4] != '%PDF':
1832 self.error_msg(self._(u'Invalid PDF document generated') + wikiutil.escape(pdf[:80]))
1833 pdf = None
1834
1835 return pdf
1836
1837
1838 def _set_fontsize(self, key, min, max, smallstep):
1839 self.valid_options[key] = {}
1840 for fontsize_big in range(min, max):
1841 for fontsize_step in range(0, 10, smallstep):
1842 fontsize = u'%d.%d' % (fontsize_big, fontsize_step,)
1843 self.valid_options[key][fontsize] = self._(fontsize)
1844 self.valid_options[key][u'%d.0' % max] = self._(u'%d.0' % max)
1845
1846
1847 def execute(pagename, request):
1848 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.