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