Rhaenys
Rhaenys

Reputation: 190

Changing selected text layers language with photoshop script

Though I managed to put a functional script to change the dictionary language of selected layers together, it's not quite what I need yet.

I'm currently retrieving the index of my selected layers, using them to get the layer names—it's the problematic part—then working the magic to change their language based on an option chosen in a custom dialog box I display.

The use of layers names is a very half-assed solution, I need precision, I need to be able to change the language based on a layer ID or index. Not only my script is prone to names conflicts but I have no idea of where those selected layers will be, if on the root of the document or inside 5 groups stack.

Here is my current code:

The main function

function main() {
    // Create window with language selection and retrieve selected value
    var lang = createWindow();

   // Retrieve selected layers
   var selectedLayers = getSelectedLayersIdx();

   // Look at each entry and change language when possible
   for(var a in selectedLayers){
        try {
           setLangByIndex(Number( selectedLayers[a]), lang );
        } catch(err) {   }
   }
}

(createWindow returns a lang value, skipping that)

Mike's get Idx

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;
}

The function which sets the language

function setLangByIndex(idx, lang){
    ref = new ActionReference();
    ref.putProperty( charIDToTypeID("Prpr") , charIDToTypeID( "Nm  " ));
    ref.putIndex( charIDToTypeID( "Lyr " ), idx );
    var name = executeActionGet(ref).getString(charIDToTypeID( "Nm  " ));

    var el = activeDocument.layers.getByName( name );
    // Temp quick block; MUST UPDATE IN CASE OF NEW LANGUAGES
     if (lang == 'pt_br' )
        el.textItem.language = Language.BRAZILLIANPORTUGUESE;
    else if (lang == 'en_us' )
        el.textItem.language = Language.ENGLISHUSA;
    else
        el.textItem.language = Language.ENGLISHUK;
}

Workarounds I inefficiently tried:

Upvotes: 4

Views: 1385

Answers (1)

Rhaenys
Rhaenys

Reputation: 190

After I noticed some annoying glitches related to Paragraph Styles (the ones you can define as the default for your text on PS, not the JS object) I gave up on being stubborn and stopped avoiding doing a new loop through the indexes array to reselect the layers when done.

/*
   *  Based in Mike Hale's "getSelectedLayersIdx" script and Adobe's Scripting Listener dump.
      Based the panel on Davide Barranca's code: http://www.davidebarranca.com/2012/10/scriptui-window-in-photoshop-palette-vs-dialog/

      Useful Resources:
      Photoshop char list: http://www.pcpix.com/Photoshop/char.htm
      ActionDescriptor methods: http://jongware.mit.edu/pscs5js_html/psjscs5/pc_ActionDescriptor.html
      TextItem methods: http://jongware.mit.edu/pscs5js_html/psjscs5/pc_TextItem.html
      You can fish for language codes here: http://wingp.googlecode.com/svn/photogp/photoshopapi/photoshop/PIStringTerminology.h
*/

/* ______________________________ SCRIPT ______________________________ */
var scriptAlert = "Language Change says";

// MUST UPDATE IF YOU WANT TO CHANGE THE AVAILABLE LANGUAGES
// The available languages, as { text displayed on dialog window : codename }. See notes above for more language codes
var langs = {};
langs['Português (Brasil)'] = 'brazilianPortugueseLanguage';
langs['English (UK)'] = 'ukenglishLanguage';
langs['English (USA)'] = 'englishLanguage';

if(app.documents.length>0){ // the script will be executed  only if an active document exists.

    var imgName = activeDocument.name;
    var extreme= imgName.substr(imgName.length - 5, 5);
    var dot = extreme.lastIndexOf(".");

    if(dot == -1) { // Checks if the active document  was saved or not.
        alert("Please, save your document!", scriptAlert, false);
    } else {
        main(); // if the active document was saved, go ahead! 
    }
}


function createWindow() { // Creates a panel asking for input
        var isDone, win, windowResource, radio_group, cancel, rb, i;

        //sentinel variable
        isDone = false;
        cancel = false;

        // Build dialog radio buttons
        i = 0;
        for( var k in langs ) {
            if( i==0 )
                rb = "rb"+i+": RadioButton { text:  '"+k+"', value: 1 }, \ ";
            else
                rb += "rb"+i+": RadioButton { text:  '"+k+"' }, \ ";
            i++;
        }

        windowResource = "palette {  \
            orientation: 'column', \
            alignChildren: ['fill', 'top'],  \
            preferredSize:[300, 130], \
            text: 'Change Spell Check Language',  \
            margins:15, \
            \
            optPanel: Panel { \
                orientation: 'row', \
                alignChildren: 'left', \
                margins:15, \
                text: ' Select the desired dictionary language for the select text ', \ "+  rb + " \
                } \
            \
            bottomGroup: Group{ \
                cancelButton: Button { text: 'Cancel', properties:{name:'cancel'}, size: [120,24], alignment:['right', 'center'] }, \
                applyButton: Button { text: 'Ok', properties:{name:'ok'}, size: [120,24], alignment:['right', 'center'], active: true }, \
            }\
        }";
        win = new Window(windowResource);

        radio_group = win.optPanel; // set group

        // Match options with language
        function selected_rbutton (rbuttons)
        {
             for (var i = 0; i < rbuttons.children.length; i++)
                 if (rbuttons.children[i].value == true)
                    return langs[rbuttons.children[i].text];
         }

        win.bottomGroup.cancelButton.onClick = function() {
            win.close();
            return cancel = true;
        };      
        // Button listeners
        win.bottomGroup.applyButton.onClick = function() {
          return isDone = true;
        };             
        // don't forget this one!
        win.onClose = function() {
            return cancel = true;
        };

        win.addEventListener("keydown", function (k)
         {
             if (k.keyName == 'Enter')
                return isDone = true;
         });

        win.show();

        while (isDone === false) {
            try {
                app.refresh();
            } catch(err) { // Esc cancels without errors
                return false;
            }
        }

       return cancel ?  false : selected_rbutton (radio_group); // return value, or false if cancelled
}


function  getSelectedLayersIdx(){ // Gets array of indexes of selected layers
    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 selectLayerByIndex( selectedLayers, add ){ // Selects layers by index. If fed an array + true add, selects them, otherwise provide a single value so it selects a single layer
    if (typeof(add)==='undefined') add = false;

    if ( add ) // If Add it's an array
        for(var a in selectedLayers){
            var desc = new ActionDescriptor();
                var ref = new ActionReference();
                ref.putIndex( charIDToTypeID( "Lyr " ), Number(selectedLayers[a]) );
            desc.putReference( charIDToTypeID( "null" ), ref );
            desc.putEnumerated( stringIDToTypeID( "selectionModifier" ), stringIDToTypeID( "selectionModifierType" ), stringIDToTypeID( "addToSelection" ) );
            var idMkVs = charIDToTypeID( "MkVs" );
            desc.putBoolean( charIDToTypeID( "MkVs" ), false );

            executeAction( charIDToTypeID( "slct" ), desc, DialogModes.NO );
        }
    else // Else I just need to make this layer active, it's a number
    {
            var desc = new ActionDescriptor();
                var ref = new ActionReference();
                var idLyr = charIDToTypeID( "Lyr " );
                ref.putIndex( charIDToTypeID( "Lyr " ), selectedLayers );
            desc.putReference( charIDToTypeID( "null" ), ref );
            desc.putBoolean( charIDToTypeID( "MkVs" ), false );
        executeAction( charIDToTypeID( "slct" ), desc, DialogModes.NO );
    }
}


function setLangByIndex( idx, lang ){ // Sets the dictionary language for a text layer based on its index
// Make layer active
    selectLayerByIndex( idx );

// And do tiny override
    var textObj = new ActionDescriptor();
        var target = new ActionReference();
        target.putProperty( charIDToTypeID( "Prpr" ), charIDToTypeID( "TxtS" ) ); // desired property: text style
        target.putEnumerated( charIDToTypeID( "TxLr" ), charIDToTypeID( "Ordn" ), charIDToTypeID( "Trgt" ) ); // the active layer
    textObj.putReference( charIDToTypeID( "null" ), target );
        var textStyle = new ActionDescriptor();
        textStyle.putInteger( stringIDToTypeID( "textOverrideFeatureName" ), 808466225 );
        textStyle.putInteger( stringIDToTypeID( "typeStyleOperationType" ), 3 );
        textStyle.putEnumerated( stringIDToTypeID( "textLanguage" ), stringIDToTypeID( "textLanguage" ), stringIDToTypeID( lang ) );

    textObj.putObject( charIDToTypeID( "T   " ), charIDToTypeID( "TxtS" ), textStyle ); // to (end); text style, desc.
executeAction( charIDToTypeID( "setd" ), textObj, DialogModes.NO ); // execute
}


////////////////////////////////////
// Main function
function main() {
    // Create window with language selection and retrieve selected value
    var lang = createWindow();

    if ( !lang ) // If cancelled please stop
        return;

     // Retrieve selected layers
     var selectedLayers = getSelectedLayersIdx();

    // Look at each entry and change language when possible; otherwise skip to next item
    for(var k in selectedLayers){
       try {
            setLangByIndex(Number(selectedLayers[k]), lang);
        } catch(err) {
            continue;
        }
    }
    selectLayerByIndex( selectedLayers, true );
}

Upvotes: 0

Related Questions