9. Namespace data:: — dados externos
← fix:: · Índice · Próximo: Biblioteca padrão →
8 funções para cruzar o conteúdo do PDF com listas e tabelas suas. Tudo local: nenhum dado sai da máquina.
9.1 Onde ficam os arquivos
Glossários e datasets aceitam caminho relativo ao diretório de execução:
data::load_glossary("termos/juridicos.txt")
data::load_dataset("dados/lotes.csv")
As bases de consulta (query_gtin, query_medicamento, query_postal_code) têm
nome fixo e são procuradas nesta ordem:
$PDFL_DATA_DIR(variável de ambiente)./dados/./- Perfis instalados por
pdfl add(pdfl_profiles/*/dados/) - Ao lado do PDF analisado
# Apontando explicitamente para a pasta das bases
PDFL_DATA_DIR=/opt/bases pdfl run perfil.pdfl documento.pdf
Se a base não for encontrada, a mensagem de erro diz onde colocá-la.
Para distribuir bases junto com os perfis, use pdfl pack — veja o
capítulo 11.
9.2 Glossários
Um glossário é um arquivo de texto com um termo por linha. Linhas vazias e
começadas com # são ignoradas.
termos/obrigatorios.txt:
# Termos que toda apólice precisa conter
prazo de carência
cobertura contratada
condições gerais
data::load_glossary(arquivo)
Carrega o glossário como lista de termos.
check "Glossário carregado" {
termos = data::load_glossary("termos/obrigatorios.txt")
print("termos no glossário:", termos.length)
require termos.contains("condições gerais")
}
data::validate_against_reference(arquivo)
O caminho mais direto: devolve a lista dos termos do glossário que não aparecem no documento. Lista vazia significa que está tudo lá.
check "Cláusulas obrigatórias" {
faltando = data::validate_against_reference("termos/obrigatorios.txt")
assert faltando.length == 0,
"cláusulas ausentes na apólice: #{faltando.join("; ")}"
}
A comparação ignora maiúsculas e espaçamento — "CONDIÇÕES GERAIS" satisfaz "condições gerais".
9.3 Datasets (CSV)
data::load_dataset(arquivo)
Carrega um CSV como lista de linhas; cada linha é uma lista de colunas. Aspas são tratadas conforme o padrão (campo entre aspas pode conter vírgula).
dados/lotes.csv:
lote,descricao,validade
L2026-08,Lote homologado agosto/2026,2028-08-01
L2026-09,Lote homologado setembro/2026,2028-09-01
check "Percorrendo a tabela" {
linhas = data::load_dataset("dados/lotes.csv")
// A primeira linha é o cabeçalho
print("colunas:", linhas.first().join(" | "))
print("registros:", linhas.length - 1)
// get(n) é 1-based: get(1) é a primeira coluna
linhas.each { |linha|
print(linha.get(1), "->", linha.get(2))
}
}
data::lookup_value(arquivo, chave)
Procura a chave na primeira coluna e devolve o valor da segunda. Devolve
null se não encontrar — e como null é falso, dá para testar direto.
check "Lote homologado" {
lote = text::extract_from_region(1, region(400, 50, 150, 20)).trim()
descricao = data::lookup_value("dados/lotes.csv", lote)
assert descricao,
"lote #{lote} não consta na tabela de homologados"
print("lote reconhecido:", descricao)
}
9.4 Bases de consulta
Estas funções procuram arquivos de nome fixo nas pastas descritas em 9.1 e
devolvem a linha inteira como lista (ou null).
data::query_gtin(codigo)
Consulta gtin.csv. Ignora pontuação do código.
dados/gtin.csv:
gtin,descricao,fabricante
7891234567895,Dipirona Sódica 500mg 20cp,Lab Exemplo
check "Produto homologado" {
// Cruzando com o código lido da própria embalagem
codigo = codes::decode_barcode(1)
produto = data::query_gtin(codigo)
assert produto,
"GTIN #{codigo} não consta na base de produtos"
print("produto:", produto.get(2))
print("fabricante:", produto.get(3))
}
data::query_medicamento(registro_ou_nome)
Consulta medicamentos.csv. Aceita o número de registro (primeira coluna) ou
parte do nome (segunda coluna).
dados/medicamentos.csv:
registro,nome,principio_ativo,tarja
1.0298.0123,Dipirona Sódica,dipirona monoidratada,livre
1.0298.0456,Amoxicilina,amoxicilina tri-hidratada,vermelha
check "Tarja correta na bula" {
registro = text::extract_from_region(1, region(50, 780, 200, 15)).trim()
medicamento = data::query_medicamento(registro)
assert medicamento,
"registro #{registro} não encontrado na base ANVISA"
// Se a tarja é vermelha, o texto obrigatório precisa estar na arte
tarja = medicamento.get(4)
print("medicamento:", medicamento.get(2), "| tarja:", tarja)
assert tarja != "vermelha" || text::require_text("VENDA SOB PRESCRIÇÃO"),
"medicamento de tarja vermelha sem o texto obrigatório"
}
data::query_postal_code(cep)
Consulta ceps.csv. Aceita CEP com ou sem hífen; exige 8 dígitos.
dados/ceps.csv:
cep,logradouro,bairro,cidade,uf
01310100,Avenida Paulista,Bela Vista,Sao Paulo,SP
check "Endereço do fabricante" {
endereco = data::query_postal_code("01310-100")
assert endereco, "CEP não encontrado na base"
print("logradouro:", endereco.get(2))
print("cidade:", endereco.get(4), "-", endereco.get(5))
}
data::validate_address(cep, "trecho")
Confere se o trecho informado aparece no endereço daquele CEP.
check "Endereço impresso confere com o CEP" {
// O endereço na embalagem precisa bater com o CEP declarado
assert data::validate_address("01310100", "Avenida Paulista"),
"endereço impresso não corresponde ao CEP informado"
}
9.5 Exemplo completo
// bula_com_bases.pdfl — validação cruzando PDF com bases locais
// Uso: PDFL_DATA_DIR=./bases pdfl run bula_com_bases.pdfl bula.pdf
profile "bula-com-referencias" {
check "Termos obrigatórios ANVISA" tags: ["glossario"] {
faltando = data::validate_against_reference("bases/termos_anvisa.txt")
assert faltando.length == 0,
"textos obrigatórios ausentes: #{faltando.join("; ")}"
}
check "Produto na base" tags: ["dados", "critico"] {
codigo = codes::decode_barcode(1)
produto = data::query_gtin(codigo)
assert produto, "GTIN #{codigo} não homologado"
// O nome na base tem que aparecer impresso na bula
nome = produto.get(2)
assert text::require_text(nome),
"o nome '#{nome}' da base não aparece na bula"
print("produto conferido:", nome)
}
check "Registro e tarja" tags: ["anvisa"] {
registro = text::extract_from_region(1, region(50, 780, 200, 15)).trim()
med = data::query_medicamento(registro)
assert med, "registro #{registro} não encontrado"
assert med.get(4) != "vermelha" || text::require_text("VENDA SOB PRESCRIÇÃO"),
"tarja vermelha exige o texto de prescrição"
}
check "Endereço do fabricante" tags: ["dados"] {
assert data::validate_address("01310100", "Avenida Paulista"),
"endereço do fabricante não confere com o CEP"
}
}