editeng/CppunitTest_editeng_core.mk             |    2 
 editeng/CppunitTest_editeng_editeng.mk          |    2 
 editeng/Library_editeng.mk                      |    3 
 editeng/source/editeng/StripPortionsHelper.cxx  |  569 ++++++++++++++++++++++++
 editeng/source/editeng/impedit3.cxx             |    2 
 editeng/source/outliner/outliner.cxx            |    1 
 include/editeng/StripPortionsHelper.hxx         |  126 +++++
 include/editeng/outliner.hxx                    |   80 ---
 include/svx/svdotext.hxx                        |   13 
 sc/source/ui/view/tabview3.cxx                  |    1 
 svx/source/svdraw/svdotextdecomposition.cxx     |  558 -----------------------
 svx/source/svdraw/svdotextpathdecomposition.cxx |    1 
 12 files changed, 710 insertions(+), 648 deletions(-)

New commits:
commit 782131d3a3574290edbe3a15d7f85d7b99dcd3fb
Author:     Armin Le Grand (Collabora) <[email protected]>
AuthorDate: Fri May 30 15:50:41 2025 +0200
Commit:     Armin Le Grand <[email protected]>
CommitDate: Fri Jun 6 11:37:06 2025 +0200

    StripPortions: Move tooling closer to EditEngine/Outliner
    
    EditEngine/Outliner::StripPortions is used with some lambda
    callbacks to convert Text to Primitives. The tooling for this
    is currently located in svx due to historical reasons (when I
    initially did that).
    
    An EditEngine/Outliner gets configured, this is dependent
    of the object type to handle (see TextBreakupHandler in svx
    and other places).
    
    Then the OutlinerParaObject gets set - the model data.
    EditEngine::StripPortions then initially uses
    ImpEditEngine::Paint which layouts all that and formats it.
    For all Text (or Graphics e.g. Bullets) those callbacks are
    used - if given. If not given it *still* does a direct paint
    to a given OutputDevice to stay compatible with legacy
    usages.
    
    Thus ImpEditEngine::Paint is currently kind of an hybrid: It
    needs to be capable of both: For Primitive creation it
    extracts data to DrawPortionInfo or DrawBulletInfo and calls
    the lambda callback where (e.g. in svx) the Primitives are
    created as needed based on that data.
    For Paint that data is used to Paint using the given
    OutputDevice.
    
    This means up to now for doing changes both had to be
    supported in that method which of course hosts the danger
    of getting unequal outputs when not being very careful.
    
    The DirectPaint is now rarely used (AFAIK only in some UI
    elements, e.g. WeldEditView stuff).
    
    The goal is to go in a direction where it would be possible
    to do those remaining Paint calls direcly inside ImpEditEngine
    using primitives, or - alternatively - change all remaining
    places to use a unified StripPortions method and send the
    result to a PrimitiveRenderer or buffer/resue it.
    
    This change is a 1st step to get the preconditions but does
    not change functionality yet. It moves that tooling to where
    it is used.
    
    Change-Id: I7225eab9a0dfd3d50cd3d1d8fe386dfb99ec42ef
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186045
    Reviewed-by: Armin Le Grand <[email protected]>
    Tested-by: Jenkins

diff --git a/editeng/CppunitTest_editeng_core.mk 
b/editeng/CppunitTest_editeng_core.mk
index 64f17ae3ba4a..8852a3c6ffdd 100644
--- a/editeng/CppunitTest_editeng_core.mk
+++ b/editeng/CppunitTest_editeng_core.mk
@@ -29,6 +29,8 @@ $(eval $(call gb_CppunitTest_use_libraries,editeng_core, \
     cppu \
     cppuhelper \
     docmodel \
+       drawinglayercore \
+    drawinglayer \
     i18nlangtag \
     i18nutil \
     lng \
diff --git a/editeng/CppunitTest_editeng_editeng.mk 
b/editeng/CppunitTest_editeng_editeng.mk
index 6c2d20917e86..5e6096adc051 100644
--- a/editeng/CppunitTest_editeng_editeng.mk
+++ b/editeng/CppunitTest_editeng_editeng.mk
@@ -23,6 +23,8 @@ $(eval $(call gb_CppunitTest_use_libraries,editeng_editeng, \
     cppu \
     cppuhelper \
     docmodel \
+       drawinglayercore \
+    drawinglayer \
     i18nlangtag \
     i18nutil \
     lng \
diff --git a/editeng/Library_editeng.mk b/editeng/Library_editeng.mk
index 05ced9a8614a..1def031b017b 100644
--- a/editeng/Library_editeng.mk
+++ b/editeng/Library_editeng.mk
@@ -73,6 +73,7 @@ $(eval $(call gb_Library_add_exception_objects,editeng,\
     editeng/source/editeng/impedit5 \
     editeng/source/editeng/misspellrange \
     editeng/source/editeng/section \
+    editeng/source/editeng/StripPortionsHelper \
     editeng/source/editeng/textconv \
     editeng/source/editeng/TextPortionList \
     editeng/source/items/borderline \
@@ -142,6 +143,8 @@ $(eval $(call gb_Library_use_libraries,editeng,\
     xo \
     basegfx \
     docmodel \
+       drawinglayercore \
+    drawinglayer \
     lng \
     svt \
     tk \
diff --git a/editeng/source/editeng/StripPortionsHelper.cxx 
b/editeng/source/editeng/StripPortionsHelper.cxx
new file mode 100644
index 000000000000..2d9312710860
--- /dev/null
+++ b/editeng/source/editeng/StripPortionsHelper.cxx
@@ -0,0 +1,569 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <editeng/StripPortionsHelper.hxx>
+#include <drawinglayer/attribute/fontattribute.hxx>
+#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
+#include <editeng/escapementitem.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
+#include <editeng/smallcaps.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textbreakuphelper.hxx>
+#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
+#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
+#include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
+
+// anonymous helpers
+namespace
+{
+rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D>
+CheckFieldPrimitive(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;
+}
+
+class DoCapitalsDrawPortionInfo : public SvxDoCapitals
+{
+private:
+    drawinglayer::primitive2d::Primitive2DContainer& mrTarget;
+    const basegfx::B2DHomMatrix& mrNewTransformA;
+    const basegfx::B2DHomMatrix& mrNewTransformB;
+    const DrawPortionInfo& m_rInfo;
+    SvxFont m_aFont;
+
+public:
+    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)
+    {
+        assert(!m_rInfo.mpDXArray.empty());
+
+        /* turn all these off as they are handled outside subportions for the 
whole portion */
+        m_aFont.SetTransparent(false);
+        m_aFont.SetUnderline(LINESTYLE_NONE);
+        m_aFont.SetOverline(LINESTYLE_NONE);
+        m_aFont.SetStrikeout(STRIKEOUT_NONE);
+
+        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
+    {
+        sal_uInt8 nProp(0);
+        if (!bUpper)
+        {
+            nProp = m_aFont.GetPropr();
+            m_aFont.SetProprRel(SMALL_CAPS_PERCENTAGE);
+        }
+
+        sal_Int32 nStartOffset = nSpanIdx - nIdx;
+        double nStartX = nStartOffset ? m_rInfo.mpDXArray[nStartOffset - 1] : 
0;
+
+        Point aStartPos(m_rInfo.mrStartPos.X() + nStartX, 
m_rInfo.mrStartPos.Y());
+
+        KernArray aDXArray;
+        aDXArray.resize(nSpanLen);
+        for (sal_Int32 i = 0; i < nSpanLen; ++i)
+            aDXArray[i] = m_rInfo.mpDXArray[nStartOffset + i] - nStartX;
+
+        auto aKashidaArray = !m_rInfo.mpKashidaArray.empty()
+                                 ? std::span<const sal_Bool>(
+                                       m_rInfo.mpKashidaArray.data() + 
nStartOffset, nSpanLen)
+                                 : std::span<const sal_Bool>();
+
+        DrawPortionInfo aInfo(aStartPos, rSpanTxt, 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);
+    }
+};
+
+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)
+{
+    ::std::vector<sal_Bool> aKashidaArray;
+
+    if (!rInfo.mpKashidaArray.empty() && rInfo.mnTextLen)
+    {
+        aKashidaArray.reserve(rInfo.mnTextLen);
+
+        for (sal_Int32 a = 0; a < rInfo.mnTextLen; a++)
+        {
+            aKashidaArray.push_back(rInfo.mpKashidaArray[a]);
+        }
+    }
+
+    // create complex text primitive and append
+    const Color aFontColor(rInfo.mrFont.GetColor());
+    const basegfx::BColor aBFontColor(aFontColor.getBColor());
+
+    const Color aTextFillColor(rInfo.mrFont.GetFillColor());
+
+    // prepare wordLineMode (for underline and strikeout)
+    // NOT for bullet texts. It is set (this may be an error by itself), but 
needs to be suppressed to hinder e.g. '1)'
+    // to be split which would not look like the original
+    const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && 
!rInfo.mbEndOfBullet);
+
+    // prepare new primitive
+    rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive;
+    const bool bDecoratedIsNeeded(
+        LINESTYLE_NONE != rInfo.mrFont.GetOverline()
+        || LINESTYLE_NONE != rInfo.mrFont.GetUnderline()
+        || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
+        || FontEmphasisMark::NONE != (rInfo.mrFont.GetEmphasisMark() & 
FontEmphasisMark::Style)
+        || FontRelief::NONE != rInfo.mrFont.GetRelief() || 
rInfo.mrFont.IsShadow()
+        || bWordLineMode);
+
+    if (bDecoratedIsNeeded)
+    {
+        // TextDecoratedPortionPrimitive2D needed, prepare some more data
+        // get overline and underline color. If it's on automatic (0xffffffff) 
use FontColor instead
+        const Color aUnderlineColor(rInfo.maTextLineColor);
+        const basegfx::BColor aBUnderlineColor(
+            (aUnderlineColor == COL_AUTO) ? aBFontColor : 
aUnderlineColor.getBColor());
+        const Color aOverlineColor(rInfo.maOverlineColor);
+        const basegfx::BColor aBOverlineColor(
+            (aOverlineColor == COL_AUTO) ? aBFontColor : 
aOverlineColor.getBColor());
+
+        // prepare overline and underline data
+        const drawinglayer::primitive2d::TextLine eFontOverline(
+            
drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetOverline()));
+        const drawinglayer::primitive2d::TextLine eFontLineStyle(
+            
drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetUnderline()));
+
+        // check UnderlineAbove
+        const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE 
!= eFontLineStyle
+                                   && rInfo.mrFont.IsUnderlineAbove());
+
+        // prepare strikeout data
+        const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
+            drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(
+                rInfo.mrFont.GetStrikeout()));
+
+        // prepare emphasis mark data
+        drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(
+            drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);
+
+        switch (rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
+        {
+            case FontEmphasisMark::Dot:
+                eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT;
+                break;
+            case FontEmphasisMark::Circle:
+                eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE;
+                break;
+            case FontEmphasisMark::Disc:
+                eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC;
+                break;
+            case FontEmphasisMark::Accent:
+                eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT;
+                break;
+            default:
+                break;
+        }
+
+        const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & 
FontEmphasisMark::PosAbove);
+        const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & 
FontEmphasisMark::PosBelow);
+
+        // prepare font relief data
+        drawinglayer::primitive2d::TextRelief eTextRelief(
+            drawinglayer::primitive2d::TEXT_RELIEF_NONE);
+
+        switch (rInfo.mrFont.GetRelief())
+        {
+            case FontRelief::Embossed:
+                eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED;
+                break;
+            case FontRelief::Engraved:
+                eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED;
+                break;
+            default:
+                break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
+        }
+
+        // prepare shadow/outline data
+        const bool bShadow(rInfo.mrFont.IsShadow());
+
+        // TextDecoratedPortionPrimitive2D is needed, create one
+        pNewPrimitive = new 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
+
+            // attributes for TextSimplePortionPrimitive2D
+            rNewTransform, rText, rInfo.mnTextStart, rInfo.mnTextLen, 
std::vector(rDXArray),
+            std::move(aKashidaArray), rFontAttribute,
+            rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(), 
aBFontColor, aTextFillColor,
+
+            // attributes for TextDecoratedPortionPrimitive2D
+            aBOverlineColor, aBUnderlineColor, eFontOverline, eFontLineStyle, 
bUnderlineAbove,
+            eTextStrikeout, bWordLineMode, eTextEmphasisMark, 
bEmphasisMarkAbove,
+            bEmphasisMarkBelow, eTextRelief, bShadow);
+    }
+    else
+    {
+        // TextSimplePortionPrimitive2D is enough
+        pNewPrimitive = new 
drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
+            rNewTransform, rText, rInfo.mnTextStart, rInfo.mnTextLen, 
std::vector(rDXArray),
+            std::move(aKashidaArray), rFontAttribute,
+            rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(), 
aBFontColor, aTextFillColor);
+    }
+
+    return pNewPrimitive;
+}
+} // end of anonymous namespace
+
+// Outliner helpers
+void CreateTextPortionPrimitivesFromDrawPortionInfo(
+    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+    const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& 
rNewTransformB,
+    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;
+
+    // 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 escapement, if necessary, translate accordingly
+    if (rInfo.mrFont.GetEscapement())
+    {
+        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;
+        }
+
+        const double fEscapement(nEsc / -100.0);
+        aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
+    }
+
+    // apply transformA
+    aNewTransform *= rNewTransformA;
+
+    // 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;
+
+    if (!rInfo.mpDXArray.empty())
+    {
+        aDXArray.reserve(rInfo.mnTextLen);
+        for (sal_Int32 a = 0; a < rInfo.mnTextLen; 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));
+
+    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)
+        {
+            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
+            {
+                // 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());
+            }
+        }
+
+        pNewPrimitive = new 
drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer));
+    }
+
+    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.mbEndOfBullet)
+    {
+        // embed in TextHierarchyBulletPrimitive2D
+        drawinglayer::primitive2d::Primitive2DContainer aNewSequence{ 
pNewPrimitive };
+        pNewPrimitive = new 
drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(
+            std::move(aNewSequence));
+    }
+
+    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;
+
+    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];
+
+        if (rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= 
rInfo.mnTextStart
+            && rCandidate.nEnd > rCandidate.nStart)
+        {
+            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 (nStart > 0 && nStart - 1 < nDXCount)
+            {
+                fStart = aDXArray[nStart - 1];
+            }
+
+            if (nEnd > 0 && nEnd - 1 < nDXCount)
+            {
+                fEnd = aDXArray[nEnd - 1];
+            }
+
+            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]);
+
+                    fStart = fTextWidth - fStart;
+                    fEnd = fTextWidth - fEnd;
+                }
+
+                // 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))
+                {
+                    fStart /= fFontScaleX;
+                    fEnd /= fFontScaleX;
+                }
+
+                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);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/editeng/source/editeng/impedit3.cxx 
b/editeng/source/editeng/impedit3.cxx
index 01159e457e21..8eb2f8ee73b0 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -47,8 +47,10 @@
 #include <editeng/scriptspaceitem.hxx>
 #include <editeng/charscaleitem.hxx>
 #include <editeng/numitem.hxx>
+#include <editeng/StripPortionsHelper.hxx>
 #include <outleeng.hxx>
 #include <TextPortion.hxx>
+#include <tools/gen.hxx>
 
 #include <svtools/colorcfg.hxx>
 #include <svl/ctloptions.hxx>
diff --git a/editeng/source/outliner/outliner.cxx 
b/editeng/source/outliner/outliner.cxx
index bef30c2ecb01..785ed710ff7d 100644
--- a/editeng/source/outliner/outliner.cxx
+++ b/editeng/source/outliner/outliner.cxx
@@ -50,6 +50,7 @@
 #include <o3tl/string_view.hxx>
 #include <o3tl/temporary.hxx>
 #include <osl/diagnose.h>
+#include <editeng/StripPortionsHelper.hxx>
 
 #include <memory>
 using std::advance;
diff --git a/include/editeng/StripPortionsHelper.hxx 
b/include/editeng/StripPortionsHelper.hxx
new file mode 100644
index 000000000000..c9129cfd0d65
--- /dev/null
+++ b/include/editeng/StripPortionsHelper.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ *   Licensed to the Apache Software Foundation (ASF) under one or more
+ *   contributor license agreements. See the NOTICE file distributed
+ *   with this work for additional information regarding copyright
+ *   ownership. The ASF licenses this file to you under the Apache
+ *   License, Version 2.0 (the "License"); you may not use this file
+ *   except in compliance with the License. You may obtain a copy of
+ *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX
+#define INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX
+
+#include <editeng/editengdllapi.h>
+
+#include <tools/gen.hxx>
+#include <tools/color.hxx>
+#include <rtl/ustring.hxx>
+#include <vcl/kernarray.hxx>
+#include <vcl/GraphicObject.hxx>
+#include <editeng/svxfont.hxx>
+#include <editeng/eedata.hxx>
+#include <editeng/flditem.hxx>
+
+namespace com::sun::star::lang
+{
+struct Locale;
+}
+
+class EDITENG_DLLPUBLIC DrawPortionInfo
+{
+public:
+    const Point& mrStartPos;
+    const OUString maText;
+    sal_Int32 mnTextStart;
+    sal_Int32 mnTextLen;
+    KernArraySpan mpDXArray;
+    std::span<const sal_Bool> mpKashidaArray;
+    const SvxFont& mrFont;
+    sal_Int32 mnPara;
+    sal_uInt8 mnBiDiLevel;
+    const EEngineData::WrongSpellVector* mpWrongSpellVector;
+    const SvxFieldData* mpFieldData;
+    bool mbEndOfLine : 1;
+    bool mbEndOfParagraph : 1;
+    bool mbEndOfBullet : 1;
+    const com::sun::star::lang::Locale* mpLocale;
+    const Color maOverlineColor;
+    const Color maTextLineColor;
+
+    bool IsRTL() const { return mnBiDiLevel % 2 == 1; }
+
+    DrawPortionInfo(const Point& rPos, OUString aTxt, sal_Int32 nTxtStart, 
sal_Int32 nTxtLen,
+                    KernArraySpan pDXArr, std::span<const sal_Bool> 
pKashidaArr,
+                    const SvxFont& rFnt, sal_Int32 nPar, sal_uInt8 nBiDiLevel,
+                    const EEngineData::WrongSpellVector* pWrongSpellVector,
+                    const SvxFieldData* pFieldData, bool bEndOfLine, bool 
bEndOfParagraph,
+                    bool bEndOfBullet, const com::sun::star::lang::Locale* 
pLocale,
+                    const Color& rOverlineColor, const Color& rTextLineColor)
+        : mrStartPos(rPos)
+        , maText(std::move(aTxt))
+        , mnTextStart(nTxtStart)
+        , mnTextLen(nTxtLen)
+        , mpDXArray(pDXArr)
+        , mpKashidaArray(pKashidaArr)
+        , mrFont(rFnt)
+        , mnPara(nPar)
+        , mnBiDiLevel(nBiDiLevel)
+        , mpWrongSpellVector(pWrongSpellVector)
+        , mpFieldData(pFieldData)
+        , mbEndOfLine(bEndOfLine)
+        , mbEndOfParagraph(bEndOfParagraph)
+        , mbEndOfBullet(bEndOfBullet)
+        , mpLocale(pLocale)
+        , maOverlineColor(rOverlineColor)
+        , maTextLineColor(rTextLineColor)
+    {
+    }
+};
+
+class EDITENG_DLLPUBLIC DrawBulletInfo
+{
+public:
+    const GraphicObject maBulletGraphicObject;
+    Point maBulletPosition;
+    Size maBulletSize;
+
+    DrawBulletInfo(const GraphicObject& rBulletGraphicObject, const Point& 
rBulletPosition,
+                   const Size& rBulletSize)
+        : maBulletGraphicObject(rBulletGraphicObject)
+        , maBulletPosition(rBulletPosition)
+        , maBulletSize(rBulletSize)
+    {
+    }
+};
+
+namespace drawinglayer::primitive2d
+{
+class Primitive2DContainer;
+}
+namespace basegfx
+{
+class B2DHomMatrix;
+}
+
+void EDITENG_DLLPUBLIC CreateTextPortionPrimitivesFromDrawPortionInfo(
+    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+    const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& 
rNewTransformB,
+    const DrawPortionInfo& rInfo);
+void EDITENG_DLLPUBLIC CreateDrawBulletPrimitivesFromDrawBulletInfo(
+    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
+    const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& 
rNewTransformB,
+    const DrawBulletInfo& rInfo);
+
+#endif // INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx
index 8be662616ce4..b2eb8b97bc1a 100644
--- a/include/editeng/outliner.hxx
+++ b/include/editeng/outliner.hxx
@@ -80,6 +80,8 @@ enum class PointerStyle;
 class SvxNumRule;
 enum class TextRotation;
 enum class SdrCompatibilityFlag;
+class DrawPortionInfo;
+class DrawBulletInfo;
 
 namespace com::sun::star::linguistic2 {
     class XSpellChecker1;
@@ -399,84 +401,6 @@ void EDITENG_DLLPUBLIC  ReplaceTextWithSynonym( EditView 
&rEditView, const OUStr
 
 typedef ::std::vector< OutlinerView* > ViewList;
 
-class EDITENG_DLLPUBLIC DrawPortionInfo
-{
-public:
-    const Point&        mrStartPos;
-    const OUString      maText;
-    sal_Int32           mnTextStart;
-    sal_Int32           mnTextLen;
-    KernArraySpan       mpDXArray;
-    std::span<const sal_Bool> mpKashidaArray;
-    const SvxFont&      mrFont;
-    sal_Int32           mnPara;
-    sal_uInt8           mnBiDiLevel;
-    const EEngineData::WrongSpellVector*  mpWrongSpellVector;
-    const SvxFieldData* mpFieldData;
-    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; }
-
-    DrawPortionInfo(
-        const Point& rPos,
-        OUString aTxt,
-        sal_Int32 nTxtStart,
-        sal_Int32 nTxtLen,
-        KernArraySpan pDXArr,
-        std::span<const sal_Bool> pKashidaArr,
-        const SvxFont& rFnt,
-        sal_Int32 nPar,
-        sal_uInt8 nBiDiLevel,
-        const EEngineData::WrongSpellVector* pWrongSpellVector,
-        const SvxFieldData* pFieldData,
-        bool bEndOfLine,
-        bool bEndOfParagraph,
-        bool bEndOfBullet,
-        const css::lang::Locale* pLocale,
-        const Color& rOverlineColor,
-        const Color& rTextLineColor)
-    :   mrStartPos(rPos),
-        maText(std::move(aTxt)),
-        mnTextStart(nTxtStart),
-        mnTextLen(nTxtLen),
-        mpDXArray(pDXArr),
-        mpKashidaArray(pKashidaArr),
-        mrFont(rFnt),
-        mnPara(nPar),
-        mnBiDiLevel(nBiDiLevel),
-        mpWrongSpellVector(pWrongSpellVector),
-        mpFieldData(pFieldData),
-        mbEndOfLine(bEndOfLine),
-        mbEndOfParagraph(bEndOfParagraph),
-        mbEndOfBullet(bEndOfBullet),
-        mpLocale(pLocale),
-        maOverlineColor(rOverlineColor),
-        maTextLineColor(rTextLineColor)
-    {}
-};
-
-class EDITENG_DLLPUBLIC DrawBulletInfo
-{
-public:
-    const GraphicObject maBulletGraphicObject;
-    Point               maBulletPosition;
-    Size                maBulletSize;
-
-    DrawBulletInfo(
-        const GraphicObject& rBulletGraphicObject,
-        const Point& rBulletPosition,
-        const Size& rBulletSize)
-    :   maBulletGraphicObject(rBulletGraphicObject),
-        maBulletPosition(rBulletPosition),
-        maBulletSize(rBulletSize)
-    {}
-};
-
 struct EDITENG_DLLPUBLIC PaintFirstLineInfo
 {
     sal_Int32 mnPara;
diff --git a/include/svx/svdotext.hxx b/include/svx/svdotext.hxx
index dea64305df03..5466d7f3b306 100644
--- a/include/svx/svdotext.hxx
+++ b/include/svx/svdotext.hxx
@@ -116,19 +116,6 @@ namespace sdr::properties
     class CellProperties;
 }
 
-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
 {
diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx
index 497da7c9dd5f..13383f15a4c2 100644
--- a/sc/source/ui/view/tabview3.cxx
+++ b/sc/source/ui/view/tabview3.cxx
@@ -22,6 +22,7 @@
 #include <scitems.hxx>
 
 #include <editeng/editview.hxx>
+#include <editeng/StripPortionsHelper.hxx>
 #include <svx/fmshell.hxx>
 #include <svx/sdr/overlay/overlaymanager.hxx>
 #include <svx/svdoole2.hxx>
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx 
b/svx/source/svdraw/svdotextdecomposition.cxx
index b8dc0eb0448d..a90694947368 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -34,14 +34,11 @@
 #include <svx/xbtmpit.hxx>
 #include <basegfx/vector/b2dvector.hxx>
 #include <sdr/primitive2d/sdrtextprimitive2d.hxx>
-#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
-#include <drawinglayer/primitive2d/textbreakuphelper.hxx>
-#include <drawinglayer/primitive2d/textprimitive2d.hxx>
-#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
 #include <basegfx/range/b2drange.hxx>
 #include <editeng/eeitem.hxx>
 #include <editeng/editstat.hxx>
 #include <editeng/smallcaps.hxx>
+#include <editeng/StripPortionsHelper.hxx>
 #include <tools/helpers.hxx>
 #include <svl/itemset.hxx>
 #include <drawinglayer/animation/animationtiming.hxx>
@@ -52,10 +49,8 @@
 #include <editeng/flditem.hxx>
 #include <editeng/adjustitem.hxx>
 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
-#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
-#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
 #include <svx/unoapi.hxx>
 #include <drawinglayer/geometry/viewinformation2d.hxx>
 #include <editeng/outlobj.hxx>
@@ -66,418 +61,6 @@
 
 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);
-
-    // 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)
-    {
-        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;
-    }
-
-    class DoCapitalsDrawPortionInfo : public SvxDoCapitals
-    {
-    private:
-        drawinglayer::primitive2d::Primitive2DContainer& mrTarget;
-        const basegfx::B2DHomMatrix& mrNewTransformA;
-        const basegfx::B2DHomMatrix& mrNewTransformB;
-        const DrawPortionInfo& m_rInfo;
-        SvxFont m_aFont;
-    public:
-        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)
-        {
-            assert(!m_rInfo.mpDXArray.empty());
-
-            /* turn all these off as they are handled outside subportions for 
the whole portion */
-            m_aFont.SetTransparent(false);
-            m_aFont.SetUnderline(LINESTYLE_NONE);
-            m_aFont.SetOverline(LINESTYLE_NONE);
-            m_aFont.SetStrikeout(STRIKEOUT_NONE);
-
-            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
-        {
-            sal_uInt8 nProp(0);
-            if (!bUpper)
-            {
-                nProp = m_aFont.GetPropr();
-                m_aFont.SetProprRel(SMALL_CAPS_PERCENTAGE);
-            }
-
-            sal_Int32 nStartOffset = nSpanIdx - nIdx;
-            double nStartX = nStartOffset ? m_rInfo.mpDXArray[nStartOffset - 
1] : 0;
-
-            Point aStartPos(m_rInfo.mrStartPos.X() + nStartX, 
m_rInfo.mrStartPos.Y());
-
-            KernArray aDXArray;
-            aDXArray.resize(nSpanLen);
-            for (sal_Int32 i = 0; i < nSpanLen; ++i)
-                aDXArray[i] = m_rInfo.mpDXArray[nStartOffset + i] - nStartX;
-
-            auto aKashidaArray = !m_rInfo.mpKashidaArray.empty() ?
-                std::span<const sal_Bool>(m_rInfo.mpKashidaArray.data() + 
nStartOffset, nSpanLen) :
-                std::span<const sal_Bool>();
-
-            DrawPortionInfo aInfo(aStartPos, rSpanTxt,
-                                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 CreateTextPortionPrimitivesFromDrawPortionInfo(
-    drawinglayer::primitive2d::Primitive2DContainer& rTarget,
-    const basegfx::B2DHomMatrix& rNewTransformA,
-    const basegfx::B2DHomMatrix& rNewTransformB,
-    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;
-
-    // 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 escapement, if necessary, translate accordingly
-    if(rInfo.mrFont.GetEscapement())
-    {
-        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;
-        }
-
-        const double fEscapement(nEsc / -100.0);
-        aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
-    }
-
-    // apply transformA
-    aNewTransform *= rNewTransformA;
-
-    // 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;
-
-    if (!rInfo.mpDXArray.empty())
-    {
-        aDXArray.reserve(rInfo.mnTextLen);
-        for(sal_Int32 a=0; a < rInfo.mnTextLen; 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));
-
-    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)
-        {
-            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
-            {
-                // 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());
-            }
-        }
-
-        pNewPrimitive = new 
drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer));
-    }
-
-    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.mbEndOfBullet)
-    {
-        // embed in TextHierarchyBulletPrimitive2D
-        drawinglayer::primitive2d::Primitive2DContainer aNewSequence { 
pNewPrimitive };
-        pNewPrimitive = new 
drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence));
-    }
-
-    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;
-
-    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];
-
-        if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= 
rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
-        {
-            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(nStart > 0 && nStart - 1 < nDXCount)
-            {
-                fStart = aDXArray[nStart - 1];
-            }
-
-            if(nEnd > 0 && nEnd - 1 < nDXCount)
-            {
-                fEnd = aDXArray[nEnd - 1];
-            }
-
-            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]);
-
-                    fStart = fTextWidth - fStart;
-                    fEnd = fTextWidth - fEnd;
-                }
-
-                // 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))
-                {
-                    fStart /= fFontScaleX;
-                    fEnd /= fFontScaleX;
-                }
-
-                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
@@ -595,145 +178,6 @@ namespace
         drawinglayer::primitive2d::Primitive2DContainer 
extractPrimitive2DSequence();
     };
 
-    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)
-    {
-        ::std::vector< sal_Bool > aKashidaArray;
-
-        if(!rInfo.mpKashidaArray.empty() && rInfo.mnTextLen)
-        {
-            aKashidaArray.reserve(rInfo.mnTextLen);
-
-            for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
-            {
-                aKashidaArray.push_back(rInfo.mpKashidaArray[a]);
-            }
-        }
-
-        // create complex text primitive and append
-        const Color aFontColor(rInfo.mrFont.GetColor());
-        const basegfx::BColor aBFontColor(aFontColor.getBColor());
-
-        const Color aTextFillColor(rInfo.mrFont.GetFillColor());
-
-        // prepare wordLineMode (for underline and strikeout)
-        // NOT for bullet texts. It is set (this may be an error by itself), 
but needs to be suppressed to hinder e.g. '1)'
-        // to be split which would not look like the original
-        const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && 
!rInfo.mbEndOfBullet);
-
-        // prepare new primitive
-        rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> 
pNewPrimitive;
-        const bool bDecoratedIsNeeded(
-               LINESTYLE_NONE != rInfo.mrFont.GetOverline()
-            || LINESTYLE_NONE != rInfo.mrFont.GetUnderline()
-            || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
-            || FontEmphasisMark::NONE != (rInfo.mrFont.GetEmphasisMark() & 
FontEmphasisMark::Style)
-            || FontRelief::NONE != rInfo.mrFont.GetRelief()
-            || rInfo.mrFont.IsShadow()
-            || bWordLineMode);
-
-        if(bDecoratedIsNeeded)
-        {
-            // TextDecoratedPortionPrimitive2D needed, prepare some more data
-            // get overline and underline color. If it's on automatic 
(0xffffffff) use FontColor instead
-            const Color aUnderlineColor(rInfo.maTextLineColor);
-            const basegfx::BColor aBUnderlineColor((aUnderlineColor == 
COL_AUTO) ? aBFontColor : aUnderlineColor.getBColor());
-            const Color aOverlineColor(rInfo.maOverlineColor);
-            const basegfx::BColor aBOverlineColor((aOverlineColor == COL_AUTO) 
? aBFontColor : aOverlineColor.getBColor());
-
-            // prepare overline and underline data
-            const drawinglayer::primitive2d::TextLine eFontOverline(
-                
drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetOverline()));
-            const drawinglayer::primitive2d::TextLine eFontLineStyle(
-                
drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetUnderline()));
-
-            // check UnderlineAbove
-            const bool bUnderlineAbove(
-                drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && 
rInfo.mrFont.IsUnderlineAbove());
-
-            // prepare strikeout data
-            const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
-                
drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
-
-            // prepare emphasis mark data
-            drawinglayer::primitive2d::TextEmphasisMark 
eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE);
-
-            switch(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style)
-            {
-                case FontEmphasisMark::Dot : eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break;
-                case FontEmphasisMark::Circle : eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break;
-                case FontEmphasisMark::Disc : eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break;
-                case FontEmphasisMark::Accent : eTextEmphasisMark = 
drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break;
-                default: break;
-            }
-
-            const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & 
FontEmphasisMark::PosAbove);
-            const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & 
FontEmphasisMark::PosBelow);
-
-            // prepare font relief data
-            drawinglayer::primitive2d::TextRelief 
eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
-
-            switch(rInfo.mrFont.GetRelief())
-            {
-                case FontRelief::Embossed : eTextRelief = 
drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
-                case FontRelief::Engraved : eTextRelief = 
drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
-                default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
-            }
-
-            // prepare shadow/outline data
-            const bool bShadow(rInfo.mrFont.IsShadow());
-
-            // TextDecoratedPortionPrimitive2D is needed, create one
-            pNewPrimitive = new 
drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
-
-                // attributes for TextSimplePortionPrimitive2D
-                rNewTransform,
-                rText,
-                rInfo.mnTextStart,
-                rInfo.mnTextLen,
-                std::vector(rDXArray),
-                std::move(aKashidaArray),
-                rFontAttribute,
-                rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
-                aBFontColor,
-                aTextFillColor,
-
-                // attributes for TextDecoratedPortionPrimitive2D
-                aBOverlineColor,
-                aBUnderlineColor,
-                eFontOverline,
-                eFontLineStyle,
-                bUnderlineAbove,
-                eTextStrikeout,
-                bWordLineMode,
-                eTextEmphasisMark,
-                bEmphasisMarkAbove,
-                bEmphasisMarkBelow,
-                eTextRelief,
-                bShadow);
-        }
-        else
-        {
-            // TextSimplePortionPrimitive2D is enough
-            pNewPrimitive = new 
drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
-                rNewTransform,
-                rText,
-                rInfo.mnTextStart,
-                rInfo.mnTextLen,
-                std::vector(rDXArray),
-                std::move(aKashidaArray),
-                rFontAttribute,
-                rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
-                aBFontColor,
-                aTextFillColor);
-        }
-
-        return pNewPrimitive;
-    }
-
     void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
     {
         // only create a line primitive when we had content; there is no need 
for
diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx 
b/svx/source/svdraw/svdotextpathdecomposition.cxx
index 142190a60327..8d3876f57810 100644
--- a/svx/source/svdraw/svdotextpathdecomposition.cxx
+++ b/svx/source/svdraw/svdotextpathdecomposition.cxx
@@ -33,6 +33,7 @@
 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
 #include <basegfx/color/bcolor.hxx>
+#include <editeng/StripPortionsHelper.hxx>
 
 // primitive decomposition helpers
 #include <drawinglayer/attribute/strokeattribute.hxx>

Reply via email to