In de PDFium Component for Delphi and Lazarus gooit de toewijzing TPdf.Active := True nooit wanneer een PDF niet laadt: TPdf.SetActive vangt elke exception op en laat de component inactief. Wilt u de echte fout zien, roep dan in plaats daarvan TPdf.LoadDocument(Options, Report) aan. Die overload werpt de originele exception opnieuw op en vult een TPdfLoadReport met de laadstatus, de native PDFium-foutcode en of de cross-reference-tabel moest worden herbouwd
Het probleem duikt meestal op in batchcode. Een tabelextractie-taak loopt een map van 13 realistische PDF's af met één gedeelde TPdf, en 7 ervan komen als falingen terug. Geen van de 7 bestanden is werkelijk kapot. De except-blokken rond de lading vuren nooit, het log blaamt de verkeerde bestandsnamen, en de eerste zichtbare fout is een kale EPdfError over een inactieve component, geworpen vanuit een property-lezing enkele regels na de lading die werkelijk faalde. Twee afzonderlijke gedragingen stapelen zich op tot dat beeld, en beide doen wat ze ontworpen zijn te doen
Waarom gooit TPdf.Active := True niet op wanneer een PDF niet laadt?
TPdf.SetActive wikkelt LoadDocument in een try..except die elke exception-klasse inslikt en de component simpelweg inactief laat. Het inslikken is met opzet: dezelfde setter draait wanneer een form-designer Active in de IDE omschakelt, en een verkeerd pad mag de IDE niet laten crashen. Op runtinetijd meldt TPdf.Active alleen of er een native documenthandle bestaat, dus na een mislukte lading leest hij False en gebeurt er niets anders. Wat er ook werd gegooid is weg, of het nu een EPdfError uit de parser was, een streamfout of een EAccessViolation uit een halfgebonden pdfium.dll. De gedetailleerde DLL-meldingen uit het diagnosticeren van pdfium.dll-laadfalingen in Delphi bereiken uw handler alleen via een aanroep die ze niet inslikt
Pdf.FileName := FileName;
try
Pdf.Active := True; // SetActive slikt elke laadexception in
except
on E: Exception do
Log.Add(FileName + ': ' + E.Message); // wordt nooit uitgevoerd
end;
// De faling komt hier in plaats daarvan boven, als een algemene EPdfError:
// 'Cannot perform this operation on an inactive Pdf1 component'
Log.Add(Format('%s: %d pages', [FileName, Pdf.PageCount]));
// Minimale fix voor bestaande code: toets Active direct na de toewijzing;
// sinds v3.122.1 bewaart LastLoadReport de tekst van de ingeslikte fout
Pdf.Active := True;
if not Pdf.Active then
Log.Add(FileName + ': load failed: ' + Pdf.LastLoadReport.ErrorMessage);
De faling komt uiteindelijk boven bij de eerste bewaakte aanroep. TPdf.PageCount begint, zoals de meeste documenteigenschappen, met CheckActive, dat een EPdfError gooit die de component noemt maar niet het bestand en niet de oorzaak. Direct na de toewijzing Pdf.Active toetsen verandert een verkeerd toegeschreven crash in een eerlijke "gefaald"-melding. Vóór PDFiumPas v3.122.1 ging de reden daar verloren; sinds v3.122.1 vervangt de mislukte toewijzing LastLoadReport door een plsFailed-report die de fouttekst draagt, dus de oorzaak overleeft het. Het exceptionobject zelf en de audit op byteniveau vereisen nog steeds een andere ingang
Waarom faalt het hergebruik van één TPdf vanaf het tweede bestand?
TPdf.FileName kan alleen worden toegekend terwijl de component inactief is, dus een gedeeld exemplaar wijst het tweede bestand af voordat hij het ooit probeert te laden. TPdf.SetFileName begint met CheckInactive, en dezelfde vangrail bewaakt Password en FormFill. Na de eerste geslaagde lading blijft het exemplaar actief, de volgende toewijzing gooit op, en vangt de batchlus die exception op en gaat verder, dan belandt de fout onder de nieuwe bestandsnaam terwijl het oude document nog open staat. Gemengd met de ingeslikte laadfalingen houdt het log op met de werkelijkheid te matchen. In de reproductie met 13 bestanden meldde een gedeeld exemplaar 7 falingen, terwijl een verse TPdf.Create(nil) per document alle 13 opende. Active := False zetten tussen bestanden werkt ook, maar één exemplaar per document houdt elk bestand vanzelfsprekend geïsoleerd
Wat geeft TPdf.LoadDocument met een TPdfLoadReport u?
TPdf.LoadDocument(const Options: TPdfLoadOptions; out Report: TPdfLoadReport) gooit de echte exception op en vertelt u bovendien in gestructureerde vorm wat er gebeurde. De bestandsoverload laadt FileName; zusteroverloads nemen TBytes of een pointer met grootte, en LoadCustomDocument(AStream, AOwnsStream, Options, Report) dekt streams. Elke één valideert de opties, controleert dat het exemplaar inactief is, draait een audit op byteniveau over de header, startxref, de xref-secties en de %%EOF-markering, en voert dan de native lading uit. De audit is begrensd door het soort limieten dat in parser-resourcebudgetten voor onbetrouwbare PDF's wordt besproken: TPdfLoadOptions.Default zet AuditByteLimit op 256 MiB, MaxIssues op 256, MaxXrefSections op 1024 en MaxXrefEntries op 4.000.000. Bij faling zet de methode Report.Status := plsFailed en gooit opnieuw; omdat Report ter plekke wordt weggeschreven, overleeft zijn inhoud de exception, en een kopie wordt in TPdf.LastLoadReport bewaard
De reportvelden beantwoorden de vragen waar een batchlog werkelijk om vraagt. Status is een van plsNotAttempted, plsLoaded, plsLoadedWithRecovery, plsRejected of plsFailed. NativeErrorCode bevat FPDF_GetLastError, dus FPDF_ERR_PASSWORD (4) scheidt een ontbrekend of verkeerd wachtwoord van een beschadigd bestand dat als FPDF_ERR_FORMAT (3) wordt gemeld. UsedRecovery, CrossReferenceTableValid en RecoveryRoute zeggen of PDFium de xref-tabel moest herbouwen, en Issues zet elke auditbevinding op de lijst met Code, Severity, Offset, ObjectNumber en MessageText, met IssuesTruncated gezet wanneer MaxIssues de lijst kortknipt
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); // één exemplaar per document
try
Pdf.FileName := Files[I];
try
Pdf.LoadDocument(Options, Report);
except
on E: Exception do
begin
// Report wordt gevuld ook al gooide LoadDocument op
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;
Wanneer laadt u met plmStrict?
Gebruik plmStrict telkens wanneer een geruisloos gerepareerd bestand erger is dan een geweigerd, zoals archiefintake, bewijsmateriaalverwerking of een ondertekenpipeline. PDFium reconstrueert geruisloos een kapotte cross-reference-tabel (ISO 32000-1 §7.5.4) door het bestand naar objecten af te zoeken, wat prima is voor een viewer en een probleem voor alles wat exact de bytes moet verwerken die hij kreeg. Na de native lading vraagt de component aan FPDF_DocumentHasValidCrossReferenceTable. In plmCompatible-modus levert een herbouw plsLoadedWithRecovery plus een plicNativeCrossReferenceRebuild-waarschuwing op. In plmStrict-modus lost de component het document, zet plsRejected, voegt plicStrictModeRejected toe en gooit EPdfError met "Strict PDF load rejected the document". Strikte modus wijst ook elke auditfout af, en TPdfLoadOptions.Default(plmStrict) schakelt RequireFinalEndOfFileMarker in, wat een ontbrekende %%EOF of data na de laatste (§7.5.5) van waarschuwing naar fout promoveert. De xref-audit vult de objectniveau-controles uit het valideren van object- en xref-streams met PDFium VCL aan
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; // geldige xref, geen auditfouten
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;
Waar houdt TPdf.LastLoadReport op de waarheid te vertellen?
TPdf.LastLoadReport is pas compleet na een LoadDocument-overload die opties neemt, want alleen die overloads draaien de byte-audit. Een geslaagde Active := True schrijft een compatibele-modus-report zonder byte-audit, dus AuditAttempted blijft False. Vóór PDFiumPas v3.122.1 schreef een mislukte er niets weg, wat betekende dat op een gedeeld exemplaar LastLoadReport nog het vorige bestand beschreef, vaak met een geruststellende plsLoaded. Sinds v3.122.1 vervangt elke mislukte lading het report: een mislukte Active := True, die de component nog steeds inactief laat zonder te gooien, en een mislukte kale LoadDocument- of LoadCustomDocument-aanroep leggen plsFailed vast met de fouttekst, wederom zonder audit. Nog twee gaten doen er in de praktijk toe. Optievalidatie en CheckInactive draaien voordat het report wordt geïnitialiseerd, dus een negatieve AuditByteLimit of een al actief exemplaar gooit op zonder een report te produceren. En NativeErrorCode is alleen betekenisvol wanneer PDFium de parse werkelijk heeft geprobeerd; bij een ontbrekend bestand gooit de wrapper op voordat PDFium draait, dus log ErrorMessage en de exceptiontekst
De praktische regel is kort. Houd Active := True voor aan de designer gekoppelde viewers waar een inactieve component een acceptabele uitslag is. Overal elders, en vooral in batch- en servercode, maakt u één TPdf per document, roept u LoadDocument(Options, Report) aan, vangt u de exception die hij gooit en logt u Report.Status, NativeErrorCode en de foutniveau-Issues samen met de bestandsnaam. De kostprijs is een paar regels per aanroepplek, en elke faling wordt aan het juiste bestand toegeschreven met zijn echte oorzaak
De load-report-API, de strikte modus en de audit op byteniveau verschepen met de PDFium Component for Delphi, C++Builder and Lazarus, naast rendering, tekstextractie, formuliervulling en PDF/A-validatie