1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - Export as PDF action 4 5 @copyright: 2013 by Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 from MoinMoin.action import ActionBase 10 from MoinMoin.wikiutil import escape 11 from MoinSupport import ActionSupport, escattr, getFormatterClass, formatText, get_send_headers 12 from os.path import join 13 import subprocess 14 15 __version__ = "0.1" 16 17 Dependencies = ['page'] 18 19 # Configuration settings. 20 21 XSLT_PROCESSOR = "/usr/bin/xsltproc" 22 FO_PROCESSOR = "/usr/bin/fop" 23 DOCBOOK_STYLESHEET_BASE = "/usr/share/xml/docbook/stylesheet" 24 25 # Tool settings. 26 27 DOCBOOK_TO_FO_STYLESHEET = "docbook-xsl/fo/docbook.xsl" 28 29 # NOTE: From docbook-xsl/fo/param.xsl. 30 31 paper_sizes = [ 32 "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "A10", 33 "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "B10", 34 "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "C10", 35 "A4landscape", "USletter", "USlandscape", "4A0", "2A0", 36 ] 37 38 paper_size_labels = { 39 "A4landscape" : "A4 landscape", 40 "USletter" : "US letter", 41 "USlandscape" : "US landscape", 42 "4A0" : "Quadruple A0", 43 "2A0" : "Double A0" 44 } 45 46 class ExportPDF(ActionBase, ActionSupport): 47 48 "Export the current page as PDF." 49 50 def get_form_html(self, buttons_html): 51 52 "Return the action's form incorporating the 'buttons_html'." 53 54 _ = self._ 55 request = self.request 56 form = self.get_form() 57 58 paper_size = form.get("paper-size", ["A4"])[0] 59 60 paper_size_options = [] 61 62 for size in paper_sizes: 63 paper_size_options.append('<option value="%s" %s>%s</option>' % ( 64 escattr(size), self._get_selected(size, paper_size), 65 escape(_(paper_size_labels.get(size) or size)) 66 )) 67 68 d = { 69 "paper_size_label" : escape(_("Paper size")), 70 "paper_size_options" : u"".join(paper_size_options), 71 "buttons_html" : buttons_html, 72 } 73 74 return u"""\ 75 <table> 76 <tr> 77 <td class="label"><label>%(paper_size_label)s</label></td> 78 <td><select name="paper-size">%(paper_size_options)s</select></td> 79 </tr> 80 <tr> 81 <td></td> 82 <td class="buttons">%(buttons_html)s</td> 83 </tr> 84 </table> 85 """ % d 86 87 def do_action(self): 88 89 "Attempt to post a comment." 90 91 _ = self._ 92 request = self.request 93 form = self.get_form() 94 page = self.page 95 96 paper_size = form.get("paper-size", [""])[0] 97 98 if not paper_size in paper_sizes: 99 return 0, _("A paper size must be chosen.") 100 101 # Get the page in DocBook format. 102 103 fmt = getFormatterClass(request, "text_docbook")(request) 104 fmt.setPage(page) 105 106 # The DocBook formatter needs to pretend a full document is being made. 107 108 page_as_docbook = [] 109 append = page_as_docbook.append 110 111 append(fmt.startDocument(page.page_name)) 112 append(fmt.startContent()) 113 append(formatText(page.get_raw_body(), request, fmt, inhibit_p=False).encode("utf-8")) 114 append(fmt.endContent()) 115 append(fmt.endDocument()) 116 117 # Send the DocBook XML to the XSLT processor. 118 119 p1 = subprocess.Popen([ 120 XSLT_PROCESSOR, 121 "-stringparam", "fop1.extensions", "1", 122 "--stringparam", "paper.type", paper_size, 123 join(DOCBOOK_STYLESHEET_BASE, DOCBOOK_TO_FO_STYLESHEET), 124 "-" 125 ], 126 shell=False, 127 stdin=subprocess.PIPE, 128 stdout=subprocess.PIPE, 129 stderr=subprocess.PIPE) 130 131 p1.stdin.write("".join(page_as_docbook)) 132 p1.stdin.close() 133 134 # Pipe the XML-FO output to the FO processor. 135 136 p2 = subprocess.Popen([ 137 FO_PROCESSOR, 138 "-fo", "-", 139 "-pdf", "-", 140 ], 141 shell=False, 142 stdin=p1.stdout, 143 stdout=subprocess.PIPE, 144 stderr=subprocess.PIPE) 145 146 out, err = p2.communicate() 147 148 retcode = p1.wait() 149 150 if retcode != 0: 151 return 0, err 152 153 retcode = p2.wait() 154 155 if retcode != 0: 156 return 0, err 157 158 send_headers = get_send_headers(request) 159 headers = ["Content-Type: application/pdf"] 160 send_headers(headers) 161 request.write(out) 162 163 return 1, None 164 165 def render_success(self, msg, msgtype=None): 166 167 """ 168 Render neither 'msg' nor 'msgtype' since a resource has already been 169 produced. 170 NOTE: msgtype is optional because MoinMoin 1.5.x does not support it. 171 """ 172 173 pass 174 175 # Action invocation function. 176 177 def execute(pagename, request): 178 ExportPDF(pagename, request).render() 179 180 # vim: tabstop=4 expandtab shiftwidth=4