Artigo Técnico

Delphi HotPDF Hello World: O Seu Primeiro Programa em PDF

Toda a biblioteca PDF tem o seu "hello world", e o do HotPDF vale a pena analisar com calma, porque o programa mais pequeno que faz alguma coisa já obriga a duas decisões que se vão repetir em todos os documentos daí em diante: que tipo de letra definir e onde na página o texto cai. Acertando nessas duas, o resto da API é variações sobre o mesmo tema. Eis tudo, um programa de consola que escreve uma única página a saudar o leitor em quase uma dezena de idiomas:

Diagrama do fluxo do programa Hello World de HotPDF com quatro chamadas em Delphi, com a origem PDF no canto inferior esquerdo e duas armadilhas silenciosas destacadas
Criar, começar, desenhar, terminar é toda a espinha dorsal, e a origem no canto inferior esquerdo explica porque as linhas seguintes usam valores de Y menores
program HelloWorld;

{$APPTYPE CONSOLE}

uses
  Winapi.Windows,   // para DEFAULT_CHARSET; em Delphi anterior ao XE2 use Windows simples
  HPDFDoc;          // unit principal do HotPDF

procedure CreateHelloWorld(const FileName: string);
var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Compression := cmFlateDecode;   // reduz os content streams
    Pdf.FontEmbedding := True;          // incorpora o tipo de letra para renderizar em qualquer lugar
    Pdf.BeginDoc;

    // Um único tipo de letra a cobrir muitos scripts. O 4.º argumento é o charset do Windows;
    // DEFAULT_CHARSET deixa o sistema escolher glifos por string.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 14, DEFAULT_CHARSET);

    // TextOut mede a partir do canto INFERIOR ESQUERDO, em pontos, com Y a crescer para cima.
    Pdf.CurrentPage.TextOut(72, 760, 0, 'Hello, Delphi PDF world!');     // Inglês
    Pdf.CurrentPage.TextOut(72, 730, 0, 'Hola, mundo Delphi PDF!');      // Espanhol
    Pdf.CurrentPage.TextOut(72, 700, 0, 'Hallo, Delphi PDF Welt!');      // Alemão
    Pdf.CurrentPage.TextOut(72, 670, 0, 'Bonjour, monde PDF Delphi!');   // Francês
    Pdf.CurrentPage.TextOut(72, 640, 0, 'Ciao, mondo Delphi PDF!');      // Italiano
    Pdf.CurrentPage.TextOut(72, 610, 0, 'Olá, mundo Delphi PDF!');       // Português
    Pdf.CurrentPage.TextOut(72, 580, 0, 'Здравствуйте, Delphi PDF мир!'); // Russo
    Pdf.CurrentPage.TextOut(72, 550, 0, 'こんにちは、Delphi PDFの世界!');    // Japonês
    Pdf.CurrentPage.TextOut(72, 520, 0, 'Merhaba, Delphi PDF dünyası!');  // Turco
    Pdf.CurrentPage.TextOut(72, 490, 0, '你好,Delphi PDF世界');           // Chinês
    Pdf.CurrentPage.TextOut(72, 460, 0, '여보세요, Delphi PDF 세계!');      // Coreano

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

begin
  CreateHelloWorld('HelloWorld.pdf');
  Writeln('Wrote HelloWorld.pdf');
end.

Hello World multi-idioma num PDF construído via componente HotPDF em Delphi

Isto é o exemplo completo. O THotPDF.Create(nil) emparelhado com try/finally ... Free é o padrão de propriedade (ownership) habitual do Delphi, e o PDF não o altera. O que merece atenção é a espinha dorsal de quatro passos lá dentro: definir propriedades, BeginDoc, desenhar, EndDoc. A ordem não é permutável

Definir as propriedades do documento antes de BeginDoc

BeginDoc é o momento em que o HotPDF fixa a estrutura do documento, pelo que tudo o que afeta o ficheiro inteiro tem de ser definido antes disso. Compression := cmFlateDecode ativa o FlateDecode para os content streams, o que faz a diferença entre um ficheiro compacto e um desnecessariamente inchado. FontEmbedding := True embala o tipo de letra usado no desenho dentro do ficheiro, para que tenha o mesmo aspeto numa máquina que nunca teve o Arial Unicode MS instalado como na sua. Atribuir qualquer um deles depois de BeginDoc não faz nada, silenciosamente, ao documento já em curso, sem qualquer erro a avisar

SetFont, depois TextOut, e atenção à origem

Há duas coisas nas chamadas de desenho que apanham toda a gente na primeira página. A primeira é a ordem: SetFont tem de correr antes de qualquer TextOut que dependa dele, e tem de ser repetido depois de cada AddPage, porque o tipo de letra atual não sobrevive a uma quebra de página. A segunda é o sistema de coordenadas. TextOut mede a partir do canto inferior esquerdo da página, não do canto superior esquerdo, com Y a aumentar à medida que se sobe, em pontos (1/72 de polegada). É por isso que as linhas aqui avançam para baixo a partir de 760, cada uma 30 pontos abaixo da anterior. Quem chega da área de gráficos de ecrã assume o oposto e escreve a primeira linha diretamente para fora da margem inferior

Um tipo de letra, muitos scripts

A razão pela qual um único SetFont consegue transportar latim, cirílico, japonês e chinês na mesma página é o próprio tipo de letra: o Arial Unicode MS reúne glifos para quase todos os scripts num único ficheiro. O quarto argumento de SetFont é o conjunto de carateres do Windows, e passar DEFAULT_CHARSET deixa o sistema escolher glifos por string em vez de fixar tudo numa única code page legada. Duas ressalvas acompanham essa comodidade. O tipo de letra tem de estar presente na máquina que constrói o PDF, porque o HotPDF incorpora aquilo a que o SO resolver o nome; se o Arial Unicode MS estiver em falta, o Windows troca discretamente por um substituto e o texto CJK pode sair como caixas vazias. E não cobre todos os scripts por igual: para escrita da direita para a esquerda, como árabe e hebraico, ou qualquer coisa que precise de shaping complexo, recorre-se a um tipo de letra dedicado, carregado com RegisterUnicodeTTF e às chamadas de texto da direita para a esquerda, o que é um tema à parte

Execute-o e obtém-se HelloWorld.pdf na pasta de trabalho: uma página, algumas linhas de texto multilingue, comprimidas, com o tipo de letra incorporado. Os próximos passos óbvios são dar a esse texto tamanho, estilo e rotação, e colocar mais do que um bloco de texto de forma deliberada, ambos abordados no exemplo de TextOut. Quando se quer desenhar linhas, caixas e imagens em vez de apenas texto, o mesmo objeto de página também suporta essas chamadas

As chamadas TextOut, SetFont e de documento aqui mostradas fazem parte do HotPDF Delphi Component para Delphi e C++Builder