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