1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
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>
31
32 #include <comphelper/processfactory.hxx>
33
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
50 #include <map>
51
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;
62
63 using namespace sfx2;
64
65 namespace drawing = com::sun::star::drawing;
66
GetPreferredExtension(OUString & rExtension,const Graphic & rGraphic)67 void GraphicHelper::GetPreferredExtension( OUString& rExtension, const Graphic& rGraphic )
68 {
69 OUString aExtension = "png";
70 auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
71
72 if (rVectorGraphicDataPtr && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
73 {
74 switch (rVectorGraphicDataPtr->getType())
75 {
76 case VectorGraphicDataType::Wmf:
77 aExtension = "wmf";
78 break;
79 case VectorGraphicDataType::Emf:
80 aExtension = "emf";
81 break;
82 default: // case VectorGraphicDataType::Svg:
83 aExtension = "svg";
84 break;
85 }
86
87 rExtension = aExtension;
88 return;
89 }
90
91 switch( rGraphic.GetGfxLink().GetType() )
92 {
93 case GfxLinkType::NativeGif:
94 aExtension = "gif";
95 break;
96 case GfxLinkType::NativeTif:
97 aExtension = "tif";
98 break;
99 case GfxLinkType::NativeWmf:
100 aExtension = "wmf";
101 break;
102 case GfxLinkType::NativeMet:
103 aExtension = "met";
104 break;
105 case GfxLinkType::NativePct:
106 aExtension = "pct";
107 break;
108 case GfxLinkType::NativeJpg:
109 aExtension = "jpg";
110 break;
111 case GfxLinkType::NativeBmp:
112 aExtension = "bmp";
113 break;
114 case GfxLinkType::NativeSvg:
115 aExtension = "svg";
116 break;
117 case GfxLinkType::NativePdf:
118 aExtension = "pdf";
119 break;
120 default:
121 break;
122 }
123 rExtension = aExtension;
124 }
125
GetImageType(const Graphic & rGraphic)126 OUString GraphicHelper::GetImageType(const Graphic& rGraphic)
127 {
128 OUString aGraphicTypeString = SvxResId(STR_IMAGE_UNKNOWN);
129 auto pGfxLink = rGraphic.GetSharedGfxLink();
130 if (pGfxLink)
131 {
132 switch (pGfxLink->GetType())
133 {
134 case GfxLinkType::NativeGif:
135 aGraphicTypeString = SvxResId(STR_IMAGE_GIF);
136 break;
137 case GfxLinkType::NativeJpg:
138 aGraphicTypeString = SvxResId(STR_IMAGE_JPEG);
139 break;
140 case GfxLinkType::NativePng:
141 aGraphicTypeString = SvxResId(STR_IMAGE_PNG);
142 break;
143 case GfxLinkType::NativeTif:
144 aGraphicTypeString = SvxResId(STR_IMAGE_TIFF);
145 break;
146 case GfxLinkType::NativeWmf:
147 aGraphicTypeString = SvxResId(STR_IMAGE_WMF);
148 break;
149 case GfxLinkType::NativeMet:
150 aGraphicTypeString = SvxResId(STR_IMAGE_MET);
151 break;
152 case GfxLinkType::NativePct:
153 aGraphicTypeString = SvxResId(STR_IMAGE_PCT);
154 break;
155 case GfxLinkType::NativeSvg:
156 aGraphicTypeString = SvxResId(STR_IMAGE_SVG);
157 break;
158 case GfxLinkType::NativeBmp:
159 aGraphicTypeString = SvxResId(STR_IMAGE_BMP);
160 break;
161 default:
162 break;
163 }
164 }
165 return aGraphicTypeString;
166 }
167 namespace {
168
169
lcl_ExecuteFilterDialog(const Sequence<PropertyValue> & rPropsForDialog,Sequence<PropertyValue> & rFilterData)170 bool lcl_ExecuteFilterDialog( const Sequence< PropertyValue >& rPropsForDialog,
171 Sequence< PropertyValue >& rFilterData )
172 {
173 bool bStatus = false;
174 try
175 {
176 Reference< XExecutableDialog > xFilterDialog(
177 comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.svtools.SvFilterOptionsDialog" ), UNO_QUERY );
178 Reference< XPropertyAccess > xFilterProperties( xFilterDialog, UNO_QUERY );
179
180 if( xFilterDialog.is() && xFilterProperties.is() )
181 {
182 xFilterProperties->setPropertyValues( rPropsForDialog );
183 if( xFilterDialog->execute() )
184 {
185 bStatus = true;
186 const Sequence< PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues();
187 for ( const auto& rProp : aPropsFromDialog )
188 {
189 if (rProp.Name == "FilterData")
190 {
191 rProp.Value >>= rFilterData;
192 }
193 }
194 }
195 }
196 }
197 catch( const NoSuchElementException& e )
198 {
199 // the filter name is unknown
200 throw ErrorCodeIOException(
201 ("lcl_ExecuteFilterDialog: NoSuchElementException"
202 " \"" + e.Message + "\": ERRCODE_IO_ABORT"),
203 Reference< XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
204 }
205 catch( const ErrorCodeIOException& )
206 {
207 throw;
208 }
209 catch( const Exception& )
210 {
211 TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
212 }
213
214 return bStatus;
215 }
216 } // anonymous ns
217
ExportGraphic(weld::Window * pParent,const Graphic & rGraphic,const OUString & rGraphicName)218 OUString GraphicHelper::ExportGraphic(weld::Window* pParent, const Graphic& rGraphic, const OUString& rGraphicName)
219 {
220 SvtPathOptions aPathOpt;
221 OUString sGraphicsPath( aPathOpt.GetGraphicPath() );
222
223 FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
224 Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
225
226 INetURLObject aPath;
227 aPath.SetSmartURL( sGraphicsPath );
228
229 // fish out the graphic's name
230
231 aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE));
232 aDialogHelper.SetDisplayDirectory( aPath.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) );
233 INetURLObject aURL;
234 aURL.SetSmartURL( rGraphicName );
235 aDialogHelper.SetFileName(aURL.GetLastName());
236
237 GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
238 const sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
239
240 OUString aExtension(aURL.GetFileExtension());
241 if( aExtension.isEmpty() )
242 {
243 GetPreferredExtension( aExtension, rGraphic );
244 }
245
246 aExtension = aExtension.toAsciiLowerCase();
247 sal_uInt16 nDefaultFilter = USHRT_MAX;
248
249 for ( sal_uInt16 i = 0; i < nCount; i++ )
250 {
251 xFilePicker->appendFilter( rGraphicFilter.GetExportFormatName( i ), rGraphicFilter.GetExportWildcard( i ) );
252 OUString aFormatShortName = rGraphicFilter.GetExportFormatShortName( i );
253 if ( aFormatShortName.equalsIgnoreAsciiCase( aExtension ) )
254 {
255 nDefaultFilter = i;
256 }
257 }
258 if ( USHRT_MAX == nDefaultFilter )
259 {
260 // "wrong" extension?
261 GetPreferredExtension( aExtension, rGraphic );
262 for ( sal_uInt16 i = 0; i < nCount; ++i )
263 if ( aExtension == rGraphicFilter.GetExportFormatShortName( i ).toAsciiLowerCase() )
264 {
265 nDefaultFilter = i;
266 break;
267 }
268 }
269
270 if( USHRT_MAX != nDefaultFilter )
271 {
272 xFilePicker->setCurrentFilter( rGraphicFilter.GetExportFormatName( nDefaultFilter ) ) ;
273
274 if( aDialogHelper.Execute() == ERRCODE_NONE )
275 {
276 OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
277 // remember used path - please don't optimize away!
278 aPath.SetSmartURL( sPath);
279 sGraphicsPath = aPath.GetPath();
280
281 if( !rGraphicName.isEmpty() &&
282 nDefaultFilter == rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter()))
283 {
284 // try to save the original graphic
285 SfxMedium aIn( rGraphicName, StreamMode::READ | StreamMode::NOCREATE );
286 if( aIn.GetInStream() && !aIn.GetInStream()->GetError() )
287 {
288 SfxMedium aOut( sPath, StreamMode::WRITE | StreamMode::SHARE_DENYNONE);
289 if( aOut.GetOutStream() && !aOut.GetOutStream()->GetError())
290 {
291 aOut.GetOutStream()->WriteStream( *aIn.GetInStream() );
292 if ( ERRCODE_NONE == aIn.GetError() )
293 {
294 aOut.Close();
295 aOut.Commit();
296 if ( ERRCODE_NONE == aOut.GetError() )
297 return sPath;
298 }
299 }
300 }
301 }
302
303 sal_uInt16 nFilter;
304 if ( !xFilePicker->getCurrentFilter().isEmpty() && rGraphicFilter.GetExportFormatCount() )
305 {
306 nFilter = rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter() );
307 }
308 else
309 {
310 nFilter = GRFILTER_FORMAT_DONTKNOW;
311 }
312 OUString aFilter( rGraphicFilter.GetExportFormatShortName( nFilter ) );
313
314 if ( rGraphic.GetType() == GraphicType::Bitmap )
315 {
316 Graphic aGraphic = rGraphic;
317 Reference<XGraphic> xGraphic = aGraphic.GetXGraphic();
318
319 OUString aExportFilter = rGraphicFilter.GetExportInternalFilterName(nFilter);
320
321 Sequence< PropertyValue > aPropsForDialog(2);
322 aPropsForDialog[0].Name = "Graphic";
323 aPropsForDialog[0].Value <<= xGraphic;
324 aPropsForDialog[1].Name = "FilterName";
325 aPropsForDialog[1].Value <<= aExportFilter;
326
327 Sequence< PropertyValue > aFilterData;
328 bool bStatus = lcl_ExecuteFilterDialog(aPropsForDialog, aFilterData);
329 if (bStatus)
330 {
331 sal_Int32 nWidth = 0;
332 sal_Int32 nHeight = 0;
333
334 for (const auto& rProp : std::as_const(aFilterData))
335 {
336 if (rProp.Name == "PixelWidth")
337 {
338 rProp.Value >>= nWidth;
339 }
340 else if (rProp.Name == "PixelHeight")
341 {
342 rProp.Value >>= nHeight;
343 }
344 }
345
346 // scaling must performed here because png/jpg writer s
347 // do not take care of that.
348 Size aSizePixel( aGraphic.GetSizePixel() );
349 if( nWidth && nHeight &&
350 ( ( nWidth != aSizePixel.Width() ) ||
351 ( nHeight != aSizePixel.Height() ) ) )
352 {
353 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
354 // export: use highest quality
355 aBmpEx.Scale( Size( nWidth, nHeight ), BmpScaleFlag::Lanczos );
356 aGraphic = aBmpEx;
357 }
358
359 XOutBitmap::WriteGraphic( aGraphic, sPath, aFilter,
360 XOutFlags::DontExpandFilename |
361 XOutFlags::DontAddExtension |
362 XOutFlags::UseNativeIfPossible,
363 nullptr, &aFilterData );
364 return sPath;
365 }
366 }
367 else
368 {
369 XOutBitmap::WriteGraphic( rGraphic, sPath, aFilter,
370 XOutFlags::DontExpandFilename |
371 XOutFlags::DontAddExtension |
372 XOutFlags::UseNativeIfPossible );
373 }
374 }
375 }
376 return OUString();
377 }
378
SaveShapeAsGraphic(weld::Window * pParent,const Reference<drawing::XShape> & xShape)379 void GraphicHelper::SaveShapeAsGraphic(weld::Window* pParent, const Reference< drawing::XShape >& xShape)
380 {
381 try
382 {
383 Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
384 Reference< XPropertySet > xShapeSet( xShape, UNO_QUERY_THROW );
385
386 SvtPathOptions aPathOpt;
387 const OUString& sGraphicPath( aPathOpt.GetGraphicPath() );
388
389 FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
390 Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
391
392 aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE) );
393
394 INetURLObject aPath;
395 aPath.SetSmartURL( sGraphicPath );
396 xFilePicker->setDisplayDirectory( aPath.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) );
397
398 // populate filter dialog filter list and select default filter to match graphic mime type
399
400 GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
401 static const OUStringLiteral aDefaultMimeType(u"image/png");
402 OUString aDefaultFormatName;
403 sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
404
405 std::map< OUString, OUString > aMimeTypeMap;
406
407 for ( sal_uInt16 i = 0; i < nCount; i++ )
408 {
409 const OUString aExportFormatName( rGraphicFilter.GetExportFormatName( i ) );
410 const OUString aFilterMimeType( rGraphicFilter.GetExportFormatMediaType( i ) );
411 xFilePicker->appendFilter( aExportFormatName, rGraphicFilter.GetExportWildcard( i ) );
412 aMimeTypeMap[ aExportFormatName ] = aFilterMimeType;
413 if( aDefaultMimeType == aFilterMimeType )
414 aDefaultFormatName = aExportFormatName;
415 }
416
417 if( !aDefaultFormatName.isEmpty() )
418 xFilePicker->setCurrentFilter( aDefaultFormatName );
419
420 // execute dialog
421
422 if( aDialogHelper.Execute() == ERRCODE_NONE )
423 {
424 OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
425 OUString aExportMimeType( aMimeTypeMap[xFilePicker->getCurrentFilter()] );
426
427 Reference< XInputStream > xGraphStream;
428
429 if( xGraphStream.is() )
430 {
431 Reference<XSimpleFileAccess3> xFileAccess = SimpleFileAccess::create( xContext );
432 xFileAccess->writeFile( sPath, xGraphStream );
433 }
434 else
435 {
436 Reference<css::drawing::XGraphicExportFilter> xGraphicExporter = css::drawing::GraphicExportFilter::create( xContext );
437
438 Sequence<PropertyValue> aDescriptor( 2 );
439 aDescriptor[0].Name = "MediaType";
440 aDescriptor[0].Value <<= aExportMimeType;
441 aDescriptor[1].Name = "URL";
442 aDescriptor[1].Value <<= sPath;
443
444 Reference< XComponent > xSourceDocument( xShape, UNO_QUERY_THROW );
445 xGraphicExporter->setSourceDocument( xSourceDocument );
446 xGraphicExporter->filter( aDescriptor );
447 }
448 }
449 }
450 catch( Exception& )
451 {
452 }
453 }
454
HasToSaveTransformedImage(weld::Widget * pWin)455 short GraphicHelper::HasToSaveTransformedImage(weld::Widget* pWin)
456 {
457 OUString aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE));
458 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
459 VclMessageType::Question, VclButtonsType::YesNo, aMsg));
460 return xBox->run();
461 }
462
463 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
464