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