Attachment 'CreatePdfDocument2_0_9.py'
Download 1 # -*- coding: iso-8859-1 -*-
2 """
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 @copyright: (C) 2006 Pascal Bauermeister
17 @copyright: (C) 2006 Raphael Bossek <raphael.bossek@solutions4linux.de>
18 @license: GNU GPL, see COPYING for details
19
20 2006-12-17 RaphaelBossek
21 * Release v2.0.9
22 * Added AUTH_TYPE for RedirectOutputRequest()
23
24 2006-11-16 RaphaelBossek
25 * Release v2.0.8
26 * Fixed another missing configuration for RedirectOutputRequest()
27
28 2006-11-15 RaphaelBossek
29 * Release v2.0.7
30 * Fixed support for MoinMoin 1.5.3
31 * Fixed SSL support.
32
33 2006-11-14 RaphaelBossek
34 * Release v2.0.6
35 * MoinMoin 1.6 support added.
36 * Fixed Windows(TM) platform support.
37 * Added support for alternative title text.
38
39 2006-09-13 RaphaelBossek
40 * Release v2.0.5
41 * Fixed RedirectOutputRequest class where definition of script_name
42 was missing.
43
44 2006-09-12 RaphaelBossek
45 * Release v2.0.4
46 * Fixed RedirectOutputRequest class where function was redifined
47 to boolean value.
48
49 2006-09-06 RaphaelBossek
50 * Release v2.0.3
51 * Fixed FastCGI support by removing stdout redirect output code. The
52 same functionality will be done by the RedirectOutputRequest class.
53 * Renamed to CreatePdfDocument (for better translation possibilities).
54 * Fixed encoding of title page.
55 * Added charset set option.
56 * Fixed waiting for HTMLDOC.
57
58 2006-09-02 RaphaelBossek
59 * Release v2.0.2
60 * Added createpdfdocument_validoptions and createpdfdocument_defaultvalues
61 configuration parameter support. You are not able to preset available
62 options and which defaults are used in your configuration file.
63
64 2006-08-30 RaphaelBossek
65 * Release v2.0.1
66 * Fixed issue with page revision number forwarding (traceback).
67
68 2006-08-30 RaphaelBossek
69 * Release v2.0.0
70 * Feature enchanced and bug fixed version.
71
72 2006-05-26 PascalBauermeister
73 * Release v1.0.2
74 * Relative image URLs turned absolute was bogus. It is less bogus now.
75
76 2006-05-26 PascalBauermeister
77 * Release v1.0.1
78 * Set env var HTMLDOC_NOCGI to solve CGI issue
79
80 2006-05-24 PascalBauermeister
81 * Initial release v1.0.0
82 """
83
84 import os
85 import sys
86 import stat
87 import re
88 import copy
89 import shutil
90 import StringIO
91 from MoinMoin import config
92 from MoinMoin import util
93 from MoinMoin import wikiutil
94 from MoinMoin import packages
95 from MoinMoin import error
96 from MoinMoin.Page import Page
97 from MoinMoin.request import RequestBase
98 from MoinMoin.widget.dialog import Dialog
99
100
101 class RedirectOutputRequest(RequestBase):
102 """Redirect output to string without HTTP headers."""
103 def __init__ (self, req, action=u'print'):
104 """Prepare a compatible request."""
105 # 1.5,1.6:req.path_info = u'/RaphaelBossek/\xd6nologe' | u'/FrontPage'
106 # after enconde() self.path_info = '/RaphaelBossek/\xd6nologe'
107 self.path_info = req.path_info.encode(config.charset)
108 # 1.5,1.6:query_string = req.query_string = u'action=CreatePdfDocument'
109 self.query_string = u'action=' + action
110 # 1.5,1.6:request_uri = req.request_uri = u'/FrontPage?action=CreatePdfDocument'
111 self.request_uri = req.path_info.replace(req.query_string, self.query_string)
112 # 1.5,1.6:url = req.url = u'localhost:8080/FrontPage?action=CreatePdfDocument'
113 self.url = req.url.replace(req.query_string, self.query_string)
114 # 1.5,1.6: Not all kinds of request support SSL.
115 if 'is_ssl' in req.__dict__:
116 self.is_ssl = req.is_ssl
117 else:
118 self.is_ssl = 0
119 # 1.5,1.6: Not all kinds of request set env.
120 self.env = {}
121 if 'env' in req.__dict__:
122 # 1.5,1.6: Do not copy env otherwise UNICODE encoding does not work right.
123 #self._setup_vars_from_std_env(req.env)
124 self.env['AUTH_TYPE'] = req.env.get('AUTH_TYPE', '')
125 self.http_host = req.http_host
126 self.http_user_agent = req.http_user_agent
127 self.request_method = None
128 self.saved_cookie = req.saved_cookie
129 self.remote_addr = req.remote_addr
130 self.script_name = getattr (req, u'script_name', u'')
131
132 self.req = req
133 RequestBase.__init__(self)
134
135 def run(self, rev = None):
136 """Start processing the document."""
137 # 1.6:MoinMoin/request/__init__.py: Initialise action from environment variables not from form.
138 self.action = u'print'
139 if rev:
140 self.form[u'rev'] = [rev]
141 self.output_string = u''
142 self.error_string = u''
143 self.sent_headers = False
144 self.failed = 0
145 RequestBase.run(self)
146 return (self.output_string, self.error_string)
147
148 def http_headers (self, *args, **kw):
149 """Used by MoinMoin 1.5.x instead of emit_http_headers()."""
150 self.sent_headers = True
151
152 def emit_http_headers(self, *args, **kw):
153 """Used by MoinMoin 1.6.x instaed of http_headers()."""
154 self.sent_headers = 1
155
156 def fail (self, err):
157 """Catch if a error occurs and save the message in our string."""
158 RequestBase.fail (self, err)
159 if not self.error_string:
160 self.error_string = str(err)
161
162 def write (self, *data):
163 """Catch the document in our output_string."""
164 if self.sent_headers:
165 if self.failed:
166 self.error_string += data[0]
167 else:
168 self.output_string += data[0]
169
170 def flush(self):
171 pass
172
173
174 class CreatePdfDocument:
175 """Implementation of the PDF document generator."""
176
177 def __init__(self):
178 self.action_name = self.__class__.__name__
179 self.pagename = None
180 self.request = None
181 self._ = None
182 self.debug = False
183 self.msg = None
184 self.errormsgsent = False
185 self.default_values = {
186 'style': u'webpage',
187 'format': u'pdf13',
188 'linkstyle': u'underline',
189 'headerleft': u't',
190 'headermiddle': u'.',
191 'headerright': u'D',
192 'footerleft': u'.',
193 'footermiddle': u'/',
194 'footerright': u'.',
195 'tocheaderleft': u'.',
196 'tocheadermiddle': u't',
197 'tocheaderright': u'.',
198 'tocfooterleft': u'.',
199 'tocfootermiddle': u'.',
200 'tocfooterright': u'i',
201 'linkcolor': u'0000E0',
202 'size': u'legal',
203 'user-password': u'',
204 'owner-password': u'',
205 'toclevels': u'3',
206 'grayscale': u'unchecked',
207 'title': u'checked',
208 'duplex': u'unchecked',
209 'landscape': u'unchecked',
210 'usersize': u'',
211 'margintop': u'0.50in',
212 'marginbottom': u'0.50in',
213 'marginleft': u'1.00in',
214 'marginright': u'0.50in',
215 'no-toc': u'checked',
216 'no-links': u'checked',
217 'firstpage': u'p1',
218 'jpeg': u'0',
219 'compression': u'0',
220 'pagemode': u'outline',
221 'pagelayout': u'single',
222 'firstpage': u'c1',
223 'numbered': u'checked',
224 'encryption': u'unchecked',
225 'permissioncopy': u'checked',
226 'permissionprint': u'checked',
227 'permissionannotate': u'checked',
228 'permissionmodify': u'checked',
229 'charset': u'iso-8859-1',
230 'debug': u'',
231 'rev': 0,
232 }
233 # We have to know which values are checkboxes within the form. If a key does
234 # not exists wihtin the form the corresponding checkbox is not checked.
235 self.form_checkbox = []
236 for key, value in self.default_values.items():
237 if value in [u'checked', u'unchecked']:
238 self.form_checkbox += [key]
239 self.contenttype = u'application/pdf'
240
241 def error_msg (self, msg):
242 """Display error message."""
243 if not self.errormsgsent:
244 Page (self.request, self.pagename).send_page (self.request, msg=msg)
245 self.errormsgsent = True
246
247 def fixhtmlstr (self, str):
248 """Convert utf-8 encoded multi-byte sequences into &#XXXX; format."""
249 htmlstr = u''
250 for c in str:
251 if ord(c) >= 128:
252 htmlstr = htmlstr + '&#%d;' % ord(c)
253 else:
254 htmlstr = htmlstr + c
255 return htmlstr
256
257 def set_page_values(self):
258 """Scan raw page for additional information relating PDF generation."""
259 #pdflines = False
260 for line in self.request.page.get_raw_body().split(u'\n'):
261 if line[:6] == u'##pdf ':
262 cols = line[6:].split()
263 # Only accept known values/settings.
264 if len(cols) > 1 and cols[0] in self.default_values:
265 self.values[cols[0]] = u' '.join(cols[1:])
266 #pdflines = True
267 continue
268 # Stop parsing with first non-pdf line (if detected at least one).
269 #elif pdflines and not line:
270 # break
271
272 def set_page_default_values(self):
273 # We are not able to recognise if this string is part of a verbatim area.
274 matchtoclvl = re.compile(r'^\[\[TableOfContents\(\s*(\d+)\s*\)\]\]')
275 matchtoc = re.compile(r'^\[\[TableOfContents\(*\)*\]\]')
276 toc = False
277 for line in self.request.page.get_raw_body().split(u'\n'):
278 if line[:10] == u'#language ' and not u'language' in self.values:
279 lang = self.make_isolang(line[10:])
280 if lang:
281 self.default_values[u'language'] = lang
282 elif not u'toclevels' in self.values and not toc:
283 result = matchtoclvl.match(line)
284 if result:
285 toclevels = int(result.group(1).strip())
286 if toclevels > 4:
287 toclevels = 4
288 self.default_values[u'toclevels'] = str(toclevels)
289 toc = True
290 elif matchtoc.match(line):
291 toc = True
292 # We assume if table-of-contents is used we intent to generate a book.
293 if toc:
294 self.default_values[u'style'] = u'book'
295 else:
296 # Do not generate a table of contents page.
297 self.default_values[u'no-toc'] = u'unchecked'
298
299 def _select (self, name, description=None):
300 """Helper function to create a selection control."""
301 str = u'<select name="%s" size="1">' % (name,)
302 if not description:
303 description = self.valid_options[name]
304 keys = description.keys()
305 keys.sort()
306 for value in keys:
307 if value == self.values[name]:
308 selected = u'selected'
309 else:
310 selected = u''
311 str += u'<option value="%s" %s>%s</option>' % (value, selected, description[value],)
312 str += u'</select>'
313 return str
314
315 def _chooseformat (self, name):
316 """Helper function to create left/middle/right selection controls."""
317 str = u""" <tr>
318 <td class="label"><label>%s :</label></td>
319 <td><table>
320 <tr>
321 <td>%s :</td>
322 <td>%s</td>
323 </tr>
324 <tr>
325 <td>%s :</td>
326 <td>%s</td>
327 </tr>
328 <tr>
329 <td>%s :</td>
330 <td>%s</td>
331 </tr>
332 </table>
333 </td>
334 <td>%s</td>
335 </tr>""" % (self.fields[u'label_' + name],
336 self.fields[u'label_left'], self._select(name + u'left', self.valid_options[u'tocformats']),
337 self.fields[u'label_middle'], self._select(name + u'middle', self.valid_options[u'tocformats']),
338 self.fields[u'label_right'], self._select(name + u'right', self.valid_options[u'tocformats']),
339 self.fields[u'help_' + name],)
340 return str
341
342 def makeform (self, errormsg=u''):
343 self.fields = {
344 'error': errormsg,
345 'pagename': wikiutil.escape(self.pagename),
346 'action': self.action_name,
347 'seperator': u'<tr><td colspan="3"><hr/></td></tr>',
348 'space': u'<tr><td colspan="3"> </td></tr>',
349
350 'label_input': self._(u'Input'),
351 'label_output': self._(u'Output'),
352 'label_page': self._(u'Page'),
353 'label_tableofcontents': self._(u'Contents'),
354 'label_pdf': self._(u'PDF'),
355 'label_security': self._(u'Security'),
356
357 'label_choose_style': self._(u'Choose style'),
358 'help_choose_style': self._(u'book: Create a structured PDF document with headings, chapters, etc.') + u'<br/>' + \
359 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/>' + \
360 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.'),
361
362 'help_titletext': self._(u'Title of the document for the front page.'),
363
364 'label_format': self._(u'Output format'),
365 'help_format': self._(u'Specifies the output format.'),
366
367 'label_outputoptions': self._(u'Output options'),
368 'label_grayscale': self._(u'Grayscale document'),
369 'label_titlepage': self._(u'Title page'),
370 'label_titletext': self._(u'Title'),
371 'label_jpeg': self._(u'JPEG big images'),
372 'label_compression': self._(u'Compression'),
373
374 'label_no-toc': self._(u'Generate a table of contents'),
375 'help_no-toc': self._(u''),
376
377 'label_toclevels': self._(u'Limit the number of levels in the table-of-contents'),
378 'help_toclevels': self._(u'Sets the number of levels in the table-of-contents.') + u' ' + self._(u'Empty for unlimited levels.'),
379
380 'label_numbered': self._(u'Numbered headings'),
381 'help_numbered': self._(u'Check to number all of the headings in the document.'),
382
383 'label_toctitle': self._(u'Table-of-contents title'),
384 'help_toctitle': self._(u'Sets the title for the table-of-contents.') + u' ' + self._(u'Empty for default title.'),
385
386 'label_left': self._(u'Left'),
387 'label_middle': self._(u'Middle'),
388 'label_right': self._(u'Right'),
389
390 'label_tocheader': self._(u'Header of table-of-contantes page'),
391 'help_tocheader': self._(u'Sets the page header to use on table-of-contents pages.'),
392
393 'label_tocfooter': self._(u'Footer of table-of-contantes page'),
394 'help_tocfooter': self._(u'Sets the page footer to use on table-of-contents pages.'),
395
396 'label_header': self._(u'Page header'),
397 'help_header': self._(u'Sets the page header to use on body pages.'),
398
399 'label_footer': self._(u'Page footer'),
400 'help_footer': self._(u'Sets the page footer to use on body pages.'),
401
402 'label_no-links': self._(u'Create HTTP links'),
403 'help_no-links': self._(u'Enables generation of links in PDF files.'),
404
405 'label_linkstyle': self._(u'Style of HTTP links'),
406 'help_linkstyle': self._(u''),
407
408 'label_linkcolor': self._(u'HTTP links color'),
409 'help_linkcolor': self._(u'Sets the color of links.'),
410
411 'label_duplex': self._(u'2-Sided'),
412 'help_duplex': self._(u'Specifies that the output should be formatted for double-sided printing.'),
413
414 'label_landscape': self._(u'Landscape'),
415
416 'label_choose_size': self._(u'Choose page size'),
417 'help_choose_size': self._(u'Choose one of the predefined standard sizes or select user defined.'),
418
419 'label_usersize': self._(u'User defined page size'),
420 '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).'),
421
422 'label_margin': self._(u'User defined margin'),
423 'label_margintop': self._(u'Top'),
424 'label_marginbottom': self._(u'Bottom'),
425 'label_marginleft': self._(u'Left'),
426 'label_marginright': self._(u'Right'),
427 '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.'),
428
429 'label_pagemode': self._(u'Page mode'),
430 '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.'),
431
432 'label_pagelayout': self._(u'Page layout'),
433 '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.'),
434
435 'label_firstpage': self._(u'First page'),
436 'help_firstpage': self._(u'Choose the initial page that will be shown.'),
437
438 'label_encryption': self._(u'Encryption'),
439 'help_encryptin': self._(u'Enables encryption and security features for PDF output.'),
440
441 'label_permissions': self._(u'Permissions'),
442 'help_permissions': self._(u'Specifies the document permissions.'),
443
444 'label_permissionannotate': self._(u'Annotate'),
445 'label_permissionprint': self._(u'Print'),
446 'label_permissionmodify': self._(u'Modify'),
447 'label_permissioncopy': self._(u'Copy'),
448
449 'label_owner-password': self._(u'Owner password'),
450 '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.'),
451
452 'label_user-password': self._(u'User password'),
453 'help_user-password': self._(u'Specifies the user password to restrict viewing permissions on this PDF document.') + u' ' + self._(u'Empty for no encryption.'),
454
455 'label_expert': self._(u'Expert'),
456 'label_language': self._(u'Language translation'),
457 'help_language': self._(u'Specify language to use for date and time format.'),
458
459 'label_charset': self._(u'Charset set'),
460 'help_charset': self._(u'Change the encoding of the text in document.'),
461
462 'button_generate': self._(u'Generate PDF'),
463 'button_remember': self._(u'Remember form'),
464 'button_cancel': self._(u'Cancel'),
465 'button_reset': self._(u'Reset'),
466 }
467 self.fields.update(self.values)
468
469 # Go through all format strings.
470 for name in [u'tocheader', u'tocfooter', u'header', u'footer']:
471 self.fields[u'choose_' + name] = self._chooseformat(name)
472
473 self.fields[u'select_style'] = self._select (u'style')
474 self.fields[u'select_format'] = self._select (u'format')
475 self.fields[u'select_linkstyle'] = self._select (u'linkstyle')
476 self.fields[u'select_size'] = self._select (u'size')
477 self.fields[u'select_jpeg'] = self._select (u'jpeg')
478 self.fields[u'select_compression'] = self._select (u'compression')
479 self.fields[u'select_toclevels'] = self._select (u'toclevels')
480 self.fields[u'select_pagemode'] = self._select (u'pagemode')
481 self.fields[u'select_pagelayout'] = self._select (u'pagelayout')
482 self.fields[u'select_firstpage'] = self._select (u'firstpage')
483 self.fields[u'select_charset'] = self._select (u'charset')
484
485 form = """<p class="error">%(error)s</p>
486 <form method="post" action="">
487 <input type="hidden" name="action" value="%(action)s"/>
488 <table>
489 <tr><td colspan="3">%(label_input)s</td></tr>
490 <tr>
491 <td class="label"><label>%(label_choose_style)s :</label></td>
492 <td class="content">%(select_style)s</td>
493 <td>%(help_choose_style)s</td>
494 </tr>
495 <tr>
496 <td class="label"><label>%(label_titletext)s :</label></td>
497 <td class="content"><input type="text" size="30" name="titletext" value="%(titletext)s" /></td>
498 <td>%(help_titletext)s</td>
499 </tr>
500 %(seperator)s
501 <tr><td colspan="3">%(label_output)s</td></tr>
502 <tr>
503 <td class="label"><label>%(label_format)s :</label></td>
504 <td class="content">%(select_format)s</td>
505 <td>%(help_format)s</td>
506 </tr>
507 <tr>
508 <td class="label"><label>%(label_outputoptions)s :</label></td>
509 <td colspan="2"><input type="checkbox" name="grayscale" value="checked" %(grayscale)s />%(label_grayscale)s
510 <input type="checkbox" name="title" value="checked" %(title)s />%(label_titlepage)s<br />
511 %(label_compression)s : %(select_compression)s
512 %(label_jpeg)s : %(select_jpeg)s</td>
513 </tr>
514 %(seperator)s
515 <tr><td colspan="3">%(label_page)s</td></tr>
516 <tr>
517 <td class="label"><label>%(label_choose_size)s :</label></td>
518 <td>%(select_size)s <br /><nobr>%(label_usersize)s : <input type="text" size="15" name="usersize" value="%(usersize)s" /></nobr></td>
519 <td>%(help_choose_size)s<br />%(help_usersize)s</td>
520 </tr>
521 <tr>
522 <td> </td>
523 <td colspan="2"><input type="checkbox" name="duplex" value="checked" %(duplex)s /> %(label_duplex)s
524 <input type="checkbox" name="landscape" value="checked" %(landscape)s /> %(label_landscape)s</td>
525 </tr>
526 <tr>
527 <td class="label"><label>%(label_margin)s :</label></td>
528 <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>
529 <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>
530 <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>
531 <td>%(help_margin)s</td>
532 </tr>
533 %(choose_header)s
534 %(choose_footer)s
535 %(seperator)s
536 <tr><td colspan="3">%(label_tableofcontents)s</td></tr>
537 <tr>
538 <td class="label"><label>%(label_no-toc)s :</label></td>
539 <td class="checkbox"><input type="checkbox" name="no-toc" value="checked" %(no-toc)s /></td>
540 <td>%(help_no-toc)s</td>
541 </tr>
542 <tr>
543 <td class="label"><label>%(label_toclevels)s :</label></td>
544 <td class="content">%(select_toclevels)s</td>
545 <td>%(help_toclevels)s</td>
546 </tr>
547 <tr>
548 <td> </td>
549 <td><input type="checkbox" name="numbered" value="checked" %(numbered)s /> %(label_numbered)s</td>
550 <td>%(help_numbered)s</td>
551 </tr>
552 <tr>
553 <td class="label"><label>%(label_toctitle)s :</label></td>
554 <td class="content"><input type="text" size="30" name="toctitle" value="%(toctitle)s" /></td>
555 <td>%(help_toctitle)s</td>
556 </tr>
557 %(choose_tocheader)s
558 %(choose_tocfooter)s
559 %(seperator)s
560 <tr><td colspan="3">%(label_pdf)s</td></tr>
561 <tr>
562 <td class="label"><label>%(label_pagemode)s :</label></td>
563 <td class="content">%(select_pagemode)s</td>
564 <td>%(help_pagemode)s</td>
565 </tr>
566 <tr>
567 <td class="label"><label>%(label_pagelayout)s :</label></td>
568 <td class="content">%(select_pagelayout)s</td>
569 <td>%(help_pagelayout)s</td>
570 </tr>
571 <tr>
572 <td class="label"><label>%(label_firstpage)s :</label></td>
573 <td class="content">%(select_firstpage)s</td>
574 <td>%(help_firstpage)s</td>
575 </tr>
576 <tr>
577 <td class="label"><label>%(label_no-links)s :</label></td>
578 <td><input type="checkbox" name="no-links" value="checked" %(no-links)s /></td>
579 <td>%(help_no-links)s</td>
580 </tr>
581 <tr>
582 <td class="label"><label>%(label_linkstyle)s :</label></td>
583 <td class="content">%(select_linkstyle)s</td>
584 <td>%(help_linkstyle)s</td>
585 </tr>
586 <tr>
587 <td class="label"><label>%(label_linkcolor)s :</label></td>
588 <td class="content"><input type="text" size="6" name="linkcolor" value="%(linkcolor)s" /></td>
589 <td>%(help_linkcolor)s</td>
590 </tr>
591 %(seperator)s
592 <tr><td colspan="3">%(label_security)s</td></tr>
593 <tr>
594 <td class="label"><label>%(label_encryption)s :</label></td>
595 <td><input type="checkbox" name="encryption" value="checked" %(encryption)s /></td>
596 <td>%(help_numbered)s</td>
597 </tr>
598 <tr>
599 <td class="label"><label>%(label_permissions)s :</label></td>
600 <td><nobr><input type="checkbox" name="permissionprint" value="checked" %(permissionprint)s /> %(label_permissionprint)s</nobr>
601 <nobr><input type="checkbox" name="permissionmodify" value="checked" %(permissionmodify)s /> %(label_permissionmodify)s</nobr><br />
602 <nobr><input type="checkbox" name="permissioncopy" value="checked" %(permissioncopy)s /> %(label_permissioncopy)s</nobr>
603 <nobr><input type="checkbox" name="permissionannotate" value="checked" %(permissionannotate)s /> %(label_permissionannotate)s</nobr></td>
604 <td>%(help_permissions)s</td>
605 </tr>
606 <tr>
607 <td class="label"><label>%(label_user-password)s :</label></td>
608 <td class="content"><input type="password" size="30" name="user-password" value="%(user-password)s" /></td>
609 <td>%(help_user-password)s</td>
610 </tr>
611 <tr>
612 <td class="label"><label>%(label_owner-password)s :</label></td>
613 <td class="content"><input type="password" size="30" name="owner-password" value="%(owner-password)s" /></td>
614 <td>%(help_owner-password)s</td>
615 </tr>
616 %(seperator)s
617 <tr><td colspan="3">%(label_expert)s</td></tr>
618 <tr>
619 <td class="label"><label>%(label_language)s :</label></td>
620 <td class="content"><input type="text" size="6" name="language" value="%(language)s" /></td>
621 <td>%(help_language)s</td>
622 </tr>
623 <tr>
624 <td class="label"><label>%(label_charset)s :</label></td>
625 <td class="content">%(select_charset)s</td>
626 <td>%(help_charset)s</td>
627 </tr>
628 %(space)s
629 <tr>
630 <td><input type="hidden" name="debug" value="%(debug)s" /><input type="hidden" name="rev" value="%(rev)s" /></td>
631 <td class="buttons" colspan="2">
632 <input type="submit" name="generate_from_form" value="%(button_generate)s" />
633 <input type="submit" name="remember" value="%(button_remember)s" />
634 <input type="submit" name="cancel" value="%(button_cancel)s" />
635 </td>
636 </tr>
637 </table>
638 </form>""" % self.fields
639 return Dialog (self.request, content=form)
640
641 def run (self, pagename, request):
642 """ Main dispatcher for the action."""
643 self.pagename = pagename
644 self.request = request
645 self._ = self.request.getText
646 self.form = self.request.form
647 self.cfg = self.request.cfg
648
649 # Canceled by user.
650 if self.form.has_key(u'cancel'):
651 return self.request.page.send_page(self.request)
652
653 # Determine calling parameters.
654 if self.form.get(u'debug', [u'0']) == [u'1']:
655 self.debug = True
656
657 # This dict contains all possible values.
658 self.valid_options = {}
659
660 self.valid_options[u'tocformats'] = {
661 u'/': self._(u'1/N,2/N Arabic page numbers'),
662 u':': self._(u'1/C,2/C Arabic chapter page numbers'),
663 u'1': self._(u'1,2,3,...'),
664 u'a': self._(u'a,b,c,...'),
665 u'A': self._(u'A,B,C,...'),
666 u'c': self._(u'Chapter title'),
667 u'C': self._(u'Chapter page number'),
668 u'd': self._(u'Date'),
669 u'D': self._(u'Date + Time'),
670 u'h': self._(u'Heading'),
671 u'i': self._(u'i,ii,iii,iv,...'),
672 u'I': self._(u'I,II,III,IV,...'),
673 u't': self._(u'Title'),
674 u'T': self._(u'Time'),
675 u'.': self._(u'Blank'),
676 # TODO: Not supported yet; u'l': self._(u'Logo image'),
677 }
678 self.valid_options[u'style'] = {
679 u'webpage': self._(u'webpage'),
680 u'book': self._(u'book'),
681 u'continuous': self._(u'continuous'),
682 }
683 self.valid_options[u'size'] = {
684 u'legal': self._(u'Legal (8.5x14in)'),
685 u'a4': self._(u'A4 (210x297mm)'),
686 u'letter': self._(u'Letter (8.5x11in)'),
687 u'universal': self._(u'Universal (8.27x11in)'),
688 u'': self._(u'User defined'),
689 }
690 self.valid_options[u'format'] = {
691 u'pdf11': self._(u'PDF 1.1 (Acrobat 2.0)'),
692 u'pdf12': self._(u'PDF 1.2 (Acrobat 3.0)'),
693 u'pdf13': self._(u'PDF 1.3 (Acrobat 4.0)'),
694 u'pdf14': self._(u'PDF 1.4 (Acrobat 5.0)'),
695 # TODO: Not supported yet:
696 #u'ps1': self._(u'PostScript Level 1'),
697 #u'ps2': self._(u'PostScript Level 2'),
698 #u'ps3': self._(u'PostScript Level 3'),
699 }
700 self.valid_options[u'linkstyle'] = {
701 u'underline': self._(u'Underline'),
702 u'plain': self._(u'Plain'),
703 }
704 self.valid_options[u'firstpage'] = {
705 u'c1': self._(u'1st chapter'),
706 u'p1': self._(u'1st page'),
707 u'toc': self._(u'Contents'),
708 }
709 self.valid_options[u'jpeg'] = {
710 u'0': self._(u'None'),
711 u'50': self._(u' 50% (Good)'),
712 u'55': u'55%', u' 60': u' 60%', u' 65': ' 65%', u' 70': ' 70%', u' 75': ' 75%',
713 u'80': ' 80%', u' 85': ' 85%', u' 90': ' 90%', u' 95': ' 95%',
714 u'100': self._(u'100% (Best)'),
715 }
716 self.valid_options[u'compression'] = {
717 u'0': self._(u'None'),
718 u'1': self._(u'1 (Fast)'),
719 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',
720 u'9': self._(u'9 (Best)'),
721 }
722 self.valid_options[u'toclevels'] = {
723 u'0': self._(u'None'),
724 u'1': u'1', u'2': '2', u'3': '3', u'4': '4'
725 }
726 self.valid_options[u'pagemode'] = {
727 u'outline': self._(u'Outline'),
728 u'document': self._(u'Document'),
729 u'fullscreen': self._(u'Full-screen'),
730 }
731 self.valid_options[u'pagelayout'] = {
732 u'single': self._(u'Single'),
733 u'one': self._(u'One column'),
734 u'twoleft': self._(u'Two column left'),
735 u'tworight': self._(u'Two column right'),
736 }
737 self.valid_options[u'charset'] = {
738 u'iso-8859-1': self._(u'ISO 8859-1'),
739 u'iso-8859-2': self._(u'ISO 8859-2'),
740 u'iso-8859-3': self._(u'ISO 8859-3'),
741 u'iso-8859-4': self._(u'ISO 8859-4'),
742 u'iso-8859-5': self._(u'ISO 8859-5'),
743 u'iso-8859-6': self._(u'ISO 8859-6'),
744 u'iso-8859-7': self._(u'ISO 8859-7'),
745 u'iso-8859-8': self._(u'ISO 8859-8'),
746 u'iso-8859-9': self._(u'ISO 8859-9'),
747 u'iso-8859-14': self._(u'ISO 8859-14'),
748 u'iso-8859-15': self._(u'ISO 8859-15'),
749 u'cp-874': self._(u'cp-847'),
750 u'cp-1250': self._(u'cp-1250'),
751 u'cp-1251': self._(u'cp-1251'),
752 u'cp-1252': self._(u'cp-1252'),
753 u'cp-1253': self._(u'cp-1253'),
754 u'cp-1254': self._(u'cp-1254'),
755 u'cp-1255': self._(u'cp-1255'),
756 u'cp-1256': self._(u'cp-1256'),
757 u'cp-1257': self._(u'cp-1257'),
758 u'cp-1258': self._(u'cp-1258'),
759 u'koi-8r': self._(u'koi-8r'),
760 }
761
762 # Set translated name of table of contents as default.
763 self.default_values[u'toctitle'] = self._(u'Contents')
764
765 self.default_values[u'titletext'] = self.pagename
766
767 # Make sure we create date and time strings in right format.
768 if self.request.page.language:
769 self.default_values[u'language'] = self.request.page.language
770 else:
771 self.default_values[u'language'] = self.make_isolang(self.cfg.__dict__.get (u'default_language', u'en'))
772
773 self.values = {}
774
775 # If the configuration variable 'createpdfdocument_validoptions' exists we update our
776 # self.valid_options dict with these values.
777 if getattr (self.request.cfg, u'createpdfdocument_validoptions', None):
778 self.valid_options.update (self.request.cfg.createpdfdocument_validoptions)
779
780 # If the configuration variable 'createpdfdocument_defaultvalues' exists we update our
781 # self.default_values dict with these values.
782 if getattr (self.request.cfg, u'createpdfdocument_defaultvalues', None):
783 for key, value in self.request.cfg.createpdfdocument_defaultvalues.items():
784 self.default_values[key] = value
785
786 # Scan page to extract default values.
787 self.set_page_default_values()
788
789 # Create a PDF document direct without any user iteraction from default and page settings.
790 if self.form.has_key(u'generate'):
791 self.set_page_values()
792 self.update_values(useform=False)
793 return self.do_generate()
794
795 # Create a PDF document from form settings.
796 if self.form.has_key(u'generate_from_form'):
797 self.update_values()
798 return self.do_generate()
799
800 if self.form.has_key(u'remember'):
801 self.update_values()
802 return self.do_remember()
803
804 self.set_page_values()
805 self.update_values(useform=False)
806 return self.request.page.send_page (self.request, msg=self.makeform())
807
808 def update_values(self, useform=True):
809 """Preset values with they form values or defaults."""
810 for key, default in self.default_values.items():
811 # Modify value only if not already set.
812 if not key in self.values:
813 # If the form does not contain the value (e.g. for checkboxes) set the
814 # default value (e.g. for checkboxes unset by default).
815 if not key in self.form:
816 # Special processing for checkboxes in forms. If the key does not exists
817 # within the form it is not checked.
818 if key in self.form_checkbox and useform:
819 self.values[key] = u'unchecked'
820 else:
821 self.values[key] = default
822 else:
823 self.values[key] = self.form[key][0]
824 # Check if revision is an integer value.
825 try:
826 self.values[u'rev'] = int(self.values.get(u'rev', self.request.page.rev))
827 except:
828 self.values[u'rev'] = self.request.page.rev
829 # Check if page revision exists.
830 (pagefname, realrev, exists) = self.request.page.get_rev (rev=self.values[u'rev'])
831 if exists:
832 self.values[u'rev'] = realrev
833 else:
834 # Determine latest revision number.
835 (pagefname, self.values[u'rev'], exists) = self.request.page.get_rev()
836
837 def do_generate(self):
838 """Create PDF document."""
839 # Generate the HTML page using MoinMoin wiki engine.
840 html = self.get_html()
841 if html:
842 if self.debug:
843 self.request.http_headers()
844 self.request.write(html)
845 else:
846 pdfdata = self.html2pdf(html)
847 if pdfdata:
848 # Send as application/pdf the generated file by HTMLDOC
849 self.send_pdf(pdfdata)
850
851 # MoinMoin1.6: send_closing_html() has to be called explicit.
852 # MoinMoin1.5: raise MoinMoinNoFooter exception to forbit creation of HTML footer.
853 if not 'send_closing_html' in self.request.theme.__dict__:
854 from MoinMoin.util import MoinMoinNoFooter
855 raise MoinMoinNoFooter
856
857 def do_remember(self):
858 """Create a message containing information about how to save the form values for future reuse."""
859 save = u''
860 for key, value in self.values.items():
861 if key in [u'user-password', u'owner-password', u'rev', u'debug']:
862 continue
863 if key in self.default_values and value == self.default_values[key]:
864 continue
865 save += u'##pdf %s %s\n' % (key, value,)
866 if save:
867 msg = self._(u'Add follwing lines at the beginning of your page:') + u'<br/><pre>' + save + u'</pre>'
868 else:
869 msg = self._(u'All values correspond to they default. Nothing have to be saved.')
870 return self.request.page.send_page (self.request, msg)
871
872 def send_pdf (self, data):
873 filename = self.pagename.replace (u'/', u'-') + u'-v' + str(self.values[u'rev']) + u'.pdf'
874
875 # Send HTTP header.
876 self.request.http_headers([
877 'Content-Type: %s' % self.contenttype,
878 'Content-Length: %d' % len(data),
879 # TODO: fix the encoding here, plain 8 bit is not allowed
880 # according to the RFCs There is no solution that is
881 # compatible to IE except stripping non-ascii chars
882 'Content-Disposition: inline; filename="%s"' % filename.encode(config.charset),
883 ])
884
885 # Send binary data.
886 sio = StringIO.StringIO(data)
887 shutil.copyfileobj (sio, self.request, 8192)
888
889 def get_html(self):
890 """Generate the HTML body of this page."""
891 # Save page as HTML.
892 newreq = RedirectOutputRequest(self.request)
893 (html, errmsg) = newreq.run(rev = self.values.get(u'rev', None))
894 if html:
895 html = self.fixhtmlstr(html)
896 # Make URLs absolute.
897 # FIXME: Until MoinMoin is not XHTML compilant we can not use a XML parser
898 # (e.g. expat) to transform the HTML document. In the meantime we try to
899 # achive the same with regular expressions subtitution.
900 base = self.request.getQualifiedURL()
901 for htmlref in [u'src', u'href']:
902 reurlref = r'(%s=[\'"])(/[^\'"]*)[\'"]' % (htmlref,)
903 urlref = re.compile (reurlref, re.I)
904 for match in urlref.finditer(html):
905 foundref = match.groups()
906 html = html.replace (foundref[0] + foundref[1], foundref[0] + base + foundref[1])
907 # Rename title of the document.
908 titletext_html = self.fixhtmlstr(wikiutil.escape(self.values['titletext']))
909 html = re.compile(r'<title>[^<]+</title>').sub(u'<title>%s</title>' % titletext_html, html)
910 else:
911 self.error_msg(self._(u'Could not redirect HTML output for further processing:') + errmsg)
912
913 return html
914
915 def make_isolang (self, language):
916 return language + u'_' + language.upper()
917
918 def html2pdf (self, html):
919 """Create a PDF document based on the current parameters."""
920 # Not all os support environment variable definition in one line with the calling application.
921 if os.name in ['posix', 'mac']:
922 htmldocopts = [u'LANG=' + self.values[u'language'], u'HTMLDOC_NOCGI=1']
923 else:
924 htmldocopts = []
925 # Determine UID to access ACL protected sites too (mandatory to download attached images).
926 htmldocopts += [u'htmldoc', "--cookies", "MOIN_ID=" + self.request.user.id, u'--no-duplex']
927
928 for key in [u'header', u'footer', u'tocheader', u'tocfooter']:
929 self.values[key] = self.values.get (key + u'left', u'.') + self.values.get (key + u'middle', u'.') + self.values.get (key + u'right', u'.')
930
931 permissions = []
932 for opt, value in self.values.items():
933 if opt in [u'language', u'debug', u'rev', u'titletext']:
934 continue
935 value = value.strip()
936 if not value:
937 continue
938 # Skip options for header/footer configuration which differenciate between position (e.g. footerright or tocheadermiddle)
939 if opt[:6] in [u'header', u'footer'] and opt[6:] or opt[:9] in [u'tocheader', u'tocfooter'] and opt[9:]:
940 continue
941 if opt in [u'style']:
942 htmldocopts += [u'--' + value]
943 elif opt in self.form_checkbox:
944 if value == u'checked':
945 if opt[:10] == u'permission':
946 permissions += [opt[10:]]
947 # Reverse meaning of 'no-' options.
948 elif opt[:3] != u'no-':
949 htmldocopts += [u'--' + opt]
950 elif opt[:3] == u'no-':
951 htmldocopts += [u'--' + opt]
952 elif opt[:6] == u'margin':
953 htmldocopts += [u'--' + opt[6:], value]
954 else:
955 htmldocopts += [u'--' + opt, value]
956 if permissions:
957 htmldocopts += [u'--permission', u','.join (permissions)]
958 htmldocopts += [u'-']
959 # Do not forget to escape all spaces!
960 eschtmldocopts = [arg.replace(u' ', u'\\ ') for arg in htmldocopts]
961 cmdstr = u' '.join(eschtmldocopts)
962 errmsg = None
963
964 pdf = None
965 if self.debug:
966 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>'
967 else:
968 inp, out, err = os.popen3 (cmdstr, u'b')
969 try:
970 inp.write(html)
971 inp.close()
972 except:
973 pass
974
975 pdf = out.read()
976 out.close()
977
978 htmldocmsg = err.read()
979 err.close()
980
981 if os.name in ['posix', 'mac']:
982 try:
983 # REMARK: Otherwise we get <defunct> processes.
984 os.wait()
985 except OSError, e:
986 # 10: No child processes.
987 if e.errno != 10:
988 raise
989
990 # Check for error message on STDOUT.
991 if pdf[:8] == u'HTMLDOC ':
992 htmldocmsg += pdf
993 pdf = None
994
995 if htmldocmsg:
996 errmsg = self._(u'Command:') + u'<pre>' + wikiutil.escape(cmdstr) + u'</pre>' + self._('returned:') + u'<pre>' + \
997 wikiutil.escape(htmldocmsg).replace(u'\n', u'<br/>') + u'</pre>'
998
999 # As it is difficult to get the htmldoc return code, we check for
1000 # error by checking the produced pdf length
1001 if not pdf and errmsg:
1002 self.error_msg(errmsg)
1003 return None
1004 elif pdf[:4] != '%PDF':
1005 self.error_msg(self._(u'Invalid PDF document generated') + wikiutil.escape(pdf[:80]))
1006 return None
1007
1008 return pdf
1009
1010
1011 def execute (pagename, request):
1012 try:
1013 CreatePdfDocument().run (pagename = pagename, request = request)
1014 except:
1015 raise
1016
1017 # vim:ts=4:et:sw=4:nu
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.