Banner

Ultima revisión 23/10/2012

Indexar documentos PDF en una página web

Uno de los problemas más comunes que tiene un desarrllador web cuando se dispone a crear un buscador que lleva documentos indexados es buscar las coincidencias de la clave de búsqueda dentro de esos documentos adjuntos o indexados.

Google, por ejemplo, indexa varios tipos de documentos y entre ellos, por supuesto, los PDF que por cierto son los más comunes.

El proceso para poder realizar la indexación es, en primer lugar, extraer el texto y, segundo guardar dichos textos en la base de datos o en archivos de texto plano. Es en el primer paso, claro, en dónde se encuetran los problemas.

Para extraer el texto de los PDF's existen varios caminos:

Extracción del texto mediante la clase de PHP "class.pdf2text.php"

Esta clase es interesante porque nos permite extraer textos de ficheros PDF e ignora todo aquello que no esté como texto en una capa y, además, soporta ASCIIHexDecode, ASCII85Decode, FlateDecode:

Funciona bastante bien y realiza la transformación a texto plano de forma más o menos rápida.

Podéis descargar la librería desde Google Code pulsando aquí

La forma de usarla es:

$a = new PDF2Text();
$a->setFilename($_GET['q']);
$a->decodePDF();
$contentIndexed.=$a->output()."\n\n";

$_GET['q'] Es el parámetro q pasado por URL y se corresponde con la dirección relativa del fichero PDF del que se va a extraer el texto a indexar.

Hemos estado probando la librería con 16 PDF's distintos y podemos afirmar que funciona bastante bien.

Extracción del texto mediante XPDF

Xpdf es un visor de código abierto para PDF's. El proyecto Xpdf también incluye un extractor de texto desde PDF, un conversor de PDF a PostScript, y otras pequeñas utilidades.

Se ejecuta bajo el sistema de ventanas X en UNIX, VMS u OS/2. Los componentes no-X (pdftops, pdftotext, ...) también se ejecutan en sistemas Windows y si se descarga el código fuente, éste, debería funcionar bien en casi cualquier sistema que tenga un compilador C++ decente. El conjunto de herramientas pesa alrededor de 47 MB en versión Linux y 17MB en su versión Windows.

Está diseñado para ser pequeño y eficiente y puede utilizar fuentes Type 1, TrueType o estándar X.

Podeis descargar las versiones compiladas desde Foolabs pulsando aquí.

La forma de usarla desde PHP es:

$contentIndexed .= shell_exec('pdftotext32 -layout -raw -eol dos '.utf8_decode($_GET['q']).' -');

$_GET['q'] Es el parámetro q pasado por URL y se corresponde con la dirección relativa del fichero PDF del que se va a extraer el texto a indexar.

Indexar los textos extraídos desde un PDF

Se han hecho algunas pruebas para buscar un texto dentro de los PDFs sin hacer ninguna indexación y, lamentablemente, hablar de tiempos con este método resulta tanto inapropiado como inaceptable.

Por eso resulta inevitable tener que indexar los contenidos de los PDF y por eso, por ejemplo, google lo hace.

Dicho esto y una vez que ya  tenemos el contenido de nuestro PDF, es el momento de meterlo en la base de datos o guardarlo en formato de texto plano.

Si queremos hacerlo en forma de base de datos nuestra recomendación es que se cree una tabla tipo para cada archivo y que contenga como campos id, num_pagina y contenido.

Luego otra tabla que contenga los campos id, nombre_tabla y nombre_archivo. Esta tabla será el enlace entre el archivo PDF y la tabla contenedora.

El motivo es simple, buscar y recuperar datos por página es más eficiente que por el contenido completo y localizar el nombre del archivo y su tabla o viceversa es inmediato. Además crear y gestionar  los índices full-text es más rápido, limpio y tiene mayor rendimiento.

La tablas por tanto quedarían asi:

CREATE TABLE `pdfFiles` (
    `id` int(10) unsigned NOT NULL,
    `nombre_archivo` char(255) default NULL,
    `nombre_tabla` char(50) NOT NULL,
    PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE `` ( /* Creada en tiempo de ejecución con el nombre del campo nombre_tabla de pdfFiles */
    `id` int(10) unsigned NOT NULL auto_increment,,
    `num_pagina` int(10) unsigned NOT NULL,
    `contenido` longtext NOT NULL,
    PRIMARY KEY  (`num_pagina`,`id`),
    FULLTEXT KEY `contenido_index` (`contenido`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


Luego otra cosa que podemos hacer es limpiar los contenidos extraídos realizando las siguientes operaciones.

// Cambiamos los saltos de página por patrones.
$contentIndexed = ereg_replace(chr(12), "\r\n{new_page}\r\n", ereg_replace("\n", "\r\n", $contentIndexed));

// Enumeramos los patrones por el número de página que corresponde.
$arrContent = explode("\r\n{new_page}\r\n", $contentIndexed);
$arrayPaginas = $arrContent; // Lo guardamos porque luego lo usaremos para guardar el archivo o en base de datos.
$contentIndexed2 = "{1}";
$xCount = 2;

foreach($arrContent as $page){
    $contentIndexed2 .= trim($page)."\r\n{".$xCount."}\r\n";
    $xCount++;
}
$contentIndexed = $contentIndexed2;

// Recorremos las líneas del archivo y eliminamos las que tengan básicamente números por no ser interesantes ya que normalmente
// son "indicadores de nueva página" Líneas como 10, [102] o {100}, se eliman ahora.
$arrContent = explode("\n", $contentIndexed);
$contentIndexed2 = "";
foreach($arrContent as $line){
    $pattern = '/[0-9]+/is';
    $coincidecia = preg_replace($pattern, '', $line);
    if(trim($coincidecia) != "" && trim($coincidecia) != "[]") {
        $contentIndexed2 .= trim($line)."\r\n";
    }
    $xCount++;
}

$contentIndexed = $contentIndexed2;

// Guardamos el archivo txt en disco.
$fp = fopen(str_replace(".pdf", "", $_GET['q']).'.txt', 'w');
fwrite($fp, $contentIndexed);
fclose($fp);

// Realizamos los INSERT correspondientes en la base de datos
$xCount = 0;
foreach($arrayPaginas as $page){
    $xCount++;
    $query = "INSERT INTO  (num_pagina, contenido) VALUES ($xCount, $page); ";
    mysql_query($query);
}

Espero que haya sido de ayuda este pequeño tutorial.