需要用到的库:jung-kurt/gofpdf
由于CellFormat方法不支持\n换行,会被变成乱码,MultiCell方法会自动将坐标定位到下一行。所以需要自己实现坐标的计算变换。通过Rect方法画出单元格,MultiCell方法在格内自动换行写字,在计算坐标重复写单元格,最终组成一行。
- 实现自动换行的表格
go
import "github.com/jung-kurt/gofpdf"
type pdfLine struct {
pdf *gofpdf.Fpdf
h float64 // 需要的行高
x float64 // 记录开始时坐标
y float64 // 记录开始时坐标
style string // 风格 F仅填充 D仅边框 或者DF两个都要
alignStr string // 对其方式 LCR为水平的左、中、右,TMBA为垂直的上、中、下、基准线
fontH float64 // 字体高度
cells []pdfCell //
}
type pdfCell struct {
w float64 // 宽度
h float64 // 行高
txtStr string // 文本
lines int // 判断文本会占几行
}
func (s *pdfLine) addLine(style string, alignStr string, cells ...pdfCell) {
s.style = style
s.alignStr = alignStr
_, _, _, mbottom := s.pdf.GetMargins() // 获取当前页面边距
_, pageh := s.pdf.GetPageSize() // 获取当前页面尺寸
x, y := s.pdf.GetXY() // 获取当前位置
// 页面剩余行高不够时 开启新一页
if s.pdf.GetY()+s.h > pageh-mbottom {
s.pdf.AddPage()
y = s.pdf.GetY()
}
s.x = x
s.y = y
_, s.fontH = s.pdf.GetFontSize()
// 记录需要的最高行高
for _, cell := range cells {
lines := s.pdf.SplitText(cell.txtStr, cell.w)
h := float64(len(lines)) * cell.h
if s.h < h {
s.h = h
}
cell.lines = len(lines)
s.cells = append(s.cells, cell)
}
s.write()
}
// 写入
func (s *pdfLine) write() {
x := s.x
y := s.y
// 手动记录并移动坐标
for _, c := range s.cells {
usedH := float64(c.lines) * s.fontH
margin := (s.h - usedH) / 2.0
s.pdf.Rect(x, s.y, c.w, s.h, s.style)
s.pdf.SetXY(x, y+margin) // 保持单元格内的文字有边距
s.pdf.MultiCell(c.w, s.fontH, c.txtStr, "", s.alignStr, false)
x += c.w
s.pdf.SetXY(x, y)
}
// 坐标重置为下一行的当前位置
s.pdf.SetXY(s.x, s.y+s.h)
// 重置变量
s.cells = nil
s.h = 0
}
// 使用 生成一个每行4列的表格
func main() {
pdf := gofpdf.New("P", "mm", "A4", "")
pdf.AddPage()
pdf.AddUTF8Font("NotoSansSC-Regular", "", "src/font/NotoSansSC-Regular.ttf")
pdf.SetFont("NotoSansSC-Regular", "", 12)
myPdf := pdfLine{pdf: pdf}
width, _ := pdf.GetPageSize() // 页面宽度
left, _, right, _ := pdf.GetMargins() // 左右边距
usable := width - left - right // 可用的页面宽度
_,h := pdf.GetFontSize() // 字体高度
tableH := h + 2 // 行高 多出2mm的边距
tableWidth := usable / 4 // 每个单元个的宽度
pdf.SetFillColor(233, 233, 233)
// 表头
myPdf.addLine("FD", "CM", []pdfCell{
{w: tableWidth, h: tableH, txtStr: "表头1"},
{w: tableWidth, h: tableH, txtStr: "表头2"},
{w: tableWidth, h: tableH, txtStr: "表头3"},
{w: tableWidth, h: tableH, txtStr: "表头4"},
}...)
// 内容
myPdf.addLine("", "CM", []pdfCell{
{w: tableWidth, h: tableH, txtStr: "内容1"},
{w: tableWidth, h: tableH, txtStr: "假设这里是很长很长的内容,你可以自己替换一下"},
{w: tableWidth, h: tableH, txtStr: "内容3"},
{w: tableWidth, h: tableH, txtStr: "内容4"},
}...)
}
- 创建页面、指定字体
go
// 添加页面
pdf.AddPage()
// 加载字体
pdf.AddUTF8Font("NotoSansSC-Regular", "", "src/font/NotoSansSC-Regular.ttf")
// 设置字体
pdf.SetFont("NotoSansSC-Regular", "", 12)
加载字体时,会将前面New方法指定的目录和AddUTF8Font方法指定的目录文件拼在一起。
- 其他常用写入方法
go
// 简单单元格,接收参数为 1.单元格长度 2.行高 3.文本
pdf.Cell(cellWeight, h, "my text")
// 自动换行的单元格,调用这个方法之左边会回到下一行的开头
pdf.MultiCell(0, h, "假设这是一个很长的单元格")
// 设置填充颜色
pdf.SetFillColor(233, 233, 233)
// 指定格式的单元格 参数 1.单元格长度 2.行高 3.文本 4.边框形式(1全边框、或者LTRB分别代表左上右下) 5.单元格
// 写入之后的坐标(1为下一行开头,2当前坐标的下一行) 6.对其方式(LCR为水平的左、中、右,TMBA为垂直的上、中、
// 下、基准线) 7.是否填充当前格子 8.连接 9.连接url
pdf.CellFormat(tableWidth, tableH, "总成本", "1", 0, "M", true, 0, "")
// 插入图片 参数分别为 1图片位置 2x坐标 3y坐标 4图片宽度 5图片高度
pdf.ImageOptions("src/font/logo.png", width-right-25, 5, 25, 0, false, opt, 0, "")