Technical Article

Suspaustų PDF srautų patvirtinimas su PDFium

Atidarykite PDF dokumentą teksto rengyklėje, ir pamatysite, kad didžioji dalis failo yra ne tekstas, o dideli dvejetainių duomenų blokai, prasidedantys stream ir besibaigiantys endstream. Šie srautai neša puslapio turinį, vaizdus, šriftus ir ICC profilius, beveik visada suspaustus, kad būtų sutaupyta vietos. Kol suspaudimas veikia, viskas gerai. Tačiau kai srautas yra sugadintas – sutrumpintas perdavimo metu, sugeneruotas su klaida arba pakeistas trečiosios šalies įrankio – atvaizdavimo programa, bandanti jį išskleisti, gali pakibti, sugriūti (angl. crash) arba išvesti tuščią puslapį be jokio įspėjimo. Programuotojui, kuriančiam failų apdorojimo sistemą, gebėjimas patikrinti šių srautų vientisumą prieš atvaizdavimą yra pagrindinė gynyba nuo nestabilių failų

Šiame straipsnyje apžvelgiama PDF srautų suspaudimo architektūra pagal ISO 32000-1 §7.3.8 skyrių ir parodoma, kaip PDFium komponentas, skirtas Delphi ir Lazarus, patvirtina Flate, LZW ir ASCII85 srautus prieš jiems pasiekiant vartotojo ecrą

Kas yra PDF srautas ir jo filtrai

Srautas PDF dokumente susideda iš žodyno, aprašančio srauto savybes, ir pačių duomenų baitų tarp stream ir endstream raktinių žodžių. Žodynas nurodo srauto ilgį (/Length) ir filtrus, kurie buvo taikomi duomenims suspausti (/Filter). Specifikacija leidžia naudoti kelis filtrus iš eilės, pavyzdžiui, suspausti LZW būdu ir tada užkoduoti ASCII85, kad duomenys išgyventų tekstinius kanalus. Tokiu atveju /Filter įrašas neša filtrų pavadinimų masyvą nurodyta tvarka

Dažniausiai naudojamas filtras yra /FlateDecode, kuris įgyvendina zlib/deflate suspaudimą. Kiti įprasti filtrai yra /LZWDecode senesniems failams, /ASCII85Decode ir /ASCIIHexDecode tekstiniam kodavimui, bei vaizdams skirti filtrai, tokie kaip /DCTDecode (JPEG) ir /JPXDecode (JPEG 2000). Srauto patvirtinimas reiškia patikrinimą, ar duomenys gali būti sėkmingai iškoduoti per visą filtrų grandinę, nepažeidžiant jų struktūros

Srauto vientisumo patikros API

PDFium suteikia tiesioginį priėjimą prie neapdorotų ir iškoduotų srautų per mažo lygio (low-level) API funkcijas. Kad patikrintumėte srautą, galite bandyti perskaityti jo iškoduotus duomenis naudodami FPDFStream_GetRawData arba FPDFStream_GetDecodedData. Pirmasis tiesiog grąžina baitus iš disko, o antrasis paleidžia iškodavimo variklį per filtrų grandinę

Iškodavimo iškvietimas yra geriausias patikros įrankis. Jei duomenys yra sugadinti ar nebaigti, FPDFStream_GetDecodedData grąžina nulį arba mažesnį baitų skaičių nei nurodyta žodyne, o vidinis variklis užfiksuoja klaidą. Žemiau pateiktame pavyzdyje parodyta, kaip patikrinti konkretaus srauto objekto vientisumą

Klaidų klasifikavimas patikroje

Kai srautas nepraeina patikros, klaida gali būti kelių rūšių. Pirmasis tipas yra sutrumpintas srautas, kai failas baigėsi anksčiau nei nurodo /Length įrašas. Tai dažna tinklo perdavimo klaida. Tokiu atveju iškodavimo variklis praneša apie netikėtą srauto pabaigą

Antrasis tipas yra netinkami zlib/deflate kontroliniai skaitmenys (angl. checksums) Flate srautuose. Kiekvienas Flate srautas baigiasi Adler-32 kontroliniu skaičiumi, kurį zlib variklis tikrina iškodavimo metu. Jei duomenų baitas pasikeitė, kontrolinis skaičius nesutaps ir zlib grąžins klaidą. Šios klaidos yra aiškus signalas apie sugadintą diską arba perdavimo klaidą, o PDFium jas atskleidžia per bendrą iškodavimo nesėkmės būseną

Prediktoriai Flate ir LZW srautuose

Srauto žodynas gali turėti /DecodeParms įrašą, kuriame saugomi papildomi parametrai filtrams. Flate ir LZW atveju dažnai naudojamas /Predictor parametras, kuris taiko PNG arba TIFF prediktorius – matematinį metodą, kuris pakeičia baitų reikšmes skirtumais tarp gretimų taškų prieš suspaudimą. Tai labai pagerina vaizdų ir grafikų suspaudimą

Prediktoriai prideda dar vieną klaidų sluoksnį. Net jei zlib iškoduoja srautą sėkmingai, prediktoriaus algoritmas gali sugriūti, jei gauti duomenys neatitinka pločio ar spalvų gylio parametrų. PDFium atlieka prediktoriaus atkūrimą iškodavimo metu, todėl FPDFStream_GetDecodedData patikrina ir šią dalį. Jei prediktoriaus parametrai sugedę, iškvietimas nepavyks

Pilnas dokumento srautų skenavimas

Kad apsaugotumėte sistemą, patikrą turėtumėte atlikti ne tik atidarius failą, bet ir periodiškai nuskaitydami visus srautus. Tai ypač svarbu archyvavimo sistemose. Procedūra apima ėjimą per visus dokumento objektus, srautų identifikavimą ir jų iškodavimo patikrinimą

Nors šis skenavimas reikalauja procesoriaus laiko, jis garantuoja, kad dokumentas vėliau nepasimes. Žemiau pateikta Delphi procedūra, kuri pereina per visus puslapio išteklių srautus, patikrina kiekvieno Flate ir LZW srauto iškodavimą ir surašo klaidas

Procedūra naudoja FPDFDoc_GetPage puslapiui gauti, o tada ištraukia išteklių žodyną. Ji skenuoja vaizdų srautus ir puslapio turinio srautus atskirai, o tai leidžia tiksliai lokalizuoti klaidą, jei kuris nors vaizdas yra sugadintas

Rezultato interpretavimas konvejeryje

uses
  PDFium, FPdfPdfa;

function CheckPdfA(const FileName: string): TPdfAValidationResult;
var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Active := True;            // parses xref/object streams on load
    Result := Pdf.ValidatePdfA;    // sees the expanded object table
  finally
    Pdf.Free;
  end;
end;

Įtraukite šią patikrą į savo dokumentų įsiurbimo konvejerį iškart po pirminio saugumo audito. Jei patikra praneša apie srauto klaidą, blokuokite failo tolimesnį apdorojimą. Taip išvengsite peržiūros programos pakibimo ties sugadintu puslapiu ir apsaugosite spausdinimo procesus nuo netikėto sustojimo. Srautų patvirtinimas yra skydas, kuris pagauna sugadintus failus prieš jiems padarant žalos jūsų sistemoms

var
  Pdf: TPdf;
  R  : TPdfXValidationResult;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'Press_Ready.pdf';
    Pdf.Active := True;
    R := Pdf.ValidatePdfX;
    if R.IsCompliant then
      Writeln('PDF/X conformance: ', Ord(R.Conformance))
    else
      Writeln('Not conformant; issue count = ', SizeOf(R.Issues));
  finally
    Pdf.Free;
  end;
end;

Jei norite sužinoti daugiau apie tai, kaip tvarkyti saugumo rizikas, susijusias su aktyviu turiniu, žr. mūsų straipsnį apie PDF saugumo rizikų auditą. Daugiau informacijos apie saugų dokumentų atvaizdavimą Windows aplinkoje rasite saugios PDF peržiūros kūrimo apžvalgoje. Visi srautų patikros metodai platinami su PDFium komponentu, skirtu Delphi ir C++Builder

Once the structure is expanded, a validator can drive the rest of a workflow over it. For a command-line preflight harness that reports conformance across a folder of inputs, see our walkthrough on building a batch preflight report CLI. When validation is a gate ahead of breaking a large document apart, the techniques in our guide to splitting PDF documents into multiple files pair naturally with the load-and-check pattern shown here. Both build on the loading and validation surface of the PDFium Component for Delphi and C++Builder.