1

I have a couple of bitmaps/pngs (screenshots), which I would like to "wrap" in EMFs having a specific width in millimeters (and a proportional height). Ideally, I would also want to add some overlays (text & rectangles).

Creating and saving an EMF seems to generally work fine, but I am unable to get any of the scaling right.

Here is my code:

var sourceBitmap = Image.FromFile(@"C:\temp\example.png");
Metafile metafile;
var aspectRatio = (float)sourceBitmap.Height / (float)sourceBitmap.Width;
var size = new Size(200, (int) (200*aspectRatio));
using (var stream = new MemoryStream())
{
    using (var offScreenBufferGraphics = Graphics.FromHwndInternal(IntPtr.Zero))
    {
        IntPtr deviceContextHandle = offScreenBufferGraphics.GetHdc();
        metafile = new Metafile(stream, deviceContextHandle, new RectangleF(0, 0, size.Width, size.Height), MetafileFrameUnit.Millimeter, EmfType.EmfPlusOnly); // this allocates one gdi object
        offScreenBufferGraphics.ReleaseHdc();
        using (var graphics = Graphics.FromImage(metafile))
        {
            graphics.PageUnit = GraphicsUnit.Millimeter;

            // may need to do something with ScaleTransform here??
            var metafileHeader = metafile.GetMetafileHeader();
            float sx = metafileHeader.DpiX / graphics.DpiX;
            float sy = metafileHeader.DpiY / graphics.DpiY;
            graphics.ScaleTransform(sx, sy);

            graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), new Rectangle(0, 0, size.Width, size.Height), GraphicsUnit.Pixel);

            var pen1 = new Pen(new SolidBrush(Color.Blue));
            var pen2 = new Pen(new SolidBrush(Color.Red));
            graphics.DrawRectangle(pen1, 0, 0, size.Width, size.Height);
            graphics.DrawLine(pen2, 0, 0, size.Width, size.Height);
            graphics.DrawLine(pen2, 0, size.Height, size.Width, 0);

            graphics.Dispose();
        }
    }
}


// save as a metafile
IntPtr metafileHandle = metafile.GetHenhmetafile();
var result = Gdi32.CopyEnhMetaFile(metafileHandle, @"C:\temp\example.emf");
if (result.ToInt32() == 0)
{
    var error = Marshal.GetLastWin32Error();
    throw new Win32Exception(error);
}
Gdi32.DeleteEnhMetaFile(result);
Gdi32.DeleteEnhMetaFile(metafileHandle);

Neither are the rectangle and diagonal lines correct, nor is the source bitmap correctly filled into the EMF. What am I doing wrong here?

The final EMF should be embedded into a Word document - hence the specific width. Note that I do not want to downsize the bitmaps and include these directly, since I would likely loose quality this way. Also note that I do not want to include the full bitmap in Word and have it adjust/scale the image, as I need to use {includepicture} which does not seem to reliably work with scaling.

user1211286
  • 681
  • 5
  • 17
  • graphics.DrawImage will honor the bimap's dpi. - Maybe setting the dpi of the sourceBitmap will help. By default it will be the screen dpi, which may be quite off. Use Bitmap.SetDpi ! – TaW Apr 23 '20 at 08:13

1 Answers1

1

I tried your code, the rectangle and diagonal lines are correct.

After changed(swaped param destRect and srcRect) the following code

graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), new Rectangle(0, 0, size.Width, size.Height), GraphicsUnit.Pixel);

to

graphics.DrawImage(sourceBitmap, new Rectangle(0, 0, size.Width, size.Height), new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), GraphicsUnit.Pixel);

The source bitmap correctly filled into the EMF.

Dallon Xu
  • 80
  • 5