Techninis straipsnis

Puslapio lygio susietieji failai PDF 2.0 su PDFlibPas

PDFlibPas įdėtąjį failą prikabina prie vieno konkretaus puslapio, o ne prie viso dokumento: į puslapio žodyną įrašo /AF masyvą, o pats turinys lieka registruotas dokumento EmbeddedFiles vardų medyje. Būtent tą suskaidymą aprašo ISO 32000-2 §14.13, ir būtent jis leidžia skaitytojui atsakyti į klausimą, į kurį dokumento lygio priedas neatsako: kuriam puslapiui šie duomenys priklauso

Naudojimo atvejai specifiškesni nei bendrieji priedai. Apklausos ataskaita, kurioje kiekvienas puslapis neša žaliąją matavimų eilutę, pagal kurią nupieštas jo grafikas. Skenuotas paketas, kuriame kiekvienas puslapis laiko OCR rezultatą, iš kurio atsirado jo teksto sluoksnis. Brėžinių rinkinys, kuriame kiekvienas lapas neša CAD ištrauką, iš kurios jis atvaizduotas. Kiekvienu atveju dokumento lygio priedų sąrašas būtų krūva failų, kurių vardai užkoduoja puslapių numerius — tai konvencija, o ne struktūra

Vienas turinys, dvi vietos, iš kurių jis nurodomas

Svarbiausias struktūrinis dalykas: puslapio lygio susiejimas nesukuria nieko antros kopijos. Failas įdedamas vieną kartą ir užregistruojamas EmbeddedFiles vardų medyje tiksliai taip, kaip dokumento lygio priedas, naudojant tą pačią failo specifikacijos techniką. Skiriasi tik tai, kur įrašoma nuoroda ir jos santykio raktas: į puslapio žodyną vietoje dokumento katalogo

Iš to seka du padariniai. Pirma, skaitytojas, kuris žino tik apie dokumento lygio priedus, vis tiek suras turinį, nes jis yra vardų medyje, kur toks skaitytojas ir žiūri. Antra, puslapio susiejimo išvalymas pašalina susietį, o ne failą. ClearPageAssociatedFiles atkabina puslapį nuo jo susietųjų failų ir palieka turinius pasiekiamus per vardų medį — tai konservatyvi elgsena: operacija, kuri sako išvalyti susiejimą, neturi tyčiomis naikinti duomenų, į kuriuos gali rodyti kita dokumento dalis

PDFlibPas rašomo PDF 2.0 dokumento puslapio lygio susiesto failo struktūra: turinys įdedamas vieną kartą ir registruojamas EmbeddedFiles vardų medyje po dokumento katalogu, o puslapio žodynas neša /AF masyvą, rodantį į tą pačią failo specifikaciją su AFRelationship raktu, todėl ClearPageAssociatedFiles atkabina susietį, nesunaikindama duomenų
Puslapio lygio susiejimas prideda antrą nuorodą, o ne antrą kopiją: skaitytojai, žinantys tik dokumento lygio priedus, vis tiek suranda turinį vardų medyje, o puslapio susieties išvalymas palieka įdėtąjį srautą pasiekiamą

Ta funkcija turi vieną sąmoningai siaurą sėkmės sąlygą, vertą žinoti. Sėkmę ji praneša tik tada, kai puslapis iš tikrųjų nešė /AF raktą. Puslapis, kuris niekada neturėjo susiejimų, grąžina nesėkmę, o ne linksmą patvirtinimą, todėl kviečiančioji pusė negali nieko nedarančio veiksmo palaikyti baigtu išvalymu

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

    // Prikabinkite matavimų eilutę, iš kurios atsirado 3 puslapio grafikas
    Idx := Lib.AddPageAssociatedFileFromFile(3,
      'series-03.csv',            // failas diske
      'measurements.csv',         // rodomas vardas PDF viduje
      'text/csv',                 // MIME tipas
      '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;

Santykio eilutė praktikoje nėra laisvas tekstas. ISO 32000-2 apibrėžia žodyną — Source, Data, Alternative, Supplement, EncryptedPayload, FormData, Schema ir Unspecified, — ir vartotojai remiasi juo. Data skaičiams už grafiko, Source dokumentui, iš kurio puslapis sugeneruotas, Alternative lygiavertei atmainai. Rinkitės iš to žodyno net tada, kai jūsų konvejeryje jo kol kas neskaito niekas, nes grandinėje sekantis įrankis gali skaityti

Kodėl tas pats paieškos veiksmas abiem kryptimis reikalauja FollowRef?

Todėl, kad nuorodų sekimas atsako į du skirtingus klausimus, ir kodas turi žinoti, kurį iš jų klausia. Rakto paieška, sekdama netiesiogines nuorodas, grąžina objektą, į kurį nuoroda rodo. Paieška, kuria neseka, grąžina pačią nuorodą. Abi teisingos, o netinkamos naudojimas duoda tylų neteisingą elgesį, o ne klaidą

Susieto failo skaitymas pademonstruoja pirmą kryptį. Norint gauti įdėtojo srauto, slepiančio už failo specifikacijos /EF ir /F raktų, objekto numerį, paieška neturi sekti, nes sekimas nuorodą išspręndžia į srauto objektą ir objekto numerio nebėra. Taisyklė apibendrinama: bet kuris kodo kelias, kuriam reikia objekto tapatybės, o ne objekto turinio, turi imti žaliąją nuorodą

Pasirinktinas turinys rodo priešingą kryptį, ir jo paieška kainavo daugiau. Pasirinktino turinio savybių žodynas į katalogą įrašomas kaip netiesioginis objektas, todėl kodas, skaitantis jį atgal nesekdamas, gauna nuorodą, o ne žodyną. Tipų patikra tos reikšmės tada žlunga, ir natūrali atsarginė šaka — jei konfigūracijos nėra, sukurti — įsijungia ir perrašo ten jau buvusią konfigūraciją. Niekas nepakelia klaidos. Sluoksniai, aprašyti straipsnyje pasirinktino turinio grupės ir sluoksniai, tiesiog praranda savo numatytąją matomumo būseną

Pamoka apibendrina abu atvejus. Kai paieška gali grąžinti ir nuorodą, ir objektą, plika tipų patikra nėra klaidų apdorojimas: tai šaka, kuri anksčiau ar vėliau bus pasirinkta dėl netinkamos priežasties. Aiškiai nuspręskite, ko reikia kiekvienai iškvietimo vietai, ir teikite pirmenybę viešajam API, kuris atsako į klausimą tiesiogiai, pavyzdžiui, pasirinktino turinio skaičiaus savybei, o ne skverbimuisi į apsaugotą prieigą prie katalogo žodyno

PDFlibPas realizuotų PDF paieškų nuorodų sekimo sprendimų žemėlapis: /EF ir /F skaitymas po failo specifikacijos neturi sekti nuorodos, nes atsakymas yra įdėtojo srauto objekto numeris, o netiesioginį /OCProperties žodyną kataloge sekti būtina, kitaip žlunganti tipų patikra tyčiomis perrašo esamą pasirinktino turinio konfigūraciją
Ta pati paieška atsako į du skirtingus klausimus: tapatybei reikia žaliosios nuorodos, turiniui — išspręsto objekto, o plika tipų patikra vietoje to sprendimo anksčiau ar vėliau įvykdo netinkamą šaką, nepakeldama klaidos
// Dokumentinio lygio priedai ir puslapio lygio susiejimai egzistuoja kartu.
// Įdėtąjį failą galima pažymėti susietu ir dokumento lygiu
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));

// Išvalymas atkabina puslapio susietį; turinys lieka vardų medyje
if Lib.ClearPageAssociatedFiles(3) > 0 then
  Writeln('page 3 associations removed, payloads still reachable');

Ką prie priedų daro suderinamumo režimai

Archyviniai profiliai riboja, ką galima įdėti, ir apribojimas vykdomas įėjimo taške, o ne išsaugojimo metu. PDF/A-1 draudžia įdėtuosius failus visiškai, PDF/A-2 leidžia tik įdėtuosius PDF/A dokumentus, o PDF/A-3 yra profilis, atvėręs įdėjimą savavalėms failų rūšims — būtent todėl ant jo ir statomi hibridiniai sąskaitų formatai

PDFlibPas atmeta priedą, kai aktyvus suderinamumo režimas jo neleidžia, — iškvietimo metu, o ne šimtus operacijų vėliau, išvesties metu. Tai sąmoningas pasirinkimas, kur klaidos atžvilgiu pigiausia reaguoti: atmetimas iškvietimo vietoje įvardija failą, kurį įdėjote, o atmetimas išsaugojimo metu įvardija dokumentą ir palieka jums išsiaiškinti, kuris iš keturiasdešimties priedų to kaltas

Todėl susietieji failai elektroninėse sąskaitose ir atsiranda taip dažnai. Hibridinė sąskaita yra PDF, kurį skaito žmogus, su prie jo prikabintu ir tinkamu santykiu pažymėtu mašininai skaitomu XML turiniu, ir konteinerio profilis, ir santykio raktas yra specifikacijos dalis, o ne konvencijos. Ta konstrukcija aprašyta straipsnyje Factur-X ir ZUGFeRD hibridinių sąskaitų kūrimas, o metaduomenų pusė — straipsnyje PDF/A-3 XMP plėtinio schema

Kada susiejimas turi būti puslapiui, o ne dokumentui?

Kai vartotojui reikia žinoti, kuriam puslapiui duomenys priklauso — ir tik tada. Dokumento lygio priedai paprastesni, plačiau palaikomi peržiūros programų ir pakankami, kai turinys apibūdina visą dokumentą: sąskaitos XML, parašo manifestas, pirminio kodo archyvas. Prie puslapio lygio susiejimo imkitės, kai turinys tikrai apibrėžtas puslapio apimtimi ir puslapio tapatybė yra jo reikšmės dalis

Praktinis apribojimas — palaikymas. Puslapio lygio susietieji failai yra PDF 2.0 konstrukcija, ir peržiūros programų palaikymas plonesnis nei dokumento lygio priedų. Kadangi turinys abiem atvejais sėdi vardų medyje, peržiūros programa, ignoruojanti /AF puslapiuose, vis tiek parodo failą savo priedų sąraše, todėl degradacija maloni. Bet jei puslapio susietis jūsų vartotojui yra esminis, o ne tik naudingi metaduomenys, patikrinkite skaitytuvą, į kurį iš tikrųjų taikotės, o nesitikėkite

Puslapio lygio susietieji failai, dokumento lygio priedai ir archyvinių profilių vartai, valdantys abu, pateikiami kartu su PDFlibPas Delphi PDF biblioteka. Jei pakeliui į vidų dar ir remontuojate senesnius failus, metaduomenų ir suderinamumo darbas, aprašytas straipsnyje konvertavimas į PDF/A su metaduomenų remontu, yra tai, kas iš pradžių nusprendžia, kuris iš šių priedų kelių jums apskritai prieinamas