Copy link to clipboard
Copied
I want to combine several thousand thin images horizontally into one Photoshop file using Batch.
I have looked at the contract sheet tool and it only goes to 100 and also leaves a 1 pixel gap.
Is there way to automatically add an image to the right of the last resizing the canvas as it goes? I managed to get resizing working but can't work out how to add +X to the width of an image with each new image, place it at the right then repeat.
Any batch experts know how to do this?
You can alternatively use the File > Scripts > Load files into stack script to produce a single file with all of the layers, presuming that they sort alphabetically in the correct order.
Change the canvas width to the required size.
Move the last layer to the right hand side of the canvas edge.
Next select all layers in the layers panel and then use the move tool's align/distribute option to distribute all layers horizontally.
EDIT: To automate the above, simply run the following script.
...I changed all references from vertical to horizontal and height to width and the alignment options etc... It seems to work as expected, but it has not been extensivly tested:
/*
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-combine-images-horizontally-via-batch/m-p/12716190#M619600
Files to Horizontally Stacked Layers.jsx
based on:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-create-one-big-png-image-from-multiple-png-images/td-p/12359415
Files
...
Copy link to clipboard
Copied
I believe that you can use a script developed by the late JJMack for this task:
http://www.mouseprints.net/old/dpr/PasteImageRoll.html
Edit: I wrote a different script for vertical stacking, should be easy enough to adapt when I have time.
Copy link to clipboard
Copied
I changed all references from vertical to horizontal and height to width and the alignment options etc... It seems to work as expected, but it has not been extensivly tested:
/*
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-combine-images-horizontally-via-batch/m-p/12716190#M619600
Files to Horizontally Stacked Layers.jsx
based on:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-create-one-big-png-image-from-multiple-png-images/td-p/12359415
Files to Vertically Stacked Layers.jsx
*/
#target photoshop
if (app.documents.length === 0) {
(function () {
// Save the current dialog display settings
var savedDisplayDialogs = app.displayDialogs;
app.displayDialogs = DialogModes.NO;
// Capture the original ruler units and set the ruler units to pixels
var origUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// Select the input folder
var inputFolder = Folder.selectDialog('Please select the input folder:');
// Test if Cancel button returns null, then do nothing
if (inputFolder === null) {
app.beep();
return;
}
// Supported file formats
var inputFiles = inputFolder.getFiles(/\.(jpg|jpeg|tif|tiff|png|psd|psb)$/i);
// Alpha numeric sort
// inputFiles.sort().reverse;
inputFiles.sort();
// Process the first "base" file
var firstFile = app.open(File(inputFiles[0]));
var firstFileName = app.activeDocument.name;
app.activeDocument.duplicate("Horizontal-Stacker", false);
firstFile.close(SaveOptions.DONOTSAVECHANGES);
var docStack = app.documents[0];
app.activeDocument = docStack;
docStack.activeLayer.name = firstFileName;
var baseHeight = app.activeDocument.width.value;
// Process the remaining file layers to the "base" file
for (var i = 1; i < inputFiles.length; i++) {
var remainingFiles = app.open(File(inputFiles[i]));
var fileName = remainingFiles.name;
remainingFiles.activeLayer.name = fileName;
remainingFiles.layers[0].duplicate(docStack, ElementPlacement.PLACEATBEGINNING);
remainingFiles.close(SaveOptions.DONOTSAVECHANGES);
relativeCanvasSize(true, baseHeight);
align2SelectAll('AdCV');
align2SelectAll('AdLf');
}
app.runMenuItem(stringIDToTypeID("selectAllLayers"));
reverseLayerStack();
app.activeDocument.flatten();
app.beep();
alert(inputFiles.length + ' files horizontally stacked!');
// Restore the dialogs
app.displayDialogs = savedDisplayDialogs;
// Return the original ruler units
app.preferences.rulerUnits = origUnits;
// Functions
function relativeCanvasSize(relative, width) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
descriptor.putBoolean(s2t("relative"), relative);
descriptor.putUnitDouble(s2t("width"), s2t("pixelsUnit"), width);
descriptor.putEnumerated(s2t("horizontal"), s2t("horizontalLocation"), s2t("top"));
executeAction(s2t("canvasSize"), descriptor, DialogModes.NO);
}
function align2SelectAll(method) {
app.activeDocument.selection.selectAll();
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
desc.putReference(charIDToTypeID("null"), ref);
desc.putEnumerated(charIDToTypeID("Usng"), charIDToTypeID("ADSt"), charIDToTypeID(method));
try {
executeAction(charIDToTypeID("Algn"), desc, DialogModes.NO);
} catch (e) { }
app.activeDocument.selection.deselect();
}
function reverseLayerStack() {
var idreverse = stringIDToTypeID("reverse");
var desc4653 = new ActionDescriptor();
var idnull = stringIDToTypeID("null");
var ref2335 = new ActionReference();
var idlayer = stringIDToTypeID("layer");
var idordinal = stringIDToTypeID("ordinal");
var idtargetEnum = stringIDToTypeID("targetEnum");
ref2335.putEnumerated(idlayer, idordinal, idtargetEnum);
desc4653.putReference(idnull, ref2335);
executeAction(idreverse, desc4653, DialogModes.NO);
}
})();
} else {
alert('Please close all open files before running this script...');
}
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
Copy link to clipboard
Copied
You can alternatively use the File > Scripts > Load files into stack script to produce a single file with all of the layers, presuming that they sort alphabetically in the correct order.
Change the canvas width to the required size.
Move the last layer to the right hand side of the canvas edge.
Next select all layers in the layers panel and then use the move tool's align/distribute option to distribute all layers horizontally.
EDIT: To automate the above, simply run the following script. I have updated the code to automatically run the load files into stack script, so it is now fully automated.
/*
Combine Images Horizontally.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-combine-images-horizontally-via-batch/td-p/12716190
Stephen Marsh, v1.0 - 31st January 2022
*/
$.evalFile(File(app.path + '/Presets/Scripts/Load Files into Stack.jsx'));
var newWidth = app.activeDocument.layers.length * 100;
canvasSize(newWidth);
align2SelectAll('AdRg');
app.runMenuItem(stringIDToTypeID('selectAllLayers'));
distributeHorizontally();
// Functions
function distributeHorizontally() {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
reference.putEnumerated( s2t( "layer" ), s2t( "ordinal" ), s2t( "targetEnum" ));
descriptor.putReference( s2t( "null" ), reference );
descriptor.putEnumerated( s2t( "using" ), s2t( "alignDistributeSelector" ), s2t( "ADSDistH" ));
executeAction( s2t( "distort" ), descriptor, DialogModes.NO );
}
function canvasSize(width) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
descriptor.putUnitDouble( s2t( "width" ), s2t( "percentUnit" ), width );
descriptor.putEnumerated( s2t( "horizontal" ), s2t( "horizontalLocation" ), s2t( "left" ));
executeAction( s2t( "canvasSize" ), descriptor, DialogModes.NO );
}
function align2SelectAll(method) {
app.activeDocument.selection.selectAll();
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
desc.putReference(charIDToTypeID("null"), ref);
desc.putEnumerated(charIDToTypeID("Usng"), charIDToTypeID("ADSt"), charIDToTypeID(method));
try {
executeAction(charIDToTypeID("Algn"), desc, DialogModes.NO);
} catch (e) { }
app.activeDocument.selection.deselect();
}
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
Copy link to clipboard
Copied
You can also try the following scripts here, just use a row value of 1 with a column value as required. You may wish to work in smaller batches, not sure how things would work on "thousands of images" even if they are small:
Copy link to clipboard
Copied
Thank you for giving me multiple options here. I'll give each a go and see which one works the best. This is for a long term art project I've been putting off. This has been very helpful.
Copy link to clipboard
Copied
Thanks, feedback on each of the proposed solutions would be appreciated. This all presumes that each separate file is the same width.
P.S. Batch/Action isn't really designed for this task, it may be possible but wouldn't be my preferred approach when scripts are available.
Copy link to clipboard
Copied
The scripts I create on the forum are often for other users, not my personal use. Stacking files in horizontal or vertical layout layers is something that I do quite often. I have combined my previously separate horizontal and vertical stacker scripts into a single script with an interface:
Note: It is assumed that the stacked files will create a canvas size below the common 30,000 or 300,000 px limits. :]
/*
Files to Horizontally or Vertically Stacked Layers scriptUI GUI.jsx
v1.0, 15th September 2024
Based on:
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-create-one-big-png-image-from-multiple-png-images/td-p/12359415
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-to-combine-images-horizontally-via-batch/m-p/12716190#M619600
*/
#target photoshop
function showGUI() {
var dlg = new Window('dialog', 'Horizontal or Vertical File Stacker (v1.0)');
dlg.preferredSize = [300, 250];
dlg.orientation = 'column';
dlg.alignChildren = 'center';
// Stack Direction Dropdown
var dropdown = dlg.add('dropdownlist', undefined, ['Horizontal Stack', 'Vertical Stack']);
dropdown.selection = 0; // Default to Horizontal Stack
// Horizontal Alignment Radio Buttons
dlg.add('statictext', undefined, 'Horizontal Stack Alignment:');
var hAlignmentGroup = dlg.add('group');
hAlignmentGroup.orientation = 'row';
var rbtnHCenter = hAlignmentGroup.add('radiobutton', undefined, 'Center Vertical');
var rbtnHTopLeft = hAlignmentGroup.add('radiobutton', undefined, 'Top Left');
rbtnHCenter.value = true; // Default to Center Vertical alignment
// Vertical Alignment Radio Buttons
dlg.add('statictext', undefined, 'Vertical Stack Alignment:');
var vAlignmentGroup = dlg.add('group');
vAlignmentGroup.orientation = 'row';
var rbtnVCenter = vAlignmentGroup.add('radiobutton', undefined, 'Center Horizontal');
var rbtnVTopLeft = vAlignmentGroup.add('radiobutton', undefined, 'Top Left');
rbtnVCenter.value = true; // Default to Center Horizontal alignment
// Reverse Layer Stack Checkbox
var reverseCheckbox = dlg.add('checkbox', undefined, 'Reverse Layer Stack');
reverseCheckbox.value = false; // Default to unchecked
// OK and Cancel Button Group
var buttonGroup = dlg.add('group');
buttonGroup.orientation = 'row';
buttonGroup.alignChildren = 'center';
buttonGroup.margins = 5;
// OK Button
var btnOK = buttonGroup.add('button', undefined, 'OK');
btnOK.onClick = function () {
var inputFiles = selectMultipleFiles();
if (inputFiles) {
dlg.close();
var stackDirection = dropdown.selection.text;
var reverseLayers = reverseCheckbox.value;
if (stackDirection === 'Horizontal Stack') {
var hAlignment = rbtnHCenter.value ? 'center' : 'topLeft';
hStacker(inputFiles, hAlignment, reverseLayers);
} else {
var vAlignment = rbtnVCenter.value ? 'center' : 'topLeft';
vStacker(inputFiles, vAlignment, reverseLayers);
}
}
};
// Cancel Button
var btnCancel = buttonGroup.add('button', undefined, 'Cancel', { name: 'cancel' });
btnCancel.onClick = function () {
dlg.close();
};
// Disable/Enable Alignment Options based on Stack Selection
dropdown.onChange = function () {
if (dropdown.selection.text === 'Horizontal Stack') {
hAlignmentGroup.enabled = true;
vAlignmentGroup.enabled = false;
} else {
hAlignmentGroup.enabled = false;
vAlignmentGroup.enabled = true;
}
};
// Initially disable vertical alignment radio buttons
vAlignmentGroup.enabled = false;
dlg.show();
}
function selectMultipleFiles() {
var inputFiles = File.openDialog('Select image files', true);
if (!inputFiles || inputFiles.length === 0) {
alert('No files selected.');
return null;
}
return inputFiles;
}
if (app.documents.length === 0) {
showGUI();
} else {
alert('Please close all open files before running this script...');
}
function hStacker(inputFiles, alignment, reverseLayers) {
(function () {
var savedDisplayDialogs = app.displayDialogs;
app.displayDialogs = DialogModes.NO;
var origUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// Sort files alphabetically
inputFiles.sort().reverse();
// Process the first "base" file
var firstFile = app.open(File(inputFiles[0]));
var firstFileName = app.activeDocument.name;
app.activeDocument.duplicate("Horizontal-Stacker", false);
firstFile.close(SaveOptions.DONOTSAVECHANGES);
var docStack = app.documents[0];
app.activeDocument = docStack;
docStack.activeLayer.name = firstFileName;
var baseWidth = app.activeDocument.width.value;
// Process the remaining file layers to the "base" file
for (var i = 1; i < inputFiles.length; i++) {
var remainingFiles = app.open(File(inputFiles[i]));
var fileName = remainingFiles.name;
remainingFiles.activeLayer.name = fileName;
remainingFiles.layers[0].duplicate(docStack, ElementPlacement.PLACEATBEGINNING);
remainingFiles.close(SaveOptions.DONOTSAVECHANGES);
relativeCanvasSizeW(true, baseWidth);
if (alignment === 'center') {
align2SelectAll('AdCV'); // Center Vertical
} else {
align2SelectAll('AdTp'); // Align to Top
}
align2SelectAll('AdLf'); // Left
}
app.runMenuItem(stringIDToTypeID("selectAllLayers"));
if (reverseLayers) {
reverseLayerStack();
}
app.activeDocument.revealAll();
app.beep();
alert(inputFiles.length + ' files stacked horizontally!');
app.displayDialogs = savedDisplayDialogs;
app.preferences.rulerUnits = origUnits;
function relativeCanvasSizeW(relative, width) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
descriptor.putBoolean(s2t("relative"), relative);
descriptor.putUnitDouble(s2t("width"), s2t("pixelsUnit"), width);
descriptor.putEnumerated(s2t("horizontal"), s2t("horizontalLocation"), s2t("top"));
executeAction(s2t("canvasSize"), descriptor, DialogModes.NO);
}
})();
}
function vStacker(inputFiles, alignment, reverseLayers) {
(function () {
var savedDisplayDialogs = app.displayDialogs;
app.displayDialogs = DialogModes.NO;
var origUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
// Sort files alphabetically
inputFiles.sort().reverse();
// Process the first "base" file
var firstFile = app.open(File(inputFiles[0]));
var firstFileName = app.activeDocument.name;
app.activeDocument.duplicate("Vertical-Stacker", false);
firstFile.close(SaveOptions.DONOTSAVECHANGES);
var docStack = app.documents[0];
app.activeDocument = docStack;
docStack.activeLayer.name = firstFileName;
// Process the remaining file layers to the "base" file
for (var i = 1; i < inputFiles.length; i++) {
var remainingFiles = app.open(File(inputFiles[i]));
var fileName = remainingFiles.name;
var baseHeight = activeDocument.height.value;
remainingFiles.activeLayer.name = fileName;
remainingFiles.layers[0].duplicate(docStack, ElementPlacement.PLACEATBEGINNING);
remainingFiles.close(SaveOptions.DONOTSAVECHANGES);
relativeCanvasSizeH(true, baseHeight);
if (alignment === 'center') {
align2SelectAll('AdCH'); // Center Horizontal
} else {
align2SelectAll('AdLf'); // Align to Left
}
align2SelectAll('AdBt'); // Bottom
}
app.runMenuItem(stringIDToTypeID("selectAllLayers"));
if (reverseLayers) {
reverseLayerStack();
}
app.activeDocument.revealAll();
app.beep();
alert(inputFiles.length + ' files stacked vertically!');
app.displayDialogs = savedDisplayDialogs;
app.preferences.rulerUnits = origUnits;
function relativeCanvasSizeH(relative, height) {
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
descriptor.putBoolean(s2t("relative"), relative);
descriptor.putUnitDouble(s2t("height"), s2t("pixelsUnit"), height);
descriptor.putEnumerated(s2t("vertical"), s2t("verticalLocation"), s2t("top"));
executeAction(s2t("canvasSize"), descriptor, DialogModes.NO);
}
})();
}
// Common Helper functions
function reverseLayerStack() {
var idreverse = stringIDToTypeID("reverse");
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(stringIDToTypeID("layer"), stringIDToTypeID("ordinal"), stringIDToTypeID("targetEnum"));
desc.putReference(stringIDToTypeID("null"), ref);
executeAction(idreverse, desc, DialogModes.NO);
}
function align2SelectAll(method) {
app.activeDocument.selection.selectAll();
var desc = new ActionDescriptor();
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
desc.putReference(charIDToTypeID("null"), ref);
desc.putEnumerated(charIDToTypeID("Usng"), charIDToTypeID("ADSt"), charIDToTypeID(method));
try {
executeAction(charIDToTypeID("Algn"), desc, DialogModes.NO);
} catch (e) {}
app.activeDocument.selection.deselect();
}
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html