xref: /trunk/main/sw/source/core/text/itradj.cxx (revision efeef26f)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_
27 #include <com/sun/star/i18n/ScriptType.hdl>
28 #endif
29 #include <vcl/outdev.hxx>
30 #include <IDocumentSettingAccess.hxx>
31 
32 #include "frame.hxx"       // CalcFlyAdjust()
33 #include "paratr.hxx"
34 #include "txtcfg.hxx"
35 #include "itrtxt.hxx"
36 #include "porglue.hxx"
37 #include "porlay.hxx"
38 #include "porfly.hxx"       // CalcFlyAdjust()
39 #include "pordrop.hxx"       // CalcFlyAdjust()
40 #include "pormulti.hxx"
41 #include <portab.hxx>
42 
43 #define MIN_TAB_WIDTH 60
44 
45 using namespace ::com::sun::star;
46 
47 /*************************************************************************
48  *                    SwTxtAdjuster::FormatBlock()
49  *************************************************************************/
50 
FormatBlock()51 void SwTxtAdjuster::FormatBlock( )
52 {
53 	// In der letzten Zeile gibt's keinen Blocksatz.
54 	// Und bei Tabulatoren aus Tradition auch nicht.
55 	// 7701: wenn Flys im Spiel sind, geht's weiter
56 
57 	const SwLinePortion *pFly = 0;
58 
59 	sal_Bool bSkip = !IsLastBlock() &&
60 		nStart + pCurr->GetLen() >= GetInfo().GetTxt().Len();
61 
62 	// ????: mehrzeilige Felder sind fies: wir muessen kontrollieren,
63 	// ob es noch andere Textportions im Absatz gibt.
64 	if( bSkip )
65 	{
66 		const SwLineLayout *pLay = pCurr->GetNext();
67 		while( pLay && !pLay->GetLen() )
68 		{
69 			const SwLinePortion *pPor = pCurr->GetFirstPortion();
70 			while( pPor && bSkip )
71 			{
72 				if( pPor->InTxtGrp() )
73 					bSkip = sal_False;
74 				pPor = pPor->GetPortion();
75 			}
76 			pLay = bSkip ? pLay->GetNext() : 0;
77 		}
78 	}
79 
80 	if( bSkip )
81 	{
82 		if( !GetInfo().GetParaPortion()->HasFly() )
83 		{
84 			if( IsLastCenter() )
85 				CalcFlyAdjust( pCurr );
86 			pCurr->FinishSpaceAdd();
87 			return;
88 		}
89 		else
90 		{
91 			const SwLinePortion *pTmpFly = NULL;
92 
93 			// 7701: beim letzten Fly soll Schluss sein
94 			const SwLinePortion *pPos = pCurr->GetFirstPortion();
95 			while( pPos )
96 			{
97 				// Ich suche jetzt den letzten Fly, hinter dem noch Text ist:
98 				if( pPos->IsFlyPortion() )
99 					pTmpFly = pPos; // Ein Fly wurde gefunden
100 				else if ( pTmpFly && pPos->InTxtGrp() )
101 				{
102 					pFly = pTmpFly; // Ein Fly mit nachfolgendem Text!
103 					pTmpFly = NULL;
104 				}
105 				pPos = pPos->GetPortion();
106 			}
107 			// 8494: Wenn keiner gefunden wurde, ist sofort Schluss!
108 			if( !pFly )
109 			{
110 				if( IsLastCenter() )
111 					CalcFlyAdjust( pCurr );
112 				pCurr->FinishSpaceAdd();
113 				return;
114 			}
115 		}
116 	}
117 
118 	const xub_StrLen nOldIdx = GetInfo().GetIdx();
119 	GetInfo().SetIdx( nStart );
120 	CalcNewBlock( pCurr, pFly );
121 	GetInfo().SetIdx( nOldIdx );
122 	GetInfo().GetParaPortion()->GetRepaint()->SetOfst(0);
123 }
124 
125 /*************************************************************************
126  *                    lcl_CheckKashidaPositions()
127  *************************************************************************/
lcl_CheckKashidaPositions(SwScriptInfo & rSI,SwTxtSizeInfo & rInf,SwTxtIter & rItr,xub_StrLen & nKashidas,xub_StrLen & nGluePortion)128 bool lcl_CheckKashidaPositions( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr,
129                                 xub_StrLen& nKashidas, xub_StrLen& nGluePortion )
130 {
131     // i60594 validate Kashida justification
132     xub_StrLen nIdx = rItr.GetStart();
133     xub_StrLen nEnd = rItr.GetEnd();
134 
135     // Note on calling KashidaJustify():
136     // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
137     // total number of kashida positions, or the number of kashida positions after some positions
138     // have been dropped.
139     // Here we want the clean total, which is OK: We have called ClearKashidaInvalid() before.
140     nKashidas = rSI.KashidaJustify ( 0, 0, rItr.GetStart(), rItr.GetLength(), 0 );
141 
142     if (!nKashidas) // nothing to do
143         return true;
144 
145     // kashida positions found in SwScriptInfo are not necessarily valid in every font
146     // if two characters are replaced by a ligature glyph, there will be no place for a kashida
147     xub_StrLen* pKashidaPos = new xub_StrLen [ nKashidas ];
148     xub_StrLen* pKashidaPosDropped = new xub_StrLen [ nKashidas ];
149     rSI.GetKashidaPositions ( nIdx, rItr.GetLength(), pKashidaPos );
150     xub_StrLen nKashidaIdx = 0;
151     while ( nKashidas && nIdx < nEnd )
152     {
153         rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
154         xub_StrLen nNext = rItr.GetNextAttr();
155 
156         // is there also a script change before?
157         // if there is, nNext should point to the script change
158         xub_StrLen nNextScript = rSI.NextScriptChg( nIdx );
159         if( nNextScript < nNext )
160             nNext = nNextScript;
161 
162         if ( nNext == STRING_LEN || nNext > nEnd )
163             nNext = nEnd;
164         xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
165         if ( nKashidasInAttr )
166         {
167             xub_StrLen nKashidasDropped = 0;
168             if ( !SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
169             {
170                 nKashidasDropped = nKashidasInAttr;
171                 nKashidas -= nKashidasDropped;
172             }
173             else
174             {
175                 sal_uLong nOldLayout = rInf.GetOut()->GetLayoutMode();
176                 rInf.GetOut()->SetLayoutMode ( nOldLayout | TEXT_LAYOUT_BIDI_RTL );
177                 nKashidasDropped = rInf.GetOut()->ValidateKashidas ( rInf.GetTxt(), nIdx, nNext - nIdx,
178                                                nKashidasInAttr, pKashidaPos + nKashidaIdx,
179                                                pKashidaPosDropped );
180                 rInf.GetOut()->SetLayoutMode ( nOldLayout );
181                 if ( nKashidasDropped )
182                 {
183                     rSI.MarkKashidasInvalid ( nKashidasDropped, pKashidaPosDropped );
184                     nKashidas -= nKashidasDropped;
185                     nGluePortion -= nKashidasDropped;
186                 }
187             }
188             nKashidaIdx += nKashidasInAttr;
189         }
190         nIdx = nNext;
191     }
192     delete[] pKashidaPos;
193     delete[] pKashidaPosDropped;
194 
195     // return false if all kashidas have been eliminated
196     return (nKashidas > 0);
197 }
198 
199 /*************************************************************************
200  *                    lcl_CheckKashidaWidth()
201  *************************************************************************/
lcl_CheckKashidaWidth(SwScriptInfo & rSI,SwTxtSizeInfo & rInf,SwTxtIter & rItr,xub_StrLen & nKashidas,xub_StrLen & nGluePortion,const long nGluePortionWidth,long & nSpaceAdd)202 bool lcl_CheckKashidaWidth ( SwScriptInfo& rSI, SwTxtSizeInfo& rInf, SwTxtIter& rItr, xub_StrLen& nKashidas,
203                              xub_StrLen& nGluePortion, const long nGluePortionWidth, long& nSpaceAdd )
204 {
205     // check kashida width
206     // if width is smaller than minimal kashida width allowed by fonts in the current line
207     // drop one kashida after the other until kashida width is OK
208     bool bAddSpaceChanged;
209     while ( nKashidas )
210     {
211         bAddSpaceChanged = false;
212         xub_StrLen nIdx = rItr.GetStart();
213         xub_StrLen nEnd = rItr.GetEnd();
214         while ( nIdx < nEnd )
215         {
216             rItr.SeekAndChgAttrIter( nIdx, rInf.GetOut() );
217             xub_StrLen nNext = rItr.GetNextAttr();
218 
219             // is there also a script change before?
220             // if there is, nNext should point to the script change
221             xub_StrLen nNextScript = rSI.NextScriptChg( nIdx );
222             if( nNextScript < nNext )
223                nNext = nNextScript;
224 
225             if ( nNext == STRING_LEN || nNext > nEnd )
226                 nNext = nEnd;
227             xub_StrLen nKashidasInAttr = rSI.KashidaJustify ( 0, 0, nIdx, nNext - nIdx );
228 
229             long nFontMinKashida = rInf.GetOut()->GetMinKashida();
230             if ( nFontMinKashida && nKashidasInAttr && SwScriptInfo::IsArabicText( rInf.GetTxt(), nIdx, nNext - nIdx ) )
231             {
232                 xub_StrLen nKashidasDropped = 0;
233                 while ( nKashidas && nGluePortion && nKashidasInAttr &&
234                         nSpaceAdd / SPACING_PRECISION_FACTOR < nFontMinKashida )
235                 {
236                     --nGluePortion;
237                     --nKashidas;
238                     --nKashidasInAttr;
239                     ++nKashidasDropped;
240                     if( !nKashidas || !nGluePortion ) // nothing left, return false to
241                         return false;                 // do regular blank justification
242 
243                     nSpaceAdd = nGluePortionWidth / nGluePortion;
244                     bAddSpaceChanged = true;
245                }
246                if( nKashidasDropped )
247                    rSI.MarkKashidasInvalid( nKashidasDropped, nIdx, nNext - nIdx );
248             }
249             if ( bAddSpaceChanged )
250                 break; // start all over again
251             nIdx = nNext;
252         }
253         if ( !bAddSpaceChanged )
254             break; // everything was OK
255     }
256    return true;
257 }
258 
259 /*************************************************************************
260  *                    SwTxtAdjuster::CalcNewBlock()
261  *
262  * CalcNewBlock() darf erst nach CalcLine() gerufen werden !
263  * Aufgespannt wird immer zwischen zwei RandPortions oder FixPortions
264  * (Tabs und Flys). Dabei werden die Glues gezaehlt und ExpandBlock gerufen.
265  *************************************************************************/
266 
CalcNewBlock(SwLineLayout * pCurrent,const SwLinePortion * pStopAt,SwTwips nReal,bool bSkipKashida)267 void SwTxtAdjuster::CalcNewBlock( SwLineLayout *pCurrent,
268                                   const SwLinePortion *pStopAt, SwTwips nReal, bool bSkipKashida )
269 {
270 	ASSERT( GetInfo().IsMulti() || SVX_ADJUST_BLOCK == GetAdjust(),
271 			"CalcNewBlock: Why?" );
272     ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" );
273 
274     pCurrent->InitSpaceAdd();
275     xub_StrLen nGluePortion = 0;
276 	xub_StrLen nCharCnt = 0;
277 	MSHORT nSpaceIdx = 0;
278 
279     // i60591: hennerdrews
280     SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo();
281     SwTxtSizeInfo aInf ( GetTxtFrm() );
282     SwTxtIter aItr ( GetTxtFrm(), &aInf );
283 
284     if ( rSI.CountKashida() )
285     {
286         while (aItr.GetCurr() != pCurrent && aItr.GetNext())
287            aItr.Next();
288 
289         if( bSkipKashida )
290         {
291             rSI.SetNoKashidaLine ( aItr.GetStart(), aItr.GetLength());
292         }
293         else
294         {
295             rSI.ClearKashidaInvalid ( aItr.GetStart(), aItr.GetLength() );
296             rSI.ClearNoKashidaLine( aItr.GetStart(), aItr.GetLength() );
297         }
298     }
299 
300     // Nicht vergessen:
301     // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite !
302     if (!bSkipKashida)
303         CalcRightMargin( pCurrent, nReal );
304 
305     // --> FME 2005-06-08 #i49277#
306     const sal_Bool bDoNotJustifyLinesWithManualBreak =
307                 GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::DO_NOT_JUSTIFY_LINES_WITH_MANUAL_BREAK);
308     // <--
309 
310     SwLinePortion *pPos = pCurrent->GetPortion();
311 
312 	while( pPos )
313 	{
314         if ( bDoNotJustifyLinesWithManualBreak &&
315              pPos->IsBreakPortion() && !IsLastBlock() )
316         {
317            pCurrent->FinishSpaceAdd();
318            break;
319         }
320 
321         if ( pPos->InTxtGrp() )
322 			nGluePortion = nGluePortion + ((SwTxtPortion*)pPos)->GetSpaceCnt( GetInfo(), nCharCnt );
323 		else if( pPos->IsMultiPortion() )
324 		{
325 			SwMultiPortion* pMulti = (SwMultiPortion*)pPos;
326 			// a multiportion with a tabulator inside breaks the text adjustment
327 			// a ruby portion will not be stretched by text adjustment
328 			// a double line portion takes additional space for each blank
329 			// in the wider line
330 			if( pMulti->HasTabulator() )
331 			{
332                 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
333                     pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
334 
335                 nSpaceIdx++;
336 				nGluePortion = 0;
337 				nCharCnt = 0;
338 			}
339 			else if( pMulti->IsDouble() )
340 				nGluePortion = nGluePortion + ((SwDoubleLinePortion*)pMulti)->GetSpaceCnt();
341             else if ( pMulti->IsBidi() )
342                 nGluePortion = nGluePortion + ((SwBidiPortion*)pMulti)->GetSpaceCnt( GetInfo() );  // i60594
343         }
344 
345 		if( pPos->InGlueGrp() )
346 		{
347 			if( pPos->InFixMargGrp() )
348 			{
349                 if ( nSpaceIdx == pCurrent->GetLLSpaceAddCount() )
350                     pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
351 
352                 const long nGluePortionWidth = static_cast<SwGluePortion*>(pPos)->GetPrtGlue() *
353                                                SPACING_PRECISION_FACTOR;
354 
355                 xub_StrLen nKashidas = 0;
356                 if( nGluePortion && rSI.CountKashida() && !bSkipKashida )
357                 {
358                     // kashida positions found in SwScriptInfo are not necessarily valid in every font
359                     // if two characters are replaced by a ligature glyph, there will be no place for a kashida
360                     if ( !lcl_CheckKashidaPositions ( rSI, aInf, aItr, nKashidas, nGluePortion ))
361                     {
362                         // all kashida positions are invalid
363                         // do regular blank justification
364                         pCurrent->FinishSpaceAdd();
365                         GetInfo().SetIdx( nStart );
366                         CalcNewBlock( pCurrent, pStopAt, nReal, true );
367                         return;
368                     }
369                 }
370 
371 				if( nGluePortion )
372 				{
373                     long nSpaceAdd = nGluePortionWidth / nGluePortion;
374 
375                     // i60594
376                     if( rSI.CountKashida() && !bSkipKashida )
377                     {
378                         if( !lcl_CheckKashidaWidth( rSI, aInf, aItr, nKashidas, nGluePortion, nGluePortionWidth, nSpaceAdd ))
379                         {
380                             // no kashidas left
381                             // do regular blank justification
382                             pCurrent->FinishSpaceAdd();
383                             GetInfo().SetIdx( nStart );
384                             CalcNewBlock( pCurrent, pStopAt, nReal, true );
385                             return;
386                         }
387                     }
388 
389                     pCurrent->SetLLSpaceAdd( nSpaceAdd , nSpaceIdx );
390                     pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() );
391 				}
392 				else if ( IsOneBlock() && nCharCnt > 1 )
393 				{
394                     const long nSpaceAdd = - nGluePortionWidth / ( nCharCnt - 1 );
395                     pCurrent->SetLLSpaceAdd( nSpaceAdd, nSpaceIdx );
396                     pPos->Width( ( (SwGluePortion*)pPos )->GetFixWidth() );
397 				}
398 
399                 nSpaceIdx++;
400 				nGluePortion = 0;
401 				nCharCnt = 0;
402 			}
403 			else
404 				++nGluePortion;
405 		}
406 		GetInfo().SetIdx( GetInfo().GetIdx() + pPos->GetLen() );
407 		if ( pPos == pStopAt )
408 		{
409             pCurrent->SetLLSpaceAdd( 0, nSpaceIdx );
410             break;
411 		}
412 		pPos = pPos->GetPortion();
413 	}
414 }
415 
416 /*************************************************************************
417  *                    SwTxtAdjuster::CalcKanaAdj()
418  *************************************************************************/
419 
CalcKanaAdj(SwLineLayout * pCurrent)420 SwTwips SwTxtAdjuster::CalcKanaAdj( SwLineLayout* pCurrent )
421 {
422     ASSERT( pCurrent->Height(), "SwTxtAdjuster::CalcBlockAdjust: missing CalcLine()" );
423     ASSERT( !pCurrent->GetpKanaComp(), "pKanaComp already exists!!" );
424 
425     SvUShorts *pNewKana = new SvUShorts;
426     pCurrent->SetKanaComp( pNewKana );
427 
428     const sal_uInt16 nNull = 0;
429     MSHORT nKanaIdx = 0;
430     long nKanaDiffSum = 0;
431     SwTwips nRepaintOfst = 0;
432     SwTwips nX = 0;
433     sal_Bool bNoCompression = sal_False;
434 
435     // Nicht vergessen:
436     // CalcRightMargin() setzt pCurrent->Width() auf die Zeilenbreite !
437     CalcRightMargin( pCurrent, 0 );
438 
439     SwLinePortion* pPos = pCurrent->GetPortion();
440 
441     while( pPos )
442     {
443         if ( pPos->InTxtGrp() )
444         {
445             // get maximum portion width from info structure, calculated
446             // during text formatting
447             sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos );
448 
449             // check, if information is stored under other key
450             if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
451                 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pCurrent );
452 
453             // calculate difference between portion width and max. width
454             nKanaDiffSum += nMaxWidthDiff;
455 
456             // we store the beginning of the first compressable portion
457             // for repaint
458             if ( nMaxWidthDiff && !nRepaintOfst )
459                 nRepaintOfst = nX + GetLeftMargin();
460         }
461         else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
462         {
463             if ( nKanaIdx == pCurrent->GetKanaComp().Count() )
464                 pCurrent->GetKanaComp().Insert( nNull, nKanaIdx );
465 
466             sal_uInt16 nRest;
467 
468             if ( pPos->InTabGrp() )
469             {
470                 nRest = ! bNoCompression &&
471                         ( pPos->Width() > MIN_TAB_WIDTH ) ?
472                         pPos->Width() - MIN_TAB_WIDTH :
473                         0;
474 
475                 // for simplifying the handling of left, right ... tabs,
476                 // we do expand portions, which are lying behind
477                 // those special tabs
478                 bNoCompression = !pPos->IsTabLeftPortion();
479             }
480             else
481             {
482                 nRest = ! bNoCompression ?
483                         ((SwGluePortion*)pPos)->GetPrtGlue() :
484                         0;
485 
486                 bNoCompression = sal_False;
487             }
488 
489             if( nKanaDiffSum )
490             {
491                 sal_uLong nCompress = ( 10000 * nRest ) / nKanaDiffSum;
492 
493                 if ( nCompress >= 10000 )
494                     // kanas can be expanded to 100%, and there is still
495                     // some space remaining
496                     nCompress = 0;
497 
498                 else
499                     nCompress = 10000 - nCompress;
500 
501                 ( pCurrent->GetKanaComp() )[ nKanaIdx ] = (sal_uInt16)nCompress;
502                 nKanaDiffSum = 0;
503             }
504 
505             nKanaIdx++;
506         }
507 
508         nX += pPos->Width();
509         pPos = pPos->GetPortion();
510     }
511 
512     // set portion width
513     nKanaIdx = 0;
514     sal_uInt16 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
515     pPos = pCurrent->GetPortion();
516 	long nDecompress = 0;
517 	nKanaDiffSum = 0;
518 
519     while( pPos )
520     {
521         if ( pPos->InTxtGrp() )
522         {
523             const sal_uInt16 nMinWidth = pPos->Width();
524 
525             // get maximum portion width from info structure, calculated
526             // during text formatting
527             sal_uInt16 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pPos );
528 
529             // check, if information is stored under other key
530             if ( !nMaxWidthDiff && pPos == pCurrent->GetFirstPortion() )
531                 nMaxWidthDiff = GetInfo().GetMaxWidthDiff( (sal_uLong)pCurrent );
532             nKanaDiffSum += nMaxWidthDiff;
533             pPos->Width( nMinWidth +
534                        ( ( 10000 - nCompress ) * nMaxWidthDiff ) / 10000 );
535 			nDecompress += pPos->Width() - nMinWidth;
536         }
537         else if( pPos->InGlueGrp() && pPos->InFixMargGrp() )
538         {
539             if( nCompress )
540             {
541 				nKanaDiffSum *= nCompress;
542 				nKanaDiffSum /= 10000;
543             }
544 
545             pPos->Width( static_cast<sal_uInt16>(pPos->Width() - nDecompress) );
546 
547             if ( pPos->InTabGrp() )
548                 // set fix width to width
549                 ((SwTabPortion*)pPos)->SetFixWidth( pPos->Width() );
550 
551             const SvUShorts& rKanaComp = pCurrent->GetKanaComp();
552             if ( ++nKanaIdx < rKanaComp.Count() )
553                 nCompress = ( pCurrent->GetKanaComp() )[ nKanaIdx ];
554 
555 			nKanaDiffSum = 0;
556 			nDecompress = 0;
557         }
558         pPos = pPos->GetPortion();
559     }
560 
561     return nRepaintOfst;
562 }
563 
564 /*************************************************************************
565  *                    SwTxtAdjuster::CalcRightMargin()
566  *************************************************************************/
567 
CalcRightMargin(SwLineLayout * pCurrent,SwTwips nReal)568 SwMarginPortion *SwTxtAdjuster::CalcRightMargin( SwLineLayout *pCurrent,
569 	SwTwips nReal )
570 {
571 	long nRealWidth;
572     const sal_uInt16 nRealHeight = GetLineHeight();
573     const sal_uInt16 nLineHeight = pCurrent->Height();
574 
575     KSHORT nPrtWidth = pCurrent->PrtWidth();
576     SwLinePortion *pLast = pCurrent->FindLastPortion();
577 
578 	if( GetInfo().IsMulti() )
579 		nRealWidth = nReal;
580 	else
581 	{
582 		nRealWidth = GetLineWidth();
583 		// Fuer jeden FlyFrm, der in den rechten Rand hineinragt,
584 		// wird eine FlyPortion angelegt.
585 		const long nLeftMar = GetLeftMargin();
586         SwRect aCurrRect( nLeftMar + nPrtWidth, Y() + nRealHeight - nLineHeight,
587                           nRealWidth - nPrtWidth, nLineHeight );
588 
589 		SwFlyPortion *pFly = CalcFlyPortion( nRealWidth, aCurrRect );
590 		while( pFly && long( nPrtWidth )< nRealWidth )
591 		{
592 			pLast->Append( pFly );
593 			pLast = pFly;
594 			if( pFly->Fix() > nPrtWidth )
595 				pFly->Width( ( pFly->Fix() - nPrtWidth) + pFly->Width() + 1);
596 			nPrtWidth += pFly->Width() + 1;
597 			aCurrRect.Left( nLeftMar + nPrtWidth );
598 			pFly = CalcFlyPortion( nRealWidth, aCurrRect );
599 		}
600 		if( pFly )
601 			delete pFly;
602 	}
603 
604 	SwMarginPortion *pRight = new SwMarginPortion( 0 );
605 	pLast->Append( pRight );
606 
607 	if( long( nPrtWidth )< nRealWidth )
608 		pRight->PrtWidth( KSHORT( nRealWidth - nPrtWidth ) );
609 
610     // pCurrent->Width() wird auf die reale Groesse gesetzt,
611 	// da jetzt die MarginPortions eingehaengt sind.
612 	// Dieser Trick hat wundersame Auswirkungen.
613     // Wenn pCurrent->Width() == nRealWidth ist, dann wird das gesamte
614 	// Adjustment implizit ausgecontert. GetLeftMarginAdjust() und
615 	// IsBlocksatz() sind der Meinung, sie haetten eine mit Zeichen
616 	// gefuellte Zeile.
617 
618     pCurrent->PrtWidth( KSHORT( nRealWidth ) );
619 	return pRight;
620 }
621 
622 /*************************************************************************
623  *                    SwTxtAdjuster::CalcFlyAdjust()
624  *************************************************************************/
625 
CalcFlyAdjust(SwLineLayout * pCurrent)626 void SwTxtAdjuster::CalcFlyAdjust( SwLineLayout *pCurrent )
627 {
628 	// 1) Es wird ein linker Rand eingefuegt:
629     SwMarginPortion *pLeft = pCurrent->CalcLeftMargin();
630 	SwGluePortion *pGlue = pLeft;       // die letzte GluePortion
631 
632 
633 	// 2) Es wird ein rechter Rand angehaengt:
634 	// CalcRightMargin berechnet auch eventuelle Ueberlappungen mit
635 	// FlyFrms.
636     CalcRightMargin( pCurrent );
637 
638 	SwLinePortion *pPos = pLeft->GetPortion();
639 	xub_StrLen nLen = 0;
640 
641 	// Wenn wir nur eine Zeile vorliegen haben und die Textportion zusammen
642 	// haengend ist und wenn zentriert wird, dann ...
643 
644 	sal_Bool bComplete = 0 == nStart;
645     const sal_Bool bTabCompat = GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT);
646     sal_Bool bMultiTab = sal_False;
647 
648 	while( pPos )
649 	{
650         if ( pPos->IsMultiPortion() && ((SwMultiPortion*)pPos)->HasTabulator() )
651             bMultiTab = sal_True;
652         else if( pPos->InFixMargGrp() &&
653                ( bTabCompat ? ! pPos->InTabGrp() : ! bMultiTab ) )
654         {
655             // in tab compat mode we do not want to change tab portions
656             // in non tab compat mode we do not want to change margins if we
657             // found a multi portion with tabs
658             if( SVX_ADJUST_RIGHT == GetAdjust() )
659                 ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
660             else
661             {
662                 // Eine schlaue Idee von MA:
663                 // Fuer die erste Textportion wird rechtsbuendig eingestellt,
664                 // fuer die letzte linksbuendig.
665 
666                 // Die erste Textportion kriegt den ganzen Glue
667                 // Aber nur, wenn wir mehr als eine Zeile besitzen.
668                 if( bComplete && GetInfo().GetTxt().Len() == nLen )
669                     ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
670                 else
671                 {
672                     if ( ! bTabCompat )
673                     {
674                         if( pLeft == pGlue )
675                         {
676                             // Wenn es nur einen linken und rechten Rand gibt,
677                             // dann teilen sich die Raender den Glue.
678                             if( nLen + pPos->GetLen() >= pCurrent->GetLen() )
679                                 ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
680                             else
681                                 ((SwGluePortion*)pPos)->MoveAllGlue( pGlue );
682                         }
683                         else
684                         {
685                             // Die letzte Textportion behaelt sein Glue
686                          if( !pPos->IsMarginPortion() )
687                               ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
688                          }
689                      }
690                      else
691                         ((SwGluePortion*)pPos)->MoveHalfGlue( pGlue );
692                 }
693             }
694 
695             pGlue = (SwFlyPortion*)pPos;
696             bComplete = sal_False;
697         }
698 		nLen = nLen + pPos->GetLen();
699 		pPos = pPos->GetPortion();
700      }
701 
702      if( ! bTabCompat && ! bMultiTab && SVX_ADJUST_RIGHT == GetAdjust() )
703         // portions are moved to the right if possible
704         pLeft->AdjustRight( pCurrent );
705 }
706 
707 /*************************************************************************
708  *                  SwTxtAdjuster::CalcAdjLine()
709  *************************************************************************/
710 
CalcAdjLine(SwLineLayout * pCurrent)711 void SwTxtAdjuster::CalcAdjLine( SwLineLayout *pCurrent )
712 {
713     ASSERT( pCurrent->IsFormatAdj(), "CalcAdjLine: Why?" );
714 
715     pCurrent->SetFormatAdj(sal_False);
716 
717     SwParaPortion* pPara = GetInfo().GetParaPortion();
718 
719     switch( GetAdjust() )
720 	{
721 		case SVX_ADJUST_RIGHT:
722 		case SVX_ADJUST_CENTER:
723 		{
724             CalcFlyAdjust( pCurrent );
725             pPara->GetRepaint()->SetOfst( 0 );
726 			break;
727 		}
728 		case SVX_ADJUST_BLOCK:
729 		{
730             // disabled for #i13507#
731             // 8311: In Zeilen mit LineBreaks gibt es keinen Blocksatz!
732 /*          if( pCurrent->GetLen() &&
733                 CH_BREAK == GetInfo().GetChar( nStart + pCurrent->GetLen() - 1 ) &&
734 				!IsLastBlock() )
735 			{
736 				if( IsLastCenter() )
737 				{
738                     CalcFlyAdjust( pCurrent );
739                     pPara->GetRepaint()->SetOfst( 0 );
740 					break;
741 				}
742 				return;
743 			}
744 */          FormatBlock();
745 			break;
746 		}
747 		default : return;
748 	}
749 }
750 
751 /*************************************************************************
752  *                    SwTxtAdjuster::CalcFlyPortion()
753  *
754  * Die Berechnung hat es in sich: nCurrWidth geibt die Breite _vor_ dem
755  * aufaddieren des Wortes das noch auf die Zeile passt! Aus diesem Grund
756  * stimmt die Breite der FlyPortion auch, wenn die Blockierungssituation
757  * bFirstWord && !WORDFITS eintritt.
758  *************************************************************************/
759 
CalcFlyPortion(const long nRealWidth,const SwRect & rCurrRect)760 SwFlyPortion *SwTxtAdjuster::CalcFlyPortion( const long nRealWidth,
761 											 const SwRect &rCurrRect )
762 {
763     SwTxtFly aTxtFly( GetTxtFrm() );
764 
765 	const KSHORT nCurrWidth = pCurr->PrtWidth();
766 	SwFlyPortion *pFlyPortion = 0;
767 
768     SwRect aLineVert( rCurrRect );
769     if ( GetTxtFrm()->IsRightToLeft() )
770         GetTxtFrm()->SwitchLTRtoRTL( aLineVert );
771     if ( GetTxtFrm()->IsVertical() )
772         GetTxtFrm()->SwitchHorizontalToVertical( aLineVert );
773 
774     // aFlyRect ist dokumentglobal !
775     SwRect aFlyRect( aTxtFly.GetFrm( aLineVert ) );
776 
777     if ( GetTxtFrm()->IsRightToLeft() )
778         GetTxtFrm()->SwitchRTLtoLTR( aFlyRect );
779     if ( GetTxtFrm()->IsVertical() )
780         GetTxtFrm()->SwitchVerticalToHorizontal( aFlyRect );
781 
782 	// Wenn ein Frame ueberlappt, wird eine Portion eroeffnet.
783 	if( aFlyRect.HasArea() )
784 	{
785 		// aLocal ist framelokal
786 		SwRect aLocal( aFlyRect );
787 		aLocal.Pos( aLocal.Left() - GetLeftMargin(), aLocal.Top() );
788         if( nCurrWidth > aLocal.Left() )
789 			aLocal.Left( nCurrWidth );
790 
791 		// Wenn das Rechteck breiter als die Zeile ist, stutzen
792 		// wir es ebenfalls zurecht.
793 		KSHORT nLocalWidth = KSHORT( aLocal.Left() + aLocal.Width() );
794 		if( nRealWidth < long( nLocalWidth ) )
795 			aLocal.Width( nRealWidth - aLocal.Left() );
796 		GetInfo().GetParaPortion()->SetFly( sal_True );
797 		pFlyPortion = new SwFlyPortion( aLocal );
798 		pFlyPortion->Height( KSHORT( rCurrRect.Height() ) );
799 		// Die Width koennte kleiner sein als die FixWidth, daher:
800 		pFlyPortion->AdjFixWidth();
801 	}
802 	return pFlyPortion;
803 }
804 
805 /*************************************************************************
806  *                SwTxtPainter::_CalcDropAdjust()
807  *************************************************************************/
808 
809 // 6721: Drops und Adjustment
810 // CalcDropAdjust wird ggf. am Ende von Format() gerufen.
811 
CalcDropAdjust()812 void SwTxtAdjuster::CalcDropAdjust()
813 {
814 	ASSERT( 1<GetDropLines() && SVX_ADJUST_LEFT!=GetAdjust() && SVX_ADJUST_BLOCK!=GetAdjust(),
815 			"CalcDropAdjust: No reason for DropAdjustment." )
816 
817     const MSHORT nLineNumber = GetLineNr();
818 
819 	// 1) Dummies ueberspringen
820 	Top();
821 
822 	if( !pCurr->IsDummy() || NextLine() )
823 	{
824 		// Erst adjustieren.
825 		GetAdjusted();
826 
827 		SwLinePortion *pPor = pCurr->GetFirstPortion();
828 
829 		// 2) Sicherstellen, dass die DropPortion dabei ist.
830 		// 3) pLeft: Die GluePor vor der DropPor
831 		if( pPor->InGlueGrp() && pPor->GetPortion()
832 			  && pPor->GetPortion()->IsDropPortion() )
833 		{
834 			const SwLinePortion *pDropPor = (SwDropPortion*) pPor->GetPortion();
835 			SwGluePortion *pLeft = (SwGluePortion*) pPor;
836 
837 			// 4) pRight: Die GluePor hinter der DropPor suchen
838 			pPor = pPor->GetPortion();
839 			while( pPor && !pPor->InFixMargGrp() )
840 				pPor = pPor->GetPortion();
841 
842 			SwGluePortion *pRight = ( pPor && pPor->InGlueGrp() ) ?
843 									(SwGluePortion*) pPor : 0;
844 			if( pRight && pRight != pLeft )
845 			{
846 				// 5) nMinLeft berechnen. Wer steht am weitesten links?
847 				const KSHORT nDropLineStart =
848 					KSHORT(GetLineStart()) + pLeft->Width() + pDropPor->Width();
849 				KSHORT nMinLeft = nDropLineStart;
850 				for( MSHORT i = 1; i < GetDropLines(); ++i )
851 				{
852 					if( NextLine() )
853 					{
854 						// Erst adjustieren.
855 						GetAdjusted();
856 
857 						pPor = pCurr->GetFirstPortion();
858 						const SwMarginPortion *pMar = pPor->IsMarginPortion() ?
859 													  (SwMarginPortion*)pPor : 0;
860 						if( !pMar )
861 							nMinLeft = 0;
862 						else
863 						{
864 							const KSHORT nLineStart =
865 								KSHORT(GetLineStart()) + pMar->Width();
866 							if( nMinLeft > nLineStart )
867 								nMinLeft = nLineStart;
868 						}
869 					}
870 				}
871 
872 				// 6) Den Glue zwischen pLeft und pRight neu verteilen.
873 				if( nMinLeft < nDropLineStart )
874 				{
875 					// Glue wird immer von pLeft nach pRight abgegeben,
876 					// damit der Text nach links wandert.
877 					const short nGlue = nDropLineStart - nMinLeft;
878 					if( !nMinLeft )
879 						pLeft->MoveAllGlue( pRight );
880 					else
881 						pLeft->MoveGlue( pRight, nGlue );
882 #ifdef DBGTXT
883 					aDbstream << "Drop adjusted: " << nGlue << endl;
884 #endif
885 				}
886 			}
887 		}
888 	}
889 
890     if( nLineNumber != GetLineNr() )
891 	{
892 		Top();
893         while( nLineNumber != GetLineNr() && Next() )
894 			;
895 	}
896 }
897 
898 /*************************************************************************
899  *                SwTxtAdjuster::CalcDropRepaint()
900  *************************************************************************/
901 
CalcDropRepaint()902 void SwTxtAdjuster::CalcDropRepaint()
903 {
904 	Top();
905 	SwRepaint &rRepaint = *GetInfo().GetParaPortion()->GetRepaint();
906 	if( rRepaint.Top() > Y() )
907 		rRepaint.Top( Y() );
908 	for( MSHORT i = 1; i < GetDropLines(); ++i )
909 		NextLine();
910 	const SwTwips nBottom = Y() + GetLineHeight() - 1;
911 	if( rRepaint.Bottom() < nBottom )
912 		rRepaint.Bottom( nBottom );
913 }
914 
915 
916