L'économie de la taille d'un fichier PDF
Spec: ISO 32000-2, §7.5.7ISO 32000-2 §7.5.7
Deux PDF peuvent paraître identiques pixel pour pixel à l’écran et différer d’un facteur dix sur le disque. La différence n’est presque jamais le contenu que tu vois ; c’est la manière dont le fichier a été assemblé en dessous. Cette page est un tour d’horizon de l’économie de taille : où vont réellement les octets d’un PDF, et les quatre leviers sur lesquels un auteur dépense un budget d’octets.
C’est le compagnon pourquoi les fichiers sont gros de Flux et filtres, qui couvre comment un filtre décode. Celui-ci reste sur le budget.
Pourquoi c’est important
Section intitulée « Pourquoi c’est important »La taille d’un fichier est rarement une métrique de vanité. C’est de la bande passante à chaque téléchargement, du stockage à chaque archivage, et de la latence à chaque aperçu. Une facture de 12 Mo qui aurait dû peser 400 Ko n’est pas un problème cosmétique lorsque tu en génères un million par mois — c’est une facture trente fois plus salée.
Le frustrant, c’est que le surpoids est généralement invisible. Le document se rend correctement, s’ouvre bien, s’imprime bien. Rien ne te dit que le même artefact aurait pu peser une fraction de sa taille, car les octets gaspillés sont structurels, pas visuels. Pour les trouver, tu dois regarder le budget, pas la page.
La version courte
Section intitulée « La version courte »Considère un PDF comme un budget que tu dépenses sur quatre postes.
- Surcoût par objet. Chaque objet indirect porte une enveloppe
N G obj/endobj, et est suivi par une entrée de références croisées. Un document chargé en pages a des milliers de petits objets, et les enveloppes s’accumulent. Les flux d’objets suppriment cette enveloppe pour tout un groupe d’objets ; chaque objet empaqueté conserve sa propre entrée de références croisées, mais sous forme d’une entrée compacte de type 2. - Octets d’image. Pour tout document comportant des photographies ou des scans, les images dominent, et le plus grand levier de loin est le choix de filtre — un codec sans perte par rapport à un codec d’image avec perte est la différence entre des mégaoctets et des kilooctets.
- Octets de police. Une police incorporée complète représente des centaines de kilooctets de glyphes que tu n’utilises jamais. Le sous-ensemble ne conserve que les glyphes que le document dessine réellement.
- L’index. La table de références croisées qui permet à un lecteur de trouver chaque objet peut elle-même être un flux compressé plutôt que du texte brut.
Réussis les quatre et le fichier est petit. Manque-en un et il domine tout le reste de ce que tu as bien fait.
L’approche de NextPDF
Section intitulée « L’approche de NextPDF »L’écrivain de NextPDF est un sérialiseur en flux à passe unique : il ajoute les octets de chaque objet à mesure qu’ils sont produits et enregistre pour chacun une entrée classique de références croisées « en usage ». Cette valeur par défaut est rapide, prévisible, et produit un fichier stable en octets — mais ce n’est pas la disposition la plus petite possible, et NextPDF est honnête à ce sujet.
Levier 1 — les flux d’objets (ObjStm)
Section intitulée « Levier 1 — les flux d’objets (ObjStm) »Un PDF est un graphe d’objets indirects. La plupart d’entre eux sont de petits
dictionnaires : nœuds de page, dictionnaires d’annotation, éléments d’arbre de
structure, entrées de plan. Chacun paie une taxe fixe — les mots-clés obj / endobj,
les numéros d’objet et de génération, et une entrée de références croisées qui le
localise. Sur un document comportant des milliers de petits objets, cette taxe est une
tranche significative du fichier.
Un flux d’objets collecte beaucoup de ces petits objets hors flux dans un seul flux et
les compresse ensemble (Spec: ISO 32000-2, §7.5.7ISO 32000-2 §7.5.7). L’enveloppe
obj / endobj est supprimée pour tout le groupe ; les valeurs à l’intérieur sont
stockées bout à bout sans mots-clés par objet, puis dégonflées en un seul bloc — ce qui
compresse aussi mieux, car le compresseur déduplicateur voit maintenant tous ces
dictionnaires similaires d’un coup. L’entrée de références croisées ne disparaît pas —
chaque objet empaqueté en a encore besoin d’une — mais elle rétrécit en une entrée
binaire compacte de type 2 dans le flux de références croisées (plus à ce sujet au
Levier 2).
Dans NextPDF, ceci est délivré par ObjectStreamPacker, un post-processeur autonome qui
prend un PDF à flux de références croisées terminé et réécrit les objets éligibles dans
un unique /Type /ObjStm. Les règles qu’il suit viennent directement de la norme : un
objet éligible est un objet de génération zéro, hors flux, après exclusion des objets
spéciaux qui doivent rester directement adressables. §7.5.7 interdit de stocker un objet
flux à l’intérieur d’un flux d’objets, de sorte que les objets flux — contenu, polices,
images — conservent leurs propres entrées ; et ObjectStreamPacker décline en outre le
propre objet flux de références croisées du document (il est réécrit) et le dictionnaire
/Encrypt, qui doivent tous deux rester directement adressables. Tout le reste de
génération zéro et hors flux est empaqueté.
| 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 |
L’activation à la demande est une posture délibérée, pas une limitation qui se cache. La sortie par défaut est stable en octets et correspond aux références dorées existantes ; activer l’empaquetage opte pour une disposition différente, plus petite, tout aussi déterministe. Tu choisis le compromis, et le moteur ne le fait jamais dans ton dos.
Levier 2 — l’index peut aussi être un flux
Section intitulée « Levier 2 — l’index peut aussi être un flux »Une fois que les objets vivent à l’intérieur d’un flux d’objets, l’index qui pointe vers
eux change de forme. Un lecteur trouve un objet empaqueté à travers une entrée de
références croisées compressée — une entrée de type 2 qui nomme le flux d’objets et
l’index à l’intérieur de celui-ci (Spec: ISO 32000-2, §7.5.8.3ISO 32000-2 §7.5.8.3).
Parce que l’ensemble des références croisées est lui-même un flux /Type /XRef, l’index
pour des milliers d’objets est empaqueté en binaire et dégonflé plutôt qu’écrit en
lignes de texte brut. La carte rétrécit en même temps que le territoire.
ObjectStreamPacker reconstruit exactement ceci : il émet le flux d’objets unique, puis
un flux de références croisées réécrit portant une entrée compacte de type 1 pour chaque
objet conservé et une entrée de type 2 pour chaque objet empaqueté, préservant chaque
numéro d’objet pour que les références existantes restent valides.
Levier 3 — le choix de filtre d’image
Section intitulée « Levier 3 — le choix de filtre d’image »Pour tout document comportant de vraies images, ce levier écrase les autres. Les octets sont la même image ; le codec est le budget. Les noms de filtre vivent dans le jeu de filtres standard (Spec: ISO 32000-2, §7.4ISO 32000-2 §7.4), et le choix entre eux est une décision de taille :
- FlateDecode est sans perte. Parfait pour les dessins au trait, les captures d’écran et tout ce qui a des aplats de couleur — et ruineux pour une photographie, où sans perte signifie chaque octet de l’original.
- DCTDecode est le JPEG : avec perte, et pour les photographies le bon choix de loin, souvent une réduction d’un facteur dix pour une baisse de qualité que personne ne remarque.
- JPXDecode est le JPEG 2000 : compression par ondelettes avec une courbe qualité/taille différente, mais un support inégal et interdit par certains profils d’archivage.
C’est exactement là où comment un filtre décode importe, et c’est le travail de l’article Flux et filtres. Le point économique est plus étroit : une photographie stockée sans perte est la cause unique la plus courante d’un PDF inutilement énorme, et aucune quantité d’empaquetage en flux d’objets ne sauvera un fichier dont le vrai poids est un scan non passé au JPEG.
Levier 4 — le sous-ensemble de polices
Section intitulée « Levier 4 — le sous-ensemble de polices »Une police incorporée est un programme. Une police complète peut représenter plusieurs centaines de kilooctets, car elle porte chaque glyphe que le créateur de la fonte ait jamais dessiné — des milliers de caractères à travers des écritures que tu n’utiliseras jamais dans ce document. Le sous-ensemble n’incorpore que les glyphes que le document dessine réellement, transformant ce programme en une petite fraction de lui-même. Une lettre d’une page n’a pas besoin de la totalité d’une police compatible CJK ; elle a besoin des quelques dizaines de glyphes qu’elle compose. Les mécaniques de la façon dont les glyphes sont sélectionnés et réindexés sont leur propre sujet — vois Polices : la partie difficile.
- 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.
Exemple pratique
Section intitulée « Exemple pratique »Il n’y a pas d’API fabriquée à montrer ici, car les décisions de taille les plus importantes sont prises avant que les octets n’atteignent l’écrivain — et le seul levier structurel que NextPDF expose est une unique activation à la demande. Conceptuellement, le budget se lit comme ceci :
- Fournis les photographies en JPEG pour qu’elles atterrissent sous
DCTDecode, et non ré-encodées sans perte. Le plus grand gain est un choix sur les données source, pas un drapeau d’écrivain. - Laisse l’écrivain sous-ensembler les polices incorporées pour que seuls les glyphes dessinés soient expédiés.
- Pour un document comportant de nombreux petits objets, opte pour l’empaquetage en flux
d’objets, qui route le fichier terminé à travers
ObjectStreamPackerpour regrouper les petits objets hors flux et réécrire le flux de références croisées.
Le chemin par défaut — entrées classiques « en usage », pas d’ObjStm — est la bonne base de référence : déterministe, stable en octets et facile à vérifier. L’empaquetage est l’amélioration réfléchie quand c’est le nombre d’objets, et non le poids des images, qui gonfle le fichier.
Idée fausse courante
Section intitulée « Idée fausse courante »Le piège est de se tourner vers un bouton « compresser le PDF » en s’attendant à ce
qu’il règle tout. La compression n’est pas un levier ; ce sont quatre, et ils ne se
substituent pas les uns aux autres. L’empaquetage en flux d’objets ne peut pas rétrécir
une photographie — c’est le travail du filtre d’image. Un JPEG parfait ne peut pas
compenser une police que tu as oublié de sous-ensembler. Et rien de tout cela n’aide si
le vrai surpoids est un scan de 10 Mo stocké sans perte parce que personne n’a
choisi DCTDecode pour lui.
La seconde idée fausse est que plus petit est toujours strictement mieux. Ce n’est pas le cas. Les flux d’objets sont incompatibles avec la linéarisation — la disposition fast-web-view qui fixe le placement absolu des objets pour que la première page se diffuse tôt. Et certains profils d’archivage restreignent quels filtres d’image sont même autorisés. La taille est un axe. Elle s’arbitre contre le streaming, la conformité d’archivage et la reproductibilité, et le bon point sur la courbe dépend de ce à quoi sert le document.
Limites et frontières
Section intitulée « Limites et frontières »Les quatre leviers sont l’économie structurelle de la taille de fichier. Ils ne sont pas une garantie universelle « rends-le plus petit », et NextPDF ne prétend pas être un ré-optimiseur pour des PDF entrants arbitraires.
ObjectStreamPacker est une optimisation au mieux qui ne risque jamais la correction.
Il décline — en retournant l’entrée inchangée — lorsque le fichier n’est pas un PDF à
flux de références croisées, lorsqu’il est chiffré, lorsqu’il porte une signature
numérique (re-disposer les objets décalerait les plages d’octets qu’une signature
protège), lorsqu’il contient déjà des flux d’objets, ou lorsqu’il n’y a aucun objet
éligible à empaqueter. Il émet exactement un flux d’objets pour tout le document ; il ne
se scinde pas en une collection /Extends, ce qui est hors périmètre et conforme pour
les tailles de document que NextPDF produit.
Les leviers d’image et de police sont en grande partie des décisions sur l’entrée.
L’écrivain de NextPDF ne transcode pas implicitement un bitmap sans perte en un flux
d’image avec perte — ré-encoder une photographie vers DCTDecode ou JPXDecode est une
décision explicite d’encodage d’image en amont, pas quelque chose que le sérialiseur
fait dans ton dos — ni ne récupère de glyphes d’une police que l’appelant a demandé
d’incorporer en entier. Les plus grands gains de taille se font en amont du sérialiseur
d’octets ; le travail du moteur est de ne pas gaspiller le budget que tu lui apportes.
Documents liés
Section intitulée « Documents liés »- Flux et filtres — le compagnon comment un filtre décode ; cette page le complète délibérément, sans le dupliquer.
- Ce qu’est réellement un PDF — le modèle d’objets indirects dont le levier des flux d’objets réduit le surcoût par objet.
- L’anatomie d’un fichier PDF — la structure de références croisées qui devient un flux compressé.
- Polices : la partie difficile — comment le sous-ensemble sélectionne et réindexe les glyphes qu’un document dessine.
Glossaire
Section intitulée « Glossaire »- Flux d’objets (ObjStm) — un seul flux qui contient de nombreux petits objets
indirects hors flux, compressés ensemble, de sorte que l’enveloppe
obj/endobjest supprimée pour le groupe. Chaque objet empaqueté conserve toujours sa propre entrée de références croisées, sous forme d’entrée compacte de type 2. À activer à la demande dans NextPDF. - Objet indirect — un objet numéroté dans le graphe d’un PDF, enveloppé dans une
enveloppe
obj/endobjet suivi par l’index de références croisées. L’enveloppe est le surcoût par objet. - Flux de références croisées — le flux
/Type /XRefqui indexe chaque objet, empaqueté en binaire et dégonflé plutôt qu’écrit en lignes de texte brut. - Entrée compressée (type 2) — une entrée de références croisées qui pointe vers un objet vivant à l’intérieur d’un flux d’objets, nommant le flux et l’index à l’intérieur de celui-ci.
- Sous-ensemble de polices — incorporer uniquement les glyphes qu’un document dessine réellement, plutôt que la fonte complète, rétrécissant le programme de police incorporé.
- Filtre sans perte vs avec perte — un codec sans perte (FlateDecode) reproduit chaque octet ; un codec d’image avec perte (DCTDecode, ou JPXDecode en mode avec perte) écarte les détails imperceptibles pour un résultat bien plus petit. (Le JPEG 2000 / JPX peut aussi être configuré sans perte.) Le choix est le levier dominant sur un fichier riche en médias.