android: Update app-specific/MIME type icons
[LibreOffice.git] / writerfilter / source / rtftok / rtfsprm.cxx
blob3efb60eb29d8cb7ec16f762eec24d1df31c2d183
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::rtftok
19 RTFSprm::RTFSprm(Id nKeyword, RTFValue::Pointer_t& pValue)
20 : m_nKeyword(nKeyword)
21 , m_pValue(pValue)
25 sal_uInt32 RTFSprm::getId() const { return m_nKeyword; }
27 Value::Pointer_t RTFSprm::getValue() { return Value::Pointer_t(m_pValue->Clone()); }
29 writerfilter::Reference<Properties>::Pointer_t RTFSprm::getProps()
31 return m_pValue->getProperties();
34 #ifdef DBG_UTIL
35 std::string RTFSprm::getName() const { return "RTFSprm"; }
36 #endif
38 #ifdef DBG_UTIL
39 std::string RTFSprm::toString() const
41 OStringBuffer aBuf("RTFSprm");
43 std::string sResult = QNameToString(m_nKeyword);
45 aBuf.append(" ('");
46 if (sResult.length() == 0)
47 aBuf.append(sal_Int32(m_nKeyword));
48 else
49 aBuf.append(sResult.c_str());
50 aBuf.append("', '" + m_pValue->toString() + "')");
52 return std::string(aBuf);
54 #endif
56 namespace
58 class RTFSprms_compare
60 Id m_keyword;
62 public:
63 RTFSprms_compare(Id keyword)
64 : m_keyword{ keyword }
67 bool operator()(const std::pair<Id, RTFValue::Pointer_t>& raPair) const
69 return raPair.first == m_keyword;
74 RTFValue::Pointer_t RTFSprms::find(Id nKeyword, bool bFirst, bool bForWrite)
76 if (bForWrite)
77 ensureCopyBeforeWrite();
79 RTFSprms_compare cmp{ nKeyword };
81 if (bFirst)
83 auto it = std::find_if(m_pSprms->begin(), m_pSprms->end(), cmp);
84 if (it != m_pSprms->end())
85 return it->second;
87 else
88 // find last
90 auto rit = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), cmp);
91 if (rit != m_pSprms->rend())
92 return rit->second;
95 return RTFValue::Pointer_t{};
98 void RTFSprms::set(Id nKeyword, const RTFValue::Pointer_t& pValue, RTFOverwrite eOverwrite)
100 ensureCopyBeforeWrite();
102 switch (eOverwrite)
104 case RTFOverwrite::YES_PREPEND:
106 m_pSprms->erase(
107 std::remove_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword }),
108 m_pSprms->end());
109 m_pSprms->emplace(m_pSprms->cbegin(), nKeyword, pValue);
110 break;
112 case RTFOverwrite::YES:
114 auto it
115 = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
116 if (it != m_pSprms->end())
117 it->second = pValue;
118 else
119 m_pSprms->emplace_back(nKeyword, pValue);
120 break;
122 case RTFOverwrite::NO_IGNORE:
124 if (std::none_of(m_pSprms->cbegin(), m_pSprms->cend(), RTFSprms_compare{ nKeyword }))
125 m_pSprms->emplace_back(nKeyword, pValue);
126 break;
128 case RTFOverwrite::NO_APPEND:
130 m_pSprms->emplace_back(nKeyword, pValue);
131 break;
136 bool RTFSprms::erase(Id nKeyword)
138 ensureCopyBeforeWrite();
140 auto i = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
141 if (i != m_pSprms->end())
143 m_pSprms->erase(i);
144 return true;
146 return false;
149 void RTFSprms::eraseLast(Id nKeyword)
151 ensureCopyBeforeWrite();
153 auto i = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), RTFSprms_compare{ nKeyword });
154 if (i != m_pSprms->rend())
155 m_pSprms->erase(std::next(i).base());
158 static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType)
160 if (nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
162 switch (id)
164 case NS_ooxml::LN_EG_RPrBase_szCs:
165 case NS_ooxml::LN_EG_RPrBase_sz:
166 return new RTFValue(24);
167 case NS_ooxml::LN_CT_Color_val:
168 return new RTFValue(0);
169 case NS_ooxml::LN_EG_RPrBase_b:
170 case NS_ooxml::LN_EG_RPrBase_i:
171 return new RTFValue(0);
172 case NS_ooxml::LN_CT_Underline_val:
173 return new RTFValue(NS_ooxml::LN_Value_ST_Underline_none);
174 case NS_ooxml::LN_CT_Fonts_ascii:
175 case NS_ooxml::LN_CT_Fonts_eastAsia:
176 case NS_ooxml::LN_CT_Fonts_cs:
177 return new RTFValue("Times New Roman");
178 default:
179 break;
183 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_paragraph)
185 switch (id)
187 case NS_ooxml::LN_CT_Spacing_before:
188 case NS_ooxml::LN_CT_Spacing_after:
189 case NS_ooxml::LN_CT_Ind_left:
190 case NS_ooxml::LN_CT_Ind_right:
191 case NS_ooxml::LN_CT_Ind_firstLine:
192 return new RTFValue(0);
193 case NS_ooxml::LN_CT_Spacing_lineRule:
194 return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto);
195 case NS_ooxml::LN_CT_Spacing_line:
196 // presumably this means 100%, cf. static const int nSingleLineSpacing = 240;
197 return new RTFValue(240);
198 case NS_ooxml::LN_CT_NumPr_numId:
199 return new RTFValue(0);
200 case NS_ooxml::LN_CT_PrBase_pBdr:
201 { // tdf#150382 default all paragraph borders to none
202 RTFSprms attributes;
203 RTFSprms sprms;
204 for (int i = 0; i < 4; ++i)
206 auto const nBorder = getParagraphBorder(i);
207 RTFSprms aAttributes;
208 RTFSprms aSprms;
209 aAttributes.set(NS_ooxml::LN_CT_Border_val,
210 new RTFValue(NS_ooxml::LN_Value_ST_Border_none));
211 sprms.set(nBorder, new RTFValue(aAttributes, aSprms));
213 return new RTFValue(attributes, sprms);
216 default:
217 break;
221 return RTFValue::Pointer_t();
224 /// Is it problematic to deduplicate this SPRM?
225 static bool isSPRMDeduplicateDenylist(Id nId, RTFSprms* pDirect)
227 switch (nId)
229 // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
230 // deduplication is explicitly not wanted for these tokens.
231 case NS_ooxml::LN_CT_TabStop_val:
232 case NS_ooxml::LN_CT_TabStop_leader:
233 case NS_ooxml::LN_CT_TabStop_pos:
234 // \htmautsp arrives after the style table, so only the non-style value is
235 // correct, keep these.
236 case NS_ooxml::LN_CT_Spacing_beforeAutospacing:
237 case NS_ooxml::LN_CT_Spacing_afterAutospacing:
238 // \chbrdr requires *all* of the border settings to be present,
239 // otherwise a default (NONE) border is created from the removed
240 // attributes which then overrides the style-defined border.
241 // See BorderHandler.cxx and NS_ooxml::LN_EG_RPrBase_bdr in DomainMapper.
242 // This also is needed for NS_ooxml::LN_CT_PBdr_top etc.
243 case NS_ooxml::LN_CT_Border_sz:
244 case NS_ooxml::LN_CT_Border_val:
245 case NS_ooxml::LN_CT_Border_color:
246 case NS_ooxml::LN_CT_Border_space:
247 case NS_ooxml::LN_CT_Border_shadow:
248 case NS_ooxml::LN_CT_Border_frame:
249 case NS_ooxml::LN_CT_Border_themeTint:
250 case NS_ooxml::LN_CT_Border_themeColor:
251 return true;
252 // Removing \fi and \li if the style has the same value would mean taking these values from
253 // \ls, while deduplication would be done to take the values from the style.
254 case NS_ooxml::LN_CT_Ind_firstLine:
255 case NS_ooxml::LN_CT_Ind_left:
256 return pDirect && pDirect->find(NS_ooxml::LN_CT_PPrBase_numPr);
258 default:
259 return false;
263 /// Should this SPRM be removed if all its children are removed?
264 static bool isSPRMChildrenExpected(Id nId)
266 switch (nId)
268 case NS_ooxml::LN_CT_PBdr_top:
269 case NS_ooxml::LN_CT_PBdr_left:
270 case NS_ooxml::LN_CT_PBdr_bottom:
271 case NS_ooxml::LN_CT_PBdr_right:
272 // Expected children are NS_ooxml::LN_CT_Border_*.
273 case NS_ooxml::LN_CT_PrBase_shd:
274 // Expected children are NS_ooxml::LN_CT_Shd_*.
275 case NS_ooxml::LN_CT_PPrBase_ind:
276 // Expected children are NS_ooxml::LN_CT_Ind_*.
277 return true;
279 default:
280 return false;
284 /// Does the clone / deduplication of a single sprm.
285 static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t> const& rSprm, RTFSprms& ret,
286 Id nStyleType, RTFSprms* pDirect = nullptr)
288 RTFValue::Pointer_t const pValue(ret.find(rSprm.first));
289 if (pValue)
291 if (rSprm.second->equals(*pValue))
293 if (!isSPRMDeduplicateDenylist(rSprm.first, pDirect))
295 ret.erase(rSprm.first); // duplicate to style
298 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
300 RTFSprms const sprms(pValue->getSprms().cloneAndDeduplicate(
301 rSprm.second->getSprms(), nStyleType, /*bImplicitPPr =*/false, pDirect));
302 RTFSprms const attributes(pValue->getAttributes().cloneAndDeduplicate(
303 rSprm.second->getAttributes(), nStyleType, /*bImplicitPPr =*/false, pDirect));
304 // Don't copy the sprm in case we expect it to have children but it doesn't have some.
305 if (!isSPRMChildrenExpected(rSprm.first) || !sprms.empty() || !attributes.empty())
306 ret.set(rSprm.first,
307 RTFValue::Pointer_t(pValue->CloneWithSprms(attributes, sprms)));
310 else
312 // not found - try to override style with default
313 RTFValue::Pointer_t const pDefault(getDefaultSPRM(rSprm.first, nStyleType));
314 if (pDefault)
316 ret.set(rSprm.first, pDefault);
318 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
320 RTFSprms const sprms(
321 RTFSprms().cloneAndDeduplicate(rSprm.second->getSprms(), nStyleType));
322 RTFSprms const attributes(
323 RTFSprms().cloneAndDeduplicate(rSprm.second->getAttributes(), nStyleType));
324 if (!sprms.empty() || !attributes.empty())
326 ret.set(rSprm.first, new RTFValue(attributes, sprms));
332 /// Extracts the list level matching nLevel from pAbstract.
333 static RTFValue::Pointer_t getListLevel(const RTFValue::Pointer_t& pAbstract, int nLevel)
335 for (const auto& rPair : pAbstract->getSprms())
337 if (rPair.first != NS_ooxml::LN_CT_AbstractNum_lvl)
338 continue;
340 RTFValue::Pointer_t pLevel = rPair.second->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl);
341 if (!pLevel)
342 continue;
344 if (pLevel->getInt() != nLevel)
345 continue;
347 return rPair.second;
350 return RTFValue::Pointer_t();
353 void RTFSprms::deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents)
355 int nLevel = 0;
356 RTFValue::Pointer_t pLevelId
357 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
358 if (pLevelId)
359 nLevel = pLevelId->getInt();
361 auto it = rInvalidListLevelFirstIndents.find(nLevel);
362 if (it == rInvalidListLevelFirstIndents.end())
363 return;
365 int nListValue = it->second;
367 RTFValue::Pointer_t pParagraphValue
368 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
369 if (!pParagraphValue)
370 return;
372 int nParagraphValue = pParagraphValue->getInt();
374 if (nParagraphValue == nListValue)
375 eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
378 void RTFSprms::duplicateList(const RTFValue::Pointer_t& pAbstract)
380 int nLevel = 0;
381 RTFValue::Pointer_t pLevelId
382 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
383 if (pLevelId)
384 nLevel = pLevelId->getInt();
386 RTFValue::Pointer_t pLevel = getListLevel(pAbstract, nLevel);
387 if (!pLevel)
388 return;
390 RTFValue::Pointer_t pLevelInd = pLevel->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind);
391 if (!pLevelInd)
392 return;
394 for (const auto& rListLevelPair : pLevelInd->getAttributes())
396 switch (rListLevelPair.first)
398 case NS_ooxml::LN_CT_Ind_left:
399 case NS_ooxml::LN_CT_Ind_right:
400 case NS_ooxml::LN_CT_Ind_firstLine:
401 RTFValue::Pointer_t pParagraphValue
402 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first);
403 if (!pParagraphValue)
404 putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first,
405 getDefaultSPRM(rListLevelPair.first, 0));
407 break;
412 RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType,
413 bool const bImplicitPPr, RTFSprms* pDirect) const
415 RTFSprms ret(*this);
416 ret.ensureCopyBeforeWrite();
418 // Note: apparently some attributes are set with OVERWRITE_NO_APPEND;
419 // it is probably a bad idea to mess with those in any way here?
420 for (auto& rSprm : rReference)
422 // Paragraph formatting sprms are directly contained in case of
423 // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of
424 // styles. So handle those children directly, to avoid unexpected
425 // addition of direct formatting sprms at the paragraph level.
426 if (bImplicitPPr && rSprm.first == NS_ooxml::LN_CT_Style_pPr)
428 for (const auto& i : rSprm.second->getSprms())
429 cloneAndDeduplicateSprm(i, ret, nStyleType, pDirect);
431 else
432 cloneAndDeduplicateSprm(rSprm, ret, nStyleType, pDirect);
434 return ret;
437 bool RTFSprms::equals(const RTFValue& rOther) const
439 return std::all_of(m_pSprms->cbegin(), m_pSprms->cend(),
440 [&](const std::pair<Id, RTFValue::Pointer_t>& raPair) -> bool {
441 return raPair.second->equals(rOther);
445 void RTFSprms::ensureCopyBeforeWrite()
447 if (m_pSprms->GetRefCount() > 1)
449 tools::SvRef<RTFSprmsImpl> pClone(new RTFSprmsImpl);
450 for (auto& rSprm : *m_pSprms)
451 pClone->push_back(
452 std::make_pair(rSprm.first, RTFValue::Pointer_t(rSprm.second->Clone())));
453 m_pSprms = pClone;
457 RTFSprms::RTFSprms()
458 : m_pSprms(new RTFSprmsImpl)
462 RTFSprms::~RTFSprms() = default;
464 void RTFSprms::clear()
466 if (m_pSprms->GetRefCount() == 1)
467 return m_pSprms->clear();
469 m_pSprms = tools::SvRef<RTFSprmsImpl>(new RTFSprmsImpl);
472 } // namespace writerfilter::rtftok
474 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */