PDF Library for Delphi는 AddModernImageFromFile과 그 스트림·문자열 변형을 통해 AVIF, HEIF, JPEG XL 이미지를 입력으로 받으며, PDF 이미지 객체로 들어가는 과정에서 알파, 임베드된 ICC 프로파일, 16비트 채널을 보존합니다. 형식 감지는 제한된 매직 넘버 읽기로 이루어지고, 디코딩은 교체 가능한 백엔드를 거치므로, 실제로는 이런 형식이 아닌 파일에 대해서는 외부의 무엇도 호출되지 않습니다
이 형식들이 문서 워크플로에 들어오게 된 것은 휴대폰 때문입니다. iOS는 몇 년째 기본으로 HEIC를 만들어내고, Android 기기는 AVIF를 만들어내며, 파손된 부품을 촬영한 현장 기술자는 2015년에 만들어진 PDF 리포트 생성기가 아예 열지도 못하는 이미지를 보내옵니다. 일반적인 대체 경로인 플랫폼 비트맵을 거친 디코딩은 예외 없이 8비트 색상만 남기고 알파와 색상 프로파일은 그 과정에서 잃어버립니다
모던 이미지 경로는 비트맵 변환이 잃어버리는 무엇을 보존할까?
세 가지가 있으며, 각각에는 이에 의존하는 워크플로가 있습니다. 알파는 그대로 살아남으며, 이는 페이지 콘텐츠 위에 합성되는 로고와 제품 컷아웃에 중요합니다. ICC 프로파일도 그대로 살아남으며, 이는 인쇄되거나 색상 매칭이 필요한 모든 것에 중요합니다. 그리고 16비트 채널도 살아남으며, 이는 8비트 양자화가 촬영의 목적이었던 바로 그 미세한 계조를 파괴하는 의료·과학 이미지에서 중요합니다
이미지를 플랫폼 비트맵으로 통과시키면 이 셋 모두를 한 번에 잃어버리며, 그것도 조용히 일어납니다. 만들어진 PDF는 대략 맞아 보이고, 인쇄소가 왜 기업 로고의 빨간색이 다르냐고 묻기 전까지는 아무도 눈치채지 못합니다. 모던 이미지 호출의 옵션 값 8은 알파, ICC, 16비트 채널을 함께 유지하는 플래그이며, 이 호출들의 기본값이기도 합니다
페이지에 하나 추가하기
이 호출은 이미지 식별자를 반환하며, 그런 다음 선택해 그리거나 한 번에 그리고 해제할 수 있습니다:
uses
PDFlibrary, PDFlibModernImage;
var
Lib: TPDFlib;
ImageID: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.NewDocument;
Lib.SetPageSize('A4');
Lib.NewPage;
// Options = 8 keeps alpha, ICC and 16-bit channels
ImageID := Lib.AddModernImageFromFile('site-photo.heic', 8);
if ImageID > 0 then
Lib.DrawImageAndRelease(ImageID, 40, 40, 515, 340)
else
Lib.DrawText(40, 40, 'image could not be decoded');
Lib.SaveToFile('inspection-report.pdf');
finally
Lib.Free;
end;
end;
감지는 디코딩보다 먼저 일어나며 의도적으로 좁게 설계되어 있습니다. 라이브러리는 제한된 헤더를 읽어 AVIF와 HEIF를 식별하는 ISO base media file format 브랜드를 인식하고, JPEG XL의 원시 시그니처와 컨테이너 시그니처를 모두 인식한 다음 호출자의 스트림 위치를 복원합니다. 알 수 없거나 위장된 입력은 외부 코덱에 도달하지 않으므로, 이름만 바꾼 실행 파일이 그림인 척 디코더에 넘겨지는 일을 막아줍니다
디코딩은 실제로 어디서 일어날까?
모던 이미지 형식은 크고 복잡한 코덱이며, 이를 PDF 라이브러리 안에 통째로 집어넣는 것은 이상한 설계 선택일 것입니다. 기본 백엔드는 배포 가능한 MagickWand 모듈을 프로세스 안에서 동적으로 로드하며, 문서화된 순서, 즉 여러분이 설정한 명시적 파일이나 디렉터리, 환경 변수, 실행 파일 디렉터리, 시스템 검색 경로를 따라 그 모듈을 찾습니다
이미 디코더를 함께 배포하는 애플리케이션이나 외부 모듈을 전혀 로드해서는 안 되는 애플리케이션은 대신 자신만의 콜백을 등록합니다. 이 계약은 작습니다. 입력 스트림을 읽고, PNG를 출력 스트림에 쓰고, 요청받은 방향을 존중하는 것뿐입니다:
function MyDecoder(InStream, OutPNG: TStream;
ImageFormat: TPDFlibModernImageFormat;
ApplyOrientation: Boolean): Boolean;
begin
// Decode InStream with your own codec and write PNG bytes to OutPNG
Result := DecodeWithBundledCodec(InStream, OutPNG,
ImageFormat, ApplyOrientation);
end;
begin
RegisterModernImageDecoderBackend(MyDecoder);
// ... add images ...
ClearModernImageDecoderBackend; // back to the default backend
end;
배포에는 한 가지 편의와 한 가지 의도적인 절제가 함께 따라옵니다. 코덱 디렉터리에 modules\coders 서브디렉터리가 있으면, 호스트 애플리케이션이 아직 설정하지 않았을 때에 한해 라이브러리가 그런 레이아웃에 필요한 코덱 환경 변수를 채워 넣습니다. 자체적인 런타임 배포 전략을 가진 애플리케이션은 그대로 유지됩니다
왜 중간에 PNG를 거칠까?
원시 픽셀 버퍼가 아니라 메모리 안의 PNG를 거쳐 다리를 놓는 것은 추가 단계처럼 보이지만 실제로는 가장 저렴하면서도 올바른 방법입니다. PNG는 살아남아야 할 모든 것, 즉 알파, 색상 타입, 비트 심도, 임베드된 ICC 프로파일을 표현할 수 있고, 라이브러리는 이미 PNG에서 알맞은 필터와 색상 공간을 가진 PDF 이미지 객체로 가는 성숙하고 검증된 경로를 갖고 있습니다. 이를 재사용한다는 것은 모던 형식이 별도의 병렬 구현을 새로 만드는 대신 수년간의 정확성 작업을 그대로 물려받는다는 뜻입니다
이 다리는 전적으로 메모리 안에서 이루어지므로 임시 파일도 만들어지지 않고 충돌 시 정리할 것도 없습니다. 한 가지 세부 사항은 명시적인 처리가 필요했습니다. 일부 변환은 형식을 바꾸는 과정에서 ICC 프로파일을 떨어뜨립니다. 그래서 이 백엔드는 형식 전환 전에 원본 프로파일을 캡처하고, Flate로 압축하고, CRC를 다시 계산해 유효한 iCCP 청크를 만든 다음, 이와 충돌할 수 있는 sRGB 청크를 제거합니다. 테스트에서 디코드된 AVIF는 16비트 알파를 가진 16비트 RGBA를 그대로 유지했고, 결과 PDF에서 추출한 프로파일은 60,960바이트로 원본 프로파일과 바이트 단위로 일치했습니다
운영 환경에서 켜기 전 실무 참고 사항
첫 사진이 도착할 때가 아니라 시작 시점에 사용 가능 여부를 확인하십시오. ModernImageCodecAvailable은 백엔드를 쓸 수 있는지 보고하고, SetModernImageCodecLibrary는 배포 환경이 코덱을 표준 위치가 아닌 곳에 둘 때 명시적인 파일이나 디렉터리를 가리킵니다:
Lib.SetModernImageCodecLibrary('C:\MyApp\codecs');
if Lib.ModernImageCodecAvailable = 0 then
Log('modern image input unavailable - HEIC and AVIF will be refused');
결과 파일 크기를 지켜보십시오. 프로파일이 임베드된 16비트 RGBA 이미지는 큰 PDF 이미지 객체이며, 이런 이미지를 마흔 개 담은 리포트는 커질 수밖에 없습니다. 문서가 인쇄가 아니라 화면 열람용이라면 임베드 전 다운샘플링이 옳은 절충안이며, 일반적인 크기 조절 수단은 PDF 파일 크기 최적화에서 다룹니다
마지막으로, 색상 정책을 의도적으로 정하십시오. 원본 프로파일을 유지하는 것은 아카이빙과 인쇄 작업에는 옳지만, 여러 사진이 뒤섞여 있는데 일관되게 보여야 할 때는 문서 전체의 색상 공간으로 변환하는 편이 옳으며, 그 변환 경로는 문서를 다른 색상 공간으로 재착색하기에서 설명합니다. 파일에 실제로 무엇이 들어갔는지 확인해야 한다면, 텍스트, 이미지, 폰트 추출의 검사 경로가 문서가 담고 있는 이미지 객체를 보고합니다
모던 이미지 입력, 색상 관리, 이미지 최적화는 모두 Delphi, C++Builder, Free Pascal용 같은 라이브러리의 일부입니다. 전체 기능 목록은 Delphi용 PDF Library 페이지에 있습니다