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