Automatizar Photoshop – A fondo

De dónde venimos

Se que la anterior entrega de “Cómo Automatizar Photoshop” no tuvo muy buena acogida. El hecho de que nadie se la haya leído, y que la información de Google Trends sea en este caso esclarecedora, no ha sido motivo suficiente para doblegar mi voluntad. Es lo absurdo de mi voluntad lo que me anima a seguir hacia delante. O hacia los lados. O para atrás. O quedarme quieto. Seguir en definitiva, aunque sea parado. Porque puedo seguir sin moverme. Incluso puedo desplazarme sin moverme. Pero eso podría ser el motivo de un nuevo post, ya veremos.

La cruda realidad

Y ese pico no es por mi 🙁

El primer capítulo nos introducía un poco en el manejo del entorno de desarrollo para Photoshop, con un ejemplo sencillo. Decíamos ayer, el ejemplo podría haberse llevado a cabo seguramente con una acción de Photoshop, sin tener que aprehender nada de programación. Y esto era premeditado. Ahora vamos a complicar el objetivo de nuestro script, pero partiendo del código anterior.

A dónde vamos

Añadamos dos requisitos para nuestro script:

  • Obtener como imágenes de salida varias relaciones de aspecto de la imagen de partida.
  • Que las imágenes de salida se salven en JPEG sólo si la imagen de entrada era JPEG, de otro modo, guardamos en PNG.

Como veis, estos requisitos ya son bastante más difíciles de satisfacer solo con acciones.

Empezamos modificando nuestro script, añadiendo un array con las relaciones de aspecto que queremos como salida:

var aspectRatio = [ [ 1, 1 ], [ 4, 3 ], [ 16, 9 ] ];

Aquí podremos añadir más relaciones de aspecto que nos puedan interesar, 5:4, 16:10, o eliminar las que no nos interesen. ¿Quién es el obtuso ahora?

Ahora bien. Vamos a recortar nuestra imagen, asi que tenemos que calcular las nuevas dimensiones. Pero alto, ávido programador, detenga su avance, meditemos juntos. He hecho este bonito gráfico. Me gustan los gráficos. Espero que nos sirva para aclararnos.

Un gráfico bonito (siento el texto en inglés)

Esto nos quiere decir, que tenemos que tener en cuenta la relación de aspecto de la imagen de partida, para saber por donde tenemos que recortar. Como veréis estamos presuponiendo que la imagen la vamos a dejar centrada siempre. No quería adelantarme, pero en nuestro próximo capítulo, vamos a ver como podemos hacer algo para “escoger” si centrar la imagen o seleccionar otras opciones… ¿Quién es el obtuso ahora?

Plasmemos esto en código por favor:

var imageAspectRatio = doc.width / doc.height;
var currentAspectRatio = aspectRatio[j][0] / aspectRatio[j][1];

var tImageWidth = imageWidth;
var tImageHeight = imageHeight;

if(  imageAspectRatio > currentAspectRatio ) {
	tImageWidth = Math.round( aspectRatio[j][0] * imageHeight / aspectRatio[j][1] );
} else {
	tImageHeight = Math.round( aspectRatio[j][1] * imageWidth / aspectRatio[j][0] );
}

Intentando explicarlo con un lenguaje más humano, aunque inherentemente más impreciso, si la imagen de partida tiene una relación mayor (es decir que es aun más estirada que nuestra relación objetivo), lo que tendremos que recortar es el ancho de la imagen. En caso contrario, es la altura lo que vamos a reducir.

Pasamos a recortar la imagen, y a redimensionarla, recordad, a nuestra altura objetivo:

doc.resizeCanvas( tImageWidth, tImageHeight, AnchorPosition.MIDDLECENTER );

var outputName = filePath + "/" + fileName;

var fileSuffix = "_" + aspectRatio[j][0] + "x" + aspectRatio[j][1];

if( imageHeight > targetHeight ) {
	var tWidth = Math.round( aspectRatio[j][0] * targetHeight / aspectRatio[j][1] );
	doc.resizeImage( tWidth, targetHeight );
	
	var fileName = getFileName( doc );

	fileSuffix += "_" + targetHeight;
}
	
fileName.name += fileSuffix;
saveFile( doc, fileName );

Aquí hemos introducido el uso de la función resizeCanvas, que como veis toma tres argumentos, que son, ancho, alto y el punto de anclaje. En este capítulo el punto de anclaje lo vamos a dejar fijo

A la vez vamos creando una variable para añadir sufijos al archivo de imagen de salida, con información que luego nos permita identificar la relación de aspecto del mismo. Notad también que la variable fileName, es un pseudo objeto, con dos campos, name y extension.

Para cumplir nuestro segundo objetivo, salvar como JPEG solo si la fuente era JPEG, y salvar en PNG en otro caso, vamos a tener que cambiar la función saveFile que creamos en la anterior entrega:

function saveFile( doc, fileName ) {
    if( fileName.extension == ".jpg" || fileName.extension == ".jpeg" ) {
        fileName.name += ".jpg";
        var jpegOptions = new JPEGSaveOptions();
        jpegOptions.quality = 10;
        jpegOptions.embedColorProfile = true;
        doc.saveAs( File( fileName.name ), jpegOptions, true );
    } else {
        fileName.name += ".png";
        var pngOptions = new PNGSaveOptions();
        pngOptions.compression = 9;
        doc.saveAs( File( fileName.name ), pngOptions, true );
    }
}

Como veis, hay una parte calcada a la del capítulo anterior, la referente a salvar en JPEG. La parte que salva en PNG, es muy parecida, pero llamando a otras funciones.

¡Y eso es todo! Veamos como queda todo junto:

var targetHeight = 512;
var aspectRatio = [ [ 1, 1 ], [ 4, 3 ], [ 16, 9 ] ];

for (var i = 0; i < app.documents.length; i++) {
	var doc = app.documents[i];
	app.activeDocument = doc;
	var imageWidth = doc.width;
	var imageHeight = doc.height;
	var imageAspectRatio = doc.width / doc.height;
    
    for (var j = 0; j < aspectRatio.length; j++) {
        var currentAspectRatio = aspectRatio[j][0] / aspectRatio[j][1];

        var tImageWidth = imageWidth;
        var tImageHeight = imageHeight;

        if(  imageAspectRatio > currentAspectRatio ) {
            tImageWidth = Math.round( aspectRatio[j][0] * imageHeight / aspectRatio[j][1] );
        } else {
            tImageHeight = Math.round( aspectRatio[j][1] * imageWidth / aspectRatio[j][0] );
        }
    
        doc.resizeCanvas( tImageWidth, tImageHeight, anchorPosition );

        var outputName = filePath + "/" + fileName;

        var fileSuffix = "_" + aspectRatio[j][0] + "x" + aspectRatio[j][1];

        if( imageHeight > targetHeight ) {
            var tWidth = Math.round( aspectRatio[j][0] * targetHeight / aspectRatio[j][1] );
            doc.resizeImage( tWidth, targetHeight );
            
            var fileName = getFileName( doc );

            fileName.name += "_" + targetHeight;
        }
            
        fileName.name += fileSuffix;
        saveFile( doc, fileName );
    }
}

function getFileName( doc ) {
    var filePath = doc.path.toString();
    var fileName = doc.name.toString();

    var lastDot = fileName.lastIndexOf( "." );
    if ( lastDot == -1 ) {
        lastDot = fileName.length;
    }

    var fileExtension = fileName.substr( lastDot );
    var fileName = fileName.substr( 0, lastDot );

    var outputName = filePath + "/" + fileName;

    return { name: outputName, extension: fileExtension };
}

function saveFile( doc, fileName ) {
    if( fileName.extension == ".jpg" || fileName.extension == ".jpeg" ) {
        fileName.name += ".jpg";
        var jpegOptions = new JPEGSaveOptions();
        jpegOptions.quality = 10;
        jpegOptions.embedColorProfile = true;
        doc.saveAs( File( fileName.name ), jpegOptions, true );
    } else {
        fileName.name += ".png";
        var pngOptions = new PNGSaveOptions();
        pngOptions.compression = 9;
        doc.saveAs( File( fileName.name ), pngOptions, true );
    }
}

Quiénes somos

En el próximo capítulo introduciremos una nueva herramienta, de la que ya hablamos en el primer capitulo, el plugin para Photoshop que nos permite volcar nuestras acciones en la interfaz como una serie de comandos JavaScript, y haremos uso de ella para personalizar lo que queremos hacer. También hablaremos un poco de cuadro de dialogo personalizados y tal. Porque a veces necesitamos la interacción humana. Ese calor humano que nuestro animal, ese que llevamos dentro nos exige de vez en cuando (a algunos más a menudo que otros, y a otros casi nunca).