LibreOffice Module sw (master) 1
itradj.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <sal/config.h>
21
22#include <o3tl/safeint.hxx>
23
25#include <doc.hxx>
26
27#include "itrtxt.hxx"
28#include "porglue.hxx"
29#include "porlay.hxx"
30#include "porfly.hxx"
31#include "pormulti.hxx"
32#include "portab.hxx"
33#include <memory>
34
35#define MIN_TAB_WIDTH 60
36
37using namespace ::com::sun::star;
38
40{
41 // Block format does not apply to the last line.
42 // And for tabs it doesn't exist out of tradition
43 // If we have Flys we continue.
44
45 const SwLinePortion *pFly = nullptr;
46
47 bool bSkip = !IsLastBlock() &&
49
50 // Multi-line fields are tricky, because we need to check whether there are
51 // any other text portions in the paragraph.
52 if( bSkip )
53 {
54 const SwLineLayout *pLay = m_pCurr->GetNext();
55 while( pLay && !pLay->GetLen() )
56 {
57 const SwLinePortion *pPor = m_pCurr->GetFirstPortion();
58 while( pPor && bSkip )
59 {
60 if( pPor->InTextGrp() )
61 bSkip = false;
62 pPor = pPor->GetNextPortion();
63 }
64 pLay = bSkip ? pLay->GetNext() : nullptr;
65 }
66 }
67
68 if( bSkip )
69 {
70 if( !GetInfo().GetParaPortion()->HasFly() )
71 {
72 if( IsLastCenter() )
75 return;
76 }
77 else
78 {
79 const SwLinePortion *pTmpFly = nullptr;
80
81 // End at the last Fly
82 const SwLinePortion *pPos = m_pCurr->GetFirstPortion();
83 while( pPos )
84 {
85 // Look for the last Fly which has text coming after it:
86 if( pPos->IsFlyPortion() )
87 pTmpFly = pPos; // Found a Fly
88 else if ( pTmpFly && pPos->InTextGrp() )
89 {
90 pFly = pTmpFly; // A Fly with follow-up text!
91 pTmpFly = nullptr;
92 }
93 pPos = pPos->GetNextPortion();
94 }
95 // End if we didn't find one
96 if( !pFly )
97 {
98 if( IsLastCenter() )
101 return;
102 }
103 }
104 }
105
106 const TextFrameIndex nOldIdx = GetInfo().GetIdx();
108 CalcNewBlock( m_pCurr, pFly );
109 GetInfo().SetIdx( nOldIdx );
111}
112
114 sal_Int32& rKashidas, TextFrameIndex& nGluePortion)
115{
116 // i60594 validate Kashida justification
117 TextFrameIndex nIdx = rItr.GetStart();
118 TextFrameIndex nEnd = rItr.GetEnd();
119
120 // Note on calling KashidaJustify():
121 // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
122 // total number of kashida positions, or the number of kashida positions after some positions
123 // have been dropped.
124 // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
125 rKashidas = rSI.KashidaJustify(nullptr, nullptr, rItr.GetStart(), rItr.GetLength());
126
127 if (rKashidas <= 0) // nothing to do
128 return true;
129
130 // kashida positions found in SwScriptInfo are not necessarily valid in every font
131 // if two characters are replaced by a ligature glyph, there will be no place for a kashida
132 std::vector<TextFrameIndex> aKashidaPos;
133 rSI.GetKashidaPositions(nIdx, rItr.GetLength(), aKashidaPos);
134 assert(aKashidaPos.size() >= o3tl::make_unsigned(rKashidas));
135 std::vector<TextFrameIndex> aKashidaPosDropped(aKashidaPos.size());
136 sal_Int32 nKashidaIdx = 0;
137 while ( rKashidas && nIdx < nEnd )
138 {
139 rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
140 TextFrameIndex nNext = rItr.GetNextAttr();
141
142 // is there also a script change before?
143 // if there is, nNext should point to the script change
144 TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
145 if( nNextScript < nNext )
146 nNext = nNextScript;
147
148 if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
149 nNext = nEnd;
150 sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, nIdx, nNext - nIdx);
151 if (nKashidasInAttr > 0)
152 {
153 // Kashida glyph looks suspicious, skip Kashida justification
154 if ( rInf.GetOut()->GetMinKashida() <= 0 )
155 {
156 return false;
157 }
158
159 sal_Int32 nKashidasDropped = 0;
160 if ( !SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
161 {
162 nKashidasDropped = nKashidasInAttr;
163 rKashidas -= nKashidasDropped;
164 }
165 else
166 {
169 nKashidasDropped = rInf.GetOut()->ValidateKashidas(
170 rInf.GetText(), sal_Int32(nIdx), sal_Int32(nNext - nIdx),
171 nKashidasInAttr,
172 reinterpret_cast<sal_Int32*>(aKashidaPos.data() + nKashidaIdx),
173 reinterpret_cast<sal_Int32*>(aKashidaPosDropped.data()));
174 rInf.GetOut()->SetLayoutMode ( nOldLayout );
175 if ( nKashidasDropped )
176 {
177 rSI.MarkKashidasInvalid(nKashidasDropped, aKashidaPosDropped.data());
178 rKashidas -= nKashidasDropped;
179 nGluePortion -= TextFrameIndex(nKashidasDropped);
180 }
181 }
182 nKashidaIdx += nKashidasInAttr;
183 }
184 nIdx = nNext;
185 }
186
187 // return false if all kashidas have been eliminated
188 return (rKashidas > 0);
189}
190
191static bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTextSizeInfo& rInf, SwTextIter& rItr, sal_Int32& rKashidas,
192 TextFrameIndex& nGluePortion, const tools::Long nGluePortionWidth, tools::Long& nSpaceAdd )
193{
194 // check kashida width
195 // if width is smaller than minimal kashida width allowed by fonts in the current line
196 // drop one kashida after the other until kashida width is OK
197 while (rKashidas)
198 {
199 bool bAddSpaceChanged = false;
200 TextFrameIndex nIdx = rItr.GetStart();
201 TextFrameIndex nEnd = rItr.GetEnd();
202 while ( nIdx < nEnd )
203 {
204 rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
205 TextFrameIndex nNext = rItr.GetNextAttr();
206
207 // is there also a script change before?
208 // if there is, nNext should point to the script change
209 TextFrameIndex const nNextScript = rSI.NextScriptChg( nIdx );
210 if( nNextScript < nNext )
211 nNext = nNextScript;
212
213 if (nNext == TextFrameIndex(COMPLETE_STRING) || nNext > nEnd)
214 nNext = nEnd;
215 sal_Int32 nKashidasInAttr = rSI.KashidaJustify(nullptr, nullptr, nIdx, nNext - nIdx);
216
217 tools::Long nFontMinKashida = rInf.GetOut()->GetMinKashida();
218 if ( nFontMinKashida && nKashidasInAttr > 0 && SwScriptInfo::IsArabicText( rInf.GetText(), nIdx, nNext - nIdx ) )
219 {
220 sal_Int32 nKashidasDropped = 0;
221 while ( rKashidas && nGluePortion && nKashidasInAttr > 0 &&
222 nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
223 {
224 --nGluePortion;
225 --rKashidas;
226 --nKashidasInAttr;
227 ++nKashidasDropped;
228 if( !rKashidas || !nGluePortion ) // nothing left, return false to
229 return false; // do regular blank justification
230
231 nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
232 bAddSpaceChanged = true;
233 }
234 if( nKashidasDropped )
235 rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
236 }
237 if ( bAddSpaceChanged )
238 break; // start all over again
239 nIdx = nNext;
240 }
241 if ( !bAddSpaceChanged )
242 break; // everything was OK
243 }
244 return true;
245}
246
247// CalcNewBlock() must only be called _after_ CalcLine()!
248// We always span between two RandPortions or FixPortions (Tabs and Flys).
249// We count the Glues and call ExpandBlock.
251 const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida )
252{
253 OSL_ENSURE( GetInfo().IsMulti() || SvxAdjust::Block == GetAdjust(),
254 "CalcNewBlock: Why?" );
255 OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
256
257 pCurrent->InitSpaceAdd();
258 TextFrameIndex nGluePortion(0);
259 TextFrameIndex nCharCnt(0);
260 sal_uInt16 nSpaceIdx = 0;
261
262 // i60591: hennerdrews
264 SwTextSizeInfo aInf ( GetTextFrame() );
265 SwTextIter aItr ( GetTextFrame(), &aInf );
266
267 if ( rSI.CountKashida() )
268 {
269 while (aItr.GetCurr() != pCurrent && aItr.GetNext())
270 aItr.Next();
271
272 if( bSkipKashida )
273 {
274 rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
275 }
276 else
277 {
278 rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
279 rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
280 }
281 }
282
283 // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
284 if (!bSkipKashida)
285 CalcRightMargin( pCurrent, nReal );
286
287 // #i49277#
288 const bool bDoNotJustifyLinesWithManualBreak =
290 bool bDoNotJustifyTab = false;
291
292 SwLinePortion *pPos = pCurrent->GetNextPortion();
293
294 while( pPos )
295 {
296 if ( ( bDoNotJustifyLinesWithManualBreak || bDoNotJustifyTab ) &&
297 pPos->IsBreakPortion() && !IsLastBlock() )
298 {
299 pCurrent->FinishSpaceAdd();
300 break;
301 }
302
303 switch ( pPos->GetWhichPor() )
304 {
308 bDoNotJustifyTab = true;
309 break;
312 bDoNotJustifyTab = false;
313 break;
314 default: break;
315 }
316
317 if ( pPos->InTextGrp() )
318 nGluePortion = nGluePortion + static_cast<SwTextPortion*>(pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
319 else if( pPos->IsMultiPortion() )
320 {
321 SwMultiPortion* pMulti = static_cast<SwMultiPortion*>(pPos);
322 // a multiportion with a tabulator inside breaks the text adjustment
323 // a ruby portion will not be stretched by text adjustment
324 // a double line portion takes additional space for each blank
325 // in the wider line
326 if( pMulti->HasTabulator() )
327 {
328 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
329 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
330
331 nSpaceIdx++;
332 nGluePortion = TextFrameIndex(0);
333 nCharCnt = TextFrameIndex(0);
334 }
335 else if( pMulti->IsDouble() )
336 nGluePortion = nGluePortion + static_cast<SwDoubleLinePortion*>(pMulti)->GetSpaceCnt();
337 else if ( pMulti->IsBidi() )
338 nGluePortion = nGluePortion + static_cast<SwBidiPortion*>(pMulti)->GetSpaceCnt( GetInfo() ); // i60594
339 }
340
341 if( pPos->InGlueGrp() )
342 {
343 if( pPos->InFixMargGrp() )
344 {
345 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
346 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
347
348 const tools::Long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
350
351 sal_Int32 nKashidas = 0;
352 if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
353 {
354 // kashida positions found in SwScriptInfo are not necessarily valid in every font
355 // if two characters are replaced by a ligature glyph, there will be no place for a kashida
356 if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
357 {
358 // all kashida positions are invalid
359 // do regular blank justification
360 pCurrent->FinishSpaceAdd();
362 CalcNewBlock( pCurrent, pStopAt, nReal, true );
363 return;
364 }
365 }
366
367 if( nGluePortion )
368 {
369 tools::Long nSpaceAdd = nGluePortionWidth / sal_Int32(nGluePortion);
370
371 // i60594
372 if( rSI.CountKashida() && !bSkipKashida )
373 {
374 if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
375 {
376 // no kashidas left
377 // do regular blank justification
378 pCurrent->FinishSpaceAdd();
380 CalcNewBlock( pCurrent, pStopAt, nReal, true );
381 return;
382 }
383 }
384
385 pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
386 pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
387 }
388 else if (IsOneBlock() && nCharCnt > TextFrameIndex(1))
389 {
390 const tools::Long nSpaceAdd = - nGluePortionWidth / (sal_Int32(nCharCnt) - 1);
391 pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
392 pPos->Width( static_cast<SwGluePortion*>(pPos)->GetFixWidth() );
393 }
394
395 nSpaceIdx++;
396 nGluePortion = TextFrameIndex(0);
397 nCharCnt = TextFrameIndex(0);
398 }
399 else
400 ++nGluePortion;
401 }
402 GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
403 if ( pPos == pStopAt )
404 {
405 pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
406 break;
407 }
408 pPos = pPos->GetNextPortion();
409 }
410}
411
413{
414 OSL_ENSURE( pCurrent->Height(), "SwTextAdjuster::CalcBlockAdjust: missing CalcLine()" );
415 OSL_ENSURE( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
416
417 pCurrent->SetKanaComp( std::make_unique<std::deque<sal_uInt16>>() );
418
419 const sal_uInt16 nNull = 0;
420 size_t nKanaIdx = 0;
421 tools::Long nKanaDiffSum = 0;
422 SwTwips nRepaintOfst = 0;
423 SwTwips nX = 0;
424 bool bNoCompression = false;
425
426 // Do not forget: CalcRightMargin() sets pCurrent->Width() to the line width!
427 CalcRightMargin( pCurrent );
428
429 SwLinePortion* pPos = pCurrent->GetNextPortion();
430
431 while( pPos )
432 {
433 if ( pPos->InTextGrp() )
434 {
435 // get maximum portion width from info structure, calculated
436 // during text formatting
437 sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
438
439 // check, if information is stored under other key
440 if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
441 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
442
443 // calculate difference between portion width and max. width
444 nKanaDiffSum += nMaxWidthDiff;
445
446 // we store the beginning of the first compressible portion
447 // for repaint
448 if ( nMaxWidthDiff && !nRepaintOfst )
449 nRepaintOfst = nX + GetLeftMargin();
450 }
451 else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
452 {
453 if ( nKanaIdx == pCurrent->GetKanaComp().size() )
454 pCurrent->GetKanaComp().push_back( nNull );
455
456 tools::Long nRest;
457
458 if ( pPos->InTabGrp() )
459 {
460 nRest = ! bNoCompression &&
461 ( pPos->Width() > MIN_TAB_WIDTH ) ?
462 pPos->Width() - MIN_TAB_WIDTH :
463 0;
464
465 // for simplifying the handling of left, right ... tabs,
466 // we do expand portions, which are lying behind
467 // those special tabs
468 bNoCompression = !pPos->IsTabLeftPortion();
469 }
470 else
471 {
472 nRest = ! bNoCompression ?
473 static_cast<SwGluePortion*>(pPos)->GetPrtGlue() :
474 0;
475
476 bNoCompression = false;
477 }
478
479 if( nKanaDiffSum )
480 {
481 sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum;
482
483 if ( nCompress >= 10000 )
484 // kanas can be expanded to 100%, and there is still
485 // some space remaining
486 nCompress = 0;
487
488 else
489 nCompress = 10000 - nCompress;
490
491 ( pCurrent->GetKanaComp() )[ nKanaIdx ] = o3tl::narrowing<sal_uInt16>(nCompress);
492 nKanaDiffSum = 0;
493 }
494
495 nKanaIdx++;
496 }
497
498 nX += pPos->Width();
499 pPos = pPos->GetNextPortion();
500 }
501
502 // set portion width
503 nKanaIdx = 0;
504 sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
505 pPos = pCurrent->GetNextPortion();
506 tools::Long nDecompress = 0;
507
508 while( pPos )
509 {
510 if ( pPos->InTextGrp() )
511 {
512 const SwTwips nMinWidth = pPos->Width();
513
514 // get maximum portion width from info structure, calculated
515 // during text formatting
516 SwTwips nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pPos );
517
518 // check, if information is stored under other key
519 if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
520 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( pCurrent );
521 pPos->Width( nMinWidth +
522 ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 );
523 nDecompress += pPos->Width() - nMinWidth;
524 }
525 else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
526 {
527 pPos->Width(pPos->Width() - nDecompress);
528
529 if ( pPos->InTabGrp() )
530 // set fix width to width
531 static_cast<SwTabPortion*>(pPos)->SetFixWidth( pPos->Width() );
532
533 if ( ++nKanaIdx < pCurrent->GetKanaComp().size() )
534 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
535
536 nDecompress = 0;
537 }
538 pPos = pPos->GetNextPortion();
539 }
540
541 return nRepaintOfst;
542}
543
545 SwTwips nReal )
546{
547 tools::Long nRealWidth;
548 const sal_uInt16 nRealHeight = GetLineHeight();
549 const sal_uInt16 nLineHeight = pCurrent->Height();
550
551 sal_uInt16 nPrtWidth = pCurrent->PrtWidth();
552 SwLinePortion *pLast = pCurrent->FindLastPortion();
553
554 if( GetInfo().IsMulti() )
555 nRealWidth = nReal;
556 else
557 {
558 nRealWidth = GetLineWidth();
559 // For each FlyFrame extending into the right margin, we create a FlyPortion.
560 const tools::Long nLeftMar = GetLeftMargin();
561 SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
562 nRealWidth - nPrtWidth, nLineHeight );
563
564 SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
565 while( pFly && tools::Long( nPrtWidth )< nRealWidth )
566 {
567 pLast->Append( pFly );
568 pLast = pFly;
569 if( pFly->GetFix() > nPrtWidth )
570 pFly->Width( ( pFly->GetFix() - nPrtWidth) + pFly->Width() + 1);
571 nPrtWidth += pFly->Width() + 1;
572 aCurrRect.Left( nLeftMar + nPrtWidth );
573 pFly = CalcFlyPortion( nRealWidth, aCurrRect );
574 }
575 delete pFly;
576 }
577
578 SwMarginPortion *pRight = new SwMarginPortion;
579 pLast->Append( pRight );
580
581 if( tools::Long( nPrtWidth )< nRealWidth )
582 pRight->PrtWidth( sal_uInt16( nRealWidth - nPrtWidth ) );
583
584 // pCurrent->Width() is set to the real size, because we attach the
585 // MarginPortions.
586 // This trick gives miraculous results:
587 // If pCurrent->Width() == nRealWidth, then the adjustment gets overruled
588 // implicitly. GetLeftMarginAdjust() and IsJustified() think they have a
589 // line filled with chars.
590
591 pCurrent->PrtWidth( sal_uInt16( nRealWidth ) );
592 return pRight;
593}
594
596{
597 // 1) We insert a left margin:
598 SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
599 SwGluePortion *pGlue = pLeft; // the last GluePortion
600
601 // 2) We attach a right margin:
602 // CalcRightMargin also calculates a possible overlap with FlyFrames.
603 CalcRightMargin( pCurrent );
604
605 SwLinePortion *pPos = pLeft->GetNextPortion();
606 TextFrameIndex nLen(0);
607
608 // If we only have one line, the text portion is consecutive and we center, then ...
609 bool bComplete = TextFrameIndex(0) == m_nStart;
611 bool bMultiTab = false;
612
613 while( pPos )
614 {
615 if ( pPos->IsMultiPortion() && static_cast<SwMultiPortion*>(pPos)->HasTabulator() )
616 bMultiTab = true;
617 else if( pPos->InFixMargGrp() &&
618 ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
619 {
620 // in tab compat mode we do not want to change tab portions
621 // in non tab compat mode we do not want to change margins if we
622 // found a multi portion with tabs
623 if( SvxAdjust::Right == GetAdjust() )
624 static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
625 else
626 {
627 // We set the first text portion to right-aligned and the last one
628 // to left-aligned.
629 // The first text portion gets the whole Glue, but only if we have
630 // more than one line.
631 if (bComplete && TextFrameIndex(GetInfo().GetText().getLength()) == nLen)
632 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
633 else
634 {
635 if ( ! bTabCompat )
636 {
637 if( pLeft == pGlue )
638 {
639 // If we only have a left and right margin, the
640 // margins share the Glue.
641 if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
642 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
643 else
644 static_cast<SwGluePortion*>(pPos)->MoveAllGlue( pGlue );
645 }
646 else
647 {
648 // The last text portion retains its Glue.
649 if( !pPos->IsMarginPortion() )
650 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
651 }
652 }
653 else
654 static_cast<SwGluePortion*>(pPos)->MoveHalfGlue( pGlue );
655 }
656 }
657
658 pGlue = static_cast<SwGluePortion*>(pPos);
659 bComplete = false;
660 }
661 nLen = nLen + pPos->GetLen();
662 pPos = pPos->GetNextPortion();
663 }
664
665 if( ! bTabCompat && ! bMultiTab && SvxAdjust::Right == GetAdjust() )
666 // portions are moved to the right if possible
667 pLeft->AdjustRight( pCurrent );
668}
669
671{
672 OSL_ENSURE( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
673
674 pCurrent->SetFormatAdj(false);
675
677
678 switch( GetAdjust() )
679 {
680 case SvxAdjust::Right:
681 case SvxAdjust::Center:
682 {
683 CalcFlyAdjust( pCurrent );
684 pPara->GetRepaint().SetOffset( 0 );
685 break;
686 }
687 case SvxAdjust::Block:
688 {
689 FormatBlock();
690 break;
691 }
692 default : return;
693 }
694}
695
696// This is a quite complicated calculation: nCurrWidth is the width _before_
697// adding the word, that still fits onto the line! For this reason the FlyPortion's
698// width is still correct if we get a deadlock-situation of:
699// bFirstWord && !WORDFITS
701 const SwRect &rCurrRect )
702{
703 SwTextFly aTextFly( GetTextFrame() );
704
705 const sal_uInt16 nCurrWidth = m_pCurr->PrtWidth();
706 SwFlyPortion *pFlyPortion = nullptr;
707
708 SwRect aLineVert( rCurrRect );
709 if ( GetTextFrame()->IsRightToLeft() )
710 GetTextFrame()->SwitchLTRtoRTL( aLineVert );
711 if ( GetTextFrame()->IsVertical() )
713
714 // aFlyRect is document-global!
715 SwRect aFlyRect( aTextFly.GetFrame( aLineVert ) );
716
717 if ( GetTextFrame()->IsRightToLeft() )
718 GetTextFrame()->SwitchRTLtoLTR( aFlyRect );
719 if ( GetTextFrame()->IsVertical() )
721
722 // If a Frame overlapps we open a Portion
723 if( aFlyRect.HasArea() )
724 {
725 // aLocal is frame-local
726 SwRect aLocal( aFlyRect );
727 aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
728 if( nCurrWidth > aLocal.Left() )
729 aLocal.Left( nCurrWidth );
730
731 // If the rect is wider than the line, we adjust it to the right size
732 const tools::Long nLocalWidth = aLocal.Left() + aLocal.Width();
733 if( nRealWidth < nLocalWidth )
734 aLocal.Width( nRealWidth - aLocal.Left() );
736 pFlyPortion = new SwFlyPortion( aLocal );
737 pFlyPortion->Height( sal_uInt16( rCurrRect.Height() ) );
738 // The Width could be smaller than the FixWidth, thus:
739 pFlyPortion->AdjFixWidth();
740 }
741 return pFlyPortion;
742}
743
744// CalcDropAdjust is called at the end by Format() if needed
746{
747 OSL_ENSURE( 1<GetDropLines() && SvxAdjust::Left!=GetAdjust() && SvxAdjust::Block!=GetAdjust(),
748 "CalcDropAdjust: No reason for DropAdjustment." );
749
750 const sal_Int32 nLineNumber = GetLineNr();
751
752 // 1) Skip dummies
753 Top();
754
755 if( !m_pCurr->IsDummy() || NextLine() )
756 {
757 // Adjust first
758 GetAdjusted();
759
761
762 // 2) Make sure we include the ropPortion
763 // 3) pLeft is the GluePor preceding the DropPor
764 if( pPor->InGlueGrp() && pPor->GetNextPortion()
765 && pPor->GetNextPortion()->IsDropPortion() )
766 {
767 const SwLinePortion *pDropPor = pPor->GetNextPortion();
768 SwGluePortion *pLeft = static_cast<SwGluePortion*>( pPor );
769
770 // 4) pRight: Find the GluePor coming after the DropPor
771 pPor = pPor->GetNextPortion();
772 while( pPor && !pPor->InFixMargGrp() )
773 pPor = pPor->GetNextPortion();
774
775 SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
776 static_cast<SwGluePortion*>(pPor) : nullptr;
777 if( pRight && pRight != pLeft )
778 {
779 // 5) Calculate nMinLeft. Who is the most to left?
780 const auto nDropLineStart =
781 GetLineStart() + pLeft->Width() + pDropPor->Width();
782 auto nMinLeft = nDropLineStart;
783 for( sal_Int32 i = 1; i < GetDropLines(); ++i )
784 {
785 if( NextLine() )
786 {
787 // Adjust first
788 GetAdjusted();
789
790 pPor = m_pCurr->GetFirstPortion();
791 const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
792 static_cast<SwMarginPortion*>(pPor) : nullptr;
793 if( !pMar )
794 nMinLeft = 0;
795 else
796 {
797 const auto nLineStart =
798 GetLineStart() + pMar->Width();
799 if( nMinLeft > nLineStart )
800 nMinLeft = nLineStart;
801 }
802 }
803 }
804
805 // 6) Distribute the Glue anew between pLeft and pRight
806 if( nMinLeft < nDropLineStart )
807 {
808 // The Glue is always passed from pLeft to pRight, so that
809 // the text moves to the left.
810 const auto nGlue = nDropLineStart - nMinLeft;
811 if( !nMinLeft )
812 pLeft->MoveAllGlue( pRight );
813 else
814 pLeft->MoveGlue( pRight, nGlue );
815 }
816 }
817 }
818 }
819
820 if( nLineNumber != GetLineNr() )
821 {
822 Top();
823 while( nLineNumber != GetLineNr() && Next() )
824 ;
825 }
826}
827
829{
830 Top();
831 SwRepaint &rRepaint = GetInfo().GetParaPortion()->GetRepaint();
832 if( rRepaint.Top() > Y() )
833 rRepaint.Top( Y() );
834 for( sal_Int32 i = 1; i < GetDropLines(); ++i )
835 NextLine();
836 const SwTwips nBottom = Y() + GetLineHeight() - 1;
837 if( rRepaint.Bottom() < nBottom )
838 rRepaint.Bottom( nBottom );
839}
840
841/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
@ DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK
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...
virtual bool get(DocumentSettingId id) const =0
Return the specified document setting.
sal_Int32 ValidateKashidas(const OUString &rTxt, sal_Int32 nIdx, sal_Int32 nLen, sal_Int32 nKashCount, const sal_Int32 *pKashidaPos, sal_Int32 *pKashidaPosDropped) const
vcl::text::ComplexTextLayoutFlags GetLayoutMode() const
tools::Long GetMinKashida() const
void SetLayoutMode(vcl::text::ComplexTextLayoutFlags nTextLayoutMode)
bool SeekAndChgAttrIter(TextFrameIndex nPos, OutputDevice *pOut)
Executes ChgPhysFnt if Seek() returns true and change font to merge character border with neighbours.
Definition: itratr.cxx:155
TextFrameIndex GetNextAttr() const
Definition: itratr.cxx:734
IDocumentSettingAccess const & getIDocumentSettingAccess() const
Definition: doc.cxx:190
sal_uInt16 GetFix() const
Definition: porglue.hxx:61
A glue portion is either a base class for other portions that want to have a certain width to push te...
Definition: porglue.hxx:31
void MoveHalfGlue(SwGluePortion *pTarget)
Definition: porglue.hxx:88
void MoveAllGlue(SwGluePortion *pTarget)
Definition: porglue.hxx:83
sal_uInt16 GetFixWidth() const
Definition: porglue.hxx:40
void MoveGlue(SwGluePortion *pTarget, const tools::Long nPrtGlue)
Definition: porglue.cxx:109
void AdjFixWidth()
Definition: porglue.hxx:77
Collection of SwLinePortion instances, representing one line of text.
Definition: porlay.hxx:79
void SetKanaComp(std::unique_ptr< std::deque< sal_uInt16 > > pNew)
Definition: porlay.hxx:200
std::deque< sal_uInt16 > & GetKanaComp()
Definition: porlay.hxx:203
void SetFormatAdj(const bool bNew)
Definition: porlay.hxx:120
sal_uInt16 GetLLSpaceAddCount() const
Definition: porlay.hxx:187
void InitSpaceAdd()
Definition: porlay.cxx:336
void FinishSpaceAdd()
Definition: porlay.hxx:186
SwLineLayout * GetNext()
Definition: porlay.hxx:159
SwLinePortion * GetFirstPortion() const
Definition: porlay.cxx:841
void SetLLSpaceAdd(tools::Long nNew, sal_uInt16 nIdx)
Definition: porlay.hxx:188
bool IsFormatAdj() const
Definition: porlay.hxx:121
virtual void Height(const SwTwips nNew, const bool bText=true) override
Definition: porlay.cxx:231
bool IsDummy() const
Definition: porlay.hxx:151
SwMarginPortion * CalcLeftMargin()
Definition: porlay.cxx:297
std::deque< sal_uInt16 > * GetpKanaComp() const
Definition: porlay.hxx:202
Base class for anything that can be part of a line in the Writer layout.
Definition: porlin.hxx:52
void PrtWidth(SwTwips nNewWidth)
Definition: porlin.hxx:83
bool IsMarginPortion() const
Definition: porlin.hxx:133
bool InTextGrp() const
Definition: porlin.hxx:105
SwLinePortion * GetNextPortion() const
Definition: porlin.hxx:75
PortionType GetWhichPor() const
Definition: porlin.hxx:102
virtual SwLinePortion * Append(SwLinePortion *pPortion)
Definition: porlin.cxx:190
TextFrameIndex GetLen() const
Definition: porlin.hxx:77
bool InGlueGrp() const
Definition: porlin.hxx:106
SwLinePortion * FindLastPortion()
Definition: porlin.cxx:179
bool IsDropPortion() const
Definition: porlin.hxx:130
bool IsTabLeftPortion() const
Definition: porlin.hxx:124
bool InTabGrp() const
Definition: porlin.hxx:107
bool IsBreakPortion() const
Definition: porlin.hxx:121
bool IsMultiPortion() const
Definition: porlin.hxx:143
bool IsFlyPortion() const
Definition: porlin.hxx:134
bool InFixMargGrp() const
Definition: porlin.hxx:115
void AdjustRight(const SwLineLayout *pCurr)
In the outer loop all portions are inspected - the GluePortions at the end are processed first.
Definition: porglue.cxx:188
bool IsDouble() const
Definition: pormulti.hxx:130
bool IsBidi() const
Definition: pormulti.hxx:132
bool HasTabulator() const
Definition: pormulti.hxx:123
Collection of SwLineLayout instances, represents the paragraph text in Writer layout.
Definition: porlay.hxx:251
void SetFly()
Definition: porlay.hxx:301
SwRepaint & GetRepaint()
Definition: porlay.hxx:285
SwScriptInfo & GetScriptInfo()
Definition: porlay.hxx:291
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
bool HasArea() const
Definition: swrect.hxx:300
void Top(const tools::Long nTop)
Definition: swrect.hxx:206
void Bottom(const tools::Long nBottom)
Definition: swrect.hxx:211
void Pos(const Point &rNew)
Definition: swrect.hxx:171
void Left(const tools::Long nLeft)
Definition: swrect.hxx:197
void Width(tools::Long nNew)
Definition: swrect.hxx:189
void SetOffset(const SwTwips nNew)
Definition: porlay.hxx:71
void ClearNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen)
Clear forced blank justification for a given line.
Definition: porlay.cxx:2529
void MarkKashidasInvalid(sal_Int32 nCnt, const TextFrameIndex *pKashidaPositions)
Marks nCnt kashida positions as invalid pKashidaPositions: array of char indices relative to the para...
Definition: porlay.cxx:2545
sal_Int32 KashidaJustify(KernArray *pKernArray, sal_Bool *pKashidaArray, TextFrameIndex nStt, TextFrameIndex nLen, tools::Long nSpaceAdd=0) const
Performs a kashida justification on the kerning array.
Definition: porlay.cxx:2307
void ClearKashidaInvalid(size_t nKashPos)
Definition: porlay.cxx:2437
void GetKashidaPositions(TextFrameIndex nStt, TextFrameIndex nLen, std::vector< TextFrameIndex > &rKashidaPosition)
retrieves kashida opportunities for a given text range.
Definition: porlay.cxx:2488
size_t CountKashida() const
Definition: scriptinfo.hxx:152
static bool IsArabicText(const OUString &rText, TextFrameIndex nStt, TextFrameIndex nLen)
Checks if text is Arabic text.
Definition: porlay.cxx:2395
void SetNoKashidaLine(TextFrameIndex nStt, TextFrameIndex nLen)
Use regular blank justification instead of kashdida justification for the given line of text.
Definition: porlay.cxx:2512
TextFrameIndex NextScriptChg(TextFrameIndex nPos) const
Definition: porlay.cxx:1858
void GetAdjusted() const
Definition: itrtxt.hxx:250
void CalcDropRepaint()
Definition: itradj.cxx:828
SwMarginPortion * CalcRightMargin(SwLineLayout *pCurr, SwTwips nReal=0)
Definition: itradj.cxx:544
void CalcAdjLine(SwLineLayout *pCurr)
Definition: itradj.cxx:670
void CalcDropAdjust()
Definition: itradj.cxx:745
SwFlyPortion * CalcFlyPortion(const tools::Long nRealWidth, const SwRect &rCurrRect)
Definition: itradj.cxx:700
void CalcFlyAdjust(SwLineLayout *pCurr)
Definition: itradj.cxx:595
void CalcNewBlock(SwLineLayout *pCurr, const SwLinePortion *pStopAt, SwTwips nReal=0, bool bSkipKashida=false)
Definition: itradj.cxx:250
SwTwips CalcKanaAdj(SwLineLayout *pCurr)
Definition: itradj.cxx:412
void FormatBlock()
Definition: itradj.cxx:39
The purpose of this class is to be the universal interface between formatting/text output and the pos...
Definition: txtfly.hxx:125
SwRect GetFrame(const SwRect &rPortion) const
Definition: txtfly.hxx:369
SwDoc & GetDoc()
Definition: txtfrm.hxx:475
void SwitchVerticalToHorizontal(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from vertical to horizontal layout.
Definition: txtfrm.cxx:580
void SwitchLTRtoRTL(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from LTR to RTL layout.
Definition: txtfrm.cxx:683
void SwitchRTLtoLTR(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from RTL to LTR layout.
Definition: txtfrm.hxx:753
void SwitchHorizontalToVertical(SwRect &rRect) const
Calculates the coordinates of a rectangle when switching from horizontal to vertical layout.
Definition: txtfrm.cxx:473
SwParaPortion * GetParaPortion()
Definition: inftxt.hxx:121
const SwLineLayout * NextLine()
Definition: itrtxt.cxx:125
TextFrameIndex GetLength() const
Definition: itrtxt.hxx:86
TextFrameIndex GetEnd() const
Definition: itrtxt.hxx:89
SwTwips Y() const
Definition: itrtxt.hxx:90
const SwLineLayout * GetNext() const
Definition: itrtxt.hxx:84
TextFrameIndex GetStart() const
Definition: itrtxt.hxx:88
SwLineLayout * m_pCurr
Definition: itrtxt.hxx:36
void Top()
Definition: itrtxt.hxx:99
SwTwips GetLineHeight() const
Definition: itrtxt.hxx:116
const SwLineLayout * Next()
Definition: itrtxt.cxx:108
const SwLineLayout * GetCurr() const
Definition: itrtxt.hxx:83
sal_Int32 GetLineNr() const
Definition: itrtxt.hxx:87
SwTextFrame * GetTextFrame()
Definition: itrtxt.hxx:134
TextFrameIndex m_nStart
Definition: itrtxt.hxx:41
sal_uInt16 GetDropLines() const
Definition: itrtxt.hxx:202
SwTextSizeInfo & GetInfo()
Definition: itrtxt.hxx:216
bool IsOneBlock() const
Definition: itrtxt.hxx:187
bool IsLastCenter() const
Definition: itrtxt.hxx:189
bool IsLastBlock() const
Definition: itrtxt.hxx:188
SwTwips GetLeftMargin() const
Definition: itrtxt.hxx:329
SvxAdjust GetAdjust() const
Definition: itrtxt.hxx:190
SwTwips GetLineStart() const
Definition: itrcrsr.cxx:379
sal_uInt16 GetLineWidth() const
Definition: itrtxt.hxx:191
This portion represents a part of the paragraph string.
Definition: portxt.hxx:27
vcl::RenderContext * GetOut()
Definition: inftxt.hxx:225
void SetIdx(const TextFrameIndex nNew)
Definition: inftxt.hxx:274
sal_uInt16 GetMaxWidthDiff(const SwLinePortion *nKey)
Definition: inftxt.hxx:302
const OUString & GetText() const
Definition: inftxt.hxx:240
TextFrameIndex GetIdx() const
Definition: inftxt.hxx:273
static bool lcl_CheckKashidaPositions(SwScriptInfo &rSI, SwTextSizeInfo &rInf, SwTextIter &rItr, sal_Int32 &rKashidas, TextFrameIndex &nGluePortion)
Definition: itradj.cxx:113
#define MIN_TAB_WIDTH
Definition: itradj.cxx:35
static bool lcl_CheckKashidaWidth(SwScriptInfo &rSI, SwTextSizeInfo &rInf, SwTextIter &rItr, sal_Int32 &rKashidas, TextFrameIndex &nGluePortion, const tools::Long nGluePortionWidth, tools::Long &nSpaceAdd)
Definition: itradj.cxx:191
double getLength(const B2DPolygon &rCandidate)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
long Long
ComplexTextLayoutFlags
#define SPACING_PRECISION_FACTOR
Definition: scriptinfo.hxx:41
sal_uIntPtr sal_uLong
tools::Long SwTwips
Definition: swtypes.hxx:51
constexpr sal_Int32 COMPLETE_STRING
Definition: swtypes.hxx:57