Here is how we make a PDF output ready app thanks to the fpdf library included with web2py
An FPDF web2py silly report Copyright (C) 2013 Alan Etkin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. This license note applies only for the recipe below. For web2py or fpdf licenses refer to http://code.google.com/p/web2py and http://code.google.com/p/fpdf
The dummy db (change with your own model):
db.define_table("person", Field("name"), format="%(name)s") db.define_table("dog", Field("name"), Field("owned_by", "reference person")) if db(db.person).count() < 1: for name in (("Bartolomé Mitre", "Mendieta"), ("Julio Argentino Roca", "Diógenes"), ("Domingo Faustino Sarmiento", "Juan"), ("Terry Gilliam", "Spam")): owned_by = db.person.insert(name=name[0]) db.dog.insert(name=name[1], owned_by=owned_by) # we change the scaffolding model a bit because of this issue # html: http://code.google.com/p/web2py/issues/detail?id=1338 # the code below is a modified version of the function at # gluon.contrib.generics def pyfpdf_from_html(html): import os from gluon.contrib.fpdf import FPDF, HTMLMixin def image_map(path): if path.startswith('/%s/static/' % request.application): return os.path.join(request.folder, path.split('/', 2)[2]) return 'http%s://%s%s' % (request.is_https and 's' or '', request.env.http_host, path) class MyFPDF(FPDF, HTMLMixin): pass pdf = MyFPDF() pdf.add_page() pdf.write_html(html, image_map=image_map) return XML(pdf.output(dest='S'))
A little customization of views/generic.pdf (make a backup of the original file if you want, i.e. as generic.pdf.original)
{{ import os # we comment this as we are using a custom model function # from gluon.contrib.generics import pdf_from_html filename = '%s/%s.html' % (request.controller,request.function) if os.path.exists(os.path.join(request.folder,'views',filename)): html=response.render(filename) else: html=BODY(BEAUTIFY(response._vars)).xml() pass # note the pyfpdf instead of pdf in the function call =pyfpdf_from_html(html) }}
Now our featured view (default/dogs.html):
{{def text(atext): return atext.decode("utf-8").encode("latin-1") }} {{=HTML(BODY(IMG(_src=URL(c="static", f="images/fpdf.png")), BR(), BR(), BR(), BR(), TABLE(THEAD(TR(TD("Dogs", _width=200), TD("Owners", _width=400))), TBODY(*[TR(TD(text(dog.name), _width=200), TD(text(dog.owned_by.name), _width=400)) for dog in dogs]))))}}
Note that you must add a static/images/fpdf.png file to your app or you'll get a path error ticket
And finally a controller without a tape recorder up his nose (default.py):
def dogs(): dogs = db(db.dog).select() return dict(dogs=dogs)
Comments (1)
0
andrew-willimott-10653 10 years ago
Great Post, thanks.
Is there a reason why the fix for def pyfpdf_from_html hasn't been made to the base web2py app ? This isn't just a change to the scaffolding app, it changes the contrib part of web2py. Do the allowed tags need to be aupdated ?