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 <osl/diagnose.h>
37 #include <systools/win32/comtools.hxx>
39 using namespace com::sun::star::uno
;
57 // convert a windows metafile picture to a LibreOffice metafile picture
59 Sequence
< sal_Int8
> WinMFPictToOOMFPict( Sequence
< sal_Int8
>& aMetaFilePict
)
61 OSL_ASSERT( aMetaFilePict
.getLength( ) == sizeof( METAFILEPICT
) );
63 Sequence
< sal_Int8
> mfpictStream
;
64 METAFILEPICT
* pMFPict
= reinterpret_cast< METAFILEPICT
* >( aMetaFilePict
.getArray( ) );
65 HMETAFILE hMf
= pMFPict
->hMF
;
66 sal_uInt32 nCount
= GetMetaFileBitsEx( hMf
, 0, nullptr );
70 mfpictStream
.realloc( nCount
+ sizeof( METAFILEHEADER
) );
72 METAFILEHEADER
* pMFHeader
= reinterpret_cast< METAFILEHEADER
* >( mfpictStream
.getArray( ) );
73 SMALL_RECT aRect
= { 0,
75 static_cast< short >( pMFPict
->xExt
),
76 static_cast< short >( pMFPict
->yExt
) };
111 pMFHeader
->key
= 0x9AC6CDD7L
;
113 pMFHeader
->bbox
= aRect
;
114 pMFHeader
->inch
= nInch
;
115 pMFHeader
->reserved
= 0;
116 pMFHeader
->checksum
= 0;
118 char* pMFBuff
= reinterpret_cast< char* >( mfpictStream
.getArray( ) );
120 nCount
= GetMetaFileBitsEx( pMFPict
->hMF
, nCount
, pMFBuff
+ sizeof( METAFILEHEADER
) );
121 OSL_ASSERT( nCount
> 0 );
127 // convert a windows enhanced metafile to a LibreOffice metafile
129 Sequence
< sal_Int8
> WinENHMFPictToOOMFPict( HENHMETAFILE hEnhMetaFile
)
131 Sequence
< sal_Int8
> aRet
;
135 ( ( nSize
= GetEnhMetaFileBits( hEnhMetaFile
, 0, nullptr ) ) != 0 ) )
137 aRet
.realloc( nSize
);
139 if( GetEnhMetaFileBits( hEnhMetaFile
, nSize
, reinterpret_cast<unsigned char*>(aRet
.getArray()) ) != nSize
)
146 // convert a LibreOffice metafile picture to a windows metafile picture
148 HMETAFILEPICT
OOMFPictToWinMFPict( Sequence
< sal_Int8
> const & aOOMetaFilePict
)
150 HMETAFILEPICT hPict
= nullptr;
151 HMETAFILE hMtf
= SetMetaFileBitsEx( aOOMetaFilePict
.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict
.getConstArray()) );
155 METAFILEPICT
* pPict
= static_cast<METAFILEPICT
*>(GlobalLock( hPict
= GlobalAlloc( GHND
, sizeof( METAFILEPICT
) ) ));
162 GlobalUnlock( hPict
);
168 // convert a LibreOffice metafile picture to a windows enhanced metafile picture
170 HENHMETAFILE
OOMFPictToWinENHMFPict( Sequence
< sal_Int8
> const & aOOMetaFilePict
)
172 HENHMETAFILE hEnhMtf
= SetEnhMetaFileBits( aOOMetaFilePict
.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict
.getConstArray()) );
177 // convert a windows device independent bitmap into a LibreOffice bitmap
179 Sequence
< sal_Int8
> WinDIBToOOBMP( const Sequence
< sal_Int8
>& aWinDIB
)
181 OSL_ENSURE(o3tl::make_unsigned(aWinDIB
.getLength()) > sizeof(BITMAPINFOHEADER
), "CF_DIBV5/CF_DIB too small (!)");
182 Sequence
< sal_Int8
> ooBmpStream
;
184 ooBmpStream
.realloc(aWinDIB
.getLength( ) + sizeof(BITMAPFILEHEADER
));
185 const BITMAPINFOHEADER
* pBmpInfoHdr
= reinterpret_cast< const BITMAPINFOHEADER
* >(aWinDIB
.getConstArray());
186 BITMAPFILEHEADER
* pBmpFileHdr
= reinterpret_cast< BITMAPFILEHEADER
* >(ooBmpStream
.getArray());
187 const DWORD
nSizeInfoOrV5(pBmpInfoHdr
->biSize
> sizeof(BITMAPINFOHEADER
) ? sizeof(BITMAPV5HEADER
) : sizeof(BITMAPINFOHEADER
));
188 DWORD
nOffset(sizeof(BITMAPFILEHEADER
) + nSizeInfoOrV5
);
190 memcpy(pBmpFileHdr
+ 1, pBmpInfoHdr
, aWinDIB
.getLength());
192 if(pBmpInfoHdr
->biBitCount
<= 8)
194 nOffset
+= (pBmpInfoHdr
->biClrUsed
? pBmpInfoHdr
->biClrUsed
: (1 << pBmpInfoHdr
->biBitCount
)) << 2;
196 else if((BI_BITFIELDS
== pBmpInfoHdr
->biCompression
) && ((16 == pBmpInfoHdr
->biBitCount
) || (32 == pBmpInfoHdr
->biBitCount
)))
201 pBmpFileHdr
->bfType
= ('M' << 8) | 'B';
202 pBmpFileHdr
->bfSize
= 0; // maybe: nMemSize + sizeof(BITMAPFILEHEADER)
203 pBmpFileHdr
->bfReserved1
= 0;
204 pBmpFileHdr
->bfReserved2
= 0;
205 pBmpFileHdr
->bfOffBits
= nOffset
;
210 // convert a LibreOffice bitmap into a windows device independent bitmap
212 Sequence
< sal_Int8
> OOBmpToWinDIB( Sequence
< sal_Int8
>& aOOBmp
)
214 Sequence
< sal_Int8
> winDIBStream( aOOBmp
.getLength( ) - sizeof( BITMAPFILEHEADER
) );
216 memcpy( winDIBStream
.getArray( ),
217 aOOBmp
.getArray( ) + sizeof( BITMAPFILEHEADER
),
218 aOOBmp
.getLength( ) - sizeof( BITMAPFILEHEADER
) );
223 static std::string
GetHtmlFormatHeader(size_t startHtml
, size_t endHtml
, size_t startFragment
, size_t endFragment
)
225 std::ostringstream htmlHeader
;
226 htmlHeader
<< "Version:1.0" << '\r' << '\n';
227 htmlHeader
<< "StartHTML:" << std::setw(10) << std::setfill('0') << std::dec
<< startHtml
<< '\r' << '\n';
228 htmlHeader
<< "EndHTML:" << std::setw(10) << std::setfill('0') << std::dec
<< endHtml
<< '\r' << '\n';
229 htmlHeader
<< "StartFragment:" << std::setw(10) << std::setfill('0') << std::dec
<< startFragment
<< '\r' << '\n';
230 htmlHeader
<< "EndFragment:" << std::setw(10) << std::setfill('0') << std::dec
<< endFragment
<< '\r' << '\n';
231 return htmlHeader
.str();
234 // the case of these tags has to match what we output in our filters
235 // both tags don't allow parameters
236 const std::string
TAG_HTML("<html>");
237 const std::string
TAG_END_HTML("</html>");
239 // The body tag may have parameters so we need to search for the
240 // closing '>' manually e.g. <body param> #92840#
241 const std::string
TAG_BODY("<body");
242 const std::string
TAG_END_BODY("</body");
244 Sequence
<sal_Int8
> TextHtmlToHTMLFormat(Sequence
<sal_Int8
> const & aTextHtml
)
246 OSL_ASSERT(aTextHtml
.getLength() > 0);
248 if (aTextHtml
.getLength() <= 0)
249 return Sequence
<sal_Int8
>();
251 // fill the buffer with dummy values to calc the exact length
252 std::string dummyHtmlHeader
= GetHtmlFormatHeader(0, 0, 0, 0);
253 size_t lHtmlFormatHeader
= dummyHtmlHeader
.length();
255 std::string
textHtml(
256 reinterpret_cast<const char*>(aTextHtml
.getConstArray()),
257 reinterpret_cast<const char*>(aTextHtml
.getConstArray()) + aTextHtml
.getLength());
259 std::string::size_type nStartHtml
= textHtml
.find(TAG_HTML
) + lHtmlFormatHeader
- 1; // we start one before '<HTML>' Word 2000 does also so
260 std::string::size_type nEndHtml
= textHtml
.find(TAG_END_HTML
) + lHtmlFormatHeader
+ TAG_END_HTML
.length() + 1; // our SOffice 5.2 wants 2 behind </HTML>?
262 // The body tag may have parameters so we need to search for the
263 // closing '>' manually e.g. <BODY param> #92840#
264 std::string::size_type nStartFragment
= textHtml
.find(">", textHtml
.find(TAG_BODY
)) + lHtmlFormatHeader
+ 1;
265 std::string::size_type nEndFragment
= textHtml
.find(TAG_END_BODY
) + lHtmlFormatHeader
;
267 std::string htmlFormat
= GetHtmlFormatHeader(nStartHtml
, nEndHtml
, nStartFragment
, nEndFragment
);
268 htmlFormat
+= textHtml
;
270 Sequence
<sal_Int8
> byteSequence(htmlFormat
.length() + 1); // space the trailing '\0'
271 memset(byteSequence
.getArray(), 0, byteSequence
.getLength());
274 static_cast<void*>(byteSequence
.getArray()),
275 static_cast<const void*>(htmlFormat
.c_str()),
276 htmlFormat
.length());
281 static std::wstring
getFileExtension(const std::wstring
& aFilename
)
283 std::wstring::size_type idx
= aFilename
.rfind(L
".");
284 if (idx
!= std::wstring::npos
)
286 return std::wstring(aFilename
, idx
);
288 return std::wstring();
291 const std::wstring SHELL_LINK_FILE_EXTENSION
= L
".lnk";
293 static bool isShellLink(const std::wstring
& aFilename
)
295 std::wstring ext
= getFileExtension(aFilename
);
296 return (_wcsicmp(ext
.c_str(), SHELL_LINK_FILE_EXTENSION
.c_str()) == 0);
299 /** Resolve a Windows Shell Link (lnk) file. If a resolution
300 is not possible simply return the provided name of the
302 static std::wstring
getShellLinkTarget(const std::wstring
& aLnkFile
)
304 OSL_ASSERT(isShellLink(aLnkFile
));
306 std::wstring target
= aLnkFile
;
310 sal::systools::COMReference
<IShellLinkW
> pIShellLink
;
311 HRESULT hr
= CoCreateInstance(
312 CLSID_ShellLink
, nullptr, CLSCTX_INPROC_SERVER
, IID_IShellLinkW
, reinterpret_cast<LPVOID
*>(&pIShellLink
));
316 sal::systools::COMReference
<IPersistFile
> pIPersistFile
=
317 pIShellLink
.QueryInterface
<IPersistFile
>(IID_IPersistFile
);
319 hr
= pIPersistFile
->Load(aLnkFile
.c_str(), STGM_READ
);
323 hr
= pIShellLink
->Resolve(nullptr, SLR_UPDATE
| SLR_NO_UI
);
327 wchar_t pathW
[MAX_PATH
];
328 WIN32_FIND_DATAW wfd
;
329 hr
= pIShellLink
->GetPath(pathW
, MAX_PATH
, &wfd
, SLGP_RAWPATH
);
335 catch(sal::systools::ComError
& ex
)
342 typedef Sequence
<sal_Int8
> ByteSequence_t
;
344 /* Calculate the size required for turning a string list into
345 a double '\0' terminated string buffer */
346 static size_t CalcSizeForStringListBuffer(const std::vector
<std::wstring
>& fileList
)
348 if ( fileList
.empty() )
351 size_t size
= 1; // one for the very final '\0'
352 for (auto const& elem
: fileList
)
354 size
+= elem
.length() + 1; // length including terminating '\0'
356 return (size
* sizeof(std::vector
<std::wstring
>::value_type::value_type
));
359 static ByteSequence_t
FileListToByteSequence(const std::vector
<std::wstring
>& fileList
)
362 size_t size
= CalcSizeForStringListBuffer(fileList
);
367 wchar_t* p
= reinterpret_cast<wchar_t*>(bseq
.getArray());
370 for (auto const& elem
: fileList
)
372 wcsncpy(p
, elem
.c_str(), elem
.length());
373 p
+= (elem
.length() + 1);
379 css::uno::Sequence
<sal_Int8
> CF_HDROPToFileList(HGLOBAL hGlobal
)
381 UINT nFiles
= DragQueryFileW(static_cast<HDROP
>(hGlobal
), 0xFFFFFFFF, nullptr, 0);
382 std::vector
<std::wstring
> files
;
384 for (UINT i
= 0; i
< nFiles
; i
++)
386 wchar_t buff
[MAX_PATH
];
387 /*UINT size =*/ DragQueryFileW(static_cast<HDROP
>(hGlobal
), i
, buff
, MAX_PATH
);
388 std::wstring filename
= buff
;
389 if (isShellLink(filename
))
390 filename
= getShellLinkTarget(filename
);
391 files
.push_back(filename
);
393 return FileListToByteSequence(files
);
396 // convert a windows bitmap handle into a LibreOffice bitmap
398 Sequence
< sal_Int8
> WinBITMAPToOOBMP( HBITMAP aHBMP
)
400 Sequence
< sal_Int8
> ooBmpStream
;
403 if( GetBitmapDimensionEx( aHBMP
, &aBmpSize
) )
405 // fill bitmap info header
406 size_t nDataBytes
= 4 * aBmpSize
.cy
* aBmpSize
.cy
;
407 Sequence
< sal_Int8
> aBitmapStream(
411 PBITMAPINFOHEADER pBmp
= reinterpret_cast<PBITMAPINFOHEADER
>(aBitmapStream
.getArray());
412 pBmp
->biSize
= sizeof( BITMAPINFOHEADER
);
413 pBmp
->biWidth
= aBmpSize
.cx
;
414 pBmp
->biHeight
= aBmpSize
.cy
;
416 pBmp
->biBitCount
= 32;
417 pBmp
->biCompression
= BI_RGB
;
418 pBmp
->biSizeImage
= static_cast<DWORD
>(nDataBytes
);
419 pBmp
->biXPelsPerMeter
= 1000;
420 pBmp
->biYPelsPerMeter
= 1000;
422 pBmp
->biClrImportant
= 0;
423 if( GetDIBits( nullptr, // DC, 0 is a default GC, basically that of the desktop
426 aBitmapStream
.getArray() + sizeof(BITMAPINFO
),
427 reinterpret_cast<LPBITMAPINFO
>(pBmp
),
430 ooBmpStream
= WinDIBToOOBMP( aBitmapStream
);
437 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */