It looks like Bitmap.SetResolution() has no effect on clipboard, see the following trivial code:
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
The file contains correctly set image DPI. In the clipbaord, it is not present.
Proof: In clipboard dump (image inserted into clipboard by IrfanView), see DIB bitmap header of 60 × 120
DPI (yellow=horizontal DPI, green=vertical DPI):
But after inserting image using .net's Clipboard.SetImage()
, both these numbers are 0
.
My goal: be able to paste image into Microsoft Word with proper size (taken from DPI and dimensions). Without DPI set in the clipboard, the image is too big after pasting. But it contains barcode already with 1 bar = 1 px resolution, so I cannot sample it down.
How to verify DPI: Either by clipboard viewer OR by opening the image in image editor which shows image properties. If you have only Word, drag&drop the image over the document. Image size of the above example should have been 1.69×1.69 cm – and if taken from file, it actually is. If from .NET-made clipboard, it isn't.
What I am missing in process of setting the image DPI?
(C# or VB, whatever you prefer.)
The Device Independent Bitmap format contains some sort of DPI information, though from what I've seen it is generally not filled in on clipboard images. But if you want to use DIB to exchange data with something that actually reads that, then, sure, you can just fill it in.
I have detailed the ways to both set and extract DIB images through the Windows clipboard by manipulating the DIB header and data as bare bytes array in this answer:
A: Copying From and To Clipboard loses image transparency
The DPI values are not filled in in the code, but they are mentioned in comment in the clipboard DIB writing function ConvertToDib(Image image)
. Looking at the DIB header specs, the DPI values should be Int32 values put on offsets 0x18 and 0x1C. These values can probably be extracted from the input Image
object given to the ConvertToDib
function, but that'll be up to you to figure out exactly.
So if you just find the commented biXPelsPerMeter
and biYPelsPerMeter
mentions in that code and put the actual code there to fill in that data, that should work:
ArrayUtils.WriteIntToByteArray(fullImage, 0x18, 4, true, (UInt32)dpiX);
ArrayUtils.WriteIntToByteArray(fullImage, 0x1C, 4, true, (UInt32)dpiY);
DPI is dots per inch, though, while this seems to expect pure-integer pixels per meter, so if the Image
object actually has it as DPI, some kind of conversion may be required there.
The same indices can be read when performing a clipboard paste (again, the code is in the answer I linked), though I haven't looked into how to actually put that information into the new Bitmap
object. You'd probably have to expand the linked BuildImage
function if you want to do that.