QR-код на С++ Builder 2010 (Embarcadero)

У нас есть клиенты ТСЖ и ЖК(жилищные кооперативы). И вот Сбербанк стал всех донимать просьбами печатать в квитанциях на комуналку не только обычный штрих-код, но и двумерный. Можно было выбрать один из 3-х кодов и одну их 3-х кодировок. Наш выбор пал на QR-код и UTF-8. Мотив: такой код можно прочитать смартфоном. Проблема QR-кодов - отображение русских букв. Есть 4 варианта кодирования - английские цифры, английские буквенно-цифровые символы, любые байты, иероглифы. При этом при первом варианте байты ужимаются тройками до 10 битов(из 24 исходных), при втором 16 бит ужимаются в 11. Предусмотрена возможность указать кодовую страницу для 3-го варианта(без ужатия), но так сложилось, что все забили на это и рассматривают этот третий вариант просто как кодирование символов UTF-8(на каждый символ - 2 байта). При этом число закодированных символов уменьшается раза в 3 (по сравнению с первыми двумя). Зато решается проблема с русскими(как и прочими) символами. Ориентировочно 1500 символов можно так закодировать.

Весь блок ТСЖ у нас сделан в Инфобухгалтере. Сами квитанции обсчитываются соответственно в Инфобухгалтере и отправляются в MS Word. Word выступает в качестве сервера автоматизации. В Инфобухгалтере есть интерфейс COM-объектов, но я написал свою DLL для отправки команд VB в Word. Так как-то живее все вертится.

Пару дней пришлось потратить на изучение теории и изучения как это вообще делается. Удивило что нет доступного инструментария. Все за деньги, что естественно не устраивало. Нашел несколько "бесплатных" DLL. Но оказывалось, что это пробники. Часто попадалась библиотека ZXing для многих платформ, то там все завязано на .Net Framwork, перед которым у меня жуткий страх).

Наконец нашел библиотеку на чистом С, пример применения и ссылки тут https://habrahabr.ru/post/182638/

Обрадовавшись попробовал тупо использовать исходники. Но из-за несовместимости компиляторов(а может просто надо было настроить директивы) посыпалось куча ошибок. Можно было конечно отладить, но там-же нашел готовую DLL и файл заголовков (qrencode.h) под Windows. Подключил, немного подправил код из статьи и все заработало.

Понравилось, что библиотека просто генерит массив. Т.е. если надо, можно включить свои управляющие символы прямо в массив. Для генерации нужно задать саму строку(с нулем на конце) и три параметра:

  • version - версия кода(от 1 до 40). Размер штрих-кода. Если поставить 0, то автоматически выбирается минимальная, xnj z b cltkfk
  • level - уровень коррекции ошибок(L, M, Q, H). Я выбрал M.
  • hint - метод кодировки. Я выбрал QR_MODE_8 для UTF-8


//---------------------------------------------------------------------------
#pragma hdrstop
#include "u.h"
#include <strstrea.h>
#include <iomanip.h>
#include <stdlib.h>
#include <math.h>
#include <iostream.h>
#include <fstream.h>
#include <WideStrUtils.hpp>
#include "my_func.h"
#define MB ::MessageBox

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma argsused
char*
WINAPI start(HWND handle,char* file_param)
{//***
 MY_BUFF mb_test;
 if(!FileExists(file_param)){
  mb_test.RESET(); mb_test<<"Не обнаружен файл(file_param) "<<file_param;
  ShowMessage(mb_test.BUFF);
  return "";}

 KEY_PARAM kp;
 kp.LOAD_FROM_FILE(file_param);
 mb_test.RESET(); mb_test<<kp.GET_PARAM("test");

 char* file_bmp=kp.GET_PARAM("file_bmp");
 char* file_for_QR=kp.GET_PARAM("file_for_QR");
 if(mb_test=="Yes")
 {//***
  mb_test.RESET();
  mb_test<<"file_bmp="<<file_bmp<<"  file_for_QR="<<file_for_QR;
  ShowMessage(mb_test.BUFF);
 }//***

 if(!FileExists(file_for_QR)){
  mb_test.RESET(); mb_test<<"Не обнаружен файл (file_for_QR)"<<file_for_QR;
  ShowMessage(mb_test.BUFF);
  return "";}

  MY_BUFF mb_tmp; mb_tmp<<file_bmp;
 if(!(mb_tmp%".bmp")){ShowMessage("Не назначен файл BMP"); return "";}


  char* p_=strrchr(mb_tmp.BUFF,'\\');
  if(p_!=NULL) p_[0]=0;

  bool res_=ForceDirectories(mb_tmp.BUFF);

  char* pixel_size=kp.GET_PARAM("pixel_size");
  char* encode_mode=kp.GET_PARAM("encode_mode");//1 - ,2 -, 3 - byte
  char* kode_type=kp.GET_PARAM("kode_type");//1 - WIN1221(ANSI) ,2 - UTF, 3 - KOI

int OUT_FILE_PIXEL_PRESCALER=atoi(pixel_size);
char* OUT_FILE=file_bmp;


#define PIXEL_COLOR_R			0 // Color of bmp pixels
#define PIXEL_COLOR_G			0
#define PIXEL_COLOR_B			0 //xff

typedef unsigned short	WORD;
typedef unsigned long	DWORD;
typedef signed long	LONG;

#define BI_RGB				0L

#pragma pack(push, 2)

typedef struct
{
	WORD    bfType;
	DWORD   bfSize;
	WORD    bfReserved1;
	WORD    bfReserved2;
	DWORD   bfOffBits;
} BITMAPFILEHEADER;

typedef struct
{
	DWORD      biSize;
	LONG       biWidth;
	LONG       biHeight;
	WORD       biPlanes;
	WORD       biBitCount;
	DWORD      biCompression;
	DWORD      biSizeImage;
	LONG       biXPelsPerMeter;
	LONG       biYPelsPerMeter;
	DWORD      biClrUsed;
	DWORD      biClrImportant;
} BITMAPINFOHEADER;

#pragma pack(pop)

	char* szSourceSring;
	MY_BUFF mb_bmp; mb_bmp.READ(file_for_QR);
	AnsiString as_=mb_bmp.BUFF;
	if(kode_type[0]=='1')
	{szSourceSring =mb_bmp.BUFF;}//win 1251
	else
	 {RawByteString rbs; rbs=AnsiToUtf8(as_);	szSourceSring =rbs.c_str();}

	unsigned int   unWidth, x, y, l, n, unWidthAdjusted, unDataBytes;
	unsigned char* pRGBData, *pSourceData, *pDestData;
	QRcode*	   pQRC;
	FILE*	   f;

		
	// Вычисление QRcode

	HINSTANCE h_dll=LoadLibrary(
	(LPCTSTR) "qrencode.dll" 	// address of filename of executable module
							   );
QRcode*
(*QRcode_encodeString)(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive);//, int casesensitive);

 (FARPROC)
 QRcode_encodeString=GetProcAddress(
	//(HMODULE)
	h_dll,	// handle to DLL module
	(LPCSTR)"QRcode_encodeString" // name of function
   );

void (*QRcode_free)(QRcode *qrcode);
 (FARPROC)
 QRcode_free=GetProcAddress(
	//(HMODULE)
	h_dll,	// handle to DLL module
	(LPCSTR)"QRcode_free" // name of function
   );

	if (pQRC = QRcode_encodeString(szSourceSring, 0, QR_ECLEVEL_M, QR_MODE_8,1))
	{
		unWidth = pQRC->width;
		unWidthAdjusted = unWidth * OUT_FILE_PIXEL_PRESCALER * 3;
		if (unWidthAdjusted % 4)
			unWidthAdjusted = (unWidthAdjusted / 4 + 1) * 4;
		unDataBytes = unWidthAdjusted * unWidth * OUT_FILE_PIXEL_PRESCALER;

		if (!(pRGBData = (unsigned char*)malloc(unDataBytes)))
		{
			printf("Out of memory");
			exit(1);
		}

		memset(pRGBData, 0xff, unDataBytes);

		// Подготовка заголовков bmp
		BITMAPFILEHEADER kFileHeader;
		kFileHeader.bfType = 0x4d42;  // "BM"
		kFileHeader.bfSize =	sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + unDataBytes;
		kFileHeader.bfReserved1 = 0;
		kFileHeader.bfReserved2 = 0;
		kFileHeader.bfOffBits =	sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

		BITMAPINFOHEADER kInfoHeader;
		kInfoHeader.biSize = sizeof(BITMAPINFOHEADER);
		kInfoHeader.biWidth = unWidth * OUT_FILE_PIXEL_PRESCALER;
		kInfoHeader.biHeight = -((int)unWidth * OUT_FILE_PIXEL_PRESCALER);
		kInfoHeader.biPlanes = 1;
		kInfoHeader.biBitCount = 24;
		kInfoHeader.biCompression = BI_RGB;
		kInfoHeader.biSizeImage = 0;
		kInfoHeader.biXPelsPerMeter = 0;
		kInfoHeader.biYPelsPerMeter = 0;
		kInfoHeader.biClrUsed = 0;
		kInfoHeader.biClrImportant = 0;

		// Конвертирование битов QrCode в bmp пиксели
		pSourceData = pQRC->data;
		for(y = 0; y < unWidth; y++)
		{
			pDestData = pRGBData + unWidthAdjusted * y * OUT_FILE_PIXEL_PRESCALER;
			for(x = 0; x < unWidth; x++)
			{
				if (*pSourceData & 1)
					for(l = 0; l < OUT_FILE_PIXEL_PRESCALER; l++)
						for(n = 0; n < OUT_FILE_PIXEL_PRESCALER; n++)
						{
							*(pDestData + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_B;
							*(pDestData + 1 + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_G;
							*(pDestData + 2 + n * 3 + unWidthAdjusted * l) = PIXEL_COLOR_R;
						}
				pDestData += 3 * OUT_FILE_PIXEL_PRESCALER;
				pSourceData++;
			}
		}

		if ((f=fopen(OUT_FILE, "wb"))!=NULL)
		{
			fwrite(&kFileHeader, sizeof(BITMAPFILEHEADER), 1, f);
			fwrite(&kInfoHeader, sizeof(BITMAPINFOHEADER), 1, f);
			fwrite(pRGBData, sizeof(unsigned char), unDataBytes, f);

			fclose(f);
		}
		else
		{
			printf("Unable to open file");
			exit(1);
		}
		free(pRGBData);
		QRcode_free(pQRC);
	}
	else
	{
		printf("NULL returned");
		exit(1);
	}

}//***

При этом нужно не забыть строку предварительно перевести в Юникод. Не как я). Часа три не мог понять, почему не кодирует русские буквы. Сгенерированную картинку можно прочитать смартфоном или загрузить на онлайн ридер, каких куча.

Ну вызов из Инфобухгалтера проблем не вызвал. Осталось как-то внедрить в Word. Можно было через COM сделать, но это долго и нудно. Я решил в Word написать макрос который загрузит картинку в нужную ячейку таблицы.

    Sub AddBarcodes()    '  BARCODE
' 
' Макрос записан 20.03.2016 NU1
'
  Dim num_tables_srt, pos_delim
  Dim num_t As Integer
 
  'ActiveDocument.Tables(1).Cell(1, 4).Range.Text = "44R"
   
  um_tables_srt = ActiveDocument.Tables(1).Cell(1, 5).Range.Text
  pos_delim = InStr(um_tables_srt, "R")
  um_tables_srt = Left(um_tables_srt, pos_delim - 1)
  'MsgBox (um_tables_srt)
 
  num_t = CInt(um_tables_srt)

  For I = 1 To num_t
 
  ActiveDocument.Tables(I).Cell(4, 2).Select
 
  Selection.InlineShapes.AddPicture FileName:="c:\ibs\QR\tsg\bmp\b" & I & ".bmp", LinkToFile:= _
        False, SaveWithDocument:=True
       
  Next I
 

End Sub

Алгоритм получился такой

  • Инфобухгалтер в цикле по квартирам генерит управляющие команды для Word и формирует картинки и сохраняет в определенном месте.
  • После того как Word сформирует все квитанции Инфобухгалтер записывает в определенню ячейку первой таблицы кол-во сформированных картинок(по кол-ву квитанций).
  • И в конце всего Инфобухгалтер посылает в Word команду на запуск макроса.
  • Макрос загружает в цикле картинки c QR-кодом и распихивает их по квитанциям.

Комментарии 0






Разрешённые теги: <b><i><br>Добавить новый комментарий: