폰트: 까다로운 부분
ISO 32000-2 §9 Evidence: Mixed evidence
한눈에 보기
섹션 제목: “한눈에 보기”폰트는 PDF가 완전히 올바르게 보이면서도 겉으로 드러나지 않게 망가질 수 있는 영역입니다. 한 페이지가 올바른 글리프를 렌더링하면서도 검색할 수 없고, 텍스트로 복사할 수 없으며, 아카이브 프로파일에 맞지 않을 수 있습니다. 이 모든 일이 한꺼번에 일어날 수 있으며, 눈에 보이는 경고 신호는 전혀 없습니다. 이 페이지는 반드시 맞아야 하는 세 가지 — 임베딩, 서브셋팅, 인코딩 —와 NextPDF가 각각에 대해 무엇을 하는지 설명합니다.
왜 중요한가
섹션 제목: “왜 중요한가”“괜찮아 보인다”는 PDF 작업에서 가장 위험한 문장이며, 폰트는 그 말이 가장 큰 피해를 만드는 영역입니다. 세 가지 독립적인 조건이 반드시 성립해야 합니다:
- 임베딩 — 폰트 프로그램이 파일 안에 함께 들어 있어서, 해당 폰트가 설치되지 않은 컴퓨터에서도 동일하게 렌더링됩니다.
- 서브셋팅 — 실제로 사용된 글리프만 담기므로, 20 MB짜리 CJK 폰트가 모든 문서를 비대하게 만들지 않습니다.
- 인코딩 — 페이지의 문자 코드에서 유니코드로 되돌아가는 올바른 매핑이 존재하므로, 텍스트를 검색, 복사, 색인할 수 있고 보조 기술로 읽을 수 있습니다.
시각적 렌더링은 첫 번째 조건만, 그것도 부분적으로만 증명합니다. 문서는 완벽한 글리프를 보여주면서도 세 번째 조건에서는 완전히 실패할 수 있습니다 — 그 텍스트는 단어가 아니라 단어를 그린 그림입니다. 이런 실패는 모든 “괜찮아 보인다” 검토를 통과한 뒤 규정 준수 감사나 증거 개시 요청에서 드러납니다.
짧게 요약하면
섹션 제목: “짧게 요약하면”- PDF 안의 폰트는 딕셔너리로 구성되며, 여기에 대개 임베디드 폰트 프로그램 스트림이 더해집니다.
- 서브셋팅은 그 프로그램을 사용된 글리프만 담도록 다시 씁니다. 서브셋 폰트의 이름에는 **여섯 개의 대문자로 된 태그와
+**가 붙어, 리더가 이를 별개의 것으로 취급합니다. - 인코딩은 문자 코드를 유니코드로 매핑하는 별개의 문제입니다.
/ToUnicodeCMap이 텍스트를 검색 가능하고 복사 가능하게 만드는 것이며 — 글리프가 올바르게 보이는지 여부와는 독립적입니다. /ToUnicode가 없거나 잘못된 채로 올바르게 보이는 텍스트는 전형적인 겉으로 드러나지 않는 실패입니다: 화면에서는 완벽하지만 실제로는 검색되지 않습니다.- NextPDF는 TrueType 폰트를 서브셋하고, 올바른 렌더링을 위해 글리프 식별자를 보존하며, 텍스트 추출이 동작하도록
/ToUnicodeCMap을 내보냅니다 — 그리고 PDF 2.0 임베딩 규칙을 단지 경고만 하는 것이 아니라 강제할 수 있습니다.
NextPDF의 접근 방식
섹션 제목: “NextPDF의 접근 방식”서브셋팅. FontSubsetter(src/Typography/FontSubsetter.php)는 원본 TrueType 테이블 디렉터리를 파싱하고 cmap을 읽어 유니코드 코드포인트를 글리프 ID에 매핑합니다. BMP 포맷 4와 CJK에 필요한 전체 유니코드 포맷 12를 모두 처리합니다. 그런 다음 단순한 서브셋터가 놓치기 쉬운 단계를 수행합니다: 복합 글리프 의존성을 전이적 폐쇄로 해석합니다. 기본 문자에 결합 표시를 더해 만들어진 악센트 글리프는 다른 글리프를 구성 요소로 참조합니다. 그 구성 요소가 누락되면 글리프가 잘못 렌더링됩니다. 서브셋터는 새 구성 요소가 더 이상 나타나지 않을 때까지 그 그래프를 순회하며, 잘못된 폰트 때문에 영원히 반복되지 않도록 사이클 가드를 둡니다.
이 파일에서 짚어 둘 만한 엔지니어링 선택은 두 가지입니다. 첫째, 글리프 ID는 재매핑되지 않고 보존되며 — 사용되지 않는 슬롯은 glyf/loca에서 0으로 채워지므로 콘텐츠 스트림의 원래 글리프 인덱스가 CIDToGIDMap /Identity 아래에서 유효하게 유지됩니다. 재매핑하면 더 작아질 수 있지만 모든 글리프 참조를 다시 써야 합니다. 식별자를 보존하는 방식이 구조적으로 올바릅니다. 둘째, 순회는 정렬(gid 오름차순)된 상태로 이루어져 서브셋이 바이트 단위로 결정적입니다 — 동일한 폰트와 동일한 글리프 사용 집합은 동일한 서브셋 바이트를 만들어 내며, 이는 재현 가능한 빌드가 요구하는 바입니다. 서브셋팅으로 파일을 약 10% 미만만 줄일 수 있다면 원본을 변경하지 않고 반환합니다. 한계적인 이득을 위해 오버헤드를 감수할 가치는 없습니다.
임베딩. 폰트 프로그램을 포함할지 여부는 명시적 정책이 결정합니다 — 결코 추측이 아닙니다. Pdf20FontEmbeddingPolicy(src/Writer/Pdf20FontEmbeddingPolicy.php)에는 두 가지 모드가 있습니다. PDF 2.0 프로파일에서는 Strict가 임베드되지 않은 표준 Type 1(“Base14”) 참조를 타입이 지정된 예외로 거부합니다 — 규격에 맞는 동작입니다. AllowBase14는 기존의 권고 경로를 보존합니다. 마이그레이션 기간에는 표준이 여전히 요구하는 최소한의 폰트 디스크립터를 내보내고, 예외를 던지는 대신 경고를 디스패치합니다. 호출자가 문서에서 그 선택을 명시적으로 하며, 폰트로부터 추론되는 일은 결코 없습니다.
인코딩. 복합(Type 0) 폰트의 경우, EmbeddedTtfFontDictBuilder(src/Writer/EmbeddedTtfFontDictBuilder.php)는 CIDFontType2 자손, Type0 부모, 그리고 문자 코드가 다시 유니코드로 해석되도록 /ToUnicode CMap 스트림을 내보냅니다. /ToUnicode 스트림이 정당하게 생략될 수 있는 경우는 단 하나뿐입니다: 자기 기술적 사전 정의 CJK CMap이 이미 리더에게 문자-대-유니코드 매핑을 제공하는 경우입니다. 그 경우에는 CMap이 곧 인코딩이므로, plain 프로파일은 바이트를 절약하기 위해 중복되는 /ToUnicode 스트림을 생략합니다. 그 경우를 제외하면, /ToUnicode 스트림이 바로 텍스트를 텍스트로 유지하는 수단입니다.
| 관심사 | 무엇을 보장하는가 | 무엇을 보장하지 않는가 | 잘못되었을 때 겉으로 드러나지 않는 실패 |
|---|---|---|---|
| 임베딩 | 폰트가 설치되지 않아도 동일하게 렌더링됨 | 텍스트가 검색 가능하다는 것 | 대체된 폰트; 다른 컴퓨터에서 잘못된 메트릭 |
| 서브셋팅 | 작은 파일; 사용된 글리프만 포함 | 인코딩에 관한 어떤 것도 | 누락된 복합 구성 요소 → 망가진 악센트 글리프 |
인코딩(/ToUnicode) | 검색 가능하고, 복사 가능하며, 접근 가능한 텍스트 | 글리프가 올바르게 렌더링된다는 것 | 완벽하게 보이는 페이지, 검색 불가 / 복사 시 깨짐 |
세 가지 폰트 관심사는 독립적입니다. 임베딩과 서브셋팅은 외관과 크기에 관한 것이고, 인코딩은 의미에 관한 것입니다. 한 페이지는 앞의 두 가지를 통과하면서도 세 번째에서 실패할 수 있으며, 이를 드러내는 시각적 신호는 아무것도 없습니다.
증거가 말하는 것
섹션 제목: “증거가 말하는 것”서브셋 이름 지정 규칙은 규범적이며 정밀합니다.
Spec: ISO 32000-2, §9.9.2 ISO 32000-2 §9.9.2 는 폰트
서브셋의 PostScript 이름(BaseFont와 디스크립터의 FontName)이
정확히 여섯 개의 대문자로 된 태그로 시작하고, 이어 더하기 기호가 오며,
그 뒤에 원본 폰트의 PostScript 이름이 오도록 요구합니다. 또한 한 파일 안에서
같은 폰트의 서로 다른 서브셋들이 서로 다른 태그를 사용하도록 요구합니다. 이 규칙 덕분에
리더는 두 서브셋을 구별하고 문서를 올바르게 병합할 수 있습니다. Evidence: Standard-backed
인코딩은 렌더링과 별개의 조항에서 다룹니다.
Spec: ISO 32000-2, §9.10.3 ISO 32000-2 §9.10.3 는 /ToUnicode를
문자 코드를 유니코드 값으로 매핑하는 CMap을 담은 스트림으로 정의하며,
이어지는 텍스트 추출 절차는
Spec: ISO 32000-2, §9.10.2 ISO 32000-2 §9.10.2 그 CMap을 사용하여
검색과 색인을 위해 문자 코드를 유니코드로 변환합니다. 글리프를
그리는 메커니즘의 어떤 부분도 /ToUnicode를 건드리지 않습니다 — 바로 이 때문에
텍스트가 올바르게 보이면서도 잘못 추출될 수 있습니다.
임베딩에 관해, 표준은 대부분의 폰트 딕셔너리가 폰트 디스크립터를 담고, 임베디드 폰트 파일 스트림은 선택 사항이지만 강력히 권장된다고 명시합니다. PDF 2.0은 특히 열네 개의 표준 Type 1 폰트에 대해 이를 강화합니다. NextPDF의 Strict 정책은 그 강화에 대한 규격에 맞는 해석입니다. AllowBase14는 명시적인 옵트인 방식의 하위 호환성 탈출구입니다 — 엔진은 결코 묵시적으로 다운그레이드하지 않습니다.
| Edition | Availability |
|---|---|
| Core | 사용 가능. 서브셋팅, |
| Pro | 프로파일 수준에서 폰트 임베딩에 대한 더 깊은 규격 준수 강제와 보고를 추가합니다. |
| Enterprise | 엔터프라이즈 운영 영역에서 동일한 규격 준수 강제를 추가합니다. |
실무 예시
섹션 제목: “실무 예시”다음은 올바르게 임베드되고, 서브셋되고, 검색 가능한 복합 폰트의 두 부분입니다. 서브셋 태그는 표준의 여섯 글자 규칙을 따른 것이며, /ToUnicode 참조는 텍스트를 추출 가능하게 유지하는 수단입니다.
% The Type 0 (composite) font dictionary20 0 obj<< /Type /Font /Subtype /Type0 /BaseFont /ABCDEF+NotoSans % six-letter subset tag + '+' /Encoding /Identity-H /DescendantFonts [21 0 R] /ToUnicode 23 0 R >> % the map that makes text searchableendobj
% The descendant CIDFontType2 (carries the subsetted program)21 0 obj<< /Type /Font /Subtype /CIDFontType2 /BaseFont /ABCDEF+NotoSans /CIDToGIDMap /Identity % glyph IDs preserved, not remapped /FontDescriptor 22 0 R >>endobj객체 20의 /ToUnicode 23 0 R이 검색 가능한 문서와 그 문서를 찍은 그림 사이의 차이입니다. (사전 정의된 CMap의 경우가 아니라면) 이를 빼도 모든 글리프는 여전히 완벽하게 칠해지지만, 페이지에서 어떤 단어를 검색해도 아무것도 찾지 못합니다.
흔한 오해
섹션 제목: “흔한 오해”핵심 함정을 분명히 말하면: 글리프가 올바르게 렌더링된다는 사실은 그 텍스트가 실제 텍스트인지에 대해 아무것도 말해 주지 않습니다. 렌더링은 인코딩-대-글리프 경로를 따릅니다. 검색과 복사는 코드-대-유니코드 경로(/ToUnicode)를 따릅니다. 둘은 폰트 딕셔너리의 서로 다른 부분을 읽는 서로 다른 메커니즘입니다. 그래서 문서가 흠잡을 데 없는 시각적 출력을 가지면서도 /ToUnicode가 없거나 잘못될 수 있습니다. 그 결과는 권위 있어 보이지만 기능적으로는 검색되지 않는 페이지입니다 — 정의상 눈에 보이는 문제가 없기 때문에 모든 시각적 검토를 통과하는 실패입니다.
관련된 또 다른 함정: “폰트가 임베드되어 있으니 아카이브에는 문제없다.” 임베딩은 필요하지만 충분하지는 않습니다. PDF/A 같은 프로파일은 여섯 글자 규칙에 따라 명명된 서브셋과 올바른 인코딩도 기대합니다. 임베드되어 있지만 검색되지 않는 문서는 여전히 실패입니다.
한계와 범위
섹션 제목: “한계와 범위”NextPDF의 서브셋터는 구체적으로 TrueType 서브셋터입니다. 필수 TrueType 테이블이 있어야 하며, 그것이 없거나 절감 효과가 약 10% 임계값 미만이면 원본 폰트를 변경하지 않고 반환합니다. 서브셋팅과 /ToUnicode CMap은 텍스트를 추출 가능하게 만들지만, 원본 폰트에 글리프를 의미 있는 문자로 다시 매핑할 정보가 없다면 이를 구제할 수는 없습니다. 유니코드 값을 결정할 수 없는 곳에서는, 아무리 많은 CMap을 내보내도 값을 만들어 내지 못합니다.
이 페이지는 NextPDF가 쓰는 문서에 올바른 폰트 구조를 만들어 내는 것에 관한 것입니다. 임의의 인바운드 PDF를 위한 폰트 복구 도구가 아닙니다. 그리고 규격에 맞는 서브셋과 인코딩을 내보내는 것만으로 문서가 전체 아카이브 프로파일을 충족한다고 인증되는 것은 아닙니다 — 그것은 별개의 더 넓은 검사입니다.
미니 FAQ
섹션 제목: “미니 FAQ”왜 여섯 글자 태그인가 — 왜 폰트 이름이 아닌가? 리더가 같은 폰트의 서로 다른 서브셋을 구별하고 글리프 집합을 충돌시키지 않으면서 문서를 병합할 수 있게 하기 위해서입니다. 서로 다른 서브셋에는 서로 다른 태그를 붙이는 것이 규칙입니다.
/ToUnicode가 없어도 괜찮은 경우는 언제인가? 자기 기술적 사전 정의 CJK CMap이 이미 문자-대-유니코드 매핑을 제공할 때입니다. 그 경우 CMap이 곧 인코딩입니다. 별도의 /ToUnicode는 중복될 것입니다. 그 경우를 제외하면, 그 부재 자체가 결함입니다.
서브셋팅이 해를 끼치는 경우가 있는가? 잘못 수행했을 때만 그렇습니다. 복합 글리프 구성 요소를 빼면 악센트 글리프가 망가집니다. 참조를 다시 쓰지 않고 글리프 ID를 재매핑하면 렌더링이 망가집니다. NextPDF는 구성 요소 폐쇄를 해석하고 글리프 식별자를 보존함으로써 두 문제를 모두 피합니다.
관련 문서
섹션 제목: “관련 문서”- 스트림과 필터 — 임베디드 폰트 프로그램은 자체 디코드 계약을 가진 필터링된 스트림 객체입니다.
- PDF가 실제로 무엇인가 — 폰트 딕셔너리와 프로그램 스트림이 위치하는 객체 모델.
- PDF 2.0: 무엇이 바뀌었는가 — 2.0 베이스라인의 강화된 폰트 임베딩 기대를 다룹니다.
용어집
섹션 제목: “용어집”- 임베디드 폰트 프로그램 — PDF 안에 스트림으로 담긴 실제 폰트 파일(TrueType/CFF/Type 1)로, 렌더링이 리더에 설치된 폰트에 의존하지 않게 합니다.
- 서브셋팅 — 크기를 줄이기 위해 폰트 프로그램을 문서가 사용하는 글리프만 담도록 다시 쓰는 것.
- 서브셋 태그 — 서브셋 폰트 이름에 붙는 의무적인 여섯 개의 대문자 접두사와
+(예:ABCDEF+NotoSans). /ToUnicode— 문자 코드를 유니코드 값으로 매핑하여 PDF 텍스트를 검색 가능하고, 복사 가능하며, 접근 가능하게 만드는 CMap 스트림.- 복합 글리프 — 다른 글리프를 구성 요소로 참조하여 만들어진 글리프; 서브셋팅할 때 그 구성 요소를 반드시 보존해야 합니다.
CIDToGIDMap /Identity— 콘텐츠 스트림의 글리프 인덱스가 폰트 자체의 글리프 ID 그대로인 모드; NextPDF는 이를 유효하게 유지하기 위해 글리프 식별자를 보존합니다.- Base14 — 열네 개의 표준 Type 1 폰트; PDF 2.0은 폰트가 이름으로 참조되기보다 임베드되기를 기대합니다.