Starting with a Clipboard (that was set using the standard Copy UI) which contains BOTH an image and text, how do I re-set the Clipboard to only the image?
I tried to use the scripts in the above link, but just could not make it work.
I’m missing the basic command to read only the image on the Clipboard into an ASObjC variable.
There might be a simpler way, but I think this should work — it’s a variation on earlier code for preserving the contents of the clipboard.
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
on fetchPicsOnlyFromClipboard()
set picTypes to current application's NSArray's arrayWithArray:{current application's NSPasteboardTypeTIFF, current application's NSPasteboardTypePNG, current application's NSPasteboardTypePDF} -- types to fetch
set aMutableArray to current application's NSMutableArray's array() -- used to store contents
-- get the pasteboard and then its pasteboard items
set thePasteboard to current application's NSPasteboard's generalPasteboard()
set theItems to thePasteboard's pasteboardItems()
-- loop through pasteboard items
repeat with pbItem in theItems
-- make a new pasteboard item to store existing item's stuff
set newPBItem to current application's NSPasteboardItem's new()
-- get the types of data stored on the pasteboard item
set theTypes to pbItem's |types|()
-- for each type, check if it's a pic, get the corresponding data and store it in the new pasteboard item
repeat with aType in theTypes
if (picTypes's containsObject:aType) as boolean then
set theData to (pbItem's dataForType:aType)'s mutableCopy()
if theData is not missing value then
(newPBItem's setData:theData forType:aType)
end if
end if
end repeat
-- add new pasteboard item to array if it contains anything
if newPBItem's |types|()'s |count|() > 0 then
(aMutableArray's addObject:newPBItem)
end if
end repeat
return aMutableArray
end fetchPicsOnlyFromClipboard
-- get pasteboard
set thePasteboard to current application's NSPasteboard's generalPasteboard()
-- get pic contents
set theClip to my fetchPicsOnlyFromClipboard()
-- clear it, then write back filtered contents
thePasteboard's clearContents()
thePasteboard's writeObjects:theClip
Curious. Does Shane’s solution work? I’d expect text+image data in a clipboard be encoded as RTFD or HTML, and that would mean that you can’t simply find the images as separate clipboard items. Instead, you’ll have to parse the html or RTF content to extra the images, which is much more work.
Good question, Thomas. No, and it is my fault for not properly inspecting the clipboard I need to process, and for not asking the right question.
Bingo! That is exactly the actual issue/clipboard I face. When I inspect the clipboard it does NOT have any images included just as image types. All are embedded in RTF.
@ShaneStanley, many thanks for your help, and I appreciate the script. I just asked the wrong question.
Anyone have a solution for extracting images out of RTF on the clipboard?
The rtfd doc in the clipboard has the images embedded (type: “com.apple.flat-rtfd”). If you save that clipboard data to a .rtfd file, TextEdit won’t be able to open it, though.
If you can paste it into TextEdit (in rich text mode), however, you’ll get the text + imgs properly in the TextEdit document. Now, if you save that, you’ll get a .rtfd folder - if you use “Show Package Contents” in Finder, you’ll find your img file inside.
I believe there are also macOS API calls for doing this, i.e. take a flat rtfd and turn it into one that you can save as a package folder. Once you accomplish that, you can extract the .tiff files from it.
Google for that type reveals some code (not ObjC AppleScript, though):
That part gets the flat rtfd data into an NSAttributedString.
All that’s left to find is a function that will write that out as a .rtfd package.
Looks like this code should do it (again, not yet AppleScripted - I leave that to someone who has some skills at it):
I was basing my code on recollections of a previous discussion with @JMichaelTX where he described the clipboard after a copy containing an image and text such that pasting into an image editor resulted in the pic being pasted, whereas pasting into a text editor resulted in the text being pasted. That won’t happen with RTFD or HTML on the clipboard in a single pasteboard item — it can only happen with multiple pasteboard items.
This will get the first image out of RTFD on the clipboard, and puts it alone on the clipboard:
use AppleScript version "2.4" -- Yosemite (10.10) or later
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 NSRTFDPboardType)
if theData is missing value then error "No RTFD on the clipboard"
set {theRTFD, theAtts} to current application's NSAttributedString's alloc()'s initWithRTFD:theData documentAttributes:(reference)
-- make file wrapper, sort of folder in memory
set theWrapper to theRTFD's RTFDFileWrapperFromRange:{0, theRTFD's |length|()} documentAttributes:theAtts
-- subwrappers are like the files in a folder, with keys like file names
set subWrappers to theWrapper's fileWrappers()
set theKeys to subWrappers's allKeys()
-- loop through and find the first non-rtf wrapper, which we'll assume is a pic
repeat with aKey in theKeys
set theExt to aKey's pathExtension() as text
if theExt is not "rtf" then
-- get the dta for the wrapper/pseudo-file
set picWrapper to (subWrappers's objectForKey:aKey)
set theData to picWrapper's regularFileContents()
-- put it on the clipboard
if theExt = "png" then
set theType to current application's NSPasteboardTypePNG
else if theExt = "tif" then
set theType to current application's NSPasteboardTypeTIFF
else if theExt = "pdf" then
set theType to current application's NSPasteboardTypePDF
else
-- you're going to have to convert it
set theImage to (current application's NSImage's alloc()'s initWithData:theData)
set theData to theImage()'s TIFFRepresentation()
set theType to current application's NSPasteboardTypeTIFF
end if
thePasteboard's clearContents()
(thePasteboard's setData:theData forType:theType)
end if
end repeat
I can use that as a script in my app iClip, in order to extract images from rich text clips. Users kept asking me for this, and only this question led me to figure out how to do it, and instead of having to hard-code it into iClip, I can now use this script. Awesome.
A (little) possible bug: Images inside my rtfd folders use “.tiff”, not “tif”, as extension. Therefore, your test should be for “tif”, not “tiff”, right?
One more possible improvement would be to not check for extensions but to check whether the UTI conforms to the public.image type: if UTTypeConformsTo(fileType, "public.image).
However, that’s not a ObjC function but a plain C function. Can those be called from AppleScript as well? And if, how does one get the filetype of a file name? One way would be to call UTTypeCreatePreferredIdentifierForTag("kUTTagClassFilenameExtension",theExt, nil), if that works.
Yes. It’s really only an outline I banged out in a hurry — it needs more work before being used, such as some more error checking.
I’m not sure how you’d get the UTI in the first place without physically saving as a file, which would slow things down a bit.
You can call them, but you can’t deal in CF types, so in practice that rules most of them out. In this case there’s an NSWorkspace alternative, -type:conformsToType:.
Actually, that’s a thought: NSWorkspace has -filenameExtension:isValidForType:.