Wrap-Around Draw Function for Printing Text

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;
    	 }
    	}
    	


Comments

  • Sorry i Forget the Email

    Posted by Seu_why on 01/15/2006 09:14pm

    Seu_why@msn.com

    Reply
  • hi

    Posted by Seu_why on 01/15/2006 09:13pm

    Will you kind enough to send me the source code?

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds