diff --git a/lcl/clipbrd.pp b/lcl/clipbrd.pp index 3c166dd214..96428340f5 100644 --- a/lcl/clipbrd.pp +++ b/lcl/clipbrd.pp @@ -215,7 +215,8 @@ type function HasPictureFormat: boolean; procedure Open; //procedure SetAsHandle(Format: integer; Value: THandle); - procedure SetAsHtml(const Html: String; const PlainText: String; {%H-}AddWindowsHeader: Boolean); + procedure SetAsHtml(Html: String); + procedure SetAsHtml(Html: String; const PlainText: String); function SetComponent(Component: TComponent): Boolean; function SetComponentAsText(Component: TComponent): Boolean; function SetFormat(FormatID: TClipboardFormat; Stream: TStream): Boolean; diff --git a/lcl/include/clipbrd.inc b/lcl/include/clipbrd.inc index 53ab10843b..70fde44b1a 100644 --- a/lcl/include/clipbrd.inc +++ b/lcl/include/clipbrd.inc @@ -177,6 +177,7 @@ begin BeginUpdate; end; + procedure TClipboard.Close; begin EndUpdate; @@ -779,58 +780,68 @@ end; In case of Windows, the MS header is automatically removed.} function TClipboard.GetAsHtml(ExtractFragmentOnly: Boolean): String; var - stream: TMemoryStream; + Stream: TMemoryStream; bom: TBOM; US: UnicodeString; begin + //debugln(['TClipboard.GetAsHtml: ExtractFragmentOnly = ',ExtractFragmentOnly]); Result := ''; if (CF_HTML = 0) or not HasFormat(CF_HTML) then + begin + //debugln(['TClipboard.GetAsHtml: CF_HTML= ',CF_HTML,' HasFormat(CF_HTML) = ',HasFormat(CF_HTML)]); exit; + end; - stream := TMemoryStream.Create; + Stream := TMemoryStream.Create; try - if not GetFormat(CF_HTML, stream) then + if not GetFormat(CF_HTML, Stream) then + begin + //debugln(['TClipboard.GetAsHtml: GetFormat(CF_HTML, stream) = False']); exit; + end; + Stream.Write(#0#0, Length(#0#0)); - stream.Write(#0#0, Length(#0#0)); - - bom := GetBomFromStream(stream); + bom := GetBomFromStream(Stream); case Bom of bomUtf8: begin - stream.Position := 3; - SetLength(Result, stream.Size - 3); - stream.Read(Result, stream.Size - 3); - //ClipBoard may return a larger stream than the size of the string + Stream.Position := 3; + SetLength(Result, Stream.Size - 3); + Stream.Read(Result, Stream.Size - 3); + //ClipBoard may return a larger Stream than the size of the string //this gets rid of it, since the string will end in a #0 (wide)char Result := PAnsiChar(Result); + //debugln(['TClipboard.GetAsHtml: Found bomUtf8']); end; bomUTF16LE: begin - stream.Position := 2; - SetLength(US, stream.Size - 2); - stream.Read(US[1], stream.Size - 2); - //ClipBoard may return a larger stream than the size of the string + Stream.Position := 2; + SetLength(US, Stream.Size - 2); + Stream.Read(US[1], Stream.Size - 2); + //ClipBoard may return a larger Stream than the size of the string //this gets rid of it, since the string will end in a #0 (wide)char US := PWideChar(US); Result := Utf16ToUtf8(US); + //debugln(['TClipboard.GetAsHtml: FoundbomUtf16LE']); end; bomUtf16BE: begin //this may need swapping of WideChars???? - stream.Position := 2; - SetLength(US, stream.Size - 2); - stream.Read(US[1], stream.Size - 2); - //ClipBoard may return a larger stream than the size of the string + Stream.Position := 2; + SetLength(US, Stream.Size - 2); + Stream.Read(US[1], Stream.Size - 2); + //ClipBoard may return a larger Stream than the size of the string //this gets rid of it, since the string will end in a #0 (wide)char US := PWideChar(US); Result := Utf16ToUtf8(US); + //debugln(['TClipboard.GetAsHtml: Found bomUtf16BE']); end; bomUndefined: begin //assume the first byte is part of the string and it is some AnsiString //CF_HTML returns a string encoded as UTF-8 on Windows Result := PAnsiChar(Stream.Memory); + //debugln(['TClipboard.GetAsHtml: Found bomUndefined']); end; end; @@ -844,44 +855,53 @@ begin end; finally - stream.Free; + Stream.Free; end; end; { Adds html-formatted text to the clipboard. The main Office applications in Windows and Linux require a valid and complete html text (i.e. with - and
tags). - In case of Windows, a specific header must be added (AddWindowsHeader = true), - otherwise the format will not be recognized by the clipboard. } -procedure TClipboard.SetAsHtml(const Html: String; const PlainText: String; {%H-}AddWindowsHeader: Boolean); + and tags), therefore we insert them if they are not present. + In case of Windows, a specific header will be added, + otherwise the format will not be recognized by the clipboard. + } +procedure TClipboard.SetAsHtml(Html: String; const PlainText: String); var - stream: TStream; + Stream: TStream; + IsValid: Boolean; begin if CF_HTML = 0 then exit; + //If the HTML does not have correct and closing insert them + MaybeInsertHtmlAndBodyTags(HTML, IsValid); + if not IsValid then + exit; + {$IFDEF WINDOWS} - if AddWindowsHeader then - stream := TStringStream.Create(InsertClipHeader(Html)) else - stream := TStringStream.Create(Html); + Stream := TStringStream.Create(InsertClipHeader(Html)); {$ELSE} - stream := TStringStream.Create(Html); + Stream := TStringStream.Create(Html); {$ENDIF} try - //Clear the clipboard before adding Html to it, - //otherwise external applications will only ever see the first copy. - ClipBoard.Clear; - stream.Position := 0; - Clipboard.AddFormat(CF_HTML, stream); + Stream.Position := 0; + Clipboard.AddFormat(CF_HTML, Stream); + if (PlainText <> '') then begin - stream.Size := 0; - stream.Position := 0; - stream.WriteAnsiString(PlainText); - stream.Position := 0; - ClipBoard.AddFormat(CF_TEXT, stream); + Stream.Size := 0; + Stream.Position := 0; + Stream.WriteBuffer(Pointer(PlainText)^, Length(PlainText)+1); //Also write terminating zero + Stream.Position := 0; + ClipBoard.AddFormat(CF_TEXT, Stream); end; + finally - stream.Free; + Stream.Free; end; end; +procedure TClipboard.SetAsHtml(Html: String); +begin + SetAsHtml(Html, ''); +end; + diff --git a/lcl/include/clipbrd_html.inc b/lcl/include/clipbrd_html.inc index 843f4ce3be..586df93d67 100644 --- a/lcl/include/clipbrd_html.inc +++ b/lcl/include/clipbrd_html.inc @@ -88,6 +88,50 @@ begin FEndTagPos := FParser.CurrentPos - Length(ActualTag) + 1; end; +procedure MaybeInsertHtmlAndBodyTags(var HTML: String; out IsValid: Boolean); +var + tagFinder: THTMLTagFinder; + HS, HE, BS, BE: Boolean; + pHS, pHE, pBS, pBE: Integer; +begin + tagFinder := THTMLTagFinder.Create(HTML, 'BODY'); + try + pBS := tagFinder.StartTagPos ; + pBE := tagFinder.EndTagPos; + BS := (pBS > -1); + BE := (pBE > -1); + finally + tagFinder.Free; + end; + + tagFinder := THTMLTagFinder.Create(HTML, 'HTML'); + try + pHS := tagFinder.StartTagPos; + pHE := tagFinder.EndTagPos; + HS := (pHS > -1); + HE := (pHE > -1); + finally + tagFinder.Free; + end; + IsValid := ((HS and HE) or (not HS and not HE)) and + ((BS and BE) or (not BS and not BE)); + + //Do not fix malformed HTML e.i. unmatched or tags + if not IsValid then + exit; + if not BS then + begin + if HS then + begin + Insert('',HTML,pHS+1); + Insert('',HTML,pHE+1+Length('')); + end + else + HTML := '' + HTML + ''; + end; + if not HS then HTML := '' + HTML + ''; +end; + {$IFDEF WINDOWS} function InsertClipHeader(HTML: String): String; var @@ -102,6 +146,7 @@ begin try fragStart := tagFinder.StartTagPos; fragEnd := tagFinder.EndTagPos; + //this should not happen, since we added them in SetAsHtml if (fragStart = -1) or (fragEnd = -1) then exit; finally diff --git a/lcl/lclbase.lpk b/lcl/lclbase.lpk index cabba2164a..5520f92a72 100644 --- a/lcl/lclbase.lpk +++ b/lcl/lclbase.lpk @@ -27,7 +27,7 @@