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