Todas as publicações

Como defino a largura das colunas em uma tabela do gpdf?

Passe template.ColumnWidths(...) para c.Table. Os valores são porcentagens da largura da Col pai. Some 100 para preencher tudo; valores finais ausentes são distribuídos automaticamente.

A pergunta, em outras palavras

Tenho uma tabela com quatro colunas. Por padrão, c.Table(header, rows) deixa todas com a mesma largura, mas em uma fatura a coluna Descrição deveria ser larga e Qtd. deveria ser estreita. Como digo ao gpdf as larguras por coluna, e qual unidade estou ajustando de fato?

A resposta rápida

Passe template.ColumnWidths(...) como TableOption:

c.Table(header, rows, template.ColumnWidths(40, 15, 20, 25))

Os valores são porcentagens da largura de conteúdo da coluna pai, não pontos. Não precisam somar 100, mas geralmente devem — o que faltar deixa espaço em branco à direita, o que sobrar transborda da célula.

Esse é o caso comum. As partes interessantes são o que acontece quando as porcentagens não somam 100, quando você passa menos valores do que colunas, e o que a "largura pai" é, exatamente.

Código que funciona (tabela de fatura com quatro colunas)

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{"Descrição", "Qtd.", "Preço unit.", "Total"}
    rows := [][]string{
        {"Contrato anual de suporte", "1", "R$ 1.200,00", "R$ 1.200,00"},
        {"Treinamento presencial (por dia)", "3", "R$ 800,00", "R$ 2.400,00"},
        {"Desenvolvimento de template sob medida", "12", "R$ 95,00", "R$ 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, ...) ocupa toda a largura da linha. A tabela vive dentro dessa coluna, então 100% da tabela = 100% da área de conteúdo da grid Col. Com ColumnWidths(40, 15, 20, 25) somando 100, cada ponto PDF horizontal é usado.

A porcentagem é porcentagem de quê

O número que você passa é embrulhado em document.Pct(w) e resolvido contra a largura de conteúdo da tabela. Isso é a largura da grid Col em que a tabela vive, menos qualquer margem, padding e borda da própria tabela (na prática, a largura da Col mesma — o estilo da tabela vem sem decoração por padrão).

Então, com r.Col(6, ...) (metade da linha) e ColumnWidths(50, 50), cada coluna da tabela é 25% da largura da linha, não 50%. As porcentagens são locais à tabela, não à página.

Isso importa quando você troca uma tabela de uma linha de largura total para um layout lado a lado. A chamada de ColumnWidths não precisa mudar — ela escala sozinha.

O que o gpdf faz quando a conta não fecha

Dois casos aparecem o tempo todo. A camada de layout lida com eles de uma forma específica que vale a pena conhecer.

Caso 1: as porcentagens não somam 100. Cada valor é usado como está. ColumnWidths(40, 30, 20) em uma tabela de três colunas produz colunas em 40%, 30% e 20% da pai — deixando 10% vazio à direita. ColumnWidths(50, 50, 50) extravasa; a terceira coluna passa da borda direita da pai e pode invadir a coluna vizinha ou sair da página.

Não há um passo de normalização. gpdf confia em você para fazer a aritmética.

Caso 2: menos larguras do que colunas. Esse é o mais interessante. As colunas finais viram auto-width e dividem o resto igualmente:

// Tabela de três colunas, só duas larguras dadas.
c.Table(header3, rows3, template.ColumnWidths(40, 30))
// → 40% / 30% / 30%   (a terceira coluna recebe 100 - 40 - 30 = 30%)

Se as larguras explícitas já somam 100 ou mais, as colunas auto recebem largura zero e somem efetivamente. Se somam menos de 100, o que sobra é dividido igualmente entre as colunas auto:

// Tabela de cinco colunas, duas larguras dadas.
c.Table(header5, rows5, template.ColumnWidths(50, 10))
// → 50% / 10% / 13,33% / 13,33% / 13,33%   (40% divididos entre três)

Há um truque útil escondido nessa regra: passar 0 também transforma uma coluna em auto. Assim, ColumnWidths(0, 30, 30) em uma tabela de três colunas fixa as duas últimas em 30% cada e entrega os 40% restantes à primeira coluna. É como dizer "eu me importo com estas larguras específicas; o resto, gpdf que decida".

A direção oposta: largura demais

Valores extras além da contagem de colunas são ignorados silenciosamente. ColumnWidths(40, 30, 20, 10) em uma tabela de duas colunas usa só os dois primeiros. É tolerante mas também um ímã de bugs — se você apaga uma coluna do cabeçalho e esquece de remover a largura correspondente, gpdf não avisa. Sem log de aviso.

A contagem de colunas em si vem do tamanho da linha de cabeçalho (ou da primeira linha de body, se não houver cabeçalho). Adicione uma célula de cabeçalho e você adicionou uma coluna; gpdf vai descobrir e rebalancear qualquer ColumnWidths que você tenha passado.

Quando porcentagens não são o que você quer

A API do builder só expõe porcentagens. Se você realmente precisa de uma coluna de largura fixa — digamos, uma coluna "Qtd." de 50 pt que não escale com a página — tem que descer uma camada para a árvore do documento:

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:   /* ... */,
}

Misture e combine: Auto, Pt, Mm, Pct funcionam na camada document. A primeira coluna com Auto recebe o que sobrar depois que as três colunas fixas forem subtraídas. Isso é mais próximo do elemento <col> do CSS do que de um sistema de porcentagens.

Você abre mão da conveniência de c.Table(header, rows, ...) montar as células para você, mas para faturas que precisam ser impressas em papel timbrado físico com posições de coluna estáveis, a troca compensa.

Receitas relacionadas

Experimente o gpdf

gpdf é uma biblioteca Go para gerar PDFs. Licença MIT, zero dependências externas, suporte CJK nativo.

go get github.com/gpdf-dev/gpdf

⭐ Star no GitHub · Leia a documentação