7. Namespace codes:: — códigos de barras e QR
← prepress:: · Índice · Próximo: fix:: →
13 funções para detectar, decodificar e validar códigos de barras e QR codes impressos no documento.
O escaneamento renderiza as páginas em alta resolução e roda uma única vez, no primeiro uso de qualquer função
codes::. Scripts que não usam o namespace não pagam esse custo.
Formatos reconhecidos: EAN-8/13, UPC-A/E, Code 128, Code 39, ITF, QR Code, Data Matrix, Aztec, PDF417, entre outros.
7.1 Detecção
codes::detect_barcodes() e codes::detect_qrcodes()
check "Embalagem tem código" {
assert codes::detect_barcodes(),
"nenhum código de barras encontrado na arte"
// QR de rastreabilidade
assert codes::detect_qrcodes(),
"faltou o QR code de rastreabilidade"
}
codes::count_barcodes()
Quantidade total de códigos detectados (barras + QR).
check "Quantidade de códigos" {
total = codes::count_barcodes()
print("códigos detectados:", total)
assert total == 2,
"esperava 2 códigos (EAN + QR), encontrei #{total}"
}
codes::get_barcode_type(n)
Formato do n-ésimo código (1-based): "EAN_13", "QR_CODE", "CODE_128"...
check "Tipo do código principal" {
tipo = codes::get_barcode_type(1)
assert tipo == "EAN_13",
"o código principal deveria ser EAN-13, é #{tipo}"
}
check "Listando todos" {
// Percorre pelos índices, de 1 até a quantidade
print("primeiro:", codes::get_barcode_type(1))
print("segundo:", codes::get_barcode_type(2))
}
codes::get_barcode_location(n)
Onde o código está: [pagina, x, y] em pontos (origem no canto inferior
esquerdo).
check "Posição do código" {
local = codes::get_barcode_location(1)
print("página:", local.get(1), "x:", local.get(2), "y:", local.get(3))
// O código deve estar na primeira página
assert local.first() == 1,
"código de barras não está na capa"
}
7.2 Decodificação e validação
codes::decode_barcode(n)
O conteúdo decodificado do n-ésimo código.
check "Conteúdo do código" {
codigo = codes::decode_barcode(1)
print("código lido:", codigo)
assert codigo.starts_with("789"),
"GTIN não é brasileiro (deveria começar com 789): #{codigo}"
}
codes::validate_barcode_checksum(n)
Valida o dígito verificador GTIN do n-ésimo código detectado.
check "Dígito verificador" {
// Um GTIN com dígito errado é rejeitado no caixa do supermercado
assert codes::validate_barcode_checksum(1),
"dígito verificador inválido no código #{codes::decode_barcode(1)}"
}
codes::validate_gtin(texto) e codes::validate_ean(texto)
Sinônimos. Validam o dígito verificador de uma string (EAN-8/13, UPC-A, GTIN-14) — útil para conferir um número vindo de outra fonte.
check "GTIN informado no texto" {
require codes::validate_gtin("7891234567895")
require !codes::validate_gtin("7891234567890") // dígito errado
// Conferindo o número impresso abaixo das barras
impresso = text::extract_from_region(1, region(400, 20, 180, 15)).trim()
assert codes::validate_ean(impresso),
"o número impresso não é um GTIN válido: #{impresso}"
}
codes::validate_code128()
Verdadeiro se há algum Code 128 decodificado com sucesso (o checksum do Code 128 é validado na própria decodificação).
check "Código logístico" {
assert codes::validate_code128(),
"faltou o Code 128 de logística"
}
7.3 Conferência cruzada
codes::compare_barcode_with_text()
Verdadeiro se o conteúdo de todos os códigos aparece no texto do documento.
Este é o teste que pega o erro mais caro da indústria: o código de barras apontando para um produto e o texto impresso dizendo outro.
check "Código confere com o texto impresso" {
assert codes::compare_barcode_with_text(),
"o número do código de barras não aparece no texto — arte com dados trocados?"
}
codes::validate_barcode_format(regex)
Verdadeiro se o conteúdo de todos os códigos casa com a expressão regular.
check "Formato esperado" {
// Só EAN-13: exatamente 13 dígitos
assert codes::validate_barcode_format("^\d{13}$"),
"há código fora do padrão EAN-13"
}
check "QR aponta para o site oficial" {
assert codes::validate_barcode_format("^https://empresa\.com\.br/.*"),
"QR code aponta para endereço não autorizado"
}
codes::validate_barcode_position(regiao) ou (x0, y0, x1, y1)
Verdadeiro se todos os códigos estão dentro da área. Aceita uma region ou
quatro números em pontos.
check "Código na área reservada" {
// Com região nomeada — mais legível
area = region(400, 20, 180, 80, "área do código")
assert codes::validate_barcode_position(area),
"código de barras fora da área reservada da embalagem"
}
check "Com coordenadas diretas" {
// x0, y0, x1, y1 em pontos
assert codes::validate_barcode_position(400, 20, 580, 100),
"código fora da posição especificada"
}
7.4 Exemplo completo
// bula_farmaceutica.pdfl — validação de código de lote em bula
// Uso: pdfl run bula_farmaceutica.pdfl bula.pdf
profile "bula-anvisa" {
check "Presença dos códigos" tags: ["codes"] {
assert codes::detect_barcodes(), "bula sem código de barras"
assert codes::count_barcodes() >= 1,
"esperava ao menos o EAN do produto"
}
check "Integridade do código" tags: ["codes"] {
codigo = codes::decode_barcode(1)
tipo = codes::get_barcode_type(1)
print("código:", tipo, "=", codigo)
assert tipo == "EAN_13", "código principal não é EAN-13 (é #{tipo})"
assert codes::validate_barcode_checksum(1),
"dígito verificador inválido: #{codigo}"
assert codigo.starts_with("789"),
"GTIN não é brasileiro: #{codigo}"
}
check "Conferência com o texto" tags: ["codes", "critico"] {
// O erro mais caro: código de um produto, texto de outro
assert codes::compare_barcode_with_text(),
"número do código não aparece no texto da bula"
}
check "Posição na arte" tags: ["codes", "layout"] {
area_reservada = region(400, 20, 180, 90, "área do código")
assert codes::validate_barcode_position(area_reservada),
"código fora da área reservada — pode ser cortado no acabamento"
}
check "Cruzamento com a base de produtos" tags: ["dados"] {
// Integra com data:: — veja o capítulo 9
codigo = codes::decode_barcode(1)
produto = data::query_gtin(codigo)
assert produto,
"GTIN #{codigo} não consta na base de produtos homologados"
print("produto:", produto.get(2))
}
}