Skip to main content
DavidBoileau
Participant
December 7, 2023
Answered

Batch enlarge images based on longest side using Preserve Detail 2.0

  • December 7, 2023
  • 2 replies
  • 882 views

Hello Everyone,

 

I'm trying to batch upscale images to a "longest side" using "Preserve Details 2.0." These a shots of products for an online retailer.

 

I have tried many ways to do this and it's proving impossible. Am I missing something? Image processor does not use "Preserve Details 2.0", Ligthroom (v7) does not preserve the transparency of the tiffs, I can't tell photoshop to scale longest side in actions, and I lost a full day trying to figure out scripting with the script listener and ChatGPT helping me.

 

All I want to do is to batch upscale a series of images (tiffs,pngs) using Preserve Details 2.0 to a "Longest Side".

 

What am I missing? It surely can't be that hard. I must be missing something.

 

Thanks in advance.

This topic has been closed for replies.
Correct answer Stephen Marsh

I'd expect Automate > Fit Image to use the interpolation method set in Preferences > General (but I don't know if it does).

 

EDIT: Fit Image doesn't use the interpolation from preferences, the code is old and the DOM only supports basic Preserve Details, so AM code would be needed:

 

    // resize the image using a good conversion method while keeping the pixel resolution
    // and the aspect ratio the same
    app.activeDocument.resizeImage(newWidth, newHeight, resolution, ResampleMethod.BICUBIC);

 

You can use a conditional action:

 

 

A very quick, rough'n'ready script version of the action:

 

/*
// Enable Preserve Details 2 in Preferences/Technology Previews
var idset = stringIDToTypeID( "set" );
    var desc2444 = new ActionDescriptor();
    var idnull = stringIDToTypeID( "null" );
        var ref135 = new ActionReference();
        var idproperty = stringIDToTypeID( "property" );
        var idexperimentalFeatures = stringIDToTypeID( "experimentalFeatures" );
        ref135.putProperty( idproperty, idexperimentalFeatures );
        var idapplication = stringIDToTypeID( "application" );
        var idordinal = stringIDToTypeID( "ordinal" );
        var idtargetEnum = stringIDToTypeID( "targetEnum" );
        ref135.putEnumerated( idapplication, idordinal, idtargetEnum );
    desc2444.putReference( idnull, ref135 );
    var idto = stringIDToTypeID( "to" );
        var desc2445 = new ActionDescriptor();
        var idexpFeatureDeepUpscale = stringIDToTypeID( "expFeatureDeepUpscale" );
        desc2445.putBoolean( idexpFeatureDeepUpscale, true );
    var idexperimentalFeatures = stringIDToTypeID( "experimentalFeatures" );
    desc2444.putObject( idto, idexperimentalFeatures, desc2445 );
executeAction( idset, desc2444, DialogModes.NO );
*/

// Pixel Width and Height Value
conditionalResize(1000, 1000);

function conditionalResize(W, H) {
    if (activeDocument.width > activeDocument.height) {
        var idimageSize = stringIDToTypeID("imageSize");
        var desc2420 = new ActionDescriptor();
        var idwidth = stringIDToTypeID("width");
        var idpixelsUnit = stringIDToTypeID("pixelsUnit");
        desc2420.putUnitDouble(idwidth, idpixelsUnit, W); // Width in px
        var idscaleStyles = stringIDToTypeID("scaleStyles");
        desc2420.putBoolean(idscaleStyles, true);
        var idconstrainProportions = stringIDToTypeID("constrainProportions");
        desc2420.putBoolean(idconstrainProportions, true);
        var idinterfaceIconFrameDimmed = stringIDToTypeID("interfaceIconFrameDimmed");
        var idinterpolationType = stringIDToTypeID("interpolationType");
        var iddeepUpscale = stringIDToTypeID("deepUpscale");
        desc2420.putEnumerated(idinterfaceIconFrameDimmed, idinterpolationType, iddeepUpscale);
        var idnoise = stringIDToTypeID("noise");
        desc2420.putInteger(idnoise, 0); // Noise value
        executeAction(idimageSize, desc2420, DialogModes.NO);
    } else {
        var idimageSize = stringIDToTypeID("imageSize");
        var desc2413 = new ActionDescriptor();
        var idheight = stringIDToTypeID("height");
        var idpixelsUnit = stringIDToTypeID("pixelsUnit");
        desc2413.putUnitDouble(idheight, idpixelsUnit, H); // Height in px
        var idconstrainProportions = stringIDToTypeID("constrainProportions");
        desc2413.putBoolean(idconstrainProportions, true);
        var idinterfaceIconFrameDimmed = stringIDToTypeID("interfaceIconFrameDimmed");
        var idinterpolationType = stringIDToTypeID("interpolationType");
        var iddeepUpscale = stringIDToTypeID("deepUpscale");
        desc2413.putEnumerated(idinterfaceIconFrameDimmed, idinterpolationType, iddeepUpscale);
        var idnoise = stringIDToTypeID("noise");
        desc2413.putInteger(idnoise, 0); // Noise value
        executeAction(idimageSize, desc2413, DialogModes.NO);
    }
}

 

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html

 

2 replies

Bojan Živković11378569
Community Expert
Community Expert
December 8, 2023

"I can't tell photoshop to scale longest side in actions"

 

Actually you can using conditional action. There are available conditions based on image orientation.

Stephen Marsh
Community Expert
Stephen MarshCommunity ExpertCorrect answer
Community Expert
December 7, 2023

I'd expect Automate > Fit Image to use the interpolation method set in Preferences > General (but I don't know if it does).

 

EDIT: Fit Image doesn't use the interpolation from preferences, the code is old and the DOM only supports basic Preserve Details, so AM code would be needed:

 

    // resize the image using a good conversion method while keeping the pixel resolution
    // and the aspect ratio the same
    app.activeDocument.resizeImage(newWidth, newHeight, resolution, ResampleMethod.BICUBIC);

 

You can use a conditional action:

 

 

A very quick, rough'n'ready script version of the action:

 

/*
// Enable Preserve Details 2 in Preferences/Technology Previews
var idset = stringIDToTypeID( "set" );
    var desc2444 = new ActionDescriptor();
    var idnull = stringIDToTypeID( "null" );
        var ref135 = new ActionReference();
        var idproperty = stringIDToTypeID( "property" );
        var idexperimentalFeatures = stringIDToTypeID( "experimentalFeatures" );
        ref135.putProperty( idproperty, idexperimentalFeatures );
        var idapplication = stringIDToTypeID( "application" );
        var idordinal = stringIDToTypeID( "ordinal" );
        var idtargetEnum = stringIDToTypeID( "targetEnum" );
        ref135.putEnumerated( idapplication, idordinal, idtargetEnum );
    desc2444.putReference( idnull, ref135 );
    var idto = stringIDToTypeID( "to" );
        var desc2445 = new ActionDescriptor();
        var idexpFeatureDeepUpscale = stringIDToTypeID( "expFeatureDeepUpscale" );
        desc2445.putBoolean( idexpFeatureDeepUpscale, true );
    var idexperimentalFeatures = stringIDToTypeID( "experimentalFeatures" );
    desc2444.putObject( idto, idexperimentalFeatures, desc2445 );
executeAction( idset, desc2444, DialogModes.NO );
*/

// Pixel Width and Height Value
conditionalResize(1000, 1000);

function conditionalResize(W, H) {
    if (activeDocument.width > activeDocument.height) {
        var idimageSize = stringIDToTypeID("imageSize");
        var desc2420 = new ActionDescriptor();
        var idwidth = stringIDToTypeID("width");
        var idpixelsUnit = stringIDToTypeID("pixelsUnit");
        desc2420.putUnitDouble(idwidth, idpixelsUnit, W); // Width in px
        var idscaleStyles = stringIDToTypeID("scaleStyles");
        desc2420.putBoolean(idscaleStyles, true);
        var idconstrainProportions = stringIDToTypeID("constrainProportions");
        desc2420.putBoolean(idconstrainProportions, true);
        var idinterfaceIconFrameDimmed = stringIDToTypeID("interfaceIconFrameDimmed");
        var idinterpolationType = stringIDToTypeID("interpolationType");
        var iddeepUpscale = stringIDToTypeID("deepUpscale");
        desc2420.putEnumerated(idinterfaceIconFrameDimmed, idinterpolationType, iddeepUpscale);
        var idnoise = stringIDToTypeID("noise");
        desc2420.putInteger(idnoise, 0); // Noise value
        executeAction(idimageSize, desc2420, DialogModes.NO);
    } else {
        var idimageSize = stringIDToTypeID("imageSize");
        var desc2413 = new ActionDescriptor();
        var idheight = stringIDToTypeID("height");
        var idpixelsUnit = stringIDToTypeID("pixelsUnit");
        desc2413.putUnitDouble(idheight, idpixelsUnit, H); // Height in px
        var idconstrainProportions = stringIDToTypeID("constrainProportions");
        desc2413.putBoolean(idconstrainProportions, true);
        var idinterfaceIconFrameDimmed = stringIDToTypeID("interfaceIconFrameDimmed");
        var idinterpolationType = stringIDToTypeID("interpolationType");
        var iddeepUpscale = stringIDToTypeID("deepUpscale");
        desc2413.putEnumerated(idinterfaceIconFrameDimmed, idinterpolationType, iddeepUpscale);
        var idnoise = stringIDToTypeID("noise");
        desc2413.putInteger(idnoise, 0); // Noise value
        executeAction(idimageSize, desc2413, DialogModes.NO);
    }
}

 

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html

 

Stephen Marsh
Community Expert
Community Expert
December 10, 2023

Here's an updated version of the File > Automate > Fit Image action recordable plugin script using Preserve Details 2.0 interpolation:

 

// c2008 Adobe Systems, Inc. All rights reserved.
// Written by Ed Rose
// based on the ADM Fit Image by Charles A. McBrian from 1997
// edited by Mike Hale added option to avoid resize on images already smaller than target size
// Don't Enlarge option in Fit Image command ignored if image is not 72ppi fixed by Mike Hale

// 2023 Update by Stephen Marsh to Use Preserve Details 2.0 interpolation

/*
@@@BUILDINFO@@@ Fit Image.jsx 1.0.0.24B
*/


/* Special properties for a JavaScript to enable it to behave like an automation plug-in, the variable name must be exactly
   as the following example and the variables must be defined in the top 1000 characters of the file

// BEGIN__HARVEST_EXCEPTION_ZSTRING
<javascriptresource>
<name>$$$/JavaScripts/FitImagePD2/Name=Fit Image - Preserve Details 2.0...</name>
<menu>automate</menu>
<enableinfo>true</enableinfo>
<eventid>809cbbe4-4591-47c9-a55b-a23af4692ffa</eventid>
<terminology><![CDATA[<< /Version 1
                         /Events <<
                          /809cbbe4-4591-47c9-a55b-a23af4692ffa [($$$/AdobePlugin/FitImagePD2/Name=Fit Image - Preserve Details 2.0) /imageReference <<
	                       /width [($$$/AdobePlugin/FitImagePD2/Width=width) /pixelsUnit]
	                       /height [($$$/AdobePlugin/FitImagePD2/Height=height) /pixelsUnit]
	                       /limit [($$$/AdobePlugin/FitImagePD2/limit=Don't Enlarge) /boolean]
                          >>]
                         >>
                      >> ]]></terminology>
</javascriptresource>
// END__HARVEST_EXCEPTION_ZSTRING
*/


// enable double clicking from the Macintosh Finder or the Windows Explorer
#target photoshop

// debug level: 0-2 (0:disable, 1:break on error, 2:break at beginning)
// $.level = 2;
// debugger; // launch debugger on next line

// on localized builds we pull the $$$/Strings from a .dat file, see documentation for more details
$.localize = true;

var isCancelled = true; // assume cancelled until actual resize occurs

// the main routine
// the FitImagePD2 object does most of the work
try {

	// create our default params
	var sizeInfo = new SizeInfo();

	GlobalVariables();
	CheckVersion();

	var gIP = new FitImagePD2();

	if (DialogModes.ALL == app.playbackDisplayDialogs) {
		gIP.CreateDialog();
		gIP.RunDialog();
	} else {
		gIP.InitVariables();
		ResizeTheImage(sizeInfo.width.value, sizeInfo.height.value);
	}

	if (!isCancelled) {
		SaveOffParameters(sizeInfo);
	}

}


// Lot's of things can go wrong
// Give a generic alert and see if they want the details
catch (e) {
	if (DialogModes.NO != app.playbackDisplayDialogs) {
		alert(e + " : " + e.line);
	}
}



// restore the dialog modes
app.displayDialogs = gSaveDialogMode;

isCancelled ? 'cancel' : undefined;

//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////

function ResizeTheImage(width, height) {
	var oldPref = app.preferences.rulerUnits;
	var docWidth;
	var docHeight;
	var docRatio;
	var newWidth;
	var newHeight;
	var resolution = app.activeDocument.resolution;
	var limit = sizeInfo.limit;

	app.preferences.rulerUnits = Units.PIXELS; // save old preferences

	// original width, height
	docWidth = (1.0 * app.activeDocument.width * resolution) / 72.0; // decimal inches assuming 72 dpi (used in docRatio)
	docHeight = (1.0 * app.activeDocument.height * resolution) / 72.0; // ditto

	if (docWidth < 1.0 || docHeight < 1.0)
		return true; // error

	if (width < 1 || height < 1)
		return true; // error

	if (limit && (app.activeDocument.width.as('px') <= width && app.activeDocument.height.as('px') <= height)) {
		app.preferences.rulerUnits = oldPref; // restore old prefs
		isCancelled = false; // if get here, definitely executed
		return false; // no error
	}

	docRatio = docWidth / docHeight; // decimal ratio of original width/height

	newWidth = width;
	newHeight = ((1.0 * width) / docRatio); // decimal calc

	if (newHeight > height) {
		newWidth = docRatio * height; // decimal calc
		newHeight = height;
	}

//////////////////////////////////////////////////////////////
	preserveDetails2(newWidth, newHeight, resolution, 0);
	app.preferences.rulerUnits = oldPref; // restore old prefs
	isCancelled = false; // if get here, definitely executed
	return false; // no error
}
//////////////////////////////////////////////////////////////

// created in
function SaveOffParameters(sizeInfo) {

	// save off our last run parameters
	var d = objectToDescriptor(sizeInfo, strMessage);
	app.putCustomOptions("809cbbe4-4591-47c9-a55b-a23af4692ffa", d);
	app.playbackDisplayDialogs = DialogModes.ALL;

	// save off another copy so Photoshop can track them corectly
	var dd = objectToDescriptor(sizeInfo, strMessage);
	app.playbackParameters = dd;
}

function GlobalVariables() {

	// a version for possible expansion issues
	gVersion = 1.12;

	gMaxResize = 300000;

	// remember the dialog modes
	gSaveDialogMode = app.displayDialogs;
	app.displayDialogs = DialogModes.NO;
	gInAlert = false;

	// all the strings that need to be localized
	strTitle = localize("$$$/JavaScript/FitImagePD2/Title=Fit Image - Preserve Details 2.0");
	strConstrainWithin = localize("$$$/JavaScript/FitImagePD2/ConstrainWithin=Constrain Within");
	strTextWidth = localize("$$$/JavaScripts/FitImagePD2/Width=&Width:");
	strTextHeight = localize("$$$/JavaScripts/FitImagePD2/Height=&Height:");
	strTextPixels = localize("$$$/JavaScripts/FitImagePD2/Pixels=pixels");
	strButtonOK = localize("$$$/JavaScripts/FitImagePD2/OK=OK");
	strButtonCancel = localize("$$$/JavaScripts/FitImagePD2/Cancel=Cancel");
	strTextSorry = localize("$$$/JavaScripts/FitImagePD2/Sorry=Sorry, Dialog failed");
	strTextInvalidType = localize("$$$/JavaScripts/FitImagePD2/InvalidType=Invalid numeric value");
	strTextInvalidNum = localize("$$$/JavaScripts/FitImagePD2/InvalidNum=A number between 1 and 300000 is required. Closest value inserted.");
	strTextNeedFile = localize("$$$/JavaScripts/FitImagePD2/NeedFile=You must have a file selected before using Fit Image");
	strMessage = localize("$$$/JavaScripts/FitImagePD2/Message=Fit Image action settings");
	strMustUse = localize("$$$/JavaScripts/ImageProcessor/MustUse=You must use Photoshop CS 2 or later to run this script!");
	strLimitResize = localize("$$$/JavaScripts/FitImagePD2/Limit=Don^}t Enlarge");
}

// the main class
function FitImagePD2() {

	this.CreateDialog = function () {

		// I will keep most of the important dialog items at the same level
		// and use auto layout
		// use overriding group so OK/Cancel buttons placed to right of panel

		var res =
			"dialog { \
				pAndB: Group { orientation: 'row', \
					info: Panel { orientation: 'column', borderStyle: 'sunken', \
						text: '" + strConstrainWithin + "', \
						w: Group { orientation: 'row', alignment: 'right',\
							s: StaticText { text:'" + strTextWidth + "' }, \
							e: EditNumber { preferredSize: [70, 20] }, \
							p: StaticText { text:'" + strTextPixels + "'} \
						}, \
						h: Group { orientation: 'row', alignment: 'right', \
							s: StaticText { text:'" + strTextHeight + "' }, \
							e: EditNumber { preferredSize: [70, 20] }, \
							p: StaticText { text:'" + strTextPixels + "'} \
						}, \
						l: Group { orientation: 'row', alignment: 'left', \
								c:Checkbox { text: '" + strLimitResize + "', value: false }, \
						} \
					}, \
					buttons: Group { orientation: 'column', alignment: 'top',  \
						okBtn: Button { text:'" + strButtonOK + "', alignment: 'fill', properties:{name:'ok'} }, \
						cancelBtn: Button { text:'" + strButtonCancel + "', alignment: 'fill', properties:{name:'cancel'} } \
					} \
				} \
			}";

		// the following, when placed after e: in w and h doesn't show up
		// this seems to be OK since px is put inside the dialog box
		//p: StaticText { text:'" + strTextPixels + "'}

		// create the main dialog window, this holds all our data
		this.dlgMain = new Window(res, strTitle);

		// create a shortcut for easier typing
		var d = this.dlgMain;

		d.defaultElement = d.pAndB.buttons.okBtn;
		d.cancelElement = d.pAndB.buttons.cancelBtn;
		d.pAndB.info.w.e.minvalue = 1;
		d.pAndB.info.w.e.maxvalue = gMaxResize;
		d.pAndB.info.h.e.minvalue = 1;
		d.pAndB.info.h.e.maxvalue = gMaxResize;
	} // end of CreateDialog

	// initialize variables of dialog
	this.InitVariables = function () {

		var oldPref = app.preferences.rulerUnits;
		app.preferences.rulerUnits = Units.PIXELS;

		// look for last used params via Photoshop registry, getCustomOptions will throw if none exist
		try {
			var desc = app.getCustomOptions("809cbbe4-4591-47c9-a55b-a23af4692ffa");
			descriptorToObject(sizeInfo, desc, strMessage);
		} catch (e) {
			// it's ok if we don't have any options, continue with defaults
		}

		// see if I am getting descriptor parameters
		var fromAction = !!app.playbackParameters.count;
		if (fromAction) {
			// reset sizeInfo to defaults
			SizeInfo = new SizeInfo();
			// set the playback options to sizeInfo
			descriptorToObject(sizeInfo, app.playbackParameters, strMessage);
		}

		// make sure got parameters before this
		if (app.documents.length <= 0) // count of documents viewed
		{
			if (DialogModes.NO != app.playbackDisplayDialogs) {
				alert(strTextNeedFile); // only put up dialog if permitted
			}
			app.preferences.rulerUnits = oldPref;
			return false; // if no docs, always return
		}

		var w = app.activeDocument.width;
		var h = app.activeDocument.height;
		var l = true;
		if (sizeInfo.width.value == 0) {
			sizeInfo.width = w;
		} else {
			w = sizeInfo.width;
		}
		if (sizeInfo.height.value == 0) {
			sizeInfo.height = h;
		} else {
			h = sizeInfo.height;
		}

		app.preferences.rulerUnits = oldPref;
		if (DialogModes.ALL == app.playbackDisplayDialogs) {
			var d = this.dlgMain;
			d.ip = this;
			d.pAndB.info.w.e.value = Number(w);
			d.pAndB.info.h.e.value = Number(h);
			d.pAndB.info.l.c.value = sizeInfo.limit;
		}
		return true;
	}

	// routine for running the dialog and it's interactions
	this.RunDialog = function () {
		var d = this.dlgMain;

		// in case hit cancel button, don't close
		d.pAndB.buttons.cancelBtn.onClick = function () {
			var dToCancel = FindDialog(this);
			dToCancel.close(false);
		}

		// nothing for now
		d.onShow = function () {}

		// do not allow anything except for numbers 0-9
		//d.pAndB.info.w.e.addEventListener ('keydown', NumericEditKeyboardHandler);

		// do not allow anything except for numbers 0-9
		//d.pAndB.info.h.e.addEventListener ('keydown', NumericEditKeyboardHandler);

		// hit OK, do resize
		d.pAndB.buttons.okBtn.onClick = function () {
			if (gInAlert == true) {
				gInAlert = false;
				return;
			}

			var lValue = d.pAndB.info.l.c.value;
			var w = d.pAndB.info.w.e.value;
			var h = d.pAndB.info.h.e.value;
			sizeInfo.limit = Boolean(lValue);
			var inputErr = false;

			if (isNaN(w) || isNaN(h)) {
				if (DialogModes.NO != app.playbackDisplayDialogs) {
					alert(strTextInvalidType);
				}
				if (isNaN(w)) {
					sizeInfo.width = new UnitValue(1, "px");
					d.pAndB.info.w.e.value = 1;
				} else {
					sizeInfo.height = new UnitValue(1, "px");
					d.pAndB.info.h.e.value = 1;
				}
				return false;
			} else if (w < 1 || w > gMaxResize || h < 1 || h > gMaxResize) {
				if (DialogModes.NO != app.playbackDisplayDialogs) {
					gInAlert = true;
					alert(strTextInvalidNum);
				}
			}

			if (w < 1) {
				inputErr = true;
				sizeInfo.width = new UnitValue(1, "px");
				d.pAndB.info.w.e.value = 1;
			}


			if (w > gMaxResize) {
				inputErr = true;
				sizeInfo.width = new UnitValue(gMaxResize, "px");
				d.pAndB.info.w.e.value = gMaxResize;
			}

			if (h < 1) {
				inputErr = true;
				sizeInfo.height = new UnitValue(1, "px");
				d.pAndB.info.h.e.value = 1;
			}

			if (h > gMaxResize) {
				inputErr = true;
				sizeInfo.height = new UnitValue(gMaxResize, "px");
				d.pAndB.info.h.e.value = gMaxResize;
			}

			if (inputErr == false) {
				sizeInfo.width = new UnitValue(w, "px");
				sizeInfo.height = new UnitValue(h, "px");
				if (ResizeTheImage(w, h)) { // the whole point
					// error, input or output size too small
				}
				d.close(true);
			}
			return;
		}

		if (!this.InitVariables()) {
			return true; // handled it
		}

		// give the hosting app the focus before showing the dialog
		app.bringToFront();
		this.dlgMain.center();

		return d.show();
	}
}

function CheckVersion() {
	var numberArray = version.split(".");
	if (numberArray[0] < 9) {
		if (DialogModes.NO != app.playbackDisplayDialogs) {
			alert(strMustUse);
		}
		throw (strMustUse);
	}
}

function FindDialog(inItem) {
	var w = inItem;
	while ('dialog' != w.type) {
		if (undefined == w.parent) {
			w = null;
			break;
		}
		w = w.parent;
	}
	return w;
}

///////////////////////////////////////////////////////////////////////////////
// Function: objectToDescriptor
// Usage: create an ActionDescriptor from a JavaScript Object
// Input: JavaScript Object (o)
//        object unique string (s)
//        Pre process converter (f)
// Return: ActionDescriptor
// NOTE: Only boolean, string, number and UnitValue are supported, use a pre processor
//       to convert (f) other types to one of these forms.
// REUSE: This routine is used in other scripts. Please update those if you
//        modify. I am not using include or eval statements as I want these
//        scripts self contained.
///////////////////////////////////////////////////////////////////////////////
function objectToDescriptor(o, s, f) {
	if (undefined != f) {
		o = f(o);
	}

	var d = new ActionDescriptor;
	var l = o.reflect.properties.length;
	d.putString(app.charIDToTypeID('Msge'), s);
	for (var i = 0; i < l; i++) {
		var k = o.reflect.properties[i].toString();
		if (k == "__proto__" || k == "__count__" || k == "__class__" || k == "reflect")
			continue;
		var v = o[k];
		k = app.stringIDToTypeID(k);
		switch (typeof (v)) {
			case "boolean":
				d.putBoolean(k, v);
				break;
			case "string":
				d.putString(k, v);
				break;
			case "number":
				d.putDouble(k, v);
				break;
			default:
				{
					if (v instanceof UnitValue) {
						var uc = new Object;
						uc["px"] = charIDToTypeID("#Pxl"); // pixelsUnit
						uc["%"] = charIDToTypeID("#Prc"); // unitPercent
						d.putUnitDouble(k, uc[v.type], v.value);
					} else {
						throw (new Error("Unsupported type in objectToDescriptor " + typeof (v)));
					}
				}
		}
	}
	return d;
}

///////////////////////////////////////////////////////////////////////////////
// Function: descriptorToObject
// Usage: update a JavaScript Object from an ActionDescriptor
// Input: JavaScript Object (o), current object to update (output)
//        Photoshop ActionDescriptor (d), descriptor to pull new params for object from
//        object unique string (s)
//        JavaScript Function (f), post process converter utility to convert
// Return: Nothing, update is applied to passed in JavaScript Object (o)
// NOTE: Only boolean, string, number and UnitValue are supported, use a post processor
//       to convert (f) other types to one of these forms.
// REUSE: This routine is used in other scripts. Please update those if you
//        modify. I am not using include or eval statements as I want these
//        scripts self contained.
///////////////////////////////////////////////////////////////////////////////

function descriptorToObject(o, d, s, f) {
	var l = d.count;
	if (l) {
		var keyMessage = app.charIDToTypeID('Msge');
		if (d.hasKey(keyMessage) && (s != d.getString(keyMessage))) return;
	}
	for (var i = 0; i < l; i++) {
		var k = d.getKey(i); // i + 1 ?
		var t = d.getType(k);
		strk = app.typeIDToStringID(k);
		switch (t) {
			case DescValueType.BOOLEANTYPE:
				o[strk] = d.getBoolean(k);
				break;
			case DescValueType.STRINGTYPE:
				o[strk] = d.getString(k);
				break;
			case DescValueType.DOUBLETYPE:
				o[strk] = d.getDouble(k);
				break;
			case DescValueType.UNITDOUBLE:
				{
					var uc = new Object;
					uc[charIDToTypeID("#Rlt")] = "px"; // unitDistance
					uc[charIDToTypeID("#Prc")] = "%"; // unitPercent
					uc[charIDToTypeID("#Pxl")] = "px"; // unitPixels
					var ut = d.getUnitDoubleType(k);
					var uv = d.getUnitDoubleValue(k);
					o[strk] = new UnitValue(uv, uc[ut]);
				}
				break;
			case DescValueType.INTEGERTYPE:
			case DescValueType.ALIASTYPE:
			case DescValueType.CLASSTYPE:
			case DescValueType.ENUMERATEDTYPE:
			case DescValueType.LISTTYPE:
			case DescValueType.OBJECTTYPE:
			case DescValueType.RAWTYPE:
			case DescValueType.REFERENCETYPE:
			default:
				throw (new Error("Unsupported type in descriptorToObject " + t));
		}
	}
	if (undefined != f) {
		o = f(o);
	}
}

///////////////////////////////////////////////////////////////////////////////
// Function: SizeInfo
// Usage: object for holding the dialog parameters
// Input: <none>
// Return: object holding the size info
///////////////////////////////////////////////////////////////////////////////
function SizeInfo() {
	this.height = new UnitValue(0, "px");
	this.width = new UnitValue(0, "px");
	this.limit = false;
}

///////////////////////////////////////////////////////////////////////////////
// Function: NumericEditKeyboardHandler
// Usage: Do not allow anything except for numbers 0-9
// Input: ScriptUI keydown event
// Return: <nothing> key is rejected and beep is sounded if invalid
///////////////////////////////////////////////////////////////////////////////
function NumericEditKeyboardHandler(event) {

	try {

		var keyIsOK = KeyIsNumeric(event) ||
			KeyIsDelete(event) ||
			KeyIsLRArrow(event) ||
			KeyIsTabEnterEscape(event);



		if (!keyIsOK) {
			//    Bad input: tell ScriptUI not to accept the keydown event
			event.preventDefault();

			/*    Notify user of invalid input: make sure NOT
			       to put up an alert dialog or do anything which
		                 requires user interaction, because that
		                 interferes with preventing the 'default'
		                 action for the keydown event */
			app.beep();
		}
	} catch (e) {
		; // alert ("Ack! bug in NumericEditKeyboardHandler: " + e);
	}
}

//    key identifier functions
function KeyHasModifier(event) {
	return event.shiftKey || event.ctrlKey || event.altKey || event.metaKey;
}

function KeyIsNumeric(event) {
	return (event.keyName >= '0') && (event.keyName <= '9') && !KeyHasModifier(event);
}

function KeyIsDelete(event) {
	//    Shift-delete is ok
	return ((event.keyName == 'Backspace') || (event.keyName == 'Delete')) && !(event.ctrlKey);
}

function KeyIsLRArrow(event) {
	return ((event.keyName == 'Left') || (event.keyName == 'Right')) && !(event.altKey || event.metaKey);
}

function KeyIsTabEnterEscape(event) {
	return event.keyName == 'Tab' || event.keyName == 'Enter' || event.keyName == 'Escape';
}

///////////////////////////////////////////////////////////////////////////////
// Function: preserveDetails2
// Usage: Resize using Preserve Details 2.0 interpolation
///////////////////////////////////////////////////////////////////////////////
function preserveDetails2(theWidth, theHeight, theResolution, theNoise) {

    // Enable Preserve Details 2 in Preferences/Technology Previews
    var idset = stringIDToTypeID("set");
    var desc2444 = new ActionDescriptor();
    var idnull = stringIDToTypeID("null");
    var ref135 = new ActionReference();
    var idproperty = stringIDToTypeID("property");
    var idexperimentalFeatures = stringIDToTypeID("experimentalFeatures");
    ref135.putProperty(idproperty, idexperimentalFeatures);
    var idapplication = stringIDToTypeID("application");
    var idordinal = stringIDToTypeID("ordinal");
    var idtargetEnum = stringIDToTypeID("targetEnum");
    ref135.putEnumerated(idapplication, idordinal, idtargetEnum);
    desc2444.putReference(idnull, ref135);
    var idto = stringIDToTypeID("to");
    var desc2445 = new ActionDescriptor();
    var idexpFeatureDeepUpscale = stringIDToTypeID("expFeatureDeepUpscale");
    desc2445.putBoolean(idexpFeatureDeepUpscale, true);
    var idexperimentalFeatures = stringIDToTypeID("experimentalFeatures");
    desc2444.putObject(idto, idexperimentalFeatures, desc2445);
    executeAction(idset, desc2444, DialogModes.NO);
    // Image Size
    function s2t(s) {
        return app.stringIDToTypeID(s);
    }
    var descriptor = new ActionDescriptor();
    descriptor.putUnitDouble(s2t("width"), s2t("pixelsUnit"), theWidth);
    descriptor.putUnitDouble(s2t("height"), s2t("pixelsUnit"), theHeight);
    descriptor.putUnitDouble(s2t("resolution"), s2t("densityUnit"), theResolution);
    descriptor.putEnumerated(s2t("interfaceIconFrameDimmed"), s2t("interpolationType"), s2t("deepUpscale"));
    descriptor.putInteger(s2t("noise"), theNoise);
    executeAction(s2t("imageSize"), descriptor, DialogModes.NO);
}

// End Fit Image.jsx

 

It can be installed alongside the original script in your Application folder > Presets > Scripts

 

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html

 

DavidBoileau
Participant
December 11, 2023

Thank you very much. I will try that out.