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