diff options
Diffstat (limited to '')
-rw-r--r-- | xmloff/source/style/styleexp.cxx | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/xmloff/source/style/styleexp.cxx b/xmloff/source/style/styleexp.cxx new file mode 100644 index 000000000..b311dbdf6 --- /dev/null +++ b/xmloff/source/style/styleexp.cxx @@ -0,0 +1,592 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/any.hxx> +#include <xmloff/xmlnamespace.hxx> +#include <xmloff/xmltoken.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/xmlexppr.hxx> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <com/sun/star/style/XStyleFamiliesSupplier.hpp> +#include <com/sun/star/style/XStyle.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XPropertyState.hpp> +#include <com/sun/star/document/XEventsSupplier.hpp> +#include <com/sun/star/text/XChapterNumberingSupplier.hpp> +#include <xmloff/xmlaustp.hxx> +#include <xmloff/styleexp.hxx> +#include <xmloff/xmlexp.hxx> +#include <xmloff/XMLEventExport.hxx> +#include <xmloff/maptype.hxx> +#include <memory> +#include <set> +#include <prstylecond.hxx> +#include <sal/log.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::text; +using namespace ::xmloff::token; + +using ::com::sun::star::document::XEventsSupplier; + +constexpr OUStringLiteral gsIsPhysical( u"IsPhysical" ); +constexpr OUStringLiteral gsIsAutoUpdate( u"IsAutoUpdate" ); +constexpr OUStringLiteral gsFollowStyle( u"FollowStyle" ); +constexpr OUStringLiteral gsNumberingStyleName( u"NumberingStyleName" ); +constexpr OUStringLiteral gsOutlineLevel( u"OutlineLevel" ); + +XMLStyleExport::XMLStyleExport( + SvXMLExport& rExp, + SvXMLAutoStylePoolP *pAutoStyleP ) : + rExport( rExp ), + pAutoStylePool( pAutoStyleP ) +{ +} + +XMLStyleExport::~XMLStyleExport() +{ +} + +void XMLStyleExport::exportStyleAttributes( const Reference< XStyle >& ) +{ +} + +void XMLStyleExport::exportStyleContent( const Reference< XStyle >& rStyle ) +{ + Reference< XPropertySet > xPropSet( rStyle, UNO_QUERY ); + assert(xPropSet.is()); + + try + { + uno::Any aProperty = xPropSet->getPropertyValue( "ParaStyleConditions" ); + uno::Sequence< beans::NamedValue > aSeq; + + aProperty >>= aSeq; + + for (beans::NamedValue const& rNamedCond : std::as_const(aSeq)) + { + OUString aStyleName; + + if (rNamedCond.Value >>= aStyleName) + { + if (!aStyleName.isEmpty()) + { + OUString aExternal = GetParaStyleCondExternal(rNamedCond.Name); + + if (!aExternal.isEmpty()) + { + bool bEncoded; + + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + XML_CONDITION, + aExternal); + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + XML_APPLY_STYLE_NAME, + GetExport().EncodeStyleName( aStyleName, + &bEncoded ) ); + SvXMLElementExport aElem( GetExport(), + XML_NAMESPACE_STYLE, + XML_MAP, + true, + true ); + } + } + } + } + } + catch( const beans::UnknownPropertyException& ) + { + } +} + +namespace +{ +/// Writes <style:style style:list-level="..."> for Writer paragraph styles. +void ExportStyleListlevel(const uno::Reference<beans::XPropertySetInfo>& xPropSetInfo, + const uno::Reference<beans::XPropertyState>& xPropState, + const uno::Reference<beans::XPropertySet>& xPropSet, SvXMLExport& rExport) +{ + if (!xPropSetInfo->hasPropertyByName("NumberingLevel")) + { + SAL_WARN("xmloff", "ExportStyleListlevel: no NumberingLevel for a Writer paragraph style"); + return; + } + + if (xPropState->getPropertyState("NumberingLevel") != beans::PropertyState_DIRECT_VALUE) + { + return; + } + + sal_Int16 nNumberingLevel{}; + if (!(xPropSet->getPropertyValue("NumberingLevel") >>= nNumberingLevel)) + { + return; + } + + // The spec is positiveInteger (1-based), but the implementation is 0-based. + rExport.AddAttribute(XML_NAMESPACE_STYLE, XML_LIST_LEVEL, OUString::number(++nNumberingLevel)); +} +} + +bool XMLStyleExport::exportStyle( + const Reference< XStyle >& rStyle, + const OUString& rXMLFamily, + const rtl::Reference < SvXMLExportPropertyMapper >& rPropMapper, + const Reference< XNameAccess >& xStyles, + const OUString* pPrefix ) +{ + Reference< XPropertySet > xPropSet( rStyle, UNO_QUERY ); + if (!xPropSet) + return false; + + Reference< XPropertySetInfo > xPropSetInfo = + xPropSet->getPropertySetInfo(); + Any aAny; + + // Don't export styles that aren't existing really. This may be the + // case for StarOffice Writer's pool styles. + if( xPropSetInfo->hasPropertyByName( gsIsPhysical ) ) + { + aAny = xPropSet->getPropertyValue( gsIsPhysical ); + if( !*o3tl::doAccess<bool>(aAny) ) + return false; + } + + // <style:style ...> + GetExport().CheckAttrList(); + + // style:name="..." + OUString sName; + + if(pPrefix) + sName = *pPrefix; + sName += rStyle->getName(); + + bool bEncoded = false; + const OUString sEncodedStyleName(GetExport().EncodeStyleName( sName, &bEncoded )); + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, sEncodedStyleName ); + + if( bEncoded ) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, + sName); + + // style:family="..." + if( !rXMLFamily.isEmpty() ) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, rXMLFamily); + + if ( xPropSetInfo->hasPropertyByName( "Hidden" ) ) + { + aAny = xPropSet->getPropertyValue( "Hidden" ); + bool bHidden = false; + if ((aAny >>= bHidden) && bHidden + && GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED) + { + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_HIDDEN, "true"); + GetExport().AddAttribute(XML_NAMESPACE_STYLE, XML_HIDDEN, "true"); // FIXME for compatibility + } + } + + // style:parent-style-name="..." + OUString sParentString(rStyle->getParentStyle()); + OUString sParent; + + if(!sParentString.isEmpty()) + { + if(pPrefix) + sParent = *pPrefix; + sParent += sParentString; + } + + if( !sParent.isEmpty() ) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_PARENT_STYLE_NAME, + GetExport().EncodeStyleName( sParent ) ); + + // style:next-style-name="..." (paragraph styles only) + if( xPropSetInfo->hasPropertyByName( gsFollowStyle ) ) + { + aAny = xPropSet->getPropertyValue( gsFollowStyle ); + OUString sNextName; + aAny >>= sNextName; + if( sName != sNextName ) + { + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_NEXT_STYLE_NAME, + GetExport().EncodeStyleName( sNextName ) ); + } + } + + // style:linked-style-name="..." (SW paragraph and character styles only) + if (xPropSetInfo->hasPropertyByName("LinkStyle")) + { + aAny = xPropSet->getPropertyValue("LinkStyle"); + OUString sLinkName; + aAny >>= sLinkName; + if (!sLinkName.isEmpty() + && (GetExport().getSaneDefaultVersion() & SvtSaveOptions::ODFSVER_EXTENDED)) + { + GetExport().AddAttribute(XML_NAMESPACE_LO_EXT, XML_LINKED_STYLE_NAME, + GetExport().EncodeStyleName(sLinkName)); + } + } + + // style:auto-update="..." (SW only) + if( xPropSetInfo->hasPropertyByName( gsIsAutoUpdate ) ) + { + aAny = xPropSet->getPropertyValue( gsIsAutoUpdate ); + if( *o3tl::doAccess<bool>(aAny) ) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_AUTO_UPDATE, + XML_TRUE ); + } + + // style:default-outline-level"..." + sal_Int32 nOutlineLevel = 0; + if( xPropSetInfo->hasPropertyByName( gsOutlineLevel ) ) + { + Reference< XPropertyState > xPropState( xPropSet, uno::UNO_QUERY ); + if( PropertyState_DIRECT_VALUE == xPropState->getPropertyState( gsOutlineLevel ) ) + { + aAny = xPropSet->getPropertyValue( gsOutlineLevel ); + aAny >>= nOutlineLevel; + if( nOutlineLevel > 0 ) + { + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + XML_DEFAULT_OUTLINE_LEVEL, + OUString::number(nOutlineLevel) ); + } + else + { + /* Empty value for style:default-outline-level does exist + since ODF 1.2. Thus, suppress its export for former versions. (#i104889#) + */ + if ( ( GetExport().getExportFlags() & SvXMLExportFlags::OASIS ) && + GetExport().getSaneDefaultVersion() >= SvtSaveOptions::ODFSVER_012) + { + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + XML_DEFAULT_OUTLINE_LEVEL, + OUString( "" )); + } + } + } + } + + // style:list-style-name="..." (SW paragraph styles only) + if( xPropSetInfo->hasPropertyByName( gsNumberingStyleName ) ) + { + Reference< XPropertyState > xPropState( xPropSet, uno::UNO_QUERY ); + if( PropertyState_DIRECT_VALUE == + xPropState->getPropertyState( gsNumberingStyleName ) ) + { + aAny = xPropSet->getPropertyValue( gsNumberingStyleName ); + if( aAny.hasValue() ) + { + OUString sListName; + aAny >>= sListName; + + /* A direct set empty list style has to be written. Otherwise, + this information is lost and causes an error, if the parent + style has a list style set. (#i69523#) + */ + if ( sListName.isEmpty() ) + { + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + XML_LIST_STYLE_NAME, + sListName /* empty string */); + } + else + { + // Written OpenDocument file format doesn't fit to the created text document (#i69627#) + bool bSuppressListStyle( false ); + { + if ( !GetExport().writeOutlineStyleAsNormalListStyle() ) + { + Reference< XChapterNumberingSupplier > xCNSupplier + (GetExport().GetModel(), UNO_QUERY); + + if (xCNSupplier.is()) + { + Reference< XIndexReplace > xNumRule + ( xCNSupplier->getChapterNumberingRules() ); + assert(xNumRule.is()); + + Reference< XPropertySet > xNumRulePropSet + (xNumRule, UNO_QUERY); + OUString sOutlineName; + xNumRulePropSet->getPropertyValue("Name") + >>= sOutlineName; + bSuppressListStyle = sListName == sOutlineName; + } + } + } + + if ( !sListName.isEmpty() && !bSuppressListStyle ) + { + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + XML_LIST_STYLE_NAME, + GetExport().EncodeStyleName( sListName ) ); + + ExportStyleListlevel(xPropSetInfo, xPropState, xPropSet, GetExport()); + } + } + } + } + else if( nOutlineLevel > 0 ) + { + + bool bNoInheritedListStyle( true ); + + Reference<XStyle> xStyle( xPropState, UNO_QUERY ); + while ( xStyle.is() ) + { + OUString aParentStyle( xStyle->getParentStyle() ); + if ( aParentStyle.isEmpty() || !xStyles->hasByName( aParentStyle ) ) + { + break; + } + else + { + xPropState.set( xStyles->getByName( aParentStyle ), UNO_QUERY ); + if ( !xPropState.is() ) + { + break; + } + if ( xPropState->getPropertyState( gsNumberingStyleName ) == PropertyState_DIRECT_VALUE ) + { + bNoInheritedListStyle = false; + break; + } + else + { + xStyle.set( xPropState, UNO_QUERY ); + } + } + } + if ( bNoInheritedListStyle ) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, + XML_LIST_STYLE_NAME, + OUString( "" )); + } + } + + // style:pool-id="..." is not required any longer since we use + // english style names only + exportStyleAttributes( rStyle ); + + // TODO: style:help-file-name="..." and style:help-id="..." can neither + // be modified by UI nor by API and that for, have not to be exported + // currently. + + { + // <style:style> + SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_STYLE, XML_STYLE, + true, true ); + + rPropMapper->SetStyleName( sName ); + + // <style:properties> + ::std::vector< XMLPropertyState > aPropStates = + rPropMapper->Filter(GetExport(), xPropSet, true); + bool const bUseExtensionNamespaceForGraphicProperties( + rXMLFamily != "drawing-page" && + rXMLFamily != "graphic" && + rXMLFamily != "presentation" && + rXMLFamily != "chart"); + rPropMapper->exportXML( GetExport(), aPropStates, + SvXmlExportFlags::IGN_WS, + bUseExtensionNamespaceForGraphicProperties ); + + rPropMapper->SetStyleName( OUString() ); + + exportStyleContent( rStyle ); + + // <script:events>, if they are supported by this style + Reference<XEventsSupplier> xEventsSupp(rStyle, UNO_QUERY); + GetExport().GetEventExport().Export(xEventsSupp); + } + return true; +} + +void XMLStyleExport::exportDefaultStyle( + const Reference< XPropertySet >& xPropSet, + const OUString& rXMLFamily, + const rtl::Reference < SvXMLExportPropertyMapper >& rPropMapper ) +{ + // <style:default-style ...> + GetExport().CheckAttrList(); + + { + // style:family="..." + if( !rXMLFamily.isEmpty() ) + GetExport().AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, + rXMLFamily ); + // <style:style> + SvXMLElementExport aElem( GetExport(), XML_NAMESPACE_STYLE, + XML_DEFAULT_STYLE, + true, true ); + // <style:properties> + ::std::vector< XMLPropertyState > aPropStates = + rPropMapper->FilterDefaults(GetExport(), xPropSet); + rPropMapper->exportXML( GetExport(), aPropStates, + SvXmlExportFlags::IGN_WS ); + } +} + +void XMLStyleExport::exportStyleFamily( + const char *pFamily, + const OUString& rXMLFamily, + const rtl::Reference < SvXMLExportPropertyMapper >& rPropMapper, + bool bUsed, XmlStyleFamily nFamily, const OUString* pPrefix) +{ + const OUString sFamily(OUString::createFromAscii(pFamily )); + exportStyleFamily( sFamily, rXMLFamily, rPropMapper, bUsed, nFamily, + pPrefix); +} + +void XMLStyleExport::exportStyleFamily( + const OUString& rFamily, const OUString& rXMLFamily, + const rtl::Reference < SvXMLExportPropertyMapper >& rPropMapper, + bool bUsed, XmlStyleFamily nFamily, const OUString* pPrefix) +{ + assert(GetExport().GetModel().is()); + Reference< XStyleFamiliesSupplier > xFamiliesSupp( GetExport().GetModel(), UNO_QUERY ); + if( !xFamiliesSupp.is() ) + return; // family not available in current model + + Reference< XNameAccess > xStyleCont; + + Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() ); + if( xFamilies->hasByName( rFamily ) ) + xFamilies->getByName( rFamily ) >>= xStyleCont; + + if( !xStyleCont.is() ) + return; + + // If next styles are supported and used styles should be exported only, + // the next style may be unused but has to be exported, too. In this case + // the names of all exported styles are remembered. + std::optional<std::set<OUString> > xExportedStyles; + bool bFirstStyle = true; + + const uno::Sequence< OUString> aSeq = xStyleCont->getElementNames(); + for(const auto& rName : aSeq) + { + Reference< XStyle > xStyle; + try + { + xStyleCont->getByName( rName ) >>= xStyle; + } + catch(const lang::IndexOutOfBoundsException&) + { + // due to bugs in prior versions it is possible that + // a binary file is missing some critical styles. + // The only possible way to deal with this is to + // not export them here and remain silent. + continue; + } + catch(css::container::NoSuchElementException&) + { + continue; + } + + assert(xStyle.is()); + if (!bUsed || xStyle->isInUse()) + { + bool bExported = exportStyle( xStyle, rXMLFamily, rPropMapper, + xStyleCont,pPrefix ); + if (bUsed && bFirstStyle && bExported) + { + // If this is the first style, find out whether next styles + // are supported. + Reference< XPropertySet > xPropSet( xStyle, UNO_QUERY ); + Reference< XPropertySetInfo > xPropSetInfo = + xPropSet->getPropertySetInfo(); + + if (xPropSetInfo->hasPropertyByName( gsFollowStyle )) + xExportedStyles.emplace(); + bFirstStyle = false; + } + + if (xExportedStyles && bExported) + { + // If next styles are supported, remember this style's name. + xExportedStyles->insert( xStyle->getName() ); + } + } + + // if an auto style pool is given, remember this style's name as a + // style name that must not be used by automatic styles. + if (pAutoStylePool) + pAutoStylePool->RegisterName( nFamily, xStyle->getName() ); + } + + if( !xExportedStyles ) + return; + + // if next styles are supported, export all next styles that are + // unused and that for, haven't been exported in the first loop. + for(const auto& rName : aSeq) + { + Reference< XStyle > xStyle; + xStyleCont->getByName( rName ) >>= xStyle; + + assert(xStyle.is()); + + Reference< XPropertySet > xPropSet( xStyle, UNO_QUERY ); + Reference< XPropertySetInfo > xPropSetInfo( xPropSet->getPropertySetInfo() ); + + // styles that aren't existing really are ignored. + if (xPropSetInfo->hasPropertyByName( gsIsPhysical )) + { + Any aAny( xPropSet->getPropertyValue( gsIsPhysical ) ); + if (!*o3tl::doAccess<bool>(aAny)) + continue; + } + + if (!xStyle->isInUse()) + continue; + + if (!xPropSetInfo->hasPropertyByName( gsFollowStyle )) + { + continue; + } + + OUString sNextName; + xPropSet->getPropertyValue( gsFollowStyle ) >>= sNextName; + OUString sTmp( sNextName ); + // if the next style hasn't been exported by now, export it now + // and remember its name. + if (xStyle->getName() != sNextName && + 0 == xExportedStyles->count( sTmp )) + { + xStyleCont->getByName( sNextName ) >>= xStyle; + assert(xStyle.is()); + + if (exportStyle(xStyle, rXMLFamily, rPropMapper, xStyleCont, pPrefix)) + xExportedStyles->insert( sTmp ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |