Wrap-Around Draw Function for Printing Text

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Why did I write this function? I basically had an application where I needed to
print the contents of a Listbox control and I couldn’t use the built-in doc/view printing.
Now this data had fields with widths longer than the field width I
could allow for each column. So I thought I would use the DrawText to do a wrap
around print. Then I spent 1 1/2 days around DrawText, because I was lazy.
Being left with no option, I wrote this fn using little bit of code sample
from Drawtext itself. (I was printing into a MetaFile DC for PrintPreview
Implementation. I guess DrawText dint like it)

MAX_NO_OF_LINES_PER_FIELD is the Maximum no of lines of you allow
for the text in case of wrapping – I made it 2, a reasonable count. But if you
want you can provide the above #define as an additional argument or change it and
print as many lines of wrap as possible.

Now a short example: if you had the fist column a persons name, which on
truncating makes no much sense you may want to wrap around

Name Sex
Cindy C  
rawford Female
Linda T  
ripp Female

Note how “r” in Crawford is printed the way it is wrapped around. This is
how the function works.


//This array contains the character widths,
//used by PrintStringInRect
int m_aiCharWidths[256];

//This is set each time when the
//OnPrint is being called
CDC* m_pDC;

//The char width
int m_iCharWidth;

// This variable is going to
// contain char height
int m_iHeightText;

void CMyView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
//temporarily store DC for the use of
//other member functions
m_pDC = pDC;

//Get the charwidths of all ascii characters
m_pDC -> GetCharWidth(0, 255, m_aiCharWidths);

//Get the text metrics
m_pDC ->GetTextMetrics(&m_TextMetric);

//This is the height of the Text
m_iHeightText =
m_TextMetric.tmHeight+m_TextMetric.tmExternalLeading;

//This is the width of the text (or you
//can very well take the width of ‘w’
//from m_aiCharWidths)
m_iCharWidth = (m_TextMetric.tmAveCharWidth
+ m_TextMetric.tmMaxCharWidth)/2;
}


void CMyView ::PrintStringInRect(CString& strText, CRect* pRect)
{
char* lpsz = NULL;
int iNoOfLines=1;

int iRectLeft = pRect -> left;

int iMaxCharCount = strText.GetLength();
if(0 >= iMaxCharCount) return;

try
{
lpsz = new char[iMaxCharCount+1];
}
catch(CMemoryException* e)
{
e -> ReportError();
e -> Delete();
}

strcpy(lpsz,strText);
int iCharCurrentCount=0;
int iOffset=0;

while(iCharCurrentCount < iMaxCharCount) { if(lpsz[iOffset] <= 0xFF) { if(pRect->right
> iRectLeft + m_aiCharWidths[(BYTE)lpsz[iOffset]])
{
iRectLeft += m_aiCharWidths[(BYTE)lpsz[iOffset]];
iOffset++;
}
else
{
//print 1 character less, now I am sure no character
//caught the corner
iOffset–;

m_pDC -> TextOut(pRect -> left,pRect->top,lpsz,iOffset);
pRect->top -= m_iHeightText;
iRectLeft = pRect -> left;
lpsz += iOffset;
iOffset=0;

//minus 1 for the extra increment from the loop, 1 for
//the space of a //character which is left out
iCharCurrentCount-=2;

iNoOfLines++;
if(iNoOfLines > MAX_NO_OF_LINES_PER_FIELD)
iCharCurrentCount = iMaxCharCount;
}
}
else
{
if(pRect->right > iRectLeft + m_iCharWidth)
{
iRectLeft += m_iCharWidth;
iOffset++;
}
else
{
//print 1 character less, now I am sure no
//character caught the corner
iOffset–;

m_pDC -> TextOut(pRect->left,pRect->top,lpsz,iOffset);
pRect->top -= m_iHeightText;
iRectLeft = pRect -> left;
lpsz += iOffset;
iOffset=0;

//minus 1 for the extra increment from the loop, 1
//for the space of a //character which is left out
iCharCurrentCount-=2;

iNoOfLines++;
if(iNoOfLines > MAX_NO_OF_LINES_PER_FIELD)
iCharCurrentCount = iMaxCharCount;
}
}
iCharCurrentCount++;
}
if(iOffset)
m_pDC -> TextOut(pRect -> left,pRect->top,lpsz,iOffset);
}

Again the variables m_iCharWidth, m_iHeightText can be used for your
other calculations as to the no of lines in a page, width of the page, etc.

How I decide whether I have wrapped the text around (for adjusting for the Next line)?

While printing a line I keep a flag whether ever I needed to use a wrap, how do
I set the value of this flag?


UINT uCount;

UCount is the Number of characters wide you want each field to be.
So field width = Ucount* m_iCharWidth

You can manage it several ways, but I do it by maintaining an array of how
many characters wide each column is, in each report.


//so if the text takes more space than allowed
//uCount* m_iCharWidth – you need to wrap around
m_bWrap = (m_pMetaDC->GetTextExtent(strText)).cx
> uCount * m_iCharWidth);

Now the next part of this post:

If a few of you ever use this post, there will be a frequently asked question for
sure, the size of the characters and widths for different screen resolutions and
drivers and monitors, between print and print preview. Well I will briefly give an
insight to my solution to avoid those problems


  1. I was not using DOC-VIEW architecture for printing.

  2. I was basing my print by writing into a metafile and then playing it back into the DC provided by OnPrint when it has to be printed or previewed. This method was used to be able to implement Print Preview functionality.

  3. I used CDC*pDC provided by OnPrint function only to determine the size of the page and its co-rdinates.

  4. For creating the metafile and for setting its attribute DC and for all other purposes I used the Printer DC created using

  5. CDC rDC;
    if(AfxGetApp()->CreatePrinterDC(rDC))
    m_pDC = &rDC;

    The reasons for if statement is that, we wont get a printer DC if the machine
    dont have a printer configured to it. This approach assured me that Print
    Preview and Print functionality’s worked identically. WYSIWYG, for all types
    of printers.

  6. Below given is how I retrieve the dimensions of the screen or printer paper and I
    do it only for the first page in case of printing and for every page in case of Print
    Preview.

  7. CRect m_originalRect;

    void CMyView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
    {
    //if its preview or if Ist page printing find
    //screen coordinates
    if(pInfo->m_bPreview || 0 == m_nPage)
    {
    CPoint point;
    //find the device offsets while writing
    point.y = pDC->GetDeviceCaps(PHYSICALOFFSETY);
    point.x = pDC->GetDeviceCaps(PHYSICALOFFSETX);

    pDC->DPtoLP(&point);
    pInfo->m_rectDraw.bottom -= point.y;
    pInfo->m_rectDraw.right -= point.x;

    //save the original rectangle before we
    //perform all sorts of header/footer adjustments
    m_originalRect = pInfo->m_rectDraw;
    }
    }


More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read