2012年5月31日 星期四

PHP imagettftext() could not read font

今天撞牆撞很大....由於手邊在碰的一個專案裡面需要產生類似驗證碼的圖片,他用了 imagecreate(), imagettftext(), imagesetpixel() 這些 PHP functions。聽說這支程式「曾經」是可以 work 的,不過在我手上就不 work,為了證明不是人品問題,一定要把這個 bug 修掉!
一開始我傻傻的在程式碼裡面翻找,但這樣實在太沒效率了,身為一個專業的....呃...雜工,一定要懂得善用工具!所以我用了..... command line php.....
$ php image.php > /dev/null
對啦,只不過是測試環境從 apache 換到 command line 底下了,這樣是有比較強嗎?有喔~因為我的測試環境有裝 xdebug,只要有 enable 就可以在 command line 底下看到 debugging messages,不用特別打開 stack trace。至於最後面把 standard output 導向 /dev/null 是為了不想看到他輸出的那張不完整的圖,那會干擾畫面。
首先第一步,我忘記詳細的訊息了,大意是說 imagecreate() is undefined 等等,這時候當然要去找找這函式有沒有需要什麼相依的 library 啊!找了一下發現,這些函式是由 PHP 的 GD module 所提供的,所以我迅速的安裝了 GD 模組...
$ sudo apt-get install php5-gd
接著,才是重頭戲的開始....
裝好 GD 模組之後,那些 function 都可以使用了,但是我卻一直收到這個錯誤訊息....
PHP Warning:  imagettftext(): Could not read font .....(後略)
當然這訊息也是伴隨著 stack trace 的,看看傳進去的字型參數,用的是相對路徑 include/font/font.ttf,當下懷疑是不是找不到檔案,用 realpath() 來幫忙看看...
echo realpath($font_path);
但是,咦,怎麼什麼都沒出現?大概花了 30 秒我才發現我做了蠢事....我已經把 standard output 丟掉了啊XD 上網找了一下「php stderr」立刻找到應該要這樣做
file_put_contents('php://stderr', realpath($font_path));
印出來之後,發現我給他的路徑是正確的,怎麼會這樣呢?苦惱許久,才想到要去查查 imagettftext() 的 manual,發現了驚人的事情!
原來,某些版本的 GD 會在你給他的路徑是相對路徑時,使用環境變數 GDFONTPATH 當作前綴,並且在最後補上 .ttf。另外,版本低於 2.0.18 的 GD 還不允許路徑裡面有空白字元。檢查了一下...嗯,路徑沒有空白字元,那會不會是路徑問題?參考官方 manual 提供的 sample code:
<?php
// Set the enviroment variable for GD
putenv('GDFONTPATH=' . realpath('.'));

// Name the font to be used (note the lack of the .ttf extension)
$font = 'SomeFont';
?>
我自己設定好了正確的 GDFONTPATH 也把後面的 .ttf 拿掉,依然不 work....到這裡我已經快崩潰了....即使我發瘋似的交叉測試拿掉或加上 .ttf、更改路徑、使用絕對路徑等等,都還是失敗。
接著我又開始想,會不會是別的原因?來看看檔案權限好了....
$ ls -l include/font/font.ttf
很好,檔案權限是 644 (rw-r--r--),應該沒問題吧?算了,死馬當活馬醫,改成 777!!
$ chmod 777 include/font/font.ttf
可想而知,還是炸了.....
在網路上找來找去,碰到類似問題的人大多是這幾種狀況:
  • 路徑錯誤 (前面提到的相對路徑問題)
  • 檔案從 windows 傳到 un*x 上就無法 work 了 (因為 un*x 對檔名是 case-sensitive 的)
  • ttf 寫成 tff (這是他自己耍笨了...)
很多人問問題會列出自己的 GD 版本,那我也來看看有沒有什麼端倪
$ php -i
...
gd

GD Support => enabled
GD Version => 2.0
FreeType Support => enabled
FreeType Linkage => with freetype
FreeType Version => 2.4.8
T1Lib Support => enabled
GIF Read Support => enabled
GIF Create Support => enabled
JPEG Support => enabled
libJPEG Version => unknown
PNG Support => enabled
libPNG Version => 1.2.46
WBMP Support => enabled

Directive => Local Value => Master Value
gd.jpeg_ignore_warning => 0 => 0
...
就沒問題嘛 orz
我已經確定我的路徑是完全正確的,而且也是絕對路徑,檔案權限也確定是可以讀取的,GD module 看起來沒有什麼錯,那到底是什麼問題?心灰意冷之際,我想說不然拿現有的其他字型來試試看好了。事實上該專案裡本來有好幾個字型檔,我早就試過幾個都不 work,結果沒想到改成用自己系統本來有的字型,就成功了!太崩潰了!!! 可是還是很高興,立馬回到本來的程式上,加上剛剛使用的字型的絕對路徑,一切都搞定了!(超開心)
所以總結而言,我一開始的環境沒辦法正確執行這個程式,只有兩個原因
  • 缺少 php-gd
  • 字型檔有某種錯誤
我對字型不太有研究,所以就無法深究該字型檔究竟有什麼問題,畢竟用 file 去看他還是個正常的 True Type font 啊....
$ file font.ttf 
font.ttf: TrueType font data
會不會跟編碼有關係呢?可是我用的只是一個 "1" 耶.....
好吧,雖然還是帶著懷疑,不過主要的問題算是解決了,結案!!!

3 則留言:

  1. 搜索到这里,同样的问题。最后发现,是FTP的上传编码问题,字体文件必须使用ASCII模式上传。

    回覆刪除
    回覆
    1. 感謝你 :) 由於事情久遠,我也無法回去確認了。不過說不定真的是我上傳的時候把字體給搞壞了....

      刪除