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