Műszaki cikk

PDF metaadatok, vázlatok és megjegyzések (annotations) magyarázata

Távolítsa el az oldal leírásokat, és egy vékony strukturális réteg marad, amelyet senki sem nyomtat ki, de minden olvasó (reader), indexelő és archiváló rendszer függ tőle. Egy oldal objektum semmit sem tud arról a fejezetről, amelyhez tartozik, a szerzőről, aki írta, vagy a lábjegyzetről, amely máshová hivatkozik. Ez a tudás egy szinttel feljebb él, a dokumentum katalógushoz (document catalog) csatolt három struktúrában: a metaadat folyamokban (metadata streams), a vázlatfában (outline tree) és az oldalankénti megjegyzés tömbökben (per-page annotation arrays). Van egy közös vonásuk, ami miatt könnyű elrontani őket. Egyik sem hordoz látható jeleket az oldalon, így egy fájl tökéletesen megjeleníthető (render perfectly), és mégis hiányozhatnak belőle a könyvjelzők, ellentmondhat a saját szerzői mezőjének, vagy egy olyan oldal objektumra mutathat (pointing a link), amely már nem létezik

Ez az a réteg, amelyet egy PDF könyvtár (library) dokumentum tulajdonságokként (document properties), könyvjelző API-kként, valamint hivatkozás vagy megjegyzés hívásokként tesz közzé, és az a réteg, amelyet egy keresőrobot (search crawler) beolvas, hogy eldöntse, miről szól a dokumentuma. Az alatta lévő objektummodellel a PDF dokumentumstruktúra bemutatója foglalkozik. Itt a hangsúly szigorúan arra helyeződik, ami a katalógushoz kapcsolódik (hangs off)

Mindhárom struktúra a katalógushoz csatlakozik. Egy teljes katalógus, amely összeköti őket, így néz ki:

1 0 obj
<< /Type /Catalog
   /Pages 2 0 R
   /Outlines 3 0 R
   /Names << /EmbeddedFiles 4 0 R >>
   /Metadata 5 0 R
>>
endobj

Négy bejegyzés, négy független alrendszer (subsystems). A /Pages a látható dokumentum; az /Outlines a könyvjelző fa; a /Metadata az XMP folyamra mutat; a /Names eléri a dokumentum szintű név szótárat (name dictionary), amely többek között a beágyazott fájlmellékleteket is tartalmazza. Mindegyik opcionális, és egy olvasó (reader), amely egyiket sem találja, még mindig megjeleníti az oldalakat. Pontosan ez az opcionális jelleg (optionality) az oka annak, hogy a navigációs réteg az első, ami megromlik (rot), amikor egy fájlt olyan eszközökkel szerkesztenek, amelyek csak az oldalakat értik

Két metaadat-tároló, amelyek nem értenek egyet

A PDF a dokumentum metaadatokat egyszerre két helyen hordozza, és a baj akkor kezdődik, amikor különböző dolgokat mondanak. Az eredeti mechanizmus a dokumentuminformációs szótár (document information dictionary), amelyre a trailerben található /Info hivatkozik: kulcs-érték párok lapos halmaza a /Title, /Author, /Subject, /Keywords, /Creator, /Producer és a két dátum számára. Egyszerű, és minden megjelenítő olvassa. A PDF 2.0 a legtöbbjüket elavultnak nyilvánítja (deprecates) a második mechanizmus, az XMP metaadat-folyam (metadata stream) javára

Az XMP egy önálló (self-contained) XML dokumentum, RDF nyelven írva, és egy folyamként van tárolva, amelyet a katalógus a /Metadata segítségével ér el, és /Type /Metadata /Subtype /XML jelöléssel van ellátva. Ellentétben a PDF objektumstruktúrába temetett Info szótárral, egy XMP csomagot (packet) úgy terveztek, hogy önállóan is kinyerhető és elemezhető legyen olyan eszközök által, amelyek semmit sem tudnak a PDF-ről. Íme egy reprezentatív csomag:

5 0 obj
<< /Type /Metadata /Subtype /XML /Length 1235 >>
stream
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/">
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <rdf:Description rdf:about=""
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:xmp="http://ns.adobe.com/xap/1.0/"
        xmlns:pdf="http://ns.adobe.com/pdf/1.3/">
      <dc:title><rdf:Alt><rdf:li xml:lang="x-default">Quarterly Report</rdf:li></rdf:Alt></dc:title>
      <dc:creator><rdf:Seq><rdf:li>A. Author</rdf:li></rdf:Seq></dc:creator>
      <xmp:CreateDate>2026-06-16T10:46:27+08:00</xmp:CreateDate>
      <xmp:CreatorTool>Reporting Service 4.2</xmp:CreatorTool>
      <pdf:Producer>losLab PDF Library</pdf:Producer>
    </rdf:Description>
  </rdf:RDF>
</x:xmpmeta>
<?xpacket end="w"?>
endstream
endobj

Ebben a blokkban három részlet dönti el, hogy a metaadatok túlélik-e (survives) a valódi eszközökkel (real tooling) való kapcsolatot. Az xpacket feldolgozási utasítások (processing instructions) nem dekorációk: úgy keretezik a csomagot, hogy egy kinyerő (extractor) megtalálja azt egy nagyobb bájtfolyamban, és egy olyan író (writer), amely kihagyja a záró <?xpacket end="w"?> taget, olyan fájlt hoz létre, amely jól megnyílik, de megbotlasztja a szigorú érvényesítőket (strict validators). A tulajdonságok adattípusai (property datatypes) is számítanak. A dc:title egy nyelvi alternatíva rdf:Alt-ba csomagolva, míg a dc:creator egy rendezett lista, és az rdf:Seq-et veszi fel; ha ezek bármelyikét csupasz (bare) szövegcsomópontként (text node) bocsátják ki, az a leggyakoribb XMP hiba, amelyet a legtöbb megjelenítő eltűr egészen addig, amíg az egyik már nem. A névtér előtagok (namespace prefixes) hagyományosak (conventional), de a hozzájuk kötődő (bind to) URI-k normatívak: egy elemző (parser) az URI-ra, nem pedig az előtagra fókuszál (keys off)

A szigorú szabály a két tárolóval (two stores) kapcsolatban az, hogy meg kell egyezniük (must agree). Ha az /Info azt mondja, hogy a szerző egy személy, a dc:creator pedig egy másikat nevez meg, akkor egy olyan dokumentumot szállított, amely kétféleképpen válaszol ugyanarra a kérdésre, és az, hogy melyik válasz nyer, attól függ, hogy a fogyasztó eszköz (consuming tool) melyik mezőt olvassa. Egy könyvtár általában mindkettőt megírja ön helyett, de abban a pillanatban, amikor az egyiket kézzel szerkeszti, vagy különböző generátorokból származó fájlokat fésül össze (merge), a kettő elsodródik egymástól (drift apart). Kezelje az Info szótárat örökölt (legacy) kompatibilitásként, az XMP-t pedig az igazság forrásaként (source of truth), és mindkettőt ugyanabból az értékkészletből regenerálja ahelyett, hogy függetlenül javítgatná (patching) őket. A PDF/A esetében ez megfelelőségi követelménnyé (conformance requirement) válik: az ISO 19005 kötelezővé teszi az XMP-t, és megtiltja, hogy bármely Info tulajdonság ellentmondjon XMP megfelelőjének

A vázlatfa (outline tree) a könyvjelző panel mögött

Amit a megjelenítő (viewer) könyvjelző (bookmarks) panelként mutat, az a fájlban szótárak kétszeresen láncolt fája (doubly linked tree), amelyet dokumentum vázlatnak (document outline) neveznek. A katalógus a /Outlines segítségével egy gyökér vázlat szótárra (root outline dictionary) mutat; a gyökér az első és az utolsó legfelső szintű elemére mutat; és minden elem (item) felfűződik (threaded) a szomszédaira és a szülőjére. Sehol sincs könyvjelzők tömbje. Az egész struktúrát a hivatkozások követésével rekonstruálják, ami pontosan az oka annak, hogy egyetlen megszakadt hivatkozás miatt egy egész ág eltűnhet (vanish) a panelről mindenféle hiba nélkül

8 0 obj                                    % a vázlat gyökere
<< /Type /Outlines /Count 4 /First 9 0 R /Last 9 0 R >>
endobj
9 0 obj                                    % legfelső szintű: egy fejezet
<< /Title (Chapter 1: Results)
   /Parent 8 0 R /Count 2
   /First 12 0 R /Last 15 0 R >>
endobj
12 0 obj                                   % első gyermek
<< /Title (Introduction)
   /Parent 9 0 R /Next 15 0 R
   /Dest [3 0 R /XYZ 72 720 0] >>
endobj
15 0 obj                                   % második gyermek, utolsó testvér
<< /Title (Methodology)
   /Parent 9 0 R /Prev 12 0 R
   /Dest [3 0 R /Fit] >>
endobj

Olvassa el a hivatkozásokat, és az invariánsok (invariants) nyilvánvalóvá válnak. Minden elem visszamutat a /Parent-jára (szülőjére). A testvérek (siblings) láncot alkotnak a /Prev és a /Next segítségével, az első elem kihagyja a /Prev-et, az utolsó pedig a /Next-et. Egy szülő (parent) az első és az utolsó gyermekét a /First és a /Last segítségével nevezi meg, és a köztes gyermekek csak a testvérlánc (sibling chain) végigjárásával érhetők el. Ha egyet elront, a hiba csendes (silent) marad: egy elavult (stale) /Next csonkít egy fejezetet, egy olyan szülő, akinek a /Last eleme nem zárja le a láncot, elárvult (orphaned) elemeket hagy, és a megjelenítő azt jeleníti meg, amit el tud érni

A /Count mező egy olyan állapotot (state) hordoz, amely meglepi az embereket. A gyökéren (root) és minden kibontott (expanded) elemen a jelenleg látható leszármazottak (descendants) számát tartalmazza; egy összecsukott (collapsed) elemen ez egy negatív szám, amelynek nagysága (magnitude) mutatja, hány leszármazott jelenne meg kibontáskor. Tehát a /Count nem egy rögzített strukturális tény a fáról, hanem a panel elmentett nyitott vagy zárt állapota, és egy generátor, amely fix pozitív összegként (positive total) kódolja, újra megnyit minden olyan ágat, amelyet a szerző zárva akart hagyni

Minden elem azzal érdemli ki a helyét, hogy valahová mutat. A /Title az, amit a panel mutat; a /Dest (célállomás) az, ahová egy kattintás (click) érkezik. A célállomás lehet soron belüli (inline) az elemben, mint fent, vagy egy név, amely a dokumentum név szótárán (name dictionary) keresztül oldódik fel; ez utóbbi a jobb választás, ha sok könyvjelző és hivatkozás ugyanazokat a helyeket célozza meg, mert így egy áthelyezett célt egy helyen javíthat ki. Egy könyvtár általában elrejti ezt a fát egy vázlat-gyökér kezelő (outline-root handle) és olyan metódusok mögé, amelyek gyermek bejegyzéseket (child entries) adnak hozzá; a HotPDF-ben a dokumentum egy THPDFDocOutlineObject típusú OutlineRoot-ot tesz elérhetővé (exposes), és összefűzi (threads) a /Prev, /Next, /Parent és /Count hivatkozásokat (links) ön helyett, ahogy elemeket fűz hozzá. Ezt érdemes kihasználni, mert ezen invariánsok kézi karbantartása a szerkesztések során az a pont, ahol a vázlatok eltörnek (outlines break)

Célállomások (Destinations): a kattintás helyének nyelvtana

A könyvjelzők és a hivatkozás megjegyzések (link annotations) egyaránt célállomásokra (destinations) mutatnak, és a célállomás több, mint egy oldalszám. Ez egy tömb (array), amely megnevez egy oldal objektumot, majd a második helyen lévő igén (verb) keresztül meghatározza, hogyan kell a megjelenítőnek keretbe foglalnia azt. A leggyakoribb és leginkább visszaélésszerűen használt az /XYZ, a [page /XYZ left top zoom] formában. Három operandusa független, és bármelyik lehet null, ami azt jelenti, hogy "hagyja ezt úgy, ahogy az olvasó beállította". Így a [page /XYZ null null null] az oldalra ugrik anélkül, hogy megérintené a görgetési pozíciót (scroll position) vagy a nagyítást (zoom), ami általában az, amit egy "ugrás az oldalra" (go to page) hivatkozástól vár. A számok az alapértelmezett felhasználói térben (default user space) vannak, bal alulról mérve, az y pedig felfelé növekszik; ez ugyanaz a koordinátarendszer, amelyet az oldal tartalma is használ. A képernyő elrendezések felől érkező szerzők reflexszerűen fentről mérnek, és az olvasót az oldal rossz végére küldik

A /Fit család a pontos pozicionálást rugalmasságra (resilience) cseréli. A [page /Fit] az egész oldalt az ablakba méretezi (scales), a [page /FitH top] az oldal szélességét egy adott felső élhez igazítja, a [page /FitR l b r t] pedig úgy nagyít (zooms) egy téglalapot, hogy az kitöltse a nézetet. Mivel ezek az oldal geometriájából (page geometry) számítják ki a léptéket, nem pedig a rögzített koordinátákból, egy /Fit célállomás az oldal átméretezése után is ésszerűen (sensible thing) viselkedik, míg egy beégetett (baked-in) nagyítással rendelkező /XYZ célállomás a margót (margin) bámulva hagyhatja az olvasót. Egy tartalomjegyzék (table of contents) esetében a /FitH a szakasz felső koordinátájával jobban öregszik (ages better), mint az /XYZ egy megtippelt nagyítással

Megjegyzések (Annotations): minden interaktív elem, ami nem az oldal tartalma

A megjegyzés (annotation) egy olyan objektum, amely átfedi (overlays) az oldalt anélkül, hogy annak tartalomfolyamának része lenne. Hivatkozások, öntapadós jegyzetek (sticky notes), kiemelések (highlights), űrlap widgetek (form widgets), fájlmelléklet ikonok, bélyegzők (stamps): mindegyik megjegyzés, és az adott oldal /Annots tömbjében vannak felsorolva. Egy megjegyzés eltávolítása ebből a tömbből eltávolítja azt az oldalról, még akkor is, ha a mögöttes tartalom érintetlen marad. Ez a lényeg: a megjegyzések egy szerkesztési réteget (editing layer) alkotnak, amely elkülönül a jelektől (marks), amelyeken elhelyezkednek

Minden megjegyzés megoszt egy kis gerincet (spine). A /Subtype (altípus) megnevezi a fajtáját, a /Rect (téglalap) megadja a határoló dobozát (bounding box) oldal koordinátákban, a /Contents (tartalom) pedig szöveget hordoz, amely egyben hozzáférhető (accessible) leírásként is szolgál. A hivatkozás megjegyzés (link annotation) az az eset, amelyet érdemes tanulmányozni, mert két formában létezik: egy puszta (bare) célállomás és egy művelet (action)

12 0 obj                                    % hivatkozás egy célállomásra
<< /Type /Annot /Subtype /Link
   /Rect [100 200 300 250]
   /Border [0 0 0]
   /Dest [5 0 R /XYZ null null null] >>
endobj
13 0 obj                                    % hivatkozás, amely egy műveletet hajt végre
<< /Type /Annot /Subtype /Link
   /Rect [50 50 200 100]
   /Border [0 0 0]
   /A << /Type /Action /S /URI /URI (https://www.example.com) >> >>
endobj

A /Rect (téglalap) egy hotspot; ha belekattintunk, az olvasót (reader) a célállomásra küldi, ugyanazt a nyelvtant (grammar) használva, amelyet a vázlat (outline) is használ. A /Border [0 0 0] valódi munkát végez, elnyomva (suppressing) a csúnya alapértelmezett téglalapot, amelyet a megjelenítők (viewers) rajzolnak a hivatkozások köré. A második forma a puszta /Dest-et egy /A (művelet) cseréli, amelynek /S altípusa kiválasztja a viselkedést: /GoTo ezen a fájlon belül, /GoToR egy másik fájlhoz, /URI egy webcímhez, /Launch egy külső program futtatásához. Ez utóbbi gyanakvást (suspicion) érdemel. Egy /Launch, amely elindít egy végrehajtható fájlt (executable), az a viselkedés, amely a PDF-eket kártékony szoftverek (malware vector) hordozójává teszi, így a megfelelő (conforming) megjelenítők blokkolják azt, vagy hangosan figyelmeztetnek (prompt loudly), és a hivatkozás a legtöbb olvasó számára meghiúsul (fails). Használja az /URI-t és a /GoTo-t, és hagyja békén a /Launch-ot

A jelölő (markup) megjegyzések, például a kiemelések (highlights) és az öntapadós jegyzetek, valamint az alakzat (shape) megjegyzések, például a /Square, hozzáadnak egy csavart (wrinkle): a képernyőn (on-screen) való megjelenésüket nem a típusuk határozza meg (implied). A megjelenítő a saját verzióját rendereli (renders its own version), hacsak nem rögzíti (pin) a megjelenést egy megjelenési folyammal (appearance stream), a /AP bejegyzéssel, amely egy form XObjectre hivatkozik, amely a rajzoló operátorokat tartalmazza. Ha ezt kihagyja, ugyanaz a kiemelés (highlight) máshogy nézhet ki két olvasóban, vagy egy szerkesztőben történt oda-vissza út (editor round-trip) előtt és után. Bármihez, aminek a pontos megjelenése a dokumentum része, biztosítsa a /AP-t. A fájlmellékletek egyébként (incidentally) ugyanezt a gépezetet (machinery) használják újra: egy beágyazott fájl folyamot és egy fájl specifikációs szótárat, amelyek vagy /FileAttachment megjegyzésként jelennek meg, vagy a katalógus /Names alatti /EmbeddedFiles név fán (name tree) keresztül

Hol törik meg ez a réteg, és hogyan lehet észrevenni (catch it)

Az ismétlődő (recurring) hiba (failure) mindezekben a lógó hivatkozás (dangling reference). A könyvjelzők nem jelennek meg többé, ha a katalógusban nincs /Outlines bejegyzés, vagy a testvérlánc (sibling chain) megszakad a fa közepén; a metaadatok figyelmen kívül lesznek hagyva, ha az XMP folyamból (stream) hiányzik a /Type /Metadata /Subtype /XML jelölés, vagy az xpacket burkoló (wrapper) hibásan formázott (malformed). A tartalom minden esetben rendben van az oldalon, így egy felületes (casual) megnyitás helyesnek tűnik, és a hiba csak azon a panelen bukkan fel (surfaces), amelyet senki sem ellenőrzött

Két olcsó szokás (cheap habits) a legtöbbet elkapja. Nyissa meg a kész fájlt egy valódi megjelenítőben (real viewer), és kattintson végig a könyvjelző panelen és a hivatkozások egy mintáján, ami úgy teszteli a hivatkozási gráfot (reference graph), ahogy azt egy olvasó fogja tenni. Ezután olvassa vissza a metaadatokat egy külön eszközzel, és győződjön meg arról, hogy az Info szótár és az XMP megegyezik, az az egyetlen nézeteltérés (disagreement), amelyet semmilyen mennyiségű kattintás nem tár fel (reveals). Hozza létre ezt a réteget egy olyan könyvtáron keresztül, amely birtokolja a hivatkozások könyvelését (link bookkeeping), és ezeknek a csapdáknak (traps) a legtöbbje soha nem nyílik meg. A Delphihez és C++Builderhez készült HotPDF Component dokumentum szintű API-kon keresztül teszi elérhetővé a vázlat (outline), a megjegyzés (annotation) és a metaadat (metadata) struktúrákat, így ön leírhatja a könyvjelző hierarchiát és a hivatkozásokat, és hagyhatja, hogy az összefűzze (thread) a hivatkozásokat. Ami azt az objektummodellt illeti, amelyhez ezek a struktúrák kapcsolódnak, a PDF fájlstruktúra műszaki áttekintése tárgyalja a katalógust és a kereszthivatkozási táblát, amelyektől függnek