1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include "FmtFilter.hxx"
24 #include <o3tl/safeint.hxx>
25 #include <o3tl/unit_conversion.hxx>
26 #include <osl/diagnose.h>
38 #include <systools/win32/comtools.hxx>
40 using namespace com::sun::star::uno
;
58 // convert a windows metafile picture to a LibreOffice metafile picture
60 Sequence
< sal_Int8
> WinMFPictToOOMFPict( Sequence
< sal_Int8
>& aMetaFilePict
)
62 OSL_ASSERT( aMetaFilePict
.getLength( ) == sizeof( METAFILEPICT
) );
64 Sequence
< sal_Int8
> mfpictStream
;
65 METAFILEPICT
* pMFPict
= reinterpret_cast< METAFILEPICT
* >( aMetaFilePict
.getArray( ) );
66 HMETAFILE hMf
= pMFPict
->hMF
;
67 sal_uInt32 nCount
= GetMetaFileBitsEx( hMf
, 0, nullptr );
71 mfpictStream
.realloc( nCount
+ sizeof( METAFILEHEADER
) );
73 METAFILEHEADER
* pMFHeader
= reinterpret_cast< METAFILEHEADER
* >( mfpictStream
.getArray( ) );
74 SMALL_RECT aRect
= { 0,
76 static_cast< short >( pMFPict
->xExt
),
77 static_cast< short >( pMFPict
->yExt
) };
83 nInch
= o3tl::convert(1, o3tl::Length::in
, o3tl::Length::pt
);
87 nInch
= o3tl::convert(1, o3tl::Length::in
, o3tl::Length::mm10
);
91 nInch
= o3tl::convert(1, o3tl::Length::in
, o3tl::Length::mm100
);
95 nInch
= o3tl::convert(1, o3tl::Length::in
, o3tl::Length::in100
);
101 nInch
= o3tl::convert(1, o3tl::Length::in
, o3tl::Length::in1000
);
105 nInch
= o3tl::convert(1, o3tl::Length::in
, o3tl::Length::twip
);
109 nInch
= o3tl::convert(1, o3tl::Length::in
, o3tl::Length::master
);
112 pMFHeader
->key
= 0x9AC6CDD7L
;
114 pMFHeader
->bbox
= aRect
;
115 pMFHeader
->inch
= nInch
;
116 pMFHeader
->reserved
= 0;
117 pMFHeader
->checksum
= 0;
119 char* pMFBuff
= reinterpret_cast< char* >( mfpictStream
.getArray( ) );
121 nCount
= GetMetaFileBitsEx( pMFPict
->hMF
, nCount
, pMFBuff
+ sizeof( METAFILEHEADER
) );
122 OSL_ASSERT( nCount
> 0 );
128 // convert a windows enhanced metafile to a LibreOffice metafile
130 Sequence
< sal_Int8
> WinENHMFPictToOOMFPict( HENHMETAFILE hEnhMetaFile
)
132 Sequence
< sal_Int8
> aRet
;
136 ( ( nSize
= GetEnhMetaFileBits( hEnhMetaFile
, 0, nullptr ) ) != 0 ) )
138 aRet
.realloc( nSize
);
140 if( GetEnhMetaFileBits( hEnhMetaFile
, nSize
, reinterpret_cast<unsigned char*>(aRet
.getArray()) ) != nSize
)
147 // convert a LibreOffice metafile picture to a windows metafile picture
149 HMETAFILEPICT
OOMFPictToWinMFPict( Sequence
< sal_Int8
> const & aOOMetaFilePict
)
151 HMETAFILEPICT hPict
= nullptr;
152 HMETAFILE hMtf
= SetMetaFileBitsEx( aOOMetaFilePict
.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict
.getConstArray()) );
156 METAFILEPICT
* pPict
= static_cast<METAFILEPICT
*>(GlobalLock( hPict
= GlobalAlloc( GHND
, sizeof( METAFILEPICT
) ) ));
163 GlobalUnlock( hPict
);
169 // convert a LibreOffice metafile picture to a windows enhanced metafile picture
171 HENHMETAFILE
OOMFPictToWinENHMFPict( Sequence
< sal_Int8
> const & aOOMetaFilePict
)
173 HENHMETAFILE hEnhMtf
= SetEnhMetaFileBits( aOOMetaFilePict
.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict
.getConstArray()) );
178 // convert a windows device independent bitmap into a LibreOffice bitmap
180 Sequence
< sal_Int8
> WinDIBToOOBMP( const Sequence
< sal_Int8
>& aWinDIB
)
182 OSL_ENSURE(o3tl::make_unsigned(aWinDIB
.getLength()) > sizeof(BITMAPINFOHEADER
), "CF_DIBV5/CF_DIB too small (!)");
183 Sequence
< sal_Int8
> ooBmpStream
;
185 ooBmpStream
.realloc(aWinDIB
.getLength( ) + sizeof(BITMAPFILEHEADER
));
186 const BITMAPINFOHEADER
* pBmpInfoHdr
= reinterpret_cast< const BITMAPINFOHEADER
* >(aWinDIB
.getConstArray());
187 BITMAPFILEHEADER
* pBmpFileHdr
= reinterpret_cast< BITMAPFILEHEADER
* >(ooBmpStream
.getArray());
188 const DWORD
nSizeInfoOrV5(pBmpInfoHdr
->biSize
> sizeof(BITMAPINFOHEADER
) ? sizeof(BITMAPV5HEADER
) : sizeof(BITMAPINFOHEADER
));
189 DWORD
nOffset(sizeof(BITMAPFILEHEADER
) + nSizeInfoOrV5
);
191 memcpy(pBmpFileHdr
+ 1, pBmpInfoHdr
, aWinDIB
.getLength());
193 if(pBmpInfoHdr
->biBitCount
<= 8)
195 nOffset
+= (pBmpInfoHdr
->biClrUsed
? pBmpInfoHdr
->biClrUsed
: (1 << pBmpInfoHdr
->biBitCount
)) << 2;
197 else if((BI_BITFIELDS
== pBmpInfoHdr
->biCompression
) && ((16 == pBmpInfoHdr
->biBitCount
) || (32 == pBmpInfoHdr
->biBitCount
)))
202 pBmpFileHdr
->bfType
= ('M' << 8) | 'B';
203 pBmpFileHdr
->bfSize
= 0; // maybe: nMemSize + sizeof(BITMAPFILEHEADER)
204 pBmpFileHdr
->bfReserved1
= 0;
205 pBmpFileHdr
->bfReserved2
= 0;
206 pBmpFileHdr
->bfOffBits
= nOffset
;
211 // convert a LibreOffice bitmap into a windows device independent bitmap
213 Sequence
< sal_Int8
> OOBmpToWinDIB( Sequence
< sal_Int8
>& aOOBmp
)
215 Sequence
< sal_Int8
> winDIBStream( aOOBmp
.getLength( ) - sizeof( BITMAPFILEHEADER
) );
217 memcpy( winDIBStream
.getArray( ),
218 aOOBmp
.getArray( ) + sizeof( BITMAPFILEHEADER
),
219 aOOBmp
.getLength( ) - sizeof( BITMAPFILEHEADER
) );
224 static std::string
GetHtmlFormatHeader(size_t startHtml
, size_t endHtml
, size_t startFragment
, size_t endFragment
)
226 std::ostringstream htmlHeader
;
227 htmlHeader
<< "Version:1.0" << '\r' << '\n';
228 htmlHeader
<< "StartHTML:" << std::setw(10) << std::setfill('0') << std::dec
<< startHtml
<< '\r' << '\n';
229 htmlHeader
<< "EndHTML:" << std::setw(10) << std::setfill('0') << std::dec
<< endHtml
<< '\r' << '\n';
230 htmlHeader
<< "StartFragment:" << std::setw(10) << std::setfill('0') << std::dec
<< startFragment
<< '\r' << '\n';
231 htmlHeader
<< "EndFragment:" << std::setw(10) << std::setfill('0') << std::dec
<< endFragment
<< '\r' << '\n';
232 return htmlHeader
.str();
235 // the case of these tags has to match what we output in our filters
236 // both tags don't allow parameters
237 const std::string
TAG_HTML("<html>");
238 const std::string
TAG_END_HTML("</html>");
240 // The body tag may have parameters so we need to search for the
241 // closing '>' manually e.g. <body param> #92840#
242 const std::string
TAG_BODY("<body");
243 const std::string
TAG_END_BODY("</body");
245 Sequence
<sal_Int8
> TextHtmlToHTMLFormat(Sequence
<sal_Int8
> const & aTextHtml
)
247 OSL_ASSERT(aTextHtml
.getLength() > 0);
249 if (aTextHtml
.getLength() <= 0)
250 return Sequence
<sal_Int8
>();
252 // fill the buffer with dummy values to calc the exact length
253 std::string dummyHtmlHeader
= GetHtmlFormatHeader(0, 0, 0, 0);
254 size_t lHtmlFormatHeader
= dummyHtmlHeader
.length();
256 std::string
textHtml(
257 reinterpret_cast<const char*>(aTextHtml
.getConstArray()),
258 reinterpret_cast<const char*>(aTextHtml
.getConstArray()) + aTextHtml
.getLength());
260 std::string::size_type nStartHtml
= textHtml
.find(TAG_HTML
) + lHtmlFormatHeader
- 1; // we start one before '<HTML>' Word 2000 does also so
261 std::string::size_type nEndHtml
= textHtml
.find(TAG_END_HTML
) + lHtmlFormatHeader
+ TAG_END_HTML
.length() + 1; // our SOffice 5.2 wants 2 behind </HTML>?
263 // The body tag may have parameters so we need to search for the
264 // closing '>' manually e.g. <BODY param> #92840#
265 std::string::size_type nStartFragment
= textHtml
.find(">", textHtml
.find(TAG_BODY
)) + lHtmlFormatHeader
+ 1;
266 std::string::size_type nEndFragment
= textHtml
.find(TAG_END_BODY
) + lHtmlFormatHeader
;
268 std::string htmlFormat
= GetHtmlFormatHeader(nStartHtml
, nEndHtml
, nStartFragment
, nEndFragment
);
269 htmlFormat
+= textHtml
;
271 Sequence
<sal_Int8
> byteSequence(htmlFormat
.length() + 1); // space the trailing '\0'
272 memset(byteSequence
.getArray(), 0, byteSequence
.getLength());
275 static_cast<void*>(byteSequence
.getArray()),
276 static_cast<const void*>(htmlFormat
.c_str()),
277 htmlFormat
.length());
282 static std::wstring
getFileExtension(const std::wstring
& aFilename
)
284 std::wstring::size_type idx
= aFilename
.rfind(L
".");
285 if (idx
!= std::wstring::npos
)
287 return std::wstring(aFilename
, idx
);
289 return std::wstring();
292 const std::wstring SHELL_LINK_FILE_EXTENSION
= L
".lnk";
294 static bool isShellLink(const std::wstring
& aFilename
)
296 std::wstring ext
= getFileExtension(aFilename
);
297 return (_wcsicmp(ext
.c_str(), SHELL_LINK_FILE_EXTENSION
.c_str()) == 0);
300 /** Resolve a Windows Shell Link (lnk) file. If a resolution
301 is not possible simply return the provided name of the
303 static std::wstring
getShellLinkTarget(const std::wstring
& aLnkFile
)
305 OSL_ASSERT(isShellLink(aLnkFile
));
307 std::wstring target
= aLnkFile
;
311 sal::systools::COMReference
<IShellLinkW
> pIShellLink
;
312 pIShellLink
.CoCreateInstance(CLSID_ShellLink
, nullptr, CLSCTX_INPROC_SERVER
);
314 sal::systools::COMReference
<IPersistFile
> pIPersistFile(pIShellLink
,
315 sal::systools::COM_QUERY_THROW
);
317 HRESULT hr
= pIPersistFile
->Load(aLnkFile
.c_str(), STGM_READ
);
321 hr
= pIShellLink
->Resolve(nullptr, SLR_UPDATE
| SLR_NO_UI
);
325 wchar_t pathW
[MAX_PATH
];
326 WIN32_FIND_DATAW wfd
;
327 hr
= pIShellLink
->GetPath(pathW
, MAX_PATH
, &wfd
, SLGP_RAWPATH
);
333 catch(sal::systools::ComError
& ex
)
340 typedef Sequence
<sal_Int8
> ByteSequence_t
;
342 /* Calculate the size required for turning a string list into
343 a double '\0' terminated string buffer */
344 static size_t CalcSizeForStringListBuffer(const std::vector
<std::wstring
>& fileList
)
346 if ( fileList
.empty() )
349 size_t size
= 1; // one for the very final '\0'
350 for (auto const& elem
: fileList
)
352 size
+= elem
.length() + 1; // length including terminating '\0'
354 return (size
* sizeof(std::vector
<std::wstring
>::value_type::value_type
));
357 static ByteSequence_t
FileListToByteSequence(const std::vector
<std::wstring
>& fileList
)
360 size_t size
= CalcSizeForStringListBuffer(fileList
);
365 wchar_t* p
= reinterpret_cast<wchar_t*>(bseq
.getArray());
368 for (auto const& elem
: fileList
)
370 wcsncpy(p
, elem
.c_str(), elem
.length());
371 p
+= (elem
.length() + 1);
377 css::uno::Sequence
<sal_Int8
> CF_HDROPToFileList(HGLOBAL hGlobal
)
379 UINT nFiles
= DragQueryFileW(static_cast<HDROP
>(hGlobal
), 0xFFFFFFFF, nullptr, 0);
380 std::vector
<std::wstring
> files
;
382 for (UINT i
= 0; i
< nFiles
; i
++)
384 wchar_t buff
[MAX_PATH
];
385 /*UINT size =*/ DragQueryFileW(static_cast<HDROP
>(hGlobal
), i
, buff
, MAX_PATH
);
386 std::wstring filename
= buff
;
387 if (isShellLink(filename
))
388 filename
= getShellLinkTarget(filename
);
389 files
.push_back(filename
);
391 return FileListToByteSequence(files
);
394 // convert a windows bitmap handle into a LibreOffice bitmap
396 Sequence
< sal_Int8
> WinBITMAPToOOBMP( HBITMAP aHBMP
)
398 Sequence
< sal_Int8
> ooBmpStream
;
401 if( GetBitmapDimensionEx( aHBMP
, &aBmpSize
) )
403 // fill bitmap info header
404 size_t nDataBytes
= 4 * aBmpSize
.cy
* aBmpSize
.cy
;
405 Sequence
< sal_Int8
> aBitmapStream(
409 PBITMAPINFOHEADER pBmp
= reinterpret_cast<PBITMAPINFOHEADER
>(aBitmapStream
.getArray());
410 pBmp
->biSize
= sizeof( BITMAPINFOHEADER
);
411 pBmp
->biWidth
= aBmpSize
.cx
;
412 pBmp
->biHeight
= aBmpSize
.cy
;
414 pBmp
->biBitCount
= 32;
415 pBmp
->biCompression
= BI_RGB
;
416 pBmp
->biSizeImage
= static_cast<DWORD
>(nDataBytes
);
417 pBmp
->biXPelsPerMeter
= 1000;
418 pBmp
->biYPelsPerMeter
= 1000;
420 pBmp
->biClrImportant
= 0;
421 if( GetDIBits( nullptr, // DC, 0 is a default GC, basically that of the desktop
424 aBitmapStream
.getArray() + sizeof(BITMAPINFO
),
425 reinterpret_cast<LPBITMAPINFO
>(pBmp
),
428 ooBmpStream
= WinDIBToOOBMP( aBitmapStream
);
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */