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
25 #include <thumbviewer.hxx>
26 #include <shlxthdl.hxx>
27 #include <registry.hxx>
28 #include <fileextensions.hxx>
30 #include <zipfile.hxx>
31 #include <utilities.hxx>
45 /* The signet.png used for thumbnails of signed documents
46 is contained as resource in this module, the resource
48 static void LoadSignetImageFromResource(ZipFile::ZipContentBuffer_t
& buffer
)
50 HRSRC hrc
= FindResourceW(g_hModule
, L
"#2000", MAKEINTRESOURCEW(RT_RCDATA
));
51 DWORD size
= SizeofResource(g_hModule
, hrc
);
52 HGLOBAL hglob
= LoadResource(g_hModule
, hrc
);
53 char* data
= static_cast<char*>(LockResource(hglob
));
54 buffer
= ZipFile::ZipContentBuffer_t(data
, data
+ size
);
57 static bool IsSignedDocument(const ZipFile
* zipfile
)
59 return zipfile
->HasContent("META-INF/documentsignatures.xml");
62 static Gdiplus::Point
CalcSignetPosition(
63 const Gdiplus::Rect
& canvas
, const Gdiplus::Rect
& thumbnail_border
, const Gdiplus::Rect
& signet
)
67 int hoffset
= canvas
.GetRight() - thumbnail_border
.GetRight();
68 int voffset
= canvas
.GetBottom() - thumbnail_border
.GetBottom();
70 if (hoffset
> voffset
)
72 x
= thumbnail_border
.GetRight() - signet
.GetRight() + min(signet
.GetRight() / 2, hoffset
);
73 y
= thumbnail_border
.GetBottom() - signet
.GetBottom();
77 x
= thumbnail_border
.GetRight() - signet
.GetRight();
78 y
= thumbnail_border
.GetBottom() - signet
.GetBottom() + min(signet
.GetBottom() / 2, voffset
);
81 return Gdiplus::Point(x
,y
);
87 Gdiplus::Rect
CalcScaledAspectRatio(const Gdiplus::Rect
& src
, const Gdiplus::Rect
& dest
)
90 if (src
.Width
>= src
.Height
)
91 result
= Gdiplus::Rect(0, 0, dest
.Width
, src
.Height
* dest
.Width
/ src
.Width
);
93 result
= Gdiplus::Rect(0, 0, src
.Width
* dest
.Height
/ src
.Height
, dest
.Height
);
100 class StreamOnZipBuffer final
: public IStream
103 explicit StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t
& zip_buffer
);
106 virtual ULONG STDMETHODCALLTYPE
AddRef() override
;
107 virtual ULONG STDMETHODCALLTYPE
Release() override
;
108 virtual HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
) override
;
111 virtual HRESULT STDMETHODCALLTYPE
Read(void *pv
, ULONG cb
, ULONG
*pcbRead
) override
;
112 virtual HRESULT STDMETHODCALLTYPE
Write(void const *pv
, ULONG cb
, ULONG
*pcbWritten
) override
;
113 virtual HRESULT STDMETHODCALLTYPE
Seek(LARGE_INTEGER dlibMove
, DWORD dwOrigin
, ULARGE_INTEGER
*plibNewPosition
) override
;
114 virtual HRESULT STDMETHODCALLTYPE
SetSize(ULARGE_INTEGER libNewSize
) override
;
115 virtual HRESULT STDMETHODCALLTYPE
CopyTo(IStream
*pstm
, ULARGE_INTEGER cb
, ULARGE_INTEGER
*pcbRead
, ULARGE_INTEGER
*pcbWritten
) override
;
116 virtual HRESULT STDMETHODCALLTYPE
Commit(DWORD grfCommitFlags
) override
;
117 virtual HRESULT STDMETHODCALLTYPE
Revert() override
;
118 virtual HRESULT STDMETHODCALLTYPE
LockRegion(ULARGE_INTEGER libOffset
, ULARGE_INTEGER cb
, DWORD dwLockType
) override
;
119 virtual HRESULT STDMETHODCALLTYPE
UnlockRegion(ULARGE_INTEGER libOffset
, ULARGE_INTEGER cb
, DWORD dwLockType
) override
;
120 virtual HRESULT STDMETHODCALLTYPE
Stat(STATSTG
*pstatstg
, DWORD grfStatFlag
) override
;
121 virtual HRESULT STDMETHODCALLTYPE
Clone(IStream
**ppstm
) override
;
125 const ZipFile::ZipContentBuffer_t
& ref_zip_buffer_
;
129 StreamOnZipBuffer::StreamOnZipBuffer(const ZipFile::ZipContentBuffer_t
& zip_buffer
) :
131 ref_zip_buffer_(zip_buffer
),
138 ULONG STDMETHODCALLTYPE
StreamOnZipBuffer::AddRef()
140 return InterlockedIncrement(&ref_count_
);
143 ULONG STDMETHODCALLTYPE
StreamOnZipBuffer::Release()
145 long refcnt
= InterlockedDecrement(&ref_count_
);
153 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
)
155 *ppvObject
= nullptr;
156 IUnknown
* pUnk
= nullptr;
158 if ((IID_IUnknown
== riid
) || (IID_IStream
== riid
))
160 pUnk
= static_cast<IStream
*>(this);
165 return E_NOINTERFACE
;
168 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Read(void *pv
, ULONG cb
, ULONG
*pcbRead
)
171 return STG_E_INVALIDPOINTER
;
173 size_t size
= ref_zip_buffer_
.size();
178 char* p
= static_cast<char*>(pv
);
181 for ( ;(pos_
< size
) && (cb
> 0); pos_
++, cb
--, read
++)
182 *p
++ = ref_zip_buffer_
[pos_
];
190 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Seek(LARGE_INTEGER dlibMove
, DWORD dwOrigin
, ULARGE_INTEGER
*)
192 __int64 size
= static_cast<__int64
>(ref_zip_buffer_
.size());
197 case STREAM_SEEK_SET
:
199 case STREAM_SEEK_CUR
:
200 p
= static_cast<__int64
>(pos_
);
202 case STREAM_SEEK_END
:
207 HRESULT hr
= STG_E_INVALIDFUNCTION
;
209 p
+= dlibMove
.QuadPart
;
211 if ( ( p
>= 0 ) && (p
< size
) )
213 pos_
= static_cast<size_t>(p
);
219 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Stat(STATSTG
*pstatstg
, DWORD grfStatFlag
)
221 if (pstatstg
== nullptr)
222 return STG_E_INVALIDPOINTER
;
224 ZeroMemory(pstatstg
, sizeof(STATSTG
));
226 if (grfStatFlag
== STATFLAG_DEFAULT
)
228 size_t sz
= 4 * sizeof(wchar_t);
229 wchar_t* name
= static_cast<wchar_t*>(CoTaskMemAlloc(sz
));
230 ZeroMemory(name
, sz
);
231 memcpy(name
, L
"png", 3 * sizeof(wchar_t));
232 pstatstg
->pwcsName
= name
;
235 pstatstg
->type
= STGTY_LOCKBYTES
;
238 uli
.LowPart
= static_cast<DWORD
>(ref_zip_buffer_
.size());
241 pstatstg
->cbSize
= uli
;
246 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Write(void const *, ULONG
, ULONG
*)
247 { return E_NOTIMPL
; }
249 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::SetSize(ULARGE_INTEGER
)
250 { return E_NOTIMPL
; }
252 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::CopyTo(IStream
*, ULARGE_INTEGER
, ULARGE_INTEGER
*, ULARGE_INTEGER
*)
253 { return E_NOTIMPL
; }
255 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Commit(DWORD
)
256 { return E_NOTIMPL
; }
258 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Revert()
259 { return E_NOTIMPL
; }
261 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::LockRegion(ULARGE_INTEGER
, ULARGE_INTEGER
, DWORD
)
262 { return E_NOTIMPL
; }
264 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::UnlockRegion(ULARGE_INTEGER
, ULARGE_INTEGER
, DWORD
)
265 { return E_NOTIMPL
; }
267 HRESULT STDMETHODCALLTYPE
StreamOnZipBuffer::Clone(IStream
**)
268 { return E_NOTIMPL
; }
271 CThumbviewer::CThumbviewer(long RefCnt
) :
274 InterlockedIncrement(&g_DllRefCnt
);
276 thumbnail_size_
.cx
= 0;
277 thumbnail_size_
.cy
= 0;
279 Gdiplus::GdiplusStartupInput gdiplusStartupInput
;
280 Gdiplus::GdiplusStartup(&gdiplus_token_
, &gdiplusStartupInput
, nullptr);
282 ZipFile::ZipContentBuffer_t img_data
;
283 internal::LoadSignetImageFromResource(img_data
);
284 IStream
* stream
= new StreamOnZipBuffer(img_data
);
285 signet_
= new Gdiplus::Bitmap(stream
, TRUE
);
289 CThumbviewer::~CThumbviewer()
292 Gdiplus::GdiplusShutdown(gdiplus_token_
);
293 InterlockedDecrement(&g_DllRefCnt
);
298 HRESULT STDMETHODCALLTYPE
CThumbviewer::QueryInterface(REFIID riid
, void __RPC_FAR
*__RPC_FAR
*ppvObject
)
300 *ppvObject
= nullptr;
301 IUnknown
* pUnk
= nullptr;
303 if ((IID_IUnknown
== riid
) || (IID_IPersistFile
== riid
))
305 pUnk
= static_cast<IPersistFile
*>(this);
310 else if (IID_IExtractImage
== riid
)
312 pUnk
= static_cast<IExtractImage
*>(this);
317 return E_NOINTERFACE
;
320 ULONG STDMETHODCALLTYPE
CThumbviewer::AddRef()
322 return InterlockedIncrement(&ref_count_
);
325 ULONG STDMETHODCALLTYPE
CThumbviewer::Release()
327 long refcnt
= InterlockedDecrement(&ref_count_
);
335 // IExtractImage2 methods
337 const std::string THUMBNAIL_CONTENT
= "Thumbnails/thumbnail.png";
339 HRESULT STDMETHODCALLTYPE
CThumbviewer::Extract(HBITMAP
*phBmpImage
)
345 std::wstring fname
= getShortPathName( filename_
);
346 std::unique_ptr
<ZipFile
> zipfile( new ZipFile( fname
) );
348 if (zipfile
->HasContent(THUMBNAIL_CONTENT
))
350 ZipFile::ZipContentBuffer_t thumbnail
;
351 zipfile
->GetUncompressedContent(THUMBNAIL_CONTENT
, thumbnail
);
352 IStream
* stream
= new StreamOnZipBuffer(thumbnail
);
354 Gdiplus::Bitmap
thumbnail_png(stream
, TRUE
);
356 if ((thumbnail_png
.GetHeight() == 0) || (thumbnail_png
.GetWidth() == 0))
362 HWND hwnd
= GetDesktopWindow();
363 HDC hdc
= GetDC(hwnd
);
364 HDC memDC
= CreateCompatibleDC(hdc
);
368 UINT offset
= 3; // reserve a little border space
370 Gdiplus::Rect
canvas(0, 0, thumbnail_size_
.cx
, thumbnail_size_
.cy
);
371 Gdiplus::Rect
canvas_thumbnail(offset
, offset
, thumbnail_size_
.cx
- 2 * offset
, thumbnail_size_
.cy
- 2 * offset
);
373 Gdiplus::Rect scaledRect
= CalcScaledAspectRatio(
374 Gdiplus::Rect(0, 0, thumbnail_png
.GetWidth(), thumbnail_png
.GetHeight()), canvas_thumbnail
);
381 ZeroMemory(&dib
, sizeof(dib
));
383 dib
.bi
.biSize
= sizeof(BITMAPINFOHEADER
);
384 dib
.bi
.biWidth
= thumbnail_size_
.cx
;
385 dib
.bi
.biHeight
= thumbnail_size_
.cy
;
387 dib
.bi
.biBitCount
= static_cast<WORD
>(color_depth_
);
388 dib
.bi
.biCompression
= BI_RGB
;
391 HBITMAP hMemBmp
= CreateDIBSection(memDC
, reinterpret_cast<LPBITMAPINFO
>(&dib
), DIB_RGB_COLORS
, &lpBits
, nullptr, 0);
392 HGDIOBJ hOldObj
= SelectObject(memDC
, hMemBmp
);
394 Gdiplus::Graphics
graphics(memDC
);
395 Gdiplus::Pen
blackPen(Gdiplus::Color(255, 0, 0, 0), 1);
397 Gdiplus::SolidBrush
whiteBrush(Gdiplus::Color(255, 255, 255, 255));
398 graphics
.FillRectangle(&whiteBrush
, canvas
);
400 scaledRect
.X
= (canvas
.Width
- scaledRect
.Width
) / 2;
401 scaledRect
.Y
= (canvas
.Height
- scaledRect
.Height
) / 2;
403 Gdiplus::Rect
border_rect(scaledRect
.X
, scaledRect
.Y
, scaledRect
.Width
, scaledRect
.Height
);
404 graphics
.DrawRectangle(&blackPen
, border_rect
);
408 scaledRect
.Width
-= 1;
409 scaledRect
.Height
-= 1;
411 graphics
.SetInterpolationMode(Gdiplus::InterpolationModeHighQualityBicubic
);
412 Gdiplus::Status stat
= graphics
.DrawImage(
413 &thumbnail_png
, scaledRect
, 0 , 0,
414 thumbnail_png
.GetWidth(), thumbnail_png
.GetHeight(), Gdiplus::UnitPixel
);
416 /* Add a signet sign to the thumbnail of signed documents */
417 if (internal::IsSignedDocument(zipfile
.get()))
419 double SCALING_FACTOR
= 0.6;
420 Gdiplus::Rect
signet_scaled(
421 0, 0, static_cast<INT
>(signet_
->GetWidth() * SCALING_FACTOR
), static_cast<INT
>(signet_
->GetHeight() * SCALING_FACTOR
));
422 Gdiplus::Point pos_signet
= internal::CalcSignetPosition(canvas_thumbnail
, border_rect
, signet_scaled
);
423 Gdiplus::Rect
dest(pos_signet
.X
, pos_signet
.Y
, signet_scaled
.GetRight(), signet_scaled
.GetBottom());
425 stat
= graphics
.DrawImage(
427 0, 0, signet_
->GetWidth(), signet_
->GetHeight(),
431 if (stat
== Gdiplus::Ok
)
433 *phBmpImage
= hMemBmp
;
437 SelectObject(memDC
, hOldObj
);
441 ReleaseDC(hwnd
, hdc
);
445 catch(std::exception
&)
447 OutputDebugStringFormatW( L
"CThumbviewer Extract ERROR!\n" );
453 HRESULT STDMETHODCALLTYPE
CThumbviewer::GetLocation(
454 LPWSTR pszPathBuffer
, DWORD cchMax
, DWORD
*pdwPriority
, const SIZE
*prgSize
, DWORD dwRecClrDepth
, DWORD
*pdwFlags
)
456 if ((prgSize
== nullptr) || (pdwFlags
== nullptr) || ((*pdwFlags
& IEIFLAG_ASYNC
) && (pdwPriority
== nullptr)))
459 thumbnail_size_
= *prgSize
;
460 color_depth_
= dwRecClrDepth
;
462 *pdwFlags
= IEIFLAG_CACHE
; // we don't cache the image
464 wcsncpy(pszPathBuffer
, filename_
.c_str(), cchMax
);
471 HRESULT STDMETHODCALLTYPE
CThumbviewer::GetClassID(CLSID
* pClassID
)
473 pClassID
= const_cast<CLSID
*>(&CLSID_THUMBVIEWER_HANDLER
);
477 // IPersistFile methods
479 HRESULT STDMETHODCALLTYPE
CThumbviewer::Load(LPCOLESTR pszFileName
, DWORD
)
481 filename_
= pszFileName
;
485 HRESULT STDMETHODCALLTYPE
CThumbviewer::IsDirty()
486 { return E_NOTIMPL
; }
488 HRESULT STDMETHODCALLTYPE
CThumbviewer::Save(LPCOLESTR
, BOOL
)
489 { return E_NOTIMPL
; }
491 HRESULT STDMETHODCALLTYPE
CThumbviewer::SaveCompleted(LPCOLESTR
)
492 { return E_NOTIMPL
; }
494 HRESULT STDMETHODCALLTYPE
CThumbviewer::GetCurFile(LPOLESTR __RPC_FAR
*)
495 { return E_NOTIMPL
; }
498 #endif // DONT_HAVE_GDIPLUS
500 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */