PDF-tiedoston kummitus (The Phantom of a PDF File)
Tämä teksti on osa blogisarjaamme, jossa yksittäiset henkilöt kirjoittavat sekalaisista pitkäaikaissäilytysteemoista vapaamuotoisesti. Blogisarjaamme ei ole tarkoitettu ohjaaviksi säännöiksi tai ohjeiksi, vaan inspiraatioksi ideoille ja keskustelulle, myös päinvastaisille ajatuksille.
Muutoshistoria:
- Päivitys 22.11.2024: Olemme iloisia siitä, että blogimme on johtanut hyvään keskusteluun viime viikkoina. Kiitämme siitä, että blogimme on johtanut erilaisten työkalujen kokeiluun, ks. Johan van der Knijffin vastausblogi. Tämä osoittaa, että oktaalikoodit, joita käytetään yhdessä UTF-16BE-koodattujen tekstimerkkijonojen kanssa, ovat hyvin tuettuja eri työkaluissa. Olemme päivittäneet Loppunäytöksen ja lisänneet pari muutakin lausetta blogiin. Sanamuoto "kaksoiskoodaus" ei ehkä ole paras, vaikka käytännössä UTF-16BE:ssä on usean tavun merkkejä, kun taas oktaalikoodaus tehdään tavu kerrallaan. Liittyen bittien määrään UTF-16BE- ja oktaalikoodeissa, raportoimme myös pyynnön PDF-määrityksestä.
- Päivitys 11.11.2024: Viittaamme nyt uusimpaan PDF-määritykseen ISO 32000-2:2020. UTF-16BE- ja oktaalimuodon kaksoiskoodauksen ei enää katsota rikkovan määritystä, mutta pohdimme hieman enemmän oktaalimuodon määrittelyä. Pitkäaikaissäilyttämisen näkökulmasta kaksoiskoodausta ei edelleenkään suositella. Tätä käsitellään hieman enemmän kirjoituksen loppunäytöksessä.
Tämä kummitustarina on kirjoitettu Halloweenin 2024 yhteydessä. Se perustuu tositapahtumiin. Kyseessä ollut PDF-tiedosto on kuitenkin korvattu tähän tarinaan testitiedostolla.
Esinäytös (Prologue)
Saimme äskettäin yksinkertaisen PDF 1.4 -tiedoston, joka ei läpäissyt kaikkia tarkistuksia validoinnissa. JHOVE (v. 1.30.0) raportoi, että PDF-tiedosto on rikki ("Not well-formed"). JHOVE:n ilmoittama virheviesti oli kielioppivirhe "PDF_HUL_66 Lexical Error" tavupaikassa 1888. Tämä tarkoittaa, että PDF-tiedoston teknisessä "kielessä" on jotain vikaa PDF-tiedoston sisäisessä rakenteessa.
Lupasimme analysoida tiedoston katsoaksemme, mikä siinä oli vikana. Voi pojat, mikä reissu!
Näytös 1: PDF-rakenteen (haudan)kaivelua
Aloimme tutkia tiedostoa heksaeditorilla. Tavupaikka 1888 oli keskellä zlib/deflate-koodattua kuvadataa. JHOVE:lla ei pitäisi olla syytä tehdä mitään kielioppitarkistusta kuvadatan keskellä. Tämä herätti heti epäilyksemme siitä, että jotain outoa oli meneillään. Kuvadataobjektin sanakirja on:
<< /N 3 /Alternate /DeviceRGB /Length 3187 /Filter /FlateDecode >>
Dataosion eli striimin todellinen pituus on 3187 tavua, joten sanakirjan tiedoissa, kuten ilmoitetussa pituudessa, ei ole mitään vikaa. Striimiobjektin tunniste on "7 0", ja siihen viitataan objektista "6 0" seuraavasti:
[ /ICCBased 7 0 R ]
Seurasimme objektiviittauksia ja päädyimme lopulta PDF-tiedoston ensimmäiseen elementtiin. Mitään poikkeuksellista ei löytynyt mistään.
Mitä seuraavaksi? On aika palata JHOVE:n palauttamaan virheilmoitukseen.
JHOVE ilmoittaa kielioppivirheestä (Lexical error), kun numeerisessa arvossa on odottamaton merkki tai kun PDF-sanakirjaobjekti ei pääty oikein merkkeihin ">>". PDF-tiedostossa on 10 objektia, joista 9 aivan oikein alkaa "<<"-merkeillä ja päättyy vastaavasti ">>"-merkkeihin. Jäljellä oleva objekti on lista, jota aivan oikein ympäröivät hakasulkeet "[" ja "]". Kolmessa objektissa on striimejä, joista jokainen alkaa aivan oikein sanalla "stream" ja päättyy sanaan "endstream". Pituusparametri Length on oikein kaikissa striimiobjekteissa. Kaikki objektit alkavat "X 0 obj"-merkeillä (jossa X on objektin indeksi) ja päättyvät "endobj"-sanaan asianmukaisesti.
Aloimme tarkastella xref-taulukkoa, joka sisältää objektien tavupaikat. Kaikki tavupaikat viittaavat objektien alkuun, ja objektit on numeroitu oikein suhteessa xref-taulukon indekseihin.
Kokonaisuudessaan PDF-rakenne näyttää hyvältä.
Väliaika: Tiedoston korjaaminen Acrobat Readerilla
Tiedämme, että jotkut PDF:n perusongelmat voidaan korjata yksinkertaisesti avaamalla tiedosto Adobe Acrobat Reader -ohjelmalla ja tallentamalla se uuteen tiedostoon ilman muutoksia. Päätimme tehdä tämän tälle PDF-tiedostolle. Kun tiedoston tallentaa Acrobat Readerilla, JHOVE ilmoittaa nyt uuden tiedoston olevan validi: "Well-formed and valid".
Avasimme molemmat tiedostot Acrobat Readerissa ja ne näyttivät identtisiltä. Käydessämme läpi PDF-tiedostorakennetta heksaeditorillamme, korjatun tiedoston rakenne oli hieman erilainen alkuperäiseen verrattuna. Tiedoston tallentaminen Acrobat Readerilla aiheutti pieniä muutoksia PDF-rakenteen objekteihin, mutta ensisilmäyksellä muutoksissa ei näyttänyt olevan mitään erikoista. Varmuuden vuoksi laskimme alkuperäisen ja uuden PDF-tiedoston sisältämien dataosioiden tarkistussummat vahvistaaksemme, että striimeihin ei ole tullut muutoksia.
Ongelma ei ole alkuperäisen tiedoston tavupaikassa 1888, eikä missään siihen suoraan tai epäsuorasti viittaavassa, vaan jossain muualla.
Näytös 2: Se on tuolla! PDF-tiedoston kummitus!
Tähän asti olimme vain tarkasti tarkastaneet kohteet, joista on suora tai epäsuora viittaus objektiin, jonka tavupaikassa virhe syntyy. Koska ne ovat kunnossa, syyn täytyy olla joissain muissa PDF-tiedoston objekteissa. Lopulta tajusimme, että PDF-tiedostoa korjatessa Producer-kenttä (tuottaja) on dokumentin tietohakemistossa (Document Information Dictionary) muuttunut. Alkuperäinen Producer-kenttä näyttää oudolta:
/Producer (\376\377\000P\000D\000F\000 \000P\000h\000a\000n\000t\000o\000m\000\000)
Acrobat Readerin avulla korjatussa tiedostossa se on:
/Producer (PDF Phantom)
Alkuperäinen Producer-kenttä alkaa 8-tavuisella merkkijonolla "\376\377" (heksakoodit 5C 33 37 36 5C 33 37 37). PDF-tiedostomuoto sisältää PostScript-muodosta perityn oktaalimerkkikoodausmuodon, ja sitä käytetään nyt tässä metatiedossa. Oktaaliarvot 376 ja 377 vastaavat heksa-arvoja FE ja FF. Tämä viittaa BOM-merkkiin (tavujärjestysmerkki, byte order marker, heksa-arvo FEFF), jota käytetään UTF-16BE-koodauksessa. Tämä tarkoittaa, että UTF-16BE-koodattu merkkijono on kaksoiskoodattu PDF-tiedostomuodon oktaalimuodolla. Samoin alimerkkijono "\000" viittaa vain null-tavuun, ja varsinaisen sisällön voi poimia näiden null-koodien välistä: \000P --> P, \000D --> D, \000F --> F, jne.
PDF-määritys ISO 32000-2:2020 määrittelee kohdassa 7.9.2.2.1, että kahden ensimmäisen tavun on oltava 254 ja 255 UTF-16BE-koodatuissa merkkijonossa. Kaksoiskoodauksessa "\376\377" BOM-tavujärjestysmerkkiä ei enää soviteta kahteen tavuun. Oktaalikoodit vastaavat kuitenkin näitä kahta tavua ja ovat osa kirjaimellisten merkkijonojen (literal string) määritelmää, eivät tekstimerkkijonojen (text string).
Määrittelyn kohdassa 7.3.4.2 oktaalikoodausta kuvataan "character code":ksi, joka intuitiivisesti antaa ymmärtää, että oktaalikoodi olisi dekoodattava merkiksi. Luvun lopussa jatketaan, että mikä tahansa 8-bittinen arvo voi esiintyä myös kuvatulla oktaalimuodolla. On silti melko epäselvää, pitäisikö yksittäinen oktaalimuotoinen "merkkikoodi" voida dekoodata merkiksi vai merkin osana olevaksi tavuksi. UTF-16BE-koodatussa merkkijonossa tässä on ero, koska kaikki merkit vievät 2 tai 4 tavua.
JHOVE ei pysty käsittelemään alkuperäisen tiedoston kaksoiskoodausta. JHOVE ei löydä sulkevaa sulkumerkkiä ")" Producer-kentästä tietääkseen, milloin kenttä päättyy. Näin ollen se jatkaa tiedoston lukemista, kunnes lopulta saavuttaa tavupaikassa 1888 ongelman (kielioppivirhe, Lexical error), jota ei voi enää tulkita. Dokumentin tietohakemisto -objektin Producer-kenttä on olemassa PDF-tiedoston tavupaikassa 87-169. Tässä oli aikamoinen jäljitys takaisin virheilmoituksen tavupaikasta 1888.
UTF-16BE-merkit käyttävät 2 tai 4 tavua kukin, ja alun perin JHOVE yhdistää ")"-merkin viereisen tavun (tai viereisten tavujen) kanssa merkin muodostamiseksi. Pelkkä välilyönnin lisääminen ennen päättyvää sulkumerkkiä ")" saa tiedoston validiksi JHOVE:n mukaan, mutta sen seurauksena JHOVE tuottaa hölynpölyä metatietoon tuottajasta (Producer).
Näytös 3: XMP kilttinä kummituksena
Acrobat Reader ymmärtää, että tuottajan pitäisi olla "PDF Phantom", ja tallentaa sen Producer-kenttään käyttäen tavallista US-ASCII-koodausta. Testataksemme korjauksen toimivuutta, muutimme tuottajaksi heksaeditorilla "Ananasakäämä", jotta se sisältäisi skandinaavisia merkkejä, ja koodasimme sen uudelleen UTF-16BE:n ja oktaalimuodon yhdistelmällä. Meidän tuli myös varmistua xref-taulukon tavupaikkojen ja startxref-tavupaikan käsittelystä, koska muiden objektien tavupaikat muuttuivat. Producer-kenttä testissämme näytti tältä:
/Producer (\376\377\000A\000n\000a\000n\000a\000s\000a\000k\000\344\000\344\000m\000\344\000\000)
Oktaaliluvut \000\344 vastaavat heksalukuja 00E4, joka on UTF-16BE:ssä "ä"-merkki. Avasimme ja tallensimme testitiedoston Acrobat Readerilla, ja tulos näytti mielenkiintoiselta. Saimme:
/Producer (PDF Phantom)
Toisin sanoen pääsimme juuri eroon "PDF Phantom”:sta, ja nyt se on taas olemassa!
Pelottavaa!
Lopulta selvisi, että koska tiedostossa on myös XMP-metatietoa, täysin eri objektissa PDF-tiedoston sisällä, Acrobat Reader ylikirjoittaa dokumentin tietohakemisto -objektissa (Document Information Dictionary) olevan Producer-kentän XMP-metatiedon Producer-kentällä, joka näyttää tältä:
<pdf:Producer>PDF Phantom</pdf:Producer>
Näytös 4: Kummituksen muodonmuutos
Teimme toisen testin, jossa poistimme XMP-metatiedon alkuperäisestä tiedostosta. Tämän seurauksena Acrobat Reader näyttää poistavan oktaalimuodon, mutta se säilyttää UTF-16BE-koodauksen "PDF Phantom" -merkkijonolle. Tuloksena on tämä:
/Producer (<fe><ff><00>P<00>D<00>F<00> <00>P<00>h<00>a<00>n<00>t<00>o<00>m<00><00>)
Tässä <fe>, <ff> ja <00> ovat ei-tulostuvia tavuja (yksi tavu kukin), jotka vastaavat vastaavasti heksakoodeja FE, FF ja 00.
Loppunäytös (Finale)
Oikean koodauksen valitseminen on tärkeää. Luultavasti standardoiduin tapa koodata metatietomerkkijonoja nykyään on käyttää US-ASCII:tä, jos mahdollista, ja Unicodea muuten. Sen sijaan PDFDocEncoding US-ASCII:n ulkopuolisilla merkeillä periytyy vanhasta PostScript-maailmasta ja nojaa laajalti vanhentuneeseen Latin-1:een.
Jos metatieto koostuu pelkistä US-ASCII-merkeistä, US-ASCII:tä voidaan käyttää suoraan. Tällöin koodaaminen UTF-16BE:llä ja oktaalikoodeilla on mitä luultavimmin tarpeetonta eikä ole mielekästä. Se lisää merkkijonon teknistä monimutkaisuutta ilman hyvää syytä. Myös PDF-määrityksen tulkitsemisen näkökulmasta pitkäaikaissäilytyksessä, kuten metatietojen merkkijonojen ymmärtämisessä, oktaalikoodien määritelmä voisi olla selkeämpi (eli merkkikoodit vs. tavujen koodaus UTF-16BE:ssä).
Ohjelmistotuki UTF-16BE- ja oktaalikoodien kaksoiskoodaukselle on myös tärkeä näkökohta. JHOVE hämmentyi siitä nyt, mutta entä muut ohjelmistot? Johan van der Knijff teki blogitekstimme innoittamana (ks. alaviite) laajan kokeilun, jossa todettiin, että tässä tapauksessa on olemassa laaja ohjelmistotuki, katso blogi "Escape from the Phantom of the PDF". Olemme kiitollisia tästä kokeilusta ja olemme samaa mieltä siitä, että ohjelmistotuki ei todennäköisesti ole ongelma. Nykyään on tärkeää miettiä tällaisia asioita, jotta minimoidaan riskit siitä, että digitaalisen sisällön ominaisuuksia ei pystytä säilyttämään ohjelmistotuen puuttumisen vuoksi. Jos jokin ominaisuus on nykyään ongelmallinen, se on mitä todennäköisimmin ongelma myös tulevaisuudessa.
Kirjoittanut Juha Lehtonen & Johan Kylander
Kansalliset pitkäaikaissäilytyspalvelut
Alaviite: Alun perin kirjoitimme: "JHOVE ei todennäköisesti ole ainoa ohjelmisto, joka hämmentyy metatietojen kaksoiskoodauksista. Tulevissa migraatioissa tällaiset asiat on tunnistettava ja käsiteltävä jotenkin saatavilla olevalla ohjelmistotuella. Ellemme ratkaise asiaa tänään."