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);
 

Reply via email to