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
- Wikipedia – Finite Field – Wikipedia contributors. (2019, May 10). Finite field. In Wikipedia, The Free Encyclopedia. Retrieved 01:41, May 20, 2019, from https://en.wikipedia.org/w/index.php?title=Finite_field&oldid=896431013