浏览器没有获取本地字体列表的API,同时FLash也被浏览抛弃。需要一种新的方式判断字体是否支持。
下图列出了一些字体,特征各不一样。最直观的是字形,其次不同字体下的相同文字的宽度也是不一样的,由此我们能通过这两点差异对字体进行判断。
ziti.jpg

字体宽度

每种字体的宽度都不尽相同。
我们可以通过分别测量默认字体和目标字体的宽度,查看二者的差异。若果不一致则说明目标字体可用,否则说明不支持该字体,浏览器会退到了默认字体。

font-load-detection-three.c10e6f652d.png

在canvas中可以使用measureText测量文字渲染的宽度,这个宽度非常精确。

var canvas = document.createElement('canvas')
var ctx = canvas.getContext("2d");
var width = ctx.measureText('123').width; // 21.111328125

使用ctx.font可以设置字号和字体,例如ctx.font="30px Arial",默认为10px sans-serif

我们可以提前计算好各种字体的宽度表,就可以快速地识别字体样式。

像素比对

根据用户设置的字体将某一个字符绘制在canvas上,并提取像素信息与默认字体进行比对,看是否一致。
这种方法最为准确,当然缺点也很明显就是计算量较大。

let isSupportFontFamily = function (f) {
  let base = "Arial";
  if (f.toLowerCase() == base.toLowerCase()) return true
  let e = "a";
  let d = 100;
  let a = 100, i = 100;
  let c = document.createElement("canvas");
  let b = c.getContext("2d");
  c.width = a;
  c.height = i;
  b.textAlign = "center";
  b.fillStyle = "black";
  b.textBaseline = "middle";
  let g = function (j) {
    b.clearRect(0, 0, a, i);
    b.font = d + "px " + j + ", " + base;
    b.fillText(e, a / 2, i / 2);
    let k = b.getImageData(0, 0, a, i).data;
    return [].slice.call(k).filter(l => l != 0);
  };

  return g(base).join("") !== g(f).join("");
};