summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.com>2022-12-20 12:14:16 +0100
committerMiklos Vajna <vmiklos@collabora.com>2022-12-20 12:56:48 +0000
commit1feb1aa08421f9d0934ab65ce94cf6054818c0f3 (patch)
treea49c15d0e4455e2decab78da85a3924ced34d003
parenta179da299a7daaa13f7d6fa3612f82eeb186f0d1 (diff)
sw: fix ODT import of paragraph marker formatting
The DOCX bugdoc had a numbering portion which was not bold, even if all characters in the paragrpah was bold. This was rendered fine, but once it was saved to ODT + reloaded, the numbering portion became bold as well, which is buggy. What happens here is that there is one span that covers the entire paragraph (and is bold) and there is an empty span at paragraph end (which is not bold), so the ODT export is fine. But once we import it back, the first span gets "upgraded" to paragraph-level formatting (because SetAttrMode::NOFORMATATTR is not used when inserting the hints) and the second span is not mapped back to the original RES_PARATR_LIST_AUTOFMT in the text node. Fix the problem by 1) improving SwXTextCursor::setPropertyValue() to work with SetAttrMode::NOFORMATATTR when multiple spans are inserted and by 2) extending SwUnoCursorHelper::SetCursorPropertyValue() to create RES_PARATR_LIST_AUTOFMT for empty spans at paragraph end. This way the original doc model and the one created after ODT export + import is much closer to each other. This builds on top of 6249858a8972aef077e0249bd93cfe8f01bce4d6 (sw: ODT import/export of DOCX's paragraph marker formatting, 2022-12-19), previously the ODT export and import of paragraph marker formatting was completely missing. Change-Id: I139e11217dcbc18744aeeb80638090781aa74933 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/144587 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
-rw-r--r--sw/inc/unoprnms.hxx1
-rw-r--r--sw/inc/unotextcursor.hxx1
-rw-r--r--sw/qa/core/unocore/data/paragraph-marker-formatted-run.docxbin0 -> 13294 bytes
-rw-r--r--sw/qa/core/unocore/unocore.cxx19
-rw-r--r--sw/source/core/unocore/unoobj.cxx81
-rw-r--r--xmloff/source/text/txtparai.cxx17
6 files changed, 116 insertions, 3 deletions
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index ff1cc28303be..17b13d02dc8e 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -531,6 +531,7 @@ inline constexpr OUStringLiteral UNO_NAME_IS_SKIP_HIDDEN_TEXT = u"IsSkipHiddenTe
inline constexpr OUStringLiteral UNO_NAME_IS_SKIP_PROTECTED_TEXT = u"IsSkipProtectedText";
inline constexpr OUStringLiteral UNO_NAME_RESET_PARAGRAPH_LIST_ATTRIBUTES
= u"ResetParagraphListAttributes";
+inline constexpr OUStringLiteral UNO_NAME_NO_FORMAT_ATTR = u"NoFormatAttr";
inline constexpr OUStringLiteral UNO_NAME_DOCUMENT_INDEX_MARKS = u"DocumentIndexMarks";
inline constexpr OUStringLiteral UNO_NAME_FOOTNOTE_IS_COLLECT_AT_TEXT_END
= u"FootnoteIsCollectAtTextEnd";
diff --git a/sw/inc/unotextcursor.hxx b/sw/inc/unotextcursor.hxx
index 922e7581329c..0d3ce0e5e60c 100644
--- a/sw/inc/unotextcursor.hxx
+++ b/sw/inc/unotextcursor.hxx
@@ -76,6 +76,7 @@ private:
const CursorType m_eType;
const css::uno::Reference< css::text::XText > m_xParentText;
sw::UnoCursorPointer m_pUnoCursor;
+ SetAttrMode m_nAttrMode = SetAttrMode::DEFAULT;
SwUnoCursor& GetCursorOrThrow() {
if(!m_pUnoCursor)
diff --git a/sw/qa/core/unocore/data/paragraph-marker-formatted-run.docx b/sw/qa/core/unocore/data/paragraph-marker-formatted-run.docx
new file mode 100644
index 000000000000..c0635c157cc9
--- /dev/null
+++ b/sw/qa/core/unocore/data/paragraph-marker-formatted-run.docx
Binary files differ
diff --git a/sw/qa/core/unocore/unocore.cxx b/sw/qa/core/unocore/unocore.cxx
index 2b0ed9e21f92..0c2da1b0524a 100644
--- a/sw/qa/core/unocore/unocore.cxx
+++ b/sw/qa/core/unocore/unocore.cxx
@@ -883,6 +883,25 @@ CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testParagraphMarkerODFExport)
getXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwFieldPortion", "font-color"));
}
+CPPUNIT_TEST_FIXTURE(SwCoreUnocoreTest, testParagraphMarkerFormattedRun)
+{
+ // Given a document with a bold run and non-bold paragraph marker:
+ createSwDoc("paragraph-marker-formatted-run.docx");
+
+ // When saving that as ODT + reload:
+ reload("writer8", nullptr);
+
+ // Then make sure that the numbering portion is still non-bold, matching Word:
+ xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+ // Without the accompanying fix in place, this test would have failed with:
+ // - Expected: normal
+ // - Actual : bold
+ // i.e. the numbering portion was bold, while its weight should be normal.
+ CPPUNIT_ASSERT_EQUAL(
+ OUString("normal"),
+ getXPath(pXmlDoc, "//SwParaPortion/SwLineLayout/SwFieldPortion", "font-weight"));
+}
+
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 5edb3e4bbd6f..51ef4da39402 100644
--- a/sw/source/core/unocore/unoobj.cxx
+++ b/sw/source/core/unocore/unoobj.cxx
@@ -230,6 +230,57 @@ lcl_setAutoStyle(IStyleAccess & rStyleAccess, const uno::Any & rValue,
rSet.Put(aFormat);
};
+/// Tries to map rValue to RES_PARATR_LIST_AUTOFMT on the current paragraph, returns true on
+/// success.
+static bool lcl_setListAutoStyle(SwPaM& rPam, const uno::Any& rValue, SfxItemSet& rItemSet)
+{
+ // See if this is an empty range at the end of a paragraph.
+ if (rPam.Start()->GetNodeIndex() != rPam.End()->GetNodeIndex())
+ {
+ return false;
+ }
+
+ if (rPam.Start()->GetContentIndex() != rPam.End()->GetContentIndex())
+ {
+ return false;
+ }
+
+ SwTextNode* pTextNode = rPam.GetPointNode().GetTextNode();
+ if (!pTextNode)
+ {
+ return false;
+ }
+
+ if (rPam.Start()->GetContentIndex() != pTextNode->Len())
+ {
+ return false;
+ }
+
+ // Look up the style content based on the name.
+ OUString sStyle;
+ if (!(rValue >>= sStyle))
+ {
+ return false;
+ }
+
+ IStyleAccess& rStyleAccess = rPam.GetDoc().GetIStyleAccess();
+ std::shared_ptr<SfxItemSet> pStyle
+ = rStyleAccess.getByName(sStyle, IStyleAccess::AUTO_STYLE_CHAR);
+ if (!pStyle)
+ {
+ return false;
+ }
+
+ // Set the style on the text node.
+ SwFormatAutoFormat aItem(RES_PARATR_LIST_AUTOFMT);
+ aItem.SetStyleHandle(pStyle);
+ pTextNode->SetAttr(aItem);
+ // Clear the style from the hints array. Without clearing, it would contain some style which
+ // happened to be there previously.
+ rItemSet.ClearItem(RES_TXTATR_AUTOFMT);
+ return true;
+}
+
void
SwUnoCursorHelper::SetTextFormatColl(const uno::Any & rAny, SwPaM & rPaM)
{
@@ -448,6 +499,11 @@ SwUnoCursorHelper::SetCursorPropertyValue(
lcl_setCharStyle(rPam.GetDoc(), rValue, rItemSet);
break;
case RES_TXTATR_AUTOFMT:
+ if (lcl_setListAutoStyle(rPam, rValue, rItemSet))
+ {
+ break;
+ }
+
lcl_setAutoStyle(rPam.GetDoc().GetIStyleAccess(),
rValue, rItemSet, false);
break;
@@ -2016,7 +2072,8 @@ SwUnoCursorHelper::GetPropertyStates(
if(!pEntry)
{
if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
- pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
+ pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
{
pStates[i] = beans::PropertyState_DEFAULT_VALUE;
continue;
@@ -2209,6 +2266,7 @@ SwXTextCursor::getPropertySetInfo()
{
{ UNO_NAME_IS_SKIP_HIDDEN_TEXT, FN_SKIP_HIDDEN_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
{ UNO_NAME_IS_SKIP_PROTECTED_TEXT, FN_SKIP_PROTECTED_TEXT, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
+ { UNO_NAME_NO_FORMAT_ATTR, 0, cppu::UnoType<bool>::get(), PROPERTY_NONE, 0},
};
const uno::Reference< beans::XPropertySetInfo > xInfo =
m_rPropSet.getPropertySetInfo();
@@ -2256,10 +2314,26 @@ SwXTextCursor::setPropertyValue(
pTextNode->ResetAttr(RES_PARATR_LIST_BEGIN, RES_PARATR_LIST_END);
}
}
+ else if (rPropertyName == UNO_NAME_NO_FORMAT_ATTR)
+ {
+ bool bSet(false);
+ if (!(rValue >>= bSet))
+ {
+ throw lang::IllegalArgumentException();
+ }
+ if (bSet)
+ {
+ m_nAttrMode = SetAttrMode::NOFORMATATTR;
+ }
+ else
+ {
+ m_nAttrMode = SetAttrMode::DEFAULT;
+ }
+ }
else
{
SwUnoCursorHelper::SetPropertyValue(rUnoCursor,
- m_rPropSet, rPropertyName, rValue);
+ m_rPropSet, rPropertyName, rValue, m_nAttrMode);
}
}
@@ -2572,7 +2646,8 @@ SwXTextCursor::getPropertyDefaults(
if (!pEntry)
{
if (pNames[i] == UNO_NAME_IS_SKIP_HIDDEN_TEXT ||
- pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT)
+ pNames[i] == UNO_NAME_IS_SKIP_PROTECTED_TEXT ||
+ pNames[i] == UNO_NAME_NO_FORMAT_ATTR)
{
continue;
}
diff --git a/xmloff/source/text/txtparai.cxx b/xmloff/source/text/txtparai.cxx
index 49d4f5abd3c4..9844e2628c89 100644
--- a/xmloff/source/text/txtparai.cxx
+++ b/xmloff/source/text/txtparai.cxx
@@ -1821,6 +1821,19 @@ void XMLParaContext::endFastElement(sal_Int32 )
if (m_xHints)
{
+ bool bSetNoFormatAttr = false;
+ uno::Reference<beans::XPropertySet> xCursorProps(xAttrCursor, uno::UNO_QUERY);
+ if (m_xHints->GetHints().size() > 1)
+ {
+ // We have multiple hints, then make try to ask the cursor to not upgrade our character
+ // attributes to paragraph-level formatting, which would lead to incorrect rendering.
+ uno::Reference<beans::XPropertySetInfo> xCursorPropsInfo = xCursorProps->getPropertySetInfo();
+ bSetNoFormatAttr = xCursorPropsInfo->hasPropertyByName("NoFormatAttr");
+ }
+ if (bSetNoFormatAttr)
+ {
+ xCursorProps->setPropertyValue("NoFormatAttr", uno::Any(true));
+ }
for (const auto & i : m_xHints->GetHints())
{
XMLHint_Impl *const pHint = i.get();
@@ -1960,6 +1973,10 @@ void XMLParaContext::endFastElement(sal_Int32 )
break;
}
}
+ if (bSetNoFormatAttr)
+ {
+ xCursorProps->setPropertyValue("NoFormatAttr", uno::Any(false));
+ }
}
m_xHints.reset();
}