Teknisk artikkel

Tilknyttede filer på sidennivå i PDF 2.0 med PDFlibPas

PDFlibPas fester en innebygd fil til én bestemt side i stedet for til dokumentet som helhet, ved å skrive en /AF-array inn i sideordboken mens selve nyttelasten forblir registrert i dokumentets EmbeddedFiles-navnetre. Den delingen er det ISO 32000-2 §14.13 beskriver, og det er den som lar en leser svare på spørsmålet et dokumentnivå-vedlegg ikke kan: hvilken side hører disse dataene til

Bruksområdene er mer spesifikke enn generelle vedlegg. En målerapport der hver side bærer råmåleserien bak grafen sin. En skannet bunke der hver side beholder OCR-resultatet som produserte tekstlaget. Et tegningssett der hvert ark bærer CAD-uttrekket det ble rendret fra. I hvert tilfelle ville en dokumentnivå-vedleggsliste vært en haug med filer der navnene koder sidetall, noe som er en konvensjon snarere enn en struktur

Én nyttelast, to steder den refereres fra

Det viktigste strukturelle poenget er at sidennivå-tilknytning ikke lager en kopi til av noe. Filen innebygges én gang og registreres i EmbeddedFiles-navnetreet nøyaktig som et dokumentnivå-vedlegg, med samme filspesifikasjonsmekanisme. Det som er annerledes, er hvor referansen og relasjonsnøkkelen skrives: inn i sideordboken i stedet for dokumentkatalogen

To konsekvenser følger. For det første finner en leser som bare kjenner dokumentnivå-vedlegg, fortsatt nyttelasten, fordi den ligger i navnetreet der en slik leser ser. For det andre fjerner å tømme sidetilknytningen bindingen, ikke filen. ClearPageAssociatedFiles kobler siden løs fra sine tilknyttede filer og lar nyttelastene være tilgjengelige gjennom navnetreet, noe som er den konservative atferden: en operasjon som sier fjern tilknytningen, skal ikke i stillhet ødelegge data en annen del av dokumentet kan referere til

Strukturen til en sidetilknyttet fil i et PDF 2.0-dokument skrevet med PDFlibPas: nyttelasten innebygges én gang og registreres i EmbeddedFiles-navnetreet under dokumentkatalogen, mens sideordboken bærer en /AF-array som refererer til samme filspesifikasjon med en AFRelationship-nøkkel, slik at ClearPageAssociatedFiles løsner bindingen uten å ødelegge data
Sidennivå-tilknytning legger til en referanse til, ikke en kopi til: lesere som bare kjenner dokumentnivå-vedlegg finner fortsatt nyttelasten i navnetreet, og å tømme sidebindingen lar den innebygde strømmen være tilgjengelig

Den funksjonen har én bevisst smal suksessbetingelse verdt å kjenne. Den rapporterer suksess bare når siden faktisk bar en /AF-nøkkel. En side som aldri har hatt tilknytninger, returnerer feil i stedet for en blid bekreftelse, slik at en kaller ikke kan forveksle en no-op med en fullført opprydding

var
  Lib: TPDFlib;
  Idx, I: Integer;
begin
  Lib := TPDFlib.Create(nil);
  try
    Lib.LoadFromFile('survey-report.pdf');

    // Fest måleserien som produserte grafen på side 3
    Idx := Lib.AddPageAssociatedFileFromFile(3,
      'series-03.csv',            // fil på disk
      'measurements.csv',         // visningsnavn inne i PDF-en
      'text/csv',                 // MIME-type
      'Raw measurement series for figure 3',
      'Data');                    // AFRelationship, ISO 32000-2 14.13

    if Idx < 0 then
      raise Exception.Create('page association refused');

    for I := 0 to Lib.GetPageAssociatedFileCount(3) - 1 do
      Writeln('page 3 associated file, embedded index ',
        Lib.GetPageAssociatedFileEmbeddedIndex(3, I));

    Lib.SaveToFile('survey-report-with-data.pdf');
  finally
    Lib.Free;
  end;
end;

Relasjonsstrengen er ikke fri tekst i praksis. ISO 32000-2 definerer et vokabular, Source, Data, Alternative, Supplement, EncryptedPayload, FormData, Schema og Unspecified, og konsumenter forholder seg til det. Data for tallene bak en graf, Source for dokumentet en side ble generert fra, Alternative for en ekvivalent representasjon. Velg fra vokabularet selv om ingenting i rørledningen din leser det ennå, fordi neste verktøy i kjeden kanskje gjør det

Hvorfor trenger samme oppslag FollowRef i begge retninger?

Fordi referanseoppfølging svarer på to forskjellige spørsmål, og koden må vite hvilket den spør om. Et nøkkeloppslag som følger indirekte referanser, returnerer objektet referansen peker på. Et oppslag som ikke følger, returnerer selve referansen. Begge er korrekte, og å bruke den gale gir en stille feilatferd i stedet for en feil

Å lese en tilknyttet fil demonstrerer den første retningen. For å få tak i objektnummeret til den innebygde strømmen bak filspesifikasjonens /EF- og /F-nøkler, må oppslaget ikke følge, fordi oppfølging løser referansen opp i strømobjektet og objektnummeret er borte. Regelen generaliserer: enhver kodevei som trenger objektidentitet snarere enn objektinnhold, må ta råreferansen

Valgfritt innhold viser motsatt retning, og det kostet mer å finne. Egenskapsordboken for valgfritt innhold skrives inn i katalogen som et indirekte objekt, så kode som leser den tilbake uten å følge, får en referanse i stedet for en ordbok. En typesjekk på den verdien feiler da, og den naturlige fallback-grenen, finnes det ingen konfigurasjon, lag én, kjører og overskriver konfigurasjonen som allerede var der. Ingenting gir feilmelding. Lagene beskrevet i valgfrie innholdsgrupper og lag mister rett og slett sin standard synlighetstilstand

Lærdommen generaliserer utover begge tilfeller. Når et oppslag kan returnere enten en referanse eller objektet, er en naken typesjekk ikke feilhåndtering: det er en gren som før eller siden tas av feil grunn. Bestem eksplisitt hva hvert kallsted trenger, og foretrekk det offentlige API-et som svarer direkte på spørsmålet, som en egenskap for antall valgfrie innholdselementer, fremfor å rekke inn i en beskyttet aksessor etter katalogordboken

Beslutningskart for referanseoppfølging i PDF-oppslag som implementert i PDFlibPas: å lese /EF og /F under en filspesifikasjon må ikke følge referansen, fordi objektnummeret til den innebygde strømmen er svaret, mens den indirekte /OCProperties-ordboken i katalogen må følges, ellers overskriver en feilende typesjekk i stillhet den eksisterende konfigurasjonen for valgfritt innhold
Samme oppslag svarer på to forskjellige spørsmål: identitet trenger råreferansen, innhold trenger det oppløste objektet, og en naken typesjekk i stedet for den beslutningen kjører før eller siden feil gren uten å gi feilmelding
// Dokumentnivå-vedlegg og sidetilknytninger eksisterer side om side. En
// innebygd fil kan også merkes som tilknyttet på dokumentnivå
if Lib.IsEmbeddedFileAssociated(0) = 0 then
  Lib.SetEmbeddedFileAssociated(0, 1, 'Supplement');

Writeln('document associated files: ', Lib.GetAssociatedFileCount);
Writeln('page 3 associated files  : ',
        Lib.GetPageAssociatedFileCount(3));

// Å tømme løsner sidebindingen; nyttelasten blir værende i navnetreet
if Lib.ClearPageAssociatedFiles(3) > 0 then
  Writeln('page 3 associations removed, payloads still reachable');

Hva samsvarsmoduser gjør med vedlegg

Arkiveringsprofiler begrenser hva som kan innebygges, og begrensningen håndheves ved inngangspunktet i stedet for ved lagring. PDF/A-1 forbyr innebygde filer helt, PDF/A-2 tillater bare innebygde PDF/A-dokumenter, og PDF/A-3 er profilen som åpnet innebygging for vilkårlige filtyper, noe som er nøyaktig grunnen til at hybride fakturaformater bygges på den

PDFlibPas nekter vedlegget når aktiv samsvarsmodus ikke tillater det, ved kallet, ikke hundrevis av operasjoner senere under utdata. Det er et bevisst valg om hvor en feil er billigst å handle på: et avslag ved kallstedet navngir filen du holdt på å legge til, mens et avslag ved lagring navngir et dokument og lar deg finne ut hvilket av førti vedlegg som forårsaket det

Dette er også grunnen til at tilknyttede filer dukker opp så ofte i elektronisk fakturering. En hybridfaktura er en PDF et menneske leser, med en maskinlesbar XML-nyttelast festet og merket med riktig relasjon, og både containerprofilen og relasjonsnøkkelen er del av spesifikasjonen snarere enn konvensjoner. Det oppbygget er dekket i bygging av Factur-X- og ZUGFeRD-hybridfakturaer, med metadatasiden i PDF/A-3 XMP-utvidelsesskjemaet

Når bør tilknytningen være per side snarere enn per dokument?

Når en konsument trenger å vite hvilken side dataene tilhører, og bare da. Dokumentnivå-vedlegg er enklere, bedre støttet av visningsprogrammer, og tilstrekkelig så lenge nyttelasten beskriver hele dokumentet, en faktura-XML, et signaturmanifest, et kildearkiv. Grip etter sidennivå-tilknytning når nyttelasten faktisk er sideavgrenset og sideidentiteten er del av betydningen

Støtte er den praktiske begrensningen. Sidetilknyttede filer er en PDF 2.0-konstruksjon, og støtten i visningsprogrammene er tynnere enn for dokumentnivå-vedlegg. Siden nyttelasten uansett ligger i navnetreet, viser et visningsprogram som ignorerer /AF på sider, fortsatt filen i vedleggslisten sin, så degraderingen er skånsom. Men hvis sidebindingen er avgjørende for konsumenten din snarere enn nyttige metadata, verifiser leseren du faktisk sikter mot, i stedet for å anta

Sidetilknyttede filer, dokumentnivå-vedlegg og arkiveringsprofilportene som styrer begge, følger med PDFlibPas Delphi PDF-bibliotek. Hvis du også reparerer eldre filer på veien inn, er metadata- og samsvarsarbeidet i konvertering til PDF/A med metadatareparasjon det som avgjør hvilke av disse vedleggsruttene som i det hele tatt er tilgjengelige for deg