/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wrtxml.hxx" #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::container; using namespace ::com::sun::star::document; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::lang; SwXMLWriter::SwXMLWriter( const OUString& rBaseURL ) { SetBaseURL( rBaseURL ); } SwXMLWriter::~SwXMLWriter() { } ErrCode SwXMLWriter::Write_( const uno::Reference < task::XStatusIndicator >& xStatusIndicator, const OUString& aDocHierarchicalName ) { // Get service factory uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); // Get data sink ... uno::Reference xGraphicStorageHandler; rtl::Reference xGraphicHelper ; uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver; rtl::Reference xObjectHelper; OSL_ENSURE( m_xStg.is(), "Where is my storage?" ); xGraphicHelper = SvXMLGraphicHelper::Create( m_xStg, SvXMLGraphicHelperMode::Write ); xGraphicStorageHandler = xGraphicHelper.get(); SfxObjectShell *pPersist = m_pDoc->GetPersist(); if( pPersist ) { xObjectHelper = SvXMLEmbeddedObjectHelper::Create( m_xStg, *pPersist, SvXMLEmbeddedObjectHelperMode::Write ); xObjectResolver = xObjectHelper.get(); } // create and prepare the XPropertySet that gets passed through // the components, and the XStatusIndicator that shows progress to // the user. // create XPropertySet with three properties for status indicator comphelper::PropertyMapEntry const aInfoMap[] = { { OUString("ProgressRange"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, { OUString("ProgressMax"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, { OUString("ProgressCurrent"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, { OUString("WrittenNumberStyles"), 0, cppu::UnoType>::get(), beans::PropertyAttribute::MAYBEVOID, 0}, { OUString("UsePrettyPrinting"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0}, { OUString("ShowChanges"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("RedlineProtectionKey"), 0, cppu::UnoType>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("BaseURI"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("StreamRelPath"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("StreamName"), 0, ::cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("AutoTextMode"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("StyleNames"), 0, cppu::UnoType>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("StyleFamilies"), 0, cppu::UnoType>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, // #i69627# { OUString("OutlineStyleAsNormalListStyle"), 0, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString("TargetStorage"),0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, { OUString(), 0, css::uno::Type(), 0, 0 } }; uno::Reference< beans::XPropertySet > xInfoSet( comphelper::GenericPropertySet_CreateInstance( new comphelper::PropertySetInfo( aInfoMap ) ) ); xInfoSet->setPropertyValue( "TargetStorage", Any( m_xStg ) ); if (m_bShowProgress) { // set progress range and start status indicator sal_Int32 nProgressRange(1000000); if (xStatusIndicator.is()) { xStatusIndicator->start(SwResId( STR_STATSTR_SWGWRITE), nProgressRange); } xInfoSet->setPropertyValue("ProgressRange", Any(nProgressRange)); xInfoSet->setPropertyValue("ProgressMax", Any(static_cast < sal_Int32 >( -1 ))); } SvtSaveOptions aSaveOpt; xInfoSet->setPropertyValue( "UsePrettyPrinting", makeAny(aSaveOpt.IsPrettyPrinting()) ); // save show redline mode ... RedlineFlags const nOrigRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); RedlineFlags nRedlineFlags(nOrigRedlineFlags); bool isShowChanges; // TODO: ideally this would be stored per-view... SwRootFrame const*const pLayout(m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()); isShowChanges = pLayout == nullptr || !pLayout->IsHideRedlines(); xInfoSet->setPropertyValue("ShowChanges", makeAny(isShowChanges)); // ... and hide redlines for export nRedlineFlags &= ~RedlineFlags::ShowMask; nRedlineFlags |= RedlineFlags::ShowInsert; m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); // Set base URI xInfoSet->setPropertyValue( "BaseURI", makeAny( GetBaseURL() ) ); if( SfxObjectCreateMode::EMBEDDED == m_pDoc->GetDocShell()->GetCreateMode() ) { const OUString aName( !aDocHierarchicalName.isEmpty() ? aDocHierarchicalName : OUString( "dummyObjectName" ) ); xInfoSet->setPropertyValue( "StreamRelPath", makeAny( aName ) ); } if( m_bBlock ) { xInfoSet->setPropertyValue( "AutoTextMode", makeAny(true) ); } // #i69627# const bool bOASIS = ( SotStorage::GetVersion( m_xStg ) > SOFFICE_FILEFORMAT_60 ); if ( bOASIS && docfunc::HasOutlineStyleToBeWrittenAsNormalListStyle( *m_pDoc ) ) { xInfoSet->setPropertyValue( "OutlineStyleAsNormalListStyle", makeAny( true ) ); } // filter arguments // - graphics + object resolver for styles + content // - status indicator // - info property set // - else empty sal_Int32 nArgs = 1; if( xStatusIndicator.is() ) nArgs++; Sequence < Any > aEmptyArgs( nArgs ); Any *pArgs = aEmptyArgs.getArray(); *pArgs++ <<= xInfoSet; if( xStatusIndicator.is() ) *pArgs++ <<= xStatusIndicator; if( xGraphicStorageHandler.is() ) nArgs++; if( xObjectResolver.is() ) nArgs++; Sequence < Any > aFilterArgs( nArgs ); pArgs = aFilterArgs.getArray(); *pArgs++ <<= xInfoSet; if( xGraphicStorageHandler.is() ) *pArgs++ <<= xGraphicStorageHandler; if( xObjectResolver.is() ) *pArgs++ <<= xObjectResolver; if( xStatusIndicator.is() ) *pArgs++ <<= xStatusIndicator; //Get model uno::Reference< lang::XComponent > xModelComp = m_pDoc->GetDocShell()->GetModel(); OSL_ENSURE( xModelComp.is(), "XMLWriter::Write: got no model" ); if( !xModelComp.is() ) return ERR_SWG_WRITE_ERROR; PutNumFormatFontsInAttrPool(); PutEditEngFontsInAttrPool(); // properties Sequence < PropertyValue > aProps( m_pOrigFileName ? 1 : 0 ); if( m_pOrigFileName ) { PropertyValue *pProps = aProps.getArray(); pProps->Name = "FileName"; pProps->Value <<= *m_pOrigFileName; } // export sub streams for package, else full stream into a file bool bWarn = false; // RDF metadata: export if ODF >= 1.2 // N.B.: embedded documents have their own manifest.rdf! if ( bOASIS ) { const uno::Reference xPropSet(m_xStg, uno::UNO_QUERY_THROW); try { OUString Version; // ODF >= 1.2 if ((xPropSet->getPropertyValue("Version") >>= Version) && Version != ODFVER_010_TEXT && Version != ODFVER_011_TEXT) { const uno::Reference xDMA( xModelComp, uno::UNO_QUERY_THROW); xDMA->storeMetadataToStorage(m_xStg); } } catch (beans::UnknownPropertyException &) { /* ignore */ } catch (uno::Exception &) { bWarn = true; } } bool bStoreMeta = ( SfxObjectCreateMode::EMBEDDED != m_pDoc->GetDocShell()->GetCreateMode() ); if ( !bStoreMeta ) { try { Reference< frame::XModule > xModule( xModelComp, UNO_QUERY ); if ( xModule.is() ) { const OUString aModuleID = xModule->getIdentifier(); bStoreMeta = !aModuleID.isEmpty() && ( aModuleID == "com.sun.star.sdb.FormDesign" || aModuleID == "com.sun.star.sdb.TextReportDesign" ); } } catch( uno::Exception& ) {} } OUString sWarnFile; if( !m_bOrganizerMode && !m_bBlock && bStoreMeta ) { if( !WriteThroughComponent( xModelComp, "meta.xml", xContext, (bOASIS ? "com.sun.star.comp.Writer.XMLOasisMetaExporter" : "com.sun.star.comp.Writer.XMLMetaExporter"), aEmptyArgs, aProps ) ) { bWarn = true; sWarnFile = "meta.xml"; } } if( !m_bBlock ) { if( !WriteThroughComponent( xModelComp, "settings.xml", xContext, (bOASIS ? "com.sun.star.comp.Writer.XMLOasisSettingsExporter" : "com.sun.star.comp.Writer.XMLSettingsExporter"), aEmptyArgs, aProps ) ) { if( !bWarn ) { bWarn = true; sWarnFile = "settings.xml"; } } } bool bErr = false; OUString sErrFile; if( !WriteThroughComponent( xModelComp, "styles.xml", xContext, (bOASIS ? "com.sun.star.comp.Writer.XMLOasisStylesExporter" : "com.sun.star.comp.Writer.XMLStylesExporter"), aFilterArgs, aProps ) ) { bErr = true; sErrFile = "styles.xml"; } if( !m_bOrganizerMode && !bErr ) { if( !WriteThroughComponent( xModelComp, "content.xml", xContext, (bOASIS ? "com.sun.star.comp.Writer.XMLOasisContentExporter" : "com.sun.star.comp.Writer.XMLContentExporter"), aFilterArgs, aProps ) ) { bErr = true; sErrFile = "content.xml"; } } if( m_pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() && m_pDoc->getIDocumentStatistics().GetDocStat().nPage > 1 && !(m_bOrganizerMode || m_bBlock || bErr || // sw_redlinehide: disable layout cache for now m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout()->HasMergedParas())) { try { uno::Reference < io::XStream > xStm = m_xStg->openStreamElement( "layout-cache", embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); std::unique_ptr pStream = utl::UcbStreamHelper::CreateStream( xStm ); if( !pStream->GetError() ) { uno::Reference < beans::XPropertySet > xSet( xStm, UNO_QUERY ); uno::Any aAny2; aAny2 <<= OUString("application/binary"); xSet->setPropertyValue("MediaType", aAny2 ); m_pDoc->WriteLayoutCache( *pStream ); } } catch ( uno::Exception& ) { } } if( xGraphicHelper ) xGraphicHelper->dispose(); xGraphicHelper.clear(); xGraphicStorageHandler = nullptr; if( xObjectHelper ) xObjectHelper->dispose(); xObjectHelper.clear(); xObjectResolver = nullptr; // restore redline mode nRedlineFlags = m_pDoc->getIDocumentRedlineAccess().GetRedlineFlags(); nRedlineFlags &= ~RedlineFlags::ShowMask; nRedlineFlags |= RedlineFlags::ShowInsert; nRedlineFlags |= nOrigRedlineFlags & RedlineFlags::ShowMask; m_pDoc->getIDocumentRedlineAccess().SetRedlineFlags( nRedlineFlags ); if (xStatusIndicator.is()) { xStatusIndicator->end(); } if( bErr ) { if( !sErrFile.isEmpty() ) return *new StringErrorInfo( ERR_WRITE_ERROR_FILE, sErrFile, DialogMask::ButtonsOk | DialogMask::MessageError ); return ERR_SWG_WRITE_ERROR; } else if( bWarn ) { if( !sWarnFile.isEmpty() ) return *new StringErrorInfo( WARN_WRITE_ERROR_FILE, sWarnFile, DialogMask::ButtonsOk | DialogMask::MessageError ); return WARN_SWG_FEATURES_LOST; } return ERRCODE_NONE; } ErrCode SwXMLWriter::WriteStorage() { return Write_( uno::Reference < task::XStatusIndicator >(), OUString() ); } ErrCode SwXMLWriter::WriteMedium( SfxMedium& aTargetMedium ) { uno::Reference < task::XStatusIndicator > xStatusIndicator; OUString aName; const SfxUnoAnyItem* pStatusBarItem = static_cast( aTargetMedium.GetItemSet()->GetItem(SID_PROGRESS_STATUSBAR_CONTROL) ); if ( pStatusBarItem ) pStatusBarItem->GetValue() >>= xStatusIndicator; const SfxStringItem* pDocHierarchItem = static_cast( aTargetMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME) ); if ( pDocHierarchItem ) aName = pDocHierarchItem->GetValue(); return Write_( xStatusIndicator, aName ); } ErrCode SwXMLWriter::Write( SwPaM& rPaM, SfxMedium& rMed, const OUString* pFileName ) { return IsStgWriter() ? static_cast(this)->Write( rPaM, rMed.GetOutputStorage(), pFileName, &rMed ) : static_cast(this)->Write( rPaM, *rMed.GetOutStream(), pFileName ); } bool SwXMLWriter::WriteThroughComponent( const uno::Reference & xComponent, const char* pStreamName, const uno::Reference & rxContext, const char* pServiceName, const Sequence & rArguments, const Sequence & rMediaDesc ) { OSL_ENSURE( m_xStg.is(), "Need storage!" ); OSL_ENSURE( nullptr != pStreamName, "Need stream name!" ); OSL_ENSURE( nullptr != pServiceName, "Need service name!" ); SAL_INFO( "sw.filter", "SwXMLWriter::WriteThroughComponent : stream " << pStreamName ); // open stream bool bRet = false; try { const OUString sStreamName = OUString::createFromAscii( pStreamName ); uno::Reference xStream = m_xStg->openStreamElement( sStreamName, embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); uno::Reference xSet( xStream, uno::UNO_QUERY ); if( !xSet.is() ) return false; xSet->setPropertyValue("MediaType", Any(OUString("text/xml")) ); // even plain stream should be encrypted in encrypted documents xSet->setPropertyValue( "UseCommonStoragePasswordEncryption", makeAny(true) ); // set buffer and create outputstream uno::Reference< io::XOutputStream > xOutputStream = xStream->getOutputStream(); // set Base URL uno::Reference< beans::XPropertySet > xInfoSet; if( rArguments.hasElements() ) rArguments.getConstArray()[0] >>= xInfoSet; OSL_ENSURE( xInfoSet.is(), "missing property set" ); if( xInfoSet.is() ) { xInfoSet->setPropertyValue( "StreamName", makeAny( sStreamName ) ); } // write the stuff bRet = WriteThroughComponent( xOutputStream, xComponent, rxContext, pServiceName, rArguments, rMediaDesc ); } catch ( uno::Exception& ) { } return bRet; } bool SwXMLWriter::WriteThroughComponent( const uno::Reference & xOutputStream, const uno::Reference & xComponent, const uno::Reference & rxContext, const char* pServiceName, const Sequence & rArguments, const Sequence & rMediaDesc ) { OSL_ENSURE( xOutputStream.is(), "I really need an output stream!" ); OSL_ENSURE( xComponent.is(), "Need component!" ); OSL_ENSURE( nullptr != pServiceName, "Need component name!" ); // get component uno::Reference< xml::sax::XWriter > xSaxWriter = xml::sax::Writer::create(rxContext); SAL_INFO( "sw.filter", "SAX-Writer created" ); // connect XML writer to output stream xSaxWriter->setOutputStream( xOutputStream ); // prepare arguments (prepend doc handler to given arguments) Sequence aArgs( 1 + rArguments.getLength() ); aArgs[0] <<= xSaxWriter; std::copy(rArguments.begin(), rArguments.end(), std::next(aArgs.begin())); // get filter component uno::Reference< document::XExporter > xExporter( rxContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii(pServiceName), aArgs, rxContext), UNO_QUERY); OSL_ENSURE( xExporter.is(), "can't instantiate export filter component" ); if( !xExporter.is() ) return false; SAL_INFO( "sw.filter", pServiceName << " instantiated." ); // connect model and filter xExporter->setSourceDocument( xComponent ); // filter! SAL_INFO( "sw.filter", "call filter()" ); uno::Reference xFilter( xExporter, UNO_QUERY ); return xFilter->filter( rMediaDesc ); } void GetXMLWriter( [[maybe_unused]] const OUString& /*rName*/, const OUString& rBaseURL, WriterRef& xRet ) { xRet = new SwXMLWriter( rBaseURL ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */