I PDFium Component for Delphi og Lazarus reiser aldri å tildele TPdf.Active := True noe når en PDF feiler å laste: TPdf.SetActive fanger ethvert unntak og lar komponenten forbli inaktiv. For å se den virkelige feilen, kall TPdf.LoadDocument(Options, Report) i stedet. Den overloaden reiser det opprinnelige unntaket på nytt og fyller en TPdfLoadReport med lastestatusen, den opprinnelige PDFium-feilkoden og om kryssreferansetabellen måtte bygges på nytt
Problemet dukker vanligvis opp i batchkode. Et tabelluttrekksjobb går gjennom en mappe med 13 ekte PDF-er med én delt TPdf, og 7 av dem kommer tilbake som feil. Ingen av de 7 filene er faktisk ødelagt. except-blokkene rundt lastingen fyres aldri, loggen skylder på feil filnavn, og den første synlige feilen er en naken EPdfError om en inaktiv komponent, reist fra en egenskapslesing flere linjer etter lastingen som faktisk feilet. To separate oppførsler stablet seg opp for å produsere det bildet, og begge fungerer som designet
Hvorfor reiser ikke TPdf.Active := True noe når en PDF feiler å laste?
TPdf.SetActive pakker LoadDocument inn i en try..except som svelger enhver unntaksklasse og rett og slett lar komponenten forbli inaktiv. Svelgingen er bevisst: samme setter kjører når en formdesigner veksler Active i IDE-en, og en dårlig sti må ikke krasje IDE-en. Ved kjøretid rapporterer TPdf.Active bare om et opprinnelig dokumenthåndtak finnes, så etter en feilet lasting leser den False og ellers ingenting. Hva ennå som ble reist, er borte, enten det var en EPdfError fra parseren, en strømfeil eller en EAccessViolation fra en halvt bundet pdfium.dll. De detaljerte DLL-meldingene beskrevet i å diagnostisere pdfium.dll lastefeil i Delphi når håndtereren din bare gjennom et kall som ikke svelger dem
Pdf.FileName := FileName;
try
Pdf.Active := True; // SetActive svelger ethvert lasteunntak
except
on E: Exception do
Log.Add(FileName + ': ' + E.Message); // kjører aldri
end;
// Feilen viser seg her i stedet, som en generisk EPdfError:
// 'Cannot perform this operation on an inactive Pdf1 component'
Log.Add(Format('%s: %d pages', [FileName, Pdf.PageCount]));
// Minimal fiks for eksisterende kode: test Active rett etter tildelingen;
// siden v3.122.1 beholder LastLoadReport teksten til den svelgede feilen
Pdf.Active := True;
if not Pdf.Active then
Log.Add(FileName + ': load failed: ' + Pdf.LastLoadReport.ErrorMessage);
Feilen viser seg til slutt ved det første voktede kallet. TPdf.PageCount, som de fleste dokumentegenskaper, starter med CheckActive, som reiser en EPdfError som navngir komponenten men ikke filen og ikke årsaken. Å teste Pdf.Active umiddelbart etter tildelingen gjør en feilattribuert krasj om til en ærlig «feilet»-oppføring. Før PDFiumPas v3.122.1 var grunnen tapt på det punktet; siden v3.122.1 erstatter den feilede tildelingen LastLoadReport med en plsFailed-rapport som bærer feilteksten, så årsaken overlever. Unntaksobjektet selv og bytenivå-revisjonen krever fortsatt et annet inngangspunkt
Hvorfor feiler gjenbruk av én TPdf fra og med den andre filen?
TPdf.FileName kan bare tildeles mens komponenten er inaktiv, så en delt instans avviser den andre filen før den noen gang prøver å laste den. TPdf.SetFileName starter med CheckInactive, og samme vakt beskytter Password og FormFill. Etter den første vellykkede lasting forblir instansen aktiv, neste tildeling reiser, og hvis batchløkken fanger det unntaket og går videre, lander feilen under det nye filnavnet mens det gamle dokumentet fortsatt er åpent. Blandet med de svelgede lastefeilene slutter loggen å stemme med virkeligheten. I 13-fil-reproduksjonen rapporterte en delt instans 7 feil, mens en fersk TPdf.Create(nil) per dokument åpnet alle 13. Å sette Active := False mellom filer fungerer også, men én instans per dokument holder hver fil isolert av konstruksjon
Hva gir TPdf.LoadDocument med en TPdfLoadReport deg?
TPdf.LoadDocument(const Options: TPdfLoadOptions; out Report: TPdfLoadReport) reiser det virkelige unntaket og forteller deg også hva som skjedde i strukturert form. Fil-overloaden laster FileName; søster-overloader tar TBytes eller en peker og størrelse, og LoadCustomDocument(AStream, AOwnsStream, Options, Report) dekker strømmer. Hver av dem validerer opsjonene, sjekker at instansen er inaktiv, kjører en bytenivå-revisjon av headeren, startxref, xref-seksjonene og %%EOF-markøren, og utfører så den opprinnelige lastingen. Revisjonen er avgrenset av samme slags grenser diskutert i parser-ressursbudsjetter for utrustede PDF-er: TPdfLoadOptions.Default setter AuditByteLimit til 256 MiB, MaxIssues til 256, MaxXrefSections til 1024 og MaxXrefEntries til 4 000 000. Ved feil setter metoden Report.Status := plsFailed og reiser på nytt; etter som Report skrives på stedet, overlever innholdet unntaket, og en kopi lagres i TPdf.LastLoadReport
Rapportfeltene svarer på spørsmålene en batchlogg faktisk trenger. Status er én av plsNotAttempted, plsLoaded, plsLoadedWithRecovery, plsRejected eller plsFailed. NativeErrorCode holder FPDF_GetLastError, så FPDF_ERR_PASSWORD (4) skiller et manglende eller galt passord fra en skadet fil rapportert som FPDF_ERR_FORMAT (3). UsedRecovery, CrossReferenceTableValid og RecoveryRoute sier om PDFium måtte bygge xref-tabellen på nytt, og Issues lister hvert revisjonsfunn med Code, Severity, Offset, ObjectNumber og MessageText, med IssuesTruncated satt når MaxIssues kortet listen ned
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 instans per dokument
try
Pdf.FileName := Files[I];
try
Pdf.LoadDocument(Options, Report);
except
on E: Exception do
begin
// Report fylles selv om LoadDocument reiste
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;
Når bør du laste med plmStrict?
Bruk plmStrict når en stille reparert fil er verre enn en avvist én, som arkivinntak, bevishåndtering eller en signeringspipeline. PDFium rekonstruerer i stillhet en ødelagt kryssreferansetabell (ISO 32000-1 §7.5.4) ved å skanne filen etter objekter, noe som er flott for en viser og et problem for alt som må behandle nøyaktig bytene det fikk. Etter den opprinnelige lastingen spør komponenten FPDF_DocumentHasValidCrossReferenceTable. I plmCompatible-modus gir en gjenoppbygging plsLoadedWithRecovery pluss en plicNativeCrossReferenceRebuild-advarsel. I plmStrict-modus losses dokumentet, plsRejected settes, plicStrictModeRejected legges til og EPdfError reises med «Strict PDF load rejected the document». Streng modus avviser også enhver revisjonsfeil, og TPdfLoadOptions.Default(plmStrict) slår på RequireFinalEndOfFileMarker, som oppgraderer en manglende %%EOF eller data etter den siste (§7.5.5) fra advarsel til feil. Xref-revisjonen kompletterer objektnivå-sjekkene i å validere objekt- og xref-strømmer med PDFium VCL
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; // gyldig xref, ingen revisjonsfeil
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;
Hvor slutter TPdf.LastLoadReport å si sannheten?
TPdf.LastLoadReport er komplett bare etter en LoadDocument-overload som tar opsjoner, for bare de overloadene kjører bytenivå-revisjonen. En vellykket Active := True skriver en compatible-modus-rapport uten byte-revisjon, så AuditAttempted forblir False. Før PDFiumPas v3.122.1 skrev en feilet én ingenting, noe som betydde at på en delt instans beskrev LastLoadReport fortsatt den forrige filen, ofte med en beroligende plsLoaded. Siden v3.122.1 erstatter hver feilede lasting rapporten: en feilet Active := True, som fortsatt lar komponenten forbli inaktiv uten å reise, og et feilet rent LoadDocument- eller LoadCustomDocument-kall registrerer plsFailed med feilteksten, igjen uten en revisjon. To hull til betyr noe i praksis. Opsjonsvalidering og CheckInactive kjører før rapporten initialiseres, så en negativ AuditByteLimit eller en allerede aktiv instans reiser uten å produsere en rapport. Og NativeErrorCode er meningsfull bare når PDFium faktisk forsøkte parset; for en manglende fil reiser wrapperen før PDFium kjører, så logg ErrorMessage og unntaksteksten i stedet
Den praktiske regelen er kort. Behold Active := True for designerbundne visere der en inaktiv komponent er et akseptabelt utfall. Alle andre steder, og fremfor alt i batch- og serverkode, lag én TPdf per dokument, kall LoadDocument(Options, Report), fang unntaket det reiser og logg Report.Status, NativeErrorCode og feilnivå-Issues sammen med filnavnet. Kostnaden er noen få linjer per kallsted, og hver feil blir attribuert til riktig fil med sin virkelige årsak
Lasterapport-API-et, strenge modus og bytenivå-revisjonen følger med PDFium Component for Delphi, C++Builder og Lazarus, sammen med rendring, tekstuttrekk, utfylling av skjemaer og PDF/A-validering