Largar um THotPDF num formulário em tempo de design é aceitável para um protótipo rápido, mas prende o componente ao tempo de vida do formulário, o que raramente é o que o código de produção pretende. Um gerador de relatórios que corre uma vez por clique num botão, uma thread de serviço que processa exportações noturnas em lote, uma classe auxiliar sem formulário nenhum: em cada uma dessas situações quer-se que o componente exista exatamente durante a duração de uma tarefa PDF e depois desapareça. Isso significa alocação em tempo de execução, e muda duas coisas que vale a pena compreender antes de escrever a primeira linha: quem é o dono do objeto, e como corre a limpeza quando algo corre mal
Semântica de Owner na VCL
Todos os construtores de componentes VCL recebem um parâmetro Owner do tipo TComponent*. Passar this (o formulário) regista o novo objeto na lista de componentes possuídos do formulário, pelo que, se o formulário for destruído enquanto o componente ainda está vivo, a VCL liberta-o automaticamente. Passar nullptr significa nenhum owner: assume-se a responsabilidade exclusiva pelo ponteiro, e nada o limpará caso uma exceção desenrole a pilha antes do delete explícito
Para uma exportação única que se conclui dentro de uma só função, qualquer uma das opções funciona, mas as duas têm modos de falha diferentes. Com this como owner, uma fuga é impossível desde que o formulário acabe por fechar; com nullptr, o ponteiro tem de chegar a um bloco __finally. Na prática, o padrão nullptr mais __finally é ligeiramente mais limpo para objetos de curta duração, porque torna a fronteira do tempo de vida visível de imediato e evita que o formulário acumule objetos possuídos que eram suposto ser temporários
Estrutura segura em relação a exceções
A geração de PDF pode falhar por razões que nada têm a ver com a API: a pasta de saída é só de leitura, falta um ficheiro de tipo de letra, um stream é descarregado prematuramente, ou os dados fornecidos pelo chamador atingem um limite de comprimento. Seja qual for a causa, o caminho de limpeza tem de correr. A forma idiomática do C++Builder para garantir isso é try/__finally:
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma link "HPDFDoc"
#pragma resource "*.dfm"
TForm1 *Form1;
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __fastcall TForm1::Button1Click(TObject *Sender)
{
THotPDF* Pdf = new THotPDF(nullptr);
try
{
Pdf->FileName = "output.pdf";
Pdf->Compression = cmFlateDecode;
Pdf->FontEmbedding = true;
Pdf->BeginDoc();
Pdf->CurrentPage->SetFont("Arial", TFontStyles(), 12);
Pdf->CurrentPage->TextOut(72, 720, 0, L"Hello from C++Builder");
Pdf->EndDoc();
}
__finally
{
delete Pdf;
}
}
Vale a pena destacar algumas coisas nessa listagem. O owner é nullptr, tornando o tempo de vida explícito. Compression e FontEmbedding são definidos antes de BeginDoc: ambas são opções ao nível do documento que o HotPDF confirma quando o documento abre, e atribuí-las depois não tem qualquer efeito. TextOut recebe coordenadas em pontos medidos a partir do canto inferior esquerdo da página, com Y a crescer para cima; o par 72, 720 coloca o texto perto do canto superior esquerdo de uma página de tamanho carta, com uma margem esquerda de uma polegada. O delete Pdf no bloco __finally corre quer BeginDoc, o desenho, ou EndDoc tenham lançado uma exceção, quer não
Evite chamar qualquer método sobre Pdf depois do delete. Se o ponteiro estiver guardado numa variável membro, defina-o como nullptr imediatamente após a eliminação, para que qualquer acesso posterior acidental produza um crash limpo em vez de corrupção silenciosa
Configuração do projeto
O C++Builder localiza THotPDF através de uma combinação de caminhos de include, caminhos de biblioteca e uma diretiva pragma. O header gerado fica junto de HPDFDoc.pas na pasta de código-fonte do HotPDF; adicione essa pasta em Project > Options > C++ Compiler > Include path. A diretiva #pragma link "HPDFDoc" diz ao linker para incluir a unit compilada sem a listar manualmente no ficheiro do projeto. Se estiver a usar o pacote em tempo de execução em vez de linkagem estática, instale primeiro os pacotes de design e de tempo de execução do HotPDF; a pragma continua a aplicar-se
Mantenha o nome da unit HPDFDoc inalterado. O C++Builder deriva o nome do header a partir do nome da unit Pascal, pelo que renomear o ficheiro ou usar um alias de caminho na pragma quebra a pesquisa silenciosamente
Âmbito e tarefas com múltiplos documentos
Para uma exportação única despoletada por uma ação do utilizador, uma variável local com âmbito ao manipulador do botão é a resposta certa: é criada, usada e destruída dentro de uma única frame de chamada, e a intenção é óbvia para quem ler o código mais tarde. A alternativa em tempo de design justifica-se quando o mesmo formulário conduz um fluxo de trabalho contínuo, como um painel de pré-visualização de impressão que reconstrói o documento sempre que o utilizador altera uma definição; nesse caso, manter o componente vivo e chamar BeginDoc/EndDoc repetidamente é menos disruptivo do que alocar e libertar repetidamente objetos na heap
Para tarefas em lote que produzem muitos documentos em sequência, dar âmbito a um THotPDF por documento compensa o overhead de alocação. O estado não transita entre documentos se não houver objeto para o transportar, e essa é uma classe de bug intermitente que nunca é preciso depurar. Alocar, gerar, eliminar, repetir
Uma propriedade que aparece em várias demos do HotPDF é AutoLaunch, que abre o ficheiro gerado no visualizador de PDF do sistema imediatamente após EndDoc. É útil enquanto se escreve o primeiro rascunho de um layout. Em produção, evite-a: abra explicitamente o caminho de saída, verifique se o ficheiro existe e tem um tamanho diferente de zero, registe o resultado e deixe que o fluxo de trabalho chamador decida se um visualizador é relevante. Numa tarefa em lote, AutoLaunch lança uma janela de visualizador por documento e pode bloquear o processo em alguns sistemas à espera que o visualizador feche
O componente THotPDF e todas as chamadas de desenho aqui mostradas fazem parte do HotPDF Delphi Component para Delphi e C++Builder