From: Cameron_C on
Joe, thank you very much for sharing this.
This answers everything I need to know about the calculations.
Honestly, I have searched through Microsoft's documentation, but I did not
find this explained. At least not in any meaningful way I could understand.
After making the changes, the scaling is fine.
I can print to two printers now.
I am still not having much luck with my Brother printer.
I will keep researching and I will post back anything I find.

Merry Christmaas Joe, you have been very helpful all year long.

Thanks,

........Cameron

"Joseph M. Newcomer" wrote:

> If you are doing a CreatePointFont, then its specification is to give the lfHeight as
> decipixels. But that is a feature CreatePointFont (look at its code). So yes, if you are
> giving font heights in decipoints, you want the / 10, but you had not actually indicated
> that you were using decipoint heights.
>
> If you are doing CreateFontIndirect, you must use the actual height with the muldiv
> computation. If you are doing CreatePointFont[Indirect] to a printer DC, the base
> computations are already done for you, so you don't want to do them yourself; the result
> will not be meaningful.
>
> Your choice of font height representation is yours. But remember that if you are doing
> CreateFont[Indirect] you want the height in pixels, not decipixels. If you are doing
> CreatePointFont[Indirect], its specificaiton is decipixels, in which case you would not be
> doing a muldiv yourself.
>
> Ultimately, you are going to get a font in pixels. To get a 10-point font on a 10ppi
> display, there is a 1:1 correspondence,but on a 600dpi display, to get a 10-point font it
> would be 10 * (600/72). If you do this in floating point, you get 6000/72, and that is
> essentially 83 pixels high. So if you single-step in far enough to
> CreatePointFont[Indirect] you should see a real CreateFont creating a font which is the
> right number of pixels for your device. An 18-point font would be 18*600/72 or about 150
> pixels high. Notice that the order matters. I tend to do computations in floating point
> and then round them and convert back to an integer; saves all kinds of hassle with integer
> truncation problems.
>
> If you are using MM_TWIPS, then you have 1440 pixels/inch, so an 18-point font is
> 18*1440/72 = 360 pixels high. GDI takes care of mapping twips-based coordinates to
> physical device coordinates.
>
> Note that if you are using a raw printer DC, the assumption is that you are using MM_TEXT.
> So if you want to use CreatePointFont[Indirect] and specify a DC, the DC has to be in the
> mapping mode you are going to use. Doesn't matter if it is TWIPS, LOENGLISH, HIENGLISH,
> LMETRIC, HIMETRIC, or your own custom ISOTROPIC or ANISOTROPIC mapping, whatever mapping
> you are going to use for the drawing is what should be set. Generally you do this in
> OnPrepareDC, so you would call that at the point where you are about to create the font.
>
> Frankly, I would not waste any effort trying to create the font in advance of printing.
> When I got into the OnDraw handler, after PrepareDC is called, that is the only place I
> would create the fonts. They would not be member variables of the class; they would be
> local variables of the OnDraw function. When I left OnDraw, they would be implicitly
> destroyed by the destructors of the local variables. I would have no reason for them to
> exist outside the context of OnDraw (or OnPaint).
> joe
>
>
> On Wed, 23 Dec 2009 12:51:01 -0800, Cameron_C <CameronC(a)discussions.microsoft.com> wrote:
>
> >Thanks Joe.
> >What value should be in the lfHeight, when I am looking for a six point font?
> >I know that I should use tenths of a point (so sixty) when I
> >CreatePointFontIndirect.
> >But, what do I want to put through the scaling math? Do I need tenths of a
> >point? Or full point values?
> >I believe that the MulDiv(a,b,c) is supposed to give me (a*b)/c.
> >But, do I start with tenths of a point or point values? Six or sixty?
> >When I use sixty, my font is now about one inch high.
> >And when I use six, I get something pretty small. Maybe it is six points.
> >And this is since I changed to set to MM_TWIPS after creating the printer DC.
> >Maybe I should not use MM_TWIPS?
> >
> >Thanks,
> >
> >
> >"Joseph M. Newcomer" wrote:
> >
> >> As pointed out, that divide-by-10 is a total disaster. Remove it.
> >> joe
> >>
> >> On Wed, 23 Dec 2009 06:26:01 -0800, Cameron_C <CameronC(a)discussions.microsoft.com> wrote:
> >>
> >> >Thanks Joe,
> >> >I removed the coding for the 42/13, and tried with the
> >> > lfIniDefaults.lfHeight = -MulDiv(m_lfReportHeader.lfHeight/10,
> >> >m_dc.GetDeviceCaps(LOGPIXELSY), 72);
> >> >This gave me some print that was about a 2 point font. With glasses I could
> >> >almost read some of it.
> >> >But this is progress, at least something is printing.
> >> >
> >> >I also just found that if I use SetBkMode(TRANSPARENT) just before I begin
> >> >to print, the report appears on my HP Photosmart printer. (Found it by
> >> >accident when I accidentally uncommented the wrong line).
> >> >
> >> >When I print, I get a printer dc, then I define the fonts. The I select the
> >> >font I want and I drawtext.
> >> >
> >> >This works fine on one printer. And, since I added the SetBkMode it works on
> >> >another (although I have no understanding of why this would be). But still
> >> >fails to print on my little Brother printer.
> >> >
> >> >One more curiousity, when I create font defaults, I do the following:
> >> > CFont cfPrinterFont;
> >> > m_bFlag = m_pApp->CreatePrinterDC(m_dc);
> >> >
> >> > /* Establish Default Font characteristics for the Reports */
> >> > cfPrinterFont.Attach(::GetStockObject(DEVICE_DEFAULT_FONT));
> >> > LOGFONT lf;
> >> > cfPrinterFont.GetLogFont(&lf);
> >> >
> >> > lf.lfHeight *= 10;
> >> > cfPrinterFont.Detach();
> >> >
> >> > cfPrinterFont.CreatePointFontIndirect(&lf, &m_dc);
> >> > cfPrinterFont.GetLogFont(&m_lfReportHeader);
> >> >Now, later when I try to use the LOGFONT, I get characters that are the
> >> >expected height, but are so thin they are unrecognizable.
> >> ***
> >> Never set lfWidth. Leave it 0 (default). Otherwise, you get the strange effects you are
> >> seeing.
> >>
> >> You can download my font explorer and play with it, but you will see that lfWidth is not a
> >> good value to use.
> >> ****
> >> >So, I reset the lfWidth to zero, in this LOGFONT structure, before I
> >> >CreatePointFontIndirect.
> >> >And, at least then I can see the characters.
> >> >Why does the width need to be 0 (which requests the default)? I thought if I
> >> >requested the DEVICE_DEFAULT_FONT for the printer DC, I would get all of the
> >> >values I needed?
> >> ****
> >> TrueType/OpenType fonts will adjust themselves by horizontal scaling to conform to the
> >> lfWidth if you do not default it.
> >> ****
> >> >Are there any other values I get in the LOGFONT that I should reset before
> >> >creating a font?
> >> >
> >> >And then one more question.
> >> >Maybe this is not an appropraite technique? I use a CFontDialog to allow the
> >> >Users to select the fonts for the reports.
> >> > LOGFONT lf = m_pPrinterControl->m_lfReportHeader;
> >> > lf.lfHeight = -MulDiv(m_pPrinterControl->m_lfReportHeader.lfHeight/10,
> >> >m_pDC->GetDeviceCaps(LOGPIXELSY), 72);
> >> > CFontDialog fdHeader(&lf,
> >> > CF_PRINTERFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT | CF_ENABLEHOOK
> >> >| CF_FORCEFONTEXIST,
> >> > m_pDC);
> >> >
> >> >Should the m_DC refer to the printerDC? Or to the display DC.
> >> >I think it has to be the display but I am not sure?
> >> ****
> >> Ideally, the printer DC for selecting printer fonts. Again, get rid of the /10 (note this
> >> is integer divide, so 18/10 = 1, which leads to your problems). But generally, if you use
> >> the display, the fonts are available for the display. This dichotomy predates the
> >> TrueType world, and in those days it allowed you to specify native printer fonts for
> >> dot-matrix printers and primitive inkjet and laser printers. It is generally no longer an
> >> issue.
> >> joe
> >> ****
> >> >
> >> >Thanks,
> >> >
> >> >
> >> >"Joseph M. Newcomer" wrote:
> >> >
> >> >> See below...
> >> >> On Tue, 22 Dec 2009 05:27:01 -0800, Cameron_C <CameronC(a)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);