여러분의 컴퓨터에서는 파일이 깔끔하게 열립니다. Acrobat도 이를 보여주고, 인쇄 미리보기도 제대로 보이며, 모든 페이지가 있습니다. 그런데 그 파일이 인쇄소로, 또는 월간 배치를 수집하는 아카이브 시스템으로 넘어가면 거부되어 돌아옵니다. CMYK 작업 안의 RGB 이미지, 없는 /Trapped 키, 인쇄기와 맞지 않는 출력 의도가 그 이유입니다. 누구 눈에도 문서 자체에는 아무 문제가 없었습니다. 프로파일에 대해서는 잘못되었고, 그 프로파일은 여러분이 없는 곳에서 검사되었습니다. 프리플라이트는 이 검사를 가리키는 인쇄 전 공정 용어이며, 진짜 질문은 PDF가 디자이너의 데스크톱이 아니라 여러분의 Delphi 코드에서 나올 때 이 검사가 어디에 속해야 하는가입니다
HotPDF는 호출할 수 있는 프리플라이트 함수를 제공하지 않습니다. 이 컴포넌트는 GUI 데모에 프리플라이트 보고서 창을 담고 있지만, 서비스나 빌드 스크립트가 호출할 수 있는 API는 그 뒤에 없으며, 그렇지 않은 척하면 존재하지 않는 메서드를 찾아 헤매게 됩니다. 이는 구멍처럼 들리지만, 여러분이 직접 생성하는 파일의 경우 여러분 자신의 출력에 검증기를 호출하는 것 자체가 애초에 잘못된 형태라는 점을 깨달으면 이야기가 달라집니다. 검증기가 검사할 모든 속성을 이미 여러분이 통제하고 있습니다. 유용한 분리 방식은 생성기가 애초에 잘못된 파일을 낼 수 없게 만든 다음, 여러분이 작성하지 않은 도구로 그것을 증명하는 것입니다
왜 자신의 출력물은 다르게 검사해야 하는가
전통적인 프리플라이트는 낯선 파일을 전제로 합니다. 어떤 디자이너, 어떤 다른 애플리케이션, 알 수 없는 수정 체인이 이를 만들어냈고, 여러분은 안에 무엇이 있는지 전혀 모르기 때문에 이를 검사합니다. 여러분의 코드가 만든 문서는 낯설지 않습니다. 글꼴 임베딩, 색 공간, 출력 의도, 메타데이터 블록, 이 모든 것을 여러분의 프로그램이 파일이 디스크에 닿기 몇 밀리초 전에 결정했습니다. 방금 스스로 내린 선택을 나중에 검사해서 발견하는 것은 헛수고입니다. 더 저렴한 방법은 그런 부적합 파일이 애초에 존재해 걸릴 일이 없도록 선택 자체를 제약하는 것입니다
검증을 외부에 두어야 하는 신뢰성 관련 이유도 있습니다. 자신의 출력물에 스스로 합격점을 주는 라이브러리는 자신의 시험지를 스스로 채점하는 셈입니다. 고객의 아카이브 시스템이나 인쇄소의 RIP가 여러분의 파일을 거부할 때, "우리 컴포넌트는 괜찮다고 합니다"는 아무런 무게도 없습니다. veraPDF나 Acrobat의 판정은 무게가 있습니다. 상대편이 같은 도구를 돌리기 때문입니다
준수를 체크리스트가 아니라 설정으로 만들기
예방 계층은 그저 설정일 뿐입니다. BeginDoc 이전에 PDFACompliance나 PDFXCompliance를 설정하면 HotPDF는 전체 생성 패스에 걸쳐 해당 규칙을 유지합니다. 글꼴을 임베드하고, 여러분이 선언한 출력 의도에 대해 DeviceRGB와 DeviceCMYK 사용을 감시하며, 프로파일이 금지하는 기능을 거부합니다. 모순은 EndDoc에서 드러나며, 이 시점에서 준수 게이트는 조용히 다운스트림에서 실패할 무언가를 배포하는 대신 예외를 던집니다. 파일이 저장되고 나면, 같은 속성들이 실제로 무엇이 강제되었는지 다시 읽어주며, 이는 여러분의 파이프라인 로그가 가장 필요로 하는 사실입니다:
// EndDoc 이후: 실행 메타데이터와 함께 강제된 프로파일을 기록합니다
if Pdf.PDFACompliance <> '' then
Log('Generated as PDF/A level ' + Pdf.PDFACompliance);
if Pdf.PDFXCompliance <> '' then
Log('Generated as PDF/X profile ' + Pdf.PDFXCompliance);
이 플래그들을 입력 데이터 해시, HotPDF 버전과 같은 로그 줄에 두십시오. 검증기와 여러분의 생성기가 어떤 파일에 대해 의견이 갈리는 날, 그 줄은 어떤 템플릿이 이를 만들었고 라이브러리의 어떤 빌드가 로드되어 있었는지 알려주며, 그렇지 않았다면 오후 한나절을 잡아먹었을 논쟁이 grep 한 번으로 끝납니다. 이 플래그들 뒤에 있는 출력 의도, ICC 프로파일, 태깅은 HotPDF로 PDF/A, PDF/X, PDF/UA 출력을 만드는 가이드에서 상세히 다룹니다
직접 생성하지 않은 파일을 위한 저렴한 첫 관문
모든 파이프라인이 순수하게 생성만 하는 것은 아닙니다. 고객이 PDF를 업로드하고, 스캐너가 폴더에 떨어뜨리며, 파트너가 이메일에 첨부합니다. 이런 파일 하나하나를 전체 구조 검증기에 통과시키는 것은 열리지도 않을 파일에 큐 시간을 낭비하는 일입니다. HotPDF의 다이렉트 파일 API는 전체 객체 트리를 로드하지 않고도 파일 구조를 충분히 읽어 "이것이 애초에 사용 가능한 PDF인가"에 답하므로, 빠르게 실패시키기 좋은 지점입니다:
function TriagePdf(Pdf: THotPDF; const FileName: string): Boolean;
var
Handle, Pages: Integer;
begin
Result := False;
Handle := Pdf.DAOpenFileReadOnly(FileName, '');
if Handle <= 0 then
Exit; // 구조적으로 읽을 수 없음: 검증하지 말고 격리함
try
Pages := Pdf.DAGetPageCount(Handle);
Result := Pages > 0;
finally
Pdf.DACloseFile(Handle);
end;
end;
이 API에 대한 두 가지 사실이 여러분이 이를 어떻게 감싸야 하는지를 결정합니다. 평평한 메모리 지름길은 암호화되지 않은 입력에만 유지됩니다. DAOpenFileReadOnly에 비밀번호를 건네면 조용히 완전한 파싱으로 물러나므로, 암호화된 것으로 알고 있는 파일이라면 선별 검사 전에 DecryptFile을 거쳐 평문 작업 사본으로 만들어야 합니다. 그리고 DAGetPageCount는 깔끔하게 열리지 않은 핸들에서는 아무 의미도 없으므로, 핸들 검사는 엄격하게 유지해야 하며 양수가 아닌 결과는 재시도가 아니라 거부입니다. 이런 패턴이 더 많이 담긴 곳은 대용량 PDF 워크플로를 위한 다이렉트 파일 API 문서입니다
빌드의 일부로 실행되는 veraPDF
PDF/A나 PDF/UA로 주장하는 무엇이든, veraPDF가 연결해야 할 검증기입니다. 이는 헤드리스로 실행되고, 배치를 받아들이며, XML이나 JSON을 내보내고, 각 실패를 해당 ISO 조항으로 이름 붙이므로, ISO 19005-1 6.2.2절에 대한 규칙 실패는 여러분이 추측할 필요 없이 곧바로 생성기 설정을 가리킵니다. 이를 Delphi에서 구동하는 것은 평범한 프로세스 제어입니다:
function RunVeraPdf(const PdfFile, ReportFile: string): Cardinal;
var
Cmd: string;
SI: TStartupInfo;
PI: TProcessInformation;
begin
Cmd := Format('cmd /c verapdf.bat --format xml "%s" > "%s"',
[PdfFile, ReportFile]);
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(SI);
if not CreateProcess(nil, PChar(Cmd), nil, nil, False,
CREATE_NO_WINDOW, nil, nil, SI, PI) then
RaiseLastOSError;
try
WaitForSingleObject(PI.hProcess, 120000); // 파일당 대기 시간을 제한함
GetExitCodeProcess(PI.hProcess, Result);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
end;
이 타임아웃은 그만한 가치가 있습니다. 잘못된 형식의 파일은 어떤 파서든 결코 빠져나오지 못하는 구석으로 몰아넣을 수 있으며, 큐 워커 안에서의 무한정 대기는 나머지 큐 전체를 함께 끌고 내려갑니다. 대기 시간을 제한하고, 타임아웃에 자신만의 실패 코드를 부여하며, 파일은 사람에게 넘기십시오. 결과를 읽을 때는 사람이 읽기 쉬운 텍스트가 아니라 XML의 규칙 식별자를 파싱하십시오. 규칙 ID는 검증기 업그레이드를 견뎌내지만 메시지의 표현은 그렇지 않으며, 안정적인 코드라야 지원 엔지니어가 예전 티켓을 검색할 수 있습니다
배치를 어떻게 실행하는지는 각 파일이 통과하는지 여부만큼이나 중요합니다. 배치당 하나가 아니라 파일당 하나의 프로세스여야, 해로운 입력 하나가 그 파일의 타임아웃만 소비하고 그 이상은 소비하지 않습니다. 검증기 프로세스 수는 코어 수로 제한하십시오. XML 보고서를 만드는 것은 CPU에 묶여 있어서 과도한 구독은 그저 스래싱만 일으킵니다. 그리고 수신 시점에 크기 상한을 두십시오. 2기가바이트짜리 스캔된 책은 파서가 아무리 참을성 있어도 큐 전체를 독점할 것입니다. 이 중 어느 것도 엄밀한 의미의 프리플라이트는 아닙니다. 이는 월말 물량을 견디며 살아남는 게이트와, 파이프라인을 새벽 두 시에 멈춰 세운 첫날 밤 꺼져 버리는 게이트 사이의 차이입니다
PDF/X는 여기서 부족한 부분입니다. veraPDF는 이를 검증하지 않으므로, 실질적인 검사는 여전히 여러분의 인쇄소가 지정한 ISO 15930 프로파일을 사용하는 Acrobat의 Preflight입니다. Acrobat은 사람을 필요로 하며, 이는 전체 커버리지가 아니라 샘플링을 의미합니다. 새 템플릿에서 나온 첫 파일에 더해, 각 배치에서 소량을 무작위로 뽑아 검사하는 동안, 사람 없이 처리할 수 있는 모든 것은 자동화된 게이트가 처리합니다. 실제로 실행되는 샘플링 검사가 영원히 절반만 완성된 채로 남는 완전한 자동화보다 낫습니다
1년 뒤에도 여전히 필요할 보고서
프리플라이트 게이트는 두 번 보답합니다. 문 앞에서 잘못된 파일을 막아설 때 한 번, 그리고 한참 뒤 누군가 특정 파일이 왜 통과되었는지 물을 때 다시 한 번입니다. 두 번째 순간이야말로 형식을 좌우해야 하는데, 얇은 보고서가 여러분을 오도가도 못하게 만드는 것이 바로 이 순간이기 때문입니다. 검사한 모든 파일에 대해 입력 해시, 위 로그 줄의 생성기 준수 플래그와 라이브러리 버전, 검증기 이름과 버전, 검사에 사용된 프로파일, 합격 또는 불합격 여부, 그리고 검증기가 제공하는 곳이라면 어디든 페이지 번호와 함께 실패한 규칙 ID를 보관하십시오. 그 보고서는 그것이 설명하는 파일 옆에 저장하십시오. 별도의 시스템에 두면 그 시스템은 그것이 문서화하는 아카이브보다 먼저 폐기될 것입니다
예외 사항도 기록해 두어야 합니다. 고객이 게이트가 싫어하는 파일을 그대로 배포하겠다고 고집할 때, 답은 모두를 위해 규칙을 느슨하게 만드는 것이 아닙니다. 누가 어떤 근거로, 언제까지 이 파일을 승인했는지 기록한 다음, 그 면제 사항을 파일의 보고서에 첨부하십시오. 이름과 만료일이 있는 면제는 누군가 책임지는 결정입니다. "임시로" 주석 처리된 검사는 자신의 날짜를 기다리는 사고입니다
그 자체로 값어치를 하는 습관이 하나 더 있습니다. 파일이 실패하면 누구도 그것을 건드리기 전에 이름 붙인 회귀 폴더에 복사해 두십시오. 디버깅할 가치가 있는 프리플라이트 문제는 거의 대부분 하나의 특정한 입력으로 거슬러 올라가며, 그런 입력들을 붙들어 두는 팀은 이 문제가 운영 환경에서 다시 나타나기를 기다리는 대신 한 시간 만에 재발을 고칩니다. 여기서 소개한 준수 속성과 다이렉트 파일 API는 Delphi와 C++Builder를 위한 HotPDF Delphi Component의 일부이며, 그 문서가 각 호출을 완전히 다룹니다