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