65.9K
CodeProject is changing. Read more.
Home

Solution to the preview problem while playing a metafile in a CPreviewDC

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.14/5 (4 votes)

Jan 26, 2005

CPOL

1 min read

viewsIcon

32996

Playing a Metfile into a CPreviewDC in print preview can cause further drawing operations to not work correctly.

Metafile played direct into preview DC

Metafile played into a bitmap and then rendered using StretchDIBits()

Introduction

I was checking into a print preview problem in one of our applications, where if we tried to render any clipboard output to the print preview screen, further drawing operations (but not all) on the same page would not work correctly. Typically, this would be seen as font text drawn at an incorrect angle or for clip regions in use not to work correctly, so that the was not be clipped.

I present in the demo code a way around this problem.

You can try running the demo application which demonstrates the problem. Its OnPrint() function looks like this:

void CPreviewMetafileView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
    CString text;
    CSize size;
    if (!IsClipboardFormatAvailable(CF_METAFILEPICT))
    {
        CString text("No METAFILE on clipboard");
        pDC->TextOut(0, 0, text);
    }

    CButton *pButton = static_cast<CButton*>(GetDlgItem(IDC_PREVIEW_DIRECT));
    bool previewDirect = (pButton->GetCheck() != 0);

    // rectangle to render the metafil into
    CRect metafileRect(pInfo->m_rectDraw.Width() / 2, 
        pInfo->m_rectDraw.top, 
        pInfo->m_rectDraw.right, 
        pInfo->m_rectDraw.Height() / 2);

    if (IsClipboardFormatAvailable(CF_METAFILEPICT))
    {
        if (previewDirect)
        {
            pDC->SaveDC();
            // play a metafile from the clipboard if available
            GLOBALHANDLE    hGMem;
            LPMETAFILEPICT  lpMFP;
            OpenClipboard();
            hGMem = GetClipboardData(CF_METAFILEPICT);
            lpMFP = (LPMETAFILEPICT)GlobalLock(hGMem);
            pDC->SaveDC();
            pDC->SetMapMode(lpMFP->mm);
            pDC->SetViewportExt(metafileRect.Width(), metafileRect.Height());
            pDC->SetViewportOrg(metafileRect.left, metafileRect.top);
            pDC->PlayMetaFile(lpMFP->hMF);
            VERIFY(pDC->RestoreDC(-1));
            GlobalUnlock(hGMem);
            CloseClipboard();
            pDC->RestoreDC(-1);
        }
        else
        {
            // render to offscreen bitmap and StretchDIBBits it
            DrawMetafileToDc(pDC, metafileRect);
        }
    }
    // the following output gets corrupted after a direct render
    text = "Text to be corrupted!";
    size = pDC->GetTextExtent(text);
    pDC->TextOut(0, size.cy, text);
    pDC->TextOut(0, size.cy * 2, text);
    pDC->TextOut(0, size.cy * 3, text);
}

As you can see, it checks to see if a metafile is available on the clipboard and renders it to the preview DC using either a direct render or an indirect render. It then adds some extra text to the output "Text to be corrupted". This text is not shown on the direct render but is on the indirect render. Although the way this corruption affects the output may be different depending on what you actually render after the metafile.

To get a metafile on the clipboard, a quick way is to copy some cells to the clipboard from Microsoft Excel.

The two functions you will be interested in the demo which you wish to use this work around are:

HANDLE CPreviewMetafileView::DDBToDIB(CBitmap& bitmap, 
                              DWORD dwCompression, CPalette* pPal)
void CPreviewMetafileView::DrawMetafileToDc(CDC * pDC, CRect targetRect)

Thats it!