2

I got stuck with a problem - I need to create a bitmap in memory, draw some text i it and save it as a BMP file and then print out the bitmap with physical printer. I can do this drawing in the dialog window context - it works fine. But when I try to do the same drawing in printer context the text doesn't appear. I really can't figure out why it is so. Please, help me guys. Thanks in advance. Here is the code:

void CMy2Dlg::OnButton1() 
{
    // TODO: Add your control notification handler code here
    CPrintDialog pd(false); 

    if (pd.DoModal()==IDOK) 
    {
        CDC PrintDC;    
        HDC hdc = pd.CreatePrinterDC();
        PrintDC.Attach(hdc);    
        DOCINFO infStru;                    
        ::ZeroMemory (&infStru, sizeof (DOCINFO));
        CString title="Print test";
        infStru.cbSize = sizeof (DOCINFO);      
        infStru.lpszDocName=title;
        infStru.lpszOutput=NULL;


        PrintDC.StartDoc(&infStru);     

        PrintDC.StartPage();

        {           
            CRect r, r2;
            CBitmap memBMP, * pOldBitmap;
            CPaintDC dc(this); 
            CDC memDC, *pDC = &memDC;
            CFont font, * pOldFont;
            int width = 2000;
            int height = 1500;
            int textwidth = 300;
            int textheight = 150;
            int oldMapMode = 0;
            int oldbkmode = 0;
            int i, j;

            LOGFONT logFont, lf;
            COLORREF oldTextColor;

            memset(&logFont, 0, sizeof(logFont));
            logFont.lfHeight = 16;
            logFont.lfWidth = 0; 
            logFont.lfEscapement = 0;
            logFont.lfOrientation = 0;
            logFont.lfWeight = FW_NORMAL; 
            logFont.lfItalic = FALSE; 
            logFont.lfUnderline = FALSE; 
            logFont.lfStrikeOut = 0; 
            logFont.lfCharSet = ANSI_CHARSET; 
            logFont.lfOutPrecision = OUT_DEFAULT_PRECIS; 
            logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS; 
            logFont.lfQuality = DEFAULT_QUALITY; 
            logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_SWISS; 
            strcpy(logFont.lfFaceName, "Arial");

            if(memDC.CreateCompatibleDC(&PrintDC)) {
                if (memBMP.CreateCompatibleBitmap(&PrintDC, width, height)) {

                    pOldBitmap = pDC->SelectObject(&memBMP);
                    pDC->FillSolidRect(0, 0, width, height, RGB(200, 200, 200));

                    oldTextColor = pDC->SetTextColor(RGB(255,0,0));
                    oldMapMode = pDC->SetMapMode(MM_LOMETRIC);
                    oldbkmode = pDC->SetBkMode(TRANSPARENT);


                    lf = logFont;
                    lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(pDC->GetSafeHdc(), LOGPIXELSY), 72);
                    //lf.lfHeight = 100;

                    font.CreateFontIndirect(&lf);
                    pOldFont = pDC->SelectObject(&font);


                    r.left = 10;
                    r.top = 10;
                    r.right = r.left + textwidth;
                    r.bottom = r.top + textheight;

                    r.top *= -1;
                    r.bottom *= -1;

                    pDC->MoveTo(r.left,  r.top);
                    pDC->LineTo(r.right, r.top);
                    pDC->LineTo(r.right, r.bottom);
                    pDC->LineTo(r.left,  r.bottom);
                    pDC->LineTo(r.left,  r.top);

                    pDC->DrawText("qwerty", &r, DT_CENTER | DT_SINGLELINE | DT_VCENTER);                    

                    pDC->SetMapMode(oldMapMode);
                    pDC->SetTextColor(oldTextColor);
                    pDC->SetBkMode(oldbkmode);

                    PrintDC.BitBlt(10, 10, width, height, pDC, 0, 0, SRCCOPY);                  

                    pDC->SelectObject(pOldBitmap);
                    pDC->SelectObject(pOldFont);                    

                    font.DeleteObject();
                    memBMP.DeleteObject();
                    pDC->DeleteDC();
                }
            }
        }

        PrintDC.EndPage();

        PrintDC.EndDoc();

        PrintDC.Detach();       
        DeleteDC(hdc);
    }
}
Peter Ruderman
  • 12,241
  • 1
  • 36
  • 58
  • 1
    Does the rectangle appear? (there are other functions to draw rectangles instead of drawing lines btw, but this is another matter) What are you trying to do with `r.top *= -1; r.bottom *= -1;`? – Constantine Georgiou Nov 29 '18 at 15:58
  • 1
    Don't put `CPaintDC` in there. Your code is going all over the place. Create a separate function `void paint(CDC *dc)` that can paint independent of print dialog and bitmap, then print that to a bitmap, then print the bitmap separately. – Barmak Shemirani Nov 29 '18 at 16:50
  • Yes, it does appear. I need negative values of vertical coordinates because of the MM_LOMETRIC map mode used (https://msdn.microsoft.com/library/715b3334-cb2b-4c9c-8067-02eb7c66c8b2.aspx#cdc__setmapmode - Positive x is to the right; positive y is up) – user10723347 Nov 29 '18 at 19:19
  • I know the code is a mess but it doesn't bother me now, its just a quick and dirty experiment. But thankee for your comment – user10723347 Nov 29 '18 at 19:21
  • Changing map mode is usually done when drawing on printer dc. But you are using `SetMapMode(MM_LOMETRIC)` on memory DC, that's going to make it more complicated when it comes to `BitBlt` that memory DC on to display/printer DC. – Barmak Shemirani Nov 29 '18 at 23:27
  • It doesn't work without MM_LOMERIC (MM_TEXT by default) which was added just because I had had no other idea about the cause of the problem. It doesn't work with virtual printer DoPDF either so it can be easily reproduced on any computer. It looks quite weird for me and I'd like to find the root ot the problem. Could anybody build the example and glance over it in debugger please? – user10723347 Nov 30 '18 at 05:25
  • My final goal is to get the resulting bitmap with the text drawn in it. I can draw the text directly on printer context and it works fine but as I understand the printer context is readonly and I can't copy the bitmap from it - all the attempts to copy (BitBlt) the resulting bitmap finished with solid black field in memery bitmap. Is there any way to get what I need? – user10723347 Nov 30 '18 at 05:30

1 Answers1

2

If SetMapMode(MM_LOMETRIC) is used on memory DC, then memory DC has to be drawn upside down when using BitBlt to copy to printer/display DC. The width/height will have to be adjusted as well. Just use the default map mode (MM_TEXT). Use SetMapMode(MM_LOMETRIC) when you are drawing on printer DC and you want that specific measurement units.

void CMy2Dlg::OnButton1() 
{
    //create the bitmap
    int w = 600, h = 400;
    CClientDC dc(this);
    CBitmap bmp;
    CDC memdc;
    memdc.CreateCompatibleDC(&dc);
    bmp.CreateCompatibleBitmap(&dc, w, h);
    auto oldbmp = memdc.SelectObject(bmp);

    //draw on bitmap
    memdc.FillSolidRect(0, 0, w, h, RGB(200, 200, 200));
    memdc.SetTextColor(RGB(255, 0, 0));
    CRect rc(0, 0, w, h);
    memdc.DrawText(L"qwerty", &rc, 0);

    dc.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);//optional: draw the bitmap on dialog

    CPrintDialog pd(false);
    if(pd.DoModal() == IDOK)
    {
        CDC PrintDC;
        HDC hdc = pd.GetPrinterDC();
        PrintDC.Attach(hdc);
        DOCINFO docinfo = { sizeof(docinfo) };
        docinfo.lpszDocName = L"Print test";
        PrintDC.StartDoc(&docinfo);
        PrintDC.StartPage();
        PrintDC.BitBlt(0, 0, w, h, &memdc, 0, 0, SRCCOPY);
        PrintDC.EndPage();
        PrintDC.EndDoc();
    }
    dc.SelectObject(oldbmp);
}
Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77