from
reportlab.pdfbase.ttfonts
import
TTFont
from
reportlab.lib.styles
import
getSampleStyleSheet
from
reportlab.platypus
import
Paragraph, SimpleDocTemplate, Image, Table
from
reportlab.platypus
import
Spacer
from
reportlab.graphics.shapes
import
Drawing
from
reportlab.graphics.charts.barcharts
import
VerticalBarChart
from
reportlab.graphics.charts.legends
import
Legend
from
reportlab.lib
import
colors
from
reportlab.lib.pagesizes
import
A4
from
reportlab.lib.units
import
cm
def
draw_text(st, text: str):
return
Paragraph(text, st)
def
draw_img(path):
img
= Image(path)
#
读取指定路径下的图片
img.drawWidth = 6*cm
#
设置图片的宽度
img.drawHeight = 5*cm
#
设置图片的高度
return
img
def
draw_table(*
args):
col_width
= 120
style
=
[
(
'
FONTNAME
'
, (0, 0), (-1, -1),
'
song
'
),
#
字体
(
'
FONTSIZE
'
, (0, 0), (-1, 0), 12),
#
第一行的字体大小
(
'
FONTSIZE
'
, (0, 1), (-1, -1), 10),
#
第二行到最后一行的字体大小
(
'
BACKGROUND
'
, (0, 0), (-1, 0),
'
#d5dae6
'
),
#
设置第一行背景颜色
(
'
ALIGN
'
, (0, 0), (-1, -1),
'
CENTER
'
),
#
第一行水平居中
(
'
ALIGN
'
, (0, 1), (-1, -1),
'
LEFT
'
),
#
第二行到最后一行左右左对齐
(
'
VALIGN
'
, (0, 0), (-1, -1),
'
MIDDLE
'
),
#
所有表格上下居中对齐
(
'
TEXTCOLOR
'
, (0, 0), (-1, -1), colors.darkslategray),
#
设置表格内文字颜色
(
'
GRID
'
, (0, 0), (-1, -1), 0.5, colors.grey),
#
设置表格框线为grey色,线宽为0.5
(
'
SPAN
'
, (0, 1), (2, 1)),
#
合并第二行一二三列
table = Table(args, colWidths=col_width, style=
style)
return
table
def
draw_bar(bar_data: list, ax: list, items: list):
drawing
= Drawing(500, 200
)
bc
=
VerticalBarChart()
bc.x
= 45
#
整个图表的x坐标
bc.y = 45
#
整个图表的y坐标
bc.height = 150
#
图表的高度
bc.width = 350
#
图表的宽度
bc.data =
bar_data
bc.strokeColor
= colors.black
#
顶部和右边轴线的颜色
bc.valueAxis.valueMin = 0
#
设置y坐标的最小值
bc.valueAxis.valueMax = 20
#
设置y坐标的最大值
bc.valueAxis.valueStep = 5
#
设置y坐标的步长
bc.categoryAxis.labels.dx = 2
bc.categoryAxis.labels.dy
= -8
bc.categoryAxis.labels.angle
= 20
bc.categoryAxis.labels.fontName
=
'
song
'
bc.categoryAxis.categoryNames
=
ax
leg =
Legend()
leg.fontName
=
'
song
'
leg.alignment
=
'
right
'
leg.boxAnchor
=
'
ne
'
leg.x
= 475
#
图例的x坐标
leg.y = 140
leg.dxTextSpace
= 10
leg.columnMaximum
= 3
leg.colorNamePairs
=
items
drawing.add(leg)
drawing.add(bc)
return
drawing
from
reportlab.lib.colors
import
Color
from
reportlab.lib.pagesizes
import
A4
from
reportlab.lib.styles
import
getSampleStyleSheet
from
reportlab.lib.units
import
cm
from
reportlab.pdfbase
import
pdfmetrics
from
reportlab.pdfbase.ttfonts
import
TTFont
from
reportlab.pdfgen
import
canvas
from
reportlab.lib
import
colors
from
reportlab.platypus
import
BaseDocTemplate, Frame, Paragraph, NextPageTemplate, PageBreak, PageTemplate, Image
def
draw_text(st, text: str):
return
Paragraph(text, st)
def
draw_img(path):
img
= Image(path)
#
读取指定路径下的图片
img.drawWidth = 5*cm
#
设置图片的宽度
img.drawHeight = 4*cm
#
设置图片的高度
return
img
def
main(filename):
pdfmetrics.registerFont(TTFont(
'
微软雅黑
'
,
'
msyh.ttf
'
))
style
=
getSampleStyleSheet()
ts
= style[
'
Heading1
'
]
ts.fontName
=
'
微软雅黑
'
#
字体名
ts.fontSize = 18
#
字体大小
ts.leading = 30
#
行间距
ts.alignment = 1
#
居中
ts.bold =
True
hs
= style[
'
Heading2
'
]
hs.fontName
=
'
微软雅黑
'
#
字体名
hs.fontSize = 15
#
字体大小
hs.leading = 20
#
行间距
hs.textColor = colors.red
#
字体颜色
ns
= style[
'
Normal
'
]
ns.fontName
=
'
微软雅黑
'
ns.fontSize
= 12
ns.wordWrap
=
'
CJK
'
#
设置自动换行
ns.alignment = 0
#
左对齐
ns.firstLineIndent = 32
#
第一行开头空格
ns.leading = 20
doc
= BaseDocTemplate(filename, showBoundary=0, pagesize=
A4)
frameT
= Frame(doc.leftMargin, doc.bottomMargin, doc.width, doc.height, id=
'
normal
'
)
w
= doc.width / 3
h
=
w
bm
= doc.height -
h
frame1
= Frame(doc.leftMargin, bm, w, h, id=
'
col1
'
)
frame2
= Frame(doc.leftMargin + w, bm, doc.width-w, h, id=
'
col2
'
)
frame3
= Frame(doc.leftMargin, doc.bottomMargin, doc.width , bm-doc.topMargin, id=
'
col3
'
)
doc.addPageTemplates([
PageTemplate(id
=
'
TwoCol
'
, frames=
[frame1, frame2, frame3]),
PageTemplate(id
=
'
OneCol
'
, frames=
frameT),
elements
=
[]
elements.append(draw_img(
"
images/title.jpg
"
))
elements.append(draw_text(ns,
'
。
'
))
elements.append(NextPageTemplate(
'
OneCol
'
))
elements.append(PageBreak())
elements.append(draw_text(ns,
"
Frame one column,
"
))
doc.build(elements)
from
reportlab.lib.styles
import
ParagraphStyle
from
reportlab.platypus
import
PageBreak
from
reportlab.platypus.paragraph
import
Paragraph
from
reportlab.platypus.doctemplate
import
PageTemplate, BaseDocTemplate
from
reportlab.pdfbase
import
pdfmetrics
from
reportlab.pdfbase.ttfonts
import
TTFont
from
reportlab.platypus.frames
import
Frame
from
reportlab.lib.units
import
cm
class
MyDocTemplate(BaseDocTemplate):
def
__init__
(self, filename, **
kw):
self.allowSplitting
=
0
BaseDocTemplate.
__init__
(self, filename, **
kw)
template
= PageTemplate(
'
normal
'
, [Frame(2.5*cm, 2.5*cm, 15*cm, 25*cm, id=
'
F1
'
)])
self.addPageTemplates(template)
self.chapter
=
0
self.section
=
0
def
afterFlowable(self, flowable):
if
isinstance(flowable, Paragraph):
text
=
flowable.getPlainText()
style
=
flowable.style.name
if
style ==
'
Title
'
:
self.chapter
+= 1
self.canv.bookmarkPage(f
"
chapter{self.chapter}
"
)
self.canv.addOutlineEntry(f
"
Chapter {self.chapter}
"
, f
"
chapter{self.chapter}
"
, level=
0)
elif
style ==
'
Heading1
'
:
self.section
+= 1
self.canv.bookmarkPage(f
"
section{self.section}
"
)
self.canv.addOutlineEntry(f
"
Section {self.section}
"
, f
"
section{self.section}
"
, level=1
)
def
main(filename):
pdfmetrics.registerFont(TTFont(
'
微软雅黑
'
,
'
msyh.ttf
'
))
title
= ParagraphStyle(name =
'
Title
'
,
fontName
=
'
微软雅黑
'
,
fontSize
= 22
,
leading
= 16
,
alignment
= 1
,
spaceAfter
= 20
)
h1
=
ParagraphStyle(
name
=
'
Heading1
'
,
fontSize
= 14
,
leading
= 16
)
story
=
[]
story.append(Paragraph(
'
继承BaseDocTemplate
'
, title))
story.append(Paragraph(
'
Section 1
'
, h1))
story.append(Paragraph(
'
Text in Section 1.1
'
))
story.append(PageBreak())
story.append(Paragraph(
'
Section 2
'
, h1))
story.append(Paragraph(
'
Text in Section 1.2
'
))
story.append(PageBreak())
story.append(Paragraph(
'
Chapter 2
'
, title))
story.append(Paragraph(
'
Section 1
'
, h1))
story.append(Paragraph(
'
Text in Section 2.1
'
))
doc
=
MyDocTemplate(filename)
doc.build(story)
四、使用SimpleDocTemplate
SimpleDocTemplate就是继承BaseDocTemplate的一种简单实现,它覆盖了接口handle_pageBegin,重载了build接口。
它把页面分成两种:首页和后续页,对应回调两个过程onFirstPage=, onLaterPages=,只需要实现这两个回调过程即可。
适用显示页眉和页脚,其它的功能就有限了。
SimpleDocTemplate(文档模板)
QrCode(二维码)
drawOn(显示Flowable)
from
reportlab.platypus
import
SimpleDocTemplate, Paragraph
from
reportlab.platypus
import
PageBreak
from
reportlab.lib.styles
import
ParagraphStyle
from
reportlab.lib.colors
import
Color
from
reportlab.lib.pagesizes
import
A4
from
reportlab.lib.units
import
mm
from
reportlab.pdfbase
import
pdfmetrics
from
reportlab.pdfbase.ttfonts
import
TTFont
from
reportlab.graphics.barcode
import
qr
def
myFirstPage(canvas, doc):
canvas.saveState()
canvas.setFillColorRGB(0, 0, 0)
canvas.setFont(
'
微软雅黑
'
,12
)
str
=
"
(内部资料)
"
canvas.drawCentredString(doc.width
/2, 25*
mm, str)
myLaterPages(canvas, doc)
canvas.restoreState()
#
页眉页脚
def
myLaterPages(canvas, doc):
canvas.saveState()
canvas.setStrokeColorRGB(
0.8, 0.8, 0.8
)
canvas.line(0,
32, doc.width, 32
)
canvas.line(0, A4[
1]-45, doc.width, A4[1]-45
)
canvas.setFillColorRGB(0, 0, 0)
canvas.setFont(
'
微软雅黑
'
,10
)
str
=f
"
Page {doc.page}
"
canvas.drawCentredString(doc.width
/2, 5*
mm, str)
canvas.setFillColorRGB(
1
, 0, 0)
qr_code
= qr.QrCode(
'
https://www.cnblogs.com/windfic
'
, width=45, height=45
)
canvas.setFillColorRGB(0, 0, 0)
qr_code.drawOn(canvas, 0, A4[
1]-45
)
canvas.restoreState()
def
main(filename):
pdfmetrics.registerFont(TTFont(
'
微软雅黑
'
,
'
msyh.ttf
'
))
doc
= SimpleDocTemplate(filename, pagesize=A4, leftMargin=10, rightMargin=10
)
title
= ParagraphStyle(name =
'
Title
'
,
fontName
=
'
微软雅黑
'
,
fontSize
= 22
,
leading
= 16
,
alignment
= 1
,
spaceAfter
= 20
)
contents
=
[]
contents.append(Paragraph(
'
使用SimpleDocTemplate
'
, title))
contents.append(Paragraph(
'
Hello
'
))
contents.append(PageBreak())
contents.append(Paragraph(
'
World
'
))
doc.build(contents, onFirstPage
=myFirstPage, onLaterPages=myLaterPages)
from
reportlab.platypus
import
SimpleDocTemplate, Image, Paragraph, PageBreak
from
reportlab.pdfgen
import
canvas
from
reportlab.lib.units
import
mm
from
reportlab.lib.colors
import
Color
from
reportlab.lib.pagesizes
import
A4
from
reportlab.pdfbase
import
pdfmetrics
from
reportlab.pdfbase.ttfonts
import
TTFont
from
reportlab.lib.styles
import
ParagraphStyle
class
NumberedCanvas(canvas.Canvas):
def
__init__
(self, *args, **
kwargs):
canvas.Canvas.
__init__
(self, *args, **
kwargs)
self._saved_page_states
=
[]
def
showPage(self):
self._saved_page_states.append(dict(self.
__dict__
))
self._startPage()
def
save(self):
"""
add page info to each page (page x of y)
"""
num_pages
=
len(self._saved_page_states)
for
state
in
self._saved_page_states:
self.
__dict__
.update(state)
self.draw_page_number(num_pages)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def
draw_page_number(self, page_count):
self.setFont(
"
Helvetica
"
, 9
)
self.setStrokeColor(Color(0, 0, 0, alpha
=0.5
))
self.line(
10*mm, 15*mm, A4[0] - 10*mm, 15*
mm)
self.setFillColor(Color(0, 0, 0, alpha
=0.5
))
self.drawCentredString(A4[0]
/2, 10*mm,
"
Page %d of %d
"
%
(self._pageNumber, page_count))
def
main(filename):
pdfmetrics.registerFont(TTFont(
'
微软雅黑
'
,
'
msyh.ttf
'
))
title
= ParagraphStyle(name =
'
Title
'
,
fontName
=
'
微软雅黑
'
,
fontSize
= 22
,
leading
= 16
,
alignment
= 1
,
spaceAfter
= 20
)
image
= Image(
"
images/title.jpg
"
)
image.drawWidth
= 160
image.drawHeight
= 160*(image.imageHeight/
image.imageWidth)
elements
=
[
Paragraph(
'
继承Canvas
'
, title),
Paragraph(
"
Hello
"
),
image,
PageBreak(),
Paragraph(
"
world
"
),
doc
=
SimpleDocTemplate(filename)
doc.build(elements, canvasmaker
=
NumberedCanvas)
直接使用Canvas类,可以精确输出,但需要自己排版,而且它的坐标原点在左下角。
其中也可以放置Flowable,需要排版的Flowable,如Table等,调用warp函数即可自动排版。
如果是内容已经排版的格式转换程序,非常推荐使用这种方式。
from
reportlab.pdfgen
import
canvas
from
reportlab.platypus
import
Image
from
reportlab.lib.pagesizes
import
A4
from
reportlab.lib.units
import
mm
from
reportlab.lib.colors
import
Color
from
reportlab.pdfbase
import
pdfmetrics
from
reportlab.pdfbase.ttfonts
import
TTFont
def
draw_page_number(c, page, count):
c.setFont(
"
微软雅黑
"
, 9
)
c.setStrokeColor(Color(0, 0, 0, alpha
=0.5
))
c.line(
10*mm, 15*mm, A4[0] - 10*mm, 15*
mm)
c.setFillColor(Color(0, 0, 0, alpha
=0.5
))
c.drawCentredString(A4[0]
/2, 10*mm,
"
Page %d of %d
"
%
(page, count))
def
main(filename):
pdfmetrics.registerFont(TTFont(
'
微软雅黑
'
,
'
msyh.ttf
'
))
c
=
canvas.Canvas(filename)
c.bookmarkPage(
"
title
"
)
c.addOutlineEntry(
"
my book
"
,
"
title
"
, level=
0)
c.setFont(
"
微软雅黑
"
, 18
)
c.drawCentredString(A4[0]
/2, A4[1] - 50,
"
单独使用Canvas
"
)
c.setFont(
"
微软雅黑
"
, 12
)
c.drawString(
100, A4[1] - 76,
"
Hello
"
*100
)
img
= Image(
"
images/title.jpg
"
)
img.drawWidth
= 160
img.drawHeight
= 160*(img.imageHeight/
img.imageWidth)
img.drawOn(c,
100, A4[1] - 200
)
draw_page_number(c,
1, 2
)
c.bookmarkPage(
"
section1
"
)
c.addOutlineEntry(
"
first section
"
,
"
section1
"
, level=1
)
c.showPage()
c.drawString(
100, A4[1] - 50,
"
World
"
)
draw_page_number(c,
2, 2
)
c.bookmarkPage(
"
section2
"
)
c.addOutlineEntry(
"
second section
"
,
"
section2
"
, level=1
)
c.showPage()
c.showOutline()
c.save()
综合以上六种方式来看,前五种基本上是同一频道,可以结合起来使用。但第六种,给我个人的感觉是更自在一点,不用去摸索,想怎么来就怎么来。
本来想推荐前五种方式融合的方案,但是当我用第六种方式实现了所有的内容,却发现代码更少,更直观。
因此,对比之下,我更推荐使用第六种方式了。
全部源码:
点此下载
https://files.cnblogs.com/files/windfic/PyPdf.7z
出处:
https://www.cnblogs.com/windfic/p/17157841.html