기술 문서

PDF 선형화와 Fast Web View 작동 원리

80MB 스캔 보고서를 링크 뒤에 놓고 브라우저에서 열어 무슨 일이 벌어지는지 보십시오. 뷰어는 그 바이트의 상당 부분이 도착할 때까지 빈 창에 앉아 있다가 첫 쪽을 한꺼번에 그립니다. 40쪽으로 건너뛰면, 잘못 지어진 파일에서는 내려받기 전체가 다시 시작될 수도 있습니다. 답답한 대목은 독자가 애초에 원한 것이 첫 쪽뿐이었다는 점입니다. 선형화는 그 문제에 대한 구조적 답입니다. PDF를 재배열해 뷰어가 파일의 작은 앞부분만으로 첫 쪽을 그리고 나머지는 필요할 때 가져오게 하는데, 그래서 Adobe가 이 기능을 "Fast Web View"라는 이름으로 내세웁니다

이 중 어느 것도 다른 파일 형식이 아닙니다. 선형화된 PDF는 규격을 따르는 리더가 특별한 처리 없이 여는 평범한 PDF입니다. 요령은 전적으로 바이트를 어떤 순서로 놓았는지와, 파일이 지니고 다니는 추가 구조 둘에 있습니다. ISO 32000-1이 부속서 F에서 그 배치 전체를 규정하며, 그 배치를 한 번 보고 나면 이 동작은 마법으로 보이기를 그치고 파일 순서를 첫 그림 지연 시간과 맞바꾼 의도적 거래로 보이기 시작합니다

선형화가 실제로 재배열하는 것

보통의 PDF는 객체를 거의 어떤 순서로든 흩뿌릴 수 있습니다. 파일 끝의 상호 참조 표가 그것을 가능하게 합니다. 리더는 끝으로 탐색해 startxref 포인터를 읽고 xref를 적재한 뒤, 거기서부터 모든 객체를 오프셋으로 찾아냅니다. 그 설계는 끝으로 탐색하는 데 아무 값도 들지 않는 로컬 파일에는 훌륭하고, 끝이 정확히 가장 늦게 도착하는 부분인 네트워크 스트리밍 파일에는 형편없습니다. 첫 쪽을 그리려면 전통적인 리더에게 쪽 객체, 그 내용 스트림, 그것이 참조하는 글꼴, 그것이 그리는 모든 이미지가 필요한데, 순서 없는 파일에서 그것들은 마지막 메가바이트를 포함해 어디에든 앉아 있을 수 있습니다

선형화는 그 순서를 바로잡습니다. 첫 쪽을 표시하는 데 필요한 객체들이 작은 머리 구역 바로 뒤, 앞쪽 가까이의 이어진 덩어리로 모이므로 바이트 스트림에서 일찍 도착합니다. 나머지 전부, 곧 남은 쪽들과 그들이 나눠 쓰는 자원은 예측 가능한 차례로 뒤따릅니다. 이 최적화를 무시하는 리더를 위해 두 번째의 완전한 상호 참조 표가 여전히 끝에 살지만, 선형화된 파일은 첫 쪽 상호 참조와 스트리밍 리더가 필요로 하는 매개변수도 앞쪽에 놓습니다. 리더는 무엇이든 그리기 전에 꼬리까지 닿아야 할 필요가 더는 없습니다

PDF: 보통의 PDF와 fast web view를 가능하게 하는 선형화된 PDF의 바이트 배치를 나란히 놓은 그림
선형화된 파일은 선형화 사전과 첫 쪽 xref와 힌트 스트림을 다른 무엇보다 앞으로 옮기고, 보통의 PDF는 유일한 색인을 파일 꼬리에 둡니다

첫 쪽 객체 묶음과 선형화 매개변수 사전

선형화된 파일에서 %PDF 머리 뒤의 맨 처음 객체는 선형화 매개변수 사전입니다. 스트리밍 리더가 이 최적화가 있는지, 그리고 그것을 어떻게 쓸지 정하려고 찾는 것이 바로 이것입니다. 이 사전은 파일 전체의 길이, 주 상호 참조 구역이 시작되는 바이트 오프셋, 첫 쪽의 객체 번호, 그리고 뒤따르는 힌트 스트림의 위치와 길이를 기록합니다. 그 숫자들이 있으면 리더는 첫 킬로바이트만으로도 첫 쪽을 보이려면 얼마를 가져와야 하는지, 다른 곳으로 건너뛰게 해 주는 색인을 어디서 찾아야 하는지 압니다

부속서 F는 여기서 "첫 쪽"이 무엇을 뜻하는지에 엄격합니다. 첫 쪽 구역은 쪽 객체 자체와 그 내용 스트림, 그리고 그 스트림들이 참조하는 자원을 담아야 하며, 그래야 그 앞부분이 내려받히고 나면 그 쪽이 자족적입니다. 공유 자원, 곧 모든 쪽에 쓰이는 글꼴이나 머리글에 되풀이되는 로고 같은 것은 특별하게 다뤄집니다. 첫 쪽을 섬길 만큼 일찍 나타나되 공유된 것으로 표시되어, 나중에 30쪽을 그릴 때 리더가 그것을 다시 가져오지 않습니다. 쪽 전용 객체와 공유 객체를 가르는 그 구분이야말로 손수 만든 "최적화기" 대부분이 틀리는 대목이고, 그것을 틀리는 것이 선형화되었다고 주장하면서도 여전히 멈칫거리는 파일을 만들어 냅니다

힌트 스트림: 쪽 건너뛰기를 값싸게 만드는 색인

첫 쪽을 빨리 보여 주는 것은 값어치의 절반일 뿐입니다. 나머지 절반은 중간의 모든 것을 내려받지 않고 임의의 쪽으로 건너뛰는 것이고, 그것을 제공하는 것이 힌트 스트림입니다. 선형화된 파일은 쪽 오프셋 힌트 표와 공유 객체 힌트 표를 지니는데, 매개변수 사전에서 참조되는 스트림으로 저장됩니다. 쪽 오프셋 표는 쪽마다 그 객체가 파일 어디에서 시작하고 얼마나 이어지는지 기록합니다. 공유 객체 표는 여러 쪽에 걸쳐 쓰이는 자원에 대해 같은 일을 합니다

그 표들이 있으면 40쪽을 원하는 리더는 파일을 순차적으로 구문 분석하지 않습니다. 힌트 표를 참조해 40쪽이 차지하는 바이트 범위를 알아내고, 서버에 정확히 그 범위를 요청하고, 그 바이트가 도착하면 그 쪽을 그리며, 아직 가지고 있지 않은 공유 자원도 같은 방식으로 당겨 옵니다. 힌트 스트림은 사실상 문서 위에 덮인 임의 접근 지도이고, 잘 선형화된 500쪽 파일이 느린 회선에서도 반응이 좋게 느껴지는 반면 같은 크기의 최적화되지 않은 파일은 그렇지 않은 이유입니다

서버가 협조해야 하는 이유

선형화는 전송이 파일의 임의 조각을 전달할 수 있다고 가정하며, 결과가 나쁠 때 형식을 탓하기 전에 그 가정을 확인해 볼 값어치가 있습니다. 그 장치는 HTTP 바이트 서빙입니다. 리더가 범위 요청을 보내고 서버가 206 Partial Content 응답으로 답합니다. 서버가 Accept-Ranges: bytes를 알리지 않거나, 그 앞의 프록시나 CDN이 범위 요청을 전체 전송으로 뭉개 버리면, 리더는 40쪽만 따로 가져올 방법이 없어 파일 전체 내려받기로 물러납니다. 그러면 PDF 안의 구조는 완벽하게 옳으면서도 통째로 낭비됩니다

이것이 "선형화가 안 통한다"로 가장 자주 오진되는 실패입니다. 파일은 멀쩡하고 전달 경로가 아닙니다. 문서를 다시 짓기 전에, 리더가 닿는 URL에 대해 호스트가 실제로 부분 내용을 돌려주는지 조건부 요청으로 확인하십시오. 많은 정적 호스트가 기본으로 그렇게 하고, 잘못 구성된 애플리케이션 서버와 캐시 계층은 많이들 그러지 않습니다

PDF: PDF 선형화 힌트 표가 HTTP 범위 요청을 이끌어 한 쪽에 대해 206 Partial Content를 돌려받는 그림
힌트 표는 리더에게 어느 쪽이든 그 바이트 범위를 주므로, 범위를 지원하는 호스트는 부분 응답 하나로 40쪽을 섬깁니다. 바이트 서빙이 없으면 파일 전체가 다시 내려받힙니다

증분 갱신은 선형화를 조용히 깨뜨립니다

선형화된 파일을 제대로 만들어 놓고도 왜 그 최적화가 증발했는지 의아해하는 사람들을 놀라게 하는 제약이 여기 있습니다. 선형화는 색인을 앞에 둔, 정성껏 배열된 단일 배치에 기댑니다. 증분 갱신은 설계상 그것을 어깁니다. 도구가 서명을 더하거나 양식 필드를 채우거나 증분 저장으로 주석을 덧붙일 때, 그 도구는 파일을 다시 쓰지 않습니다. 바뀐 객체와 새 상호 참조 구역과 새 트레일러를 끝에 덧붙이고, 원래 바이트는 손대지 않은 채 남깁니다. 그 덧붙이기가 증분 갱신의 요점 전부입니다. 빠르고, 감사나 서명 검증을 위해 앞선 개정판을 보존합니다

부작용은 이제 파일이 정성껏 놓인 첫 쪽 덩어리 뒤인 꼬리에 가장 새로운 상호 참조 데이터를 두게 되고, 앞쪽의 선형화 매개변수 사전은 더는 파일과 맞지 않는 배치를 설명한다는 것입니다. 규격을 따르는 리더는 그 어긋남을 알아채고 이 문서를 평범한 비선형화 PDF로 다룹니다. 원래의 선형화 구조가 여전히 파일 앞 절반에 앉아 있는데도 Fast Web View는 사라집니다. 갱신을 여러 번 덧붙이면 그때마다 개정판이 끝에 하나씩 쌓이고, 낡은 앞쪽 색인과 실제 상태 사이의 틈이 벌어집니다

PDF: 증분 갱신이 개정판을 덧붙여 선형화 사전과 어긋나게 만들다가 마지막 전면 재작성 한 번이 fast web view를 되살리는 그림
증분 저장은 낡은 앞쪽 사전 너머로 개정판을 하나씩 쌓습니다. 전면 재작성으로 마무리하고 선형화를 맨 마지막에 다시 하면 Fast Web View가 온전히 남습니다

여러분의 작업 흐름에 편집과 Fast Web View가 둘 다 필요하다면, 규칙은 구조에서 곧장 따라 나옵니다. 문서가 계속 바뀌는 동안에는 증분으로 편집하고, 끝에 한 번 다시 선형화하십시오. 배치를 되살리는 것은 전면 재작성입니다. HotPDF 용어로는, 진행 중 편집은 델타를 덧붙이는 BeginIncrementalUpdate와 SaveIncrementalUpdate를 거치고, 마무리 단계는 문서 전체를 적재해 새로 직렬화하는 LoadFromFile 뒤의 SaveLoadedDocument로 쌓인 옛 개정판을 떨구고 말끔한 배치 하나를 내놓는다는 뜻입니다. 같은 거래가 객체 스트림에도 나타납니다. UseObjectStreams를 UseXRefStream과 함께 켜면 상호 참조가 압축되고 객체가 빽빽하게 담겨 파일 크기에 도움이 되지만, 다른 어떤 구조적 선택과 마찬가지로 덧붙인 개정판에 볼트로 죄는 것이 아니라 그 마지막 재작성 중에 적용해야 합니다

// 진행 중 편집: 델타를 덧붙이고 앞선 개정판을 온전히 둡니다.
// 이러면 파일은 선형화되지 않은 채로 남습니다.
Pdf.BeginIncrementalUpdate('report.pdf');
Pdf.AddPage;
Pdf.CurrentPage.TextOut(72, 760, 0, 'Addendum');
Pdf.SaveIncrementalUpdate('report.pdf');

// 마무리 단계: 전면 재직렬화가 쌓인 개정판을 떨구고
// 말끔한 배치 하나를 만듭니다. 그 출력에 선형화기를 다시 돌리십시오.
Pdf.LoadFromFile('report.pdf');
Pdf.SaveLoadedDocument('report-final.pdf');

HotPDF는 한 번에 끝내는 "선형화" 루틴을 드러내지 않으므로, 실용적인 방식은 말끔하게 전부 다시 쓴 파일을 만들고 그 위에 전용 최적화기를 돌리는 것입니다. 명령줄 도구가 재배열을 직접 처리합니다. qpdf는 플래그 하나로 파일을 선형화된 형태로 다시 씁니다:

qpdf --linearize report-final.pdf report-web.pdf

파일이 선형화되었는지 알아보는 법

파일 이름이나 그것을 만들었다고 주장하는 도구를 믿지 말고 바이트를 확인하십시오. 가장 곧바른 검사는 파일 머리입니다. 파일을 열어 머리 뒤의 첫 객체로 /Linearized 키를 지닌 선형화 매개변수 사전이 있는지 보십시오. 리더 쪽의 지름길은 Acrobat의 문서 속성 대화 상자인데, 구조가 정말로 있고 최신일 때만 "Fast Web View: Yes"를 보고합니다

스크립트로 검사한다면 qpdf가 구조의 존재와 무결성을 함께 보고합니다. 파일이 자기 배치를 더는 반영하지 않는 선형화 사전을 지닐 수 있고, 그것이 바로 증분 갱신이 남기는 상태이므로 이 점이 중요합니다:

# "File is linearized"를 보고하고 layout에 맞춰 hint table을 검증
qpdf --check report-web.pdf

# linearization parameter와 hint data를 자세히 dump
qpdf --show-linearization report-web.pdf

제값을 하는 것은 검증 단계입니다. 사전이 있는지만 확인하는 통과는 색인이 엉뚱한 오프셋을 가리키는 파일도 기꺼이 축복해 줍니다. 힌트 표를 실제 객체 위치와 맞춰 보는 검사라야 그 최적화가 진짜 리더의 범위 요청 아래에서 버티리라고 알려 줍니다

선형화는 웹으로 제공되는 큰 문서라면 어디에나 적용할 값어치가 있고, 특히 고르지 않은 연결의 모바일 리더에 그렇습니다. 앞으로 실은 색인에 파일 크기의 몇 퍼센트가 듭니다. 헷갈리지 말아야 할 두 가지는, PDF 안의 구조와 그 바깥의 바이트 서빙이 둘 다 옳아야 한다는 것, 그리고 나중의 어떤 편집이든 파일을 다시 쓸 때까지는 그 최적화를 되돌린다는 것입니다. 다시 선형화하는 일은 다른 모든 변경이 끝난 뒤 파이프라인의 마지막 단계로 다루십시오. 여기서 설명한 상호 참조와 객체 스트림과 증분 갱신 동작은 델파이와 C++Builder를 위한 HotPDF Delphi Component가 구현하는 구조 모델의 일부입니다. 더 넓은 파일 배치 배경은 PDF가 어떻게 구조화되는가를, 코드로 보는 증분 갱신과 대용량 파일 작업 흐름은 델파이에서 대용량 PDF 처리하기를 보십시오