diff options
author | Khaled Hosny <khaled@aliftype.com> | 2022-08-08 22:08:37 +0200 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2022-08-14 21:10:24 +0200 |
commit | 3901e029bd39575f700e69a73818565d62226a23 (patch) | |
tree | 2851945d9b13ac071d1e21af53288f46125dbc32 | |
parent | cc54063b915a58db9133c919c151ec7e6209f4cd (diff) |
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 <caolanm@redhat.com>
69 files changed, 346 insertions, 228 deletions
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, {}, {}, SalLayoutFlags::GlyphItemsOnly, layoutCache); if (layout) { diff --git a/vcl/source/gdi/metaact.cxx b/vcl/source/gdi/metaact.cxx index 4d6705e39115..cd06b016d4be 100644 --- a/vcl/source/gdi/metaact.cxx +++ b/vcl/source/gdi/metaact.cxx @@ -601,6 +601,7 @@ MetaTextArrayAction::MetaTextArrayAction( const MetaTextArrayAction& rAction ) : maStartPt ( rAction.maStartPt ), maStr ( rAction.maStr ), maDXAry ( rAction.maDXAry ), + maKashidaAry( rAction.maKashidaAry ), mnIndex ( rAction.mnIndex ), mnLen ( rAction.mnLen ) { @@ -609,12 +610,14 @@ MetaTextArrayAction::MetaTextArrayAction( const MetaTextArrayAction& rAction ) : MetaTextArrayAction::MetaTextArrayAction( const Point& rStartPt, OUString aStr, std::vector<sal_Int32> aDXAry, + std::vector<sal_Bool> aKashidaAry, sal_Int32 nIndex, sal_Int32 nLen ) : MetaAction ( MetaActionType::TEXTARRAY ), maStartPt ( rStartPt ), maStr (std::move( aStr )), maDXAry (std::move( aDXAry )), + maKashidaAry(std::move( aKashidaAry )), mnIndex ( nIndex ), mnLen ( nLen ) { @@ -623,12 +626,14 @@ MetaTextArrayAction::MetaTextArrayAction( const Point& rStartPt, MetaTextArrayAction::MetaTextArrayAction( const Point& rStartPt, OUString aStr, o3tl::span<const sal_Int32> pDXAry, + o3tl::span<const sal_Bool> pKashidaAry, sal_Int32 nIndex, sal_Int32 nLen ) : MetaAction ( MetaActionType::TEXTARRAY ), maStartPt ( rStartPt ), maStr (std::move( aStr )), maDXAry ( pDXAry.begin(), pDXAry.end() ), + maKashidaAry( pKashidaAry.begin(), pKashidaAry.end() ), mnIndex ( nIndex ), mnLen ( nLen ) { @@ -640,7 +645,7 @@ MetaTextArrayAction::~MetaTextArrayAction() void MetaTextArrayAction::Execute( OutputDevice* pOut ) { - pOut->DrawTextArray( maStartPt, maStr, maDXAry, mnIndex, mnLen ); + pOut->DrawTextArray( maStartPt, maStr, maDXAry, maKashidaAry, mnIndex, mnLen ); } rtl::Reference<MetaAction> MetaTextArrayAction::Clone() const @@ -669,6 +674,11 @@ void MetaTextArrayAction::SetDXArray(std::vector<sal_Int32> aArray) maDXAry = std::move(aArray); } +void MetaTextArrayAction::SetKashidaArray(std::vector<sal_Bool> aArray) +{ + maKashidaAry = std::move(aArray); +} + MetaStretchTextAction::MetaStretchTextAction() : MetaAction ( MetaActionType::STRETCHTEXT ), mnWidth ( 0 ), diff --git a/vcl/source/gdi/pdfwriter.cxx b/vcl/source/gdi/pdfwriter.cxx index 1a8d407c7247..3e20c12f2720 100644 --- a/vcl/source/gdi/pdfwriter.cxx +++ b/vcl/source/gdi/pdfwriter.cxx @@ -82,10 +82,11 @@ void PDFWriter::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 ) { - xImplementation->drawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen ); + xImplementation->drawTextArray( rStartPt, rStr, pDXAry, pKashidaAry, nIndex, nLen ); } void PDFWriter::DrawStretchText( diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx index f16bdc090f03..f0413d635a54 100644 --- a/vcl/source/gdi/pdfwriter_impl.cxx +++ b/vcl/source/gdi/pdfwriter_impl.cxx @@ -6616,14 +6616,14 @@ void PDFWriterImpl::drawText( const Point& rPos, const OUString& rText, sal_Int3 const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()-> GetLayoutGlyphs( this, rText, nIndex, nLen ); std::unique_ptr<SalLayout> pLayout = ImplLayout( rText, nIndex, nLen, rPos, - 0, {}, SalLayoutFlags::NONE, nullptr, layoutGlyphs ); + 0, {}, {}, SalLayoutFlags::NONE, nullptr, layoutGlyphs ); if( pLayout ) { drawLayout( *pLayout, rText, bTextLines ); } } -void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, o3tl::span<const sal_Int32> pDXArray, sal_Int32 nIndex, sal_Int32 nLen ) +void PDFWriterImpl::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 ) { MARK( "drawText with array" ); @@ -6633,7 +6633,7 @@ void PDFWriterImpl::drawTextArray( const Point& rPos, const OUString& rText, o3t // this also enforces font substitution and sets the font on SalGraphics const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()-> GetLayoutGlyphs( this, rText, nIndex, nLen ); - std::unique_ptr<SalLayout> pLayout = ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray, + std::unique_ptr<SalLayout> pLayout = ImplLayout( rText, nIndex, nLen, rPos, 0, pDXArray, pKashidaArray, SalLayoutFlags::NONE, nullptr, layoutGlyphs ); if( pLayout ) { @@ -6652,7 +6652,7 @@ void PDFWriterImpl::drawStretchText( const Point& rPos, sal_uLong nWidth, const const SalLayoutGlyphs* layoutGlyphs = SalLayoutGlyphsCache::self()-> GetLayoutGlyphs( this, rText, nIndex, nLen, nWidth ); std::unique_ptr<SalLayout> pLayout = ImplLayout( rText, nIndex, nLen, rPos, nWidth, - {}, SalLayoutFlags::NONE, nullptr, layoutGlyphs ); + {}, {}, SalLayoutFlags::NONE, nullptr, layoutGlyphs ); if( pLayout ) { drawLayout( *pLayout, rText, true ); diff --git a/vcl/source/gdi/pdfwriter_impl2.cxx b/vcl/source/gdi/pdfwriter_impl2.cxx index f4c60a661684..cac3813cdedc 100644 --- a/vcl/source/gdi/pdfwriter_impl2.cxx +++ b/vcl/source/gdi/pdfwriter_impl2.cxx @@ -813,7 +813,7 @@ void PDFWriterImpl::playMetafile( const GDIMetaFile& i_rMtf, vcl::PDFExtOutDevDa case MetaActionType::TEXTARRAY: { const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction); - m_rOuterFace.DrawTextArray( pA->GetPoint(), pA->GetText(), pA->GetDXArray(), pA->GetIndex(), pA->GetLen() ); + m_rOuterFace.DrawTextArray( pA->GetPoint(), pA->GetText(), pA->GetDXArray(), pA->GetKashidaArray(), pA->GetIndex(), pA->GetLen() ); } break; diff --git a/vcl/source/gdi/textlayout.cxx b/vcl/source/gdi/textlayout.cxx index 416c68343f47..25bf47767ec7 100644 --- a/vcl/source/gdi/textlayout.cxx +++ b/vcl/source/gdi/textlayout.cxx @@ -209,7 +209,7 @@ namespace vcl std::vector<sal_Int32> aCharWidths; tools::Long nTextWidth = GetTextArray( _rText, &aCharWidths, _nStartIndex, _nLength ); - m_rTargetDevice.DrawTextArray( _rStartPoint, _rText, aCharWidths, _nStartIndex, _nLength ); + m_rTargetDevice.DrawTextArray( _rStartPoint, _rText, aCharWidths, {}, _nStartIndex, _nLength ); m_aCompleteTextRect.Union( tools::Rectangle( _rStartPoint, Size( nTextWidth, m_rTargetDevice.GetTextHeight() ) ) ); } diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index 2f7199fa3fa3..06f43fa2d3ea 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -872,7 +872,7 @@ void OutputDevice::DrawText( const Point& rStartPt, const OUString& rStr, if(mpFontInstance->mpConversion) pLayoutCache = nullptr; - std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, {}, eDefaultLayout, nullptr, pLayoutCache); + std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, {}, {}, eDefaultLayout, nullptr, pLayoutCache); if(pSalLayout) { ImplDrawText( *pSalLayout ); @@ -921,6 +921,7 @@ float OutputDevice::approximate_digit_width() const void OutputDevice::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, SalLayoutFlags flags, const SalLayoutGlyphs* pSalLayoutCache ) { @@ -931,7 +932,7 @@ void OutputDevice::DrawTextArray( const Point& rStartPt, const OUString& rStr, nLen = rStr.getLength() - nIndex; } if ( mpMetaFile ) - mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, nIndex, nLen ) ); + mpMetaFile->AddAction( new MetaTextArrayAction( rStartPt, rStr, pDXAry, pKashidaAry, nIndex, nLen ) ); if ( !IsDeviceOutputNecessary() ) return; @@ -943,14 +944,14 @@ void OutputDevice::DrawTextArray( const Point& rStartPt, const OUString& rStr, if( mbOutputClipped ) return; - std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, pDXAry, flags, nullptr, pSalLayoutCache); + std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, rStartPt, 0, pDXAry, pKashidaAry, flags, nullptr, pSalLayoutCache); if( pSalLayout ) { ImplDrawText( *pSalLayout ); } if( mpAlphaVDev ) - mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, nIndex, nLen, flags ); + mpAlphaVDev->DrawTextArray( rStartPt, rStr, pDXAry, pKashidaAry, nIndex, nLen, flags ); } tools::Long OutputDevice::GetTextArray( const OUString& rStr, std::vector<sal_Int32>* pDXAry, @@ -968,7 +969,7 @@ tools::Long OutputDevice::GetTextArray( const OUString& rStr, std::vector<sal_In // do layout std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, - Point(0,0), 0, {}, eDefaultLayout, pLayoutCache, pSalLayoutCache); + Point(0,0), 0, {}, {}, eDefaultLayout, pLayoutCache, pSalLayoutCache); if( !pSalLayout ) { // The caller expects this to init the elements of pDXAry. @@ -1076,7 +1077,7 @@ void OutputDevice::GetCaretPositions( const OUString& rStr, sal_Int32* pCaretXAr nLen = rStr.getLength() - nIndex; // layout complex text - std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, Point(0, 0), 0, {}, + std::unique_ptr<SalLayout> pSalLayout = ImplLayout(rStr, nIndex, nLen, Point(0, 0), 0, {}, {}, eDefaultLayout, nullptr, pGlyphs); if( !pSalLayout ) { @@ -1312,7 +1313,9 @@ OutputDevice::FontMappingUseData OutputDevice::FinishTrackingFontMappingUse() std::unique_ptr<SalLayout> OutputDevice::ImplLayout(const OUString& rOrigStr, sal_Int32 nMinIndex, sal_Int32 nLen, const Point& rLogicalPos, tools::Long nLogicalWidth, - o3tl::span<const sal_Int32> pDXArray, SalLayoutFlags flags, + o3tl::span<const sal_Int32> pDXArray, + o3tl::span<const sal_Bool> pKashidaArray, + SalLayoutFlags flags, vcl::text::TextLayoutCache const* pLayoutCache, const SalLayoutGlyphs* pGlyphs) const { @@ -1427,6 +1430,9 @@ std::unique_ptr<SalLayout> OutputDevice::ImplLayout(const OUString& rOrigStr, } } + if (!pKashidaArray.empty()) + aLayoutArgs.SetKashidaArray(pKashidaArray.data()); + // get matching layout object for base font std::unique_ptr<SalLayout> pSalLayout = mpGraphics->GetTextLayout(0); @@ -1505,7 +1511,7 @@ sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, tools::Long nTextWid const SalLayoutGlyphs* pGlyphs) const { std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rStr, nIndex, nLen, - Point(0,0), 0, {}, eDefaultLayout, pLayoutCache, pGlyphs); + Point(0,0), 0, {}, {}, eDefaultLayout, pLayoutCache, pGlyphs); sal_Int32 nRetVal = -1; if( pSalLayout ) { @@ -1539,7 +1545,7 @@ sal_Int32 OutputDevice::GetTextBreak( const OUString& rStr, tools::Long nTextWid rHyphenPos = -1; std::unique_ptr<SalLayout> pSalLayout = ImplLayout( rStr, nIndex, nLen, - Point(0,0), 0, {}, eDefaultLayout, pLayoutCache, pGlyphs); + Point(0,0), 0, {}, {}, eDefaultLayout, pLayoutCache, pGlyphs); sal_Int32 nRetVal = -1; if( pSalLayout ) { @@ -2397,6 +2403,7 @@ bool OutputDevice::GetTextBoundRect( tools::Rectangle& rRect, const OUString& rStr, sal_Int32 nBase, sal_Int32 nIndex, sal_Int32 nLen, sal_uLong nLayoutWidth, o3tl::span<const sal_Int32> pDXAry, + o3tl::span<const sal_Bool> pKashidaAry, const SalLayoutGlyphs* pGlyphs ) const { bool bRet = false; @@ -2410,7 +2417,7 @@ bool OutputDevice::GetTextBoundRect( tools::Rectangle& rRect, { sal_Int32 nStart = std::min( nBase, nIndex ); sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart; - pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry ); + pSalLayout = ImplLayout( rStr, nStart, nOfsLen, aPoint, nLayoutWidth, pDXAry, pKashidaAry ); if( pSalLayout ) { nXOffset = pSalLayout->GetTextWidth(); @@ -2421,7 +2428,7 @@ bool OutputDevice::GetTextBoundRect( tools::Rectangle& rRect, } } - pSalLayout = ImplLayout(rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry, eDefaultLayout, + pSalLayout = ImplLayout(rStr, nIndex, nLen, aPoint, nLayoutWidth, pDXAry, pKashidaAry, eDefaultLayout, nullptr, pGlyphs); if( pSalLayout ) { @@ -2461,7 +2468,9 @@ bool OutputDevice::GetTextBoundRect( tools::Rectangle& rRect, bool OutputDevice::GetTextOutlines( basegfx::B2DPolyPolygonVector& rVector, const OUString& rStr, sal_Int32 nBase, sal_Int32 nIndex, sal_Int32 nLen, - sal_uLong nLayoutWidth, o3tl::span<const sal_Int32> pDXArray ) const + sal_uLong nLayoutWidth, + o3tl::span<const sal_Int32> pDXArray, + o3tl::span<const sal_Bool> pKashidaArray ) const { if (!InitFont()) return false; @@ -2491,7 +2500,7 @@ bool OutputDevice::GetTextOutlines( basegfx::B2DPolyPolygonVector& rVector, { sal_Int32 nStart = std::min( nBase, nIndex ); sal_Int32 nOfsLen = std::max( nBase, nIndex ) - nStart; - pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nLayoutWidth, pDXArray ); + pSalLayout = ImplLayout( rStr, nStart, nOfsLen, Point(0,0), nLayoutWidth, pDXArray, pKashidaArray); if( pSalLayout ) { nXOffset = pSalLayout->GetTextWidth(); @@ -2502,7 +2511,7 @@ bool OutputDevice::GetTextOutlines( basegfx::B2DPolyPolygonVector& rVector, } } - pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray ); + pSalLayout = ImplLayout( rStr, nIndex, nLen, Point(0,0), nLayoutWidth, pDXArray, pKashidaArray ); if( pSalLayout ) { bRet = pSalLayout->GetOutline(rVector); @@ -2548,14 +2557,15 @@ bool OutputDevice::GetTextOutlines( basegfx::B2DPolyPolygonVector& rVector, bool OutputDevice::GetTextOutlines( PolyPolyVector& rResultVector, const OUString& rStr, sal_Int32 nBase, sal_Int32 nIndex, sal_Int32 nLen, - sal_uLong nLayoutWidth, o3tl::span<const sal_Int32> pDXArray ) const + sal_uLong nLayoutWidth, o3tl::span<const sal_Int32> pDXArray, + o3tl::span<const sal_Bool> pKashidaArray ) const { rResultVector.clear(); // get the basegfx polypolygon vector basegfx::B2DPolyPolygonVector aB2DPolyPolyVector; if( !GetTextOutlines( aB2DPolyPolyVector, rStr, nBase, nIndex, nLen, - nLayoutWidth, pDXArray ) ) + nLayoutWidth, pDXArray, pKashidaArray ) ) return false; // convert to a tool polypolygon vector diff --git a/vcl/source/outdev/transparent.cxx b/vcl/source/outdev/transparent.cxx index a4019ca94f85..536498703bb5 100644 --- a/vcl/source/outdev/transparent.cxx +++ b/vcl/source/outdev/transparent.cxx @@ -1193,7 +1193,7 @@ tools::Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevic // #105987# ImplLayout takes everything in logical coordinates std::unique_ptr<SalLayout> pSalLayout = rOut.ImplLayout( rTextAct.GetText(), rTextAct.GetIndex(), rTextAct.GetLen(), rTextAct.GetPoint(), - 0, rTextAct.GetDXArray()); + 0, rTextAct.GetDXArray(), rTextAct.GetKashidaArray() ); if( pSalLayout ) { tools::Rectangle aBoundRect( rOut.ImplGetTextBoundRect( *pSalLayout ) ); diff --git a/vcl/source/text/ImplLayoutArgs.cxx b/vcl/source/text/ImplLayoutArgs.cxx index 80e85a66dc26..45b951a9845d 100644 --- a/vcl/source/text/ImplLayoutArgs.cxx +++ b/vcl/source/text/ImplLayoutArgs.cxx @@ -39,6 +39,7 @@ ImplLayoutArgs::ImplLayoutArgs(const OUString& rStr, int nMinCharPos, int nEndCh , m_pTextLayoutCache(pLayoutCache) , mpDXArray(nullptr) , mpAltNaturalDXArray(nullptr) + , mpKashidaArray(nullptr) , mnLayoutWidth(0) , mnOrientation(0) { @@ -97,6 +98,11 @@ void ImplLayoutArgs::SetAltNaturalDXArray(double const* pDXArray) mpAltNaturalDXArray = pDXArray; } +void ImplLayoutArgs::SetKashidaArray(sal_Bool const* pKashidaArray) +{ + mpKashidaArray = pKashidaArray; +} + void ImplLayoutArgs::SetOrientation(Degree10 nOrientation) { mnOrientation = nOrientation; } void ImplLayoutArgs::ResetPos() { maRuns.ResetPos(); } @@ -269,7 +275,6 @@ std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs TEST(DisableKerning); TEST(KerningAsian); TEST(Vertical); - TEST(KashidaJustification); TEST(ForFallback); #undef TEST s << "}"; @@ -333,6 +338,31 @@ std::ostream& operator<<(std::ostream& s, vcl::text::ImplLayoutArgs const& rArgs else s << "NULL"; + s << ",KashidaArray="; + if (rArgs.mpKashidaArray) + { + s << "["; + int count = rArgs.mnEndCharPos - rArgs.mnMinCharPos; + lim = count; + if (lim > 10) + lim = 7; + for (int i = 0; i < lim; i++) + { + s << rArgs.mpKashidaArray[i]; + if (i < lim - 1) + s << ","; + } + if (count > lim) + { + if (count > lim + 1) + s << "..."; + s << rArgs.mpKashidaArray[count - 1]; + } + s << "]"; + } + else + s << "NULL"; + s << ",LayoutWidth=" << rArgs.mnLayoutWidth; s << "}"; diff --git a/vcl/source/window/menuitemlist.cxx b/vcl/source/window/menuitemlist.cxx index 173a6204eb48..0efae5b43b8c 100644 --- a/vcl/source/window/menuitemlist.cxx +++ b/vcl/source/window/menuitemlist.cxx @@ -48,7 +48,7 @@ SalLayoutGlyphs* MenuItemData::GetTextGlyphs(const OutputDevice* pOutputDevice) OUString aNonMnemonicString = OutputDevice::GetNonMnemonicString(aText); std::unique_ptr<SalLayout> pLayout = pOutputDevice->ImplLayout(aNonMnemonicString, 0, aNonMnemonicString.getLength(), - Point(0, 0), 0, {}, SalLayoutFlags::GlyphItemsOnly); + Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly); if (!pLayout) return nullptr; diff --git a/vcl/source/window/status.cxx b/vcl/source/window/status.cxx index 8c701b496b14..81ce41db73e7 100644 --- a/vcl/source/window/status.cxx +++ b/vcl/source/window/status.cxx @@ -79,7 +79,7 @@ SalLayoutGlyphs* ImplStatusItem::GetTextGlyphs(const OutputDevice* outputDevice) if(!mLayoutGlyphsCache.has_value()) { std::unique_ptr<SalLayout> pSalLayout = outputDevice->ImplLayout( - maText, 0, -1, Point(0, 0), 0, {}, SalLayoutFlags::GlyphItemsOnly); + maText, 0, -1, Point(0, 0), 0, {}, {}, SalLayoutFlags::GlyphItemsOnly); mLayoutGlyphsCache = pSalLayout ? pSalLayout->GetGlyphs() : SalLayoutGlyphs(); } return mLayoutGlyphsCache->IsValid() ? &mLayoutGlyphsCache.value() : nullptr; |