If you benefit from web2py hope you feel encouraged to pay it forward by contributing back to society in whatever form you choose!

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)

Related slices

Comments (1)

  • Login to post



  • 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 ?

     

     

     


Hosting graciously provided by:
Python Anywhere