Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

RichEdit - Adding Formatted Text

5 views
Skip to first unread message

Derek J. Stutsman

unread,
Mar 27, 1998, 3:00:00 AM3/27/98
to

Hi All...this is probably really simple, but the method escapes me.

I want to add text to a TRichEdit control a line at a time, and I want to
programmatically define the text attributes such as font, color, bold, etc.
I can do it by adding the text, then selecting what I want and setting the
attributes, but that is very slow and seems horribly inefficient.

Ideally I'd like to do something line Richedit1.Lines.Add('This is a line
with one *bold* word');

I know the component works via RTF but setting RTF codes in my Lines.Add
doesn't seem to affect it. Poking in the source reveals that it only does
the RTF "decoding" when loading from a steam.

So, how can I do this?

Thanks very much,
Derek J. Stutsman
der...@dallas.net

Rene Tschaggelar

unread,
Mar 27, 1998, 3:00:00 AM3/27/98
to

You could hexdump a RTF file and have a look at
the control codes.

Rene

Derek J. Stutsman

unread,
Mar 27, 1998, 3:00:00 AM3/27/98
to

>You could hexdump a RTF file and have a look at
>the control codes.


Well, that doesn't work. The RTF codes are just ascii symbols, so hex isn't
needed. Try this: Take a richedit full of formatted text and set PlainText
:= false and do a lines.savetofile(). The resulting file will look
something like this:

{\rtf1\ansi\ansicpg1252\deff0\deftab720{\fonttbl{\f0\fswiss MS Sans
Serif;}{\f1\froman\fcharset2 Symbol;}{\f2\fswiss\fcharset1 MS Sans
Serif;}{\f3\fswiss\fcharset1 MS Sans
Serif;}}..{\colortbl\red0\green0\blue0;\red8\green0\blue0;}..\deflang1033\ho
rzdoc{\*\fchars }{\*\lchars }\pard\plain\f3\fs16 Plain \plain\f0\fs16\cf1\b
BOLD\plain\f3\fs16 Plain..\par }

Now, adding that to a TRichEdit using lines.add fails to work...it just
addes the codes and everything to the text the user sees.

Surely someone out there has done this kind of thing before...

Peter Below

unread,
Mar 28, 1998, 3:00:00 AM3/28/98
to

Derek,

here is some sample code i wrote for another question. The PutRTFSelection
method should be of some interest for you. Note that you need to feed it a
syntactically correct RTF file, complete with starting {\rtf1 and ending }
tags. I don't know if you need all the paraphernalia like the font table,
though. Experiment <g>.

Getting formatted text in and out of a TRichedit. Project needs
3 TButtons, 1 TMemo, 1 TRichEdit, 1 TColorDialog, 1 TFontDialog,
Uses RichEdit.

Type
// These declarations are wrong in richedit.pas, the stdcall is missing.
TEditStreamCallBack = function (dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): Longint; stdcall;

TEditStream = record
dwCookie: Longint;
dwError: Longint;
pfnCallback: TEditStreamCallBack;
end;

Function EditStreamInCallback(dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): Longint; stdcall;
var
theStream: TStream;
dataAvail: LongInt;
begin
theStream := TStream(dwCookie);

with theStream do begin
dataAvail := Size - Position;
if dataAvail <= cb then begin
if dataAvail <= 0 then
Result := 0
else begin
pcb := Read(pbBuff^, dataAvail);
Result := pcb;
end
end
else begin
pcb := Read(pbBuff^, cb);
Result := pcb;
end;
end;
end;

Function EditStreamOutCallback(dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): Longint; stdcall;
var
theStream: TStream;
begin
theStream := TStream(dwCookie);

with theStream do begin
If cb > 0 Then Begin
pcb := Write(pbBuff^, cb);
Result := pcb;
End
Else
Result := 0;
end;
end;

Procedure GetRTFSelection( aRichEdit: TRichEdit; intoStream: TStream );
Var
editstream: TEditStream;
Begin
With editstream Do Begin
dwCookie:= Longint(intoStream);
dwError:= 0;
pfnCallback:= EditStreamOutCallBack;
end;
aRichedit.Perform( EM_STREAMOUT, SF_RTF or SFF_SELECTION,
longint(@editstream));
End;

Procedure PutRTFSelection( aRichEdit: TRichEdit; sourceStream: TStream );
Var
editstream: TEditStream;
Begin
With editstream Do Begin
dwCookie:= Longint(sourceStream);
dwError:= 0;
pfnCallback:= EditStreamInCallBack;
end;
aRichedit.Perform( EM_STREAMIN, SF_RTF or SFF_SELECTION,
longint(@editstream));
End;

procedure TForm1.Button2Click(Sender: TObject);
Var
aMemStream: TMemoryStream;
begin
aMemStream := TMemoryStream.Create;
try
GetRTFSelection( richedit1, aMemStream );
aMemStream.Position := 0;
memo1.Lines.LoadFromStream( aMemStream );
finally
aMemStream.Free;
end;
end;

procedure TForm1.Button3Click(Sender: TObject);
Var
aMemStream: TMemoryStream;
begin
aMemStream := TMemoryStream.Create;
try
memo1.Lines.SaveToStream( aMemStream );
aMemStream.Position := 0;
PutRTFSelection( richedit1, aMemStream );
finally
aMemStream.Free;
end;
end;

procedure TForm1.RichEdit1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
If [ssCtrl] = Shift Then
Case Key of
Ord('B'): With (Sender As TRichEdit).SelAttributes Do
If fsBold In Style Then
Style := Style - [fsBold]
Else
Style := Style + [fsBold];
Ord('U'): With (Sender As TRichEdit).SelAttributes Do
If fsUnderline In Style Then
Style := Style - [fsUnderline]
Else
Style := Style + [fsUnderline];
Ord('I'): With (Sender As TRichEdit).SelAttributes Do
If fsItalic In Style Then
Style := Style - [fsItalic]
Else
Style := Style + [fsItalic];
Ord('T'): If ColorDialog1.Execute Then
(Sender As TRichEdit).SelAttributes.Color :=
ColorDialog1.Color;
Ord('F'): If FontDialog1.Execute Then
(Sender As TRichEdit).SelAttributes.Assign(
FontDialog1.Font );
End; { Case }
end;

procedure TForm1.RichEdit1KeyPress(Sender: TObject; var Key: Char);
begin
{Ctrl-I yields a #9 character, a Tab. We have to swallow that. }
If (Key = #9) and (GetKeyState( VK_CONTROL ) < 0) Then
Key := #0;
end;

You now have a simple rich text editor and can type some formatted text
into the rich edit. Select some of it and click button2. The selection is
fetched and copied as RTF text into the memo. Put the caret somewhere in
the rich edit and click button3. The RTF text from the memo is inserted at
the selection into the rich edit. To combine several snippets of RTF text
into one block one would have to remove the trailing } from block 1, the
leading {\rtf1 from block 2 and copy both together into a new block. You
can test that with a little cut and paste on the memo before you hit
button3.


Peter Below (TeamB) 10011...@compuserve.com)


Lawrence Lustig

unread,
Mar 28, 1998, 3:00:00 AM3/28/98
to

"Derek J. Stutsman" <der...@dallas.net> wrote:

>I want to add text to a TRichEdit control a line at a time, and I want to
>programmatically define the text attributes such as font, color, bold, etc.
>I can do it by adding the text, then selecting what I want and setting the
>attributes, but that is very slow and seems horribly inefficient.
>
>Ideally I'd like to do something line Richedit1.Lines.Add('This is a line
>with one *bold* word');

I've been messing with this myself, and nothing about TRichEdit seems
straightforward. Here's some stuff I think I might have figured out:

To enter text using features available from the
TRichEdit.SelAttributes property, first make sure that nothing is
selected and SelStart is at the correct position, then set
SelAttributes.Whatever, then set SelText := YourTextHere. You can
repeatedly do steps 2 and 3 of this and the text will be inserted
sequentially. If you want to use this method to start a new
paragraph, remember to add #13#10 to your last string.

To enter text using features available from TRichEdit.Paragraph, set
Paragraph.Whatever, then use Lines.Add.


-------------------------------------
Larry Lustig
http://www.pipeline.com/~nyguide/
NY-DC 2 Day Tours, NY-PHILADELPHIA 1 Day Tour,
NY-Shopping Outlet 1 Day Tour.

Larry's Opinionated Guide to New York City
(for Cheapskates): The information you need
from a real New Yorker. Available at Travel
Bookstores, or from the author
(email for information).

Antônio Augusto

unread,
Mar 31, 1998, 3:00:00 AM3/31/98
to

Button3 from this example seems to be not functional. Am I adding my own
missing?

-------------------------------------------------------

Peter Below

unread,
Mar 31, 1998, 3:00:00 AM3/31/98
to

>
> Button3 from this example seems to be not functional. Am I adding my own
> missing?

You have to connect Button3Click to the Button3 OnClick handler, of course.
I don't have the complelete originaly sample app around anymore but i it
worked <g>.

Peter Below (TeamB) 10011...@compuserve.com)


Daniel P. Stasinski

unread,
Apr 5, 1998, 4:00:00 AM4/5/98
to 10011...@compuserve.com

> here is some sample code i wrote for another question. The
> PutRTFSelection method should be of some interest for you.
> Note that you need to feed it a syntactically correct RTF file,
> complete with starting {\rtf1 and ending }

Hi Peter, I tested the code and the PutRTFSelection procedure does not
function. I also tried a few modifications based on what I saw in the
ComCtrls.pas file and none seemed to pan out. Any ideas?

Thank you,

Daniel

Brent Rose

unread,
Apr 24, 1998, 3:00:00 AM4/24/98
to

Ditto the problems with PutRTFSelection...I cannot get Button3 code to
work...it leaves the Memo box blank...?

Peter also points out the ommision of "stdcall" in the declaration of
TEditStreamCallBack in richedit.pas. I'm not sure what is required to
correct this & avoid the "calling convention differs" error. If editing
richedit.pas is required, how does one incorporate this back into the
component library in D3.02? (I tried using an edited & renamed copy of
richedit.pas in the meantime and this seemed to work...but it doesn't seem
the "right way".)

Another of Peters samples deals with streaming RT out to a Delphi string and
back again (sent to Nermi Karacabeyli). This snippet works fine:

AMemStream := TMemoryStream.Create;
try
RichEdit1.Lines.SaveToStream(AMemStream);
AMemStream.Position := 0;
SetLength(str, AMemStream.Size);
AMemStream.Read(str[1], AMemStream.Size);
finally
AMemStream.Free;
end;

You then suggest that the "inverse" can be used to read the string back into
a RichEdit or a file. I tried this:

s := 'Sample text';
TextSize := Length(s);
AMemStream := TMemoryStream.Create;
try
AMemStream.SetSize(TextSize);
AMemStream.Position := 0;
AMemStream.Write(s, TextSize);
RichEdit1.Lines.LoadFromStream(AMemStream);
finally
AMemStream.Free;
end;

...but cannot get any joy! In seems to be a similar problem to above
(maybe?? - or just my code??). What am I missing or not understanding
here??

Your help would be most appreciated. Thanks.

Peter Below

unread,
Apr 25, 1998, 3:00:00 AM4/25/98
to

In article <6hobv2$rd...@forums.borland.com>, Brent Rose wrote:
> or just my code??
>
Your code <g>. You have to use a complete rich edit "file" in the string,
for example

procedure TForm1.Button2Click(Sender: TObject);
var
s: string;
textsize: Integer;
aMemStream: TMemoryStream;
begin
S := '{\rtf1\ansi\deff0\deftab720'+
'{\fonttbl{\f0\fnil MS Sans Serif;}'+
'{\f1\fnil\fcharset2 Symbol;}{\f2\fswiss\fprq2 System;}'+
'{\f3\fnil Times New Roman;}}'+
'{\colortbl\red0\green0\blue0;}\deflang1031\pard\plain\f3\fs20'+
'{\b Sample text}\par Normal Text\par}';


TextSize := Length(s);
AMemStream := TMemoryStream.Create;
try
AMemStream.SetSize(TextSize);

AMemStream.Write(s[1], TextSize);
richedit1.plaintext := False;
AMemStream.Position := 0;
RichEdit1.Lines.LoadFromStream(AMemStream);
finally
AMemStream.Free;
end;
End;

The minimum necessary for S would be something like

S := '{\rtf1\ansi'+
'{\b Sample text}\par Normal Text\par}';

A simple variation of the above shows that PutRTFSelection *does* work, if
applied correctly:

procedure TForm1.Button2Click(Sender: TObject);
var
s: string;
textsize: Integer;
aMemStream: TMemoryStream;
begin
S := '{\rtf1\ansi'+
'{\b Sample text}\par Normal Text\par}';


TextSize := Length(s);
AMemStream := TMemoryStream.Create;
try
AMemStream.SetSize(TextSize);

AMemStream.Write(s[1], TextSize);
richedit1.plaintext := False;
AMemStream.Position := 0;
PutRTFSelection( richedit1, amemstream );
finally
AMemStream.Free;
end;
End;

The stream you feed to PutRTFSelection also needs to hold a valid rich text
file or it will fail to work. You can convert a simple text string to an
rich text string usually by simply doing a

rtstring := '{\rtf1\ansi '+aString+'}';

But note that some characters like {, }, \ have to be converted to octal
tags in aString or they will confuse the RT parser!

The problem with the wrong declaration of TEditStreamCallBack in unit
richedit is solved simply by redeclaring the type correctly before it is
used by your code:

Type


TEditStreamCallBack = function (dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): Longint; stdcall;

Peter Below (TeamB) 10011...@compuserve.com)


Brent Rose

unread,
Apr 25, 1998, 3:00:00 AM4/25/98
to

Thanks for the help! (I see that I foolishly reset the stream position
before the Write instead of AFTER it.).

At the risk of looking even more silly, I must confess I still cannot get
the "PutRTFSelection" procedure to work. (Incidentally, I HAD redeclared the
"stdcall" type, but it didn't work coz I misspelt "TEditStream" in the
record declaration below it!!)

The PutRTFSelection code definitely executes, passing once through the
EditStreamInCallback function (the return value for bytes read ("pcb")
happens to be 393 for my RT sample) - but RichEdit1 remains steadfastly
unchanged.

Applying the simple variation you suggest of replacing the line:

RichEdit1.Lines.LoadFromStream(AMemStream);

(which works perfectly!) with the line

PutRTFSelection(RichEdit1, AMemStream);

does not work (at least for me - which proves nothing of course!).
Obviously it has worked for you, but I can't reproduce it. At least one
other person has had a similar problem, but I can find no posted
response...is this just me being dumb AGAIN?


Peter Below

unread,
Apr 26, 1998, 3:00:00 AM4/26/98
to

Brent,

i can only include the actual source code parts relevant for the task as
clipped from by testbed application (which is a complete mess <g>). Unit
Richedit is in the Uses clause in the Implementation section.

Type
// These declarations are wrong in richedit.pas, the stdcall is missing.

TEditStreamCallBack = function (dwCookie: Longint; pbBuff: PByte;
cb: Longint; var pcb: Longint): Longint; stdcall;

TEditStream = record


dwCookie: Longint;
dwError: Longint;
pfnCallback: TEditStreamCallBack;
end;

Function EditStreamInCallback(dwCookie: Longint; pbBuff: PByte;


cb: Longint; var pcb: Longint): Longint; stdcall;

var
theStream: TStream;
dataAvail: LongInt;
begin
theStream := TStream(dwCookie);

with theStream do begin
dataAvail := Size - Position;
if dataAvail <= cb then begin

pcb := Read(pbBuff^, dataAvail);
Result := 0;


end
else begin
pcb := Read(pbBuff^, cb);
Result := pcb;
end;
end;
end;

Function EditStreamOutCallback(dwCookie: Longint; pbBuff: PByte;


cb: Longint; var pcb: Longint): Longint; stdcall;

procedure TForm1.Button2Click(Sender: TObject);


var
s: string;
textsize: Integer;
aMemStream: TMemoryStream;
begin
S := '{\rtf1\ansi'+
'{\b Sample text}\par Normal Text\par}';
TextSize := Length(s);
AMemStream := TMemoryStream.Create;
try
AMemStream.SetSize(TextSize);
AMemStream.Write(s[1], TextSize);
richedit1.plaintext := False;
AMemStream.Position := 0;
PutRTFSelection( richedit1, amemstream );
finally
AMemStream.Free;
end;
End;

Peter Below (TeamB) 10011...@compuserve.com)


Brent Rose

unread,
Apr 27, 1998, 3:00:00 AM4/27/98
to

Peter Below <10011...@compuserve.com> wrote in message ...


>Brent,
>
>i can only include the actual source code parts relevant for the task as
>clipped from by testbed application (which is a complete mess <g>).

Thanks Peter! Problem solved! It works beautifully!

You may note that your implementation of the function EditStreamInCallBack
in this last message is somewhat different from the original addressed to
Derek Stutsman. I guess you have multiple versions somewhere in the
"complete mess".

Thanks again for your patience!

0 new messages