1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <com/sun/star/lang/DisposedException.hpp>
21 #include <com/sun/star/embed/EmbedStates.hpp>
22 #include <com/sun/star/embed/EmbedMapUnits.hpp>
23 #include <com/sun/star/embed/EmbedMisc.hpp>
24 #include <com/sun/star/embed/Aspects.hpp>
25 #include <com/sun/star/embed/WrongStateException.hpp>
26 #include <com/sun/star/io/XSeekable.hpp>
27 #include <com/sun/star/embed/NoVisualAreaSizeException.hpp>
28 
29 #include <oleembobj.hxx>
30 #include <comphelper/mimeconfighelper.hxx>
31 #include <comphelper/seqstream.hxx>
32 #include <filter/msfilter/classids.hxx>
33 #include <sal/log.hxx>
34 #include <tools/diagnose_ex.h>
35 
36 #if defined(_WIN32)
37 #include "olecomponent.hxx"
38 #endif
39 
40 using namespace ::com::sun::star;
41 using namespace ::comphelper;
42 
GetVisualRepresentationInNativeFormat_Impl(const uno::Reference<io::XStream> & xCachedVisRepr)43 embed::VisualRepresentation OleEmbeddedObject::GetVisualRepresentationInNativeFormat_Impl(
44                     const uno::Reference< io::XStream >& xCachedVisRepr )
45 {
46     embed::VisualRepresentation aVisualRepr;
47 
48     // TODO: detect the format in the future for now use workaround
49     uno::Reference< io::XInputStream > xInStream = xCachedVisRepr->getInputStream();
50     if ( !xInStream.is() )
51         throw uno::RuntimeException();
52     uno::Reference< io::XSeekable > xSeekable( xCachedVisRepr, uno::UNO_QUERY_THROW );
53 
54     uno::Sequence< sal_Int8 > aSeq( 2 );
55     xInStream->readBytes( aSeq, 2 );
56     xSeekable->seek( 0 );
57     if ( aSeq.getLength() == 2 && aSeq[0] == 'B' && aSeq[1] == 'M' )
58     {
59         // it's a bitmap
60         aVisualRepr.Flavor = datatransfer::DataFlavor(
61             "application/x-openoffice-bitmap;windows_formatname=\"Bitmap\"",
62             "Bitmap",
63             cppu::UnoType<uno::Sequence< sal_Int8 >>::get() );
64     }
65     else
66     {
67         // it's a metafile
68         aVisualRepr.Flavor = datatransfer::DataFlavor(
69             "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"",
70             "Windows Metafile",
71             cppu::UnoType<uno::Sequence< sal_Int8 >>::get() );
72     }
73 
74     sal_Int32 nStreamLength = static_cast<sal_Int32>(xSeekable->getLength());
75     uno::Sequence< sal_Int8 > aRepresent( nStreamLength );
76     xInStream->readBytes( aRepresent, nStreamLength );
77     aVisualRepr.Data <<= aRepresent;
78 
79     return aVisualRepr;
80 }
81 
setVisualAreaSize(sal_Int64 nAspect,const awt::Size & aSize)82 void SAL_CALL OleEmbeddedObject::setVisualAreaSize( sal_Int64 nAspect, const awt::Size& aSize )
83 {
84     // begin wrapping related part ====================
85     uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
86     if ( xWrappedObject.is() )
87     {
88         // the object was converted to OOo embedded object, the current implementation is now only a wrapper
89         xWrappedObject->setVisualAreaSize( nAspect, aSize );
90         return;
91     }
92     // end wrapping related part ====================
93 
94     ::osl::ResettableMutexGuard aGuard( m_aMutex );
95     if ( m_bDisposed )
96         throw lang::DisposedException(); // TODO
97 
98     SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" );
99     if ( nAspect == embed::Aspects::MSOLE_ICON )
100         // no representation can be retrieved
101         throw embed::WrongStateException( "Illegal call!",
102                                     static_cast< ::cppu::OWeakObject* >(this) );
103 
104     if ( m_nObjectState == -1 )
105         throw embed::WrongStateException( "The object is not loaded!",
106                                     static_cast< ::cppu::OWeakObject* >(this) );
107 
108 #ifdef _WIN32
109     // RECOMPOSE_ON_RESIZE misc flag means that the object has to be switched to running state on resize.
110     // SetExtent() is called only for objects that require it,
111     // it should not be called for MSWord documents to workaround problem i49369
112     // If cached size is not set, that means that this is the size initialization, so there is no need to set the real size
113     bool bAllowToSetExtent =
114       ( ( getStatus( nAspect ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE )
115       && !MimeConfigurationHelper::ClassIDsEqual(m_aClassID, MimeConfigurationHelper::GetSequenceClassID(MSO_WW8_CLASSID))
116       && m_bHasCachedSize );
117 
118     if ( m_nObjectState == embed::EmbedStates::LOADED && bAllowToSetExtent )
119     {
120         aGuard.clear();
121         try {
122             changeState( embed::EmbedStates::RUNNING );
123         }
124         catch( const uno::Exception& )
125         {
126             SAL_WARN( "embeddedobj.ole", "The object should not be resized without activation!" );
127         }
128         aGuard.reset();
129     }
130 
131     if ( m_pOleComponent && m_nObjectState != embed::EmbedStates::LOADED && bAllowToSetExtent )
132     {
133         awt::Size aSizeToSet = aSize;
134         aGuard.clear();
135         try {
136             m_pOleComponent->SetExtent( aSizeToSet, nAspect ); // will throw an exception in case of failure
137             m_bHasSizeToSet = false;
138         }
139         catch( const uno::Exception& )
140         {
141             // some objects do not allow to set the size even in running state
142             m_bHasSizeToSet = true;
143             m_aSizeToSet = aSizeToSet;
144             m_nAspectToSet = nAspect;
145         }
146         aGuard.reset();
147     }
148 #endif
149 
150     // cache the values
151     m_bHasCachedSize = true;
152     m_aCachedSize = aSize;
153     m_nCachedAspect = nAspect;
154 }
155 
getVisualAreaSize(sal_Int64 nAspect)156 awt::Size SAL_CALL OleEmbeddedObject::getVisualAreaSize( sal_Int64 nAspect )
157 {
158     // begin wrapping related part ====================
159     uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
160     if ( xWrappedObject.is() )
161     {
162         // the object was converted to OOo embedded object, the current implementation is now only a wrapper
163         return xWrappedObject->getVisualAreaSize( nAspect );
164     }
165     // end wrapping related part ====================
166 
167     ::osl::ResettableMutexGuard aGuard( m_aMutex );
168     if ( m_bDisposed )
169         throw lang::DisposedException(); // TODO
170 
171     SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" );
172     if ( nAspect == embed::Aspects::MSOLE_ICON )
173         // no representation can be retrieved
174         throw embed::WrongStateException( "Illegal call!",
175                                     static_cast< ::cppu::OWeakObject* >(this) );
176 
177     if ( m_nObjectState == -1 )
178         throw embed::WrongStateException( "The object is not loaded!",
179                                     static_cast< ::cppu::OWeakObject* >(this) );
180 
181     awt::Size aResult;
182 
183 #ifdef _WIN32
184     // TODO/LATER: Support different aspects
185     if ( m_pOleComponent && !m_bHasSizeToSet && nAspect == embed::Aspects::MSOLE_CONTENT )
186     {
187         try
188         {
189             // the cached size updated every time the object is stored
190             if ( m_bHasCachedSize )
191             {
192                 aResult = m_aCachedSize;
193             }
194             else
195             {
196                 // there is no internal cache
197                 awt::Size aSize;
198                 aGuard.clear();
199 
200                 bool bBackToLoaded = false;
201 
202                 bool bSuccess = false;
203                 if ( getCurrentState() == embed::EmbedStates::LOADED )
204                 {
205                     SAL_WARN( "embeddedobj.ole", "Loaded object has no cached size!" );
206 
207                     // try to switch the object to RUNNING state and request the value again
208                     try {
209                         changeState( embed::EmbedStates::RUNNING );
210                         // the links should be switched back to loaded state to avoid too
211                         // many open MathType instances
212                         bBackToLoaded = true;
213                     }
214                     catch( const uno::Exception& )
215                     {
216                         throw embed::NoVisualAreaSizeException(
217                                 "No size available!",
218                                 static_cast< ::cppu::OWeakObject* >(this) );
219                     }
220                 }
221 
222                 try
223                 {
224                     // first try to get size using replacement image
225                     aSize = m_pOleComponent->GetExtent( nAspect ); // will throw an exception in case of failure
226                     bSuccess = true;
227                 }
228                 catch( const uno::Exception& )
229                 {
230                 }
231 
232                 if (bBackToLoaded)
233                 {
234                     try
235                     {
236                         changeState(embed::EmbedStates::LOADED);
237                     }
238                     catch( const uno::Exception& )
239                     {
240                         TOOLS_WARN_EXCEPTION("embeddedobj.ole", "ignoring ");
241                     }
242                 }
243 
244                 if ( !bSuccess )
245                 {
246                     try
247                     {
248                         // second try the cached replacement image
249                         aSize = m_pOleComponent->GetCachedExtent( nAspect ); // will throw an exception in case of failure
250                         bSuccess = true;
251                     }
252                     catch( const uno::Exception& )
253                     {
254                     }
255                 }
256 
257                 if ( !bSuccess )
258                 {
259                     try
260                     {
261                         // third try the size reported by the object
262                         aSize = m_pOleComponent->GetRecommendedExtent( nAspect ); // will throw an exception in case of failure
263                         bSuccess = true;
264                     }
265                     catch( const uno::Exception& )
266                     {
267                     }
268                 }
269 
270                 if ( !bSuccess )
271                     throw embed::NoVisualAreaSizeException(
272                                     "No size available!",
273                                     static_cast< ::cppu::OWeakObject* >(this) );
274 
275                 aGuard.reset();
276 
277                 m_aCachedSize = aSize;
278                 m_nCachedAspect = nAspect;
279                 m_bHasCachedSize = true;
280 
281                 aResult = m_aCachedSize;
282             }
283         }
284         catch ( const embed::NoVisualAreaSizeException& )
285         {
286             throw;
287         }
288         catch ( const uno::Exception& )
289         {
290             throw embed::NoVisualAreaSizeException(
291                             "No size available!",
292                             static_cast< ::cppu::OWeakObject* >(this) );
293         }
294     }
295     else
296 #endif
297     {
298         // return cached value
299         if ( !m_bHasCachedSize )
300         {
301             throw embed::NoVisualAreaSizeException(
302                             "No size available!",
303                             static_cast< ::cppu::OWeakObject* >(this) );
304         }
305         SAL_WARN_IF( nAspect != m_nCachedAspect, "embeddedobj.ole", "Unexpected aspect is requested!" );
306         aResult = m_aCachedSize;
307     }
308 
309     return aResult;
310 }
311 
getPreferredVisualRepresentation(sal_Int64 nAspect)312 embed::VisualRepresentation SAL_CALL OleEmbeddedObject::getPreferredVisualRepresentation( sal_Int64 nAspect )
313 {
314     // begin wrapping related part ====================
315     uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
316     if ( xWrappedObject.is() )
317     {
318         // the object was converted to OOo embedded object, the current implementation is now only a wrapper
319         return xWrappedObject->getPreferredVisualRepresentation( nAspect );
320     }
321     // end wrapping related part ====================
322 
323     ::osl::MutexGuard aGuard( m_aMutex );
324     if ( m_bDisposed )
325         throw lang::DisposedException(); // TODO
326 
327     SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" );
328     if ( nAspect == embed::Aspects::MSOLE_ICON )
329         // no representation can be retrieved
330         throw embed::WrongStateException( "Illegal call!",
331                                     static_cast< ::cppu::OWeakObject* >(this) );
332 
333     // TODO: if the object has cached representation then it should be returned
334     // TODO: if the object has no cached representation and is in loaded state it should switch itself to the running state
335     if ( m_nObjectState == -1 )
336         throw embed::WrongStateException( "The object is not loaded!",
337                                     static_cast< ::cppu::OWeakObject* >(this) );
338 
339     embed::VisualRepresentation aVisualRepr;
340 
341     // TODO: in case of different aspects they must be applied to the mediatype and XTransferable must be used
342     // the cache is used only as a fallback if object is not in loaded state
343     if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream )
344       && m_nObjectState == embed::EmbedStates::LOADED )
345     {
346         m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream, true );
347         SetVisReplInStream( m_xCachedVisualRepresentation.is() );
348     }
349 
350 #ifdef _WIN32
351     if ( !m_xCachedVisualRepresentation.is() && m_pOleComponent )
352     {
353         try
354         {
355             if ( m_nObjectState == embed::EmbedStates::LOADED )
356                 changeState( embed::EmbedStates::RUNNING );
357 
358             datatransfer::DataFlavor aDataFlavor(
359                     "application/x-openoffice-wmf;windows_formatname=\"Image WMF\"",
360                     "Windows Metafile",
361                     cppu::UnoType<uno::Sequence< sal_Int8 >>::get() );
362 
363             aVisualRepr.Data = m_pOleComponent->getTransferData( aDataFlavor );
364             aVisualRepr.Flavor = aDataFlavor;
365 
366             uno::Sequence< sal_Int8 > aVisReplSeq;
367             aVisualRepr.Data >>= aVisReplSeq;
368             if ( aVisReplSeq.getLength() )
369             {
370                 m_xCachedVisualRepresentation = GetNewFilledTempStream_Impl(
371                     uno::Reference< io::XInputStream >(
372                         new ::comphelper::SequenceInputStream(aVisReplSeq)));
373             }
374 
375             return aVisualRepr;
376         }
377         catch( const uno::Exception& )
378         {}
379     }
380 #endif
381 
382     // the cache is used only as a fallback if object is not in loaded state
383     if ( !m_xCachedVisualRepresentation.is() && ( !m_bVisReplInitialized || m_bVisReplInStream ) )
384     {
385         m_xCachedVisualRepresentation = TryToRetrieveCachedVisualRepresentation_Impl( m_xObjectStream );
386         SetVisReplInStream( m_xCachedVisualRepresentation.is() );
387     }
388 
389     if ( !m_xCachedVisualRepresentation.is() )
390     {
391         // no representation can be retrieved
392         throw embed::WrongStateException( "Illegal call!",
393                                     static_cast< ::cppu::OWeakObject* >(this) );
394     }
395 
396     return GetVisualRepresentationInNativeFormat_Impl( m_xCachedVisualRepresentation );
397 }
398 
getMapUnit(sal_Int64 nAspect)399 sal_Int32 SAL_CALL OleEmbeddedObject::getMapUnit( sal_Int64 nAspect )
400 {
401     // begin wrapping related part ====================
402     uno::Reference< embed::XEmbeddedObject > xWrappedObject = m_xWrappedObject;
403     if ( xWrappedObject.is() )
404     {
405         // the object was converted to OOo embedded object, the current implementation is now only a wrapper
406         return xWrappedObject->getMapUnit( nAspect );
407     }
408     // end wrapping related part ====================
409 
410     ::osl::MutexGuard aGuard( m_aMutex );
411     if ( m_bDisposed )
412         throw lang::DisposedException(); // TODO
413 
414     SAL_WARN_IF( nAspect == embed::Aspects::MSOLE_ICON, "embeddedobj.ole", "For iconified objects no graphical replacement is required!" );
415     if ( nAspect == embed::Aspects::MSOLE_ICON )
416         // no representation can be retrieved
417         throw embed::WrongStateException( "Illegal call!",
418                                     static_cast< ::cppu::OWeakObject* >(this) );
419 
420     if ( m_nObjectState == -1 )
421         throw embed::WrongStateException( "The object is not loaded!",
422                                     static_cast< ::cppu::OWeakObject* >(this) );
423 
424     return embed::EmbedMapUnits::ONE_100TH_MM;
425 }
426 
427 
428 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
429