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
