1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/embed/XStorage.hpp>
21 #include <com/sun/star/embed/ElementModes.hpp>
22 #include <com/sun/star/container/XIndexContainer.hpp>
23 #include <com/sun/star/beans/PropertyAttribute.hpp>
24 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
25 #include <com/sun/star/io/XActiveDataSource.hpp>
26 #include <com/sun/star/xml/sax/Writer.hpp>
27 #include <com/sun/star/document/XExporter.hpp>
28 #include <com/sun/star/document/XFilter.hpp>
29 #include <com/sun/star/frame/XModule.hpp>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/genericpropertyset.hxx>
32 #include <o3tl/any.hxx>
33 #include <unotools/streamwrap.hxx>
34 #include <svx/xmlgrhlp.hxx>
35 #include <svx/xmleohlp.hxx>
36 #include <unotools/saveopt.hxx>
37 #include <svl/stritem.hxx>
38 #include <sfx2/frame.hxx>
39 #include <sfx2/docfile.hxx>
42 #include <docfunc.hxx>
43 #include <IDocumentRedlineAccess.hxx>
44 #include <IDocumentStatistics.hxx>
45 #include <IDocumentLayoutAccess.hxx>
46 #include <docstat.hxx>
49 #include <unotools/ucbstreamhelper.hxx>
52 #include <statstr.hrc>
54 #include <comphelper/documentconstants.hxx>
55 #include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
57 using namespace ::com::sun::star
;
58 using namespace ::com::sun::star::uno
;
59 using namespace ::com::sun::star::container
;
60 using namespace ::com::sun::star::document
;
61 using namespace ::com::sun::star::beans
;
62 using namespace ::com::sun::star::lang
;
64 SwXMLWriter::SwXMLWriter( const OUString
& rBaseURL
)
66 SetBaseURL( rBaseURL
);
69 SwXMLWriter::~SwXMLWriter()
73 sal_uInt32
SwXMLWriter::Write_( const uno::Reference
< task::XStatusIndicator
>& xStatusIndicator
,
74 const OUString
& aDocHierarchicalName
)
76 // Get service factory
77 uno::Reference
< uno::XComponentContext
> xContext
=
78 comphelper::getProcessComponentContext();
81 uno::Reference
< io::XOutputStream
> xOut
;
82 tools::SvRef
<SotStorageStream
> xDocStream
;
83 uno::Reference
< document::XGraphicObjectResolver
> xGraphicResolver
;
84 SvXMLGraphicHelper
*pGraphicHelper
= nullptr;
85 uno::Reference
< document::XEmbeddedObjectResolver
> xObjectResolver
;
86 SvXMLEmbeddedObjectHelper
*pObjectHelper
= nullptr;
88 OSL_ENSURE( xStg
.is(), "Where is my storage?" );
89 pGraphicHelper
= SvXMLGraphicHelper::Create( xStg
,
90 GRAPHICHELPER_MODE_WRITE
,
92 xGraphicResolver
= pGraphicHelper
;
94 SfxObjectShell
*pPersist
= pDoc
->GetPersist();
97 pObjectHelper
= SvXMLEmbeddedObjectHelper::Create(
99 EMBEDDEDOBJECTHELPER_MODE_WRITE
,
101 xObjectResolver
= pObjectHelper
;
104 // create and prepare the XPropertySet that gets passed through
105 // the components, and the XStatusIndicator that shows progress to
108 // create XPropertySet with three properties for status indicator
109 comphelper::PropertyMapEntry
const aInfoMap
[] =
111 { OUString("ProgressRange"), 0,
112 ::cppu::UnoType
<sal_Int32
>::get(),
113 beans::PropertyAttribute::MAYBEVOID
, 0},
114 { OUString("ProgressMax"), 0,
115 ::cppu::UnoType
<sal_Int32
>::get(),
116 beans::PropertyAttribute::MAYBEVOID
, 0},
117 { OUString("ProgressCurrent"), 0,
118 ::cppu::UnoType
<sal_Int32
>::get(),
119 beans::PropertyAttribute::MAYBEVOID
, 0},
120 { OUString("WrittenNumberStyles"), 0,
121 cppu::UnoType
<uno::Sequence
<sal_Int32
>>::get(),
122 beans::PropertyAttribute::MAYBEVOID
, 0},
123 { OUString("UsePrettyPrinting"), 0,
124 cppu::UnoType
<bool>::get(),
125 beans::PropertyAttribute::MAYBEVOID
, 0},
126 { OUString("ShowChanges"), 0,
127 cppu::UnoType
<bool>::get(),
128 beans::PropertyAttribute::MAYBEVOID
, 0 },
129 { OUString("RedlineProtectionKey"), 0,
130 cppu::UnoType
<Sequence
<sal_Int8
>>::get(),
131 beans::PropertyAttribute::MAYBEVOID
, 0 },
132 { OUString("BaseURI"), 0,
133 ::cppu::UnoType
<OUString
>::get(),
134 beans::PropertyAttribute::MAYBEVOID
, 0 },
135 { OUString("StreamRelPath"), 0,
136 ::cppu::UnoType
<OUString
>::get(),
137 beans::PropertyAttribute::MAYBEVOID
, 0 },
138 { OUString("StreamName"), 0,
139 ::cppu::UnoType
<OUString
>::get(),
140 beans::PropertyAttribute::MAYBEVOID
, 0 },
141 { OUString("AutoTextMode"), 0,
142 cppu::UnoType
<bool>::get(),
143 beans::PropertyAttribute::MAYBEVOID
, 0 },
144 { OUString("StyleNames"), 0,
145 cppu::UnoType
<Sequence
<OUString
>>::get(),
146 beans::PropertyAttribute::MAYBEVOID
, 0 },
147 { OUString("StyleFamilies"), 0,
148 cppu::UnoType
<Sequence
<sal_Int32
>>::get(),
149 beans::PropertyAttribute::MAYBEVOID
, 0 },
151 { OUString("OutlineStyleAsNormalListStyle"), 0,
152 cppu::UnoType
<bool>::get(),
153 beans::PropertyAttribute::MAYBEVOID
, 0 },
154 { OUString("TargetStorage"),0, cppu::UnoType
<embed::XStorage
>::get(),
155 css::beans::PropertyAttribute::MAYBEVOID
, 0 },
156 { OUString(), 0, css::uno::Type(), 0, 0 }
158 uno::Reference
< beans::XPropertySet
> xInfoSet(
159 comphelper::GenericPropertySet_CreateInstance(
160 new comphelper::PropertySetInfo( aInfoMap
) ) );
162 xInfoSet
->setPropertyValue( "TargetStorage", Any( xStg
) );
167 // set progress range and start status indicator
168 sal_Int32
nProgressRange(1000000);
169 if (xStatusIndicator
.is())
171 xStatusIndicator
->start(SW_RESSTR( STR_STATSTR_SWGWRITE
),
174 xInfoSet
->setPropertyValue("ProgressRange", Any(nProgressRange
));
176 xInfoSet
->setPropertyValue("ProgressMax", Any(static_cast < sal_Int32
>( -1 )));
179 SvtSaveOptions aSaveOpt
;
180 xInfoSet
->setPropertyValue( "UsePrettyPrinting", makeAny(aSaveOpt
.IsPrettyPrinting()) );
182 // save show redline mode ...
183 const OUString
sShowChanges("ShowChanges");
184 sal_uInt16 nRedlineMode
= pDoc
->getIDocumentRedlineAccess().GetRedlineMode();
185 xInfoSet
->setPropertyValue( sShowChanges
,
186 makeAny( IDocumentRedlineAccess::IsShowChanges( nRedlineMode
) ) );
187 // ... and hide redlines for export
188 nRedlineMode
&= ~nsRedlineMode_t::REDLINE_SHOW_MASK
;
189 nRedlineMode
|= nsRedlineMode_t::REDLINE_SHOW_INSERT
;
190 pDoc
->getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t
)( nRedlineMode
));
193 xInfoSet
->setPropertyValue( "BaseURI", makeAny( GetBaseURL() ) );
195 if( SfxObjectCreateMode::EMBEDDED
== pDoc
->GetDocShell()->GetCreateMode() )
197 const OUString
aName( !aDocHierarchicalName
.isEmpty()
198 ? aDocHierarchicalName
199 : OUString( "dummyObjectName" ) );
201 xInfoSet
->setPropertyValue( "StreamRelPath", makeAny( aName
) );
206 xInfoSet
->setPropertyValue( "AutoTextMode", makeAny(true) );
210 const bool bOASIS
= ( SotStorage::GetVersion( xStg
) > SOFFICE_FILEFORMAT_60
);
212 docfunc::HasOutlineStyleToBeWrittenAsNormalListStyle( *pDoc
) )
214 xInfoSet
->setPropertyValue( "OutlineStyleAsNormalListStyle", makeAny( true ) );
218 // - graphics + object resolver for styles + content
219 // - status indicator
220 // - info property set
223 if( xStatusIndicator
.is() )
226 Sequence
< Any
> aEmptyArgs( nArgs
);
227 Any
*pArgs
= aEmptyArgs
.getArray();
228 *pArgs
++ <<= xInfoSet
;
229 if( xStatusIndicator
.is() )
230 *pArgs
++ <<= xStatusIndicator
;
232 if( xGraphicResolver
.is() )
234 if( xObjectResolver
.is() )
237 Sequence
< Any
> aFilterArgs( nArgs
);
238 pArgs
= aFilterArgs
.getArray();
239 *pArgs
++ <<= xInfoSet
;
240 if( xGraphicResolver
.is() )
241 *pArgs
++ <<= xGraphicResolver
;
242 if( xObjectResolver
.is() )
243 *pArgs
++ <<= xObjectResolver
;
244 if( xStatusIndicator
.is() )
245 *pArgs
++ <<= xStatusIndicator
;
248 uno::Reference
< lang::XComponent
> xModelComp(
249 pDoc
->GetDocShell()->GetModel(), UNO_QUERY
);
250 OSL_ENSURE( xModelComp
.is(), "XMLWriter::Write: got no model" );
251 if( !xModelComp
.is() )
252 return ERR_SWG_WRITE_ERROR
;
254 PutNumFormatFontsInAttrPool();
255 PutEditEngFontsInAttrPool();
258 Sequence
< PropertyValue
> aProps( pOrigFileName
? 1 : 0 );
261 PropertyValue
*pProps
= aProps
.getArray();
262 pProps
->Name
= "FileName";
263 pProps
->Value
<<= *pOrigFileName
;
266 // export sub streams for package, else full stream into a file
269 // RDF metadata: export if ODF >= 1.2
270 // N.B.: embedded documents have their own manifest.rdf!
273 const uno::Reference
<beans::XPropertySet
> xPropSet(xStg
,
274 uno::UNO_QUERY_THROW
);
279 if ((xPropSet
->getPropertyValue("Version") >>= Version
)
280 && Version
!= ODFVER_010_TEXT
281 && Version
!= ODFVER_011_TEXT
)
283 const uno::Reference
<rdf::XDocumentMetadataAccess
> xDMA(
284 xModelComp
, uno::UNO_QUERY_THROW
);
285 xDMA
->storeMetadataToStorage(xStg
);
288 catch (beans::UnknownPropertyException
&)
290 catch (uno::Exception
&)
296 bool bStoreMeta
= ( SfxObjectCreateMode::EMBEDDED
!= pDoc
->GetDocShell()->GetCreateMode() );
301 Reference
< frame::XModule
> xModule( xModelComp
, UNO_QUERY
);
304 const OUString aModuleID
= xModule
->getIdentifier();
305 bStoreMeta
= !aModuleID
.isEmpty() &&
306 ( aModuleID
== "com.sun.star.sdb.FormDesign" ||
307 aModuleID
== "com.sun.star.sdb.TextReportDesign" );
310 catch( uno::Exception
& )
315 if( !bOrganizerMode
&& !bBlock
&& bStoreMeta
)
317 if( !WriteThroughComponent(
318 xModelComp
, "meta.xml", xContext
,
319 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisMetaExporter"
320 : "com.sun.star.comp.Writer.XMLMetaExporter"),
321 aEmptyArgs
, aProps
) )
324 sWarnFile
= "meta.xml";
330 if( !WriteThroughComponent(
331 xModelComp
, "settings.xml", xContext
,
332 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisSettingsExporter"
333 : "com.sun.star.comp.Writer.XMLSettingsExporter"),
334 aEmptyArgs
, aProps
) )
339 sWarnFile
= "settings.xml";
347 if( !WriteThroughComponent(
348 xModelComp
, "styles.xml", xContext
,
349 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisStylesExporter"
350 : "com.sun.star.comp.Writer.XMLStylesExporter"),
351 aFilterArgs
, aProps
) )
354 sErrFile
= "styles.xml";
357 if( !bOrganizerMode
&& !bErr
)
359 if( !WriteThroughComponent(
360 xModelComp
, "content.xml", xContext
,
361 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisContentExporter"
362 : "com.sun.star.comp.Writer.XMLContentExporter"),
363 aFilterArgs
, aProps
) )
366 sErrFile
= "content.xml";
370 if( pDoc
->getIDocumentLayoutAccess().GetCurrentViewShell() && pDoc
->getIDocumentStatistics().GetDocStat().nPage
> 1 &&
371 !(bOrganizerMode
|| bBlock
|| bErr
) )
375 uno::Reference
< io::XStream
> xStm
= xStg
->openStreamElement( "layout-cache", embed::ElementModes::READWRITE
| embed::ElementModes::TRUNCATE
);
376 SvStream
* pStream
= utl::UcbStreamHelper::CreateStream( xStm
);
377 if( !pStream
->GetError() )
379 uno::Reference
< beans::XPropertySet
> xSet( xStm
, UNO_QUERY
);
381 aAny2
<<= OUString("application/binary");
382 xSet
->setPropertyValue("MediaType", aAny2
);
383 pDoc
->WriteLayoutCache( *pStream
);
388 catch ( uno::Exception
& )
394 SvXMLGraphicHelper::Destroy( pGraphicHelper
);
395 xGraphicResolver
= nullptr;
398 SvXMLEmbeddedObjectHelper::Destroy( pObjectHelper
);
399 xObjectResolver
= nullptr;
401 // restore redline mode
402 aAny
= xInfoSet
->getPropertyValue( sShowChanges
);
403 nRedlineMode
= pDoc
->getIDocumentRedlineAccess().GetRedlineMode();
404 nRedlineMode
&= ~nsRedlineMode_t::REDLINE_SHOW_MASK
;
405 nRedlineMode
|= nsRedlineMode_t::REDLINE_SHOW_INSERT
;
406 if ( *o3tl::doAccess
<bool>(aAny
) )
407 nRedlineMode
|= nsRedlineMode_t::REDLINE_SHOW_DELETE
;
408 pDoc
->getIDocumentRedlineAccess().SetRedlineMode((RedlineMode_t
)( nRedlineMode
));
410 if (xStatusIndicator
.is())
412 xStatusIndicator
->end();
417 if( !sErrFile
.isEmpty() )
418 return *new StringErrorInfo( ERR_WRITE_ERROR_FILE
, sErrFile
,
419 ERRCODE_BUTTON_OK
| ERRCODE_MSG_ERROR
);
420 return ERR_SWG_WRITE_ERROR
;
424 if( !sWarnFile
.isEmpty() )
425 return *new StringErrorInfo( WARN_WRITE_ERROR_FILE
, sWarnFile
,
426 ERRCODE_BUTTON_OK
| ERRCODE_MSG_ERROR
);
427 return WARN_SWG_FEATURES_LOST
;
433 sal_uLong
SwXMLWriter::WriteStorage()
435 return Write_( uno::Reference
< task::XStatusIndicator
>(), OUString() );
438 sal_uLong
SwXMLWriter::WriteMedium( SfxMedium
& aTargetMedium
)
440 uno::Reference
< task::XStatusIndicator
> xStatusIndicator
;
442 const SfxUnoAnyItem
* pStatusBarItem
= static_cast<const SfxUnoAnyItem
*>(
443 aTargetMedium
.GetItemSet()->GetItem(SID_PROGRESS_STATUSBAR_CONTROL
) );
444 if ( pStatusBarItem
)
445 pStatusBarItem
->GetValue() >>= xStatusIndicator
;
446 const SfxStringItem
* pDocHierarchItem
= static_cast<const SfxStringItem
*>(
447 aTargetMedium
.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME
) );
448 if ( pDocHierarchItem
)
449 aName
= pDocHierarchItem
->GetValue();
451 return Write_( xStatusIndicator
, aName
);
454 sal_uLong
SwXMLWriter::Write( SwPaM
& rPaM
, SfxMedium
& rMed
,
455 const OUString
* pFileName
)
458 ? static_cast<StgWriter
*>(this)->Write( rPaM
, rMed
.GetOutputStorage(), pFileName
, &rMed
)
459 : static_cast<Writer
*>(this)->Write( rPaM
, *rMed
.GetOutStream(), pFileName
);
462 bool SwXMLWriter::WriteThroughComponent(
463 const uno::Reference
<XComponent
> & xComponent
,
464 const sal_Char
* pStreamName
,
465 const uno::Reference
<uno::XComponentContext
> & rxContext
,
466 const sal_Char
* pServiceName
,
467 const Sequence
<Any
> & rArguments
,
468 const Sequence
<beans::PropertyValue
> & rMediaDesc
)
470 OSL_ENSURE( xStg
.is(), "Need storage!" );
471 OSL_ENSURE( nullptr != pStreamName
, "Need stream name!" );
472 OSL_ENSURE( nullptr != pServiceName
, "Need service name!" );
474 SAL_INFO( "sw.filter", "SwXMLWriter::WriteThroughComponent : stream " << pStreamName
);
479 const OUString sStreamName
= OUString::createFromAscii( pStreamName
);
480 uno::Reference
<io::XStream
> xStream
=
481 xStg
->openStreamElement( sStreamName
,
482 embed::ElementModes::READWRITE
| embed::ElementModes::TRUNCATE
);
484 uno::Reference
<beans::XPropertySet
> xSet( xStream
, uno::UNO_QUERY
);
488 xSet
->setPropertyValue("MediaType", Any(OUString("text/xml")) );
490 // even plain stream should be encrypted in encrypted documents
491 xSet
->setPropertyValue( "UseCommonStoragePasswordEncryption", makeAny(true) );
493 // set buffer and create outputstream
494 uno::Reference
< io::XOutputStream
> xOutputStream
= xStream
->getOutputStream();
497 uno::Reference
< beans::XPropertySet
> xInfoSet
;
498 if( rArguments
.getLength() > 0 )
499 rArguments
.getConstArray()[0] >>= xInfoSet
;
500 OSL_ENSURE( xInfoSet
.is(), "missing property set" );
503 xInfoSet
->setPropertyValue( "StreamName", makeAny( sStreamName
) );
507 bRet
= WriteThroughComponent(
508 xOutputStream
, xComponent
, rxContext
,
509 pServiceName
, rArguments
, rMediaDesc
);
511 catch ( uno::Exception
& )
519 bool SwXMLWriter::WriteThroughComponent(
520 const uno::Reference
<io::XOutputStream
> & xOutputStream
,
521 const uno::Reference
<XComponent
> & xComponent
,
522 const uno::Reference
<XComponentContext
> & rxContext
,
523 const sal_Char
* pServiceName
,
524 const Sequence
<Any
> & rArguments
,
525 const Sequence
<PropertyValue
> & rMediaDesc
)
527 OSL_ENSURE( xOutputStream
.is(), "I really need an output stream!" );
528 OSL_ENSURE( xComponent
.is(), "Need component!" );
529 OSL_ENSURE( nullptr != pServiceName
, "Need component name!" );
532 uno::Reference
< xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(rxContext
);
533 SAL_INFO( "sw.filter", "SAX-Writer created" );
534 // connect XML writer to output stream
535 xSaxWriter
->setOutputStream( xOutputStream
);
537 // prepare arguments (prepend doc handler to given arguments)
538 uno::Reference
<xml::sax::XDocumentHandler
> xDocHandler( xSaxWriter
,UNO_QUERY
);
539 Sequence
<Any
> aArgs( 1 + rArguments
.getLength() );
540 aArgs
[0] <<= xDocHandler
;
541 for(sal_Int32 i
= 0; i
< rArguments
.getLength(); i
++)
542 aArgs
[i
+1] = rArguments
[i
];
544 // get filter component
545 uno::Reference
< document::XExporter
> xExporter(
546 rxContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
547 OUString::createFromAscii(pServiceName
), aArgs
, rxContext
), UNO_QUERY
);
548 OSL_ENSURE( xExporter
.is(),
549 "can't instantiate export filter component" );
550 if( !xExporter
.is() )
552 SAL_INFO( "sw.filter", pServiceName
<< " instantiated." );
553 // connect model and filter
554 xExporter
->setSourceDocument( xComponent
);
557 SAL_INFO( "sw.filter", "call filter()" );
558 uno::Reference
<XFilter
> xFilter( xExporter
, UNO_QUERY
);
559 return xFilter
->filter( rMediaDesc
);
562 void GetXMLWriter( const OUString
& /*rName*/, const OUString
& rBaseURL
, WriterRef
& xRet
)
564 xRet
= new SwXMLWriter( rBaseURL
);
567 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */