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