利用golang实现pdf中自动换行的表格

来自:网络
时间:2024-03-30
阅读:

需要用到的库:jung-kurt/gofpdf

由于CellFormat方法不支持\n换行,会被变成乱码,MultiCell方法会自动将坐标定位到下一行。所以需要自己实现坐标的计算变换。通过Rect方法画出单元格,MultiCell方法在格内自动换行写字,在计算坐标重复写单元格,最终组成一行。

参考ExampleFpdf_Rect()

实现自动换行的表格

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
	_, 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)
	}
	_, _, _, 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.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"},
		}...)
}

创建页面、指定字体

	// 添加页面
	pdf.AddPage()
	// 加载字体
	pdf.AddUTF8Font("NotoSansSC-Regular", "", "src/font/NotoSansSC-Regular.ttf")
	// 设置字体
	pdf.SetFont("NotoSansSC-Regular", "", 12)

加载字体时,会将前面New方法指定的目录和AddUTF8Font方法指定的目录文件拼在一起。

其他常用写入方法

	// 简单单元格,接收参数为 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, "")
返回顶部
顶部