xref: /core/svx/source/core/graphichelper.cxx (revision 01786272)
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 
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->getVectorGraphicDataArrayLength())
73     {
74         switch (rVectorGraphicDataPtr->getVectorGraphicDataType())
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 
126 namespace {
127 
128 
129 bool lcl_ExecuteFilterDialog( const Sequence< PropertyValue >& rPropsForDialog,
130                               Sequence< PropertyValue >& rFilterData )
131 {
132     bool bStatus = false;
133     try
134     {
135         Reference< XExecutableDialog > xFilterDialog(
136                 comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.svtools.SvFilterOptionsDialog" ), UNO_QUERY );
137         Reference< XPropertyAccess > xFilterProperties( xFilterDialog, UNO_QUERY );
138 
139         if( xFilterDialog.is() && xFilterProperties.is() )
140         {
141             xFilterProperties->setPropertyValues( rPropsForDialog );
142             if( xFilterDialog->execute() )
143             {
144                 bStatus = true;
145                 const Sequence< PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues();
146                 for ( const auto& rProp : aPropsFromDialog )
147                 {
148                     if (rProp.Name == "FilterData")
149                     {
150                         rProp.Value >>= rFilterData;
151                     }
152                 }
153             }
154         }
155     }
156     catch( const NoSuchElementException& e )
157     {
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));
163     }
164     catch( const ErrorCodeIOException& )
165     {
166         throw;
167     }
168     catch( const Exception& )
169     {
170         TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
171     }
172 
173     return bStatus;
174 }
175 } // anonymous ns
176 
177 OUString GraphicHelper::ExportGraphic(weld::Window* pParent, const Graphic& rGraphic, const OUString& rGraphicName)
178 {
179     SvtPathOptions aPathOpt;
180     OUString sGraphicsPath( aPathOpt.GetGraphicPath() );
181 
182     FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
183     Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
184 
185     INetURLObject aPath;
186     aPath.SetSmartURL( sGraphicsPath );
187 
188     // fish out the graphic's name
189 
190     aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE));
191     aDialogHelper.SetDisplayDirectory( aPath.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) );
192     INetURLObject aURL;
193     aURL.SetSmartURL( rGraphicName );
194     aDialogHelper.SetFileName(aURL.GetLastName());
195 
196     GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
197     const sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
198 
199     OUString aExtension(aURL.GetFileExtension());
200     if( aExtension.isEmpty() )
201     {
202         GetPreferredExtension( aExtension, rGraphic );
203     }
204 
205     aExtension = aExtension.toAsciiLowerCase();
206     sal_uInt16 nDefaultFilter = USHRT_MAX;
207 
208     for ( sal_uInt16 i = 0; i < nCount; i++ )
209     {
210         xFilePicker->appendFilter( rGraphicFilter.GetExportFormatName( i ), rGraphicFilter.GetExportWildcard( i ) );
211         OUString aFormatShortName = rGraphicFilter.GetExportFormatShortName( i );
212         if ( aFormatShortName.equalsIgnoreAsciiCase( aExtension ) )
213         {
214             nDefaultFilter = i;
215         }
216     }
217     if ( USHRT_MAX == nDefaultFilter )
218     {
219         // "wrong" extension?
220         GetPreferredExtension( aExtension, rGraphic );
221         for ( sal_uInt16 i = 0; i < nCount; ++i )
222             if ( aExtension == rGraphicFilter.GetExportFormatShortName( i ).toAsciiLowerCase() )
223             {
224                 nDefaultFilter =  i;
225                 break;
226             }
227     }
228 
229     if( USHRT_MAX != nDefaultFilter )
230     {
231         xFilePicker->setCurrentFilter( rGraphicFilter.GetExportFormatName( nDefaultFilter ) ) ;
232 
233         if( aDialogHelper.Execute() == ERRCODE_NONE )
234         {
235             OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
236             // remember used path - please don't optimize away!
237             aPath.SetSmartURL( sPath);
238             sGraphicsPath = aPath.GetPath();
239 
240             if( !rGraphicName.isEmpty() &&
241                 nDefaultFilter == rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter()))
242             {
243                 // try to save the original graphic
244                 SfxMedium aIn( rGraphicName, StreamMode::READ | StreamMode::NOCREATE );
245                 if( aIn.GetInStream() && !aIn.GetInStream()->GetError() )
246                 {
247                     SfxMedium aOut( sPath, StreamMode::WRITE | StreamMode::SHARE_DENYNONE);
248                     if( aOut.GetOutStream() && !aOut.GetOutStream()->GetError())
249                     {
250                         aOut.GetOutStream()->WriteStream( *aIn.GetInStream() );
251                         if ( ERRCODE_NONE == aIn.GetError() )
252                         {
253                             aOut.Close();
254                             aOut.Commit();
255                             if ( ERRCODE_NONE == aOut.GetError() )
256                                 return sPath;
257                         }
258                     }
259                 }
260             }
261 
262             sal_uInt16 nFilter;
263             if ( !xFilePicker->getCurrentFilter().isEmpty() && rGraphicFilter.GetExportFormatCount() )
264             {
265                 nFilter = rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter() );
266             }
267             else
268             {
269                 nFilter = GRFILTER_FORMAT_DONTKNOW;
270             }
271             OUString aFilter( rGraphicFilter.GetExportFormatShortName( nFilter ) );
272 
273             if ( rGraphic.GetType() == GraphicType::Bitmap )
274             {
275                 Graphic aGraphic = rGraphic;
276                 Reference<XGraphic> xGraphic = aGraphic.GetXGraphic();
277 
278                 OUString aExportFilter = rGraphicFilter.GetExportInternalFilterName(nFilter);
279 
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;
285 
286                 Sequence< PropertyValue > aFilterData;
287                 bool bStatus = lcl_ExecuteFilterDialog(aPropsForDialog, aFilterData);
288                 if (bStatus)
289                 {
290                     sal_Int32 nWidth = 0;
291                     sal_Int32 nHeight = 0;
292 
293                     for (const auto& rProp : std::as_const(aFilterData))
294                     {
295                         if (rProp.Name == "PixelWidth")
296                         {
297                             rProp.Value >>= nWidth;
298                         }
299                         else if (rProp.Name == "PixelHeight")
300                         {
301                             rProp.Value >>= nHeight;
302                         }
303                     }
304 
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() ) ) )
311                     {
312                         BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
313                         // export: use highest quality
314                         aBmpEx.Scale( Size( nWidth, nHeight ), BmpScaleFlag::Lanczos );
315                         aGraphic = aBmpEx;
316                     }
317 
318                     XOutBitmap::WriteGraphic( aGraphic, sPath, aFilter,
319                                                 XOutFlags::DontExpandFilename |
320                                                 XOutFlags::DontAddExtension |
321                                                 XOutFlags::UseNativeIfPossible,
322                                                 nullptr, &aFilterData );
323                     return sPath;
324                 }
325             }
326             else
327             {
328                 XOutBitmap::WriteGraphic( rGraphic, sPath, aFilter,
329                                             XOutFlags::DontExpandFilename |
330                                             XOutFlags::DontAddExtension |
331                                             XOutFlags::UseNativeIfPossible );
332             }
333         }
334     }
335     return OUString();
336 }
337 
338 void GraphicHelper::SaveShapeAsGraphic(weld::Window* pParent,  const Reference< drawing::XShape >& xShape)
339 {
340     try
341     {
342         Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
343         Reference< XPropertySet > xShapeSet( xShape, UNO_QUERY_THROW );
344 
345         SvtPathOptions aPathOpt;
346         const OUString& sGraphicPath( aPathOpt.GetGraphicPath() );
347 
348         FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
349         Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
350 
351         aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE) );
352 
353         INetURLObject aPath;
354         aPath.SetSmartURL( sGraphicPath );
355         xFilePicker->setDisplayDirectory( aPath.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) );
356 
357         // populate filter dialog filter list and select default filter to match graphic mime type
358 
359         GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
360         const OUString aDefaultMimeType("image/png");
361         OUString aDefaultFormatName;
362         sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
363 
364         std::map< OUString, OUString > aMimeTypeMap;
365 
366         for ( sal_uInt16 i = 0; i < nCount; i++ )
367         {
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;
374         }
375 
376         if( !aDefaultFormatName.isEmpty() )
377             xFilePicker->setCurrentFilter( aDefaultFormatName );
378 
379         // execute dialog
380 
381         if( aDialogHelper.Execute() == ERRCODE_NONE )
382         {
383             OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
384             OUString aExportMimeType( aMimeTypeMap[xFilePicker->getCurrentFilter()] );
385 
386             Reference< XInputStream > xGraphStream;
387 
388             if( xGraphStream.is() )
389             {
390                 Reference<XSimpleFileAccess3> xFileAccess = SimpleFileAccess::create( xContext );
391                 xFileAccess->writeFile( sPath, xGraphStream );
392             }
393             else
394             {
395                 Reference<css::drawing::XGraphicExportFilter> xGraphicExporter = css::drawing::GraphicExportFilter::create( xContext );
396 
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;
402 
403                 Reference< XComponent > xSourceDocument( xShape, UNO_QUERY_THROW );
404                 xGraphicExporter->setSourceDocument( xSourceDocument );
405                 xGraphicExporter->filter( aDescriptor );
406             }
407         }
408     }
409     catch( Exception& )
410     {
411     }
412 }
413 
414 short GraphicHelper::HasToSaveTransformedImage(weld::Widget* pWin)
415 {
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));
419     return xBox->run();
420 }
421 
422 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
423