"Joseph M. Newcomer" wrote:
See below...
On Tue, 22 Dec 2009 05:27:01 -0800, Cameron_C <CameronC@discussions.microsoft.com> wrote:
Hello Everyone.
I have been struggling with this item for some time.
For a while I was content to ignore it, but it has finally made it to the
top of my list.
I have an application where the User may choose reporting fonts.
Three fonts. One for the header, one for the body, and one for the footer of
the various reports. I save the LOGFONT structures for later use.
At the time I do a StartDocument I grab the LOGFONT structures I had
previously saved, and CreatePointFontIndirect to create the fonts.
As I walk through the code in debug mode, everything seems to work fine.
However, the printing works perfectly on an HP 9040DN, but no details appear
when I print to a Brother or the HP Photosmart I have. The graphics boxes do
appear though.
Anyway my first question relates to the voodoo-like black magic of scaling
the font.
I want a ten point font. I select a ten point font from the CFontDialog, and
save it. Later, when I want to create the font I call
CreatePointFontIndirect, and I pass a value that is in tenths of a point. So,
now I would pass 100, for my ten point font.
****
The truth is that the font is scaled to the display. If you want to use that font on a
printer, you must scale it to the printer.
Typically, you want to use CFont::CreatePointFontIndirect and for the second parameter
pass in your printer DC for printing and either NULL or your display DC for display.
*****
From experience this never worked. After poking through various web sites, I
found someone had success with using a "Cook''s Constant" of 42/13. So, I
have taken my value of 100, and multipled by 42/13. Is this obtuse or am I
off base here? Anyway, this appears to do the trick, at least for the HP
printer.
****
Sounds like sheer luck. I have no idea what this is doing.
*****
I checked the Microsoft docs, and they suggest using something along the
lines of -MulDiv(m_lfReportHeader.lfHeight/10,
m_dc.GetDeviceCaps(LOGPIXELSY), 72);
Now, I have tried this with the printers that do not deliver the report as
execpted, and I see no difference. I have not tried this with the HP printer
that is really working.
*****
It may be a function of the printer dirvers, but it seems obtuse if it is.
****
Here is a code snippet showing how I have created the fonts.
/*
Assign default Report Header Font for the Reports
Note: m_szIniSectionName will refer to the specific Report for the Printer.
m_ProcessIni.m_szIniSectionName will refer to the DEFAULT entries
for the Printer.
*/
LOGFONT lfIniDefaults;
BOOL bFlag=FALSE;
TEXTMETRIC myTextMetrics;
m_ProcessIni.m_szIniKeyName.LoadString(IDS_REPORTHEADERFONT);
bFlag = m_ProcessIni.getKeyValueForFont(&lfIniDefaults,
m_ProcessIni.m_szIniKeyName, m_szIniSectionName);
****
Have you considered the Registry for this? My Registry library actually saves some font
information automatically. I see no value in parsing font information from a .INI file (I
gave up doing that when I abandoned Win16 many years ago)
****
if (!bFlag) // No font information available from the ini file, so save
DEFAULT
{
m_ProcessIni.setKeyValueForFont(m_plfReportHeader,
m_ProcessIni.m_szIniKeyName, m_szIniSectionName);
}
else
{
m_lfReportHeader = lfIniDefaults;
}
lfIniDefaults = m_lfReportHeader;
lfIniDefaults.lfHeight*=(42/13); // I have no idea why this scaling is
required... But it is. Found this in a news group.
****
Neither do I, it looks really bogus, like someone discovered some random value which gave
the illusion of working so claimed it made sense. 42/13 is silly anyway; 42/13 is 3, and
expressed as 42/13 doesn't change the fact it is 3. And I have no idea what 3 does. Now
42.0/13.0, working as a double, e.g.,
something =(int)( (double) something * 42.0 / 13.0)
might have meaning, but "3" is just silly.
****
// lfIniDefaults.lfHeight = -MulDiv(m_lfReportHeader.lfHeight/10,
m_dc.GetDeviceCaps(LOGPIXELSY), 72);
****
If you get rid of the gratutitous *=3, this should work correctly. But it is what
CreatePointFontIndirect does. Read the code.
****
/*
Establish Font characteristics for the Report Header lines.
lfIniDefaults Points to a LOGFONT structure that defines the
characteristics
of the logical font.
The lfHeight member of the LOGFONT structure is measured in tenths of a
point
rather than logical units.
(For instance, set lfHeight to 120 to request a 12-point font.)
CreatePointFontIndirect returns non-zero when successful.
If bFlag is zero, we have a problem!
*/
CFont*pOldHeaderFont, *pOldFooterFont, *pOldBodyFont;
*****
Never use commas in declaration lists. And there is no reason to save old state like
"OldHeaderFont"; instead, use CDC::SaveDC and CDC::RestoreDC to mainain the purity of your
DC.
****
m_cfReportHeader.DeleteObject();
bFlag = m_cfReportHeader.CreatePointFontIndirect(&lfIniDefaults, &m_dc);
****
OK, you Are using CreatePointFontIndirect, so why all that other stuff which either
duplicates it or duplicates it badly?
****
pOldHeaderFont = m_dc.SelectObject(&m_cfReportHeader);
****
I have not saved an "old setting" on SelectObject in about 20 years. Use
SaveDC/RestoreDC. Otherwise, you get a lot of garbage variables and lose track of them
(there are over 30 DC parameters you might want to save the "old" version of!)
****
m_pcfReportHeader = &m_cfReportHeader;
*****
How in the world does this assigment make any sense whatsoever? It is complete nonsense.
Get rid of it.
****
// m_hFontReportHeader is the CGdiObject public data member that stores
the handle
m_hFontReportHeader = (HFONT) pOldHeaderFont->GetSafeHandle();
*****
This makes even less sense. Why do you need an HFONT anyway? There is something
seriously wrong hwere with what you are doing.
****
//m_hFontReportHeader = (HFONT) m_pcfReportHeader->GetSafeHandle();
/* Get Average size of printed text for the Header Font */
m_dc.GetTextMetrics(&myTextMetrics);
m_yLineHeader = myTextMetrics.tmHeight;
Later when I want to use the font in a report, I select the font, and do a
DrawText.
Here is a code snippet:
pcfOriginal =
ts->_this->PrinterControl.m_dc.SelectObject(ts->_this->PrinterControl.m_pcfReportHeader);
****
There is no need to store the original. m_dc.SaveDC() will save the DC state. Get rid of
the assignment.
What is _this?
If this is a subroutine that is being called for each of the headers, just pass the font
in as a CFont& and when you need the address, use & on that. This business of having some
memver variable which is set to a pointer doesn't make sense.
****
iPrintedHeight=ts->_this->PrinterControl.m_dc.DrawText(szHeader,
rclHeader, DT_TOP|DT_CENTER);
iPrintedHeight=ts->_this->PrinterControl.m_dc.DrawText(szSubHeader,
rclSubHeader, DT_TOP|DT_CENTER);
pcfOriginal =
ts->_this->PrinterControl.m_dc.SelectObject(ts->_this->PrinterControl.m_pcfReportFooter);
****
I'm already confused. You set pcfOriginal to one value when you select the header font,
and then you overwrite that when you select the footer font (and why would you not simply
write
&ts->_this->PrinterControl.m_cfReportFooter
instead of that silly technique of storing pointer variables which you then use here?) so
what is it going to be restored to?
****
iPrintedHeight=ts->_this->PrinterControl.m_dc.DrawText(szFooter,
rclFooter, DT_BOTTOM|DT_CENTER);
szBuffer.Format(_T("Page %d"), ++lPageNumber);
iPrintedHeight=ts->_this->PrinterControl.m_dc.DrawText(szBuffer,
rclFooter, DT_BOTTOM|DT_RIGHT);
pcfOriginal =
ts->_this->PrinterControl.m_dc.SelectObject(ts->_this->PrinterControl.m_pcfReportBody);
****
You really need to understand what the & operator does, and where you use it. All those
intermediate pointer-to-font variables are completely silly!
I note that at no point do you restore the font you changed, that is, I see no
....m_dc.SelectObject(pcfOriginal);
and that means that saving the value is pointless. And then you leave the font selected
into the DC, which is poor practice. See my essay on SaveDC/RestoreDC on my MVP Tips
site.
****
One last question, I know that the Fonts are applicable for the DC that has
been selected. When the DC is destroyed, and recreated, do the Fonts need to
be recreated? Do I need to destroy and recreate the fonts every time I
destroy and recreate the DC? Should I?
****
Yes. Because if I create a font for my one printer (1200dpi) then close the printer, and
then open another DC on the other printer (600 dpi), my fonts would be 2x too large. So
it is rare I would create the fonts as member variables at all. I would typically create
them in the printing routine handler, and let the normal CFont::~CFont destructor get rid
of them when I was done printing. I don;t use global variables when local variables would
do. And I'm willing to pass parameters around, including a parameter which is my printing
context, e.g.,
class PrintingContext {
public:
CFont Header;
CFont Section;
CFont MajorTitle;
CFont Text;
};
and initialize those variables. I'd probably have a constructor that took the CDC and
created the fonts relative to it. And I'd probably have other things in it such as the
current line, current page #, etc., and never, ever, under any conditions imaginable would