Attachment 'pdf.py'

Download

   1 # -*- coding: iso-8859-1 -*-
   2 r"""
   3 = pdf action =
   4 [[TableOfContents]]
   5 
   6 == Purpose ==
   7 
   8 This action is used to get a pdf copy of the page.
   9 
  10 It does an on-the-fly, server-side, html to pdf conversion using
  11 ''htmldoc'', and sends an `application/pdf` content to the web
  12 browser.
  13 
  14 == Calling sequence ==
  15 
  16 {{{
  17 http://my.server/MyWiki/MyPage?action=pdf
  18 
  19 http://my.server/MyWiki/MyPage?action=pdf&style=NAME
  20 
  21 http://my.server/MyWiki/MyPage?action=pdf&rev=NUM
  22 
  23 http://my.server/MyWiki/MyPage?action=pdf&pageaction=ACTION
  24 
  25 http://my.server/MyWiki/MyPage?action=pdf&help=1}}}
  26 
  27  style:::
  28    `page` or `book`.
  29    These styles can be redefined or extended in the wiki config (see
  30    below).  Asking for an unknown style will display an error and list
  31    available styles.
  32 
  33  rev:::
  34    Specifies a page version number.
  35 
  36  pageaction:::
  37   Specifies an action for rendering the page. By default and if
  38   omitted, the `print` action will be used.
  39 
  40  help:::
  41    Displays this help.
  42 
  43 
  44 == Procedure ==
  45 
  46 Usage:
  47  * Append `&action=pdf` to any URL
  48  * or insert the macro `[[pdf]]` (coming soon!) in a page, which will
  49  insert a small control panel
  50 
  51 Hints on the `book` style:
  52 
  53  * Have at least one heading, else ''htmldoc'' will return an error.
  54  
  55  * ''htmldoc'' will ignore any content before the first heading.
  56 
  57  * ''htmldoc'' automatically generates a table of contents which is
  58  looking nicer (has page numbers). To avoid double TOCs, place the
  59  `[[TableOfContents]]` macro at start of your page, before any
  60  heading. This way, the output of the macro will be ignored by
  61  ''htmldoc''.
  62 
  63 Suggestions:
  64  * Modify your themes to include a pdf link/icon besides print.
  65  * Modify the `AttachFile` standard action to add a pdf link.
  66 
  67 
  68 == Installation ==
  69 
  70  1. install ''htmldoc'' on the wiki server (on a Debian box:
  71  `apt-get install htmldoc`)
  72 
  73  1. copy the [http://moinmoin.wikiwikiweb.de/ActionMarket?action=AttachFile&do=get&target=pdf.py pdf.py] 
  74  action to the `data/plugin/action` directory of your wiki
  75 
  76 
  77 == Optional: customizing styles ==
  78 
  79 To install new styles, or modify the default ones, edit
  80 `wikiconfig.py` and add:
  81 {{{
  82     htmldoc_styles = {
  83         "page":
  84             "--verbose --no-localfiles --no-compression --jpeg " \
  85             "--header t.D --footer ./. --size a4 --left 0.5in " \
  86             "--webpage",
  87         "book":
  88             "--verbose --no-localfiles --no-compression --jpeg " \
  89             "--header t.D --footer ./. --size a4 --left 0.5in",
  90         } }}}
  91 
  92 By this means, you can change the page format from `a4` to `letter`,
  93 or add new styles to the dictionary.
  94 
  95 For a complete list of available ''htmldoc'' options, see
  96 http://www.easysw.com/htmldoc/docfiles/8-cmdref.html
  97 
  98 
  99 == Modification History ==
 100 {{{
 101 @copyright: 2006  by Pascal Bauermeister
 102 @license: GNU GPL, see COPYING for details.
 103 
 104 2006-05-24 v1.0.0 PascalBauermeister
 105  Initial revision
 106 
 107 2006-05-26 v1.0.1 PascalBauermeister
 108  * Set env var HTMLDOC_NOCGI to solve CGI issue
 109 
 110 2006-05-26 v1.0.2 PascalBauermeister
 111  * Relative image URLs turned absolute was bogus. It is less bogus now.
 112 
 113 }}}
 114 """
 115 
 116 import os, mimetypes, time, zipfile
 117 from MoinMoin import config, user, util, wikiutil, packages
 118 from MoinMoin.Page import Page
 119 from MoinMoin.util import MoinMoinNoFooter, filesys
 120 
 121 action_name = __name__.split('.')[-1]
 122 def_style = 'page'
 123 
 124 def error_msg (pagename, request, msg):
 125     Page (request, pagename).send_page (request, msg=msg)
 126 
 127         
 128 def format (src_text, request, formatter):
 129     # parse the text (in wiki source format) and make HTML,
 130     # after diverting sys.stdout to a string
 131     import StringIO
 132     from MoinMoin.parser import wiki
 133     str_out = StringIO.StringIO ()      # create str to collect output
 134     request.redirect (str_out)          # divert output to that string
 135     p = Page (request, "$$$")
 136     formatter.setPage (p)
 137     try:                                # parse text
 138         wiki.Parser (src_text, request).format (formatter)
 139     finally:
 140         request.redirect ()             # restore output
 141     formatter.setPage (request.page)
 142     return str_out.getvalue ()          # return what was generated
 143 
 144 
 145 def get_style (request, name):
 146     """Defines built-in, default styles. These styles can be exended
 147     or replaced by declaring a dictionary similar to def_styles
 148     below, but named htmldoc_styles, in the wikiconfig.py file.
 149     """
 150     _ = request.getText
 151 
 152     def_styles = {
 153         "page":
 154         "--verbose --no-localfiles --no-compression --jpeg " \
 155         "--header t.D --footer ./. --size a4 --left 0.5in " \
 156         "--webpage",
 157         "book":
 158         "--verbose --no-localfiles --no-compression --jpeg " \
 159         "--header t.D --footer ./. --size a4 --left 0.5in",
 160         }
 161 
 162     try: # get styles from config, to update the default ones
 163         cfg_styles = request.cfg.htmldoc_styles
 164         styles = def_styles
 165         styles.update (cfg_styles)
 166     except AttributeError:
 167         styles = def_styles
 168 
 169     try: # get wanted style
 170         style = styles [name]
 171     except KeyError:
 172         msg = _("Unknown style: ") + name + "<br>" + \
 173               _("Possible styles: ") + ', '.join (styles.keys ()) + "<br>" + \
 174               _("Default style: ") + def_style
 175         error_msg (request.page.page_name, request, msg)
 176         return None
 177         
 178     return style
 179 
 180             
 181 def escape (str):
 182     return str.replace ('&','&amp;').replace ('<', '&lt;').replace ('>', '&gt;')
 183 
 184 
 185 def execute (pagename, request):
 186     """ Main dispatcher for the action.
 187     """
 188     _ = request.getText
 189 
 190     debug = 0
 191     revision = None
 192     pageaction = None
 193     style = def_style
 194     msg = None
 195 
 196     if request.form.has_key ('rev'):
 197         revision = request.form ['rev'] [0]
 198     if request.form.has_key ('pageaction'):
 199         pageaction = request.form ['pageaction'] [0]
 200     if request.form.has_key ('style'):
 201         style = request.form ['style'] [0]
 202     if request.form.has_key ('help'):
 203         help = format (__doc__, request, request.formatter)
 204         error_msg (pagename, request, help)
 205 	return
 206     if request.form.has_key ('debug'):
 207         debug = 1
 208 
 209     # make pdf
 210     html = get_html (request, revision, pageaction, debug)
 211 
 212     flags = get_style (request, style)
 213     if not flags: return
 214 
 215     data = html2pdf (request, html, flags, debug)
 216     if not data:  return
 217     
 218     # send it
 219     filename = pagename.replace ('/', '-') + ".pdf"
 220     send_pdf (filename, request, data, debug)
 221     return
 222 
 223 
 224 def send_pdf (filename, request, data, debug):
 225     import shutil, StringIO
 226 
 227     # get mimetype
 228     type = "application/pdf"
 229     if debug: type = "text/plain"
 230 
 231     # send header
 232     request.http_headers([
 233         "Content-Type: %s" % type,
 234         "Content-Length: %d" % len (data),
 235         # TODO: fix the encoding here, plain 8 bit is not allowed
 236         # according to the RFCs There is no solution that is
 237         # compatible to IE except stripping non-ascii chars
 238         "Content-Disposition: inline; filename=\"%s\"" %
 239         filename.encode (config.charset),
 240         ])
 241 
 242     # send data
 243     sio = StringIO.StringIO (data)
 244     shutil.copyfileobj (sio, request, 8192)
 245 
 246     raise MoinMoinNoFooter
 247 
 248 
 249 # regex to match the source url of <img> tags
 250 import re
 251 RX_IMG = re.compile (r'(<img.*? )src=.(/.*?>)', re.I)
 252 
 253 
 254 def get_html (request, revision, pageaction, debug):
 255     """
 256     get the html body of this page
 257 
 258     @param request:    the request for the page
 259     @param revision:   wanted revision (None for head)
 260     @param pageaction: action for page (None for print)
 261     """
 262     import copy, sys, StringIO
 263 
 264     # Get HTML for the page:
 265     #   make a copy of the request, run it and intercept the output
 266     orig_form = {}
 267     orig_form.update (request.form)
 268     #   set form:
 269     if pageaction == action_name : pageaction = None
 270     if pageaction: request.form ["action"] = [pageaction]
 271     else:          request.form ["action"] = [u"print"]
 272     if revision:   request.form ["rev"] = [revision]
 273     #   divert stdout, re-run the request
 274     old_stdout = sys.stdout
 275     out = StringIO.StringIO ()
 276     sys.stdout = out
 277     copy.copy (request).run ()
 278     html = out.getvalue ()
 279     out.close ()
 280     #   restore things
 281     sys.stdout = old_stdout
 282     request.form = orig_form
 283 
 284     # Do some post-processing on th ehtml
 285 
 286     #   lf -> cr    
 287     html = html.replace ('\r\n', '\n')
 288     html = html.replace ('\r', '\n')
 289 
 290     #   remove headers
 291     lines = html.split ('\n')
 292     while lines [0].strip (): del lines [0]
 293     html = '\n'.join (lines)
 294 
 295     #   look for non-html content (TODO: better done by examining
 296     #   headers)
 297     start = html.strip () [0:20] .lower ()
 298     if not (start.startswith ("<html") or start.startswith ("<!doctype html")):
 299         return "<pre>%s</pre>" % html
 300     
 301     #   make images URLs absolute
 302     base = request.getQualifiedURL()
 303     #   this is bogus because other attributes may preceed src...
 304     ##    html = html.replace ('<img src="/', '<img src="' + base + '/')
 305     #   instead, find URL in <img ... src="URL">, and replace URL by the
 306     #   same prefixed by the URL base:
 307     matches = []
 308     for match in RX_IMG.finditer (html):
 309         m = match.groups () [1]
 310         if m not in matches:
 311             html = html.replace (m, base+m)
 312             matches.append (m)
 313     #   this is not really proof... but I'm too lazy to do an HTML
 314     #   parser for this !
 315 
 316     return html
 317 
 318     
 319 def html2pdf (request, html, flags, debug):
 320     import  os
 321 
 322     cmd = "HTMLDOC_NOCGI=1 htmldoc -t pdf %s -" % flags
 323     inp, out, err = os.popen3 (cmd, 'b')
 324     inp.write (html)
 325     inp.close ()
 326 
 327     pdf = out.read ()
 328     out.close ()
 329     msg = "Command: <pre>" + escape (cmd) + "</pre>returned:<pre>"+ \
 330           escape (err.read ()).replace ('\n','<br>') + "</pre>"
 331     err.close ()
 332 
 333     # as it is difficult to get the htmldoc return code, we check for
 334     # error by checking the produced pdf length
 335     if not len (pdf):
 336         error_msg (request.page.page_name, request, msg)
 337 
 338     if debug: return html
 339     return pdf

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.
  • [get | view] (2011-04-14 07:28:03, 4.7 KB) [[attachment:CreateNewPage.py]]
  • [get | view] (2011-04-14 07:26:24, 4.2 KB) [[attachment:CreateNewPage1.6.py]]
  • [get | view] (2006-09-10 21:29:29, 40.4 KB) [[attachment:CreatePdfDocument2_0_3.py]]
  • [get | view] (2006-09-12 06:05:06, 40.5 KB) [[attachment:CreatePdfDocument2_0_4.py]]
  • [get | view] (2006-09-12 12:00:09, 40.6 KB) [[attachment:CreatePdfDocument2_0_5.py]]
  • [get | view] (2006-11-14 21:56:11, 43.5 KB) [[attachment:CreatePdfDocument2_0_6.py]]
  • [get | view] (2006-11-15 17:00:47, 43.8 KB) [[attachment:CreatePdfDocument2_0_7.py]]
  • [get | view] (2006-11-16 22:06:18, 43.8 KB) [[attachment:CreatePdfDocument2_0_8.py]]
  • [get | view] (2006-12-17 15:54:21, 43.6 KB) [[attachment:CreatePdfDocument2_0_9.py]]
  • [get | view] (2007-08-20 09:10:23, 67.2 KB) [[attachment:CreatePdfDocument2_1_0.py]]
  • [get | view] (2007-08-21 07:39:49, 67.1 KB) [[attachment:CreatePdfDocument2_1_1.py]]
  • [get | view] (2007-09-11 19:16:37, 67.3 KB) [[attachment:CreatePdfDocument2_1_2.py]]
  • [get | view] (2007-09-18 20:17:58, 68.1 KB) [[attachment:CreatePdfDocument2_1_3.py]]
  • [get | view] (2007-09-21 13:32:54, 71.1 KB) [[attachment:CreatePdfDocument2_1_4.py]]
  • [get | view] (2007-09-23 20:56:30, 73.4 KB) [[attachment:CreatePdfDocument2_1_5.py]]
  • [get | view] (2007-09-25 20:54:48, 74.5 KB) [[attachment:CreatePdfDocument2_2_0.py]]
  • [get | view] (2008-06-23 21:08:49, 77.0 KB) [[attachment:CreatePdfDocument2_3_0.py]]
  • [get | view] (2008-06-26 19:25:07, 81.0 KB) [[attachment:CreatePdfDocument2_3_1.py]]
  • [get | view] (2008-07-06 05:50:38, 83.1 KB) [[attachment:CreatePdfDocument2_3_2.py]]
  • [get | view] (2008-07-09 17:42:02, 83.3 KB) [[attachment:CreatePdfDocument2_3_3.py]]
  • [get | view] (2008-09-07 11:11:01, 83.5 KB) [[attachment:CreatePdfDocument2_3_4.py]]
  • [get | view] (2009-01-11 15:53:09, 84.3 KB) [[attachment:CreatePdfDocument2_3_5.py]]
  • [get | view] (2009-02-16 06:52:06, 84.2 KB) [[attachment:CreatePdfDocument2_3_6.py]]
  • [get | view] (2010-01-29 11:53:21, 82.8 KB) [[attachment:CreatePdfDocument2_4_0.py]]
  • [get | view] (2010-01-31 14:10:03, 84.6 KB) [[attachment:CreatePdfDocument2_4_1.py]]
  • [get | view] (2010-09-18 16:23:23, 85.6 KB) [[attachment:CreatePdfDocument2_4_2.py]]
  • [get | view] (2006-06-16 20:56:53, 4.9 KB) [[attachment:FlashManager.py-1.5.3-1]]
  • [get | view] (2003-12-07 18:15:53, 3.9 KB) [[attachment:HTML2MoinMoin.py]]
  • [get | view] (2005-10-16 08:24:35, 4.9 KB) [[attachment:HelpOn-1.3.5-4.py]]
  • [get | view] (2006-02-03 19:21:04, 4.9 KB) [[attachment:HelpOn-1.5.1-5.py]]
  • [get | view] (2006-07-04 10:45:22, 4.8 KB) [[attachment:HelpOn-1.5.4-6.py]]
  • [get | view] (2006-07-04 22:39:14, 4.8 KB) [[attachment:HelpOn-1.6.0-7.py]]
  • [get | view] (2006-07-06 13:50:17, 4.0 KB) [[attachment:HelpOn-1.6.0-8.py]]
  • [get | view] (2008-01-10 17:43:24, 4.8 KB) [[attachment:HelpOn-1.6.0-9.py]]
  • [get | view] (2008-08-19 14:44:54, 5.0 KB) [[attachment:HelpOn-1.7.1-10.py]]
  • [get | view] (2005-02-20 18:28:34, 10.8 KB) [[attachment:IRSS.py]]
  • [get | view] (2005-03-09 22:46:23, 2.9 KB) [[attachment:ImportHtml-1.2.py]]
  • [get | view] (2003-12-07 18:15:53, 2.8 KB) [[attachment:ImportHtml.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:IrcChat.py]]
  • [get | view] (2008-06-09 11:27:20, 4.4 KB) [[attachment:MoinCrypt.py]]
  • [get | view] (2010-11-29 12:08:27, 7.5 KB) [[attachment:PageActions.py]]
  • [get | view] (2006-08-07 15:12:19, 0.5 KB) [[attachment:PermanentLink.py]]
  • [get | view] (2003-12-07 18:15:53, 6.3 KB) [[attachment:PhoneDial.py]]
  • [get | view] (2005-04-17 14:21:47, 3.6 KB) [[attachment:RecommendPage-1.3.4-1.py]]
  • [get | view] (2005-04-19 18:21:52, 5.5 KB) [[attachment:RecommendPage-1.3.4-2.py]]
  • [get | view] (2005-05-02 19:53:09, 5.6 KB) [[attachment:RecommendPage-1.3.4-3.py]]
  • [get | view] (2005-09-03 07:33:35, 6.3 KB) [[attachment:RecommendPage-1.3.4-4.py]]
  • [get | view] (2005-09-05 17:44:03, 6.9 KB) [[attachment:RecommendPage-1.3.5-5.py]]
  • [get | view] (2005-09-07 16:42:26, 7.5 KB) [[attachment:RecommendPage-1.3.5-6.py]]
  • [get | view] (2005-09-08 16:06:28, 7.7 KB) [[attachment:RecommendPage-1.3.5-7.py]]
  • [get | view] (2005-11-01 11:31:51, 9.0 KB) [[attachment:RecommendPage-1.3.5-8.py]]
  • [get | view] (2006-02-03 19:40:51, 8.3 KB) [[attachment:RecommendPage-1.5.1-9.py]]
  • [get | view] (2008-01-11 09:14:35, 6.8 KB) [[attachment:RecommendPage-1.6.0-10.py]]
  • [get | view] (2008-08-19 14:44:59, 6.9 KB) [[attachment:RecommendPage-1.7.1-11.py]]
  • [get | view] (2008-06-09 11:27:40, 1.7 KB) [[attachment:ShowActions.py]]
  • [get | view] (2008-06-09 10:34:02, 5.3 KB) [[attachment:ShowDecrypted.py]]
  • [get | view] (2005-03-30 21:17:28, 7.7 KB) [[attachment:Slideshow.py]]
  • [get | view] (2004-02-02 20:48:31, 2.0 KB) [[attachment:SubscribeUser.py]]
  • [get | view] (2007-01-26 17:08:30, 2.2 KB) [[attachment:Subscribers-1.6.0.py]]
  • [get | view] (2003-12-07 18:15:53, 1.8 KB) [[attachment:Subscribers.py]]
  • [get | view] (2006-03-18 23:16:51, 0.8 KB) [[attachment:UserPreferences.py]]
  • [get | view] (2004-01-05 09:56:25, 8.1 KB) [[attachment:VisualSiteMap.py]]
  • [get | view] (2015-08-30 21:04:23, 11.1 KB) [[attachment:VisualSiteMap_1.10.py]]
  • [get | view] (2004-10-08 10:59:16, 9.3 KB) [[attachment:VisualSiteMap_1.2.py]]
  • [get | view] (2005-03-16 01:30:09, 9.8 KB) [[attachment:VisualSiteMap_1.3.py]]
  • [get | view] (2014-08-19 01:34:10, 10.8 KB) [[attachment:VisualSiteMap_1.9.py]]
  • [get | view] (2007-08-18 18:52:55, 1.0 KB) [[attachment:backlink.py]]
  • [get | view] (2007-03-15 05:53:49, 23.5 KB) [[attachment:findandreplace0.1Beta.py]]
  • [get | view] (2005-03-27 20:32:10, 3.6 KB) [[attachment:gallery2image-1.3.3-1.py]]
  • [get | view] (2005-08-03 20:14:56, 4.0 KB) [[attachment:gallery2image-1.3.3-2.py]]
  • [get | view] (2005-11-13 18:10:26, 20.7 KB) [[attachment:gallery2image-1.3.5-10.py]]
  • [get | view] (2005-11-25 22:03:50, 20.8 KB) [[attachment:gallery2image-1.3.5-11.py]]
  • [get | view] (2005-08-08 17:23:43, 8.4 KB) [[attachment:gallery2image-1.3.5-4.py]]
  • [get | view] (2005-08-13 15:15:45, 13.7 KB) [[attachment:gallery2image-1.3.5-5.py]]
  • [get | view] (2005-08-31 22:05:22, 15.5 KB) [[attachment:gallery2image-1.3.5-6.py]]
  • [get | view] (2005-10-29 20:23:50, 15.9 KB) [[attachment:gallery2image-1.3.5-8.py]]
  • [get | view] (2005-11-01 11:31:24, 17.6 KB) [[attachment:gallery2image-1.3.5-9.py]]
  • [get | view] (2006-01-27 20:52:32, 20.9 KB) [[attachment:gallery2image-1.5.1-12.py]]
  • [get | view] (2006-08-06 09:01:01, 22.1 KB) [[attachment:gallery2image-1.5.4-13.py]]
  • [get | view] (2006-08-11 18:21:40, 22.2 KB) [[attachment:gallery2image-1.5.4-14.py]]
  • [get | view] (2006-11-16 20:23:27, 22.6 KB) [[attachment:gallery2image-1.5.6-16.py]]
  • [get | view] (2006-08-11 18:30:22, 22.2 KB) [[attachment:gallery2image-1.6.0-15.py]]
  • [get | view] (2008-02-06 10:13:45, 22.3 KB) [[attachment:gallery2image-1.6.0-16.py]]
  • [get | view] (2008-05-20 15:51:09, 22.4 KB) [[attachment:gallery2image-1.6.3-17.py]]
  • [get | view] (2006-09-06 06:19:48, 1.3 KB) [[attachment:getmmap.py]]
  • [get | view] (2004-07-18 09:48:00, 1.5 KB) [[attachment:localnames.py]]
  • [get | view] (2005-03-25 15:02:31, 2.6 KB) [[attachment:newpageonly.py]]
  • [get | view] (2005-03-30 09:02:00, 3.5 KB) [[attachment:newpageonly_20050330.py]]
  • [get | view] (2006-06-06 19:12:27, 9.7 KB) [[attachment:pdf.py]]
  • [get | view] (2006-08-30 10:51:51, 36.0 KB) [[attachment:pdf2_0_0.py]]
  • [get | view] (2006-08-30 13:57:36, 36.5 KB) [[attachment:pdf2_0_1.py]]
  • [get | view] (2006-02-04 04:25:29, 1.0 KB) [[attachment:sisterindex.py]]
  • [get | view] (2004-10-28 07:33:10, 0.7 KB) [[attachment:xml.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.