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