Msftedit seems to be inconsistant handling paragaph marker (\par) at end of file

508 Views Asked by At

I have implemented the use of cricheditctrl to concatinate rtf text and I ran into an issue with \par at the end of the file. Wordpad uses the same generator and does the same thing. (Msftedit 5.41.21.2510).

If I, where wtrf is a cricheditctrl:

const char*  header = "this is a test header\r\n";
wrtf.SetWindowTextA(header);

The resultant rtf is:

{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 System;}}
{\*\generator Msftedit 5.41.21.2510;}\viewkind4\uc1\pard\b\f0\fs20 this is a test header\par
\par}

Two \par at the end.

In the bigger picture, I'm doing my own stuff with the rtf content. If I don't compose with a double \par at the end, doing something like:

std::string dest(_RichEditPreamble);
dest+= std::string("\\cf1 this is a test\\par\\par}";
SetRichText(wrtf,dest.c_str());
wrtf.SetSel(-1, -1);   // Select last character
SetRichText(wrtf, more_rtf, SF_RTF | SFF_SELECTION);

I won't get a paragraph separator between the two entries. They will butt right up to each other. In word pad if I enter a simple:

test

with one newline, I get:

...\viewkind4\uc1\pard\sa120\cf1\f0\fs24 test\par
\f1\par
}

So, at least, this is always consistent on my machine. But I can find no talk about it in the Word 2007: Rich Text Format (RTF) Specification, version 1.9.1.

My concern is that this is not constant behavior and I may get different results on other machines. And then, maybe I have missed something about how to properly end an RTF document. I did search the heck out of this. Thanks.

UPDATE : And I'm sorry for the image, but I think it helps. It is only getting more confusing.

enter image description here So I'm pulling content from a database and you can see the content is:

{rtf_stuff ... content\par}

And into the top window, and you can see the paragraph marker is working, just one.

sel= GetRichText( re, SF_RTF );
std::ofstream ts(R"(C:\cpp\ReserveAnalyst_14\StockCommentParser\test.txt)");
ts << sel;

And test.txt has:

{\rtf1\stuff ... asphalt sealing.\par
\par
}

Now there are two \par. And in the second RTF window where I place the data with:

SetRichText( pCommentFrm->GetRichCtrl( ), text, SF_RTF | SFF_SELECTION ); //todo ??

I end up with two paragraphs! (the second rtf window)

So, just in case, this is the call back that I've used for 20 years:

DWORD CALLBACK EditStreamCallBack(
    DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG *pcb )
{
    _afxRichEditStreamCookie* pCookie = (_afxRichEditStreamCookie*)dwCookie;
    CArchive& ar = pCookie->m_ar;
    DWORD dw = 0;
    *pcb = cb;
    TRY
    {
        if ( ar.IsStoring( ) )
        ar.GetFile( )->Write( pbBuff, cb );
        else
            *pcb = ar.GetFile( )->Read( pbBuff, cb );
    }
        CATCH( CFileException, e )
    {
        *pcb = 0;
        pCookie->m_dwError = (DWORD)e->m_cause;
        dw = 1;
        e->Delete( );
    }
    AND_CATCH_ALL( e )
    {
        *pcb = 0;
        pCookie->m_dwError = -1;
        dw = 1;
        e->Delete( );
    }
    END_CATCH_ALL
        return dw;
}

UPDATE2 :Now I have to believe it is a flaw in the control. I was seeing it but it was not registering in my mind. So with this:

std::string source1(_RichEditPreamble);
source1 += "\\cf1 test 1\\par}";
SetRichText(wrtf,source1.c_str());
std::string source2(_RichEditPreamble);
source2 += "\\cf0 test 2\\par";
wrtf.SetSel(-1, -1);   // Select last character
SetRichText(wrtf, source2.c_str(), SF_RTF | SFF_SELECTION);

auto result = GetRichText(wrtf);
std::ofstream ts("..\\rtf_io.rtf");
ts << result;

The result in the file is:

{\rtf1\,,,\viewkind4\uc1\pard\sa120\cf1\f0\fs24 test 1\cf2 test 2\cf1\par}

The wrtf.SetSel(-1, -1); is placing the insertion point in front of the last \par, not after it. The clue is the last par has a color tag of \cf1 from the first insertion. And in this case it is dropping one of my \par where in the previous case it is not so it only looks like I'm getting extra \par. It is driving me crazy! :)

1

There are 1 best solutions below

0
On BEST ANSWER

After some testing and working with the CRichEditCtrl, I've found that it is not made to concatenate RTF docs. Even with a SetSel(-1,-1), it will treat the insertion just as that, an insertion. What that means is all the characteristics of the text above the insertion are appended to the end of the doc. For me, I needed a true append where what characteristics end in the insertion would sit at the end of the doc. I want the user to get what they see if they were to add more content to the doc. I did come up with what I think will be an adequate hack. It may not always work but at least it should not wind up in an exception.

BOOL AppendRichText( CRichEditCtrl& rtf, LPCTSTR buf )
{
    rtf.SetSel(-1, -1);
    if( ! SetRichText(rtf,buf, SF_RTF | SFF_SELECTION))
        return FALSE;
    auto buffer = GetRichText(rtf);
    char* che= buffer.get();
    for(; *che; ++che);//to end
    char* ch= che;
    for(; *ch != ' '; --ch);//back to first space
    for(; *ch != '\\'; ++ch);//then to first '\', assumes not \\,\},\{ for now
    if( ch + 10 > che )
        return FALSE;//but it should fit....
    auto re = R"(\par\par})"; // the replacement
    for( size_t i= 0; i < 10; ++i)
        *ch++ = *re++;
    return SetRichText(rtf,buffer.get());
}

This removes any formatting from the end of the doc and leaves only a couple of \par

The other thing I learned that follows from the title of this post is that to end a document with a paragraph marker, you must end the document with two of the \par control words. I rechecked the 2007: Rich Text Format (RTF) Specification, version 1.9.1.. Nowhere could I find any distinction about ending a document with a \par. And that a single \par has no paragraph property, it take two for it to work at the end of a doc. I checked and Wordpad, MS Word, and Open Office all do it.

I have put a copy of my console test project here and here There is an OLE windowless CRichEditCtrl in use here that you may find useful.