Việc thả một THotPDF vào một biểu mẫu (form) tại thời gian thiết kế là ổn cho một nguyên mẫu nhanh chóng, nhưng nó liên kết thành phần với vòng đời của biểu mẫu, điều hiếm khi được mong muốn trong mã sản xuất (production code). Một trình tạo báo cáo (report generator) chạy một lần mỗi khi nhấp chuột, một luồng dịch vụ (service thread) thực hiện xuất hàng loạt qua đêm, một lớp trình trợ giúp (helper class) không hề có biểu mẫu nào: trong từng tình huống đó, bạn muốn thành phần này chỉ tồn tại trong đúng khoảng thời gian của một tác vụ PDF rồi biến mất. Điều đó có nghĩa là phân bổ thời gian chạy (runtime allocation), và nó thay đổi hai điều đáng để hiểu trước khi viết dòng đầu tiên: ai sở hữu đối tượng và quá trình dọn dẹp chạy như thế nào khi xảy ra sự cố
Ngữ nghĩa về quyền sở hữu (owner semantics) trong VCL
Mỗi hàm tạo thành phần VCL (VCL component constructor) nhận một tham số Owner thuộc kiểu TComponent*. Truyền this (biểu mẫu) sẽ đăng ký đối tượng mới vào danh sách thành phần được sở hữu (owned-component list) của biểu mẫu, do đó nếu biểu mẫu bị phá hủy trong khi thành phần vẫn đang tồn tại, VCL sẽ tự động giải phóng nó. Truyền nullptr có nghĩa là không có chủ sở hữu (no owner): bạn chịu trách nhiệm duy nhất về con trỏ và không có gì sẽ tự dọn dẹp nó cho bạn nếu một ngoại lệ (exception) tháo gỡ ngăn xếp (unwinds the stack) trước khi bạn dùng lệnh delete tường minh
Đối với thao tác xuất một lần (one-shot export) hoàn tất trong một hàm duy nhất, cả hai lựa chọn đều có tác dụng, nhưng cả hai đều có các chế độ lỗi khác nhau. Khi coi this là chủ sở hữu, việc rò rỉ là không thể xảy ra miễn là biểu mẫu cuối cùng vẫn đóng; khi coi nullptr là chủ sở hữu, con trỏ phải đạt đến một khối __finally. Trong thực tế, mẫu nullptr cộng __finally gọn gàng hơn đôi chút đối với các đối tượng có thời gian tồn tại ngắn vì nó giúp người ta có thể nhìn thấy ranh giới vòng đời ngay lập tức và tránh được tình trạng biểu mẫu tích tụ các đối tượng được sở hữu mà vốn dĩ chỉ mang tính tạm thời
Cấu trúc an toàn trước ngoại lệ (Exception-safe structure)
Quá trình tạo PDF có thể không thành công vì những lý do không liên quan gì đến API: thư mục đầu ra chỉ đọc, thiếu tệp phông chữ, luồng dữ liệu (stream) xả (flushes) sớm, hoặc dữ liệu do người gọi cung cấp đạt đến giới hạn độ dài. Dù nguyên nhân là gì, đường dẫn dọn dẹp vẫn phải chạy. Cách thành ngữ của C++Builder (idiomatic C++Builder) để đảm bảo điều đó là 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;
}
}
Có một số điểm trong danh sách (listing) đó đáng chú ý. Chủ sở hữu là nullptr, làm cho thời gian tồn tại trở nên rõ ràng. Compression và FontEmbedding được đặt trước BeginDoc: cả hai đều là các tùy chọn cấp tài liệu mà HotPDF cam kết khi tài liệu mở, và việc gán chúng sau đó không có tác dụng. TextOut nhận tọa độ bằng điểm (points) được đo từ góc dưới cùng bên trái của trang, trục Y tăng dần hướng lên trên; cặp 72, 720 đặt văn bản ở gần trên cùng bên trái của một trang kích thước letter có lề trái một inch. Lệnh delete Pdf trong khối __finally chạy bất kể quá trình BeginDoc, vẽ (drawing) hay EndDoc có gây ra ngoại lệ hay không
Tránh gọi bất kỳ phương thức nào trên Pdf sau lệnh delete. Nếu con trỏ được lưu trong một biến thành viên (member variable), hãy đặt nó thành nullptr ngay sau khi xóa để bất kỳ truy cập vô tình nào sau đó cũng tạo ra sự cố rõ ràng (clean crash) thay vì gây hư hỏng thầm lặng (silent corruption)
Cấu hình dự án
C++Builder định vị THotPDF thông qua việc kết hợp các đường dẫn bao gồm (include paths), đường dẫn thư viện (library paths), và chỉ thị pragma (pragma directive). Tệp tiêu đề (header) được tạo sẽ nằm cùng HPDFDoc.pas trong thư mục nguồn của HotPDF; hãy thêm thư mục đó vào Project > Options > C++ Compiler > Include path. Chỉ thị #pragma link "HPDFDoc" báo cho trình liên kết (linker) kéo khối mã đã được biên dịch vào mà không cần phải liệt kê khối mã đó trong tệp dự án theo cách thủ công. Nếu bạn đang sử dụng gói thời gian chạy (runtime package) thay vì liên kết tĩnh (static linking), trước tiên hãy cài đặt các gói thiết kế và thời gian chạy HotPDF; pragma vẫn được áp dụng
Hãy giữ nguyên tên khối là HPDFDoc. C++Builder bắt nguồn tên của tiêu đề (header name) từ tên khối Pascal, vì vậy, việc đổi tên tệp hay dùng bí danh đường dẫn (path alias) trong pragma sẽ làm hỏng quá trình tra cứu (lookup) một cách thầm lặng
Giới hạn phạm vi (Scoping) và các tác vụ nhiều tài liệu
Đối với thao tác xuất một lần (single export) do hành động của người dùng kích hoạt, một biến cục bộ được giới hạn (scoped) trong trình xử lý nút (button handler) là câu trả lời chính xác: nó được tạo, sử dụng, và hủy trong vòng một khung cuộc gọi (call frame), và mục đích rõ ràng với bất kỳ ai đọc mã sau này. Giải pháp thay thế tại thời gian thiết kế có thể biện minh khi chính biểu mẫu đó thúc đẩy một quy trình làm việc (workflow) liên tục, ví dụ như panel xem trước bản in giúp xây dựng lại tài liệu mỗi khi người dùng thay đổi cài đặt; trong trường hợp đó, giữ cho thành phần hoạt động và gọi lại lệnh BeginDoc/EndDoc liên tục sẽ ít gây gián đoạn hơn so với việc liên tục phân bổ và giải phóng các đối tượng heap
Đối với các tác vụ hàng loạt tạo ra nhiều tài liệu tuần tự, việc giới hạn một THotPDF trên mỗi tài liệu rất đáng với chi phí phân bổ (allocation overhead). Trạng thái (state) sẽ không chuyển giữa các tài liệu nếu không có đối tượng nào để mang nó, và đó là một loại lỗi gián đoạn (intermittent bug) mà bạn không bao giờ phải gỡ lỗi. Phân bổ, tạo, xóa, lặp lại
Một thuộc tính xuất hiện ở một số bản demo của HotPDF là AutoLaunch, với chức năng mở tệp vừa tạo trong trình xem PDF của hệ thống (system PDF viewer) ngay sau EndDoc. Chức năng này rất hữu ích trong khi viết bản nháp bố cục đầu tiên. Trong sản xuất, hãy bỏ qua nó: tự động mở đường dẫn đầu ra một cách trực tiếp, xác minh tệp tồn tại và có kích thước khác không, ghi lại (log) kết quả, và để cho quy trình làm việc (workflow) đang gọi tự quyết định xem có cần đến trình xem tài liệu hay không. Trong một tác vụ hàng loạt, AutoLaunch mở một cửa sổ trình xem cho mỗi tài liệu và nó sẽ chặn quá trình (block the process) trên một số hệ thống để chờ trình xem đóng lại
Thành phần THotPDF và tất cả lệnh gọi vẽ được hiển thị ở đây đều thuộc về Thành phần HotPDF dành cho Delphi và C++Builder