LibreOffice Module sw (master)  1
pormulti.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 <deque>
21 #include <memory>
22 
23 #include <hintids.hxx>
24 
25 #include <editeng/twolinesitem.hxx>
27 #include <vcl/outdev.hxx>
28 #include <txatbase.hxx>
29 #include <fmtruby.hxx>
30 #include <txtatr.hxx>
31 #include <charfmt.hxx>
32 #include <layfrm.hxx>
33 #include <SwPortionHandler.hxx>
34 #include "pormulti.hxx"
35 #include "inftxt.hxx"
36 #include "itrpaint.hxx"
37 #include <viewopt.hxx>
38 #include "itrform2.hxx"
39 #include "porfld.hxx"
40 #include "porglue.hxx"
41 #include "porrst.hxx"
42 #include <pagefrm.hxx>
43 #include <rowfrm.hxx>
44 #include <tgrditem.hxx>
45 #include <swtable.hxx>
46 #include <fmtfsize.hxx>
47 #include <doc.hxx>
48 
49 using namespace ::com::sun::star;
50 
51 // A SwMultiPortion is not a simple portion,
52 // it's a container, which contains almost a SwLineLayoutPortion.
53 // This SwLineLayout could be followed by other textportions via pPortion
54 // and by another SwLineLayout via pNext to realize a doubleline portion.
56 {
57 }
58 
60 {
61  OSL_FAIL( "Don't try SwMultiPortion::Paint, try SwTextPainter::PaintMultiPortion" );
62 }
63 
64 // Summarize the internal lines to calculate the (external) size.
65 // The internal line has to calculate first.
67 {
68  Width( 0 );
69  Height( 0 );
70  SetAscent( 0 );
71  SetFlyInContent( false );
72  SwLineLayout *pLay = &GetRoot();
73  do
74  {
75  pLay->CalcLine( rLine, rInf );
76  if( rLine.IsFlyInCntBase() )
77  SetFlyInContent( true );
78  if( IsRuby() && ( OnTop() == ( pLay == &GetRoot() ) ) )
79  {
80  // An empty phonetic line don't need an ascent or a height.
81  if( !pLay->Width() )
82  {
83  pLay->SetAscent( 0 );
84  pLay->Height( 0 );
85  }
86  if( OnTop() )
87  SetAscent( GetAscent() + pLay->Height() );
88  }
89  else
90  SetAscent( GetAscent() + pLay->GetAscent() );
91 
92  // Increase the line height, except for ruby text on the right.
93  if ( !IsRuby() || !OnRight() || pLay == &GetRoot() )
94  Height( Height() + pLay->Height() );
95  else
96  {
97  // We already added the width after building the portion,
98  // so no need to add it twice.
99  break;
100  }
101 
102  if( Width() < pLay->Width() )
103  Width( pLay->Width() );
104  pLay = pLay->GetNext();
105  } while ( pLay );
106  if( !HasBrackets() )
107  return;
108 
109  sal_uInt16 nTmp = static_cast<SwDoubleLinePortion*>(this)->GetBrackets()->nHeight;
110  if( nTmp > Height() )
111  {
112  const sal_uInt16 nAdd = ( nTmp - Height() ) / 2;
113  GetRoot().SetAscent( GetRoot().GetAscent() + nAdd );
114  GetRoot().Height( GetRoot().Height() + nAdd );
115  Height( nTmp );
116  }
117  nTmp = static_cast<SwDoubleLinePortion*>(this)->GetBrackets()->nAscent;
118  if( nTmp > GetAscent() )
119  SetAscent( nTmp );
120 }
121 
123 {
124  return 0;
125 }
126 
128 {
129  return false;
130 }
131 
133 {
134  rPH.Text( GetLen(), GetWhichPor() );
135 }
136 
138 {
139  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwMultiPortion"));
140  (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this);
141  (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), BAD_CAST(typeid(*this).name()));
142 
143  for (const SwLineLayout* pLine = &GetRoot(); pLine; pLine = pLine->GetNext())
144  {
145  pLine->dumpAsXml(pWriter);
146  }
147 
148  (void)xmlTextWriterEndElement(pWriter);
149 }
150 
151 // sets the tabulator-flag, if there's any tabulator-portion inside.
153 {
155  // First line
156  for( m_bTab1 = m_bTab2 = false; pPor; pPor = pPor->GetNextPortion() )
157  if( pPor->InTabGrp() )
158  SetTab1( true );
159  if( GetRoot().GetNext() )
160  {
161  // Second line
162  pPor = GetRoot().GetNext()->GetFirstPortion();
163  do
164  {
165  if( pPor->InTabGrp() )
166  SetTab2( true );
167  pPor = pPor->GetNextPortion();
168  } while ( pPor );
169  }
170 }
171 
173  TextFrameIndex const nEnd, bool bRTL )
174  : SwMultiPortion( nEnd )
175 {
176  const SvxCharRotateItem* pRot = static_cast<const SvxCharRotateItem*>(rCreate.pItem);
177  if( !pRot )
178  {
179  const SwTextAttr& rAttr = *rCreate.pAttr;
180  const SfxPoolItem *const pItem =
182  if ( pItem )
183  {
184  pRot = static_cast<const SvxCharRotateItem*>(pItem);
185  }
186  }
187  if( pRot )
188  {
189  sal_uInt8 nDir;
190  if ( bRTL )
191  nDir = pRot->IsBottomToTop() ? 3 : 1;
192  else
193  nDir = pRot->IsBottomToTop() ? 1 : 3;
194 
195  SetDirection( nDir );
196  }
197 }
198 
200  : SwMultiPortion( nEnd ), m_nLevel( nLv )
201 {
202  SetBidi();
203 
204  if ( m_nLevel % 2 )
206  else
208 }
209 
211 {
212  return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt(rInf)) * nSpaceAdd / SPACING_PRECISION_FACTOR;
213 }
214 
215 bool SwBidiPortion::ChgSpaceAdd( SwLineLayout* pCurr, tools::Long nSpaceAdd ) const
216 {
217  if( !HasTabulator() && nSpaceAdd > 0 && !pCurr->IsSpaceAdd() )
218  {
219  pCurr->CreateSpaceAdd();
220  pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
221  return true;
222  }
223 
224  return false;
225 }
226 
228 {
229  // Calculate number of blanks for justified alignment
230  TextFrameIndex nTmpStart = rInf.GetIdx();
231  TextFrameIndex nNull(0);
232  TextFrameIndex nBlanks(0);
233 
234  for (SwLinePortion* pPor = GetRoot().GetFirstPortion(); pPor; pPor = pPor->GetNextPortion())
235  {
236  if( pPor->InTextGrp() )
237  nBlanks = nBlanks + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
238  else if ( pPor->IsMultiPortion() &&
239  static_cast<SwMultiPortion*>(pPor)->IsBidi() )
240  nBlanks = nBlanks + static_cast<SwBidiPortion*>(pPor)->GetSpaceCnt( rInf );
241 
242  const_cast<SwTextSizeInfo &>(rInf).SetIdx( rInf.GetIdx() + pPor->GetLen() );
243  }
244  const_cast<SwTextSizeInfo &>(rInf).SetIdx( nTmpStart );
245  return nBlanks;
246 }
247 
248 // This constructor is for the continuation of a doubleline portion
249 // in the next line.
250 // It takes the same brackets and if the original has no content except
251 // brackets, these will be deleted.
253  SwDoubleLinePortion& rDouble, TextFrameIndex const nEnd)
254  : SwMultiPortion(nEnd)
255  , m_nLineDiff(0)
256  , m_nBlank1(0)
257  , m_nBlank2(0)
258 {
259  SetDirection( rDouble.GetDirection() );
260  SetDouble();
261  if( rDouble.GetBrackets() )
262  {
263  SetBrackets( rDouble );
264  // An empty multiportion needs no brackets.
265  // Notice: GetLen() might be zero, if the multiportion contains
266  // the second part of a field and the width might be zero, if
267  // it contains a note only. In this cases the brackets are okay.
268  // But if the length and the width are both zero, the portion
269  // is really empty.
270  if( rDouble.Width() == rDouble.BracketWidth() )
271  rDouble.ClearBrackets();
272  }
273 }
274 
275 // This constructor uses the textattribute to get the right brackets.
276 // The textattribute could be a 2-line-attribute or a character- or
277 // internet style, which contains the 2-line-attribute.
279  const SwMultiCreator& rCreate, TextFrameIndex const nEnd)
280  : SwMultiPortion(nEnd)
281  , m_pBracket(new SwBracket)
282  , m_nLineDiff(0)
283  , m_nBlank1(0)
284  , m_nBlank2(0)
285 {
286  m_pBracket->nAscent = 0;
287  m_pBracket->nHeight = 0;
288  m_pBracket->nPreWidth = 0;
289  m_pBracket->nPostWidth = 0;
290 
291  SetDouble();
292  const SvxTwoLinesItem* pTwo = static_cast<const SvxTwoLinesItem*>(rCreate.pItem);
293  if( pTwo )
294  m_pBracket->nStart = TextFrameIndex(0);
295  else
296  {
297  const SwTextAttr& rAttr = *rCreate.pAttr;
298  m_pBracket->nStart = rCreate.nStartOfAttr;
299 
300  const SfxPoolItem * const pItem =
302  if ( pItem )
303  {
304  pTwo = static_cast<const SvxTwoLinesItem*>(pItem);
305  }
306  }
307  if( pTwo )
308  {
309  m_pBracket->cPre = pTwo->GetStartBracket();
310  m_pBracket->cPost = pTwo->GetEndBracket();
311  }
312  else
313  {
314  m_pBracket->cPre = 0;
315  m_pBracket->cPost = 0;
316  }
317  SwFontScript nTmp = SW_SCRIPTS;
318  if( m_pBracket->cPre > 255 )
319  {
320  OUString aText(m_pBracket->cPre);
321  nTmp = SwScriptInfo::WhichFont(0, aText);
322  }
323  m_pBracket->nPreScript = nTmp;
324  nTmp = SW_SCRIPTS;
325  if( m_pBracket->cPost > 255 )
326  {
327  OUString aText(m_pBracket->cPost);
328  nTmp = SwScriptInfo::WhichFont(0, aText);
329  }
330  m_pBracket->nPostScript = nTmp;
331 
332  if( !m_pBracket->cPre && !m_pBracket->cPost )
333  {
334  m_pBracket.reset();
335  }
336 
337  // double line portions have the same direction as the frame directions
338  if ( rCreate.nLevel % 2 )
340  else
342 }
343 
344 // paints the wished bracket,
345 // if the multiportion has surrounding brackets.
346 // The X-position of the SwTextPaintInfo will be modified:
347 // the open bracket sets position behind itself,
348 // the close bracket in front of itself.
350  tools::Long nSpaceAdd,
351  bool bOpen ) const
352 {
353  sal_Unicode cCh = bOpen ? m_pBracket->cPre : m_pBracket->cPost;
354  if( !cCh )
355  return;
356  const sal_uInt16 nChWidth = bOpen ? PreWidth() : PostWidth();
357  if( !nChWidth )
358  return;
359  if( !bOpen )
360  rInf.X( rInf.X() + Width() - PostWidth() +
361  ( nSpaceAdd > 0 ? CalcSpacing( nSpaceAdd, rInf ) : 0 ) );
362 
363  SwBlankPortion aBlank( cCh, true );
364  aBlank.SetAscent( m_pBracket->nAscent );
365  aBlank.Width( nChWidth );
366  aBlank.Height( m_pBracket->nHeight );
367  {
368  SwFont aTmpFnt( *rInf.GetFont() );
369  SwFontScript nAct = bOpen ? m_pBracket->nPreScript : m_pBracket->nPostScript;
370  if( SW_SCRIPTS > nAct )
371  aTmpFnt.SetActual( nAct );
372  aTmpFnt.SetProportion( 100 );
373  SwFontSave aSave( rInf, &aTmpFnt );
374  aBlank.Paint( rInf );
375  }
376  if( bOpen )
377  rInf.X( rInf.X() + PreWidth() );
378 }
379 
380 // creates the bracket-structure
381 // and fills it, if not both characters are 0x00.
383 {
384  if( rDouble.m_pBracket )
385  {
386  m_pBracket.reset( new SwBracket );
387  m_pBracket->cPre = rDouble.m_pBracket->cPre;
388  m_pBracket->cPost = rDouble.m_pBracket->cPost;
389  m_pBracket->nPreScript = rDouble.m_pBracket->nPreScript;
390  m_pBracket->nPostScript = rDouble.m_pBracket->nPostScript;
391  m_pBracket->nStart = rDouble.m_pBracket->nStart;
392  }
393 }
394 
395 // calculates the size of the brackets => pBracket,
396 // reduces the nMaxWidth-parameter ( minus bracket-width )
397 // and moves the rInf-x-position behind the opening bracket.
399 {
400  nMaxWidth -= rInf.X();
401  SwFont aTmpFnt( *rInf.GetFont() );
402  aTmpFnt.SetProportion( 100 );
403  m_pBracket->nAscent = 0;
404  m_pBracket->nHeight = 0;
405  if( m_pBracket->cPre )
406  {
407  OUString aStr( m_pBracket->cPre );
408  SwFontScript nActualScr = aTmpFnt.GetActual();
409  if( SW_SCRIPTS > m_pBracket->nPreScript )
410  aTmpFnt.SetActual( m_pBracket->nPreScript );
411  SwFontSave aSave( rInf, &aTmpFnt );
412  SwPosSize aSize = rInf.GetTextSize( aStr );
413  m_pBracket->nAscent = rInf.GetAscent();
414  m_pBracket->nHeight = aSize.Height();
415  aTmpFnt.SetActual( nActualScr );
416  if( nMaxWidth > aSize.Width() )
417  {
418  m_pBracket->nPreWidth = aSize.Width();
419  nMaxWidth -= aSize.Width();
420  rInf.X( rInf.X() + aSize.Width() );
421  }
422  else
423  {
424  m_pBracket->nPreWidth = 0;
425  nMaxWidth = 0;
426  }
427  }
428  else
429  m_pBracket->nPreWidth = 0;
430  if( m_pBracket->cPost )
431  {
432  OUString aStr( m_pBracket->cPost );
433  if( SW_SCRIPTS > m_pBracket->nPostScript )
434  aTmpFnt.SetActual( m_pBracket->nPostScript );
435  SwFontSave aSave( rInf, &aTmpFnt );
436  SwPosSize aSize = rInf.GetTextSize( aStr );
437  const sal_uInt16 nTmpAsc = rInf.GetAscent();
438  if( nTmpAsc > m_pBracket->nAscent )
439  {
440  m_pBracket->nHeight += nTmpAsc - m_pBracket->nAscent;
441  m_pBracket->nAscent = nTmpAsc;
442  }
443  if( aSize.Height() > m_pBracket->nHeight )
444  m_pBracket->nHeight = aSize.Height();
445  if( nMaxWidth > aSize.Width() )
446  {
447  m_pBracket->nPostWidth = aSize.Width();
448  nMaxWidth -= aSize.Width();
449  }
450  else
451  {
452  m_pBracket->nPostWidth = 0;
453  nMaxWidth = 0;
454  }
455  }
456  else
457  m_pBracket->nPostWidth = 0;
458  nMaxWidth += rInf.X();
459 }
460 
461 // calculates the number of blanks in each line and
462 // the difference of the width of the two lines.
463 // These results are used from the text adjustment.
465 {
467  TextFrameIndex nNull(0);
468  TextFrameIndex nStart = rInf.GetIdx();
469  SetTab1( false );
470  SetTab2( false );
471  for (m_nBlank1 = TextFrameIndex(0); pPor; pPor = pPor->GetNextPortion())
472  {
473  if( pPor->InTextGrp() )
474  m_nBlank1 = m_nBlank1 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
475  rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
476  if( pPor->InTabGrp() )
477  SetTab1( true );
478  }
479  m_nLineDiff = GetRoot().Width();
480  if( GetRoot().GetNext() )
481  {
482  pPor = GetRoot().GetNext()->GetFirstPortion();
483  m_nLineDiff -= GetRoot().GetNext()->Width();
484  }
485  for (m_nBlank2 = TextFrameIndex(0); pPor; pPor = pPor->GetNextPortion())
486  {
487  if( pPor->InTextGrp() )
488  m_nBlank2 = m_nBlank2 + static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nNull );
489  rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
490  if( pPor->InTabGrp() )
491  SetTab2( true );
492  }
493  rInf.SetIdx( nStart );
494 }
495 
497 {
498  return HasTabulator() ? 0 : sal_Int32(GetSpaceCnt()) * nSpaceAdd / SPACING_PRECISION_FACTOR;
499 }
500 
501 // Merges the spaces for text adjustment from the inner and outer part.
502 // Inside the doubleline portion the wider line has no spaceadd-array, the
503 // smaller line has such an array to reach width of the wider line.
504 // If the surrounding line has text adjustment and the doubleline portion
505 // contains no tabulator, it is necessary to create/manipulate the inner
506 // space arrays.
508  tools::Long nSpaceAdd ) const
509 {
510  bool bRet = false;
511  if( !HasTabulator() && nSpaceAdd > 0 )
512  {
513  if( !pCurr->IsSpaceAdd() )
514  {
515  // The wider line gets the spaceadd from the surrounding line direct
516  pCurr->CreateSpaceAdd();
517  pCurr->SetLLSpaceAdd( nSpaceAdd, 0 );
518  bRet = true;
519  }
520  else
521  {
522  sal_Int32 const nMyBlank = sal_Int32(GetSmallerSpaceCnt());
523  sal_Int32 const nOther = sal_Int32(GetSpaceCnt());
524  SwTwips nMultiSpace = pCurr->GetLLSpaceAdd( 0 ) * nMyBlank + nOther * nSpaceAdd;
525 
526  if( nMyBlank )
527  nMultiSpace /= sal_Int32(nMyBlank);
528 
529 // pCurr->SetLLSpaceAdd( nMultiSpace, 0 );
530  // #i65711# SetLLSpaceAdd replaces the first value,
531  // instead we want to insert a new first value:
532  std::vector<tools::Long>* pVec = pCurr->GetpLLSpaceAdd();
533  pVec->insert( pVec->begin(), nMultiSpace );
534  bRet = true;
535  }
536  }
537  return bRet;
538 }
539 // cancels the manipulation from SwDoubleLinePortion::ChangeSpaceAdd(..)
541 {
542  pCurr->RemoveFirstLLSpaceAdd();
543  if( !pCurr->GetLLSpaceAddCount() )
544  pCurr->FinishSpaceAdd();
545 }
546 
548 {
549 }
550 
551 // constructs a ruby portion, i.e. an additional text is displayed
552 // beside the main text, e.g. phonetic characters.
554  : SwMultiPortion( nEnd )
555  , m_nRubyOffset( rRuby.GetRubyOffset() )
556  , m_nAdjustment( rRuby.GetAdjustment() )
557 {
558  SetDirection( rRuby.GetDirection() );
559  SetRubyPosition( rRuby.GetRubyPosition() );
560  SetRuby();
561 }
562 
563 // constructs a ruby portion, i.e. an additional text is displayed
564 // beside the main text, e.g. phonetic characters.
566  const IDocumentSettingAccess& rIDocumentSettingAccess,
567  TextFrameIndex const nEnd, TextFrameIndex const nOffs,
568  const SwTextSizeInfo &rInf )
569  : SwMultiPortion( nEnd )
570 {
571  SetRuby();
572  OSL_ENSURE( SwMultiCreatorId::Ruby == rCreate.nId, "Ruby expected" );
573  OSL_ENSURE( RES_TXTATR_CJK_RUBY == rCreate.pAttr->Which(), "Wrong attribute" );
574  const SwFormatRuby& rRuby = rCreate.pAttr->GetRuby();
575  m_nAdjustment = rRuby.GetAdjustment();
576  m_nRubyOffset = nOffs;
577 
578  const SwTextFrame *pFrame = rInf.GetTextFrame();
579  RubyPosition ePos = static_cast<RubyPosition>( rRuby.GetPosition() );
580 
581  // RIGHT is designed for horizontal writing mode only.
582  if ( ePos == RubyPosition::RIGHT && pFrame->IsVertical() )
583  ePos = RubyPosition::ABOVE;
584 
585  // In grid mode we force the ruby text to the upper or lower line
586  if ( rInf.SnapToGrid() )
587  {
588  SwTextGridItem const*const pGrid( GetGridItem(pFrame->FindPageFrame()) );
589  if ( pGrid )
591  }
592 
593  SetRubyPosition( ePos );
594 
595  const SwCharFormat *const pFormat =
596  static_txtattr_cast<SwTextRuby const*>(rCreate.pAttr)->GetCharFormat();
597  std::unique_ptr<SwFont> pRubyFont;
598  if( pFormat )
599  {
600  const SwAttrSet& rSet = pFormat->GetAttrSet();
601  pRubyFont.reset(new SwFont( rFnt ));
602  pRubyFont->SetDiffFnt( &rSet, &rIDocumentSettingAccess );
603 
604  // we do not allow a vertical font for the ruby text
605  pRubyFont->SetVertical( rFnt.GetOrientation() , OnRight() );
606  }
607 
608  OUString aStr = rRuby.GetText().copy( sal_Int32(nOffs) );
609  SwFieldPortion *pField = new SwFieldPortion( aStr, std::move(pRubyFont) );
610  pField->SetNextOffset( nOffs );
611  pField->SetFollow( true );
612 
613  if( OnTop() )
614  GetRoot().SetNextPortion( pField );
615  else
616  {
617  GetRoot().SetNext( new SwLineLayout() );
618  GetRoot().GetNext()->SetNextPortion( pField );
619  }
620 
621  // ruby portions have the same direction as the frame directions
622  if ( rCreate.nLevel % 2 )
623  {
624  // switch right and left ruby adjustment in rtl environment
625  if ( css::text::RubyAdjust_LEFT == m_nAdjustment )
626  m_nAdjustment = css::text::RubyAdjust_RIGHT;
627  else if ( css::text::RubyAdjust_RIGHT == m_nAdjustment )
628  m_nAdjustment = css::text::RubyAdjust_LEFT;
629 
631  }
632  else
634 }
635 
636 // In ruby portion there are different alignments for
637 // the ruby text and the main text.
638 // Left, right, centered and two possibilities of block adjustment
639 // The block adjustment is realized by spacing between the characters,
640 // either with a half space or no space in front of the first letter and
641 // a half space at the end of the last letter.
642 // Notice: the smaller line will be manipulated, normally it's the ruby line,
643 // but it could be the main text, too.
644 // If there is a tabulator in smaller line, no adjustment is possible.
646 {
647  SwTwips nLineDiff = GetRoot().Width() - GetRoot().GetNext()->Width();
648  TextFrameIndex const nOldIdx = rInf.GetIdx();
649  if( !nLineDiff )
650  return;
651  SwLineLayout *pCurr;
652  if( nLineDiff < 0 )
653  { // The first line has to be adjusted.
654  if( GetTab1() )
655  return;
656  pCurr = &GetRoot();
657  nLineDiff = -nLineDiff;
658  }
659  else
660  { // The second line has to be adjusted.
661  if( GetTab2() )
662  return;
663  pCurr = GetRoot().GetNext();
664  rInf.SetIdx( nOldIdx + GetRoot().GetLen() );
665  }
666  sal_uInt16 nLeft = 0; // the space in front of the first letter
667  sal_uInt16 nRight = 0; // the space at the end of the last letter
668  TextFrameIndex nSub(0);
669  switch ( m_nAdjustment )
670  {
671  case css::text::RubyAdjust_CENTER: nRight = o3tl::narrowing<sal_uInt16>(nLineDiff / 2);
672  [[fallthrough]];
673  case css::text::RubyAdjust_RIGHT: nLeft = o3tl::narrowing<sal_uInt16>(nLineDiff - nRight); break;
674  case css::text::RubyAdjust_BLOCK: nSub = TextFrameIndex(1);
675  [[fallthrough]];
676  case css::text::RubyAdjust_INDENT_BLOCK:
677  {
678  TextFrameIndex nCharCnt(0);
679  SwLinePortion *pPor;
680  for( pPor = pCurr->GetFirstPortion(); pPor; pPor = pPor->GetNextPortion() )
681  {
682  if( pPor->InTextGrp() )
683  static_cast<SwTextPortion*>(pPor)->GetSpaceCnt( rInf, nCharCnt );
684  rInf.SetIdx( rInf.GetIdx() + pPor->GetLen() );
685  }
686  if( nCharCnt > nSub )
687  {
688  SwTwips nCalc = nLineDiff / sal_Int32(nCharCnt - nSub);
689  short nTmp;
690  if( nCalc < SHRT_MAX )
691  nTmp = -short(nCalc);
692  else
693  nTmp = SHRT_MIN;
694 
695  pCurr->CreateSpaceAdd( SPACING_PRECISION_FACTOR * nTmp );
696  nLineDiff -= nCalc * (sal_Int32(nCharCnt) - 1);
697  }
698  if( nLineDiff > 1 )
699  {
700  nRight = o3tl::narrowing<sal_uInt16>(nLineDiff / 2);
701  nLeft = o3tl::narrowing<sal_uInt16>(nLineDiff - nRight);
702  }
703  break;
704  }
705  default: OSL_FAIL( "New ruby adjustment" );
706  }
707  if( nLeft || nRight )
708  {
709  if( !pCurr->GetNextPortion() )
711  if( nLeft )
712  {
713  SwMarginPortion *pMarg = new SwMarginPortion;
714  pMarg->AddPrtWidth( nLeft );
715  pMarg->SetNextPortion( pCurr->GetNextPortion() );
716  pCurr->SetNextPortion( pMarg );
717  }
718  if( nRight )
719  {
720  SwMarginPortion *pMarg = new SwMarginPortion;
721  pMarg->AddPrtWidth( nRight );
722  pCurr->FindLastPortion()->Append( pMarg );
723  }
724  }
725 
726  pCurr->Width( Width() );
727  rInf.SetIdx( nOldIdx );
728 }
729 
730 // has to change the nRubyOffset, if there's a fieldportion
731 // in the phonetic line.
732 // The nRubyOffset is the position in the rubystring, where the
733 // next SwRubyPortion has start the displaying of the phonetics.
735 {
736  const SwLineLayout *pCurr = &GetRoot();
737  if( !OnTop() )
738  {
739  pCurr = pCurr->GetNext();
740  if( !pCurr )
741  return;
742  }
743  const SwLinePortion *pPor = pCurr->GetFirstPortion();
744  const SwFieldPortion *pField = nullptr;
745  while( pPor )
746  {
747  if( pPor->InFieldGrp() )
748  pField = static_cast<const SwFieldPortion*>(pPor);
749  pPor = pPor->GetNextPortion();
750  }
751  if( pField )
752  {
753  if( pField->HasFollow() )
754  m_nRubyOffset = pField->GetNextOffset();
755  else
757  }
758 }
759 
760 // A little helper function for GetMultiCreator(..)
761 // It extracts the 2-line-format from a 2-line-attribute or a character style.
762 // The rValue is set to true, if the 2-line-attribute's value is set and
763 // no 2-line-format reference is passed. If there is a 2-line-format reference,
764 // then the rValue is set only, if the 2-line-attribute's value is set _and_
765 // the 2-line-formats has the same brackets.
766 static bool lcl_Check2Lines(const SfxPoolItem *const pItem,
767  const SvxTwoLinesItem* &rpRef, bool &rValue)
768 {
769  if( pItem )
770  {
771  rValue = static_cast<const SvxTwoLinesItem*>(pItem)->GetValue();
772  if( !rpRef )
773  rpRef = static_cast<const SvxTwoLinesItem*>(pItem);
774  else if( static_cast<const SvxTwoLinesItem*>(pItem)->GetEndBracket() !=
775  rpRef->GetEndBracket() ||
776  static_cast<const SvxTwoLinesItem*>(pItem)->GetStartBracket() !=
777  rpRef->GetStartBracket() )
778  rValue = false;
779  return true;
780  }
781  return false;
782 }
783 
784 static bool lcl_Has2Lines(const SwTextAttr& rAttr,
785  const SvxTwoLinesItem* &rpRef, bool &rValue)
786 {
788  return lcl_Check2Lines(pItem, rpRef, rValue);
789 }
790 
791 // is a little help function for GetMultiCreator(..)
792 // It extracts the charrotation from a charrotate-attribute or a character style.
793 // The rValue is set to true, if the charrotate-attribute's value is set and
794 // no charrotate-format reference is passed.
795 // If there is a charrotate-format reference, then the rValue is set only,
796 // if the charrotate-attribute's value is set _and_ identical
797 // to the charrotate-format's value.
798 static bool lcl_CheckRotation(const SfxPoolItem *const pItem,
799  const SvxCharRotateItem* &rpRef, bool &rValue)
800 {
801  if ( pItem )
802  {
803  rValue = static_cast<const SvxCharRotateItem*>(pItem)->GetValue() != 0_deg10;
804  if( !rpRef )
805  rpRef = static_cast<const SvxCharRotateItem*>(pItem);
806  else if( static_cast<const SvxCharRotateItem*>(pItem)->GetValue() !=
807  rpRef->GetValue() )
808  rValue = false;
809  return true;
810  }
811 
812  return false;
813 }
814 
815 static bool lcl_HasRotation(const SwTextAttr& rAttr,
816  const SvxCharRotateItem* &rpRef, bool &rValue)
817 {
818  const SfxPoolItem* pItem = CharFormat::GetItem( rAttr, RES_CHRATR_ROTATE );
819  return lcl_CheckRotation(pItem, rpRef, rValue);
820 }
821 
822 namespace sw {
823  namespace {
824 
825  // need to use a very special attribute iterator here that returns
826  // both the hints and the nodes, so that GetMultiCreator() can handle
827  // items in the nodes' set properly
828  class MergedAttrIterMulti
829  : public MergedAttrIterBase
830  {
831  private:
832  bool m_First = true;
833  public:
834  MergedAttrIterMulti(SwTextFrame const& rFrame) : MergedAttrIterBase(rFrame) {}
835  SwTextAttr const* NextAttr(SwTextNode const*& rpNode);
836  // can't have operator= because m_pMerged/m_pNode const
837  void Assign(MergedAttrIterMulti const& rOther)
838  {
839  assert(m_pMerged == rOther.m_pMerged);
840  assert(m_pNode == rOther.m_pNode);
841  m_CurrentExtent = rOther.m_CurrentExtent;
842  m_CurrentHint = rOther.m_CurrentHint;
843  m_First = rOther.m_First;
844  }
845  };
846 
847  }
848 
849  SwTextAttr const* MergedAttrIterMulti::NextAttr(SwTextNode const*& rpNode)
850  {
851  if (m_First)
852  {
853  m_First = false;
854  rpNode = m_pMerged
855  ? !m_pMerged->extents.empty()
856  ? m_pMerged->extents[0].pNode
857  : m_pMerged->pFirstNode
858  : m_pNode;
859  return nullptr;
860  }
861  if (m_pMerged)
862  {
863  while (m_CurrentExtent < m_pMerged->extents.size())
864  {
865  sw::Extent const& rExtent(m_pMerged->extents[m_CurrentExtent]);
866  if (SwpHints const*const pHints = rExtent.pNode->GetpSwpHints())
867  {
868  while (m_CurrentHint < pHints->Count())
869  {
870  SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
871  if (rExtent.nEnd < pHint->GetStart())
872  {
873  break;
874  }
875  ++m_CurrentHint;
876  if (rExtent.nStart <= pHint->GetStart())
877  {
878  rpNode = rExtent.pNode;
879  return pHint;
880  }
881  }
882  }
883  ++m_CurrentExtent;
884  if (m_CurrentExtent < m_pMerged->extents.size() &&
885  rExtent.pNode != m_pMerged->extents[m_CurrentExtent].pNode)
886  {
887  m_CurrentHint = 0; // reset
888  rpNode = m_pMerged->extents[m_CurrentExtent].pNode;
889  return nullptr;
890  }
891  }
892  return nullptr;
893  }
894  else
895  {
896  SwpHints const*const pHints(m_pNode->GetpSwpHints());
897  if (pHints)
898  {
899  if (m_CurrentHint < pHints->Count())
900  {
901  SwTextAttr const*const pHint(pHints->Get(m_CurrentHint));
902  ++m_CurrentHint;
903  rpNode = m_pNode;
904  return pHint;
905  }
906  }
907  return nullptr;
908  }
909  }
910 }
911 
912 // If we (e.g. the position rPos) are inside a two-line-attribute or
913 // a ruby-attribute, the attribute will be returned in a SwMultiCreator-struct,
914 // otherwise the function returns zero.
915 // The rPos parameter is set to the end of the multiportion,
916 // normally this is the end of the attribute,
917 // but sometimes it is the start of another attribute, which finished or
918 // interrupts the first attribute.
919 // E.g. a ruby portion interrupts a 2-line-attribute, a 2-line-attribute
920 // with different brackets interrupts another 2-line-attribute.
921 std::optional<SwMultiCreator> SwTextSizeInfo::GetMultiCreator(TextFrameIndex &rPos,
922  SwMultiPortion const * pMulti ) const
923 {
924  SwScriptInfo& rSI = const_cast<SwParaPortion*>(GetParaPortion())->GetScriptInfo();
925 
926  // get the last embedding level
927  sal_uInt8 nCurrLevel;
928  if ( pMulti )
929  {
930  OSL_ENSURE( pMulti->IsBidi(), "Nested MultiPortion is not BidiPortion" );
931  // level associated with bidi-portion;
932  nCurrLevel = static_cast<SwBidiPortion const *>(pMulti)->GetLevel();
933  }
934  else
935  // no nested bidi portion required
936  nCurrLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
937 
938  // check if there is a field at rPos:
939  sal_uInt8 nNextLevel = nCurrLevel;
940  bool bFieldBidi = false;
941 
942  if (rPos < TextFrameIndex(GetText().getLength()) && CH_TXTATR_BREAKWORD == GetChar(rPos))
943  {
944  bFieldBidi = true;
945  }
946  else
947  nNextLevel = rSI.DirType( rPos );
948 
949  if (TextFrameIndex(GetText().getLength()) != rPos && nNextLevel > nCurrLevel)
950  {
951  rPos = bFieldBidi ? rPos + TextFrameIndex(1) : rSI.NextDirChg(rPos, &nCurrLevel);
952  if (TextFrameIndex(COMPLETE_STRING) == rPos)
953  return {};
954  SwMultiCreator aRet;
955  aRet.pItem = nullptr;
956  aRet.pAttr = nullptr;
957  aRet.nStartOfAttr = TextFrameIndex(-1);
959  aRet.nLevel = nCurrLevel + 1;
960  return aRet;
961  }
962 
963  // a bidi portion can only contain other bidi portions
964  if ( pMulti )
965  return {};
966 
967  // need the node that contains input rPos
968  std::pair<SwTextNode const*, sal_Int32> startPos(m_pFrame->MapViewToModel(rPos));
969  const SvxCharRotateItem* pActiveRotateItem(nullptr);
970  const SfxPoolItem* pNodeRotateItem(nullptr);
971  const SvxTwoLinesItem* pActiveTwoLinesItem(nullptr);
972  const SfxPoolItem* pNodeTwoLinesItem(nullptr);
973  SwTextAttr const* pActiveTwoLinesHint(nullptr);
974  SwTextAttr const* pActiveRotateHint(nullptr);
975  const SwTextAttr *pRuby = nullptr;
976  sw::MergedAttrIterMulti iterAtStartOfNode(*m_pFrame);
977  bool bTwo = false;
978  bool bRot = false;
979 
980  for (sw::MergedAttrIterMulti iter = *m_pFrame; ; )
981  {
982  SwTextNode const* pNode(nullptr);
983  SwTextAttr const*const pAttr = iter.NextAttr(pNode);
984  if (!pNode)
985  {
986  break;
987  }
988  if (pAttr)
989  {
990  assert(pNode->GetIndex() <= startPos.first->GetIndex()); // should break earlier
991  if (startPos.first->GetIndex() <= pNode->GetIndex())
992  {
993  if (startPos.first->GetIndex() != pNode->GetIndex()
994  || startPos.second < pAttr->GetStart())
995  {
996  break;
997  }
998  if (startPos.second < pAttr->GetAnyEnd())
999  {
1000  // sw_redlinehide: ruby *always* splits
1001  if (RES_TXTATR_CJK_RUBY == pAttr->Which())
1002  pRuby = pAttr;
1003  else
1004  {
1005  const SvxCharRotateItem* pRoTmp = nullptr;
1006  if (lcl_HasRotation( *pAttr, pRoTmp, bRot ))
1007  {
1008  pActiveRotateHint = bRot ? pAttr : nullptr;
1009  pActiveRotateItem = pRoTmp;
1010  }
1011  const SvxTwoLinesItem* p2Tmp = nullptr;
1012  if (lcl_Has2Lines( *pAttr, p2Tmp, bTwo ))
1013  {
1014  pActiveTwoLinesHint = bTwo ? pAttr : nullptr;
1015  pActiveTwoLinesItem = p2Tmp;
1016  }
1017  }
1018  }
1019  }
1020  }
1021  else if (pNode) // !pAttr && pNode means the node changed
1022  {
1023  if (startPos.first->GetIndex() < pNode->GetIndex())
1024  {
1025  break; // only one node initially
1026  }
1027  if (startPos.first->GetIndex() == pNode->GetIndex())
1028  {
1029  iterAtStartOfNode.Assign(iter);
1030  if (SfxItemState::SET == pNode->GetSwAttrSet().GetItemState(
1031  RES_CHRATR_ROTATE, true, &pNodeRotateItem) &&
1032  static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue())
1033  {
1034  pActiveRotateItem = static_cast<const SvxCharRotateItem*>(pNodeRotateItem);
1035  }
1036  else
1037  {
1038  pNodeRotateItem = nullptr;
1039  }
1040  if (SfxItemState::SET == startPos.first->GetSwAttrSet().GetItemState(
1041  RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem) &&
1042  static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetValue())
1043  {
1044  pActiveTwoLinesItem = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem);
1045  }
1046  else
1047  {
1048  pNodeTwoLinesItem = nullptr;
1049  }
1050  }
1051  }
1052  }
1053  if (!pRuby && !pActiveTwoLinesItem && !pActiveRotateItem)
1054  return {};
1055 
1056  if( pRuby )
1057  { // The winner is ... a ruby attribute and so
1058  // the end of the multiportion is the end of the ruby attribute.
1059  rPos = m_pFrame->MapModelToView(startPos.first, *pRuby->End());
1060  SwMultiCreator aRet;
1061  aRet.pItem = nullptr;
1062  aRet.pAttr = pRuby;
1063  aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
1064  aRet.nId = SwMultiCreatorId::Ruby;
1065  aRet.nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
1066  return aRet;
1067  }
1068  if (pActiveTwoLinesHint ||
1069  (pNodeTwoLinesItem && pNodeTwoLinesItem == pActiveTwoLinesItem &&
1070  rPos < TextFrameIndex(GetText().getLength())))
1071  { // The winner is a 2-line-attribute,
1072  // the end of the multiportion depends on the following attributes...
1073  SwMultiCreator aRet;
1074 
1075  // We note the endpositions of the 2-line attributes in aEnd as stack
1076  std::deque<TextFrameIndex> aEnd;
1077 
1078  // The bOn flag signs the state of the last 2-line attribute in the
1079  // aEnd-stack, it is compatible with the winner-attribute or
1080  // it interrupts the other attribute.
1081  bool bOn = true;
1082 
1083  if (pActiveTwoLinesHint)
1084  {
1085  aRet.pItem = nullptr;
1086  aRet.pAttr = pActiveTwoLinesHint;
1087  aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
1088  if (pNodeTwoLinesItem)
1089  {
1090  aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
1091  bOn = static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetEndBracket() ==
1092  pActiveTwoLinesItem->GetEndBracket() &&
1093  static_cast<const SvxTwoLinesItem*>(pNodeTwoLinesItem)->GetStartBracket() ==
1094  pActiveTwoLinesItem->GetStartBracket();
1095  }
1096  else
1097  {
1098  aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *aRet.pAttr->End()));
1099  }
1100  }
1101  else
1102  {
1103  aRet.pItem = pNodeTwoLinesItem;
1104  aRet.pAttr = nullptr;
1105  aRet.nStartOfAttr = TextFrameIndex(-1);
1106  aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
1107  }
1109  aRet.nLevel = GetTextFrame()->IsRightToLeft() ? 1 : 0;
1110 
1111  // pActiveTwoLinesHint is the last 2-line-attribute, which contains
1112  // the actual position.
1113 
1114  // At this moment we know that at position rPos the "winner"-attribute
1115  // causes a 2-line-portion. The end of the attribute is the end of the
1116  // portion, if there's no interrupting attribute.
1117  // There are two kinds of interrupters:
1118  // - ruby attributes stops the 2-line-attribute, the end of the
1119  // multiline is the start of the ruby attribute
1120  // - 2-line-attributes with value "Off" or with different brackets,
1121  // these attributes may interrupt the winner, but they could be
1122  // neutralized by another 2-line-attribute starting at the same
1123  // position with the same brackets as the winner-attribute.
1124 
1125  // In the following loop rPos is the critical position and it will be
1126  // evaluated, if at rPos starts an interrupting or a maintaining
1127  // continuity attribute.
1128 
1129  // iterAtStartOfNode is positioned to the first hint of the node
1130  // (if any); the node item itself has already been handled above
1131  for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
1132  {
1133  SwTextNode const* pNode(nullptr);
1134  SwTextAttr const*const pTmp = iter.NextAttr(pNode);
1135  if (!pNode)
1136  {
1137  break;
1138  }
1139  assert(startPos.first->GetIndex() <= pNode->GetIndex());
1140  TextFrameIndex nTmpStart;
1141  TextFrameIndex nTmpEnd;
1142  if (pTmp)
1143  {
1144  nTmpEnd = m_pFrame->MapModelToView(pNode, pTmp->GetAnyEnd());
1145  if (nTmpEnd <= rPos)
1146  continue;
1147  nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
1148  }
1149  else
1150  {
1151  pNodeTwoLinesItem = nullptr;
1152  pNode->GetSwAttrSet().GetItemState(
1153  RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem);
1154  nTmpStart = m_pFrame->MapModelToView(pNode, 0);
1155  nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
1156  assert(rPos <= nTmpEnd); // next node must not have smaller index
1157  }
1158 
1159  if (rPos < nTmpStart)
1160  {
1161  // If bOn is false and the next attribute starts later than rPos
1162  // the winner attribute is interrupted at rPos.
1163  // If the start of the next attribute is behind the end of
1164  // the last attribute on the aEnd-stack, this is the endposition
1165  // on the stack is the end of the 2-line portion.
1166  if (!bOn || aEnd.back() < nTmpStart)
1167  break;
1168  // At this moment, bOn is true and the next attribute starts
1169  // behind rPos, so we could move rPos to the next startpoint
1170  rPos = nTmpStart;
1171  // We clean up the aEnd-stack, endpositions equal to rPos are
1172  // superfluous.
1173  while( !aEnd.empty() && aEnd.back() <= rPos )
1174  {
1175  bOn = !bOn;
1176  aEnd.pop_back();
1177  }
1178  // If the endstack is empty, we simulate an attribute with
1179  // state true and endposition rPos
1180  if( aEnd.empty() )
1181  {
1182  aEnd.push_front( rPos );
1183  bOn = true;
1184  }
1185  }
1186  // A ruby attribute stops the 2-line immediately
1187  if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
1188  return aRet;
1189  if (pTmp ? lcl_Has2Lines(*pTmp, pActiveTwoLinesItem, bTwo)
1190  : lcl_Check2Lines(pNodeTwoLinesItem, pActiveTwoLinesItem, bTwo))
1191  { // We have an interesting attribute...
1192  if( bTwo == bOn )
1193  { // .. with the same state, so the last attribute could
1194  // be continued.
1195  if (aEnd.back() < nTmpEnd)
1196  aEnd.back() = nTmpEnd;
1197  }
1198  else
1199  { // .. with a different state.
1200  bOn = bTwo;
1201  // If this is smaller than the last on the stack, we put
1202  // it on the stack. If it has the same endposition, the last
1203  // could be removed.
1204  if (nTmpEnd < aEnd.back())
1205  aEnd.push_back( nTmpEnd );
1206  else if( aEnd.size() > 1 )
1207  aEnd.pop_back();
1208  else
1209  aEnd.back() = nTmpEnd;
1210  }
1211  }
1212  }
1213  if( bOn && !aEnd.empty() )
1214  rPos = aEnd.back();
1215  return aRet;
1216  }
1217  if (pActiveRotateHint ||
1218  (pNodeRotateItem && pNodeRotateItem == pActiveRotateItem &&
1219  rPos < TextFrameIndex(GetText().getLength())))
1220  { // The winner is a rotate-attribute,
1221  // the end of the multiportion depends on the following attributes...
1222  SwMultiCreator aRet;
1224 
1225  // We note the endpositions of the 2-line attributes in aEnd as stack
1226  std::deque<TextFrameIndex> aEnd;
1227 
1228  // The bOn flag signs the state of the last 2-line attribute in the
1229  // aEnd-stack, which could interrupts the winning rotation attribute.
1230  bool bOn = pNodeTwoLinesItem != nullptr;
1231  aEnd.push_front(TextFrameIndex(GetText().getLength()));
1232 
1233  // first, search for the start position of the next TWOLINE portion
1234  // because the ROTATE portion must end there at the latest
1235  TextFrameIndex n2Start = rPos;
1236  for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
1237  {
1238  SwTextNode const* pNode(nullptr);
1239  SwTextAttr const*const pTmp = iter.NextAttr(pNode);
1240  if (!pNode)
1241  {
1242  break;
1243  }
1244  assert(startPos.first->GetIndex() <= pNode->GetIndex());
1245  TextFrameIndex nTmpStart;
1246  TextFrameIndex nTmpEnd;
1247  if (pTmp)
1248  {
1249  nTmpEnd = m_pFrame->MapModelToView(pNode, pTmp->GetAnyEnd());
1250  if (nTmpEnd <= n2Start)
1251  continue;
1252  nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
1253  }
1254  else
1255  {
1256  pNodeTwoLinesItem = nullptr;
1257  pNode->GetSwAttrSet().GetItemState(
1258  RES_CHRATR_TWO_LINES, true, &pNodeTwoLinesItem);
1259  nTmpStart = m_pFrame->MapModelToView(pNode, 0);
1260  nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
1261  assert(n2Start <= nTmpEnd); // next node must not have smaller index
1262  }
1263 
1264  if (n2Start < nTmpStart)
1265  {
1266  if (bOn || aEnd.back() < nTmpStart)
1267  break;
1268  n2Start = nTmpStart;
1269  while( !aEnd.empty() && aEnd.back() <= n2Start )
1270  {
1271  bOn = !bOn;
1272  aEnd.pop_back();
1273  }
1274  if( aEnd.empty() )
1275  {
1276  aEnd.push_front( n2Start );
1277  bOn = false;
1278  }
1279  }
1280  // A ruby attribute stops immediately
1281  if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
1282  {
1283  bOn = true;
1284  break;
1285  }
1286  const SvxTwoLinesItem* p2Lines = nullptr;
1287  if (pTmp ? lcl_Has2Lines(*pTmp, p2Lines, bTwo)
1288  : lcl_Check2Lines(pNodeTwoLinesItem, p2Lines, bTwo))
1289  {
1290  if( bTwo == bOn )
1291  {
1292  if (aEnd.back() < nTmpEnd)
1293  aEnd.back() = nTmpEnd;
1294  }
1295  else
1296  {
1297  bOn = bTwo;
1298  if (nTmpEnd < aEnd.back())
1299  aEnd.push_back( nTmpEnd );
1300  else if( aEnd.size() > 1 )
1301  aEnd.pop_back();
1302  else
1303  aEnd.back() = nTmpEnd;
1304  }
1305  }
1306  }
1307  if( !bOn && !aEnd.empty() )
1308  n2Start = aEnd.back();
1309 
1310  aEnd.clear();
1311 
1312  // now, search for the end of the ROTATE portion, similar to above
1313  bOn = true;
1314  if (pActiveRotateHint)
1315  {
1316  aRet.pItem = nullptr;
1317  aRet.pAttr = pActiveRotateHint;
1318  aRet.nStartOfAttr = m_pFrame->MapModelToView(startPos.first, aRet.pAttr->GetStart());
1319  if (pNodeRotateItem)
1320  {
1321  aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
1322  bOn = static_cast<const SvxCharRotateItem*>(pNodeRotateItem)->GetValue() ==
1323  pActiveRotateItem->GetValue();
1324  }
1325  else
1326  {
1327  aEnd.push_front(m_pFrame->MapModelToView(startPos.first, *aRet.pAttr->End()));
1328  }
1329  }
1330  else
1331  {
1332  aRet.pItem = pNodeRotateItem;
1333  aRet.pAttr = nullptr;
1334  aRet.nStartOfAttr = TextFrameIndex(-1);
1335  aEnd.push_front(m_pFrame->MapModelToView(startPos.first, startPos.first->Len()));
1336  }
1337  for (sw::MergedAttrIterMulti iter = iterAtStartOfNode; ; )
1338  {
1339  SwTextNode const* pNode(nullptr);
1340  SwTextAttr const*const pTmp = iter.NextAttr(pNode);
1341  if (!pNode)
1342  {
1343  break;
1344  }
1345  assert(startPos.first->GetIndex() <= pNode->GetIndex());
1346  TextFrameIndex nTmpStart;
1347  TextFrameIndex nTmpEnd;
1348  if (pTmp)
1349  {
1350  nTmpEnd = m_pFrame->MapModelToView(pNode, pTmp->GetAnyEnd());
1351  if (nTmpEnd <= rPos)
1352  continue;
1353  nTmpStart = m_pFrame->MapModelToView(pNode, pTmp->GetStart());
1354  }
1355  else
1356  {
1357  pNodeRotateItem = nullptr;
1358  pNode->GetSwAttrSet().GetItemState(
1359  RES_CHRATR_ROTATE, true, &pNodeRotateItem);
1360  nTmpStart = m_pFrame->MapModelToView(pNode, 0);
1361  nTmpEnd = m_pFrame->MapModelToView(pNode, pNode->Len());
1362  assert(rPos <= nTmpEnd); // next node must not have smaller index
1363  }
1364 
1365  if (rPos < nTmpStart)
1366  {
1367  if (!bOn || aEnd.back() < nTmpStart)
1368  break;
1369  rPos = nTmpStart;
1370  while( !aEnd.empty() && aEnd.back() <= rPos )
1371  {
1372  bOn = !bOn;
1373  aEnd.pop_back();
1374  }
1375  if( aEnd.empty() )
1376  {
1377  aEnd.push_front( rPos );
1378  bOn = true;
1379  }
1380  }
1381  if (pTmp && RES_TXTATR_CJK_RUBY == pTmp->Which())
1382  {
1383  bOn = false;
1384  break;
1385  }
1386  // TODO why does this use bTwo, not bRot ???
1387  if (pTmp ? lcl_HasRotation(*pTmp, pActiveRotateItem, bTwo)
1388  : lcl_CheckRotation(pNodeRotateItem, pActiveRotateItem, bTwo))
1389  {
1390  if( bTwo == bOn )
1391  {
1392  if (aEnd.back() < nTmpEnd)
1393  aEnd.back() = nTmpEnd;
1394  }
1395  else
1396  {
1397  bOn = bTwo;
1398  if (nTmpEnd < aEnd.back())
1399  aEnd.push_back( nTmpEnd );
1400  else if( aEnd.size() > 1 )
1401  aEnd.pop_back();
1402  else
1403  aEnd.back() = nTmpEnd;
1404  }
1405  }
1406  }
1407  if( bOn && !aEnd.empty() )
1408  rPos = aEnd.back();
1409  if( rPos > n2Start )
1410  rPos = n2Start;
1411  return aRet;
1412  }
1413  return {};
1414 }
1415 
1416 namespace {
1417 
1418 // A little helper class to manage the spaceadd-arrays of the text adjustment
1419 // during a PaintMultiPortion.
1420 // The constructor prepares the array for the first line of multiportion,
1421 // the SecondLine-function restores the values for the first line and prepares
1422 // the second line.
1423 // The destructor restores the values of the last manipulation.
1424 class SwSpaceManipulator
1425 {
1426  SwTextPaintInfo& m_rInfo;
1427  SwMultiPortion& m_rMulti;
1428  std::vector<tools::Long>* m_pOldSpaceAdd;
1429  sal_uInt16 m_nOldSpaceIndex;
1430  tools::Long m_nSpaceAdd;
1431  bool m_bSpaceChg;
1432  sal_uInt8 m_nOldDir;
1433 
1434 public:
1435  SwSpaceManipulator( SwTextPaintInfo& rInf, SwMultiPortion& rMult );
1436  ~SwSpaceManipulator();
1437  void SecondLine();
1438  tools::Long GetSpaceAdd() const { return m_nSpaceAdd; }
1439 };
1440 
1441 }
1442 
1443 SwSpaceManipulator::SwSpaceManipulator(SwTextPaintInfo& rInf, SwMultiPortion& rMult)
1444  : m_rInfo(rInf)
1445  , m_rMulti(rMult)
1446  , m_nSpaceAdd(0)
1447 {
1448  m_pOldSpaceAdd = m_rInfo.GetpSpaceAdd();
1449  m_nOldSpaceIndex = m_rInfo.GetSpaceIdx();
1450  m_nOldDir = m_rInfo.GetDirection();
1451  m_rInfo.SetDirection(m_rMulti.GetDirection());
1452  m_bSpaceChg = false;
1453 
1454  if (m_rMulti.IsDouble())
1455  {
1456  m_nSpaceAdd = (m_pOldSpaceAdd && !m_rMulti.HasTabulator()) ? m_rInfo.GetSpaceAdd() : 0;
1457  if (m_rMulti.GetRoot().IsSpaceAdd())
1458  {
1459  m_rInfo.SetpSpaceAdd(m_rMulti.GetRoot().GetpLLSpaceAdd());
1460  m_rInfo.ResetSpaceIdx();
1461  m_bSpaceChg = m_rMulti.ChgSpaceAdd(&m_rMulti.GetRoot(), m_nSpaceAdd);
1462  }
1463  else if (m_rMulti.HasTabulator())
1464  m_rInfo.SetpSpaceAdd(nullptr);
1465  }
1466  else if (!m_rMulti.IsBidi())
1467  {
1468  m_rInfo.SetpSpaceAdd(m_rMulti.GetRoot().GetpLLSpaceAdd());
1469  m_rInfo.ResetSpaceIdx();
1470  }
1471 }
1472 
1473 void SwSpaceManipulator::SecondLine()
1474 {
1475  if (m_bSpaceChg)
1476  {
1477  m_rInfo.RemoveFirstSpaceAdd();
1478  m_bSpaceChg = false;
1479  }
1480  SwLineLayout* pLay = m_rMulti.GetRoot().GetNext();
1481  if( pLay->IsSpaceAdd() )
1482  {
1483  m_rInfo.SetpSpaceAdd(pLay->GetpLLSpaceAdd());
1484  m_rInfo.ResetSpaceIdx();
1485  m_bSpaceChg = m_rMulti.ChgSpaceAdd(pLay, m_nSpaceAdd);
1486  }
1487  else
1488  {
1489  m_rInfo.SetpSpaceAdd((!m_rMulti.IsDouble() || m_rMulti.HasTabulator()) ? nullptr
1490  : m_pOldSpaceAdd);
1491  m_rInfo.SetSpaceIdx(m_nOldSpaceIndex);
1492  }
1493 }
1494 
1495 SwSpaceManipulator::~SwSpaceManipulator()
1496 {
1497  if (m_bSpaceChg)
1498  {
1499  m_rInfo.RemoveFirstSpaceAdd();
1500  m_bSpaceChg = false;
1501  }
1502  m_rInfo.SetpSpaceAdd(m_pOldSpaceAdd);
1503  m_rInfo.SetSpaceIdx(m_nOldSpaceIndex);
1504  m_rInfo.SetDirection(m_nOldDir);
1505 }
1506 
1507 // Manages the paint for a SwMultiPortion.
1508 // External, for the calling function, it seems to be a normal Paint-function,
1509 // internal it is like a SwTextFrame::PaintSwFrame with multiple DrawTextLines
1511  SwMultiPortion& rMulti, const SwMultiPortion* pEnvPor )
1512 {
1513  SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
1514  const bool bHasGrid = pGrid && GetInfo().SnapToGrid();
1515  sal_uInt16 nRubyHeight = 0;
1516  bool bRubyTop = true;
1517 
1518  if ( bHasGrid && pGrid->IsSquaredMode() )
1519  {
1520  nRubyHeight = pGrid->GetRubyHeight();
1521  bRubyTop = ! pGrid->GetRubyTextBelow();
1522  }
1523 
1524  // do not allow grid mode for first line in ruby portion
1525  const bool bRubyInGrid = bHasGrid && rMulti.IsRuby();
1526 
1527  const sal_uInt16 nOldHeight = rMulti.Height();
1528  const bool bOldGridModeAllowed = GetInfo().SnapToGrid();
1529 
1530  if ( bRubyInGrid )
1531  {
1532  GetInfo().SetSnapToGrid( ! bRubyTop );
1533  if (pGrid->IsSquaredMode())
1534  rMulti.Height( m_pCurr->Height() );
1535  }
1536 
1537  SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1538  bool bEnvDir = false;
1539  bool bThisDir = false;
1540  bool bFrameDir = false;
1541  if ( rMulti.IsBidi() )
1542  {
1543  // these values are needed for the calculation of the x coordinate
1544  // and the layout mode
1545  OSL_ENSURE( ! pEnvPor || pEnvPor->IsBidi(),
1546  "Oh no, I expected a BidiPortion" );
1547  bFrameDir = GetInfo().GetTextFrame()->IsRightToLeft();
1548  bEnvDir = pEnvPor ? ((static_cast<const SwBidiPortion*>(pEnvPor)->GetLevel() % 2) != 0) : bFrameDir;
1549  bThisDir = (static_cast<SwBidiPortion&>(rMulti).GetLevel() % 2) != 0;
1550  }
1551 
1552 #if OSL_DEBUG_LEVEL > 1
1553  // only paint first level bidi portions
1554  if( rMulti.Width() > 1 && ! pEnvPor )
1555  GetInfo().DrawViewOpt( rMulti, PortionType::Field );
1556 #endif
1557 
1558  if ( bRubyInGrid && pGrid->IsSquaredMode() )
1559  rMulti.Height( nOldHeight );
1560 
1561  // do we have to repaint a post it portion?
1562  if( GetInfo().OnWin() && rMulti.GetNextPortion() &&
1563  ! rMulti.GetNextPortion()->Width() )
1564  rMulti.GetNextPortion()->PrePaint( GetInfo(), &rMulti );
1565 
1566  // old values must be saved and restored at the end
1567  TextFrameIndex const nOldLen = GetInfo().GetLen();
1568  const SwTwips nOldX = GetInfo().X();
1569  const SwTwips nOldY = GetInfo().Y();
1570  TextFrameIndex const nOldIdx = GetInfo().GetIdx();
1571 
1572  SwSpaceManipulator aManip( GetInfo(), rMulti );
1573 
1574  std::unique_ptr<SwFontSave> pFontSave;
1575  std::unique_ptr<SwFont> pTmpFnt;
1576 
1577  if( rMulti.IsDouble() )
1578  {
1579  pTmpFnt.reset(new SwFont( *GetInfo().GetFont() ));
1580  if( rMulti.IsDouble() )
1581  {
1582  SetPropFont( 50 );
1583  pTmpFnt->SetProportion( GetPropFont() );
1584  }
1585  pFontSave.reset(new SwFontSave( GetInfo(), pTmpFnt.get(), this ));
1586  }
1587  else
1588  {
1589  pFontSave = nullptr;
1590  pTmpFnt = nullptr;
1591  }
1592 
1593  if( rMulti.HasBrackets() )
1594  {
1595  TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx();
1596  GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
1597  SeekAndChg( GetInfo() );
1598  static_cast<SwDoubleLinePortion&>(rMulti).PaintBracket( GetInfo(), 0, true );
1599  GetInfo().SetIdx( nTmpOldIdx );
1600  }
1601 
1602  const SwTwips nTmpX = GetInfo().X();
1603 
1604  SwLineLayout* pLay = &rMulti.GetRoot();// the first line of the multiportion
1605  SwLinePortion* pPor = pLay->GetFirstPortion();//first portion of these line
1606  SwTwips nOfst = 0;
1607 
1608  // GetInfo().Y() is the baseline from the surrounding line. We must switch
1609  // this temporary to the baseline of the inner lines of the multiportion.
1610  if( rMulti.HasRotation() )
1611  {
1612  if( rMulti.IsRevers() )
1613  {
1614  GetInfo().Y( nOldY - rMulti.GetAscent() );
1615  nOfst = nTmpX + rMulti.Width();
1616  }
1617  else
1618  {
1619  GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1620  nOfst = nTmpX;
1621  }
1622  }
1623  else if ( rMulti.IsBidi() )
1624  {
1625  // does the current bidi portion has the same direction
1626  // as its environment?
1627  if ( bEnvDir != bThisDir )
1628  {
1629  // different directions, we have to adjust the x coordinate
1630  SwTwips nMultiWidth = rMulti.Width() +
1631  rMulti.CalcSpacing( GetInfo().GetSpaceAdd(), GetInfo() );
1632 
1633  if ( bFrameDir == bThisDir )
1634  GetInfo().X( GetInfo().X() - nMultiWidth );
1635  else
1636  GetInfo().X( GetInfo().X() + nMultiWidth );
1637  }
1638 
1639  nOfst = nOldY - rMulti.GetAscent();
1640 
1641  // set layout mode
1642  aLayoutModeModifier.Modify( bThisDir );
1643  }
1644  else
1645  nOfst = nOldY - rMulti.GetAscent();
1646 
1647  bool bRest = pLay->IsRest();
1648  bool bFirst = true;
1649 
1650  OSL_ENSURE( nullptr == GetInfo().GetUnderFnt() || rMulti.IsBidi(),
1651  " Only BiDi portions are allowed to use the common underlining font" );
1652 
1653  if ( rMulti.IsRuby() )
1654  GetInfo().SetRuby( rMulti.OnTop() );
1655 
1656  do
1657  {
1658  if ( bHasGrid && pGrid->IsSquaredMode() )
1659  {
1660  if( rMulti.HasRotation() )
1661  {
1662  const sal_uInt16 nAdjustment = ( pLay->Height() - pPor->Height() ) / 2 +
1663  pPor->GetAscent();
1664  if( rMulti.IsRevers() )
1665  GetInfo().X( nOfst - nAdjustment );
1666  else
1667  GetInfo().X( nOfst + nAdjustment );
1668  }
1669  else
1670  {
1671  // special treatment for ruby portions in grid mode
1672  SwTwips nAdjustment = 0;
1673  if ( rMulti.IsRuby() )
1674  {
1675  if ( bRubyTop != ( pLay == &rMulti.GetRoot() ) )
1676  // adjust base text
1677  nAdjustment = ( m_pCurr->Height() - nRubyHeight - pPor->Height() ) / 2;
1678  else if ( bRubyTop )
1679  // adjust upper ruby text
1680  nAdjustment = nRubyHeight - pPor->Height();
1681  // else adjust lower ruby text
1682  }
1683 
1684  GetInfo().Y( nOfst + nAdjustment + pPor->GetAscent() );
1685  }
1686  }
1687  else if( rMulti.HasRotation() )
1688  {
1689  if( rMulti.IsRevers() )
1690  GetInfo().X( nOfst - AdjustBaseLine( *pLay, pPor, 0, 0, true ) );
1691  else
1692  GetInfo().X( nOfst + AdjustBaseLine( *pLay, pPor ) );
1693  }
1694  else if ( rMulti.IsRuby() && rMulti.OnRight() && GetInfo().IsRuby() )
1695  {
1696  SwTwips nLineDiff = std::max(( rMulti.GetRoot().Height() - pPor->Width() ) / 2, static_cast<SwTwips>(0) );
1697  GetInfo().Y( nOfst + nLineDiff );
1698  // Draw the ruby text on top of the preserved space.
1699  GetInfo().X( GetInfo().X() - pPor->Height() );
1700  }
1701  else
1702  GetInfo().Y( nOfst + AdjustBaseLine( *pLay, pPor ) );
1703 
1704  bool bSeeked = true;
1705  GetInfo().SetLen( pPor->GetLen() );
1706 
1707  if( bRest && pPor->InFieldGrp() && !pPor->GetLen() )
1708  {
1709  if( static_cast<SwFieldPortion*>(pPor)->HasFont() )
1710  bSeeked = false;
1711  else
1713  }
1714  else if( pPor->InTextGrp() || pPor->InFieldGrp() || pPor->InTabGrp() )
1715  SeekAndChg( GetInfo() );
1716  else if ( !bFirst && pPor->IsBreakPortion() && GetInfo().GetOpt().IsParagraph() )
1717  {
1718  if( GetRedln() )
1719  SeekAndChg( GetInfo() );
1720  else
1722  }
1723  else
1724  bSeeked = false;
1725 
1726  SwLinePortion *pNext = pPor->GetNextPortion();
1727  if(GetInfo().OnWin() && pNext && !pNext->Width() )
1728  {
1729  if ( !bSeeked )
1730  SeekAndChg( GetInfo() );
1731  pNext->PrePaint( GetInfo(), pPor );
1732  }
1733 
1734  CheckSpecialUnderline( pPor );
1735  SwUnderlineFont* pUnderLineFnt = GetInfo().GetUnderFnt();
1736  if ( pUnderLineFnt )
1737  {
1738  if ( rMulti.IsDouble() )
1739  pUnderLineFnt->GetFont().SetProportion( 50 );
1740  pUnderLineFnt->SetPos( GetInfo().GetPos() );
1741  }
1742 
1743  if ( rMulti.IsBidi() )
1744  {
1745  // we do not allow any rotation inside a bidi portion
1746  SwFont* pTmpFont = GetInfo().GetFont();
1747  pTmpFont->SetVertical( 0_deg10, GetInfo().GetTextFrame()->IsVertical() );
1748  }
1749 
1750  if( pPor->IsMultiPortion() && static_cast<SwMultiPortion*>(pPor)->IsBidi() )
1751  {
1752  // but we do allow nested bidi portions
1753  OSL_ENSURE( rMulti.IsBidi(), "Only nesting of bidi portions is allowed" );
1754  PaintMultiPortion( rPaint, static_cast<SwMultiPortion&>(*pPor), &rMulti );
1755  }
1756  else
1757  pPor->Paint( GetInfo() );
1758 
1759  bFirst &= !pPor->GetLen();
1760  if( pNext || !pPor->IsMarginPortion() )
1761  pPor->Move( GetInfo() );
1762 
1763  pPor = pNext;
1764 
1765  // If there's no portion left, we go to the next line
1766  if( !pPor && pLay->GetNext() )
1767  {
1768  pLay = pLay->GetNext();
1769  pPor = pLay->GetFirstPortion();
1770  bRest = pLay->IsRest();
1771  aManip.SecondLine();
1772 
1773  // delete underline font
1774  delete GetInfo().GetUnderFnt();
1775  GetInfo().SetUnderFnt( nullptr );
1776 
1777  if( rMulti.HasRotation() )
1778  {
1779  if( rMulti.IsRevers() )
1780  {
1781  nOfst += pLay->Height();
1782  GetInfo().Y( nOldY - rMulti.GetAscent() );
1783  }
1784  else
1785  {
1786  nOfst -= pLay->Height();
1787  GetInfo().Y( nOldY - rMulti.GetAscent() + rMulti.Height() );
1788  }
1789  }
1790  else if ( bHasGrid && rMulti.IsRuby() )
1791  {
1792  GetInfo().SetSnapToGrid( bRubyTop );
1793  GetInfo().X( nTmpX );
1794  if (pGrid->IsSquaredMode() )
1795  {
1796  if ( bRubyTop )
1797  nOfst += nRubyHeight;
1798  else
1799  nOfst += m_pCurr->Height() - nRubyHeight;
1800  }
1801  else
1802  {
1803  nOfst += rMulti.GetRoot().Height();
1804  }
1805  }
1806  else if ( rMulti.IsRuby() && rMulti.OnRight() )
1807  {
1809  GetInfo().SetRuby( true );
1810  } else
1811  {
1812  GetInfo().X( nTmpX );
1813  // We switch to the baseline of the next inner line
1814  nOfst += rMulti.GetRoot().Height();
1815  }
1816  }
1817  } while( pPor );
1818 
1819  if ( bRubyInGrid )
1820  GetInfo().SetSnapToGrid( bOldGridModeAllowed );
1821 
1822  // delete underline font
1823  if ( ! rMulti.IsBidi() )
1824  {
1825  delete GetInfo().GetUnderFnt();
1826  GetInfo().SetUnderFnt( nullptr );
1827  }
1828 
1829  GetInfo().SetIdx( nOldIdx );
1830  GetInfo().Y( nOldY );
1831 
1832  if( rMulti.HasBrackets() )
1833  {
1834  TextFrameIndex const nTmpOldIdx = GetInfo().GetIdx();
1835  GetInfo().SetIdx(static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart);
1836  SeekAndChg( GetInfo() );
1837  GetInfo().X( nOldX );
1838  static_cast<SwDoubleLinePortion&>(rMulti).PaintBracket( GetInfo(),
1839  aManip.GetSpaceAdd(), false );
1840  GetInfo().SetIdx( nTmpOldIdx );
1841  }
1842  // Restore the saved values
1843  GetInfo().X( nOldX );
1844  GetInfo().SetLen( nOldLen );
1845  pFontSave.reset();
1846  pTmpFnt.reset();
1847  SetPropFont( 0 );
1848 }
1849 
1850 static bool lcl_ExtractFieldFollow( SwLineLayout* pLine, SwLinePortion* &rpField )
1851 {
1852  SwLinePortion* pLast = pLine;
1853  rpField = pLine->GetNextPortion();
1854  while( rpField && !rpField->InFieldGrp() )
1855  {
1856  pLast = rpField;
1857  rpField = rpField->GetNextPortion();
1858  }
1859  bool bRet = rpField != nullptr;
1860  if( bRet )
1861  {
1862  if( static_cast<SwFieldPortion*>(rpField)->IsFollow() )
1863  {
1864  rpField->Truncate();
1865  pLast->SetNextPortion( nullptr );
1866  }
1867  else
1868  rpField = nullptr;
1869  }
1870  pLine->Truncate();
1871  return bRet;
1872 }
1873 
1874 // If a multi portion completely has to go to the
1875 // next line, this function is called to truncate
1876 // the rest of the remaining multi portion
1878  TextFrameIndex const nStartIdx)
1879 {
1880  rMulti.GetRoot().Truncate();
1881  rMulti.GetRoot().SetLen(TextFrameIndex(0));
1882  rMulti.GetRoot().Width(0);
1883 // rMulti.CalcSize( *this, aInf );
1884  if ( rMulti.GetRoot().GetNext() )
1885  {
1886  rMulti.GetRoot().GetNext()->Truncate();
1887  rMulti.GetRoot().GetNext()->SetLen(TextFrameIndex(0));
1888  rMulti.GetRoot().GetNext()->Width( 0 );
1889  }
1890  rMulti.Width( 0 );
1891  rMulti.SetLen(TextFrameIndex(0));
1892  rInf.SetIdx( nStartIdx );
1893 }
1894 
1895 // Manages the formatting of a SwMultiPortion. External, for the calling
1896 // function, it seems to be a normal Format-function, internal it is like a
1897 // SwTextFrame::Format_ with multiple BuildPortions
1899  SwMultiPortion& rMulti )
1900 {
1901  SwTwips nMaxWidth = rInf.Width();
1902  SwTwips nOldX = 0;
1903 
1904  if( rMulti.HasBrackets() )
1905  {
1906  TextFrameIndex const nOldIdx = rInf.GetIdx();
1907  rInf.SetIdx( static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets()->nStart );
1908  SeekAndChg( rInf );
1909  nOldX = GetInfo().X();
1910  static_cast<SwDoubleLinePortion&>(rMulti).FormatBrackets( rInf, nMaxWidth );
1911  rInf.SetIdx( nOldIdx );
1912  }
1913 
1914  SeekAndChg( rInf );
1915  std::unique_ptr<SwFontSave> xFontSave;
1916  std::unique_ptr<SwFont> xTmpFont;
1917  if( rMulti.IsDouble() )
1918  {
1919  xTmpFont.reset(new SwFont( *rInf.GetFont() ));
1920  if( rMulti.IsDouble() )
1921  {
1922  SetPropFont( 50 );
1923  xTmpFont->SetProportion( GetPropFont() );
1924  }
1925  xFontSave.reset(new SwFontSave(rInf, xTmpFont.get(), this));
1926  }
1927 
1928  SwLayoutModeModifier aLayoutModeModifier( *GetInfo().GetOut() );
1929  if ( rMulti.IsBidi() )
1930  {
1931  // set layout mode
1932  aLayoutModeModifier.Modify( ! rInf.GetTextFrame()->IsRightToLeft() );
1933  }
1934 
1935  SwTwips nTmpX = 0;
1936 
1937  if( rMulti.HasRotation() )
1938  {
1939  // For nMaxWidth we take the height of the body frame.
1940  // #i25067#: If the current frame is inside a table, we restrict
1941  // nMaxWidth to the current frame height, unless the frame size
1942  // attribute is set to variable size:
1943 
1944  // We set nTmpX (which is used for portion calculating) to the
1945  // current Y value
1946  const SwPageFrame* pPage = m_pFrame->FindPageFrame();
1947  OSL_ENSURE( pPage, "No page in frame!");
1948  const SwLayoutFrame* pUpperFrame = pPage;
1949 
1950  if ( m_pFrame->IsInTab() )
1951  {
1952  pUpperFrame = m_pFrame->GetUpper();
1953  while ( pUpperFrame && !pUpperFrame->IsCellFrame() )
1954  pUpperFrame = pUpperFrame->GetUpper();
1955  assert(pUpperFrame); //pFrame is in table but does not have an upper cell frame
1956  if (!pUpperFrame)
1957  return false;
1958  const SwTableLine* pLine = static_cast<const SwRowFrame*>(pUpperFrame->GetUpper())->GetTabLine();
1959  const SwFormatFrameSize& rFrameFormatSize = pLine->GetFrameFormat()->GetFrameSize();
1960  if ( SwFrameSize::Variable == rFrameFormatSize.GetHeightSizeType() )
1961  pUpperFrame = pPage;
1962  }
1963  if ( pUpperFrame == pPage && !m_pFrame->IsInFootnote() )
1964  pUpperFrame = pPage->FindBodyCont();
1965 
1966  nMaxWidth = pUpperFrame ?
1967  ( rInf.GetTextFrame()->IsVertical() ?
1968  pUpperFrame->getFramePrintArea().Width() :
1969  pUpperFrame->getFramePrintArea().Height() ) :
1970  USHRT_MAX;
1971  }
1972  else
1973  nTmpX = rInf.X();
1974 
1975  SwMultiPortion* pOldMulti = m_pMulti;
1976 
1977  m_pMulti = &rMulti;
1978  SwLineLayout *pOldCurr = m_pCurr;
1979  TextFrameIndex const nOldStart = GetStart();
1980  SwTwips nMinWidth = nTmpX + 1;
1981  SwTwips nActWidth = nMaxWidth;
1982  const TextFrameIndex nStartIdx = rInf.GetIdx();
1983  TextFrameIndex nMultiLen = rMulti.GetLen();
1984 
1985  SwLinePortion *pFirstRest;
1986  SwLinePortion *pSecondRest;
1987  if( rMulti.IsFormatted() )
1988  {
1989  if( !lcl_ExtractFieldFollow( &rMulti.GetRoot(), pFirstRest )
1990  && rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1991  lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pFirstRest );
1992  if( !rMulti.IsDouble() && rMulti.GetRoot().GetNext() )
1993  lcl_ExtractFieldFollow( rMulti.GetRoot().GetNext(), pSecondRest );
1994  else
1995  pSecondRest = nullptr;
1996  }
1997  else
1998  {
1999  pFirstRest = rMulti.GetRoot().GetNextPortion();
2000  pSecondRest = rMulti.GetRoot().GetNext() ?
2001  rMulti.GetRoot().GetNext()->GetNextPortion() : nullptr;
2002  if( pFirstRest )
2003  rMulti.GetRoot().SetNextPortion( nullptr );
2004  if( pSecondRest )
2005  rMulti.GetRoot().GetNext()->SetNextPortion( nullptr );
2006  rMulti.SetFormatted();
2007  nMultiLen = nMultiLen - rInf.GetIdx();
2008  }
2009 
2010  // save some values
2011  const OUString* pOldText = &(rInf.GetText());
2012  const SwTwips nOldPaintOfst = rInf.GetPaintOfst();
2013  std::shared_ptr<vcl::text::TextLayoutCache> const pOldCachedVclData(rInf.GetCachedVclData());
2014  rInf.SetCachedVclData(nullptr);
2015 
2016  OUString const aMultiStr( rInf.GetText().copy(0, sal_Int32(nMultiLen + rInf.GetIdx())) );
2017  rInf.SetText( aMultiStr );
2018  SwTextFormatInfo aInf( rInf, rMulti.GetRoot(), nActWidth );
2019  // Do we allow break cuts? The FirstMulti-Flag is evaluated during
2020  // line break determination.
2021  bool bFirstMulti = rInf.GetIdx() != rInf.GetLineStart();
2022 
2023  SwLinePortion *pNextFirst = nullptr;
2024  SwLinePortion *pNextSecond = nullptr;
2025  bool bRet = false;
2026 
2027  SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame()));
2028  const bool bHasGrid = pGrid && GRID_LINES_CHARS == pGrid->GetGridType();
2029 
2030  bool bRubyTop = false;
2031 
2032  if ( bHasGrid )
2033  bRubyTop = ! pGrid->GetRubyTextBelow();
2034 
2035  do
2036  {
2037  m_pCurr = &rMulti.GetRoot();
2038  m_nStart = nStartIdx;
2039  bRet = false;
2040  FormatReset( aInf );
2041  aInf.X( nTmpX );
2042  aInf.Width( sal_uInt16(nActWidth) );
2043  aInf.RealWidth( sal_uInt16(nActWidth) );
2044  aInf.SetFirstMulti( bFirstMulti );
2045  aInf.SetNumDone( rInf.IsNumDone() );
2046  aInf.SetFootnoteDone( rInf.IsFootnoteDone() );
2047 
2048  // if there's a bookmark at the start of the MultiPortion, it will be
2049  // painted with the rotation etc. of the MultiPortion; move it *inside*
2050  // so it gets positioned correctly; currently there's no other portion
2051  // inserted between the end of WhichFirstPortion() and
2052  // BuildMultiPortion()
2053  if (rInf.GetLast()->GetWhichPor() == PortionType::Bookmark)
2054  {
2055  auto const pBookmark(static_cast<SwBookmarkPortion*>(rInf.GetLast()));
2056  auto *const pPrevious = pBookmark->FindPrevPortion(rInf.GetRoot());
2057  assert(!pPrevious || pPrevious->GetNextPortion() == pBookmark);
2058  if (pPrevious)
2059  {
2060  pPrevious->SetNextPortion(nullptr);
2061  }
2062  rInf.SetLast(pPrevious);
2063  assert(m_pCurr->GetNextPortion() == nullptr);
2064  m_pCurr->SetNextPortion(pBookmark);
2065  }
2066 
2067  if( pFirstRest )
2068  {
2069  OSL_ENSURE( pFirstRest->InFieldGrp(), "BuildMulti: Fieldrest expected");
2070  SwFieldPortion *pField =
2071  static_cast<SwFieldPortion*>(pFirstRest)->Clone(
2072  static_cast<SwFieldPortion*>(pFirstRest)->GetExp() );
2073  pField->SetFollow( true );
2074  aInf.SetRest( pField );
2075  }
2076  aInf.SetRuby( rMulti.IsRuby() && rMulti.OnTop() );
2077 
2078  // in grid mode we temporarily have to disable the grid for the ruby line
2079  const bool bOldGridModeAllowed = GetInfo().SnapToGrid();
2080  if ( bHasGrid && aInf.IsRuby() && bRubyTop )
2081  aInf.SetSnapToGrid( false );
2082 
2083  // If there's no more rubytext, then buildportion is forbidden
2084  if( pFirstRest || !aInf.IsRuby() )
2085  BuildPortions( aInf );
2086 
2087  aInf.SetSnapToGrid( bOldGridModeAllowed );
2088 
2089  rMulti.CalcSize( *this, aInf );
2091 
2092  if( rMulti.IsBidi() )
2093  {
2094  pNextFirst = aInf.GetRest();
2095  break;
2096  }
2097 
2098  if( rMulti.HasRotation() && !rMulti.IsDouble() )
2099  break;
2100  // second line has to be formatted
2101  else if( m_pCurr->GetLen()<nMultiLen || rMulti.IsRuby() || aInf.GetRest())
2102  {
2103  TextFrameIndex const nFirstLen = m_pCurr->GetLen();
2104  delete m_pCurr->GetNext();
2105  m_pCurr->SetNext( new SwLineLayout() );
2106  m_pCurr = m_pCurr->GetNext();
2107  m_nStart = aInf.GetIdx();
2108  aInf.X( nTmpX );
2109  SwTextFormatInfo aTmp( aInf, *m_pCurr, nActWidth );
2110  if( rMulti.IsRuby() )
2111  {
2112  aTmp.SetRuby( !rMulti.OnTop() );
2113  pNextFirst = aInf.GetRest();
2114  if( pSecondRest )
2115  {
2116  OSL_ENSURE( pSecondRest->InFieldGrp(), "Fieldrest expected");
2117  SwFieldPortion *pField = static_cast<SwFieldPortion*>(pSecondRest)->Clone(
2118  static_cast<SwFieldPortion*>(pSecondRest)->GetExp() );
2119  pField->SetFollow( true );
2120  aTmp.SetRest( pField );
2121  }
2122  if( !rMulti.OnTop() && nFirstLen < nMultiLen )
2123  bRet = true;
2124  }
2125  else
2126  aTmp.SetRest( aInf.GetRest() );
2127  aInf.SetRest( nullptr );
2128 
2129  // in grid mode we temporarily have to disable the grid for the ruby line
2130  if ( bHasGrid && aTmp.IsRuby() && ! bRubyTop )
2131  aTmp.SetSnapToGrid( false );
2132 
2133  BuildPortions( aTmp );
2134 
2135  const SwLinePortion *pRightPortion = rMulti.OnRight() ?
2136  rMulti.GetRoot().GetNext()->GetNextPortion() : nullptr;
2137  if (pRightPortion)
2138  {
2139  // The ruby text on the right is vertical.
2140  // The width and the height are swapped.
2141  SwTwips nHeight = pRightPortion->Height();
2142  // Keep room for the ruby text.
2143  rMulti.GetRoot().FindLastPortion()->AddPrtWidth( nHeight );
2144  }
2145 
2146  aTmp.SetSnapToGrid( bOldGridModeAllowed );
2147 
2148  rMulti.CalcSize( *this, aInf );
2149  rMulti.GetRoot().SetRealHeight( rMulti.GetRoot().Height() );
2151  if( rMulti.IsRuby() )
2152  {
2153  pNextSecond = aTmp.GetRest();
2154  if( pNextFirst )
2155  bRet = true;
2156  }
2157  else
2158  pNextFirst = aTmp.GetRest();
2159  if( ( !aTmp.IsRuby() && nFirstLen + m_pCurr->GetLen() < nMultiLen )
2160  || aTmp.GetRest() )
2161  // our guess for width of multiportion was too small,
2162  // text did not fit into multiportion
2163  bRet = true;
2164  }
2165  if( rMulti.IsRuby() )
2166  break;
2167  if( bRet )
2168  {
2169  // our guess for multiportion width was too small,
2170  // we set min to act
2171  nMinWidth = nActWidth;
2172  nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
2173  if ( nActWidth == nMaxWidth && rInf.GetLineStart() == rInf.GetIdx() )
2174  // we have too less space, we must allow break cuts
2175  // ( the first multi flag is considered during TextPortion::Format_() )
2176  bFirstMulti = false;
2177  if( nActWidth <= nMinWidth )
2178  break;
2179  }
2180  else
2181  {
2182  // For Solaris, this optimization can causes trouble:
2183  // Setting this to the portion width ( = rMulti.Width() )
2184  // can make GetTextBreak inside SwTextGuess::Guess return too small
2185  // values. Therefore we add some extra twips.
2186  if( nActWidth > nTmpX + rMulti.Width() + 6 )
2187  nActWidth = nTmpX + rMulti.Width() + 6;
2188  nMaxWidth = nActWidth;
2189  nActWidth = ( 3 * nMaxWidth + nMinWidth + 3 ) / 4;
2190  if( nActWidth >= nMaxWidth )
2191  break;
2192  // we do not allow break cuts during formatting
2193  bFirstMulti = true;
2194  }
2195  delete pNextFirst;
2196  pNextFirst = nullptr;
2197  } while ( true );
2198 
2199  m_pMulti = pOldMulti;
2200 
2201  m_pCurr = pOldCurr;
2202  m_nStart = nOldStart;
2203  SetPropFont( 0 );
2204 
2205  rMulti.SetLen( rMulti.GetRoot().GetLen() + ( rMulti.GetRoot().GetNext() ?
2206  rMulti.GetRoot().GetNext()->GetLen() : TextFrameIndex(0) ) );
2207 
2208  if( rMulti.IsDouble() )
2209  {
2210  static_cast<SwDoubleLinePortion&>(rMulti).CalcBlanks( rInf );
2211  if( static_cast<SwDoubleLinePortion&>(rMulti).GetLineDiff() )
2212  {
2213  SwLineLayout* pLine = &rMulti.GetRoot();
2214  if( static_cast<SwDoubleLinePortion&>(rMulti).GetLineDiff() > 0 )
2215  {
2216  rInf.SetIdx( nStartIdx + pLine->GetLen() );
2217  pLine = pLine->GetNext();
2218  }
2219  if( pLine )
2220  {
2221  GetInfo().SetMulti( true );
2222 
2223  // If the fourth element bSkipKashida of function CalcNewBlock is true, multiportion will be showed in justification.
2224  // Kashida (Persian) is a type of justification used in some cursive scripts, particularly Arabic.
2225  // In contrast to white-space justification, which increases the length of a line of text by expanding spaces between words or individual letters,
2226  // kashida justification is accomplished by elongating characters at certain chosen points.
2227  // Kashida justification can be combined with white-space justification to various extents.
2228  // The default value of bSkipKashida (the 4th parameter passed to 'CalcNewBlock') is false.
2229  // Only when Adjust is SvxAdjust::Block ( alignment is justify ), multiportion will be showed in justification in new code.
2230  CalcNewBlock( pLine, nullptr, rMulti.Width(), GetAdjust() != SvxAdjust::Block );
2231 
2232  GetInfo().SetMulti( false );
2233  }
2234  rInf.SetIdx( nStartIdx );
2235  }
2236  if( static_cast<SwDoubleLinePortion&>(rMulti).GetBrackets() )
2237  {
2238  rMulti.Width( rMulti.Width() +
2239  static_cast<SwDoubleLinePortion&>(rMulti).BracketWidth() );
2240  GetInfo().X( nOldX );
2241  }
2242  }
2243  else
2244  {
2245  rMulti.ActualizeTabulator();
2246  if( rMulti.IsRuby() )
2247  {
2248  static_cast<SwRubyPortion&>(rMulti).Adjust( rInf );
2249  static_cast<SwRubyPortion&>(rMulti).CalcRubyOffset();
2250  }
2251  }
2252  if( rMulti.HasRotation() )
2253  {
2254  SwTwips nH = rMulti.Width();
2255  SwTwips nAsc = rMulti.GetAscent() + ( nH - rMulti.Height() )/2;
2256  if( nAsc > nH )
2257  nAsc = nH;
2258  else if( nAsc < 0 )
2259  nAsc = 0;
2260  rMulti.Width( rMulti.Height() );
2261  rMulti.Height( sal_uInt16(nH) );
2262  rMulti.SetAscent( sal_uInt16(nAsc) );
2263  bRet = ( rInf.GetPos().X() + rMulti.Width() > rInf.Width() ) &&
2264  nStartIdx != rInf.GetLineStart();
2265  }
2266  else if ( rMulti.IsBidi() )
2267  {
2268  bRet = rMulti.GetLen() < nMultiLen || pNextFirst;
2269  }
2270 
2271  // line break has to be performed!
2272  if( bRet )
2273  {
2274  OSL_ENSURE( !pNextFirst || pNextFirst->InFieldGrp(),
2275  "BuildMultiPortion: Surprising restportion, field expected" );
2276  SwMultiPortion *pTmp;
2277  if( rMulti.IsDouble() )
2278  pTmp = new SwDoubleLinePortion( static_cast<SwDoubleLinePortion&>(rMulti),
2279  nMultiLen + rInf.GetIdx() );
2280  else if( rMulti.IsRuby() )
2281  {
2282  OSL_ENSURE( !pNextSecond || pNextSecond->InFieldGrp(),
2283  "BuildMultiPortion: Surprising restportion, field expected" );
2284 
2285  if ( rInf.GetIdx() == rInf.GetLineStart() )
2286  {
2287  // the ruby portion has to be split in two portions
2288  pTmp = new SwRubyPortion( static_cast<SwRubyPortion&>(rMulti),
2289  nMultiLen + rInf.GetIdx() );
2290 
2291  if( pNextSecond )
2292  {
2293  pTmp->GetRoot().SetNext( new SwLineLayout() );
2294  pTmp->GetRoot().GetNext()->SetNextPortion( pNextSecond );
2295  }
2296  pTmp->SetFollowField();
2297  }
2298  else
2299  {
2300  // we try to keep our ruby portion together
2301  lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2302  pTmp = nullptr;
2303  }
2304  }
2305  else if( rMulti.HasRotation() )
2306  {
2307  // we try to keep our rotated portion together
2308  lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2309  pTmp = new SwRotatedPortion( nMultiLen + rInf.GetIdx(),
2310  rMulti.GetDirection() );
2311  }
2312  // during a recursion of BuildMultiPortions we may not build
2313  // a new SwBidiPortion, this would cause a memory leak
2314  else if( rMulti.IsBidi() && ! m_pMulti )
2315  {
2316  if ( ! rMulti.GetLen() )
2317  lcl_TruncateMultiPortion( rMulti, rInf, nStartIdx );
2318 
2319  // If there is a HolePortion at the end of the bidi portion,
2320  // it has to be moved behind the bidi portion. Otherwise
2321  // the visual cursor travelling gets into trouble.
2322  SwLineLayout& aRoot = rMulti.GetRoot();
2323  SwLinePortion* pPor = aRoot.GetFirstPortion();
2324  while ( pPor )
2325  {
2326  if ( pPor->GetNextPortion() && pPor->GetNextPortion()->IsHolePortion() )
2327  {
2328  SwLinePortion* pHolePor = pPor->GetNextPortion();
2329  pPor->SetNextPortion( nullptr );
2330  aRoot.SetLen( aRoot.GetLen() - pHolePor->GetLen() );
2331  rMulti.SetLen( rMulti.GetLen() - pHolePor->GetLen() );
2332  rMulti.SetNextPortion( pHolePor );
2333  break;
2334  }
2335  pPor = pPor->GetNextPortion();
2336  }
2337 
2338  pTmp = new SwBidiPortion( nMultiLen + rInf.GetIdx(),
2339  static_cast<SwBidiPortion&>(rMulti).GetLevel() );
2340  }
2341  else
2342  pTmp = nullptr;
2343 
2344  if ( ! rMulti.GetLen() && rInf.GetLast() )
2345  {
2346  SeekAndChgBefore( rInf );
2347  rInf.GetLast()->FormatEOL( rInf );
2348  }
2349 
2350  if( pNextFirst && pTmp )
2351  {
2352  pTmp->SetFollowField();
2353  pTmp->GetRoot().SetNextPortion( pNextFirst );
2354  }
2355  else
2356  // A follow field portion is still waiting. If nobody wants it,
2357  // we delete it.
2358  delete pNextFirst;
2359 
2360  rInf.SetRest( pTmp );
2361  }
2362 
2363  rInf.SetCachedVclData(pOldCachedVclData);
2364  rInf.SetText( *pOldText );
2365  rInf.SetPaintOfst( nOldPaintOfst );
2366  rInf.SetStop( aInf.IsStop() );
2367  rInf.SetNumDone( true );
2368  rInf.SetFootnoteDone( true );
2369  SeekAndChg( rInf );
2370  delete pFirstRest;
2371  delete pSecondRest;
2372  xFontSave.reset();
2373  return bRet;
2374 }
2375 
2376 // When a fieldportion at the end of line breaks and needs a following
2377 // fieldportion in the next line, then the "restportion" of the formatinfo
2378 // has to be set. Normally this happens during the formatting of the first
2379 // part of the fieldportion.
2380 // But sometimes the formatting starts at the line with the following part,
2381 // especially when the following part is on the next page.
2382 // In this case the MakeRestPortion-function has to create the following part.
2383 // The first parameter is the line that contains possibly a first part
2384 // of a field. When the function finds such field part, it creates the right
2385 // restportion. This may be a multiportion, e.g. if the field is surrounded by
2386 // a doubleline- or ruby-portion.
2387 // The second parameter is the start index of the line.
2389  TextFrameIndex nPosition)
2390 {
2391  if( !nPosition )
2392  return nullptr;
2393  TextFrameIndex nMultiPos = nPosition - pLine->GetLen();
2394  const SwMultiPortion *pTmpMulti = nullptr;
2395  const SwMultiPortion *pHelpMulti = nullptr;
2396  const SwLinePortion* pPor = pLine->GetFirstPortion();
2397  SwFieldPortion *pField = nullptr;
2398  while( pPor )
2399  {
2400  if( pPor->GetLen() && !pHelpMulti )
2401  {
2402  nMultiPos = nMultiPos + pPor->GetLen();
2403  pTmpMulti = nullptr;
2404  }
2405  if( pPor->InFieldGrp() )
2406  {
2407  if( !pHelpMulti )
2408  pTmpMulti = nullptr;
2409  pField = const_cast<SwFieldPortion*>(static_cast<const SwFieldPortion*>(pPor));
2410  }
2411  else if( pPor->IsMultiPortion() )
2412  {
2413  OSL_ENSURE( !pHelpMulti || pHelpMulti->IsBidi(),
2414  "Nested multiportions are forbidden." );
2415 
2416  pField = nullptr;
2417  pTmpMulti = static_cast<const SwMultiPortion*>(pPor);
2418  }
2419  pPor = pPor->GetNextPortion();
2420  // If the last portion is a multi-portion, we enter it
2421  // and look for a field portion inside.
2422  // If we are already in a multiportion, we could change to the
2423  // next line
2424  if( !pPor && pTmpMulti )
2425  {
2426  if( pHelpMulti )
2427  { // We're already inside the multiportion, let's take the second
2428  // line, if we are in a double line portion
2429  if( !pHelpMulti->IsRuby() )
2430  pPor = pHelpMulti->GetRoot().GetNext();
2431  pTmpMulti = nullptr;
2432  }
2433  else
2434  { // Now we enter a multiportion, in a ruby portion we take the
2435  // main line, not the phonetic line, in a doublelineportion we
2436  // starts with the first line.
2437  pHelpMulti = pTmpMulti;
2438  nMultiPos = nMultiPos - pHelpMulti->GetLen();
2439  if( pHelpMulti->IsRuby() && pHelpMulti->OnTop() )
2440  pPor = pHelpMulti->GetRoot().GetNext();
2441  else
2442  pPor = pHelpMulti->GetRoot().GetFirstPortion();
2443  }
2444  }
2445  }
2446  if( pField && !pField->HasFollow() )
2447  pField = nullptr;
2448 
2449  SwLinePortion *pRest = nullptr;
2450  if( pField )
2451  {
2452  const SwTextAttr *pHint = GetAttr(nPosition - TextFrameIndex(1));
2453  if ( pHint
2454  && ( pHint->Which() == RES_TXTATR_FIELD
2455  || pHint->Which() == RES_TXTATR_ANNOTATION ) )
2456  {
2457  pRest = NewFieldPortion( GetInfo(), pHint );
2458  if( pRest->InFieldGrp() )
2459  static_cast<SwFieldPortion*>(pRest)->TakeNextOffset( pField );
2460  else
2461  {
2462  delete pRest;
2463  pRest = nullptr;
2464  }
2465  }
2466  }
2467  if( !pHelpMulti )
2468  return pRest;
2469 
2470  nPosition = nMultiPos + pHelpMulti->GetLen();
2471  std::optional<SwMultiCreator> pCreate = GetInfo().GetMultiCreator( nMultiPos, nullptr );
2472 
2473  if ( !pCreate )
2474  {
2475  OSL_ENSURE( !pHelpMulti->GetLen(), "Multiportion without attribute?" );
2476  if ( nMultiPos )
2477  --nMultiPos;
2478  pCreate = GetInfo().GetMultiCreator( --nMultiPos, nullptr );
2479  }
2480 
2481  if (!pCreate)
2482  return pRest;
2483 
2484  if( pRest || nMultiPos > nPosition || ( pHelpMulti->IsRuby() &&
2485  static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset() < TextFrameIndex(COMPLETE_STRING)))
2486  {
2487  SwMultiPortion* pTmp;
2488  if( pHelpMulti->IsDouble() )
2489  pTmp = new SwDoubleLinePortion( *pCreate, nMultiPos );
2490  else if( pHelpMulti->IsBidi() )
2491  pTmp = new SwBidiPortion( nMultiPos, pCreate->nLevel );
2492  else if( pHelpMulti->IsRuby() )
2493  {
2494  pTmp = new SwRubyPortion( *pCreate, *GetInfo().GetFont(),
2496  nMultiPos, static_cast<const SwRubyPortion*>(pHelpMulti)->GetRubyOffset(),
2497  GetInfo() );
2498  }
2499  else if( pHelpMulti->HasRotation() )
2500  pTmp = new SwRotatedPortion( nMultiPos, pHelpMulti->GetDirection() );
2501  else
2502  {
2503  return pRest;
2504  }
2505  pCreate.reset();
2506  pTmp->SetFollowField();
2507  if( pRest )
2508  {
2509  SwLineLayout *pLay = &pTmp->GetRoot();
2510  if( pTmp->IsRuby() && pTmp->OnTop() )
2511  {
2512  pLay->SetNext( new SwLineLayout() );
2513  pLay = pLay->GetNext();
2514  }
2515  pLay->SetNextPortion( pRest );
2516  }
2517  return pTmp;
2518  }
2519  return pRest;
2520 }
2521 
2522 // SwTextCursorSave notes the start and current line of a SwTextCursor,
2523 // sets them to the values for GetModelPositionForViewPoint inside a multiportion
2524 // and restores them in the destructor.
2526  SwMultiPortion* pMulti,
2527  SwTwips nY,
2528  SwTwips& nX,
2529  TextFrameIndex const nCurrStart,
2530  tools::Long nSpaceAdd )
2531  : pTextCursor(pCursor),
2532  pCurr(pCursor->m_pCurr),
2533  nStart(pCursor->m_nStart)
2534 {
2535  pCursor->m_nStart = nCurrStart;
2536  pCursor->m_pCurr = &pMulti->GetRoot();
2537  while( pCursor->Y() + pCursor->GetLineHeight() < nY &&
2538  pCursor->Next() )
2539  ; // nothing
2540  nWidth = pCursor->m_pCurr->Width();
2541  nOldProp = pCursor->GetPropFont();
2542 
2543  if ( pMulti->IsDouble() || pMulti->IsBidi() )
2544  {
2545  bSpaceChg = pMulti->ChgSpaceAdd( pCursor->m_pCurr, nSpaceAdd );
2546 
2547  TextFrameIndex nSpaceCnt;
2548  if ( pMulti->IsDouble() )
2549  {
2550  pCursor->SetPropFont( 50 );
2551  nSpaceCnt = static_cast<SwDoubleLinePortion*>(pMulti)->GetSpaceCnt();
2552  }
2553  else
2554  {
2555  TextFrameIndex const nOldIdx = pCursor->GetInfo().GetIdx();
2556  pCursor->GetInfo().SetIdx ( nCurrStart );
2557  nSpaceCnt = static_cast<SwBidiPortion*>(pMulti)->GetSpaceCnt(pCursor->GetInfo());
2558  pCursor->GetInfo().SetIdx ( nOldIdx );
2559  }
2560 
2561  if( nSpaceAdd > 0 && !pMulti->HasTabulator() )
2562  pCursor->m_pCurr->Width( o3tl::narrowing<sal_uInt16>(nWidth + nSpaceAdd * sal_Int32(nSpaceCnt) / SPACING_PRECISION_FACTOR) );
2563 
2564  // For a BidiPortion we have to calculate the offset from the
2565  // end of the portion
2566  if ( nX && pMulti->IsBidi() )
2567  nX = pCursor->m_pCurr->Width() - nX;
2568  }
2569  else
2570  bSpaceChg = false;
2571 }
2572 
2574 {
2575  if( bSpaceChg )
2581 }
2582 
2583 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void dumpAsXml(xmlTextWriterPtr pWriter) const override
Definition: pormulti.cxx:137
virtual void Height(const SwTwips nNew, const bool bText=true) override
Definition: porlay.cxx:199
void Move(SwTextPaintInfo &rInf)
Definition: porlin.cxx:268
Represents the visualization of a paragraph.
Definition: txtfrm.hxx:159
SwFontScript WhichFont(TextFrameIndex nIdx) const
Definition: porlay.cxx:834
virtual tools::Long CalcSpacing(tools::Long nSpaceAdd, const SwTextSizeInfo &rInf) const override
Definition: pormulti.cxx:122
void FormatReset(SwTextFormatInfo &rInf)
Definition: itrform2.cxx:2010
void SetUnderFnt(SwUnderlineFont *pNew)
Definition: inftxt.hxx:235
void CalcSize(SwTextFormatter &rLine, SwTextFormatInfo &rInf)
Definition: pormulti.cxx:66
void SetSnapToGrid(const bool bN)
Definition: inftxt.hxx:216
SwRubyPortion(const SwRubyPortion &rRuby, TextFrameIndex nEnd)
Definition: pormulti.cxx:553
virtual ~SwMultiPortion() override
Definition: pormulti.cxx:55
virtual ~SwDoubleLinePortion() override
Definition: pormulti.cxx:547
void SetTab1(bool bNew)
Definition: pormulti.hxx:113
sal_uInt16 BracketWidth()
Definition: pormulti.hxx:175
void CalcNewBlock(SwLineLayout *pCurr, const SwLinePortion *pStopAt, SwTwips nReal=0, bool bSkipKashida=false)
Definition: itradj.cxx:250
TextFrameIndex nStart
Definition: pormulti.hxx:240
bool IsRest() const
Definition: porlay.hxx:131
SwExpandPortion * NewFieldPortion(SwTextFormatInfo &rInf, const SwTextAttr *pHt) const
Definition: txtfld.cxx:76
SwTwips GetPaintOfst() const
Definition: inftxt.hxx:730
SwFont * GetFont()
Definition: inftxt.hxx:231
SwLineLayout * GetNext()
Definition: porlay.hxx:161
SwTextAttr * GetAttr(TextFrameIndex nPos) const
Returns the attribute for a position.
Definition: itratr.cxx:143
sal_Unicode GetStartBracket() const
std::string GetValue
TextFrameIndex GetStart() const
Definition: itrtxt.hxx:88
RubyPosition
Definition: pormulti.hxx:43
bool IsInFootnote() const
Definition: frame.hxx:950
bool IsRuby() const
Definition: inftxt.hxx:207
const SfxPoolItem * GetItem(const SwTextAttr &rAttr, sal_uInt16 nWhich)
Extracts pool item of type nWhich from rAttr.
Definition: atrstck.cxx:156
static bool lcl_HasRotation(const SwTextAttr &rAttr, const SvxCharRotateItem *&rpRef, bool &rValue)
Definition: pormulti.cxx:815
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:278
sal_uInt8 m_nLevel
Definition: pormulti.hxx:220
long Long
void SetPropFont(const sal_uInt8 nNew)
Definition: itratr.hxx:107
const SwRect & getFramePrintArea() const
Definition: frame.hxx:181
#define SW_SCRIPTS
Definition: swfont.hxx:128
The SwPortionHandler interface implements a visitor for the layout engine's text portions.
void SetFlyInContent(bool bNew)
Definition: pormulti.hxx:129
void SetLast(SwLinePortion *pNewLast)
Definition: inftxt.hxx:561
void CalcLine(SwTextFormatter &rLine, SwTextFormatInfo &rInf)
Definition: porlay.cxx:335
const SwLineLayout & GetRoot() const
Definition: pormulti.hxx:120
bool IsNumDone() const
Definition: inftxt.hxx:628
void CalcRubyOffset()
Definition: pormulti.cxx:734
void SetLen(TextFrameIndex const nLen)
Definition: porlin.hxx:76
bool GetRubyTextBelow() const
Definition: tgrditem.hxx:85
virtual tools::Long CalcSpacing(tools::Long nSpaceAdd, const SwTextSizeInfo &rInf) const override
Definition: pormulti.cxx:496
SwTableLine is one table row in the document model.
Definition: swtable.hxx:358
#define DIR_TOP2BOTTOM
Definition: inftxt.hxx:57
bool SeekAndChg(SwTextSizeInfo &rInf)
Definition: itrtxt.hxx:310
bool IsBottomToTop() const
SwTextCursor * pTextCursor
Definition: pormulti.hxx:238
const SwLineLayout * Next()
Definition: itrtxt.cxx:107
Dialog to specify the properties of date form field.
void SetText(const OUString &rNew)
Definition: inftxt.hxx:275
bool IsCellFrame() const
Definition: frame.hxx:1227
void SetMulti(const bool bNew)
Definition: inftxt.hxx:204
const std::shared_ptr< vcl::text::TextLayoutCache > & GetCachedVclData() const
Definition: inftxt.hxx:328
Of course Writer needs its own rectangles.
Definition: swrect.hxx:34
sal_uInt16 Which() const
Definition: txatbase.hxx:114
bool InFieldGrp() const
Definition: porlin.hxx:105
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:157
SwTwips Y() const
Definition: itrtxt.hxx:90
SwPosSize GetTextSize(OutputDevice *pOut, const SwScriptInfo *pSI, const OUString &rText, TextFrameIndex nIdx, TextFrameIndex nLen) const
Definition: inftxt.cxx:382
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(59)
static bool lcl_Check2Lines(const SfxPoolItem *const pItem, const SvxTwoLinesItem *&rpRef, bool &rValue)
Definition: pormulti.cxx:766
sal_uInt8 GetDirection() const
Definition: pormulti.hxx:148
virtual void Text(TextFrameIndex nLength, PortionType nType, sal_Int32 nHeight=0, sal_Int32 nWidth=0)=0
(empty) destructor
css::chart::ChartAxisLabelPosition ePos
Collection of SwLineLayout instances, represents the paragraph text in Writer layout.
Definition: porlay.hxx:251
Degree10 GetOrientation(const bool bVertLayout=false, const bool bVertFormatLRBT=false) const
Definition: swfont.cxx:412
virtual void Paint(const SwTextPaintInfo &rInf) const override
Definition: pormulti.cxx:59
sal_uInt8 DirType(const TextFrameIndex nPos) const
Definition: porlay.cxx:1769
tools::Long GetLLSpaceAdd(sal_uInt16 nIdx)
Definition: porlay.hxx:197
constexpr TypedWhichId< SvxTwoLinesItem > RES_CHRATR_TWO_LINES(34)
void SetTab2(bool bNew)
Definition: pormulti.hxx:114
SwTextFormatInfo & GetInfo()
Definition: itrform2.hxx:213
bool IsMarginPortion() const
Definition: porlin.hxx:127
virtual void HandlePortion(SwPortionHandler &rPH) const override
Definition: pormulti.cxx:132
#define CH_TXTATR_BREAKWORD
Definition: hintids.hxx:170
void PaintBracket(SwTextPaintInfo &rInf, tools::Long nSpaceAdd, bool bOpen) const
Definition: pormulti.cxx:349
sal_uInt16 sal_Unicode
SwRotatedPortion(TextFrameIndex const nEnd, sal_uInt8 nDir)
Definition: pormulti.hxx:212
Degree10 GetValue() const
sal_uInt16 GetLLSpaceAddCount() const
Definition: porlay.hxx:189
sal_Unicode GetEndBracket() const
virtual void Paint(const SwTextPaintInfo &rInf) const =0
bool BuildMultiPortion(SwTextFormatInfo &rInf, SwMultiPortion &rMulti)
Definition: pormulti.cxx:1898
void FormatBrackets(SwTextFormatInfo &rInf, SwTwips &nMaxWidth)
Definition: pormulti.cxx:398
bool IsBreakPortion() const
Definition: porlin.hxx:115
const SfxPoolItem * pItem
Definition: pormulti.hxx:54
A wrapper around SfxPoolItem to store the start position of (usually) a text portion, with an optional end.
Definition: txatbase.hxx:41
sal_Unicode GetChar(TextFrameIndex const nPos) const
Definition: inftxt.hxx:240
void SetFollow(bool bNew)
Definition: porfld.hxx:83
bool IsInTab() const
Definition: frame.hxx:956
bool HasRotation() const
Definition: pormulti.hxx:146
SwTextPaintInfo & GetInfo()
Definition: itrpaint.hxx:57
void Adjust_(SwTextFormatInfo &rInf)
Definition: pormulti.cxx:645
sal_Int32 GetStart() const
Definition: txatbase.hxx:86
bool IsSquaredMode() const
Definition: tgrditem.hxx:104
void DrawViewOpt(const SwLinePortion &rPor, PortionType nWhich, const Color *pColor=nullptr) const
Definition: inftxt.cxx:1277
void Width(tools::Long nNew)
Definition: swrect.hxx:189
TextFrameIndex m_nRubyOffset
Definition: pormulti.hxx:191
constexpr TypedWhichId< SwFormatRuby > RES_TXTATR_CJK_RUBY(53)
static void lcl_TruncateMultiPortion(SwMultiPortion &rMulti, SwTextFormatInfo &rInf, TextFrameIndex const nStartIdx)
Definition: pormulti.cxx:1877
bool IsSpaceAdd() const
Definition: porlay.hxx:185
TextFrameIndex GetLineStart() const
Definition: inftxt.hxx:589
void SetCachedVclData(std::shared_ptr< vcl::text::TextLayoutCache > const &pCachedVclData)
Definition: inftxt.hxx:332
void SetBidi()
Definition: pormulti.hxx:111
void SetPaintOfst(const SwTwips nNew)
Definition: inftxt.hxx:735
void SetNumDone(const bool bNew)
Definition: inftxt.hxx:629
void SetFormatted()
Definition: pormulti.hxx:125
bool IsBidi() const
Definition: pormulti.hxx:132
sal_uInt8 nOldProp
Definition: pormulti.hxx:242
SwTwips Y() const
Definition: inftxt.hxx:380
static bool lcl_Has2Lines(const SwTextAttr &rAttr, const SvxTwoLinesItem *&rpRef, bool &rValue)
Definition: pormulti.cxx:784
static SwTextPortion * CopyLinePortion(const SwLinePortion &rPortion)
Definition: portxt.cxx:211
sal_Int32 m_nLevel
TextFrameIndex NextDirChg(const TextFrameIndex nPos, const sal_uInt8 *pLevel=nullptr) const
Definition: porlay.cxx:1754
Describes a part of a single text node, which will be part of a text frame, even when redlines are hi...
Definition: txtfrm.hxx:85
struct _xmlTextWriter * xmlTextWriterPtr
const SwViewOption & GetOpt() const
Definition: inftxt.hxx:238
constexpr TypedWhichId< SwFormatField > RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN)
sal_uInt16 nWidth
Definition: pormulti.hxx:241
virtual SwLinePortion * Append(SwLinePortion *pPortion)
Definition: porlin.cxx:189
void PaintMultiPortion(const SwRect &rPaint, SwMultiPortion &rMulti, const SwMultiPortion *pEnvPor=nullptr)
Definition: pormulti.cxx:1510
#define DIR_LEFT2RIGHT
Definition: inftxt.hxx:54
SfxItemState GetItemState(sal_uInt16 nWhich, bool bSrchInParent=true, const SfxPoolItem **ppItem=nullptr) const
void SetFollowField()
Definition: pormulti.hxx:127
Reference< XAnimationNode > Clone(const Reference< XAnimationNode > &xSourceNode, const SdPage *pSource, const SdPage *pTarget)
Collection of SwLinePortion instances, representing one line of text.
Definition: porlay.hxx:80
static bool lcl_ExtractFieldFollow(SwLineLayout *pLine, SwLinePortion *&rpField)
Definition: pormulti.cxx:1850
SwMultiPortion * m_pMulti
Definition: itrform2.hxx:36
static void ResetSpaceAdd(SwLineLayout *pCurr)
Definition: pormulti.cxx:540
void SetStop(const bool bNew)
Definition: inftxt.hxx:574
SwPageFrame * FindPageFrame()
Definition: frame.hxx:681
SwParaPortion * GetParaPortion()
Definition: inftxt.hxx:121
css::text::RubyAdjust m_nAdjustment
Definition: pormulti.hxx:192
const SwTextAttr * pAttr
Definition: pormulti.hxx:53
bool GetTab1() const
Definition: pormulti.hxx:116
Count
SwTextGridItem const * GetGridItem(SwPageFrame const *const)
Definition: pagechg.cxx:2574
void SetVertical(Degree10 nDir, const bool bVertLayout=false, const bool bVertLayoutLRBT=false)
Definition: swfont.cxx:417
SwNodeOffset GetIndex() const
Definition: node.hxx:292
SwLinePortion * GetFirstPortion() const
Definition: porlay.cxx:783
bool IsParagraph(bool bHard=false) const
Definition: viewopt.hxx:236
TextFrameIndex GetSpaceCnt(const SwTextSizeInfo &rInf) const
Definition: pormulti.cxx:227
SwLayoutFrame * GetUpper()
Definition: frame.hxx:679
SwFont & GetFont()
Definition: swfont.hxx:962
SwTextCursorSave(SwTextCursor *pTextCursor, SwMultiPortion *pMulti, SwTwips nY, SwTwips &nX, TextFrameIndex nCurrStart, tools::Long nSpaceAdd)
Definition: pormulti.cxx:2525
void BuildPortions(SwTextFormatInfo &rInf)
Definition: itrform2.cxx:363
virtual bool ChgSpaceAdd(SwLineLayout *pCurr, tools::Long nSpaceAdd) const override
Definition: pormulti.cxx:507
sal_uInt16 Width() const
Definition: inftxt.hxx:527
void FinishSpaceAdd()
Definition: porlay.hxx:188
SwLineLayout * GetRoot()
Definition: inftxt.hxx:556
Provides access to settings of a document.
SwFrameFormat * GetFrameFormat()
Definition: swtable.hxx:380
void Modify(bool bChgToRTL)
Definition: txtfrm.cxx:712
TextFrameIndex GetIdx() const
Definition: inftxt.hxx:271
TextFrameIndex GetSmallerSpaceCnt() const
Definition: pormulti.hxx:182
T static_txtattr_cast(S *s)
Definition: txatbase.hxx:241
void CalcBlanks(SwTextFormatInfo &rInf)
Definition: pormulti.cxx:464
void PrePaint(const SwTextPaintInfo &rInf, const SwLinePortion *pLast) const
Definition: porlin.cxx:76
void SetRuby(const bool bNew)
Definition: inftxt.hxx:208
SwTwips Height() const
Definition: possiz.hxx:50
sal_uInt8 nLevel
Definition: pormulti.hxx:56
vcl::Font GetFont(vcl::Font const &rFont, DrawModeFlags nDrawMode, StyleSettings const &rStyleSettings)
bool SnapToGrid() const
Definition: inftxt.hxx:215
Represents the style of a text portion.
Definition: charfmt.hxx:26
TextFrameIndex GetLen() const
Definition: porlin.hxx:75
void SetPos(const Point &rPoint)
Definition: swfont.hxx:970
A page of the document layout.
Definition: pagefrm.hxx:57
bool InTabGrp() const
Definition: porlin.hxx:101
SwTextFrame * GetTextFrame()
Definition: inftxt.hxx:284
sal_uInt16 PreWidth() const
Definition: pormulti.hxx:171
std::pair< SwTextNode *, sal_Int32 > MapViewToModel(TextFrameIndex nIndex) const
map position in potentially merged text frame to SwPosition
Definition: txtfrm.cxx:1232
tools::Long SwTwips
Definition: swtypes.hxx:52
const OUString & GetText() const
Definition: inftxt.hxx:239
bool IsHolePortion() const
Definition: porlin.hxx:129
std::unique_ptr< SwBracket > m_pBracket
Definition: pormulti.hxx:158
SwTextFrame * m_pFrame
Definition: inftxt.hxx:152
SwLayoutFrame * FindBodyCont()
Searches the first ContentFrame in BodyText below the page.
Definition: findfrm.cxx:43
SwTwips & GetAscent()
Definition: porlin.hxx:78
SwUnderlineFont * GetUnderFnt() const
Definition: inftxt.hxx:236
void Truncate()
Definition: porlin.hxx:201
bool HasBrackets() const
Definition: pormulti.hxx:250
const Point & GetPos() const
Definition: inftxt.hxx:428
bool IsMultiPortion() const
Definition: porlin.hxx:137
bool OnTop() const
Definition: pormulti.hxx:133
SwTwips X() const
Definition: inftxt.hxx:378
SwTextFrame * m_pFrame
Definition: itrtxt.hxx:34
Base class for anything that can be part of a line in the Writer layout.
Definition: porlin.hxx:51
bool SeekAndChgBefore(SwTextSizeInfo &rInf)
Definition: itrtxt.hxx:315
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:79
TextFrameIndex m_nStart
Definition: itrtxt.hxx:41
#define DIR_RIGHT2LEFT
Definition: inftxt.hxx:56
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
bool IsRevers() const
Definition: pormulti.hxx:147
void SetLen(const TextFrameIndex nNew)
Definition: inftxt.hxx:274
SwTwips Width() const
Definition: possiz.hxx:52
TextFrameIndex m_nBlank1
Number of blanks in the first line.
Definition: pormulti.hxx:160
void SetRuby()
Definition: pormulti.hxx:110
sal_uInt8 GetPropFont() const
Definition: itratr.hxx:106
void SetNext(SwLineLayout *pNew)
Definition: porlay.hxx:163
SwMultiCreatorId nId
Definition: pormulti.hxx:55
unsigned char sal_uInt8
PortionType GetWhichPor() const
Definition: porlin.hxx:96
bool GetTab2() const
Definition: pormulti.hxx:117
void SetDirection(sal_uInt8 nNew)
Definition: pormulti.hxx:115
bool IsDouble() const
Definition: pormulti.hxx:130
void SetFootnoteDone(const bool bNew)
Definition: inftxt.hxx:625
bool OnRight() const
Definition: pormulti.hxx:134
sal_uInt16 PostWidth() const
Definition: pormulti.hxx:172
SwLinePortion * GetRest()
Definition: inftxt.hxx:575
TextFrameIndex MapModelToView(SwTextNode const *pNode, sal_Int32 nIndex) const
Definition: txtfrm.cxx:1253
SwLinePortion * FindLastPortion()
Definition: porlin.cxx:178
TextFrameIndex GetSpaceCnt() const
Definition: pormulti.hxx:180
virtual void Paint(const SwTextPaintInfo &rInf) const override
Definition: porexp.cxx:195
void RemoveFirstLLSpaceAdd()
Definition: porlay.hxx:198
IDocumentSettingAccess const & getIDocumentSettingAccess() const
Definition: doc.cxx:176
SvxAdjust GetAdjust() const
Definition: itrtxt.hxx:190
void ActualizeTabulator()
Definition: pormulti.cxx:152
SwTextSizeInfo & GetInfo()
Definition: itrtxt.hxx:216
virtual bool ChgSpaceAdd(SwLineLayout *pCurr, tools::Long nSpaceAdd) const override
Definition: pormulti.cxx:215
bool IsRightToLeft() const
Definition: frame.hxx:988
virtual tools::Long CalcSpacing(tools::Long nSpaceAdd, const SwTextSizeInfo &rInf) const override
Definition: pormulti.cxx:210
TextFrameIndex GetLen() const
Definition: inftxt.hxx:273
const sal_Int32 * End() const
Definition: txatbase.hxx:152
void CreateSpaceAdd(const tools::Long nInit=0)
Definition: porlay.cxx:312
void SetDouble()
Definition: pormulti.hxx:109
void SetDirection(const sal_uInt8 nNew)
Definition: inftxt.hxx:218
bool HasFollow() const
Definition: porfld.hxx:94
void AddPrtWidth(const SwTwips nNew)
Definition: porlin.hxx:83
bool InTextGrp() const
Definition: porlin.hxx:99
double getLength(const B2DPolygon &rCandidate)
SwFontScript
Definition: swfont.hxx:122
SwLinePortion * GetLast()
Definition: inftxt.hxx:560
friend class SwFontSave
Definition: itratr.hxx:34
void SetRealHeight(SwTwips nNew)
Definition: porlay.hxx:170
RubyPosition GetRubyPosition() const
Definition: pormulti.hxx:135
const SwFormatFrameSize & GetFrameSize(bool=true) const
Definition: fmtfsize.hxx:104
void SetNextPortion(SwLinePortion *pNew)
Definition: porlin.hxx:77
const SwAttrSet & GetSwAttrSet() const
Does node has already its own auto-attributes? Access to SwAttrSet.
Definition: node.hxx:716
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:58
void SetBrackets(const SwDoubleLinePortion &rDouble)
Definition: pormulti.cxx:382
SwLinePortion * MakeRestPortion(const SwLineLayout *pLine, TextFrameIndex nPos)
Definition: pormulti.cxx:2388
bool IsVertical() const
Definition: frame.hxx:974
TextFrameIndex nStartOfAttr
Definition: pormulti.hxx:52
#define SPACING_PRECISION_FACTOR
Definition: scriptinfo.hxx:40
void SetNextOffset(TextFrameIndex nNew)
Definition: porfld.hxx:98
SwDoc & GetDoc()
Definition: txtfrm.hxx:461
constexpr TypedWhichId< SvxCharRotateItem > RES_CHRATR_ROTATE(32)
A layout frame is a frame that contains other frames (m_pLower), e.g. SwPageFrame or SwTabFrame...
Definition: layfrm.hxx:35
std::optional< SwMultiCreator > GetMultiCreator(TextFrameIndex &rPos, SwMultiPortion const *pM) const
Definition: pormulti.cxx:921
SwTextGrid GetGridType() const
Definition: tgrditem.hxx:81
bool HasTabulator() const
Definition: pormulti.hxx:123
TextFrameIndex m_nBlank2
Number of blanks in the second line.
Definition: pormulti.hxx:161
void SetRubyPosition(RubyPosition eNew)
Definition: pormulti.hxx:112
SwTextFrame * GetTextFrame()
Definition: itrtxt.hxx:134
bool IsFlyInCntBase() const
Definition: itrform2.hxx:210
SwLineLayout * m_pCurr
Definition: itrtxt.hxx:36
SwTwips GetLineHeight() const
Definition: itrtxt.hxx:116
const SwAttrSet & GetAttrSet() const
For querying the attribute array.
Definition: format.hxx:120
SwLineLayout * pCurr
Definition: pormulti.hxx:239
virtual void FormatEOL(SwTextFormatInfo &rInf)
Definition: porlin.cxx:265
void CheckSpecialUnderline(const SwLinePortion *pPor, tools::Long nAdjustBaseLine=0)
Definition: itrpaint.cxx:501
Frame is variable in Var-direction.
std::vector< tools::Long > * GetpLLSpaceAdd() const
Definition: porlay.hxx:199
void SetProportion(const sal_uInt8 nNewPropr)
Definition: swfont.hxx:762
SwLinePortion * GetNextPortion() const
Definition: porlin.hxx:73
void SetIdx(const TextFrameIndex nNew)
Definition: inftxt.hxx:272
virtual bool ChgSpaceAdd(SwLineLayout *pCurr, tools::Long nSpaceAdd) const
Definition: pormulti.cxx:127
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...
void SetRest(SwLinePortion *pNewRest)
Definition: inftxt.hxx:576
SwBidiPortion(TextFrameIndex nEnd, sal_uInt8 nLv)
Definition: pormulti.cxx:199
void Height(tools::Long nNew)
Definition: swrect.hxx:193
bool IsFormatted() const
Definition: pormulti.hxx:124
TextFrameIndex GetNextOffset() const
Definition: porfld.hxx:97
bool IsRuby() const
Definition: pormulti.hxx:131
aStr
sal_uInt16 GetRubyHeight() const
Definition: tgrditem.hxx:78
SwRedlineItr * GetRedln()
Definition: itratr.hxx:81
SwBracket * GetBrackets() const
Definition: pormulti.hxx:167
bool IsFootnoteDone() const
Definition: inftxt.hxx:624
SwDoubleLinePortion(SwDoubleLinePortion &rDouble, TextFrameIndex nEnd)
Definition: pormulti.cxx:252
const SwFormatRuby & GetRuby() const
Definition: txatbase.hxx:234
SwTwips AdjustBaseLine(const SwLineLayout &rLine, const SwLinePortion *pPor, SwTwips nPorHeight=0, SwTwips nAscent=0, const bool bAutoToCentered=false) const
Definition: itrtxt.cxx:214
static bool lcl_CheckRotation(const SfxPoolItem *const pItem, const SvxCharRotateItem *&rpRef, bool &rValue)
Definition: pormulti.cxx:798
SwRowFrame is one table row in the document layout.
Definition: rowfrm.hxx:28
sal_uInt16 GetAscent() const
Definition: inftxt.hxx:706
void SetLLSpaceAdd(tools::Long nNew, sal_uInt16 nIdx)
Definition: porlay.hxx:190
SwFrameSize GetHeightSizeType() const
Definition: fmtfsize.hxx:80
void SetAscent(const SwTwips nNewAsc)
Definition: porlin.hxx:80
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo