Search code examples
c++winapitextrtfrichedit

How to find runs of bold text inside of a RichEdit control?


I can obviously do it one character at a time using EM_GETCHARFORMAT, but it is extremely slow.

One idea is to somehow use the ITextDocument/ITextFont interfaces, the other is to use the EM_STREAMOUT message and manually parse the RTF. But I can't decide which approach is better and am very fuzzy on the implementation details. Will appreciate any help, thanks!


Solution

  • I've found a solution that satisfies me and think will share it with you:

    The ITextRange interface contains a very useful method Expand which can be used to find continuous runs of constant character (tomCharFormat) and paragraph (tomParaFormat) formatting.

    Here is some sample code (warning: code is a proof-of-concept spaghetti without any error handling, apply refactoring as needed):

        // Get necessary interfaces
        IRichEditOle* ole;
        SendMessage(hwndRichEdit, EM_GETOLEINTERFACE, 0, (LPARAM)&ole);
    
        ITextDocument* doc;
        ole->QueryInterface(__uuidof(ITextDocument), (void**)&doc);
    
        long start = 0;
    
        // Get total length:        
        ITextRange* range;
        doc->Range(start, start, &range);
        range->Expand(tomStory, NULL);
        long eof;
        range->GetEnd(&eof);
    
        // Extract formatting:
    
        struct TextCharFormat { long start, length; DWORD effects; }
        std::vector<TextCharFormat> fs;
    
        while(start < eof - 1)
        {
            doc->Range(start, start, &range);
    
            long n;
            range->Expand(tomCharFormat, &n); // <-- Magic happens here
    
            ITextFont* font;
            range->GetFont(&font);
    
            DWORD effects = 0;
            long flag;
    
            font->GetBold(&flag);
            if (flag == tomTrue) effects |= CFE_BOLD;
    
            font->GetItalic(&flag);
            if (flag == tomTrue) effects |= CFE_ITALIC;
    
            font->GetUnderline(&flag);
            if (flag == tomSingle) effects |= CFE_UNDERLINE;
    
            font->GetStrikeThrough(&flag);
            if (flag == tomTrue) effects |= CFE_STRIKEOUT;
    
            if (effects)
            {
                TextCharFormat f = { start, n, effects };
                fs.push_back(f);
            }
            start += n;
        }