Aller au contenu
getnextpdf.com

Pourquoi le texte d'un PDF n'est pas vraiment du texte

Quand tu lis un PDF, tu vois des mots. Le fichier ne contient pas de mots. Il contient des instructions pour peindre des formes à des coordonnées, et ces formes se trouvent ressembler à des lettres. L’écart entre ce qu’un PDF dessine et ce qu’il signifie est la raison pour laquelle une page peut paraître impeccable et pourtant se copier en charabia. Cette page traite de cet écart, et de la petite correspondance distincte qui le comble.

Tout ce que tu fais d’un PDF en tant que texte — le sélectionner, le copier, le rechercher, l’indexer pour la récupération, le lire à voix haute avec un lecteur d’écran — dépend de la reconstitution de caractères que le fichier n’a jamais directement stockés. Si cette reconstitution échoue, l’échec est invisible. La page s’affiche toujours. Personne ne s’en aperçoit tant que quelqu’un ne copie pas un paragraphe dans un courriel et n’obtient □□□□, ou ne cherche dans un contrat de 400 pages une clause qui s’y trouve manifestement et ne trouve rien.

C’est une catégorie de bogue coûteuse précisément parce qu’elle survit à tout contrôle visuel. Il n’y a rien à voir. Le document a l’air faisant autorité et est, du point de vue des machines, muet.

  • Un PDF dessine des glyphes — des formes visuelles choisies par des codes numériques dans une police — et non des caractères Unicode.
  • La même forme peut signifier des caractères différents, et le même caractère peut être dessiné par des glyphes différents. Apparence et signification sont découplées par conception.
  • Pour récupérer le texte, un lecteur parcourt le chemin d’affichage à l’envers : du code au caractère. La CMap /ToUnicode de la police est la table de correspondance de cette étape inverse (Spec: ISO 32000-2, §9.10).
  • Pas de /ToUnicode, ou une mauvaise, signifie copier-coller illisible et recherche infructueuse — avec la page toujours parfaite au pixel près.
  • C’est le problème de la signification. Savoir si le glyphe a la bonne apparence est le problème distinct de l’apparence traité dans Fonts: the hard part. NextPDF émet une CMap /ToUnicode correcte pour que l’aller-retour soit fidèle.

Commence par la façon dont le texte arrive sur la page. Un flux de contenu ne dit pas « écris le mot file. » Il sélectionne une police, puis transmet une chaîne de codes à un opérateur d’affichage de texte (Spec: ISO 32000-2, §9). Chaque code est un index — une position dans un programme de police — et la police transforme cet index en un contour de glyphe et le peint. Le code 70 n’est pas le caractère F. C’est « l’emplacement 70 dans cette police », et l’emplacement 70 se trouve contenir une courbe en forme de F. Choisis une autre police et l’emplacement 70 pourrait être un flocon de neige.

Un code ne signifie donc quelque chose que relativement à l’encodage de sa police. Pour une police simple, cet encodage est en gros d’un octet par glyphe. Pour les écritures qui nécessitent des milliers de glyphes — le CJK, ou tout document en Unicode complet — ce n’est pas assez de place, et le PDF recourt à une police composite (Type 0) (Spec: ISO 32000-2, §9.7). Une police composite lit des codes multi-octets via une CMap, les transforme en CID (identifiants de caractères), et associe les CID à des glyphes. C’est une indirection élégante qui permet à une seule police d’adresser un ensemble de glyphes énorme. C’est aussi un endroit de plus où la piste de « ce qui est montré » vers « ce que cela signifie » peut se perdre.

Maintenant, exécute le processus à l’envers. Pour extraire le texte, un lecteur prend les codes qu’il trouve dans le flux de contenu et doit reconstituer des caractères (Spec: ISO 32000-2, §9.10). L’encodage qui a dessiné le glyphe allait du caractère au glyphe ; l’extraction a besoin du code de glyphe au caractère, et cette direction n’est pas garantie inversible. Une police sous-ensemble peut avoir renuméroté ses glyphes. Les CID d’une police composite peuvent être privés. La forme sur la page ne porte aucune signification Unicode intrinsèque.

Le remède est de livrer la correspondance inverse aux côtés de la police. Cette correspondance est la CMap /ToUnicode : pour chaque code que le document utilise, elle consigne la valeur Unicode (ou les valeurs) que ce code représente. Avec elle, l’extraction est une simple recherche. Sans elle, un lecteur en est réduit à deviner à partir des encodages de police et d’heuristiques — et deviner est exactement là où fi devient un point d’interrogation.

  1. Your charactersThe Unicode text you set, for example the word 'file'.
  2. Encoding → codesThe font's encoding turns characters into numeric codes; a composite font routes them through a CMap to CIDs.
  3. Codes → glyphsEach code selects a glyph outline, which is painted to the page. This is the part you see.
  4. Extraction reverses itA reader reads the codes back and looks each one up in /ToUnicode to recover Unicode.
  5. Text againWith a correct /ToUnicode, copy, search, indexing, and screen readers all get the original characters back.
Les deux directions du texte PDF. Le rendu va du caractère → glyphe et c'est ce que tu vois. L'extraction va du code → caractère et c'est ce que tu peux copier, rechercher et lire à voix haute — et cela ne fonctionne que lorsque la correspondance /ToUnicode boucle la boucle.

NextPDF écrit la correspondance inverse par principe. Lorsqu’il intègre une police composite, il émet le descendant CIDFontType2, le parent Type0, et une CMap /ToUnicode pour que les codes se résolvent à nouveau en Unicode — le travail d’encodage décrit dans Fonts: the hard part. Le point qu’il vaut la peine de distinguer ici, c’est le pourquoi : le flux /ToUnicode ne sert pas à donner la bonne apparence à la page. Les glyphes ont déjà la bonne apparence sans lui. C’est l’ unique artefact qui maintient le texte extractible.

L’endroit le plus célèbre où cela apparaît est la ligature fi. Beaucoup de polices dessinent f et i comme un seul glyphe combiné, parce que le point du i entre en collision avec le crochet du f. Sur la page, c’est une seule forme, dessinée par un seul code. La question est de savoir ce qui se passe quand tu la copies.

% 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

Cette unique entrée fait la différence entre une recherche de « file » qui aboutit et une recherche de « file » qui manque silencieusement chaque occurrence rendue avec la ligature. Associe le code de la ligature à la séquence de deux caractères f + i, et le mot est recherchable et copiable. Laisse-le non associé, et la page affiche toujours file parfaitement tandis que le texte ne dit discrètement rien qu’une machine puisse lire.

C’est pourquoi « fi » peut s’extraire en un caractère ou deux, et pourquoi la différence n’est pas aléatoire — c’est ce que la correspondance /ToUnicode a reçu l’ordre de dire. Une correspondance correcte décompose la ligature en ses caractères. NextPDF émet exactement ce genre d’entrée, si bien qu’une ligature sur la page devient deux lettres ordinaires dans le presse-papiers.

Le piège, nommé : « le texte s’affiche, donc le texte est bon. » Le rendu et l’extraction sont des mécanismes différents lisant des données différentes. Le rendu suit le chemin code-vers-glyphe et c’est ce que tes yeux vérifient. L’extraction suit le chemin code-vers-caractère via /ToUnicode et c’est ce que vérifie tout consommateur machine. Un document peut réussir le premier haut la main et échouer complètement au second, parce que par construction il n’y a rien de visuel pour le révéler.

Un second piège, plus subtil : supposer qu’un glyphe « sait évidemment » quel caractère il est. Ce n’est pas le cas. Un glyphe est une forme avec un index. La correspondance vers un caractère est une information supplémentaire que le producteur doit fournir. Si le producteur ne l’a jamais écrite, aucun lecteur ne peut la reconstituer fidèlement — il ne peut que deviner.

Faithful text extraction from documents NextPDF writes — edition availability
EditionAvailability
CoreNextPDF 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.
ProNot in this edition
EnterpriseNot in this edition

Une correspondance /ToUnicode ne peut transporter que l’information que la police source expose réellement. Là où un glyphe n’a aucun caractère déterminable — une marque purement décorative, un symbole à usage privé sans attribution Unicode — aucune correspondance ne peut inventer une signification qui n’a jamais été là. NextPDF émet la correspondance véridique qu’il peut dériver ; il ne fabrique pas de caractères pour combler un vide.

Cette page traite des documents que NextPDF écrit. Ce n’est pas un outil de réparation pour des PDF entrants arbitraires dont le /ToUnicode est déjà absent ou erroné ; récupérer le texte de ceux-là est un problème distinct, heuristique. Et une extraction fidèle est nécessaire pour l’accessibilité, mais n’en est pas la totalité — l’ordre de lecture, les balises, et le texte de remplacement sont traités dans What makes a PDF accessible.

Pourquoi le même PDF se copie-t-il bien dans un lecteur et mal dans un autre ? Les différents lecteurs se rabattent différemment lorsque /ToUnicode est absent. Certains devinent à partir de l’encodage intégré de la police et tombent juste sur du texte latin courant ; d’autres non. Un /ToUnicode correct supprime la loterie — chaque lecteur conforme obtient les mêmes caractères.

Est-ce la même chose que la police non intégrée ? Non. L’intégration concerne l’apparence — si le glyphe se peint sans la police installée. /ToUnicode concerne la signification — si le code se ramène à un caractère. Une police peut être parfaitement intégrée et n’avoir tout de même aucun /ToUnicode exploitable, ce qui est l’échec joli-mais-non-recherchable.

L’extraction a-t-elle parfois besoin de plus d’un caractère par code ? Oui — le cas de la ligature en est exactement un : un code correspond à deux caractères. Une entrée /ToUnicode peut associer un seul code à une courte séquence, et c’est ainsi que les ligatures, et certaines formes composées, reviennent sous la forme de leurs lettres constitutives.

  • Fonts: the hard part — la pièce compagne sur le versant de l’apparence : intégration, sous-ensemble, et comment l’encodage est construit. Cette page est le versant de la signification de la même pièce de monnaie.
  • What makes a PDF accessible — un texte fidèle est un ingrédient ; l’ordre de lecture, les balises et le texte alternatif sont le reste.
  • What a PDF actually is — le modèle d’objets dans lequel vivent les polices, les encodages et les flux /ToUnicode.
  • Glyphe — une forme visuelle dans une police (un contour). Ce qu’un PDF peint réellement. Un glyphe a un index mais aucune signification de caractère intrinsèque.
  • Caractère — une unité de langage écrit, identifiée par un point de code Unicode. Ce que tu veux dire, et ce que l’extraction tente de reconstituer.
  • Code — la valeur numérique dans un flux de contenu qui sélectionne un glyphe dans la police courante. Pas un caractère Unicode ; significative uniquement relativement à la police.
  • CID — un identifiant de caractère utilisé par une police composite pour adresser un glyphe au sein d’un grand ensemble ; une étape intermédiaire entre le code et le glyphe.
  • Police composite (Type 0) — une police qui lit des codes multi-octets via une CMap pour atteindre des CID et des glyphes, utilisée quand une police doit adresser des milliers de glyphes.
  • /ToUnicode — le flux CMap qui associe les codes qu’un document utilise aux valeurs Unicode ; ce qui rend le texte PDF recherchable, copiable et accessible.
  • Ligature — un glyphe unique qui dessine deux lettres ou plus comme une seule forme (par exemple fi) ; son entrée /ToUnicode la décompose en ses caractères.