vcl/win/dtrans/DOTransferable.cxx |   82 +++++++++++++++++++++++++++-----------
 vcl/win/dtrans/FmtFilter.cxx      |   38 ++++++-----------
 2 files changed, 73 insertions(+), 47 deletions(-)

New commits:
commit 262d39f6cf40dd2f41e13662d861769b6eaa3c70
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sat Nov 8 17:49:01 2025 +0500
Commit:     Adolfo Jayme Barrientos <[email protected]>
CommitDate: Tue Nov 11 10:00:55 2025 +0100

    tdf#155855: fix pasting from CF_BITMAP
    
    It has been implemented in commit e9391ae8eb3d0a4edf977f0159ee7e07a0a95859
    (vcl113: #i111834# support pasting HBITMAP). But I fail to see how it
    could ever work, despite the OOo bug was verified.
    
    The problems were:
    
    1. MIME type added for CF_BITMAP was exactly the same as for CF_DIB. Given
    that the wanted flavor is passed to CDOTransferable::getTransferData in
    DataFlavor struct, which uses the MIME type string, which is only turned
    into CFormatEtc (encapsulating FORMATETC) in the function, it was never
    possible to request CF_BITMAP: always CF_DIB got requested.
    
    2. WinBITMAPToOOBMP used a call to GetBitmapDimensionEx to obtain the
    dimensions out of HBITMAP. But that function is only used together with
    SetBitmapDimensionEx; and the value that these two functions control is
    documented to be only usable by applications, not by the system, and it
    has nothing to do with the actual bitmap data stored in HBITMAP, it's a
    kind of custom metadata. Generally, GetBitmapDimensionEx returns (0, 0)
    for any bitmap in the clipboard. And then, it passed nullptr as HDC to
    GetDIBits, which is 100% guaranteed to fail.
    
    My theory is, that the CF_BITMAP-related code added in that patch always
    was a dead code, and it happened to work because the system has a feature
    of Synthesized Clipboard Formats, where CF_DIB will be created by system
    on demand - so the change allowed to find and map CF_BITMAP in clipboard
    to the MIME type shared with CF_DIB; then the MIME type was converted to
    CF_DIB; and its functional code path was used.
    
    However, this automatic generation of CF_DIB obviously doesn't happen in
    case of Windows Clipboard History. Request for CF_DIB fails, so we have
    to make the code for CF_BITMAP (the only data in the clipboard that the
    history provides for image data) work.
    
    This change adds a fallback to CDOTransferable::getClipboardData, that
    tries CF_BITMAP when CF_DIB was requested and has failed. It also fixes
    WinBITMAPToOOBMP.
    
    Change-Id: I7d5eb64cc378510d303686d2bab9c53129607980
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193630
    Reviewed-by: Mike Kaganski <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit 426a9af71d99c8610b28c3bf73fb8e3fd88e0a64)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193722
    Reviewed-by: Adolfo Jayme Barrientos <[email protected]>

diff --git a/vcl/win/dtrans/DOTransferable.cxx 
b/vcl/win/dtrans/DOTransferable.cxx
index 38329a209c1c..3004f9fe16d7 100644
--- a/vcl/win/dtrans/DOTransferable.cxx
+++ b/vcl/win/dtrans/DOTransferable.cxx
@@ -419,30 +419,65 @@ void CDOTransferable::tryToGetIDataObjectIfAbsent()
 // in case of failures because nothing should have been
 // allocated etc.
 
-CDOTransferable::ByteSequence_t CDOTransferable::getClipboardData( CFormatEtc& 
aFormatEtc )
+namespace
 {
-    STGMEDIUM stgmedium;
-    tryToGetIDataObjectIfAbsent();
-    if (!m_rDataObject.is()) // Maybe we are shutting down, and clipboard is 
already destroyed?
-        throw RuntimeException();
-    HRESULT hr = m_rDataObject->GetData( aFormatEtc, &stgmedium );
+// Uses a fallback to try TYMED_ISTREAM instead of TYMED_HGLOBAL
+HRESULT getClipboardData_impl(const IDataObjectPtr& pDataObject, CFormatEtc& 
rFormatEtc,
+                              STGMEDIUM& rStgmedium)
+{
+    const HRESULT hr = pDataObject->GetData( rFormatEtc, &rStgmedium );
+    if (SUCCEEDED(hr))
+        return hr;
+
+    const DWORD nOrigTymed = rFormatEtc.getTymed();
 
     // in case of failure to get a WMF metafile handle, try to get a memory 
block
-    if( FAILED( hr ) &&
-        ( CF_METAFILEPICT == aFormatEtc.getClipformat() ) &&
-        ( TYMED_MFPICT == aFormatEtc.getTymed() ) )
+    if( ( CF_METAFILEPICT == rFormatEtc.getClipformat() ) &&
+        ( TYMED_MFPICT ==  nOrigTymed) )
     {
-        CFormatEtc aTempFormat( aFormatEtc );
-        aTempFormat.setTymed( TYMED_HGLOBAL );
-        hr = m_rDataObject->GetData( aTempFormat, &stgmedium );
+        rFormatEtc.setTymed(TYMED_HGLOBAL);
+        // Do not overwrite original error
+        HRESULT hr2 = pDataObject->GetData(rFormatEtc, &rStgmedium);
+        if (SUCCEEDED(hr2))
+            return hr2;
     }
 
-    if (FAILED(hr) && aFormatEtc.getTymed() == TYMED_HGLOBAL)
+    if (nOrigTymed == TYMED_HGLOBAL)
     {
         // Handle type is not memory, try stream.
-        CFormatEtc aTempFormat(aFormatEtc);
-        aTempFormat.setTymed(TYMED_ISTREAM);
-        hr = m_rDataObject->GetData(aTempFormat, &stgmedium);
+        rFormatEtc.setTymed(TYMED_ISTREAM);
+        HRESULT hr2 = pDataObject->GetData(rFormatEtc, &rStgmedium);
+        if (SUCCEEDED(hr2))
+            return hr2;
+    }
+
+    if (rFormatEtc.getClipformat() == CF_BITMAP && nOrigTymed != TYMED_GDI)
+    {
+        // Try GDI
+        rFormatEtc.setTymed(TYMED_GDI);
+        HRESULT hr2 = pDataObject->GetData(rFormatEtc, &rStgmedium);
+        if (SUCCEEDED(hr2))
+            return hr2;
+    }
+
+    return hr; // original error
+}
+}
+
+CDOTransferable::ByteSequence_t CDOTransferable::getClipboardData( CFormatEtc& 
aFormatEtc )
+{
+    CFormatEtc aLocalFormatEtc(aFormatEtc);
+    STGMEDIUM stgmedium;
+    tryToGetIDataObjectIfAbsent();
+    if (!m_rDataObject.is()) // Maybe we are shutting down, and clipboard is 
already destroyed?
+        throw RuntimeException();
+    HRESULT hr = getClipboardData_impl(m_rDataObject, aLocalFormatEtc, 
stgmedium);
+
+    if (FAILED(hr) && aFormatEtc.getClipformat() == CF_DIB)
+    {
+        aLocalFormatEtc = aFormatEtc;
+        aLocalFormatEtc.setClipformat(CF_BITMAP);
+        hr = getClipboardData_impl(m_rDataObject, aLocalFormatEtc, stgmedium);
     }
 
     if ( FAILED( hr ) )
@@ -464,14 +499,14 @@ CDOTransferable::ByteSequence_t 
CDOTransferable::getClipboardData( CFormatEtc& a
 
     try
     {
-        if ( CF_ENHMETAFILE == aFormatEtc.getClipformat() )
+        if (CF_ENHMETAFILE == aLocalFormatEtc.getClipformat())
             byteStream = WinENHMFPictToOOMFPict( stgmedium.hEnhMetaFile );
-        else if (CF_HDROP == aFormatEtc.getClipformat())
+        else if (CF_HDROP == aLocalFormatEtc.getClipformat())
             byteStream = CF_HDROPToFileList(stgmedium.hGlobal);
-        else if ( CF_BITMAP == aFormatEtc.getClipformat() )
+        else if (CF_BITMAP == aLocalFormatEtc.getClipformat())
         {
             byteStream = WinBITMAPToOOBMP(stgmedium.hBitmap);
-            if( aFormatEtc.getTymed() == TYMED_GDI &&
+            if (aLocalFormatEtc.getTymed() == TYMED_GDI &&
                 ! stgmedium.pUnkForRelease )
             {
                 DeleteObject(stgmedium.hBitmap);
@@ -479,15 +514,16 @@ CDOTransferable::ByteSequence_t 
CDOTransferable::getClipboardData( CFormatEtc& a
         }
         else
         {
-            clipDataToByteStream( aFormatEtc.getClipformat( ), stgmedium, 
byteStream );
+            clipDataToByteStream(aLocalFormatEtc.getClipformat(), stgmedium, 
byteStream);
 
             // format conversion if necessary
             // #i124085# DIBV5 should not happen currently, but keep as a hint 
here
-            if(CF_DIBV5 == aFormatEtc.getClipformat() || CF_DIB == 
aFormatEtc.getClipformat())
+            if (CF_DIBV5 == aLocalFormatEtc.getClipformat()
+                || CF_DIB == aLocalFormatEtc.getClipformat())
             {
                 byteStream = WinDIBToOOBMP(byteStream);
             }
-            else if(CF_METAFILEPICT == aFormatEtc.getClipformat())
+            else if (CF_METAFILEPICT == aLocalFormatEtc.getClipformat())
             {
                 byteStream = WinMFPictToOOMFPict(byteStream);
             }
diff --git a/vcl/win/dtrans/FmtFilter.cxx b/vcl/win/dtrans/FmtFilter.cxx
index 614c8d27f375..47aff85c6cf4 100644
--- a/vcl/win/dtrans/FmtFilter.cxx
+++ b/vcl/win/dtrans/FmtFilter.cxx
@@ -183,9 +183,8 @@ HENHMETAFILE OOMFPictToWinENHMFPict( Sequence< sal_Int8 > 
const & aOOMetaFilePic
 Sequence< sal_Int8 > WinDIBToOOBMP( const Sequence< sal_Int8 >& aWinDIB )
 {
     OSL_ENSURE(o3tl::make_unsigned(aWinDIB.getLength()) > 
sizeof(BITMAPINFOHEADER), "CF_DIBV5/CF_DIB too small (!)");
-    Sequence< sal_Int8 > ooBmpStream;
+    Sequence< sal_Int8 > ooBmpStream(aWinDIB.getLength( ) + 
sizeof(BITMAPFILEHEADER));
 
-    ooBmpStream.realloc(aWinDIB.getLength( ) + sizeof(BITMAPFILEHEADER));
     const BITMAPINFOHEADER* pBmpInfoHdr = reinterpret_cast< const 
BITMAPINFOHEADER* >(aWinDIB.getConstArray());
     BITMAPFILEHEADER* pBmpFileHdr = reinterpret_cast< BITMAPFILEHEADER* 
>(ooBmpStream.getArray());
     const DWORD nSizeInfoOrV5(pBmpInfoHdr->biSize > sizeof(BITMAPINFOHEADER) ? 
sizeof(BITMAPV5HEADER) : sizeof(BITMAPINFOHEADER));
@@ -400,36 +399,27 @@ Sequence< sal_Int8 > WinBITMAPToOOBMP( HBITMAP aHBMP )
 {
     Sequence< sal_Int8 > ooBmpStream;
 
-    SIZE aBmpSize;
-    if( GetBitmapDimensionEx( aHBMP, &aBmpSize ) )
+    if (BITMAP bm{}; GetObjectW(aHBMP, sizeof(bm), &bm))
     {
-        // fill bitmap info header
-        size_t nDataBytes = 4 * aBmpSize.cy * aBmpSize.cy;
+        size_t nDataBytes = ((bm.bmWidth * bm.bmBitsPixel + 31) / 32) * 4 * 
bm.bmHeight;
         Sequence< sal_Int8 > aBitmapStream(
             sizeof(BITMAPINFO) +
             nDataBytes
             );
-        PBITMAPINFOHEADER pBmp = 
reinterpret_cast<PBITMAPINFOHEADER>(aBitmapStream.getArray());
-        pBmp->biSize = sizeof( BITMAPINFOHEADER );
-        pBmp->biWidth  = aBmpSize.cx;
-        pBmp->biHeight = aBmpSize.cy;
-        pBmp->biPlanes = 1;
-        pBmp->biBitCount = 32;
-        pBmp->biCompression = BI_RGB;
-        pBmp->biSizeImage = static_cast<DWORD>(nDataBytes);
-        pBmp->biXPelsPerMeter = 1000;
-        pBmp->biYPelsPerMeter = 1000;
-        pBmp->biClrUsed = 0;
-        pBmp->biClrImportant = 0;
-        if( GetDIBits( nullptr, // DC, 0 is a default GC, basically that of 
the desktop
-                       aHBMP,
-                       0, aBmpSize.cy,
-                       aBitmapStream.getArray() + sizeof(BITMAPINFO),
-                       reinterpret_cast<LPBITMAPINFO>(pBmp),
-                       DIB_RGB_COLORS ) )
+        PBITMAPINFO pBmp = 
reinterpret_cast<PBITMAPINFO>(aBitmapStream.getArray());
+        pBmp->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+        pBmp->bmiHeader.biWidth = bm.bmWidth;
+        pBmp->bmiHeader.biHeight = bm.bmHeight;
+        pBmp->bmiHeader.biPlanes = 1;
+        pBmp->bmiHeader.biBitCount = bm.bmBitsPixel;
+        pBmp->bmiHeader.biCompression = BI_RGB;
+
+        HDC hdc = GetDC(nullptr);
+        if (GetDIBits(hdc, aHBMP, 0, bm.bmHeight, pBmp + 1, pBmp, 
DIB_RGB_COLORS))
         {
             ooBmpStream = WinDIBToOOBMP( aBitmapStream );
         }
+        ReleaseDC(nullptr, hdc);
     }
 
     return ooBmpStream;

Reply via email to