13 Replies Latest reply on Nov 19, 2014 7:31 AM by c.pfaffenbichler

    script to crop (no delete pixel) snap to ruler guide

    saigon80

      Hi everyone,

      I manually make 1 ruler guide and automatically create 3 ruler guides. These 4 ruler guide form a rectangular.

      Could I have a script that crop my image (no delete pixel) to fit that rectangular formed by the 4 guides .

       

      Thank you in advance for your help!

        • 1. Re: script to crop (no delete pixel) snap to ruler guide
          c.pfaffenbichler Level 9
          Could I have a script that crop my image (no delete pixel) to fit that rectangular formed by the 4 guides .

          If there are only four guides sure.

          What is the problem for you?

          • 2. Re: script to crop (no delete pixel) snap to ruler guide
            c.pfaffenbichler Level 9

            As for getting Guide’s values check this out

            http://ps-scripts.com/bb/viewtopic.php?f=9&t=5888&sid=4ff57991865206d5d31f00277338331c

             

            As for the cropping you can record it with ScriptingListener.plugin and use the code with the values collected in the Script.

            • 3. Re: script to crop (no delete pixel) snap to ruler guide
              JJMack Most Valuable Participant

              If you then make a selection of that rectangle area.  A script could re-size your image layer to fill that area, position it  and mask off any excess if the aspect ratio of the image is not the same as the rectangle's area.

              • 4. Re: script to crop (no delete pixel) snap to ruler guide
                JJMack Most Valuable Participant

                I had a fight scripting it. Strange thing were happing when I was testing. There seems to be a bug in scripting in cs6 -> CC 2014. Re-size layer seems to back up a history step. undoing the last step.  When I was using suspend history states I was loosing the last guide I set or the layer mask from my last test that I deleted the mask was restored and the script would fail.

                http://www.mouseprints.net/old/dpr/FitImageToGuides.jsx

                 

                /* ==========================================================
                // 2014  John J. McAssey (JJMack)
                // ======================================================= */
                
                // This script is supplied as is. It is provided as freeware.
                // The author accepts no liability for any problems arising from its use.
                
                /* Help Category note tag menu can be used to place script in automate menu
                <javascriptresource>
                <about>$$$/JavaScripts/FitImageToGuides/About=JJMack's FitImageToGuides .^r^rCopyright 2014 Mouseprints.^r^rFour and only four guides are required</about>
                <category>JJMack's Script</category>
                </javascriptresource>
                */
                
                // enable double-clicking from Mac Finder or Windows Explorer
                #target photoshop // this command only works in Photoshop CS2 and higher
                
                // bring application forward for double-click events
                app.bringToFront();
                
                // ensure at least one document open
                if (!documents.length) alert('There are no documents open.', 'No Document');
                else {
                  // declare Global variables
                
                  main(); // at least one document exists proceed
                  //app.activeDocument.suspendHistory('Fix Image to Guides','main()');  // seems to be a problem layer is resize Photoshop back up a history step ?
                }
                ///////////////////////////////////////////////////////////////////////////////
                //                            main function                                  //
                ///////////////////////////////////////////////////////////////////////////////
                function main() {
                  // declare local variables
                  var orig_ruler_units = app.preferences.rulerUnits;
                  var orig_type_units = app.preferences.typeUnits;
                  var orig_display_dialogs = app.displayDialogs;
                  app.preferences.rulerUnits = Units.PIXELS; // Set the ruler units to PIXELS
                  app.preferences.typeUnits = TypeUnits.POINTS;   // Set Type units to POINTS
                  app.displayDialogs = DialogModes.NO; // Set Dialogs off
                  try { code(); }
                  // display error message if something goes wrong
                  catch(e) { alert(e + ': on line ' + e.line, 'Script Error', true); }
                  app.displayDialogs = orig_display_dialogs; // Reset display dialogs
                  app.preferences.typeUnits  = orig_type_units; // Reset ruler units to original settings
                  app.preferences.rulerUnits = orig_ruler_units; // Reset units to original settings
                }
                ///////////////////////////////////////////////////////////////////////////////
                //                           main function end                               //
                ///////////////////////////////////////////////////////////////////////////////
                
                /////////////////////////////////////////////////////////////////////////////////////
                // The real code is embedded into this function so that at any point it can return //
                // to the main line function to let it restore users edit environment and end      //
                /////////////////////////////////////////////////////////////////////////////////////
                function code() {
                  if (app.activeDocument.guides.length != 4) { alert("Four and only four Guides are required"); return; } // quit
                  // get guides;
                  var theVert = new Array;
                  var theHor = new Array;
                  for (var m = 0; m < app.activeDocument.guides.length; m++) {
                  if (app.activeDocument.guides[m].direction == Direction.HORIZONTAL) {theVert.push(app.activeDocument.guides[m].coordinate)}
                  else {theHor.push(app.activeDocument.guides[m].coordinate)}
                    };
                  if (theHor.length != 2 || theVert.length != 2) { alert("Four Guides two vertical and two horizontal are required"); return; } // quit
                  getTarget=getSelectedLayersIdx();
                  if (getTarget.length!=1){ alert("The number of layers targeted is " + getTarget.length ); return; } // quit
                  if (app.activeDocument.activeLayer.isBackgroundLayer ) { alert("Can not resize the background layer"); return; } // quit
                  if (!app.activeDocument.activeLayer.visible ) { alert("Active layer is  not visible"); return; } // quit
                  //if (hasLayerMask()) { alert("Active layer is  Masked"); return; } // quit
                  if (app.activeDocument.activeLayer.kind == LayerKind.NORMAL  || app.activeDocument.activeLayer.kind == LayerKind.SMARTOBJECT && hasLayerMask()) { deleteLayerMask ();}
                  if (app.activeDocument.activeLayer.kind != LayerKind.NORMAL  && app.activeDocument.activeLayer.kind != LayerKind.SMARTOBJECT )  {
                  alert("Active layer is " + app.activeDocument.activeLayer.kind); return; } // quit
                  // set selection to the ared defined but the guide lines the selectiom may get undone bt the bug in .resize() backing up a steo in histoty ???
                  app.activeDocument.selection.select([[theHor[0], theVert[0]], [theHor[1], theVert[0]], [theHor[1], theVert[1]], [theHor[0], theVert[1]]]);
                  // resize current normal layer or smart object layer to just cover selection canvas area aspect ratio and size and mask off any overflow
                  var SB = app.activeDocument.selection.bounds; // Get selection bounds
                  var SWidth = (SB[2].value) - (SB[0].value); // Area width
                  var SHeight = (SB[3].value) - (SB[1].value); // Area height
                  var LB = app.activeDocument.activeLayer.bounds; // Get Active layers bounds
                  var LWidth = (LB[2].value) - (LB[0].value); // Area width
                  var LHeight = (LB[3].value) - (LB[1].value); // Area height
                  var userResampleMethod = app.preferences.interpolation; // Save interpolation settings
                  app.preferences.interpolation = ResampleMethod.BICUBIC; // resample interpolation bicubic
                  try {
                  if (LWidth/LHeight<SWidth/SHeight) { // layer's Aspect Ratio less the Canvas area Aspect Ratio
                  var percentageChange = ((SWidth/LWidth)*100); // Resize to canvas area width
                  app.activeDocument.activeLayer.resize(percentageChange,percentageChange,AnchorPosition.MIDDLECENTER);
                  }
                  else {
                  var percentageChange = ((SHeight/LHeight)*100); // resize to canvas area height
                  app.activeDocument.activeLayer.resize(percentageChange,percentageChange,AnchorPosition.MIDDLECENTER);
                  }
                  }
                  catch(e) {
                  app.preferences.interpolation = userResampleMethod; // Reset interpolation setting
                  selectFront(); // Photoshop make top layer current when none are targeted
                  code(); // Retry  with top visible layer selected targeted
                  return; // rest would have been done during the retry
                  }
                  app.preferences.interpolation = userResampleMethod; // Reset interpolation setting
                  // Seems to be a bug in  resize() the document seems to first be backed up a step in history
                  app.activeDocument.selection.select([[theHor[0], theVert[0]], [theHor[1], theVert[0]], [theHor[1], theVert[1]], [theHor[0], theVert[1]]]); // redo the selection
                  align('AdCH'); // align to horizontal center
                  align('AdCV'); // align to vertical center
                  addLayermask(); // add layer mask
                }
                /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Helper Functions
                /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                function align(method) {
                  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){}
                }
                ///////////////////////////////////////////////////////////////////////////////
                // 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 getSelectedLayersIdx(){
                      var selectedLayers = new Array;
                      var ref = new ActionReference();
                      ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
                      var desc = executeActionGet(ref);
                      if( desc.hasKey( stringIDToTypeID( 'targetLayers' ) ) ){
                         desc = desc.getList( stringIDToTypeID( 'targetLayers' ));
                          var c = desc.count
                          var selectedLayers = new Array();
                          for(var i=0;i<c;i++){
                            try{
                               activeDocument.backgroundLayer;
                               selectedLayers.push(  desc.getReference( i ).getIndex() );
                            }catch(e){
                               selectedLayers.push(  desc.getReference( i ).getIndex()+1 );
                            }
                          }
                       }else{
                         var ref = new ActionReference();
                         ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "ItmI" ));
                         ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
                         try{
                            activeDocument.backgroundLayer;
                            selectedLayers.push( executeActionGet(ref).getInteger(charIDToTypeID( "ItmI" ))-1);
                         }catch(e){
                            selectedLayers.push( executeActionGet(ref).getInteger(charIDToTypeID( "ItmI" )));
                         }
                      }
                      return selectedLayers;
                };
                function selectFront() {
                // Alt+. shortcut select ftont visible layer
                var idslct = charIDToTypeID( "slct" );
                    var desc250 = new ActionDescriptor();
                    var idnull = charIDToTypeID( "null" );
                        var ref207 = new ActionReference();
                        var idLyr = charIDToTypeID( "Lyr " );
                        var idOrdn = charIDToTypeID( "Ordn" );
                        var idFrnt = charIDToTypeID( "Frnt" );
                        ref207.putEnumerated( idLyr, idOrdn, idFrnt );
                    desc250.putReference( idnull, ref207 );
                    var idMkVs = charIDToTypeID( "MkVs" );
                    desc250.putBoolean( idMkVs, false );
                executeAction( idslct, desc250, DialogModes.NO );
                }
                function deleteLayerMask (apply) {
                // Delet Layer mask default to not apply first
                if (apply == undefined) {var apply = false};
                try {
                var idDlt = charIDToTypeID( "Dlt " );
                    var desc9 = new ActionDescriptor();
                    var idnull = charIDToTypeID( "null" );
                        var ref5 = new ActionReference();
                        var idChnl = charIDToTypeID( "Chnl" );
                        var idChnl = charIDToTypeID( "Chnl" );
                        var idMsk = charIDToTypeID( "Msk " );
                        ref5.putEnumerated( idChnl, idChnl, idMsk );
                    desc9.putReference( idnull, ref5 );
                    var idAply = charIDToTypeID( "Aply" );
                    desc9.putBoolean( idAply, apply );
                executeAction( idDlt, desc9, DialogModes.NO );
                }
                catch (e) {}
                };
                function addLayermask(){
                // Add layer Mask
                var idMk = charIDToTypeID( "Mk  " );
                    var desc52 = new ActionDescriptor();
                    var idNw = charIDToTypeID( "Nw  " );
                    var idChnl = charIDToTypeID( "Chnl" );
                    desc52.putClass( idNw, idChnl );
                    var idAt = charIDToTypeID( "At  " );
                        var ref19 = new ActionReference();
                        var idChnl = charIDToTypeID( "Chnl" );
                        var idChnl = charIDToTypeID( "Chnl" );
                        var idMsk = charIDToTypeID( "Msk " );
                        ref19.putEnumerated( idChnl, idChnl, idMsk );
                    desc52.putReference( idAt, ref19 );
                    var idUsng = charIDToTypeID( "Usng" );
                    var idUsrM = charIDToTypeID( "UsrM" );
                    var idRvlS = charIDToTypeID( "RvlS" );
                    desc52.putEnumerated( idUsng, idUsrM, idRvlS );
                executeAction( idMk, desc52, DialogModes.NO );
                // Un link layer mask just added fron the layers content
                var idsetd = charIDToTypeID( "setd" );
                    var desc2 = new ActionDescriptor();
                    var idnull = charIDToTypeID( "null" );
                        var ref1 = new ActionReference();
                        var idLyr = charIDToTypeID( "Lyr " );
                        var idOrdn = charIDToTypeID( "Ordn" );
                        var idTrgt = charIDToTypeID( "Trgt" );
                        ref1.putEnumerated( idLyr, idOrdn, idTrgt );
                    desc2.putReference( idnull, ref1 );
                    var idT = charIDToTypeID( "T   " );
                        var desc3 = new ActionDescriptor();
                        var idUsrs = charIDToTypeID( "Usrs" );
                        desc3.putBoolean( idUsrs, false );
                    var idLyr = charIDToTypeID( "Lyr " );
                    desc2.putObject( idT, idLyr, desc3 );
                executeAction( idsetd, desc2, DialogModes.NO );
                }
                
                
                • 5. Re: script to crop (no delete pixel) snap to ruler guide
                  saigon80 Level 1

                  Thank you so much.

                  That works !

                  • 6. Re: script to crop (no delete pixel) snap to ruler guide
                    c.pfaffenbichler Level 9

                    // make crop based on 4 guides;

                    // 2014, use at your own risk;

                    #target "photoshop-70.032"

                    if (app.documents.length > 0) {main()};

                    ////// function //////

                    function main () {

                    // set to pixels and 72ppi;

                    var myDocument = app.activeDocument;

                    var originalRulerUnits = app.preferences.rulerUnits;

                    app.preferences.rulerUnits = Units.PIXELS;

                    var originalResolution = myDocument.resolution;

                    myDocument.resizeImage (null, null, 72, ResampleMethod.NONE);

                    if (myDocument.guides.length == 4) {

                    // get guides;

                    var theVert = new Array;

                    var theHor = new Array;

                    for (var m = 0; m < myDocument.guides.length; m++) {

                      if (myDocument.guides[m].direction == Direction.HORIZONTAL) {theVert.push(myDocument.guides[m].coordinate)}

                      else {theHor.push(myDocument.guides[m].coordinate)}

                      };

                    // crop;

                    if (theHor.length == 2 && theVert.length == 2) {

                      cropThis(Number(theVert[0]), Number(theHor[0]), Number(theVert[1]), Number(theHor[1]))

                      };

                    };

                    // reset;

                    app.preferences.rulerUnits = originalRulerUnits;

                    myDocument.resizeImage (null, null, originalResolution, ResampleMethod.NONE);

                    };

                    ////// crop //////

                    function cropThis (x1, x2, x3, x4) {

                    // =======================================================

                    var idCrop = charIDToTypeID( "Crop" );

                        var desc7 = new ActionDescriptor();

                        var idT = charIDToTypeID( "T   " );

                            var desc8 = new ActionDescriptor();

                            var idTop = charIDToTypeID( "Top " );

                            var idRlt = charIDToTypeID( "#Rlt" );

                            desc8.putUnitDouble( idTop, idRlt, x1 );

                            var idLeft = charIDToTypeID( "Left" );

                            var idRlt = charIDToTypeID( "#Rlt" );

                            desc8.putUnitDouble( idLeft, idRlt, x2 );

                            var idBtom = charIDToTypeID( "Btom" );

                            var idRlt = charIDToTypeID( "#Rlt" );

                            desc8.putUnitDouble( idBtom, idRlt, x3 );

                            var idRght = charIDToTypeID( "Rght" );

                            var idRlt = charIDToTypeID( "#Rlt" );

                            desc8.putUnitDouble( idRght, idRlt, x4 );

                        var idRctn = charIDToTypeID( "Rctn" );

                        desc7.putObject( idT, idRctn, desc8 );

                        var idAngl = charIDToTypeID( "Angl" );

                        var idAng = charIDToTypeID( "#Ang" );

                        desc7.putUnitDouble( idAngl, idAng, 0.000000 );

                        var idDlt = charIDToTypeID( "Dlt " );

                        desc7.putBoolean( idDlt, false );

                        var idcropAspectRatioModeKey = stringIDToTypeID( "cropAspectRatioModeKey" );

                        var idcropAspectRatioModeClass = stringIDToTypeID( "cropAspectRatioModeClass" );

                        var idtargetSize = stringIDToTypeID( "targetSize" );

                        desc7.putEnumerated( idcropAspectRatioModeKey, idcropAspectRatioModeClass, idtargetSize );

                    executeAction( idCrop, desc7, DialogModes.NO );

                    };

                    • 7. Re: script to crop (no delete pixel) snap to ruler guide
                      JJMack Most Valuable Participant

                      I beginning to think there a big problem on Photoshop scripting one my windows machine CS6 CC and CC 2014

                      her is a before and after screen capture before running your script and after. Wound up with a 1 pixel high document

                      Capture.jpg

                      • 8. Re: script to crop (no delete pixel) snap to ruler guide
                        c.pfaffenbichler Level 9

                        Seems more likely to be a problem with the Script …

                        Could you please post a jpg of that image (fill it with some color maybe, just keep the guides)?

                        • 9. Re: script to crop (no delete pixel) snap to ruler guide
                          JJMack Most Valuable Participant

                          Before posting the file I copy and pasted you code again and save it to CropToGuides.jsx and now it works. Do not know what went wrong the first time. The problem must have been in the test document I created. For I just tested the old copy paste I tried on my new test document and that too works.  I wish I had save the original test document I tried.

                           

                          I have also coded around Adobe's layer re-size bug so now my FitImageToGuides.jsx works like it should.  http://www.mouseprints.net/old/dpr/FitImageToGuides.jsx

                          • 10. Re: script to crop (no delete pixel) snap to ruler guide
                            JJMack Most Valuable Participant

                            Yes you script seems to do some unexpected thing depending on document content. Here is a psd file http://www.mouseprints.net/old/dpr/Untitled-1.psd

                             

                            After opening it I dragged the right most guide over to to the left side of the girl. note that she is left inside the  giides and the black is on the right. I run your script and the crop size is correct but the girl ends up or the right and the black layer below her shows on the left not the right. I look are the script and did not understand the need for the resolution changes  so I tried not doing them that made things worse.

                             

                            Capture.jpg

                            The problem seems related to the order in which the guide are made.  You may need to sort their order. using the same psd I open it cleared the guides dragged out a guide to the left side then to the right side then o the bottom side and last to the to side. and ran you script. here the result.

                            Capture.jpg

                            Using right left top bottom order I get the expected

                            Capture.jpg

                            Changed your script to  your code that make a selection of the area that the guides define and crop the selection  this  works.

                            // make crop based on 4 guides;
                            // 2014, use at your own risk;
                            #target "photoshop-70.032"
                            if (app.documents.length > 0) {main()}; // If there is and open document
                            ////// mainline function //////
                            function main () {
                            var myDocument = app.activeDocument;
                            var originalRulerUnits = app.preferences.rulerUnits;
                            app.preferences.rulerUnits = Units.PIXELS;
                            if (myDocument.guides.length == 4) { // get guides;
                                 var theVert = new Array;
                                 var theHor = new Array;
                                    for (var m = 0; m < myDocument.guides.length; m++) {
                                        if (myDocument.guides[m].direction == Direction.HORIZONTAL) {theVert.push(myDocument.guides[m].coordinate)}
                                        else {theHor.push(myDocument.guides[m].coordinate)}
                                        };
                                 if (theHor.length == 2 && theVert.length == 2) {
                                      myDocument.selection.select([[theHor[0], theVert[0]], [theHor[1], theVert[0]], [theHor[1], theVert[1]], [theHor[0], theVert[1]]]);
                                      crop(); // crop selection;
                                      myDocument.selection.deselect();
                                      };
                                 else {alert("Two horizontal guide and two vertical guides are required");}
                                 };
                            else {alert("Four and only four guides are required");}
                            app.preferences.rulerUnits = originalRulerUnits; // reset;
                            };
                            ////// Helper functions //////
                            function crop() {
                            // Crop selection
                            var idCrop = charIDToTypeID( "Crop" );
                                var desc92 = new ActionDescriptor();
                                var idDlt = charIDToTypeID( "Dlt " );
                                desc92.putBoolean( idDlt, true );
                            executeAction( idCrop, desc92, DialogModes.NO );
                            };
                            
                            • 11. Re: script to crop (no delete pixel) snap to ruler guide
                              c.pfaffenbichler Level 9
                              I run your script and the crop size is correct but the girl ends up or the right and the black layer below her shows on the left not the right.

                              My tests had been too simple to notice the issue.

                              Changing the crop-line to

                              cropThis(Number(Math.min(theVert[0], theVert[1])), Number(Math.min(theHor[0],theHor[1])), Number(Math.max(theVert[0], theVert[1])), Number(Math.max(theHor[0],theHor[1])))

                              should also work.

                              • 12. Re: script to crop (no delete pixel) snap to ruler guide
                                JJMack Most Valuable Participant

                                Yes I see it would. The thing is I don't know javascript. I'm just a hacker and would never have thought of using Math min and max to sort things out. So now I'll try to add that to my gray matter but I'll be 74 next month and it seems to be forgetting more then its remembering these days.

                                 

                                Thank for being here  I have often stolen you code you have appended here.  Your a great asset here....

                                • 13. Re: script to crop (no delete pixel) snap to ruler guide
                                  c.pfaffenbichler Level 9

                                  Let me return the compliment – you have been spending a lot of time on these Forums helping other Photoshop users.

                                  Hopefully you’ll have the inclination to continue to do so for a while yet.