Delphi ve Lazarus için PDFium Component'te, bir PDF yüklenemediğinde TPdf.Active := True atamak asla yükseltmez: TPdf.SetActive her istisnayı yakalar ve bileşeni etkin dışı bırakır. Gerçek hatayı görmek için bunun yerine TPdf.LoadDocument(Options, Report) çağırın. O aşırı yükleme özgün istisnayı yeniden yükseltir ve bir TPdfLoadReport'u yükleme durumu, yerli PDFium hata kodu ve çapraz referans tablosunun yeniden kurulmak zorunda kalıp kalmadığıyla doldurur
Sorun genellikle toplu kodda ortaya çıkar. Bir tablo çıkarma işi, tek bir paylaşımlı TPdf ile 13 gerçek dünya PDF'inden oluşan bir klasörü gezer ve 7'si başarısızlık olarak döner. O 7 dosyadan hiçbiri aslında bozuk değildir. Yükleme çevresindeki except blokları hiç tetiklenmez, günlük yanlış dosya adlarını suçlar ve ilk görünür hata, gerçekten başarısız olan yüklemenin birkaç satır sonra yapıldığı bir özellik okumasından yükselen, etkin dışı bileşen hakkında çıplak bir EPdfError'dur. İki ayrı davranış üst üste binerek o tabloyu üretir ve ikisi de tasarlandığı gibi çalışır
Bir PDF yüklenemediğinde TPdf.Active := True neden yükseltmez?
TPdf.SetActive, LoadDocument'i her istisna sınıfını yutan ve bileşeni basitçe etkin dışı bırakan bir try..except içine alır. Yutma bilinçlidir: aynı setter, bir form tasarımcısı IDE'de Active'i değiştirdiğinde de koşar ve kötü bir yol IDE'yi çökertmemelidir. Çalışma zamanında TPdf.Active, yalnızca bir yerli belge tanıtıcısının var olup olmadığını bildirir; başarısız bir yüklemenin ardından False okur ve başka hiçbir şey olmaz. Yükselen her ne ise gitmiştir; ayrıştırıcıdan bir EPdfError, bir akış hatası ya da yarım bağlanmış bir pdfium.dll'den bir EAccessViolation olmuş olsun. Delphi'de pdfium.dll yükleme başarısızlıklarını teşhis makalesinde anlatılan ayrıntılı DLL iletileri, onları yutmayan bir çağrı üzerinden handler'ınıza ulaşır
Pdf.FileName := FileName;
try
Pdf.Active := True; // SetActive her yükleme istisnasını yutar
except
on E: Exception do
Log.Add(FileName + ': ' + E.Message); // hiç koşmaz
end;
// Başarısızlık bunun yerine burada ortaya çıkar; jenerik bir EPdfError olarak:
// 'Cannot perform this operation on an inactive Pdf1 component'
Log.Add(Format('%s: %d pages', [FileName, Pdf.PageCount]));
// Mevcut kod için asgari düzeltme: atamanın hemen ardından Active'i sına;
// v3.122.1'den beri LastLoadReport, yutulan hatanın metnini korur
Pdf.Active := True;
if not Pdf.Active then
Log.Add(FileName + ': load failed: ' + Pdf.LastLoadReport.ErrorMessage);
Başarısızlık nihayet ilk korumalı çağrıda ortaya çıkar. Çoğu belge özelliği gibi TPdf.PageCount, CheckActive ile başlar; bu, bileşeni adandıran ama dosyayı ve nedeni adandırmayan bir EPdfError yükseltir. Atamanın hemen ardından Pdf.Active'i sınamak, yanlış atfedilen bir çöküşü dürüst bir "başarısız" girdisine çevirir. PDFiumPas v3.122.1 öncesinde neden bu noktada kayboluyordu; v3.122.1'den beri başarısız atama, hata metnini taşıyan bir plsFailed raporuyla LastLoadReport'u değiştirir; böylece neden hayatta kalır. İstisna nesnesinin kendisi ile bayt düzeyi denetim hâlâ farklı bir giriş noktası gerektirir
Tek bir TPdf'i yeniden kullanmak ikinci dosyadan itibaren neden başarısız olur?
TPdf.FileName yalnızca bileşen etkin dışıyken atanabilir; paylaşımlı bir örnek, ikinci dosyayı yükmeyi daha denemeden reddeder. TPdf.SetFileName CheckInactive ile başlar ve aynı koruma Password ile FormFill'i de korur. İlk başarılı yüklemenin ardından örnek etkin kalır, sonraki atama yükseltir ve toplu döngü o istisnayı yakalayıp devam ederse hata, eski belge hâlâ açıkken yeni dosya adının altına düşer. Yutulan yükleme başarısızlıklarıyla karışınca günlük, gerçekle eşleşmeyi bırakır. 13 dosyalık yeniden üretimde paylaşımlı bir örnek 7 başarısızlık bildirirken, belge başına taze bir TPdf.Create(nil) 13'ünü de açtı. Dosyalar arasında Active := False ayarlamak da işe yarar; ama belge başına bir örnek, her dosyayı yapısından izole tutar
TPdfLoadReport ile TPdf.LoadDocument size ne kazandırır?
TPdf.LoadDocument(const Options: TPdfLoadOptions; out Report: TPdfLoadReport) gerçek istisnayı yükseltir ve olup biteni yapılandırılmış biçimde de söyler. Dosya aşırı yüklemesi FileName'i yükler; kardeş aşırı yüklemeler TBytes ya da işaretçi ve boyut alır ve LoadCustomDocument(AStream, AOwnsStream, Options, Report) akışları kapsar. Her biri seçenekleri doğrular, örneğin etkin dışı olduğunu denetler, başlığın, startxref'in, xref bölümlerinin ve %%EOF işaretinin bayt düzeyi denetimini koşar, sonra yerli yüklemeyi yapar. Denetim, güvenilmeyen PDF'ler için ayrıştırıcı kaynak bütçeleri makalesinde tartışılan türden sınırlarla çevrilidir: TPdfLoadOptions.Default, AuditByteLimit'i 256 MiB'ye, MaxIssues'i 256'ya, MaxXrefSections'i 1024'e ve MaxXrefEntries'i 4.000.000'a kurar. Başarısızlıkta yöntem Report.Status := plsFailed kurar ve yeniden yükseltir; Report yerinde yazıldığı için içeriği istisnayı aşar ve bir kopyası TPdf.LastLoadReport'ta saklanır
Rapor alanları, bir toplu iş günlüğünün gerçekten gereksindiği soruları yanıtlar. Status, plsNotAttempted, plsLoaded, plsLoadedWithRecovery, plsRejected ya da plsFailed'den biridir. NativeErrorCode, FPDF_GetLastError'ı tutar; böylece FPDF_ERR_PASSWORD (4), FPDF_ERR_FORMAT (3) olarak bildirilen bozuk dosyadan eksik ya da yanlış parolayı ayırır. UsedRecovery, CrossReferenceTableValid ve RecoveryRoute, PDFium'un xref tablosunu yeniden kurmak zorunda kalıp kalmadığını söyler ve Issues, her denetim bulgusunu Code, Severity, Offset, ObjectNumber ve MessageText ile listeler; MaxIssues listeyi kısalttığında IssuesTruncated kurulur
uses
SysUtils, Classes, TypInfo, FPdfView, PDFium;
procedure ProcessBatch(Files, Log: TStrings);
var
I: Integer;
Pdf: TPdf;
Options: TPdfLoadOptions;
Report: TPdfLoadReport;
begin
Options := TPdfLoadOptions.Default(plmCompatible);
for I := 0 to Files.Count - 1 do
begin
Pdf := TPdf.Create(nil); // belge başına bir örnek
try
Pdf.FileName := Files[I];
try
Pdf.LoadDocument(Options, Report);
except
on E: Exception do
begin
// LoadDocument yükselse bile Report dolar
if Report.NativeErrorCode = FPDF_ERR_PASSWORD then
Log.Add(Files[I] + ': password required')
else
Log.Add(Format('%s: %s (%s)', [Files[I],
GetEnumName(TypeInfo(TPdfLoadStatus), Ord(Report.Status)),
E.Message]));
Continue;
end;
end;
if Report.UsedRecovery then
Log.Add(Files[I] + ': opened after PDFium rebuilt the xref table');
ExtractTables(Pdf, Log);
finally
Pdf.Free;
end;
end;
end;
plmStrict ile ne zaman yüklemelisiniz?
Sessizce onarılmış bir dosyanın reddedilmiş olandan daha kötü olduğu her durumda plmStrict kullanın; arşiv kabulü, kanıt işleme ya da bir imzalama hattı gibi. PDFium, bozuk bir çapraz referans tablosunu (ISO 32000-1 §7.5.4) dosyayı nesneler için tarayarak sessizce yeniden kurar; bu, bir görüntüleyici için harikadır ve kendisine verilen baytları tam olarak işlemesi gereken her şey için bir sorundur. Yerli yüklemenin ardından bileşen FPDF_DocumentHasValidCrossReferenceTable'ı sorar. plmCompatible modunda bir yeniden kurulum, plsLoadedWithRecovery artı bir plicNativeCrossReferenceRebuild uyarısı verir. plmStrict modunda bileşen belgeyi boşaltır, plsRejected kurar, plicStrictModeRejected ekler ve "Strict PDF load rejected the document" iletisiyle EPdfError yükseltir. Katı mod ayrıca her denetim hatasını reddeder ve TPdfLoadOptions.Default(plmStrict), RequireFinalEndOfFileMarker'ı açar; bu, eksik bir %%EOF'u ya da sondakinin ardındaki veriyi (§7.5.5) uyarıdan hataya çeker. Xref denetimi, PDFium VCL ile object ve xref akışlarını doğrulama makalesindeki nesne düzeyi denetimleri tamamlar
function AcceptForArchive(const FileName: string; out Reason: string): Boolean;
var
Pdf: TPdf;
Report: TPdfLoadReport;
I: Integer;
begin
Result := False;
Reason := '';
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
try
Pdf.LoadDocument(TPdfLoadOptions.Default(plmStrict), Report);
Result := True; // geçerli xref, denetim hatası yok
except
on E: EPdfError do
begin
Reason := E.Message;
for I := 0 to High(Report.Issues) do
if Report.Issues[I].Severity = plisError then
Reason := Reason + sLineBreak + Format(' at offset %d: %s',
[Report.Issues[I].Offset, Report.Issues[I].MessageText]);
end;
end;
finally
Pdf.Free;
end;
end;
TPdf.LastLoadReport gerçeği söylemeyi nerede bırakır?
TPdf.LastLoadReport, yalnızca seçenek alan bir LoadDocument aşırı yüklemesinden sonra tamdır; çünkü bayt denetimini koşan yalnızca o aşırı yüklemelerdir. Başarılı bir Active := True, bayt denetimsiz uyumlu mod bir rapor yazar; böylece AuditAttempted False kalır. PDFiumPas v3.122.1 öncesinde başarısız olan hiçbir şey yazmıyordu; bu, paylaşımlı bir örnek üzerinde LastLoadReport'un hâlâ önceki dosyayı betimlediği, çoğu zaman güven veren bir plsLoaded ile olduğu anlamına gelirdi. v3.122.1'den beri her başarısız yükleme raporu değiştirir: hâlâ yükseltmeden bileşeni etkin dışı bırakan başarısız bir Active := True ile başarısız bir düz LoadDocument ya da LoadCustomDocument çağrısı, hata metniyle plsFailed kaydeder; bunlar da denetimsizdir. Pratikte önemli iki boşluk daha vardır. Seçenek doğrulaması ile CheckInactive, rapor kurulmadan önce koşar; negatif bir AuditByteLimit ya da hâlihazırda etkin bir örnek, rapor üretmeksizin yükseltir. Bir de NativeErrorCode, yalnızca PDFium ayrıştırmayı fiilen denediğinde anlamlıdır; eksik dosya için sarmalayıcı, PDFium koşmadan önce yükseltir; o yüzden bunun yerine ErrorMessage'i ve istisna metnini günlüğe yazın
Pratik kural kısadır. Active := True'yu, etkin dışı bileşenin kabul edilebilir bir sonuç olduğu tasarımcıya bağlı görüntüleyicilerde tutun. Geri kalan her yerde ve her şeyden önce toplu iş ile sunucu kodunda, belge başına bir TPdf kurun, LoadDocument(Options, Report) çağırın, yükselttiği istisnayı yakalayın ve Report.Status, NativeErrorCode ile hata düzeyindeki Issues'i dosya adıyla birlikte günlüğe yazın. Maliyet, çağrı noktası başına birkaç satırdır ve her başarısızlık, gerçek nedeniyle doğru dosyaya atfedilir
Yükleme raporu API'si, katı mod ve bayt düzeyi denetim, Delphi, C++Builder ve Lazarus için PDFium Component ile birlikte gelir; yanında render, metin çıkarma, form doldurma ve PDF/A doğrulaması da vardır