In the Photoshop Feature Request Forum the ability to free transform multiple objects has been requested a while back.
http://forums.adobe.com/thread/768368?tstart=0
The best I could come up with so far is a dialog with sliders as that seems to allow a preview for all affected layers.
I’d appreciate it if any one of you could give the Script a look to see if any Scripting-atrocities are contained.
Happy New Year,
C.Pfaffenbichler
Code: Select all// scale and/or rotate multiple selected layers and groups with dialog;
// selected pixel-layers and groups are converted to smart objects if not such already;
// groups take precedent over contained layers and naturally adjustment layers situated in a selected group will subsequently only affect that;
// adjustment layers are edited only if masked;
// thanks to xbytor, jeffrey tranberry, mike hale and paul riggott;
// 2010, pfaffenbichler; use it at your own risk;
#target photoshop
// check and get back array of selected layers;
var checksOut = true;
checksOut = checkPhotoshop(checksOut);
if (checksOut[0] == true) {
// define document;
var myDocument = app.activeDocument;
myDocument.selection.deselect();
// set the history states;
var histStates = historyStates();
var memUsage = memoryUsage();
if (histStates < checksOut[1].length * 2 + 1) {
setHistoryStates (checksOut[1].length * 2 + 1, memUsage)
};
var theFirstState = myDocument.activeHistoryState;
// smartify the layers;
var theLayers = getSelectedLayersAndSmartify(myDocument);
var thisState = myDocument.activeHistoryState;
////////////////////////////////////
// the dialog;
var dlg = new Window("dialog", "scale the selected layers", [500,300,840,500]);
// scale;
dlg.scale = dlg.add('panel', [15,15,325,65], 'scale');
dlg.scale.slider = dlg.scale.add('slider', [12,12,300,32], 500);
dlg.scale.slider.value = 20;
dlg.scale.slider.onChange = scaleTheLayers;
// rotate;
dlg.rotate = dlg.add('panel', [15,75,325,125], 'rotate');
dlg.rotate.slider = dlg.rotate.add('slider', [12,12,300,32], 360);
dlg.rotate.slider.value = 50;
dlg.rotate.slider.onChange = scaleTheLayers;
// buttons for ok and cancel;
dlg.buttons = dlg.add('panel', [15,135,325,185], '');
dlg.buttons.buildBtn = dlg.buttons.add('button', [13,12,145,34], 'OK', {name:'ok'});
dlg.buttons.cancelBtn = dlg.buttons.add('button', [155,12,290,34], 'Cancel', {name:'cancel'});
// show the dialog;
dlg.center();
var myReturn = dlg.show ();
////////////////////////////////////
if (myReturn == 1) {
// select the layers;
reselectLayers (myDocument, theLayers);
}
else {
// go back in history and select the layers;
myDocument.activeHistoryState = theFirstState;
reselectLayers (myDocument, checksOut[1]);
};
// reset the history states;
setHistoryStates (histStates, memUsage);
};
////////////////////////////////////
////// scale the layers //////
function scaleTheLayers () {
app.activeDocument.suspendHistory("scale__"+(String(dlg.scale.slider.value * 5).slice(0,7))+"%_rotate_"+(String(dlg.rotate.slider.value * 3.6 - 180).slice(0,7))+"˚", "main()");
function main () {
app.activeDocument.activeHistoryState = thisState;
var theScale = dlg.scale.slider.value * 5;
var theRotate = dlg.rotate.slider.value * 3.6 - 180;
for (var m = 0; m < theLayers.length; m++) {
try {
myDocument.activeLayer = theLayers[m];
// =======================================================
var idTrnf = charIDToTypeID( "Trnf" );
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref2.putEnumerated( idLyr, idOrdn, idTrgt );
desc3.putReference( idnull, ref2 );
var idFTcs = charIDToTypeID( "FTcs" );
var idQCSt = charIDToTypeID( "QCSt" );
var idQcsa = charIDToTypeID( "Qcsa" );
desc3.putEnumerated( idFTcs, idQCSt, idQcsa );
var idOfst = charIDToTypeID( "Ofst" );
var desc4 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
var idPxl = charIDToTypeID( "#Pxl" );
desc4.putUnitDouble( idHrzn, idPxl, 0.000000 );
var idVrtc = charIDToTypeID( "Vrtc" );
var idPxl = charIDToTypeID( "#Pxl" );
desc4.putUnitDouble( idVrtc, idPxl, -0.000000 );
var idOfst = charIDToTypeID( "Ofst" );
desc3.putObject( idOfst, idOfst, desc4 );
var idWdth = charIDToTypeID( "Wdth" );
var idPrc = charIDToTypeID( "#Prc" );
desc3.putUnitDouble( idWdth, idPrc, theScale );
var idHght = charIDToTypeID( "Hght" );
var idPrc = charIDToTypeID( "#Prc" );
desc3.putUnitDouble( idHght, idPrc, theScale );
var idAngl = charIDToTypeID( "Angl" );
var idAng = charIDToTypeID( "#Ang" );
desc3.putUnitDouble( idAngl, idAng, theRotate );
var idLnkd = charIDToTypeID( "Lnkd" );
desc3.putBoolean( idLnkd, true );
executeAction( idTrnf, desc3, DialogModes.NO );
}
catch (e) {}
};
app.refresh()
}
};
////// check if file is eligible for this script //////
function checkPhotoshop(checksOut) {
if (app.documents.length == 0) {
alert("no documents are open\nplease open a document and try again");
checksOut = false
}
else {
if (app.activeDocument.mode == DocumentMode.BITMAP || app.activeDocument.mode == DocumentMode.INDEXEDCOLOR || app.activeDocument.mode == DocumentMode.MULTICHANNEL) {
alert("the current document cannot have layers");
checksOut = false
}
else {
var theLayers = getSelectedLayers(app.activeDocument);
if (theLayers.length < 2) {
alert("the current document does not have more than one layer selected");
checksOut = false
}
};
};
return [checksOut, theLayers]
};
////// function collect all layers //////
function collectLayers (theParent) {
if (!allLayers) {
var allLayers = new Array}
else {};
for (var m = theParent.layers.length - 1; m >= 0;m--) {
var theLayer = theParent.layers[m];
// apply the function to layersets;
if (theLayer.typename == "ArtLayer") {
allLayers.push(theLayer)
}
else {
allLayers = allLayers.concat(collectLayers(theLayer))
// this line includes the layer groups;
allLayers.push(theLayer);
}
}
return allLayers
};
////// adapted from an xbytor script, thanks to xbytor //////
function getSelectedLayers (doc) {
var layers = collectLayers(doc);
var visible = [];
var selLayers = [];
for (var i = 0; i < layers.length; i++) {
var l = layers;
visible = l.visible;
l.visible = true;
};
////// hide selected;
var id807 = charIDToTypeID( "Hd " );
var desc161 = new ActionDescriptor();
var id808 = charIDToTypeID( "null" );
var ref116 = new ActionReference();
var id809 = charIDToTypeID( "Lyr " );
var id810 = charIDToTypeID( "Ordn" );
var id811 = charIDToTypeID( "Trgt" );
ref116.putEnumerated( id809, id810, id811 );
desc161.putReference( id808, ref116 );
executeAction( id807, desc161, DialogModes.NO );
////// collect invisibles;
for (var i = 0; i < layers.length; i++) {
var l = layers;
if (!l.visible) {
selLayers.push(l);
};
l.visible = visible;
};
return selLayers;
};
////// adapted from an xbytor script, thanks to xbytor //////
function getSelectedLayersAndSmartify (doc) {
var layers = collectLayers(doc);
var visible = [];
var selLayers = [];
for (var i = 0; i < layers.length; i++) {
var l = layers;
visible = l.visible;
l.visible = true;
};
////// hide selected;
var id807 = charIDToTypeID( "Hd " );
var desc161 = new ActionDescriptor();
var id808 = charIDToTypeID( "null" );
var ref116 = new ActionReference();
var id809 = charIDToTypeID( "Lyr " );
var id810 = charIDToTypeID( "Ordn" );
var id811 = charIDToTypeID( "Trgt" );
ref116.putEnumerated( id809, id810, id811 );
desc161.putReference( id808, ref116 );
executeAction( id807, desc161, DialogModes.NO );
////// collect invisibles;
for (var i = 0; i < layers.length; i++) {
var l = layers;
if (!l.visible) {
selLayers.push(l);
};
l.visible = visible;
};
// filter out layers within selected groups and smartify pixel layers and groups;
var theLayers = new Array;
for (var m = 0; m < selLayers.length; m++) {
var thisLayer = selLayers[m];
var check = true;
for (var n = 0; n < selLayers.length; n++) {
var theCheckLayer = selLayers[n];
var theParent = thisLayer.parent;
while (theParent != app.activeDocument) {
if (theParent == theCheckLayer) {
check = false;
};
theParent = theParent.parent
}
};
if (check == true) {theLayers.push(thisLayer)}
};
// smartify;
var theFinalLayers = new Array;
for (var n = 0; n < theLayers.length; n++) {
var thisLayer = smartify2010(theLayers[n]);
if (thisLayer != false) {theFinalLayers.push(thisLayer)}
};
return theFinalLayers;
};
////// function to smartify if not //////
function smartify2010 (theLayer) {
// make layers smart objects if they are not already;
app.activeDocument.activeLayer = theLayer;
// process pixel-layers and groups;
if (theLayer.kind == "LayerKind.GRADIENTFILL" || theLayer.kind == "LayerKind.LAYER3D" || theLayer.kind == "LayerKind.NORMAL" ||
theLayer.kind == "LayerKind.PATTERNFILL" || theLayer.kind == "LayerKind.SOLIDFILL" || theLayer.kind == "LayerKind.TEXT" ||
theLayer.typename == "LayerSet") {
var id557 = charIDToTypeID( "slct" );
var desc108 = new ActionDescriptor();
var id558 = charIDToTypeID( "null" );
var ref77 = new ActionReference();
var id559 = charIDToTypeID( "Mn " );
var id560 = charIDToTypeID( "MnIt" );
var id561 = stringIDToTypeID( "newPlacedLayer" );
ref77.putEnumerated( id559, id560, id561 );
desc108.putReference( id558, ref77 );
executeAction( id557, desc108, DialogModes.NO );
return app.activeDocument.activeLayer
};
// return smart objects;
if (theLayer.kind == LayerKind.SMARTOBJECT || theLayer.kind == "LayerKind.VIDEO") {return theLayer};
// if neither pixel-layer, group nor smart object return only masked adjustment layers;
if (theLayer.kind == "LayerKind.BLACKANDWHITE" || theLayer.kind == "LayerKind.BRIGHTNESSCONTRAST" ||
theLayer.kind == "LayerKind.CHANNELMIXER" || theLayer.kind == "LayerKind.COLORBALANCE" || theLayer.kind == "LayerKind.CURVES" ||
theLayer.kind == "LayerKind.EXPOSURE" || theLayer.kind == "LayerKind.GRADIENTMAP" || theLayer.kind == "LayerKind.HUESATURATION" ||
theLayer.kind == "LayerKind.INVERSION" || theLayer.kind == "LayerKind.LAYER3D" || theLayer.kind == "LayerKind.LEVELS" ||
theLayer.kind == "LayerKind.PHOTOFILTER" || theLayer.kind == "LayerKind.POSTERIZE" || theLayer.kind == "LayerKind.SELECTIVECOLOR" ||
theLayer.kind == "LayerKind.THRESHOLD" || theLayer.kind == "LayerKind.VIBRANCE") {
if (hasLayerMask() == true || hasVectorMask() == true) {
return theLayer
}
else {return false}
}
};
// from »Flatten All Masks.jsx« by jeffrey tranberry;
///////////////////////////////////////////////////////////////////////////////
// Function: hasLayerMask
// Usage: see if there is a raster layer mask
// Input: <none> Must have an open document
// Return: true if there is a vector mask
///////////////////////////////////////////////////////////////////////////////
function hasLayerMask() {
var hasLayerMask = false;
try {
var ref = new ActionReference();
var keyUserMaskEnabled = app.charIDToTypeID( 'UsrM' );
ref.putProperty( app.charIDToTypeID( 'Prpr' ), keyUserMaskEnabled );
ref.putEnumerated( app.charIDToTypeID( 'Lyr ' ), app.charIDToTypeID( 'Ordn' ), app.charIDToTypeID( 'Trgt' ) );
var desc = executeActionGet( ref );
if ( desc.hasKey( keyUserMaskEnabled ) ) {
hasLayerMask = true;
}
}catch(e) {
hasLayerMask = false;
}
return hasLayerMask;
};
///////////////////////////////////////////////////////////////////////////////
// Function: hasVectorMask
// Usage: see if there is a vector layer mask
// Input: <none> Must have an open document
// Return: true if there is a vector mask
///////////////////////////////////////////////////////////////////////////////
function hasVectorMask() {
var hasVectorMask = false;
try {
var ref = new ActionReference();
var keyVectorMaskEnabled = app.stringIDToTypeID( 'vectorMask' );
var keyKind = app.charIDToTypeID( 'Knd ' );
ref.putEnumerated( app.charIDToTypeID( 'Path' ), app.charIDToTypeID( 'Ordn' ), keyVectorMaskEnabled );
var desc = executeActionGet( ref );
if ( desc.hasKey( keyKind ) ) {
var kindValue = desc.getEnumerationValue( keyKind );
if (kindValue == keyVectorMaskEnabled) {
hasVectorMask = true;
}
}
}catch(e) {
hasVectorMask = false;
}
return hasVectorMask;
};
////// get number of history states //////
function historyStates () {
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("capp"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var appDesc = executeActionGet(ref);
var prefDesc = appDesc.getObjectValue(charIDToTypeID( "CchP" ));
var theHistoryStates = prefDesc.getInteger(charIDToTypeID( "HsSt" ));
return theHistoryStates
};
////// get memory usage percentage //////
function memoryUsage () {
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("capp"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var appDesc = executeActionGet(ref);
var prefDesc = appDesc.getObjectValue(charIDToTypeID( "CchP" ));
var theHistoryStates = prefDesc.getInteger(charIDToTypeID( "MmrU" ));
return theHistoryStates
};
////// set number of history states //////
function setHistoryStates (histStates, memUsage) {
// =======================================================
var idsetd = charIDToTypeID( "setd" );
var desc2 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref1 = new ActionReference();
var idPrpr = charIDToTypeID( "Prpr" );
var idCchP = charIDToTypeID( "CchP" );
ref1.putProperty( idPrpr, idCchP );
var idcapp = charIDToTypeID( "capp" );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref1.putEnumerated( idcapp, idOrdn, idTrgt );
desc2.putReference( idnull, ref1 );
var idT = charIDToTypeID( "T " );
var desc3 = new ActionDescriptor();
var idHsSt = charIDToTypeID( "HsSt" );
desc3.putInteger( idHsSt, histStates );
var idMmrU = charIDToTypeID( "MmrU" );
var idPrc = charIDToTypeID( "#Prc" );
desc3.putUnitDouble( idMmrU, idPrc, memUsage );
var idCchP = charIDToTypeID( "CchP" );
desc2.putObject( idT, idCchP, desc3 );
executeAction( idsetd, desc2, DialogModes.NO );
};
////// select layer additionally to selected layers //////
function addToSelectedLayers (layerIndex) {
// =======================================================
var idslct = charIDToTypeID( "slct" );
var desc8 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref7 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
ref7.putIndex( idLyr, layerIndex - 1 );
desc8.putReference( idnull, ref7 );
var idselectionModifier = stringIDToTypeID( "selectionModifier" );
var idselectionModifierType = stringIDToTypeID( "selectionModifierType" );
var idaddToSelection = stringIDToTypeID( "addToSelection" );
desc8.putEnumerated( idselectionModifier, idselectionModifierType, idaddToSelection );
var idMkVs = charIDToTypeID( "MkVs" );
desc8.putBoolean( idMkVs, false );
try {executeAction( idslct, desc8, DialogModes.NO )}
catch (e) {};
};
////// based on a getLayerId and getLayerItemIndexByLayerID by mike hale //////
function getLayerItemIndex(layer) {
app.activeDocument.activeLayer = layer;
//Assumes activeDocument and activeLayer
var ref = new ActionReference();
ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
d = executeActionGet(ref);
var id = d.getInteger(charIDToTypeID('LyrI'));
// get index;
var ref = new ActionReference();
ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "ItmI" ));
ref.putIdentifier( charIDToTypeID( "Lyr " ), id );
try{var itemIndex = executeActionGet(ref).getInteger(charIDToTypeID( "ItmI" ))}
catch(e){var itemIndex = true};
//
return itemIndex
};
////// re-select layers //////
function reselectLayers (myDocument, theLayers) {
var layerIndices = new Array;
for (var m = 0; m < theLayers.length; m++) {
layerIndices.push(getLayerItemIndex(theLayers[m]));
};
myDocument.activeLayer = theLayers[0];
for (var n = 1; n < theLayers.length; n++) {
addToSelectedLayers (layerIndices[n])
};
};