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)
48 extern HINSTANCE g_hModule
;
52 /* The signet.png used for thumbnails of signed documents
53 is contained as resource in this module, the resource
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");
72 ZeroMemory(&osvi
, sizeof(osvi
));
73 osvi
.dwOSVersionInfoSize
= sizeof(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
86 Gdiplus::Point
CalcSignetPosition(
87 const Gdiplus::Rect
& canvas
, const Gdiplus::Rect
& thumbnail_border
, const Gdiplus::Rect
& signet
)
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();
101 x
= thumbnail_border
.GetRight() - signet
.GetRight();
102 y
= thumbnail_border
.GetBottom() - signet
.GetBottom() + min(signet
.GetBottom() / 2, voffset
);
108 return Gdiplus::Point(x
,y
);
112 class StreamOnZipBuffer
: public IStream
115 StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t
& zip_buffer
);
118 virtual ULONG STDMETHODCALLTYPE
AddRef();
119 virtual ULONG STDMETHODCALLTYPE
Release( void);
120 virtual HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
);
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
);
137 const ZipFile::ZipContentBuffer_t
& ref_zip_buffer_
;
141 StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t
& zip_buffer
) :
143 ref_zip_buffer_(zip_buffer
),
150 ULONG STDMETHODCALLTYPE
StreamOnZipBuffer::AddRef(void)
152 return InterlockedIncrement(&ref_count_
);
155 ULONG STDMETHODCALLTYPE
StreamOnZipBuffer::Release( void)
157 long refcnt
= InterlockedDecrement(&ref_count_
);
165 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
)
170 if ((IID_IUnknown
== riid
) || (IID_IStream
== riid
))
172 pUnk
= static_cast<IStream
*>(this);
177 return E_NOINTERFACE
;
180 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Read(void *pv
, ULONG cb
, ULONG
*pcbRead
)
183 return STG_E_INVALIDPOINTER
;
185 size_t size
= ref_zip_buffer_
.size();
190 char* p
= reinterpret_cast<char*>(pv
);
193 for ( ;(pos_
< size
) && (cb
> 0); pos_
++, cb
--, read
++)
194 *p
++ = ref_zip_buffer_
[pos_
];
202 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove
, DWORD dwOrigin
, ULARGE_INTEGER
*)
204 __int64 size
= (__int64
) ref_zip_buffer_
.size();
209 case STREAM_SEEK_SET
:
211 case STREAM_SEEK_CUR
:
214 case STREAM_SEEK_END
:
219 HRESULT hr
= STG_E_INVALIDFUNCTION
;
221 p
+= dlibMove
.QuadPart
;
223 if ( ( p
>= 0 ) && (p
< size
) )
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
;
250 uli
.LowPart
= static_cast<DWORD
>(ref_zip_buffer_
.size());
253 pstatstg
->cbSize
= uli
;
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
) :
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
);
303 CThumbviewer::~CThumbviewer()
306 Gdiplus::GdiplusShutdown(gdiplus_token_
);
307 InterlockedDecrement(&g_DllRefCnt
);
312 HRESULT STDMETHODCALLTYPE
CThumbviewer::QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
)
317 if ((IID_IUnknown
== riid
) || (IID_IPersistFile
== riid
))
319 pUnk
= static_cast<IPersistFile
*>(this);
324 else if (IID_IExtractImage
== riid
)
326 pUnk
= static_cast<IExtractImage
*>(this);
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_
);
349 // IExtractImage2 methods
351 const std::string THUMBNAIL_CONTENT
= "Thumbnails/thumbnail.png";
353 HRESULT STDMETHODCALLTYPE
CThumbviewer::Extract(HBITMAP
*phBmpImage
)
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))
376 HWND hwnd
= GetDesktopWindow();
377 HDC hdc
= GetDC(hwnd
);
378 HDC memDC
= CreateCompatibleDC(hdc
);
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
);
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
;
401 dib
.bi
.biBitCount
= static_cast<WORD
>(color_depth_
);
402 dib
.bi
.biCompression
= BI_RGB
;
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
);
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(
441 0, 0, signet_
->GetWidth(), signet_
->GetHeight(),
445 if (stat
== Gdiplus::Ok
)
447 *phBmpImage
= hMemBmp
;
451 SelectObject(memDC
, hOldObj
);
455 ReleaseDC(hwnd
, hdc
);
459 catch(std::exception
&)
461 OutputDebugStringFormat( "CThumbviewer Extract ERROR!\n" );
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
)))
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
);
485 HRESULT STDMETHODCALLTYPE
CThumbviewer::GetClassID(CLSID
* pClassID
)
487 pClassID
= const_cast<CLSID
*>(&CLSID_THUMBVIEWER_HANDLER
);
491 // IPersistFile methods
493 HRESULT STDMETHODCALLTYPE
CThumbviewer::Load(LPCOLESTR pszFileName
, DWORD
)
495 filename_
= pszFileName
;
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
);
518 result
= Gdiplus::Rect(0, 0, src
.Width
* dest
.Height
/ src
.Height
, dest
.Height
);
523 #endif // DONT_HAVE_GDIPLUS
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */