2. Tipos do documento
← A linguagem · Índice · Próximo: text:: →
Todo script recebe automaticamente a variável doc, que representa o PDF em
análise. A partir dela você chega às páginas, fontes e imagens.
2.1 doc — o documento
Propriedades
| Propriedade | Tipo | O que é |
|---|---|---|
doc.page_count |
número | Quantidade de páginas |
doc.title |
texto | Título dos metadados (vazio se ausente) |
doc.author |
texto | Autor dos metadados (vazio se ausente) |
doc.filename |
texto | Nome do arquivo analisado |
doc.pages |
lista | Todas as páginas |
doc.fonts |
lista | Todas as fontes usadas |
doc.images |
lista | Todas as imagens de todas as páginas |
check "Propriedades do documento" {
print("arquivo:", doc.filename)
print("páginas:", doc.page_count)
print("título:", doc.title)
// As coleções são listas comuns — aceitam todos os métodos de lista
require doc.pages.length == doc.page_count
require doc.fonts.length > 0
print("imagens no documento inteiro:", doc.images.length)
}
Métodos
doc.extract_text()
Todo o texto do documento, com as páginas separadas por quebra de linha.
check "Texto do documento" {
texto = doc.extract_text()
assert texto.trim() != "", "PDF sem texto extraível (só imagens?)"
require texto.contains("Contrato")
print("caracteres no total:", texto.length)
}
2.2 page — a página
Páginas vêm de doc.pages (dentro de blocos) ou da variável page (dentro de
uma rule).
Propriedades
| Propriedade | Tipo | O que é |
|---|---|---|
page.number |
número | Número da página, começando em 1 |
page.index |
número | Índice da página, começando em 0 |
page.width |
número | Largura em pontos |
page.height |
número | Altura em pontos |
page.images |
lista | Imagens desta página |
page.tac |
número | Cobertura de tinta máxima estimada (%) |
page.ink_coverage |
número | Cobertura média de tinta estimada (%) |
page.min_stroke_width |
número/null | Menor espessura de traço (pt); null se não há traços |
page.has_media_box |
booleano | Tem MediaBox definida |
page.has_crop_box |
booleano | Tem CropBox definida |
page.has_trim_box |
booleano | Tem TrimBox definida |
page.has_bleed_box |
booleano | Tem BleedBox definida |
page.has_art_box |
booleano | Tem ArtBox definida |
check "Formato das páginas" {
doc.pages.each { |page|
// number é o que o usuário vê; index é para cálculos internos
assert page.width > 100mm,
"página #{page.number} estreita demais: #{page.width}pt"
// Caixas: essenciais para impressão
assert page.has_trim_box,
"página #{page.number} sem TrimBox (área de corte)"
assert page.has_bleed_box,
"página #{page.number} sem BleedBox (sangria)"
}
}
check "Tinta e traços" {
doc.pages.each { |page|
assert page.tac <= 300,
"página #{page.number}: #{page.tac}% de tinta (limite 300%)"
// min_stroke_width pode ser null (página sem traços) —
// null é falso, então este teste é seguro:
assert !page.min_stroke_width || page.min_stroke_width >= 0.25,
"página #{page.number} tem traço fino demais"
}
}
Métodos
page.extract_text()
Texto apenas desta página.
check "Páginas em branco" {
brancas = doc.pages.filter { |p| p.extract_text().trim() == "" }
assert brancas.length == 0,
"#{brancas.length} página(s) em branco: #{brancas.map { |p| p.number }.join(", ")}"
}
2.3 font — a fonte
Fontes vêm de doc.fonts.
| Propriedade | Tipo | O que é |
|---|---|---|
font.name |
texto | Nome da fonte |
font.is_embedded |
booleano | Está embutida no arquivo |
check "Fontes embutidas" {
// Fonte não embutida é substituída pelo leitor — o texto muda de aparência
doc.fonts.each { |font|
assert font.is_embedded,
"fonte '#{font.name}' não está embutida no PDF"
}
}
check "Relatório de fontes" {
print("fontes usadas:", doc.fonts.map { |f| f.name }.join(", "))
faltando = doc.fonts.filter { |f| !f.is_embedded }
print("não embutidas:", faltando.length)
}
2.4 image — a imagem
Imagens vêm de doc.images (todas) ou page.images (de uma página).
| Propriedade | Tipo | O que é |
|---|---|---|
image.width |
número | Largura em pixels |
image.height |
número | Altura em pixels |
image.dpi |
número | Resolução efetiva (o menor entre dpi_x e dpi_y) |
image.dpi_x |
número | Resolução horizontal efetiva |
image.dpi_y |
número | Resolução vertical efetiva |
image.color_space |
texto | DeviceRGB, DeviceCMYK, Indexed... |
image.page_number |
número | Página onde aparece (1-based) |
image.bits_per_pixel |
número | Bits por pixel |
O DPI é o efetivo, calculado como pixels ÷ tamanho impresso na página — não o valor nominal gravado nos metadados. É o número que importa para qualidade de impressão: uma imagem de 1000 px esticada para ocupar 20 cm tem DPI baixo, mesmo que os metadados digam outra coisa.
profile "imagens-para-offset" {
const DPI_MINIMO = 300
check "Resolução" {
doc.images.each { |img|
assert img.dpi >= DPI_MINIMO,
"imagem #{img.width}x#{img.height}px na página #{img.page_number}: #{img.dpi} DPI (mínimo #{DPI_MINIMO})"
}
}
check "Espaço de cor" {
// Impressão offset trabalha em CMYK; RGB precisa de conversão
doc.images.each { |img|
assert img.color_space != "DeviceRGB",
"imagem RGB na página #{img.page_number} — converter para CMYK"
}
}
check "Imagens por página" {
doc.pages.each { |page|
// page.images traz só as imagens daquela página
print("página", page.number, "tem", page.images.length, "imagem(ns)")
}
}
}
2.5 region — área da página
Regiões delimitam áreas retangulares para validar partes específicas da página: rodapé, cabeçalho, área do código de barras, tarja de medicamento.
Criando
// region(x, y, largura, altura [, "nome"])
// A origem (0,0) é o canto INFERIOR esquerdo, como no PDF.
cabecalho = region(0, 742, 595, 100, "cabeçalho")
rodape = region(0, 0, 595, 60, "rodapé")
tarja = region(20mm, 250mm, 60mm, 15mm, "tarja vermelha")
Propriedades
| Propriedade | O que é |
|---|---|
region.name |
Nome dado na criação (vazio se omitido) |
region.x / region.y |
Canto inferior esquerdo |
region.width / region.height |
Dimensões |
region.right / region.top |
Bordas direita e superior (calculadas) |
region.area |
Área em pontos quadrados |
Métodos
| Método | O que faz |
|---|---|
region.contains_point(x, y) |
O ponto está dentro? |
region.intersects(outra) |
As duas regiões se sobrepõem? |
region.expand(pt) |
Nova região maior em todos os lados |
region.inset(pt) |
Nova região menor em todos os lados |
region.export_coordinates() |
[x0, y0, x1, y1] |
check "Trabalhando com regiões" {
rodape = region(0, 0, 595, 60, "rodapé")
require rodape.name == "rodapé"
require rodape.top == 60.0
require rodape.right == 595.0
require rodape.area == 35700.0
// Um ponto no rodapé?
require rodape.contains_point(300, 30)
require !rodape.contains_point(300, 500)
// Sobreposição: útil para detectar elementos invadindo áreas
cabecalho = region(0, 780, 595, 62)
require !rodape.intersects(cabecalho)
// expand/inset devolvem NOVAS regiões (a original não muda)
folga = rodape.expand(5mm) // 5mm maior de cada lado
seguro = rodape.inset(3mm) // 3mm menor de cada lado
require folga.area > rodape.area
require seguro.area < rodape.area
}
Usando regiões nas validações
profile "bula-farmaceutica" {
check "Tarja de tarja vermelha" {
// A tarja precisa estar no topo, com texto legal
tarja = region(0, 700, 595, 142, "tarja")
conteudo = text::extract_from_region(1, tarja)
assert conteudo.contains("VENDA SOB PRESCRIÇÃO"),
"tarja sem o texto obrigatório"
}
check "Tinta na área de dobra" {
// Excesso de tinta na dobra causa problemas de acabamento
dobra = region(290, 0, 15, 842, "dobra central")
medida = prepress::calculate_tac_by_region(1, dobra)
assert medida.first() < 240,
"tinta demais na dobra: #{medida.first()}%"
}
check "Código de barras no lugar certo" {
area_codigo = region(400, 20, 180, 80, "área do código")
assert codes::validate_barcode_position(area_codigo),
"código de barras fora da área reservada"
}
}