Click here to Skip to main content
Click here to Skip to main content

Printing support functions

By , 6 Feb 2000
 

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)

About the Author

Michael A. Barnhart
Systems Engineer
United States United States
Member
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. Since then I have been focusing on improving information flow and quality with web based communications (Web Services and SOA concepts.) Mostly in an evangelist role.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membermanoj kumar choubey18 Feb '12 - 0:09 
Nice
Generalprinter service on C#.netmemberbalu1234530 Jul '08 - 7:39 
Hi,
 
Iam developing an application/service for printers.The scenario is as follows
 
1. When a user selects a document and then he says the print command(no through my application...he selects normal print in file menu)then i have to catch some details like printername,no.of pages,document name..and so on...This action should be done before it get out from spooler.
 
2. As all of you that after printing data from spooler will be deleted..but in general for example for HP printer 3 files like lpr1234.tmp,shockwaveobject file ,.shd are generated. In this I have take take required files and keep them in my own spooler.

On my extensive search I came to know that by using WMI and WindowsAPI functions I can get to this goal...
 
But Iam unable to get into the right way....
If I have to use APIs then kindly tell what are the method I have to implement(if possible give me documentation link)
 
If through WMI....kindly guide me for the same
 
ALL these should be act as a service.....
 
thanks in Advance
GeneralNeed Helpmembercs0248013 Sep '07 - 22:23 
I use this code in X64 OS.In debug mode printing is fine.
But In Release mode Startpage return -ve value. plz help me
QuestionCan MFC Dialog base support printingmemberwangsus14 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
GeneralPrintmemberSeu_why28 Mar '06 - 16:01 
please send your source project to me .^_^
 
Thank you !
 
Seu_why
Generalprintmemberamitrajin02@gmail.com5 Sep '05 - 0:50 
I am drawing some lines while printing using page size A4.if I change the page size at runtime the lines are not printed correctly. how can I reflect the change of pagesize to print correctly.

 
gdfgdf
GeneralPrinter Consistence in Visual Basicmembere_screw21 Jul '05 - 3:33 
Hi , Thanks all.
I have developed an application in visual basic which prints on pre defined papers. My problem is when i print the content on EPSON InkJet/DeskJet its printing fine. But when i use another printer like HP Laser Printer its not printing on the paper( printing outside). My paper is very small (18cms x 10cms). In laser printer, when i keep the paper and move the lever to make paper not to move, its coming to the center of the tray. And when i print its printing in the left margin or right margin, where there is no paper and my paper in center! Could any one plz help me how to make it print in the center?
 
I have seen the code here By M.A Barnhart, but i couldnot understand the code except the GETDEVICECAPS. Could anyone plz help me doing the same in visual basic, if its the solution i am looking for.
 
Thanks in advance
 

Thanks in advance
 
Electronic Screw
GeneralCrashes when destructingmemberpanzerdivisionmarkus14 Jul '05 - 20:51 
Hi!
This code looks really great and might just be what I'm looking for. But I have a little problem.
When my function exits the program crashes, and i can't exactly understand why. It hink it crashes becuase when it destructs the CPrintInfo object, the destructor tries to delete CPrintInfo.m_pPD and this pointer is not valid.
 
I don't know if this problem is because of me, but I have copied your code right off so it should work shouldn't it.
 
Do you think you could help me, please?
 
Regards
Markus
GeneralRe: Crashes when destructingmemberMichael A. Barnhart15 Jul '05 - 0:05 
The code as shonw was for 98 and NT4. What enviroment do you have?
 
For W2K conflicts did occur. It has been too long and I do not remember any details.
 
If it helps here is a newer example (but I have not run this in a few years either.)
 
void CChildView::PrintSetup(int item)
{
 
CDC dc;
CPrintDialog printDlg(FALSE);
 
if (printDlg.DoModal() == IDCANCEL) // Get printer settings from user
return;
 
dc.Attach(printDlg.GetPrinterDC()); // Attach a printer DC
dc.m_bPrinting = TRUE;
 
CString strTitle; // Get the application title
strTitle.LoadString(AFX_IDS_APP_TITLE);
 
DOCINFO di; // Initialise print document details
::ZeroMemory (&di, sizeof (DOCINFO));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = strTitle;
 
BOOL bPrintingOK = dc.StartDoc(&di); // Begin a new print job
 
// Get the printing extents and store in the m_rectDraw field of a
// CPrintInfo object
CPrintInfo Info;
Info.m_rectDraw.SetRect(0,0,
dc.GetDeviceCaps(HORZRES),
dc.GetDeviceCaps(VERTRES));
 
CRect MyArea;
MyArea = UserPage(&dc, 0.9f); // fixed margin in inches (you can change this)
 
dc.StartPage(); // begin new page
 
switch(item)
{
case(1):
PrintLoose(&dc,MyArea);
break;
case(2):
PrintRecord(&dc,MyArea);
break;
}

if (bPrintingOK)
{
dc.EndPage();
dc.EndDoc(); // end a print job
}
else
{
dc.AbortDoc(); // abort job.
}
 
dc.Detach(); // detach the printer DC
return;
}

 
I do not mind getting old. It beats all the other options that I can think of.
GeneralRe: Crashes when destructingmemberpanzerdivisionmarkus15 Jul '05 - 1:54 
Ok, I'm coding for 2000/XP.
It appears that if I don't use the CPrintInfo pointer it works just fine.
I can't really see where you use that variable, what is it good for? Because I've never used that one when I do printing code.
GeneralIntercepting a print jobmemberAlexEvans2 Jul '05 - 16:40 
Hello all
 
Say I have a program - for which I have no source code, and it prints a "report" in this case it is actually a Point-Of-Sale RECEIPT going to either a SERIAL or PARALLEL printer port
 
I want tyo TRAP, the print Job, before it gets to the port, re-package it (add some Logo, and header / footer lines) and then let go of it to the intended printer..
 
Can this be done? Any suggestions please (using MFC / C++)
 
Thanks
Alex
GeneralRe: Intercepting a print jobmemberMichael A. Barnhart3 Jul '05 - 1:00 
AlexEvans wrote:
Can this be done?
 
Yes it can. I have no background in intercepting serial connections. Have you posted this question in the C++ forum?
 
I do not mind getting old. It beats all the other options that I can think of.
GeneralRestore to original MapModememberDavid_Leikis26 May '05 - 6:07 
In the UserPage function the line:
 
int OriginalMapMode = pDC->SetMapMode(MM_TWIPS);
 
was good to save the original, but you forgot to restore it.
 
You should do this at the end of UserPage also.
 
pDC->SetMapMode(OriginalMapMode);
 
Thanks for the good work!
 


 
David Leikis
GeneralRe: Restore to original MapModememberMichael A. Barnhart26 May '05 - 10:52 
David_Leikis wrote:
but you forgot to restore it.
 
YEP Blush | :O , Thanks for the note.
 
I do not mind getting old. It beats all the other options that can think of.
QuestionHow to change the size of a print page?memberdeer@webmail.hebut.edu.cn22 Apr '05 - 23:15 
I used the codes below to change a print page size, but it can only be used succeed in Win98, cannot in WinXP. Can you help me to fix it? Thank you.
 
CView::OnPreparePrinting(CPrintInfo* pInfo)
{
CPrintDialog dlgPrint(FALSE);
if( !dlgPrint.GetDefaults() )return FALSE;
LPDEVMODE pDM=dlgPrint.GetDevMode();
if (pDM==NULL)return FALSE;
pDM->dmPaperSize = DMPAPER_USER;
pDM->dmFields |= DM_PAPERSIZE;
pDM->dmPaperLength = 920;
pDM->dmPaperWidth = 2410;
::GlobalUnlock(pDM);
pInfo->m_pPD->m_pd.hDC = dlgPrint.CreatePrinterDC();
pInfo->SetMaxPage( 1 );
return DoPreparePrinting(pInfo);
}
AnswerRe: How to change the size of a print page?memberMichael A. Barnhart22 Apr '05 - 23:56 
I have not had very much interaction with WinXP. From a few minutes review, I do not see anything out of place with your code. Question though. Is this with the same printer on each system?
 
I do not mind getting old. It beats all the other options that can think of.
QuestionHow to print the image keeping it's original size without considering the size of page,memberMasterFan20 Oct '04 - 22:48 
I want to print the image keeping it's original size without considering the size of page,how can i do?
Generalline thicknessmemberssoft24 Sep '03 - 19:36 
hi,
 
how about adding a function for line thickness. if you draw a line with thickness 1 on a DMP it is a lot thicker that the line with the same width drawn on laser (or any hi-res) printer.
 
milind
Questionhow to get a printer job is color or not?membertw_vincent18 Jul '03 - 6:33 
In MSDN, it says that you can retrieve a color attribute of a printer job by deviceMode->dmColor,
But I found that it doesn't work fine on all printer driver, could some body help me how to get
a printer job is colot or not, exactly? Cry | :((
GeneralExcellent Code, Help Wanted...memberasharveyuk9 Feb '03 - 6:35 
Hi,
 
First of all great piece of code that's really helping me sort a particular project, thanks!
 
My problem/query is that I get inconsistent paper area results depending on which printer I select (which seems to be a theme looking at the other threads but I can't seem to get myself pointed in the right direction to solve the issue satisfactorily). Like the others it seems to be Inkjets which are the problem?
 
Firstly, If I set the margins too low on certain printers the top/left print areas come back as -ve figures which I assume should be simply set to zero?
 
Secondly, amongst other things, I'm trying to get some text wedged in the very bottom right hand of the page - with the non-inkjet printers this comes out just fine but with inkjets(and with the margins >=0) the bottom is far too great a figure?
 
Whilst I'm not a newbie, I wouldn't say I'm an expert so perhaps I'm missing something quite simple?
 
I'm using NT4/MSVC++ 6.0,
Printers OK, HP Laserjet, WinFax
Printers NOK, Canon BJC 4000, HP Deskjet
 
Thanks Very Much
ASH
GeneralRe: Excellent Code, Help Wanted...memberMichael A. Barnhart9 Feb '03 - 6:59 
First a suggestion.
 
Start with a simple problem and make it work first.
Say a 2 inch margin (or what ever scale you wish) that is well inside all printable bounds of the printers you need to deal with. Draw a box around this area and some text in the middle. Is that consistent?
 
Next work this same problem out to the edges of the sheet to be printed. Where do failures begin?
 
On the I have dealt with HP 500,550, 660 and some wide carriage with out problems, other than yes the lower printable area is not accessable. Same with a Canon 2000. If that is the problem there is not to much you can do. It is out of the boundary the printer supports. But you should be able to prove that is the case with the trial above.
 
"I will find a new sig someday."
GeneralStartDoc() not working for Releasememberlucky76020 Jan '03 - 12:02 
Confused | :confused: First off I want to thank you Michael for your excellent and highly useful source!
 
I have implemented this code and it workes perfectly. At least when the active
configuration is Debug. I made no other changes than setting it to Release and
then StartDoc() began returning -1. Why is that?! I changed it back to Debug
and it worked fine once again.
I don't imagine that StartDoc() only works for Debug builds. Do you have any idea
as to why this is happening to me?
 
Please help. I appreciate your assistance.
 
Thanks.
GeneralRe: StartDoc() not working for ReleasememberMichael A. Barnhart20 Jan '03 - 12:18 
There should not be any issue with debug vs release. One issue could be is everything being initialized. You could do a quick test by printing out the prameters (say a message box) and verifying all the data exists.
What printer and os are you trying to use? Some differences do exist depending on how you are trying to initialize the printer, but that should not be in the start doc but in the scaling calculations.
 
"I will find a new sig someday."
Generalquestion about CPrintDialog and DEVMODE Structurememberlavocat2 Dec '02 - 0:20 
I would like to retrieve the height and the width of the paper selected in
the CPrintDialog by the user.
When I display the CPrintDialog then if the user change the paper,
dmPaperLength and dmPaperWidth are not updated but dmFormName is updated.
 
dmPaperLength and dmPaperWidth are updated only if I reload the CPrintDialog and
press ok button again.
 
I remarq that dmpapersize is updated but how can I retrieve the height and the width
from dmpapersize??
 
Thank you,
 
Christophe from Belgium
 

This is my code :
//-------------------------------------------
int ret;
PRINTDLG PRT_DLG;
LPDEVMODE lpDevMode;
 
CPrintDialog pd( true,PD_ALLPAGES|PD_RETURNDC|PD_HIDEPRINTTOFILE,NULL);
ret = AfxGetApp()->DoPrintDialog(&pd);
if(ret == IDOK)
{
AfxGetApp()->GetPrinterDeviceDefaults(&PRT_DLG);
lpDevMode = (LPDEVMODE)::GlobalLock(PRT_DLG.hDevMode);

if(lpDevMode != NULL)
{
CString str2;
m_paperHeight.cm = (float)(lpDevMode->dmPaperLength/100) ;
m_paperHeight.inches = (lpDevMode->dmPaperLength/100) * (float)0.39;
m_paperWidth.cm =(float) (lpDevMode->dmPaperWidth/100) ;
m_paperWidth.inches = (lpDevMode->dmPaperWidth/100) * (float)0.39;
CEdit * ptre=(CEdit *) GetDlgItem(IDC_EDIT14);
ptre->SetWindowText(( char*) lpDevMode->dmFormName);
initWidthHeightofPaper();
::GlobalUnlock(PRT_DLG.hDevMode);
}
}
//--------------------------------------------

GeneralRe: question about CPrintDialog and DEVMODE StructurememberMichael A. Barnhart2 Dec '02 - 12:40 
lavocat wrote:
AfxGetApp()->GetPrinterDeviceDefaults(&PRT_DLG);
 
This line gets the defaults for the printer not what you have selected.
 
To get the current selection try something like:
 
int ret;
CPrintDialog pd( true,PD_ALLPAGES|PD_RETURNDC|PD_HIDEPRINTTOFILE,NULL);
ret = AfxGetApp()->DoPrintDialog(&pd);
if(ret == IDOK)
{
CDC dc;
dc.Attach(pd.GetPrinterDC()); // attach a printer DC
// Set Map Mode
int OriginalMapMode = dc.SetMapMode(MM_LOMETRIC);
CSize Physical;
// This gets the Physical size of the page in Device Units
Physical.cx = dc.GetDeviceCaps(PHYSICALWIDTH);
Physical.cy = dc.GetDeviceCaps(PHYSICALHEIGHT);
dc.DPtoLP(&Physical);
 
double m_paperHeight_cm = ((double)Physical.cy/100.) ;
double m_paperHeight_inches = ((double)Physical.cy/254.);
double m_paperWidth_cm = ((double)Physical.cx/100.) ;
double m_paperWidth_inches = ((double)Physical.cx/254.);
}


 
"I will find a new sig someday."
GeneralRe: question about CPrintDialog and DEVMODE Structurememberlavocat2 Dec '02 - 21:41 
Thank you very much! this is perfect now.
 
But when you said "This line gets the defaults for the printer not what you have selected..." I'm ok with it but then why does the name of the paper change and not the size of this
GeneralRe: question about CPrintDialog and DEVMODE StructurememberMichael A. Barnhart3 Dec '02 - 0:26 
lavocat wrote:
but then why does the name of the paper change and not the size of this
 
I would have to say that falls into the undocumented features list of Windows.Smile | :)
 
"I will find a new sig someday."
GeneralPageArea Calcmembercdoersom26 Nov '02 - 10:22 
Great article, but when I tried it the PageArea came out a bit small; particularly on an inkjet printer. I suggest the following changes:
 
PageArea.left & PageArea.top are ok.
 
For others,
 
PageArea.right = (long)Physical.cx+PrintOffset.cx-margin*inch;
PageArea.bottom = (int)-(Physical.cy+PrintOffset.cy-margin*inch);
 
To go one step further, you might want to limit the endpoints (as suggested by Joep Oude Veldhuis) by using __max() and __min() functions to limit the top and left side to 0, and the bottom and right side to the printable area as follows:
 
PageArea.left = __max(PageArea.left, 0);
PageArea.right = __min(PageArea.right, Printable.cx);
PageArea.top = __min(PageArea.top, 0);
PageArea.bottom = __max(PageArea.bottom, -Printable.cy);
 
Chuck

GeneralRe: PageArea CalcmemberMichael A. Barnhart26 Nov '02 - 13:17 
First thanks.
 
I agree adding the max and min would make this more robust. I just have always tested the margin so it has never been an issue.
 
If you compare my version for right vs yours they are the same except the sign on the PrintOffset is switched. I believe it should be negative. I have used this code with a number of ink jets so that should not be cause. But none have had very high resolution. I wonder if that may be a factor? What printer are you using? and what dpi does it have?
 
"I will find a new sig someday."
GeneralRe: PageArea Calcmembercdoersom26 Nov '02 - 18:48 
The more I look at this, the more I realize that it should really be
 
PageArea.right = (long)Physical.cx-margin*inch;
PageArea.bottom = (int)-(Physical.cy-margin*inch);
 
The offset doesn't enter into this at all. When you calc the margin, it is from the edge of the paper (Physical Dimension).
 
A quick test on my HP Laserjet III (300 dpi) and HP PhotoSmart 1215 (600 DPI)worked ok. The problem with many Inkjets is that the top offset is small and the bottom offset is large. So if you're not careful about how you apply the offset returned from GetDeviceCaps(), you can end up with little margin at the top, and a big margin at the bottom.
 

GeneralRe: PageArea CalcmemberMichael A. Barnhart27 Nov '02 - 0:11 
cdoersom wrote:
The offset doesn't enter into this at all.
 
The offset definitly figures into this! 0,0 is the upper left corner of the printable page. Using this technique you know what to add to your relative 0,0 (your consistent area in the margin.) the PageArea left and top and you know what values to limit your printing to PageArea right and bottom.
 
So when you print you allways print from your margin reference and add the page area to your value from the margine location. Are you trying to still print relative to 0,0 of the page or adding the returned value of PageArea to your local values from your desired margin?
 
Try a little test. Set this up and draw a line from 0,0 to say 0,200
then draw a lint from 0,0 to PageArea.left,PageArea.top
 
Do this with several mapping modes (and call cdc.DPtoLP to correct the values in PageArea) You should see your first line change lengths and show you the edge of the printable area of the page. The line from 0,0 to your margin position should be to the desired location physically on the page form the actual page edge.
 
"I will find a new sig someday."
GeneralRe: PageArea Calcmembercdoersom27 Nov '02 - 6:04 
I tried the line drawing you suggested and the page area you originally calculated was correct. Turns out I had a sign change problem that caused mine to not work. Sorry for the confusion.
 
Any suggestions for taking the page area and then calculating the font size necessary to just fill the page with say 65 lines of text? The number of lines can change from 60 to 100.
 
Thanks for your help.
 
Chuck
GeneralRe: PageArea CalcmemberMichael A. Barnhart27 Nov '02 - 12:59 
cdoersom wrote:
Sorry for the confusion.
 
No problem, it is good to go back over things every once in awhile.
 

cdoersom wrote:
Any suggestions for taking the page area and then calculating the font size necessary to just fill the page with say 65 lines of text?
 
This is just off the top so it may need some adjusting.
 
int NumLines = 65;
CSize fsize;
fsize.cx = fsize.cy = PageArea.Height()/NumLines;
pdc->DPtoLP(&fsize);
 
BaseFont.CreateFont( -fsize, 0, 0, 0, FW_MEDIUM,
FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH , "Courier New" );


 
"I will find a new sig someday."
GeneralInkjet printers give dpi differentmemberRichard Jones20 Nov '02 - 4:36 
I have tried using the devmode.dmPrintQuality on an HP 970 Deskjet, and it
reports a quality of -3 (medium).
 
Any idea what dpi this would correspond to?Confused | :confused:
 
I also tried an HP Designjet 650c color plotter, and got the same thing.
I am using Win2K.
 

 
Rick Jones
Information Systems
GeneralRe: Inkjet printers give dpi differentmemberMichael A. Barnhart20 Nov '02 - 13:27 
Well, this is the first time in months I recieved a message of a post. So at least for me that is good news.
 
HP uses different DPI for b&w vs color. So they only report low, high, or medium vs the actual values.
 
Assuming the 970 is about the same 995 the max b&w is 600 dpi and color is 1200. So medium would be about 400 b&w / 800 color and low about 1/2 of those numbers. This is a guess on my part from what I remember my 660 printed.
 
"I will find a new sig someday."
GeneralRe: Inkjet printers give dpi differentmemberRichard Jones22 Nov '02 - 2:25 
Thanks for the info.
 
I figured out a workaround.
 
Using the printer's hDC, I get the LOGPIXELSX.
 
CPrintDialog dlg(TRUE);
dlg.GetDefaults();
 
HDC hdc = dlg.CreatePrinterDC();
ASSERT(hdc);

int res = GetDeviceCaps(hdc, LOGPIXELSX);

GeneralProb getting color setting from DEVMODEmemberjcn12319 Apr '02 - 8:45 
I'm using the following code snippet with a Lexmark Z22-Z32 driver (inexpensive, inkjet) to invoke a print dialog via a "setup" button
 
CPrintDialog *dlg = new CPrintDialog(FALSE, PD_PAGENUMS |
PD_NOSELECTION | PD_USEDEVMODECOPIES | PD_DISABLEPRINTTOFILE);
 
resultCode = dlg->DoModal();
LPDEVMODE printerDevMode = dlg->GetDevMode();
 
TRACE(“%d”,printerDevMode->dmColor);
 
It works fine under NT, but in win 98/ME, the color value (dmColor) always comes back as "2" (color) even when the user selects "1" (Black & White) Other values such as orientation, number of copies, etc come out fine, but this one gets reset... Again, in NT it works fine. Is this likely to be an error/feature of the related driver or is there something that I might be missing?
Thanks,
JCN

GeneralRe: Prob getting color setting from DEVMODEmemberi.Wahn25 Jul '02 - 21:32 
I had a similar problem. I wanted to store the printer settings for particular documents. This worked fine for most settings, but some selections seemed to vanish after storing and reloading. After several trials and errors I looked again into the DEVMODE documentation and read every entry carefully to be sure to get it right this time. I found the entry 'dmDriverExtra' where the doc says:
 
"Contains the number of bytes of private driver-data that follow this structure. If a device driver does not use device-specific information, set this member to zero."
 
That means there is driver-dependend some more data directly after the DEVMODE structure which I did not save at all in my routines. I found out that some drivers need to have about 1000Bytes. And the driver which seemed to ignore some options stored these in it's private data section - including some color options!
 
So it could be a chance if you try to examine the private data. And before you ask Wink | ;) there *is* truly a difference between 9x and NT regarding this. The first is the size of the DEVMODE structure. And I found out that the drivers for one particular printer use the private data differently. But that was just in my cases.
 
regards,
MI
GeneralTwo small problemsmemberTony Brown17 Jul '01 - 1:11 
Thank you for some very clear simple code and easy to use compared to the nightmare printing examples I have come across.
 
There are a couple of nitpicky errors. The lines :-
MyDocInfo.lpszDocName="DocName";
MyDocInfo.lpszOutput="";
 
I think should read
MyDocInfo.lpszDocName=DocName;
// If you really want your app name
MyDocInfo.lpszOutput=0;
// At least to get Startdoc to not generate an error on one of our 95 boxes
 
and before the GlobalUnlock reset the pd to avoid a heap error:-
MyPrintInfo.m_pPD = 0;
// We set it, so we uninitialise it
GlobalUnlock(MyPrintMode);
// Release the print mode.
 


GeneralRe: Two small problemsmemberMichael A. Barnhart17 Jul '01 - 13:33 
Tony,
Good comments. Thanks for being observant.
GeneralRe: Two small problemsmemberDuncan123452 Apr '03 - 1:57 
Hang on a moment...
 
Without Tony's line:
MyPrintInfo.m_pPD = NULL;
 
I get a crash because the MyPrintInfo object tries to
delete its "m_pPD" member. So I put in the line, but
then I got a leak of a CPrintDialog object. It turns
out the the MyPrintInfo actually "new"s one in its
constructor. So, at the top of the code, before you say:
 
MyPrintInfo.m_pPD = &MyPrintDlg;
 
... you first have to dispose of its original one, thus:
 
if (NULL != MyPrintInfo.m_pPD)
    delete MyPrintInfo.m_pPD;
 
Perhaps you guys are using a newer MFC than me (I'm using
VC6). Also, my code is inside an ATL in-process COM server
(although I severely doubt if that makes any difference).

GeneralThe same, but slightly differentmemberJoep Oude Veldhuis21 Feb '01 - 0:29 
Too bad I didn't see this great article sooner, now I had to figure this out myself. I came up with a slightly different solution, leading to a CRect that can replace CPrintInfo::m_rectDraw. I use CPageSetupDialog to get the user's preference and store that in a global variable named rcPrintMargin. You can use that in the function below as follows:
 
CPrintInfo info;
CPrintInfo::m_rectDraw = GetDrawingRect(pDC, rcPrintMargin);
 
CRect GetDrawingRect(CDC * pDC, const CRect & rcUserMargin)
{
// Get the top en left unprintable margins
CSize sizeOffset(pDC->GetDeviceCaps(PHYSICALOFFSETX),
pDC->GetDeviceCaps(PHYSICALOFFSETY));
 
// Get the physical page size
CSize sizePhysical(pDC->GetDeviceCaps(PHYSICALWIDTH),
pDC->GetDeviceCaps(PHYSICALHEIGHT));
 
// Get the size of the printable area
CSize sizePrintable(pDC->GetDeviceCaps(HORZRES),
pDC->GetDeviceCaps(VERTRES));
 
// Calculate the unprintable margins at right and bottom edges
CSize sizeUnpBR(sizePhysical.cx - sizePrintable.cx - sizeOffset.cx,
sizePhysical.cy - sizePrintable.cy - sizeOffset.cy);
 
// Convert user margins from HIMETRIC to device units
CSize sizeTL(rcUserMargin.left, rcUserMargin.top);
pDC->HIMETRICtoDP(&sizeTL);
CSize sizeBR(rcUserMargin.right, rcUserMargin.bottom);
pDC->HIMETRICtoDP(&sizeBR);
 
// Calculate the drawing rect to replace CPrintInfo::m_rectDraw
CRect rcDraw;
rcDraw.top = __max(sizeTL.cy - sizeOffset.cy, 0);
rcDraw.left = __max(sizeTL.cx - sizeOffset.cx, 0);
rcDraw.bottom = sizePrintable.cy - __max(sizeBR.cy - sizeUnpBR.cy, 0);
rcDraw.right = sizePrintable.cx - __max(sizeBR.cx - sizeUnpBR.cx, 0);
 
return rcDraw;
}

GeneralGoodmemberraja sekhar18 Feb '01 - 21:17 
Is there any way we can get the margin also.Using sdk calls it self.Instead of hard coding itEek! | :eek:
GeneralRe: GoodmemberMichael A. Barnhart19 Feb '01 - 12:10 
I must be missing part of your question. There is no hard coding for the margin other than setting the margin you wish. You have the option of useing a single number or values for each side of the page.
 
You have three items (of interest to get from the GetDeviceCaps call
1) THe Physical size of the page
Physical.cx = pDC->GetDeviceCaps(PHYSICALWIDTH);
Physical.cy = pDC->GetDeviceCaps(PHYSICALHEIGHT);
 
2) The offset to the corner of the page
PrintOffset.cx = pDC->GetDeviceCaps(PHYSICALOFFSETX);
PrintOffset.cy = pDC->GetDeviceCaps(PHYSICALOFFSETY);
 
3) The printable size of the page
Printable.cx = (int)((float)pDC->GetDeviceCaps(HORZSIZE)*56.69);
Printable.cy = (int)((float)pDC->GetDeviceCaps(VERTSIZE)*56.69);
 

From these you have to convert to consistent units. If you want to return the margins you just use the offset for the top and left and the remainder of the physical minus offset minus printable to the right and bottom.
 
Hope this helps.

GeneralPost Script Printermembernavid_ahsan4 Dec '00 - 16:59 

I just want to determine whether the user has selected Post Script printing or not? How can I do this?
 
Navid
GeneralIn millimeters !?!?!sussLuc Bergeron21 Sep '00 - 10:46 
How do you compute the scaling factor if i'm using the MM_LOMETRIC mapping mode ???
 
Thanx in advance for your help :
GeneralRe: In millimeters !?!?!sussMichael A. Barnhart22 Sep '00 - 12:18 
You have a couple of options. Either change the factor in the GetDeviceCaps or (probably better) change the value of "inch" to some other scale by multiplying (or dividing) the 1440 by the ratio of twips to your desired mode.
 
// 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
GeneralPrinter DifferencessussSujay Ghosh16 Jul '00 - 22:51 
Hi ,
 
I am writing a MFC app. which prints docs. via the doc/view architecture. Now suppose, I write my app. based on dot matrix printer, and when I try the same app on a Laser, the printing becomes small. How do I go around in making my app printer independent.
 
Get back asap.
 
Sujay Ghosh
 

GeneralRe: Printer DifferencessussMichael Barnhart17 Jul '00 - 3:03 
If you just need the font size to be the same, use the function CreateFontSize to determine the actual size of the font to create in the font creation as shown. Note these will not be exactly the same place on the page between these printers
GeneralMissing Line in UserPagesussMichael Barnhart9 Feb '00 - 6:14 
The UserPage does not reset the logical units to the original units that were saved at the start of the code. A line prior to the return should be added!
 
pDC->SetMapMode(OriginalMapMode);

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 7 Feb 2000
Article Copyright 2000 by Michael A. Barnhart
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid