tdf#61444 Correct Writer text layout across formatting changes
Previously, Writer performed text layout for each span of text separately. This caused incorrect kerning when the text style changed mid-word, for example by highlighting or changing the color of a single letter. This change updates Writer so it will also consider neighboring text while performing text layout. Change-Id: I511096c009343f39cc1b9ba745909c5b8cbad86f Reviewed-on: https://gerrit.libreoffice.org/c/core/+/167016 Tested-by: Jenkins Reviewed-by: Jonathan Clark <jonathan@libreoffice.org>
This commit is contained in:
parent
5e88d86d8c
commit
30d376fb7d
22 changed files with 605 additions and 184 deletions
|
@ -1059,6 +1059,16 @@ public:
|
|||
vcl::text::TextLayoutCache const* = nullptr,
|
||||
SalLayoutGlyphs const*const pLayoutCache = nullptr) const;
|
||||
|
||||
void DrawPartialTextArray(const Point& rStartPt, const OUString& rStr, KernArraySpan aKernArray,
|
||||
std::span<const sal_Bool> pKashidaAry, sal_Int32 nIndex,
|
||||
sal_Int32 nLen, sal_Int32 nPartIndex, sal_Int32 nPartLen,
|
||||
SalLayoutFlags flags = SalLayoutFlags::NONE,
|
||||
const SalLayoutGlyphs* pLayoutCache = nullptr);
|
||||
double GetPartialTextArray(const OUString& rStr, KernArray* pDXAry, sal_Int32 nIndex,
|
||||
sal_Int32 nLen, sal_Int32 nPartIndex, sal_Int32 nPartLen,
|
||||
bool bCaret = false, const vcl::text::TextLayoutCache* = nullptr,
|
||||
const SalLayoutGlyphs* pLayoutCache = nullptr) const;
|
||||
|
||||
SAL_DLLPRIVATE void GetCaretPositions( const OUString&, KernArray& rCaretXArray,
|
||||
sal_Int32 nIndex, sal_Int32 nLen,
|
||||
const SalLayoutGlyphs* pGlyphs = nullptr ) const;
|
||||
|
|
|
@ -97,6 +97,16 @@ public:
|
|||
virtual sal_Int32 GetTextBreak(double nMaxWidth, double nCharExtra, int nFactor) const = 0;
|
||||
virtual double FillDXArray( std::vector<double>* pDXArray, const OUString& rStr ) const = 0;
|
||||
virtual double GetTextWidth() const { return FillDXArray( nullptr, {} ); }
|
||||
|
||||
virtual double FillPartialDXArray(std::vector<double>* pDXArray, const OUString& rStr,
|
||||
sal_Int32 skipStart, sal_Int32 amt) const
|
||||
= 0;
|
||||
|
||||
virtual double GetPartialTextWidth(sal_Int32 skipStart, sal_Int32 amt) const
|
||||
{
|
||||
return FillPartialDXArray(nullptr, {}, skipStart, amt);
|
||||
}
|
||||
|
||||
virtual void GetCaretPositions( std::vector<double>& rCaretPositions, const OUString& rStr ) const = 0;
|
||||
virtual bool IsKashidaPosValid ( int /*nCharPos*/, int /*nNextCharPos*/ ) const = 0; // i60594
|
||||
|
||||
|
|
|
@ -2507,7 +2507,8 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest7, testTdf87922)
|
|||
const OUString& rText = aNodeIndex.GetNode().GetTextNode()->GetText();
|
||||
sal_Int32 nLength = rText.getLength();
|
||||
SwDrawTextInfo aDrawTextInfo(pWrtShell, *pWrtShell->GetOut(), pScriptInfo, rText,
|
||||
TextFrameIndex(0), TextFrameIndex(nLength));
|
||||
TextFrameIndex(0), TextFrameIndex(nLength),
|
||||
/*layout context*/ std::nullopt);
|
||||
// Root -> page -> body -> text.
|
||||
SwTextFrame* pTextFrame
|
||||
= static_cast<SwTextFrame*>(pWrtShell->GetLayout()->GetLower()->GetLower()->GetLower());
|
||||
|
|
Binary file not shown.
|
@ -26,6 +26,7 @@
|
|||
#include <swtypes.hxx>
|
||||
#include "TextFrameIndex.hxx"
|
||||
#include <swdllapi.h>
|
||||
#include "swporlayoutcontext.hxx"
|
||||
|
||||
class SwTextFrame;
|
||||
class SwViewShell;
|
||||
|
@ -44,127 +45,94 @@ class SwUnderlineFont;
|
|||
// encapsulates information for drawing text
|
||||
class SW_DLLPUBLIC SwDrawTextInfo
|
||||
{
|
||||
const SwTextFrame* m_pFrame;
|
||||
const SwTextFrame* m_pFrame = nullptr;
|
||||
VclPtr<OutputDevice> m_pOut;
|
||||
SwViewShell const * m_pSh;
|
||||
SwViewShell const* m_pSh;
|
||||
const SwScriptInfo* m_pScriptInfo;
|
||||
Point m_aPos;
|
||||
vcl::text::TextLayoutCache const* m_pCachedVclData;
|
||||
OUString m_aText;
|
||||
sw::WrongListIterator* m_pWrong;
|
||||
sw::WrongListIterator* m_pGrammarCheck;
|
||||
sw::WrongListIterator* m_pSmartTags;
|
||||
sw::WrongListIterator* m_pWrong = nullptr;
|
||||
sw::WrongListIterator* m_pGrammarCheck = nullptr;
|
||||
sw::WrongListIterator* m_pSmartTags = nullptr;
|
||||
Size m_aSize;
|
||||
SwFont *m_pFnt;
|
||||
SwUnderlineFont* m_pUnderFnt;
|
||||
TextFrameIndex* m_pHyphPos;
|
||||
tools::Long m_nKanaDiff;
|
||||
SwFont* m_pFnt = nullptr;
|
||||
SwUnderlineFont* m_pUnderFnt = nullptr;
|
||||
TextFrameIndex* m_pHyphPos = nullptr;
|
||||
tools::Long m_nKanaDiff = 0;
|
||||
TextFrameIndex m_nIdx;
|
||||
TextFrameIndex m_nLen;
|
||||
TextFrameIndex m_nMeasureLen;
|
||||
TextFrameIndex m_nMeasureLen = TextFrameIndex{ COMPLETE_STRING };
|
||||
std::optional<SwLinePortionLayoutContext> m_nLayoutContext;
|
||||
/// this is not a string index
|
||||
sal_Int32 m_nOfst;
|
||||
sal_Int32 m_nOfst = 0;
|
||||
sal_uInt16 m_nWidth;
|
||||
sal_uInt16 m_nAscent;
|
||||
sal_uInt16 m_nCompress;
|
||||
tools::Long m_nCharacterSpacing;
|
||||
tools::Long m_nSpace;
|
||||
tools::Long m_nKern;
|
||||
TextFrameIndex m_nNumberOfBlanks;
|
||||
sal_uInt8 m_nCursorBidiLevel;
|
||||
sal_uInt16 m_nAscent = 0;
|
||||
sal_uInt16 m_nCompress = 0;
|
||||
tools::Long m_nCharacterSpacing = 0;
|
||||
tools::Long m_nSpace = 0;
|
||||
tools::Long m_nKern = 0;
|
||||
TextFrameIndex m_nNumberOfBlanks = TextFrameIndex{ 0 };
|
||||
sal_uInt8 m_nCursorBidiLevel = 0;
|
||||
bool m_bBullet : 1;
|
||||
bool m_bUpper : 1; // for small caps: upper case flag
|
||||
bool m_bDrawSpace : 1; // for small caps: underline/ line through
|
||||
bool m_bGreyWave : 1; // grey wave line for extended text input
|
||||
bool m_bUpper : 1 = false; // for small caps: upper case flag
|
||||
bool m_bDrawSpace : 1 = false; // for small caps: underline/ line through
|
||||
bool m_bGreyWave : 1 = false; // grey wave line for extended text input
|
||||
// For underlining we need to know, if a section is right in front of a
|
||||
// whole block or a fix margin section.
|
||||
bool m_bSpaceStop : 1;
|
||||
bool m_bSnapToGrid : 1; // Does paragraph snap to grid?
|
||||
bool m_bSpaceStop : 1 = false;
|
||||
bool m_bSnapToGrid : 1 = false; // Does paragraph snap to grid?
|
||||
// Paint text as if text has LTR direction, used for line numbering
|
||||
bool m_bIgnoreFrameRTL : 1;
|
||||
bool m_bIgnoreFrameRTL : 1 = false;
|
||||
// GetModelPositionForViewPoint should not return the next position if screen position is
|
||||
// inside second half of bound rect, used for Accessibility
|
||||
bool m_bPosMatchesBounds :1;
|
||||
bool m_bPosMatchesBounds : 1 = false;
|
||||
|
||||
#ifdef DBG_UTIL
|
||||
// These flags should control that the appropriate Set-function has been
|
||||
// called before calling the Get-function of a member
|
||||
bool m_bPos : 1;
|
||||
bool m_bWrong : 1;
|
||||
bool m_bGrammarCheck : 1;
|
||||
bool m_bSize : 1;
|
||||
bool m_bFnt : 1;
|
||||
bool m_bHyph : 1;
|
||||
bool m_bKana : 1;
|
||||
bool m_bOfst : 1;
|
||||
bool m_bAscent: 1;
|
||||
bool m_bCharacterSpacing : 1;
|
||||
bool m_bSpace : 1;
|
||||
bool m_bNumberOfBlanks : 1;
|
||||
bool m_bUppr : 1;
|
||||
bool m_bDrawSp: 1;
|
||||
bool m_bPos : 1 = false;
|
||||
bool m_bWrong : 1 = false;
|
||||
bool m_bGrammarCheck : 1 = false;
|
||||
bool m_bSize : 1 = false;
|
||||
bool m_bFnt : 1 = false;
|
||||
bool m_bHyph : 1 = false;
|
||||
bool m_bKana : 1 = false;
|
||||
bool m_bOfst : 1 = false;
|
||||
bool m_bAscent : 1 = false;
|
||||
bool m_bCharacterSpacing : 1 = false;
|
||||
bool m_bSpace : 1 = false;
|
||||
bool m_bNumberOfBlanks : 1 = false;
|
||||
bool m_bUppr : 1 = false;
|
||||
bool m_bDrawSp : 1 = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
/// constructor for simple strings
|
||||
SwDrawTextInfo( SwViewShell const *pSh, OutputDevice &rOut,
|
||||
const OUString &rText, sal_Int32 const nIdx, sal_Int32 const nLen,
|
||||
sal_uInt16 nWidth = 0, bool bBullet = false)
|
||||
: SwDrawTextInfo(pSh, rOut, nullptr, rText, TextFrameIndex(nIdx), TextFrameIndex(nLen), nWidth, bBullet)
|
||||
SwDrawTextInfo(SwViewShell const* pSh, OutputDevice& rOut, const OUString& rText,
|
||||
sal_Int32 const nIdx, sal_Int32 const nLen, sal_uInt16 nWidth = 0,
|
||||
bool bBullet = false)
|
||||
: SwDrawTextInfo(pSh, rOut, nullptr, rText, TextFrameIndex(nIdx), TextFrameIndex(nLen),
|
||||
/*layout context*/ std::nullopt, nWidth, bBullet)
|
||||
{}
|
||||
/// constructor for text frame contents
|
||||
SwDrawTextInfo( SwViewShell const *pSh, OutputDevice &rOut, const SwScriptInfo* pSI,
|
||||
const OUString &rText, TextFrameIndex const nIdx, TextFrameIndex const nLen,
|
||||
sal_uInt16 nWidth = 0, bool bBullet = false,
|
||||
vcl::text::TextLayoutCache const*const pCachedVclData = nullptr)
|
||||
: m_pCachedVclData(pCachedVclData)
|
||||
SwDrawTextInfo(SwViewShell const* pSh, OutputDevice& rOut, const SwScriptInfo* pSI,
|
||||
const OUString& rText, TextFrameIndex const nIdx, TextFrameIndex const nLen,
|
||||
std::optional<SwLinePortionLayoutContext> nLayoutContext, sal_uInt16 nWidth = 0,
|
||||
bool bBullet = false,
|
||||
vcl::text::TextLayoutCache const* const pCachedVclData = nullptr)
|
||||
: m_pOut(&rOut)
|
||||
, m_pSh(pSh)
|
||||
, m_pScriptInfo(pSI)
|
||||
, m_pCachedVclData(pCachedVclData)
|
||||
, m_aText(rText)
|
||||
, m_nIdx(nIdx)
|
||||
, m_nLen(nLen)
|
||||
, m_nLayoutContext(nLayoutContext)
|
||||
, m_nWidth(nWidth)
|
||||
, m_bBullet(bBullet)
|
||||
{
|
||||
m_pFrame = nullptr;
|
||||
m_pSh = pSh;
|
||||
m_pOut = &rOut;
|
||||
m_pScriptInfo = pSI;
|
||||
m_aText = rText;
|
||||
m_nIdx = nIdx;
|
||||
m_nLen = nLen;
|
||||
m_nMeasureLen = TextFrameIndex(COMPLETE_STRING);
|
||||
m_nKern = 0;
|
||||
m_nCompress = 0;
|
||||
m_nWidth = nWidth;
|
||||
m_nNumberOfBlanks = TextFrameIndex(0);
|
||||
m_nCursorBidiLevel = 0;
|
||||
m_bBullet = bBullet;
|
||||
m_pUnderFnt = nullptr;
|
||||
m_bGreyWave = false;
|
||||
m_bSpaceStop = false;
|
||||
m_bSnapToGrid = false;
|
||||
m_bIgnoreFrameRTL = false;
|
||||
m_bPosMatchesBounds = false;
|
||||
|
||||
// These values are initialized but have to be set explicitly via their
|
||||
// Set-function before they may be accessed by their Get-function:
|
||||
m_pWrong = nullptr;
|
||||
m_pGrammarCheck = nullptr;
|
||||
m_pSmartTags = nullptr;
|
||||
m_pFnt = nullptr;
|
||||
m_pHyphPos = nullptr;
|
||||
m_nKanaDiff = 0;
|
||||
m_nOfst = 0;
|
||||
m_nAscent = 0;
|
||||
m_nCharacterSpacing = 0;
|
||||
m_nSpace = 0;
|
||||
m_bUpper = false;
|
||||
m_bDrawSpace = false;
|
||||
|
||||
#ifdef DBG_UTIL
|
||||
// these flags control whether the matching member variables have been
|
||||
// set by using the Set-function before they may be accessed by their
|
||||
// Get-function:
|
||||
m_bPos = m_bWrong = m_bGrammarCheck = m_bSize = m_bFnt = m_bAscent =
|
||||
m_bSpace = m_bNumberOfBlanks = m_bUppr =
|
||||
m_bDrawSp = m_bKana = m_bOfst = m_bHyph =
|
||||
m_bCharacterSpacing = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
const SwTextFrame* GetFrame() const
|
||||
|
@ -280,6 +248,8 @@ public:
|
|||
return m_nMeasureLen;
|
||||
}
|
||||
|
||||
std::optional<SwLinePortionLayoutContext> GetLayoutContext() const { return m_nLayoutContext; }
|
||||
|
||||
sal_Int32 GetOffset() const
|
||||
{
|
||||
#ifdef DBG_UTIL
|
||||
|
@ -445,6 +415,11 @@ public:
|
|||
m_nLen = nNewLen;
|
||||
}
|
||||
|
||||
void SetLayoutContext(std::optional<SwLinePortionLayoutContext> nNew)
|
||||
{
|
||||
m_nLayoutContext = nNew;
|
||||
}
|
||||
|
||||
void SetWrong(sw::WrongListIterator *const pNew)
|
||||
{
|
||||
m_pWrong = pNew;
|
||||
|
|
27
sw/source/core/inc/swporlayoutcontext.hxx
Normal file
27
sw/source/core/inc/swporlayoutcontext.hxx
Normal file
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
||||
/*
|
||||
* This file is part of the LibreOffice project.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sal/types.h>
|
||||
|
||||
class SwLinePortionLayoutContext
|
||||
{
|
||||
public:
|
||||
sal_Int32 m_nBegin = -1;
|
||||
sal_Int32 m_nEnd = -1;
|
||||
|
||||
SwLinePortionLayoutContext() = default;
|
||||
SwLinePortionLayoutContext(sal_Int32 nBegin, sal_Int32 nEnd)
|
||||
: m_nBegin(nBegin)
|
||||
, m_nEnd(nEnd)
|
||||
{
|
||||
}
|
||||
};
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
|
@ -80,7 +80,8 @@ bool maybeAdjustPositionsForBlockAdjust(TextFrameIndex& rCutPos, TextFrameIndex&
|
|||
TextFrameIndex& rBreakStart, sal_uInt16& rBreakWidth,
|
||||
sal_uInt16& rExtraBlankWidth, sal_uInt16& rMaxSizeDiff,
|
||||
const SwTextFormatInfo& rInf, const SwScriptInfo& rSI,
|
||||
sal_uInt16 maxComp)
|
||||
sal_uInt16 maxComp,
|
||||
std::optional<SwLinePortionLayoutContext> nLayoutContext)
|
||||
{
|
||||
const auto& adjObj = rInf.GetTextFrame()->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
|
||||
const SvxAdjust& adjust = adjObj.GetAdjust();
|
||||
|
@ -139,10 +140,10 @@ bool maybeAdjustPositionsForBlockAdjust(TextFrameIndex& rCutPos, TextFrameIndex&
|
|||
rBreakStart = rCutPos = newCutPos;
|
||||
rBreakPos = breakPos;
|
||||
|
||||
rInf.GetTextSize(&rSI, rInf.GetIdx(), breakPos - rInf.GetIdx(), maxComp, rBreakWidth,
|
||||
rMaxSizeDiff, rInf.GetCachedVclData().get());
|
||||
rInf.GetTextSize(&rSI, breakPos, rBreakStart - breakPos, maxComp, rExtraBlankWidth,
|
||||
rMaxSizeDiff, rInf.GetCachedVclData().get());
|
||||
rInf.GetTextSize(&rSI, rInf.GetIdx(), breakPos - rInf.GetIdx(), nLayoutContext, maxComp,
|
||||
rBreakWidth, rMaxSizeDiff, rInf.GetCachedVclData().get());
|
||||
rInf.GetTextSize(&rSI, breakPos, rBreakStart - breakPos, nLayoutContext, maxComp,
|
||||
rExtraBlankWidth, rMaxSizeDiff, rInf.GetCachedVclData().get());
|
||||
|
||||
return false; // require SwHolePortion creation
|
||||
}
|
||||
|
@ -247,8 +248,8 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
|||
( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
|
||||
{
|
||||
// call GetTextSize with maximum compression (for kanas)
|
||||
rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen,
|
||||
nMaxComp, m_nBreakWidth, nMaxSizeDiff );
|
||||
rInf.GetTextSize(&rSI, rInf.GetIdx(), nMaxLen, rPor.GetLayoutContext(), nMaxComp,
|
||||
m_nBreakWidth, nMaxSizeDiff);
|
||||
|
||||
if ( ( m_nBreakWidth <= nLineWidth ) || ( bUnbreakableNumberings && rPor.IsNumberPortion() ) )
|
||||
{
|
||||
|
@ -257,7 +258,8 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
|||
bool bRet = rPor.InFieldGrp()
|
||||
|| maybeAdjustPositionsForBlockAdjust(m_nCutPos, m_nBreakPos, m_nBreakStart,
|
||||
m_nBreakWidth, m_nExtraBlankWidth,
|
||||
nMaxSizeDiff, rInf, rSI, nMaxComp);
|
||||
nMaxSizeDiff, rInf, rSI, nMaxComp,
|
||||
rPor.GetLayoutContext());
|
||||
if( nItalic &&
|
||||
(m_nCutPos >= TextFrameIndex(rInf.GetText().getLength()) ||
|
||||
// #i48035# Needed for CalcFitToContent
|
||||
|
@ -419,8 +421,8 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
|||
if ( TextFrameIndex(COMPLETE_STRING) != m_nCutPos )
|
||||
{
|
||||
sal_uInt16 nMinSize;
|
||||
rInf.GetTextSize( &rSI, rInf.GetIdx(), m_nCutPos - rInf.GetIdx(),
|
||||
nMaxComp, nMinSize, nMaxSizeDiff );
|
||||
rInf.GetTextSize(&rSI, rInf.GetIdx(), m_nCutPos - rInf.GetIdx(),
|
||||
rPor.GetLayoutContext(), nMaxComp, nMinSize, nMaxSizeDiff);
|
||||
OSL_ENSURE( nMinSize <= nLineWidth, "What a Guess!!!" );
|
||||
}
|
||||
#endif
|
||||
|
@ -430,8 +432,8 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
|||
{
|
||||
// second check if everything fits to line
|
||||
m_nCutPos = m_nBreakPos = rInf.GetIdx() + nMaxLen - TextFrameIndex(1);
|
||||
rInf.GetTextSize( &rSI, rInf.GetIdx(), nMaxLen, nMaxComp,
|
||||
m_nBreakWidth, nMaxSizeDiff );
|
||||
rInf.GetTextSize(&rSI, rInf.GetIdx(), nMaxLen, rPor.GetLayoutContext(), nMaxComp,
|
||||
m_nBreakWidth, nMaxSizeDiff);
|
||||
|
||||
// The following comparison should always give true, otherwise
|
||||
// there likely has been a pixel rounding error in GetTextBreak
|
||||
|
@ -440,7 +442,8 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
|||
bool bRet = rPor.InFieldGrp()
|
||||
|| maybeAdjustPositionsForBlockAdjust(m_nCutPos, m_nBreakPos, m_nBreakStart,
|
||||
m_nBreakWidth, m_nExtraBlankWidth,
|
||||
nMaxSizeDiff, rInf, rSI, nMaxComp);
|
||||
nMaxSizeDiff, rInf, rSI, nMaxComp,
|
||||
rPor.GetLayoutContext());
|
||||
|
||||
if (nItalic && (m_nBreakPos + TextFrameIndex(1)) >= TextFrameIndex(rInf.GetText().getLength()))
|
||||
m_nBreakWidth += nItalic;
|
||||
|
@ -731,9 +734,8 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
|||
|
||||
if( nPorLen )
|
||||
{
|
||||
rInf.GetTextSize( &rSI, rInf.GetIdx(), nPorLen,
|
||||
nMaxComp, m_nBreakWidth, nMaxSizeDiff,
|
||||
rInf.GetCachedVclData().get() );
|
||||
rInf.GetTextSize(&rSI, rInf.GetIdx(), nPorLen, rPor.GetLayoutContext(), nMaxComp,
|
||||
m_nBreakWidth, nMaxSizeDiff, rInf.GetCachedVclData().get());
|
||||
|
||||
// save maximum width for later use
|
||||
if ( nMaxSizeDiff )
|
||||
|
@ -747,8 +749,9 @@ bool SwTextGuess::Guess( const SwTextPortion& rPor, SwTextFormatInfo &rInf,
|
|||
if (m_nBreakStart > rInf.GetIdx() + nPorLen + m_nFieldDiff)
|
||||
{
|
||||
rInf.GetTextSize(&rSI, rInf.GetIdx() + nPorLen,
|
||||
m_nBreakStart - rInf.GetIdx() - nPorLen - m_nFieldDiff, nMaxComp,
|
||||
m_nExtraBlankWidth, nMaxSizeDiff, rInf.GetCachedVclData().get());
|
||||
m_nBreakStart - rInf.GetIdx() - nPorLen - m_nFieldDiff,
|
||||
rPor.GetLayoutContext(), nMaxComp, m_nExtraBlankWidth, nMaxSizeDiff,
|
||||
rInf.GetCachedVclData().get());
|
||||
}
|
||||
|
||||
if( m_pHanging )
|
||||
|
|
|
@ -402,7 +402,8 @@ SwPosSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
|
|||
const TextFrameIndex nIndex,
|
||||
const TextFrameIndex nLength) const
|
||||
{
|
||||
SwDrawTextInfo aDrawInf( m_pVsh, *pOutDev, pSI, rText, nIndex, nLength );
|
||||
SwDrawTextInfo aDrawInf(m_pVsh, *pOutDev, pSI, rText, nIndex, nLength,
|
||||
/*layout context*/ std::nullopt);
|
||||
aDrawInf.SetFrame( m_pFrame );
|
||||
aDrawInf.SetFont( m_pFnt );
|
||||
aDrawInf.SetSnapToGrid( SnapToGrid() );
|
||||
|
@ -410,7 +411,8 @@ SwPosSize SwTextSizeInfo::GetTextSize( OutputDevice* pOutDev,
|
|||
return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
|
||||
}
|
||||
|
||||
SwPosSize SwTextSizeInfo::GetTextSize() const
|
||||
SwPosSize
|
||||
SwTextSizeInfo::GetTextSize(std::optional<SwLinePortionLayoutContext> nLayoutContext) const
|
||||
{
|
||||
const SwScriptInfo& rSI =
|
||||
const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
|
||||
|
@ -423,7 +425,7 @@ SwPosSize SwTextSizeInfo::GetTextSize() const
|
|||
GetKanaComp() :
|
||||
0 ;
|
||||
|
||||
SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen );
|
||||
SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, &rSI, *m_pText, m_nIdx, m_nLen, nLayoutContext);
|
||||
aDrawInf.SetMeasureLen( m_nMeasureLen );
|
||||
aDrawInf.SetFrame( m_pFrame );
|
||||
aDrawInf.SetFont( m_pFnt );
|
||||
|
@ -432,13 +434,15 @@ SwPosSize SwTextSizeInfo::GetTextSize() const
|
|||
return SwPosSize(m_pFnt->GetTextSize_( aDrawInf ));
|
||||
}
|
||||
|
||||
void SwTextSizeInfo::GetTextSize( const SwScriptInfo* pSI, const TextFrameIndex nIndex,
|
||||
const TextFrameIndex nLength, const sal_uInt16 nComp,
|
||||
sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
|
||||
vcl::text::TextLayoutCache const*const pCache) const
|
||||
void SwTextSizeInfo::GetTextSize(const SwScriptInfo* pSI, const TextFrameIndex nIndex,
|
||||
const TextFrameIndex nLength,
|
||||
std::optional<SwLinePortionLayoutContext> nLayoutContext,
|
||||
const sal_uInt16 nComp, sal_uInt16& nMinSize,
|
||||
sal_uInt16& nMaxSizeDiff,
|
||||
vcl::text::TextLayoutCache const* const pCache) const
|
||||
{
|
||||
SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength,
|
||||
0, false, pCache);
|
||||
SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, pSI, *m_pText, nIndex, nLength, nLayoutContext, 0,
|
||||
false, pCache);
|
||||
aDrawInf.SetFrame( m_pFrame );
|
||||
aDrawInf.SetFont( m_pFnt );
|
||||
aDrawInf.SetSnapToGrid( SnapToGrid() );
|
||||
|
@ -457,8 +461,8 @@ TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
|
|||
const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
|
||||
|
||||
OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
|
||||
SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
|
||||
*m_pText, GetIdx(), nMaxLen, 0, false, pCache );
|
||||
SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, &rScriptInfo, *m_pText, GetIdx(), nMaxLen,
|
||||
/*layout context*/ std::nullopt, 0, false, pCache);
|
||||
aDrawInf.SetFrame( m_pFrame );
|
||||
aDrawInf.SetFont( m_pFnt );
|
||||
aDrawInf.SetSnapToGrid( SnapToGrid() );
|
||||
|
@ -478,8 +482,8 @@ TextFrameIndex SwTextSizeInfo::GetTextBreak( const tools::Long nLineWidth,
|
|||
const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
|
||||
|
||||
OSL_ENSURE( m_pRef == m_pOut, "GetTextBreak is supposed to use the RefDev" );
|
||||
SwDrawTextInfo aDrawInf( m_pVsh, *m_pOut, &rScriptInfo,
|
||||
*m_pText, GetIdx(), nMaxLen, 0, false, pCache );
|
||||
SwDrawTextInfo aDrawInf(m_pVsh, *m_pOut, &rScriptInfo, *m_pText, GetIdx(), nMaxLen,
|
||||
/*layout context*/ std::nullopt, 0, false, pCache);
|
||||
aDrawInf.SetFrame( m_pFrame );
|
||||
aDrawInf.SetFont( m_pFnt );
|
||||
aDrawInf.SetSnapToGrid( SnapToGrid() );
|
||||
|
@ -632,8 +636,8 @@ void SwTextPaintInfo::DrawText_( const OUString &rText, const SwLinePortion &rPo
|
|||
const bool bTmpSmart = bSmartTag && OnWin() && !GetOpt().IsPagePreview() && SwSmartTagMgr::Get().IsSmartTagsEnabled();
|
||||
|
||||
OSL_ENSURE( GetParaPortion(), "No paragraph!");
|
||||
SwDrawTextInfo aDrawInf( m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart, nLength,
|
||||
rPor.Width(), bBullet );
|
||||
SwDrawTextInfo aDrawInf(m_pFrame->getRootFrame()->GetCurrShell(), *m_pOut, pSI, rText, nStart,
|
||||
nLength, rPor.GetLayoutContext(), rPor.Width(), bBullet);
|
||||
|
||||
aDrawInf.SetUnderFnt( m_pUnderFnt );
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ protected:
|
|||
TextFrameIndex m_nIdx;
|
||||
TextFrameIndex m_nLen;
|
||||
TextFrameIndex m_nMeasureLen;
|
||||
std::optional<SwLinePortionLayoutContext> m_nLayoutContext;
|
||||
sal_uInt16 m_nKanaIdx;
|
||||
bool m_bOnWin : 1;
|
||||
bool m_bNotEOL : 1;
|
||||
|
@ -248,11 +249,12 @@ public:
|
|||
SwPosSize GetTextSize( OutputDevice* pOut, const SwScriptInfo* pSI,
|
||||
const OUString& rText, TextFrameIndex nIdx,
|
||||
TextFrameIndex nLen ) const;
|
||||
SwPosSize GetTextSize() const;
|
||||
void GetTextSize( const SwScriptInfo* pSI, TextFrameIndex nIdx,
|
||||
TextFrameIndex nLen, const sal_uInt16 nComp,
|
||||
sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
|
||||
vcl::text::TextLayoutCache const* = nullptr) const;
|
||||
SwPosSize GetTextSize(std::optional<SwLinePortionLayoutContext> nLayoutContext
|
||||
= std::nullopt) const;
|
||||
void GetTextSize(const SwScriptInfo* pSI, TextFrameIndex nIdx, TextFrameIndex nLen,
|
||||
std::optional<SwLinePortionLayoutContext> nLayoutContext,
|
||||
const sal_uInt16 nComp, sal_uInt16& nMinSize, sal_uInt16& nMaxSizeDiff,
|
||||
vcl::text::TextLayoutCache const* = nullptr) const;
|
||||
inline SwPosSize GetTextSize(const SwScriptInfo* pSI, TextFrameIndex nIdx,
|
||||
TextFrameIndex nLen) const;
|
||||
inline SwPosSize GetTextSize( const OUString &rText ) const;
|
||||
|
@ -278,6 +280,13 @@ public:
|
|||
void SetMeasureLen(const TextFrameIndex nNew) { m_nMeasureLen = nNew; }
|
||||
void SetText( const OUString &rNew ){ m_pText = &rNew; }
|
||||
|
||||
std::optional<SwLinePortionLayoutContext> GetLayoutContext() const { return m_nLayoutContext; }
|
||||
|
||||
void SetLayoutContext(std::optional<SwLinePortionLayoutContext> nNew)
|
||||
{
|
||||
m_nLayoutContext = nNew;
|
||||
}
|
||||
|
||||
// No Bullets for the symbol font!
|
||||
bool IsNoSymbol() const
|
||||
{ return RTL_TEXTENCODING_SYMBOL != m_pFnt->GetCharSet( m_pFnt->GetActual() ); }
|
||||
|
|
|
@ -1706,12 +1706,9 @@ TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, con
|
|||
SAL_WARN_IF( aSizeInf.GetIdx().get() + pPor->GetLen().get() > aSizeInf.GetText().getLength(), "sw", "portion and text are out of sync" );
|
||||
TextFrameIndex nSafeLen( std::min(pPor->GetLen().get(), aSizeInf.GetText().getLength() - aSizeInf.GetIdx().get()) );
|
||||
|
||||
SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
|
||||
*aSizeInf.GetOut(),
|
||||
&pPara->GetScriptInfo(),
|
||||
aSizeInf.GetText(),
|
||||
aSizeInf.GetIdx(),
|
||||
nSafeLen );
|
||||
SwDrawTextInfo aDrawInf(aSizeInf.GetVsh(), *aSizeInf.GetOut(),
|
||||
&pPara->GetScriptInfo(), aSizeInf.GetText(),
|
||||
aSizeInf.GetIdx(), nSafeLen, aSizeInf.GetLayoutContext());
|
||||
|
||||
// Drop portion works like a multi portion, just its parts are not portions
|
||||
if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
|
||||
|
|
|
@ -1354,6 +1354,7 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
|
|||
// bookmarks
|
||||
const TextFrameIndex nNextBookmark = m_pScriptInfo->NextBookmark(rInf.GetIdx());
|
||||
|
||||
auto nNextContext = std::min({ nNextChg, nNextScript, nNextDir });
|
||||
nNextChg = std::min({ nNextChg, nNextAttr, nNextScript, nNextDir, nNextHidden, nNextBookmark });
|
||||
|
||||
// Turbo boost:
|
||||
|
@ -1398,6 +1399,53 @@ SwTextPortion *SwTextFormatter::NewTextPortion( SwTextFormatInfo &rInf )
|
|||
|
||||
pPor->SetLen( nNextChg - rInf.GetIdx() );
|
||||
rInf.SetLen( pPor->GetLen() );
|
||||
|
||||
// Generate a new layout context for the text portion. This is necessary
|
||||
// for the first text portion in a paragraph, or for any successive
|
||||
// portions that are outside of the bounds of the previous context.
|
||||
if (!rInf.GetLayoutContext().has_value()
|
||||
|| rInf.GetLayoutContext()->m_nEnd <= rInf.GetIdx().get())
|
||||
{
|
||||
// The layout context must terminate at special characters
|
||||
sal_Int32 nEnd = rInf.GetIdx().get();
|
||||
for (; nEnd < nNextContext.get(); ++nEnd)
|
||||
{
|
||||
bool bAtEnd = false;
|
||||
switch (rInf.GetText()[nEnd])
|
||||
{
|
||||
case CH_TXTATR_BREAKWORD:
|
||||
case CH_TXTATR_INWORD:
|
||||
case CH_TXTATR_TAB:
|
||||
case CH_TXT_ATR_INPUTFIELDSTART:
|
||||
case CH_TXT_ATR_INPUTFIELDEND:
|
||||
case CH_TXT_ATR_FORMELEMENT:
|
||||
case CH_TXT_ATR_FIELDSTART:
|
||||
case CH_TXT_ATR_FIELDSEP:
|
||||
case CH_TXT_ATR_FIELDEND:
|
||||
bAtEnd = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (bAtEnd)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<SwLinePortionLayoutContext> nNewContext;
|
||||
if (rInf.GetIdx().get() != nEnd)
|
||||
{
|
||||
nNewContext = SwLinePortionLayoutContext{ rInf.GetIdx().get(), nEnd };
|
||||
}
|
||||
|
||||
rInf.SetLayoutContext(nNewContext);
|
||||
}
|
||||
|
||||
pPor->SetLayoutContext(rInf.GetLayoutContext());
|
||||
|
||||
return pPor;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <txttypes.hxx>
|
||||
#include <TextFrameIndex.hxx>
|
||||
#include <rtl/ustring.hxx>
|
||||
#include <swporlayoutcontext.hxx>
|
||||
|
||||
class SwTextSizeInfo;
|
||||
class SwTextPaintInfo;
|
||||
|
@ -48,6 +49,7 @@ private:
|
|||
bool m_bJoinBorderWithPrev;
|
||||
bool m_bJoinBorderWithNext;
|
||||
SwTwips m_nExtraBlankWidth = 0; // width of spaces after the break
|
||||
std::optional<SwLinePortionLayoutContext> m_nLayoutContext;
|
||||
|
||||
void Truncate_();
|
||||
|
||||
|
@ -72,6 +74,11 @@ public:
|
|||
void ExtraBlankWidth(const SwTwips nNew) { m_nExtraBlankWidth = nNew; }
|
||||
SwTwips GetHangingBaseline() const { return mnHangingBaseline; }
|
||||
void SetHangingBaseline( const SwTwips nNewBaseline ) { mnHangingBaseline = nNewBaseline; }
|
||||
std::optional<SwLinePortionLayoutContext> GetLayoutContext() const { return m_nLayoutContext; }
|
||||
void SetLayoutContext(std::optional<SwLinePortionLayoutContext> nNew)
|
||||
{
|
||||
m_nLayoutContext = nNew;
|
||||
}
|
||||
|
||||
// Insert methods
|
||||
virtual SwLinePortion *Insert( SwLinePortion *pPortion );
|
||||
|
@ -179,6 +186,7 @@ inline SwLinePortion &SwLinePortion::operator=(const SwLinePortion &rPortion)
|
|||
m_bJoinBorderWithPrev = rPortion.m_bJoinBorderWithPrev;
|
||||
m_bJoinBorderWithNext = rPortion.m_bJoinBorderWithNext;
|
||||
m_nExtraBlankWidth = rPortion.m_nExtraBlankWidth;
|
||||
m_nLayoutContext = rPortion.m_nLayoutContext;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -191,7 +199,8 @@ inline SwLinePortion::SwLinePortion(const SwLinePortion &rPortion) :
|
|||
mnWhichPor( rPortion.mnWhichPor ),
|
||||
m_bJoinBorderWithPrev( rPortion.m_bJoinBorderWithPrev ),
|
||||
m_bJoinBorderWithNext( rPortion.m_bJoinBorderWithNext ),
|
||||
m_nExtraBlankWidth(rPortion.m_nExtraBlankWidth)
|
||||
m_nExtraBlankWidth(rPortion.m_nExtraBlankWidth),
|
||||
m_nLayoutContext(rPortion.m_nLayoutContext)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -547,7 +547,7 @@ TextFrameIndex SwTextPortion::GetModelPositionForViewPoint(const sal_uInt16 nOfs
|
|||
// The GetTextSize() assumes that the own length is correct
|
||||
SwPosSize SwTextPortion::GetTextSize( const SwTextSizeInfo &rInf ) const
|
||||
{
|
||||
SwPosSize aSize = rInf.GetTextSize();
|
||||
SwPosSize aSize = rInf.GetTextSize(GetLayoutContext());
|
||||
if( !GetJoinBorderWithPrev() )
|
||||
aSize.Width(aSize.Width() + rInf.GetFont()->GetLeftBorderSpace() );
|
||||
if( !GetJoinBorderWithNext() )
|
||||
|
|
|
@ -743,27 +743,66 @@ static void lcl_DrawLineForWrongListData(
|
|||
}
|
||||
|
||||
static void GetTextArray(const OutputDevice& rDevice, const OUString& rStr, KernArray& rDXAry,
|
||||
sal_Int32 nIndex, sal_Int32 nLen, bool bCaret = false,
|
||||
sal_Int32 nIndex, sal_Int32 nLen,
|
||||
std::optional<SwLinePortionLayoutContext> nLayoutContext,
|
||||
bool bCaret = false,
|
||||
const vcl::text::TextLayoutCache* layoutCache = nullptr)
|
||||
{
|
||||
const SalLayoutGlyphs* pLayoutCache = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rDevice, rStr, nIndex, nLen,
|
||||
0, layoutCache);
|
||||
rDevice.GetTextArray(rStr, &rDXAry, nIndex, nLen, bCaret, layoutCache, pLayoutCache);
|
||||
if (nLayoutContext.has_value())
|
||||
{
|
||||
auto nStrEnd = nIndex + nLen;
|
||||
auto nContextBegin = std::clamp(nLayoutContext->m_nBegin, sal_Int32{0}, nIndex);
|
||||
auto nContextEnd = std::clamp(nLayoutContext->m_nEnd, nStrEnd, rStr.getLength());
|
||||
auto nContextLen = nContextEnd - nContextBegin;
|
||||
|
||||
const SalLayoutGlyphs* pLayoutCache = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
|
||||
&rDevice, rStr, nContextBegin, nContextLen, 0, layoutCache);
|
||||
rDevice.GetPartialTextArray(rStr, &rDXAry, nContextBegin, nContextLen, nIndex, nLen, bCaret,
|
||||
layoutCache, pLayoutCache);
|
||||
}
|
||||
else
|
||||
{
|
||||
const SalLayoutGlyphs* pLayoutCache = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(
|
||||
&rDevice, rStr, nIndex, nLen, 0, layoutCache);
|
||||
rDevice.GetTextArray(rStr, &rDXAry, nIndex, nLen, bCaret, layoutCache, pLayoutCache);
|
||||
}
|
||||
}
|
||||
|
||||
static void GetTextArray(const OutputDevice& rOutputDevice, const SwDrawTextInfo& rInf, KernArray& rDXAry,
|
||||
bool bCaret = false)
|
||||
static void GetTextArray(const OutputDevice& rOutputDevice, const SwDrawTextInfo& rInf,
|
||||
KernArray& rDXAry, bool bCaret = false)
|
||||
{
|
||||
return GetTextArray(rOutputDevice, rInf.GetText(), rDXAry, rInf.GetIdx().get(), rInf.GetLen().get(),
|
||||
bCaret, rInf.GetVclCache());
|
||||
GetTextArray(rOutputDevice, rInf.GetText(), rDXAry, rInf.GetIdx().get(), rInf.GetLen().get(),
|
||||
rInf.GetLayoutContext(), bCaret, rInf.GetVclCache());
|
||||
}
|
||||
|
||||
static void GetTextArray(const OutputDevice& rOutputDevice, const SwDrawTextInfo& rInf, KernArray& rDXAry,
|
||||
sal_Int32 nLen, bool bCaret = false)
|
||||
static void GetTextArray(const OutputDevice& rOutputDevice, const SwDrawTextInfo& rInf,
|
||||
KernArray& rDXAry, sal_Int32 nLen, bool bCaret = false)
|
||||
{
|
||||
// Substring is fine.
|
||||
assert( nLen <= rInf.GetLen().get());
|
||||
return GetTextArray(rOutputDevice, rInf.GetText(), rDXAry, rInf.GetIdx().get(), nLen, bCaret, rInf.GetVclCache());
|
||||
assert(nLen <= rInf.GetLen().get());
|
||||
GetTextArray(rOutputDevice, rInf.GetText(), rDXAry, rInf.GetIdx().get(), nLen,
|
||||
rInf.GetLayoutContext(), bCaret, rInf.GetVclCache());
|
||||
}
|
||||
|
||||
static void DrawTextArray(OutputDevice& rOutputDevice, const Point& rStartPt, const OUString& rStr,
|
||||
KernArray& aKernArray, std::span<const sal_Bool> pKashidaAry,
|
||||
sal_Int32 nIndex, sal_Int32 nLen,
|
||||
std::optional<SwLinePortionLayoutContext> nLayoutContext)
|
||||
{
|
||||
if (nLayoutContext.has_value())
|
||||
{
|
||||
auto nStrEnd = nIndex + nLen;
|
||||
auto nContextBegin = std::clamp(nLayoutContext->m_nBegin, sal_Int32{0}, nIndex);
|
||||
auto nContextEnd = std::clamp(nLayoutContext->m_nEnd, nStrEnd, rStr.getLength());
|
||||
auto nContextLen = nContextEnd - nContextBegin;
|
||||
|
||||
rOutputDevice.DrawPartialTextArray(rStartPt, rStr, aKernArray, pKashidaAry, nContextBegin,
|
||||
nContextLen, nIndex, nLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
rOutputDevice.DrawTextArray(rStartPt, rStr, aKernArray, pKashidaAry, nIndex, nLen);
|
||||
}
|
||||
}
|
||||
|
||||
void SwFntObj::DrawText( SwDrawTextInfo &rInf )
|
||||
|
@ -965,8 +1004,9 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
|
|||
if ( bSwitchH2V )
|
||||
rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
|
||||
|
||||
rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
|
||||
aKernArray, {}, sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
|
||||
DrawTextArray(rInf.GetOut(), aTextOriginPos, rInf.GetText(), aKernArray, {},
|
||||
sal_Int32{ rInf.GetIdx() }, sal_Int32{ rInf.GetLen() },
|
||||
rInf.GetLayoutContext());
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -1121,7 +1161,6 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
|
|||
if (TextFrameIndex(1) == rInf.GetLen())
|
||||
{
|
||||
aKernArray.set(0, rInf.GetWidth() + nSpaceAdd);
|
||||
|
||||
rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
|
||||
aKernArray, aKashidaArray, sal_Int32(rInf.GetIdx()), 1 );
|
||||
}
|
||||
|
@ -1129,13 +1168,16 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
|
|||
{
|
||||
sal_Int32 nIndex(sal_Int32(rInf.GetLen()) - 2);
|
||||
aKernArray.adjust(nIndex, nSpaceAdd);
|
||||
rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
|
||||
aKernArray, aKashidaArray, sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
|
||||
DrawTextArray(rInf.GetOut(), aTextOriginPos, rInf.GetText(), aKernArray,
|
||||
aKashidaArray, sal_Int32{ rInf.GetIdx() },
|
||||
sal_Int32{ rInf.GetLen() }, rInf.GetLayoutContext());
|
||||
}
|
||||
}
|
||||
else
|
||||
rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(),
|
||||
aKernArray, aKashidaArray, sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
|
||||
else {
|
||||
DrawTextArray(rInf.GetOut(), aTextOriginPos, rInf.GetText(), aKernArray,
|
||||
aKashidaArray, sal_Int32{ rInf.GetIdx() },
|
||||
sal_Int32{ rInf.GetLen() }, rInf.GetLayoutContext());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1485,10 +1527,8 @@ void SwFntObj::DrawText( SwDrawTextInfo &rInf )
|
|||
rInf.GetFrame()->SwitchHorizontalToVertical( aTextOriginPos );
|
||||
|
||||
sal_Int32 nIdx = sal_Int32(rInf.GetIdx());
|
||||
const SalLayoutGlyphs* pGlyphs = SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&rInf.GetOut(),
|
||||
rInf.GetText(), nIdx, nLen);
|
||||
rInf.GetOut().DrawTextArray( aTextOriginPos, rInf.GetText(), aKernArray, aKashidaArray,
|
||||
nIdx, nLen, SalLayoutFlags::NONE, pGlyphs );
|
||||
DrawTextArray(rInf.GetOut(), aTextOriginPos, rInf.GetText(), aKernArray,
|
||||
aKashidaArray, nIdx, nLen, rInf.GetLayoutContext());
|
||||
if (bBullet)
|
||||
{
|
||||
rInf.GetOut().Push();
|
||||
|
@ -1633,8 +1673,8 @@ Size SwFntObj::GetTextSize( SwDrawTextInfo& rInf )
|
|||
if( !GetScrFont()->IsSameInstance( rInf.GetOut().GetFont() ) )
|
||||
rInf.GetOut().SetFont( *m_pScrFont );
|
||||
|
||||
GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray,
|
||||
sal_Int32(rInf.GetIdx()), sal_Int32(nLn), bCaret);
|
||||
GetTextArray(*m_pPrinter, rInf.GetText(), aKernArray, sal_Int32(rInf.GetIdx()),
|
||||
sal_Int32(nLn), rInf.GetLayoutContext(), bCaret);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1989,8 +2029,8 @@ TextFrameIndex SwFont::GetTextBreak(SwDrawTextInfo const & rInf, tools::Long nTe
|
|||
const sal_uInt16 nGridWidth = GetGridWidth(*pGrid, *pDoc);
|
||||
|
||||
KernArray aKernArray;
|
||||
GetTextArray( rInf.GetOut(), rInf.GetText(), aKernArray,
|
||||
sal_Int32(rInf.GetIdx()), sal_Int32(rInf.GetLen()));
|
||||
GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, sal_Int32(rInf.GetIdx()),
|
||||
sal_Int32(rInf.GetLen()), rInf.GetLayoutContext());
|
||||
|
||||
if (pGrid->IsSnapToChars())
|
||||
{
|
||||
|
@ -2117,8 +2157,8 @@ TextFrameIndex SwFont::GetTextBreak(SwDrawTextInfo const & rInf, tools::Long nTe
|
|||
else if (nLn > nTextBreak2 + nTextBreak2)
|
||||
nLn = nTextBreak2 + nTextBreak2;
|
||||
KernArray aKernArray;
|
||||
GetTextArray( rInf.GetOut(), rInf.GetText(), aKernArray,
|
||||
sal_Int32(rInf.GetIdx()), sal_Int32(nLn));
|
||||
GetTextArray(rInf.GetOut(), rInf.GetText(), aKernArray, sal_Int32(rInf.GetIdx()),
|
||||
sal_Int32(nLn), rInf.GetLayoutContext());
|
||||
if( rInf.GetScriptInfo()->Compress( aKernArray, rInf.GetIdx(), nLn,
|
||||
rInf.GetKanaComp(), o3tl::narrowing<sal_uInt16>(GetHeight( m_nActual )),
|
||||
lcl_IsFullstopCentered( rInf.GetOut() ) ) )
|
||||
|
|
|
@ -225,7 +225,7 @@ TextFrameIndex SwFont::GetCapitalBreak( SwViewShell const * pSh, const OutputDev
|
|||
// Start:
|
||||
Point aPos( 0, 0 );
|
||||
SwDrawTextInfo aInfo(pSh, *const_cast<OutputDevice*>(pOut), pScript, rText, nIdx, nLen,
|
||||
0, false);
|
||||
/*layout context*/ std::nullopt, 0, false);
|
||||
aInfo.SetPos( aPos );
|
||||
aInfo.SetSpace( 0 );
|
||||
aInfo.SetWrong( nullptr );
|
||||
|
@ -375,11 +375,9 @@ void SwDoCapitalCursorOfst::Do()
|
|||
}
|
||||
else
|
||||
{
|
||||
SwDrawTextInfo aDrawInf( m_rInf.GetShell(), *m_rInf.GetpOut(),
|
||||
m_rInf.GetScriptInfo(),
|
||||
m_rInf.GetText(),
|
||||
m_rInf.GetIdx(),
|
||||
m_rInf.GetLen(), 0, false );
|
||||
SwDrawTextInfo aDrawInf(m_rInf.GetShell(), *m_rInf.GetpOut(), m_rInf.GetScriptInfo(),
|
||||
m_rInf.GetText(), m_rInf.GetIdx(), m_rInf.GetLen(),
|
||||
/*layout context*/ std::nullopt, 0, false);
|
||||
aDrawInf.SetOffset(m_nOfst);
|
||||
aDrawInf.SetKern( m_rInf.GetKern() );
|
||||
aDrawInf.SetKanaComp( m_rInf.GetKanaComp() );
|
||||
|
|
|
@ -59,7 +59,10 @@ public:
|
|||
SAL_DLLPRIVATE void DrawText(SalGraphics&) const override;
|
||||
SAL_DLLPRIVATE sal_Int32 GetTextBreak(double nMaxWidth, double nCharExtra, int nFactor) const override;
|
||||
SAL_DLLPRIVATE double GetTextWidth() const final override;
|
||||
SAL_DLLPRIVATE double GetPartialTextWidth(sal_Int32 skipStart, sal_Int32 amt) const override;
|
||||
SAL_DLLPRIVATE double FillDXArray(std::vector<double>* pDXArray, const OUString& rStr) const override;
|
||||
SAL_DLLPRIVATE double FillPartialDXArray(std::vector<double>* pDXArray, const OUString& rStr,
|
||||
sal_Int32 skipStart, sal_Int32 amt) const override;
|
||||
SAL_DLLPRIVATE void GetCaretPositions(std::vector<double>& rCaretPositions, const OUString& rStr) const override;
|
||||
SAL_DLLPRIVATE bool GetNextGlyph(const GlyphItem** pGlyph, basegfx::B2DPoint& rPos, int& nStart,
|
||||
const LogicalFontInstance** ppGlyphFont = nullptr) const override;
|
||||
|
@ -116,7 +119,10 @@ public:
|
|||
|
||||
// used by upper layers
|
||||
double GetTextWidth() const final override;
|
||||
double GetPartialTextWidth(sal_Int32 skipStart, sal_Int32 amt) const final override;
|
||||
double FillDXArray(std::vector<double>* pDXArray, const OUString& rStr) const final override;
|
||||
double FillPartialDXArray(std::vector<double>* pDXArray, const OUString& rStr,
|
||||
sal_Int32 skipStart, sal_Int32 amt) const final override;
|
||||
sal_Int32 GetTextBreak(double nMaxWidth, double nCharExtra, int nFactor) const final override;
|
||||
void GetCaretPositions(std::vector<double>& rCaretPositions, const OUString& rStr) const override;
|
||||
|
||||
|
|
|
@ -611,4 +611,70 @@ CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testTdf107612)
|
|||
#endif
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testPartialKoreanJamoComposition)
|
||||
{
|
||||
OUString aStr = u"은"_ustr;
|
||||
vcl::Font aFont("DejaVu Sans", "Book", Size(0, 2048));
|
||||
|
||||
ScopedVclPtrInstance<VirtualDevice> pOutDev;
|
||||
pOutDev->SetFont(aFont);
|
||||
|
||||
// Absolute character widths for the complete array.
|
||||
KernArray aCompleteWidths;
|
||||
auto nCompleteWidth = pOutDev->GetTextArray(aStr, &aCompleteWidths);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(size_t{ 3 }, aCompleteWidths.size());
|
||||
|
||||
// Accumulate partial widths
|
||||
double nPartialWidth = 0.0;
|
||||
|
||||
sal_Int32 nPrevWidth = 0;
|
||||
for (sal_Int32 i = 0; i < 3; ++i)
|
||||
{
|
||||
KernArray aFragmentWidths;
|
||||
auto nFragmentWidth = pOutDev->GetPartialTextArray(
|
||||
aStr, &aFragmentWidths, /*nIndex*/ 0, /*nLen*/ 3, /*nPartIndex*/ i, /*nPartLen*/ 1);
|
||||
nPartialWidth += nFragmentWidth;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(size_t{ 1 }, aFragmentWidths.size());
|
||||
CPPUNIT_ASSERT_EQUAL(aCompleteWidths[i] - nPrevWidth, aFragmentWidths[0]);
|
||||
nPrevWidth = aCompleteWidths[i];
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(nCompleteWidth, nPartialWidth, /*delta*/ 0.01);
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(VclComplexTextTest, testPartialArabicComposition)
|
||||
{
|
||||
OUString aStr = u"سُكُونْ"_ustr;
|
||||
vcl::Font aFont("DejaVu Sans", "Book", Size(0, 2048));
|
||||
|
||||
ScopedVclPtrInstance<VirtualDevice> pOutDev;
|
||||
pOutDev->SetFont(aFont);
|
||||
|
||||
// Absolute character widths for the complete array.
|
||||
KernArray aCompleteWidths;
|
||||
auto nCompleteWidth = pOutDev->GetTextArray(aStr, &aCompleteWidths);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(size_t{ 7 }, aCompleteWidths.size());
|
||||
|
||||
// Accumulate partial widths
|
||||
double nPartialWidth = 0.0;
|
||||
|
||||
sal_Int32 nPrevWidth = 0;
|
||||
for (sal_Int32 i = 0; i < 7; ++i)
|
||||
{
|
||||
KernArray aFragmentWidths;
|
||||
auto nFragmentWidth = pOutDev->GetPartialTextArray(
|
||||
aStr, &aFragmentWidths, /*nIndex*/ 0, /*nLen*/ 7, /*nPartIndex*/ i, /*nPartLen*/ 1);
|
||||
nPartialWidth += nFragmentWidth;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(size_t{ 1 }, aFragmentWidths.size());
|
||||
CPPUNIT_ASSERT_EQUAL(aCompleteWidths[i] - nPrevWidth, aFragmentWidths[0]);
|
||||
nPrevWidth = aCompleteWidths[i];
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(nCompleteWidth, nPartialWidth, /*delta*/ 0.01);
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
|
BIN
vcl/qa/cppunit/pdfexport/data/tdf61444.odt
Normal file
BIN
vcl/qa/cppunit/pdfexport/data/tdf61444.odt
Normal file
Binary file not shown.
|
@ -4999,6 +4999,55 @@ CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf159817)
|
|||
CPPUNIT_ASSERT_EQUAL(basegfx::B2DPoint(138.6, 623.7), roundPoint(37));
|
||||
}
|
||||
|
||||
// Tests that kerning is correctly applied across color changes
|
||||
CPPUNIT_TEST_FIXTURE(PdfExportTest2, testTdf61444)
|
||||
{
|
||||
aMediaDescriptor["FilterName"] <<= OUString("writer_pdf_Export");
|
||||
saveAsPDF(u"tdf61444.odt");
|
||||
std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument = parsePDFExport();
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(1, pPdfDocument->getPageCount());
|
||||
|
||||
// Get the first page
|
||||
std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(/*nIndex*/ 0);
|
||||
CPPUNIT_ASSERT(pPdfPage);
|
||||
std::unique_ptr<vcl::pdf::PDFiumTextPage> pTextPage = pPdfPage->getTextPage();
|
||||
CPPUNIT_ASSERT(pTextPage);
|
||||
|
||||
// 4 text objects should be present
|
||||
int nPageObjectCount = pPdfPage->getObjectCount();
|
||||
CPPUNIT_ASSERT_EQUAL(4, nPageObjectCount);
|
||||
|
||||
OUString sText[4];
|
||||
basegfx::B2DRectangle aRect[4];
|
||||
|
||||
int nTextObjectCount = 0;
|
||||
for (int i = 0; i < nPageObjectCount; ++i)
|
||||
{
|
||||
auto pPageObject = pPdfPage->getObject(i);
|
||||
CPPUNIT_ASSERT_MESSAGE("no object", pPageObject != nullptr);
|
||||
if (pPageObject->getType() == vcl::pdf::PDFPageObjectType::Text)
|
||||
{
|
||||
sText[nTextObjectCount] = pPageObject->getText(pTextPage);
|
||||
aRect[nTextObjectCount] = pPageObject->getBounds();
|
||||
++nTextObjectCount;
|
||||
}
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(4, nTextObjectCount);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(u"Wait"_ustr, sText[0].trim());
|
||||
CPPUNIT_ASSERT_EQUAL(u"W"_ustr, sText[1].trim());
|
||||
CPPUNIT_ASSERT_EQUAL(u"ai"_ustr, sText[2].trim());
|
||||
CPPUNIT_ASSERT_EQUAL(u"t"_ustr, sText[3].trim());
|
||||
|
||||
// Both lines should have the same kerning, so should end at approximately the same X coordinate
|
||||
auto solid_extent = aRect[0].getMaxX();
|
||||
auto color_extent = aRect[3].getMaxX();
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(solid_extent, color_extent, /*delta*/ 0.15);
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
CPPUNIT_PLUGIN_IMPLEMENT();
|
||||
|
|
|
@ -846,6 +846,39 @@ CPPUNIT_TEST_FIXTURE(VclTextTest, testGetRightBottomAlignedMultiLineTextRect)
|
|||
| DrawTextFlags::MultiLine));
|
||||
}
|
||||
|
||||
CPPUNIT_TEST_FIXTURE(VclTextTest, testPartialTextArraySizeMatch)
|
||||
{
|
||||
OUString aWater = u"Water"_ustr;
|
||||
vcl::Font aFont("DejaVu Sans", "Book", Size(0, 2048));
|
||||
|
||||
ScopedVclPtrInstance<VirtualDevice> pOutDev;
|
||||
pOutDev->SetFont(aFont);
|
||||
|
||||
// Absolute character widths for the complete array.
|
||||
KernArray aCompleteWidths;
|
||||
auto nCompleteWidth = pOutDev->GetTextArray(aWater, &aCompleteWidths);
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(size_t{ 5 }, aCompleteWidths.size());
|
||||
|
||||
// Accumulate partial widths
|
||||
double nPartialWidth = 0.0;
|
||||
|
||||
sal_Int32 nPrevWidth = 0;
|
||||
for (sal_Int32 i = 0; i < 5; ++i)
|
||||
{
|
||||
KernArray aFragmentWidths;
|
||||
auto nFragmentWidth = pOutDev->GetPartialTextArray(
|
||||
aWater, &aFragmentWidths, /*nIndex*/ 0, /*nLen*/ 5, /*nPartIndex*/ i, /*nPartLen*/ 1);
|
||||
nPartialWidth += nFragmentWidth;
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL(size_t{ 1 }, aFragmentWidths.size());
|
||||
CPPUNIT_ASSERT_EQUAL(aCompleteWidths[i] - nPrevWidth, aFragmentWidths[0]);
|
||||
nPrevWidth = aCompleteWidths[i];
|
||||
}
|
||||
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL(nCompleteWidth, nPartialWidth, /*delta*/ 0.01);
|
||||
}
|
||||
|
||||
CPPUNIT_PLUGIN_IMPLEMENT();
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
|
|
@ -274,6 +274,25 @@ double GenericSalLayout::FillDXArray( std::vector<double>* pCharWidths, const OU
|
|||
return GetTextWidth();
|
||||
}
|
||||
|
||||
double GenericSalLayout::FillPartialDXArray(std::vector<double>* pCharWidths, const OUString& rStr,
|
||||
sal_Int32 skipStart, sal_Int32 amt) const
|
||||
{
|
||||
if (pCharWidths)
|
||||
{
|
||||
GetCharWidths(*pCharWidths, rStr);
|
||||
|
||||
// Strip excess characters from the array
|
||||
if (skipStart < static_cast<sal_Int32>(pCharWidths->size()))
|
||||
{
|
||||
std::copy(pCharWidths->begin() + skipStart, pCharWidths->end(), pCharWidths->begin());
|
||||
}
|
||||
|
||||
pCharWidths->resize(amt, 0.0);
|
||||
}
|
||||
|
||||
return GetPartialTextWidth(skipStart, amt);
|
||||
}
|
||||
|
||||
// the text width is the maximum logical extent of all glyphs
|
||||
double GenericSalLayout::GetTextWidth() const
|
||||
{
|
||||
|
@ -287,6 +306,27 @@ double GenericSalLayout::GetTextWidth() const
|
|||
return nWidth;
|
||||
}
|
||||
|
||||
double GenericSalLayout::GetPartialTextWidth(sal_Int32 skipStart, sal_Int32 amt) const
|
||||
{
|
||||
if (!m_GlyphItems.IsValid())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto skipEnd = skipStart + amt;
|
||||
double nWidth = 0.0;
|
||||
for (auto const& aGlyphItem : m_GlyphItems)
|
||||
{
|
||||
auto pos = aGlyphItem.charPos();
|
||||
if (pos >= skipStart && pos < skipEnd)
|
||||
{
|
||||
nWidth += aGlyphItem.newWidth();
|
||||
}
|
||||
}
|
||||
|
||||
return nWidth;
|
||||
}
|
||||
|
||||
void GenericSalLayout::Justify(double nNewWidth)
|
||||
{
|
||||
double nOldWidth = GetTextWidth();
|
||||
|
@ -1056,6 +1096,29 @@ double MultiSalLayout::GetTextWidth() const
|
|||
return nWidth;
|
||||
}
|
||||
|
||||
double MultiSalLayout::GetPartialTextWidth(sal_Int32 skipStart, sal_Int32 amt) const
|
||||
{
|
||||
// Measure text width. There might be holes in each SalLayout due to
|
||||
// missing chars, so we use GetNextGlyph() to get the glyphs across all
|
||||
// layouts.
|
||||
int nStart = 0;
|
||||
basegfx::B2DPoint aPos;
|
||||
const GlyphItem* pGlyphItem;
|
||||
|
||||
auto skipEnd = skipStart + amt;
|
||||
double nWidth = 0;
|
||||
while (GetNextGlyph(&pGlyphItem, aPos, nStart))
|
||||
{
|
||||
auto cpos = pGlyphItem->charPos();
|
||||
if (cpos >= skipStart && cpos < skipEnd)
|
||||
{
|
||||
nWidth += pGlyphItem->newWidth();
|
||||
}
|
||||
}
|
||||
|
||||
return nWidth;
|
||||
}
|
||||
|
||||
double MultiSalLayout::FillDXArray( std::vector<double>* pCharWidths, const OUString& rStr ) const
|
||||
{
|
||||
if (pCharWidths)
|
||||
|
@ -1089,6 +1152,25 @@ double MultiSalLayout::FillDXArray( std::vector<double>* pCharWidths, const OUSt
|
|||
return GetTextWidth();
|
||||
}
|
||||
|
||||
double MultiSalLayout::FillPartialDXArray(std::vector<double>* pCharWidths, const OUString& rStr,
|
||||
sal_Int32 skipStart, sal_Int32 amt) const
|
||||
{
|
||||
if (pCharWidths)
|
||||
{
|
||||
FillDXArray(pCharWidths, rStr);
|
||||
|
||||
// Strip excess characters from the array
|
||||
if (skipStart < static_cast<sal_Int32>(pCharWidths->size()))
|
||||
{
|
||||
std::copy(pCharWidths->begin() + skipStart, pCharWidths->end(), pCharWidths->begin());
|
||||
}
|
||||
|
||||
pCharWidths->resize(amt);
|
||||
}
|
||||
|
||||
return GetPartialTextWidth(skipStart, amt);
|
||||
}
|
||||
|
||||
void MultiSalLayout::GetCaretPositions(std::vector<double>& rCaretPositions,
|
||||
const OUString& rStr) const
|
||||
{
|
||||
|
|
|
@ -689,6 +689,27 @@ float OutputDevice::approximate_digit_width() const
|
|||
return GetTextWidth(u"0123456789"_ustr) / 10.0;
|
||||
}
|
||||
|
||||
void OutputDevice::DrawPartialTextArray(const Point& rStartPt, const OUString& rStr,
|
||||
KernArraySpan pDXArray,
|
||||
std::span<const sal_Bool> pKashidaArray,
|
||||
sal_Int32 /*nIndex*/, sal_Int32 /*nLen*/,
|
||||
sal_Int32 nPartIndex, sal_Int32 nPartLen,
|
||||
SalLayoutFlags flags, const SalLayoutGlyphs* pLayoutCache)
|
||||
{
|
||||
// Currently, this is just a wrapper for DrawTextArray().
|
||||
//
|
||||
// In certain documents, DrawTextArray/DrawPartialTextArray can be called such that combining
|
||||
// characters straddle multiple draw calls. This can happen if, for example, a user attempts to
|
||||
// use different text colors for a character and its diacritical marks.
|
||||
//
|
||||
// In order to fix this issue, this implementation should be replaced with one that performs
|
||||
// correct glyph substitutions across text portion boundaries, using the extra layout context.
|
||||
//
|
||||
// See tdf#124116
|
||||
DrawTextArray(rStartPt, rStr, pDXArray, pKashidaArray, nPartIndex, nPartLen, flags,
|
||||
pLayoutCache);
|
||||
}
|
||||
|
||||
void OutputDevice::DrawTextArray( const Point& rStartPt, const OUString& rStr,
|
||||
KernArraySpan pDXAry,
|
||||
std::span<const sal_Bool> pKashidaAry,
|
||||
|
@ -728,6 +749,20 @@ double OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernArray,
|
|||
sal_Int32 nIndex, sal_Int32 nLen, bool bCaret,
|
||||
vcl::text::TextLayoutCache const*const pLayoutCache,
|
||||
SalLayoutGlyphs const*const pSalLayoutCache) const
|
||||
{
|
||||
return GetPartialTextArray(rStr, pKernArray, nIndex, nLen, nIndex, nLen, bCaret, pLayoutCache,
|
||||
pSalLayoutCache);
|
||||
}
|
||||
|
||||
double OutputDevice::GetPartialTextArray(const OUString &rStr,
|
||||
KernArray* pKernArray,
|
||||
sal_Int32 nIndex,
|
||||
sal_Int32 nLen,
|
||||
sal_Int32 nPartIndex,
|
||||
sal_Int32 nPartLen,
|
||||
bool bCaret,
|
||||
const vcl::text::TextLayoutCache* pLayoutCache,
|
||||
const SalLayoutGlyphs* pSalLayoutCache) const
|
||||
{
|
||||
if( nIndex >= rStr.getLength() )
|
||||
return 0; // TODO: this looks like a buggy caller?
|
||||
|
@ -737,6 +772,11 @@ double OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernArray,
|
|||
nLen = rStr.getLength() - nIndex;
|
||||
}
|
||||
|
||||
if (nPartLen < 0 || nPartIndex + nPartLen >= rStr.getLength())
|
||||
{
|
||||
nPartLen = rStr.getLength() - nPartIndex;
|
||||
}
|
||||
|
||||
std::vector<sal_Int32>* pDXAry = pKernArray ? &pKernArray->get_subunit_array() : nullptr;
|
||||
|
||||
// do layout
|
||||
|
@ -752,7 +792,7 @@ double OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernArray,
|
|||
// and hope that is sufficient.
|
||||
if (pDXAry)
|
||||
{
|
||||
pDXAry->resize(nLen);
|
||||
pDXAry->resize(nPartLen);
|
||||
std::fill(pDXAry->begin(), pDXAry->end(), 0);
|
||||
}
|
||||
return 0;
|
||||
|
@ -761,15 +801,29 @@ double OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernArray,
|
|||
std::unique_ptr<std::vector<double>> xDXPixelArray;
|
||||
if(pDXAry)
|
||||
{
|
||||
xDXPixelArray.reset(new std::vector<double>(nLen));
|
||||
xDXPixelArray.reset(new std::vector<double>(nPartLen));
|
||||
}
|
||||
std::vector<double>* pDXPixelArray = xDXPixelArray.get();
|
||||
double nWidth = pSalLayout->FillDXArray(pDXPixelArray, bCaret ? rStr : OUString());
|
||||
|
||||
double nWidth = 0.0;
|
||||
|
||||
// Fall back to the unbounded DX array when there is no expanded layout context. This is
|
||||
// necessary for certain situations where characters are appended to the input string, such as
|
||||
// automatic ellipsis.
|
||||
if (nIndex == nPartIndex && nLen == nPartLen)
|
||||
{
|
||||
nWidth = pSalLayout->FillDXArray(pDXPixelArray, bCaret ? rStr : OUString());
|
||||
}
|
||||
else
|
||||
{
|
||||
nWidth = pSalLayout->FillPartialDXArray(pDXPixelArray, bCaret ? rStr : OUString(),
|
||||
nPartIndex - nIndex, nPartLen);
|
||||
}
|
||||
|
||||
// convert virtual char widths to virtual absolute positions
|
||||
if( pDXPixelArray )
|
||||
{
|
||||
for( int i = 1; i < nLen; ++i )
|
||||
for (int i = 1; i < nPartLen; ++i)
|
||||
{
|
||||
(*pDXPixelArray)[i] += (*pDXPixelArray)[i - 1];
|
||||
}
|
||||
|
@ -782,20 +836,20 @@ double OutputDevice::GetTextArray( const OUString& rStr, KernArray* pKernArray,
|
|||
int nSubPixelFactor = pKernArray->get_factor();
|
||||
if (mbMap)
|
||||
{
|
||||
for (int i = 0; i < nLen; ++i)
|
||||
for (int i = 0; i < nPartLen; ++i)
|
||||
(*pDXPixelArray)[i] = ImplDevicePixelToLogicWidthDouble((*pDXPixelArray)[i] * nSubPixelFactor);
|
||||
}
|
||||
else if (nSubPixelFactor)
|
||||
{
|
||||
for (int i = 0; i < nLen; ++i)
|
||||
for (int i = 0; i < nPartLen; ++i)
|
||||
(*pDXPixelArray)[i] *= nSubPixelFactor;
|
||||
}
|
||||
}
|
||||
|
||||
if (pDXAry)
|
||||
{
|
||||
pDXAry->resize(nLen);
|
||||
for (int i = 0; i < nLen; ++i)
|
||||
pDXAry->resize(nPartLen);
|
||||
for (int i = 0; i < nPartLen; ++i)
|
||||
(*pDXAry)[i] = basegfx::fround((*pDXPixelArray)[i]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue