/* -*- 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 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 an 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, props); SwNumRulesWithName *const pRules(GetOrCreateRules()); pRules->SetNumFormat(nIndex, aNumberFormat, charStyleName); } }; namespace { 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 an 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, uno::Reference const& xAttrList) : SvXMLImportContext(rImport) { OUString name; OUString displayName; XmlStyleFamily nFamily(XmlStyleFamily::DATA_STYLE); for (auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList )) if (aIter.getToken() == (XML_NAMESPACE_STYLE | XML_FAMILY)) { if (IsXMLToken(aIter, XML_TEXT)) nFamily = XmlStyleFamily::TEXT_TEXT; else if (IsXMLToken(aIter, XML_NAME)) name = aIter.toString(); else if (IsXMLToken(aIter, XML_DISPLAY_NAME)) displayName = aIter.toString(); else SAL_WARN("xmloff", "unknown value for style:family=" << aIter.toString()); } else SAL_WARN("xmloff", "unknown attribute " << SvXMLImport::getPrefixAndNameFromToken(aIter.getToken()) << "=" << aIter.toString()); if (nFamily != XmlStyleFamily::DATA_STYLE && !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) : SvXMLImportContext(rImport) , m_rNumRules(rNumRules) , m_nCounter(0) { } virtual void SAL_CALL startFastElement( sal_Int32 /*nElement*/, const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ ) override {} virtual void SAL_CALL endFastElement(sal_Int32 /*Element*/) 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 nPrefix, const OUString& rLocalName, const css::uno::Reference< css::xml::sax::XAttributeList >& 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; } } return nullptr; } virtual css::uno::Reference SAL_CALL createFastChildContext( sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & xAttrList ) override { if (Element == XML_ELEMENT(TEXT, XML_OUTLINE_STYLE)) { // handled in CreateChildContext } else if (Element == XML_ELEMENT(STYLE, XML_STYLE)) { return new StoredChapterNumberingDummyStyleContext(GetImport(), xAttrList); } return nullptr; } }; 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 *CreateFastContext( sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > & /*xAttrList*/ ) override { if (Element == XML_ELEMENT(OFFICE, XML_STYLES)) return new StoredChapterNumberingRootContext(m_rNumRules, *this); return nullptr; } }; } 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)); xWriter->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&) { TOOLS_WARN_EXCEPTION("sw.ui", "ExportStoredChapterNumberingRules"); } } void ImportStoredChapterNumberingRules(SwChapterNumRules & rRules, SvStream & rStream, OUString const& rFileName) { uno::Reference const xContext( ::comphelper::getProcessComponentContext()); uno::Reference const xInStream( new ::utl::OInputStreamWrapper(rStream)); rtl::Reference const xImport(new StoredChapterNumberingImport(xContext, rRules)); xml::sax::InputSource const source(xInStream, "", "", rFileName); try { xImport->parseStream(source); } catch (uno::Exception const&) { TOOLS_WARN_EXCEPTION("sw.ui", "ImportStoredChapterNumberingRules"); } } } // namespace sw /* vim:set shiftwidth=4 softtabstop=4 expandtab: */