Reputation: 11
My windows c++ program creates EMFs (Enhanced Metafile Format) to export to clipboard, word and excel.
The following example code generates an EMF rectangle (width=height=25) that is only 12x12 while the canvas is 25x25 (note: screen resolution of my laptop is3600x1800). At other screen resolutions, similar scaling anomalies occur (too big/too small). It would appear the scaling of the graphics-drawing needs to be set as a function of the resolution. I clearly have a gap in my knowledge here...any help is appreciated.
HDC ref_dc = GetDC(NULL);
Rect r(0, 0, 25, 25);
Metafile* emf = new Metafile(ref_dc, r, MetafileFrameUnitPixel, EmfTypeEmfPlusDual, L"Drawing");//to HDC
Graphics* g = new Graphics(emf);
//draw a simple box
Gdiplus::Pen* pen = new Pen(Color(0, 255, 0), 1.0f);
pen->SetDashStyle(DashStyleSolid);
pen->SetLineCap(LineCapRound, LineCapRound, DashCapFlat);
g->DrawRectangle(pen, r); // DrawMyObject(g);
// code here to put on clipboard
Upvotes: 1
Views: 2195
Reputation: 11
Self Answer:
The Graphics is created with a 'new' because it must be destroyed before the EMF is 'written'. Else the emf does not 'record' the steps performed in by the Graphics object. Perhaps we should put an extra set of braces, but that would be equally awkward. According to the documentation: "The recording ends when the Graphics object is deleted or goes out of scope." Hence the explicit new(s) and delete(s). The EMF get pushed to the clipboard post destruction of Graphics and prior to destruction of EMF.
*The mistake/problem appears to be caused by using the constructor for the EMF that includes Rect struct as an argument. The Rect dimensions appears to have no discernible relationship with graphcs object coordinates used for drawing, and thus it crops the resultant EMF in unpredictable ways. Using a constructor with only an HDC or an HDC and a filename solves this problem, at least in my hands.
Last, adding the code lines
REAL xScale = mfh->GetDpiX() / g.GetDpiX(); REAL yScale = mfh->GetDpiY() / g.GetDpiY(); g.ScaleTransform(xScale, yScale);
provides a base scaling such that the emfs are more or less similar size regardless of screen resolutions. This is quite useful to give a consistent/reasonable default size for exports on the user side.
Upvotes: 0
Reputation: 244732
Although unrelated to your actual problem, I feel very compelled to point out that your programming style of creating all the objects on the heap is quite odd. There is no reason to use new
like this. Just create temporary objects on the stack. This keeps you from leaking memory and other resources like a sieve. By way of illustration, all you need is this:
// Create Graphics object
Graphics g(emf);
// Draw a simple box
Gdiplus::Pen pen(Color(0, 255, 0), 1.0f);
pen.SetDashStyle(DashStyleSolid);
pen.SetLineCap(LineCapRound, LineCapRound, DashCapFlat);
g.DrawRectangle(pen, r);
// g and pen automatically go out of scope here, implicitly calling the destructor
// and freeing their resources. No need to call delete.
As for your actual question, metafiles don't have a fixed size. They basically just encapsulate a series of GDI drawing instructions that can be recapitulated at will.
The normal way to put an enhanced metafile on the clipboard would be to call SetClipboardData
function, using the CF_ENHMETAFILE
format. The handle type would obviously then be HENHMETAFILE
. You'll need to get GDI+ to give you one of those, probably by using the Metafile::GetHENHMETAFILE
method after you've finished loading/creating your metafile object.
The scaling/sizing is the responsibility of the client code, the one that receives your metafile from the clipboard and tries to display it. The metafile header contains an entry that specifies its horizontal and vertical resolution. That can then be scaled in terms of the display DPI. In GDI+, something like:
Graphics g(...);
Metafile mf(L"MyFile.emf");
MetafileHeader mfh;
mf->GetMetafileHeader(&mfh);
REAL xScale = mfh->GetDpiX() / g.GetDpiX();
REAL yScale = mfh->GetDpiY() / g.GetDpiY();
g.ScaleTransform(xScale, yScale);
Upvotes: 1