PDF 첨부 파일은 문서의 내장 파일 트리 구조에 저장되며, 대부분의 PDF 뷰어 프로그램들은 이를 클립 아이콘 패널이나 첨부 파일 사이드바 형태로 외부에 표출합니다. Delphi 코드 상에서 PDFium Component는 TPdf의 인덱스 속성 세트를 통해 이 구조를 노출합니다: 즉 정수 인덱스를 사용해 데이터를 순회하고, 파일 이름과 바이트 페이로드를 읽어 들이며, 새로운 첨부 공간을 할당하거나 기존 공간을 삭제할 수 있습니다. 제공되는 API 구성은 비교적 단순하지만, 상용 코드를 작성하기 전에 숙지해 두어야 할 몇 가지 순서 제약 조건과 경로 정규화 규칙이 있습니다
열린 문서로부터 첨부 파일 데이터 읽기
AttachmentCount 속성은 문서에 선언된 내장 첨부 파일들의 총 개수를 반환합니다. PDFium 라이브러리의 연동 함수 호출 결과를 직접 읽어오므로 PDF에 내장된 실제 데이터를 충실히 대변합니다. 여기서 AttachmentName[Index]는 화면에 표시할 파일 이름을 WString 형식으로 반환하고, Attachment[Index]는 가공되지 않은 바이트 데이터를 TBytes 배열 형태로 전달합니다. 두 인덱스 모두 0부터 시작합니다. 이 속성들을 조회하기 전에 반드시 문서가 열린 상태(Pdf.Active = True)여야 합니다. 닫힌 문서에서 이들을 조회하면 예외 발생 없이 그냥 0 또는 빈 결과값만 반환됩니다
한 가지 주의할 점: Attachment[Index]는 호출될 때마다 메모리에 첨부 파일 데이터 전체를 실시간 할당해 반환한다는 사실입니다. 대용량 첨부 파일이 여러 개 엮인 문서의 경우, 화면에 파일 목록을 단순 표시하기 위해 모든 첨부 파일 데이터를 순회하며 매번 로드하면 메모리가 낭비됩니다. 화면 목록 구성 등에는 AttachmentName 속성을 먼저 읽어 활용하고, 바이트 데이터 로드는 사용자가 해당 파일을 실제로 더블클릭하거나 다운로드 요청했을 때 지연 호출되도록 배제하십시오
procedure ListAttachments(Pdf: TPdf);
var
I: Integer;
Data: TBytes;
begin
if not Pdf.Active then
Exit;
for I := 0 to Pdf.AttachmentCount - 1 do
begin
Data := Pdf.Attachment[I];
Writeln(Format('%d: %s (%d bytes)',
[I, Pdf.AttachmentName[I], Length(Data)]));
end;
end;
첨부 파일을 디스크 파일로 저장하기
추출은 raw 바이트 단위로 동작한다. 바이트를 읽어 필요한 곳에 쓰기만 할 뿐이므로, 경로 구성과 정제(sanitization)는 전부 호출자 코드의 몫이다. 첨부 파일 이름이 신뢰할 수 없는 문서에서 올 때는 이 차이가 중요해진다. PDF 첨부 파일 이름은 파일 안에 저장된 문자열에 불과하므로 경로 구분자, 유니코드 유사 문자, 그 밖에 TFileStream.Create에 그대로 넘겼다가 의도치 않은 결과를 낼 문자들을 담을 수 있다. 출력 경로를 만들기 전에는 이름을 항상 ExtractFileName으로 걸러 주고, 점으로 시작하거나 시스템이 기대하는 범위 밖의 문자를 담은 이름은 거부하는 쪽도 고려하라
Attachment[Index]가 반환하는 바이트 배열은 호출자 소유 메모리입니다. 이를 TFileStream 등을 거쳐 파일로 기록해 저장할 수 있으며, 선언된 임의의 파일 확장자명을 맹신하기보다는 저장 전에 바이트 헤더의 첫 몇 바이트를 대조해 실제 파일 포맷 사양이 부합하는지 교차 체크해 볼 수도 있습니다
procedure ExtractAttachment(Pdf: TPdf; Index: Integer; const OutputDir: string);
var
SafeName: string;
OutPath: string;
Data: TBytes;
FS: TFileStream;
begin
SafeName := ExtractFileName(Pdf.AttachmentName[Index]);
if SafeName = '' then
SafeName := Format('attachment_%d', [Index]);
OutPath := IncludeTrailingPathDelimiter(OutputDir) + SafeName;
Data := Pdf.Attachment[Index];
FS := TFileStream.Create(OutPath, fmCreate);
try
if Length(Data) > 0 then
FS.WriteBuffer(Data[0], Length(Data));
finally
FS.Free;
end;
end;
첨부 파일 추가 및 2단계 연동 구조
첨부 파일을 추가하려면 한 번이 아닌 두 번의 단계를 거쳐야 합니다. 우선 CreateAttachment(Name)을 호출하여 내장 파일 트리에 새 항목 영역을 생성하며, 성공 시 True가 반환됩니다. 이렇게 새로 생성된 파일 공간은 비어 있는 상태입니다. 이후 방금 생성된 타겟 공간을 가리키는 Attachment[AttachmentCount - 1] 인덱스 경로 상에 실제 파일 바이트 데이터를 대입해 채워 넣습니다. 만약 CreateAttachment 결과가 False인데도 데이터를 대입하면 기존의 엉뚱한 마지막 첨부 파일 데이터가 덮어씌워져 유실되는 사고가 나므로 예외 처리를 연동해야 합니다
첨부 파일 목록을 수정한 뒤 변경 사항은 메모리에만 존재한다. 갱신된 내장 파일 트리를 담은 새 파일을 쓰려면 SaveAs를 호출한다. 엔진이 소스에 대한 읽기 핸들을 쥐고 있기 때문에, SaveAs의 대상은 현재 열려 있는 파일과 다른 경로여야 한다. 제자리 갱신의 표준 패턴은 임시 경로에 저장하고, 문서를 닫고, 원본을 지우거나 이름을 바꾼 다음, 임시 파일을 제자리로 이름 바꾸고 다시 여는 것이다
procedure AddFileAttachment(Pdf: TPdf; const FilePath: string);
var
FS: TFileStream;
Data: TBytes;
AttachName: string;
begin
if not Pdf.Active then
Exit;
FS := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyWrite);
try
SetLength(Data, FS.Size);
if FS.Size > 0 then
FS.ReadBuffer(Data[0], FS.Size);
finally
FS.Free;
end;
AttachName := ExtractFileName(FilePath);
if Pdf.CreateAttachment(AttachName) then
Pdf.Attachment[Pdf.AttachmentCount - 1] := Data;
end;
첨부 파일 형식(MIME Type) 정보
이름 및 바이트 페이로드와 더불어, AttachmentType[Index]는 문서 작성 시점에 함께 기입된 MIME 타입 문자열 정보를 파일 사전 구조로부터 읽어 반환합니다. 많은 PDF 생성기들이 이 필드를 누락시키거나 범용적인 application/octet-stream 형태로 대충 적기 때문에, 상용 가공 파이프라인 단에서 이 속성만으로 파일 형식을 판단하는 것은 위험합니다. 정확한 검증을 위해 바이트 페이로드의 첫 몇 바이트를 직접 파싱해 시그니처를 체크하십시오: 예를 들어 중첩 PDF의 경우 %PDF를, 오피스 문서의 경우 ZIP 압축 시그니처인 PK\x03\x04를, 레거시 바이너리 복합 문서의 경우 \xD0\xCF\x11\xE0을 확인해 보는 형태입니다. 타입 정보 속성은 UI 상의 보조 표기 용도로만 활용하고, 실제 가공 연산 시에는 추출된 실 바이트 정합성 체크를 병행하십시오
첨부 파일 삭제 처리
DeleteAttachment(Index)는 특정 지정 인덱스의 첨부 파일 항목을 파괴하며, 성공 시 True를 반환합니다. 삭제 처리 후 뒤에 있던 남은 항목 인덱스들이 하나씩 앞으로 당겨지므로(shift down), 루프를 돌며 다중 첨부 파일을 제거할 때는 항목이 누락되지 않도록 인덱스 순방향이 아닌 끝 항목부터 시작하는 역방향(downto) 루프를 돌려 삭제해 나가야 합니다. 이 변경사항도 SaveAs를 통해 최종 저장해 주어야 확정됩니다
문서 가공 파이프라인에서 보안 확보나 파일 용량 최적화 등의 목적으로 인입된 PDF 내의 모든 첨부 파일을 일괄 박리하는 처리는 매우 흔하게 쓰입니다. 다음 루프를 적용해 처리해 주십시오:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
실무 환경에서의 PDF 첨부 파일 활용 사례
첨부 파일 처리 API는 PDFium이 해독 가능한 모든 PDF 규격을 완벽 수용하나, 실제 상용 실무 상에서 첨부 파일 구조가 빈번하게 활용되는 핵심 시나리오는 다음과 같습니다. 장기 보존 표준인 PDF/A-3(ISO 19005-3) 규격은 아카이빙 데이터 보존용 소스 파일을 PDF 바디 내부에 함께 묶어 보존할 수 있도록 첨부 파일 구조를 공식 허용하고 있습니다. ZUGFeRD 및 Factur-X 전자 송장 기술은 이 기능을 핵심 스펙으로 활용하여, 사용자가 육안으로 볼 수 있는 PDF 지면 디자인 레이아웃 내부에 구조화된 XML 데이터 파일을 묶어 전송합니다. 그 밖에 메일에서 연동 추출된 PDF의 경우 메일 본문의 오리지널 첨부 파일 정보들이 이 구조 상에 같이 녹아 있기도 하며, 테크니컬 디자인 설명서 등에서 원본 CAD 및 소스 리소스 데이터를 PDF 내부에 묶어 아카이빙하는 용도로도 널리 활용됩니다
외부로부터 다량의 PDF 파일을 인입받아 처리하는 자동화 가공 시스템을 설계 중이라면, 파일 진입 필터 단에서 AttachmentCount를 읽어 체크하는 방어적 구성을 적용해 볼 가치가 충분합니다. 첫째, 송장 PDF 내부의 XML 데이터처럼 자동 추출해서 DB화해야 하는 유용한 비즈니스 소스 데이터를 식별해 선별할 수 있습니다. 둘째, 검증되지 않은 첨부 파일 구조 내부에는 악의적인 실행 파일이나 맬웨어 코드 조각이 실려 있을 가능성도 배제할 수 없으므로, 해당 파일을 직접 열어 처리할 계획이 없더라도 첨부 파일 포함 유무 자체를 사전에 식별해 모니터링하기에 적합합니다. 두 조작 모두 어려운 코딩 없이 컴포넌트의 카운트 속성과 파일 이름 확인을 통해 실 바이트 가공 여부를 깔끔하게 연동해 낼 수 있습니다
여기에 소개된 첨부 파일 제어 속성 구성 요소 등은 Delphi 및 C++Builder용 PDFium Component의 표준 내장 사양으로 제공됩니다