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 <vcl/outdev.hxx>
22 #include <doc.hxx>
23 
24 #include <frame.hxx>
25 #include <paratr.hxx>
26 #include "itrtxt.hxx"
27 #include "porglue.hxx"
28 #include "porlay.hxx"
29 #include "porfly.hxx"
30 #include "pordrop.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() >= static_cast<size_t>(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 
291  SwLinePortion *pPos = pCurrent->GetNextPortion();
292 
293  while( pPos )
294  {
295  if ( bDoNotJustifyLinesWithManualBreak &&
296  pPos->IsBreakPortion() && !IsLastBlock() )
297  {
298  pCurrent->FinishSpaceAdd();
299  break;
300  }
301 
302  if ( pPos->InTextGrp() )
303  nGluePortion = nGluePortion + static_cast<SwTextPortion*>(pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
304  else if( pPos->IsMultiPortion() )
305  {
306  SwMultiPortion* pMulti = static_cast<SwMultiPortion*>(pPos);
307  // a multiportion with a tabulator inside breaks the text adjustment
308  // a ruby portion will not be stretched by text adjustment
309  // a double line portion takes additional space for each blank
310  // in the wider line
311  if( pMulti->HasTabulator() )
312  {
313  if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
314  pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
315 
316  nSpaceIdx++;
317  nGluePortion = TextFrameIndex(0);
318  nCharCnt = TextFrameIndex(0);
319  }
320  else if( pMulti->IsDouble() )
321  nGluePortion = nGluePortion + static_cast<SwDoubleLinePortion*>(pMulti)->GetSpaceCnt();
322  else if ( pMulti->IsBidi() )
323  nGluePortion = nGluePortion + static_cast<SwBidiPortion*>(pMulti)->GetSpaceCnt( GetInfo() ); // i60594
324  }
325 
326  if( pPos->InGlueGrp() )
327  {
328  if( pPos->InFixMargGrp() )
329  {
330  if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
331  pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
332 
333  const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
335 
336  sal_Int32 nKashidas = 0;
337  if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
338  {
339  // kashida positions found in SwScriptInfo are not necessarily valid in every font
340  // if two characters are replaced by a ligature glyph, there will be no place for a kashida
341  if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
342  {
343  // all kashida positions are invalid
344  // do regular blank justification
345  pCurrent->FinishSpaceAdd();
346  GetInfo().SetIdx( m_nStart );
347  CalcNewBlock( pCurrent, pStopAt, nReal, true );
348  return;
349  }
350  }
351 
352  if( nGluePortion )
353  {
354  long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
355 
356  // i60594
357  if( rSI.CountKashida() && !bSkipKashida )
358  {
359  if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
360  {
361  // no kashidas left
362  // do regular blank justification
363  pCurrent->FinishSpaceAdd();
364  GetInfo().SetIdx( m_nStart );
365  CalcNewBlock( pCurrent, pStopAt, nReal, true );
366  return;
367  }
368  }
369 
370  pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
371  pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
372  }
373  else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
374  {
375  const long nSpaceAdd = - nGluePortionWidth / (sal_Int32(nCharCnt) - 1);
376  pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
377  pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
378  }
379 
380  nSpaceIdx++;
381  nGluePortion = TextFrameIndex(0);
382  nCharCnt = TextFrameIndex(0);
383  }
384  else
385  ++nGluePortion;
386  }
387  GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
388  if ( pPos == pStopAt )
389  {
390  pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
391  break;
392  }
393  pPos = pPos->GetNextPortion();
394  }
395 }
396 
398 {
399  OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
400  OSL_ENSURE( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
401 
402  pCurrent->SetKanaComp( std::make_unique<std::deque<sal_uInt16>>() );
403 
404  const sal_uInt16 nNull = 0;
405  size_t nKanaIdx = 0;
406  long nKanaDiffSum = 0;
407  SwTwips nRepaintOfst = 0;
408  SwTwips nX = 0;
409  bool bNoCompression = false;
410 
411  // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
412  CalcRightMargin( pCurrent );
413 
414  SwLinePortion* pPos = pCurrent->GetNextPortion();
415 
416  while( pPos )
417  {
418  if ( pPos->InTextGrp() )
419  {
420  // get maximum portion width from info structure, calculated
421  // during text formatting
422  sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
423 
424  // check, if information is stored under other key
425  if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
426  nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
427 
428  // calculate difference between portion width and max. width
429  nKanaDiffSum += nMaxWidthDiff;
430 
431  // we store the beginning of the first compressible portion
432  // for repaint
433  if ( nMaxWidthDiff && !nRepaintOfst )
434  nRepaintOfst = nX + GetLeftMargin();
435  }
436  else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
437  {
438  if ( nKanaIdx == pCurrent->GetKanaComp().size() )
439  pCurrent->GetKanaComp().push_back( nNull );
440 
441  long nRest;
442 
443  if ( pPos->InTabGrp() )
444  {
445  nRest = ! bNoCompression &&
446  ( pPos->Width() > MIN_TAB_WIDTH ) ?
447  pPos->Width() - MIN_TAB_WIDTH :
448  0;
449 
450  // for simplifying the handling of left, right ... tabs,
451  // we do expand portions, which are lying behind
452  // those special tabs
453  bNoCompression = !pPos->IsTabLeftPortion();
454  }
455  else
456  {
457  nRest = ! bNoCompression ?
458  static_cast<SwGluePortion*>(pPos)->GetPrtGlue() :
459  0;
460 
461  bNoCompression = false;
462  }
463 
464  if( nKanaDiffSum )
465  {
466  sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum;
467 
468  if ( nCompress >= 10000 )
469  // kanas can be expanded to 100%, and there is still
470  // some space remaining
471  nCompress = 0;
472 
473  else
474  nCompress = 10000 - nCompress;
475 
476  ( pCurrent->GetKanaComp() )[ nKanaIdx ] = static_cast<sal_uInt16>(nCompress);
477  nKanaDiffSum = 0;
478  }
479 
480  nKanaIdx++;
481  }
482 
483  nX += pPos->Width();
484  pPos = pPos->GetNextPortion();
485  }
486 
487  // set portion width
488  nKanaIdx = 0;
489  sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
490  pPos = pCurrent->GetNextPortion();
491  long nDecompress = 0;
492 
493  while( pPos )
494  {
495  if ( pPos->InTextGrp() )
496  {
497  const sal_uInt16 nMinWidth = pPos->Width();
498 
499  // get maximum portion width from info structure, calculated
500  // during text formatting
501  sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
502 
503  // check, if information is stored under other key
504  if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
505  nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
506  pPos->Width( nMinWidth +
507  ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 );
508  nDecompress += pPos->Width() - nMinWidth;
509  }
510  else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
511  {
512  pPos->Width( static_cast<sal_uInt16>(pPos->Width() - nDecompress) );
513 
514  if ( pPos->InTabGrp() )
515  // set fix width to width
516  static_cast<SwTabPortion*>(pPos)->SetFixWidth( pPos->Width() );
517 
518  if ( ++nKanaIdx < pCurrent->GetKanaComp().size() )
519  nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
520 
521  nDecompress = 0;
522  }
523  pPos = pPos->GetNextPortion();
524  }
525 
526  return nRepaintOfst;
527 }
528 
530  SwTwips nReal )
531 {
532  long nRealWidth;
533  const sal_uInt16 nRealHeight = GetLineHeight();
534  const sal_uInt16 nLineHeight = pCurrent->Height();
535 
536  sal_uInt16 nPrtWidth = pCurrent->PrtWidth();
537  SwLinePortion *pLast = pCurrent->FindLastPortion();
538 
539  if( GetInfo().IsMulti() )
540  nRealWidth = nReal;
541  else
542  {
543  nRealWidth = GetLineWidth();
544  // For each FlyFrame extending into the right margin, we create a FlyPortion.
545  const long nLeftMar = GetLeftMargin();
546  SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
547  nRealWidth - nPrtWidth, nLineHeight );
548 
549  SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
550  while( pFly && long( nPrtWidth )< nRealWidth )
551  {
552  pLast->Append( pFly );
553  pLast = pFly;
554  if( pFly->GetFix() > nPrtWidth )
555  pFly->Width( ( pFly->GetFix() - nPrtWidth) + pFly->Width() + 1);
556  nPrtWidth += pFly->Width() + 1;
557  aCurrRect.Left( nLeftMar + nPrtWidth );
558  pFly = CalcFlyPortion( nRealWidth, aCurrRect );
559  }
560  delete pFly;
561  }
562 
563  SwMarginPortion *pRight = new SwMarginPortion;
564  pLast->Append( pRight );
565 
566  if( long( nPrtWidth )< nRealWidth )
567  pRight->PrtWidth( sal_uInt16( nRealWidth - nPrtWidth ) );
568 
569  // pCurrent->Width() is set to the real size, because we attach the
570  // MarginPortions.
571  // This trick gives miraculous results:
572  // If pCurrent->Width() == nRealWidth, then the adjustment gets overruled
573  // implicitly. GetLeftMarginAdjust() and IsJustified() think they have a
574  // line filled with chars.
575 
576  pCurrent->PrtWidth( sal_uInt16( nRealWidth ) );
577  return pRight;
578 }
579 
581 {
582  // 1) We insert a left margin:
583  SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
584  SwGluePortion *pGlue = pLeft; // the last GluePortion
585 
586  // 2) We attach a right margin:
587  // CalcRightMargin also calculates a possible overlap with FlyFrames.
588  CalcRightMargin( pCurrent );
589 
590  SwLinePortion *pPos = pLeft->GetNextPortion();
591  TextFrameIndex nLen(0);
592 
593  // If we only have one line, the text portion is consecutive and we center, then ...
594  bool bComplete = TextFrameIndex(0) == m_nStart;
596  bool bMultiTab = false;
597 
598  while( pPos )
599  {
600  if ( pPos->IsMultiPortion() && static_cast<SwMultiPortion*>(pPos)->HasTabulator() )
601  bMultiTab = true;
602  else if( pPos->InFixMargGrp() &&
603  ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
604  {
605  // in tab compat mode we do not want to change tab portions
606  // in non tab compat mode we do not want to change margins if we
607  // found a multi portion with tabs
608  if( SvxAdjust::Right == GetAdjust() )
609  static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
610  else
611  {
612  // We set the first text portion to right-aligned and the last one
613  // to left-aligned.
614  // The first text portion gets the whole Glue, but only if we have
615  // more than one line.
616  if (bComplete && TextFrameIndex(GetInfo().GetText().getLength()) == nLen)
617  static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
618  else
619  {
620  if ( ! bTabCompat )
621  {
622  if( pLeft == pGlue )
623  {
624  // If we only have a left and right margin, the
625  // margins share the Glue.
626  if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
627  static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
628  else
629  static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
630  }
631  else
632  {
633  // The last text portion retains its Glue.
634  if( !pPos->IsMarginPortion() )
635  static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
636  }
637  }
638  else
639  static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
640  }
641  }
642 
643  pGlue = static_cast<SwGluePortion*>(pPos);
644  bComplete = false;
645  }
646  nLen = nLen + pPos->GetLen();
647  pPos = pPos->GetNextPortion();
648  }
649 
650  if( ! bTabCompat && ! bMultiTab && SvxAdjust::Right == GetAdjust() )
651  // portions are moved to the right if possible
652  pLeft->AdjustRight( pCurrent );
653 }
654 
656 {
657  OSL_ENSURE( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
658 
659  pCurrent->SetFormatAdj(false);
660 
661  SwParaPortion* pPara = GetInfo().GetParaPortion();
662 
663  switch( GetAdjust() )
664  {
665  case SvxAdjust::Right:
666  case SvxAdjust::Center:
667  {
668  CalcFlyAdjust( pCurrent );
669  pPara->GetRepaint().SetOfst( 0 );
670  break;
671  }
672  case SvxAdjust::Block:
673  {
674  FormatBlock();
675  break;
676  }
677  default : return;
678  }
679 }
680 
681 // This is a quite complicated calculation: nCurrWidth is the width _before_
682 // adding the word, that still fits onto the line! For this reason the FlyPortion's
683 // width is still correct if we get a deadlock-situation of:
684 // bFirstWord && !WORDFITS
686  const SwRect &rCurrRect )
687 {
688  SwTextFly aTextFly( GetTextFrame() );
689 
690  const sal_uInt16 nCurrWidth = m_pCurr->PrtWidth();
691  SwFlyPortion *pFlyPortion = nullptr;
692 
693  SwRect aLineVert( rCurrRect );
694  if ( GetTextFrame()->IsRightToLeft() )
695  GetTextFrame()->SwitchLTRtoRTL( aLineVert );
696  if ( GetTextFrame()->IsVertical() )
698 
699  // aFlyRect is document-global!
700  SwRect aFlyRect( aTextFly.GetFrame( aLineVert ) );
701 
702  if ( GetTextFrame()->IsRightToLeft() )
703  GetTextFrame()->SwitchRTLtoLTR( aFlyRect );
704  if ( GetTextFrame()->IsVertical() )
706 
707  // If a Frame overlapps we open a Portion
708  if( aFlyRect.HasArea() )
709  {
710  // aLocal is frame-local
711  SwRect aLocal( aFlyRect );
712  aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
713  if( nCurrWidth > aLocal.Left() )
714  aLocal.Left( nCurrWidth );
715 
716  // If the rect is wider than the line, we adjust it to the right size
717  const long nLocalWidth = aLocal.Left() + aLocal.Width();
718  if( nRealWidth < nLocalWidth )
719  aLocal.Width( nRealWidth - aLocal.Left() );
721  pFlyPortion = new SwFlyPortion( aLocal );
722  pFlyPortion->Height( sal_uInt16( rCurrRect.Height() ) );
723  // The Width could be smaller than the FixWidth, thus:
724  pFlyPortion->AdjFixWidth();
725  }
726  return pFlyPortion;
727 }
728 
729 // CalcDropAdjust is called at the end by Format() if needed
731 {
732  OSL_ENSURE( 1<GetDropLines() && SvxAdjust::Left!=GetAdjust() && SvxAdjust::Block!=GetAdjust(),
733  "CalcDropAdjust: No reason for DropAdjustment." );
734 
735  const sal_uInt16 nLineNumber = GetLineNr();
736 
737  // 1) Skip dummies
738  Top();
739 
740  if( !m_pCurr->IsDummy() || NextLine() )
741  {
742  // Adjust first
743  GetAdjusted();
744 
746 
747  // 2) Make sure we include the ropPortion
748  // 3) pLeft is the GluePor preceding the DropPor
749  if( pPor->InGlueGrp() && pPor->GetNextPortion()
750  && pPor->GetNextPortion()->IsDropPortion() )
751  {
752  const SwLinePortion *pDropPor = pPor->GetNextPortion();
753  SwGluePortion *pLeft = static_cast<SwGluePortion*>( pPor );
754 
755  // 4) pRight: Find the GluePor coming after the DropPor
756  pPor = pPor->GetNextPortion();
757  while( pPor && !pPor->InFixMargGrp() )
758  pPor = pPor->GetNextPortion();
759 
760  SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
761  static_cast<SwGluePortion*>(pPor) : nullptr;
762  if( pRight && pRight != pLeft )
763  {
764  // 5) Calculate nMinLeft. Who is the most to left?
765  const auto nDropLineStart =
766  GetLineStart() + pLeft->Width() + pDropPor->Width();
767  auto nMinLeft = nDropLineStart;
768  for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
769  {
770  if( NextLine() )
771  {
772  // Adjust first
773  GetAdjusted();
774 
775  pPor = m_pCurr->GetFirstPortion();
776  const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
777  static_cast<SwMarginPortion*>(pPor) : nullptr;
778  if( !pMar )
779  nMinLeft = 0;
780  else
781  {
782  const auto nLineStart =
783  GetLineStart() + pMar->Width();
784  if( nMinLeft > nLineStart )
785  nMinLeft = nLineStart;
786  }
787  }
788  }
789 
790  // 6) Distribute the Glue anew between pLeft and pRight
791  if( nMinLeft < nDropLineStart )
792  {
793  // The Glue is always passed from pLeft to pRight, so that
794  // the text moves to the left.
795  const auto nGlue = nDropLineStart - nMinLeft;
796  if( !nMinLeft )
797  pLeft->MoveAllGlue( pRight );
798  else
799  pLeft->MoveGlue( pRight, nGlue );
800  }
801  }
802  }
803  }
804 
805  if( nLineNumber != GetLineNr() )
806  {
807  Top();
808  while( nLineNumber != GetLineNr() && Next() )
809  ;
810  }
811 }
812 
814 {
815  Top();
816  SwRepaint &rRepaint = GetInfo().GetParaPortion()->GetRepaint();
817  if( rRepaint.Top() > Y() )
818  rRepaint.Top( Y() );
819  for( sal_uInt16 i = 1; i < GetDropLines(); ++i )
820  NextLine();
821  const SwTwips nBottom = Y() + GetLineHeight() - 1;
822  if( rRepaint.Bottom() < nBottom )
823  rRepaint.Bottom( nBottom );
824 }
825 
826 /* 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:155
void SwitchVerticalToHorizontal(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from vertical to horizontal layout...
Definition: txtfrm.cxx:590
void SetKanaComp(std::unique_ptr< std::deque< sal_uInt16 >> pNew)
Definition: porlay.hxx:182
void CalcDropRepaint()
Definition: itradj.cxx:813
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:1426
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:262
std::deque< sal_uInt16 > & GetKanaComp()
Definition: porlay.hxx:185
const SwLineLayout * NextLine()
Definition: itrtxt.cxx:125
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:483
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:2061
void Height(long nNew)
Definition: swrect.hxx:189
void MoveGlue(SwGluePortion *pTarget, const long nPrtGlue)
Definition: porglue.cxx:110
ComplexTextLayoutFlags GetLayoutMode() const
TextFrameIndex GetNextAttr() const
Definition: itratr.cxx:732
long SwTwips
Definition: swtypes.hxx:49
void SetLayoutMode(ComplexTextLayoutFlags nTextLayoutMode)
void CalcDropAdjust()
Definition: itradj.cxx:730
const SwLineLayout * Next()
Definition: itrtxt.cxx:109
void Pos(const Point &rNew)
Definition: swrect.hxx:167
SwTwips CalcKanaAdj(SwLineLayout *pCurr)
Definition: itradj.cxx:397
sal_uInt16 GetFix() const
Definition: porglue.hxx:56
Of course Writer needs its own rectangles.
Definition: swrect.hxx:34
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:2028
sal_uInt16 GetLLSpaceAddCount() const
Definition: porlay.hxx:169
void Top(const long nTop)
Definition: swrect.hxx:202
void CalcAdjLine(SwLineLayout *pCurr)
Definition: itradj.cxx:655
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
void SetOfst(const SwTwips nNew)
Definition: porlay.hxx:71
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:301
static bool IsArabicText(const OUString &rText, TextFrameIndex nStt, TextFrameIndex nLen)
Checks if text is Arabic text.
Definition: porlay.cxx:1911
void CalcFlyAdjust(SwLineLayout *pCurr)
Definition: itradj.cxx:580
bool InGlueGrp() const
Definition: porlin.hxx:99
virtual SwLinePortion * Append(SwLinePortion *pPortion)
Definition: porlin.cxx:193
bool IsDummy() const
Definition: porlay.hxx:135
SwTwips GetLineStart() const
Definition: itrcrsr.cxx:372
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
int i
SwLinePortion * GetFirstPortion() const
Definition: porlay.cxx:673
SwTwips GetLeftMargin() const
Definition: itrtxt.hxx:328
void FinishSpaceAdd()
Definition: porlay.hxx:168
SwFlyPortion * CalcFlyPortion(const long nRealWidth, const SwRect &rCurrRect)
Definition: itradj.cxx:685
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:693
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:193
void Bottom(const long nBottom)
Definition: swrect.hxx:207
void ClearNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen)
Clear forced blank justification for a given line.
Definition: porlay.cxx:2045
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:724
TextFrameIndex GetLength() const
Definition: itrtxt.hxx:86
SwRepaint & GetRepaint()
Definition: porlay.hxx:264
void Width(long nNew)
Definition: swrect.hxx:185
bool IsDouble() const
Definition: pormulti.hxx:131
SwLinePortion * FindLastPortion()
Definition: porlin.cxx:182
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:963
void ClearKashidaInvalid(size_t nKashPos)
Definition: porlay.cxx:1953
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:529
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:146
const SwLineLayout * GetCurr() const
Definition: itrtxt.hxx:83
#define SPACING_PRECISION_FACTOR
Definition: scriptinfo.hxx:37
long GetMinKashida() const
#define MIN_TAB_WIDTH
Definition: itradj.cxx:35
SwDoc & GetDoc()
Definition: txtfrm.hxx:448
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:2004
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:165
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:1826
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