Saturday, August 27, 2005

 

Script of the Day -- Smart Title Case

I posted a version of this script on the Adobe U2U forum just the other day. This is an improved version that will use text files for the two lists (words that stay lowercase and words with internal capitals) rather than requiring you to edit the script itself. But I get ahead of myself. Let me start by explaining what I mean by "Smart" Title Case.

In the InDesign user interface, you can select some text and then choose Title Case from the Change Case submenu of the Type menu. If you do this, InDesign simply gives each selected word an initial cap, forcing the rest of the word to lowercase. So, if you type:and then you select it and use the built-in feature, you'll get:I call that "Dumb" Title Case. The application doesn't even know how to spell its own name. Use this script, and, provided you have "InDesign" in the list of words with internal capitals, you'll get:Interestingly, I had this script in mind this morning when I wrote about the isInArray method, but this script doesn't actually use that method because of the need to perform the comparisons in lowercase -- if you don't already know it, JavaScript's text comparisons are case sensitive. So for this script I created a function that not only tells the calling script whether or not the word of interest is in the list, but also where in the list it is.
//DESCRIPTION: Converts selected text to title case smartly

// Customize this script by either editing these arrays:
var ignoreWords = ["a","an","and","the","to","with","in","on",
 "as","of","or","at","is","into","by","from","for"];
var intCaps = ["PineRidge","InDesign","NJ","UMC"];

// or by creating text files named ignoreWords.txt
// and intCaps.txt in the script's folder

ignoreWords = getIgnoreFile(ignoreWords);
intCaps = getIntCaps(intCaps);

try {
 myText = app.selection[0].texts[0].contents;
} catch(e) {
 exit();
}

theWords = myText.toLowerCase().split(" ");

//First word must have a cap, but might have an internal cap

myNewText = "";
for (var j = 0; theWords.length > j; j++) {
 k = isIn(intCaps,theWords[j])
 if (k > -1) {
  myNewText = myNewText + intCaps[k] + " ";
  continue;
 } else {
  if ((isIn(ignoreWords,theWords[j]) > -1) && (j != 0)) {
   myNewText = myNewText + theWords[j] + " ";
  } else {
   myNewText = myNewText + InitCap(theWords[j]) + " ";
  }
 }
}

app.selection[0].texts[0].contents = myNewText.substring(0,myNewText.length - 1);

// +++++++ Functions Start Here +++++++++++++++++++++++

function getIgnoreFile(theWords) {
 var myFile = File(File(getScriptPath()).parent.fsName + "/ignoreWords.txt");
 if (!myFile.exists) { return theWords }
 // File exists, so use it instead
 myFile.open("r");
 var importedWords = myFile.read();
 myFile.close();
 return importedWords.split("\n"); // Could filter these, but what's the point?
}

function getIntCaps(theWords) {
 var myFile = File(File(getScriptPath()).parent.fsName + "/intCaps.txt");
 if (!myFile.exists) { return theWords }
 // File exists, so use it instead
 myFile.open("r");
 var importedWords = myFile.read();
 myFile.close();
 return importedWords.split("\n"); // Could filter these, but what's the point?
}

function getScriptPath() {
 // This function returns the path to the active script, even when running ESTK
 try {
  return app.activeScript;
 } catch(e) {
  return e.fileName;
 }
}

function isIn(aList,aWord) {
 for (var i = 0; aList.length > i; i++) {
  if (aList[i].toLowerCase() == aWord) {
   return i;
  }
 }
 return -1;
}

function InitCap(aWord) {
 if (aWord.length == 1) {
  return (aWord.toUpperCase());
 }
 return (aWord.substr(0,1).toUpperCase() + aWord.substring(1,aWord.length))
}

And, as is so often the case, I no sooner publish a script and I realize there's a problem with it. What if the first word in a title is a word like "iTunes"? You surely don't want it to be represented by "ITunes" and yet that's what this script will do. I need to come up with a mechanism for for dealing with this (although you could just choose to not select the first character and the script will then work in this situation).

It is also worth pointing out that the technique employed here will cause any internal character styling to be lost, unless the character styling is applied by a nested style. In all my use of this script over the past two or more years, this has never mattered.

Comments:
I'm wondering how much value there is in just listing the scripts here. Why not just let them be available for download and leave it at that?

Perhaps what's needed is a place where the scripts are decomposed and described in detail. Perhaps a second Blog aimed at providing such detail to those who are interested.

Dave
 
Keep the (smaller) scripts visible, as here.

I can see what you are doing, and can copy/paste or adapt from this.

If it was a d/l, I'd have ignored it and *maybe* re-invent.

Larger scripts would be a different matter.
 
This was a godsend!!

Once I changed the file name to .jsx and dropped it in "\\Adobe InDesign CS3\Scripts\Scripts Panel\Version 4.0 Scripts\" it worked like a charm.

I'm glad I spent the hour tracking it down as it will save me many many hours in the future!

Thanks!
 
Post a Comment

<< Home

This page is powered by Blogger. Isn't yours?