C/C++ 二维码生成库编译及使用

1.libqr

libqr源码地址:https://github.com/rsky/qrcode,该库自带输出bitmap和png格式图片二维码的功能,所以依赖的第三方库较少(只依赖libz)。

  • 编译步骤(交叉编译):

1. 下载源码:git clone https://github.com/rsky/qrcode

2. 进入编译目录:cd qrcode/libqr

3. libqr使用cmake方式编译,交叉编译具体如下:

1) 修改CMakeLists:vi CMakeLists.txt,文件开头添加如下内容(交叉编译器根据实际情况自行修改)

# 告知工具当前使用的是交叉编译方式,必须配置
SET(CMAKE_SYSTEM_NAME Generic)
# 交叉编译根目录
SET(CMAKE_FIND_ROOT_PATH "/opt/arm-linux-androideabi-4.9/sysroot")
# 指定c交叉编译器
SET(CMAKE_C_COMPILER "arm-linux-androideabi-gcc")
# 指定C++交叉编译器
SET(CMAKE_CXX_COMPILER "arm-linux-androideabi-g++")
# Search for FIND_PROGRAM() in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

2) 生成Makefile:cmake .

3) 编译:make

  • 使用方法:

#include "qr.h"
#include <fstream>

using namespace std;

int qrencode(char *string, char *path)
{
	int errcode = QR_ERR_NONE;
	QRCode* p = qrInit(8, QR_EM_8BIT, 1, -1, &errcode);
	if (p == NULL) {
		printf("error\n");
		return -1;
	}
	qrAddData(p, (const qr_byte_t*)string, strlen(string));
	if (!qrFinalize(p)) {
		printf("finalize error\n");
		return -1;
	}
	int size = 0;
	// width = height = qr_vertable[version] * mag + sep * mag * 2
	qr_byte_t * buffer = qrSymbolToPNG(p, 5, 5, &size);
	if (buffer == NULL) {
		printf("error %s", qrGetErrorInfo(p));
		return -1;
	}
	ofstream f(path);
	if (f.fail()) {
		return -1;
	}
	f.write((const char *)buffer, size);
	f.close();
	return 0;
}

该库的优点是编译和使用简单,但个人测试,待编码的字符串超过152个字符时,生成的二维码无法被正常解析,具体原因未深入分析。

2.libqrencode

libqrencode项目地址:https://fukuchi.org/works/qrencode/。该库依赖libpthread、libz、libpng和libiconv,其中libpthread和libz一般编译器都有自带,而libpng和libiconv不一定有,如有依赖需自行编译。该库目前最新版本为4.0.2,本文以该版本编译为例:

  • 编译步骤(交叉编译):

1. 下载源码:wget https://fukuchi.org/works/qrencode/qrencode-4.0.2.tar.gz

2.解压源码:tar zxvf qrencode-4.0.2.tar.gz

3.进入编译目录:cd qrencode-4.0.2/

4. libqrencode同时提供cmake和configure方式编译,但configure方式,工程提供的config.sub和config.guess太旧,没有支持我所使用的androideabi(可找新的config.sub和config.guess替换),故本文只介绍cmake编译方式,具体如下:

1)修改CMakeLists:vi CMakeLists.txt ,文件开头添加如下内容 (交叉编译器根据实际情况自行修改,默认配置编译会生成qrencode命令行工具,依赖libpng等第三库)

# 告知工具当前使用的是交叉编译方式,必须配置
SET(CMAKE_SYSTEM_NAME Generic)
# 交叉编译根目录
SET(CMAKE_FIND_ROOT_PATH "/opt/arm-linux-androideabi-4.9/sysroot")
# 指定c交叉编译器
SET(CMAKE_C_COMPILER "arm-linux-androideabi-gcc")
# 指定C++交叉编译器
SET(CMAKE_CXX_COMPILER "arm-linux-androideabi-g++")
# Search for FIND_PROGRAM() in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

2)生成Makefile:cmake .

3)编译:make

  • 使用方法(可参考源码中的qrenc.c文件):

#include "qrencode.h"
#include "png/png.h"
#include <errno.h>
#include <stdlib.h>

enum imageType {
	PNG_TYPE,
	PNG32_TYPE,
};

#define INCHES_PER_METER (100.0/2.54)
static int dpi = 72;
static int size = 5;
static int eightbit = 0; // encode entire data in 8-bit mode
static int version = 0; // specify the minimum version of the symbol.
static int margin = 5; // specify the width of the margins. (default=4 (2 for Micro QR)))
static QRecLevel level = QR_ECLEVEL_L;
static QRencodeMode hint = QR_MODE_8;
static unsigned char fg_color[4] = {0, 0, 0, 255};
static unsigned char bg_color[4] = {255, 255, 255, 255};

static void fillRow(unsigned char *row, int num, const unsigned char color[])
{
	int i;

	for(i = 0; i < num; i++) {
		memcpy(row, color, 4);
		row += 4;
	}
}

static int writePNG(const QRcode *qrcode, const char *outfile, enum imageType type)
{
	static FILE *fp; // avoid clobbering by setjmp.
	png_structp png_ptr;
	png_infop info_ptr;
	png_colorp palette = NULL;
	png_byte alpha_values[2];
	unsigned char *row, *p, *q;
	int x, y, xx, yy, bit;
	int realwidth;

	realwidth = (qrcode->width + margin * 2) * size;
	if(type == PNG_TYPE) {
		row = (unsigned char *)malloc((realwidth + 7) / 8);
	} else if(type == PNG32_TYPE) {
		row = (unsigned char *)malloc(realwidth * 4);
	} else {
		fprintf(stderr, "Internal error.\n");
		exit(EXIT_FAILURE);
	}
	if(row == NULL) {
		fprintf(stderr, "Failed to allocate memory.\n");
		exit(EXIT_FAILURE);
	}

	if(outfile[0] == '-' && outfile[1] == '\0') {
		fp = stdout;
	} else {
		fp = fopen(outfile, "wb");
		if(fp == NULL) {
			fprintf(stderr, "Failed to create file: %s\n", outfile);
			perror(NULL);
			exit(EXIT_FAILURE);
		}
	}

	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if(png_ptr == NULL) {
		fprintf(stderr, "Failed to initialize PNG writer.\n");
		exit(EXIT_FAILURE);
	}

	info_ptr = png_create_info_struct(png_ptr);
	if(info_ptr == NULL) {
		fprintf(stderr, "Failed to initialize PNG write.\n");
		exit(EXIT_FAILURE);
	}

	if(setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_write_struct(&png_ptr, &info_ptr);
		fprintf(stderr, "Failed to write PNG image.\n");
		exit(EXIT_FAILURE);
	}

	if(type == PNG_TYPE) {
		palette = (png_colorp) malloc(sizeof(png_color) * 2);
		if(palette == NULL) {
			fprintf(stderr, "Failed to allocate memory.\n");
			exit(EXIT_FAILURE);
		}
		palette[0].red   = fg_color[0];
		palette[0].green = fg_color[1];
		palette[0].blue  = fg_color[2];
		palette[1].red   = bg_color[0];
		palette[1].green = bg_color[1];
		palette[1].blue  = bg_color[2];
		alpha_values[0] = fg_color[3];
		alpha_values[1] = bg_color[3];
		png_set_PLTE(png_ptr, info_ptr, palette, 2);
		png_set_tRNS(png_ptr, info_ptr, alpha_values, 2, NULL);
	}

	png_init_io(png_ptr, fp);
	if(type == PNG_TYPE) {
		png_set_IHDR(png_ptr, info_ptr,
				realwidth, realwidth,
				1,
				PNG_COLOR_TYPE_PALETTE,
				PNG_INTERLACE_NONE,
				PNG_COMPRESSION_TYPE_DEFAULT,
				PNG_FILTER_TYPE_DEFAULT);
	} else {
		png_set_IHDR(png_ptr, info_ptr,
				realwidth, realwidth,
				8,
				PNG_COLOR_TYPE_RGB_ALPHA,
				PNG_INTERLACE_NONE,
				PNG_COMPRESSION_TYPE_DEFAULT,
				PNG_FILTER_TYPE_DEFAULT);
	}
	png_set_pHYs(png_ptr, info_ptr,
			dpi * INCHES_PER_METER,
			dpi * INCHES_PER_METER,
			PNG_RESOLUTION_METER);
	png_write_info(png_ptr, info_ptr);

	if(type == PNG_TYPE) {
	/* top margin */
		memset(row, 0xff, (realwidth + 7) / 8);
		for(y = 0; y < margin * size; y++) {
			png_write_row(png_ptr, row);
		}

		/* data */
		p = qrcode->data;
		for(y = 0; y < qrcode->width; y++) {
			memset(row, 0xff, (realwidth + 7) / 8);
			q = row;
			q += margin * size / 8;
			bit = 7 - (margin * size % 8);
			for(x = 0; x < qrcode->width; x++) {
				for(xx = 0; xx < size; xx++) {
					*q ^= (*p & 1) << bit;
					bit--;
					if(bit < 0) {
						q++;
						bit = 7;
					}
				}
				p++;
			}
			for(yy = 0; yy < size; yy++) {
				png_write_row(png_ptr, row);
			}
		}
		/* bottom margin */
		memset(row, 0xff, (realwidth + 7) / 8);
		for(y = 0; y < margin * size; y++) {
			png_write_row(png_ptr, row);
		}
	} else {
		/* top margin */
		fillRow(row, realwidth, bg_color);
		for(y = 0; y < margin * size; y++) {
			png_write_row(png_ptr, row);
		}

		/* data */
		p = qrcode->data;
		for(y = 0; y < qrcode->width; y++) {
			fillRow(row, realwidth, bg_color);
			for(x = 0; x < qrcode->width; x++) {
				for(xx = 0; xx < size; xx++) {
					if(*p & 1) {
						memcpy(&row[((margin + x) * size + xx) * 4], fg_color, 4);
					}
				}
				p++;
			}
			for(yy = 0; yy < size; yy++) {
				png_write_row(png_ptr, row);
			}
		}
		/* bottom margin */
		fillRow(row, realwidth, bg_color);
		for(y = 0; y < margin * size; y++) {
			png_write_row(png_ptr, row);
		}
	}

	png_write_end(png_ptr, info_ptr);
	png_destroy_write_struct(&png_ptr, &info_ptr);

	fclose(fp);
	free(row);
	free(palette);

	return 0;
}

static QRcode *encode(const unsigned char *intext, int length)
{
	QRcode *code;

	if(eightbit) {
		code = QRcode_encodeData(length, intext, version, level);
	} else {
		code = QRcode_encodeString((char *)intext, version, level, hint, true);
	}

	return code;
}

void qrencode(char *intext, int length, const char *outfile)
{
	QRcode *qrcode;

	qrcode = encode((const unsigned char *)intext, length);
	if(qrcode == NULL) {
		if(errno == ERANGE) {
			fprintf(stderr, "Failed to encode the input data: Input data too large\n");
		} else {
			perror("Failed to encode the input data");
		}
		exit(EXIT_FAILURE);
	}
	writePNG(qrcode, outfile, PNG_TYPE);
	QRcode_free(qrcode);
}

3. zint

zint项目地址:http://zint.github.io/,该库依赖libpng和libz第三方库,编译前需保证已安装。

  • 编译步骤(交叉编译):

1. 下载源码:git clone https://github.com/zint/zint.git

2. 进入编译目录:cd zint/;mkdir build;cd build

3. zint也是使用cmake方式编译,交叉编译具体如下:

1) 修改CMakeLists:vi CMakeLists.txt,文本开头添加如下内容

# 告知工具当前使用的是交叉编译方式,必须配置
SET(CMAKE_SYSTEM_NAME Generic)
# 交叉编译根目录
SET(CMAKE_FIND_ROOT_PATH "/opt/arm-linux-androideabi-4.9/sysroot")
# 指定c交叉编译器
SET(CMAKE_C_COMPILER "arm-linux-androideabi-gcc")
# 指定C++交叉编译器
SET(CMAKE_CXX_COMPILER "arm-linux-androideabi-g++")
# Search for FIND_PROGRAM() in the build host directories
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# For libraries and headers in the target directories
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

2)生成Makefile:cmake ..

3)编译:make (编译过程有出现multiple definition of 'concat'错误,通过注释掉backend/common.c文件中的concat和itoc函数,即可正常编译)

  • 使用方法:

可参考:https://www.jianshu.com/p/aab1e19da4cd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值