/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include "svdglob.hxx" #include "svx/svdstr.hrc" #include #include // For handling of glTF models #include #include #include using namespace ::com::sun::star; // Note: the temp file is read only, until it is deleted! // It may be shared between multiple documents in case of copy/paste, // hence the shared_ptr. struct MediaTempFile { OUString const m_TempFileURL; OUString const m_TempDirURL; // yet another hack, for the glTF models MediaTempFile(OUString const& rURL, OUString const& rDirURL) : m_TempFileURL(rURL), m_TempDirURL(rDirURL) {} ~MediaTempFile() { ::osl::File::remove(m_TempFileURL); if (!m_TempDirURL.isEmpty()) { ::utl::removeTree(m_TempDirURL); } } }; struct SdrMediaObj::Impl { ::avmedia::MediaItem m_MediaProperties; std::shared_ptr< MediaTempFile > m_pTempFile; uno::Reference< graphic::XGraphic > m_xCachedSnapshot; OUString m_LastFailedPkgURL; }; SdrMediaObj::SdrMediaObj() : SdrRectObj() , m_xImpl( new Impl ) { } SdrMediaObj::SdrMediaObj( const tools::Rectangle& rRect ) : SdrRectObj( rRect ) , m_xImpl( new Impl ) { } SdrMediaObj::~SdrMediaObj() { } bool SdrMediaObj::HasTextEdit() const { return false; } sdr::contact::ViewContact* SdrMediaObj::CreateObjectSpecificViewContact() { return new sdr::contact::ViewContactOfSdrMediaObj( *this ); } void SdrMediaObj::TakeObjInfo( SdrObjTransformInfoRec& rInfo ) const { rInfo.bSelectAllowed = true; rInfo.bMoveAllowed = true; rInfo.bResizeFreeAllowed = true; rInfo.bResizePropAllowed = true; rInfo.bRotateFreeAllowed = false; rInfo.bRotate90Allowed = false; rInfo.bMirrorFreeAllowed = false; rInfo.bMirror45Allowed = false; rInfo.bMirror90Allowed = false; rInfo.bTransparenceAllowed = false; rInfo.bGradientAllowed = false; rInfo.bShearAllowed = false; rInfo.bEdgeRadiusAllowed = false; rInfo.bNoOrthoDesired = false; rInfo.bNoContortion = false; rInfo.bCanConvToPath = false; rInfo.bCanConvToPoly = false; rInfo.bCanConvToContour = false; rInfo.bCanConvToPathLineToArea = false; rInfo.bCanConvToPolyLineToArea = false; } sal_uInt16 SdrMediaObj::GetObjIdentifier() const { return sal_uInt16( OBJ_MEDIA ); } OUString SdrMediaObj::TakeObjNameSingul() const { OUStringBuffer sName(ImpGetResStr(STR_ObjNameSingulMEDIA)); OUString aName(GetName()); if (!aName.isEmpty()) { sName.append(' '); sName.append('\''); sName.append(aName); sName.append('\''); } return sName.makeStringAndClear(); } OUString SdrMediaObj::TakeObjNamePlural() const { return ImpGetResStr(STR_ObjNamePluralMEDIA); } SdrMediaObj* SdrMediaObj::Clone() const { return CloneHelper< SdrMediaObj >(); } SdrMediaObj& SdrMediaObj::operator=(const SdrMediaObj& rObj) { if( this == &rObj ) return *this; SdrRectObj::operator=( rObj ); m_xImpl->m_pTempFile = rObj.m_xImpl->m_pTempFile; // before props setMediaProperties( rObj.getMediaProperties() ); m_xImpl->m_xCachedSnapshot = rObj.m_xImpl->m_xCachedSnapshot; return *this; } const uno::Reference< graphic::XGraphic > SdrMediaObj::getSnapshot() const { if( !m_xImpl->m_xCachedSnapshot.is() ) { OUString aRealURL = m_xImpl->m_MediaProperties.getTempURL(); if( aRealURL.isEmpty() ) aRealURL = m_xImpl->m_MediaProperties.getURL(); m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame( aRealURL, m_xImpl->m_MediaProperties.getReferer(), m_xImpl->m_MediaProperties.getMimeType()); } return m_xImpl->m_xCachedSnapshot; } void SdrMediaObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */ ) { Size aSize( Application::GetDefaultDevice()->PixelToLogic( static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( GetViewContact() ).getPreferredSize(), MapUnit::Map100thMM ) ); Size aMaxSize( rMaxRect.GetSize() ); if( aSize.Height() != 0 && aSize.Width() != 0 ) { Point aPos( rMaxRect.TopLeft() ); // if graphic is too large, fit it to the page if ( (!bShrinkOnly || ( aSize.Height() > aMaxSize.Height() ) || ( aSize.Width() > aMaxSize.Width() ) )&& aSize.Height() && aMaxSize.Height() ) { float fGrfWH = (float)aSize.Width() / (float)aSize.Height(); float fWinWH = (float)aMaxSize.Width() / (float)aMaxSize.Height(); // scale graphic to page size if ( fGrfWH < fWinWH ) { aSize.Width() = (long)(aMaxSize.Height() * fGrfWH); aSize.Height()= aMaxSize.Height(); } else if ( fGrfWH > 0.F ) { aSize.Width() = aMaxSize.Width(); aSize.Height()= (long)(aMaxSize.Width() / fGrfWH); } aPos = rMaxRect.Center(); } if( bShrinkOnly ) aPos = maRect.TopLeft(); aPos.X() -= aSize.Width() / 2; aPos.Y() -= aSize.Height() / 2; SetLogicRect( tools::Rectangle( aPos, aSize ) ); } } void SdrMediaObj::setURL( const OUString& rURL, const OUString& rReferer, const OUString& rMimeType ) { ::avmedia::MediaItem aURLItem; if( !rMimeType.isEmpty() ) m_xImpl->m_MediaProperties.setMimeType(rMimeType); aURLItem.setURL( rURL, "", rReferer ); setMediaProperties( aURLItem ); } const OUString& SdrMediaObj::getURL() const { return m_xImpl->m_MediaProperties.getURL(); } void SdrMediaObj::setMediaProperties( const ::avmedia::MediaItem& rState ) { mediaPropertiesChanged( rState ); static_cast< sdr::contact::ViewContactOfSdrMediaObj& >( GetViewContact() ).executeMediaItem( getMediaProperties() ); } const ::avmedia::MediaItem& SdrMediaObj::getMediaProperties() const { return m_xImpl->m_MediaProperties; } uno::Reference SdrMediaObj::GetInputStream() { if (!m_xImpl->m_pTempFile) { SAL_WARN("svx", "this is only intended for embedded media"); return nullptr; } ucbhelper::Content tempFile(m_xImpl->m_pTempFile->m_TempFileURL, uno::Reference(), comphelper::getProcessComponentContext()); return tempFile.openStream(); } #if HAVE_FEATURE_GLTF static bool lcl_HandleJsonPackageURL( const OUString& rURL, SdrModel* const pModel, OUString& o_rTempFileURL, OUString& o_rTempDirURL) { // Create a temporary folder which will contain all files of glTF model o_rTempDirURL = ::utl::TempFile(nullptr, true).GetURL(); const sal_uInt16 nPackageLength = OString("vnd.sun.star.Package:").getLength(); const OUString sUrlPath = rURL.copy(nPackageLength,rURL.lastIndexOf("/")-nPackageLength); try { // Base storage: uno::Reference const xSBD( pModel->getUnoModel(), uno::UNO_QUERY_THROW); const uno::Reference xStorage( xSBD->getDocumentStorage(), uno::UNO_QUERY_THROW); // Model source ::comphelper::LifecycleProxy proxy; const uno::Reference xModelStorage( ::comphelper::OStorageHelper::GetStorageAtPath(xStorage, sUrlPath, embed::ElementModes::READ, proxy)); // Copy all files of glTF model from storage to the temp folder uno::Reference< container::XNameAccess > xNameAccess( xModelStorage, uno::UNO_QUERY ); const uno::Sequence< OUString > aFilenames = xNameAccess->getElementNames(); for( sal_Int32 nFileIndex = 0; nFileIndex < aFilenames.getLength(); ++nFileIndex ) { // Generate temp file path const OUString& rFilename = aFilenames[nFileIndex]; INetURLObject aUrlObj(o_rTempDirURL); aUrlObj.insertName(rFilename); const OUString sFilepath = aUrlObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ); // Media URL will point at json file if( rFilename.endsWith(".json") ) o_rTempFileURL = sFilepath; // Create temp file and fill it from storage ::ucbhelper::Content aTargetContent(sFilepath, uno::Reference(), comphelper::getProcessComponentContext()); uno::Reference const xStream( xModelStorage->openStreamElement(rFilename,embed::ElementModes::READ), uno::UNO_SET_THROW); uno::Reference const xInputStream( xStream->getInputStream(), uno::UNO_SET_THROW); aTargetContent.writeStream(xInputStream,true); } } catch (uno::Exception const& e) { SAL_INFO("svx", "exception while copying glTF related files to temp directory '" << e.Message << "'"); } return true; } #endif static bool lcl_CopyToTempFile( uno::Reference const& xInStream, OUString & o_rTempFileURL) { OUString tempFileURL; ::osl::FileBase::RC const err = ::osl::FileBase::createTempFile(nullptr, nullptr, & tempFileURL); if (::osl::FileBase::E_None != err) { SAL_INFO("svx", "cannot create temp file"); return false; } try { ::ucbhelper::Content tempContent(tempFileURL, uno::Reference(), comphelper::getProcessComponentContext()); tempContent.writeStream(xInStream, true); // copy stream to file } catch (uno::Exception const& e) { SAL_WARN("svx", "exception: '" << e.Message << "'"); return false; } o_rTempFileURL = tempFileURL; return true; } void SdrMediaObj::SetInputStream(uno::Reference const& xStream) { if (m_xImpl->m_pTempFile || m_xImpl->m_LastFailedPkgURL.isEmpty()) { SAL_WARN("svx", "this is only intended for embedded media"); return; } OUString tempFileURL; bool const bSuccess = lcl_CopyToTempFile(xStream, tempFileURL); if (bSuccess) { m_xImpl->m_pTempFile.reset(new MediaTempFile(tempFileURL, "")); m_xImpl->m_MediaProperties.setURL( m_xImpl->m_LastFailedPkgURL, tempFileURL, ""); } m_xImpl->m_LastFailedPkgURL.clear(); // once only } /// copy a stream from XStorage to temp file static bool lcl_HandlePackageURL( OUString const & rURL, SdrModel *const pModel, OUString & o_rTempFileURL) { if (!pModel) { SAL_WARN("svx", "no model"); return false; } ::comphelper::LifecycleProxy sourceProxy; uno::Reference xInStream; try { xInStream = pModel->GetDocumentStream(rURL, sourceProxy); } catch (container::NoSuchElementException const&) { SAL_INFO("svx", "not found: '" << OUString(rURL) << "'"); return false; } catch (uno::Exception const& e) { SAL_WARN("svx", "exception: '" << e.Message << "'"); return false; } if (!xInStream.is()) { SAL_WARN("svx", "no stream?"); return false; } return lcl_CopyToTempFile(xInStream, o_rTempFileURL); } void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProperties ) { bool bBroadcastChanged = false; const AVMediaSetMask nMaskSet = rNewProperties.getMaskSet(); // use only a subset of MediaItem properties for own own properties if( AVMediaSetMask::MIME_TYPE & nMaskSet ) m_xImpl->m_MediaProperties.setMimeType( rNewProperties.getMimeType() ); if( ( AVMediaSetMask::URL & nMaskSet ) && ( rNewProperties.getURL() != getURL() )) { m_xImpl->m_xCachedSnapshot.clear(); OUString const& url(rNewProperties.getURL()); if (url.startsWithIgnoreAsciiCase("vnd.sun.star.Package:")) { if ( !m_xImpl->m_pTempFile || (m_xImpl->m_pTempFile->m_TempFileURL != rNewProperties.getTempURL())) { OUString tempFileURL; OUString tempDirURL; bool bSuccess; #if HAVE_FEATURE_GLTF if( url.endsWith(".json") ) bSuccess = lcl_HandleJsonPackageURL(url, GetModel(), tempFileURL, tempDirURL); else #endif bSuccess = lcl_HandlePackageURL(url, GetModel(), tempFileURL); if (bSuccess) { m_xImpl->m_pTempFile.reset( new MediaTempFile(tempFileURL, tempDirURL)); m_xImpl->m_MediaProperties.setURL(url, tempFileURL, ""); } else // this case is for Clone via operator= { m_xImpl->m_pTempFile.reset(); m_xImpl->m_MediaProperties.setURL("", "", ""); // UGLY: oox import also gets here, because unlike ODF // getDocumentStorage() is not the imported file... m_xImpl->m_LastFailedPkgURL = url; } } else { m_xImpl->m_MediaProperties.setURL(url, rNewProperties.getTempURL(), ""); } } else { m_xImpl->m_pTempFile.reset(); m_xImpl->m_MediaProperties.setURL(url, "", rNewProperties.getReferer()); } bBroadcastChanged = true; } if( AVMediaSetMask::LOOP & nMaskSet ) m_xImpl->m_MediaProperties.setLoop( rNewProperties.isLoop() ); if( AVMediaSetMask::MUTE & nMaskSet ) m_xImpl->m_MediaProperties.setMute( rNewProperties.isMute() ); if( AVMediaSetMask::VOLUMEDB & nMaskSet ) m_xImpl->m_MediaProperties.setVolumeDB( rNewProperties.getVolumeDB() ); if( AVMediaSetMask::ZOOM & nMaskSet ) m_xImpl->m_MediaProperties.setZoom( rNewProperties.getZoom() ); if( bBroadcastChanged ) { SetChanged(); BroadcastObjectChange(); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */