Ett skannat medicinskt preparatglas, en bildruta från en flygfotografering, en filmruta arkiverad med fullt dynamiskt omfång. Dessa är bilderna som anländer som JPEG 2000, och de anländer så av en anledning. Formatet behåller 12 eller 16 bitar per kanal, komprimerar med en wavelet-transform i stället för det block-DCT som JPEG använder, och kan koda samma bild antingen förlustfritt eller förstörande från en enda kodström. När ett dokument byggt från dessa källor ska bli en PDF måste bilden passera genom ett filter som PDF-specifikationen reserverar för exakt denna codec
HotPDF v2.228.0 återställde en fungerande JPEG 2000-avkodningsmotor för den vägen. En tidigare version hade levererat enheten med stubbfunktioner som returnerade nil, så API:et fanns men avkodade ingenting. Den nuvarande motorn binder OpenJPEG 2.5.4 statiskt och omvandlar en JP2- eller J2K-källa till pixlar som HotPDF kan placera på en sida
JPXDecode-filtret i PDF
ISO 32000-1 definierar filtret JPXDecode i §7.4.9. Ett PDF-bild-XObject namnger sin komprimering i strömordbokens /Filter-post, och JPXDecode är det värde som säger att strömdata är en JPEG 2000-kodström och inte den baslinje-JPEG som /DCTDecode bär på. Filtret är vad som låter en PDF rymma wavelet-komprimerad bilddata med högt bitdjup, och det tillåter både det förlustfria och det förstörande läget av codec:en, eftersom läget är en egenskap hos kodströmmen själv och inte hos omslaget runt den
Den sista punkten är den som är värd att hålla fast vid. JPEG 2000 är en enda algoritm med ett förlustfritt specialfall, inte två separata format. Den reversibla 5/3-wavelet:en rekonstruerar de ursprungliga samplingarna exakt; den irreversibla 9/7-wavelet:en byter ut den exaktheten mot en mindre fil. En avkodare behandlar båda på samma sätt vid läsning, vilket är anledningen till att HotPDF bara behöver en avkodningsväg för att acceptera vad en JPXDecode-ström än kastar på den
Vad avkodaren gör med pixlarna
I det vanliga fallet förväntar sig PDF-bild-XObjects 8 bitar per komponent i DeviceGray eller DeviceRGB. JPEG 2000 överskrider rutinmässigt detta, och dess komponentmodell är mer generell än ett packat raster, så avkodaren har tre jobb att utföra innan datan är användbar som en normal bild
För det första samplas komponenter med högt bitdjup om till 8 bitar. Ett 12-bitars eller 16-bitars prov skalas ner till intervallet 0 till 255 så att resultatet blir ett vanligt 8-bitars raster. Signerade komponenter skiftas till osignerat intervall först. Detaljen spelar roll eftersom den i sig själv innebär förlust: en 16-bitars gråskaleskanning förlorar sitt djupa tonomfång i samma ögonblick som den blir en 8-bitars PDF-bild, vilket är rätt avvägning för skärm- och utskriftsutmatning men inte för återarkivering
För det andra konverteras en YCbCr-färgrymd (codec:en kallar det SYCC) till RGB. JPEG 2000 lagrar ofta färg i en luma-kroma-rymd för komprimeringseffektivitet, samma idé som baslinje-JPEG använder, och avkodaren tillämpar den vanliga inversa transformen så att sidan får äkta RGB
För det tredje uppsamplas nedsamplade komponenter genom närmaste-granne-replikering. Kromakanaler lagras ofta i halverad upplösning, så avkodaren läser varje komponent med dess egna dimensioner och dess egen samplingsfaktor, och replikerar sedan prover för att föra upp varje kanal till bildens fulla storlek innan sammanflätning sker. Närmaste-granne håller steget billigt; kromat som den fyller var lågfrekvent från början, så den synliga kostnaden är liten
JP2-lådor kontra en rå J2K-kodström
En JPEG 2000-fil kommer i två former, och HotPDF upptäcker vilken den läser utifrån de första byten snarare än från filändelsen. En JP2-fil är en lådstrukturerad behållare: den inleds med den tolv byte långa signaturlådan 00 00 00 0C 6A 50 20 20 och omsluter kodströmmen tillsammans med lådor som beskriver färgrymd, upplösning och metadata. En rå J2K-kodström bär inte på någon behållare alls och börjar med SOC-markören FF 4F FF 51. Avkodaren läser dessa inledande byten, känner igen signaturen och väljer matchande OpenJPEG-codec för varje fall
Båda formerna hanteras eftersom båda förekommer ute i naturen. Fångstenheter och arkiv som behöver sidometadatan avger JP2; verktyg som vill ha den minsta möjliga nyttolasten avger den rena kodströmmen. Formattypen modelleras som en enum, TJpeg2000FileType, med medlemmarna jtInvalid, jtJP2, jtJ2K och jtJPT. JPT-medlemmen namnger streamingvarianten JPIP; den bytesignatur-baserade detektorn löser de två former den kan avkoda, JP2 och J2K, och rapporterar allt annat som jtInvalid så att en inmatning som inte stöds misslyckas rent i stället för att producera skräp
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;
Förlustfritt och förstörande på kodningssidan
Avkodaren läser båda lägena utan att få veta vilket det är. Valet blir en parameter först när du går åt andra hållet och producerar en JPEG 2000-fil, vilket HotPDF också kan göra via TJpeg2000Bitmap-klassen, en TBitmap-ättling som laddar och sparar rasterdata som JP2. Två egenskaper styr utmatningen. LosslessCompression är ett booleskt värde som väljer den reversibla wavelet:en när den är sann; CompressionQuality är ett TJpeg2000QualityRange, ett heltal från 1 till 100 där 1 är litet och fult och 100 är stort och naturtroget. Standardvärdena lever i namngivna konstanter: Jpeg2000DefaultLosslessCompression är False och Jpeg2000DefaultLossyQuality är 80
Beslutet är ett innehållsbeslut. Förlustfritt passar en masterkopia, en medicinsk eller juridisk skanning, allt som kan kodas om senare och inte får ackumulera generationsförluster. Förstörande på kvalitet 80 passar en bild som är på väg mot skärm eller utskrift, där wavelet:ens graciösa nedbrytning ger en märkbart mindre fil utan någon artefakt en läsare skulle upptäcka. Det finns en CMYK-varning att flagga för: bitmappen exponerar SetCMYK för att markera fyrkanalsdata som CMYK i stället för RGBA, vilket spelar roll för utskriftsflöden som behåller separationer intakta
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;
Varför det inte finns någon avkoda-vid-inläsning-filterpipeline
Ett arkitektoniskt faktum formar hur du använder något av detta, och det är lätt att anta motsatsen. HotPDF har inget generellt bildfilter för avkodning vid inläsning. När du öppnar en PDF som redan innehåller en JPXDecode-bild, avkodar motorn inte den strömmen. Den behåller JPEG 2000-byten exakt som de är, så en sidkopiering eller en dokumentsammanfogning för med sig bilden orörd, byte för byte. Avkodaren har en enda ingångspunkt, och den ligger på skapandesidan: den filbaserade AddImage, som utifrån filändelse skickar vidare för att hantera .jp2, .j2k, .jpt och .jpc-källor
Denna uppdelning är en korrekt design i stället för en begränsning. Att avkoda en inbäddad JPX-ström vid inläsning, bara för att koda om den när den sparas, skulle konvertera en förlustfritt arkiverad bild till en förstörande sådan och blåsa upp varje sammanfogning, allt för en bild du bara menade att flytta från en PDF till en annan. Att låta strömmen passera bokstavligen är en förlustfri operation och en snabb sådan. Avkodning skjuts upp till det enda ögonblick det verkligen krävs: när du ger motorn en JPEG 2000-fil från disken och ber den att rastrera den bilden för placering på en ny sida. Då måste filen bli till pixlar, och avkodaren körs
Registrera stöd och placera en bild
Registrering av JPEG 2000-bilder måste väljas till aktivt bakom kommandoväxeln HPDF_REGISTER_JPEG2000_PICTURE, som är avstängd som standard. Anledningen är en verklig konflikt, inte försiktighet: att registrera filformaten jp2, j2k och jpc globalt med TPicture kan störa den detektering av BLOB-format som ReportBuilders TppDBImage förlitar sig på. Definiera växeln när den integrationen inte är aktuell, och filformaten registreras så att TPicture känner igen dem; lämna den odefinierad och filändelsedispatchen för AddImage avkodar ändå JPEG 2000-filer direkt, eftersom den vägen inte går genom TPicture alls
Med detta förstått är placeringen av en JPEG 2000-bild samma treanropsrytm som vilken annan HotPDF-bild som helst. Ge AddImage en .jp2-sökväg och en komprimeringstyp för hur bilden ska lagras i utmatningen, och placera sedan det returnerade bildindexet på sidan med 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;
Den komprimering du skickar till AddImage styr hur den avkodade bilden lagras om, inte hur den lästes. En JPEG 2000-fil avkodad till en bitmapp kan gå ut igen som en DCTDecode-JPEG, ett Flate-raster eller ett annat filter som stöds, beroende på vad som passar dokumentet. Avkodningen från JP2 eller J2K sker först oavsett, så samma anrop accepterar en wavelet-komprimerad källa och bäddar in den i vilken form som helst som resten av din pipeline förväntar sig
För en bredare bild av hur bilder och teckensnitt landar i den genererade utmatningen, se våra anteckningar om rapportutmatning med teckensnitt och bilder. När det dokument du bygger återanvänder innehåll från befintliga PDF:er samspelar det pass-through-beteende som beskrivs här med sammanfognings- och revisionsmekaniken i objektströmmar och inkrementella uppdateringar. JPEG 2000-avkodningsmotorn levereras som en del av HotPDF Component för Delphi och C++Builder, vid sidan av de bild-, teckensnitts- och dokument-API:er som täcks på andra ställen på denna blogg