/* -*- 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/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::xmloff::token; namespace sw { class StoredChapterNumberingRules : public ::cppu::WeakImplHelper { private: // TODO in case this ever becomes accessible via api need a invalidate SwChapterNumRules & m_rNumRules; sal_uInt16 const m_nIndex; SwNumRulesWithName * GetOrCreateRules() { SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); if (!pRules) { m_rNumRules.CreateEmptyNumRule(m_nIndex); pRules = m_rNumRules.GetRules(m_nIndex); assert(pRules); } return const_cast(pRules); } public: StoredChapterNumberingRules( SwChapterNumRules & rNumRules, sal_uInt16 const nIndex) : m_rNumRules(rNumRules) , m_nIndex(nIndex) { assert(m_nIndex < SwChapterNumRules::nMaxRules); } // XNamed virtual OUString SAL_CALL getName() override { SolarMutexGuard g; SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); if (!pRules) { return OUString(); } return pRules->GetName(); } virtual void SAL_CALL setName(OUString const& rName) override { SolarMutexGuard g; SwNumRulesWithName *const pRules(GetOrCreateRules()); pRules->SetName(rName); } // XElementAccess virtual uno::Type SAL_CALL getElementType() override { return ::cppu::UnoType>::get(); } virtual ::sal_Bool SAL_CALL hasElements() override { return true; } // XIndexAccess virtual sal_Int32 SAL_CALL getCount() override { return MAXLEVEL; } virtual uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override { if (nIndex < 0 || MAXLEVEL <= nIndex) throw lang::IndexOutOfBoundsException(); SolarMutexGuard g; SwNumRulesWithName const* pRules(m_rNumRules.GetRules(m_nIndex)); if (!pRules) { return uno::Any(); } SwNumFormat const* pNumFormat(nullptr); OUString const* pCharStyleName(nullptr); pRules->GetNumFormat(nIndex, pNumFormat, pCharStyleName); if (!pNumFormat) { // the dialog only fills in those levels that are non-default return uno::Any(); // the export will ignore this level, yay } assert(pCharStyleName); OUString dummy; // pass in empty HeadingStyleName - can't import anyway uno::Sequence const ret( SwXNumberingRules::GetPropertiesForNumFormat( *pNumFormat, *pCharStyleName, &dummy, "")); return uno::makeAny(ret); } // XIndexReplace virtual void SAL_CALL replaceByIndex( sal_Int32 nIndex, uno::Any const& rElement) override { if (nIndex < 0 || MAXLEVEL <= nIndex) throw lang::IndexOutOfBoundsException(); uno::Sequence props; if (!(rElement >>= props)) throw lang::IllegalArgumentException("invalid type", static_cast< ::cppu::OWeakObject*>(this), 1); SolarMutexGuard g; SwNumFormat aNumberFormat; OUString charStyleName; SwXNumberingRules::SetPropertiesToNumFormat( aNumberFormat, charStyleName, nullptr, nullptr, nullptr, nullptr, nullptr, props); SwNumRulesWithName *const pRules(GetOrCreateRules()); pRules->SetNumFormat(nIndex, aNumberFormat, charStyleName); } }; class StoredChapterNumberingExport : public SvXMLExport { public: StoredChapterNumberingExport( uno::Reference const& xContext, OUString const& rFileName, uno::Reference const& xHandler) : SvXMLExport(xContext, "sw::StoredChapterNumberingExport", rFileName, util::MeasureUnit::CM, xHandler) { GetNamespaceMap_().Add(GetXMLToken(XML_NP_OFFICE), GetXMLToken(XML_N_OFFICE), XML_NAMESPACE_OFFICE); GetNamespaceMap_().Add(GetXMLToken(XML_NP_TEXT), GetXMLToken(XML_N_TEXT), XML_NAMESPACE_TEXT); GetNamespaceMap_().Add(GetXMLToken(XML_NP_STYLE), GetXMLToken(XML_N_STYLE), XML_NAMESPACE_STYLE); GetNamespaceMap_().Add(GetXMLToken(XML_NP_FO), GetXMLToken(XML_N_FO), XML_NAMESPACE_FO); GetNamespaceMap_().Add(GetXMLToken(XML_NP_SVG), GetXMLToken(XML_N_SVG), XML_NAMESPACE_SVG); } virtual void ExportAutoStyles_() override {} virtual void ExportMasterStyles_() override {} virtual void ExportContent_() override {} void ExportRule(SvxXMLNumRuleExport & rExport, uno::Reference const& xRule) { uno::Reference const xNamed(xRule, uno::UNO_QUERY); OUString const name(xNamed->getName()); bool bEncoded(false); AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, EncodeStyleName(name, &bEncoded) ); if (bEncoded) { AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, name); } SvXMLElementExport aElem( *this, XML_NAMESPACE_TEXT, XML_OUTLINE_STYLE, true, true ); rExport.exportLevelStyles(xRule, true); } void ExportRules( std::set const& rCharStyles, std::vector> const& rRules) { GetDocHandler()->startDocument(); AddAttribute(XML_NAMESPACE_NONE, GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_OFFICE), GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_OFFICE)); AddAttribute(XML_NAMESPACE_NONE, GetNamespaceMap_().GetAttrNameByKey (XML_NAMESPACE_TEXT), GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_TEXT)); AddAttribute(XML_NAMESPACE_NONE, GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_STYLE), GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_STYLE)); AddAttribute(XML_NAMESPACE_NONE, GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_FO), GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_FO)); AddAttribute(XML_NAMESPACE_NONE, GetNamespaceMap_().GetAttrNameByKey(XML_NAMESPACE_SVG), GetNamespaceMap_().GetNameByKey(XML_NAMESPACE_SVG)); { // let's just have a office:styles as a dummy root SvXMLElementExport styles(*this, XML_NAMESPACE_OFFICE, XML_STYLES, true, true); // horrible hack for char styles to get display-name mapping for (const auto& rCharStyle : rCharStyles) { AddAttribute( XML_NAMESPACE_STYLE, XML_FAMILY, XML_TEXT ); bool bEncoded(false); AddAttribute( XML_NAMESPACE_STYLE, XML_NAME, EncodeStyleName(rCharStyle, &bEncoded) ); if (bEncoded) { AddAttribute(XML_NAMESPACE_STYLE, XML_DISPLAY_NAME, rCharStyle); } SvXMLElementExport style(*this, XML_NAMESPACE_STYLE, XML_STYLE, true, true); } SvxXMLNumRuleExport numRuleExport(*this); for (const auto& rRule : rRules) { ExportRule(numRuleExport, rRule); } } GetDocHandler()->endDocument(); } }; /** Dummy import context for style:style element that can just read the attributes needed to map name to display-name. Unfortunately the "real" context for this depends on some other things. The mapping is necessary to import the text:style-name attribute of the text:outline-level-style element. */ class StoredChapterNumberingDummyStyleContext : public SvXMLImportContext { public: StoredChapterNumberingDummyStyleContext( SvXMLImport & rImport, sal_uInt16 const nPrefix, OUString const& rLocalName, uno::Reference const& xAttrList) : SvXMLImportContext(rImport, nPrefix, rLocalName) { OUString name; OUString displayName; sal_uInt16 nFamily(0); for (sal_Int32 i = 0; i < xAttrList->getLength(); ++i) { OUString localName; sal_uInt16 const prefix(rImport.GetNamespaceMap().GetKeyByAttrName( xAttrList->getNameByIndex(i), &localName)); OUString const& rValue = xAttrList->getValueByIndex(i); if (XML_NAMESPACE_STYLE == prefix) { if (IsXMLToken(localName, XML_FAMILY)) { if (IsXMLToken(rValue, XML_TEXT)) { nFamily = XML_STYLE_FAMILY_TEXT_TEXT; } } else if (IsXMLToken(localName, XML_NAME)) { name = rValue; } else if (IsXMLToken(localName, XML_DISPLAY_NAME)) { displayName = rValue; } } } if (nFamily && !name.isEmpty() && !displayName.isEmpty()) { rImport.AddStyleDisplayName(nFamily, name, displayName); } } }; class StoredChapterNumberingImport; class StoredChapterNumberingRootContext : public SvXMLImportContext { private: SwChapterNumRules & m_rNumRules; size_t m_nCounter; std::vector> m_Contexts; public: StoredChapterNumberingRootContext( SwChapterNumRules & rNumRules, SvXMLImport & rImport, sal_uInt16 const nPrefix, OUString const& rLocalName) : SvXMLImportContext(rImport, nPrefix, rLocalName) , m_rNumRules(rNumRules) , m_nCounter(0) { } virtual void EndElement() override { assert(m_Contexts.size() < SwChapterNumRules::nMaxRules); for (auto iter = m_Contexts.begin(); iter != m_Contexts.end(); ++iter) { uno::Reference const xRule( new sw::StoredChapterNumberingRules(m_rNumRules, iter - m_Contexts.begin())); (*iter)->FillUnoNumRule(xRule); // TODO: xmloff's outline-style import seems to ignore this??? uno::Reference const xNamed(xRule, uno::UNO_QUERY); xNamed->setName((*iter)->GetDisplayName()); } } virtual SvXMLImportContextRef CreateChildContext( sal_uInt16 const nPrefix, OUString const& rLocalName, uno::Reference const& xAttrList) override { if (XML_NAMESPACE_TEXT == nPrefix && IsXMLToken(rLocalName, XML_OUTLINE_STYLE)) { ++m_nCounter; if (m_nCounter <= SwChapterNumRules::nMaxRules) { SvxXMLListStyleContext *const pContext( new SvxXMLListStyleContext(GetImport(), nPrefix, rLocalName, xAttrList, true)); m_Contexts.emplace_back(pContext); return pContext; } } else if (XML_NAMESPACE_STYLE == nPrefix && IsXMLToken(rLocalName, XML_STYLE)) { return new StoredChapterNumberingDummyStyleContext( GetImport(), nPrefix, rLocalName, xAttrList); } return SvXMLImportContext::CreateChildContext( nPrefix, rLocalName, xAttrList); } }; class StoredChapterNumberingImport : public SvXMLImport { private: SwChapterNumRules & m_rNumRules; public: StoredChapterNumberingImport( uno::Reference const& xContext, SwChapterNumRules & rNumRules) : SvXMLImport(xContext, "sw::StoredChapterNumberingImport", SvXMLImportFlags::ALL) , m_rNumRules(rNumRules) { } virtual SvXMLImportContext * CreateDocumentContext( sal_uInt16 const nPrefix, OUString const& rLocalName, uno::Reference const& xAttrList) override { if (XML_NAMESPACE_OFFICE == nPrefix && IsXMLToken(rLocalName, XML_STYLES)) { return new StoredChapterNumberingRootContext(m_rNumRules, *this, nPrefix, rLocalName); } return SvXMLImport::CreateDocumentContext(nPrefix, rLocalName, xAttrList); } }; void ExportStoredChapterNumberingRules(SwChapterNumRules & rRules, SvStream & rStream, OUString const& rFileName) { uno::Reference const xContext( ::comphelper::getProcessComponentContext()); uno::Reference const xOutStream( new ::utl::OOutputStreamWrapper(rStream)); uno::Reference const xWriter( xml::sax::Writer::create(xContext)); uno::Reference const xADS(xWriter, uno::UNO_QUERY); xADS->setOutputStream(xOutStream); rtl::Reference exp(new StoredChapterNumberingExport(xContext, rFileName, xWriter)); // if style name contains a space then name != display-name // ... and the import needs to map from name to display-name then! std::set charStyles; std::vector> numRules; for (size_t i = 0; i < SwChapterNumRules::nMaxRules; ++i) { if (SwNumRulesWithName const* pRule = rRules.GetRules(i)) { for (size_t j = 0; j < MAXLEVEL; ++j) { SwNumFormat const* pDummy(nullptr); OUString const* pCharStyleName(nullptr); pRule->GetNumFormat(j, pDummy, pCharStyleName); if (pCharStyleName && !pCharStyleName->isEmpty()) { charStyles.insert(*pCharStyleName); } } numRules.push_back(new StoredChapterNumberingRules(rRules, i)); } } try { exp->ExportRules(charStyles, numRules); } catch (uno::Exception const& e) { SAL_WARN("sw.ui", "ExportStoredChapterNumberingRules: " << e); } } void ImportStoredChapterNumberingRules(SwChapterNumRules & rRules, SvStream & rStream, OUString const& rFileName) { uno::Reference const xContext( ::comphelper::getProcessComponentContext()); uno::Reference const xInStream( new ::utl::OInputStreamWrapper(rStream)); uno::Reference const xParser( xml::sax::Parser::create(xContext)); uno::Reference const xHandler( new StoredChapterNumberingImport(xContext, rRules)); xParser->setDocumentHandler(xHandler); xml::sax::InputSource const source(xInStream, "", "", rFileName); try { xParser->parseStream(source); } catch (uno::Exception const& e) { SAL_WARN("sw.ui", "ImportStoredChapterNumberingRules: " << e); } } } // namespace sw /* vim:set shiftwidth=4 softtabstop=4 expandtab: */