Search code examples
javascriptadobe-indesignunderline

How to underline charachters in InDesign with JavaScript?


I started writing this piece of code for InDesign to underline all letters except from the one with descendants, and added a dialog window to chose stroke and offset of the line. Now I have two problems:

  1. the program underlines all letters
  2. the stroke and offset won't change

I'm a beginner in Javascript and it's the first time coding for InDesign. Does someone have a clue? Thank you!

 // UNDERLINE ALL BUT NO DESCENDANTS



//Make certain that user interaction (display of dialogs, etc.) is turned on.
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;

if (app.documents.length != 0){
    try {
      // Run script with single undo if supported
      if (parseFloat(app.version) < 6) {
        main();
      } else {
        app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Expand State Abbreviations");
      }
      // Global error reporting
    } catch ( error ) {
      alert( error + " (Line " + error.line + " in file " + error.fileName + ")");
    }
}else{
    alert("Open a document first before running this script.");
}


///MAIN FUNCTION 
function main(){

     if(app.selection.length != 0){
        myDisplayDialog();
    }
}




//INTERFACE 
function myDisplayDialog(){
    //declare variables 
    //general 
    var myDoc = app.activeDocument;
    var mS = myDoc.selection; 
    // dialog 
    var myDialog = app.dialogs.add({name:"Underliner"});
    var myLabelWidth = 70;  
    with(myDialog.dialogColumns.add()){
        with(borderPanels.add()){
            with(dialogColumns.add()){
                with(dialogRows.add()){
                    staticTexts.add({staticLabel:"Stroke:", minWidth:myLabelWidth});
                    staticTexts.add({staticLabel:"Offset:", minWidth:myLabelWidth});
                }
            }              
                with(dialogRows.add()){
                    staticTexts.add({staticLabel:""});
                    var myStroke = measurementEditboxes.add({editValue:1, editUnits:MeasurementUnits.points});
                    var myOffset = measurementEditboxes.add({editValue: 15, editUnits:MeasurementUnits.points});
                    
                }
            
        }
    }

    var myResult = myDialog.show();
    if(myResult == true){
            var myStroke = myStroke.editValue;
            var myOffset = myOffset.editValue;
            myDialog.destroy();
            underline(mS,myStroke,myOffset);
        }
        else{
            myDialog.destroy();
            alert("Invalid page range.");
        }
 }



//REAL FUNCTION 
function underline(charList,stroke, offset){
var len = charList.length;
const doNotUnderline = ['g','j','p','q','y'];
    for (var i=0; i < len; i++){
         try{
            var myChar = charList[i];
            //console.log(typeof myText);
            if (includes(myChar, doNotUnderline) == false)
            {
                myChar.underline = true;
                myChar.underlineWeight == stroke;
                myChar.underlineOffset == offset;
                
            } else {
                myChar.underline = false;
                
            }

         }catch(r){
            alert(r.description);
            break;
        }
    }
}


//function to know if char is in array
function includes(elemento,array)
{
    var len = array.length;
    for(var i=0; i<len ;i++)
    {
        if(array[i]==elemento){return true;}
    }
    return false;
}

Solution

  • Try these changes in the function underline():

    //REAL FUNCTION
    function underline(words,stroke, offset) {  // <------ here 'words' instead of 'charList'
        var charList = words[0].characters;     // <------ here get 'characters' of the 'words'
        var len = charList.length;
    
        const doNotUnderline = ['g','j','p','q','y'].join(); // <------- here '.join()'
        for (var i=0; i < len; i++){
             try{
                var myChar = charList[i];
                // if (includes(myChar, doNotUnderline) == false) // <----- no need 
                if (doNotUnderline.indexOf(myChar.contents) < 0) // <------ 'indexOf()' instead of 'includes()' 
                {
                    myChar.underline = true;
                    myChar.underlineWeight = stroke; // <------- here '=' instead of '=='
                    myChar.underlineOffset = offset; // <------- here '=' instead of '=='
    
                } else {
                    myChar.underline = false;
                }
    
             }catch(r){
                alert(r.description);
                break;
            }
        }
    }
    

    Probably there can be another improvements as well. It's need additional researches. But if you change these lines it should work to a degree.

    And there is one little thing that improves user experience greatly: to keep last used values in the input fields. It can be done pretty easy, let me know it you need it.

    Update

    Here is the way I'm using to store and restore any preferences of my scripts.

    Add somewhere at the start of your script these lines:

    // get preferences
    var PREFS = { stroke: 1, offset: 15 };                        // set default prefs
    var PREFS_FILE = File(Folder.temp + '/underline_prefs.json'); // the file with preferences
    if (PREFS_FILE.exists) PREFS = $.evalFile(PREFS_FILE);        // get the prefs from the file
    

    Now you can use the global values PREFS.stroke and PREFS.offset anywhere you want. In your case they go here:

    with(dialogRows.add()){
        staticTexts.add({staticLabel:""});
        var myStroke = measurementEditboxes.add({editValue:PREFS.stroke, editUnits:MeasurementUnits.points});
        var myOffset = measurementEditboxes.add({editValue:PREFS.offset, editUnits:MeasurementUnits.points});
    }
    

    This way script will get the stroke and weight from the file underline_prefs.json that will be stored in the standard temporary folder of current user.

    Final step is to save the values back into the file after the script got them from the dialog window.

    I'd put this piece of code here:

    if (myResult == true) {
      var myStroke = myStroke.editValue;
      var myOffset = myOffset.editValue;
      myDialog.destroy();
      underline(mS, myStroke, myOffset);
    
      // save preferences here
      PREFS.stroke = myStroke;
      PREFS.offset = myOffset;
      PREFS_FILE.open('w');
      PREFS_FILE.write(PREFS.toSource());
      PREFS_FILE.close();
    
    } else {
      myDialog.destroy();
      alert("Invalid page range.");
    }
    

    Voilá. Now don't need to type the values every time they differ from default ones.