Get raw (ASCII) RTF content from the clipboard

I just wrote the following script to get RTF content from the clipboard.
.

-- script: Get raw RTF content from the clipboard
-- written: by me, right now

clipboard_as_raw_RTF()

on clipboard_as_raw_RTF()
	-- catch the hext text from the error message
	try
		(the clipboard as «class RTF ») as text
	on error errorMessage
		set hexRTFData to errorMessage
	end try
	-- clear the garbage text 
	set ATID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to {"«data RTF ", "»"}
	set hexRTFData to text item 2 of hexRTFData
	set AppleScript's text item delimiters to ATID
	-- convert hexadecimal text to ASCII text
	do shell script "/bin/echo -n " & hexRTFData & " | xxd -r -p" without altering line endings
end clipboard_as_raw_RTF

.
It works, but I would like to know if there is a better way to accomplish this task. For example, to shorten the script without degrading the speed.

Shortened version:
.

-- script: Get raw RTF content from the clipboard
-- written: by me, right now

clipboard_as_raw_RTF()

on clipboard_as_raw_RTF()
	try
		(the clipboard as «class RTF ») as text
	on error errorMessage -- catch HEX text from error message
		set hexRTFData to text 22 thru -18 of errorMessage
	end try
	-- convert HEX text to ASCII text
	do shell script "/bin/echo -n " & hexRTFData & " | xxd -r -p" without altering line endings
end clipboard_as_raw_RTF

If you’re willing to install unrtf, a gnu utility, you could try this:

do shell script "pbpaste | /usr/local/bin/unrtf --text"

You can get it easily with one of the package managers, such as macports.

More info can be found here:

NB the path to unrtf may vary with the installation

.
Thanks for this link, but I’m not interested in a third-party utility, but in how I myself could program the simple task of getting raw RTF content from the clipboard using standard OSX tools. Also, pbpaste can’t pass the rich content inside the shell workflow, as I know.

Here is another such solution, which is shorter than my previous ones, but much slower:
.

do shell script "osascript -e 'the clipboard as «class RTF »' | perl -ne 'print chr foreach unpack(\"C*\",pack(\"H*\",substr($_,11,-3)))'"

.
As you can see, they work, but they work in a somewhat clumsy way.
In post #1 I’m catching content from an error message, and here

do shell script “osascript -e ‘the clipboard as «class RTF »’”

I’m executing Apple script inside osascript inside do shell script inside Apple script just to pass the hex text to perl command.

There must be a better way.

Hi @KniazidisR.

I don’t know if this will be of interest. It’s not shorter than your script, but it is a bit faster:

on clipboard_as_raw_RTF()
	try
		set RTFData to (the clipboard as «class RTF »)
	on error errmsg
		display dialog errmsg
		return missing value
	end try
	
	set temporaryItemPath to (path to temporary items as text) & "RTFData.txt"
	set fileRef to (open for access file temporaryItemPath with write permission)
	try
		write RTFData to fileRef
		set RTFText to (read fileRef from 1 as «class utf8»)
		close access fileRef
		return RTFText
	on error errmsg
		close access fileRef
		display dialog errmsg
	end try
end clipboard_as_raw_RTF

clipboard_as_raw_RTF()

Edit: Or, come to think of it, there’s this:

use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

on clipboard_as_raw_RTF()
	set pasteboard1 to current application's class "NSPasteboard"'s generalPasteboard()'s pasteboardItems()'s firstObject()
	if ((pasteboard1's types()'s containsObject:("public.rtf")) as boolean) then
		set RTFData to pasteboard1's dataForType:("public.rtf")
		set RTFText to current application's class "NSString"'s alloc()'s ¬
			initWithData:(RTFData) encoding:(current application's NSUTF8StringEncoding)
		return RTFText as text
	end if
	return missing value
end clipboard_as_raw_RTF

clipboard_as_raw_RTF()
2 Likes

Hi Nigel,
What do you think about that?

use framework "Foundation"
use framework "AppKit"
use scripting additions

set thePasteboard to current application's NSPasteboard's generalPasteboard()
set theData to (thePasteboard's dataForType:(current application's NSPasteboardTypeRTF)) -- or NSRTFPboardType prior to 10.14
return (current application's NSString's alloc()'s initWithData:(theData) encoding:(current application's NSUTF8StringEncoding)) as string
3 Likes

Hi @ionah.

Yes, that’s briefer! :smile: Looking at the documentation again, I see that dataForType: acts on the first pasteboard item anyway and returns missing value if the requested data type isn’t there. So much of my code isn’t necessary. But I personally would prefer to return missing value rather than an empty string if there’s no RTF data:

use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

on clipboard_as_raw_RTF()
	set thePasteboard to current application's NSPasteboard's generalPasteboard()
	set theData to (thePasteboard's dataForType:(current application's NSPasteboardTypeRTF))
	if (theData is missing value) then return theData -- If no RTF data.
	return (current application's NSString's alloc()'s initWithData:(theData) encoding:(current application's NSUTF8StringEncoding)) as text
end clipboard_as_raw_RTF

clipboard_as_raw_RTF()
2 Likes

Thanks, @Nigel_Garvey and @ionah.

I liked the latest edit from @Nigel_Garvey. Unfortunately, I could only mark 1 post as a solution, and that post was by @ionah. This solution will come in handy as the best alternative to my 1st plain-Apple script in the original question.

COMMENT:
I noticed that my plain-AppleScript returns a result in which the newline character is RETURN, and in the AsObjC version it is LINEFEED. I wonder which of the 2 uses the original source symbol and which one modifies it. Maybe it doesn’t matter, I’m not sure.

The relevant code’s “0A” in the hexadecimal, so it’s a linefeed there. The returns are probably coming from do shell script, which renders line endings as returns unless without altering line endings is used.

2 Likes

Thank you.
I have updated my scripts in post #1 according to your comment. Now everything works correctly.