RSS

Генератор QR-кодов на Delphi. Способ 1

Все Вы наверняка хоть раз сталкивались с QR-кодами. Данный способ графического кодирования информации получил широкое распространение относительно недавно. Отличие от штрих-кода в том, что с помощью QR-кода можно кодировать относительно большие объемы информации. Информация может содержать как текст, так и числа.

Например, в этом QR коде закодирован адрес страницы с данной статьей.

Как правило, в QR-кодах кодируют адреса сайтов или адрес страницы с некоторой доп. информацией (цены, страницы подписки), могут быть закодированы номера телефонов и любая другая информация.

Раскодирование QR-кода осуществляется с помощью мобильного телефона и установленного на него приложения. Таких приложений великое множество как для Android или iOS, так и для других операционных систем. Так же, на просторах сети, можно найти великое множество сервисов по дешифрованию QR-кодов со снимка или изображения.

В этой статье я опишу один из способов генерации QR-кода и приведу пример приложения на Delphi, которое позволит Вам создать QR-код и сохранить его в виде изображения.

В первом способе мы воспользуемся сервисом Google (Google API). Да, в статье не будет рассматриваться алгоритм кодирования и генерации QR т.к. это довольно объемный и сложный алгоритм. Если кому интересно, то здесь описаны основы чтения QR-кода.

 И так, приступаем к созданию приложения.

Создайте новый проект с формой. Добавьте на форму следующие компоненты:
— Button 2шт. (вкладка Standart);
— ComboBox 2шт. (вкладка Standart);
— Memo 1шт. (вкладка Standart);
— Label 2шт. (вкладка Standart);
— Image 1 шт. (вкладка Additional);
— SavePictureDialog 1шт. (вкладка Dialogs).

Перейдите в окно Object Inspector (F11) и присвойте компонентам следующие имена:
— ComboBox1 свойство Name =  ErrLevel – будет выбор уровня коррекции ошибок;
— ComboBox2 свойство Name =  QR_img_size – будет выбор размера изображения кода;
— Memo1 свойство Name = QR_text – будет содержать кодируемый текст;
— Button1 свойство Name = GetQR_But – кнопка, с помощью которой будем посылать запрос;
— Button2 свойство Name = Exp_QR_but – кнопка сохранения полученного кода в файл png.
Остальные имена оставляем по умолчанию.

Компоненту Image1 задайте свойства Height и Width равными 500, свойство Center переключите в True для отображения результата (картинки) по центру объекта Image1.

Заполним ComboBox’ы значениями. Для этого выделите первый ComboBox с именем ErrLevel, найдите свойство Items и щелкните рядом по кнопке с надписью «». У Вас откроется окно String List Editor. Внесите туда следующие строки:

L — Восстанавливает до 7% потерянных данных
M — Восстанавливает до 15% потерянных данных
Q — Восстанавливает до 25% потерянных данных
H — Восстанавливает до 30% потерянных данных

Жмем ОК. Так же, установите свойство ItemIndex = 0. Этим свойством мы установили значение по умолчанию. В нашем случае это первая строка «L — Восстанавливает до 7% потерянных данных».

Для ComboBox с именем QR_img_size, аналогично создайте список со следующими значениями:
100×100
150×150
200×200
250×250
300×300
350×350
400×400
450×450
500×500

Свойство ItemIndex у данного компонента установите равным 1.
 
Ну и для Memo c именем QR_text задайте свойство Lines аналогично свойству Items. Я указал по умолчанию «Введите текст». И еще одно свойство ScrollBars измените на ssBoth. Включение данного параметра делает доступными полосы прокрутки по горизонтали и вертикали.
 
Остальным компонентам задайте свойства Caption (Подпись) как на картинке ниже т.е. украшательство. Расположение и примерный вид следующий:

Ну, теперь приступаем к кодингу. Открываем редактор кода Code Explorer (View – Code Explorer).
 
Примечание: Исходный код постарался раскомментировать детально, поэтому детально углубляться не стану. Отмечу некоторые важные моменты.
 
Для работы приложения с интернетом, а именно запросы на сервер, получение данных и т.д. нам потребуются библиотеки HTTPApp и WinInet. Укажите их в разделе Uses. Так же, для работы с PNG изображениями (именно в таком формате Google возвращает картинку с QR-кодом) нам потребуется сторонний компонент PNGImage. У обладателей версий Delphi 2009 и более поздней, этот компонент идет уже в составе Delphi. Доп. Компонент находится в прикрепленном архиве в папке “Компонент”, там же, я набросал видеоролик по установке и подключению данного компонента.
 
После установки компонента допишите в Uses библиотеку PngImage.
 
Теперь смотрим полный листинг и разбираем по основным блокам. Остальное, как я уже говорил, все с подробными комментариями.

unit QR_unit_ex;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, HTTPApp,WinInet, ExtDlgs,PngImage;

//Подключение доп. библиотек HTTPApp и WinInet
//PngImage — сторонняя библиотека для работы с файлами типа PNG

type
  TForm1 = class(TForm)
    Image1: TImage;
    ErrLevel: TComboBox;
    QR_img_size: TComboBox;
    QR_text: TMemo;
    GetQR_But: TButton;
    Exp_QR_but: TButton;
    Label1: TLabel;
    Label2: TLabel;
    SavePictureDialog1: TSavePictureDialog;
    procedure GetQR_ButClick(Sender: TObject);
    procedure Exp_QR_butClick(Sender: TObject);
  
  private
    { Private declarations }
  public

  end;

const
    UrlQrCode=‘https://chart.googleapis.com/chart?chs=%dx%d&cht=qr&chld=%s&chl=%s’;

//Констатнта содержащая ссылку на Google API со след. входными параметрами
// chs=%dx%d — размер пулучаемого изображения QR, где %d — целочисленные параметры Ширины и Длины (напр. 150х150)
// chld=%s — уровень коррекции ошибок. %s — принимает значение L,M,Q,H
// chl=%s — параметр содержащий кодируемую информацию. %s принимает текстовые, числовые или 
//текстово-числовые значения

var
  Form1: TForm1;

implementation

{$R *.dfm}

//Процедура делающая запрос по сгенерированному URL и принимающая результатом в Stream изображение QR
//в формате PNG

 procedure WinInet_QRGet(const Url: string; Stream: TStream);
const
BuffSize = 1024*1024; //размер буфера 1кб
var
  hInter   : HINTERNET;
  UrlHandle: HINTERNET;
  BytesRead: DWORD;
  Buffer   : Pointer;

begin

hInter := InternetOpen(», INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0); //открываем соединение
  if Assigned(hInter) then //если соединение успешно, то продолжаем
  begin
    Stream.Seek(0,0); // устанавливаем указатель на 0 байт в потоке Stream 
    GetMem(Buffer,BuffSize);
    try
        //Посылаем URL с параметрами и значением
        UrlHandle := InternetOpenUrl(hInter, PChar(Url), nil, 0, INTERNET_FLAG_RELOAD, 0);

        if Assigned(UrlHandle) then
        begin
          repeat
            InternetReadFile(UrlHandle, Buffer, BuffSize, BytesRead); //читаем полученный файл PNG в переменную Buffer 

            if BytesRead>0 then
             Stream.WriteBuffer(Buffer^,BytesRead); //пишет в поток Stream прочитанные данные из Buffer
          until BytesRead = 0;
          InternetCloseHandle(UrlHandle); //по окончанию закрываем поток UrlHandle

        end;
    finally
      FreeMem(Buffer); //очищаем Buffer
    end;
    InternetCloseHandle(hInter); // закрываем интернет соединение
  end

end;

//процедура подготовки URL с присвоением наших параметров
 procedure GetQrCode(Width, Height: Word; ErrLevel:TCombobox;
  const Data: string; StreamImage: TMemoryStream);
 var
 EncodedURL: string;

  begin

 //генератор ссылки
 //с помощью функции Format подставляем наши параметры в ссылку UrlQrCode (константа)
// ключи в ссылке %d и %s соответственно число или текст
// параметры перечисляются по порядку

      EncodedURL:=Format(UrlQrCode,[Width,Height,ErrLevel.Items.Strings[ErrLevel.ItemIndex][1],HTTPEncode(AnsiToUtf8(Data))]);
     
// Width,Height — ширина высота изображения
// ErrLevel.Items.Strings[ErrLevel.ItemIndex][1] — наш ComboBox c уровнем коррекции ошибок. 
// В качестве значения берется первый символ (L,M,Q,H)
// HTTPEncode(AnsiToUtf8(Data)) — приводим наш текст в формат HTTP. 
//AnsiToUtf8 — функция конвертирующая текст из ANSI в UTF8. 
// Необходимо для корректного отображения русского текста 

      //посылаем запрос. Получаем поток содержащий изображение в переменной StreamImage
      WinInet_QRGet(EncodedURL,StreamImage);

end;

procedure TForm1.GetQR_ButClick(Sender: TObject);
var
   ImageStream: TMemoryStream;
    PngImage: TPngObject; //описание объекта графич. файла типа PNG
    Width, Height: Word;
begin
     Image1.Picture:=nil; //очищаем Image1
     ImageStream:=TMemoryStream.Create; // создаем поток который будет содержать данные изображения QR-кода
     PngImage:=TPngObject.Create; // создаем экземпляр объекта Png

    // задаем ширину изображения (вытаскиваем первые 3 символа из ComboBox — QR_img_size)
     Width := StrToInt(copy(QR_img_size.Text,0,3));
    // задаем высоту изображения (вытаскиваем последние 3 символа из ComboBox — QR_img_size)
     Height := StrToInt(copy(QR_img_size.Text,5,3));

     
 try
   try
      // вызываем процедуру GetQrCode и передаем устанвочные параметры из интерфейса приложения
      // результат будет сохранен в поток ImageStream

       GetQrCode(Width,Height,ErrLevel, QR_text.Lines.Text,ImageStream);
      
       if ImageStream.Size>0 then //проверям размер потока. Если больше 0 то загружаем в Image1
       begin
          ImageStream.Position:=0; //устанавливаем указатель в начало (0-ая позиция)
          PngImage.LoadFromStream(ImageStream); //загружаем поток в объект PNGImage

          Image1.Picture.Assign(PngImage); // присоединяем объект PNGImage к компоненту Image1 т.е. отражаем QR-код
       end;
   except
      on E: exception do
      ShowMessage(E.Message); //обработка исключения. вывод сообщения об ошибке
   end;
 finally
  ImageStream.Free; //очищаем поток ImageStream
  PngImage.Free; // очищаем объект PngImage
 end;

end;

procedure TForm1.Exp_QR_butClick(Sender: TObject);
begin
//сохранение изображения открытого в Image1 в файл с расширением .png
if SavePictureDialog1.Execute then image1.Picture.Graphic.SaveToFile(SavePictureDialog1.FileName+’.png’);
end;

end.

Константа  UrlQrCode содержит заготовку-ссылку с метками для ввода наших параметров. Метки начинаются со знака «%» следом идет символ d и s, где d- число, s-текст.
 
Процедура  GetQrCode — содержит входные параметры  Width, Height, ErrLevel, Data, StreamImage.  Width, Height, ErrLevel, Data — параметры, которые мы задаем в интерфейсе программы с помощью ComboBox’ов и Memo. StreamImage — переменная, которая будет содержать данные результата полученного при отправке запроса на Google Api. С помощью функции Format обрабатывается наша константа  UrlQrCode, параметры, отмеченные %d и %s, заменяются входными значениями. Параметры подставляются последовательно.
 
Функция AnsiToUtf8 преобразует введенное сообщение, содержащееся в переменной Data в кодировку UTF8. Если не выполнить данного преобразования, то возможны проблемы с дальнейшим отображением русского текста. Вместо читаемого текста, будут «каракули».
 
Функция  HTTPEncode — преобразует русскоязычный текст в формат «%D0%92%D0%B2%D0%B5%D0%B4%D0%B8%D1%82%D0%B5», латиница остается без изменения за исключением пробелов и знаков пунктуации.
 
В результате, в переменной  EncodedURL,  при параметрах по умолчанию, мы получим следующий адрес: «https://chart.googleapis.com/chart?chs=150×150&cht=qr&chld=L&chl=%D0%92%D0%B2%D0%B5%D0%B4%D0%B8%D1%82%D0%B5+%D1%82%D0%B5%D0%BA%D1%81%D1%82%0D%0A»
 
Собственно, если перейти по данной ссылке в браузере, мы увидим изображение QR-кода. Нам остается только сделать этот переход программно с помощью процедуры WinInet_QRGet и полученный результат, содержащийся в переменной StreamImage, отобразить в компоненте Image1.
 
Некоторые моменты в процедуре  GetQR_ButClick:
PngImage:=TPngObject.Create  — создание экземпляра объекта PNGImage. В него мы загрузим данные изображения QR из потока  StreamImage.
Загрузка происходит с помощью метода PngImage.LoadFromStream(ImageStream).
 
Примечание: Если воспользоваться методом  PngImage.LoadFromFile, то мы можем загрузить PNG-картинку из файла для дальнейших манипуляций с ней. Хочу напомнить, что в Delphi 7 нет поддержки изображений формата PNG, для этого мы использовали стороннюю библиотеку  TPNGImage.  PngImage так же имеет метод  SaveFromFile, в параметре которого нам необходимо указать полный путь и имя файла png (PngImage.SaveFromFile(“C:\test.png”)). Данный метод нам позволяет сохранить содержимое объекта PngImage в файл.
 
Image1.Picture.Assign(PngImage) — в данной строке мы присоединяем содержимое объекта   PngImage  к объекту Image1. Собственно, после данной команды у нас отобразится полученный QR-код на форме.
 
В процедуре  Exp_QR_butClick мы сохраняем содержимое Image1 в файл с использованием диалога SavePictureDialog1. Если визуальное отображение QR-кода на форме не обязательно, то сохранение в файл можно сделать сразу после строки PngImage.LoadFromStream(ImageStream), добавив строчку следующего кода: PngImage.SaveFromFile(“C:\test.png”).
 
В прикрепленном файле Вы можете найти исходный код приложения и требуемый компонент. Для обладателей новых версий Delphi с 2009 и выше устанавливать компонент TPNGImage не нужно!
 
В данном методе есть один существенный недостаток, связь с интернетом, в следующей статье я приведу пример генератора, не использующего интернет и сервис Google. Мы создадим полностью самостоятельное приложение-генератор QR-кода. Но а данный пример будет полезен для ознакомления и потом, у Google API есть много интересных функций которые могут оказаться полезными.
 

Прикрепленный файл: QR_Google_Example.zip