LibreOffice Module svx (master)  1
graphichelper.cxx
Go to the documentation of this file.
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 
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 
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->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 
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 
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 
218 OUString GraphicHelper::ExportGraphic(weld::Window* pParent, const Graphic& rGraphic, const OUString& rGraphicName)
219 {
220  FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
221  Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
222 
223  // fish out the graphic's name
224  aDialogHelper.SetContext(FileDialogHelper::ExportImage);
225  aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE));
227  aURL.SetSmartURL( rGraphicName );
228  aDialogHelper.SetFileName(aURL.GetLastName());
229 
231  const sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
232 
233  OUString aExtension(aURL.GetFileExtension());
234  if( aExtension.isEmpty() )
235  {
236  GetPreferredExtension( aExtension, rGraphic );
237  }
238 
239  aExtension = aExtension.toAsciiLowerCase();
240  sal_uInt16 nDefaultFilter = USHRT_MAX;
241 
242  for ( sal_uInt16 i = 0; i < nCount; i++ )
243  {
244  xFilePicker->appendFilter( rGraphicFilter.GetExportFormatName( i ), rGraphicFilter.GetExportWildcard( i ) );
245  OUString aFormatShortName = rGraphicFilter.GetExportFormatShortName( i );
246  if ( aFormatShortName.equalsIgnoreAsciiCase( aExtension ) )
247  {
248  nDefaultFilter = i;
249  }
250  }
251  if ( USHRT_MAX == nDefaultFilter )
252  {
253  // "wrong" extension?
254  GetPreferredExtension( aExtension, rGraphic );
255  for ( sal_uInt16 i = 0; i < nCount; ++i )
256  if ( aExtension == rGraphicFilter.GetExportFormatShortName( i ).toAsciiLowerCase() )
257  {
258  nDefaultFilter = i;
259  break;
260  }
261  }
262 
263  if( USHRT_MAX != nDefaultFilter )
264  {
265  xFilePicker->setCurrentFilter( rGraphicFilter.GetExportFormatName( nDefaultFilter ) ) ;
266 
267  if( aDialogHelper.Execute() == ERRCODE_NONE )
268  {
269  OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
270  if( !rGraphicName.isEmpty() &&
271  nDefaultFilter == rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter()))
272  {
273  // try to save the original graphic
274  SfxMedium aIn( rGraphicName, StreamMode::READ | StreamMode::NOCREATE );
275  if( aIn.GetInStream() && !aIn.GetInStream()->GetError() )
276  {
277  SfxMedium aOut( sPath, StreamMode::WRITE | StreamMode::SHARE_DENYNONE);
278  if( aOut.GetOutStream() && !aOut.GetOutStream()->GetError())
279  {
280  aOut.GetOutStream()->WriteStream( *aIn.GetInStream() );
281  if ( ERRCODE_NONE == aIn.GetError() )
282  {
283  aOut.Close();
284  aOut.Commit();
285  if ( ERRCODE_NONE == aOut.GetError() )
286  return sPath;
287  }
288  }
289  }
290  }
291 
292  sal_uInt16 nFilter;
293  if ( !xFilePicker->getCurrentFilter().isEmpty() && rGraphicFilter.GetExportFormatCount() )
294  {
295  nFilter = rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter() );
296  }
297  else
298  {
299  nFilter = GRFILTER_FORMAT_DONTKNOW;
300  }
301  OUString aFilter( rGraphicFilter.GetExportFormatShortName( nFilter ) );
302 
303  if ( rGraphic.GetType() == GraphicType::Bitmap )
304  {
305  Graphic aGraphic = rGraphic;
306  Reference<XGraphic> xGraphic = aGraphic.GetXGraphic();
307 
308  OUString aExportFilter = rGraphicFilter.GetExportInternalFilterName(nFilter);
309 
310  Sequence< PropertyValue > aPropsForDialog(2);
311  aPropsForDialog[0].Name = "Graphic";
312  aPropsForDialog[0].Value <<= xGraphic;
313  aPropsForDialog[1].Name = "FilterName";
314  aPropsForDialog[1].Value <<= aExportFilter;
315 
316  Sequence< PropertyValue > aFilterData;
317  bool bStatus = lcl_ExecuteFilterDialog(aPropsForDialog, aFilterData);
318  if (bStatus)
319  {
320  sal_Int32 nWidth = 0;
321  sal_Int32 nHeight = 0;
322 
323  for (const auto& rProp : std::as_const(aFilterData))
324  {
325  if (rProp.Name == "PixelWidth")
326  {
327  rProp.Value >>= nWidth;
328  }
329  else if (rProp.Name == "PixelHeight")
330  {
331  rProp.Value >>= nHeight;
332  }
333  }
334 
335  // scaling must performed here because png/jpg writer s
336  // do not take care of that.
337  Size aSizePixel( aGraphic.GetSizePixel() );
338  if( nWidth && nHeight &&
339  ( ( nWidth != aSizePixel.Width() ) ||
340  ( nHeight != aSizePixel.Height() ) ) )
341  {
342  BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
343  // export: use highest quality
344  aBmpEx.Scale( Size( nWidth, nHeight ), BmpScaleFlag::Lanczos );
345  aGraphic = aBmpEx;
346  }
347 
348  XOutBitmap::WriteGraphic( aGraphic, sPath, aFilter,
352  nullptr, &aFilterData );
353  return sPath;
354  }
355  }
356  else
357  {
358  XOutBitmap::WriteGraphic( rGraphic, sPath, aFilter,
362  }
363  }
364  }
365  return OUString();
366 }
367 
368 void GraphicHelper::SaveShapeAsGraphic(weld::Window* pParent, const Reference< drawing::XShape >& xShape)
369 {
370  try
371  {
373  Reference< XPropertySet > xShapeSet( xShape, UNO_QUERY_THROW );
374 
375  FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
376  Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
377  aDialogHelper.SetContext(FileDialogHelper::ExportImage);
378  aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE) );
379 
380  // populate filter dialog filter list and select default filter to match graphic mime type
381 
383  static const OUStringLiteral aDefaultMimeType(u"image/png");
384  OUString aDefaultFormatName;
385  sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
386 
387  std::map< OUString, OUString > aMimeTypeMap;
388 
389  for ( sal_uInt16 i = 0; i < nCount; i++ )
390  {
391  const OUString aExportFormatName( rGraphicFilter.GetExportFormatName( i ) );
392  const OUString aFilterMimeType( rGraphicFilter.GetExportFormatMediaType( i ) );
393  xFilePicker->appendFilter( aExportFormatName, rGraphicFilter.GetExportWildcard( i ) );
394  aMimeTypeMap[ aExportFormatName ] = aFilterMimeType;
395  if( aDefaultMimeType == aFilterMimeType )
396  aDefaultFormatName = aExportFormatName;
397  }
398 
399  if( !aDefaultFormatName.isEmpty() )
400  xFilePicker->setCurrentFilter( aDefaultFormatName );
401 
402  // execute dialog
403 
404  if( aDialogHelper.Execute() == ERRCODE_NONE )
405  {
406  OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
407  OUString aExportMimeType( aMimeTypeMap[xFilePicker->getCurrentFilter()] );
408 
409  Reference< XInputStream > xGraphStream;
410 
411  if( xGraphStream.is() )
412  {
413  Reference<XSimpleFileAccess3> xFileAccess = SimpleFileAccess::create( xContext );
414  xFileAccess->writeFile( sPath, xGraphStream );
415  }
416  else
417  {
418  Reference<css::drawing::XGraphicExportFilter> xGraphicExporter = css::drawing::GraphicExportFilter::create( xContext );
419 
420  Sequence<PropertyValue> aDescriptor( 2 );
421  aDescriptor[0].Name = "MediaType";
422  aDescriptor[0].Value <<= aExportMimeType;
423  aDescriptor[1].Name = "URL";
424  aDescriptor[1].Value <<= sPath;
425 
426  Reference< XComponent > xSourceDocument( xShape, UNO_QUERY_THROW );
427  xGraphicExporter->setSourceDocument( xSourceDocument );
428  xGraphicExporter->filter( aDescriptor );
429  }
430  }
431  }
432  catch( Exception& )
433  {
434  }
435 }
436 
437 short GraphicHelper::HasToSaveTransformedImage(weld::Widget* pWin)
438 {
439  OUString aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE));
440  std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
441  VclMessageType::Question, VclButtonsType::YesNo, aMsg));
442  return xBox->run();
443 }
444 
445 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
URL aURL
Size GetSizePixel(const OutputDevice *pRefDevice=nullptr) const
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
OUString GetLastName(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
const std::shared_ptr< GfxLink > & GetSharedGfxLink() const
OUString GetExportInternalFilterName(sal_uInt16 nFormat)
int nCount
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
sal_uInt16 GetExportFormatCount() const
static ErrCode WriteGraphic(const Graphic &rGraphic, OUString &rFileName, const OUString &rFilterName, const XOutFlags nFlags, const Size *pMtfSize_100TH_MM=nullptr, const css::uno::Sequence< css::beans::PropertyValue > *pFilterData=nullptr)
Definition: _xoutbmp.cxx:103
OUString GetExportFormatName(sal_uInt16 nFormat)
#define TOOLS_WARN_EXCEPTION(area, stream)
int i
GraphicType GetType() const
HRESULT createInstance(REFIID iid, Ifc **ppIfc)
sal_uInt16 GetExportFormatNumber(std::u16string_view rFormatName)
#define GRFILTER_FORMAT_DONTKNOW
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
GfxLink GetGfxLink() const
OUString GetExportFormatShortName(sal_uInt16 nFormat)
Reference< XMultiServiceFactory > getProcessServiceFactory()
#define ERRCODE_NONE
OUString GetFileExtension() const
Reference< XComponentContext > getProcessComponentContext()
OUString GetExportWildcard(sal_uInt16 nFormat)
css::uno::Reference< css::graphic::XGraphic > GetXGraphic() const
static GraphicFilter & GetGraphicFilter()
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() const
OUString GetExportFormatMediaType(sal_uInt16 nFormat)
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, bool bMobile=false)
bool SetSmartURL(OUString const &rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)