drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx |   12 
 editeng/source/editeng/StripPortionsHelper.cxx             |   35 +
 editeng/source/editeng/editview.cxx                        |   14 
 editeng/source/editeng/impedit.hxx                         |    2 
 editeng/source/editeng/impedit3.cxx                        |  231 ++++++++++---
 editeng/source/outliner/outleeng.cxx                       |    4 
 editeng/source/outliner/outlvw.cxx                         |   10 
 include/editeng/StripPortionsHelper.hxx                    |   24 +
 include/editeng/editview.hxx                               |    2 
 include/editeng/outliner.hxx                               |    5 
 include/svx/svdoutl.hxx                                    |   19 -
 sd/source/ui/view/outlview.cxx                             |   57 +++
 svx/source/svdraw/svdotextpathdecomposition.cxx            |    9 
 svx/source/svdraw/svdoutl.cxx                              |   25 -
 14 files changed, 348 insertions(+), 101 deletions(-)

New commits:
commit 6c8b8020510f8816c40a42e6c3a7fde12012b142
Author:     Armin Le Grand (collabora) <[email protected]>
AuthorDate: Thu Jul 10 13:35:13 2025 +0200
Commit:     Armin Le Grand <[email protected]>
CommitDate: Fri Jul 11 14:35:31 2025 +0200

    StripPortions: full Primitive usage for EditEngine Paint
    
    There were three methods left that use PaintOrStrip in
    a mode to directly draw Text: DrawText_ToRectangle,
    DrawText_ToPosition and DrawText_ToEditView.
    
    I adapted all three to use PaintOrStrip in a Primitive
    creating mode and render using a PrmitiveRenderer.
    
    While StripPortions was already using the Primitive
    creating mode the others needed adaptions ranging from
    handling clipping over rotation to transformations.
    
    I checked these as good as I could, but since errors
    cannot be excluded, I added an ENV_VAR called
      DISBALE_EDITENGINE_ON_PRIMITIVES
    with which that change can be deactivated. Please use
    this by setting it for the office when bugs pop up
    whose bibisect point to this change - if it does not
    happen with that var set, this change is responsible.
    
    Offering this comes with a cost: Currently PaintOrStrip
    *still* offers both modes, but when this change causes
    no big problems I will be enabled to remove all direct
    paint and stuff done extra there. This will then allow
    in the future simpler changes since changes to EditEngine
    output will no longer have to be done in two scopes.
    
    For now this allows all remaining places that paint
    EditEngine content still directly to continue to work.
    This can in the future be extended to get the exact
    same context as seq<Primitives> so offers the path to
    potential re-use (not only paint, you can process and
    transform Primitives in many ways).
    
    A special case is the SD OutlinerView: It uses a set
    callback (PaintingFirstLineHdl) to additionally paint
    a PageNumber and a Graphic in that mode, but I managed
    to replace/extend that to Primitive usage so this is
    also prepared for simplifications in the future. It
    would also be good to change that callback from it's
    current form then.
    
    Change-Id: Idc9233f0ce27c3e4153851f4e45faa24d15af36d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187628
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <[email protected]>

diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index dc2383c29411..95769d43212d 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -1384,6 +1384,7 @@ void 
VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
     // thus do the MetafileAction embedding stuff but just handle recursively.
     static constexpr OString aCommentStringCommon("FIELD_SEQ_BEGIN"_ostr);
     OUString aURL;
+    const bool bIsExportTaggedPDF(mpPDFExtOutDevData && 
mpPDFExtOutDevData->GetIsExportTaggedPDF());
 
     switch (rFieldPrimitive.getType())
     {
@@ -1406,6 +1407,10 @@ void 
VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
                 mpMetaFile->AddAction(new MetaCommentAction(
                     aCommentStringCommon, 0, reinterpret_cast<const 
sal_uInt8*>(aURL.getStr()),
                     2 * aURL.getLength()));
+
+                if (bIsExportTaggedPDF)
+                    
mpPDFExtOutDevData->WrapBeginStructureElement(vcl::pdf::StructElement::Link,
+                                                                  
u"Link"_ustr);
             }
 
             break;
@@ -1436,6 +1441,13 @@ void 
VclMetafileProcessor2D::processTextHierarchyFieldPrimitive2D(
     aBookmark.aBookmark = aURL;
     std::vector<vcl::PDFExtOutDevBookmarkEntry>& rBookmarks = 
mpPDFExtOutDevData->GetBookmarks();
     rBookmarks.push_back(aBookmark);
+
+    if (bIsExportTaggedPDF)
+    {
+        
mpPDFExtOutDevData->SetStructureAttributeNumerical(vcl::PDFWriter::LinkAnnotation,
+                                                           aBookmark.nLinkId);
+        mpPDFExtOutDevData->EndStructureElement();
+    }
 }
 
 void VclMetafileProcessor2D::processTextHierarchyLinePrimitive2D(
diff --git a/editeng/source/editeng/StripPortionsHelper.cxx 
b/editeng/source/editeng/StripPortionsHelper.cxx
index 61e9e939ca69..71391194734b 100644
--- a/editeng/source/editeng/StripPortionsHelper.cxx
+++ b/editeng/source/editeng/StripPortionsHelper.cxx
@@ -23,6 +23,7 @@
 #include <editeng/escapementitem.hxx>
 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
 #include <editeng/smallcaps.hxx>
+#include <editeng/outliner.hxx>
 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
 #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
@@ -648,6 +649,12 @@ void TextHierarchyBreakup::processDrawBulletInfo(const 
DrawBulletInfo& rDrawBull
     maTextPortionPrimitives.push_back(pNewPrimitive);
 }
 
+void TextHierarchyBreakup::directlyAddB2DPrimitive(
+    const drawinglayer::primitive2d::Primitive2DReference& rSource)
+{
+    maTextPortionPrimitives.push_back(rSource);
+}
+
 TextHierarchyBreakup::TextHierarchyBreakup()
     : maTextPortionPrimitives()
     , maLinePrimitives()
@@ -685,4 +692,32 @@ TextHierarchyBreakup::getTextPortionPrimitives()
     return maParagraphPrimitives;
 }
 
+sal_Int16 TextHierarchyBreakupOutliner::getOutlineLevelFromParagraph(sal_Int32 
nPara) const
+{
+    sal_Int16 nDepth(mrOutliner.GetDepth(nPara));
+    EBulletInfo eInfo(mrOutliner.GetBulletInfo(nPara));
+    // Pass -1 to signal VclMetafileProcessor2D that there is no active
+    // bullets/numbering in this paragraph (i.e. this is normal text)
+    return eInfo.bVisible ? nDepth : -1;
+}
+
+sal_Int32 TextHierarchyBreakupOutliner::getParagraphCount() const
+{
+    return mrOutliner.GetParagraphCount();
+}
+
+TextHierarchyBreakupOutliner::TextHierarchyBreakupOutliner(Outliner& rOutliner)
+    : TextHierarchyBreakup()
+    , mrOutliner(rOutliner)
+{
+}
+
+TextHierarchyBreakupOutliner::TextHierarchyBreakupOutliner(
+    Outliner& rOutliner, const basegfx::B2DHomMatrix& rNewTransformA,
+    const basegfx::B2DHomMatrix& rNewTransformB)
+    : TextHierarchyBreakup(rNewTransformA, rNewTransformB)
+    , mrOutliner(rOutliner)
+{
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/editview.cxx 
b/editeng/source/editeng/editview.cxx
index 3429ec7717f1..ae94c4c9b3f0 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -49,6 +49,7 @@
 #include <vcl/window.hxx>
 #include <editeng/acorrcfg.hxx>
 #include <editeng/unolingu.hxx>
+#include <editeng/StripPortionsHelper.hxx>
 #include <unotools/lingucfg.hxx>
 
 #include <com/sun/star/frame/XStorable.hpp>
@@ -401,9 +402,20 @@ Point EditView::CalculateTextPaintStartPosition() const
     return getImpEditEngine().CalculateTextPaintStartPosition(getImpl());
 }
 
+void EditView::DrawText_ToEditView( TextHierarchyBreakup& rHelper, const 
tools::Rectangle& rRect, OutputDevice* pTargetDevice )
+{
+    getImpEditEngine().DrawText_ToEditView(rHelper, &getImpl(), rRect, 
pTargetDevice);
+}
+
 void EditView::DrawText_ToEditView( const tools::Rectangle& rRect, 
OutputDevice* pTargetDevice )
 {
-    getImpEditEngine().DrawText_ToEditView(&getImpl(), rRect, pTargetDevice);
+    // use TextHierarchyBreakup to get all text embedded to the
+    // TextHierarchy.*Primitive2D groupings for better processing
+    TextHierarchyBreakup aHelper;
+
+    // hand that Helper over to DrawText_ToEditView at the EditEngine
+    // for usage
+    getImpEditEngine().DrawText_ToEditView(aHelper, &getImpl(), rRect, 
pTargetDevice);
 }
 
 void EditView::setEditEngine(EditEngine& rEditEngine)
diff --git a/editeng/source/editeng/impedit.hxx 
b/editeng/source/editeng/impedit.hxx
index eca225e8ca0c..32bcbd1337d1 100644
--- a/editeng/source/editeng/impedit.hxx
+++ b/editeng/source/editeng/impedit.hxx
@@ -1003,7 +1003,7 @@ public:
     void                    DrawText_ToRectangle( OutputDevice& rOutDev, const 
tools::Rectangle& rOutRect, const Point& rStartDocPos, bool bHardClip );
     void                    UpdateViews( EditView* pCurView = nullptr );
     Point CalculateTextPaintStartPosition(ImpEditView& rView) const;
-    void                    DrawText_ToEditView( ImpEditView* pView, const 
tools::Rectangle& rRect, OutputDevice* pTargetDevice );
+    void                    DrawText_ToEditView( TextHierarchyBreakup& 
rHelper, ImpEditView* pView, const tools::Rectangle& rRect, OutputDevice* 
pTargetDevice );
     void PaintOrStrip( OutputDevice& rOutDev, tools::Rectangle aClipRect, 
Point aStartPos, Degree10 nOrientation = 0_deg10, StripPortionsHelper* 
pStripPortionsHelper = nullptr);
 
     bool                MouseButtonUp( const MouseEvent& rMouseEvent, 
EditView* pView );
diff --git a/editeng/source/editeng/impedit3.cxx 
b/editeng/source/editeng/impedit3.cxx
index 373967943123..7ddae54bd77d 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -88,6 +88,15 @@
 
 #include <vcl/outdev/ScopedStates.hxx>
 
+#include <vcl/canvastools.hxx>
+#include <drawinglayer/processor2d/processor2dtools.hxx>
+#include <drawinglayer/processor2d/baseprocessor2d.hxx>
+#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
+#include <drawinglayer/primitive2d/maskprimitive2d.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+
 #include <unicode/uchar.h>
 
 using namespace ::com::sun::star;
@@ -3354,18 +3363,61 @@ Point ImpEditEngine::MoveToNextLine(
 
 void ImpEditEngine::DrawText_ToPosition( OutputDevice& rOutDev, const Point& 
rStartPos, Degree10 nOrientation )
 {
+    if( rOutDev.GetConnectMetaFile() )
+        rOutDev.Push();
+
     // Create with 2 points, as with positive points it will end up with
     // LONGMAX as Size, Bottom and Right in the range > LONGMAX.
     tools::Rectangle aBigRect( -0x3FFFFFFF, -0x3FFFFFFF, 0x3FFFFFFF, 
0x3FFFFFFF );
-    if( rOutDev.GetConnectMetaFile() )
-        rOutDev.Push();
     Point aStartPos( rStartPos );
+
     if ( IsEffectivelyVertical() )
     {
         aStartPos.AdjustX(GetPaperSize().Width() );
         rStartPos.RotateAround(aStartPos, nOrientation);
     }
-    PaintOrStrip(rOutDev, aBigRect, aStartPos, nOrientation);
+
+    static bool bUsePrimitives(nullptr == 
std::getenv("DISBALE_EDITENGINE_ON_PRIMITIVES"));
+
+    if (bUsePrimitives)
+    {
+        // extract Primitives.
+        // Do not use Orientation, that will be added below as transformation
+        TextHierarchyBreakup aHelper;
+        PaintOrStrip(rOutDev, aBigRect, aStartPos, 0_deg10, &aHelper);
+
+        if (aHelper.getTextPortionPrimitives().empty())
+            // no Primitives, done
+            return;
+
+        // create ViewInformation2D based on target OutputDevice
+        drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+        
aViewInformation2D.setViewTransformation(rOutDev.GetViewTransformation());
+
+        // get content
+        drawinglayer::primitive2d::Primitive2DContainer 
aContent(aHelper.getTextPortionPrimitives());
+
+        if (0_deg10 != nOrientation)
+        {
+            // if we have an Orientation, add rotation. Note that input value 
is
+            // 10th degree and wrong oriented for a right-hand coordinate 
system (sigh)
+            const double fAngle(-toRadians(nOrientation));
+            aContent = drawinglayer::primitive2d::Primitive2DContainer{
+                new drawinglayer::primitive2d::TransformPrimitive2D(
+                    basegfx::utils::createRotateAroundPoint(aStartPos.X(), 
aStartPos.Y(), fAngle),
+                    std::move(aContent))};
+        }
+
+        // create PrimitiveProcessor and render to target
+        std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
+            
drawinglayer::processor2d::createProcessor2DFromOutputDevice(rOutDev, 
aViewInformation2D));
+        xProcessor->process(aContent);
+    }
+    else
+    {
+        PaintOrStrip(rOutDev, aBigRect, aStartPos, nOrientation);
+    }
+
     if (rOutDev.GetConnectMetaFile())
         rOutDev.Pop();
 }
@@ -3394,48 +3446,92 @@ void ImpEditEngine::DrawText_ToRectangle( OutputDevice& 
rOutDev, const tools::Re
         aStartPos.setY( aOutRect.Top() - rStartDocPos.X() );
     }
 
-    bool bClipRegion = rOutDev.IsClipRegion();
-    bool bMetafile = rOutDev.GetConnectMetaFile();
-    vcl::Region aOldRegion = rOutDev.GetClipRegion();
+    static bool bUsePrimitives(nullptr == 
std::getenv("DISBALE_EDITENGINE_ON_PRIMITIVES"));
 
-    // If one existed => intersection!
-    // Use Push/pop for creating the Meta file
-    if ( bMetafile )
-        rOutDev.Push();
+    if (bUsePrimitives)
+    {
+        // extract Primitives
+        TextHierarchyBreakup aHelper;
+        PaintOrStrip(rOutDev, aOutRect, aStartPos, 0_deg10, &aHelper);
+
+        if (aHelper.getTextPortionPrimitives().empty())
+            // no Primitives, done
+            return;
+
+        // create ViewInformation2D based on target OutputDevice
+        drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+        
aViewInformation2D.setViewTransformation(rOutDev.GetViewTransformation());
+        const basegfx::B2DRange 
aClipRange(vcl::unotools::b2DRectangleFromRectangle(aOutRect));
+        aViewInformation2D.setViewport(aClipRange);
+
+        // get content and it's range
+        drawinglayer::primitive2d::Primitive2DContainer 
aContent(aHelper.getTextPortionPrimitives());
+        const basegfx::B2DRange 
aContentRange(aContent.getB2DRange(aViewInformation2D));
+
+        if (!aContentRange.overlaps(aClipRange))
+            // no overlap, nothing visible
+            return;
 
-    // Always use the Intersect method, it is a must for Metafile!
-    if ( bHardClip )
+        if (bHardClip && !aClipRange.isInside(aContentRange))
+        {
+            // not completely inside aClipRange and clipping requested
+            // Embed to MaskPrimitive2D
+            aContent = drawinglayer::primitive2d::Primitive2DContainer{
+                new drawinglayer::primitive2d::MaskPrimitive2D(
+                    
basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aClipRange)),
+                    std::move(aContent))};
+        }
+
+        // create PrimitiveProcessor and render to target
+        std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
+            
drawinglayer::processor2d::createProcessor2DFromOutputDevice(rOutDev, 
aViewInformation2D));
+        xProcessor->process(aContent);
+    }
+    else
     {
-        // Clip only if necessary...
-        if (!IsFormatted())
-            FormatDoc();
-        tools::Long nTextWidth = !IsEffectivelyVertical() ? 
CalcTextWidth(true) : GetTextHeight();
-        if ( rStartDocPos.X() || rStartDocPos.Y() ||
-             ( rOutRect.GetHeight() < 
static_cast<tools::Long>(GetTextHeight()) ) ||
-             ( rOutRect.GetWidth() < nTextWidth ) )
+        bool bClipRegion = rOutDev.IsClipRegion();
+        bool bMetafile = rOutDev.GetConnectMetaFile();
+        vcl::Region aOldRegion = rOutDev.GetClipRegion();
+
+        // If one existed => intersection!
+        // Use Push/pop for creating the Meta file
+        if ( bMetafile )
+            rOutDev.Push();
+
+        // Always use the Intersect method, it is a must for Metafile!
+        if ( bHardClip )
         {
-            // Some printer drivers cause problems if characters graze the
-            // ClipRegion, therefore rather add a pixel more ...
-            tools::Rectangle aClipRect( aOutRect );
-            if ( rOutDev.GetOutDevType() == OUTDEV_PRINTER )
+            // Clip only if necessary...
+            if (!IsFormatted())
+                FormatDoc();
+            tools::Long nTextWidth = !IsEffectivelyVertical() ? 
CalcTextWidth(true) : GetTextHeight();
+            if ( rStartDocPos.X() || rStartDocPos.Y() ||
+                ( rOutRect.GetHeight() < 
static_cast<tools::Long>(GetTextHeight()) ) ||
+                ( rOutRect.GetWidth() < nTextWidth ) )
             {
-                Size aPixSz( 1, 0 );
-                aPixSz = rOutDev.PixelToLogic( aPixSz );
-                aClipRect.AdjustRight(aPixSz.Width() );
-                aClipRect.AdjustBottom(aPixSz.Width() );
+                // Some printer drivers cause problems if characters graze the
+                // ClipRegion, therefore rather add a pixel more ...
+                tools::Rectangle aClipRect( aOutRect );
+                if ( rOutDev.GetOutDevType() == OUTDEV_PRINTER )
+                {
+                    Size aPixSz( 1, 0 );
+                    aPixSz = rOutDev.PixelToLogic( aPixSz );
+                    aClipRect.AdjustRight(aPixSz.Width() );
+                    aClipRect.AdjustBottom(aPixSz.Width() );
+                }
+                rOutDev.IntersectClipRegion( aClipRect );
             }
-            rOutDev.IntersectClipRegion( aClipRect );
         }
-    }
 
-    PaintOrStrip(rOutDev, aOutRect, aStartPos);
+        PaintOrStrip(rOutDev, aOutRect, aStartPos);
 
-    if ( bMetafile )
-        rOutDev.Pop();
-    else if ( bClipRegion )
-        rOutDev.SetClipRegion( aOldRegion );
-    else
-        rOutDev.SetClipRegion();
+        if ( bMetafile )
+            rOutDev.Pop();
+        else if ( bClipRegion )
+            rOutDev.SetClipRegion( aOldRegion );
+        else
+            rOutDev.SetClipRegion();
+    }
 }
 
 // TODO: use IterateLineAreas in ImpEditEngine::Paint, to avoid algorithm 
duplication
@@ -3822,7 +3918,7 @@ void ImpEditEngine::PaintOrStrip( OutputDevice& rOutDev, 
tools::Rectangle aClipR
                                     pDXArray = KernArraySpan(aTmpDXArray);
 
                                     // add a meta file comment if we record to 
a metafile
-                                    if( bMetafileValid )
+                                    if( !pStripPortionsHelper && 
bMetafileValid )
                                     {
                                         const SvxFieldItem* pFieldItem = 
dynamic_cast<const SvxFieldItem*>(pAttr->GetItem());
                                         if( pFieldItem )
@@ -4132,7 +4228,7 @@ void ImpEditEngine::PaintOrStrip( OutputDevice& rOutDev, 
tools::Rectangle aClipR
                                 if ( rTextPortion.GetKind() == 
PortionKind::FIELD )
                                 {
                                     // add a meta file comment if we record to 
a metafile
-                                    if( bMetafileValid )
+                                    if( !pStripPortionsHelper && 
bMetafileValid )
                                     {
                                         const EditCharAttrib* pAttr = 
rParaPortion.GetNode()->GetCharAttribs().FindFeature(nIndex);
                                         assert( pAttr && "Field not found" );
@@ -4311,7 +4407,7 @@ Point 
ImpEditEngine::CalculateTextPaintStartPosition(ImpEditView& rView) const
     return aStartPos;
 }
 
-void ImpEditEngine::DrawText_ToEditView( ImpEditView* pView, const 
tools::Rectangle& rRect, OutputDevice* pTargetDevice )
+void ImpEditEngine::DrawText_ToEditView( TextHierarchyBreakup& rHelper, 
ImpEditView* pView, const tools::Rectangle& rRect, OutputDevice* pTargetDevice )
 {
     if ( !IsUpdateLayout() || IsInUndo() )
         return;
@@ -4339,16 +4435,59 @@ void ImpEditEngine::DrawText_ToEditView( ImpEditView* 
pView, const tools::Rectan
             aClipRect.SetRight( nMaxX );
     }
 
-    bool bClipRegion = rTarget.IsClipRegion();
-    vcl::Region aOldRegion = rTarget.GetClipRegion();
-    rTarget.IntersectClipRegion( aClipRect );
+    static bool bUsePrimitives(nullptr == 
std::getenv("DISBALE_EDITENGINE_ON_PRIMITIVES"));
+
+    if (bUsePrimitives)
+    {
+        // extract Primitives
+        PaintOrStrip(rTarget, aClipRect, aStartPos, 0_deg10, &rHelper);
+
+        if (rHelper.getTextPortionPrimitives().empty())
+            // no Primitives, done
+            return;
+
+        // create ViewInformation2D based on target OutputDevice
+        drawinglayer::geometry::ViewInformation2D aViewInformation2D;
+        
aViewInformation2D.setViewTransformation(rTarget.GetViewTransformation());
+        const basegfx::B2DRange 
aClipRange(vcl::unotools::b2DRectangleFromRectangle(aClipRect));
+        aViewInformation2D.setViewport(aClipRange);
 
-    PaintOrStrip(rTarget, aClipRect, aStartPos);
+        // get content and it's range
+        drawinglayer::primitive2d::Primitive2DContainer 
aContent(rHelper.getTextPortionPrimitives());
+        const basegfx::B2DRange 
aContentRange(aContent.getB2DRange(aViewInformation2D));
 
-    if ( bClipRegion )
-        rTarget.SetClipRegion( aOldRegion );
+        if (!aContentRange.overlaps(aClipRange))
+            // no overlap, nothing visible
+            return;
+
+        if (!aClipRange.isInside(aContentRange))
+        {
+            // not completely inside aClipRange, clipping needed.
+            // Embed to MaskPrimitive2D
+            aContent = drawinglayer::primitive2d::Primitive2DContainer{
+                new drawinglayer::primitive2d::MaskPrimitive2D(
+                    
basegfx::B2DPolyPolygon(basegfx::utils::createPolygonFromRect(aClipRange)),
+                    std::move(aContent))};
+        }
+
+        // create PrimitiveProcessor and render to target
+        std::unique_ptr<drawinglayer::processor2d::BaseProcessor2D> xProcessor(
+            
drawinglayer::processor2d::createProcessor2DFromOutputDevice(rTarget, 
aViewInformation2D));
+        xProcessor->process(aContent);
+    }
     else
-        rTarget.SetClipRegion();
+    {
+        bool bClipRegion = rTarget.IsClipRegion();
+        vcl::Region aOldRegion = rTarget.GetClipRegion();
+        rTarget.IntersectClipRegion( aClipRect );
+
+        PaintOrStrip(rTarget, aClipRect, aStartPos);
+
+        if ( bClipRegion )
+            rTarget.SetClipRegion( aOldRegion );
+        else
+            rTarget.SetClipRegion();
+    }
 
     pView->DrawSelectionXOR(pView->GetEditSelection(), nullptr, &rTarget);
 }
diff --git a/editeng/source/outliner/outleeng.cxx 
b/editeng/source/outliner/outleeng.cxx
index a4c193eb66ab..92dcbfb78fab 100644
--- a/editeng/source/outliner/outleeng.cxx
+++ b/editeng/source/outliner/outleeng.cxx
@@ -39,11 +39,11 @@ OutlinerEditEng::~OutlinerEditEng()
 {
 }
 
-void OutlinerEditEng::ProcessFirstLineOfParagraph(sal_Int32 nPara, const 
Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& 
rOutDev, StripPortionsHelper* pStripPortionsHelper)//,
+void OutlinerEditEng::ProcessFirstLineOfParagraph(sal_Int32 nPara, const 
Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& 
rOutDev, StripPortionsHelper* pStripPortionsHelper)
 {
     if( GetControlWord() & EEControlBits::OUTLINER )
     {
-        PaintFirstLineInfo aInfo(nPara, rStartPos, &rOutDev);
+        PaintFirstLineInfo aInfo(nPara, rStartPos, &rOutDev, 
pStripPortionsHelper);
         pOwner->maPaintFirstLineHdl.Call( &aInfo );
     }
 
diff --git a/editeng/source/outliner/outlvw.cxx 
b/editeng/source/outliner/outlvw.cxx
index e1e28f04fcad..c7e772be9524 100644
--- a/editeng/source/outliner/outlvw.cxx
+++ b/editeng/source/outliner/outlvw.cxx
@@ -24,6 +24,7 @@
 #include <editeng/editeng.hxx>
 #include <editeng/editview.hxx>
 #include <editeng/editdata.hxx>
+#include <editeng/StripPortionsHelper.hxx>
 
 #include <svl/style.hxx>
 #include <svl/languageoptions.hxx>
@@ -66,7 +67,14 @@ void OutlinerView::DrawText_ToEditView( const 
tools::Rectangle& rRect, OutputDev
     if( rOwner.bFirstParaIsEmpty )
         rOwner.Insert( OUString() );
 
-    pEditView->DrawText_ToEditView( rRect, pTargetDevice );
+    // use TextHierarchyBreakupOutliner to get all text embedded to the
+    // TextHierarchy.*Primitive2D groupings for better processing, plus
+    // the correct paragtaph countings
+    TextHierarchyBreakupOutliner aHelper(rOwner);
+
+    // hand that Helper over to DrawText_ToEditView at the EditEngine
+    // for usage
+    pEditView->DrawText_ToEditView( aHelper, rRect, pTargetDevice );
 }
 
 bool OutlinerView::PostKeyEvent( const KeyEvent& rKEvt, vcl::Window const * 
pFrameWin )
diff --git a/include/editeng/StripPortionsHelper.hxx 
b/include/editeng/StripPortionsHelper.hxx
index 7da5cc88d516..dc235471eb37 100644
--- a/include/editeng/StripPortionsHelper.hxx
+++ b/include/editeng/StripPortionsHelper.hxx
@@ -38,6 +38,8 @@ namespace com::sun::star::lang
 struct Locale;
 }
 
+class Outliner;
+
 class EDITENG_DLLPUBLIC DrawPortionInfo
 {
 public:
@@ -110,6 +112,8 @@ class EDITENG_DLLPUBLIC StripPortionsHelper
 public:
     virtual void processDrawPortionInfo(const DrawPortionInfo&) = 0;
     virtual void processDrawBulletInfo(const DrawBulletInfo&) = 0;
+    virtual void directlyAddB2DPrimitive(const 
drawinglayer::primitive2d::Primitive2DReference&)
+        = 0;
 };
 
 class EDITENG_DLLPUBLIC TextHierarchyBreakup : public StripPortionsHelper
@@ -127,8 +131,10 @@ protected:
     void flushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara);
 
 public:
-    virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo);
-    virtual void processDrawBulletInfo(const DrawBulletInfo& rDrawBulletInfo);
+    virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo) override;
+    virtual void processDrawBulletInfo(const DrawBulletInfo& rDrawBulletInfo) 
override;
+    virtual void
+    directlyAddB2DPrimitive(const 
drawinglayer::primitive2d::Primitive2DReference&) override;
 
     TextHierarchyBreakup();
     TextHierarchyBreakup(const basegfx::B2DHomMatrix& rNewTransformA,
@@ -137,6 +143,20 @@ public:
     const drawinglayer::primitive2d::Primitive2DContainer& 
getTextPortionPrimitives();
 };
 
+class EDITENG_DLLPUBLIC TextHierarchyBreakupOutliner : public 
TextHierarchyBreakup
+{
+    Outliner& mrOutliner;
+
+protected:
+    virtual sal_Int16 getOutlineLevelFromParagraph(sal_Int32 nPara) const 
override;
+    virtual sal_Int32 getParagraphCount() const override;
+
+public:
+    TextHierarchyBreakupOutliner(Outliner& rOutliner);
+    TextHierarchyBreakupOutliner(Outliner& rOutliner, const 
basegfx::B2DHomMatrix& rNewTransformA,
+                                 const basegfx::B2DHomMatrix& rNewTransformB);
+};
+
 #endif // INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/editeng/editview.hxx b/include/editeng/editview.hxx
index c725cdbda71c..e77cea8c56b1 100644
--- a/include/editeng/editview.hxx
+++ b/include/editeng/editview.hxx
@@ -69,6 +69,7 @@ class InputContext;
 class OutputDevice;
 enum class TransliterationFlags;
 enum class PointerStyle;
+class TextHierarchyBreakup;
 
 namespace com {
 namespace sun {
@@ -204,6 +205,7 @@ public:
 
     Point CalculateTextPaintStartPosition() const;
     void            DrawText_ToEditView( const tools::Rectangle& rRect, 
OutputDevice* pTargetDevice = nullptr );
+    void            DrawText_ToEditView( TextHierarchyBreakup& rHelper, const 
tools::Rectangle& rRect, OutputDevice* pTargetDevice = nullptr );
     tools::Rectangle       GetInvalidateRect() const;
     SAL_DLLPRIVATE void            InvalidateWindow(const tools::Rectangle& 
rClipRect);
     void            InvalidateOtherViewWindows( const tools::Rectangle& 
rInvRect );
diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx
index df22cbe70fa3..a6f740d3ec36 100644
--- a/include/editeng/outliner.hxx
+++ b/include/editeng/outliner.hxx
@@ -407,9 +407,10 @@ struct EDITENG_DLLPUBLIC PaintFirstLineInfo
     sal_Int32 mnPara;
     const Point& mrStartPos;
     VclPtr<OutputDevice> mpOutDev;
+    StripPortionsHelper* mpStripPortionsHelper;
 
-    PaintFirstLineInfo( sal_Int32 nPara, const Point& rStartPos, OutputDevice* 
pOutDev )
-        : mnPara( nPara ), mrStartPos( rStartPos ), mpOutDev( pOutDev )
+    PaintFirstLineInfo( sal_Int32 nPara, const Point& rStartPos, OutputDevice* 
pOutDev, StripPortionsHelper* pStripPortionsHelper )
+        : mnPara( nPara ), mrStartPos( rStartPos ), mpOutDev( pOutDev ), 
mpStripPortionsHelper( pStripPortionsHelper )
     {}
 };
 
diff --git a/include/svx/svdoutl.hxx b/include/svx/svdoutl.hxx
index 5ef5985ef26b..0af4f6804849 100644
--- a/include/svx/svdoutl.hxx
+++ b/include/svx/svdoutl.hxx
@@ -52,21 +52,6 @@ public:
     virtual std::optional<bool> GetCompatFlag(SdrCompatibilityFlag eFlag) 
const override;
 };
 
-class TextHierarchyBreakupOutliner : public TextHierarchyBreakup
-{
-    SdrOutliner&                mrOutliner;
-
-protected:
-    virtual sal_Int16 getOutlineLevelFromParagraph(sal_Int32 nPara) const;
-    virtual sal_Int32 getParagraphCount() const;
-
-public:
-    TextHierarchyBreakupOutliner(
-        SdrOutliner& rOutliner,
-        const basegfx::B2DHomMatrix& rNewTransformA,
-        const basegfx::B2DHomMatrix& rNewTransformB);
-};
-
 class TextHierarchyBreakupBlockText : public TextHierarchyBreakupOutliner
 {
     // ClipRange for BlockText decomposition; only text portions completely
@@ -75,7 +60,7 @@ class TextHierarchyBreakupBlockText : public 
TextHierarchyBreakupOutliner
     const basegfx::B2DRange&    mrClipRange;
 
 public:
-    virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo);
+    virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo) override;
 
     TextHierarchyBreakupBlockText(
         SdrOutliner& rOutliner,
@@ -90,7 +75,7 @@ class TextHierarchyBreakupContourText : public 
TextHierarchyBreakupOutliner
     basegfx::B2DVector              maScale;
 
 public:
-    virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo);
+    virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo) override;
 
     TextHierarchyBreakupContourText(
         SdrOutliner& rOutliner,
diff --git a/sd/source/ui/view/outlview.cxx b/sd/source/ui/view/outlview.cxx
index 768f32dff6d9..14c7ad84afe3 100644
--- a/sd/source/ui/view/outlview.cxx
+++ b/sd/source/ui/view/outlview.cxx
@@ -44,6 +44,13 @@
 
 #include <com/sun/star/frame/XFrame.hpp>
 
+#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <drawinglayer/primitive2d/textprimitive2d.hxx>
+#include <vcl/metric.hxx>
+
 #include <DrawDocShell.hxx>
 #include <drawdoc.hxx>
 #include <Window.hxx>
@@ -1576,8 +1583,23 @@ IMPL_LINK(OutlineView, PaintingFirstLineHdl, 
PaintFirstLineInfo*, pInfo, void)
     Point aImagePos( pInfo->mrStartPos );
     aImagePos.AdjustX(aOutSize.Width() - aImageSize.Width() - aOffset.Width() 
) ;
     aImagePos.AdjustY((aOutSize.Height() - aImageSize.Height()) / 2 );
+    static bool bUsePrimitives(nullptr == 
std::getenv("DISBALE_EDITENGINE_ON_PRIMITIVES"));
 
-    pInfo->mpOutDev->DrawImage( aImagePos, aImageSize, maSlideImage );
+    if (bUsePrimitives && pInfo->mpStripPortionsHelper)
+    {
+        // create BitmapPrimitive2D and add directly
+        const drawinglayer::primitive2d::Primitive2DReference xBitmap(
+            new drawinglayer::primitive2d::BitmapPrimitive2D(
+                maSlideImage.GetBitmapEx(),
+                basegfx::utils::createScaleTranslateB2DHomMatrix(
+                    aImageSize.Width(), aImageSize.Height(),
+                    aImagePos.X(), aImagePos.Y())));
+        pInfo->mpStripPortionsHelper->directlyAddB2DPrimitive(xBitmap);
+    }
+    else
+    {
+        pInfo->mpOutDev->DrawImage( aImagePos, aImageSize, maSlideImage );
+    }
 
     const bool bVertical = mrOutliner.IsVertical();
     const bool bRightToLeftPara = rEditEngine.IsRightToLeft( pInfo->mnPara );
@@ -1612,7 +1634,38 @@ IMPL_LINK(OutlineView, PaintingFirstLineHdl, 
PaintFirstLineInfo*, pInfo, void)
         aTextPos.AdjustY( -(aTextSz.Width()) );
         aTextPos.AdjustX(nBulletHeight / 2 );
     }
-    pInfo->mpOutDev->DrawText( aTextPos, aPageText );
+
+    if (bUsePrimitives && pInfo->mpStripPortionsHelper)
+    {
+        // create TextSimplePortionPrimitive2D and add directly
+        basegfx::B2DVector aFontSize;
+        const vcl::Font& rFont(pInfo->mpOutDev->GetFont());
+        drawinglayer::attribute::FontAttribute aFontAttr(
+            drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontSize, 
rFont, false, false));
+        const FontMetric aFontMetric(pInfo->mpOutDev->GetFontMetric(rFont));
+        const double fTextOffsetY(aFontMetric.GetAscent());
+        const basegfx::B2DHomMatrix aTextTransform(
+            basegfx::utils::createScaleTranslateB2DHomMatrix(
+                aFontSize.getX(), aFontSize.getY(),
+                aTextPos.X(), aTextPos.Y() + fTextOffsetY));
+
+        const drawinglayer::primitive2d::Primitive2DReference xText(
+            new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+                aTextTransform,
+                aPageText,
+                0,
+                aPageText.getLength(),
+                std::vector<double>(),
+                {},
+                std::move(aFontAttr),
+                css::lang::Locale(),
+                pInfo->mpOutDev->GetTextColor().getBColor()));
+        pInfo->mpStripPortionsHelper->directlyAddB2DPrimitive(xText);
+    }
+    else
+    {
+        pInfo->mpOutDev->DrawText( aTextPos, aPageText );
+    }
 }
 
 void OutlineView::UpdateParagraph( sal_Int32 nPara )
diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx 
b/svx/source/svdraw/svdotextpathdecomposition.cxx
index 14c9693b3d64..ec4017ba4350 100644
--- a/svx/source/svdraw/svdotextpathdecomposition.cxx
+++ b/svx/source/svdraw/svdotextpathdecomposition.cxx
@@ -154,17 +154,22 @@ namespace
         ::std::vector< impPathTextPortion >         maPathTextPortions;
 
     public:
-        virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo)
+        virtual void processDrawPortionInfo(const DrawPortionInfo& 
rDrawPortionInfo) override
         {
             // extract and add data for TextOnPath further processing
             maPathTextPortions.emplace_back(rDrawPortionInfo);
         }
 
-        virtual void processDrawBulletInfo(const DrawBulletInfo&)
+        virtual void processDrawBulletInfo(const DrawBulletInfo&) override
         {
             // nothing to do here, bullets are for now ignored for TextOnLine
         }
 
+        virtual void directlyAddB2DPrimitive(const 
drawinglayer::primitive2d::Primitive2DReference&) override
+        {
+            // nothing to do here, no support for directly adding Primitives
+        }
+
         const ::std::vector< impPathTextPortion >& sortAndGetPathTextPortions()
         {
             if(!maPathTextPortions.empty())
diff --git a/svx/source/svdraw/svdoutl.cxx b/svx/source/svdraw/svdoutl.cxx
index 7b4cf2499e36..da7bafc506c7 100644
--- a/svx/source/svdraw/svdoutl.cxx
+++ b/svx/source/svdraw/svdoutl.cxx
@@ -122,31 +122,6 @@ std::optional<bool> 
SdrOutliner::GetCompatFlag(SdrCompatibilityFlag eFlag) const
     return {};
 }
 
-sal_Int16 TextHierarchyBreakupOutliner::getOutlineLevelFromParagraph(sal_Int32 
nPara) const
-{
-    sal_Int16 nDepth(mrOutliner.GetDepth(nPara));
-    EBulletInfo eInfo(mrOutliner.GetBulletInfo(nPara));
-    // Pass -1 to signal VclMetafileProcessor2D that there is no active
-    // bullets/numbering in this paragraph (i.e. this is normal text)
-    return eInfo.bVisible ?  nDepth : -1;
-}
-
-sal_Int32 TextHierarchyBreakupOutliner::getParagraphCount() const
-{
-    return mrOutliner.GetParagraphCount();
-}
-
-TextHierarchyBreakupOutliner::TextHierarchyBreakupOutliner(
-    SdrOutliner& rOutliner,
-    const basegfx::B2DHomMatrix& rNewTransformA,
-    const basegfx::B2DHomMatrix& rNewTransformB)
-: TextHierarchyBreakup(
-    rNewTransformA,
-    rNewTransformB)
-, mrOutliner(rOutliner)
-{
-}
-
 void TextHierarchyBreakupBlockText::processDrawPortionInfo(const 
DrawPortionInfo& rDrawPortionInfo)
 {
     // Is clipping wanted? This is text clipping; only accept a portion

Reply via email to