2009-07-11 38 views
11

Tôi đang viết một bài kiểm tra đơn vị cho tiện ích "Trình dọn dẹp văn bản" sẽ xóa mọi định dạng, v.v. khỏi văn bản trên bảng tạm. Ví dụ, nếu bạn sao chép một số văn bản từ một tài liệu Word hoặc một trang web với nhiều định dạng, bạn có thể muốn dán nó vào một Word DOC khác như văn bản cũ thông thường, thuần tuý.Làm cách nào để đặt một số văn bản được định dạng vào Clipboard?

Để viết một bài kiểm tra đơn vị cho điều này, tôi cần, tất nhiên, để viết mã thực sự đặt một số văn bản được định dạng vào clipboard.

Vì vậy, câu hỏi của tôi là - làm cách nào để làm điều đó trong mã Delphi?

+0

Trợ giúp "sao chép delphi vào khay nhớ tạm" có giúp ích không? Tôi thấy một vài bài viết được viết về việc này. Có điều gì đó cụ thể làm cho những bài viết đó không hữu ích? – shady

+0

Vâng, họ không cho bạn biết làm thế nào để đặt một cái gì đó trong đó được định dạng hoặc những gì mà định dạng nên được. Ví dụ: điều này không thực sự hữu ích: http://msdn.microsoft.com/en-us/library/ms649016%28VS.85%29.aspx –

+0

Bạn biết rằng Word và hầu hết các ứng dụng cũng đặt văn bản được định dạng trên bảng tạm trong cả định dạng và văn bản thuần túy. Khi dán bạn có thể chọn định dạng nào bạn muốn bằng cách sử dụng lệnh "Dán đặc biệt". Bạn có thể có các lý do khác để viết chương trình này nhưng nếu bạn thực sự chỉ chuyển từ một tài liệu từ sang một từ khác, mọi thứ được xây dựng để loại bỏ định dạng. –

Trả lời

9

Dưới đây là một ví dụ về cách sao chép vào clipboard dưới dạng html: http://www.swissdelphicenter.ch/torry/showcode.php?id=1391

Tôi đã sửa đổi mã một chút để nó hoạt động trong Delphi 2009.

// If you've ever tried sticking html into the clipboard using the usual CF_TEXT 
// format then you might have been disappointed to discover that wysiwyg html 
// editors paste your offering as if it were just text, 
// rather than recognising it as html. For that you need the CF_HTML format. 
// CF_HTML is entirely text format and uses the transformation format UTF-8. 
// It includes a description, a context, and within the context, the fragment. 
// 
// As you may know one can place multiple items of data onto the clipboard for 
// a single clipboard entry, which means that the same data can be pasted in a 
// variety of different formats in order to cope with target 
// applications of varying sophistocation. 
// 
// The following example shows how to stick CF_TEXT (and CF_HTML) 
// into the clipboard. 

function FormatHTMLClipboardHeader(HTMLText: string): string; 
const 
    CrLf = #13#10; 
begin 
    Result := 'Version:0.9' + CrLf; 
    Result := Result + 'StartHTML:-1' + CrLf; 
    Result := Result + 'EndHTML:-1' + CrLf; 
    Result := Result + 'StartFragment:000081' + CrLf; 
    Result := Result + 'EndFragment:°°°°°°' + CrLf; 
    Result := Result + HTMLText + CrLf; 
    Result := StringReplace(Result, '°°°°°°', Format('%.6d', [Length(Result)]), []); 
end; 

//The second parameter is optional and is put into the clipboard as CF_HTML. 
//Function can be used standalone or in conjunction with the VCL clipboard so long as 
//you use the USEVCLCLIPBOARD conditional define 
//($define USEVCLCLIPBOARD} 
//(and clipboard.open, clipboard.close). 
//Code from http://www.lorriman.com 
procedure CopyHTMLToClipBoard(const str: AnsiString; const htmlStr: AnsiString = ''); 
var 
    gMem: HGLOBAL; 
    lp: PChar; 
    Strings: array[0..1] of AnsiString; 
    Formats: array[0..1] of UINT; 
    i: Integer; 
begin 
    gMem := 0; 
    {$IFNDEF USEVCLCLIPBOARD} 
    Win32Check(OpenClipBoard(0)); 
    {$ENDIF} 
    try 
    //most descriptive first as per api docs 
    Strings[0] := FormatHTMLClipboardHeader(htmlStr); 
    Strings[1] := str; 
    Formats[0] := RegisterClipboardFormat('HTML Format'); 
    Formats[1] := CF_TEXT; 
    {$IFNDEF USEVCLCLIPBOARD} 
    Win32Check(EmptyClipBoard); 
    {$ENDIF} 
    for i := 0 to High(Strings) do 
    begin 
     if Strings[i] = '' then Continue; 
     //an extra "1" for the null terminator 
     gMem := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, Length(Strings[i]) + 1); 
     {Succeeded, now read the stream contents into the memory the pointer points at} 
     try 
     Win32Check(gmem <> 0); 
     lp := GlobalLock(gMem); 
     Win32Check(lp <> nil); 
     CopyMemory(lp, PChar(Strings[i]), Length(Strings[i]) + 1); 
     finally 
     GlobalUnlock(gMem); 
     end; 
     Win32Check(gmem <> 0); 
     SetClipboardData(Formats[i], gMEm); 
     Win32Check(gmem <> 0); 
     gmem := 0; 
    end; 
    finally 
    {$IFNDEF USEVCLCLIPBOARD} 
    Win32Check(CloseClipBoard); 
    {$ENDIF} 
    end; 
end; 

// Example: 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    CopyHTMLToClipBoard('Hello world', 'Hello <b>world</b>'); 
end; 

Nếu bạn dán này trong MS Word, bạn sẽ thấy điều này:

Xin chào thế giới

+0

Tuyệt vời - chính xác những gì tôi cần. Cảm ơn. –

+0

'CopyMemory (lp, PChar (Strings [i])' - PAnsiChar –

+0

'Chuỗi [0]: = Định dạngHTMLClipboardHeader (htmlStr);' -> 'Chuỗi [0]: = UTF8ENCODE (Định dạngHTMLClipboardHeader (htmlStr));' chỉ UTF -8 https://msdn.microsoft.com/en-us/library/aa767917.aspx –

10

Trong DSiWin32 ta có:

var 
    GCF_HTML: UINT; 

{:Checks if HTML format is stored on the clipboard. 
    @since 2008-04-29 
    @author gabr 
} 
function DSiIsHtmlFormatOnClipboard: boolean; 
begin 
    Result := IsClipboardFormatAvailable(GCF_HTML); 
end; { DSiIsHtmlFormatOnClipboard } 

{:Retrieves HTML format from the clipboard. If there is no HTML format on the clipboard, 
    function returns empty string. 
    @since 2008-04-29 
    @author MP002, gabr 
} 
function DSiGetHtmlFormatFromClipboard: string; 
var 
    hClipData  : THandle; 
    idxEndFragment : integer; 
    idxStartFragment: integer; 
    pClipData  : PChar; 
begin 
    Result := ''; 
    if DSiIsHtmlFormatOnClipboard then begin 
    Win32Check(OpenClipboard(0)); 
    try 
     hClipData := GetClipboardData(GCF_HTML); 
     if hClipData <> 0 then begin 
     pClipData := GlobalLock(hClipData); 
     Win32Check(assigned(pClipData)); 
     try 
      idxStartFragment := Pos('<!--StartFragment-->', pClipData); // len = 20 
      idxEndFragment := Pos('<!--EndFragment-->', pClipData); 
      if (idxStartFragment >= 0) and (idxEndFragment >= idxStartFragment) then 
      Result := Copy(pClipData, idxStartFragment + 20, idxEndFragment - idxStartFragment - 20); 
     finally GlobalUnlock(hClipData); end; 
     end; 
    finally Win32Check(CloseClipboard); end; 
    end; 
end; { DSiGetHtmlFormatFromClipboard } 

{:Copies HTML (and, optionally, text) format to the clipboard. 
    @since 2008-04-29 
    @author MP002, gabr 
} 
procedure DSiCopyHtmlFormatToClipboard(const sHtml, sText: string); 

    function MakeFragment(const sHtml: string): string; 
    const 
    CVersion  = 'Version:1.0'#13#10; 
    CStartHTML  = 'StartHTML:'; 
    CEndHTML  = 'EndHTML:'; 
    CStartFragment = 'StartFragment:'; 
    CEndFragment = 'EndFragment:'; 
    CHTMLIntro  = '<sHtml><head><title>HTML clipboard</title></head><body><!--StartFragment-->'; 
    CHTMLExtro  = '<!--EndFragment--></body></sHtml>'; 
    CNumberLengthAndCR = 10; 
    CDescriptionLength = // Let the compiler determine the description length. 
     Length(CVersion) + Length(CStartHTML) + Length(CEndHTML) + 
     Length(CStartFragment) + Length(CEndFragment) + 4*CNumberLengthAndCR; 
    var 
    description  : string; 
    idxEndFragment : integer; 
    idxEndHtml  : integer; 
    idxStartFragment: integer; 
    idxStartHtml : integer; 
    begin 
    // The sHtml clipboard format is defined by using byte positions in the entire block 
    // where sHtml text and fragments start and end. These positions are written in a 
    // description. Unfortunately the positions depend on the length of the description 
    // but the description may change with varying positions. To solve this dilemma the 
    // offsets are converted into fixed length strings which makes it possible to know 
    // the description length in advance. 
    idxStartHtml := CDescriptionLength;    // position 0 after the description 
    idxStartFragment := idxStartHtml + Length(CHTMLIntro); 
    idxEndFragment := idxStartFragment + Length(sHtml); 
    idxEndHtml := idxEndFragment + Length(CHTMLExtro); 
    description := CVersion + 
     SysUtils.Format('%s%.8d', [CStartHTML, idxStartHtml]) + #13#10 + 
     SysUtils.Format('%s%.8d', [CEndHTML, idxEndHtml]) + #13#10 + 
     SysUtils.Format('%s%.8d', [CStartFragment, idxStartFragment]) + #13#10 + 
     SysUtils.Format('%s%.8d', [CEndFragment, idxEndFragment]) + #13#10; 
    Result := description + CHTMLIntro + sHtml + CHTMLExtro; 
    end; { MakeFragment } 

var 
    clipFormats: array[0..1] of UINT; 
    clipStrings: array[0..1] of string; 
    hClipData : HGLOBAL; 
    iFormats : integer; 
    pClipData : PChar; 

begin { DSiCopyHtmlFormatToClipboard } 
    Win32Check(OpenClipBoard(0)); 
    try 
    //most descriptive first as per api docs 
    clipStrings[0] := MakeFragment(sHtml); 
    if sText = '' then 
     clipStrings[1] := sHtml 
    else 
     clipStrings[1] := sText; 
    clipFormats[0] := GCF_HTML; 
    clipFormats[1] := CF_TEXT; 
    Win32Check(EmptyClipBoard); 
    for iFormats := 0 to High(clipStrings) do begin 
     if clipStrings[iFormats] = '' then 
     continue; 
     hClipData := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, Length(clipStrings[iFormats]) + 1); 
     Win32Check(hClipData <> 0); 
     try 
     pClipData := GlobalLock(hClipData); 
     Win32Check(assigned(pClipData)); 
     try 
      Move(PChar(clipStrings[iFormats])^, pClipData^, Length(clipStrings[iFormats]) + 1); 
     finally GlobalUnlock(hClipData); end; 
     Win32Check(SetClipboardData(clipFormats[iFormats], hClipData) <> 0); 
     hClipData := 0; 
     finally 
     if hClipData <> 0 then 
      GlobalFree(hClipData); 
     end; 
    end; 
    finally Win32Check(CloseClipboard); end; 
end; { DSiCopyHtmlFormatToClipboard } 

initialization 
    GCF_HTML := RegisterClipboardFormat('HTML Format'); 

EDIT: @Edelcom: Trong Delphi 7, DSiWin32 cần xác định

_STARTUPINFOW = record 
    cb: DWORD; 
    lpReserved: PWideChar; 
    lpDesktop: PWideChar; 
    lpTitle: PWideChar; 
    dwX: DWORD; 
    dwY: DWORD; 
    dwXSize: DWORD; 
    dwYSize: DWORD; 
    dwXCountChars: DWORD; 
    dwYCountChars: DWORD; 
    dwFillAttribute: DWORD; 
    dwFlags: DWORD; 
    wShowWindow: Word; 
    cbReserved2: Word; 
    lpReserved2: PByte; 
    hStdInput: THandle; 
    hStdOutput: THandle; 
    hStdError: THandle; 
    end; 
    TStartupInfoW = _STARTUPINFOW; 
    PStartupInfoW = ^TStartupInfoW; 

tôi sẽ đưa vào này và phát hành phiên bản mới.

+0

Mã này hoạt động tốt hơn phiên bản được chấp nhận (Tôi gặp sự cố khi dán vào thành phần TRichView với thành phần cuối cùng) – Frantic

+0

+1 Cảm ơn bạn đã chia sẻ mã này - Tôi chỉ đang tìm kiếm một bản sao html sang chức năng clipboard. – Edelcom

+0

Tôi đã sao chép đơn vị DSiWin32 từ trang web của bạn, nhưng điều này không biên dịch (nó phàn nàn về loại TStartupInfoW), tôi đang chạy Delhpi 7. Tôi có thể thay đổi điều này thành TStartupInfo không? – Edelcom

1

Câu trả lời được chấp nhận từ Wouter là một khởi đầu tốt, nhưng không xử lý các ký tự unicode. Tôi đã sửa đổi mã ví dụ để làm việc với unicode (html và dữ liệu văn bản). Cũng bị rò rỉ bộ nhớ cố định.

function FormatHTMLClipboardHeader(HTMLText: UTF8String): UTF8String; 
const 
    CrLf = #13#10; 
begin 
    Result := 'Version:0.9' + CrLf; 
    Result := Result + 'StartHTML:-1' + CrLf; 
    Result := Result + 'EndHTML:-1' + CrLf; 
    Result := Result + 'StartFragment:000081' + CrLf; 
    Result := Result + 'EndFragment:°°°°°°' + CrLf; 
    Result := Result + HTMLText + CrLf; 
    Result := UTF8String(StringReplace(string(Result), '°°°°°°', Format('%.6d', [Length(Result)]), [])); 
end; 


//The second parameter is optional and is put into the clipboard as CF_HTML. 
procedure CopyHTMLToClipBoard(const str: String; const htmlStr: String = ''); 
var 
    gMem : HGLOBAL; 
    lp  : Pointer; 
    HString : UTF8String; 
begin 
    {$WARN SYMBOL_PLATFORM OFF} 
    Win32Check(OpenClipBoard(0)); 

    try 
    Win32Check(EmptyClipBoard); 

    if (htmlStr <> '') then 
    begin 
     // convert to utf8 and add header, which windows html clipboard format requires 
     HString := FormatHTMLClipboardHeader(UTF8String(htmlStr)); 

     //an extra "1" for the null terminator 
     gMem := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, Length(HString) + 1); 
     {Succeeded, now read the stream contents into the memory the pointer points at} 
     try 
     Win32Check(gmem <> 0); 
     lp := GlobalLock(gMem); 
     Win32Check(lp <> nil); 
     CopyMemory(lp, Pointer(HString), Length(HString) + 1); 
     Win32Check(gmem <> 0); 
     SetClipboardData(RegisterClipboardFormat('HTML Format'), gMem); 
     Win32Check(gmem <> 0); 
     finally 
     GlobalUnlock(gMem); 
     GlobalFree(gMem); 
     end; 
    end; 

    // Now just place plain unicode text, double buffer size as it's utf16 
    gMem := GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, (Length(str) + 1) * 2); 
    {Succeeded, now read the stream contents into the memory the pointer points at} 
    try 
     Win32Check(gmem <> 0); 
     lp := GlobalLock(gMem); 
     Win32Check(lp <> nil); 
     CopyMemory(lp, Pointer(str), (Length(str) + 1) * 2); 
     Win32Check(gmem <> 0); 
     SetClipboardData(CF_UNICODETEXT, gMem); 
     Win32Check(gmem <> 0); 
    finally 
     GlobalUnlock(gMem); 
     GlobalFree(gMem); 
    end; 

    finally 
    Win32Check(CloseClipBoard); 
    end; 
    {$WARN SYMBOL_PLATFORM ON} 
end; 
+0

Một lưu ý cho bất cứ ai cố gắng này - nếu bạn sử dụng mã này như là bạn sẽ thấy rằng các cuộc gọi để lấy dữ liệu clipboard có thể thất bại vì các cuộc gọi đến globalfree, nếu bạn có vấn đề thử mà không có nó. –

Các vấn đề liên quan