From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- svx/source/core/extedit.cxx | 204 ++++++++++++++++ svx/source/core/graphichelper.cxx | 485 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 689 insertions(+) create mode 100644 svx/source/core/extedit.cxx create mode 100644 svx/source/core/graphichelper.cxx (limited to 'svx/source/core') diff --git a/svx/source/core/extedit.cxx b/svx/source/core/extedit.cxx new file mode 100644 index 000000000..3225e47f0 --- /dev/null +++ b/svx/source/core/extedit.cxx @@ -0,0 +1,204 @@ +/* -*- 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/. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace css::uno; +using namespace css::system; + +ExternalToolEdit::ExternalToolEdit() +{ +} + +ExternalToolEdit::~ExternalToolEdit() +{ +} + +void ExternalToolEdit::HandleCloseEvent(ExternalToolEdit* pData) +{ + Graphic newGraphic; + + //import the temp file image stream into the newGraphic + std::unique_ptr pStream(utl::UcbStreamHelper::CreateStream(pData->m_aFileName, StreamMode::READ)); + if(pStream) + { + GraphicConverter::Import(*pStream, newGraphic); + + // Now update the Graphic in the shell by re-reading from the newGraphic + pData->Update( newGraphic ); + } +} + +void ExternalToolEdit::StartListeningEvent() +{ + //Start an event listener implemented via VCL timeout + assert(!m_pChecker); + m_pChecker.reset(new FileChangedChecker( + m_aFileName, [this] () { return HandleCloseEvent(this); })); +} + +namespace { + +// self-destructing thread to make shell execute async +class ExternalToolEditThread + : public ::salhelper::Thread +{ +private: + OUString const m_aFileName; + + virtual void execute() override; + +public: + explicit ExternalToolEditThread(OUString const& rFileName) + : ::salhelper::Thread("ExternalToolEdit") + , m_aFileName(rFileName) + {} +}; + +} + +void ExternalToolEditThread::execute() +{ + try + { + Reference const xSystemShellExecute( + SystemShellExecute::create( ::comphelper::getProcessComponentContext())); + xSystemShellExecute->execute(m_aFileName, OUString(), + SystemShellExecuteFlags::URIS_ONLY); + } + catch (Exception const&) + { + TOOLS_WARN_EXCEPTION("svx", "ExternalToolEditThread"); + } +} + +void ExternalToolEdit::Edit(GraphicObject const*const pGraphicObject) +{ + //Get the graphic from the GraphicObject + const Graphic& aGraphic = pGraphicObject->GetGraphic(); + + //get the Preferred File Extension for this graphic + OUString fExtension; + GraphicHelper::GetPreferredExtension(fExtension, aGraphic); + + //Create the temp File + OUString aTempFileBase; + OUString aTempFileName; + + osl::FileBase::RC rc = + osl::FileBase::createTempFile(nullptr, nullptr, &aTempFileBase); + if (osl::FileBase::E_None != rc) + { + SAL_WARN("svx", "ExternalToolEdit::Edit: cannot create temp file"); + return; + } + + // Move it to a file name with image extension properly set + aTempFileName = aTempFileBase + "." + fExtension; + // FIXME: this is pretty stupid, need a better osl temp file API + rc = osl::File::move(aTempFileBase, aTempFileName); + if (osl::FileBase::E_None != rc) + { + SAL_WARN("svx", "ExternalToolEdit::Edit: cannot move temp file"); + return; + } + + //Write Graphic to the Temp File + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + sal_uInt16 nFilter(rGraphicFilter.GetExportFormatNumberForShortName(fExtension)); + + OUString aFilter(rGraphicFilter.GetExportFormatShortName(nFilter)); + + // Write the Graphic to the file now + XOutBitmap::WriteGraphic(aGraphic, aTempFileName, aFilter, XOutFlags::UseNativeIfPossible | XOutFlags::DontExpandFilename); + + // There is a possibility that sPath extension might have been changed if the + // provided extension is not writable + m_aFileName = aTempFileName; + + //Create a thread + + rtl::Reference const pThread( + new ExternalToolEditThread(m_aFileName)); + pThread->launch(); + + StartListeningEvent(); +} + +SdrExternalToolEdit::SdrExternalToolEdit( + FmFormView* pView, + SdrObject* pObj) +: m_pView(pView) + ,m_pObj(pObj) +{ + assert(m_pObj && m_pView); + StartListening(m_pObj->getSdrModelFromSdrObject()); +} + + +void SdrExternalToolEdit::Notify(SfxBroadcaster & rBC, SfxHint const& rHint) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + SdrHint const*const pSdrHint(static_cast(&rHint)); + if (SdrHintKind::ModelCleared == pSdrHint->GetKind() + || (pSdrHint->GetObject() == m_pObj + && SdrHintKind::ObjectRemoved == pSdrHint->GetKind())) + { + m_pView = nullptr; + m_pObj = nullptr; + m_pChecker.reset(); // avoid modifying deleted object + EndListening(rBC); + } +} + +void SdrExternalToolEdit::Update(Graphic & rGraphic) +{ + assert(m_pObj && m_pView); // timer should be deleted by Notify() too + SdrPageView *const pPageView = m_pView->GetSdrPageView(); + if (!pPageView) + return; + + SdrGrafObj *const pNewObj(static_cast(m_pObj->CloneSdrObject(m_pObj->getSdrModelFromSdrObject()))); + assert(pNewObj); + OUString const description = + m_pView->GetDescriptionOfMarkedObjects() + " External Edit"; + m_pView->BegUndo(description); + pNewObj->SetGraphicObject(rGraphic); + // set to new object before ReplaceObjectAtView() so that Notify() will + // not delete the running timer and crash + SdrObject *const pOldObj = m_pObj; + m_pObj = pNewObj; + m_pView->ReplaceObjectAtView(pOldObj, *pPageView, pNewObj); + m_pView->EndUndo(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/core/graphichelper.cxx b/svx/source/core/graphichelper.cxx new file mode 100644 index 000000000..853be47d3 --- /dev/null +++ b/svx/source/core/graphichelper.cxx @@ -0,0 +1,485 @@ +/* -*- 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace css::uno; +using namespace css::lang; +using namespace css::graphic; +using namespace css::ucb; +using namespace css::beans; +using namespace css::io; +using namespace css::document; +using namespace css::ui::dialogs; +using namespace css::container; +using namespace com::sun::star::task; + +using namespace sfx2; + +namespace drawing = com::sun::star::drawing; + +void GraphicHelper::GetPreferredExtension( OUString& rExtension, const Graphic& rGraphic ) +{ + OUString aExtension = "png"; + auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData()); + + if (rVectorGraphicDataPtr && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty()) + { + switch (rVectorGraphicDataPtr->getType()) + { + case VectorGraphicDataType::Wmf: + aExtension = "wmf"; + break; + case VectorGraphicDataType::Emf: + aExtension = "emf"; + break; + default: // case VectorGraphicDataType::Svg: + aExtension = "svg"; + break; + } + + rExtension = aExtension; + return; + } + + switch( rGraphic.GetGfxLink().GetType() ) + { + case GfxLinkType::NativeGif: + aExtension = "gif"; + break; + case GfxLinkType::NativeTif: + aExtension = "tif"; + break; + case GfxLinkType::NativeWmf: + aExtension = "wmf"; + break; + case GfxLinkType::NativeMet: + aExtension = "met"; + break; + case GfxLinkType::NativePct: + aExtension = "pct"; + break; + case GfxLinkType::NativeJpg: + aExtension = "jpg"; + break; + case GfxLinkType::NativeBmp: + aExtension = "bmp"; + break; + case GfxLinkType::NativeSvg: + aExtension = "svg"; + break; + case GfxLinkType::NativePdf: + aExtension = "pdf"; + break; + case GfxLinkType::NativeWebp: + aExtension = "webp"; + break; + default: + break; + } + rExtension = aExtension; +} + +OUString GraphicHelper::GetImageType(const Graphic& rGraphic) +{ + OUString aGraphicTypeString = SvxResId(STR_IMAGE_UNKNOWN); + auto pGfxLink = rGraphic.GetSharedGfxLink(); + if (pGfxLink) + { + switch (pGfxLink->GetType()) + { + case GfxLinkType::NativeGif: + aGraphicTypeString = SvxResId(STR_IMAGE_GIF); + break; + case GfxLinkType::NativeJpg: + aGraphicTypeString = SvxResId(STR_IMAGE_JPEG); + break; + case GfxLinkType::NativePng: + aGraphicTypeString = SvxResId(STR_IMAGE_PNG); + break; + case GfxLinkType::NativeTif: + aGraphicTypeString = SvxResId(STR_IMAGE_TIFF); + break; + case GfxLinkType::NativeWmf: + aGraphicTypeString = SvxResId(STR_IMAGE_WMF); + break; + case GfxLinkType::NativeMet: + aGraphicTypeString = SvxResId(STR_IMAGE_MET); + break; + case GfxLinkType::NativePct: + aGraphicTypeString = SvxResId(STR_IMAGE_PCT); + break; + case GfxLinkType::NativeSvg: + aGraphicTypeString = SvxResId(STR_IMAGE_SVG); + break; + case GfxLinkType::NativeBmp: + aGraphicTypeString = SvxResId(STR_IMAGE_BMP); + break; + case GfxLinkType::NativeWebp: + aGraphicTypeString = SvxResId(STR_IMAGE_WEBP); + break; + default: + break; + } + } + return aGraphicTypeString; +} +namespace { + + +bool lcl_ExecuteFilterDialog( const Sequence< PropertyValue >& rPropsForDialog, + Sequence< PropertyValue >& rFilterData ) +{ + bool bStatus = false; + try + { + Reference< XExecutableDialog > xFilterDialog( + comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.svtools.SvFilterOptionsDialog" ), UNO_QUERY ); + Reference< XPropertyAccess > xFilterProperties( xFilterDialog, UNO_QUERY ); + + if( xFilterDialog.is() && xFilterProperties.is() ) + { + xFilterProperties->setPropertyValues( rPropsForDialog ); + if( xFilterDialog->execute() ) + { + bStatus = true; + const Sequence< PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues(); + for ( const auto& rProp : aPropsFromDialog ) + { + if (rProp.Name == "FilterData") + { + rProp.Value >>= rFilterData; + } + } + } + } + } + catch( const NoSuchElementException& e ) + { + // the filter name is unknown + throw ErrorCodeIOException( + ("lcl_ExecuteFilterDialog: NoSuchElementException" + " \"" + e.Message + "\": ERRCODE_IO_ABORT"), + Reference< XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER)); + } + catch( const ErrorCodeIOException& ) + { + throw; + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring"); + } + + return bStatus; +} +} // anonymous ns + +OUString GraphicHelper::ExportGraphic(weld::Window* pParent, const Graphic& rGraphic, const OUString& rGraphicName) +{ + FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent); + Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker(); + + // fish out the graphic's name + aDialogHelper.SetContext(FileDialogHelper::ExportImage); + aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE)); + INetURLObject aURL; + aURL.SetSmartURL( rGraphicName ); + aDialogHelper.SetFileName(aURL.GetLastName()); + + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + const sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount(); + + OUString aExtension(aURL.GetFileExtension()); + if( aExtension.isEmpty() ) + { + GetPreferredExtension( aExtension, rGraphic ); + } + + aExtension = aExtension.toAsciiLowerCase(); + sal_uInt16 nDefaultFilter = USHRT_MAX; + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + xFilePicker->appendFilter( rGraphicFilter.GetExportFormatName( i ), rGraphicFilter.GetExportWildcard( i ) ); + OUString aFormatShortName = rGraphicFilter.GetExportFormatShortName( i ); + if ( aFormatShortName.equalsIgnoreAsciiCase( aExtension ) ) + { + nDefaultFilter = i; + } + } + if ( USHRT_MAX == nDefaultFilter ) + { + // "wrong" extension? + GetPreferredExtension( aExtension, rGraphic ); + for ( sal_uInt16 i = 0; i < nCount; ++i ) + if ( aExtension == rGraphicFilter.GetExportFormatShortName( i ).toAsciiLowerCase() ) + { + nDefaultFilter = i; + break; + } + } + + if( USHRT_MAX != nDefaultFilter ) + { + xFilePicker->setCurrentFilter( rGraphicFilter.GetExportFormatName( nDefaultFilter ) ) ; + + if( aDialogHelper.Execute() == ERRCODE_NONE ) + { + OUString sPath( xFilePicker->getFiles().getConstArray()[0] ); + if( !rGraphicName.isEmpty() && + nDefaultFilter == rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter())) + { + // try to save the original graphic + SfxMedium aIn( rGraphicName, StreamMode::READ | StreamMode::NOCREATE ); + if( aIn.GetInStream() && !aIn.GetInStream()->GetError() ) + { + SfxMedium aOut( sPath, StreamMode::WRITE | StreamMode::SHARE_DENYNONE); + if( aOut.GetOutStream() && !aOut.GetOutStream()->GetError()) + { + aOut.GetOutStream()->WriteStream( *aIn.GetInStream() ); + if ( ERRCODE_NONE == aIn.GetError() ) + { + aOut.Close(); + aOut.Commit(); + if ( ERRCODE_NONE == aOut.GetError() ) + return sPath; + } + } + } + } + + sal_uInt16 nFilter; + if ( !xFilePicker->getCurrentFilter().isEmpty() && rGraphicFilter.GetExportFormatCount() ) + { + nFilter = rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter() ); + } + else + { + nFilter = GRFILTER_FORMAT_DONTKNOW; + } + OUString aFilter( rGraphicFilter.GetExportFormatShortName( nFilter ) ); + + if ( rGraphic.GetType() == GraphicType::Bitmap ) + { + Graphic aGraphic = rGraphic; + Reference xGraphic = aGraphic.GetXGraphic(); + + OUString aExportFilter = rGraphicFilter.GetExportInternalFilterName(nFilter); + + Sequence< PropertyValue > aPropsForDialog{ + comphelper::makePropertyValue("Graphic", xGraphic), + comphelper::makePropertyValue("FilterName", aExportFilter) + }; + + Sequence< PropertyValue > aFilterData; + bool bStatus = lcl_ExecuteFilterDialog(aPropsForDialog, aFilterData); + if (bStatus) + { + sal_Int32 nWidth = 0; + sal_Int32 nHeight = 0; + + for (const auto& rProp : std::as_const(aFilterData)) + { + if (rProp.Name == "PixelWidth") + { + rProp.Value >>= nWidth; + } + else if (rProp.Name == "PixelHeight") + { + rProp.Value >>= nHeight; + } + } + + // scaling must performed here because png/jpg writer s + // do not take care of that. + Size aSizePixel( aGraphic.GetSizePixel() ); + if( nWidth && nHeight && + ( ( nWidth != aSizePixel.Width() ) || + ( nHeight != aSizePixel.Height() ) ) ) + { + BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); + // export: use highest quality + aBmpEx.Scale( Size( nWidth, nHeight ), BmpScaleFlag::Lanczos ); + aGraphic = aBmpEx; + } + + XOutBitmap::WriteGraphic( aGraphic, sPath, aFilter, + XOutFlags::DontExpandFilename | + XOutFlags::DontAddExtension | + XOutFlags::UseNativeIfPossible, + nullptr, &aFilterData ); + return sPath; + } + } + else + { + XOutBitmap::WriteGraphic( rGraphic, sPath, aFilter, + XOutFlags::DontExpandFilename | + XOutFlags::DontAddExtension | + XOutFlags::UseNativeIfPossible ); + } + } + } + return OUString(); +} + +void GraphicHelper::SaveShapeAsGraphicToPath( + const css::uno::Reference& xComponent, + const css::uno::Reference& xShape, const OUString& aExportMimeType, + const OUString& sPath) +{ + Reference xContext(::comphelper::getProcessComponentContext()); + Reference xGraphStream; + + if (xGraphStream.is()) + { + Reference xFileAccess = SimpleFileAccess::create(xContext); + xFileAccess->writeFile(sPath, xGraphStream); + } + else if (xComponent.is() && aExportMimeType == "application/pdf") + { + css::uno::Reference xMSF(xContext->getServiceManager(), + css::uno::UNO_QUERY); + css::uno::Reference xExporter( + xMSF->createInstance("com.sun.star.comp.PDF.PDFFilter"), css::uno::UNO_QUERY); + xExporter->setSourceDocument(xComponent); + + css::uno::Reference xShapes + = css::drawing::ShapeCollection::create(comphelper::getProcessComponentContext()); + xShapes->add(xShape); + css::uno::Sequence aFilterData{ + comphelper::makePropertyValue("Selection", xShapes), + }; + SvFileStream aStream(sPath, StreamMode::READWRITE | StreamMode::TRUNC); + css::uno::Reference xStream(new utl::OStreamWrapper(aStream)); + css::uno::Sequence aDescriptor{ + comphelper::makePropertyValue("FilterData", aFilterData), + comphelper::makePropertyValue("OutputStream", xStream) + }; + css::uno::Reference xFilter(xExporter, css::uno::UNO_QUERY); + xFilter->filter(aDescriptor); + } + else + { + Reference xGraphicExporter + = css::drawing::GraphicExportFilter::create(xContext); + + Sequence aDescriptor{ comphelper::makePropertyValue("MediaType", + aExportMimeType), + comphelper::makePropertyValue("URL", sPath) }; + + Reference xSourceDocument(xShape, UNO_QUERY_THROW); + xGraphicExporter->setSourceDocument(xSourceDocument); + xGraphicExporter->filter(aDescriptor); + } +} + +void GraphicHelper::SaveShapeAsGraphic(weld::Window* pParent, + const css::uno::Reference& xComponent, + const Reference& xShape) +{ + try + { + Reference< XPropertySet > xShapeSet( xShape, UNO_QUERY_THROW ); + + FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent); + Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker(); + aDialogHelper.SetContext(FileDialogHelper::ExportImage); + aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE) ); + + // populate filter dialog filter list and select default filter to match graphic mime type + + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + static const OUStringLiteral aDefaultMimeType(u"image/png"); + OUString aDefaultFormatName; + sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount(); + + std::map< OUString, OUString > aMimeTypeMap; + + for ( sal_uInt16 i = 0; i < nCount; i++ ) + { + const OUString aExportFormatName( rGraphicFilter.GetExportFormatName( i ) ); + const OUString aFilterMimeType( rGraphicFilter.GetExportFormatMediaType( i ) ); + xFilePicker->appendFilter( aExportFormatName, rGraphicFilter.GetExportWildcard( i ) ); + aMimeTypeMap[ aExportFormatName ] = aFilterMimeType; + if( aDefaultMimeType == aFilterMimeType ) + aDefaultFormatName = aExportFormatName; + } + + if( !aDefaultFormatName.isEmpty() ) + xFilePicker->setCurrentFilter( aDefaultFormatName ); + + // execute dialog + + if( aDialogHelper.Execute() == ERRCODE_NONE ) + { + OUString sPath( xFilePicker->getFiles().getConstArray()[0] ); + OUString aExportMimeType( aMimeTypeMap[xFilePicker->getCurrentFilter()] ); + + GraphicHelper::SaveShapeAsGraphicToPath(xComponent, xShape, aExportMimeType, sPath); + } + } + catch( Exception& ) + { + } +} + +short GraphicHelper::HasToSaveTransformedImage(weld::Widget* pWin) +{ + OUString aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE)); + std::unique_ptr xBox(Application::CreateMessageDialog(pWin, + VclMessageType::Question, VclButtonsType::YesNo, aMsg)); + return xBox->run(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3