Printing to a Printer DC with MFC

2019-07-14 13:44发布

I've followed Microsoft's tutorial on creating a Device Context, and I've tried looking around the internet for a decent source (apparently, MFC is a mystical thing). The following successfully prints out "Hello, World!"; except it's extremely tiny.

How can I send a CImage to the printer, rather than text? And how could I get the text's size to be bigger than a couple millimeters? I've scoured MSDN, but everything is either outdated (like the example code I am using), or just not well documented.

 // get the default printer
  CPrintDialog dlg(FALSE);
  dlg.GetDefaults();

  // is a default printer set up?
  HDC hdcPrinter = dlg.GetPrinterDC();
  if (hdcPrinter == NULL)
  {
    //MessageBox(_T("Buy a printer!"));
  }
  else
  {
    // create a CDC and attach it to the default printer
    CDC dcPrinter;
    dcPrinter.Attach(hdcPrinter);

    // call StartDoc() to begin printing
    DOCINFO docinfo;
    memset(&docinfo, 0, sizeof(docinfo));
    docinfo.cbSize = sizeof(docinfo);
    docinfo.lpszDocName = _T("CDC::StartDoc() Code Fragment");

    // if it fails, complain and exit gracefully
    if (dcPrinter.StartDoc(&docinfo) < 0)
    {
      //MessageBox(_T("Printer wouldn't initalize"));
    }
    else
    {
      // start a page
      if (dcPrinter.StartPage() < 0)
      {
        //MessageBox(_T("Could not start page"));
        dcPrinter.AbortDoc();
      }
      else
      {
        // actually do some printing
        //CGdiObject* pOldFont = dcPrinter.SelectStockObject(SYSTEM_FONT);

        dcPrinter.SetMapMode(MM_HIENGLISH);
      auto font = CreateFont(
      3'000,                        // nHeight
      1'500,                         // nWidth
      0,                         // nEscapement
      0,                         // nOrientation
      FW_NORMAL,                 // nWeight
      FALSE,                     // bItalic
      FALSE,                     // bUnderline
      0,                         // cStrikeOut
      ANSI_CHARSET,              // nCharSet
      OUT_DEFAULT_PRECIS,        // nOutPrecision
      CLIP_DEFAULT_PRECIS,       // nClipPrecision
      DEFAULT_QUALITY,           // nQuality
      DEFAULT_PITCH | FF_SWISS,  // nPitchAndFamily
      _T("Arial"));                 // lpszFacename
      dcPrinter.SelectObject(&font);
        dcPrinter.TextOut(450, 450, _T("Hello World!"), 12);
        dcPrinter.EndPage();
        dcPrinter.EndDoc();
        //dcPrinter.SelectObject(pOldFont);
      }
    }
  }

标签: c++ printing mfc
4条回答
叼着烟拽天下
2楼-- · 2019-07-14 14:14

Use CFont::CreatePointFont() or CFont::CreatePointFontIndirect() to create a font that is reasonable. Most printers are 600 DPI. Most screens are 96 DPI. A 12 point font on the screen is basically a 2 point font and illegible on a printer.

Create the font and select it into your DC. Do not forget to select it out of the DC after using it and before destroying your DC (CDC class). (The CDC destructor automatically deletes the HDC).

查看更多
Juvenile、少年°
3楼-- · 2019-07-14 14:15

I have used the following to successfully print "Hello World!" and "Have a Nice Day!" to the right of a 200x200 monochrome bitmap (MyLogo.bmp) placed at the origin of the printer page (I am using a black & white thermal printer):

CDC printDC( GetMyPrintDC() );    // e.g. as per original code
DOCINFO di( GetMyDocInfo() );     // e.g. as per original code
printDC.StartDoc( &di );
  ATL::CImage logo;
  logo.Load( "MyLogo.bmp" );
  const BOOL result( logo.Draw( printDC.GetSafeHdc(), CPoint( 0, 0 ) ) );
CFont myFont, *old;
myFont.CreatePointFont(100, "Courier New", &printDC);
old = printDC.SelectObject(&myFont);
printDC.TextOut( 250, 50, "   Hello World!" );
printDC.TextOut( 250, 150, "Have a nice Day!" );
printDC.SelectObject( old );
myFont.DeleteObject();
printDC.EndPage();
printDC.EndDoc();
printDC.DeleteDC();

The three indented lines highlight all that is required to render a CImage on my printer. Vary the parameter in CreatePointFont() to size the (otherwise tiny) text to suit.

查看更多
在下西门庆
4楼-- · 2019-07-14 14:19

Here is the problem:

dcPrinter.SetMapMode(MM_TEXT);

MM_TEXT maps one logical point to one device point; considering typical resolution of 600 DPI for a printer, your stuff will be few times smaller that on the screen.

Use MM_HIENGLISH or some other device-independent mode; here is MSDN link.

查看更多
女痞
5楼-- · 2019-07-14 14:40

Tiny Text Problem

The problem is that, by default, the size of a font is specified in device-dependent units and printers are generally much higher resolution that a screen. So if you've created a font that is 20 pixels high on the screen (which might have 96 pixels per inch) when you try to use that font on a printer, which maybe has 300 or 600 dots per inch, your text looks really small.

As another answer shows, one idea is to change the mapping mode so that the printer uses units that are closer to what is on the screen.

An alternative way is to create a new font with an appropriate size (the lfHeight field in the LOGFONT structure) based on the DPI of the printer, which you can determine with the GetDeviceCaps function. This can be handy if you want a particular font size, like 14 point text.

LOGFONT lf = {0};
lf.lfHeight = -MulDiv(point_size, ::GetDeviceCaps(dcPrinter, LOGPIXELSY), 72);
// init other field of lf as you like
HFONT hfontPrinter = ::CreateFontIndirect(&lf);
HFONT hfontOld = ::SelectObject(hdcPrinter, hfontPrinter);

// now draw to the hdcPrinter

::SelectObject(hdcPrinter, hfontOld);
::DeleteObject(hfontPrinter);

Sending a CImage

I don't use MFC, but it looks like you can just call CImage::StretchBlt with the printer DC. Once again, you'll probably have to take the printer's much higher resolution into account when you choose the target coordinates.

查看更多
登录 后发表回答