公司的打印机最近新添加一个打印图片的功能,具体是用户在后台设置他的logo,服务器接收到图片文件,转换单色的bmp文件(热敏打印机),再由php将单色bmp图片转换成bin文件,里面包含了图片的头信息和数据。做这么个小功能还花了我不少时间,走了很多弯路,各位看官请容我一一讲述吧。
思路1:使用PHP扩展用c语言来完成这个操作
PHP扩展开发及内核应用
1.下载php的完整版源码,我的是php-5.4.41版本的,因为公司的服务器就是使用的5.4.41的,不然版本不对,扩展就算开发好了,后面也是无法添加到php里的(可以兼容老版本的,但是开发版本不能比服务器版本高)。 然后用cd命令跳转到php-5.4.41/ext目录下: cd /home/pchangl/php-5.4.41/ext 然后在主文件夹下创建一个extension_def的目录(这个目录不是必须的,只是我为了方便管理.def的文件才建立的),在这个文件夹下建立一个叫做pchangl.def的文件(def的文件是扩展方法的签名)。 这个时候建立好了def的文件,我们就开始使用php/ext目录下的ext_skel小工具为我们的扩展生成框架。root@pchangl:/home/pchangl/php-5.4.41/ext #ext_skel --extname=pchangl --proto=/home/pchangl/php-extension/pchangl.def 这个时候就给我们生成了扩展的框架:
#ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" #include "php_pchangl.h" static int le_pchangl; const zend_function_entry pchangl_functions[] = { PHP_FE(confirm_pchangl_compiled, NULL) /* For testing, remove later. */ PHP_FE_END /* Must be the last line in pchangl_functions[] */ }; zend_module_entry pchangl_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "pchangl", pchangl_functions, PHP_MINIT(pchangl), PHP_MSHUTDOWN(pchangl), PHP_RINIT(pchangl), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(pchangl), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(pchangl), #if ZEND_MODULE_API_NO >= 20010901 PHP_PCHANGL_VERSION, #endif STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_PCHANGL ZEND_GET_MODULE(pchangl) #endif PHP_MINIT_FUNCTION(pchangl) { return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(pchangl) { return SUCCESS; } PHP_RINIT_FUNCTION(pchangl) { return SUCCESS; } PHP_RSHUTDOWN_FUNCTION(pchangl) { return SUCCESS; } PHP_MINFO_FUNCTION(pchangl) { php_info_print_table_start(); php_info_print_table_header(2, "pchangl support", "enabled"); php_info_print_table_end(); } PHP_FUNCTION(confirm_pchangl_compiled) { char *arg = NULL; int arg_len, len; char *strg; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg, &arg_len) == FAILURE) { return; } len = spprintf(&strg, 0, "Congratulations! You have successfully modified ext/%.78s/config.m4. Module %.78s is now compiled into PHP.", "pchangl", arg); RETURN_STRINGL(strg, len, 0); } 上面的这个代码是ext_skel工具生成的框架,你写的扩展也要按照他的标准来写,由于代码长度的原因,我删除掉了一部分生成的注释,如果想看的话,你可以自己编写个框架看看注视,下面我就讲解下几个比较重要的部分吧。 PHP_MINIT_FUNCTION(pchangl) { return SUCCESS; } 这个方法随着php在apache或者nginx中启动而诞生内存的时候,就会执行,可以把这个方法当成初始化的方法吧。 PHP_RINIT_FUNCTION(pchangl) { return SUCCESS; } 这个方法会在每一个页面请求到来的时候执行。 PHP_RSHUTDOWN_FUNCTION(pchangl) { return SUCCESS; } RINIT_FUNCTION方法执行玩后,请求也处理的差不多了,这个时候就会进入这个方法,启动回收程序来收拾烂摊子。 PHP_MSHUTDOWN_FUNCTION(pchangl) { return SUCCESS; } 前面该启动的也启动了,该结束的也结束了,现在该apache老人家休息的时候,当apache通知php自己要stop的时候,就会进入这个方法。给扩展下最后通碟,如果有未了的心愿,就放在这个方法里,这可是最后的机会哦 PHP_FUNCTION(function name) { //实现 } 这个方法就是我们扩展方法的主体了。 pchangl.c这个文件就介绍到这里了,其他的内容就到我推荐的几个资料中查询吧,都有的。我也就不一一讲解了。 介绍完了环境的搭建,我就开始写自己的扩展,我们的bmp转bin的c代码是这样的:#include <stdio.h> #include <stdlib.h> #include <memory.h> //调试开关 #define PRINTF_DEBUG 0 //四个字节对齐 进1制处理 #define WIDTHBYTES(bits) (((bits)+31)/32*4) //每一行的像素宽度必须是4的倍数,否则补0补齐。 /*定义BYTE为无符号一个字节的类型*/ typedef unsigned char BYTE; /*定义WORD为无符号两个字节的类型*/ typedef unsigned short WORD; /*定义DWORD为无符号四个字节的类型*/ typedef unsigned long DWORD; #pragma pack(1) /*位图文件头*/ typedef struct BMP_FILE_HEADER { WORD bType; /* 文件标识符 */ DWORD bSize; /* 文件的大小 */ WORD bReserved1; /* 保留值,必须设置为0 */ WORD bReserved2; /* 保留值,必须设置为0 */ DWORD bOffset; /* 文件头的最后到图像数据位开始的偏移量 */ } BMPFILEHEADER; /*位图信息头*/ typedef struct BMP_INFO { DWORD bInfoSize; /* 信息头的大小 */ DWORD bWidth; /* 图像的宽度 */ DWORD bHeight; /* 图像的高度 */ WORD bPlanes; /* 图像的位面数 */ WORD bBitCount; /* 每个像素的位数 */ DWORD bCompression; /* 压缩类型 */ DWORD bmpImageSize; /* 图像的大小,以字节为单位 */ DWORD bXPelsPerMeter; /* 水平分辨率 */ DWORD bYPelsPerMeter; /* 垂直分辨率 */ DWORD bClrUsed; /* 使用的色彩数 */ DWORD bClrImportant; /* 重要的颜色数 */ } BMPINF; typedef struct tagRGBQUAD { BYTE rgbBlue; //蓝色的亮度(值范围为0-255) BYTE rgbGreen; //绿色的亮度(值范围为0-255) BYTE rgbRed; //红色的亮度(值范围为0-255) BYTE rgbReserved; //保留,必须为0 } RGBQUAD; #pragma pack() int main() { FILE *fpin = NULL; FILE *fpout = NULL; BMPFILEHEADER fileHeader = { 0, 0 }; BMPINF infoHeader = { 0, 0 }; RGBQUAD imgHeader[256 * 4] = { 0, 0 }; DWORD offset = 0, width = 0, height = 0, bitCount = 0; DWORD l_width = 0, bmp_width = 0, bmp_height = 0; DWORD i = 0, j = 0, m = 0; BYTE *bmpDataTmp = NULL; BYTE *bmpPixelTmp = NULL; BYTE bmp_start[8] = "ICONTOP"; BYTE bmp_end[8] = "ICONEND"; //打开文件 if((fpin = fopen("logo.bmp", "rb")) == NULL) { printf("\r\nCann't open the file!\r\n"); #if PRINTF_DEBUG while(1); #endif return 1; //错误,返回1 } //把fpin指针移动到文件开头 fseek(fpin, 0, 0); //获取文件头数据 fread(&fileHeader, sizeof(fileHeader), 1, fpin); //获取信息头数据 fread(&infoHeader, sizeof(infoHeader), 1, fpin); //计算并输出位图数据的偏移量,图像的宽度和高度,每个像素的位数 offset = fileHeader.bOffset; width = infoHeader.bWidth; height = infoHeader.bHeight; bitCount = infoHeader.bBitCount; #if PRINTF_DEBUG printf("\r\noffset=%d, width=%d, height=%d, bitCount=%d\r\n", offset, width, height, bitCount); #endif l_width = WIDTHBYTES(width * bitCount); //每一行的像素宽度必须是4的倍数,否则补0补齐。 //图象限制:单色位图,宽48字节内,尺寸1024 * 20字节内。 if((bitCount == 1) && (((l_width + bitCount - 1) / bitCount) * bitCount / bitCount <= 48) && (((l_width + bitCount - 1) / bitCount) * bitCount / bitCount * height <= 1024 * 20)) { if((bmpPixelTmp = (unsigned char *)malloc(sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height))) == NULL) { printf("\r\nData allocation failed!\r\n"); #if PRINTF_DEBUG while(1); #endif return 1; //错误,返回1 } memset(bmpPixelTmp, 0, sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height)); //获取其他数据 fread(imgHeader, sizeof(char), (offset - 54), fpin); //获取图像数据 fread(bmpPixelTmp, sizeof(char), (l_width * height), fpin); //关闭文件 fclose(fpin); if((bmpDataTmp = (unsigned char *)malloc(sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height / bitCount))) == NULL) { printf("\r\nData allocation failed!\r\n"); #if PRINTF_DEBUG while(1); #endif return 1; //错误,返回1 } memset(bmpDataTmp, 0, sizeof(char) * (((l_width + bitCount - 1) / bitCount) * bitCount * height / bitCount)); /*********************************************图像数据纠正*********************************************/ for(m = 0, i = 0; i < height; i++) { for(j = 0; j < l_width; j = j + 1) { bmpDataTmp[m] = ~bmpPixelTmp[(height - i - 1) * l_width + j + 0]; //图像纠正 m = m + 1; } } /**********************************************结 束**********************************************/ /*********************************************图像数据填充*********************************************/ for(m = 0, i = 0; i < height; i++) { #if PRINTF_DEBUG printf("\r\n"); #endif for(j = 0; j < (((l_width + bitCount - 1) / bitCount)*bitCount / bitCount); j = j + 1) { if(j == width / 8) { bmpDataTmp[m] = (bmpDataTmp[m]) & (~(0xff >> (width - width / 8 * 8))); //多余位填白 } else if(j > width / 8) { bmpDataTmp[m] = 0x00; //多余字节填白 } else { bmpDataTmp[m] = bmpDataTmp[m]; //不处理 } #if PRINTF_DEBUG printf("%d%d%d%d%d%d%d%d", (bmpDataTmp[m] & 0x80) >> 7, (bmpDataTmp[m] & 0x40) >> 6, (bmpDataTmp[m] & 0x20) >> 5, (bmpDataTmp[m] & 0x10) >> 4, (bmpDataTmp[m] & 0x08) >> 3, (bmpDataTmp[m] & 0x04) >> 2, (bmpDataTmp[m] & 0x02) >> 1, (bmpDataTmp[m] & 0x01) >> 0); #endif m = m + 1; } } /**********************************************结 束**********************************************/ /*****************************************数据写入BIN文件******************************************/ //第01~07字节:命令头"ICONTOP" //第08~09字节:图片高bmp_height //第10~11字节:图片宽bmp_width // 其他字节:图像数据 //最后7个字节:命令尾"ICONEND" /**************************************************************************************************/ //打开文件 if((fpout = fopen("logo.bin", "wb+")) == NULL) { printf("\r\nCann't open the file!\r\n"); #if PRINTF_DEBUG while(1); #endif return 1; //错误,返回1 } //把fpout指针移动到文件开头 fseek(fpout, 0, 0); bmp_height = height; bmp_width = ((l_width + bitCount - 1) / bitCount) * bitCount / bitCount; //写入命令头"ICONTOP" fwrite(&bmp_start, 7, 1, fpout); //写入图片高 fwrite(&bmp_height, 2, 1, fpout); //写入图片宽 fwrite(&bmp_width, 2, 1, fpout); //写入图像数据 fwrite(bmpDataTmp, (bmp_height * bmp_width), 1, fpout); //写入命令尾"ICONEND" fwrite(&bmp_end, 7, 1, fpout); //关闭文件 fclose(fpout); /**********************************************结 束**********************************************/ free(bmpDataTmp); bmpDataTmp = NULL; free(bmpPixelTmp); bmpPixelTmp = NULL; } else { //关闭文件 fclose(fpin); printf("\r\nIt has high resolution for this picture!\r\n"); #if PRINTF_DEBUG while(1); #endif return 1; //错误,返回1 } #if PRINTF_DEBUG while(1); #endif return 0; //正确,返回0 } (责任编辑:最模板) |