Конвертировать HTML в RTF - PullRequest
3 голосов
/ 25 марта 2011

У меня есть заметки в базе данных, хранящиеся в виде HTML с компонентом DHtml. Но я хочу изменить формат на RTF и использовать вместо него DevExpress TcxRichEdit, потому что я считаю, что это более простой компонент, более стабильный и т. Д. У пользователей возникают проблемы с исчезающими текстами, которые я, конечно, не могу воспроизвести.

TcxRichEdit работает нормально и может сохранять и загружать заметки снова. Проблема в старых заметках в формате html. Я пробовал эту процедуру , но так и не получил ее на работу. Сгенерирована RTF-строка, но она не была принята TcxRichEdit.

Тогда у меня появилась идея использовать буфер обмена. Имея DHtml и TcxRichEdit рядом, я должен скопировать и вставить между ними и позволить буферу обмена делать фактическое преобразование. На практике все оказалось не так просто, как я думал ...

Вот код:

  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;

Проблема в том, что vHtmlDlg.Editor.SelectAll всегда возвращает False.

function 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;

На самом деле GetDOM возвращает 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;

Нет, это GetBusy, который возвращает true ...

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

Итак, я пробовал копать глубже и глубже в html-компоненте, но я до сих пор не понимаю, почему я не могу использовать SelectAll.

Вот упрощенная версия инициализации и использования.

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

Есть идеи, почему SelectAll возвращает false и не работает?

Edit1: Еще кое-что. Документы для html-компонента здесь http://www.profgrid.com/documentation/htmledit/ Кажется, команда stop останавливает загрузку HTML-страницы в элемент управления. Я использовал случайно, потому что в другом месте это предотвращает блокировку при загрузке данных в HTML. В любом случае было бы очень приятно получить преобразование и избавиться от компонента HTML!

Edit2: Я наконец нашел решение, и это было довольно просто. Просто добавьте компонент Dhtml в форму во время разработки, а не создавайте его в коде. Таким образом, свойство busy было ложным, и оно просто работает. Нет необходимости проверять занятость в цикле while, как это делается в методе SetSource. Я ставлю jachquate галочку, когда он отмечает, что компонент был асинхронным. Окончательный код для преобразования строки выглядит следующим образом:

  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;

Спасибо за поддержку и приверженность моей проблеме!

Ответы [ 2 ]

2 голосов
/ 25 марта 2011

Поскольку это однократное преобразование, я с вами в отношении использования буфера обмена.

Компонент HTML выглядит как своего рода Async компонент, поэтому вы должны иметьждать, потому что он будет обрабатывать загрузку / представление предоставленного HTML в других потоках, все инкапсулированных компонентом.Я не знаю конкретного компонента, но держу пари, что это сработает:

  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;

Если это многократное преобразование, выполняемое на пользовательском компьютере, прочитайте ответ @ Chris .

2 голосов
/ 25 марта 2011

У вас может быть шанс, если вы вставите задержку после вырезания / копирования перед вставкой.Но в целом, использование такого буфера обмена - очень плохая практика.Буфер обмена предназначен для удобства пользователя, а не программиста.Любые программы, работающие с буфером обмена, будут реагировать на это, включая любые сеансы удаленного рабочего стола / citrix, которые будут пытаться прокачать эту грязь через сеть.

...