I have a function which generates a PDF and I have a Flask website. I would like to combine the two so that when you visit the site, a PDF is generated and downloaded. I am working on combining various bits of code that I don't fully understand. The PDF is generated and downloaded but fails to ever load when I try to open it. What am I missing?
import cStringIO
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from flask import make_response, Flask
from reportlab.pdfgen import canvas
app = Flask(__name__)
#app.route('/')
def pdf():
output = cStringIO.StringIO()
doc = SimpleDocTemplate("test.pdf",pagesize=letter)
Story=[]
styles=getSampleStyleSheet()
styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
ptext = '<font size=12>test</font>'
Story.append(Paragraph(ptext, styles["Justify"]))
doc.build(Story)
pdf_out = output.getvalue()
output.close()
response = make_response(pdf_out)
response.headers['Content-Disposition'] = "attachment; filename='test.pdf"
response.mimetype = 'application/pdf'
return response
app.run()
You can use Flask's send_file for serving files:
from Flask import send_file
return send_file('test.pdf', as_attachment=True)
With as_attachment=True you can force the client to download the file instead of viewing it inside the browser.
Related
I'm trying to do the following:
Download a pdf file from S3 to my heroku.
Process the pdf.
Email the pdf as an attachment.
Is it possible? If yes, could you please give me a tip how?
I'm running Django and pdf is about 1MB.
This is my processing part:
from PyPDF2 import PdfFileWriter, PdfFileReader
from reportlab.pdfgen import canvas
from reportlab.lib.colors import HexColor
import os, sys
import requests
from io import BytesIO
URL = "https://domainname.com/sample.pdf"
response=requests.get(URL)
p = BytesIO(response.content)
p.seek(0, os.SEEK_END)
def watermark_product(watermark_text, input_file_path, output_file_path):
c = canvas.Canvas("watermark.pdf")
c.setFont("Helvetica", 24)
c.setFillGray(0.5,0.5)
c.saveState()
c.translate(500,100)
c.rotate(45)
c.drawCentredString(0, 300, watermark_text)
c.restoreState()
c.save()
input_file = PdfFileReader(input_file_path)
output_writer = PdfFileWriter()
total_pages = input_file.getNumPages()
for single_page in range(total_pages):
page = input_file.getPage(single_page)
watermark = PdfFileReader("watermark.pdf")
page.mergePage(watermark.getPage(0))
output_writer.addPage(page)
with open(output_file_path, "wb") as outputStream:
output_writer.write(outputStream)
os.remove("watermark.pdf")
watermark_product('testtesatd', p, 'w1.pdf')
EDIT:
I've managed to keep the pdf file in memory.
Here is my code in myclass.py
class Pdf():
def render_pdf(self,name,html):
from xhtml2pdf import pisa
from StringIO import StringIO
pdf = StringIO()
pisa.CreatePDF(StringIO(html), pdf)
return pdf
And I am calling it in api.py like this
#app.route('/invoice/<business_name>/<tin>', methods=['GET'])
def view_invoice(business_name,tin):
#pdf = StringIO()
html = render_template('certificate.html', business_name=business_name,tin=tin)
file_class = Pdf()
pdf = file_class.render_pdf(business_name,html)
return pdf
But it throws this error
AttributeError: StringIO instance has no __call__ method
The following script worked well for me. Note the changes I made:
Pdf.render_pdf() now returns pdf.getvalue(), a str.
view_invoice() now returns a tuple, so that the Content-Type header can be set.
#!/usr/bin/env python
from flask import Flask, render_template
app = Flask(__name__)
class Pdf():
def render_pdf(self, name, html):
from xhtml2pdf import pisa
from StringIO import StringIO
pdf = StringIO()
pisa.CreatePDF(StringIO(html), pdf)
return pdf.getvalue()
#app.route('/invoice/<business_name>/<tin>', methods=['GET'])
def view_invoice(business_name, tin):
#pdf = StringIO()
html = render_template(
'certificate.html', business_name=business_name, tin=tin)
file_class = Pdf()
pdf = file_class.render_pdf(business_name, html)
headers = {
'content-type': 'application.pdf',
'content-disposition': 'attachment; filename=certificate.pdf'}
return pdf, 200, headers
if __name__ == '__main__':
app.run(debug=True)
I am working on a Flask app which generates a dynamic plot and displays it through a jinja template. I would like to use the template to call a function in the Flask app which returns png data, and then embed the response in a data uri.
This gist is very close to my goal, except I would like to avoid using url_for (and thus routes). Instead, I would like to just display the image data inline using a data uri (img src="data:image/png;base64,...)
Instead of sending the output back as an image with a response, take the output and encode it to base64:
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
from base64 import b64encode
from io import BytesIO
png_output = BytesIO()
canvas.print_png(png_output)
data = b64encode(png_output.getvalue()).decode('ascii')
data_url = 'data:image/png;base64,{}'.format(quote(data))
This has the canvas write to an in-memory file, and the resulting PNG data is then encoded to base64 and interpolated in a data URL.
Complete solution based on your example, tested and working:
from flask import Flask, render_template
import urllib
app = Flask(__name__)
#app.route("/simple.png")
def simple():
import datetime
import StringIO
import random
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib.dates import DateFormatter
fig=Figure()
ax=fig.add_subplot(111)
x=[]
y=[]
now=datetime.datetime.now()
delta=datetime.timedelta(days=1)
for i in range(10):
x.append(now)
now+=delta
y.append(random.randint(0, 1000))
ax.plot_date(x, y, '-')
ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
fig.autofmt_xdate()
canvas=FigureCanvas(fig)
png_output = StringIO.StringIO()
canvas.print_png(png_output)
png_output = png_output.getvalue().encode("base64")
return render_template("test.html", img_data=urllib.quote(png_output.rstrip('\n')))
if __name__ == "__main__":
app.run()
Template:
<img src="data:image/png;base64,{{img_data}}"/>
I'm developing an application that runs on an Apache server with Django framework. My current script works fine when it runs on the local desktop (without Django). The script downloads all the images from a website to a folder on the desktop. However, when I run the script on the server a file object is just create by Django that apparently has something in it (should be google's logo), however, I can't open up the file. I also create an html file, updated image link locations, but the html file gets created fine, I'm assuming because it's all text, maybe? I believe I may have to use a file wrapper somewhere, but I'm not sure. Any help is appreciated, below is my code, Thanks!
from django.http import HttpResponse
from bs4 import BeautifulSoup as bsoup
import urlparse
from urllib2 import urlopen
from urllib import urlretrieve
import os
import sys
import zipfile
from django.core.servers.basehttp import FileWrapper
def getdata(request):
out = 'C:\Users\user\Desktop\images'
if request.GET.get('q'):
#url = str(request.GET['q'])
url = "http://google.com"
soup = bsoup(urlopen(url))
parsedURL = list(urlparse.urlparse(url))
for image in soup.findAll("img"):
print "Old Image Path: %(src)s" % image
#Get file name
filename = image["src"].split("/")[-1]
#Get full path name if url has to be parsed
parsedURL[2] = image["src"]
image["src"] = '%s\%s' % (out,filename)
print 'New Path: %s' % image["src"]
# print image
outpath = os.path.join(out, filename)
#retrieve images
if image["src"].lower().startswith("http"):
urlretrieve(image["src"], outpath)
else:
urlretrieve(urlparse.urlunparse(parsedURL), out) #Constructs URL from tuple (parsedURL)
#Create HTML File and writes to it to check output (stored in same directory).
html = soup.prettify("utf-8")
with open("output.html", "wb") as file:
file.write(html)
else:
url = 'You submitted nothing!'
return HttpResponse(url)
My problem had to do with storing the files on the desktop. I stored the files in the DJango workspace folder, changed the paths, and it worked for me.
I am trying to generate a zip file and store in App Engine's Blobstore. Right now, I do not get a valid zip file from the Blobstore. Not sure the problem is with zipping, storing, retrieving or downloading.
I have built the code based on snippets from the following questions.
Is it possible to generate and return a ZIP file with App Engine?
Zipping dynamic files in App Engine (Python)
After storing in Blobstore, I let users download it through a Flask application.
Here is the gist of what I am trying to do.
def zipit():
zipstream = StringIO.StringIO()
zfile = zipfile.ZipFile(file=zipstream, mode='w')
bytes = "lorem ipsum dolor sit amet"
zfile.writestr('loremipsum', bytes, compress_type=zipfile.ZIP_STORED)
zfile.close()
zipstream.seek(0)
return zipstream.getvalue()
zip_file = files.blobstore.create(mime_type='application/zip')
zip_data = zipit()
with files.open(zip_file, 'a') as f:
f.write(zip_data)
files.finalize(zip_file)
blob_key = files.blobstore.get_blob_key(zip_file)
blob_data = blobstore.BlobReader(blob_key).read()
# http://flask.pocoo.org/docs/api/
response = make_response(blob_data)
response.headers['Content-Type'] = 'application/zip'
response.headers['Content-Disposition'] = 'attachment; filename="loremipsum.zip"'
return response
Any help is much appreciated.
Most of your code works for me in a webapp handler in dev_appserver.py. My version below serves the zip file directly out of the Blobstore, vs. trying to read it into app instance RAM and serve it. Maybe this is what you intended? If not, continue looking for the problem in your code that reads and serves the value, because I believe you're creating a valid Zip file in the Blobstore.
#!/usr/bin/env python
import StringIO
import zipfile
from google.appengine.api import files
from google.appengine.ext import blobstore
from google.appengine.ext import webapp
from google.appengine.ext.webapp import blobstore_handlers
from google.appengine.ext.webapp import util
def zipit():
zipstream = StringIO.StringIO()
zfile = zipfile.ZipFile(file=zipstream, mode='w')
bytes = "lorem ipsum dolor sit amet"
zfile.writestr('loremipsum', bytes, compress_type=zipfile.ZIP_STORED)
zfile.close()
zipstream.seek(0)
return zipstream.getvalue()
class MainHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self):
k = self.request.get('key')
if k:
self.send_blob(k)
return
zip_file = files.blobstore.create(mime_type='application/zip')
zip_data = zipit()
with files.open(zip_file, 'a') as f:
f.write(zip_data)
files.finalize(zip_file)
blob_key = files.blobstore.get_blob_key(zip_file)
self.response.out.write('get zip' % blob_key)
application = webapp.WSGIApplication([('/getzip', MainHandler)])
def main():
util.run_wsgi_app(application)
if __name__ == '__main__':
main()