25
$\begingroup$

After reading this question I have determined that

Rasterize[Graphics[Circle[]], "Image", Background -> None]

allows you to do Save As on the Image and the keep the transparency. But if you use the the menus Edit->Copy As->Bitmap or Edit->Copy As->Metafile I have noticed that graphics fail to keep their transparency when pasting into other applications.

Now I can Export an image and copy and paste it back into Mathematica(or another application) and everything works just fine, therefore the clipboard can hold references to transparent images.

Doing some further investigation, here is an image of ClipSpy showing how CF_HDROP and FileNameW(FileName doesn't seem to contain the entire piece of text) contain the location of the png file, allowing you to paste it into other applications.

enter image description here

When working with lots of images, exporting every image becomes quite tedious. It seems like their should be some way to automatically have Mathematica export the file and store a reference of it in the clipboard to retain the image transparency.

So how do I patch or extend Mathematica's clipboard functionality to work with transparent images? Possible useful links for myself and others 1 2 3 4

EDIT: It appears a handful of applications like Inkscape and Powerpoint don't allow you to paste with only a CF_DROP reference. Instead it seems to be better to store the actual image as a PNG on the clipboard.

$\endgroup$
2
  • $\begingroup$ What program are you trying to paste into? $\endgroup$
    – 0xFE
    May 30, 2013 at 3:09
  • $\begingroup$ I'm not sure what you are trying to do - if you really have a lot of images, then exporting them as files can be done en masse. It seems to me that dealing with each individual image separately via the clipboard is the definition of tedious. $\endgroup$
    – bill s
    May 30, 2013 at 3:32

3 Answers 3

15
$\begingroup$

Here is a considerable simplification of Liam's accepted answer. It avoids the need to create and compile a C# program. This is basically just a small modification to Simon Woods' answer, so that it writes directly to the clipboard instead of creating a temporary file on disk. This avoids the need to clean up the file afterward.

Needs["NETLink`"]

WriteToClipboardTransparent[g_] :=
    Module[{png, strm, dataObject},
        InstallNET[];
        png = ExportString[Rasterize[g, "Image", Background->None], "PNG"];
        NETBlock[
            strm = NETNew["System.IO.MemoryStream", ToCharacterCode[png]];
            dataObject = NETNew["System.Windows.Forms.DataObject"];
            dataObject@SetData["PNG", strm];
            LoadNETType["System.Windows.Forms.Clipboard"];
            Clipboard`SetDataObject[dataObject]
        ]
    ]

The button and menu commands would then simply call WriteToClipboardTransparent[NotebookRead[InputNotebook]].

$\endgroup$
6
  • 1
    $\begingroup$ Awesome! How did you get bye without having to use [STAThread]? According to stackoverflow.com/questions/5944605/c-sharp-clipboard-gettext/… I think you need to run under an a STAThread. I couldn't get the code to run consistently without such an attribute. I mean if it works, it works :) $\endgroup$
    – William
    Jul 8, 2013 at 19:12
  • 4
    $\begingroup$ You do need the [STAThread] attribute when writing a standalone program, but the .NET/Link thread that handles calls from Mathematica already has that attribute. If you're curious, you can see this property being set in line 79 of Reader.cs in the .NET/Link source code. $\endgroup$ Jul 8, 2013 at 20:33
  • $\begingroup$ Could this same technique be used to create an .exe file for Snagit? This article has a similar problem, but didn't cover the transparency issue. dc-abaper.blogspot.com/2011/07/… $\endgroup$
    – Glen Lipka
    Jul 15, 2013 at 17:40
  • $\begingroup$ @GlenLipka Yes I believe so athlough I'm not entirely sure of the best way to do such. $\endgroup$
    – William
    Mar 1, 2014 at 20:53
  • $\begingroup$ @ToddGayley, This doesn't work for me since SetData is not recognized. (I'm using Mathematica 11.1 on Ubuntu 17.04) $\endgroup$
    – Miladiouss
    Jul 27, 2017 at 0:19
11
$\begingroup$

Something like this? It does just what you suggested: export the file and then place a FileDrop reference to it on the clipboard.

Needs["NETLink`"];
InstallNET[];
LoadNETType["System.Windows.Forms.DataFormats"];
LoadNETType["System.Windows.Forms.Clipboard"];

exportToClipboard[graphics_] := Module[{dob, file},
  file = FileNameJoin[{$TemporaryDirectory, "mmaClipboard.png"}];
  Export[file, graphics];
  NETBlock[
   dob = NETNew["System.Windows.Forms.DataObject"];
   dob @ SetData[DataFormats`FileDrop, True, {file}];
   Clipboard`SetDataObject[dob]]];

e.g.

image = Rasterize[Graphics[Circle[]], "Image", Background -> None];

exportToClipboard[image]

Paste into Powerpoint...

enter image description here

$\endgroup$
6
  • $\begingroup$ Can you use ExportString to avoid dumping to a file? $\endgroup$
    – Ajasja
    May 31, 2013 at 19:12
  • 2
    $\begingroup$ @Ajasja, with this method only the file name goes onto the clipboard, not the contents. It's using the same mechanism as if you "copy" a file on the desktop and paste it into an application, the application just receives the file path and imports the file itself. You can put image data onto the clipboard but it has to be converted to a specific bitmap format. This way is easier! $\endgroup$ May 31, 2013 at 20:11
  • $\begingroup$ I do love the simpleness of this. I was orignally trying to use C APIs this is better. @Ajasja I have noticed that this solution doesn't work with a select number of apps(Inkscape) while it works great with others like Photoshop. I am looking into copying the image as a PNG to the clipboard. I believe it involves something like MemoryStream ms = new MemoryStream(); bitmap.Save(ms, ImageFormat.Png); IDataObject dataObject = new DataObject(); dataObject.SetData("PNG", false, ms); System.Windows.Forms.Clipboard.Clear(); System.Windows.Forms.Clipboard.SetDataObject(dataObject, false); $\endgroup$
    – William
    May 31, 2013 at 20:18
  • $\begingroup$ @Ajasja It appears dragging and dropping the png file works fine in most any application, while pasting only sometimes works, I am going to try and create a pop up window that opens and stays on top of other windows temporarily while you drag the image into another application. $\endgroup$
    – William
    May 31, 2013 at 20:26
  • $\begingroup$ For anyone using this regularly, I would advise replacing Directory[] with $TemporaryDirectory to avoid putting pictures in random places across your system. My Directory[] location changes depending on the script and what files I might be working on. $\endgroup$
    – William
    Jun 11, 2013 at 18:43
9
$\begingroup$

The following code copies the actua PNG image to the clipboard with transparency. This varies from the other answer in that it actual stores the entire image instead of a reference to the file. This code will likely work with more applications then the one above. It currently uses the Ctrl+Shift+C to copy with transparency. The code takes a while to run currently because the program gets recompiled and rerun each time you hit the keyboard shortcut. Please suggest any modifications to help make it more portable or faster

To test that it is working run.

   WriteToClipboard["PNG", ExportString[
     Rasterize[
       Graphics[{Circle[]}]
      , "Image", Background -> None], {"Base64", "PNG"}]]

The following prints a Button that copy the selected Graphic as a PNG, and sets the keyboard shortcut Ctrl + Shift + C.

enter image description here

(* Construct Button *)
Print[Button["Copy Image",
  If[
   Head[NotebookRead[EvaluationNotebook[]]] === GraphicsBox,Null,
   MessageDialog["You must select a Graphic."];
   Return[];
   ];
  MessageDialog[AbsoluteTiming[
    WriteToClipboard["PNG", 
      ExportString[
       Rasterize[NotebookRead[EvaluationNotebook[]], "Image", 
        Background -> None], {"Base64", "PNG"}]];
    ]]
  ]
 ]
(* Set up keyboard shortcut *)
FrontEndExecute[
 FrontEnd`AddMenuCommands["ClipboardWithPNG", {
   MenuItem["ClipboardWithPNG", FrontEnd`KernelExecute[Function[

       WriteToClipboard["PNG", ExportString[
         Rasterize[
          NotebookRead[InputNotebook[]]
          , "Image", Background -> None], {"Base64", "PNG"}]]

       ][]
     ], MenuKey["c", Modifiers -> {"Control", "Shift"}],
    System`MenuEvaluator -> Automatic]}]]

And the needed code and functions.

Module[{temp, t2, CopyFileOverwrite, csfile, exefile, file, NRun},
 temp  = FileNameJoin[{$TemporaryDirectory, 
    "ClipboardTransparency314241234"}];
 NRun[s_] := (str = OpenWrite[FileNameJoin[{temp, "temp.bat"}]];
   WriteString[str, s];
   Import["!" <> Close[str], "Text"];);

 If[DirectoryQ[temp],
  t2 = Directory[];
  SetDirectory[temp];
  Map[Function[DeleteFile[#]], FileNames[]];
  SetDirectory[t2];
  DeleteDirectory[temp, DeleteContents -> True];
  , Null
  ];
 CreateDirectory[temp];

 CopyFile[
  FileNameJoin[{$InstallationDirectory, "SystemFiles", "Links", 
    "NETLink", "Wolfram.NETLink.dll"}],
  FileNameJoin[{temp, "Wolfram.NETLink.dll"}]
  ];

 csfile = FileNameJoin[{temp, "File.cs"}];
 exefile = FileNameJoin[{temp, "File.exe"}];
 file = OpenWrite[csfile];
 WriteString[file, "using System;
        using Wolfram.NETLink;
        using System.IO;
        using System.Drawing;
        using System.Windows.Forms;

        public class File{
         [STAThread]
         public static void Main(String[] args) {
            string input = System.Console.In.ReadToEnd();
            byte[] bytes = Convert.FromBase64String(input); 
            Stream stream = new MemoryStream(bytes);
            IDataObject dataObject = new DataObject();
            dataObject.SetData(args[0], stream);
            Clipboard.SetDataObject(dataObject, true);
          }
        }"];
 Close[file];
 NRun ["cd " <> temp  "
   C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\csc.exe  \
/reference:Wolfram.NETLink.dll File.cs"];

 WriteToClipboard[type_, data_] := (
   dataloc = FileNameJoin[{temp, "data.txt"}];
   datafile = OpenWrite[dataloc];
   WriteString[datafile, data];
   Close[datafile];
   NRun["type " <> dataloc <> " | " <> exefile <> " " <> type];
   );
 ]

To help measure timing I have been using this modified code with this function.

Time[code_] := (
  absolute = AbsoluteTiming[ReleaseHold[code]];
  (* Print[ToString[absolute[[1]]]<>" | "<>ToString[code] ]; *)
  absolute[[2]]
  )
Time[HoldForm[Print["test"]]]

Old Code: To help users understand the evolution of the code, here is the old technique. It re compiles the program each time WriteToCliboard is run, therefore it will likely much slower.

$\endgroup$
3
  • $\begingroup$ is there a way to convert it to CF_BITMAP? $\endgroup$
    – Rainb
    Feb 15, 2021 at 18:37
  • $\begingroup$ @Rainb I'm not sure what the point would be there was already no transparency support $\endgroup$
    – William
    Feb 15, 2021 at 19:00
  • $\begingroup$ I think CF_DIBV5 has transparency support. $\endgroup$
    – Rainb
    Feb 15, 2021 at 21:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.