开发者

Convert HTML to RTF

I have notes in the database stored as html with the DHtml component. But I want to change format to RTF and use DevExpress TcxRichEdit instead because I feel that is a simpler component, more stable etc. Users have problems with dissapearing texts that I of course cannot reproduce.

TcxRichEdit is working fine and can save and load notes again. The problem is the old notes in html format. I have tried this routine but I never got it to work. An rtf string was generated but it was not accepted by TcxRichEdit.

Then I got the idea to use the clipboard. By having the DHtml and TcxRichEdit side by side I should copy and paste between them and let the clipboard do the actual conversion. In practice it was not so simple as I thought...

Here is some code:

  function _ConvertToRtf(aHtml: String; aRichEdit: TcxRichEdit): Boolean;
  begin
    Result := False;
    if (aHtml <> '') and (aHtml <> '&nbsp;') then
    begin
      if not AnsiStartsStr('{\rtf1', aHtml) then
      begin
        vHtmlDlg.InitDoc := aHtml;
        vHtmlDlg.Editor.Stop;
        if vHtmlDlg.Editor.SelectAll then
          if vHtmlDlg.Editor.CutToClipboard then
          begin
            aRichEdit.Clear;
            aRichEdit.PasteFromClipboard;
          end;
      end;

      Result := True;
    end;
  end;

Problem is that vHtmlDlg.Editor.SelectAll always return False.

f开发者_运维技巧unction TCustomProfDHTMLEdit.SelectAll: Boolean;
const
  CGID_MSHTML: TGUID = '{DE4BA900-59CA-11CF-9592-444553540000}';
var
  D: IDispatch;
  CommandTarget: IOleCommandTarget;
  vaIn, vaOut: OleVariant;
  hr: HRESULT;
begin
  Result := False;
  if GetDOM(D) then
  try
    CommandTarget := D as IOleCommandTarget;
    hr := CommandTarget.Exec(@CGID_MSHTML, 31, OLECMDEXECOPT_DODEFAULT, vaIn, vaOut);
    Result := SUCCEEDED(hr)
  except
  end
end;

It is actually GetDOM that returns False:

function TProfDHTMLEdit2.GetDOM(out P: IDispatch): Boolean;
begin
  if Busy then
  begin
    P := nil;
    Result := False
  end
  else
    try
      P := (IDispatch(GetOleObject) as IWebBrowser2).Document;
      Result := True
    except
      P := nil;
      Result := False
    end
end;

No it is GetBusy that returns true...

function TProfDHTMLEdit2.GetBusy: Boolean;
begin
  if FDocumentCompleteReason <> dcrUndefined then
    Result := True
  else
    Result := False
end;

So I have tried the dig deeper and depper in the html component but I still don't understand why I cannot use SelectAll.

Here is a simplified version of how I initialize and use it.

  vHtmlDlg := TDhtmlEditorForm.Create(nil);
  vHtmlDlg.Show;
  vHtmlDlg.BrowseMode := False;
  try
   // Call ConvertToRtf with strings in a loop here 
  finally
    vHtmlDlg.Free;
  end;

Any idea why SelectAll returns false and didn't work ?

Edit1: One more thing. Docs for the html component are here http://www.profgrid.com/documentation/htmledit/ The stop command seems to stop loading an HTML page into the control. I used in by chance because in another place it prevents a lockup when loading data in html. It would anyway be real nice to get the conversion and get rid of the HTML component!

Edit2: I found the solution at last and it was quite simple. Just add the Dhtml component to a form at designtime instead of create it in code. That way the busy property was false and it simply works. There is no need to check busy in a while loop as this is done in SetSource method. I give jachquate the checkmark as he note me on that the component was asynced. The final code for convert a string look like this:

  function _ConvertToRtf(aHtml: String; aRichEdit: TcxRichEdit): Integer;
  begin
    if (aHtml <> '') and (aHtml <> '&nbsp;') then
    begin
      if not AnsiStartsStr('{\rtf1', aHtml) then
      begin
        DhtmlMemo.Source := aHtml;
        if DhtmlMemo.SelectAll then
          if DhtmlMemo.CutToClipboard then
          begin
            aRichEdit.Clear;
            aRichEdit.PasteFromClipboard;
          end;

        if VarIsNull(aRichEdit.EditValue) then
          Result := 0    // Not valid. The caller would delete the note.
        else
          Result := 2;   // String was converted
      end
      else
        Result := 1;     // String already in rtf. Do nothing.
    end
    else
      Result := 0;       
  end;

Thanks for the support and commitment on my problem!


As this is a one time conversion, I'm with you in regard to use the clipboard.

The HTML component looks like a kind of Async component, so you must have to wait, because it would be processing the loading/representing the provided HTML in other threads, all encapsulated by the component. I do not know the particular component, but I bet this will work:

  function _ConvertToRtf(aHtml: String; aRichEdit: TcxRichEdit): Boolean;
  begin
    Result := False;
    if (aHtml <> '') and (aHtml <> '&nbsp;') then
    begin
      if not AnsiStartsStr('{\rtf1', aHtml) then
      begin
        vHtmlDlg.InitDoc := aHtml;
        vHtmlDlg.Editor.Stop;
        //before or after stop, I'm not sure what stop means
        while vHtmlDlg.Busy do
          Sleep(1); // or maybe Application.ProcessMessages, try both
        if vHtmlDlg.Editor.SelectAll then
          if vHtmlDlg.Editor.CutToClipboard then
          begin
            aRichEdit.Clear;
            aRichEdit.PasteFromClipboard;
            Result := True;  //of course you return true only if this succeeds.
          end;
      end;
    end;
  end;

If this is a multiple-time conversion to be done on a user machine, read the @Chris answer.


You may have a chance if you put in a delay after the cut/copy, before the paste. But in general, using the clipboard like this is very bad practice. The clipboard is provided for the convenience of the user, not the programmer. Any clipboard-aware programs are going to react to this, including any remote desktop/citrix sessions which will be trying to pump this crud across the network.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜