20 jun 2011

Rotar un gif

Hay ocasiones que las cosas simples se complican un poco, claro, con paciencia y saliva todo se puede, pero a veces simplemente no vale la pena el esfuerzo, mejor dejarlo y seguir con otra cosa,si, es verdad, esta introduccion suena un poco dramatica pero la escribo en el preciso momento en que la frustracion, el desencanto, la rabia y por poco la locura se apoderan de mi ser: estoy tratando de rotar un gif en Kubuntu.

Parece chiste, pero quiero hacer una burbuja de dialogo, simplemente eso, pero la imagen de la flecha que señala el origen de la burbuja apunta hacia abajo, y yo la quiero hacia arriba, bien, simple en principio, o eso al menos creia yo.

Bien, veamos.., doble click... trato de editarla con Eye of GNOME 2.30.0, predeterminado (vengo de Gnome) y tiene los botones de rotar, simple!, roto y salvo... pero mmmmmm, error: "Formato de archivo desconocido o no permitido", rayos, a ver que mas tengo.

Abrir con Gwenview 2.4.3, ok, tiene los comandos de rotar, bien, roto a la derecha dos veces y salvo, pero ohoh!:
"Gwenview no puede guardar imágenes en formato «gif».", pero caracoles!, que raro, sera porque el formato no es libre o algo asi?
(offtopic, busco algo de info al respecto: segun Wikipedia "El 20 de junio de 2003 expiró en Estados Unidos la patente por el algoritmo LZW" que supongo usaria el formato GIF), en fin, que se yo, no pasa nada, la bateria de herramientas Linux a mi rescate.

Probemos con F-Spot 0.6.1.5, a ver que tal, editar > rotar a la derecha..., ups!!!: "Error al rotar la foto. Se recibio el error 'No se puede rotar este tipo de foto'", caramba caramba, la puta que lo pario con la dichosa imagencita.

Mmmmm, Okular? nones, es para PDF's, que hace ahi esa opcion?, a ver que mas tengo...

Sigo con Gimp, este esta poderoso, tiene que andar, veamos.... tiene mas comandos que una central nuclear: Archivo: no..., Editar no...,Seleccionar no..., Ver tampoco..., me estoy calentando... aca! Imagen > transformar > Rotar 180º, ja! pan comido.

Salvo y listo, al fin, a ver que tal quedo, OMFG!!, me cambio el fondo de la imagen, ahora se ven bloques grises y blancos de fondo, NOOOOO!!! LRPMQLP!!, maldita maquina, QUIERO ROTAR UN GIFFFFF!!!!, uffffffff!!!!

Paz! paz!, me voy a VirtualBox a abrir un Windows XP, me niego a instalar nada para esta tonteria, espero que uds tengan mas suerte.

Evitando SOP en GWT

Cuando desarrollamos aplicaciones web una de las tares mas básicas es la comunicación cliente servidor, y si bien GWT nos proporciona diferentes mecanismos (RPC, JSONP, etc) , uno de los mas habituales es la realización de peticiones HTTP al servidor.
Para esto se nos proporciona la clase HTTP cliente mediante la cual creamos y recibimos requests, siempre bajo las restricciones que impone la política de SOP (same origin policy) de los navegadores, que evita que el código cliente interactue con recursos de otros dominios previniendo potenciales problemas de seguridad.
Por otro lado, una de las formas mas prácticas de desarrollar en GWT es el llamado modo desarrollo, en el cual las aplicaciones corren sobre la máquina virtual de Java, pero quedamos restringuidos a realizar peticiones HTTP al dominio en el cual estamos ejecutando, del tipo "http://127.0.0.1:8888/MyApp.html?gwt.codesvr=127.0.0.1:9997".
Dicho esto nada mas cómodo que trabajar en modo desarrollo y realizar desde ahí las consultas al server, pero lamentablemente estos dos panoramas son totalmente incompatibles, quizás podríamos crear resultados de retorno simulados para las peticiones, también compilar todo cada tanto para verificar que todo funciona como esperamos, pero aun así cualquiera de las dos opciones es bastante impráctica, sobre todo compilar que ya de por si se lleva su tiempo, otra posible solución es cambiar todo esto por el uso de JSONP, pero no es lo que queremos, eso es solo otro hack al navegador, yo quiero Modo Desarrollo mas peticiones HTTP.

Leyendo precisamente en Wikipedia sobre SOP, encontré un enlace a una técnica un poco desconocida llamada CORS (Cross-Origin Resource Sharing) la cual habilita la posibilidad de realizar peticiones evitando el SOP de los navegadores, y claro, vi la luz al final del tunel.
Básicamente se trata de agregarle a la respuesta del servidor una serie de cabeceras que especifiquen el uso de esta técnica, soportada por varios navegadores modernos (ok, ok,  solo probé en Chrome y FF4, si alguien desarrolla con otro browser largo de aquí).

Para ver como funciona, realizaremos una aplicación de prueba con GWT y Python, recuerden que el uso de esta técnica es solo para desarrollo, nunca para codigo en produccion.
Este metodo fue probado con Python sobre WSGI y PHP, los navegadores son Firefox 4.0 y Chrome 6.0.472.63 sobre una caja Kubuntu 10.04.2 LTS.

Primero, abrimos Eclipse y realizamos un nuevo proyecto GWT, vacío, asi observamos bien el funcionamiento de esta técnica, luego creamos una petición HTTP (recordar de agregar al archivo XML del módulo la linea  <inherits name="com.google.gwt.http.HTTP" />) y la apuntamos a un dominio diferente al que corremos la aplicación, quedando de tal manera:


package cors.test.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Window;
/**
* Entry point classes define onModuleLoad().
*/
public class CORS_test implements EntryPoint {
 public void onModuleLoad() {
  
  String url = "http://localhost/CORS_test.wsgi";
  RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
  
  try{
   Request request = builder.sendRequest(null, new RequestCallback() {
    
    @Override
    public void onResponseReceived(Request request, Response response) {
     
     if(200 == response.getStatusCode()){
      Window.alert(response.getText());
     }
     else{
      // handle the error
      Window.alert("failed petition");
     }
    }
    
    @Override
    public void onError(Request request, Throwable exception) {
     Window.alert("CORS... sure");
    }
   });
   
  }
  catch(RequestException e){
   Window.alert("Couldn't connect to server");
  }
  
 }
}

Luego, creamos el archivo de respondera a la petición, en este caso CORS_test.wsgi y lo colocamos en nuestro localhost:

def application(environ, start_response):
    status = '200 OK'
    response_headers = [('Content-type','text/plain')]
    start_response(status, response_headers)
    return ['Hello world from Python!']

apuntamos nuestro navegador a http://localhost/CORS_test.wsgi y debemos ver el mensaje "Hello world from Python!"
Ahora, corremos nuestra aplicación GWT en modo desarrollo, recibimos el siguiente mensaje: "failed petition", efectivamente SOP nos esta bloqueando las peticiones, por lo que agregamos las siguientes cabeceras en la respuesta:

# CORS_test.wsgi

def application(environ, start_response):
    status = '200 OK'
    response_headers = [
        ('Content-type','text/plain'),
        ('Access-Control-Allow-Origin','http://127.0.0.1:8888'),
        ('Access-Control-Allow-Methods','GET, POST')
    ]
    start_response(status, response_headers)
    return ['Hello world from Python!']

y probamos nuevamente, obteniendo el mensaje: "Hello world from Python!"
Fácil verdad, lo importante es que la cabecera "Access-Control-Allow-Origin" especifique nuestro dominio, en este caso "127.0.0.1:8888", aunque también podríamos haber colocado *  e igual funcionaría, ahora toca en nuestro querido PHP, recordemos cambiar el valor de la URL por: String url = "http://localhost/CORS_test.php".

# CORS_test.php

<?php

header("Content-type:text/plain");
header("Access-Control-Allow-Origin:http://127.0.0.1:8888");
header("Access-Control-Allow-Methods:GET, POST");
echo "Hello world! from PHP";

?>

cuidado con los espacios en blanco de la etiqueta header, pueden hacer que la aplicación falle, ahora al correrlo veremos el mensaje: "Hello world! from PHP".
Bueno, esta es la técnica básica, los invito a leer la  especificación  , y ahora si: podemos volver a desarrollar dignamente.