未在 CSS 中定义时如何获取实际渲染的字体?

Posted

技术标签:

【中文标题】未在 CSS 中定义时如何获取实际渲染的字体?【英文标题】:How to get the actual rendered font when it's not defined in CSS? 【发布时间】:2011-11-18 15:45:08 【问题描述】:

当 CSS font-facefont-size 属性未定义时,如何获取元素的实际字体和字体大小?

例如,javascript sn-p

object.style.fontFamily

不返回任何值。这很明显,假设 CSS 没有在任何地方对 object 应用样式。但是,当然,要使用某种字体来呈现文本,可能是系统字体或网络浏览器默认字体。

那么,例如,JavaScript 可以获取该渲染字体吗?

【问题讨论】:

Java 或 Flash 都可以。 JS - 无法为您提供字体名称,但您可以尝试通过破译画布中呈现的文本来检测字体。 不知道对你有没有用,但我通常使用WhatFont bookmarklet 来确定页面上使用的是什么字体,所以可能有一些想法可以帮助你,如果它成功检测到您正在谈论的字体... 你为什么要这样做? ..我不应该怀疑皇帝的问题,但无论如何我一直是个叛逆者。 @Lollero 哈哈! ;-) 我想根据为该 textarea 定义的字体确定该 textarea 的列数,因为 cols 是 Xhtml 中 textarea 的必需属性。 仅出于调试原因需要此信息的人,而不是实际需要使用 JavaScript 以编程方式获取它的人,应改为查看 ***.com/questions/884177/…。一些浏览器的开发工具提供了这些信息。 【参考方案1】:

我建议这个功能:

function css( element, property ) 
    return window.getComputedStyle( element, null ).getPropertyValue( property );

用法:

css( object, 'font-size' ) // returns '16px' for instance

注意:getComputedStyle 在 IE8 中不起作用。

现场演示: http://jsfiddle.net/4mxzE/

【讨论】:

谢谢,看来可以了。还有一小段代码:-)。我不介意它在 IE8 中不起作用,因为 IE 通常缺乏 W3C 标准,因此要使您的网站布局与 IE 兼容需要很长时间。 IE 有类似的 element.currentStyle 代替:quirksmode.org/dom/getstyles.html 如果使用 jQuery,请将 element 更改为 $(selector).get(index).get() 方法返回由 $(selector) 选择的 DOM 元素。即使只选择了一个元素,请务必设置index = 0,因为没有任何参数的.get() 将返回一个数组。 虽然这仍然会返回 css 指定的字体,而不是文本实际呈现的字体,如果它们不同的话。我正在寻找一种方法来查看浏览器是否已下载并显示我的自定义字体。 @MoizTankiwala 我认为这是因为getComputedStyle 返回的是“计算值”,而不是“使用值”(see examples here)。【参考方案2】:

没有标准、可靠的方法来确定实际使用的字体。此处之前的答案将报告样式化的 fontFamily 样式值,但这可以是字体名称列表,并且不具体标识呈现的 实际 字体(即 实际这里提出的问题)。

(正如一些 cmets 中提到的,有一些方法可以通过检查视觉提示来猜测字体,但这不可能 100% 可靠。)

【讨论】:

我找到了一种逐像素检查的方法,你可能会说这是一种“视觉提示”,但如果不是像素集合,字体又是什么?......如果像鸭子和游泳一样说话像鸭子…… 嗯,大多数现代字体不是像素的集合,它们是矢量图形。这不是毫无意义的吹毛求疵!我的意思是浏览器之间的渲染会有差异 - 例如,可以以不同的方式支持字距调整和连字(或根本不支持),这会影响渲染文本的尺寸。 我现在在下面看到你的答案,我上面的观点并不真正适用于它。有趣的方法:) Chrome 现在会告诉你正在渲染哪些字体(如 Salman A 的回答)——在开发者工具中,转到 Computed 选项卡,在底部有一个 Rendered Fonts 部分。【参考方案3】:

您可以在 Chrome/Firefox 开发者工具中找到关于渲染字体的信息。尝试检查以下代码 sn-p 中的段落:

p  font-family: sans-serif;  
<p>Some text and <span title="an emoji">?</span></p>

在 Chrome 开发者工具中(在 55.0.2883.75 m 64 位上测试)你会得到以下信息:


在 Firefox 开发人员工具中(在 47.0.2 上使用 about:config > devtools.fontinspector.enabled = true 测试)您会获得以下信息:

【讨论】:

没有显示哪些字体被渲染 =( 如果Consolas 没有找到,Menlo 将被使用。如何看到,即使使用 Devtools? @Rudie:面板(在 Chrome 中呈现字体,在 FF 中显示字体选项卡)准确显示使用的字体。浏览器可以使用字体系列中未列出的字体。在 chrome 示例中,您可以清楚地看到“Lucida Sans Unicode”未列在字体系列中。 好吧,该死的,我显然是个盲人。 渲染字体 部分很棒!和新的,我想,我从来没有见过它。你知道是否有办法用 JS 获取这些信息吗?我正在搜索 devtools 代码。谢啦! =) 该死的。搜索结束于未公开的this._agent.getPlatformFontsForNode()。如果您有兴趣,请稍后致电github.com/ChromiumWebApps/blink/blob/…。 @Rudie 如果你还需要它,我已经做了这个小提琴jsfiddle.net/JulienCabanes/ubzrmmy5【参考方案4】:

我找到了一种方法(适用于所有支持 <canvas> 的浏览器),如果字体呈现发生变化,则逐个像素检查

function renderedfont(ele) 
    var getDefaultFonts = function () 
        var iframe = document.createElement('iframe');
        var html = '<html><body>';
        var fonts;
        document.body.appendChild(iframe);
        iframe.contentWindow.document.open();
        iframe.contentWindow.document.write(html);
        var subele = iframe.contentWindow.document.createElement(ele.tagName);
        iframe.contentWindow.document.body.appendChild(subele);
        fonts = getComputedStyle(subele)['font-family'];
        document.body.removeChild(iframe);
        return fonts;
    
    var fonts = getComputedStyle(ele)['font-family'] + ',' + getDefaultFonts();
    var fontsArray = fonts.split(',');
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext("2d");
    var testString = "abcdefghijklmnopqrstuvwxyz!@#$%^&*()ñ";
    var prevImageData;
    document.body.appendChild(canvas);
    canvas.width = 500;
    canvas.height = 300;
    fontsArray.unshift('"Font That Doesnt Exists ' + Math.random() + '"');

    for (var i = 0; i < fontsArray.length; i++) 
        var fontName = fontsArray[i].trim();
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.font = '16px ' + fontName + ', monospace';
        ctx.fillText(testString, 10, 100);
        var idata = ctx.getImageData(0, 0, canvas.width, canvas.height); 
        var data = idata.data
        if (prevImageData) 
            for (var j = 0; j < data.length; j += 3) 
                if (prevImageData[j + 3] !== data[j + 3]) 
                    document.body.removeChild(canvas);
                    return fontName;
                
            
        
        prevImageData = data;
    

    document.body.removeChild(canvas);
    return 'monospace';

所以要使用你只需这样做:

renderedfont(document.body);
// Arial

如果您将其用于其他方言(例如日语),您可能需要将 testString 变量更改为该方言中最常见的字符。

【讨论】:

这是一个很酷的方法,但它仍然不起作用,因为它不知道要尝试哪种字体。您可以将字体命名为 Foo,但仍会渲染 Arial,或渲染 Consolas 的引用等宽字体。 Chrome 知道,但不会公开该信息 =( devtools 在 Computed 中有一个 Rendered fonts 部分,这始终是正确的,但它使用了那些隐藏的方法。 第一部分:此方法丢弃假字体,因此如果您使用“Foo”,它会返回正在渲染的实际字体,而不是“Foo”,因为此方法通过创建一个隔离的副本来再次检查默认字体节点。秒部分:没错,它在渲染“Consolas”时返回“monospace”,但要修复它很容易,您只需要尝试渲染“Consolas”并检查其渲染是否与“monospace”相同。转换只是少数(幻想,等宽,衬线),所以它是可能的。您可以将图像发送到服务器以检查所有字体。【参考方案5】:

在这里有点晚了,但不得不解决同样的问题...... Šime Vidas 的答案基本上是正确的,但如今,您可以发挥创意,并获得答案。

基本上,定义你自己的字体,你确定不匹配任何现有的字体。 然后,在每个计算样式字体之后添加您的字体,并查看它是否是您的字体被渲染。如果不是,恭喜你找到了渲染的字体

这是小提琴: https://jsfiddle.net/obutjanw/

这是定义你需要的函数的代码:

    (function(win) 

        var style = null;
        function createClass(name,rules)
            if (typeof(rules)!="string") 
                rules = JSON.stringify(rules).trim();
            

            if (rules.startsWith("")&&rules.endsWith("")) 
                rules = rules.substring(1,rules.length-1);
            

            if (style==null) 
                style = document.createElement('style');
                style.type = 'text/css';
                var head = document.getElementsByTagName('head');
                if (head.length=0) 
                    var h = document.createElement('head');
                    document.insertBefore(h,document.body);
                    head = [h];
                
                head[0].appendChild(style);
            


            var rule;
            if(!(style.sheet||).insertRule) 
                rule = (style.styleSheet || style.sheet).addRule(name, rules);
             else 
                style.sheet.insertRule(name+""+rules+"",0);
                rule = style.sheet
            
            return rule;
        

        function removeNode(node) 
            if (node.remove) 
                node.remove();
             else 
                var pn = node.parentElement;
                if (pn!=null) 
                    pn.removeChild(node);
                
            
        



        createClass("@font-face", " font-family: 'void';  src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPQAA0AAAAAJLwAAAN2AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGh4GYACCehEICrcUtUcLgVYAATYCJAODGgQgBYQrB4IOGy8iyD4M2GTIPrT6WF2CYVmsrZSME0Lxo0HkLsnMe3J+bjz8/1jtvj/u6wnzjEfTZlY97lD30CyaNpNQaV4SibwX23xgzQx8YoKmJQvZq6ZBWjJtbams7UgryfyPub1/XCfUSbdKmihiPrNKphKi5QzxL7t7f4Ygsik2JcsCqWjBhCpFOWuA+r3/lb/f/ynl4AsvOYHFxycux78I1qIX+gd5kXU+ZsO7wLswbYM/aGDRxjV8TPCAshIIMC3BsAwDkAAb0NvIC+Bt1UII+PmLFgF/hd0tbtmBKvpAEsJFJIuEZDoXOdKdvlnrANSzuI5ODoqTBnPXWUMoMLdg3mfM5HgyMvzfCugCzEGQYQ45BQFX1ASmGBwDbTaYhJrjHt5r10f62D9QM6cHAk2AuaL8WWyod/QWEE4QLaFWFCqRyTS5fKGWdOtLt+d0B+sPBEPhSDQWTyRT6Uw2ly8US+VKtVZv8IIoyYqqQd9Ny3bcZqvd6fb6g+FoPJnO5ovlar3Z7vYA5ScZ7ex193Bz+4z/ClwC/AB8oXoA1Q/QBEggoUqTYA8V8QP3xWofHHUvrXZRqXfN8qbGV8YZV/BYWfkBegAgFnoLMY558+Pt8rg7Lt/z7D56vHHjeA/kHpUZZIyeL9n5vJD5V237BwsBgH8gRAhBCAmRRAYRurBwyYHg13/L7+pCvTFrmXM6isP3aQYoSiBYmswqCfwfekQgMH26vOBlp5nIt6nNIJs88OmzLcScJXAJBwUBOeW2FwjQk4KAkLULgaTgJAIZWZcRyMq6KUBOxX0B8rLeC1DT8waBuokUYKIpYQXQyFC0nUzq9pMZRReBLHKDzOlbk3lFr8maFR5HqdvkgxFXG6MrdRFjNCViJUaL5+QiVrjAwbN9F7h5QMLZB9QKuCbhOcOWi97AARc3ldBBDi8bFfJTmJ/1iItcwZOIMfIycmDvY6ScyfmvdqFTF5QimmrIbmrhJOdJlQqCBU9IMJG7EXVZ2rAZzfk/h3ThhdtKRD2rDSRULcKD5KXtU+4+qq9BUUlZRVVNXUOTnYOTi5sHjkCi0BgsjpePX0BQSFhEVExcQlJKWkZWTl5BUUlZRVVNXQNPIJLIFCqNwWSxOVxNLW0dXT19A0MjYxNTM3MLSytrG1s7ewffpMhE1htvffTO++5q7O2mciQwb+6tAgAAAA==) format('woff2'), url(data:application/font-woff;charset=utf-8;base64,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) format('woff'); ");


        var tests = document.createElement("span");
        tests.innerHTML = "0123";
        tests.style.display = "inline-block";
        tests.style.fontFamily = "void";
        document.body.appendChild(tests);
        setTimeout(function() 
            removeNode(tests);
        ,0);

        function getRenderedFontFamily(elm, computedstyle) 
            var cs = (typeof(computedstyle) == 'undefined') ? win.getComputedStyle(elm) : computedstyle;
            var fontF   = (cs.fontFamily        || elm.style['font-family'] || '').replace(/['"]*/g, '');
            var tfontF = fontF.split(",");
            var tests = document.createElement("span");
            tests.innerHTML = "0123";
            tests.style.display = "inline-block";
            tests.style.fontFamily = "void";
            elm.appendChild(tests);
            var refcs = window.getComputedStyle(tests);
            var refw = refcs.width;
            var tested = ;
            while (tfontF!=null) 
                for (var i=0;i<tfontF.length;i++) 
                    if (tested[tfontF[i]]) continue;
                    tests.style.fontFamily = tfontF[i] + ", void";
                    if (refcs.width!=refw) 
                        removeNode(tests);
                        return tfontF[i].trim();
                    
                    tested[tfontF[i]] = true;
                
                if (elm.parentElement) 
                    elm = elm.parentElement;
                    var cs1 = win.getComputedStyle(elm);
                    fontF   = (cs1.fontFamily       || elm.style['font-family'] || '').replace(/['"]*/g, '');
                    tfontF = fontF.split(",");
                 else 
                    tfontF = null;
                
            
            removeNode(tests);
        
        win.getRenderedFontFamily =  getRenderedFontFamily;
    )(window);

请注意,如果在加载时附加使用字体的节点,您确实应该保留该部分,否则浏览器可能会在第一次调用 getRenderedFontFamily 时延迟渲染正确字体

【讨论】:

以上是关于未在 CSS 中定义时如何获取实际渲染的字体?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用“第一个可用字体”定义 CSS 'ex' 单元?

位图字体渲染和字距调整

如何解决 Vue.js 中的“属性或方法未在实例上定义但在渲染期间引用..”?

Bootstrap5 和其他自定义 CSS 未在 .net core web api 中加载

如何获取由 css 设置的元素的字体大小

css中如何设置字体