在 PHP 中用描點(diǎn)法“繪制”中文
為了實(shí)現(xiàn)更多的自動(dòng)控制,可以使用CGI(Common Gateway Interface)程序來實(shí)現(xiàn)這些功能。軟件需求:PHP:GD Library配置支持PHP的服務(wù)器。我用OmniHTTPd Professional
對(duì)于計(jì)數(shù)器和實(shí)時(shí)數(shù)據(jù)統(tǒng)計(jì)、發(fā)布,我們可以用圖片來完成。在圖片中輸出文字。在PHP中,要?jiǎng)?chuàng)建一個(gè)圖片,并在上面顯示點(diǎn)內(nèi)容,基本步驟如下:
<?php//http頭,告訴瀏覽器,這是一個(gè)GIF圖片header ('Content-type: image/gif')// 要畫畫,先要有花布不是?創(chuàng)建一個(gè)400×300調(diào)色板圖像$im = imagecreate (400, 300)$black = imagecolorallocate ($im, 0, 0, 0)// 默認(rèn)黑色背景。//(默認(rèn),是指第一個(gè)定義的顏色。如果在此行代碼前面定義了另一個(gè)顏色,那么,最先定義的那個(gè),就是默認(rèn)背景顏色。)$red = imagecolorallocate ($im, 255, 0, 0)//紅色。如果這兩行交換,你會(huì)發(fā)現(xiàn)背景是紅色,文字是黑色。$string='1234567890'// 要繪制的字符imagestring ($im,12,10,10,$string,$red)//在(10,10)開始繪制字符串imagepng ($im)// 以png格式輸出,也可以用imagejpeg($im);或magegif($im);但后者,如果GD版本高于1.6,就不能用了。imagedestroy ($im)// 結(jié)束,清除所有占用的內(nèi)存資源?>
上面示例,在400×300的圖片上,自點(diǎn)(10,10)開始,繪制12磅的'1234567890'。你有沒有注意到這張圖片的大小是:251字節(jié)!你也可以試試其他的輸出格式。圖片的大小,與圖片中非背景象素點(diǎn)數(shù)有關(guān),跟輸出多少象素?zé)o關(guān)。然而,有一個(gè)問題。你可以用imagestring()輸出如下的信息:imagestring($im,1,0,0,'abcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_+{}|:'<>?[]';,./',$red);可是,你無法正確輸出中文?。。magestring($im,1,0,0,'啊',$red);你看到的,決不是中文?。《莵y碼。PHP默認(rèn)的字符集是UTF-8,而簡(jiǎn)體中文是GB2312。
如何解決?!為了解決這個(gè)問題,你可以讓PHP加載擴(kuò)展模塊php_iconv.dll(UNIT下的后綴名是.SO),不過,有時(shí)候,可能不能正常工作。本來,我要把一段測(cè)試代碼放上來,可這次,怎么弄都沒有成功。為了避免錯(cuò)誤,我還是不把它們放上來了。但,最致命的,如果你的空間服務(wù)商關(guān)閉了該擴(kuò)展模塊,或者,甚至禁止了加載模塊的DL()函數(shù),那,你就只能跟中文BYE-BYE了。還好,還有其他辦法。可以通過字符映射,將預(yù)先轉(zhuǎn)換好的碼表中字符輸出來。但,你需要一張碼表!或者,手工繪制每一個(gè)中文的每一個(gè)點(diǎn)!感覺怎么樣?!
好,來吧,我們一起來畫字!
畫字,首先要知道怎么畫。初中的簡(jiǎn)單函數(shù),學(xué)過吧?要畫出函數(shù)的圖形,做過吧?算出某點(diǎn)的坐標(biāo),然后連接兩相鄰點(diǎn)。這種方法,叫描點(diǎn)法。我們要做的,是盡量多地將點(diǎn)算出來,然后在相應(yīng)坐標(biāo)顯示出來。你是否聽說過點(diǎn)陣打印機(jī)、點(diǎn)陣漢字?在輸出漢字時(shí),它們是用一個(gè)個(gè)點(diǎn)來表示的。
在某個(gè)坐標(biāo)上顯示一個(gè)某種顏色的點(diǎn)的函數(shù)是:int imagesetpixel ( resource image, int x, int y, int color)假定我要在坐標(biāo)(100,100)處顯示一個(gè)白色的點(diǎn),那么,只需如下代碼:
<?php header ('Content-type: image/gif') $image = imagecreate (400, 300) $black = imagecolorallocate ($image, 0, 0, 0) $white = imagecolorallocate ($image, 255, 255, 255) // 定義白色 imagesetpixel ( $image, 100, 100, $white) imagepng ($image) imagedestroy ($image)?>
也就是說,我們只要獲取某個(gè)漢字的所有點(diǎn)的信息,我們就能夠通過這個(gè)函數(shù),輸出那個(gè)漢字。在文件chs16.fon里,保存的,是國標(biāo)區(qū)位碼表(國家標(biāo)準(zhǔn)信息交換用漢字編碼基本字符集GB-2312)。它是漢字的點(diǎn)陣字庫。(WIN98系統(tǒng)中,此文件在c:windowscommand下。如果你要把它放在UNIX系統(tǒng)下使用,請(qǐng)注意大小寫。如果沒有,你可以在文末找到鏈接。)它是MSDOS時(shí)代的,但,好東西,還是應(yīng)該拿出來一用的。
從chs16.fon里,我們可以讀取漢字的點(diǎn)陣數(shù)據(jù)。每個(gè)漢字,都是由16×16個(gè)點(diǎn)構(gòu)成的。筆劃走過的地方,點(diǎn)的值為1,否則為0;每個(gè)點(diǎn)占用一個(gè)位,每8個(gè)點(diǎn)構(gòu)成一個(gè)字節(jié)。那么,一個(gè)漢字,就需要(16×16÷8=32)字節(jié)。
下面這個(gè)實(shí)例,是為了說明字符點(diǎn)陣的表示方法。這里,定義了一個(gè)8×8的矩陣,顯示了一個(gè)字母C,白色的方塊用0表示,黑色方塊用1表示,那么,這八行圖形的代碼分別是:
行
二進(jìn)制表示
十六進(jìn)制表示
0
00000000
0x00
1
00111110
0x3E
2
01110000
0xE0
3
01110000
0xE0
4
01110000
0xE0
5
01110000
0xE0
6
00111110
0x3E
7
00000000
0x00
要輸出這些點(diǎn)的話,就需要先畫第一行,然后第二行、第三行……到最后一行。用一個(gè)循環(huán): for($hang=0;$hang<8;$hang++)在每一行中,有八個(gè)格子,需要分別繪制,從第一個(gè),然后第二個(gè)、第三個(gè)……到最后一個(gè)。用一個(gè)循環(huán): for($gezi=0;$gezi<8;$gezi++)兩個(gè)循環(huán)聯(lián)列: for($hang=0;$hang<8;$hang++)for($gezi=0;$gezi<8;$gezi++){ //在這里,我們就能輸出點(diǎn)了。 imagesetpixel ( $image, $gezi, $hang, $color);}
但,我們?nèi)绾沃赖侥睦锶プx某個(gè)漢字的點(diǎn)陣數(shù)據(jù)呢?
一般的字符,比如ASCII碼,是用數(shù)字0--127(即二進(jìn)制00000000到01111111)來表示,而中文,則是用兩個(gè)高位為1的字節(jié)(100000000 100000000)表示。如:; 半角字符'A',機(jī)內(nèi)碼為 (01000001)(它實(shí)際上是ASCII碼值)。下面,讓我們打開'字符映射表'看看吧。如果你為了節(jié)省磁盤,沒有安裝,那就裝一下,不大。如果不會(huì)安裝,那你就接下去看我亂侃吧。在字符映射表里,字體選擇'楷體_GB2312',點(diǎn)擊'特殊符號(hào)',這時(shí),你可以看到國標(biāo)區(qū)位碼表,從字符(10110000 10100001)開始,一直到(10011111 11111111)。 全角字符'A',機(jī)內(nèi)碼為:(10100011 11000001)(它實(shí)際是兩個(gè)高位為1的ASCII碼)。 中文'啊'的機(jī)內(nèi)碼,是(10110000 10100001);在GB-2312字符集中,'啊'在表中位置是第16區(qū)第1位,這個(gè)坐標(biāo)(16,1),用二進(jìn)制表示,就是(00010000,00000001)。這,就是'啊'的區(qū)位碼。請(qǐng)看:中文字符:啊機(jī)內(nèi)碼:;(10110000 10100001)區(qū)位碼:;(00010000,00000001)相差:;;;(10100000,10100000)所以, 區(qū)位碼與機(jī)內(nèi)碼的換算公式為 【區(qū)位碼】+(10100000 10100000)=【機(jī)內(nèi)碼】。即: 區(qū)位碼0 + (10100000) = 機(jī)內(nèi)碼0; 區(qū)位碼1 + (10100000) = 機(jī)內(nèi)碼1;這樣的話,點(diǎn)陣數(shù)據(jù),就可以通過漢字'機(jī)內(nèi)碼'-> '區(qū)位碼'進(jìn)行索引、查找。
前面已經(jīng)講了一個(gè)漢字,在表中要占用32字節(jié),所以,我們定義了一個(gè)含有32個(gè)元素的數(shù)組: $buffer=array(0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0); 用來保存從字庫讀出的32個(gè)字節(jié)數(shù)據(jù)。
接下來的問題,某一個(gè)字符,到底保存在文件的什么位置呢?
由于一個(gè)漢字用了32個(gè)字節(jié),而GB-2312區(qū)位碼表表有94行、94列,那么,只要知道該字符在表中是第幾個(gè),再乘以32就行了。所以定義偏移量: $offset=(94*($qh-1)+($wh-1))*32; $qh表示區(qū)(qu)、$wh表示位(wei);減1,是因?yàn)镻HP從0開始計(jì)數(shù)。位置找到,就只需要用fseek()函數(shù)定到碼表的這個(gè)位置,然后讀32字節(jié)到$buffer就行了。另外,由于中文是由兩個(gè)字節(jié)組成,而前面給出的點(diǎn)陣示例是8位,一個(gè)字節(jié),所以,畫點(diǎn)的代碼要修改一下:for($hang=0;$hang<16;$hang++)for($j=0;$j<2;$j++)//因?yàn)槭莾蓚€(gè)字節(jié),所以插入一個(gè)循環(huán)for($gezi=0;$gezi<8;$gezi++){ imagesetpixel ( $image, $gezi +8*$j, $hang , $color);}
好,我們開始編程吧!
/**************************************;;文件名:'draw1.0.inc.php**;中文顯示點(diǎn)陣輸出version 1.0**;只提供簡(jiǎn)單的操作:輸出默認(rèn)大小的純中文字符串到圖片的坐標(biāo)(0,0)上**;更多功能,請(qǐng)見下一版本。*****************************************/function draw($image,$string,$color){ $fp=fopen('chs16.fon','rb')//二進(jìn)制方式讀點(diǎn)陣字庫chs16.fon if (!feof($fp))//如果文件指針到了文件末尾,退出,不要忘記關(guān)閉文件 { while($string)//當(dāng)字符串不為0 { $qh=ord(substr($string,0,1))-0xa0 $wh=ord(substr($string,1,2))-0xa0/* 這兩行代碼,其實(shí)是獲取一個(gè)中文的機(jī)內(nèi)碼。 substr($string,0,1);是從$string中獲取第一個(gè)字節(jié),然后,通過ord();將這個(gè)字符轉(zhuǎn)換為整數(shù)。(由于PHP不支持無符號(hào)整數(shù),所以沒有這一步轉(zhuǎn)換的話,你就只能得到一個(gè)0。)在轉(zhuǎn)換為整數(shù)之后,就能進(jìn)行計(jì)算了。機(jī)內(nèi)碼減去0xa0(10100000),就得到了區(qū)位碼。substr($string,1,2);是獲取$string中的第二個(gè)字節(jié)。*/ $offset=(94*($qh-1)+($wh-1))*32/*得到了漢字的區(qū)位值后,就開始計(jì)算偏移量了。*/ fseek($fp,$offset,SEEK_SET)/*在字庫文件$fp中,將文件指針定位到偏移量。*/ $buffer=preg_split('//', fread($fp,32), -1, PREG_SPLIT_NO_EMPTY)/* fread($fp,32);是從$fp中讀取32個(gè)字節(jié)數(shù)據(jù),然后通過preg_split();分配到數(shù)組$buffer中。preg_split();是一個(gè)支持正則表達(dá)的函數(shù)。關(guān)于正則表達(dá)式,我正在學(xué)習(xí)中。為什么這樣用,我也不知道。PHP手冊(cè)里有本實(shí)例。*/ for($i=0$i<16$i++) //點(diǎn)陣的行數(shù):16 列數(shù)也應(yīng)該是16 for($j=0$j<2$j++) //因?yàn)槭莾蓚€(gè)字節(jié),那么,就要一個(gè)一個(gè)地畫了 for($k=0$k<8$k++) //每個(gè)字節(jié),都有8個(gè)點(diǎn)的數(shù)據(jù) if(((ord($buffer[$i*2+$j])>>(7-$k))&0x01))//如果這個(gè)點(diǎn)的值為1,輸出;否則,沒有 { imagesetpixel($image,$x+8*$j+$k, $i, $color) } $string=substr($string,2) //中文由兩個(gè)字節(jié)表示,所以,輸出一個(gè)漢字后,就要去掉兩個(gè)字節(jié)。 $x=24 //一個(gè)漢字輸出結(jié)束,空開一點(diǎn),給下一個(gè)漢字。因?yàn)檫@個(gè)漢字是16×16點(diǎn),那么,$x的值設(shè)為16,就夠了。但,太擠了不是? } } fclose($fp)}
下面,我給出一個(gè)測(cè)試實(shí)例:
<?phpheader ('Content-type: image/gif') include 'draw1.0.inc.php'$im = imagecreate (400, 300) $black = imagecolorallocate ($im, 0, 0, 0)$string='中文'drawer($im,$string)imagepng ($im)imagedestroy ($im)?>
對(duì)于這個(gè)函數(shù),我們還可以進(jìn)行擴(kuò)充,以實(shí)現(xiàn)不同的效果。
相關(guān)附件1:chs16.fon 本地下載相關(guān)附件2:代碼實(shí)例打包下載一個(gè)實(shí)時(shí)用戶留言板留言數(shù)量統(tǒng)計(jì)表實(shí)例:http://medlem.spray.se/letmegetone/messageboard/userinfo.htm
作者聯(lián)系方式:Homepage: http://medlem.spray.se/letmegetoneE-mail: [email protected]
