QR Code em AdvPL – Parte 03

Introdução

No post anterior vimos uma breve introdução a classe ZQRCODE. Agora, vamos ver um fonte usando essa classe ! A classe faz parte do projeto ZLIB, fontes disponíveis no GITHUB.

QR Code Print

Geração de QR Code

A geração da imagem de um determinado QR Code envolve duas etapas. A primeira é criar a matriz de bits ou Grid do QR Code, e a segunda é a criação da imagem — por hora um Bitmap monocromático. O fonte abaixo dá conta do recado usando a classe ZQRCODE e a ZBITMAP.

#include 'protheus.ch'
#include 'zlib.ch'

#define MODULE_PIXEL_SIZE     12   //  Tamanho em PIXELs do Modulo

/* ------------------------------------------------
Primeira interface para a criação de um QR Code 
------------------------------------------------ */

USER Function NewQRCode()
Local oDlg
Local cQRData := space(240)
Local cErrC , aErrorC := {}
Local nMode := 2  // Alfanumerico
Local aMasks := {}                
Local cMask 
Local nI
Local oQR

// Cria uma caixa de diálogo com área util de 800x600  PIXELs
DEFINE DIALOG oDlg TITLE "QR Code em AdvPL" FROM 0,0 TO 600,800 PIXEL

// Monta um GET para entrada de dados em variável string
// @ 18,15 SAY oSay1 PROMPT "Get String" SIZE 30,12 OF oDlg  PIXEL
@ 18,15 SAY "QR Data" PIXEL
@ 15,50 GET oGet1 VAR cQRData MULTILINE SIZE 260,36 OF oDlg  PIXEL

// Lista de níveis de correções de erro disponíveis 
aadd(aErrorC,"L.Low")
aadd(aErrorC,"M.Medium")
aadd(aErrorC,"Q.Quartile")
aadd(aErrorC,"H.High")

// Monta um objeto Combo, para escolha de string entre elementos de um array
@ 58,15 SAY "Error C" PIXEL
@ 55,50 COMBOBOX oComboBox1 VAR cErrC ITEMS aErrorC SIZE 80,12 OF oDlg  PIXEL

// Lista de Tipos de Mascaramento de dados 
// TODO - Escolha automatica de mascaramento 
// aadd(aMasks,"A.Auto")
For nI := 0 to 7
	aadd(aMasks,cValToChar(nI)+".Mask "+cValToChar(nI))
Next

// Monta um objeto Combo, para escolher o mascaramento de dados
// ou deixar no automatico ( TODO ) 
@ 73,15 SAY "Mask" PIXEL
@ 70,50 COMBOBOX oCombo2 VAR cMask ITEMS aMasks SIZE 80,12 OF oDlg  PIXEL

// Area reservada a Imagem 
@ 55,135 BITMAP oBmpQRCode OF oDlg PIXEL 
  
// Reserva a imagem em branco com 
// o Tamanho da versao 1 com margem de 4 modulos ) 
oBmpQRCode:nWidth := 29*MODULE_PIXEL_SIZE  
oBmpQRCode:nHeight := 29*MODULE_PIXEL_SIZE
oBmpQRCode:lStretch := .T.

// Botao para a geracão do QR Code
@ 85,50 BUTTON oButton1 PROMPT "&Gerar QR Code" ;
  ACTION (NewQRCode(@oQR,cQRData,cErrC,nMode,cMask,oBMPQRCode)) ;
  SIZE 080, 013 of oDlg  PIXEL

@ 100,50 BUTTON oButton2 PROMPT "&Exportar Imagem" ;
  ACTION (ExportImg(oQR)) WHEN oQR != NIL ;
  SIZE 080, 013 of oDlg  PIXEL

ACTIVATE DIALOG oDlg CENTER 

Return

// ------------------------------------------------
// Geração do QR Code na interface

STATIC Function NewQRCode(oQR,cQRData,cErrC,nMode,cMask,oBMPQRCode)

IF oQR != NIL
	FreeObj(oQR)
Endif

// Cria QRCode vazio 
oQR := ZQRCODE():New()

// Trata os dados informados 
cQRData := alltrim(cQRData)
cErrC := left(cErrC,1)
cMask := left(cMask,1)
                             
If empty(cQRData)
	MsgStop("Dados para o QR Code não informados.")
Endif

// Inicializa o QRCode com Error Correction "L", sem versao definida
// Informa o Error Correction
oQR:SetEC(cErrC)

// Seta o dado a ser colocado no QRCode
// e seta o modo de codificacao ( 1 = Numerico, 2 = Alfanumerico, 3 = Bytes , 4 = Kanji )
oQR:SetData(cQRData,nMode)

// Monta a matriz de dados 
// Se a versao nao foi especificada, determina de acordo 
// com o tamanho dos dados a serem plotados
// oQR:oLogger:SETECHO(.T.)

oQR:BuildData()

// oQR:oLogger:SETECHO(.F.)

// Monta a matriz final do QRCode 
oQR:DeployBits()

/*
conout("After Deploy Bits ")
oQR:PrintBits()
*/

// Seleciona a mascara de dados 
// Caso seja automatico, nao informa
// para a engine de emissao decidir
// O método SelectMask já aplica a máscara
If cMask <> 'A'
	oQR:SelectMask(val(cMask))
Else
	oQR:SelectMask()
Endif

/*
conout("After Mask Data ")
oQR:PrintBits()
*/

// Exporta para um BITMAP monocromatico
// Margem minima = 4 modulos ( de cada lado ) 
oBMP := ZBITMAP():NEW(oQR:nSize+8,oQR:nSize+8,1)

// Plota os módulos setados com 1 
// com a cor preta no Bitmap

For nL := 1 to oQR:nSize
	For nC := 1 to oQR:nSize
		If oQR:aGrid[nL][nC] == 1 .or. oQR:aGrid[nL][nC] == 3
			oBmp:SetPixel(nL+3,nC+3,0)
		Endif
	NExt
Next	

// Gera a imagem em disco 
// CRia um arquivo temporario com nome unico 
// para driblar o cache de imagem da interface
cFile := '\qrtmp_'+cvaltochar(seconds())+'.bmp'
oBmp:SaveToBmp(cFile)

// Redimensiona a imagem na tela
// com o tamanho da versao escolhida +margens
oBmpQRCode:nWidth := (oQR:nSize+8)*MODULE_PIXEL_SIZE 
oBmpQRCode:nHeight := (oQR:nSize+8)*MODULE_PIXEL_SIZE
oBmpQRCode:lStretch := .T.

// Carrega a imagem gerada na interface
oBMPQRCode:Load(,cFile)

// apaga a imagem temporária do disco 
// e limpa a classe bitmap da memoria 
ferase(cFile)
FreeObj(oBmp)

Return


// ------------------------------------------
// Exporta o QR Code para imagem BITMAP 

STATIC Function ExportImg(oQR)
Local nImgSize
Local oBmp
Local nL
Local nC
Local nL1,nL2,nC1,nC2
Local cFile

nImgSize := ( oQR:nSize+8 ) * MODULE_PIXEL_SIZE

oBMP := ZBITMAP():NEW( nImgSize,nImgSize, 1 )
oBMP:nPenSize := 1

// Plota os módulos setados com 1 
// com quadrados vazados no Bitmap
// e pinta o interior dos quadrados

For nL := 1 to oQR:nSize
	For nC := 1 to oQR:nSize
		If oQR:aGrid[nL][nC] == 1 .or. oQR:aGrid[nL][nC] == 3
		    nL1 := (nL+2)*MODULE_PIXEL_SIZE
		    nC1 := (nC+2)*MODULE_PIXEL_SIZE
		    nL2 := nL1+MODULE_PIXEL_SIZE
		    nC2 := nC1+MODULE_PIXEL_SIZE
			oBmp:Rectangle(nL1,nC1,nL2,nC2,0)
			oBmp:Paint(nL1+1,nC1+1)
		Endif
	Next
Next	

// Salva o arquivo em disc 
cFile := '\QRCode.bmp'
oBmp:SaveToBmp(cFile)
FreeObj(oBmp)

MsgInfo("Imagem do QR Code exportada para o arquivo ["+cFile+"]")

Return

Por hora a aplicação gera um QR Code com capacidade aproximada de até 200 caracteres, usando uma codificação Alfanumérica restrita de 44 caracteres. É suficiente para textos simples e URLs. O fonte da implementação será destrinchado em próximos posts da sequência, afinal ele está com quase 3000 linhas de código — e ainda não está completo.

Exportação da Imagem

A imagem exportada é criada como um BITMAP monocromático, com cada módulo desenhado com um quadrado de 12 pixels. Podemos diminuir no fonte este tamanho, tomando apenas o cuidado de gerar um QR Code com tamanho suficiente para o scanner ou aplicativo usado para leitura seja capaz de lê-lo. No objeto BITMAP criado é possível aumentar o número de cores da imagem, trocar a cor de plotagem, etc.

Mascaramento de Dados

Existem oito formas de mascaramento de dados, para gerar um QR Code que seja mais fácil ou menos suscetível de erro de leitura pelo Scanner. Atualmente a implementação ainda requer que você informe qual o mascaramento desejado, a parte que faz a avaliação dos mascaramentos para escolha automática ainda está em desenvolvimento.

Próximas etapas

O objetivo da implementação é suportar até o nível 40 com correção de erro “H” (High), mas para isso ainda é necessário codificar a separação dos dados em grupos para a geração do Grid do QR Code — também em desenvolvimento.

Conclusão

Não sabendo que era impossível, ele foi lá e fez. — Jean Cocteau

Agradeço a todos pela audiência, e lhes desejo TERABYTES DE SUCESSO !!!

 

 

4 comentários sobre “QR Code em AdvPL – Parte 03

  1. Nunca mais eu desafio esse cara a fazer algo, desafiei ele pelo facebook a construir o QRCode em ADVPL, sabe o que aconteceu, me dei mal kksss em menos de um mês bummmmmm taí o fonte, realmente ele é Louco kkkss ou melhor Julio Louco.
    Valeuu cara vc é 10000

    Curtido por 1 pessoa

  2. Julio, parabéns pelo trabalho.
    Comecei a usar a zLib para impressão do QRCode com os comandos para impressora térmica Argox.
    Ajustei o seu fonte para gerar a imagem e consegui ler a imagem com MSCBLoadGRF(“img”), antes com a biblioteca ZXing, apenas conseguia ler a imagem se convertesse para monocromático com o Paint.Agora eu consigo gerar a imagem dinamicamente numa aplicação de testes para produção. Por enquanto a versão simples do QRCode está atendendo.
    Com os seus fontes é possível até ler um bmp maior do que recomendam no tdn, bem como imprimi-lo na Argox..
    Excelente trabalho, sei o quanto é trabalhoso, e instigante quando se entendem bem, eu ainda estou apanhando no entendimento, mas já consegui algo razoável para testar. É claro que com muito da sua contribuição.
    Abraços e mais uma vez parabéns.

    Curtido por 2 pessoas

    • Fera, muito gato pelo feedback, e posso dizer que é uma satisfação compartilhar conhecimento e saber que ele está sendo bem aproveitado !!!! Os próximos posts com maiores esclarecimentos de cada etapa estão no “forno” … 😀 Agradeço a audiência 😀

      Curtido por 1 pessoa

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s