Texto, tipos de letra e coordenadas no relatório
Um relatório PDF em Delphi começa por três decisões simples: onde colocar o texto, que tipo de letra garante o mesmo aspeto no servidor e na máquina de desenvolvimento, e como inserir imagens sem aumentar inutilmente o ficheiro. O HotPDF expõe estas operações diretamente no objeto de página, mas obriga a pensar no sistema de coordenadas do PDF, cuja origem fica no canto inferior esquerdo
A chamada TextOut recebe X, Y, ângulo e texto. Para quem vem do canvas VCL, o ponto que causa erros é o eixo Y crescer para cima. Uma regra prática é calcular Y como altura da página menos a distância pretendida ao topo. Assim o modelo mental continua próximo do desenho em ecrã, mas o resultado obedece ao espaço do PDF
Estado da página e seleção de tipos de letra
Depois de AddPage, CurrentPage passa a apontar para a nova página e o estado de texto deve ser reposto. Reemitir SetFont no início de cada página é uma disciplina simples que evita cabeçalhos com o tipo errado, linhas de detalhe no tamanho anterior ou páginas seguintes desenhadas com valores herdados por acidente
Os problemas de tipos de letra quase nunca são problemas de desenho, mas de instalação. Se a fonte corporativa existe apenas no computador do programador, a execução em serviço pode substituí-la sem aviso. Registar um ficheiro TTF com RegisterUnicodeTTF e distribuí-lo com a aplicação torna o resultado previsível. Texto Unicode deve entrar como WideString, mantendo nomes, acentos e símbolos no mesmo fluxo que as legendas ASCII
Imagens e reutilização de recursos
A inserção de imagens tem duas fases. AddImage embebe os pixels uma vez e devolve um índice. ShowImage posiciona esse índice na página com X, Y, largura, altura e rotação. A largura e a altura não são o canto oposto da caixa, e confundir esta assinatura é uma das causas clássicas de logótipos esticados ou deslocados
Registar uma imagem dentro do ciclo de páginas repete o mesmo conteúdo em cada página e multiplica o tamanho do PDF. Registe cada recurso uma vez, guarde o índice numa tabela por caminho de ficheiro e reutilize esse índice. Para fotografias use JPEG com qualidade controlada; para logótipos, gráficos e arte plana, prefira compressão lossless para evitar artefactos
Vetores, linhas e paginação
Linhas horizontais, caixas de totais e fundos de secção devem ser desenhados como vetores. MoveTo, LineTo, Rectangle, Stroke e Fill produzem conteúdo nítido em qualquer zoom e quase não aumentam o tamanho do ficheiro. A ordem continua importante: definir o estado de pintura, construir o caminho e só depois chamar Stroke ou Fill
A paginação deve ser testada com dados reais, não apenas com dez linhas curtas de demonstração. Mantenha um cursor Y, subtraia a altura de cada linha e crie nova página antes de atravessar a margem inferior. A rotina que cria a página também deve repor o tipo de letra, redesenhar o cabeçalho e preservar as escolhas de layout que tornam o relatório consistente
Verificação antes de publicar
Antes de entregar o gerador, abra o PDF em mais do que um leitor, imprima uma página com linhas finas, confirme a incorporação das fontes e verifique o tamanho final com imagens reais. Este teste simples encontra diferenças entre ambiente de desenvolvimento, serviço de produção e visualizadores externos antes de o cliente as encontrar
Exemplos de código adicionais
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice-0001.pdf';
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 792 - 50, 0, 'INVOICE'); // 50pt from top of Letter
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 792 - 70, 0, 'Date: 2026-06-11');
Pdf.CurrentPage.TextOut(300, 400, 45, 'COPY'); // rotated stamp
Pdf.AddPage; // CurrentPage now points here
Pdf.CurrentPage.SetFont('Arial', [], 10); // font state does not carry over
Pdf.CurrentPage.TextOut(50, 742, 0, 'Page 2 detail rows');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;Pdf.RegisterUnicodeTTF('C:\ProgramData\MyApp\Fonts\NotoSans.ttf');
Pdf.CurrentPage.SetFont('NotoSans', [], 12);
Pdf.CurrentPage.TextOut(50, 700, 0, WideString('Łódź - Ünïcode test ✓'));var
Png: TPngImage;
Logo: TBitmap;
LogoIdx: Integer;
begin
Png := TPngImage.Create;
Logo := TBitmap.Create;
try
Png.LoadFromFile('brand-logo.png');
Logo.Assign(Png); // decode PNG to a bitmap
LogoIdx := Pdf.AddImage(Logo, icFlate); // lossless for flat-color art
finally
Logo.Free;
Png.Free;
end;
// (Index, X, Y, Width, Height, Angle): not (X1, Y1, X2, Y2)
Pdf.CurrentPage.ShowImage(LogoIdx, 50, 700, 120, 40, 0);
end;// Horizontal rule under the table header
Pdf.CurrentPage.SetLineWidth(0.75);
Pdf.CurrentPage.MoveTo(50, 660);
Pdf.CurrentPage.LineTo(545, 660);
Pdf.CurrentPage.Stroke;
// Shaded totals box: X, Y, width, height
Pdf.CurrentPage.SetRGBFillColor(RGB(235, 235, 235));
Pdf.CurrentPage.Rectangle(395, 120, 150, 40);
Pdf.CurrentPage.Fill;