Journal PDF, mais que fait la police

Posté par  . Licence CC By‑SA.
178
21
août
2021

Sommaire

Salutations

Le PDF, quel format merveilleux. On génère un fichier et on est sûrs qu'il va s'afficher correctement partout, que ce soit avec des lecteurs libres (Poppler et donc Okular/Evince, mupdf, pdf.js, pdfium et donc Chromium…) ou propriétaires.
Ou pas.
«tiens, je me demandais, ton okular aussi il fait un rendu dégueulasse des fonts sur une attestation d'assurance maladie ameli ?»

En voilà une question qu'elle est bonne. Et oui, il fait un rendu dégueulasse. Bon, le document de test utilisé ici sera une attestation de mutuelle, mais l'idée est la même. Alors plongeons dans la fosse à purin qu'est le rendu de texte en PDF…

1) Regard d'utilisateur

Bon. Ouvrons le PDF avec Okular (une version anonymisée est disponible sur https://rock.pinaraf.info/~pinaraf/dlfp-pdf/test-pdf-fonts-anon.pdf). Le texte de l'adresse est effectivement dégueulasse. Le MA de MADAME est tout compressé, avec le M qui mord sur le A (ou l'inverse, je n'ai jamais étudié la férocité relative des lettres). De même sur le DA ou le D mord le A. C'est très sale.
Okular a le bon goût, dans la fenêtre des propriétés du document (menu Fichier), d'indiquer les polices utilisées et leurs substitutions, et même le chemin du fichier de police sur le disque.
On observe que 6 polices sont utilisées, deux Arial (dispo gratuitement avec une licence flexible), deux Calibri (coucou Microsoft qui interdit d'installer ces polices sur Linux sans licence), une Monospace821BT-Roman inconnue au bataillon et une UNWXZK+Windings-Regular, du monde de microsoft à nouveau. Sur toutes ces polices, seule la Wingdings est embarquée dans le document. Pour toutes les autres, le moteur de rendu a du trouver la police sur le système, ou procéder à une substitution, plus ou moins heureuse…
Arial BoldMT et ArialMT sont chez moi remplacées par Liberation Sans Bold et DejaVu Sans. Le remplacement de ArialMT par DejaVu Sans n'est pas le plus heureux qu'il soit, c'est incohérent avec Arial BoldMT, mais admettons.
Les Calibri sont remplacés par DejaVu Sans. C'est la même famille (sans-serif, les hors la loi se font plaisir), mais par contre il n'y a pas de garantie de compatibilité de chasse, donc le résultat risque d'être médiocre.
La dernière, Monospace821BT-Roman, est remplacée par DejaVu Sans. Et là, ça pique. Monospace821BT est une police à chasse fixe (tous les caractères ont la même taille). DejaVu Sans ne l'est pas du tout. Et c'est fort probablement cette police qui est utilisée pour l'adresse, ce qui explique l'apparence affreuse de l'adresse sur le document.

Quand on ouvre le document avec mupdf, on observe que l'adresse n'est pas très belle, les caractères semblent déformés, ce qui est particulièrement visible sur le I de MACHIN qui est extrêmement large. Mais le document est globalement plus propre qu'avec poppler.

Avec pdf.js et pdfium par contre (donc Firefox et Chromium), le résultat est correct. Et pdf.js faisant du rendu sur un document HTML, on peut utiliser l'inspecteur de Firefox pour regarder ce qui est fait ! On observe que l'adresse utilise une police à chasse fixe (font-family monospace), ce qui permet un rendu tout à fait correct du texte.

Pour achever ce tour du point de vue de l'utilisateur, regardons ce que fait la concurrence propriétaire, Acrobat Reader (dans une VM sous Windows 10, avec uniquement les polices de base). L'adresse apparait comme sur mupdf, avec des espacement entre caratères assez désagréable, mais elle est lisible, et le caractère I n'est pas sur-gras. Dans les propriétés du document, la police Monospace821BT-Roman a été remplacée par Adobe Sans MM, erreur similaire à Poppler.

2) Plus technique, le contenu du PDF…

Un PDF est un flux constitué d'une succession d'objets. Cela permet notamment des fonctionnalités très pratiques en cas de contraintes de place ou de bande passante : si vous avez un fichier de 1 GB (avec plein d'images ou de vidéos par exemple), nul besoin de lire tout le document pour afficher la première page (si le PDF est bien construit).
Pour regarder le contenu d'un PDF, le meilleur outil que je connaisse est podofobrowser (https://github.com/KubaO/podofobrowser).
La vue par défaut est arborescente, ce qui sera plus pratique pour nous.
L'élément de départ est le catalogue, un dictionnaire. Ce qui nous intéresse dedans est la liste des polices de caractères. Pour cela, on va se promener dans Pages, qui contient une liste de 1 élément (normal, il n'y a qu'une page). L'objet Page (position 5 0 dans le document) est un dictionnaire, qui contient des Resources, dont les polices utilisées.
On y retrouve bien nos 6 polices de caractères, et la cinquième est la Monospace821BT-Roman. Dans les propriétés listées ici, on observe des informations sur la chasse des caractères (commune à chaque caractère, 602), des flags (valeur 32), un type (Type1)… Rien de transcendant pour nous pour le moment.
Mais que contient ce flags ? Le code source de mupdf est assez clair là dessus :

enum
{
        PDF_FD_FIXED_PITCH = 1 << 0,
        PDF_FD_SERIF = 1 << 1,
        PDF_FD_SYMBOLIC = 1 << 2,
        PDF_FD_SCRIPT = 1 << 3,
        PDF_FD_NONSYMBOLIC = 1 << 5,
        PDF_FD_ITALIC = 1 << 6,
        PDF_FD_ALL_CAP = 1 << 16,
        PDF_FD_SMALL_CAP = 1 << 17,
        PDF_FD_FORCE_BOLD = 1 << 18
};

La police est donc juste indiquée comme «non symbolique» (rappel, 2**5 = 32). Et pas comme ayant une chasse fixe. Du coup, forcément, ça complique le remplacement…

Avec podofobrowser, je change donc ce flag pour y mettre la valeur 33, je sauvegarde, je recharge dans okular… et rien, nada, toujours la même police… Bigre… Mais si je change le BaseFont (le nom donc) en «/Monospace», là le remplacement est bien fait et le texte s'affiche correctement. Sur mupdf, le flag est suffisant par contre pour qu'une belle police à chasse fixe soit prise.

Bigre bigre. Il va donc falloir regarder comment fait chaque implémentation libre alors.

3) Commençons avec mupdf

mupdf a un code assez clair, même s'il est très peu commenté. Tout se passe dans https://github.com/ArtifexSoftware/mupdf/blob/master/source/pdf/pdf-font.c
La fonction qui déclenche tout est pdf_load_font_descriptor. Elle va lire les différents éléments du PDF, puis appeler pdf_load_system_font qui passe la balle à pdf_load_substitute_font. Si la police n'est pas trouvée sur le système, alors pdf_lookup_substitute_font prend le relais, avec les informations sur monospace/serif/gras/italique. Si la police est mono, alors la police Courier est prise, en serif ce sera Times et sinon Helvetica.

mupdf suppose donc que les flags sont correctement renseignés. Sinon, la police de substitution choisie sera nécessairement mauvaise. Pour compenser, le code de dessin utilise la largeur des caractères pour contraindre le rendu de chaque glyphe (fonction fz_adjust_ft_glyph_width appelée par do_ft_render_glyph dans source/fitz/font.c)
C'est donc pour cela que le rendu de mupdf n'est pas parfait, mais reste acceptable : les caractères ne sont pas prévus pour être en chasse fixe, et sont individuellement redimensionnés. D'où le I qui semble bien trop large.

4) Que fait pdf.js ?

Nous avons vu que la police n'a pas le bon flag, et pourtant pdf.js choisit bien une police en chasse fixe. Un petit coup d'inspecteur web montre un font-family: monospace.
Donc regardons le code source de pdf.js.

projects/pdf.js/src $ git grep monospace
core/evaluator.js:    // Heuristic: detection of monospace font by checking all non-zero widths
core/evaluator.js:    let monospace = false;
core/evaluator.js:      monospace = true;
core/evaluator.js:      monospace,
core/evaluator.js:          (metrics.monospace ? FontFlags.FixedPitch : 0) |
core/fonts.js:      fallbackName = "monospace";

C'est tentant ce commentaire, regardons… https://github.com/mozilla/pdf.js/blob/master/src/core/evaluator.js#L3552
Le code vérifie la largeur de chaque caractère de la police. Si toutes les largeurs non-nulles sont égales, alors la police est considérée comme étant à chasse fixe, même en l'absence du flag FixedPitch. Simple et efficace. Le code traduit ensuite ce flag en fallbackName "monospace" qui correspondra à la famille de police en CSS, https://github.com/mozilla/pdf.js/blob/master/src/core/fonts.js#L871

5) Quid de poppler

Dans le cas de poppler, la gestion de la substitution est… différente. Tout d'abord, un algorithme similaire à celui de pdf.js est bien présent pour identifier un flag FixedWidth manquant : https://gitlab.freedesktop.org/poppler/poppler/-/blob/master/poppler/GfxFont.cc#L1362. Le reste de la substitution a lieu dans https://gitlab.freedesktop.org/poppler/poppler/-/blob/master/poppler/FontInfo.cc#L189
Si la police n'est pas embarquée dans le PDF, alors il appelle findSystemFontFile, https://gitlab.freedesktop.org/poppler/poppler/-/blob/master/poppler/GlobalParams.cc#L873
Cette fonction va construire une requête fontconfig pour trouver la police qui correspond. Fontconfig est en quelque sorte le gestionnaire de polices : il mantient un cache des polices installées, de leurs propriétés, et est capable de trouver une police en fonction d'un ensemble de critères (type de police, nom, langues supportées…) Ces critères sont regroupés dans ce que Fontconfig appelle un pattern.
En posant dans gdb un breakpoint sur la fonction FcConfigSubstitute, on peut extraire le pattern et l'afficher avec un appel à FcPatternPrint. On obtient alors le pattern suivant:

Pattern has 3 elts (size 16)
        family: "Monospace821BT"(s)
        spacing: 100(i)(s)
        lang: "xx"(s)

Si on fait cette recherche en ligne de commande, à l'aide de fc-match, on obtient ce résultat :

% fc-match "Monospace821BT:spacing=100:lang=xx" file family
DejaVu Sans:file=/usr/share/fonts/TTF/DejaVuSans.ttf

Et c'est… inattendu. Le critère spacing: 100 correspond pourtant bien à une demande de police à chasse fixe si je comprends bien la signification de la constante dans le code (cf https://gitlab.freedesktop.org/fontconfig/fontconfig/-/blob/main/fontconfig/fontconfig.h#L178) et différentes personnes sur internet. Mais la police donnée ici ne l'est pas du tout.

Si je regarde mon catalogue de police complet :

% fc-list : family spacing | grep "DejaVu Sans"     
DejaVu Sans Mono:spacing=100
DejaVu Sans,DejaVu Sans Light
DejaVu Sans
DejaVu Sans,DejaVu Sans Condensed

Donc l'info de chasse fixe est bien présente, et uniquement sur la police DejaVu Sans Mono.

Bon… ben… on va creuser ce que fait fontconfig, ça a l'air rigolo…

En lisant un peu le code source, je suis tombé sur la sympatique variable d'environnement FC_DEBUG, qui contient un ensemble de flag afin d'activer plus ou moins de messages de debug : https://gitlab.freedesktop.org/fontconfig/fontconfig/-/blob/main/src/fcint.h#L99
La subtilité n'étant pas mon truc, je lance donc fc-match avec en variable d'environnement FC_DEBUG=8191. «On sait jamais».

Ça produit un log particulièrement volumineux, mais on aura au moins toutes les informations. Je vous l'ai mis ici à disposition : https://rock.pinaraf.info/~pinaraf/dlfp-pdf/log-fc.txt
Afin de sauter toute la configuration, cherchons où la substitution commence en cherchant notre police, Monospace821BT. La première occurence est à la ligne 46849 (oui, c'est beaucoup de messages de debug). En continuant les itérations, on observe à la ligne 47403 que la famille a été changée en family: "Monospace821BT"(s) "sans-serif"(w), ce qui est évidemment faux. Ce remplacement a été fait par le fichier de règles /etc/fonts/conf.d/49-sansserif.conf recopié ici:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
  <description>Add sans-serif to the family when no generic name</description>
<!--
  If the font still has no generic name, add sans-serif
 -->
        <match target="pattern">
                <test qual="all" name="family" compare="not_eq">
                        <string>sans-serif</string>
                </test>
                <test qual="all" name="family" compare="not_eq">
                        <string>serif</string>
                </test>
                <test qual="all" name="family" compare="not_eq">
                        <string>monospace</string>
                </test>
                <edit name="family" mode="append_last">
                        <string>sans-serif</string>
                </edit>
        </match>
</fontconfig>

Si aucune famille générique n'a été trouvée, alors on ajoute la famille sans-serif… J'avoue ne pas comprendre la logique puisque cela ignore complètement le critère spacing: 100.

J'ai donc ajouté le fichier de configuration suivant, dans /etc/fonts/conf.d/48-spacing.conf :

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
  <description>Add mono to the family when no generic name and spacing is 100</description>
<!--
  If the font has no generic name and spacing 100, add mono
 -->
        <match target="pattern">
                <test qual="all" name="family" compare="not_eq">
                        <string>sans-serif</string>
                </test>
                <test qual="all" name="family" compare="not_eq">
                        <string>serif</string>
                </test>
                <test qual="all" name="family" compare="not_eq">
                        <string>monospace</string>
                </test>
                <test qual="all" name="spacing" compare="eq">
                        <int>100</int>
                </test>
                <edit name="family" mode="append_last">
                        <string>monospace</string>
                </edit>
        </match>
</fontconfig>

Et là, miracle…

% fc-match "Monospace821BT:spacing=100:lang=xx" file family 
DejaVu Sans Mono:file=/usr/share/fonts/TTF/DejaVuSansMono.ttf

Et le PDF s'affiche correctement dans okular !

La merge request a bien sûr été réalisée pour le projet fontconfig, parce que même si c'est pas un bug (ce dont je doute), c'est un comportement piégeux qui a eu même des «grands» logiciels. https://gitlab.freedesktop.org/fontconfig/fontconfig/-/merge_requests/195

Il restera encore à creuser le code de dessin des caractères de poppler pour qu'il respecte systématiquement la largeur demandée dans la police, mais ça, c'est pour une prochaine fois…

Conclusion

C'est pas parce que le PDF est lisible qu'il faut ignorer un problème. J'avais observé ce bug sur mes premières attestation de carte vitale il y a plus de 10 ans, mais je n'avais jamais pris la peine de l'explorer, en supposant qu'il était lié à ma frilosité sur l'installation de polices. C'est uniquement quand j'ai appris que d'autres avaient le problème que j'ai creusé… J'aurais du creuser il y a 10 ans…

  • # Bravo

    Posté par  (site Web personnel) . Évalué à 10 (+16/-0).

    Aussi bien pour la correction du bug que pour le journal, c'était très intéressant.

    • [^] # Purée De Fichier (bien Tordu)

      Posté par  . Évalué à 1 (+0/-0).

      J'ai fortement apprécié aussi, même si je n'ai pas compris le coup de devoir vérifier la largeur des caractères : peut-on spécifier une fonte à chasse fixe puis indiquer des largeurs différentes pour les différents symboles ? (ce serait plus clair si j'ai un exemple utile)

      • [^] # Re: Purée De Fichier (bien Tordu)

        Posté par  . Évalué à 7 (+5/-0).

        Les PDFs sont conçus pour avoir des polices manquantes. Quand la police manque, c'est une police «compatible» qui sera choisie, sauf que compatible est un vaste mot. Pour être donc plus proche du rendu original, les largeurs des caractères sont précisées. Maintenant, spécifier des largeurs différentes sur une police à chasse fixe… je me demande ce que ça ferait…

      • [^] # Re: Purée De Fichier (bien Tordu)

        Posté par  . Évalué à 4 (+4/-0).

        peut-on spécifier une fonte à chasse fixe

        Si je comprends bien, ce pdf ne spécifie pas une fonte à chasse fixe :

        Avec podofobrowser, je change donc ce flag pour y mettre la valeur 33, je sauvegarde, je recharge dans okular… et rien, nada, toujours la même police… Bigre… Mais si je change le BaseFont (le nom donc) en «/Monospace», là le remplacement est bien fait et le texte s'affiche correctement. Sur mupdf, le flag est suffisant par contre pour qu'une belle police à chasse fixe soit prise.

        Du coup la vérification de largeur des caractères est une astuce des visionneuses si ce flag n'est pas renseigné mais que la police (inconnue) est bien à chasse fixe

        • [^] # Re: Purée De Fichier (bien Tordu)

          Posté par  . Évalué à 1 (+0/-0).

          Je crois que ce qui n'est pas clair dans ma tête, c'est la déclaration de basefont : y a juste son nom c'est ça ? pas son/sa type/famille ? Parce-que j'ai l'impression que cette information que contient le drapeau (flag) non ?
          En plus dans le passage cité il a bien remplacé les deux, ce qui laisse supposer que la substitution ne tient pas compte du drapeau mais va utiliser un heuristique à la place (pourquoi faire simple quand on peut faire compliqué quoi.)

          • [^] # Re: Purée De Fichier (bien Tordu)

            Posté par  . Évalué à 3 (+1/-0).

            Oui le drapeau est supposé indiquer le type de police. Mais je pense que trop de générateurs de PDF ont fait l'erreur et que pour mieux afficher tous les documents qui ont été générés par le passé, il n'y a plus guère de choix.

            Par contre le remplacement des deux n'est nécessaire que à cause du bug de fontconfig. Poppler prend bien en compte le drapeau s'il est présent, mais le nom de police faisait partir en vrille fontconfig sur sa configuration par défaut.

            • [^] # Re: Purée De Fichier (bien Tordu)

              Posté par  . Évalué à 1 (+0/-0). Dernière modification le 22/08/21 à 12:35.

              OK, c'est plus clair. Dommage qu'il faille compenser les erreurs de mauvais générateurs…

              Pour Poppler, de ce que j'ai compris, ce n'était pas exactement le nom mais le fait que l'information de type est écrasée par l'information de taille ou une histoire comme ça (une gestion de surcharge ou de priorisation des diverses informations quoi.)

      • [^] # Re: Purée De Fichier (bien Tordu)

        Posté par  . Évalué à 2 (+1/-0).

        Désolé, pas pu éditer à temps : le titre aurait du être avec Format et non Fichier mais je crois que tout le monde a compris.

  • # Magnifique "Yak shaving"

    Posté par  . Évalué à 10 (+11/-0).

    Je ne connais pas la traduction française de "Yak shaving", mais je trouve que cet article en est une belle illustration:
    on commence par vouloir simplement imprimer une attestation de mutuelle, on fait un détour par les codes sources de plusieurs visionneurs de PDF pour finir par proposer une correction de bogue dans une bibliothèque de gestion des polices d'écritures.

    Bref, merci pour ce journal à la fois divertissant et instructif !

    PS: en anglais un article de blog que j'ai lu hier à ce sujet, où j'ai découvert l'expression "Yak shaving": https://antfu.me/posts/about-yak-shaving

  • # Heuristique

    Posté par  . Évalué à 7 (+5/-0).

    J'ai quelques doutes sur l'intérêt de passer une heuristique « si tous les caractères ont la même taille, alors on va dire que c'est une police à chasse fixe ». Ça permet bien de corriger le souci d'un PDF mal généré mais en même temps ça masque le souci, qui a donc peu de chances d'être remonté aux développeurs de l'outil en question. Y-a-t-il un autre intérêt en dehors de compenser pour un flag incorrect ?

    Au passage, il me semble qu'il est possible d'embarquer les polices dans le PDF. Perso je préfère ça, justement pour éviter les surprises en changeant d'appareil.

    • [^] # Re: Heuristique

      Posté par  . Évalué à 7 (+5/-0).

      Y-a-t-il un autre intérêt en dehors de compenser pour un flag incorrect ?

      Alors la vérification des tailles, non, ça n'a pas d'intérêt. Mais la taille sert en cas de récupération de police de la même famille que la police demandée, mais pas nécessairement de la même taille précisément.

      Au passage, il me semble qu'il est possible d'embarquer les polices dans le PDF. Perso je préfère ça, justement pour éviter les surprises en changeant d'appareil.

      Tout à fait, je le mentionne avec la police Wingdings qui est embarquée dans le document. Mais pour les polices que «tout le monde a», il peut être légitime de ne pas les inclure. C'est le cas notamment de Helvetica et ses clones. Ça permet d'avoir un PDF un peu moins volumineux, même si ça sert moins aujourd'hui…

      • [^] # Re: Heuristique

        Posté par  (site Web personnel) . Évalué à 10 (+10/-0).

        Au passage, il me semble qu'il est possible d'embarquer les polices dans le PDF. Perso je préfère ça, justement pour éviter les surprises en changeant d'appareil.

        En fait, c'est même mieux que ça, il y a trois choix lors de la création :
        - ne pas inclure les polices (fichier léger)
        - inclure les polices utilisées (fichier lourd)
        - inclure uniquement les caractères utilisés (fichier assez léger)

        Perso je code en générant toujours la troisième option, qui est un bon équilibre entre la taille du fichier et l'assurance qu'il soit correctement affiché partout.

        Donc dans le cas du journal, "simplement" (pas toujours simple en réalité, faut savoir toucher les bonnes personnes) dire à l'organisme en question d'inclure les caractères utilisés dans le PDF.

        • [^] # Re: Heuristique

          Posté par  . Évalué à 2 (+2/-1).

          Je n'ai jamais compris la différence entre les deux derniers. Qu'entend-t-on par inclusion de caractères utilisés ?

          • [^] # Re: Heuristique

            Posté par  (site Web personnel) . Évalué à 7 (+5/-0). Dernière modification le 22/08/21 à 08:37.

            Qu'entend-t-on par inclusion de caractères utilisés ?

            Ben exactement ça : il n'y a que les caractères utilisés dans le document, et non l'ensemble des caractères de la police, qui sont intégrés au dit document. Est-ce plus clair.

            « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

            • [^] # Re: Heuristique

              Posté par  . Évalué à 9 (+7/-0).

              Par exemple la police wingdings embarquée dans le document du journal est réduite à seulement 3 caractères. Décompressée, la police ne fait donc plus que 6164 octets en TrueType (ce qui me semble encore beaucoup, mais je ne connais pas assez le format TrueType pour juger). À titre de comparaison, la police NotoSansSymbols, du même style que la wingdings, pèse 252 ko.

            • [^] # Re: Heuristique

              Posté par  . Évalué à 2 (+1/-0).

              J'ai compris les mots …mais pas le détail du fonctionnement. En fait je n'arrive pas à imaginer comment on inclus des bouts de police et si ça marche pour toutes les polices (ou juste les TrueTypes ou autre ?) Faut dire que rien que la notion de police est toute une histoire…
              En tout cas merci pour toutes les réponses. Je vais creuser ça quand j'aurai un peu de temps.

              • [^] # Re: Heuristique

                Posté par  (site Web personnel) . Évalué à 3 (+1/-0). Dernière modification le 23/08/21 à 09:42.

                ça signifie que si dans ton documents lettre "Ù" n'est pas utilisée, ce n'est pas nécessaire de l'embarquer la police de caractères. Du coup, ça peut alléger beaucoup certaines polices, surtout celles très étendues.

                Pourquoi bloquer la publicité et les traqueurs : https://greboca.com/Pourquoi-bloquer-la-publicite-et-les-traqueurs.html

              • [^] # Re: Heuristique

                Posté par  . Évalué à 3 (+1/-0).

                En très schématique, une police, c'est une table de correspondance entre les codes de caractères (en UTF-8, par exemple) et un ou plusieurs dessins représentant la lettre dans la police.

                Si tu inclue toute la police, tu inclue l'intégralité de la table telle quelle, que les caractères soient présents dans le document ou non. Dans l'autre cas, tu filtre la table pour ne garder que les caractère utiles pour rendre ton document.

                • [^] # Re: Heuristique

                  Posté par  (site Web personnel) . Évalué à 3 (+2/-1).

                  Bref, il faut que le document se rende à la police.

                  Python 3 - Apprendre à programmer dans l'écosystème Python → https://www.dunod.com/EAN/9782100809141

              • [^] # Re: Heuristique

                Posté par  (site Web personnel) . Évalué à 10 (+9/-0).

                Cela fonctionne pour les polices Type 1 et les TrueType. Si cela t’intéresse, je t’invite à lire le PDF Reference, qui est vraiment bien foutu (chapitre 5 pour le texte).

                En gros, dans le PDF, tu as la séquence suivante :

                /F13 10 TJ % Utilisation de la police F13 en taille 10
                ( 1 2 3 3 4 ) Tj % Affichage des caractères 1 2 3 4
                

                Puis une table faisant la correspondance :

                25 0 obj 
                <<  /Type /Encoding
                /Differences
                [ 1 H
                  2 e
                  3 l
                  4 o ] 
                >>
                endobj
                

                Du temps où je bossais avec le secteur éditique, les imprimeurs étaient vraiment emmerdés par ces histoires de polices embarquées. Comme chaque page contenait des polices partielles, il n’était pas possible de mutualiser les éléments du PDF sur plusieurs page, car la page 1 contenait uniquement les caractères pour afficher "M. Martin", la page 2 pour afficher "Mme Samia" etc. Au final, la mémoire du contrôleur d’impression explosait en essayant de charger toutes les ressources.

                • [^] # Re: Heuristique

                  Posté par  . Évalué à 4 (+2/-0).

                  Comme chaque page contenait des polices partielles, il n’était pas possible de mutualiser les éléments du PDF sur plusieurs page, car la page 1 contenait uniquement les caractères pour afficher "M. Martin", la page 2 pour afficher "Mme Samia" etc.

                  C'est triste, je présume que ça aide à faire des traitements pages par page (genre sortir une page d'un document), mais j'aurais cru que les définitions de fonts embarquées seraient faites au début, puis simplement référencé.

                  • [^] # Re: Heuristique

                    Posté par  . Évalué à 4 (+2/-0).

                    Ça devrait être effectivement le cas quand tu produit le document en une fois.
                    L'exemple donné ressemble à un cas où chaque page aurait été produite séparément, puis le tout combiné dans un PDF unique sans retravailler le contenu.

                    • [^] # Re: Heuristique

                      Posté par  (site Web personnel) . Évalué à 5 (+3/-0).

                      Notre boulot consistait à récupérer les impressions faite via une imprimante virtuelle, puis envoyer le tout en centre éditique pour mutualiser les couts. Comme cela venait de postes bureautiques multiples, on dépendait de la source d’impression que nous ne controlions pas.

                      On avait mis en place des outils pour fusionner les éléments identiques (font de page, logo, polices complètes), mais ça ne passait pas pour les polices. La seule solution fut de pré-traiter le pdf pour réécrire tous les caractères caractères dans une police complète (vu que le glyphe A pouvait etre codé différement selon les pages). C’était une belle aventure quand j’y repense :)

                • [^] # Re: Heuristique

                  Posté par  . Évalué à 1 (+0/-0).

                  Merci beaucoup : c'est bien plus clair maintenant (même si je suis loin de prétendre à la maîtrise.) La référence est très intéressante à lire (mais il faut du temps pour arriver à digérer la chose.)

                  Merci aussi pour ton retour d'expérience. Comme quoi, il peut être mieux d'inclure toute la police pour avoir un document plus exploitable …sauf si on sait qu'on va traiter des pages individuelles et qu'on en a les moyens.

          • [^] # Re: Heuristique

            Posté par  . Évalué à 3 (+1/-0).

            Ça signifie que les lettres non-utilisées dans le document (par exemple : le 'œ' en italique) ne seront pas ajoutées au PDF.

        • [^] # Re: Heuristique

          Posté par  (site Web personnel) . Évalué à 10 (+11/-0).

          Il existe une 4ème solution : utiliser l’une des 14 polices gérées nativement par les specs du format PDF et censées etre implémentées dans tout lecteur (ou imprimante etc) :

          • Courier (+ Bold, Italique, BoldItalique)
          • Times-Roman (+ Bold, Italique, BoldItalique)
          • Helvetica (+ Bold, Italique, BoldItalique)
          • Symbol
          • ZapfDingbats

          à noter l’absence du New par rapport aux polices de Microsoft, et l’utilisation de la police Helvetica (dite l’originale pour ceux qui connaissent l’histoire d’Arial)

          • [^] # Re: Heuristique

            Posté par  (site Web personnel) . Évalué à 5 (+3/-0).

            Alors, je regarde la liste des mes polices :

            • Courier, avec ou sans News, j'ai pas ;
            • Times-Roman, avec ou sans News, j'ai pas ;
            • Helvetica, j'ai pas ;
            • Symbols, j'ai pas ;
            • ZapgDingbats non plus.

            Et Helvetica n'est pas implémentée par défaut sur Windows. Effectivement, Arial est censée la remplacer, mais elle est moins bien dessinée.

            Pour Linux, les polices de substitution doivent être la série Liberation : Mono, Sans et Serif ainsi qu'OpenSymbol et, là je suis moins sûre de moi Standard Symbols PS (pour remplacer Symbols ?). Elles ont théoriquement exactement la même chasse pour au moins la série Liberation si je ne me trompe.

            « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

            • [^] # Re: Heuristique

              Posté par  (site Web personnel) . Évalué à 2 (+1/-0).

              Avoir les 10 polices n'est en effet déjà pas gagné, mais quid de leur contenu ? je doute qu'elles soient toutes unicodes complètes (en fait, je suis certain qu'elles ne le sont pas, même en laissant de côté Symbols et ZapgDingbats), donc s'il n'y a pas de problème pour l'anglais et quelques variations de charset proches, ça doit merdouiller copieusement pour l'arabe, le mongolien, le chinois, le japonnais, et bien d'autres…
              Bref, ce n'est pas une bonne idée de compter sur ces 10 polices, mieux vaux inclure les caractères utilisés, voire la police complète si c'est un roman.

              • [^] # Re: Heuristique

                Posté par  (site Web personnel) . Évalué à 1 (+1/-2). Dernière modification le 24/08/21 à 09:45.

                On est d'accord et ce d'autant plus que ces polices ne sont pas forcément ce qui se fait de mieux. Je dirais que, pour des formulaires administratifs dans des langues qui s'écrivent avec des caractères latins, ça va. Mais pas plus.

                En fait c'est tout le problème que tu mets bien en valeur de l’ethnocentrisme étroit de certaines normes informatiques.

                « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

                • [^] # Re: Heuristique

                  Posté par  . Évalué à -1 (+2/-5). Dernière modification le 24/08/21 à 17:23.

                  En fait c'est tout le problème que tu mets bien en valeur de l’ethnocentrisme étroit de certaines normes informatiques.

                  Pour le coup je ne suis pas vraiment d'accord. Si tu inclus dans la norme une police CJK (il manquera toujours l'arabe, l'hébreu et sans doute d'autres) tu ajoute une centaine de Mio à tous lecteur qui veut gérer la norme.

                  Sachant que la majorité des langues utilisant ces glyphes ont aussi un système d'écriture en 26 caractères latin, je trouve que ne pas l'inclure dans le standard tout en permettant son usage (ceux qui utilisent ces langues apprennent très vite à intégrer les polices avec).

                  Pour le coup leur système d'écriture est suffisamment contraignant pour que ça ai du sens.

            • [^] # Re: Heuristique

              Posté par  . Évalué à 3 (+2/-0).

              Si elles sont « censé » être présentes, cela suppose que les lecteurs requièrent les paquets des polices de substitutions qu'elles gèrent ?

              Sinon oui, c'est bien la série Liberation qui libère les équivalents.

            • [^] # Re: Heuristique

              Posté par  . Évalué à 5 (+3/-0).

              Et Helvetica n'est pas implémentée par défaut sur Windows. Effectivement, Arial est censée la remplacer, mais elle est moins bien dessinée.
              Pour Linux, les polices de substitution doivent être la série Liberation : Mono, Sans et Serif ainsi qu'OpenSymbol

              L'OS n'est pas l'afficheur de PDF. Sous Windows, Acrobat Reader embarque un panel de police, par exemple AdobePiStd qui remplace la ZapgDingbats. Mupdf embarque les Nimbus Sans en remplacement d'Helvetica. Nimbus Sans qui, au passage, ne gère pas que les langues occidentales, fort heureusement.
              C'est un choix d'implémentation de poppler que de ne pas embarquer de polices, ce qui peut poser problème du coup.

              • [^] # Re: Heuristique

                Posté par  . Évalué à 1 (+0/-0).

                Comme on est dans un seul système (i.e. le truc n'est pas multi-plateforme à ma connaissance), Poppler peut avoir des dépendances.

                • [^] # Re: Heuristique

                  Posté par  . Évalué à 2 (+0/-0).

                  Popple tourne sous Linux, Windows, MacOS. Il peut avoir des dépendances, ce n'est pas lié. Par défaut sous Debian en tout cas, poppler ne dépend pas spécifiquement d'un ensemble de polices. poppler-data suggère d'installer un ensemble de polices pour les langues asiatiques, mais c'est tout. Après, via les dépendances à fontconfig il va dépendre d'un ensemble de polices, mais c'est au choix de la distribution. fontconfig ne m'oblige pas à avoir de police de symboles par exemple.

                  • [^] # Re: Heuristique

                    Posté par  . Évalué à 1 (+0/-0).

                    Ah OK, je ne savais pas pour W et M.

                    Pour les dépendances je disais justement que c'est dommage que ça ne s'assure pas de pouvoir respecter la norme (ça peut être directement en exigeant font-liberation/ttf-mscorefonts-installer pour Debian et d'autres, ou indirectement si c'est à faire au niveau de fontconfig ou xorg…) En gros, je vois comme un loupé.

        • [^] # Re: Heuristique

          Posté par  . Évalué à 2 (+1/-0).

          Je n'ai pas l'habitude de voir ce choix proposé lors de la génération d'un PDF ! Vous savez faire ce type de choix pour

          • impression d'un page web ou d'un PDF depuis Firefox ?
          • création d'un PDF depuis des sources tex ?
          • [^] # Re: Heuristique

            Posté par  (site Web personnel) . Évalué à 7 (+5/-0).

            Dans Firefox non, mais je ne sais pas si c'est nécessaire.

            Dans LibreOffice, tu définis ça dans les propriétés du fichier et, si tu as paramétré ton logiciel pour afficher les propriétés à l'enregistrement et que tu as pris la bonne habitude de ne pas exporter directement le fichier en pdf mais d'afficher les options, c'est demandé au moment de l'export en pdf. Plus précisément, tu précises ça dans l'onglet Polices de la boite de dialogue Propriétés.

            Pour la création d'un PDF depuis des sources tex, ça dépend comment tu le crées non ? Si tu fais ça en ligne de commande ou en passant par un logiciel graphique.

            « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

            • [^] # Re: Heuristique

              Posté par  . Évalué à 1 (+0/-0). Dernière modification le 22/08/21 à 20:46.

              Merci pour les informations liées à LibreOffice. En général, pour les sources tex, les logiciels graphiques (TexMaker) passent par pdflatex, mais je ne trouve rien dans la page man correspondante. On trouve des solutions sur internet liées à la commande \pdfmapfile, c'est gérable, mais c'est loin d'être trivial !

              • [^] # Re: Heuristique

                Posté par  (site Web personnel) . Évalué à 4 (+2/-0). Dernière modification le 22/08/21 à 21:11.

                Ouvre un pdf créé à partir d'un fichier tex, vas dans Okular (ou un autre lecteur de pdf) dans Fichier > Propriétés et regarde dans l'onglet Polices. Il est possible que ça se fasse de toute façon sans que tu aies quoique ce soit à faire.

                En théorie le format pdf fait ça.

                « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

              • [^] # Re: Heuristique

                Posté par  . Évalué à 3 (+2/-0).

                pdflatex intègre les fontes utilisées dans le document à l'exception des 14 fontes de base définies par le standard. Pour celles ci, voir l'option pdftexDownloadBase14 dans le fichier updmap.cfg.

  • # un mystère résolu

    Posté par  . Évalué à 10 (+13/-0).

    J'ai trouvé cet article trés bien construit et intéressant. J'avoue m'être demandé à de nombreuses reprises pourquoi les lettres se marchaient dessus, maintenant je sais.

  • # Merci

    Posté par  . Évalué à 7 (+5/-0).

    Les relevés PDF de ma banque ont un rendu raté aussi, je vais essayer d'appliquer la technique pour trouver le problème, merci !

  • # Humm....

    Posté par  . Évalué à 7 (+6/-0). Dernière modification le 23/08/21 à 14:10.

    Es tu certain que cela fonctionne comme tu le penses?

    Ce qui m'embête c'est ça :

    # fc-match "AAAAAAAA:lang=xx" file family
    Bitstream Vera Sans Mono:file=/usr/share/fonts/truetype/ttf-bitstream-vera/VeraMono.ttf

    Avec ton fichier de config, j'ai donc l'impression que toutes les polices inconnues deviennent monospace. Je n'ai pas trouvé de PDF où cela se produit mais en cherchant un peu j'ai trouvé ce post daté de quelques mois et décrivant le même problème.

    https://unix.stackexchange.com/questions/651692/fontconfig-set-every-font-with-spacing-100-as-monospace

    • [^] # Re: Humm....

      Posté par  . Évalué à 10 (+10/-0).

      Après quelques tests, il me semble que le problème vient du paramêtre qual="all" dans le test du spacing. Cela fonctionne correctement en l'enlevant ou en remplaçant all par any (la valeur par défaut).

      En théorie, qual=all permet de comparer la propriété à plusieurs valeurs. Cela n'a pas vraiment de sens pour un entier donc j'imagine que le cas n'a été ni programmé ni testé correctement dans Fontconfig.

  • # pdffonts

    Posté par  . Évalué à 10 (+10/-0).

    Pour info, pdffonts est un des outils de manipulation des PDF fournit par poppler-utils (Debian). L'option -subst permet d'obtenir facilement les substitutions pour les fontes 'not-embedded'. Par exemple

    (bash) pdffonts -subst test-pdf-fonts-anon.pdf 
    
    
     name                                 object ID substitute font                      substitute font file
    ------------------------------------ --------- ------------------------------------ ------------------------------------
    Calibri-Bold                              8  0 Carlito Bold                         /usr/share/fonts/truetype/crosextra/Carlito-Bold.ttf
    Arial-BoldMT                              9  0 Liberation Sans Bold                 /usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf
    ArialMT                                  10  0 Bitstream Vera Sans                  /usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf
    Calibri                                  11  0 Carlito                              /usr/share/fonts/truetype/crosextra/Carlito-Regular.ttf
    Monospace821BT-Roman                     12  0 Bitstream Vera Sans                  /usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf
    • [^] # Re: pdffonts

      Posté par  . Évalué à 10 (+10/-0).

      Et une fois que l'on a identifié la fonte posant problème (donc Monospace21BT avec Roman), on peut alors rajouter un alias dans /etc/fonts/conf.d ou localement dans ~/.config/fontconfig/conf.d/

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE fontconfig SYSTEM "../fonts.dtd">
      <fontconfig>
        <alias binding="same">
            <family>Monospace821BT</family>
            <accept>
               <family>Monospace</family>
            </accept>
        </alias> 
      </fontconfig>
  • # Merci

    Posté par  (site Web personnel) . Évalué à 4 (+2/-0).

    Les contributions au logiciel libre, c'est la vie!

  • # Un petit oubli ?

    Posté par  . Évalué à 2 (+2/-0).

    Bonjour,

    Comme les autres, bravo pour ce sujet, bien analysé, bien documenté, etc.

    Un tout petit regret, cependant, à propos de la source :

    Bon. Ouvrons le PDF avec Okular (une version anonymisée est disponible sur https://rock.pinaraf.info/~pinaraf/dlfp-pdf/test-pdf-fonts-anon.pdf). Le texte de l'adresse est effectivement dégueulasse. Le MA de MADAME est tout compressé, avec le M qui mord sur le A (ou l'inverse, je n'ai jamais étudié la férocité relative des lettres). De même sur le DA ou le D mord le A. C'est très sale.

    Merci pour le lien vers le fichier, mais sans doute qu'une image du texte défectueux aurait été utile car chez moi (vieux Linux Debian Wheezy, vieux FF) le MA de MADAME s'affiche correctement, tout comme le reste.
    Or (je viens de vérifier) je n'ai pas la police Monospace821BT dans ma machine.

    Ou alors c'est l'anonymisation qui a modifié des paramètres dans le fichier ?
    jp

    • [^] # Re: Un petit oubli ?

      Posté par  . Évalué à 7 (+5/-0).

      Merci pour le lien vers le fichier, mais sans doute qu'une image du texte défectueux aurait été utile car chez moi (vieux Linux Debian Wheezy, vieux FF) le MA de MADAME s'affiche correctement, tout comme le reste.

      Vous avez tout à fait raison, monsieur le premier ministre.

      Sur Firefox en tout cas, c'est normal que tout s'affiche bien.
      Sur Okular, ça donne ça :

      adresse kaput

      • [^] # Re: Un petit oubli ?

        Posté par  . Évalué à -1 (+0/-1). Dernière modification le 25/08/21 à 19:18.

        Bonsoir,

        Sur Firefox en tout cas, c'est normal que tout s'affiche bien.

        Pourquoi ? J'ai tout bien lu ce matin, et n'ai pas capté comment FF s'y prend.

        Bon, je vais relire…

        Vous avez tout à fait raison, monsieur le premier ministre.

        ???????

        Regards,

        • [^] # Re: Un petit oubli ?

          Posté par  . Évalué à 1 (+0/-0).

          J'ai lu en diagonale, et je pense que tu utilise une version de FF assez récente …qui ne fait pas appel à une extension d'un lecteur externe mais à son traitement interne de type pdfjs (pour lequel le problème ne se pose pas)

        • [^] # Re: Un petit oubli ?

          Posté par  . Évalué à 4 (+2/-0).

          Pourquoi ? J'ai tout bien lu ce matin, et n'ai pas capté comment FF s'y prend.

          Firefox a son afficheur, pdf.js, qui calcule bien que la police demandée est en chasse fixe, et assigne donc dans les propriétés CSS un font-family: monospace.

          Vous avez tout à fait raison, monsieur le premier ministre.

          ???????

          J'allais dire juste «vous avez tout à fait raison», et forcément j'ai pas pu m'empêcher, il a fallu que je complète la citation… (cette phrase est issue du débat des deux tours de la présidentielle de 88)

          • [^] # Re: Un petit oubli ?

            Posté par  . Évalué à 2 (+2/-0). Dernière modification le 26/08/21 à 11:29.

            Bonjour,

            Firefox a son afficheur, pdf.js

                $ locate pdf.js
                $

            Donc, non, en ce qui me concerne. Par contre, Gil cot a peut-être mis le doigt là où il fallait :

            ne fait pas appel à une extension d'un lecteur externe mais à son traitement interne de type pdfjs

            Comment s'en assurer ?

            Par ailleurs (on ne peut pas faire de multi-citations dans ce forum ?),

            je pense que tu utilises une version de FF assez récente

            À propos me donne 52.6.0 (32 bits), first offered to ESR channel users on January 23, 2018

            Est-ce que c'est assez récent (3 ans et demi) ?

            J'allais dire juste «vous avez tout à fait raison», et forcément j'ai pas pu m'empêcher, il a fallu que je complète la citation… (cette phrase est issue du débat des deux tours de la présidentielle de 88)
            Et comme je ne suis pas spécialiste de la politique et que je sors d'une sale affaire sur un autre forum, ça m'a grave déstabilisé.

            Bonne journée,

            • [^] # Re: Un petit oubli ?

              Posté par  . Évalué à 4 (+2/-0).

              La notion de "assez récente" est très relative. Cela fait 8 ans que Firefox a son afficheur PDF intégré, depuis la version 19…
              Tu ne le trouveras pas par un locate, il est planqué dans un fichier de ressources.

              $ unzip -l /usr/lib/firefox-esr/browser/omni.ja | grep pdf.js
              284283 2010-01-01 00:00 chrome/pdfjs/content/build/pdf.js

              • [^] # Re: Un petit oubli ?

                Posté par  . Évalué à 1 (+1/-0).

                Tu ne le trouveras pas par un locate, il est planqué dans un fichier de ressources.

                Merci à toi pour tes renseignements bien détaillés.

                $ unzip -l /usr/lib/firefox-esr/browser/omni.ja | grep pdf.js
                284283 2010-01-01 00:00 chrome/pdfjs/content/build/pdf.js

                L'informatique c'est magnifique :

                $ unzip -l /usr/lib/firefox-esr/browser/omni.ja | grep pdf.js
                warning [/usr/lib/firefox-esr/browser/omni.ja]: 12969884 extra bytes at beginning or within zipfile
                (attempting to process anyway)
                error [/usr/lib/firefox-esr/browser/omni.ja]: reported length of central directory is
                -12969884 bytes too long (Atari STZip zipfile? J.H.Holm ZIPSPLIT 1.1
                zipfile?). Compensating...
                343081 2010-01-01 00:00 chrome/pdfjs/content/build/pdf.js

                • [^] # Re: Un petit oubli ?

                  Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

                  pdf.js a eu pas mal d'évolutions dans les dernières versions de Firefox:

                  • acroform;
                  • xfa;
                  • tagged pdf (pour l'accessibilité);
                  • support Javascript dans les PDFs;
                  • pas mal de corrections de bugs liés aux polices;
                  • etc

                  Donc, il vaut mieux utiliser la release de Firefox pour tester (ou nightly ou beta) plutôt que de vielles versions (ou ESR).

  • # la carte tiers payant toujours illisible

    Posté par  . Évalué à 2 (+1/-0).

    Rien n’est parfait, en tout cas, la carte tiers payant que SwissLife m’envoie tous les ans s’affiche très mal dans le lecteur par défaut du bureau d'Ubuntu et depuis la 16.04 à la 20.04. Par contre, le lecteur de Firefox l'affiche bien, mais à l'impression c'est aussi illisible (il doit demander au lecteur pas défaut), du coup je dois utiliser un PC sous Windows ! De plus, le lecteur de document d'Ubuntu Touch l'affiche aussi très bien…
    Lorsque j'ai fait le commentaire à SwissLife, sur leur police utilisée, la personne m'a simplement renvoyé une autre copie !

    • [^] # Re: la carte tiers payant toujours illisible

      Posté par  . Évalué à 3 (+1/-0).

      Pour l'impression, tu as essayé l'impression PDF de firefox ? (oui imprimer un pdf en pdf pour ensuite pouvoir l'imprimer…) C'est embêtant mais moins que changer de passer sous windows pour ça.

      • [^] # Re: la carte tiers payant toujours illisible

        Posté par  . Évalué à 2 (+1/-0).

        L'impression PDF de Firefox produit un PDFs contenant une image bitmap avec une résolution de 300 ppi. La qualité d'impression sera donc assez moyenne mais c'est probablement suffisant pour un relevé bancaire ou une attestation de la sécu.

    • [^] # Re: la carte tiers payant toujours illisible

      Posté par  . Évalué à 5 (+3/-0).

      Par contre, le lecteur de Firefox l'affiche bien, mais à l'impression c'est aussi illisible

      Le système d'impression utilise nativement du PDF. Du coup il est possible que Firefox fasse juste passe-plats pour imprimer le document, qui du coup est rendu derrière par le système d'impression ou l'imprimante directement.
      Firefox n'a pas dans ses options la même option qu'a Okular lors de l'impression, à savoir en anglais «Force rasterization», qui convertit le PDF en images pour l'imprimante, contournant moultes bugs dans cette dernière du coup (mon record c'est une imprimante qui a imprimé une stack trace au milieu du document).

      En lecteur alternatif, je recommande vivement mupdf (que je n'avais jamais essayé avant d'écrire ce journal) qui a un moteur de rendu à part entière.

      Si tu veux tu peux m'envoyer en privé le document pour que je vois pourquoi il s'affiche mal (mais s'agissant de données de santé je doute que ce soit conseillé), mon adresse mail est la même que mon adresse XMPP… Sinon les astuces de ce journal ou des commentaires devraient aider.

  • # Liens non permanent

    Posté par  . Évalué à 5 (+5/-0).

    Petite note pour dire qu'utiliser des liens github qui mentionne master n'est pas très viable, il suffit de qq changements sur le projet dans les fichiers linkés pour que les liens ne soient plus à jour !

    Utilisez des permalinks plutôt !
    (dans l'interface github, quand on sélectionne 1+ lignes, les 3 points à gauche montre un menu qui permet d'avoir un permalink)

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.