summaryrefslogtreecommitdiffstats
path: root/writerfilter/source/rtftok/rtfsprm.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'writerfilter/source/rtftok/rtfsprm.cxx')
-rw-r--r--writerfilter/source/rtftok/rtfsprm.cxx476
1 files changed, 476 insertions, 0 deletions
diff --git a/writerfilter/source/rtftok/rtfsprm.cxx b/writerfilter/source/rtftok/rtfsprm.cxx
new file mode 100644
index 000000000..90bc97001
--- /dev/null
+++ b/writerfilter/source/rtftok/rtfsprm.cxx
@@ -0,0 +1,476 @@
+/* -*- 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 "rtfsprm.hxx"
+#include <ooxml/resourceids.hxx>
+#include <ooxml/QNameToString.hxx>
+#include <rtl/strbuf.hxx>
+#include "rtfdocumentimpl.hxx"
+#include <algorithm>
+
+namespace writerfilter::rtftok
+{
+RTFSprm::RTFSprm(Id nKeyword, RTFValue::Pointer_t& pValue)
+ : m_nKeyword(nKeyword)
+ , m_pValue(pValue)
+{
+}
+
+sal_uInt32 RTFSprm::getId() const { return m_nKeyword; }
+
+Value::Pointer_t RTFSprm::getValue() { return Value::Pointer_t(m_pValue->Clone()); }
+
+writerfilter::Reference<Properties>::Pointer_t RTFSprm::getProps()
+{
+ return m_pValue->getProperties();
+}
+
+#ifdef DBG_UTIL
+std::string RTFSprm::getName() const { return "RTFSprm"; }
+#endif
+
+#ifdef DBG_UTIL
+std::string RTFSprm::toString() const
+{
+ OStringBuffer aBuf("RTFSprm");
+
+ std::string sResult = QNameToString(m_nKeyword);
+
+ aBuf.append(" ('");
+ if (sResult.length() == 0)
+ aBuf.append(sal_Int32(m_nKeyword));
+ else
+ aBuf.append(sResult.c_str());
+ aBuf.append("', '");
+ aBuf.append(m_pValue->toString().c_str());
+ aBuf.append("')");
+
+ return aBuf.makeStringAndClear().getStr();
+}
+#endif
+
+namespace
+{
+class RTFSprms_compare
+{
+ Id keyword;
+
+public:
+ RTFSprms_compare(Id kw)
+ : keyword{ kw }
+ {
+ }
+ bool operator()(const std::pair<Id, RTFValue::Pointer_t>& raPair) const
+ {
+ return raPair.first == keyword;
+ }
+};
+}
+
+RTFValue::Pointer_t RTFSprms::find(Id nKeyword, bool bFirst, bool bForWrite)
+{
+ if (bForWrite)
+ ensureCopyBeforeWrite();
+
+ RTFSprms_compare cmp{ nKeyword };
+
+ if (bFirst)
+ {
+ auto it = std::find_if(m_pSprms->begin(), m_pSprms->end(), cmp);
+ if (it != m_pSprms->end())
+ return it->second;
+ }
+ else
+ // find last
+ {
+ auto rit = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), cmp);
+ if (rit != m_pSprms->rend())
+ return rit->second;
+ }
+
+ return RTFValue::Pointer_t{};
+}
+
+void RTFSprms::set(Id nKeyword, const RTFValue::Pointer_t& pValue, RTFOverwrite eOverwrite)
+{
+ ensureCopyBeforeWrite();
+
+ switch (eOverwrite)
+ {
+ case RTFOverwrite::YES_PREPEND:
+ {
+ m_pSprms->erase(
+ std::remove_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword }),
+ m_pSprms->end());
+ m_pSprms->emplace(m_pSprms->cbegin(), nKeyword, pValue);
+ break;
+ }
+ case RTFOverwrite::YES:
+ {
+ auto it
+ = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
+ if (it != m_pSprms->end())
+ it->second = pValue;
+ else
+ m_pSprms->emplace_back(nKeyword, pValue);
+ break;
+ }
+ case RTFOverwrite::NO_IGNORE:
+ {
+ if (std::none_of(m_pSprms->cbegin(), m_pSprms->cend(), RTFSprms_compare{ nKeyword }))
+ m_pSprms->emplace_back(nKeyword, pValue);
+ break;
+ }
+ case RTFOverwrite::NO_APPEND:
+ {
+ m_pSprms->emplace_back(nKeyword, pValue);
+ break;
+ }
+ }
+}
+
+bool RTFSprms::erase(Id nKeyword)
+{
+ ensureCopyBeforeWrite();
+
+ auto i = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
+ if (i != m_pSprms->end())
+ {
+ m_pSprms->erase(i);
+ return true;
+ }
+ return false;
+}
+
+void RTFSprms::eraseLast(Id nKeyword)
+{
+ ensureCopyBeforeWrite();
+
+ auto i = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), RTFSprms_compare{ nKeyword });
+ if (i != m_pSprms->rend())
+ m_pSprms->erase(std::next(i).base());
+}
+
+static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType)
+{
+ if (nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
+ {
+ switch (id)
+ {
+ case NS_ooxml::LN_EG_RPrBase_szCs:
+ case NS_ooxml::LN_EG_RPrBase_sz:
+ return new RTFValue(24);
+ case NS_ooxml::LN_CT_Color_val:
+ return new RTFValue(0);
+ case NS_ooxml::LN_EG_RPrBase_b:
+ case NS_ooxml::LN_EG_RPrBase_i:
+ return new RTFValue(0);
+ case NS_ooxml::LN_CT_Underline_val:
+ return new RTFValue(NS_ooxml::LN_Value_ST_Underline_none);
+ case NS_ooxml::LN_CT_Fonts_ascii:
+ case NS_ooxml::LN_CT_Fonts_eastAsia:
+ case NS_ooxml::LN_CT_Fonts_cs:
+ return new RTFValue("Times New Roman");
+ default:
+ break;
+ }
+ }
+
+ if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_paragraph)
+ {
+ switch (id)
+ {
+ case NS_ooxml::LN_CT_Spacing_before:
+ case NS_ooxml::LN_CT_Spacing_after:
+ case NS_ooxml::LN_CT_Ind_left:
+ case NS_ooxml::LN_CT_Ind_right:
+ case NS_ooxml::LN_CT_Ind_firstLine:
+ return new RTFValue(0);
+
+ case NS_ooxml::LN_CT_Spacing_lineRule:
+ return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto);
+ case NS_ooxml::LN_CT_Spacing_line:
+ // presumably this means 100%, cf. static const int nSingleLineSpacing = 240;
+ return new RTFValue(240);
+
+ case NS_ooxml::LN_CT_PrBase_pBdr:
+ { // tdf#150382 default all paragraph borders to none
+ RTFSprms attributes;
+ RTFSprms sprms;
+ for (int i = 0; i < 4; ++i)
+ {
+ auto const nBorder = getParagraphBorder(i);
+ RTFSprms aAttributes;
+ RTFSprms aSprms;
+ aAttributes.set(NS_ooxml::LN_CT_Border_val,
+ new RTFValue(NS_ooxml::LN_Value_ST_Border_none));
+ sprms.set(nBorder, new RTFValue(aAttributes, aSprms));
+ }
+ return new RTFValue(attributes, sprms);
+ }
+
+ default:
+ break;
+ }
+ }
+
+ return RTFValue::Pointer_t();
+}
+
+/// Is it problematic to deduplicate this SPRM?
+static bool isSPRMDeduplicateDenylist(Id nId, RTFSprms* pDirect)
+{
+ switch (nId)
+ {
+ // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
+ // deduplication is explicitly not wanted for these tokens.
+ case NS_ooxml::LN_CT_TabStop_val:
+ case NS_ooxml::LN_CT_TabStop_leader:
+ case NS_ooxml::LN_CT_TabStop_pos:
+ // \htmautsp arrives after the style table, so only the non-style value is
+ // correct, keep these.
+ case NS_ooxml::LN_CT_Spacing_beforeAutospacing:
+ case NS_ooxml::LN_CT_Spacing_afterAutospacing:
+ // \chbrdr requires *all* of the border settings to be present,
+ // otherwise a default (NONE) border is created from the removed
+ // attributes which then overrides the style-defined border.
+ // See BorderHandler.cxx and NS_ooxml::LN_EG_RPrBase_bdr in DomainMapper.
+ // This also is needed for NS_ooxml::LN_CT_PBdr_top etc.
+ case NS_ooxml::LN_CT_Border_sz:
+ case NS_ooxml::LN_CT_Border_val:
+ case NS_ooxml::LN_CT_Border_color:
+ case NS_ooxml::LN_CT_Border_space:
+ case NS_ooxml::LN_CT_Border_shadow:
+ case NS_ooxml::LN_CT_Border_frame:
+ case NS_ooxml::LN_CT_Border_themeTint:
+ case NS_ooxml::LN_CT_Border_themeColor:
+ return true;
+ // Removing \fi and \li if the style has the same value would mean taking these values from
+ // \ls, while deduplication would be done to take the values from the style.
+ case NS_ooxml::LN_CT_Ind_firstLine:
+ case NS_ooxml::LN_CT_Ind_left:
+ return pDirect && pDirect->find(NS_ooxml::LN_CT_PPrBase_numPr);
+
+ default:
+ return false;
+ }
+}
+
+/// Should this SPRM be removed if all its children are removed?
+static bool isSPRMChildrenExpected(Id nId)
+{
+ switch (nId)
+ {
+ case NS_ooxml::LN_CT_PBdr_top:
+ case NS_ooxml::LN_CT_PBdr_left:
+ case NS_ooxml::LN_CT_PBdr_bottom:
+ case NS_ooxml::LN_CT_PBdr_right:
+ // Expected children are NS_ooxml::LN_CT_Border_*.
+ case NS_ooxml::LN_CT_PrBase_shd:
+ // Expected children are NS_ooxml::LN_CT_Shd_*.
+ case NS_ooxml::LN_CT_PPrBase_ind:
+ // Expected children are NS_ooxml::LN_CT_Ind_*.
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/// Does the clone / deduplication of a single sprm.
+static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t> const& rSprm, RTFSprms& ret,
+ Id nStyleType, RTFSprms* pDirect = nullptr)
+{
+ RTFValue::Pointer_t const pValue(ret.find(rSprm.first));
+ if (pValue)
+ {
+ if (rSprm.second->equals(*pValue))
+ {
+ if (!isSPRMDeduplicateDenylist(rSprm.first, pDirect))
+ {
+ ret.erase(rSprm.first); // duplicate to style
+ }
+ }
+ else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
+ {
+ RTFSprms const sprms(pValue->getSprms().cloneAndDeduplicate(
+ rSprm.second->getSprms(), nStyleType, /*bImplicitPPr =*/false, pDirect));
+ RTFSprms const attributes(pValue->getAttributes().cloneAndDeduplicate(
+ rSprm.second->getAttributes(), nStyleType, /*bImplicitPPr =*/false, pDirect));
+ // Don't copy the sprm in case we expect it to have children but it doesn't have some.
+ if (!isSPRMChildrenExpected(rSprm.first) || !sprms.empty() || !attributes.empty())
+ ret.set(rSprm.first,
+ RTFValue::Pointer_t(pValue->CloneWithSprms(attributes, sprms)));
+ }
+ }
+ else
+ {
+ // not found - try to override style with default
+ RTFValue::Pointer_t const pDefault(getDefaultSPRM(rSprm.first, nStyleType));
+ if (pDefault)
+ {
+ ret.set(rSprm.first, pDefault);
+ }
+ else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
+ {
+ RTFSprms const sprms(
+ RTFSprms().cloneAndDeduplicate(rSprm.second->getSprms(), nStyleType));
+ RTFSprms const attributes(
+ RTFSprms().cloneAndDeduplicate(rSprm.second->getAttributes(), nStyleType));
+ if (!sprms.empty() || !attributes.empty())
+ {
+ ret.set(rSprm.first, new RTFValue(attributes, sprms));
+ }
+ }
+ }
+}
+
+/// Extracts the list level matching nLevel from pAbstract.
+static RTFValue::Pointer_t getListLevel(const RTFValue::Pointer_t& pAbstract, int nLevel)
+{
+ for (const auto& rPair : pAbstract->getSprms())
+ {
+ if (rPair.first != NS_ooxml::LN_CT_AbstractNum_lvl)
+ continue;
+
+ RTFValue::Pointer_t pLevel = rPair.second->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl);
+ if (!pLevel)
+ continue;
+
+ if (pLevel->getInt() != nLevel)
+ continue;
+
+ return rPair.second;
+ }
+
+ return RTFValue::Pointer_t();
+}
+
+void RTFSprms::deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents)
+{
+ int nLevel = 0;
+ RTFValue::Pointer_t pLevelId
+ = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
+ if (pLevelId)
+ nLevel = pLevelId->getInt();
+
+ auto it = rInvalidListLevelFirstIndents.find(nLevel);
+ if (it == rInvalidListLevelFirstIndents.end())
+ return;
+
+ int nListValue = it->second;
+
+ RTFValue::Pointer_t pParagraphValue
+ = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
+ if (!pParagraphValue)
+ return;
+
+ int nParagraphValue = pParagraphValue->getInt();
+
+ if (nParagraphValue == nListValue)
+ eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
+}
+
+void RTFSprms::duplicateList(const RTFValue::Pointer_t& pAbstract)
+{
+ int nLevel = 0;
+ RTFValue::Pointer_t pLevelId
+ = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
+ if (pLevelId)
+ nLevel = pLevelId->getInt();
+
+ RTFValue::Pointer_t pLevel = getListLevel(pAbstract, nLevel);
+ if (!pLevel)
+ return;
+
+ RTFValue::Pointer_t pLevelInd = pLevel->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind);
+ if (!pLevelInd)
+ return;
+
+ for (const auto& rListLevelPair : pLevelInd->getAttributes())
+ {
+ switch (rListLevelPair.first)
+ {
+ case NS_ooxml::LN_CT_Ind_left:
+ case NS_ooxml::LN_CT_Ind_right:
+ case NS_ooxml::LN_CT_Ind_firstLine:
+ RTFValue::Pointer_t pParagraphValue
+ = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first);
+ if (!pParagraphValue)
+ putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first,
+ getDefaultSPRM(rListLevelPair.first, 0));
+
+ break;
+ }
+ }
+}
+
+RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType,
+ bool const bImplicitPPr, RTFSprms* pDirect) const
+{
+ RTFSprms ret(*this);
+ ret.ensureCopyBeforeWrite();
+
+ // Note: apparently some attributes are set with OVERWRITE_NO_APPEND;
+ // it is probably a bad idea to mess with those in any way here?
+ for (auto& rSprm : rReference)
+ {
+ // Paragraph formatting sprms are directly contained in case of
+ // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of
+ // styles. So handle those children directly, to avoid unexpected
+ // addition of direct formatting sprms at the paragraph level.
+ if (bImplicitPPr && rSprm.first == NS_ooxml::LN_CT_Style_pPr)
+ {
+ for (const auto& i : rSprm.second->getSprms())
+ cloneAndDeduplicateSprm(i, ret, nStyleType, pDirect);
+ }
+ else
+ cloneAndDeduplicateSprm(rSprm, ret, nStyleType, pDirect);
+ }
+ return ret;
+}
+
+bool RTFSprms::equals(const RTFValue& rOther) const
+{
+ return std::all_of(m_pSprms->cbegin(), m_pSprms->cend(),
+ [&](const std::pair<Id, RTFValue::Pointer_t>& raPair) -> bool {
+ return raPair.second->equals(rOther);
+ });
+}
+
+void RTFSprms::ensureCopyBeforeWrite()
+{
+ if (m_pSprms->GetRefCount() > 1)
+ {
+ tools::SvRef<RTFSprmsImpl> pClone(new RTFSprmsImpl);
+ for (auto& rSprm : *m_pSprms)
+ pClone->push_back(
+ std::make_pair(rSprm.first, RTFValue::Pointer_t(rSprm.second->Clone())));
+ m_pSprms = pClone;
+ }
+}
+
+RTFSprms::RTFSprms()
+ : m_pSprms(new RTFSprmsImpl)
+{
+}
+
+RTFSprms::~RTFSprms() = default;
+
+void RTFSprms::clear()
+{
+ if (m_pSprms->GetRefCount() == 1)
+ return m_pSprms->clear();
+
+ m_pSprms = tools::SvRef<RTFSprmsImpl>(new RTFSprmsImpl);
+}
+
+} // namespace writerfilter::rtftok
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */