如何在 gpdf 表格中设置列宽?
在 c.Table 中传入 template.ColumnWidths(...)。值是相对于父 Col 宽度的百分比。合计 100 占满全宽,未传完的尾部列会自动均分剩余空间。
换个说法的问题
我有一个四列的表格。默认 c.Table(header, rows) 会把所有列做成等宽,但发票里 品名 列应该宽,数量 列应该窄。要怎么告诉 gpdf 每列的宽度?我设置的到底是什么单位?
一句话回答
把 template.ColumnWidths(...) 作为 TableOption 传入:
c.Table(header, rows, template.ColumnWidths(40, 15, 20, 25))
值是 父 Col 内容宽度的百分比,不是磅。合计不必是 100,但通常应该是——少了右边会留白,多了会从单元格溢出。
常规情况就这样。有意思的是百分比合计不到 100、传入的值少于列数、以及"父宽度"到底指什么。
可运行的代码(四列发票表格)
package main
import (
"log"
"os"
"github.com/gpdf-dev/gpdf"
"github.com/gpdf-dev/gpdf/document"
"github.com/gpdf-dev/gpdf/pdf"
"github.com/gpdf-dev/gpdf/template"
)
func main() {
doc := gpdf.NewDocument(
gpdf.WithPageSize(gpdf.A4),
gpdf.WithMargins(document.UniformEdges(document.Mm(20))),
)
header := []string{"品名", "数量", "单价", "金额"}
rows := [][]string{
{"年度支持合同", "1", "¥1,200.00", "¥1,200.00"},
{"现场培训(每天)", "3", "¥800.00", "¥2,400.00"},
{"自定义模板开发", "12", "¥95.00", "¥1,140.00"},
}
page := doc.AddPage()
page.AutoRow(func(r *template.RowBuilder) {
r.Col(12, func(c *template.ColBuilder) {
c.Table(header, rows,
template.ColumnWidths(40, 15, 20, 25),
template.TableHeaderStyle(template.BgColor(pdf.Gray(0.92))),
)
})
})
data, err := doc.Generate()
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile("invoice.pdf", data, 0o644); err != nil {
log.Fatal(err)
}
}
r.Col(12, ...) 占满整行宽度。表格放在这个 Col 里,所以表格的 100% = grid Col 内容区的 100%。ColumnWidths(40, 15, 20, 25) 合计为 100,每一磅水平空间都被用满。
百分比是相对于"什么"的百分比
传入的数值会被包装成 document.Pct(w),对照 表格的内容宽度 来解析。表格内容宽度 = 所在 grid Col 的宽度 - 表格自身的 margin / padding / border(实际就是 Col 宽度——表格默认没有装饰)。
所以如果用 r.Col(6, ...)(半行)配 ColumnWidths(50, 50),每列实际是 行宽的 25%,不是 50%。百分比是相对于表格的,不是相对于页面的。
布局变更时这点很有用。把表格从全宽改成两个并排,ColumnWidths 不用改——会自动缩放。
数学不对劲时 gpdf 会怎么做
实际写代码时常见两种情况,布局引擎对它们的处理是确定的,值得了解。
情况 1:百分比合计不到 100。 每个值按字面使用。三列表格用 ColumnWidths(40, 30, 20) 得到 40% / 30% / 20%,右边留白 10%。ColumnWidths(50, 50, 50) 会溢出——第三列伸出父容器右边,可能挤进相邻列或跑出页面。
不会做归一化。算术由你负责。
情况 2:传入的值少于列数。 这一种更有意思。没有显式值的尾部列会变成 auto-width,平分剩余宽度:
// 三列表格,只给两个宽度。
c.Table(header3, rows3, template.ColumnWidths(40, 30))
// → 40% / 30% / 30% (第三列拿到 100 - 40 - 30 = 30%)
如果显式值合计已经达到 100 或更多,auto 列会得到 0 宽,等于消失。如果合计不到 100,剩余按 auto 列的数量均分:
// 五列表格,给两个宽度。
c.Table(header5, rows5, template.ColumnWidths(50, 10))
// → 50% / 10% / 13.33% / 13.33% / 13.33% (40% 三等分)
这条规则里藏着一个小技巧:传 0 也会让列变成 auto。三列表格写 ColumnWidths(0, 30, 30) 把后两列固定为 30%,第一列拿剩下的 40%。当你想说"这几列我要精确控制,其余交给你"时很合用。
反方向:值多于列数
超出列数的值会被默默忽略。两列表格传 ColumnWidths(40, 30, 20, 10),只用前两个。这种宽容也是 bug 温床——你从表头删掉一列但忘了删对应的宽度值,gpdf 不会警告,也不打日志。
列数本身从表头行的长度推断(没有表头则用第一条 body 行)。加一个表头单元格就多一列,gpdf 会发现并对你传的 ColumnWidths 重新分配。
如果不想用百分比
构建器 API 只暴露百分比。如果你确实需要固定宽度的列——比如不随页面变化的 50pt "数量"列——就要降到下一层(document 树):
import "github.com/gpdf-dev/gpdf/document"
tbl := &document.Table{
Columns: []document.TableColumn{
{Width: document.Auto},
{Width: document.Pt(50)},
{Width: document.Pt(80)},
{Width: document.Pt(80)},
},
Header: /* ... */,
Body: /* ... */,
}
可以混用:document 层的 Auto / Pt / Mm / Pct 都支持。第一列设为 Auto 时拿走三列固定宽度之后剩下的部分。这更接近 CSS 的 <col> 元素,而不是百分比体系。
代价是失去 c.Table(header, rows, ...) 自动构建单元格的便利,但对那种要在实体信纸抬头上打印、要求列位置稳定的发票,这个权衡值得。
相关菜谱
- gpdf 中的 12 列网格如何工作? ——行的 12 列如何成为表格百分比所基于的"父宽度"
- 50 行以内用 Go 生成发票 PDF ——
ColumnWidths(40, 15, 20, 25)在完整发票文档中的使用
试用 gpdf
gpdf 是用于生成 PDF 的 Go 库。MIT 许可,零外部依赖,原生支持 CJK。
go get github.com/gpdf-dev/gpdf