###How Do I Copy Image File to Clipboard and Retain Format?
I want to, using AppleScript, copy an image file (given it’s POSIX path) to the Clipboard and retain the same format that is in the file. So that when I paste it, also using AppleScript, it will paste into a rich text app using the same format.
I’ve done a bit of searching, and I can’t seem to find a bullet-proof script.
I figure ASObjC is the solution, but I don’t know how to write it.
Any help would be greatly appreciated.
**EDIT: 2017-05-01 9:38 PM CT**
Based mostly on the guidance from @ShaneStanley, but also from @ccstone and @Tom, I was able to cobble together what I hope will be the solution for this question/problem.
See my (hopefully final) script at:
http://forum.latenightsw.com/t/how-do-i-copy-image-file-to-clipboard-and-retain-format/590/27
You may not always be able to retain the format exactly, but try this:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set theImage to current application's NSImage's alloc()'s initWithContentsOfFile:posixPath
if theImage is missing value then error "Can't read image format"
set theClip to current application's NSPasteboard's generalPasteboard()
theClip's clearContents()
theClip's writeObjects:{theImage}
From the clipboard created your script, it pasted great into:
TextEdit
Evernote
Word
This forum
May I ask one more question?
What would be the paste command in AppleScript (other than the old standby of keystroke) ?
Thanks again. I know you must be busy getting ready for your big trip, so I really appreciate you taking the time to respond to my post.
**Questions if you have time**
If not, they can wait until you get back.
**(1) What format is put on the Clipboard?**
The file I copied was a "PNG", but a Clipboard viewer I have report it as a "TIFF":
<img src="/uploads/default/original/1X/9de7d35b6f8ed8fa2a61138fb3d8ba6d6c3595e1.png" width="288" height="194">
EDIT: 2017-04-28 8:13 PM CT
Maybe the Clipboard viewer doesn’t see everything.
When I add this statement to the script:
set clipRec to the clipboard as record
SD6 reports this:
Obviously many more formats.
So, is SD6 correct?
When I paste the image into SnagIT, and then copy the image from SnagIT, it reports as this:
<img src="/uploads/default/original/1X/f6c7158f814f96d8c8ab20600d32bb7eb18d6113.png" width="383" height="264">
Apparently SnagIT is putting both TIFF and PNG on the Clipboard.
**(2) If the script does put just TIFF on the Clipboard, how do I force it to use the format of the file?** I almost never want to use the TIFF format.
Thanks again, and don't worry if you don't have time for these questions now.
You are mis-understanding how the clipboard works.
When you copy, you pass, say, an image or some text to the clipboard. The clipboard then works out what formats it can offer that data in – styled text might be offered in styled and non-styled formats, for example, and images will be offered in multiple formats. So you might pass one format, but what you passed can be offered in others. (Of course apps can have their own proprietary clipboard formats, too, and they can provide the data in more than one format – and more than one clipboard.)
When you paste, typically the receiving application will pass the clipboard a list of formats it can accept, in preference order, and the clipboard then works through them in order – returning the first it finds. The app then asks for the data in the returned format. (There are variations – a text editor might only support NSStrings, so it it doesn’t need to check.)
And a PDF is an image file in the terms of the script above.
Now you can control what gets placed on the clipboard a bit more, but that starts to get complicated because you need to deal with a range of different possibilities. Not something for now.
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set theClip to current application's NSPasteboard's generalPasteboard()
theClip's |types|() as list
No, that doubles things up with old style types listed as if they are separate items -- better this:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set theClip to current application's NSPasteboard's generalPasteboard()
theClip's (pasteboardItems()'s objectAtIndex:0)'s |types|() as list
This assumes a single pasteboard item, which is mostly the case.
Thanks. That’s more convenient than getting the Clipboard as record – especially when image data is involved.
I have the same Clipboard Viewer app (from the app-store) that JM has, and I have the very similar one that comes with Xcode-extras – but it’s nice to be able to get clip-types as text within Script Debugger.
No matter what the input file is (e.g. PNG) it always writes only TIFF data to the clipboard:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set theClip to current application's NSPasteboard's generalPasteboard()
theClip's clearContents()
set thePath to "/Users/tom/_Tmp ƒ/test-600.png"
--set thePath to "/Users/tom/_Tmp ƒ/test.jpg"
set theImage to current application's NSImage's alloc()'s initWithContentsOfFile:thePath
if theImage is missing value then error "Can't read image format"
theClip's writeObjects:{theImage}
theClip's |types|() as list
# Returns always: {"public.tiff", "NeXT TIFF v4.0 pasteboard type"}
# Returns for big image data: {}
When I paste that clipboard into a mail message I get a LZW-compressed TIFF file of 1.4MB. (The source PNG file has 325KB.)
In Addition, if the amount of image data (not the file size!) is big enough it fails completely. (For example with a 1MB PNG file that contains 630MB of expanded image data.)
Whereas, when I use plain old vanilla AppleScript (set the clipboard to … as …), everything works as expected:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set theClip to current application's NSPasteboard's generalPasteboard()
theClip's clearContents()
set thePath to "/Users/tom/_Tmp ƒ/test-600.png"
--set thePath to "/Users/tom/_Tmp ƒ/test.jpg"
set the clipboard to (read thePath as «class PNGf») # [1]
--set the clipboard to (read thePath as JPEG picture) # [2]
theClip's |types|() as list
# [1] Returns: {"public.png", "Apple PNG pasteboard type", "public.tiff", "NeXT TIFF v4.0 pasteboard type"}
# [2] Reurns: {"public.jpeg", "CorePasteboardFlavorType 0x4A504547", "public.tiff", "NeXT TIFF v4.0 pasteboard type"}
When I paste that clipboard into Mail I get a 325 KB PNG file. (The same as the source.)
And it doesn’t choke on big image data.
Can you reproduce this?
If yes, is there an ASOC way to get the same result as with vanilla AppleScript?
PS:
Here the test file: test-600.png.zip (174.4 KB)
And this is the big one that makes the ASOC script fail: test-1200.png.zip (473.3 KB)
I’m seeing the same thing.
Using Tom’s example file, PNG, 325KB:
When I paste into Evernote, Note Size: 962.2 KB
When I drag/drop file into Evernote, Note Size: 317.KB
set thePath to "/Users/tom/_Tmp ƒ/test-600.png"
--set thePath to "/Users/tom/_Tmp ƒ/test.jpg"
set the clipboard to (read thePath as «class PNGf») # [1]
--set the clipboard to (read thePath as JPEG picture) # [2]
How would you handle reading any type of image file?
Is there a way to set the class in the read based on the file?
I know how to convert the clipboard’s content into jpeg but I don’t know how to move this jpeg data into the pasteboard.
Here is the handler (borrowed from Shane STANLEY):
on jpegFromClipToPath:thePath compressFactor:compfactor -- 0.0 = max compression, 1.0 = none
-- set thePath to POSIX path of thePath
set pb to current application's NSPasteboard's generalPasteboard() -- get pasteboard
set theData to pb's dataForType:"public.tiff" -- get tiff data off pasteboard
if theData = missing value then error "No tiff data found on clipboard"
set newRep to current application's NSBitmapImageRep's imageRepWithData:theData
set theData to (newRep's representationUsingType:(current application's NSJPEGFileType) |properties|:{NSImageCompressionFactor:compfactor, NSImageProgressive:false})
if (thePath does not end with ".jpg") and (thePath does not end with ".jpeg") then
set thePath to my ChangeExtension(thePath, "jpg") as text
end if
set theResult to (theData's writeToFile:thePath atomically:true)
return {(theResult = 1), thePath}
end jpegFromClipToPath:compressFactor:
Yes, I’m a bit surprised by that. I could understand it adding TIFF, and even preferring TIFF, but I expected it would add PNG too. It may be that it will only do the conversions lazily on larger images.
Yes, there’s no guarantee with the clipboard – as a TIFF that file is 50 megapixels-plus, so it’s obviously drawn the line.
You can just treat it as data. I’m not sure if it works for all formats, but it probably will for the obvious ones:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set posixPath to "/Users/shane/desktop/test-1200.png"
set {theData, theError} to current application's NSData's dataWithContentsOfFile:posixPath options:0 |error|:(reference)
set theClip to current application's NSPasteboard's generalPasteboard()
theClip's clearContents()
theClip's setData:theData forType:"public.png"
theClip's (pasteboardItems()'s objectAtIndex:0)'s |types|() as list
In case it’s not obvious, the type in setData:forType: will obviously have to match the file type. And the common types are also defined as constants (for example, NSPasteboardTypePNG).
In that case presumably the NSURL is on the clipboard, and TextEdit then reads the data from the file. So the clipboard really has no part in the decision.
There is – but for the application, not the clipboard.
When I applied the Shane’s script to the file “test-1200.png” and pasted in TextEdit I got a tiff file in a rtfd document - which is the normal behavior.
When I did the same and pasted in Mail, I got a png file - which is fine.
Then I edited the script so that it put jpeg data in the clipboard.
When I applied the edited script to the “test-1200.png” file and pasted in Mail, although the clipboard’s content was {“public.jpeg”, “CorePasteboardFlavorType 0x4A504547”, “public.tiff”, “NeXT TIFF v4.0 pasteboard type”}, I got a tiff file - which is not what I hoped to get.