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