기술 기사

Delphi PDF에서 기본 JBIG2 이중 레벨 이미지 압축

스캔된 계약서는 흰색 종이에 검은색 잉크로 찍힌 인치당 수백 개의 점으로 구성됩니다. 픽셀당 1비트의 비트맵으로 저장하면 이미 작지만, 이러한 페이지 수백 장이 모이면 이메일로 보낼 수 없을 만큼 PDF 크기가 커집니다. 올바른 필터를 사용하면 이러한 계산이 달라집니다. JBIG2는 ISO 32000-1에서 이중 레벨 이미지용으로 정의한 최고 비율의 압축 방식으로, 스캔된 텍스트 더미에 적용할 경우 CCITT Group 4가 생성하는 크기를 일상적으로 절반으로 줄여줍니다. 입력된 문서가 팩스로 수신되었거나, 스캔되었거나, 다른 방식으로 두 가지 색상으로 축소된 경우에 선택해야 하는 필터이며, HotPDF는 이를 PDF에 직접 작성할 수 있습니다

이 포맷은 일반 이미지 코덱에는 없는 두 가지 아이디어를 통해 이러한 비율을 달성합니다. 검은색 연속선이 흰색 배경과 어떻게 배치되는지 모델링하고, 스캔된 페이지가 대부분 수천 번 반복되는 동일한 수백 개의 글리프 모양이라는 사실에 주목합니다. 이 두 가지를 이해하면 추측하는 대신 의도적으로 인코딩 옵션을 선택할 수 있습니다

PDF 사양에서 JBIG2의 위치

ISO 32000-1은 JBIG2Decode를 §7.4.7의 스트림 필터 목록에 포함하고 있으며, PDF 1.4부터 사용할 수 있습니다. 이는 /BitsPerComponent가 1이고 단일 채널로 해석되는 색상 공간을 갖는 이미지 XObject의 한 곳에만 적용됩니다. 이것이 핵심입니다. JBIG2는 이중 레벨 코덱이므로 사진에 대해 DCT나 JPXDecode와 경쟁하지 않습니다. 문서 스캐너가 생성하는 종류의 2톤 페이지에서 Group 3 및 Group 4 팩스 필터인 CCITTFaxDecode와 정확히 경쟁합니다

디코더는 표준에서 PDF 프로필이라고 부르는 포함된 JBIG2 구성을 소비하며, 여기서 각 이미지 스트림은 단순한 비트스트림 대신 세그먼트 시퀀스를 보관합니다. 선택적 /JBIG2Globals 스트림은 동일한 문서 내의 여러 이미지에서 공유되는 세그먼트를 전달하며, 이는 반복되는 콘텐츠를 페이지 단위가 아닌 전체 파일 단위로 한 번만 저장할 수 있게 해주는 메커니즘입니다. HotPDF는 기본적으로 페이지별 스트림을 내보내며 백엔드에서 요청하지 않는 한 전역 채널을 비워 둡니다

백엔드 우선 인코더 아키텍처

완전한 JBIG2 인코더는 대규모 소프트웨어이며, 역사적으로 가장 공격적인 부분은 특허의 제약을 받고 모든 제품에 적합하지 않은 라이선스 하에 배포되었습니다. HotPDF는 인터페이스를 엔진에서 분리하여 이러한 긴장을 해결합니다. HPDFJBIG2 유닛은 라이브러리의 나머지 부분이 수행하는 호출을 정의하고, 적당한 내장 인코더를 함께 제공하여 JBIG2가 즉시 작동하도록 합니다. 프로덕션 수준의 비율이 필요한 경우 더 강력한 엔진을 등록하면 라이브러리가 호출 코드의 변경 없이 해당 엔진에 위임합니다

이 스위치는 단일 등록 호출입니다. 백엔드가 등록되지 않은 경우 인코더는 내장 경로로 대체됩니다. 하나를 등록하면 이후의 모든 인코딩이 해당 경로를 통해 실행됩니다

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;

RegisterJBIG2DecoderBackend를 통한 디코딩에도 동일한 훅이 존재하며, 이를 조사하기 위한 IsJBIG2DecoderBackendAvailable이 있습니다. 이것이 라이브러리가 하나의 단일 인코더 대신 작은 내장 경로와 백엔드 이음새를 제공하는 이유입니다. 내장 경로는 바이너리를 가볍게 유지하고 라이선스 문제로부터 자유롭게 하며, 이음새를 통해 전체 인코더 라이선스를 보유한 팀이 PDF 작성 계층을 전혀 건드리지 않고 플러그인할 수 있게 해줍니다

인코딩 옵션이 실제로 교환하는 것

인코딩은 Lossless, UseGlobalSegments, UseSymbolDictionaryLossyLevel 필드가 있는 레코드인 TJBIG2EncodeOptions를 통해 구성됩니다. 구성 요소 친화적인 래퍼인 THPDFJBIG2OptionsLossless, UseSymbolDictionaryLossyLevel을 게시하여 객체 관리자에서 설정할 수 있도록 하며, 내부적으로 레코드로 변환합니다. 세 가지 의도가 설정을 주도합니다

무손실 재구성은 모든 픽셀을 유지합니다. Lossless를 True로 설정하고 LossyLevel을 0으로 두면 디코딩된 비트맵이 입력과 비트 단위로 동일해집니다. 이는 선형 예술, 기술 도면 및 서명이나 스탬프와 같이 누락된 픽셀이 의미를 변경할 수 있는 모든 페이지에 대한 유일한 안전한 선택입니다. 기호 사전 코딩은 텍스트 인식 중복 제거를 켜며 JBIG2를 팩스 필터와 분리하는 옵션입니다. 0에서 9 사이의 정수인 손실 수준은 유능한 백엔드가 거의 동일한 마크를 동일한 기호로 처리하여 크기 대비 충실도를 교환할 수 있게 합니다. 0은 무손실을 의미합니다. 내장 인코더는 무손실 경로만 존중하고 0이 아닌 손실 수준을 무시하므로, 더 높은 수준은 이를 구현하는 백엔드가 등록된 후에만 적용됩니다

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;

기호 사전과 텍스트 스캔이 유리한 이유

스캔된 텍스트 페이지는 실제로 단어의 이미지가 아닙니다. 그것은 수백 번 인쇄된 동일한 문자 e, 동일한 t, 동일한 쉼표이며, 각 인스턴스는 기본 모양의 약간 노이즈가 있는 복사본입니다. 기호 사전은 이러한 구조를 포착합니다. 인코더는 페이지에 있는 구별되는 표시를 수집하여 사전에 넣고 각 모양을 한 번만 저장한 다음, 페이지를 사전 항목을 참조하는 위치 목록으로 기록합니다. 동일한 글리프가 천 번 등장할 때 하나의 저장된 비트맵과 천 개의 저렴한 배치 비용만 지불하면 됩니다

이것이 바로 JBIG2가 CCITT Group 4를 앞서는 부분입니다. Group 4는 글리프의 개념 없이 각 스캔 라인을 바로 위의 라인과 비교하여 코딩하므로, 문자가 나타날 때마다 모든 문자의 전체 비용을 지불해야 합니다. JBIG2는 한 번만 지불합니다. 동일한 사전이 문서 수준의 전역 스트림으로 승격되면 다중 페이지 스캔에 걸쳐 절감 효과가 배가됩니다. 여러 페이지에서 공유되는 모양이 전체 파일에 대해 단 한 번만 저장되기 때문입니다. 조밀한 텍스트에서 그 차이는 미미하지 않습니다. 이것이 JBIG2가 존재하는 이유입니다

기타 모든 것을 위한 일반 영역 및 MMR

모든 이중 레벨 이미지가 텍스트는 아닙니다. 지도, 회로도, 엔지니어링 도면 및 혼합 페이지에는 어떤 사전으로도 요약할 수 없는 선형 예술이 있습니다. 이들을 위해 JBIG2는 기호 훈련 없이 픽셀 직사각형을 직접 압축하는 일반 영역을 코딩합니다. 표준은 일반 영역이 Group 4 팩스에서 이미 사용하는 수정된 수정된 READ 코딩(MMR)을 사용할 수 있도록 허용하며, 이는 각 픽셀 행을 그 위의 행과 비교하여 모델링합니다

이것이 HotPDF가 내장 인코더로 제공하는 경로입니다. 백엔드가 등록되지 않았고 요청이 무손실일 때, 라이브러리는 비트맵을 단일 MMR 일반 영역으로 압축하고 PDF 프로필이 요구하는 JBIG2 세그먼트 구조로 래핑합니다. 사전, 훈련 패스 및 참조할 두 번째 이미지가 필요 없으므로 선형 예술 및 혼합 이중 레벨 콘텐츠에 대한 신뢰할 수 있는 기본값입니다. 순수 텍스트에서는 전체 기호 사전 인코더와 일치하지 않지만 항상 올바르고, 항상 무손실이며, 항상 존재합니다. 이를 위한 인코더 표면은 한 번의 호출입니다

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;

문서 작성 시 기능 켜기

일상적인 용도에서는 인코더 클래스를 직접 건드리지 않습니다. HotPDF는 문서의 이미지 압축 선택 항목으로 JBIG2를 노출합니다. THPDFImageCompressionType 열거형은 Flate, JPEG, CCITT 옵션과 함께 icJBIG2를 포함하며, 문서는 이 압축이 선택되었을 때 사용되는 설정을 보관하는 THPDFJBIG2Options 유형의 JBIG2Options 속성을 가집니다. 이러한 방식으로 압축하려는 이중 레벨 이미지를 추가하기 전에 두 가지를 모두 구성하십시오

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;

주목할 만한 한 가지 편리함은 TDBGrid를 PDF로 직접 렌더링하는 DBGridHotPDFExport 애드온입니다. 출력물은 주로 이중 레벨 규칙과 텍스트이므로, JBIG2로 구성된 문서는 사용자의 추가 조작 없이도 이러한 내보내기를 작게 유지합니다. 이 블로그의 관련 주제 두 가지에서 주변 워크플로우에 대해 더 깊이 다룹니다. 보고서를 작성할 때 이미지와 글꼴이 배치되는 방법은 Delphi에서 글꼴 및 이미지를 사용한 보고서 출력을 참조하십시오. 압축된 문서가 보관 프로필을 충족해야 하는 경우 Delphi의 PDF/A, PDF/X 및 PDF/UA 유효성 검사에 있는 규칙이 특정 적합성 수준에서 어떤 필터를 허용하는지 알려줍니다. JBIG2는 여기서 다룬 다른 로딩, 편집 및 암호화 API와 함께 Delphi 및 C++Builder용 HotPDF Component의 일부로 제공됩니다