4. Namespace struct:: — estrutura e metadados

text:: · Índice · Próximo: visual::

23 funções sobre o arquivo em si: metadados, objetos internos, segurança e rastreabilidade.

As funções a partir de list_objects leem a estrutura interna do arquivo. Essa análise roda uma única vez, no primeiro uso, e fica em cache.


4.1 Metadados

Leitura direta

Função Devolve
struct::get_title() Título
struct::get_author() Autor
struct::get_subject() Assunto
struct::get_keywords() Palavras-chave
struct::get_creator() Programa que criou o documento original
struct::get_producer() Programa que gerou o PDF

Todas devolvem string vazia quando o campo não existe.

check "Metadados obrigatórios" {
  assert struct::get_title() != "", "PDF sem título"
  assert struct::get_author() != "", "PDF sem autor"

  // Producer revela a ferramenta de origem — útil para rastrear problemas
  print("gerado por:", struct::get_producer())
  print("criado em:", struct::get_creator())
}

check "Origem confiável" {
  // Alguns fluxos só aceitam PDFs de ferramentas homologadas
  produtor = struct::get_producer()
  assert produtor.contains("Adobe") || produtor.contains("Ghostscript"),
    "PDF gerado por ferramenta não homologada: #{produtor}"
}

struct::get_creation_date() e struct::get_modification_date()

Datas já convertidas do formato interno do PDF (D:20260802173622-03'00') para AAAA-MM-DD HH:MM:SS.

check "Datas do arquivo" {
  criacao = struct::get_creation_date()
  assert criacao != "", "PDF sem data de criação"
  print("criado em:", criacao)
  print("modificado em:", struct::get_modification_date())

  // Comparação de texto funciona porque o formato é ordenável
  assert criacao > "2026-01-01", "arquivo antigo demais para esta campanha"
}

struct::list_metadata_entries()

Lista com todas as entradas preenchidas, no formato "Chave: valor".

check "Inventário de metadados" {
  entradas = struct::list_metadata_entries()
  print("metadados:", entradas.join(" | "))
  require entradas.length >= 2
}

struct::extract_xmp()

Metadados XMP (XML) do catálogo. String vazia se não houver.

check "XMP presente" {
  xmp = struct::extract_xmp()
  assert xmp != "", "PDF sem metadados XMP"

  // XMP é XML — dá para procurar campos específicos
  assert xmp.contains("pdfaid"), "sem identificação PDF/A no XMP"
  print("XMP tem", xmp.length, "caracteres")
}

4.2 Arquivo e rastreabilidade

struct::file_size()

Tamanho em bytes.

check "Tamanho para envio por e-mail" {
  mb = struct::file_size() / 1024 / 1024
  assert mb < 10, "arquivo com #{round(mb)} MB (limite de 10 MB para e-mail)"
}

struct::calculate_sha256()

Hash SHA-256 do arquivo — a impressão digital para trilha de auditoria.

check "Registro de auditoria" {
  // O hash entra no relatório e prova qual arquivo exato foi aprovado
  hash = struct::calculate_sha256()
  print("SHA-256:", hash)
  require hash.length == 64
}

struct::detect_file_bloat([kb_por_pagina])

Verdadeiro se o arquivo está "inchado" — acima do limite de KB por página (padrão 1024).

check "Arquivo enxuto" {
  assert !struct::detect_file_bloat(1024),
    "arquivo pesado: #{struct::file_size() / 1024} KB para #{doc.page_count} páginas"

  // Limite mais rígido para publicação web
  assert !struct::detect_file_bloat(200),
    "pesado demais para publicação web"
}

4.3 Objetos internos

struct::count_objects()

Quantidade de objetos de conteúdo (texto, imagem, traço) nas páginas.

check "Documento não está vazio" {
  require struct::count_objects() > 0
  print("objetos de conteúdo:", struct::count_objects())
}

struct::list_objects()

Lista todos os objetos do arquivo no formato "número: tipo".

check "Inventário do arquivo" {
  objetos = struct::list_objects()
  print("total de objetos:", objetos.length)

  // Quantos são fontes?
  fontes = objetos.filter { |o| o.contains("Font") }
  print("objetos de fonte:", fontes.length)
}

struct::detect_unreferenced_objects()

Objetos que não são alcançáveis a partir do trailer — peso morto no arquivo.

Objetos de infraestrutura (ObjStm, XRef) não entram na lista: eles nunca são referenciados pelo trailer por definição, e reportá-los seria alarme falso.

check "Sem lixo no arquivo" {
  soltos = struct::detect_unreferenced_objects()
  assert soltos.length == 0,
    "#{soltos.length} objeto(s) não referenciado(s): #{soltos.join(", ")}"
}

struct::detect_orphaned_resources()

Como o anterior, mas só recursos (fontes, imagens, XObjects) — o tipo de sobra que mais pesa no arquivo.

check "Recursos órfãos" {
  orfaos = struct::detect_orphaned_resources()
  assert orfaos.length == 0,
    "recursos embutidos sem uso: #{orfaos.join(", ")} — rode 'pdfl fix' com remove_unused_resources()"
}

struct::measure_object_size(numero)

Tamanho aproximado de um objeto específico, em bytes.

check "Objeto mais pesado" {
  // Combinando com list_objects para investigar o que ocupa espaço
  struct::list_objects().each { |entrada| print(entrada) }
  print("tamanho do objeto 5:", struct::measure_object_size(5), "bytes")
}

4.4 Segurança

struct::detect_javascript()

Verdadeiro se há JavaScript embutido no PDF.

check "Sem código executável" {
  // JavaScript em PDF é vetor de ataque comum e desnecessário
  // em documentos de produção gráfica
  assert !struct::detect_javascript(),
    "PDF contém JavaScript embutido"
}

struct::detect_suspicious_actions()

Lista ações de risco encontradas: JavaScript, Launch (executa programa), URI, SubmitForm, ImportData, GoToR — cada uma com o objeto de origem.

check "Ações do documento" {
  acoes = struct::detect_suspicious_actions()
  assert acoes.length == 0,
    "ações suspeitas no PDF: #{acoes.join("; ")}"
}

check "Só links são aceitáveis" {
  // Se links externos são permitidos, filtre só o que preocupa
  perigosas = struct::detect_suspicious_actions().filter { |a|
    a.contains("Launch") || a.contains("JavaScript")
  }
  assert perigosas.length == 0, "ações perigosas: #{perigosas.join("; ")}"
}

struct::check_encryption()

Verdadeiro se o documento está criptografado.

check "Arquivo aberto para produção" {
  // PDF criptografado pode falhar na RIP da gráfica
  assert !struct::check_encryption(),
    "PDF criptografado — remova a proteção antes de enviar para impressão"
}

struct::validate_permissions()

Verdadeiro se não há restrições de permissão (documento livre para processar).

check "Permissões" {
  assert struct::validate_permissions(),
    "PDF com restrições de permissão que podem impedir o processamento"
}

struct::validate_signatures()

Verdadeiro se há campos de assinatura digital no documento.

Esta função detecta a presença dos campos. A validação criptográfica da cadeia de certificados não é feita nesta versão.

check "Documento assinado" {
  assert struct::validate_signatures(),
    "documento sem campo de assinatura digital"
}

4.5 Exemplo completo

// auditoria.pdfl — verificação de conformidade e segurança
profile "auditoria-de-arquivo" {

  check "Identificação" tags: ["metadados"] {
    assert struct::get_title() != "", "sem título"
    assert struct::get_author() != "", "sem autor"
    assert struct::get_creation_date() != "", "sem data de criação"
    print("documento:", struct::get_title())
    print("gerado por:", struct::get_producer())
  }

  check "Rastreabilidade" tags: ["auditoria"] {
    // O hash prova exatamente qual arquivo foi validado
    print("SHA-256:", struct::calculate_sha256())
    print("tamanho:", struct::file_size() / 1024, "KB")
  }

  check "Segurança" tags: ["seguranca"] {
    assert !struct::detect_javascript(), "JavaScript embutido"
    assert !struct::check_encryption(), "arquivo criptografado"
    acoes = struct::detect_suspicious_actions()
    assert acoes.length == 0, "ações suspeitas: #{acoes.join("; ")}"
  }

  check "Higiene do arquivo" tags: ["otimizacao"] {
    orfaos = struct::detect_orphaned_resources()
    assert orfaos.length == 0, "recursos sem uso: #{orfaos.join(", ")}"
    assert !struct::detect_file_bloat(1024), "arquivo inchado"
  }
}

text:: · Índice · Próximo: visual::