Bump version to 5.0-14
[LibreOffice.git] / shell / source / win32 / shlxthandler / thumbviewer / thumbviewer.cxx
blob3062f8402966c0cdaeeba61aa35d54d32e25f362
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 #ifdef _WIN32_WINNT_WINBLUE
44 #include <VersionHelpers.h>
45 #endif
46 #if defined _MSC_VER
47 #pragma warning(pop)
48 #endif
49 #include <memory>
51 extern HINSTANCE g_hModule;
53 namespace internal
55 /* The signet.png used for thumbnails of signed documents
56 is contained as resource in this module, the resource
57 id is 2000 */
58 void LoadSignetImageFromResource(ZipFile::ZipContentBuffer_t& buffer)
60 HRSRC hrc = FindResource(g_hModule, TEXT("#2000"), RT_RCDATA);
61 DWORD size = SizeofResource(g_hModule, hrc);
62 HGLOBAL hglob = LoadResource(g_hModule, hrc);
63 char* data = reinterpret_cast<char*>(LockResource(hglob));
64 buffer = ZipFile::ZipContentBuffer_t(data, data + size);
67 bool IsSignedDocument(const ZipFile* zipfile)
69 return zipfile->HasContent("META-INF/documentsignatures.xml");
72 bool IsWindowsXP()
74 // the Win32 SDK 8.1 deprecates GetVersionEx()
75 #ifdef _WIN32_WINNT_WINBLUE
76 return IsWindowsXPOrGreater() ? true : false;
77 #else
78 OSVERSIONINFO osvi;
79 ZeroMemory(&osvi, sizeof(osvi));
80 osvi.dwOSVersionInfoSize = sizeof(osvi);
81 GetVersionEx(&osvi);
83 return ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
84 ((osvi.dwMajorVersion >= 5) && (osvi.dwMinorVersion >= 1)));
85 #endif
88 /* Calculate where to position the signet image.
89 On Windows ME we need to shift the signet a
90 little bit to the left because Windows ME
91 puts an overlay icon to the lower right
92 corner of a thumbnail image so that our signet
93 we be hidden. */
94 Gdiplus::Point CalcSignetPosition(
95 const Gdiplus::Rect& canvas, const Gdiplus::Rect& thumbnail_border, const Gdiplus::Rect& signet)
97 int x = 0;
98 int y = 0;
99 int hoffset = canvas.GetRight() - thumbnail_border.GetRight();
100 int voffset = canvas.GetBottom() - thumbnail_border.GetBottom();
102 if (hoffset > voffset)
104 x = thumbnail_border.GetRight() - signet.GetRight() + min(signet.GetRight() / 2, hoffset);
105 y = thumbnail_border.GetBottom() - signet.GetBottom();
107 else
109 x = thumbnail_border.GetRight() - signet.GetRight();
110 y = thumbnail_border.GetBottom() - signet.GetBottom() + min(signet.GetBottom() / 2, voffset);
113 if (!IsWindowsXP())
114 x -= 15;
116 return Gdiplus::Point(x,y);
120 class StreamOnZipBuffer : public IStream
122 public:
123 StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer);
125 // IUnknown
126 virtual ULONG STDMETHODCALLTYPE AddRef();
127 virtual ULONG STDMETHODCALLTYPE Release();
128 virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject);
130 // IStream
131 virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead);
132 virtual HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten);
133 virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
134 virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize);
135 virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten);
136 virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags);
137 virtual HRESULT STDMETHODCALLTYPE Revert();
138 virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
139 virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType);
140 virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag);
141 virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm);
143 private:
144 LONG ref_count_;
145 const ZipFile::ZipContentBuffer_t& ref_zip_buffer_;
146 size_t pos_;
149 StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer) :
150 ref_count_(1),
151 ref_zip_buffer_(zip_buffer),
152 pos_(0)
156 // IUnknown methods
158 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::AddRef()
160 return InterlockedIncrement(&ref_count_);
163 ULONG STDMETHODCALLTYPE StreamOnZipBuffer::Release()
165 long refcnt = InterlockedDecrement(&ref_count_);
167 if (0 == ref_count_)
168 delete this;
170 return refcnt;
173 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
175 *ppvObject = 0;
176 IUnknown* pUnk = 0;
178 if ((IID_IUnknown == riid) || (IID_IStream == riid))
180 pUnk = static_cast<IStream*>(this);
181 pUnk->AddRef();
182 *ppvObject = pUnk;
183 return S_OK;
185 return E_NOINTERFACE;
188 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Read(void *pv, ULONG cb, ULONG *pcbRead)
190 if (pv == NULL)
191 return STG_E_INVALIDPOINTER;
193 size_t size = ref_zip_buffer_.size();
195 if (pos_ > size)
196 return S_FALSE;
198 char* p = reinterpret_cast<char*>(pv);
199 ULONG read = 0;
201 for ( ;(pos_ < size) && (cb > 0); pos_++, cb--, read++)
202 *p++ = ref_zip_buffer_[pos_];
204 if (pcbRead)
205 *pcbRead = read;
207 return S_OK;
210 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *)
212 __int64 size = (__int64) ref_zip_buffer_.size();
213 __int64 p = 0;
215 switch (dwOrigin)
217 case STREAM_SEEK_SET:
218 break;
219 case STREAM_SEEK_CUR:
220 p = (__int64) pos_;
221 break;
222 case STREAM_SEEK_END:
223 p = size - 1;
224 break;
227 HRESULT hr = STG_E_INVALIDFUNCTION;
229 p += dlibMove.QuadPart;
231 if ( ( p >= 0 ) && (p < size) )
233 pos_ = (size_t) p;
234 hr = S_OK;
236 return hr;
239 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
241 if (pstatstg == NULL)
242 return STG_E_INVALIDPOINTER;
244 ZeroMemory(pstatstg, sizeof(STATSTG));
246 if (grfStatFlag == STATFLAG_DEFAULT)
248 size_t sz = 4 * sizeof(wchar_t);
249 wchar_t* name = reinterpret_cast<wchar_t*>(CoTaskMemAlloc(sz));
250 ZeroMemory(name, sz);
251 memcpy(name, L"png", 3 * sizeof(wchar_t));
252 pstatstg->pwcsName = name;
255 pstatstg->type = STGTY_LOCKBYTES;
257 ULARGE_INTEGER uli;
258 uli.LowPart = static_cast<DWORD>(ref_zip_buffer_.size());
259 uli.HighPart = 0;
261 pstatstg->cbSize = uli;
263 return S_OK;
266 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Write(void const *, ULONG, ULONG *)
267 { return E_NOTIMPL; }
269 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::SetSize(ULARGE_INTEGER)
270 { return E_NOTIMPL; }
272 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::CopyTo(IStream *, ULARGE_INTEGER, ULARGE_INTEGER *, ULARGE_INTEGER *)
273 { return E_NOTIMPL; }
275 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Commit(DWORD)
276 { return E_NOTIMPL; }
278 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Revert()
279 { return E_NOTIMPL; }
281 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
282 { return E_NOTIMPL; }
284 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
285 { return E_NOTIMPL; }
287 HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Clone(IStream **)
288 { return E_NOTIMPL; }
293 CThumbviewer::CThumbviewer(long RefCnt) :
294 ref_count_(RefCnt)
296 InterlockedIncrement(&g_DllRefCnt);
298 thumbnail_size_.cx = 0;
299 thumbnail_size_.cy = 0;
301 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
302 Gdiplus::GdiplusStartup(&gdiplus_token_, &gdiplusStartupInput, NULL);
304 ZipFile::ZipContentBuffer_t img_data;
305 internal::LoadSignetImageFromResource(img_data);
306 IStream* stream = new StreamOnZipBuffer(img_data);
307 signet_ = new Gdiplus::Bitmap(stream, TRUE);
308 stream->Release();
311 CThumbviewer::~CThumbviewer()
313 delete signet_;
314 Gdiplus::GdiplusShutdown(gdiplus_token_);
315 InterlockedDecrement(&g_DllRefCnt);
318 // IUnknown methods
320 HRESULT STDMETHODCALLTYPE CThumbviewer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
322 *ppvObject = 0;
323 IUnknown* pUnk = 0;
325 if ((IID_IUnknown == riid) || (IID_IPersistFile == riid))
327 pUnk = static_cast<IPersistFile*>(this);
328 pUnk->AddRef();
329 *ppvObject = pUnk;
330 return S_OK;
332 else if (IID_IExtractImage == riid)
334 pUnk = static_cast<IExtractImage*>(this);
335 pUnk->AddRef();
336 *ppvObject = pUnk;
337 return S_OK;
339 return E_NOINTERFACE;
342 ULONG STDMETHODCALLTYPE CThumbviewer::AddRef()
344 return InterlockedIncrement(&ref_count_);
347 ULONG STDMETHODCALLTYPE CThumbviewer::Release()
349 long refcnt = InterlockedDecrement(&ref_count_);
351 if (0 == ref_count_)
352 delete this;
354 return refcnt;
357 // IExtractImage2 methods
359 const std::string THUMBNAIL_CONTENT = "Thumbnails/thumbnail.png";
361 HRESULT STDMETHODCALLTYPE CThumbviewer::Extract(HBITMAP *phBmpImage)
363 HRESULT hr = E_FAIL;
367 std::wstring fname = getShortPathName( filename_ );
368 std::unique_ptr<ZipFile> zipfile( new ZipFile( WStringToString( fname ) ) );
370 if (zipfile->HasContent(THUMBNAIL_CONTENT))
372 ZipFile::ZipContentBuffer_t thumbnail;
373 zipfile->GetUncompressedContent(THUMBNAIL_CONTENT, thumbnail);
374 IStream* stream = new StreamOnZipBuffer(thumbnail);
376 Gdiplus::Bitmap thumbnail_png(stream, TRUE);
378 if ((thumbnail_png.GetHeight() == 0) || (thumbnail_png.GetWidth() == 0))
380 stream->Release();
381 return E_FAIL;
384 HWND hwnd = GetDesktopWindow();
385 HDC hdc = GetDC(hwnd);
386 HDC memDC = CreateCompatibleDC(hdc);
388 if (memDC)
390 UINT offset = 3; // reserve a little border space
392 Gdiplus::Rect canvas(0, 0, thumbnail_size_.cx, thumbnail_size_.cy);
393 Gdiplus::Rect canvas_thumbnail(offset, offset, thumbnail_size_.cx - 2 * offset, thumbnail_size_.cy - 2 * offset);
395 Gdiplus::Rect scaledRect = CalcScaledAspectRatio(
396 Gdiplus::Rect(0, 0, thumbnail_png.GetWidth(), thumbnail_png.GetHeight()), canvas_thumbnail);
398 struct {
399 BITMAPINFOHEADER bi;
400 DWORD ct[256];
401 } dib;
403 ZeroMemory(&dib, sizeof(dib));
405 dib.bi.biSize = sizeof(BITMAPINFOHEADER);
406 dib.bi.biWidth = thumbnail_size_.cx;
407 dib.bi.biHeight = thumbnail_size_.cy;
408 dib.bi.biPlanes = 1;
409 dib.bi.biBitCount = static_cast<WORD>(color_depth_);
410 dib.bi.biCompression = BI_RGB;
412 LPVOID lpBits;
413 HBITMAP hMemBmp = CreateDIBSection(memDC, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &lpBits, NULL, 0);
414 HGDIOBJ hOldObj = SelectObject(memDC, hMemBmp);
416 Gdiplus::Graphics graphics(memDC);
417 Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0, 0), 1);
419 Gdiplus::SolidBrush whiteBrush(Gdiplus::Color(255, 255, 255, 255));
420 graphics.FillRectangle(&whiteBrush, canvas);
422 scaledRect.X = (canvas.Width - scaledRect.Width) / 2;
423 scaledRect.Y = (canvas.Height - scaledRect.Height) / 2;
425 Gdiplus::Rect border_rect(scaledRect.X, scaledRect.Y, scaledRect.Width, scaledRect.Height);
426 graphics.DrawRectangle(&blackPen, border_rect);
428 scaledRect.X += 1;
429 scaledRect.Y += 1;
430 scaledRect.Width -= 1;
431 scaledRect.Height -= 1;
433 graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic);
434 Gdiplus::Status stat = graphics.DrawImage(
435 &thumbnail_png, scaledRect, 0 , 0,
436 thumbnail_png.GetWidth(), thumbnail_png.GetHeight(), Gdiplus::UnitPixel);
438 /* Add a signet sign to the thumbnail of signed documents */
439 if (internal::IsSignedDocument(zipfile.get()))
441 double SCALING_FACTOR = 0.6;
442 Gdiplus::Rect signet_scaled(
443 0, 0, static_cast<INT>(signet_->GetWidth() * SCALING_FACTOR), static_cast<INT>(signet_->GetHeight() * SCALING_FACTOR));
444 Gdiplus::Point pos_signet = internal::CalcSignetPosition(canvas_thumbnail, border_rect, signet_scaled);
445 Gdiplus::Rect dest(pos_signet.X, pos_signet.Y, signet_scaled.GetRight(), signet_scaled.GetBottom());
447 stat = graphics.DrawImage(
448 signet_, dest,
449 0, 0, signet_->GetWidth(), signet_->GetHeight(),
450 Gdiplus::UnitPixel);
453 if (stat == Gdiplus::Ok)
455 *phBmpImage = hMemBmp;
456 hr = NOERROR;
459 SelectObject(memDC, hOldObj);
460 DeleteDC(memDC);
463 ReleaseDC(hwnd, hdc);
464 stream->Release();
467 catch(std::exception&)
469 OutputDebugStringFormat( "CThumbviewer Extract ERROR!\n" );
470 hr = E_FAIL;
472 return hr;
475 HRESULT STDMETHODCALLTYPE CThumbviewer::GetLocation(
476 LPWSTR pszPathBuffer, DWORD cchMax, DWORD *pdwPriority, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags)
478 if ((prgSize == NULL) || (pdwFlags == NULL) || ((*pdwFlags & IEIFLAG_ASYNC) && (pdwPriority == NULL)))
479 return E_INVALIDARG;
481 thumbnail_size_ = *prgSize;
482 color_depth_ = dwRecClrDepth;
484 *pdwFlags = IEIFLAG_CACHE; // we don't cache the image
486 wcsncpy(pszPathBuffer, filename_.c_str(), cchMax);
488 return NOERROR;
491 // IPersist methods
493 HRESULT STDMETHODCALLTYPE CThumbviewer::GetClassID(CLSID* pClassID)
495 pClassID = const_cast<CLSID*>(&CLSID_THUMBVIEWER_HANDLER);
496 return S_OK;
499 // IPersistFile methods
501 HRESULT STDMETHODCALLTYPE CThumbviewer::Load(LPCOLESTR pszFileName, DWORD)
503 filename_ = pszFileName;
504 return S_OK;
507 HRESULT STDMETHODCALLTYPE CThumbviewer::IsDirty()
508 { return E_NOTIMPL; }
510 HRESULT STDMETHODCALLTYPE CThumbviewer::Save(LPCOLESTR, BOOL)
511 { return E_NOTIMPL; }
513 HRESULT STDMETHODCALLTYPE CThumbviewer::SaveCompleted(LPCOLESTR)
514 { return E_NOTIMPL; }
516 HRESULT STDMETHODCALLTYPE CThumbviewer::GetCurFile(LPOLESTR __RPC_FAR*)
517 { return E_NOTIMPL; }
520 Gdiplus::Rect CThumbviewer::CalcScaledAspectRatio(const Gdiplus::Rect& src, const Gdiplus::Rect& dest)
522 Gdiplus::Rect result;
523 if (src.Width >= src.Height)
524 result = Gdiplus::Rect(0, 0, dest.Width, src.Height * dest.Width / src.Width);
525 else
526 result = Gdiplus::Rect(0, 0, src.Width * dest.Height / src.Height, dest.Height);
528 return result;
531 #endif // DONT_HAVE_GDIPLUS
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */