/// <summary> /// Set an Image to the clipboard /// This method will place images to the clipboard depending on the ClipboardFormats setting. /// e.g. Bitmap which works with pretty much everything and type Dib for e.g. OpenOffice /// because OpenOffice has a bug http://qa.openoffice.org/issues/show_bug.cgi?id=85661 /// The Dib (Device Indenpendend Bitmap) in 32bpp actually won't work with Powerpoint 2003! /// When pasting a Dib in PP 2003 the Bitmap is somehow shifted left! /// For this problem the user should not use the direct paste (=Dib), but select Bitmap /// </summary> public static void SetClipboardData(ISurface surface) { var dataObject = new DataObject(); // This will work for Office and most other applications //ido.SetData(DataFormats.Bitmap, true, image); MemoryStream dibStream = null; MemoryStream dibV5Stream = null; MemoryStream pngStream = null; Bitmap bitmapToSave = null; var disposeImage = false; try { var outputSettings = new SurfaceOutputSettings(OutputFormats.png, 100, false); // Create the image which is going to be saved so we don't create it multiple times disposeImage = ImageOutput.CreateBitmapFromSurface(surface, outputSettings, out bitmapToSave); try { // Create PNG stream if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.PNG)) { pngStream = new MemoryStream(); // PNG works for e.g. Powerpoint var pngOutputSettings = new SurfaceOutputSettings(OutputFormats.png, 100, false); ImageOutput.SaveToStream(bitmapToSave, null, pngStream, pngOutputSettings); pngStream.Seek(0, SeekOrigin.Begin); // Set the PNG stream dataObject.SetData(FORMAT_PNG, false, pngStream); } } catch (Exception pngEx) { Log.Error().WriteLine(pngEx, "Error creating PNG for the Clipboard."); } try { if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.DIB)) { using (var tmpBmpStream = new MemoryStream()) { // Save image as BMP var bmpOutputSettings = new SurfaceOutputSettings(OutputFormats.bmp, 100, false); ImageOutput.SaveToStream(bitmapToSave, null, tmpBmpStream, bmpOutputSettings); dibStream = new MemoryStream(); // Copy the source, but skip the "BITMAPFILEHEADER" which has a size of 14 dibStream.Write(tmpBmpStream.GetBuffer(), BITMAPFILEHEADER_LENGTH, (int)tmpBmpStream.Length - BITMAPFILEHEADER_LENGTH); } // Set the DIB to the clipboard DataObject dataObject.SetData(DataFormats.Dib, true, dibStream); } } catch (Exception dibEx) { Log.Error().WriteLine(dibEx, "Error creating DIB for the Clipboard."); } // CF_DibV5 try { if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.DIBV5)) { // Create the stream for the clipboard dibV5Stream = new MemoryStream(); // Create the BITMAPINFOHEADER var header = BitmapInfoHeader.Create(bitmapToSave.Width, bitmapToSave.Height, 32); // Make sure we have BI_BITFIELDS, this seems to be normal for Format17? header.Compression = BitmapCompressionMethods.BI_BITFIELDS; var headerBytes = BinaryStructHelper.ToByteArray(header); // Write the BITMAPINFOHEADER to the stream dibV5Stream.Write(headerBytes, 0, headerBytes.Length); // As we have specified BI_COMPRESSION.BI_BITFIELDS, the BitfieldColorMask needs to be added var colorMask = BitfieldColorMask.Create(); // Create the byte[] from the struct var colorMaskBytes = BinaryStructHelper.ToByteArray(colorMask); Array.Reverse(colorMaskBytes); // Write to the stream dibV5Stream.Write(colorMaskBytes, 0, colorMaskBytes.Length); // Create the raw bytes for the pixels only var bitmapBytes = BitmapToByteArray(bitmapToSave); // Write to the stream dibV5Stream.Write(bitmapBytes, 0, bitmapBytes.Length); // Set the DIBv5 to the clipboard DataObject dataObject.SetData(FORMAT_17, true, dibV5Stream); } } catch (Exception dibEx) { Log.Error().WriteLine(dibEx, "Error creating DIB for the Clipboard."); } // Set the HTML if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.HTML)) { var tmpFile = ImageOutput.SaveToTmpFile(surface, new SurfaceOutputSettings(OutputFormats.png, 100, false), null); var html = GetHtmlString(surface, tmpFile); dataObject.SetText(html, TextDataFormat.Html); } else if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.HTMLDATAURL)) { string html; using (var tmpPngStream = new MemoryStream()) { var pngOutputSettings = new SurfaceOutputSettings(OutputFormats.png, 100, false) { // Do not allow to reduce the colors, some applications dislike 256 color images // reported with bug #3594681 DisableReduceColors = true }; // Check if we can use the previously used image if (bitmapToSave.PixelFormat != PixelFormat.Format8bppIndexed) { ImageOutput.SaveToStream(bitmapToSave, surface, tmpPngStream, pngOutputSettings); } else { ImageOutput.SaveToStream(surface, tmpPngStream, pngOutputSettings); } html = GetHtmlDataUrlString(surface, tmpPngStream); } dataObject.SetText(html, TextDataFormat.Html); } } finally { // we need to use the SetDataOject before the streams are closed otherwise the buffer will be gone! // Check if Bitmap is wanted if (CoreConfig.ClipboardFormats.Contains(ClipboardFormats.BITMAP)) { dataObject.SetImage(bitmapToSave); // Place the DataObject to the clipboard SetDataObject(dataObject, true); } else { // Place the DataObject to the clipboard SetDataObject(dataObject, true); } pngStream?.Dispose(); dibStream?.Dispose(); dibV5Stream?.Dispose(); // cleanup if needed if (disposeImage) { bitmapToSave?.Dispose(); } } }
/// <summary> /// Helper method to try to get an image in the specified format from the dataObject /// the DIB reader should solve some issues /// It also supports Format17/DibV5, by using the following information: http://stackoverflow.com/a/14335591 /// </summary> /// <param name="format">string with the format</param> /// <param name="dataObject">IDataObject</param> /// <returns>Bitmap or null</returns> private static Bitmap GetBitmapForFormat(string format, IDataObject dataObject) { var clipboardObject = GetFromDataObject(dataObject, format); var imageStream = clipboardObject as MemoryStream; if (!IsValidStream(imageStream)) { // TODO: add "HTML Format" support here... return(clipboardObject as Bitmap); } if (CoreConfig.EnableSpecialDIBClipboardReader) { if (format == FORMAT_17 || format == DataFormats.Dib) { Log.Info().WriteLine("Found DIB stream, trying to process it."); try { if (imageStream != null) { var dibBuffer = new byte[imageStream.Length]; imageStream.Read(dibBuffer, 0, dibBuffer.Length); var infoHeader = BinaryStructHelper.FromByteArray <BitmapInfoHeader>(dibBuffer); if (!infoHeader.IsDibV5) { Log.Info().WriteLine("Using special DIB <v5 format reader with biCompression {0}", infoHeader.Compression); var fileHeaderSize = Marshal.SizeOf(typeof(BitmapFileHeader)); var fileHeader = BitmapFileHeader.Create(infoHeader); var fileHeaderBytes = BinaryStructHelper.ToByteArray(fileHeader); using (var bitmapStream = new MemoryStream()) { bitmapStream.Write(fileHeaderBytes, 0, fileHeaderSize); bitmapStream.Write(dibBuffer, 0, dibBuffer.Length); bitmapStream.Seek(0, SeekOrigin.Begin); var image = BitmapHelper.FromStream(bitmapStream); if (image != null) { return(image); } } } else { Log.Info().WriteLine("Using special DIBV5 / Format17 format reader"); // CF_DIBV5 var gcHandle = IntPtr.Zero; try { var handle = GCHandle.Alloc(dibBuffer, GCHandleType.Pinned); gcHandle = GCHandle.ToIntPtr(handle); return (new Bitmap(infoHeader.Width, infoHeader.Height, -(int)(infoHeader.SizeImage / infoHeader.Height), infoHeader.BitCount == 32 ? PixelFormat.Format32bppArgb : PixelFormat.Format24bppRgb, new IntPtr(handle.AddrOfPinnedObject().ToInt32() + infoHeader.OffsetToPixels + (infoHeader.Height - 1) * (int)(infoHeader.SizeImage / infoHeader.Height)) )); } catch (Exception ex) { Log.Error().WriteLine(ex, "Problem retrieving Format17 from clipboard."); } finally { if (gcHandle == IntPtr.Zero) { GCHandle.FromIntPtr(gcHandle).Free(); } } } } } catch (Exception dibEx) { Log.Error().WriteLine(dibEx, "Problem retrieving DIB from clipboard."); } } } else { Log.Info().WriteLine("Skipping special DIB format reader as it's disabled in the configuration."); } try { if (imageStream != null) { imageStream.Seek(0, SeekOrigin.Begin); var tmpImage = BitmapHelper.FromStream(imageStream); if (tmpImage != null) { Log.Info().WriteLine("Got image with clipboard format {0} from the clipboard.", format); return(tmpImage); } } } catch (Exception streamImageEx) { Log.Error().WriteLine(streamImageEx, $"Problem retrieving {format} from clipboard."); } return(null); }