Τεχνικό Άρθρο

Δυναμικός κύκλος ζωής του HotPDF στον C++Builder

Το να αφήσετε ένα THotPDF πάνω σε μια φόρμα κατά τον σχεδιασμό είναι μια χαρά για ένα γρήγορο πρωτότυπο, όμως δένει το στοιχείο με τη διάρκεια ζωής της φόρμας, κάτι που σπάνια θέλει ο κώδικας παραγωγής. Μια γεννήτρια αναφορών που τρέχει μία φορά ανά πάτημα κουμπιού, ένα νήμα υπηρεσίας που ομαδοποιεί νυχτερινές εξαγωγές, μια βοηθητική κλάση που δεν έχει καθόλου φόρμα: σε καθεμία από αυτές τις περιπτώσεις θέλετε το στοιχείο να υπάρχει ακριβώς για τη διάρκεια μίας εργασίας PDF και μετά να εξαφανίζεται. Αυτό σημαίνει δέσμευση στον χρόνο εκτέλεσης, και αλλάζει δύο πράγματα που αξίζει να καταλάβετε πριν γράψετε την πρώτη γραμμή: ποιος κατέχει το αντικείμενο, και πώς εκτελείται ο καθαρισμός όταν κάτι πάει στραβά

Σημασιολογία του owner στη VCL

Κάθε constructor στοιχείου της VCL δέχεται μια παράμετρο Owner τύπου TComponent*. Περνώντας το this (τη φόρμα) καταχωρείτε το νέο αντικείμενο στη λίστα των κατεχόμενων στοιχείων της φόρμας, ώστε αν η φόρμα καταστραφεί ενώ το στοιχείο είναι ακόμη ζωντανό, η VCL το αποδεσμεύει αυτόματα. Περνώντας nullptr σημαίνει ότι δεν υπάρχει owner: αναλαμβάνετε αποκλειστικά την ευθύνη του δείκτη, και τίποτα δεν θα τον καθαρίσει για λογαριασμό σας αν μια εξαίρεση ξετυλίξει τη στοίβα πριν από το ρητό σας delete

Για μια εξαγωγή μίας χρήσης που ολοκληρώνεται μέσα σε μία συνάρτηση, οποιαδήποτε από τις δύο επιλογές λειτουργεί, αλλά έχουν διαφορετικούς τρόπους αστοχίας. Με το this ως owner, η διαρροή είναι αδύνατη εφόσον η φόρμα τελικά κλείσει· με nullptr, ο δείκτης πρέπει να φτάσει σε ένα μπλοκ __finally. Στην πράξη, το μοτίβο nullptr συν __finally είναι ελαφρώς καθαρότερο για βραχύβια αντικείμενα, επειδή κάνει το όριο της διάρκειας ζωής ορατό με μια ματιά και αποφεύγει να συσσωρεύει η φόρμα κατεχόμενα αντικείμενα που προορίζονταν να είναι προσωρινά

Χρονοδιάγραμμα του δυναμικού κύκλου ζωής ενός THotPDF στον C++Builder, από το new THotPDF(nullptr) και τον ορισμό επιλογών μέχρι τα BeginDoc και EndDoc, και το delete Pdf μέσα σε ένα μπλοκ __finally που εκτελείται ακόμη και όταν εγείρεται εξαίρεση
Ένα THotPDF του χρόνου εκτέλεσης ζει για ακριβώς μία εργασία PDF: ορίστε επιλογές πριν από το BeginDoc, σχεδιάστε, κλείστε, και αφήστε το μπλοκ __finally να εκδώσει το delete ανεξάρτητα από εξαιρέσεις

Δομή ασφαλής ως προς τις εξαιρέσεις

Η παραγωγή 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;
    }
}

Μερικά σημεία σε αυτή την καταχώριση αξίζει να τονιστούν. Ο owner είναι nullptr, κάνοντας τη διάρκεια ζωής ρητή. Τα Compression και FontEmbedding ορίζονται πριν από το BeginDoc: και τα δύο είναι επιλογές σε επίπεδο εγγράφου που το HotPDF οριστικοποιεί όταν ανοίγει το έγγραφο, και η ανάθεσή τους αργότερα δεν έχει καμία επίδραση. Το TextOut δέχεται συντεταγμένες σε σημεία μετρημένες από την κάτω αριστερή γωνία της σελίδας, με το Y να αυξάνεται προς τα πάνω· το ζεύγος 72, 720 τοποθετεί το κείμενο κοντά στην επάνω αριστερή γωνία μιας σελίδας μεγέθους letter με περιθώριο μίας ίντσας αριστερά. Το delete Pdf στο μπλοκ __finally εκτελείται είτε το BeginDoc, η σχεδίαση ή το EndDoc ήγειραν εξαίρεση είτε όχι

Αποφύγετε να καλέσετε οποιαδήποτε μέθοδο στο Pdf μετά το delete. Αν ο δείκτης αποθηκεύεται σε μεταβλητή μέλους, θέστε τον σε nullptr αμέσως μετά τη διαγραφή, ώστε κάθε τυχαία μεταγενέστερη πρόσβαση να παράγει ένα καθαρό crash αντί για σιωπηλή αλλοίωση

Ρύθμιση του έργου

Ο C++Builder εντοπίζει το THotPDF μέσω ενός συνδυασμού από include paths, library paths και μια οδηγία pragma. Η παραγόμενη κεφαλίδα βρίσκεται δίπλα στο HPDFDoc.pas στον κατάλογο πηγαίου κώδικα του HotPDF· προσθέστε αυτόν τον κατάλογο στο Project > Options > C++ Compiler > Include path. Η οδηγία #pragma link "HPDFDoc" λέει στον linker να ενσωματώσει τη μεταγλωττισμένη μονάδα χωρίς να την καταχωρίσετε χειροκίνητα στο αρχείο του έργου. Αν χρησιμοποιείτε το runtime package αντί για στατική σύνδεση, εγκαταστήστε πρώτα τα design και runtime packages του HotPDF· η pragma εξακολουθεί να ισχύει

Διατηρήστε το όνομα μονάδας HPDFDoc αμετάβλητο. Ο C++Builder παράγει το όνομα της κεφαλίδας από το όνομα της μονάδας Pascal, οπότε η μετονομασία του αρχείου ή η χρήση ψευδωνύμου διαδρομής στην pragma χαλάει σιωπηλά την αναζήτηση

Εμβέλεια και εργασίες πολλαπλών εγγράφων

Για μία μεμονωμένη εξαγωγή που πυροδοτείται από ενέργεια του χρήστη, μια τοπική μεταβλητή με εμβέλεια τον χειριστή του κουμπιού είναι η σωστή απάντηση: δημιουργείται, χρησιμοποιείται και καταστρέφεται μέσα σε ένα πλαίσιο κλήσης, και η πρόθεση είναι προφανής σε όποιον διαβάσει τον κώδικα αργότερα. Η εναλλακτική του χρόνου σχεδίασης δικαιολογείται όταν η ίδια φόρμα οδηγεί μια συνεχή ροή εργασίας, όπως ένας πίνακας προεπισκόπησης εκτύπωσης που ξαναχτίζει το έγγραφο κάθε φορά που ο χρήστης αλλάζει μια ρύθμιση· σε αυτή την περίπτωση, το να κρατάτε το στοιχείο ζωντανό και να καλείτε επανειλημμένα BeginDoc/EndDoc είναι λιγότερο ενοχλητικό από το να δεσμεύετε και να αποδεσμεύετε συνεχώς αντικείμενα στον σωρό

Για μαζικές εργασίες που παράγουν πολλά έγγραφα στη σειρά, το να δίνετε εμβέλεια σε ένα THotPDF ανά έγγραφο αξίζει το κόστος της δέσμευσης. Η κατάσταση δεν μεταφέρεται μεταξύ εγγράφων αν δεν υπάρχει αντικείμενο να τη μεταφέρει, και αυτή είναι μία κατηγορία διακοπτόμενων σφαλμάτων που δεν χρειάζεται ποτέ να αποσφαλματώσετε. Δεσμεύστε, παραγάγετε, διαγράψτε, επαναλάβετε

Μια ιδιότητα που εμφανίζεται σε αρκετά demo του HotPDF είναι το AutoLaunch, το οποίο ανοίγει το παραγόμενο αρχείο στο πρόγραμμα προβολής PDF του συστήματος αμέσως μετά το EndDoc. Είναι χρήσιμο όσο γράφετε το πρώτο προσχέδιο μιας διάταξης. Στην παραγωγή, παραλείψτε το: ανοίξτε ρητά τη διαδρομή εξόδου, επαληθεύστε ότι το αρχείο υπάρχει και έχει μη μηδενικό μέγεθος, καταγράψτε το αποτέλεσμα, και αφήστε τη ροή εργασίας που καλεί να αποφασίσει αν ένα πρόγραμμα προβολής έχει νόημα. Σε μια μαζική εργασία, το AutoLaunch ανοίγει ένα παράθυρο προβολής ανά έγγραφο και σε ορισμένα συστήματα θα μπλοκάρει τη διεργασία περιμένοντας να κλείσει το πρόγραμμα προβολής

Διάγραμμα μαζικού βρόχου που δεσμεύει ένα φρέσκο THotPDF ανά έγγραφο και το αποδεσμεύει μετά το EndDoc ώστε να μη διαρρέει κατάσταση μεταξύ περασμάτων, με προειδοποίηση κατά της ενεργοποίησης του AutoLaunch σε εξαγωγές PDF παραγωγής
Δώστε εμβέλεια σε ένα φρέσκο THotPDF ανά έγγραφο μαζικής εργασίας ώστε να μη διαρρέει κατάσταση μεταξύ περασμάτων, και αφήστε το AutoLaunch απενεργοποιημένο ώστε αδέσποτα παράθυρα προβολής να μην μπορούν να κολλήσουν την εργασία

Το στοιχείο THotPDF και όλες οι κλήσεις σχεδίασης που παρουσιάζονται εδώ αποτελούν μέρος του HotPDF Delphi Component για Delphi και C++Builder