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/beans/PropertyAttribute.hpp>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <com/sun/star/task/XStatusIndicator.hpp>
25 #include <com/sun/star/xml/sax/Writer.hpp>
26 #include <com/sun/star/drawing/XDrawPageSupplier.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>
31 #include <officecfg/Office/Common.hxx>
32 #include <comphelper/fileformat.h>
33 #include <comphelper/processfactory.hxx>
34 #include <comphelper/genericpropertyset.hxx>
35 #include <comphelper/propertysetinfo.hxx>
36 #include <vcl/errinf.hxx>
37 #include <osl/diagnose.h>
38 #include <sal/log.hxx>
39 #include <svx/xmlgrhlp.hxx>
40 #include <svx/xmleohlp.hxx>
41 #include <svl/stritem.hxx>
42 #include <sfx2/frame.hxx>
43 #include <sfx2/docfile.hxx>
44 #include <sfx2/sfxsids.hrc>
47 #include <docfunc.hxx>
48 #include <IDocumentRedlineAccess.hxx>
49 #include <IDocumentMarkAccess.hxx>
50 #include <IDocumentStatistics.hxx>
51 #include <IDocumentLayoutAccess.hxx>
52 #include <rootfrm.hxx>
53 #include <docstat.hxx>
56 #include <xmloff/shapeexport.hxx>
57 #include <unotools/ucbstreamhelper.hxx>
61 #include <strings.hrc>
63 #include <comphelper/documentconstants.hxx>
64 #include <com/sun/star/rdf/XDocumentMetadataAccess.hpp>
66 using namespace ::com::sun::star
;
67 using namespace ::com::sun::star::uno
;
68 using namespace ::com::sun::star::container
;
69 using namespace ::com::sun::star::document
;
70 using namespace ::com::sun::star::beans
;
71 using namespace ::com::sun::star::lang
;
73 SwXMLWriter::SwXMLWriter( const OUString
& rBaseURL
)
75 SetBaseURL( rBaseURL
);
78 SwXMLWriter::~SwXMLWriter()
82 ErrCode
SwXMLWriter::Write_(const SfxItemSet
* pMediumItemSet
)
84 uno::Reference
<task::XStatusIndicator
> xStatusIndicator
;
85 OUString aDocHierarchicalName
;
90 const SfxUnoAnyItem
* pStatusBarItem
=
91 pMediumItemSet
->GetItem(SID_PROGRESS_STATUSBAR_CONTROL
);
93 pStatusBarItem
->GetValue() >>= xStatusIndicator
;
94 const SfxStringItem
* pDocHierarchItem
=
95 pMediumItemSet
->GetItem(SID_DOC_HIERARCHICALNAME
);
97 aDocHierarchicalName
= pDocHierarchItem
->GetValue();
98 const SfxBoolItem
* pNoEmbDS
= pMediumItemSet
->GetItem(SID_NO_EMBEDDED_DS
);
100 bNoEmbDS
= pNoEmbDS
->GetValue();
103 // Get service factory
104 uno::Reference
< uno::XComponentContext
> xContext
=
105 comphelper::getProcessComponentContext();
108 uno::Reference
<document::XGraphicStorageHandler
> xGraphicStorageHandler
;
109 rtl::Reference
<SvXMLGraphicHelper
> xGraphicHelper
;
110 uno::Reference
< document::XEmbeddedObjectResolver
> xObjectResolver
;
111 rtl::Reference
<SvXMLEmbeddedObjectHelper
> xObjectHelper
;
113 OSL_ENSURE( m_xStg
.is(), "Where is my storage?" );
114 xGraphicHelper
= SvXMLGraphicHelper::Create( m_xStg
,
115 SvXMLGraphicHelperMode::Write
);
116 xGraphicStorageHandler
= xGraphicHelper
.get();
118 SfxObjectShell
*pPersist
= m_pDoc
->GetPersist();
121 xObjectHelper
= SvXMLEmbeddedObjectHelper::Create(
123 SvXMLEmbeddedObjectHelperMode::Write
);
124 xObjectResolver
= xObjectHelper
.get();
127 // create and prepare the XPropertySet that gets passed through
128 // the components, and the XStatusIndicator that shows progress to
131 // create XPropertySet with three properties for status indicator
132 static comphelper::PropertyMapEntry
const aInfoMap
[] =
134 { OUString("ProgressRange"), 0,
135 ::cppu::UnoType
<sal_Int32
>::get(),
136 beans::PropertyAttribute::MAYBEVOID
, 0},
137 { OUString("ProgressMax"), 0,
138 ::cppu::UnoType
<sal_Int32
>::get(),
139 beans::PropertyAttribute::MAYBEVOID
, 0},
140 { OUString("ProgressCurrent"), 0,
141 ::cppu::UnoType
<sal_Int32
>::get(),
142 beans::PropertyAttribute::MAYBEVOID
, 0},
143 { OUString("WrittenNumberStyles"), 0,
144 cppu::UnoType
<uno::Sequence
<sal_Int32
>>::get(),
145 beans::PropertyAttribute::MAYBEVOID
, 0},
146 { OUString("UsePrettyPrinting"), 0,
147 cppu::UnoType
<bool>::get(),
148 beans::PropertyAttribute::MAYBEVOID
, 0},
149 { OUString("ShowChanges"), 0,
150 cppu::UnoType
<bool>::get(),
151 beans::PropertyAttribute::MAYBEVOID
, 0 },
152 { OUString("RedlineProtectionKey"), 0,
153 cppu::UnoType
<Sequence
<sal_Int8
>>::get(),
154 beans::PropertyAttribute::MAYBEVOID
, 0 },
155 { OUString("BaseURI"), 0,
156 ::cppu::UnoType
<OUString
>::get(),
157 beans::PropertyAttribute::MAYBEVOID
, 0 },
158 { OUString("StreamRelPath"), 0,
159 ::cppu::UnoType
<OUString
>::get(),
160 beans::PropertyAttribute::MAYBEVOID
, 0 },
161 { OUString("StreamName"), 0,
162 ::cppu::UnoType
<OUString
>::get(),
163 beans::PropertyAttribute::MAYBEVOID
, 0 },
164 { OUString("AutoTextMode"), 0,
165 cppu::UnoType
<bool>::get(),
166 beans::PropertyAttribute::MAYBEVOID
, 0 },
167 { OUString("StyleNames"), 0,
168 cppu::UnoType
<Sequence
<OUString
>>::get(),
169 beans::PropertyAttribute::MAYBEVOID
, 0 },
170 { OUString("StyleFamilies"), 0,
171 cppu::UnoType
<Sequence
<sal_Int32
>>::get(),
172 beans::PropertyAttribute::MAYBEVOID
, 0 },
174 { OUString("OutlineStyleAsNormalListStyle"), 0,
175 cppu::UnoType
<bool>::get(),
176 beans::PropertyAttribute::MAYBEVOID
, 0 },
177 { OUString("TargetStorage"),0, cppu::UnoType
<embed::XStorage
>::get(),
178 css::beans::PropertyAttribute::MAYBEVOID
, 0 },
180 { OUString("NoEmbDataSet"), 0,
181 cppu::UnoType
<bool>::get(),
182 beans::PropertyAttribute::MAYBEVOID
, 0 },
184 uno::Reference
< beans::XPropertySet
> xInfoSet(
185 comphelper::GenericPropertySet_CreateInstance(
186 new comphelper::PropertySetInfo( aInfoMap
) ) );
188 xInfoSet
->setPropertyValue( "TargetStorage", Any( m_xStg
) );
190 xInfoSet
->setPropertyValue("NoEmbDataSet", Any(bNoEmbDS
));
194 // set progress range and start status indicator
195 sal_Int32
nProgressRange(1000000);
196 if (xStatusIndicator
.is())
198 xStatusIndicator
->start(SwResId( STR_STATSTR_SWGWRITE
),
201 xInfoSet
->setPropertyValue("ProgressRange", Any(nProgressRange
));
203 xInfoSet
->setPropertyValue("ProgressMax", Any(static_cast < sal_Int32
>( -1 )));
206 xInfoSet
->setPropertyValue( "UsePrettyPrinting", Any(officecfg::Office::Common::Save::Document::PrettyPrinting::get()) );
208 uno::Reference
<lang::XComponent
> const xModelComp(m_pDoc
->GetDocShell()->GetModel());
209 uno::Reference
<drawing::XDrawPageSupplier
> const xDPS(xModelComp
, uno::UNO_QUERY
);
211 xmloff::FixZOrder(xDPS
->getDrawPage(), sw::GetZOrderLayer(m_pDoc
->getIDocumentDrawModelAccess()));
213 // save show redline mode ...
214 RedlineFlags
const nOrigRedlineFlags
= m_pDoc
->getIDocumentRedlineAccess().GetRedlineFlags();
215 RedlineFlags
nRedlineFlags(nOrigRedlineFlags
);
217 // TODO: ideally this would be stored per-view...
218 SwRootFrame
const*const pLayout(m_pDoc
->getIDocumentLayoutAccess().GetCurrentLayout());
219 isShowChanges
= pLayout
== nullptr || !pLayout
->IsHideRedlines();
220 xInfoSet
->setPropertyValue("ShowChanges", Any(isShowChanges
));
221 // ... and hide redlines for export
222 nRedlineFlags
&= ~RedlineFlags::ShowMask
;
223 nRedlineFlags
|= RedlineFlags::ShowInsert
;
224 m_pDoc
->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags
);
227 xInfoSet
->setPropertyValue( "BaseURI", Any( GetBaseURL() ) );
229 if( SfxObjectCreateMode::EMBEDDED
== m_pDoc
->GetDocShell()->GetCreateMode() )
231 const OUString
aName( !aDocHierarchicalName
.isEmpty()
232 ? aDocHierarchicalName
233 : OUString( "dummyObjectName" ) );
235 xInfoSet
->setPropertyValue( "StreamRelPath", Any( aName
) );
240 xInfoSet
->setPropertyValue( "AutoTextMode", Any(true) );
244 const bool bOASIS
= ( SotStorage::GetVersion( m_xStg
) > SOFFICE_FILEFORMAT_60
);
246 docfunc::HasOutlineStyleToBeWrittenAsNormalListStyle( *m_pDoc
) )
248 xInfoSet
->setPropertyValue( "OutlineStyleAsNormalListStyle", Any( true ) );
252 // - graphics + object resolver for styles + content
253 // - status indicator
254 // - info property set
257 if( xStatusIndicator
.is() )
260 Sequence
< Any
> aEmptyArgs( nArgs
);
261 Any
*pArgs
= aEmptyArgs
.getArray();
262 *pArgs
++ <<= xInfoSet
;
263 if( xStatusIndicator
.is() )
264 *pArgs
++ <<= xStatusIndicator
;
266 if( xGraphicStorageHandler
.is() )
268 if( xObjectResolver
.is() )
271 Sequence
< Any
> aFilterArgs( nArgs
);
272 pArgs
= aFilterArgs
.getArray();
273 *pArgs
++ <<= xInfoSet
;
274 if( xGraphicStorageHandler
.is() )
275 *pArgs
++ <<= xGraphicStorageHandler
;
276 if( xObjectResolver
.is() )
277 *pArgs
++ <<= xObjectResolver
;
278 if( xStatusIndicator
.is() )
279 *pArgs
++ <<= xStatusIndicator
;
281 PutNumFormatFontsInAttrPool();
282 PutEditEngFontsInAttrPool();
285 Sequence
< PropertyValue
> aProps( m_pOrigFileName
? 1 : 0 );
286 if( m_pOrigFileName
)
288 PropertyValue
*pProps
= aProps
.getArray();
289 pProps
->Name
= "FileName";
290 pProps
->Value
<<= *m_pOrigFileName
;
293 // export sub streams for package, else full stream into a file
296 // RDF metadata: export if ODF >= 1.2
297 // N.B.: embedded documents have their own manifest.rdf!
300 const uno::Reference
<beans::XPropertySet
> xPropSet(m_xStg
,
301 uno::UNO_QUERY_THROW
);
306 if ((xPropSet
->getPropertyValue("Version") >>= Version
)
307 && Version
!= ODFVER_010_TEXT
308 && Version
!= ODFVER_011_TEXT
)
310 const uno::Reference
<rdf::XDocumentMetadataAccess
> xDMA(
311 xModelComp
, uno::UNO_QUERY_THROW
);
312 xDMA
->storeMetadataToStorage(m_xStg
);
315 catch (beans::UnknownPropertyException
&)
317 catch (uno::Exception
&)
323 bool bStoreMeta
= ( SfxObjectCreateMode::EMBEDDED
!= m_pDoc
->GetDocShell()->GetCreateMode() );
328 Reference
< frame::XModule
> xModule( xModelComp
, UNO_QUERY
);
331 const OUString aModuleID
= xModule
->getIdentifier();
332 bStoreMeta
= !aModuleID
.isEmpty() &&
333 ( aModuleID
== "com.sun.star.sdb.FormDesign" ||
334 aModuleID
== "com.sun.star.sdb.TextReportDesign" );
337 catch( uno::Exception
& )
342 if( !m_bOrganizerMode
&& !m_bBlock
&& bStoreMeta
)
344 if( !WriteThroughComponent(
345 xModelComp
, "meta.xml", xContext
,
346 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisMetaExporter"
347 : "com.sun.star.comp.Writer.XMLMetaExporter"),
348 aEmptyArgs
, aProps
) )
351 sWarnFile
= "meta.xml";
357 if( !WriteThroughComponent(
358 xModelComp
, "settings.xml", xContext
,
359 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisSettingsExporter"
360 : "com.sun.star.comp.Writer.XMLSettingsExporter"),
361 aEmptyArgs
, aProps
) )
366 sWarnFile
= "settings.xml";
374 if( !WriteThroughComponent(
375 xModelComp
, "styles.xml", xContext
,
376 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisStylesExporter"
377 : "com.sun.star.comp.Writer.XMLStylesExporter"),
378 aFilterArgs
, aProps
) )
381 sErrFile
= "styles.xml";
384 if( !m_bOrganizerMode
&& !bErr
)
386 if( !WriteThroughComponent(
387 xModelComp
, "content.xml", xContext
,
388 (bOASIS
? "com.sun.star.comp.Writer.XMLOasisContentExporter"
389 : "com.sun.star.comp.Writer.XMLContentExporter"),
390 aFilterArgs
, aProps
) )
393 sErrFile
= "content.xml";
397 if( m_pDoc
->getIDocumentLayoutAccess().GetCurrentViewShell() && m_pDoc
->getIDocumentStatistics().GetDocStat().nPage
> 1 &&
398 !(m_bOrganizerMode
|| m_bBlock
|| bErr
||
399 // sw_redlinehide: disable layout cache for now
400 m_pDoc
->getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas()))
404 uno::Reference
< io::XStream
> xStm
= m_xStg
->openStreamElement( "layout-cache", embed::ElementModes::READWRITE
| embed::ElementModes::TRUNCATE
);
405 std::unique_ptr
<SvStream
> pStream
= utl::UcbStreamHelper::CreateStream( xStm
);
406 if( !pStream
->GetError() )
408 uno::Reference
< beans::XPropertySet
> xSet( xStm
, UNO_QUERY
);
410 aAny2
<<= OUString("application/binary");
411 xSet
->setPropertyValue("MediaType", aAny2
);
412 m_pDoc
->WriteLayoutCache( *pStream
);
415 catch ( uno::Exception
& )
421 xGraphicHelper
->dispose();
422 xGraphicHelper
.clear();
423 xGraphicStorageHandler
= nullptr;
426 xObjectHelper
->dispose();
427 xObjectHelper
.clear();
428 xObjectResolver
= nullptr;
430 // restore redline mode
431 nRedlineFlags
= m_pDoc
->getIDocumentRedlineAccess().GetRedlineFlags();
432 nRedlineFlags
&= ~RedlineFlags::ShowMask
;
433 nRedlineFlags
|= RedlineFlags::ShowInsert
;
434 nRedlineFlags
|= nOrigRedlineFlags
& RedlineFlags::ShowMask
;
435 m_pDoc
->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags
);
437 // tdf#115815 restore annotation ranges collapsed by hide redlines
438 m_pDoc
->getIDocumentMarkAccess()->restoreAnnotationMarks();
440 if (xStatusIndicator
.is())
442 xStatusIndicator
->end();
447 if( !sErrFile
.isEmpty() )
448 return *new StringErrorInfo( ERR_WRITE_ERROR_FILE
, sErrFile
,
449 DialogMask::ButtonsOk
| DialogMask::MessageError
);
450 return ERR_SWG_WRITE_ERROR
;
454 if( !sWarnFile
.isEmpty() )
455 return *new StringErrorInfo( WARN_WRITE_ERROR_FILE
, sWarnFile
,
456 DialogMask::ButtonsOk
| DialogMask::MessageError
);
457 return WARN_SWG_FEATURES_LOST
;
463 ErrCode
SwXMLWriter::WriteStorage()
465 return Write_(nullptr);
468 ErrCode
SwXMLWriter::WriteMedium( SfxMedium
& aTargetMedium
)
470 return Write_(aTargetMedium
.GetItemSet());
473 ErrCode
SwXMLWriter::Write( SwPaM
& rPaM
, SfxMedium
& rMed
,
474 const OUString
* pFileName
)
477 ? static_cast<StgWriter
*>(this)->Write( rPaM
, rMed
.GetOutputStorage(), pFileName
, &rMed
)
478 : static_cast<Writer
*>(this)->Write( rPaM
, *rMed
.GetOutStream(), pFileName
);
481 bool SwXMLWriter::WriteThroughComponent(
482 const uno::Reference
<XComponent
> & xComponent
,
483 const char* pStreamName
,
484 const uno::Reference
<uno::XComponentContext
> & rxContext
,
485 const char* pServiceName
,
486 const Sequence
<Any
> & rArguments
,
487 const Sequence
<beans::PropertyValue
> & rMediaDesc
)
489 OSL_ENSURE( m_xStg
.is(), "Need storage!" );
490 OSL_ENSURE( nullptr != pStreamName
, "Need stream name!" );
491 OSL_ENSURE( nullptr != pServiceName
, "Need service name!" );
493 SAL_INFO( "sw.filter", "SwXMLWriter::WriteThroughComponent : stream " << pStreamName
);
498 const OUString sStreamName
= OUString::createFromAscii( pStreamName
);
499 uno::Reference
<io::XStream
> xStream
=
500 m_xStg
->openStreamElement( sStreamName
,
501 embed::ElementModes::READWRITE
| embed::ElementModes::TRUNCATE
);
503 uno::Reference
<beans::XPropertySet
> xSet( xStream
, uno::UNO_QUERY
);
507 xSet
->setPropertyValue("MediaType", Any(OUString("text/xml")) );
509 // even plain stream should be encrypted in encrypted documents
510 xSet
->setPropertyValue( "UseCommonStoragePasswordEncryption", Any(true) );
512 // set buffer and create outputstream
513 uno::Reference
< io::XOutputStream
> xOutputStream
= xStream
->getOutputStream();
516 uno::Reference
< beans::XPropertySet
> xInfoSet
;
517 if( rArguments
.hasElements() )
518 rArguments
.getConstArray()[0] >>= xInfoSet
;
519 OSL_ENSURE( xInfoSet
.is(), "missing property set" );
522 xInfoSet
->setPropertyValue( "StreamName", Any( sStreamName
) );
526 bRet
= WriteThroughComponent(
527 xOutputStream
, xComponent
, rxContext
,
528 pServiceName
, rArguments
, rMediaDesc
);
530 catch ( uno::Exception
& )
538 bool SwXMLWriter::WriteThroughComponent(
539 const uno::Reference
<io::XOutputStream
> & xOutputStream
,
540 const uno::Reference
<XComponent
> & xComponent
,
541 const uno::Reference
<XComponentContext
> & rxContext
,
542 const char* pServiceName
,
543 const Sequence
<Any
> & rArguments
,
544 const Sequence
<PropertyValue
> & rMediaDesc
)
546 OSL_ENSURE( xOutputStream
.is(), "I really need an output stream!" );
547 OSL_ENSURE( xComponent
.is(), "Need component!" );
548 OSL_ENSURE( nullptr != pServiceName
, "Need component name!" );
551 uno::Reference
< xml::sax::XWriter
> xSaxWriter
= xml::sax::Writer::create(rxContext
);
552 SAL_INFO( "sw.filter", "SAX-Writer created" );
553 // connect XML writer to output stream
554 xSaxWriter
->setOutputStream( xOutputStream
);
556 // prepare arguments (prepend doc handler to given arguments)
557 Sequence
<Any
> aArgs( 1 + rArguments
.getLength() );
558 auto pArgs
= aArgs
.getArray();
559 *pArgs
<<= xSaxWriter
;
560 std::copy(rArguments
.begin(), rArguments
.end(), std::next(pArgs
));
562 // get filter component
563 uno::Reference
< document::XExporter
> xExporter(
564 rxContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
565 OUString::createFromAscii(pServiceName
), aArgs
, rxContext
), UNO_QUERY
);
566 OSL_ENSURE( xExporter
.is(),
567 "can't instantiate export filter component" );
568 if( !xExporter
.is() )
570 SAL_INFO( "sw.filter", pServiceName
<< " instantiated." );
571 // connect model and filter
572 xExporter
->setSourceDocument( xComponent
);
575 SAL_INFO( "sw.filter", "call filter()" );
576 uno::Reference
<XFilter
> xFilter( xExporter
, UNO_QUERY
);
577 return xFilter
->filter( rMediaDesc
);
581 [[maybe_unused
]] std::u16string_view
/*rName*/, const OUString
& rBaseURL
, WriterRef
& xRet
)
583 xRet
= new SwXMLWriter( rBaseURL
);
586 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */