Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Draw text with largest possible font in rectangle

Status
Not open for further replies.

GiantDaddY

Programmer
Sep 21, 2001
9
BE
I want to reserve space ( a rectangle )on my document to print text in, I would want the text 2 b in the largest possible font possible for that text and in the specific rectangle, I don't want to perform a loop where I create a font, check if the text can fit the rectangel with that font and if not go 2 the next font ...

Is there another way 2 make this happen ?

Thanks very much !!
 
No, you can't do it without iteration, in my opinion. On the other hand, I solved this not quite trivial problem some years ago. The code tries for an initial correct value, and usually has the right value after at most one iteration. It also handles cases like italic, where you have to take the slant into account. Here is the MFC class which does it:

CHCR.h

class CHCR
{
public:
CHCR();
CRect Create(CString strText, CRect& rRect, CString strFontName,
int nWeight, BOOL bItalic, BOOL bUnderline,
CDC* pDC, BOOL bCreateRegion = FALSE, int nBGMode = TRANSPARENT);
protected:
private:
CString m_strOldText;
CString m_strOldFont;
CRect m_rectOld, m_rectText;
int m_nOldWeight;
BOOL m_bOldItalic;
BOOL m_bOldUnderline;
CRgn m_rgn;
CFont m_font;
int m_nStartAdjust;
int m_x, m_y;
};

CHCR.cpp

// CHCR.cpp CHCR class implementation
#include "StdAfx.h"
#include "CHCR.h"

// Constructor
CHCR::CHCR() {}

// Creator. Outputs text to Dc or creates a clipping region within DC.
// The text is matched to the width and height of the supplied rectangle.
CRect CHCR::Create(CString strText, CRect& rRect, CString strFontName,
int nWeight, BOOL bItalic, BOOL bUnderline,
CDC* pDC, BOOL bCreateRegion, int nBGMode)
{

if (rRect.Height()<1 || rRect.Width()<10) return CRect();

if ((strText.GetLength()<1)||(strFontName.GetLength()<1)) return CRect();

// Only create new mask if parameters changed
if ((strText!=m_strOldText)||(rRect!=m_rectOld)||(strFontName!=m_strOldFont)||
(m_nOldWeight!=nWeight)||(m_bOldItalic!=bItalic)||(m_bOldUnderline!=bUnderline))
{
m_strOldText = strText;
m_rectOld.CopyRect(rRect);
m_strOldFont = strFontName;
m_nOldWeight = nWeight;
m_bOldItalic = bItalic;
m_bOldUnderline = bUnderline;

// Create the font with character size according to window size
BOOL bOK = FALSE;
CSize size;
CFont* pFontOld;
int dec = 0;
BOOL bStrikeOut = FALSE;
int nWeight1 = nWeight?FW_BOLD:FW_NORMAL;
int nCharHeight;
int nChars = strText.GetLength();
int nCharWidth = (rRect.Width()+nChars-1)/nChars;

int ttry = 1;
while (!bOK)
{
nCharHeight = rRect.Height();
m_font.DeleteObject();
m_font.CreateFont(nCharHeight, nCharWidth, 0, 0, nWeight1, bItalic, bUnderline, bStrikeOut,
ANSI_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS,
DRAFT_QUALITY, FF_DONTCARE|DEFAULT_PITCH, strFontName);
pFontOld = pDC->SelectObject(&m_font);
size = pDC->GetTextExtent(strText);
TEXTMETRIC tm;
pDC->GetTextMetrics(&tm);
ABC abc;
pDC->GetCharABCWidths(strText[0], strText[0], &abc);
m_nStartAdjust = abc.abcA;
int last = strText.GetLength()-1;
pDC->GetCharABCWidths(strText[last], strText[last], &abc);
size += CSize(-(abc.abcC+m_nStartAdjust), 0);

bOK = size.cx<=rRect.Width();
if (!bOK)
{
dec = (size.cx-rRect.Width())/nChars;
dec = (dec<1)?1:dec;
dec = (dec>5)?5:5;
nCharWidth -= dec;
}
}

// Center the text in window
m_x = rRect.left+(rRect.Width()-size.cx)/2;
m_y = rRect.top+(rRect.Height()-size.cy)/2;
m_rectText = CRect(CPoint(m_x, m_y), size);

// Adjust display start
m_x -= m_nStartAdjust;

// Optionally create a path from characters
if (bCreateRegion)
{
// Create a path from the text
pDC->SetBkMode(nBGMode);
pDC->BeginPath();
pDC->TextOut(m_x, m_y, strText);
pDC->EndPath();
pDC->SelectObject(pFontOld);

// Create a clipping region from path if bCreateRegion set
CRect rectRgn;
m_rgn.DeleteObject();
m_rgn.CreateFromPath(pDC);
m_rgn.GetRgnBox(&rectRgn);
}
}

if (bCreateRegion)
pDC->SelectClipRgn(&m_rgn);
else
{
CFont* pFontOld = pDC->SelectObject(&m_font);
pDC->SetBkMode(nBGMode);
pDC->TextOut(m_x, m_y, strText);
pDC->SelectObject(pFontOld);
//pDC->FrameRect(CRect(m_rectText.left, m_rectText.top, m_rectText.right, m_rectText.bottom) ,&CBrush(RGB(0,0,0))); // Test only!
}

return m_rectText;
}

It actually outputs the text to the supplied CDC (or optionally creates a clipping region for the text).

You use it like this:

CRect cr(200,10,400,40);
CString text(&quot;Hey!&quot;);
CString font(&quot;Arial&quot;);
CHCR chcr;
chcr.Create(text,
cr,
font,
FW_BOLD,
TRUE,
FALSE,
&dc,
FALSE);

If you aren't using MFC, at least you have the basic logic to solve the problem. :) I just can't help it, I like MS...:)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top