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 #include <config_features.h>
22 #include <svx/svdomedia.hxx>
24 #include <rtl/ustring.hxx>
25 #include <osl/file.hxx>
27 #include <com/sun/star/document/XStorageBasedDocument.hpp>
28 #include <com/sun/star/embed/XStorage.hpp>
30 #include <ucbhelper/content.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/storagehelper.hxx>
34 #include <vcl/svapp.hxx>
36 #include <svx/svdmodel.hxx>
37 #include "svdglob.hxx"
38 #include "svx/svdstr.hrc"
39 #include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
40 #include <avmedia/mediawindow.hxx>
42 // For handling of glTF models
43 #include <unotools/tempfile.hxx>
44 #include <unotools/localfilehelper.hxx>
45 #include <tools/urlobj.hxx>
47 using namespace ::com::sun::star
;
52 // Note: the temp file is read only, until it is deleted!
53 // It may be shared between multiple documents in case of copy/paste,
54 // hence the shared_ptr.
57 OUString
const m_TempFileURL
;
58 OUString
const m_TempDirURL
; // yet another hack, for the glTF models
59 MediaTempFile(OUString
const& rURL
, OUString
const& rDirURL
)
60 : m_TempFileURL(rURL
), m_TempDirURL(rDirURL
)
64 ::osl::File::remove(m_TempFileURL
);
65 if (!m_TempDirURL
.isEmpty())
67 ::utl::removeTree(m_TempDirURL
);
72 struct SdrMediaObj::Impl
74 ::avmedia::MediaItem m_MediaProperties
;
75 ::boost::shared_ptr
< MediaTempFile
> m_pTempFile
;
76 uno::Reference
< graphic::XGraphic
> m_xCachedSnapshot
;
77 OUString m_LastFailedPkgURL
;
80 TYPEINIT1( SdrMediaObj
, SdrRectObj
);
84 SdrMediaObj::SdrMediaObj()
86 , m_xImpl( new Impl() )
90 SdrMediaObj::SdrMediaObj( const Rectangle
& rRect
)
92 , m_xImpl( new Impl() )
96 SdrMediaObj::~SdrMediaObj()
100 bool SdrMediaObj::HasTextEdit() const
105 sdr::contact::ViewContact
* SdrMediaObj::CreateObjectSpecificViewContact()
107 return new sdr::contact::ViewContactOfSdrMediaObj( *this );
110 void SdrMediaObj::TakeObjInfo( SdrObjTransformInfoRec
& rInfo
) const
112 rInfo
.bSelectAllowed
= true;
113 rInfo
.bMoveAllowed
= true;
114 rInfo
.bResizeFreeAllowed
= true;
115 rInfo
.bResizePropAllowed
= true;
116 rInfo
.bRotateFreeAllowed
= false;
117 rInfo
.bRotate90Allowed
= false;
118 rInfo
.bMirrorFreeAllowed
= false;
119 rInfo
.bMirror45Allowed
= false;
120 rInfo
.bMirror90Allowed
= false;
121 rInfo
.bTransparenceAllowed
= false;
122 rInfo
.bGradientAllowed
= false;
123 rInfo
.bShearAllowed
= false;
124 rInfo
.bEdgeRadiusAllowed
= false;
125 rInfo
.bNoOrthoDesired
= false;
126 rInfo
.bNoContortion
= false;
127 rInfo
.bCanConvToPath
= false;
128 rInfo
.bCanConvToPoly
= false;
129 rInfo
.bCanConvToContour
= false;
130 rInfo
.bCanConvToPathLineToArea
= false;
131 rInfo
.bCanConvToPolyLineToArea
= false;
134 sal_uInt16
SdrMediaObj::GetObjIdentifier() const
136 return sal_uInt16( OBJ_MEDIA
);
139 OUString
SdrMediaObj::TakeObjNameSingul() const
141 OUStringBuffer
sName(ImpGetResStr(STR_ObjNameSingulMEDIA
));
143 OUString
aName(GetName());
145 if (!aName
.isEmpty())
153 return sName
.makeStringAndClear();
156 OUString
SdrMediaObj::TakeObjNamePlural() const
158 return ImpGetResStr(STR_ObjNamePluralMEDIA
);
161 SdrMediaObj
* SdrMediaObj::Clone() const
163 return CloneHelper
< SdrMediaObj
>();
166 SdrMediaObj
& SdrMediaObj::operator=(const SdrMediaObj
& rObj
)
170 SdrRectObj::operator=( rObj
);
172 m_xImpl
->m_pTempFile
= rObj
.m_xImpl
->m_pTempFile
; // before props
173 setMediaProperties( rObj
.getMediaProperties() );
174 m_xImpl
->m_xCachedSnapshot
= rObj
.m_xImpl
->m_xCachedSnapshot
;
178 const uno::Reference
< graphic::XGraphic
> SdrMediaObj::getSnapshot() const
180 if( !m_xImpl
->m_xCachedSnapshot
.is() )
182 OUString aRealURL
= m_xImpl
->m_MediaProperties
.getTempURL();
183 if( aRealURL
.isEmpty() )
184 aRealURL
= m_xImpl
->m_MediaProperties
.getURL();
185 m_xImpl
->m_xCachedSnapshot
= avmedia::MediaWindow::grabFrame( aRealURL
, m_xImpl
->m_MediaProperties
.getReferer(), m_xImpl
->m_MediaProperties
.getMimeType());
187 return m_xImpl
->m_xCachedSnapshot
;
190 void SdrMediaObj::AdjustToMaxRect( const Rectangle
& rMaxRect
, bool bShrinkOnly
/* = false */ )
192 Size
aSize( Application::GetDefaultDevice()->PixelToLogic( getPreferredSize(), MAP_100TH_MM
) );
193 Size
aMaxSize( rMaxRect
.GetSize() );
195 if( aSize
.Height() != 0 && aSize
.Width() != 0 )
197 Point
aPos( rMaxRect
.TopLeft() );
199 // if graphic is too large, fit it to the page
200 if ( (!bShrinkOnly
||
201 ( aSize
.Height() > aMaxSize
.Height() ) ||
202 ( aSize
.Width() > aMaxSize
.Width() ) )&&
203 aSize
.Height() && aMaxSize
.Height() )
205 float fGrfWH
= (float)aSize
.Width() /
206 (float)aSize
.Height();
207 float fWinWH
= (float)aMaxSize
.Width() /
208 (float)aMaxSize
.Height();
210 // scale graphic to page size
211 if ( fGrfWH
< fWinWH
)
213 aSize
.Width() = (long)(aMaxSize
.Height() * fGrfWH
);
214 aSize
.Height()= aMaxSize
.Height();
216 else if ( fGrfWH
> 0.F
)
218 aSize
.Width() = aMaxSize
.Width();
219 aSize
.Height()= (long)(aMaxSize
.Width() / fGrfWH
);
222 aPos
= rMaxRect
.Center();
226 aPos
= maRect
.TopLeft();
228 aPos
.X() -= aSize
.Width() / 2;
229 aPos
.Y() -= aSize
.Height() / 2;
230 SetLogicRect( Rectangle( aPos
, aSize
) );
234 void SdrMediaObj::setURL( const OUString
& rURL
, const OUString
& rReferer
, const OUString
& rMimeType
)
236 ::avmedia::MediaItem aURLItem
;
237 if( !rMimeType
.isEmpty() )
238 m_xImpl
->m_MediaProperties
.setMimeType(rMimeType
);
239 aURLItem
.setURL( rURL
, "", rReferer
);
240 setMediaProperties( aURLItem
);
243 const OUString
& SdrMediaObj::getURL() const
245 return m_xImpl
->m_MediaProperties
.getURL();
248 void SdrMediaObj::setMediaProperties( const ::avmedia::MediaItem
& rState
)
250 mediaPropertiesChanged( rState
);
251 static_cast< sdr::contact::ViewContactOfSdrMediaObj
& >( GetViewContact() ).executeMediaItem( getMediaProperties() );
254 const ::avmedia::MediaItem
& SdrMediaObj::getMediaProperties() const
256 return m_xImpl
->m_MediaProperties
;
259 Size
SdrMediaObj::getPreferredSize() const
261 return static_cast< sdr::contact::ViewContactOfSdrMediaObj
& >( GetViewContact() ).getPreferredSize();
264 uno::Reference
<io::XInputStream
> SdrMediaObj::GetInputStream()
266 if (!m_xImpl
->m_pTempFile
)
268 SAL_WARN("svx", "this is only intended for embedded media");
271 ucbhelper::Content
tempFile(m_xImpl
->m_pTempFile
->m_TempFileURL
,
272 uno::Reference
<ucb::XCommandEnvironment
>(),
273 comphelper::getProcessComponentContext());
274 return tempFile
.openStream();
277 #if HAVE_FEATURE_GLTF
278 static bool lcl_HandleJsonPackageURL(
279 const OUString
& rURL
,
280 SdrModel
* const pModel
,
281 OUString
& o_rTempFileURL
,
282 OUString
& o_rTempDirURL
)
284 // Create a temporary folder which will contain all files of glTF model
285 o_rTempDirURL
= ::utl::TempFile(NULL
, true).GetURL();
287 const sal_uInt16 nPackageLength
= OString("vnd.sun.star.Package:").getLength();
288 const OUString sUrlPath
= rURL
.copy(nPackageLength
,rURL
.lastIndexOf("/")-nPackageLength
);
292 uno::Reference
<document::XStorageBasedDocument
> const xSBD(
293 pModel
->getUnoModel(), uno::UNO_QUERY_THROW
);
294 const uno::Reference
<embed::XStorage
> xStorage(
295 xSBD
->getDocumentStorage(), uno::UNO_QUERY_THROW
);
298 ::comphelper::LifecycleProxy proxy
;
299 const uno::Reference
<embed::XStorage
> xModelStorage(
300 ::comphelper::OStorageHelper::GetStorageAtPath(xStorage
, sUrlPath
,
301 embed::ElementModes::READ
, proxy
));
303 // Copy all files of glTF model from storage to the temp folder
304 uno::Reference
< container::XNameAccess
> xNameAccess( xModelStorage
, uno::UNO_QUERY
);
305 const uno::Sequence
< OUString
> aFilenames
= xNameAccess
->getElementNames();
306 for( sal_Int32 nFileIndex
= 0; nFileIndex
< aFilenames
.getLength(); ++nFileIndex
)
308 // Generate temp file path
309 const OUString
& rFilename
= aFilenames
[nFileIndex
];
310 INetURLObject
aUrlObj(o_rTempDirURL
);
311 aUrlObj
.insertName(rFilename
);
312 const OUString sFilepath
= aUrlObj
.GetMainURL( INetURLObject::NO_DECODE
);
314 // Media URL will point at json file
315 if( rFilename
.endsWith(".json") )
316 o_rTempFileURL
= sFilepath
;
318 // Create temp file and fill it from storage
319 ::ucbhelper::Content
aTargetContent(sFilepath
,
320 uno::Reference
<ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext());
322 uno::Reference
<io::XStream
> const xStream(
323 xModelStorage
->openStreamElement(rFilename
,embed::ElementModes::READ
), uno::UNO_SET_THROW
);
324 uno::Reference
<io::XInputStream
> const xInputStream(
325 xStream
->getInputStream(), uno::UNO_SET_THROW
);
327 aTargetContent
.writeStream(xInputStream
,true);
331 catch (uno::Exception
const& e
)
333 SAL_INFO("svx", "exception while copying glTF related files to temp directory '" << e
.Message
<< "'");
339 static bool lcl_CopyToTempFile(
340 uno::Reference
<io::XInputStream
> const& xInStream
,
341 OUString
& o_rTempFileURL
)
343 OUString tempFileURL
;
344 ::osl::FileBase::RC
const err
=
345 ::osl::FileBase::createTempFile(0, 0, & tempFileURL
);
346 if (::osl::FileBase::E_None
!= err
)
348 SAL_INFO("svx", "cannot create temp file");
354 ::ucbhelper::Content
tempContent(tempFileURL
,
355 uno::Reference
<ucb::XCommandEnvironment
>(),
356 comphelper::getProcessComponentContext());
357 tempContent
.writeStream(xInStream
, true); // copy stream to file
359 catch (uno::Exception
const& e
)
361 SAL_WARN("svx", "exception: '" << e
.Message
<< "'");
364 o_rTempFileURL
= tempFileURL
;
368 void SdrMediaObj::SetInputStream(uno::Reference
<io::XInputStream
> const& xStream
)
370 if (m_xImpl
->m_pTempFile
|| m_xImpl
->m_LastFailedPkgURL
.isEmpty())
372 SAL_WARN("svx", "this is only intended for embedded media");
375 OUString tempFileURL
;
376 bool const bSuccess
= lcl_CopyToTempFile(xStream
, tempFileURL
);
379 m_xImpl
->m_pTempFile
.reset(new MediaTempFile(tempFileURL
, ""));
380 m_xImpl
->m_MediaProperties
.setURL(
381 m_xImpl
->m_LastFailedPkgURL
, tempFileURL
, "");
383 m_xImpl
->m_LastFailedPkgURL
.clear(); // once only
386 /// copy a stream from XStorage to temp file
387 static bool lcl_HandlePackageURL(
388 OUString
const & rURL
,
389 SdrModel
*const pModel
,
390 OUString
& o_rTempFileURL
)
394 SAL_WARN("svx", "no model");
397 ::comphelper::LifecycleProxy sourceProxy
;
398 uno::Reference
<io::XInputStream
> xInStream
;
400 xInStream
= pModel
->GetDocumentStream(rURL
, sourceProxy
);
402 catch (container::NoSuchElementException
const&)
404 SAL_INFO("svx", "not found: '" << OUString(rURL
) << "'");
407 catch (uno::Exception
const& e
)
409 SAL_WARN("svx", "exception: '" << e
.Message
<< "'");
414 SAL_WARN("svx", "no stream?");
417 return lcl_CopyToTempFile(xInStream
, o_rTempFileURL
);
420 void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem
& rNewProperties
)
422 bool bBroadcastChanged
= false;
423 const AVMediaSetMask nMaskSet
= rNewProperties
.getMaskSet();
425 // use only a subset of MediaItem properties for own own properties
426 if( AVMediaSetMask::MIME_TYPE
& nMaskSet
)
427 m_xImpl
->m_MediaProperties
.setMimeType( rNewProperties
.getMimeType() );
429 if( ( AVMediaSetMask::URL
& nMaskSet
) &&
430 ( rNewProperties
.getURL() != getURL() ))
432 m_xImpl
->m_xCachedSnapshot
.clear();
433 OUString
const url(rNewProperties
.getURL());
434 if (url
.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
436 if ( !m_xImpl
->m_pTempFile
437 || (m_xImpl
->m_pTempFile
->m_TempFileURL
!=
438 rNewProperties
.getTempURL()))
440 OUString tempFileURL
;
443 #if HAVE_FEATURE_GLTF
444 if( url
.endsWith(".json") )
445 bSuccess
= lcl_HandleJsonPackageURL(url
, GetModel(), tempFileURL
, tempDirURL
);
448 bSuccess
= lcl_HandlePackageURL(url
, GetModel(), tempFileURL
);
451 m_xImpl
->m_pTempFile
.reset(
452 new MediaTempFile(tempFileURL
, tempDirURL
));
453 m_xImpl
->m_MediaProperties
.setURL(url
, tempFileURL
, "");
455 else // this case is for Clone via operator=
457 m_xImpl
->m_pTempFile
.reset();
458 m_xImpl
->m_MediaProperties
.setURL("", "", "");
459 // UGLY: oox import also gets here, because unlike ODF
460 // getDocumentStorage() is not the imported file...
461 m_xImpl
->m_LastFailedPkgURL
= url
;
466 m_xImpl
->m_MediaProperties
.setURL(url
,
467 rNewProperties
.getTempURL(), "");
472 m_xImpl
->m_pTempFile
.reset();
473 m_xImpl
->m_MediaProperties
.setURL(url
, "", rNewProperties
.getReferer());
475 bBroadcastChanged
= true;
478 if( AVMediaSetMask::LOOP
& nMaskSet
)
479 m_xImpl
->m_MediaProperties
.setLoop( rNewProperties
.isLoop() );
481 if( AVMediaSetMask::MUTE
& nMaskSet
)
482 m_xImpl
->m_MediaProperties
.setMute( rNewProperties
.isMute() );
484 if( AVMediaSetMask::VOLUMEDB
& nMaskSet
)
485 m_xImpl
->m_MediaProperties
.setVolumeDB( rNewProperties
.getVolumeDB() );
487 if( AVMediaSetMask::ZOOM
& nMaskSet
)
488 m_xImpl
->m_MediaProperties
.setZoom( rNewProperties
.getZoom() );
490 if( bBroadcastChanged
)
493 BroadcastObjectChange();
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */