I am trying to fill a DevExpress VCL TdxRichEditControl
with the string of a calculated RTF file without taking the time to save the file. So, here is a demo project:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
UnicodeString txt = "{\\rtf1\\deff0{\\fonttbl{\\f0 Calibri;}{\\f1 Arial Unicode MS;}{\\f2 Britannic Bold;}}{\\colortbl ;\\red0\\green0\\blue255 ;\\red255\\green0\\blue0 ;}{\\*\\defchp \\fs22}{\\*\\defpap \\sl275\\slmult1\\sa200}{\\stylesheet {\\ql\\sl275\\slmult1\\sa200\\fs22 Normal;}{\\*\\cs1\\fs22 Default Paragraph Font;}{\\*\\cs2\\sbasedon1\\fs22 Line Number;}{\\*\\cs3\\ul\\fs22\\cf1 Hyperlink;}{\\*\\ts4\\tsrowd\\fs22\\ql\\sl275\\slmult1\\sa200\\tscellpaddfl3\\tscellpaddl108\\tscellpaddfb3\\tscellpaddfr3\\tscellpaddr108\\tscellpaddft3\\tsvertalt\\cltxlrtb Normal Table;}{\\*\\ts5\\tsrowd\\sbasedon4\\fs22\\ql\\sl275\\slmult1\\sa200\\trbrdrt\\brdrs\\brdrw10\\trbrdrl\\brdrs\\brdrw10\\trbrdrb\\brdrs\\brdrw10\\trbrdrr\\brdrs\\brdrw10\\trbrdrh\\brdrs\\brdrw10\\trbrdrv\\brdrs\\brdrw10\\tscellpaddfl3\\tscellpaddl108\\tscellpaddfr3\\tscellpaddr108\\tsvertalt\\cltxlrtb Table Simple 1;}}{\\*\\listoverridetable}\\nouicompat\\splytwnine\\htmautsp\\sectd\\pard\\plain\\ql\\sl275\\slmult1\\sa200{\\f1\\fs28\\cf1 This is in blue }{\\b\\i\\f2\\fs40\\cf2 Red bold}\\b\\i\\fs22\\cf2\\par}";
TMemoryStream *AStream = new TMemoryStream();
AStream->WriteBuffer(txt.c_str(), txt.Length());
AStream->Position = 0;
AStream->SaveToFile("C:/Trash/Test.rtf");
AStream->Position = 0;
dxRichEditControl1->Document->InsertDocumentContent(dxRichEditControl1->Document->Range->Start, AStream, TdxRichEditDocumentFormat::Rtf);
AStream->Free();
}
I saved it to a file in this demo to see what is going wrong and found the file contains a space in every other character (so, of course, the component I was trying to fill with this data failed to display anything). When I look at the file, this is the type of stuff I see:
{ \ r t f 1 \ d e f f 0 { \ f o n t t b l { \ f 0 C a l i b r i ; } { \ f 1 A r i a l U n i c o d e M S ; } { \ f 2 B r i t a n n i c B o l d ; } } { \ c o l
I want to stick with storing txt variable as a UnicodeString
, because the real project has a class that calculates that string, but any ideas how to fix this issue? I realize I could probably do something time consuming like loop through and eliminate every other character, but I want some efficient solution.
RTF is a 7-bit ASCII format, so you should not be treating it as UTF-16 at all. The correct solution is to simply change the UnicodeString
to an AnsiString
:
AnsiString txt = ...;
The "extra spaces" are due to 0x00
bytes being added when the 7-bit ASCII characters are extended to 16-bit values in UTF-16.
Besides, you are not even writing the UnicodeString
to the TMemoryStream
correctly anyway. Since WriteBuffer()
deals with raw bytes, not string characters, you would need to multiply txt.Length()
by sizeof(System::WideChar)
to get the correct byte count:
UnicodeString txt = ...;
...
AStream->WriteBuffer(txt.c_str(), txt.Length() * sizeof(WideChar));
If you change to AnsiString
, you don't need to multiply since sizeof(AnsiChar)
is 1.
But, if you are set on sticking with UnicodeString
, then at least use TStringStream
instead, so that you can specify a byte encoding for it:
UnicodeString txt = ...;
TStringStream *AStream = new TStringStream(txt, TEncoding::ASCII);
...
Also, since the stream is created with new
, you should change AStream->Free();
to delete AStream;
instead. NEVER call TObject::Free()
directly in C++, that is a Delphi idiom. The C++ way to free something that is allocated with new
is delete
. It will call the TObject
destructor as expected, just as TObject::Free()
does.