LibreOffice Module dtrans (master)  1
FmtFilter.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <string.h>
21 
22 #include "FmtFilter.hxx"
23 
24 #include <o3tl/safeint.hxx>
25 #include <osl/diagnose.h>
26 
27 #include <shobjidl.h>
28 #include <shlguid.h>
29 #include <objidl.h>
30 #include <shellapi.h>
31 
32 #include <string>
33 #include <sstream>
34 #include <vector>
35 #include <iomanip>
36 
37 #include <systools/win32/comtools.hxx>
38 
39 using namespace com::sun::star::uno;
40 
41 namespace {
42 
43 #pragma pack(2)
44 struct METAFILEHEADER
45 {
46  DWORD key;
47  short hmf;
48  SMALL_RECT bbox;
49  WORD inch;
50  DWORD reserved;
51  WORD checksum;
52 };
53 #pragma pack()
54 
55 }
56 
57 // convert a windows metafile picture to a LibreOffice metafile picture
58 
60 {
61  OSL_ASSERT( aMetaFilePict.getLength( ) == sizeof( METAFILEPICT ) );
62 
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 );
67 
68  if ( nCount > 0 )
69  {
70  mfpictStream.realloc( nCount + sizeof( METAFILEHEADER ) );
71 
72  METAFILEHEADER* pMFHeader = reinterpret_cast< METAFILEHEADER* >( mfpictStream.getArray( ) );
73  SMALL_RECT aRect = { 0,
74  0,
75  static_cast< short >( pMFPict->xExt ),
76  static_cast< short >( pMFPict->yExt ) };
77  USHORT nInch;
78 
79  switch( pMFPict->mm )
80  {
81  case MM_TEXT:
82  nInch = 72;
83  break;
84 
85  case MM_LOMETRIC:
86  nInch = 100;
87  break;
88 
89  case MM_HIMETRIC:
90  nInch = 1000;
91  break;
92 
93  case MM_LOENGLISH:
94  nInch = 254;
95  break;
96 
97  case MM_HIENGLISH:
98  case MM_ISOTROPIC:
99  case MM_ANISOTROPIC:
100  nInch = 2540;
101  break;
102 
103  case MM_TWIPS:
104  nInch = 1440;
105  break;
106 
107  default:
108  nInch = 576;
109  }
110 
111  pMFHeader->key = 0x9AC6CDD7L;
112  pMFHeader->hmf = 0;
113  pMFHeader->bbox = aRect;
114  pMFHeader->inch = nInch;
115  pMFHeader->reserved = 0;
116  pMFHeader->checksum = 0;
117 
118  char* pMFBuff = reinterpret_cast< char* >( mfpictStream.getArray( ) );
119 
120  nCount = GetMetaFileBitsEx( pMFPict->hMF, nCount, pMFBuff + sizeof( METAFILEHEADER ) );
121  OSL_ASSERT( nCount > 0 );
122  }
123 
124  return mfpictStream;
125 }
126 
127 // convert a windows enhanced metafile to a LibreOffice metafile
128 
129 Sequence< sal_Int8 > WinENHMFPictToOOMFPict( HENHMETAFILE hEnhMetaFile )
130 {
132  UINT nSize = 0;
133 
134  if( hEnhMetaFile &&
135  ( ( nSize = GetEnhMetaFileBits( hEnhMetaFile, 0, nullptr ) ) != 0 ) )
136  {
137  aRet.realloc( nSize );
138 
139  if( GetEnhMetaFileBits( hEnhMetaFile, nSize, reinterpret_cast<unsigned char*>(aRet.getArray()) ) != nSize )
140  aRet.realloc( 0 );
141  }
142 
143  return aRet;
144 }
145 
146 // convert a LibreOffice metafile picture to a windows metafile picture
147 
148 HMETAFILEPICT OOMFPictToWinMFPict( Sequence< sal_Int8 > const & aOOMetaFilePict )
149 {
150  HMETAFILEPICT hPict = nullptr;
151  HMETAFILE hMtf = SetMetaFileBitsEx( aOOMetaFilePict.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict.getConstArray()) );
152 
153  if( hMtf )
154  {
155  METAFILEPICT* pPict = static_cast<METAFILEPICT*>(GlobalLock( hPict = GlobalAlloc( GHND, sizeof( METAFILEPICT ) ) ));
156 
157  pPict->mm = 8;
158  pPict->xExt = 0;
159  pPict->yExt = 0;
160  pPict->hMF = hMtf;
161 
162  GlobalUnlock( hPict );
163  }
164 
165  return hPict;
166 }
167 
168 // convert a LibreOffice metafile picture to a windows enhanced metafile picture
169 
170 HENHMETAFILE OOMFPictToWinENHMFPict( Sequence< sal_Int8 > const & aOOMetaFilePict )
171 {
172  HENHMETAFILE hEnhMtf = SetEnhMetaFileBits( aOOMetaFilePict.getLength(), reinterpret_cast<unsigned char const *>(aOOMetaFilePict.getConstArray()) );
173 
174  return hEnhMtf;
175 }
176 
177 // convert a windows device independent bitmap into a LibreOffice bitmap
178 
180 {
181  OSL_ENSURE(o3tl::make_unsigned(aWinDIB.getLength()) > sizeof(BITMAPINFOHEADER), "CF_DIBV5/CF_DIB too small (!)");
182  Sequence< sal_Int8 > ooBmpStream;
183 
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);
189 
190  memcpy(pBmpFileHdr + 1, pBmpInfoHdr, aWinDIB.getLength());
191 
192  if(pBmpInfoHdr->biBitCount <= 8)
193  {
194  nOffset += (pBmpInfoHdr->biClrUsed ? pBmpInfoHdr->biClrUsed : (1 << pBmpInfoHdr->biBitCount)) << 2;
195  }
196  else if((BI_BITFIELDS == pBmpInfoHdr->biCompression ) && ((16 == pBmpInfoHdr->biBitCount ) || (32 == pBmpInfoHdr->biBitCount )))
197  {
198  nOffset += 12;
199  }
200 
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;
206 
207  return ooBmpStream;
208 }
209 
210 // convert a LibreOffice bitmap into a windows device independent bitmap
211 
213 {
214  Sequence< sal_Int8 > winDIBStream( aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) );
215 
216  memcpy( winDIBStream.getArray( ),
217  aOOBmp.getArray( ) + sizeof( BITMAPFILEHEADER ),
218  aOOBmp.getLength( ) - sizeof( BITMAPFILEHEADER ) );
219 
220  return winDIBStream;
221 }
222 
223 static std::string GetHtmlFormatHeader(size_t startHtml, size_t endHtml, size_t startFragment, size_t endFragment)
224 {
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();
232 }
233 
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>");
238 
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");
243 
245 {
246  OSL_ASSERT(aTextHtml.getLength() > 0);
247 
248  if (aTextHtml.getLength() <= 0)
249  return Sequence<sal_Int8>();
250 
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();
254 
255  std::string textHtml(
256  reinterpret_cast<const char*>(aTextHtml.getConstArray()),
257  reinterpret_cast<const char*>(aTextHtml.getConstArray()) + aTextHtml.getLength());
258 
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>?
261 
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;
266 
267  std::string htmlFormat = GetHtmlFormatHeader(nStartHtml, nEndHtml, nStartFragment, nEndFragment);
268  htmlFormat += textHtml;
269 
270  Sequence<sal_Int8> byteSequence(htmlFormat.length() + 1); // space the trailing '\0'
271  memset(byteSequence.getArray(), 0, byteSequence.getLength());
272 
273  memcpy(
274  static_cast<void*>(byteSequence.getArray()),
275  static_cast<const void*>(htmlFormat.c_str()),
276  htmlFormat.length());
277 
278  return byteSequence;
279 }
280 
281 static std::wstring getFileExtension(const std::wstring& aFilename)
282 {
283  std::wstring::size_type idx = aFilename.rfind(L".");
284  if (idx != std::wstring::npos)
285  {
286  return std::wstring(aFilename, idx);
287  }
288  return std::wstring();
289 }
290 
291 const std::wstring SHELL_LINK_FILE_EXTENSION = L".lnk";
292 
293 static bool isShellLink(const std::wstring& aFilename)
294 {
295  std::wstring ext = getFileExtension(aFilename);
296  return (_wcsicmp(ext.c_str(), SHELL_LINK_FILE_EXTENSION.c_str()) == 0);
297 }
298 
302 static std::wstring getShellLinkTarget(const std::wstring& aLnkFile)
303 {
304  OSL_ASSERT(isShellLink(aLnkFile));
305 
306  std::wstring target = aLnkFile;
307 
308  try
309  {
310  sal::systools::COMReference<IShellLinkW> pIShellLink;
311  HRESULT hr = CoCreateInstance(
312  CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, reinterpret_cast<LPVOID*>(&pIShellLink));
313  if (FAILED(hr))
314  return target;
315 
316  sal::systools::COMReference<IPersistFile> pIPersistFile =
317  pIShellLink.QueryInterface<IPersistFile>(IID_IPersistFile);
318 
319  hr = pIPersistFile->Load(aLnkFile.c_str(), STGM_READ);
320  if (FAILED(hr))
321  return target;
322 
323  hr = pIShellLink->Resolve(nullptr, SLR_UPDATE | SLR_NO_UI);
324  if (FAILED(hr))
325  return target;
326 
327  wchar_t pathW[MAX_PATH];
328  WIN32_FIND_DATAW wfd;
329  hr = pIShellLink->GetPath(pathW, MAX_PATH, &wfd, SLGP_RAWPATH);
330  if (FAILED(hr))
331  return target;
332 
333  target = pathW;
334  }
335  catch(sal::systools::ComError& ex)
336  {
337  OSL_FAIL(ex.what());
338  }
339  return target;
340 }
341 
343 
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)
347 {
348  if ( fileList.empty() )
349  return 0;
350 
351  size_t size = 1; // one for the very final '\0'
352  for (auto const& elem : fileList)
353  {
354  size += elem.length() + 1; // length including terminating '\0'
355  }
356  return (size * sizeof(std::vector<std::wstring>::value_type::value_type));
357 }
358 
359 static ByteSequence_t FileListToByteSequence(const std::vector<std::wstring>& fileList)
360 {
361  ByteSequence_t bseq;
362  size_t size = CalcSizeForStringListBuffer(fileList);
363 
364  if (size > 0)
365  {
366  bseq.realloc(size);
367  wchar_t* p = reinterpret_cast<wchar_t*>(bseq.getArray());
368  ZeroMemory(p, size);
369 
370  for (auto const& elem : fileList)
371  {
372  wcsncpy(p, elem.c_str(), elem.length());
373  p += (elem.length() + 1);
374  }
375  }
376  return bseq;
377 }
378 
379 css::uno::Sequence<sal_Int8> CF_HDROPToFileList(HGLOBAL hGlobal)
380 {
381  UINT nFiles = DragQueryFileW(static_cast<HDROP>(hGlobal), 0xFFFFFFFF, nullptr, 0);
382  std::vector<std::wstring> files;
383 
384  for (UINT i = 0; i < nFiles; i++)
385  {
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);
392  }
393  return FileListToByteSequence(files);
394 }
395 
396 // convert a windows bitmap handle into a LibreOffice bitmap
397 
399 {
400  Sequence< sal_Int8 > ooBmpStream;
401 
402  SIZE aBmpSize;
403  if( GetBitmapDimensionEx( aHBMP, &aBmpSize ) )
404  {
405  // fill bitmap info header
406  size_t nDataBytes = 4 * aBmpSize.cy * aBmpSize.cy;
407  Sequence< sal_Int8 > aBitmapStream(
408  sizeof(BITMAPINFO) +
409  nDataBytes
410  );
411  PBITMAPINFOHEADER pBmp = reinterpret_cast<PBITMAPINFOHEADER>(aBitmapStream.getArray());
412  pBmp->biSize = sizeof( BITMAPINFOHEADER );
413  pBmp->biWidth = aBmpSize.cx;
414  pBmp->biHeight = aBmpSize.cy;
415  pBmp->biPlanes = 1;
416  pBmp->biBitCount = 32;
417  pBmp->biCompression = BI_RGB;
418  pBmp->biSizeImage = static_cast<DWORD>(nDataBytes);
419  pBmp->biXPelsPerMeter = 1000;
420  pBmp->biYPelsPerMeter = 1000;
421  pBmp->biClrUsed = 0;
422  pBmp->biClrImportant = 0;
423  if( GetDIBits( nullptr, // DC, 0 is a default GC, basically that of the desktop
424  aHBMP,
425  0, aBmpSize.cy,
426  aBitmapStream.getArray() + sizeof(BITMAPINFO),
427  reinterpret_cast<LPBITMAPINFO>(pBmp),
428  DIB_RGB_COLORS ) )
429  {
430  ooBmpStream = WinDIBToOOBMP( aBitmapStream );
431  }
432  }
433 
434  return ooBmpStream;
435 }
436 
437 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Sequence< sal_Int8 > WinENHMFPictToOOMFPict(HENHMETAFILE hEnhMetaFile)
Definition: FmtFilter.cxx:129
#define MM_LOMETRIC
static std::wstring getFileExtension(const std::wstring &aFilename)
Definition: FmtFilter.cxx:281
Sequence< sal_Int8 > ByteSequence_t
Definition: FmtFilter.cxx:342
EXTERN_C BOOL BOOL const wchar_t *pProgramPath HRESULT hr
HMETAFILEPICT OOMFPictToWinMFPict(Sequence< sal_Int8 > const &aOOMetaFilePict)
Definition: FmtFilter.cxx:148
const std::wstring SHELL_LINK_FILE_EXTENSION
Definition: FmtFilter.cxx:291
css::uno::Sequence< sal_Int8 > CF_HDROPToFileList(HGLOBAL hGlobal)
Return a FileList in which Windows Shell Links (lnk) are resolved.
Definition: FmtFilter.cxx:379
#define MM_TWIPS
static bool isShellLink(const std::wstring &aFilename)
Definition: FmtFilter.cxx:293
HENHMETAFILE OOMFPictToWinENHMFPict(Sequence< sal_Int8 > const &aOOMetaFilePict)
Definition: FmtFilter.cxx:170
static std::string GetHtmlFormatHeader(size_t startHtml, size_t endHtml, size_t startFragment, size_t endFragment)
Definition: FmtFilter.cxx:223
const std::string TAG_END_BODY("</body")
int nCount
#define MM_ISOTROPIC
const std::string TAG_HTML("<html>")
#define MM_ANISOTROPIC
int i
Sequence< sal_Int8 > WinBITMAPToOOBMP(HBITMAP aHBMP)
Definition: FmtFilter.cxx:398
const std::string TAG_END_HTML("</html>")
#define MM_LOENGLISH
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
Sequence< sal_Int8 > TextHtmlToHTMLFormat(Sequence< sal_Int8 > const &aTextHtml)
Definition: FmtFilter.cxx:244
size
unsigned short WORD
#define MM_HIENGLISH
const sal_uInt16 idx[]
static std::wstring getShellLinkTarget(const std::wstring &aLnkFile)
Resolve a Windows Shell Link (lnk) file.
Definition: FmtFilter.cxx:302
static size_t CalcSizeForStringListBuffer(const std::vector< std::wstring > &fileList)
Definition: FmtFilter.cxx:346
Sequence< sal_Int8 > WinMFPictToOOMFPict(Sequence< sal_Int8 > &aMetaFilePict)
Definition: FmtFilter.cxx:59
const std::string TAG_BODY("<body")
static ByteSequence_t FileListToByteSequence(const std::vector< std::wstring > &fileList)
Definition: FmtFilter.cxx:359
Sequence< sal_Int8 > OOBmpToWinDIB(Sequence< sal_Int8 > &aOOBmp)
Definition: FmtFilter.cxx:212
void * p
Sequence< sal_Int8 > WinDIBToOOBMP(const Sequence< sal_Int8 > &aWinDIB)
Definition: FmtFilter.cxx:179
#define MAX_PATH
#define MM_TEXT
#define MM_HIMETRIC