offapi/com/sun/star/text/XTextViewTextRangeSupplier.idl | 7 + sw/qa/uibase/uno/uno.cxx | 64 ++++++++++++++++ sw/source/uibase/uno/unotxvw.cxx | 18 ++++ 3 files changed, 88 insertions(+), 1 deletion(-)
New commits: commit 44c23800e3e94143f3737d32232e15169a092eac Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Tue Nov 22 08:40:47 2022 +0100 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Tue Nov 22 12:18:33 2022 +0100 sw, createTextRangeByPixelPosition(): fix crash when the position is an image Using createTextRangeByPixelPosition() with a pixel position that leads to a graphic node resulted in a crash. The direct reason for this is that the makeMark() call in SwXTextRange::SetPositions() returns nullptr in case rPaM points to a graphic node, but later we dereference that result unconditionally. This also uncovers that the XTextRange returned by createTextRangeByPixelPosition() is meant to point to a text node, but a pixel position may be closest to a graphic node. Fix the problem by explicitly checking for graphic nodes; and try to look up the anchor position of such graphics, which will be definitely a text node. In practice this will mean that in case the image's anchor type is as-char, then we'll return a cursor position which will be on the left hand side of the image. (cherry picked from commit 2302ebefb2e25878e8fe1e64d208f265f87d5b9b) Conflicts: sw/qa/uibase/uno/uno.cxx sw/source/uibase/uno/unotxvw.cxx Change-Id: Ief58148247fe3cd4371ed245b4eff5b45ca2aa15 diff --git a/offapi/com/sun/star/text/XTextViewTextRangeSupplier.idl b/offapi/com/sun/star/text/XTextViewTextRangeSupplier.idl index 57adf359f756..e5a8b6913eab 100644 --- a/offapi/com/sun/star/text/XTextViewTextRangeSupplier.idl +++ b/offapi/com/sun/star/text/XTextViewTextRangeSupplier.idl @@ -32,7 +32,12 @@ module com { module sun { module star { module text { */ interface XTextViewTextRangeSupplier: com::sun::star::uno::XInterface { - /** @returns + /** creates the text range of the document model position at a view-dependent pixel position. + + Note that in case the model position is a graphic, then the model position of its anchor is + returned. + + @returns the text range of the document position. */ com::sun::star::text::XTextRange createTextRangeByPixelPosition([in] com::sun::star::awt::Point PixelPosition); diff --git a/sw/qa/uibase/uno/uno.cxx b/sw/qa/uibase/uno/uno.cxx index 23c1829ab7de..8085798d8f4d 100644 --- a/sw/qa/uibase/uno/uno.cxx +++ b/sw/qa/uibase/uno/uno.cxx @@ -10,6 +10,18 @@ #include <swmodeltestbase.hxx> #include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/text/XTextDocument.hpp> +#include <com/sun/star/text/XTextViewTextRangeSupplier.hpp> + +#include <rootfrm.hxx> +#include <sortedobjs.hxx> +#include <anchoredobject.hxx> +#include <frameformats.hxx> +#include <fmtanchr.hxx> +#include <wrtsh.hxx> +#include <edtwin.hxx> +#include <view.hxx> +#include <unotextrange.hxx> /// Covers sw/source/uibase/uno/ fixes. class SwUibaseUnoTest : public SwModelTestBase @@ -31,6 +43,58 @@ CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testLockControllers) mxComponent.clear(); } +CPPUNIT_TEST_FIXTURE(SwUibaseUnoTest, testCreateTextRangeByPixelPositionGraphic) +{ + // Given a document with an as-char image and the center of that image in pixels: + mxComponent = loadFromDesktop("private:factory/swriter", "com.sun.star.text.TextDocument"); + uno::Reference<lang::XMultiServiceFactory> xFactory(mxComponent, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xTextGraphic( + xFactory->createInstance("com.sun.star.text.TextGraphicObject"), uno::UNO_QUERY); + xTextGraphic->setPropertyValue("AnchorType", + uno::Any(text::TextContentAnchorType_AS_CHARACTER)); + xTextGraphic->setPropertyValue("Width", uno::Any(static_cast<sal_Int32>(10000))); + xTextGraphic->setPropertyValue("Height", uno::Any(static_cast<sal_Int32>(10000))); + uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY); + uno::Reference<text::XText> xBodyText = xTextDocument->getText(); + uno::Reference<text::XTextCursor> xCursor(xBodyText->createTextCursor()); + uno::Reference<text::XTextContent> xTextContent(xTextGraphic, uno::UNO_QUERY); + xBodyText->insertTextContent(xCursor, xTextContent, false); + SwXTextDocument* pTextDoc = dynamic_cast<SwXTextDocument*>(mxComponent.get()); + SwDoc* pDoc = pTextDoc->GetDocShell()->GetDoc(); + SwDocShell* pDocShell = pDoc->GetDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + SwRootFrame* pLayout = pWrtShell->GetLayout(); + SwFrame* pPage = pLayout->GetLower(); + SwFrame* pBody = pPage->GetLower(); + SwFrame* pText = pBody->GetLower(); + SwSortedObjs& rDrawObjs = *pText->GetDrawObjs(); + SwAnchoredObject* pAnchored = rDrawObjs[0]; + Point aLogic = pAnchored->GetObjRect().Center(); + SwView* pView = pDocShell->GetView(); + SwEditWin& rEditWin = pView->GetEditWin(); + Point aPixel = rEditWin.LogicToPixel(aLogic); + + // When converting that pixel position to a document model position (text range): + uno::Reference<frame::XModel2> xModel(mxComponent, uno::UNO_QUERY); + uno::Reference<container::XEnumeration> xControllers = xModel->getControllers(); + uno::Reference<text::XTextViewTextRangeSupplier> xController(xControllers->nextElement(), + uno::UNO_QUERY); + awt::Point aPoint(aPixel.getX(), aPixel.getY()); + // Without the accompanying fix in place, this test would have crashed, because an XTextRange + // can't point to a graphic node. + uno::Reference<text::XTextRange> xTextRange + = xController->createTextRangeByPixelPosition(aPoint); + + // Then make sure that the anchor of the image is returned: + const SwFrameFormats& rFormats = *pDoc->GetSpzFrameFormats(); + const SwFrameFormat* pFormat = rFormats[0]; + SwPosition aAnchorPos(*pFormat->GetAnchor().GetContentAnchor()); + auto pTextRange = dynamic_cast<SwXTextRange*>(xTextRange.get()); + SwPaM aPaM(pDoc->GetNodes()); + pTextRange->GetPositions(aPaM); + CPPUNIT_ASSERT_EQUAL(aAnchorPos, *aPaM.GetPoint()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/uibase/uno/unotxvw.cxx b/sw/source/uibase/uno/unotxvw.cxx index 438e648f480c..821ec11fe934 100644 --- a/sw/source/uibase/uno/unotxvw.cxx +++ b/sw/source/uibase/uno/unotxvw.cxx @@ -72,6 +72,7 @@ #include <comphelper/servicehelper.hxx> #include <cppuhelper/supportsservice.hxx> #include <cppuhelper/typeprovider.hxx> +#include <fmtanchr.hxx> using namespace ::com::sun::star; using namespace ::com::sun::star::uno; @@ -528,6 +529,23 @@ SwXTextView::createTextRangeByPixelPosition(const awt::Point& rPixelPosition) SwWrtShell& rSh = m_pView->GetWrtShell(); SwPosition aPosition(*rSh.GetCurrentShellCursor().GetPoint()); rSh.GetLayout()->GetModelPositionForViewPoint(&aPosition, aLogicPoint); + + if (aPosition.nNode.GetNode().IsGrfNode()) + { + // The point is closest to a graphic node, look up its format. + const SwFrameFormat* pGraphicFormat = aPosition.nNode.GetNode().GetFlyFormat(); + if (pGraphicFormat) + { + // Get the anchor of this format. + const SwFormatAnchor& rAnchor = pGraphicFormat->GetAnchor(); + const SwPosition* pAnchor = rAnchor.GetContentAnchor(); + if (pAnchor) + { + aPosition = *pAnchor; + } + } + } + uno::Reference<text::XTextRange> xRet = SwXTextRange::CreateXTextRange(*rSh.GetDoc(), aPosition, /*pMark=*/nullptr);