1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <o3tl/safeint.hxx>
24 #include <IDocumentSettingAccess.hxx>
28 #include "porglue.hxx"
31 #include "pormulti.hxx"
35 #define MIN_TAB_WIDTH 60
37 using namespace ::com::sun::star
;
39 void SwTextAdjuster::FormatBlock( )
41 // Block format does not apply to the last line.
42 // And for tabs it doesn't exist out of tradition
43 // If we have Flys we continue.
45 const SwLinePortion
*pFly
= nullptr;
47 bool bSkip
= !IsLastBlock() &&
48 m_nStart
+ m_pCurr
->GetLen() >= TextFrameIndex(GetInfo().GetText().getLength());
50 // Multi-line fields are tricky, because we need to check whether there are
51 // any other text portions in the paragraph.
54 const SwLineLayout
*pLay
= m_pCurr
->GetNext();
55 while( pLay
&& !pLay
->GetLen() )
57 const SwLinePortion
*pPor
= m_pCurr
->GetFirstPortion();
58 while( pPor
&& bSkip
)
60 if( pPor
->InTextGrp() )
62 pPor
= pPor
->GetNextPortion();
64 pLay
= bSkip
? pLay
->GetNext() : nullptr;
70 if( !GetInfo().GetParaPortion()->HasFly() )
73 CalcFlyAdjust( m_pCurr
);
74 m_pCurr
->FinishSpaceAdd();
79 const SwLinePortion
*pTmpFly
= nullptr;
81 // End at the last Fly
82 const SwLinePortion
*pPos
= m_pCurr
->GetFirstPortion();
85 // Look for the last Fly which has text coming after it:
86 if( pPos
->IsFlyPortion() )
87 pTmpFly
= pPos
; // Found a Fly
88 else if ( pTmpFly
&& pPos
->InTextGrp() )
90 pFly
= pTmpFly
; // A Fly with follow-up text!
93 pPos
= pPos
->GetNextPortion();
95 // End if we didn't find one
99 CalcFlyAdjust( m_pCurr
);
100 m_pCurr
->FinishSpaceAdd();
106 const TextFrameIndex nOldIdx
= GetInfo().GetIdx();
107 GetInfo().SetIdx( m_nStart
);
108 CalcNewBlock( m_pCurr
, pFly
);
109 GetInfo().SetIdx( nOldIdx
);
110 GetInfo().GetParaPortion()->GetRepaint().SetOffset(0);
113 static bool lcl_CheckKashidaPositions( SwScriptInfo
& rSI
, SwTextSizeInfo
& rInf
, SwTextIter
& rItr
,
114 sal_Int32
& rKashidas
, TextFrameIndex
& nGluePortion
)
116 // i60594 validate Kashida justification
117 TextFrameIndex nIdx
= rItr
.GetStart();
118 TextFrameIndex nEnd
= rItr
.GetEnd();
120 // Note on calling KashidaJustify():
121 // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
122 // total number of kashida positions, or the number of kashida positions after some positions
123 // have been dropped.
124 // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
125 rKashidas
= rSI
.KashidaJustify(nullptr, nullptr, rItr
.GetStart(), rItr
.GetLength());
127 if (rKashidas
<= 0) // nothing to do
130 // kashida positions found in SwScriptInfo are not necessarily valid in every font
131 // if two characters are replaced by a ligature glyph, there will be no place for a kashida
132 std::vector
<TextFrameIndex
> aKashidaPos
;
133 rSI
.GetKashidaPositions(nIdx
, rItr
.GetLength(), aKashidaPos
);
134 assert(aKashidaPos
.size() >= o3tl::make_unsigned(rKashidas
));
135 std::vector
<TextFrameIndex
> aKashidaPosDropped(aKashidaPos
.size());
136 sal_Int32 nKashidaIdx
= 0;
137 while ( rKashidas
&& nIdx
< nEnd
)
139 rItr
.SeekAndChgAttrIter( nIdx
, rInf
.GetOut() );
140 TextFrameIndex nNext
= rItr
.GetNextAttr();
142 // is there also a script change before?
143 // if there is, nNext should point to the script change
144 TextFrameIndex
const nNextScript
= rSI
.NextScriptChg( nIdx
);
145 if( nNextScript
< nNext
)
148 if (nNext
== TextFrameIndex(COMPLETE_STRING
) || nNext
> nEnd
)
150 sal_Int32 nKashidasInAttr
= rSI
.KashidaJustify(nullptr, nullptr, nIdx
, nNext
- nIdx
);
151 if (nKashidasInAttr
> 0)
153 // Kashida glyph looks suspicious, skip Kashida justification
154 if ( rInf
.GetOut()->GetMinKashida() <= 0 )
159 sal_Int32 nKashidasDropped
= 0;
160 if ( !SwScriptInfo::IsArabicText( rInf
.GetText(), nIdx
, nNext
- nIdx
) )
162 nKashidasDropped
= nKashidasInAttr
;
163 rKashidas
-= nKashidasDropped
;
167 vcl::text::ComplexTextLayoutFlags nOldLayout
= rInf
.GetOut()->GetLayoutMode();
168 rInf
.GetOut()->SetLayoutMode ( nOldLayout
| vcl::text::ComplexTextLayoutFlags::BiDiRtl
);
169 nKashidasDropped
= rInf
.GetOut()->ValidateKashidas(
170 rInf
.GetText(), sal_Int32(nIdx
), sal_Int32(nNext
- nIdx
),
172 reinterpret_cast<sal_Int32
*>(aKashidaPos
.data() + nKashidaIdx
),
173 reinterpret_cast<sal_Int32
*>(aKashidaPosDropped
.data()));
174 rInf
.GetOut()->SetLayoutMode ( nOldLayout
);
175 if ( nKashidasDropped
)
177 rSI
.MarkKashidasInvalid(nKashidasDropped
, aKashidaPosDropped
.data());
178 rKashidas
-= nKashidasDropped
;
179 nGluePortion
-= TextFrameIndex(nKashidasDropped
);
182 nKashidaIdx
+= nKashidasInAttr
;
187 // return false if all kashidas have been eliminated
188 return (rKashidas
> 0);
191 static bool lcl_CheckKashidaWidth ( SwScriptInfo
& rSI
, SwTextSizeInfo
& rInf
, SwTextIter
& rItr
, sal_Int32
& rKashidas
,
192 TextFrameIndex
& nGluePortion
, const tools::Long nGluePortionWidth
, tools::Long
& nSpaceAdd
)
194 // check kashida width
195 // if width is smaller than minimal kashida width allowed by fonts in the current line
196 // drop one kashida after the other until kashida width is OK
199 bool bAddSpaceChanged
= false;
200 TextFrameIndex nIdx
= rItr
.GetStart();
201 TextFrameIndex nEnd
= rItr
.GetEnd();
202 while ( nIdx
< nEnd
)
204 rItr
.SeekAndChgAttrIter( nIdx
, rInf
.GetOut() );
205 TextFrameIndex nNext
= rItr
.GetNextAttr();
207 // is there also a script change before?
208 // if there is, nNext should point to the script change
209 TextFrameIndex
const nNextScript
= rSI
.NextScriptChg( nIdx
);
210 if( nNextScript
< nNext
)
213 if (nNext
== TextFrameIndex(COMPLETE_STRING
) || nNext
> nEnd
)
215 sal_Int32 nKashidasInAttr
= rSI
.KashidaJustify(nullptr, nullptr, nIdx
, nNext
- nIdx
);
217 tools::Long nFontMinKashida
= rInf
.GetOut()->GetMinKashida();
218 if ( nFontMinKashida
&& nKashidasInAttr
> 0 && SwScriptInfo::IsArabicText( rInf
.GetText(), nIdx
, nNext
- nIdx
) )
220 sal_Int32 nKashidasDropped
= 0;
221 while ( rKashidas
&& nGluePortion
&& nKashidasInAttr
> 0 &&
222 nSpaceAdd
/ SPACING_PRECISION_FACTOR
< nFontMinKashida
)
228 if( !rKashidas
|| !nGluePortion
) // nothing left, return false to
229 return false; // do regular blank justification
231 nSpaceAdd
= nGluePortionWidth
/ sal_Int32(nGluePortion
);
232 bAddSpaceChanged
= true;
234 if( nKashidasDropped
)
235 rSI
.MarkKashidasInvalid( nKashidasDropped
, nIdx
, nNext
- nIdx
);
237 if ( bAddSpaceChanged
)
238 break; // start all over again
241 if ( !bAddSpaceChanged
)
242 break; // everything was OK
247 // CalcNewBlock() must only be called _after_ CalcLine()!
248 // We always span between two RandPortions or FixPortions (Tabs and Flys).
249 // We count the Glues and call ExpandBlock.
250 void SwTextAdjuster::CalcNewBlock( SwLineLayout
*pCurrent
,
251 const SwLinePortion
*pStopAt
, SwTwips nReal
, bool bSkipKashida
)
253 OSL_ENSURE( GetInfo().IsMulti() || SvxAdjust::Block
== GetAdjust(),
254 "CalcNewBlock: Why?" );
255 OSL_ENSURE( pCurrent
->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
257 pCurrent
->InitSpaceAdd();
258 TextFrameIndex
nGluePortion(0);
259 TextFrameIndex
nCharCnt(0);
260 sal_uInt16 nSpaceIdx
= 0;
262 // i60591: hennerdrews
263 SwScriptInfo
& rSI
= GetInfo().GetParaPortion()->GetScriptInfo();
264 SwTextSizeInfo
aInf ( GetTextFrame() );
265 SwTextIter
aItr ( GetTextFrame(), &aInf
);
267 if ( rSI
.CountKashida() )
269 while (aItr
.GetCurr() != pCurrent
&& aItr
.GetNext())
274 rSI
.SetNoKashidaLine ( aItr
.GetStart(), aItr
.GetLength());
278 rSI
.ClearKashidaInvalid ( aItr
.GetStart(), aItr
.GetLength() );
279 rSI
.ClearNoKashidaLine( aItr
.GetStart(), aItr
.GetLength() );
283 // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
285 CalcRightMargin( pCurrent
, nReal
);
288 const bool bDoNotJustifyLinesWithManualBreak
=
289 GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK
);
290 bool bDoNotJustifyTab
= false;
292 SwLinePortion
*pPos
= pCurrent
->GetNextPortion();
296 if ( ( bDoNotJustifyLinesWithManualBreak
|| bDoNotJustifyTab
) &&
297 pPos
->IsBreakPortion() && !IsLastBlock() )
299 pCurrent
->FinishSpaceAdd();
303 switch ( pPos
->GetWhichPor() )
305 case PortionType::TabCenter
:
306 case PortionType::TabRight
:
307 case PortionType::TabDecimal
:
308 bDoNotJustifyTab
= true;
310 case PortionType::TabLeft
:
311 case PortionType::Break
:
312 bDoNotJustifyTab
= false;
317 if ( pPos
->InTextGrp() )
318 nGluePortion
= nGluePortion
+ static_cast<SwTextPortion
*>(pPos
)->GetSpaceCnt( GetInfo(), nCharCnt
);
319 else if( pPos
->IsMultiPortion() )
321 SwMultiPortion
* pMulti
= static_cast<SwMultiPortion
*>(pPos
);
322 // a multiportion with a tabulator inside breaks the text adjustment
323 // a ruby portion will not be stretched by text adjustment
324 // a double line portion takes additional space for each blank
326 if( pMulti
->HasTabulator() )
328 if ( nSpaceIdx
== pCurrent
->GetLLSpaceAddCount() )
329 pCurrent
->SetLLSpaceAdd( 0, nSpaceIdx
);
332 nGluePortion
= TextFrameIndex(0);
333 nCharCnt
= TextFrameIndex(0);
335 else if( pMulti
->IsDouble() )
336 nGluePortion
= nGluePortion
+ static_cast<SwDoubleLinePortion
*>(pMulti
)->GetSpaceCnt();
337 else if ( pMulti
->IsBidi() )
338 nGluePortion
= nGluePortion
+ static_cast<SwBidiPortion
*>(pMulti
)->GetSpaceCnt( GetInfo() ); // i60594
341 if( pPos
->InGlueGrp() )
343 if( pPos
->InFixMargGrp() )
345 if ( nSpaceIdx
== pCurrent
->GetLLSpaceAddCount() )
346 pCurrent
->SetLLSpaceAdd( 0, nSpaceIdx
);
348 const tools::Long nGluePortionWidth
= static_cast<SwGluePortion
*>(pPos
)->GetPrtGlue() *
349 SPACING_PRECISION_FACTOR
;
351 sal_Int32 nKashidas
= 0;
352 if( nGluePortion
&& rSI
.CountKashida() && !bSkipKashida
)
354 // kashida positions found in SwScriptInfo are not necessarily valid in every font
355 // if two characters are replaced by a ligature glyph, there will be no place for a kashida
356 if ( !lcl_CheckKashidaPositions ( rSI
, aInf
, aItr
, nKashidas
, nGluePortion
))
358 // all kashida positions are invalid
359 // do regular blank justification
360 pCurrent
->FinishSpaceAdd();
361 GetInfo().SetIdx( m_nStart
);
362 CalcNewBlock( pCurrent
, pStopAt
, nReal
, true );
369 tools::Long nSpaceAdd
= nGluePortionWidth
/ sal_Int32(nGluePortion
);
372 if( rSI
.CountKashida() && !bSkipKashida
)
374 if( !lcl_CheckKashidaWidth( rSI
, aInf
, aItr
, nKashidas
, nGluePortion
, nGluePortionWidth
, nSpaceAdd
))
377 // do regular blank justification
378 pCurrent
->FinishSpaceAdd();
379 GetInfo().SetIdx( m_nStart
);
380 CalcNewBlock( pCurrent
, pStopAt
, nReal
, true );
385 pCurrent
->SetLLSpaceAdd( nSpaceAdd
, nSpaceIdx
);
386 pPos
->Width( static_cast<SwGluePortion
*>(pPos
)->GetFixWidth() );
388 else if (IsOneBlock() && nCharCnt
> TextFrameIndex(1))
390 const tools::Long nSpaceAdd
= - nGluePortionWidth
/ (sal_Int32(nCharCnt
) - 1);
391 pCurrent
->SetLLSpaceAdd( nSpaceAdd
, nSpaceIdx
);
392 pPos
->Width( static_cast<SwGluePortion
*>(pPos
)->GetFixWidth() );
396 nGluePortion
= TextFrameIndex(0);
397 nCharCnt
= TextFrameIndex(0);
402 GetInfo().SetIdx( GetInfo().GetIdx() + pPos
->GetLen() );
403 if ( pPos
== pStopAt
)
405 pCurrent
->SetLLSpaceAdd( 0, nSpaceIdx
);
408 pPos
= pPos
->GetNextPortion();
412 SwTwips
SwTextAdjuster::CalcKanaAdj( SwLineLayout
* pCurrent
)
414 OSL_ENSURE( pCurrent
->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
415 OSL_ENSURE( !pCurrent
->GetpKanaComp(), "pKanaComp already exists!!" );
417 pCurrent
->SetKanaComp( std::make_unique
<std::deque
<sal_uInt16
>>() );
419 const sal_uInt16 nNull
= 0;
421 tools::Long nKanaDiffSum
= 0;
422 SwTwips nRepaintOfst
= 0;
424 bool bNoCompression
= false;
426 // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
427 CalcRightMargin( pCurrent
);
429 SwLinePortion
* pPos
= pCurrent
->GetNextPortion();
433 if ( pPos
->InTextGrp() )
435 // get maximum portion width from info structure, calculated
436 // during text formatting
437 sal_uInt16 nMaxWidthDiff
= GetInfo().GetMaxWidthDiff( pPos
);
439 // check, if information is stored under other key
440 if ( !nMaxWidthDiff
&& pPos
== pCurrent
->GetFirstPortion() )
441 nMaxWidthDiff
= GetInfo().GetMaxWidthDiff( pCurrent
);
443 // calculate difference between portion width and max. width
444 nKanaDiffSum
+= nMaxWidthDiff
;
446 // we store the beginning of the first compressible portion
448 if ( nMaxWidthDiff
&& !nRepaintOfst
)
449 nRepaintOfst
= nX
+ GetLeftMargin();
451 else if( pPos
->InGlueGrp() && pPos
->InFixMargGrp() )
453 if ( nKanaIdx
== pCurrent
->GetKanaComp().size() )
454 pCurrent
->GetKanaComp().push_back( nNull
);
458 if ( pPos
->InTabGrp() )
460 nRest
= ! bNoCompression
&&
461 ( pPos
->Width() > MIN_TAB_WIDTH
) ?
462 pPos
->Width() - MIN_TAB_WIDTH
:
465 // for simplifying the handling of left, right ... tabs,
466 // we do expand portions, which are lying behind
467 // those special tabs
468 bNoCompression
= !pPos
->IsTabLeftPortion();
472 nRest
= ! bNoCompression
?
473 static_cast<SwGluePortion
*>(pPos
)->GetPrtGlue() :
476 bNoCompression
= false;
481 sal_uLong nCompress
= ( 10000 * nRest
) / nKanaDiffSum
;
483 if ( nCompress
>= 10000 )
484 // kanas can be expanded to 100%, and there is still
485 // some space remaining
489 nCompress
= 10000 - nCompress
;
491 ( pCurrent
->GetKanaComp() )[ nKanaIdx
] = o3tl::narrowing
<sal_uInt16
>(nCompress
);
499 pPos
= pPos
->GetNextPortion();
504 sal_uInt16 nCompress
= ( pCurrent
->GetKanaComp() )[ nKanaIdx
];
505 pPos
= pCurrent
->GetNextPortion();
506 tools::Long nDecompress
= 0;
510 if ( pPos
->InTextGrp() )
512 const SwTwips nMinWidth
= pPos
->Width();
514 // get maximum portion width from info structure, calculated
515 // during text formatting
516 SwTwips nMaxWidthDiff
= GetInfo().GetMaxWidthDiff( pPos
);
518 // check, if information is stored under other key
519 if ( !nMaxWidthDiff
&& pPos
== pCurrent
->GetFirstPortion() )
520 nMaxWidthDiff
= GetInfo().GetMaxWidthDiff( pCurrent
);
521 pPos
->Width( nMinWidth
+
522 ( ( 10000 - nCompress
) * nMaxWidthDiff
) / 10000 );
523 nDecompress
+= pPos
->Width() - nMinWidth
;
525 else if( pPos
->InGlueGrp() && pPos
->InFixMargGrp() )
527 pPos
->Width(pPos
->Width() - nDecompress
);
529 if ( pPos
->InTabGrp() )
530 // set fix width to width
531 static_cast<SwTabPortion
*>(pPos
)->SetFixWidth( pPos
->Width() );
533 if ( ++nKanaIdx
< pCurrent
->GetKanaComp().size() )
534 nCompress
= ( pCurrent
->GetKanaComp() )[ nKanaIdx
];
538 pPos
= pPos
->GetNextPortion();
544 SwMarginPortion
*SwTextAdjuster::CalcRightMargin( SwLineLayout
*pCurrent
,
547 tools::Long nRealWidth
;
548 const sal_uInt16 nRealHeight
= GetLineHeight();
549 const sal_uInt16 nLineHeight
= pCurrent
->Height();
551 sal_uInt16 nPrtWidth
= pCurrent
->PrtWidth();
552 SwLinePortion
*pLast
= pCurrent
->FindLastPortion();
554 if( GetInfo().IsMulti() )
558 nRealWidth
= GetLineWidth();
559 // For each FlyFrame extending into the right margin, we create a FlyPortion.
560 const tools::Long nLeftMar
= GetLeftMargin();
561 SwRect
aCurrRect( nLeftMar
+ nPrtWidth
, Y() + nRealHeight
- nLineHeight
,
562 nRealWidth
- nPrtWidth
, nLineHeight
);
564 SwFlyPortion
*pFly
= CalcFlyPortion( nRealWidth
, aCurrRect
);
565 while( pFly
&& tools::Long( nPrtWidth
)< nRealWidth
)
567 pLast
->Append( pFly
);
569 if( pFly
->GetFix() > nPrtWidth
)
570 pFly
->Width( ( pFly
->GetFix() - nPrtWidth
) + pFly
->Width() + 1);
571 nPrtWidth
+= pFly
->Width() + 1;
572 aCurrRect
.Left( nLeftMar
+ nPrtWidth
);
573 pFly
= CalcFlyPortion( nRealWidth
, aCurrRect
);
578 SwMarginPortion
*pRight
= new SwMarginPortion
;
579 pLast
->Append( pRight
);
581 if( tools::Long( nPrtWidth
)< nRealWidth
)
582 pRight
->PrtWidth( sal_uInt16( nRealWidth
- nPrtWidth
) );
584 // pCurrent->Width() is set to the real size, because we attach the
586 // This trick gives miraculous results:
587 // If pCurrent->Width() == nRealWidth, then the adjustment gets overruled
588 // implicitly. GetLeftMarginAdjust() and IsJustified() think they have a
589 // line filled with chars.
591 pCurrent
->PrtWidth( sal_uInt16( nRealWidth
) );
595 void SwTextAdjuster::CalcFlyAdjust( SwLineLayout
*pCurrent
)
597 // 1) We insert a left margin:
598 SwMarginPortion
*pLeft
= pCurrent
->CalcLeftMargin();
599 SwGluePortion
*pGlue
= pLeft
; // the last GluePortion
601 // 2) We attach a right margin:
602 // CalcRightMargin also calculates a possible overlap with FlyFrames.
603 CalcRightMargin( pCurrent
);
605 SwLinePortion
*pPos
= pLeft
->GetNextPortion();
606 TextFrameIndex
nLen(0);
608 // If we only have one line, the text portion is consecutive and we center, then ...
609 bool bComplete
= TextFrameIndex(0) == m_nStart
;
610 const bool bTabCompat
= GetTextFrame()->GetDoc().getIDocumentSettingAccess().get(DocumentSettingId::TAB_COMPAT
);
611 bool bMultiTab
= false;
615 if ( pPos
->IsMultiPortion() && static_cast<SwMultiPortion
*>(pPos
)->HasTabulator() )
617 else if( pPos
->InFixMargGrp() &&
618 ( bTabCompat
? ! pPos
->InTabGrp() : ! bMultiTab
) )
620 // in tab compat mode we do not want to change tab portions
621 // in non tab compat mode we do not want to change margins if we
622 // found a multi portion with tabs
623 if( SvxAdjust::Right
== GetAdjust() )
624 static_cast<SwGluePortion
*>(pPos
)->MoveAllGlue( pGlue
);
627 // We set the first text portion to right-aligned and the last one
629 // The first text portion gets the whole Glue, but only if we have
630 // more than one line.
631 if (bComplete
&& TextFrameIndex(GetInfo().GetText().getLength()) == nLen
)
632 static_cast<SwGluePortion
*>(pPos
)->MoveHalfGlue( pGlue
);
639 // If we only have a left and right margin, the
640 // margins share the Glue.
641 if( nLen
+ pPos
->GetLen() >= pCurrent
->GetLen() )
642 static_cast<SwGluePortion
*>(pPos
)->MoveHalfGlue( pGlue
);
644 static_cast<SwGluePortion
*>(pPos
)->MoveAllGlue( pGlue
);
648 // The last text portion retains its Glue.
649 if( !pPos
->IsMarginPortion() )
650 static_cast<SwGluePortion
*>(pPos
)->MoveHalfGlue( pGlue
);
654 static_cast<SwGluePortion
*>(pPos
)->MoveHalfGlue( pGlue
);
658 pGlue
= static_cast<SwGluePortion
*>(pPos
);
661 nLen
= nLen
+ pPos
->GetLen();
662 pPos
= pPos
->GetNextPortion();
665 if( ! bTabCompat
&& ! bMultiTab
&& SvxAdjust::Right
== GetAdjust() )
666 // portions are moved to the right if possible
667 pLeft
->AdjustRight( pCurrent
);
670 void SwTextAdjuster::CalcAdjLine( SwLineLayout
*pCurrent
)
672 OSL_ENSURE( pCurrent
->IsFormatAdj(), "CalcAdjLine: Why?" );
674 pCurrent
->SetFormatAdj(false);
676 SwParaPortion
* pPara
= GetInfo().GetParaPortion();
678 switch( GetAdjust() )
680 case SvxAdjust::Right
:
681 case SvxAdjust::Center
:
683 CalcFlyAdjust( pCurrent
);
684 pPara
->GetRepaint().SetOffset( 0 );
687 case SvxAdjust::Block
:
696 // This is a quite complicated calculation: nCurrWidth is the width _before_
697 // adding the word, that still fits onto the line! For this reason the FlyPortion's
698 // width is still correct if we get a deadlock-situation of:
699 // bFirstWord && !WORDFITS
700 SwFlyPortion
*SwTextAdjuster::CalcFlyPortion( const tools::Long nRealWidth
,
701 const SwRect
&rCurrRect
)
703 SwTextFly
aTextFly( GetTextFrame() );
705 const sal_uInt16 nCurrWidth
= m_pCurr
->PrtWidth();
706 SwFlyPortion
*pFlyPortion
= nullptr;
708 SwRect
aLineVert( rCurrRect
);
709 if ( GetTextFrame()->IsRightToLeft() )
710 GetTextFrame()->SwitchLTRtoRTL( aLineVert
);
711 if ( GetTextFrame()->IsVertical() )
712 GetTextFrame()->SwitchHorizontalToVertical( aLineVert
);
714 // aFlyRect is document-global!
715 SwRect
aFlyRect( aTextFly
.GetFrame( aLineVert
) );
717 if ( GetTextFrame()->IsRightToLeft() )
718 GetTextFrame()->SwitchRTLtoLTR( aFlyRect
);
719 if ( GetTextFrame()->IsVertical() )
720 GetTextFrame()->SwitchVerticalToHorizontal( aFlyRect
);
722 // If a Frame overlapps we open a Portion
723 if( aFlyRect
.HasArea() )
725 // aLocal is frame-local
726 SwRect
aLocal( aFlyRect
);
727 aLocal
.Pos( aLocal
.Left() - GetLeftMargin(), aLocal
.Top() );
728 if( nCurrWidth
> aLocal
.Left() )
729 aLocal
.Left( nCurrWidth
);
731 // If the rect is wider than the line, we adjust it to the right size
732 const tools::Long nLocalWidth
= aLocal
.Left() + aLocal
.Width();
733 if( nRealWidth
< nLocalWidth
)
734 aLocal
.Width( nRealWidth
- aLocal
.Left() );
735 GetInfo().GetParaPortion()->SetFly();
736 pFlyPortion
= new SwFlyPortion( aLocal
);
737 pFlyPortion
->Height( sal_uInt16( rCurrRect
.Height() ) );
738 // The Width could be smaller than the FixWidth, thus:
739 pFlyPortion
->AdjFixWidth();
744 // CalcDropAdjust is called at the end by Format() if needed
745 void SwTextAdjuster::CalcDropAdjust()
747 OSL_ENSURE( 1<GetDropLines() && SvxAdjust::Left
!=GetAdjust() && SvxAdjust::Block
!=GetAdjust(),
748 "CalcDropAdjust: No reason for DropAdjustment." );
750 const sal_Int32 nLineNumber
= GetLineNr();
755 if( !m_pCurr
->IsDummy() || NextLine() )
760 SwLinePortion
*pPor
= m_pCurr
->GetFirstPortion();
762 // 2) Make sure we include the ropPortion
763 // 3) pLeft is the GluePor preceding the DropPor
764 if( pPor
->InGlueGrp() && pPor
->GetNextPortion()
765 && pPor
->GetNextPortion()->IsDropPortion() )
767 const SwLinePortion
*pDropPor
= pPor
->GetNextPortion();
768 SwGluePortion
*pLeft
= static_cast<SwGluePortion
*>( pPor
);
770 // 4) pRight: Find the GluePor coming after the DropPor
771 pPor
= pPor
->GetNextPortion();
772 while( pPor
&& !pPor
->InFixMargGrp() )
773 pPor
= pPor
->GetNextPortion();
775 SwGluePortion
*pRight
= ( pPor
&& pPor
->InGlueGrp() ) ?
776 static_cast<SwGluePortion
*>(pPor
) : nullptr;
777 if( pRight
&& pRight
!= pLeft
)
779 // 5) Calculate nMinLeft. Who is the most to left?
780 const auto nDropLineStart
=
781 GetLineStart() + pLeft
->Width() + pDropPor
->Width();
782 auto nMinLeft
= nDropLineStart
;
783 for( sal_Int32 i
= 1; i
< GetDropLines(); ++i
)
790 pPor
= m_pCurr
->GetFirstPortion();
791 const SwMarginPortion
*pMar
= pPor
->IsMarginPortion() ?
792 static_cast<SwMarginPortion
*>(pPor
) : nullptr;
797 const auto nLineStart
=
798 GetLineStart() + pMar
->Width();
799 if( nMinLeft
> nLineStart
)
800 nMinLeft
= nLineStart
;
805 // 6) Distribute the Glue anew between pLeft and pRight
806 if( nMinLeft
< nDropLineStart
)
808 // The Glue is always passed from pLeft to pRight, so that
809 // the text moves to the left.
810 const auto nGlue
= nDropLineStart
- nMinLeft
;
812 pLeft
->MoveAllGlue( pRight
);
814 pLeft
->MoveGlue( pRight
, nGlue
);
820 if( nLineNumber
!= GetLineNr() )
823 while( nLineNumber
!= GetLineNr() && Next() )
828 void SwTextAdjuster::CalcDropRepaint()
831 SwRepaint
&rRepaint
= GetInfo().GetParaPortion()->GetRepaint();
832 if( rRepaint
.Top() > Y() )
834 for( sal_Int32 i
= 1; i
< GetDropLines(); ++i
)
836 const SwTwips nBottom
= Y() + GetLineHeight() - 1;
837 if( rRepaint
.Bottom() < nBottom
)
838 rRepaint
.Bottom( nBottom
);
841 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */