1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_shell.hxx" 30 31 #include "internal/global.hxx" 32 33 #ifndef INFOTIPS_HXX_INCLUDED 34 #include "internal/thumbviewer.hxx" 35 #endif 36 #include "internal/shlxthdl.hxx" 37 #include "internal/registry.hxx" 38 #include "internal/fileextensions.hxx" 39 #include "internal/config.hxx" 40 #include "internal/zipfile.hxx" 41 #include "internal/utilities.hxx" 42 43 #include "internal/resource.h" 44 45 #include <stdio.h> 46 #include <utility> 47 #include <stdlib.h> 48 49 #if defined _MSC_VER 50 #pragma warning(push, 1) 51 #endif 52 #include <shellapi.h> 53 #if defined _MSC_VER 54 #pragma warning(pop) 55 #endif 56 #include <memory> 57 58 extern HINSTANCE g_hModule; 59 60 namespace internal 61 { 62 /* The signet.png used for thumbnails of signed documents 63 is contained as resource in this module, the resource 64 id is 2000 */ 65 void LoadSignetImageFromResource(ZipFile::ZipContentBuffer_t& buffer) 66 { 67 HRSRC hrc = FindResource(g_hModule, TEXT("#2000"), RT_RCDATA); 68 DWORD size = SizeofResource(g_hModule, hrc); 69 HGLOBAL hglob = LoadResource(g_hModule, hrc); 70 char* data = reinterpret_cast<char*>(LockResource(hglob)); 71 buffer = ZipFile::ZipContentBuffer_t(data, data + size); 72 } 73 74 bool IsSignedDocument(const ZipFile* zipfile) 75 { 76 return zipfile->HasContent("META-INF/documentsignatures.xml"); 77 } 78 79 bool IsWindowsXP() 80 { 81 OSVERSIONINFO osvi; 82 ZeroMemory(&osvi, sizeof(osvi)); 83 osvi.dwOSVersionInfoSize = sizeof(osvi); 84 GetVersionEx(&osvi); 85 86 return ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && 87 ((osvi.dwMajorVersion >= 5) && (osvi.dwMinorVersion >= 1))); 88 } 89 90 /* Calculate where to position the signet image. 91 On Windows ME we need to shift the signet a 92 little bit to the left because Windows ME 93 puts an overlay icon to the lower right 94 corner of a thumbnail image so that our signet 95 we be hidden. */ 96 Gdiplus::Point CalcSignetPosition( 97 const Gdiplus::Rect& canvas, const Gdiplus::Rect& thumbnail_border, const Gdiplus::Rect& signet) 98 { 99 int x = 0; 100 int y = 0; 101 int hoffset = canvas.GetRight() - thumbnail_border.GetRight(); 102 int voffset = canvas.GetBottom() - thumbnail_border.GetBottom(); 103 104 if (hoffset > voffset) 105 { 106 x = thumbnail_border.GetRight() - signet.GetRight() + min(signet.GetRight() / 2, hoffset); 107 y = thumbnail_border.GetBottom() - signet.GetBottom(); 108 } 109 else 110 { 111 x = thumbnail_border.GetRight() - signet.GetRight(); 112 y = thumbnail_border.GetBottom() - signet.GetBottom() + min(signet.GetBottom() / 2, voffset); 113 } 114 115 if (!IsWindowsXP()) 116 x -= 15; 117 118 return Gdiplus::Point(x,y); 119 } 120 } 121 122 class StreamOnZipBuffer : public IStream 123 { 124 public: 125 StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer); 126 127 // IUnknown 128 virtual ULONG STDMETHODCALLTYPE AddRef(); 129 virtual ULONG STDMETHODCALLTYPE Release( void); 130 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject); 131 132 // IStream 133 virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead); 134 virtual HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten); 135 virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition); 136 virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize); 137 virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten); 138 virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags); 139 virtual HRESULT STDMETHODCALLTYPE Revert(void); 140 virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType); 141 virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType); 142 virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag); 143 virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm); 144 145 private: 146 LONG ref_count_; 147 const ZipFile::ZipContentBuffer_t& ref_zip_buffer_; 148 size_t pos_; 149 }; 150 151 StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer) : 152 ref_count_(1), 153 ref_zip_buffer_(zip_buffer), 154 pos_(0) 155 { 156 } 157 158 // IUnknown methods 159 160 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::AddRef(void) 161 { 162 return InterlockedIncrement(&ref_count_); 163 } 164 165 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::Release( void) 166 { 167 long refcnt = InterlockedDecrement(&ref_count_); 168 169 if (0 == ref_count_) 170 delete this; 171 172 return refcnt; 173 } 174 175 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) 176 { 177 *ppvObject = 0; 178 IUnknown* pUnk = 0; 179 180 if ((IID_IUnknown == riid) || (IID_IStream == riid)) 181 { 182 pUnk = static_cast<IStream*>(this); 183 pUnk->AddRef(); 184 *ppvObject = pUnk; 185 return S_OK; 186 } 187 return E_NOINTERFACE; 188 } 189 190 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Read(void *pv, ULONG cb, ULONG *pcbRead) 191 { 192 if (pv == NULL) 193 return STG_E_INVALIDPOINTER; 194 195 size_t size = ref_zip_buffer_.size(); 196 197 if (pos_ > size) 198 return S_FALSE; 199 200 char* p = reinterpret_cast<char*>(pv); 201 ULONG read = 0; 202 203 for ( ;(pos_ < size) && (cb > 0); pos_++, cb--, read++) 204 *p++ = ref_zip_buffer_[pos_]; 205 206 if (pcbRead) 207 *pcbRead = read; 208 209 return S_OK; 210 } 211 212 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *) 213 { 214 __int64 size = (__int64) ref_zip_buffer_.size(); 215 __int64 p = 0; 216 217 switch (dwOrigin) 218 { 219 case STREAM_SEEK_SET: 220 break; 221 case STREAM_SEEK_CUR: 222 p = (__int64) pos_; 223 break; 224 case STREAM_SEEK_END: 225 p = size - 1; 226 break; 227 } 228 229 HRESULT hr = STG_E_INVALIDFUNCTION; 230 231 p += dlibMove.QuadPart; 232 233 if ( ( p >= 0 ) && (p < size) ) 234 { 235 pos_ = (size_t) p; 236 hr = S_OK; 237 } 238 return hr; 239 } 240 241 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Stat(STATSTG *pstatstg, DWORD grfStatFlag) 242 { 243 if (pstatstg == NULL) 244 return STG_E_INVALIDPOINTER; 245 246 ZeroMemory(pstatstg, sizeof(STATSTG)); 247 248 if (grfStatFlag == STATFLAG_DEFAULT) 249 { 250 size_t sz = 4 * sizeof(wchar_t); 251 wchar_t* name = reinterpret_cast<wchar_t*>(CoTaskMemAlloc(sz)); 252 ZeroMemory(name, sz); 253 memcpy(name, L"png", 3 * sizeof(wchar_t)); 254 pstatstg->pwcsName = name; 255 } 256 257 pstatstg->type = STGTY_LOCKBYTES; 258 259 ULARGE_INTEGER uli; 260 uli.LowPart = ref_zip_buffer_.size(); 261 uli.HighPart = 0; 262 263 pstatstg->cbSize = uli; 264 265 return S_OK; 266 } 267 268 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Write(void const *, ULONG, ULONG *) 269 { return E_NOTIMPL; } 270 271 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::SetSize(ULARGE_INTEGER) 272 { return E_NOTIMPL; } 273 274 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::CopyTo(IStream *, ULARGE_INTEGER, ULARGE_INTEGER *, ULARGE_INTEGER *) 275 { return E_NOTIMPL; } 276 277 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Commit(DWORD) 278 { return E_NOTIMPL; } 279 280 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Revert(void) 281 { return E_NOTIMPL; } 282 283 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) 284 { return E_NOTIMPL; } 285 286 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) 287 { return E_NOTIMPL; } 288 289 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Clone(IStream **) 290 { return E_NOTIMPL; } 291 292 293 //######################################### 294 295 296 CThumbviewer::CThumbviewer(long RefCnt) : 297 ref_count_(RefCnt) 298 { 299 InterlockedIncrement(&g_DllRefCnt); 300 301 thumbnail_size_.cx = 0; 302 thumbnail_size_.cy = 0; 303 304 Gdiplus::GdiplusStartupInput gdiplusStartupInput; 305 Gdiplus::GdiplusStartup(&gdiplus_token_, &gdiplusStartupInput, NULL); 306 307 ZipFile::ZipContentBuffer_t img_data; 308 internal::LoadSignetImageFromResource(img_data); 309 IStream* stream = new StreamOnZipBuffer(img_data); 310 signet_ = new Gdiplus::Bitmap(stream, TRUE); 311 stream->Release(); 312 } 313 314 CThumbviewer::~CThumbviewer() 315 { 316 delete signet_; 317 Gdiplus::GdiplusShutdown(gdiplus_token_); 318 InterlockedDecrement(&g_DllRefCnt); 319 } 320 321 // IUnknown methods 322 323 HRESULT STDMETHODCALLTYPE CThumbviewer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) 324 { 325 *ppvObject = 0; 326 IUnknown* pUnk = 0; 327 328 if ((IID_IUnknown == riid) || (IID_IPersistFile == riid)) 329 { 330 pUnk = static_cast<IPersistFile*>(this); 331 pUnk->AddRef(); 332 *ppvObject = pUnk; 333 return S_OK; 334 } 335 else if (IID_IExtractImage == riid) 336 { 337 pUnk = static_cast<IExtractImage*>(this); 338 pUnk->AddRef(); 339 *ppvObject = pUnk; 340 return S_OK; 341 } 342 return E_NOINTERFACE; 343 } 344 345 ULONG STDMETHODCALLTYPE CThumbviewer::AddRef(void) 346 { 347 return InterlockedIncrement(&ref_count_); 348 } 349 350 ULONG STDMETHODCALLTYPE CThumbviewer::Release( void) 351 { 352 long refcnt = InterlockedDecrement(&ref_count_); 353 354 if (0 == ref_count_) 355 delete this; 356 357 return refcnt; 358 } 359 360 // IExtractImage2 methods 361 362 const std::string THUMBNAIL_CONTENT = "Thumbnails/thumbnail.png"; 363 364 HRESULT STDMETHODCALLTYPE CThumbviewer::Extract(HBITMAP *phBmpImage) 365 { 366 HRESULT hr = E_FAIL; 367 368 try 369 { 370 std::wstring fname = getShortPathName( filename_ ); 371 std::auto_ptr<ZipFile> zipfile( new ZipFile( WStringToString( fname ) ) ); 372 373 if (zipfile->HasContent(THUMBNAIL_CONTENT)) 374 { 375 ZipFile::ZipContentBuffer_t thumbnail; 376 zipfile->GetUncompressedContent(THUMBNAIL_CONTENT, thumbnail); 377 IStream* stream = new StreamOnZipBuffer(thumbnail); 378 379 Gdiplus::Bitmap thumbnail_png(stream, TRUE); 380 381 if ((thumbnail_png.GetHeight() == 0) || (thumbnail_png.GetWidth() == 0)) 382 { 383 stream->Release(); 384 return E_FAIL; 385 } 386 387 HWND hwnd = GetDesktopWindow(); 388 HDC hdc = GetDC(hwnd); 389 HDC memDC = CreateCompatibleDC(hdc); 390 391 if (memDC) 392 { 393 UINT offset = 3; // reserve a little border space 394 395 Gdiplus::Rect canvas(0, 0, thumbnail_size_.cx, thumbnail_size_.cy); 396 Gdiplus::Rect canvas_thumbnail(offset, offset, thumbnail_size_.cx - 2 * offset, thumbnail_size_.cy - 2 * offset); 397 398 Gdiplus::Rect scaledRect = CalcScaledAspectRatio( 399 Gdiplus::Rect(0, 0, thumbnail_png.GetWidth(), thumbnail_png.GetHeight()), canvas_thumbnail); 400 401 struct { 402 BITMAPINFOHEADER bi; 403 DWORD ct[256]; 404 } dib; 405 406 ZeroMemory(&dib, sizeof(dib)); 407 408 dib.bi.biSize = sizeof(BITMAPINFOHEADER); 409 dib.bi.biWidth = thumbnail_size_.cx; 410 dib.bi.biHeight = thumbnail_size_.cy; 411 dib.bi.biPlanes = 1; 412 dib.bi.biBitCount = static_cast<WORD>(color_depth_); 413 dib.bi.biCompression = BI_RGB; 414 415 LPVOID lpBits; 416 HBITMAP hMemBmp = CreateDIBSection(memDC, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &lpBits, NULL, 0); 417 HGDIOBJ hOldObj = SelectObject(memDC, hMemBmp); 418 419 Gdiplus::Graphics graphics(memDC); 420 Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0, 0), 1); 421 422 Gdiplus::SolidBrush whiteBrush(Gdiplus::Color(255, 255, 255, 255)); 423 graphics.FillRectangle(&whiteBrush, canvas); 424 425 scaledRect.X = (canvas.Width - scaledRect.Width) / 2; 426 scaledRect.Y = (canvas.Height - scaledRect.Height) / 2; 427 428 Gdiplus::Rect border_rect(scaledRect.X, scaledRect.Y, scaledRect.Width, scaledRect.Height); 429 graphics.DrawRectangle(&blackPen, border_rect); 430 431 scaledRect.X += 1; 432 scaledRect.Y += 1; 433 scaledRect.Width -= 1; 434 scaledRect.Height -= 1; 435 436 graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic); 437 Gdiplus::Status stat = graphics.DrawImage( 438 &thumbnail_png, scaledRect, 0 , 0, 439 thumbnail_png.GetWidth(), thumbnail_png.GetHeight(), Gdiplus::UnitPixel); 440 441 /* Add a signet sign to the thumbnail of signed documents */ 442 if (internal::IsSignedDocument(zipfile.get())) 443 { 444 double SCALING_FACTOR = 0.6; 445 Gdiplus::Rect signet_scaled( 446 0, 0, static_cast<INT>(signet_->GetWidth() * SCALING_FACTOR), static_cast<INT>(signet_->GetHeight() * SCALING_FACTOR)); 447 Gdiplus::Point pos_signet = internal::CalcSignetPosition(canvas_thumbnail, border_rect, signet_scaled); 448 Gdiplus::Rect dest(pos_signet.X, pos_signet.Y, signet_scaled.GetRight(), signet_scaled.GetBottom()); 449 450 stat = graphics.DrawImage( 451 signet_, dest, 452 0, 0, signet_->GetWidth(), signet_->GetHeight(), 453 Gdiplus::UnitPixel); 454 } 455 456 if (stat == Gdiplus::Ok) 457 { 458 *phBmpImage = hMemBmp; 459 hr = NOERROR; 460 } 461 462 SelectObject(memDC, hOldObj); 463 DeleteDC(memDC); 464 } 465 466 ReleaseDC(hwnd, hdc); 467 stream->Release(); 468 } 469 } 470 catch(std::exception&) 471 { 472 OutputDebugStringFormat( "CThumbviewer Extract ERROR!\n" ); 473 hr = E_FAIL; 474 } 475 return hr; 476 } 477 478 HRESULT STDMETHODCALLTYPE CThumbviewer::GetLocation( 479 LPWSTR pszPathBuffer, DWORD cchMax, DWORD *pdwPriority, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags) 480 { 481 if ((prgSize == NULL) || (pdwFlags == NULL) || ((*pdwFlags & IEIFLAG_ASYNC) && (pdwPriority == NULL))) 482 return E_INVALIDARG; 483 484 thumbnail_size_ = *prgSize; 485 color_depth_ = dwRecClrDepth; 486 487 *pdwFlags = IEIFLAG_CACHE; // we don't cache the image 488 489 wcsncpy(pszPathBuffer, filename_.c_str(), cchMax); 490 491 return NOERROR; 492 } 493 494 // IPersist methods 495 496 HRESULT STDMETHODCALLTYPE CThumbviewer::GetClassID(CLSID* pClassID) 497 { 498 pClassID = const_cast<CLSID*>(&CLSID_THUMBVIEWER_HANDLER); 499 return S_OK; 500 } 501 502 // IPersistFile methods 503 504 HRESULT STDMETHODCALLTYPE CThumbviewer::Load(LPCOLESTR pszFileName, DWORD) 505 { 506 filename_ = pszFileName; 507 return S_OK; 508 } 509 510 HRESULT STDMETHODCALLTYPE CThumbviewer::IsDirty() 511 { return E_NOTIMPL; } 512 513 HRESULT STDMETHODCALLTYPE CThumbviewer::Save(LPCOLESTR, BOOL) 514 { return E_NOTIMPL; } 515 516 HRESULT STDMETHODCALLTYPE CThumbviewer::SaveCompleted(LPCOLESTR) 517 { return E_NOTIMPL; } 518 519 HRESULT STDMETHODCALLTYPE CThumbviewer::GetCurFile(LPOLESTR __RPC_FAR*) 520 { return E_NOTIMPL; } 521 522 523 Gdiplus::Rect CThumbviewer::CalcScaledAspectRatio(Gdiplus::Rect src, Gdiplus::Rect dest) 524 { 525 Gdiplus::Rect result; 526 if (src.Width >= src.Height) 527 result = Gdiplus::Rect(0, 0, dest.Width, src.Height * dest.Width / src.Width); 528 else 529 result = Gdiplus::Rect(0, 0, src.Width * dest.Height / src.Height, dest.Height); 530 531 return result; 532 } 533 534