Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / writerfilter / source / rtftok / rtfsprm.cxx
blob76838e3aa62024a2b43caad96e382fbe770a110d
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"
15 #include <algorithm>
17 namespace writerfilter
19 namespace rtftok
21 RTFSprm::RTFSprm(Id nKeyword, RTFValue::Pointer_t& pValue)
22 : m_nKeyword(nKeyword)
23 , m_pValue(pValue)
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();
36 #ifdef DBG_UTIL
37 std::string RTFSprm::getName() const { return "RTFSprm"; }
38 #endif
40 #ifdef DBG_UTIL
41 std::string RTFSprm::toString() const
43 OStringBuffer aBuf("RTFSprm");
45 std::string sResult = (*QNameToString::Instance())(m_nKeyword);
47 aBuf.append(" ('");
48 if (sResult.length() == 0)
49 aBuf.append(sal_Int32(m_nKeyword));
50 else
51 aBuf.append(sResult.c_str());
52 aBuf.append("', '");
53 aBuf.append(m_pValue->toString().c_str());
54 aBuf.append("')");
56 return aBuf.makeStringAndClear().getStr();
58 #endif
60 namespace
62 class RTFSprms_compare
64 Id keyword;
66 public:
67 RTFSprms_compare(Id kw)
68 : keyword{ 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)
80 if (bForWrite)
81 ensureCopyBeforeWrite();
83 RTFSprms_compare cmp{ nKeyword };
85 if (bFirst)
87 auto it = std::find_if(m_pSprms->begin(), m_pSprms->end(), cmp);
88 if (it != m_pSprms->end())
89 return it->second;
91 else
92 // find last
94 auto rit = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), cmp);
95 if (rit != m_pSprms->rend())
96 return rit->second;
99 return RTFValue::Pointer_t{};
102 void RTFSprms::set(Id nKeyword, const RTFValue::Pointer_t& pValue, RTFOverwrite eOverwrite)
104 ensureCopyBeforeWrite();
106 switch (eOverwrite)
108 case RTFOverwrite::YES_PREPEND:
110 m_pSprms->erase(
111 std::remove_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword }),
112 m_pSprms->end());
113 m_pSprms->emplace(m_pSprms->cbegin(), nKeyword, pValue);
114 break;
116 case RTFOverwrite::YES:
118 auto it
119 = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
120 if (it != m_pSprms->end())
121 it->second = pValue;
122 else
123 m_pSprms->emplace_back(nKeyword, pValue);
124 break;
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);
130 break;
132 case RTFOverwrite::NO_APPEND:
134 m_pSprms->emplace_back(nKeyword, pValue);
135 break;
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())
147 m_pSprms->erase(i);
148 return true;
150 return false;
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)
166 switch (id)
168 case NS_ooxml::LN_EG_RPrBase_b:
169 return new RTFValue(0);
170 default:
171 break;
175 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_paragraph)
177 switch (id)
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);
192 default:
193 break;
197 return RTFValue::Pointer_t();
200 /// Is it problematic to deduplicate this SPRM?
201 static bool isSPRMDeduplicateBlacklist(Id nId)
203 switch (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:
214 return true;
216 default:
217 return false;
221 /// Should this SPRM be removed if all its children are removed?
222 static bool isSPRMChildrenExpected(Id nId)
224 switch (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_*.
235 return true;
237 default:
238 return false;
242 /// Does the clone / deduplication of a single sprm.
243 static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t> const& rSprm, RTFSprms& ret,
244 Id nStyleType)
246 RTFValue::Pointer_t const pValue(ret.find(rSprm.first));
247 if (pValue)
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())
262 ret.set(rSprm.first,
263 RTFValue::Pointer_t(pValue->CloneWithSprms(attributes, sprms)));
266 else
268 // not found - try to override style with default
269 RTFValue::Pointer_t const pDefault(getDefaultSPRM(rSprm.first, nStyleType));
270 if (pDefault)
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)
294 continue;
296 RTFValue::Pointer_t pLevel = rPair.second->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl);
297 if (!pLevel)
298 continue;
300 if (pLevel->getInt() != nLevel)
301 continue;
303 return rPair.second;
306 return RTFValue::Pointer_t();
309 void RTFSprms::deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents)
311 int nLevel = 0;
312 RTFValue::Pointer_t pLevelId
313 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
314 if (pLevelId)
315 nLevel = pLevelId->getInt();
317 auto it = rInvalidListLevelFirstIndents.find(nLevel);
318 if (it == rInvalidListLevelFirstIndents.end())
319 return;
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)
326 return;
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)
336 int nLevel = 0;
337 RTFValue::Pointer_t pLevelId
338 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
339 if (pLevelId)
340 nLevel = pLevelId->getInt();
342 RTFValue::Pointer_t pLevel = getListLevel(pAbstract, nLevel);
343 if (!pLevel)
344 return;
346 RTFValue::Pointer_t pLevelInd = pLevel->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind);
347 if (!pLevelInd)
348 return;
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));
363 break;
368 RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType,
369 bool const bImplicitPPr) const
371 RTFSprms ret(*this);
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);
387 else
388 cloneAndDeduplicateSprm(rSprm, ret, nStyleType);
390 return ret;
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)
407 pClone->push_back(
408 std::make_pair(rSprm.first, RTFValue::Pointer_t(rSprm.second->Clone())));
409 m_pSprms = pClone;
413 RTFSprms::RTFSprms()
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: */