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 <vcl/graphicfilter.hxx>
21#include <sfx2/docfile.hxx>
23#include <svx/xoutbmp.hxx>
24#include <svx/dialmgr.hxx>
25#include <svx/graphichelper.hxx>
26#include <svx/strings.hrc>
28#include <vcl/svapp.hxx>
29#include <vcl/weld.hxx>
30
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#include <com/sun/star/drawing/ShapeCollection.hpp>
50
51#include <map>
52
54
55using namespace css::uno;
56using namespace css::lang;
57using namespace css::graphic;
58using namespace css::ucb;
59using namespace css::beans;
60using namespace css::io;
61using namespace css::document;
62using namespace css::ui::dialogs;
63using namespace css::container;
64using namespace com::sun::star::task;
65
66using namespace sfx2;
67
69
70void GraphicHelper::GetPreferredExtension( OUString& rExtension, const Graphic& rGraphic )
71{
72 OUString aExtension = "png";
73 auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
74
75 if (rVectorGraphicDataPtr && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
76 {
77 switch (rVectorGraphicDataPtr->getType())
78 {
79 case VectorGraphicDataType::Wmf:
80 aExtension = "wmf";
81 break;
82 case VectorGraphicDataType::Emf:
83 aExtension = "emf";
84 break;
85 default: // case VectorGraphicDataType::Svg:
86 aExtension = "svg";
87 break;
88 }
89
90 rExtension = aExtension;
91 return;
92 }
93
94 switch( rGraphic.GetGfxLink().GetType() )
95 {
96 case GfxLinkType::NativeGif:
97 aExtension = "gif";
98 break;
99 case GfxLinkType::NativeTif:
100 aExtension = "tif";
101 break;
102 case GfxLinkType::NativeWmf:
103 aExtension = "wmf";
104 break;
105 case GfxLinkType::NativeMet:
106 aExtension = "met";
107 break;
108 case GfxLinkType::NativePct:
109 aExtension = "pct";
110 break;
111 case GfxLinkType::NativeJpg:
112 aExtension = "jpg";
113 break;
114 case GfxLinkType::NativeBmp:
115 aExtension = "bmp";
116 break;
117 case GfxLinkType::NativeSvg:
118 aExtension = "svg";
119 break;
120 case GfxLinkType::NativePdf:
121 aExtension = "pdf";
122 break;
123 case GfxLinkType::NativeWebp:
124 aExtension = "webp";
125 break;
126 default:
127 break;
128 }
129 rExtension = aExtension;
130}
131
132OUString GraphicHelper::GetImageType(const Graphic& rGraphic)
133{
134 OUString aGraphicTypeString = SvxResId(STR_IMAGE_UNKNOWN);
135 auto pGfxLink = rGraphic.GetSharedGfxLink();
136 if (pGfxLink)
137 {
138 switch (pGfxLink->GetType())
139 {
140 case GfxLinkType::NativeGif:
141 aGraphicTypeString = SvxResId(STR_IMAGE_GIF);
142 break;
143 case GfxLinkType::NativeJpg:
144 aGraphicTypeString = SvxResId(STR_IMAGE_JPEG);
145 break;
146 case GfxLinkType::NativePng:
147 aGraphicTypeString = SvxResId(STR_IMAGE_PNG);
148 break;
149 case GfxLinkType::NativeTif:
150 aGraphicTypeString = SvxResId(STR_IMAGE_TIFF);
151 break;
152 case GfxLinkType::NativeWmf:
153 aGraphicTypeString = SvxResId(STR_IMAGE_WMF);
154 break;
155 case GfxLinkType::NativeMet:
156 aGraphicTypeString = SvxResId(STR_IMAGE_MET);
157 break;
158 case GfxLinkType::NativePct:
159 aGraphicTypeString = SvxResId(STR_IMAGE_PCT);
160 break;
161 case GfxLinkType::NativeSvg:
162 aGraphicTypeString = SvxResId(STR_IMAGE_SVG);
163 break;
164 case GfxLinkType::NativeBmp:
165 aGraphicTypeString = SvxResId(STR_IMAGE_BMP);
166 break;
167 case GfxLinkType::NativeWebp:
168 aGraphicTypeString = SvxResId(STR_IMAGE_WEBP);
169 break;
170 default:
171 break;
172 }
173 }
174 return aGraphicTypeString;
175}
176namespace {
177
178
179bool lcl_ExecuteFilterDialog( const Sequence< PropertyValue >& rPropsForDialog,
180 Sequence< PropertyValue >& rFilterData )
181{
182 bool bStatus = false;
183 try
184 {
185 Reference< XExecutableDialog > xFilterDialog(
186 comphelper::getProcessServiceFactory()->createInstance( "com.sun.star.svtools.SvFilterOptionsDialog" ), UNO_QUERY );
187 Reference< XPropertyAccess > xFilterProperties( xFilterDialog, UNO_QUERY );
188
189 if( xFilterDialog.is() && xFilterProperties.is() )
190 {
191 xFilterProperties->setPropertyValues( rPropsForDialog );
192 if( xFilterDialog->execute() )
193 {
194 bStatus = true;
195 const Sequence< PropertyValue > aPropsFromDialog = xFilterProperties->getPropertyValues();
196 for ( const auto& rProp : aPropsFromDialog )
197 {
198 if (rProp.Name == "FilterData")
199 {
200 rProp.Value >>= rFilterData;
201 }
202 }
203 }
204 }
205 }
206 catch( const NoSuchElementException& e )
207 {
208 // the filter name is unknown
209 throw ErrorCodeIOException(
210 ("lcl_ExecuteFilterDialog: NoSuchElementException"
211 " \"" + e.Message + "\": ERRCODE_IO_ABORT"),
212 Reference< XInterface >(), sal_uInt32(ERRCODE_IO_INVALIDPARAMETER));
213 }
214 catch( const ErrorCodeIOException& )
215 {
216 throw;
217 }
218 catch( const Exception& )
219 {
220 TOOLS_WARN_EXCEPTION("sfx.doc", "ignoring");
221 }
222
223 return bStatus;
224}
225} // anonymous ns
226
227OUString GraphicHelper::ExportGraphic(weld::Window* pParent, const Graphic& rGraphic, const OUString& rGraphicName)
228{
229 FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
230 Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
231
232 // fish out the graphic's name
233 aDialogHelper.SetContext(FileDialogHelper::ExportImage);
234 aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_EXPORT_GRAPHIC_TITLE));
236 aURL.SetSmartURL( rGraphicName );
237 aDialogHelper.SetFileName(aURL.GetLastName());
238
240 const sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
241
242 OUString aExtension(aURL.GetFileExtension());
243 if( aExtension.isEmpty() )
244 {
245 GetPreferredExtension( aExtension, rGraphic );
246 }
247
248 aExtension = aExtension.toAsciiLowerCase();
249 sal_uInt16 nDefaultFilter = USHRT_MAX;
250
251 for ( sal_uInt16 i = 0; i < nCount; i++ )
252 {
253 xFilePicker->appendFilter( rGraphicFilter.GetExportFormatName( i ), rGraphicFilter.GetExportWildcard( i ) );
254 OUString aFormatShortName = rGraphicFilter.GetExportFormatShortName( i );
255 if ( aFormatShortName.equalsIgnoreAsciiCase( aExtension ) )
256 {
257 nDefaultFilter = i;
258 }
259 }
260 if ( USHRT_MAX == nDefaultFilter )
261 {
262 // "wrong" extension?
263 GetPreferredExtension( aExtension, rGraphic );
264 for ( sal_uInt16 i = 0; i < nCount; ++i )
265 if ( aExtension == rGraphicFilter.GetExportFormatShortName( i ).toAsciiLowerCase() )
266 {
267 nDefaultFilter = i;
268 break;
269 }
270 }
271
272 if( USHRT_MAX != nDefaultFilter )
273 {
274 xFilePicker->setCurrentFilter( rGraphicFilter.GetExportFormatName( nDefaultFilter ) ) ;
275
276 if( aDialogHelper.Execute() == ERRCODE_NONE )
277 {
278 OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
279 if( !rGraphicName.isEmpty() &&
280 nDefaultFilter == rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter()))
281 {
282 // try to save the original graphic
283 SfxMedium aIn( rGraphicName, StreamMode::READ | StreamMode::NOCREATE );
284 if( aIn.GetInStream() && !aIn.GetInStream()->GetError() )
285 {
286 SfxMedium aOut( sPath, StreamMode::WRITE | StreamMode::SHARE_DENYNONE);
287 if( aOut.GetOutStream() && !aOut.GetOutStream()->GetError())
288 {
289 aOut.GetOutStream()->WriteStream( *aIn.GetInStream() );
290 if ( ERRCODE_NONE == aIn.GetError() )
291 {
292 aOut.Close();
293 aOut.Commit();
294 if ( ERRCODE_NONE == aOut.GetError() )
295 return sPath;
296 }
297 }
298 }
299 }
300
301 sal_uInt16 nFilter;
302 if ( !xFilePicker->getCurrentFilter().isEmpty() && rGraphicFilter.GetExportFormatCount() )
303 {
304 nFilter = rGraphicFilter.GetExportFormatNumber( xFilePicker->getCurrentFilter() );
305 }
306 else
307 {
308 nFilter = GRFILTER_FORMAT_DONTKNOW;
309 }
310 OUString aFilter( rGraphicFilter.GetExportFormatShortName( nFilter ) );
311
312 if ( rGraphic.GetType() == GraphicType::Bitmap )
313 {
314 Graphic aGraphic = rGraphic;
315 Reference<XGraphic> xGraphic = aGraphic.GetXGraphic();
316
317 OUString aExportFilter = rGraphicFilter.GetExportInternalFilterName(nFilter);
318
319 Sequence< PropertyValue > aPropsForDialog{
320 comphelper::makePropertyValue("Graphic", xGraphic),
321 comphelper::makePropertyValue("FilterName", aExportFilter)
322 };
323
324 Sequence< PropertyValue > aFilterData;
325 bool bStatus = lcl_ExecuteFilterDialog(aPropsForDialog, aFilterData);
326 if (bStatus)
327 {
328 sal_Int32 nWidth = 0;
329 sal_Int32 nHeight = 0;
330
331 for (const auto& rProp : std::as_const(aFilterData))
332 {
333 if (rProp.Name == "PixelWidth")
334 {
335 rProp.Value >>= nWidth;
336 }
337 else if (rProp.Name == "PixelHeight")
338 {
339 rProp.Value >>= nHeight;
340 }
341 }
342
343 // scaling must performed here because png/jpg writer s
344 // do not take care of that.
345 Size aSizePixel( aGraphic.GetSizePixel() );
346 if( nWidth && nHeight &&
347 ( ( nWidth != aSizePixel.Width() ) ||
348 ( nHeight != aSizePixel.Height() ) ) )
349 {
350 BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
351 // export: use highest quality
352 aBmpEx.Scale( Size( nWidth, nHeight ), BmpScaleFlag::Lanczos );
353 aGraphic = aBmpEx;
354 }
355
356 XOutBitmap::WriteGraphic( aGraphic, sPath, aFilter,
360 nullptr, &aFilterData );
361 return sPath;
362 }
363 }
364 else
365 {
366 XOutBitmap::WriteGraphic( rGraphic, sPath, aFilter,
370 }
371 }
372 }
373 return OUString();
374}
375
376void GraphicHelper::SaveShapeAsGraphicToPath(
377 const css::uno::Reference<css::lang::XComponent>& xComponent,
378 const css::uno::Reference<css::drawing::XShape>& xShape, const OUString& aExportMimeType,
379 const OUString& sPath)
380{
381 Reference<XComponentContext> xContext(::comphelper::getProcessComponentContext());
382 Reference<XInputStream> xGraphStream;
383
384 if (xGraphStream.is())
385 {
386 Reference<XSimpleFileAccess3> xFileAccess = SimpleFileAccess::create(xContext);
387 xFileAccess->writeFile(sPath, xGraphStream);
388 }
389 else if (xComponent.is() && aExportMimeType == "application/pdf")
390 {
391 css::uno::Reference<css::lang::XMultiServiceFactory> xMSF(xContext->getServiceManager(),
392 css::uno::UNO_QUERY);
393 css::uno::Reference<css::document::XExporter> xExporter(
394 xMSF->createInstance("com.sun.star.comp.PDF.PDFFilter"), css::uno::UNO_QUERY);
395 xExporter->setSourceDocument(xComponent);
396
397 css::uno::Reference<css::drawing::XShapes> xShapes
398 = css::drawing::ShapeCollection::create(comphelper::getProcessComponentContext());
399 xShapes->add(xShape);
400 css::uno::Sequence<PropertyValue> aFilterData{
401 comphelper::makePropertyValue("Selection", xShapes),
402 };
403 SvFileStream aStream(sPath, StreamMode::READWRITE | StreamMode::TRUNC);
404 css::uno::Reference<css::io::XOutputStream> xStream(new utl::OStreamWrapper(aStream));
405 css::uno::Sequence<PropertyValue> aDescriptor{
406 comphelper::makePropertyValue("FilterData", aFilterData),
407 comphelper::makePropertyValue("OutputStream", xStream)
408 };
409 css::uno::Reference<css::document::XFilter> xFilter(xExporter, css::uno::UNO_QUERY);
410 xFilter->filter(aDescriptor);
411 }
412 else
413 {
414 Reference<css::drawing::XGraphicExportFilter> xGraphicExporter
415 = css::drawing::GraphicExportFilter::create(xContext);
416
417 Sequence<PropertyValue> aDescriptor{ comphelper::makePropertyValue("MediaType",
418 aExportMimeType),
419 comphelper::makePropertyValue("URL", sPath) };
420
421 Reference<XComponent> xSourceDocument(xShape, UNO_QUERY_THROW);
422 xGraphicExporter->setSourceDocument(xSourceDocument);
423 xGraphicExporter->filter(aDescriptor);
424 }
425}
426
427void GraphicHelper::SaveShapeAsGraphic(weld::Window* pParent,
428 const css::uno::Reference<css::lang::XComponent>& xComponent,
429 const Reference<drawing::XShape>& xShape)
430{
431 try
432 {
433 Reference< XPropertySet > xShapeSet( xShape, UNO_QUERY_THROW );
434
435 FileDialogHelper aDialogHelper(TemplateDescription::FILESAVE_AUTOEXTENSION, FileDialogFlags::NONE, pParent);
436 Reference < XFilePicker3 > xFilePicker = aDialogHelper.GetFilePicker();
437 aDialogHelper.SetContext(FileDialogHelper::ExportImage);
438 aDialogHelper.SetTitle( SvxResId(RID_SVXSTR_SAVEAS_IMAGE) );
439
440 // populate filter dialog filter list and select default filter to match graphic mime type
441
443 static constexpr OUStringLiteral aDefaultMimeType(u"image/png");
444 OUString aDefaultFormatName;
445 sal_uInt16 nCount = rGraphicFilter.GetExportFormatCount();
446
447 std::map< OUString, OUString > aMimeTypeMap;
448
449 for ( sal_uInt16 i = 0; i < nCount; i++ )
450 {
451 const OUString aExportFormatName( rGraphicFilter.GetExportFormatName( i ) );
452 const OUString aFilterMimeType( rGraphicFilter.GetExportFormatMediaType( i ) );
453 xFilePicker->appendFilter( aExportFormatName, rGraphicFilter.GetExportWildcard( i ) );
454 aMimeTypeMap[ aExportFormatName ] = aFilterMimeType;
455 if( aDefaultMimeType == aFilterMimeType )
456 aDefaultFormatName = aExportFormatName;
457 }
458
459 if( !aDefaultFormatName.isEmpty() )
460 xFilePicker->setCurrentFilter( aDefaultFormatName );
461
462 // execute dialog
463
464 if( aDialogHelper.Execute() == ERRCODE_NONE )
465 {
466 OUString sPath( xFilePicker->getFiles().getConstArray()[0] );
467 OUString aExportMimeType( aMimeTypeMap[xFilePicker->getCurrentFilter()] );
468
469 GraphicHelper::SaveShapeAsGraphicToPath(xComponent, xShape, aExportMimeType, sPath);
470 }
471 }
472 catch( Exception& )
473 {
474 }
475}
476
477short GraphicHelper::HasToSaveTransformedImage(weld::Widget* pWin)
478{
479 OUString aMsg(SvxResId(RID_SVXSTR_SAVE_MODIFIED_IMAGE));
480 std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin,
481 VclMessageType::Question, VclButtonsType::YesNo, aMsg));
482 return xBox->run();
483}
484
485/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XInputStream > xStream
HRESULT createInstance(REFIID iid, Ifc **ppIfc)
static weld::MessageDialog * CreateMessageDialog(weld::Widget *pParent, VclMessageType eMessageType, VclButtonsType eButtonType, const OUString &rPrimaryMessage, const ILibreOfficeKitNotifier *pNotifier=nullptr)
OUString GetExportWildcard(sal_uInt16 nFormat)
static GraphicFilter & GetGraphicFilter()
sal_uInt16 GetExportFormatNumber(std::u16string_view rFormatName)
OUString GetExportFormatName(sal_uInt16 nFormat)
sal_uInt16 GetExportFormatCount() const
OUString GetExportFormatShortName(sal_uInt16 nFormat)
OUString GetExportFormatMediaType(sal_uInt16 nFormat)
OUString GetExportInternalFilterName(sal_uInt16 nFormat)
css::uno::Reference< css::graphic::XGraphic > GetXGraphic() const
const std::shared_ptr< GfxLink > & GetSharedGfxLink() const
GraphicType GetType() const
GfxLink GetGfxLink() const
BitmapEx GetBitmapEx(const GraphicConversionParameters &rParameters=GraphicConversionParameters()) const
Size GetSizePixel(const OutputDevice *pRefDevice=nullptr) const
const std::shared_ptr< VectorGraphicData > & getVectorGraphicData() 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, OUString *pMediaType=nullptr)
Definition: _xoutbmp.cxx:170
int nCount
#define TOOLS_WARN_EXCEPTION(area, stream)
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
URL aURL
#define GRFILTER_FORMAT_DONTKNOW
Reference< XMultiServiceFactory > getProcessServiceFactory()
Reference< XComponentContext > getProcessComponentContext()
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
int i
@ DontAddExtension
@ DontExpandFilename
@ UseNativeIfPossible