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