PDF 속 텍스트가 사실은 텍스트가 아닌 이유
한눈에 보기
섹션 제목: “한눈에 보기”PDF를 읽을 때 당신은 단어를 봅니다. 파일은 단어를 담고 있지 않습니다. 그것은 좌표에 모양을 그리라는 명령을 담고 있고, 그 모양이 우연히 글자처럼 보일 뿐입니다. PDF가 그리는 것과 그것이 의미하는 것 사이의 간극이, 한 페이지가 흠잡을 데 없이 보이면서도 복사하면 의미 없는 문자열로 나오는 이유입니다. 이 페이지는 그 간극과, 그것을 메우는 작고 별개인 맵에 관한 것입니다.
왜 중요한가
섹션 제목: “왜 중요한가”PDF에 대해 당신이 텍스트로서 하는 모든 일 — 선택하기, 복사하기, 검색하기,
검색을 위해 색인하기, 스크린 리더로 소리 내어 읽기 — 은 파일이 직접 저장한 적이
없는 문자를 복원하는 데 의존합니다. 그 복원이 실패하면, 그 실패는 보이지
않습니다. 페이지는 여전히 렌더링됩니다. 누군가 한 단락을 이메일에 복사해
넣었다가 □□□□를 얻거나, 분명히 있는 조항을 찾으려고 400페이지짜리 계약서를
검색했는데 아무것도 나오지 않을 때까지 아무도 알아채지 못합니다.
이것은 정확히 모든 시각적 검토를 통과하기 때문에 값비싼 부류의 버그입니다. 볼 것이 아무것도 없습니다. 문서는 권위 있어 보이지만, 기계의 목적에서는 벙어리 입니다.
짧게 요약하면
섹션 제목: “짧게 요약하면”- PDF는 폰트 안으로 들어가는 숫자 코드로 선택된 시각적 모양인 글리프를 그립니다 — 유니코드 문자가 아닙니다.
- 같은 모양이 서로 다른 문자를 의미할 수 있고, 같은 문자가 서로 다른 글리프로 그려질 수 있습니다. 외관과 의미는 설계상 분리되어 있습니다.
- 텍스트를 다시 꺼내려면, 리더는 그리는 경로를 역방향으로 실행합니다: 코드에서
문자로. 폰트의
/ToUnicodeCMap이 그 역방향 단계를 위한 조회 테이블입니다 (Spec: ISO 32000-2, §9.10ISO 32000-2 §9.10). /ToUnicode가 없거나 잘못되면 복사-붙여넣기가 깨지고 검색이 실패합니다 — 그러면서도 페이지는 여전히 픽셀 단위로 완벽합니다.- 이것은 의미 문제입니다. 글리프가 올바르게 보이는지 여부는
폰트: 까다로운 부분에서 다루는 별개의
외관 문제입니다. NextPDF는 라운드트립이 충실하도록 올바른
/ToUnicode를 내보냅니다.
NextPDF의 접근 방식
섹션 제목: “NextPDF의 접근 방식”애초에 텍스트가 어떻게 페이지에 올라가는지부터 시작합시다. 콘텐츠 스트림은
“file이라는 단어를 써라”라고 말하지 않습니다. 그것은 폰트를 선택한 다음,
코드의 문자열을 텍스트 표시 연산자에 건넵니다
(Spec: ISO 32000-2, §9ISO 32000-2 §9). 각 코드는 인덱스 — 폰트
프로그램 안의 위치 — 이며, 폰트는 그 인덱스를 글리프 윤곽선으로 바꾸어 그립니다.
코드 70은 문자 F가 아닙니다. 그것은 “이 폰트의 슬롯 70”이며, 슬롯 70이
우연히 F 모양의 곡선을 담고 있을 뿐입니다. 다른 폰트를 고르면 슬롯 70은
눈송이일 수도 있습니다.
따라서 코드는 그 폰트의 인코딩에 상대적으로만 무언가를 의미합니다. 단순 폰트의 경우 그 인코딩은 대략 글리프당 1바이트입니다. 수천 개의 글리프가 필요한 문자 체계 — CJK, 또는 전체 유니코드 문서라면 무엇이든 — 의 경우에는 그것으로 공간이 부족하며, PDF는 복합(Type 0) 폰트에 손을 뻗습니다 (Spec: ISO 32000-2, §9.7ISO 32000-2 §9.7). 복합 폰트는 멀티바이트 코드를 CMap을 통해 읽어 CID(문자 식별자)로 바꾸고, CID를 글리프에 매핑합니다. 그것은 하나의 폰트가 막대한 글리프 집합을 다룰 수 있게 하는 우아한 간접 참조입니다. 그러나 그것은 또한 “무엇이 표시되는가”에서 “그것이 무엇을 의미하는가”로 가는 자취가 끊길 수 있는 또 하나의 지점이기도 합니다.
이제 그것을 거꾸로 실행해 봅시다. 텍스트를 추출하려면, 리더는 콘텐츠 스트림에서 찾은 코드를 가져다 문자를 복원해야 합니다 (Spec: ISO 32000-2, §9.10ISO 32000-2 §9.10). 글리프를 그린 인코딩은 문자에서 글리프로 갔습니다. 추출은 글리프 코드에서 문자로를 필요로 하는데, 그 방향이 역으로 뒤집을 수 있다는 보장은 없습니다. 서브셋 폰트는 자신의 글리프를 다시 번호 매겼을 수 있습니다. 복합 폰트의 CID는 사적인 것일 수 있습니다. 페이지 위의 모양은 본질적인 유니코드 의미를 지니지 않습니다.
해결책은 폰트와 함께 역방향 맵을 함께 실어 보내는 것입니다. 그 맵이 바로
/ToUnicode CMap입니다: 문서가 사용하는 각 코드에 대해, 그 코드가
나타내는 유니코드 값(들)을 기록합니다. 그것이 있으면 추출은 깔끔한 조회입니다.
그것이 없으면 리더는 폰트 인코딩과 휴리스틱으로 추측하는 수밖에 없으며 — 추측은
바로 fi가 물음표가 되는 지점입니다.
- Your charactersThe Unicode text you set, for example the word 'file'.
- Encoding → codesThe font's encoding turns characters into numeric codes; a composite font routes them through a CMap to CIDs.
- Codes → glyphsEach code selects a glyph outline, which is painted to the page. This is the part you see.
- Extraction reverses itA reader reads the codes back and looks each one up in /ToUnicode to recover Unicode.
- Text againWith a correct /ToUnicode, copy, search, indexing, and screen readers all get the original characters back.
NextPDF는 역방향 맵을 당연한 일로 기록합니다. 복합 폰트를 임베드할 때 그것은
CIDFontType2 자손, Type0 부모, 그리고 코드가 다시 유니코드로 해석되도록
/ToUnicode CMap을 내보냅니다 — 폰트: 까다로운
부분에서 설명하는 인코딩 작업입니다. 여기서
따로 짚어 둘 만한 점은 왜입니다: /ToUnicode 스트림은 페이지를 올바르게
보이게 하는 것과는 무관합니다. 글리프는 그것 없이도 이미 올바르게 보입니다.
그것은 텍스트를 추출 가능하게 유지하는 단 하나의 산물입니다.
실용 예제
섹션 제목: “실용 예제”이것이 가장 유명하게 드러나는 곳은 fi 합자입니다. 많은 폰트가 f와 i를
하나로 결합된 글리프로 그립니다. i의 점이 f의 갈고리와 충돌하기 때문입니다.
페이지 위에서 그것은 하나의 모양이며, 하나의 코드로 그려집니다. 문제는 당신이
그것을 복사할 때 무슨 일이 일어나느냐입니다.
% A /ToUnicode entry that maps the single ligature code to TWO characters,% so selecting the 'fi' glyph copies out as 'f' then 'i' — not one mystery box.1 beginbfchar<0085> <00660069> % code 0x85 -> U+0066 'f' U+0069 'i'endbfchar그 하나의 엔트리가, “file”에 대한 검색이 일치하는 것과 합자로 렌더링된 모든
출현을 검색이 소리 없이 놓치는 것의 차이입니다. 합자 코드를 두 문자 시퀀스
f + i로 매핑하면 단어는 검색 가능하고 복사 가능해집니다. 매핑하지 않은 채로
두면 페이지는 여전히 file을 완벽하게 보여 주지만, 텍스트는 기계가 읽을 수 있는
아무것도 조용히 말하지 않습니다.
이것이 “fi”가 한 문자로도 두 문자로도 추출될 수 있는 이유이며, 그 차이가 무작위
가 아닌 이유입니다 — 그것은 /ToUnicode 맵이 무엇이라고 말하도록 들었느냐일
뿐입니다. 올바른 맵은 합자를 그 문자들로 다시 분해합니다. NextPDF는 정확히 그런
종류의 엔트리를 내보내므로, 페이지 위의 합자는 클립보드 위의 두 개의 평범한
글자가 됩니다.
흔한 오해
섹션 제목: “흔한 오해”이름 붙은 함정: “텍스트가 렌더링되니까 텍스트는 괜찮다.” 렌더링과 추출은
서로 다른 데이터를 읽는 서로 다른 메커니즘입니다. 렌더링은 코드-대-글리프
경로를 따르며, 당신의 눈이 확인하는 것입니다. 추출은 /ToUnicode를 통한
코드-대-문자 경로를 따르며, 모든 기계 소비자가 확인하는 것입니다. 문서는 첫
번째를 만점으로 통과하면서도 두 번째를 완전히 실패할 수 있습니다. 구조상 그것을
드러낼 시각적인 것이 아무것도 없기 때문입니다.
두 번째의 더 미묘한 함정: 글리프가 자신이 어떤 문자인지 “당연히” 안다고 가정하는 것입니다. 그렇지 않습니다. 글리프는 인덱스를 가진 모양입니다. 문자로 되돌아가는 매핑은 생산자가 제공해야 하는 추가 정보입니다. 생산자가 그것을 결코 쓰지 않았다면, 어떤 리더도 그것을 충실하게 복원할 수 없습니다 — 추측만 할 수 있을 뿐입니다.
한계와 경계
섹션 제목: “한계와 경계”| Edition | Availability |
|---|---|
| Core | NextPDF emits a correct /ToUnicode CMap for the fonts it embeds, including the ligature and composite-font cases, so the documents it produces are searchable and copyable. |
| Pro | Not in this edition |
| Enterprise | Not in this edition |
/ToUnicode 맵은 원본 폰트가 실제로 노출하는 정보만 담을 수 있습니다. 글리프가
결정 가능한 문자를 갖지 않는 경우 — 순수하게 장식적인 표시, 유니코드 할당이 없는
사용자 정의 영역 기호 — 어떤 맵도 애초에 없던 의미를 만들어 낼 수 없습니다.
NextPDF는 자신이 도출할 수 있는 진실한 매핑을 내보냅니다. 그것은 간극을 메우려고
문자를 날조하지 않습니다.
이 페이지는 NextPDF가 작성하는 문서에 관한 것입니다. 그것은 /ToUnicode가
이미 누락되었거나 잘못된 임의의 인바운드 PDF를 위한 수리 도구가 아닙니다. 그런
것에서 텍스트를 복원하는 일은 별개의 휴리스틱 문제입니다. 그리고 충실한 추출은
접근성에 필요하지만 그 전부는 아닙니다 — 읽기 순서, 태그, 대체 텍스트는
PDF를 접근 가능하게 만드는 것에서
다룹니다.
미니 FAQ
섹션 제목: “미니 FAQ”왜 같은 PDF가 어떤 리더에서는 잘 복사되고 다른 리더에서는 형편없이
복사되나요?
서로 다른 리더는 /ToUnicode가 누락되었을 때 서로 다르게 폴백합니다. 일부는
폰트의 내장 인코딩으로 추측하여 흔한 라틴 텍스트에서 운 좋게 맞고, 일부는 그렇지
않습니다. 올바른 /ToUnicode는 그 복불복을 제거합니다 — 적합한 모든 리더가 같은
문자를 얻습니다.
이것이 폰트가 임베드되지 않은 것과 같은 것인가요?
아닙니다. 임베딩은 외관에 관한 것입니다 — 폰트가 설치되지 않아도 글리프가
그려지는지 여부. /ToUnicode는 의미에 관한 것입니다 — 코드가 문자로 다시
매핑되는지 여부. 폰트는 완벽하게 임베드되고도 쓸 만한 /ToUnicode가 전혀 없을
수 있으며, 그것이 바로 검색 불가능하지만 예쁜 실패입니다.
추출에 코드당 하나 이상의 문자가 필요한 경우가 있나요?
예 — 합자 경우가 정확히 그것입니다: 하나의 코드가 두 문자에 매핑됩니다.
/ToUnicode 엔트리는 단일 코드를 짧은 시퀀스에 매핑할 수 있으며, 그것이 합자와
일부 조합 형태가 그 구성 글자로 되돌아오는 방식입니다.
관련 문서
섹션 제목: “관련 문서”- 폰트: 까다로운 부분 — 외관 측면의 동반 글: 임베딩, 서브셋팅, 그리고 인코딩이 어떻게 구축되는지. 이 페이지는 같은 동전의 의미 측면입니다.
- PDF를 접근 가능하게 만드는 것 — 충실한 텍스트는 한 가지 재료이며, 읽기 순서, 태그, 대체 텍스트가 나머지입니다.
- PDF가 실제로 무엇인가 — 폰트,
인코딩,
/ToUnicode스트림이 들어 있는 객체 모델.
용어집
섹션 제목: “용어집”- 글리프 — 폰트 안의 시각적 모양(윤곽선). PDF가 실제로 그리는 것. 글리프는 인덱스를 갖지만 본질적인 문자 의미는 없습니다.
- 문자 — 유니코드 코드 포인트로 식별되는 문자 언어의 단위. 당신이 의미하는 것이며, 추출이 복원하려는 것.
- 코드 — 콘텐츠 스트림 안에서 현재 폰트로부터 글리프를 선택하는 숫자 값. 유니코드 문자가 아니며, 폰트에 상대적으로만 의미가 있습니다.
- CID — 큰 집합 안에서 글리프를 가리키기 위해 복합 폰트가 사용하는 문자 식별자; 코드와 글리프 사이의 중간 단계.
- 복합(Type 0) 폰트 — 멀티바이트 코드를 CMap을 통해 읽어 CID와 글리프에 도달하는 폰트로, 하나의 폰트가 수천 개의 글리프를 다뤄야 할 때 사용됩니다.
/ToUnicode— 문서가 사용하는 코드를 유니코드 값으로 다시 매핑하는 CMap 스트림; PDF 텍스트를 검색 가능, 복사 가능, 접근 가능하게 만드는 것.- 합자 — 둘 이상의 글자를 하나의 모양으로 그리는 단일 글리프(예:
fi); 그/ToUnicode엔트리는 그것을 다시 그 문자들로 분해합니다.