lister toutes les polices que le navigateur d'un utilisateur peut afficher

105

Existe-t-il un moyen en javascript pour obtenir les noms de toutes les polices (ou familles de polices) que le navigateur peut afficher? (Je veux donner à l'utilisateur une liste déroulante avec une liste de toutes les polices disponibles et permettre à l'utilisateur de choisir une police.) Je préférerais ne pas avoir à coder en dur cette liste à l'avance ou à l'envoyer à partir du serveur. (Intuitivement, il semble que le navigateur devrait savoir quelles polices il possède et cela devrait être exposé à javascript d'une manière ou d'une autre.)

mattsh
la source

Réponses:

38

La version JavaScript est un peu floconneuse. Il obtient les polices en parcourant les polices connues et en les testant.

Le moyen le plus précis (même s'il doit utiliser un plugin propriétaire) est d' utiliser Flash . Ici, vous pouvez obtenir la liste des polices sans avoir à les tester individuellement à l'aide de dimensions.

Vous allez devoir décider d'avoir une liste exacte au détriment de ne pas travailler sur certains appareils (iDevices, navigateurs sans plugin Flash, etc.), ou une liste partielle avec un meilleur support via JavaScript uniquement .

Alex
la source
30
@Jared Pour avoir mentionné Flash? Je n'ai pas dit que c'était la seule solution, j'ai mentionné que c'était le moyen le plus précis de détecter les polices.
alex
4
@alex Oui. Cela pourrait donner une mauvaise impression aux développeurs, en particulier aux nouveaux. Je suggère d'éditer votre réponse pour mieux expliquer les avantages et les inconvénients de l'utilisation de Flash, peut-être simplement "Ce n'est pas recommandé, mais ..." ou quelque chose comme ça.
Jared
19
@Jared Dois-je écrire toutes mes réponses pour fournir des informations de A à Z aux lecteurs dans la chance qu'ils sont nouveaux dans le métier? J'ai expliqué que Flash nécessite un plugin propriétaire, mais j'ai également mentionné que c'est actuellement le seul moyen d'obtenir toutes les polices disponibles (la méthode JavaScript détecte simplement un sous-ensemble de polices, ce qui est probablement suffisant pour la plupart des cas d'utilisation). Je ne suis pas non plus content de devoir utiliser Flash, mais c'est tout ce que nous avons pour le moment pour cette tâche.
alex le
7
@Jared Vous voyez ce dernier paragraphe? Vous voudrez peut-être le relire.
alex le
10
@Jared Ce paragraphe a toujours existé.
alex
73

Oui il y a! Je suis si heureux que vous ayez posé cette question parce que je veux maintenant l'utiliser aussi.

+1 pour la question, et voici votre réponse :)

http://www.lalit.org/lab/javascript-css-font-detect

Code de http://www.lalit.org/wordpress/wp-content/uploads/2008/05/fontdetect.js?ver=0.3

/**
 * JavaScript code to detect available availability of a
 * particular font in a browser using JavaScript and CSS.
 *
 * Author : Lalit Patel
 * Website: http://www.lalit.org/lab/javascript-css-font-detect/
 * License: Apache Software License 2.0
 *          http://www.apache.org/licenses/LICENSE-2.0
 * Version: 0.15 (21 Sep 2009)
 *          Changed comparision font to default from sans-default-default,
 *          as in FF3.0 font of child element didn't fallback
 *          to parent element if the font is missing.
 * Version: 0.2 (04 Mar 2012)
 *          Comparing font against all the 3 generic font families ie,
 *          'monospace', 'sans-serif' and 'sans'. If it doesn't match all 3
 *          then that font is 100% not available in the system
 * Version: 0.3 (24 Mar 2012)
 *          Replaced sans with serif in the list of baseFonts
 */

/**
 * Usage: d = new Detector();
 *        d.detect('font name');
 */
var Detector = function() {
    // a font will be compared against all the three default fonts.
    // and if it doesn't match all 3 then that font is not available.
    var baseFonts = ['monospace', 'sans-serif', 'serif'];

    //we use m or w because these two characters take up the maximum width.
    // And we use a LLi so that the same matching fonts can get separated
    var testString = "mmmmmmmmmmlli";

    //we test using 72px font size, we may use any size. I guess larger the better.
    var testSize = '72px';

    var h = document.getElementsByTagName("body")[0];

    // create a SPAN in the document to get the width of the text we use to test
    var s = document.createElement("span");
    s.style.fontSize = testSize;
    s.innerHTML = testString;
    var defaultWidth = {};
    var defaultHeight = {};
    for (var index in baseFonts) {
        //get the default width for the three base fonts
        s.style.fontFamily = baseFonts[index];
        h.appendChild(s);
        defaultWidth[baseFonts[index]] = s.offsetWidth; //width for the default font
        defaultHeight[baseFonts[index]] = s.offsetHeight; //height for the defualt font
        h.removeChild(s);
    }

    function detect(font) {
        var detected = false;
        for (var index in baseFonts) {
            s.style.fontFamily = font + ',' + baseFonts[index]; // name of the font along with the base font for fallback.
            h.appendChild(s);
            var matched = (s.offsetWidth != defaultWidth[baseFonts[index]] || s.offsetHeight != defaultHeight[baseFonts[index]]);
            h.removeChild(s);
            detected = detected || matched;
        }
        return detected;
    }

    this.detect = detect;
};

Résumé

Comment ça marche?

Ce code fonctionne sur le principe simple que chaque caractère apparaît différemment dans différentes polices. Ainsi, des polices différentes prendront une largeur et une hauteur différentes pour la même chaîne de caractères de même taille de police.

Marko
la source
2
Très sournois. C'est génial.
récursif
4
Merci, oui, c'est utile une fois que j'ai une liste de polices pour tester ce qui est installé, mais le problème est de savoir comment générer une liste de noms de polices en premier lieu.
mattsh
43
Cela donnera seulement un oui / non pour savoir si une police est installée.
rektide
2
J'ai d'abord pensé que c'était génial, puis j'ai trouvé quelques problèmes. Le principal problème est que chaque navigateur renvoie des résultats différents. Certainement pas fiable.
Błażej Klisz
11
Intéressant et utile mais ne répond pas à la question. Cela ne récupère pas les noms des polices disponibles dans le navigateur. Donner un -1 réticent.
BenjaminGolder
11

Il existe un moyen de le faire en utilisant document.fonts

La valeur renvoyée est l'interface FontFaceSet du document. L'interface FontFaceSet est utile pour charger de nouvelles polices, vérifier l'état des polices précédemment chargées, etc.

  • Les valeurs renvoyées sont détaillées avec le poids, le style, etc.
function listFonts() {
  let { fonts } = document;
  const it = fonts.entries();

  let arr = [];
  let done = false;

  while (!done) {
    const font = it.next();
    if (!font.done) {
      arr.push(font.value[0]);
    } else {
      done = font.done;
    }
  }

  return arr;
}
  • Renvoie uniquement la famille de polices
function listFonts() {
  let { fonts } = document;
  const it = fonts.entries();

  let arr = [];
  let done = false;

  while (!done) {
    const font = it.next();
    if (!font.done) {
      arr.push(font.value[0].family);
    } else {
      done = font.done;
    }
  }

  // converted to set then arr to filter repetitive values
  return [...new Set(arr)];
}

Je l'ai testé sans lier de polices dans le HTML, puis lié la police Roboto, testé à nouveau et il a été ajouté au résultat.

Youssef AbouEgla
la source
cet extrait de code a parfaitement fonctionné grâce! `` `` listFonts () {let fonts = document ['fonts']; const it = fonts.entries (); laissez arr = []; laissez fait = faux; while (! done) {const font = it.next (); if (! font.done) {arr.push (font.value [0] .family); } else {done = font.done; }} // converti en set puis arr pour filtrer les valeurs répétitives return [... new Set (arr)]; } `` ``
rufreakde
5
<SCRIPT>
    function getFonts()
    {
        var nFontLen = dlgHelper.fonts.count;
        var rgFonts = new Array();
        for ( var i = 1; i < nFontLen + 1; i++ )
            rgFonts[i] = dlgHelper.fonts(i); 

        rgFonts.sort();
        for ( var j = 0; j < nFontLen; j++ )
            document.write( rgFonts[j] + "<BR>" );
    }
</SCRIPT>

<BODY onload="getFonts()">
<OBJECT id=dlgHelper CLASSID="clsid:3050f819-98b5-11cf-bb82-00aa00bdce0b" width="0px" height="0px">
</OBJECT>
MPC
la source
2
@Robert Sköld, oui, il semble être uniquement IE. Il est toujours utile à de nombreuses fins, bien que lorsqu'il est utilisé sérieusement, vous devriez avoir une détection de fonctionnalités afin que les personnes utilisant d'autres navigateurs comprennent; voir par exemple cs.tut.fi/~jkorpela/listfonts1.html
Jukka K. Korpela
Cela ne fonctionnera pas dans IE11 pour Windows Phone? Y a-t-il autre chose que je dois ajouter pour Windows Phone ???
jats
4

Solution FontFaceSet.check ()

  • La détection de toutes les polices disponibles est une technique courante d' empreinte digitale du navigateur , il est donc peu probable qu'une API JS soit ajoutée et retournera directement une liste.
  • Le support FontFaceSet.check () est assez bon pour être utilisé mais nécessitera une solution de secours, par exemple cette réponse pour les navigateurs plus anciens.
  • La vérification de la liste de polices suivante prend 150 ms + et devra donc être exécutée uniquement selon les besoins et le résultat mis en cache.

Liste des polices Windows 10

'Arial',
'Arial Black',
'Bahnschrift',
'Calibri',
'Cambria',
'Cambria Math',
'Candara',
'Comic Sans MS',
'Consolas',
'Constantia',
'Corbel',
'Courier New',
'Ebrima',
'Franklin Gothic Medium',
'Gabriola',
'Gadugi',
'Georgia',
'HoloLens MDL2 Assets',
'Impact',
'Ink Free',
'Javanese Text',
'Leelawadee UI',
'Lucida Console',
'Lucida Sans Unicode',
'Malgun Gothic',
'Marlett',
'Microsoft Himalaya',
'Microsoft JhengHei',
'Microsoft New Tai Lue',
'Microsoft PhagsPa',
'Microsoft Sans Serif',
'Microsoft Tai Le',
'Microsoft YaHei',
'Microsoft Yi Baiti',
'MingLiU-ExtB',
'Mongolian Baiti',
'MS Gothic',
'MV Boli',
'Myanmar Text',
'Nirmala UI',
'Palatino Linotype',
'Segoe MDL2 Assets',
'Segoe Print',
'Segoe Script',
'Segoe UI',
'Segoe UI Historic',
'Segoe UI Emoji',
'Segoe UI Symbol',
'SimSun',
'Sitka',
'Sylfaen',
'Symbol',
'Tahoma',
'Times New Roman',
'Trebuchet MS',
'Verdana',
'Webdings',
'Wingdings',
'Yu Gothic',

Liste des polices macOS / iOS

'American Typewriter',
'Andale Mono',
'Arial',
'Arial Black',
'Arial Narrow',
'Arial Rounded MT Bold',
'Arial Unicode MS',
'Avenir',
'Avenir Next',
'Avenir Next Condensed',
'Baskerville',
'Big Caslon',
'Bodoni 72',
'Bodoni 72 Oldstyle',
'Bodoni 72 Smallcaps',
'Bradley Hand',
'Brush Script MT',
'Chalkboard',
'Chalkboard SE',
'Chalkduster',
'Charter',
'Cochin',
'Comic Sans MS',
'Copperplate',
'Courier',
'Courier New',
'Didot',
'DIN Alternate',
'DIN Condensed',
'Futura',
'Geneva',
'Georgia',
'Gill Sans',
'Helvetica',
'Helvetica Neue',
'Herculanum',
'Hoefler Text',
'Impact',
'Lucida Grande',
'Luminari',
'Marker Felt',
'Menlo',
'Microsoft Sans Serif',
'Monaco',
'Noteworthy',
'Optima',
'Palatino',
'Papyrus',
'Phosphate',
'Rockwell',
'Savoye LET',
'SignPainter',
'Skia',
'Snell Roundhand',
'Tahoma',
'Times',
'Times New Roman',
'Trattatello',
'Trebuchet MS',
'Verdana',
'Zapfino',

FontFaceSet.check ()

const fontCheck = new Set([
  // Windows 10
'Arial', 'Arial Black', 'Bahnschrift', 'Calibri', 'Cambria', 'Cambria Math', 'Candara', 'Comic Sans MS', 'Consolas', 'Constantia', 'Corbel', 'Courier New', 'Ebrima', 'Franklin Gothic Medium', 'Gabriola', 'Gadugi', 'Georgia', 'HoloLens MDL2 Assets', 'Impact', 'Ink Free', 'Javanese Text', 'Leelawadee UI', 'Lucida Console', 'Lucida Sans Unicode', 'Malgun Gothic', 'Marlett', 'Microsoft Himalaya', 'Microsoft JhengHei', 'Microsoft New Tai Lue', 'Microsoft PhagsPa', 'Microsoft Sans Serif', 'Microsoft Tai Le', 'Microsoft YaHei', 'Microsoft Yi Baiti', 'MingLiU-ExtB', 'Mongolian Baiti', 'MS Gothic', 'MV Boli', 'Myanmar Text', 'Nirmala UI', 'Palatino Linotype', 'Segoe MDL2 Assets', 'Segoe Print', 'Segoe Script', 'Segoe UI', 'Segoe UI Historic', 'Segoe UI Emoji', 'Segoe UI Symbol', 'SimSun', 'Sitka', 'Sylfaen', 'Symbol', 'Tahoma', 'Times New Roman', 'Trebuchet MS', 'Verdana', 'Webdings', 'Wingdings', 'Yu Gothic',
  // macOS
  'American Typewriter', 'Andale Mono', 'Arial', 'Arial Black', 'Arial Narrow', 'Arial Rounded MT Bold', 'Arial Unicode MS', 'Avenir', 'Avenir Next', 'Avenir Next Condensed', 'Baskerville', 'Big Caslon', 'Bodoni 72', 'Bodoni 72 Oldstyle', 'Bodoni 72 Smallcaps', 'Bradley Hand', 'Brush Script MT', 'Chalkboard', 'Chalkboard SE', 'Chalkduster', 'Charter', 'Cochin', 'Comic Sans MS', 'Copperplate', 'Courier', 'Courier New', 'Didot', 'DIN Alternate', 'DIN Condensed', 'Futura', 'Geneva', 'Georgia', 'Gill Sans', 'Helvetica', 'Helvetica Neue', 'Herculanum', 'Hoefler Text', 'Impact', 'Lucida Grande', 'Luminari', 'Marker Felt', 'Menlo', 'Microsoft Sans Serif', 'Monaco', 'Noteworthy', 'Optima', 'Palatino', 'Papyrus', 'Phosphate', 'Rockwell', 'Savoye LET', 'SignPainter', 'Skia', 'Snell Roundhand', 'Tahoma', 'Times', 'Times New Roman', 'Trattatello', 'Trebuchet MS', 'Verdana', 'Zapfino',
].sort());

(async() => {
  await document.fonts.ready;

  const fontAvailable = new Set();

  for (const font of fontCheck.values()) {
    if (document.fonts.check(`12px "${font}"`)) {
      fontAvailable.add(font);
    }
  }

  console.log('Available Fonts:', [...fontAvailable.values()]);
})();

Chris
la source
merci, c'est ce que je recherche aussi pour la conception Web éventuelle le long des polices système locales pour gagner beaucoup de fiabilité dans l'affichage du contenu ou l'analyse de la page de manière à ne pas remplir beaucoup le processeur
Constantin
3

Dans ma recherche pour cela, j'ai également trouvé Font.js , qui ajoute un objet Font un peu comme Image, il est donc possible de vérifier quand une police est réellement prête à être utilisée. Fonctionne également sur les polices installées / système. L'inconvénient est IE9 + uniquement en raison du besoin Object.defineProperty(d'autres navigateurs l'ont), mais si vous faites du Web moderne, cela semble être une option encore meilleure. (Je vais, malheureusement, devoir aller avec la réponse ci-dessus, voter pour le moment et passer à autre chose pour le moment. :))

Stoffe
la source
3

J'ai ajouté deux méthodes au détecteur de Lalit Patel ci-dessus:

  • addFont (family, stylesheetUrl, ruleString) -> détecte si la police 'family' existe, sinon ajoute une feuille de style chargeant la police en utilisant soit stylesheetUrl si elle est donnée, soit ruleString
  • addFontsArr (arr) -> ajoute un tableau de polices

Avec cela, vous pouvez faire:

fonts = [ 'Arial', 'Arial Black', { family: 'Lato', stylesheetUrl: 'https://fonts.googleapis.com/css?family=Lato'}, 'Leelawadee UI']
(new FontDetector()).addFontsArr(fonts);

code:

/**
 * JavaScript code to detect available availability of a
 * particular font in a browser using JavaScript and CSS.
 *
 * Author : Lalit Patel
 * Website: http://www.lalit.org/lab/javascript-css-font-detect/
 * License: Apache Software License 2.0
 *          http://www.apache.org/licenses/LICENSE-2.0
 * Version: 0.15 (21 Sep 2009)
 *          Changed comparision font to default from sans-default-default,
 *          as in FF3.0 font of child element didn't fallback
 *          to parent element if the font is missing.
 * Version: 0.2 (04 Mar 2012)
 *          Comparing font against all the 3 generic font families ie,
 *          'monospace', 'sans-serif' and 'sans'. If it doesn't match all 3
 *          then that font is 100% not available in the system
 * Version: 0.3 (24 Mar 2012)
 *          Replaced sans with serif in the list of baseFonts
 */

/**
 * Usage: d = new Detector();
 *        d.detect('font name');
 */
function FontDetector() {
    this.detect = detect;
    this.addFont = addFont;
    this.addFontsArr = addFontsArr;

    // a font will be compared against all the three default fonts.
    // and if it doesn't match all 3 then that font is not available.
    var baseFonts = ['monospace', 'sans-serif', 'serif'];

    //we use m or w because these two characters take up the maximum width.
    // And we use a LLi so that the same matching fonts can get separated
    var testString = "mmmmmmmmmmlli";

    //we test using 72px font size, we may use any size. I guess larger the better.
    var testSize = '72px';

    var h = document.getElementsByTagName("body")[0];

    // create a SPAN in the document to get the width of the text we use to test
    var s = document.createElement("span");
    s.style.fontSize = testSize;
    s.innerHTML = testString;
    var defaultWidth = {};
    var defaultHeight = {};
    for (var index in baseFonts) {
        //get the default width for the three base fonts
        s.style.fontFamily = baseFonts[index];
        h.appendChild(s);
        defaultWidth[baseFonts[index]] = s.offsetWidth; //width for the default font
        defaultHeight[baseFonts[index]] = s.offsetHeight; //height for the defualt font
        h.removeChild(s);
    }

    function detect(font) {
        var detected = false;
        for (var index in baseFonts) {
            s.style.fontFamily = font + ',' + baseFonts[index]; // name of the font along with the base font for fallback.
            h.appendChild(s);
            var matched = (s.offsetWidth != defaultWidth[baseFonts[index]] || s.offsetHeight != defaultHeight[baseFonts[index]]);
            h.removeChild(s);
            detected = detected || matched;
        }
        return detected;
    }

    function addFont(family, stylesheetUrl, ruleString) {
        if (detect(family)) {
            //console.log('using internal font '+family);
            return true;
        }
        if (stylesheetUrl) {
            console.log('added stylesheet '+stylesheetUrl);
            var head = document.head, link = document.createElement('link');
            link.type = 'text/css';
            link.rel = 'stylesheet';
            link.href = stylesheetUrl;
            head.appendChild(link);
            return true;          
        }

        if (ruleString) {
            console.log('adding font rule:'+rule);
            var newStyle = document.createElement('style');
            newStyle.appendChild(document.createTextNode(rule));
            document.head.appendChild(newStyle);
            return true;
        }

        console.log('could not add font '+family);
    }

    function addFontsArr(arr) {
        arr.forEach(a => typeof a==='string' ? addFont(a) : addFont(a.family, a.stylesheetUrl, a.ruleString));
    }
};
kofifus
la source
2

Peut-être que cela pourrait être fait d'une manière complètement différente, en utilisant une feuille de sprites avec des images de polices connues pour un caractère spécifique et en la comparant avec des instantanés d'un élément de canevas sur lequel le même caractère est dessiné avec ce que le navigateur rapporte comme la même police. La comparaison peut être faite avec quelque chose comme resemble.js .

C'est plus lent, mais devrait également nous permettre de détecter quand le navigateur ment.

fzzylogic
la source
2

La réponse courte est. Peu de choses ont changé en ce qui concerne la détection des polices dans les navigateurs en 2020, sauf que l'utilisation de Flash est maintenant une idée encore pire .

Il n'y a actuellement aucun système natif de navigateur pour "lister" toutes les polices disponibles. Cependant, les navigateurs vous permettront de vérifier si une police est chargée / prête à l'aide de l' API FontFaceSet . Il est assez bien pris en charge dans les navigateurs modernes.

Ceci est destiné à montrer si une police Web est complètement téléchargée MAIS cela fonctionnera également pour les polices système. Le hic, c'est que vous devez fournir une liste de polices à vérifier.

Ainsi, en conjonction avec un user agent test (pas toujours précis), vous pouvez produire une liste de polices système courantes pour chaque type de périphérique. Testez ensuite ces polices et toutes les polices Web que vous chargez.

REMARQUE: Cela ne vous donnera PAS une liste complète des polices disponibles, mais vous pouvez vérifier les polices couramment installées par les produits MS Office ou Adobe.

Bryce Howitson
la source
0

J'ai récemment remarqué que si je règle la valeur context.font pour un canevas HTML5, sur quelque chose d'invalide, tel que "junk", le changement est ignoré par le canevas. Je ne sais pas si cela est spécifique au navigateur, mais cela semble fonctionner de cette façon sur Chrome. J'ai également vu d'autres articles (la police HTML 5 canvas étant ignorée ) qui indiquent que cela se produit dans d'autres navigateurs.

On pourrait alors écrire une chaîne avec la valeur par défaut, qui je crois est "10px sans serif" ( https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font ), définir la police à celui que vous testez et écrivez à nouveau la chaîne. S'il s'agit de la même chose que le premier dessin, la police n'est pas disponible.

AFF
la source