Levy's ink.
Doodles, whimsy & life.
About
Blog
Mess
Catalog

中文网页美化的正确打开方式 - 字体剪裁

说到网页字体选择绝对是一个令人头大的问题,如何能正确选一个所有系统都支持、或至少能保留作者想保留的特征(是否等宽、有无衬线等)的字体操碎了各种前端程序员和Designer的心,最终无非向安全字体低头,亦或是用把字体转化为图片、Flash插件等小把戏来保留那么一点点设计者的初衷。

Fontface的出现绝对大快人心:通过把字体文件以资源的形式提供,网页也能渲染出设计者嵌入的字体了。可问题依然存在:对于西方人使用的只需支持ascii字符及少量扩展的字体,一个几十K的文件足以,充其量不过几个js脚本或css的大小,Fontface的确完美解决了嵌入字体的问题。那么对于中文字体呢?对于连国人都不能掌握其全部字符的语言,一个字体十几M已经算最小值,这在当今的网速下是不可容忍的。

解决方案

首先需要感谢党主席的日志 中文Web Font 给我打开了新世界的大门:由于每个网站包含的字符数往往只是字体字符集中很小的子集,如果只保留他们并将其应用于对应的网站,得到的字体文件将会大大减小。

比如一个接近12M的华文宋体,在经过对本博客关于页面内容的剪裁后,获得的字体文件仅83K,算是一个能接受的大小了。那么该如何进行字体裁剪又如何合适地与前端建立联系呢?

党主席也在日志中提供了他的实现方案:基于Google sfntly的剪裁工具。由于并不懂Scala,故打算自己封装一个;又由于Web Server基于Node,最终我在npm上看到了这货——Fontmin,项目主页是这里,前端简直闪瞎狗眼...(后来才知道是Baidu FE搞的)

不过既然是国人弄的,对中文的原生支持一定会比外国人制作的裁剪工具强上不少。又由于是用Node实现的,在此基础上搭一个后台也算简单。于是就开工做了这么一个货色。

Font Tailor

顾名思义,就是一个剪裁字体的服务器。在本网站全站使用,并利用其剪裁华文宋体——打开控制台,看到的提示

The font[stsong] has been fetched successfully.

便是了。

工作原理

大体来说,工作原理如下:
前端向字体服务器发送请求->字体服务器GET指定地址并搜集其全部字符->将对应的字符生成字体->返回前端并用Fontface展现

由于生成字体消耗大量CPU资源,故实际实现中采用双缓存机制:缓存url->字体映射、缓存字符集->字体映射,为保证url内容的时新性,setting中提供url缓存的expireTime以供设置(font.levy.at保存期为一天)

另外,生成的字体会占用一定硬盘空间,为防止不可控地恶意API调用,只有满足白名单正则表达式的url才能被收录。同时,定期执行的清理器将不在缓存映射表中字体文件清理掉。

使用方式

见网站前端源码:

function fetchFont(url)
{
    if (url==undefined)
        url=window.location.href;
    j=document.createElement("script");
    j.src="http://"+effect_helper.FONT_SERVER+"/jspadding.js?addcss=1&font=stsong&url="+encodeURIComponent(url);
    document.body.appendChild(j);
}

总而言之,就是获取一个js,该js会主动将字体加入fontface。更多调用方式可见Github Page的README。

不足之处

尽管大致实现了一个服务,至今为止也无虞地跟随博客系统跑了十几天,但在细节之处还是有很多需要润色的地方:

  • NodeJS在对某个特定的url发起http get时会抛出异常,原因不明,导致异常的url特征也不能确定。算是一个issue。
  • 在加载font的时候所有受影响的字体会消失,加载完成后出现,比较影响用户体验——这也是我为什么不直接把font作为get对象的原因。
  • Node本身作为后台和计算执行者,由于其单线程,并不适合进行这样的CPU密集型应用(计算字体时会阻塞http请求的处理),考虑在将来将字体计算模块分离出去进行进程级调用。
  • 兼容性强的fontface除了ttf,还需要woff和svg的支持,不过目前该服务还不提供另外两个文件格式的引用,这也直接导致了ie上不能顺利应用字体。好在fontmin很贴心地提供了文件格式转换插件,未来一定把这个功能补上。

最后,欢迎部署在自己服务器并使用,欢迎Fork,欢迎帮除bug ^ ^。