gpdf 테이블에서 열 너비를 지정하는 방법
c.Table 에 template.ColumnWidths(...) 를 전달한다. 값은 부모 Col 너비에 대한 백분율. 합계 100 으로 전체 폭, 끝부분을 생략하면 남은 공간이 자동 분배된다.
질문을 다시 표현하면
4 열 테이블이 있다. 기본 c.Table(header, rows) 는 모든 열을 동일한 폭으로 만들지만, 송장이라면 품목 열은 넓고 수량 열은 좁아야 한다. 열별 너비를 어떻게 지정하나? 그리고 그 값의 단위는 뭔가?
빠른 답
template.ColumnWidths(...) 를 TableOption 으로 전달한다:
c.Table(header, rows, template.ColumnWidths(40, 15, 20, 25))
값은 부모 Col 의 content 너비에 대한 백분율 이다. 포인트가 아니다. 합계가 100 일 필요는 없지만 보통 그래야 한다 — 부족하면 오른쪽에 빈 공간이 남고, 넘치면 셀에서 흘러나간다.
평범한 경우는 이게 전부다. 흥미로운 부분은 합계가 100 이 안 될 때, 열 개수보다 적은 값을 넘겼을 때, 그리고 "부모 너비" 가 실제로 가리키는 게 뭔가, 이 세 가지다.
동작하는 코드 (4 열 송장 테이블)
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,000", "₩1,200,000"},
{"현장 교육 (1 일)", "3", "₩800,000", "₩2,400,000"},
{"맞춤 템플릿 개발", "12", "₩95,000", "₩1,140,000"},
}
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 content 너비의 100%. ColumnWidths(40, 15, 20, 25) 합계가 100 이므로 가로 PDF 포인트가 빠짐없이 사용된다.
백분율은 "무엇" 의 백분율인가
전달한 숫자는 내부에서 document.Pct(w) 로 감싸지고, 테이블의 content 너비 에 대해 해석된다. 테이블의 content 너비는 배치된 grid Col 의 너비에서 테이블 자체의 margin / padding / border 를 뺀 값 (실무에선 그냥 Col 너비 — 테이블 장식은 기본적으로 없음).
그래서 r.Col(6, ...) (행의 절반) 안에서 ColumnWidths(50, 50) 을 쓰면 각 테이블 열은 행 너비의 25% 가 된다. 50% 가 아니다. 백분율은 테이블 로컬이지 페이지 기준이 아니다.
이게 레이아웃 변경 시 효과를 본다. 테이블을 전체 폭에서 좌우 두 개로 바꿔도 ColumnWidths 값을 고칠 필요가 없다 — 그대로 스케일된다.
수가 안 맞을 때 gpdf 가 하는 일
실무에서 자주 마주치는 케이스 두 가지. 레이아웃 엔진의 동작이 명확하게 정해져 있으니 알아둘 가치가 있다.
케이스 1: 백분율 합계가 100 이 안 된다. 각 값은 그대로 사용된다. 3 열 테이블에서 ColumnWidths(40, 30, 20) 이면 열은 40% / 30% / 20% 가 되고 오른쪽에 10% 의 공백이 남는다. ColumnWidths(50, 50, 50) 은 overflow — 세 번째 열이 부모 오른쪽 끝을 넘어 옆 열에 침범하거나 페이지 밖으로 나간다.
정규화는 일어나지 않는다. 계산은 작성자 책임.
케이스 2: 열 개수보다 값이 적다. 이쪽이 더 흥미롭다. 명시값 없는 끝쪽 열들은 auto-width 가 되고, 남은 폭을 균등하게 나눠 갖는다:
// 3 열 테이블, 값은 두 개만.
c.Table(header3, rows3, template.ColumnWidths(40, 30))
// → 40% / 30% / 30% (세 번째 열은 100 - 40 - 30 = 30%)
명시값 합계가 이미 100 이상이면 auto 열은 너비 0 이 되어 사실상 사라진다. 100 미만이면 잔여분을 auto 열 수만큼 균등 분할한다:
// 5 열 테이블, 값은 두 개.
c.Table(header5, rows5, template.ColumnWidths(50, 10))
// → 50% / 10% / 13.33% / 13.33% / 13.33% (40% 를 3 등분)
이 규칙에는 유용한 트릭이 숨어 있다: 0 을 넘겨도 열이 auto 가 된다. 3 열 테이블에서 ColumnWidths(0, 30, 30) 으로 쓰면 뒤 두 열을 30% 씩 고정하고 첫 열에 남은 40% 를 줄 수 있다. "이 열들만 정확히 정하고 나머지는 맡긴다" 라고 쓰고 싶을 때 유용하다.
반대 방향: 값이 너무 많을 때
열 개수를 초과한 값은 조용히 무시된다. 2 열 테이블에 ColumnWidths(40, 30, 20, 10) 을 넘겨도 앞의 두 개만 사용된다. 관대하지만 버그의 온상이기도 하다 — 헤더에서 열을 하나 빼고 대응 너비 값을 같이 빼는 걸 잊어도, 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 열은 고정 3 열을 뺀 나머지를 가져간다. CSS 의 <col> 요소에 가깝지, 백분율 시스템과는 다르다.
c.Table(header, rows, ...) 가 해주던 셀 구성을 직접 써야 하지만, 종이 레터헤드에 인쇄해야 하고 열 위치가 정확히 고정되어야 하는 송장에는 그만한 가치가 있는 트레이드오프다.
관련 레시피
- gpdf 의 12 열 그리드는 어떻게 동작하나? — 행의 12 열이 테이블 백분율 계산의 기준 "부모 너비" 를 어떻게 정하는가
- Go 로 50 줄 이내에 송장 PDF 만들기 —
ColumnWidths(40, 15, 20, 25)를 송장 문서 전체의 맥락에서 보기
gpdf 사용해 보기
gpdf 는 PDF 생성을 위한 Go 라이브러리다. MIT 라이선스, 외부 의존성 0, CJK 네이티브 지원.
go get github.com/gpdf-dev/gpdf