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