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"
16 namespace writerfilter
20 RTFSprm::RTFSprm(Id nKeyword
, RTFValue::Pointer_t
& pValue
)
21 : m_nKeyword(nKeyword
)
26 sal_uInt32
RTFSprm::getId() const { return m_nKeyword
; }
28 Value::Pointer_t
RTFSprm::getValue() { return Value::Pointer_t(m_pValue
->Clone()); }
30 writerfilter::Reference
<Properties
>::Pointer_t
RTFSprm::getProps()
32 return m_pValue
->getProperties();
35 #ifdef DEBUG_WRITERFILTER
36 std::string
RTFSprm::getName() const { return "RTFSprm"; }
39 #ifdef DEBUG_WRITERFILTER
40 std::string
RTFSprm::toString() const
42 OStringBuffer
aBuf("RTFSprm");
46 sResult
= (*QNameToString::Instance())(m_nKeyword
);
49 if (sResult
.length() == 0)
50 aBuf
.append(sal_Int32(m_nKeyword
));
52 aBuf
.append(sResult
.c_str());
54 aBuf
.append(m_pValue
->toString().c_str());
57 return aBuf
.makeStringAndClear().getStr();
61 RTFValue::Pointer_t
RTFSprms::find(Id nKeyword
, bool bFirst
, bool bForWrite
)
63 RTFValue::Pointer_t pValue
;
66 ensureCopyBeforeWrite();
68 for (auto& rSprm
: *m_pSprms
)
69 if (rSprm
.first
== nKeyword
)
74 pValue
= rSprm
.second
;
79 void RTFSprms::set(Id nKeyword
, const RTFValue::Pointer_t
& pValue
, RTFOverwrite eOverwrite
)
81 ensureCopyBeforeWrite();
83 if (eOverwrite
== RTFOverwrite::YES_PREPEND
)
85 auto it
= std::remove_if(
86 m_pSprms
->begin(), m_pSprms
->end(),
87 [nKeyword
](const RTFSprms::Entry_t
& rSprm
) { return rSprm
.first
== nKeyword
; });
88 m_pSprms
->erase(it
, m_pSprms
->end());
89 m_pSprms
->insert(m_pSprms
->begin(), std::make_pair(nKeyword
, pValue
));
94 if (eOverwrite
== RTFOverwrite::YES
|| eOverwrite
== RTFOverwrite::NO_IGNORE
)
96 for (auto& rSprm
: *m_pSprms
)
97 if (rSprm
.first
== nKeyword
)
99 if (eOverwrite
== RTFOverwrite::YES
)
101 rSprm
.second
= pValue
;
109 if (eOverwrite
== RTFOverwrite::NO_APPEND
|| !bFound
)
110 m_pSprms
->push_back(std::make_pair(nKeyword
, pValue
));
113 bool RTFSprms::erase(Id nKeyword
)
115 ensureCopyBeforeWrite();
117 auto i
= std::find_if(
118 m_pSprms
->begin(), m_pSprms
->end(),
119 [&nKeyword
](RTFSprmsImpl::value_type
& rEntry
) { return rEntry
.first
== nKeyword
; });
120 if (i
!= m_pSprms
->end())
128 void RTFSprms::eraseLast(Id nKeyword
)
130 ensureCopyBeforeWrite();
132 auto i
= std::find_if(
133 m_pSprms
->rbegin(), m_pSprms
->rend(),
134 [&nKeyword
](RTFSprmsImpl::value_type
& rEntry
) { return rEntry
.first
== nKeyword
; });
135 if (i
!= m_pSprms
->rend())
136 m_pSprms
->erase(std::next(i
).base());
139 static RTFValue::Pointer_t
getDefaultSPRM(Id
const id
, Id nStyleType
)
141 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_character
)
145 case NS_ooxml::LN_EG_RPrBase_b
:
146 return new RTFValue(0);
152 if (!nStyleType
|| nStyleType
== NS_ooxml::LN_Value_ST_StyleType_paragraph
)
156 case NS_ooxml::LN_CT_Spacing_before
:
157 case NS_ooxml::LN_CT_Spacing_after
:
158 case NS_ooxml::LN_CT_Ind_left
:
159 case NS_ooxml::LN_CT_Ind_right
:
160 case NS_ooxml::LN_CT_Ind_firstLine
:
161 return new RTFValue(0);
163 case NS_ooxml::LN_CT_Spacing_lineRule
:
164 return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto
);
165 case NS_ooxml::LN_CT_Spacing_line
:
166 // presumably this means 100%, cf. static const int nSingleLineSpacing = 240;
167 return new RTFValue(240);
174 return RTFValue::Pointer_t();
177 /// Is it problematic to deduplicate this SPRM?
178 static bool isSPRMDeduplicateBlacklist(Id nId
)
182 // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper,
183 // deduplication is explicitly not wanted for these tokens.
184 case NS_ooxml::LN_CT_TabStop_val
:
185 case NS_ooxml::LN_CT_TabStop_leader
:
186 case NS_ooxml::LN_CT_TabStop_pos
:
187 // \htmautsp arrives after the style table, so only the non-style value is
188 // correct, keep these.
189 case NS_ooxml::LN_CT_Spacing_beforeAutospacing
:
190 case NS_ooxml::LN_CT_Spacing_afterAutospacing
:
198 /// Should this SPRM be removed if all its children is removed?
199 static bool isSPRMChildrenExpected(Id nId
)
203 case NS_ooxml::LN_CT_PBdr_top
:
204 case NS_ooxml::LN_CT_PBdr_left
:
205 case NS_ooxml::LN_CT_PBdr_bottom
:
206 case NS_ooxml::LN_CT_PBdr_right
:
207 // Expected children are NS_ooxml::LN_CT_Border_*.
209 case NS_ooxml::LN_CT_PrBase_shd
:
210 // Expected children are NS_ooxml::LN_CT_Shd_*.
212 case NS_ooxml::LN_CT_PPrBase_ind
:
213 // Expected children are NS_ooxml::LN_CT_Ind_*.
221 /// Does the clone / deduplication of a single sprm.
222 static void cloneAndDeduplicateSprm(std::pair
<Id
, RTFValue::Pointer_t
> const& rSprm
, RTFSprms
& ret
,
225 RTFValue::Pointer_t
const pValue(ret
.find(rSprm
.first
));
228 if (rSprm
.second
->equals(*pValue
))
230 if (!isSPRMDeduplicateBlacklist(rSprm
.first
))
231 ret
.erase(rSprm
.first
); // duplicate to style
233 else if (!rSprm
.second
->getSprms().empty() || !rSprm
.second
->getAttributes().empty())
235 RTFSprms
const sprms(
236 pValue
->getSprms().cloneAndDeduplicate(rSprm
.second
->getSprms(), nStyleType
));
237 RTFSprms
const attributes(pValue
->getAttributes().cloneAndDeduplicate(
238 rSprm
.second
->getAttributes(), nStyleType
));
239 // Don't copy the sprm in case we expect it to have children but it doesn't have some.
240 if (!isSPRMChildrenExpected(rSprm
.first
) || !sprms
.empty() || !attributes
.empty())
242 RTFValue::Pointer_t(pValue
->CloneWithSprms(attributes
, sprms
)));
247 // not found - try to override style with default
248 RTFValue::Pointer_t
const pDefault(getDefaultSPRM(rSprm
.first
, nStyleType
));
251 ret
.set(rSprm
.first
, pDefault
);
253 else if (!rSprm
.second
->getSprms().empty() || !rSprm
.second
->getAttributes().empty())
255 RTFSprms
const sprms(
256 RTFSprms().cloneAndDeduplicate(rSprm
.second
->getSprms(), nStyleType
));
257 RTFSprms
const attributes(
258 RTFSprms().cloneAndDeduplicate(rSprm
.second
->getAttributes(), nStyleType
));
259 if (!sprms
.empty() || !attributes
.empty())
261 ret
.set(rSprm
.first
, new RTFValue(attributes
, sprms
));
267 /// Extracts the list level matching nLevel from pAbstract.
268 static RTFValue::Pointer_t
getListLevel(const RTFValue::Pointer_t
& pAbstract
, int nLevel
)
270 for (const auto& rPair
: pAbstract
->getSprms())
272 if (rPair
.first
!= NS_ooxml::LN_CT_AbstractNum_lvl
)
275 RTFValue::Pointer_t pLevel
= rPair
.second
->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl
);
279 if (pLevel
->getInt() != nLevel
)
285 return RTFValue::Pointer_t();
288 void RTFSprms::deduplicateList(const std::map
<int, int>& rInvalidListLevelFirstIndents
)
291 RTFValue::Pointer_t pLevelId
292 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
294 nLevel
= pLevelId
->getInt();
296 auto it
= rInvalidListLevelFirstIndents
.find(nLevel
);
297 if (it
== rInvalidListLevelFirstIndents
.end())
300 int nListValue
= it
->second
;
302 RTFValue::Pointer_t pParagraphValue
303 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, NS_ooxml::LN_CT_Ind_firstLine
);
304 if (!pParagraphValue
)
307 int nParagraphValue
= pParagraphValue
->getInt();
309 if (nParagraphValue
== nListValue
)
310 eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, NS_ooxml::LN_CT_Ind_firstLine
);
313 void RTFSprms::duplicateList(const RTFValue::Pointer_t
& pAbstract
)
316 RTFValue::Pointer_t pLevelId
317 = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr
, NS_ooxml::LN_CT_NumPr_ilvl
);
319 nLevel
= pLevelId
->getInt();
321 RTFValue::Pointer_t pLevel
= getListLevel(pAbstract
, nLevel
);
325 RTFValue::Pointer_t pLevelInd
= pLevel
->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind
);
329 for (const auto& rListLevelPair
: pLevelInd
->getAttributes())
331 switch (rListLevelPair
.first
)
333 case NS_ooxml::LN_CT_Ind_left
:
334 case NS_ooxml::LN_CT_Ind_right
:
335 case NS_ooxml::LN_CT_Ind_firstLine
:
336 RTFValue::Pointer_t pParagraphValue
337 = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, rListLevelPair
.first
);
338 if (!pParagraphValue
)
339 putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind
, rListLevelPair
.first
,
340 getDefaultSPRM(rListLevelPair
.first
, 0));
347 RTFSprms
RTFSprms::cloneAndDeduplicate(RTFSprms
& rReference
, Id
const nStyleType
,
348 bool const bImplicitPPr
) const
351 ret
.ensureCopyBeforeWrite();
353 // Note: apparently some attributes are set with OVERWRITE_NO_APPEND;
354 // it is probably a bad idea to mess with those in any way here?
355 for (auto& rSprm
: rReference
)
357 // Paragraph formatting sprms are directly contained in case of
358 // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of
359 // styles. So handle those children directly, to avoid unexpected
360 // addition of direct formatting sprms at the paragraph level.
361 if (bImplicitPPr
&& rSprm
.first
== NS_ooxml::LN_CT_Style_pPr
)
363 for (auto& i
: rSprm
.second
->getSprms())
364 cloneAndDeduplicateSprm(i
, ret
, nStyleType
);
367 cloneAndDeduplicateSprm(rSprm
, ret
, nStyleType
);
372 bool RTFSprms::equals(RTFValue
& rOther
)
374 for (auto& rSprm
: *m_pSprms
)
375 if (!rSprm
.second
->equals(rOther
))
380 void RTFSprms::ensureCopyBeforeWrite()
382 if (m_pSprms
->GetRefCount() > 1)
384 tools::SvRef
<RTFSprmsImpl
> pClone(new RTFSprmsImpl
);
385 for (auto& rSprm
: *m_pSprms
)
387 std::make_pair(rSprm
.first
, RTFValue::Pointer_t(rSprm
.second
->Clone())));
393 : m_pSprms(new RTFSprmsImpl
)
397 RTFSprms::~RTFSprms() = default;
399 void RTFSprms::clear()
401 if (m_pSprms
->GetRefCount() == 1)
402 return m_pSprms
->clear();
404 m_pSprms
= tools::SvRef
<RTFSprmsImpl
>(new RTFSprmsImpl
);
407 } // namespace rtftok
408 } // namespace writerfilter
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */