Saving and restoring images to/from the Clipboard

How to save the clipboard (image) on file
and restore the clipboard from this file.

I can save/restore when the clipboard is text
but when the clipboard is an image the restored clipboard is text (image data as text ?)

This code, taken from one of my scripts, will write the clipboard picture data to a file. My script is for OS X so I don’t know if it works on pre-OS X systems.

set clip to the clipboard

if class of clip's item 2 is picture then
	set image_file to ((path to desktop as text) & "clipboard image")
	try
		set f to open for access file image_file with write permission
		try
			set eof of f to 0
		end try
		write (clip's item 1) to f
		close access f
	on error
		try
			close access f
		end try
	end try
end if

Since my script doesn’t restore the image to the clipboard, I don’t know how to do it. It might be possible by using a scriptable app, such as GraphicConverter, or an osax.

– Rob

You can read/write to a file using data other than text. You can read/write just about any type of data (image, record, list, string, etc.). With that in mind, this works for me:

set clip to the clipboard

if class of clip = picture then
	set image_file to ((path to desktop as text) & "clipboard image.pict")
	try
		set f to open for access file image_file with write permission
		try
			set eof of f to 0
		end try
		write (clip as picture) to f
		close access f
		tell application "Finder" to set file type of file image_file to "PICT"
	on error
		try
			close access f
		end try
	end try
	set the_data to read file image_file as picture
	set the clipboard to the_data
end if

Jon

Another point to note with pre-X systems (I haven’t yet checked it out in X itself) is that the application trying to access the clipboard has to be frontmost. In the current case, this will be the application running the script. It’s a good precaution to use ‘activate’ before either ‘the clipboard’ or ‘set the clipboard to’.

Jon, your code doesn’t work for me (no file generated) in OS X 10.2.8.

Nigel, thanks for that reminder. I always try to avoid using the clipboard in scripts so I forget about this rule. My lone script that does use the clipboard doesn’t seem to care (no bug reports anyway) so maybe I’ve been lucky. :wink:

– Rob

Jon’s script works fine on my OS 9.2.2 system (AppleScript 1.8.3), but can’t read the image file immediately after that’s been closed for access on my OS 8.6 machine (AS 1.3.7). I can’t see why it wouldn’t generate any file at all in 10.2.8 (unless class of clip is not picture). :slight_smile:

I have to say that this issue is confusing me (to say the least). With an image on the clipboard, if I run the following in Script Debugger:

set clip to the clipboard
class of clip
--> list

Item 1 is a long string of data and item 2 is a viewable picture (if I inspect it in SD). Here’s the beginning of the data in item 1:

«data TIFF4D4D002A000

I’m at a loss as to why Jon’s script won’t work for me (other than the list class causing it to terminate). As far as I know, Standard Additions is the only thing being called (according to Script Debugger’s manifest anyway). I even tried running it in Script Editor 2, in case SD was toying with me, but it made no difference. :confused:

Now I wonder if my distributed script works for others.

– Rob (the one in Ohio with the stupid look on his face)

I never get a list when I ask for the class of the clipboard in SE 2.0 (well, not unless I set it to a list: “set the clipboard to {1, 2, 3}”). Rob, is there some OSAX that is intercepting the call to the clipboard on your system? NG, that’s odd that it creates the file on your system but then it doesn’t read it back in. Is it possible to read the file and then coerce the read data to a picture?

Jon

Good question. To test, I disabled all but the default osaxen (Satimage and Jon’s were enabled previously), restarted the computer and tried again with the same result - a list of two items. Weird.

– Rob

I’ve now rebooted into 10.2.8 and taken another look at Jon’s script. When I copy a JPEG to the clipboard from Preview, ‘the clipboard’ returns a list containing a ‘TIFF picture’ and a ‘picture’. I can save these to file and get them back again, but setting the clipboard to the original list confuses it utterly. It doesn’t seem to like the TIFF picture. It seems OK with the ‘picture’ though. If you ‘set the clipboard to’ just a ‘picture’, that’s what you’ll get out of it - not a list.

set clip to the clipboard

if class of clip = picture then
else if class of clip = list and (count clip) = 2 and class of item 2 of clip is picture then
	set clip to item 1 of clip
else
	return
end if
set image_file to ((path to desktop as text) & "clipboard image.pict")
try
	set f to open for access file image_file with write permission
	try
		set eof of f to 0
	end try
	write (clip as TIFF picture) to f
	close access f
	tell application "Finder" to set file type of file image_file to "PICT"
on error
	try
		close access f
	end try
end try
-- set the clipboard to something else so we know
--the picture's there for the right reasons later
set the clipboard to ""
set the_data to read file image_file as TIFF picture
set the clipboard to the_data

The problem I was having in 8.6, Jon, was that when the script tried to read the file after saving it, the system complained that the file wasn’t open. This can be coded round by opening it again for access and reading with the new reference number.

When testing these scripts, I’ve simply been using Safari’s contextual menu to “Copy Image to Clipboard”. Nigel, I’m sure glad that you were able to confirm that it is indeed possible to get a list from the clipboard when it holds an image. I thought I was losing my sanity. :wink:

– Rob

I don’t know about your sanity, Rob. I’ve just noticed that I posted one of the failed versions of the script from when I was messing around with it. In both places where it says ‘as TIFF picture’, it should say ‘as picture’. :oops:

… and it should be 'set clip to item 2 of clip. Oh dear… :-

set clip to the clipboard

if class of clip = picture then
else if class of clip = list and (count clip) = 2 and class of item 2 of clip = picture then
   set clip to item 2 of clip
else
   return
end if
set image_file to ((path to desktop as text) & "clipboard image.pict")
try
   set f to open for access file image_file with write permission
   try
      set eof of f to 0
   end try
   write (clip as picture) to f
   close access f
   tell application "Finder" to set file type of file image_file to "PICT"
on error
   try
      close access f
   end try
end try
-- Set the clipboard to something else so we know
-- the picture's there for the right reasons later
set the clipboard to ""
set the_data to read file image_file as picture
set the clipboard to the_data

Time for bed. :slight_smile:

Well, of course I was only checking with copying an image in Photoshop. Here are my results in Mac OS X 10.3.2:

return {class of (the clipboard), the clipboard}

--copied from Safari's contextual menu
-->{record, {picture:?data?, TIFF picture:?data?}}

--copied from Photoshop
-->{picture, ?data?}

--copied from Preview
-->{record, {picture:?data?, TIFF picture:?data?}}

--copied from GraphicConverter
-->{record, {?class RECT?:?data?, picture:?data?}}

--copied from QuickTime Player
-->{record, {picture:?data?, ?class moov?:?data?}}

Jon

I guess sometime after 8.6, Standard Additions was changed to be able to read a file without having to open it first. I wonder when that happened.

Jon

Great! Now the clipboard’s returning records. :? I thought at first there might be a fortune to be made from writing ‘if’ lines for this script, but this should do the trick (if it works in 10.3):

set clip to the clipboard as list -- 'set clip to (the clipboard) as list'?
repeat with thisItem in clip
  if thisItem's class is picture then
    set clip to thisItem's contents
    exit repeat
  end if
end repeat

if clip's class is picture then
  -- as previously discussed
  
end if

Interestingly - and by now way off topic - if I set the clipboard to a record in 8.6, 9.2.2, or 10.2.8, it comes back as a list of separated labels and values:

set the clipboard to {a:1, b:2, c:"Rhubarb"}
the clipboard
--> {"a", 1, "b", 2, "c", "Rhubarb"}

But this separation only happens with top level record properties:

set the clipboard to {a:1, b:2, c:{d:"Rhubarb", e:"custard"}}
the clipboard
--> {"a", 1, "b", 2, "c", {d:"Rhubarb", e:"custard"}}

set the clipboard to {{a:1, b:2, c:"Rhubarb"}} -- a record in a list
the clipboard
--> {{a:1, b:2, c:"Rhubarb"}}

Actually, 8.6 can read a file without explicitly opening it for access first. It just has problems doing so after the file’s been created and explicitly closed for access by the same application in the same session. If I quit Script Editor, open it again, put a new picture on the clipboard, and run the script again with the file already in existence, everything works fine. Seems to be a bug.

Is it possible to make the problem more “general” ?
Save (to file) and restore (from this file) the clipboard whatever was is in the clipboard (text, picture, sound,…).

(My need is only for OS X)

I think this discussion has shown that it’s hard enough to write for a specific data type, let alone generally. :slight_smile:

It’s possible to store whatever’s returned by ‘the clipboard’ and to retrieve it later from the file by generic means:

on saveClipboard(theFile)
  set f to open for access file (theFile as string) with write permission
  try
    set eof f to 0
    -- Save the clipboard contents encapsulated in a list so that
    -- the file be can read 'as list' whatever the actual data
    write {the clipboard} to f
    close access f
  on error msg number errNum
    close access f
    error msg number errNum
  end try
end saveClipboard

on readSavedClipboardContents(theFile)
  set f to open for access file (theFile as string)
  try
    set clip to item 1 of (read f as list)
    close access f
  on error msg number errNum
    close access f
    error msg number errNum
  end try
  
  return clip
end readSavedClipboardContents


saveClipboard(choose file name) -- 'choose file name' should be 'new file' in OS 8.6

set theData to readSavedClipboardContents(choose file)

The problem comes when you try to put the data back into the clipboard with ‘set the clipboard to’. You need to write your own specific code to analyse the retrieved data and put back only what will work. Even then, there’s no guarantee that this would be a form you could paste back into a document, though it might be.

I guess I don’t understand the original problem. Why not just store the contents of the clipboard to a property of the script and then restore it from that property some time in the future? Doing it this way, you don’t need to know what type of data is on the clipboard at all (and it could be anything so when writing the file you’d need to know the class and when reading it back in you’d have to either know the class again or check for so many different classes: string, list, record, picture, TIFF, sound, script, etc., etc., etc.). If you need to save multiple clipboards, save the the data to the property as an item in a list or record (possibly adding a time/date stamp and a short description for use later when restoring the clipboard).

Jon