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