editeng/inc/outleeng.hxx                        |   16 
 editeng/source/editeng/editeng.cxx              |   23 
 editeng/source/editeng/impedit.hxx              |    5 
 editeng/source/editeng/impedit3.cxx             |   42 -
 editeng/source/outliner/outleeng.cxx            |   22 
 editeng/source/outliner/outliner.cxx            |   74 --
 include/editeng/editeng.hxx                     |   25 
 include/editeng/outliner.hxx                    |   80 --
 include/svx/svdotext.hxx                        |   14 
 sd/source/ui/view/outlview.cxx                  |    1 
 svx/source/svdraw/svdotextdecomposition.cxx     |  832 ++++++++++++------------
 svx/source/svdraw/svdotextpathdecomposition.cxx |   13 
 12 files changed, 550 insertions(+), 597 deletions(-)

New commits:
commit 3da11981797a45f83195d63c20818f315406e3dd
Author:     Armin Le Grand (Collabora) <[email protected]>
AuthorDate: Thu Mar 27 12:01:13 2025 +0100
Commit:     Armin Le Grand <[email protected]>
CommitDate: Thu Mar 27 18:56:22 2025 +0100

    Make conversion to Primitives more accessible for EditEngine/Outliner
    
    When checking how TextEdit on the Overlay may be doable in Calc
    I stumbled about that I initially did that based on Outliner
    (svx) for all DrawObjects/Draw/Impress, but it is not accessible
    from EditEngine.
    Calc again just uses an EditEngine for TextEdit in Cell, so I
    checked doability. Quite some stuff has to be changed, basically
    instead of unsing IMPL/DECL_LINK I use lambda functions now. The
    former callbacks were anyways exclusively used for this and had
    no general used case.
    Also simplified and cleaned up quite some other stuff, some
    member functions could be removed, some helper classes deleted
    or simplified/streamlined.
    This should do no change to existing decomposes or edit views,
    but will make it possible to move in the direction of CellEdit
    on the Overlay and also prepares decompositions there to allow
    going in the direction of primitive based paints in Calc.
    
    Change-Id: Ia9c4c1129c9579ba4870b091977082e5f0d319f8
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183388
    Reviewed-by: Armin Le Grand <[email protected]>
    Tested-by: Jenkins

diff --git a/editeng/inc/outleeng.hxx b/editeng/inc/outleeng.hxx
index 892c336d3290..5415c26810fb 100644
--- a/editeng/inc/outleeng.hxx
+++ b/editeng/inc/outleeng.hxx
@@ -35,24 +35,14 @@ public:
                         OutlinerEditEng( Outliner* pOwner, SfxItemPool* pPool 
);
                         virtual ~OutlinerEditEng() override;
 
-    virtual void        PaintingFirstLine(sal_Int32 nPara, const Point& 
rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev) 
override;
+    virtual void        PaintingFirstLine(sal_Int32 nPara, const Point& 
rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev,
+        const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+        const std::function<void(const DrawBulletInfo&)>& rDrawBullet) 
override;
 
     virtual void        ParagraphInserted( sal_Int32 nNewParagraph ) override;
     virtual void        ParagraphDeleted( sal_Int32 nDeletedParagraph ) 
override;
     virtual void        ParagraphConnected( sal_Int32 nLeftParagraph, 
sal_Int32 nRightParagraph ) override;
 
-    virtual void DrawingText( const Point& rStartPos, const OUString& rText, 
sal_Int32 nTextStart,
-                              sal_Int32 nTextLen, KernArraySpan pDXArray,
-                              std::span<const sal_Bool> pKashidaArray, const 
SvxFont& rFont,
-                              sal_Int32 nPara, sal_uInt8 nRightToLeft,
-                              const EEngineData::WrongSpellVector* 
pWrongSpellVector,
-                              const SvxFieldData* pFieldData,
-                              bool bEndOfLine,
-                              bool bEndOfParagraph,
-                              const css::lang::Locale* pLocale,
-                              const Color& rOverlineColor,
-                              const Color& rTextLineColor) override;
-
     virtual void        StyleSheetChanged( SfxStyleSheet* pStyle ) override;
     virtual void        ParaAttribsChanged( sal_Int32 nPara ) override;
     virtual bool        SpellNextDocument() override;
diff --git a/editeng/source/editeng/editeng.cxx 
b/editeng/source/editeng/editeng.cxx
index ee4f23275377..8a8a3029d6e1 100644
--- a/editeng/source/editeng/editeng.cxx
+++ b/editeng/source/editeng/editeng.cxx
@@ -1071,8 +1071,13 @@ SvxFont EditEngine::GetStandardSvxFont( sal_Int32 nPara )
     return pNode->GetCharAttribs().GetDefFont();
 }
 
-void EditEngine::StripPortions()
+void EditEngine::StripPortions(
+    const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+    const std::function<void(const DrawBulletInfo&)>& rDrawBullet)
 {
+    if (!rDrawPortion && !rDrawBullet)
+        return;
+
     ScopedVclPtrInstance< VirtualDevice > aTmpDev;
     tools::Rectangle aBigRect( Point( 0, 0 ), Size( 0x7FFFFFFF, 0x7FFFFFFF ) );
     if ( IsEffectivelyVertical() )
@@ -1088,7 +1093,8 @@ void EditEngine::StripPortions()
             aBigRect.SetBottom( 0 );
         }
     }
-    getImpl().Paint(*aTmpDev, aBigRect, Point(), true);
+
+    getImpl().Paint(*aTmpDev, aBigRect, Point(), 0_deg10, rDrawPortion, 
rDrawBullet);
 }
 
 void EditEngine::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList )
@@ -1578,16 +1584,9 @@ EditEngine::CreateTransferable(const ESelection& 
rSelection)
 
 // ======================    Virtual Methods    ========================
 
-void EditEngine::DrawingText( const Point&, const OUString&, sal_Int32, 
sal_Int32,
-                              KernArraySpan, std::span<const sal_Bool>,
-                              const SvxFont&, sal_Int32 /*nPara*/, sal_uInt8 
/*nRightToLeft*/,
-                              const EEngineData::WrongSpellVector*, const 
SvxFieldData*, bool, bool,
-                              const css::lang::Locale*, const Color&, const 
Color&)
-
-{
-}
-
-void EditEngine::PaintingFirstLine(sal_Int32, const Point&, const Point&, 
Degree10, OutputDevice&)
+void EditEngine::PaintingFirstLine(sal_Int32, const Point&, const Point&, 
Degree10, OutputDevice&,
+    const std::function<void(const DrawPortionInfo&)>&,
+    const std::function<void(const DrawBulletInfo&)>&)
 {
 }
 
diff --git a/editeng/source/editeng/impedit.hxx 
b/editeng/source/editeng/impedit.hxx
index 24d30ba3b559..f99e6d7cc469 100644
--- a/editeng/source/editeng/impedit.hxx
+++ b/editeng/source/editeng/impedit.hxx
@@ -992,7 +992,10 @@ public:
     void                    Draw( OutputDevice& rOutDev, const 
tools::Rectangle& rOutRect, const Point& rStartDocPos, bool bClip );
     void                    UpdateViews( EditView* pCurView = nullptr );
     void                    Paint( ImpEditView* pView, const tools::Rectangle& 
rRect, OutputDevice* pTargetDevice );
-    void                    Paint(OutputDevice& rOutDev, tools::Rectangle 
aClipRect, Point aStartPos, bool bStripOnly = false, Degree10 nOrientation = 
0_deg10);
+    void Paint(
+        OutputDevice& rOutDev, tools::Rectangle aClipRect, Point aStartPos, 
Degree10 nOrientation = 0_deg10,
+        const std::function<void(const DrawPortionInfo&)>& rDrawPortion = 
std::function<void(const DrawPortionInfo&)>(),
+        const std::function<void(const DrawBulletInfo&)>& rDrawBullet = 
std::function<void(const DrawBulletInfo&)>());
 
     bool                MouseButtonUp( const MouseEvent& rMouseEvent, 
EditView* pView );
     bool                MouseButtonDown( const MouseEvent& rMouseEvent, 
EditView* pView );
diff --git a/editeng/source/editeng/impedit3.cxx 
b/editeng/source/editeng/impedit3.cxx
index 6b86cbd0962b..fd214f2cc1c7 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -3242,7 +3242,7 @@ void ImpEditEngine::Draw( OutputDevice& rOutDev, const 
Point& rStartPos, Degree1
         aStartPos.AdjustX(GetPaperSize().Width() );
         rStartPos.RotateAround(aStartPos, nOrientation);
     }
-    Paint(rOutDev, aBigRect, aStartPos, false, nOrientation);
+    Paint(rOutDev, aBigRect, aStartPos, nOrientation);
     if (rOutDev.GetConnectMetaFile())
         rOutDev.Pop();
 }
@@ -3316,9 +3316,11 @@ void ImpEditEngine::Draw( OutputDevice& rOutDev, const 
tools::Rectangle& rOutRec
 }
 
 // TODO: use IterateLineAreas in ImpEditEngine::Paint, to avoid algorithm 
duplication
-void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, 
Point aStartPos, bool bStripOnly, Degree10 nOrientation )
+void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, 
Point aStartPos, Degree10 nOrientation,
+    const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+    const std::function<void(const DrawBulletInfo&)>& rDrawBullet)
 {
-    if ( !IsUpdateLayout() && !bStripOnly )
+    if ( !IsUpdateLayout() && !rDrawPortion )
         return;
 
     if ( !IsFormatted() )
@@ -3408,7 +3410,8 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                     {
                         Point aLineStart(aStartPos);
                         adjustYDirectionAware(aLineStart, -nLineHeight);
-                        GetEditEnginePtr()->PaintingFirstLine(nParaPortion, 
aLineStart, aOrigin, nOrientation, rOutDev);
+                        GetEditEnginePtr()->PaintingFirstLine(nParaPortion, 
aLineStart, aOrigin, nOrientation, rOutDev,
+                            rDrawPortion, rDrawBullet);
 
                         // Remember whether a bullet was painted.
                         const SfxBoolItem& rBulletState = 
mpEditEngine->GetParaAttrib(nParaPortion, EE_PARA_BULLETSTATE);
@@ -3595,7 +3598,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                     //It is not perfect, it still use 
lineBreaksList, so it won’t seek
                                     //word ends to wrap text there, but it 
would be difficult to change
                                     //this due to needed adaptations in 
EditEngine
-                                    if (bStripOnly && !bParsingFields && 
pExtraInfo && !pExtraInfo->lineBreaksList.empty())
+                                    if (rDrawPortion && !bParsingFields && 
pExtraInfo && !pExtraInfo->lineBreaksList.empty())
                                     {
                                         bParsingFields = true;
                                         itSubLines = 
pExtraInfo->lineBreaksList.begin();
@@ -3699,7 +3702,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                 if (rTextPortion.IsRightToLeft())
                                     
aRedLineTmpPos.AdjustX(rTextPortion.GetSize().Width() );
 
-                                if ( bStripOnly )
+                                if ( rDrawPortion )
                                 {
                                     EEngineData::WrongSpellVector 
aWrongSpellVector;
 
@@ -3783,14 +3786,16 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                         
ImplCalcDigitLang(aTmpFont.GetLanguage()));
 
                                     // StripPortions() data callback
-                                    GetEditEnginePtr()->DrawingText( aOutPos, 
aText, nTextStart, nTextLen, pDXArray, pKashidaArray,
+                                    const DrawPortionInfo aInfo(
+                                        aOutPos, aText, nTextStart, nTextLen, 
pDXArray, pKashidaArray,
                                         aTmpFont, nParaPortion, 
rTextPortion.GetRightToLeftLevel(),
                                         !aWrongSpellVector.empty() ? 
&aWrongSpellVector : nullptr,
                                         pFieldData,
-                                        bEndOfLine, bEndOfParagraph, // 
support for EOL/EOP TEXT comments
+                                        bEndOfLine, bEndOfParagraph, false, // 
support for EOL/EOP TEXT comments
                                         &aLocale,
                                         aOverlineColor,
                                         aTextLineColor);
+                                    rDrawPortion(aInfo);
 
                                     // #108052# remember that EOP is written 
already for this ParaPortion
                                     if(bEndOfParagraph)
@@ -4015,7 +4020,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                     comphelper::string::padToLength(aBuf, 
nChars, rTextPortion.GetExtraValue());
                                     OUString aText(aBuf.makeStringAndClear());
 
-                                    if ( bStripOnly )
+                                    if ( rDrawPortion )
                                     {
                                         // create EOL and EOP bools
                                         const bool bEndOfLine(nPortion == 
pLine->GetEndPortion());
@@ -4025,15 +4030,16 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                         const Color 
aTextLineColor(rOutDev.GetTextLineColor());
 
                                         // StripPortions() data callback
-                                        GetEditEnginePtr()->DrawingText(
+                                        const DrawPortionInfo aInfo(
                                             aTmpPos, aText, 0, 
aText.getLength(), {}, {},
                                             aTmpFont, nParaPortion, 0,
                                             nullptr,
                                             nullptr,
-                                            bEndOfLine, bEndOfParagraph,
+                                            bEndOfLine, bEndOfParagraph, false,
                                             nullptr,
                                             aOverlineColor,
                                             aTextLineColor);
+                                        rDrawPortion(aInfo);
                                     }
                                     else
                                     {
@@ -4042,7 +4048,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                         rOutDev.DrawStretchText( aTmpPos, 
rTextPortion.GetSize().Width(), aText );
                                     }
                                 }
-                                else if ( bStripOnly )
+                                else if ( rDrawPortion )
                                 {
                                     // #i108052# When stripping, a callback 
for _empty_ paragraphs is also needed.
                                     // This was optimized away (by not 
rendering the space-only tab portion), so do
@@ -4053,15 +4059,16 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                     const Color 
aOverlineColor(rOutDev.GetOverlineColor());
                                     const Color 
aTextLineColor(rOutDev.GetTextLineColor());
 
-                                    GetEditEnginePtr()->DrawingText(
+                                    const DrawPortionInfo aInfo(
                                         aTmpPos, OUString(), 0, 0, {}, {},
                                         aTmpFont, nParaPortion, 0,
                                         nullptr,
                                         nullptr,
-                                        bEndOfLine, bEndOfParagraph,
+                                        bEndOfLine, bEndOfParagraph, false,
                                         nullptr,
                                         aOverlineColor,
                                         aTextLineColor);
+                                    rDrawPortion(aInfo);
                                 }
                             }
                             break;
@@ -4097,20 +4104,21 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
             // that the reason for #i108052# was fixed/removed again, so this 
is a try to fix
             // the number of paragraphs (and counting empty ones) now 
independent from the
             // changes in EditEngine behaviour.
-            if(!bEndOfParagraphWritten && !bPaintBullet && bStripOnly)
+            if(!bEndOfParagraphWritten && !bPaintBullet && rDrawPortion)
             {
                 const Color aOverlineColor(rOutDev.GetOverlineColor());
                 const Color aTextLineColor(rOutDev.GetTextLineColor());
 
-                GetEditEnginePtr()->DrawingText(
+                const DrawPortionInfo aInfo(
                     aTmpPos, OUString(), 0, 0, {}, {},
                     aTmpFont, nParaPortion, 0,
                     nullptr,
                     nullptr,
-                    false, true, // support for EOL/EOP TEXT comments
+                    false, true, false, // support for EOL/EOP TEXT comments
                     nullptr,
                     aOverlineColor,
                     aTextLineColor);
+                rDrawPortion(aInfo);
             }
         }
         else
diff --git a/editeng/source/outliner/outleeng.cxx 
b/editeng/source/outliner/outleeng.cxx
index da297400382b..68e3dcca07ae 100644
--- a/editeng/source/outliner/outleeng.cxx
+++ b/editeng/source/outliner/outleeng.cxx
@@ -39,7 +39,10 @@ OutlinerEditEng::~OutlinerEditEng()
 {
 }
 
-void OutlinerEditEng::PaintingFirstLine(sal_Int32 nPara, const Point& 
rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev)
+void OutlinerEditEng::PaintingFirstLine(sal_Int32 nPara, const Point& 
rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev,
+    const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+    const std::function<void(const DrawBulletInfo&)>& rDrawBullet
+)
 {
     if( GetControlWord() & EEControlBits::OUTLINER )
     {
@@ -47,7 +50,7 @@ void OutlinerEditEng::PaintingFirstLine(sal_Int32 nPara, 
const Point& rStartPos,
         pOwner->maPaintFirstLineHdl.Call( &aInfo );
     }
 
-    pOwner->PaintBullet(nPara, rStartPos, rOrigin, nOrientation, rOutDev);
+    pOwner->PaintBullet(nPara, rStartPos, rOrigin, nOrientation, rOutDev, 
rDrawPortion, rDrawBullet);
 }
 
 const SvxNumberFormat* OutlinerEditEng::GetNumberFormat( sal_Int32 nPara ) 
const
@@ -150,21 +153,6 @@ OUString OutlinerEditEng::GetUndoComment( sal_uInt16 
nUndoId ) const
     }
 }
 
-void OutlinerEditEng::DrawingText( const Point& rStartPos, const OUString& 
rText, sal_Int32 nTextStart, sal_Int32 nTextLen,
-                                   KernArraySpan pDXArray, std::span<const 
sal_Bool> pKashidaArray,
-                                   const SvxFont& rFont, sal_Int32 nPara, 
sal_uInt8 nRightToLeft,
-                                   const EEngineData::WrongSpellVector* 
pWrongSpellVector,
-                                   const SvxFieldData* pFieldData,
-                                   bool bEndOfLine,
-                                   bool bEndOfParagraph,
-                                   const css::lang::Locale* pLocale,
-                                   const Color& rOverlineColor,
-                                   const Color& rTextLineColor)
-{
-    
pOwner->DrawingText(rStartPos,rText,nTextStart,nTextLen,pDXArray,pKashidaArray,rFont,nPara,nRightToLeft,
-        pWrongSpellVector, pFieldData, bEndOfLine, bEndOfParagraph, 
false/*bEndOfBullet*/, pLocale, rOverlineColor, rTextLineColor);
-}
-
 OUString OutlinerEditEng::CalcFieldValue( const SvxFieldItem& rField, 
sal_Int32 nPara, sal_Int32 nPos, std::optional<Color>& rpTxtColor, 
std::optional<Color>& rpFldColor, std::optional<FontLineStyle>& rpFldLineStyle )
 {
     return pOwner->CalcFieldValue( rField, nPara, nPos, rpTxtColor, 
rpFldColor, rpFldLineStyle );
diff --git a/editeng/source/outliner/outliner.cxx 
b/editeng/source/outliner/outliner.cxx
index b1e65c95219f..b4c566b0ee10 100644
--- a/editeng/source/outliner/outliner.cxx
+++ b/editeng/source/outliner/outliner.cxx
@@ -869,8 +869,11 @@ vcl::Font Outliner::ImpCalcBulletFont( sal_Int32 nPara ) 
const
     return aBulletFont;
 }
 
-void Outliner::PaintBullet(sal_Int32 nPara, const Point& rStartPos, const 
Point& rOrigin,
-                           Degree10 nOrientation, OutputDevice& rOutDev)
+void Outliner::PaintBullet(
+    sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin,
+    Degree10 nOrientation, OutputDevice& rOutDev,
+    const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+    const std::function<void(const DrawBulletInfo&)>& rDrawBullet)
 {
 
     bool bDrawBullet = false;
@@ -954,9 +957,9 @@ void Outliner::PaintBullet(sal_Int32 nPara, const Point& 
rStartPos, const Point&
                 nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | 
vcl::text::ComplexTextLayoutFlags::TextOriginLeft | 
vcl::text::ComplexTextLayoutFlags::BiDiStrong;
             rOutDev.SetLayoutMode( nLayoutMode );
 
-            if(bStrippingPortions)
+            if(rDrawPortion)
             {
-                const vcl::Font& aSvxFont(rOutDev.GetFont());
+                const SvxFont aSvxFont(rOutDev.GetFont());
                 KernArray aBuf;
                 rOutDev.GetTextArray( pPara->GetText(), &aBuf );
 
@@ -967,8 +970,10 @@ void Outliner::PaintBullet(sal_Int32 nPara, const Point& 
rStartPos, const Point&
                     aTextPos.AdjustY( -(aMetric.GetDescent()) );
                 }
 
-                DrawingText(aTextPos, pPara->GetText(), 0, 
pPara->GetText().getLength(), aBuf, {},
+                const DrawPortionInfo aInfo(
+                    aTextPos, pPara->GetText(), 0, 
pPara->GetText().getLength(), aBuf, {},
                     aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, 
nullptr, false, false, true, nullptr, Color(), Color());
+                rDrawPortion(aInfo);
             }
             else
             {
@@ -1004,21 +1009,17 @@ void Outliner::PaintBullet(sal_Int32 nPara, const 
Point& rStartPos, const Point&
                     }
                 }
 
-                if(bStrippingPortions)
+                if(rDrawBullet)
                 {
-                    if(aDrawBulletHdl.IsSet())
-                    {
-                        // call something analog to aDrawPortionHdl (if set) 
and feed it something
-                        // analog to DrawPortionInfo...
-                        // created aDrawBulletHdl, Set/GetDrawBulletHdl.
-                        // created DrawBulletInfo and added handling to 
sdrtextdecomposition.cxx
-                        DrawBulletInfo aDrawBulletInfo(
-                            *pFmt->GetBrush()->GetGraphicObject(),
-                            aBulletPos,
-                            pPara->aBulSize);
-
-                        aDrawBulletHdl.Call(&aDrawBulletInfo);
-                    }
+                    // call something analog to aDrawPortionHdl (if set) and 
feed it something
+                    // analog to DrawPortionInfo...
+                    // created aDrawBulletHdl, Set/GetDrawBulletHdl.
+                    // created DrawBulletInfo and added handling to 
sdrtextdecomposition.cxx
+                    DrawBulletInfo aDrawBulletInfo(
+                        *pFmt->GetBrush()->GetGraphicObject(),
+                        aBulletPos,
+                        pPara->aBulSize);
+                    rDrawBullet(aDrawBulletInfo);
                 }
                 else
                 {
@@ -1029,8 +1030,7 @@ void Outliner::PaintBullet(sal_Int32 nPara, const Point& 
rStartPos, const Point&
     }
 
     // In case of collapsed subparagraphs paint a line before the text.
-    if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) 
||
-            bStrippingPortions || nOrientation )
+    if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) 
|| rDrawPortion || nOrientation )
         return;
 
     tools::Long nWidth = rOutDev.PixelToLogic( Size( 10, 0 ) ).Width();
@@ -1231,7 +1231,6 @@ Outliner::Outliner(SfxItemPool* pPool, OutlinerMode nMode)
     , nMaxDepth(9)
     , bFirstParaIsEmpty(true)
     , nBlockInsCallback(0)
-    , bStrippingPortions(false)
     , bPasting(false)
 {
 
@@ -1637,34 +1636,11 @@ void Outliner::Remove( Paragraph const * pPara, 
sal_Int32 nParaCount )
     }
 }
 
-void Outliner::StripPortions()
-{
-    bStrippingPortions = true;
-    pEditEngine->StripPortions();
-    bStrippingPortions = false;
-}
-
-void Outliner::DrawingText( const Point& rStartPos, const OUString& rText, 
sal_Int32 nTextStart,
-                            sal_Int32 nTextLen, std::span<const double> 
pDXArray,
-                            std::span<const sal_Bool> pKashidaArray, const 
SvxFont& rFont,
-                            sal_Int32 nPara, sal_uInt8 nRightToLeft,
-                            const EEngineData::WrongSpellVector* 
pWrongSpellVector,
-                            const SvxFieldData* pFieldData,
-                            bool bEndOfLine,
-                            bool bEndOfParagraph,
-                            bool bEndOfBullet,
-                            const css::lang::Locale* pLocale,
-                            const Color& rOverlineColor,
-                            const Color& rTextLineColor)
+void Outliner::StripPortions(
+    const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+    const std::function<void(const DrawBulletInfo&)>& rDrawBullet)
 {
-    if(aDrawPortionHdl.IsSet())
-    {
-        DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, 
nPara, pDXArray, pKashidaArray, pWrongSpellVector,
-            pFieldData, pLocale, rOverlineColor, rTextLineColor, nRightToLeft,
-            bEndOfLine, bEndOfParagraph, bEndOfBullet);
-
-        aDrawPortionHdl.Call( &aInfo );
-    }
+    pEditEngine->StripPortions(rDrawPortion, rDrawBullet);
 }
 
 bool Outliner::RemovingPagesHdl( OutlinerView* pView )
diff --git a/include/editeng/editeng.hxx b/include/editeng/editeng.hxx
index 8695b3ae0f14..3b0277f9335d 100644
--- a/include/editeng/editeng.hxx
+++ b/include/editeng/editeng.hxx
@@ -114,6 +114,8 @@ class ParaPortionList;
 enum class CharCompressType;
 enum class TransliterationFlags;
 class LinkParamNone;
+class DrawPortionInfo;
+class DrawBulletInfo;
 
 /** values for:
        SfxItemSet GetAttribs( const ESelection& rSel, EditEngineAttribs 
nOnlyHardAttrib = EditEngineAttribs::All );
@@ -367,7 +369,9 @@ public:
 
     bool            IsInSelectionMode() const;
 
-    SAL_DLLPRIVATE void            StripPortions();
+    void            StripPortions(
+        const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+        const std::function<void(const DrawBulletInfo&)>& rDrawBullet);
     void            GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& 
rList );
 
     SAL_DLLPRIVATE tools::Long            GetFirstLineStartX( sal_Int32 
nParagraph );
@@ -488,7 +492,10 @@ public:
     SAL_DLLPRIVATE void            SetBeginPasteOrDropHdl( const 
Link<PasteOrDropInfos&,void>& rLink );
     SAL_DLLPRIVATE void            SetEndPasteOrDropHdl( const 
Link<PasteOrDropInfos&,void>& rLink );
 
-    virtual void    PaintingFirstLine(sal_Int32 nPara, const Point& rStartPos, 
const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev);
+    virtual void    PaintingFirstLine(sal_Int32 nPara, const Point& rStartPos, 
const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev,
+        const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+        const std::function<void(const DrawBulletInfo&)>& rDrawBullet);
+
     virtual void    ParagraphInserted( sal_Int32 nNewParagraph );
     virtual void    ParagraphDeleted( sal_Int32 nDeletedParagraph );
     virtual void    ParagraphConnected( sal_Int32 nLeftParagraph, sal_Int32 
nRightParagraph );
@@ -496,20 +503,6 @@ public:
     virtual void    StyleSheetChanged( SfxStyleSheet* pStyle );
     SAL_DLLPRIVATE void            ParagraphHeightChanged( sal_Int32 nPara );
 
-    virtual void DrawingText( const Point& rStartPos, const OUString& rText,
-                              sal_Int32 nTextStart, sal_Int32 nTextLen,
-                              KernArraySpan pDXArray,
-                              std::span<const sal_Bool> pKashidaArray,
-                              const SvxFont& rFont,
-                              sal_Int32 nPara, sal_uInt8 nRightToLeft,
-                              const EEngineData::WrongSpellVector* 
pWrongSpellVector,
-                              const SvxFieldData* pFieldData,
-                              bool bEndOfLine,
-                              bool bEndOfParagraph,
-                              const css::lang::Locale* pLocale,
-                              const Color& rOverlineColor,
-                              const Color& rTextLineColor);
-
     virtual OUString  GetUndoComment( sal_uInt16 nUndoId ) const;
     virtual bool    SpellNextDocument();
     /** @return true, when click was consumed. false otherwise. */
diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx
index c6363520e1bc..d1c6c840ca07 100644
--- a/include/editeng/outliner.hxx
+++ b/include/editeng/outliner.hxx
@@ -406,22 +406,19 @@ public:
     const OUString      maText;
     sal_Int32           mnTextStart;
     sal_Int32           mnTextLen;
-    sal_Int32           mnPara;
-    const SvxFont&      mrFont;
-    KernArraySpan mpDXArray;
+    KernArraySpan       mpDXArray;
     std::span<const sal_Bool> mpKashidaArray;
-
+    const SvxFont&      mrFont;
+    sal_Int32           mnPara;
+    sal_uInt8           mnBiDiLevel;
     const EEngineData::WrongSpellVector*  mpWrongSpellVector;
     const SvxFieldData* mpFieldData;
-    const css::lang::Locale* mpLocale;
-    const Color         maOverlineColor;
-    const Color         maTextLineColor;
-
-    sal_uInt8           mnBiDiLevel;
-
     bool                mbEndOfLine : 1;
     bool                mbEndOfParagraph : 1;
     bool                mbEndOfBullet : 1;
+    const css::lang::Locale* mpLocale;
+    const Color         maOverlineColor;
+    const Color         maTextLineColor;
 
     bool IsRTL() const { return mnBiDiLevel % 2 == 1; }
 
@@ -430,36 +427,36 @@ public:
         OUString aTxt,
         sal_Int32 nTxtStart,
         sal_Int32 nTxtLen,
-        const SvxFont& rFnt,
-        sal_Int32 nPar,
         KernArraySpan pDXArr,
         std::span<const sal_Bool> pKashidaArr,
+        const SvxFont& rFnt,
+        sal_Int32 nPar,
+        sal_uInt8 nBiDiLevel,
         const EEngineData::WrongSpellVector* pWrongSpellVector,
         const SvxFieldData* pFieldData,
-        const css::lang::Locale* pLocale,
-        const Color& rOverlineColor,
-        const Color& rTextLineColor,
-        sal_uInt8 nBiDiLevel,
         bool bEndOfLine,
         bool bEndOfParagraph,
-        bool bEndOfBullet)
+        bool bEndOfBullet,
+        const css::lang::Locale* pLocale,
+        const Color& rOverlineColor,
+        const Color& rTextLineColor)
     :   mrStartPos(rPos),
         maText(std::move(aTxt)),
         mnTextStart(nTxtStart),
         mnTextLen(nTxtLen),
-        mnPara(nPar),
-        mrFont(rFnt),
         mpDXArray(pDXArr),
         mpKashidaArray(pKashidaArr),
+        mrFont(rFnt),
+        mnPara(nPar),
+        mnBiDiLevel(nBiDiLevel),
         mpWrongSpellVector(pWrongSpellVector),
         mpFieldData(pFieldData),
-        mpLocale(pLocale),
-        maOverlineColor(rOverlineColor),
-        maTextLineColor(rTextLineColor),
-        mnBiDiLevel(nBiDiLevel),
         mbEndOfLine(bEndOfLine),
         mbEndOfParagraph(bEndOfParagraph),
-        mbEndOfBullet(bEndOfBullet)
+        mbEndOfBullet(bEndOfBullet),
+        mpLocale(pLocale),
+        maOverlineColor(rOverlineColor),
+        maTextLineColor(rTextLineColor)
     {}
 };
 
@@ -591,8 +588,6 @@ private:
     ViewList            aViewList;
 
     sal_Int32           mnFirstSelPage;
-    Link<DrawPortionInfo*,void> aDrawPortionHdl;
-    Link<DrawBulletInfo*,void>     aDrawBulletHdl;
     Link<ParagraphHdlParam,void>   aParaInsertedHdl;
     Link<ParagraphHdlParam,void>   aParaRemovingHdl;
     Link<DepthChangeHdlParam,void> aDepthChangedHdl;
@@ -613,7 +608,6 @@ private:
 
     bool                bFirstParaIsEmpty;
     sal_uInt8           nBlockInsCallback;
-    bool                bStrippingPortions;
     bool                bPasting;
 
     Link<EENotify&,void> aOutlinerNotifyHdl;
@@ -657,9 +651,12 @@ protected:
     SAL_DLLPRIVATE void            StyleSheetChanged( SfxStyleSheet const * 
pStyle );
 
     SAL_DLLPRIVATE void            InvalidateBullet(sal_Int32 nPara);
-    SAL_DLLPRIVATE void            PaintBullet(sal_Int32 nPara, const Point& 
rStartPos,
-                                const Point& rOrigin, Degree10 nOrientation,
-                                OutputDevice& rOutDev);
+    SAL_DLLPRIVATE void            PaintBullet(
+        sal_Int32 nPara, const Point& rStartPos,
+        const Point& rOrigin, Degree10 nOrientation,
+        OutputDevice& rOutDev,
+        const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+        const std::function<void(const DrawBulletInfo&)>& rDrawBullet);
 
     // used by OutlinerEditEng. Allows Outliner objects to provide
     // bullet access to the EditEngine.
@@ -781,10 +778,6 @@ public:
     SAL_DLLPRIVATE void            SetCalcFieldValueHdl(const 
Link<EditFieldInfo*,void>& rLink ) { aCalcFieldValueHdl= rLink; }
     SAL_DLLPRIVATE const Link<EditFieldInfo*,void>& GetCalcFieldValueHdl() 
const { return aCalcFieldValueHdl; }
 
-    SAL_DLLPRIVATE void            SetDrawPortionHdl(const 
Link<DrawPortionInfo*,void>& rLink){aDrawPortionHdl=rLink;}
-
-    SAL_DLLPRIVATE void            SetDrawBulletHdl(const 
Link<DrawBulletInfo*,void>& rLink){aDrawBulletHdl=rLink;}
-
     SAL_DLLPRIVATE void            SetPaintFirstLineHdl(const 
Link<PaintFirstLineInfo*,void>& rLink) { maPaintFirstLineHdl = rLink; }
 
     void            SetModifyHdl( const Link<LinkParamNone*,void>& rLink );
@@ -828,22 +821,9 @@ public:
     OUString const & GetWordDelimiters() const;
     OUString        GetWord( const EPaM& rPos );
 
-    void            StripPortions();
-
-    SAL_DLLPRIVATE void DrawingText( const Point& rStartPos, const OUString& 
rText,
-                              sal_Int32 nTextStart, sal_Int32 nTextLen,
-                              KernArraySpan pDXArray,
-                              std::span<const sal_Bool> pKashidaArray,
-                              const SvxFont& rFont,
-                              sal_Int32 nPara, sal_uInt8 nRightToLeft,
-                              const EEngineData::WrongSpellVector* 
pWrongSpellVector,
-                              const SvxFieldData* pFieldData,
-                              bool bEndOfLine,
-                              bool bEndOfParagraph,
-                              bool bEndOfBullet,
-                              const css::lang::Locale* pLocale,
-                              const Color& rOverlineColor,
-                              const Color& rTextLineColor);
+    void            StripPortions(
+        const std::function<void(const DrawPortionInfo&)>& rDrawPortion,
+        const std::function<void(const DrawBulletInfo&)>& rDrawBullet);
 
     Size            CalcTextSize();
 
diff --git a/include/svx/svdotext.hxx b/include/svx/svdotext.hxx
index 899e81de4a0a..dea64305df03 100644
--- a/include/svx/svdotext.hxx
+++ b/include/svx/svdotext.hxx
@@ -116,8 +116,20 @@ namespace sdr::properties
     class CellProperties;
 }
 
-//   SdrTextObj
+class DrawPortionInfo;
+class DrawBulletInfo;
+void SVXCORE_DLLPUBLIC CreateTextPortionPrimitivesFromDrawPortionInfo(
+    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+    const basegfx::B2DHomMatrix& rNewTransformA,
+    const basegfx::B2DHomMatrix& rNewTransformB,
+    const DrawPortionInfo& rInfo);
+void SVXCORE_DLLPUBLIC CreateDrawBulletPrimitivesFromDrawBulletInfo(
+    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+    const basegfx::B2DHomMatrix& rNewTransformA,
+    const basegfx::B2DHomMatrix& rNewTransformB,
+    const DrawBulletInfo& rInfo);
 
+//   SdrTextObj
 class SVXCORE_DLLPUBLIC SdrTextObj : public SdrAttrObj, public 
svx::ITextProvider
 {
 private:
diff --git a/sd/source/ui/view/outlview.cxx b/sd/source/ui/view/outlview.cxx
index 3e713c912dae..f2f5ca1ffe1a 100644
--- a/sd/source/ui/view/outlview.cxx
+++ b/sd/source/ui/view/outlview.cxx
@@ -1300,7 +1300,6 @@ void OutlineView::ResetLinks() const
     mrOutliner.SetStatusEventHdl(Link<EditStatus&,void>());
     mrOutliner.SetRemovingPagesHdl(Link<OutlinerView*,bool>());
     mrOutliner.SetIndentingPagesHdl(Link<OutlinerView*,bool>());
-    mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
     mrOutliner.SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*,void>());
     mrOutliner.SetEndPasteOrDropHdl(Link<PasteOrDropInfos*,void>());
 }
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx 
b/svx/source/svdraw/svdotextdecomposition.cxx
index 8b235a21517a..b8dc0eb0448d 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -67,107 +67,76 @@
 using namespace com::sun::star;
 
 // helpers
-
 namespace
 {
     rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
buildTextPortionPrimitive(const DrawPortionInfo& rInfo, const OUString& rText,
-                                                                               
          const drawinglayer::attribute::FontAttribute& rFontAttribute,
-                                                                               
          const std::vector<double>& rDXArray,
-                                                                               
          const basegfx::B2DHomMatrix& rNewTransform);
+        const drawinglayer::attribute::FontAttribute& rFontAttribute,
+        const std::vector<double>& rDXArray,
+        const basegfx::B2DHomMatrix& rNewTransform);
 
-    class impTextBreakupHandler
+    // static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, 
const DrawPortionInfo& rInfo);
+    rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
CheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, 
const DrawPortionInfo& rInfo)
     {
-    private:
-        drawinglayer::primitive2d::Primitive2DContainer             
maTextPortionPrimitives;
-        drawinglayer::primitive2d::Primitive2DContainer             
maLinePrimitives;
-        drawinglayer::primitive2d::Primitive2DContainer             
maParagraphPrimitives;
-
-        SdrOutliner&                                                mrOutliner;
-        basegfx::B2DHomMatrix                                       
maNewTransformA;
-        basegfx::B2DHomMatrix                                       
maNewTransformB;
-
-        // the visible area for contour text decomposition
-        basegfx::B2DVector                                          maScale;
-
-        // ClipRange for BlockText decomposition; only text portions completely
-        // inside are to be accepted, so this is different from geometric 
clipping
-        // (which would allow e.g. upper parts of portions to remain). Only 
used for
-        // BlockText (see there)
-        basegfx::B2DRange                                           
maClipRange;
-
-        DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo*, void);
-        DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo*, void);
-        DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo*, void);
-
-        DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo*, void);
-        DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo*, void);
-        DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo*, void);
-
-        static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, 
const DrawPortionInfo& rInfo);
-        void impFlushTextPortionPrimitivesToLinePrimitives();
-        void impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara);
-        void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
-        void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
-
-    public:
-        explicit impTextBreakupHandler(SdrOutliner& rOutliner)
-        :   mrOutliner(rOutliner)
+        rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> xRet = 
pPrimitive;
+        if(rInfo.mpFieldData)
         {
-        }
+            // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a 
TextHierarchyFieldPrimitive2D
+            // which holds the field type and, if applicable, the URL
+            const SvxURLField* pURLField = dynamic_cast< const SvxURLField* 
>(rInfo.mpFieldData);
+            const SvxPageField* pPageField = dynamic_cast< const SvxPageField* 
>(rInfo.mpFieldData);
 
-        void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& 
rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const 
basegfx::B2DVector& rScale)
-        {
-            maScale = rScale;
-            maNewTransformA = rNewTransformA;
-            maNewTransformB = rNewTransformB;
-            mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, 
decomposeContourTextPrimitive));
-            mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, 
decomposeContourBulletPrimitive));
-            mrOutliner.StripPortions();
-            mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
-            mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
-        }
+            // embed current primitive to a sequence
+            drawinglayer::primitive2d::Primitive2DContainer aSequence;
 
-        void decomposeBlockTextPrimitive(
-            const basegfx::B2DHomMatrix& rNewTransformA,
-            const basegfx::B2DHomMatrix& rNewTransformB,
-            const basegfx::B2DRange& rClipRange)
-        {
-            maNewTransformA = rNewTransformA;
-            maNewTransformB = rNewTransformB;
-            maClipRange = rClipRange;
-            mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, 
decomposeBlockTextPrimitive));
-            mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, 
decomposeBlockBulletPrimitive));
-            mrOutliner.StripPortions();
-            mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
-            mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
-        }
+            if(pPrimitive)
+            {
+                aSequence.resize(1);
+                aSequence[0] = 
drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
+            }
 
-        void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& 
rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
-        {
-            maNewTransformA = rNewTransformA;
-            maNewTransformB = rNewTransformB;
-            mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, 
decomposeStretchTextPrimitive));
-            mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, 
decomposeStretchBulletPrimitive));
-            mrOutliner.StripPortions();
-            mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
-            mrOutliner.SetDrawBulletHdl(Link<DrawBulletInfo*,void>());
+            if(pURLField)
+            {
+                // 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.emplace_back("URL", pURLField->GetURL());
+                meValues.emplace_back("Representation", 
pURLField->GetRepresentation());
+                meValues.emplace_back("TargetFrame", 
pURLField->GetTargetFrame());
+                meValues.emplace_back("AltText", pURLField->GetName());
+                meValues.emplace_back("SvxURLFormat", 
OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat())));
+                xRet = new 
drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), 
drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues);
+            }
+            else if(pPageField)
+            {
+                xRet = new 
drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), 
drawinglayer::primitive2d::FIELD_TYPE_PAGE);
+            }
+            else
+            {
+                xRet = new 
drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), 
drawinglayer::primitive2d::FIELD_TYPE_COMMON);
+            }
         }
 
-        drawinglayer::primitive2d::Primitive2DContainer 
extractPrimitive2DSequence();
-
-        void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
-    };
+        return xRet;
+    }
 
     class DoCapitalsDrawPortionInfo : public SvxDoCapitals
     {
     private:
-        impTextBreakupHandler& m_rHandler;
+        drawinglayer::primitive2d::Primitive2DContainer& mrTarget;
+        const basegfx::B2DHomMatrix& mrNewTransformA;
+        const basegfx::B2DHomMatrix& mrNewTransformB;
         const DrawPortionInfo& m_rInfo;
         SvxFont m_aFont;
     public:
-        DoCapitalsDrawPortionInfo(impTextBreakupHandler& rHandler, const 
DrawPortionInfo& rInfo)
-            : SvxDoCapitals(rInfo.maText, rInfo.mnTextStart, rInfo.mnTextLen)
-            , m_rHandler(rHandler)
+        DoCapitalsDrawPortionInfo(
+            drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+            const basegfx::B2DHomMatrix& rNewTransformA,
+            const basegfx::B2DHomMatrix& rNewTransformB,
+            const DrawPortionInfo& rInfo)
+        : SvxDoCapitals(rInfo.maText, rInfo.mnTextStart, rInfo.mnTextLen)
+            , mrTarget(rTarget)
+            , mrNewTransformA(rNewTransformA)
+            , mrNewTransformB(rNewTransformB)
             , m_rInfo(rInfo)
             , m_aFont(rInfo.mrFont)
         {
@@ -182,7 +151,7 @@ namespace
             m_aFont.SetCaseMap(SvxCaseMap::NotMapped); /* otherwise this would 
call itself */
         }
         virtual void Do( const OUString &rSpanTxt, const sal_Int32 nSpanIdx,
-                         const sal_Int32 nSpanLen, const bool bUpper ) override
+                        const sal_Int32 nSpanLen, const bool bUpper ) override
         {
             sal_uInt8 nProp(0);
             if (!bUpper)
@@ -206,258 +175,425 @@ namespace
                 std::span<const sal_Bool>();
 
             DrawPortionInfo aInfo(aStartPos, rSpanTxt,
-                                  nSpanIdx, nSpanLen,
-                                  m_aFont, m_rInfo.mnPara,
-                                  aDXArray, aKashidaArray,
-                                  nullptr, /* no spelling in subportion, 
handled outside */
-                                  nullptr, /* no field in subportion, handled 
outside */
-                                  m_rInfo.mpLocale, m_rInfo.maOverlineColor, 
m_rInfo.maTextLineColor,
-                                  m_rInfo.mnBiDiLevel,
-                                  false, false, false);
-
-            m_rHandler.impCreateTextPortionPrimitive(aInfo);
+                                nSpanIdx, nSpanLen,
+                                aDXArray, aKashidaArray,
+                                m_aFont, m_rInfo.mnPara,
+                                m_rInfo.mnBiDiLevel,
+                                nullptr, /* no spelling in subportion, handled 
outside */
+                                nullptr, /* no field in subportion, handled 
outside */
+                                false, false, false,
+                                m_rInfo.mpLocale, m_rInfo.maOverlineColor, 
m_rInfo.maTextLineColor);
+
+            CreateTextPortionPrimitivesFromDrawPortionInfo(
+                mrTarget,
+                mrNewTransformA,
+                mrNewTransformB,
+                aInfo);
 
             if (!bUpper)
                 m_aFont.SetPropr(nProp);
         }
     };
+} // end of anonymous namespace
 
-    void impTextBreakupHandler::impCreateTextPortionPrimitive(const 
DrawPortionInfo& rInfo)
-    {
-        if(rInfo.maText.isEmpty() || !rInfo.mnTextLen)
-            return;
-
-        basegfx::B2DVector aFontScaling;
-        drawinglayer::attribute::FontAttribute aFontAttribute(
-            drawinglayer::primitive2d::getFontAttributeFromVclFont(
-                aFontScaling,
-                rInfo.mrFont,
-                rInfo.IsRTL(),
-                false));
-        basegfx::B2DHomMatrix aNewTransform;
+void CreateTextPortionPrimitivesFromDrawPortionInfo(
+    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+    const basegfx::B2DHomMatrix& rNewTransformA,
+    const basegfx::B2DHomMatrix& rNewTransformB,
+    const DrawPortionInfo& rInfo)
+{
+    if(rInfo.maText.isEmpty() || !rInfo.mnTextLen)
+        return;
 
-        // add font scale to new transform
-        aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
+    basegfx::B2DVector aFontScaling;
+    drawinglayer::attribute::FontAttribute aFontAttribute(
+        drawinglayer::primitive2d::getFontAttributeFromVclFont(
+            aFontScaling,
+            rInfo.mrFont,
+            rInfo.IsRTL(),
+            false));
+    basegfx::B2DHomMatrix aNewTransform;
+
+    // add font scale to new transform
+    aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
+
+    // look for proportional font scaling, if necessary, scale accordingly
+    sal_Int8 nPropr(rInfo.mrFont.GetPropr());
+    const double fPropFontFactor(nPropr / 100.0);
+    if (100 != nPropr)
+        aNewTransform.scale(fPropFontFactor, fPropFontFactor);
+
+    // apply font rotate
+    if(rInfo.mrFont.GetOrientation())
+    {
+        aNewTransform.rotate(-toRadians(rInfo.mrFont.GetOrientation()));
+    }
 
-        // look for proportional font scaling, if necessary, scale accordingly
-        sal_Int8 nPropr(rInfo.mrFont.GetPropr());
-        const double fPropFontFactor(nPropr / 100.0);
-        if (100 != nPropr)
-            aNewTransform.scale(fPropFontFactor, fPropFontFactor);
+    // look for escapement, if necessary, translate accordingly
+    if(rInfo.mrFont.GetEscapement())
+    {
+        sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
 
-        // apply font rotate
-        if(rInfo.mrFont.GetOrientation())
+        if(DFLT_ESC_AUTO_SUPER == nEsc)
         {
-            aNewTransform.rotate(-toRadians(rInfo.mrFont.GetOrientation()));
+            nEsc = .8 * (100 - nPropr);
+            assert (nEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to 
be changed, but how to confirm that???");
+            nEsc = DFLT_ESC_SUPER;
         }
-
-        // look for escapement, if necessary, translate accordingly
-        if(rInfo.mrFont.GetEscapement())
+        else if(DFLT_ESC_AUTO_SUB == nEsc)
         {
-            sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
-
-            if(DFLT_ESC_AUTO_SUPER == nEsc)
-            {
-                nEsc = .8 * (100 - nPropr);
-                assert (nEsc == DFLT_ESC_SUPER && "I'm sure this formula needs 
to be changed, but how to confirm that???");
-                nEsc = DFLT_ESC_SUPER;
-            }
-            else if(DFLT_ESC_AUTO_SUB == nEsc)
-            {
-                nEsc = .2 * -(100 - nPropr);
-                assert (nEsc == -20 && "I'm sure this formula needs to be 
changed, but how to confirm that???");
-                nEsc = -20;
-            }
-
-            if(nEsc > MAX_ESC_POS)
-            {
-                nEsc = MAX_ESC_POS;
-            }
-            else if(nEsc < -MAX_ESC_POS)
-            {
-                nEsc = -MAX_ESC_POS;
-            }
+            nEsc = .2 * -(100 - nPropr);
+            assert (nEsc == -20 && "I'm sure this formula needs to be changed, 
but how to confirm that???");
+            nEsc = -20;
+        }
 
-            const double fEscapement(nEsc / -100.0);
-            aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
+        if(nEsc > MAX_ESC_POS)
+        {
+            nEsc = MAX_ESC_POS;
+        }
+        else if(nEsc < -MAX_ESC_POS)
+        {
+            nEsc = -MAX_ESC_POS;
         }
 
-        // apply transformA
-        aNewTransform *= maNewTransformA;
+        const double fEscapement(nEsc / -100.0);
+        aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
+    }
 
-        // apply local offset
-        aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
+    // apply transformA
+    aNewTransform *= rNewTransformA;
 
-        // also apply embedding object's transform
-        aNewTransform *= maNewTransformB;
+    // apply local offset
+    aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
+
+    // also apply embedding object's transform
+    aNewTransform *= rNewTransformB;
 
-        // prepare DXArray content. To make it independent from font size (and 
such from
-        // the text transformation), scale it to unit coordinates
-        ::std::vector< double > aDXArray;
+    // prepare DXArray content. To make it independent from font size (and 
such from
+    // the text transformation), scale it to unit coordinates
+    ::std::vector< double > aDXArray;
 
-        if (!rInfo.mpDXArray.empty())
+    if (!rInfo.mpDXArray.empty())
+    {
+        aDXArray.reserve(rInfo.mnTextLen);
+        for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
         {
-            aDXArray.reserve(rInfo.mnTextLen);
-            for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
-            {
-                aDXArray.push_back(rInfo.mpDXArray[a]);
-            }
+            aDXArray.push_back(rInfo.mpDXArray[a]);
         }
+    }
 
-        OUString caseMappedText = rInfo.mrFont.CalcCaseMap(rInfo.maText);
-        rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
pNewPrimitive(buildTextPortionPrimitive(rInfo, caseMappedText,
-                                                                               
                            aFontAttribute,
-                                                                               
                            aDXArray, aNewTransform));
+    OUString caseMappedText = rInfo.mrFont.CalcCaseMap(rInfo.maText);
+    rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
pNewPrimitive(buildTextPortionPrimitive(rInfo, caseMappedText,
+                                                                               
                         aFontAttribute,
+                                                                               
                         aDXArray, aNewTransform));
 
-        bool bSmallCaps = rInfo.mrFont.IsCapital();
-        if (bSmallCaps && rInfo.mpDXArray.empty())
-        {
-            SAL_WARN("svx", "SmallCaps requested with DXArray, abandoning");
-            bSmallCaps = false;
-        }
-        if (bSmallCaps)
+    bool bSmallCaps = rInfo.mrFont.IsCapital();
+    if (bSmallCaps && rInfo.mpDXArray.empty())
+    {
+        SAL_WARN("svx", "SmallCaps requested with DXArray, abandoning");
+        bSmallCaps = false;
+    }
+    if (bSmallCaps)
+    {
+        // rerun with each sub-portion
+        DoCapitalsDrawPortionInfo aDoDrawPortionInfo(
+            rTarget,
+            rNewTransformA,
+            rNewTransformB,
+            rInfo);
+        rInfo.mrFont.DoOnCapitals(aDoDrawPortionInfo);
+
+        // transfer collected primitives from rTarget to a new container
+        drawinglayer::primitive2d::Primitive2DContainer aContainer = 
std::move(rTarget);
+
+        // Take any decoration for the whole formatted portion and keep it to 
get continuous over/under/strike-through
+        if (pNewPrimitive->getPrimitive2DID() == 
PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
         {
-            // rerun with each sub-portion
-            DoCapitalsDrawPortionInfo aDoDrawPortionInfo(*this, rInfo);
-            rInfo.mrFont.DoOnCapitals(aDoDrawPortionInfo);
-
-            // transfer collected primitives from maTextPortionPrimitives to a 
new container
-            drawinglayer::primitive2d::Primitive2DContainer aContainer = 
std::move(maTextPortionPrimitives);
+            const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* 
pTCPP =
+                static_cast<const 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>(pNewPrimitive.get());
 
-            // Take any decoration for the whole formatted portion and keep it 
to get continuous over/under/strike-through
-            if (pNewPrimitive->getPrimitive2DID() == 
PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
+            if (pTCPP->getWordLineMode()) // single word mode: 'Individual 
words' in UI
             {
-                const 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* pTCPP =
-                    static_cast<const 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>(pNewPrimitive.get());
-
-                if (pTCPP->getWordLineMode()) // single word mode: 'Individual 
words' in UI
-                {
-                    // Split to single word primitives using TextBreakupHelper
-                    drawinglayer::primitive2d::TextBreakupHelper 
aTextBreakupHelper(*pTCPP);
-                    drawinglayer::primitive2d::Primitive2DContainer 
aBroken(aTextBreakupHelper.extractResult(drawinglayer::primitive2d::BreakupUnit::Word));
-                    for (auto& rPortion : aBroken)
-                    {
-                        assert(rPortion->getPrimitive2DID() == 
PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D &&
-                               "TextBreakupHelper generates same output 
primitive type as input");
-
-                        const 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* pPortion =
-                            static_cast<const 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>(rPortion.get());
-
-                        // create and add decoration
-                        const drawinglayer::primitive2d::Primitive2DContainer& 
rDecorationGeometryContent(
-                            pPortion->getOrCreateDecorationGeometryContent(
-                                pPortion->getTextTransform(),
-                                caseMappedText,
-                                pPortion->getTextPosition(),
-                                pPortion->getTextLength(),
-                                pPortion->getDXArray()));
-
-                        aContainer.insert(aContainer.end(), 
rDecorationGeometryContent.begin(), rDecorationGeometryContent.end());
-                    }
-                }
-                else
+                // Split to single word primitives using TextBreakupHelper
+                drawinglayer::primitive2d::TextBreakupHelper 
aTextBreakupHelper(*pTCPP);
+                drawinglayer::primitive2d::Primitive2DContainer 
aBroken(aTextBreakupHelper.extractResult(drawinglayer::primitive2d::BreakupUnit::Word));
+                for (auto& rPortion : aBroken)
                 {
+                    assert(rPortion->getPrimitive2DID() == 
PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D &&
+                            "TextBreakupHelper generates same output primitive 
type as input");
+
+                    const 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* pPortion =
+                        static_cast<const 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>(rPortion.get());
+
                     // create and add decoration
                     const drawinglayer::primitive2d::Primitive2DContainer& 
rDecorationGeometryContent(
-                        pTCPP->getOrCreateDecorationGeometryContent(
-                            pTCPP->getTextTransform(),
+                        pPortion->getOrCreateDecorationGeometryContent(
+                            pPortion->getTextTransform(),
                             caseMappedText,
-                            rInfo.mnTextStart,
-                            rInfo.mnTextLen,
-                            aDXArray));
+                            pPortion->getTextPosition(),
+                            pPortion->getTextLength(),
+                            pPortion->getDXArray()));
 
                     aContainer.insert(aContainer.end(), 
rDecorationGeometryContent.begin(), rDecorationGeometryContent.end());
                 }
             }
-
-            pNewPrimitive = new 
drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer));
+            else
+            {
+                // create and add decoration
+                const drawinglayer::primitive2d::Primitive2DContainer& 
rDecorationGeometryContent(
+                    pTCPP->getOrCreateDecorationGeometryContent(
+                        pTCPP->getTextTransform(),
+                        caseMappedText,
+                        rInfo.mnTextStart,
+                        rInfo.mnTextLen,
+                        aDXArray));
+
+                aContainer.insert(aContainer.end(), 
rDecorationGeometryContent.begin(), rDecorationGeometryContent.end());
+            }
         }
 
-        const Color aFontColor(rInfo.mrFont.GetColor());
-        if (aFontColor.IsTransparent())
-        {
-            // Handle semi-transparent text for both the decorated and simple 
case here.
-            pNewPrimitive = new 
drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
-                drawinglayer::primitive2d::Primitive2DContainer{ pNewPrimitive 
},
-                (255 - aFontColor.GetAlpha()) / 255.0);
-        }
+        pNewPrimitive = new 
drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer));
+    }
 
-        if(rInfo.mbEndOfBullet)
-        {
-            // embed in TextHierarchyBulletPrimitive2D
-            drawinglayer::primitive2d::Primitive2DContainer aNewSequence { 
pNewPrimitive };
-            pNewPrimitive = new 
drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
-        }
+    const Color aFontColor(rInfo.mrFont.GetColor());
+    if (aFontColor.IsTransparent())
+    {
+        // Handle semi-transparent text for both the decorated and simple case 
here.
+        pNewPrimitive = new 
drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
+            drawinglayer::primitive2d::Primitive2DContainer{ pNewPrimitive },
+            (255 - aFontColor.GetAlpha()) / 255.0);
+    }
 
-        if(rInfo.mpFieldData)
-        {
-            pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive.get(), rInfo);
-        }
+    if(rInfo.mbEndOfBullet)
+    {
+        // embed in TextHierarchyBulletPrimitive2D
+        drawinglayer::primitive2d::Primitive2DContainer aNewSequence { 
pNewPrimitive };
+        pNewPrimitive = new 
drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
+    }
 
-        maTextPortionPrimitives.push_back(pNewPrimitive);
+    if(rInfo.mpFieldData)
+    {
+        pNewPrimitive = CheckFieldPrimitive(pNewPrimitive.get(), rInfo);
+    }
+
+    rTarget.push_back(pNewPrimitive);
 
-        // support for WrongSpellVector. Create WrongSpellPrimitives as needed
-        if(!rInfo.mpWrongSpellVector || aDXArray.empty())
-            return;
+    // support for WrongSpellVector. Create WrongSpellPrimitives as needed
+    if(!rInfo.mpWrongSpellVector || aDXArray.empty())
+        return;
 
-        const sal_Int32 nSize(rInfo.mpWrongSpellVector->size());
-        const sal_Int32 nDXCount(aDXArray.size());
-        const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
+    const sal_Int32 nSize(rInfo.mpWrongSpellVector->size());
+    const sal_Int32 nDXCount(aDXArray.size());
+    const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
+
+    for(sal_Int32 a(0); a < nSize; a++)
+    {
+        const EEngineData::WrongSpellClass& rCandidate = 
(*rInfo.mpWrongSpellVector)[a];
 
-        for(sal_Int32 a(0); a < nSize; a++)
+        if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= 
rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
         {
-            const EEngineData::WrongSpellClass& rCandidate = 
(*rInfo.mpWrongSpellVector)[a];
+            const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart);
+            const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
+            double fStart(0.0);
+            double fEnd(0.0);
 
-            if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= 
rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
+            if(nStart > 0 && nStart - 1 < nDXCount)
             {
-                const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart);
-                const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
-                double fStart(0.0);
-                double fEnd(0.0);
+                fStart = aDXArray[nStart - 1];
+            }
 
-                if(nStart > 0 && nStart - 1 < nDXCount)
+            if(nEnd > 0 && nEnd - 1 < nDXCount)
+            {
+                fEnd = aDXArray[nEnd - 1];
+            }
+
+            if(!basegfx::fTools::equal(fStart, fEnd))
+            {
+                if(rInfo.IsRTL())
                 {
-                    fStart = aDXArray[nStart - 1];
+                    // #i98523#
+                    // When the portion is RTL, mirror the redlining using the
+                    // full portion width
+                    const double fTextWidth(aDXArray[aDXArray.size() - 1]);
+
+                    fStart = fTextWidth - fStart;
+                    fEnd = fTextWidth - fEnd;
                 }
 
-                if(nEnd > 0 && nEnd - 1 < nDXCount)
+                // need to take FontScaling out of values; it's already part of
+                // aNewTransform and would be double applied
+                const double fFontScaleX(aFontScaling.getX() * 
fPropFontFactor);
+
+                if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+                    && !basegfx::fTools::equalZero(fFontScaleX))
                 {
-                    fEnd = aDXArray[nEnd - 1];
+                    fStart /= fFontScaleX;
+                    fEnd /= fFontScaleX;
                 }
 
-                if(!basegfx::fTools::equal(fStart, fEnd))
-                {
-                    if(rInfo.IsRTL())
-                    {
-                        // #i98523#
-                        // When the portion is RTL, mirror the redlining using 
the
-                        // full portion width
-                        const double fTextWidth(aDXArray[aDXArray.size() - 1]);
+                rTarget.push_back(new 
drawinglayer::primitive2d::WrongSpellPrimitive2D(
+                    aNewTransform,
+                    fStart,
+                    fEnd,
+                    aSpellColor));
+            }
+        }
+    }
+}
+
+void CreateDrawBulletPrimitivesFromDrawBulletInfo(
+    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+    const basegfx::B2DHomMatrix& rNewTransformA,
+    const basegfx::B2DHomMatrix& rNewTransformB,
+    const DrawBulletInfo& rInfo)
+{
+    basegfx::B2DHomMatrix aNewTransform;
+
+    // add size to new transform
+    aNewTransform.scale(rInfo.maBulletSize.getWidth(), 
rInfo.maBulletSize.getHeight());
+
+    // apply transformA
+    aNewTransform *= rNewTransformA;
+
+    // apply local offset
+    aNewTransform.translate(rInfo.maBulletPosition.X(), 
rInfo.maBulletPosition.Y());
+
+    // also apply embedding object's transform
+    aNewTransform *= rNewTransformB;
+
+    // prepare empty GraphicAttr
+    const GraphicAttr aGraphicAttr;
+
+    // create GraphicPrimitive2D
+    const drawinglayer::primitive2d::Primitive2DReference aNewReference(new 
drawinglayer::primitive2d::GraphicPrimitive2D(
+        aNewTransform,
+        rInfo.maBulletGraphicObject,
+        aGraphicAttr));
+
+    // embed in TextHierarchyBulletPrimitive2D
+    drawinglayer::primitive2d::Primitive2DContainer aNewSequence { 
aNewReference };
+    rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive = 
new 
drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
+
+    // add to output
+    rTarget.push_back(pNewPrimitive);
+}
+
+namespace
+{
+    class impTextBreakupHandler
+    {
+    private:
+        drawinglayer::primitive2d::Primitive2DContainer             
maTextPortionPrimitives;
+        drawinglayer::primitive2d::Primitive2DContainer             
maLinePrimitives;
+        drawinglayer::primitive2d::Primitive2DContainer             
maParagraphPrimitives;
+
+        SdrOutliner&                                                mrOutliner;
+        basegfx::B2DHomMatrix                                       
maNewTransformA;
+        basegfx::B2DHomMatrix                                       
maNewTransformB;
+
+        // the visible area for contour text decomposition
+        basegfx::B2DVector                                          maScale;
+
+        // ClipRange for BlockText decomposition; only text portions completely
+        // inside are to be accepted, so this is different from geometric 
clipping
+        // (which would allow e.g. upper parts of portions to remain). Only 
used for
+        // BlockText (see there)
+        basegfx::B2DRange                                           
maClipRange;
+
+        void impFlushTextPortionPrimitivesToLinePrimitives();
+        void impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara);
+        void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
+        void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
+
+    public:
+        explicit impTextBreakupHandler(SdrOutliner& rOutliner)
+        :   mrOutliner(rOutliner)
+        {
+        }
 
-                        fStart = fTextWidth - fStart;
-                        fEnd = fTextWidth - fEnd;
+        void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& 
rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const 
basegfx::B2DVector& rScale)
+        {
+            maScale = rScale;
+            maNewTransformA = rNewTransformA;
+            maNewTransformB = rNewTransformB;
+
+            mrOutliner.StripPortions(
+                [this](const DrawPortionInfo& rInfo){
+                    // for contour text, ignore (clip away) all portions which 
are below
+                    // the visible area given by maScale
+                    if(static_cast<double>(rInfo.mrStartPos.Y()) < 
maScale.getY())
+                    {
+                        impHandleDrawPortionInfo(rInfo);
                     }
+                },
+                [this](const DrawBulletInfo& rInfo){ 
impHandleDrawBulletInfo(rInfo); });
+        }
 
-                    // need to take FontScaling out of values; it's already 
part of
-                    // aNewTransform and would be double applied
-                    const double fFontScaleX(aFontScaling.getX() * 
fPropFontFactor);
+        void decomposeBlockTextPrimitive(
+            const basegfx::B2DHomMatrix& rNewTransformA,
+            const basegfx::B2DHomMatrix& rNewTransformB,
+            const basegfx::B2DRange& rClipRange)
+        {
+            maNewTransformA = rNewTransformA;
+            maNewTransformB = rNewTransformB;
+            maClipRange = rClipRange;
 
-                    if(!basegfx::fTools::equal(fFontScaleX, 1.0)
-                        && !basegfx::fTools::equalZero(fFontScaleX))
+            mrOutliner.StripPortions(
+                [this](const DrawPortionInfo& rInfo){
+                    // Is clipping wanted? This is text clipping; only accept 
a portion
+                    // if it's completely in the range
+                    if(!maClipRange.isEmpty())
                     {
-                        fStart /= fFontScaleX;
-                        fEnd /= fFontScaleX;
+                        // Test start position first; this allows to not get 
the text range at
+                        // all if text is far outside
+                        const basegfx::B2DPoint 
aStartPosition(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
+
+                        if(!maClipRange.isInside(aStartPosition))
+                        {
+                            return;
+                        }
+
+                        // Start position is inside. Get TextBoundRect and 
TopLeft next
+                        drawinglayer::primitive2d::TextLayouterDevice 
aTextLayouterDevice;
+                        aTextLayouterDevice.setFont(rInfo.mrFont);
+
+                        const basegfx::B2DRange aTextBoundRect(
+                            aTextLayouterDevice.getTextBoundRect(
+                                rInfo.maText, rInfo.mnTextStart, 
rInfo.mnTextLen));
+                        const basegfx::B2DPoint 
aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
+
+                        if(!maClipRange.isInside(aTopLeft))
+                        {
+                            return;
+                        }
+
+                        // TopLeft is inside. Get BottomRight and check
+                        const basegfx::B2DPoint 
aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
+
+                        if(!maClipRange.isInside(aBottomRight))
+                        {
+                            return;
+                        }
+
+                        // all inside, clip was successful
                     }
+                    impHandleDrawPortionInfo(rInfo);
+                },
+                [this](const DrawBulletInfo& rInfo){ 
impHandleDrawBulletInfo(rInfo); });
+        }
 
-                    maTextPortionPrimitives.push_back(new 
drawinglayer::primitive2d::WrongSpellPrimitive2D(
-                        aNewTransform,
-                        fStart,
-                        fEnd,
-                        aSpellColor));
-                }
-            }
+        void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& 
rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
+        {
+            maNewTransformA = rNewTransformA;
+            maNewTransformB = rNewTransformB;
+
+            mrOutliner.StripPortions(
+                [this](const DrawPortionInfo& rInfo){ 
impHandleDrawPortionInfo(rInfo); },
+                [this](const DrawBulletInfo& rInfo){ 
impHandleDrawBulletInfo(rInfo); });
         }
-    }
+
+        drawinglayer::primitive2d::Primitive2DContainer 
extractPrimitive2DSequence();
+    };
 
     rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
buildTextPortionPrimitive(
             const DrawPortionInfo& rInfo, const OUString& rText,
@@ -598,50 +734,6 @@ namespace
         return pNewPrimitive;
     }
 
-    rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D*
 pPrimitive, const DrawPortionInfo& rInfo)
-    {
-        rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> xRet = 
pPrimitive;
-        if(rInfo.mpFieldData)
-        {
-            // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a 
TextHierarchyFieldPrimitive2D
-            // which holds the field type and, if applicable, the URL
-            const SvxURLField* pURLField = dynamic_cast< const SvxURLField* 
>(rInfo.mpFieldData);
-            const SvxPageField* pPageField = dynamic_cast< const SvxPageField* 
>(rInfo.mpFieldData);
-
-            // embed current primitive to a sequence
-            drawinglayer::primitive2d::Primitive2DContainer aSequence;
-
-            if(pPrimitive)
-            {
-                aSequence.resize(1);
-                aSequence[0] = 
drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
-            }
-
-            if(pURLField)
-            {
-                // 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.emplace_back("URL", pURLField->GetURL());
-                meValues.emplace_back("Representation", 
pURLField->GetRepresentation());
-                meValues.emplace_back("TargetFrame", 
pURLField->GetTargetFrame());
-                meValues.emplace_back("AltText", pURLField->GetName());
-                meValues.emplace_back("SvxURLFormat", 
OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat())));
-                xRet = new 
drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), 
drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues);
-            }
-            else if(pPageField)
-            {
-                xRet = new 
drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), 
drawinglayer::primitive2d::FIELD_TYPE_PAGE);
-            }
-            else
-            {
-                xRet = new 
drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), 
drawinglayer::primitive2d::FIELD_TYPE_COMMON);
-            }
-        }
-
-        return xRet;
-    }
-
     void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
     {
         // only create a line primitive when we had content; there is no need 
for
@@ -671,7 +763,11 @@ namespace
 
     void impTextBreakupHandler::impHandleDrawPortionInfo(const 
DrawPortionInfo& rInfo)
     {
-        impCreateTextPortionPrimitive(rInfo);
+        CreateTextPortionPrimitivesFromDrawPortionInfo(
+            maTextPortionPrimitives,
+            maNewTransformA,
+            maNewTransformB,
+            rInfo);
 
         if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
         {
@@ -686,6 +782,11 @@ namespace
 
     void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& 
rInfo)
     {
+        CreateDrawBulletPrimitivesFromDrawBulletInfo(
+            maTextPortionPrimitives,
+            maNewTransformA,
+            maNewTransformB,
+            rInfo);
         basegfx::B2DHomMatrix aNewTransform;
 
         // add size to new transform
@@ -717,93 +818,6 @@ namespace
         maTextPortionPrimitives.push_back(pNewPrimitive);
     }
 
-    IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, 
DrawPortionInfo*, pInfo, void)
-    {
-        // for contour text, ignore (clip away) all portions which are below
-        // the visible area given by maScale
-        if(pInfo && static_cast<double>(pInfo->mrStartPos.Y()) < 
maScale.getY())
-        {
-            impHandleDrawPortionInfo(*pInfo);
-        }
-    }
-
-    IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, 
DrawPortionInfo*, pInfo, void)
-    {
-        if(!pInfo)
-            return;
-
-        // Is clipping wanted? This is text clipping; only accept a portion
-        // if it's completely in the range
-        if(!maClipRange.isEmpty())
-        {
-            // Test start position first; this allows to not get the text 
range at
-            // all if text is far outside
-            const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), 
pInfo->mrStartPos.Y());
-
-            if(!maClipRange.isInside(aStartPosition))
-            {
-                return;
-            }
-
-            // Start position is inside. Get TextBoundRect and TopLeft next
-            drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
-            aTextLayouterDevice.setFont(pInfo->mrFont);
-
-            const basegfx::B2DRange aTextBoundRect(
-                aTextLayouterDevice.getTextBoundRect(
-                    pInfo->maText, pInfo->mnTextStart, pInfo->mnTextLen));
-            const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + 
aStartPosition);
-
-            if(!maClipRange.isInside(aTopLeft))
-            {
-                return;
-            }
-
-            // TopLeft is inside. Get BottomRight and check
-            const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + 
aStartPosition);
-
-            if(!maClipRange.isInside(aBottomRight))
-            {
-                return;
-            }
-
-            // all inside, clip was successful
-        }
-        impHandleDrawPortionInfo(*pInfo);
-    }
-
-    IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, 
DrawPortionInfo*, pInfo, void)
-    {
-        if(pInfo)
-        {
-            impHandleDrawPortionInfo(*pInfo);
-        }
-    }
-
-    IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, 
DrawBulletInfo*, pInfo, void)
-    {
-        if(pInfo)
-        {
-            impHandleDrawBulletInfo(*pInfo);
-        }
-    }
-
-    IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, 
DrawBulletInfo*, pInfo, void)
-    {
-        if(pInfo)
-        {
-            impHandleDrawBulletInfo(*pInfo);
-        }
-    }
-
-    IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, 
DrawBulletInfo*, pInfo, void)
-    {
-        if(pInfo)
-        {
-            impHandleDrawBulletInfo(*pInfo);
-        }
-    }
-
     drawinglayer::primitive2d::Primitive2DContainer 
impTextBreakupHandler::extractPrimitive2DSequence()
     {
         if(!maTextPortionPrimitives.empty())
@@ -822,9 +836,7 @@ namespace
     }
 } // end of anonymous namespace
 
-
 // primitive decompositions
-
 void SdrTextObj::impDecomposeContourTextPrimitive(
     drawinglayer::primitive2d::Primitive2DContainer& rTarget,
     const drawinglayer::primitive2d::SdrContourTextPrimitive2D& 
rSdrContourTextPrimitive,
diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx 
b/svx/source/svdraw/svdotextpathdecomposition.cxx
index b33d00880aca..142190a60327 100644
--- a/svx/source/svdraw/svdotextpathdecomposition.cxx
+++ b/svx/source/svdraw/svdotextpathdecomposition.cxx
@@ -153,8 +153,6 @@ namespace
         SdrOutliner&                                mrOutliner;
         ::std::vector< impPathTextPortion >         maPathTextPortions;
 
-        DECL_LINK(decompositionPathTextPrimitive, DrawPortionInfo*, void );
-
     public:
         explicit impTextBreakupHandler(SdrOutliner& rOutliner)
         :   mrOutliner(rOutliner)
@@ -164,8 +162,9 @@ namespace
         const ::std::vector< impPathTextPortion >& 
decompositionPathTextPrimitive()
         {
             // strip portions to maPathTextPortions
-            mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, 
decompositionPathTextPrimitive));
-            mrOutliner.StripPortions();
+            mrOutliner.StripPortions(
+                [this](const DrawPortionInfo& rInfo){ 
maPathTextPortions.emplace_back(rInfo); },
+                std::function<void(const DrawBulletInfo&)>());
 
             if(!maPathTextPortions.empty())
             {
@@ -176,11 +175,6 @@ namespace
             return maPathTextPortions;
         }
     };
-
-    IMPL_LINK(impTextBreakupHandler, decompositionPathTextPrimitive, 
DrawPortionInfo*, pInfo, void)
-    {
-        maPathTextPortions.emplace_back(*pInfo);
-    }
 } // end of anonymous namespace
 
 
@@ -734,7 +728,6 @@ void SdrTextObj::impDecomposePathTextPrimitive(
     }
 
     // clean up outliner
-    rOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>());
     rOutliner.Clear();
     rOutliner.setVisualizedPage(nullptr);
 

Reply via email to