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