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 <unotools/pathoptions.hxx>
21 #include <vcl/graphicfilter.hxx>
22 #include <sfx2/docfile.hxx>
23 #include <sfx2/filedlghelper.hxx>
24 #include <svx/xoutbmp.hxx>
25 #include <svx/dialmgr.hxx>
26 #include <svx/graphichelper.hxx>
27 #include <svx/strings.hrc>
28 #include <tools/diagnose_ex.h>
29 #include <vcl/svapp.hxx>
30 #include <vcl/weld.hxx>
32 #include <comphelper/processfactory.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>
52 using namespace css::uno
;
53 using namespace css::lang
;
54 using namespace css::graphic
;
55 using namespace css::ucb
;
56 using namespace css::beans
;
57 using namespace css::io
;
58 using namespace css::document
;
59 using namespace css::ui::dialogs
;
60 using namespace css::container
;
61 using namespace com::sun::star::task
;
65 namespace drawing
= com::sun::star::drawing
;
67 void GraphicHelper::GetPreferredExtension( OUString
& rExtension
, const Graphic
& rGraphic
)
69 OUString aExtension
= "png";
70 auto const & rVectorGraphicDataPtr(rGraphic
.getVectorGraphicData());
72 if (rVectorGraphicDataPtr
&& rVectorGraphicDataPtr
->getVectorGraphicDataArrayLength())
74 switch (rVectorGraphicDataPtr
->getVectorGraphicDataType())
76 case VectorGraphicDataType::Wmf
:
79 case VectorGraphicDataType::Emf
:
82 default: // case VectorGraphicDataType::Svg:
87 rExtension
= aExtension
;
91 switch( rGraphic
.GetGfxLink().GetType() )
93 case GfxLinkType::NativeGif
:
96 case GfxLinkType::NativeTif
:
99 case GfxLinkType::NativeWmf
:
102 case GfxLinkType::NativeMet
:
105 case GfxLinkType::NativePct
:
108 case GfxLinkType::NativeJpg
:
111 case GfxLinkType::NativeBmp
:
114 case GfxLinkType::NativeSvg
:
117 case GfxLinkType::NativePdf
:
123 rExtension
= aExtension
;
129 bool lcl_ExecuteFilterDialog( const Sequence
< PropertyValue
>& rPropsForDialog
,
130 Sequence
< PropertyValue
>& rFilterData
)
132 bool bStatus
= false;
135 Reference
< XExecutableDialog
> xFilterDialog(
136 comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.svtools.SvFilterOptionsDialog" ), UNO_QUERY
);
137 Reference
< XPropertyAccess
> xFilterProperties( xFilterDialog
, UNO_QUERY
);
139 if( xFilterDialog
.is() && xFilterProperties
.is() )
141 xFilterProperties
->setPropertyValues( rPropsForDialog
);
142 if( xFilterDialog
->execute() )
145 const Sequence
< PropertyValue
> aPropsFromDialog
= xFilterProperties
->getPropertyValues();
146 for ( const auto& rProp
: aPropsFromDialog
)
148 if (rProp
.Name
== "FilterData")
150 rProp
.Value
>>= rFilterData
;
156 catch( const NoSuchElementException
& e
)
158 // the filter name is unknown
159 throw ErrorCodeIOException(
160 ("lcl_ExecuteFilterDialog: NoSuchElementException"
161 " \"" + e
.Message
+ "\": ERRCODE_IO_ABORT"),
162 Reference
< XInterface
>(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER
));
164 catch( const ErrorCodeIOException
& )
168 catch( const Exception
& )
170 TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
177 OUString
GraphicHelper::ExportGraphic(weld::Window
* pParent
, const Graphic
& rGraphic
, const OUString
& rGraphicName
)
179 SvtPathOptions aPathOpt
;
180 OUString
sGraphicsPath( aPathOpt
.GetGraphicPath() );
182 FileDialogHelper
aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
183 Reference
< XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
186 aPath
.SetSmartURL( sGraphicsPath
);
188 // fish out the graphic's name
190 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE
));
191 aDialogHelper
.SetDisplayDirectory( aPath
.GetMainURL(INetURLObject::DecodeMechanism::ToIUri
) );
193 aURL
.SetSmartURL( rGraphicName
);
194 aDialogHelper
.SetFileName(aURL
.GetLastName());
196 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
197 const sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
199 OUString
aExtension(aURL
.GetFileExtension());
200 if( aExtension
.isEmpty() )
202 GetPreferredExtension( aExtension
, rGraphic
);
205 aExtension
= aExtension
.toAsciiLowerCase();
206 sal_uInt16 nDefaultFilter
= USHRT_MAX
;
208 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
210 xFilePicker
->appendFilter( rGraphicFilter
.GetExportFormatName( i
), rGraphicFilter
.GetExportWildcard( i
) );
211 OUString aFormatShortName
= rGraphicFilter
.GetExportFormatShortName( i
);
212 if ( aFormatShortName
.equalsIgnoreAsciiCase( aExtension
) )
217 if ( USHRT_MAX
== nDefaultFilter
)
219 // "wrong" extension?
220 GetPreferredExtension( aExtension
, rGraphic
);
221 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
222 if ( aExtension
== rGraphicFilter
.GetExportFormatShortName( i
).toAsciiLowerCase() )
229 if( USHRT_MAX
!= nDefaultFilter
)
231 xFilePicker
->setCurrentFilter( rGraphicFilter
.GetExportFormatName( nDefaultFilter
) ) ;
233 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
235 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
236 // remember used path - please don't optimize away!
237 aPath
.SetSmartURL( sPath
);
238 sGraphicsPath
= aPath
.GetPath();
240 if( !rGraphicName
.isEmpty() &&
241 nDefaultFilter
== rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter()))
243 // try to save the original graphic
244 SfxMedium
aIn( rGraphicName
, StreamMode::READ
| StreamMode::NOCREATE
);
245 if( aIn
.GetInStream() && !aIn
.GetInStream()->GetError() )
247 SfxMedium
aOut( sPath
, StreamMode::WRITE
| StreamMode::SHARE_DENYNONE
);
248 if( aOut
.GetOutStream() && !aOut
.GetOutStream()->GetError())
250 aOut
.GetOutStream()->WriteStream( *aIn
.GetInStream() );
251 if ( ERRCODE_NONE
== aIn
.GetError() )
255 if ( ERRCODE_NONE
== aOut
.GetError() )
263 if ( !xFilePicker
->getCurrentFilter().isEmpty() && rGraphicFilter
.GetExportFormatCount() )
265 nFilter
= rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter() );
269 nFilter
= GRFILTER_FORMAT_DONTKNOW
;
271 OUString
aFilter( rGraphicFilter
.GetExportFormatShortName( nFilter
) );
273 if ( rGraphic
.GetType() == GraphicType::Bitmap
)
275 Graphic aGraphic
= rGraphic
;
276 Reference
<XGraphic
> xGraphic
= aGraphic
.GetXGraphic();
278 OUString aExportFilter
= rGraphicFilter
.GetExportInternalFilterName(nFilter
);
280 Sequence
< PropertyValue
> aPropsForDialog(2);
281 aPropsForDialog
[0].Name
= "Graphic";
282 aPropsForDialog
[0].Value
<<= xGraphic
;
283 aPropsForDialog
[1].Name
= "FilterName";
284 aPropsForDialog
[1].Value
<<= aExportFilter
;
286 Sequence
< PropertyValue
> aFilterData
;
287 bool bStatus
= lcl_ExecuteFilterDialog(aPropsForDialog
, aFilterData
);
290 sal_Int32 nWidth
= 0;
291 sal_Int32 nHeight
= 0;
293 for (const auto& rProp
: std::as_const(aFilterData
))
295 if (rProp
.Name
== "PixelWidth")
297 rProp
.Value
>>= nWidth
;
299 else if (rProp
.Name
== "PixelHeight")
301 rProp
.Value
>>= nHeight
;
305 // scaling must performed here because png/jpg writer s
306 // do not take care of that.
307 Size
aSizePixel( aGraphic
.GetSizePixel() );
308 if( nWidth
&& nHeight
&&
309 ( ( nWidth
!= aSizePixel
.Width() ) ||
310 ( nHeight
!= aSizePixel
.Height() ) ) )
312 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
313 // export: use highest quality
314 aBmpEx
.Scale( Size( nWidth
, nHeight
), BmpScaleFlag::Lanczos
);
318 XOutBitmap::WriteGraphic( aGraphic
, sPath
, aFilter
,
319 XOutFlags::DontExpandFilename
|
320 XOutFlags::DontAddExtension
|
321 XOutFlags::UseNativeIfPossible
,
322 nullptr, &aFilterData
);
328 XOutBitmap::WriteGraphic( rGraphic
, sPath
, aFilter
,
329 XOutFlags::DontExpandFilename
|
330 XOutFlags::DontAddExtension
|
331 XOutFlags::UseNativeIfPossible
);
338 void GraphicHelper::SaveShapeAsGraphic(weld::Window
* pParent
, const Reference
< drawing::XShape
>& xShape
)
342 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
343 Reference
< XPropertySet
> xShapeSet( xShape
, UNO_QUERY_THROW
);
345 SvtPathOptions aPathOpt
;
346 const OUString
& sGraphicPath( aPathOpt
.GetGraphicPath() );
348 FileDialogHelper
aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
349 Reference
< XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
351 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE
) );
354 aPath
.SetSmartURL( sGraphicPath
);
355 xFilePicker
->setDisplayDirectory( aPath
.GetMainURL(INetURLObject::DecodeMechanism::ToIUri
) );
357 // populate filter dialog filter list and select default filter to match graphic mime type
359 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
360 const OUString
aDefaultMimeType("image/png");
361 OUString aDefaultFormatName
;
362 sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
364 std::map
< OUString
, OUString
> aMimeTypeMap
;
366 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
368 const OUString
aExportFormatName( rGraphicFilter
.GetExportFormatName( i
) );
369 const OUString
aFilterMimeType( rGraphicFilter
.GetExportFormatMediaType( i
) );
370 xFilePicker
->appendFilter( aExportFormatName
, rGraphicFilter
.GetExportWildcard( i
) );
371 aMimeTypeMap
[ aExportFormatName
] = aFilterMimeType
;
372 if( aDefaultMimeType
== aFilterMimeType
)
373 aDefaultFormatName
= aExportFormatName
;
376 if( !aDefaultFormatName
.isEmpty() )
377 xFilePicker
->setCurrentFilter( aDefaultFormatName
);
381 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
383 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
384 OUString
aExportMimeType( aMimeTypeMap
[xFilePicker
->getCurrentFilter()] );
386 Reference
< XInputStream
> xGraphStream
;
388 if( xGraphStream
.is() )
390 Reference
<XSimpleFileAccess3
> xFileAccess
= SimpleFileAccess::create( xContext
);
391 xFileAccess
->writeFile( sPath
, xGraphStream
);
395 Reference
<css::drawing::XGraphicExportFilter
> xGraphicExporter
= css::drawing::GraphicExportFilter::create( xContext
);
397 Sequence
<PropertyValue
> aDescriptor( 2 );
398 aDescriptor
[0].Name
= "MediaType";
399 aDescriptor
[0].Value
<<= aExportMimeType
;
400 aDescriptor
[1].Name
= "URL";
401 aDescriptor
[1].Value
<<= sPath
;
403 Reference
< XComponent
> xSourceDocument( xShape
, UNO_QUERY_THROW
);
404 xGraphicExporter
->setSourceDocument( xSourceDocument
);
405 xGraphicExporter
->filter( aDescriptor
);
414 short GraphicHelper::HasToSaveTransformedImage(weld::Widget
* pWin
)
416 OUString
aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE
));
417 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
418 VclMessageType::Question
, VclButtonsType::YesNo
, aMsg
));
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */