Skip to content

feat: implementar extraccion optativa de urls de pdf#2

Open
diegocostares wants to merge 1 commit into
masterfrom
feature/pdf-url-extraction
Open

feat: implementar extraccion optativa de urls de pdf#2
diegocostares wants to merge 1 commit into
masterfrom
feature/pdf-url-extraction

Conversation

@diegocostares

Copy link
Copy Markdown

Problema

Actualmente, la gema utiliza pdftotext (parte de la suite poppler-utils) para extraer texto de archivos PDF. Esta herramienta es excelente para el análisis de layout visual: logra identificar y agrupar correctamente el texto en formatos complejos, como los currículums de múltiples columnas.

Sin embargo, pdftotext omite por diseño la lectura de diccionarios de hipervínculos (anotaciones /Annots y /URI en el estándar PDF), ya que un link no es texto "visible". Como consecuencia, se pierden las URLs embebidas (ej. un enlace en la palabra "LinkedIn"), afectando procesos posteriores que dependan de recolectar estos datos (como extraer perfiles sociales).

Solución omplementada

Dado que extraer URLs implica un costo de procesamiento secundario, la solución se diseñó bajo dos premisas: Responsabilidad Única (SRP) y Ejecución Optativa (para no penalizar a los usuarios que solo quieren el texto base).

  1. Plaintext::PdfUrlExtractor: Se creó una clase aislada responsable de ejecutar pdftohtml -xml silenciosamente. Su único trabajo es capturar el XML, buscar coincidencias con Regex para <a href="..."> y retornar un listado limpio y único de URLs.
  2. Plaintext::PdfHandler#text: Se hizo un override del método. Se sigue ejecutando pdftotext para obtener el texto con el layout intacto, pero si se pasa el flag extract_urls: true en el hash de opciones, se invoca al extractor y se apendan las URLs al final del texto con saltos de línea.

diagrama de secuencia

sequenceDiagram
    participant User
    participant Resolver as Plaintext::Resolver
    participant Handler as Plaintext::PdfHandler
    participant CLI1 as OS: pdftotext
    participant Extractor as Plaintext::PdfUrlExtractor
    participant CLI2 as OS: pdftohtml

    User->>Resolver: text(file, {extract_urls: true})
    Resolver->>Handler: text(file, options)
    
    %% Ejecución Principal (Layout)
    Handler->>CLI1: exec('/usr/bin/pdftotext file -')
    CLI1-->>Handler: retorna String (Texto Plano con Layout)
    
    %% Ejecución Secundaria Optativa (Links)
    opt if options[:extract_urls] == true
        Handler->>Extractor: extract()
        Extractor->>CLI2: exec('pdftohtml -xml -stdout file')
        CLI2-->>Extractor: retorna String (XML Crudo)
        Extractor->>Extractor: Ejecutar Regex (/href="([^"]+)"/)
        Extractor-->>Handler: retorna Array de Strings (URLs Únicas)
        Handler->>Handler: Concatenar Texto Principal + \n\n + URLs
    end
    
    Handler-->>Resolver: retorna String Final
Loading

Alternativas exploradas y por qué se descartaron

Durante el análisis previo a esta implementación, evaluamos otras alternativas que fueron descartadas por los siguientes motivos:

  1. Reemplazar pdftotext por pdftohtml completamente:
    • Por qué se descartó: pdftohtml es incapaz de respetar columnas. Lee el PDF de forma horizontal destructiva (izquierda a derecha, línea por línea). Si un CV tiene dos columnas ("Experiencia" a la izquierda y "Educación" a la derecha), pdftohtml mezcla las oraciones de ambas columnas, rompiendo por completo el parseo de IA o Regex aguas abajo. pdftotext es el único que respeta el layout.
  2. Añadir la gema pdf-reader (Enfoque Ruby Nativo):
    • Por qué se descartó: Implicaba agregar dependencias externas pesadas al gemspec. Considerando que el ecosistema poppler-utils ya viene con pdftohtml instalado (es del mismo creador que pdftotext), es mucho más ligero y nativo reutilizar las herramientas C++ existentes en el sistema operativo. Ademas, esto hacia que funcionara mucho mas lento. se trato de hacer ese enfoque en la webapp, pero para hacerlo mas eficiente, se llego a esta propuesta.

Cambios clave y validaciones

  • Nuevo Extractor: Implementación y aislamiento en lib/plaintext/pdf_url_extractor.rb.
  • Condicional en Handler: Uso de options[:extract_urls] en PdfHandler.
  • Rendimiento Comprobado: Ejecutar el comando extra añade una sobrecarga ínfima (~0.008 segundos), pero aún así se mantiene tras un flag (disabled by default) por purismo y retrocompatibilidad.
Captura de pantalla 2026-06-30 a la(s) 4 29 57 p m

Como `pdftotext` descarta los diccionarios de hipervínculos por diseño para enfocarse solo en layout visual,
se integra un extractor secundario aislado usando `pdftohtml -xml` (que también viene en poppler-utils)
para rescatar los enlaces. Este comportamiento es optativo (vía `extract_urls: true`) para no penalizar
el rendimiento de extracción estándar de los usuarios que solo quieren el texto.

Signed-off-by: diegocostares <dcosta@buk.cl>
@diegocostares diegocostares self-assigned this Jun 30, 2026
@diegocostares diegocostares force-pushed the feature/pdf-url-extraction branch from f9dc096 to 6b74cd3 Compare June 30, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant