Home  >  Article  >  Backend Development  >  在PHP中用描点法“绘制”中文_PHP

在PHP中用描点法“绘制”中文_PHP

WBOY
WBOYOriginal
2016-06-01 12:38:20858browse

前言:
现在,越来越多的人喜欢上网了,越来越多的人拥有了自己的个人主页。随着各种自动化软件工具的出现,制作网页越来越简单。但,由于特效随处可得,创新的东西,越来越少。说不定,哪天,你会发现某个网站上的计数器和自己的一模一样。网页越做越老练。网页上的东西,也越来越多,越来越丰富。
设问:

在网页上,如果我要添加一个计数器:
以前,那就去空间提供商那里要个链接,或其他地方去复制个地址,但这一些,总归是别人做的,好不好,你是没有太多的发言权的,只能一个一个地找。
在网页上,我要将一些信息发布出去:
假如信息是文本,做一个新页面,加个链接;
是数据,做一个新页面,加个链接:
可要是这些数据经常更新,甚至,每小时、每分钟,都可能会改变,你是否愿意守在电脑前,不停修改、上传呢?(咱可不是商业网站,没有人愿意为你而烧钱。)
而留言板、聊天室、论坛,这些,决不是单靠HTML和javascript就能搞定的。
为了实现更多的自动控制,可以使用CGI(Common Gateway Interface)程序来实现这些功能。

软件需求:
PHP:GD Library
配置支持PHP的服务器。我用OmniHTTPd Professional

对于计数器和实时数据统计、发布,我们可以用图片来完成。在图片中输出文字。
在PHP中,要创建一个图片,并在上面显示点内容,基本步骤如下:


//http头,告诉浏览器,这是一个GIF图片
header ("Content-type: image/gif");
// 要画画,先要有花布不是?创建一个400×300调色板图像
$im = imagecreate (400, 300);
$black = imagecolorallocate ($im, 0, 0, 0);
// 默认黑色背景。
//(默认,是指第一个定义的颜色。如果在此行代码前面定义了另一个颜色,那么,最先定义的那个,就是默认背景颜色。)
$red = imagecolorallocate ($im, 255, 0, 0);
//红色。如果这两行交换,你会发现背景是红色,文字是黑色。
$string="1234567890";
// 要绘制的字符
imagestring ($im,12,10,10,$string,$red);
//在(10,10)开始绘制字符串
imagepng ($im);
// 以png格式输出,也可以用imagejpeg($im);或magegif($im);但后者,如果GD版本高于1.6,就不能用了。
imagedestroy ($im);
// 结束,清除所有占用的内存资源
?>

上面示例,在400×300的图片上,自点(10,10)开始,绘制12磅的"1234567890"。你有没有注意到这张图片的大小是:251字节!你也可以试试其他的输出格式。
图片的大小,与图片中非背景象素点数有关,跟输出多少象素无关。

然而,有一个问题。
你可以用imagestring()输出如下的信息:
imagestring($im,1,0,0,"abcdefghijklmnopqrstuvwxyz0123456789~!@#$%^&*()_+{}|:"?[]';,./",$red);
可是,你无法正确输出中文!!!
imagestring($im,1,0,0,"啊",$red);
你看到的,决不是中文!!而是乱码。
PHP默认的字符集是UTF-8,而简体中文是GB2312。

如何解决?!
为了解决这个问题,你可以让PHP加载扩展模块php_iconv.dll(UNIT下的后缀名是.SO),不过,有时候,可能不能正常工作。本来,我要把一段测试代码放上来,可这次,怎么弄都没有成功。为了避免错误,我还是不把它们放上来了。
但,最致命的,如果你的空间服务商关闭了该扩展模块,或者,甚至禁止了加载模块的DL()函数,那,你就只能跟中文BYE-BYE了。
还好,还有其他办法。
可以通过字符映射,将预先转换好的码表中字符输出来。但,你需要一张码表!
或者,手工绘制每一个中文的每一个点!感觉怎么样?!

好,来吧,我们一起来画字!

画字,首先要知道怎么画。
初中的简单函数,学过吧?要画出函数的图形,做过吧?算出某点的坐标,然后连接两相邻点。这种方法,叫描点法。
我们要做的,是尽量多地将点算出来,然后在相应坐标显示出来。
你是否听说过点阵打印机、点阵汉字?
在输出汉字时,它们是用一个个点来表示的。

在某个坐标上显示一个某种颜色的点的函数是:
int imagesetpixel ( resource image, int x, int y, int color)
假定我要在坐标(100,100)处显示一个白色的点,那么,只需如下代码:


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);
?>

也就是说,我们只要获取某个汉字的所有点的信息,我们就能够通过这个函数,输出那个汉字。

在文件chs16.fon里,保存的,是国标区位码表(国家标准信息交换用汉字编码基本字符集GB-2312)。它是汉字的点阵字库。(WIN98系统中,此文件在c:windowscommand下。如果你要把它放在UNIX系统下使用,请注意大小写。如果没有,你可以在文末找到链接。)
它是MSDOS时代的,但,好东西,还是应该拿出来一用的。

从chs16.fon里,我们可以读取汉字的点阵数据。每个汉字,都是由16×16个点构成的。笔划走过的地方,点的值为1,否则为0;每个点占用一个位,每8个点构成一个字节。那么,一个汉字,就需要(16×16÷8=32)字节。

下面这个实例,是为了说明字符点阵的表示方法。
这里,定义了一个8×8的矩阵,显示了一个字母C,白色的方块用0表示,黑色方块用1表示,那么,这八行图形的代码分别是:




二进制表示
十六进制表示

0
00000000
0x00

1
00111110
0x3E

2
01110000
0xE0

3
01110000
0xE0

4
01110000
0xE0

5
01110000
0xE0

6
00111110
0x3E

7
00000000
0x00




要输出这些点的话,就需要先画第一行,然后第二行、第三行……到最后一行。
用一个循环:
for($hang=0;$hang在每一行中,有八个格子,需要分别绘制,从第一个,然后第二个、第三个……到最后一个。
用一个循环:
for($gezi=0;$gezi两个循环联列:
for($hang=0;$hangfor($gezi=0;$gezi{ //在这里,我们就能输出点了。
imagesetpixel ( $image, $gezi, $hang, $color);
}

但,我们如何知道到哪里去读某个汉字的点阵数据呢?

一般的字符,比如ASCII码,是用数字0--127(即二进制00000000到01111111)来表示,而中文,则是用两个高位为1的字节(100000000 100000000)表示。如: 半角字符"A",机内码为 (01000001)(它实际上是ASCII码值)。
下面,让我们打开"字符映射表"看看吧。如果你为了节省磁盘,没有安装,那就装一下,不大。如果不会安装,那你就接下去看我乱侃吧。
在字符映射表里,字体选择"楷体_GB2312",点击"特殊符号",这时,你可以看到国标区位码表,从字符(10110000 10100001)开始,一直到(10011111 11111111)。
全角字符"A",机内码为:(10100011 11000001)(它实际是两个高位为1的ASCII码)。
中文"啊"的机内码,是(10110000 10100001);
在GB-2312字符集中,"啊"在表中位置是第16区第1位,这个坐标(16,1),用二进制表示,就是(00010000,00000001)。这,就是"啊"的区位码。
请看:

中文字符: 啊
机内码: (10110000 10100001)
区位码: (00010000,00000001)
相差: (10100000,10100000)

所以,
区位码与机内码的换算公式为 【区位码】+(10100000 10100000)=【机内码】。即:
区位码0 + (10100000) = 机内码0;
区位码1 + (10100000) = 机内码1;
这样的话,点阵数据,就可以通过汉字"机内码"-> "区位码"进行索引、查找。

前面已经讲了一个汉字,在表中要占用32字节,所以,我们定义了一个含有32个元素的数组:
$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个字节数据。

接下来的问题,某一个字符,到底保存在文件的什么位置呢?

由于一个汉字用了32个字节,而GB-2312区位码表表有94行、94列,那么,只要知道该字符在表中是第几个,再乘以32就行了。
所以定义偏移量:
$offset=(94*($qh-1)+($wh-1))*32;
$qh表示区(qu)、$wh表示位(wei);减1,是因为PHP从0开始计数。
位置找到,就只需要用fseek()函数定到码表的这个位置,然后读32字节到$buffer就行了。
另外,由于中文是由两个字节组成,而前面给出的点阵示例是8位,一个字节,所以,画点的代码要修改一下:
for($hang=0;$hangfor($j=0;$jfor($gezi=0;$gezi{
imagesetpixel ( $image, $gezi +8*$j, $hang , $color);
}

好,我们开始编程吧!


/*************************************
* 文件名:'draw1.0.inc.php
** 中文显示点阵输出 version 1.0
** 只提供简单的操作:输出默认大小的纯中文字符串到图片的坐标(0,0)上
** 更多功能,请见下一版本。
*
****************************************/
function draw($image,$string,$color)
{
$fp=fopen("chs16.fon","rb");//二进制方式读点阵字库chs16.fon
if (!feof($fp))//如果文件指针到了文件末尾,退出,不要忘记关闭文件
{
while($string)//当字符串不为0
{
$qh=ord(substr($string,0,1))-0xa0;
$wh=ord(substr($string,1,2))-0xa0;
/* 这两行代码,其实是获取一个中文的机内码。
substr($string,0,1);是从$string中获取第一个字节,然后,通过ord();将这个字符转换为整数。(由于PHP不支持无符号整数,所以没有这一步转换的话,你就只能得到一个0。)在转换为整数之后,就能进行计算了。机内码减去0xa0(10100000),就得到了区位码。
substr($string,1,2);是获取$string中的第二个字节。*/
$offset=(94*($qh-1)+($wh-1))*32;
/*得到了汉字的区位值后,就开始计算偏移量了。*/
fseek($fp,$offset,SEEK_SET);
/*在字库文件$fp中,将文件指针定位到偏移量。*/
$buffer=preg_split('//', fread($fp,32), -1, PREG_SPLIT_NO_EMPTY);
/* fread($fp,32);是从$fp中读取32个字节数据,然后通过preg_split();分配到数组$buffer中。preg_split();是一个支持正则表达的函数。关于正则表达式,我正在学习中。为什么这样用,我也不知道。PHP手册里有本实例。*/
for($i=0;$ifor($j=0;$jfor($k=0;$kif(((ord($buffer[$i*2+$j])>>(7-$k))&0x01))//如果这个点的值为1,输出;否则,没有
{
imagesetpixel($image,$x+8*$j+$k, $i, $color);
}
$string=substr($string,2); //中文由两个字节表示,所以,输出一个汉字后,就要去掉两个字节。
$x=24; //一个汉字输出结束,空开一点,给下一个汉字。因为这个汉字是16×16点,那么,$x的值设为16,就够了。但,太挤了不是?
}
}
fclose($fp);
}
下面,我给出一个测试实例:


header ("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);
?>

对于这个函数,我们还可以进行扩充,以实现不同的效果。

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn