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