列出用户浏览器可以显示的每种字体


105

javascript中是否有办法获取浏览器可以显示的所有字体(或字体家族)的名称?(我想为用户提供一个包含所有可用字体的列表的下拉列表,并允许用户选择一种字体。)我不希望不必事先对该列表进行硬编码或从服务器发送下来。(直觉上,似乎浏览器应该知道它拥有什么字体,并且应该以某种方式将其公开给javascript。)

Answers:


38

JavaScript版本有点片状。它通过迭代已知的字体和测试来获取字体。

最准确的方法(尽管必须使用适当的插件)是使用Flash。在这里,您可以获取字体列表,而不必使用尺寸分别测试字体。

您将不得不决定是否拥有确切的列表,但要以不能在某些设备(iDevices,不带Flash插件的浏览器等)上运行为代价,还是要通过JavaScript更好地支持部分列表


30
@Jared要提到Flash吗?我并不是说这是唯一的解决方案,而是提到这是检测字体的最准确方法。
亚历克斯

4
@alex是的。这可能给开发人员特别是新开发人员带来错误的印象。我建议编辑您的答案以更好地说明使用Flash的优缺点,也许只是“不推荐,但...”之类的东西。
杰瑞德

19
@Jared我是否需要写出所有答案,以便为读者提供从头开始的新信息,以防他们是新手?我确实解释说Flash需要一个适当的插件,但是我也提到了它是目前获取所有可用字体的唯一方法(JavaScript方法仅检测字体的一个子集,对于大多数用例来说可能已经足够了)。我也对必须使用Flash感到不满意,但这是我们目前完成此任务的全部。
Alex

7
@Jared看到最后一段吗?您可能希望再次阅读。
亚历克斯

10
@Jared该段始终存在。
亚历克斯

73

就在这里!我很高兴您提出这个问题,因为我现在也想使用它。

+1问题,这是您的答案:)

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

来自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;
};

摘要

它是如何工作的?

该代码基于以下简单原理工作:每个字符以不同的字体显示不同。因此,对于相同字体大小的相同字符串,不同字体将采用不同的宽度和高度。


2
非常狡猾。这太棒了。
递归

4
谢谢,是的,一旦我有一个字体列表来测试安装的字体,这将很有用,但是问题是首先要如何生成字体名称列表。
马特什

43
对于是否安装了字体,这只会给出是/否。
rektide

2
首先,我认为这很棒,但后来发现了一些问题。主要问题是每个浏览器返回不同的结果。绝对不可靠。
BłażejKlisz

11
有趣且有用,但不能回答问题。这不会检索浏览器中可用字体的名称。给一个勉强的-1。
BenjaminGolder

10

有一种方法可以使用 document.fonts

返回的值是文档的FontFaceSet接口。FontFaceSet接口对于加载新字体,检查先前加载的字体的状态等非常有用。

  • 返回的值带有重量,样式等详细信息。
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;
}
  • 仅返回字体系列
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)];
}

我测试了它,但未链接HTML中的任何字体,然后链接了Roboto字体,再次进行了测试,并将其添加到结果中。


此代码段非常有效,谢谢!```listFonts(){let fonts = document ['fonts']; const it = fonts.entries(); 让arr = []; 让完成=假; while(!done){const font = it.next(); 如果(!font.done){arr.push(font.value [0] .family); } else {done = font.done; }} //转换为set,然后转换为arr以过滤重复值,返回[... 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>

2
@RobertSköld,是的,它似乎仅适用于IE。它仍然可以用于许多目的,尽管当认真使用时,您应该进行一些功能检测,以便使用其他浏览器的人可以理解。参见例如cs.tut.fi/~jkorpela/listfonts1.html
Jukka K.

在Windows Phone的IE11中将无法使用?我还需要为Windows Phone添加其他内容吗???
2014年

4

FontFaceSet.check()解决方案

  • 检测所有可用字体是常见的浏览器指纹识别技术,因此不太可能会添加任何JS API来直接返回列表。
  • FontFaceSet.check()支持足以使用,但是需要回退,例如,此答案对于较旧的浏览器,。
  • 检查以下字体列表需要150毫秒以上的时间,因此仅需根据需要运行,并缓存结果。

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 / 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()]);
})();


谢谢,这也是我寻找最终的网站设计以及本地系统字体的目的,因为它们在显示内容或解析页面时以不占用太多CPU的方式获得了很大的灵活性
康斯坦丁

3

在搜索中,我还找到了Font.js,它添加了一个类似于Image的Font对象,因此可以检查何时可以实际使用字体。也适用于已安装/系统字体。IE9 +的缺点仅是出于需要Object.defineProperty(其他浏览器也有),但是如果您使用的是现代网络,这似乎是一个更好的选择。(可悲的是,我将不得不接受上面的答案,赞成并继续前进。:)


3

我在上面的Lalit Patel的检测器中添加了两种方法:

  • addFont(family,stylesheetUrl,ruleString)->检测字体'family'是否存在,如果不存在,则使用stylesheetUrl(如果给定)添加rulesheet来加载字体,否则使用ruleString
  • addFontsArr(arr)->添加字体数组

这样,您可以执行以下操作:

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

码:

/**
 * 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));
    }
};

2

也许可以通过完全不同的方式来完成此操作,即使用具有特定字符已知字体图像的Spritesheet,并将其与画布元素的快照进行比较,在画布元素上绘制相同字符,然后将浏览器报告为相同字体。可以使用诸如resemble.js之类的东西进行比较

这比较慢,但是也应该允许我们检测浏览器何时说谎。


2

简短的答案是。到2020年在浏览器中的字体检测方面并没有太大改变,只是使用Flash现在变得更糟

当前没有浏览器本机系统可以“列出”所有可用字体。但是,浏览器将允许您使用FontFaceSet API检查字体是否已加载/就绪。在现代浏览器中,它得到了很好的支持。

这旨在显示是否完全下载了Web字体,但它也适用于系统字体。要注意的是,您必须提供要检查的字体列表

因此,结合user agent 测试(并非总是准确),您可以生成常见系统字体的列表为每种设备类型。然后针对这些以及您加载的所有网络字体进行测试。

注意:这不会为您提供可用字体的完整列表,但是您可以检查MS Office或Adobe产品通常安装的字体。


0

我最近注意到,如果将HTML5画布的context.font值设置为无效值(例如“垃圾”),则该更改将被画布忽略。我不知道这是否特定于浏览器,但在Chrome上似乎可以这样工作。我还看过其他文章(HTML 5画布字体被忽略),它们表明它发生在其他浏览器中。

然后可以写出一个带有默认值的字符串,我相信它是“ 10px sans serif”(https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/font),设置字体到要测试的一个,然后再次写入字符串。如果它与第一个图形相同,则该字体不可用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.