스캔된 의료 슬라이드, 항공 측량 타일, 전체 동적 범위로 보관된 필름 프레임. 이러한 사진들은 JPEG 2000으로 도착하며, 그렇게 도착하는 데는 이유가 있습니다. 이 포맷은 채널당 12비트 또는 16비트를 유지하고, JPEG가 사용하는 블록 DCT 대신 웨이블릿 변환으로 압축하며, 하나의 코드스트림에서 동일한 사진을 무손실 또는 손실 방식으로 인코딩할 수 있습니다. 이러한 소스로 작성된 문서가 PDF가 되어야 할 때, 이미지는 PDF 사양이 정확히 이 코덱을 위해 예약해 둔 필터를 통과해야 합니다
HotPDF v2.228.0은 해당 경로를 위해 작동하는 JPEG 2000 디코딩 엔진을 복원했습니다. 이전 빌드에서는 nil을 반환하는 스텁 함수가 포함된 유닛을 제공했기 때문에 API는 존재했지만 아무것도 디코딩하지 못했습니다. 현재 엔진은 OpenJPEG 2.5.4를 정적으로 바인딩하며 JP2 또는 J2K 소스를 HotPDF가 페이지에 배치할 수 있는 픽셀로 변환합니다
PDF의 JPXDecode 필터
ISO 32000-1은 §7.4.9에서 JPXDecode 필터를 정의합니다. PDF 이미지 XObject는 스트림 사전의 /Filter 항목에 압축 방식을 지정하며, JPXDecode는 스트림 데이터가 /DCTDecode가 전달하는 베이스라인 JPEG가 아니라 JPEG 2000 코드스트림임을 나타내는 값입니다. 이 필터를 통해 PDF는 높은 비트 심도를 가진 웨이블릿 압축 이미지 데이터를 보유할 수 있으며, 코덱의 무손실 및 손실 모드를 모두 허용합니다. 이는 모드가 래퍼가 아닌 코드스트림 자체의 속성이기 때문입니다
마지막 요점은 명심할 가치가 있습니다. JPEG 2000은 두 개의 개별 형식이 아니라 무손실 특수 사례가 있는 단일 알고리즘입니다. 가역적인 5/3 웨이블릿은 원본 샘플을 정확하게 재구성하고, 비가역적인 9/7 웨이블릿은 그 정확성을 더 작은 파일 크기와 교환합니다. 디코더는 읽기 시 두 가지를 모두 동일하게 처리하므로 HotPDF가 JPXDecode 스트림이 던지는 모든 것을 수용하는 데 단일 디코딩 경로만 필요한 것입니다
디코더가 픽셀에 수행하는 작업
일반적인 경우 PDF 이미지 XObject는 DeviceGray 또는 DeviceRGB에서 구성 요소당 8비트를 기대합니다. JPEG 2000은 일상적으로 이를 초과하며 구성 요소 모델이 패킹된 래스터보다 더 일반적이므로, 디코더는 데이터를 일반 이미지로 사용하기 전에 세 가지 작업을 수행해야 합니다
첫째, 높은 비트 심도 구성 요소는 8비트로 리샘플링됩니다. 12비트 또는 16비트 샘플은 0에서 255 범위로 축소되어 결과가 일반적인 8비트 래스터가 됩니다. 부호 있는 구성 요소는 먼저 부호 없는 범위로 이동됩니다. 이 세부 사항은 그 자체로 손실이 있기 때문에 중요합니다. 16비트 회색조 스캔은 8비트 PDF 이미지가 되는 순간 깊은 색조 범위를 잃게 되며, 이는 화면 및 인쇄 출력에는 적합하지만 재보관용으로는 적합하지 않습니다
둘째, YCbCr(코덱에서는 이를 SYCC라고 부름) 색상 공간이 RGB로 변환됩니다. JPEG 2000은 압축 효율성을 위해 루마-크로마 공간에 색상을 저장하는 경우가 많으며(베이스라인 JPEG가 사용하는 것과 동일한 아이디어), 디코더는 페이지가 진정한 RGB를 수신하도록 표준 역변환을 적용합니다
셋째, 서브샘플링된 구성 요소는 최근접 이웃 복제를 통해 업샘플링됩니다. 크로마 채널은 절반 해상도로 저장되는 경우가 많으므로 디코더는 각 구성 요소를 자체 치수와 자체 샘플링 계수로 읽은 다음, 샘플을 복제하여 인터리빙하기 전에 모든 채널을 전체 이미지 크기로 가져옵니다. 최근접 이웃 방식은 단계를 저렴하게 유지합니다. 채우고 있는 크로마는 애초에 저주파였으므로 눈에 띄는 비용은 적습니다
JP2 상자 대 원시 J2K 코드스트림
JPEG 2000 파일은 두 가지 형태로 제공되며, HotPDF는 파일 확장자가 아닌 첫 번째 바이트를 통해 읽고 있는 파일을 감지합니다. JP2 파일은 상자 구조의 컨테이너입니다. 12바이트 서명 상자 00 00 00 0C 6A 50 20 20으로 시작하며 색상 공간, 해상도 및 메타데이터를 설명하는 상자들과 함께 코드스트림을 래핑합니다. 원시 J2K 코드스트림은 컨테이너가 전혀 없으며 SOC 마커 FF 4F FF 51로 시작합니다. 디코더는 해당 선두 바이트를 읽고 서명을 인식한 다음 각 경우에 일치하는 OpenJPEG 코덱을 선택합니다
두 형태 모두 실제로 발생하기 때문에 두 가지 모두 처리됩니다. 측면 메타데이터가 필요한 캡처 장치 및 아카이브는 JP2를 내보내고, 가능한 가장 작은 페이로드를 원하는 도구는 베어 코드스트림을 내보냅니다. 형식 유형은 jtInvalid, jtJP2, jtJ2K 및 jtJPT 멤버를 가진 열거형 TJpeg2000FileType으로 모델링됩니다. JPT 멤버는 JPIP 스트리밍 변형을 지정합니다. 바이트 서명 탐지기는 디코딩할 수 있는 두 가지 형태인 JP2와 J2K를 판별하고 다른 모든 것은 jtInvalid로 보고하여 지원되지 않는 입력이 가비지를 생성하는 대신 깔끔하게 실패하도록 합니다
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;
인코딩 측의 무손실 및 손실
디코더는 어떤 모드인지 듣지 않고도 두 모드를 모두 읽습니다. 이 선택은 반대 방향으로 가서 HotPDF가 래스터 데이터를 JP2로 로드하고 저장하는 TBitmap의 자손인 TJpeg2000Bitmap 클래스를 통해 수행할 수 있는 JPEG 2000 파일을 생성할 때만 매개 변수가 됩니다. 두 속성이 출력을 제어합니다. LosslessCompression은 참일 때 가역적인 웨이블릿을 선택하는 부울입니다. CompressionQuality는 TJpeg2000QualityRange로, 1이 작고 거칠며 100이 크고 충실한 1에서 100 사이의 정수입니다. 기본값은 명명된 상수에 있습니다: Jpeg2000DefaultLosslessCompression은 False이고 Jpeg2000DefaultLossyQuality는 80입니다
이 결정은 콘텐츠에 대한 결정입니다. 무손실은 원본 복사본, 의료 또는 법률 스캔, 나중에 다시 인코딩될 수 있고 세대 간 손실이 누적되어서는 안 되는 모든 항목에 적합합니다. 품질 80의 손실 방식은 화면이나 인쇄용 사진에 적합하며, 웨이블릿의 부드러운 품질 저하를 통해 독자가 알아차릴 수 있는 아티팩트 없이 눈에 띄게 더 작은 파일을 제공합니다. 주목해야 할 CMYK 관련 주의 사항이 하나 있습니다. 비트맵은 분리를 온전하게 유지하는 인쇄 파이프라인에 중요한 역할을 하는 4채널 데이터를 RGBA 대신 CMYK로 표시하는 SetCMYK를 노출합니다
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;
로드 시 디코딩 필터 파이프라인이 없는 이유
한 가지 아키텍처적 사실이 이 모든 것을 사용하는 방식을 형성하며, 그 반대를 가정하기 쉽습니다. HotPDF에는 범용 로드 시 디코딩 이미지 필터가 없습니다. 이미 JPXDecode 이미지가 포함된 PDF를 열 때 엔진은 해당 스트림을 디코딩하지 않습니다. 엔진은 JPEG 2000 바이트를 그대로 유지하므로 페이지 복사 또는 문서 병합을 수행할 때 이미지가 바이트 단위로 수정되지 않고 그대로 전달됩니다. 디코더에는 단일 진입점이 있으며 생성 측에 있습니다: 파일 기반 AddImage는 파일 확장자로 전달되어 .jp2, .j2k, .jpt 및 .jpc 소스를 처리합니다
이 분리는 한계가 아니라 올바른 설계입니다. 저장 시 다시 인코딩하기 위해 로드 시 포함된 JPX 스트림을 디코딩한다면, 단지 한 PDF에서 다른 PDF로 이미지를 이동시키기 위해 무손실 보관된 이미지를 손실 이미지로 변환하고 모든 병합 크기를 부풀리게 됩니다. 스트림을 있는 그대로 전달하는 것은 무손실 작업이며 빠른 작업입니다. 디코딩은 디스크에서 엔진에 JPEG 2000 파일을 전달하고 새 페이지에 배치하기 위해 해당 사진을 래스터화하도록 요청하는 등 실제로 필요할 때로 연기됩니다. 그 시점에서 파일은 픽셀이 되어야 하고 디코더가 실행됩니다
지원 등록 및 이미지 배치
JPEG 2000 사진 등록은 기본적으로 꺼져 있는 HPDF_REGISTER_JPEG2000_PICTURE 컴파일 스위치 뒤에서 옵트인 방식입니다. 주의 때문이 아니라 실제 충돌 때문입니다. jp2, j2k, jpc 파일 형식을 TPicture와 전역적으로 등록하면 ReportBuilder의 TppDBImage가 의존하는 BLOB 형식 감지를 방해할 수 있습니다. 해당 통합이 적용되지 않을 때 스위치를 정의하면 TPicture가 파일 형식을 인식하도록 등록됩니다. 정의하지 않고 두면 AddImage 확장자 디스패치가 여전히 JPEG 2000 파일을 직접 디코딩합니다. 해당 경로가 TPicture를 전혀 거치지 않기 때문입니다
이를 이해한 상태에서 JPEG 2000 사진을 배치하는 것은 다른 HotPDF 이미지와 동일한 3번의 호출 리듬입니다. AddImage에 .jp2 경로와 사진이 출력에 저장되는 방식에 대한 압축 형식을 전달한 다음, 반환된 이미지 인덱스를 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;
AddImage에 전달하는 압축은 디코딩된 사진이 어떻게 읽혔는지가 아니라 어떻게 다시 저장되는지를 제어합니다. 비트맵으로 디코딩된 JPEG 2000 파일은 문서에 적합한 DCTDecode JPEG, Flate 래스터 또는 기타 지원되는 필터로 다시 나갈 수 있습니다. JP2 또는 J2K의 디코딩은 상관없이 먼저 이루어지므로, 동일한 호출이 웨이블릿 압축 소스를 수락하고 나머지 파이프라인이 예상하는 형태로 포함합니다
생성된 출력에 이미지와 글꼴이 어떻게 나타나는지에 대한 더 넓은 그림을 보려면 글꼴 및 이미지를 사용한 보고서 출력에 대한 참고 사항을 참조하십시오. 조립 중인 문서가 기존 PDF의 콘텐츠를 재사용하는 경우, 여기에 설명된 패스스루 동작은 객체 스트림 및 증분 업데이트의 병합 및 수정 메커니즘과 짝을 이룹니다. JPEG 2000 디코딩 엔진은 이 블로그의 다른 곳에서 다루는 이미지, 글꼴 및 문서 API와 함께 Delphi 및 C++Builder용 HotPDF Component의 일부로 제공됩니다