東拼西湊個 webfont service

一直以來,敝社網站面臨著有點令人尷尬的情況:為了閱讀上的美觀,網頁版型的標題應該要是襯線體(明體/宋體),但在不同平台上看到的字體卻總是無法有一致的體驗。為此,我們有幾種可能的作法:

  1. 把各種系統有的襯線體都寫進 CSS,讓瀏覽器自己去 fallback
  2. 直接用 CSS 把整個字體讀進來
  3. 使用 typekit 或是 justfont 這類的動態 webfont service

因為辦公室 90% 以上設備都是 Mac 或是 iOS(辦公室只有我用 PC,孤單寂寞覺得冷),好一段時間我們都使用 solution 1,依序以「冬青體、宋體 TC、儷宋體、新細明體」的順序寫進 CSS。但時不時會因為標題出現了冬青體沒有的漢字,fallback 變成了宋體、儷宋甚至是新細明體時的缺字或是 baseline 歪掉了的問題。baseline 的問題或許可以用 CSS 去解,但兩套不同字體的字重,甚或是不同語系的漢字筆順寫法,總是會在無意間就強暴了閱讀者的眼睛(用 Windows 則永遠都是新細明體,反正一直以來都很醜所以..)。

至於 solution 2,我們曾經嘗試過直接讀入 Google Font 提供的、體積較小的 cwTeXMing,但只有 Big5 字集的結果,是顯示時反而更容易出現缺字的情形,而超過 2MB 的大小,初次讀入頁面的時間與流量成本也是非常可觀的。其他支援 Unicode 的 IPA 明朝或是花園明朝的粗體實在不甚好看,體積也十分龐大,算不上是堪用的解法。

於是我們轉向了 solution 3,直接使用別人提供的解決方案確實是很愉快,把字體設定好,套上 style 便可打完收工,但 script 裝上去兩三天,我們就把免費的 quota 吃完了(typekit 是 25K PV/month,justfont 是 10K PV/month)。要嘛就交個保護費,要嘛就退回到 solution 1。因為服務費用貴貴的,所以我們就繼續讓 Windows 使用者眼睛受傷了(錯)。


精美的思源宋體

這個問題就這樣被我放置 play 了三四個月,直到最近思源宋體的推出。思源宋體在各種字重還有不同語系的筆畫寫法上都有著墨,顯示效果也比其他開源字體來得優異許多。在推出當天便可以用 typekit 所提供的 webfont 服務掛上網頁。但問題依舊,按次計算的費用十分可觀。
如果是在自家網頁顯示思源宋體,到底該怎麼辦呢?快速地想了一下,或許可以這樣:

  1. 付保護費
  2. 寫隻小程式,在 server 端把字體 render 成透明背景的圖片掛進頁面
  3. 自幹 webfont service

solution 1 好貴,我付不起(炸)(醜哭)。網頁整頁只用到標題幾個字,跟整頁字體都使用 webfont 的成本是一樣的,怎麼看都覺得很微妙。
solution 2 已經是行之有年的作法,但遇到 RWD 網頁就會爆炸,不同字體大小或是螢幕解析度也會帶來各種大大小小的問題,實在不是很好。至於一個字一張圖或是把字體轉成 SVG 圖片之類的作法因為太硬派了我們還是當作沒看到好了。

那麼,solution 3 呢?之前曾經看過 timdream 小帥提 – 提姆提拉米蘇 在 IE 6 上(對,不要懷疑,就是 IE 6)實作過中文的 EOT font subset,效果不錯,但印象中似乎是固定的文字集。我也忘了當時他是用甚麼工具去把 font subset 拆出來了(補充:小帥提在 comment 中表示是使用 WangShen Lu 大大提供的 script)。

於是就用 font subset 當關鍵字翻找了一下網路,很快地便找到了 Google 的網頁字體最佳化指南,當中提到了 fonttools 這套工具,其中提供了 pyftsubset 這個指令,可以依照傳入的文字生出不同的 font subset。

那麼,這麼好用的工具要怎麼安裝呢?請點進 GitHub 看一下,因為太簡單了所以這裡也不贅述。

於是我很快速地把 fonttools 裝起來,看了一下 pyftsubset 的說明:


pyftsubset –help

看來只要用 --text 或是 --text-file 就能讓程式生出有那幾個字的 font subset 了。於是便開心地下載了思源黑體的 OTF 檔(我們只用 SemiBold 這個字重),然後打了指令測試:


pyftsubset SourceHanSerifTC-SemiBold.otf –text-file=test.txt

嗯,在 R700 這台老筆電上跑一下就跑完了,看起來沒有錯誤,生出來的 subset 大概 12KB,所以趕緊來寫個 HTML 測試測試:


測試用的 HTML

「唔喔喔!真的會動耶!」


IT’S ALIVE!!

寫到這邊,其實會 Python 的朋友們已經可以自己動手用 fonttools 包個 service 出來了。

但資質駑鈍如我,還是比較孰悉 PHP,所以就用了萬能的 system call 去吐產生的字體檔,輸入的 query string 就是需要使用的文字字元。

作法有很多種,我是直接把輸出導到 /dev/stdout,把結果直接吐出來,因為程式碼太醜了所以我就不貼上來了,大致上就是用 shell_exec("pyftsubset 一大串醜醜的東西") 這樣(遠目)

至於效能問題.. 用 system call 真的.. 會.. 比較.. 慢,不過只要 cache 做得好,除了第一次讀入的人比較悲劇之外,後面的 request 應該堪稱迅速。秉持著一貫地懶惰,我也只用了目前線上機器架構中既有的東西拼湊:對外放個 cache server (Varnish Cache),更外面再放個 CDN (Cloudflare, free tier),防止大量 requests 打爆機器。

嗯,server 端的部分真的就只有這樣,真正有作用的程式碼也大概只有兩行,就算寫得再爛都不會太難讀懂(掩面),需要特別注意的是要送出 CORs header,還有記得防止 XSS 還有 arbitrary code execution 之類的攻擊。

接著就是 client 部分啦。

我採取的是在讀入頁面之後,用 DOM selector 把需要套用字體的物件文字撈出來,sort、unique 之後,送回 server 生出相對應的字集字體,再用 jQuery 套上去的 approach,如此作法有幾個好處:

  1. 對於用到同樣字集的網頁,request uri 會長得一樣,cache 會比較簡單
  2. 產生的 request uri 相對短一點(真的就是短一點點)
  3. script 寫一次就可以到處撒(咦)

那麼,該怎麼動態產生 font-face 的部分呢?嗯,我也不知道(好不負責)

因為我很懶惰,所以用了現成的 FontFace jQuery Plugin 來載入字體。

很快速地讀了一下程式碼,依照需求寫出的 client 端程式碼看起來大概是這樣:


總之就是一坨義大利麵

然後只要打個 loadFont('.someClass'); 就可以動態載入字體了,真是非常愉快呢 ww

需要注意的是,如果在網頁裡要 call 很多次,每次的 font name 要不一樣,否則套用到不同物件時只會讀入第一次載入的 font name 的字集。在這裡我使用了 stack overflow 大法找到了很好很強大的解答,測試了一下會動所以就沒有再多做甚麼修改。

於是這東拼西湊,這個速度不是很快 (!) 而且因為用 query string 所以不能傳太多字進去 (!!) 然後還只能吐一種字體 (!!!) 還弱弱第只支援 modern browser 的 webfont service 就跑起來啦 wwww

從此之後,我們的網頁就有了精美的中文襯線體:


精美啊!

至於讀入字體時的 FOUT, FOIT, FOFT 問題,可以看一下這篇文章,裡面有提供幾個 robust 的解法,加上去就可以解決一些惱人的字體閃爍問題喔 :D

雖然自己 host 還是比不上那些 service provider 的速度,但租台小台 VPS 的成本非常低,加上 CDN 後效能其實沒那麼慘,在用量不大的狀況下就湊合著用吧。

以上就是這次的義大利麵料理指南,大家快去找個小廚房(利益揭示:連結是我的 DigitalOcean 推薦碼,申請成功您可以拿到 USD $10 credit,最小台的機器可以用兩個月喔)來料理一下吧 (?)

補充:剛剛 高偉格 大大說可以直接用 font-spider 來建 service,啊啊啊相見恨晚 QQ

DreamHost 與 twbbs.org

嗯.. 上週四我發現 DreamHost 有個按鈕,
說按下去會變成無限空間與無限流量,
按下去之後.. 真的變成無限空間無限流量了。

有圖有真相。

20081130_DH

其實這張截圖是已經放了一些東西上去,
之前用量還不到 1KB.. 整個非常誇張 (汗)

於是腦筋就動到了找不到家放的松合論壇,
但很快地便遇到第一個問題,它用的是 .twbbs.org 網域。

上網找了半天,看到了這篇文章
設定之後才發現,只有 .twbbs.org.tw 可以用,
雖然 .twbbs.org 與 .twbbs.org.tw 使用同樣的 record,
但是 DreamHost 的 Name Server 只認識有 .tw 那個..

於是就又寫了客服,接著,一切都順了 XD

現在,同樣的方式設定 .twbbs.org 也可以使用囉;
如果同時需要設定 .twbbs.org.tw 與 .twbbs.org,
根據客服的回答,因系統限制,則須在 panel 自行新增。

以上 :p

關於那個 project

是的,我是指上次放出來的那個夭壽準關鍵字
因為我太無聊了,所以稍稍介紹一下程式運作方式。


首先呢,就是蒐集一大票描述資訊,
它們最初看起來可能會是這樣。

20081019_wff_fig01

有些人可能以為,我接著把資料拿去斷詞了,
但事實上,蒐集到的資訊沒有斷詞的必要,
我只把句子全部斷開,變成一個字一個字的形狀,
看起來會像是下圖這樣的形狀。

20081019_wff_fig02

聰明的你應該會發現,裡面有些文字是無用的,
利用一些簡單的字元特性,我們可以很快地清除符號,
清理完之後,資料會變成像下圖這樣。

20081019_wff_fig03

看到這邊,我想不會寫程式的人該也知道,
只要把看到最多次的字詞挑出來,就會是關鍵字了啊!

20081019_wff_fig04

至於該怎麼挑,有很多方式,
有些可以直接抽取出有用的 substring,
在此要說明的是,圖中的資料是人工挑選過的,
原始輸出資料並沒有這麼乾淨。

20081019_wff_fig05

嗯,大概就是這樣。


接下來可以做什麼呢,到底?

accessibility

這個被譯為是「可及性」,
或是更貼切些,「親和力」的字,
是做網頁時,不時提醒自己要注意的重點。

之前實驗室接了學校的校內計畫,
計畫內容,大致上就是做出一個網站,
讓其他的子計畫可以上傳資料上去。

我不是很確定學校的網站,
是不是一定都要符合無障礙規範,
老實說,我覺得這規範實在是擾人啊。

做網頁時,我通常會把瀏覽器的圖片與 CSS 拿掉,
讓網頁呈現它「原來」應該呈現的樣式,
如果拿掉了圖片、多媒體資料與 CSS 後,
頁面仍能具備高度的可讀性,
我就認為,這是具備一定親和力的網頁。

那麼,我們來看看通過所謂「無障礙」標章的網頁吧。

首先是無障礙網頁空間服務網,
也就是提供檢測與標章登錄的那個網頁。

20081014_enable-01

從這篇邊我們不難看出,用表格排版的缺陷;
但除此之外,似乎還算是能夠理解的頁面。

接下來,是通過 AAA 標章的網頁。

20081014_enable-02

如果使用者的瀏覽器不幸地 (?) 支援 JavaScript,
他將會看到像上面這張圖的驚人網頁。

一開始我嚇了一跳,不過發現文字是 JS 產生後,
關掉 JS 再拿掉 CSS 與圖片,畫面變成這樣。

20081014_enable-04

我想,政府機關大概真的是很喜歡用表格排版.. ||

最後是我們做的網頁,到目前為止都還未申請無障礙標章。

20081014_enable-03

嗯哼。

Test 04: 夭壽準關鍵字

我的網站上一直有個目錄叫 “test”,
顧名思義,就是一些測試的玩具,
因為前三個作品都滿失敗的,
我並沒有在 blog 上面提過它們。

昨天胡亂寫出了新的產品,「夭壽準關鍵字」,
因為結果挺有趣的,所以決定扔上來給大家玩玩看 :p

魷魚由於我沒有什麼美工細胞,
目前網頁看起來呆呆的:

20080915_KWFinder-01

整個頁面只有一個輸入欄位,
與一些先進所做的有趣的產品連結;
這個產品的使用方式非常簡單,
只要在欄位輸入無名小站的 ID..

20080915_KWFinder-02

按下「啾咪」之後,系統就會使用神秘公式,
計算出與這個 ID 可能有關的關鍵字,像這樣:

20080915_KWFinder-03

至於這個系統有多準呢?

嗯.. 老實說我也不知道 (炸)

因為系統認識的 ID 是有限的,
有些比較不有名的人,可能會沒有關鍵字,
而有些人,系統可能會找不到任何東西。

遇到這種狀況的時候,請不要灰心氣餒,
因為這就是人生啊這個系統本來就笨笨的,
所以需要一段時間才會彙整好資料..

是說,目前只有一台苦命的電腦在算這些東西,
資料可能要花上一年半載一段時間才會更新 (汗)

另外,這個系統似乎常常爆炸,
使用時如果遇到什麼狀況,請裝做沒有看到 (炸)

突然想到的小招式

剛剛在 Twitter 上面看到 tenz 提到的 Ubiquity
這才想到,Opera 的網址列搜尋功能似乎可以貼 tweet,
於是我就嘗試了一下。

首先,登入 Twitter。

20080827_opera-twitter01

接著在輸入欄點選右鍵,「建立搜尋」。

20080827_opera-twitter02

然後,設定快速鍵後按下確定。

20080827_opera-twitter03

試試看能不能 post tweet。

20080827_opera-twitter04

看來這招似乎是有用啊。

20080827_opera-twitter05

稍稍看了一下 Ubiquity,它能做的事情似乎更多,
但我在想,或許可以用 userscript 來達成某些功能,
不過這就已經不在我的業務範圍了 (飄)