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
:
107 std::remove_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
}),
109 m_pSprms
->emplace(m_pSprms
->cbegin(), nKeyword
, pValue
);
112 case RTFOverwrite::YES
:
115 = std::find_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
});
116 if (it
!= m_pSprms
->end())
119 m_pSprms
->emplace_back(nKeyword
, pValue
);
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
);
128 case RTFOverwrite::NO_APPEND
:
130 m_pSprms
->emplace_back(nKeyword
, pValue
);
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())
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
)
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");
183 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_paragraph
)
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
204 for (int i
= 0; i
< 4; ++i
)
206 auto const nBorder
= getParagraphBorder(i
);
207 RTFSprms aAttributes
;
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
);
221 return RTFValue::Pointer_t();
224 /// Is it problematic to deduplicate this SPRM?
225 static bool isSPRMDeduplicateDenylist(Id nId
, RTFSprms
* pDirect
)
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
:
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
);
263 /// Should this SPRM be removed if all its children are removed?
264 static bool isSPRMChildrenExpected(Id 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_*.
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
));
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())
307 RTFValue::Pointer_t(pValue
->CloneWithSprms(attributes
, sprms
)));
312 // not found - try to override style with default
313 RTFValue::Pointer_t
const pDefault(getDefaultSPRM(rSprm
.first
, nStyleType
));
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
)
340 RTFValue::Pointer_t pLevel
= rPair
.second
->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl
);
344 if (pLevel
->getInt() != nLevel
)
350 return RTFValue::Pointer_t();
353 void RTFSprms::deduplicateList(const std::map
<int, int>& rInvalidListLevelFirstIndents
)
356 RTFValue::Pointer_t pLevelId
357 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
359 nLevel
= pLevelId
->getInt();
361 auto it
= rInvalidListLevelFirstIndents
.find(nLevel
);
362 if (it
== rInvalidListLevelFirstIndents
.end())
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
)
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
)
381 RTFValue::Pointer_t pLevelId
382 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
384 nLevel
= pLevelId
->getInt();
386 RTFValue::Pointer_t pLevel
= getListLevel(pAbstract
, nLevel
);
390 RTFValue::Pointer_t pLevelInd
= pLevel
->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind
);
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));
412 RTFSprms
RTFSprms::cloneAndDeduplicate(RTFSprms
& rReference
, Id
const nStyleType
,
413 bool const bImplicitPPr
, RTFSprms
* pDirect
) const
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
);
432 cloneAndDeduplicateSprm(rSprm
, ret
, nStyleType
, pDirect
);
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
)
452 std::make_pair(rSprm
.first
, RTFValue::Pointer_t(rSprm
.second
->Clone())));
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: */