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