Soltar um THotPDF em um formulário em tempo de design é bom para um protótipo rápido, mas vincula o componente ao tempo de vida do formulário, o que raramente é o que o código de produção deseja. Um gerador de relatórios que é executado uma vez por clique de botão, uma thread de serviço que agrupa exportações noturnas, uma classe auxiliar que não tem nenhum formulário: em cada uma dessas situações, você deseja que o componente exista exatamente pela duração de um trabalho PDF e depois desapareça. Isso significa alocação em tempo de execução e muda duas coisas que valem a pena entender antes de escrever a primeira linha: quem é o proprietário do objeto e como a limpeza é executada quando algo dá errado
Semântica de proprietário na VCL
Todo construtor de componente VCL recebe um parâmetro Owner do tipo TComponent*. Passar this (o formulário) registra o novo objeto na lista de componentes pertencentes ao formulário, portanto, se o formulário for destruído enquanto o componente ainda estiver vivo, a VCL o liberará automaticamente. Passar nullptr significa nenhum proprietário: você assume a responsabilidade exclusiva pelo ponteiro e nada o limpará para você se uma exceção desenrolar a pilha antes do seu delete explícito
Para uma exportação de disparo único que é concluída em uma única função, qualquer uma das opções funciona, mas as duas têm modos de falha diferentes. Com this como proprietário, um vazamento é impossível, desde que o formulário acabe fechando; com nullptr, o ponteiro deve atingir um bloco __finally. Na prática, o padrão nullptr mais __finally é um pouco mais limpo para objetos de vida curta porque torna o limite de tempo de vida visível rapidamente e evita que o formulário acumule objetos de sua propriedade que deveriam ser temporários
Estrutura segura contra exceções
A geração de PDF pode falhar por motivos que não têm nada a ver com a API: o diretório de saída é somente leitura, um arquivo de fonte está ausente, um fluxo é descarregado prematuramente ou os dados fornecidos pelo chamador atingem um limite de comprimento. Seja qual for a causa, o caminho de limpeza tem que ser executado. A maneira idiomática no C++Builder de 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;
}
}
Algumas coisas nessa listagem valem a pena destacar. O proprietário é nullptr, tornando o tempo de vida explícito. Compression e FontEmbedding são definidos antes de BeginDoc: ambas são opções no nível do documento que o HotPDF confirma quando o documento é aberto, e atribuí-las depois não tem efeito. TextOut usa coordenadas em pontos medidos a partir do canto inferior esquerdo da página, com Y aumentando para cima; o par 72, 720 coloca o texto próximo ao canto superior esquerdo de uma página tamanho carta com uma margem esquerda de uma polegada. O delete Pdf no bloco __finally é executado independentemente de BeginDoc, o desenho ou EndDoc gerarem uma exceção ou não
Evite chamar qualquer método em Pdf após o delete. Se o ponteiro estiver armazenado em uma variável de membro, defina-o como nullptr imediatamente após a exclusão para que qualquer acesso acidental posterior produza uma falha limpa em vez de corrupção silenciosa
Configuração do projeto
O C++Builder localiza o THotPDF por meio de uma combinação de caminhos de inclusão, caminhos de biblioteca e uma diretiva pragma. O cabeçalho gerado fica junto com HPDFDoc.pas no diretório de origem do HotPDF; adicione esse diretório a Project > Options > C++ Compiler > Include path. A diretiva #pragma link "HPDFDoc" diz ao vinculador para puxar a unidade compilada sem listá-la no arquivo do projeto manualmente. Se você estiver usando o pacote de tempo de execução em vez de vinculação estática, instale primeiro os pacotes de design e tempo de execução do HotPDF; o pragma ainda se aplica
Mantenha o nome da unidade HPDFDoc inalterado. O C++Builder deriva o nome do cabeçalho a partir do nome da unidade Pascal, portanto, renomear o arquivo ou usar um alias de caminho no pragma quebra a pesquisa silenciosamente
Escopo e trabalhos de vários documentos
Para uma única exportação acionada por ação do usuário, uma variável local com escopo definido para o manipulador do botão é a resposta certa: ela é criada, usada e destruída em um quadro de chamada, e a intenção é óbvia para quem ler o código mais tarde. A alternativa de tempo de design é justificada quando o mesmo formulário conduz um fluxo de trabalho contínuo, como um painel de visualização de impressão que reconstrói o documento sempre que o usuário altera uma configuração; nesse caso, manter o componente vivo e chamar BeginDoc/EndDoc repetidamente é menos perturbador do que alocar e liberar objetos de heap repetidamente
Para trabalhos em lote que produzem muitos documentos em sequência, definir o escopo de um THotPDF por documento compensa a sobrecarga de alocação. O estado não é transferido entre documentos se não houver objeto para carregá-lo, e essa é uma classe de bug intermitente que você nunca precisa depurar. Aloque, gere, exclua, repita
Uma propriedade que aparece em várias demonstrações do HotPDF é AutoLaunch, que abre o arquivo gerado no visualizador de PDF do sistema imediatamente após o EndDoc. Ela é útil ao escrever o primeiro rascunho de um layout. Na produção, ignore-a: abra o caminho de saída explicitamente, verifique se o arquivo existe e tem um tamanho diferente de zero, registre o resultado e deixe o fluxo de trabalho de chamada decidir se um visualizador é relevante. Em um trabalho em lote, o AutoLaunch inicia uma janela do visualizador por documento e bloqueará o processo em alguns sistemas aguardando o fechamento do visualizador
O componente THotPDF e todas as chamadas de desenho mostradas aqui fazem parte do Componente HotPDF para Delphi e C++Builder