1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include "rtfsprm.hxx"
11 #include <ooxml/resourceids.hxx>
12 #include <ooxml/QNameToString.hxx>
13 #include <rtl/strbuf.hxx>
14 #include "rtfdocumentimpl.hxx"
17 namespace writerfilter
21 RTFSprm::RTFSprm(Id nKeyword
, RTFValue::Pointer_t
& pValue
)
22 : m_nKeyword(nKeyword
)
27 sal_uInt32
RTFSprm::getId() const { return m_nKeyword
; }
29 Value::Pointer_t
RTFSprm::getValue() { return Value::Pointer_t(m_pValue
->Clone()); }
31 writerfilter::Reference
<Properties
>::Pointer_t
RTFSprm::getProps()
33 return m_pValue
->getProperties();
37 std::string
RTFSprm::getName() const { return "RTFSprm"; }
41 std::string
RTFSprm::toString() const
43 OStringBuffer
aBuf("RTFSprm");
45 std::string sResult
= (*QNameToString::Instance())(m_nKeyword
);
48 if (sResult
.length() == 0)
49 aBuf
.append(sal_Int32(m_nKeyword
));
51 aBuf
.append(sResult
.c_str());
53 aBuf
.append(m_pValue
->toString().c_str());
56 return aBuf
.makeStringAndClear().getStr();
62 class RTFSprms_compare
67 RTFSprms_compare(Id kw
)
71 bool operator()(const std::pair
<Id
, RTFValue::Pointer_t
>& raPair
) const
73 return raPair
.first
== keyword
;
78 RTFValue::Pointer_t
RTFSprms::find(Id nKeyword
, bool bFirst
, bool bForWrite
)
81 ensureCopyBeforeWrite();
83 RTFSprms_compare cmp
{ nKeyword
};
87 auto it
= std::find_if(m_pSprms
->begin(), m_pSprms
->end(), cmp
);
88 if (it
!= m_pSprms
->end())
94 auto rit
= std::find_if(m_pSprms
->rbegin(), m_pSprms
->rend(), cmp
);
95 if (rit
!= m_pSprms
->rend())
99 return RTFValue::Pointer_t
{};
102 void RTFSprms::set(Id nKeyword
, const RTFValue::Pointer_t
& pValue
, RTFOverwrite eOverwrite
)
104 ensureCopyBeforeWrite();
108 case RTFOverwrite::YES_PREPEND
:
111 std::remove_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
}),
113 m_pSprms
->emplace(m_pSprms
->cbegin(), nKeyword
, pValue
);
116 case RTFOverwrite::YES
:
119 = std::find_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
});
120 if (it
!= m_pSprms
->end())
123 m_pSprms
->emplace_back(nKeyword
, pValue
);
126 case RTFOverwrite::NO_IGNORE
:
128 if (std::none_of(m_pSprms
->cbegin(), m_pSprms
->cend(), RTFSprms_compare
{ nKeyword
}))
129 m_pSprms
->emplace_back(nKeyword
, pValue
);
132 case RTFOverwrite::NO_APPEND
:
134 m_pSprms
->emplace_back(nKeyword
, pValue
);
140 bool RTFSprms::erase(Id nKeyword
)
142 ensureCopyBeforeWrite();
144 auto i
= std::find_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
});
145 if (i
!= m_pSprms
->end())
153 void RTFSprms::eraseLast(Id nKeyword
)
155 ensureCopyBeforeWrite();
157 auto i
= std::find_if(m_pSprms
->rbegin(), m_pSprms
->rend(), RTFSprms_compare
{ nKeyword
});
158 if (i
!= m_pSprms
->rend())
159 m_pSprms
->erase(std::next(i
).base());
162 static RTFValue::Pointer_t
getDefaultSPRM(Id
const id
, Id nStyleType
)
164 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
)
168 case NS_ooxml::LN_EG_RPrBase_b
:
169 return new RTFValue(0);
175 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_paragraph
)
179 case NS_ooxml::LN_CT_Spacing_before
:
180 case NS_ooxml::LN_CT_Spacing_after
:
181 case NS_ooxml::LN_CT_Ind_left
:
182 case NS_ooxml::LN_CT_Ind_right
:
183 case NS_ooxml::LN_CT_Ind_firstLine
:
184 return new RTFValue(0);
186 case NS_ooxml::LN_CT_Spacing_lineRule
:
187 return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto
);
188 case NS_ooxml::LN_CT_Spacing_line
:
189 // presumably this means 100%, cf. static const int nSingleLineSpacing = 240;
190 return new RTFValue(240);
197 return RTFValue::Pointer_t();
200 /// Is it problematic to deduplicate this SPRM?
201 static bool isSPRMDeduplicateBlacklist(Id nId
)
205 // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
206 // deduplication is explicitly not wanted for these tokens.
207 case NS_ooxml::LN_CT_TabStop_val
:
208 case NS_ooxml::LN_CT_TabStop_leader
:
209 case NS_ooxml::LN_CT_TabStop_pos
:
210 // \htmautsp arrives after the style table, so only the non-style value is
211 // correct, keep these.
212 case NS_ooxml::LN_CT_Spacing_beforeAutospacing
:
213 case NS_ooxml::LN_CT_Spacing_afterAutospacing
:
221 /// Should this SPRM be removed if all its children are removed?
222 static bool isSPRMChildrenExpected(Id nId
)
226 case NS_ooxml::LN_CT_PBdr_top
:
227 case NS_ooxml::LN_CT_PBdr_left
:
228 case NS_ooxml::LN_CT_PBdr_bottom
:
229 case NS_ooxml::LN_CT_PBdr_right
:
230 // Expected children are NS_ooxml::LN_CT_Border_*.
231 case NS_ooxml::LN_CT_PrBase_shd
:
232 // Expected children are NS_ooxml::LN_CT_Shd_*.
233 case NS_ooxml::LN_CT_PPrBase_ind
:
234 // Expected children are NS_ooxml::LN_CT_Ind_*.
242 /// Does the clone / deduplication of a single sprm.
243 static void cloneAndDeduplicateSprm(std::pair
<Id
, RTFValue::Pointer_t
> const& rSprm
, RTFSprms
& ret
,
246 RTFValue::Pointer_t
const pValue(ret
.find(rSprm
.first
));
249 if (rSprm
.second
->equals(*pValue
))
251 if (!isSPRMDeduplicateBlacklist(rSprm
.first
))
252 ret
.erase(rSprm
.first
); // duplicate to style
254 else if (!rSprm
.second
->getSprms().empty() || !rSprm
.second
->getAttributes().empty())
256 RTFSprms
const sprms(
257 pValue
->getSprms().cloneAndDeduplicate(rSprm
.second
->getSprms(), nStyleType
));
258 RTFSprms
const attributes(pValue
->getAttributes().cloneAndDeduplicate(
259 rSprm
.second
->getAttributes(), nStyleType
));
260 // Don't copy the sprm in case we expect it to have children but it doesn't have some.
261 if (!isSPRMChildrenExpected(rSprm
.first
) || !sprms
.empty() || !attributes
.empty())
263 RTFValue::Pointer_t(pValue
->CloneWithSprms(attributes
, sprms
)));
268 // not found - try to override style with default
269 RTFValue::Pointer_t
const pDefault(getDefaultSPRM(rSprm
.first
, nStyleType
));
272 ret
.set(rSprm
.first
, pDefault
);
274 else if (!rSprm
.second
->getSprms().empty() || !rSprm
.second
->getAttributes().empty())
276 RTFSprms
const sprms(
277 RTFSprms().cloneAndDeduplicate(rSprm
.second
->getSprms(), nStyleType
));
278 RTFSprms
const attributes(
279 RTFSprms().cloneAndDeduplicate(rSprm
.second
->getAttributes(), nStyleType
));
280 if (!sprms
.empty() || !attributes
.empty())
282 ret
.set(rSprm
.first
, new RTFValue(attributes
, sprms
));
288 /// Extracts the list level matching nLevel from pAbstract.
289 static RTFValue::Pointer_t
getListLevel(const RTFValue::Pointer_t
& pAbstract
, int nLevel
)
291 for (const auto& rPair
: pAbstract
->getSprms())
293 if (rPair
.first
!= NS_ooxml::LN_CT_AbstractNum_lvl
)
296 RTFValue::Pointer_t pLevel
= rPair
.second
->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl
);
300 if (pLevel
->getInt() != nLevel
)
306 return RTFValue::Pointer_t();
309 void RTFSprms::deduplicateList(const std::map
<int, int>& rInvalidListLevelFirstIndents
)
312 RTFValue::Pointer_t pLevelId
313 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
315 nLevel
= pLevelId
->getInt();
317 auto it
= rInvalidListLevelFirstIndents
.find(nLevel
);
318 if (it
== rInvalidListLevelFirstIndents
.end())
321 int nListValue
= it
->second
;
323 RTFValue::Pointer_t pParagraphValue
324 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, NS_ooxml::LN_CT_Ind_firstLine
);
325 if (!pParagraphValue
)
328 int nParagraphValue
= pParagraphValue
->getInt();
330 if (nParagraphValue
== nListValue
)
331 eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, NS_ooxml::LN_CT_Ind_firstLine
);
334 void RTFSprms::duplicateList(const RTFValue::Pointer_t
& pAbstract
)
337 RTFValue::Pointer_t pLevelId
338 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
340 nLevel
= pLevelId
->getInt();
342 RTFValue::Pointer_t pLevel
= getListLevel(pAbstract
, nLevel
);
346 RTFValue::Pointer_t pLevelInd
= pLevel
->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind
);
350 for (const auto& rListLevelPair
: pLevelInd
->getAttributes())
352 switch (rListLevelPair
.first
)
354 case NS_ooxml::LN_CT_Ind_left
:
355 case NS_ooxml::LN_CT_Ind_right
:
356 case NS_ooxml::LN_CT_Ind_firstLine
:
357 RTFValue::Pointer_t pParagraphValue
358 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, rListLevelPair
.first
);
359 if (!pParagraphValue
)
360 putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, rListLevelPair
.first
,
361 getDefaultSPRM(rListLevelPair
.first
, 0));
368 RTFSprms
RTFSprms::cloneAndDeduplicate(RTFSprms
& rReference
, Id
const nStyleType
,
369 bool const bImplicitPPr
) const
372 ret
.ensureCopyBeforeWrite();
374 // Note: apparently some attributes are set with OVERWRITE_NO_APPEND;
375 // it is probably a bad idea to mess with those in any way here?
376 for (auto& rSprm
: rReference
)
378 // Paragraph formatting sprms are directly contained in case of
379 // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of
380 // styles. So handle those children directly, to avoid unexpected
381 // addition of direct formatting sprms at the paragraph level.
382 if (bImplicitPPr
&& rSprm
.first
== NS_ooxml::LN_CT_Style_pPr
)
384 for (const auto& i
: rSprm
.second
->getSprms())
385 cloneAndDeduplicateSprm(i
, ret
, nStyleType
);
388 cloneAndDeduplicateSprm(rSprm
, ret
, nStyleType
);
393 bool RTFSprms::equals(const RTFValue
& rOther
) const
395 return std::all_of(m_pSprms
->cbegin(), m_pSprms
->cend(),
396 [&](const std::pair
<Id
, RTFValue::Pointer_t
>& raPair
) -> bool {
397 return raPair
.second
->equals(rOther
);
401 void RTFSprms::ensureCopyBeforeWrite()
403 if (m_pSprms
->GetRefCount() > 1)
405 tools::SvRef
<RTFSprmsImpl
> pClone(new RTFSprmsImpl
);
406 for (auto& rSprm
: *m_pSprms
)
408 std::make_pair(rSprm
.first
, RTFValue::Pointer_t(rSprm
.second
->Clone())));
414 : m_pSprms(new RTFSprmsImpl
)
418 RTFSprms::~RTFSprms() = default;
420 void RTFSprms::clear()
422 if (m_pSprms
->GetRefCount() == 1)
423 return m_pSprms
->clear();
425 m_pSprms
= tools::SvRef
<RTFSprmsImpl
>(new RTFSprmsImpl
);
428 } // namespace rtftok
429 } // namespace writerfilter
431 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */