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