1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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"
40 #pragma warning(push, 1)
43 #ifdef _WIN32_WINNT_WINBLUE
44 #include <VersionHelpers.h>
51 extern HINSTANCE g_hModule
;
55 /* The signet.png used for thumbnails of signed documents
56 is contained as resource in this module, the resource
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");
74 // the Win32 SDK 8.1 deprecates GetVersionEx()
75 #ifdef _WIN32_WINNT_WINBLUE
76 return IsWindowsXPOrGreater() ? true : false;
79 ZeroMemory(&osvi
, sizeof(osvi
));
80 osvi
.dwOSVersionInfoSize
= sizeof(osvi
);
83 return ((osvi
.dwPlatformId
== VER_PLATFORM_WIN32_NT
) &&
84 ((osvi
.dwMajorVersion
>= 5) && (osvi
.dwMinorVersion
>= 1)));
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
94 Gdiplus::Point
CalcSignetPosition(
95 const Gdiplus::Rect
& canvas
, const Gdiplus::Rect
& thumbnail_border
, const Gdiplus::Rect
& signet
)
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();
109 x
= thumbnail_border
.GetRight() - signet
.GetRight();
110 y
= thumbnail_border
.GetBottom() - signet
.GetBottom() + min(signet
.GetBottom() / 2, voffset
);
116 return Gdiplus::Point(x
,y
);
120 class StreamOnZipBuffer
: public IStream
123 StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t
& zip_buffer
);
126 virtual ULONG STDMETHODCALLTYPE
AddRef();
127 virtual ULONG STDMETHODCALLTYPE
Release();
128 virtual HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
);
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
);
145 const ZipFile::ZipContentBuffer_t
& ref_zip_buffer_
;
149 StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t
& zip_buffer
) :
151 ref_zip_buffer_(zip_buffer
),
158 ULONG STDMETHODCALLTYPE
StreamOnZipBuffer::AddRef()
160 return InterlockedIncrement(&ref_count_
);
163 ULONG STDMETHODCALLTYPE
StreamOnZipBuffer::Release()
165 long refcnt
= InterlockedDecrement(&ref_count_
);
173 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
)
178 if ((IID_IUnknown
== riid
) || (IID_IStream
== riid
))
180 pUnk
= static_cast<IStream
*>(this);
185 return E_NOINTERFACE
;
188 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Read(void *pv
, ULONG cb
, ULONG
*pcbRead
)
191 return STG_E_INVALIDPOINTER
;
193 size_t size
= ref_zip_buffer_
.size();
198 char* p
= reinterpret_cast<char*>(pv
);
201 for ( ;(pos_
< size
) && (cb
> 0); pos_
++, cb
--, read
++)
202 *p
++ = ref_zip_buffer_
[pos_
];
210 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove
, DWORD dwOrigin
, ULARGE_INTEGER
*)
212 __int64 size
= (__int64
) ref_zip_buffer_
.size();
217 case STREAM_SEEK_SET
:
219 case STREAM_SEEK_CUR
:
222 case STREAM_SEEK_END
:
227 HRESULT hr
= STG_E_INVALIDFUNCTION
;
229 p
+= dlibMove
.QuadPart
;
231 if ( ( p
>= 0 ) && (p
< size
) )
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
;
258 uli
.LowPart
= static_cast<DWORD
>(ref_zip_buffer_
.size());
261 pstatstg
->cbSize
= uli
;
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
) :
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
);
311 CThumbviewer::~CThumbviewer()
314 Gdiplus::GdiplusShutdown(gdiplus_token_
);
315 InterlockedDecrement(&g_DllRefCnt
);
320 HRESULT STDMETHODCALLTYPE
CThumbviewer::QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
)
325 if ((IID_IUnknown
== riid
) || (IID_IPersistFile
== riid
))
327 pUnk
= static_cast<IPersistFile
*>(this);
332 else if (IID_IExtractImage
== riid
)
334 pUnk
= static_cast<IExtractImage
*>(this);
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_
);
357 // IExtractImage2 methods
359 const std::string THUMBNAIL_CONTENT
= "Thumbnails/thumbnail.png";
361 HRESULT STDMETHODCALLTYPE
CThumbviewer::Extract(HBITMAP
*phBmpImage
)
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))
384 HWND hwnd
= GetDesktopWindow();
385 HDC hdc
= GetDC(hwnd
);
386 HDC memDC
= CreateCompatibleDC(hdc
);
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
);
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
;
409 dib
.bi
.biBitCount
= static_cast<WORD
>(color_depth_
);
410 dib
.bi
.biCompression
= BI_RGB
;
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
);
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(
449 0, 0, signet_
->GetWidth(), signet_
->GetHeight(),
453 if (stat
== Gdiplus::Ok
)
455 *phBmpImage
= hMemBmp
;
459 SelectObject(memDC
, hOldObj
);
463 ReleaseDC(hwnd
, hdc
);
467 catch(std::exception
&)
469 OutputDebugStringFormat( "CThumbviewer Extract ERROR!\n" );
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
)))
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
);
493 HRESULT STDMETHODCALLTYPE
CThumbviewer::GetClassID(CLSID
* pClassID
)
495 pClassID
= const_cast<CLSID
*>(&CLSID_THUMBVIEWER_HANDLER
);
499 // IPersistFile methods
501 HRESULT STDMETHODCALLTYPE
CThumbviewer::Load(LPCOLESTR pszFileName
, DWORD
)
503 filename_
= pszFileName
;
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
);
526 result
= Gdiplus::Rect(0, 0, src
.Width
* dest
.Height
/ src
.Height
, dest
.Height
);
531 #endif // DONT_HAVE_GDIPLUS
533 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */