LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / writerfilter / source / rtftok / rtfsprm.cxx
blob2edfec829edf93adb7cda08ee080ed307fa33b99
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("', '");
51 aBuf.append(m_pValue->toString().c_str());
52 aBuf.append("')");
54 return aBuf.makeStringAndClear().getStr();
56 #endif
58 namespace
60 class RTFSprms_compare
62 Id keyword;
64 public:
65 RTFSprms_compare(Id kw)
66 : keyword{ kw }
69 bool operator()(const std::pair<Id, RTFValue::Pointer_t>& raPair) const
71 return raPair.first == keyword;
76 RTFValue::Pointer_t RTFSprms::find(Id nKeyword, bool bFirst, bool bForWrite)
78 if (bForWrite)
79 ensureCopyBeforeWrite();
81 RTFSprms_compare cmp{ nKeyword };
83 if (bFirst)
85 auto it = std::find_if(m_pSprms->begin(), m_pSprms->end(), cmp);
86 if (it != m_pSprms->end())
87 return it->second;
89 else
90 // find last
92 auto rit = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), cmp);
93 if (rit != m_pSprms->rend())
94 return rit->second;
97 return RTFValue::Pointer_t{};
100 void RTFSprms::set(Id nKeyword, const RTFValue::Pointer_t& pValue, RTFOverwrite eOverwrite)
102 ensureCopyBeforeWrite();
104 switch (eOverwrite)
106 case RTFOverwrite::YES_PREPEND:
108 m_pSprms->erase(
109 std::remove_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword }),
110 m_pSprms->end());
111 m_pSprms->emplace(m_pSprms->cbegin(), nKeyword, pValue);
112 break;
114 case RTFOverwrite::YES:
116 auto it
117 = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
118 if (it != m_pSprms->end())
119 it->second = pValue;
120 else
121 m_pSprms->emplace_back(nKeyword, pValue);
122 break;
124 case RTFOverwrite::NO_IGNORE:
126 if (std::none_of(m_pSprms->cbegin(), m_pSprms->cend(), RTFSprms_compare{ nKeyword }))
127 m_pSprms->emplace_back(nKeyword, pValue);
128 break;
130 case RTFOverwrite::NO_APPEND:
132 m_pSprms->emplace_back(nKeyword, pValue);
133 break;
138 bool RTFSprms::erase(Id nKeyword)
140 ensureCopyBeforeWrite();
142 auto i = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
143 if (i != m_pSprms->end())
145 m_pSprms->erase(i);
146 return true;
148 return false;
151 void RTFSprms::eraseLast(Id nKeyword)
153 ensureCopyBeforeWrite();
155 auto i = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), RTFSprms_compare{ nKeyword });
156 if (i != m_pSprms->rend())
157 m_pSprms->erase(std::next(i).base());
160 static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType)
162 if (nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
164 switch (id)
166 case NS_ooxml::LN_EG_RPrBase_szCs:
167 case NS_ooxml::LN_EG_RPrBase_sz:
168 return new RTFValue(24);
169 case NS_ooxml::LN_CT_Color_val:
170 return new RTFValue(0);
171 case NS_ooxml::LN_EG_RPrBase_b:
172 case NS_ooxml::LN_EG_RPrBase_i:
173 return new RTFValue(0);
174 case NS_ooxml::LN_CT_Underline_val:
175 return new RTFValue(NS_ooxml::LN_Value_ST_Underline_none);
176 case NS_ooxml::LN_CT_Fonts_ascii:
177 case NS_ooxml::LN_CT_Fonts_eastAsia:
178 case NS_ooxml::LN_CT_Fonts_cs:
179 return new RTFValue("Times New Roman");
180 default:
181 break;
185 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_paragraph)
187 switch (id)
189 case NS_ooxml::LN_CT_Spacing_before:
190 case NS_ooxml::LN_CT_Spacing_after:
191 case NS_ooxml::LN_CT_Ind_left:
192 case NS_ooxml::LN_CT_Ind_right:
193 case NS_ooxml::LN_CT_Ind_firstLine:
194 return new RTFValue(0);
196 case NS_ooxml::LN_CT_Spacing_lineRule:
197 return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto);
198 case NS_ooxml::LN_CT_Spacing_line:
199 // presumably this means 100%, cf. static const int nSingleLineSpacing = 240;
200 return new RTFValue(240);
202 default:
203 break;
207 return RTFValue::Pointer_t();
210 /// Is it problematic to deduplicate this SPRM?
211 static bool isSPRMDeduplicateDenylist(Id nId, RTFSprms* pDirect)
213 switch (nId)
215 // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
216 // deduplication is explicitly not wanted for these tokens.
217 case NS_ooxml::LN_CT_TabStop_val:
218 case NS_ooxml::LN_CT_TabStop_leader:
219 case NS_ooxml::LN_CT_TabStop_pos:
220 // \htmautsp arrives after the style table, so only the non-style value is
221 // correct, keep these.
222 case NS_ooxml::LN_CT_Spacing_beforeAutospacing:
223 case NS_ooxml::LN_CT_Spacing_afterAutospacing:
224 // \chbrdr requires *all* of the border settings to be present,
225 // otherwise a default (NONE) border is created from the removed
226 // attributes which then overrides the style-defined border.
227 // See BorderHandler.cxx and NS_ooxml::LN_EG_RPrBase_bdr in DomainMapper.
228 // This also is needed for NS_ooxml::LN_CT_PBdr_top etc.
229 case NS_ooxml::LN_CT_Border_sz:
230 case NS_ooxml::LN_CT_Border_val:
231 case NS_ooxml::LN_CT_Border_color:
232 case NS_ooxml::LN_CT_Border_space:
233 case NS_ooxml::LN_CT_Border_shadow:
234 case NS_ooxml::LN_CT_Border_frame:
235 case NS_ooxml::LN_CT_Border_themeTint:
236 case NS_ooxml::LN_CT_Border_themeColor:
237 return true;
238 // Removing \fi and \li if the style has the same value would mean taking these values from
239 // \ls, while deduplication would be done to take the values from the style.
240 case NS_ooxml::LN_CT_Ind_firstLine:
241 case NS_ooxml::LN_CT_Ind_left:
242 return pDirect && pDirect->find(NS_ooxml::LN_CT_PPrBase_numPr);
244 default:
245 return false;
249 /// Should this SPRM be removed if all its children are removed?
250 static bool isSPRMChildrenExpected(Id nId)
252 switch (nId)
254 case NS_ooxml::LN_CT_PBdr_top:
255 case NS_ooxml::LN_CT_PBdr_left:
256 case NS_ooxml::LN_CT_PBdr_bottom:
257 case NS_ooxml::LN_CT_PBdr_right:
258 // Expected children are NS_ooxml::LN_CT_Border_*.
259 case NS_ooxml::LN_CT_PrBase_shd:
260 // Expected children are NS_ooxml::LN_CT_Shd_*.
261 case NS_ooxml::LN_CT_PPrBase_ind:
262 // Expected children are NS_ooxml::LN_CT_Ind_*.
263 return true;
265 default:
266 return false;
270 /// Does the clone / deduplication of a single sprm.
271 static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t> const& rSprm, RTFSprms& ret,
272 Id nStyleType, RTFSprms* pDirect = nullptr)
274 RTFValue::Pointer_t const pValue(ret.find(rSprm.first));
275 if (pValue)
277 if (rSprm.second->equals(*pValue))
279 if (!isSPRMDeduplicateDenylist(rSprm.first, pDirect))
281 ret.erase(rSprm.first); // duplicate to style
284 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
286 RTFSprms const sprms(pValue->getSprms().cloneAndDeduplicate(
287 rSprm.second->getSprms(), nStyleType, /*bImplicitPPr =*/false, pDirect));
288 RTFSprms const attributes(pValue->getAttributes().cloneAndDeduplicate(
289 rSprm.second->getAttributes(), nStyleType, /*bImplicitPPr =*/false, pDirect));
290 // Don't copy the sprm in case we expect it to have children but it doesn't have some.
291 if (!isSPRMChildrenExpected(rSprm.first) || !sprms.empty() || !attributes.empty())
292 ret.set(rSprm.first,
293 RTFValue::Pointer_t(pValue->CloneWithSprms(attributes, sprms)));
296 else
298 // not found - try to override style with default
299 RTFValue::Pointer_t const pDefault(getDefaultSPRM(rSprm.first, nStyleType));
300 if (pDefault)
302 ret.set(rSprm.first, pDefault);
304 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
306 RTFSprms const sprms(
307 RTFSprms().cloneAndDeduplicate(rSprm.second->getSprms(), nStyleType));
308 RTFSprms const attributes(
309 RTFSprms().cloneAndDeduplicate(rSprm.second->getAttributes(), nStyleType));
310 if (!sprms.empty() || !attributes.empty())
312 ret.set(rSprm.first, new RTFValue(attributes, sprms));
318 /// Extracts the list level matching nLevel from pAbstract.
319 static RTFValue::Pointer_t getListLevel(const RTFValue::Pointer_t& pAbstract, int nLevel)
321 for (const auto& rPair : pAbstract->getSprms())
323 if (rPair.first != NS_ooxml::LN_CT_AbstractNum_lvl)
324 continue;
326 RTFValue::Pointer_t pLevel = rPair.second->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl);
327 if (!pLevel)
328 continue;
330 if (pLevel->getInt() != nLevel)
331 continue;
333 return rPair.second;
336 return RTFValue::Pointer_t();
339 void RTFSprms::deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents)
341 int nLevel = 0;
342 RTFValue::Pointer_t pLevelId
343 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
344 if (pLevelId)
345 nLevel = pLevelId->getInt();
347 auto it = rInvalidListLevelFirstIndents.find(nLevel);
348 if (it == rInvalidListLevelFirstIndents.end())
349 return;
351 int nListValue = it->second;
353 RTFValue::Pointer_t pParagraphValue
354 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
355 if (!pParagraphValue)
356 return;
358 int nParagraphValue = pParagraphValue->getInt();
360 if (nParagraphValue == nListValue)
361 eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
364 void RTFSprms::duplicateList(const RTFValue::Pointer_t& pAbstract)
366 int nLevel = 0;
367 RTFValue::Pointer_t pLevelId
368 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl);
369 if (pLevelId)
370 nLevel = pLevelId->getInt();
372 RTFValue::Pointer_t pLevel = getListLevel(pAbstract, nLevel);
373 if (!pLevel)
374 return;
376 RTFValue::Pointer_t pLevelInd = pLevel->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind);
377 if (!pLevelInd)
378 return;
380 for (const auto& rListLevelPair : pLevelInd->getAttributes())
382 switch (rListLevelPair.first)
384 case NS_ooxml::LN_CT_Ind_left:
385 case NS_ooxml::LN_CT_Ind_right:
386 case NS_ooxml::LN_CT_Ind_firstLine:
387 RTFValue::Pointer_t pParagraphValue
388 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first);
389 if (!pParagraphValue)
390 putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first,
391 getDefaultSPRM(rListLevelPair.first, 0));
393 break;
398 RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType,
399 bool const bImplicitPPr, RTFSprms* pDirect) const
401 RTFSprms ret(*this);
402 ret.ensureCopyBeforeWrite();
404 // Note: apparently some attributes are set with OVERWRITE_NO_APPEND;
405 // it is probably a bad idea to mess with those in any way here?
406 for (auto& rSprm : rReference)
408 // Paragraph formatting sprms are directly contained in case of
409 // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of
410 // styles. So handle those children directly, to avoid unexpected
411 // addition of direct formatting sprms at the paragraph level.
412 if (bImplicitPPr && rSprm.first == NS_ooxml::LN_CT_Style_pPr)
414 for (const auto& i : rSprm.second->getSprms())
415 cloneAndDeduplicateSprm(i, ret, nStyleType, pDirect);
417 else
418 cloneAndDeduplicateSprm(rSprm, ret, nStyleType, pDirect);
420 return ret;
423 bool RTFSprms::equals(const RTFValue& rOther) const
425 return std::all_of(m_pSprms->cbegin(), m_pSprms->cend(),
426 [&](const std::pair<Id, RTFValue::Pointer_t>& raPair) -> bool {
427 return raPair.second->equals(rOther);
431 void RTFSprms::ensureCopyBeforeWrite()
433 if (m_pSprms->GetRefCount() > 1)
435 tools::SvRef<RTFSprmsImpl> pClone(new RTFSprmsImpl);
436 for (auto& rSprm : *m_pSprms)
437 pClone->push_back(
438 std::make_pair(rSprm.first, RTFValue::Pointer_t(rSprm.second->Clone())));
439 m_pSprms = pClone;
443 RTFSprms::RTFSprms()
444 : m_pSprms(new RTFSprmsImpl)
448 RTFSprms::~RTFSprms() = default;
450 void RTFSprms::clear()
452 if (m_pSprms->GetRefCount() == 1)
453 return m_pSprms->clear();
455 m_pSprms = tools::SvRef<RTFSprmsImpl>(new RTFSprmsImpl);
458 } // namespace writerfilter::rtftok
460 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */