TextEdit - copy text with background color yellow to new file

Hi all

I have an RTF file in TextEdit where I highlight stuff by setting the background color to yellow.

I’m looking for an automated way to copy all that highlighted text to a new file. Is there a way to do that with AppleScript?

This a Sample File: http://dl.dropbox.com/u/984690/Sample.rtf
And this is what the result should look like: http://dl.dropbox.com/u/984690/Result.rtf (or at least something similar)


As I found no Background color and no selection in the TextEdit dictionary, my first idea was to search an other Word Processor bu I didn’t found a free one offering these features.
I tried Pages but it drop the background color.
So, I decided to apply brute force.
I got a script doing the trick but I don’t know if it’s sufficient to treat different files.
Here it is :

set source to choose file of type {"public.rtf"} without invisible

set leTexte to read source
set enListe to my decoupe(leTexte, "\\cb2 ")
set nouvelleliste to {}
item 1 of my decoupe(item 1 of enListe, "\\cf0")
set item 1 of enListe to result & "\\cf0"
repeat with i from 2 to count of enListe
	item 1 of my decoupe(item i of enListe, "\\")
	set item i of enListe to result & "\\cb1 \\" & return
end repeat
set nouveauTexte to my recolle(enListe, "\\cb2 ")

my writeTo((path to desktop as text) & "Result.rtf", nouveauTexte & "}", text, false)


on decoupe(t, d)
	local oTIDs, l
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d
	set l to text items of t
	set AppleScript's text item delimiters to oTIDs
	return l
end decoupe


on recolle(l, d)
	local oTIDs, t
	set oTIDs to AppleScript's text item delimiters
	set AppleScript's text item delimiters to d
	set t to "" & l
	set AppleScript's text item delimiters to oTIDs
	return t
end recolle

Handler borrowed to Regulus6633 - http://macscripter.net/viewtopic.php?id=36861
on writeTo(targetFile, theData, dataType, apendData)
	-- targetFile is the path to the file you want to write
	-- theData is the data you want in the file.
	-- dataType is the data type of theData and it can be text, list, record etc.
	-- apendData is true to append theData to the end of the current contents of the file or false to overwrite it
		set targetFile to targetFile as text
		set openFile to open for access file targetFile with write permission
		if not apendData then set eof of openFile to 0
		write theData to openFile starting at eof as dataType
		close access openFile
		return true
	on error
			close access file targetFile
		end try
		return false
	end try
end writeTo


Yvan KOENIG (VALLAURIS, France) mardi 14 février 2012 17:54:54

As Yvan says, there is no direct way. It can be done using AppleScriptObjC, though. The following script requires that you download and install ASObjC Runner (free) to run. Alternatively, you can just use the code enclosed in “script … end script” and save it as a Cocoa-AppleScript applet (which requires Lion for the saving part).

It works on the frontmost TextEdit document, and assumes it has been saved. It extracts text with any background colour.

script saveColoredBits
	tell application "TextEdit"
		set oldPath to path of document 1
	end tell
	set newPath to oldPath & "_bits.rtf" -- where to write new file
	-- read rtf file into attributed string, store document attributes
	tell current application's NSAttributedString to set {fullString, docAtts} to alloc()'s initWithPath_documentAttributes_(oldPath, reference)
	-- get number of characters
	set theLength to fullString's |length|() as integer
	-- make new attributed string to append colored bits to
	tell current application's NSMutableAttributedString to set foundString to alloc()'s init()
	-- set character offset to start checking
	set theIndex to 0
	repeat while theIndex < theLength -- ends at end of file
		-- get the background color of char theIndex, and range of following colored chars
		set {theColor, theRange} to fullString's attribute_atIndex_effectiveRange_(current application's NSBackgroundColorAttributeName, theIndex, reference)
		-- check for missing value, which means no background color
		if theColor is not missing value then
			-- grab the colored string and append it
			set theString to fullString's attributedSubstringFromRange_(theRange)
			tell foundString to appendAttributedString_(theString)
			-- make an attributed string containing a return with the same attributes, and append it
			tell current application's NSAttributedString to set theReturn to alloc()'s initWithString_attributes_(return, theString's attributesAtIndex_effectiveRange_(0, missing value))
			tell foundString to appendAttributedString_(theReturn)
		end if
		-- update theIndex to start with next character
		set theIndex to (location of theRange) + (|length| of theRange)
	end repeat
	-- convert attributed string to rtf data and write it to file
	set theData to foundString's RTFFromRange_documentAttributes_({0, foundString's |length|()}, docAtts)
	tell theData to writeToFile_atomically_(newPath, true)
end script

tell application "ASObjC Runner" to run the script {saveColoredBits}

It’s not clear to me how Simon himself changes background colours in TextEdit, unless there’s a facility for this in the Lion version.

The irritating thing (in Snow Leopard, at least) is that ‘attribute runs’ are affected by changes in background colour, but their properties don’t give any information about it! The script below actually works with Simon’s sample document, but it relies on the beginning of the text being unmarked and there being no other attribute changes the text such as font or size. It’s therefore only posted here for interest. Yvan’s or Shane’s scripts are the way to go for the moment.

tell application "TextEdit"
	set newDoc to (make new document)
	tell text of document "Sample.rtf"
		repeat with i from 2 to (count attribute runs) by 2
			make new paragraph at end of newDoc's text with data (attribute run i)
			make new paragraph at end of newDoc's text with data return
		end repeat
	end tell
end tell

There is: a button in the new toolbar thingie.

I suspect attribute runs are based on all underlying attributes – it would be fairly complex to do otherwise.

Thanks for the answers, the Script in Yvan’s Comment 2 did the trick for my use case!