GoPdf的简单使用
项目地址:https://github.com/signintech/gopdf
在最近的工作中要求使用go去生成一份PDF文档。一开始是选择的https://github.com/jung-kurt/gofpdf这家的,但是在测试的时候发现这货不支持中文。因此换成了GoPdf。
在生成的PDF的过程中感觉比较烦的就是坐标的计算,GoPdf框架的坐标轴使用左上角开始的,向左为x轴的正方向,向下为y轴的正方向。这的坐标轴的设置倒是和Android的坐标轴设置类似(听说传统pdf的坐标轴是从右下角开始的?)。这样的坐标设计不需要我倒着计算坐标了。
我们写PDF时一般都是使用A4纸的大小,如何设置成A4纸大小官方文档上有说明,对于这个大小,我在使用的时候弄出了一些小小的问题。
设置成A4纸大小:
pdf.Start(gopdf.Config{ PageSize: gopdf.Rect{W: 595.28, H: 841.89}})
图片绘制
如图:

使用的图片原图:

可以从上图看出绘制的图片有一部分超出了绘制区域。
在出现这个问题后,我google到了如下信息:
分辨率是72像素/英寸时,A4纸的尺寸的图像的像素是595×842;
分辨率是96像素/英寸时,A4纸的尺寸的图像的像素是794×1123;(默认)
分辨率是120像素/英寸时,A4纸的尺寸的图像的像素是1487×2105;
分辨率是150像素/英寸时,A4纸的尺寸的图像的像素是1240×1754;
分辨率是300像素/英寸时,A4纸的尺寸的图像的像素是2480×3508;
那我就想用分辨率是120像素 / 英寸的W和H进行尝试,在进行尝试后发现生成的PDF已经不是A4纸的大小了。
于是我就去github上询问了作者,作者说A4值的大小就是文档中提供的W和H。当得到这个答案后,整个人就懵逼了。那如何将这张图片完整的绘制在指点区域中呢?重新看一下提供的方法:
func (gp *GoPdf) Image(picPath string, x float64, y float64, rect *Rect) error
第一个参数是图片的路径,第二和第三个参数是要绘制的坐标,那最后一个参数是做什么的呢?在之前的方法中我直接设置了nil,后面通过测试,这个参数就是图片要绘制的区域。通过在这个参数中指定绘制图片的区域大小,就能够把这张图片完整的绘制出来。
绘制文字
注:如果要绘制中文内容,请找一个可靠一定的中文字库。不然会出现明明在代码中设置了文字的内容,但是生成的PDF中没有一个现实的中文内容。
字库问题
在这个框架中使用的字库文件只能是ttf文件,不能是ttc文件,如果使用ttc文件会报错。
绘制文字
绘制文字使用的一般方法是:
func (gp *GoPdf) Cell(rectangle *Rect, text string) error
这个rectangle参数一般设置为nil,这个参数具体是做什么的我并没有进行尝试。
在调用这个方法之前需要对字体,字体大小进行设置:
添加字体:
func (gp *GoPdf) AddTTFFont(family string, ttfpath string) error
设置使用的字体和大小:
func (gp *GoPdf) SetFont(family string, style string, size int) error
其中family为之前添加的字体名称,style为使用的风格,size是字体的大小。style支持“”和U两种,一般使用“”。画文字和画图片不同,Cell()方法并没有设设置坐标,因此需要在画文字前使用func (gp *GoPdf) SetX(x float64)
和func (gp *GoPdf) SetY(y float64)
两个方法来设置文字的起始坐标。把一个文字所占的区域想象成一个矩形区域,设置的x
和y
值也是该矩形区域的左上坐标。因此在计算下一行文字的起始位置,不要忘记了在y轴方向上加上文字的大小。
如果要设置文字的颜色就使用:
func (gp *GoPdf) SetTextColor(r uint8, g uint8, b uint8)
rgb就是指三原色,例如#a8a8a8(灰色,随便举个栗子),那么就可以这样调用:SetTextCoolor(0xa8,0xa8,0xa8)
。
画直线
画直线使用的方法是:
func (gp *GoPdf) Line(x1 float64, y1 float64, x2 float64, y2 float64)
(x1,y1)为起始坐标,(x2,y2)为结束位置。
设置线的颜色可以使用:
func (gp *GoPdf) SetStrokeColor(r uint8, g uint8, b uint8)
设置线的宽度:
func (gp *GoPdf) SetLineWidth(width float64))
但是线的宽度是从基准线向两边扩展。
画椭圆
func (gp *GoPdf) Oval(x1 float64, y1 float64, x2 float64, y2 float64)
这是画椭圆的方法和我我们在学校里的椭圆方程不同,经过测试,是画一个矩形中的内切矩形。因此只有两个坐标就可以了,一个是左上角坐标,一个是右下角坐标。
组合
带背景颜色的文字
先上效果:

我实现的思路是先画绿色背景。然后在写文字内容。文字和边框周围有一定的间距,为了能够复用我定义了如下的结构体:
type Border struct {
//使用的字体
Family string
// 内容
Content string
//内容大小
ContentSize int
//背景颜色
BackgroundColor [3]uint8
//左内边距
LeftPadding float64
//上内边距
TopPadding float64
//右内边距
RightPadding float64
//下内边距
BottomPadding float64
}
因为线的长度和文字的长度有关,因此需要先计算文字的长度:
func (gp *GoPdf) MeasureTextWidth(text string) (float64, error)
在得到文字的长度后,加上LeftPadding
和RightPadding
就是背景的宽度。背景的高度需要在文字的大小基础上添加上TopPadding
和BottomPadding
。
func (h *Helper) drawBorder(border *Border, x, y float64) (currentX, currentY float64, err error) {
h.PDF.SetX(x)
h.PDF.SetY(y)
h.PDF.SetFont(border.Family, "", border.ContentSize)
//设置成白色
h.PDF.SetTextColor(0xFF, 0xFF, 0xFF)
h.PDF.SetStrokeColor(border.BackgroundColor[0], border.BackgroundColor[1], border.BackgroundColor[2])
//得到线宽
lineheight := float64(border.ContentSize) + border.TopPadding + border.BottomPadding
h.PDF.SetLineWidth(lineheight)
contentLen, err := h.PDF.MeasureTextWidth(border.Content)
if err != nil {
return
}
lineWidth := border.LeftPadding + contentLen + border.RightPadding
h.PDF.Line(x, y+lineheight/2, x+lineWidth, y+lineheight/2)
//写文字内容
h.PDF.SetX(x + border.LeftPadding)
h.PDF.SetY(y + border.TopPadding)
h.PDF.Cell(nil, border.Content)
currentX = x
currentY = y + lineheight
return
}
可以在这里面看到,我画线y轴方向,向下添加了半个线高。在线画完之后,重新设置x和y坐标,来进行文字内容的绘制。
最后
在绘制PDF的时候需要注意使用func (gp *GoPdf) AddPage()
来进行添加新页,不然生成的PDF文件是无法打开的。
gopdf这个开源框架中还提供了大量的使用方法,比如画贝塞尔曲线(func (gp *GoPdf) Curve(x0 float64, y0 float64, x1 float64, y1 float64, x2 float64, y2 float64, x3 float64, y3 float64, style string)
),设置线的类型(func (gp *GoPdf) SetLineType(linetype string)
)等等。
使用该框架给我最大的感受就是:要实现什么效果,最重要的还是对效果进行分析,选择正确的方法,计算出正确坐标(这个我感觉比较重要)