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>
52 #include <frozen/bits/defines.h>
53 #include <frozen/bits/elsa_std.h>
54 #include <frozen/unordered_map.h>
56 #include <unotools/streamwrap.hxx>
63 const auto constGfxTypeToExtension
= frozen::make_unordered_map
<GfxLinkType
, OUString
>(
65 { GfxLinkType::NativePng
, u
"png"_ustr
},
66 { GfxLinkType::NativeGif
, u
"gif"_ustr
},
67 { GfxLinkType::NativeTif
, u
"tif"_ustr
},
68 { GfxLinkType::NativeWmf
, u
"wmf"_ustr
},
69 { GfxLinkType::NativeMet
, u
"met"_ustr
},
70 { GfxLinkType::NativePct
, u
"pct"_ustr
},
71 { GfxLinkType::NativeJpg
, u
"jpg"_ustr
},
72 { GfxLinkType::NativeBmp
, u
"bmp"_ustr
},
73 { GfxLinkType::NativeSvg
, u
"svg"_ustr
},
74 { GfxLinkType::NativePdf
, u
"pdf"_ustr
},
75 { GfxLinkType::NativeWebp
, u
"webp"_ustr
},
78 const auto constVectorGraphicTypeToExtension
= frozen::make_unordered_map
<VectorGraphicDataType
, OUString
>(
80 { VectorGraphicDataType::Wmf
, u
"wmf"_ustr
},
81 { VectorGraphicDataType::Emf
, u
"emf"_ustr
},
82 { VectorGraphicDataType::Svg
, u
"svg"_ustr
},
85 const auto constGfxTypeToString
= frozen::make_unordered_map
<GfxLinkType
, TranslateId
>(
87 { GfxLinkType::NativePng
, STR_IMAGE_PNG
},
88 { GfxLinkType::NativeGif
, STR_IMAGE_GIF
},
89 { GfxLinkType::NativeTif
, STR_IMAGE_TIFF
},
90 { GfxLinkType::NativeWmf
, STR_IMAGE_WMF
},
91 { GfxLinkType::NativeMet
, STR_IMAGE_MET
},
92 { GfxLinkType::NativePct
, STR_IMAGE_PCT
},
93 { GfxLinkType::NativeJpg
, STR_IMAGE_JPEG
},
94 { GfxLinkType::NativeBmp
, STR_IMAGE_BMP
},
95 { GfxLinkType::NativeSvg
, STR_IMAGE_SVG
},
96 { GfxLinkType::NativePdf
, STR_IMAGE_PNG
},
97 { GfxLinkType::NativeWebp
, STR_IMAGE_WEBP
},
100 } // end anonymous ns
102 void GraphicHelper::GetPreferredExtension( OUString
& rExtension
, const Graphic
& rGraphic
)
104 auto const & rVectorGraphicDataPtr(rGraphic
.getVectorGraphicData());
106 if (rVectorGraphicDataPtr
&& !rVectorGraphicDataPtr
->getBinaryDataContainer().isEmpty())
108 auto eType
= rVectorGraphicDataPtr
->getType();
109 const auto iter
= constVectorGraphicTypeToExtension
.find(eType
);
110 if (iter
!= constVectorGraphicTypeToExtension
.end())
111 rExtension
= iter
->second
;
113 rExtension
= u
"svg"_ustr
; // not sure this makes sense but it is like this for a long time
117 auto eType
= rGraphic
.GetGfxLink().GetType();
118 const auto iter
= constGfxTypeToExtension
.find(eType
);
119 if (iter
!= constGfxTypeToExtension
.end())
120 rExtension
= iter
->second
;
122 rExtension
= u
"png"_ustr
; // not sure this makes sense but it is like this for a long time
126 OUString
GraphicHelper::GetImageType(const Graphic
& rGraphic
)
128 const auto& pGfxLink
= rGraphic
.GetSharedGfxLink();
131 auto iter
= constGfxTypeToString
.find(pGfxLink
->GetType());
132 if (iter
!= constGfxTypeToString
.end())
133 return SvxResId(iter
->second
);
135 return SvxResId(STR_IMAGE_UNKNOWN
);
141 bool lcl_ExecuteFilterDialog(const uno::Sequence
<beans::PropertyValue
>& rPropsForDialog
,
142 uno::Sequence
<beans::PropertyValue
>& rFilterData
)
144 bool bStatus
= false;
147 uno::Reference
<ui::dialogs::XExecutableDialog
> xFilterDialog(
148 comphelper::getProcessServiceFactory()->createInstance( u
"com.sun.star.svtools.SvFilterOptionsDialog"_ustr
), uno::UNO_QUERY
);
149 uno::Reference
<beans::XPropertyAccess
> xFilterProperties( xFilterDialog
, uno::UNO_QUERY
);
151 if( xFilterDialog
.is() && xFilterProperties
.is() )
153 xFilterProperties
->setPropertyValues( rPropsForDialog
);
154 if( xFilterDialog
->execute() )
157 const uno::Sequence
<beans::PropertyValue
> aPropsFromDialog
= xFilterProperties
->getPropertyValues();
158 for ( const auto& rProp
: aPropsFromDialog
)
160 if (rProp
.Name
== "FilterData")
162 rProp
.Value
>>= rFilterData
;
168 catch (container::NoSuchElementException
const& exception
)
170 // the filter name is unknown
171 throw task::ErrorCodeIOException(
172 ("lcl_ExecuteFilterDialog: NoSuchElementException"
173 " \"" + exception
.Message
+ "\": ERRCODE_IO_ABORT"),
174 uno::Reference
<uno::XInterface
>(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER
));
176 catch (const task::ErrorCodeIOException
&)
180 catch (const uno::Exception
&)
182 TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
189 OUString
GraphicHelper::ExportGraphic(weld::Window
* pParent
, const Graphic
& rGraphic
, const OUString
& rGraphicName
)
191 sfx2::FileDialogHelper
aDialogHelper(ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
192 uno::Reference
<ui::dialogs::XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
194 // fish out the graphic's name
195 aDialogHelper
.SetContext(sfx2::FileDialogHelper::ExportImage
);
196 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE
));
198 aURL
.SetSmartURL( rGraphicName
);
199 aDialogHelper
.SetFileName(aURL
.GetLastName());
201 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
202 const sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
204 OUString
aExtension(aURL
.GetFileExtension());
205 if( aExtension
.isEmpty() )
207 GetPreferredExtension( aExtension
, rGraphic
);
210 aExtension
= aExtension
.toAsciiLowerCase();
211 sal_uInt16 nDefaultFilter
= USHRT_MAX
;
213 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
215 xFilePicker
->appendFilter( rGraphicFilter
.GetExportFormatName( i
), rGraphicFilter
.GetExportWildcard( i
) );
216 OUString aFormatShortName
= rGraphicFilter
.GetExportFormatShortName( i
);
217 if ( aFormatShortName
.equalsIgnoreAsciiCase( aExtension
) )
222 if ( USHRT_MAX
== nDefaultFilter
)
224 // "wrong" extension?
225 GetPreferredExtension( aExtension
, rGraphic
);
226 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
227 if ( aExtension
== rGraphicFilter
.GetExportFormatShortName( i
).toAsciiLowerCase() )
234 if( USHRT_MAX
!= nDefaultFilter
)
236 xFilePicker
->setCurrentFilter( rGraphicFilter
.GetExportFormatName( nDefaultFilter
) ) ;
238 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
240 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
241 if( !rGraphicName
.isEmpty() &&
242 nDefaultFilter
== rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter()))
244 // try to save the original graphic
245 SfxMedium
aIn( rGraphicName
, StreamMode::READ
| StreamMode::NOCREATE
);
246 if( aIn
.GetInStream() && !aIn
.GetInStream()->GetError() )
248 SfxMedium
aOut( sPath
, StreamMode::WRITE
| StreamMode::SHARE_DENYNONE
);
249 if( aOut
.GetOutStream() && !aOut
.GetOutStream()->GetError())
251 aOut
.GetOutStream()->WriteStream( *aIn
.GetInStream() );
252 if ( ERRCODE_NONE
== aIn
.GetErrorIgnoreWarning() )
256 if ( ERRCODE_NONE
== aOut
.GetErrorIgnoreWarning() )
264 if ( !xFilePicker
->getCurrentFilter().isEmpty() && rGraphicFilter
.GetExportFormatCount() )
266 nFilter
= rGraphicFilter
.GetExportFormatNumber( xFilePicker
->getCurrentFilter() );
270 nFilter
= GRFILTER_FORMAT_DONTKNOW
;
272 OUString
aFilter( rGraphicFilter
.GetExportFormatShortName( nFilter
) );
274 if ( rGraphic
.GetType() == GraphicType::Bitmap
)
276 Graphic aGraphic
= rGraphic
;
277 uno::Reference
<graphic::XGraphic
> xGraphic
= aGraphic
.GetXGraphic();
279 OUString aExportFilter
= rGraphicFilter
.GetExportInternalFilterName(nFilter
);
281 uno::Sequence
<beans::PropertyValue
> aPropsForDialog
283 comphelper::makePropertyValue(u
"Graphic"_ustr
, xGraphic
),
284 comphelper::makePropertyValue(u
"FilterName"_ustr
, aExportFilter
)
287 uno::Sequence
<beans::PropertyValue
> aFilterData
;
288 bool bStatus
= lcl_ExecuteFilterDialog(aPropsForDialog
, aFilterData
);
291 sal_Int32 nWidth
= 0;
292 sal_Int32 nHeight
= 0;
294 for (const auto& rProp
: aFilterData
)
296 if (rProp
.Name
== "PixelWidth")
298 rProp
.Value
>>= nWidth
;
300 else if (rProp
.Name
== "PixelHeight")
302 rProp
.Value
>>= nHeight
;
306 // scaling must performed here because png/jpg writer s
307 // do not take care of that.
308 Size
aSizePixel( aGraphic
.GetSizePixel() );
309 if( nWidth
&& nHeight
&&
310 ( ( nWidth
!= aSizePixel
.Width() ) ||
311 ( nHeight
!= aSizePixel
.Height() ) ) )
313 BitmapEx
aBmpEx( aGraphic
.GetBitmapEx() );
314 // export: use highest quality
315 aBmpEx
.Scale( Size( nWidth
, nHeight
), BmpScaleFlag::Lanczos
);
319 XOutBitmap::WriteGraphic( aGraphic
, sPath
, aFilter
,
320 XOutFlags::DontExpandFilename
|
321 XOutFlags::DontAddExtension
|
322 XOutFlags::UseNativeIfPossible
,
323 nullptr, &aFilterData
);
329 XOutBitmap::WriteGraphic( rGraphic
, sPath
, aFilter
,
330 XOutFlags::DontExpandFilename
|
331 XOutFlags::DontAddExtension
|
332 XOutFlags::UseNativeIfPossible
);
339 void GraphicHelper::SaveShapeAsGraphicToPath(
340 const uno::Reference
<lang::XComponent
>& xComponent
,
341 const uno::Reference
<drawing::XShape
>& xShape
, const OUString
& aExportMimeType
,
342 const OUString
& sPath
)
344 const uno::Reference
<uno::XComponentContext
>& xContext(::comphelper::getProcessComponentContext());
345 uno::Reference
<io::XInputStream
> xGraphStream
;
347 if (xGraphStream
.is())
349 uno::Reference
<ucb::XSimpleFileAccess3
> xFileAccess
= ucb::SimpleFileAccess::create(xContext
);
350 xFileAccess
->writeFile(sPath
, xGraphStream
);
352 else if (xComponent
.is() && aExportMimeType
== "application/pdf")
354 uno::Reference
<lang::XMultiServiceFactory
> xMSF(xContext
->getServiceManager(), uno::UNO_QUERY
);
355 uno::Reference
<document::XExporter
> xExporter(
356 xMSF
->createInstance(u
"com.sun.star.comp.PDF.PDFFilter"_ustr
), uno::UNO_QUERY
);
357 xExporter
->setSourceDocument(xComponent
);
359 uno::Reference
<drawing::XShapes
> xShapes
360 = drawing::ShapeCollection::create(comphelper::getProcessComponentContext());
361 xShapes
->add(xShape
);
362 uno::Sequence
<beans::PropertyValue
> aFilterData
{
363 comphelper::makePropertyValue(u
"Selection"_ustr
, xShapes
),
365 SvFileStream
aStream(sPath
, StreamMode::READWRITE
| StreamMode::TRUNC
);
366 uno::Reference
<io::XOutputStream
> xStream(new utl::OStreamWrapper(aStream
));
367 uno::Sequence
<beans::PropertyValue
> aDescriptor
369 comphelper::makePropertyValue(u
"FilterData"_ustr
, aFilterData
),
370 comphelper::makePropertyValue(u
"OutputStream"_ustr
, xStream
)
372 uno::Reference
<document::XFilter
> xFilter(xExporter
, uno::UNO_QUERY
);
373 xFilter
->filter(aDescriptor
);
377 uno::Reference
<drawing::XGraphicExportFilter
> xGraphicExporter
= drawing::GraphicExportFilter::create(xContext
);
379 uno::Sequence
<beans::PropertyValue
> aDescriptor
{ comphelper::makePropertyValue(u
"MediaType"_ustr
,
381 comphelper::makePropertyValue(u
"URL"_ustr
, sPath
) };
383 uno::Reference
<lang::XComponent
> xSourceDocument(xShape
, uno::UNO_QUERY_THROW
);
384 xGraphicExporter
->setSourceDocument(xSourceDocument
);
385 xGraphicExporter
->filter(aDescriptor
);
389 void GraphicHelper::SaveShapeAsGraphic(weld::Window
* pParent
,
390 const uno::Reference
<lang::XComponent
>& xComponent
,
391 const uno::Reference
<drawing::XShape
>& xShape
)
395 uno::Reference
<beans::XPropertySet
> xShapeSet(xShape
, uno::UNO_QUERY_THROW
);
397 sfx2::FileDialogHelper
aDialogHelper(ui::dialogs::TemplateDescription::FILESAVE_AUTOEXTENSION
, FileDialogFlags::NONE
, pParent
);
398 uno::Reference
<ui::dialogs::XFilePicker3
> xFilePicker
= aDialogHelper
.GetFilePicker();
399 aDialogHelper
.SetContext(sfx2::FileDialogHelper::ExportImage
);
400 aDialogHelper
.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE
) );
402 // populate filter dialog filter list and select default filter to match graphic mime type
404 GraphicFilter
& rGraphicFilter
= GraphicFilter::GetGraphicFilter();
405 static constexpr OUStringLiteral
aDefaultMimeType(u
"image/png");
406 OUString aDefaultFormatName
;
407 sal_uInt16 nCount
= rGraphicFilter
.GetExportFormatCount();
409 std::map
< OUString
, OUString
> aMimeTypeMap
;
411 for ( sal_uInt16 i
= 0; i
< nCount
; i
++ )
413 const OUString
aExportFormatName( rGraphicFilter
.GetExportFormatName( i
) );
414 const OUString
aFilterMimeType( rGraphicFilter
.GetExportFormatMediaType( i
) );
415 xFilePicker
->appendFilter( aExportFormatName
, rGraphicFilter
.GetExportWildcard( i
) );
416 aMimeTypeMap
[ aExportFormatName
] = aFilterMimeType
;
417 if( aDefaultMimeType
== aFilterMimeType
)
418 aDefaultFormatName
= aExportFormatName
;
421 if( !aDefaultFormatName
.isEmpty() )
422 xFilePicker
->setCurrentFilter( aDefaultFormatName
);
426 if( aDialogHelper
.Execute() == ERRCODE_NONE
)
428 OUString
sPath( xFilePicker
->getFiles().getConstArray()[0] );
429 OUString
aExportMimeType( aMimeTypeMap
[xFilePicker
->getCurrentFilter()] );
431 GraphicHelper::SaveShapeAsGraphicToPath(xComponent
, xShape
, aExportMimeType
, sPath
);
434 catch (uno::Exception
&)
439 short GraphicHelper::HasToSaveTransformedImage(weld::Widget
* pWin
)
441 OUString
aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE
));
442 std::unique_ptr
<weld::MessageDialog
> xBox(Application::CreateMessageDialog(pWin
,
443 VclMessageType::Question
, VclButtonsType::YesNo
, aMsg
));
447 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */