当前版本在magick模块中增加了和绘制文字相关的模块函数(位于module_magick.c文件中) 在my_webroot/v0_12_0/目录中增加了一个captcha.zl的脚本,该脚本就使用了上面这些模块函数来生成图形验证码...

项目下载地址:

    zenglServer源代码的相关地址:https://github.com/zenglong/zenglServer  当前版本对应的tag标签为:v0.12.0

zenglServer v0.12.0:

    当前版本在magick模块中增加了和绘制文字相关的模块函数(位于module_magick.c文件中):

/**
 * magickNewDrawingWand模块函数,新建一个DrawingWand实例,并将该实例的指针返回
 * 在执行具体的图像矢量操作之前,需要先新建一个DrawingWand实例,因为,大部分图像矢量操作接口都需要接受一个DrawingWand实例指针作为参数
 * 该模块函数在创建了一个DrawingWand实例指针后,还会将该指针存储到资源列表中,最后将创建好的实例指针以整数的形式作为结果返回
 */
ZL_EXP_VOID module_magick_new_drawing_wand(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	st_magick_wand_genesis();
	DrawingWand * d_wand = NewDrawingWand();
	write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] NewDrawingWand: %x\n", d_wand); // debug
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)d_wand, 0);
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	int ret_code = resource_list_set_member(&(my_data->resource_list), d_wand, st_magick_destroy_drawing_wand_callback);
	if(ret_code != 0) {
		zenglApi_Exit(VM_ARG, "magickNewDrawingWand add resource to resource_list failed, resource_list_set_member error code:%d", ret_code);
	}
}

/**
 * magickNewPixelWand模块函数,新建一个PixelWand实例,并将该实例的指针返回
 * 在执行具体的像素操作之前,需要先新建一个PixelWand实例,因为,大部分像素操作接口都需要接受一个PixelWand实例指针作为参数
 * 该模块函数在创建了一个PixelWand实例指针后,还会将该指针存储到资源列表中,最后将创建好的实例指针以整数的形式作为结果返回
 */
ZL_EXP_VOID module_magick_new_pixel_wand(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	st_magick_wand_genesis();
	PixelWand * p_wand = NewPixelWand();
	write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] NewPixelWand: %x\n", p_wand); // debug
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)p_wand, 0);
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	int ret_code = resource_list_set_member(&(my_data->resource_list), p_wand, st_magick_destroy_pixel_wand_callback);
	if(ret_code != 0) {
		zenglApi_Exit(VM_ARG, "magickNewPixelWand add resource to resource_list failed, resource_list_set_member error code:%d", ret_code);
	}
}

/**
 * magickPixelSetColor模块,为PixelWand实例设置颜色,PixelWand实例在设置了颜色后,就可以作为其他接口的参数,用作绘图的色彩
 * 该模块函数的第一个参数p_wand必须是一个有效的PixelWand实例指针,第二个参数color是需要设置的颜色的字符串,例如:blue", "#0000ff"等
 * 例如:
 * use magick;
 * wand = magickNewWand();
 * p_wand = magickNewPixelWand();
 * magickPixelSetColor(p_wand, "white");
 * magickNewImage(wand, 85, 30, p_wand);
 * 上面脚本创建了一个宽85像素,高30像素,白色背景的图像
 * 该模块函数最终会通过PixelSetColor的API接口去执行具体的像素操作
 * 该接口的官方文档:https://www.imagemagick.org/api/pixel-wand.php#PixelSetColor
 */
ZL_EXP_VOID module_magick_pixel_set_color(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	............................................................
	retval = PixelSetColor(p_wand, (const char *)color);
	if(retval == MagickFalse) {
		ExceptionType severity;
		char * description = PixelGetException(p_wand, &severity);
		write_to_server_log_pipe(WRITE_TO_PIPE, "PixelSetColor failed: %s\n", description);
		description=(char *) MagickRelinquishMemory(description);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0);
	}
	else
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0);
}

/**
 * magickNewImage模块函数,在MagickWand实例中使用指定的尺寸和背景色,创建一个空白的图像画布
 * 该模块函数的第一个参数magick_wand必须是一个有效的MagickWand实例指针,第二个参数width表示创建画布的宽,
 * 第三个参数height表示画布的高,最后一个参数background必须是一个有效的PixelWand实例指针,表示需要创建的画布的背景色
 * 示例代码,参考magickPixelSetColor模块函数的示例代码
 * 该模块函数最终会通过MagickNewImage这个API接口去执行具体的操作
 * 该接口的官方文档:https://www.imagemagick.org/api/magick-image.php#MagickNewImage
 */
ZL_EXP_VOID module_magick_new_image(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	...........................................................
	retval = MagickNewImage(magick_wand,width,height,p_wand);
	if(retval == MagickFalse) {
		ExceptionType severity;
		char * description=MagickGetException(magick_wand, &severity);
		write_to_server_log_pipe(WRITE_TO_PIPE, "MagickNewImage failed: %s\n", description);
		description=(char *) MagickRelinquishMemory(description);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0);
	}
	else
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0);
}

/**
 * magickSetImageFormat模块函数,设置MagickWand中图像的格式
 * 该模块函数的第一个参数magick_wand必须是一个有效的MagickWand实例指针,第二个参数format表示需要设置的图像格式,例如:png,jpg等
 * 例如:
 * use builtin, magick, request;
 * wand = magickNewWand();
 * p_wand = magickNewPixelWand();
 * magickPixelSetColor(p_wand, "white");
 * magickNewImage(wand, 85, 30, p_wand);
 * magickSetImageFormat(wand, "jpg");
 * output = magickGetImageBlob(wand, &length); // 获取图像的二进制数据
 * rqtSetResponseHeader("Content-Type: image/" + magickGetImageFormat(wand));
 * bltOutputBlob(output, length); // 输出二进制数据
 * 上面代码中,创建了一个白色背景的图像,并将该图像设置为了jpg格式,
 * 最后,获取该图像的jpg格式的二进制数据,并将这些二进制数据输出给浏览器,从而可以在浏览器中看到jpg格式的图片
 * 该模块函数最终会通过MagickSetImageFormat这个接口去执行具体的操作
 * 该接口的官方文档:https://www.imagemagick.org/api/magick-image.php#MagickSetImageFormat
 */
ZL_EXP_VOID module_magick_set_image_format(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	...........................................................
	retval = MagickSetImageFormat(magick_wand, (const char *)format);
	if(retval == MagickFalse) {
		ExceptionType severity;
		char * description=MagickGetException(magick_wand, &severity);
		write_to_server_log_pipe(WRITE_TO_PIPE, "MagickSetImageFormat failed: %s\n", description);
		description=(char *) MagickRelinquishMemory(description);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0);
	}
	else
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0);
}

/**
 * magickGetImageBlob模块函数,获取图像在指定格式下(jpg, png等格式)的二进制数据
 * 该模块函数的第一个参数magick_wand必须是一个有效的MagickWand实例指针,第二个参数length表示返回的二进制数据的字节大小,必须是引用类型
 * 示例代码,参考magickSetImageFormat模块函数
 * 该模块函数最终会通过MagickGetImageBlob这个接口去执行底层的操作
 * 该接口的官方文档:https://www.imagemagick.org/api/magick-image.php#MagickGetImageBlob
 */
ZL_EXP_VOID module_magick_get_image_blob(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	...........................................................
	unsigned char * output = MagickGetImageBlob(magick_wand,&length);
	arg.type = ZL_EXP_FAT_INT;
	arg.val.integer = (ZL_EXP_LONG)length;
	zenglApi_SetFunArg(VM_ARG,2,&arg);
	if(output == NULL) {
		ExceptionType severity;
		char * description=MagickGetException(magick_wand, &severity);
		write_to_server_log_pipe(WRITE_TO_PIPE, "MagickGetImageBlob failed: %s\n", description);
		description=(char *) MagickRelinquishMemory(description);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
	}
	else {
		write_to_server_log_pipe(WRITE_TO_PIPE, "[debug] MagickGetImageBlob: %x\n", output); // debug
		int ret_code = resource_list_set_member(&(my_data->resource_list), output, st_magick_destroy_image_blob_callback);
		if(ret_code != 0) {
			zenglApi_Exit(VM_ARG, "magickGetImageBlob add resource to resource_list failed, resource_list_set_member error code:%d", ret_code);
		}
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, (ZL_EXP_LONG)output, 0);
	}
}

/**
 * magickDrawSetFont模块函数,设置绘制文字所使用的字体
 * 该模块函数的第一个参数d_wand必须是有效的DrawingWand实例指针,
 * 第二个参数font_name表示需要设置的字体,可以是字体名,例如:"Helvetica Regular",也可以是某个字体文件的相对路径(相对于当前执行脚本的路径),例如:xerox_serif_narrow.ttf
 * 示例:
 * use magick;
 * def TRUE 1;
 * def FALSE 0;
 * wand = magickNewWand();
 * p_wand = magickNewPixelWand();
 * magickPixelSetColor(p_wand, "white");
 * magickNewImage(wand, 85, 30, p_wand); // 创建一个白色背景的画布
 * d_wand = magickNewDrawingWand();      // 新建一个DrawingWand实例
 * magickDrawSetFont(d_wand, "xerox_serif_narrow.ttf"); // 设置字体
 * magickDrawSetFontSize(d_wand, 24);    // 设置字体大小
 * magickDrawSetTextAntialias(d_wand, TRUE); // 开启抗锯齿(默认情况下就是开启)
 * magickDrawAnnotation(d_wand, 4, 20, "Hello"); // 使用xerox_serif_narrow.ttf对应的字体绘制Hello
 * magickDrawImage(wand, d_wand);        // 将文字信息渲染到画布上
 *
 * 该模块函数最终会通过DrawSetFont这个接口去执行具体的操作
 * 该接口的官方文档:https://www.imagemagick.org/api/drawing-wand.php#DrawSetFont
 */
ZL_EXP_VOID module_magick_draw_set_font(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	...........................................................
	MagickBooleanType retval;
	retval = DrawSetFont (d_wand, (const char *)font_name);
	if(retval == MagickFalse) {
		ExceptionType severity;
		char * description = DrawGetException(d_wand, &severity);
		write_to_server_log_pipe(WRITE_TO_PIPE, "DrawSetFont failed: %s\n", description);
		description=(char *) MagickRelinquishMemory(description);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0);
	}
	else
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0);
}

/**
 * magickDrawSetFontSize模块函数,设置绘制文字时所使用的字体大小
 * 该模块函数的第一个参数d_wand必须是有效的DrawingWand实例指针,第二个参数pointsize表示需要设置的字体大小
 * 示例代码参考magickDrawSetFont模块函数
 * 该模块函数最终会通过DrawSetFontSize接口去执行具体的操作
 * 该接口的官方文档:https://www.imagemagick.org/api/drawing-wand.php#DrawSetFontSize
 */
ZL_EXP_VOID module_magick_draw_set_font_size(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	............................................................
	DrawSetFontSize(d_wand, (const double)pointsize);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
}

/**
 * magickDrawSetTextAntialias模块函数,控制绘制的文本是否是抗锯齿的,默认情况下(没有使用该模块函数的情况下),文本是抗锯齿的。
 * 该模块函数的第一个参数d_wand必须是有效的DrawingWand实例指针,第二个参数text_antialias是整数类型用于判断是否开启抗锯齿,如果text_antialias不等于0就开启抗锯齿,
 * 如果text_antialias等于0,则关闭抗锯齿。
 * 示例代码参考magickDrawSetFont模块函数
 *
 * magickDrawSetTextAntialias模块函数最终会通过DrawSetTextAntialias接口去执行具体的操作
 * 该接口的官方文档:https://www.imagemagick.org/api/drawing-wand.php#DrawSetTextAntialias
 */
ZL_EXP_VOID module_magick_draw_set_text_antialias(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	............................................................
	int text_antialias = arg.val.integer;
	if(text_antialias != 0) {
		DrawSetTextAntialias(d_wand,MagickTrue);
	}
	else
		DrawSetTextAntialias(d_wand,MagickFalse);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
}

/**
 * magickDrawAnnotation模块函数,绘制文本信息
 * 该模块函数的第一个参数d_wand必须是有效的DrawingWand实例指针,第二个参数x表示要绘制文本的横坐标,第三个参数y表示要绘制的纵坐标,最后一个参数text表示要绘制的文本
 * 例如:magickDrawAnnotation(d_wand, 4, 20, "Hello"); 表示在横坐标为4像素,纵坐标为20像素的位置处绘制文本Hello
 * 该模块函数最终会通过DrawAnnotation这个API接口去执行具体的操作,该接口的官方文档:https://www.imagemagick.org/api/drawing-wand.php#DrawAnnotation
 */
ZL_EXP_VOID module_magick_draw_annotation(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	............................................................
	char * text = arg.val.str;
	DrawAnnotation(d_wand, (const double)x, (const double)y, (const unsigned char *)text);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
}

/**
 * magickDrawImage模块函数,将DrawingWand实例中包含的矢量图像信息(例如文本信息等)渲染到MagickWand实例所对应的画布上
 * 该模块函数的第一个参数magick_wand必须是有效的MagickWand实例指针,第二个参数d_wand必须是有效的DrawingWand实例指针
 * 例如:
 * magickDrawAnnotation(d_wand, 4, 20, "Hello"); // 在d_wand中绘制矢量文本
 * magickDrawImage(wand, d_wand);                // 将d_wand包含的矢量文本渲染到wand对应的画布上
 *
 * 该模块函数最终会通过MagickDrawImage这个底层API接口去执行具体的操作,该接口的官方文档:https://www.imagemagick.org/api/magick-image.php#MagickDrawImage
 */
ZL_EXP_VOID module_magick_draw_image(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	............................................................
	MagickBooleanType retval;
	retval = MagickDrawImage(magick_wand,d_wand);
	if(retval == MagickFalse) {
		ExceptionType severity;
		char * description=MagickGetException(magick_wand, &severity);
		write_to_server_log_pipe(WRITE_TO_PIPE, "MagickDrawImage failed: %s\n", description);
		description=(char *) MagickRelinquishMemory(description);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0);
	}
	else
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0);
}

/**
 * magickSwirlImage模块函数,围绕图像中心旋转像素
 * 该模块函数的第一个参数magick_wand必须是有效的MagickWand实例指针,第二个参数degrees表示旋转的度数,度数越大,旋转效果越明显。
 * 例如:
 * magickSwirlImage(wand, 20); // 将wand对应的画布,围绕中心旋转20度
 *
 * 该模块函数最终会通过底层的API接口MagickSwirlImage去执行具体的操作
 * 该接口的官方文档:https://www.imagemagick.org/api/magick-image.php#MagickSwirlImage
 * 官方文档中的MagickSwirlImage函数原型是ImageMagick 7.x中的版本,在ImageMagick 6.x中,是没有最后一个method参数的
 */
ZL_EXP_VOID module_magick_swirl_image(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	............................................................
	MagickBooleanType retval;
	retval = MagickSwirlImage(magick_wand, (const double)degrees);
	if(retval == MagickFalse) {
		ExceptionType severity;
		char * description=MagickGetException(magick_wand, &severity);
		write_to_server_log_pipe(WRITE_TO_PIPE, "MagickSwirlImage failed: %s\n", description);
		description=(char *) MagickRelinquishMemory(description);
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0);
	}
	else
		zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0);
}

/**
 * magickClearDrawingWand模块函数,清理与DrawingWand实例相关的资源
 * 该模块函数的第一个参数d_wand必须是有效的DrawingWand实例指针
 * 例如:
 * magickDrawAnnotation(d_wand, 4, 20, "Hello"); // 在d_wand中绘制文本Hello
 * magickDrawImage(wand, d_wand);                // 将文本渲染到wand画布上
 * magickSwirlImage(wand, 20);                   // 将画布中心旋转20度像素,让文本产生扭曲效果
 * magickClearDrawingWand(d_wand);               // 清理d_wand中包含的文本信息
 * magickDrawLine(d_wand, 10, 10, 65, 25);       // 在d_wand中绘制线条
 * magickDrawImage(wand, d_wand);                // 将d_wand中包含的线条渲染到画布上
 * 上面脚本中先使用magickClearDrawingWand模块函数清理掉d_wand中包含的资源(包括之前绘制的文本),这样,绘制线条时,就不会残留之前绘制的文本信息了
 * 该模块函数最终会通过底层的API接口ClearDrawingWand去执行具体的操作,接口官方文档:https://www.imagemagick.org/api/drawing-wand.php#ClearDrawingWand
 */
ZL_EXP_VOID module_magick_clear_drawing_wand(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 1)
		zenglApi_Exit(VM_ARG,"usage: magickClearDrawingWand(d_wand): integer");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [d_wand] of magickClearDrawingWand must be integer");
	}
	DrawingWand * d_wand = (DrawingWand *)arg.val.integer;
	st_assert_drawing_wand(VM_ARG, d_wand, "magickClearDrawingWand");
	ClearDrawingWand(d_wand);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
}

/**
 * magickDrawLine模块函数,根据指定的起始和结束位置,绘制一条直线
 * 该模块函数的第一个参数d_wand必须是有效的DrawingWand实例指针,第二个参数sx表示起始位置的横坐标,第三个参数sy表示起始位置的纵坐标,
 * 第四个参数ex表示结束位置的横坐标,最后一个参数ey表示结束位置的纵坐标
 * 例如:magickDrawLine(d_wand, 10, 10, 65, 25); 表示在(10,10)到(65,25)之间绘制一条直线
 * 该模块函数最终会通过底层API接口DrawLine去执行具体的操作,接口官方文档:https://www.imagemagick.org/api/drawing-wand.php#DrawLine
 */
ZL_EXP_VOID module_magick_draw_line(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	............................................................
	DrawLine(d_wand, sx, sy, ex, ey);
	zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
}

/**
 * magick模块的初始化函数,里面设置了与该模块相关的各个模块函数及其相关的处理句柄
 */
ZL_EXP_VOID module_magick_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT moduleID)
{
	............................................................
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickNewDrawingWand",module_magick_new_drawing_wand);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickNewPixelWand",module_magick_new_pixel_wand);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickPixelSetColor",module_magick_pixel_set_color);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickNewImage",module_magick_new_image);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickSetImageFormat",module_magick_set_image_format);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickGetImageBlob",module_magick_get_image_blob);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickDrawSetFont",module_magick_draw_set_font);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickDrawSetFontSize",module_magick_draw_set_font_size);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickDrawSetTextAntialias",module_magick_draw_set_text_antialias);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickDrawAnnotation",module_magick_draw_annotation);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickDrawImage",module_magick_draw_image);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickSwirlImage",module_magick_swirl_image);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickClearDrawingWand",module_magick_clear_drawing_wand);
	zenglApi_SetModFunHandle(VM_ARG,moduleID,"magickDrawLine",module_magick_draw_line);
	............................................................
}


    在my_webroot/v0_12_0/目录中增加了一个captcha.zl的脚本,该脚本就使用了上面这些模块函数来生成图形验证码:

use builtin, magick, request, session;
def TRUE 1;
def FALSE 0;

wand = magickNewWand();
p_wand = magickNewPixelWand();

magickPixelSetColor(p_wand, "white");
magickNewImage(wand, 85, 30, p_wand);
d_wand = magickNewDrawingWand();
// magickDrawSetFont(d_wand, "Helvetica Regular");
magickDrawSetFont(d_wand, "xerox_serif_narrow.ttf");
magickDrawSetFontSize(d_wand, 24);
magickDrawSetTextAntialias(d_wand, TRUE);
// magickDrawSetTextAntialias(d_wand, FALSE);
captcha = bltRandomStr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 6);
magickDrawAnnotation(d_wand, 4, 20, captcha);
magickDrawImage(wand, d_wand);
magickSwirlImage(wand, 20);

magickClearDrawingWand(d_wand);
magickDrawLine(d_wand, bltRand( 0, 70 ), bltRand( 0, 30 ), bltRand( 0, 70 ), bltRand( 0, 30 ));
magickDrawLine(d_wand, bltRand( 0, 70 ), bltRand( 0, 30 ), bltRand( 0, 70 ), bltRand( 0, 30 ));
magickDrawLine(d_wand, bltRand( 0, 70 ), bltRand( 0, 30 ), bltRand( 0, 70 ), bltRand( 0, 30 ));
magickDrawLine(d_wand, bltRand( 0, 70 ), bltRand( 0, 30 ), bltRand( 0, 70 ), bltRand( 0, 30 ));
magickDrawLine(d_wand, bltRand( 0, 70 ), bltRand( 0, 30 ), bltRand( 0, 70 ), bltRand( 0, 30 ));

magickDrawImage(wand, d_wand);

magickSetImageFormat(wand, "jpg");
output = magickGetImageBlob(wand, &length);
rqtSetResponseHeader("Content-Type: image/" + magickGetImageFormat(wand));
cookies = rqtGetCookie();
sess_id = cookies['SESSION'];
if(!sess_id)
	sess_id = sessMakeId();
	rqtSetResponseHeader("Set-Cookie: SESSION="+sess_id+"; path=/");
else
	data = sessGetData(sess_id);
endif
data['captcha'] = captcha;
sessSetData(sess_id, data);
bltOutputBlob(output, length);


    在my_webroot/v0_12_0/目录中,还有一个test.zl脚本,该脚本用于测试生成图形验证码和校验输入的验证码:

use builtin, request, session;

def TRUE 1;
def FALSE 0;

print '<!Doctype html>
<html>
<head><meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>图形验证码</title>
</head>
<body>';

posts = rqtGetBodyAsArray();
if(posts['submit'])
	bltStr(&posts['captcha'], TRUE);
	cookies = rqtGetCookie();
	sess_id = cookies['SESSION'];
	data = sessGetData(sess_id);
	bltStr(&data['captcha'], TRUE);
	if(data['captcha'] && data['captcha'] == posts['captcha'])
		print '校验通过!';
	else
		print '校验失败,输入的图形验证码无效!';
	endif
	sessDelete(sess_id);
	print '<br/><p><a href="test.zl">再试一次</a></p>';
else
	random = bltRandomStr('0123456789', 10);
	print '<form action="test.zl" method="post" enctype="multipart/form-data">
		<p>验证码:<input name="captcha" value="" type="text"><img src="captcha.zl?' + random + '" /></p>
		<input type="hidden" name="submit" value="yes">
		<input value="校验" type="submit" style="width:180px;height:25px"></form>';
endif

print '</body></html>';


    上面脚本会先通过captcha.zl生成图形验证码,然后将用户输入的验证码和图形验证码进行校验,执行结果如下:



图1:test.zl测试图形验证码

    输入验证码,点击校验按钮,如果输入正确,会显示“校验通过!”。否则会显示“校验失败,输入的图形验证码无效!”。

    当前版本在builtin内建模块中也新增了一些模块函数(位于module_builtin.c文件中):

/**
 * bltOutputBlob模块函数,直接将二进制数据输出到客户端
 * 该模块函数的第一个参数blob为字节指针,指向需要输出的二进制数据。第二个参数length表示二进制数据的字节大小
 * 例如:
 * output = magickGetImageBlob(wand, &length); // 获取图像的二进制数据
 * rqtSetResponseHeader("Content-Type: image/" + magickGetImageFormat(wand));
 * bltOutputBlob(output, length); // 输出二进制数据
 * 上面代码片段中,先通过magickGetImageBlob获取图像的二进制数据和二进制数据的长度(以字节为单位的大小),
 * 接着就可以通过bltOutputBlob模块函数将图像的二进制数据输出到客户端
 */
ZL_EXP_VOID module_builtin_output_blob(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 2)
		zenglApi_Exit(VM_ARG,"usage: bltOutputBlob(blob, length)");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [blob] of bltOutputBlob must be integer");
	}
	char * blob = (char *)arg.val.integer;
	zenglApi_GetFunArg(VM_ARG,2,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the second argument [length] of bltOutputBlob must be integer");
	}
	int length = arg.val.integer;
	MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
	dynamic_string_append(&my_data->response_body, blob, length, RESPONSE_BODY_STR_SIZE);
	zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, length, 0);
}

/**
 * bltRandomStr模块函数,根据指定的字符序列和长度,生成随机的字符串
 * 第一个参数str表示用于生成随机字符串的字符序列,第二个参数length表示需要生成的随机字符串的长度
 * 例如:
 * captcha = bltRandomStr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 6);
 * 上面代码执行后可以得到一个包含字母和数字的长度为6的随机字符串
 */
ZL_EXP_VOID module_builtin_random_str(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	.........................................................
	char * dest = (char *)zenglApi_AllocMem(VM_ARG, (length + 1));
	builtin_init_rand_seed();
	int next_seed = 0;
	for(int i = 0; i < length;i++) {
		next_seed = rand();
		int index = (double) next_seed / RAND_MAX * (str_len - 1);
		dest[i] = charset[index];
	}
	srand((unsigned int)next_seed);
	dest[length] = '\0';
	zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_STR, dest, 0, 0);
	zenglApi_FreeMem(VM_ARG, dest);
}

/**
 * bltRand模块函数,根据指定的最小值和最大值,得到这两个值之间的随机整数
 * 该模块函数的第一个参数min表示可能生成的随机数的最小值,第二个参数max表示可能生成的最大值
 * 例如:bltRand( 0, 30 ) 将返回0到30之间的随机数
 */
ZL_EXP_VOID module_builtin_rand(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 2)
		zenglApi_Exit(VM_ARG,"usage: bltRand(min, max): integer");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the first argument [min] of bltRand must be integer");
	}
	int min = arg.val.integer;
	zenglApi_GetFunArg(VM_ARG,2,&arg);
	if(arg.type != ZL_EXP_FAT_INT) {
		zenglApi_Exit(VM_ARG,"the second argument [max] of bltRand must be integer");
	}
	int max = arg.val.integer;
	builtin_init_rand_seed();
	int next_seed = rand();
	int retval = ((double) next_seed / RAND_MAX * (max - min)) + min;
	srand((unsigned int)next_seed);
	zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, retval, 0);
}

/**
 * bltUtfStrLen模块函数,计算utf8字符串的长度,主要用于计算有多少个utf8编码的汉字
 * 第一个参数str是需要计算长度的字符串
 * 例如:
 * len = bltUtfStrLen('世界s你好!abcd');
 * 上面代码返回的结果会是10,其中有5个汉字和5个英文字母
 * 具体的计算方法来源于下面这个链接
 * https://stackoverflow.com/questions/32936646/getting-the-string-length-on-utf-8-in-c
 */
ZL_EXP_VOID module_builtin_utf_str_len(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 1)
		zenglApi_Exit(VM_ARG,"usage: bltUtfStrLen(str): integer");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the first argument [str] of bltUtfStrLen must be string");
	}
	char * s = arg.val.str;
	int count = 0;
	while (*s) {
		count += (*s++ & 0xC0) != 0x80;
	}
	zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, count, 0);
}

/**
 * bltStrLen模块函数,以字节为单位计算字符串的长度,第一个参数str是要计算长度的字符串
 * 例如:len = bltStrLen('世界s你好!abcd'); 返回的结果会是20,由于一个utf8编码的汉字包含3个字节,5个汉字就是15个字节,再加上5个英文字母,返回的长度就是20
 * 该模块函数是直接调用底层的strlen的C库函数来计算长度的
 */
ZL_EXP_VOID module_builtin_str_len(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 1)
		zenglApi_Exit(VM_ARG,"usage: bltStrLen(str): integer");
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type != ZL_EXP_FAT_STR) {
		zenglApi_Exit(VM_ARG,"the first argument [str] of bltStrLen must be string");
	}
	char * s = arg.val.str;
	int count = strlen(s);
	zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, count, 0);
}

/**
 * bltStrReplace模块函数,可以用于执行字符串的替换操作
 * 第一个参数str表示源字符串,第二个参数search是要搜索的子字符串,第三个参数replace表示要进行替换的字符串,最后一个可选参数isSetData表示是否将替换后的结果设置到第一个参数
 * 默认情况下isSetData是0,也就是只返回替换的结果,不会设置第一个参数,如果isSetData是不为0的整数值,则会将替换的结果设置到第一个参数(第一个参数需要是引用类型,才能设置成功)
 * 例如:
 * use builtin;
 * def TRUE 1;
 * str = '世界s你好!abcd';
 * print bltStrReplace(&str, 'abcd', 'hello world!', TRUE) + '<br/>';
 * print str + '<br/>';
 * 上面脚本执行的结果会是:
 * 世界s你好!hello world!
 * 世界s你好!hello world!
 * 上面将str中的abcd替换为了hello world!,同时由于bltStrReplace模块函数的最后一个参数设置为了TRUE(也就是1),
 * 因此,源字符串str也被设置为了替换后的字符串:世界s你好!hello world!
 */
ZL_EXP_VOID module_builtin_str_replace(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 3)
		zenglApi_Exit(VM_ARG,"usage: bltStrReplace(str|&str, search, replace[, isSetData=0]): string");
	......................................................
}

/**
 * bltIsNone模块函数,检测某个变量是否是NONE类型(未初始化时的类型),需要将变量的引用作为第一个参数传递进来
 * 如果变量被初始化过了,则返回0,如果没有被初始化过,也就是没有被设置过具体的类型(例如整数,浮点数,字符串等),则返回1
 * 例如:
 * str = '世界s你好!abcd';
 * print 'bltIsNone(&str): ' + (bltIsNone(&str) ? 'TRUE' : 'FALSE')  + '<br/>';
 * print 'bltIsNone(&test): ' + (bltIsNone(&test) ? 'TRUE' : 'FALSE');
 * 上面脚本执行的结果会是:
 * bltIsNone(&str): FALSE
 * bltIsNone(&test): TRUE
 * 由于str被初始化为了字符串,因此,str变量不是NONE类型,test变量没有被初始化过,所以是NONE类型
 */
ZL_EXP_VOID module_builtin_is_none(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
	ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
	if(argcount < 1)
		zenglApi_Exit(VM_ARG,"usage: bltIsNone(&data): integer");
	zenglApi_GetFunArgInfo(VM_ARG,1,&arg);
	switch(arg.type){
	case ZL_EXP_FAT_ADDR:
	case ZL_EXP_FAT_ADDR_LOC:
	case ZL_EXP_FAT_ADDR_MEMBLK:
		break;
	default:
		zenglApi_Exit(VM_ARG,"the first argument [data] of bltIsNone must be address type");
		break;
	}
	zenglApi_GetFunArg(VM_ARG,1,&arg);
	if(arg.type == ZL_EXP_FAT_NONE)
		zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_TRUE, 0);
	else
		zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, ZL_EXP_FALSE, 0);
}


    上面这些内建模块函数中,bltOutputBlob,bltRandomStr以及bltRand模块函数在上面生成图形验证码的captcha.zl脚本中都用到了。其余的bltUtfStrLen,bltStrLen等模块函数可以在my_webroot/v0_12_0/test2.zl脚本中看到相关测试代码:

use builtin;

def TRUE 1;
def FALSE 0;

print '<!Doctype html>
<html>
<head><meta http-equiv="content-type" content="text/html;charset=utf-8" />
<title>utf8字符串长度检测</title>
</head>
<body>';

str = '世界s你好!abcd';
print '"' + str + '" str len:' + bltStrLen(str) + "<br/>";
print '"' + str + '" utf str len:' + bltUtfStrLen(str) + '<br/>';
print bltStrReplace(&str, 'abcd', 'hello world!', TRUE) + '<br/>';
print str + '<br/>';
print bltStrReplace(&str, str, '', TRUE) + 'end' + '<br/>';
print str + 'end<br/>';
print 'bltIsNone(&str): ' + (bltIsNone(&str) ? 'TRUE' : 'FALSE')  + '<br/>';
print 'bltIsNone(&test): ' + (bltIsNone(&test) ? 'TRUE' : 'FALSE');
print '</body></html>';


    执行结果如下:



图2:test2.zl测试脚本

    以上就是当前版本相关的内容。OK,就到这里,休息,休息一下 o(∩_∩)o~~

结束语:

    有时,最好的选择就是重新开始!

——  《美国队长2》
 
上下篇

下一篇: zenglServer v0.13.0 目录入口文件以及模板路径调整

上一篇: zenglServer v0.11.0 共享内存,crustache转义,magick模块,新增bltDate等内建模块函数

相关文章

zenglServer v0.21.0 增加base64编解码相关的内建模块函数

zenglServer v0.4.0 daemon守护进程, epoll事件驱动

zenglServer v0.3.0 mysql模块

zenglServer v0.18.0 直接在命令行中执行脚本

zenglServer v0.8.0-v0.8.1

zenglServer v0.5.0 设置响应头, 获取cookie