【问题标题】:How to copy HTML Formatted text As HTML to ClipBoard in Firemonkey如何将 HTML 格式的文本作为 HTML 复制到 Firemonkey 中的剪贴板
【发布时间】:2020-12-11 12:50:22
【问题描述】:

使用 Delphi FireMonkey,我需要将 HTML 格式的字符串复制到 Delphi 中的剪贴板,以便其他粘贴它的应用程序将其视为 HTML。

我已经尝试通过隐藏的备忘录组件复制简单文本,它可以工作,但被复制为简单文本。我需要 HTML 格式的副本。将其作为文本粘贴到任何富文本程序中时,它会显示所有 HTML 标记而不是格式化文本。

我目前将文本复制到剪贴板的方法是:

procedure TForm1.Button1Click(Sender: TObject);
var
  SourceText: string;
begin
  SourceText := 'This is a <b>bold</b> html line';
  //I can use TMemo to copy it to clipboard like:
  Memo1.lines.Text := SourceText;
  Memo1.SelectAll;
  memo1.CopyToClipboard;
end;

但问题是,如果我将复制到剪贴板上的文本粘贴到 Microsoft Word 中,它将被粘贴为:

  • “这是&lt;b&gt;bold&lt;/b&gt; html 行”。我想要并且应该是这样的:

  • 这是 粗体 html 行

注意:我已经阅读了其他关于如何在 Windows 中完成的讨论,但我需要一个用于跨平台应用程序的 Firemonkey 解决方案。任何帮助表示赞赏。

【问题讨论】:

  • 我们不知道您要复制或粘贴到的内容。我们没有关于您的代码的详细信息。请拨打tour 了解如何充分利用本网站,然后相应地编辑问题。
  • 感谢您的指导。我用一个例子相应地更新了这个问题。
  • 这里不会有一个简单的答案。在 Windows 上,您可能希望将 rtf 和 html 放入剪贴板。我希望这将需要您进行一些定制的编码。
  • 我有一个解决方案给你。重新打开问题后,我将发布。这是一个单独的函数 CopyHtmlToClipboard 来完成这项工作。
  • @fpiette 谢谢。是的,请。我迫切需要这个。我还没有关闭问题,不知道如何重新打开它。

标签: delphi clipboard firemonkey copy-paste


【解决方案1】:

通过调用平台服务得到的接口IFMXExtendedClipboardService使用FireMonkey跨平台复制数据到剪贴板:

var
    Svc      : IFMXExtendedClipboardService;
begin
    if not TPlatformServices.Current.SupportsPlatformService(IFMXExtendedClipboardService, Svc) then 
        Exit;  // Not clipboard supported
    // Code using the interface

此接口具有从剪贴板复制/获取文本/图像的方法:SetTextGetTextSetImageGetImage

对于其他类型的数据,用户必须注册数据格式,然后向剪贴板写入/读取数据:RegisterCustomFormatIsCustomFormatRegisteredUnregisterCustomFormatHasCustomFormatGetCustomFormatSetCustomFormat .

在您的问题中,您说您想将 HTML 格式的数据复制到剪贴板。这可以翻译为注册 HTML 格式,使用您的数据创建一个流并调用 SetCustomFormat 传递格式和流。

任何格式都可以通过上述接口方法使用和传输到剪贴板/从剪贴板传输。下面的代码获取流stream 并使用ClipFormat 将其内容复制到剪贴板:

if TPlatformServices.Current.SupportsPlatformService(
                IFMXExtendedClipboardService, Svc) then begin
    if not Svc.IsCustomFormatRegistered(ClipFormat) then
        Svc.RegisterCustomFormat(ClipFormat);
    Svc.SetCustomFormat(ClipFormat, Stream);
end;

一种格式只能注册一次,因此调用IsCustomFormatRegistered 以防止多次调用RegisterCustomFormat

格式的名称是一个简单的字符串,可以是任何东西。复制和粘贴应用程序必须就格式名称和数据格式(数据写入流的方式)达成一致。

由于样式的原因,编写 HTML 格式的数据可能很困难。由于样式的原因,仅获取完整 HTML 文档的简单副本可能无法正确呈现它。

如果您通过剪贴板在两个应用程序之间传输数据,您可以做任何您想做的事情。但是在您的应用程序和另一个应用程序(您在问题中提到 Microsoft Word)之间传输数据要困难得多。

在 Microsoft 产品和 Windows 平台上的所有其他产品中,剪贴板中的 HTML 格式描述为here

您在问题中给出的示例格式正确后如下所示:

Version:0.9
StartHTML:00000144
EndHTML:00000218
StartFragment:00000167
EndFragment:00000205
StartSelection:00000167
EndSelection:00000205
<!DOCTYPE><HTML><BODY><P>This is a <b>bold</b> html line</P></BODY></HTML>

实际字符串如上,每行末尾有一个CRLF。格式名称为“HTML 格式”。

你看到句子This is a &lt;b&gt;bold&lt;/b&gt; html line必须被HTML标签包围以形成一个有效的完整的HTML文档,并且前面有一个由许多keyword:value对组成的标题。如果是字符串,则这些值是偏移的。关键字是不言自明的。该字符串是 UTF8,如果您使用 HTML 实体来表示特殊字符,则可以将其简化为 ANSI。

我写了一个函数来构建整个字符串:

function FormatHtmlForClipboard(const HtmlSrc : UTF8String) : UTF8String;
const
    Header = 'Version:0.9'             + #13#10 +
             'StartHTML:00000000'      + #13#10 +
             'EndHTML:00000000'        + #13#10 +
             'StartFragment:00000000'  + #13#10 +
             'EndFragment:00000000'    + #13#10 +
             'StartSelection:00000000' + #13#10 +
             'EndSelection:00000000'   + #13#10;
var
    BodyStart : Integer;
    BodyEnd   : Integer;
    HdrLen    : Integer;
begin
    Result := Header;
    BodyStart := Pos('<BODY>', String(HtmlSrc));
    if BodyStart <= 0 then
        raise Exception.Create('<BODY> tag not found');
    Inc(BodyStart, 6);
    BodyEnd := Pos('</BODY>', String(HtmlSrc));
    if BodyEnd <= 0 then
        raise Exception.Create('</BODY> tag not found');

    HdrLen := Length(Header) - 1;
    WriteNumberIntoString(HdrLen,                   'StartHTML:',      Result);
    WriteNumberIntoString(HdrLen + Length(HtmlSrc), 'EndHTML:',        Result);
    WriteNumberIntoString(HdrLen + BodyStart,       'StartFragment:',  Result);
    WriteNumberIntoString(HdrLen + BodyEnd,         'EndFragment:',    Result);
    WriteNumberIntoString(HdrLen + BodyStart,       'StartSelection:', Result);
    WriteNumberIntoString(HdrLen + BodyEnd,         'EndSelection:',   Result);
    Result := Result + HtmlSrc;
end;

procedure WriteNumberIntoString(
    N        : Integer;
    const At : UTF8String;
    var S    : UTF8String);
var
    I : Integer;
    V : UTF8String;
begin
    I := Pos(At, S);
    if I <= 0 then
        Exit;
    I := I + Length(At);
    V := UTF8String(Format('%08.8d', [N]));
    Move(V[1], S[I], Length(V));
end;

综上所述,将 HTML 格式的数据复制到剪贴板的函数如下所示:

procedure CopyHtmlToClipboard(const HtmlSrc : UTF8String);
var
    Svc      : IFMXExtendedClipboardService;
    Stream   : TStringStream;
    HtmlData : UTF8String;
const
    ClipFormat = 'HTML format';   // This is what Windows expect
                                  // Maybe other platform want something else
begin
    HtmlData := FormatHtmlForClipboard(HtmlSrc);
    Stream   := TStringStream.Create(HtmlData);
    if TPlatformServices.Current.SupportsPlatformService(
                    IFMXExtendedClipboardService, Svc) then begin
        if not Svc.IsCustomFormatRegistered(ClipFormat) then
            Svc.RegisterCustomFormat(ClipFormat);
        Svc.SetCustomFormat(ClipFormat, Stream);
    end;
end;

你应该像这样使用函数:

procedure TForm1.Button1Click(Sender: TObject);
begin
    CopyHtmlToClipboard(
        '<!DOCTYPE><HTML><BODY><P>' +
        'This is a <b>bold</b> html line' +    //<== Your actual HTML text
        '</P></BODY></HTML>');
end;

【讨论】:

  • 美丽而精彩的解释。让我检查一个快速的 fmx 项目,然后回来寻求任何额外的指导。非常感谢。
  • 非常感谢。工作精美。我的问题立即解决了。将此解决方案标记为已接受的答案。只是一点额外的指导,如果我将此代码用于 Unicode 字符,它们将被复制为????。我用“String”更改了所有“AnsiString”,但仍然无法正常工作。在这里还能做些什么来使它与 Unicode 兼容?非常感谢。
  • @HilalSoftware 关于 unicode:正如我所说,HTML 必须使用 UTF8。您可以使用 UTF8String 代替 AnsiString。如果字符串包含特殊字符,则必须将字符串 (Unicode UTF16) 转换为 UTF8。请注意,目标应用程序(Word 或其他)上的 HTML 呈现不一定会接受任何内联字符。您应该使用 HTML 实体(参见 dev.w3.org/html5/html-author/charref)。
  • 已解决。问题在于 TStringStream 如下所述:stackoverflow.com/questions/2557358/…“TStringStream 在内部使用默认系统 ANSI 编码(每个字符 1 个字节)”因此在这方面更改 create 如下删除问题 Stream := TStringStream.Create(HtmlData,TEncoding.UTF8 );// TEncoding.Unicode);无法将其作为 TEncoding.Unicode 解决,但 TEncoding.UTF8 正在完成这项工作。
  • @HilalSoftware 我编辑了我的答案,用 UTF8String 替换了所有 AnsiString。如果您需要更多关于 UTF8String 或 HTML 实体的帮助,请提出一个新问题(首先自己做一些研究,确保写出一个“好问题”并避免您的问题被“关闭为重复”或“需要详细信息和清晰度")。
猜你喜欢
  • 2014-10-28
  • 1970-01-01
  • 1970-01-01
  • 2016-03-15
  • 2013-06-22
  • 1970-01-01
  • 2014-01-24
相关资源
最近更新 更多