29.7.14

Las limitaciones de CSS en un EPUB

EPUB es un formato abierto de libro electrónico desarrollado por el IDPF. Una de sus características es que el texto de libro se encuentra en XHTML (también puede estar en formato DTBook, pero de momento, todos los que he leído usan XHTML), y se puede (y debe) usar CSS para aplicar estilos. Esto hace que una persona con experiencia en el mundo HTML, pueda maquetar libros en formato EPUB, con un pequeño aprendizaje adicional.

Un EPUB no es una página web, por lo que hay algunas recomendaciones oficiales, como el evitar la pseudoclase :hover o limitaciones a la hora de usar la propiedad position.

El problema es que nos encontramos en una situación similar a la de los 90 con la web. Por un lado, aunque el estándar oficial va por la versión 3, aún quedan muchos dispositivos en manos de los usuarios, que sólo soportan la versión 2. Por otro, hay varias aplicaciones para dispositivos móviles (sobre todo en Android) que permiten leer EPUB, con bastantes limitaciones. Y si bien, para tener una experiencia agradable es preferible el uso de un eReader con tinta electrónica, la gratuidad de muchas de estas aplicaciones hace que haya gente que se decante por leer en su tablet o móvil.

No hay en la web (o al menos no he encontrado) una lista de limitaciones en el soporte CSS de distintos dispositivos, así que os detallo aquí las cosas que me he ido encontrando de forma experimental.

Para empezar, no todos los dispositivos tienen un soporte completo de los selectores CSS. Algo tan simple como:


p {
  text-indent: 0;
}

p+p {
  text-indent: 2em;
}

que nos permitiría que todos los párrafos estén sangrados excepto el primero (práctica habitual en la narrativa), no es entendible por todos los dispositivos. Tampoco está garantizado que funcionen las pseudoclases (como :first-child) ni los pseudoelementos (como :first-letter). Pero es más, ni siquiera algo tan básico como el anidamiento de elementos, o el uso de varios selectores separados por comas, funcionará en todos sitios. Olvidáos pues de cosas como:


.chapter p { 
}

.title, .subtitle, .author {
}

Posiblemente un buen lector los entendería, pero alguna aplicación de Android no lo hará. La técnica más segura es limitarse a un único selector por declaración, con un único elemento (con o sin clase), lo que nos obliga a añadir muchas clases en nuestro HTML, y a repetir código en la CSS, para selectores que deben tener el mismo estilo. Por ejemplo:


h1 {
  font-family: Arial, sans-serif; 
  font-weight: bold;
  font-size: 26px;
}

h2 {
  font-family: Arial, sans-serif; 
  font-weight: bold;
  font-size: 20px;
}

p {
  text-indent: 2em;
}

p.first {
  text-indent: 0;
}

p.in-copyright-page {
}

img.in-copyright-page {
}

ul.in-copyright-page {
}

Más cosas. Según el estándar CSS, cuando el valor de una propiedad de medida (como margin, padding) es cero, no es necesario especificar ninguna unidad. Parece algo lógico, ya que 0em y 0px es en realidad lo mismo: cero. Pero existen aplicaciones o dispositivos lectores de EPUB, que si no se especifica siempre una unidad, ignorarán el valor. Así que, para curarse en salud, lo mejor es especificarla, aunque el valor numérico sea cero.

Por último, algunos dispositivos no soportan las propiedades compuestas o multivalor, es decir, aquellas propiedades que en realidad están especificando varios valores de forma compacta. Por ejemplo:


p {
  margin: 0em;
}

puede no funcionar en algún dispositivo. Así que, aunque tedioso, es más seguro optar por la versión más larga de especificar lo mismo:


p {
  margin-top: 0em;
  margin-right: 0em;
  margin-bottom: 0em;
  margin-left: 0em;
}

Vuelvo a repetir que esto son limitaciones que he descubierto en algunos lectores (dispositivos o aplicaciones). Un buen eReader seguramente soportará correctamente todo el estándar CSS. Pero si queremos que nuestro EPUB se vea correctamente en la mayor cantidad de lectores posibles, es conveniente tener estas restricciones en mente.

5.7.14

NoClassDefFoundError cuando la clase sí que se encuentra

La documentación oficial de la clase java.lang.NoClassDefFoundError nos dice que esta excepción se lanza cuando la máquina virtual intenta cargar la definición de una clase, pero esta definición no se encuentra. Esto indica que cuando el programa se compiló, la clase estaba presente, pero en la ejecución ya no está.

Si al ejecutar una aplicación nos salta esta excepción, al leer esto enseguida pensamos en problemas de classpath o de empaquetamiento, y nos ponemos como locos a comprobar si el jar está bien construido, si están todos los jars, si tenemos bien definido el classpath, etc. Pero a veces, por mucho que busquemos no encontramos ningún error ahí. Es más, es especialmente desconcertante cuando la clase supuestamente desaparecida, está en el mismo jar que la clase que la busca y no la encuentra.

¿Qué puede estar ocurriendo? Bien, hay un motivo por el que la máquina virtual puede lanzar una java.lang.NoClassDefFoundError, aunque encuentre la clase: durante la inicialización estática de la misma. Imaginemos la siguiente clase:


public class SomeClass {
    
    public static final int SOME_CONSTANT = precalculateSomeValue();
    
    static {
        initializeSomething();
    }
 
    // (...)   
 
}

Si durante la ejecución del método precalculateSomeValue() o de initializeSomething(), se lanzara una excepción, la clase que hace referencia a SomeClass (y que ha «disparado» la inicialización), lanzará una NoClassDefFoundError. Fijaos que en este caso, la clase sí que ha sido localizada correctamente por la máquina virtual. El problema no es que no la haya encontrado, sino que no la ha podido inicializar.