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