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 <vcl/graphicfilter.hxx>
21 #include <sfx2/docfile.hxx>
22 #include <sfx2/filedlghelper.hxx>
23 #include <svx/xoutbmp.hxx>
24 #include <svx/dialmgr.hxx>
25 #include <svx/graphichelper.hxx>
26 #include <svx/strings.hrc>
27 #include <comphelper/diagnose_ex.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/weld.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/propertyvalue.hxx>
34 #include <com/sun/star/beans/XPropertySet.hpp>
35 #include <com/sun/star/beans/PropertyValue.hpp>
36 #include <com/sun/star/container/NoSuchElementException.hpp>
37 #include <com/sun/star/document/XExporter.hpp>
38 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
39 #include <com/sun/star/drawing/XShape.hpp>
40 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
41 #include <com/sun/star/lang/XComponent.hpp>
42 #include <com/sun/star/io/XInputStream.hpp>
43 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
44 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
45 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
46 #include <com/sun/star/beans/XPropertyAccess.hpp>
47 #include <com/sun/star/task/ErrorCodeIOException.hpp>
48 #include <com/sun/star/graphic/XGraphic.hpp>
49 #include <com/sun/star/drawing/ShapeCollection.hpp>
53 #include <unotools/streamwrap.hxx>
55 using namespace css::uno
;
56 using namespace css::lang
;
57 using namespace css::graphic
;
58 using namespace css::ucb
;
59 using namespace css::beans
;
60 using namespace css::io
;
61 using namespace css::document
;
62 using namespace css::ui::dialogs
;
63 using namespace css::container
;
64 using namespace com::sun::star::task
;
68 namespace drawing
= com::sun::star::drawing
;
70 void GraphicHelper::GetPreferredExtension( OUString
& rExtension
, const Graphic
& rGraphic
)
72 OUString aExtension
= "png";
73 auto const & rVectorGraphicDataPtr(rGraphic
.getVectorGraphicData());
75 if (rVectorGraphicDataPtr
&& !rVectorGraphicDataPtr
->getBinaryDataContainer().isEmpty())
77 switch (rVectorGraphicDataPtr
->getType())
79 case VectorGraphicDataType::Wmf
:
82 case VectorGraphicDataType::Emf
:
85 default: // case VectorGraphicDataType::Svg:
90 rExtension
= aExtension
;
94 switch( rGraphic
.GetGfxLink().GetType() )
96 case GfxLinkType::NativeGif
:
99 case GfxLinkType::NativeTif
:
102 case GfxLinkType::NativeWmf
:
105 case GfxLinkType::NativeMet
:
108 case GfxLinkType::NativePct
:
111 case GfxLinkType::NativeJpg
:
114 case GfxLinkType::NativeBmp
:
117 case GfxLinkType::NativeSvg
:
120 case GfxLinkType::NativePdf
:
123 case GfxLinkType::NativeWebp
:
129 rExtension
= aExtension
;
132 OUString
GraphicHelper::GetImageType(const Graphic
& rGraphic
)
134 OUString aGraphicTypeString
= SvxResId(STR_IMAGE_UNKNOWN
);
135 auto pGfxLink
= rGraphic
.GetSharedGfxLink();
138 switch (pGfxLink
->GetType())
140 case GfxLinkType::NativeGif
:
141 aGraphicTypeString
= SvxResId(STR_IMAGE_GIF
);
143 case GfxLinkType::NativeJpg
:
144 aGraphicTypeString
= SvxResId(STR_IMAGE_JPEG
);
146 case GfxLinkType::NativePng
:
147 aGraphicTypeString
= SvxResId(STR_IMAGE_PNG
);
149 case GfxLinkType::NativeTif
:
150 aGraphicTypeString
= SvxResId(STR_IMAGE_TIFF
);
152 case GfxLinkType::NativeWmf
:
153 aGraphicTypeString
= SvxResId(STR_IMAGE_WMF
);
155 case GfxLinkType::NativeMet
:
156 aGraphicTypeString
= SvxResId(STR_IMAGE_MET
);
158 case GfxLinkType::NativePct
:
159 aGraphicTypeString
= SvxResId(STR_IMAGE_PCT
);
161 case GfxLinkType::NativeSvg
:
162 aGraphicTypeString
= SvxResId(STR_IMAGE_SVG
);
164 case GfxLinkType::NativeBmp
:
165 aGraphicTypeString
= SvxResId(STR_IMAGE_BMP
);
167 case GfxLinkType::NativeWebp
:
168 aGraphicTypeString
= SvxResId(STR_IMAGE_WEBP
);
174 return aGraphicTypeString
;
179 bool lcl_ExecuteFilterDialog( const Sequence
< PropertyValue
>& rPropsForDialog
,
180 Sequence
< PropertyValue
>& rFilterData
)
182 bool bStatus
= false;
185 Reference
< XExecutableDialog
> xFilterDialog(
186 comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.svtools.SvFilterOptionsDialog" ), UNO_QUERY
);
187 Reference
< XPropertyAccess
> xFilterProperties( xFilterDialog
, UNO_QUERY
);
189 if( xFilterDialog
.is() && xFilterProperties
.is() )
191 xFilterProperties
->setPropertyValues( rPropsForDialog
);
192 if( xFilterDialog
->execute() )
195 const Sequence
< PropertyValue
> aPropsFromDialog
= xFilterProperties
->getPropertyValues();
196 for ( const auto& rProp
: aPropsFromDialog
)
198 if (rProp
.Name
== "FilterData")
200 rProp
.Value
>>= rFilterData
;
206 catch( const NoSuchElementException
& e
)
208 // the filter name is unknown
209 throw ErrorCodeIOException(
210 ("lcl_ExecuteFilterDialog: NoSuchElementException"
211 " \"" + e
.Message
+ "\": ERRCODE_IO_ABORT"),
212 Reference
< XInterface
>(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER
));
214 catch( const ErrorCodeIOException
& )
218 catch( const Exception
& )
220 TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
227 OUString
GraphicHelper::ExportGraphic(weld::Window
* pParent
, const Graphic
& rGraphic
, const OUString
& rGraphicName
)
229 FileDialogHelper
aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
230 Reference
< XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
232 // fish out the graphic's name
233 aDialogHelper
.SetContext(FileDialogHelper::ExportImage
);
234 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE
));
236 aURL
.SetSmartURL( rGraphicName
);
237 aDialogHelper
.SetFileName(aURL
.GetLastName());
239 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
240 const sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
242 OUString
aExtension(aURL
.GetFileExtension());
243 if( aExtension
.isEmpty() )
245 GetPreferredExtension( aExtension
, rGraphic
);
248 aExtension
= aExtension
.toAsciiLowerCase();
249 sal_uInt16 nDefaultFilter
= USHRT_MAX
;
251 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
253 xFilePicker
->appendFilter( rGraphicFilter
.GetExportFormatName( i
), rGraphicFilter
.GetExportWildcard( i
) );
254 OUString aFormatShortName
= rGraphicFilter
.GetExportFormatShortName( i
);
255 if ( aFormatShortName
.equalsIgnoreAsciiCase( aExtension
) )
260 if ( USHRT_MAX
== nDefaultFilter
)
262 // "wrong" extension?
263 GetPreferredExtension( aExtension
, rGraphic
);
264 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
265 if ( aExtension
== rGraphicFilter
.GetExportFormatShortName( i
).toAsciiLowerCase() )
272 if( USHRT_MAX
!= nDefaultFilter
)
274 xFilePicker
->setCurrentFilter( rGraphicFilter
.GetExportFormatName( nDefaultFilter
) ) ;
276 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
278 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
279 if( !rGraphicName
.isEmpty() &&
280 nDefaultFilter
== rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter()))
282 // try to save the original graphic
283 SfxMedium
aIn( rGraphicName
, StreamMode::READ
| StreamMode::NOCREATE
);
284 if( aIn
.GetInStream() && !aIn
.GetInStream()->GetError() )
286 SfxMedium
aOut( sPath
, StreamMode::WRITE
| StreamMode::SHARE_DENYNONE
);
287 if( aOut
.GetOutStream() && !aOut
.GetOutStream()->GetError())
289 aOut
.GetOutStream()->WriteStream( *aIn
.GetInStream() );
290 if ( ERRCODE_NONE
== aIn
.GetError() )
294 if ( ERRCODE_NONE
== aOut
.GetError() )
302 if ( !xFilePicker
->getCurrentFilter().isEmpty() && rGraphicFilter
.GetExportFormatCount() )
304 nFilter
= rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter() );
308 nFilter
= GRFILTER_FORMAT_DONTKNOW
;
310 OUString
aFilter( rGraphicFilter
.GetExportFormatShortName( nFilter
) );
312 if ( rGraphic
.GetType() == GraphicType::Bitmap
)
314 Graphic aGraphic
= rGraphic
;
315 Reference
<XGraphic
> xGraphic
= aGraphic
.GetXGraphic();
317 OUString aExportFilter
= rGraphicFilter
.GetExportInternalFilterName(nFilter
);
319 Sequence
< PropertyValue
> aPropsForDialog
{
320 comphelper::makePropertyValue("Graphic", xGraphic
),
321 comphelper::makePropertyValue("FilterName", aExportFilter
)
324 Sequence
< PropertyValue
> aFilterData
;
325 bool bStatus
= lcl_ExecuteFilterDialog(aPropsForDialog
, aFilterData
);
328 sal_Int32 nWidth
= 0;
329 sal_Int32 nHeight
= 0;
331 for (const auto& rProp
: std::as_const(aFilterData
))
333 if (rProp
.Name
== "PixelWidth")
335 rProp
.Value
>>= nWidth
;
337 else if (rProp
.Name
== "PixelHeight")
339 rProp
.Value
>>= nHeight
;
343 // scaling must performed here because png/jpg writer s
344 // do not take care of that.
345 Size
aSizePixel( aGraphic
.GetSizePixel() );
346 if( nWidth
&& nHeight
&&
347 ( ( nWidth
!= aSizePixel
.Width() ) ||
348 ( nHeight
!= aSizePixel
.Height() ) ) )
350 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
351 // export: use highest quality
352 aBmpEx
.Scale( Size( nWidth
, nHeight
), BmpScaleFlag::Lanczos
);
356 XOutBitmap::WriteGraphic( aGraphic
, sPath
, aFilter
,
357 XOutFlags::DontExpandFilename
|
358 XOutFlags::DontAddExtension
|
359 XOutFlags::UseNativeIfPossible
,
360 nullptr, &aFilterData
);
366 XOutBitmap::WriteGraphic( rGraphic
, sPath
, aFilter
,
367 XOutFlags::DontExpandFilename
|
368 XOutFlags::DontAddExtension
|
369 XOutFlags::UseNativeIfPossible
);
376 void GraphicHelper::SaveShapeAsGraphicToPath(
377 const css::uno::Reference
<css::lang::XComponent
>& xComponent
,
378 const css::uno::Reference
<css::drawing::XShape
>& xShape
, const OUString
& aExportMimeType
,
379 const OUString
& sPath
)
381 Reference
<XComponentContext
> xContext(::comphelper::getProcessComponentContext());
382 Reference
<XInputStream
> xGraphStream
;
384 if (xGraphStream
.is())
386 Reference
<XSimpleFileAccess3
> xFileAccess
= SimpleFileAccess::create(xContext
);
387 xFileAccess
->writeFile(sPath
, xGraphStream
);
389 else if (xComponent
.is() && aExportMimeType
== "application/pdf")
391 css::uno::Reference
<css::lang::XMultiServiceFactory
> xMSF(xContext
->getServiceManager(),
392 css::uno::UNO_QUERY
);
393 css::uno::Reference
<css::document::XExporter
> xExporter(
394 xMSF
->createInstance("com.sun.star.comp.PDF.PDFFilter"), css::uno::UNO_QUERY
);
395 xExporter
->setSourceDocument(xComponent
);
397 css::uno::Reference
<css::drawing::XShapes
> xShapes
398 = css::drawing::ShapeCollection::create(comphelper::getProcessComponentContext());
399 xShapes
->add(xShape
);
400 css::uno::Sequence
<PropertyValue
> aFilterData
{
401 comphelper::makePropertyValue("Selection", xShapes
),
403 SvFileStream
aStream(sPath
, StreamMode::READWRITE
| StreamMode::TRUNC
);
404 css::uno::Reference
<css::io::XOutputStream
> xStream(new utl::OStreamWrapper(aStream
));
405 css::uno::Sequence
<PropertyValue
> aDescriptor
{
406 comphelper::makePropertyValue("FilterData", aFilterData
),
407 comphelper::makePropertyValue("OutputStream", xStream
)
409 css::uno::Reference
<css::document::XFilter
> xFilter(xExporter
, css::uno::UNO_QUERY
);
410 xFilter
->filter(aDescriptor
);
414 Reference
<css::drawing::XGraphicExportFilter
> xGraphicExporter
415 = css::drawing::GraphicExportFilter::create(xContext
);
417 Sequence
<PropertyValue
> aDescriptor
{ comphelper::makePropertyValue("MediaType",
419 comphelper::makePropertyValue("URL", sPath
) };
421 Reference
<XComponent
> xSourceDocument(xShape
, UNO_QUERY_THROW
);
422 xGraphicExporter
->setSourceDocument(xSourceDocument
);
423 xGraphicExporter
->filter(aDescriptor
);
427 void GraphicHelper::SaveShapeAsGraphic(weld::Window
* pParent
,
428 const css::uno::Reference
<css::lang::XComponent
>& xComponent
,
429 const Reference
<drawing::XShape
>& xShape
)
433 Reference
< XPropertySet
> xShapeSet( xShape
, UNO_QUERY_THROW
);
435 FileDialogHelper
aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
436 Reference
< XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
437 aDialogHelper
.SetContext(FileDialogHelper::ExportImage
);
438 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE
) );
440 // populate filter dialog filter list and select default filter to match graphic mime type
442 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
443 static const OUStringLiteral
aDefaultMimeType(u
"image/png");
444 OUString aDefaultFormatName
;
445 sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
447 std::map
< OUString
, OUString
> aMimeTypeMap
;
449 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
451 const OUString
aExportFormatName( rGraphicFilter
.GetExportFormatName( i
) );
452 const OUString
aFilterMimeType( rGraphicFilter
.GetExportFormatMediaType( i
) );
453 xFilePicker
->appendFilter( aExportFormatName
, rGraphicFilter
.GetExportWildcard( i
) );
454 aMimeTypeMap
[ aExportFormatName
] = aFilterMimeType
;
455 if( aDefaultMimeType
== aFilterMimeType
)
456 aDefaultFormatName
= aExportFormatName
;
459 if( !aDefaultFormatName
.isEmpty() )
460 xFilePicker
->setCurrentFilter( aDefaultFormatName
);
464 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
466 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
467 OUString
aExportMimeType( aMimeTypeMap
[xFilePicker
->getCurrentFilter()] );
469 GraphicHelper::SaveShapeAsGraphicToPath(xComponent
, xShape
, aExportMimeType
, sPath
);
477 short GraphicHelper::HasToSaveTransformedImage(weld::Widget
* pWin
)
479 OUString
aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE
));
480 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
481 VclMessageType::Question
, VclButtonsType::YesNo
, aMsg
));
485 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */