Msftedit 似乎在处理文件末尾的段落标记 (\par) 时不一致

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

我已经实现了使用 cricheditctrl 来连接 rtf 文本,并且我 运行 成为文件末尾 \par 的问题。写字板使用相同的生成器并执行相同的操作。 (Msftedit 5.41.21.2510).

如果我,其中 wtrf 是一个 cricheditctrl:

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

生成的 rtf 是:

{\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}

最后两个\par

总的来说,我正在用 rtf 内容做我自己的事情。如果我最后不使用双 \par 组合,则执行如下操作:

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

我不会在两个条目之间使用段落分隔符。他们会互相顶撞。如果我输入一个简单的字板:

test

用一个换行符,我得到:

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

所以,至少,这在我的机器上始终是一致的。但是我在 Word 2007: Rich Text Format (RTF) Specification, version 1.9.1.

中找不到关于它的讨论

我担心的是这不是恒定的行为,我可能会在其他机器上得到不同的结果。然后,也许我错过了一些有关如何正确结束 RTF 文档的内容。我确实搜索了这个。 谢谢。

更新 : 我对图片感到抱歉,但我认为它有帮助。只会越来越混乱。

所以我从数据库中提取内容,你可以看到内容是:

{rtf_stuff ... content\par}

然后进入顶部 window,您可以看到段落标记正在工作,只有一个。

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

并且 test.txt 有:

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

现在有两个\par。在第二个 RTF window 中,我将数据放置在:

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

我结束了两段! (第二个 rtf window)

所以,为了以防万一,这是我使用了 20 年的回调:

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 :现在我不得不相信这是控件中的一个缺陷。我看到了它,但它并没有记在我的脑海里。所以有了这个:

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;

文件中的结果是:

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

wrtf.SetSel(-1, -1); 将插入点放在最后一个 \par 的前面,而不是放在它后面。线索是最后一个 par 在第一次插入时有一个 \cf1 的颜色标签。在这种情况下,它会丢弃我的 \par 之一,而在之前的情况下它不是,所以它看起来只是我得到了额外的 \par。这让我发疯! :)

经过一些测试和使用 CRichEditCtrl,我发现它不是用来连接 RTF 文档的。即使使用 SetSel(-1,-1),它也会将插入视为插入。这意味着插入上方文本的所有特征都附加到文档的末尾。对我来说,我需要一个真正的追加,其中插入结束的特征将位于文档的末尾。如果用户要向文档中添加更多内容,我希望用户能够看到他们看到的内容。我确实想出了我认为足够的技巧。它可能并不总是有效,但至少不应以异常结束。

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

这会删除文档末尾的所有格式,只留下几个 \par

我从这个 post 的标题中了解到的另一件事是,要以段落标记结束文档,您必须以两个 \par 控制词结束文档。我重新检查了 2007: Rich Text Format (RTF) Specification, version 1.9.1.。我在任何地方都找不到关于以 \par 结束文档的任何区别。而且单个 \par 没有段落 属性,它需要两个才能在文档末尾工作。我检查了一下,写字板、MS Word 和 Open Office 都可以。

我已经复制了我的控制台测试项目here and here这里使用了一个 OLE 无窗口 CRichEditCtrl,您可能会发现它很有用。