lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / writerfilter / source / rtftok / rtfsprm.cxx
blob9eab27298050b691422b18aa47c59a84121300d9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include "rtfsprm.hxx"
11 #include <ooxml/resourceids.hxx>
12 #include <ooxml/QNameToString.hxx>
13 #include <rtl/strbuf.hxx>
14 #include "rtfdocumentimpl.hxx"
16 namespace writerfilter
18 namespace rtftok
20 RTFSprm::RTFSprm(Id nKeyword, RTFValue::Pointer_t& pValue)
21 : m_nKeyword(nKeyword)
22 , m_pValue(pValue)
26 sal_uInt32 RTFSprm::getId() const { return m_nKeyword; }
28 Value::Pointer_t RTFSprm::getValue() { return Value::Pointer_t(m_pValue->Clone()); }
30 writerfilter::Reference<Properties>::Pointer_t RTFSprm::getProps()
32 return m_pValue->getProperties();
35 #ifdef DEBUG_WRITERFILTER
36 std::string RTFSprm::getName() const { return "RTFSprm"; }
37 #endif
39 #ifdef DEBUG_WRITERFILTER
40 std::string RTFSprm::toString() const
42 OStringBuffer aBuf("RTFSprm");
44 std::string sResult;
46 sResult = (*QNameToString::Instance())(m_nKeyword);
48 aBuf.append(" ('");
49 if (sResult.length() == 0)
50 aBuf.append(sal_Int32(m_nKeyword));
51 else
52 aBuf.append(sResult.c_str());
53 aBuf.append("', '");
54 aBuf.append(m_pValue->toString().c_str());
55 aBuf.append("')");
57 return aBuf.makeStringAndClear().getStr();
59 #endif
61 RTFValue::Pointer_t RTFSprms::find(Id nKeyword, bool bFirst, bool bForWrite)
63 RTFValue::Pointer_t pValue;
65 if (bForWrite)
66 ensureCopyBeforeWrite();
68 for (auto& rSprm : *m_pSprms)
69 if (rSprm.first == nKeyword)
71 if (bFirst)
72 return rSprm.second;
74 pValue = rSprm.second;
76 return pValue;
79 void RTFSprms::set(Id nKeyword, const RTFValue::Pointer_t& pValue, RTFOverwrite eOverwrite)
81 ensureCopyBeforeWrite();
83 if (eOverwrite == RTFOverwrite::YES_PREPEND)
85 auto it = std::remove_if(
86 m_pSprms->begin(), m_pSprms->end(),
87 [nKeyword](const RTFSprms::Entry_t& rSprm) { return rSprm.first == nKeyword; });
88 m_pSprms->erase(it, m_pSprms->end());
89 m_pSprms->insert(m_pSprms->begin(), std::make_pair(nKeyword, pValue));
90 return;
93 bool bFound = false;
94 if (eOverwrite == RTFOverwrite::YES || eOverwrite == RTFOverwrite::NO_IGNORE)
96 for (auto& rSprm : *m_pSprms)
97 if (rSprm.first == nKeyword)
99 if (eOverwrite == RTFOverwrite::YES)
101 rSprm.second = pValue;
102 return;
105 bFound = true;
106 break;
109 if (eOverwrite == RTFOverwrite::NO_APPEND || !bFound)
110 m_pSprms->push_back(std::make_pair(nKeyword, pValue));
113 bool RTFSprms::erase(Id nKeyword)
115 ensureCopyBeforeWrite();
117 auto i = std::find_if(
118 m_pSprms->begin(), m_pSprms->end(),
119 [&nKeyword](RTFSprmsImpl::value_type& rEntry) { return rEntry.first == nKeyword; });
120 if (i != m_pSprms->end())
122 m_pSprms->erase(i);
123 return true;
125 return false;
128 void RTFSprms::eraseLast(Id nKeyword)
130 ensureCopyBeforeWrite();
132 auto i = std::find_if(
133 m_pSprms->rbegin(), m_pSprms->rend(),
134 [&nKeyword](RTFSprmsImpl::value_type& rEntry) { return rEntry.first == nKeyword; });
135 if (i != m_pSprms->rend())
136 m_pSprms->erase(std::next(i).base());
139 static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType)
141 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
143 switch (id)
145 case NS_ooxml::LN_EG_RPrBase_b:
146 return new RTFValue(0);
147 default:
148 break;
152 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_paragraph)
154 switch (id)
156 case NS_ooxml::LN_CT_Spacing_before:
157 case NS_ooxml::LN_CT_Spacing_after:
158 case NS_ooxml::LN_CT_Ind_left:
159 case NS_ooxml::LN_CT_Ind_right:
160 case NS_ooxml::LN_CT_Ind_firstLine:
161 return new RTFValue(0);
163 case NS_ooxml::LN_CT_Spacing_lineRule:
164 return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto);
165 case NS_ooxml::LN_CT_Spacing_line:
166 // presumably this means 100%, cf. static const int nSingleLineSpacing = 240;
167 return new RTFValue(240);
169 default:
170 break;
174 return RTFValue::Pointer_t();
177 /// Is it problematic to deduplicate this SPRM?
178 static bool isSPRMDeduplicateBlacklist(Id nId)
180 switch (nId)
182 // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
183 // deduplication is explicitly not wanted for these tokens.
184 case NS_ooxml::LN_CT_TabStop_val:
185 case NS_ooxml::LN_CT_TabStop_leader:
186 case NS_ooxml::LN_CT_TabStop_pos:
187 // \htmautsp arrives after the style table, so only the non-style value is
188 // correct, keep these.
189 case NS_ooxml::LN_CT_Spacing_beforeAutospacing:
190 case NS_ooxml::LN_CT_Spacing_afterAutospacing:
191 return true;
193 default:
194 return false;
198 /// Should this SPRM be removed if all its children is removed?
199 static bool isSPRMChildrenExpected(Id nId)
201 switch (nId)
203 case NS_ooxml::LN_CT_PBdr_top:
204 case NS_ooxml::LN_CT_PBdr_left:
205 case NS_ooxml::LN_CT_PBdr_bottom:
206 case NS_ooxml::LN_CT_PBdr_right:
207 // Expected children are NS_ooxml::LN_CT_Border_*.
208 SAL_FALLTHROUGH;
209 case NS_ooxml::LN_CT_PrBase_shd:
210 // Expected children are NS_ooxml::LN_CT_Shd_*.
211 SAL_FALLTHROUGH;
212 case NS_ooxml::LN_CT_PPrBase_ind:
213 // Expected children are NS_ooxml::LN_CT_Ind_*.
214 return true;
216 default:
217 return false;
221 /// Does the clone / deduplication of a single sprm.
222 static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t> const& rSprm, RTFSprms& ret,
223 Id nStyleType)
225 RTFValue::Pointer_t const pValue(ret.find(rSprm.first));
226 if (pValue)
228 if (rSprm.second->equals(*pValue))
230 if (!isSPRMDeduplicateBlacklist(rSprm.first))
231 ret.erase(rSprm.first); // duplicate to style
233 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
235 RTFSprms const sprms(
236 pValue->getSprms().cloneAndDeduplicate(rSprm.second->getSprms(), nStyleType));
237 RTFSprms const attributes(pValue->getAttributes().cloneAndDeduplicate(
238 rSprm.second->getAttributes(), nStyleType));
239 // Don't copy the sprm in case we expect it to have children but it doesn't have some.
240 if (!isSPRMChildrenExpected(rSprm.first) || !sprms.empty() || !attributes.empty())
241 ret.set(rSprm.first,
242 RTFValue::Pointer_t(pValue->CloneWithSprms(attributes, sprms)));
245 else
247 // not found - try to override style with default
248 RTFValue::Pointer_t const pDefault(getDefaultSPRM(rSprm.first, nStyleType));
249 if (pDefault)
251 ret.set(rSprm.first, pDefault);
253 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
255 RTFSprms const sprms(
256 RTFSprms().cloneAndDeduplicate(rSprm.second->getSprms(), nStyleType));
257 RTFSprms const attributes(
258 RTFSprms().cloneAndDeduplicate(rSprm.second->getAttributes(), nStyleType));
259 if (!sprms.empty() || !attributes.empty())
261 ret.set(rSprm.first, new RTFValue(attributes, sprms));
267 /// Extracts the list level matching nLevel from pAbstract.
268 static RTFValue::Pointer_t getListLevel(const RTFValue::Pointer_t& pAbstract, int nLevel)
270 for (const auto& rPair : pAbstract->getSprms())
272 if (rPair.first != NS_ooxml::LN_CT_AbstractNum_lvl)
273 continue;
275 RTFValue::Pointer_t pLevel = rPair.second->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl);
276 if (!pLevel)
277 continue;
279 if (pLevel->getInt() != nLevel)
280 continue;
282 return rPair.second;
285 return RTFValue::Pointer_t();
288 void RTFSprms::deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents)
290 int nLevel = 0;
291 RTFValue::Pointer_t pLevelId
292 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
293 if (pLevelId)
294 nLevel = pLevelId->getInt();
296 auto it = rInvalidListLevelFirstIndents.find(nLevel);
297 if (it == rInvalidListLevelFirstIndents.end())
298 return;
300 int nListValue = it->second;
302 RTFValue::Pointer_t pParagraphValue
303 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
304 if (!pParagraphValue)
305 return;
307 int nParagraphValue = pParagraphValue->getInt();
309 if (nParagraphValue == nListValue)
310 eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
313 void RTFSprms::duplicateList(const RTFValue::Pointer_t& pAbstract)
315 int nLevel = 0;
316 RTFValue::Pointer_t pLevelId
317 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
318 if (pLevelId)
319 nLevel = pLevelId->getInt();
321 RTFValue::Pointer_t pLevel = getListLevel(pAbstract, nLevel);
322 if (!pLevel)
323 return;
325 RTFValue::Pointer_t pLevelInd = pLevel->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind);
326 if (!pLevelInd)
327 return;
329 for (const auto& rListLevelPair : pLevelInd->getAttributes())
331 switch (rListLevelPair.first)
333 case NS_ooxml::LN_CT_Ind_left:
334 case NS_ooxml::LN_CT_Ind_right:
335 case NS_ooxml::LN_CT_Ind_firstLine:
336 RTFValue::Pointer_t pParagraphValue
337 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first);
338 if (!pParagraphValue)
339 putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first,
340 getDefaultSPRM(rListLevelPair.first, 0));
342 break;
347 RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType,
348 bool const bImplicitPPr) const
350 RTFSprms ret(*this);
351 ret.ensureCopyBeforeWrite();
353 // Note: apparently some attributes are set with OVERWRITE_NO_APPEND;
354 // it is probably a bad idea to mess with those in any way here?
355 for (auto& rSprm : rReference)
357 // Paragraph formatting sprms are directly contained in case of
358 // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of
359 // styles. So handle those children directly, to avoid unexpected
360 // addition of direct formatting sprms at the paragraph level.
361 if (bImplicitPPr && rSprm.first == NS_ooxml::LN_CT_Style_pPr)
363 for (auto& i : rSprm.second->getSprms())
364 cloneAndDeduplicateSprm(i, ret, nStyleType);
366 else
367 cloneAndDeduplicateSprm(rSprm, ret, nStyleType);
369 return ret;
372 bool RTFSprms::equals(RTFValue& rOther)
374 for (auto& rSprm : *m_pSprms)
375 if (!rSprm.second->equals(rOther))
376 return false;
377 return true;
380 void RTFSprms::ensureCopyBeforeWrite()
382 if (m_pSprms->GetRefCount() > 1)
384 tools::SvRef<RTFSprmsImpl> pClone(new RTFSprmsImpl);
385 for (auto& rSprm : *m_pSprms)
386 pClone->push_back(
387 std::make_pair(rSprm.first, RTFValue::Pointer_t(rSprm.second->Clone())));
388 m_pSprms = pClone;
392 RTFSprms::RTFSprms()
393 : m_pSprms(new RTFSprmsImpl)
397 RTFSprms::~RTFSprms() = default;
399 void RTFSprms::clear()
401 if (m_pSprms->GetRefCount() == 1)
402 return m_pSprms->clear();
404 m_pSprms = tools::SvRef<RTFSprmsImpl>(new RTFSprmsImpl);
407 } // namespace rtftok
408 } // namespace writerfilter
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */