Technical Article

Delphi HotPDF Hello World: O Seu Primeiro Programa PDF

Todas as bibliotecas de PDF incluem um "hello world", e vale a pena analisar com calma o do HotPDF, porque o programa mais pequeno que faz alguma coisa já força duas decisões que irá repetir em todos os documentos seguintes: qual o tipo de letra a definir e onde o texto fica posicionado na página. Acerte nestas duas decisões e o resto da API será apenas variações sobre o mesmo tema. Aqui está o programa completo, um programa de consola que escreve uma única página a saudar o leitor em quase uma dúzia de idiomas:

program HelloWorld;

{$APPTYPE CONSOLE}

uses
  Winapi.Windows,   // for DEFAULT_CHARSET; on pre-XE2 Delphi use plain Windows
  HPDFDoc;          // HotPDF main unit

procedure CreateHelloWorld(const FileName: string);
var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Compression := cmFlateDecode;   // shrink the content streams
    Pdf.FontEmbedding := True;          // embed the face so it renders anywhere
    Pdf.BeginDoc;

    // One face covering many scripts. The 4th argument is the Windows charset;
    // DEFAULT_CHARSET lets the system pick glyphs per string.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 14, DEFAULT_CHARSET);

    // TextOut measures from the BOTTOM-LEFT corner, in points, Y growing upward.
    Pdf.CurrentPage.TextOut(72, 760, 0, 'Hello, Delphi PDF world!');     // English
    Pdf.CurrentPage.TextOut(72, 730, 0, 'Hola, mundo Delphi PDF!');      // Spanish
    Pdf.CurrentPage.TextOut(72, 700, 0, 'Hallo, Delphi PDF Welt!');      // German
    Pdf.CurrentPage.TextOut(72, 670, 0, 'Bonjour, monde PDF Delphi!');   // French
    Pdf.CurrentPage.TextOut(72, 640, 0, 'Ciao, mondo Delphi PDF!');      // Italian
    Pdf.CurrentPage.TextOut(72, 610, 0, 'Olá, mundo Delphi PDF!');       // Portuguese
    Pdf.CurrentPage.TextOut(72, 580, 0, 'Здравствуйте, Delphi PDF мир!'); // Russian
    Pdf.CurrentPage.TextOut(72, 550, 0, 'こんにちは、Delphi PDFの世界!');    // Japanese
    Pdf.CurrentPage.TextOut(72, 520, 0, 'Merhaba, Delphi PDF dünyası!');  // Turkish
    Pdf.CurrentPage.TextOut(72, 490, 0, '你好,Delphi PDF世界');           // Chinese
    Pdf.CurrentPage.TextOut(72, 460, 0, '여보세요, Delphi PDF 세계!');      // Korean

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

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

Multilingual Hello World PDF produced by the HotPDF component in Delphi

Este é o exemplo completo. O THotPDF.Create(nil) emparelhado com try/finally ... Free é o padrão de propriedade comum no Delphi, e o PDF não o altera. O que merece atenção é a estrutura interna de quatro passos: definir propriedades, BeginDoc, desenhar, EndDoc. A ordem não é intercambiável.

Definir propriedades do documento antes de BeginDoc

O BeginDoc é o momento em que o HotPDF consolida a estrutura do documento, pelo que qualquer elemento que afete todo o ficheiro deve ser definido antes dele. A linha Compression := cmFlateDecode ativa a descodificação Flate para os fluxos de conteúdo, o que faz a diferença entre um ficheiro compacto e um desnecessariamente inflacionado. A linha FontEmbedding := True incorpora o tipo de letra com o qual desenha no ficheiro, para que este se apresente da mesma forma numa máquina onde o Arial Unicode MS nunca tenha sido instalado. Se atribuir qualquer uma destas propriedades após BeginDoc, esta não terá qualquer efeito no documento em curso, sem que nenhum erro o avise.

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

Duas coisas sobre as chamadas de desenho costumam surpreender toda a gente na sua primeira página. La primeira é a ordem: o método SetFont tem de ser executado antes de qualquer TextOut que dele dependa, e tem de ser repetido após cada AddPage, porque o tipo de letra atual não sobrevive a uma quebra de página. A segunda é o sistema de coordenadas. O método TextOut mede a partir do canto inferior esquerdo da página, e não do superior esquerdo, com o Y a crescer à medida que sobe, em pontos (1/72 de polegada). É por isso que as linhas aqui descem a partir de 760, cada uma 30 pontos abaixo da anterior. Quem vem de gráficos de ecrã assume o oposto e acaba por desenhar a primeira linha fora do limite inferior.

Um tipo de letra, muitos alfabetos

A razão pela qual um único SetFont consegue processar caracteres latinos, cirílicos, japoneses e chineses na mesma página é a própria família tipográfica: o Arial Unicode MS agrupa glifos para quase todos os alfabetos num único ficheiro. O quarto argumento de SetFont é o conjunto de caracteres do Windows, e passar DEFAULT_CHARSET permite ao sistema escolher glifos por string em vez de fixar tudo numa página de códigos antiga. Dois aspetos importantes acompanham esta conveniência. O tipo de letra deve estar presente na máquina que gera o PDF, pois o HotPDF incorpora o que o sistema operativo associar ao nome; se o Arial Unicode MS faltar, o Windows substitui-o silenciosamente e o seu texto CJK poderá aparecer como caixas vazias. Além disso, não cobre todos os alfabetos da mesma forma: para escrita da direita para a esquerda, como árabe e hebraico, ou qualquer escrita que exija modelação complexa, deve utilizar um tipo de letra dedicado carregado com RegisterUnicodeTTF e as chamadas de texto da direita para a esquerda, que é um tópico por si só.

Execute o programa e obterá o ficheiro HelloWorld.pdf no diretório de trabalho: uma página, algumas linhas de texto multilingue, comprimido e com o tipo de letra incorporado. Os passos seguintes óbvios são atribuir tamanho, estilo e rotação a esse texto, bem como posicionar deliberadamente mais do que um bloco de texto, aspetos abordados no exemplo de TextOut. Quando pretender desenhar linhas, caixas e imagens em vez de apenas texto, o mesmo objeto de página suporta também essas chamadas.

As chamadas de TextOut, SetFont e de documento aqui apresentadas fazem parte do Componente HotPDF para Delphi e C++Builder.