Ekonomia rozmiaru pliku PDF
Spec: ISO 32000-2, §7.5.7ISO 32000-2 §7.5.7
W skrócie
Dział zatytułowany „W skrócie”Dwa pliki PDF mogą wyglądać na ekranie identycznie piksel po pikselu, a różnić się na dysku dziesięciokrotnie. Różnicą prawie nigdy nie jest treść, którą widzisz; jest nią to, jak plik został złożony pod spodem. Ta strona to przewodnik po ekonomii rozmiaru: dokąd naprawdę idą bajty PDF oraz cztery dźwignie, na które autor wydaje budżet bajtów.
To towarzysz dlaczego pliki są duże dla Strumieni i filtrów, które omawiają, jak filtr dekoduje. Ta strona trzyma się budżetu.
Dlaczego to ma znaczenie
Dział zatytułowany „Dlaczego to ma znaczenie”Rozmiar pliku rzadko jest metryką próżności. To przepustowość przy każdym pobraniu, magazyn przy każdym archiwum oraz opóźnienie przy każdym podglądzie. Faktura 12 MB, która powinna mieć 400 KB, nie jest problemem kosmetycznym, gdy generujesz milion takich miesięcznie — to trzydziestokrotny rachunek.
Frustrujące jest to, że przerost jest zwykle niewidoczny. Dokument renderuje się poprawnie, otwiera się dobrze, drukuje dobrze. Nic ci nie mówi, że ten sam artefakt mógł być ułamkiem rozmiaru, ponieważ zmarnowane bajty są strukturalne, a nie wizualne. Aby je znaleźć, musisz spojrzeć na budżet, a nie na stronę.
Wersja skrócona
Dział zatytułowany „Wersja skrócona”Pomyśl o PDF jako o budżecie, który wydajesz na cztery pozycje.
- Narzut na obiekt. Każdy obiekt pośredni niesie obwiednię
N G obj/endobji jest śledzony przez jeden wpis odwołań krzyżowych. Dokument bogaty w strony ma tysiące maleńkich obiektów, a obwiednie się sumują. Strumienie obiektów zrzucają tę obwiednię dla całej grupy obiektów; każdy upakowany obiekt wciąż zachowuje własny wpis odwołań krzyżowych, ale jako zwięzły wpis typu 2. - Bajty obrazu. Dla każdego dokumentu ze zdjęciami lub skanami obrazy dominują, a największą pojedynczą dźwignią jest wybór filtra — bezstratny kodek kontra stratny kodek obrazu to różnica między megabajtami a kilobajtami.
- Bajty czcionki. Pełna osadzona czcionka to setki kilobajtów glifów, których nigdy nie używasz. Tworzenie podzbioru zachowuje tylko glify, które dokument faktycznie rysuje.
- Indeks. Tablica odwołań krzyżowych, która pozwala czytnikowi odnaleźć każdy obiekt, sama może być skompresowanym strumieniem, a nie zwykłym tekstem.
Zrób wszystkie cztery dobrze, a plik jest mały. Przegap jeden, a zdominuje wszystko inne, co zrobiłeś dobrze.
Jak podchodzi do tego NextPDF
Dział zatytułowany „Jak podchodzi do tego NextPDF”Komponent zapisujący NextPDF to jednoprzebiegowy serializator strumieniowy: dołącza bajty każdego obiektu w miarę ich wytwarzania i zapisuje klasyczny wpis odwołań krzyżowych „w użyciu” dla każdego. To ustawienie domyślne jest szybkie, przewidywalne i wytwarza plik stabilny bajtowo — ale nie jest najmniejszym możliwym układem, a NextPDF jest co do tego uczciwy.
Dźwignia 1 — strumienie obiektów (ObjStm)
Dział zatytułowany „Dźwignia 1 — strumienie obiektów (ObjStm)”PDF to graf obiektów pośrednich. Większość z nich to małe słowniki: węzły stron,
słowniki adnotacji, elementy drzewa struktury, wpisy konspektu. Każdy płaci stały
podatek — słowa kluczowe obj / endobj, numery obiektu i generacji oraz wpis
odwołań krzyżowych, który go lokalizuje. W dokumencie z tysiącami małych obiektów
ten podatek to znacząca część pliku.
Strumień obiektów zbiera wiele z tych małych obiektów niebędących strumieniami
w jeden strumień i kompresuje je razem
(Spec: ISO 32000-2, §7.5.7ISO 32000-2 §7.5.7). Obwiednia obj / endobj
jest zrzucana dla całej grupy; wartości w środku są przechowywane jedna za drugą
bez słów kluczowych dla pojedynczego obiektu, a następnie deflowane jako
pojedynczy blok — co także kompresuje się lepiej, ponieważ deduplikujący
kompresor widzi teraz wszystkie te podobne słowniki naraz. Wpis odwołań
krzyżowych nie znika — każdy upakowany obiekt wciąż go potrzebuje — ale kurczy
się do zwięzłego binarnego wpisu typu 2 w strumieniu odwołań krzyżowych (więcej o
tym w Dźwigni 2).
W NextPDF dostarcza to ObjectStreamPacker, samodzielny postprocesor, który
bierze gotowy PDF ze strumieniem odwołań krzyżowych i przepisuje kwalifikujące
się obiekty w jeden /Type /ObjStm. Reguły, którymi się kieruje, pochodzą wprost
ze standardu: kwalifikującym się obiektem jest obiekt o generacji zero, niebędący
strumieniem, po wykluczeniu obiektów specjalnych, które muszą pozostać
bezpośrednio adresowalne. §7.5.7 zabrania przechowywania obiektu strumienia
wewnątrz strumienia obiektów, więc obiekty strumieni — treść, czcionki, obrazy —
zachowują własne wpisy; a ObjectStreamPacker dodatkowo odmawia własnemu
obiektowi strumienia odwołań krzyżowych dokumentu (jest on przepisywany) oraz
słownikowi /Encrypt, które oba muszą pozostać bezpośrednio adresowalne. Wszystko
inne o generacji zero i niebędące strumieniem jest upakowane.
| Edition | Availability |
|---|---|
| Core | Full support in the open-source core via ObjectStreamPacker. It is opt-in: the single-pass writer’s default emits classic in-use entries, so output stays byte-identical unless you enable packing. The packer is deterministic, with its own reproducible golden baseline. |
| Pro | Not in this edition |
| Enterprise | Not in this edition |
Włączanie opcjonalne to celowa postawa, a nie ukryte ograniczenie. Domyślne wyjście jest stabilne bajtowo i pasuje do istniejących baz wzorcowych; włączenie pakowania wybiera inny, mniejszy, równie deterministyczny układ. Wybierasz kompromis, a silnik nigdy nie podejmuje go za twoimi plecami.
Dźwignia 2 — indeks też może być strumieniem
Dział zatytułowany „Dźwignia 2 — indeks też może być strumieniem”Gdy obiekty żyją wewnątrz strumienia obiektów, indeks, który na nie wskazuje,
zmienia kształt. Czytnik odnajduje upakowany obiekt przez skompresowany wpis
odwołań krzyżowych — wpis typu 2, który nazywa strumień obiektów i indeks w jego
obrębie (Spec: ISO 32000-2, §7.5.8.3ISO 32000-2 §7.5.8.3). Ponieważ całe
odwołania krzyżowe są same strumieniem /Type /XRef, indeks dla tysięcy obiektów
jest pakowany binarnie i deflowany, a nie zapisywany jako wiersze zwykłego
tekstu. Mapa kurczy się wraz z terytorium.
ObjectStreamPacker odbudowuje dokładnie to: emituje pojedynczy strumień
obiektów, a następnie przepisany strumień odwołań krzyżowych niosący zwięzły wpis
typu 1 dla każdego zachowanego obiektu oraz wpis typu 2 dla każdego upakowanego,
zachowując każdy numer obiektu, by istniejące odwołania pozostały ważne.
Dźwignia 3 — wybór filtra obrazu
Dział zatytułowany „Dźwignia 3 — wybór filtra obrazu”Dla każdego dokumentu z prawdziwymi obrazami ta dźwignia przyćmiewa pozostałe. Bajty to ten sam obraz; kodek to budżet. Nazwy filtrów żyją w standardowym zestawie filtrów (Spec: ISO 32000-2, §7.4ISO 32000-2 §7.4), a wybór między nimi to decyzja o rozmiarze:
- FlateDecode jest bezstratny. Idealny dla grafiki liniowej, zrzutów ekranu i wszystkiego z płaskim kolorem — i zgubny dla zdjęcia, gdzie bezstratny oznacza każdy bajt oryginału.
- DCTDecode to JPEG: stratny i dla zdjęć właściwy wybór z dużym zapasem, często dziesięciokrotna redukcja przy spadku jakości, którego nikt nie zauważa.
- JPXDecode to JPEG 2000: kompresja falkowa o innej krzywej jakość/rozmiar, ale nierównym wsparciu i zakazana przez niektóre profile archiwalne.
To dokładnie tam, gdzie jak filtr dekoduje ma znaczenie, a to zadanie artykułu Strumienie i filtry. Punkt ekonomiczny jest węższy: zdjęcie przechowywane bezstratnie to najczęstsza pojedyncza przyczyna niepotrzebnie ogromnego PDF, a żadne pakowanie strumieni obiektów nie uratuje pliku, którego prawdziwym ciężarem jest jeden nieskonwertowany na JPEG skan.
Dźwignia 4 — tworzenie podzbioru czcionek
Dział zatytułowany „Dźwignia 4 — tworzenie podzbioru czcionek”Osadzona czcionka to program. Pełny może mieć kilkaset kilobajtów, ponieważ niesie każdy glif, jaki projektant kroju kiedykolwiek narysował — tysiące znaków w pismach, których nigdy nie użyjesz w tym dokumencie. Tworzenie podzbioru osadza tylko glify, które dokument faktycznie rysuje, zamieniając ten program w mały ułamek samego siebie. Jednostronicowy list nie potrzebuje całej czcionki obsługującej CJK; potrzebuje tych kilkudziesięciu glifów, które składa. Mechanika tego, jak glify są wybierane i ponownie indeksowane, to osobny temat — zobacz Czcionki: trudna część.
- 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.
Praktyczny przykład
Dział zatytułowany „Praktyczny przykład”Nie ma tu zmyślonego API do pokazania, ponieważ najważniejsze decyzje o rozmiarze podejmuje się zanim bajty dotrą do komponentu zapisującego — a jedyna strukturalna dźwignia, którą NextPDF udostępnia, to pojedyncze włączanie opcjonalne. Koncepcyjnie budżet czyta się tak:
- Podawaj zdjęcia jako JPEG, by lądowały pod
DCTDecode, a nie zakodowane na nowo bezstratnie. Największą wygraną jest wybór dotyczący danych źródłowych, a nie flaga komponentu zapisującego. - Pozwól komponentowi zapisującemu tworzyć podzbiory osadzonych czcionek, by wysyłane były tylko narysowane glify.
- Dla dokumentu z wieloma małymi obiektami włącz pakowanie strumieni obiektów,
które prowadzi gotowy plik przez
ObjectStreamPacker, by zgrupować małe obiekty niebędące strumieniami i przepisać strumień odwołań krzyżowych.
Ścieżka domyślna — klasyczne wpisy „w użyciu”, bez ObjStm — to właściwa baza: deterministyczna, stabilna bajtowo i łatwa do zweryfikowania. Pakowanie to przemyślane ulepszenie, gdy to liczba obiektów, a nie ciężar obrazu, jest tym, co nadyma plik.
Częste nieporozumienie
Dział zatytułowany „Częste nieporozumienie”Pułapką jest sięganie po przycisk „skompresuj PDF” i oczekiwanie, że naprawi
wszystko. Kompresja to nie jedna dźwignia; to cztery, i nie zastępują się
nawzajem. Pakowanie strumieni obiektów nie potrafi zmniejszyć zdjęcia — to
zadanie filtra obrazu. Doskonały JPEG nie zrównoważy czcionki, której zapomniałeś
zredukować do podzbioru. A nic z tego nie pomaga, jeśli prawdziwym przerostem
jest skan 10 MB przechowywany bezstratnie, ponieważ nikt nie wybrał dla
niego DCTDecode.
Drugie nieporozumienie jest takie, że mniejszy jest zawsze ściśle lepszy. Nie jest. Strumienie obiektów są niezgodne z linearyzacją — układem szybkiego podglądu w sieci, który ustala bezwzględne rozmieszczenie obiektów, by pierwsza strona strumieniowała się wcześnie. A niektóre profile archiwalne ograniczają, które filtry obrazu są w ogóle dozwolone. Rozmiar to jedna oś. Wymienia się go ze strumieniowaniem, zgodnością archiwalną oraz powtarzalnością, a właściwy punkt na krzywej zależy od tego, do czego dokument służy.
Ograniczenia i granice
Dział zatytułowany „Ograniczenia i granice”Cztery dźwignie to strukturalna ekonomia rozmiaru pliku. Nie są uniwersalną gwarancją „zmniejsz to”, a NextPDF nie udaje, że jest reoptymalizatorem dla dowolnych przychodzących plików PDF.
ObjectStreamPacker to optymalizacja w najlepszym razie, która nigdy nie ryzykuje
poprawności. Odmawia — zwracając dane wejściowe niezmienione — gdy plik nie jest
PDF ze strumieniem odwołań krzyżowych, gdy jest zaszyfrowany, gdy niesie podpis
cyfrowy (przekładanie obiektów przesunęłoby zakresy bajtów, które podpis chroni),
gdy już zawiera strumienie obiektów lub gdy nie ma kwalifikującego się obiektu do
upakowania. Emituje dokładnie jeden strumień obiektów dla całego dokumentu; nie
dzieli na kolekcję /Extends, co jest poza zakresem i zgodne dla rozmiarów
dokumentów, które wytwarza NextPDF.
Dźwignie obrazu i czcionki to w dużej mierze decyzje o danych wejściowych.
Komponent zapisujący NextPDF nie transkoduje niejawnie bezstratnej bitmapy w
stratny strumień obrazu — przekodowanie zdjęcia na DCTDecode lub JPXDecode to
jawna nadrzędna decyzja kodowania obrazu, a nie coś, co serializator robi za
twoimi plecami — ani nie odzyskuje glifów z czcionki, którą wywołujący poprosił o
osadzenie w całości. Największe wygrane na rozmiarze podejmuje się wyżej w
łańcuchu niż serializator bajtów; zadaniem silnika jest nie marnować budżetu,
który mu przynosisz.
Powiązane dokumenty
Dział zatytułowany „Powiązane dokumenty”- Strumienie i filtry — towarzysz jak filtr dekoduje; ta strona celowo go uzupełnia, a nie powiela.
- Czym właściwie jest PDF — model obiektów pośrednich, którego narzut na obiekt redukuje dźwignia strumienia obiektów.
- Anatomia pliku PDF — struktura odwołań krzyżowych, która staje się skompresowanym strumieniem.
- Czcionki: trudna część — jak tworzenie podzbioru wybiera i ponownie indeksuje glify, które rysuje dokument.
Słownik pojęć
Dział zatytułowany „Słownik pojęć”- Strumień obiektów (ObjStm) — pojedynczy strumień, który trzyma wiele małych
obiektów pośrednich niebędących strumieniami, skompresowanych razem, dzięki
czemu obwiednia
obj/endobjjest zrzucana dla grupy. Każdy upakowany obiekt wciąż zachowuje własny wpis odwołań krzyżowych, jako zwięzły wpis typu 2. W NextPDF włączany opcjonalnie. - Obiekt pośredni — numerowany obiekt w grafie PDF, opakowany w obwiednię
obj/endobji śledzony przez indeks odwołań krzyżowych. Obwiednia to narzut na obiekt. - Strumień odwołań krzyżowych — strumień
/Type /XRef, który indeksuje każdy obiekt, pakowany binarnie i deflowany, a nie zapisywany jako wiersze zwykłego tekstu. - Skompresowany wpis (typu 2) — wpis odwołań krzyżowych, który wskazuje na obiekt żyjący wewnątrz strumienia obiektów, nazywając strumień i indeks w jego obrębie.
- Tworzenie podzbioru czcionek — osadzanie tylko glifów, które dokument faktycznie rysuje, zamiast pełnego kroju, zmniejszając osadzony program czcionki.
- Filtr bezstratny kontra stratny — bezstratny kodek (FlateDecode) odtwarza każdy bajt; stratny kodek obrazu (DCTDecode lub JPXDecode w trybie stratnym) odrzuca niezauważalny szczegół na rzecz znacznie mniejszego wyniku. (JPEG 2000 / JPX można też skonfigurować bezstratnie.) Ten wybór to dominująca dźwignia w pliku bogatym w media.