How to create PDF files in a Python/Django application using ReportLab
https://assist-software.net/blog/how-create-pdf-files-python-django-application-using-reportlab
Web Developer at ASSIST
„Nothing is too small to know and nothing too big to attempt.” - William Cornelius Van Horne
Introduction
Nowadays many web applications have the possibility to save and display data in a portable format. In Python/Django applications we have the possibility to display data in a web page, but also save it in such a format. Official Django documentation contains a basic tutorial about saving data in PDF (Portable Document Format).
First of all, in order to use this library you need to install it. You can install it using pip or easy_install by typing: pip install reportlab or easy_install reportab in your virtual environment. It's fully recommended to use a virtual environment for each of your projects especially if you work on many projects and each of them use different versions of libraries. For example in this application, which is also available on GitHub, we use these libraries:
- Django==1.8.2
- Pillow==2.8.2
- reportlab==3.2.0
In a previous article, the main subject was How to export excel files in a Python/Django application, whereas this article will show you how to export PDF files. This application will extend the one in the previous article; as a recap, the application saves weather data for multiple towns. A user can add different data for weather using the administration interface provided by Django. The home page of application displays today's weather for different towns. The main menu also contains a link to the list of all towns and a link to weather history, which is a page where the user can filter weather history by town and can generate both Excel or PDF files reports.
How to serve the file for download
In order to download the data written in a file format, we can use two buttons on the HTML page, one for Excel and one for PDF. Each of the buttons have an associated name:
1
|
<button class = "btn btn-default pull-right" name= "pdf" type= "submit" ></button> |
Forwarding in the corresponding view we have to check if the button name exists in the request. If it does then we create a HttpResponse object with the corresponding content. Afterwards we have to set the content_type to be “application/pdf”, the Content-Disposition to be an attachment, add a name for filename and the corresponding file type, in our case PDF. This will tell the browser to treat this HttpResponse object as an attachment of pdf type.
1
2
3
4
5
6
7
8
9
10
|
if 'pdf' in request.POST: response = HttpResponse(content_type= 'application/pdf' ) today = date.today() filename = 'pdf_demo' + today.strftime( '%Y-%m-%d' ) response[ 'Content-Disposition' ] = 'attachement; filename={0}.pdf' .format(filename) buffer = BytesIO() report = PdfPrint(buffer, 'A4' ) pdf = report.report(weather_period, 'Weather statistics data' ) response.write(pdf) return response |
We use a buffer which contains the PDF data. For this we can use BytesIO class that represents a stream implementation using an in-memory bytes buffer. For the filename we created a filename variable by joining a string and current date.
How to create a PDF document
In order to be as modular as possible we’ve created a class named PdfPrint that contains several methods for creating different elements. This class has an __init__ method with two parameters: buffer and pagesize. Buffer is used to hold data and pagesize is used to set page type and it's width and height. ReportLab has a series of build-in types: A0 to A6, B0 to B6 and letter type, A4 being the default format if we don't give one. Also you have the possibility to set your own page size by providing custom height and width.
1
2
3
4
5
6
7
8
|
def __init__(self, buffer, pageSize): self.buffer = buffer # default format is A4 if pageSize == 'A4' : self.pageSize = A4 elif pageSize == 'Letter' : self.pageSize = letter self.width, self.height = self.pageSize |
The lowest level interface used to generate PDF files is Canvas. Canvas can be seen as a white paper where you can “draw” elements like text, images or other graphics elements.
In order to create more complex documents we need to use Platypus (Page Layout and Typography Using Scripts), a high level layout library of ReportLab. Platypus has several layers: DocTemplates, PageTemplates, Frames, Flowables and Canvas. DocTemplates and PageTemplates are used to set document specifications layout. Flowables it is used to wrap, draw or split text or graphics elements in the document. Main flowables used to draw text elements are Paragraph, Spacer and Table. In the next parts of this article we will try to explain how to add such elements in a PDF file.
For the PDF template pages we used SimpleDocTemplate class, a special case document template that can handle multiple simple documents. The code presented bellow is used to set document characteristics (set the margins and the page size). After setting document characteristics, we've created a list used to add flowable elements. At this point we only add title to this list.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
def report(self, weather_history, title): # set some characteristics for pdf document doc = SimpleDocTemplate( self.buffer, rightMargin= 72 , leftMargin= 72 , topMargin= 30 , bottomMargin= 72 , pagesize=self.pageSize) styles = getSampleStyleSheet() # create document data = [] data.append(Paragraph(title, styles[ 'Title' ])) # create other flowables doc.build(data) pdf = self.buffer.getvalue() self.buffer.close() return pdf |
How to add paragraphs and spaces in between
This section shows you how to add some paragraphs to the document. In order to do this we first need to use a certain style for the paragraph. ReportLab library comes with a set of predefined styles such as: Title, Heading1 to Heading6, Italic, Normal, etc. In order to use these styles we need to use this function getSampleStyleSheet:
1
|
styles = getSampleStyleSheet() |
But we can also add our custom text style. To do this we can use the ParagraphStyle class.
1
|
styles.add(ParagraphStyle( name= "ParagraphTitle" , fontSize= 11 , alignment=TA_JUSTIFY, fontName= "FreeSansBold" )) |
This class construction method has several parameters, most used parameters are:
- name - the name of the new style
- fontSize – the dimension of the new style
- alignment – the style alignment, it can be: TA_JUSTIFY, TA_CENTER, TA_LEFT or TA_RIGHT
- fontName – you can use 14 predefined fonts offered by ReportLab or you can define your own one
To use other fonts than the predefined ones you need to add the specific TTF font file in the static folder. Afterwards you need to register that font like this:
1
|
pdfmetrics.registerFont(TTFont( 'FreeSans' , settings.STATIC_ROOT+ 'FreeSans.ttf' )) |
This feature offered by ReportLab library is very important, especially if you need to write in a language that uses special characters, others than the ones in English.
After setting your custom paragraph styles it is time to use it. In order to do this, simply create an empty list and add to it any element that you want on your document. To add a paragraph in your document use Paragraph class by instantiating an object with the text you want to appear and the paragraph style.
1
|
Paragraph( 'Paragraph example' , styles[ 'Title' ]) |
But, between paragraphs or other elements there should be empty spaces. To do this ReportLab library came with a specific class called Spacer. So in order to create custom spaces between document elements we can use this method by adding it as an element on the page Spacer(width, height).
Usually a PDF document can contain more than paragraphs, for example, we may want to add a table. This library came with specific classes for adding tables, you can find out more about that in the next section.
How to add a table
Another important flowable element of ReportLab library is represented by tables. The class used to draw a table into a document is named Table. The __init__ method for this class has one mandatory parameter: a list with table data including the table header. It can also have optional parameters used to customize rows and columns: colsWidths or rowHeights. Official ReportLab library documentation tells us that “a cell table can contain anything that can be converted to string or flowable”.
In order to set some custom specifications for our table we can use TableStyle class. To customize our table we call setStyle function with an TableStyle object as parameter. This class came with a list of tuple commands, each of the tuples being represented like this:
- first element is the attached name;
- second and third are coordinates for starting and stopping points for the cells that will be affected;
- other options like color or thickness.
In our example INNERGRID and BOX are used to set the black color and thickness of 0.25 for the cell lines, VALIGN and BACKGROUND are used to set the text alignment and background color for the table header only.
1
2
3
4
5
|
wh_table.setStyle(TableStyle( [( 'INNERGRID' , ( 0 , 0 ), (- 1 , - 1 ), 0.25 , colors.black), ( 'BOX' , ( 0 , 0 ), (- 1 , - 1 ), 0.5 , colors.black), ( 'VALIGN' , ( 0 , 0 ), (- 1 , 0 ), 'MIDDLE' ), ( 'BACKGROUND' , ( 0 , 0 ), (- 1 , 0 ), colors.gray)])) |
After setting the style for our table we need to add it into the document like any ordinary flowable element by calling append function with table name as parameter.
In other projects we may need to show data in a more friendly and easy way to visualize it, other than displaying it in a table. For this we need to create charts based on that data.
How to add charts in PDF documents
ReportLab library offers support to add some other graphics elements like charts. The library can be used to generate different types of charts like line, bar or pie charts. In order to create a chart first we need to create a drawing area by instantiating a Drawing object. Next we need to draw the chart into this area: d = Drawing(0, 170).
For all our charts we've created other two methods to set chart title and chart legend. The method used to “draw” the chart title creates a Label object and sets a specific font, size and position for it.
1
2
3
4
|
chart_title = Label() chart_title.fontName = 'FreeSansBold' chart_title.fontSize = 16 chart_title.textAnchor = 'middle' |
The second method used, called legend_draw has two mandatory parameters: legend labels, a chart object and also **kwargs in order to provide an arbitrary number of custom arguments for each type of chart.
Firstly, we need to create a Legend object and afterwards set some characteristics for it, beginning with the start position point and labels alignment.
1
2
3
4
|
legend = Legend() legend.x = kwargs[ 'x' ] legend.y = kwargs[ 'y' ] legend.alignment = 'right' |
Other characteristics that can be set for the legend:
- boxAnchor – sets anchor point for the legend area
1
|
legend.boxAnchor = kwargs[ 'boxAnchor' ] |
- columnMaximum – sets the maximum number of items included in one column
1
|
legend.columnMaximum = kwargs[ 'columnMaximum' ] |
- deltax – sets the distance between neighbouring items
1
|
legend.deltax = 0 |
- colorNamePairs – creates a list of color/item name tuples using zip function
1
|
legend.colorNamePairs = zip(lcolors, labels) |
Next using enumerate python feature we set the interior colors of the plot area according to the specific chart:
1
2
3
4
5
6
7
|
for i, color in enumerate(lcolors): if chart_type == 'line' : chart.lines[i].fillColor = color elif chart_type == 'pie' : chart.slices[i].fillColor = color elif chart_type == 'bar' : chart.bars[i].fillColor = color |
Because we need to serve data into a certain format I have created some other functions in a file called utils.py. I've also created a function that generates random colors for the bar and pie charts.
a. How to add line charts
In our example we want to add a line chart that will display the data from a period of time with the minimum and maximum registered temperatures. The library came with some classes used to create line charts like AbstractLineChart, LineChart, HorizontalLineChart, HorizontalLineChart3D or SampleHorizontalLineChart. We use the SampleHorizontalLineChart which offers many simple methods to create a line chart.
1
|
chart = SampleHorizontalLineChart() |
After creating the chart object, it is time to set some custom properties: width, height and some other characteristics for our chart:
- data – the displayed data, it's format being a list of tuples
1
|
chart.data = values |
- lineLabels.fontName – sets the desired font for data lines
1
|
chart.lineLabels.fontName = 'FreeSans' |
- strokeColor and fillColor - sets colors for the plot area border and interior area
1
2
|
chart.strokeColor = colors.white chart.fillColor = colors.lightblue |
These parameters can also set attributes for lines; color and line width:
1
2
|
chart.lines[ 0 ].strokeColor = colors.red chart.lines[ 0 ].strokeWidth = 2 |
- lines.symbol - sets markers for points, you can chose from a list of markers types: Square, Circle, Diamond, StarFive, Triangle, Pentagon, StarSix, Heptagon, Octagon, Cross or even other symbols like UK_Flag
1
|
chart.lines.symbol = makeMarker( 'Square' ) |
- lineLabelFormat – sets the format used for the points displayed in the plot
1
|
chart.lineLabelFormat = '%2.0f' |
- joinAxisMode – this attribute is used to set the intersecting point for the axes
1
|
chart.categoryAxis.joinAxisMode = 'bottom' |
- labels – this attributes can be used to set some characteristic for axes labels like:
fontName – sets font
angle – sets text inclination
boxAnchor – sets label position
1
2
3
|
chart.categoryAxis.labels.fontName = 'FreeSans' chart.categoryAxis.labels.angle = 45 chart.categoryAxis.labels.boxAnchor = 'e' |
- categoryNames – sets labels for X axis
1
|
chart.categoryAxis.categoryNames = days |
- labelTextFormat – sets the format displayed for Y axis
1
|
chart.valueAxis.labelTextFormat = '%2.0f °C' |
- valueStep – sets the steps for Y axis
1
|
chart.valueAxis.valueStep = 10 |
And here is the result of setting all those characteristics:
You should know that this data is not real and I chose to add some negative values for temperatures only to show how you can set some characteristics for line charts.
b. How to add bar charts
Another chart type used to display data in a schematic mode is bar chart. ReportLab library comes with several types of bar charts: BarChart3D, HorizontalBarChart, HorizontalBarChart3D, VerticalBarChart or VerticalBarChart3D. In our example we used VerticalBarChart to display the wind speed statistic for all towns for a frame of time.
Firstly we create an VerticalBarChart object bc = VerticalBarChart() and subsequently we can set characteristics for this object. The characteristics that we can set are:
- width
- height
- data - list or lists of numbers
- barSpacing – sets the bar width between bars
1
|
bc.barSpacing = 0.5 |
- labels – sets some characteristics for the labels that appear in the X axis. In our example we set the position
1
2
|
bc.categoryAxis.labels.dx = 8 bc.categoryAxis.labels.dy = - 2 |
- categoryNames – sets the names displayed on the X axis
1
|
bc.categoryAxis.categoryNames = days |
- barLabelFormat – sets the label format for the bars
1
|
bc.barLabelFormat = '%d' |
- barLabels – sets some characteristics for the bar charts; we used nudge that sets distance between top of a bar and it's label
1
|
bc.barLabels.nudge = 7 |
- valueAxis – sets the characteristics for the Y axis, for example
- labelTextFormat – to set the labels format
- valueMin - to set the minimum value for the Y axis
1
2
|
bc.valueAxis.labelTextFormat = '%d km/h' bc.valueAxis.valueMin = 0 |
After setting all these characteristics we can see the result:
c. How to add pie charts
There are some projects where you need to display data that is linked. Each piece of data represents a proportional or percentage part of the total. Therefore, the best approach for this is representing it by pie charts. ReportLab library came with several classes that can be used to create pie charts: Pie, LegendedPie, Doughnut and Pie3d
In our example we want to create a pie chart that will show a statistic regarding the percentage of the precipitation probability. We divided the precipitation probability in five categories: between 0-20, 21-40, 41-60, 61-80 and 81-100 percentage. We used the Pie class to create our chart, for this we set the x and y coordinates and some other characteristics:
1
2
3
|
pc = Pie() pc.x = 0 pc.y = 50 |
- data – sets the data for the chart (list of numbers)
1
|
pc.data = values |
- sideLabels – adds a line that link the label and it's specific slice
1
|
pc.sideLabels = 1 |
- slices – sets some characteristics for the component slices, for example:
- strokeWidth – sets the width of line between slices
strokeColor – sets the line color
1
2
|
pc.slices.strokeWidth = 0 pc.slices.strokeColor = None |
After setting these characteristics to our pie, it is time to see the result:
How to add page numbers to a PDF document
In some cases when your documents have more than one page you can add numbering. In order to apply numbering on the document pages we've created a method that will draw the numbers on the each page of document. To get the number of current page we used getPageNumber method that returns the page number for each page being generated. After we get the number we draw it in the page at a certain position by using the drawCenteredString method that places the given number on the canvas.
1
2
3
|
def pageNumber(self, canvas, doc): number = canvas.getPageNumber() canvas.drawCentredString( 100 *mm, 15 *mm, str(number)) |
We've used this function when we've called the build function for the SimpleDocTemplate and set the onFirstPage and onLaterPages parameters.
In conclusion
In the end, we can say that ReportLab library represents a very powerful tool. Moreover, I think it is an easier enough tool that can help a developer to create portable documents using Python language and integrate it into a Django project. This article tries to show you how to setup and use some of the features offered by ReportLab library to create PDF files.
You can access the entire source code of this application by accessing GitHub . I hope this article is useful and helps you when need to create and export data into PDF documents in a Python/Django application. If you want to do more than this and create more complex documents or use other options you can access the official ReportLab library documentation.
However, if you need to export your data in other formats, like xls for example, you can check this article: How to export excel files in a python/django application.
How to create PDF files in a Python/Django application using ReportLab的更多相关文章
- How to export Excel files in a Python/Django application
https://assist-software.net/blog/how-export-excel-files-python-django-application CONTENTS Introduct ...
- “Stamping” PDF Files Downloaded from SharePoint 2010
http://blog.falchionconsulting.com/index.php/2012/03/stamping-pdf-files-downloaded-from-sharepoint-2 ...
- Working with PDF files in C# using PdfBox and IKVM
I have found two primary libraries for programmatically manipulating PDF files; PdfBox and iText. T ...
- create pdf file using Spire.Pdf or iTextSharp or PdfSharp
Spire.Pdf: 注:pdf 显示中文一定要设置相应的中文字体,其他外文类似.否则显示为乱码( 如果繁体的服务器上生成的中文内容PDF文档,在简体操作系统保存或并传给简体系统上查看,会存在乱码问题 ...
- Write Custom Java to Create LZO Files
https://cwiki.apache.org/confluence/display/Hive/LanguageManual+LZO LanguageManual LZO Skip to e ...
- python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
python Django教程 之 模型(数据库).自定义Field.数据表更改.QuerySet API 一.Django 模型(数据库) Django 模型是与数据库相关的,与数据库相关的代码 ...
- python Django之Ajax
python Django之Ajax AJAX,Asynchronous JavaScript and XML (异步的JavaScript和XML),一种创建交互式网页应用的网页开发技术方案. 异步 ...
- python Django注册页面显示头像
python Django注册页面显示头像(views) def register(request): ''' 注册 :param request: :return: ''' if request.m ...
- [python] python django web 开发 —— 15分钟送到会用(只能送你到这了)
1.安装python环境 1.1 安装python包管理器: wget https://bootstrap.pypa.io/get-pip.py sudo python get-pip.py 1. ...
随机推荐
- myeclipse8.6首次运行maven项目的问题解决
myeclipse8.6导入maven项目后识别为普通java项目,即项目图标上没有小M的标识.这时是无法直接运行的. 解决方法:1,打开Window --> perferences,找到mye ...
- HTML:基本的标签
概述: <html></html>标准的语言格式,回环标签,有头和躯体部分,头里面一般显示标题title,躯体部分显示内容:背景色.文字.图片.超链接.表格.表单等. 可以直接 ...
- Orchard运用 - 理解App_Data目录结构
了解一个系统,应该基本上要了解目录结构及其组织形式.这样对于开发人员更是必备的知识,比如开发模块最终安装到哪,主题Themes是如何配置启用. 今天跟大家分享其实是个笔记记录,就是看到有一篇文章介绍A ...
- go语言基础之包和自定义包与main包
1.包 所有 Go 语言的程序都会组织成若干组文件,每组文件被称为一个包.这样每个包的代码都可以作为很小的复用单元,被其他项目引用. 一个包的源代码保存在一个或多个以.go为文件后缀名的源文件中,通常 ...
- STL之set集合容器 【转】
set集合容器实现了红黑树(Red-Black Tree)的平衡二叉检索树的的数据结构,在插入元素时,它会自动调整二叉树的排列,把该元素放到适当的位置,以确保每个子树根节点的键值大于左子树所有节点的键 ...
- Webview 支持文件上传
默认情况下情况下,在一个带有input tpye=file标签的Html页面,使用Android的WebView是不能够支持上传文件的(在iOS和微信上完全正常工作).而这个,也是在我们的前端工程师告 ...
- Linux CentOS7 系统目录详解
1.目录结构 2.文件类型 LINUX有四种基本文件系统类型:普通文件.目录文件.连接文件和特殊文件,可用file命令来识别. 普通文件:如文本文件.C语言元代码.SHELL脚本.二进制的可执行文件 ...
- STM32 控制GSM模块收发信息 F407 discovery
main.c #include "stm32f4_discovery.h" #include <stdio.h> #define LED1_ON GPIO_SetBit ...
- 文件及文件夹更改通知/监测软件TheFolderSpy
TheFolderSpy是Windows环境下一个监测文件(夹)更改,删除,创建,重命名的绿色免安装小软件,并在文件及文件夹有更改时发送Email通知管理者 该软件使用.Net开发,所以需要安装.Ne ...
- html中块注释<!--[if IE]>….<![endif]--> (<!--[if !IE]>||<![endif]
1. <!--[if !IE]><!--> 除IE外都可识别 <!--<![endif]-->2. <!--[if IE]> 所有的IE可识别 & ...