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