/IC 색 없이 그린 레닥션 마크는 적용 단계를 거쳐 투명한 구멍이나 빈 외곽선으로 나올 수 있는데, 그 위의 검토자 코멘트는 파일에 남습니다. PDFlibPas는 ApplyRedactions로 Redact 어노테이션을 적용합니다. 덮인 콘텐츠가 먼저 가고, 오버레이는 /RO나 /IC에서 오며 빠지면 검정이 대신하고, 마크의 사전은 비워집니다
표시는 되돌릴 수 있고, 적용은 아닙니다
ISO 32000-1 §12.5.6.23은 레닥션을 콘텐츠 식별과 콘텐츠 제거로 나눕니다. 식별 동안 Redact 어노테이션은 사용자가 여전히 보고 옮기고 다시 정의할 수 있는 제안일 뿐인데, 검토 라운드를 가능하게 하는 것이 바로 그것입니다. 제거는 애플리케이션의 몫이고 요구는 단단합니다. 어노테이션이 지목하는 모든 것이 가고, 어노테이션이 가고, 이미지의 덮인 부분은 데이터가 파괴됩니다. 픽셀 위의 클리핑 패스나 이미지 마스크로는 부족하기 때문입니다. 키들은 ISO 32000-1의 Table 192(ISO 32000-2의 Table 195)에 나열되고, 그 대부분은 무엇을 지우는가보다 제거 뒤 페이지가 어떻게 보여야 하는가를 묘사합니다
| 키 | 규격에서의 역할 | ApplyRedactions가 하는 일 |
|---|---|---|
QuadPoints | n개의 사변형으로 나타낸 제거 영역. 있으면 Rect를 대신해 영역이 됩니다 | 각 사변형의 바운딩 박스를 따로 지웁니다. 길이가 8의 배수가 아닌 배열은 Rect로 물러납니다 |
Rect | QuadPoints가 없을 때의 제거 영역 | 뒤집힌 모서리를 정규화해 사용합니다 |
IC | 제거 뒤의 DeviceRGB 채움. 없으면 투명. RO가 있으면 무시됩니다 | 있으면 지키고, 없으면 검정으로 더합니다 |
RO | Form XObject 오버레이. IC, OverlayText, DA, Q보다 우선합니다 | 보통 외형으로 설치되어 페이지에 평탄화됩니다 |
OverlayText, Repeat | 영역 위에 그리는 레이블, 타일링 선택 | 만들어진 오버레이에 그려지고, Repeat가 참이면 타일링됩니다 |
DA, Q | 레이블의 폰트, 크기, 색. 정렬 | 크기와 색은 DA에서, 정렬은 Q에서 읽습니다 |
AddRedactAnnotation으로 마크 만들기
TPDFlib.AddRedactAnnotation(Left, Top, Width, Height, FillR, FillG, FillB, OverlayText, RepeatOverlay)는 선택된 페이지에 마크를 더하고 페이지의 새 어노테이션 수를 돌려주는데, 그 수가 곧 1부터 셈하는 마크의 인덱스이고 아무것도 더해지지 않으면 0입니다. 좌표는 현재 원점과 단위를 따르므로, 예제는 왼쪽 위 원점으로 바꿉니다. 거기서 Top은 위쪽 모서리입니다. 호출은 /Rect와 채움 컴포넌트로부터의 /IC, 그리고 /F0 10 Tf 1 1 1 rg라는 고정 /DA, 곧 10포인트 흰 텍스트를 씁니다. 빈 문자열이 아닌 OverlayText는 가운데 정렬 레이블을 위해 /OverlayText, /Repeat, /Q 1을 더합니다. Redact 어노테이션은 PDF 1.7 기능이므로 더 오래된 헤더는 1.7로 올라갑니다. 외형 스트림은 아직 쓰이지 않습니다. 적용 전 마크는 검토자 애플리케이션이 그리는 대로 보입니다. 레이블이 흰색으로 고정되어 있으니 OverlayText는 어두운 채움과 짝지으세요. 연한 노랑 위에서는 레이블이 사라집니다. 마크는 모든 PDFlibPas 에디션에서 만들 수 있습니다
uses
System.SysUtils, PDFlibrary;
procedure MarkForReview(const SourceFile, ReviewFile: string);
var
Pdf: TPDFlib;
Index: Integer;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile(SourceFile, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s', [SourceFile]);
Pdf.SetOrigin(1); // 왼쪽 위 원점: Top이 위쪽 모서리입니다
Pdf.SetMeasurementUnits(0); // 포인트
if Pdf.SelectPage(1) <> 1 then
raise Exception.Create('Page 1 not found');
// 증인 이름: 검정 채움, 가운데 흰 레이블
Index := Pdf.AddRedactAnnotation(72, 180, 220, 14, 0, 0, 0, 'REDACTED');
if Index = 0 then
raise Exception.Create('Mark was not added');
// 계정 블록: 면제 코드를 더 높은 상자에 타일링합니다
Pdf.AddRedactAnnotation(72, 410, 300, 40, 0, 0, 0, '(b)(6)', 1);
// 마크만 있을 뿐, 원본 콘텐츠의 모든 바이트는 여전히 남아 있습니다
Pdf.SaveToFile(ReviewFile);
finally
Pdf.Free;
end;
end;
QuadPoints가 제거 영역을 정하고, 회신은 텍스트를 지킵니다
마크가 /QuadPoints를 싣는 순간 그 사변형들이 ApplyRedactions가 무엇을 지우는지 정하고, Rect는 화면 표시를 묶는 것만 남습니다. 텍스트 선택 도구로 만든 마크는 보통 선택된 줄마다 사변형 하나를 지니므로, 그런 마크를 SetAnnotRect로 크기 조정하면 화면의 프레임만 옮겨지고 제거 영역은 제자리에 남습니다. 영역을 옮기는 호출은 SetAnnotQuadPoints이고, Rect만 의지하는 마크는 GetAnnotQuadCount가 0을 돌려줍니다. 마크에 붙는 텍스트는 다른 규칙을 따릅니다. 검토 도구는 메모를 /Contents에, 저자를 /T에 두고, 레닥션 마크의 메모는 지워질 바로 그 것을 묘사하는 경향이 있습니다("고객 SSN, 법무팀 확인"). ApplyRedactions는 마크 자신의 사전을 비우지만, /IRT로 그 아래에 이어진 회신은 별개의 어노테이션이라 사각형이 레닥션 영역과 겹칠 때만 지워지므로, 여백에 세워 둔 회신은 텍스트를 지킵니다. 아래 목록 출력은 마크마다 사변형 수와 그 아래 이어진 회신들을 인쇄합니다
procedure ListPendingMarks(Pdf: TPDFlib);
var
P, I, Parent: Integer;
begin
for P := 1 to Pdf.PageCount do
begin
if Pdf.SelectPage(P) <> 1 then
Continue;
for I := 1 to Pdf.AnnotationCount do
if Pdf.GetAnnotType(I) = 'Redact' then
Writeln(Format('p%d #%d by %s, quads=%d: %s',
[P, I, Pdf.GetAnnotTitle(I), Pdf.GetAnnotQuadCount(I),
Pdf.GetAnnotContentsEx(I)]))
else
begin
Parent := Pdf.GetAnnotReplyTo(I); // /IRT가 없을 때 0
if (Parent > 0) and (Pdf.GetAnnotType(Parent) = 'Redact') then
Writeln(Format(' reply #%d to #%d: %s',
[I, Parent, Pdf.GetAnnotContentsEx(I)]));
end;
end;
end;
ApplyRedactions 뒤에는 어느 오버레이가 나타나는가?
/RO 항목은 있기만 하면 이깁니다. Table 192의 우선 규칙이 그렇게 요구합니다. ApplyRedactions는 그 Form XObject를 어노테이션의 보통 외형(/AP /N)으로 설치하고 외형 상태를 버리며 평탄화하므로, /IC, /OverlayText, /DA, /Q는 그 마크에 아무 효과가 없습니다. 배치가 유일한 미묘한 부분입니다. 규격은 오버레이의 원점이 Rect의 왼쪽 아래 모서리와 나란하다고 말하고, 평탄화는 Form의 BBox를 그 Matrix로 Rect에 사상합니다. 평탄화가 폼 필드 위젯에 적용하는 바로 그 BBox와 Matrix 배치입니다. BBox가 [0 0 w h] 꼴로 사각형에 맞춰지고 Matrix가 항등이면 두 해석 아래에서 같게 보이므로, 자기 도구가 /RO 마크를 만들 때는 그 꼴을 요청하세요
/RO 없이는 ApplyRedactions가 사전을 정규화하고 외형을 직접 지어냅니다. /C와 /BS를 지우고 /Border를 [0 0 0]으로 놓습니다. 그 항목들은 보통 색 외곽선인 적용 전 마크를 묘사하고, 그것을 최종 페이지에 구워 넣으면 끝난 레닥션이 안 끝난 것처럼 보입니다. 테두리가 사라지면 채움이 사각형 전체를 덮습니다. 레이블은 만들어진 Form 안에서 그려지는데, 크기와 색은 /DA에서, 정렬은 /Q에서, 타일링은 /Repeat에서 오고 폰트 이름 F0은 그 Form의 자체 리소스에서 표준 Helvetica로 묶입니다. 이 묶음은 파일이 도구 사이를 다닐 때 문제가 됩니다. ISO 32000-1 §12.7.3.3은 DA 폰트 이름을 대화형 양식의 기본 리소스(/DR)의 항목으로 정의하고, 폼 필드가 없는 문서에는 그것이 없으며, 같은 마크를 적용하는 다른 애플리케이션은 대체 폰트로 바꿔치기할 수 있습니다. PDFlibPas는 그 탐색을 건너뛰고, Helvetica면 REDACTED나 면제 코드 같은 라틴 문자 텍스트에는 레이블이 어울립니다
없는 /IC는 검정 상자가 됩니다
규격의 없는 /IC 기본값은 투명한 내부이고, PDFlibPas는 일부러 그것에서 벗어납니다. 제거 뒤 투명한 내부는 종이를 보여 줍니다. 이름이 있던 자리의 흰 구멍이고, 그 구멍은 원래부터 아무것도 없던 레이아웃과 똑같이 보입니다. 리더는 레닥션된 것과 빈 것을 구별하지 못하고, 검토자는 추출 텍스트를 비교하지 않고서는 레닥션이 있었는지 확인하지 못합니다. 외곽선뿐인 마크의 적용 전 외형을 평탄화할 때 적용 단계가 내놓는 빈 테두리는 다르게 실패합니다. 아무도 손대지 않은 제안으로 읽힙니다. 우리는 기본값을 문자 그대로 따를지 저울질하고 검정 상자를 택했습니다. 무언가 가려졌다는 보이는 징후는 레닥션 문서가 리더에게 말하는 것의 일부이고, 이 선택은 작성자가 채움을 밝히지 않은 마크에만 영향을 주기 때문입니다. 사전에 이미 있는 /IC는 값이 무엇이든 쓰이므로, 흰색을 요청한 생산자는 흰색을 얻습니다
오버레이가 그려지기 전에 사라진 것
ApplyRedactions는 페이지마다 일하고 모든 오버레이를 평탄화하기 전에 모든 제거를 끝냅니다. 그래서 오버레이가 자기 영역에 걸릴 일은 없습니다. 페이지마다 사변형마다 사각형 하나씩, 혹은 사변형 없는 마크의 Rect를 모아 그 각각에 맞춰 모든 콘텐츠 레이어를 지웁니다:
- 텍스트는 글리프 단위로 잘리고, 페이지 폰트와 전체 텍스트 상태에서 재므로, 일부만 덮인 문자열의 나머지는 원래 위치에 남습니다
- 영역 안쪽에 그리는 벡터 패스, 인라인 이미지, 셰이딩은 콘텐츠 스트림에서 삭제됩니다
- Form XObject는 재귀로 들어갑니다. 다른 페이지나 배치도 그리는 Form은 사본을 통해 레닥션되므로, 그 페이지들은 원본을 지킵니다
- 이미지 XObject는 덮인 픽셀만 잃고 검정(CMYK는 K 잉크 최대)으로 덮어 씁니다. JPEG, JPEG 2000, JBIG2, CCITT 이미지는 디코딩되어 무손실 Flate 스트림으로 저장됩니다
- 태그가 붙은 PDF에서는 지워진 마크 콘텐츠에 붙은
/Alt와/ActualText가 페이지 콘텐츠와 구조 트리에서 비워집니다 - 사각형이 영역에 닿는 다른 어노테이션은 삭제되고, 위젯이 그 자리에 앉은 폼 필드는 값을 잃습니다
픽셀 단계는 실전에서의 규격 이미지 조항입니다. 스캔된 여권 위에 그린 검정 사각형은 눈을 만족시켜도 §12.5.6.23을 여전히 실패합니다. 원본 샘플은 누군가 이미지를 추출하는 순간 디코딩되기 때문입니다. 같은 엔진이 직접 넘긴 사각형에서 콘텐츠를 지우고 SanitizeDocument와 짝을 이루는 RedactRegion 뒤에 앉아 있고, 어노테이션은 하나도 읽지 않습니다. 콘텐츠가 사라지면 마크마다 평탄화되고 사전은 /Type과 /Subtype까지 깎입니다. 평탄화는 어노테이션을 페이지의 /Annots 배열에서 빼지만, 사전은 여전히 문서 안의 객체이고, 그것을 쓰는 저장은 /Contents, /RC, /T와 검토 흔적의 나머지를 받는 사람에게 넘깁니다
적용 전 리비전 없이 저장하기
증분 저장은 위의 모든 것을 무효로 만듭니다. §7.5.6(ISO 32000-1과 ISO 32000-2에서 같은 번호) 아래에서 증분 업데이트는 바뀐 객체들을 파일 끝에 덧붙이고 원본 바이트는 그대로 두므로, 페이지 콘텐츠와 이미지, 표시된 마크를 싣은 원본 리비전은 파일을 더 이른 %%EOF에서 자르는 사람에게는 계속 읽힙니다. PDFlibPas Small Business 에디션의 ExtractDocumentRevision이 그것을 한 번의 호출로 합니다. ApplyRedactions 뒤에는 SaveToFile로 새 파일을 쓰고, 덧붙이는 SaveChanges는 절대 쓰지 마십시오. 저장 전에 GarbageCollectObjects가 객체 테이블을 훑고 아무도 참조하지 않는 객체를 버립니다. 적용 단계 전에 마크가 싣던 외형 스트림 같은 것들입니다. 전면 저장은 SetGarbageCollect가 켜거나 SaveToFileOptions에 넘길 TPDFlibSaveOptions 레코드의 GarbageCollect 필드가 설정돼 있지 않으면 그 훑기를 건너뜁니다. 메타데이터가 남는 구멍입니다. SanitizeDocument(SANITIZE_METADATA or SANITIZE_XMP_PACKET)는 Author, Subject, Keywords, Creator, Producer를 비우고 XMP 스트림을 제거하며, Title은 검토할 몫으로 남깁니다. 여기서 SANITIZE_ALL은 피하세요. SANITIZE_ANNOTATIONS를 포함해 문서에 남은 모든 어노테이션을 지웁니다. 지키려던 메모도 그 가운데 있습니다
ApplyRedactions는 RedactRegion처럼 Enterprise 에디션에 있습니다. 평범한 TPDFlib에서는 0을 돌려주고 LastErrorCode를 PDFLIB_ERROR_ENTERPRISE_REQUIRED(900)으로 놓으므로, 배포 단계는 PDFlibEnterprise 단위의 TPDFlibEnterprise를 씁니다
uses
System.SysUtils, PDFlibrary, PDFlibEnterprise;
procedure ApplyAndRelease(const ReviewFile, ReleaseFile: string);
var
Pdf: TPDFlibEnterprise;
Applied, Orphans: Integer;
begin
Pdf := TPDFlibEnterprise.Create;
try
if Pdf.LoadFromFile(ReviewFile, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s', [ReviewFile]);
Applied := Pdf.ApplyRedactions; // 모든 페이지의 모든 Redact 마크
if Applied = 0 then
raise Exception.CreateFmt('No marks applied (error %d)',
[Pdf.LastErrorCode]);
// Info와 XMP는 페이지가 더 이상 보여 주지 않는 것을 여전히 이름 댈 수 있습니다
Pdf.SanitizeDocument(SANITIZE_METADATA or SANITIZE_XMP_PACKET);
Orphans := Pdf.GarbageCollectObjects;
// 새 파일로 전면 재작성. 레닥션 뒤에 SaveChanges는 금물입니다
if Pdf.SaveToFile(ReleaseFile) <> 1 then
raise Exception.Create('Save failed');
Writeln(Format('%d marks applied, %d unreferenced objects dropped',
[Applied, Orphans]));
finally
Pdf.Free;
end;
end;
기대되는 것: ApplyRedactions는 모든 페이지에 걸쳐 평탄화한 Redact 마크 수를 돌려주고, MarkForReview가 쓴 검토 사본에서는 2입니다. GarbageCollectObjects는 훑기가 버린 참조 없는 객체 수를 돌려줍니다
어느 도구가 마크를 그렸든 그 /RO와 /IC 항목이 받는 사람이 페이지에서 보는 것을 정하고, PDFlibPas Enterprise for Delphi의 적용 단계가 그 밑에서 사라진 것을 정합니다