الجواب المختصر عن بطاقة الدعم تلك هو نعم، مع حدود. يُبنى HotPDF 2.730.0 على Free Pascal 3.2.2 و Lazarus 4.6 لـ Win64، ومسارات الإنشاء والتحميل والحفظ الأساسية تعمل. وما لا يتبعها هو أي شيء يقوم على كائن ترميز أصلي مربوط ثابتًا أو على دوال Delphi المجهولة
يصل السؤال بالطريقة نفسها عادةً: فريق يوحّد على Lazarus لأداة عبر المنصات، أو يرث قاعدة كود بـ Free Pascal، ويريد مكوّن PDF نفسه الذي يرخّصه لـ Delphi. نادرًا ما يكون ترحيل مكتبة Delphi ناضجة مسألة صياغة. الجزء المثير هو ما يكشفه الترحيل عن مواضع اقتران المكتبة ضمنًا بسلسلة أدوات واحدة، وفي هذه الحالة يجلس الاقتران في موضعين محددين جدًا: ABI ملفات الكائن لبرمجيات الترميز المضمَّنة، وميزات المصرّف المختبئة خلف رمز إصدار
ما الذي يحتاجه Free Pascal 3.2.2 قبل أن تُترجَم وحدة HPDFDoc
لا يُترجَم HotPDF على Free Pascal إلا في وضع Delphi، وفقط حين تكون أدلة وحدات LCL الخاصة بـ Lazarus ضمن مسار البحث. ولا واحد منهما قابل للمساومة. تضبط HotPDF.inc المصرّف بتوجيهَي {$MODE DELPHI} و{$H+} داخل كتلة {$IFDEF FPC} وترفض أي إصدار أقدم بتوجيه {$FATAL} حين تكون FPC_FULLVERSION أقل من 30202، فتفشل تثبيتة 3.0.x بوضوح بدل أن تنتج وحدة معطوبة. وتضمّن حزمة وقت تشغيل Lazarus HotPDFLaz.lpk الباقي: LCL كحزمة مطلوبة و-Mdelphi كخيار مخصص
متطلب LCL يفاجئ من يريد مخرجات وحدة تحكم فقط، لكنه بنيوي. توفّر HPDFFPCCompat أنواع VCL الخاصة بـ Delphi التي لا مقابل لها في Free Pascal، فتُسقط TMetafile وTMetafileCanvas على أصناف الصور النقطية واللوحات في LCL وتُحيل TRichEdit إلى TMemo، بينما تُحيل HPDFDoc TPNGObject إلى Graphics.TPortableNetworkGraphic. عاملها كرقاقات وقت ترجمة لا كتكافؤ ميزات: صنف metafile مدعوم بصورة نقطية يُبقي الوحدة قابلة للترجمة، لكنه لا يجعل مسارات metafile تسلك ما تسلكه على Delphi. حتى اختبار الدخان بلا واجهة يسحب وحدة Interfaces، ويمرّر سكربت البناء خيار -Fu لكل من lcl\units\x86_64-win64 ودليل خرج lazutils
لماذا لا يصلح D2009+ لأن يكون بوابة الإصدار
يغرى المرء بمعاملة بنية Free Pascal كمصرّف حديث وتعريف رمز ميزات Delphi الأحدث ببساطة. HotPDF لا يفعل ذلك، والسبب يستحق أن يُقال صراحة: D2009+ لا تعني سلاسل Unicode وحدها، بل تتحكم أيضًا بوحدات واجهة API العلنية لها مُعبَّرة بدوال مجهولة. Free Pascal 3.2.2 لا يدعم دوال Delphi المجهولة ولا واجهات API تلك، فاستعارة الرمز ستسحب كودًا يستحيل ترجمته. لذلك تحمل جملة uses في HPDFDoc ذيلين شرطيين منفصلين، والتداخل بينهما متعمَّد لا عرضي
uses
// ...
HPDFJavaScript,
HPDFFormCalcGraph
{$IFDEF FPC}
, HPDFFPCCodecStubs,
HPDFCMS,
HPDFWinCertSigner
{$ENDIF}
{$IFDEF D2009+}
, HPDFXFARuntime,
HPDFCMS,
HPDFWinCertSigner,
HPDFSignVerify,
HPDFSignatureBatch
{$ENDIF};
لماذا تتوقف برمجيات الترميز الأصلية عند الرابط؟
لأنها كائنات COFF لـ Win64 مولَّدة من سلسلة أدوات بعينها، ولا رابط من رابطَي Free Pascal على Win64 يقبلها: لا الرابط الداخلي ولا مسار GNU ld الخارجي. هذه مشكلة ABI ملفات كائن لا مشكلة Pascal، ولا قدر من الكود الشرطي يصلحها. تسلك المكتبة الطريق الأمين الوحيد المتاح. كل توجيه {$L} يسحب كائن ترميز ثابتًا ملفوفٌ في {$IFNDEF FPC}، فتحذفه بنية Free Pascal ببساطة، وتوفّر HPDFFPCCodecStubs بعد ذلك كل رمز خارجي مفقود كبديل يطلق استثناءً بدل أن يُعيد قيمة
// 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;
جدول البدائل ذلك طويل، وقراءته تخبرك بالضبط أي القدرات حكر على Delphi اليوم: نقاط دخول deflate في zlib-ng و zopfli، وضغط libjpeg وفك ضغطه، وترميز OpenJPEG لـ JPEG 2000، وlibtiff ومُهيّئاته لكل نوع ضغط، وترميز JBIG2 وفكّه، ونقاط دخول تحويلات الألوان في Little-CMS، وبدائيات AES. خيار التصميم خلف البدائل أهم من القائمة. الرمز المفقود وقت الربط يعطيك جدار مراجع غير معرّفة من وحدة لم تمسسها قط؛ والبديل الذي يطلق ENotSupportedException يعطيك بنية تعمل، ورسالة تسمّي السبب، وتتبع مكدس يشير إلى موضع الاستدعاء. ويعني ذلك أيضًا أن بنية Free Pascal لا تنتج أبدًا بايتات خاطئة بصمت حيث تنتج بنية Delphi بايتات صحيحة. لاحظ الأثر من الدرجة الثانية أيضًا: تشغيل برمجيات ترميز الصور غير الموثوقة في عملية معزولة قرار لا يظهر إلا في بنية Delphi، لأن بنية Free Pascal لا تملك أصلًا مفككًا أصليًا داخل العملية لعزله في وضع رملي
الضغط: أول سطر يتغير هو cmNone
قبل أن ترحّل أي شيء آخر، عيّن Compression إلى cmNone. تتيح THPDFCompressionMethod قيمتين اثنتين فقط، cmNone وcmFlateDecode، والثانية تتجه مباشرة إلى نقاط دخول deflate التي هي بدائل في بنية Free Pascal. تحقّق من نموذج الكائنات الأساسي أولًا والضغط مطفأ، ثم قرر ما الذي تحتاجه أيضًا. هذا هو الترتيب الذي يسلكه اختبار الدخان الموزَّع: أنشئ مستندًا من صفحة واحدة بلا ضغط، وأعد تحميله، وأكّد أن عدد الصفحات عاد واحدًا. الخرج بلا ضغط أكبر حجمًا، وما يزال 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 يصل إلى رمز بديل
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.
ماذا يحدث للتصيير المتوازي للصفحات؟
ما تزال تُترجَم، وما تزال تُعيد صورًا نقطية صحيحة، لكنها تكفّ عن كونها متوازية. كل من THotPDF.RenderLoadedPagesParallel وTHotPDF.RenderLoadedPagesParallelOrdered مبني على TThread.CreateAnonymousThread بإغلاق procedure مباشر (inline)، وهو ما لا يستطيع Free Pascal 3.2.2 التعبير عنه، لذا يشغّل فرع Free Pascal مسارًا تسلسليًا حتميًا بديلًا: يجتاز فهارس الصفحات بالترتيب، ويستدعي RenderLoadedPageToBitmap لكل منها، ويعدّ النجاحات. شكل API والقيمة المُعادة ومصفوفة الخرج بلا تغيير، وهو ما يتيح لقاعدة كود واحدة أن تُبنى بالطريقتين
var
Bitmaps: THPDFBitmapArray;
Info: THPDFParallelRenderPipelineInfo;
Rendered: Integer;
begin
Rendered := Pdf.RenderLoadedPagesParallel([0, 1, 2, 3], 150, 4,
Bitmaps, Info);
// Delphi: Info.WorkerCount كما سمحت به ميزانية الذاكرة
// Free Pascal: Info.WorkerCount دائمًا 1، والصفحات بترتيب الفهرس
if Info.WorkerCount = 1 then
LogSerialFallback(Rendered, Info.RequestedWorkerCount);
المسار البديل ليس صامتًا، وهذا هو الجزء الجدير بأن تُصمِّم حوله. يملأ THPDFParallelRenderPipelineInfo بأمانة: PageCount من الطلب، وRequestedWorkerCount يعكس ما طلبته، وWorkerCount مضبوطًا على 1، وعددا المكتمل والمسلَّم مطابقين لما عاد فعلًا. الكود الذي يفحص Info أصلًا لتقدير شريط تقدم أو ميزانية ذاكرة يستمر في العمل ويقرأ الحقيقة لا افتراضًا. إن كانت خطة إنتاجيتك تعتمد على خط أنابيب التصيير المتوازي ونموذج الضغط الخلفي، فتلك الخطة خطة Delphi؛ وعلى Free Pascal احسب تكلفة تصيير صفحة إلى صورة نقطية أحادية الخيط مضروبة في عدد الصفحات
أي بنية ينبغي أن توزّعها فعلًا؟
اختر حسب القدرة لا حسب التفضيل. إن كان سير عملك تجميع مستندات أو رسمًا نصيًا ومتجهيًا أو تعبئة نماذج أو تحميلًا وحفظًا، فبنية Free Pascal على Win64 تغطيه، وعليك أن تتحقق والضغط مطفأ قبل تشغيل أي شيء. وإن كان يضم صور JPEG أو JPEG 2000 أو TIFF أو JBIG2، أو تحويلات ألوان ICC، أو خرجًا مضغوطًا، أو إنتاجية تعتمد على أنوية كثيرة، فابقَ على Delphi أو C++Builder في الوقت الحاضر. الحدود يرسمها ABI ملفات كائن وميزة لغوية مفقودة، وكلاهما مرئي في المصدر لا مدفون في مصفوفة دعم، وكلاهما يفشل بخطأ مسمّى لا بنتيجة خاطئة
تأتي حزمة Free Pascal و Lazarus في التوزيعة نفسها مع وحدات Delphi و C++Builder، فيغطي الترخيص كليهما ويمكنك اختبار مسار Lazarus على مستنداتك قبل الالتزام به؛ وتحمل صفحة منتج مكوّن HotPDF لـ PDF في Delphi مصفوفة دعم المصرّفات الحالية ومرجع API الكامل