首先,先说明一下需求,之所以需要生成PDF中文文档,是因为网站需要生成打印用户的报名表信息。因此将踩过的坑记录下来。
Python版本 3.7.4
reportlab包的版本 3.5.31
Linux版本 Centos7
在作出说明之前,首先大家可以参考reportlab的官方英文文档,文章后面已附带上了。
如果是有了一定的reportlab基础的,并且只想解决加载中文的问题的,可以直接跳到最后,看核心代码。希望看了后留个赞^ _ ^
根据官方手册的说明,Unicode和UTF8是默认的输入编码。
以下内容是根据手册翻译的相关说明:
3.1 Unicode和UTF8是默认的输入编码
从reportlab 2.0版(2006年5月)开始,您提供给我们的API的所有文本输入都应使用UTF8或Python Unicode对象。 这适用于canvas.drawString和相关API的参数,表单元格内容,图形对象参数和段落源文本。
我们考虑过使输入编码可配置,甚至取决于语言环境,但决定“显式比隐式更好”。
这简化了我们以前有关希腊字母,符号等的许多工作。 要显示字符,请找出其Unicode代码点,并确保所使用的字体能够显示它。
如果要改用ReportLab 1.x应用程序,或从另一个包含单字节数据的源中读取数据(例如latin-1或WinAnsi),则需要转换为Unicode。 Python编解码器软件包现在包括所有常见编码(包括亚洲编码)的转换器。
3.2替代字体的自动输出
代码中仍然有很多地方,包括rl_config默认的Encoding参数,以及传递给各种Font构造函数的参数,这些参数引用了编码。 在过去人们需要使用PDF查看设备支持的Symbol和ZapfDingbats字体中的字形时,这些功能很有用。 默认情况下,标准字体(Helvetica,Courier和Times Roman)将提供Latin-1中可用的字形。 但是,如果我们的引擎检测到不在字体中的字符,它将尝试切换到Symbol或Za pfDingbats来显示这些字符。 例如,如果在对drawString的调用中包括一对面向右的scis sors的Unicode字符\ u2702,则应该看到它们(有一个示例intest_pdfgen_general.py/pdf)。 无需在代码中切换字体。
3.3使用非标准的Type 1字体
如上一章所述,每个Acrobat Reader副本都内置了14种标准字体,因此ReportLab PDF库只需要按名称引用即可。 如果要使用其他字体,它们必须可用于您的代码,并将嵌入到PDF文档中。
您可以使用下面描述的机制在文档中包括任意字体。 我们有一个名为DarkGardenMK的开源字体,我们可以将其用于测试和/或记录目的(也可以使用)。 它与ReportLab分发包捆绑在directoryreportlab / fonts中。
现在,字体嵌入依赖于Adobe AFM(‘Adobe Font Metrics’)和PFB(‘Printer Font Binary’)格式的字体描述文件。 前者是一个ASCII文件,包含有关字体中字符(“字形”)的信息,例如高度,宽度,边界框信息和其他“度量”,而后者是描述字体形状的二进制文件。 reportlab / fonts目录包含用作示例字体的文件“ DarkGardenMK.afm”和“ DarkGardenMK.pfb”。
在DarkGardenMK示例中,我们明确指定了要加载的字体描述文件的位置。 一般而言,您宁愿将字体存储在某些规范的位置,并使嵌入机制知道它们。 使用在本节开始时已经看到的相同配置机制,我们可以指定Type-1字体的默认搜索路径。
不幸的是,对于这样的位置(甚至不在同一平台上),还没有可靠的标准,因此,您可能必须编辑reportlab / rl_config.py文件来修改T1SearchPathidentifier的值以包含其他目录。 我们自己的建议是在开发中使用reportlab / fontsfolder。 并在任何类型的受控服务器部署中将所需的字体作为应用程序的打包部分。 这样可以使您免受其他软件或系统管理员正在安装和卸载的字体的影响。
关于缺少字形的警告
如果指定编码,通常会假定字体设计者已提供了所有需要的字形,但是并不总是如此。 在我们的示例字体中,字母出现了,但是缺少许多符号和重音符号。 字体的默认行为是打印一个’notdef’字符-通常是斑点,点或空格-当传递该字符时它将无法绘制。 但是,您可以要求图书馆发出警告。 下面的代码(在加载字体之前执行)将导致在注册时针对未包含在字体中的任何字形生成警告。
import reportlab.rl_config
reportlab.rl_config.warnOnMissingFontGlyphs = 0
3.4标准单字节字体编码
本节向您显示常用编码中可用的字形。
下面的代码表显示了WinAnsiEncoding中的字符。 这是Windows和美国和西欧的许多Unix系统上的标准编码。 它也被称为代码页1252,实际上与ISO-Latin-1相同(它包含一个或两个额外的字符)。 这是Reportlab PDF库使用的默认编码。 它是由reportlab / lib,codecharts.py中的标准例程生成的,可用于显示字体的内容。 沿边缘的索引号以十六进制表示。
内容可能有点多,但是看看了解了就好,主要明白,它默认使用的是Unicode和UTF-8的编码格式即刻。噢,对了另外说一下,在python2中字符传unicode码是使用unicode函数,而python3中只需要使用str()函数就可以了。
import reportlab.pdfbase.ttfonts #导入reportlab的注册字体模块
tem = reportlab.pdfbas e.ttfonts.TTFont('song','simsun.ttc') #导入字体
reportlab.pdfbase.pdfmetrics.registerFont(tem) #注册当前目录下的字体
为了项目方便我直接将要使用的字体放在了当前目录下,在实际下项目中,需要改变TTFont的第二个参数给与实际的指定路径。而TTFont的第一个参数是指定这个字体的名字,在后面我们使用canvas的描绘工具时,就可以使用这个名字去指定对应的字体了。
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer,Table,TableStyle
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib import colors
from reportlab.rl_config import defaultPageSize #导入默认页面大小,一个数组
from reportlab.lib.units import inch #导入英寸的单位,一个inch代表一英寸
#上面导入的库都模块都会用到,主要解释一下第一个导入语句导入的东西
#SimpleDocTemplate是reportlab的一个简单文档模板,关于模板的使用手册里面也有。使用它可以快速方便的生成文档
#Paragraph是设置文章的段落格式,将指定的字符串内容形成能够被SimpleDocTemplate对象填充使用的内容
#Spacer是用来填充指定大小的空白内容的方法
#Table实用来生成一个SimpleDocTemplate对象中的表格内容,最后在文档中展示一个表格。
#TbleStyle功能如其名。简单的说一下,reportlab中的表格功能是很强大的,详细的使用见手册。
PAGE_HEIGHT=defaultPageSize[1] #从导入的默认页面大小中,获得对应的页面高度与页面宽度
PAGE_WIDTH=defaultPageSize[0] #获取宽度
styles = getSampleStyleSheet() #获取简单的样式表格
# 首先,我们从其他模块导入一些构造函数,一些段落样式以及其他便利。
Title = "Hello Table"
pageinfo = "页"
def myFirstPage(canvas, doc): #设置首页,参数格式固定
canvas.saveState() #保存之前的画笔格式等状态,并设置新的状态
canvas.setFont('song',5) #使用注册的字体
text = '页眉文本''
canvas.drawString(PAGE_WIDTH/5.0,PAGE_HEIGHT-50, str(text)) #前两个参数是设置画笔所绘值的文字所在的位置,其起始坐标是页面的左下角,第一个参数是设置x的位置,第二个参数设置y的位置
canvas.setFont('Times-Bold',16) #设置新的样式
canvas.drawCentredString(PAGE_WIDTH/2.0, PAGE_HEIGHT-108, Title) #绘制居中文本到页面上
canvas.setFont('song',9)
canvas.drawString(inch, 0.75 * inch, "第 1 %s" % pageinfo) # 设置第一页的页脚;可以看出,页脚是直接画在相应的位置的,也就是说,需要我们手动的调出页脚所在的合适位置。但是一般来说,都是左边句1*inch,下边剧0.75*inch。
canvas.restoreState() #将画笔格式等状态还原
# 我们使用上述功能定义文档首页的固定功能。
def myLaterPages(canvas, doc): #设置其他页
canvas.saveState() #保存之前画笔的格式
canvas.setFont('song',9) #设置新的样式
canvas.drawString(inch, 0.75 * inch, "第 %d %s" % (doc.page, pageinfo)) #填充新的内容
canvas.restoreState #画笔样式还原
# 因为我们希望第一页之后的页面看起来与第一页不同,所以我们为其他页面的固定功能定义了一种替代布局。
# 请注意,以上两个函数使用pdfgen级画布操作来绘制页面的注释。
def go():
import reportlab.pdfbase.ttfonts #导入reportlab的注册字体
reportlab.pdfbase.pdfmetrics.registerFont(reportlab.pdfbase.ttfonts.TTFont('song', 'simsun.ttc')) #注册当前目录下的字体,宋体
doc = SimpleDocTemplate("phello.pdf") #生成一个模板的对象doc,给出指定的文件类型对象或者文件名,没有的该文件则会新生成
Story = [Spacer(1,0.5*inch)] #使用Story列表来存储添入文档中的内容,先填入开头的部分空白区域
style = styles["Normal"] #选着默认中的正常格式
style.fontName = 'song' #使用自己注册的字体
data = [ #生成一个表格中的数据,必须是一个是一个二维数组
[1,2,3,4,5],
[5,4,3,2,1],
[2,3,4,5,1],
[3,4,5,1,2],
]
t = Table(data,5*[1*inch],4*[0.8*inch]) #使用表格数据生成一个表格对象,可以被用来填入文档模板对象doc
t.setStyle(TableStyle([ #使用表格的setStyle方法设置表格样式,其样式是一个Tabletyle对象,对应的样式设置内容请查阅手册
('BACKGROUND',(1,1),(-2,-2),colors.green),
('TEXTCOLOR',(0,0),(1,-1),colors.red),
('BOX',(0,0),(-1,-1),2,colors.black), #BOX命令设置边框
('BOX',(0,0),(1,-1),1,colors.red),
('BOX',(0,0),(-1,0),3,colors.blue),
('LINEABOVE',(0,1),(-3,1),5,colors.blue),
]))
Story.append(t) #将表格添加到模板对象
for i in range(100): #遍历添加正文信息,添加每一个段落。
bogustext = ("This is Paragraph number %s. " % i) *20 #将文本复制20遍,再输出到填充对相中
p = Paragraph(bogustext, style) #使用Paragraph对内容设置段落格式style,必须要对这种内容设置段落格式才可以保存到doc对象中
Story.append(p) #添加段落内容到列表,内容必须要经过Paragraph方法加上格式处理后才能正常的绘制
Story.append(Spacer(1,0.2*inch)) #在上面的信息填充完毕之后,再添入空格区域,形成段落间距的效果;
doc.build(Story, onFirstPage=myFirstPage, onLaterPages=myLaterPages) #使用doc的build方法建立文档,其中三个参数的含义一目了然。
# onFirstPage设置第一页的样式,onLaterPages设置其他页的样式,具体的样式通过对应的函数定义
# 最后,我们创建一个Story并构建文档。
# 请注意,我们在此使用的是“罐头”文档模板,该模板随页面模板一起预先构建。
# 我们还使用了预先构建的段落样式。
# 我们在这里仅使用两种类型的可流动物-Spacers(空白间隔)和Paragraphs(段落)。
# 第一个空格键确保Paragraphs跳过标题字符串。
go() #执行主逻辑函数
如果需要使用中文字体在reportlab中表现出来,我们需要使用先加载相应目录下的字体,然后再注册该字体为对应的字体名称,后面即可以在各种样式设置中使用它。
以下是核心代码。
import reportlab.pdfbase.ttfonts #导入reportlab的注册字体
reportlab.pdfbase.pdfmetrics.registerFont(reportlab.pdfbase.ttfonts.TTFont('song', 'simsun.ttc')) #注册当前目录下的字体,宋体