About GDI/GDI+ coordinate compatibility? About GDI/GDI+ coordinate compatibility? windows windows

About GDI/GDI+ coordinate compatibility?


Short answer: call graphics.SetPageUnit(Gdiplus::UnitPixel)

I had the same problem as https://stackoverflow.com/a/4894969/700027: when printing, the coordinates for GDI+ (Gdiplus::Graphics) did not match the coordinates from GDI (HDC).

graphics.GetPageUnit() was returning UnitDisplay. The documentation of UnitDisplay is:

Specifies display units. For example, if the display device is a monitor, then the unit is 1 pixel.

I had wrongly assumed that for a printer, UnitDisplay would use printer dots. After much struggle I finally found out that it was actually using 1/100 of inches for an unknown reason. If I use Gdiplus::UnitPixel then GDI+ coordinates are the same as GDI coordinates.


We have had the same problem.

(Background: GDI works fine for just about everything, and seems to be much faster for our spreadsheet-style displays with 1000's of cells of text that need to be rendered. However we need GDI+ for displaying .jpg's.)

GDI+ scaling seemed correct when displaying stuff on the screen. We have a Print Preview feature which uses a coordinate transformation to let the app render using printer coordinates but to appear on the screen. Everything worked fine until we sent it to an actual printer (or PDF writer) when the scaling got stuffed up.

After a man-week's worth of work (and getting a hint from you with your solution), this is our understanding:

There is a bug in GDI+ that when you call: "Graphics(HDC)" (create a Graphics object from a GDI device context), where the HDC is from a printer or software printer with e.g. 6000 x 4000 pixel resolution, then GDI+ ignores the fact that the HDC is working with this large resolution and instead it applies its own resolution of about 1000 x 800 pixels.

Therefore, your workaround is probably the correct and best solution to the problem.

Our solution is similar but a little different, on the grounds that we don't actually want any coordinate transforms:

        graphics.GetVisibleClipBounds(&rect);        double deltaY = (double)GetPrinterH()/(double)rect.Height;        double deltaX = (double)GetPrinterW()/(double)rect.Width;        x1=x1/deltaX;        x2=x2/deltaX;        y1=y1/deltaY;        y2=y2/deltaY;        graphics.DrawImage(jpeg->image, x1,y1,x2-x1,y2-y1);

These scaling factors seem to be very close to '6' on many printer drivers.


I found a workaround for the issues when printing. Note that the graphics object in the example doesn't set any world space transformations, so drawing is done directly in page space.

Setting the page units to inches, then converting coordinates to inches seems to fix the drawing issues without much additional work. Tested with display and printer DCs at different DPIs (ranging from 72 to 4000.)

Gdiplus::Graphics graphics(..);Gdiplus::RectF    rect(0.0f, 0.0f, 1.0f, 1.0f);Gdiplus::REAL     dpiX = graphics.getDpiX();Gdiplus::REAL     dpiY = graphics.getDpiY();/* Logical coordinates to inches. In this example, the window extents are   equal to the DC's DPI. You will have to convert to inches based on your   specific configuration. */rect.X      /= dpiX;rect.Y      /= dpiY;rect.Width  /= dpiX;rect.Height /= dpiY;graphics.SetPageUnit(Gdiplus::UnitInch);graphics.FillRectangle(.., rect);