Categoría: java

  • Modificar Brillo y Contraste con Java 2D

    Modificar Brillo y Contraste con Java 2D

    Como podéis comprobar, en el blog comento sobre ciertos temas o problemas que van surgiendo durante nuestro trabajo diario y que damos solución mediante Java y Javascript (o cualquier otra tecnología, mayoritariamente orientada a la web). Hace poco publiqué un post sobre cómo realizar zoom en imágenes publicadas en la aplicación web. Este problema afortunadamente pudimos resolverlo mediante javascript y jQuery. Tras esto, el cliente nos propuso como requisito realizar ajustes de brillo y contraste sobre imágenes digitalizadas y publicadas en nuestra aplicación.  Lo primero que se nos ocurrió fue incorporar alguna librería javascript para abordar lo que nos proponían, pero no encontramos nada adecuado que funcionara en todos los navegadores (esperaremos a HTML5 y el objeto canvas). La otra opción era, obviamente, realizar una petición al servidor para que nos enviara la imagen transformada con los datos de brillo y contraste actualizados.

    El lenguaje Java incorpora la librería Java2D, cuyo API proporciona un amplio conjunto de clases para la composición y transformación de la imagen. La solución consistía en realizar una petición al servidor para recargar la página y actualizar la imagen, de manera que en el atributo src de la imagen realizara una petición al servidor para obtener una nueva imagen transformada, pasando los nuevos parámetros para brillo y contraste:

    <img width="687" height="960" style="position: absolute; top: 0px; left: 82px;" src="/MiApp/ImageServlet?accion=42&id_imagen=1&brillo=10.0&contrast=1.0" />

    En la página incorporamos cinco botones: más brillo, más contraste, menos brillo, menos contraste y restablecer brillo y contraste originales. Establecemos una escala de manera que los valores que podamos asignar al brillo van desde -125 hasta 125 (por defecto 0) y para el contraste desde 0.0 hasta 2.0 (por defecto 1.0). Cada vez que pulsamos los botones de brillo actualizamos la página para que el valor del parámetro brillo, dentro del atributo src de la imagen, se incremente o decremente en 5 unidades (o lo que deseemos). Haremos lo mismo en el caso del contraste con una variación de +0.1 ó -0.1 unidad (también modificable). El botón restablecer deja los parámetros en 0.0 para el brillo y 1.0 para el contraste. En resumen, los botones recargan de nuevo la página, pasando los nuevos valores actualizados de brillo y contraste, aumentando o decrementando su valor en función del botón pulsado.

    El controlador que sirve la imagen en el servidor (en el ejemplo de arriba sería el servlet ImageServlet cuando recupera el parámetro accion con el valor 42) recogerá dichos parámetros, y cargará la imagen en memoria desde el archivo de un modo similar a éste:

    JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new BufferedInputStream(new FileInputStream(ruta)));
    BufferedImage buf = decoder.decodeAsBufferedImage();

    El parámetro ruta es la ruta a la imagen en el sistema de ficheros (he omitido cómo recuperar la ruta a partir del identificador de imagen pasado como parámetro). Una vez cargada la imagen invocaremos al método que realizará la transformación, pasando como parámetro la referencia a la imagen y los valores de brillo y contraste:

    buf = brightContrastChange(buf, brillo, contraste);

    Y el código fuente de la función es la siguiente:

    private static BufferedImage brightContrastChange(BufferedImage bufferedImagen, Float brillo, Float contraste)
    {
    //Transformación de la imagen con nuevos valores brillo y contraste
    BufferedImageOp operacion = new RescaleOp(contraste, brillo, null);
    bufferedImagen = operacion.filter(bufferedImagen, null);
    return bufferedImagen;
    }

    Únicamente nos queda escribir la imagen en el outputStream para enviarla al navegador:

    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(output);
    encoder.encode(bufferedImage);
    output.flush();

    Siendo output el outputStream o flujo de salida. Obviamente estos son los pasos básicos, pero se pueden incorporar numerosas mejoras.

    Una de ellas es la de obtener la imagen mediante una petición AJAX, sin necesidad de recargar la página para actualizar los parámetros brillo y contraste. Otra es la de mostrar una barra sobre la que podamos seleccionar un valor determinado sin saltos de 5 en 5 (brillo) o de 0.1 en 0.1 (contraste). Un componente que nos ayudó enormemente a acometer esta funcionalidad fue el slider de jquery.ui y que comentaré en otra entrada del blog próximamente.

  • Visor de Imágenes con jQuery y el plugin iViewer

    Visor de Imágenes con jQuery y el plugin iViewer

    Anteriormente ya he publicado varios posts hablando sobre la librería jQuery, en concreto como resaltar una búsqueda con jQuery y cómo actualizar tablas displaytag con ajax y jQuery. Gracias a esta librería y el plugin iViewer podemos cargar una imagen, visualizarla en pantalla dentro de un contenedor con la posibilidad de realizar zoom de la imagen con el ratón dentro del contenedor.

    Puedes ver una demo pulsando sobre el enlace: http://joseantoniosaiz.es/samples/iviewer

    ¿Cómo se usa? Obviamente debemos de partir de una página que posea un contenedor en el que cargar la imagen, y al que daremos un ancho y un alto.

    <html>
    <head>
    <style>
    .viewer
    {
    width: 768px;
    height: 573px;
    border: 1px solid black;
    position: relative;
    }
    .wrapper
    {
    overflow: hidden;
    }
    </style>
    </head>
    <body>
    <h1>jQuery Zoom</h1>
    <br/>
    <div id="viewer"></div>
    </body>
    </html>

    También debemos incluir las referencias a las librerías javascript de jQuery y del plugin iViewer, así como el enlace al fichero de estilos del plugin (yo lo inserté entre los tags head de la página)

    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" ></script>
    <script type="text/javascript" src="jquery.iviewer.js" ></script>
    <link rel="stylesheet" href="jquery.iviewer.css" />

    Para terminar debemos añadir el código javascript para invocar al plugin y cargue la imagen con los parámetros que deseemos. Al insertar el código dentro de la función ready nos aseguramos que se ejecutará cuando el documento HTML esté listo para visualizarse.

    <script type="text/javascript">
    var $ = jQuery;
    $(document).ready(function(){
    var iviewer = {};
    $("#viewer").iviewer(
    {
    src: "CIMG0498.JPG",
    initCallback: function()
    {
    iviewer = this;
    }
    });
    });
    </script>

    En este ejemplo únicamente hemos añadido el parámetro src con la ruta a la imagen. Para ver más parámetros podemos consultar la página oficial del plugin. También he probado a cargar una imagen añadiendo una URL de una imagen publicada en cualquier web en vez de una ruta al fichero y funciona correctamente.

    Una vez que se ha cargado la imagen, aparecerá una barra con un enlace para ampliar el zoom de la imagen, otro para reducirlo, otro para ver la imagen a tamaño real y otro para ajustarla al área de visión. También se ofrece el porcentaje de zoom. Pulsando el botón izquierdo del ratón podemos arrastrar la imagen para desplazarnos por ella. Si deseamos realizar zoom con la rueda del ratón podemos insertar el plugin mouse wheel extension:

    <script type="text/javascript" src="jquery.mousewheel.min.js" ></script>

  • Internacionalización (I18n) con DisplayTag y Spring

    Internacionalización (I18n) con DisplayTag y Spring

    La librería de tags Displaytag nos permite mostrar listas de objetos en forma de tabla, facilitandónos la ordenación, la paginación de los resultados, el agrupamiento, la exportación de datos, etc., tal cómo ya habíamos comentado en un post anterior. La configuración se realiza mediante un fichero de propiedades que debe localizarse en el CLASSPATH de la aplicación, llamado display-tag.properties. En él se definen, tanto los estilos y apariencia, como los mensajes que aparecerán cuando se muestran los controles de paginación, o no hay datos, etc. La labor de configuración es bastante sencilla, pero cuando tu aplicación es multiidioma y necesitas configurar los mensajes o etiquetas que aparecen en la tabla en función del idioma del usuario, el tema no es tan trivial.

    Displaytag

    En nuestra aplicación desarrollamos con Spring framework y tenemos configurada la internacionalización en el fichero xml de configuración de Spring de la siguiente forma:
    <!-- Bean para recoger el archivo de propiedades con los mensajes -->
    <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
    <list>
    <value>/WEB-INF/properties/messages</value>
    </list>
    </property>
    <!-- Tiempo para la actualización del archivo properties -->
    <property name="cacheSeconds" value="60"></property>
    </bean>
    <!-- Bean para cambiar el idioma (permite cambiar el “Locale” actual en cada petición al servidor, mediante el uso del parámetro 'siteLanguage') -->
    <bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    <property name="paramName">
    <value>siteLanguage</value>
    </property>
    </bean>
    <!-- Bean que especifica el "locale" actual utilizando la sesión del usuario -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver"></bean>

    Por defecto la aplicación recoge el idioma en el que esté configurado el navegador, pero una vez que hemos iniciado la aplicación podemos modificar el idioma durante la sesión del usuario. Para esto se realizará una petición al servidor con el parámetro «siteLanguage» y el nuevo locale. Hemos tenido que realizar una tarea previa en las páginas para que no inserten los mensajes de forma literal, sino que contengan una clave con la que se obtengan los mensajes almacenados en el fichero de propiedades. En nuestro caso utilizamos el fichero messages.properties para los mensajes en castellano y messages_en.properties para los mensajes en inglés, por ejemplo (el sufijo representa el idioma, en, es, de, it,fr,etc.).

    Tan sólo nos falta la configuración del displaytag para que tanto los mensajes como los títulos de las columnas de la tabla se configuren siguiendo el idioma seleccionado. En el caso de los encabezados de las columnas actuamos de la misma manera que en las demás etiquetas de la página, es decir, en la cabecera hacemos referencia a una clave que recogerá el mensaje del fichero de mensajes con el idioma correspondiente:

    <display:column titleKey="etiq.tabla.titulo" property="titulo" sortable="true" sortProperty="titulo"></display:column>

    Los demás mensajes de la tabla se recogen del fichero de configuración displaytag.properties. En él se deben añadir estas dos líneas:
    # locale provider (Jstl provider by default)
    locale.provider=org.displaytag.localization.I18nSpringAdapter
    # locale.resolver (nothing by default, simply use locale from request)
    locale.resolver=org.displaytag.localization.I18nSpringAdapter

    Una vez hecho esto tenemos que configurar un nuevo fichero displaytag.properties con el sufijo correspondiente al idioma seleccionado (displaytag_en.properties, displaytag_de.properties, etc.), pero únicamente con las claves de los mensajes que queremos cambiar con el idioma, no es necesario duplicar todo el fichero, ya que los valores que no se encuentren los recoge del original. Este nuevo fichero se debe localizar en la misma carpeta, siempre en el classpath de la aplicación, como por ejemplo en la carpeta donde residan las clases java.

  • Deshabilitar Cache-Control en JBoss/Tomcat

    En recientes fechas el usuario de una aplicación de consulta por la Intranet nos ha solicitado la securización de la misma vía nombre de usuario y contraseña. Al hacer esto, nos han reportado una incidencia por la que al intentar guardar una imagen en formato JPG con Internet Explorer (pulsando el botón derecho y seleccionando la opción Guardar Imagen como…), el único formato en el que permite guardar es BMP. Antes de la modificación, la imagen podía almacenarse en el formato original (tanto el content-type: image/jpg como el filename se añaden a la cabecera en el servidor). Tras esto detectamos problemas al descargar ficheros pdf, pero únicamente si lo hacemos con Internet Explorer, ni Chrome, ni Firefox, ni Safari actuaban de esta forma.

    Investigando un poco el tema descubrimos que el problema venía por unas cabeceras de respuesta en la petición HTTP que misteriosamente aparecían al servir la imagen:

    Pragma: No-cache
    Cache-Control: no-cache
    Expires: Thu, 01 Jan 1970 01:00:00 CET

    Estas cabeceras siempre las añade JBoss (por defecto) a la petición cuando la aplicación se encuentra en un entorno seguro para evitar que se cacheen los recursos. Internet Explorer las interpreta de una manera diferente a los demás navegadores y a saber por qué razón no permite descargar los ficheros o guardarlos en un formato diferente, aunque todo indica que es por motivos de seguridad.

    Hemos encontrado un método de evitar esto. Se trata de incorporar una válvula (Valve) al contexto de nuestra aplicación web, que indique a JBoss que deshabilite el control de la cache para los recursos securizados. Para ello nos creamos un fichero context.xml (más información sobre el contexto de las aplicaciones web en JBoss/Tomcat) dentro de la carpeta web-inf de nuestra aplicación web:
    <Context>
    <Valve className="org.apache.catalina.authenticator.FormAuthenticator"
    disableProxyCaching="false" ></Valve>
    </Context>

    En nuestro caso utilizamos esta manera, ya que la autenticación está basada en formulario, tal y como configuramos en el fichero web.xml de la aplicación. Si el tipo de autenticación es HTTP Básica, entonces utilizaremos:
    <Context>
    <Valve className="org.apache.catalina.authenticator.BasicAuthenticator"
    disableProxyCaching="false" ></Valve>
    </Context>

    Con esto las cabeceras que indicamos más arriba no son añadidas por JBoss y solucionamos los problemas ocasionados en Internet Explorer. Esto no quiere decir que no podamos deshabilitar la caché para un recurso, sólo indica que somos nosotros los que controlamos este comportamiento, pudiendo añadir en el servidor las cabeceras para las peticiones que nos interesen, mediante el método:

    response.addHeader("Cache-Control", "no-cache");

    Este error no es exclusivo de JBoss, ocurre igualmente en Tomcat (JBoss está construido sobre Tomcat). La versión de JBoss que utilizamos es la 4.0.2 y las versiones de Internet Explorer en las que hemos probado son la 7 y la 8.

  • Resaltar una búsqueda con jQuery

    Resaltar una búsqueda con jQuery

    Después de algunos meses sin publicar ningún post, y ya pasadas las vacaciones de verano, vuelvo con un tema que seguro que a los que trabajamos diseñando / desarrollando aplicaciones web nos han pedido los usuarios en más de alguna ocasión: Resaltar en la pantalla las palabras introducidas en una búsqueda.

    En nuestro caso tenemos una página con un formulario de búsqueda con diferentes campos: una caja de texto (búsqueda por texto), dos campos de fechas (búsqueda por fechas) y una lista desplegable de países. Todos ellos se combinan en una consulta a la Base de datos y muestra un listado con los resultados. Dichos resultados se pueden ampliar en otra página pulsando sobre el registro correspondiente. Cómo os imagináis es en esta página de detalle donde se nos requirió que se resaltaran las palabras introducidas en la búsqueda.

    Después de manejar varias opciones (resaltar en BBDD o en la propia aplicación, de manera que al navegador le llegaba el trabajo hecho), me decanté por hacerlo mediante javascript una vez que haya cargado todo el documento en la página. Para ello hemos incluido dentro del proyecto la librería jQuery (de la que ya os he hablado anteriormente) y de un plugin para ella que podéis consultar en http://bartaz.github.com/sandbox.js/jquery.highlight.html.

    Básicamente esta librería contiene una función que se encarga de recorrer todas las palabras del contenedor (documento, div, table, etc.) que le especifiquemos y si encuentra alguna de las palabras utilizadas en la búsqueda y que le pasamos como parámetro la envuelve en una etiqueta <span class=»highlight»>:

    <script type="text/javascript" src="/js/jquery-1.4.2.js"></script>
    <script type="text/javascript" src="/js/jquery-highlight.js"></script>
    <script type="text/javascript">
    $(document).ready(function(){
    $("html").highlight(divide_string("<c:out value='${modelo.form.textoLibre}'/>"),{ wordsOnly: true });
    });
    </script>

    Este código se ejecuta una vez que se ha cargado el documento y llama a la función highlight para que resalte las palabras dentro del elemento HTML del DOM. Asímismo le pasamos la opción wordsOnly:true para que resalte palabras completas (si en la búsqueda se introdujo ‘este’, que no resalte parcialmente Esteban). La función divide_string recibe el texto introducido en la caja de búsqueda y retorna un array de palabras, obviando palabras comunes como artículos y preposiciones. Aquí os paso el código:

    function divide_string(cadena)
    {
    var pattern =                       "\\b(Sr|Sra|Sres|Sta|...más_palabras_comunes...|vuestro|vuestros|y|yo|él|en)\\b";
    var re = new RegExp(pattern, "gi");
    cadena = cadena.replace(re,"");
    var mytool_array = cadena.split(" ");
    return mytool_array;
    }

    Por último indicar que fue necesario realizar modificaciones del plugin ya que no resaltaba las palabras que tenían acento si en la caja de texto buscabas sin acento. Justo antes de crear la expresión regular a evaluar, modificamos el patrón para incluir las vocales acentuadas:

    pattern = pattern.replace(/a/gi,"[a|á]");pattern = pattern.replace(/e/gi,"[e|é]");pattern = pattern.replace(/i/gi,"[i|í]");pattern = pattern.replace(/o/gi,"[o|ó]");pattern = pattern.replace(/u/gi,"[u|ú]");
    if (settings.wordsOnly) {
    pattern = "\\b" + pattern + "\\b";
    }
    var re = new RegExp(pattern, flag);

    Seguro que alguna vez os resulta útil.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para su aceptación y la de nuestra política de cookies.

ACEPTAR
Aviso de cookies