Upscale Images
Script for Adobe InDesign
Fix low-res images in InDesign documents. The script calls Photoshop to upscale images placed in InDesign that are below a specified resolution.
- Specify minimum resolution
- Upscale original images or renamed copies
- Uses Photoshop Preserve Details 2.0
- Adapt open source to customize or create other scripts
How-to Video
How to use the script
The interface is a single section with three primary options, and two additional options when upscaling copies. Set options as desired and then click the OK button to begin. The script examines each raster image placed in the document. For images with less effective PPI than the minimum, the script calls Photoshop to upscale the image. Photoshop uses the resampling option “Preserve Details 2.0,” which does an amazing job of increasing resolution. Once all images are examined and upscaled as needed, the user is notified.
Minimum resolution — enter the pixels per inch that when an image is less than, the image is upscaled to. The default value is 300.
Upscale original images — by default, image files currently placed in the document are upscaled and each link is updated. Use the next option to upscale copies instead.
Upscale copies and relink — the script makes a copy of each link to upscale, then upscales the copy and relinks to the new name. When enabled, the next two options become available.
Suffix — enter text to append to file names of copies. The default value is “_upscaled”.
Include scale percentage — adds to the suffix the percentage value that each image is upscaled.
Source code
(download button below)
/*
Upscale Images
Copyright 2023 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
(function () {
var title = "Upscale Images";
if (!/indesign/i.test(app.name)) {
alert("Script for InDesign", title, false);
return;
}
var upscaleImage = 'function upscaleImage(args){var a;var desc1;var doc;var effectivePpi;var filePath;var height;var resolution;var width;app.displayDialogs=DialogModes.NO;app.preferences.rulerUnits=Units.PIXELS;try{a=eval(args);filePath=a[0].replace(/%39/g,"\'");width=a[1];height=a[2];resolution=a[3];doc=app.open(new File(filePath));effectivePpi=Math.min((Number(doc.width)/width),(Number(doc.height)/height));doc.resizeImage(null,null,effectivePpi,ResampleMethod.NONE);desc1=new ActionDescriptor();desc1.putUnitDouble(stringIDToTypeID("resolution"),stringIDToTypeID("densityUnit"),resolution);desc1.putBoolean(stringIDToTypeID("constrainProportions"),true);desc1.putEnumerated(stringIDToTypeID("interfaceIconFrameDimmed"),stringIDToTypeID("interpolationType"),stringIDToTypeID("deepUpscale"));desc1.putInteger(stringIDToTypeID("noise"),20);executeAction(stringIDToTypeID("imageSize"),desc1,DialogModes.NO);doc.save();return"OK"}catch(e){return e.message}finally{doc.close(SaveOptions.DONOTSAVECHANGES)}};';
// Script variables.
var doc;
var doneMessage;
var error;
var progress;
// Reusable UI variables.
var g; // group
var p; // panel
var w; // window
// Permanent UI variables.
var btnCancel;
var btnOk;
var cbIncludeScale;
var grpUpscaleCopies;
var inpCopySuffix;
var inpResolution;
var rbUpscaleCopies;
var rbUpscaleOriginals;
// SETUP
// Script requires open document.
if (!app.documents.length) {
alert("Open a document", title, false);
return;
}
doc = app.activeDocument;
// CREATE PROGRESS WINDOW
progress = new Window("palette", "Progress", undefined, {
"closeButton": false
});
progress.t = progress.add("statictext");
progress.t.preferredSize.width = 450;
progress.b = progress.add("progressbar");
progress.b.preferredSize.width = 450;
progress.display = function (message) {
message && (this.t.text = message);
this.show();
this.update();
};
progress.increment = function () {
this.b.value++;
};
progress.set = function (steps) {
this.b.value = 0;
this.b.minvalue = 0;
this.b.maxvalue = steps;
};
// CREATE USER INTERFACE
w = new Window("dialog", title);
w.alignChildren = "fill";
p = w.add("panel");
p.alignChildren = "fill";
g = p.add("group");
g.add("statictext", undefined, "Minimum resolution:");
inpResolution = g.add("edittext");
inpResolution.preferredSize.width = 60;
g.add("statictext", undefined, "pixels/inch");
rbUpscaleOriginals = p.add("radiobutton", undefined, "Upscale original images");
rbUpscaleCopies = p.add("radiobutton", undefined, "Upscale copies and relink");
grpUpscaleCopies = p.add("group");
grpUpscaleCopies.margins = [24, 0, 0, 0];
grpUpscaleCopies.orientation = "column";
grpUpscaleCopies.alignChildren = "left";
g = grpUpscaleCopies.add("group");
g.add("statictext", undefined, "Suffix:");
inpCopySuffix = g.add("edittext");
inpCopySuffix.preferredSize.width = 110;
cbIncludeScale = grpUpscaleCopies.add("checkbox", undefined, "Include scale percentage");
// Action Buttons
g = w.add("group");
g.alignment = "center";
btnOk = g.add("button", undefined, "OK");
btnCancel = g.add("button", undefined, "Cancel");
// Panel Copyright
p = w.add("panel");
p.add("statictext", undefined, "Copyright 2023 William Campbell");
// SET UI VALUES
inpResolution.text = "300";
rbUpscaleOriginals.value = true;
rbUpscaleCopies.value = false;
inpCopySuffix.text = "_upscaled";
cbIncludeScale.value = false;
configureUi();
// UI ELEMENT EVENT HANDLERS
inpResolution.onChange = function () {
var s;
var v;
s = this.text.replace(/[^0-9]/g, "");
if (s != this.text) {
alert("Numeric input only.\nNon-numeric characters removed.", " ", false);
}
v = Math.round(Number(s) || 0);
if (v == 0) {
alert("Invalid value reset to default", " ", false);
this.text = "300";
} else {
this.text = v.toString();
}
};
rbUpscaleOriginals.onClick = configureUi;
rbUpscaleCopies.onClick = configureUi;
btnOk.onClick = function () {
if (rbUpscaleCopies.value && !inpCopySuffix.text && !cbIncludeScale.value) {
alert("To upscale copies, add a suffix and/or include scale percentage", " ", false);
return;
}
w.close(1);
};
btnCancel.onClick = function () {
w.close(0);
};
// DISPLAY THE DIALOG
if (w.show() == 1) {
doneMessage = "";
try {
process();
if (error) {
doneMessage = "Photoshop returned errors\nSee file 'upscale.log' on Desktop";
} else {
doneMessage = "Done";
}
} catch (e) {
error = error || e;
doneMessage = "An error has occurred.\nLine " + error.line + ": " + error.message;
}
progress.close();
doneMessage && alert(doneMessage, title, error);
}
//====================================================================
// END PROGRAM EXECUTION, BEGIN FUNCTIONS
//====================================================================
function configureUi() {
grpUpscaleCopies.enabled = rbUpscaleCopies.value;
}
function logError(filePath, message) {
var f = new File("~/Desktop/upscale.log");
f.open("a");
f.writeln(new Date().toLocaleString());
f.writeln(" " + filePath);
f.writeln(" Photoshop " + message);
f.close();
error = true;
}
function process() {
var args;
var bt;
var effectivePpi;
var fileCopy;
var filePath;
var gb;
var graphic;
var height;
var i;
var link;
var resolution;
var suffix;
var width;
var updateLink = function (link, fileCopy) {
// Useless access to status property is necessary.
// Without it, method 'update' has no effect.
link.status;
if (fileCopy) {
link.relink(fileCopy);
}
link.update();
};
app.scriptPreferences.measurementUnit = MeasurementUnits.INCHES;
resolution = Number(inpResolution.text);
progress.display();
progress.set(doc.links.length);
error = false;
for (i = 0; i < doc.links.length; i++) {
progress.increment();
link = doc.links[i];
graphic = link.parent;
if (graphic instanceof Image) {
progress.display(link.name);
effectivePpi = Math.min(graphic.effectivePpi[0], graphic.effectivePpi[1]);
if (effectivePpi < resolution) {
progress.display("UPSCALING: " + link.name);
filePath = link.filePath;
gb = graphic.geometricBounds;
width = Number(gb[3] - gb[1]); // Must be inches.
height = Number(gb[2] - gb[0]); // Must be inches.
if (rbUpscaleCopies.value) {
// Make a copy of the file.
suffix = inpCopySuffix.text;
if (cbIncludeScale.value) {
// Include scale percentage.
suffix += Math.ceil((resolution / effectivePpi) * 100).toString();
}
fileCopy = new File(filePath.replace(/(\.[^\.]+)$/, suffix + "$1"));
new File(filePath).copy(fileCopy);
filePath = fileCopy.fullName;
}
// File names that contain single quotes fail.
// Replace with '%39' and restore in Photoshop.
args = [
filePath.replace(/'/g, "%39"),
width,
height,
resolution
];
bt = new BridgeTalk();
bt.target = "photoshop";
//
// IMPORTANT:
// Must wrap 'args.toSource()' in single quotes.
//
bt.body = upscaleImage + "upscaleImage('" + args.toSource() + "');";
bt.onResult = function (response) {
if (response.body != "OK") {
logError(filePath, response.body);
} else {
updateLink(link, fileCopy);
}
};
bt.onError = function (response) {
logError(filePath, response.body);
};
bt.send(100);
}
}
}
// Any image upscaled that is placed more than once,
// only first instance of image was updated.
// Check all doc links status and update.
for (i = 0; i < doc.links.length; i++) {
link = doc.links[i];
if (link.status == LinkStatus.LINK_OUT_OF_DATE) {
link.update();
}
}
}
})();
/*
//
// Upscale Image, Photoshop ExtendScript
//
// Minify and make a string
// Must wrap in ' because contains "
//
function upscaleImage(args) {
var a;
var desc1;
var doc;
var effectivePpi;
var filePath;
var height;
var resolution;
var width;
app.displayDialogs = DialogModes.NO;
app.preferences.rulerUnits = Units.PIXELS;
try {
a = eval(args);
filePath = a[0].replace(/%39/g, "\'");
width = a[1];
height = a[2];
resolution = a[3];
doc = app.open(new File(filePath));
effectivePpi = Math.min((Number(doc.width) / width), (Number(doc.height) / height));
doc.resizeImage(null, null, effectivePpi, ResampleMethod.NONE);
desc1 = new ActionDescriptor();
desc1.putUnitDouble(stringIDToTypeID("resolution"), stringIDToTypeID("densityUnit"), resolution);
desc1.putBoolean(stringIDToTypeID("constrainProportions"), true);
desc1.putEnumerated(stringIDToTypeID("interfaceIconFrameDimmed"), stringIDToTypeID("interpolationType"), stringIDToTypeID("deepUpscale"));
desc1.putInteger(stringIDToTypeID("noise"), 20);
executeAction(stringIDToTypeID("imageSize"), desc1, DialogModes.NO);
doc.save();
return "OK";
} catch (e) {
return e.message;
} finally {
doc.close(SaveOptions.DONOTSAVECHANGES);
}
}
*/
Upscale Images
For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.
IMPORTANT: scripts are developed for the latest Adobe Creative Cloud applications. Many scripts work in CC 2018 and later, even some as far back as CS6, but may not perform as expected, or run at all, when used in versions prior to 2018. Photoshop features Select Subject and Preserve Details 2.0 definitely fail prior to CC 2018 (version 19) as the features do not exist in earlier versions. For best results use the latest versions of Adobe Creative Cloud applications.
IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.
IMPORTANT: fees paid for software products are the purchase of a non-exclusive license to use the software product and do not grant the purchaser any degree of ownership of the software code. Author of the intellectual property and copyright holder William Campbell retains 100% ownership of all code used in all software products regardless of the inspiration for the software product design or functionality.