Click here to Skip to main content
15,879,095 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.8K   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

 
GeneralRe: question about CPrintDialog and DEVMODE Structure Pin
lavocat2-Dec-02 21:41
lavocat2-Dec-02 21:41 
GeneralRe: question about CPrintDialog and DEVMODE Structure Pin
Michael A. Barnhart3-Dec-02 0:26
Michael A. Barnhart3-Dec-02 0:26 
GeneralPageArea Calc Pin
BrCD26-Nov-02 10:22
BrCD26-Nov-02 10:22 
GeneralRe: PageArea Calc Pin
Michael A. Barnhart26-Nov-02 13:17
Michael A. Barnhart26-Nov-02 13:17 
GeneralRe: PageArea Calc Pin
BrCD26-Nov-02 18:48
BrCD26-Nov-02 18:48 
GeneralRe: PageArea Calc Pin
Michael A. Barnhart27-Nov-02 0:11
Michael A. Barnhart27-Nov-02 0:11 
GeneralRe: PageArea Calc Pin
BrCD27-Nov-02 6:04
BrCD27-Nov-02 6:04 
GeneralRe: PageArea Calc Pin
Michael A. Barnhart27-Nov-02 12:59
Michael A. Barnhart27-Nov-02 12:59 
GeneralInkjet printers give dpi different Pin
Richard Jones20-Nov-02 4:36
Richard Jones20-Nov-02 4:36 
GeneralRe: Inkjet printers give dpi different Pin
Michael A. Barnhart20-Nov-02 13:27
Michael A. Barnhart20-Nov-02 13:27 
GeneralRe: Inkjet printers give dpi different Pin
Richard Jones22-Nov-02 2:25
Richard Jones22-Nov-02 2:25 
GeneralProb getting color setting from DEVMODE Pin
jcn12319-Apr-02 8:45
jcn12319-Apr-02 8:45 
GeneralRe: Prob getting color setting from DEVMODE Pin
i.Wahn25-Jul-02 21:32
i.Wahn25-Jul-02 21:32 
GeneralTwo small problems Pin
17-Jul-01 1:11
suss17-Jul-01 1:11 
GeneralRe: Two small problems Pin
Michael A. Barnhart17-Jul-01 13:33
Michael A. Barnhart17-Jul-01 13:33 
GeneralRe: Two small problems Pin
Duncan123452-Apr-03 1:57
Duncan123452-Apr-03 1:57 
GeneralThe same, but slightly different Pin
21-Feb-01 0:29
suss21-Feb-01 0:29 
GeneralGood Pin
18-Feb-01 21:17
suss18-Feb-01 21:17 
GeneralRe: Good Pin
Michael A. Barnhart19-Feb-01 12:10
Michael A. Barnhart19-Feb-01 12:10 
GeneralPost Script Printer Pin
navid_ahsan4-Dec-00 16:59
navid_ahsan4-Dec-00 16:59 
GeneralIn millimeters !?!?! Pin
Luc Bergeron21-Sep-00 10:46
Luc Bergeron21-Sep-00 10:46 
GeneralRe: In millimeters !?!?! Pin
Michael A. Barnhart22-Sep-00 12:18
Michael A. Barnhart22-Sep-00 12:18 
GeneralPrinter Differences Pin
Sujay Ghosh16-Jul-00 22:51
Sujay Ghosh16-Jul-00 22:51 
GeneralRe: Printer Differences Pin
Member 1031214817-Jul-00 3:03
Member 1031214817-Jul-00 3:03 
GeneralMissing Line in UserPage Pin
Member 103121489-Feb-00 6:14
Member 103121489-Feb-00 6:14 

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.