У нас есть клиенты ТСЖ и ЖК(жилищные кооперативы). И вот Сбербанк стал всех донимать просьбами печатать в квитанциях на комуналку не только обычный штрих-код, но и двумерный. Можно было выбрать один из 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. Подключил, немного подправил код из статьи и все заработало.
Понравилось, что библиотека просто генерит массив. Т.е. если надо, можно включить свои управляющие символы прямо в массив. Для генерации нужно задать саму строку(с нулем на конце) и три параметра:
//---------------------------------------------------------------------------
#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
Алгоритм получился такой
Комментарии 0