QR Code em AdvPL – Parte 04

Introdução

No post anterior, vimos sem grandes detalhes as etapas de criar um QR Code “from scratch” (do zero), e as áreas reservadas da imagem. Agora vamos ver os passos necessários com mais detalhes.

Passo 01 – Escolha da correção de erro

Como vimos no post anterior, quanto maior o nível de correção de erro, mais módulos (quadradinhos) são necessários para representar a informação. Como cada versão de QR Code possui um tamanho fixo, a quantidade de informações que podemos colocar dentro de um QR Code varia de acordo com o tipo do alfabeto usado para representar os dados (numérico, alfanumérico, bytes). Por exemplo, a versão 1 com correção de erro “L” (baixa), cabem 25 símbolos alfanuméricos ou 17 bytes. Quando usada a mesma versão, mas o nível de correção de erro “H” (alto), cabem 10 símbolos alfanuméricos ou 7 bytes.

Desse modo, primeiro verificamos qual é o tipo de alfabeto que podemos utilizar, e escolhemos uma correção de erro que atenda a necessidade. Vários sites de informações e recursos de QR Code comentam que níveis mais altos são recomendáveis para utilização em ambientes onde a impressão está mais sujeita a danos — como etiquetas e manifestos colados em caixas, para um chão de fábrica. Tão importante quanto isso, é a escolha posterior de um tamanho para a imagem impressa, deve-se certificar que o equipamento usado para fazer a leitura (scanner) seja capaz de ler a informação.

Uma vez sabendo a quantidade de símbolos no alfabeto utilizado, e o nível de correção de erro desejado, podemos escolher uma versão de QR Code que as informações a serem representadas “caibam dentro”.

Dentro da classe ZQRCODE() existe uma função chamada GetQRLimits(), que justamente retorna um array pré-calculado com as versões, correção de erro, e capacidade de símbolos por modo de codificação (alfabeto) utilizado.

Passo 02 – Codificar os dados

Seguindo uma receita de bolo para cada codificação usada, os dados a serem representados no QR Code devem passar por uma primeira etapa de processamento, gerando um stream ou sequência de BITS (os zeros e uns) para então ser re-codificado em uma sequência de números inteiros de tamanho igual a capacidade da versão do QR Code em uso. Para isso são usadas conversões de numérico decimal para binário e vice-versa. No final desta etapa, teremos um array de números inteiros com valores entre 0 e 255, que vamos chamar de “Code Words de Dados”.

O método responsável pelas etapas de codificação de dados chama-se BuildData(). O dicionário alfanumérico “restrito” de 44 símbolos é armazenado em um array estático (_Alpha) e alimentado na carga do programa através da função GetAlpha(). Para chamá-lo é necessário no mínimo informar a correção de erro desejada e atribuir os dados que vão compor o QR Code. De posse dessa informação, caso você ainda não tenha escolhido uma versão para o QR Code, internamente será chamado o método BestVersion(), que vai identificar se os dados informados podem ser codificados com o alfabeto restrito ou codificados em bytes mesmo, buscando a menor versão necessária para caber as informações desejadas.

** Por hora este método assume que os dados serão codificados em bytes, e calcula a menor versão necessária para a codificação neste formato ***

Passo 03 – Codificar a recuperação de erros

Feita a codificação dos dados, consultamos uma outra lista pronta, através da função GetECCW(), que a partir da versão e da correção de erro utilizada, retorna a quantidade de CodeWords de correção de erro que devem ser geradas para fazer parte do QR Code.

O menor número de CodeWords de erro a ser gerado são 7, quando usamos a versão 1 com correção de erro “L”. Embora a quantidade de CodeWords aumente significativamente em versões mais altas, existe uma regra para divisão de blocos de dados em agrupamento de dois tipos, a partir da versão 3 com correção de erro “H”, onde a lista de CodeWords para correção de erro são geradas por grupo. Logo, mesmo que um QR Code de versão 40 com correção de erro “L” suporte 2956 CodeWords de dados, cada grupo terá no máximo 30 CodeWords para correção de erro, geradas para 19 blocos tipo 1 com 118 CodeWords de dados cada, e 6 blocos tipo 2 com 119 CodeWords de dados cada. Logo, serão 30 gerações de 30 CodeWords de correção de erro. Todos estes valores podem ser obtidos na lista retornada pela função GetECCW().

A geração destes códigos envolve algumas fórmulas matemáticas com polinômios (equações), porém como as fórmulas de base para o cálculo são montadas de acordo com a quantidade de CodeWords de correção de erro a serem montados, basta criar uma lista com as fórmulas prontas para cada número necessário para cada versão. As fórmulas estão representadas em arrays bidimensionais, retornados pela função GetPolyGen(), que recebe como parâmetro o número de bytes (ou CodeWords) de correção de erro necessários.

Cada cálculo de correção de erro parte de um grupo de dados — representado por um array de números (que convenientemente vamos chamar de “mensagem”) — e uma sequência de operações polinomiais feitas com a mensagem. O resultado final destas operações será justamente um array com a lista de bytes (CodeWords) de correção de erro gerados para aquela mensagem.

As etapas do cálculo são multiplicações e divisões polinomiais, algo relativamente complexo. Porém, as operações necessárias tornam-se possíveis de ser feitas com um programa simples, graças ao uso de uma representação aritmética de campos finitos (ou Galois Field). Na prática, e visto de forma superficial, a utilização de duas listas de conversão pré-calculadas , é possível transformar o polinômio com fatores gerados com os dados da mensagem para a aritmética GF(256). Com isso, ao invés de precisar criar ou usar um algoritmo complexo para fazer uma multiplicação ou divisão polinomial, a representação de fatores usando essa aritmética permite que as operações de multiplicação sejam feitas apenas somando expoentes na equação, e a divisão polinomial longa seja feita usando o operador binário XOR (“ou exclusivo”) diretamente com os fatores. As conversões entre os fatores decimais e os fatores em notação GF(256) são feitas com base nas funções GetGFA2Int() e GetGFInt2A(), usando listas pré-calculadas.

Passo 04 – Estruturação da mensagem final

Após calcular as duas listas (ou arrays) de CodeWords (dados e correção de erro), a estruturação da mensagem final terá mais etapas quanto maior for a versão do QR Code a ser emitida. As etapas consistem na criação de um ou mais grupos de dados, de tamanho limitado e pré-definido, com os dados e a correção de erro seqüenciados ou intercalados. A função GetECCW() também retorna a informação de quantos grupos de dados são necessários para cada versão, a intercalação de dados e códigos de erro somente é necessária quando temos mais de um grupo de dados, e a regra aplicada é a mesma, independente de quantos grupos de dados são necessários para estruturar a mensagem final.

O objetivo desta etapa é obter a sequência final de CodeWords ou bytes, contemplando o array de dados e o array de correção de erro, que será usado para gerar a sequência de módulos binária para compor a matriz final do QR Code.

Passo 05 – Disposição dos módulos da mensagem final

O método DeployBits() gera a seqüencia binária de dados para o QR Code, alimentando as áreas de dados do QRCode, ignorando as áreas reservadas. É um malabarismo de bits em zigue-zague, comecando de baixo para cima, da direita para a esquerda, preenchendo sempre 2 colunas de bits.

Passo 06 – Mascaramento de dados

Aqui a coisa enrosca … após distribuir os dados, você já tem um QRCode … MAS, como os dados usados para compor o QRCode podem gerar seqüencias longas ou contínuas de blocos preenchidos ou blocos vazios, e até mesmo gerar algo parecido com uma marca de controle, o IDEAL [e que a aplicação que emita o QRCode seja capaz de aplicar os 8 (oito) fórmulas de mascaramento de dados sobre os dados originais do QRCode, e escolha qual a melhor alternativa de apresentação. Essa etapa ainda está em desenvolvimento, por hora a classe permite eu definir ou escolher um método, ela ainda não escolhe sozinha … Essa tarefa, quando completa, será feita pelo método SelectMask().

Passo 07 – Informações de versão e formato

As informações de versão e formato do QR Code são colocadas em áreas pré-determinadas, de acordo com a versão e formato escolhidos. Existem máscaras de dados prontas já armazenadas no fonte para informar esses dados nos lugares corretos.

Após aplicar a máscara com o SelectMask(), o array aGrid da classe vai conter as informações necessárias para a plotagem do QRCode. Basta varrer todas as linhas e colunas do array, e onde o elemento oObj:aGrid[nRow][nCol] for igual a 1 ou 3 ( dado preenchido ou dado de controle ), deve ser plotado um módulo preenchido, senão o módulo deve ser vazio. No fonte de exemplo do post anterior (QR Code em AdvPL – Parte 03), a imagem do QRCode é gerada com um bitmap em memória, onde cada módulo ocupa um pixel, e depois de gerada, é usado o método Stretch() da classe de Bitmap, para aumentar a imagem 12x — assim , cada módulo tera 12×12 pixels — definido em

MODULE_PIXEL_SIZE 

Conclusão

Todo esse esforço para gerar um QRCode tem apenas um objetivo: Mostrar que é possível de ser feito … e mostrar quais as dificuldades em lidar com blocos de informações em baixo nível — de pixels a bits — e que dá para fazer em AdvPL 😀

Espero que todas essas informações sejam de utilidade e apreciação de todos aqueles que buscam saber e fazer sempre mais !!! E conhecer cada vez mais !!!

Desejo a todos, como sempre, TERABYTES DE SUCESSO !!!!

Referências

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 !!!

 

 

QR Code em AdvPL – Parte 02

Introdução

O post anterior foi apenas para abrir o apetite. Agora nós vamos entrar no passo a passo da classe ZQRCODE.

Classe ZQRCODE

A implementação mais simples para a utilização do usuário normalmente é a mais complexa de ser implementada. Simplificar uma ponta normalmente significa puxar a dificuldade e ter mais etapas e processos na outra. Na Internet existem várias implementações de sites gratuitos com geradores online de QR Code.

Boa parte delas permite gerar um QR Code solicitando ao usuário apenas um link, um email, um texto livre. Outras já são mais sofisticadas, permitem ao usuário informar o nível de correção de erro da imagem — quanto maior o nível, mais módulos (os quadradinhos) são usados para desenhar a imagem.

Criando o Objeto ZQRCODE

O método NEW(), construtor da classe, não recebe parâmetros, apenas cria um objeto vazio para a confecção de um QR Code. Por default, a correção de erro utilizada é “L” (Low) — a menor e mais leve. Por hora basta saber que existem 4 níveis de correção, e que isso pode ser alterado por um método na instância do objeto.

Setando a correção de erro

Através do método SETEC(), podemos alterar o nível da correção de erros da imagem a ser gerada. São 4 níveis, L (Low) , M (Medium), Q (Quartile) e H (High). Cada um dos níveis permite um índice de recuperação aproximado — variando de 7% (L) a 30% (H) — na prática, isto indica o percentual aproximando de “o quanto uma imagem pode estar danificada mas ainda assim ser lida”. Este recurso permite por exemplo a criação de QR Codes “artísticos”, por exemplo com uma outra imagem ou logotipo sobrescrevendo uma parte do código, e mesmo assim ele é lido com sucesso.

Informando os dados

Através do método SETDATA() podemos definir qual a informação que será colocada dentro da área de dados do QR Code. Embora exista suporte específico para dados numéricos, a implementação em AdvPL recebe sempre uma string. Como o QR Code possui 4 modos de codificação de dados (numérico, alfanumérico restrito, bytes e Kanji), você pode opcionalmente informar um número de 1 a 4 no modo desejado, ou não informar este parâmetro — a classe decide o melhor método baseado nas informações fornecidas. E, apenas para informação, Kanji não foi implementado 😀

Montando o QR Code

O método BUILDDATA() é onde começa realmente a implementação. Baseado nas escolhas anteriores e nos dados fornecidos, este método concentra chamadas para vários métodos e funções auxiliares. Uma das primeiras é a decisão de qual modo serão codificados os dados — numérico, alfanumérico ou bytes.

Como o modo alfanumérico contempla apenas letras maiúsculas, números e os caracteres  ” “ (espaço em branco), “$” (cifrão) , “%” (percentual), “*” (asterisco), “+” (soma), “-“ (subtração), “.” (ponto), “/” (barra direita) e “:” (dois pontos), ele é suficiente para um endereço de um Web Site, ou mesmo um e-mail — mesmo não tendo o caractere “@” ( arroba) neste alfabeto, nada impede de usar uma escape sequence de URL “%40” no lugar deste caractere — onde a interpretação depende do leitor — ou codificar em bytes UTF8 ( onde o “@” ‘é o caractere ASCII 64 ).

Escolhido o alfabeto, e assumindo um nível de correção de erro, podemos estimar qual é a versão de QR Code necessária para que o dado informado “caiba” dentro. A classe possui uma lista predefinida de capacidade por nível de correção de erro e alfabetos, logo uma busca ordenada nesta lista é suficiente para encontrar a versão de QR Code adequada.

Determinada a versão — e consequentemente o tamanho — do QR Code, a propriedade ::aGrid é inicializada com um array bidimensional de números, onde cada posição equivale a um módulo (quadradinho) do QR Code. Todas as posições do array são inicializadas com o valor -1 — que indica que esta posição está livre para uso — e posteriormente as áreas reservadas e de dados serão preenchidas com 0 (módulo vazio) ou 1 (módulo usado).

Áreas reservadas

Um QR Code é lido por um scanner óptico, de uma forma diferente de um código de barras tradicional. Para ser possível a leitura dos dados na ordem correta, e permitir ao leitor determinar a localização e alinhamento dos dados. O padrão de módulos chamado de “localizador” é uma imagem composta por uma matriz 7×7 formando um quadrado com 26 módulos, e na área interna 9 módulos no centro formando outro quadrado.

Um QR Code utiliza três localizadores, distribuídos nos cantos superior esquerdo, superior direito e inferior esquerdo. A partir da versão 2, é necessário utilizar uma imagem adicional, chamada de “alinhamento”. Ele é formado por uma matriz 5×5 formando um quadrado de 18 módulos, com um módulo central. Quanto maior a versão do QR Code, e consequentemente seu tamanho, mais imagens de alinhamento são necessárias.

Veja abaixo o padrão localizador e o padrão de alinhamento, respectivamente:

qr_Pattern01        qr_Pattern02

Em volta de cada localizador, deve ser deixada em branco uma margem de um módulo. O localizador é 7×7, mas ele reserva uma área de 8×8 módulos, onde a linha e coluna de margem de módulos não setados está na parte de dentro do QR Code.

Os três localizadores são ligados entre si por uma sequência alternada de módulos ligados e desligados, formando uma espécie de “linha pontilhada”. Os módulos que compreendem estas linhas também são reservados. Estas linhas pontilhadas são chamadas de “Timing Patterns“. E, existe mais um ponto reservado do lado do localizador inferior, chamado “Dark Module“, deve ser setado na nona coluna da esquerda para a direita do localizador inferior, imediatamente a direita da primeira linha do localizador inferior. Veja abaixo um QR Code versão 1 com os localizadores e as “Timing Lines” e o “Dark Module” desenhados.

qr_Pattern03

Ainda faz parte das áreas reservadas 30 módulos, em volta do localizador superior esquerdo, na lateral direita do localizador inferior esquerdo, e na primeira linha abaixo do localizador superior direito. Estes módulos são reservados para representar a formatação e correção de erro, que usam 15 módulos. São reservados 30 módulos, para a informação ser colocada duas vezes na imagem. Estes espaços estão destacados em azul. Posteriormente vamos ver como estas áreas são preenchidas.

Codificação dos dados

Uma vez com a definição do alfabeto a ser usado, os dados fornecidos são codificados em uma sequência de bits. Caso a sequencia de bits seja menor do que a área de dados (quantidade de módulos não reservados disponíveis), são usados dois bytes predefinidos para preencher a sequência até o final. Após a codificação binária, cada conjunto de 8 bits (1 byte) é convertido para um valor inteiro de 0 a 255, que na prática compõe a sequencia de dados da mensagem.

Porém esta sequência não é desenhada diretamente no QR Code. De acordo com a versão e do nível de correção de erro utilizado, um determinado número de bytes de correção de erro devem ser gerados usando a sequencia de dados da mensagem e um polinômio (equação) gerada sob medida para a quantidade de bytes de correção de erro. Neste ponto do processamento, chamamos cada um destes “bytes” ou números de “Code Words”.

Geração da correção de erro

Esta é uma das etapas mais complexas do processo. No tutorial mais completo que eu encontrei, as equações e as etapas — multiplicação e divisão de polinômios — era explicada em detalhes, até mesmo como funciona a geração do polinômio de acordo com o número de Code Words necessárias para correção de erro.

A primeira grande sacada foi aproveitar um recurso de montagem dinâmica das equações para cada número de bytes de correção de erro necessários, e guardar as equações em uma lista, assim eu já tenho no programa as equações prontas.

A segunda grande sacada foi aproveitar uma lista pronta de conversão de dados do polinômio em uma notação de potência de dois — ou notação Alpha — também já calculada. Como existe a necessidade durante o processamento de transformar uma equação com os valores numéricos dos elementos entre notações, foram montadas duas funções de conversão com a lista (Alpha para Numérico e vice-versa).

A terceira grande sacada foi, após entender como os cálculos funcionam, que era possível representar os polinômios com arrays bidimensionais, e que as operações poderiam ser feitas apenas com os valores e as potências armazenados nestes arrays. Uma vez que eu entendi a regra, aí sim foi possível escrever o resto da rotina.

Depois de multiplicações e divisões e conversões, o método BUIDERRDATA() retorna um array contendo os números (bytes ou Code Words) que completam a sequência de Code Words dos dados.

Disposição dos dados na matriz

Chegando na penúltima etapa, após montar as sequencias de dados e de correção de erro, a próxima etapa é gerar um (ou mais) grupos de bits com todos estes números e distribuir de forma ordenada na matriz do QR Code, sem passar por cima das áreas reservadas. Esta etapa não é muito complexa, porém pode exigir a separação e intercalação de dados, de acordo com o tamanho (versão) do QR Code a ser gerado.

Na forma mais simples, com QR Code que usam apenas um grupo de informações, a sequência final de CodeWords é gerada apenas acrescentando a correção de erro no final da sequência de dados, gerando uma string binária (apenas com “0” e “1”) e distribuindo pares de bits da direita para a esquerda, iniciando no canto inferior direito, de baixo para cima, e invertendo a direção e pulando duas colunas para a esquerda a cada vez que o limite superior ou inferior são atingidos. É praticamente um ziguezague de ziguezagues. E ainda não acabou.

Mascaramento de Dados

Dependendo da sequência de bits que compõe a mensagem, e da forma que eles foram distribuídos dentro do QR Code, pode acontecer agrupamentos de muitos módulos preenchidos, junto com áreas em branco, ou ainda áreas de dados que podem ficar parecidas com as áreas reservadas e “confundirem” o scanner.

Para evitar este tipo de ocorrência, existem 8 fórmulas de “mascaramento de dados”, criadas para serem aplicadas após a disposição dos dados, para verificar como ficou a versão final do QR Code. Existe uma técnica que envolve algumas regras analisar as sequencias e áreas de dados do QR Code, para determinar um fator de “penalidade”.

A etapa de mascaramento de dados consiste em guardar a versão original dos dados distribuídos no QR Code, gerar todas as 8 possibilidades de aplicação de mascaramento de dados, e avaliar o índice de penalidade de cada uma. O mascaramento que retornar o menor índice é a melhor opção de uso para facilitar a leitura pelo scanner. Este trabalho é feito pelo método MaskDataGrid()

Informações de versão e formato

Após escolher o mascaramento a ser usado, e já ter as áreas de dados e reservadas plotadas, é possível determinar aquela sequência de formato de 15 módulos que eu mencionei anteriormente, para finalmente colocá-la no grid do QR Code. Com isso feito, o resultado final é o array ::aGrid preenchido com zeros e uns, onde cada 0 é um módulo em branco — vazio — e cada 1 é um módulo a ser desenhado. Com este array, é possível criar um objeto ZBITMAP monocromático, e fazer a pintura de blocos de pontos para formar os quadradinhos necessários, lembrando que a imagem final deve reservar 5 módulos de margem (superior, inferior e laterais) em branco.

Conclusão

Nos próximos posts sobre QR Code, vou detalhar cada etapa deste processo, desde a escolha do alfabeto ou modo de codificação, até a geração da imagem final, com o código, claro !!!!

Espero que vocês tenham gostado, e desejo a todos TERABYTES DE SUCESSO !!!

Referências

 

QR Code em AdvPL – Parte 01

Introdução

Alguém me comentou algo sobre QR Code no meu Facebook, e como eu já tinha ouvido falar nesse tipo de código, resolvi pesquisar um pouco. O resultado da pesquisa acabou saindo em um código em AdvPL para gerar um QR Code “na unha”. Vamos ver passo a passo o que e como isso foi feito a partir deste post.

O que é o QR Code

Visto de uma forma muito, muito simplista, um QR Code é um código de barras quadriculado. Trata-se de uma imagem quadriculada formando uma matriz de bits, onde cada bit “1” é um quadradinho preto, e cada bit “0” é um quadradinho branco. Visto como apenas um mapa de bits quadriculado, e encontrando muitos geradores de QR Code online na Internet, eu não imaginava a complexidade escondida por trás da estrutura dessa imagem. A definição detalhada e completa você encontra na Wikipedia.

Tudo em AdvPL

A imagem acima, por exemplo, é um QR Code que contém o endereço deste blog — “https://siga0984.wordpress.com/&#8221;. Um QR Code pode conter apenas números, caracteres alfanuméricos (com alfabeto limitado), caracteres UNICODE (UTF8) e até mesmo Kanji — este código inclusive foi desenvolvido para a Industria Automobilística Japonesa em 1994.

Embora sua utilização inicial fosse apenas para etiquetar caixas, atualmente ele é utilizado das mais diversas formas. Com a possibilidade de leitura da imagem por um Smartphone, ele pode ser usado para identificar um website, um evento, códigos promocionais, conter dados criptografados — como o verso da versão impressa da nova CNH Brasileira  — enfim, o céu é o limite.

Características Gerais

Cada “quadradinho” de um QR Code é chamado de “módulo”. Existem 40 versões de QR Code, relacionadas diretamente ao tamanho da matriz de módulos. A versão 1 inicia com 21×21 módulos, e a 40 com 177×177 módulos. Cada versão incrementa o tamanho da matriz em 4 unidades. A codificação dos dados pode usar 4 níveis de recuperação de erro, o que permite inclusive ler com sucesso um QRCode parcialmente danificado. A imagem contém os dados, a correção de erros, áreas reservadas para informações sobre a imagem — como os dado foram codificados na imagem — e alguns padrões de imagens fixas para auxiliar no alinhamento óptico do leitor. A partir de determinadas versões os dados são divididos em grupos, e o método de disposição da matriz de bits intercala dados e correção de erro — não fica tão complexo, apenas mais trabalhoso.

Mergulhando nos bits

Implementar um gerador de QR Code não está sendo tão simples, cada etapa possui suas características e regras. A maioria é relativamente simples, apenas requer vários passos — exceto pelo cálculo da correção de erro… Passei três dias mastigando exemplos e pesquisando o algoritmo de Reed-Solomon, só consegui implementar os cálculos depois que eu realmente entendi — ou quase — a multiplicação e divisão de polinômios. Neurônios colocados para escanteio ha quase 30 anos foram “ressuscitados” lembrando das regras matemáticas ligadas a equações, potências e afins.

Boa parte da implementação envolve a geração de sequências binárias e diversas conversões — as funções criadas para isso foram incorporadas na ZLIB, e já foram posadas no Blog, e a última parte seria justamente a geração da imagem, já praticamente pronta com a classe ZBITMAP — publicada em posts anteriores.

Fontes de Pesquisa

Nas minhas buscas, o que salvou a pátria e trouxe informações indispensáveis para começar a implementação em AdvPL e esclarecer “o que é e como funciona” foi o site https://www.thonky.com/qr-code-tutorial/ !! Além de explicar como funciona e detalhar os cálculos, o conteúdo publicado engloba algumas tabelas com os resultados pré-calculados, uma mão na roda. Wikipedia e outros sites complementaram as informações.

Conclusão

Para este momento, concluo apenas que a jornada do QR Code está apenas começando, mas seu objetivo não é apenas mostrar por dentro como a mágica foi feita, mas também abordar as escolhas feitas na codificação e ter mais um exemplo prático de orientação a objetos em AdvPL 😀

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

Referências