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 <sal/log.hxx>
29 #include <tools/diagnose_ex.h>
30 #include <vcl/svapp.hxx>
31 #include <vcl/weld.hxx>
33 #include <cppuhelper/exc_hlp.hxx>
34 #include <comphelper/processfactory.hxx>
36 #include <com/sun/star/beans/XPropertySet.hpp>
37 #include <com/sun/star/beans/PropertyValues.hpp>
38 #include <com/sun/star/beans/PropertyValue.hpp>
39 #include <com/sun/star/document/XExporter.hpp>
40 #include <com/sun/star/document/XFilter.hpp>
41 #include <com/sun/star/drawing/GraphicExportFilter.hpp>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/lang/XComponent.hpp>
44 #include <com/sun/star/io/XInputStream.hpp>
45 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
46 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
47 #include <com/sun/star/ui/dialogs/XFilterManager.hpp>
48 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
49 #include <com/sun/star/container/XNameAccess.hpp>
50 #include <com/sun/star/beans/XPropertyAccess.hpp>
51 #include <com/sun/star/task/ErrorCodeIOException.hpp>
52 #include <com/sun/star/task/InteractionHandler.hpp>
53 #include <com/sun/star/graphic/XGraphic.hpp>
57 using namespace css::uno
;
58 using namespace css::lang
;
59 using namespace css::graphic
;
60 using namespace css::ucb
;
61 using namespace css::beans
;
62 using namespace css::io
;
63 using namespace css::document
;
64 using namespace css::ui::dialogs
;
65 using namespace css::container
;
66 using namespace com::sun::star::task
;
67 using namespace css::frame
;
71 namespace drawing
= com::sun::star::drawing
;
73 void GraphicHelper::GetPreferredExtension( OUString
& rExtension
, const Graphic
& rGraphic
)
75 OUString aExtension
= "png";
76 const VectorGraphicDataPtr
& aVectorGraphicDataPtr(rGraphic
.getVectorGraphicData());
78 if (aVectorGraphicDataPtr
.get() && aVectorGraphicDataPtr
->getVectorGraphicDataArrayLength())
80 switch (aVectorGraphicDataPtr
->getVectorGraphicDataType())
82 case VectorGraphicDataType::Wmf
:
85 case VectorGraphicDataType::Emf
:
88 default: // case VectorGraphicDataType::Svg:
93 rExtension
= aExtension
;
97 switch( rGraphic
.GetGfxLink().GetType() )
99 case GfxLinkType::NativeGif
:
102 case GfxLinkType::NativeTif
:
105 case GfxLinkType::NativeWmf
:
108 case GfxLinkType::NativeMet
:
111 case GfxLinkType::NativePct
:
114 case GfxLinkType::NativeJpg
:
117 case GfxLinkType::NativeBmp
:
120 case GfxLinkType::NativeSvg
:
123 case GfxLinkType::NativePdf
:
129 rExtension
= aExtension
;
135 bool lcl_ExecuteFilterDialog( const Sequence
< PropertyValue
>& rPropsForDialog
,
136 Sequence
< PropertyValue
>& rFilterData
)
138 bool bStatus
= false;
141 const OUString
aServiceName("com.sun.star.svtools.SvFilterOptionsDialog");
142 Reference
< XExecutableDialog
> xFilterDialog(
143 comphelper::getProcessServiceFactory()->createInstance( aServiceName
), UNO_QUERY
);
144 Reference
< XPropertyAccess
> xFilterProperties( xFilterDialog
, UNO_QUERY
);
146 if( xFilterDialog
.is() && xFilterProperties
.is() )
148 xFilterProperties
->setPropertyValues( rPropsForDialog
);
149 if( xFilterDialog
->execute() )
152 const Sequence
< PropertyValue
> aPropsFromDialog
= xFilterProperties
->getPropertyValues();
153 for ( const auto& rProp
: aPropsFromDialog
)
155 if (rProp
.Name
== "FilterData")
157 rProp
.Value
>>= rFilterData
;
163 catch( const NoSuchElementException
& e
)
165 // the filter name is unknown
166 throw ErrorCodeIOException(
167 ("lcl_ExecuteFilterDialog: NoSuchElementException"
168 " \"" + e
.Message
+ "\": ERRCODE_IO_ABORT"),
169 Reference
< XInterface
>(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER
));
171 catch( const ErrorCodeIOException
& )
175 catch( const Exception
& )
177 TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
184 OUString
GraphicHelper::ExportGraphic(weld::Window
* pParent
, const Graphic
& rGraphic
, const OUString
& rGraphicName
)
186 SvtPathOptions aPathOpt
;
187 OUString
sGraphicsPath( aPathOpt
.GetGraphicPath() );
189 FileDialogHelper
aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
190 Reference
< XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
193 aPath
.SetSmartURL( sGraphicsPath
);
195 // fish out the graphic's name
197 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE
));
198 aDialogHelper
.SetDisplayDirectory( aPath
.GetMainURL(INetURLObject::DecodeMechanism::ToIUri
) );
200 aURL
.SetSmartURL( rGraphicName
);
201 aDialogHelper
.SetFileName(aURL
.GetLastName());
203 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
204 const sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
206 OUString
aExtension(aURL
.GetFileExtension());
207 if( aExtension
.isEmpty() )
209 GetPreferredExtension( aExtension
, rGraphic
);
212 aExtension
= aExtension
.toAsciiLowerCase();
213 sal_uInt16 nDefaultFilter
= USHRT_MAX
;
215 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
217 xFilePicker
->appendFilter( rGraphicFilter
.GetExportFormatName( i
), rGraphicFilter
.GetExportWildcard( i
) );
218 OUString aFormatShortName
= rGraphicFilter
.GetExportFormatShortName( i
);
219 if ( aFormatShortName
.equalsIgnoreAsciiCase( aExtension
) )
224 if ( USHRT_MAX
== nDefaultFilter
)
226 // "wrong" extension?
227 GetPreferredExtension( aExtension
, rGraphic
);
228 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
229 if ( aExtension
== rGraphicFilter
.GetExportFormatShortName( i
).toAsciiLowerCase() )
236 if( USHRT_MAX
!= nDefaultFilter
)
238 xFilePicker
->setCurrentFilter( rGraphicFilter
.GetExportFormatName( nDefaultFilter
) ) ;
240 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
242 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
243 // remember used path - please don't optimize away!
244 aPath
.SetSmartURL( sPath
);
245 sGraphicsPath
= aPath
.GetPath();
247 if( !rGraphicName
.isEmpty() &&
248 nDefaultFilter
== rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter()))
250 // try to save the original graphic
251 SfxMedium
aIn( rGraphicName
, StreamMode::READ
| StreamMode::NOCREATE
);
252 if( aIn
.GetInStream() && !aIn
.GetInStream()->GetError() )
254 SfxMedium
aOut( sPath
, StreamMode::WRITE
| StreamMode::SHARE_DENYNONE
);
255 if( aOut
.GetOutStream() && !aOut
.GetOutStream()->GetError())
257 aOut
.GetOutStream()->WriteStream( *aIn
.GetInStream() );
258 if ( ERRCODE_NONE
== aIn
.GetError() )
262 if ( ERRCODE_NONE
== aOut
.GetError() )
270 if ( !xFilePicker
->getCurrentFilter().isEmpty() && rGraphicFilter
.GetExportFormatCount() )
272 nFilter
= rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter() );
276 nFilter
= GRFILTER_FORMAT_DONTKNOW
;
278 OUString
aFilter( rGraphicFilter
.GetExportFormatShortName( nFilter
) );
280 if ( rGraphic
.GetType() == GraphicType::Bitmap
)
282 Graphic aGraphic
= rGraphic
;
283 Reference
<XGraphic
> xGraphic
= aGraphic
.GetXGraphic();
285 OUString aExportFilter
= rGraphicFilter
.GetExportInternalFilterName(nFilter
);
287 Sequence
< PropertyValue
> aPropsForDialog(2);
288 aPropsForDialog
[0].Name
= "Graphic";
289 aPropsForDialog
[0].Value
<<= xGraphic
;
290 aPropsForDialog
[1].Name
= "FilterName";
291 aPropsForDialog
[1].Value
<<= aExportFilter
;
293 Sequence
< PropertyValue
> aFilterData
;
294 bool bStatus
= lcl_ExecuteFilterDialog(aPropsForDialog
, aFilterData
);
297 sal_Int32 nWidth
= 0;
298 sal_Int32 nHeight
= 0;
300 for (const auto& rProp
: std::as_const(aFilterData
))
302 if (rProp
.Name
== "PixelWidth")
304 rProp
.Value
>>= nWidth
;
306 else if (rProp
.Name
== "PixelHeight")
308 rProp
.Value
>>= nHeight
;
312 // scaling must performed here because png/jpg writer s
313 // do not take care of that.
314 Size
aSizePixel( aGraphic
.GetSizePixel() );
315 if( nWidth
&& nHeight
&&
316 ( ( nWidth
!= aSizePixel
.Width() ) ||
317 ( nHeight
!= aSizePixel
.Height() ) ) )
319 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
320 // export: use highest quality
321 aBmpEx
.Scale( Size( nWidth
, nHeight
), BmpScaleFlag::Lanczos
);
325 XOutBitmap::WriteGraphic( aGraphic
, sPath
, aFilter
,
326 XOutFlags::DontExpandFilename
|
327 XOutFlags::DontAddExtension
|
328 XOutFlags::UseNativeIfPossible
,
329 nullptr, &aFilterData
);
335 XOutBitmap::WriteGraphic( rGraphic
, sPath
, aFilter
,
336 XOutFlags::DontExpandFilename
|
337 XOutFlags::DontAddExtension
|
338 XOutFlags::UseNativeIfPossible
);
345 void GraphicHelper::SaveShapeAsGraphic(weld::Window
* pParent
, const Reference
< drawing::XShape
>& xShape
)
349 Reference
< XComponentContext
> xContext( ::comphelper::getProcessComponentContext() );
350 Reference
< XPropertySet
> xShapeSet( xShape
, UNO_QUERY_THROW
);
352 SvtPathOptions aPathOpt
;
353 const OUString
& sGraphicPath( aPathOpt
.GetGraphicPath() );
355 FileDialogHelper
aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
356 Reference
< XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
358 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE
) );
361 aPath
.SetSmartURL( sGraphicPath
);
362 xFilePicker
->setDisplayDirectory( aPath
.GetMainURL(INetURLObject::DecodeMechanism::ToIUri
) );
364 // populate filter dialog filter list and select default filter to match graphic mime type
366 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
367 const OUString
aDefaultMimeType("image/png");
368 OUString aDefaultFormatName
;
369 sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
371 std::map
< OUString
, OUString
> aMimeTypeMap
;
373 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
375 const OUString
aExportFormatName( rGraphicFilter
.GetExportFormatName( i
) );
376 const OUString
aFilterMimeType( rGraphicFilter
.GetExportFormatMediaType( i
) );
377 xFilePicker
->appendFilter( aExportFormatName
, rGraphicFilter
.GetExportWildcard( i
) );
378 aMimeTypeMap
[ aExportFormatName
] = aFilterMimeType
;
379 if( aDefaultMimeType
== aFilterMimeType
)
380 aDefaultFormatName
= aExportFormatName
;
383 if( !aDefaultFormatName
.isEmpty() )
384 xFilePicker
->setCurrentFilter( aDefaultFormatName
);
388 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
390 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
391 OUString
aExportMimeType( aMimeTypeMap
[xFilePicker
->getCurrentFilter()] );
393 Reference
< XInputStream
> xGraphStream
;
395 if( xGraphStream
.is() )
397 Reference
<XSimpleFileAccess3
> xFileAccess
= SimpleFileAccess::create( xContext
);
398 xFileAccess
->writeFile( sPath
, xGraphStream
);
402 Reference
<css::drawing::XGraphicExportFilter
> xGraphicExporter
= css::drawing::GraphicExportFilter::create( xContext
);
404 Sequence
<PropertyValue
> aDescriptor( 2 );
405 aDescriptor
[0].Name
= "MediaType";
406 aDescriptor
[0].Value
<<= aExportMimeType
;
407 aDescriptor
[1].Name
= "URL";
408 aDescriptor
[1].Value
<<= sPath
;
410 Reference
< XComponent
> xSourceDocument( xShape
, UNO_QUERY_THROW
);
411 xGraphicExporter
->setSourceDocument( xSourceDocument
);
412 xGraphicExporter
->filter( aDescriptor
);
421 short GraphicHelper::HasToSaveTransformedImage(weld::Widget
* pWin
)
423 OUString
aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE
));
424 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
425 VclMessageType::Question
, VclButtonsType::YesNo
, aMsg
));
429 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */