Teknisk artikel

Sammenlægning af PDF-formularer i Delphi: regler for dobbelte felter

PDF Library for Delphi sammenlægger to AcroForm-dokumenter med en eksplicit politik for felter, der deler et navn. MergeDocumentEx tager kildedokumentidentifikatoren og én af tre strategier: dfsReject afviser sammenlægningen, dfsMerge beholder det delte navn og synkroniserer værdier, og dfsAutoNumber omdøber de indkommende felter deterministisk. Navneskanningen sker, før nogen objektnumre forskydes, så en afvist sammenlægning efterlader begge dokumenter fuldt brugbare

Enhver, der har samlet en PDF-ansøgningspakke, har ramt dette. Tre formularer, hver med et felt kaldet Signature eller Date eller Total, sammenlægges til én fil. I en AcroForm er det fuldt kvalificerede feltnavn feltets identitet, så to felter med samme navn er slet ikke to felter: at udfylde det ene udfylder det andet, og en underskrift anvendt på det ene dækker et omfang, ingen tilsigtede

Hvorfor afgøres navnekollisionen før sammenlægningen?

Den ældre MergeDocument sammenkæder de to AcroForm-rod-feltarrays og tilbyder intet valg. Værre er, at når resultatet er ubrugeligt, sker opdagelsen efter, at objektnumre er blevet omnummereret og sidetræer syet sammen, hvilket efterlader den kaldende med et dokument i en tilstand, ingen af originalerne var i

MergeDocumentEx vender rækkefølgen om. Den indsamler de øverste feltnavne fra begge dokumenter, sammenligner dem og anvender strategien, før noget flytter sig. En afvisning er derfor en ren no-op: måldokumentet er urørt, kildedokumentet er urørt, og begge forbliver åbne og brugbare, hvilket sammenlægningstesten bekræfter ved at læse en feltværdi tilbage ud af kilden efter en afvist sammenlægning

Sammenligningen bruger et ordnet, versalfølsomt navnesæt, så omkostningen er proportional med det samlede feltantal gange en logaritmisk faktor frem for med produktet af de to antal. Versalfølsomhed er det rigtige valg her, fordi PDF-feltnavne er versalfølsomme; at folde dem ville sammenlægge felter, specifikationen behandler som forskellige

De tre strategier, og hvornår hver er rigtig

dfsReject er strategien til automatiserede pipelines, der ikke må producere tvetydige dokumenter. Sammenlægningen returnerer nul, og LastErrorCode rapporterer 705, en dedikeret kode, så dobbelte navne kan skelnes fra enhver anden sammenlægningsfejl og dirigeres til en specifik afhjælpning, som regel omdøbning af felter opstrøms

dfsMerge beholder bevidst det delte navn og synkroniserer mål- og standardværdien ind i kildefeltet, så en konform viewer behandler de flere widgets som ét logisk navngivet felt, hvilket er standard AcroForm-adfærd for et felt med flere widget-annoteringer. Hvad den ikke gør, er at folde forskellige feltdictionaries ind i ét enkelt objekt. Hvert felt beholder sin egen sideassociation, fremtoning og handlinger, fordi at kollapse dem stiltiende ville kassere formatering og adfærd, der hører til det indkommende dokument

dfsAutoNumber omdøber indkommende dubletter ved at tilføje et numerisk suffiks, der starter ved _2, og tager det første ledige. Resultatet er reproducerbart: det afhænger kun af de tilstedeværende navne, aldrig af feltobjektnumre, så at sammenlægge det samme par dokumenter to gange giver de samme navne begge gange. Den egenskab betyder noget, når efterfølgende kode, en FDF-import eller en databaseafbildning refererer til felter efter navn

uses
  PDFlibrary;

var
  Lib: TPDFlib;
  TargetDoc, SourceDoc: Integer;
begin
  Lib := TPDFlib.Create;
  try
    TargetDoc := Lib.SelectedDocument;
    Lib.LoadFromFile('application-part1.pdf', '');

    SourceDoc := Lib.NewDocument;
    Lib.LoadFromFile('application-part2.pdf', '');

    Lib.SelectDocument(TargetDoc);
    if Lib.MergeDocumentEx(SourceDoc, dfsReject) = 0 then
    begin
      if Lib.LastErrorCode = 705 then
      begin
        // Begge dokumenter er stadig intakte - prøv igen med en politik
        Log('duplicate field names; retrying with auto-numbering');
        Lib.MergeDocumentEx(SourceDoc, dfsAutoNumber);
      end;
    end;

    Lib.SaveToFile('application-complete.pdf');
  finally
    Lib.Free;
  end;
end;

Bemærk det to-trins mønster i den kode, som kun er muligt, fordi afvisning er ikke-destruktiv. Prøv den strenge politik først, inspicér fejlen, beslut derefter. Med en sammenlægning, der fejler halvvejs, ville fallbacken skulle starte forfra ved at genindlæse begge filer

Hvordan den sammenlagte formular ser ud bagefter

Under dfsMerge producerer et målfelt ved navn Shared, der bærer "Target value", og et kildefelt med samme navn to felter, begge navngivet Shared, begge rapporterende målværdien, fordi mål- og standardværdien synkroniseres ind i det indkommende felt. Det er den tilsigtede semantik for et delt navn: ét logisk felt, flere widgets, én værdi

Under dfsAutoNumber producerer det samme input Shared og Shared_2 som separate felter med uafhængige værdier. Vælg mellem de to ved at stille ét enkelt spørgsmål: skal udfyldning af den ene kontrol udfylde den anden? For et underskrivernavn gentaget på hver del af en pakke, ja, og dfsMerge er rigtig. For en totalsum, der betyder noget forskelligt på hver formular, nej, og automatisk nummerering er rigtig

// Efter en sammenlægning, opremse hvad du rent faktisk fik
for I := 1 to Lib.FormFieldCount do
  Log(Format('%d: %s = %s',
    [I, Lib.GetFormFieldTitle(I), Lib.GetFormFieldValue(I)]));

Praktiske noter til at samle formularpakker

Den vellykkede sammenlægning forbruger kildedokumentet: det fjernes fra bibliotekets dokumentliste, hvilket er hvorfor DocumentCount falder fra to til én. Fortsæt ikke med at bruge kildeidentifikatoren bagefter. Dokumentversionen hæves til den højeste af de to, så at sammenlægge en PDF 2.0-formular ind i et 1.7-dokument giver en 2.0-fil

Rækkefølge betyder noget for navne. At sammenlægge A ind i B og at sammenlægge B ind i A producerer forskellige auto-nummererede resultater, da det dokument, der udfører sammenlægningen, beholder sine navne uændrede. Når en pakke har en kanonisk primær formular, gør den til målet

Underskriftsfelter fortjener deres egen overvejelse. En underskrift, der blev anvendt før en sammenlægning, dækker kun den revision, den underskrev, så sammenlægning ugyldiggør den i den praktiske forstand, at filen har ændret sig siden underskrivning. Saml først, og underskriv det samlede dokument, frem for at sammenlægge underskrevne dele. Når sammenlægningen handler om sideindhold frem for formularer, er den hurtigere sti beskrevet i hurtig PDF-sammenlægning med byte-referenceforskydning det bedre værktøj

Planlæg endelig datasiden af pakken sammen med sammenlægningen. Hvis feltværdier ankommer fra et eksternt system, beslut, om det system adresserer felter efter navn, før du vælger automatisk nummerering, fordi Shared_2 ikke vil matche en afbildning, der forventer Shared. Import- og eksportformater er beskrevet i FDF-, XFDF- og XFA-formulardataudveksling, og feltniveau-scripting-adfærd, der også kan påvirkes af omdøbning, er beskrevet i interaktive formularhandlinger og JavaScript

Formularsammenlægning, dataudveksling og underskrivning kører i det samme bibliotek til Delphi, C++Builder og Free Pascal; den komplette funktionsliste findes på siden PDF Library til Delphi