Tekninen artikkeli

Natiivi JBIG2-pakkaus kaksitasokuville Delphi PDF -tiedostoissa

Skannattu sopimus on satoja pisteitä tuumalla mustaa mustetta valkoisella paperilla. Tallennettuna yhden bitin pikseliä kohden käyttävänä bittikarttana se on jo pieni, mutta sata tällaista sivua paisuttaa PDF-tiedoston liian suureksi sähköpostiin. Oikea suodatin muuttaa laskutoimitusta. JBIG2 on tehokkain ISO 32000-1 -standardin määrittelemä pakkaus kaksitasokuville, ja skannatun tekstin pinossa se puolittaa CCITT Group 4:n tuottaman koon. Tämä on suodatin, joka kannattaa valita, kun syöte on faksattu, skannattu tai muuten pienennetty kahteen väriin, ja HotPDF voi kirjoittaa sen suoraan PDF-tiedostoon

Muoto saavuttaa pakkaussuhteensa kahdella idealla, joita yleisellä kuvakoodekilla ei ole. Se mallintaa mustien alueiden sijoittumista valkoista taustaa vasten, ja se huomaa skannatun sivun koostuvan enimmäkseen samoista sadoista merkkimuodoista, jotka toistuvat tuhansia kertoja. Molempien ymmärtäminen antaa mahdollisuuden valita pakkausasetukset tarkoituksella arvailun sijaan

JBIG2:n paikka PDF-määrityksessä

ISO 32000-1 listaa JBIG2Decode-suodattimen virtasuodattimien joukossa kohdassa §7.4.7, ja se on saatavilla PDF 1.4 -versiosta alkaen. Sitä sovelletaan vain yhteen paikkaan: kuva-XObjecteihin, joiden /BitsPerComponent on 1 ja joiden väriavaruus ratkeaa yhdeksi kanavaksi. Se on koko asian ydin. JBIG2 on kaksitasokoodekki, joten se ei koskaan kilpaile valokuvissa DCT:n tai JPXDecoden kanssa. Se kilpailee CCITTFaxDecode-suodattimen, eli Group 3- ja Group 4 -faksisuodattimien, kanssa juuri sellaisilla kaksisävyisillä sivuilla, joita asiakirjaskanneri tuottaa

Dekooderi hyödyntää upotettua JBIG2-organisaatiota, jota standardi kutsuu PDF-profiiliksi, jossa kukin kuvavirta sisältää segmenttien sarjan pelkän bittivirran sijaan. Valinnainen /JBIG2Globals-virta kuljettaa useiden kuvien kesken jaettuja segmenttejä samassa asiakirjassa, mikä on mekanismi, jonka avulla toistuva sisältö voidaan tallentaa kerran koko tiedostoon eikä kerran sivulle. HotPDF lähettää kuvakohtaisen virran oletuksena ja pitää globaalikanavan vapaana, ellei taustajärjestelmä pyydä sitä

Taustajärjestelmä edellä kulkeva kooderiarkkitehtuuri

Täydellinen JBIG2-kooderi on suuri ohjelmisto, ja sen aggressiivisimmat osat ovat historiallisesti olleet patenttien rasittamia ja niitä on toimitettu lisensseillä, jotka eivät sovi jokaiseen tuotteeseen. HotPDF ratkaisee tämän jännitteen erottamalla käyttöliittymän moottorista. Yksikkö HPDFJBIG2 määrittelee kutsut, joita muu kirjasto tekee, ja se sisältää vaatimattoman sisäänrakennetun kooderin, jotta JBIG2 toimii suoraan asennuksen jälkeen. Kun tarvitset tuotantotason pakkaussuhteita, rekisteröit vahvemman moottorin, jolle kirjasto delegoi työn ilman muutoksia kutsuvaan koodiisi

Vaihto tapahtuu yhdellä rekisteröintikutsulla. Jos taustajärjestelmää ei ole rekisteröity, kooderi turvautuu sisäänrakennettuun polkuunsa. Rekisteröi sellainen, niin jokainen myöhempi koodaus kulkee sen kautta

uses
  HPDFJBIG2;

// Query what is active, then optionally install a stronger engine.
if not IsJBIG2EncoderBackendAvailable then
  // Production backend not present: HotPDF uses its built-in MMR path.
  RegisterJBIG2EncoderBackend(MyVendorJBIG2Encode);

// Later, to return to the built-in behaviour:
// ClearJBIG2Backends;

Sama koukku on olemassa dekoodausta varten RegisterJBIG2DecoderBackend-kutsun kautta, ja IsJBIG2DecoderBackendAvailable-kutsua käytetään sen tarkistamiseen. Tästä syystä kirjasto toimitetaan pienellä sisäänrakennetulla polulla ja taustajärjestelmän rajapinnalla yhden monoliittisen kooderin sijaan. Sisäänrakennettu polku pitää binäärin kevyenä ja vapaana lisenssisotkuista, kun taas rajapinnan ansiosta täyden kooderin lisensoinut tiimi voi liittää sen koskematta lainkaan PDF-kirjoituskerrokseen

Mitä pakkausasetukset todellisuudessa vaihtavat

Koodaus määritetään TJBIG2EncodeOptions-tietueen kautta, jossa on kentät Lossless, UseGlobalSegments, UseSymbolDictionary ja LossyLevel. Komponenttiystävällinen kääre THPDFJBIG2Options julkaisee ominaisuudet Lossless, UseSymbolDictionary ja LossyLevel, jotta ne voidaan asettaa Object Inspectorista, ja se muunnetaan tietueeksi sisäisesti. Asetuksia ohjaa kolme tarkoitusta

Häviötön uudelleenrakennus säilyttää jokaisen pikselin. Aseta Lossless arvoon True ja jätä LossyLevel nollaan, jolloin dekoodattu bittikartta on bitilleen identtinen syötteen kanssa. Tämä on ainoa turvallinen valinta viivapiirroksille, teknisille piirustuksille ja kaikille sivuille, joilla pudonnut pikseli voisi muuttaa merkitystä, kuten allekirjoitukselle tai leimalle. Symbolisanakirjakoodaus kytkee päälle tekstitietoisen deduplikoinnin ja on vaihtoehto, joka erottaa JBIG2:n faksisuodattimista. Häviöllinen taso, kokonaisluku väliltä 0 ja 9, antaa kykenevän taustajärjestelmän vaihtaa tarkkuutta kokoon käsittelemällä lähes identtisiä merkkejä samana symbolina. Nolla tarkoittaa häviötöntä. Sisäänrakennettu kooderi noudattaa vain häviötöntä polkua ja jättää huomiotta kaikki nollasta poikkeavat häviötasot, joten korkeammat tasot tulevat voimaan vasta, kun ne toteuttava taustajärjestelmä on rekisteröity

var
  Options: TJBIG2EncodeOptions;
begin
  Options := DefaultJBIG2EncodeOptions;   // Lossless True, symbol dictionary on
  Options.Lossless := True;
  Options.LossyLevel := 0;                // 0 keeps every pixel
  Options.UseSymbolDictionary := True;    // dedupe repeated glyphs
  // Pass Options to a backend, or let THPDFJBIG2Options carry them.
end;

Symbolisanakirjat ja miksi tekstiskannaukset voittavat

Skannattu tekstisivu ei oikeastaan ole kuva sanoista. Se on sama kirjain e tulostettuna satoja kertoja, sama t, sama pilkku, ja jokainen esiintymä on hieman kohinainen kopio yhdestä taustalla olevasta muodosta. Symbolisanakirja kaappaa tämän rakenteen. Kooderi kerää sivun erilliset merkit sanakirjaan, tallentaa jokaisen muodon kerran ja tallentaa sitten sivun luettelona sijainneista, jotka viittaavat sanakirjan merkintöihin. Tuhat saman merkin esiintymää maksaa yhden tallennetun bittikartan ja tuhat halpaa sijoitusta

Juuri tässä JBIG2 nousee CCITT Group 4:n edelle. Group 4 koodaa jokaisen skannausrivin sen yläpuolella olevaa riviä vasten ilman käsitystä merkeistä, joten se maksaa täyden hinnan jokaisesta kirjaimesta aina, kun kirjain esiintyy. JBIG2 maksaa kerran. Kun sama sanakirja ylennetään asiakirjatason globaalivirtaan, säästö moninkertaistuu monisivuisessa skannauksessa, koska sivu toisensa jälkeen jakamat muodot tallennetaan vain kerran koko tiedostoon. Tiheässä tekstissä ero ei ole marginaalinen. Se on syy, miksi JBIG2 on olemassa

Yleinen alue ja MMR kaikelle muulle

Jokainen kaksitasokuva ei ole tekstiä. Kartoissa, kaavioissa, teknisissä piirustuksissa ja sekasivuissa on viivapiirroksia, joita mikään sanakirja ei voi tiivistää. Näitä varten JBIG2 koodaa yleisen alueen, suorakulmion pikseleitä, jotka on pakattu suoraan ilman symboliopetusta. Standardi sallii yleisen alueen käyttää MMR:ää, muokattua READ-koodausta, jota Group 4 -faksi jo käyttää ja joka mallintaa jokaisen pikselirivin sen yläpuolella olevaa riviä vasten

Tämä on se polku, jonka HotPDF toimittaa sisäänrakennetussa kooderissaan. Kun taustajärjestelmää ei ole rekisteröity ja pyyntö on häviötön, kirjasto pakkaa bittikartan yhtenäiseksi MMR-yleisalueeksi ja käärii sen JBIG2-segmenttirakenteeseen, jota PDF-profiili vaatii. Se ei tarvitse sanakirjaa, opetusvaihetta tai toista kuvaa viitteeksi, joten se on luotettava oletus viivapiirroksille ja sekamuotoiselle kaksitasosisällölle. Se ei vedä vertoja täydelle symbolisanakirjakooderille puhtaassa tekstissä, mutta se on aina oikein, aina häviötön ja aina paikalla. Kooderin rajapinta sitä varten on yksi kutsu

var
  Encoder: THPDFJBIG2Encoder;
  ImageData: TJBIG2ByteArray;
  Scanlines: TJBIG2ScanlineArray;  // one byte array per row, MSB-first
  W, H: Integer;
begin
  // Scanlines, W and H describe a 1-bit page; each row is (W + 7) div 8 bytes.
  Encoder := THPDFJBIG2Encoder.Create;
  try
    if Encoder.EncodeToByteArray(Scanlines, W, H, ImageData) then
      // ImageData now holds a JBIG2 stream ready for a /JBIG2Decode XObject.
      ;
  finally
    Encoder.Free;
  end;
end;

Sen käyttöönotto asiakirjaa rakennettaessa

Päivittäisessä käytössä et koske kooderiluokkaan suoraan. HotPDF tarjoaa JBIG2:n kuvanpakkausvaihtoehtona asiakirjalle. Luettelo THPDFImageCompressionType sisältää icJBIG2:n Flate-, JPEG- ja CCITT-vaihtoehtojen rinnalla, ja asiakirjassa on JBIG2Options-ominaisuus, jonka tyyppi on THPDFJBIG2Options, ja joka sisältää asetukset, joita käytetään, kun kyseinen pakkaus valitaan. Määritä molemmat ennen kuin lisäät kaksitasokuvat, jotka haluat pakata tällä tavalla

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.ImageCompressionType := icJBIG2;     // route 1-bit images through JBIG2
    Pdf.JBIG2Options.Lossless := True;        // keep every pixel
    Pdf.JBIG2Options.UseSymbolDictionary := True;
    Pdf.JBIG2Options.LossyLevel := 0;
    // Add pages and place your scanned 1-bit images here.
  finally
    Pdf.Free;
  end;
end;

Yksi mainitsemisen arvoinen mukavuus on DBGridHotPDFExport-lisäosa, joka hahmontaa TDBGrid:n suoraan PDF-tiedostoksi. Sen tuloste on pitkälti kaksitasoisia viivoja ja tekstiä, joten JBIG2:lle määritetty asiakirja pitää nämä viennit kompakteina ilman ylimääräistä käsittelyä. Kaksi tähän liittyvää aihetta tässä blogissa perehtyy syvemmälle ympäröivään työnkulkuun. Lisätietoja siitä, kuinka kuvat ja fontit asetetaan raportteja rakennettaessa, katso raportin tulostus fonteilla ja kuvilla Delphissä. Kun pakatun asiakirjan on täytettävä arkistointiprofiili, säännöt artikkelissa PDF/A-, PDF/X- ja PDF/UA-validointi Delphissä kertovat, mitkä suodattimet tietty vaatimustenmukaisuustaso hyväksyy. JBIG2 toimitetaan osana HotPDF-komponenttia Delphille ja C++Builderille täällä muualla käsiteltyjen lataus-, muokkaus- ja salausohjelmointirajapintojen ohella