Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / MFC
Article

Printing support functions

Rate me:
Please Sign up or sign in to vote.
4.93/5 (11 votes)
6 Feb 2000CPOL 242.4K   52   52
Two methods for obtaining consistent output between printers.

Introduction

Using the default properties supplied for printer output does not give consistent results. The printable region and the pixel density cause variations. If you are outputting pages for a formal report, consistent margins and font size are often required. The following functions provide a method for obtaining consistent output between printers.

Included functions are:

  1. UserPage which returns a CRect which defines a consistent printable area for each printer (Your margins must be within the printable region of all printers of course!)
  2. CreateFontSize which returns a CSize which defines the font attribute with respect to the desired point size and printer characteristics.

The remaining code shows sample usage of these functions.

//Input: pointer to device context for printer
//Input: desired margin
//Output: CRect to use for printing area
CRect CChildView::UserPage(CDC * pDC, float margin)
{
    // This function returns the area in device units to be used to
    // prints a page with a true boarder of "margin".
    //
    // You could use individual margins for each edge
    // and apply below as needed.
    //
    // Set Map Mode - We do not want device units
    // due to lack of consistency.
    // If you do not use TWIPS you will have to change
    // the scaling factor below.
    int OriginalMapMode = pDC->SetMapMode(MM_TWIPS);

    // Variable needed to store printer info.
    CSize PrintOffset,Physical,Printable;

    // This gets the Physical size of the page in Device Units
    Physical.cx = pDC->GetDeviceCaps(PHYSICALWIDTH);
    Physical.cy = pDC->GetDeviceCaps(PHYSICALHEIGHT);
    // convert to logical
    pDC->DPtoLP(&Physical);

    // This gets the offset of the printable area from the
    // top corner of the page in Device Units
    PrintOffset.cx = pDC->GetDeviceCaps(PHYSICALOFFSETX);
    PrintOffset.cy = pDC->GetDeviceCaps(PHYSICALOFFSETY);
    // convert to logical
    pDC->DPtoLP(&PrintOffset);

    // Set Page scale to TWIPS, Which is 1440 per inch,
    // Zero/Zero is the upper left corner
    // Get Printable Page Size (This is in MM!) so convert to twips.
    Printable.cx =  (int)((float)pDC->GetDeviceCaps(HORZSIZE)*56.69);
    Printable.cy = (int)((float)pDC->GetDeviceCaps(VERTSIZE)*56.69);

    // Positive X -> RIGHT
    // Positive Y -> UP
    // Ref Zero is upper left corner
    int inch = 1440; // Scaling Factor Inches to TWIPS
    int Dx1, Dx2, Dy1, Dy2; // Distance printable area is from edge of paper
    Dx1 = PrintOffset.cx;
    Dy1 = PrintOffset.cy;
    // calculate remaining borders
    Dy2 = Physical.cy-Printable.cy-Dy1;
    Dx2 = Physical.cx-Printable.cx-Dx1;
    //
    // Define the User Area's location
    CRect PageArea;
    PageArea.left = (long)(margin*inch-Dx1);
    PageArea.right = (long)(Printable.cx-margin*inch+Dx2);
    PageArea.top = (int)-(margin*inch-Dy1); // My scale is inverted for y
    PageArea.bottom = (int)-(Printable.cy-margin*inch+Dy2);
    // now put back to device units to return to the program.
    pDC->LPtoDP(&PageArea);
    //
    // return
    return PageArea;
}
// Input: pointer to device context for printer
// Input: desired point size of font (in points).
// Output: integer height to send to CreateFont function.
int CChildView::CreateFontSize(CDC *pdc, int points)
{
    // This will calculate the font size for the printer that is specified
    // by a point size.
    //
    // if points is:
    //  (-) negative uses height as value for Net Font Height
    //                                         (ie. point size)
    //  (+) positive height is Total Height plus Leading Height!
    CSize size;
    int perinch = pdc->GetDeviceCaps(LOGPIXELSY);
    size.cx = size.cy = (perinch*points)/72;
    pdc->DPtoLP(&size);
    return size.cy;
}

To use CreateFontSize, just insert the function call into the CreateFont function:

BaseFont.CreateFont( -CreateFontSize(pdc,11), 0, 0, 0, FW_MEDIUM,
    FALSE, FALSE, 0, ANSI_CHARSET,
    OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
    DEFAULT_QUALITY, DEFAULT_PITCH , "Courier New" );

The UserPage function can be used internal to your OnPrint function. Where you call it, use the returned area rather than the region found from the pDC's GetDeviceCaps function. (Usually sent in the PrintInfo data.) Or you can call it to set the region in to be passed.

void CChildView::PrintSetup(int item)
{
    // Create Standard windows dialog.
    BOOL bStdSetUpDlg = TRUE;
    // See PRINTDLG for flags to set defaults in dialog.
//    DWORD dwFlags = PD_ALLPAGES | PD_USEDEVMODECOPIES | 
         PD_NOPAGENUMS | PD_HIDEPRINTTOFILE | PD_NOSELECTION;
    DWORD dwFlags = PD_ALLPAGES;
    // Parent (may be NULL)
    CWnd *pParent = this;
    CPrintDialog MyPrintDlg(bStdSetUpDlg,dwFlags,pParent);
    // Print Info
    CPrintInfo MyPrintInfo;
    // first link with dialog so data is shared
    // Your input into min and max pages is now shared.
    MyPrintInfo.m_pPD = &MyPrintDlg;
    //
    // Get Users Answer;
    int MyAnswer;
    MyAnswer = MyPrintDlg.DoModal();
    // Allow the user to cancel
    if(MyAnswer==IDCANCEL) return;
    //
    // Get the mode the printer is in from the Print Dialog.
    // This memory block must be unlocked later.
    DEVMODE *MyPrintMode;
    MyPrintMode = MyPrintDlg.GetDevMode();
    // 
    // Create our Printer Context
    CDC MyPrintDC;
    MyPrintDC.CreateDC(MyPrintDlg.GetDriverName(), // Ignored for Printer DC's
        MyPrintDlg.GetDeviceName(), // The only required item for Printer DC's
        MyPrintDlg.GetPortName(), // Ignored for Printer DC's
        MyPrintMode); // Optional Item for Printer DC's
    //
    // Start the Document for our document
    DOCINFO MyDocInfo;
    MyDocInfo.cbSize=sizeof(DOCINFO);
    CString DocName;
    DocName.LoadString(AFX_IDS_APP_TITLE);
    MyDocInfo.lpszDocName="DocName";
    MyDocInfo.lpszOutput="";
    //
    // Start the document
    int iErr = MyPrintDC.StartDoc(&MyDocInfo);
    if(iErr < 0)
    {
        //success returns positive value
        MyPrintDC.AbortDoc();
        GlobalUnlock(MyPrintMode); // Release the print mode.
        return;
    }
    // success so set flag to printing
    MyPrintDC.m_bPrinting=TRUE;
    // Most programs us the device's printable region found with
    // MyPrintDC.GetDevicecaps(****) functions.
    // However this is not consistent between printers so -->
    // The UserPage functions calculates what margins
    // to specify so we have the
    // actual distance from the edge of the page
    // to be consistent between printers.
    CRect MyArea;
    // fixed margin in inches (you can change this)
    MyArea = UserPage(&MyPrintDC, 0.9f);
    MyPrintInfo.m_rectDraw.SetRect(MyArea.left, 
            MyArea.top,MyArea.right,MyArea.bottom);
    //
    // We are now into personal preferences based on your program needs.
    //
    // We can call OnBeginPrinting and OnEndPrinting functions
    // to initialize and clean up
    // and loop through calls to OnPrint
    // (calling Startpage and EndPage functions)
    //
    // or as I have done here->
    // Call the StartPage the first time EndPage at the end
    // with the print fnuction handling the begin
    // and end when needed internally.
    //
    // Start the page. (This allways sets the DC to device units!) 
    MyPrintDC.StartPage();
    // Set mode.
    MyPrintDC.SetMapMode(MM_TEXT);
    //
    // We are now ready to print our data. Switch to the options allowed.
    // For our usage we will end and restart
    // each page in the functions called
    // based on the location of the current print location on the page.
    //
    // Internal to the fucntions we need to call:
    //     pdc->EndPage();
    //    pdc->StartPage(); // Returns in Device units
    //    pdc->SetMapMode(MM_LOENGLISH); // Reset to our desired mode
    //  Reset position to draw, etc....
    // as needed.
    //
    switch(item)
    {
    case(1):
        PrintLoose(&MyPrintDC,MyArea);
        break;
    case(2):
        PrintRecord(&MyPrintDC,MyArea);
        break;
    }
    // We are all done. Clean up
    MyPrintDC.m_bPrinting=FALSE;
    // end last page
    MyPrintDC.EndPage();
    // end the document
    MyPrintDC.EndDoc();
    // Release the device context
    GlobalUnlock(MyPrintMode); // Release the print mode.
    return;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired
United States United States
Began programming in 1968 on a Wang 720. Move to Fortran and began developing FEM (finite element model) applications on an IBM 360 in 1973. Developed custom FEM editors for most of my career until 1995.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey18-Feb-12 0:09
professionalManoj Kumar Choubey18-Feb-12 0:09 
Generalprinter service on C#.net Pin
balu1234530-Jul-08 7:39
balu1234530-Jul-08 7:39 
GeneralNeed Help Pin
cs0248013-Sep-07 22:23
cs0248013-Sep-07 22:23 
QuestionCan MFC Dialog base support printing Pin
wangsus14-Jun-06 8:23
wangsus14-Jun-06 8:23 
Hi,

We are going to create a dialog based application. We want to print something from dialog. Can MFC standard print ability to support this printing? Anyone can help me for that?

Thanks,

Susan
GeneralPrint Pin
Seu_why28-Mar-06 16:01
Seu_why28-Mar-06 16:01 
Generalprint Pin
Member 21784995-Sep-05 0:50
Member 21784995-Sep-05 0:50 
GeneralPrinter Consistence in Visual Basic Pin
e_screw21-Jul-05 3:33
e_screw21-Jul-05 3:33 
GeneralCrashes when destructing Pin
panzerdivisionmarkus14-Jul-05 20:51
panzerdivisionmarkus14-Jul-05 20:51 
GeneralRe: Crashes when destructing Pin
Michael A. Barnhart15-Jul-05 0:05
Michael A. Barnhart15-Jul-05 0:05 
GeneralRe: Crashes when destructing Pin
panzerdivisionmarkus15-Jul-05 1:54
panzerdivisionmarkus15-Jul-05 1:54 
GeneralIntercepting a print job Pin
AlexEvans2-Jul-05 16:40
AlexEvans2-Jul-05 16:40 
GeneralRe: Intercepting a print job Pin
Michael A. Barnhart3-Jul-05 1:00
Michael A. Barnhart3-Jul-05 1:00 
GeneralRestore to original MapMode Pin
David_Leikis26-May-05 6:07
David_Leikis26-May-05 6:07 
GeneralRe: Restore to original MapMode Pin
Michael A. Barnhart26-May-05 10:52
Michael A. Barnhart26-May-05 10:52 
QuestionHow to change the size of a print page? Pin
zhang lu22-Apr-05 23:15
zhang lu22-Apr-05 23:15 
AnswerRe: How to change the size of a print page? Pin
Michael A. Barnhart22-Apr-05 23:56
Michael A. Barnhart22-Apr-05 23:56 
QuestionHow to print the image keeping it's original size without considering the size of page, Pin
MasterFan20-Oct-04 22:48
MasterFan20-Oct-04 22:48 
Generalline thickness Pin
ssoft24-Sep-03 19:36
ssoft24-Sep-03 19:36 
Questionhow to get a printer job is color or not? Pin
tw_vincent18-Jul-03 6:33
tw_vincent18-Jul-03 6:33 
GeneralExcellent Code, Help Wanted... Pin
asharveyuk9-Feb-03 6:35
asharveyuk9-Feb-03 6:35 
GeneralRe: Excellent Code, Help Wanted... Pin
Michael A. Barnhart9-Feb-03 6:59
Michael A. Barnhart9-Feb-03 6:59 
GeneralStartDoc() not working for Release Pin
lucky76020-Jan-03 12:02
lucky76020-Jan-03 12:02 
GeneralRe: StartDoc() not working for Release Pin
Michael A. Barnhart20-Jan-03 12:18
Michael A. Barnhart20-Jan-03 12:18 
Generalquestion about CPrintDialog and DEVMODE Structure Pin
lavocat2-Dec-02 0:20
lavocat2-Dec-02 0:20 
GeneralRe: question about CPrintDialog and DEVMODE Structure Pin
Michael A. Barnhart2-Dec-02 12:40
Michael A. Barnhart2-Dec-02 12:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.