PDF 파일 크기의 경제학
Spec: ISO 32000-2, §7.5.7ISO 32000-2 §7.5.7
한눈에 보기
섹션 제목: “한눈에 보기”두 PDF가 화면에서 픽셀 대 픽셀로 동일하게 보이면서도 디스크에서는 열 배 차이 날 수 있습니다. 그 차이는 거의 결코 여러분이 보는 콘텐츠가 아닙니다. 그것은 파일이 그 아래에서 어떻게 조립되었는가입니다. 이 페이지는 크기 경제학 투어입니다. PDF의 바이트가 실제로 어디로 가는지, 그리고 저자가 바이트 예산을 쓰는 네 가지 레버를 다룹니다.
이것은 필터가 어떻게 디코딩하는가를 다루는 스트림과 필터의 왜 파일이 큰가 동반편입니다. 이 페이지는 예산에 머뭅니다.
이것이 중요한 이유
섹션 제목: “이것이 중요한 이유”파일 크기는 좀처럼 허영심을 채우는 지표가 아닙니다. 그것은 모든 다운로드에서의 대역폭, 모든 보관에서의 저장 공간, 그리고 모든 미리 보기에서의 지연 시간입니다. 400 KB 였어야 할 12 MB 인보이스는, 한 달에 백만 장을 생성할 때 겉치레 문제가 아닙니다 — 그것은 서른 배의 청구서입니다.
답답한 부분은 비대함이 대개 보이지 않는다는 것입니다. 문서는 올바르게 렌더링되고, 잘 열리고, 잘 인쇄됩니다. 같은 아티팩트가 그 크기의 일부일 수도 있었다는 것을 아무것도 알려 주지 않는데, 낭비된 바이트는 시각적인 것이 아니라 구조적인 것이기 때문입니다. 그것들을 찾으려면 페이지가 아니라 예산을 보아야 합니다.
짧게 요약하면
섹션 제목: “짧게 요약하면”PDF를 네 개의 항목에 쓰는 예산이라고 생각하십시오.
- 객체별 오버헤드. 모든 간접 객체는
N G obj/endobj봉투를 나르고, 하나의 상호 참조 항목으로 추적됩니다. 페이지가 많은 문서는 수천 개의 작은 객체를 가지며, 그 봉투가 쌓입니다. 객체 스트림은 객체 한 무리 전체에 대해 그 봉투를 떨어뜨립니다. 패킹된 각 객체는 여전히 자신의 상호 참조 항목을 유지하지만, 컴팩트한 type-2 항목으로서입니다. - 이미지 바이트. 사진이나 스캔본이 있는 어떤 문서에서든 이미지가 지배하며, 단 하나의 가장 큰 레버는 필터 선택입니다 — 무손실 코덱 대 손실 이미지 코덱은 메가바이트와 킬로바이트의 차이입니다.
- 폰트 바이트. 완전히 임베디드된 폰트는 여러분이 결코 쓰지 않는 수백 킬로바이트의 글리프입니다. 서브셋팅은 문서가 실제로 그리는 글리프만 유지합니다.
- 인덱스. 리더가 모든 객체를 찾게 해 주는 상호 참조 테이블은, 평문이 아니라 그 자체로 압축된 스트림일 수 있습니다.
네 가지를 모두 제대로 하면 파일은 작습니다. 하나를 놓치면 그것이 여러분이 잘한 그 밖의 모든 것을 압도합니다.
NextPDF가 이를 다루는 방식
섹션 제목: “NextPDF가 이를 다루는 방식”NextPDF의 작성기는 단일 패스 스트리밍 직렬화기입니다. 그것은 각 객체의 바이트가 생산되는 대로 덧붙이고, 각각에 대해 고전적인 사용 중 상호 참조 항목을 기록합니다. 그 기본값은 빠르고, 예측 가능하며, 바이트 안정적인 파일을 만들어 냅니다 — 하지만 그것은 가능한 가장 작은 레이아웃이 아니며, NextPDF는 그것에 대해 정직합니다.
레버 1 — 객체 스트림 (ObjStm)
섹션 제목: “레버 1 — 객체 스트림 (ObjStm)”PDF는 간접 객체의 그래프입니다. 그것들 대부분은 작은 딕셔너리입니다. 페이지 노드, 주석
딕셔너리, 구조 트리 요소, 아웃라인 항목입니다. 각각은 고정된 세금을 냅니다 — obj /
endobj 키워드, 객체 번호와 생성 번호, 그리고 그것을 위치시키는 상호 참조 항목입니다.
수천 개의 작은 객체를 가진 문서에서, 그 세금은 파일의 의미 있는 한 조각입니다.
객체 스트림은 그 작은 비스트림 객체 다수를 하나의 스트림으로 모아 함께
압축합니다(Spec: ISO 32000-2, §7.5.7ISO 32000-2 §7.5.7). obj / endobj
봉투는 무리 전체에 대해 떨어뜨려집니다. 안의 값들은 객체별 키워드 없이 등을 맞대고
저장된 다음, 단일 블록으로 deflate 됩니다 — 이는 또한 더 잘 압축되는데, 중복 제거
압축기가 이제 그 비슷한 딕셔너리들을 한꺼번에 보기 때문입니다. 상호 참조 항목은 사라지지
않습니다 — 패킹된 각 객체는 여전히 하나를 필요로 합니다 — 하지만 그것은 상호 참조
스트림 안의 컴팩트한 이진 type-2 항목으로 줄어듭니다(이에 관해서는 레버 2 에서 더
다룹니다).
NextPDF에서 이것은 ObjectStreamPacker로 전달되는데, 완성된 상호 참조 스트림 PDF를
받아 적격 객체들을 단일 /Type /ObjStm으로 다시 작성하는 독립적 후처리기입니다. 그것이
따르는 규칙은 표준에서 곧장 옵니다. 적격 객체는 생성 번호 0 의 비스트림 객체이며, 직접
주소 지정이 가능한 채로 남아야 하는 특수 객체를 제외한 후의 것입니다. §7.5.7 은 스트림
객체를 객체 스트림 안에 저장하는 것을 금지하므로, 스트림 객체 — 콘텐츠, 폰트, 이미지 —
는 자신의 항목을 유지합니다. 그리고 ObjectStreamPacker는 추가로 문서 자신의 상호 참조
스트림 객체(그것은 다시 작성됩니다)와 /Encrypt 딕셔너리를 사양하는데, 둘 다 직접 주소
지정이 가능한 채로 남아야 합니다. 그 밖의 모든 생성 번호 0 의 비스트림 객체는 패킹됩니다.
| Edition | Availability |
|---|---|
| Core | 오픈 소스 코어에서 ObjectStreamPacker를 통해 완전 지원. 그것은 옵트인입니다. 단일 패스 작성기의 기본값은 고전적인 사용 중 항목을 내보내므로, 패킹을 활성화하지 않는 한 출력은 바이트 동일하게 유지됩니다. 패커는 결정적이며, 자신의 재현 가능한 골든 베이스라인을 가집니다. |
| Pro | Not in this edition |
| Enterprise | Not in this edition |
옵트인은 숨어 있는 한계가 아니라 의도적인 자세입니다. 기본 출력은 바이트 안정적이고 기존 골든 베이스라인과 일치합니다. 패킹을 켜는 것은 다르고, 더 작으며, 똑같이 결정적인 레이아웃을 선택하는 것입니다. 여러분이 그 거래를 고르며, 엔진은 결코 그것을 여러분 등 뒤에서 하지 않습니다.
레버 2 — 인덱스도 스트림일 수 있다
섹션 제목: “레버 2 — 인덱스도 스트림일 수 있다”일단 객체가 객체 스트림 안에 살면, 그것을 가리키는 인덱스가 모양을 바꿉니다. 리더는
패킹된 객체를 압축된 상호 참조 항목 — 객체 스트림과 그 안의 인덱스를 명명하는 type-2
항목 — 을 통해 찾습니다(Spec: ISO 32000-2, §7.5.8.3ISO 32000-2 §7.5.8.3). 상호
참조 전체가 그 자체로 /Type /XRef 스트림이기 때문에, 수천 개 객체를 위한 인덱스는
평문 행으로 쓰이는 대신 이진으로 패킹되고 deflate 됩니다. 지도는 영토와 나란히
줄어듭니다.
ObjectStreamPacker는 정확히 이것을 다시 빌드합니다. 그것은 단일 객체 스트림을 내보낸
다음, 유지된 각 객체에 대한 컴팩트한 type-1 항목과 패킹된 각 객체에 대한 type-2 항목을
나르는, 다시 작성된 상호 참조 스트림을 내보내며, 기존 참조가 유효하게 유지되도록 모든
객체 번호를 보존합니다.
레버 3 — 이미지 필터 선택
섹션 제목: “레버 3 — 이미지 필터 선택”진짜 이미지가 있는 어떤 문서에서든, 이 레버가 나머지를 압도합니다. 바이트는 같은 그림이고, 코덱이 예산입니다. 필터 이름은 표준 필터 집합에 삽니다(Spec: ISO 32000-2, §7.4ISO 32000-2 §7.4), 그리고 그것들 사이의 선택은 크기 결정입니다.
- FlateDecode는 무손실입니다. 선화, 스크린샷, 그리고 평평한 색을 가진 어떤 것에든 완벽합니다 — 그리고 사진에는 파멸적인데, 거기서 무손실은 원본의 모든 바이트를 뜻하기 때문입니다.
- DCTDecode는 JPEG 입니다. 손실적이며, 사진에는 넓은 차이로 옳은 선택이고, 아무도 알아차리지 못하는 품질 하락의 대가로 흔히 열 배의 감소입니다.
- JPXDecode는 JPEG 2000 입니다. 다른 품질/크기 곡선을 가진 웨이블릿 압축이지만, 지원이 고르지 않고 일부 보관 프로파일에서는 허용되지 않습니다.
이것이 바로 필터가 어떻게 디코딩하는가가 중요한 지점이며, 그것은 스트림과 필터 글의 일입니다. 경제학의 요점은 더 좁습니다. 무손실로 저장된 사진은 불필요하게 거대한 PDF의 가장 흔한 단일 원인이며, 어떤 양의 객체 스트림 패킹도 진짜 무게가 JPEG 처리되지 않은 스캔본 하나인 파일을 구하지 못합니다.
레버 4 — 폰트 서브셋팅
섹션 제목: “레버 4 — 폰트 서브셋팅”임베디드 폰트는 하나의 프로그램입니다. 완전한 것은 수백 킬로바이트일 수 있는데, 그것이 서체 디자이너가 그린 모든 글리프 — 이 문서에서 결코 쓰지 않을 스크립트에 걸친 수천 개의 문자 — 를 나르기 때문입니다. 서브셋팅은 문서가 실제로 그리는 글리프만 임베드하여, 그 프로그램을 그 자신의 작은 일부로 바꿉니다. 한 페이지짜리 편지는 CJK 가능 폰트의 전체를 필요로 하지 않습니다. 그것은 자신이 조판하는 몇십 개의 글리프를 필요로 합니다. 글리프가 어떻게 선택되고 다시 인덱싱되는지의 메커니즘은 그 자체로 별개의 주제입니다 — 폰트: 어려운 부분을 보십시오.
- Image bytesFor media-heavy files, the largest line item. The filter choice — lossless Flate vs a lossy image codec like DCT (or JPX in a lossy mode) — is the dominant size lever.
- Font bytesA full embedded font is mostly glyphs you never draw. Subsetting keeps only the used glyphs, often shrinking the program by an order of magnitude.
- Per-object overheadThousands of small dictionaries each pay an obj/endobj envelope plus a cross-reference entry. Object streams (ObjStm) drop the envelope for the whole group; each object keeps a compact type-2 cross-reference entry.
- The indexA compressed cross-reference stream with type-2 entries binary-packs and deflates the map of every object, replacing plaintext index rows.
실제 예시
섹션 제목: “실제 예시”여기서 보여 줄 꾸며 낸 API는 없는데, 가장 중요한 크기 결정은 바이트가 작성기에 닿기 전에 내려지기 때문입니다 — 그리고 NextPDF가 노출하는 단 하나의 구조적 레버는 단일 옵트인입니다. 개념적으로, 예산은 이렇게 읽힙니다.
- 사진을 JPEG로 공급하여 무손실로 다시 인코딩되는 대신
DCTDecode아래에 떨어지게 하십시오. 가장 큰 승리는 작성기 플래그가 아니라 소스 데이터에 관한 선택입니다. - 작성기가 임베디드 폰트를 서브셋팅하게 하여 그려지는 글리프만 실리게 하십시오.
- 작은 객체가 많은 문서의 경우, 객체 스트림 패킹을 옵트인하면, 완성된 파일을
ObjectStreamPacker를 통해 보내 작은 비스트림 객체를 묶고 상호 참조 스트림을 다시 작성합니다.
기본 경로 — 고전적인 사용 중 항목, ObjStm 없음 — 는 올바른 베이스라인입니다. 결정적이고, 바이트 안정적이며, 검증하기 쉽습니다. 패킹은 이미지 무게가 아니라 객체 수가 파일을 부풀리고 있을 때의 숙고된 업그레이드입니다.
흔한 오해
섹션 제목: “흔한 오해”함정은 “PDF 압축” 버튼에 손을 뻗어 그것이 모든 것을 고쳐 주기를 기대하는 것입니다.
압축은 하나의 레버가 아닙니다. 그것은 넷이며, 서로를 대체하지 않습니다. 객체 스트림
패킹은 사진을 줄일 수 없습니다 — 그것은 이미지 필터의 일입니다. 완벽한 JPEG는 여러분이
서브셋하기를 잊은 폰트를 상쇄할 수 없습니다. 그리고 진짜 비대함이, 아무도 그것을 위해
DCTDecode를 고르지 않아서 무손실로 저장된 10 MB 스캔본이라면, 그 어느 것도
도움이 되지 않습니다.
두 번째 오해는 더 작은 것이 항상 엄격히 더 낫다는 것입니다. 그렇지 않습니다. 객체 스트림은 라이너라이제이션과 호환되지 않습니다 — 첫 페이지가 일찍 스트리밍되도록 절대적 객체 배치를 고정하는 fast-web-view 레이아웃 말입니다. 그리고 일부 보관 프로파일은 어떤 이미지 필터가 허용되는지조차 제한합니다. 크기는 하나의 축입니다. 그것은 스트리밍, 보관 적합성, 그리고 재현성과 맞바꾸어지며, 곡선 위의 올바른 지점은 문서가 무엇을 위한 것인지에 달려 있습니다.
한계와 경계
섹션 제목: “한계와 경계”네 가지 레버는 파일 크기의 구조적 경제학입니다. 그것들은 보편적인 “더 작게 만들기” 보장이 아니며, NextPDF는 임의의 인바운드 PDF를 위한 재최적화기인 척하지 않습니다.
ObjectStreamPacker는 결코 정확성을 위태롭게 하지 않는 최선의 노력 최적화입니다.
그것은 — 입력을 변경하지 않은 채 반환하며 — 사양합니다. 파일이 상호 참조 스트림 PDF가
아닐 때, 그것이 암호화되었을 때, 그것이 디지털 서명을 나를 때(객체를 다시 배치하면
서명이 보호하는 바이트 범위가 어긋납니다), 그것이 이미 객체 스트림을 담고 있을 때, 또는
패킹할 적격 객체가 없을 때입니다. 그것은 문서 전체에 대해 정확히 하나의 객체 스트림을
내보냅니다. 그것은 /Extends 컬렉션으로 분할하지 않으며, 이는 NextPDF가 만들어 내는
문서 크기에 대해 범위 밖이고 적합합니다.
이미지와 폰트 레버는 대체로 입력에 관한 결정입니다. NextPDF의 작성기는 무손실 비트맵을
손실 이미지 스트림으로 암묵적으로 트랜스코딩하지 않으며 — 사진을 DCTDecode나
JPXDecode로 다시 인코딩하는 것은 직렬화기가 여러분 등 뒤에서 하는 일이 아니라 명시적인
상류 이미지 인코딩 결정입니다 — 호출자가 완전히 임베드하라고 요청한 폰트에서 글리프를
회수하지도 않습니다. 가장 큰 크기 승리는 바이트 직렬화기의 상류에서 내려집니다. 엔진의
일은 여러분이 그것에 가져오는 예산을 낭비하지 않는 것입니다.
관련 문서
섹션 제목: “관련 문서”- 스트림과 필터 — 필터가 어떻게 디코딩하는가 동반편. 이 페이지는 의도적으로 그것을 보완하지, 중복하지 않습니다.
- PDF란 실제로 무엇인가 — 객체 스트림 레버가 그 객체별 오버헤드를 줄이는 간접 객체 모델.
- PDF 파일의 해부 — 압축된 스트림이 되는 상호 참조 구조.
- 폰트: 어려운 부분 — 서브셋팅이 문서가 그리는 글리프를 어떻게 선택하고 다시 인덱싱하는지.
용어집
섹션 제목: “용어집”- 객체 스트림(object stream, ObjStm) — 많은 작은 비스트림 간접 객체를 함께 압축하여
담는 단일 스트림으로,
obj/endobj봉투가 무리에 대해 떨어뜨려집니다. 패킹된 각 객체는 여전히 자신의 상호 참조 항목을 컴팩트한 type-2 항목으로 유지합니다. NextPDF에서 옵트인입니다. - 간접 객체(indirect object) — PDF 그래프 안의 번호 매겨진 객체로,
obj/endobj봉투로 감싸여 상호 참조 인덱스로 추적됩니다. 그 봉투가 객체별 오버헤드입니다. - 상호 참조 스트림(cross-reference stream) — 모든 객체를 인덱싱하는
/Type /XRef스트림으로, 평문 행으로 쓰이는 대신 이진으로 패킹되고 deflate 됩니다. - 압축된(type-2) 항목 — 객체 스트림 안에 사는 객체를 가리키는 상호 참조 항목으로, 그 스트림과 그 안의 인덱스를 명명합니다.
- 폰트 서브셋팅(font subsetting) — 전체 서체가 아니라 문서가 실제로 그리는 글리프만 임베드하여, 임베디드 폰트 프로그램을 줄이는 것입니다.
- 무손실 대 손실 필터 — 무손실 코덱(FlateDecode)은 모든 바이트를 재현합니다. 손실 이미지 코덱(DCTDecode, 또는 손실 모드의 JPXDecode)은 훨씬 더 작은 결과를 위해 지각되지 않는 세부를 버립니다. (JPEG 2000 / JPX 는 무손실로도 구성될 수 있습니다.) 그 선택은 미디어가 많은 파일에서 지배적인 레버입니다.