يتلخص عارض PDF في Delphi في مكونين والتوصيل بينهما. يمتلك TPdf المستند: فهو يفتح الملف، ويفك تشفيره، ويجيب على أسئلة حول عدد الصفحات والبيانات الوصفية. TPdfView هو عنصر التحكم المرئي الذي يرسم الصفحات على الشاشة ويتعامل مع التمرير، والتكبير/التصغير، والصفحة التي ينظر إليها المستخدم حاليًا. يغلف PDFium Component نفس محرك العرض الذي يتم شحنه داخل Chrome، وبالتالي فإن الصور الرمزية، وصقل الحواف، واللون الذي تحصل عليه على القماش يتطابق مع ما يراه المستخدمون بالفعل في متصفحهم. العمل ليس في العرض. بل يكمن في توصيل كائن المستند بالعرض، والتحميل دون التعطل بسبب ملف تالف أو محمي بكلمة مرور، وإعطاء المستخدم حفنة من عناصر التحكم التي تجعل العارض يبدو مكتملًا: تقليب الصفحة، وتغيير التكبير/التصغير، وملاءمة الصفحة مع النافذة
يستعرض هذا عملية التجميع بالترتيب الذي ستقوم بالبناء به فعليًا. كل شيء هنا يعرض صفحة واحدة في كل مرة، وهو ما تريده معظم مسارات عمل المستندات. إذا كنت بحاجة إلى تكديس الصفحات في عمود تمرير مستمر واحد، فهذا قرار تخطيط مختلف وليس المسار هنا
توصيل TPdf بـ TPdfView
أسقط TPdf و TPdfView على النموذج، ثم أخبر العرض بالمستند الذي يجب عرضه. هذا التعيين الفردي هو الرابط بأكمله بين المستند غير المرئي وعنصر التحكم الذي يرسمه
procedure TFormMain.FormCreate(Sender: TObject);
begin
// تم إسقاط Pdf و PdfView في وقت التصميم.
PdfView.Pdf := Pdf; // يعرض العرض ما يحمله هذا المستند
PdfView.FitMode := pfmFitWidth; // ابدأ المستخدم بتكبير معقول
end;
قبل تشغيل أي من هذا، يجب أن تكون مكتبة PDFium الأصلية موجودة على الجهاز. يستدعي PDFium Component ملف pdfium32.dll أو pdfium64.dll اعتمادًا على النظام الأساسي المستهدف، ويرفض المستند ببساطة الفتح إذا تعذر العثور على ملف DLL. قم بشحن ملف DLL المطابق بجانب الملف القابل للتنفيذ الخاص بك، أو ضعه حيث سيجده محمل النظام. لا توجد الإصدارات التي تدعم V8 إلا لملفات PDF التي تحمل JavaScript ترغب في تنفيذه، وهو ما لا يفعله عارض عادي، لذا استخدم ملف DLL القياسي ما لم يكن لديك سبب ملموس لعدم القيام بذلك
تحميل مستند دون الوثوق بالمدخلات
الغريزة هي تغليف التحميل في try/except ومعاملة الاستثناء المطروح كفشل. هذه الغريزة خاطئة هنا، والخطأ فيها ينتج عارضًا يبدو جيدًا حتى يسلمه شخص ما ملفًا مكسورًا. لا يؤدي تعيين Active := True إلى طرح استثناء عند فشل التحميل. يكتشف PDFium Component الخطأ الداخلي ويترك Active على الحالة False، لذا فإن الطريقة الصادقة الوحيدة لمعرفة ما إذا كان المستند قد فُتح هي إعادة قراءة الخاصية بعد تعيينها
procedure TFormMain.OpenDocument(const FileName: string);
begin
Pdf.FileName := FileName;
Pdf.Active := True; // لا يطرح استثناء؛ الفشل يترك Active = False
if not Pdf.Active then
begin
ShowMessage('Could not open ' + FileName);
Exit;
end;
PdfView.PageNumber := 1; // يتتبع العرض صفحته الحالية بنفسه
UpdatePageLabel;
end;
هناك شيئان يستحقان الاهتمام. الأول هو أن PageNumber موجود في كلا الكائنين وهما مستقلان. Pdf.PageNumber هو مفهوم المستند للصفحة الحالية؛ PdfView.PageNumber هي الصفحة التي يعرضها عنصر التحكم فعليًا، وهي الصفحة التي قمت بتعيينها لنقل المستخدم عبر الملف. تعيين أحدهما لا يحرك الآخر، لذلك يقوم العارض دائمًا بقيادة خاصية العرض. الثاني هو الفهرسة المستندة إلى 1: تعمل الصفحات من 1 إلى Pdf.PageCount، وليس من 0، مما يوقع أي شخص معتاد على المصفوفات المستندة إلى الصفر في الخطأ
التعامل مع ملف مشفر
تُطوى المستندات المشفرة في نفس مسار التحميل. إذا تم تعيين كلمة مرور الفتح قبل التنشيط، فسيتم فك تشفير المستند عند فتحه؛ وإذا كانت خاطئة أو مفقودة، فستظل Active بحالة False تمامًا كما تفعل مع ملف تالف. لذا فإن الاسترداد يتمثل في المطالبة بكلمة مرور ومحاولة التنشيط مرة أخرى
procedure TFormMain.OpenWithPassword(const FileName: string);
var
Password: string;
begin
Pdf.FileName := FileName;
Pdf.Active := True;
if not Pdf.Active then
begin
if InputQuery('Password required', 'Password:', Password) then
begin
Pdf.Password := Password; // يجب ضبطها قبل Active := True
Pdf.Active := True;
end;
if not Pdf.Active then
begin
ShowMessage('Unable to open the document.');
Exit;
end;
end;
PdfView.PageNumber := 1;
end;
نظرًا لأن الفشل يكون صامتًا لكل من كلمة المرور الخاطئة والملف التالف، فلا يمكنك التمييز بينهما من خلال Active وحدها. في الممارسة العملية، هذا مقبول للعارض: فإما أن يوفر المستخدم كلمة المرور الصحيحة أو يعلم أن الملف لن يفتح، والرسالة تقرأ نفس الشيء في كلتا الحالتين
تصفح المستند
مع فتح المستند، يكون التنقل حسابيًا على PdfView.PageNumber محددًا بـ Pdf.PageCount. العمل الحقيقي الوحيد هو التثبيت، لذلك لا تدفع الأزرار الصفحة أبدًا خارج النطاق وتظل أزرار الأول والأخير معطلة في نهايات الملف
procedure TFormMain.GoToPage(NewPage: Integer);
begin
if not Pdf.Active then
Exit;
if NewPage < 1 then
NewPage := 1
else if NewPage > Pdf.PageCount then
NewPage := Pdf.PageCount;
PdfView.PageNumber := NewPage;
UpdatePageLabel;
end;
// أزرار التنقل الأربعة تختزل إلى استدعاء واحد لكل منها
procedure TFormMain.FirstClick(Sender: TObject); begin GoToPage(1); end;
procedure TFormMain.PrevClick(Sender: TObject); begin GoToPage(PdfView.PageNumber - 1); end;
procedure TFormMain.NextClick(Sender: TObject); begin GoToPage(PdfView.PageNumber + 1); end;
procedure TFormMain.LastClick(Sender: TObject); begin GoToPage(Pdf.PageCount); end;
يعد مربع نص "انتقال إلى الصفحة N" هو نفس استدعاء GoToPage المغذى من عدد صحيح تم تحليله، ويغطي التثبيت الحالة التي يكتب فيها المستخدم 9999 في ملف مكون من عشر صفحات. احتفظ بـ UpdatePageLabel باعتباره المكان الوحيد الذي يكتب "الصفحة 3 من 12" حتى لا تخرج القراءة عن التزامن مع ما يظهره العرض أبدًا
التكبير/التصغير: النسب المئوية الصريحة وأوضاع الملاءمة
يصل التكبير/التصغير في TPdfView بنكهتين تتفاعلان، وفهم التفاعل هو الفرق بين تحكم التكبير الذي يتصرف بشكل جيد والتحكم الذي يحارب المستخدم. المسار المباشر هو خاصية Zoom، وهي نسبة مئوية حيث يعني 100 الحجم الفعلي. المسار الآخر هو FitMode، والذي يخبر العرض بحساب التكبير نيابة عنك والاستمرار في إعادة حسابه عند تغيير حجم النافذة
// تكبيرات ثابتة
PdfView.Zoom := 100; // الحجم الفعلي
PdfView.Zoom := 50; // النصف
PdfView.Zoom := 200; // الضعف
// اجعل العرض يضبط حجم الصفحة على النافذة ويحافظ عليه عند تغيير حجمها
PdfView.FitMode := pfmFitWidth; // عرض الصفحة يملأ عنصر التحكم
PdfView.FitMode := pfmFitPage; // الصفحة كاملة مرئية
PdfView.FitMode := pfmActualSize; // 1:1 مع نقاط المستند
إليك الجزء الذي يوقع الناس في الخطأ. يؤدي تعيين Zoom مباشرة إلى إعادة تعيين FitMode إلى pfmNone. هذا سلوك صحيح، وليس خطأ: في اللحظة التي يختار فيها المستخدم 150٪ بالضبط، لم يعد بإمكان العرض أن يحترم "الملاءمة للعرض"، لأن الطلبين يتعارضان. النتيجة على واجهة المستخدم الخاصة بك هي أن زر التكبير وزر ملاءمة الصفحة هما حالتان متنافيتان، ويجب أن يجعل شريط الأدوات الوضع النشط مرئيًا. عندما ينقر المستخدم على الملاءمة للصفحة، قم بتعيين FitMode؛ وعندما ينقرون على تكبير/تصغير رقمي، قم بتعيين Zoom واتركه يمسح وضع الملاءمة بمفرده
إذا كنت تفضل حساب قيمة الملاءمة بنفسك، ربما لبذر منزلق التكبير بالنسبة المئوية الحالية للملاءمة، فإن المساعدين لكل صفحة يمنحونك الأرقام دون تغيير الوضع. تُرجع PageWidthZoom[N] و PageZoom[N] و ActualSizeZoom[N] النسبة المئوية التي من شأنها ملاءمة الصفحة N للعرض، أو ملاءمتها بالكامل، أو عرضها بحجمها الفعلي
// زرع قراءة تكبير من قيمة الملاءمة للعرض للصفحة الحالية
var
FitPercent: Double;
begin
FitPercent := PdfView.PageWidthZoom[PdfView.PageNumber];
ZoomEdit.Text := Format('%.0f%%', [FitPercent]);
end;
ما يحتاجه العارض النهائي حقًا
يحتوي العارض أعلاه على بضع عشرات من الأسطر، وهو يقوم بالفعل بالمهمة التي يحتاجها مسار عمل المستندات: فتح ملف، والنجاة من ملف سيء، وإظهار صفحة، والتنقل بين الصفحات، وتغيير التكبير يدويًا أو بالملاءمة. يقوم PDFium بالأجزاء الصعبة بصمت. يتم حل الخطوط المضمنة، وتُرسم التعليقات التوضيحية وحقول النموذج حيث يضعها المستند، وتتطابق الصفحة التي تراها مع تلك التي سيراها مستخدم Chrome، لأنه نفس المحرك الذي يرسم كليهما
من هذه القاعدة، تكون الإضافات تزايدية وليست هيكلية. يقرأ تحديد النص والبحث من نفس طبقة النص التي يبنيها PDFium بالفعل؛ وتكون البيانات الوصفية مثل Pdf.Title و Pdf.Author على بُعد قراءة خاصية واحدة؛ والتناوب والتدرج الرمادي عبارة عن خيارات عرض تمررها عندما ترسم صفحة إلى صورة نقطية. لا يغير أي من ذلك العمود الفقري الذي لديك هنا، وهو كائن المستند والعرض وتدفق التحميل ثم التنقل الذي يربط بينهما. اجعل ذلك العمود الفقري صحيحًا والباقي مجرد زخرفة
مكونات TPdf و TPdfView المستخدمة طوال الوقت هي جزء من PDFium Component لـ Delphi و C++Builder، والذي يحمل مرجع العارض الكامل على صفحة المنتج الخاصة به