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());
51 aBuf
.append(m_pValue
->toString().c_str());
54 return aBuf
.makeStringAndClear().getStr();
60 class RTFSprms_compare
65 RTFSprms_compare(Id 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
)
79 ensureCopyBeforeWrite();
81 RTFSprms_compare cmp
{ nKeyword
};
85 auto it
= std::find_if(m_pSprms
->begin(), m_pSprms
->end(), cmp
);
86 if (it
!= m_pSprms
->end())
92 auto rit
= std::find_if(m_pSprms
->rbegin(), m_pSprms
->rend(), cmp
);
93 if (rit
!= m_pSprms
->rend())
97 return RTFValue::Pointer_t
{};
100 void RTFSprms::set(Id nKeyword
, const RTFValue::Pointer_t
& pValue
, RTFOverwrite eOverwrite
)
102 ensureCopyBeforeWrite();
106 case RTFOverwrite::YES_PREPEND
:
109 std::remove_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
}),
111 m_pSprms
->emplace(m_pSprms
->cbegin(), nKeyword
, pValue
);
114 case RTFOverwrite::YES
:
117 = std::find_if(m_pSprms
->begin(), m_pSprms
->end(), RTFSprms_compare
{ nKeyword
});
118 if (it
!= m_pSprms
->end())
121 m_pSprms
->emplace_back(nKeyword
, pValue
);
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
);
130 case RTFOverwrite::NO_APPEND
:
132 m_pSprms
->emplace_back(nKeyword
, pValue
);
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())
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
)
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");
185 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_paragraph
)
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);
207 return RTFValue::Pointer_t();
210 /// Is it problematic to deduplicate this SPRM?
211 static bool isSPRMDeduplicateDenylist(Id nId
, RTFSprms
* pDirect
)
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
:
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
);
249 /// Should this SPRM be removed if all its children are removed?
250 static bool isSPRMChildrenExpected(Id 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_*.
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
));
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())
293 RTFValue::Pointer_t(pValue
->CloneWithSprms(attributes
, sprms
)));
298 // not found - try to override style with default
299 RTFValue::Pointer_t
const pDefault(getDefaultSPRM(rSprm
.first
, nStyleType
));
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
)
326 RTFValue::Pointer_t pLevel
= rPair
.second
->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl
);
330 if (pLevel
->getInt() != nLevel
)
336 return RTFValue::Pointer_t();
339 void RTFSprms::deduplicateList(const std::map
<int, int>& rInvalidListLevelFirstIndents
)
342 RTFValue::Pointer_t pLevelId
343 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
345 nLevel
= pLevelId
->getInt();
347 auto it
= rInvalidListLevelFirstIndents
.find(nLevel
);
348 if (it
== rInvalidListLevelFirstIndents
.end())
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
)
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
)
367 RTFValue::Pointer_t pLevelId
368 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
370 nLevel
= pLevelId
->getInt();
372 RTFValue::Pointer_t pLevel
= getListLevel(pAbstract
, nLevel
);
376 RTFValue::Pointer_t pLevelInd
= pLevel
->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind
);
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));
398 RTFSprms
RTFSprms::cloneAndDeduplicate(RTFSprms
& rReference
, Id
const nStyleType
,
399 bool const bImplicitPPr
, RTFSprms
* pDirect
) const
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
);
418 cloneAndDeduplicateSprm(rSprm
, ret
, nStyleType
, pDirect
);
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
)
438 std::make_pair(rSprm
.first
, RTFValue::Pointer_t(rSprm
.second
->Clone())));
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: */