Monday, November 25, 2013

PDF in a CherryPy


By Vasudev Ram

PDF



I've known about CherryPy, a minimalist Python web framework (originally created by Remi Delon), since its early days, and have tried it out some.

Recently I had the idea (*) of using CherryPy and my xtopdf PDF creation toolkit together, to serve PDF via a web app.

(*) I thought of it because of my earlier two posts, PDF in a Flask and PDF in a Bottle.

The difference between the Flask and Bottle apps and this CherryPy app, is that the former two use a form to submit the text to be converted to PDF, whereas this app lets you upload a file to convert to PDF. Here's the code for PDF in a CherryPy (pdf_cherry.py):

# pdf_cherry.py
# Author: Vasudev Ram - http://www.dancingbison.com
# Program to generate PDF using CherryPy and xtopdf.
# Requires Reportlab v1.21 open source version.
# Written using CherryPy 3.2.4 and Python 2.7.5.

import os
from PDFWriter import PDFWriter
from file_utils import change_file_ext
from textwrap import TextWrapper

import os
import cherrypy
from cherrypy.lib.static import serve_file

class Root(object):
    pass

class Upload(object):

    def index(self):
        return """
        <html><head><title>PDF in a CherryPy</title></head><body>
            <h3>PDF in a CherryPy</h3>
            <h4>Upload a text file to convert to PDF.</h4>
            <form action="text_to_pdf" method="post" enctype="multipart/form-data">
            filename: <input type="file" name="uploadFile" /><br/>
            <input type="submit"/>
            </form>
        </body></html>
        """
    index.exposed = True

    def text_to_pdf(self, uploadFile):
        txt_filename = uploadFile.filename
        pdf_filename = change_file_ext(txt_filename, ".txt", ".pdf")
        pw = PDFWriter(pdf_filename)
        pw.setFont("Courier", 10)
        pw.setHeader("Text file: " + txt_filename + \
            " PDF file: " + pdf_filename)
        pw.setFooter("Generated by CherryPy, xtopdf and Reportlab")
        wrapper = TextWrapper(width = 120, drop_whitespace = True,
            break_long_words = True)
        for lin in uploadFile.file.readlines():
            lines = wrapper.wrap(lin)
            for lin2 in lines:
                pw.writeLine(lin)
        pw.close()
        respons = "<html><head><title>PDF in a CherryPy</title></head>"
        respons += "<body><h3>PDF in a CherryPy</h3>"
        respons += "Text file: " + txt_filename + "<br/>"
        respons += 'PDF file: <a href="/download/?filepath=' + os.getcwd() + \
            "\\" + pdf_filename + '">' + \
            pdf_filename + '</a><br/>'
        respons += "</body></html>"
        return respons

    text_to_pdf.exposed = True

class Download:

    def index(self, filepath):
        return serve_file(filepath, "application/x-download", "attachment")
    index.exposed = True

def main():
    root = Root()
    root.upload = Upload()
    root.download = Download()
    cherrypy.quickstart(root)

if __name__ == '__main__':
    main()

# EOF

Run it with:
python pdf_cherry.py
Then go to localhost:8080/upload in your browser, select a text file to convert to PDF, and click Submit. If there are no errors, you'll get another page with a link to download the generated PDF.

Here's a screenshot of pdf_cherry.py eating its own dog food, sort of :-)


I'm adding pdf_cherry.py to my xtopdf project on Bitbucket. [Update: Done.]

See other posts about Python, xtopdf, CherryPy, and web servers on my blog.

(The xtopdf posts are a subset of the Python posts, since xtopdf is written in Python.)

Users of CherryPy include Hulu and Netflix.

- Vasudev Ram - Python training and consulting





1 comment:

Vasudev Ram said...

Added error handling to the pdf_cherry.py code in my xtopdf project on Bitbucket.

xtopdf.

I'm not updating the post above, but readers can get the updated code from Bitbucket.