Extracción de datos de un pdf desde Java


A veces  necesitamos  extraer información de texto procedente de  ficheros  en formato pdf   por ejemplo  para automatizar la extracción de determinada información  relevante que contengan o simplemente porque deseamos guardar la información editable  en  otro formato mas amigable. En realidad es realmente interesante    intentar   automatizar esta  tarea  pues así nos   evitamos  manipulaciones manuales   y tediosas lo cual seguramente nos hagan perder mucho tiempo  con la gran escasez de este elemento de la vida moderna del que  tampoco disponemos Vamos  a ver dos métodos  para hacerlo  usando el IDE  de  Eclipse y el lenguaje Java

Método 1; mediante un paso intermedio con conversión previa de los ficheros pdf a ficheros de texto

PDF to TXT Converter es una aplicación de Windows para convertir archivos pdf a archivos de formato de texto sin formato en modo batch. Este programa también admite la conversión de rango de páginas específicas a archivos txt  de modo que después de la conversión, obtendrá el texto editable del documento PDF original PDF to TXT Converter Esta  utilidad la podemos descargar   desde  aqui Hay una pequeña pega con este programa, pues dado que es shareware en la versión gratuita   tiene  bastantes limitaciones ,entre ellas que no se procesaran más de 200 documentos de un sola vez  ( si se intenta con más de esa cantidad   el programa pierde el control) Un punto a su favor es que permite convertir automáticamente directorios enteros  con contenido de ficheros pdf y de este modo no necesitamos seleccionar  uno a uno cuál de ellos queremos convertir ( pero no olvide que, a no ser que compre la version completa, solo debería contener como máximo 200  ficheros) Como vemos ,lo interesante de este programa  es que permite convertir los  ficheros pdf a texto  lo cual nos facilitara  procesar estos  muy fácilmente desde   java Ahora    vamos a   ver un ejemplo    cómo extraer  los metadatos de un documento  de tipo texto obtenido usando el programa anterior La idea es que a partir de los pdf convertidos previamente a txt , es que iremos  leyendo el contenidos de cada fichero txt e  interpretando las cadenas extraidas de los pdf para buscar  por ejemplo cuatro  meta-datos que necesitamos , haciendo  cuenta del orden en el que aparecen asi como las palabras justo anteriors: -meta1, -meta2 -meta3 -meta4: En esta búsqueda tiene sentido el número de orden en el que aparecen las cadenas anteriores a  la busqueda  pues según el orden en que aparezca corresponderá a un meta-dato u otro. En este caso meta2 hay que buscarlo dos veces , pero según este orden la cadena siguiente es meta2 o meta4 Veamos el ejemplo; //******************************************************* //BLOQUE PRINCIPAL PARA EXTRAER  4 METADATOS de un fichero //******************************************************* //abre el fichero Scanner in =null; DecimalFormat formateador = new DecimalFormat(«####################»); Integer contador=0; //solo procesaremos los ficheros con la extrension txt if (sub.matches(tipofichero)) try{ in = new Scanner(new FileReader(nombreFichero)); in.useLocale(Locale.ENGLISH); //lee el fichero palabra a palabra while (in.hasNext()) { //lee primera palabra String palabra = in.next(); caa=palabra; if (procesa==true) { //System.out.println(«Palabra:»+palabra+ «contador=»+contador); if (contador==1) {instalacion=palabra; } if (contador==5) {nif=palabra; } procesa=false; contador++; } if (caa.matches(«pedido:»)) { // System.out.println(«<<<Palabra pedido: ENCONTRADA:>>>»); procesan=true; contador++; } if (caa.matches(«instalación:»)) { //System.out.println(«<<<<Palabra instalación: ENCONTRADA:>>>>»);//fecha procesa=true; contador++; } if (caa.matches(«NIF/NIE/Pasaporte:»)) { // System.out.println(«<<<<Palabra NIF/NIE/Pasaporte: ENCONTRADA>>>>»); procesa=true; contador++; } //lee números while (in.hasNextDouble()) { // lee un double double d = in.nextDouble(); if (procesan==true) { //System.out.println(«Número:»+formateador.format(d)+»contador=»+contador); if (contador==3) {pedido=d; } if (contador==7) {linea=d; } procesan=false; contador++; } } } // while (in.hasNext()) //resumen de datos capturados del pdf convertidos en txt y capturada en 4 campos que necesitamos System.out.println(«Instalacion:»+instalacion); //fecha System.out.println(«Pedido:»+formateador.format(pedido)); System.out.println(«NIF:»+nif); System.out.println(«Telefono:»+formateador.format(linea)); ….  

Método 2  directo usando la Liberia ItextPDF

iText es una biblioteca Open Source para crear y manipular archivos PDFRTF, y HTML en Java. Fue escrita por Bruno Lowagie, Paulo Soares, y otros; está distribuida bajo la Affero General Public License. El mismo documento puede ser exportado en múltiples formatos, o múltiples instancias del mismo formato. Los datos pueden ser escritos a un fichero o, por ejemplo, desde un servlet a un navegador web. Más recientemente, ha sido extendida a una biblioteca PDF de propósito general, capaz de rellenar formularios, mover páginas de un PDF a otro, y otras cosas. Estas extensiones son a menudo mutuamente excluyentes. Una clase te permite rellenar en formularios, mientras una clase diferente e incompatible hace posible copiar páginas de un PDF a otro. El soporte de PDF de iText es, sin embargo, bastante extensivo. Esto soporta firmas basadas en PKI de PDF, cifrado de 40-bit y 128-bit, corrección de colores, PDF/X, gestión de colores por perfiles ICC, y es anfitriona de otras característica Podemos descargar el fichero itextpdf-5.1.0.jar  desde el respositorio de Maven   o de esta web  http://www.java2s.com/Code/Jar/i/Downloaditextpdf510jar.htm No debemos olvidar importar el jar desde el Ide  del  Eclipse   en propiedades del proyecto-->Java Build Path–>libraries –>Add External   JARs Este es un ejemplo  de captura sencilla   de como procesar un pdf directaeente import java.io.IOException; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.parser.PdfTextExtractor; public class prueba { /** * @param args */ public static void main(String[] args) { try { PdfReader reader = new PdfReader(«d:/ttttt.pdf»); System.out.println(«This PDF has «+reader.getNumberOfPages()+» pages.»); String page = PdfTextExtractor.getTextFromPage(reader, 1); System.out.println(«Page Content:\n\n»+page+»\n\n»); System.out.println(«Is this document tampered: «+reader.isTampered()); System.out.println(«Is this document encrypted: «+reader.isEncrypted()); reader.close(); } catch (IOException e) { e.printStackTrace(); } } } Es importante la sentencia String page = PdfTextExtractor.getTextFromPage(reader, 1);  porque si ponemos 0 o un valor superior daría error Como   gracias  a la librería podemos capturar la informacion del   fichero pdf y aplicar la clase Sacanne para analizar las cadenas     para buscar determinada  informacion  que queremos catalogar vamos a  ver   el ejemplo anterior  usando los mismos campos  y con la misma casuitica: Este seria el mismo ejemplo de la  primer parte  , pero usando directamente los fichero pdf sin convertir a pdf: import java.awt.Rectangle; import java.awt.print.PageFormat; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; import java.text.DecimalFormat; import java.util.List; import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.FileReader; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Scanner; import java.util.Locale; import java.io.FileNotFoundException; import java.nio.charset.StandardCharsets; import java.text.DecimalFormat; import java.io.StringReader; import java.util.Locale; import java.util.Scanner; import com.itextpdf.text.pdf.PdfReader; import com.itextpdf.text.pdf.parser.PdfTextExtractor; public class Main { /** * @param args */ public static void main(final String[] args) throws IOException { String instalacion=»»; String nif=»»; double pedido=0; double linea=0; boolean procesa=false; boolean procesan=false; String tipofichero=»pdf»; // solo tomaremos ficheros con esta extension String caa = substring(20); String ruta = «D:\temp\ficheros\»; String ficherox=»28348758018_20181201000002.pdf»; String fichero=ruta+ ficherox; System.out.println(«Fichero a analizar =» +ficherox); int numero=ficherox.length()-3; //calcula donde empieza la extension del fichero // System.out.println(«numero=»+numero); String sub =ficherox.substring( numero); //calcula la extension del fichero System.out.println(«Extension del fichero :»+sub); //abrimos el PDF PdfReader reader = new PdfReader(fichero); int pages = reader.getNumberOfPages(); System.out.println(«Este PDF tiene «+pages+» paginas.»); //empezamos la coversion a pdf String page = PdfTextExtractor.getTextFromPage(reader, 1); ////<———————————————–aqui da errror System.out.println(«Contenido del documento de la pagina «+pages); System.out.println(«\n\n»+page+»\n\n»); ///////////////////////////////////////////////////////////////////// //solo procesaremos los ficheros con la extrension pdf //////////////////////////////////////////////////////////////////// if (sub.matches(tipofichero)) /// try { //para procesar el texto lo copiamos a una cadena String nombreFichero = page ; //Fichero; System.out.println(«Convertido salida del conversor a un String «); //borra el scanner // Scanner in =null; //formatos de numeros DecimalFormat formateador = new DecimalFormat(«####################»); //inicializa conatdor int contador=0; //in = new Scanner(new FileReader(nombreFichero));///AQUI ESTA EL PROBLEMA PUES SCANNER NO EST ACEPTADNO EL STRING Scanner in = new Scanner(nombreFichero); in.useLocale(Locale.ENGLISH); System.out.println(«Es correcto el pdfl Convertido salida del conversor a un String «); //lee el fichero palabra a palabra while (in.hasNext()) //leemos toda la cadena { //lee primera palabra String palabra = in.next(); caa=palabra; if (procesa==true) { //System.out.println(«Palabra:»+palabra+ «contador=»+contador); if (contador==1) {instalacion=palabra; } if (contador==5) {nif=palabra; } procesa=false; contador++; } if (caa.matches(«pedido:»)) { // System.out.println(«<<<Palabra pedido: ENCONTRADA:>>>»); procesan=true; contador++; } if (caa.matches(«instalación:»)) { //System.out.println(«<<<<Palabra instalación: ENCONTRADA:>>>>»);//fecha procesa=true; contador++; } if (caa.matches(«NIF/NIE/Pasaporte:»)) { // System.out.println(«<<<<Palabra NIF/NIE/Pasaporte: ENCONTRADA>>>>»); procesa=true; contador++; } //lee números while (in.hasNextDouble()) { // lee un double double d = in.nextDouble(); if (procesan==true) { //System.out.println(«Número:»+formateador.format(d)+»contador=»+contador); if (contador==3) {pedido=d; } if (contador==7) {linea=d; } procesan=false; contador++; } } } // while (in.hasNext()) in.close(); //resumen de datos capturados del pdf convertido en txt System.out.println(«Instalacion:»+instalacion); //fecha System.out.println(«Pedido:»+formateador.format(pedido)); System.out.println(«NIF:»+nif); System.out.println(«Telefono:»+formateador.format(linea)); //fin de metatados de un documento INDIVIDUAL en el xml } } private static String substring(int i) { // TODO Auto-generated method stub return null; }//CLASS }     Con esta librería como vemos  podemos procesar  de un modo relativamente sencilllo   cualquier contenido de un fichero  pdf    de un modo muy potente  como acabamos de ver .

Como eliminar la luz residual de las luminarias con leds


Puede  que al realizar una pequeña instalación en la que hacemos uso de luminaria basada en diodos  LED nos hayamos  topado con un curioso fenómeno  que al pagar esta   queda una levísima  iluminación que solo se  advierte claramente si nos quedamos a oscuras.

Este fenómeno que no ocurre no existe con las luminarias incandescentes se explica  porque los leds llegan a lucir (aunque muy débilmente) con corrientes del orden de pocos microAmp , lo cual ya es suficiente  de hecho para que a  podemos observar una débil luz( similar al efecto de ver carteles fluorescentes  que con la luz que destellan)

Con esta anomalía  podemos tener la sensación molesta de que estamos ante un relativamente molesto  gasto de suministro eléctrico ,si bien es mínimo   dado la mínima corriente que los atraviesa  y que por tanto consumimos

Es cierto que hay personas que no les molesta en absoluto este efecto pues por la noche gracias a «ese defecto» podemos ver lo justo para ir a cualquier parte de la casa sin encender la luz en plena oscuridad..¿pero y si deseamos   que no se encienda en absoluto? Pues veamos como solucionarlo

 

Cables y derivaciones en lamparas de sobremesa o de pie

Antes de conectar o desconectar elementos , en caso de lamparas de  sobremesa, de pie, de mesa ,flexo,etc si tiene conectadas luminarias de leds , podemos probar los siguientes recursos:

  • Invertir la forma de conectar el enchufe a la toma de corriente ( es decir invertir la fase por el neutro y viceversa)
  • Probar con otra luminaria de otra marca o modelo ( podría se defecto de la propia bombilla de led sobre todo si es de origen asiático de bajo coste)
  • Revisar el cableado  interior  pues si toca uno de los cables con el apantallado metálico ademas de ser peligroso podria hacer suficiente masa para que se encienda la luminaria levemente
  • Reemplazar el interruptor monopolar por bipolar , es decir que corte no solo uno de los cables ( fase o neutro ) sino ambos a la vez.

Interruptores con testigos de neón

Esta particular característica de los leds está creando un nuevo problema cuando estos se usan para iluminación porque las instalaciones eléctricas, muchas veces tienen diferencias de potencial a tierra que generan pequeñas fugas de corriente y que no dejan que las lámparas de leds se apaguen completamente.

Este  fenómeno es especialmente persistente  cuando se usan interruptores de encendido con luz de señalización con neón como podemos observar en el  esquema de abajo  donde el testigo de neón con la correspondiente resistencia va en paralelo con el propio interruptor:

.

 

Es evidente en el esquema  anterior que si abrimos el interruptor  , la lampara de neon  y la resistencia de 330k quedará en serie  con la luminaria de leds, lo cual en muchos caso sera suficiente para que este conjunto resistencia-neón  deje pasar la suficiente corriente  para que se ilumine levemente la luminaria.

Por  tanto si deseamos eliminar este efecto residual bastará eliminar el testigo de neón del interruptor ( suelen ir separados del interruptor  y enchufables para sustituirlo fácilmente en caso de que se fundan) 

Pero ¿Y si no desea eliminar el testigo del interruptor?  pues para eliminar la luminosidad residual en las lámparas a leds podemos aprovechar dos factores:

  • Que  la corriente que atraviesa el circuito es muy baja.
  • Que es necesario superar un umbral de tensión para que los leds se enciendan , umbral  que dependerá de como esten conectados en la luminaria (  más alto si los leds están conectados en serie).

¿Y como lo logramos ? pues  lo que tenemos que hacer es conectar en paralelo con   la luminaria una resistencia de  1Mohmio  para lograr que la caída de tensión sobre la lámpara, cuando esta se encuentra apagada, sea más baja de la tensión de umbral citada.

Naturalmente, debido a que existen numerosos tipos de lámparas, será necesario probar experimentalmente el valor justo.

Podemos empezar con un valor muy alto, por ejemplo 1Mohms , lo cual dará una corriente de I=V/R =220/1.000.000 =0,00022 ( es decir 2.2mA  ) ,   que en  potencia disipada seria =I^2 x R = 0,00022*0,00022*100000= 0.0484W , lo cual es asumible con una simple resistencia de 1/4W)

Si no logramos que los leds se apaguen puede ir bajando de forma  muy conservadora este valor pues a medida  que disminuya este valor la corriente y la potencia disipada por esta resistencia serán mayores

Como referencia , como tope de valores podemos estar en 1/4 de Watt con valores por encima de 330K con 220V y 100K con 110V. 

 

Por cierto,  hay personas  que precisamente buscan potenciar   el fenómeno de la luz residual de los leds   por ejemplo, conectando una resistencia de algunos K en paralelo con los interruptores de alimentación de 12V en modo tal que quede una débil luz en el ambiente para permitir de ver cuando todas las luces están apagadas. Es un sistema realmente cómodo. 

Solución con rele 

Tal y como hemos hablado con las lamparas de sobremesa , muchas veces el problema se debe a que los interruptores no cortan por completo los dos hilos ( fase  y neutro ) de la instalación ya que suelen ser monopolares . Ademas  para empeorar al situación en algunas y instalaciones esta conectado el neutro en lugar de la fase al interruptor (o incluso hay instalaciones con fase y fase en lugar de fase o neutro como debería  ser )

 

En este caso no podemos hacer casi nada sino buscar un sistema que haga que ese resto de corriente no llegue a la luminaria provocando ese resplandor residual, para lo cual podemos optar por «un cortador de luz.» , es decir un «relé»o contacto que actúe cortando eléctricamente los dos hilos que llegan a la lámpara   y de esta manera impidiendo la llegada de tensión por ambos hilos simultáneamente.

El proceso consiste en separar ligeramente la luminaria del techo y colocar el «relé» entre los cables que llegan a la luminaria . De esta forma y una vez colocado de nuevo, cuando accionamos el pulsador de la luz sólo oiremos un muy ligero chasquido (es imperceptible) que nos indica que la corriente ha sido totalmente cortada y por lo tanto no llega residuo alguno que provoque el pequeño resplandor.

 

 

 

 

Solución  dudosa con condensador

Por último hay personas que optan   por conectar  un condensador  de .47uf en paralelo con lo podriamos llamarlo polos de la lampara, asi este absorbe la corriente residual y antes de completar su carga se descarga por el cambio de ciclo,

Es  una solución interesante aunque podría  tener un problema: la reactancia del capacitor es Xc = 1 / (2 * Pi * f * C ) = 1 / (2 * 3,14 * 50 * 0,00000047) = 6772 ohms. Por lo tanto, la potencia disipada por el capacitor será P = V * V / R = 7,15 Watts. Es decir, tendríamos un consumo extra de 7 Watts que se pierde en el condensador

 

 

Por cierto este efecto  al igual que no les afecta a todas las instalaciones   tampoco les afecta por igual  a todos las luminarias  dependiendo muchas veces del fabricante y  modelo para manifestarse o no este efecto cuanto menos indeseado