Copy link to clipboard
Copied
Hi,
I have a regular need to level horizon and do so by using ruler>Image Rotation>Arbitary
Id like to streamline this process by drawing line 1st then the rest of the process is done by action. Catch is that the degree value will be different every time.
Is there a way to do this in an action where the different value is read every time?
Thanks in advance
Todd
I forgot to mention that you can just record the ruler tool straighten button press into an action and use an F-key, where it will use the script mentioned above. No need for a custom installation of the script unless you require a custom keyboard shortcut.
Copy link to clipboard
Copied
It's currently a 2-step process... Draw the line with the ruler tool to set the angle, then press the "straighten layer" button – which calls the "Straighten.jsx" script that ships with Photoshop.
// (c) Copyright 2011. Adobe Systems, Incorporated. All rights reserved.
//
// Straighten.jsx - Straighten and crop a photo, based on the ruler position.
// Added prototype Layer transform via shift modifier-steveg, 2011
//
// John Peterson, Adobe Systems, 2009
//
/*
@@@BUILDINFO@@@ Straighten.jsx 3.0.0.4
*/
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
<javascriptresource>
<name>$$$/JavaScripts/Straighten/Menu=Straighten to Ruler</name>
<menu>automate</menu>
</javascriptresource>
// END__HARVEST_EXCEPTION_ZSTRING
*/
// on localized builds we pull the $$$/Strings from a .dat file
$.localize = true;
var g_StackScriptFolderPath = app.path + "/"+ localize("$$$/ScriptingSupport/InstalledScripts=Presets/Scripts") + "/"
+ localize("$$$/private/Exposuremerge/StackScriptOnly=Stack Scripts Only/");
$.evalFile( g_StackScriptFolderPath + "Geometry.jsx");
$.evalFile(g_StackScriptFolderPath + "Terminology.jsx");
// Create a base object to scope the rest of the functions in the file
function Straightener()
{
this.pluginName = "Straighten";
}
straighten = new Straightener();
straighten.getUnitPoint = function( desc )
{
var x = desc.getUnitDoubleValue( kxStr );
var y = desc.getUnitDoubleValue( kyStr );
return new TPoint( x, y );
}
// Special version of crop supporting the hidePixels option (DOM FIX)
straighten.hideCrop = function( cropRect, hidePixels )
{
if (typeof hidePixels == "undefined")
hidePixels = true;
// Hiding pixels does not work on simple documents
if ((app.activeDocument.layers.length == 1)
&& (app.activeDocument.activeLayer.isBackgroundLayer))
hidePixels = false;
var desc = new ActionDescriptor();
var cropDesc = new ActionDescriptor();
cropDesc.putUnitDouble( enumTop, unitPixels, cropRect.fTop );
cropDesc.putUnitDouble( enumLeft, unitPixels, cropRect.fLeft );
cropDesc.putUnitDouble( keyBottom, unitPixels, cropRect.fBottom );
cropDesc.putUnitDouble( enumRight, unitPixels, cropRect.fRight );
desc.putObject( keyTo, classRectangle, cropDesc );
desc.putUnitDouble( enumAngle, unitAngle, 0.0 );
if (hidePixels)
desc.putBoolean( eventHide, true );
executeAction( eventCrop, desc, DialogModes.NO );
}
// Remove the ruler and update the display (DOM FIX)
straighten.clearRuler = function()
{
desc = new ActionDescriptor();
var eventClearRuler = app.stringIDToTypeID("clearRuler");
executeAction( eventClearRuler, desc, DialogModes.NO );
}
// Get the endpoints from the ruler (DOM FIX)
straighten.getRulerEndpoints = function()
{
var desc1 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putProperty( classProperty, krulerPointsStr );
ref1.putEnumerated( classDocument, typeOrdinal, enumTarget );
desc1.putReference( typeNULL, ref1 );
var result = executeAction( eventGet, desc1, DialogModes.NO );
if (result.hasKey( kpointsStr ))
{
var i, ptList = result.getList( kpointsStr );
// The middle item in the list is an unused "midpoint" (currently == p0)
var p0 = this.getUnitPoint( ptList.getObjectValue(0) );
var p1 = this.getUnitPoint( ptList.getObjectValue(2) );
if (p0.fX < p1.fX)
return [p0, p1];
else
return [p1, p0];
}
else
return [];
}
// Given two ruler endpoints, compute the rotation (in radians)
// needed to make the ruler horizontal or vertical.
straighten.getRotationAngle = function( p0, p1 )
{
// Perfectly horizontal or vertical - no rotation
if ((p0.fX == p1.fX) || (p0.fY == p1.fY))
return 0.0;
var a, t, v = p1 - p0;
if (Math.abs(v.fY) > Math.abs(v.fX))
{
// If the line is mostly vertical, adjust the angle to
// straighten to the vertical axis.
t = v.fX;
v.fX = v.fY;
v.fY = t;
if (v.fX < 0)
{
v.fX = - v.fX;
a = v.vectorAngle();
}
else
a = -v.vectorAngle();
}
else
a = v.vectorAngle();
return -a;
}
// Given an angle and a rectangle, compute the rotated
// rectangle and a new bounding rectangle for it.
straighten.computeRotation = function( angle, rect )
{
// Rotate rect about the origin, and find the bounds of the rotated rectangle.
rect.setCenter( TPoint.kOrigin );
var i;
this.fRotatedPoints = rect.getCornerPoints();
this.fRotatedBounds = new TRect(0,0,0,0);
for (i in this.fRotatedPoints)
{
this.fRotatedPoints[i] = this.fRotatedPoints[i].rotate(angle);
this.fRotatedBounds.extendTo( this.fRotatedPoints[i] );
}
// Move the origin back to top left corner of the enclosing rect,
// the new center is based on that.
this.fRotatedBounds.offset( -this.fRotatedBounds.getTopLeft() );
this.fNewCenter = this.fRotatedBounds.getCenter();
for (i in this.fRotatedPoints)
this.fRotatedPoints[i] += this.fNewCenter;
}
// Given an rectange and a rotation angle, find a new rectangle with the
// same proportions that fits inside the original.
straighten.getCropRect = function( angle, rect )
{
this.computeRotation( angle, rect );
rect.setCenter( this.fNewCenter );
var cropPts = [];
// So sign test works below
if (angle < -Math.PI)
angle += 2*Math.PI;
var off1 = (angle > 0) ? 3 : 0;
var off2 = (angle > 0) ? 0 : 1;
// Draw lines from the center through the corners of the original rect.
// Record where those lines intersect the rotated rect.
var ii, rectPoints = [rect.getTopLeft(), rect.getTopRight(), rect.getBottomRight(), rect.getBottomLeft()];
for (ii in rectPoints)
{
i = Number(ii); // Whacky Javascript uses strings, not numbers, for index
cropPts[i] = TPoint.lineSegmentIntersect( this.fRotatedPoints[(i + off1) % 4],
this.fRotatedPoints[(i + off2) % 4],
this.fNewCenter, rectPoints[i] );
}
// The new crop is based on the minimum bounds of the intersections found above.
var cropRect = new TRect( Math.max(cropPts[0].fX, cropPts[3].fX), Math.max(cropPts[0].fY, cropPts[1].fY),
Math.min(cropPts[1].fX, cropPts[2].fX), Math.min(cropPts[2].fY, cropPts[3].fY) );
return cropRect;
}
straighten.getRulerAngle = function()
{
var rulerPts = this.getRulerEndpoints();
if (rulerPts.length == 0)
return 0.0;
return this.getRotationAngle( rulerPts[0], rulerPts[1] );
}
// Straighten the layer to the ruler with transform. Returns the rotation angle,
// or zero if there was no rotation (or no ruler).
straighten.rotateLayerToRuler = function()
{
var angle = this.getRulerAngle();
if (angle == 0.0)
return;
if(!app.activeDocument.activeLayer.visible){
alert(localize("$$$/Straighten/StraightenHiddenLayer=Could not straighten the layer because the target layer is hidden."));
return;
}
layerRef = app.activeDocument.activeLayer;
layerRef.rotate( angle * 180.0 / Math.PI );
return angle;
}
// Straighten the image to the ruler. Returns the rotation angle,
// or zero if there was no rotation (or no ruler).
straighten.rotateCanvasToRuler = function()
{
var angle = this.getRulerAngle();
app.activeDocument.rotateCanvas( angle * 180.0 / Math.PI );
return angle;
}
// Crop the image to the bounds, rotated by angle (in radians)
straighten.cropRotatedImage = function( angle, bounds )
{
var newBounds = this.getCropRect( angle, bounds );
// Ideally we'd report an error here, but since it's past RC, we'll just silently fail.
if (newBounds.isValid())
this.hideCrop( newBounds, true );
// app.activeDocument.crop( [UnitValue(newBounds.fLeft, 'px'), UnitValue(newBounds.fTop, 'px'),
// UnitValue(newBounds.fRight, 'px'), UnitValue(newBounds.fBottom, 'px')] );
}
// Original functionality, now not used.
straighten.rotateAndCropCanvas = function ()
{
var angle, bounds = new TRect( 0, 0, app.activeDocument.width.as('px'), app.activeDocument.height.as('px') );
angle = straighten.rotateCanvasToRuler();
straighten.cropRotatedImage( angle, bounds );
}
// Main interactive entry.
straighten.doInteractive = function()
{
var angle, bounds = new TRect( 0, 0, app.activeDocument.width.as('px'), app.activeDocument.height.as('px') );
if (app.activeDocument.activeLayer.isBackgroundLayer == true)
app.activeDocument.activeLayer.isBackgroundLayer = false;
angle = straighten.rotateLayerToRuler()
// alert(localize("$$$/Straighten/Script/NoBGLayer=Operation cannot be performed on background layer"));
if (angle != 0.0)
straighten.clearRuler();
}
// Set runstraightenFromScript if you want to load this script w/o running it.
if ((typeof(runstraightenFromScript) == 'undefined')
|| (runstraightenFromScript==false))
straighten.doInteractive();
What other steps do you wish to do via action?
Another option is using Filter > Camera Raw Filter to geometry to transform, which can be automated via a script:
https://community.adobe.com/t5/photoshop/jsx-gt-camera-raw-filter-gt-transform-possible/m-p/11197302
/*
https://community.adobe.com/t5/photoshop/jsx-gt-camera-raw-filter-gt-transform-possible/m-p/11197302?page=1#M338216
JSX > Camera Raw Filter > Transform. Possible?
Camera Raw Filter - Transform Values:
1 = Auto, 2 = Full, 3 = Level, 4 = Vertical
*/
transformCRF(transValue = 1);
function transformCRF() {
var actDesc = new ActionDescriptor();
var transCode = charIDToTypeID("PerU");
actDesc.putInteger(transCode, transValue);
executeAction(stringIDToTypeID("Adobe Camera Raw Filter"), actDesc, DialogModes.NO);
}
Copy link to clipboard
Copied
Thanks @Stephen Marsh Is there a way to linik that script to a function button, say F13?
Copy link to clipboard
Copied
Thanks @Stephen Marsh Is there a way to linik that script to a function button, say F13?
By @Tango..........
If you want an F-key, then record the execution of the script into an action.
Either install the script in the Presets/Scripts folder or use the File > Scripts > Browse command, more here:
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
P.S. If you install the script (not browse), then it can have a standard keyboard shortcut applied via Edit > Keyboard Shortcuts.
Copy link to clipboard
Copied
I forgot to mention that you can just record the ruler tool straighten button press into an action and use an F-key, where it will use the script mentioned above. No need for a custom installation of the script unless you require a custom keyboard shortcut.