Environment: VC6 SP5, WinNT Family, Win9X
People who have ever tried to implement WYSIWYG text output encountered the problem of how to draw text on different devices and in different resolutions in the same way — especially if you implemented text formatting.
Here is an explanation how to implement WYSIWYG text output:
- First of all, we have to retrieve the refernce font data:
- We have to calculate distances between letters using our current device context resolution.
- Now we can display our text on any devices we want using ExtTextOut function.
// Allocate memory to store font cached data
*ppFontInfo=new CTLFontInfo();// Retrieve OUTLINETEXTMETRIC structure size
UINT nSize=GetOutlineTextMetrics(hDC,0,NULL);if(!nSize)
throw system_exception();pTextMetrics=(OUTLINETEXTMETRIC*)new BYTE[nSize];
// Retrieve OUTLINETEXTMETRIC structure
if(!GetOutlineTextMetrics(hDC,nSize,pTextMetrics))
throw system_exception();// Get reference dc
// The best solution is to use device context with
// the largest resolution
if(!m_hReferenceDC)
{
if(!CreateReferenceDC())
leave;
}// Retrieve device resolution
// Note: if you use GetDeviceCaps(LOGPIXELSY) with
// display device context it will return 96 or 120
// (depends on yours display settings). We have
// to calculate display resolution using next formula:
// GetDeviceCaps(HORZRES)/GetDeviceCaps(HORZSIZE)*25.4.
// Where: 25.4 – mm per inch.
double nLogPixelsY=GetDisplayLogPixelsY( m_hReferenceDC,
m_bDisplay);// Create reference font with height equal to EMSquare.
LOGFONT lReferenceFont=lFont;
lReferenceFont.lfHeight=-MulDiv(pTextMetrics->otmEMSquare,
nLogPixelsY,72);
HFONT hReferenceFont=CreateFontIndirect(&lReferenceFont);if(!hReferenceFont)
throw system_exception();if((hOldFont=
(HFONT)SelectObject(m_hReferenceDC,hReferenceFont))==NULL)
throw system_exception();// Retrieve reference font’s OUTLINETEXTMETRIC structure size
UINT nSize1=GetOutlineTextMetrics(m_hReferenceDC,0,NULL);if(nSize!=nSize1)
{
delete pTextMetrics;
pTextMetrics=(OUTLINETEXTMETRIC*)new BYTE[nSize1];
}// Retrieve reference font’s OUTLINETEXTMETRIC structure
if(!GetOutlineTextMetrics(m_hReferenceDC,nSize1,pTextMetrics))
throw system_exception();// Store text metrics inside cached object
// NOTE: We have to divide all metrics by nLogPixelsY to
// use them later with another device context
(*ppFontInfo)->FillInTextMetrics(pTextMetrics,nLogPixelsY);// Retrieve characters widths
int nCharsCount=pTextMetrics->otmTextMetrics.tmLastChar-
pTextMetrics->otmTextMetrics.tmFirstChar+1;int* pCharWidths=new int[nCharsCount];
if(!GetCharWidth(m_hReferenceDC,
pTextMetrics->otmTextMetrics.tmFirstChar,
pTextMetrics->otmTextMetrics.tmLastChar,
pCharWidths))
leave;(*ppFontInfo)->FillInCharactersWidths(pCharWidths,nCharsCount,
pTextMetrics->otmTextMetrics.tmFirstChar,
nLogPixelsY);// Retrieve kerning pairs
DWORD dwSize=GetKerningPairs(m_hReferenceDC,0,NULL);if(dwSize)
{
pKerningPairs=
(KERNINGPAIR*)new BYTE[dwSize*sizeof(KERNINGPAIR)];if(!GetKerningPairs(m_hReferenceDC,dwSize,pKerningPairs))
throw system_exception();(*ppFontInfo)->FillInKerningPairs( pKerningPairs,
dwSize,
nLogPixelsY);delete pKerningPairs;
pKerningPairs=NULL;
}delete pTextMetrics;
pTextMetrics=NULL;
TL_PAIR Pair(0,0);long nCount=strlen(szText);
double fKerningValue=0.0;
double fCurrentWidth=0.0;
double fTotalWidth=0.0;// This coefficient will be used to convert font
// height from EMSquare to required height
double fReferenceFont2LocalFont=m_fFontHeight/
(double)pFontInfo->m_TextMetrics.otmEMSquare;
double fLogPixelsY=GetDisplayLogPixelsY(hDC,bDisplay);for(long i=0;i < nCount;i++)
{
// Do we need to correct distances between characters?
if(bPerformKerning)
{
if(i < nCount-1)
{
// Adjust kerning
Pair.m_nFirst=szText[i];
Pair.m_nSecond=szText[i+1];
fKerningValue=pFontInfo->m_KerningPairsArray[Pair];
}
else
fKerningValue=0.0;
}// Make sure we don’t use incorrect (zero-sized) characters…
_ASSERT(pFontInfo->m_CharactersWidthsArray[szText[i]]);// Calculate local character width
fCurrentWidth=
pFontInfo->m_CharactersWidthsArray[szText[i]]+
fKerningValue;fCurrentWidth*=
fLogPixelsY*fReferenceFont2LocalFont*fZoomFactor;CharactersWidthsArray.push_back(fCurrentWidth);
fTotalWidth+=fCurrentWidth;
}
ExtTextOut(hDC,x,y,ETO_CLIPPED,
&Rect,
m_TextLines[i]->GetTextLine(),
m_TextLines[i]->GetTextLineLength(),
m_TextLines[i]->GetWidthsArray());
This method has one disadvantage: we can use only OpenType/TrueType fonts.
I haven’t implemented print preview because I didn’t have time to do so. Source code has been written for demo purposes only and I didn’t optimize it.
You can find basic information about fonts and text in the book Windows Graphics Programming: Win32 GDI and DirectDraw by Feng Yuan.