sw/qa/extras/ooxmlexport/data/tdf155945.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport18.cxx | 11 ++++++ sw/source/core/unocore/unoobj.cxx | 48 +++++++++++++++------------ 3 files changed, 38 insertions(+), 21 deletions(-)
New commits: commit 8afe5d3ad14932884097ccdc8e28e35b97d9b259 Author: Mike Kaganski <mike.kagan...@collabora.com> AuthorDate: Wed Jun 21 13:38:14 2023 +0300 Commit: Mike Kaganski <mike.kagan...@collabora.com> CommitDate: Wed Jun 21 23:02:14 2023 +0200 tdf#155945: do not turn another style attributes to direct formatting SwUnoCursorHelper::SetPropertyValues obtains the node's item set using SwUnoCursorHelper::GetCursorAttr, adds new items for the property values, and calls SwUnoCursorHelper::SetCursorAttr to set the updated item set to the node. This puts all the item set data as direct formatting of the node (autostyle). For complex SfxPoolItem, containing several properties, it could result in some properties having incorrect values e.g. in this sequence: 1. SwUnoCursorHelper::SetPropertyValues is called with a property sequence including "ParaStyleName" and "ParaTopMargin", represented by a complex SfxPoolItem (SvxULSpaceItem's nPropUpper). The current node uses another style, which has a non-default bottom margin; new style has 0 as bottom margin. 2. It builds a set for the union of all WhichIds corresponding to all the passed properties, which includes WIDs for paragraph style and for the respective SvxULSpaceItem. 3. It calls SwUnoCursorHelper::GetCursorAttr to fill the set with current values for these WhichIds, getting also values defined in still applied style, including an SvxULSpaceItem with both upper and lower spacing (the bottom margin value of old paragraph is stored there). 4. It sets new value of paragraph style in this item set, and then calls SwUnoCursorHelper::SetCursorAttr with the item set. Now the set contains a new value for paragraph style, *and* an old value of the paragraphs margins. This margins value is set as the paragraph's direct formatting (autostyle). 5. SwUnoCursorHelper::GetCursorAttr is called, updating the item set with new values of paragraph style and margins; the direct-formatted value for the latter is returned, still containing the values previously inherited from old style. 6. New value of top margin is set, modifying only nPropUpper member of SvxULSpaceItem, and keeping nPropLower (value for bottom margin) as it was, i.e. keeps the value incorrectly saved from old style. This happens e.g. on DOCX import. Commit db115bec9254417ef7a3faf687478fe5424ab378 (tdf#78510 sw,cui: split SvxLRSpaceItem for SwTextNode, SwTextFormatColl, 2023-02-24) has incidentally fixed handling of left and right paragraph indents, making their items independent; but top/bottom margins still need to be fixed. This change splits the properties that cause side effects (styles) from the rest; so that properties with side effects are processed first, with item sets containing only one WID for the currently processed property. This both allows to avoid the problem, and simplifies the code. Change-Id: Idd451fa1e53806cdb6b9064dec31e9171d14d5d9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153384 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kagan...@collabora.com> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/153408 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> diff --git a/sw/qa/extras/ooxmlexport/data/tdf155945.docx b/sw/qa/extras/ooxmlexport/data/tdf155945.docx new file mode 100644 index 000000000000..9858ac711790 Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf155945.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx index 00fbebcb3f4e..e686e1c3dd02 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport18.cxx @@ -587,6 +587,17 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf153128) CPPUNIT_ASSERT_LESS(sal_Int32(30), nFirstLineHeight); } +CPPUNIT_TEST_FIXTURE(Test, testTdf155945) +{ + createSwDoc("tdf155945.docx"); + + CPPUNIT_ASSERT_EQUAL(3, getParagraphs()); + // Without a fix in place, this would fail with + // - Expected: 0 + // - Actual : 423 + CPPUNIT_ASSERT_EQUAL(sal_Int32(0), getProperty<sal_Int32>(getParagraph(2), "ParaBottomMargin")); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/unocore/unoobj.cxx b/sw/source/core/unocore/unoobj.cxx index 4315d173796d..146c568ef009 100644 --- a/sw/source/core/unocore/unoobj.cxx +++ b/sw/source/core/unocore/unoobj.cxx @@ -1934,6 +1934,7 @@ void SwUnoCursorHelper::SetPropertyValues( // Build set of attributes we want to fetch WhichRangesContainer aRanges; + std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aSideEffectsEntries; std::vector<std::pair<const SfxItemPropertyMapEntry*, const uno::Any&>> aEntries; aEntries.reserve(aPropertyValues.size()); for (const auto& rPropVal : aPropertyValues) @@ -1954,39 +1955,44 @@ void SwUnoCursorHelper::SetPropertyValues( aPropertyVetoExMsg += "Property is read-only: '" + rPropertyName + "' "; continue; } - aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID); - aEntries.emplace_back(pEntry, rPropVal.Value); + if (propertyCausesSideEffectsInNodes(pEntry->nWID)) + { + aSideEffectsEntries.emplace_back(pEntry, rPropVal.Value); + } + else + { + aRanges = aRanges.MergeRange(pEntry->nWID, pEntry->nWID); + aEntries.emplace_back(pEntry, rPropVal.Value); + } + } + + // Entries with side effects first, using dedicated one-element SfxItemSet for each + for (const auto& [pEntry, rValue] : aSideEffectsEntries) + { + SfxItemSet aItemSet(rDoc.GetAttrPool(), pEntry->nWID, pEntry->nWID); + // we need to get up-to-date item set from nodes + SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet); + // this can set some attributes in nodes' mpAttrSet + if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet)) + rPropSet.setPropertyValue(*pEntry, rValue, aItemSet); + SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/); } if (!aEntries.empty()) { // Fetch, overwrite, and re-set the attributes from the core SfxItemSet aItemSet(rDoc.GetAttrPool(), std::move(aRanges)); + // we need to get up-to-date item set from nodes + SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet); - bool bPreviousPropertyCausesSideEffectsInNodes = false; - for (size_t i = 0; i < aEntries.size(); ++i) + for (const auto& [pEntry, rValue] : aEntries) { - SfxItemPropertyMapEntry const*const pEntry = aEntries[i].first; - bool bPropertyCausesSideEffectsInNodes = - propertyCausesSideEffectsInNodes(pEntry->nWID); - - // we need to get up-to-date item set from nodes - if (i == 0 || bPreviousPropertyCausesSideEffectsInNodes) - { - aItemSet.ClearItem(); - SwUnoCursorHelper::GetCursorAttr(rPaM, aItemSet); - } - - const uno::Any &rValue = aEntries[i].second; // this can set some attributes in nodes' mpAttrSet if (!SwUnoCursorHelper::SetCursorPropertyValue(*pEntry, rValue, rPaM, aItemSet)) rPropSet.setPropertyValue(*pEntry, rValue, aItemSet); - - if (i + 1 == aEntries.size() || bPropertyCausesSideEffectsInNodes) - SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false/*bTableMode*/); - - bPreviousPropertyCausesSideEffectsInNodes = bPropertyCausesSideEffectsInNodes; } + + SwUnoCursorHelper::SetCursorAttr(rPaM, aItemSet, nAttrMode, false /*bTableMode*/); } if (!aUnknownExMsg.isEmpty())