在 TRichEdit 中加载长 RTF 文本不起作用
Loading long RTF-Text in TRichEdit does not work
如果长 RTF 序列(例如 150 000 个字符)流式传输到 TRichEdit
控件(在 XE4 中),则控件不显示文本,而是显示原始 RTF 代码:
{\rtf1\ansi\ansicpg1252\deff0...
怎么了?
procedure TForm1.Button1Click(Sender: TObject);
var
RtfText: string;
Stream: TStringStream;
begin
RtfText := GenerateRtfText();
Stream := TStringStream.Create(RtfText);
try
RichEdit2.PlainText := False;
RichEdit2.Lines.LoadFromStream(Stream); //<--- ERROR: RichEdit displays raw RTF-Code
// if RtfText is too long
if StartsText('{\rtf', RichEdit2.Lines.Text) then
begin
ShowMessage('Oh no, not converted!');
//WORKAROUND: 2nd try seems to work...
//Stream.Position := 0;
//RichEdit2.Lines.LoadFromStream(Stream);
end;
finally
Stream.Free;
end;
end;
例如具有以下RTF生成功能:
function TForm1.GenerateRtfText: string;
var
I: Integer;
Stream: TStringStream;
const
DOES_WORK_COUNT = 10000;
DOES_NOT_WORK_COUNT = 15000;
begin
//Fill
RichEdit1.Lines.BeginUpdate;
try
//for I := 0 to DOES_WORK_COUNT do
for I := 0 to DOES_NOT_WORK_COUNT do
RichEdit1.Lines.Add(IntToStr(I));
finally
RichEdit1.Lines.EndUpdate;
end;
//Convert to RTF
Stream := TStringStream.Create;
try
RichEdit1.Lines.SaveToStream(Stream);
Result := Stream.DataString;
finally
Stream.Free;
end;
end;
已编辑:即使复制和粘贴也无法正常工作:
这是我所做的:
- 我将 RichEdit1 的生成内容(第 1..15000 行,编号为 1..15000)复制到 notpad.exe 中以删除任何 RTF
- 我把记事本里的内容复制到RichEdit2里
结果:
- 只正确显示了12773行。最后一行只有
12
- 如果我尝试将另一个字符添加到 TRichEdit 中,什么也不会发生
- 如果我删除 10 个字符(每个退格键),之后我可以恰好添加 10 个字符...
TRichEdit 是否有隐藏的字符限制?
Rich Edit 控件有文本限制。
尝试使用 EM_EXLIMITTEXT
消息,它设置了用户可以键入或粘贴到 Rich Edit 控件中的文本数量上限。此消息还限制了在流式传输 RTF (PlainText = False
) 时可以流式传输到 Rich Edit 控件中的文本量。但在串流纯文本时不限制控制
例如:
const
RE_MAX_TEXT_SIZE = 256000;
SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, RE_MAX_TEXT_SIZE);
或:
SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, FFFFFF0);
对于 TRichEditStrings.LoadFromFile()
中实施的最大限制:RichEdit.DoSetMaxLength(FFFFFF0);
但是,DoSetMaxLength()
在源代码中 不正确 使用,因为它应该在 在 加载流之前被调用。此外,DoSetMaxLength()
根本不用于 TRichEditStrings.LoadFromStream()
。 Remy 在他的回答评论中。
除了kobik说的:
TRichEdit.Lines.LoadFromStream()
在内部使用 EM_STREAMIN
。当 TRichEdit.PlainText
为 false 时,LoadFromStream()
将首先尝试以 RTF 格式加载流数据,如果遇到任何错误,它将以纯文本格式重新加载流数据。这就是您看到原始 RTF 代码出现的原因。
RTF 是一种基于 ASCII 的格式,因此 LoadFromStream()
需要 8 位 RTF 数据(在 PlainText=True
的情况下,将尝试将其转换为 Unicode)。尝试使用 AnsiString
和 TMemoryStream
而不是 (Unicode)String
和 TStringStream
作为您的 RTF 流。
type
TReadOnlyMemoryBufferStream = class(TCustomMemoryStream)
public
constructor Create(APtr: Pointer; ASize: NativeInt);
function Write(const Buffer; Count: Longint): Longint; override;
end;
constructor TReadOnlyMemoryBufferStream.Create(APtr: Pointer; ASize: NativeInt);
begin
inherited Create;
SetPointer(APtr, ASize);
end;
function TReadOnlyMemoryBufferStream.Write(const Buffer; Count: Longint): Longint;
begin
Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
RtfText: AnsiString;
Stream: TReadOnlyMemoryBufferStream;
begin
RtfText := GenerateRtfText();
Stream := TReadOnlyMemoryBufferStream.Create(PAnsiChar(RtfText), Length(RtfText));
try
RichEdit2.PlainText := False;
RichEdit2.Lines.LoadFromStream(Stream);
...
finally
Stream.Free;
end;
end;
function TForm1.GenerateRtfText: AnsiString;
var
I: Integer;
Stream: TMemoryStream;
const
DOES_WORK_COUNT = 10000;
DOES_NOT_WORK_COUNT = 15000;
begin
//Fill
RichEdit1.Lines.BeginUpdate;
try
//for I := 0 to DOES_WORK_COUNT do
for I := 0 to DOES_NOT_WORK_COUNT do
RichEdit1.Lines.Add(IntToStr(I));
finally
RichEdit1.Lines.EndUpdate;
end;
//Convert to RTF
Stream := TMemoryStream.Create;
try
RichEdit1.PlainText := False;
RichEdit1.Lines.SaveToStream(Stream);
SetString(Result, PAnsiChar(Stream.Memory), Stream.Size);
finally
Stream.Free;
end;
end;
如果长 RTF 序列(例如 150 000 个字符)流式传输到 TRichEdit
控件(在 XE4 中),则控件不显示文本,而是显示原始 RTF 代码:
{\rtf1\ansi\ansicpg1252\deff0...
怎么了?
procedure TForm1.Button1Click(Sender: TObject);
var
RtfText: string;
Stream: TStringStream;
begin
RtfText := GenerateRtfText();
Stream := TStringStream.Create(RtfText);
try
RichEdit2.PlainText := False;
RichEdit2.Lines.LoadFromStream(Stream); //<--- ERROR: RichEdit displays raw RTF-Code
// if RtfText is too long
if StartsText('{\rtf', RichEdit2.Lines.Text) then
begin
ShowMessage('Oh no, not converted!');
//WORKAROUND: 2nd try seems to work...
//Stream.Position := 0;
//RichEdit2.Lines.LoadFromStream(Stream);
end;
finally
Stream.Free;
end;
end;
例如具有以下RTF生成功能:
function TForm1.GenerateRtfText: string;
var
I: Integer;
Stream: TStringStream;
const
DOES_WORK_COUNT = 10000;
DOES_NOT_WORK_COUNT = 15000;
begin
//Fill
RichEdit1.Lines.BeginUpdate;
try
//for I := 0 to DOES_WORK_COUNT do
for I := 0 to DOES_NOT_WORK_COUNT do
RichEdit1.Lines.Add(IntToStr(I));
finally
RichEdit1.Lines.EndUpdate;
end;
//Convert to RTF
Stream := TStringStream.Create;
try
RichEdit1.Lines.SaveToStream(Stream);
Result := Stream.DataString;
finally
Stream.Free;
end;
end;
已编辑:即使复制和粘贴也无法正常工作:
这是我所做的:
- 我将 RichEdit1 的生成内容(第 1..15000 行,编号为 1..15000)复制到 notpad.exe 中以删除任何 RTF
- 我把记事本里的内容复制到RichEdit2里
结果:
- 只正确显示了12773行。最后一行只有
12
- 如果我尝试将另一个字符添加到 TRichEdit 中,什么也不会发生
- 如果我删除 10 个字符(每个退格键),之后我可以恰好添加 10 个字符...
TRichEdit 是否有隐藏的字符限制?
Rich Edit 控件有文本限制。
尝试使用 EM_EXLIMITTEXT
消息,它设置了用户可以键入或粘贴到 Rich Edit 控件中的文本数量上限。此消息还限制了在流式传输 RTF (PlainText = False
) 时可以流式传输到 Rich Edit 控件中的文本量。但在串流纯文本时不限制控制
例如:
const
RE_MAX_TEXT_SIZE = 256000;
SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, RE_MAX_TEXT_SIZE);
或:
SendMessage(RichEdit1.Handle, EM_EXLIMITTEXT, 0, FFFFFF0);
对于 TRichEditStrings.LoadFromFile()
中实施的最大限制:RichEdit.DoSetMaxLength(FFFFFF0);
但是,DoSetMaxLength()
在源代码中 不正确 使用,因为它应该在 在 加载流之前被调用。此外,DoSetMaxLength()
根本不用于 TRichEditStrings.LoadFromStream()
。 Remy
除了kobik说的:
TRichEdit.Lines.LoadFromStream()
在内部使用 EM_STREAMIN
。当 TRichEdit.PlainText
为 false 时,LoadFromStream()
将首先尝试以 RTF 格式加载流数据,如果遇到任何错误,它将以纯文本格式重新加载流数据。这就是您看到原始 RTF 代码出现的原因。
RTF 是一种基于 ASCII 的格式,因此 LoadFromStream()
需要 8 位 RTF 数据(在 PlainText=True
的情况下,将尝试将其转换为 Unicode)。尝试使用 AnsiString
和 TMemoryStream
而不是 (Unicode)String
和 TStringStream
作为您的 RTF 流。
type
TReadOnlyMemoryBufferStream = class(TCustomMemoryStream)
public
constructor Create(APtr: Pointer; ASize: NativeInt);
function Write(const Buffer; Count: Longint): Longint; override;
end;
constructor TReadOnlyMemoryBufferStream.Create(APtr: Pointer; ASize: NativeInt);
begin
inherited Create;
SetPointer(APtr, ASize);
end;
function TReadOnlyMemoryBufferStream.Write(const Buffer; Count: Longint): Longint;
begin
Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
RtfText: AnsiString;
Stream: TReadOnlyMemoryBufferStream;
begin
RtfText := GenerateRtfText();
Stream := TReadOnlyMemoryBufferStream.Create(PAnsiChar(RtfText), Length(RtfText));
try
RichEdit2.PlainText := False;
RichEdit2.Lines.LoadFromStream(Stream);
...
finally
Stream.Free;
end;
end;
function TForm1.GenerateRtfText: AnsiString;
var
I: Integer;
Stream: TMemoryStream;
const
DOES_WORK_COUNT = 10000;
DOES_NOT_WORK_COUNT = 15000;
begin
//Fill
RichEdit1.Lines.BeginUpdate;
try
//for I := 0 to DOES_WORK_COUNT do
for I := 0 to DOES_NOT_WORK_COUNT do
RichEdit1.Lines.Add(IntToStr(I));
finally
RichEdit1.Lines.EndUpdate;
end;
//Convert to RTF
Stream := TMemoryStream.Create;
try
RichEdit1.PlainText := False;
RichEdit1.Lines.SaveToStream(Stream);
SetString(Result, PAnsiChar(Stream.Memory), Stream.Size);
finally
Stream.Free;
end;
end;