Tehnični članek

Dodajanje slik JPEG 2000 v PDF v Delphi s HotPDF

Skenirani medicinski preparat, ploščica iz aerofonametrične izmere, filmski kader, arhiviran pri polnem dinamičnem obsegu. To so slike, ki prispejo kot JPEG 2000, in prispejo tako z razlogom. Format ohranja 12 ali 16 bitov na kanal, stisne z valčno transformacijo namesto bločnega DCT, ki ga uporablja JPEG, in isti posnetek lahko kodira bodisi brez izgube bodisi z izgubo iz enega kodnega toka. Ko mora dokument, zgrajen iz teh virov, postati PDF, mora slika potovati skozi filter, ki ga specifikacija PDF rezervira natanko za ta kodek

HotPDF v2.228.0 je obnovil delujoč motor za dekodiranje JPEG 2000 za to pot. Prejšnja gradnja je enoto pristavila s stub funkcijami, ki so vračale nil, zato je API obstajal, a ni dekodiral ničesar. Trenutni motor statično veže OpenJPEG 2.5.4 in pretvori vir JP2 ali J2K v piksle, ki jih HotPDF lahko postavi na stran

Filter JPXDecode v PDF

ISO 32000-1 definira filter JPXDecode v §7.4.9. XObject PDF slike poimenuje svojo kompresijo v vnosu /Filter slovarja toka, in JPXDecode je vrednost, ki pove, da so podatki toka kodirani s JPEG 2000 in ne z osnovnim JPEG, ki ga nosi /DCTDecode. Filter je tisto, ki PDF-ju omogoča, da vsebuje podatke slik, stisnjene z valčno kompresijo in visoko bitno globino, in sprejema tako brezizgubni kot izgubni način kodeka, ker je način lastnost samega kodnega toka in ne ovitka okoli njega

Zadnja točka je vredna zapomnjenja. JPEG 2000 je en sam algoritem z brezizgubnim posebnim primerom, ne dva ločena formata. Reverzibilni valček 5/3 natanko rekonstruira izvirne vzorce; ireverzibilni valček 9/7 žrtvuje to natančnost za manjšo datoteko. Dekoder ob branju obravnava oba enako, zato HotPDF potrebuje samo eno pot dekodiranja, da sprejme kar koli, kar mu tok JPXDecode prinese

Kaj dekoder naredi s piksli

XObject-i slik PDF v pogostem primeru pričakujejo 8 bitov na komponento v prostoru DeviceGray ali DeviceRGB. JPEG 2000 to rutinsko presega, njegov komponentni model pa je bolj splošen kot pakiran rastrski vzorec, zato ima dekoder tri naloge, preden so podatki uporabni kot navadna slika

Najprej se komponente z visoko bitno globino preračunajo na 8 bitov. 12-bitni ali 16-bitni vzorec se skalira na razpon od 0 do 255, tako da je rezultat navaden 8-bitni rastrski vzorec. Predznačene komponente se najprej premaknejo v razpon brez predznaka. Podrobnost je pomembna, ker je sama po sebi izgubna: 16-bitni sivinski sken izgubi globok tonalni razpon, takoj ko postane 8-bitna slika PDF - kar je prava zamenjava za prikaz na zaslonu in tiskanje, ne pa za ponovno arhiviranje

Drugič, barvni prostor YCbCr (kodek ga imenuje SYCC) se pretvori v RGB. JPEG 2000 pogosto shranjuje barve v prostoru svetlost-krominanca za učinkovitost kompresije - enaka zamisel, ki jo uporablja osnovni JPEG - in dekoder uporabi standardno inverzno transformacijo, tako da stran prejme pravi RGB

Tretjič, podvzorčene komponente se povzorčijo z replikacijo najbližjega soseda. Krominančni kanali so pogosto shranjeni pri polovici ločljivosti, zato dekoder prebere vsako komponento pri lastnih dimenzijah in lastnem faktorju vzorčenja, nato pa replicira vzorce, da vsak kanal dvigne na polno velikost slike, preden jih prepleta. Najbližji sosed ohranja korak poceni; krominanca, ki jo zapolnjuje, je bila nizkofrekvečna že od začetka, zato je vidna cena majhna

Škatle JP2 v primerjavi s surovim kodnim tokom J2K

Datoteka JPEG 2000 pride v dveh oblikah, in HotPDF zazna, katero bere, iz prvih bajtov, ne pa iz končnice datoteke. Datoteka JP2 je vsebnik s strukturo škatel: odpre se z dvanajst-bajtno podpisno škatlo 00 00 00 0C 6A 50 20 20 in ovija kodni tok skupaj s škatlami, ki opisujejo barvni prostor, ločljivost in metapodatke. Surovi kodni tok J2K ne vsebuje vsebnika in se začne z oznako SOC FF 4F FF 51. Dekoder prebere te vodilne bajte, prepozna podpis in izbere ujemajoči se OpenJPEG kodek za vsak primer

Obe obliki sta podprti, ker se obe pojavljata v praksi. Naprave za zajem in arhivi, ki potrebujejo stranske metapodatke, oddajajo JP2; orodja, ki hočejo najmanjši možen tovor, oddajajo goli kodni tok. Vrsta formata je modelirana kot naštevanje TJpeg2000FileType s člani jtInvalid, jtJP2, jtJ2K in jtJPT. Člen JPT poimenuje različico pretakanja JPIP; detektor bajtnih podpisov razreši dve obliki, ki ju zna dekodirati - JP2 in J2K - in vse ostalo poroča kot jtInvalid, tako da nepodprten vhod odpove čisto, namesto da bi ustvaril smeti

uses
  HPDFJpeg2000;

var
  Decoder: THPDFJpeg2000Decoder;
  Pixels: TJpeg2000ByteArray;
begin
  Decoder := THPDFJpeg2000Decoder.Create;
  try
    if Decoder.LoadFromStream(Input) then          // JP2 or J2K, auto-detected
      if Decoder.GetImageData(Pixels) then
        // Pixels is 8-bit interleaved, ColorComponents channels wide,
        // row-major top to bottom: ready for a DeviceGray/DeviceRGB XObject.
        ProcessRaster(Decoder.Width, Decoder.Height,
                      Decoder.ColorComponents, Pixels);
  finally
    Decoder.Free;
  end;
end;

Brezizgubno in izgubno na strani kodiranja

Dekoder bere oba načina brez potrebe po poznavanju katerega. Izbira postane parameter šele, ko greste v drugo smer in ustvarite datoteko JPEG 2000, kar HotPDF prav tako zmore prek razreda TJpeg2000Bitmap - potomca TBitmap, ki naloži in shrani rastrske podatke kot JP2. Dve lastnosti urejata izhod. LosslessCompression je logična vrednost, ki pri vrednosti True izbere reverzibilni valček; CompressionQuality je TJpeg2000QualityRange - celo število od 1 do 100, kjer je 1 majhno in slabo, 100 pa veliko in zvesto. Privzete vrednosti živijo v poimenovanih konstantah: Jpeg2000DefaultLosslessCompression je False, Jpeg2000DefaultLossyQuality pa je 80

Odločitev je vsebinska. Brezizgubno ustreza matični kopiji, medicinskim ali pravnim skeniranjem, vsemu, kar se bo morda pozneje znova kodiralo in ne sme kopičiti generacijske izgube. Izgubno pri kakovosti 80 ustreza sliki, namenjeni zaslonu ali tiskanju, kjer valčeva elegantna degradacija daje opazno manjšo datoteko brez artefaktov, ki bi jih bralnik opazil. Ena opomba o CMYK: bitmapa izpostavi SetCMYK, da označi štirikanalske podatke kot CMYK namesto RGBA, kar je pomembno za tiskalne cevovode, ki ohranjajo ločitve nedotaknjene

uses
  HPDFJpeg2000;

var
  Bmp: TJpeg2000Bitmap;
begin
  Bmp := TJpeg2000Bitmap.Create;
  try
    Bmp.LoadFromStream(Source);              // decode an existing JP2/J2K
    Bmp.LosslessCompression := True;         // reversible 5/3 wavelet
    // or, for a smaller lossy file:
    // Bmp.LosslessCompression := False;
    // Bmp.CompressionQuality := 80;         // matches the default
    Bmp.SaveToStream(Output);                // always writes a JP2 file
  finally
    Bmp.Free;
  end;
end;

Zakaj ni cevovoda filtrov za dekodiranje ob nalaganju

Ena arhitekturna dejstvo oblikuje način, kako kar koli od tega uporabljate, in zlahka predpostavljamo nasprotno. HotPDF nima splošnega filtra za dekodiranje slik ob nalaganju. Ko odprete PDF, ki že vsebuje sliko JPXDecode, motor tega toka ne dekodira. Ohranja bajte JPEG 2000 točno takšne, kot so, zato kopija strani ali združitev dokumenta prenese sliko skozi nedotaknjeno, bajt za bajtom. Dekoder ima eno samo vstopno točko in je na strani ustvarjanja: AddImage, ki temelji na datoteki, poslan po končnici za upravljanje virov .jp2, .j2k, .jpt in .jpc

Ta ločitev je pravilna zasnova, ne omejitev. Dekodiranje vgrajenega toka JPX ob nalaganju samo zato, da ga ob shranjevanju znova kodirate, bi pretvorilo brezizgubno arhivirano sliko v izgubno in napihnjuje vsako združitev - vse to za sliko, ki ste jo nameravali samo premakniti iz enega PDF-ja v drugega. Prenos toka bajt za bajtom je brezizgubna in hitra operacija. Dekodiranje je odloženo na edini trenutek, ko je resnično potrebno: ko motorju podate datoteko JPEG 2000 z diska in jo prosite za rastriranje slike za postavitev na novo stran. V tem trenutku mora datoteka postati piksli in dekoder se zažene

Registracija podpore in postavitev slike

Registracija slike JPEG 2000 je neobvezna za stikalom prevajalnika HPDF_REGISTER_JPEG2000_PICTURE, ki je privzeto izklopljeno. Razlog je dejanski konflikt, ne previdnost: globalna registracija formatov datotek jp2, j2k in jpc v TPicture lahko moti zaznavanje formata BLOB, na katerega se zanaša TppDBImage v ReportBuilderju. Definirajte stikalo, ko ta integracija ni aktivna, in formati datotek se registrirajo, tako da jih TPicture prepozna; pustite ga nedefiniranega in razpošiljanje razširitvij AddImage še vedno neposredno dekodira datoteke JPEG 2000, ker ta pot sploh ne gre skozi TPicture

Ko to razumete, je postavitev slike JPEG 2000 enak trikratni ritem klica kot vsaka druga slika HotPDF. Podajte AddImage pot .jp2 in vrsto kompresije za shranjevanje slike v izhodu, nato postavite vrnjeni indeks slike na stran s ShowImage

var
  Pdf: THotPDF;
  ImgIndex: Integer;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.BeginDoc;
    Pdf.AddPage;
    // The .jp2 source is decoded through the OpenJPEG backend, then
    // re-embedded with the compression you request here.
    ImgIndex := Pdf.AddImage('Scan_16bit.jp2', icJpeg);
    // x, y, width, height in points; final 0 is the rotation angle.
    Pdf.ShowImage(ImgIndex, 72, 72, 400, 300, 0);
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Kompresija, ki jo posredujete AddImage, nadzoruje, kako je dekodirani posnetek znova shranjen - ne kako je bil prebran. Datoteko JPEG 2000, dekodirano v rastrski vzorec, je mogoče vrniti kot DCTDecode JPEG, rastrski vzorec Flate ali drug podprt filter - kar ustreza dokumentu. Dekodiranje iz JP2 ali J2K se zgodi najprej ne glede na to, zato isti klic sprejme vir, stisnjen z valčno kompresijo, in ga vgradi v katero koli obliko, ki jo pričakuje preostali del vašega cevovoda

Za širšo sliko tega, kako slike in pisave pristanejo v generiranem izhodu, si oglejte naše opombe o izhodnih poročilih s pisavami in slikami. Ko dokument, ki ga sestavljate, znova uporablja vsebino iz obstoječih PDF-jev, se vedenje prehoda, opisano tukaj, poveže z mehaniko združevanja in revizij v tokovih objektov in postopnih posodobitvah. Motor za dekodiranje JPEG 2000 se prinaša kot del komponente HotPDF za Delphi in C++Builder, skupaj z API-ji za slike, pisave in dokumente, ki so obravnavani drugje na tem blogu