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