blob: c2c0247ab9bfc4c4041fd5f723100cfb160cb266 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_dtrans.hxx"
#include "FmtFilter.hxx"
#include <osl/diagnose.h>
#include <comphelper/sequence.hxx>
#if defined _MSC_VER
#pragma warning(push,1)
#pragma warning(disable:4917)
#endif
#include <Shobjidl.h>
#include <shlguid.h>
#include <ObjIdl.h>
#include <shellapi.h>
#if defined _MSC_VER
#pragma warning(pop)
#endif
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>
#include <systools/win32/comtools.hxx>
using namespace com::sun::star::uno;
using rtl::OString;
#pragma pack(2)
struct METAFILEHEADER
{
DWORD key;
short hmf;
SMALL_RECT bbox;
WORD inch;
DWORD reserved;
WORD checksum;
};
#pragma pack()
//------------------------------------------------------------------------
// convert a windows metafile picture to a openoffice metafile picture
//------------------------------------------------------------------------
Sequence< sal_Int8 > SAL_CALL WinMFPictToOOMFPict( Sequence< sal_Int8 >& aMetaFilePict )
{
OSL_ASSERT( aMetaFilePict.getLength( ) == sizeof( METAFILEPICT ) );
Sequence< sal_Int8 > mfpictStream;
METAFILEPICT* pMFPict = reinterpret_cast< METAFILEPICT* >( aMetaFilePict.getArray( ) );
HMETAFILE hMf = pMFPict->hMF;
sal_uInt32 nCount = GetMetaFileBitsEx( hMf, 0, NULL );
if ( nCount > 0 )
{
mfpictStream.realloc( nCount + sizeof( METAFILEHEADER ) );
METAFILEHEADER* pMFHeader = reinterpret_cast< METAFILEHEADER* >( mfpictStream.getArray( ) );
SMALL_RECT aRect = { 0,
0,
static_cast< short >( pMFPict->xExt ),
static_cast< short >( pMFPict->yExt ) };
USHORT nInch;
switch( pMFPict->mm )
{
case MM_TEXT:
nInch = 72;
break;
case MM_LOMETRIC:
nInch = 100;
break;
case MM_HIMETRIC:
nInch = 1000;
break;
case MM_LOENGLISH:
nInch = 254;
break;
case MM_HIENGLISH:
case MM_ISOTROPIC:
case MM_ANISOTROPIC:
nInch = 2540;
break;
case MM_TWIPS:
nInch = 1440;
break;
default:
nInch = 576;
}
pMFHeader->key = 0x9AC6CDD7L;
pMFHeader->hmf = 0;
pMFHeader->bbox = aRect;
pMFHeader->inch = nInch;
pMFHeader->reserved = 0;
pMFHeader->checksum = 0;
char* pMFBuff = reinterpret_cast< char* >( mfpictStream.getArray( ) );
nCount = GetMetaFileBitsEx( pMFPict->hMF, nCount, pMFBuff + sizeof( METAFILEHEADER ) );
OSL_ASSERT( nCount > 0 );
}
return mfpictStream;
}
//-------------------------------------------------------------
// convert a windows enhanced metafile to a openoffice metafile
//-------------------------------------------------------------
Sequence< sal_Int8 > SAL_CALL WinENHMFPictToOOMFPict( HENHMETAFILE hEnhMetaFile )
{
Sequence< sal_Int8 > aRet;
UINT nSize = 0;
if( hEnhMetaFile &&
( ( nSize = GetEnhMetaFileBits( hEnhMetaFile, 0, NULL ) ) != 0 ) )
{
aRet.realloc( nSize );
if( GetEnhMetaFileBits( hEnhMetaFile, nSize, (sal_uChar*) aRet.getArray() ) != nSize )
aRet.realloc( 0 );
}
return aRet;
}
//------------------------------------------------------------------------
// convert a openoffice metafile picture to a windows metafile picture
//------------------------------------------------------------------------
HMETAFILEPICT SAL_CALL OOMFPictToWinMFPict( Sequence< sal_Int8 >& aOOMetaFilePict )
{
HMETAFILEPICT hPict = NULL;
HMETAFILE hMtf = SetMetaFileBitsEx( aOOMetaFilePict.getLength(), (sal_uChar*) aOOMetaFilePict.getConstArray() );
if( hMtf )
{
METAFILEPICT* pPict = (METAFILEPICT*) GlobalLock( hPict = GlobalAlloc( GHND, sizeof( METAFILEPICT ) ) );
pPict->mm = 8;
pPict->xExt = 0;
pPict->yExt = 0;
pPict->hMF = hMtf;
GlobalUnlock( hPict );
}
return hPict;
}
//-----------------------------------------------------------------------------
// convert a openoffice metafile picture to a windows enhanced metafile picture
//-----------------------------------------------------------------------------
HENHMETAFILE SAL_CALL OOMFPictToWinENHMFPict( Sequence< sal_Int8 >& aOOMetaFilePict )
{
HENHMETAFILE hEnhMtf = SetEnhMetaFileBits( aOOMetaFilePict.getLength(), (sal_uChar*) aOOMetaFilePict.getConstArray() );
return hEnhMtf;
}
//------------------------------------------------------------------------
// convert a windows device independent bitmap into a openoffice bitmap
//------------------------------------------------------------------------
Sequence< sal_Int8 > SAL_CALL WinDIBToOOBMP( const Sequence< sal_Int8 >& aWinDIB )
{
OSL_ENSURE(aWinDIB.getLength() > sizeof(BITMAPINFOHEADER), "CF_DIBV5/CF_DIB too small (!)");
Sequence< sal_Int8 > ooBmpStream;
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));
DWORD nOffset(sizeof(BITMAPFILEHEADER) + nSizeInfoOrV5);
rtl_copyMemory(pBmpFileHdr + 1, pBmpInfoHdr, aWinDIB.getLength());
if(pBmpInfoHdr->biBitCount <= 8)
{
nOffset += (pBmpInfoHdr->biClrUsed ? pBmpInfoHdr->biClrUsed : (1 << pBmpInfoHdr->biBitCount)) << 2;
}
else if((BI_BITFIELDS == pBmpInfoHdr->biCompression ) && ((16 == pBmpInfoHdr->biBitCount ) || (32 == pBmpInfoHdr->biBitCount )))
{
nOffset += 12;
}
pBmpFileHdr->bfType = 'MB';
pBmpFileHdr->bfSize = 0; // maybe: nMemSize + sizeof(BITMAPFILEHEADER)
pBmpFileHdr->bfReserved1 = 0;
pBmpFileHdr->bfReserved2 = 0;
pBmpFileHdr->bfOffBits = nOffset;
return ooBmpStream;
}
//------------------------------------------------------------------------
// convert a openoffice bitmap into a windows device independent bitmap
//------------------------------------------------------------------------
Sequence< sal_Int8 > SAL_CALL OOBmpToWinDIB( Sequence< sal_Int8 >& aOOBmp )
{
Sequence< sal_Int8 > winDIBStream( aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) );
rtl_copyMemory( winDIBStream.getArray( ),
aOOBmp.getArray( ) + sizeof( BITMAPFILEHEADER ),
aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) );
return winDIBStream;
}
//------------------------------------------------------------------------------
// converts the openoffice text/html clipboard format to the HTML Format
// well known under MS Windows
// the MS HTML Format has a header before the real html data
//
// Version:1.0 Version number of the clipboard. Staring is 0.9
// StartHTML: Byte count from the beginning of the clipboard to the start
// of the context, or -1 if no context
// EndHTML: Byte count from the beginning of the clipboard to the end
// of the context, or -1 if no context
// StartFragment: Byte count from the beginning of the clipboard to the
// start of the fragment
// EndFragment: Byte count from the beginning of the clipboard to the
// end of the fragment
// StartSelection: Byte count from the beginning of the clipboard to the
// start of the selection
// EndSelection: Byte count from the beginning of the clipboard to the
// end of the selection
//
// StartSelection and EndSelection are optional
// The fragment should be preceded and followed by the HTML comments
// <!--StartFragment--> and <!--EndFragment--> (no space between !-- and the
// text
//------------------------------------------------------------------------------
/*
Sequence< sal_Int8 > SAL_CALL TextHtmlToHTMLFormat( Sequence< sal_Int8 >& aTextHtml )
{
OSL_ASSERT( aTextHtml.getLength( ) > 0 );
// check parameter
if ( !(aTextHtml.getLength( ) > 0) )
return Sequence< sal_Int8 >( );
// we create a buffer with the approximated size of
// the HTML Format header
char aHTMLFmtHdr[120];
rtl_zeroMemory( aHTMLFmtHdr, sizeof( aHTMLFmtHdr ) );
// fill the buffer with dummy values to calc the
// exact length
wsprintf(
aHTMLFmtHdr,
"Version:1.0\nStartHTML:%010d\r\nnEndHTML:%010d\r\nStartFragment:%010\r\nnEndFragment:%010d\r\n", 0, 0, 0, 0 );
sal_uInt32 lHTMLFmtHdr = rtl_str_getLength( aHTMLFmtHdr );
// the office allways writes the start
// and end html tag in upper cases and
// without spaces
// both tags don't allow parameters
OString startHtmlTag( "<HTML>" );
OString endHtmlTag( "</HTML>" );
// we don't include '>' into the search
// because the body tag allows parameters
// e.g. <BODY param>
// #92840#
OString startBodyTag( "<BODY" );
OString endBodyTag( "</BODY" );
OString textHtml(
reinterpret_cast< const sal_Char* >( aTextHtml.getConstArray( ) ),
aTextHtml.getLength( ) );
sal_Int32 nStartHtml = textHtml.indexOf( startHtmlTag );
sal_Int32 nEndHtml = textHtml.indexOf( endHtmlTag );
sal_Int32 nStartFrgmt = textHtml.indexOf( startBodyTag );
sal_Int32 nEndFrgmt = textHtml.indexOf( endBodyTag );
OSL_ASSERT( (nStartHtml >= 0) && (nEndHtml > nStartHtml) && (nStartFrgmt > nStartHtml) && (nEndFrgmt > nStartFrgmt) );
Sequence< sal_Int8 > aHTMLFmtSequence;
if ( (nStartHtml > -1) && (nEndHtml > -1) && (nStartFrgmt > -1) && (nEndFrgmt > -1) )
{
nStartHtml = nStartHtml + lHTMLFmtHdr - 1; // we start one before <HTML> Word 2000 does also so
nEndHtml = nEndHtml + lHTMLFmtHdr + endHtmlTag.getLength( ) + 1; // our SOffice 5.2 wants 2 behind </HTML>?
nStartFrgmt = nStartFrgmt + startBodyTag.getLength( ) + lHTMLFmtHdr; // after the <BODY> tag
nEndFrgmt = nEndFrgmt + lHTMLFmtHdr;
// fill the html header
rtl_zeroMemory( aHTMLFmtHdr, sizeof( aHTMLFmtHdr ) );
wsprintf(
aHTMLFmtHdr,
"Version:1.0\nStartHTML:%010d\r\nEndHTML:%010d\r\nStartFragment:%010d\r\nEndFragment:%010d\r\n",
nStartHtml, nEndHtml, nStartFrgmt, nEndFrgmt );
// we add space for a trailing \0
aHTMLFmtSequence.realloc( lHTMLFmtHdr + aTextHtml.getLength( ) + 1 );
rtl_zeroMemory( aHTMLFmtSequence.getArray( ), aHTMLFmtSequence.getLength( ) );
// copy the HTML Format header
rtl_copyMemory(
static_cast< LPVOID >( aHTMLFmtSequence.getArray( ) ),
static_cast< LPVOID >( aHTMLFmtHdr ), lHTMLFmtHdr );
// concat the text/html
rtl_copyMemory(
static_cast< LPVOID >( aHTMLFmtSequence.getArray( ) + lHTMLFmtHdr ),
static_cast< LPVOID >( aTextHtml.getArray( ) ),
aTextHtml.getLength( ) );
}
return aHTMLFmtSequence;
}
*/
std::string GetHtmlFormatHeader(size_t startHtml, size_t endHtml, size_t startFragment, size_t endFragment)
{
std::ostringstream htmlHeader;
htmlHeader << "Version:1.0" << '\r' << '\n';
htmlHeader << "StartHTML:" << std::setw(10) << std::setfill('0') << std::dec << startHtml << '\r' << '\n';
htmlHeader << "EndHTML:" << std::setw(10) << std::setfill('0') << std::dec << endHtml << '\r' << '\n';
htmlHeader << "StartFragment:" << std::setw(10) << std::setfill('0') << std::dec << startFragment << '\r' << '\n';
htmlHeader << "EndFragment:" << std::setw(10) << std::setfill('0') << std::dec << endFragment << '\r' << '\n';
return htmlHeader.str();
}
// the office allways writes the start and end html tag in upper cases and
// without spaces both tags don't allow parameters
const std::string TAG_HTML = std::string("<HTML>");
const std::string TAG_END_HTML = std::string("</HTML>");
// The body tag may have parameters so we need to search for the
// closing '>' manually e.g. <BODY param> #92840#
const std::string TAG_BODY = std::string("<BODY");
const std::string TAG_END_BODY = std::string("</BODY");
Sequence<sal_Int8> SAL_CALL TextHtmlToHTMLFormat(Sequence<sal_Int8>& aTextHtml)
{
OSL_ASSERT(aTextHtml.getLength() > 0);
if (!(aTextHtml.getLength() > 0))
return Sequence<sal_Int8>();
// fill the buffer with dummy values to calc the exact length
std::string dummyHtmlHeader = GetHtmlFormatHeader(0, 0, 0, 0);
size_t lHtmlFormatHeader = dummyHtmlHeader.length();
std::string textHtml(
reinterpret_cast<const sal_Char*>(aTextHtml.getConstArray()),
reinterpret_cast<const sal_Char*>(aTextHtml.getConstArray()) + aTextHtml.getLength());
std::string::size_type nStartHtml = textHtml.find(TAG_HTML) + lHtmlFormatHeader - 1; // we start one before '<HTML>' Word 2000 does also so
std::string::size_type nEndHtml = textHtml.find(TAG_END_HTML) + lHtmlFormatHeader + TAG_END_HTML.length() + 1; // our SOffice 5.2 wants 2 behind </HTML>?
// The body tag may have parameters so we need to search for the
// closing '>' manually e.g. <BODY param> #92840#
std::string::size_type nStartFragment = textHtml.find(">", textHtml.find(TAG_BODY)) + lHtmlFormatHeader + 1;
std::string::size_type nEndFragment = textHtml.find(TAG_END_BODY) + lHtmlFormatHeader;
std::string htmlFormat = GetHtmlFormatHeader(nStartHtml, nEndHtml, nStartFragment, nEndFragment);
htmlFormat += textHtml;
Sequence<sal_Int8> byteSequence(htmlFormat.length() + 1); // space the trailing '\0'
rtl_zeroMemory(byteSequence.getArray(), byteSequence.getLength());
rtl_copyMemory(
static_cast<void*>(byteSequence.getArray()),
static_cast<const void*>(htmlFormat.c_str()),
htmlFormat.length());
return byteSequence;
}
std::wstring getFileExtension(const std::wstring& aFilename)
{
std::wstring::size_type idx = aFilename.rfind(L".");
if ((idx != std::wstring::npos))
{
return std::wstring(aFilename, idx);
}
return std::wstring();
}
const std::wstring SHELL_LINK_FILE_EXTENSION = L".lnk";
bool isShellLink(const std::wstring& aFilename)
{
std::wstring ext = getFileExtension(aFilename);
return (_wcsicmp(ext.c_str(), SHELL_LINK_FILE_EXTENSION.c_str()) == 0);
}
/** Resolve a Windows Shell Link (lnk) file. If a resolution
is not possible simply return the provided name of the
lnk file. */
std::wstring getShellLinkTarget(const std::wstring& aLnkFile)
{
OSL_ASSERT(isShellLink(aLnkFile));
std::wstring target = aLnkFile;
try
{
sal::systools::COMReference<IShellLinkA> pIShellLink;
HRESULT hr = CoCreateInstance(
CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<LPVOID*>(&pIShellLink));
if (FAILED(hr))
return target;
sal::systools::COMReference<IPersistFile> pIPersistFile =
pIShellLink.QueryInterface<IPersistFile>(IID_IPersistFile);
hr = pIPersistFile->Load(aLnkFile.c_str(), STGM_READ);
if (FAILED(hr))
return target;
hr = pIShellLink->Resolve(NULL, SLR_UPDATE | SLR_NO_UI);
if (FAILED(hr))
return target;
char pathA[MAX_PATH];
WIN32_FIND_DATA wfd;
hr = pIShellLink->GetPath(pathA, MAX_PATH, &wfd, SLGP_RAWPATH);
if (FAILED(hr))
return target;
wchar_t pathW[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, pathA, -1, pathW, MAX_PATH);
target = pathW;
}
catch(sal::systools::ComError& ex)
{
OSL_ENSURE(false, ex.what());
ex = ex;
}
return target;
}
typedef std::vector<std::wstring> FileList_t;
typedef FileList_t::value_type FileList_ValueType_t;
typedef Sequence<sal_Int8> ByteSequence_t;
/* Calculate the size required for turning a string list into
a double '\0' terminated string buffer */
size_t CalcSizeForStringListBuffer(const FileList_t& fileList)
{
if (fileList.size() == 0)
return 0;
size_t size = 1; // one for the very final '\0'
FileList_t::const_iterator iter_end = fileList.end();
for (FileList_t::const_iterator iter = fileList.begin(); iter != iter_end; ++iter)
{
size += iter->length() + 1; // length including terminating '\0'
}
return (size * sizeof(FileList_ValueType_t::value_type));
}
ByteSequence_t FileListToByteSequence(const FileList_t& fileList)
{
ByteSequence_t bseq;
size_t size = CalcSizeForStringListBuffer(fileList);
if (size > 0)
{
bseq.realloc(size);
wchar_t* p = reinterpret_cast<wchar_t*>(bseq.getArray());
ZeroMemory(p, size);
FileList_t::const_iterator iter;
FileList_t::const_iterator iter_end = fileList.end();
for (iter = fileList.begin(); iter != iter_end; ++iter)
{
wcsncpy(p, iter->c_str(), iter->length());
p += (iter->length() + 1);
}
}
return bseq;
}
ByteSequence_t CF_HDROPToFileList(HGLOBAL hGlobal)
{
UINT nFiles = DragQueryFileW((HDROP)hGlobal, 0xFFFFFFFF, NULL, 0);
FileList_t files;
for (UINT i = 0; i < nFiles; i++)
{
wchar_t buff[MAX_PATH];
/*UINT size =*/ DragQueryFileW((HDROP)hGlobal, i, buff, MAX_PATH);
std::wstring filename = buff;
if (isShellLink(filename))
filename = getShellLinkTarget(filename);
files.push_back(filename);
}
return FileListToByteSequence(files);
}
//------------------------------------------------------------------------
// convert a windows bitmap handle into a openoffice bitmap
//------------------------------------------------------------------------
Sequence< sal_Int8 > SAL_CALL WinBITMAPToOOBMP( HBITMAP aHBMP )
{
Sequence< sal_Int8 > ooBmpStream;
SIZE aBmpSize;
if( GetBitmapDimensionEx( aHBMP, &aBmpSize ) )
{
// fill bitmap info header
size_t nDataBytes = 4 * aBmpSize.cy * aBmpSize.cy;
Sequence< sal_Int8 > aBitmapStream(
sizeof(BITMAPINFO) +
nDataBytes
);
PBITMAPINFOHEADER pBmp = (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 = (DWORD)nDataBytes;
pBmp->biXPelsPerMeter = 1000;
pBmp->biYPelsPerMeter = 1000;
pBmp->biClrUsed = 0;
pBmp->biClrImportant = 0;
if( GetDIBits( 0, // DC, 0 is a default GC, basically that of the desktop
aHBMP,
0, aBmpSize.cy,
aBitmapStream.getArray() + sizeof(BITMAPINFO),
(LPBITMAPINFO)pBmp,
DIB_RGB_COLORS ) )
{
ooBmpStream = WinDIBToOOBMP( aBitmapStream );
}
}
return ooBmpStream;
}