Tehnički članak

Associated Files na razini stranice u PDF 2.0 uz PDFlibPas

PDFlibPas prikačuje ugrađenu datoteku uz jednu konkretnu stranicu umjesto uz dokument u cjelini, tako da upiše /AF array u rječnik stranice dok payload ostaje registriran u EmbeddedFiles name tree dokumenta. Ta podjela je ono što ISO 32000-2 §14.13 opisuje, i to je ono što čitaču omogućuje da odgovori na pitanje koje prilog na razini dokumenta ne može: kojoj stranici ti podaci pripadaju

Primjene su specifičnije od općih priloga. Izvještaj o snimanju terena gdje svaka stranica nosi sirovi niz mjerenja iza svog grafikona. Skenirana serija gdje svaka stranica čuva OCR rezultat koji je proizveo njezin sloj teksta. Skup nacrta gdje svaki list nosi CAD izvod iz kojeg je renderiran. U svakom od tih slučajeva popis priloga na razini dokumenta bio bi hrpa datoteka čija imena kodiraju brojeve stranica, a to je konvencija, a ne struktura

Jedan payload, dva mjesta s kojih se na njega referencira

Važna strukturna točka je da asocijacija na razini stranice ne stvara drugu kopiju bilo čega. Datoteka se ugrađuje jednom i registrira u EmbeddedFiles name tree točno kao što to čini prilog na razini dokumenta, koristeći isti file specification mehanizam. Razlikuje se mjesto gdje se upisuju referenca i njezin relationship ključ: u rječnik stranice umjesto u katalog dokumenta

Slijede dvije posljedice. Prvo, čitač koji zna samo za priloge na razini dokumenta i dalje nalazi payload, jer je on u name tree gdje takav čitač traži. Drugo, brisanje asocijacije stranice uklanja vezu, a ne datoteku. ClearPageAssociatedFiles odvaja stranicu od njezinih associated datoteka i ostavlja payload dohvatljivima kroz name tree, a to je konzervativno ponašanje: operacija koja kaže očisti asocijaciju ne bi tiho trebala uništiti podatke na koje se drugi dio dokumenta možda referencira

Struktura associated datoteke na razini stranice u PDF 2.0 dokumentu koji piše PDFlibPas: payload je jednom ugrađen i registriran u EmbeddedFiles name tree pod katalogom dokumenta, dok rječnik stranice nosi /AF array koji referencira istu file specification s AFRelationship ključem, pa ClearPageAssociatedFiles odvaja vezu bez uništavanja podataka
Asocijacija na razini stranice dodaje drugu referencu, a ne drugu kopiju: čitači koji znaju samo za priloge na razini dokumenta i dalje nalaze payload u name tree, i brisanje veze stranice ostavlja ugrađeni stream dohvatljivim

Ta funkcija ima jedan namjerno uzak uvjet uspjeha koji vrijedi znati. Uspjeh prijavljuje samo kada stranica zaista nosi /AF ključ. Stranica koja nikad nije imala asocijacije vraća neuspjeh umjesto vesele potvrde, pa pozivatelj ne može no-op zamijeniti za dovršeno čišćenje

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

    // Prikači niz mjerenja koji je proizveo grafikon na stranici 3
    Idx := Lib.AddPageAssociatedFileFromFile(3,
      'series-03.csv',            // datoteka na disku
      'measurements.csv',         // ime za prikaz unutar PDF-a
      'text/csv',                 // MIME tip
      '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;

Relationship string u praksi nije slobodan tekst. ISO 32000-2 definira vokabular, Source, Data, Alternative, Supplement, EncryptedPayload, FormData, Schema i Unspecified, i potrošači se na njega oslanjaju. Data za brojeve iza grafikona, Source za dokument iz kojega je stranica generirana, Alternative za ekvivalentnu reprezentaciju. Birajte iz vokabulara i kad još ništa u vašem pipelineu to ne čita, jer sljedeći alat u lancu možda hoće

Zašto isti lookup treba FollowRef u oba smjera?

Zato praćenje referenci odgovara na dva različita pitanja, i kod mora znati koje od njih postavlja. Lookup ključa koji prati indirektne reference vraća objekt na koji referenca pokazuje. Lookup koji ne prati vraća samu referencu. Oboje je ispravno, i korištenje pogrešnog proizvodi tiho pogrešno ponašanje umjesto greške

Čitanje associated datoteke pokazuje prvi smjer. Da biste dobili object number ugrađenog streama iza file specification /EF i /F ključeva, lookup ne smije pratiti, jer praćenje razriješi referencu u stream objekt i object number je nestao. Pravilo se generalizira: svaki kodni put kojem treba identitet objekta, a ne njegov sadržaj, mora uzeti sirovu referencu

Optional content pokazuje suprotni smjer, i koštao je više da se pronađe. Rječnik svojstava optional contenta upisuje se u katalog kao indirektni objekt, pa kod koji ga čita nazad bez praćenja dobiva referencu umjesto rječnika. Provjera tipa nad tom vrijednošću tada padne, i prirodna fallback grana, ako nema konfiguracije stvori jednu, se izvrši i pregazi konfiguraciju koja je već bila tamo. Ništa ne baca iznimku. Slojevi opisani u optional content grupama i slojevima jednostavno izgube svoje zadano stanje vidljivosti

Lekcija se generalizira iza oba slučaja. Kada lookup može vratiti ili referencu ili objekt, gola provjera tipa nije rukovanje greškama: to je grana koja će jednog dana biti uzeta iz pogrešnog razloga. Odlučite izrijekom što svako mjesto poziva treba, i radije birajte javni API koji na pitanje odgovara izravno, poput propertyja koji broji optional-contente, nego zaranjanje u protected accessor za rječnik kataloga

Karta odluka za praćenje referenci u PDF lookupovima kako ih implementira PDFlibPas: čitanje /EF i /F pod file specification ne smije pratiti referencu jer je object number ugrađenog streama odgovor, dok se indirektni /OCProperties rječnik u katalogu mora pratiti ili će pala provjera tipa tiho pregaziti postojeću optional content konfiguraciju
Isti lookup odgovara na dva različita pitanja: identitet treba sirovu referencu, sadržaj treba razriješeni objekt, i gola provjera tipa umjesto te odluke jednog dana izvrši pogrešnu granu bez da išta javi
// Prilozi na razini dokumenta i asocijacije na razini stranice koegzistiraju.
// Ugrađena datoteka može biti označena kao associated i na razini dokumenta
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));

// Brisanje odvaja vezu stranice; payload ostaje u name tree
if Lib.ClearPageAssociatedFiles(3) > 0 then
  Writeln('page 3 associations removed, payloads still reachable');

Što conformance načini rade s prilozima

Arhivski profili ograničavaju što se smije ugraditi, i to ograničenje se provodi na ulaznoj točki, a ne u trenutku spremanja. PDF/A-1 ugrađene datoteke zabranjuje u potpunosti, PDF/A-2 dopušta samo ugrađene PDF/A dokumente, a PDF/A-3 je profil koji je otvorio ugrađivanje za proizvoljne tipove datoteka, i upravo zato se na njemu grade hibridni formati računa

PDFlibPas odbija prilog kada aktivni conformance način to ne dopušta, na pozivu, a ne stotine operacija kasnije tijekom ispisa. To je namjerni izbor o tome gdje je grešku najjeftinije adresirati: odbijanje na mjestu poziva imenuje datoteku koju ste dodavali, dok odbijanje u trenutku spremanja imenuje dokument i ostavlja vas da ispitujete koji je od četrdeset priloga uzrok

Zato se associated datoteke i pojavljuju tako često u elektroničkom fakturiranju. Hibridni račun je PDF koji čovjek čita, s mašinski čitljivim XML payloadom prikačenim i označenim pravim relationshipom, i kontejnerski profil i relationship ključ dio su specifikacije, a ne konvencije. Ta izgradnja pokrivena je u izgradnji Factur-X i ZUGFeRD hibridnih računa, a metadata strana u PDF/A-3 XMP extension schemi

Kada asocijacija treba biti po stranici, a ne po dokumentu?

Kada potrošač treba znati kojoj stranici podaci pripadaju, i samo tada. Prilozi na razini dokumenta su jednostavniji, šire podržani od preglednika i dovoljni kad god payload opisuje cijeli dokument, XML računa, manifest potpisa, arhivu izvora. Za asocijaciju na razini stranice posežite kada je payload zaista opsegom vezan uz stranicu i identitet stranice je dio njegova značenja

Podrška je praktično ograničenje. Associated datoteke na razini stranice su konstrukcija iz PDF 2.0, i podrška u preglednicima je tanja nego za priloge na razini dokumenta. Budući da payload u svakom slučaju sjedi u name tree, preglednik koji ignorira /AF na stranicama i dalje pokazuje datoteku u svom popisu priloga, pa je degradacija graciozna. Ali ako je veza sa stranicom bitna za vašeg potrošača, a ne samo koristan metadata, provjerite čitač koji stvarno ciljate umjesto da pretpostavljate

Associated datoteke na razini stranice, prilozi na razini dokumenta i arhivski profilni gateovi koji upravljaju obojima dolaze uz PDFlibPas Delphi PDF biblioteku. Ako usput popravljate i starije datoteke pri unosu, rad na metadati i conformanceu u pretvorbi u PDF/A s popravkom metadate je ono što odlučuje koja je od ovih ruta priloga vama uopće dostupna