1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include "rtfsprm.hxx"
11 #include <ooxml/resourceids.hxx>
12 #include <ooxml/QNameToString.hxx>
13 #include <rtl/strbuf.hxx>
14 #include "rtfdocumentimpl.hxx"
17 namespace writerfilter::rtftok
19 RTFSprm::RTFSprm(Id nKeyword
, RTFValue::Pointer_t
& pValue
)
20 : m_nKeyword(nKeyword
)
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();
35 std::string
RTFSprm::getName() const { return "RTFSprm"; }
39 std::string
RTFSprm::toString() const
41 OStringBuffer
aBuf("RTFSprm");
43 std::string sResult
= QNameToString(m_nKeyword
);
46 if (sResult
.length() == 0)
47 aBuf
.append(sal_Int32(m_nKeyword
));
49 aBuf
.append(sResult
.c_str());
50 aBuf
.append("', '" + m_pValue
->toString() + "')");
52 return std::string(aBuf
);
58 class RTFSprms_compare
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
)
77 ensureCopyBeforeWrite();
79 RTFSprms_compare cmp
{ nKeyword
};
83 auto it
= std::find_if(m_pSprms
->begin(), m_pSprms
->end(), cmp
);
84 if (it
!= m_pSprms
->end())
90 auto rit
= std::find_if(m_pSprms
->rbegin(), m_pSprms
->rend(), cmp
);
91 if (rit
!= m_pSprms
->rend())
95 return RTFValue::Pointer_t
{};
98 void RTFSprms::set(Id nKeyword
, const RTFValue::Pointer_t
& pValue
, RTFOverwrite eOverwrite
)
100 ensureCopyBeforeWrite();
104 case RTFOverwrite::YES_PREPEND
:
106 std::erase_if(*m_pSprms
, RTFSprms_compare
{ nKeyword
});
107 m_pSprms
->emplace(m_pSprms
->cbegin(), nKeyword
, pValue
);
110 case RTFOverwrite::YES
:
113 = std::find_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
});
114 if (it
!= m_pSprms
->end())
117 m_pSprms
->emplace_back(nKeyword
, pValue
);
120 case RTFOverwrite::NO_IGNORE
:
122 if (std::none_of(m_pSprms
->cbegin(), m_pSprms
->cend(), RTFSprms_compare
{ nKeyword
}))
123 m_pSprms
->emplace_back(nKeyword
, pValue
);
126 case RTFOverwrite::NO_APPEND
:
128 m_pSprms
->emplace_back(nKeyword
, pValue
);
134 bool RTFSprms::erase(Id nKeyword
)
136 ensureCopyBeforeWrite();
138 auto i
= std::find_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
});
139 if (i
!= m_pSprms
->end())
147 void RTFSprms::eraseLast(Id nKeyword
)
149 ensureCopyBeforeWrite();
151 auto i
= std::find_if(m_pSprms
->rbegin(), m_pSprms
->rend(), RTFSprms_compare
{ nKeyword
});
152 if (i
!= m_pSprms
->rend())
153 m_pSprms
->erase(std::next(i
).base());
156 static RTFValue::Pointer_t
getDefaultSPRM(Id
const id
, Id nStyleType
)
158 if (nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
)
162 case NS_ooxml::LN_EG_RPrBase_szCs
:
163 case NS_ooxml::LN_EG_RPrBase_sz
:
164 return new RTFValue(24);
165 case NS_ooxml::LN_CT_Color_val
:
166 return new RTFValue(0);
167 case NS_ooxml::LN_EG_RPrBase_b
:
168 case NS_ooxml::LN_EG_RPrBase_i
:
169 return new RTFValue(0);
170 case NS_ooxml::LN_CT_Underline_val
:
171 return new RTFValue(NS_ooxml::LN_Value_ST_Underline_none
);
172 case NS_ooxml::LN_CT_Fonts_ascii
:
173 case NS_ooxml::LN_CT_Fonts_eastAsia
:
174 case NS_ooxml::LN_CT_Fonts_cs
:
175 return new RTFValue("Times New Roman");
181 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_paragraph
)
185 case NS_ooxml::LN_CT_Spacing_before
:
186 case NS_ooxml::LN_CT_Spacing_after
:
187 case NS_ooxml::LN_CT_Ind_left
:
188 case NS_ooxml::LN_CT_Ind_right
:
189 case NS_ooxml::LN_CT_Ind_firstLine
:
190 return new RTFValue(0);
191 case NS_ooxml::LN_CT_Spacing_lineRule
:
192 return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto
);
193 case NS_ooxml::LN_CT_Spacing_line
:
194 // presumably this means 100%, cf. static const int nSingleLineSpacing = 240;
195 return new RTFValue(240);
196 case NS_ooxml::LN_CT_NumPr_numId
:
197 return new RTFValue(0);
198 case NS_ooxml::LN_CT_PrBase_pBdr
:
199 { // tdf#150382 default all paragraph borders to none
202 for (int i
= 0; i
< 4; ++i
)
204 auto const nBorder
= getParagraphBorder(i
);
205 RTFSprms aAttributes
;
207 aAttributes
.set(NS_ooxml::LN_CT_Border_val
,
208 new RTFValue(NS_ooxml::LN_Value_ST_Border_none
));
209 sprms
.set(nBorder
, new RTFValue(aAttributes
, aSprms
));
211 return new RTFValue(attributes
, sprms
);
219 return RTFValue::Pointer_t();
222 /// Is it problematic to deduplicate this SPRM?
223 static bool isSPRMDeduplicateDenylist(Id nId
, RTFSprms
* pDirect
)
227 // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
228 // deduplication is explicitly not wanted for these tokens.
229 case NS_ooxml::LN_CT_TabStop_val
:
230 case NS_ooxml::LN_CT_TabStop_leader
:
231 case NS_ooxml::LN_CT_TabStop_pos
:
232 // \htmautsp arrives after the style table, so only the non-style value is
233 // correct, keep these.
234 case NS_ooxml::LN_CT_Spacing_beforeAutospacing
:
235 case NS_ooxml::LN_CT_Spacing_afterAutospacing
:
236 // \chbrdr requires *all* of the border settings to be present,
237 // otherwise a default (NONE) border is created from the removed
238 // attributes which then overrides the style-defined border.
239 // See BorderHandler.cxx and NS_ooxml::LN_EG_RPrBase_bdr in DomainMapper.
240 // This also is needed for NS_ooxml::LN_CT_PBdr_top etc.
241 case NS_ooxml::LN_CT_Border_sz
:
242 case NS_ooxml::LN_CT_Border_val
:
243 case NS_ooxml::LN_CT_Border_color
:
244 case NS_ooxml::LN_CT_Border_space
:
245 case NS_ooxml::LN_CT_Border_shadow
:
246 case NS_ooxml::LN_CT_Border_frame
:
247 case NS_ooxml::LN_CT_Border_themeTint
:
248 case NS_ooxml::LN_CT_Border_themeColor
:
250 // Removing \fi and \li if the style has the same value would mean taking these values from
251 // \ls, while deduplication would be done to take the values from the style.
252 case NS_ooxml::LN_CT_Ind_firstLine
:
253 case NS_ooxml::LN_CT_Ind_left
:
254 return pDirect
&& pDirect
->find(NS_ooxml::LN_CT_PPrBase_numPr
);
261 /// Should this SPRM be removed if all its children are removed?
262 static bool isSPRMChildrenExpected(Id nId
)
266 case NS_ooxml::LN_CT_PBdr_top
:
267 case NS_ooxml::LN_CT_PBdr_left
:
268 case NS_ooxml::LN_CT_PBdr_bottom
:
269 case NS_ooxml::LN_CT_PBdr_right
:
270 // Expected children are NS_ooxml::LN_CT_Border_*.
271 case NS_ooxml::LN_CT_PrBase_shd
:
272 // Expected children are NS_ooxml::LN_CT_Shd_*.
273 case NS_ooxml::LN_CT_PPrBase_ind
:
274 // Expected children are NS_ooxml::LN_CT_Ind_*.
282 /// Does the clone / deduplication of a single sprm.
283 static void cloneAndDeduplicateSprm(std::pair
<Id
, RTFValue::Pointer_t
> const& rSprm
, RTFSprms
& ret
,
284 Id nStyleType
, RTFSprms
* pDirect
= nullptr)
286 RTFValue::Pointer_t
const pValue(ret
.find(rSprm
.first
));
289 if (rSprm
.second
->equals(*pValue
))
291 //this removes properties that are equal at the style and at the sprm
292 //don't do that for paragraph styles
293 if (nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
)
295 if (!isSPRMDeduplicateDenylist(rSprm
.first
, pDirect
))
297 ret
.erase(rSprm
.first
); // duplicate to style
301 else if (!rSprm
.second
->getSprms().empty() || !rSprm
.second
->getAttributes().empty())
303 RTFSprms
const sprms(pValue
->getSprms().cloneAndDeduplicate(
304 rSprm
.second
->getSprms(), nStyleType
, /*bImplicitPPr =*/false, pDirect
));
305 RTFSprms
const attributes(pValue
->getAttributes().cloneAndDeduplicate(
306 rSprm
.second
->getAttributes(), nStyleType
, /*bImplicitPPr =*/false, pDirect
));
307 // Don't copy the sprm in case we expect it to have children but it doesn't have some.
308 if (!isSPRMChildrenExpected(rSprm
.first
) || !sprms
.empty() || !attributes
.empty())
310 RTFValue::Pointer_t(pValue
->CloneWithSprms(attributes
, sprms
)));
315 // not found - try to override style with default
316 RTFValue::Pointer_t
const pDefault(getDefaultSPRM(rSprm
.first
, nStyleType
));
319 ret
.set(rSprm
.first
, pDefault
);
321 else if (!rSprm
.second
->getSprms().empty() || !rSprm
.second
->getAttributes().empty())
323 RTFSprms
const sprms(
324 RTFSprms().cloneAndDeduplicate(rSprm
.second
->getSprms(), nStyleType
));
325 RTFSprms
const attributes(
326 RTFSprms().cloneAndDeduplicate(rSprm
.second
->getAttributes(), nStyleType
));
327 if (!sprms
.empty() || !attributes
.empty())
329 ret
.set(rSprm
.first
, new RTFValue(attributes
, sprms
));
335 /// Extracts the list level matching nLevel from pAbstract.
336 static RTFValue::Pointer_t
getListLevel(const RTFValue::Pointer_t
& pAbstract
, int nLevel
)
338 for (const auto& rPair
: pAbstract
->getSprms())
340 if (rPair
.first
!= NS_ooxml::LN_CT_AbstractNum_lvl
)
343 RTFValue::Pointer_t pLevel
= rPair
.second
->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl
);
347 if (pLevel
->getInt() != nLevel
)
353 return RTFValue::Pointer_t();
356 void RTFSprms::deduplicateList(const std::map
<int, int>& rInvalidListLevelFirstIndents
)
359 RTFValue::Pointer_t pLevelId
360 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
362 nLevel
= pLevelId
->getInt();
364 auto it
= rInvalidListLevelFirstIndents
.find(nLevel
);
365 if (it
== rInvalidListLevelFirstIndents
.end())
368 int nListValue
= it
->second
;
370 RTFValue::Pointer_t pParagraphValue
371 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, NS_ooxml::LN_CT_Ind_firstLine
);
372 if (!pParagraphValue
)
375 int nParagraphValue
= pParagraphValue
->getInt();
377 if (nParagraphValue
== nListValue
)
378 eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, NS_ooxml::LN_CT_Ind_firstLine
);
381 void RTFSprms::duplicateList(const RTFValue::Pointer_t
& pAbstract
)
384 RTFValue::Pointer_t pLevelId
385 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
387 nLevel
= pLevelId
->getInt();
389 RTFValue::Pointer_t pLevel
= getListLevel(pAbstract
, nLevel
);
393 RTFValue::Pointer_t pLevelInd
= pLevel
->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind
);
397 for (const auto& rListLevelPair
: pLevelInd
->getAttributes())
399 switch (rListLevelPair
.first
)
401 case NS_ooxml::LN_CT_Ind_left
:
402 case NS_ooxml::LN_CT_Ind_right
:
403 case NS_ooxml::LN_CT_Ind_firstLine
:
404 RTFValue::Pointer_t pParagraphValue
405 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, rListLevelPair
.first
);
406 if (!pParagraphValue
)
407 putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, rListLevelPair
.first
,
408 getDefaultSPRM(rListLevelPair
.first
, 0));
415 RTFSprms
RTFSprms::cloneAndDeduplicate(RTFSprms
& rReference
, Id
const nStyleType
,
416 bool const bImplicitPPr
, RTFSprms
* pDirect
) const
419 ret
.ensureCopyBeforeWrite();
421 // Note: apparently some attributes are set with OVERWRITE_NO_APPEND;
422 // it is probably a bad idea to mess with those in any way here?
423 for (auto& rSprm
: rReference
)
425 // Paragraph formatting sprms are directly contained in case of
426 // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of
427 // styles. So handle those children directly, to avoid unexpected
428 // addition of direct formatting sprms at the paragraph level.
429 if (bImplicitPPr
&& rSprm
.first
== NS_ooxml::LN_CT_Style_pPr
)
431 for (const auto& i
: rSprm
.second
->getSprms())
432 cloneAndDeduplicateSprm(i
, ret
, nStyleType
, pDirect
);
435 cloneAndDeduplicateSprm(rSprm
, ret
, nStyleType
, pDirect
);
440 bool RTFSprms::equals(const RTFSprms
& rOther
) const
442 auto it1
= m_pSprms
->cbegin();
443 auto it1End
= m_pSprms
->cend();
444 auto it2
= rOther
.m_pSprms
->cbegin();
445 auto it2End
= rOther
.m_pSprms
->cend();
446 while (it1
!= it1End
&& it2
!= it2End
)
448 if (it1
->first
!= it2
->first
)
450 if (!it1
->second
->equals(*it2
->second
))
455 return it1
== it1End
&& it2
== it2End
;
458 void RTFSprms::ensureCopyBeforeWrite()
460 if (m_pSprms
->GetRefCount() > 1)
462 tools::SvRef
<RTFSprmsImpl
> pClone(new RTFSprmsImpl
);
463 for (auto& rSprm
: *m_pSprms
)
465 std::make_pair(rSprm
.first
, RTFValue::Pointer_t(rSprm
.second
->Clone())));
471 : m_pSprms(new RTFSprmsImpl
)
475 RTFSprms::~RTFSprms() = default;
477 void RTFSprms::clear()
479 if (m_pSprms
->GetRefCount() == 1)
480 return m_pSprms
->clear();
482 m_pSprms
= tools::SvRef
<RTFSprmsImpl
>(new RTFSprmsImpl
);
485 } // namespace writerfilter::rtftok
487 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */