// Cambiar tamaño de imagen // (c) 2024 DASOTA // Script para Directory Opus // Metadatos y configuraciones del script function OnInit(initData) { initData.name = "Image Resize"; // Nombre del script initData.version = "1.1"; initData.copyright = "(c) 2024 DASOTA"; initData.desc = "Cambiar tamaño de imagen"; // Descripción initData.default_enable = true; initData.min_version = "13.0"; initData.config_desc = DOpus.Create.Map(); initData.config_desc("idioma") = "Idioma predeterminado"; initData.config.idioma = DOpus.Create.Vector(0, "Español", "English"); // Idioma predeterminado (0 = Español, 1 = English) } // Añadir comando ImageResize function OnAddCommands(addCmdData) { var cmd = addCmdData.AddCommand(); cmd.name = "ImageResize"; // Nombre del comando para ejecutarse desde un botón cmd.method = "Onresize"; cmd.desc = "Cambiar tamaño de imagen"; // Descripción cmd.label = "Image Resize"; // Título del script cmd.template = "dialog1"; // Nombre del objeto Diálogo en la pestaña Recursos cmd.hide = false; cmd.icon = "script"; } // ============================================================================================================================================= // FUNCIÓN PRINCIPAL // ============================================================================================================================================= function Onresize(scriptCmdData) { var tab = scriptCmdData.func.sourcetab; if (tab.selected_files.count === 0 || tab.selected_files(0).metadata != "image") { // Si no existe archivo seleccionado, o si existe pero no es una imagen... DOpus.Output("No existe ninguna imagen válida seleccionada"); // Mostrar mensaje en consola return; // E interumpir análisis del resto del código } // Variables globales, pues además de usadas en la función "cargarArchivos()", son necesárias en el botón "Copiar al portapapeles" var item = null; var dimensiones_label = ""; var cmd = DOpus.Create.Command(); // Crear objeto comando para ejecutar comandos var sysinfo = DOpus.Create.SysInfo(); // Crear objeto SysInfo() para calcular dimensiones del monitor var monitor = sysinfo.Monitors(0); // 0 = Monitor principal var dlg = scriptCmdData.func.Dlg(); dlg.title = "Image Resize - Directory Opus"; // Nombre de la barra Título dlg.template = 'dialog1'; // Nombre del objeto Diálogo en la pestaña Recursos dlg.detach = true; // Poder interactuar con el Diálogo dlg.Create // Crear el diálogo traduccion(dlg); // Llamar a la función traduccion(dlg) cargarArchivo(); // Llamar a la función cargarArchivo() // FUNCIÓN CARGAR ARCHIVO ================================================================================================================== function cargarArchivo() { var tab = DOpus.listers.lastactive.activetab; // Obtener el tab activo en ese momento var seleccion = tab.selected; // Elementos seleccionados if (seleccion.count === 0) return; var extensiones = ["jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp"]; // Extensiones de imágenes válidas // Filtrar solo imágenes var imagenesSeleccionadas = []; for (var i = 0; i < seleccion.count; i++) { var f = seleccion(i); var ext = f.ext.toLowerCase().replace('.', ''); for (var j = 0; j < extensiones.length; j++) { if (ext === extensiones[j]) { imagenesSeleccionadas.push(f); break; } } } if (imagenesSeleccionadas.length === 0) { DOpus.Output("No existe ninguna imagen seleccionada"); return; } // SI UNA ÚNICA IMAGEN SELECCIONADA if (imagenesSeleccionadas.length === 1) { item = imagenesSeleccionadas[0]; // Mostrar datos de imagen selecionada //DOpus.Output('Seleccionada: ' + item.name); // Depuración dlg.Control("nombre").value = item.name_stem; // Nombre de archivo (sin extensión) dlg.Control("comentario").value = item.metadata.image.imagedesc || ""; // Comentario dlg.Control("fecha_captura").label = item.metadata.image.datetaken || ""; // Fecha de captura dlg.Control("resolucion").label = item.metadata.image_text.picsize || ""; // Resolución (Ancho x Alto x Profundidad). 'item.metadata.image.picsize' muestra el valor al estilo 5792395746373 dlg.Control("relacion").label = (item.metadata.image.aspectratio).toFixed(2); // Relación de aspecto (redondeada a 2 lugares decimales) var reso = item.metadata.image.picresx || ""; // Puntos por pulgada original (dpi) dlg.Control("ppp").label = reso.toString().replace("dpi", "ppp"); // Reemplazar "dpi" por "ppp" dlg.Control("tamano").label = item.size.fmt || ""; // Tamaño (sin '.fmt' el tamaño se muestra al estilo 454345324) dlg.Control("miniatura").label = DOpus.LoadImage(item.realpath); // Miniatura dlg.Control("ancho").value = item.metadata.image.picwidth || ""; // Ancho dlg.Control("alto").value = item.metadata.image.picheight || ""; // Alto dlg.Control("nombre_final").value = dlg.Control("nombre").value; // Nombre final = Nombre dlg.Control("comentario_final").value = dlg.Control("comentario").value; // Comentario final = Comentario // Convertir dimensiones, de pulgadas a CM if (item.metadata && item.metadata.image && item.metadata.image.picphysx && item.metadata.image.picphysy) { // Verificar si la imagen tiene metadatos, entre ellos dimensiones físicas (picphysx y picphysy) (ancho y alto em pulgadas) var ancho_fisico_str = item.metadata.image.picphysx.split("\"")[0]; // Eliminarle las comillas a dimensiones (Ej: 14.8" > 14.8) var alto_fisico_str = item.metadata.image.picphysy.split("\"")[0]; var ancho_cm = (parseFloat(ancho_fisico_str) * 2.54).toFixed(2); // Convertir el texto "14.8" a número usando parseFloat(...), luego lo convierte a centímetros (* 2.54) y lo redondea a 2 cifras decimales ( .toFixed(2) ) var alto_cm = (parseFloat(alto_fisico_str) * 2.54).toFixed(2); dimensiones_label = ancho_cm + " x " + alto_cm + " cm"; dlg.Control("dimensiones").label = dimensiones_label; // Dimensiones en CM } else { dlg.Control("dimensiones").label = ""; } // Ruta de la imagen para usarla en el botón guardar rutasImagenes = [item.realpath]; // SI VARIAS IMÁGENES SELECCIONADAS } else { // Mostrar mensaje < varias imágenes > en los siguientes controles dlg.Control("nombre").value = "< varias imágenes >"; dlg.Control("comentario").value = "< varias imágenes >"; dlg.Control("nombre_final").value = "< mantener nombres >"; dlg.Control("comentario_final").value = "< mantener comentarios >"; // Mostrar los siguientes controles vacíos dlg.Control("fecha_captura").label = ""; dlg.Control("tamano").label = ""; dlg.Control("resolucion").label = ""; dlg.Control("dimensiones").label = ""; dlg.Control("relacion").label = ""; dlg.Control("ppp").label = ""; dlg.Control("miniatura").label = ""; dlg.Control("ancho").value = "1024"; dlg.Control("alto").value = "768"; // Rutas de todas las imágenes para usarlas en el botón guardar rutasImagenes = []; for (var k = 0; k < imagenesSeleccionadas.length; k++) { rutasImagenes.push(imagenesSeleccionadas[k].realpath); } } // Mostrar los valores de estos controles siempre (con una o varias imágenes seleccionadas) dlg.Control("ruta").label = imagenesSeleccionadas[0].path; // Ruta de la carpeta contenedora, no incluye nombres de imágenes dlg.Control("predeterminados").SelectItem(4); // Selección automática de la lista 'Predeterminados' dlg.Control("monitor").value = monitor.width + " x " + monitor.height; // Ancho y alto del monitor dlg.Control("monitor").enabled = false; // Comenzar con el control desactivado } // Fin de cargarArchivo() dlg.Show(); // Mostrar el diálogo ========================================================================================================== while (true) { // Mientras verdadero (usuario no presione el botón Cerrar)... msg = dlg.GetMsg(); // Mostrar el diálogo if (!msg.result) break; // Cuando falso (usuario presione el botón Cerrar), interrumpir el ciclo y cerrar el diálogo // Función activar controles (usada en los botones pixeles, porciento, predetrminados y monitor) function activar(dlg, controles) { for (var i = 0; i < controles.length; i++) { dlg.Control(controles[i]).enabled = true; } } // Función desactivar controles (usada en los botones pixeles, porciento, predetrminados y monitor) function desactivar(dlg, controles) { for (var i = 0; i < controles.length; i++) { dlg.Control(controles[i]).enabled = false; } } // Valor de la variable "relacion" (usada en el botón guardar) if (dlg.Control("mantener_relacion").value) { var relacion = "PRESERVEASPECTRATIO"; } else { var relacion = ""; } // EVENTOS ============================================================================================================================= // Clic sobre miniatura de imagen if ((msg.event === "click" || msg.event === "click_up") && msg.control) { if (msg.control === "miniatura") { cmd.RunCommand("Show AUTOFILELIST"); // Show = Abrir con el visor de Opus. AUTOFILELIST = Obtener rutas de imágenes aledañas para poder navegar a través de ellas si se desea } } // EVENTOS CLIC ======================================================================================================================== if (msg.event == "click") { switch (msg.control) { case "btn_pixeles": activar(dlg, ["ancho_t", "ancho", "alto_t", "alto", "btn_mitad", "btn_doble"]); // Activar estos controles desactivar(dlg, ["predeterminados", "monitor"]); // Desactivar estos controles if (rutasImagenes.length === 1) { // Si una sola imagen seleccionada, usar sus dimensiones dlg.Control("ancho").value = item.metadata.image.picwidth; dlg.Control("alto").value = item.metadata.image.picheight; } else { // Si varias imágenes seleccionadas, usar los siguientes valores predeterminados dlg.Control("ancho").value = 1024; dlg.Control("alto").value = 768; } break; case "btn_porciento": activar(dlg, ["ancho_t", "ancho", "alto_t", "alto", "btn_mitad", "btn_doble"]); // Activar estos controles desactivar(dlg, ["predeterminados", "monitor"]); // Desactivar estos controles dlg.Control("ancho").value = 100; // Ancho: 100% dlg.Control("alto").value = 100; // Alto: 100% break; case "btn_predeterminados": activar(dlg, ["predeterminados"]); // Activar estos controles desactivar(dlg, ["ancho_t", "ancho", "alto_t", "alto", "btn_mitad", "btn_doble", "monitor"]); // Desactivar estos controles var ancho_array = [320, 640, 800, 1024, 1280, 1920, 3840, 5120]; // Anchos var alto_array = [240, 480, 600, 768, 720, 1080, 2160, 2880]; // Altos dlg.Control("ancho_final").value = ancho_array[dlg.Control("predeterminados").value]; // Según el elemento seleccionado en la lista (0 a 7), así será el valor seleccionado en el array (1-8) dlg.Control("alto_final").value = alto_array[dlg.Control("predeterminados").value]; // Según el elemento seleccionado en la lista (0 a 7), así será el valor seleccionado en el array (1-8) break; case "btn_monitor": activar(dlg, ["monitor"]); desactivar(dlg, ["ancho_t", "ancho", "alto_t", "alto", "btn_mitad", "btn_doble", "predeterminados"]); dlg.Control("ancho_final").value = monitor.width; // Ancho del monitor dlg.Control("alto_final").value = monitor.height; // Alto del monitor break; case "btn_mitad": dlg.Control("ancho").value = (dlg.Control("ancho").value / 2).toFixed(); // Ancho 50% dlg.Control("alto").value = (dlg.Control("alto").value / 2).toFixed(); // Alto 50% break; case "btn_doble": dlg.Control("ancho").value = dlg.Control("ancho").value * 2; // Ancho 200% dlg.Control("alto").value = dlg.Control("alto").value * 2; // Alto 200% break; // Botones Atrás y Adelante ==================================================================================================== case "btn_atras": case "btn_adelante": var count = 0; // Contador para evitar bucle infinito var max_items = tab.stats.items; // Número total de elementos en la carpeta var item; // Variable para almacenar el ítem seleccionado do { // Ejecutar comando para seleccionar archivo anterior o siguiente if (cmd.RunCommand('Select ' + (msg.control == 'btn_atras' ? 'PREV' : 'NEXT'))) { DOpus.Delay(10); // Esperar 10 ms para que la selección se actualice item = tab.GetFocusItem(); // Obtener el elemento actualmente enfocado } else { break; // Si no se pudo seleccionar (límite alcanzado), salir del bucle } count++; // Incrementar contador para evitar loops infinitos if (item) { var ext = item.ext.toLowerCase().replace('.', ''); // Obtener extensión en minúsculas, y sin el punto // Lista de extensiones válidas para considerar imágenes var extensiones = ["jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp"]; var esImagen = false; // Buscar si la extensión está en la lista de imágenes for (var i = 0; i < extensiones.length; i++) { if (ext === extensiones[i]) { esImagen = true; // Encontró una extensión válida break; // Salir del ciclo, ya no hace falta seguir buscando } } if (item.is_dir || !esImagen || !item.metadata || !item.metadata.image) { // Si selección es una carpeta, no es una imagen, o es una imagen, pero sin metadatos... item = null; // Invalidar ítem y saltar al siguiente para continuar buscando } } } while (count <= max_items && !item); // Continuar mientras no se encuentre imagen válida y no se supere el total de archivos existentes if (item) { // Si se encuentra una imagen válida... cargarArchivo(); // Llamar a la función cargarArchivo() para cargar los datos en los controles } break; case "btn_copiar": // Botón copiar al portapapeles ============================================================================= var info = []; for (var i = 0; i < rutasImagenes.length; i++) { var ruta = rutasImagenes[i]; var archivo = DOpus.FSUtil.GetItem(ruta); // Obtener el objeto del archivo var meta = archivo.metadata.image; // Metadatos del archivo info.push("Nombre:\t\t" + archivo.name_stem); info.push("Comentario:\t\t" + (meta.imagedesc || "")); info.push("Ruta:\t\t\t" + archivo.path); info.push("Fecha de captura:\t" + (meta.datetaken || "")); info.push("Resolución:\t\t" + (archivo.metadata.image_text.picsize || "")); info.push("Dimensiones:\t\t" + dimensiones_label); info.push("Relación de aspecto:\t" + (meta.aspectratio ? meta.aspectratio.toFixed(2) : "")); // Si la imagen tiene "meta.aspectratio", redondearla a 2 lugares decimales, sino "" info.push("PPP:\t\t\t" + (meta.picresx.replace("dpi", "ppp") || "")); // Puntos por pulgada (reemplazar "dpi" por "ppp") info.push("Tamaño:\t\t" + archivo.size.fmt); info.push(""); // Línea en blanco entre imágenes } var texto = info.join("\r\n"); DOpus.SetClip(texto); // Copiar datos al portapapeles dlg.Control("mensaje_copiado").label = "Copiado!"; // Mostrar mensaje DOpus.Delay(2000); // Espera 2 segundos dlg.Control("mensaje_copiado").label = ""; // Ocultar mensaje break; case "btn_guardar": // Botón guardar =========================================================================================== var anchoFinal = dlg.Control("ancho_final").value; var altoFinal = dlg.Control("alto_final").value; var comentario = dlg.Control("comentario").value; var nombre = dlg.Control("nombre").value; // SI UNA ÚNICA IMAGEN SELECCIONADA if (rutasImagenes.length === 1) { cmd.RunCommand('Image WIDTH="' + anchoFinal + '" HEIGHT="' + altoFinal + '" "' + relacion + '" HERE REPLACE'); // Ancho de la imagen cmd.RunCommand('SetAttr META "imagedesc:' + comentario + '"'); // Comentario cmd.RunCommand('Rename IGNOREEXT PATTERN="*" TO="' + nombre + '"'); // Renombrado dlg.EndDlg(1); break; } // SI VARIAS IMÁGENES SELECCIONADAS // Cambiar tamaño y comentario, archivo por archivo for (var i = 0; i < rutasImagenes.length; i++) { cmd.RunCommand('Image WIDTH="' + anchoFinal + '" HEIGHT="' + altoFinal + '" "' + relacion + '" HERE REPLACE FILE="' + rutasImagenes[i] + '"'); if (comentario !== "< varias imágenes >") { // Si el campo comentario = < varias imágenes >, ignorar este campo del guardado (no guardar), si no... cmd.RunCommand('SetAttr FILE="' + rutasImagenes[i] + '" META "imagedesc:' + comentario + '"'); } } // Renombrar todos los archivos de una vez para enumerarlos, Ejemplo: Archivo (01), Archivo (02), etc. if (nombre !== "< varias imágenes >") { // Si el campo nombre = < varias imágenes >, ignorar este campo del guardado (no guardar), si no... cmd.ClearFiles(); for (var i = 0; i < rutasImagenes.length; i++) { cmd.AddFile(rutasImagenes[i]); } cmd.RunCommand('Rename IGNOREEXT PATTERN * TO "' + nombre + ' ([#2])" NUMBER'); } dlg.EndDlg(1); break; case 'btn_cancelar': // Botón cancelar ========================================================================================= dlg.EndDlg(0); // Cerrar ventana (devolviendo el valor 0 = botón cancelar) break; } // EVENTOS DE EDICIÓN ================================================================================================================== } else if (msg.event == "editchange") { switch (msg.control) { case "nombre": dlg.Control("nombre_final").value = dlg.Control("nombre").value; break; case "comentario": dlg.Control("comentario_final").value = dlg.Control("comentario").value; break; case "ancho": // Si botón "Pixeles" seleccionado if (dlg.Control("btn_pixeles").value) { dlg.Control("ancho_final").value = dlg.Control("ancho").value; // El valor del control ancho_final = al valor del control ancho } // Si botón "Por ciento" seleccionado if (dlg.Control("btn_porciento").value) { if (rutasImagenes.length === 1) { // Si una única imagen seleccionada... dlg.Control("ancho_final").value = (item.metadata.image.picwidth * dlg.Control("ancho").value / 100).toFixed(); // El valor del control ancho_final es calculado teniendo en cuenta el valor del control ancho (%) y el ancho de la imagen } else { // Si varias imágenes seleccionadas... dlg.Control("ancho_final").value = (1024 * dlg.Control("ancho").value / 100).toFixed(); // El valor del control ancho_final es calculado teniendo en cuenta el valor del control ancho (%) y un ancho predeterminado (1024) } } // Si la opción mantener relación está seleccionada y el campo ancho está enfocado, al modificar el campo ancho, modificar también el campo alto... if (relacion && dlg.Control("ancho").focus) { if (rutasImagenes.length === 1) { // Si una única imagen seleccionada... newalto = Math.round(dlg.Control("ancho").value / item.metadata.image.aspectratio); // Calcular el valor del campo alto en función de la relación de aspecto de la imagen } else { // Si varias imágenes seleccionadas... newalto = Math.round(dlg.Control("ancho").value / (1024 / 768) ); // Calcular el valor del campo alto en función de relación de aspecto predeterminada (1024 / 768) } dlg.Control("alto").value = newalto; // Asignar el valor calculado al campo alto dlg.Control("alto_final").value = newalto; // Asignar el valor calculado al campo alto_final } break; case "alto": // Si botón "Pixeles" seleccionado if (dlg.Control("btn_pixeles").value) { dlg.Control("alto_final").value = dlg.Control("alto").value; // El valor del control alto_final = al valor del control alto } // Si botón "Por ciento" seleccionado if (dlg.Control("btn_porciento").value) { if (rutasImagenes.length === 1) { // Si una única imagen seleccionada... dlg.Control("alto_final").value = (item.metadata.image.picheight * dlg.Control("alto").value / 100).toFixed(); // El valor del control alto_final es calculado teniendo en cuenta el valor del control alto (%) y el alto de la imagen } else { // Si varias imágenes seleccionadas... dlg.Control("alto_final").value = (768 * dlg.Control("alto").value / 100).toFixed(); // El valor del control alto_final es calculado teniendo en cuenta el valor del control alto (%) y un alto predeterminado (768) } } // Si la opción mantener relación está seleccionada y el campo alto está enfocado, al modificar el campo alto, modificar también el campo ancho... if (relacion && dlg.Control("alto").focus) { if (rutasImagenes.length === 1) { // Si una única imagen seleccionada... newancho = Math.round(dlg.Control("alto").value * item.metadata.image.aspectratio); // Calcular el valor del campo ancho en función de la relación de aspecto de la imagen } else { // Si varias imágenes seleccionadas... newancho = Math.round(dlg.Control("alto").value * (1024 / 768) ); // Calcular el valor del campo ancho en función de relación de aspecto predeterminada (1024 / 768) } dlg.Control("ancho").value = newancho; // Asignar el valor calculado al campo ancho dlg.Control("ancho_final").value = newancho; // Asignar el valor calculado al campo ancho_final } break; } // Final del switch (msg.control) } else if (msg.event == "selchange" && msg.control == "predeterminados") { // Actualización de los valores del campo 'Prededeterminados' en 'Ancho final' y 'Alto final' dlg.Control("ancho_final").value = ancho_array[dlg.Control("predeterminados").value] dlg.Control("alto_final").value = alto_array[dlg.Control("predeterminados").value] } //DOpus.Output("Evento: " + msg.event + ", Control: " + msg.control) // Mostrar, en el registro de Opus, eventos y controles que se que se activan } } // ============================================================================================================================================= // FUNCIÓN TRADUCCIÓN // ============================================================================================================================================= function traduccion(dlg) { // 1. DICCIONARIO var idiomas = { 1: { // Inglés // Archivo grupo_archivo: "File", nombre_t: "Name:", comentario_t: "Comment:", ruta_t: "Path:", fecha_captura_t: "Capture:", resolucion_t: "Resolution:", dimensiones_t: "Dimensions:", relacion_t: "Ratio:", ppp_t: "DPI:", tamano_t: "Size:", // Método resize grupo_metodo: "Resize Method", btn_pixeles: "Pixels", btn_porciento: "Percent", btn_predeterminados: "Presets", btn_monitor: "Monitor", // Tamaño grupo_tamano: "Size", ancho_t: "Width:", alto_t: "Height:", btn_mitad: "Half", btn_doble: "Double", mantener_relacion: "Keep aspect ratio", // Resultado grupo_resultado: "Result", nombre_final_t: "Name:", comentario_final_t: "Comment:", tamano_final_t: "SIZE", // Botones del diálogo btn_copiar: "Copy metadata", btn_guardar: "Save", btn_cancelar: "Cancel" } }; // 2. TRADUCCIÓN DE CONTROLES var idioma = Script.config.idioma; // idioma = 0, si Español; idioma = 1, si English if (idioma != 1) return; // Solo aplica la traducción en caso de idioma = 1 var tra = idiomas[idioma]; // En este caso aplica el diccionario idiomas [1] (English) for (var clave in tra) { var control = dlg.Control(clave); if (control) { control.label = tra[clave]; } } } // Fin de traduccion() ==SCRIPT RESOURCES