20

我需要在运行时使用 TRichEdit 来执行 rtf 到文本的转换,如此所述。我成功地做到了这一点,但如果不能填充 TRichedit.Lines,我必须将虚拟表单设置为父表单。(错误:缺少父级)。我在下面粘贴了我的功能,有人可以建议一种避免定义父母的方法吗?您能否对此发表评论并告诉我您是否找到了更高效的想法?

注意:我需要一个字符串,而不是 TStrings 作为输出,这就是它被设计成这样的原因。

function RtfToText(const RTF: string;ReplaceLineFeedWithSpace: Boolean): string;
var
  RTFConverter: TRichEdit;
  MyStringStream: TStringStream;
  i: integer;
  CustomLineFeed: string;

begin
  if ReplaceLineFeedWithSpace then
    CustomLineFeed := ' '
    else
    CustomLineFeed := #13;
  try
    RTFConverter := TRichEdit.Create(nil);
    try
      MyStringStream := TStringStream.Create(RTF);
      RTFConverter.parent := Form4; // this is the part I don't like
      RTFConverter.Lines.LoadFromStream(MyStringStream);
      RTFConverter.PlainText := True;
      for i := 0 to RTFConverter.Lines.Count - 1 do
      begin
        if i < RTFConverter.Lines.Count - 1 then
          Result := Result + RTFConverter.Lines[i] + CustomLineFeed
          else
          Result := Result + RTFConverter.Lines[i];
      end;
    finally
      MyStringStream.Free;
    end;
  finally
    RTFConverter.Free;
  end;

end;

更新:回答后我更新了函数并将其写在这里以供参考:

function RtfToText(const RTF: string;ReplaceLineFeedWithSpace: Boolean): string;
var
  RTFConverter: TRichEdit;
  MyStringStream: TStringStream;
begin
  RTFConverter := TRichEdit.CreateParented(HWND_MESSAGE);
  try
    MyStringStream := TStringStream.Create(RTF);
    try
      RTFConverter.Lines.LoadFromStream(MyStringStream);
      RTFConverter.PlainText := True;
      RTFConverter.Lines.StrictDelimiter := True;
      if ReplaceLineFeedWithSpace then
        RTFConverter.Lines.Delimiter := ' '
        else
        RTFConverter.Lines.Delimiter := #13;
      Result := RTFConverter.Lines.DelimitedText;
    finally
      MyStringStream.Free;
    end;
  finally
    RTFConverter.Free;
  end;
end;
4

4 回答 4

34

TRichEdit 控件是 Windows 中 RichEdit 控件的包装器。Windows 的控件是......好吧.. Windows,它们需要一个窗口句柄才能工作。Delphi 需要调用 CreateWindow 或 CreateWindowEx 来创建句柄,并且这两个例程都需要一个有效的父窗口句柄才能工作。Delphi 尝试使用控件父级的句柄(这是有道理的!)。令人高兴的是,可以使用替代构造函数(CreateParanted(HWND)构造函数),并且微软的好人HWND_MESSAGE将其用作实际上不需要“窗口”(仅消息传递)的窗口的父级。

此代码按预期工作:

procedure TForm2.Button2Click(Sender: TObject);
var R:TRichEdit;
    L:TStringList;
begin
  R := TRichEdit.CreateParented(HWND_MESSAGE);
  try
    R.PlainText := False;
    R.Lines.LoadFromFile('C:\Temp\text.rtf');
    R.PlainText := True;

    Memo1.Lines.Text := R.Lines.Text;
  finally 
    R.Free;
  end;
end;
于 2010-07-12T13:49:33.397 回答
8

这是 VCL 工作方式的一部分,如果没有一些繁重的变通方法,你不会让它以不同的方式工作。但是您不需要将虚拟表单定义为父表单;只需使用您当前的表格并visible := false;在 TRichEdit 上进行设置。

但是,如果您真的想提高性能,则可以丢弃用于构建结果字符串的循环。它必须重新分配和复制大量内存。使用 TrichEdit.Lines 的 Text 属性获取每行之间的 CRLF,使用 DelimitedText 获取其他内容,例如空格。他们使用只分配一次的内部缓冲区,如果您处理大量文本,这将大大加快连接速度。

于 2010-07-12T11:25:35.917 回答
4

我使用DrawRichText来绘制没有 RichEdit 控件的 RTF。(IIRC 这被称为Windowless Rich Edit Controls。)也许您也可以使用它来进行转换 - 但是我从未尝试过。

于 2010-07-12T13:40:28.430 回答
0

这对我开始使用 TRichEdit 最有帮助,但对转换没有帮助。但是,这可以按预期工作,并且您不需要设置行分隔符:

// RTF to Plain:
procedure TForm3.Button1Click(Sender: TObject);
var
    l:TStringList;
    s:WideString;
    RE:TRichEdit;
    ss:TStringStream;
begin
    ss := TStringStream.Create;
    s := Memo1.Text; // Input String
    RE := TRichEdit.CreateParented(HWND_MESSAGE);
    l := TStringList.Create;
    l.Add(s);
    ss.Position := 0;
    l.SaveToStream(ss);
    ss.Position := 0;
    RE.Lines.LoadFromStream(ss);
    Memo2.Text := RE.Text; // Output String
end;

// Plain to RTF:
procedure TForm3.Button2Click(Sender: TObject);
var
    RE:TRichEdit;
    ss:TStringStream;
begin
    RE := TRichEdit.CreateParented(HWND_MESSAGE);
    RE.Text := Memo2.Text; // Input String
    ss := TStringStream.Create;
    ss.Position := 0;
    RE.Lines.SaveToStream(ss);
    ss.Position := 0;
    Memo1.Text := ss.ReadString(ss.Size); // Output String
end;

我在转换为纯文本时使用了 TStringList "l",因为 TStringStream 以某种方式将每个字符都放在了一个新行中。

编辑:使代码更好一点,并删除了未使用的变量。

于 2014-12-12T10:17:43.903 回答