Η σύντομη απάντηση σε αυτό το ticket υποστήριξης είναι ναι, με όρια. Το HotPDF 2.730.0 χτίζει σε Free Pascal 3.2.2 και Lazarus 4.6 για Win64, και οι βασικές διαδρομές create, load και save δουλεύουν. Αυτό που δεν ακολουθεί είναι οτιδήποτε στηρίζεται σε statically linked native codec object ή σε ανώνυμες μεθόδους της Delphi
Η ερώτηση συνήθως φτάνει με τον ίδιο τρόπο: μια ομάδα τυποποιεί σε Lazarus για ένα cross-platform εργαλείο, ή κληρονομεί μια codebase Free Pascal, και θέλει το ίδιο PDF component που ήδη έχει license για Delphi. Το porting μιας ώριμης βιβλιοθήκης Delphi σπάνια είναι θέμα σύνταξης. Το ενδιαφέρον μέρος είναι τι αποκαλύπτει το port για το πού ήταν ήσυχα συνδεδεμένη η βιβλιοθήκη με ένα toolchain, και εδώ η σύνδεση κάθεται σε δύο πολύ συγκεκριμένα σημεία: το object-file ABI των bundled codecs, και τις δυνατότητες μεταγλωττιστή που κρύβονται πίσω από ένα σύμβολο έκδοσης
Τι χρειάζεται το Free Pascal 3.2.2 πριν συνταχθεί το HPDFDoc
Το HotPDF συντάσσεται κάτω από Free Pascal μόνο σε Delphi mode, και μόνο όταν οι κατάλογοι units LCL του Lazarus είναι στη διαδρομή αναζήτησης. Κανένα από τα δύο δεν είναι διαπραγματεύσιμο. Το HotPDF.inc αλλάζει τον μεταγλωττιστή με {$MODE DELPHI} και {$H+} μέσα στο block {$IFDEF FPC} του και αρνείται οτιδήποτε παλαιότερο με {$FATAL} όταν το FPC_FULLVERSION είναι κάτω από 30202, ώστε μια εγκατάσταση 3.0.x να αποτυγχάνει δυνατά αντί να παράγει χαλασμένο unit. Το runtime package Lazarus HotPDFLaz.lpk κωδικοποιεί τα υπόλοιπα: LCL ως required package και -Mdelphi ως custom option
Η απαίτηση LCL εκπλήσσει όσους θέλουν μόνο έξοδο κονσόλας, αλλά είναι δομική. Το HPDFFPCCompat προμηθεύει τους τύπους VCL της Delphi για τους οποίους το Free Pascal δεν έχει αντίστοιχο, αντιστοιχίζοντας τα TMetafile και TMetafileCanvas σε κλάσεις bitmap και canvas του LCL και κάνοντας alias το TRichEdit στο TMemo, ενώ το HPDFDoc κάνει alias το TPNGObject στο Graphics.TPortableNetworkGraphic. Αντιμετωπίστε τα ως compile-time shims, όχι ως ισοδυναμία δυνατοτήτων: μια κλάση metafile στηριγμένη σε bitmap κρατά το unit να συντάσσεται, δεν κάνει τις διαδρομές metafile να συμπεριφέρονται όπως στη Delphi. Ακόμα και το non-GUI smoke test τραβάει το Interfaces, και το build script περνά -Fu για lcl\units\x86_64-win64 και τον κατάλογο εξόδου lazutils
Γιατί το D2009+ δεν μπορεί να λειτουργήσει και ως πύλη έκδοσης
Είναι δελεαστικό να αντιμετωπίσετε το Free Pascal build ως σύγχρονο μεταγλωττιστή και απλώς να ορίσετε το νεότερο σύμβολο δυνατοτήτων της Delphi. Το HotPDF δεν το κάνει, και η αιτία αξίζει να ειπωθεί ευθέως: το D2009+ δεν σημαίνει μόνο Unicode strings, φράσσει και units της οποίας το δημόσιο API εκφράζεται με ανώνυμες μεθόδους. Το Free Pascal 3.2.2 δεν υποστηρίζει ούτε ανώνυμες μεθόδους Delphi ούτε εκείνα τα API, οπότε ο δανεισμός του συμβόλου θα έσερνε κώδικα που δεν μπορεί να συνταχθεί. Η uses ρήτρα του HPDFDoc φέρει επομένως δύο ξεχωριστά conditional tails, και η επικάλυψη μεταξύ τους είναι σκόπιμη και όχι τυχαία
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
Γιατί σταματούν τα native codecs στον linker;
Επειδή είναι Win64 COFF objects που εκπέμπει ένα συγκεκριμένο toolchain, και κανένας linker του Free Pascal σε Win64 δεν θα τα καταναλώσει: ούτε ο εσωτερικός linker, ούτε η εξωτερική διαδρομή GNU ld. Αυτό είναι πρόβλημα object-file ABI, όχι πρόβλημα Pascal, και καμιά ποσότητα conditional source δεν το διορθώνει. Η βιβλιοθήκη παίρνει τη μόνη ειλικρινή διαδρομή διαθέσιμη. Κάθε directive {$L} που τραβάει ένα static codec object είναι τυλιγμένο σε {$IFNDEF FPC}, ώστε το Free Pascal build απλώς τα παραλείπει, και το HPDFFPCCodecStubs μετά προμηθεύει κάθε λείπον εξωτερικό σύμβολο ως stub που πετάει exception αντί να επιστρέφει
// HPDFFPCCodecStubs.pas
function HPDFFPCNativeCodecUnavailable: PtrUInt;
begin
raise ENotSupportedException.Create(
'This native codec is not available in the Free Pascal build');
end;
function HPDFFPCStub_deflate: PtrUInt; cdecl;
public name 'deflate';
begin
Result := HPDFFPCNativeCodecUnavailable;
end;
Ο πίνακας stubs είναι μεγάλος, και η ανάγνωσή του σας λέει ακριβώς ποιες δυνατότητες είναι Delphi-only σήμερα: τα entry points deflate του zlib-ng και zopfli, η συμπίεση και αποσυμπίεση libjpeg, το codec OpenJPEG JPEG 2000, το libtiff και οι αρχικοποιήσεις ανά συμπίεση, encode και decode JBIG2, τα entry points μετασχηματισμού χρωμάτων Little-CMS, και τα primitives AES. Η επιλογή σχεδιασμού πίσω από τα stubs μετράει περισσότερο από τη λίστα. Ένα λείπον σύμβολο στον linker σάς δίνει έναν τοίχο undefined references από unit που δεν αγγίξατε ποτέ· ένα stub που πετάει ENotSupportedException σάς δίνει ένα build που τρέχει, ένα μήνυμα που ονομάζει την αιτία, και stack trace που δείχνει το call site. Σημαίνει επίσης ότι ένα Free Pascal build δεν παράγει ποτέ σιωπηλά λάθος bytes εκεί όπου ένα Delphi build θα παρήγαγε σωστά. Σημειώστε και το δευτερογενές αποτέλεσμα: το running untrusted image codecs in an isolated process είναι απόφαση που προκύπτει μόνο στο Delphi build, επειδή ένα Free Pascal build δεν έχει καθόλου in-process native decoder για sandbox
Συμπίεση: η πρώτη γραμμή που αλλάζει είναι cmNone
Πριν κάνετε port οτιδήποτε άλλο, ορίστε το Compression σε cmNone. Το THPDFCompressionMethod προσφέρει ακριβώς δύο τιμές, cmNone και cmFlateDecode, και η δεύτερη δρομολογεί ευθεία στα entry points deflate που είναι stubs σε Free Pascal build. Επαληθεύστε πρώτα το βασικό object model με τη συμπίεση κλειστή, μετά αποφασίστε τι άλλο χρειάζεστε. Αυτή είναι η σειρά που χρησιμοποιεί το shipped smoke test: δημιουργία μονοσέλιδου ασυμπίεστου εγγράφου, επαναφόρτωση, και βεβαίωση ότι το πλήθος σελίδων γύρισε ένα. Η ασυμπίεστη έξοδος είναι μεγαλύτερη, και παραμένει ένα απολύτως έγκυρο PDF
program HotPDFLazarusSmoke;
{$mode delphi}
{$H+}
uses
Interfaces, SysUtils, HPDFDoc;
var
Pdf, Reloaded: THotPDF;
OutputFile: string;
PageCount: Integer;
begin
OutputFile := IncludeTrailingPathDelimiter(GetTempDir) +
'HotPDF-FPC-Smoke.pdf';
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := OutputFile;
Pdf.Compression := cmNone; // το cmFlateDecode φτάνει σε stubbed σύμβολο
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [], 12);
Pdf.CurrentPage.TextOut(72, 72, 0, 'HotPDF Free Pascal smoke test');
Pdf.EndDoc;
finally
Pdf.Free;
end;
Reloaded := THotPDF.Create(nil);
try
PageCount := Reloaded.LoadFromFile(OutputFile);
if PageCount <> 1 then
raise Exception.CreateFmt('Expected one page, got %d', [PageCount]);
finally
Reloaded.Free;
end;
end.
Τι γίνεται με την παράλληλη απόδοση σελίδων;
Εξακολουθεί να συντάσσεται, εξακολουθεί να επιστρέφει σωστά bitmaps, και σταματά να είναι παράλληλη. Το THotPDF.RenderLoadedPagesParallel και το THotPDF.RenderLoadedPagesParallelOrdered χτίζονται πάνω σε TThread.CreateAnonymousThread με inline procedure closure, που το Free Pascal 3.2.2 δεν μπορεί να εκφράσει, ώστε ο κλάδος Free Pascal τρέχει έναν ντετερμινιστικό σειριακό fallback: περπατά τα page indices με τη σειρά, καλεί RenderLoadedPageToBitmap για καθένα, και μετρά τις επιτυχίες. Το σχήμα API, η τιμή επιστροφής και το output array δεν αλλάζουν, και αυτό επιτρέπει σε μια codebase να χτίζεται και με τους δύο τρόπους
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi: το Info.WorkerCount είναι ό,τι επέτρεπε το budget μνήμης
// Free Pascal: το Info.WorkerCount είναι πάντα 1, σελίδες σε σειρά index
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
Το fallback δεν είναι σιωπηλό, και αυτό είναι το μέρος που αξίζει σχεδιασμό γύρω του. Γεμίζει το THPDFParallelRenderPipelineInfo ειλικρινά: PageCount από το αίτημα, RequestedWorkerCount που αντανακλά ό,τι ζητήσατε, WorkerCount ορισμένο σε 1, και τα πλήθη completed και delivered ταιριασμένα με ό,τι όντως γύρισε. Κώδικας που ήδη εξετάζει το Info για να διαστάσει μια μπάρα προόδου ή ένα budget μνήμης συνεχίζει να δουλεύει και διαβάζει την αλήθεια και όχι μια υπόθεση. Αν το σχέδιο throughput σας εξαρτάται από το pipeline παράλληλης απόδοσης και το μοντέλο backpressure του, αυτό το σχέδιο είναι σχέδιο Delphi· σε Free Pascal, προβλέψτε το single-threaded κόστος του rendering σελίδας σε bitmap επί το πλήθος σελίδων
Ποιο build πρέπει πραγματικά να στείλετε;
Διαλέξτε κατά δυνατότητα, όχι κατά προτίμηση. Αν το workflow σας είναι συναρμολόγηση εγγράφων, κείμενο και διανυσματικό σχέδιο, συμπλήρωση φορμών, φόρτωση και αποθήκευση, το Free Pascal build σε Win64 το καλύπτει, και πρέπει να επαληθεύσετε με συμπίεση κλειστή πριν ενεργοποιήσετε οτιδήποτε. Αν περιλαμβάνει εικόνες JPEG ή JPEG 2000 ή TIFF ή JBIG2, μετασχηματισμούς χρωμάτων ICC, συμπιεσμένη έξοδο, ή throughput που εξαρτάται από πολλούς πυρήνες, μείνετε σε Delphi ή C++Builder προς το παρόν. Το όριο χαράσσεται από ένα object-file ABI και ένα λείπον γλωσσικό χαρακτηριστικό, και τα δύο ορατά στον πηγαίο κώδικα και όχι θαμμένα σε μήτρα υποστήριξης, και τα δύο αποτυγχάνουν με επώνυμο σφάλμα και όχι με λάθος αποτέλεσμα
Το πακέτο Free Pascal και Lazarus στέλνεται στην ίδια διανομή με τα units Delphi και C++Builder, ώστε μια άδεια καλύπτει και τα δύο και μπορείτε να δοκιμάσετε τη διαδρομή Lazarus στα δικά σας έγγραφα πριν την υιοθετήσετε· η σελίδα προϊόντος HotPDF Delphi PDF Component φέρει την τρέχουσα μήτρα υποστήριξης μεταγλωττιστών και την πλήρη αναφορά API