xref: /AOO41X/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