Inserting an RTF string using StreamIn


When inserting Rich Text Formatted text into the control there are two approaches you can take. Insert the control into the text, then select it and then format it. This can result in a lot of code and not very clean code at that. The other approach is that you can format the text into a CString variable and insert that in one fell swoop. This is much faster and reduces the number of lines of code.

Step 1: Define the EditStreamCallBack() callback function

When we stream in data into the rich edit control, we have to define a callback function that is called by the control to supply the actual data. This callback function can be called repeatedly by control till the function indicates that there is no more data.

One of the arguments passed to the callback function is an application defined value. We will use this value to pass in a CString object's address. The second argument is the address of the buffer where the data is to be written by the function, the third argument specifies the number of bytes requested by the rich edit control. The final argument is pointer to a long value. The callback function should set this value to the number of bytes actually copied to the buffer. If this value is less than the number of bytes requested by the control, then the control assumes that there is no more text available and it will stop calling this function.

We have defined the EditStreamCallBack() function as a file static function. This makes the function local to the file. We can define a function with the same name in another file. We could have defined this function as a class function but it would have had to be a class static function. Note the type CALLBACK. Forgetting to specify this can be cause for major headaches.

If you are a performance freak, you'll notice that this function is not very efficient for long strings. Also, this function modifies the string. You might want to enhance this if it is important to your application. For strings less than about 4KB this function is OK since it gets called only once.
static DWORD CALLBACK EditStreamCallBack(DWORD dwCookie, LPBYTE pbBuff, LONG cb, 
					LONG *pcb)
{
	CString *pstr = (CString *)dwCookie;

	if( pstr->GetLength() < cb )
	{
		*pcb = pstr->GetLength();
		memcpy(pbBuff, (LPCSTR)*pstr, *pcb );
		pstr->Empty();
	}
	else
	{
		*pcb = cb;
		memcpy(pbBuff, (LPCSTR)*pstr, *pcb );
		*pstr = pstr->Right( pstr->GetLength() - cb );
	}
	return 0;
}

Step 2: Call StreamIn() with the right arguments

When inserting the RTF string, the information in the string should be complete otherwise it could mess up the formatting of the text in the control. That is, the string should contain the font information, the tab stops, the language, the font size etc. I won't go into the RTF format codes but the format code used in the sample code below should not be very difficult to decifer.

To build the RTF string, we use a prefix string with the preliminary information such as the font table, font size etc. We append our text to this string and at the end we add the postfix string, that completes the RTF string.

Here's the code snippet that calls the StreamIn() function. Note that in the call to StreamIn(), the first argument is a combination of SF_RTF and SFF_SELECTION. The first flag indicates that the text inserted into the rich edit control contains rich text formatting. The second flag indicates that the control should replace the selection with the inserted text. If you don't specify the SFF_SELECTION flag any previous text in the rich edit control will be cleared out.

The second argument to the function is the EDITSTREAM structure. This structure has three members. The first member is simply a value that is passed on to the callback function we defined. This member will contain the address of our string variable. The second argument is the error code returned by EditStreamCallBack() via the StreamIn() function. The last member is a pointer to EditStreamCallBack() - the callback function.

	CString rtfPrefix, rtfPostfix;
	rtfPrefix = "{\\rtf1\\ansi\\deff0\\deftab720{\\fonttbl{\\f0\\froman "
		"Times New Roman;}}\n{\\colortbl\\red0\\green0\\blue0;}\n"
		"\\deflang1033\\pard\\tx360\\tx720\\tx1080\\tx1440\\tx1800"
		"\\tx2160\\tx2520\\tx2880\\tx3240\\tx3600\\tx3960\\tx4320"
		"\\tx4680\\tx5040\\tx5400\\tx5760\\tx6120"
		"\\tx6480\\plain\\f3\\fs20 ";
	rtfPostfix = "\n\\par }";


	// The rtfString contains the word Bold in bold font.
	CString rtfString = rtfPrefix + "\\b Bold\\b0" + rtfPostfix;

	EDITSTREAM es = {(DWORD)&rtfString, 0, EditStreamCallBack};

	// richEd is the rich edit control
	richEd.StreamIn(SF_RTF | SFF_SELECTION, es);



Comments

  • Inserting an TEXT string using StreamIn (small addition to Zafir's article)

    Posted by bogdan347 on 01/02/2009 10:15am

    I suppose that Zafir developed his code on an older compiler, because on VS 2005 the code doesn't compile. That's why I made some changes to his code and replaced the "source of reading" from CString to string (a STL class which is more familiar to me): 
    
    static DWORD CALLBACK StringStreamInCallback(DWORD dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb) 
    { 
    	string* pString = reinterpret_cast(dwCookie); 
    	LPBYTE pByte = new BYTE[pString->length()];//pointer to a table of bytes (string length gives the table's size) 
    	for(unsigned int i = 0; i < pString->length(); i++) 
    		pByte[i] = (*pString)[i]; //copy string into the table of bytes 
    	if( pString->length() < cb ) 
    	{ //if the string to be read is smaller than the number of bytes requested to be read 
    	//then StringStreamInCallback is called only once 
    		*pcb = pString->length(); 
    		memcpy(pbBuff, pByte, *pcb ); //copy the table of bytes (the actually "converted" string) to buffer parameter 
    		*pString = "";//string is emptied. Beware that the string argument is modified !!! 
    	} 
    	else 
    	{ //the length of the string is bigger than the number of requested bytes 
    	//to read => StringStreamInCallback is called at least twice 
    		*pcb = cb; 
    		memcpy(pbBuff, pByte, *pcb ); 
    		string sTemp = *pString; //store string in a temp location 
    		*pString = ""; //string is emptied 
    		for(unsigned int i = cb; i < pString->length(); i++) 
    			*pString = *pString + sTemp[i];//copy the rest (the bytes that were not read) back to the string 
    	} 
    	delete[] pByte; //don't forget to delete the array on the heap! 
    	return 0; } 
    
    Then the actual writing to the rich edit control becomes: 
    string stringBuffer = "This text will appear on the richEd control\nYou can also copy a file into a string and use StringStreamInCallback"; 
    EDITSTREAM es = {reinterpret_cast(&stringBuffer), 0, StringStreamInCallback}; 
    richEd.StreamIn(SF_TEXT, es); //select TEXT mode

    Reply
  • stream - unicode support

    Posted by m m on 08/07/2008 05:39am

    CString *pstr = (CString *) dwCookie;
    if (pstr->GetLength() * sizeof(TCHAR) <= cb)
    {
    *pcb = pstr->GetLength() * sizeof(TCHAR);
     memcpy( pbBuff, (LPCTSTR)(*pstr),  ((*pcb)+1)*(sizeof (TCHAR)) );
    pstr->Empty();
    }
    else
    {
    *pcb = cb;
     memcpy( pbBuff, (LPCTSTR)(*pstr),  ((*pcb)+1)*(sizeof (TCHAR)) );
     int ncount =  pstr->GetLength() -   (     cb / (sizeof(TCHAR) )) ;
    *pstr = pstr->Right(  ncount      );
    }

    • Error :(

      Posted by break; on 10/30/2009 04:56am

      Hi, thanks for your code, only i have a problem with memcpy(), it fails and a recive a error message with memcpy!
      CODE:
      CAtlTraceCategory *CAtlAllocator::GetCategory(int iCategory) const
      {
      	if(iCategory == m_pProcess->CategoryCount())
      		return NULL;
      
      	ATLASSERT((iCategory < m_pProcess->CategoryCount()) || (iCategory == -1));
      	CAtlTraceCategory *pCategory = NULL;
      	if(iCategory >= 0)
      	{
      		BYTE *pb = reinterpret_cast(m_pProcess) + m_pProcess->MaxSize();
      		pCategory = reinterpret_cast(pb) - iCategory - 1;
      	}
      	return pCategory;
      }
      CODE END
      Error is in this line:
      BYTE *pb = reinterpret_cast(m_pProcess) + m_pProcess->MaxSize();
      
      Any idea how to solve this??
      Thanks for any help!!

      Reply
    Reply
  • How to draw a border around a paragraph

    Posted by Legacy on 02/17/2004 12:00am

    Originally posted by: Sebastian

    How can I draw a border around a paragraph? I tried something like \box \brdrs but I don't see anything in my CRichEditCtrl. Do I have to set up something before I can draw borders in this control?

    kind regards, Sebastian

    Reply
  • How to write files with rtf hyperlinks from richedit controls

    Posted by Legacy on 01/26/2004 12:00am

    Originally posted by: Nicole Jacobs

    I wrote an application in VC++ 6.0 that has richedit controls. The hyperlink detection works fine. I save the contents of the file using rtf. When the contents of the control are written to a file, they are just plain text. How do I make the file write hyperlinks properly?

    Reply
  • How to read a file correctly so no data is changed durning operation.

    Posted by Legacy on 01/16/2004 12:00am

    Originally posted by: Willem

    I hve this problem that when i open a file, sometimes the data is corrupted or read falulty so that if i, for example read an .exe file, i sometimes result with the growth of the data (5k peak with 1.38m file).

    The idea in my project is an encryption program able to encrypt any file, and it is neccessary to open the file into the rich edit so that you can edit the data easily.

    I am currenty using the copypasted version of the msdn version of file streaming into rich edit. I have tried types SF_TEXT and SF_RTF and im using RichEdit 2.0.

    Reply
  • Anybody know Zafir's contact details?

    Posted by Legacy on 12/04/2003 12:00am

    Originally posted by: Deon Ball

    Reply
  • How to increase the Stream In block size to >4k ?

    Posted by Legacy on 12/03/2003 12:00am

    Originally posted by: Deon Ball

    Does anybody know how to increase the block size ( cb = 4k )?

    the relevant section of the article follows

    "
    If you are a performance freak, you'll notice that this function is not very efficient for long strings. Also, this function modifies the string. You might want to enhance this if it is important to your application. For strings less than about 4KB this function is OK since it gets called only once.

    static DWORD CALLBACK EditStreamCallBack(DWORD dwCookie, LPBYTE pbBuff, LONG cb,
    LONG *pcb)
    {
    CString *pstr = (CString *)dwCookie;

    if( pstr->GetLength() < cb )
    {
    *pcb = pstr->GetLength();
    memcpy(pbBuff, (LPCSTR)*pstr, *pcb );
    pstr->Empty();
    }
    else
    {
    *pcb = cb;
    memcpy(pbBuff, (LPCSTR)*pstr, *pcb );
    *pstr = pstr->Right( pstr->GetLength() - cb );
    }
    return 0;
    }
    "

    Reply
  • Inserting a header and footer in every page?

    Posted by Legacy on 10/21/2003 12:00am

    Originally posted by: Aneesh

    Anybody knows how to insert Header anf Footer in in a Richedit control's every page?
    

    Reply
  • StreamOut doesn't preserve CFM_LINK settings

    Posted by Legacy on 07/12/2003 12:00am

    Originally posted by: Dan Samuel

    I just discovered CFM_LINK and am enjoying having clickable links in my CRichEditCtrl. However, when I use streamOut and streamIn to save my control contents to disk and back, all the RTF formatting is preserved except for the links.

    I imagine this is because the links are an extension and not really RTF.. but I don't care! I just want it to work! waaaaaah!

    I imagine I will have to scan the control after loading it from file and manually restore all the links. I am not using AUTOURLDETECT because I have my own link formats

    Other than this, CFM_LINK is the coolest thing ever.

    Reply
  • Rtf code for hyperlink

    Posted by Legacy on 04/25/2003 12:00am

    Originally posted by: SG

    Can someone please let me know how to denote a text as a hyperlink. I have tried all sorts of searches on the internet but couldn't find any good example.
    Please help.
    Thanks,
    sg.

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

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • A modern mobile IT strategy is no longer an option, it is an absolute business necessity. Today's most productive employees are not tied to a desk, an office, or a location. They are mobile. And your company's IT strategy has to be ready to support them with easy, reliable, 24/7 access to the business information they need, from anywhere in the world, across a broad range of communication devices. Here's how some of the nation's most progressive corporations are meeting the many needs of their mobile workers …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds