How Do I Set Clipboard (Pasteboard) to Both Rich Text (RTF) and Plain Text?

How Do I Set Clipboard (Pasteboard) to Both Rich Text (RTF) and Plain Text?

I have a script that is several years old which uses multiple shell scripts in order to place both a rich text hyperlink and a plain text markdown link on the Clipboard. Thus, the appropriate link is pasted based on whether the target document is rich text or plain text.

With the number of recent posts about using ASObjC to create rich text and to read/write to the Clipboard, I’m wondering if there is a better method using ASObjC?

My starting point is two strings in AppleScript:

set htmlLinkStr to "<a href=\"" & urlStr & "\" style=\"" & gStyleLink & "\">" & linkTextStr & "</a>"
# Need to convert htmlLinkStr to rich text

set mdLinkStr to "[" & linkTextStr & "](" & urlStr & ")"

So the steps I need are:

  1. Convert htmlLinkStr to rich text (htmlLinkRTF)
  2. Put both htmlLinkRTF (as rich text) and mdLinkStr (as plain text) on Clipboard.

Any suggestions and/or sample code?

I don’t know about better — you don’t say what you’re using — but it can certainly be done simply enough.

You create a link attributed string by setting the attribute NSLinkAttributeName to an NSURL for the link.

If you want to put multiple values for the first object on the clipboard, you need to use something like the approach @alldritt used elsewhere: getting the attributed string as RTF data, and then using setData:forType: to put it on the clipboard. So:

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

-- classes, constants, and enums used
property NSRTFTextDocumentType : a reference to current application's NSRTFTextDocumentType
property NSPasteboardTypeRTF : a reference to current application's NSPasteboardTypeRTF
property NSPasteboardTypeString : a reference to current application's NSPasteboardTypeString
property NSLinkAttributeName : a reference to current application's NSLinkAttributeName
property NSSingleUnderlineStyle : a reference to 1
property NSColor : a reference to current application's NSColor
property NSUnderlineStyleAttributeName : a reference to current application's NSUnderlineStyleAttributeName
property NSForegroundColorAttributeName : a reference to current application's NSForegroundColorAttributeName
property NSDictionary : a reference to current application's NSDictionary
property NSMutableAttributedString : a reference to current application's NSMutableAttributedString
property |NSURL| : a reference to current application's |NSURL|

set linkTextStr to "Click here"
set urlStr to "http://forum.latenightsw.com"
set mdLinkStr to "[" & linkTextStr & "](" & urlStr & ")"
set linkURL to |NSURL|'s URLWithString:urlStr
-- define attributes to apply
set theAtts to NSDictionary's dictionaryWithObjects:{linkURL, NSColor's blueColor(), NSSingleUnderlineStyle} forKeys:{NSLinkAttributeName, NSForegroundColorAttributeName, NSUnderlineStyleAttributeName}
-- make attributed string
set attString to NSMutableAttributedString's alloc()'s initWithString:linkTextStr attributes:theAtts
-- need it in RTF data form for clipboard
set rtfData to attString's RTFFromRange:{0, attString's |length|()} documentAttributes:{DocumentType:NSRTFTextDocumentType}
set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
pb's clearContents()
-- set both types for the first object on the clipboard
pb's setData:rtfData forType:NSPasteboardTypeRTF
pb's setString:mdLinkStr forType:NSPasteboardTypeString

2 Likes

Thanks, Shane. This looks great.

Here is what I used before:

Create RTF Hyperlink

on copyHTMLasRTFtoClipboard(pstrHTML)
    
  -- REWRITTEN AS RTF AND COPIED TO THE CLIPBOARD
  set lstrCMD to "echo " & quoted form of pstrHTML & " | textutil -format html -convert rtf -stdin -stdout | pbcopy -Prefer rtf"
  do shell script lstrCMD
    
end copyHTMLasRTFtoClipboard

Set Clipboard to Both RTF and Plain Text

  --- GET RTF DATA FROM CLIPBOARD INTO RECORD FOR COPY LATER ---
  
  set recClip to the clipboard as record
  set dataRTF to «class RTF » of recClip -- binary RTF data
  
  --- COPY TO CLIPBOARD:  RTF Hyperlink & Plain Text MD Link  ---
  
  set the clipboard to {Unicode text:strPTLink, «class RTF »:dataRTF}

Shane, thanks again for you kind help.
You solved my specific problem, putting a hyperlink on the clipboard as RTF, but unfortunately I don’t see how to apply that to a more general case of converting any arbitrary HTML code to RTF, and put that on the clipboard.

Could you please share the code to do this?

Thanks.

It’s fairly similar:

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

-- classes, constants, and enums used
property NSUTF8StringEncoding : a reference to 4
property NSString : a reference to current application's NSString
property NSRTFTextDocumentType : a reference to current application's NSRTFTextDocumentType
property NSPasteboardTypeRTF : a reference to current application's NSPasteboardTypeRTF
property NSPasteboardTypeString : a reference to current application's NSPasteboardTypeString
property NSDictionary : a reference to current application's NSDictionary
property NSAttributedString : a reference to current application's NSAttributedString
property |NSURL| : a reference to current application's |NSURL|

set someHTML to "Click <b>here</b>"
-- convert to data
set htmlString to NSString's stringWithString:someHTML
set htmlData to htmlString's dataUsingEncoding:NSUTF8StringEncoding
-- make attributed string
set attString to NSAttributedString's alloc()'s initWithHTML:htmlData documentAttributes:(missing value)
-- need it in RTF data form for clipboard
set rtfData to attString's RTFFromRange:{0, attString's |length|()} documentAttributes:{DocumentType:NSRTFTextDocumentType}
set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
pb's clearContents()
-- set both types for the first object on the clipboard
pb's setData:rtfData forType:NSPasteboardTypeRTF
pb's setString:someHTML forType:NSPasteboardTypeString
2 Likes

Thanks, Shane. Your script works great, but the RTF on the clipboard is just a bad as when I use the shell script. There seems to be a wide range of interpretation of the RTF by various apps, like Evernote, TextEdit, Word, etc. Interesting enough the rendered view in SD is the best! When I create a Note in Evernote using the HTML it looks very nice.

So, I’m thinking I need to put encoded HTML on the pasteboard as well to give the apps another choice. Is that possible? Can we put both RTF and encoded HTML (correct term?) on the pasteboard at the same time? If so, can you please share that?

That code is putting both RTF and the HTML on the clipboard. But you have no control over which an app chooses.

I suspect what you’re seeing is the result of your minimal HTML, which in turn is resulting in only some RTF attributes being set, and the rest being therefore provided by the client app. That’s really the nature of both HTML and RTF.