How to create 2 pages from reportlab using one PageTemplate? - django

Hi all reportlab master,
I've search the web and also here in stackoverflow but can't find a similar situation on my problem I'm trying to solve during this Holiday vacation.
In django admin, I'm trying to create an action to view my database to a specific format. If I select one record I can view the report in one page pdf. Which is fine. In case the user try to more record that's where the problem start. For example I select multiple record, I can view the report but all content is still in one page pdf.
Is there a way to show a record per page in pdf? All reportlab master jedi, Please help me how to do this the right way.
Here's my code on what I did.
from django.contrib import admin
from models import LatestRsl
from io import BytesIO
from reportlab.pdfgen import canvas
from django.http import HttpResponse
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from reportlab.lib.units import inch
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.platypus import BaseDocTemplate, PageTemplate, Paragraph, Frame
from reportlab.lib.pagesizes import letter
def go(modeladmin, request, queryset):
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'filename = testframe.pdf'
buffer = StringIO()
c = canvas.Canvas(buffer)
doc = BaseDocTemplate(buffer, showBoundary=1, leftMargin= 0.1*inch, rightMargin= 0.1*inch,
topMargin= 0.1*inch, bottomMargin= 0.1*inch)
signfr = Frame(5.1*inch, 1.2*inch, 2.8*inch, 0.44*inch, showBoundary=1)
modelfr = Frame(3.6*inch, 4.6*inch, 2.8*inch, 0.44*inch, showBoundary=1)
doc.addPageTemplates([PageTemplate(id= 'rsl_frame', frames=[signfr, modelfr]),
PageTemplate(id= 'rsl_frame2', frames=[signfr, modelfr])])
story = []
styles=getSampleStyleSheet()
styles.add(ParagraphStyle(name='Verdana9', fontName= 'Verdana', fontSize= 9))
styles.add(ParagraphStyle(name='VerdanaB10', fontName= 'VerdanaB', fontSize= 10))
for obj in queryset:
#1st frame
model = Paragraph(obj.make,styles["Verdana9"])
story.append(model)
modelfr.addFromList(story,c)
#2nd frame
signatory = Paragraph(obj.signatory,styles["VerdanaB10"])
story.append(signatory)
signfr.addFromList(story,c)
doc.build(story)
c.showPage()
c.save()
pdf = buffer.getvalue()
buffer.close()
response.write(pdf)
return response

Assuming your queryset variable contains all the records you need, you could insert a PageBreak object. Just add from reportlab.platypus import PageBreak to the top of your file, then append a PageBreak object to your document's elements.
If you want to change the template for each page, you can also append a NextPageTemplate and pass the id of your PageTemplate. You'll need to add from reportlab.platypus import NextPageTemplate to the top of your file as well.
for obj in queryset:
#1st frame
model = Paragraph(obj.make,styles["Verdana9"])
story.append(model)
modelfr.addFromList(story,c)
#2nd frame
signatory = Paragraph(obj.signatory,styles["VerdanaB10"])
story.append(signatory)
signfr.addFromList(story,c)
# Force the report to use a different PageTemplate on the next page
story.append(NextPageTemplate('rsl_frame2'))
# Start a new page for the next object in the query
story.append(PageBreak())
You could move the PageBreak wherever you need it, but it's a simple "function" flowable. NextPageTemplate can take the id of any valid PageTemplate object that you've added via addPageTemplates.

Related

Django Reportlab getKeepWithNext

I am trying to add an image to a PDF generated in Reportlab. I am trying to access the image from a Django field, specifying the full path of the file.
When I run the below code I get: "Exception Value: getKeepWithNext".
Any help as to what I am doing wrong would be greatly appreciated.
def holding_pdf(self, course_slug, holding_slug):
buffer = io.BytesIO()
holding = HoldingDetail.objects.get(identifier=holding_slug)
doc = SimpleDocTemplate(buffer,
rightMargin=72,
leftMargin=72,
topMargin=72,
bottomMargin=72,
pagesize=A4,
title=f"Why the {holding.name} is in the portfolio.pdf")
elements = []
styles = getSampleStyleSheet()
elements.append(Paragraph(str(holding.logo.path), styles['Normal']))
elements.append(Image(holding.logo.path))
print(holding.logo.path)
doc.build(elements)
buffer.seek(0)
return FileResponse(buffer, as_attachment=False, filename=f"Why the {holding.name} is in the portfolio.pdf")
I think that you're using an incorrect Image class. Verify the python imports in your file, and sure you use the Image flowable class provides by reportlab.
from reportlab.lib.pagesizes import A4
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph, SimpleDocTemplate, Image

Django Cannot find cursor object in connection object

I am developing a web application using django. I am new Django.
I have call a stored procedure from my application. I gone through the django documentation and i found out that using cursor object i can call the procedure. But i cannot find the cursor object in connection object.
This is how my code looks like :
from django.db import connection
cursor = connection.cursor()
But i cannot find cursor object itself in the connection.
Please help me out where i am going wrong.
I can't see anything wrong with the code you post, I'll assume you don't know how to proceed after you have the cursor, so this is an example:
from django.db import models
from django.db import connection
class Document(models.Model):
# fields
url = models.CharField(max_length=900)
content = models.TextField()
title = models.TextField()
# static method to perform a fulltext search
#staticmethod
def search(search_string):
# create a cursor
cur = connection.cursor()
# execute the stored procedure passing in
# search_string as a parameter
cur.callproc('searcher_document_search', [search_string,])
# grab the results
results = cur.fetchall()
cur.close()
# wrap the results up into Document domain objects
return [Document(*row) for row in results]

SVG rendering differs between Safari/Chrome/Firefox

I am dynamically generating a figure as part of a website. The figure is generated by matplotlib as part of a Django view function. Within the view function, I render the figure as an SVG and then add that SVG's content to the view's context and then render a very simple template using that context. It's a graph of temperature over time from a particular sensor.
When I view the page in a browser, everything works right, in general terms: the graph is shown in the right place and generally shows the right info. But the image looks different when viewed in Safari vs either Chrome or Firefox.
Here's how it looks in Chrome/Firefox:
And here's how it looks in Safari:
A link to the SVG itself is here.
Here's the code that is generating the SVG:
from django.views.generic import TemplateView
from mysite.models import TempReading, TempSeries
import numpy as np
import pandas as pd
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import seaborn as sbn
import StringIO
class TestView(TemplateView):
template_name = 'mysite/test.html'
def get_context_data(self, **kwargs):
upstairs = TempSeries.objects.get(name='Upstairs')
upstairstemps = upstairs.tempreading_set.all().order_by('-timestamp')
frame = pd.DataFrame(list(upstairstemps.values()))
frame.set_index('timestamp', inplace=True)
fig = Figure()
ax = fig.add_subplot(1,1,1)
frame['value'].plot(ax=ax)
ax.get_xaxis().grid(color='w', linewidth=1)
ax.get_yaxis().grid(color='w', linewidth=1)
fig.set(facecolor='w')
canvas = FigureCanvas(fig)
imgdata = StringIO.StringIO()
canvas.print_svg(imgdata)
imgstr = imgdata.getvalue()
context = super(TestView, self).get_context_data(**kwargs)
context['svgtext'] = imgstr
return context
Any thoughts on why the SVG would render differently in different browsers?

Django admin upload and image to s3 and then resize the image and save a thumb problem

I am having error after error trying to upload and resize images to s3 with pil and botos3 and the django default_storage. I am trying to do this on save in the admin.
here is the code:
from django.db import models
from django.forms import CheckboxSelectMultiple
import tempfile
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage as s3_storage
from django.core.cache import cache
from datetime import datetime
import Image, os
import PIL.Image as PIL
import re, os, sys, urlparse
class screenshot(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
image = models.ImageField(upload_to='screenshots')
thumbnail = models.ImageField(upload_to='screenshots-thumbs', blank=True, null=True, editable=False)
def save(self):
super(screenshot, self).save() # Call the "real" save() method
if self.image:
thumb = Image.open(self.image.path)
thumb.thumbnail(100, 100)
filename = str(self.slug)
temp_image = open(os.path.join('tmp',filename), 'w')
thumb.save(temp_image, 'JPEG')
from django.core.files import File
thumb_data = open(os.path.join('/tmp',filename), 'r')
thumb_file = File(thumb_data)
new_file.thumb.save(str(self.slug) + '.jpg', thumb_file)
def __str__(self):
return self.title
This is just one of the many ways I have tried to get it working, and I either get (2, 'No such file or directory') or some other error.
Please can someone help me to get it working. I want it to use the django backend to get the image uploaded to be resized and saved as the thumbnail and then saved. Let me know if you need to know any information. I would be happy to use the django snippet - http://djangosnippets.org/snippets/224/ but I don't know what data to feed it. I get the same IOErrors and 'no such path/filename' even though the main image is uploading to s3 fine. I have also tried things like:
myimage = open(settings.MEDIA_URL + str(self.image))
myimage_io = StringIO.StringIO()
imageresize = myimage.resize((100,100), Image.ANTIALIAS)
imageresize.save('resize_100_100_aa.jpg', 'JPEG', quality=75)
It's been 3 days of looking now so I am starting to go spare! Thanks
I had a similar problem, but in my case using sorl-thumbnail was not an option. I found that I can open an Image directly from S3BotoStorage by passing in a file descriptor instead of a path.
So instead of
thumb = Image.open(self.image.path)
use
thumb = Image.open(s3_storage.open(self.image.name))
Then you can process and save the new file locally as you were doing before.
Why don't you try sorl-thumbnail. It has the exact same interface as the default ImageField django provides and it seems like it would be a lot nicer to work with than the roll-your-own support.
Storage support
Pluggable Engine support (PIL, pgmagick)
Pluggable Key Value Store support (redis, cached db)
Pluggable Backend support
Admin integration with possibility to delete
Dummy generation
Flexible, simple syntax, generates no html
ImageField for model that deletes thumbnails
CSS style cropping options
Margin calculation for vertical positioning

How to open a generated PDF file in browser?

I have written a Pdf merger which merges an original file with a watermark.
What I want to do now is to open 'document-output.pdf' file in the browser by a Django view. I already checked Django's related articles, but since my approach is relatively different, I don't directly create the PDF object, using the response object as its "file.", so I am kind of lost.
So, how can I do is in a Django view?
from pyPdf import PdfFileWriter, PdfFileReader
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
output = PdfFileWriter()
input = PdfFileReader(file('file.pdf', 'rb'))
# get number of pages
num_pages = input.getNumPages()
# register new chinese font
pdfmetrics.registerFont(TTFont('chinese_font','/usr/share/fonts/truetype/mac/LiHeiPro.ttf'))
# generate watermark on the fly
pdf = Canvas("watermark.pdf")
pdf.setFont("chinese_font", 12)
pdf.setStrokeColorRGB(0.5, 1, 0)
pdf.drawString(10, 830, "你好")
pdf.save()
# put on watermark
watermark = PdfFileReader(file('watermark.pdf', 'rb'))
page1 = input.getPage(0)
page1.mergePage(watermark.getPage(0))
# add processed pdf page
output.addPage(page1)
# then, add rest of pages
for num in range(1, num_pages):
output.addPage(input.getPage(num))
outputStream = file("document-output.pdf", "wb")
output.write(outputStream)
outputStream.close()
I know its an older post but we can use the embed tag of html to implement this kind of functionality. For e.g.:
<embed height="100%" width="100%" name="plugin" src="filename.pdf" type="application/pdf">
So in your case, you can simply send the response using render to response as:
return render_to_response("abc.html",{"filename":filename})
and in the abc.html you can put this filename (with the path) in the embed tag, as mentioned above.
Hope this helps.
In addition to sending your PDF back to the browser, you can also save some cycles by storing your watermark in a string buffer.
from pyPdf import PdfFileWriter, PdfFileReader
from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from django.http import HttpResponse
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
def some_view(request):
output = PdfFileWriter()
input = PdfFileReader(file('file.pdf', 'rb'))
#create response object
response = HttpResponse(mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'
# get number of pages
num_pages = input.getNumPages()
#register the font
pdfmetrics.registerFont(TTFont('chinese_font','/usr/share/fonts/truetype/mac/LiHeiPro.ttf'))
# generate watermark on the fly
buffer = StringIO() # create string buffer for PDF
pdf = Canvas(buffer)
pdf.setFont("chinese_font", 12)
pdf.setStrokeColorRGB(0.5, 1, 0)
pdf.drawString(96, 26, "88888")
pdf.save()
# put on watermark from buffer
watermark = PdfFileReader(buffer)
page1 = input.getPage(0)
page1.mergePage(watermark.getPage(0))
# add processed pdf page
output.addPage(page1)
#stream to browser
outputStream = response
output.write(response)
outputStream.close()
return response
I am not sure I follow. If you want the PDF content to be sent to the browser you should use an HttpResponse instance. This line in your code
outputStream = file("document-output.pdf", "wb")
will not serve to write the PDF contents to the response. Instead it looks to me like it will write the contents to a local file, which is not the same.
Update
Based on comment:
How to send PDF content to a HttpResponse object as it will open in the browser, not as an attachment.
AFAIK (if anyone knows better, correct me) this is browser dependent.
If you leave out the Content-Disposition = "attachment; filename=foo.pdf from the response headers you can send the contents to the browser without a specific filename. This prompted my Firefox browser (3.6.10, Ubuntu Jaunty) to ask me if I wanted to open it using a program. On Chrome (6.0.472.62, Ubuntu Jaunty) the file got downloaded as download.pdf without any prompting.
remove 'attachment' from this line with Chris comment
response['Content-Disposition'] = 'attachment; filename=somefilename.pdf'