4. Espacio de nombres struct:: — estructura y metadatos

text:: · Índice · Siguiente: visual::

23 funciones sobre el archivo en sí: metadatos, objetos internos, seguridad y trazabilidad.

Las funciones a partir de list_objects leen la estructura interna del archivo. Ese análisis se ejecuta una sola vez, en el primer uso, y queda en caché.


4.1 Metadatos

Lectura directa

Función Devuelve
struct::get_title() Título
struct::get_author() Autor
struct::get_subject() Asunto
struct::get_keywords() Palabras clave
struct::get_creator() Programa que creó el documento original
struct::get_producer() Programa que generó el PDF

Todas devuelven una cadena vacía cuando el campo no existe.

check "Metadatos obligatorios" {
  assert struct::get_title() != "", "PDF sin título"
  assert struct::get_author() != "", "PDF sin autor"

  // Producer revela la herramienta de origen: útil para rastrear problemas
  print("generado por:", struct::get_producer())
  print("creado en:", struct::get_creator())
}

check "Origen de confianza" {
  // Algunos flujos solo aceptan PDF de herramientas homologadas
  productor = struct::get_producer()
  assert productor.contains("Adobe") || productor.contains("Ghostscript"),
    "PDF generado por una herramienta no homologada: #{productor}"
}

struct::get_creation_date() y struct::get_modification_date()

Fechas ya convertidas del formato interno del PDF (D:20260802173622-03'00') a AAAA-MM-DD HH:MM:SS.

check "Fechas del archivo" {
  creacion = struct::get_creation_date()
  assert creacion != "", "PDF sin fecha de creación"
  print("creado en:", creacion)
  print("modificado en:", struct::get_modification_date())

  // La comparación de texto funciona porque el formato es ordenable
  assert creacion > "2026-01-01", "archivo demasiado antiguo para esta campaña"
}

struct::list_metadata_entries()

Lista con todas las entradas rellenadas, con el formato "Clave: valor".

check "Inventario de metadatos" {
  entradas = struct::list_metadata_entries()
  print("metadatos:", entradas.join(" | "))
  require entradas.length >= 2
}

struct::extract_xmp()

Metadatos XMP (XML) del catálogo. Cadena vacía si no los hay.

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

  // XMP es XML: se pueden buscar campos concretos
  assert xmp.contains("pdfaid"), "sin identificación PDF/A en el XMP"
  print("el XMP tiene", xmp.length, "caracteres")
}

4.2 Archivo y trazabilidad

struct::file_size()

Tamaño en bytes.

check "Tamaño para enviar por correo" {
  mb = struct::file_size() / 1024 / 1024
  assert mb < 10, "archivo de #{round(mb)} MB (límite de 10 MB por correo)"
}

struct::calculate_sha256()

Hash SHA-256 del archivo: la huella digital para la traza de auditoría.

check "Registro de auditoría" {
  // El hash entra en el informe y prueba qué archivo exacto se aprobó
  hash = struct::calculate_sha256()
  print("SHA-256:", hash)
  require hash.length == 64
}

struct::detect_file_bloat([kb_por_pagina])

Verdadero si el archivo está «hinchado», por encima del límite de KB por página (1024 por defecto).

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

  // Límite más estricto para publicación web
  assert !struct::detect_file_bloat(200),
    "demasiado pesado para publicación web"
}

4.3 Objetos internos

struct::count_objects()

Cantidad de objetos de contenido (texto, imagen, filete) en las páginas.

check "El documento no está vacío" {
  require struct::count_objects() > 0
  print("objetos de contenido:", struct::count_objects())
}

struct::list_objects()

Lista todos los objetos del archivo con el formato "número: tipo".

check "Inventario del archivo" {
  objetos = struct::list_objects()
  print("total de objetos:", objetos.length)

  // ¿Cuántos son fuentes?
  fuentes = objetos.filter { |o| o.contains("Font") }
  print("objetos de fuente:", fuentes.length)
}

struct::detect_unreferenced_objects()

Objetos a los que no se llega desde el tráiler: peso muerto en el archivo.

Los objetos de infraestructura (ObjStm, XRef) no entran en la lista: por definición el tráiler nunca los referencia, y avisarlos sería una falsa alarma.

check "Sin basura en el archivo" {
  sueltos = struct::detect_unreferenced_objects()
  assert sueltos.length == 0,
    "#{sueltos.length} objeto(s) sin referenciar: #{sueltos.join(", ")}"
}

struct::detect_orphaned_resources()

Como el anterior, pero solo recursos (fuentes, imágenes, XObjects): el tipo de sobra que más pesa en el archivo.

check "Recursos huérfanos" {
  huerfanos = struct::detect_orphaned_resources()
  assert huerfanos.length == 0,
    "recursos incrustados sin usar: #{huerfanos.join(", ")} — ejecuta 'pdfl fix' con remove_unused_resources()"
}

struct::measure_object_size(numero)

Tamaño aproximado de un objeto concreto, en bytes.

check "El objeto más pesado" {
  // Combinándolo con list_objects para investigar qué ocupa espacio
  struct::list_objects().each { |entrada| print(entrada) }
  print("tamaño del objeto 5:", struct::measure_object_size(5), "bytes")
}

4.4 Seguridad

struct::detect_javascript()

Verdadero si hay JavaScript incrustado en el PDF.

check "Sin código ejecutable" {
  // El JavaScript en un PDF es un vector de ataque habitual e innecesario
  // en documentos de producción gráfica
  assert !struct::detect_javascript(),
    "el PDF contiene JavaScript incrustado"
}

struct::detect_suspicious_actions()

Lista las acciones de riesgo encontradas: JavaScript, Launch (ejecuta un programa), URI, SubmitForm, ImportData, GoToR, cada una con el objeto de origen.

check "Acciones del documento" {
  acciones = struct::detect_suspicious_actions()
  assert acciones.length == 0,
    "acciones sospechosas en el PDF: #{acciones.join("; ")}"
}

check "Solo los enlaces son aceptables" {
  // Si se permiten enlaces externos, filtra solo lo que preocupa
  peligrosas = struct::detect_suspicious_actions().filter { |a|
    a.contains("Launch") || a.contains("JavaScript")
  }
  assert peligrosas.length == 0, "acciones peligrosas: #{peligrosas.join("; ")}"
}

struct::check_encryption()

Verdadero si el documento está cifrado.

check "Archivo abierto para producción" {
  // Un PDF cifrado puede fallar en el RIP de la imprenta
  assert !struct::check_encryption(),
    "PDF cifrado — quita la protección antes de mandarlo a imprimir"
}

struct::validate_permissions()

Verdadero si no hay restricciones de permisos (documento libre para procesar).

check "Permisos" {
  assert struct::validate_permissions(),
    "PDF con restricciones de permisos que pueden impedir el procesamiento"
}

struct::validate_signatures()

Verdadero si hay campos de firma digital en el documento.

Esta función detecta la presencia de los campos. La validación criptográfica de la cadena de certificados no se hace en esta versión.

check "Documento firmado" {
  assert struct::validate_signatures(),
    "documento sin campo de firma digital"
}

4.5 Ejemplo completo

// auditoria.pdfl — comprobación de conformidad y seguridad
profile "auditoria-de-archivo" {

  check "Identificación" tags: ["metadatos"] {
    assert struct::get_title() != "", "sin título"
    assert struct::get_author() != "", "sin autor"
    assert struct::get_creation_date() != "", "sin fecha de creación"
    print("documento:", struct::get_title())
    print("generado por:", struct::get_producer())
  }

  check "Trazabilidad" tags: ["auditoria"] {
    // El hash prueba exactamente qué archivo se validó
    print("SHA-256:", struct::calculate_sha256())
    print("tamaño:", struct::file_size() / 1024, "KB")
  }

  check "Seguridad" tags: ["seguridad"] {
    assert !struct::detect_javascript(), "JavaScript incrustado"
    assert !struct::check_encryption(), "archivo cifrado"
    acciones = struct::detect_suspicious_actions()
    assert acciones.length == 0, "acciones sospechosas: #{acciones.join("; ")}"
  }

  check "Higiene del archivo" tags: ["optimizacion"] {
    huerfanos = struct::detect_orphaned_resources()
    assert huerfanos.length == 0, "recursos sin usar: #{huerfanos.join(", ")}"
    assert !struct::detect_file_bloat(1024), "archivo hinchado"
  }
}

text:: · Índice · Siguiente: visual::