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

Εκτύπωση Φορτωμένου PDF στο Delphi με το HotPDF και το TPrinter

Το HotPDF εκτυπώνει ένα φορτωμένο PDF σε φυσικό εκτυπωτή, σε οποιοδήποτε device context των Windows ή στο πρόχειρο (clipboard) μέσω τριών κλήσεων: το PrintLoadedDocument οδηγεί ένα VCL TPrinter, το RenderLoadedPageToDC σχεδιάζει μια σελίδα απευθείας σε οποιοδήποτε HDC, και το RenderLoadedPageToClipboard αντιγράφει μια σελίδα ως bitmap ή ως διανυσματικό metafile. Το HotPDF είναι ένα εγγενές στοιχείο VCL PDF για Delphi και C++Builder, και πρόσθεσε αυτήν την κορυφαία διαδρομή εκτύπωσης στην έκδοση v2.331.0, βελτιώνοντάς την στην έκδοση v2.348.0, οπότε δεν χρειάζεται πλέον να δημιουργείτε χειροκίνητα έναν βρόχο TPrinter γύρω από έναν αποδότη bitmap. Και οι τρεις κλήσεις σχεδιάζουν το διανυσματικό περιεχόμενο της σελίδας απευθείας εκεί που μετράει, έτσι ώστε η εκτύπωση να παραμένει τόσο ευκρινής όσο μπορεί να την αποδώσει ο εκτυπωτής

Γιατί να σχεδιάσετε μια σελίδα PDF απευθείας στον εκτυπωτή αντί για bitmap;

Μια σελίδα PDF είναι ένα πρόγραμμα τελεστών διανυσματικής σχεδίασης — διαδρομές, κείμενο και εικόνες που εκτελούνται σύμφωνα με το μοντέλο γραφικών ISO 32000-1 — και όχι ένα πλέγμα pixel. Ο προφανής τρόπος για να εκτυπώσετε μια σελίδα είναι να την εικονοποιήσετε (rasterize) σε ένα bitmap και να μεταφέρετε αυτό το bitmap στον καμβά του εκτυπωτή. Αυτό λειτουργεί, αλλά πετάει το μόνο πράγμα στο οποίο ένας εκτυπωτής είναι καλός. Ένας σύγχρονος εκτυπωτής λέιζερ αποδίδει 600 ή 1200 DPI. Ένα bitmap που έχει εικονοποιηθεί στα 150 ή 300 DPI και στη συνέχεια τεντωθεί για να χωρέσει, φτάνει θαμπό, με οδοντωτές άκρες κειμένου και ορατή παρεμβολή (interpolation). Το RenderLoadedPageToDC ακολουθεί την καλύτερη διαδρομή: αναπαράγει τη ροή περιεχομένου της σελίδας ως κλήσεις GDI που εκπέμπονται απευθείας στο device context προορισμού, έτσι ώστε το διανυσματικό κείμενο και το γραμμικό σχέδιο να φτάνουν στον εκτυπωτή ως διανύσματα και να εικονοποιούνται μία φορά, από τον εκτυπωτή, στη δική του ανάλυση

Πώς εκτυπώνετε ένα φορτωμένο PDF σε φυσικό εκτυπωτή στο Delphi;

Το HotPDF εκτυπώνει ένα φορτωμένο έγγραφο με το PrintLoadedDocument(Printer, Options), το οποίο λαμβάνει ένα εκχωρημένο VCL TPrinter και μια εγγραφή THPDFPrintOptions, εκτυπώνει τις επιλεγμένες σελίδες και επιστρέφει τον αριθμό των σελίδων που στάλθηκαν. Ορίστε πρώτα το Printer.PrinterIndex ώστε να επιλεγεί μια συσκευή, συμπληρώστε μια εγγραφή επιλογών — η συνάρτηση κλάσης THPDFPrintOptions.Default σας δίνει λογικές τιμές — και καλέστε την μία φορά. Η βιβλιοθήκη ανοίγει την εργασία με το Printer.BeginDoc, διατρέχει το εύρος σελίδων εκπέμποντας ένα Printer.NewPage μεταξύ των σελίδων και την κλείνει με το Printer.EndDoc, οπότε δεν διαχειρίζεστε ποτέ τον κύκλο ζωής της εργασίας εκτύπωσης χειροκίνητα

uses Printers;

var
  Pdf: THotPDF;
  Opts: THPDFPrintOptions;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('invoice.pdf') > 0 then
    begin
      Printer.PrinterIndex := -1;         // -1 selects the default printer
      Opts := THPDFPrintOptions.Default;  // all pages, 300 DPI, FitPage, colour
      Opts.PageRange := '1,3-5';          // one-based; empty prints every page
      Opts.Copies := 2;
      Pdf.PrintLoadedDocument(Printer, Opts);
    end;
  finally
    Pdf.Free;
  end;
end;

Η συμβολοσειρά PageRange βασίζεται στο 1 και διαβάζεται με τον τρόπο που διαβάζεται ένας διάλογος εκτύπωσης: η τιμή '1,3-5' εκτυπώνει τη σελίδα 1 και τις σελίδες 3 έως 5, ενώ μια κενή συμβολοσειρά εκτυπώνει ολόκληρο το έγγραφο. Το Copies αντιστοιχίζει απευθείας στο Printer.Copies. Να είστε ειλικρινείς με τον εαυτό σας σχετικά με τη σελιδοποίηση (Collate): επειδή η VCL εκθέτει τη σελιδοποίηση με ασυνεπή τρόπο σε διάφορες εκδόσεις του Delphi, το HotPDF ορίζει τον αριθμό των αντιγράφων και αφήνει τη σελιδοποίηση στην προεπιλογή του προγράμματος οδήγησης του εκτυπωτή, οπότε όταν χρειάζεστε εγγυημένη σειρά σελιδοποίησης, ορίστε την στο πρόγραμμα οδήγησης μέσω του TPrinterSetupDialog αντί να εμπιστευτείτε τη σημαία

Πώς το DPI και το FitPage καθορίζουν την κλίμακα εκτύπωσης;

Το FitPage και το DPI αλληλεπιδρούν με τρόπο που αξίζει να κατανοήσετε προτού στείλετε το έργο σας. Με το FitPage σε True — την προεπιλογή — το HotPDF διαβάζει το MediaBox κάθε σελίδας, το κλιμακώνει στα Printer.PageWidth και Printer.PageHeight διατηρώντας την αναλογία διαστάσεων, και αποδίζει στην κλίμακα που κάνει τη σελίδα να γεμίζει το χαρτί. Σε αυτήν τη λειτουργία, το Options.DPI ουσιαστικά παρακάμπτεται από την κλίμακα προσαρμογής, επειδή η έξοδος πρέπει να ταιριάζει με τις φυσικές διαστάσεις της σελίδας του εκτυπωτή. Με το FitPage σε False, το HotPDF αποδίζει κάθε σελίδα 1:1 ακριβώς στο Options.DPI, αντιστοιχίζοντας 72 μονάδες PDF σε pixel συσκευής DPI, κάτι που είναι αυτό που θέλετε όταν η σελίδα προέλευσης και το χαρτί έχουν ήδη το ίδιο μέγεθος και σας ενδιαφέρει η ακριβής ανάλυση. Επιλέξτε FitPage όταν το μέγεθος της σελίδας προέλευσης και το μέγεθος του δίσκου χαρτιού διαφέρουν. Επιλέξτε 1:1 όταν ταιριάζουν και θέλετε ντετερμινιστική κλιμάκωση

Δύο επιλογές που προστέθηκαν στην έκδοση v2.348.0 διορθώνουν τις εκτυπώσεις που φαίνονται σχεδόν σωστές. Κάθε φυσικός εκτυπωτής έχει ένα σκληρό περιθώριο (hard margin) — ένα μη εκτυπώσιμο όριο που το υλικό δεν μπορεί να φτάσει — και από προεπιλογή η αρχή της σελίδας προσγειώνεται μέσα σε αυτό, οπότε μια σελίδα πλήρους εκτύπωσης χάνει μια λωρίδα από δύο άκρες. Ορίστε το IgnoreHardMargin σε True και το HotPDF ερωτά για τα PHYSICALOFFSETX και PHYSICALOFFSETY του εκτυπωτή, και στη συνέχεια μετατοπίζει την αρχή της συσκευής για να ακυρώσει αυτή τη μετατόπιση, έτσι ώστε η αρχή της σελίδας να αντιστοιχεί στην πραγματική γωνία του χαρτιού. Η δεύτερη επιλογή, το AutoRotate, είναι ενεργοποιημένη από προεπιλογή: διαβάζει το MediaBox κάθε σελίδας και, όταν μια σελίδα είναι πιο πλατιά από ό,τι ψηλή, γυρίζει τον εκτυπωτή σε οριζόντιο προσανατολισμό (landscape), έτσι ώστε μια πλατιά σελίδα να εκτυπώνεται χωρίς περικοπή αντί να κόβεται σε κατακόρυφο προσανατολισμό (portrait). Το AutoRotate παραμερίζει τη στιγμή που ορίζετε το Orientation ρητά, οπότε μια σκόπιμη επιλογή προσανατολισμού κερδίζει πάντα

Πώς αποδίδετε μια σελίδα PDF σε οποιοδήποτε device context;

Το RenderLoadedPageToDC(PageIndex, DC, DPI, W, H) είναι το βασικό στοιχείο κάτω από τον βρόχο εκτύπωσης, και είναι χρήσιμο από μόνο του. Το PrintLoadedDocument το καλεί μία φορά ανά σελίδα στον καμβά του εκτυπωτή, αλλά ο προορισμός μπορεί να είναι οποιοδήποτε Windows HDC: ένα off-screen memory DC, ο καμβάς ενός στοιχείου ελέγχου σε ένα προσαρμοσμένο πρόγραμμα προβολής ή μια επιφάνεια προεπισκόπησης εκτύπωσης. Το PageIndex βασίζεται στο 0, τα W και H είναι οι μονάδες συσκευής της επιφάνειας προορισμού, και ο αποδότης διατρέχει τα /Contents της σελίδας και εκπέμπει κλήσεις GDI στο DC σας. Μια σκόπιμη συμπεριφορά που αξίζει να γνωρίζετε: δεν σχεδιάζει κανένα γέμισμα φόντου. Ο καλών κατέχει την επιφάνεια, οπότε αν θέλετε λευκό χαρτί πίσω από τη σελίδα, γεμίζετε το DC μόνοι σας πριν από την απόδοση, πράγμα που σας επιτρέπει να συνθέσετε μια σελίδα πάνω από έναν υπάρχοντα καμβά όταν το θέλετε

// Draw page 1 into an off-screen 24-bit surface at 150 DPI
var
  Surface: TBitmap;
begin
  Surface := TBitmap.Create;
  try
    Surface.PixelFormat := pf24bit;
    Surface.SetSize(1275, 1650);           // 8.5 x 11 in at 150 DPI
    Surface.Canvas.Brush.Color := clWhite;
    Surface.Canvas.FillRect(Surface.Canvas.ClipRect);  // caller paints the background
    Pdf.RenderLoadedPageToDC(0, Surface.Canvas.Handle, 150, Surface.Width, Surface.Height);
    // Surface now holds the composited page, ready to blit or save
  finally
    Surface.Free;
  end;
end;

Όταν χρειάζεστε απλώς ένα έτοιμο TBitmap — μια μικρογραφία, μια εξαγωγή εικόνας, μια προεπισκόπηση που θα αποθηκεύσετε προσωρινά (cache) — χρησιμοποιήστε το RenderLoadedPageToBitmap, το οποίο καλύπτεται στο συνοδευτικό άρθρο σχετικά με την απόδοση σελίδων PDF σε bitmap. Η διαφορά είναι στο ποιος κατέχει την επιφάνεια: η κλήση bitmap εκχωρεί και επιστρέει ένα buffer pixel, ενώ το RenderLoadedPageToDC σχεδιάζει σε ένα device context που ήδη κατέχετε, το οποίο είναι ακριβώς αυτό που σας παραδίζει ένας καμβάς εκτυπωτή ή ένας χειριστής σχεδίασης. Και τα δύο μοιράζονται τον ίδιο διερμηνέα ροής περιεχομένου και την ενσωματωμένη μηχανή γλύφων, οπότε το κείμενο βγαίνει με τα ακριβή ενσωματωμένα περιγράμματα σε κάθε περίπτωση

Πώς επικολλούμε μια σελίδα PDF στο Word ή σε ένα πρόγραμμα επεξεργασίας εικόνας;

Το RenderLoadedPageToClipboard(PageIndex, DPI, AsMetafile) τοποθετεί μια σελίδα στο πρόχειρο των Windows, έτοιμη να επικολληθεί στο Word, το PowerPoint, ένα email ή ένα πρόγραμμα επεξεργασίας εικόνας. Η σημαία AsMetafile επιλέγει τη μορφή, και η επιλογή αυτή έχει μεγαλύτερη σημασία από ό,τι φαίνεται. Περάστε το False και το HotPDF τοποθετεί ένα bitmap συσκευής 24-bit (CF_BITMAP) που αποδίδεται στο δικό σας DPI — ένα σταθερό πλέγμα pixel που επικολλάται οπουδήποτε αλλά εμφανίζει pixelation όταν ο παραλήπτης το μεγεθύνει. Περάστε το True και τοποθετεί ένα βελτιωμένο metafile (CF_ENHMETAFILE) στο οποίο το κείμενο και το διανυσματικό σχέδιο της σελίδας ταξιδεύουν ως εντολές σχεδίασης GDI, έτσι ώστε το επικολλημένο αποτέλεσμα να παραμένει ευκρινές σε οποιοδήποτε ζουμ και να αναδιατάσσεται σε οποιοδήποτε μέγεθος δίνει το έγγραφο προορισμού

// Copy page 2 as a scalable vector metafile for pasting into Word
Pdf.RenderLoadedPageToClipboard(1, 300, True);

// Copy the same page as a fixed 300-DPI bitmap instead
Pdf.RenderLoadedPageToClipboard(1, 300, False);

Ο κανόνας είναι απλός. Εάν ένας χρήστης πρόκειται να επικολλήσει τη σελίδα σε ένα έγγραφο και μπορεί να αλλάξει το μέγεθός της — ένα σχήμα σε μια αναφορά, μια ρήτρα που εισάγεται σε ένα προσχέδιο σύμβασης — χρησιμοποιήστε το metafile, επειδή το διανυσματικό κείμενο εκτυπώνεται και μεγεθύνεται καθαρά. Εάν ο προορισμός κατανοεί μόνο δεδομένα raster, ή εάν θέλετε συγκεκριμένα ένα παγωμένο στιγμιότυπο σε γνωστή ανάλυση, χρησιμοποιήστε το bitmap. Για την εξαγωγή των λέξεων από μια σελίδα αντί για μια εικόνα τους, αυτή είναι μια εντελώς διαφορετική εργασία, η οποία αντιμετωπίζεται από το API εξαγωγής κειμένου, το οποίο επιστρέφει τους χαρακτήρες και τις θέσεις τους αντί για μια αποδοθείσα εικόνα

Πού σταματά η διαδρομή εκτύπωσης

Όλα σε αυτήν τη σελίδα αφορούν μόνο τα Windows, και αξίζει να το αναφέρουμε ξεκάθαρα. Τα PrintLoadedDocument, RenderLoadedPageToDC και RenderLoadedPageToClipboard είναι χτισμένα πάνω στο TPrinter, το μοντέλο GDI device-context, το πρόχειρο των Windows και τα βελτιωμένα metafiles — όλα αυτά είναι δομές Win32 και Win64. Σε άλλους προορισμούς, το υπόλοιπο του HotPDF εξακολουθεί να δημιουργεί, να επεξεργάζεται και να διαβάζει PDF, αλλά αυτή η επιφάνεια εκτύπωσης και πρόχειρου που υποστηρίζεται από το GDI δεν είναι διαθέσιμη. Ο αποδότης στοχεύει επίσης στο κοινό υποσύνολο εγγράφων κειμένου, διαδρομών και εικόνων, οπότε τα προσχέδια σχεδιασμού με έντονες διαβαθμίσεις και τα χρώματα που διαχειρίζεται το ICC εκτυπώνονται ως πιστή προεπισκόπηση και όχι ως χρωματομετρικό δοκίμιο, το ίδιο όριο που φέρει και ο αποδότης bitmap

Για μαζική εκτύπωση σε πολλά αρχεία, φορτώστε και εκτυπώστε κάθε έγγραφο με τη σειρά και παρακολουθήστε τον λογαριασμό μνήμης: το PrintLoadedDocument αποδίζει μία σελίδα τη φορά, αλλά το ίδιο το γράφημα του φορτωμένου εγγράφου ζει στη μνήμη για ολόκληρη την εργασία. Όταν οδηγείτε μια ουρά εκτύπωσης με εκατοντάδες μεγάλα αρχεία, το streaming Direct File API για ροές εργασίας μεγάλων PDF συνδυάζεται καλά με τη διαδρομή εκτύπωσης — διαχωρίστε, συγχωνεύστε ή επιλέξτε πρώτα τις σελίδες που χρειάζεστε στον δίσκο, και στη συνέχεια φορτώστε μόνο το ελαφρύ αποτέλεσμα για εκτύπωση. Με αυτόν τον τρόπο τα PrintLoadedDocument, RenderLoadedPageToDC και RenderLoadedPageToClipboard παρέχονται ως μέρος του HotPDF Component για Delphi και C++Builder — μια εγγενή βιβλιοθήκη VCL χωρίς εξωτερικές εξαρτήσεις DLL που καλύπτει τη δημιουργία, την επεξεργασία, την εξαγωγή κειμένου, την απόδοση PDF, και τώρα την εκτύπωση απευθείας στο TPrinter, σε οποιοδήποτε device context ή στο πρόχειρο