使用ReportLab和RML生成PDF报表

安装reportlab和rml2pdf

1
2
3
4
$ brew install freetype
$ sudo pip install reportlab
$ sudo pip install rlextra -i https://www.reportlab.com/pypi/ # rml2pdf,会因为没有付费而产生页脚
$ sudo pip install z3c.rml # rml2pdf,免费版本

如果是大型、复杂报告,还是是RML生成比较好维护。

reportlab:pdfgen

pdfgen是最底层的api,用于在页面上绘制内容:

1
2
3
4
5
6
7
8
9
from reportlab.pdfgen import canvas
def hello(c):
c.drawString(100,100,"Hello World")
c = canvas.Canvas("hello.pdf")
hello(c)
c.showPage() # 停止在当前页面绘制
c.save()

可以调整pdf大小等:

1
2
3
from reportlab.lib.pagesizes import letter, A4
myCanvas = Canvas('myfile.pdf', pagesize=letter)
width, height = letter #keep for later

更多绘制细节调整:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def hello(c):
from reportlab.lib.units import inch
# move the origin up and to the left
c.translate(inch,inch)
# define a large font
c.setFont("Helvetica", 14)
# choose some colors
c.setStrokeColorRGB(0.2,0.5,0.3)
c.setFillColorRGB(1,0,1)
# draw some lines
c.line(0,0,0,1.7*inch)
c.line(0,0,1*inch,0)
# draw a rectangle
c.rect(0.2*inch,0.2*inch,1*inch,1.5*inch, fill=1)
# make text go straight up
c.rotate(90)
# change color
c.setFillColorRGB(0,0,0.77)
# say hello (note after rotate the y coord needs to be negative!)
c.drawString(0.3*inch, -inch, "Hello World")
`

绘制API

  • 线条
1
2
canvas.line(x1,y1,x2,y2)
canvas.lines(linelist)
  • 形状
1
2
3
4
5
6
7
8
canvas.grid(xlist, ylist)
canvas.bezier(x1, y1, x2, y2, x3, y3, x4, y4)
canvas.arc(x1,y1,x2,y2)
canvas.rect(x, y, width, height, stroke=1, fill=0)
canvas.ellipse(x1,y1, x2,y2, stroke=1, fill=0)
canvas.wedge(x1,y1, x2,y2, startAng, extent, stroke=1, fill=0)
canvas.circle(x_cen, y_cen, r, stroke=1, fill=0)
canvas.roundRect(x, y, width, height, radius, stroke=1, fill=0)
  • 字符串
1
2
3
canvas.drawString(x, y, text):
canvas.drawRightString(x, y, text)
canvas.drawCentredString(x, y, text)
  • text对象
1
2
textobject = canvas.beginText(x, y)
canvas.drawText(textobject)
  • path对象
1
2
3
path = canvas.beginPath()
canvas.drawPath(path, stroke=1, fill=0, fillMode=None)
canvas.clipPath(path, stroke=1, fill=0, fillMode=None)
  • 图片和PIL(Python Imaging Library)
1
2
canvas.drawInlineImage(self, image, x,y, width=None,height=None)
canvas.drawImage(self, image, x,y, width=None,height=None,mask=None)
  • 结束一个页面
1
canvas.showPage()

状态API

  • 颜色
1
2
3
4
5
6
7
8
canvas.setFillColorCMYK(c, m, y, k)
canvas.setStrikeColorCMYK(c, m, y, k)
canvas.setFillColorRGB(r, g, b)
canvas.setStrokeColorRGB(r, g, b)
canvas.setFillColor(acolor)
canvas.setStrokeColor(acolor)
canvas.setFillGray(gray)
canvas.setStrokeGray(gray)
  • 字体
1
canvas.setFont(psfontname, size, leading = None)
  • 线条风格
1
2
3
4
5
canvas.setLineWidth(width)
canvas.setLineCap(mode)
canvas.setLineJoin(mode)
canvas.setMiterLimit(limit)
canvas.setDash(self, array=[], phase=0)
  • 几何属性
1
2
3
4
5
6
canvas.setPageSize(pair)
canvas.transform(a,b,c,d,e,f):
canvas.translate(dx, dy)
canvas.scale(x, y)
canvas.rotate(theta)
canvas.skew(alpha, beta)
  • 状态控制
1
2
canvas.saveState()
canvas.restoreState()
  • 其它canvas方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
canvas.setAuthor()
canvas.addOutlineEntry(title, key, level=0, closed=None)
canvas.setTitle(title)
canvas.setSubject(subj)
canvas.pageHasData()
canvas.showOutline()
canvas.bookmarkPage(name)
canvas.bookmarkHorizontalAbsolute(name, yhorizontal)
canvas.doForm()
canvas.beginForm(name, lowerx=0, lowery=0, upperx=None, uppery=None)
canvas.endForm()
canvas.linkAbsolute(contents, destinationname, Rect=None, addtopage=1, name=None, **kw)
canvas.linkRect(contents, destinationname, Rect=None, addtopage=1, relative=1, name=None, **kw)
canvas.getPageNumber()
canvas.addLiteral()
canvas.getAvailableFonts()
canvas.stringWidth(self, text, fontName, fontSize, encoding=None)
canvas.setPageCompression(onoff=1)
canvas.setPageTransition(self, effectname=None, duration=1,
direction=0,dimension='H',motion='I')

坐标

默认origin坐标(0,0)是在左下角.

  • 改变起始坐标:
1
canvas.translate(x,y)
  • 缩放:
1
canvas.scale(0.75, 0.5)
  • 同时改变起始坐标与缩放:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def scaletranslate(canvas):
from reportlab.lib.units import inch
canvas.setFont("Courier-BoldOblique", 12)
# save the state
canvas.saveState()
# scale then translate
canvas.scale(0.3, 0.5)
canvas.translate(2.4*inch, 1.5*inch)
canvas.drawString(0, 2.7*inch, "Scale then translate")
coords(canvas)
# forget the scale and translate...
canvas.restoreState()
# translate then scale
canvas.translate(2.4*inch, 1.5*inch)
canvas.scale(0.3, 0.5)
canvas.drawString(0, 2.7*inch, "Translate then scale")
coords(canvas)
  • 镜像图片
1
2
3
4
5
def mirror(canvas):
from reportlab.lib.units import inch
canvas.translate(5.5*inch, 0)
canvas.scale(-1.0, 1.0) # 缩放实现
coords(canvas)

text对象

1
2
3
4
5
6
7
8
9
textobject.setTextOrigin(x,y)
textobject.setTextTransform(a,b,c,d,e,f)
textobject.moveCursor(dx, dy) # from start of current LINE
(x,y) = textobject.getCursor()
x = textobject.getX(); y = textobject.getY()
textobject.setFont(psfontname, size, leading = None)
textobject.textOut(text)
textobject.textLine(text='')
textobject.textLines(stuff, trim=1)

字体

1
2
3
4
5
6
7
8
9
10
11
12
# we know some glyphs are missing, suppress warnings
import reportlab.rl_config
reportlab.rl_config.warnOnMissingFontGlyphs = 0
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont('Vera', 'Vera.ttf'))
pdfmetrics.registerFont(TTFont('VeraBd', 'VeraBd.ttf'))
pdfmetrics.registerFont(TTFont('VeraIt', 'VeraIt.ttf'))
pdfmetrics.registerFont(TTFont('VeraBI', 'VeraBI.ttf'))
canvas.setFont('Vera', 32)
canvas.drawString(10, 150, "Some text encoded in UTF-8")
canvas.drawString(10, 100, "In the Vera TT Font!")

RML(Report Markup Language)和rml2pdf

文件结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE document SYSTEM "rml.dtd">
<document filename="planet.pdf">
<docinit>
<!-- 内容 -->
</docinit>
<template>
<!-- 内容 -->
</template>
<story>
<!-- 内容 -->
</story>
</document>

定义字体等初始化

1
2
3
<docinit>
<registerTTFont faceName="ClassizismAntiqua" fileName="files/ClassizismAntiquaBook.ttf"/>
</docinit>

定义模板、页面属性等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template showBoundary="0">
<pageTemplate id="frontpage">
<pageGraphics>
<!-- 页面属性内容-->
</pageGraphics>
<frame id="first" x1="50" y1="340" width="250" height="275"/>
<frame id="second" x1="50" y1="40" width="125" height="300"/>
<frame id="third" x1="180" y1="40" width="125" height="300"/>
</pageTemplate>
<pageTemplate id="page2">
<pageGraphics>
<!-- 页面属性内容-->
</pageGraphics>
</pageTemplate>
</template>

定义风格

1
2
3
4
5
6
<stylesheet>
<paraStyle name="headline" alignment="LEFT" fontSize="40"/>
<paraStyle name="strapline" alignment="LEFT"/>
<paraStyle name="normal" alignment="LEFT" spaceBefore="5"/>
<paraStyle name="caption" alignment="LEFT" fontSize="8"/>
</stylesheet>

故事

1
2
3
4
5
6
<story firstPageTemplate="frontpage">>
<setNextTemplate name="page2"/>
<nextFrame/>
<para> <!--内容--> </para>
<para style="headline"> <!-- 内容--> </para>
</story>

资料

吴羽舒 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!