Η δημιουργία μιας αναφοράς καταλήγει στο να τοποθετήσετε τρία πράγματα σε μια σελίδα και να τα κάνετε να συμφωνούν για το πού κάθονται: κείμενο σε γνωστές συντεταγμένες, γραμματοσειρές που αποδίδονται το ίδιο στον server όπως και στην επιφάνεια εργασίας σας, και εικόνες με μέγεθος που ταιριάζει. Οτιδήποτε άλλο κάνει μια βιβλιοθήκη αναφορών οργανώνεται γύρω από αυτά τα τρία. Το HotPDF, η βιβλιοθήκη δημιουργίας PDF της losLab για Delphi και C++Builder, σας δίνει καθένα από αυτά ως απευθείας κλήση στο αντικείμενο σελίδας, και η μόνη πραγματική τριβή είναι το σύστημα συντεταγμένων από κάτω, το οποίο τρέχει προς την αντίθετη κατεύθυνση από το canvas VCL που έχετε συνηθίσει. Τακτοποιήστε πρώτα αυτόν τον προσανατολισμό και η υπόλοιπη δουλειά διάταξης σταματά να σας αντιστέκεται
Τοποθέτηση κειμένου και η αρχή κάτω-αριστερά
Σχεδόν η πρώτη αναφορά όλων βγαίνει ανάποδα. Ο τίτλος προσγειώνεται κοντά στο κάτω άκρο και κάθε γραμμή από κάτω του σκαρφαλώνει προς την κορυφή. Τίποτα δεν δυσλειτουργεί. Ο χώρος χρήστη PDF, όπως ορίζεται στο ISO 32000-1 §8.3, βάζει την αρχή στην κάτω αριστερή γωνία με το Y να αυξάνεται προς τα πάνω, που είναι το καθρεφτικό είδωλο του canvas GDI όπου το Y αυξάνεται προς τα κάτω από την πάνω αριστερή γωνία. Πέντε λεπτά που ξοδεύετε συμφιλιωμένοι με αυτό σώζουν μια διάταξη που διαφορετικά θα ξαναγράφατε μόλις οι αριθμοί σταματήσουν να βγάζουν νόημα
Η κεντρική κλήση του αντικειμένου σελίδας είναι το TextOut(X, Y, Angle, Text). Τα X και Y εντοπίζουν το κείμενο σε points από την κάτω αριστερή γωνία, και το Angle το περιστρέφει σε μοίρες, που είναι πώς σχεδιάζεται μια διαγώνια σφραγίδα DRAFT ή COPY χωρίς καμία ειδική υποστήριξη. Το κόλπο που αφήνει τη διαίσθηση εκπαιδευμένη στο VCL να συνεχίζει να λειτουργεί είναι να εκφράσετε το Y ως το ύψος σελίδας μείον την απόσταση που θέλετε από την κορυφή:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice-0001.pdf';
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 792 - 50, 0, 'INVOICE'); // 50pt από την κορυφή σελίδας Letter
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 792 - 70, 0, 'Date: 2026-06-11');
Pdf.CurrentPage.TextOut(300, 400, 45, 'COPY'); // περιστραμμένη σφραγίδα
Pdf.AddPage; // Το CurrentPage δείχνει τώρα εδώ
Pdf.CurrentPage.SetFont('Arial', [], 10); // η κατάσταση γραμματοσειράς δεν μεταφέρεται
Pdf.CurrentPage.TextOut(50, 742, 0, 'Page 2 detail rows');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Οι δύο κατά κατάσταση (stateful) συμπεριφορές σε αυτή τη λίστα ευθύνονται για τα περισσότερα σφάλματα που εμφανίζονται μόνο στη σελίδα δύο. Το AddPage ανακατευθύνει το CurrentPage προς τη σελίδα που μόλις δημιούργησε, οπότε μια αναφορά σελίδας που κρατήσατε νωρίτερα δεν σχεδιάζει πια εκεί που περιμένετε. Η επιλογή γραμματοσειράς είναι επίσης ανά σελίδα και όχι ανά έγγραφο. Αν παραλείψετε το SetFont μετά από ένα AddPage, το πρώτο TextOut στη νέα σελίδα επιστρέφει σε ό,τι προεπιλογή είχε η σελίδα στην αρχή, όχι στην έντονη γραμματοσειρά τίτλου που ορίσατε τρεις σελίδες πριν. Η ασφαλής συνήθεια είναι να αντιμετωπίζετε το "ξεκίνα νέα σελίδα" και το "επανακαθόρισε την κατάσταση κειμένου" ως ένα αδιαχώριστο βήμα μέσα στον βρόχο αναφοράς
Γραμματοσειρές που υπάρχουν στον server, όχι μόνο στην επιφάνεια εργασίας σας
Τα περισσότερα προβλήματα γραμματοσειρών είναι στην πραγματικότητα προβλήματα ανάπτυξης μεταμφιεσμένα. Ο υπολογιστής ανάπτυξής σας έχει εγκατεστημένη την εταιρική γραμματοσειρά, οπότε η αναφορά φαίνεται σωστή στην οθόνη σας και κυκλοφορεί. Ο host παραγωγής τρέχει την εργασία κάτω από έναν λογαριασμό υπηρεσίας που ποτέ δεν είχε εγκατεστημένη αυτή τη γραμματοσειρά, ο renderer αντικαθιστά σιωπηλά κάτι που μπορεί να βρει, και το πρώτο πράγμα που ακούει κανείς είναι ένας πελάτης να ρωτά γιατί άλλαξε το letterhead. Η διέξοδος είναι να σταματήσετε να εμπιστεύεστε τον φάκελο γραμματοσειρών του λειτουργικού συστήματος και να φορτώνετε τη γραμματοσειρά από ένα αρχείο που ο installer σας βάζει στον δίσκο. Η κλήση εγγραφής Unicode του HotPDF δέχεται μια διαδρομή και κάνει ακριβώς αυτό:
Pdf.RegisterUnicodeTTF('C:\ProgramData\MyApp\Fonts\NotoSans.ttf');
Pdf.CurrentPage.SetFont('NotoSans', [], 12);
Pdf.CurrentPage.TextOut(50, 700, 0, WideString('Łódź - Ünïcode test ✓'));
Το TextOut δέχεται απευθείας ένα WideString, κάτι που έχει μεγαλύτερη σημασία απ' όσο φαίνεται με την πρώτη ματιά. Ένα όνομα πελάτη με τόνο, μια γερμανική οδός, μια πολωνική πόλη: αυτά δεν είναι ακραίες περιπτώσεις, είναι το κανονικό περιεχόμενο ενός πίνακα πελατών, και περνούν από την ίδια κλήση με τις ετικέτες ASCII που κωδικοποιείτε με το χέρι, εφόσον η καταχωρημένη γραμματοσειρά πράγματι περιέχει τα glyphs. Ένας περιορισμός έκδοσης συνοδεύει τις ενσωματωμένες γραμματοσειρές: το έγγραφο πρέπει να είναι PDF 1.5 ή νεότερο, οπότε αν μια άσχετη απαίτηση σας καθηλώνει σε παλαιότερη έκδοση, αυτό είναι το πράγμα που θα σπάσει σιωπηλά. Σενάρια από δεξιά προς αριστερά όπως τα αραβικά και τα εβραϊκά χρειάζονται πραγματική μορφοποίηση (shaping) αντί για απλή αναζήτηση glyph, και αυτό έχει τη δική του ροή επεξεργασίας· δείτε το άρθρο μας για τη μορφοποίηση κειμένου σύνθετων σεναρίων με το HotPDF
Όταν καμία εγκατεστημένη γραμματοσειρά δεν μπορεί να εκφράσει αυτό που χρειάζεστε, σκεφτείτε χαρακτήρες MICR σε μια επιταγή ή ένα ιδιόκτητο σύνολο συμβόλων, οι γραμματοσειρές Type 3 καλύπτουν το κενό. Ορίζετε κάθε glyph ως ένα μικρό content stream μέσω των RegisterType3Font και AddType3Glyph. Είναι μια εξειδικευμένη γωνία του API και σπάνια θα καταφύγετε σε αυτήν, αλλά είναι πολύ πιο καθαρή από το να σκορπίζετε εκατοντάδες μικροσκοπικά bitmap συμβόλων σε μια σελίδα
Εικόνες: τα μεσαία ορίσματα είναι πλάτος και ύψος, όχι γωνία
Ο χειρισμός εικόνων χωρίζεται σε δύο βήματα, και το να τα κρατάτε ξεχωριστά είναι όλο το νόημα. Το AddImage δέχεται ένα TBitmap ή TJPEGImage, το ενσωματώνει μία φορά, και επιστρέφει έναν δείκτη. Το εικαστικό PNG πρέπει να αποκωδικοποιηθεί σε bitmap προτού φτάσει εκεί. Το ShowImage έπειτα σχεδιάζει αυτόν τον δείκτη όπου και όσο συχνά θέλετε. Η σειρά ορισμάτων στο ShowImage είναι το ένα σημείο που αξίζει να διαβάσετε πιο αργά:
var
Png: TPngImage;
Logo: TBitmap;
LogoIdx: Integer;
begin
Png := TPngImage.Create;
Logo := TBitmap.Create;
try
Png.LoadFromFile('brand-logo.png');
Logo.Assign(Png); // αποκωδικοποίηση PNG σε bitmap
LogoIdx := Pdf.AddImage(Logo, icFlate); // χωρίς απώλειες για επίπεδο-χρώμα εικαστικό
finally
Logo.Free;
Png.Free;
end;
// (Index, X, Y, Width, Height, Angle): όχι (X1, Y1, X2, Y2)
Pdf.CurrentPage.ShowImage(LogoIdx, 50, 700, 120, 40, 0);
end;
Οι δύο αριθμοί μετά τη θέση είναι πλάτος και ύψος. Δεν είναι οι συντεταγμένες της απέναντι γωνίας, και το τελευταίο όρισμα είναι γωνία περιστροφής σε μοίρες. Διαβάστε την υπογραφή ως κουτί X1/Y1/X2/Y2 και ένα λογότυπο 120 επί 40 τοποθετημένο στο (50, 700) απλώνεται αντ' αυτού από εκεί μέχρι το (120, 40), σκορπίζοντας σε μεγάλο μέρος της σελίδας. Η έξοδος κάνει το λάθος προφανές ενώ ο πηγαίος κώδικας φαίνεται εντελώς λογικός, κάτι που είναι αυτό που σας κάνει να χάσετε ένα απόγευμα. Το KeepImageAspectRatio είναι εξ ορισμού True, οπότε ένα κουτί με λάθος αναλογίες κάνει letterbox την εικόνα αντί να την παραμορφώσει· γυρίστε το σε False μόνο όταν πράγματι εννοείτε να την τεντώσετε
Ο διαχωρισμός μεταξύ καταχώρισης και τοποθέτησης αποδίδει σε μεγάλες εκτελέσεις. Επειδή το AddImage ενσωματώνει τα pixel μία φορά και κάθε ShowImage με εκείνον τον δείκτη δείχνει πίσω στο ίδιο ενσωματωμένο αντικείμενο, το πού καλείτε το AddImage καθορίζει το μέγεθος του αρχείου. Καλέστε το μέσα στον βρόχο σελίδων για μια κατάσταση 500 σελίδων και το ίδιο λογότυπο ενσωματώνεται 500 φορές. Καλέστε το μία φορά πριν τον βρόχο, κρατήστε τον δείκτη, και το λογότυπο αποθηκεύεται μία μόνο φορά. Ένα μικρό λεξικό με κλειδί τη διαδρομή του asset αρκεί για να εξασφαλίσει ότι κάθε ξεχωριστή εικόνα καταχωρείται ακριβώς μία φορά
Η επιλογή του codec είναι ο άλλος μοχλός μεγέθους. Το φωτογραφικό περιεχόμενο, σαρωμένα συνημμένα και παρόμοια, ανήκει στο JPEG: περάστε icJpeg στο AddImage και ρίξτε το JpegQuality γύρω στο 85, καθώς η ιδιότητα ξεκινά από το 100 και η διαφορά στο 85 είναι αόρατη σε μια τυπωμένη σελίδα. Το εικαστικό επίπεδου χρώματος όπως λογότυπα, γραφήματα, και σχέδια γραμμών ανήκει στο icFlate, όπου η συμπίεση χωρίς απώλειες είναι ήδη συμπαγής και το JPEG θα άφηνε ορατό ringing γύρω από τις σκληρές ακμές. Μια εκτέλεση καταστάσεων που σπρώχνει μία φωτογραφία πλήρους ποιότητας σε κάθε σελίδα μπορεί να φουσκώσει σε gigabyte· το ίδιο περιεχόμενο σε JPEG 85 προσγειώνεται περίπου στο ένα δέκατο του μεγέθους, και κανείς αναγνώστης δεν μπορεί να το καταλάβει
Γραμμές, κουτιά και σκίαση με primitives διαδρομών
Η οριζόντια γραμμή κάτω από την κεφαλίδα ενός πίνακα και το γκρι κουτί πίσω από ένα σύνολο δεν χρειάζεται να είναι εικόνες. Σχεδιάστε τα ως διανύσματα και παραμένουν καθαρά σε οποιοδήποτε zoom, εκτυπώνονται καθαρά, και προσθέτουν σχεδόν τίποτα στο αρχείο. Το HotPDF ακολουθεί το ίδιο μοντέλο που χρησιμοποιούν τα ακατέργαστα content stream του PDF: χτίστε μια διαδρομή, έπειτα καλέστε έναν τελεστή που τη ζωγραφίζει
// Οριζόντια γραμμή κάτω από την κεφαλίδα του πίνακα
Pdf.CurrentPage.SetLineWidth(0.75);
Pdf.CurrentPage.MoveTo(50, 660);
Pdf.CurrentPage.LineTo(545, 660);
Pdf.CurrentPage.Stroke;
// Σκιασμένο κουτί συνόλων: X, Y, πλάτος, ύψος
Pdf.CurrentPage.SetRGBFillColor(RGB(235, 235, 235));
Pdf.CurrentPage.Rectangle(395, 120, 150, 40);
Pdf.CurrentPage.Fill;
Η σειρά δεν είναι προαιρετική: ορίστε την κατάσταση ζωγραφικής, κατασκευάστε τη διαδρομή, έπειτα καλέστε το Stroke ή το Fill. Μια διαδρομή που χτίζετε αλλά ποτέ δεν ζωγραφίζετε δεν συνεισφέρει τίποτα στη σελίδα, κάτι που είναι σχεδόν πάντα η απάντηση όταν μια γραμμή "δεν εμφανίζεται". Το SetRGBFillColor δέχεται ένα μοναδικό TColor, οπότε οι γνωστές σταθερές VCL όπως clNavy και clBlack μπαίνουν κατευθείαν, και το Rectangle χρησιμοποιεί τα ίδια ορίσματα πλάτους-και-ύψους όπως η τοποθέτηση εικόνας αντί για δύο γωνίες. Μια προειδοποίηση για λεπτές γραμμές: οτιδήποτε κάτω από περίπου μισό point μπορεί να φαίνεται κομψό σε μια οθόνη και έπειτα να εξαφανίζεται σε έναν εκτυπωτή γραφείου 600 dpi, οπότε το 0,75pt είναι ένα λογικό κατώτατο όριο για κάθε γραμμή που πρέπει να επιβιώσει στην εκτύπωση
Σελιδοποίηση έναντι πραγματικών δεδομένων, όχι δεδομένων δείγματος
Μία λεπτομέρεια που πρέπει να τακτοποιηθεί πριν παγιωθεί η διάταξη: οι αριθμητικές στήλες πρέπει να ευθυγραμμίζονται στη δεξιά τους άκρη, και ο τρόπος να το κάνετε αυτό είναι να μετρήσετε το αποδιδόμενο πλάτος κάθε τιμής και να την τοποθετήσετε πίσω από το όριο της στήλης, όχι να γεμίσετε τη συμβολοσειρά με κενά στην αρχή. Το γέμισμα με κενά ευθυγραμμίζεται μόνο σε μια γραμματοσειρά monospaced, και κανείς δεν στήνει μια οικονομική αναφορά σε γραμματοσειρά monospaced. Περάστε τις τιμές πρώτα μέσα από τις τοπικά ενήμερες ρουτίνες του Delphi όπως το FormatFloat, ώστε ο διαχωριστής χιλιάδων του οποίου μετράτε το πλάτος να είναι ο ίδιος που θα εμφανίσει πράγματι η τοπική ρύθμιση του πελάτη
Ο κίνδυνος με τη σελιδοποίηση είναι ότι τη γράφετε ενάντια στο demo dataset, όπου δέκα σύντομες γραμμές χωρούν σε μία σελίδα και ο βρόχος ποτέ δεν χρειάζεται να σπάσει. Η παραγωγή σας δίνει έναν πελάτη του οποίου η επωνυμία εταιρείας τρέχει 140 χαρακτήρες και μια κατάσταση με 4.000 στοιχεία γραμμής, και τώρα ο βρόχος πρέπει να σπάει σωστά κάθε φορά. Το μοτίβο που αντέχει είναι ένας ενιαίος δρομέας Y που κινείται προς τα κάτω καθώς αφαιρείτε το ύψος κάθε γραμμής, και ένας έλεγχος που ξεκινά μια νέα σελίδα τη στιγμή που ο δρομέας θα διέσχιζε το κάτω περιθώριο. Προς τα κάτω εδώ σημαίνει μειούμενο Y, που είναι το ένα σημείο όπου η αρχή κάτω-αριστερά παραμένει αντι-διαισθητική. Κρατήστε όλο αυτό σε μία ρουτίνα που επίσης επανεκδίδει το SetFont και ξανασχεδιάζει την τρέχουσα κεφαλίδα στη νέα σελίδα, και τα σφάλματα off-by-one-page ποτέ δεν βρίσκουν πάτημα. Όταν οι ίδιες αναφορές πρέπει επίσης να ικανοποιούν αρχειακούς κανόνες ή κανόνες προσβασιμότητας, οι επιλογές που κάνετε ακριβώς εδώ, ποιες γραμματοσειρές ενσωματώνετε, αν η έξοδος είναι tagged, ποιους χρωματικούς χώρους χρησιμοποιείτε, είναι αυτές που αστυνομεύουν αυτά τα πρότυπα· ο οδηγός HotPDF για PDF/A, PDF/X και PDF/UA αξίζει να διαβαστεί προτού παγιωθεί το template
Κάθε κλήση που παρουσιάζεται εδώ, η τοποθέτηση κειμένου, η καταχώριση γραμματοσειρών, η ενσωμάτωση εικόνων, και η σχεδίαση διαδρομών, κυκλοφορεί στο HotPDF Delphi Component για Delphi και C++Builder, του οποίου η αναφορά τεκμηριώνει το πλήρες API εξόδου μαζί με τα χαρακτηριστικά φορμών, κρυπτογράφησης και υπογραφής δίπλα στα οποία βρίσκεται