[Libreoffice-commits] core.git: Branch 'private/swe/libreoffice-5-2+backports' - drawinglayer/source include/drawinglayer include/svx svx/source

Armin Le Grand Armin.Le.Grand at cib.de
Wed Aug 16 14:36:39 UTC 2017


 drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx  |   23 ++
 drawinglayer/source/processor2d/hittestprocessor2d.cxx        |    9 
 drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx    |   12 -
 include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx |   10 -
 include/drawinglayer/processor2d/hittestprocessor2d.hxx       |   16 +
 include/svx/sdrhittesthelper.hxx                              |    9 
 svx/source/svdraw/sdrhittesthelper.cxx                        |   22 +-
 svx/source/svdraw/svdotextdecomposition.cxx                   |   13 +
 svx/source/svdraw/svdview.cxx                                 |   92 ++++++----
 9 files changed, 152 insertions(+), 54 deletions(-)

New commits:
commit e4de923e0e771e40414d6b3b9b77389b457aa562
Author: Armin Le Grand <Armin.Le.Grand at cib.de>
Date:   Mon Jul 31 14:46:03 2017 +0200

    Corrected HitTest for layouted text
    
    For text layouted using EditEngine the HitTest in SVX is
    identifying Field like URLs. Thus ist is better to use the
    anyways more precise primitives for HitTest (rotation/shear/
    mirror, ...). This was necessary since the former mechanism
    which used a combination of primitive-beased HitTest and then
    using an Outliner to get the position/content of the Field
    landed on different positions e.g. when the layout needed to
    use multiple lines for the contained URL, but there could
    be more cases found.
    Adapted the text decompositon, the primitive HitTest and
    the TextHirearchyFieldPrimitive2D accordingly.
    
    Change-Id: Ice559e20d02547fdcfcf9783e7cc5481706aab03
    Reviewed-on: https://gerrit.libreoffice.org/40591
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Armin Le Grand <Armin.Le.Grand at cib.de>

diff --git a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
index 86883e43b6cb..45fa8531bee3 100644
--- a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
@@ -95,11 +95,28 @@ namespace drawinglayer
         TextHierarchyFieldPrimitive2D::TextHierarchyFieldPrimitive2D(
             const Primitive2DContainer& rChildren,
             const FieldType& rFieldType,
-            const OUString& rString)
+            const std::vector< std::pair< OUString, OUString>>* pNameValue)
         :   GroupPrimitive2D(rChildren),
             meType(rFieldType),
-            maString(rString)
+            meNameValue()
         {
+            if (nullptr != pNameValue)
+            {
+                meNameValue = *pNameValue;
+            }
+        }
+
+        OUString TextHierarchyFieldPrimitive2D::getValue(const OUString& rName) const
+        {
+            for (const std::pair< OUString, OUString >& candidate : meNameValue)
+            {
+                if (candidate.first.equals(rName))
+                {
+                    return candidate.second;
+                }
+            }
+
+            return OUString();
         }
 
         bool TextHierarchyFieldPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
@@ -109,7 +126,7 @@ namespace drawinglayer
                 const TextHierarchyFieldPrimitive2D& rCompare = static_cast<const TextHierarchyFieldPrimitive2D&>(rPrimitive);
 
                 return (getType() == rCompare.getType()
-                    &&  getString() == rCompare.getString());
+                    && getNameValue() == rCompare.getNameValue());
             }
 
             return false;
diff --git a/drawinglayer/source/processor2d/hittestprocessor2d.cxx b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
index 4853a2838aff..ce9ffacd8492 100644
--- a/drawinglayer/source/processor2d/hittestprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/hittestprocessor2d.cxx
@@ -45,6 +45,8 @@ namespace drawinglayer
         :   BaseProcessor2D(rViewInformation),
             maDiscreteHitPosition(),
             mfDiscreteHitTolerance(0.0),
+            maHitStack(),
+            mbCollectHitStack(false),
             mbHit(false),
             mbHitToleranceUsed(false),
             mbUseInvisiblePrimitiveContent(true),
@@ -544,6 +546,13 @@ namespace drawinglayer
                     break;
                 }
             }
+
+            if (getHit() && getCollectHitStack())
+            {
+                /// push candidate to HitStack to create it. This only happens when a hit is found and
+                /// creating the HitStack was requested (see collectHitStack)
+                maHitStack.push_back(primitive2d::Primitive2DReference(const_cast< primitive2d::BasePrimitive2D* >(&rCandidate)));
+            }
         }
 
     } // end of namespace processor2d
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 6d069d394ad0..d687edd27011 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -979,6 +979,7 @@ namespace drawinglayer
                     const OString aCommentStringCommon("FIELD_SEQ_BEGIN");
                     const OString aCommentStringPage("FIELD_SEQ_BEGIN;PageField");
                     const OString aCommentStringEnd("FIELD_SEQ_END");
+                    OUString aURL;
 
                     switch(rFieldPrimitive.getType())
                     {
@@ -994,8 +995,13 @@ namespace drawinglayer
                         }
                         case drawinglayer::primitive2d::FIELD_TYPE_URL :
                         {
-                            const OUString& rURL = rFieldPrimitive.getString();
-                            mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast< const sal_uInt8* >(rURL.getStr()), 2 * rURL.getLength()));
+                            aURL = rFieldPrimitive.getValue("URL");
+
+                            if (!aURL.isEmpty())
+                            {
+                                mpMetaFile->AddAction(new MetaCommentAction(aCommentStringCommon, 0, reinterpret_cast<const sal_uInt8*>(aURL.getStr()), 2 * aURL.getLength()));
+                            }
+
                             break;
                         }
                     }
@@ -1016,7 +1022,7 @@ namespace drawinglayer
                             (sal_Int32)ceil(aViewRange.getMaxX()), (sal_Int32)ceil(aViewRange.getMaxY()));
                         vcl::PDFExtOutDevBookmarkEntry aBookmark;
                         aBookmark.nLinkId = mpPDFExtOutDevData->CreateLink(aRectLogic);
-                        aBookmark.aBookmark = rFieldPrimitive.getString();
+                        aBookmark.aBookmark = aURL;
                         std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = mpPDFExtOutDevData->GetBookmarks();
                         rBookmarks.push_back( aBookmark );
                     }
diff --git a/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx b/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
index 093ccce65fa3..5234081584a3 100644
--- a/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
@@ -148,19 +148,23 @@ namespace drawinglayer
         class DRAWINGLAYER_DLLPUBLIC TextHierarchyFieldPrimitive2D : public GroupPrimitive2D
         {
         private:
+            /// field type definition
             FieldType                               meType;
-            OUString                           maString;
+
+            /// field data as name/value pairs (dependent of field type definition)
+            std::vector< std::pair< OUString, OUString>>    meNameValue;
 
         public:
             /// constructor
             TextHierarchyFieldPrimitive2D(
                 const Primitive2DContainer& rChildren,
                 const FieldType& rFieldType,
-                const OUString& rString);
+                const std::vector< std::pair< OUString, OUString>>* pNameValue = nullptr);
 
             /// data read access
             FieldType getType() const { return meType; }
-            const OUString& getString() const { return maString; }
+            const std::vector< std::pair< OUString, OUString>>& getNameValue() const { return meNameValue; }
+            OUString getValue(const OUString& rName) const;
 
             /// compare operator
             virtual bool operator==(const BasePrimitive2D& rPrimitive) const override;
diff --git a/include/drawinglayer/processor2d/hittestprocessor2d.hxx b/include/drawinglayer/processor2d/hittestprocessor2d.hxx
index 9f78f44b9f90..9c285cb1997b 100644
--- a/include/drawinglayer/processor2d/hittestprocessor2d.hxx
+++ b/include/drawinglayer/processor2d/hittestprocessor2d.hxx
@@ -46,7 +46,13 @@ namespace drawinglayer
             /// discrete HitTolerance
             double                      mfDiscreteHitTolerance;
 
-            /// bitfield
+            /// stack of HitPrimitives, taken care of during HitTest run
+            primitive2d::Primitive2DContainer        maHitStack;
+
+            /// flag if HitStack shall be collected as part of the result, default is false
+            bool                        mbCollectHitStack : 1;
+
+            /// Boolean to flag if a hit was found. If yes, fast exit is taken
             bool                        mbHit : 1;
             bool                        mbHitToleranceUsed : 1;
 
@@ -79,9 +85,17 @@ namespace drawinglayer
                 bool bHitTextOnly);
             virtual ~HitTestProcessor2D();
 
+            /// switch on collecting primitives for a found hit on maHitStack, default is off
+            void collectHitStack(bool bCollect) { mbCollectHitStack = bCollect; }
+
+            /// get HitStack of primitives, first is the one that created the hit, last is the
+            /// top-most
+            const primitive2d::Primitive2DContainer& getHitStack() const { return maHitStack; }
+
             /// data read access
             const basegfx::B2DPoint& getDiscreteHitPosition() const { return maDiscreteHitPosition; }
             double getDiscreteHitTolerance() const { return mfDiscreteHitTolerance; }
+            bool getCollectHitStack() const { return mbCollectHitStack; }
             bool getHit() const { return mbHit; }
             bool getUseInvisiblePrimitiveContent() const { return mbUseInvisiblePrimitiveContent;}
             bool getHitTextOnly() const { return mbHitTextOnly; }
diff --git a/include/svx/sdrhittesthelper.hxx b/include/svx/sdrhittesthelper.hxx
index 2943c42a22c9..b70fda24a852 100644
--- a/include/svx/sdrhittesthelper.hxx
+++ b/include/svx/sdrhittesthelper.hxx
@@ -32,6 +32,7 @@ class SetOfByte;
 class SdrObjList;
 namespace sdr { namespace contact { class ViewObjectContact; }}
 namespace basegfx { class B2DPoint; }
+namespace drawinglayer { namespace primitive2d { class Primitive2DContainer; }}
 
 
 // Wrappers for classic Sdr* Mode/View classes
@@ -42,7 +43,9 @@ SVX_DLLPUBLIC SdrObject* SdrObjectPrimitiveHit(
     sal_uInt16 nTol,
     const SdrPageView& rSdrPageView,
     const SetOfByte* pVisiLayer,
-    bool bTextOnly);
+    bool bTextOnly,
+    /// allow getting back an evtl. resulting primitive stack which lead to a hit
+    drawinglayer::primitive2d::Primitive2DContainer* pHitContainer = nullptr);
 
 SVX_DLLPUBLIC SdrObject* SdrObjListPrimitiveHit(
     const SdrObjList& rList,
@@ -59,7 +62,9 @@ SVX_DLLPUBLIC bool ViewObjectContactPrimitiveHit(
     const sdr::contact::ViewObjectContact& rVOC,
     const basegfx::B2DPoint& rHitPosition,
     double fLogicHitTolerance,
-    bool bTextOnly);
+    bool bTextOnly,
+    /// allow to get back the stack of primitives that lead to the hit
+    drawinglayer::primitive2d::Primitive2DContainer* pHitContainer = nullptr);
 
 
 #endif // INCLUDED_SVX_SDRHITTESTHELPER_HXX
diff --git a/svx/source/svdraw/sdrhittesthelper.cxx b/svx/source/svdraw/sdrhittesthelper.cxx
index ec3a4d21dba0..ff8dfbca6f0c 100644
--- a/svx/source/svdraw/sdrhittesthelper.cxx
+++ b/svx/source/svdraw/sdrhittesthelper.cxx
@@ -39,7 +39,8 @@ SdrObject* SdrObjectPrimitiveHit(
     sal_uInt16 nTol,
     const SdrPageView& rSdrPageView,
     const SetOfByte* pVisiLayer,
-    bool bTextOnly)
+    bool bTextOnly,
+    drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
 {
     SdrObject* pResult = nullptr;
 
@@ -77,7 +78,7 @@ SdrObject* SdrObjectPrimitiveHit(
                     const sdr::contact::ViewObjectContact& rVOC = rObject.GetViewContact().GetViewObjectContact(
                         rSdrPageView.GetPageWindow(0)->GetObjectContact());
 
-                    if(ViewObjectContactPrimitiveHit(rVOC, aHitPosition, fLogicTolerance, bTextOnly))
+                    if(ViewObjectContactPrimitiveHit(rVOC, aHitPosition, fLogicTolerance, bTextOnly, pHitContainer))
                     {
                           pResult = const_cast< SdrObject* >(&rObject);
                     }
@@ -117,7 +118,8 @@ bool ViewObjectContactPrimitiveHit(
     const sdr::contact::ViewObjectContact& rVOC,
     const basegfx::B2DPoint& rHitPosition,
     double fLogicHitTolerance,
-    bool bTextOnly)
+    bool bTextOnly,
+    drawinglayer::primitive2d::Primitive2DContainer* pHitContainer)
 {
     basegfx::B2DRange aObjectRange(rVOC.getObjectRange());
 
@@ -146,11 +148,23 @@ bool ViewObjectContactPrimitiveHit(
                     fLogicHitTolerance,
                     bTextOnly);
 
+                // ask for HitStack
+                aHitTestProcessor2D.collectHitStack(true);
+
                 // feed it with the primitives
                 aHitTestProcessor2D.process(rSequence);
 
                 // deliver result
-                return aHitTestProcessor2D.getHit();
+                if (aHitTestProcessor2D.getHit())
+                {
+                    if (pHitContainer)
+                    {
+                        // fetch HitStack primitives if requested
+                        *pHitContainer = aHitTestProcessor2D.getHitStack();
+                    }
+
+                    return true;
+                }
             }
         }
     }
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx
index 9b6a8c9eaa3a..3de59d7deed0 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -479,15 +479,22 @@ namespace
 
             if(pURLField)
             {
-                pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
+                // extended this to hold more of the contents of the original
+                // SvxURLField since that stuff is still used in HitTest and e.g. Calc
+                std::vector< std::pair< OUString, OUString>> meValues;
+                meValues.push_back(std::pair< OUString, OUString>("URL", pURLField->GetURL()));
+                meValues.push_back(std::pair< OUString, OUString>("Representation", pURLField->GetRepresentation()));
+                meValues.push_back(std::pair< OUString, OUString>("TargetFrame", pURLField->GetTargetFrame()));
+                meValues.push_back(std::pair< OUString, OUString>("SvxURLFormat", OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat()))));
+                pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues);
             }
             else if(pPageField)
             {
-                pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, "");
+                pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE);
             }
             else
             {
-                pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, "");
+                pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON);
             }
         }
 
diff --git a/svx/source/svdraw/svdview.cxx b/svx/source/svdraw/svdview.cxx
index bc0475cc07a9..0640149ea831 100644
--- a/svx/source/svdraw/svdview.cxx
+++ b/svx/source/svdraw/svdview.cxx
@@ -49,6 +49,7 @@
 #include <svx/sdrhittesthelper.hxx>
 #include <svx/sdr/contact/viewcontact.hxx>
 #include <drawinglayer/processor2d/contourextractor2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
 
 
 SdrViewEvent::SdrViewEvent()
@@ -448,45 +449,66 @@ SdrHitKind SdrView::PickAnything(const Point& rLogicPos, SdrViewEvent& rVEvt) co
         SdrTextObj* pTextObj=dynamic_cast<SdrTextObj*>( pHitObj );
         if (pTextObj!=nullptr && pTextObj->HasText())
         {
-            bool bTEHit(pPV &&
-                SdrObjectPrimitiveHit(*pTextObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true));
-
-            if (bTEHit)
+            // use the primitive-based HitTest which is more accurate anyways. It
+            // will correctly handle rotated/mirrored/sheared/scaled text and can
+            // now return a HitContainer containing the primitive hierarchy of the
+            // primitive that triggered the hit. The first entry is that primitive,
+            // the others are the full stack of primitives leading to that one which
+            // includes grouping primitives (like TextHierarchyPrimitives we deed here)
+            // but also all decomposed ones which lead to the creation of that primitive
+            drawinglayer::primitive2d::Primitive2DContainer aHitContainer;
+            const bool bTEHit(pPV && SdrObjectPrimitiveHit(*pTextObj, aLocalLogicPosition, 0, *pPV, &pPV->GetVisibleLayers(), true, &aHitContainer));
+
+            if (bTEHit && !aHitContainer.empty())
             {
-                Rectangle aTextRect;
-                Rectangle aAnchor;
-                SdrOutliner* pOutliner = &pTextObj->ImpGetDrawOutliner();
-                if( pTextObj->GetModel() )
-                    pOutliner = &pTextObj->GetModel()->GetHitTestOutliner();
-
-                pTextObj->TakeTextRect( *pOutliner, aTextRect, false, &aAnchor, false );
-
-                // #i73628# Use a text-relative position for hit test in hit test outliner
-                Point aTemporaryTextRelativePosition(aLocalLogicPosition - aTextRect.TopLeft());
-
-                // account for FitToSize
-                bool bFitToSize(pTextObj->IsFitToSize());
-                if (bFitToSize) {
-                    Fraction aX(aTextRect.GetWidth()-1,aAnchor.GetWidth()-1);
-                    Fraction aY(aTextRect.GetHeight()-1,aAnchor.GetHeight()-1);
-                    ResizePoint(aTemporaryTextRelativePosition,Point(),aX,aY);
+                // search for TextHierarchyFieldPrimitive2D which contains the needed information
+                // about a possible URLField
+                const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D* pTextHierarchyFieldPrimitive2D = nullptr;
+
+                for (const drawinglayer::primitive2d::Primitive2DReference& xReference : aHitContainer)
+                {
+                    if (xReference.is())
+                    {
+                        // try to cast to drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D implementation
+                        pTextHierarchyFieldPrimitive2D = dynamic_cast<const drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D*>(xReference.get());
+
+                        if (pTextHierarchyFieldPrimitive2D)
+                        {
+                            break;
+                        }
+                    }
                 }
-                // account for rotation
-                const GeoStat& rGeo=pTextObj->GetGeoStat();
-                if (rGeo.nRotationAngle!=0) RotatePoint(aTemporaryTextRelativePosition,Point(),-rGeo.nSin,rGeo.nCos); // -sin for Unrotate
-                // we currently don't account for ticker text
-                if(mpActualOutDev && mpActualOutDev->GetOutDevType() == OUTDEV_WINDOW)
+
+                if (nullptr != pTextHierarchyFieldPrimitive2D)
                 {
-                    OutlinerView aOLV(pOutliner, const_cast<vcl::Window*>(static_cast<const vcl::Window*>(mpActualOutDev.get())));
-                    const EditView& aEV=aOLV.GetEditView();
-                    const SvxFieldItem* pItem=aEV.GetField(aTemporaryTextRelativePosition);
-                    if (pItem!=nullptr) {
-                        const SvxFieldData* pFld=pItem->GetField();
-                        const SvxURLField* pURL=dynamic_cast<const SvxURLField*>( pFld );
-                        if (pURL!=nullptr) {
-                            eHit=SDRHIT_URLFIELD;
-                            rVEvt.pURLField=pURL;
+                    if (drawinglayer::primitive2d::FieldType::FIELD_TYPE_URL == pTextHierarchyFieldPrimitive2D->getType())
+                    {
+                        // problem with the old code is that a *pointer* to an instance of
+                        // SvxURLField is set in the Event which is per se not good since that
+                        // data comes from a temporary EditEngine's data and could vanish any
+                        // moment. Have to replace for now with a static instance that gets
+                        // filled/initialized from the original data held in the TextHierarchyField-
+                        // Primitive2D (see impTextBreakupHandler::impCheckFieldPrimitive).
+                        // Unfortunately things like 'TargetFrame' are still used in Calc, so this
+                        // can currently not get replaced. For the future the Name/Value vector or
+                        // the TextHierarchyFieldPrimitive2D itself should/will be used for handling
+                        // that data
+                        static SvxURLField aSvxURLField;
+
+                        aSvxURLField.SetURL(pTextHierarchyFieldPrimitive2D->getValue("URL"));
+                        aSvxURLField.SetRepresentation(pTextHierarchyFieldPrimitive2D->getValue("Representation"));
+                        aSvxURLField.SetTargetFrame(pTextHierarchyFieldPrimitive2D->getValue("TargetFrame"));
+                        const OUString aFormat(pTextHierarchyFieldPrimitive2D->getValue("SvxURLFormat"));
+
+                        if (!aFormat.isEmpty())
+                        {
+                            aSvxURLField.SetFormat(static_cast<SvxURLFormat>(aFormat.toInt32()));
                         }
+
+                        // set HitKind and pointer to local static instance in the Event
+                        // to comply to old stuff
+                        eHit = SDRHIT_URLFIELD;
+                        rVEvt.pURLField = &aSvxURLField;
                     }
                 }
             }


More information about the Libreoffice-commits mailing list