Link to home
Start Free TrialLog in
Avatar of qocarlos
qocarlosFlag for Saint Kitts and Nevis

asked on

Help on metafiles

Hi all!

I have programmed an application that display 1D and 2D plots. In this application I'm using MM_TWIPS mapping mode to get a WYSIWYG interface with zooming capabilities. All this works just fine.
Now I want to copy the graphics to the clipboard as an enhanced metafiles but until now I wasn't able to get it to work correctly. The following is the code I'm using, pretty simple:

void CMyView::OnCopyMetafile()
{

     CMetaFileDC * m_pMetaDC = new CMetaFileDC();
     //draw meta file
     CClientDC clientDC(this);
     OnPrepareDC(&clientDC);
     m_pMetaDC->CreateEnhanced(&clientDC,NULL,NULL,"whatever");
     
     
     m_pMetaDC->m_hAttribDC = clientDC.m_hDC;

     
     OnDraw(m_pMetaDC);
     
     //close meta file dc and prepare for clipboard;
     HENHMETAFILE hMF = m_pMetaDC->CloseEnhanced();

     //copy to clipboard
     OpenClipboard();
     EmptyClipboard();
     ::SetClipboardData(CF_ENHMETAFILE,hMF);
     CloseClipboard();
     //DeleteMetaFile(hMF);
     delete m_pMetaDC;
     
}

This code used to work properly in older versions of my application in which I used MM_TEXT mapping mode. However, with MM_TWIPS, after pasting the metafile into MS Word, I get an inversed metafile along the Y-axis.
I also had to use a little trick in order the metifle DC to support GDI functions such as LPtoDP, GetClipBox and others. This trick was the line m_pMetaDC->m_hAttribDC = clientDC.m_hDC;


Any idea about how to solve this problem?

Thanks,
Carlos
Avatar of DanRollins
DanRollins
Flag of United States of America image

I hae not worked with MM_TWIPS, but...

from Q180330:

A non-normalized rectangle can often be produced unintentionally if you use mapping modes other than MM_TEXT.

MM_HIENGLISH, MM_LOENGLISH, MM_HIMETRIC, MM_LOMETRIC, and MM_TWIPS cause the y-axis to have the reverse direction of that for the default mapping mode (MM_TEXT). This can result in non- normalized RECTs.

When you specify to draw the metafile, try swapping the rc.top and rc.bottom in the destination rectangle.

-- Dan
Also in CDC::SetMapMode, it explictly states:

MM_TWIPS   Each logical unit is converted to 1/20 of a point. (Because a point is 1/72 inch, a twip is 1/1440 inch.) Positive x is to the right; positive y is up.

(see the last clause of the last sentence)

-- Dan
Avatar of qocarlos

ASKER

Hi Dan,

Thanks for your comments. I've tried to swap the top and bottom in the destination rectangle but I'm getting the same result :-(
I know that in MM_TWIPS positive y is up. Everything works correctly while drawing both in the screen and in the printer but not in the metafile.
Is there any special issue regarding metafiles? I can't find anything useful in the documentation.

Thanks,
Carlos
More information ...
I'm using a printing framework from codeguru. If you want to reproduce my problem you can download a sample application from this link:

http://www.codeguru.com/printing/textbmp.shtml

(if you want to build the sample app with VC 6.0 you have to make the changes commented in http://www.codeguru.com/mfc/comments/7759.shtml)

Then you can add exactly the code I wrote in my question (CMyView::OnCopyMetafile() ) and you will see the problem I'm having ...



More information ...
I'm using a printing framework from codeguru. If you want to reproduce my problem you can download a sample application from this link:

http://www.codeguru.com/printing/textbmp.shtml

(if you want to build the sample app with VC 6.0 you have to make the changes commented in http://www.codeguru.com/mfc/comments/7759.shtml)

Then you can add exactly the code I wrote in my question (CMyView::OnCopyMetafile() ) and you will see the problem I'm having ...



I believe this question is hard, so I feel like I should increase the points.
It does not fail when I test it.

I used the ClassWizard to add an OnEditCopy() handler to the CTextbmpView class.  I then pasted your code verbatim into that function.

I then run the program and choose Edit/Copy.  When I go to Ms Word and choose Paste, the image comes out right side up.

However, it is pretty clear that this code uses MM_TEXT.  Where have you changed it in you version so that it uses MM_TWIPS?

Also, why are you using MM_TWIPS?

-- Dan
Hi Dan,

I did something wrong in the metafile code I wrote here. I forgot to include the following line:

m_pMetaDC->m_hAttribDC = clientDC.m_hDC;
m_pMetaDC->m_bPrinting = true; //I FORGOT THIS LINE

Why am I adding this line? because the metafile I want to export should be equivalent to the printer version of the view.

In all my OnDraw() functions I have code like this:

if (!pDC->IsPrinting())
{
(...)
}

The code inside this condition shouln't be either executed when the DC is a metafile.

Is there a way to know if the device context corresponds to a metafile? i.e. I'd like to do something like this:

if (!pDC->IsPrinting() && pDC->IsMetafile())
{
(...)
}


Besides this clarification, there is something I don't understand. You say that "it is pretty clear that this code uses MM_TEXT". I don't see that. AFAIK, the code uses MM_TWIPS. It onl uses MM_TEXT to BitBlt the Bitmap in the OnDraw() function. The code sets the mapping mode to MM_TWIPS in function SetScreenGlobals().

Thanks,
Carlos
Avatar of migel
migel

Hi!
did you try ajust drawing org/extents (that is call SetViewPortOrg SetWindowOrg) before calling OnDraw?
>>It onl uses MM_TEXT to BitBlt the Bitmap in the OnDraw() function.

Yes, that is why I said that.  But coudn't that be the core of the problem?

-- Dan
Hi,

If I don't use the line
m_pMetaDC->m_bPrinting = true;

what I get is a metafile which displays exactly the same contents as the view, but this is not what I want. I want to create a metafile that representes exactly the same content as the print preview.
The main problem is I don't want a metafile with the screen resolution but with the printer resolution because if the user wants to print the bitmap out from MS Word, he will get a poor resolution plot.

I tried to use create a printer device context instead of using CClientDC, but this wasn't fix the problem.

This is the new code:

void CMyView::OnCopyMetafile()
{
     CMetaFileDC * m_pMetaDC = new CMetaFileDC();
     //Get printer DC
     CDC dc;
     CPrintDialog dlg(FALSE);
     if (AfxGetApp()->GetPrinterDeviceDefaults(&dlg.m_pd))
     {
          // GetPrinterDC returns a HDC so attach it
         
          HDC hDC= dlg.CreatePrinterDC();
          ASSERT(hDC != NULL);
          dc.Attach(hDC);
     }

     
     OnPrepareDC(&dc);
     m_pMetaDC->CreateEnhanced(&dc,NULL,NULL,"whatever");
     
     
     m_pMetaDC->m_hAttribDC = dc.m_hDC;
     
     m_pMetaDC->m_bPrinting = true;
     
     OnDraw(m_pMetaDC);
     
     //close meta file dc and prepare for clipboard;
     HENHMETAFILE hMF = m_pMetaDC->CloseEnhanced();
     
     //copy to clipboard
     OpenClipboard();
     EmptyClipboard();
     ::SetClipboardData(CF_ENHMETAFILE,hMF);
     CloseClipboard();
     delete m_pMetaDC;    
}

Could you please try this code and explain me what's wrong with it?

Thanks in advance,
Carlos
I think you are on the wrong track.

Remember, you are capturing a MetaFile and not a bitmap.  That means that a drawing command such as

  pDC->RoundRect( 500, -3000, 4500, -5000, 1000, 1000 );

is recorded as a command, and not low-res or hi-res image of a rounded-corner rectangle.

There is no need to mess with the IsPrinting flag.  When a program renders that metafile into a DC, the command will be executed in a way that makes sense of the device.

So, I'm saying it works perfectly.  I have copied the metafile (without the IsPrining turned on), and pasted it into Ms Word and the result prints out very nicely.  It even shows that tiny little circle that is produced when you left-click.

But I really do understand that there is something you are seeing that makes you think that it is broken.    So...

Please explain, in the most precise, descriptive language possible, exactly what bad thing is happening.  

I might be able to figure out why it is happening, or describe how to make it look better, or explain why it is not really bad after all.

-- Dan
Hi Dan,

Sorry for not being able to explain clearly what's the problem I'm having. English is not my mother language but I'm trying to do my best ...

You say that it works perfectly. After pasing the metafile in Word and if you enlarge it (eg. make the metafile twice of big), does the result still print out nicely? In addition, I don't want to draw the paper borders and I would liket to obtain the full page in the metafile, I would like to get the same result as the print preview's (the whole page fits on the paper).

I believe the best way to explain my problem is sending the executable of my application to you so you can see exactly what's my problem. My email is qocarlos@usc.es, if you send me an email I could send my application to you.
Do you agree?
Thanks again,
Carlos





Oops, when I said "After pasing the metafile ...." I wanted to say "After pasting the metafile ..."
I sent you email.  I'll look at the project.  

>>I would like to obtain the full page in the metafile

This is the kind of thing I don't understand.  It seems to me that the metafile should contain whatever you drew.  Word will scale it up or down.  Whether it gets shunk down to 1-inch by 1-inch or expanded to fill a full page later should not make any difference.

-- Dan
Hi Dan,

Did you receive my application? Is there anything else I should explain?
Yes, I got it gocarlos.  Sorry, I've been tied up. I should be able to look at it tonight.  However, if you sent just the EXE, well, chances that I can help are about halfway between slim and none.

-- Dan
Hi Dan,
I believe it really makes no difference if I send you the whole project with the source code or just the EXE, because the problem I have can be reproduced with the Codeguru sample app. Did you try to print a metafile created from my app? Did you note that the resolution is really poort (actually you get screen resolution) ? Anyway, I could send you the full application with the source code if you believe this is important to solve my problem.

Thanks,
Carlos
Hi Dan,
Don't worry, there is no rush :-)
I believe it really makes no difference if I send you the whole project with the source code or just the EXE, because the problem I have can be reproduced with the Codeguru sample app. Did you try to print a metafile created from my app? Did you note that the resolution is really poor (actually you get screen resolution) ? Well, I could send you the full application with the source code if you believe this is important to solve my problem.

Thanks,
Carlos
ASKER CERTIFIED SOLUTION
Avatar of DanRollins
DanRollins
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks, I will try it out this weekend and let you know the results.

Carlos
Hi Dan,
I have almost fixed it. I still have some problems but I don't have the time to solve them right now. I'll be back to them later on ...
I have an additional question related to this one, can you help me out? I can increase the points here or create a new question, just let me know what you prefer.
The problem is the following. In the sample application from codeguru, how can I get the device-context of the view? i.e. I'm trying to directly draw in my view from a dialog outside of a WM_PAINT handler.

Normally I'd do it using this method:


CMyDialog::SomeFunction()
{
   CMyView* pView: GetMyView(); //This function just returns a pointer to the view I want to draw in
   CDC* pDrawDC;
   pDrawDC = pView->GetDC();
   ASSERT_VALID(pDrawDC);
   //Drawing code will go here
   pView->ReleaseDC(pDrawDC);
}

But now this doesn't work because this DC uses the default metrics mode (MM_TEXT) and I want to reuse all the page setup implemented in the view.
Is there another way?

Thanks,
Carlos
>>I'm trying to directly draw in my view from a dialog outside of a WM_PAINT handler.

This is not recommended.  Why?  Because the WM_PAINT handler won't know that you have drawn there.  For instance, let's say your CMyDialog::SomeFunction() draws a diaagonal red line across the graph.  Now you drag another app's window acrosss your own or pop up Word the pop it down, or whatever.  When your app is asked to refresh its screen, it will not draw that diagonal red line.

So the recommended way to add some lines or whatever to the view would be to, perhaps, create a fn and have OnDraw call it.  You just pass the CDC that have there.

CMyView::OnDraw( CDC* pCDC)
{
 ... all the stuff that is there...
 AddSomeOtherStuff(pCDC);
}

CMyView::AddSomeOtherStuff( CDC* pCDC)
{
  .. draw a diagnonal line, the DC is all set as needed...
}

Now I can conceive of some case where you would *want* the stuff you draw to be temporary (some sort of animation to blink the graph handles or something).  I think that you would need to just be careful to set up your DC to match the same settings as used in the OnDraw() fn

-- Dan
Hi Dan,
Thanks for your comment.
Yes, I need to do a sort of temporary draws, they don't have to persist along all the time. I found a workaround and now is working properly.

Carlos