Artículo técnico

Crear y liberar un componente HotPDF dinámicamente en C++Builder

Colocar un THotPDF en un formulario en tiempo de diseño está bien para un prototipo rápido, pero vincula el componente al tiempo de vida del formulario, que rara vez es lo que el código de producción necesita. Un generador de reportes que se ejecuta una vez por clic de botón, un hilo de servicio que procesa exportaciones nocturnas por lotes, una clase de ayuda que no tiene formulario en absoluto: en cada una de esas situaciones usted desea que el componente exista exactamente durante la duración de un trabajo PDF y luego desaparezca. Eso significa asignación en tiempo de ejecución, y cambia dos cosas que vale la pena entender antes de escribir la primera línea: quién posee el objeto y cómo se ejecuta la limpieza cuando algo sale mal

Semántica de Owner en VCL

Todo constructor de componentes VCL toma un parámetro Owner de tipo TComponent*. Pasar this (el formulario) registra el nuevo objeto en la lista de componentes poseídos del formulario, de modo que si el formulario se destruye mientras el componente sigue activo, la VCL lo libera automáticamente. Pasar nullptr significa que no hay propietario: usted asume la responsabilidad exclusiva del puntero, y nada lo limpiará por usted si una excepción desenreda la pila antes de su delete explícito

Para una exportación de una sola vez que se completa dentro de una sola función, cualquiera de las dos opciones funciona, pero ambas tienen diferentes modos de falla. Con this como propietario, una fuga es imposible siempre que el formulario finalmente se cierre; con nullptr, el puntero debe alcanzar un bloque __finally. En la práctica, el patrón nullptr más __finally es un poco más limpio para objetos de corta duración porque hace que el límite de tiempo de vida sea visible de un vistazo y evita que el formulario acumule objetos poseídos que estaban destinados a ser temporales

Estructura segura contra excepciones

La generación de PDF puede fallar por razones que no tienen nada que ver con la API: el directorio de salida es de solo lectura, falta un archivo de fuente, un flujo se vacía prematuramente o los datos suministrados por el invocador alcanzan un límite de longitud. Cualquiera sea la causa, la ruta de limpieza tiene que ejecutarse. La forma idiomática de C++Builder para garantizar eso es 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 la pena destacar algunas cosas en ese listado. El propietario es nullptr, lo que hace explícito el tiempo de vida. Compression y FontEmbedding se establecen antes de BeginDoc: ambas son opciones a nivel de documento que HotPDF aplica cuando se abre el documento, y asignarlas después no tiene ningún efecto. TextOut toma las coordenadas en puntos medidas desde la esquina inferior izquierda de la página, con Y aumentando hacia arriba; el par 72, 720 coloca el texto cerca de la parte superior izquierda de una página tamaño carta con un margen izquierdo de una pulgada. El delete Pdf en el bloque __finally se ejecuta independientemente de si BeginDoc, el dibujo o EndDoc generaron una excepción o no

Evite llamar a cualquier método en Pdf después de delete. Si el puntero se almacena en una variable miembro, establézcalo en nullptr inmediatamente después de la eliminación para que cualquier acceso posterior accidental produzca un bloqueo limpio en lugar de una corrupción silenciosa

Configuración del proyecto

C++Builder localiza THotPDF a través de una combinación de rutas de inclusión, rutas de bibliotecas y una directiva pragma. El encabezado generado se encuentra junto a HPDFDoc.pas en el directorio fuente de HotPDF; agregue ese directorio a Project > Options > C++ Compiler > Include path. La directiva #pragma link "HPDFDoc" le indica al enlazador que extraiga la unidad compilada sin incluirla manualmente en el archivo del proyecto. Si usted está utilizando el paquete de tiempo de ejecución en lugar de la vinculación estática, instale primero los paquetes de diseño y tiempo de ejecución de HotPDF; el pragma sigue aplicándose

Mantenga el nombre de la unidad HPDFDoc sin cambios. C++Builder deriva el nombre del encabezado del nombre de la unidad Pascal, por lo que renombrar el archivo o usar un alias de ruta en el pragma rompe la búsqueda silenciosamente

Alcance y trabajos de múltiples documentos

Para una sola exportación desencadenada por la acción del usuario, una variable local con alcance al manejador del botón es la respuesta correcta: se crea, se usa y se destruye dentro de un marco de llamada, y la intención es obvia para cualquiera que lea el código más adelante. La alternativa en tiempo de diseño se justifica cuando el mismo formulario maneja un flujo de trabajo continuo, como un panel de vista previa de impresión que reconstruye el documento cada vez que el usuario cambia una configuración; en ese caso, mantener el componente activo y llamar a BeginDoc/EndDoc repetidamente es menos perjudicial que asignar y liberar repetidamente objetos en el montón

Para trabajos por lotes que producen muchos documentos en secuencia, asignar un THotPDF por documento vale la pena por el costo de asignación. El estado no se transfiere entre documentos si no hay ningún objeto que lo transfiera, y esa es una clase de error intermitente que usted nunca tendrá que depurar. Asigne, genere, elimine, repita

Una propiedad que aparece en varias demostraciones de HotPDF es AutoLaunch, que abre el archivo generado en el visor de PDF del sistema inmediatamente después de EndDoc. Es útil mientras se escribe el primer borrador de un diseño. En producción, omítalo: abra la ruta de salida explícitamente, verifique que el archivo exista y tenga un tamaño distinto de cero, registre el resultado y deje que el flujo de trabajo invocador decida si un visor es relevante. En un trabajo por lotes, AutoLaunch lanza una ventana del visor por documento y bloqueará el proceso en algunos sistemas esperando que el visor se cierre

El componente THotPDF y todas las llamadas de dibujo que se muestran aquí son parte del HotPDF Component para Delphi y C++Builder