Размещение THotPDF на форме во время разработки отлично подходит для быстрого прототипа, но это привязывает компонент к времени жизни формы, что редко бывает нужно в рабочем коде. Генератор отчетов, который запускается один раз по нажатию кнопки, служебный поток, который выполняет пакетный ночной экспорт, вспомогательный класс, у которого вообще нет формы: в каждой из этих ситуаций вы хотите, чтобы компонент существовал ровно столько, сколько длится одно задание PDF, а затем исчезал. Это означает выделение во время выполнения, и это меняет две вещи, которые стоит понять перед написанием первой строки: кто владеет объектом и как выполняется очистка, когда что-то идет не так
Семантика владельца в VCL
Каждый конструктор компонента VCL принимает параметр Owner типа TComponent*. Передача this (формы) регистрирует новый объект в списке собственных компонентов формы, поэтому, если форма будет уничтожена, пока компонент еще жив, VCL освободит его автоматически. Передача nullptr означает отсутствие владельца: вы берете на себя исключительную ответственность за указатель, и ничто не очистит его за вас, если исключение раскрутит стек до вашего явного delete
Для одноразового экспорта, который завершается внутри одной функции, работает любой выбор, но у них разные режимы сбоя. При this в качестве владельца утечка невозможна, пока форма в конечном итоге закрывается; при nullptr указатель должен достичь блока __finally. На практике паттерн nullptr плюс __finally немного чище для короткоживущих объектов, потому что он делает границу времени жизни видимой с первого взгляда и избегает накопления формой принадлежащих объектов, которые должны были быть временными
Безопасная для исключений структура
Генерация PDF может завершиться неудачно по причинам, не имеющим ничего общего с API: выходной каталог доступен только для чтения, отсутствует файл шрифта, поток сбрасывается преждевременно, или предоставленные вызывающей стороной данные достигают предела длины. Какова бы ни была причина, путь очистки должен быть выполнен. Идиоматический способ C++Builder гарантировать это — 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;
}
}
Некоторые вещи в этом листинге стоят упоминания. Владельцем является nullptr, что делает время жизни явным. Compression и FontEmbedding устанавливаются до BeginDoc: оба являются параметрами уровня документа, которые HotPDF фиксирует при открытии документа, и их последующее назначение не имеет эффекта. TextOut принимает координаты в пунктах, измеряемые от нижнего левого угла страницы, Y увеличивается вверх; пара 72, 720 помещает текст около верхнего левого угла страницы формата Letter с дюймовым левым полем. delete Pdf в блоке __finally выполняется независимо от того, вызвали ли исключение BeginDoc, рисование или EndDoc
Избегайте вызова любого метода у Pdf после delete. Если указатель хранится в переменной-члене, установите его в nullptr сразу после удаления, чтобы любой случайный последующий доступ приводил к чистому сбою, а не к скрытому повреждению данных
Конфигурация проекта
C++Builder находит THotPDF через комбинацию путей включения, путей к библиотекам и директивы pragma. Сгенерированный заголовочный файл находится рядом с HPDFDoc.pas в каталоге исходных кодов HotPDF; добавьте этот каталог в Project > Options > C++ Compiler > Include path. Директива #pragma link "HPDFDoc" указывает компоновщику подтянуть скомпилированный модуль без добавления его в файл проекта вручную. Если вы используете пакет времени выполнения вместо статической линковки, сначала установите design-time и runtime пакеты HotPDF; pragma по-прежнему применяется
Оставьте имя модуля HPDFDoc без изменений. C++Builder получает имя заголовка из имени модуля Pascal, поэтому переименование файла или использование псевдонима пути в pragma незаметно ломает поиск
Область видимости и многодокументные задания
Для одиночного экспорта, запускаемого действием пользователя, правильным ответом будет локальная переменная, ограниченная обработчиком кнопки: она создается, используется и уничтожается в пределах одного кадра вызова, и намерение очевидно для любого, кто позже будет читать код. Альтернатива времени разработки оправдана, когда одна и та же форма управляет непрерывным рабочим процессом, например панелью предварительного просмотра печати, которая перестраивает документ всякий раз, когда пользователь изменяет настройку; в этом случае сохранение активности компонента и повторные вызовы BeginDoc/EndDoc менее разрушительны, чем многократное выделение и освобождение объектов в куче
Для пакетных заданий, которые производят много документов последовательно, ограничение области видимости одного THotPDF на документ стоит затрат на выделение памяти. Состояние не переносится между документами, если нет объекта, который его переносит, и это один класс периодических ошибок, которые вам никогда не придется отлаживать. Выделить, сгенерировать, удалить, повторить
Одно свойство, которое появляется в нескольких демо-версиях HotPDF — это AutoLaunch, которое открывает сгенерированный файл в системном средстве просмотра PDF сразу после EndDoc. Это полезно при написании первого черновика макета. В рабочем коде пропустите его: откройте выходной путь явно, проверьте, существует ли файл и имеет ли он ненулевой размер, запишите результат в журнал и позвольте вызывающему рабочему процессу решить, уместна ли программа просмотра. В пакетном задании AutoLaunch запускает одно окно просмотра на документ и заблокирует процесс в некоторых системах в ожидании закрытия программы просмотра
Компонент THotPDF и все показанные здесь вызовы отрисовки являются частью компонента HotPDF для Delphi и C++Builder