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