• 读取图片文件MetaFile放入Windows剪切板


    前言

    前段时间群里有个小伙在工作中遇到一个问题,透明的图片存入剪切板在粘贴到adobe PDF中出现不透明问题但是粘贴到Excel可以,还有就是从excel复制再粘贴到PDF也是可以。小伙在群里发了两天都没有解决,当时看到这个问题感觉很有趣我就去尝试了一下,当时我用的WPS,我试了一下可以粘贴到WPS 打开的PDF中,当时我感觉是PDF编辑器的问题,建议小伙换个,小伙说不能换客户要求必须是这个,好的嘛那就开始搞

    号脉

    我打开两个神级IDE VS 分别获取一下从excel复制之后和通过小伙程序复制之后存到剪切板中的数据,我对比了一下剪切板中的确有很多的不同,那就开始尝试是因啥不同导致的,我用程序把从Excel中复制获取的数据按照参数对象一个一个从新清空剪切板在重新放入剪切板,尝试是哪个参数对象导致的PDF无法呈现预想的结果。最后确定是因为剪切板中没有存储MetafilePict数据导致的

    开药

    下面获取图片文件的MetaFile数据

    public Metafile GetGeometryMetafile(Bitmap bitmap)
      	{
    
      		Metafile metafile;
      		using (MemoryStream stream = new MemoryStream())
      		using (Graphics rtfBoxGraphics = Graphics.FromImage(bitmap))
      		{
      			IntPtr pDeviceContext = rtfBoxGraphics.GetHdc();
    
      			metafile = new Metafile(stream, pDeviceContext);
      			using (Graphics imageGraphics = Graphics.FromImage(metafile))
      			{
      				//imageGraphics.DrawImage(bitmap, new Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height)); 
      				imageGraphics.DrawImageUnscaled(bitmap, new Rectangle(0, 0, bitmap.Width, bitmap.Height));
      			}
      			rtfBoxGraphics.ReleaseHdc(pDeviceContext);
      		}
      		return metafile;
    
    
      	}
    

    吃药

    下面把获取的文件MetaFile放入剪切板
    因为特殊原因,需要调用系统dll进行剪切板操作

    internal static class ClipboardMetafileHelper
    		{
    
    			[DllImport("user32.dll")]
    			static extern bool OpenClipboard(IntPtr hWndNewOwner);
    
    			[DllImport("user32.dll")]
    			static extern bool EmptyClipboard();
    
    			[DllImport("user32.dll")]
    			static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);
    
    			[DllImport("user32.dll")]
    			static extern bool CloseClipboard();
    
    			[DllImport("gdi32.dll")]
    			static extern IntPtr CopyEnhMetaFile(IntPtr hemfSrc, string fileName);
    
    			[DllImport("gdi32.dll")]
    			static extern bool DeleteEnhMetaFile(IntPtr hemf);
    
    
    			/// 
    			/// Copies the given  to the clipboard.
    			/// The given  is set to an invalid state inside this function.
    			/// 
    			static public bool PutEnhMetafileOnClipboard(IntPtr hWnd, Metafile metafile)
    			{
    				return PutEnhMetafileOnClipboard(hWnd, metafile, true);
    			}
    
    
    			/// 
    			/// Copies the given  to the clipboard.
    			/// The given  is set to an invalid state inside this function.
    			/// 
    			static public bool PutEnhMetafileOnClipboard(IntPtr hWnd, Metafile metafile, bool clearClipboard)
    			{
    				if (metafile == null) throw new ArgumentNullException("metafile");
    				bool bResult = false;
    				IntPtr hEMF, hEMF2;
    				hEMF = metafile.GetHenhmetafile(); // invalidates mf
    				if (!hEMF.Equals(IntPtr.Zero))
    				{
    					try
    					{
    						hEMF2 = CopyEnhMetaFile(hEMF, null);
    						if (!hEMF2.Equals(IntPtr.Zero))
    						{
    							if (OpenClipboard(hWnd))
    							{
    								try
    								{
    									if (clearClipboard)
    									{
    										if (!EmptyClipboard())
    											return false;
    									}
    									IntPtr hRes = SetClipboardData(14 /*CF_ENHMETAFILE*/, hEMF2);
    									bResult = hRes.Equals(hEMF2);
    								}
    								finally
    								{
    									CloseClipboard();
    								}
    							}
    						}
    					}
    					finally
    					{
    						DeleteEnhMetaFile(hEMF);
    					}
    				}
    				return bResult;
    			}
    
    
    			/// 
    			/// Copies the given  to the specified file. If the file does not exist, it will be created.
    			/// The given  is set to an invalid state inside this function.
    			/// 
    			static public bool SaveEnhMetaFile(string fileName, Metafile metafile)
    			{
    				if (metafile == null) throw new ArgumentNullException("metafile");
    				bool result = false;
    				IntPtr hEmf = metafile.GetHenhmetafile();
    				if (hEmf != IntPtr.Zero)
    				{
    					IntPtr resHEnh = CopyEnhMetaFile(hEmf, fileName);
    					if (resHEnh != IntPtr.Zero)
    					{
    						DeleteEnhMetaFile(resHEnh);
    						result = true;
    					}
    					DeleteEnhMetaFile(hEmf);
    					metafile.Dispose();
    				}
    				return result;
    			}
    		}
    
    var path = AppDomain.CurrentDomain.BaseDirectory + "1.png";
    		
    			Bitmap bm = new Bitmap(path, false);
    			
    			var mf = GetGeometryMetafile(bm);
    			ClipboardMetafileHelper.PutEnhMetafileOnClipboard(IntPtr.Zero, mf);
    

    总结

    功能看着挺简单,其实内部有很多的坑(文件MetaFIle数据的获取,MetaFile怎么才能正确放到剪切板中),从网上找的资料也不是很全,没有现成的代码,这里做一下总结汇总,供大家使用。上述代码也有一定的问题(剪切板中的数据放到别的编辑器有的失效是因为剪切板中数据缺少对应编辑器所需的参数,可根据Clipboard.GetDataObject()获取缓存数据对象判断编辑器需要的对象然后利用下面方法进行后续代码优化。

    public static void SetClipboardImage(Bitmap image, Bitmap imageNoTr, DataObject data)
    		{
    			Clipboard.Clear();
    			if (data == null)
    				data = new DataObject();
    			if (imageNoTr == null)
    				imageNoTr = image;
    			using (MemoryStream pngMemStream = new MemoryStream())
    			using (MemoryStream dibMemStream = new MemoryStream())
    			{
    				// As standard bitmap, without transparency support
    				data.SetData(DataFormats.Bitmap, imageNoTr);
    				// As PNG. Gimp will prefer this over the other two.
    				image.Save(pngMemStream, ImageFormat.Png);
    				data.SetData("PNG", pngMemStream);
    				// As DIB. This is (wrongly) accepted as ARGB by many applications.
    				Byte[] dibData = ConvertToDib(image);
    				dibMemStream.Write(dibData, 0, dibData.Length);
    				data.SetData(DataFormats.Dib, dibMemStream);
    				// The 'copy=true' argument means the MemoryStreams can be safely disposed after the operation.
    				Clipboard.SetDataObject(data, true);
    			}
    		}
    

    引用资料

    https://www.cnblogs.com/watsonyin/archive/2007/11/22/968651.html
    https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.forms.clipboard?view=windowsdesktop-6.0
    https://github.com/LudovicT/NShape


    __EOF__

  • 本文作者: MYMeng
  • 本文链接: https://www.cnblogs.com/bcodepod/p/16616768.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    高性能网络IO框架研究一:三种模式
    一年前端面试打怪升级之路
    百战RHCE(第四十六战:运维工程师必会技-Ansible学习1-基础知识讲解)
    LeetCode高频题55. 跳跃游戏
    计算机视觉+人工智能面试笔试总结——模型量化
    介绍几种常用的Oracle客户端工具
    三维医学图像处理系统(PACS)源码
    Vue 常用指令
    Docker18:Docker- compose容器编排
    Python 无废话-基础知识流程控制语句
  • 原文地址:https://www.cnblogs.com/bcodepod/p/16616768.html