Using Delphi FireMonkey, I need to copy HTML formatted string to the clipboard in Delphi so that other application pasting it will see it as HTML.
I already trying to copy simple text through a hidden Memo component and it work but is copied as simple text. I need HTML format copy. Having it as text, when pasting in any rich text program it shows all the HTML tags instead of formatted text.
My Current approach for copying text to clipboard is:
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;
But the problem is that if I paste the text copied on clipboard in Microsoft word it will be pasted as:
"This is a <b>bold</b>
html line". I want and it should be like:
This is a bold html line
Note: I have read other discussions about how it can be done in windows but I need a Firemonkey solution for a cross platform application. Any help is appreciated.
To copy data to the clipboard in a cross platform way with FireMonkey is done thru the interface IFMXExtendedClipboardService
that is obtained by calling the platform service:
var
Svc : IFMXExtendedClipboardService;
begin
if not TPlatformServices.Current.SupportsPlatformService(IFMXExtendedClipboardService, Svc) then
Exit; // Not clipboard supported
// Code using the interface
This interface has methods to copy/get text/images to/from clipboard: SetText
, GetText
, SetImage
, GetImage
.
For other kind of data, the user must register the data format and then write/read data to/from clipboard: RegisterCustomFormat
, IsCustomFormatRegistered
, UnregisterCustomFormat
, HasCustomFormat
, GetCustomFormat
, SetCustomFormat
.
In your question, you say you want to copy HTML formatted data to the clipboard. This can be translated to register the HTML format, create a stream with your data and call SetCustomFormat passing the format and stream.
Any format can be used and transmitted to/from the clipboard using the above mentioned interface methods. The code below takes the stream stream
and copy his content to the clipboard using ClipFormat
:
if TPlatformServices.Current.SupportsPlatformService(
IFMXExtendedClipboardService, Svc) then begin
if not Svc.IsCustomFormatRegistered(ClipFormat) then
Svc.RegisterCustomFormat(ClipFormat);
Svc.SetCustomFormat(ClipFormat, Stream);
end;
A format must be registered only once hence the call to IsCustomFormatRegistered
to prevent calling RegisterCustomFormat
more than once.
The name of the format is a simple string and can be anything. Copying and pasting applications must agree on the format name and the data format (The way the data is written in the stream).
Writing HTML formatted data may be difficult because of styling. Taking just a simple copy of an complete HTML document will probably not render it correctly because of styles.
If you transfer data thru the clipboard between two of your applications, you can do whatever you want. But transferring data between your application and another (You mentioned Microsoft Word in your question) is much more difficult.
In Microsoft products and all others on the Windows platform, the HTML format in the clipboard is described here.
The example you give in your question once properly formatted looks like this:
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>
The actual string is as above, with a CRLF at the end of each line. The format name is "HTML format".
You see the sentence This is a <b>bold</b> html line
must be surrounded by HTML tags to form a valid complete HTML document, and be preceded by a header made of a number of keyword:value
pairs. The values are offset if the string. The keywords are quite self explanatory. The string is UTF8 which can be reduced to ANSI if you use HTML entities to represent special characters.
I wrote a function to build the entire string:
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;
With all that, the function to copy a HTML formatted data to the clipboard looks like:
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;
You shall use the function like this:
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;