Como escalo uma imagem proporcionalmente para caber em uma coluna?
Passe os bytes para c.Image. gpdf já escala mantendo a proporção pela largura da coluna. Use FitWidth ou FitHeight quando precisar de um limite explícito.
A pergunta, em outras palavras
Tenho um logo, um gráfico ou um screenshot — digamos um PNG 1200×800 — e quero colocar dentro de uma das colunas do gpdf. Não quero fazer a conta da proporção na mão. Não quero que a imagem fique ovalada. Não quero que estoure para a coluna ao lado. Reduzir mantendo a proporção, e pronto.
TL;DR
c.Image(imgBytes)
No caso mais comum, essa é a receita inteira. c.Image usa FitContain por padrão, que reduz a imagem até a largura da coluna preservando a proporção original. Se a imagem já é menor que a coluna, o gpdf desenha no tamanho natural.
Precisa de um limite menor que a coluna inteira? Acrescente template.FitWidth ou template.FitHeight:
c.Image(imgBytes, template.FitWidth(document.Mm(40)))
c.Image(imgBytes, template.FitHeight(document.Mm(20)))
As duas opções preservam a proporção do original. Você especifica só uma dimensão; o gpdf calcula a outra.
Exemplo completo
package main
import (
"log"
"os"
"github.com/gpdf-dev/gpdf"
"github.com/gpdf-dev/gpdf/document"
"github.com/gpdf-dev/gpdf/template"
)
func main() {
logo, err := os.ReadFile("logo.png")
if err != nil {
log.Fatal(err)
}
chart, err := os.ReadFile("chart.png")
if err != nil {
log.Fatal(err)
}
doc := gpdf.NewDocument(
gpdf.WithPageSize(gpdf.A4),
gpdf.WithMargins(document.UniformEdges(document.Mm(20))),
)
page := doc.AddPage()
page.AutoRow(func(r *template.RowBuilder) {
// Coluna estreita para o logo, fixada em 30mm
r.Col(3, func(c *template.ColBuilder) {
c.Image(logo, template.FitWidth(document.Mm(30)))
})
// Coluna larga para o gráfico, fit padrão usa toda a largura
r.Col(9, func(c *template.ColBuilder) {
c.Image(chart)
})
})
data, err := doc.Generate()
if err != nil {
log.Fatal(err)
}
if err := os.WriteFile("report.pdf", data, 0o644); err != nil {
log.Fatal(err)
}
}
A célula do logo (3 colunas) usa FitWidth(30mm) porque queremos um logo pequeno e consistente independente do espaço da coluna. A do gráfico (9 colunas) leva um c.Image(chart) puro para usar toda a largura disponível. As duas mantêm a proporção. Nenhuma precisa conhecer as dimensões em píxeis do arquivo de origem.
O que "proporcional" significa exatamente no gpdf
São 4 modos de fit; um é o padrão e cobre uns 90% dos casos reais:
| Modo | O que faz | Quando usar |
|---|---|---|
FitContain (padrão) | Reduz para caber dentro da caixa, preserva a proporção, pode deixar espaço sobrando | Logos, gráficos, screenshots — quase tudo |
FitCover | Escala até cobrir toda a caixa preservando a proporção, corta o que sobra | Banners hero, cortes quadrados de fotos de perfil |
FitStretch | Escala até preencher exatamente a caixa, distorce a proporção | Quase nunca — quando você usa, normalmente é bug |
FitOriginal | Renderiza no tamanho original em píxeis convertido a 72 DPI | Diagramas feitos em resolução de impressão que não devem ser reamostrados |
FitWidth e FitHeight fixam uma dimensão e usam FitContain para a outra. São o atalho ergonômico para "me importa a largura" ou "me importa a altura" — quase nunca é necessário chamar WithFitMode direto.
A pegadinha clássica
O erro mais comum é informar width e height ao mesmo tempo, com valores que não batem com a proporção do original, e depois reclamar que a imagem ficou amassada. Tipo isto:
// Não faça isso a menos que seja realmente intencional
c.Image(img,
template.FitWidth(document.Mm(40)),
template.FitHeight(document.Mm(40)),
)
Se o PNG é 1200×800, forçar dentro de uma caixa 40×40 obriga a sacrificar uma de duas coisas: a proporção (comportamento FitStretch) ou parte da imagem (comportamento FitCover). O fit padrão é FitContain, então o gpdf mantém a proporção e deixa uma das dimensões sem preencher — a imagem sai 40mm de largura e ~26mm de altura, com espaço vazio embaixo na caixa de 40mm.
A solução é escolher uma dimensão e confiar no cálculo. Se você realmente precisa de um corte quadrado de uma imagem não quadrada, o que quer é FitCover, não duas dimensões em conflito:
c.Image(img,
template.FitWidth(document.Mm(40)),
template.FitHeight(document.Mm(40)),
template.WithFitMode(document.FitCover),
)
Píxeis não mentem
O gpdf lê as dimensões intrínsecas em píxeis do header do PNG ou JPEG antes de qualquer decisão de escala. Então uma foto 4000×3000 jogada em uma coluna de 60mm não é "reduzida no original" — o gpdf incorpora os bytes inteiros e o leitor PDF faz a reamostragem na hora de renderizar. O PDF de saída tem o mesmo tamanho de arquivo independente do tamanho de exibição.
Se o tamanho do arquivo importa mais que a qualidade máxima de impressão, reduza a imagem você mesmo com algo como image/draw antes de passar para o gpdf. A biblioteca não vai jogar píxeis fora silenciosamente. Essa decisão é sua.
E se a coluna ficar estreita demais?
Se uma coluna acaba mais estreita do que o esperado na hora de renderizar — porque a página quebrou de forma inesperada, ou uma célula de tabela encolheu para caber no conteúdo — o FitContain padrão vai reduzir alegremente o seu logo até ficar do tamanho de um selo. Se isso incomoda, defina um piso:
c.Image(logo,
template.FitWidth(document.Mm(30)),
template.MinDisplayWidth(document.Mm(20)),
)
MinDisplayWidth diz ao motor de layout: se for preciso reduzir essa imagem abaixo de 20mm para caber, manda para a próxima página. A imagem sai legível ou não sai — nunca o pior dos dois mundos no meio.
Receitas relacionadas
- Como incorporo um PNG com transparência no gpdf? — mesmo ponto de entrada
c.Image, lado do canal alfa - Como funciona a grade de 12 colunas no gpdf? — em quantos mm "a largura da coluna" realmente se traduz
- Como defino larguras de coluna em uma tabela? — quando a caixa em volta da imagem é uma célula de tabela e não uma coluna de linha
Experimente o gpdf
gpdf é uma biblioteca Go para gerar PDFs. MIT, zero dependências externas, manuseio de imagens e fontes em Go puro.
go get github.com/gpdf-dev/gpdf