bump product version to 4.1.6.2
[LibreOffice.git] / shell / source / win32 / shlxthandler / thumbviewer / thumbviewer.cxx
blob45918f5f5d81c8cc8e31f0a98606baed928c1290
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 .
20 #ifndef DONT_HAVE_GDIPLUS
23 #include "internal/global.hxx"
25 #include "internal/thumbviewer.hxx"
26 #include "internal/shlxthdl.hxx"
27 #include "internal/registry.hxx"
28 #include "internal/fileextensions.hxx"
29 #include "internal/config.hxx"
30 #include "internal/zipfile.hxx"
31 #include "internal/utilities.hxx"
33 #include "internal/resource.h"
35 #include <stdio.h>
36 #include <utility>
37 #include <stdlib.h>
39 #if defined _MSC_VER
40 #pragma warning(push, 1)
41 #endif
42 #include <shellapi.h>
43 #if defined _MSC_VER
44 #pragma warning(pop)
45 #endif
46 #include <memory>
48 extern HINSTANCE g_hModule;
50 namespace internal
52 /* The signet.png used for thumbnails of signed documents
53 is contained as resource in this module, the resource
54 id is 2000 */
55 void LoadSignetImageFromResource(ZipFile::ZipContentBuffer_t& buffer)
57 HRSRC hrc = FindResource(g_hModule, TEXT("#2000"), RT_RCDATA);
58 DWORD size = SizeofResource(g_hModule, hrc);
59 HGLOBAL hglob = LoadResource(g_hModule, hrc);
60 char* data = reinterpret_cast<char*>(LockResource(hglob));
61 buffer = ZipFile::ZipContentBuffer_t(data, data + size);
64 bool IsSignedDocument(const ZipFile* zipfile)
66 return zipfile->HasContent("META-INF/documentsignatures.xml");
69 bool IsWindowsXP()
71 OSVERSIONINFO osvi;
72 ZeroMemory(&osvi, sizeof(osvi));
73 osvi.dwOSVersionInfoSize = sizeof(osvi);
74 GetVersionEx(&osvi);
76 return ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
77 ((osvi.dwMajorVersion >= 5) && (osvi.dwMinorVersion >= 1)));
80 /* Calculate where to position the signet image.
81 On Windows ME we need to shift the signet a
82 little bit to the left because Windows ME
83 puts an overlay icon to the lower right
84 corner of a thumbnail image so that our signet
85 we be hidden. */
86 Gdiplus::Point CalcSignetPosition(
87 const Gdiplus::Rect& canvas, const Gdiplus::Rect& thumbnail_border, const Gdiplus::Rect& signet)
89 int x = 0;
90 int y = 0;
91 int hoffset = canvas.GetRight() - thumbnail_border.GetRight();
92 int voffset = canvas.GetBottom() - thumbnail_border.GetBottom();
94 if (hoffset > voffset)
96 x = thumbnail_border.GetRight() - signet.GetRight() + min(signet.GetRight() / 2, hoffset);
97 y = thumbnail_border.GetBottom() - signet.GetBottom();
99 else
101 x = thumbnail_border.GetRight() - signet.GetRight();
102 y = thumbnail_border.GetBottom() - signet.GetBottom() + min(signet.GetBottom() / 2, voffset);
105 if (!IsWindowsXP())
106 x -= 15;
108 return Gdiplus::Point(x,y);
112 class StreamOnZipBuffer : public IStream
114 public:
115 StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer);
117 // IUnknown
118 virtual ULONG STDMETHODCALLTYPE AddRef();
119 virtual ULONG STDMETHODCALLTYPE Release( void);
120 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject);
122 // IStream
123 virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead);
124 virtual HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten);
125 virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
126 virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize);
127 virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten);
128 virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags);
129 virtual HRESULT STDMETHODCALLTYPE Revert(void);
130 virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
131 virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
132 virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag);
133 virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm);
135 private:
136 LONG ref_count_;
137 const ZipFile::ZipContentBuffer_t& ref_zip_buffer_;
138 size_t pos_;
141 StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer) :
142 ref_count_(1),
143 ref_zip_buffer_(zip_buffer),
144 pos_(0)
148 // IUnknown methods
150 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::AddRef(void)
152 return InterlockedIncrement(&ref_count_);
155 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::Release( void)
157 long refcnt = InterlockedDecrement(&ref_count_);
159 if (0 == ref_count_)
160 delete this;
162 return refcnt;
165 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
167 *ppvObject = 0;
168 IUnknown* pUnk = 0;
170 if ((IID_IUnknown == riid) || (IID_IStream == riid))
172 pUnk = static_cast<IStream*>(this);
173 pUnk->AddRef();
174 *ppvObject = pUnk;
175 return S_OK;
177 return E_NOINTERFACE;
180 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Read(void *pv, ULONG cb, ULONG *pcbRead)
182 if (pv == NULL)
183 return STG_E_INVALIDPOINTER;
185 size_t size = ref_zip_buffer_.size();
187 if (pos_ > size)
188 return S_FALSE;
190 char* p = reinterpret_cast<char*>(pv);
191 ULONG read = 0;
193 for ( ;(pos_ < size) && (cb > 0); pos_++, cb--, read++)
194 *p++ = ref_zip_buffer_[pos_];
196 if (pcbRead)
197 *pcbRead = read;
199 return S_OK;
202 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *)
204 __int64 size = (__int64) ref_zip_buffer_.size();
205 __int64 p = 0;
207 switch (dwOrigin)
209 case STREAM_SEEK_SET:
210 break;
211 case STREAM_SEEK_CUR:
212 p = (__int64) pos_;
213 break;
214 case STREAM_SEEK_END:
215 p = size - 1;
216 break;
219 HRESULT hr = STG_E_INVALIDFUNCTION;
221 p += dlibMove.QuadPart;
223 if ( ( p >= 0 ) && (p < size) )
225 pos_ = (size_t) p;
226 hr = S_OK;
228 return hr;
231 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
233 if (pstatstg == NULL)
234 return STG_E_INVALIDPOINTER;
236 ZeroMemory(pstatstg, sizeof(STATSTG));
238 if (grfStatFlag == STATFLAG_DEFAULT)
240 size_t sz = 4 * sizeof(wchar_t);
241 wchar_t* name = reinterpret_cast<wchar_t*>(CoTaskMemAlloc(sz));
242 ZeroMemory(name, sz);
243 memcpy(name, L"png", 3 * sizeof(wchar_t));
244 pstatstg->pwcsName = name;
247 pstatstg->type = STGTY_LOCKBYTES;
249 ULARGE_INTEGER uli;
250 uli.LowPart = static_cast<DWORD>(ref_zip_buffer_.size());
251 uli.HighPart = 0;
253 pstatstg->cbSize = uli;
255 return S_OK;
258 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Write(void const *, ULONG, ULONG *)
259 { return E_NOTIMPL; }
261 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::SetSize(ULARGE_INTEGER)
262 { return E_NOTIMPL; }
264 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::CopyTo(IStream *, ULARGE_INTEGER, ULARGE_INTEGER *, ULARGE_INTEGER *)
265 { return E_NOTIMPL; }
267 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Commit(DWORD)
268 { return E_NOTIMPL; }
270 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Revert(void)
271 { return E_NOTIMPL; }
273 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
274 { return E_NOTIMPL; }
276 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
277 { return E_NOTIMPL; }
279 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Clone(IStream **)
280 { return E_NOTIMPL; }
285 CThumbviewer::CThumbviewer(long RefCnt) :
286 ref_count_(RefCnt)
288 InterlockedIncrement(&g_DllRefCnt);
290 thumbnail_size_.cx = 0;
291 thumbnail_size_.cy = 0;
293 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
294 Gdiplus::GdiplusStartup(&gdiplus_token_, &gdiplusStartupInput, NULL);
296 ZipFile::ZipContentBuffer_t img_data;
297 internal::LoadSignetImageFromResource(img_data);
298 IStream* stream = new StreamOnZipBuffer(img_data);
299 signet_ = new Gdiplus::Bitmap(stream, TRUE);
300 stream->Release();
303 CThumbviewer::~CThumbviewer()
305 delete signet_;
306 Gdiplus::GdiplusShutdown(gdiplus_token_);
307 InterlockedDecrement(&g_DllRefCnt);
310 // IUnknown methods
312 HRESULT STDMETHODCALLTYPE CThumbviewer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
314 *ppvObject = 0;
315 IUnknown* pUnk = 0;
317 if ((IID_IUnknown == riid) || (IID_IPersistFile == riid))
319 pUnk = static_cast<IPersistFile*>(this);
320 pUnk->AddRef();
321 *ppvObject = pUnk;
322 return S_OK;
324 else if (IID_IExtractImage == riid)
326 pUnk = static_cast<IExtractImage*>(this);
327 pUnk->AddRef();
328 *ppvObject = pUnk;
329 return S_OK;
331 return E_NOINTERFACE;
334 ULONG STDMETHODCALLTYPE CThumbviewer::AddRef(void)
336 return InterlockedIncrement(&ref_count_);
339 ULONG STDMETHODCALLTYPE CThumbviewer::Release( void)
341 long refcnt = InterlockedDecrement(&ref_count_);
343 if (0 == ref_count_)
344 delete this;
346 return refcnt;
349 // IExtractImage2 methods
351 const std::string THUMBNAIL_CONTENT = "Thumbnails/thumbnail.png";
353 HRESULT STDMETHODCALLTYPE CThumbviewer::Extract(HBITMAP *phBmpImage)
355 HRESULT hr = E_FAIL;
359 std::wstring fname = getShortPathName( filename_ );
360 std::auto_ptr<ZipFile> zipfile( new ZipFile( WStringToString( fname ) ) );
362 if (zipfile->HasContent(THUMBNAIL_CONTENT))
364 ZipFile::ZipContentBuffer_t thumbnail;
365 zipfile->GetUncompressedContent(THUMBNAIL_CONTENT, thumbnail);
366 IStream* stream = new StreamOnZipBuffer(thumbnail);
368 Gdiplus::Bitmap thumbnail_png(stream, TRUE);
370 if ((thumbnail_png.GetHeight() == 0) || (thumbnail_png.GetWidth() == 0))
372 stream->Release();
373 return E_FAIL;
376 HWND hwnd = GetDesktopWindow();
377 HDC hdc = GetDC(hwnd);
378 HDC memDC = CreateCompatibleDC(hdc);
380 if (memDC)
382 UINT offset = 3; // reserve a little border space
384 Gdiplus::Rect canvas(0, 0, thumbnail_size_.cx, thumbnail_size_.cy);
385 Gdiplus::Rect canvas_thumbnail(offset, offset, thumbnail_size_.cx - 2 * offset, thumbnail_size_.cy - 2 * offset);
387 Gdiplus::Rect scaledRect = CalcScaledAspectRatio(
388 Gdiplus::Rect(0, 0, thumbnail_png.GetWidth(), thumbnail_png.GetHeight()), canvas_thumbnail);
390 struct {
391 BITMAPINFOHEADER bi;
392 DWORD ct[256];
393 } dib;
395 ZeroMemory(&dib, sizeof(dib));
397 dib.bi.biSize = sizeof(BITMAPINFOHEADER);
398 dib.bi.biWidth = thumbnail_size_.cx;
399 dib.bi.biHeight = thumbnail_size_.cy;
400 dib.bi.biPlanes = 1;
401 dib.bi.biBitCount = static_cast<WORD>(color_depth_);
402 dib.bi.biCompression = BI_RGB;
404 LPVOID lpBits;
405 HBITMAP hMemBmp = CreateDIBSection(memDC, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &lpBits, NULL, 0);
406 HGDIOBJ hOldObj = SelectObject(memDC, hMemBmp);
408 Gdiplus::Graphics graphics(memDC);
409 Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0, 0), 1);
411 Gdiplus::SolidBrush whiteBrush(Gdiplus::Color(255, 255, 255, 255));
412 graphics.FillRectangle(&whiteBrush, canvas);
414 scaledRect.X = (canvas.Width - scaledRect.Width) / 2;
415 scaledRect.Y = (canvas.Height - scaledRect.Height) / 2;
417 Gdiplus::Rect border_rect(scaledRect.X, scaledRect.Y, scaledRect.Width, scaledRect.Height);
418 graphics.DrawRectangle(&blackPen, border_rect);
420 scaledRect.X += 1;
421 scaledRect.Y += 1;
422 scaledRect.Width -= 1;
423 scaledRect.Height -= 1;
425 graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
426 Gdiplus::Status stat = graphics.DrawImage(
427 &thumbnail_png, scaledRect, 0 , 0,
428 thumbnail_png.GetWidth(), thumbnail_png.GetHeight(), Gdiplus::UnitPixel);
430 /* Add a signet sign to the thumbnail of signed documents */
431 if (internal::IsSignedDocument(zipfile.get()))
433 double SCALING_FACTOR = 0.6;
434 Gdiplus::Rect signet_scaled(
435 0, 0, static_cast<INT>(signet_->GetWidth() * SCALING_FACTOR), static_cast<INT>(signet_->GetHeight() * SCALING_FACTOR));
436 Gdiplus::Point pos_signet = internal::CalcSignetPosition(canvas_thumbnail, border_rect, signet_scaled);
437 Gdiplus::Rect dest(pos_signet.X, pos_signet.Y, signet_scaled.GetRight(), signet_scaled.GetBottom());
439 stat = graphics.DrawImage(
440 signet_, dest,
441 0, 0, signet_->GetWidth(), signet_->GetHeight(),
442 Gdiplus::UnitPixel);
445 if (stat == Gdiplus::Ok)
447 *phBmpImage = hMemBmp;
448 hr = NOERROR;
451 SelectObject(memDC, hOldObj);
452 DeleteDC(memDC);
455 ReleaseDC(hwnd, hdc);
456 stream->Release();
459 catch(std::exception&)
461 OutputDebugStringFormat( "CThumbviewer Extract ERROR!\n" );
462 hr = E_FAIL;
464 return hr;
467 HRESULT STDMETHODCALLTYPE CThumbviewer::GetLocation(
468 LPWSTR pszPathBuffer, DWORD cchMax, DWORD *pdwPriority, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags)
470 if ((prgSize == NULL) || (pdwFlags == NULL) || ((*pdwFlags & IEIFLAG_ASYNC) && (pdwPriority == NULL)))
471 return E_INVALIDARG;
473 thumbnail_size_ = *prgSize;
474 color_depth_ = dwRecClrDepth;
476 *pdwFlags = IEIFLAG_CACHE; // we don't cache the image
478 wcsncpy(pszPathBuffer, filename_.c_str(), cchMax);
480 return NOERROR;
483 // IPersist methods
485 HRESULT STDMETHODCALLTYPE CThumbviewer::GetClassID(CLSID* pClassID)
487 pClassID = const_cast<CLSID*>(&CLSID_THUMBVIEWER_HANDLER);
488 return S_OK;
491 // IPersistFile methods
493 HRESULT STDMETHODCALLTYPE CThumbviewer::Load(LPCOLESTR pszFileName, DWORD)
495 filename_ = pszFileName;
496 return S_OK;
499 HRESULT STDMETHODCALLTYPE CThumbviewer::IsDirty()
500 { return E_NOTIMPL; }
502 HRESULT STDMETHODCALLTYPE CThumbviewer::Save(LPCOLESTR, BOOL)
503 { return E_NOTIMPL; }
505 HRESULT STDMETHODCALLTYPE CThumbviewer::SaveCompleted(LPCOLESTR)
506 { return E_NOTIMPL; }
508 HRESULT STDMETHODCALLTYPE CThumbviewer::GetCurFile(LPOLESTR __RPC_FAR*)
509 { return E_NOTIMPL; }
512 Gdiplus::Rect CThumbviewer::CalcScaledAspectRatio(Gdiplus::Rect src, Gdiplus::Rect dest)
514 Gdiplus::Rect result;
515 if (src.Width >= src.Height)
516 result = Gdiplus::Rect(0, 0, dest.Width, src.Height * dest.Width / src.Width);
517 else
518 result = Gdiplus::Rect(0, 0, src.Width * dest.Height / src.Height, dest.Height);
520 return result;
523 #endif // DONT_HAVE_GDIPLUS
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */