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 <sal/config.h>
21 #include <sal/log.hxx>
22
23 #include <sal/macros.h>
24 #include <com/sun/star/embed/XTransactedObject.hpp>
25 #include <com/sun/star/embed/ElementModes.hpp>
26 #include <com/sun/star/beans/XPropertySet.hpp>
27 #include <com/sun/star/io/NotConnectedException.hpp>
28 #include <com/sun/star/lang/XServiceInfo.hpp>
29 #include <com/sun/star/lang/XInitialization.hpp>
30 #include <comphelper/fileformat.h>
31 #include <comphelper/graphicmimetype.hxx>
32 #include <cppuhelper/compbase.hxx>
33 #include <cppuhelper/implbase.hxx>
34 #include <cppuhelper/supportsservice.hxx>
35
36 #include <rtl/ref.hxx>
37 #include <unotools/ucbstreamhelper.hxx>
38 #include <unotools/streamwrap.hxx>
39 #include <unotools/tempfile.hxx>
40 #include <unotools/saveopt.hxx>
41 #include <vcl/cvtgrf.hxx>
42 #include <vcl/gfxlink.hxx>
43 #include <vcl/metaact.hxx>
44 #include <tools/zcodec.hxx>
45
46 #include <vcl/GraphicObject.hxx>
47 #include <vcl/graphicfilter.hxx>
48 #include <svx/xmlgrhlp.hxx>
49 #include <svx/xmleohlp.hxx>
50
51 #include <algorithm>
52 #include <memory>
53 #include <utility>
54
55 using namespace com::sun::star;
56 using namespace com::sun::star::uno;
57 using namespace com::sun::star::io;
58
59 namespace com { namespace sun { namespace star { namespace uno { class XComponentContext; } } } }
60
61 #define XML_GRAPHICSTORAGE_NAME "Pictures"
62 #define XML_GRAPHICOBJECT_URL_BASE "vnd.sun.star.GraphicObject:"
63
64 namespace {
65
ImplCheckForEPS(GDIMetaFile const & rMtf)66 const MetaCommentAction* ImplCheckForEPS( GDIMetaFile const & rMtf )
67 {
68 const MetaCommentAction* pComment = nullptr;
69
70 if ( rMtf.GetActionSize() >= 2
71 && rMtf.GetAction(0)->GetType() == MetaActionType::EPS
72 && rMtf.GetAction(1)->GetType() == MetaActionType::COMMENT
73 && ( static_cast<const MetaCommentAction*>(rMtf.GetAction( 1 ))->GetComment() == "EPSReplacementGraphic" ) )
74 pComment = static_cast<const MetaCommentAction*>(rMtf.GetAction( 1 ));
75
76 return pComment;
77 }
78
79 namespace xmloff {
80
81 class GraphicInputStream : public cppu::WeakImplHelper<XInputStream>
82 {
83 private:
84 virtual sal_Int32 SAL_CALL readBytes(Sequence<sal_Int8> & aData, sal_Int32 nBytesToRead) override;
85 virtual sal_Int32 SAL_CALL readSomeBytes(Sequence<sal_Int8> & aData, sal_Int32 nMaxBytesToRead) override;
86 virtual void SAL_CALL skipBytes(sal_Int32 nBytesToSkip) override;
87 virtual sal_Int32 SAL_CALL available() override;
88 virtual void SAL_CALL closeInput() override;
89
90 private:
91 utl::TempFile maTempFile;
92 Reference<XInputStream> mxStreamWrapper;
93
94 public:
95
96 explicit GraphicInputStream(GraphicObject const & raGraphicObject, const OUString & rMimeType);
97 GraphicInputStream(const GraphicInputStream&) = delete;
98
99 GraphicInputStream& operator=(const GraphicInputStream&) = delete;
100
exists() const101 bool exists() const
102 {
103 return mxStreamWrapper.is();
104 }
105 };
106
107
GraphicInputStream(GraphicObject const & aGraphicObject,const OUString & rMimeType)108 GraphicInputStream::GraphicInputStream(GraphicObject const & aGraphicObject, const OUString & rMimeType)
109 {
110 maTempFile.EnableKillingFile();
111
112 if (aGraphicObject.GetType() != GraphicType::NONE)
113 {
114 std::unique_ptr<SvStream> pStream = ::utl::UcbStreamHelper::CreateStream(maTempFile.GetURL(), StreamMode::WRITE | StreamMode::TRUNC);
115
116 if (pStream)
117 {
118 const Graphic& aGraphic(aGraphicObject.GetGraphic());
119 const GfxLink aGfxLink(aGraphic.GetGfxLink());
120 bool bRet = false;
121
122 if (aGfxLink.GetDataSize() && aGfxLink.GetData())
123 {
124 if (rMimeType.isEmpty())
125 {
126 pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
127 bRet = (pStream->GetError() == ERRCODE_NONE);
128 }
129 else
130 {
131 GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
132 bRet = (rFilter.ExportGraphic(aGraphic, "", *pStream, rFilter.GetExportFormatNumberForMediaType(rMimeType)) == ERRCODE_NONE);
133 }
134 }
135 else
136 {
137 if (aGraphic.GetType() == GraphicType::Bitmap)
138 {
139 GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
140 OUString aFormat = rMimeType;
141
142 if (aGraphic.IsAnimated())
143 aFormat = "image/gif";
144 else if (aFormat.isEmpty())
145 aFormat = "image/png";
146
147 bRet = (rFilter.ExportGraphic(aGraphic, "", *pStream, rFilter.GetExportFormatNumberForMediaType(aFormat)) == ERRCODE_NONE);
148 }
149 else if (rMimeType.isEmpty() && aGraphic.GetType() == GraphicType::GdiMetafile)
150 {
151 pStream->SetVersion(SOFFICE_FILEFORMAT_8);
152 pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
153 const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()).Write(*pStream);
154 bRet = (pStream->GetError() == ERRCODE_NONE);
155 }
156 else if (!rMimeType.isEmpty())
157 {
158 GraphicFilter & rFilter = GraphicFilter::GetGraphicFilter();
159 bRet = ( rFilter.ExportGraphic( aGraphic, "", *pStream, rFilter.GetExportFormatNumberForMediaType( rMimeType ) ) == ERRCODE_NONE );
160 }
161 }
162
163 if (bRet)
164 {
165 pStream->Seek( 0 );
166 mxStreamWrapper = new ::utl::OInputStreamWrapper(std::move(pStream));
167 }
168 }
169 }
170 }
171
readBytes(Sequence<sal_Int8> & rData,sal_Int32 nBytesToRead)172 sal_Int32 SAL_CALL GraphicInputStream::readBytes(Sequence<sal_Int8> & rData, sal_Int32 nBytesToRead)
173 {
174 if (!mxStreamWrapper.is())
175 throw NotConnectedException();
176
177 return mxStreamWrapper->readBytes(rData, nBytesToRead);
178 }
179
readSomeBytes(Sequence<sal_Int8> & rData,sal_Int32 nMaxBytesToRead)180 sal_Int32 SAL_CALL GraphicInputStream::readSomeBytes(Sequence<sal_Int8>& rData, sal_Int32 nMaxBytesToRead )
181 {
182 if (!mxStreamWrapper.is())
183 throw NotConnectedException() ;
184
185 return mxStreamWrapper->readSomeBytes(rData, nMaxBytesToRead);
186 }
187
skipBytes(sal_Int32 nBytesToSkip)188 void SAL_CALL GraphicInputStream::skipBytes(sal_Int32 nBytesToSkip)
189 {
190 if (!mxStreamWrapper.is())
191 throw NotConnectedException();
192
193 mxStreamWrapper->skipBytes(nBytesToSkip);
194 }
195
available()196 sal_Int32 SAL_CALL GraphicInputStream::available()
197 {
198 if (!mxStreamWrapper.is())
199 throw NotConnectedException();
200
201 return mxStreamWrapper->available();
202 }
203
closeInput()204 void SAL_CALL GraphicInputStream::closeInput()
205 {
206 if (!mxStreamWrapper.is())
207 throw NotConnectedException();
208
209 mxStreamWrapper->closeInput();
210 }
211
212 } // end xmloff namespace
213
214 class SvXMLGraphicOutputStream:
215 public cppu::WeakImplHelper<XOutputStream>
216 {
217 private:
218
219 // XOutputStream
220 virtual void SAL_CALL writeBytes( const Sequence< sal_Int8 >& rData ) override;
221 virtual void SAL_CALL flush() override;
222 virtual void SAL_CALL closeOutput() override;
223
224 private:
225
226 std::unique_ptr<::utl::TempFile> mpTmp;
227 std::unique_ptr<SvStream> mpOStm;
228 Reference< XOutputStream > mxStmWrapper;
229 std::unique_ptr<GraphicObject> mxGrfObj;
230 bool mbClosed;
231
232 public:
233
234 SvXMLGraphicOutputStream();
235 virtual ~SvXMLGraphicOutputStream() override;
236 SvXMLGraphicOutputStream(const SvXMLGraphicOutputStream&) = delete;
237 SvXMLGraphicOutputStream& operator=(const SvXMLGraphicOutputStream&) = delete;
238
Exists() const239 bool Exists() const { return mxStmWrapper.is(); }
240 const GraphicObject& GetGraphicObject();
241 Graphic GetGraphic();
242 };
243
SvXMLGraphicOutputStream()244 SvXMLGraphicOutputStream::SvXMLGraphicOutputStream()
245 : mpTmp(new ::utl::TempFile)
246 , mxGrfObj(new GraphicObject)
247 , mbClosed(false)
248 {
249 mpTmp->EnableKillingFile();
250
251 mpOStm = ::utl::UcbStreamHelper::CreateStream( mpTmp->GetURL(), StreamMode::WRITE | StreamMode::TRUNC );
252
253 if( mpOStm )
254 mxStmWrapper = new ::utl::OOutputStreamWrapper( *mpOStm );
255 }
256
~SvXMLGraphicOutputStream()257 SvXMLGraphicOutputStream::~SvXMLGraphicOutputStream()
258 {
259 mpTmp.reset();
260 mpOStm.reset();
261 }
262
writeBytes(const Sequence<sal_Int8> & rData)263 void SAL_CALL SvXMLGraphicOutputStream::writeBytes( const Sequence< sal_Int8 >& rData )
264 {
265 if( !mxStmWrapper.is() )
266 throw NotConnectedException() ;
267
268 mxStmWrapper->writeBytes( rData );
269 }
270
flush()271 void SAL_CALL SvXMLGraphicOutputStream::flush()
272 {
273 if( !mxStmWrapper.is() )
274 throw NotConnectedException() ;
275
276 mxStmWrapper->flush();
277 }
278
closeOutput()279 void SAL_CALL SvXMLGraphicOutputStream::closeOutput()
280 {
281 if( !mxStmWrapper.is() )
282 throw NotConnectedException() ;
283
284 mxStmWrapper->closeOutput();
285 mxStmWrapper.clear();
286
287 mbClosed = true;
288 }
289
GetGraphic()290 Graphic SvXMLGraphicOutputStream::GetGraphic()
291 {
292 Graphic aGraphic;
293
294 if (mbClosed && mxGrfObj->GetType() == GraphicType::NONE && mpOStm)
295 {
296 mpOStm->Seek( 0 );
297 sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
298 sal_uInt16 nDeterminedFormat = GRFILTER_FORMAT_DONTKNOW;
299 GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, "", *mpOStm ,nFormat,&nDeterminedFormat);
300
301 if (nDeterminedFormat == GRFILTER_FORMAT_DONTKNOW)
302 {
303 //Read the first two byte to check whether it is a gzipped stream, is so it may be in wmz or emz format
304 //unzip them and try again
305
306 sal_uInt8 sFirstBytes[ 2 ];
307
308 sal_uIntPtr nStreamLen = mpOStm->TellEnd();
309 mpOStm->Seek( 0 );
310
311 if ( !nStreamLen )
312 {
313 SvLockBytes* pLockBytes = mpOStm->GetLockBytes();
314 if ( pLockBytes )
315 pLockBytes->SetSynchronMode();
316
317 nStreamLen = mpOStm->TellEnd();
318 mpOStm->Seek( 0 );
319 }
320 if( nStreamLen >= 2 )
321 {
322 //read two byte
323 mpOStm->ReadBytes(sFirstBytes, 2);
324
325 if( sFirstBytes[0] == 0x1f && sFirstBytes[1] == 0x8b )
326 {
327 std::unique_ptr<SvMemoryStream> pDest(new SvMemoryStream);
328 ZCodec aZCodec( 0x8000, 0x8000 );
329 aZCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
330 mpOStm->Seek( 0 );
331 aZCodec.Decompress( *mpOStm, *pDest );
332
333 if (aZCodec.EndCompression())
334 {
335 sal_uIntPtr nStreamLen_ = pDest->TellEnd();
336 if (nStreamLen_)
337 {
338 pDest->Seek(0);
339 GraphicFilter::GetGraphicFilter().ImportGraphic( aGraphic, "", *pDest ,nFormat,&nDeterminedFormat );
340 }
341 }
342 }
343 }
344 }
345 }
346
347 if (aGraphic.GetType() != GraphicType::NONE)
348 {
349 mpOStm.reset();
350 mpTmp.reset();
351 }
352 return aGraphic;
353 }
354
GetGraphicObject()355 const GraphicObject& SvXMLGraphicOutputStream::GetGraphicObject()
356 {
357 Graphic aGraphic(GetGraphic());
358 if (aGraphic.GetType() != GraphicType::NONE)
359 {
360 mxGrfObj.reset(new GraphicObject(aGraphic));
361 }
362 return *mxGrfObj;
363 }
364
365 }
366
SvXMLGraphicHelper(SvXMLGraphicHelperMode eCreateMode)367 SvXMLGraphicHelper::SvXMLGraphicHelper(SvXMLGraphicHelperMode eCreateMode)
368 : cppu::WeakComponentImplHelper<document::XGraphicObjectResolver,
369 document::XGraphicStorageHandler,
370 document::XBinaryStreamResolver>(maMutex)
371 {
372 Init( nullptr, eCreateMode );
373 }
374
SvXMLGraphicHelper()375 SvXMLGraphicHelper::SvXMLGraphicHelper()
376 : cppu::WeakComponentImplHelper<document::XGraphicObjectResolver,
377 document::XGraphicStorageHandler,
378 document::XBinaryStreamResolver>(maMutex)
379 , meCreateMode(SvXMLGraphicHelperMode::Read)
380 {
381 }
382
~SvXMLGraphicHelper()383 SvXMLGraphicHelper::~SvXMLGraphicHelper()
384 {
385 }
386
disposing()387 void SAL_CALL SvXMLGraphicHelper::disposing()
388 {
389 }
390
ImplGetStreamNames(const OUString & rURLStr,OUString & rPictureStorageName,OUString & rPictureStreamName)391 bool SvXMLGraphicHelper::ImplGetStreamNames( const OUString& rURLStr,
392 OUString& rPictureStorageName,
393 OUString& rPictureStreamName )
394 {
395 if (rURLStr.isEmpty())
396 return false;
397
398 const OUString aURLStr {rURLStr.copy(rURLStr.lastIndexOf(':')+1)};
399
400 if( !aURLStr.isEmpty() && aURLStr.indexOf('/')<0 ) // just one token?
401 {
402 rPictureStorageName = XML_GRAPHICSTORAGE_NAME;
403 rPictureStreamName = aURLStr;
404 }
405 else
406 SvXMLEmbeddedObjectHelper::splitObjectURL(aURLStr, rPictureStorageName, rPictureStreamName);
407
408 SAL_WARN_IF(rPictureStreamName.isEmpty(), "svx", "SvXMLGraphicHelper::ImplInsertGraphicURL: invalid scheme: " << rURLStr);
409
410 return !rPictureStreamName.isEmpty();
411 }
412
ImplGetGraphicStorage(const OUString & rStorageName)413 uno::Reference < embed::XStorage > SvXMLGraphicHelper::ImplGetGraphicStorage( const OUString& rStorageName )
414 {
415 uno::Reference < embed::XStorage > xRetStorage;
416 if( mxRootStorage.is() )
417 {
418 try
419 {
420 maCurStorageName = rStorageName;
421 xRetStorage = mxRootStorage->openStorageElement(
422 maCurStorageName,
423 ( SvXMLGraphicHelperMode::Write == meCreateMode )
424 ? embed::ElementModes::READWRITE
425 : embed::ElementModes::READ );
426 }
427 catch ( uno::Exception& )
428 {
429 }
430 //#i43196# try again to open the storage element - this time readonly
431 if(!xRetStorage.is())
432 {
433 try
434 {
435 maCurStorageName = rStorageName;
436 xRetStorage = mxRootStorage->openStorageElement( maCurStorageName, embed::ElementModes::READ );
437 }
438 catch ( uno::Exception& )
439 {
440 }
441 }
442 }
443
444 return xRetStorage;
445 }
446
ImplGetGraphicStream(const OUString & rPictureStorageName,const OUString & rPictureStreamName)447 SvxGraphicHelperStream_Impl SvXMLGraphicHelper::ImplGetGraphicStream( const OUString& rPictureStorageName,
448 const OUString& rPictureStreamName )
449 {
450 SvxGraphicHelperStream_Impl aRet;
451 aRet.xStorage = ImplGetGraphicStorage( rPictureStorageName );
452
453 if( aRet.xStorage.is() )
454 {
455 sal_Int32 nMode = embed::ElementModes::READ;
456 if ( SvXMLGraphicHelperMode::Write == meCreateMode )
457 {
458 nMode = embed::ElementModes::READWRITE;
459 }
460
461 aRet.xStream = aRet.xStorage->openStreamElement( rPictureStreamName, nMode );
462 if( aRet.xStream.is() && ( SvXMLGraphicHelperMode::Write == meCreateMode ) )
463 {
464 uno::Reference < beans::XPropertySet > xProps( aRet.xStream, uno::UNO_QUERY );
465 xProps->setPropertyValue( "UseCommonStoragePasswordEncryption", uno::makeAny( true) );
466 }
467 }
468
469 return aRet;
470 }
471
ImplGetGraphicMimeType(const OUString & rFileName)472 OUString SvXMLGraphicHelper::ImplGetGraphicMimeType( const OUString& rFileName )
473 {
474 if( ( rFileName.getLength() >= 4 ) && ( rFileName[ rFileName.getLength() - 4 ] == '.' ) )
475 {
476 const OString aExt(OUStringToOString(rFileName.copy(rFileName.getLength() - 3),
477 RTL_TEXTENCODING_ASCII_US));
478 return comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension( aExt );
479 }
480
481 return OUString();
482 }
483
ImplReadGraphic(const OUString & rPictureStorageName,const OUString & rPictureStreamName)484 Graphic SvXMLGraphicHelper::ImplReadGraphic( const OUString& rPictureStorageName,
485 const OUString& rPictureStreamName )
486 {
487 Graphic aReturnGraphic;
488 SvxGraphicHelperStream_Impl aStream( ImplGetGraphicStream( rPictureStorageName, rPictureStreamName ) );
489 if (aStream.xStream.is())
490 {
491 GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter();
492 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
493 Graphic aGraphic = rGraphicFilter.ImportUnloadedGraphic(*pStream);
494 if (!aGraphic.IsNone())
495 aReturnGraphic = aGraphic;
496 else
497 rGraphicFilter.ImportGraphic(aReturnGraphic, "", *pStream);
498 }
499
500 return aReturnGraphic;
501 }
502
Init(const uno::Reference<embed::XStorage> & rXMLStorage,SvXMLGraphicHelperMode eCreateMode,const OUString & rGraphicMimeType)503 void SvXMLGraphicHelper::Init( const uno::Reference < embed::XStorage >& rXMLStorage,
504 SvXMLGraphicHelperMode eCreateMode,
505 const OUString& rGraphicMimeType )
506 {
507 mxRootStorage = rXMLStorage;
508 meCreateMode = eCreateMode;
509 maOutputMimeType = rGraphicMimeType;
510 }
511
Create(const uno::Reference<embed::XStorage> & rXMLStorage,SvXMLGraphicHelperMode eCreateMode)512 rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( const uno::Reference < embed::XStorage >& rXMLStorage,
513 SvXMLGraphicHelperMode eCreateMode )
514 {
515 rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
516 pThis->Init( rXMLStorage, eCreateMode, OUString() );
517
518 return pThis;
519 }
520
Create(SvXMLGraphicHelperMode eCreateMode,const OUString & rGraphicMimeType)521 rtl::Reference<SvXMLGraphicHelper> SvXMLGraphicHelper::Create( SvXMLGraphicHelperMode eCreateMode,
522 const OUString& rGraphicMimeType )
523 {
524 rtl::Reference<SvXMLGraphicHelper> pThis = new SvXMLGraphicHelper;
525
526 pThis->Init( nullptr, eCreateMode, rGraphicMimeType );
527
528 return pThis;
529 }
530
531 namespace
532 {
533
splitUserDataFromURL(OUString const & rWholeURL,OUString & rJustURL,OUString & rUserData)534 void splitUserDataFromURL(OUString const & rWholeURL, OUString & rJustURL, OUString & rUserData)
535 {
536 sal_Int32 nUser = rWholeURL.indexOf('?');
537 if (nUser >= 0)
538 {
539 rJustURL = rWholeURL.copy(0, nUser);
540 nUser++;
541 rUserData = rWholeURL.copy(nUser);
542 }
543 else
544 {
545 rJustURL = rWholeURL;
546 }
547 }
548
549 } // end anonymous namespace
550
551 // XGraphicObjectResolver
resolveGraphicObjectURL(const OUString &)552 OUString SAL_CALL SvXMLGraphicHelper::resolveGraphicObjectURL( const OUString& /*rURL*/ )
553 {
554 throw uno::RuntimeException("XGraphicObjectResolver has been removed in LibreOffice 6.1");
555 }
556
557 // XGraphicStorageHandler
loadGraphic(OUString const & rURL)558 uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphic(OUString const & rURL)
559 {
560 osl::MutexGuard aGuard(maMutex);
561
562 uno::Reference<graphic::XGraphic> xGraphic;
563
564 OUString aURLOnly;
565 OUString aUserData;
566 splitUserDataFromURL(rURL, aURLOnly, aUserData);
567
568 auto aIterator = maGraphicObjects.find(aURLOnly);
569 if (aIterator != maGraphicObjects.end())
570 {
571 return aIterator->second;
572 }
573
574 OUString aPictureStorageName, aPictureStreamName;
575
576 if (ImplGetStreamNames(aURLOnly, aPictureStorageName, aPictureStreamName))
577 {
578 const GraphicObject aGraphicObject(ImplReadGraphic(aPictureStorageName, aPictureStreamName));
579
580 if (aGraphicObject.GetType() != GraphicType::NONE)
581 {
582 xGraphic = aGraphicObject.GetGraphic().GetXGraphic();
583 maGraphicObjects[aURLOnly] = xGraphic;
584 }
585 }
586
587 return xGraphic;
588 }
589
loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)590 uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
591 {
592 osl::MutexGuard aGuard(maMutex);
593
594 uno::Reference<graphic::XGraphic> xGraphic;
595
596 if ((SvXMLGraphicHelperMode::Read == meCreateMode) && rxOutputStream.is())
597 {
598
599 SvXMLGraphicOutputStream* pGraphicOutputStream = static_cast<SvXMLGraphicOutputStream*>(rxOutputStream.get());
600 if (pGraphicOutputStream)
601 {
602 xGraphic = pGraphicOutputStream->GetGraphic().GetXGraphic();
603 }
604 }
605 return xGraphic;
606 }
607
saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,OUString & rOutSavedMimeType,OUString const & rRequestName)608 OUString SAL_CALL SvXMLGraphicHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
609 OUString & rOutSavedMimeType, OUString const & rRequestName)
610 {
611 return implSaveGraphic(rxGraphic, rOutSavedMimeType, rRequestName);
612 }
613
saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)614 OUString SAL_CALL SvXMLGraphicHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
615 {
616 OUString aOutMimeType;
617 return implSaveGraphic(rxGraphic, aOutMimeType, OUString());
618 }
619
implSaveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,OUString & rOutSavedMimeType,OUString const & rRequestName)620 OUString SvXMLGraphicHelper::implSaveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
621 OUString & rOutSavedMimeType, OUString const & rRequestName)
622 {
623 Graphic aGraphic(rxGraphic);
624
625 auto aIterator = maExportGraphics.find(aGraphic);
626 if (aIterator != maExportGraphics.end())
627 {
628 auto const & aURLAndMimePair = aIterator->second;
629 rOutSavedMimeType = aURLAndMimePair.second;
630 return aURLAndMimePair.first;
631 }
632
633 GraphicObject aGraphicObject(aGraphic);
634
635 if (aGraphicObject.GetType() != GraphicType::NONE)
636 {
637 const GfxLink aGfxLink(aGraphic.GetGfxLink());
638 OUString aExtension;
639 bool bUseGfxLink = true;
640
641 if (aGfxLink.GetDataSize())
642 {
643 switch (aGfxLink.GetType())
644 {
645 case GfxLinkType::EpsBuffer: aExtension = ".eps"; break;
646 case GfxLinkType::NativeGif: aExtension = ".gif"; break;
647 // #i15508# added BMP type for better exports (checked, works)
648 case GfxLinkType::NativeBmp: aExtension = ".bmp"; break;
649 case GfxLinkType::NativeJpg: aExtension = ".jpg"; break;
650 case GfxLinkType::NativePng: aExtension = ".png"; break;
651 case GfxLinkType::NativeTif: aExtension = ".tif"; break;
652 case GfxLinkType::NativeWmf:
653 if (aGfxLink.IsEMF())
654 aExtension = ".emf";
655 else
656 aExtension = ".wmf";
657 break;
658 case GfxLinkType::NativeMet: aExtension = ".met"; break;
659 case GfxLinkType::NativePct: aExtension = ".pct"; break;
660 case GfxLinkType::NativeSvg:
661 // backward-compat kludge: since no released OOo
662 // version to date can handle svg properly, wrap it up
663 // into an svm. slight catch22 here, since strict ODF
664 // conformance _recommends_ svg - then again, most old
665 // ODF consumers are believed to be OOo
666 if (SvtSaveOptions().GetODFDefaultVersion() <= SvtSaveOptions::ODFVER_012)
667 {
668 bUseGfxLink = false;
669 aExtension = ".svm";
670 }
671 else
672 {
673 aExtension = ".svg";
674 }
675 break;
676 case GfxLinkType::NativePdf: aExtension = ".pdf"; break;
677
678 default:
679 aExtension = ".grf";
680 break;
681 }
682 }
683 else
684 {
685 if (aGraphicObject.GetType() == GraphicType::Bitmap)
686 {
687 if (aGraphicObject.IsAnimated())
688 aExtension = ".gif";
689 else
690 aExtension = ".png";
691 }
692 else if (aGraphicObject.GetType() == GraphicType::GdiMetafile)
693 {
694 // SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
695 GDIMetaFile& rMetafile(const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
696
697 if (ImplCheckForEPS(rMetafile))
698 aExtension = ".eps";
699 else
700 aExtension = ".svm";
701 }
702 }
703
704 OUString rPictureStreamName;
705 if (!rRequestName.isEmpty())
706 {
707 rPictureStreamName = rRequestName + aExtension;
708 }
709 else
710 {
711 OUString sId = OStringToOUString(aGraphicObject.GetUniqueID(), RTL_TEXTENCODING_ASCII_US);
712 rPictureStreamName = sId + aExtension;
713 }
714
715 SvxGraphicHelperStream_Impl aStream(ImplGetGraphicStream(XML_GRAPHICSTORAGE_NAME, rPictureStreamName));
716
717 if (aStream.xStream.is())
718 {
719 const OUString aMimeType(ImplGetGraphicMimeType(rPictureStreamName));
720 uno::Reference<beans::XPropertySet> xProps(aStream.xStream, uno::UNO_QUERY);
721
722 // set stream properties (MediaType/Compression)
723 if (!aMimeType.isEmpty())
724 {
725 xProps->setPropertyValue("MediaType", uno::Any(aMimeType));
726 }
727
728 // picture formats that actually _do_ benefit from zip
729 // storage compression
730 // .svm pics gets compressed via ZBITMAP old-style stream
731 // option below
732 static const char* aCompressiblePics[] =
733 {
734 "image/svg+xml",
735 "image/x-emf",
736 "image/x-wmf",
737 "image/tiff",
738 "image/x-eps",
739 "image/bmp",
740 "image/x-pict"
741 };
742
743 bool bSuccess = false;
744
745 bool bCompressed = aMimeType.isEmpty();
746 if( !bCompressed )
747 {
748 for(const char* p : aCompressiblePics)
749 {
750 if( aMimeType.equalsIgnoreAsciiCaseAscii(p) )
751 {
752 bCompressed = true;
753 break;
754 }
755 }
756 }
757
758 xProps->setPropertyValue("Compressed", Any(bCompressed));
759
760 std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(aStream.xStream));
761 if (bUseGfxLink && aGfxLink.GetDataSize() && aGfxLink.GetData())
762 {
763 const std::shared_ptr<std::vector<sal_Int8>> rPdfData = aGraphic.getPdfData();
764 if (rPdfData && !rPdfData->empty())
765 {
766 // See if we have this PDF already, and avoid duplicate storage.
767 auto aIt = maExportPdf.find(rPdfData.get());
768 if (aIt != maExportPdf.end())
769 {
770 auto const& aURLAndMimePair = aIt->second;
771 rOutSavedMimeType = aURLAndMimePair.second;
772 return aURLAndMimePair.first;
773 }
774
775 // The graphic has PDF data attached to it, use that.
776 // vcl::ImportPDF() possibly downgraded the PDF data from a
777 // higher PDF version, while aGfxLink still contains the
778 // original data provided by the user.
779 pStream->WriteBytes(rPdfData->data(), rPdfData->size());
780 }
781 else
782 {
783 pStream->WriteBytes(aGfxLink.GetData(), aGfxLink.GetDataSize());
784 }
785
786 rOutSavedMimeType = aMimeType;
787 bSuccess = (pStream->GetError() == ERRCODE_NONE);
788 }
789 else
790 {
791 if (aGraphic.GetType() == GraphicType::Bitmap)
792 {
793 GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
794 OUString aFormat;
795
796 if (aGraphic.IsAnimated())
797 {
798 aFormat = "gif";
799 }
800 else
801 {
802 aFormat = "png";
803 }
804 rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(aFormat.toUtf8());
805
806 bSuccess = (rFilter.ExportGraphic(aGraphic, "", *pStream, rFilter.GetExportFormatNumberForShortName(aFormat)) == ERRCODE_NONE);
807 }
808 else if (aGraphic.GetType() == GraphicType::GdiMetafile)
809 {
810 pStream->SetVersion(SOFFICE_FILEFORMAT_8);
811 pStream->SetCompressMode(SvStreamCompressFlags::ZBITMAP);
812 rOutSavedMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension("svm");
813
814 // SJ: first check if this metafile is just an eps file, then we will store the eps instead of svm
815 GDIMetaFile& rMtf(const_cast<GDIMetaFile&>(aGraphic.GetGDIMetaFile()));
816 const MetaCommentAction* pComment = ImplCheckForEPS(rMtf);
817 if (pComment)
818 {
819 sal_uInt32 nSize = pComment->GetDataSize();
820 const sal_uInt8* pData = pComment->GetData();
821 if (nSize && pData)
822 pStream->WriteBytes(pData, nSize);
823
824 const MetaEPSAction* pAct = static_cast<const MetaEPSAction*>(rMtf.FirstAction());
825 const GfxLink& rLink = pAct->GetLink();
826
827 pStream->WriteBytes(rLink.GetData(), rLink.GetDataSize());
828 }
829 else
830 {
831 rMtf.Write(*pStream);
832 }
833
834 bSuccess = (pStream->GetError() == ERRCODE_NONE);
835 }
836 }
837
838 if (!bSuccess)
839 return OUString();
840
841 uno::Reference<embed::XTransactedObject> xStorage(aStream.xStorage, uno::UNO_QUERY);
842 pStream.reset();
843 aStream.xStream->getOutputStream()->closeOutput();
844 if (xStorage.is())
845 xStorage->commit();
846
847 OUString aStoragePath = "Pictures/" + rPictureStreamName;
848
849 // put into cache
850 maExportGraphics[aGraphic] = std::make_pair(aStoragePath, rOutSavedMimeType);
851 if (aGraphic.hasPdfData())
852 maExportPdf[aGraphic.getPdfData().get()] = std::make_pair(aStoragePath, rOutSavedMimeType);
853
854 return aStoragePath;
855 }
856 }
857
858 return OUString();
859 }
860
createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)861 uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
862 {
863 Reference<XInputStream> xInputStream;
864
865 Graphic aGraphic(rxGraphic);
866 GraphicObject aGraphicObject(aGraphic);
867
868 if (SvXMLGraphicHelperMode::Write == meCreateMode)
869 {
870 OUString sMimeType = comphelper::GraphicMimeTypeHelper::GetMimeTypeForExtension(OUStringToOString(maOutputMimeType, RTL_TEXTENCODING_ASCII_US));
871 std::unique_ptr<xmloff::GraphicInputStream> pInputStream(new xmloff::GraphicInputStream(aGraphicObject, sMimeType));
872
873 // We release the pointer from unique_ptr and assign it to the input stream return type.
874 // In case the stream doesn't exists, unique_ptr will delete the pointer when we go out of scope.
875 if (pInputStream->exists())
876 xInputStream = pInputStream.release();
877 }
878
879 return xInputStream;
880 }
881
882 // XBinaryStreamResolver
getInputStream(const OUString &)883 Reference< XInputStream > SAL_CALL SvXMLGraphicHelper::getInputStream( const OUString& /*rURL*/ )
884 {
885 Reference<XInputStream> xRet;
886 return xRet;
887 }
888
createOutputStream()889 Reference< XOutputStream > SAL_CALL SvXMLGraphicHelper::createOutputStream()
890 {
891 Reference< XOutputStream > xRet;
892
893 if( SvXMLGraphicHelperMode::Read == meCreateMode )
894 {
895 std::unique_ptr<SvXMLGraphicOutputStream> pOutputStream(new SvXMLGraphicOutputStream);
896
897 if( pOutputStream->Exists() )
898 {
899 xRet = pOutputStream.release();
900 maGrfStms.push_back( xRet );
901 }
902 }
903
904 return xRet;
905 }
906
resolveOutputStream(const Reference<XOutputStream> & rxBinaryStream)907 OUString SAL_CALL SvXMLGraphicHelper::resolveOutputStream( const Reference< XOutputStream >& rxBinaryStream )
908 {
909 OUString aRet;
910
911 if( ( SvXMLGraphicHelperMode::Read == meCreateMode ) && rxBinaryStream.is() )
912 {
913 if( ::std::find( maGrfStms.begin(), maGrfStms.end(), rxBinaryStream ) != maGrfStms.end() )
914 {
915 SvXMLGraphicOutputStream* pOStm = static_cast< SvXMLGraphicOutputStream* >( rxBinaryStream.get() );
916
917 if( pOStm )
918 {
919 const GraphicObject& rGrfObj = pOStm->GetGraphicObject();
920 const OUString aId(OStringToOUString(
921 rGrfObj.GetUniqueID(), RTL_TEXTENCODING_ASCII_US));
922
923 if( !aId.isEmpty() )
924 {
925 aRet = XML_GRAPHICOBJECT_URL_BASE + aId;
926 }
927 }
928 }
929 }
930
931 return aRet;
932 }
933
934 // for instantiation via service manager
935 namespace {
936
937 namespace impl
938 {
939 typedef cppu::WeakComponentImplHelper<lang::XInitialization,
940 document::XGraphicObjectResolver,
941 document::XGraphicStorageHandler,
942 document::XBinaryStreamResolver,
943 lang::XServiceInfo>
944 SvXMLGraphicImportExportHelper_Base;
945
946 class MutexContainer
947 {
948 public:
949 virtual ~MutexContainer();
950
951 protected:
952 mutable ::osl::Mutex m_aMutex;
953 };
954
~MutexContainer()955 MutexContainer::~MutexContainer()
956 {}
957
958 } // namespace impl
959
960 class SvXMLGraphicImportExportHelper :
961 public impl::MutexContainer,
962 public impl::SvXMLGraphicImportExportHelper_Base
963 {
964 public:
965 explicit SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode );
966
967 protected:
968 // is called from WeakComponentImplHelper when XComponent::dispose() was
969 // called from outside
970 virtual void SAL_CALL disposing() override;
971
972 // ____ XInitialization ____
973 // one argument is allowed, which is the XStorage
974 virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override;
975
976 // ____ XGraphicObjectResolver ____
977 virtual OUString SAL_CALL resolveGraphicObjectURL( const OUString& aURL ) override;
978
979 // ____ XGraphicStorageHandler ____
980 virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
981 loadGraphic(const OUString& aURL) override;
982
983 virtual css::uno::Reference<css::graphic::XGraphic> SAL_CALL
984 loadGraphicFromOutputStream(css::uno::Reference<css::io::XOutputStream> const & rxOutputStream) override;
985
986 virtual OUString SAL_CALL
987 saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
988
989 virtual OUString SAL_CALL
990 saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic, OUString & rOutSavedMimeType, OUString const & rRequestName) override;
991
992 virtual css::uno::Reference<css::io::XInputStream> SAL_CALL
993 createInputStream(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic) override;
994
995 // ____ XBinaryStreamResolver ____
996 virtual Reference< io::XInputStream > SAL_CALL getInputStream( const OUString& aURL ) override;
997 virtual Reference< io::XOutputStream > SAL_CALL createOutputStream() override;
998 virtual OUString SAL_CALL resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream ) override;
999
1000 // ____ XServiceInfo ____
1001 virtual OUString SAL_CALL getImplementationName() override;
1002 virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
1003 virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
1004
1005 private:
1006 SvXMLGraphicHelperMode const m_eGraphicHelperMode;
1007 Reference< XGraphicObjectResolver > m_xGraphicObjectResolver;
1008 Reference< XGraphicStorageHandler > m_xGraphicStorageHandler;
1009 Reference< XBinaryStreamResolver > m_xBinaryStreamResolver;
1010 };
1011
SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode eMode)1012 SvXMLGraphicImportExportHelper::SvXMLGraphicImportExportHelper( SvXMLGraphicHelperMode eMode ) :
1013 impl::SvXMLGraphicImportExportHelper_Base( m_aMutex ),
1014 m_eGraphicHelperMode( eMode )
1015 {}
1016
disposing()1017 void SAL_CALL SvXMLGraphicImportExportHelper::disposing()
1018 {
1019 Reference< XComponent > xComp( m_xGraphicObjectResolver, UNO_QUERY );
1020 OSL_ASSERT( xComp.is());
1021 if( xComp.is())
1022 xComp->dispose();
1023 // m_xBinaryStreamResolver and m_xGraphicStorageHandler are a reference to the same object,
1024 // don't call dispose() again
1025 }
1026
1027 // ____ XInitialization ____
initialize(const Sequence<Any> & aArguments)1028 void SAL_CALL SvXMLGraphicImportExportHelper::initialize(
1029 const Sequence< Any >& aArguments )
1030 {
1031 Reference< embed::XStorage > xStorage;
1032 if( aArguments.hasElements() )
1033 aArguments[0] >>= xStorage;
1034
1035 rtl::Reference<SvXMLGraphicHelper> pHelper( SvXMLGraphicHelper::Create( xStorage, m_eGraphicHelperMode ));
1036 m_xGraphicObjectResolver.set( pHelper.get() );
1037 m_xGraphicStorageHandler.set( pHelper.get() );
1038 m_xBinaryStreamResolver.set( pHelper.get() );
1039 }
1040
1041 // ____ XGraphicObjectResolver ____
resolveGraphicObjectURL(const OUString & aURL)1042 OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveGraphicObjectURL( const OUString& aURL )
1043 {
1044 return m_xGraphicObjectResolver->resolveGraphicObjectURL( aURL );
1045 }
1046
1047 // ____ XGraphicStorageHandler ____
loadGraphic(OUString const & rURL)1048 uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphic(OUString const & rURL)
1049 {
1050 return m_xGraphicStorageHandler->loadGraphic(rURL);
1051 }
1052
loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)1053 uno::Reference<graphic::XGraphic> SAL_CALL SvXMLGraphicImportExportHelper::loadGraphicFromOutputStream(uno::Reference<io::XOutputStream> const & rxOutputStream)
1054 {
1055 return m_xGraphicStorageHandler->loadGraphicFromOutputStream(rxOutputStream);
1056 }
1057
saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)1058 OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphic(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic)
1059 {
1060 return m_xGraphicStorageHandler->saveGraphic(rxGraphic);
1061 }
1062
saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,OUString & rOutSavedMimeType,OUString const & rRequestName)1063 OUString SAL_CALL SvXMLGraphicImportExportHelper::saveGraphicByName(css::uno::Reference<css::graphic::XGraphic> const & rxGraphic,
1064 OUString & rOutSavedMimeType, OUString const & rRequestName)
1065 {
1066 return m_xGraphicStorageHandler->saveGraphicByName(rxGraphic, rOutSavedMimeType, rRequestName);
1067 }
1068
createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)1069 uno::Reference<io::XInputStream> SAL_CALL SvXMLGraphicImportExportHelper::createInputStream(uno::Reference<graphic::XGraphic> const & rxGraphic)
1070 {
1071 return m_xGraphicStorageHandler->createInputStream(rxGraphic);
1072 }
1073
1074 // ____ XBinaryStreamResolver ____
getInputStream(const OUString & aURL)1075 Reference< io::XInputStream > SAL_CALL SvXMLGraphicImportExportHelper::getInputStream( const OUString& aURL )
1076 {
1077 return m_xBinaryStreamResolver->getInputStream( aURL );
1078 }
createOutputStream()1079 Reference< io::XOutputStream > SAL_CALL SvXMLGraphicImportExportHelper::createOutputStream()
1080 {
1081 return m_xBinaryStreamResolver->createOutputStream();
1082 }
resolveOutputStream(const Reference<io::XOutputStream> & aBinaryStream)1083 OUString SAL_CALL SvXMLGraphicImportExportHelper::resolveOutputStream( const Reference< io::XOutputStream >& aBinaryStream )
1084 {
1085 return m_xBinaryStreamResolver->resolveOutputStream( aBinaryStream );
1086 }
1087
1088 // ____ XServiceInfo ____
getImplementationName()1089 OUString SAL_CALL SvXMLGraphicImportExportHelper::getImplementationName()
1090 {
1091 if( m_eGraphicHelperMode == SvXMLGraphicHelperMode::Read )
1092 return "com.sun.star.comp.Svx.GraphicImportHelper";
1093 return "com.sun.star.comp.Svx.GraphicExportHelper";
1094 }
1095
supportsService(const OUString & ServiceName)1096 sal_Bool SAL_CALL SvXMLGraphicImportExportHelper::supportsService( const OUString& ServiceName )
1097 {
1098 return cppu::supportsService(this, ServiceName);
1099 }
1100
getSupportedServiceNames()1101 Sequence< OUString > SAL_CALL SvXMLGraphicImportExportHelper::getSupportedServiceNames()
1102 {
1103 return { "com.sun.star.document.GraphicObjectResolver",
1104 "com.sun.star.document.GraphicStorageHandler",
1105 "com.sun.star.document.BinaryStreamResolver" };
1106 }
1107
1108 }
1109
1110 /** Create this with createInstanceWithArguments. service name
1111 "com.sun.star.comp.Svx.GraphicImportHelper", one argument which is the
1112 XStorage. Without arguments no helper class is created. With an empty
1113 argument the helper class is created and initialized like in the CTOR to
1114 SvXMLGraphicHelper that only gets the create mode.
1115
1116 You should call dispose after you no longer need this component.
1117
1118 uses eCreateMode == SvXMLGraphicHelperMode::Read, bDirect == sal_True in
1119 SvXMLGraphicHelper
1120 */
1121 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_Svx_GraphicImportHelper_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)1122 com_sun_star_comp_Svx_GraphicImportHelper_get_implementation(
1123 css::uno::XComponentContext *,
1124 css::uno::Sequence<css::uno::Any> const &)
1125 {
1126 return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Read));
1127 }
1128
1129 /** Create this with createInstanceWithArguments. service name
1130 "com.sun.star.comp.Svx.GraphicExportHelper", one argument which is the
1131 XStorage. Without arguments no helper class is created. With an empty
1132 argument the helper class is created and initialized like in the CTOR to
1133 SvXMLGraphicHelper that only gets the create mode
1134
1135 To write the Pictures stream, you have to call dispose at this component.
1136 Make sure you call dispose before you commit the parent storage.
1137
1138 uses eCreateMode == SvXMLGraphicHelperMode::Write, bDirect == sal_True in
1139 SvXMLGraphicHelper
1140 */
1141 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_comp_Svx_GraphicExportHelper_get_implementation(css::uno::XComponentContext *,css::uno::Sequence<css::uno::Any> const &)1142 com_sun_star_comp_Svx_GraphicExportHelper_get_implementation(
1143 css::uno::XComponentContext *,
1144 css::uno::Sequence<css::uno::Any> const &)
1145 {
1146 return cppu::acquire(new SvXMLGraphicImportExportHelper(SvXMLGraphicHelperMode::Write));
1147 }
1148
1149 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1150