גרירת THotPDF לתוך טופס בזמן עיצוב היא בסדר עבור אב-טיפוס מהיר, אך היא קושרת את הרכיב למחזור החיים של הטופס, מה שנדיר למצוא בקוד ייצור (production). מחולל דוחות שרץ פעם אחת לכל לחיצת כפתור, תהליכון שירות שמבצע ייצוא לילי במנות (batches), מחלקת עזר שאין לה טופס כלל: בכל אחד מהמצבים הללו אתה רוצה שהרכיב יתקיים בדיוק למשך משימת PDF אחת ואז ייעלם. המשמעות היא הקצאה בזמן ריצה, והדבר משנה שני דברים ששווה להבין לפני שכותבים את השורה הראשונה: מי הבעלים של האובייקט, וכיצד מתבצע הניקוי כאשר משהו משתבש
סמנטיקת הבעלים ב-VCL
כל בנאי (constructor) של רכיב VCL מקבל פרמטר Owner מסוג *TComponent. העברת this (הטופס) רושמת את האובייקט החדש ברשימת הרכיבים בבעלות הטופס, כך שאם הטופס נהרס בזמן שהרכיב עדיין חי, ה-VCL משחרר אותו אוטומטית. העברת nullptr פירושה שאין בעלים: אתה לוקח אחריות בלעדית על המצביע, ושום דבר לא ינקה אותו עבורך אם חריגה תגלגל לאחור את המחסנית לפני ה-delete המפורש שלך
עבור ייצוא חד-פעמי שמסתיים בתוך פונקציה יחידה, כל אחת מהאפשרויות עובדת, אך לשתיהן יש מצבי כשל שונים. עם this כבעלים, דליפה היא בלתי אפשרית כל עוד הטופס נסגר בסופו של דבר; עם nullptr, המצביע חייב להגיע לבלוק __finally. בפועל, תבנית ה-nullptr יחד עם __finally מעט נקייה יותר עבור אובייקטים בעלי תוחלת חיים קצרה מכיוון שהיא הופכת את גבול מחזור החיים לגלוי במבט חטוף ומונעת מהטופס לצבור אובייקטים בבעלות שנועדו להיות זמניים
מבנה בטוח מפני חריגות (Exception-safe)
יצירת PDF עשויה להיכשל מסיבות שאין להן שום קשר ל-API: ספריית הפלט היא לקריאה בלבד, קובץ גופן חסר, זרם מתרוקן בטרם עת, או נתונים שסופקו על ידי המתקשר מגיעים למגבלת אורך. לא משנה מה הסיבה, נתיב הניקוי חייב לרוץ. הדרך המקובלת (idiomatic) ב-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 מקבל קואורדינטות בנקודות (points) הנמדדות מהפינה השמאלית-תחתונה של הדף, כשציר Y גדל כלפי מעלה; הצמד 72, 720 ממקם טקסט קרוב לפינה השמאלית-עליונה של דף בגודל letter עם שוליים שמאליים של אינץ' אחד. delete Pdf בבלוק __finally רץ בין אם BeginDoc, הציור או EndDoc העלו חריגה ובין אם לא
הימנע מלקרוא לשיטה כלשהי על Pdf לאחר delete. אם המצביע מאוחסן במשתנה חבר, הגדר אותו ל-nullptr מיד לאחר המחיקה כך שכל גישה מקרית מאוחרת יותר תייצר קריסה נקייה ולא השחתה שקטה
תצורת הפרויקט
C++Builder מאתר את THotPDF דרך שילוב של נתיבי include, נתיבי ספריות (library paths) והנחיית pragma. קובץ הכותרת שנוצר שוכן לצד HPDFDoc.pas בספריית המקור של HotPDF; הוסף ספרייה זו ל-Project > Options > C++ Compiler > Include path. ההנחיה #pragma link "HPDFDoc" אומרת למקשר (linker) למשוך פנימה את היחידה המהודרת מבלי לרשום אותה ידנית בקובץ הפרויקט. אם אתה משתמש בחבילת זמן הריצה (runtime package) במקום בקישור סטטי, התקן תחילה את חבילות העיצוב וזמן הריצה של HotPDF; ה-pragma עדיין רלוונטי
שמור על שם היחידה HPDFDoc ללא שינוי. C++Builder גוזר את שם קובץ הכותרת משם היחידה ב-Pascal, כך ששינוי שם הקובץ או שימוש בכינוי נתיב ב-pragma שובר את בדיקת המזהה (lookup) בשקט
תחומים (Scoping) ומשימות מרובות מסמכים
עבור ייצוא יחיד המופעל על ידי פעולת משתמש, משתנה מקומי התחום (scoped) למנהל הכפתורים הוא התשובה הנכונה: הוא נוצר, נמצא בשימוש ונהרס בתוך מסגרת קריאה אחת, והכוונה ברורה לכל מי שיקרא את הקוד מאוחר יותר. חלופת זמן העיצוב מוצדקת כאשר אותו טופס מניע זרימת עבודה רציפה, כגון חלונית תצוגה מקדימה להדפסה הבונָה מחדש את המסמך בכל פעם שהמשתמש משנה הגדרה; במקרה כזה, שמירת הרכיב חי וקריאה חוזרת ל-BeginDoc/EndDoc פחות מפריעה מאשר הקצאה ושחרור חוזרים של אובייקטים בערימה (heap)
עבור עבודות אצווה (batch jobs) המייצרות מסמכים רבים ברצף, תחום (scoping) של THotPDF אחד לכל מסמך שווה את תקורת ההקצאה. מצב (State) אינו עובר בין מסמכים אם אין אובייקט שיישא אותו, וזהו סוג אחד של באג לסירוגין שלעולם לא תצטרך לנפות (debug). הקצה, ייצר, מחק, חזור
מאפיין אחד שמופיע במספר הדגמות של HotPDF הוא AutoLaunch, הפותח את הקובץ שנוצר במציג ה-PDF של המערכת מיד לאחר EndDoc. הוא שימושי בעת כתיבת הטיוטה הראשונה של פריסה. בייצור (production), דלג עליו: פתח את נתיב הפלט במפורש, ודא שהקובץ קיים וגודלו אינו אפס, תעד (log) את התוצאה, ותן לזרימת העבודה הקוראת להחליט אם מציג רלוונטי. בעבודת אצווה, AutoLaunch מפעיל חלון מציג אחד לכל מסמך ויחסום את התהליך במערכות מסוימות בהמתנה לסגירת המציג
רכיב THotPDF וכל קריאות הציור המוצגות כאן הם חלק מרכיב HotPDF עבור Delphi ו-C++Builder