xref: /trunk/main/sw/source/core/text/porfld.cxx (revision a893be29343ee97512d484e6e8fefa91df2b44cb)
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 
27 
28 #include <hintids.hxx>
29 
30 #include <com/sun/star/i18n/ScriptType.hdl>
31 #include <vcl/graph.hxx>
32 #include <editeng/brshitem.hxx>
33 #include <vcl/metric.hxx>
34 #include <vcl/outdev.hxx>
35 #include <viewopt.hxx>  // SwViewOptions
36 #include <txtcfg.hxx>
37 #include <SwPortionHandler.hxx>
38 #include <porlay.hxx>
39 #include <porfld.hxx>
40 #include <inftxt.hxx>
41 #include <blink.hxx>    // pBlink
42 #include <frmtool.hxx>  // DrawGraphic
43 #include <viewsh.hxx>
44 #include <docsh.hxx>
45 #include <doc.hxx>
46 #include "rootfrm.hxx"
47 #include <breakit.hxx>
48 #include <porrst.hxx>
49 #include <porftn.hxx>   // SwFtnPortion
50 #include <accessibilityoptions.hxx>
51 #include <editeng/lrspitem.hxx>
52 #include <unicode/ubidi.h>
53 
54 using namespace ::com::sun::star;
55 
56 /*************************************************************************
57  *                      class SwFldPortion
58  *************************************************************************/
59 
60 SwLinePortion *SwFldPortion::Compress()
61 { return (GetLen() || aExpand.Len() || SwLinePortion::Compress()) ? this : 0; }
62 
63 SwFldPortion *SwFldPortion::Clone( const XubString &rExpand ) const
64 {
65     SwFont *pNewFnt;
66     if( 0 != ( pNewFnt = pFnt ) )
67     {
68         pNewFnt = new SwFont( *pFnt );
69     }
70     SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder );
71     pClone->SetNextOffset( nNextOffset );
72     pClone->m_bNoLength = this->m_bNoLength;
73     return pClone;
74 }
75 
76 void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld )
77 {
78     ASSERT( pFld, "TakeNextOffset: Missing Source" );
79     nNextOffset = pFld->GetNextOffset();
80     aExpand.Erase( 0, nNextOffset );
81     bFollow = sal_True;
82 }
83 
84 SwFldPortion::SwFldPortion( const XubString &rExpand, SwFont *pFont, sal_Bool bPlaceHold )
85     : aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(STRING_LEN), nViewWidth(0),
86       bFollow( sal_False ), bHasFollow( sal_False ), bPlaceHolder( bPlaceHold )
87     , m_bNoLength( sal_False )
88 {
89     SetWhichPor( POR_FLD );
90     m_nAttrFldType = 0;
91 }
92 
93 SwFldPortion::SwFldPortion( const SwFldPortion& rFld )
94     : SwExpandPortion( rFld ),
95       aExpand( rFld.GetExp() ),
96       nNextOffset( rFld.GetNextOffset() ),
97       nNextScriptChg( rFld.GetNextScriptChg() ),
98       bFollow( rFld.IsFollow() ),
99       bLeft( rFld.IsLeft() ),
100       bHide( rFld.IsHide() ),
101       bCenter( rFld.IsCenter() ),
102       bHasFollow( rFld.HasFollow() ),
103       bPlaceHolder( rFld.bPlaceHolder )
104     , m_bNoLength( rFld.m_bNoLength )
105 {
106     if ( rFld.HasFont() )
107         pFnt = new SwFont( *rFld.GetFont() );
108     else
109         pFnt = 0;
110 
111     SetWhichPor( POR_FLD );
112 }
113 
114 SwFldPortion::~SwFldPortion()
115 {
116     delete pFnt;
117     if( pBlink )
118         pBlink->Delete( this );
119 }
120 
121 /*************************************************************************
122  *               virtual SwFldPortion::GetViewWidth()
123  *************************************************************************/
124 
125 KSHORT SwFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
126 {
127     // Wir stehen zwar im const, aber nViewWidth sollte erst im letzten
128     // Moment errechnet werden:
129     SwFldPortion* pThis = (SwFldPortion*)this;
130     if( !Width() && rInf.OnWin() && !rInf.GetOpt().IsPagePreview() &&
131             !rInf.GetOpt().IsReadonly() && SwViewOption::IsFieldShadings() )
132     {
133         if( !nViewWidth )
134             pThis->nViewWidth = rInf.GetTxtSize( ' ' ).Width();
135     }
136     else
137         pThis->nViewWidth = 0;
138     return nViewWidth;
139 }
140 
141 /*************************************************************************
142  *                 virtual SwFldPortion::Format()
143  *************************************************************************/
144 
145 // 8653: in keinem Fall nur SetLen(0);
146 
147 /*************************************************************************
148  *   Hilfsklasse SwFldSlot
149  **************************************************************************/
150 
151 class SwFldSlot
152 {
153     const XubString *pOldTxt;
154     XubString aTxt;
155     xub_StrLen nIdx;
156     xub_StrLen nLen;
157     sal_Bool bOn;
158     SwTxtFormatInfo *pInf;
159 public:
160     SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor );
161     ~SwFldSlot();
162 };
163 
164 SwFldSlot::SwFldSlot( const SwTxtFormatInfo* pNew, const SwFldPortion *pPor )
165 {
166     bOn = pPor->GetExpTxt( *pNew, aTxt );
167 
168     // Der Text wird ausgetauscht...
169     if( bOn )
170     {
171         pInf = (SwTxtFormatInfo*)pNew;
172         nIdx = pInf->GetIdx();
173         nLen = pInf->GetLen();
174         pOldTxt = &(pInf->GetTxt());
175         pInf->SetLen( aTxt.Len() );
176         if( pPor->IsFollow() )
177         {
178             pInf->SetFakeLineStart( nIdx > pInf->GetLineStart() );
179             pInf->SetIdx( 0 );
180         }
181         else
182         {
183             XubString aTmp( aTxt );
184             aTxt = *pOldTxt;
185             aTxt.Erase( nIdx, 1 );
186             aTxt.Insert( aTmp, nIdx );
187         }
188         pInf->SetTxt( aTxt );
189     }
190 }
191 
192 SwFldSlot::~SwFldSlot()
193 {
194     if( bOn )
195     {
196         pInf->SetTxt( *pOldTxt );
197         pInf->SetIdx( nIdx );
198         pInf->SetLen( nLen );
199         pInf->SetFakeLineStart( sal_False );
200     }
201 }
202 
203 void SwFldPortion::CheckScript( const SwTxtSizeInfo &rInf )
204 {
205     String aTxt;
206     if( GetExpTxt( rInf, aTxt ) && aTxt.Len() && pBreakIt->GetBreakIter().is() )
207     {
208         sal_uInt8 nActual = pFnt ? pFnt->GetActual() : rInf.GetFont()->GetActual();
209         sal_uInt16 nScript;
210         {
211             nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, 0 );
212             xub_StrLen nChg = 0;
213             if( i18n::ScriptType::WEAK == nScript )
214             {
215                 nChg =(xub_StrLen)pBreakIt->GetBreakIter()->endOfScript(aTxt,0,nScript);
216                 if( nChg < aTxt.Len() )
217                     nScript = pBreakIt->GetBreakIter()->getScriptType( aTxt, nChg );
218             }
219 
220             //
221             // nNextScriptChg will be evaluated during SwFldPortion::Format()
222             //
223             if ( nChg < aTxt.Len() )
224                 nNextScriptChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( aTxt, nChg, nScript );
225             else
226                 nNextScriptChg = aTxt.Len();
227 
228         }
229         sal_uInt8 nTmp;
230         switch ( nScript ) {
231             case i18n::ScriptType::LATIN : nTmp = SW_LATIN; break;
232             case i18n::ScriptType::ASIAN : nTmp = SW_CJK; break;
233             case i18n::ScriptType::COMPLEX : nTmp = SW_CTL; break;
234             default: nTmp = nActual;
235         }
236 
237         // #i16354# Change script type for RTL text to CTL.
238         const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo();
239         // --> OD 2009-01-29 #i98418#
240 //        const sal_uInt8 nFldDir = IsNumberPortion() ?
241         const sal_uInt8 nFldDir = ( IsNumberPortion() || IsFtnNumPortion() ) ?
242                              rSI.GetDefaultDir() :
243                              rSI.DirType( IsFollow() ? rInf.GetIdx() - 1 : rInf.GetIdx() );
244         // <--
245         if ( UBIDI_RTL == nFldDir )
246         {
247             UErrorCode nError = U_ZERO_ERROR;
248             UBiDi* pBidi = ubidi_openSized( aTxt.Len(), 0, &nError );
249             ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aTxt.GetBuffer()), aTxt.Len(), nFldDir, NULL, &nError );
250             int32_t nEnd;
251             UBiDiLevel nCurrDir;
252             ubidi_getLogicalRun( pBidi, 0, &nEnd, &nCurrDir );
253             ubidi_close( pBidi );
254             const xub_StrLen nNextDirChg = (xub_StrLen)nEnd;
255             nNextScriptChg = Min( nNextScriptChg, nNextDirChg );
256 
257             // #i89825# change the script type also to CTL
258             // if there is no strong LTR char in the LTR run (numbers)
259             if ( nCurrDir != UBIDI_RTL )
260             {
261                 nCurrDir = UBIDI_RTL;
262                 for ( xub_StrLen nCharIdx = 0; nCharIdx < nEnd; ++nCharIdx )
263                 {
264                     UCharDirection nCharDir = u_charDirection ( aTxt.GetChar ( nCharIdx ));
265                     if ( nCharDir == U_LEFT_TO_RIGHT ||
266                          nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
267                          nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
268                     {
269                         nCurrDir = UBIDI_LTR;
270                         break;
271                     }
272                 }
273             }
274 
275             if ( nCurrDir == UBIDI_RTL )
276                 nTmp = SW_CTL;
277         }
278 
279         // --> OD 2009-01-29 #i98418#
280         // keep determined script type for footnote portions as preferred script type.
281         // For footnote portions a font can not be created directly - see footnote
282         // portion format method.
283 //         if( !IsFtnPortion() && nTmp != nActual )
284         if ( IsFtnPortion() )
285         {
286             dynamic_cast<SwFtnPortion*>(this)->SetPreferredScriptType( nTmp );
287         }
288         else if ( nTmp != nActual )
289         {
290             if( !pFnt )
291                 pFnt = new SwFont( *rInf.GetFont() );
292             pFnt->SetActual( nTmp );
293         }
294         // <--
295     }
296 }
297 
298 sal_Bool SwFldPortion::Format( SwTxtFormatInfo &rInf )
299 {
300     // Scope wegen aDiffTxt::DTOR!
301     xub_StrLen nRest;
302     sal_Bool bFull;
303     sal_Bool bEOL = sal_False;
304     long nTxtRest = rInf.GetTxt().Len() - rInf.GetIdx();
305     {
306         SwFldSlot aDiffTxt( &rInf, this );
307         SwLayoutModeModifier aLayoutModeModifier( *rInf.GetOut() );
308         aLayoutModeModifier.SetAuto();
309 
310         // Field portion has to be split in several parts if
311         // 1. There are script/direction changes inside the field
312         // 2. There are portion breaks (tab, break) inside the field:
313         const xub_StrLen nOldFullLen = rInf.GetLen();
314         xub_StrLen nFullLen = rInf.ScanPortionEnd( rInf.GetIdx(), rInf.GetIdx() + nOldFullLen ) - rInf.GetIdx();
315         if ( nNextScriptChg < nFullLen )
316         {
317             nFullLen = nNextScriptChg;
318             rInf.SetHookChar( 0 );
319         }
320         rInf.SetLen( nFullLen );
321 
322         if ( STRING_LEN != rInf.GetUnderScorePos() &&
323              rInf.GetUnderScorePos() > rInf.GetIdx() )
324              rInf.SetUnderScorePos( rInf.GetIdx() );
325 
326         if( pFnt )
327             pFnt->GoMagic( rInf.GetVsh(), pFnt->GetActual() );
328 
329         SwFontSave aSave( rInf, pFnt );
330 
331         // 8674: Laenge muss 0 sein, bei bFull nach Format ist die Laenge
332         // gesetzt und wird in nRest uebertragen. Ansonsten bleibt die
333         // Laenge erhalten und wuerde auch in nRest einfliessen!
334         SetLen(0);
335         const MSHORT nFollow = IsFollow() ? 0 : 1;
336 
337         // So komisch es aussieht, die Abfrage auf GetLen() muss wegen der
338         // ExpandPortions _hinter_ aDiffTxt (vgl. SoftHyphs)
339         // sal_False returnen wegen SetFull ...
340         if( !nFullLen )
341         {
342             // nicht Init(), weil wir Hoehe und Ascent brauchen
343             Width(0);
344             bFull = rInf.Width() <= rInf.GetPos().X();
345         }
346         else
347         {
348             xub_StrLen nOldLineStart = rInf.GetLineStart();
349             if( IsFollow() )
350                 rInf.SetLineStart( 0 );
351             rInf.SetNotEOL( nFullLen == nOldFullLen && nTxtRest > nFollow );
352 
353             // the height depending on the fields font is set,
354             // this is required for SwTxtGuess::Guess
355             Height( rInf.GetTxtHeight() );
356             // If a kerning portion is inserted after our field portion,
357             // the ascent and height must be known
358             SetAscent( rInf.GetAscent() );
359             bFull = SwTxtPortion::Format( rInf );
360             rInf.SetNotEOL( sal_False );
361             rInf.SetLineStart( nOldLineStart );
362         }
363         xub_StrLen nTmpLen = GetLen();
364         bEOL = !nTmpLen && nFollow && bFull;
365         nRest = nOldFullLen - nTmpLen;
366 
367         // Das Zeichen wird in der ersten Portion gehalten.
368         // Unbedingt nach Format!
369         SetLen( (m_bNoLength) ? 0 : nFollow );
370 
371         if( nRest )
372         {
373             // aExpand ist noch nicht gekuerzt worden, der neue Ofst
374             // ergibt sich durch nRest.
375             xub_StrLen nNextOfst = aExpand.Len() - nRest;
376 
377             if ( IsQuoVadisPortion() )
378                 nNextOfst = nNextOfst + ((SwQuoVadisPortion*)this)->GetContTxt().Len();
379 
380             XubString aNew( aExpand, nNextOfst, STRING_LEN );
381             aExpand.Erase( nNextOfst, STRING_LEN );
382 
383             // These characters should not be contained in the follow
384             // field portion. They are handled via the HookChar mechanism.
385             switch( aNew.GetChar( 0 ))
386             {
387                 case CH_BREAK  : bFull = sal_True;
388                             // kein break;
389                 case ' ' :
390                 case CH_TAB    :
391                 case CHAR_HARDHYPHEN:               // non-breaking hyphen
392                 case CHAR_SOFTHYPHEN:
393                 case CHAR_HARDBLANK:
394                 case CHAR_ZWSP :
395                 case CHAR_ZWNBSP :
396                 case CH_TXTATR_BREAKWORD:
397                 case CH_TXTATR_INWORD:
398                 {
399                     aNew.Erase( 0, 1 );
400                     ++nNextOfst;
401                     break;
402                 }
403                 default: ;
404             }
405 
406             // Even if there is no more text left for a follow field,
407             // we have to build a follow field portion (without font),
408             // otherwise the HookChar mechanism would not work.
409             SwFldPortion *pFld = Clone( aNew );
410             if( aNew.Len() && !pFld->GetFont() )
411             {
412                 SwFont *pNewFnt = new SwFont( *rInf.GetFont() );
413                 pFld->SetFont( pNewFnt );
414             }
415             pFld->SetFollow( sal_True );
416             SetHasFollow( sal_True );
417             // In nNextOffset steht bei einem neuangelegten Feld zunaechst
418             // der Offset, an dem es selbst im Originalstring beginnt.
419             // Wenn beim Formatieren ein FollowFeld angelegt wird, wird
420             // der Offset dieses FollowFelds in nNextOffset festgehalten.
421             nNextOffset = nNextOffset + nNextOfst;
422             pFld->SetNextOffset( nNextOffset );
423             rInf.SetRest( pFld );
424         }
425     }
426 
427     if( bEOL && rInf.GetLast() && !rInf.GetUnderFlow() )
428         rInf.GetLast()->FormatEOL( rInf );
429     return bFull;
430 }
431 
432 /*************************************************************************
433  *               virtual SwFldPortion::Paint()
434  *************************************************************************/
435 
436 void SwFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
437 {
438     SwFontSave aSave( rInf, pFnt );
439 
440     ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
441     if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
442     {
443         // Dies ist eine freizuegige Auslegung der Hintergrundbelegung ...
444         rInf.DrawViewOpt( *this, POR_FLD );
445         SwExpandPortion::Paint( rInf );
446     }
447 }
448 
449 /*************************************************************************
450  *              virtual SwFldPortion::GetExpTxt()
451  *************************************************************************/
452 
453 sal_Bool SwFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
454 {
455     rTxt = aExpand;
456     if( !rTxt.Len() && rInf.OnWin() &&
457         !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
458             SwViewOption::IsFieldShadings() &&
459             !HasFollow() )
460         rTxt = ' ';
461     return sal_True;
462 }
463 
464 /*************************************************************************
465  *              virtual SwFldPortion::HandlePortion()
466  *************************************************************************/
467 
468 void SwFldPortion::HandlePortion( SwPortionHandler& rPH ) const
469 {
470     rPH.Special( GetLen(), aExpand, GetWhichPor() );
471     if( GetWhichPor() == POR_FLD )
472     {
473         rPH.SetAttrFieldType(m_nAttrFldType);
474     }
475 }
476 
477 /*************************************************************************
478  *                virtual SwFldPortion::GetTxtSize()
479  *************************************************************************/
480 
481 SwPosSize SwFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
482 {
483     SwFontSave aSave( rInf, pFnt );
484     SwPosSize aSize( SwExpandPortion::GetTxtSize( rInf ) );
485     return aSize;
486 }
487 
488 /*************************************************************************
489  *                      class SwHiddenPortion
490  *************************************************************************/
491 
492 SwFldPortion *SwHiddenPortion::Clone(const XubString &rExpand ) const
493 {
494     SwFont *pNewFnt;
495     if( 0 != ( pNewFnt = pFnt ) )
496         pNewFnt = new SwFont( *pFnt );
497     return new SwHiddenPortion( rExpand, pNewFnt );
498 }
499 
500 /*************************************************************************
501  *               virtual SwHiddenPortion::Paint()
502  *************************************************************************/
503 
504 void SwHiddenPortion::Paint( const SwTxtPaintInfo &rInf ) const
505 {
506     if( Width() )
507     {
508         SwFontSave aSave( rInf, pFnt );
509         rInf.DrawViewOpt( *this, POR_HIDDEN );
510         SwExpandPortion::Paint( rInf );
511     }
512 }
513 
514 /*************************************************************************
515  *              virtual SwHiddenPortion::GetExpTxt()
516  *************************************************************************/
517 
518 sal_Bool SwHiddenPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
519 {
520     // Nicht auf IsHidden() abfragen !
521     return SwFldPortion::GetExpTxt( rInf, rTxt );
522 }
523 
524 /*************************************************************************
525  *                      class SwNumberPortion
526  *************************************************************************/
527 
528 // --> OD 2008-01-23 #newlistlevelattrs#
529 SwNumberPortion::SwNumberPortion( const XubString &rExpand,
530                                   SwFont *pFont,
531                                   const sal_Bool bLft,
532                                   const sal_Bool bCntr,
533                                   const KSHORT nMinDst,
534                                   const bool bLabelAlignmentPosAndSpaceModeActive )
535         : SwFldPortion( rExpand, pFont ),
536           nFixWidth(0),
537           nMinDist( nMinDst ),
538           // --> OD 2008-01-23 #newlistlevelattrs#
539           mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
540           // <--
541 {
542     SetWhichPor( POR_NUMBER );
543     SetLeft( bLft );
544     SetHide( sal_False );
545     SetCenter( bCntr );
546 }
547 
548 xub_StrLen SwNumberPortion::GetCrsrOfst( const MSHORT ) const
549 {
550     return 0;
551 }
552 
553 SwFldPortion *SwNumberPortion::Clone( const XubString &rExpand ) const
554 {
555     SwFont *pNewFnt;
556     if( 0 != ( pNewFnt = pFnt ) )
557         pNewFnt = new SwFont( *pFnt );
558     // --> OD 2008-01-23 #newlistlevelattrs#
559     return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(),
560                                 nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
561     // <--
562 }
563 
564 /*************************************************************************
565  *                 virtual SwNumberPortion::Format()
566  *************************************************************************/
567 
568 // 5010: Wir sind in der Lage, mehrzeilige NumFelder anzulegen!
569 // 3689: Fies ist, wenn man in der Dialogbox soviel Davor-Text
570 // eingibt, bis die Zeile ueberlaeuft.
571 // Man muss die Fly-Ausweichmanoever beachten!
572 
573 sal_Bool SwNumberPortion::Format( SwTxtFormatInfo &rInf )
574 {
575     SetHide( sal_False );
576     const sal_Bool bFull = SwFldPortion::Format( rInf );
577     SetLen( 0 );
578     // a numbering portion can be contained in a rotated portion!!!
579     nFixWidth = rInf.IsMulti() ? Height() : Width();
580     rInf.SetNumDone( !rInf.GetRest() );
581     if( rInf.IsNumDone() )
582     {
583 //        SetAscent( rInf.GetAscent() );
584         ASSERT( Height() && nAscent, "NumberPortions without Height | Ascent" );
585 
586         long nDiff( 0 );
587         // --> OD 2008-01-23 #newlistlevelattrs#
588         if ( !mbLabelAlignmentPosAndSpaceModeActive )
589         {
590             if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
591                  // --> FME 2004-08-13 #i32902#
592                  !IsFtnNumPortion() )
593                  // <--
594             {
595                 nDiff = rInf.Left()
596                     + rInf.GetTxtFrm()->GetTxtNode()->
597                     GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst()
598                     - rInf.First()
599                     + rInf.ForcedLeftMargin();
600             }
601             else
602             {
603                 nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
604             }
605         }
606         // <--
607         // Ein Vorschlag von Juergen und Volkmar:
608         // Der Textteil hinter der Numerierung sollte immer
609         // mindestens beim linken Rand beginnen.
610         if( nDiff < 0 )
611             nDiff = 0;
612         else if ( nDiff > rInf.X() )
613             nDiff -= rInf.X();
614         else
615             nDiff = 0;
616 
617         if( nDiff < nFixWidth + nMinDist )
618             nDiff = nFixWidth + nMinDist;
619         // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
620         // fieser Sonderfall: FlyFrm liegt in dem Bereich,
621         // den wir uns gerade unter den Nagel reissen wollen.
622         // Die NumberPortion wird als verborgen markiert.
623         const sal_Bool bFly = rInf.GetFly() ||
624             ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
625         if( nDiff > rInf.Width() )
626         {
627             nDiff = rInf.Width();
628             if ( bFly )
629                 SetHide( sal_True );
630         }
631 
632         // A numbering portion can be inside a SwRotatedPortion. Then the
633         // Height has to be changed
634         if ( rInf.IsMulti() )
635         {
636             if ( Height() < nDiff )
637                 Height( KSHORT( nDiff ) );
638         }
639         else if( Width() < nDiff )
640             Width( KSHORT(nDiff) );
641     }
642     return bFull;
643 }
644 
645 void SwNumberPortion::FormatEOL( SwTxtFormatInfo& )
646 {
647 /*  Ein FormatEOL deutet daraufhin, dass der folgende Text
648  *  nicht mit auf die Zeile passte. Damit die Numerierung mitwandert,
649  *  wird diese NumberPortion verborgen.
650  */
651 
652     // This caused trouble with flys anchored as characters.
653     // If one of these is numbered but does not fit to the line,
654     // it calls this function, causing a loop because both the number
655     // portion and the fly portion go to the next line
656 //    SetHide( sal_True );
657 }
658 
659 /*************************************************************************
660  *               virtual SwNumberPortion::Paint()
661  *************************************************************************/
662 
663 void SwNumberPortion::Paint( const SwTxtPaintInfo &rInf ) const
664 {
665 /*  Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
666  *  Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
667  */
668 
669     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
670     {
671         SwLinePortion *pTmp = GetPortion();
672         while ( pTmp && !pTmp->InTxtGrp() )
673             pTmp = pTmp->GetPortion();
674         if ( !pTmp )
675             return;
676     }
677 
678     // calculate the width of the number portion, including follows
679     const KSHORT nOldWidth = Width();
680     sal_uInt16 nSumWidth = 0;
681     sal_uInt16 nOffset = 0;
682 
683     const SwLinePortion* pTmp = this;
684     while ( pTmp && pTmp->InNumberGrp() )
685     {
686         nSumWidth = nSumWidth + pTmp->Width();
687         if ( ((SwNumberPortion*)pTmp)->HasFollow() )
688             pTmp = pTmp->GetPortion();
689         else
690         {
691             nOffset = pTmp->Width() - ((SwNumberPortion*)pTmp)->nFixWidth;
692             break;
693         }
694     }
695 
696     // The master portion takes care for painting the background of the
697     // follow field portions
698     if ( ! IsFollow() )
699     {
700         SwLinePortion *pThis = (SwLinePortion*)this;
701         pThis->Width( nSumWidth );
702         rInf.DrawViewOpt( *this, POR_NUMBER );
703         pThis->Width( nOldWidth );
704     }
705 
706     if( aExpand.Len() )
707     {
708         const SwFont *pTmpFnt = rInf.GetFont();
709         sal_Bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() ||
710                                  UNDERLINE_NONE != pTmpFnt->GetOverline()  ||
711                                  STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
712                                  !pTmpFnt->IsWordLineMode();
713         if( bPaintSpace && pFnt )
714             bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() ||
715                             UNDERLINE_NONE != pFnt->GetOverline()  ||
716                             STRIKEOUT_NONE != pFnt->GetStrikeout() ) &&
717                             !pFnt->IsWordLineMode();
718 
719         SwFontSave aSave( rInf, pFnt );
720 
721         if( nFixWidth == Width() && ! HasFollow() )
722             SwExpandPortion::Paint( rInf );
723         else
724         {
725             // logisches const: Width wird wieder zurueckgesetzt
726             SwLinePortion *pThis = (SwLinePortion*)this;
727             bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
728             KSHORT nSpaceOffs = nFixWidth;
729             pThis->Width( nFixWidth );
730 
731             if( ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
732                 ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ) )
733                 SwExpandPortion::Paint( rInf );
734             else
735             {
736                 SwTxtPaintInfo aInf( rInf );
737                 if( nOffset < nMinDist )
738                     nOffset = 0;
739                 else
740                 {
741                     if( IsCenter() )
742                     {
743                         /* #110778# a / 2 * 2 == a is not a tautology */
744                         KSHORT nTmpOffset = nOffset;
745                         nOffset /= 2;
746                         if( nOffset < nMinDist )
747                             nOffset = nTmpOffset - nMinDist;
748                     }
749                     else
750                         nOffset = nOffset - nMinDist;
751                 }
752                 aInf.X( aInf.X() + nOffset );
753                 SwExpandPortion::Paint( aInf );
754                 if( bPaintSpace )
755                     nSpaceOffs = nSpaceOffs + nOffset;
756             }
757             if( bPaintSpace && nOldWidth > nSpaceOffs )
758             {
759                 SwTxtPaintInfo aInf( rInf );
760 static sal_Char __READONLY_DATA sDoubleSpace[] = "  ";
761                 aInf.X( aInf.X() + nSpaceOffs );
762 
763                 // --> FME 2005-08-12 #i53199# Adjust position of underline:
764                 if ( rInf.GetUnderFnt() )
765                 {
766                     const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
767                     rInf.GetUnderFnt()->SetPos( aNewPos );
768                 }
769                 // <--
770 
771                 pThis->Width( nOldWidth - nSpaceOffs + 12 );
772                 {
773                     SwTxtSlot aDiffTxt( &aInf, this, true, false, sDoubleSpace );
774                     aInf.DrawText( *this, aInf.GetLen(), sal_True );
775                 }
776             }
777             pThis->Width( nOldWidth );
778         }
779     }
780 }
781 
782 
783 /*************************************************************************
784  *                      class SwBulletPortion
785  *************************************************************************/
786 
787 // --> OD 2008-01-23 #newlistlevelattrs#
788 SwBulletPortion::SwBulletPortion( const xub_Unicode cBullet,
789                                   const XubString& rBulletFollowedBy,
790                                   SwFont *pFont,
791                                   const sal_Bool bLft,
792                                   const sal_Bool bCntr,
793                                   const KSHORT nMinDst,
794                                   const bool bLabelAlignmentPosAndSpaceModeActive )
795     : SwNumberPortion( XubString( rBulletFollowedBy ).Insert( cBullet, 0 ) ,
796                        pFont, bLft, bCntr, nMinDst,
797                        bLabelAlignmentPosAndSpaceModeActive )
798 // <--
799 {
800     SetWhichPor( POR_BULLET );
801 }
802 
803 /*************************************************************************
804  *                      class SwGrfNumPortion
805  *************************************************************************/
806 
807 #define GRFNUM_SECURE 10
808 
809 // --> OD 2008-01-23 #newlistlevelattrs#
810 SwGrfNumPortion::SwGrfNumPortion(
811         SwFrm*,
812         const XubString& rGraphicFollowedBy,
813         const SvxBrushItem* pGrfBrush,
814         const SwFmtVertOrient* pGrfOrient, const Size& rGrfSize,
815         const sal_Bool bLft, const sal_Bool bCntr, const KSHORT nMinDst,
816         const bool bLabelAlignmentPosAndSpaceModeActive ) :
817     SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst,
818                      bLabelAlignmentPosAndSpaceModeActive ),
819 // <--
820     pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
821 {
822     SetWhichPor( POR_GRFNUM );
823     SetAnimated( sal_False );
824     bReplace = sal_False;
825     if( pGrfBrush )
826     {
827         *pBrush = *pGrfBrush;
828         const Graphic* pGraph = pGrfBrush->GetGraphic();
829         if( pGraph )
830             SetAnimated( pGraph->IsAnimated() );
831         else
832             bReplace = sal_True;
833     }
834     if( pGrfOrient )
835     {
836         nYPos = pGrfOrient->GetPos();
837         eOrient = pGrfOrient->GetVertOrient();
838     }
839     else
840     {
841         nYPos = 0;
842         eOrient = text::VertOrientation::TOP;
843     }
844     Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
845     nFixWidth = Width();
846     nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
847     Height( KSHORT(nGrfHeight) );
848     bNoPaint = sal_False;
849 }
850 
851 SwGrfNumPortion::~SwGrfNumPortion()
852 {
853     if ( IsAnimated() )
854         ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
855     delete pBrush;
856 }
857 
858 void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
859 {
860     if ( IsAnimated() )
861         ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( pOut, nId );
862 }
863 
864 sal_Bool SwGrfNumPortion::Format( SwTxtFormatInfo &rInf )
865 {
866     SetHide( sal_False );
867     // --> OD 2008-01-29 #newlistlevelattrs#
868 //    Width( nFixWidth );
869     KSHORT nFollowedByWidth( 0 );
870     if ( mbLabelAlignmentPosAndSpaceModeActive )
871     {
872         SwFldPortion::Format( rInf );
873         nFollowedByWidth = Width();
874         SetLen( 0 );
875     }
876     Width( nFixWidth + nFollowedByWidth );
877     // <--
878     const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
879     const sal_Bool bFly = rInf.GetFly() ||
880         ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
881     SetAscent( static_cast<sal_uInt16>(GetRelPos() > 0 ? GetRelPos() : 0) );
882     if( GetAscent() > Height() )
883         Height( GetAscent() );
884 
885     if( bFull )
886     {
887         Width( rInf.Width() - (KSHORT)rInf.X() );
888         if( bFly )
889         {
890             SetLen( 0 );
891             SetNoPaint( sal_True );
892             rInf.SetNumDone( sal_False );
893             return sal_True;
894         }
895     }
896     rInf.SetNumDone( sal_True );
897     // --> OD 2008-01-23 #newlistlevelattrs#
898 //    long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
899     long nDiff = mbLabelAlignmentPosAndSpaceModeActive
900                  ? 0
901                  : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
902     // <--
903     // Ein Vorschlag von Juergen und Volkmar:
904     // Der Textteil hinter der Numerierung sollte immer
905     // mindestens beim linken Rand beginnen.
906     if( nDiff < 0 )
907         nDiff = 0;
908     else if ( nDiff > rInf.X() )
909         nDiff -= rInf.X();
910     if( nDiff < nFixWidth + nMinDist )
911         nDiff = nFixWidth + nMinDist;
912     // 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
913     // fieser Sonderfall: FlyFrm liegt in dem Bereich,
914     // den wir uns gerade unter den Nagel reissen wollen.
915     // Die NumberPortion wird als verborgen markiert.
916     if( nDiff > rInf.Width() )
917     {
918         nDiff = rInf.Width();
919         if( bFly )
920             SetHide( sal_True );
921     }
922 
923     if( Width() < nDiff )
924         Width( KSHORT(nDiff) );
925     return bFull;
926 }
927 
928 void SwGrfNumPortion::Paint( const SwTxtPaintInfo &rInf ) const
929 {
930     if( DontPaint() )
931         return;
932 /*  Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
933  *  Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
934  */
935     if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
936     {
937         SwLinePortion *pTmp = GetPortion();
938         while ( pTmp && !pTmp->InTxtGrp() )
939             pTmp = pTmp->GetPortion();
940         if ( !pTmp )
941             return;
942     }
943     Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
944     long nTmpWidth = Max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) );
945     Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
946 
947     // --> OD 2008-02-05 #newlistlevelattrs#
948     const sal_Bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
949                               ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
950                               ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() );
951     // <--
952 
953     if( nFixWidth < Width() && !bTmpLeft )
954     {
955         KSHORT nOffset = Width() - nFixWidth;
956         if( nOffset < nMinDist )
957             nOffset = 0;
958         else
959         {
960             if( IsCenter() )
961             {
962                 nOffset /= 2;
963                 if( nOffset < nMinDist )
964                     nOffset = Width() - nFixWidth - nMinDist;
965             }
966             else
967                 nOffset = nOffset - nMinDist;
968         }
969         aPos.X() += nOffset;
970     }
971 
972     if( bReplace )
973     {
974         KSHORT nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120;
975         aSize = Size( nTmpH, nTmpH );
976         aPos.Y() = rInf.Y() - nTmpH;
977     }
978     SwRect aTmp( aPos, aSize );
979 
980     sal_Bool bDraw = sal_True;
981 
982     if ( IsAnimated() )
983     {
984         bDraw = !rInf.GetOpt().IsGraphic();
985         if( !nId )
986         {
987             SetId( long( rInf.GetTxtFrm() ) );
988             rInf.GetTxtFrm()->SetAnimation();
989         }
990         if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
991         {
992             rInf.NoteAnimation();
993             const ViewShell* pViewShell = rInf.GetVsh();
994 
995             // virtual device, not pdf export
996             if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
997                 pViewShell && pViewShell->GetWin()  )
998             {
999                 ( (Graphic*) pBrush->GetGraphic() )->StopAnimation(0,nId);
1000                 rInf.GetTxtFrm()->getRootFrm()->GetCurrShell()->InvalidateWindows( aTmp );
1001             }
1002 
1003 
1004             else if ( pViewShell &&
1005                      !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
1006                      !pViewShell->IsPreView() &&
1007                       // --> FME 2004-06-21 #i9684# Stop animation during printing/pdf export.
1008                       pViewShell->GetWin() )
1009                       // <--
1010             {
1011                 ( (Graphic*) pBrush->GetGraphic() )->StartAnimation(
1012                     (OutputDevice*)rInf.GetOut(), aPos, aSize, nId );
1013             }
1014 
1015             // pdf export, printing, preview, stop animations...
1016             else
1017                 bDraw = sal_True;
1018         }
1019         if( bDraw )
1020             ( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
1021     }
1022 
1023     SwRect aRepaint( rInf.GetPaintRect() );
1024     const SwTxtFrm& rFrm = *rInf.GetTxtFrm();
1025     if( rFrm.IsVertical() )
1026     {
1027         rFrm.SwitchHorizontalToVertical( aTmp );
1028         rFrm.SwitchHorizontalToVertical( aRepaint );
1029     }
1030 
1031     if( rFrm.IsRightToLeft() )
1032     {
1033         rFrm.SwitchLTRtoRTL( aTmp );
1034         rFrm.SwitchLTRtoRTL( aRepaint );
1035     }
1036 
1037     if( bDraw && aTmp.HasArea() )
1038     {
1039         DrawGraphic( pBrush, (OutputDevice*)rInf.GetOut(),
1040             aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
1041     }
1042 }
1043 
1044 void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
1045                                long nFlyAsc, long nFlyDesc )
1046 {
1047     if ( GetOrient() != text::VertOrientation::NONE )
1048     {
1049         SetRelPos( 0 );
1050         if ( GetOrient() == text::VertOrientation::CENTER )
1051             SetRelPos( GetGrfHeight() / 2 );
1052         else if ( GetOrient() == text::VertOrientation::TOP )
1053             SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1054         else if ( GetOrient() == text::VertOrientation::BOTTOM )
1055             ;
1056         else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1057             SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1058         else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1059             SetRelPos( nLnAscent );
1060         else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1061             SetRelPos( GetGrfHeight() - nLnDescent );
1062         else
1063         {
1064             if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1065             {
1066                 // wenn ich genauso gross bin wie die Zeile, brauche ich mich
1067                 // nicht an der Zeile nicht weiter ausrichten, ich lasse
1068                 // dann auch den max. Ascent der Zeile unveraendert
1069 
1070                 SetRelPos( nFlyAsc );
1071             }
1072             else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1073                 SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1074             else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1075                 SetRelPos( nFlyAsc );
1076             else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1077                 SetRelPos( GetGrfHeight() - nFlyDesc );
1078         }
1079     }
1080 }
1081 
1082 void SwTxtFrm::StopAnimation( OutputDevice* pOut )
1083 {
1084     ASSERT( HasAnimation(), "SwTxtFrm::StopAnimation: Which Animation?" );
1085     if( HasPara() )
1086     {
1087         SwLineLayout *pLine = GetPara();
1088         while( pLine )
1089         {
1090             SwLinePortion *pPor = pLine->GetPortion();
1091             while( pPor )
1092             {
1093                 if( pPor->IsGrfNumPortion() )
1094                     ((SwGrfNumPortion*)pPor)->StopAnimation( pOut );
1095                 // Die Numerierungsportion sitzt immer vor dem ersten Zeichen,
1096                 // deshalb koennen wir abbrechen, sobald wir eine Portion mit
1097                 // einer Laenge > 0 erreicht haben.
1098                 pPor = pPor->GetLen() ? 0 : pPor->GetPortion();
1099             }
1100             pLine = pLine->GetLen() ? 0 : pLine->GetNext();
1101         }
1102     }
1103 }
1104 
1105 /*************************************************************************
1106  * SwCombinedPortion::SwCombinedPortion(..)
1107  * initializes the script array and clears the width array
1108  *************************************************************************/
1109 
1110 SwCombinedPortion::SwCombinedPortion( const XubString &rTxt )
1111      : SwFldPortion( rTxt )
1112 {
1113     SetLen(1);
1114     SetWhichPor( POR_COMBINED );
1115     if( aExpand.Len() > 6 )
1116         aExpand.Erase( 6 );
1117     // Initialization of the scripttype array,
1118     // the arrays of width and position are filled by the format function
1119     if( pBreakIt->GetBreakIter().is() )
1120     {
1121         sal_uInt8 nScr = SW_SCRIPTS;
1122         for( sal_uInt16 i = 0; i < rTxt.Len(); ++i )
1123         {
1124             sal_uInt16 nScript = pBreakIt->GetBreakIter()->getScriptType( rTxt, i );
1125             switch ( nScript ) {
1126                 case i18n::ScriptType::LATIN : nScr = SW_LATIN; break;
1127                 case i18n::ScriptType::ASIAN : nScr = SW_CJK; break;
1128                 case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break;
1129             }
1130             aScrType[i] = nScr;
1131         }
1132     }
1133     else
1134     {
1135         for( sal_uInt16 i = 0; i < 6; aScrType[i++] = 0 )
1136             ; // nothing
1137     }
1138     memset( &aWidth, 0, sizeof(aWidth) );
1139 }
1140 
1141 /*************************************************************************
1142  * SwCombinedPortion::Paint(..)
1143  *************************************************************************/
1144 
1145 void SwCombinedPortion::Paint( const SwTxtPaintInfo &rInf ) const
1146 {
1147     ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
1148     if( Width() )
1149     {
1150         rInf.DrawBackBrush( *this );
1151         rInf.DrawViewOpt( *this, POR_FLD );
1152 
1153         // do we have to repaint a post it portion?
1154         if( rInf.OnWin() && pPortion && !pPortion->Width() )
1155             pPortion->PrePaint( rInf, this );
1156 
1157         sal_uInt16 nCount = aExpand.Len();
1158         if( !nCount )
1159             return;
1160         ASSERT( nCount < 7, "Too much combined characters" );
1161 
1162         // the first character of the second row
1163         sal_uInt16 nTop = ( nCount + 1 ) / 2;
1164 
1165         SwFont aTmpFont( *rInf.GetFont() );
1166         aTmpFont.SetProportion( nProportion );  // a smaller font
1167         SwFontSave aFontSave( rInf, &aTmpFont );
1168 
1169         sal_uInt16 i = 0;
1170         Point aOldPos = rInf.GetPos();
1171         Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
1172         while( i < nCount )
1173         {
1174             if( i == nTop ) // change the row
1175                 aOutPos.Y() = aOldPos.Y() + nLowPos;    // Y of the second row
1176             aOutPos.X() = aOldPos.X() + aPos[i];        // X position
1177             const sal_uInt8 nAct = aScrType[i];             // script type
1178             aTmpFont.SetActual( nAct );
1179             // if there're more than 4 characters to display, we choose fonts
1180             // with 2/3 of the original font width.
1181             if( aWidth[ nAct ] )
1182             {
1183                 Size aTmpSz = aTmpFont.GetSize( nAct );
1184                 if( aTmpSz.Width() != aWidth[ nAct ] )
1185                 {
1186                     aTmpSz.Width() = aWidth[ nAct ];
1187                     aTmpFont.SetSize( aTmpSz, nAct );
1188                 }
1189             }
1190             ((SwTxtPaintInfo&)rInf).SetPos( aOutPos );
1191             rInf.DrawText( aExpand, *this, i, 1 );
1192             ++i;
1193         }
1194         // rInf is const, so we have to take back our manipulations
1195         ((SwTxtPaintInfo&)rInf).SetPos( aOldPos );
1196     }
1197 }
1198 
1199 /*************************************************************************
1200  * SwCombinedPortion::Format(..)
1201  *************************************************************************/
1202 
1203 sal_Bool SwCombinedPortion::Format( SwTxtFormatInfo &rInf )
1204 {
1205     sal_uInt16 nCount = aExpand.Len();
1206     if( !nCount )
1207     {
1208         Width( 0 );
1209         return sal_False;
1210     }
1211 
1212     ASSERT( nCount < 7, "Too much combined characters" );
1213     // If there are leading "weak"-scripttyped characters in this portion,
1214     // they get the actual scripttype.
1215     sal_uInt16 i = 0;
1216     while( i < nCount && SW_SCRIPTS == aScrType[i] )
1217         aScrType[i++] = rInf.GetFont()->GetActual();
1218     if( nCount > 4 )
1219     {
1220         // more than four? Ok, then we need the 2/3 font width
1221         i = 0;
1222         while( i < aExpand.Len() )
1223         {
1224             ASSERT( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1225             if( !aWidth[ aScrType[i] ] )
1226             {
1227                 rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
1228                 aWidth[ aScrType[i] ] =
1229                         static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3);
1230             }
1231             ++i;
1232         }
1233     }
1234 
1235     sal_uInt16 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1236     ViewShell *pSh = rInf.GetTxtFrm()->getRootFrm()->GetCurrShell();
1237     SwFont aTmpFont( *rInf.GetFont() );
1238     SwFontSave aFontSave( rInf, &aTmpFont );
1239     nProportion = 55;
1240     // In nMainAscent/Descent we store the ascent and descent
1241     // of the original surrounding font
1242     sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1243     sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1244     const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1245     nMainDescent = nMainDescent - nMainAscent;
1246     // we start with a 50% font, but if we notice that the combined portion
1247     // becomes bigger than the surrounding font, we check 45% and maybe 40%.
1248     do
1249     {
1250         nProportion -= 5;
1251         aTmpFont.SetProportion( nProportion );
1252         i = 0;
1253         memset( &aPos, 0, sizeof(aPos) );
1254         nMaxDescent = 0;
1255         nMaxAscent = 0;
1256         nMaxWidth = 0;
1257         nUpPos = nLowPos = 0;
1258 
1259         // Now we get the width of all characters.
1260         // The ascent and the width of the first line are stored in the
1261         // ascent member of the portion, the descent in nLowPos.
1262         // The ascent, descent and width of the second line are stored in the
1263         // local nMaxAscent, nMaxDescent and nMaxWidth variables.
1264         while( i < nCount )
1265         {
1266             sal_uInt8 nScrp = aScrType[i];
1267             aTmpFont.SetActual( nScrp );
1268             if( aWidth[ nScrp ] )
1269             {
1270                 Size aFontSize( aTmpFont.GetSize( nScrp ) );
1271                 aFontSize.Width() = aWidth[ nScrp ];
1272                 aTmpFont.SetSize( aFontSize, nScrp );
1273             }
1274 
1275             SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 );
1276             Size aSize = aTmpFont._GetTxtSize( aDrawInf );
1277             sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1278             aPos[ i ] = (sal_uInt16)aSize.Width();
1279             if( i == nTop ) // enter the second line
1280             {
1281                 nLowPos = nMaxDescent;
1282                 Height( nMaxDescent + nMaxAscent );
1283                 Width( nMaxWidth );
1284                 SetAscent( nMaxAscent );
1285                 nMaxAscent = 0;
1286                 nMaxDescent = 0;
1287                 nMaxWidth = 0;
1288             }
1289             nMaxWidth = nMaxWidth + aPos[ i++ ];
1290             if( nAsc > nMaxAscent )
1291                 nMaxAscent = nAsc;
1292             if( aSize.Height() - nAsc > nMaxDescent )
1293                 nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
1294         }
1295         // for one or two characters we double the width of the portion
1296         if( nCount < 3 )
1297         {
1298             nMaxWidth *= 2;
1299             Width( 2*Width() );
1300             if( nCount < 2 )
1301             {
1302                 Height( nMaxAscent + nMaxDescent );
1303                 nLowPos = nMaxDescent;
1304             }
1305         }
1306         Height( Height() + nMaxDescent + nMaxAscent );
1307         nUpPos = nMaxAscent;
1308         SetAscent( Height() - nMaxDescent - nLowPos );
1309     } while( nProportion > 40 && ( GetAscent() > nMainAscent ||
1310                                     Height() - GetAscent() > nMainDescent ) );
1311     // if the combined portion is smaller than the surrounding text,
1312     // the portion grows. This looks better, if there's a character background.
1313     if( GetAscent() < nMainAscent )
1314     {
1315         Height( Height() + nMainAscent - GetAscent() );
1316         SetAscent( nMainAscent );
1317     }
1318     if( Height() < nMainAscent + nMainDescent )
1319         Height( nMainAscent + nMainDescent );
1320 
1321     // We calculate the x positions of the characters in both lines..
1322     sal_uInt16 nTopDiff = 0;
1323     sal_uInt16 nBotDiff = 0;
1324     if( nMaxWidth > Width() )
1325     {
1326         nTopDiff = ( nMaxWidth - Width() ) / 2;
1327         Width( nMaxWidth );
1328     }
1329     else
1330         nBotDiff = ( Width() - nMaxWidth ) / 2;
1331     switch( nTop)
1332     {
1333         case 3: aPos[1] = aPos[0] + nTopDiff;  // no break
1334         case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
1335     }
1336     aPos[0] = 0;
1337     switch( nCount )
1338     {
1339         case 5: aPos[4] = aPos[3] + nBotDiff;   // no break
1340         case 3: aPos[nTop] = nBotDiff;          break;
1341         case 6: aPos[4] = aPos[3] + nBotDiff;   // no break
1342         case 4: aPos[nTop] = 0;                 // no break
1343         case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
1344     }
1345 
1346     // Does the combined portion fit the line?
1347     const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
1348     if( bFull )
1349     {
1350         if( rInf.GetLineStart() == rInf.GetIdx() && (!rInf.GetLast()->InFldGrp()
1351             || !((SwFldPortion*)rInf.GetLast())->IsFollow() ) )
1352             Width( (sal_uInt16)( rInf.Width() - rInf.X() ) );
1353         else
1354         {
1355             Truncate();
1356             Width( 0 );
1357             SetLen( 0 );
1358             if( rInf.GetLast() )
1359                 rInf.GetLast()->FormatEOL( rInf );
1360         }
1361     }
1362     return bFull;
1363 }
1364 
1365 /*************************************************************************
1366  * SwCombinedPortion::GetViewWidth(..)
1367  *************************************************************************/
1368 
1369 KSHORT SwCombinedPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
1370 {
1371     if( !GetLen() ) // for the dummy part at the end of the line, where
1372         return 0;   // the combined portion doesn't fit.
1373     return SwFldPortion::GetViewWidth( rInf );
1374 }
1375