如何将图像与DPI信息一起保存到剪贴板中?

时间:2015-09-29 16:13:42

标签: c# .net vb.net image dpi

看起来 Bitmap.SetResolution()对剪贴板没有影响,请参阅以下简单的代码:

 
Dim bitmap1 As Image = New System.Drawing.Bitmap(100, 100)
Using gr As Graphics = Graphics.FromImage(bitmap1)
    gr.FillRectangle(System.Drawing.Brushes.Black, 0, 0, 99, 99)
    gr.FillRectangle(System.Drawing.Brushes.White, 10, 10, 89, 89)
End Using

bitmap1.SetResolution(150, 150)  

Clipboard.SetImage(bitmap1)                              ' DPI not set
bitmap1.Save("D:\bitmap1.png", imaging.ImageFormat.Png)  ' DPI set

该文件包含正确设置的图像DPI。在clipbaord中,它不存在。

证明:在剪贴板转储中(图片由IrfanView插入到剪贴板中),请参阅 60 × 120 DPI 的DIB位图标题(黄色=水平DPI,绿色=垂直DPI):

enter image description here

但是在使用Clipboard.SetImage()插入图片后,这两个数字都是0

我的目标:能够将图像粘贴到具有适当大小的Microsoft Word中(取自DPI和尺寸)。如果没有在剪贴板中设置DPI,粘贴后图像太大。但它包含的条形码已经是1 bar = 1 px分辨率,因此我无法对其进行采样。

如何验证DPI:通过剪贴板查看器或在图像编辑器中打开显示图像属性的图像。如果您只有Word,请将图像拖放到文档上。上面例子的图像尺寸应该是1.69×1.69厘米 - 如果从文件中取出,它实际上是。如果来自.NET制作的剪贴板,则不是。

在设置图像DPI的过程中我缺少什么?

(C#或VB,无论你喜欢什么。)

3 个答案:

答案 0 :(得分:1)

设备独立位图格式包含某种DPI信息,但据我所知,它通常没有填写在剪贴板图像上。但是,如果你想使用DIB与实际读取数据的东西交换数据,那么,你可以填写它。

我已经详细介绍了通过Windows剪贴板设置和提取DIB图像的方法,方法是在这个答案中操作DIB头和数据作为裸字节数组:

A: Copying From and To Clipboard loses image transparency

代码中没有填写DPI值,但在剪贴板DIB写入函数ConvertToDib(Image image)的注释中提到了它们。查看the DIB header specs,DPI值应为Int32值,置于偏移量0x18和0x1C。这些值可能是从赋予Image函数的输入ConvertToDib对象中提取的,但这取决于您完全弄清楚。

因此,如果您只是在该代码中找到注释的biXPelsPerMeterbiYPelsPerMeter,并将实际代码放在那里以填写该数据,那应该有效:

ArrayUtils.WriteIntToByteArray(fullImage, 0x18, 4, true, (UInt32)dpiX);
ArrayUtils.WriteIntToByteArray(fullImage, 0x1C, 4, true, (UInt32)dpiY);

DPI是每英寸的点数,虽然这似乎预计每米的纯整数像素,所以如果Image对象实际上将它作为DPI,那么可能需要某种转换。

执行剪贴板粘贴时可以读取相同的索引(同样,代码在我链接的答案中),但我还没有研究如何将这些信息实际放入新的Bitmap宾语。如果你想这样做,你可能需要扩展链接的BuildImage功能。

答案 1 :(得分:1)

我找到的解决方法是创建一个临时文件并将其推送到剪贴板而不是使用SetImage:

  bitmap1.SetResolution(300, 300)
  Dim strTempFilename As String = TempDirectory & "\mytempfilename.TIF"
  bitmap1.Save(strTempFilename, Imaging.ImageFormat.Tiff)
  Dim DataObject As New DataObject()
  Dim file(0) As String
  file(0) = strTempFilename
  DataObject.SetData(DataFormats.FileDrop, True, file)
  Clipboard.SetDataObject(DataObject)

为简洁起见,我省略了所有错误检查等。这种方法的缺点是之后有一个临时文件要删除。

答案 2 :(得分:0)

编辑(2017-10-04): 这是我最初接受的答案。我将已接受的答案更改为更近期的答案,通过绕过开箱即用的流程来解决问题。

Hans Passant的解释 (不知道为什么他删除了他的答案,我即将接受它。)

复制到剪贴板的位图由DataObject.CreateCompatibleBitmap()创建 方法。对此方法的评论与您的​​问题相关:

// GDI+ returns a DIBSECTION based HBITMAP. The clipboard deals well
// only with bitmaps created using CreateCompatibleBitmap(). So, we
// convert the DIBSECTION into a compatible bitmap.

DIBSECTION通过dsBmih.biXPelsPerMeterbiYPelsPerMeter提高分辨率 CreateCompatibleBitmap个字段。一个"兼容的位图",Shared Sub CopyImageToClipboardAlongWithDpi(image As Image) Clipboard.SetImage(image) 'standard case, without DPI information '*** now with DPI information, if possible Const toolPath As String = "C:\Program Files (x86)\IrfanView\i_view32.exe" If image IsNot Nothing AndAlso IO.File.Exists(toolPath) Then Dim tempFilename As String = IO.Path.Combine(IO.Path.GetTempPath, "tempimage.png") If clsFileUtils.TryDeleteFileIfExists(tempFilename) Then image.Save(tempFilename, Imaging.ImageFormat.Png) With New Process .StartInfo.FileName = toolPath .StartInfo.Arguments = $"""{tempFilename}"" /clipcopy /killmesoftly" .Start() End With Threading.Thread.Sleep(500) clsFileUtils.TryDeleteFileIfExists(tempFilename) End If End If End Sub 创建的那种 不是,它是与视频适配器设置匹配的DDB(设备相关位图)。

当然,它不起作用。评论有点误导,"剪贴板只处理好#34;是 荒谬的是,剪贴板并没有对您存储在其中的数据类型进行说明。它是 从剪贴板粘贴的应用程序,可能与其他东西不能很好地处理 DDB。今天是否仍然如此仍然值得怀疑,但并未完全受到考验 经常。您必须尝试使用​​DataFormats.Dib,但Winforms和WPF都不支持它 代码很难得到。

引用结束。

这是我的丑陋和疯狂的临时解决方法 - 但至少有些东西正在工作

info.plist

Thanks to Irfan Skiljan for great IrfanView.

我仍然愿意提供有用的答案或建议。