第三方模块reportlab的技术文档里有一句话,大意是生产pdf文档时重复代码的都应该做成form,可以节省很多时间和空间。为啥重复的部分要写成form不写成函数呢?
这里的form不翻译成表格或表单,form还有形式、形状、形态……的意思,但用哪个都不太贴切,所以我们不用中文翻译还是用form这个名称。
首先来看怎样生成一个form,在主函数里生成画布对象c,然后调用forms()来看form的生成和使用:
if __name__ == '__main__':
from reportlab.pdfgen import canvas
c = canvas.Canvas('speCap.pdf')
forms(c)
c.showPage()
c.save()
在函数forms()里,生成form用画布对象的方法函数beginForm(),用给form起的名字字串做实参。然后放重复使用的代码,画布对象的方法函数endForm()宣告form定义的完成。
def forms(myCanvas):
# ------------------ 生成Form ---------------------
myCanvas.beginForm("SpumoniForm")
# form的名字为SpumoniForm
spumoni(myCanvas)
# 调用函数画图一所示的图形
from reportlab.lib.colors import red, green, blue
myCanvas.setFillColor(green)
# 设置字体颜色
myCanvas.setFont('Helvetica-BoldOblique', 20)
# 设置字体和大小
myCanvas.drawString(80, 260, 'Love, Enjoy, Appreciate')
# 写字
myCanvas.setStrokeColor(blue)
# 设置线条颜色
myCanvas.rect(50, 50, 300, 300)
# 画正方形
myCanvas.endForm()
spumoni()是前面定义过的、往画布上画图形的函数,调用它可生成如图一这样的图形:
图一
定义SpumoniForm这个form所做的事情就是:画图一的图形;设置字体和大小,而后写一行字Love, Enjoy, Appreciate;再画一个蓝色的正方形,如图二
图二
但现在我们只是定义了这个form,没有使用它,只定义不使用的form是不会被显示的。
用画布对象的方法函数doForm(form的名字)来使用form,所以接下来:
def forms(myCanvas):
……
# -------------- 使用Form ---------------------
myCanvas.setStrokeColor(red)
# 设置线条颜色为红色
myCanvas.doForm("SpumoniForm")
这时候生成的整个pdf文件打开后看到的图形和文字就是图二的样子。注意在用doForm()应用SpumoniForm之前整个画布的线条颜色用 myCanvas.setStrokeColor(red)设成了红色,而画布上显示的是蓝色正方形,可见画布上的设置没有影响到form内部的操作。虽然form作为一个整体往画布上放时还是要受制于画布的。
应用SpumoniForm之后,继续往画布上画圆圈和写字:
def forms(myCanvas):
……
# -------------- 使用Form ---------------------
myCanvas.setStrokeColor(red)
myCanvas.doForm("SpumoniForm")
# 圆和字的颜色由画布规定
myCanvas.circle(260, 330, 25)
myCanvas.setFont('Times-Italic', 16)
myCanvas.drawCentredString(260, 330, 'fly')
生成的speCap.pdf文件打开后,forms(c)函数起作用的部分效果如图三:
图三
圆圈的颜色是红色,圆圈里的字fly是黑色(之前没有对画布上字体颜色的设置,默认是黑色),没有受SpumoniForm内部绿字蓝线条的影响。
现在把SpumoniForm里的操作用一个函数spuFunc()来实现,就是把myCanvas.beginForm("SpumoniForm")与myCanvas.endForm()之间的语句全盘搬过去:
def spuFunc(c):
spumoni(c)
c.setFillColor(green)
c.setFont('Helvetica-BoldOblique', 20)
c.drawString(80, 260, 'Love, Enjoy, Appreciate')
c.setStrokeColor(blue)
c.rect(50, 50, 300, 300)
然后把函数forms()中的语句myCanvas.doForm("SpumoniForm")替换成spuFunc(myCanvas),其它保持不变:
def forms(myCanvas):
……
myCanvas.setStrokeColor(red)
# myCanvas.doForm("SpumoniForm")
# 用函数实现Form同样的功能
spuFunc(myCanvas)
myCanvas.circle(260, 330, 25)
myCanvas.setFont('Times-Italic', 16)
myCanvas.drawCentredString(260, 330, 'fly')
运行结果如图四:
图四
首先函数调用spuFunc(myCanvas)把myCanvas作为实参传给了函数spuFunc()的形参,跟数值和字符串实参形参不一样,那种情况传递的是值,形参的作用域限于所在的函数内部。这里传递的是画布的地址,也就是说实参myCanvas和形参c指向同一块画布,它俩是同一个画布对象的不同名字,不受局部作用域的限制。所以在Forms()函数里用myCanvas.setStrokeColor(red)将画布的线条线条设成红色,进入函数spuFunc()也是有效的,只不过后来被spuFunc()函数内的语句c.setStrokeColor(blue)給覆盖了,字体、字体大小和字的颜色同理。这就是为什么画出的正方形是蓝色,写出来的字love, enjoy and appreciate是绿色的。从spuFun()函数出来回到forms()函数时,同一个画布对象的字体颜色仍是绿色,线条颜色仍是蓝色,所以写出来的字fly是绿色的,画出来的圈则是蓝色。
可见如果用函数,函数内的画布设置受调用之前的画布设置影响,函数内部的画布设置也会影响调用之后的画布设置;而用form实现跟函数同样的功能,form内部的操作比如画布设置相对于form外是独立互不影响的。
如果非要用函数实现form的功能,那就在进出函数时用方法函数saveState()和restoreState()保存和恢复画布的状态。
全部代码:
def spumoni(canvas):
from reportlab.lib.units import cm
from reportlab.lib.colors import pink, green, brown, white
x = 0; dx = 2*cm
for i in range(4):
for color in (pink, green, brown):
canvas.setFillColor(color)
canvas.rect(x,4*cm,dx,20*cm,stroke=0,fill=1)
x = x+dx
canvas.setFillColor(white)
canvas.setFont("Helvetica-Bold", 85)
canvas.drawCentredString(10*cm, 10*cm, "SPUMONI")
def spuFunc(c):
spumoni(c)
c.setFillColor(green)
c.setFont('Helvetica-BoldOblique', 20)
c.drawString(80, 260, 'Love, Enjoy, Appreciate')
c.setStrokeColor(blue)
c.rect(50, 50, 300, 300)
def forms(myCanvas):
# ------------------ 生成Form ---------------------
myCanvas.beginForm("SpumoniForm")
# form的名字为SpumoniForm
spumoni(myCanvas)
# 调用函数画图一所示的图形
from reportlab.lib.colors import red, green, blue
myCanvas.setFillColor(green)
# 设置字体颜色
myCanvas.setFont('Helvetica-BoldOblique', 20)
# 设置字体和大小
myCanvas.drawString(80, 260, 'Love, Enjoy, Appreciate')
# 写字
myCanvas.setStrokeColor(blue)
# 设置线条颜色
myCanvas.rect(50, 50, 300, 300)
# 画正方形
# -------------- 使用Form ---------------------
myCanvas.setStrokeColor(red)
myCanvas.doForm("SpumoniForm")
# 用函数实现Form同样的功能
# spuFunc(myCanvas)
# 圆和字的颜色由画布规定
myCanvas.circle(260, 330, 25)
myCanvas.setFont('Times-Italic', 16)
myCanvas.drawCentredString(260, 330, 'fly')
if __name__ == '__main__':
from reportlab.pdfgen import canvas
c = canvas.Canvas('speCap.pdf')
forms(c)
c.showPage()
c.save()