Is there a way to underline text in TextEdit using Applescript?

I’ve searched extensively and can’t find a way.

Does anyone know if it is possible to underline text in TextEdit using Applescript?

While it is possible in Pages, I’d prefer to do this natively in RTF in TextEdit.

Thanks.

Hello.

I think you should read the chapter covering TextEdit in “AppleScript in a Nutshell” I think you may be lucky at Google Books.

As far as I know you can only change color, font and size in TextEdit. :frowning:
That explains your failure!

You should be able to create an attribute run with underlied text.

But maybe it is so that you can’t.

I swear I have seen attribute runs with italic and bold text.

Thanks for the responses so far.

Though I’m not totally sure, I fear mourmartins may be correct, as searching TextEdit’s Applescript dictionary returns nothing for “underline.”

McUsr, I’m not familiar with “create an attribute run with underlied text.” Any guidance on a good place to learn about this?

Meanwhile, I’ve had the idea that I might accomplish this by somehow selecting specified text with Applescript and then telling System Events to keystroke U using command down. Does that sound reasonable? I’m just not sure how to go about selecting the text I want with Applescript.

Hello!

I am not sure if I saw that from within text edit, but I am sure I have seen it (italic, and bold) when inspecting clipboard content.

I think I have a fix for you, the fix is to convert your rtf to html from text-edit, which must be possible to do.

Then you embellish the text you want underlined with tags. ( I hope I am not dreaming this up!)

The final step would be to convert the the text back to rtf again. It is awkward and laborious, but I think it should work.

As for attribute runs, I think the fonts has attributes of italics, underlined, and bold, and that is where you should be able to set those properties of the text.

This is just where I would have looked for this. And tried some. You could try search for Kai and TextEdit here, also for RTF, and I am sure something should turn up.

Nice suggestion, and I thought it might work. However, I tried and the HTML underline tags just show up as text after converted to RTF. They do not get encoded into RTF underline tags.

I’ll keep looking.

Hello, it works.

But you have to insert some styles in the head of the html, I made it work by first entering rtf and then converting to html, and look at the result.

Here is some html. You can see that underlined text must be implemented by the span tag, and a class that is a css class for the span tag. I guess any text-decoration class will work here.

[code]

For u.... p.p1 {margin: 0.0px 0.0px 12.0px 0.0px; font: 12.0px Times} span.s1 {text-decoration: underline}

It is good!

There is a file with the mail.

[/code]

Hmm.

Thanks for the help.

I’ve been trying to find a solution for most of the day and am now brain dead. I’m also not fluent with HTML. I’ll have to give this a look in the morning to try and make sense.

For a small, fast, scriptable thingie try Tex-Edit Plus
http://www.tex-edit.com/ I have no experience with it, but seems OK.

Here’s a bit of Sunday evenng fun, but it’s too flimsy and unreliable for serious work: :slight_smile:

on underscore(u)
	tell application "TextEdit" to activate
	
	set oldClip to (the clipboard as record)
	set the clipboard to ""
	
	tell application "System Events" to keystroke "ac" using {command down}
	
	repeat while ((the clipboard) is "")
		delay 0.2
	end repeat
	set RTFData to «class RTF » of (the clipboard as record)
	
	set p to (path to temporary items as text) & "Test.dat"
	set fRef to (open for access file p with write permission)
	try
		set eof fRef to 0
		write RTFData to fRef
		set RTFText to (read fRef from 1 as string)
		if (RTFText contains u) then
			set eof fRef to 0
			set astid to AppleScript's text item delimiters
			set AppleScript's text item delimiters to u
			set newRTFText to text item 1 of RTFText & "\\ul " & u & "\\ulnone " & text from text item 2 to -1 of RTFText
			set AppleScript's text item delimiters to astid
			write newRTFText to fRef
		end if
		set newRTFData to (read fRef from 1 as «class RTF »)
	end try
	close access fRef
	
	set the clipboard to {«class RTF »:newRTFData}
	repeat until ((the clipboard as record) contains {«class RTF »:newRTFData})
		delay 0.2
	end repeat
	
	tell application "System Events" to keystroke "v" using {command down}
	delay 0.2
	set the clipboard to oldClip
end underscore

tell application "TextEdit" to make new document with properties {text:"Now is the time for all good men to come to the aid of the party."}

underscore("all good men")

I found a way of doing it for a word. Just got to figure a sentence.

Here is my example:

In the Texedit preferences. → Open and Save Tab, tick the check box to : Display RTF files as RTF code instead of formatted text

Then open your doc. ( I would do this on copies, at least until your sure of the results)

It will open and display the rear RTF code.

Run this script.

set toUnderscroe to "systems"
tell application "TextEdit"
	activate
	set doc to document 1
	
	
	
	set words of doc whose it is toUnderscroe to "\\ul " & toUnderscroe & "\\ulnone "
	 
	save doc
end tell

close the document. Uncheck the check box to : Display RTF files as RTF code instead of formatted text.

Open the Document and you should see the word/s underlined.

Mwahahaha! Thank you, Nigel!

That works pretty good, and I am very grateful for your work.

I’m able to follow some of the code but not all of it. At least I’ll learn something from this.

Just a couple questions:

I’ve tested your function by running it with several successive queries, and for some reason the script occasionally hangs on the final query/call (the final call never completes and Applescript shows the script as continually running).

Also, as it happens, I have Alfred set to activate on double-pressing the command key, so I repeatedly see the Alfred entry box pop-up as the script is running multiple queries. Is there someway I can tell System Events to “command up” after it is used for command down, or should I just place some brief delays in there after the command downs?

Thanks so much, again.

Phil

Also, thank you, Mark. You posted while I was drafting my above comment.

I’ll probably stick with Nigel’s script as it doesn’t require the preference changes and close/open. But thank you, regardless, for the help.

NIgel, I solved my second question. Putting a brief delay after the “ac” with command down did the trick.

Thanks again.

No problem.

But since I spent a little bit of time working on this before I posted the first script I want to post my findings :slight_smile:

My original idea was to just read the file using a unix command. Make a new doc and put the text returned from the unix command in it. That way the returned text would be in RTF code. And no changing the prefs. or opening the file.

But I ran into the problem that saving the document via the applescript was giving me permission errors.

So I just went with the script I posted.

But now I have figured out how to target specific text in the doc. I have gone back to that.

You then just need to save the new doc manually

set toUnderscroe to "writing systems around" # string to underscore

set toUnderscroe to "writing systems around" # string to underscore

#UPDATED Script: get the path of the open document 1 and gives you a duplicate with the changes.

tell application "TextEdit"
	activate
	set rawRTFpath to path of document 1
	
	set rawRTF to (do shell script "cat " & quoted form of rawRTFpath) #path to the file
	set doc to make new document
	
	set text of doc to rawRTF
	
	
	set theRTF to (text of doc)
	set textNumber1 to (offset of toUnderscroe in theRTF)
	set character textNumber1 of doc to "\\ul " & character textNumber1 of doc
	set textNumber2 to (textNumber1 + (count of toUnderscroe) + (count of "\\ul "))
	set character textNumber2 of doc to "\\ulnone " & character textNumber2 of doc
	
	tell application "System Events" to keystroke "s" using {command down, shift down}# when the doc is duplicated it gives you a normal copy of the doc.
	 
end tell


It seems to me that the hardest part is specifying what you want underlined. Here’s another approach – to use it, you will have to go to AppleScript Editor (10.7 or later), choose New from Template and Cocoa-AppleScript App, paste it in, and save it as an app. Then select a word in an open TextEdit document, copy it, then run the script.

set theTarget to the clipboard as text -- whatever text you want to underline
tell application "TextEdit"
	set thePath to path of document 1
	close document 1 saving yes
end tell
-- read in the RTF
set {theRTF, docAtts} to current application's NSMutableAttributedString's alloc()'s initWithPath_documentAttributes_(thePath, reference)
set theText to theRTF's |string|() as text -- get is a string
set theRanges to my rangesOf_in_(theTarget, theText) -- calculate ranges of target string
-- define underline attribute
set attrsDict to current application's NSDictionary's dictionaryWithObject_forKey_(1, current application's NSUnderlineStyleAttributeName)
repeat with aRange in theRanges
	theRTF's addAttributes_range_(attrsDict, aRange) -- ad the attribute
end repeat
-- convert to data and save
set theData to theRTF's RTFFromRange_documentAttributes_({0, length of theText}, docAtts)
theData's writeToFile_atomically_(thePath, true)
--reopen in TextEdit
tell application "TextEdit" to open thePath
current application's NSApp's terminate_(me) -- tell me to quit

on rangesOf_in_(theTarget, theString) -- rework this to suit; you need some way to decide what to underscore
	set targetLength to length of theTarget
	set saveTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {theTarget}
	set theBits to text items of theString
	set AppleScript's text item delimiters to saveTID
	set theRanges to {}
	set thePrev to 0
	repeat with i from 1 to (count of theBits) - 1
		set end of theRanges to {thePrev + (length of item i of theBits), targetLength}
		set thePrev to thePrev + (length of item i of theBits) + targetLength
	end repeat
	return theRanges
end rangesOf_in_

I can’t say for sure. It’s a flimsy script ” as I wrote when posting it ” and sensitive to timing issues. The repeats (one of which is probably not exiting in your case) are there because there’s no feedback about what happens as the result of keystrokes (only that they’ve been issued) or about when the contents of the clipboard have actually changed. The script has to test these for itself or simply pause more than long enough to let them happen. You could try replacing the repeated short delays with single longer ones.

Interesting. I thought at first that the “a” and the “c” might be getting individual commands-down, but your solution suggests it was the execution time of the script between the two ‘keystroke’ commands being less than your “double-press” time!

It is a great handler Shane.

I hope you don’t find it untimely, inappropritate or cheeky. But I did change it a little bit, as we are operating with 1 as the start index, I also subtracted one from the length, when the start position, and length pairs are collected.

I changed the name as well, not out of disrespect, but because I guess the handler doesn’t work like it should in AsoC anyway.

on rangesOf(theTarget, theString) -- rework this to suit; you need some way to decide what to underscore
” © Shane Stanley
	set targetLength to (length of theTarget)
	set saveTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {theTarget}
	set theBits to text items of theString
	set AppleScript's text item delimiters to saveTID
	set theRanges to {}
	set thePrev to 1
	repeat with i from 1 to (count of theBits) - 1
		set end of theRanges to {thePrev + (length of item i of theBits), targetLength - 1}
		set thePrev to thePrev + (length of item i of theBits) + targetLength
	end repeat
	return theRanges
end rangesOf