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