LibreOffice Module writerfilter (master) 1
rtfsprm.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#include "rtfsprm.hxx"
11#include <ooxml/resourceids.hxx>
13#include <rtl/strbuf.hxx>
14#include "rtfdocumentimpl.hxx"
15#include <algorithm>
16
18{
20 : m_nKeyword(nKeyword)
21 , m_pValue(pValue)
22{
23}
24
25sal_uInt32 RTFSprm::getId() const { return m_nKeyword; }
26
28
30{
31 return m_pValue->getProperties();
32}
33
34#ifdef DBG_UTIL
35std::string RTFSprm::getName() const { return "RTFSprm"; }
36#endif
37
38#ifdef DBG_UTIL
39std::string RTFSprm::toString() const
40{
41 OStringBuffer aBuf("RTFSprm");
42
43 std::string sResult = QNameToString(m_nKeyword);
44
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() + "')");
51
52 return std::string(aBuf);
53}
54#endif
55
56namespace
57{
58class RTFSprms_compare
59{
61
62public:
63 RTFSprms_compare(Id keyword)
64 : m_keyword{ keyword }
65 {
66 }
67 bool operator()(const std::pair<Id, RTFValue::Pointer_t>& raPair) const
68 {
69 return raPair.first == m_keyword;
70 }
71};
72}
73
74RTFValue::Pointer_t RTFSprms::find(Id nKeyword, bool bFirst, bool bForWrite)
75{
76 if (bForWrite)
78
79 RTFSprms_compare cmp{ nKeyword };
80
81 if (bFirst)
82 {
83 auto it = std::find_if(m_pSprms->begin(), m_pSprms->end(), cmp);
84 if (it != m_pSprms->end())
85 return it->second;
86 }
87 else
88 // find last
89 {
90 auto rit = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), cmp);
91 if (rit != m_pSprms->rend())
92 return rit->second;
93 }
94
95 return RTFValue::Pointer_t{};
96}
97
98void RTFSprms::set(Id nKeyword, const RTFValue::Pointer_t& pValue, RTFOverwrite eOverwrite)
99{
101
102 switch (eOverwrite)
103 {
105 {
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;
111 }
113 {
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;
121 }
123 {
124 if (std::none_of(m_pSprms->cbegin(), m_pSprms->cend(), RTFSprms_compare{ nKeyword }))
125 m_pSprms->emplace_back(nKeyword, pValue);
126 break;
127 }
129 {
130 m_pSprms->emplace_back(nKeyword, pValue);
131 break;
132 }
133 }
134}
135
136bool RTFSprms::erase(Id nKeyword)
137{
139
140 auto i = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword });
141 if (i != m_pSprms->end())
142 {
143 m_pSprms->erase(i);
144 return true;
145 }
146 return false;
147}
148
150{
152
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());
156}
157
158static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType)
159{
160 if (nStyleType == NS_ooxml::LN_Value_ST_StyleType_character)
161 {
162 switch (id)
163 {
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;
180 }
181 }
182
183 if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_paragraph)
184 {
185 switch (id)
186 {
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)
205 {
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));
212 }
213 return new RTFValue(attributes, sprms);
214 }
215
216 default:
217 break;
218 }
219 }
220
221 return RTFValue::Pointer_t();
222}
223
225static bool isSPRMDeduplicateDenylist(Id nId, RTFSprms* pDirect)
226{
227 switch (nId)
228 {
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);
257
258 default:
259 return false;
260 }
261}
262
265{
266 switch (nId)
267 {
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;
278
279 default:
280 return false;
281 }
282}
283
285static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t> const& rSprm, RTFSprms& ret,
286 Id nStyleType, RTFSprms* pDirect = nullptr)
287{
288 RTFValue::Pointer_t const pValue(ret.find(rSprm.first));
289 if (pValue)
290 {
291 if (rSprm.second->equals(*pValue))
292 {
293 if (!isSPRMDeduplicateDenylist(rSprm.first, pDirect))
294 {
295 ret.erase(rSprm.first); // duplicate to style
296 }
297 }
298 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
299 {
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)));
308 }
309 }
310 else
311 {
312 // not found - try to override style with default
313 RTFValue::Pointer_t const pDefault(getDefaultSPRM(rSprm.first, nStyleType));
314 if (pDefault)
315 {
316 ret.set(rSprm.first, pDefault);
317 }
318 else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty())
319 {
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())
325 {
326 ret.set(rSprm.first, new RTFValue(attributes, sprms));
327 }
328 }
329 }
330}
331
333static RTFValue::Pointer_t getListLevel(const RTFValue::Pointer_t& pAbstract, int nLevel)
334{
335 for (const auto& rPair : pAbstract->getSprms())
336 {
337 if (rPair.first != NS_ooxml::LN_CT_AbstractNum_lvl)
338 continue;
339
340 RTFValue::Pointer_t pLevel = rPair.second->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl);
341 if (!pLevel)
342 continue;
343
344 if (pLevel->getInt() != nLevel)
345 continue;
346
347 return rPair.second;
348 }
349
350 return RTFValue::Pointer_t();
351}
352
353void RTFSprms::deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents)
354{
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();
360
361 auto it = rInvalidListLevelFirstIndents.find(nLevel);
362 if (it == rInvalidListLevelFirstIndents.end())
363 return;
364
365 int nListValue = it->second;
366
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;
371
372 int nParagraphValue = pParagraphValue->getInt();
373
374 if (nParagraphValue == nListValue)
375 eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine);
376}
377
379{
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();
385
386 RTFValue::Pointer_t pLevel = getListLevel(pAbstract, nLevel);
387 if (!pLevel)
388 return;
389
390 RTFValue::Pointer_t pLevelInd = pLevel->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind);
391 if (!pLevelInd)
392 return;
393
394 for (const auto& rListLevelPair : pLevelInd->getAttributes())
395 {
396 switch (rListLevelPair.first)
397 {
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));
406
407 break;
408 }
409 }
410}
411
412RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType,
413 bool const bImplicitPPr, RTFSprms* pDirect) const
414{
415 RTFSprms ret(*this);
417
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)
421 {
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)
427 {
428 for (const auto& i : rSprm.second->getSprms())
429 cloneAndDeduplicateSprm(i, ret, nStyleType, pDirect);
430 }
431 else
432 cloneAndDeduplicateSprm(rSprm, ret, nStyleType, pDirect);
433 }
434 return ret;
435}
436
437bool RTFSprms::equals(const RTFValue& rOther) const
438{
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);
442 });
443}
444
446{
447 if (m_pSprms->GetRefCount() > 1)
448 {
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;
454 }
455}
456
458 : m_pSprms(new RTFSprmsImpl)
459{
460}
461
462RTFSprms::~RTFSprms() = default;
463
465{
466 if (m_pSprms->GetRefCount() == 1)
467 return m_pSprms->clear();
468
470}
471
472} // namespace writerfilter::rtftok
473
474/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
tools::SvRef< Value > Pointer_t
Pointer to a value.
RTFValue::Pointer_t & m_pValue
Definition: rtfsprm.hxx:97
writerfilter::Reference< Properties >::Pointer_t getProps() override
Returns reference to properties contained in the SPRM.
Definition: rtfsprm.cxx:29
std::string getName() const override
Returns name of sprm.
Definition: rtfsprm.cxx:35
Value::Pointer_t getValue() override
Returns value of the SPRM.
Definition: rtfsprm.cxx:27
sal_uInt32 getId() const override
Returns id of the SPRM.
Definition: rtfsprm.cxx:25
std::string toString() const override
Returns string representation of sprm.
Definition: rtfsprm.cxx:39
RTFSprm(Id nKeyword, RTFValue::Pointer_t &pValue)
Definition: rtfsprm.cxx:19
The payload of RTFSprms which is only copied on write.
Definition: rtfsprm.hxx:26
A list of RTFSprm with a copy constructor that performs a deep copy.
Definition: rtfsprm.hxx:39
RTFValue::Pointer_t find(Id nKeyword, bool bFirst=true, bool bForWrite=false)
Definition: rtfsprm.cxx:74
bool equals(const RTFValue &rOther) const
Definition: rtfsprm.cxx:437
void deduplicateList(const std::map< int, int > &rInvalidListLevelFirstIndents)
Removes duplicated values based on in-list properties.
Definition: rtfsprm.cxx:353
void set(Id nKeyword, const RTFValue::Pointer_t &pValue, RTFOverwrite eOverwrite=RTFOverwrite::YES)
Does the same as ->push_back(), except that it can overwrite or ignore existing entries.
Definition: rtfsprm.cxx:98
tools::SvRef< RTFSprmsImpl > m_pSprms
Definition: rtfsprm.hxx:80
bool erase(Id nKeyword)
Definition: rtfsprm.cxx:136
void eraseLast(Id nKeyword)
Definition: rtfsprm.cxx:149
RTFSprms cloneAndDeduplicate(RTFSprms &rReference, Id nStyleType, bool bImplicitPPr=false, RTFSprms *pDirect=nullptr) const
Removes elements which are already in the reference set.
Definition: rtfsprm.cxx:412
void duplicateList(const RTFValue::Pointer_t &pAbstract)
Inserts default values to override attributes of pAbstract.
Definition: rtfsprm.cxx:378
Value of an RTF keyword.
Definition: rtfvalue.hxx:33
tools::SvRef< RTFValue > Pointer_t
Definition: rtfvalue.hxx:41
void const * base
ParagraphAdjust nParagraphValue
aBuf
tools::Long const nBorder
int i
static bool isSPRMDeduplicateDenylist(Id nId, RTFSprms *pDirect)
Is it problematic to deduplicate this SPRM?
Definition: rtfsprm.cxx:225
static bool isSPRMChildrenExpected(Id nId)
Should this SPRM be removed if all its children are removed?
Definition: rtfsprm.cxx:264
RTFValue::Pointer_t getNestedAttribute(RTFSprms &rSprms, Id nParent, Id nId)
Looks up the nParent then the nested nId attribute in rSprms.
static void cloneAndDeduplicateSprm(std::pair< Id, RTFValue::Pointer_t > const &rSprm, RTFSprms &ret, Id nStyleType, RTFSprms *pDirect=nullptr)
Does the clone / deduplication of a single sprm.
Definition: rtfsprm.cxx:285
void putNestedAttribute(RTFSprms &rSprms, Id nParent, Id nId, const RTFValue::Pointer_t &pValue, RTFOverwrite eOverwrite, bool bAttribute)
bool eraseNestedAttribute(RTFSprms &rSprms, Id nParent, Id nId)
@ NO_IGNORE
No, if the key is already in the list, then ignore, otherwise append.
@ NO_APPEND
No, always append the value to the end of the list.
@ YES_PREPEND
Yes, always prepend the value to the start of the list and remove existing entries.
@ YES
Yes, if an existing key is found, overwrite it.
RTFValue::Pointer_t getNestedSprm(RTFSprms &rSprms, Id nParent, Id nId)
Looks up the nParent then the nested nId sprm in rSprms.
Id getParagraphBorder(sal_uInt32 nIndex)
static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType)
Definition: rtfsprm.cxx:158
static RTFValue::Pointer_t getListLevel(const RTFValue::Pointer_t &pAbstract, int nLevel)
Extracts the list level matching nLevel from pAbstract.
Definition: rtfsprm.cxx:333
std::string QNameToString(Id)
sal_Int16 nId
sal_uInt32 Id
Id m_keyword
Definition: rtfsprm.cxx:60