Вы находитесь на странице: 1из 4

Perl • DESARROLLO

Archivo y acceso a PDFs

EL BIBLIOTECARIO
Este mes aprenderemos a ubicar artículos en archivos PDF protegidos y cómo usar una base de datos para

acceder a estos artículos más tarde. POR MICHAEL SCHILLI

N
o importa si se trata de un entre- Después de escanear, guardamos cada Esto crea un registro con el nombre de la
tenido artículo del periódico página como un fichero PNG. Una reso- publicación (“New Yorker”), el título del
local o de un excelente análisis lución de 200dpi será suficiente para documento (“La tortura del
político del New Yorker: pueden ser una mantener el texto legible. Usaremos el Outsourcing”), el número (“2005/02/
buena lectura para un día lluvioso, y programa convert en un truco sacado de 14”) y la página (106).
puede que queramos archivarlos de algu- [3] para unificar todas las páginas en un La información se guarda en una base
na manera para poder recuperarlos fácil- único documento PDF: de datos, que admite consultas SQL.
mente. Hemos empleado el motor de bases de
Desafortunadamente, estas joyas del convert -density 200 U datos SQLite. Si comprobamos la infor-
periodismo puede que no estén disponi- -quality 95 \ mación referida a su instalación com-
bles online, dado que muchos periódicos -resize "1600x1600>" U probaremos que sólo necesitamos unos
aún no tienen sus ediciones en la Web. *.png archivo.pdf pocos módulos CPAN y el script se
Mala cosa: el papel termina ocupando encargará de todo lo demás.
un montón de sitio en nuestro despacho, Este comando recopila los archivos *.png El script no copia el documento PDF,
y ¿realmente queremos llenar estanterías del directorio actual, limitando el ancho y archivo.pdf, a la base de datos, sino a un
y estanterías de carpetones? alto a 1600 píxeles. Las imágenes de directorio donde se guardan todos los
menor tamaño quedarán intactas debido archivos bajo una secuencia numérica
Escanear y Comprimir al signo > utilizado. convert unifica las (000001, 000002,…). Si añadimos nues-
Una alternativa es digitalizar los artícu- páginas en un PDF a una resolución de tro artículo del New Yorker a la inmacu-
los de nuestro interés con un escáner y 200dpi. Los archivos PNG se comprimen lada base de datos, se creará un nuevo
guardarlos en discos como archivos PDF. a JPG dentro del PDF conservando una documento denominado “000001”, que
Para poder llevar la cuenta de los archi- calidad del 95% respecto del original. contiene el PDF.
vos adecuadamente en una colección Nuestro segundo documento será un
que crece continuamente, presentamos Dentro del Archivo artículo de un periódico local que relata
este mes magsafe, un script en Perl con Después de escanear las once páginas una reyerta en un pub Londinense:
el que administrar una base de datos de del excelente artículo “La tortura del
archivos escaneados (el listado completo Outsourcing” del New Yorker, escrito magsafe -m U
de este script se puede bajar de [1]). por Jane Mayer, y haberlo pasado a "Southwest London Inquirer"
Dos o tres páginas se escanean rápida- archivo.pdf, realizamos la siguiente lla- -t "Pintas voladoras"
mente con la excelente interfaz gráfica mada al script magsafe para registrarlo: -i 2005/02 -p 4 -d U
xsane, del proyecto Sane [2], que es archivo.pdf
compatible con casi cualquier escáner a magsafe -m "New Yorker" -a U
través de su back-end. Tanto los escáne- "Jane Mayer" Como se puede comprobar, no es obliga-
res Epson como los HP All-in-One -t "La tortura del Outsourcing" torio incluir el nombre del autor. De
Officejet funcionaron a la perfección -i 2005/02/14 -p 106 -d U hecho, los autores de muchos artículos
bajo Linux en nuestro laboratorio. archivo.pdf en prensa son anónimos.

WWW.LINUX- MAGAZINE.ES Número 08 49


DESARROLLO • Perl

Si ejecutamos magsafe sin especificar La siguiente línea busca los artículos con
ningún parámetro, el script nos requerirá la palabra Pintas en el título:
más información de la siguiente forma:
$ magsafe -s title:Pintas
$ magsafe "Pintas voladoras", U
[1] New Unknown, Southwest London U
[2] New Yorker Inquirer, 2005/02,
[3] Southwest London Inquirer 4, /DATA/DOCS/000002
Magazine [1]>
Internamente, magsafe genera una con-
Como hemos introducido previamente sulta SQL, después de añadir la cadena
dos artículos, magsafe tomó nota de los de búsqueda entre caracteres de tanto
nombres de las publicaciones y ahora por ciento. Supongamos que estamos
las muestra por pantalla en un menú. buscando artículos publicados en 2005,
Para añadir una nueva publicación, sólo es decir, con un valor de “2005” en el
tenemos que presionar 1 y teclear el campo issue. En este caso, introduciría-
nombre: mos:

Magazine [1]> 1 magsafe -s issue:2005


Enter Magazine Name []> The Sun Figura 1: Escaneo de un artículo con xsane.
Document []> ... Podríamos restringir adicionalmente la
consulta a artículos del “Southwest crear una nueva base de datos con dos
magsafe copia el documento PDF especi- London Inquirer”: tablas. La Figura 2 muestra el esquema.
ficado por la opción -d (o introducido La tabla doc contiene una línea para
interactivamente como acabamos de ver) magsafe -s "issue:2005 U cada documento que guardemos. La línea
a un directorio codificado, le asigna un mag:Southwest" incluye el título y autor del artículo, el
número correlativo a cada documento número, la página y el nombre de la publi-
(000001, 000002,…) y referencia la ruta Base de Datos Abstracta cación de la cual se sacó el artículo. La
hasta este archivo en la base de datos. Hemos visto la abstracción de base de mayoría de la gente no lee demasiadas
datos Class::DBI que usa magsafe en publicaciones, pero lo hacen regularmen-
Una Aguja en un Pajar otras ocasiones en la sección de Perl. En te. El diseño de la base de datos sería
Usar una base de datos tiene la ventaja esta ocasión, vamos a hacer las cosas pobre si guardáramos el nombre completo
de poder buscar los datos de la manera incluso más fáciles. El módulo de la publicación en cada línea. En vez de
que más se ajuste a lo que necesitemos. Class::DBI::Loader simplifica las defini- esto, la tabla doc tiene un campo mag con
Para nuestra base de datos de periódicos, ciones de clase para Class::DBI, pues una identificación numérica que apunta a
que hemos guardado en un archivo analiza el diseño de la base de datos y la línea en la tabla mag, que tiene el iden-
denominado doc_escaneados.dat, es genera clases de abstracción y sus rela- tificador asociado al nombre completo de
muy fácil hacerlo desde la línea de ciones por referencia. la publicación.
comandos con sqlite3 y unas mínimas El listado magsafe muestra cómo se La tabla de definiciones que sigue a
nociones del viejo SQL: implementa. El módulo Getopt::Std ana- línea 147 en magsafe es un homenaje al
liza las opciones de línea de comandos estándar SQL, incluyendo la línea:
$ sqlite3 doc_escaneados.dat que acepta magsafe. La línea 29 busca -
sqlite> SELECT * from doc where a, -m, -t, etc. Los dos puntos en el forma- mag INT REFERENCES mag,
title like '%Pintas%' to de la cadena establecen que un pará-
2|Pintas voladoras U metro debe seguir a cada opción marca- en la tabla doc, que tiene una pinta algo
||2|4|2005/02 da de esta forma. getopts guarda los inusual. Esta es una manera de indicar que
CTRL-D valores en un hash, %o, el cual se proce- la columna mag apunta a la tabla mag, y
$ sa más tarde. también de establecer la relación entre el
La línea 33 comprueba si existe el identificador de la publicación y el título.
Podríamos objetar que el proceso es algo directorio del documento (puede estar Las primeras dos columnas en ambas
incómodo. magsafe nos ofrece un legua- vacío), y se puede escribir en él. Si no es tablas son identificaciones numéricas, que
je simplificado de consultas como res- así, se tendrá que crear el directorio se etiquetan como claves primarias. El
puesta. antes de iniciar el programa. constructor de clases Class::DBI::Loader
Si usamos el parámetro -s, tendremos La función db_init() de la línea 36 se llamado en la línea 40 necesita que le asig-
que especificar la cadena de búsqueda asegura de que los usuarios no tienen nen indentificadores únicos a los objetos
con el siguiente formato: que preocuparse de los detalles de la que constituyen las líneas de la tabla. A
base de datos. Si la base de datos no las filas nuevas se les asigna automática-
"campo:patrón campo: U existe, db_init() usa unas pocas líneas mente nuevos identificadores incremen-
patrón ..." SQL, que empiezan en la línea 147, para tando el último que se usó.

50 Número 08 WWW.LINUX- MAGAZINE.ES


Perl • DESARROLLO

La siguiente línea: y mag y las enlace. Gracias a la condi-


ción REFERENCES de SQL, entiende que
namespace => "Scanned::DB" la columna mag de la tabla doc es sim-
plemente una clave externa, que se usa
en la llamada al constructor del cargador para hacer un JOIN con la tabla mag.
de base de datos se asegura de que las
clases de abstracción de tablas son visi- Desde la Línea de
bles en Perl en el espacio de nombres de Comandos
Scanned::DB. En la línea 56 el script comienza a proce-
Después de la llamada sar los parámetros de la línea de coman-
Class::DBI::Loader->new(), el método dos. Si no se pone la -s, el usuario no
find_class() va a buscar los objetos que busca una entrada de la base de datos,
representan las tablas, como se ve en las sino que introducirá una nueva línea.
líneas 50 y 52, proporcionando la tabla Las líneas de la 59 a la 67 aceptan valo-
de nombres como argumentos. $docdb, res de varias opciones de la línea de
objeto de Scanned::DB::Doc type, apunta comandos. Si una o más opciones no se
a la tabla de documentos doc, mientras fijan, la función ask() (proporcionada
que $magdb (Scanned::DB::Mag) apunta por el módulo Sysadm::Install) muestra Figura 2: Esquema de la base de datos
a la tabla de publicaciones, mag. al usuario los valores que faltan. El SQLite.
Para permitir que los objetos cumplan campo del autor es opcional y no se pre-
con las consultas estándar Class::DBI y gunta por el. porcionan métodos para acceder a los
también condiciones WHERE más com- Seleccionar una publicación es un campos individuales en los registros: por
plicadas, el parámetro additional_classes poco mas complejo y usa la función ejemplo, $obj-> name() devuelve el
en la línea 43 realiza una petición al mag_pick definida en la línea 169 y nombre de la publicación, que se guarda
paquete Class::DBI::Abstract-Search. siguientes. El método retrieve_all() del en la columna name de la tabla mag.
El parámetro relationships en la línea objeto tabla de base de datos $magsdb Si $picked no está activado, es decir, si la
46 le indica a Class::DBI::Loader que lee todas las filas existentes en la tabla opción en la línea de comandos correspon-
analice las relaciones entre las tablas doc mag. Los objetos de datos devueltos pro- diente a la publicación está vacía, la línea
DESARROLLO • Perl

179 usa la función pick() (también de revistas resultantes de la petición de bús- Instalación
Sysadm::Install) para ofrecer al usuario ele- queda, y la entrada de hash asignada a la El script necesita los módulos DBI,
gir de un menú de los nombres de las revis- clave “mag” apunta a un array que con- Class::DBI, Class::DBI::SQLite,
tas. Si el usuario elige la primera entrada, tiene esos identificadores como elemen- Class::DBI::Abstract-Search y
New, la línea 181 invalida la selección y la tos. DBD::SQLite para la abstracción, todos
línea 186 permite al usuario introducir el Por otro lado, si un usuario busca por ellos disponibles desde CPAN.
nombre de una nueva publicación, que otra columna diferente de mag, se habili- Adicionalmente, necesitaremos
será mostrada en la lista la próxima vez. El ta la rama else en la línea 103 y la clave Text::Iconv y Sysadm::Install.
método find_or_create() crea entonces una de búsqueda se coloca entre signos de Si preferimos usar el cliente desde la
nueva entrada en la tabla mag, o bien porcentaje y se asigna al nombre de línea de comandos sqlite3 para investigar
encuentra una revista existente. columna clave en el hash %search. la base de datos SQL manualmente, ten-
De esta manera, la variable $mag en El contenido del hash %search es dremos que descargar el tarball desde
la línea 57 representa una revista exactamente lo que espera el método [4], compilarlo e instalarlo desde allí.
nueva o existente. Como search_where(). Se llama en la línea 108 Las versiones 1 y 2 de Sqlite no funcio-
Class::DBI::Loader hizo el trabajo pre- y devuelve líneas que cumplen todas las narán, dado que DBD::SQLite se basa en
viamente y analizó las relaciones entre condiciones especificadas por el hash. Si sqlite 3.x que no es compatible con las
las tablas doc y mag, gracias a las ban- el parámetro de comparación cmp está bases de datos generadas usando otras
deras relationships, ahora la línea 69 puesto en “like” en las opciones adicio- versiones de SQlite.
puede, sin más, llamar a nales del hash, search_where() no busca Tenga cuidado: si estamos usando
$mag->add_to_docs() para añadir un coincidencias literales, sino por patrones bases de datos SQLite para otras aplica-
artículo a la tabla doc y permitir a su con comodines dados como %, cum- ciones con versiones anteriores del
columna mag que apunte a la publica- pliendo con el estándar SQL. módulo DBD::SQLite y queremos seguir
ción de la tabla mag. El método El comando print de la línea 113 se usándolos, necesitaremos hacer una con-
add_to_docs() del objeto revista no llama para cada objeto que se ha encon- versión al nuevo formato SQLite antes de
está definido explícitamente en magsa- trado, activado por la subsecuente cláu- hacer la actualización:
fe. Se genera automáticamente en la sula for @objs.
capa de abstracción de base de datos sqlite OLD.DB .dump | U
DBI::Class. Caracteres no Estándar sqlite3 NEW.DB
Para copiar el documento PDF al direc- SQLite3 espera que todas las cadenas
torio de documentos, magsafe llama a la estén en UTF-8. En caso de que estemos Una vez que hayamos instalado la última
función cp desde Sysadm::Install en la guardando los títulos con caracteres versión del módulo DBD::SQLite, no
línea 83. La función docpath() definida acentuados, con ISO 8859-1, no funcio- podremos leer bases de datos creadas
en la línea 129 devuelve la futura ruta nará. Algunas distribuciones más nuevas con versiones anteriores.
completa del archivo. Para ello, simple- de Linux tienen sus terminales en UTF-8, La variable $DOC_DIR define el direc-
mente convierte la identificación a un mientras que las más antiguas usan ISO torio de documentos en la línea 27 del
entero de seis dígitos rellena con ceros y 8859-1 con más frecuencia. Debemos script magsafe. Adicionalmente, la varia-
anexa el documento al directorio. comprobar la variable de entorno LANG. ble $DB_NAME en la línea 16 establece
Si está fijada a en_US.UTF-8, por ejem- el nombre del archivo de base de datos
Más Fácil que SQL plo, nuestras terminales usan UTF-8. SQLite. Este directorio debe existir antes
En caso de petición de búsqueda, la línea Si la variable $UTF8_TERM de la línea de iniciar el programa y se debe poder
89 itera sobre los pares column:pattern, 20 está fijada a un valor permitido, el escribir a él.
que proceden de la opción -s y separados script interpretará toda la entrada como Ahora, los entusiastas de los medios
por espacios en blanco. La línea 92 separa UTF-8 y no acometerá una conversión. Si escritos pueden escanear artículos inte-
entonces el nombre de la columna de la $UTF8_TERM es 0, magsafe dará por resantes de las revistas que leen y dejar
palabra clave de búsqueda. Si el nombre supuesto que los datos de entrada están éstas en el contenedor de reciclaje.
de la columna es mag, la línea 96 busca en ISO 8859-1 y convertirá todo a UTF-8 Imáginese todo lo que se puede hacer
primero una revista coincidente adosán- antes de situarlos en la base de datos. con el espacio que dejaremos libre. ■
dole caracteres de porcentaje y llamando a Si se necesita la conversión, magsafe
search_like() en la tabla mag. Esto puede recurrirá al módulo CPAN Iconv. La RECURSOS
no devolver ninguna revista, una o línea 23 crea un objeto Text::Iconv para
muchas en @mags. El método id() convertir de ISO-8859-1 a UTF-8. [1] Descarga de magsafe.pl: https://www.
encuentra los identificadores de las revis- Adicionalmente llama a raise_error() linux-magazine.es/Magazine/
tas coincidentes, dejando a la línea 99 que con el valor 1, para forzar una excep- Downloads/08
guarde una tupla tal que: ción en caso de error. El método con- [2] http://www.sane-project.org
vert() convierte cualquier cadena que [3] PDF Hacks, por Sid Steward, O’Reilly
"mag" => [$id1, $id2, ...] le pasemos de una codificación a la 2004
otra. El módulo Encode, para aquellos [4] Página Web de SQLite, http://www.
en el hash %search. $id1, $id2, etc… son que tengan Perl 5.8.x o posterior, es sqlite.org
los identificadores numéricos para las otra alternativa.

52 Número 08 WWW.LINUX- MAGAZINE.ES

Вам также может понравиться