我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

还是回到检测网页实际渲染字体的这个话题上,本文列出两个在国外论坛上看到的代码。测试结果是各有优劣,检测结果也是偶尔准确,偶尔不准确。所以,这里就是列出来学习学习思路。本来苏南大叔是改了一些逻辑的,后来发现检测结果都不是太理想,就没有继续修改了。贴出来的代码,都是原版的,肯定是有一些问题的,大家自行调试。

苏南大叔:js代码,检测网页实际渲染字体的两种方案原理 - 检测网页实际渲染字体的两种方案原理
js代码,检测网页实际渲染字体的两种方案原理(图1-1)

大家好,这里是苏南大叔的博客,随便写写技术文章的地方,一个不想被百度云备案一直各种威胁的地方。本文描述检测网页实际渲染字体的两个可能的方案(意思是效果都不咋地)。测试环境:win10chrome@105.0.5195.102。这两个代码,都来自于下面这个页面地址:

方案一,比较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';
}

这里就不详细说了,大体的思路就是,先在canvas上面设置一个不存在的字体,获得字体不存在的时候的标准数据。然后再分别设置目标字体里面的字体,去比较。如果数据有变化,这个字体就是存在的的。因为是依次设置的,所以第一个被检测到存在的字体必然是生效的字体。

var p = document.querySelector("p");
console.log(renderedfont(p));

不过实际的效果是,偶尔检测的对,偶尔不对。所以,大家自己调试吧。

方案二,比较字体宽度

(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);

这个代码的原理是这样的:这里加载了一个特殊的字体void,这个字体不显示任何内容,所以,它渲染的元素的实际宽度都非常非常窄。那么,对于用于检测的系列字体,依然是顺序检测。但凡被检测的字体存在的话,这个渲染的元素宽度就会明显不同。那么,就可以判断字体的存在性。依据font-family的生效顺序,它就是个生效的被渲染的字体。

var p = document.querySelector("p");
console.log(getRenderedFontFamily(p));

这个代码的实际测试结果是:偶然会发生检测到undefined的情况。所以,效果也不是很好。

相关链接

这两个代码,都来自于下面这个链接:

有个谷歌插件,叫做font finder,实现的效果和本文描述的效果是差不多的,大家可以去试试看。

下面还有其它的相关文章:

值得说明的是:有一些特殊的字体,是一组字体。具体表示的是哪个字体,还是要具体问题具体分析的。或者说,可能出现的检测结果是这种带皮儿的字体名字的情况。

总结

这里的两个检测字体实际渲染结果的代码,是存在有使用场景的,具体的效果就有待考证了。更多字体相关文章,请点击:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   css    font