/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_shell.hxx" #include "internal/global.hxx" #ifndef INFOTIPS_HXX_INCLUDED #include "internal/thumbviewer.hxx" #endif #include "internal/shlxthdl.hxx" #include "internal/registry.hxx" #include "internal/fileextensions.hxx" #include "internal/config.hxx" #include "internal/zipfile.hxx" #include "internal/utilities.hxx" #include "internal/resource.h" #include #include #include #if defined _MSC_VER #pragma warning(push, 1) #endif #include #if defined _MSC_VER #pragma warning(pop) #endif #include extern HINSTANCE g_hModule; namespace internal { /* The signet.png used for thumbnails of signed documents is contained as resource in this module, the resource id is 2000 */ void LoadSignetImageFromResource(ZipFile::ZipContentBuffer_t& buffer) { HRSRC hrc = FindResource(g_hModule, TEXT("#2000"), RT_RCDATA); DWORD size = SizeofResource(g_hModule, hrc); HGLOBAL hglob = LoadResource(g_hModule, hrc); char* data = reinterpret_cast(LockResource(hglob)); buffer = ZipFile::ZipContentBuffer_t(data, data + size); } bool IsSignedDocument(const ZipFile* zipfile) { return zipfile->HasContent("META-INF/documentsignatures.xml"); } bool IsWindowsXP() { OSVERSIONINFO osvi; ZeroMemory(&osvi, sizeof(osvi)); osvi.dwOSVersionInfoSize = sizeof(osvi); GetVersionEx(&osvi); return ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && ((osvi.dwMajorVersion >= 5) && (osvi.dwMinorVersion >= 1))); } /* Calculate where to position the signet image. On Windows ME we need to shift the signet a little bit to the left because Windows ME puts an overlay icon to the lower right corner of a thumbnail image so that our signet we be hidden. */ Gdiplus::Point CalcSignetPosition( const Gdiplus::Rect& canvas, const Gdiplus::Rect& thumbnail_border, const Gdiplus::Rect& signet) { int x = 0; int y = 0; int hoffset = canvas.GetRight() - thumbnail_border.GetRight(); int voffset = canvas.GetBottom() - thumbnail_border.GetBottom(); if (hoffset > voffset) { x = thumbnail_border.GetRight() - signet.GetRight() + min(signet.GetRight() / 2, hoffset); y = thumbnail_border.GetBottom() - signet.GetBottom(); } else { x = thumbnail_border.GetRight() - signet.GetRight(); y = thumbnail_border.GetBottom() - signet.GetBottom() + min(signet.GetBottom() / 2, voffset); } if (!IsWindowsXP()) x -= 15; return Gdiplus::Point(x,y); } } class StreamOnZipBuffer : public IStream { public: StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer); // IUnknown virtual ULONG STDMETHODCALLTYPE AddRef(); virtual ULONG STDMETHODCALLTYPE Release( void); virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject); // IStream virtual HRESULT STDMETHODCALLTYPE Read(void *pv, ULONG cb, ULONG *pcbRead); virtual HRESULT STDMETHODCALLTYPE Write(void const *pv, ULONG cb, ULONG *pcbWritten); virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition); virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER libNewSize); virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten); virtual HRESULT STDMETHODCALLTYPE Commit(DWORD grfCommitFlags); virtual HRESULT STDMETHODCALLTYPE Revert(void); virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType); virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType); virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG *pstatstg, DWORD grfStatFlag); virtual HRESULT STDMETHODCALLTYPE Clone(IStream **ppstm); private: LONG ref_count_; const ZipFile::ZipContentBuffer_t& ref_zip_buffer_; size_t pos_; }; StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t& zip_buffer) : ref_count_(1), ref_zip_buffer_(zip_buffer), pos_(0) { } // IUnknown methods ULONG STDMETHODCALLTYPE StreamOnZipBuffer::AddRef(void) { return InterlockedIncrement(&ref_count_); } ULONG STDMETHODCALLTYPE StreamOnZipBuffer::Release( void) { long refcnt = InterlockedDecrement(&ref_count_); if (0 == ref_count_) delete this; return refcnt; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) { *ppvObject = 0; IUnknown* pUnk = 0; if ((IID_IUnknown == riid) || (IID_IStream == riid)) { pUnk = static_cast(this); pUnk->AddRef(); *ppvObject = pUnk; return S_OK; } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Read(void *pv, ULONG cb, ULONG *pcbRead) { if (pv == NULL) return STG_E_INVALIDPOINTER; size_t size = ref_zip_buffer_.size(); if (pos_ > size) return S_FALSE; char* p = reinterpret_cast(pv); ULONG read = 0; for ( ;(pos_ < size) && (cb > 0); pos_++, cb--, read++) *p++ = ref_zip_buffer_[pos_]; if (pcbRead) *pcbRead = read; return S_OK; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *) { __int64 size = (__int64) ref_zip_buffer_.size(); __int64 p = 0; switch (dwOrigin) { case STREAM_SEEK_SET: break; case STREAM_SEEK_CUR: p = (__int64) pos_; break; case STREAM_SEEK_END: p = size - 1; break; } HRESULT hr = STG_E_INVALIDFUNCTION; p += dlibMove.QuadPart; if ( ( p >= 0 ) && (p < size) ) { pos_ = (size_t) p; hr = S_OK; } return hr; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Stat(STATSTG *pstatstg, DWORD grfStatFlag) { if (pstatstg == NULL) return STG_E_INVALIDPOINTER; ZeroMemory(pstatstg, sizeof(STATSTG)); if (grfStatFlag == STATFLAG_DEFAULT) { size_t sz = 4 * sizeof(wchar_t); wchar_t* name = reinterpret_cast(CoTaskMemAlloc(sz)); ZeroMemory(name, sz); memcpy(name, L"png", 3 * sizeof(wchar_t)); pstatstg->pwcsName = name; } pstatstg->type = STGTY_LOCKBYTES; ULARGE_INTEGER uli; uli.LowPart = ref_zip_buffer_.size(); uli.HighPart = 0; pstatstg->cbSize = uli; return S_OK; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Write(void const *, ULONG, ULONG *) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::SetSize(ULARGE_INTEGER) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::CopyTo(IStream *, ULARGE_INTEGER, ULARGE_INTEGER *, ULARGE_INTEGER *) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Commit(DWORD) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Revert(void) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE StreamOnZipBuffer::Clone(IStream **) { return E_NOTIMPL; } //######################################### CThumbviewer::CThumbviewer(long RefCnt) : ref_count_(RefCnt) { InterlockedIncrement(&g_DllRefCnt); thumbnail_size_.cx = 0; thumbnail_size_.cy = 0; Gdiplus::GdiplusStartupInput gdiplusStartupInput; Gdiplus::GdiplusStartup(&gdiplus_token_, &gdiplusStartupInput, NULL); ZipFile::ZipContentBuffer_t img_data; internal::LoadSignetImageFromResource(img_data); IStream* stream = new StreamOnZipBuffer(img_data); signet_ = new Gdiplus::Bitmap(stream, TRUE); stream->Release(); } CThumbviewer::~CThumbviewer() { delete signet_; Gdiplus::GdiplusShutdown(gdiplus_token_); InterlockedDecrement(&g_DllRefCnt); } // IUnknown methods HRESULT STDMETHODCALLTYPE CThumbviewer::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject) { *ppvObject = 0; IUnknown* pUnk = 0; if ((IID_IUnknown == riid) || (IID_IPersistFile == riid)) { pUnk = static_cast(this); pUnk->AddRef(); *ppvObject = pUnk; return S_OK; } else if (IID_IExtractImage == riid) { pUnk = static_cast(this); pUnk->AddRef(); *ppvObject = pUnk; return S_OK; } return E_NOINTERFACE; } ULONG STDMETHODCALLTYPE CThumbviewer::AddRef(void) { return InterlockedIncrement(&ref_count_); } ULONG STDMETHODCALLTYPE CThumbviewer::Release( void) { long refcnt = InterlockedDecrement(&ref_count_); if (0 == ref_count_) delete this; return refcnt; } // IExtractImage2 methods const std::string THUMBNAIL_CONTENT = "Thumbnails/thumbnail.png"; HRESULT STDMETHODCALLTYPE CThumbviewer::Extract(HBITMAP *phBmpImage) { HRESULT hr = E_FAIL; try { std::auto_ptr zipfile(new ZipFile(WStringToString(filename_))); if (zipfile->HasContent(THUMBNAIL_CONTENT)) { ZipFile::ZipContentBuffer_t thumbnail; zipfile->GetUncompressedContent(THUMBNAIL_CONTENT, thumbnail); IStream* stream = new StreamOnZipBuffer(thumbnail); Gdiplus::Bitmap thumbnail_png(stream, TRUE); if ((thumbnail_png.GetHeight() == 0) || (thumbnail_png.GetWidth() == 0)) { stream->Release(); return E_FAIL; } HWND hwnd = GetDesktopWindow(); HDC hdc = GetDC(hwnd); HDC memDC = CreateCompatibleDC(hdc); if (memDC) { UINT offset = 3; // reserve a little border space Gdiplus::Rect canvas(0, 0, thumbnail_size_.cx, thumbnail_size_.cy); Gdiplus::Rect canvas_thumbnail(offset, offset, thumbnail_size_.cx - 2 * offset, thumbnail_size_.cy - 2 * offset); Gdiplus::Rect scaledRect = CalcScaledAspectRatio( Gdiplus::Rect(0, 0, thumbnail_png.GetWidth(), thumbnail_png.GetHeight()), canvas_thumbnail); struct { BITMAPINFOHEADER bi; DWORD ct[256]; } dib; ZeroMemory(&dib, sizeof(dib)); dib.bi.biSize = sizeof(BITMAPINFOHEADER); dib.bi.biWidth = thumbnail_size_.cx; dib.bi.biHeight = thumbnail_size_.cy; dib.bi.biPlanes = 1; dib.bi.biBitCount = static_cast(color_depth_); dib.bi.biCompression = BI_RGB; LPVOID lpBits; HBITMAP hMemBmp = CreateDIBSection(memDC, (LPBITMAPINFO)&dib, DIB_RGB_COLORS, &lpBits, NULL, 0); HGDIOBJ hOldObj = SelectObject(memDC, hMemBmp); Gdiplus::Graphics graphics(memDC); Gdiplus::Pen blackPen(Gdiplus::Color(255, 0, 0, 0), 1); Gdiplus::SolidBrush whiteBrush(Gdiplus::Color(255, 255, 255, 255)); graphics.FillRectangle(&whiteBrush, canvas); scaledRect.X = (canvas.Width - scaledRect.Width) / 2; scaledRect.Y = (canvas.Height - scaledRect.Height) / 2; Gdiplus::Rect border_rect(scaledRect.X, scaledRect.Y, scaledRect.Width, scaledRect.Height); graphics.DrawRectangle(&blackPen, border_rect); scaledRect.X += 1; scaledRect.Y += 1; scaledRect.Width -= 1; scaledRect.Height -= 1; graphics.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic); Gdiplus::Status stat = graphics.DrawImage( &thumbnail_png, scaledRect, 0 , 0, thumbnail_png.GetWidth(), thumbnail_png.GetHeight(), Gdiplus::UnitPixel); /* Add a signet sign to the thumbnail of signed documents */ if (internal::IsSignedDocument(zipfile.get())) { double SCALING_FACTOR = 0.6; Gdiplus::Rect signet_scaled( 0, 0, static_cast(signet_->GetWidth() * SCALING_FACTOR), static_cast(signet_->GetHeight() * SCALING_FACTOR)); Gdiplus::Point pos_signet = internal::CalcSignetPosition(canvas_thumbnail, border_rect, signet_scaled); Gdiplus::Rect dest(pos_signet.X, pos_signet.Y, signet_scaled.GetRight(), signet_scaled.GetBottom()); stat = graphics.DrawImage( signet_, dest, 0, 0, signet_->GetWidth(), signet_->GetHeight(), Gdiplus::UnitPixel); } if (stat == Gdiplus::Ok) { *phBmpImage = hMemBmp; hr = NOERROR; } SelectObject(memDC, hOldObj); DeleteDC(memDC); } ReleaseDC(hwnd, hdc); stream->Release(); } } catch(std::exception&) { OutputDebugStringFormat( "CThumbviewer Extract ERROR!\n" ); hr = E_FAIL; } return hr; } HRESULT STDMETHODCALLTYPE CThumbviewer::GetLocation( LPWSTR pszPathBuffer, DWORD cchMax, DWORD *pdwPriority, const SIZE *prgSize, DWORD dwRecClrDepth, DWORD *pdwFlags) { if ((prgSize == NULL) || (pdwFlags == NULL) || ((*pdwFlags & IEIFLAG_ASYNC) && (pdwPriority == NULL))) return E_INVALIDARG; thumbnail_size_ = *prgSize; color_depth_ = dwRecClrDepth; *pdwFlags = IEIFLAG_CACHE; // we don't cache the image wcsncpy(pszPathBuffer, filename_.c_str(), cchMax); return NOERROR; } // IPersist methods HRESULT STDMETHODCALLTYPE CThumbviewer::GetClassID(CLSID* pClassID) { pClassID = const_cast(&CLSID_THUMBVIEWER_HANDLER); return S_OK; } // IPersistFile methods HRESULT STDMETHODCALLTYPE CThumbviewer::Load(LPCOLESTR pszFileName, DWORD) { filename_ = pszFileName; return S_OK; } HRESULT STDMETHODCALLTYPE CThumbviewer::IsDirty() { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CThumbviewer::Save(LPCOLESTR, BOOL) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CThumbviewer::SaveCompleted(LPCOLESTR) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE CThumbviewer::GetCurFile(LPOLESTR __RPC_FAR*) { return E_NOTIMPL; } Gdiplus::Rect CThumbviewer::CalcScaledAspectRatio(Gdiplus::Rect src, Gdiplus::Rect dest) { Gdiplus::Rect result; if (src.Width >= src.Height) result = Gdiplus::Rect(0, 0, dest.Width, src.Height * dest.Width / src.Width); else result = Gdiplus::Rect(0, 0, src.Width * dest.Height / src.Height, dest.Height); return result; }