LibreOffice Module sw (master)  1
itradj.cxx
Go to the documentation of this file.
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 <sal/config.h>
21 
22 #include <o3tl/safeint.hxx>
23 
25 #include <doc.hxx>
26 
27 #include "itrtxt.hxx"
28 #include "porglue.hxx"
29 #include "porlay.hxx"
30 #include "porfly.hxx"
31 #include "pormulti.hxx"
32 #include "portab.hxx"
33 #include <memory>
34 
35 #define MIN_TAB_WIDTH 60
36 
37 using namespace ::com::sun::star;
38 
40 {
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.
44 
45  const SwLinePortion *pFly = nullptr;
46 
47  bool bSkip = !IsLastBlock() &&
48  m_nStart + m_pCurr->GetLen() >= TextFrameIndex(GetInfo().GetText().getLength());
49 
50  // Multi-line fields are tricky, because we need to check whether there are
51  // any other text portions in the paragraph.
52  if( bSkip )
53  {
54  const SwLineLayout *pLay = m_pCurr->GetNext();
55  while( pLay && !pLay->GetLen() )
56  {
57  const SwLinePortion *pPor = m_pCurr->GetFirstPortion();
58  while( pPor && bSkip )
59  {
60  if( pPor->InTextGrp() )
61  bSkip = false;
62  pPor = pPor->GetNextPortion();
63  }
64  pLay = bSkip ? pLay->GetNext() : nullptr;
65  }
66  }
67 
68  if( bSkip )
69  {
70  if( !GetInfo().GetParaPortion()->HasFly() )
71  {
72  if( IsLastCenter() )
75  return;
76  }
77  else
78  {
79  const SwLinePortion *pTmpFly = nullptr;
80 
81  // End at the last Fly
82  const SwLinePortion *pPos = m_pCurr->GetFirstPortion();
83  while( pPos )
84  {
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() )
89  {
90  pFly = pTmpFly; // A Fly with follow-up text!
91  pTmpFly = nullptr;
92  }
93  pPos = pPos->GetNextPortion();
94  }
95  // End if we didn't find one
96  if( !pFly )
97  {
98  if( IsLastCenter() )
101  return;
102  }
103  }
104  }
105 
106  const TextFrameIndex nOldIdx = GetInfo().GetIdx();
107  GetInfo().SetIdx( m_nStart );
108  CalcNewBlock( m_pCurr, pFly );
109  GetInfo().SetIdx( nOldIdx );
111 }
112 
114  sal_Int32& rKashidas, TextFrameIndex& nGluePortion)
115 {
116  // i60594 validate Kashida justification
117  TextFrameIndex nIdx = rItr.GetStart();
118  TextFrameIndex nEnd = rItr.GetEnd();
119 
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() );
126 
127  if (rKashidas <= 0) // nothing to do
128  return true;
129 
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 )
138  {
139  rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
140  TextFrameIndex nNext = rItr.GetNextAttr();
141 
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 )
146  nNext = nNextScript;
147 
148  if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
149  nNext = nEnd;
150  sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx );
151  if (nKashidasInAttr > 0)
152  {
153  // Kashida glyph looks suspicious, skip Kashida justification
154  if ( rInf.GetOut()->GetMinKashida() <= 0 )
155  {
156  return false;
157  }
158 
159  sal_Int32 nKashidasDropped = 0;
160  if ( !SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
161  {
162  nKashidasDropped = nKashidasInAttr;
163  rKashidas -= nKashidasDropped;
164  }
165  else
166  {
167  ComplexTextLayoutFlags nOldLayout = rInf.GetOut()->GetLayoutMode();
168  rInf.GetOut()->SetLayoutMode ( nOldLayout | ComplexTextLayoutFlags::BiDiRtl );
169  nKashidasDropped = rInf.GetOut()->ValidateKashidas(
170  rInf.GetText(), sal_Int32(nIdx), sal_Int32(nNext - nIdx),
171  nKashidasInAttr,
172  reinterpret_cast<sal_Int32*>(aKashidaPos.data() + nKashidaIdx),
173  reinterpret_cast<sal_Int32*>(aKashidaPosDropped.data()));
174  rInf.GetOut()->SetLayoutMode ( nOldLayout );
175  if ( nKashidasDropped )
176  {
177  rSI.MarkKashidasInvalid(nKashidasDropped, aKashidaPosDropped.data());
178  rKashidas -= nKashidasDropped;
179  nGluePortion -= TextFrameIndex(nKashidasDropped);
180  }
181  }
182  nKashidaIdx += nKashidasInAttr;
183  }
184  nIdx = nNext;
185  }
186 
187  // return false if all kashidas have been eliminated
188  return (rKashidas > 0);
189 }
190 
191 static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr, sal_Int32& rKashidas,
192  TextFrameIndex& nGluePortion, const long nGluePortionWidth, long& nSpaceAdd )
193 {
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
197  while (rKashidas)
198  {
199  bool bAddSpaceChanged = false;
200  TextFrameIndex nIdx = rItr.GetStart();
201  TextFrameIndex nEnd = rItr.GetEnd();
202  while ( nIdx < nEnd )
203  {
204  rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
205  TextFrameIndex nNext = rItr.GetNextAttr();
206 
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 )
211  nNext = nNextScript;
212 
213  if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
214  nNext = nEnd;
215  sal_Int32 nKashidasInAttr = rSI.KashidaJustify ( nullptr, nullptr, nIdx, nNext - nIdx );
216 
217  long nFontMinKashida = rInf.GetOut()->GetMinKashida();
218  if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
219  {
220  sal_Int32 nKashidasDropped = 0;
221  while ( rKashidas && nGluePortion && nKashidasInAttr > 0 &&
222  nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
223  {
224  --nGluePortion;
225  --rKashidas;
226  --nKashidasInAttr;
227  ++nKashidasDropped;
228  if( !rKashidas || !nGluePortion ) // nothing left, return false to
229  return false; // do regular blank justification
230 
231  nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
232  bAddSpaceChanged = true;
233  }
234  if( nKashidasDropped )
235  rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
236  }
237  if ( bAddSpaceChanged )
238  break; // start all over again
239  nIdx = nNext;
240  }
241  if ( !bAddSpaceChanged )
242  break; // everything was OK
243  }
244  return true;
245 }
246 
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.
251  const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida )
252 {
253  OSL_ENSURE( GetInfo().IsMulti() || SvxAdjust::Block == GetAdjust(),
254  "CalcNewBlock: Why?" );
255  OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
256 
257  pCurrent->InitSpaceAdd();
258  TextFrameIndex nGluePortion(0);
259  TextFrameIndex nCharCnt(0);
260  sal_uInt16 nSpaceIdx = 0;
261 
262  // i60591: hennerdrews
264  SwTextSizeInfo aInf ( GetTextFrame() );
265  SwTextIter aItr ( GetTextFrame(), &aInf );
266 
267  if ( rSI.CountKashida() )
268  {
269  while (aItr.GetCurr() != pCurrent && aItr.GetNext())
270  aItr.Next();
271 
272  if( bSkipKashida )
273  {
274  rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
275  }
276  else
277  {
278  rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
279  rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
280  }
281  }
282 
283  // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
284  if (!bSkipKashida)
285  CalcRightMargin( pCurrent, nReal );
286 
287  // #i49277#
288  const bool bDoNotJustifyLinesWithManualBreak =
290  bool bDoNotJustifyTab = false;
291 
292  SwLinePortion *pPos = pCurrent->GetNextPortion();
293 
294  while( pPos )
295  {
296  if ( ( bDoNotJustifyLinesWithManualBreak || bDoNotJustifyTab ) &&
297  pPos->IsBreakPortion() && !IsLastBlock() )
298  {
299  pCurrent->FinishSpaceAdd();
300  break;
301  }
302 
303  switch ( pPos->GetWhichPor() )
304  {
306  case PortionType::TabRight :
308  bDoNotJustifyTab = true;
309  break;
310  case PortionType::TabLeft :
311  case PortionType::Break:
312  bDoNotJustifyTab = false;
313  break;
314  default: break;
315  }
316 
317  if ( pPos->InTextGrp() )
318  nGluePortion = nGluePortion + static_cast<SwTextPortion*>(pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
319  else if( pPos->IsMultiPortion() )
320  {
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
325  // in the wider line
326  if( pMulti->HasTabulator() )
327  {
328  if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
329  pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
330 
331  nSpaceIdx++;
332  nGluePortion = TextFrameIndex(0);
333  nCharCnt = TextFrameIndex(0);
334  }
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
339  }
340 
341  if( pPos->InGlueGrp() )
342  {
343  if( pPos->InFixMargGrp() )
344  {
345  if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
346  pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
347 
348  const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
350 
351  sal_Int32 nKashidas = 0;
352  if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
353  {
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 ))
357  {
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 );
363  return;
364  }
365  }
366 
367  if( nGluePortion )
368  {
369  long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
370 
371  // i60594
372  if( rSI.CountKashida() && !bSkipKashida )
373  {
374  if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
375  {
376  // no kashidas left
377  // do regular blank justification
378  pCurrent->FinishSpaceAdd();
379  GetInfo().SetIdx( m_nStart );
380  CalcNewBlock( pCurrent, pStopAt, nReal, true );
381  return;
382  }
383  }
384 
385  pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
386  pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
387  }
388  else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
389  {
390  const long nSpaceAdd = - nGluePortionWidth / (sal_Int32(nCharCnt) - 1);
391  pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
392  pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
393  }
394 
395  nSpaceIdx++;
396  nGluePortion = TextFrameIndex(0);
397  nCharCnt = TextFrameIndex(0);
398  }
399  else
400  ++nGluePortion;
401  }
402  GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
403  if ( pPos == pStopAt )
404  {
405  pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
406  break;
407  }
408  pPos = pPos->GetNextPortion();
409  }
410 }
411 
413 {
414  OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
415  OSL_ENSURE( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
416 
417  pCurrent->SetKanaComp( std::make_unique<std::deque<sal_uInt16>>() );
418 
419  const sal_uInt16 nNull = 0;
420  size_t nKanaIdx = 0;
421  long nKanaDiffSum = 0;
422  SwTwips nRepaintOfst = 0;
423  SwTwips nX = 0;
424  bool bNoCompression = false;
425 
426  // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
427  CalcRightMargin( pCurrent );
428 
429  SwLinePortion* pPos = pCurrent->GetNextPortion();
430 
431  while( pPos )
432  {
433  if ( pPos->InTextGrp() )
434  {
435  // get maximum portion width from info structure, calculated
436  // during text formatting
437  sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
438 
439  // check, if information is stored under other key
440  if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
441  nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
442 
443  // calculate difference between portion width and max. width
444  nKanaDiffSum += nMaxWidthDiff;
445 
446  // we store the beginning of the first compressible portion
447  // for repaint
448  if ( nMaxWidthDiff && !nRepaintOfst )
449  nRepaintOfst = nX + GetLeftMargin();
450  }
451  else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
452  {
453  if ( nKanaIdx == pCurrent->GetKanaComp().size() )
454  pCurrent->GetKanaComp().push_back( nNull );
455 
456  long nRest;
457 
458  if ( pPos->InTabGrp() )
459  {
460  nRest = ! bNoCompression &&
461  ( pPos->Width() > MIN_TAB_WIDTH ) ?
462  pPos->Width() - MIN_TAB_WIDTH :
463  0;
464 
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();
469  }
470  else
471  {
472  nRest = ! bNoCompression ?
473  static_cast<SwGluePortion*>(pPos)->GetPrtGlue() :
474  0;
475 
476  bNoCompression = false;
477  }
478 
479  if( nKanaDiffSum )
480  {
481  sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum;
482 
483  if ( nCompress >= 10000 )
484  // kanas can be expanded to 100%, and there is still
485  // some space remaining
486  nCompress = 0;
487 
488  else
489  nCompress = 10000 - nCompress;
490 
491  ( pCurrent->GetKanaComp() )[ nKanaIdx ] = static_cast<sal_uInt16>(nCompress);
492  nKanaDiffSum = 0;
493  }
494 
495  nKanaIdx++;
496  }
497 
498  nX += pPos->Width();
499  pPos = pPos->GetNextPortion();
500  }
501 
502  // set portion width
503  nKanaIdx = 0;
504  sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
505  pPos = pCurrent->GetNextPortion();
506  long nDecompress = 0;
507 
508  while( pPos )
509  {
510  if ( pPos->InTextGrp() )
511  {
512  const sal_uInt16 nMinWidth = pPos->Width();
513 
514  // get maximum portion width from info structure, calculated
515  // during text formatting
516  sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
517 
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;
524  }
525  else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
526  {
527  pPos->Width( static_cast<sal_uInt16>(pPos->Width() - nDecompress) );
528 
529  if ( pPos->InTabGrp() )
530  // set fix width to width
531  static_cast<SwTabPortion*>(pPos)->SetFixWidth( pPos->Width() );
532 
533  if ( ++nKanaIdx < pCurrent->GetKanaComp().size() )
534  nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
535 
536  nDecompress = 0;
537  }
538  pPos = pPos->GetNextPortion();
539  }
540 
541  return nRepaintOfst;
542 }
543 
545  SwTwips nReal )
546 {
547  long nRealWidth;
548  const sal_uInt16 nRealHeight = GetLineHeight();
549  const sal_uInt16 nLineHeight = pCurrent->Height();
550 
551  sal_uInt16 nPrtWidth = pCurrent->PrtWidth();
552  SwLinePortion *pLast = pCurrent->FindLastPortion();
553 
554  if( GetInfo().IsMulti() )
555  nRealWidth = nReal;
556  else
557  {
558  nRealWidth = GetLineWidth();
559  // For each FlyFrame extending into the right margin, we create a FlyPortion.
560  const long nLeftMar = GetLeftMargin();
561  SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
562  nRealWidth - nPrtWidth, nLineHeight );
563 
564  SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
565  while( pFly && long( nPrtWidth )< nRealWidth )
566  {
567  pLast->Append( pFly );
568  pLast = 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 );
574  }
575  delete pFly;
576  }
577 
578  SwMarginPortion *pRight = new SwMarginPortion;
579  pLast->Append( pRight );
580 
581  if( long( nPrtWidth )< nRealWidth )
582  pRight->PrtWidth( sal_uInt16( nRealWidth - nPrtWidth ) );
583 
584  // pCurrent->Width() is set to the real size, because we attach the
585  // MarginPortions.
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.
590 
591  pCurrent->PrtWidth( sal_uInt16( nRealWidth ) );
592  return pRight;
593 }
594 
596 {
597  // 1) We insert a left margin:
598  SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
599  SwGluePortion *pGlue = pLeft; // the last GluePortion
600 
601  // 2) We attach a right margin:
602  // CalcRightMargin also calculates a possible overlap with FlyFrames.
603  CalcRightMargin( pCurrent );
604 
605  SwLinePortion *pPos = pLeft->GetNextPortion();
606  TextFrameIndex nLen(0);
607 
608  // If we only have one line, the text portion is consecutive and we center, then ...
609  bool bComplete = TextFrameIndex(0) == m_nStart;
611  bool bMultiTab = false;
612 
613  while( pPos )
614  {
615  if ( pPos->IsMultiPortion() && static_cast<SwMultiPortion*>(pPos)->HasTabulator() )
616  bMultiTab = true;
617  else if( pPos->InFixMargGrp() &&
618  ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
619  {
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 );
625  else
626  {
627  // We set the first text portion to right-aligned and the last one
628  // to left-aligned.
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 );
633  else
634  {
635  if ( ! bTabCompat )
636  {
637  if( pLeft == pGlue )
638  {
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 );
643  else
644  static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
645  }
646  else
647  {
648  // The last text portion retains its Glue.
649  if( !pPos->IsMarginPortion() )
650  static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
651  }
652  }
653  else
654  static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
655  }
656  }
657 
658  pGlue = static_cast<SwGluePortion*>(pPos);
659  bComplete = false;
660  }
661  nLen = nLen + pPos->GetLen();
662  pPos = pPos->GetNextPortion();
663  }
664 
665  if( ! bTabCompat && ! bMultiTab && SvxAdjust::Right == GetAdjust() )
666  // portions are moved to the right if possible
667  pLeft->AdjustRight( pCurrent );
668 }
669 
671 {
672  OSL_ENSURE( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
673 
674  pCurrent->SetFormatAdj(false);
675 
676  SwParaPortion* pPara = GetInfo().GetParaPortion();
677 
678  switch( GetAdjust() )
679  {
680  case SvxAdjust::Right:
681  case SvxAdjust::Center:
682  {
683  CalcFlyAdjust( pCurrent );
684  pPara->GetRepaint().SetOffset( 0 );
685  break;
686  }
687  case SvxAdjust::Block:
688  {
689  FormatBlock();
690  break;
691  }
692  default : return;
693  }
694 }
695 
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
701  const SwRect &rCurrRect )
702 {
703  SwTextFly aTextFly( GetTextFrame() );
704 
705  const sal_uInt16 nCurrWidth = m_pCurr->PrtWidth();
706  SwFlyPortion *pFlyPortion = nullptr;
707 
708  SwRect aLineVert( rCurrRect );
709  if ( GetTextFrame()->IsRightToLeft() )
710  GetTextFrame()->SwitchLTRtoRTL( aLineVert );
711  if ( GetTextFrame()->IsVertical() )
713 
714  // aFlyRect is document-global!
715  SwRect aFlyRect( aTextFly.GetFrame( aLineVert ) );
716 
717  if ( GetTextFrame()->IsRightToLeft() )
718  GetTextFrame()->SwitchRTLtoLTR( aFlyRect );
719  if ( GetTextFrame()->IsVertical() )
721 
722  // If a Frame overlapps we open a Portion
723  if( aFlyRect.HasArea() )
724  {
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 );
730 
731  // If the rect is wider than the line, we adjust it to the right size
732  const long nLocalWidth = aLocal.Left() + aLocal.Width();
733  if( nRealWidth < nLocalWidth )
734  aLocal.Width( nRealWidth - aLocal.Left() );
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();
740  }
741  return pFlyPortion;
742 }
743 
744 // CalcDropAdjust is called at the end by Format() if needed
746 {
747  OSL_ENSURE( 1<GetDropLines() && SvxAdjust::Left!=GetAdjust() && SvxAdjust::Block!=GetAdjust(),
748  "CalcDropAdjust: No reason for DropAdjustment." );
749 
750  const sal_uInt16 nLineNumber = GetLineNr();
751 
752  // 1) Skip dummies
753  Top();
754 
755  if( !m_pCurr->IsDummy() || NextLine() )
756  {
757  // Adjust first
758  GetAdjusted();
759 
761 
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() )
766  {
767  const SwLinePortion *pDropPor = pPor->GetNextPortion();
768  SwGluePortion *pLeft = static_cast<SwGluePortion*>( pPor );
769 
770  // 4) pRight: Find the GluePor coming after the DropPor
771  pPor = pPor->GetNextPortion();
772  while( pPor && !pPor->InFixMargGrp() )
773  pPor = pPor->GetNextPortion();
774 
775  SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
776  static_cast<SwGluePortion*>(pPor) : nullptr;
777  if( pRight && pRight != pLeft )
778  {
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_uInt16 i = 1; i < GetDropLines(); ++i )
784  {
785  if( NextLine() )
786  {
787  // Adjust first
788  GetAdjusted();
789 
790  pPor = m_pCurr->GetFirstPortion();
791  const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
792  static_cast<SwMarginPortion*>(pPor) : nullptr;
793  if( !pMar )
794  nMinLeft = 0;
795  else
796  {
797  const auto nLineStart =
798  GetLineStart() + pMar->Width();
799  if( nMinLeft > nLineStart )
800  nMinLeft = nLineStart;
801  }
802  }
803  }
804 
805  // 6) Distribute the Glue anew between pLeft and pRight
806  if( nMinLeft < nDropLineStart )
807  {
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;
811  if( !nMinLeft )
812  pLeft->MoveAllGlue( pRight );
813  else
814  pLeft->MoveGlue( pRight, nGlue );
815  }
816  }
817  }
818  }
819 
820  if( nLineNumber != GetLineNr() )
821  {
822  Top();
823  while( nLineNumber != GetLineNr() && Next() )
824  ;
825  }
826 }
827 
829 {
830  Top();
831  SwRepaint &rRepaint = GetInfo().GetParaPortion()->GetRepaint();
832  if( rRepaint.Top() > Y() )
833  rRepaint.Top( Y() );
834  for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
835  NextLine();
836  const SwTwips nBottom = Y() + GetLineHeight() - 1;
837  if( rRepaint.Bottom() < nBottom )
838  rRepaint.Bottom( nBottom );
839 }
840 
841 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void MoveAllGlue(SwGluePortion *pTarget)
Definition: porglue.hxx:76
void AdjFixWidth()
Definition: porglue.hxx:70
std::deque< sal_uInt16 > * GetpKanaComp() const
Definition: porlay.hxx:184
bool SeekAndChgAttrIter(TextFrameIndex nPos, OutputDevice *pOut)
Executes ChgPhysFnt if Seek() returns true and change font to merge character border with neighbours...
Definition: itratr.cxx:151
void SwitchVerticalToHorizontal(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from vertical to horizontal layout...
Definition: txtfrm.cxx:578
void SetKanaComp(std::unique_ptr< std::deque< sal_uInt16 >> pNew)
Definition: porlay.hxx:182
void CalcDropRepaint()
Definition: itradj.cxx:828
void SetFormatAdj(const bool bNew)
Definition: porlay.hxx:110
void GetAdjusted() const
Definition: itrtxt.hxx:250
sal_uInt16 GetLineNr() const
Definition: itrtxt.hxx:87
TextFrameIndex NextScriptChg(TextFrameIndex nPos) const
Definition: porlay.cxx:1614
void CalcNewBlock(SwLineLayout *pCurr, const SwLinePortion *pStopAt, SwTwips nReal=0, bool bSkipKashida=false)
Definition: itradj.cxx:250
sal_uInt16 Height() const
Definition: possiz.hxx:44
SwLineLayout * GetNext()
Definition: porlay.hxx:143
SwMarginPortion * CalcLeftMargin()
Definition: porlay.cxx:255
std::deque< sal_uInt16 > & GetKanaComp()
Definition: porlay.hxx:185
const SwLineLayout * NextLine()
Definition: itrtxt.cxx:124
TextFrameIndex GetStart() const
Definition: itrtxt.hxx:88
void SwitchHorizontalToVertical(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from horizontal to vertical layout...
Definition: txtfrm.cxx:471
sal_uIntPtr sal_uLong
The purpose of this class is to be the universal interface between formatting/text output and the pos...
Definition: txtfly.hxx:119
void MarkKashidasInvalid(sal_Int32 nCnt, const TextFrameIndex *pKashidaPositions)
Marks nCnt kashida positions as invalid pKashidaPositions: array of char indices relative to the para...
Definition: porlay.cxx:2278
void Height(long nNew)
Definition: swrect.hxx:191
void MoveGlue(SwGluePortion *pTarget, const long nPrtGlue)
Definition: porglue.cxx:108
ComplexTextLayoutFlags GetLayoutMode() const
TextFrameIndex GetNextAttr() const
Definition: itratr.cxx:728
long SwTwips
Definition: swtypes.hxx:49
void SetLayoutMode(ComplexTextLayoutFlags nTextLayoutMode)
void CalcDropAdjust()
Definition: itradj.cxx:745
const SwLineLayout * Next()
Definition: itrtxt.cxx:107
void Pos(const Point &rNew)
Definition: swrect.hxx:169
SwTwips CalcKanaAdj(SwLineLayout *pCurr)
Definition: itradj.cxx:412
sal_uInt16 GetFix() const
Definition: porglue.hxx:56
Of course Writer needs its own rectangles.
Definition: swrect.hxx:35
SwTwips Y() const
Definition: itrtxt.hxx:90
Collection of SwLineLayout instances, represents the paragraph text in Writer layout.
Definition: porlay.hxx:229
bool IsLastBlock() const
Definition: itrtxt.hxx:188
void SetFly()
Definition: porlay.hxx:280
bool IsMarginPortion() const
Definition: porlin.hxx:124
SwRect GetFrame(const SwRect &rPortion) const
Definition: txtfly.hxx:356
void SetNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen)
Use regular blank justification instead of kashdida justification for the given line of text...
Definition: porlay.cxx:2245
sal_uInt16 GetLLSpaceAddCount() const
Definition: porlay.hxx:169
void Top(const long nTop)
Definition: swrect.hxx:204
void CalcAdjLine(SwLineLayout *pCurr)
Definition: itradj.cxx:670
static bool lcl_CheckKashidaWidth(SwScriptInfo &rSI, SwTextSizeInfo &rInf, SwTextIter &rItr, sal_Int32 &rKashidas, TextFrameIndex &nGluePortion, const long nGluePortionWidth, long &nSpaceAdd)
Definition: itradj.cxx:191
bool IsBreakPortion() const
Definition: porlin.hxx:114
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
bool InFixMargGrp() const
Definition: porlin.hxx:108
static bool lcl_CheckKashidaPositions(SwScriptInfo &rSI, SwTextSizeInfo &rInf, SwTextIter &rItr, sal_Int32 &rKashidas, TextFrameIndex &nGluePortion)
Definition: itradj.cxx:113
bool IsBidi() const
Definition: pormulti.hxx:133
void InitSpaceAdd()
Definition: porlay.cxx:294
static bool IsArabicText(const OUString &rText, TextFrameIndex nStt, TextFrameIndex nLen)
Checks if text is Arabic text.
Definition: porlay.cxx:2128
void CalcFlyAdjust(SwLineLayout *pCurr)
Definition: itradj.cxx:595
bool InGlueGrp() const
Definition: porlin.hxx:99
virtual SwLinePortion * Append(SwLinePortion *pPortion)
Definition: porlin.cxx:189
bool IsDummy() const
Definition: porlay.hxx:135
SwTwips GetLineStart() const
Definition: itrcrsr.cxx:376
int i
void FormatBlock()
Definition: itradj.cxx:39
Collection of SwLinePortion instances, representing one line of text.
Definition: porlay.hxx:78
void Top()
Definition: itrtxt.hxx:99
vcl::RenderContext * GetOut()
Definition: inftxt.hxx:231
ComplexTextLayoutFlags
SwParaPortion * GetParaPortion()
Definition: inftxt.hxx:128
bool IsFlyPortion() const
Definition: porlin.hxx:125
SwLinePortion * GetFirstPortion() const
Definition: porlay.cxx:670
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
SwTwips GetLeftMargin() const
Definition: itrtxt.hxx:328
void FinishSpaceAdd()
Definition: porlay.hxx:168
SwFlyPortion * CalcFlyPortion(const long nRealWidth, const SwRect &rCurrRect)
Definition: itradj.cxx:700
bool IsTabLeftPortion() const
Definition: porlin.hxx:117
sal_uInt16 GetLineHeight() const
Definition: itrtxt.hxx:116
TextFrameIndex GetIdx() const
Definition: inftxt.hxx:278
bool IsDropPortion() const
Definition: porlin.hxx:121
bool IsOneBlock() const
Definition: itrtxt.hxx:187
bool IsFormatAdj() const
Definition: porlay.hxx:111
sal_uInt16 GetMaxWidthDiff(const SwLinePortion *nKey)
Definition: inftxt.hxx:305
void SetLLSpaceAdd(long nNew, sal_uInt16 nIdx)
Definition: porlay.hxx:170
void MoveHalfGlue(SwGluePortion *pTarget)
Definition: porglue.hxx:81
TextFrameIndex GetLen() const
Definition: porlin.hxx:74
bool InTabGrp() const
Definition: porlin.hxx:100
const OUString & GetText() const
Definition: inftxt.hxx:246
bool IsMultiPortion() const
Definition: porlin.hxx:134
void SwitchLTRtoRTL(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from LTR to RTL layout.
Definition: txtfrm.cxx:681
Base class for anything that can be part of a line in the Writer layout.
Definition: porlin.hxx:50
void Left(const long nLeft)
Definition: swrect.hxx:195
void Bottom(const long nBottom)
Definition: swrect.hxx:209
void ClearNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen)
Clear forced blank justification for a given line.
Definition: porlay.cxx:2262
TextFrameIndex m_nStart
Definition: itrtxt.hxx:41
void SwitchRTLtoLTR(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from RTL to LTR layout.
Definition: txtfrm.hxx:726
TextFrameIndex GetLength() const
Definition: itrtxt.hxx:86
PortionType GetWhichPor() const
Definition: porlin.hxx:95
SwRepaint & GetRepaint()
Definition: porlay.hxx:264
void Width(long nNew)
Definition: swrect.hxx:187
bool IsDouble() const
Definition: pormulti.hxx:131
SwLinePortion * FindLastPortion()
Definition: porlin.cxx:178
IDocumentSettingAccess const & getIDocumentSettingAccess() const
Definition: doc.cxx:175
SvxAdjust GetAdjust() const
Definition: itrtxt.hxx:190
bool IsLastCenter() const
Definition: itrtxt.hxx:189
SwTextSizeInfo & GetInfo()
Definition: itrtxt.hxx:216
bool IsRightToLeft() const
Definition: frame.hxx:965
void ClearKashidaInvalid(size_t nKashPos)
Definition: porlay.cxx:2170
bool InTextGrp() const
Definition: porlin.hxx:98
sal_uInt16 Width() const
Definition: possiz.hxx:46
SwScriptInfo & GetScriptInfo()
Definition: porlay.hxx:270
double getLength(const B2DPolygon &rCandidate)
virtual bool get(DocumentSettingId id) const =0
Return the specified document setting.
SwMarginPortion * CalcRightMargin(SwLineLayout *pCurr, SwTwips nReal=0)
Definition: itradj.cxx:544
sal_Int32 ValidateKashidas(const OUString &rTxt, sal_Int32 nIdx, sal_Int32 nLen, sal_Int32 nKashCount, const sal_Int32 *pKashidaPos, sal_Int32 *pKashidaPosDropped) const
const SwLineLayout * GetNext() const
Definition: itrtxt.hxx:84
sal_uInt16 GetLineWidth() const
Definition: itrtxt.hxx:191
size_t CountKashida() const
Definition: scriptinfo.hxx:150
const SwLineLayout * GetCurr() const
Definition: itrtxt.hxx:83
#define SPACING_PRECISION_FACTOR
Definition: scriptinfo.hxx:39
long GetMinKashida() const
#define MIN_TAB_WIDTH
Definition: itradj.cxx:35
SwDoc & GetDoc()
Definition: txtfrm.hxx:451
bool HasTabulator() const
Definition: pormulti.hxx:124
SwTextFrame * GetTextFrame()
Definition: itrtxt.hxx:134
SwLineLayout * m_pCurr
Definition: itrtxt.hxx:36
void PrtWidth(sal_uInt16 nNewWidth)
Definition: porlin.hxx:80
void GetKashidaPositions(TextFrameIndex nStt, TextFrameIndex nLen, std::vector< TextFrameIndex > &rKashidaPosition)
retrieves kashida opportunities for a given text range.
Definition: porlay.cxx:2221
void SetOffset(const SwTwips nNew)
Definition: porlay.hxx:71
TextFrameIndex GetEnd() const
Definition: itrtxt.hxx:89
void AdjustRight(const SwLineLayout *pCurr)
In the outer loop all portions are inspected - the GluePortions at the end are processed first...
Definition: porglue.cxx:163
sal_uInt16 GetDropLines() const
Definition: itrtxt.hxx:202
SwLinePortion * GetNextPortion() const
Definition: porlin.hxx:72
void SetIdx(const TextFrameIndex nNew)
Definition: inftxt.hxx:279
sal_Int32 KashidaJustify(long *pKernArray, long *pScrArray, TextFrameIndex nStt, TextFrameIndex nLen, long nSpaceAdd=0) const
Performs a kashida justification on the kerning array.
Definition: porlay.cxx:2043
o3tl::strong_int< sal_Int32, struct Tag_TextFrameIndex > TextFrameIndex
Denotes a character index in a text frame at a layout level, after extent mapping from a text node at...
const sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:61