xref: /core/sw/source/core/text/itrcrsr.cxx (revision 0e4bcbb6)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <ndtxt.hxx>
21 #include <doc.hxx>
22 #include <paratr.hxx>
23 #include <flyfrm.hxx>
24 #include <pam.hxx>
25 #include <swselectionlist.hxx>
26 #include <sortedobjs.hxx>
27 #include <editeng/adjustitem.hxx>
28 #include <editeng/lspcitem.hxx>
29 #include <editeng/lrspitem.hxx>
30 #include <frmatr.hxx>
31 #include <tgrditem.hxx>
32 #include <IDocumentSettingAccess.hxx>
33 #include <pagefrm.hxx>
34 
35 #include "itrtxt.hxx"
36 #include <txtfrm.hxx>
37 #include <flyfrms.hxx>
38 #include "porfld.hxx"
39 #include "porfly.hxx"
40 #include "pordrop.hxx"
41 #include <crstate.hxx>
42 #include "pormulti.hxx"
43 #include <numrule.hxx>
44 #include <com/sun/star/i18n/ScriptType.hpp>
45 
46 // Not reentrant !!!
47 // is set in GetCharRect and is interpreted in UnitUp/Down.
48 bool SwTextCursor::s_bRightMargin = false;
49 
50 // After calculating the position of a character during GetCharRect
51 // this function allows to find the coordinates of a position (defined
52 // in pCMS->pSpecialPos) inside a special portion (e.g., a field)
53 static void lcl_GetCharRectInsideField( SwTextSizeInfo& rInf, SwRect& rOrig,
54                                  const SwCursorMoveState& rCMS,
55                                  const SwLinePortion& rPor )
56 {
57     OSL_ENSURE( rCMS.m_pSpecialPos, "Information about special pos missing" );
58 
59     if ( rPor.InFieldGrp() && !static_cast<const SwFieldPortion&>(rPor).GetExp().isEmpty() )
60     {
61         const sal_Int32 nCharOfst = rCMS.m_pSpecialPos->nCharOfst;
62         sal_Int32 nFieldIdx = 0;
63         sal_Int32 nFieldLen = 0;
64 
65         OUString sString;
66         const OUString* pString = nullptr;
67         const SwLinePortion* pPor = &rPor;
68         do
69         {
70             if ( pPor->InFieldGrp() )
71             {
72                 sString = static_cast<const SwFieldPortion*>(pPor)->GetExp();
73                 pString = &sString;
74                 nFieldLen = pString->getLength();
75             }
76             else
77             {
78                 pString = nullptr;
79                 nFieldLen = 0;
80             }
81 
82             if ( ! pPor->GetNextPortion() || nFieldIdx + nFieldLen > nCharOfst )
83                 break;
84 
85             nFieldIdx = nFieldIdx + nFieldLen;
86             rOrig.Pos().AdjustX(pPor->Width() );
87             pPor = pPor->GetNextPortion();
88 
89         } while ( true );
90 
91         OSL_ENSURE( nCharOfst >= nFieldIdx, "Request of position inside field failed" );
92         sal_Int32 nLen = nCharOfst - nFieldIdx + 1;
93 
94         if ( pString )
95         {
96             // get script for field portion
97             rInf.GetFont()->SetActual( SwScriptInfo::WhichFont(0, *pString) );
98 
99             TextFrameIndex const nOldLen = pPor->GetLen();
100             const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen - 1));
101             const SwTwips nX1 = pPor->GetLen() ?
102                                 pPor->GetTextSize( rInf ).Width() :
103                                 0;
104 
105             SwTwips nX2 = 0;
106             if ( rCMS.m_bRealWidth )
107             {
108                 const_cast<SwLinePortion*>(pPor)->SetLen(TextFrameIndex(nLen));
109                 nX2 = pPor->GetTextSize( rInf ).Width();
110             }
111 
112             const_cast<SwLinePortion*>(pPor)->SetLen( nOldLen );
113 
114             rOrig.Pos().AdjustX(nX1 );
115             rOrig.Width( ( nX2 > nX1 ) ?
116                          ( nX2 - nX1 ) :
117                            1 );
118         }
119     }
120     else
121     {
122         // special cases: no common fields, e.g., graphic number portion,
123         // FlyInCntPortions, Notes
124         rOrig.Width( rCMS.m_bRealWidth && rPor.Width() ? rPor.Width() : 1 );
125     }
126 }
127 
128 // #i111284#
129 namespace {
130     bool IsLabelAlignmentActive( const SwTextNode& rTextNode )
131     {
132         bool bRet( false );
133 
134         if ( rTextNode.GetNumRule() )
135         {
136             int nListLevel = rTextNode.GetActualListLevel();
137 
138             if (nListLevel < 0)
139                 nListLevel = 0;
140 
141             if (nListLevel >= MAXLEVEL)
142                 nListLevel = MAXLEVEL - 1;
143 
144             const SwNumFormat& rNumFormat =
145                     rTextNode.GetNumRule()->Get( o3tl::narrowing<sal_uInt16>(nListLevel) );
146             if ( rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
147             {
148                 bRet = true;
149             }
150         }
151 
152         return bRet;
153     }
154 } // end of anonymous namespace
155 
156 void SwTextMargin::CtorInitTextMargin( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
157 {
158     CtorInitTextIter( pNewFrame, pNewInf );
159 
160     m_pInf = pNewInf;
161     GetInfo().SetFont( GetFnt() );
162     const SwTextNode *const pNode = m_pFrame->GetTextNodeForParaProps();
163 
164     const SvxLRSpaceItem &rSpace = pNode->GetSwAttrSet().GetLRSpace();
165     // #i95907#
166     // #i111284#
167     const SwTextNode *pTextNode = m_pFrame->GetTextNodeForParaProps();
168     const bool bLabelAlignmentActive = IsLabelAlignmentActive( *pTextNode );
169     const bool bListLevelIndentsApplicable = pTextNode->AreListLevelIndentsApplicable();
170     const bool bListLevelIndentsApplicableAndLabelAlignmentActive = bListLevelIndentsApplicable && bLabelAlignmentActive;
171 
172     // Carefully adjust the text formatting ranges.
173 
174     // This whole area desperately needs some rework. There are
175     // quite a couple of values that need to be considered:
176     // 1. paragraph indent
177     // 2. paragraph first line indent
178     // 3. numbering indent
179     // 4. numbering spacing to text
180     // 5. paragraph border
181     // Note: These values have already been used during calculation
182     // of the printing area of the paragraph.
183     const int nLMWithNum = pNode->GetLeftMarginWithNum( true );
184     if ( m_pFrame->IsRightToLeft() )
185     {
186         // this calculation is identical this the calculation for L2R layout - see below
187         mnLeft = m_pFrame->getFrameArea().Left() +
188                 m_pFrame->getFramePrintArea().Left() +
189                 nLMWithNum -
190                 pNode->GetLeftMarginWithNum() -
191                 // #i95907#
192                 // #i111284#
193                 // rSpace.GetLeft() + rSpace.GetTextLeft();
194                 ( bListLevelIndentsApplicableAndLabelAlignmentActive
195                   ? 0
196                   : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
197     }
198     else
199     {
200         // #i95907#
201         // #i111284#
202         if ( bListLevelIndentsApplicableAndLabelAlignmentActive ||
203              !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
204         {
205             // this calculation is identical this the calculation for R2L layout - see above
206             mnLeft = m_pFrame->getFrameArea().Left() +
207                     m_pFrame->getFramePrintArea().Left() +
208                     nLMWithNum -
209                     pNode->GetLeftMarginWithNum() -
210                     // #i95907#
211                     // #i111284#
212                     ( bListLevelIndentsApplicableAndLabelAlignmentActive
213                       ? 0
214                       : ( rSpace.GetLeft() - rSpace.GetTextLeft() ) );
215         }
216         else
217         {
218             mnLeft = m_pFrame->getFrameArea().Left() +
219                     std::max( tools::Long( rSpace.GetTextLeft() + nLMWithNum ),
220                          m_pFrame->getFramePrintArea().Left() );
221         }
222     }
223 
224     mnRight = m_pFrame->getFrameArea().Left() + m_pFrame->getFramePrintArea().Left() + m_pFrame->getFramePrintArea().Width();
225 
226     if( mnLeft >= mnRight &&
227          // #i53066# Omit adjustment of nLeft for numbered
228          // paras inside cells inside new documents:
229         ( pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) ||
230           !m_pFrame->IsInTab() ||
231           ( !nLMWithNum && (!bLabelAlignmentActive || bListLevelIndentsApplicable) ) ) )
232     {
233         mnLeft = m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left();
234         if( mnLeft >= mnRight )   // e.g. with large paragraph indentations in slim table columns
235             mnRight = mnLeft + 1; // einen goennen wir uns immer
236     }
237 
238     if( m_pFrame->IsFollow() && m_pFrame->GetOffset() )
239         mnFirst = mnLeft;
240     else
241     {
242         short nFLOfst = 0;
243         tools::Long nFirstLineOfs = 0;
244         if( !pNode->GetFirstLineOfsWithNum( nFLOfst ) &&
245             rSpace.IsAutoFirst() )
246         {
247             nFirstLineOfs = GetFnt()->GetSize( GetFnt()->GetActual() ).Height();
248             LanguageType const aLang = m_pFrame->GetLangOfChar(
249                     TextFrameIndex(0), css::i18n::ScriptType::ASIAN);
250             if (aLang != LANGUAGE_KOREAN && aLang != LANGUAGE_JAPANESE)
251                 nFirstLineOfs<<=1;
252 
253             // tdf#129448: Auto first-line indent should not be effected by line space.
254             // Below is for compatibility with old documents.
255             if (!pNode->getIDocumentSettingAccess()->get(DocumentSettingId::AUTO_FIRST_LINE_INDENT_DISREGARD_LINE_SPACE))
256             {
257                 const SvxLineSpacingItem *pSpace = m_aLineInf.GetLineSpacing();
258                 if( pSpace )
259                 {
260                     switch( pSpace->GetLineSpaceRule() )
261                     {
262                         case SvxLineSpaceRule::Auto:
263                         break;
264                         case SvxLineSpaceRule::Min:
265                         {
266                             if( nFirstLineOfs < pSpace->GetLineHeight() )
267                                 nFirstLineOfs = pSpace->GetLineHeight();
268                             break;
269                         }
270                         case SvxLineSpaceRule::Fix:
271                             nFirstLineOfs = pSpace->GetLineHeight();
272                         break;
273                         default: OSL_FAIL( ": unknown LineSpaceRule" );
274                     }
275                     switch( pSpace->GetInterLineSpaceRule() )
276                     {
277                         case SvxInterLineSpaceRule::Off:
278                         break;
279                         case SvxInterLineSpaceRule::Prop:
280                         {
281                             tools::Long nTmp = pSpace->GetPropLineSpace();
282                             // 50% is the minimum, at 0% we switch to
283                             // the default value 100%...
284                             if( nTmp < 50 )
285                                 nTmp = nTmp ? 50 : 100;
286 
287                             nTmp *= nFirstLineOfs;
288                             nTmp /= 100;
289                             if( !nTmp )
290                                 ++nTmp;
291                             nFirstLineOfs = nTmp;
292                             break;
293                         }
294                         case SvxInterLineSpaceRule::Fix:
295                         {
296                             nFirstLineOfs += pSpace->GetInterLineSpace();
297                             break;
298                         }
299                         default: OSL_FAIL( ": unknown InterLineSpaceRule" );
300                     }
301                 }
302             }
303         }
304         else
305             nFirstLineOfs = nFLOfst;
306 
307         // #i95907#
308         // #i111284#
309         if ( m_pFrame->IsRightToLeft() ||
310              bListLevelIndentsApplicableAndLabelAlignmentActive ||
311              !pNode->getIDocumentSettingAccess()->get(DocumentSettingId::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) )
312         {
313             if ( nFirstLineOfs < 0 && m_pFrame->IsInTab() &&
314                  mnLeft == m_pFrame->getFramePrintArea().Left() + m_pFrame->getFrameArea().Left() &&
315                  !m_pFrame->IsRightToLeft() &&
316                  !bListLevelIndentsApplicableAndLabelAlignmentActive )
317             {
318                 // tdf#130218 always show hanging indent in narrow table cells
319                 // to avoid hiding the text content of the first line
320                 mnLeft -= nFirstLineOfs;
321             }
322 
323             mnFirst = mnLeft + nFirstLineOfs;
324         }
325         else
326         {
327               mnFirst = m_pFrame->getFrameArea().Left() +
328                      std::max( rSpace.GetTextLeft() + nLMWithNum+ nFirstLineOfs,
329                           m_pFrame->getFramePrintArea().Left() );
330         }
331 
332         // Note: <SwTextFrame::GetAdditionalFirstLineOffset()> returns a negative
333         //       value for the new list label position and space mode LABEL_ALIGNMENT
334         //       and label alignment CENTER and RIGHT in L2R layout respectively
335         //       label alignment LEFT and CENTER in R2L layout
336         mnFirst += m_pFrame->GetAdditionalFirstLineOffset();
337 
338         if( mnFirst >= mnRight )
339             mnFirst = mnRight - 1;
340     }
341     const SvxAdjustItem& rAdjust = m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetAdjust();
342     mnAdjust = rAdjust.GetAdjust();
343 
344     // left is left and right is right
345     if ( m_pFrame->IsRightToLeft() )
346     {
347         if ( SvxAdjust::Left == mnAdjust )
348             mnAdjust = SvxAdjust::Right;
349         else if ( SvxAdjust::Right == mnAdjust )
350             mnAdjust = SvxAdjust::Left;
351     }
352 
353     m_bOneBlock = rAdjust.GetOneWord() == SvxAdjust::Block;
354     m_bLastBlock = rAdjust.GetLastBlock() == SvxAdjust::Block;
355     m_bLastCenter = rAdjust.GetLastBlock() == SvxAdjust::Center;
356 
357     // #i91133#
358     mnTabLeft = pNode->GetLeftMarginForTabCalculation();
359 
360     DropInit();
361 }
362 
363 void SwTextMargin::DropInit()
364 {
365     mnDropLeft = mnDropLines = mnDropHeight = mnDropDescent = 0;
366     const SwParaPortion *pPara = GetInfo().GetParaPortion();
367     if( pPara )
368     {
369         const SwDropPortion *pPorDrop = pPara->FindDropPortion();
370         if ( pPorDrop )
371         {
372             mnDropLeft = pPorDrop->GetDropLeft();
373             mnDropLines = pPorDrop->GetLines();
374             mnDropHeight = pPorDrop->GetDropHeight();
375             mnDropDescent = pPorDrop->GetDropDescent();
376         }
377     }
378 }
379 
380 // The function is interpreting / observing / evaluating / keeping / respecting the first line indention and the specified width.
381 SwTwips SwTextMargin::GetLineStart() const
382 {
383     SwTwips nRet = GetLeftMargin();
384     if( GetAdjust() != SvxAdjust::Left &&
385         !m_pCurr->GetFirstPortion()->IsMarginPortion() )
386     {
387         // If the first portion is a Margin, then the
388         // adjustment is expressed by the portions.
389         if( GetAdjust() == SvxAdjust::Right )
390             nRet = Right() - CurrWidth();
391         else if( GetAdjust() == SvxAdjust::Center )
392             nRet += (GetLineWidth() - CurrWidth()) / 2;
393     }
394     return nRet;
395 }
396 
397 void SwTextCursor::CtorInitTextCursor( SwTextFrame *pNewFrame, SwTextSizeInfo *pNewInf )
398 {
399     CtorInitTextMargin( pNewFrame, pNewInf );
400     // 6096: Attention, the iterators are derived!
401     // GetInfo().SetOut( GetInfo().GetWin() );
402 }
403 
404 // 1170: Ancient bug: Shift-End forgets the last character ...
405 void SwTextCursor::GetEndCharRect(SwRect* pOrig, const TextFrameIndex nOfst,
406                                   SwCursorMoveState* pCMS, const tools::Long nMax )
407 {
408     // 1170: Ambiguity of document positions
409     s_bRightMargin = true;
410     CharCursorToLine(nOfst);
411 
412     // Somehow twisted: nOfst names the position behind the last
413     // character of the last line == This is the position in front of the first character
414     // of the line, in which we are situated:
415     if( nOfst != GetStart() || !m_pCurr->GetLen() )
416     {
417         // 8810: Master line RightMargin, after that LeftMargin
418         GetCharRect( pOrig, nOfst, pCMS, nMax );
419         s_bRightMargin = nOfst >= GetEnd() && nOfst < TextFrameIndex(GetInfo().GetText().getLength());
420         return;
421     }
422 
423     if( !GetPrev() || !GetPrev()->GetLen() || !PrevLine() )
424     {
425         GetCharRect( pOrig, nOfst, pCMS, nMax );
426         return;
427     }
428 
429     // If necessary, as catch up, do the adjustment
430     GetAdjusted();
431 
432     tools::Long nX = 0;
433     tools::Long nLast = 0;
434     SwLinePortion *pPor = m_pCurr->GetFirstPortion();
435 
436     SwTwips nTmpHeight, nTmpAscent;
437     CalcAscentAndHeight( nTmpAscent, nTmpHeight );
438     sal_uInt16 nPorHeight = nTmpHeight;
439     sal_uInt16 nPorAscent = nTmpAscent;
440 
441     // Search for the last Text/EndPortion of the line
442     while( pPor )
443     {
444         nX = nX + pPor->Width();
445         if( pPor->InTextGrp() || ( pPor->GetLen() && !pPor->IsFlyPortion()
446             && !pPor->IsHolePortion() ) || pPor->IsBreakPortion() )
447         {
448             nLast = nX;
449             nPorHeight = pPor->Height();
450             nPorAscent = pPor->GetAscent();
451         }
452         pPor = pPor->GetNextPortion();
453     }
454 
455     const Size aCharSize( 1, nTmpHeight );
456     pOrig->Pos( GetTopLeft() );
457     pOrig->SSize( aCharSize );
458     pOrig->Pos().AdjustX(nLast );
459     const SwTwips nTmpRight = Right() - 1;
460     if( pOrig->Left() > nTmpRight )
461         pOrig->Pos().setX( nTmpRight );
462 
463     if ( pCMS && pCMS->m_bRealHeight )
464     {
465         if ( nTmpAscent > nPorAscent )
466             pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
467         else
468             pCMS->m_aRealHeight.setX( 0 );
469         OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
470         pCMS->m_aRealHeight.setY( nPorHeight );
471     }
472 }
473 
474 // internal function, called by SwTextCursor::GetCharRect() to calculate
475 // the relative character position in the current line.
476 // pOrig refers to x and y coordinates, width and height of the cursor
477 // pCMS is used for restricting the cursor, if there are different font
478 // heights in one line ( first value = offset to y of pOrig, second
479 // value = real height of (shortened) cursor
480 void SwTextCursor::GetCharRect_( SwRect* pOrig, TextFrameIndex const nOfst,
481     SwCursorMoveState* pCMS )
482 {
483     const OUString aText = GetInfo().GetText();
484     SwTextSizeInfo aInf( GetInfo(), &aText, m_nStart );
485     if( GetPropFont() )
486         aInf.GetFont()->SetProportion( GetPropFont() );
487     SwTwips nTmpAscent, nTmpHeight;  // Line height
488     CalcAscentAndHeight( nTmpAscent, nTmpHeight );
489     const Size  aCharSize( 1, nTmpHeight );
490     const Point aCharPos;
491     pOrig->Pos( aCharPos );
492     pOrig->SSize( aCharSize );
493 
494     // If we are looking for a position inside a field which covers
495     // more than one line we may not skip any "empty portions" at the
496     // beginning of a line
497     const bool bInsideFirstField = pCMS && pCMS->m_pSpecialPos &&
498                                     ( pCMS->m_pSpecialPos->nLineOfst ||
499                                       SwSPExtendRange::BEFORE ==
500                                       pCMS->m_pSpecialPos->nExtendRange );
501 
502     bool bWidth = pCMS && pCMS->m_bRealWidth;
503     if( !m_pCurr->GetLen() && !m_pCurr->Width() )
504     {
505         if ( pCMS && pCMS->m_bRealHeight )
506         {
507             pCMS->m_aRealHeight.setX( 0 );
508             pCMS->m_aRealHeight.setY( nTmpHeight );
509         }
510     }
511     else
512     {
513         SwTwips nPorHeight = nTmpHeight;
514         SwTwips nPorAscent = nTmpAscent;
515         SwTwips nX = 0;
516         SwTwips nTmpFirst = 0;
517         SwLinePortion *pPor = m_pCurr->GetFirstPortion();
518         SwBidiPortion* pLastBidiPor = nullptr;
519         TextFrameIndex nLastBidiIdx(-1);
520         SwTwips nLastBidiPorWidth = 0;
521         std::deque<sal_uInt16>* pKanaComp = m_pCurr->GetpKanaComp();
522         sal_uInt16 nSpaceIdx = 0;
523         size_t nKanaIdx = 0;
524         tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
525 
526         bool bNoText = true;
527 
528         // First all portions without Len at beginning of line are skipped.
529         // Exceptions are the mean special portions from WhichFirstPortion:
530         // Num, ErgoSum, FootnoteNum, FieldRests
531         // 8477: but also the only Textportion of an empty line with
532         // Right/Center-Adjustment! So not just pPor->GetExpandPortion() ...
533         while( pPor && !pPor->GetLen() && ! bInsideFirstField )
534         {
535             nX += pPor->Width();
536             if ( pPor->InSpaceGrp() && nSpaceAdd )
537                 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
538             if( bNoText )
539                 nTmpFirst = nX;
540             // 8670: EndPortions count once as TextPortions.
541             // if( pPor->InTextGrp() || pPor->IsBreakPortion() )
542             if( pPor->InTextGrp() || pPor->IsBreakPortion() || pPor->InTabGrp() )
543             {
544                 bNoText = false;
545                 nTmpFirst = nX;
546             }
547             if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
548             {
549                 if ( m_pCurr->IsSpaceAdd() )
550                 {
551                     if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
552                         nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
553                     else
554                         nSpaceAdd = 0;
555                 }
556 
557                 if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
558                     ++nKanaIdx;
559             }
560             if( pPor->InFixMargGrp() )
561             {
562                 if( pPor->IsMarginPortion() )
563                     bNoText = false;
564                 else
565                 {
566                     // fix margin portion => next SpaceAdd, KanaComp value
567                     if ( m_pCurr->IsSpaceAdd() )
568                     {
569                         if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
570                             nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
571                         else
572                             nSpaceAdd = 0;
573                     }
574 
575                     if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
576                         ++nKanaIdx;
577                 }
578             }
579             pPor = pPor->GetNextPortion();
580         }
581 
582         if( !pPor )
583         {
584             // There's just Spezialportions.
585             nX = nTmpFirst;
586         }
587         else
588         {
589             if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
590                 (!pPor->InFieldGrp() || pPor->GetAscent() ) )
591             {
592                 nPorHeight = pPor->Height();
593                 nPorAscent = pPor->GetAscent();
594             }
595             while( pPor && !pPor->IsBreakPortion() && ( aInf.GetIdx() < nOfst ||
596                    ( bWidth && ( pPor->IsKernPortion() || pPor->IsMultiPortion() ) ) ) )
597             {
598                 if( !pPor->IsMarginPortion() && !pPor->IsPostItsPortion() &&
599                     (!pPor->InFieldGrp() || pPor->GetAscent() ) )
600                 {
601                     nPorHeight = pPor->Height();
602                     nPorAscent = pPor->GetAscent();
603                 }
604 
605                 // If we are behind the portion, we add the portion width to
606                 // nX. Special case: nOfst = aInf.GetIdx() + pPor->GetLen().
607                 // For common portions (including BidiPortions) we want to add
608                 // the portion width to nX. For MultiPortions, nExtra = 0,
609                 // therefore we go to the 'else' branch and start a recursion.
610                 const TextFrameIndex nExtra( (pPor->IsMultiPortion()
611                              && !static_cast<SwMultiPortion*>(pPor)->IsBidi()
612                              && !bWidth)
613                         ? 0 : 1 );
614                 if ( aInf.GetIdx() + pPor->GetLen() < nOfst + nExtra )
615                 {
616                     if ( pPor->InSpaceGrp() && nSpaceAdd )
617                         nX += pPor->PrtWidth() +
618                               pPor->CalcSpacing( nSpaceAdd, aInf );
619                     else
620                     {
621                         if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
622                         {
623                             // update to current SpaceAdd, KanaComp values
624                             if ( m_pCurr->IsSpaceAdd() )
625                             {
626                                 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
627                                     nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
628                                 else
629                                     nSpaceAdd = 0;
630                             }
631 
632                             if ( pKanaComp &&
633                                 ( nKanaIdx + 1 ) < pKanaComp->size()
634                                 )
635                                 ++nKanaIdx;
636                         }
637                         if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
638                                 !pPor->GetNextPortion()->IsMarginPortion() ) )
639                             nX += pPor->PrtWidth();
640                     }
641                     if( pPor->IsMultiPortion() )
642                     {
643                         if ( static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
644                         {
645                             if ( m_pCurr->IsSpaceAdd() )
646                             {
647                                 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
648                                     nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
649                                 else
650                                     nSpaceAdd = 0;
651                             }
652 
653                             if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
654                                 ++nKanaIdx;
655                         }
656 
657                         // if we are right behind a BidiPortion, we have to
658                         // hold a pointer to the BidiPortion in order to
659                         // find the correct cursor position, depending on the
660                         // cursor level
661                         if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() &&
662                              aInf.GetIdx() + pPor->GetLen() == nOfst )
663                         {
664                              pLastBidiPor = static_cast<SwBidiPortion*>(pPor);
665                              nLastBidiIdx = aInf.GetIdx();
666                              nLastBidiPorWidth = pLastBidiPor->Width() +
667                                                  pLastBidiPor->CalcSpacing( nSpaceAdd, aInf );
668                         }
669                     }
670 
671                     aInf.SetIdx( aInf.GetIdx() + pPor->GetLen() );
672                     pPor = pPor->GetNextPortion();
673                 }
674                 else
675                 {
676                     if( pPor->IsMultiPortion() )
677                     {
678                         nTmpAscent = AdjustBaseLine( *m_pCurr, pPor );
679                         GetInfo().SetMulti( true );
680                         pOrig->Pos().AdjustY(nTmpAscent - nPorAscent );
681 
682                         if( pCMS && pCMS->m_b2Lines )
683                         {
684                             const bool bRecursion (pCMS->m_x2Lines);
685                             if ( !bRecursion )
686                             {
687                                 pCMS->m_x2Lines = Sw2LinesPos();
688                                 pCMS->m_x2Lines->aLine = SwRect(aCharPos, aCharSize);
689                             }
690 
691                             if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
692                             {
693                                 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
694                                     pCMS->m_x2Lines->nMultiType = MultiPortionType::ROT_270;
695                                 else
696                                     pCMS->m_x2Lines->nMultiType = MultiPortionType::ROT_90;
697                             }
698                             else if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
699                                 pCMS->m_x2Lines->nMultiType = MultiPortionType::TWOLINE;
700                             else if( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
701                                 pCMS->m_x2Lines->nMultiType = MultiPortionType::BIDI;
702                             else
703                                 pCMS->m_x2Lines->nMultiType = MultiPortionType::RUBY;
704 
705                             SwTwips nTmpWidth = pPor->Width();
706                             if( nSpaceAdd )
707                                 nTmpWidth += pPor->CalcSpacing(nSpaceAdd, aInf);
708 
709                             SwRect aRect( Point(aCharPos.X() + nX, pOrig->Top() ),
710                                           Size( nTmpWidth, pPor->Height() ) );
711 
712                             if ( ! bRecursion )
713                                 pCMS->m_x2Lines->aPortion = aRect;
714                             else
715                                 pCMS->m_x2Lines->aPortion2 = aRect;
716                         }
717 
718                         // In a multi-portion we use GetCharRect()-function
719                         // recursively and must add the x-position
720                         // of the multi-portion.
721                         TextFrameIndex const nOldStart = m_nStart;
722                         SwTwips nOldY = m_nY;
723                         sal_uInt8 nOldProp = GetPropFont();
724                         m_nStart = aInf.GetIdx();
725                         SwLineLayout* pOldCurr = m_pCurr;
726                         m_pCurr = &static_cast<SwMultiPortion*>(pPor)->GetRoot();
727                         if( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
728                             SetPropFont( 50 );
729 
730                         SwTextGridItem const*const pGrid(
731                                 GetGridItem(GetTextFrame()->FindPageFrame()));
732                         const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
733                         const sal_uInt16 nRubyHeight = bHasGrid ?
734                                                    pGrid->GetRubyHeight() : 0;
735 
736                         if( m_nStart + m_pCurr->GetLen() <= nOfst && GetNext() &&
737                             ( ! static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
738                                 static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
739                         {
740                             sal_uInt16 nOffset;
741                             // in grid mode we may only add the height of the
742                             // ruby line if ruby line is on top
743                             if ( bHasGrid &&
744                                 static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
745                                 static_cast<SwMultiPortion*>(pPor)->OnTop() )
746                                 nOffset = nRubyHeight;
747                             else
748                                 nOffset = GetLineHeight();
749 
750                             pOrig->Pos().AdjustY(nOffset );
751                             Next();
752                         }
753 
754                         const bool bSpaceChg = static_cast<SwMultiPortion*>(pPor)->
755                                                 ChgSpaceAdd( m_pCurr, nSpaceAdd );
756                         Point aOldPos = pOrig->Pos();
757 
758                         // Ok, for ruby portions in grid mode we have to
759                         // temporarily set the inner line height to the
760                         // outer line height because that value is needed
761                         // for the adjustment inside the recursion
762                         const sal_uInt16 nOldRubyHeight = m_pCurr->Height();
763                         const sal_uInt16 nOldRubyRealHeight = m_pCurr->GetRealHeight();
764                         const bool bChgHeight =
765                                 static_cast<SwMultiPortion*>(pPor)->IsRuby() && bHasGrid;
766 
767                         if ( bChgHeight )
768                         {
769                             m_pCurr->Height( pOldCurr->Height() - nRubyHeight );
770                             m_pCurr->SetRealHeight( pOldCurr->GetRealHeight() -
771                                                   nRubyHeight );
772                         }
773 
774                         SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
775                         if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
776                         {
777                             aLayoutModeModifier.Modify(
778                                 static_cast<SwBidiPortion*>(pPor)->GetLevel() % 2 );
779                         }
780 
781                         GetCharRect_( pOrig, nOfst, pCMS );
782 
783                         if ( bChgHeight )
784                         {
785                             m_pCurr->Height( nOldRubyHeight );
786                             m_pCurr->SetRealHeight( nOldRubyRealHeight );
787                         }
788 
789                         // if we are still in the first row of
790                         // our 2 line multiportion, we use the FirstMulti flag
791                         // to indicate this
792                         if ( static_cast<SwMultiPortion*>(pPor)->IsDouble() )
793                         {
794                             // the recursion may have damaged our font size
795                             SetPropFont( nOldProp );
796                             GetInfo().GetFont()->SetProportion( 100 );
797 
798                             if ( m_pCurr == &static_cast<SwMultiPortion*>(pPor)->GetRoot() )
799                             {
800                                 GetInfo().SetFirstMulti( true );
801 
802                                 // we want to treat a double line portion like a
803                                 // single line portion, if there is no text in
804                                 // the second line
805                                 if ( !m_pCurr->GetNext() ||
806                                      !m_pCurr->GetNext()->GetLen() )
807                                     GetInfo().SetMulti( false );
808                             }
809                         }
810                         // ruby portions are treated like single line portions
811                         else if( static_cast<SwMultiPortion*>(pPor)->IsRuby() ||
812                                  static_cast<SwMultiPortion*>(pPor)->IsBidi() )
813                             GetInfo().SetMulti( false );
814 
815                         // calculate cursor values
816                         if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
817                         {
818                             GetInfo().SetMulti( false );
819                             tools::Long nTmp = pOrig->Width();
820                             pOrig->Width( pOrig->Height() );
821                             pOrig->Height( nTmp );
822                             nTmp = pOrig->Left() - aOldPos.X();
823 
824                             // if we travel into our rotated portion from
825                             // a line below, we have to take care, that the
826                             // y coord in pOrig is less than line height:
827                             if ( nTmp )
828                                 nTmp--;
829 
830                             pOrig->Pos().setX( nX + aOldPos.X() );
831                             if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
832                                 pOrig->Pos().setY( aOldPos.Y() + nTmp );
833                             else
834                                 pOrig->Pos().setY( aOldPos.Y()
835                                     + pPor->Height() - nTmp - pOrig->Height() );
836                             if ( pCMS && pCMS->m_bRealHeight )
837                             {
838                                 pCMS->m_aRealHeight.setY( -pCMS->m_aRealHeight.Y() );
839                                 // result for rotated multi portion is not
840                                 // correct for reverse (270 degree) portions
841                                 if( static_cast<SwMultiPortion*>(pPor)->IsRevers() )
842                                 {
843                                     if ( SvxParaVertAlignItem::Align::Automatic ==
844                                          GetLineInfo().GetVertAlign() )
845                                         // if vertical alignment is set to auto,
846                                         // we switch from base line alignment
847                                         // to centered alignment
848                                         pCMS->m_aRealHeight.setX(
849                                             ( pOrig->Width() +
850                                               pCMS->m_aRealHeight.Y() ) / 2 );
851                                     else
852                                         pCMS->m_aRealHeight.setX(
853                                             pOrig->Width() -
854                                             pCMS->m_aRealHeight.X() +
855                                             pCMS->m_aRealHeight.Y() );
856                                 }
857                             }
858                         }
859                         else
860                         {
861                             pOrig->Pos().AdjustY(aOldPos.Y() );
862                             if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
863                             {
864                                 const SwTwips nPorWidth = pPor->Width() +
865                                                          pPor->CalcSpacing( nSpaceAdd, aInf );
866                                 const SwTwips nInsideOfst = pOrig->Pos().X();
867                                 pOrig->Pos().setX( nX + nPorWidth -
868                                                    nInsideOfst - pOrig->Width() );
869                             }
870                             else
871                                 pOrig->Pos().AdjustX(nX );
872 
873                             if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
874                                 pOrig->Pos().AdjustX(
875                                     static_cast<SwDoubleLinePortion*>(pPor)->PreWidth() );
876                         }
877 
878                         if( bSpaceChg )
879                             SwDoubleLinePortion::ResetSpaceAdd( m_pCurr );
880 
881                         m_pCurr = pOldCurr;
882                         m_nStart = nOldStart;
883                         m_nY = nOldY;
884                         m_bPrev = false;
885 
886                         return;
887                     }
888                     if ( pPor->PrtWidth() )
889                     {
890                         TextFrameIndex const nOldLen = pPor->GetLen();
891                         pPor->SetLen( nOfst - aInf.GetIdx() );
892                         aInf.SetLen( pPor->GetLen() );
893                         if( nX || !pPor->InNumberGrp() )
894                         {
895                             SeekAndChg( aInf );
896                             const bool bOldOnWin = aInf.OnWin();
897                             aInf.SetOnWin( false ); // no BULLETs!
898                             SwTwips nTmp = nX;
899                             aInf.SetKanaComp( pKanaComp );
900                             aInf.SetKanaIdx( nKanaIdx );
901                             nX += pPor->GetTextSize( aInf ).Width();
902                             aInf.SetOnWin( bOldOnWin );
903                             if ( pPor->InSpaceGrp() && nSpaceAdd )
904                                 nX += pPor->CalcSpacing( nSpaceAdd, aInf );
905                             if( bWidth )
906                             {
907                                 pPor->SetLen(pPor->GetLen() + TextFrameIndex(1));
908                                 aInf.SetLen( pPor->GetLen() );
909                                 aInf.SetOnWin( false ); // no BULLETs!
910                                 nTmp += pPor->GetTextSize( aInf ).Width();
911                                 aInf.SetOnWin( bOldOnWin );
912                                 if ( pPor->InSpaceGrp() && nSpaceAdd )
913                                     nTmp += pPor->CalcSpacing(nSpaceAdd, aInf);
914                                 pOrig->Width( nTmp - nX );
915                             }
916                         }
917                         pPor->SetLen( nOldLen );
918 
919                         // Shift the cursor with the right border width
920                         // Note: nX remains positive because GetTextSize() also include the width of the right border
921                         if( aInf.GetIdx() < nOfst && nOfst < aInf.GetIdx() + pPor->GetLen() )
922                         {
923                             // Find the current drop portion part and use its right border
924                             if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
925                             {
926                                 SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
927                                 const SwDropPortionPart* pCurrPart = pDrop->GetPart();
928                                 TextFrameIndex nSumLength(0);
929                                 while( pCurrPart && (nSumLength += pCurrPart->GetLen()) < nOfst - aInf.GetIdx() )
930                                 {
931                                     pCurrPart = pCurrPart->GetFollow();
932                                 }
933                                 if( pCurrPart && nSumLength != nOfst - aInf.GetIdx() &&
934                                     pCurrPart->GetFont().GetRightBorder() && !pCurrPart->GetJoinBorderWithNext() )
935                                 {
936                                     nX -= pCurrPart->GetFont().GetRightBorderSpace();
937                                 }
938                             }
939                             else if( GetInfo().GetFont()->GetRightBorder() && !pPor->GetJoinBorderWithNext())
940                             {
941                                 nX -= GetInfo().GetFont()->GetRightBorderSpace();
942                             }
943                         }
944                     }
945                     bWidth = false;
946                     break;
947                 }
948             }
949         }
950 
951         if( pPor )
952         {
953             OSL_ENSURE( !pPor->InNumberGrp() || bInsideFirstField, "Number surprise" );
954             bool bEmptyField = false;
955             if( pPor->InFieldGrp() && pPor->GetLen() )
956             {
957                 SwFieldPortion *pTmp = static_cast<SwFieldPortion*>(pPor);
958                 while( pTmp->HasFollow() && pTmp->GetExp().isEmpty() )
959                 {
960                     sal_uInt16 nAddX = pTmp->Width();
961                     SwLinePortion *pNext = pTmp->GetNextPortion();
962                     while( pNext && !pNext->InFieldGrp() )
963                     {
964                         OSL_ENSURE( !pNext->GetLen(), "Where's my field follow?" );
965                         nAddX = nAddX + pNext->Width();
966                         pNext = pNext->GetNextPortion();
967                     }
968                     if( !pNext )
969                         break;
970                     pTmp = static_cast<SwFieldPortion*>(pNext);
971                     nPorHeight = pTmp->Height();
972                     nPorAscent = pTmp->GetAscent();
973                     nX += nAddX;
974                     bEmptyField = true;
975                 }
976             }
977             // 8513: Fields in justified text, skipped
978             while( pPor && !pPor->GetLen() && ! bInsideFirstField &&
979                    ( pPor->IsFlyPortion() || pPor->IsKernPortion() ||
980                      pPor->IsBlankPortion() || pPor->InTabGrp() ||
981                      ( !bEmptyField && pPor->InFieldGrp() ) ) )
982             {
983                 if ( pPor->InSpaceGrp() && nSpaceAdd )
984                     nX += pPor->PrtWidth() +
985                           pPor->CalcSpacing( nSpaceAdd, aInf );
986                 else
987                 {
988                     if( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() )
989                     {
990                         if ( m_pCurr->IsSpaceAdd() )
991                         {
992                             if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
993                                 nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
994                             else
995                                 nSpaceAdd = 0;
996                         }
997 
998                         if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
999                             ++nKanaIdx;
1000                     }
1001                     if ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
1002                             !pPor->GetNextPortion()->IsMarginPortion() ) )
1003                         nX += pPor->PrtWidth();
1004                 }
1005                 if( pPor->IsMultiPortion() &&
1006                     static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1007                 {
1008                     if ( m_pCurr->IsSpaceAdd() )
1009                     {
1010                         if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1011                             nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1012                         else
1013                             nSpaceAdd = 0;
1014                     }
1015 
1016                     if( pKanaComp && ( nKanaIdx + 1 ) < pKanaComp->size() )
1017                         ++nKanaIdx;
1018                 }
1019                 if( !pPor->IsFlyPortion() )
1020                 {
1021                     nPorHeight = pPor->Height();
1022                     nPorAscent = pPor->GetAscent();
1023                 }
1024                 pPor = pPor->GetNextPortion();
1025             }
1026 
1027             if( aInf.GetIdx() == nOfst && pPor && pPor->InHyphGrp() &&
1028                 pPor->GetNextPortion() && pPor->GetNextPortion()->InFixGrp() )
1029             {
1030                 // All special portions have to be skipped
1031                 // Taking the German word "zusammen" as example: zu-[FLY]sammen, 'u' == 19, 's' == 20; Right()
1032                 // Without the adjustment we end up in front of '-', with the
1033                 // adjustment in front of the 's'.
1034                 while( pPor && !pPor->GetLen() )
1035                 {
1036                     nX += pPor->Width();
1037                     if( !pPor->IsMarginPortion() )
1038                     {
1039                         nPorHeight = pPor->Height();
1040                         nPorAscent = pPor->GetAscent();
1041                     }
1042                     pPor = pPor->GetNextPortion();
1043                 }
1044             }
1045             if( pPor && pCMS )
1046             {
1047                 if( pCMS->m_bFieldInfo && pPor->InFieldGrp() && pPor->Width() )
1048                     pOrig->Width( pPor->Width() );
1049                 if( pPor->IsDropPortion() )
1050                 {
1051                     nPorAscent = static_cast<SwDropPortion*>(pPor)->GetDropHeight();
1052                     // The drop height is only calculated, if we have more than
1053                     // one line. Otherwise it is 0.
1054                     if ( ! nPorAscent)
1055                         nPorAscent = pPor->Height();
1056                     nPorHeight = nPorAscent;
1057                     pOrig->Height( nPorHeight +
1058                         static_cast<SwDropPortion*>(pPor)->GetDropDescent() );
1059                     if( nTmpHeight < pOrig->Height() )
1060                     {
1061                         nTmpAscent = nPorAscent;
1062                         nTmpHeight = sal_uInt16( pOrig->Height() );
1063                     }
1064                 }
1065                 if( bWidth && pPor->PrtWidth() && pPor->GetLen() &&
1066                     aInf.GetIdx() == nOfst )
1067                 {
1068                     if( !pPor->IsFlyPortion() && pPor->Height() &&
1069                         pPor->GetAscent() )
1070                     {
1071                         nPorHeight = pPor->Height();
1072                         nPorAscent = pPor->GetAscent();
1073                     }
1074                     SwTwips nTmp;
1075                     if (TextFrameIndex(2) > pPor->GetLen())
1076                     {
1077                         nTmp = pPor->Width();
1078                         if ( pPor->InSpaceGrp() && nSpaceAdd )
1079                             nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1080                     }
1081                     else
1082                     {
1083                         const bool bOldOnWin = aInf.OnWin();
1084                         TextFrameIndex const nOldLen = pPor->GetLen();
1085                         pPor->SetLen( TextFrameIndex(1) );
1086                         aInf.SetLen( pPor->GetLen() );
1087                         SeekAndChg( aInf );
1088                         aInf.SetOnWin( false ); // no BULLETs!
1089                         aInf.SetKanaComp( pKanaComp );
1090                         aInf.SetKanaIdx( nKanaIdx );
1091                         nTmp = pPor->GetTextSize( aInf ).Width();
1092                         aInf.SetOnWin( bOldOnWin );
1093                         if ( pPor->InSpaceGrp() && nSpaceAdd )
1094                             nTmp += pPor->CalcSpacing( nSpaceAdd, aInf );
1095                         pPor->SetLen( nOldLen );
1096                     }
1097                     pOrig->Width( nTmp );
1098                 }
1099 
1100                 // travel inside field portion?
1101                 if ( pCMS->m_pSpecialPos )
1102                 {
1103                     // apply attributes to font
1104                     Seek( nOfst );
1105                     lcl_GetCharRectInsideField( aInf, *pOrig, *pCMS, *pPor );
1106                 }
1107             }
1108         }
1109 
1110         // special case: We are at the beginning of a BidiPortion or
1111         // directly behind a BidiPortion
1112         if ( pCMS &&
1113                 ( pLastBidiPor ||
1114                 ( pPor &&
1115                   pPor->IsMultiPortion() &&
1116                   static_cast<SwMultiPortion*>(pPor)->IsBidi() ) ) )
1117         {
1118             // we determine if the cursor has to blink before or behind
1119             // the bidi portion
1120             if ( pLastBidiPor )
1121             {
1122                 const sal_uInt8 nPortionLevel = pLastBidiPor->GetLevel();
1123 
1124                 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1125                 {
1126                     // we came from inside the bidi portion, we want to blink
1127                     // behind the portion
1128                     pOrig->Pos().AdjustX( -nLastBidiPorWidth );
1129 
1130                     // Again, there is a special case: logically behind
1131                     // the portion can actually mean that the cursor is inside
1132                     // the portion. This can happen is the last portion
1133                     // inside the bidi portion is a nested bidi portion
1134                     SwLineLayout& rLineLayout =
1135                             static_cast<SwMultiPortion*>(pLastBidiPor)->GetRoot();
1136 
1137                     const SwLinePortion *pLast = rLineLayout.FindLastPortion();
1138                     if ( pLast->IsMultiPortion() )
1139                     {
1140                         OSL_ENSURE( static_cast<const SwMultiPortion*>(pLast)->IsBidi(),
1141                                  "Non-BidiPortion inside BidiPortion" );
1142                         TextFrameIndex const nIdx = aInf.GetIdx();
1143                         // correct the index before using CalcSpacing.
1144                         aInf.SetIdx(nLastBidiIdx);
1145                         pOrig->Pos().AdjustX(pLast->Width() +
1146                                             pLast->CalcSpacing( nSpaceAdd, aInf ) );
1147                         aInf.SetIdx(nIdx);
1148                     }
1149                 }
1150             }
1151             else
1152             {
1153                 const sal_uInt8 nPortionLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1154 
1155                 if ( pCMS->m_nCursorBidiLevel >= nPortionLevel )
1156                 {
1157                     // we came from inside the bidi portion, we want to blink
1158                     // behind the portion
1159                     pOrig->Pos().AdjustX(pPor->Width() +
1160                                         pPor->CalcSpacing( nSpaceAdd, aInf ) );
1161                 }
1162             }
1163         }
1164 
1165         pOrig->Pos().AdjustX(nX );
1166 
1167         if ( pCMS && pCMS->m_bRealHeight )
1168         {
1169             nTmpAscent = AdjustBaseLine( *m_pCurr, nullptr, nPorHeight, nPorAscent );
1170             if ( nTmpAscent > nPorAscent )
1171                 pCMS->m_aRealHeight.setX( nTmpAscent - nPorAscent );
1172             else
1173                 pCMS->m_aRealHeight.setX( 0 );
1174             OSL_ENSURE( nPorHeight, "GetCharRect: Missing Portion-Height" );
1175             if ( nTmpHeight > nPorHeight )
1176                 pCMS->m_aRealHeight.setY( nPorHeight );
1177             else
1178                 pCMS->m_aRealHeight.setY( nTmpHeight );
1179         }
1180     }
1181 }
1182 
1183 void SwTextCursor::GetCharRect( SwRect* pOrig, TextFrameIndex const nOfst,
1184                                SwCursorMoveState* pCMS, const tools::Long nMax )
1185 {
1186     CharCursorToLine(nOfst);
1187 
1188     // Indicates that a position inside a special portion (field, number portion)
1189     // is requested.
1190     const bool bSpecialPos = pCMS && pCMS->m_pSpecialPos;
1191     TextFrameIndex nFindOfst = nOfst;
1192 
1193     if ( bSpecialPos )
1194     {
1195         const SwSPExtendRange nExtendRange = pCMS->m_pSpecialPos->nExtendRange;
1196 
1197         OSL_ENSURE( ! pCMS->m_pSpecialPos->nLineOfst || SwSPExtendRange::BEFORE != nExtendRange,
1198                 "LineOffset AND Number Portion?" );
1199 
1200         // portions which are behind the string
1201         if ( SwSPExtendRange::BEHIND == nExtendRange )
1202             ++nFindOfst;
1203 
1204         // skip lines for fields which cover more than one line
1205         for ( sal_uInt16 i = 0; i < pCMS->m_pSpecialPos->nLineOfst; i++ )
1206             Next();
1207     }
1208 
1209     // If necessary, as catch up, do the adjustment
1210     GetAdjusted();
1211 
1212     const Point aCharPos( GetTopLeft() );
1213 
1214     GetCharRect_( pOrig, nFindOfst, pCMS );
1215 
1216     // This actually would have to be "-1 LogicToPixel", but that seems too
1217     // expensive, so it's a value (-12), that should hopefully be OK.
1218     const SwTwips nTmpRight = Right() - 12;
1219 
1220     pOrig->Pos().AdjustX(aCharPos.X() );
1221     pOrig->Pos().AdjustY(aCharPos.Y() );
1222 
1223     if( pCMS && pCMS->m_b2Lines && pCMS->m_x2Lines )
1224     {
1225         pCMS->m_x2Lines->aLine.Pos().AdjustX(aCharPos.X() );
1226         pCMS->m_x2Lines->aLine.Pos().AdjustY(aCharPos.Y() );
1227         pCMS->m_x2Lines->aPortion.Pos().AdjustX(aCharPos.X() );
1228         pCMS->m_x2Lines->aPortion.Pos().AdjustY(aCharPos.Y() );
1229     }
1230 
1231     const IDocumentSettingAccess& rIDSA = GetTextFrame()->GetDoc().getIDocumentSettingAccess();
1232     const bool bTabOverMargin = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN)
1233         || rIDSA.get(DocumentSettingId::TAB_OVER_SPACING);
1234     // Make sure the cursor respects the right margin, unless in compat mode, where the tab size has priority over the margin size.
1235     if( pOrig->Left() > nTmpRight && !bTabOverMargin)
1236         pOrig->Pos().setX( nTmpRight );
1237 
1238     if( nMax )
1239     {
1240         if( pOrig->Top() + pOrig->Height() > nMax )
1241         {
1242             if( pOrig->Top() > nMax )
1243                 pOrig->Top( nMax );
1244             pOrig->Height( nMax - pOrig->Top() );
1245         }
1246         if ( pCMS && pCMS->m_bRealHeight && pCMS->m_aRealHeight.Y() >= 0 )
1247         {
1248             tools::Long nTmp = pCMS->m_aRealHeight.X() + pOrig->Top();
1249             if( nTmp >= nMax )
1250             {
1251                 pCMS->m_aRealHeight.setX( nMax - pOrig->Top() );
1252                 pCMS->m_aRealHeight.setY( 0 );
1253             }
1254             else if( nTmp + pCMS->m_aRealHeight.Y() > nMax )
1255                 pCMS->m_aRealHeight.setY( nMax - nTmp );
1256         }
1257     }
1258     tools::Long nOut = pOrig->Right() - GetTextFrame()->getFrameArea().Right();
1259     if( nOut > 0 )
1260     {
1261         if( GetTextFrame()->getFrameArea().Width() < GetTextFrame()->getFramePrintArea().Left()
1262                                    + GetTextFrame()->getFramePrintArea().Width() )
1263             nOut += GetTextFrame()->getFrameArea().Width() - GetTextFrame()->getFramePrintArea().Left()
1264                     - GetTextFrame()->getFramePrintArea().Width();
1265         if( nOut > 0 )
1266             pOrig->Pos().AdjustX( -(nOut + 10) );
1267     }
1268 }
1269 
1270 /**
1271  * Determines if SwTextCursor::GetModelPositionForViewPoint() should consider the next portion when calculating the
1272  * doc model position from a Point.
1273  */
1274 static bool ConsiderNextPortionForCursorOffset(const SwLinePortion* pPor, SwTwips nWidth30, sal_uInt16 nX)
1275 {
1276     if (!pPor->GetNextPortion() || pPor->IsBreakPortion())
1277     {
1278         return false;
1279     }
1280 
1281     // tdf#138592: consider all following zero-width text portions of current text portion,
1282     // like combining characters.
1283     if (nWidth30 == nX && pPor->IsTextPortion() && pPor->GetNextPortion()->IsTextPortion()
1284         && pPor->GetNextPortion()->Width() == 0)
1285         return true;
1286 
1287     // If we're past the target position, stop the iteration in general.
1288     // Exception: don't stop the iteration between as-char fly portions and their comments.
1289     if (nWidth30 >= nX && (!pPor->IsFlyCntPortion() || !pPor->GetNextPortion()->IsPostItsPortion()))
1290     {
1291         // Normally returns false.
1292 
1293         // Another exception: If the cursor is at the very end of the portion, and the next portion is a comment,
1294         // then place the cursor after the zero-width comment. This is primarily to benefit the very end of a line.
1295         return nWidth30 == nX && pPor->GetNextPortion()->IsPostItsPortion();
1296     }
1297 
1298     return true;
1299 }
1300 
1301 // Return: Offset in String
1302 TextFrameIndex SwTextCursor::GetModelPositionForViewPoint( SwPosition *pPos, const Point &rPoint,
1303                                     bool bChgNode, SwCursorMoveState* pCMS ) const
1304 {
1305     // If necessary, as catch up, do the adjustment
1306     GetAdjusted();
1307 
1308     const OUString &rText = GetInfo().GetText();
1309     TextFrameIndex nOffset(0);
1310 
1311     // x is the horizontal offset within the line.
1312     SwTwips x = rPoint.X();
1313     const SwTwips nLeftMargin  = GetLineStart();
1314     SwTwips nRightMargin = GetLineEnd() +
1315         ( GetCurr()->IsHanging() ? GetCurr()->GetHangingMargin() : 0 );
1316     if( nRightMargin == nLeftMargin )
1317         nRightMargin += 30;
1318 
1319     const bool bLeftOver = x < nLeftMargin;
1320     if( bLeftOver )
1321         x = nLeftMargin;
1322     const bool bRightOver = x > nRightMargin;
1323     if( bRightOver )
1324         x = nRightMargin;
1325 
1326     const bool bRightAllowed = pCMS && ( pCMS->m_eState == CursorMoveState::NONE );
1327 
1328     // Until here everything in document coordinates.
1329     x -= nLeftMargin;
1330 
1331     SwTwips nX = x;
1332 
1333     // If there are attribute changes in the line, search for the paragraph,
1334     // in which nX is situated.
1335     SwLinePortion *pPor = m_pCurr->GetFirstPortion();
1336     TextFrameIndex nCurrStart = m_nStart;
1337     bool bHolePortion = false;
1338     bool bLastHyph = false;
1339 
1340     std::deque<sal_uInt16> *pKanaComp = m_pCurr->GetpKanaComp();
1341     TextFrameIndex const nOldIdx = GetInfo().GetIdx();
1342     sal_uInt16 nSpaceIdx = 0;
1343     size_t nKanaIdx = 0;
1344     tools::Long nSpaceAdd = m_pCurr->IsSpaceAdd() ? m_pCurr->GetLLSpaceAdd( 0 ) : 0;
1345     short nKanaComp = pKanaComp ? (*pKanaComp)[0] : 0;
1346 
1347     // nWidth is the width of the line, or the width of
1348     // the paragraph with the font change, in which nX is situated.
1349 
1350     SwTwips nWidth = pPor->Width();
1351     if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1352     {
1353         if ( pPor->InSpaceGrp() && nSpaceAdd )
1354         {
1355             const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1356             nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1357         }
1358         if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1359             ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1360           )
1361         {
1362             if ( m_pCurr->IsSpaceAdd() )
1363             {
1364                 if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1365                     nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1366                 else
1367                     nSpaceAdd = 0;
1368             }
1369 
1370             if( pKanaComp )
1371             {
1372                 if ( nKanaIdx + 1 < pKanaComp->size() )
1373                     nKanaComp = (*pKanaComp)[++nKanaIdx];
1374                 else
1375                     nKanaComp = 0;
1376             }
1377         }
1378     }
1379 
1380     SwTwips nWidth30;
1381     if ( pPor->IsPostItsPortion() )
1382         nWidth30 = 0;
1383     else
1384         nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1385                      30 :
1386                      nWidth;
1387 
1388     while (ConsiderNextPortionForCursorOffset(pPor, nWidth30, nX))
1389     {
1390         nX = nX - nWidth;
1391         nCurrStart = nCurrStart + pPor->GetLen();
1392         bHolePortion = pPor->IsHolePortion();
1393         pPor = pPor->GetNextPortion();
1394         nWidth = pPor->Width();
1395         if ( m_pCurr->IsSpaceAdd() || pKanaComp )
1396         {
1397             if ( pPor->InSpaceGrp() && nSpaceAdd )
1398             {
1399                 const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nCurrStart );
1400                 nWidth = nWidth + sal_uInt16( pPor->CalcSpacing( nSpaceAdd, GetInfo() ) );
1401             }
1402 
1403             if( ( pPor->InFixMargGrp() && ! pPor->IsMarginPortion() ) ||
1404                 ( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->HasTabulator() )
1405               )
1406             {
1407                 if ( m_pCurr->IsSpaceAdd() )
1408                 {
1409                     if ( ++nSpaceIdx < m_pCurr->GetLLSpaceAddCount() )
1410                         nSpaceAdd = m_pCurr->GetLLSpaceAdd( nSpaceIdx );
1411                     else
1412                         nSpaceAdd = 0;
1413                 }
1414 
1415                 if ( pKanaComp )
1416                 {
1417                     if( nKanaIdx + 1 < pKanaComp->size() )
1418                         nKanaComp = (*pKanaComp)[++nKanaIdx];
1419                     else
1420                         nKanaComp = 0;
1421                 }
1422             }
1423         }
1424 
1425         if ( pPor->IsPostItsPortion() )
1426             nWidth30 = 0;
1427         else
1428             nWidth30 = ! nWidth && pPor->GetLen() && pPor->InToxRefOrFieldGrp() ?
1429                          30 :
1430                          nWidth;
1431         if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1432             bLastHyph = pPor->InHyphGrp();
1433     }
1434 
1435     const bool bLastPortion = (nullptr == pPor->GetNextPortion());
1436 
1437     if( nX==nWidth )
1438     {
1439         SwLinePortion *pNextPor = pPor->GetNextPortion();
1440         while( pNextPor && pNextPor->InFieldGrp() && !pNextPor->Width() )
1441         {
1442             nCurrStart = nCurrStart + pPor->GetLen();
1443             pPor = pNextPor;
1444             if( !pPor->IsFlyPortion() && !pPor->IsMarginPortion() )
1445                 bLastHyph = pPor->InHyphGrp();
1446             pNextPor = pPor->GetNextPortion();
1447         }
1448     }
1449 
1450     const_cast<SwTextSizeInfo&>(GetInfo()).SetIdx( nOldIdx );
1451 
1452     TextFrameIndex nLength = pPor->GetLen();
1453 
1454     const bool bFieldInfo = pCMS && pCMS->m_bFieldInfo;
1455 
1456     if( bFieldInfo && ( nWidth30 < nX || bRightOver || bLeftOver ||
1457         ( pPor->InNumberGrp() && !pPor->IsFootnoteNumPortion() ) ||
1458         ( pPor->IsMarginPortion() && nWidth > nX + 30 ) ) )
1459         pCMS->m_bPosCorr = true;
1460 
1461     // #i27615#
1462     if (pCMS && pCMS->m_bInFrontOfLabel)
1463     {
1464         if (2 * nX >= nWidth || !pPor->InNumberGrp() || pPor->IsFootnoteNumPortion())
1465             pCMS->m_bInFrontOfLabel = false;
1466     }
1467 
1468     // 7684: We are exactly ended up at their HyphPortion. It is our task to
1469     // provide, that we end up in the String.
1470     // 7993: If length = 0, then we must exit...
1471     if( !nLength )
1472     {
1473         if( pCMS )
1474         {
1475             if( pPor->IsFlyPortion() && bFieldInfo )
1476                 pCMS->m_bPosCorr = true;
1477 
1478             if (!bRightOver && nX)
1479             {
1480                 if( pPor->IsFootnoteNumPortion())
1481                     pCMS->m_bFootnoteNoInfo = true;
1482                 else if (pPor->InNumberGrp() ) // #i23726#
1483                 {
1484                     pCMS->m_nInNumPortionOffset = nX;
1485                     pCMS->m_bInNumPortion = true;
1486                 }
1487             }
1488         }
1489         if( !nCurrStart )
1490             return TextFrameIndex(0);
1491 
1492         // 7849, 7816: pPor->GetHyphPortion is mandatory!
1493         if( bHolePortion || ( !bRightAllowed && bLastHyph ) ||
1494             ( pPor->IsMarginPortion() && !pPor->GetNextPortion() &&
1495               // 46598: Consider the situation: We might end up behind the last character,
1496               // in the last line of a centered paragraph
1497               nCurrStart < TextFrameIndex(rText.getLength())))
1498             --nCurrStart;
1499         else if( pPor->InFieldGrp() && static_cast<SwFieldPortion*>(pPor)->IsFollow()
1500                  && nWidth > nX )
1501         {
1502             if( bFieldInfo )
1503                 --nCurrStart;
1504             else
1505             {
1506                 sal_uInt16 nHeight = pPor->Height();
1507                 if ( !nHeight || nHeight > nWidth )
1508                     nHeight = nWidth;
1509                 if( bChgNode && nWidth - nHeight/2 > nX )
1510                     --nCurrStart;
1511             }
1512         }
1513         return nCurrStart;
1514     }
1515     if (TextFrameIndex(1) == nLength || pPor->InFieldGrp())
1516     {
1517         if ( nWidth )
1518         {
1519             // no quick return for as-character frames, we want to peek inside
1520             if (!(bChgNode && pPos && pPor->IsFlyCntPortion())
1521             // if we want to get the position inside the field, we should not return
1522                 && (!pCMS || !pCMS->m_pSpecialPos))
1523             {
1524                 if ( pPor->InFieldGrp() ||
1525                      ( pPor->IsMultiPortion() &&
1526                        static_cast<SwMultiPortion*>(pPor)->IsBidi()  ) )
1527                 {
1528                     sal_uInt16 nHeight = 0;
1529                     if( !bFieldInfo )
1530                     {
1531                         nHeight = pPor->Height();
1532                         if ( !nHeight || nHeight > nWidth )
1533                             nHeight = nWidth;
1534                     }
1535 
1536                     if( nWidth - nHeight/2 <= nX &&
1537                         ( ! pPor->InFieldGrp() ||
1538                           !static_cast<SwFieldPortion*>(pPor)->HasFollow() ) )
1539                     {
1540                         if (pPor->InFieldGrp())
1541                         {
1542                             nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
1543                         }
1544                         else
1545                         {
1546                             ++nCurrStart;
1547                         }
1548                     }
1549                 }
1550                 else if ( ( !pPor->IsFlyPortion() || ( pPor->GetNextPortion() &&
1551                     !pPor->GetNextPortion()->IsMarginPortion() &&
1552                     !pPor->GetNextPortion()->IsHolePortion() ) )
1553                          && ( nWidth/2 < nX ) &&
1554                          ( !bFieldInfo ||
1555                             ( pPor->GetNextPortion() &&
1556                               pPor->GetNextPortion()->IsPostItsPortion() ) )
1557                          && ( bRightAllowed || !bLastHyph ))
1558                     ++nCurrStart;
1559 
1560                 return nCurrStart;
1561             }
1562         }
1563         else
1564         {
1565             if ( pPor->IsPostItsPortion() || pPor->IsBreakPortion() ||
1566                  pPor->InToxRefGrp() )
1567             {
1568                 SwPostItsPortion* pPostItsPortion = pPor->IsPostItsPortion() ? dynamic_cast<SwPostItsPortion*>(pPor) : nullptr;
1569                 if (pPostItsPortion)
1570                 {
1571                     if (!pPostItsPortion->IsScript()) // tdf#141079
1572                     {
1573                         // Offset would be nCurrStart + nLength below, do the same for post-it portions.
1574                         nCurrStart += pPor->GetLen();
1575                     }
1576                 }
1577                 return nCurrStart;
1578             }
1579             if ( pPor->InFieldGrp() )
1580             {
1581                 if( bRightOver && !static_cast<SwFieldPortion*>(pPor)->HasFollow() )
1582                 {
1583                     nCurrStart += static_cast<SwFieldPortion*>(pPor)->GetFieldLen();
1584                 }
1585                 return nCurrStart;
1586             }
1587         }
1588     }
1589 
1590     // Skip space at the end of the line
1591     if( bLastPortion && (m_pCurr->GetNext() || m_pFrame->GetFollow() )
1592         && rText[sal_Int32(nCurrStart + nLength) - 1] == ' ' )
1593         --nLength;
1594 
1595     if( nWidth > nX ||
1596       ( nWidth == nX && pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsDouble() ) )
1597     {
1598         if( pPor->IsMultiPortion() )
1599         {
1600             // In a multi-portion we use GetModelPositionForViewPoint()-function recursively
1601             SwTwips nTmpY = rPoint.Y() - m_pCurr->GetAscent() + pPor->GetAscent();
1602             // if we are in the first line of a double line portion, we have
1603             // to add a value to nTmpY for not staying in this line
1604             // we also want to skip the first line, if we are inside ruby
1605             if ( ( static_cast<SwTextSizeInfo*>(m_pInf)->IsMulti() &&
1606                    static_cast<SwTextSizeInfo*>(m_pInf)->IsFirstMulti() ) ||
1607                  ( static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1608                    static_cast<SwMultiPortion*>(pPor)->OnTop() ) )
1609                 nTmpY += static_cast<SwMultiPortion*>(pPor)->Height();
1610 
1611             // Important for cursor traveling in ruby portions:
1612             // We have to set nTmpY to 0 in order to stay in the first row
1613             // if the phonetic line is the second row
1614             if (   static_cast<SwMultiPortion*>(pPor)->IsRuby() &&
1615                  ! static_cast<SwMultiPortion*>(pPor)->OnTop() )
1616                 nTmpY = 0;
1617 
1618             SwTextCursorSave aSave( const_cast<SwTextCursor*>(this), static_cast<SwMultiPortion*>(pPor),
1619                  nTmpY, nX, nCurrStart, nSpaceAdd );
1620 
1621             SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1622             if ( static_cast<SwMultiPortion*>(pPor)->IsBidi() )
1623             {
1624                 const sal_uInt8 nBidiLevel = static_cast<SwBidiPortion*>(pPor)->GetLevel();
1625                 aLayoutModeModifier.Modify( nBidiLevel % 2 );
1626             }
1627 
1628             if( static_cast<SwMultiPortion*>(pPor)->HasRotation() )
1629             {
1630                 nTmpY -= m_nY;
1631                 if( !static_cast<SwMultiPortion*>(pPor)->IsRevers() )
1632                     nTmpY = pPor->Height() - nTmpY;
1633                 if( nTmpY < 0 )
1634                     nTmpY = 0;
1635                 nX = o3tl::narrowing<sal_uInt16>(nTmpY);
1636             }
1637 
1638             if( static_cast<SwMultiPortion*>(pPor)->HasBrackets() )
1639             {
1640                 const sal_uInt16 nPreWidth = static_cast<SwDoubleLinePortion*>(pPor)->PreWidth();
1641                 if ( nX > nPreWidth )
1642                     nX = nX - nPreWidth;
1643                 else
1644                     nX = 0;
1645             }
1646 
1647             return GetModelPositionForViewPoint( pPos, Point( GetLineStart() + nX, rPoint.Y() ),
1648                                 bChgNode, pCMS );
1649         }
1650         if( pPor->InTextGrp() )
1651         {
1652             sal_uInt8 nOldProp;
1653             if( GetPropFont() )
1654             {
1655                 const_cast<SwFont*>(GetFnt())->SetProportion( GetPropFont() );
1656                 nOldProp = GetFnt()->GetPropr();
1657             }
1658             else
1659                 nOldProp = 0;
1660             {
1661                 SwTextSizeInfo aSizeInf( GetInfo(), &rText, nCurrStart );
1662                 const_cast<SwTextCursor*>(this)->SeekAndChg( aSizeInf );
1663                 SwTextSlot aDiffText( &aSizeInf, static_cast<SwTextPortion*>(pPor), false, false );
1664                 SwFontSave aSave( aSizeInf, pPor->IsDropPortion() ?
1665                         static_cast<SwDropPortion*>(pPor)->GetFnt() : nullptr );
1666 
1667                 SwParaPortion* pPara = const_cast<SwParaPortion*>(GetInfo().GetParaPortion());
1668                 OSL_ENSURE( pPara, "No paragraph!" );
1669 
1670                 // protect against bugs elsewhere
1671                 SAL_WARN_IF( aSizeInf.GetIdx().get() + pPor->GetLen().get() > aSizeInf.GetText().getLength(), "sw", "portion and text are out of sync" );
1672                 TextFrameIndex nSafeLen( std::min(pPor->GetLen().get(), aSizeInf.GetText().getLength() - aSizeInf.GetIdx().get()) );
1673 
1674                 SwDrawTextInfo aDrawInf( aSizeInf.GetVsh(),
1675                                          *aSizeInf.GetOut(),
1676                                          &pPara->GetScriptInfo(),
1677                                          aSizeInf.GetText(),
1678                                          aSizeInf.GetIdx(),
1679                                          nSafeLen );
1680 
1681                 // Drop portion works like a multi portion, just its parts are not portions
1682                 if( pPor->IsDropPortion() && static_cast<SwDropPortion*>(pPor)->GetLines() > 1 )
1683                 {
1684                     SwDropPortion* pDrop = static_cast<SwDropPortion*>(pPor);
1685                     const SwDropPortionPart* pCurrPart = pDrop->GetPart();
1686                     sal_uInt16 nSumWidth = 0;
1687                     sal_uInt16 nSumBorderWidth = 0;
1688                     // Shift offset with the right and left border of previous parts and left border of actual one
1689                     while (pCurrPart && nSumWidth <= nX - sal_Int32(nCurrStart))
1690                     {
1691                         nSumWidth += pCurrPart->GetWidth();
1692                         if( pCurrPart->GetFont().GetLeftBorder() && !pCurrPart->GetJoinBorderWithPrev() )
1693                         {
1694                             nSumBorderWidth += pCurrPart->GetFont().GetLeftBorderSpace();
1695                         }
1696                         if (nSumWidth <= nX - sal_Int32(nCurrStart) && pCurrPart->GetFont().GetRightBorder() &&
1697                             !pCurrPart->GetJoinBorderWithNext() )
1698                         {
1699                             nSumBorderWidth += pCurrPart->GetFont().GetRightBorderSpace();
1700                         }
1701                         pCurrPart = pCurrPart->GetFollow();
1702                     }
1703                     nX = std::max(static_cast<SwTwips>(0), nX - nSumBorderWidth);
1704                 }
1705                 // Shift the offset with the left border width
1706                 else if( GetInfo().GetFont()->GetLeftBorder() && !pPor->GetJoinBorderWithPrev() )
1707                 {
1708                     nX = std::max(static_cast<SwTwips>(0), nX - GetInfo().GetFont()->GetLeftBorderSpace());
1709                 }
1710 
1711                 aDrawInf.SetOffset( nX );
1712 
1713                 if ( nSpaceAdd )
1714                 {
1715                     TextFrameIndex nCharCnt(0);
1716                     // #i41860# Thai justified alignment needs some
1717                     // additional information:
1718                     aDrawInf.SetNumberOfBlanks( pPor->InTextGrp() ?
1719                                                 static_cast<const SwTextPortion*>(pPor)->GetSpaceCnt( aSizeInf, nCharCnt ) :
1720                                                 TextFrameIndex(0) );
1721                 }
1722 
1723                 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1724                     aDrawInf.SetLen( TextFrameIndex(COMPLETE_STRING) );
1725 
1726                 aDrawInf.SetSpace( nSpaceAdd );
1727                 aDrawInf.SetFont( aSizeInf.GetFont() );
1728                 aDrawInf.SetFrame( m_pFrame );
1729                 aDrawInf.SetSnapToGrid( aSizeInf.SnapToGrid() );
1730                 aDrawInf.SetPosMatchesBounds( pCMS && pCMS->m_bPosMatchesBounds );
1731 
1732                 if ( SwFontScript::CJK == aSizeInf.GetFont()->GetActual() &&
1733                      pPara->GetScriptInfo().CountCompChg() &&
1734                     ! pPor->InFieldGrp() )
1735                     aDrawInf.SetKanaComp( nKanaComp );
1736 
1737                 nLength = aSizeInf.GetFont()->GetModelPositionForViewPoint_( aDrawInf );
1738 
1739                 // get position inside field portion?
1740                 if ( pPor->InFieldGrp() && pCMS && pCMS->m_pSpecialPos )
1741                 {
1742                     pCMS->m_pSpecialPos->nCharOfst = sal_Int32(nLength);
1743                     nLength = TextFrameIndex(0);
1744                 }
1745 
1746                 // set cursor bidi level
1747                 if ( pCMS )
1748                     pCMS->m_nCursorBidiLevel =
1749                         aDrawInf.GetCursorBidiLevel();
1750 
1751                 if( bFieldInfo && nLength == pPor->GetLen() &&
1752                     ( ! pPor->GetNextPortion() ||
1753                       ! pPor->GetNextPortion()->IsPostItsPortion() ) )
1754                     --nLength;
1755             }
1756             if( nOldProp )
1757                 const_cast<SwFont*>(GetFnt())->SetProportion( nOldProp );
1758         }
1759         else
1760         {
1761             sw::FlyContentPortion* pFlyPor(nullptr);
1762             if(bChgNode && pPos && (pFlyPor = dynamic_cast<sw::FlyContentPortion*>(pPor)))
1763             {
1764                 // JP 24.11.94: if the Position is not in Fly, then
1765                 //              we many not return with COMPLETE_STRING as value!
1766                 //              (BugId: 9692 + Change in feshview)
1767                 SwFlyInContentFrame *pTmp = pFlyPor->GetFlyFrame();
1768                 SwFrame* pLower = pTmp->GetLower();
1769                 bool bChgNodeInner = pLower
1770                     && (pLower->IsTextFrame() || pLower->IsLayoutFrame());
1771                 Point aTmpPoint( rPoint );
1772 
1773                 if ( m_pFrame->IsRightToLeft() )
1774                     m_pFrame->SwitchLTRtoRTL( aTmpPoint );
1775 
1776                 if ( m_pFrame->IsVertical() )
1777                     m_pFrame->SwitchHorizontalToVertical( aTmpPoint );
1778 
1779                 if( bChgNodeInner && pTmp->getFrameArea().Contains( aTmpPoint ) &&
1780                     !( pTmp->IsProtected() ) )
1781                 {
1782                     pFlyPor->GetFlyCursorOfst(aTmpPoint, *pPos, pCMS);
1783                     // After a change of the frame, our font must be still
1784                     // available for/in the OutputDevice.
1785                     // For comparison: Paint and new SwFlyCntPortion !
1786                     static_cast<SwTextSizeInfo*>(m_pInf)->SelectFont();
1787 
1788                     // 6776: The pIter->GetModelPositionForViewPoint is returning here
1789                     // from a nesting with COMPLETE_STRING.
1790                     return TextFrameIndex(COMPLETE_STRING);
1791                 }
1792             }
1793             else
1794                 nLength = pPor->GetModelPositionForViewPoint( nX );
1795         }
1796     }
1797     nOffset = nCurrStart + nLength;
1798 
1799     // 7684: We end up in front of the HyphPortion. We must assure
1800     // that we end up in the string.
1801     // If we are at end of line in front of FlyFrames, we must proceed the same way.
1802     if( nOffset && pPor->GetLen() == nLength && pPor->GetNextPortion() &&
1803         !pPor->GetNextPortion()->GetLen() && pPor->GetNextPortion()->InHyphGrp() )
1804         --nOffset;
1805 
1806     return nOffset;
1807 }
1808 
1809 /** Looks for text portions which are inside the given rectangle
1810 
1811     For a rectangular text selection every text portions which is inside the given
1812     rectangle has to be put into the SwSelectionList as SwPaM
1813     From these SwPaM the SwCursors will be created.
1814 
1815     @param rSelList
1816     The container for the overlapped text portions
1817 
1818     @param rRect
1819     A rectangle in document coordinates, text inside this rectangle has to be
1820     selected.
1821 
1822     @return [ true, false ]
1823     true if any overlapping text portion has been found and put into list
1824     false if no portion overlaps, the list has been unchanged
1825 */
1826 bool SwTextFrame::FillSelection( SwSelectionList& rSelList, const SwRect& rRect ) const
1827 {
1828     bool bRet = false;
1829     // GetPaintArea() instead getFrameArea() for negative indents
1830     SwRect aTmpFrame( GetPaintArea() );
1831     if( !rRect.Overlaps( aTmpFrame ) )
1832         return false;
1833     if( rSelList.checkContext( this ) )
1834     {
1835         SwRect aRect( aTmpFrame );
1836         aRect.Intersection( rRect );
1837         SwPosition aPosL( MapViewToModelPos(TextFrameIndex(0)) );
1838         if( IsEmpty() )
1839         {
1840             SwPaM *pPam = new SwPaM( aPosL, aPosL );
1841             rSelList.insertPaM( pPam );
1842         }
1843         else if( aRect.HasArea() )
1844         {
1845             SwPosition aOld(aPosL.nNode.GetNodes().GetEndOfContent());
1846             SwPosition aPosR( aPosL );
1847             Point aPoint;
1848             SwTextInfo aInf( const_cast<SwTextFrame*>(this) );
1849             SwTextIter aLine( const_cast<SwTextFrame*>(this), &aInf );
1850             // We have to care for top-to-bottom layout, where right becomes top etc.
1851             SwRectFnSet aRectFnSet(this);
1852             SwTwips nTop = aRectFnSet.GetTop(aRect);
1853             SwTwips nBottom = aRectFnSet.GetBottom(aRect);
1854             SwTwips nLeft = aRectFnSet.GetLeft(aRect);
1855             SwTwips nRight = aRectFnSet.GetRight(aRect);
1856             SwTwips nY = aLine.Y(); // Top position of the first line
1857             SwTwips nLastY = nY;
1858             while( nY < nTop && aLine.Next() ) // line above rectangle
1859             {
1860                 nLastY = nY;
1861                 nY = aLine.Y();
1862             }
1863             bool bLastLine = false;
1864             if( nY < nTop && !aLine.GetNext() )
1865             {
1866                 bLastLine = true;
1867                 nY += aLine.GetLineHeight();
1868             }
1869             do // check the lines for overlapping
1870             {
1871                 if( nLastY < nTop ) // if the last line was above rectangle
1872                     nLastY = nTop;
1873                 if( nY > nBottom ) // if the current line leaves the rectangle
1874                     nY = nBottom;
1875                 if( nY >= nLastY ) // gotcha: overlapping
1876                 {
1877                     nLastY += nY;
1878                     nLastY /= 2;
1879                     if( aRectFnSet.IsVert() )
1880                     {
1881                         aPoint.setX( nLastY );
1882                         aPoint.setY( nLeft );
1883                     }
1884                     else
1885                     {
1886                         aPoint.setX( nLeft );
1887                         aPoint.setY( nLastY );
1888                     }
1889                     // Looking for the position of the left border of the rectangle
1890                     // in this text line
1891                     SwCursorMoveState aState( CursorMoveState::UpDown );
1892                     if( GetModelPositionForViewPoint( &aPosL, aPoint, &aState ) )
1893                     {
1894                         if( aRectFnSet.IsVert() )
1895                         {
1896                             aPoint.setX( nLastY );
1897                             aPoint.setY( nRight );
1898                         }
1899                         else
1900                         {
1901                             aPoint.setX( nRight );
1902                             aPoint.setY( nLastY );
1903                         }
1904                         // If we get a right position and if the left position
1905                         // is not the same like the left position of the line before
1906                         // which could happen e.g. for field portions or fly frames
1907                         // a SwPaM will be inserted with these positions
1908                         if( GetModelPositionForViewPoint( &aPosR, aPoint, &aState ) &&
1909                             aOld != aPosL)
1910                         {
1911                             SwPaM *pPam = new SwPaM( aPosL, aPosR );
1912                             rSelList.insertPaM( pPam );
1913                             aOld = aPosL;
1914                         }
1915                     }
1916                 }
1917                 if( aLine.Next() )
1918                 {
1919                     nLastY = nY;
1920                     nY = aLine.Y();
1921                 }
1922                 else if( !bLastLine )
1923                 {
1924                     bLastLine = true;
1925                     nLastY = nY;
1926                     nY += aLine.GetLineHeight();
1927                 }
1928                 else
1929                     break;
1930             }while( nLastY < nBottom );
1931         }
1932     }
1933     if( GetDrawObjs() )
1934     {
1935         const SwSortedObjs &rObjs = *GetDrawObjs();
1936         for (SwAnchoredObject* pAnchoredObj : rObjs)
1937         {
1938             const SwFlyFrame* pFly = pAnchoredObj->DynCastFlyFrame();
1939             if( !pFly )
1940                 continue;
1941             if( pFly->IsFlyInContentFrame() && pFly->FillSelection( rSelList, rRect ) )
1942                 bRet = true;
1943         }
1944     }
1945     return bRet;
1946 }
1947 
1948 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1949