Typographisme

Un site tenu par des amoureux de la typo, sur le Web comme ailleurs.

Bonnes pratiques pour les déclarations @font-face

De vous à moi, je crois qu’on peut se le dire, 2010 aura été l’année du retour en force des polices typographiques embarquées dans les navigateurs. Les déclarations @font-face fleurissent un peu partout, et nous ne nous en plaindrons pas. Ceci dit, la compatibilité multi-navigateur de ces déclarations est parfois un peu problématique. Voyons comment s’en sortir techniquement parlant.

Petit rappel historique

À l’origine, les déclarations CSS @font-face faisaient partie de la norme CSS 2.0 et étaient supportées aussi bien par Internet Explorer 6 que par Netscape 4. Nous étions alors à la fin des années 90 et la guerre entre ces deux navigateurs faisait rage. Malheureusement, les constructeurs ainsi que les fondeurs ne parvinrent pas à se mettre d’accord à l’époque sur un format de fonte et le haut débit n’étant pas encore généralisé, cette technologie ne fut que peu ou pas utilisée. En conséquence, lors de l’écriture de la norme CSS 2.1, cette fonctionnalité fut purement et simplement supprimée.

Avec CSS 3, c’est le grand retour en force des fontes web à travers le module CSS Fonts qui redéfinit la méthode d’insertion des fontes personnalisées dans les navigateurs. Les spécifications c’est bien, mais comme d’habitude avec CSS, il faut voir au cas par cas ce que cela donne avec les différents navigateurs.

Déclaration simplifiée

Une déclaration simple ressemble à ça :

@font-face{
    font-family : "maFonte";
    src : url('maFonte.ttf');
}

La propriété font-family permet de définir le nom de la fonte tel qu’il sera utilisé par la suite dans les déclarations de style. La propriété src quant à elle permet de spécifier le fichier source de la fonte qui sera utilisé par le navigateur pour rendre la police. L’utilisation de la police se fera très simplement de la façon suivante :

p {
    font-family : "maFonte", Helvetica, Arial, sans-serif;
}

Gestion des formats de fonte

Le problème de l’exemple précédent, c’est qu’il ne fonctionnera qu’avec les navigateurs qui supportent les polices au format TrueType. Hélas, tous les navigateurs ne supportent pas ce format particulier. Donc, pour vous assurez que votre rendu typographique sera cohérent [1] entre les divers navigateurs, vous devez proposer vos fontes dans un format compréhensible par chacun d’entre eux. Si ce n’est pas possible, les règles de la cascade CSS s’appliqueront et, dans le cas de l’exemple précédente le texte s’affichera alors en Helvetica, Arial ou, en dernier ressort, dans la fonte sans empattement par défaut disponible.

Il n’y a qu’une seule façon de déclarer plusieurs fichiers alternatifs pour une même fonte, c’est en séparant les déclarations d’url par une virgule (pratique assez courante avec CSS3).

@font-face{
    font-family : "maFonte";
    src : url('maFonte.woff') format('woff'),
    url('maFonte.svg#abcd') format('svg'),
    url('maFonte.ttf') format('truetype');
}

Comme vous pouvez le voir, la propriété src requiert l’url d’un fichier de fonte et peut comporter une déclaration de format. Déclarer le format permet de dire explicitement aux navigateurs le type de fonte qui se cache derrière l’url d’un fichier. La déclaration de format n’est pas obligatoire mais si vous ne la rajoutez pas, le navigateur va devoir télécharger la fonte pour vérifier son format, ce qui du point de vue des performances (et donc du rendu de vos polices) peut être vite catastrophique.

Notez la présence de l’ancre nommée sur l’url de la police SVG. Il est nécessaire pour Safari afin de lui dire explicitement où, dans le fichier SVG, se trouve la déclaration de police. À première vue, ça peut paraitre un peu idiot, mais il faut se rappeler qu’un fichier SVG peut contenir bien d’autres choses que des polices de caractères ou bien en contenir plusieurs.

Notez également, l’ordre de déclaration des formats : d’abord WOFF, puis SVG et enfin TrueType. Il est important de respecter cet ordre-ci car certains navigateurs savent utiliser plusieurs de ces formats. Or ils utiliseront le premier qu’ils comprennent dans la liste. Il est donc important de bien déclarer vos formats du plus léger au plus lourd pour des questions de performances (SVG est plus léger que TrueType si vous faîtes attention à le servir en version compressée).

Le cas Internet Explorer

Le type de déclaration vu précédemment va marcher avec tous les navigateurs sauf Internet Explorer (ne riez pas). Certes d’une part cette déclaration ne comporte pas le seul format connu par IE (le format EOT) mais de toute façon, IE ne comprend pas la syntaxe CSS3, à base de déclarations multiples séparées par des virgules, utilisée pour la propriété src. Néanmoins, on peut facilement utiliser les règles de la cascade CSS pour fournir un fichier alternatif pour IE.

@font-face{
    font-family : "maFonte";

    /* pour IE */
    src : url('maFonte.eot');

    /* pour les autres */
    src : url('maFonte.woff') format('woff'),
    url('maFonte.svg#abcd') format('svg'),
    url('maFonte.ttf') format('truetype');
}

Mais même comme cela ce n’est toujours pas suffisant. En effet, malgré les règles de la cascade CSS, IE souffre d’un bug qui va le pousser à essayer de comprendre la deuxième déclaration src et il va donc envoyer une requête HTTP vers : maFonte.woff)%20format('woff'),%20url(m [...] tf)%20format('truetype ! Je sais, c’est assez surprenant, mais il y a longtemps que j’ai appris à ne plus m’étonner des bugs de IE.

Attention, ce qui suit sur la fonction local() n’est plus d’actualité, reportez vous à la mise à jour en fin d’article pour en savoir plus

Pour résoudre ce problème, il suffit d’utiliser l’appel aux fontes locales via la fonction CSS local(). Cette fonction CSS permet de dire à un navigateur d’aller chercher une police dans la liste des polices locales installées sur la machine de l’utilisateur

@font-face{
    font-family : "maFonte";

    src : url('maFonte.eot');

    src : local('maFonte'),
    url('maFonte.woff') format('woff'),
    url('maFonte.svg#abcd') format('svg'),
    url('maFonte.ttf') format('truetype');
}

Voila, comme IE ne connait pas la fonction local(), il va considérer que la deuxième déclaration n’est pas valide pour lui et, en respectant la cascade CSS, il va se rabattre sur la première déclaration qui, elle, est valide pour lui. Oui, mais nos ennuis ne sont pas tout à fait finis.

Le problème avec les appels aux fontes locales, c’est que rien ne vous garantit que la police locale corresponde bien à celle que vous voulez utiliser. Donc, si vous voulez garder un minimum de contrôle sur l’affichage de vos polices typographiques, il faut vous assurer d’appeler une police locale qui n’existe pas. Le problème vient du fait que d’une part, n’importe quelle suite de caractères peut faire référence à une police installée localement, et d’autre part, la fonction local() doit impérativement contenir une chaine de caractères. Heureusement, la solution vient cette fois d’une restriction existant chez les Mac. En effet, la spécification OpenType définie clairement que la plate-forme Macintosh d’Apple ne peut pas avoir de fonte nommée sur 2 bytes. Il est donc hautement improbable qu’une police ait un jour un nom aussi court. On peut donc utiliser n’importe quel caractère Unicode codé sur 2 bytes pour définir un appel à une fonte locale qui n’existe pas. Et tant qu’à faire, puisque tout marche enfin, pourquoi ne pas utiliser ce caractère : ☺

@font-face{
    font-family : "maFonte";

    src : url('maFonte.eot');

    src : local('☺'),
    url('maFonte.woff') format('woff'),
    url('maFonte.svg#abcd') format('svg'),
    url('maFonte.ttf') format('truetype');
}

Les alternatives

Maintenant qu’on s’en est sorti avec les différents formats de fontes, vous avez peut-être envie de gras et d’italique ? @font-face permet de déclarer ces alternatives de manière explicite, mais tous les navigateurs ne les gèrent pas correctement.

La manière la plus simple de gérer vos alternatives consiste à avoir une déclaration @font-face pour chaque police avec un nom différent.

@font-face{
    font-family : "maFonte";

    src : url('maFonte.eot');
    src : local('☺'),
    url('maFonte.woff') format('woff'),
    url('maFonte.svg#abcd') format('svg'),
    url('maFonte.ttf') format('truetype');
}

@font-face{
    font-family : "maFonteGras";

    src : url('maFonteGras.eot');
    src : local('☺'),
    url('maFonteGras.woff') format('woff'),
    url('maFonteGras.svg#abcd') format('svg'),
    url('maFonteGras.ttf') format('truetype');
}

p {
    font-family : "maFonte", Helvetica, Arial, sans-serif;
}

p strong {
    font-family : "maFonteGras", Helvetica, Arial, sans-serif;
    font-weight : normal;
}

Le problème de cette approche, c’est que si vous êtes dans un contexte où vous ne maîtrisez pas absolument votre style (avec un CMS par exemple) vous pouvez vous retrouver avec des polices affichées avec des faux gras ou des faux italiques, ce qui est généralement assez moche. D’un autre côté, dans l’exemple de code précédent, si pour une raison ou une autre, la police myFonteGras n’est pas chargée, vous perdez le gras sur les polices de substitution.

Heureusement, @font-face dispose d’un mécanisme de gestion des alternatives assez simple pour résoudre ces questions. Il vous suffit de rajouter les propriétés font-weight (pour le gras) font-variant (pour les petites capitales) et font-style (pour l’italique) dans vos déclarations.

@font-face{
    font-family : "maFonte";

    src : url('maFonte.eot');
    src : local('☺'),
    url('maFonte.woff') format('woff'),
    url('maFonte.svg#abcd') format('svg'),
    url('maFonte.ttf') format('truetype');

    font-weight:normal;
    font-style:normal;
    font-variant:normal;
}

@font-face{
    font-family : "maFonte";

    src : url('maFonteGras.eot');
    src : local('☺'),
    url('maFonteGras.woff') format('woff'),
    url('maFonteGras.svg#abcd') format('svg'),
    url('maFonteGras.ttf') format('truetype');

    font-weigth : bold;
}

Normalement, vous ne devriez pas avoir besoin de déclarer les variantes normal, mais il s’avère que cela est nécessaire pour Safari et Chrome qui, sinon, ont du mal à gérer les alternatives. En fait, de manière générale, cette méthode est encore très buggée dans la plupart des navigateurs et pas du tout supportée par Internet Explorer. C’est donc à vous de voir dans quels cas de figure vous tolérerez du faux gras ou du faux italique ou pas.

Conclusion

Le support de @font-face étant ce qu’il est à l’heure actuelle, il restera toujours nécessaire de faire des tests multi-navigateurs pour s’assurer du bon rendu de vos polices typographiques. Ceci étant, avec le contenu de cet article vous avez un terrain solide sur lequel vous reposer. De manière générale, je vous invite à utiliser le générateur de fontes du site fontsquirrel.com qui vous permet de packager vos polices pour le web aux petits oignons et qui vous générera en plus les déclarations de styles qui vont bien.

Cet article est fortement inspiré des articles de Paul Irish sur le même sujet. Si vous voulez plus de détails sur les contraintes techniques qu’imposent chaque navigateur en terme de support de @font-face je ne saurais trop vous recommander d’y jeter un coup d’œil :

Une autre excellente ressource à lire, l’article de Tim Brown (en anglais) : How to use CSS @font-face

MAJ du 4 Fevrier 2011

Depuis que j’ai écris cet article, des bugs on été trouvés liés à l’usage de la fonction local() sous Androïd et donc, cette méthode de déclaration n’est plus recommandée. Heureusement, des solutions ont été trouvé pour résoudre ce souci. Dans un premier temps, les principaux fournisseurs de fontes en ligne se sont rabattus sur la solution que proposait Richard Fink qui marche très bien mais est un peu “moche”. Néanmoins, les gens de chez FontSpring ont trouvé une solution à la fois plus élégante et vraiment passe partout :

@font-face{
    font-family : "maFonte";

src : url('maFonte.eot'); /* Pour IE9 Compat mode */
    src : url('maFonte.eot?') format('eot),
    url('maFonte.woff') format('woff'),
    url('maFonte.svg#abcd') format('svg'),
    url('maFonte.ttf') format('truetype');
}

Comme je l’explique ci-avant, les version d’Internet Explorer 6, 7 et 8 souffrent d’un bug de parsing qui leur font croire que l’adresse de la fonte est la chaine de caractère comprise entre la première parenthèse ouvrante et la toute dernière parenthèse fermante de la déclaration src. Le coup de génie de FontSpring a été de rajouter un “?” à la fin de l’adresse de la fonte EOT. De cette manière, les vieilles versions de IE vont bien charger le fichier de police et tout ce qui sera après le “?” ne servira à rien mais évitera un appel vers une adresse qui n’existe pas puisque ce sera compris comme étant un paramètre d’URL.

Vous noterez qu’il est requis de préciser le format des fontes, y compris celui de la fonte EOT pour que les autres navigateurs ne la charge pas “pour voir s’ils la comprennent”. Par contre, ce qu’il faut savoir, c’est que le vrai nom de format pour les fontes EOT, ce n’est pas “eot” mais “embedded-opentype”. Pourquoi ne pas utiliser le vraie nom de format me diriez-vous ? Et bien c’est tout simplement pour IE9. En effet, IE9 peut utiliser aussi bien les fontes EOT que les fontes WOFF. Si vous voulez qu’il utilise les fontes WOFF, vous devez faire en sorte qu’il ignore la fonte EOT. Comme IE9 comprend les déclarations de fichier multiple de la propriété src, il suffit juste de fournir un nom de format qu’il ne comprendra pas pour la fonte EOT et il passera alors à la fonte suivante dans la liste, à savoir dans ce cas, la fonte WOFF.

  1. Enfin, à peu près cohérent, je ne vous parlerai pas ici des différents types de rendu typographique qui peuvent fortement varier selon le moteur de rendu utilisé, qui change en fonction des différentes combinaisons : navigateur + OS. [retour]

dans Design Web Par Jeremie