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 <> ' ') 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 <> ' ') 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 <> ' ') 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.
精彩评论