xref: /AOO41X/main/sw/source/core/text/itradj.cxx (revision efeef26f81c84063fb0a91bde3856d4a51172d90)
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