Technický článek

Přidání obrázků JPEG 2000 do PDF v Delphi pomocí HotPDF

Naskenovaný medicínský snímek, dlaždice leteckého průzkumu, filmový snímek archivovaný v plném dynamickém rozsahu. To jsou obrázky, které přicházejí jako JPEG 2000, a přicházejí tak z doprého důvodu. Formát uchovavá 12 nebo 16 bitů na kanál, komprimuje vlnkovou transformací místo blokovaného DCT používaného v JPEG a dokáže kódovat tentýž obrázek buď bezeztrátově nebo ztrátově z jediného kódového proudu. Když se má dokument sestavovaný z těchto zdrojů stát PDF, musí obrázek projít filtrem, který specifikace PDF vyhrazuje přesně pro tento kodek

HotPDF v2.228.0 obnovil funkční dekodér JPEG 2000 pro tuto cestu. Dřívější sestava obsahovala jednotku s prázdnými funkcemi vracejícími nil, takže API existovalo, ale nedékódovalo nic. Současný dekodér staticky váže OpenJPEG 2.5.4 a převádí zdroj JP2 nebo J2K na pixely, které HotPDF dokáže umístit na stránku

Filtr JPXDecode v PDF

ISO 32000-1 definuje filtr JPXDecode v §7.4.9. Obrazový XObject v PDF označuje své kompresní schéma položkou /Filter v datovém slovníku proudu a JPXDecode je hodnota říkající, že data proudu jsou kódovým proudem JPEG 2000, nikoli základním JPEGem, který nese /DCTDecode. Filtr umožňuje PDF uchovávat obrázková data komprimovaná vlnkovým algoritmem s vysokou bitovou hloubkou a podporuje jak bezeztrátový, tak ztrátový režim kodeku, protože režim je vlastností samotného kódového proudu, nikoli obalu kolem něho

Tato poslední poznámka stojí za zapamatování. JPEG 2000 je jediný algoritmus s bezeztrátovým speciálním případem, nikoli dva oddělené formáty. Reverzibilní vlnka 5/3 rekonstruuje původní vzorky přesně; nerevertibilní vlnka 9/7 vymění tuto přesnost za menší soubor. Dekodér nakládá s oběma při čtení stejně, proto HotPDF potřebuje pouze jednu cestu dekodéru, aby přijímal cokoliv, co proud JPXDecode obsahuje

Co dekodér dělá s pixely

Obrazové XObjects v PDF ve běžném případě očekávají 8 bitů na komponentu v DeviceGray nebo DeviceRGB. JPEG 2000 toto běžně překračuje a jeho komponentový model je obecnější než balené rastrové pole, takže dekodér má tři úkoly, než jsou data použitelná jako normální obrázek

Za prvé, komponenty s vysokou bitovou hloubkou se převzorkují na 8 bitů. 12bitový nebo 16bitový vzorek se zaškáluje na rozsah 0 až 255, takže výsledkem je běžné 8bitové rastrové pole. Znaménkové komponenty se nejprve posunou do rozsahu bez znaménka. Tento detail je důležitý, protože je sám o sobě ztrátový: 16bitový šedý sken ztrácí svůj hluboký tonální rozsah v okamžiku, kdy se stane 8bitovým obrázkem PDF, což je správný kompromis pro výstup na obrazovce a tisku, ale ne pro opětovnou archivaci

Za druhé, barevný prostor YCbCr (kodek ho nazývá SYCC) se převede na RGB. JPEG 2000 často ukládá barvu v prostoru luma-chroma pro efektivitu komprese, stejnou myšlenku, jakou používá základní JPEG, a dekodér aplikuje standardní inverzní transformaci, aby stránka získala skutečné RGB

Za třetí, podvzorkované komponenty jsou převzorkovány přiblížením nejbližšího souseda. Chromové kanály jsou často uloženy v poloviní rozlišení, takže dekodér čte každou komponentu ve vlastních rozměrech a vlastním vzorkovacím faktoru, pak replikuje vzorky, aby vyřadil každý kanál na plnou velikost obrázku před prokládáním. Nejbližší soused udělá krok levným; chroma, které se plní, mělo nízkou frekvenci od začátku, takže viditelné náklady jsou malé

Rámce JP2 versus holý kódový proud J2K

Soubor JPEG 2000 má dva tvary a HotPDF detekuje, který čte, z prvních bytů, nikoli z přípony souboru. Soubor JP2 je kontejner se strukturou rámců: otevírá se dvanáctibytovým podpisovým rámcem 00 00 00 0C 6A 50 20 20 a zabalí kódový proud spolu s rámci popisujícími barevný prostor, rozlišení a metadata. Holý kódový proud J2K neobsahuje žádný kontejner a začíná markérem SOC FF 4F FF 51. Dekodér přečte tyto úvodní byty, rozezná podpis a pro každý případ vybere odpovídající OpenJPEG kodek

Oba tvary jsou zpracovávány, protože oba se v praxi vyskytují. Zařízení a archivy, které potřebují postranní metadata, generují JP2; nástroje, které chtějí nejmenší možné datové zatížení, generují holý kódový proud. Typ formátu je modelován jako výčet TJpeg2000FileType s členy jtInvalid, jtJP2, jtJ2K a jtJPT. Člen JPT pojmenovává variantu streamování JPIP; detektor bytového podpisu rozlišuje oba tvary, které dokáže dekódovat, JP2 a J2K, a cokoliv jiného hlásí jako jtInvalid, aby nepodporovaný vstup selhal čistě místo generérování nésmyslného výstupu

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;

Bezeztrátové a ztrátové kódování

Dekodér čte oba režimy bez toho, aby mu bylo řečeno, který to je. Volba se stává parametrem až tehdy, když jdete opačným směrem a vytváříte soubor JPEG 2000, což HotPDF také dokáže prostřednictvím třídy TJpeg2000Bitmap, potomka TBitmap, který načítá a ukládá rastrová data jako JP2. Dvě vlastnosti řídí výstup. LosslessCompression je logická hodnota, která při nastavení True vybere reverzibilní vlnku; CompressionQuality je TJpeg2000QualityRange, celé číslo od 1 do 100, kde 1 je malý a ošklivý a 100 je velký a věrný. Výchozí hodnoty jsou pojmenovány konstantami: Jpeg2000DefaultLosslessCompression je False a Jpeg2000DefaultLossyQuality je 80

Rozhodnutí je rozhodnutím o obsahu. Bezeztrátový režim se hodí pro hlavní kopii, medicínský nebo právní sken, cokoliv, co může být později znovu kódováno a nesmí akumulovat generační ztetu. Ztrátový režim při kvalitě 80 se hodí pro obrázky určené k zobrazení na obrazovce nebo tisku, kde graciozní degradace vlnky poskytuje výrazně menší soubor bez artefaktů, které by čtenář zaznamenal. Je tu jedno upozornění týkající se CMYK: bitmapa vystavuje SetCMYK pro označení čtyřkanálových dat jako CMYK místo RGBA, což je důležité pro tiskové kanály, které udržují separace v celistvosti

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;

Proč neexistuje kanál filtrů při načítání

Jeden architektonický fakt formuje to, jak cokoli z toho používáte, a je snadné předpokládat opak. HotPDF nemá žádný obecný filtr pro dekódování obrázků při načtení. Když otevřete PDF, které již obsahuje obrázek JPXDecode, modul nerozkóduje tento proud. Uchovává byty JPEG 2000 přesně tak, jak jsou, takže kopírovaní stránky nebo sloučení dokumentu přenáší obrázek nezměněný, byte po bytu. Dekodér má jediný vstupní bod a je na straně vytváření: AddImage založený na souboru, který se spouští podle přípony souboru pro zpracování zdrojů .jp2, .j2k, .jpt a .jpc

Toto rozdělení je správným designem, nikoli omezením. Dekódování vloženého proudu JPX při načtení, jenom aby ho bylo při uložení znovu zakódovat, by přeměnilo bezeztrátově archivovaný obrázek na ztrátový a nafouklo každé sloučení, a to všechno jen kvůli obrázku, který jste chtěli pouze přesunout z jednoho PDF do druhého. Průchod proudu beze změny je bezeztrátová operace a je rychlá. Dekódování se odkládá na jediný okamžik, kdy je skutečně potřebné: když předáte modulu soubor JPEG 2000 z disku a požadáte ho o rasterizaci obrázku pro umístění na nové stránce. V tu chvíli se soubor musí stát pixely a dekodér se spustí

Registrace podpory a vložení obrázku

Registrace obrázků JPEG 2000 je volitelnou funkcionalitou za přepínačem kompilace HPDF_REGISTER_JPEG2000_PICTURE, který je ve výchozím nastavení vypínut. Důvodem je skutečný konflikt, nikoliv opatrnost: globální registrace formátů souborů jp2, j2k a jpc u TPicture může zasahovat do detekce formátu BLOB, na které spoléhá TppDBImage z ReportBuilderu. Definujte přepínač, pokud tato integrace není aktivní, a formáty souborů se zaregistrují tak, že je TPicture rozpozná; ponechte ho nedefinovaný a odeslání souborů JPEG 2000 přes AddImage stále funguje, protože tato cesta nejde přes TPicture vůbec

Po pochopení tohoto je umístění obrázku JPEG 2000 stejným tříČínným rytmem jako kterýkoliv jiný obrázek v HotPDF. Předejte AddImage cestu .jp2 a typ komprese pro způsob uložení obrázku ve výstupu, pak umístěte vrácený index obrázku na stránce pomocí 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;

Komprese, kterou předáte AddImage, řídí, jak bude dekódovaný obrázek znovu uložen, nikoli jak byl načten. Soubor JPEG 2000 dekódovaný na bitmapu může jít zpět jako DCTDecode JPEG, Flate raster nebo jiný podporovaný filtr, podle toho, co dokumentu vyhovuje. Dekódování z JP2 nebo J2K proběhne nejprve bez ohledu na to, takže totéž volání přijímá zdroj komprimovaný vlnkovou metodou a vkládá ho v formě, jakou očekává zbytek vašeho kanálu

Pro širší přehled o tom, jak obrázky a fonty přistávají v generovaném výstupu, si přečtěte naše poznámky o výstupu sestav s fonty a obrázky. Když dokument, který sestavujete, opětovně používá obsah ze stávajících PDF, chování průchodu pops. zde se provázuje s mechanikou sloučení a revizí v článku o objektových proudech a inkrementálních aktualizacích. Dekodér JPEG 2000 je součástí HotPDF Component pro Delphi a C++Builder, spolu s rozhraními API pro obrázky, fonty a dokumenty popsanými jinde na tomto blogu