Teknisk artikel

XFDF-eksport og -import i Delphi med PDFium Component

PDFium Component til Delphi og Lazarus udveksler formulardata og annotationer via XFDF, XML-udvekslingsformatet defineret af ISO 19444-1. TPdf.ExportXFDF serialiserer enhver formularfeltværdi og enhver understøttet annotation i det indlæste dokument til en XFDF-fil eller TStream; TPdf.ImportXFDF læser et XFDF-dokument tilbage, genskaber annotationer på deres sider og anvender feltværdier. Den ene tur-retur dækker to arbejdsgange, som ethvert dokumentprogram før eller siden får brug for: En anmelder markerer en PDF i Acrobat og sender dig kommentarerne til sammenfletning, eller en overholdelsesregel siger, at formulardata skal leve i en separat fil, der kan sammenlignes og revideres, frem for at blive bagt ind i selve PDF'en

Understøtter PDFium eksport og import af XFDF?

Det gør selve PDFium-biblioteket ikke. Den indfødte C-API har overhovedet ingen XFDF- eller FDF-funktioner — intet i dens offentlige headere læser eller skriver nogen af formaterne, så ingen mængde indpakning bringer dig dertil. PDFium Component implementerer derfor hele XFDF-motoren i Pascal, i en dedikeret enhed, der bygger og parser XML'en direkte oven på den eksisterende TPdf-annotation og formularfelt-datamodel. Skriveren udsender XML'en i hånden, og læseren er en håndskrevet tilstandsmaskine-parser, så funktionen tilføjer aucune afhængighed af Delphi XMLDoc eller FPC DOM-enhederne og opfører sig identisk på Delphi og Lazarus

Dette designvalg betyder noget, når du evaluerer alternativer. Hvis du har kaldt rå FPDF_*-funktioner fra Pascal, is XFDF en mur: Funktionen er simpelthen ikke i DLL'en. Komponenten krydser denne mur by treating XFDF as a pure serialization problem — indsamle de feltværdier og annotationsposter, som indpakningen allerede forstår at læse, skrive dem ud som standard XML og vende processen om ved import

Hvad indeholder en XFDF-fil?

ISO 19444-1 definerer XFDF som XML-repræsentationen af FDF, og dens indholdspakke deler sig i to blokke på øverste niveau. <fields>-elementet bærer hierarkiske formularfeltnavne og deres værdier — alt, hvad en bruger har skrevet, valgt eller markeret i en AcroForm. <annots>-elementet bærer annotationerne: TPdf.ExportXFDF udsender 18 undertyper — tekst, fremhævning, understregning, gennemstregning, bølgestreg, linje, cirkel, firkant, caret, polygon, polylinje, stempel, blæk, fritext, filvedhæftning, lyd, link og redigering. Widget-annotationer mangler bevidst i <annots>, fordi deres data rejser i <fields>, og fremviser-interne undertyper som f.eks. Popup er ikke en del af XFDF-ordforrådet

For at gøre turen frem og tilbage trofast blev posten TPdfAnnotation udvidet med de metadata, som XFDF forventer: Name (den unikke NM-identifikator), Subject, ModificationDate, CreationDate, Icon, Opacity, linje-endepunkter, polygon- og polylinje-hjørner samt blækbevægelsesstier. Hvert nyt felt parres med en Has*-boolsk skildvagt, så kode, der byggede annotationer mod tidligere versioner, bliver ved med at kompilere og producere de samme ordbøger — et uindstillet felt skrives aldrig. Hvis du allerede opretter markeringer programmatisk, bærer den samme post, du bruger til tekstmarkeringsannotationer med fire punkter, nu alt, hvad XFDF har brug for

Hvordan eksporterer du formulardata og annotationer til XFDF?

Et enkelt kald klarer hele dokumentet. Indlæs PDF'en med formularudfyldning aktiveret, kald ExportXFDF, og komponenten gennemgår hver side, indsamler feltværdier og annotationer og skriver XML'en. Returværdien er antallet af skrevne UTF-8-bytes, hvilket gør et billigt kvalitetstjek i logfiler muligt

var
  Pdf: TPdf;
  Bytes: Integer;
begin
  Pdf := TPdf.Create(Self);
  Pdf.FormFill := True;            // needed so field values are live
  Pdf.FileName := 'expense-report.pdf';
  Pdf.Active := True;
  Bytes := Pdf.ExportXFDF('expense-report.xfdf');
  ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;

Begge retninger har en TStream-overbelastning, så intet tvinger en midlertidig fil på disken. Eksport til en TMemoryStream er den naturlige form, når XFDF'en er på vej to et HTTP-svar, en database-blob eller en signeret arkivpost

var
  Buffer: TMemoryStream;
begin
  Buffer := TMemoryStream.Create;
  try
    Pdf.ExportXFDF(Buffer);        // same XML, no file involved
    Buffer.Position := 0;
    // hand the stream to a web response, blob column, or zip entry
  finally
    Buffer.Free;
  end;
end;

Hvordan fletter ImportXFDF kommentarer tilbage i dokumentet?

Importsiden er der, hvor anmelderens arbejdsgang afsluttes. En kollega annoterer kontrakten i Acrobat, eksporterer kommentarer som XFDF og sender dig et par kilobyte XML i stedet for en ekstra kopi af PDF'en. TPdf.ImportXFDF parser denne fil, opretter hver annotation på den side, som dens page-attribut navngiver, og skriver feltværdier i de matchende widget-annotationer. Funktionen returnerer det samlede antal anvendte felter plus annotationer, så brugerfladen kan bekræfte præcis, hvor meget der ankom

var
  Applied: Integer;
begin
  Applied := Pdf.ImportXFDF('review-comments.xfdf');
  StatusBar.SimpleText :=
    Format('%d fields and annotations merged', [Applied]);
end;

Parseren er en håndskrevet tag-scanner bygget til tolerance frem for strenghed: Ukendte elementer springes over, behandlingsinstruktioner, DOCTYPE-erklæringer og kommentarer ignoreres, ukendte flagnavne droppes, og attributværdier undviges via det fulde sæt af XML-enheds- og numeriske tegnreferencer, inklusive UTF-16-surrogatpar. XFDF produceret af Acrobat, af en webfremviser eller af et andet værktøj flettes uden dikkedarer. Når annotationerne er i dokumentet, fortsætter anmeldelsescyklussen med det svar- og statusmaskineri der er beskrevet i opbygning af et annotationsanmeldelsesworkflow med PDFium-komponenten, og importerede feltværdier deltager i normal tabulatorrækkefølge-logik dækket i formularfeltnavigation

Hvorfor betyder decimaltegn noget for XFDF?

Enhver annotation i XFDF placeres af koordinatstrenge — rect="70.5,540,200,560" og lignende — og ISO 19444-1 kræver punktum som decimaltegn. Delphis standard-flydende formatering følger Windows-landestandarden, så på et tysk eller fransk system forvandler en naiv FloatToStr 70.5 til 70,5, hvilket ødelægger en kommasepareret koordinatliste til vrøvl, som andre processorer afviser eller misforstår. PDFium Component normaliserer hvert tal, den skriver: Landestandardens decimaltegn omskrives til et punktum, og efterfølgende nuller trimmes, så den eksporterede fil is byte-identisk, uanset om den blev produceret på en en-US- eller en de-DE-maskine. Hvis du henter eller redigerer PDF-annotationer på en europæisk kundes system, har du helt sikkert mødt denne fejlklasse — det er godt at vide, at komponenten håndterer det for dig

Hvad er grænserne for turen frem og tilbage?

To grænser er værd at nævne tydeligt. For det første transporterer XFDF annotationsdata, ikke rendered udseende — udseendestrømme er ikke en del af formatet, så den modtagende fremviser genererer hvert annotationsudseende ud fra dets egenskaber. En fremhævning eller en firkant vil rendere korrekt overalt; et stempel med et brugerdefineret udseende vil falde tilbage til det, som målfremviseren tegner for det stempelnavn. For det andet har den underliggende PDFium-API settere for strenge, men ingen for annotationsnumre eller stigeometri, så Opacity, linje-endepunkter, hjørner og blækbevægelser er kun til eksport: Komponenten bevarer dem trofast, når den skriver XFDF, men kan ikke skrive dem tilbage til en PDF ved import. Feltværdier, indhold, farver, rektangler, firepunkter, datoer og identitetsmetadata kører alle tur-retur i begge retninger

For en praktisk start leveres eksemplet XfdfLab i demosættene til Delphi, C++Builder og Lazarus og forbinder begge kald til knapper over enhver PDF, du åbner. Understøttelse af XFDF er inkluderet i den aktuelle udgivelse af PDFium Component, sammen med de annotations- og formular-API'er, den bygger på