Search code examples
c#asp.net-web-apigdiwmf

What governs DC scaling?


This code gets different scaling depending on which computer I run it on.

        Metafile image;
        IntPtr dib;
        var memoryHdc = Win32Utils.CreateMemoryHdc(IntPtr.Zero, 1, 1, out dib);
        try
        {
            image = new Metafile(memoryHdc, EmfType.EmfOnly);

            using (var g = Graphics.FromImage(image))
            {
                Render(g, html, left, top, maxWidth, cssData, stylesheetLoad, imageLoad);
            }
        }
        finally
        {
            Win32Utils.ReleaseMemoryHdc(memoryHdc, dib);
        }

Going into the Render method, the Metafile object has a PixelFormat of DontCare and consequently does not have valid vertical or horizontal resolutions.

Coming out of the Render method, it has a value of Format32bppRgb and PhysicalDimension.Width and PhysicalDimension.Height have increased to accommodate the rendered image.

How can I make scaling independent of local settings?

Here's the implementation of CreateMemoryHdc (I didn't write it, it's from an OSS library).

    public static IntPtr CreateMemoryHdc(IntPtr hdc, int width, int height, out IntPtr dib)
    {
        // Create a memory DC so we can work off-screen
        IntPtr memoryHdc = CreateCompatibleDC(hdc);
        SetBkMode(memoryHdc, 1);

        // Create a device-independent bitmap and select it into our DC
        var info = new BitMapInfo();
        info.biSize = Marshal.SizeOf(info);
        info.biWidth = width;
        info.biHeight = -height;
        info.biPlanes = 1;
        info.biBitCount = 32;
        info.biCompression = 0; // BI_RGB
        IntPtr ppvBits;
        dib = CreateDIBSection(hdc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
        SelectObject(memoryHdc, dib);

        return memoryHdc;
    }

As you can see, the width, height and bit depth passed to the DC constructor are constant. Creating the metafile produces different physical dimensions. Right after executing this

            image = new Metafile(memoryHdc, EmfType.EmfOnly);

the metafile has PhysicalDimension.Height (and width) of 26.43 on my workstation and 31.25 on the server to which I am deploying, so the difference in scaling is already evident and therefore probably not a consequence of anything in the rendering.

This may be relevant. BitMapInfo is defined in the OSS library and looks like this:

internal struct BitMapInfo
{
    public int biSize;
    public int biWidth;
    public int biHeight;
    public short biPlanes;
    public short biBitCount;
    public int biCompression;
    public int biSizeImage;
    public int biXPelsPerMeter;
    public int biYPelsPerMeter;
    public int biClrUsed;
    public int biClrImportant;
    public byte bmiColors_rgbBlue;
    public byte bmiColors_rgbGreen;
    public byte bmiColors_rgbRed;
    public byte bmiColors_rgbReserved;
}

so possibly setting biXPelsPerMeter and biYPelsPerMeter will help. The above code doesn't set them and may be allowing platform values.

Unfortunately, setting these values doesn't seem to make any difference. msdn says

biXPelsPerMeter

The horizontal resolution, in pixels-per-meter, of the target device for the bitmap. An application can use this value to select a bitmap from a resource group that best matches the characteristics of the current device.

So these settings are used when loading a bitmap from a resource. No help here.

This all looks pertinent https://www.codeproject.com/articles/177394/%2fArticles%2f177394%2fWorking-with-Metafile-Images-in-NET

It may help to know that this code does not run in an application. It renders HTML as a metafile for printing, and it lives inside a Web API webservice.

There is no user interface so I'm not sure how to interpret the question of whether it is DPI Aware. The evidence suggests it's DPI affected so the question may be pertinent.


Solution

  • GDI doesn't scale. Use GDI+ for device independence. You will lose antialiasing but most print devices are high DPI anyway.

    Does the library in use have an option to use GDI+ instead?

    (In my own case, yes. Problem solved.)