canvas/source/cairo/cairo_textlayout.cxx                      |    2 
 canvas/source/directx/dx_textlayout_drawhelper.cxx            |    1 
 canvas/source/vcl/textlayout.cxx                              |    1 
 drawinglayer/source/primitive2d/textbreakuphelper.cxx         |   10 +
 drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx  |    7 
 drawinglayer/source/primitive2d/textlayoutdevice.cxx          |    6 
 drawinglayer/source/primitive2d/textprimitive2d.cxx           |   13 -
 drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx  |    1 
 drawinglayer/source/processor2d/vclprocessor2d.cxx            |    3 
 drawinglayer/source/tools/emfphelperdata.cxx                  |    4 
 drawinglayer/source/tools/wmfemfhelper.cxx                    |    7 
 editeng/inc/editdoc.hxx                                       |    4 
 editeng/source/editeng/editeng.cxx                            |    3 
 editeng/source/editeng/impedit3.cxx                           |   33 ++-
 editeng/source/items/svxfont.cxx                              |   13 -
 editeng/source/outliner/outleeng.cxx                          |    5 
 editeng/source/outliner/outleeng.hxx                          |    3 
 editeng/source/outliner/outliner.cxx                          |    9 
 emfio/source/reader/mtftools.cxx                              |    4 
 include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx |    2 
 include/drawinglayer/primitive2d/textlayoutdevice.hxx         |    3 
 include/drawinglayer/primitive2d/textprimitive2d.hxx          |    8 
 include/editeng/editeng.hxx                                   |    4 
 include/editeng/outliner.hxx                                  |    7 
 include/editeng/svxfont.hxx                                   |    3 
 include/vcl/metaact.hxx                                       |   11 -
 include/vcl/outdev.hxx                                        |   12 -
 include/vcl/pdfwriter.hxx                                     |    1 
 include/vcl/rendercontext/SalLayoutFlags.hxx                  |    3 
 sc/source/ui/view/hintwin.cxx                                 |    4 
 sd/source/ui/view/sdview.cxx                                  |    1 
 sfx2/source/control/thumbnailviewitem.cxx                     |    1 
 svgio/source/svgreader/svgcharacternode.cxx                   |    2 
 svtools/source/control/ruler.cxx                              |    4 
 svx/source/diagram/IDiagramHelper.cxx                         |    3 
 svx/source/svdraw/svdotextdecomposition.cxx                   |   14 +
 svx/source/svdraw/svdotextpathdecomposition.cxx               |    5 
 svx/source/tbxctrls/StylesPreviewWindow.cxx                   |    3 
 sw/source/core/inc/scriptinfo.hxx                             |    2 
 sw/source/core/layout/paintfrm.cxx                            |    1 
 sw/source/core/text/itradj.cxx                                |    6 
 sw/source/core/text/porlay.cxx                                |    6 
 sw/source/core/text/portxt.cxx                                |    2 
 sw/source/core/txtnode/fntcache.cxx                           |   32 ++-
 sw/source/uibase/docvw/HeaderFooterWin.cxx                    |    2 
 sw/source/uibase/docvw/UnfloatTableButton.cxx                 |    2 
 vcl/inc/ImplLayoutArgs.hxx                                    |    2 
 vcl/inc/impglyphitem.hxx                                      |    4 
 vcl/inc/pdf/pdfwriter_impl.hxx                                |    2 
 vcl/inc/sallayout.hxx                                         |    2 
 vcl/qa/cppunit/complextext.cxx                                |   37 ---
 vcl/qa/cppunit/svm/svmtest.cxx                                |    2 
 vcl/qa/cppunit/text.cxx                                       |    2 
 vcl/source/control/imp_listbox.cxx                            |    2 
 vcl/source/filter/eps/eps.cxx                                 |   12 -
 vcl/source/filter/svm/SvmConverter.cxx                        |    2 
 vcl/source/gdi/CommonSalLayout.cxx                            |   98 +++-------
 vcl/source/gdi/gdimtf.cxx                                     |    4 
 vcl/source/gdi/impglyphitem.cxx                               |   25 --
 vcl/source/gdi/metaact.cxx                                    |   12 +
 vcl/source/gdi/pdfwriter.cxx                                  |    3 
 vcl/source/gdi/pdfwriter_impl.cxx                             |    8 
 vcl/source/gdi/pdfwriter_impl2.cxx                            |    2 
 vcl/source/gdi/textlayout.cxx                                 |    2 
 vcl/source/outdev/text.cxx                                    |   42 ++--
 vcl/source/outdev/transparent.cxx                             |    2 
 vcl/source/text/ImplLayoutArgs.cxx                            |   32 +++
 vcl/source/window/menuitemlist.cxx                            |    2 
 vcl/source/window/status.cxx                                  |    2 
 69 files changed, 346 insertions(+), 228 deletions(-)

New commits:
commit 3901e029bd39575f700e69a73818565d62226a23
Author:     Khaled Hosny <kha...@aliftype.com>
AuthorDate: Mon Aug 8 22:08:37 2022 +0200
Commit:     Caolán McNamara <caol...@redhat.com>
CommitDate: Sun Aug 14 21:10:24 2022 +0200

    tdf#104921: Cleanup Kashida insertion logic
    
    Communicate Kashida insertion positions in an explicit way.
    
    Rest of LibreOffice communicate adjustments to character widths (e.g.
    for justification or spacing) using so-called DX array. DX array is an
    array of absolute character positions (e.g. DX[n] is the position after
    character n from the start of the lines, and its widths is DX[n] -
    DX[n-1]).
    
    This DX array is modified also when Kashidas are inserted after a given
    character for Arabic justification, by expanding its width. VCL would
    use this to know where to insert the Kashidas and how many ones.
    
    But because DX array is used for both widths adjustments and kashida
    insertion, this turns out to be a source of bugs since VCL has tosecond
    guess the DX array to find which is pure width adjustment and which also
    involves Kashida insertion, and the heuristics it uses are fragile.
    
    This change adds a second array of booleans that records where Kashida
    is inserted and communicates it all the way from where Kashida insertion
    is decoded in Writer and down to VCL layout.
    
    This change passes the Kashida array only when it seems necessary (e.g.
    during drawing but not when measuring text since the DX array is enough
    in this case). Hopefully no places where Kashida insertion needs to be
    passed down were missed.
    
    A couple of glyph and layout flags that were used for old heuristics and
    no longer needed and are removed.
    
    This also fixes:
    tdf#87731
    tdf#106309
    tdf#108604
    tdf#112849
    tdf#114257
    tdf#127176
    tdf#145647
    tdf#146199
    
    Change-Id: I4ed0850ef2fdc3e9143341afac649e7e7d463c39
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138068
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/canvas/source/cairo/cairo_textlayout.cxx 
b/canvas/source/cairo/cairo_textlayout.cxx
index cbbf02c5629d..2b48dd977d52 100644
--- a/canvas/source/cairo/cairo_textlayout.cxx
+++ b/canvas/source/cairo/cairo_textlayout.cxx
@@ -270,7 +270,7 @@ namespace cairocanvas
 
         if (maLogicalAdvancements.hasElements())
         {
-            rOutDev.DrawTextArray( rOutpos, maText.Text, aOffsets,
+            rOutDev.DrawTextArray( rOutpos, maText.Text, aOffsets, {},
                                    
::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
                                    
::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
         }
diff --git a/canvas/source/directx/dx_textlayout_drawhelper.cxx 
b/canvas/source/directx/dx_textlayout_drawhelper.cxx
index 20ff8bd441c9..9e83b77ca901 100644
--- a/canvas/source/directx/dx_textlayout_drawhelper.cxx
+++ b/canvas/source/directx/dx_textlayout_drawhelper.cxx
@@ -215,6 +215,7 @@ namespace dxcanvas
                 xVirtualDevice->DrawTextArray( aEmptyPoint,
                                               aText,
                                               DXArray,
+                                              {},
                                               rText.StartPosition,
                                               rText.Length,
                                               bIsRTL ? SalLayoutFlags::BiDiRtl 
: SalLayoutFlags::NONE);
diff --git a/canvas/source/vcl/textlayout.cxx b/canvas/source/vcl/textlayout.cxx
index 63a3453ff0c4..f628d155f3a7 100644
--- a/canvas/source/vcl/textlayout.cxx
+++ b/canvas/source/vcl/textlayout.cxx
@@ -344,6 +344,7 @@ namespace vclcanvas
             rOutDev.DrawTextArray( rOutpos,
                                    maText.Text,
                                    aOffsets,
+                                   {},
                                    
::canvas::tools::numeric_cast<sal_uInt16>(maText.StartPosition),
                                    
::canvas::tools::numeric_cast<sal_uInt16>(maText.Length) );
         }
diff --git a/drawinglayer/source/primitive2d/textbreakuphelper.cxx 
b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
index 5ca10ce633b6..8f92d9817a0e 100644
--- a/drawinglayer/source/primitive2d/textbreakuphelper.cxx
+++ b/drawinglayer/source/primitive2d/textbreakuphelper.cxx
@@ -58,6 +58,7 @@ namespace drawinglayer::primitive2d
             // prepare values for new portion
             basegfx::B2DHomMatrix aNewTransform;
             std::vector< double > aNewDXArray;
+            std::vector< sal_Bool > aNewKashidaArray;
             const bool bNewStartIsNotOldStart(nIndex > 
mrSource.getTextPosition());
 
             if(!mbNoDXArray)
@@ -68,6 +69,13 @@ namespace drawinglayer::primitive2d
                     mrSource.getDXArray().begin() + ((nIndex + nLength) - 
mrSource.getTextPosition()));
             }
 
+            if(!mbNoDXArray && !mrSource.getKashidaArray().empty())
+            {
+                aNewKashidaArray = std::vector< sal_Bool >(
+                    mrSource.getKashidaArray().begin() + (nIndex - 
mrSource.getTextPosition()),
+                    mrSource.getKashidaArray().begin() + ((nIndex + nLength) - 
mrSource.getTextPosition()));
+            }
+
             if(bNewStartIsNotOldStart)
             {
                 // needs to be moved to a new start position
@@ -137,6 +145,7 @@ namespace drawinglayer::primitive2d
                         nIndex,
                         nLength,
                         std::move(aNewDXArray),
+                        std::move(aNewKashidaArray),
                         mrSource.getFontAttribute(),
                         mrSource.getLocale(),
                         mrSource.getFontColor(),
@@ -168,6 +177,7 @@ namespace drawinglayer::primitive2d
                         nIndex,
                         nLength,
                         std::move(aNewDXArray),
+                        std::move(aNewKashidaArray),
                         mrSource.getFontAttribute(),
                         mrSource.getLocale(),
                         mrSource.getFontColor()));
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx 
b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
index 0db26fbeb28d..b14e6994f7c9 100644
--- a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -36,6 +36,7 @@ namespace drawinglayer::primitive2d
             sal_Int32 nTextPosition,
             sal_Int32 nTextLength,
             const std::vector< double >& rDXArray,
+            const std::vector< sal_Bool >& rKashidaArray,
             const attribute::FontAttribute& rFontAttribute) const
         {
             // create the SimpleTextPrimitive needed in any case
@@ -46,6 +47,7 @@ namespace drawinglayer::primitive2d
                     nTextPosition,
                     nTextLength,
                     std::vector(rDXArray),
+                    std::vector(rKashidaArray),
                     rFontAttribute,
                     getLocale(),
                     getFontColor())));
@@ -189,7 +191,7 @@ namespace drawinglayer::primitive2d
                 getFontAttribute().getBiDiStrong());
 
             // handle as one word
-            impCreateGeometryContent(aRetval, aDecTrans, getText(), 
getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
+            impCreateGeometryContent(aRetval, aDecTrans, getText(), 
getTextPosition(), getTextLength(), getDXArray(), getKashidaArray(), 
aNewFontAttribute);
 
             // Handle Shadow, Outline and TextRelief
             if(!aRetval.empty())
@@ -294,6 +296,7 @@ namespace drawinglayer::primitive2d
             sal_Int32 nTextPosition,
             sal_Int32 nTextLength,
             std::vector< double >&& rDXArray,
+            std::vector< sal_Bool >&& rKashidaArray,
             const attribute::FontAttribute& rFontAttribute,
             const css::lang::Locale& rLocale,
             const basegfx::BColor& rFontColor,
@@ -312,7 +315,7 @@ namespace drawinglayer::primitive2d
             bool bEmphasisMarkBelow,
             TextRelief eTextRelief,
             bool bShadow)
-        :   TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, 
nTextLength, std::move(rDXArray), rFontAttribute, rLocale, rFontColor, false, 
0, rFillColor),
+        :   TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, 
nTextLength, std::move(rDXArray), std::move(rKashidaArray), rFontAttribute, 
rLocale, rFontColor, false, 0, rFillColor),
             maOverlineColor(rOverlineColor),
             maTextlineColor(rTextlineColor),
             meFontOverline(eFontOverline),
diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx 
b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
index f70f9f63b81d..78e0c23189ad 100644
--- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx
+++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
@@ -215,8 +215,8 @@ double TextLayouterDevice::getTextWidth(const OUString& 
rText, sal_uInt32 nIndex
 
 void TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& 
rB2DPolyPolyVector,
                                          const OUString& rText, sal_uInt32 
nIndex,
-                                         sal_uInt32 nLength,
-                                         const std::vector<double>& rDXArray) 
const
+                                         sal_uInt32 nLength, const 
std::vector<double>& rDXArray,
+                                         const std::vector<sal_Bool>& 
rKashidaArray) const
 {
     const sal_uInt32 nDXArrayCount(rDXArray.size());
     sal_uInt32 nTextLength(nLength);
@@ -239,7 +239,7 @@ void 
TextLayouterDevice::getTextOutlines(basegfx::B2DPolyPolygonVector& rB2DPoly
         }
 
         mrDevice.GetTextOutlines(rB2DPolyPolyVector, rText, nIndex, nIndex, 
nLength, 0,
-                                 aIntegerDXArray);
+                                 aIntegerDXArray, rKashidaArray);
     }
     else
     {
diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx 
b/drawinglayer/source/primitive2d/textprimitive2d.cxx
index 6330c89b9184..f60f73b21045 100644
--- a/drawinglayer/source/primitive2d/textprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx
@@ -132,13 +132,13 @@ void 
TextSimplePortionPrimitive2D::getTextOutlinesAndTransformation(
 
         // get the text outlines
         aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), 
getTextLength(),
-                                      aScaledDXArray);
+                                      aScaledDXArray, getKashidaArray());
     }
     else
     {
         // get the text outlines
         aTextLayouter.getTextOutlines(rTarget, getText(), getTextPosition(), 
getTextLength(),
-                                      getDXArray());
+                                      getDXArray(), getKashidaArray());
     }
 
     // create primitives for the outlines
@@ -202,14 +202,16 @@ void TextSimplePortionPrimitive2D::create2DDecomposition(
 
 TextSimplePortionPrimitive2D::TextSimplePortionPrimitive2D(
     basegfx::B2DHomMatrix rNewTransform, OUString rText, sal_Int32 
nTextPosition,
-    sal_Int32 nTextLength, std::vector<double>&& rDXArray, 
attribute::FontAttribute aFontAttribute,
-    css::lang::Locale aLocale, const basegfx::BColor& rFontColor, bool bFilled,
-    tools::Long nWidthToFill, const Color& rTextFillColor)
+    sal_Int32 nTextLength, std::vector<double>&& rDXArray, 
std::vector<sal_Bool>&& rKashidaArray,
+    attribute::FontAttribute aFontAttribute, css::lang::Locale aLocale,
+    const basegfx::BColor& rFontColor, bool bFilled, tools::Long nWidthToFill,
+    const Color& rTextFillColor)
     : maTextTransform(std::move(rNewTransform))
     , maText(std::move(rText))
     , mnTextPosition(nTextPosition)
     , mnTextLength(nTextLength)
     , maDXArray(std::move(rDXArray))
+    , maKashidaArray(std::move(rKashidaArray))
     , maFontAttribute(std::move(aFontAttribute))
     , maLocale(std::move(aLocale))
     , maFontColor(rFontColor)
@@ -241,6 +243,7 @@ bool TextSimplePortionPrimitive2D::operator==(const 
BasePrimitive2D& rPrimitive)
                 && getTextPosition() == rCompare.getTextPosition()
                 && getTextLength() == rCompare.getTextLength()
                 && getDXArray() == rCompare.getDXArray()
+                && getKashidaArray() == rCompare.getKashidaArray()
                 && getFontAttribute() == rCompare.getFontAttribute()
                 && LocalesAreEqual(getLocale(), rCompare.getLocale())
                 && getFontColor() == rCompare.getFontColor() && mbFilled == 
rCompare.mbFilled
diff --git a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx 
b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
index f7aedb3c9172..269be2d01817 100644
--- a/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textstrikeoutprimitive2d.cxx
@@ -97,6 +97,7 @@ namespace drawinglayer::primitive2d
                     0,
                     len,
                     std::move(aDXArray),
+                    {},
                     getFontAttribute(),
                     getLocale(),
                     getFontColor()));
diff --git a/drawinglayer/source/processor2d/vclprocessor2d.cxx 
b/drawinglayer/source/processor2d/vclprocessor2d.cxx
index 019feba35182..822800882d55 100644
--- a/drawinglayer/source/processor2d/vclprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclprocessor2d.cxx
@@ -321,7 +321,8 @@ void 
VclProcessor2D::RenderTextSimpleOrDecoratedPortionPrimitive2D(
 
             if (!aTransformedDXArray.empty())
             {
-                mpOutputDevice->DrawTextArray(aStartPoint, aText, 
aTransformedDXArray, nPos, nLen);
+                mpOutputDevice->DrawTextArray(aStartPoint, aText, 
aTransformedDXArray,
+                                              
rTextCandidate.getKashidaArray(), nPos, nLen);
             }
             else
             {
diff --git a/drawinglayer/source/tools/emfphelperdata.cxx 
b/drawinglayer/source/tools/emfphelperdata.cxx
index 542259568cd0..e9b8422557a9 100644
--- a/drawinglayer/source/tools/emfphelperdata.cxx
+++ b/drawinglayer/source/tools/emfphelperdata.cxx
@@ -1683,6 +1683,7 @@ namespace emfplushelper
                                             0,             // text always 
starts at 0
                                             stringLength,
                                             std::move(emptyVector),   // 
EMF-PLUS has no DX-array
+                                            {},
                                             fontAttribute,
                                             locale,
                                             color.getBColor(), // Font Color
@@ -1702,6 +1703,7 @@ namespace emfplushelper
                                             0,             // text always 
starts at 0
                                             stringLength,
                                             std::move(emptyVector),   // 
EMF-PLUS has no DX-array
+                                            {},
                                             fontAttribute,
                                             locale,
                                             color.getBColor());
@@ -2195,6 +2197,7 @@ namespace emfplushelper
                                                     pos,            // take 
character at current pos
                                                     aLength,        // use 
determined length
                                                     std::move(aDXArray),       
// generated DXArray
+                                                    {},
                                                     fontAttribute,
                                                     
Application::GetSettings().GetLanguageTag().getLocale(),
                                                     color.getBColor(),
@@ -2214,6 +2217,7 @@ namespace emfplushelper
                                                     pos,            // take 
character at current pos
                                                     aLength,        // use 
determined length
                                                     std::move(aDXArray),       
// generated DXArray
+                                                    {},
                                                     fontAttribute,
                                                     
Application::GetSettings().GetLanguageTag().getLocale(),
                                                     color.getBColor());
diff --git a/drawinglayer/source/tools/wmfemfhelper.cxx 
b/drawinglayer/source/tools/wmfemfhelper.cxx
index f763cd887ff7..4ec6863eecea 100644
--- a/drawinglayer/source/tools/wmfemfhelper.cxx
+++ b/drawinglayer/source/tools/wmfemfhelper.cxx
@@ -1077,6 +1077,7 @@ namespace wmfemfhelper
         sal_uInt16 nTextStart,
         sal_uInt16 nTextLength,
         std::vector< double >&& rDXArray,
+        std::vector< sal_Bool >&& rKashidaArray,
         TargetHolder& rTarget,
         PropertyHolder const & rProperty)
     {
@@ -1161,6 +1162,7 @@ namespace wmfemfhelper
                     nTextStart,
                     nTextLength,
                     std::move(rDXArray),
+                    std::move(rKashidaArray),
                     aFontAttribute,
                     aLocale,
                     aFontColor,
@@ -1189,6 +1191,7 @@ namespace wmfemfhelper
                     nTextStart,
                     nTextLength,
                     std::vector(rDXArray),
+                    std::vector(rKashidaArray),
                     std::move(aFontAttribute),
                     std::move(aLocale),
                     aFontColor);
@@ -1770,6 +1773,7 @@ namespace wmfemfhelper
                             nTextIndex,
                             nTextLength,
                             std::move(aDXArray),
+                            {},
                             rTargetHolders.Current(),
                             rPropertyHolders.Current());
                     }
@@ -1794,6 +1798,7 @@ namespace wmfemfhelper
                         // prepare DXArray (if used)
                         std::vector< double > aDXArray;
                         const std::vector<sal_Int32> & rDXArray = 
pA->GetDXArray();
+                        std::vector< sal_Bool > aKashidaArray = 
pA->GetKashidaArray();
 
                         if(!rDXArray.empty())
                         {
@@ -1811,6 +1816,7 @@ namespace wmfemfhelper
                             nTextIndex,
                             nTextLength,
                             std::move(aDXArray),
+                            std::move(aKashidaArray),
                             rTargetHolders.Current(),
                             rPropertyHolders.Current());
                     }
@@ -1874,6 +1880,7 @@ namespace wmfemfhelper
                             nTextIndex,
                             nTextLength,
                             std::move(aTextArray),
+                            {},
                             rTargetHolders.Current(),
                             rPropertyHolders.Current());
                     }
diff --git a/editeng/inc/editdoc.hxx b/editeng/inc/editdoc.hxx
index 6ce00d05c40b..25a3dca4b1fc 100644
--- a/editeng/inc/editdoc.hxx
+++ b/editeng/inc/editdoc.hxx
@@ -463,6 +463,7 @@ public:
 
 private:
     CharPosArrayType aPositions;
+    std::vector<sal_Bool> aKashidaPositions;
     sal_Int32          nTxtWidth;
     sal_Int32          nStartPosX;
     sal_Int32          nStart;     // could be replaced by nStartPortion
@@ -531,6 +532,9 @@ public:
     CharPosArrayType& GetCharPosArray() { return aPositions;}
     const CharPosArrayType& GetCharPosArray() const { return aPositions;}
 
+    std::vector<sal_Bool>& GetKashidaArray() { return aKashidaPositions; }
+    const std::vector<sal_Bool>& GetKashidaArray() const { return 
aKashidaPositions; }
+
     EditLine*       Clone() const;
 
     EditLine&   operator = ( const EditLine& rLine );
diff --git a/editeng/source/editeng/editeng.cxx 
b/editeng/source/editeng/editeng.cxx
index db4ae5db4491..88bc04a9efed 100644
--- a/editeng/source/editeng/editeng.cxx
+++ b/editeng/source/editeng/editeng.cxx
@@ -2465,7 +2465,8 @@ css::uno::Reference< css::datatransfer::XTransferable >
 // ======================    Virtual Methods    ========================
 
 void EditEngine::DrawingText( const Point&, const OUString&, sal_Int32, 
sal_Int32,
-                              o3tl::span<const sal_Int32>, const SvxFont&, 
sal_Int32 /*nPara*/, sal_uInt8 /*nRightToLeft*/,
+                              o3tl::span<const sal_Int32>, o3tl::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&)
 
diff --git a/editeng/source/editeng/impedit3.cxx 
b/editeng/source/editeng/impedit3.cxx
index 143a763208e1..9d71560c108b 100644
--- a/editeng/source/editeng/impedit3.cxx
+++ b/editeng/source/editeng/impedit3.cxx
@@ -2147,6 +2147,10 @@ void ImpEditEngine::ImpAdjustBlocks( ParaPortion* 
pParaPortion, EditLine* pLine,
         nLastScript = nScript;
     }
 
+    // Save the number of blanks, we will use it below when marking Kashida
+    // positions.
+    auto nBlankSize = aPositions.size();
+
     // Kashidas ?
     ImpFindKashidas( pNode, nFirstChar, nLastChar, aPositions );
 
@@ -2186,6 +2190,19 @@ void ImpEditEngine::ImpAdjustBlocks( ParaPortion* 
pParaPortion, EditLine* pLine,
     DBG_ASSERT( nSomeExtraSpace < static_cast<tools::Long>(nGaps), 
"AdjustBlocks: ExtraSpace too large" );
     DBG_ASSERT( nSomeExtraSpace >= 0, "AdjustBlocks: ExtraSpace < 0 " );
 
+    // Mark Kashida positions, so that VCL knows where to insert Kashida and
+    // where to only expand the width.
+    if (aPositions.size() > nBlankSize)
+    {
+        pLine->GetKashidaArray().resize(pLine->GetCharPosArray().size(), 
false);
+        for (auto i = nBlankSize; i < aPositions.size(); i++)
+        {
+            auto nChar = aPositions[i];
+            if ( nChar < nLastChar )
+                pLine->GetKashidaArray()[nChar-nFirstChar] = 1 /*sal_True*/;
+        }
+    }
+
     // Correct the positions in the Array and the portion widths:
     // Last character won't be considered...
     for (auto const& nChar : aPositions)
@@ -2202,7 +2219,6 @@ void ImpEditEngine::ImpAdjustBlocks( ParaPortion* 
pParaPortion, EditLine* pLine,
                 rLastPortion.GetSize().AdjustWidth( 1 );
 
             // Correct positions in array
-            // Even for kashidas just change positions, VCL will then draw the 
kashida automatically
             sal_Int32 nPortionEnd = nPortionStart + rLastPortion.GetLen();
             for ( sal_Int32 _n = nChar; _n < nPortionEnd; _n++ )
             {
@@ -3293,6 +3309,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                 sal_Int32 nTextStart = 0;
                                 sal_Int32 nTextLen = 0;
                                 o3tl::span<const sal_Int32> pDXArray;
+                                o3tl::span<const sal_Bool> pKashidaArray;
                                 std::vector<sal_Int32> aTmpDXArray;
 
                                 if ( rTextPortion.GetKind() == 
PortionKind::TEXT )
@@ -3303,6 +3320,12 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                     pDXArray = 
o3tl::span(pLine->GetCharPosArray().data() + (nIndex - pLine->GetStart()),
                                                     
pLine->GetCharPosArray().size() - (nIndex - pLine->GetStart()));
 
+                                    if (!pLine->GetKashidaArray().empty())
+                                    {
+                                        pKashidaArray = 
o3tl::span(pLine->GetKashidaArray().data() + (nIndex - pLine->GetStart()),
+                                                    
pLine->GetKashidaArray().size() - (nIndex - pLine->GetStart()));
+                                    }
+
                                     // Paint control characters (#i55716#)
                                     /* XXX: Given that there's special handling
                                      * only for some specific characters
@@ -3552,7 +3575,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                         
ImplCalcDigitLang(aTmpFont.GetLanguage()));
 
                                     // StripPortions() data callback
-                                    GetEditEnginePtr()->DrawingText( aOutPos, 
aText, nTextStart, nTextLen, pDXArray,
+                                    GetEditEnginePtr()->DrawingText( aOutPos, 
aText, nTextStart, nTextLen, pDXArray, pKashidaArray,
                                         aTmpFont, n, 
rTextPortion.GetRightToLeftLevel(),
                                         !aWrongSpellVector.empty() ? 
&aWrongSpellVector : nullptr,
                                         pFieldData,
@@ -3656,7 +3679,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                             --nTextLen;
 
                                         // output directly
-                                        aTmpFont.QuickDrawText( &rOutDev, 
aRealOutPos, aText, nTextStart, nTextLen, pDXArray );
+                                        aTmpFont.QuickDrawText( &rOutDev, 
aRealOutPos, aText, nTextStart, nTextLen, pDXArray, pKashidaArray );
 
                                         if ( bDrawFrame )
                                         {
@@ -3792,7 +3815,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                                     const Color 
aTextLineColor(rOutDev.GetTextLineColor());
 
                                     GetEditEnginePtr()->DrawingText(
-                                        aTmpPos, OUString(), 0, 0, {},
+                                        aTmpPos, OUString(), 0, 0, {}, {},
                                         aTmpFont, n, 0,
                                         nullptr,
                                         nullptr,
@@ -3841,7 +3864,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, 
tools::Rectangle aClipRect, Po
                 const Color aTextLineColor(rOutDev.GetTextLineColor());
 
                 GetEditEnginePtr()->DrawingText(
-                    aTmpPos, OUString(), 0, 0, {},
+                    aTmpPos, OUString(), 0, 0, {}, {},
                     aTmpFont, n, 0,
                     nullptr,
                     nullptr,
diff --git a/editeng/source/items/svxfont.cxx b/editeng/source/items/svxfont.cxx
index ca8c5f3fddc6..ac360873824a 100644
--- a/editeng/source/items/svxfont.cxx
+++ b/editeng/source/items/svxfont.cxx
@@ -545,21 +545,24 @@ Size SvxFont::GetTextSize(const OutputDevice& rOut, const 
OUString &rTxt,
 
 static void DrawTextArray( OutputDevice* pOut, const Point& rStartPt, const 
OUString& rStr,
                            o3tl::span<const sal_Int32> pDXAry,
+                           o3tl::span<const sal_Bool> pKashidaAry,
                            sal_Int32 nIndex, sal_Int32 nLen )
 {
     const SalLayoutGlyphs* layoutGlyphs = 
SalLayoutGlyphsCache::self()->GetLayoutGlyphs(pOut, rStr, nIndex, nLen);
-    pOut->DrawTextArray(rStartPt, rStr, pDXAry, nIndex, nLen, 
SalLayoutFlags::NONE, layoutGlyphs);
+    pOut->DrawTextArray(rStartPt, rStr, pDXAry, pKashidaAry, nIndex, nLen, 
SalLayoutFlags::NONE, layoutGlyphs);
 }
 
 void SvxFont::QuickDrawText( OutputDevice *pOut,
     const Point &rPos, const OUString &rTxt,
-    const sal_Int32 nIdx, const sal_Int32 nLen, o3tl::span<const sal_Int32> 
pDXArray ) const
+    const sal_Int32 nIdx, const sal_Int32 nLen,
+    o3tl::span<const sal_Int32> pDXArray,
+    o3tl::span<const sal_Bool> pKashidaArray) const
 {
 
     // Font has to be selected in OutputDevice...
     if ( !IsCaseMap() && !IsCapital() && !IsKern() && !IsEsc() )
     {
-        DrawTextArray( pOut, rPos, rTxt, pDXArray, nIdx, nLen );
+        DrawTextArray( pOut, rPos, rTxt, pDXArray, pKashidaArray, nIdx, nLen );
         return;
     }
 
@@ -596,9 +599,9 @@ void SvxFont::QuickDrawText( OutputDevice *pOut,
         else
         {
             if ( !IsCaseMap() )
-                DrawTextArray( pOut, aPos, rTxt, pDXArray, nIdx, nLen );
+                DrawTextArray( pOut, aPos, rTxt, pDXArray, pKashidaArray, 
nIdx, nLen );
             else
-                DrawTextArray( pOut, aPos, CalcCaseMap( rTxt ), pDXArray, 
nIdx, nLen );
+                DrawTextArray( pOut, aPos, CalcCaseMap( rTxt ), pDXArray, 
pKashidaArray, nIdx, nLen );
         }
     }
 }
diff --git a/editeng/source/outliner/outleeng.cxx 
b/editeng/source/outliner/outleeng.cxx
index e4fc414ad8e3..275636b6581e 100644
--- a/editeng/source/outliner/outleeng.cxx
+++ b/editeng/source/outliner/outleeng.cxx
@@ -141,7 +141,8 @@ OUString OutlinerEditEng::GetUndoComment( sal_uInt16 
nUndoId ) const
 }
 
 void OutlinerEditEng::DrawingText( const Point& rStartPos, const OUString& 
rText, sal_Int32 nTextStart, sal_Int32 nTextLen,
-                                   o3tl::span<const sal_Int32> pDXArray, const 
SvxFont& rFont, sal_Int32 nPara, sal_uInt8 nRightToLeft,
+                                   o3tl::span<const sal_Int32> pDXArray, 
o3tl::span<const sal_Bool> pKashidaArray,
+                                   const SvxFont& rFont, sal_Int32 nPara, 
sal_uInt8 nRightToLeft,
                                    const EEngineData::WrongSpellVector* 
pWrongSpellVector,
                                    const SvxFieldData* pFieldData,
                                    bool bEndOfLine,
@@ -150,7 +151,7 @@ void OutlinerEditEng::DrawingText( const Point& rStartPos, 
const OUString& rText
                                    const Color& rOverlineColor,
                                    const Color& rTextLineColor)
 {
-    
pOwner->DrawingText(rStartPos,rText,nTextStart,nTextLen,pDXArray,rFont,nPara,nRightToLeft,
+    
pOwner->DrawingText(rStartPos,rText,nTextStart,nTextLen,pDXArray,pKashidaArray,rFont,nPara,nRightToLeft,
         pWrongSpellVector, pFieldData, bEndOfLine, bEndOfParagraph, 
false/*bEndOfBullet*/, pLocale, rOverlineColor, rTextLineColor);
 }
 
diff --git a/editeng/source/outliner/outleeng.hxx 
b/editeng/source/outliner/outleeng.hxx
index 963e74582ec0..d19b54eba06a 100644
--- a/editeng/source/outliner/outleeng.hxx
+++ b/editeng/source/outliner/outleeng.hxx
@@ -44,7 +44,8 @@ public:
     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, o3tl::span<const sal_Int32> 
pDXArray, const SvxFont& rFont,
+                              sal_Int32 nTextLen, o3tl::span<const sal_Int32> 
pDXArray,
+                              o3tl::span<const sal_Bool> pKashidaArray, const 
SvxFont& rFont,
                               sal_Int32 nPara, sal_uInt8 nRightToLeft,
                               const EEngineData::WrongSpellVector* 
pWrongSpellVector,
                               const SvxFieldData* pFieldData,
diff --git a/editeng/source/outliner/outliner.cxx 
b/editeng/source/outliner/outliner.cxx
index 4d67810c7a77..d7ea27662e77 100644
--- a/editeng/source/outliner/outliner.cxx
+++ b/editeng/source/outliner/outliner.cxx
@@ -974,7 +974,7 @@ void Outliner::PaintBullet(sal_Int32 nPara, const Point& 
rStartPos, const Point&
                     aTextPos.AdjustY( -(aMetric.GetDescent()) );
                 }
 
-                DrawingText(aTextPos, pPara->GetText(), 0, 
pPara->GetText().getLength(), aBuf,
+                DrawingText(aTextPos, pPara->GetText(), 0, 
pPara->GetText().getLength(), aBuf, {},
                     aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, 
nullptr, false, false, true, nullptr, Color(), Color());
             }
             else
@@ -1650,7 +1650,8 @@ void Outliner::StripPortions()
 }
 
 void Outliner::DrawingText( const Point& rStartPos, const OUString& rText, 
sal_Int32 nTextStart,
-                            sal_Int32 nTextLen, o3tl::span<const sal_Int32> 
pDXArray,const SvxFont& rFont,
+                            sal_Int32 nTextLen, o3tl::span<const sal_Int32> 
pDXArray,
+                            o3tl::span<const sal_Bool> pKashidaArray, const 
SvxFont& rFont,
                             sal_Int32 nPara, sal_uInt8 nRightToLeft,
                             const EEngineData::WrongSpellVector* 
pWrongSpellVector,
                             const SvxFieldData* pFieldData,
@@ -1663,7 +1664,7 @@ void Outliner::DrawingText( const Point& rStartPos, const 
OUString& rText, sal_I
 {
     if(aDrawPortionHdl.IsSet())
     {
-        DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, 
nPara, pDXArray, pWrongSpellVector,
+        DrawPortionInfo aInfo( rStartPos, rText, nTextStart, nTextLen, rFont, 
nPara, pDXArray, pKashidaArray, pWrongSpellVector,
             pFieldData, pLocale, rOverlineColor, rTextLineColor, nRightToLeft, 
false, 0, bEndOfLine, bEndOfParagraph, bEndOfBullet);
 
         aDrawPortionHdl.Call( &aInfo );
@@ -1676,7 +1677,7 @@ void Outliner::DrawingTab( const Point& rStartPos, 
tools::Long nWidth, const OUS
 {
     if(aDrawPortionHdl.IsSet())
     {
-        DrawPortionInfo aInfo( rStartPos, rChar, 0, rChar.getLength(), rFont, 
nPara, {}, nullptr,
+        DrawPortionInfo aInfo( rStartPos, rChar, 0, rChar.getLength(), rFont, 
nPara, {}, {}, nullptr,
             nullptr, nullptr, rOverlineColor, rTextLineColor, nRightToLeft, 
true, nWidth, bEndOfLine, bEndOfParagraph, false);
 
         aDrawPortionHdl.Call( &aInfo );
diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx
index 67dfdbeaedf0..95c51188bc3f 100644
--- a/emfio/source/reader/mtftools.cxx
+++ b/emfio/source/reader/mtftools.cxx
@@ -1828,7 +1828,7 @@ namespace emfio
                 {
                     Point aCharDisplacement( i ? (*pDXArry)[i-1] : 0, i ? 
pDYArry[i-1] : 0 );
                     Point().RotateAround(aCharDisplacement, 
maFont.GetOrientation());
-                    mpGDIMetaFile->AddAction( new MetaTextArrayAction( 
rPosition + aCharDisplacement, OUString( rText[i] ), o3tl::span<const 
sal_Int32>{}, 0, 1 ) );
+                    mpGDIMetaFile->AddAction( new MetaTextArrayAction( 
rPosition + aCharDisplacement, OUString( rText[i] ), o3tl::span<const 
sal_Int32>{}, {}, 0, 1 ) );
                 }
             }
             else
@@ -1859,7 +1859,7 @@ namespace emfio
                     pVDev->GetTextArray( rText, &aMyDXArray, 0, 
rText.getLength());
                     pDX = aMyDXArray;
                 }
-                mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, 
rText, pDX, 0, rText.getLength() ) );
+                mpGDIMetaFile->AddAction( new MetaTextArrayAction( rPosition, 
rText, pDX, {}, 0, rText.getLength() ) );
             }
         }
         SetGfxMode( nOldGfxMode );
diff --git a/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx 
b/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx
index 1a4d821c7e60..73663ff95d32 100644
--- a/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/textdecoratedprimitive2d.hxx
@@ -63,6 +63,7 @@ namespace drawinglayer::primitive2d
                 sal_Int32 nTextPosition,
                 sal_Int32 nTextLength,
                 const ::std::vector< double >& rDXArray,
+                const ::std::vector< sal_Bool >& rKashidaArray,
                 const attribute::FontAttribute& rFontAttribute) const;
 
             /// local decomposition.
@@ -77,6 +78,7 @@ namespace drawinglayer::primitive2d
                 sal_Int32 nTextPosition,
                 sal_Int32 nTextLength,
                 std::vector< double >&& rDXArray,
+                std::vector< sal_Bool >&& rKashidaArray,
                 const attribute::FontAttribute& rFontAttribute,
                 const css::lang::Locale& rLocale,
                 const basegfx::BColor& rFontColor,
diff --git a/include/drawinglayer/primitive2d/textlayoutdevice.hxx 
b/include/drawinglayer/primitive2d/textlayoutdevice.hxx
index 93587769c449..6348de0ddd25 100644
--- a/include/drawinglayer/primitive2d/textlayoutdevice.hxx
+++ b/include/drawinglayer/primitive2d/textlayoutdevice.hxx
@@ -84,7 +84,8 @@ public:
     double getTextWidth(const OUString& rText, sal_uInt32 nIndex, sal_uInt32 
nLength) const;
 
     void getTextOutlines(basegfx::B2DPolyPolygonVector&, const OUString& 
rText, sal_uInt32 nIndex,
-                         sal_uInt32 nLength, const ::std::vector<double>& 
rDXArray) const;
+                         sal_uInt32 nLength, const ::std::vector<double>& 
rDXArray,
+                         const ::std::vector<sal_Bool>& rKashidaArray) const;
 
     basegfx::B2DRange getTextBoundRect(const OUString& rText, sal_uInt32 
nIndex,
                                        sal_uInt32 nLength) const;
diff --git a/include/drawinglayer/primitive2d/textprimitive2d.hxx 
b/include/drawinglayer/primitive2d/textprimitive2d.hxx
index fd80e531e910..4182b0c0fbe4 100644
--- a/include/drawinglayer/primitive2d/textprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/textprimitive2d.hxx
@@ -72,6 +72,9 @@ namespace drawinglayer::primitive2d
     point in FontCoordinateSystem X-Direction (given by maTextTransform) to 
the start
     point of the second character
 
+    @param rKashidaArray
+    The Kashida insertion positions.
+
     @param rFontAttribute
     The font definition
 
@@ -107,6 +110,9 @@ private:
     /// The DX array in logic units
     std::vector<double> maDXArray;
 
+    /// The Kashida array
+    std::vector<sal_Bool> maKashidaArray;
+
     /// The font definition
     attribute::FontAttribute maFontAttribute;
 
@@ -139,6 +145,7 @@ public:
     TextSimplePortionPrimitive2D(basegfx::B2DHomMatrix aNewTransform, OUString 
aText,
                                  sal_Int32 nTextPosition, sal_Int32 
nTextLength,
                                  std::vector<double>&& rDXArray,
+                                 std::vector<sal_Bool>&& rKashidaArray,
                                  attribute::FontAttribute aFontAttribute, 
css::lang::Locale aLocale,
                                  const basegfx::BColor& rFontColor, bool 
bFilled = false,
                                  tools::Long nWidthToFill = 0,
@@ -156,6 +163,7 @@ public:
     sal_Int32 getTextPosition() const { return mnTextPosition; }
     sal_Int32 getTextLength() const { return mnTextLength; }
     const ::std::vector<double>& getDXArray() const { return maDXArray; }
+    const ::std::vector<sal_Bool>& getKashidaArray() const { return 
maKashidaArray; }
     const attribute::FontAttribute& getFontAttribute() const { return 
maFontAttribute; }
     const css::lang::Locale& getLocale() const { return maLocale; }
     const basegfx::BColor& getFontColor() const { return maFontColor; }
diff --git a/include/editeng/editeng.hxx b/include/editeng/editeng.hxx
index a0ce29ca26d1..ae0990666ded 100644
--- a/include/editeng/editeng.hxx
+++ b/include/editeng/editeng.hxx
@@ -498,7 +498,9 @@ public:
 
     virtual void DrawingText( const Point& rStartPos, const OUString& rText,
                               sal_Int32 nTextStart, sal_Int32 nTextLen,
-                              o3tl::span<const sal_Int32> pDXArray, const 
SvxFont& rFont,
+                              o3tl::span<const sal_Int32> pDXArray,
+                              o3tl::span<const sal_Bool> pKashidaArray,
+                              const SvxFont& rFont,
                               sal_Int32 nPara, sal_uInt8 nRightToLeft,
                               const EEngineData::WrongSpellVector* 
pWrongSpellVector,
                               const SvxFieldData* pFieldData,
diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx
index 6e19aa129a78..3326a3d6662b 100644
--- a/include/editeng/outliner.hxx
+++ b/include/editeng/outliner.hxx
@@ -401,6 +401,7 @@ public:
     sal_Int32           mnPara;
     const SvxFont&      mrFont;
     o3tl::span<const sal_Int32> mpDXArray;
+    o3tl::span<const sal_Bool> mpKashidaArray;
 
     const EEngineData::WrongSpellVector*  mpWrongSpellVector;
     const SvxFieldData* mpFieldData;
@@ -427,6 +428,7 @@ public:
         const SvxFont& rFnt,
         sal_Int32 nPar,
         o3tl::span<const sal_Int32> pDXArr,
+        o3tl::span<const sal_Bool> pKashidaArr,
         const EEngineData::WrongSpellVector* pWrongSpellVector,
         const SvxFieldData* pFieldData,
         const css::lang::Locale* pLocale,
@@ -445,6 +447,7 @@ public:
         mnPara(nPar),
         mrFont(rFnt),
         mpDXArray(pDXArr),
+        mpKashidaArray(pKashidaArr),
         mpWrongSpellVector(pWrongSpellVector),
         mpFieldData(pFieldData),
         mpLocale(pLocale),
@@ -818,7 +821,9 @@ public:
 
     void DrawingText( const Point& rStartPos, const OUString& rText,
                               sal_Int32 nTextStart, sal_Int32 nTextLen,
-                              o3tl::span<const sal_Int32> pDXArray, const 
SvxFont& rFont,
+                              o3tl::span<const sal_Int32> pDXArray,
+                              o3tl::span<const sal_Bool> pKashidaArray,
+                              const SvxFont& rFont,
                               sal_Int32 nPara, sal_uInt8 nRightToLeft,
                               const EEngineData::WrongSpellVector* 
pWrongSpellVector,
                               const SvxFieldData* pFieldData,
diff --git a/include/editeng/svxfont.hxx b/include/editeng/svxfont.hxx
index e0f92c57289d..ac4ec390128a 100644
--- a/include/editeng/svxfont.hxx
+++ b/include/editeng/svxfont.hxx
@@ -96,7 +96,8 @@ public:
 
     void QuickDrawText( OutputDevice *pOut, const Point &rPos, const OUString 
&rTxt,
                         const sal_Int32 nIdx = 0, const sal_Int32 nLen = 
SAL_MAX_INT32,
-                        o3tl::span<const sal_Int32> pDXArray = {} ) const;
+                        o3tl::span<const sal_Int32> pDXArray = {},
+                        o3tl::span<const sal_Bool> pKashidaArray = {} ) const;
 
     Size QuickGetTextSize( const OutputDevice *pOut, const OUString &rTxt,
                            const sal_Int32 nIdx, const sal_Int32 nLen,
diff --git a/include/vcl/metaact.hxx b/include/vcl/metaact.hxx
index 37c3db3d9273..11caf48b2429 100644
--- a/include/vcl/metaact.hxx
+++ b/include/vcl/metaact.hxx
@@ -507,6 +507,7 @@ private:
     Point       maStartPt;
     OUString    maStr;
     std::vector<sal_Int32> maDXAry;
+    std::vector<sal_Bool> maKashidaAry;
     sal_Int32   mnIndex;
     sal_Int32   mnLen;
 
@@ -516,10 +517,14 @@ public:
                         MetaTextArrayAction();
                         MetaTextArrayAction( const MetaTextArrayAction& 
rAction );
     MetaTextArrayAction( const Point& rStartPt, OUString aStr,
-                         std::vector<sal_Int32>  rDXAry, sal_Int32 nIndex,
+                         std::vector<sal_Int32> rDXAry,
+                         std::vector<sal_Bool> pKashidaAry,
+                         sal_Int32 nIndex,
                          sal_Int32 nLen );
     MetaTextArrayAction( const Point& rStartPt, OUString aStr,
-                         o3tl::span<const sal_Int32> pDXAry, sal_Int32 nIndex,
+                         o3tl::span<const sal_Int32> pDXAry,
+                         o3tl::span<const sal_Bool> pKashidaAry,
+                         sal_Int32 nIndex,
                          sal_Int32 nLen );
 
     virtual void        Execute( OutputDevice* pOut ) override;
@@ -534,11 +539,13 @@ public:
     sal_Int32       GetIndex() const { return mnIndex; }
     sal_Int32       GetLen() const { return mnLen; }
     const std::vector<sal_Int32> & GetDXArray() const { return maDXAry; }
+    const std::vector<sal_Bool> & GetKashidaArray() const { return 
maKashidaAry; }
     void            SetPoint(const Point& rPt) { maStartPt = rPt; }
     void            SetText(const OUString& rStr) { maStr = rStr; }
     void            SetIndex(sal_Int32 rIndex) { mnIndex = rIndex; }
     void            SetLen(sal_Int32 rLen) { mnLen = rLen; }
     void            SetDXArray(std::vector<sal_Int32> aArray);
+    void            SetKashidaArray(std::vector<sal_Bool> aArray);
 };
 
 class SAL_DLLPUBLIC_RTTI MetaStretchTextAction final : public MetaAction
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index 2595415db1b5..af55ebd3d614 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -963,6 +963,7 @@ public:
     bool                        GetTextBoundRect( tools::Rectangle& rRect,
                                                   const OUString& rStr, 
sal_Int32 nBase = 0, sal_Int32 nIndex = 0, sal_Int32 nLen = -1,
                                                   sal_uLong nLayoutWidth = 0, 
o3tl::span<const sal_Int32> pDXArray = {},
+                                                  o3tl::span<const sal_Bool> 
pKashidaArray = {},
                                                   const SalLayoutGlyphs* 
pGlyphs = nullptr ) const;
 
     tools::Rectangle            ImplGetTextBoundRect( const SalLayout& ) const;
@@ -973,12 +974,14 @@ public:
     bool                        GetTextOutlines( PolyPolyVector&,
                                                  const OUString& rStr, 
sal_Int32 nBase = 0, sal_Int32 nIndex = 0,
                                                  sal_Int32 nLen = -1,
-                                                 sal_uLong nLayoutWidth = 0, 
o3tl::span<const sal_Int32> pDXArray = {} ) const;
+                                                 sal_uLong nLayoutWidth = 0, 
o3tl::span<const sal_Int32> pDXArray = {},
+                                                 o3tl::span<const sal_Bool> 
pKashidaArray = {} ) const;
 
     bool                        GetTextOutlines( basegfx::B2DPolyPolygonVector 
&rVector,
                                                  const OUString& rStr, 
sal_Int32 nBase, sal_Int32 nIndex = 0,
                                                  sal_Int32 nLen = -1,
-                                                 sal_uLong nLayoutWidth = 0, 
o3tl::span<const sal_Int32> pDXArray = {} ) const;
+                                                 sal_uLong nLayoutWidth = 0, 
o3tl::span<const sal_Int32> pDXArray = {},
+                                                 o3tl::span<const sal_Bool> 
pKashidaArray = {} ) const;
 
 
     OUString                    GetEllipsisString( const OUString& rStr, 
tools::Long nMaxWidth,
@@ -1044,6 +1047,7 @@ public:
 
     void                        DrawTextArray( const Point& rStartPt, const 
OUString& rStr,
                                                o3tl::span<const sal_Int32> 
pDXAry,
+                                               o3tl::span<const sal_Bool> 
pKashidaAry={},
                                                sal_Int32 nIndex = 0,
                                                sal_Int32 nLen = -1,
                                                SalLayoutFlags flags = 
SalLayoutFlags::NONE,
@@ -1236,7 +1240,9 @@ public:
     std::unique_ptr<SalLayout>
                                 ImplLayout( const OUString&, sal_Int32 nIndex, 
sal_Int32 nLen,
                                             const Point& rLogicPos = 
Point(0,0), tools::Long nLogicWidth=0,
-                                            o3tl::span<const sal_Int32> 
pLogicDXArray={}, SalLayoutFlags flags = SalLayoutFlags::NONE,
+                                            o3tl::span<const sal_Int32> 
pLogicDXArray={},
+                                            o3tl::span<const sal_Bool> 
pKashidaArray={},
+                                            SalLayoutFlags flags = 
SalLayoutFlags::NONE,
                                             vcl::text::TextLayoutCache const* 
= nullptr,
                                             const SalLayoutGlyphs* pGlyphs = 
nullptr) const;
 
diff --git a/include/vcl/pdfwriter.hxx b/include/vcl/pdfwriter.hxx
index 8764f3f49c8e..0c2cbb6294dd 100644
--- a/include/vcl/pdfwriter.hxx
+++ b/include/vcl/pdfwriter.hxx
@@ -764,6 +764,7 @@ The following structure describes the permissions used in 
PDF security
                                       FontLineStyle eOverline );
     void                DrawTextArray( const Point& rStartPt, const OUString& 
rStr,
                                        o3tl::span<const sal_Int32> pDXAry,
+                                       o3tl::span<const sal_Bool> pKashidaAry,
                                        sal_Int32 nIndex,
                                        sal_Int32 nLen );
     void                DrawStretchText( const Point& rStartPt, sal_uLong 
nWidth,
diff --git a/include/vcl/rendercontext/SalLayoutFlags.hxx 
b/include/vcl/rendercontext/SalLayoutFlags.hxx
index 68c26cadb48b..e8c5901d8528 100644
--- a/include/vcl/rendercontext/SalLayoutFlags.hxx
+++ b/include/vcl/rendercontext/SalLayoutFlags.hxx
@@ -30,13 +30,12 @@ enum class SalLayoutFlags
     DisableKerning = 0x0010,
     KerningAsian = 0x0020,
     Vertical = 0x0040,
-    KashidaJustification = 0x0800,
     ForFallback = 0x2000,
     GlyphItemsOnly = 0x4000,
 };
 namespace o3tl
 {
-template <> struct typed_flags<SalLayoutFlags> : 
is_typed_flags<SalLayoutFlags, 0x6877>
+template <> struct typed_flags<SalLayoutFlags> : 
is_typed_flags<SalLayoutFlags, 0x6077>
 {
 };
 }
diff --git a/sc/source/ui/view/hintwin.cxx b/sc/source/ui/view/hintwin.cxx
index 1dc76d139dd0..d57d8e0b787f 100644
--- a/sc/source/ui/view/hintwin.cxx
+++ b/sc/source/ui/view/hintwin.cxx
@@ -83,7 +83,7 @@ drawinglayer::primitive2d::Primitive2DContainer 
ScOverlayHint::createOverlaySequ
     rtl::Reference<drawinglayer::primitive2d::TextSimplePortionPrimitive2D> 
pTitle =
         new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                         aTextMatrix, m_aTitle, 0, m_aTitle.getLength(),
-                        std::vector<double>(), std::move(aFontAttr), 
css::lang::Locale(),
+                        std::vector<double>(), {}, std::move(aFontAttr), 
css::lang::Locale(),
                         rColor.getBColor());
 
     Point aTextStart(nLeft + aHintMargin.Width() + aIndent.Width(),
@@ -123,7 +123,7 @@ drawinglayer::primitive2d::Primitive2DContainer 
ScOverlayHint::createOverlaySequ
         
rtl::Reference<drawinglayer::primitive2d::TextSimplePortionPrimitive2D> 
pMessage =
                                         new 
drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                                                 aTextMatrix, aLine, 0, 
aLine.getLength(),
-                                                std::vector<double>(), 
aFontAttr, css::lang::Locale(),
+                                                std::vector<double>(), {}, 
aFontAttr, css::lang::Locale(),
                                                 rColor.getBColor());
 
         rRange.expand(pMessage->getB2DRange(aDummy));
diff --git a/sd/source/ui/view/sdview.cxx b/sd/source/ui/view/sdview.cxx
index 429a8c408753..7099977b31cf 100644
--- a/sd/source/ui/view/sdview.cxx
+++ b/sd/source/ui/view/sdview.cxx
@@ -430,6 +430,7 @@ void ViewRedirector::createRedirectedPrimitive2DSequence(
                                 0,
                                 nTextLength,
                                 std::move(aDXArray),
+                                {},
                                 std::move(aFontAttribute),
                                 std::move(aLocale),
                                 aRGBColor));
diff --git a/sfx2/source/control/thumbnailviewitem.cxx 
b/sfx2/source/control/thumbnailviewitem.cxx
index 36a33edc086b..9b75120289be 100644
--- a/sfx2/source/control/thumbnailviewitem.cxx
+++ b/sfx2/source/control/thumbnailviewitem.cxx
@@ -269,6 +269,7 @@ void ThumbnailViewItem::addTextPrimitives (const OUString& 
rText, const Thumbnai
                     new TextSimplePortionPrimitive2D(aTextMatrix,
                                                      aText, nLineStart, 
nLineLength,
                                                      std::vector<double>(),
+                                                     {},
                                                      pAttrs->aFontAttr,
                                                      css::lang::Locale(),
                                                      aTextColor));
diff --git a/svgio/source/svgreader/svgcharacternode.cxx 
b/svgio/source/svgreader/svgcharacternode.cxx
index 581129cf1025..0a4731b200d5 100644
--- a/svgio/source/svgreader/svgcharacternode.cxx
+++ b/svgio/source/svgreader/svgcharacternode.cxx
@@ -452,6 +452,7 @@ namespace svgio::svgreader
                         nIndex,
                         nLength,
                         std::move(aTextArray),
+                        {},
                         aFontAttribute,
                         aLocale,
                         aFill,
@@ -480,6 +481,7 @@ namespace svgio::svgreader
                         nIndex,
                         nLength,
                         std::move(aTextArray),
+                        {},
                         aFontAttribute,
                         aLocale,
                         aFill);
diff --git a/svtools/source/control/ruler.cxx b/svtools/source/control/ruler.cxx
index 07e030aac109..340d29b27cfc 100644
--- a/svtools/source/control/ruler.cxx
+++ b/svtools/source/control/ruler.cxx
@@ -79,7 +79,7 @@ SalLayoutGlyphs* lcl_GetRulerTextGlyphs(const 
vcl::RenderContext& rRenderContext
     // Calculate glyph items.
 
     std::unique_ptr<SalLayout> pLayout = rRenderContext.ImplLayout(
-        rText, 0, rText.getLength(), Point(0, 0), 0, {}, 
SalLayoutFlags::GlyphItemsOnly);
+        rText, 0, rText.getLength(), Point(0, 0), 0, {}, {}, 
SalLayoutFlags::GlyphItemsOnly);
     if (!pLayout)
         return nullptr;
 
@@ -339,7 +339,7 @@ void Ruler::ImplVDrawText(vcl::RenderContext& 
rRenderContext, tools::Long nX, to
     tools::Rectangle aRect;
     SalLayoutGlyphs* pTextLayout
         = lcl_GetRulerTextGlyphs(rRenderContext, rText, maTextGlyphs[rText]);
-    rRenderContext.GetTextBoundRect(aRect, rText, 0, 0, -1, 0, {}, 
pTextLayout);
+    rRenderContext.GetTextBoundRect(aRect, rText, 0, 0, -1, 0, {}, {}, 
pTextLayout);
 
     tools::Long nShiftX = ( aRect.GetWidth() / 2 ) + aRect.Left();
     tools::Long nShiftY = ( aRect.GetHeight() / 2 ) + aRect.Top();
diff --git a/svx/source/diagram/IDiagramHelper.cxx 
b/svx/source/diagram/IDiagramHelper.cxx
index 672c7485540a..ad1db2334d68 100644
--- a/svx/source/diagram/IDiagramHelper.cxx
+++ b/svx/source/diagram/IDiagramHelper.cxx
@@ -167,7 +167,8 @@ void OverlayDiagramPrimitive::create2DDecomposition(
             aName,
             0,
             aName.getLength(),
-            aDXArray);
+            aDXArray,
+            {});
 
         // put into one PolyPolygon (also simplification - overlapping chars
         // may create XOR gaps, so these exist for a reason, but low 
probability)
diff --git a/svx/source/svdraw/svdotextdecomposition.cxx 
b/svx/source/svdraw/svdotextdecomposition.cxx
index 666c20d48f5a..f5955eb7d869 100644
--- a/svx/source/svdraw/svdotextdecomposition.cxx
+++ b/svx/source/svdraw/svdotextdecomposition.cxx
@@ -234,6 +234,18 @@ namespace
             }
         }
 
+        ::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());
@@ -316,6 +328,7 @@ namespace
                 rInfo.mnTextStart,
                 rInfo.mnTextLen,
                 std::vector(aDXArray),
+                std::vector(aKashidaArray),
                 aFontAttribute,
                 rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
                 aBFontColor,
@@ -344,6 +357,7 @@ namespace
                 rInfo.mnTextStart,
                 rInfo.mnTextLen,
                 std::vector(aDXArray),
+                std::vector(aKashidaArray),
                 std::move(aFontAttribute),
                 rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(),
                 aBFontColor,
diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx 
b/svx/source/svdraw/svdotextpathdecomposition.cxx
index 99a7169c1fe6..7537625b2a26 100644
--- a/svx/source/svdraw/svdotextpathdecomposition.cxx
+++ b/svx/source/svdraw/svdotextpathdecomposition.cxx
@@ -58,6 +58,7 @@ namespace
         sal_Int32                                   mnParagraph;
         SvxFont                                     maFont;
         ::std::vector< double >                     maDblDXArray;   // double 
DXArray, font size independent -> unit coordinate system
+        ::std::vector< sal_Bool >                   maKashidaArray;
         lang::Locale                           maLocale;
 
         bool                                        mbRTL : 1;
@@ -70,6 +71,7 @@ namespace
             mnTextLength(rInfo.mnTextLen),
             mnParagraph(rInfo.mnPara),
             maFont(rInfo.mrFont),
+            maKashidaArray(rInfo.mpKashidaArray.begin(), 
rInfo.mpKashidaArray.end()),
             maLocale(rInfo.mpLocale ? *rInfo.mpLocale : lang::Locale()),
             mbRTL(!rInfo.mrFont.IsVertical() && rInfo.IsRTL())
         {
@@ -107,6 +109,7 @@ namespace
         const SvxFont& getFont() const { return maFont; }
         bool isRTL() const { return mbRTL; }
         const ::std::vector< double >& getDoubleDXArray() const { return 
maDblDXArray; }
+        const ::std::vector< sal_Bool >& getKashidaArray() const { return 
maKashidaArray; }
         const lang::Locale& getLocale() const { return maLocale; }
 
         sal_Int32 getPortionIndex(sal_Int32 nIndex, sal_Int32 nLength) const
@@ -497,6 +500,7 @@ namespace
                                         nPortionIndex,
                                         nNextGlyphLen,
                                         std::vector(aNewDXArray),
+                                        
std::vector(pCandidate->getKashidaArray()),
                                         aCandidateFontAttribute,
                                         pCandidate->getLocale(),
                                         aRGBShadowColor) );
@@ -514,6 +518,7 @@ namespace
                                         nPortionIndex,
                                         nNextGlyphLen,
                                         std::move(aNewDXArray),
+                                        
std::vector(pCandidate->getKashidaArray()),
                                         aCandidateFontAttribute,
                                         pCandidate->getLocale(),
                                         aRGBColor) );
diff --git a/svx/source/tbxctrls/StylesPreviewWindow.cxx 
b/svx/source/tbxctrls/StylesPreviewWindow.cxx
index 3ad9fa780117..8f5dfea0b2c8 100644
--- a/svx/source/tbxctrls/StylesPreviewWindow.cxx
+++ b/svx/source/tbxctrls/StylesPreviewWindow.cxx
@@ -358,7 +358,8 @@ void StyleItemController::DrawText(vcl::RenderContext& 
rRenderContext)
     const SalLayoutGlyphs* layoutGlyphs
         = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rRenderContext, 
m_aStyleName.second);
     tools::Rectangle aTextRect;
-    rRenderContext.GetTextBoundRect(aTextRect, m_aStyleName.second, 0, 0, -1, 
0, {}, layoutGlyphs);
+    rRenderContext.GetTextBoundRect(aTextRect, m_aStyleName.second, 0, 0, -1, 
0, {}, {},
+                                    layoutGlyphs);
 
     Point aPos(0, 0);
     aPos.AdjustX(LEFT_MARGIN);
diff --git a/sw/source/core/inc/scriptinfo.hxx 
b/sw/source/core/inc/scriptinfo.hxx
index cfe9ef3e55fb..ee536803a617 100644
--- a/sw/source/core/inc/scriptinfo.hxx
+++ b/sw/source/core/inc/scriptinfo.hxx
@@ -289,7 +289,7 @@ public:
                 The value which has to be added to a kashida opportunity.
     @return The number of kashida opportunities in the given range
 */
-    sal_Int32 KashidaJustify( sal_Int32* pKernArray,
+    sal_Int32 KashidaJustify( sal_Int32* pKernArray, sal_Bool* pKashidaArray,
           TextFrameIndex nStt, TextFrameIndex nLen, tools::Long nSpaceAdd = 0) 
const;
 
 /** Clears array of kashidas marked as invalid
diff --git a/sw/source/core/layout/paintfrm.cxx 
b/sw/source/core/layout/paintfrm.cxx
index 1c9fde902730..dd76c8609fda 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -3782,6 +3782,7 @@ void SwColumnFrame::PaintBreak( ) const
                 aTextMatrix,
                 aBreakText, 0, aBreakText.getLength(),
                 std::vector< double >(),
+                {},
                 std::move(aFontAttr),
                 lang::Locale(),
                 aLineColor ) );
diff --git a/sw/source/core/text/itradj.cxx b/sw/source/core/text/itradj.cxx
index a952ce7649c2..b248f20f4785 100644
--- a/sw/source/core/text/itradj.cxx
+++ b/sw/source/core/text/itradj.cxx
@@ -122,7 +122,7 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, 
SwTextSizeInfo& rInf,
     // total number of kashida positions, or the number of kashida positions 
after some positions
     // have been dropped.
     // Here we want the clean total, which is OK: We have called 
ClearKashidaInvalid() before.
-    rKashidas = rSI.KashidaJustify(nullptr, rItr.GetStart(), rItr.GetLength());
+    rKashidas = rSI.KashidaJustify(nullptr, nullptr, rItr.GetStart(), 
rItr.GetLength());
 
     if (rKashidas <= 0) // nothing to do
         return true;
@@ -147,7 +147,7 @@ static bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, 
SwTextSizeInfo& rInf,
 
         if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
             nNext = nEnd;
-        sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nIdx, nNext - 
nIdx);
+        sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, nIdx, 
nNext - nIdx);
         if (nKashidasInAttr > 0)
         {
             // Kashida glyph looks suspicious, skip Kashida justification
@@ -212,7 +212,7 @@ static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, 
SwTextSizeInfo& rInf, SwT
 
             if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
                 nNext = nEnd;
-            sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nIdx, 
nNext - nIdx);
+            sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, 
nIdx, nNext - nIdx);
 
             tools::Long nFontMinKashida = rInf.GetOut()->GetMinKashida();
             if ( nFontMinKashida && nKashidasInAttr > 0 && 
SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
diff --git a/sw/source/core/text/porlay.cxx b/sw/source/core/text/porlay.cxx
index 30abc01703f1..3db6a07600e9 100644
--- a/sw/source/core/text/porlay.cxx
+++ b/sw/source/core/text/porlay.cxx
@@ -2231,6 +2231,7 @@ tools::Long SwScriptInfo::Compress(sal_Int32* pKernArray, 
TextFrameIndex nIdx, T
 // have been dropped, depending on the state of the m_KashidaInvalid set.
 
 sal_Int32 SwScriptInfo::KashidaJustify( sal_Int32* pKernArray,
+                                        sal_Bool* pKashidaArray,
                                         TextFrameIndex const nStt,
                                         TextFrameIndex const nLen,
                                         tools::Long nSpaceAdd ) const
@@ -2286,6 +2287,11 @@ sal_Int32 SwScriptInfo::KashidaJustify( sal_Int32* 
pKernArray,
         {
             TextFrameIndex nArrayPos = nIdx - nStt;
 
+            // mark Kashida insertion positions, code in VCL will use this
+            // array to know where to insert Kashida.
+            if (pKashidaArray)
+                pKashidaArray[sal_Int32(nArrayPos)] = true;
+
             // next kashida position
             ++nCntKash;
             while (nCntKash < nCntKashEnd && !IsKashidaValid(nCntKash))
diff --git a/sw/source/core/text/portxt.cxx b/sw/source/core/text/portxt.cxx
index c0f66496f134..09a2fc2c0b67 100644
--- a/sw/source/core/text/portxt.cxx
+++ b/sw/source/core/text/portxt.cxx
@@ -113,7 +113,7 @@ static TextFrameIndex lcl_AddSpace(const SwTextSizeInfo 
&rInf,
     {
         if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && 
pSI->CountKashida() )
         {
-            const sal_Int32 nKashRes = pSI->KashidaJustify(nullptr, nPos, nEnd 
- nPos);
+            const sal_Int32 nKashRes = pSI->KashidaJustify(nullptr, nullptr, 
nPos, nEnd - nPos);
             // i60591: need to check result of KashidaJustify
             // determine if kashida justification is applicable
             if (nKashRes != -1)
diff --git a/sw/source/core/txtnode/fntcache.cxx 
b/sw/source/core/txtnode/fntcache.cxx
index 4058f12a1492..88d9037a3eb1 100644
--- a/sw/source/core/txtnode/fntcache.cxx
+++ b/sw/source/core/txtnode/fntcache.cxx
@@ -951,7 +951,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
                 rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
 
             rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
-                aKernArray, sal_Int32(rInf.GetIdx()), 
sal_Int32(rInf.GetLen()));
+                aKernArray, {}, sal_Int32(rInf.GetIdx()), 
sal_Int32(rInf.GetLen()));
 
             return;
         }
@@ -980,6 +980,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
         {
             std::vector<sal_Int32> aKernArray;
             GetTextArray(rInf.GetOut(), rInf, aKernArray);
+            std::vector<sal_Bool> aKashidaArray;
 
             if( bStretch )
             {
@@ -1050,13 +1051,16 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
                 {
                     if ( SwScriptInfo::IsArabicText( rInf.GetText(), 
rInf.GetIdx(), rInf.GetLen() ) )
                     {
+                        aKashidaArray.resize(aKernArray.size(), false);
                         if ( pSI && pSI->CountKashida() &&
-                            pSI->KashidaJustify( aKernArray.data(), 
rInf.GetIdx(),
+                            pSI->KashidaJustify( aKernArray.data(), 
aKashidaArray.data(), rInf.GetIdx(),
                                                  rInf.GetLen(), nSpaceAdd ) != 
-1 )
                         {
                             bSpecialJust = true;
                             nSpaceAdd = 0;
                         }
+                        else
+                            aKashidaArray.clear();
                     }
                 }
 
@@ -1104,18 +1108,18 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
                         aKernArray[0] = rInf.GetWidth() + nSpaceAdd;
 
                         rInf.GetOut().DrawTextArray( aTextOriginPos, 
rInf.GetText(),
-                             aKernArray, sal_Int32(rInf.GetIdx()), 1 );
+                             aKernArray, aKashidaArray, 
sal_Int32(rInf.GetIdx()), 1 );
                     }
                     else
                     {
                         aKernArray[ sal_Int32(rInf.GetLen()) - 2 ] += 
nSpaceAdd;
                         rInf.GetOut().DrawTextArray( aTextOriginPos, 
rInf.GetText(),
-                            aKernArray, sal_Int32(rInf.GetIdx()), 
sal_Int32(rInf.GetLen()));
+                            aKernArray, aKashidaArray, 
sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
                     }
                 }
                 else
                     rInf.GetOut().DrawTextArray( aTextOriginPos, 
rInf.GetText(),
-                            aKernArray, sal_Int32(rInf.GetIdx()), 
sal_Int32(rInf.GetLen()));
+                            aKernArray, aKashidaArray, 
sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
             }
             else
             {
@@ -1209,6 +1213,8 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
             GetTextArray(rInf.GetOut(), rInf, aKernArray);
         }
 
+        std::vector<sal_Bool> aKashidaArray;
+
         // Modify Printer and ScreenArrays for special justifications
 
         tools::Long nSpaceAdd = rInf.GetSpace() / SPACING_PRECISION_FACTOR;
@@ -1248,12 +1254,16 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
             {
                 if ( SwScriptInfo::IsArabicText( rInf.GetText(), 
rInf.GetIdx(), rInf.GetLen() ) )
                 {
+                    aKashidaArray.resize(aKernArray.size(), false);
                     if ( pSI && pSI->CountKashida() &&
-                         pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(),
+                         pSI->KashidaJustify( aKernArray.data(), 
aKashidaArray.data(), rInf.GetIdx(),
                                               rInf.GetLen(), nSpaceAdd ) != -1 
)
                         nSpaceAdd = 0;
                     else
+                    {
+                        aKashidaArray.clear();
                         bNoHalfSpace = true;
+                    }
                 }
             }
 
@@ -1339,9 +1349,9 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
                 rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
 
             rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
-                         aKernArray, sal_Int32(rInf.GetIdx()), 1 );
+                         aKernArray, aKashidaArray, sal_Int32(rInf.GetIdx()), 
1 );
             if( bBullet )
-                rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, aKernArray,
+                rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, 
aKernArray, aKashidaArray,
                                              rInf.GetIdx() ? 1 : 0, 1 );
         }
         else
@@ -1464,7 +1474,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
                             : sal_Int32(rInf.GetIdx());
                 const SalLayoutGlyphs* pGlyphs = 
SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rInf.GetOut(),
                      *pStr, nTmpIdx, nLen);
-                rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, aKernArray,
+                rInf.GetOut().DrawTextArray( aTextOriginPos, *pStr, 
aKernArray, aKashidaArray,
                                              nTmpIdx , nLen, 
SalLayoutFlags::NONE, pGlyphs );
                 if (bBullet)
                 {
@@ -1503,7 +1513,7 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
                         }
                     }
                     rInf.GetOut().DrawTextArray( aTextOriginPos, 
aBulletOverlay, aKernArray,
-                                                 nTmpIdx , nLen );
+                                                 aKashidaArray, nTmpIdx , nLen 
);
                     pTmpFont->SetColor( aPreviousColor );
 
                     pTmpFont->SetUnderline(aPreviousUnderline);
@@ -1738,7 +1748,7 @@ TextFrameIndex 
SwFntObj::GetModelPositionForViewPoint(SwDrawTextInfo &rInf)
             if ( SwScriptInfo::IsArabicText( rInf.GetText(), rInf.GetIdx(), 
rInf.GetLen() ) )
             {
                 if ( pSI && pSI->CountKashida() &&
-                    pSI->KashidaJustify( aKernArray.data(), rInf.GetIdx(), 
rInf.GetLen(),
+                    pSI->KashidaJustify( aKernArray.data(), nullptr, 
rInf.GetIdx(), rInf.GetLen(),
                                          nSpaceAdd ) != -1 )
                     nSpaceAdd = 0;
             }
diff --git a/sw/source/uibase/docvw/HeaderFooterWin.cxx 
b/sw/source/uibase/docvw/HeaderFooterWin.cxx
index cca26f41e78c..ba75b20ec3ff 100644
--- a/sw/source/uibase/docvw/HeaderFooterWin.cxx
+++ b/sw/source/uibase/docvw/HeaderFooterWin.cxx
@@ -319,7 +319,7 @@ void SwHeaderFooterWin::PaintButton()
     aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
                     new 
drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                         aTextMatrix, m_sLabel, 0, m_sLabel.getLength(),
-                        std::vector<double>(), std::move(aFontAttr), 
css::lang::Locale(), aLineColor)));
+                        std::vector<double>(), {}, std::move(aFontAttr), 
css::lang::Locale(), aLineColor)));
 
     // Create the 'plus' or 'arrow' primitive
     B2DRectangle aSignArea(B2DPoint(aRect.Right() - BUTTON_WIDTH, 0.0),
diff --git a/sw/source/uibase/docvw/UnfloatTableButton.cxx 
b/sw/source/uibase/docvw/UnfloatTableButton.cxx
index 82320732c707..49bf9c660771 100644
--- a/sw/source/uibase/docvw/UnfloatTableButton.cxx
+++ b/sw/source/uibase/docvw/UnfloatTableButton.cxx
@@ -223,7 +223,7 @@ void UnfloatTableButton::PaintButton()
 
     aSeq.push_back(drawinglayer::primitive2d::Primitive2DReference(
         new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
-            aTextMatrix, m_sLabel, 0, m_sLabel.getLength(), 
std::vector<double>(),
+            aTextMatrix, m_sLabel, 0, m_sLabel.getLength(), 
std::vector<double>(), {},
             std::move(aFontAttr), css::lang::Locale(), aLineColor)));
 
     // Create the processor and process the primitives
diff --git a/vcl/inc/ImplLayoutArgs.hxx b/vcl/inc/ImplLayoutArgs.hxx
index a94557afeb36..105a4e2d8961 100644
--- a/vcl/inc/ImplLayoutArgs.hxx
+++ b/vcl/inc/ImplLayoutArgs.hxx
@@ -37,6 +37,7 @@ public:
     // positioning related inputs
     const DeviceCoordinate* mpDXArray; // in integer pixel units
     const double* mpAltNaturalDXArray; // in floating point pixel units
+    const sal_Bool* mpKashidaArray;
     DeviceCoordinate mnLayoutWidth; // in pixel units
     Degree10 mnOrientation; // in 0-3600 system
 
@@ -50,6 +51,7 @@ public:
     void SetLayoutWidth(DeviceCoordinate nWidth);
     void SetDXArray(const DeviceCoordinate* pDXArray);
     void SetAltNaturalDXArray(const double* pDXArray);
+    void SetKashidaArray(const sal_Bool* pKashidaArray);
     void SetOrientation(Degree10 nOrientation);
 
     void ResetPos();
diff --git a/vcl/inc/impglyphitem.hxx b/vcl/inc/impglyphitem.hxx
index f0e4e70ef21c..f43ef0e99e61 100644
--- a/vcl/inc/impglyphitem.hxx
+++ b/vcl/inc/impglyphitem.hxx
@@ -37,7 +37,6 @@ enum class GlyphItemFlags : sal_uInt16
     IS_DIACRITIC = 0x04,
     IS_VERTICAL = 0x08,
     IS_SPACING = 0x10,
-    ALLOW_KASHIDA = 0x20,
     IS_DROPPED = 0x40,
     IS_CLUSTER_START = 0x80,
     IS_UNSAFE_TO_BREAK = 0x100, // HB_GLYPH_FLAG_UNSAFE_TO_BREAK from harfbuzz
@@ -45,7 +44,7 @@ enum class GlyphItemFlags : sal_uInt16
 };
 namespace o3tl
 {
-template <> struct typed_flags<GlyphItemFlags> : 
is_typed_flags<GlyphItemFlags, 0x3ff>
+template <> struct typed_flags<GlyphItemFlags> : 
is_typed_flags<GlyphItemFlags, 0x3df>
 {
 };
 };
@@ -82,7 +81,6 @@ public:
     bool IsDiacritic() const { return bool(m_nFlags & 
GlyphItemFlags::IS_DIACRITIC); }
     bool IsVertical() const { return bool(m_nFlags & 
GlyphItemFlags::IS_VERTICAL); }
     bool IsSpacing() const { return bool(m_nFlags & 
GlyphItemFlags::IS_SPACING); }
-    bool AllowKashida() const { return bool(m_nFlags & 
GlyphItemFlags::ALLOW_KASHIDA); }
     bool IsDropped() const { return bool(m_nFlags & 
GlyphItemFlags::IS_DROPPED); }
     bool IsClusterStart() const { return bool(m_nFlags & 
GlyphItemFlags::IS_CLUSTER_START); }
     bool IsUnsafeToBreak() const { return bool(m_nFlags & 
GlyphItemFlags::IS_UNSAFE_TO_BREAK); }
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index d718529476b9..4ebddbe5edaf 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -1188,7 +1188,7 @@ public:
 
     /* actual drawing functions */
     void drawText( const Point& rPos, const OUString& rText, sal_Int32 nIndex, 
sal_Int32 nLen, bool bTextLines = true );
-    void drawTextArray( const Point& rPos, const OUString& rText, 
o3tl::span<const sal_Int32> pDXArray, sal_Int32 nIndex, sal_Int32 nLen );
+    void drawTextArray( const Point& rPos, const OUString& rText, 
o3tl::span<const sal_Int32> pDXArray, o3tl::span<const sal_Bool> pKashidaArray, 
sal_Int32 nIndex, sal_Int32 nLen );
     void drawStretchText( const Point& rPos, sal_uLong nWidth, const OUString& 
rText,
                           sal_Int32 nIndex, sal_Int32 nLen  );
     void drawText( const tools::Rectangle& rRect, const OUString& rOrigStr, 
DrawTextFlags nStyle );
diff --git a/vcl/inc/sallayout.hxx b/vcl/inc/sallayout.hxx
index 35f27ee20edf..885de2446db7 100644
--- a/vcl/inc/sallayout.hxx
+++ b/vcl/inc/sallayout.hxx
@@ -143,7 +143,7 @@ private:
                     GenericSalLayout& operator=( const GenericSalLayout& ) = 
delete;
 
     template<typename DC>
-    void            ApplyDXArray(const DC*, SalLayoutFlags nLayoutFlags);
+    void            ApplyDXArray(const DC*, const sal_Bool*);
     void            Justify(DeviceCoordinate nNewWidth);
     void            ApplyAsianKerning(const OUString& rStr);
 
diff --git a/vcl/qa/cppunit/complextext.cxx b/vcl/qa/cppunit/complextext.cxx
index 9e96205946b1..ff85b14e33ef 100644
--- a/vcl/qa/cppunit/complextext.cxx
+++ b/vcl/qa/cppunit/complextext.cxx
@@ -49,14 +49,12 @@ public:
 
     /// Play with font measuring etc.
     void testArabic();
-    void testKashida();
     void testTdf95650(); // Windows-only issue
     void testCaching();
     void testCachingSubstring();
 
     CPPUNIT_TEST_SUITE(VclComplexTextTest);
     CPPUNIT_TEST(testArabic);
-    CPPUNIT_TEST(testKashida);
     CPPUNIT_TEST(testTdf95650);
     CPPUNIT_TEST(testCaching);
     CPPUNIT_TEST(testCachingSubstring);
@@ -114,31 +112,6 @@ void VclComplexTextTest::testArabic()
 #endif
 }
 
-void VclComplexTextTest::testKashida()
-{
-#if HAVE_MORE_FONTS
-    // Cache the glyph list of some Arabic text.
-    ScopedVclPtrInstance<VirtualDevice> pOutputDevice;
-    auto aText
-        = OUString(u"عنصر الفوسفور عنصر فلزي صلب. تتكون الدورة الرابعة من 15 
عنصرا.");
-    std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(
-        aText, 0, aText.getLength(), Point(0, 0), 0, {}, 
SalLayoutFlags::GlyphItemsOnly);
-    SalLayoutGlyphs aGlyphs = pLayout->GetGlyphs();
-    CPPUNIT_ASSERT(aGlyphs.IsValid());
-    CPPUNIT_ASSERT(aGlyphs.Impl(0) != nullptr);
-
-    // Now lay it out using the cached glyph list.
-    vcl::text::ImplLayoutArgs aLayoutArgs(aText, 0, aText.getLength(), 
SalLayoutFlags::NONE,
-                               pOutputDevice->GetFont().GetLanguageTag(), 
nullptr);
-    pLayout = pOutputDevice->GetGraphics()->GetTextLayout(0);
-    CPPUNIT_ASSERT(pLayout->LayoutText(aLayoutArgs, aGlyphs.Impl(0)));
-
-    // Without the accompanying fix in place, this test would have failed with 
'assertion failed'.
-    // The kashida justification flag was lost when going via the glyph cache.
-    CPPUNIT_ASSERT(aLayoutArgs.mnFlags & SalLayoutFlags::KashidaJustification);
-#endif
-}
-
 void VclComplexTextTest::testTdf95650()
 {
     static constexpr OUStringLiteral aTxt =
@@ -152,7 +125,7 @@ void VclComplexTextTest::testTdf95650()
 
     OutputDevice *pOutDev = pWin->GetOutDev();
     // Check that the following executes without failing assertion
-    pOutDev->ImplLayout(aTxt, 9, 1, Point(), 0, {}, SalLayoutFlags::BiDiRtl);
+    pOutDev->ImplLayout(aTxt, 9, 1, Point(), 0, {}, {}, 
SalLayoutFlags::BiDiRtl);
 }
 
 static void checkCompareGlyphs( const SalLayoutGlyphs& aGlyphs1, const 
SalLayoutGlyphs& aGlyphs2,
@@ -190,11 +163,11 @@ static void testCachedGlyphs( const OUString& aText, 
const OUString& aFontName )
     SalLayoutGlyphsCache::self()->clear();
     // Get the glyphs for the text.
     std::unique_ptr<SalLayout> pLayout1 = pOutputDevice->ImplLayout(
-        aText, 0, aText.getLength(), Point(0, 0), 0, {}, 
SalLayoutFlags::GlyphItemsOnly);
+        aText, 0, aText.getLength(), Point(0, 0), 0, {}, {}, 
SalLayoutFlags::GlyphItemsOnly);
     SalLayoutGlyphs aGlyphs1 = pLayout1->GetGlyphs();
     // Reuse the cached glyphs to get glyphs again.
     std::unique_ptr<SalLayout> pLayout2 = pOutputDevice->ImplLayout(
-        aText, 0, aText.getLength(), Point(0, 0), 0, {}, 
SalLayoutFlags::GlyphItemsOnly, nullptr, &aGlyphs1);
+        aText, 0, aText.getLength(), Point(0, 0), 0, {}, {}, 
SalLayoutFlags::GlyphItemsOnly, nullptr, &aGlyphs1);
     SalLayoutGlyphs aGlyphs2 = pLayout2->GetGlyphs();
     checkCompareGlyphs(aGlyphs1, aGlyphs2, message + " (reuse)");
     // Get cached glyphs from SalLayoutGlyphsCache.
@@ -228,7 +201,7 @@ static void testCachedGlyphsSubstring( const OUString& 
aText, const OUString& aF
     SalLayoutGlyphsCache::self()->clear();
     std::shared_ptr<const vcl::text::TextLayoutCache> layoutCache = 
OutputDevice::CreateTextLayoutCache(aText);
     // Get the glyphs for the entire text once, to ensure the cache can built 
subsets from it.
-    pOutputDevice->ImplLayout( aText, 0, aText.getLength(), Point(0, 0), 0, 
{}, SalLayoutFlags::GlyphItemsOnly,
+    pOutputDevice->ImplLayout( aText, 0, aText.getLength(), Point(0, 0), 0, 
{}, {}, SalLayoutFlags::GlyphItemsOnly,
         layoutCache.get());
     // Now check for all subsets. Some of them possibly do not make sense in 
practice, but the code
     // should cope with them.
@@ -237,7 +210,7 @@ static void testCachedGlyphsSubstring( const OUString& 
aText, const OUString& aF
         {
             std::string message = prefix + " (" + std::to_string(pos) + "/" + 
std::to_string(len) + ")";
             std::unique_ptr<SalLayout> pLayout1 = pOutputDevice->ImplLayout(
-                aText, pos, len, Point(0, 0), 0, {}, 
SalLayoutFlags::GlyphItemsOnly, layoutCache.get());
+                aText, pos, len, Point(0, 0), 0, {}, {}, 
SalLayoutFlags::GlyphItemsOnly, layoutCache.get());
             SalLayoutGlyphs aGlyphs1 = pLayout1->GetGlyphs();
             const SalLayoutGlyphs* aGlyphs2 = 
SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
                 pOutputDevice, aText, pos, len, 0, layoutCache.get());
diff --git a/vcl/qa/cppunit/svm/svmtest.cxx b/vcl/qa/cppunit/svm/svmtest.cxx
index fcea61afb5a9..2f4f17d7beb4 100644
--- a/vcl/qa/cppunit/svm/svmtest.cxx
+++ b/vcl/qa/cppunit/svm/svmtest.cxx
@@ -860,7 +860,7 @@ void SvmTest::testTextArray()
     ScopedVclPtrInstance<VirtualDevice> pVirtualDev;
     setupBaseVirtualDevice(*pVirtualDev, aGDIMetaFile);
     sal_Int32 const aDX[] = { 10, 15, 20, 25, 30, 35 };
-    pVirtualDev->DrawTextArray(Point(4,6), "123456", aDX, 1, 4);
+    pVirtualDev->DrawTextArray(Point(4,6), "123456", aDX, {}, 1, 4);
 
     checkTextArray(writeAndReadStream(aGDIMetaFile));
     checkTextArray(readFile(u"textarray.svm"));
diff --git a/vcl/qa/cppunit/text.cxx b/vcl/qa/cppunit/text.cxx
index 668e84ef2aad..7a73ee3bc6e2 100644
--- a/vcl/qa/cppunit/text.cxx
+++ b/vcl/qa/cppunit/text.cxx
@@ -702,7 +702,7 @@ void 
VclTextTest::testImplLayoutArgs_PrepareFallback_precalculatedglyphs()
     const OString sUTF8String(u8"Тхе яуицк\n ыумпед овер");
     const OUString sTestString(OUString::fromUtf8(sUTF8String));
     std::unique_ptr<SalLayout> pLayout
-        = pVirDev->ImplLayout(sTestString, 0, sTestString.getLength(), 
Point(0, 0), 0, {},
+        = pVirDev->ImplLayout(sTestString, 0, sTestString.getLength(), 
Point(0, 0), 0, {}, {},
                               SalLayoutFlags::GlyphItemsOnly);
     SalLayoutGlyphs aGlyphs = pLayout->GetGlyphs();
     SalLayoutGlyphsImpl* pGlyphsImpl = aGlyphs.Impl(1);
diff --git a/vcl/source/control/imp_listbox.cxx 
b/vcl/source/control/imp_listbox.cxx
index 911770678667..fbd655eb04dd 100644
--- a/vcl/source/control/imp_listbox.cxx
+++ b/vcl/source/control/imp_listbox.cxx
@@ -590,7 +590,7 @@ SalLayoutGlyphs* ImplEntryType::GetTextGlyphs(const 
OutputDevice* pOutputDevice)
         return &maStrGlyphs;
 
     std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(
-        maStr, 0, maStr.getLength(), Point(0, 0), 0, {}, 
SalLayoutFlags::GlyphItemsOnly);
+        maStr, 0, maStr.getLength(), Point(0, 0), 0, {}, {}, 
SalLayoutFlags::GlyphItemsOnly);
     if (!pLayout)
         return nullptr;
 
diff --git a/vcl/source/filter/eps/eps.cxx b/vcl/source/filter/eps/eps.cxx
index bb25bdccb57e..797d6b887efc 100644
--- a/vcl/source/filter/eps/eps.cxx
+++ b/vcl/source/filter/eps/eps.cxx
@@ -201,7 +201,7 @@ private:
 
     void                ImplSetClipRegion( vcl::Region const & rRegion );
     void                ImplBmp( Bitmap const *, Bitmap const *, const Point 
&, double nWidth, double nHeight );
-    void                ImplText( const OUString& rUniString, const Point& 
rPos, o3tl::span<const sal_Int32> pDXArry, sal_Int32 nWidth, VirtualDevice 
const & rVDev );
+    void                ImplText( const OUString& rUniString, const Point& 
rPos, o3tl::span<const sal_Int32> pDXArry, o3tl::span<const sal_Bool> 
pKashidaArry, sal_Int32 nWidth, VirtualDevice const & rVDev );
     void                ImplSetAttrForText( const Point & rPoint );
     void                ImplWriteCharacter( char );
     void                ImplWriteString( const OString&, VirtualDevice const & 
rVDev, o3tl::span<const sal_Int32> pDXArry, bool bStretch );
@@ -748,7 +748,7 @@ void PSWriter::ImplWriteActions( const GDIMetaFile& rMtf, 
VirtualDevice& rVDev )
                 OUString  aUniStr = pA->GetText().copy( pA->GetIndex(), 
pA->GetLen() );
                 Point     aPoint( pA->GetPoint() );
 
-                ImplText( aUniStr, aPoint, {}, 0, rVDev );
+                ImplText( aUniStr, aPoint, {}, {}, 0, rVDev );
             }
             break;
 
@@ -764,7 +764,7 @@ void PSWriter::ImplWriteActions( const GDIMetaFile& rMtf, 
VirtualDevice& rVDev )
                 OUString  aUniStr = pA->GetText().copy( pA->GetIndex(), 
pA->GetLen() );
                 Point     aPoint( pA->GetPoint() );
 
-                ImplText( aUniStr, aPoint, {}, pA->GetWidth(), rVDev );
+                ImplText( aUniStr, aPoint, {}, {}, pA->GetWidth(), rVDev );
             }
             break;
 
@@ -774,7 +774,7 @@ void PSWriter::ImplWriteActions( const GDIMetaFile& rMtf, 
VirtualDevice& rVDev )
                 OUString  aUniStr = pA->GetText().copy( pA->GetIndex(), 
pA->GetLen() );
                 Point     aPoint( pA->GetPoint() );
 
-                ImplText( aUniStr, aPoint, pA->GetDXArray(), 0, rVDev );
+                ImplText( aUniStr, aPoint, pA->GetDXArray(), 
pA->GetKashidaArray(), 0, rVDev );
             }
             break;
 
@@ -1988,7 +1988,7 @@ void PSWriter::ImplWriteString( const OString& rString, 
VirtualDevice const & rV
     }
 }
 
-void PSWriter::ImplText( const OUString& rUniString, const Point& rPos, 
o3tl::span<const sal_Int32> pDXArry, sal_Int32 nWidth, VirtualDevice const & 
rVDev )
+void PSWriter::ImplText( const OUString& rUniString, const Point& rPos, 
o3tl::span<const sal_Int32> pDXArry, o3tl::span<const sal_Bool> pKashidaArry, 
sal_Int32 nWidth, VirtualDevice const & rVDev )
 {
     if ( rUniString.isEmpty() )
         return;
@@ -2015,7 +2015,7 @@ void PSWriter::ImplText( const OUString& rUniString, 
const Point& rPos, o3tl::sp
         bool bOldLineColor = bLineColor;
         bLineColor = false;
         std::vector<tools::PolyPolygon> aPolyPolyVec;
-        if ( pVirDev->GetTextOutlines( aPolyPolyVec, rUniString, 0, 0, -1, 
nWidth, pDXArry ) )
+        if ( pVirDev->GetTextOutlines( aPolyPolyVec, rUniString, 0, 0, -1, 
nWidth, pDXArry, pKashidaArry ) )
         {
             // always adjust text position to match baseline alignment
             ImplWriteLine( "pum" );
diff --git a/vcl/source/filter/svm/SvmConverter.cxx 
b/vcl/source/filter/svm/SvmConverter.cxx
index 4127d132557e..efc036cc264a 100644
--- a/vcl/source/filter/svm/SvmConverter.cxx
+++ b/vcl/source/filter/svm/SvmConverter.cxx
@@ -809,7 +809,7 @@ void SVMConverter::ImplConvertFromSVM1( SvStream& rIStm, 
GDIMetaFile& rMtf )
                     if ( nUnicodeCommentActionNumber == i )
                         ImplReadUnicodeComment( nUnicodeCommentStreamPos, 
rIStm, aStr );
                     ClampRange(aStr, nIndex, nLen, &aDXAry);
-                    rMtf.AddAction( new MetaTextArrayAction( aPt, aStr, 
std::move(aDXAry), nIndex, nLen ) );
+                    rMtf.AddAction( new MetaTextArrayAction( aPt, aStr, 
std::move(aDXAry), {}, nIndex, nLen ) );
                 }
 
                 if (nActionSize < 24)
diff --git a/vcl/source/gdi/CommonSalLayout.cxx 
b/vcl/source/gdi/CommonSalLayout.cxx
index 98364d4ae443..da7334352858 100644
--- a/vcl/source/gdi/CommonSalLayout.cxx
+++ b/vcl/source/gdi/CommonSalLayout.cxx
@@ -197,9 +197,9 @@ void 
GenericSalLayout::AdjustLayout(vcl::text::ImplLayoutArgs& rArgs)
     SalLayout::AdjustLayout(rArgs);
 
     if (rArgs.mpAltNaturalDXArray) // Used when 
"TextRenderModeForResolutionIndependentLayout" is set
-        ApplyDXArray(rArgs.mpAltNaturalDXArray, rArgs.mnFlags);
+        ApplyDXArray(rArgs.mpAltNaturalDXArray, rArgs.mpKashidaArray);
     else if (rArgs.mpDXArray)   // Normal case
-        ApplyDXArray(rArgs.mpDXArray, rArgs.mnFlags);
+        ApplyDXArray(rArgs.mpDXArray, rArgs.mpKashidaArray);
     else if (rArgs.mnLayoutWidth)
         Justify(rArgs.mnLayoutWidth);
     // apply asian kerning if the glyphs are not already formatted
@@ -565,14 +565,6 @@ bool 
GenericSalLayout::LayoutText(vcl::text::ImplLayoutArgs& rArgs, const SalLay
                 if (u_isUWhiteSpace(aChar))
                     nGlyphFlags |= GlyphItemFlags::IS_SPACING;
 
-                if (aSubRun.maScript == HB_SCRIPT_ARABIC &&
-                    HB_DIRECTION_IS_BACKWARD(aSubRun.maDirection) &&
-                    !(nGlyphFlags & GlyphItemFlags::IS_SPACING))
-                {
-                    nGlyphFlags |= GlyphItemFlags::ALLOW_KASHIDA;
-                    rArgs.mnFlags |= SalLayoutFlags::KashidaJustification;
-                }
-
 #if HB_VERSION_ATLEAST(1, 5, 0)
                 if (hb_glyph_info_get_glyph_flags(&pHbGlyphInfos[i]) & 
HB_GLYPH_FLAG_UNSAFE_TO_BREAK)
                     nGlyphFlags |= GlyphItemFlags::IS_UNSAFE_TO_BREAK;
@@ -657,24 +649,12 @@ void 
GenericSalLayout::GetCharWidths(std::vector<DeviceCoordinate>& rCharWidths)
     }
 }
 
-// A note on how Kashida justification is implemented (because it took me 5
-// years to figure it out):
-// The decision to insert Kashidas, where and how much is taken by Writer.
-// This decision is communicated to us in a very indirect way; by increasing
-// the width of the character after which Kashidas should be inserted by the
-// desired amount.
-//
-// Writer eventually calls IsKashidaPosValid() to check whether it can insert a
-// Kashida between two characters or not.
-//
-// Here we do:
-// - In LayoutText() set KashidaJustification flag based on text script.
-// - In ApplyDXArray():
-//   * Check the above flag to decide whether to insert Kashidas or not.
-//   * For any RTL glyph that has DX adjustment, insert enough Kashidas to
-//     fill in the added space.
+// - pDXArray: is the adjustments to glyph advances (usually due to
+//   justification).
+// - pKashidaArray: is the places where kashidas are inserted (for Arabic
+//   justification). The number of kashidas is calculated from the pDXArray.
 template<typename DC>
-void GenericSalLayout::ApplyDXArray(const DC* pDXArray, SalLayoutFlags 
nLayoutFlags)
+void GenericSalLayout::ApplyDXArray(const DC* pDXArray, const sal_Bool* 
pKashidaArray)
 {
     int nCharCount = mnEndCharPos - mnMinCharPos;
     std::vector<DeviceCoordinate> aOldCharWidths;
@@ -692,18 +672,6 @@ void GenericSalLayout::ApplyDXArray(const DC* pDXArray, 
SalLayoutFlags nLayoutFl
             pNewCharWidths[i] = pDXArray[i] - pDXArray[i - 1];
     }
 
-    bool bKashidaJustify = false;
-    DeviceCoordinate nKashidaWidth = 0;
-    hb_codepoint_t nKashidaIndex = 0;
-    if (nLayoutFlags & SalLayoutFlags::KashidaJustification)
-    {
-        hb_font_t *pHbFont = GetFont().GetHbFont();
-        // Find Kashida glyph width and index.
-        if (hb_font_get_glyph(pHbFont, 0x0640, 0, &nKashidaIndex))
-            nKashidaWidth = GetFont().GetKashidaWidth();
-        bKashidaJustify = nKashidaWidth != 0;
-    }
-
     // Map of Kashida insertion points (in the glyph items vector) and the
     // requested width.
     std::map<size_t, DeviceCoordinate> pKashidas;
@@ -743,15 +711,15 @@ void GenericSalLayout::ApplyDXArray(const DC* pDXArray, 
SalLayoutFlags nLayoutFl
             // loop below.
             i++;
         }
-        else
+        else // RTL
         {
             // Adjust the width and position of the first (rightmost) glyph in
-            // the cluster.
-            // For RTL, we put all the adjustment to the left of the glyph.
+            // the cluster. This is RTL, so we put all the adjustment to the
+            // left of the glyph.
             m_GlyphItems[i].addNewWidth(nDiff);
             m_GlyphItems[i].adjustLinearPosX(nDelta + nDiff);
 
-            // Adjust the X position of all glyphs in the cluster.
+            // Adjust the X position of the rest of the glyphs in the cluster.
             size_t j = i;
             while (j > 0)
             {
@@ -761,36 +729,44 @@ void GenericSalLayout::ApplyDXArray(const DC* pDXArray, 
SalLayoutFlags nLayoutFl
                 m_GlyphItems[j].adjustLinearPosX(nDelta + nDiff);
             }
 
-            // If this glyph is Kashida-justifiable, then mark this as a
-            // Kashida position. Since this must be a RTL glyph, we mark the
-            // last glyph in the cluster not the first as this would be the
-            // base glyph.
-            if (bKashidaJustify && m_GlyphItems[i].AllowKashida() &&
-                nDiff > m_GlyphItems[i].charCount()) // Rounding errors, 1 
pixel per character!
+            // Move any non-spacing marks to keep attached to this cluster.
+            while (j > 0)
             {
-                pKashidas[i] = nDiff;
-                // Move any non-spacing marks attached to this cluster as well.
-                // Looping backward because this is RTL glyph.
-                while (j > 0)
-                {
-                    if (!m_GlyphItems[j].IsDiacritic())
-                        break;
-                    m_GlyphItems[j--].adjustLinearPosX(nDiff);
-                }
+                if (!m_GlyphItems[j].IsDiacritic())
+                    break;
+                m_GlyphItems[j--].adjustLinearPosX(nDiff);
             }
+
+            // This is a Kashida insertion position, mark it. Kashida glyphs
+            // will be inserted below.
+            if (pKashidaArray && pKashidaArray[nCharPos])
+                pKashidas[i] = nDiff;
+
             i++;
         }
 
         // Increment the delta, the loop above makes sure we do so only once
         // for every character (cluster) not for every glyph (otherwise we
-        // would apply it multiple times for each glyphs belonging to the same
-        // character which is wrong since DX adjustments are character based).
+        // would apply it multiple times for each glyph belonging to the same
+        // character which is wrong as DX adjustments are character based).
         nDelta += nDiff;
     }
 
     // Insert Kashida glyphs.
-    if (!bKashidaJustify || pKashidas.empty())
+    if (pKashidas.empty())
+        return;
+
+    // Find Kashida glyph width and index.
+    DeviceCoordinate nKashidaWidth = 0;
+    hb_codepoint_t nKashidaIndex = 0;
+    if (hb_font_get_glyph(GetFont().GetHbFont(), 0x0640, 0, &nKashidaIndex))
+        nKashidaWidth = GetFont().GetKashidaWidth();
+
+    if (nKashidaWidth <= 0)
+    {
+        SAL_WARN("vcl.gdi", "Asked to insert Kashidas in a font with 
zero-width Kashida");
         return;
+    }
 
     size_t nInserted = 0;
     for (auto const& pKashida : pKashidas)
diff --git a/vcl/source/gdi/gdimtf.cxx b/vcl/source/gdi/gdimtf.cxx
index 4d889f6d54d0..e15cf05b9cf5 100644
--- a/vcl/source/gdi/gdimtf.cxx
+++ b/vcl/source/gdi/gdimtf.cxx
@@ -978,7 +978,7 @@ void GDIMetaFile::Rotate( Degree10 nAngle10 )
             {
                 MetaTextArrayAction* pAct = 
static_cast<MetaTextArrayAction*>(pAction);
                 aMtf.AddAction( new MetaTextArrayAction( ImplGetRotatedPoint( 
pAct->GetPoint(), aRotAnchor, aRotOffset, fSin, fCos ),
-                                                                              
pAct->GetText(), pAct->GetDXArray(), pAct->GetIndex(), pAct->GetLen() ) );
+                                                                              
pAct->GetText(), pAct->GetDXArray(), pAct->GetKashidaArray(), pAct->GetIndex(), 
pAct->GetLen() ) );
             }
             break;
 
@@ -1452,7 +1452,7 @@ tools::Rectangle GDIMetaFile::GetBoundRect( OutputDevice& 
i_rReference ) const
             tools::Rectangle aRect;
             // hdu said base = index
             aMapVDev->GetTextBoundRect( aRect, pAct->GetText(), 
pAct->GetIndex(), pAct->GetIndex(), pAct->GetLen(),
-                                       0, pAct->GetDXArray() );
+                                       0, pAct->GetDXArray(), 
pAct->GetKashidaArray() );
             Point aPt( pAct->GetPoint() );
             aRect.Move( aPt.X(), aPt.Y() );
             ImplActionBounds( aBound, OutputDevice::LogicToLogic( aRect, 
aMapVDev->GetMapMode(), GetPrefMapMode() ), aClipStack );
diff --git a/vcl/source/gdi/impglyphitem.cxx b/vcl/source/gdi/impglyphitem.cxx
index 51bff5966d32..6cbb245e922b 100644
--- a/vcl/source/gdi/impglyphitem.cxx
+++ b/vcl/source/gdi/impglyphitem.cxx
@@ -297,27 +297,6 @@ static SalLayoutGlyphs makeGlyphsSubset(const 
SalLayoutGlyphs& source,
         // would assert on flags being different.
         cloned->SetFlags(cloned->GetFlags()
                          | outputDevice->GetBiDiLayoutFlags(text, index, index 
+ len));
-        // SalLayoutFlags::KashidaJustification is set only if any glyph
-        // in the range has GlyphItemFlags::ALLOW_KASHIDA (otherwise unset it).
-        if (cloned->GetFlags() & SalLayoutFlags::KashidaJustification)
-        {
-            bool hasKashida = false;
-            for (const GlyphItem& item : *cloned)
-            {
-                if (item.AllowKashida())
-                {
-                    hasKashida = true;
-                    break;
-                }
-            }
-            if (!hasKashida)
-                cloned->SetFlags(cloned->GetFlags() & 
~SalLayoutFlags::KashidaJustification);
-        }
-#ifdef DBG_UTIL
-        else
-            for (const GlyphItem& item : *cloned)
-                assert(!item.AllowKashida());
-#endif
         ret.AppendImpl(cloned);
     }
     return ret;
@@ -433,7 +412,7 @@ SalLayoutGlyphsCache::GetLayoutGlyphs(VclPtr<const 
OutputDevice> outputDevice, c
                 // Check if the subset result really matches what we would get 
normally,
                 // to make sure corner cases are handled well (see 
SalLayoutGlyphsImpl::cloneCharRange()).
                 std::unique_ptr<SalLayout> layout
-                    = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 
0), nLogicWidth, {},
+                    = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 
0), nLogicWidth, {}, {},
                                                SalLayoutFlags::GlyphItemsOnly, 
layoutCache);
                 assert(layout);
                 checkGlyphsEqual(mLastTemporaryGlyphs, layout->GetGlyphs());
@@ -458,7 +437,7 @@ SalLayoutGlyphsCache::GetLayoutGlyphs(VclPtr<const 
OutputDevice> outputDevice, c
         layoutCache = tmpLayoutCache.get();
     }
     std::unique_ptr<SalLayout> layout
-        = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), 
nLogicWidth, {},
+        = outputDevice->ImplLayout(text, nIndex, nLen, Point(0, 0), 
nLogicWidth, {}, {},

... etc. - the rest is truncated

Reply via email to