tdf#170236 sw: import comment anchored inside a math formula

A Word comment whose w:commentReference sits inside <m:oMath> was lost on
DOCX import: OOXMLFastContextHandlerLinear/Math buffers every child of the
formula and never routes commentReference to handleComment, so comments.xml
was never resolved and no annotation was created.

Intercept w:commentReference in OOXMLFastContextHandlerLinear: collect its
id and resolve it via the existing resolveComment() path once the formula
object has been emitted (in OOXMLFastContextHandlerMath::process). The tag
is never buffered into the formula, so the Starmath buffer stays balanced
and formula import is unaffected.

Signed-off-by: Dmitry Kulikov <dkul@altlinux.org>
Change-Id: I9b0dccc79d5a7da9294b18f0cb58776a2bd8b553
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207888
Tested-by: Jenkins
Reviewed-by: Dan Williams <dcbw@libreoffice.org>
This commit is contained in:
Dmitry Kulikov
2026-10-08 02:37:31 +02:00
committed by Dan Williams
parent f2531aab9b
commit 23b745e20d
4 changed files with 44 additions and 2 deletions
Binary file not shown.
+25
View File
@@ -1960,6 +1960,31 @@ CPPUNIT_TEST_FIXTURE(Test, testInlineFormulaTextMode)
testFitsOnOnePage();
}
CPPUNIT_TEST_FIXTURE(Test, testTdf170236CommentInFormula)
{
// tdf#170236: a w:commentReference inside an m:oMath formula was dropped on import
createSwDoc("tdf170236.docx");
uno::Reference<text::XTextFieldsSupplier> xTextFieldsSupplier(mxComponent, uno::UNO_QUERY);
uno::Reference<container::XEnumerationAccess> xFieldsAccess(xTextFieldsSupplier->getTextFields());
uno::Reference<container::XEnumeration> xFields(xFieldsAccess->createEnumeration());
bool bFoundAnnotation = false;
while (xFields->hasMoreElements())
{
uno::Reference<lang::XServiceInfo> xServiceInfo(xFields->nextElement(), uno::UNO_QUERY);
if (xServiceInfo->supportsService(u"com.sun.star.text.textfield.Annotation"_ustr))
{
bFoundAnnotation = true;
uno::Reference<beans::XPropertySet> xPropertySet(xServiceInfo, uno::UNO_QUERY);
OUString aContent;
xPropertySet->getPropertyValue(u"Content"_ustr) >>= aContent;
CPPUNIT_ASSERT_EQUAL(u"Hello World!"_ustr, aContent);
break;
}
}
CPPUNIT_ASSERT_MESSAGE("No annotation found in document", bFoundAnnotation);
}
// tests should only be added to ooxmlIMPORT *if* they fail round-tripping in ooxmlEXPORT
} // end of anonymous namespace
@@ -2492,9 +2492,18 @@ void OOXMLFastContextHandlerLinear::lcl_endFastElement(Token_t Element)
}
uno::Reference< xml::sax::XFastContextHandler >
OOXMLFastContextHandlerLinear::lcl_createFastChildContext(Token_t,
const uno::Reference< xml::sax::XFastAttributeList >&)
OOXMLFastContextHandlerLinear::lcl_createFastChildContext(Token_t Element,
const uno::Reference< xml::sax::XFastAttributeList >& Attribs)
{
// tdf#170236: buffering a w:commentReference would feed it to Starmath, which
// drops it; capture the id instead and resolve it after the formula (process()).
if (Element == W_TOKEN(commentReference))
{
if (Attribs && Attribs->hasAttribute(W_TOKEN(id)))
m_aCommentIds.push_back(Attribs->getValue(W_TOKEN(id)).toInt32());
return nullptr;
}
uno::Reference< xml::sax::XFastContextHandler > xContextHandler;
xContextHandler.set( this );
return xContextHandler;
@@ -2556,6 +2565,10 @@ void OOXMLFastContextHandlerMath::process()
else
pProps->add(NS_ooxml::LN_starmath, pVal, OOXMLProperty::ATTRIBUTE);
mpStream->props( pProps.get() );
// tdf#170236: resolve now, so the anchor lands right after the emitted formula
for (sal_Int32 nCommentId : m_aCommentIds)
resolveComment(nCommentId);
}
OOXMLFastContextHandlerCommentEx::OOXMLFastContextHandlerCommentEx(
@@ -20,6 +20,7 @@
#pragma once
#include <set>
#include <vector>
#include <cppuhelper/implbase.hxx>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/xml/sax/XFastContextHandler.hpp>
@@ -596,6 +597,9 @@ protected:
// should be private, but not much point in making deep copies of it
oox::formulaimport::XmlStreamBuilder m_buffer;
// tdf#170236: comment ids referenced inside the formula, resolved in Math::process()
std::vector<sal_Int32> m_aCommentIds;
private:
int m_depthCount;
};