xref: /AOO41X/main/sw/source/core/text/porfld.cxx (revision 0aabba3ab117bd1ebf1560f23f52dfa1a54fd4c7)
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     // --> OD 2009-11-25 #i107143#
72     // pass placeholder property to created <SwFldPortion> instance.
73     SwFldPortion* pClone = new SwFldPortion( rExpand, pNewFnt, bPlaceHolder );
74     // <--
75     pClone->SetNextOffset( nNextOffset );
76     pClone->m_bNoLength = this->m_bNoLength;
77     return pClone;
78 }
79 
80 void SwFldPortion::TakeNextOffset( const SwFldPortion* pFld )
81 {
82 	ASSERT( pFld, "TakeNextOffset: Missing Source" );
83 	nNextOffset = pFld->GetNextOffset();
84 	aExpand.Erase( 0, nNextOffset );
85 	bFollow = sal_True;
86 }
87 
88 SwFldPortion::SwFldPortion( const XubString &rExpand, SwFont *pFont, sal_Bool bPlaceHold )
89     : aExpand(rExpand), pFnt(pFont), nNextOffset(0), nNextScriptChg(STRING_LEN), nViewWidth(0),
90       bFollow( sal_False ), bHasFollow( sal_False ), bPlaceHolder( bPlaceHold )
91     , m_bNoLength( sal_False )
92 {
93     SetWhichPor( POR_FLD );
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                 // --> FME 2006-01-11 #i59759# Erase additional control
398                 // characters from field string, otherwise we get stuck in
399                 // a loop.
400                 case CHAR_ZWSP :
401                 case CHAR_ZWNBSP :
402         //        case CHAR_RLM :
403         //        case CHAR_LRM :
404                 // <--
405                 // --> OD 2010-06-03 #i111750#
406                 // - Erasing further control characters from field string in
407                 // to avoid loop.
408                 case CH_TXTATR_BREAKWORD:
409                 case CH_TXTATR_INWORD:
410                 // <--
411 				{
412 					aNew.Erase( 0, 1 );
413 					++nNextOfst;
414 					break;
415 				}
416                 default: ;
417 			}
418 
419             // Even if there is no more text left for a follow field,
420             // we have to build a follow field portion (without font),
421             // otherwise the HookChar mechanism would not work.
422             SwFldPortion *pFld = Clone( aNew );
423 			if( aNew.Len() && !pFld->GetFont() )
424 			{
425 				SwFont *pNewFnt = new SwFont( *rInf.GetFont() );
426 				pFld->SetFont( pNewFnt );
427 			}
428 			pFld->SetFollow( sal_True );
429 			SetHasFollow( sal_True );
430 			// In nNextOffset steht bei einem neuangelegten Feld zunaechst
431 			// der Offset, an dem es selbst im Originalstring beginnt.
432 			// Wenn beim Formatieren ein FollowFeld angelegt wird, wird
433 			// der Offset dieses FollowFelds in nNextOffset festgehalten.
434 			nNextOffset = nNextOffset + nNextOfst;
435 			pFld->SetNextOffset( nNextOffset );
436 			rInf.SetRest( pFld );
437 		}
438 	}
439 
440 	if( bEOL && rInf.GetLast() && !rInf.GetUnderFlow() )
441 		rInf.GetLast()->FormatEOL( rInf );
442 	return bFull;
443 }
444 
445 /*************************************************************************
446  *               virtual SwFldPortion::Paint()
447  *************************************************************************/
448 
449 void SwFldPortion::Paint( const SwTxtPaintInfo &rInf ) const
450 {
451 	SwFontSave aSave( rInf, pFnt );
452 
453 	ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
454     if( Width() && ( !bPlaceHolder || rInf.GetOpt().IsShowPlaceHolderFields() ) )
455 	{
456 		// Dies ist eine freizuegige Auslegung der Hintergrundbelegung ...
457 		rInf.DrawViewOpt( *this, POR_FLD );
458 		SwExpandPortion::Paint( rInf );
459 	}
460 }
461 
462 /*************************************************************************
463  *              virtual SwFldPortion::GetExpTxt()
464  *************************************************************************/
465 
466 sal_Bool SwFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
467 {
468 	rTxt = aExpand;
469     if( !rTxt.Len() && rInf.OnWin() &&
470         !rInf.GetOpt().IsPagePreview() && !rInf.GetOpt().IsReadonly() &&
471             SwViewOption::IsFieldShadings() &&
472             !HasFollow() )
473 		rTxt = ' ';
474 	return sal_True;
475 }
476 
477 /*************************************************************************
478  *              virtual SwFldPortion::HandlePortion()
479  *************************************************************************/
480 
481 void SwFldPortion::HandlePortion( SwPortionHandler& rPH ) const
482 {
483     rPH.Special( GetLen(), aExpand, GetWhichPor() );
484 }
485 
486 /*************************************************************************
487  *                virtual SwFldPortion::GetTxtSize()
488  *************************************************************************/
489 
490 SwPosSize SwFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const
491 {
492 	SwFontSave aSave( rInf, pFnt );
493 	SwPosSize aSize( SwExpandPortion::GetTxtSize( rInf ) );
494 	return aSize;
495 }
496 
497 /*************************************************************************
498  *                      class SwHiddenPortion
499  *************************************************************************/
500 
501 SwFldPortion *SwHiddenPortion::Clone(const XubString &rExpand ) const
502 {
503 	SwFont *pNewFnt;
504 	if( 0 != ( pNewFnt = pFnt ) )
505 		pNewFnt = new SwFont( *pFnt );
506 	return new SwHiddenPortion( rExpand, pNewFnt );
507 }
508 
509 /*************************************************************************
510  *               virtual SwHiddenPortion::Paint()
511  *************************************************************************/
512 
513 void SwHiddenPortion::Paint( const SwTxtPaintInfo &rInf ) const
514 {
515 	if( Width() )
516 	{
517 		SwFontSave aSave( rInf, pFnt );
518 		rInf.DrawViewOpt( *this, POR_HIDDEN );
519 		SwExpandPortion::Paint( rInf );
520 	}
521 }
522 
523 /*************************************************************************
524  *              virtual SwHiddenPortion::GetExpTxt()
525  *************************************************************************/
526 
527 sal_Bool SwHiddenPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const
528 {
529 	// Nicht auf IsHidden() abfragen !
530 	return SwFldPortion::GetExpTxt( rInf, rTxt );
531 }
532 
533 /*************************************************************************
534  *                      class SwNumberPortion
535  *************************************************************************/
536 
537 // --> OD 2008-01-23 #newlistlevelattrs#
538 SwNumberPortion::SwNumberPortion( const XubString &rExpand,
539                                   SwFont *pFont,
540                                   const sal_Bool bLft,
541                                   const sal_Bool bCntr,
542                                   const KSHORT nMinDst,
543                                   const bool bLabelAlignmentPosAndSpaceModeActive )
544         : SwFldPortion( rExpand, pFont ),
545           nFixWidth(0),
546           nMinDist( nMinDst ),
547           // --> OD 2008-01-23 #newlistlevelattrs#
548           mbLabelAlignmentPosAndSpaceModeActive( bLabelAlignmentPosAndSpaceModeActive )
549           // <--
550 {
551 	SetWhichPor( POR_NUMBER );
552 	SetLeft( bLft );
553 	SetHide( sal_False );
554 	SetCenter( bCntr );
555 }
556 
557 xub_StrLen SwNumberPortion::GetCrsrOfst( const MSHORT ) const
558 {
559 	return 0;
560 }
561 
562 SwFldPortion *SwNumberPortion::Clone( const XubString &rExpand ) const
563 {
564 	SwFont *pNewFnt;
565 	if( 0 != ( pNewFnt = pFnt ) )
566 		pNewFnt = new SwFont( *pFnt );
567     // --> OD 2008-01-23 #newlistlevelattrs#
568 	return new SwNumberPortion( rExpand, pNewFnt, IsLeft(), IsCenter(),
569                                 nMinDist, mbLabelAlignmentPosAndSpaceModeActive );
570     // <--
571 }
572 
573 /*************************************************************************
574  *                 virtual SwNumberPortion::Format()
575  *************************************************************************/
576 
577 // 5010: Wir sind in der Lage, mehrzeilige NumFelder anzulegen!
578 // 3689: Fies ist, wenn man in der Dialogbox soviel Davor-Text
579 // eingibt, bis die Zeile ueberlaeuft.
580 // Man muss die Fly-Ausweichmanoever beachten!
581 
582 sal_Bool SwNumberPortion::Format( SwTxtFormatInfo &rInf )
583 {
584 	SetHide( sal_False );
585 	const sal_Bool bFull = SwFldPortion::Format( rInf );
586 	SetLen( 0 );
587     // a numbering portion can be contained in a rotated portion!!!
588     nFixWidth = rInf.IsMulti() ? Height() : Width();
589 	rInf.SetNumDone( !rInf.GetRest() );
590 	if( rInf.IsNumDone() )
591 	{
592 //        SetAscent( rInf.GetAscent() );
593         ASSERT( Height() && nAscent, "NumberPortions without Height | Ascent" );
594 
595         long nDiff( 0 );
596         // --> OD 2008-01-23 #newlistlevelattrs#
597         if ( !mbLabelAlignmentPosAndSpaceModeActive )
598         {
599             if ( !rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_FIRST_LINE_INDENT_IN_NUMBERING) &&
600                  // --> FME 2004-08-13 #i32902#
601                  !IsFtnNumPortion() )
602                  // <--
603             {
604                 nDiff = rInf.Left()
605                     + rInf.GetTxtFrm()->GetTxtNode()->
606                     GetSwAttrSet().GetLRSpace().GetTxtFirstLineOfst()
607                     - rInf.First()
608                     + rInf.ForcedLeftMargin();
609             }
610             else
611             {
612                 nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
613             }
614         }
615         // <--
616         // Ein Vorschlag von Juergen und Volkmar:
617         // Der Textteil hinter der Numerierung sollte immer
618         // mindestens beim linken Rand beginnen.
619         if( nDiff < 0 )
620             nDiff = 0;
621         else if ( nDiff > rInf.X() )
622             nDiff -= rInf.X();
623         else
624             nDiff = 0;
625 
626 		if( nDiff < nFixWidth + nMinDist )
627 			nDiff = nFixWidth + nMinDist;
628 		// 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
629 		// fieser Sonderfall: FlyFrm liegt in dem Bereich,
630 		// den wir uns gerade unter den Nagel reissen wollen.
631 		// Die NumberPortion wird als verborgen markiert.
632         const sal_Bool bFly = rInf.GetFly() ||
633             ( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
634         if( nDiff > rInf.Width() )
635 		{
636 			nDiff = rInf.Width();
637             if ( bFly )
638                 SetHide( sal_True );
639 		}
640 
641         // A numbering portion can be inside a SwRotatedPortion. Then the
642         // Height has to be changed
643         if ( rInf.IsMulti() )
644         {
645             if ( Height() < nDiff )
646                 Height( KSHORT( nDiff ) );
647         }
648         else if( Width() < nDiff )
649             Width( KSHORT(nDiff) );
650 	}
651 	return bFull;
652 }
653 
654 void SwNumberPortion::FormatEOL( SwTxtFormatInfo& )
655 {
656 /*	Ein FormatEOL deutet daraufhin, dass der folgende Text
657  *	nicht mit auf die Zeile passte. Damit die Numerierung mitwandert,
658  *  wird diese NumberPortion verborgen.
659  */
660 
661     // This caused trouble with flys anchored as characters.
662     // If one of these is numbered but does not fit to the line,
663     // it calls this function, causing a loop because both the number
664     // portion and the fly portion go to the next line
665 //    SetHide( sal_True );
666 }
667 
668 /*************************************************************************
669  *               virtual SwNumberPortion::Paint()
670  *************************************************************************/
671 
672 void SwNumberPortion::Paint( const SwTxtPaintInfo &rInf ) const
673 {
674 /*	Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
675  * 	Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
676  */
677 
678 	if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
679 	{
680 		SwLinePortion *pTmp = GetPortion();
681 		while ( pTmp && !pTmp->InTxtGrp() )
682 			pTmp = pTmp->GetPortion();
683 		if ( !pTmp )
684 			return;
685 	}
686 
687     // calculate the width of the number portion, including follows
688     const KSHORT nOldWidth = Width();
689     sal_uInt16 nSumWidth = 0;
690     sal_uInt16 nOffset = 0;
691 
692     const SwLinePortion* pTmp = this;
693     while ( pTmp && pTmp->InNumberGrp() )
694     {
695         nSumWidth = nSumWidth + pTmp->Width();
696         if ( ((SwNumberPortion*)pTmp)->HasFollow() )
697             pTmp = pTmp->GetPortion();
698         else
699         {
700             nOffset = pTmp->Width() - ((SwNumberPortion*)pTmp)->nFixWidth;
701             break;
702         }
703     }
704 
705     // The master portion takes care for painting the background of the
706     // follow field portions
707     if ( ! IsFollow() )
708     {
709         SwLinePortion *pThis = (SwLinePortion*)this;
710         pThis->Width( nSumWidth );
711         rInf.DrawViewOpt( *this, POR_NUMBER );
712         pThis->Width( nOldWidth );
713     }
714 
715 	if( aExpand.Len() )
716 	{
717 		const SwFont *pTmpFnt = rInf.GetFont();
718 		sal_Bool bPaintSpace = ( UNDERLINE_NONE != pTmpFnt->GetUnderline() ||
719 							     UNDERLINE_NONE != pTmpFnt->GetOverline()  ||
720 							     STRIKEOUT_NONE != pTmpFnt->GetStrikeout() ) &&
721 							     !pTmpFnt->IsWordLineMode();
722 		if( bPaintSpace && pFnt )
723 			bPaintSpace = ( UNDERLINE_NONE != pFnt->GetUnderline() ||
724 							UNDERLINE_NONE != pFnt->GetOverline()  ||
725 							STRIKEOUT_NONE != pFnt->GetStrikeout() ) &&
726 							!pFnt->IsWordLineMode();
727 
728 		SwFontSave aSave( rInf, pFnt );
729 
730         if( nFixWidth == Width() && ! HasFollow() )
731 			SwExpandPortion::Paint( rInf );
732 		else
733 		{
734 			// logisches const: Width wird wieder zurueckgesetzt
735 			SwLinePortion *pThis = (SwLinePortion*)this;
736 			bPaintSpace = bPaintSpace && nFixWidth < nOldWidth;
737 			KSHORT nSpaceOffs = nFixWidth;
738 			pThis->Width( nFixWidth );
739 
740 			if( ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
741                 ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() ) )
742 				SwExpandPortion::Paint( rInf );
743 			else
744 			{
745 				SwTxtPaintInfo aInf( rInf );
746 				if( nOffset < nMinDist )
747 					nOffset = 0;
748 				else
749 				{
750 					if( IsCenter() )
751 					{
752                         /* #110778# a / 2 * 2 == a is not a tautology */
753                         KSHORT nTmpOffset = nOffset;
754 						nOffset /= 2;
755 						if( nOffset < nMinDist )
756 							nOffset = nTmpOffset - nMinDist;
757 					}
758 					else
759 						nOffset = nOffset - nMinDist;
760 				}
761 				aInf.X( aInf.X() + nOffset );
762 				SwExpandPortion::Paint( aInf );
763 				if( bPaintSpace )
764 					nSpaceOffs = nSpaceOffs + nOffset;
765 			}
766 			if( bPaintSpace && nOldWidth > nSpaceOffs )
767 			{
768 				SwTxtPaintInfo aInf( rInf );
769 static sal_Char __READONLY_DATA sDoubleSpace[] = "  ";
770 				aInf.X( aInf.X() + nSpaceOffs );
771 
772                 // --> FME 2005-08-12 #i53199# Adjust position of underline:
773                 if ( rInf.GetUnderFnt() )
774                 {
775                     const Point aNewPos( aInf.GetPos().X(), rInf.GetUnderFnt()->GetPos().Y() );
776                     rInf.GetUnderFnt()->SetPos( aNewPos );
777                 }
778                 // <--
779 
780 				pThis->Width( nOldWidth - nSpaceOffs + 12 );
781 				{
782                     SwTxtSlot aDiffTxt( &aInf, this, true, false, sDoubleSpace );
783 					aInf.DrawText( *this, aInf.GetLen(), sal_True );
784 				}
785 			}
786 			pThis->Width( nOldWidth );
787 		}
788 	}
789 }
790 
791 
792 /*************************************************************************
793  *                      class SwBulletPortion
794  *************************************************************************/
795 
796 // --> OD 2008-01-23 #newlistlevelattrs#
797 SwBulletPortion::SwBulletPortion( const xub_Unicode cBullet,
798                                   const XubString& rBulletFollowedBy,
799                                   SwFont *pFont,
800                                   const sal_Bool bLft,
801                                   const sal_Bool bCntr,
802                                   const KSHORT nMinDst,
803                                   const bool bLabelAlignmentPosAndSpaceModeActive )
804     : SwNumberPortion( XubString( rBulletFollowedBy ).Insert( cBullet, 0 ) ,
805                        pFont, bLft, bCntr, nMinDst,
806                        bLabelAlignmentPosAndSpaceModeActive )
807 // <--
808 {
809 	SetWhichPor( POR_BULLET );
810 }
811 
812 /*************************************************************************
813  *                      class SwGrfNumPortion
814  *************************************************************************/
815 
816 #define GRFNUM_SECURE 10
817 
818 // --> OD 2008-01-23 #newlistlevelattrs#
819 SwGrfNumPortion::SwGrfNumPortion(
820 		SwFrm*,
821         const XubString& rGraphicFollowedBy,
822 		const SvxBrushItem* pGrfBrush,
823 		const SwFmtVertOrient* pGrfOrient, const Size& rGrfSize,
824         const sal_Bool bLft, const sal_Bool bCntr, const KSHORT nMinDst,
825         const bool bLabelAlignmentPosAndSpaceModeActive ) :
826     SwNumberPortion( rGraphicFollowedBy, NULL, bLft, bCntr, nMinDst,
827                      bLabelAlignmentPosAndSpaceModeActive ),
828 // <--
829     pBrush( new SvxBrushItem(RES_BACKGROUND) ), nId( 0 )
830 {
831 	SetWhichPor( POR_GRFNUM );
832 	SetAnimated( sal_False );
833 	bReplace = sal_False;
834 	if( pGrfBrush )
835 	{
836 		*pBrush = *pGrfBrush;
837 		const Graphic* pGraph = pGrfBrush->GetGraphic();
838 		if( pGraph )
839 			SetAnimated( pGraph->IsAnimated() );
840 		else
841 			bReplace = sal_True;
842 	}
843 	if( pGrfOrient )
844 	{
845 		nYPos = pGrfOrient->GetPos();
846 		eOrient = pGrfOrient->GetVertOrient();
847 	}
848 	else
849 	{
850 		nYPos = 0;
851         eOrient = text::VertOrientation::TOP;
852 	}
853     Width( static_cast<sal_uInt16>(rGrfSize.Width() + 2 * GRFNUM_SECURE) );
854 	nFixWidth = Width();
855 	nGrfHeight = rGrfSize.Height() + 2 * GRFNUM_SECURE;
856 	Height( KSHORT(nGrfHeight) );
857 	bNoPaint = sal_False;
858 }
859 
860 SwGrfNumPortion::~SwGrfNumPortion()
861 {
862 	if ( IsAnimated() )
863 		( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
864 	delete pBrush;
865 }
866 
867 void SwGrfNumPortion::StopAnimation( OutputDevice* pOut )
868 {
869 	if ( IsAnimated() )
870 		( (Graphic*) pBrush->GetGraphic() )->StopAnimation( pOut, nId );
871 }
872 
873 sal_Bool SwGrfNumPortion::Format( SwTxtFormatInfo &rInf )
874 {
875 	SetHide( sal_False );
876     // --> OD 2008-01-29 #newlistlevelattrs#
877 //    Width( nFixWidth );
878     KSHORT nFollowedByWidth( 0 );
879     if ( mbLabelAlignmentPosAndSpaceModeActive )
880     {
881         SwFldPortion::Format( rInf );
882         nFollowedByWidth = Width();
883         SetLen( 0 );
884     }
885     Width( nFixWidth + nFollowedByWidth );
886     // <--
887 	const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
888 	const sal_Bool bFly = rInf.GetFly() ||
889 		( rInf.GetLast() && rInf.GetLast()->IsFlyPortion() );
890     SetAscent( static_cast<sal_uInt16>(GetRelPos() > 0 ? GetRelPos() : 0) );
891 	if( GetAscent() > Height() )
892 		Height( GetAscent() );
893 
894 	if( bFull )
895 	{
896 		Width( rInf.Width() - (KSHORT)rInf.X() );
897 		if( bFly )
898 		{
899 			SetLen( 0 );
900 			SetNoPaint( sal_True );
901 			rInf.SetNumDone( sal_False );
902 			return sal_True;
903 		}
904 	}
905 	rInf.SetNumDone( sal_True );
906     // --> OD 2008-01-23 #newlistlevelattrs#
907 //    long nDiff = rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
908     long nDiff = mbLabelAlignmentPosAndSpaceModeActive
909                  ? 0
910                  : rInf.Left() - rInf.First() + rInf.ForcedLeftMargin();
911     // <--
912 	// Ein Vorschlag von Juergen und Volkmar:
913 	// Der Textteil hinter der Numerierung sollte immer
914 	// mindestens beim linken Rand beginnen.
915 	if( nDiff < 0 )
916 		nDiff = 0;
917 	else if ( nDiff > rInf.X() )
918 		nDiff -= rInf.X();
919 	if( nDiff < nFixWidth + nMinDist )
920 		nDiff = nFixWidth + nMinDist;
921 	// 2739: Numerierung weicht Fly aus, kein nDiff in der zweiten Runde
922 	// fieser Sonderfall: FlyFrm liegt in dem Bereich,
923 	// den wir uns gerade unter den Nagel reissen wollen.
924 	// Die NumberPortion wird als verborgen markiert.
925 	if( nDiff > rInf.Width() )
926 	{
927 		nDiff = rInf.Width();
928 		if( bFly )
929 			SetHide( sal_True );
930 	}
931 
932 	if( Width() < nDiff )
933 		Width( KSHORT(nDiff) );
934 	return bFull;
935 }
936 
937 void SwGrfNumPortion::Paint( const SwTxtPaintInfo &rInf ) const
938 {
939 	if( DontPaint() )
940 		return;
941 /*	Eine verborgene NumberPortion wird nicht angezeigt, es sei denn, es gibt
942  * 	Textportions in dieser Zeile oder es gibt ueberhaupt nur eine einzige Zeile.
943  */
944 	if ( IsHide() && rInf.GetParaPortion() && rInf.GetParaPortion()->GetNext() )
945 	{
946 		SwLinePortion *pTmp = GetPortion();
947 		while ( pTmp && !pTmp->InTxtGrp() )
948 			pTmp = pTmp->GetPortion();
949 		if ( !pTmp )
950 			return;
951 	}
952 	Point aPos( rInf.X() + GRFNUM_SECURE, rInf.Y() - GetRelPos() + GRFNUM_SECURE );
953 	long nTmpWidth = Max( (long)0, (long)(nFixWidth - 2 * GRFNUM_SECURE) );
954 	Size aSize( nTmpWidth, GetGrfHeight() - 2 * GRFNUM_SECURE );
955 
956     // --> OD 2008-02-05 #newlistlevelattrs#
957     const sal_Bool bTmpLeft = mbLabelAlignmentPosAndSpaceModeActive ||
958                               ( IsLeft() && ! rInf.GetTxtFrm()->IsRightToLeft() ) ||
959                               ( ! IsLeft() && ! IsCenter() && rInf.GetTxtFrm()->IsRightToLeft() );
960     // <--
961 
962     if( nFixWidth < Width() && !bTmpLeft )
963 	{
964 		KSHORT nOffset = Width() - nFixWidth;
965 		if( nOffset < nMinDist )
966 			nOffset = 0;
967 		else
968 		{
969 			if( IsCenter() )
970 			{
971 				nOffset /= 2;
972 				if( nOffset < nMinDist )
973 					nOffset = Width() - nFixWidth - nMinDist;
974 			}
975 			else
976 				nOffset = nOffset - nMinDist;
977 		}
978 		aPos.X() += nOffset;
979 	}
980 
981 	if( bReplace )
982 	{
983 		KSHORT nTmpH = GetPortion() ? GetPortion()->GetAscent() : 120;
984 		aSize = Size( nTmpH, nTmpH );
985 		aPos.Y() = rInf.Y() - nTmpH;
986 	}
987 	SwRect aTmp( aPos, aSize );
988 
989 	sal_Bool bDraw = sal_True;
990 
991 	if ( IsAnimated() )
992 	{
993 		bDraw = !rInf.GetOpt().IsGraphic();
994 		if( !nId )
995 		{
996 			SetId( long( rInf.GetTxtFrm() ) );
997 			rInf.GetTxtFrm()->SetAnimation();
998 		}
999 		if( aTmp.IsOver( rInf.GetPaintRect() ) && !bDraw )
1000 		{
1001 			rInf.NoteAnimation();
1002             const ViewShell* pViewShell = rInf.GetVsh();
1003 
1004             // virtual device, not pdf export
1005             if( OUTDEV_VIRDEV == rInf.GetOut()->GetOutDevType() &&
1006                 pViewShell && pViewShell->GetWin()  )
1007             {
1008 				( (Graphic*) pBrush->GetGraphic() )->StopAnimation(0,nId);
1009 				rInf.GetTxtFrm()->getRootFrm()->GetCurrShell()->InvalidateWindows( aTmp );
1010 			}
1011 
1012 
1013             else if ( pViewShell &&
1014                      !pViewShell->GetAccessibilityOptions()->IsStopAnimatedGraphics() &&
1015                      !pViewShell->IsPreView() &&
1016                       // --> FME 2004-06-21 #i9684# Stop animation during printing/pdf export.
1017                       pViewShell->GetWin() )
1018                       // <--
1019             {
1020 				( (Graphic*) pBrush->GetGraphic() )->StartAnimation(
1021 					(OutputDevice*)rInf.GetOut(), aPos, aSize, nId );
1022             }
1023 
1024             // pdf export, printing, preview, stop animations...
1025             else
1026                 bDraw = sal_True;
1027 		}
1028 		if( bDraw )
1029 			( (Graphic*) pBrush->GetGraphic() )->StopAnimation( 0, nId );
1030 	}
1031 
1032     SwRect aRepaint( rInf.GetPaintRect() );
1033 	const SwTxtFrm& rFrm = *rInf.GetTxtFrm();
1034     if( rFrm.IsVertical() )
1035     {
1036         rFrm.SwitchHorizontalToVertical( aTmp );
1037         rFrm.SwitchHorizontalToVertical( aRepaint );
1038     }
1039 
1040     if( rFrm.IsRightToLeft() )
1041     {
1042         rFrm.SwitchLTRtoRTL( aTmp );
1043         rFrm.SwitchLTRtoRTL( aRepaint );
1044     }
1045 
1046 	if( bDraw && aTmp.HasArea() )
1047 		DrawGraphic( pBrush, (OutputDevice*)rInf.GetOut(),
1048             aTmp, aRepaint, bReplace ? GRFNUM_REPLACE : GRFNUM_YES );
1049 }
1050 
1051 void SwGrfNumPortion::SetBase( long nLnAscent, long nLnDescent,
1052 							   long nFlyAsc, long nFlyDesc )
1053 {
1054     if ( GetOrient() != text::VertOrientation::NONE )
1055 	{
1056 		SetRelPos( 0 );
1057         if ( GetOrient() == text::VertOrientation::CENTER )
1058 			SetRelPos( GetGrfHeight() / 2 );
1059         else if ( GetOrient() == text::VertOrientation::TOP )
1060 			SetRelPos( GetGrfHeight() - GRFNUM_SECURE );
1061         else if ( GetOrient() == text::VertOrientation::BOTTOM )
1062 			;
1063         else if ( GetOrient() == text::VertOrientation::CHAR_CENTER )
1064 			SetRelPos( ( GetGrfHeight() + nLnAscent - nLnDescent ) / 2 );
1065         else if ( GetOrient() == text::VertOrientation::CHAR_TOP )
1066 			SetRelPos( nLnAscent );
1067         else if ( GetOrient() == text::VertOrientation::CHAR_BOTTOM )
1068 			SetRelPos( GetGrfHeight() - nLnDescent );
1069 		else
1070 		{
1071 			if( GetGrfHeight() >= nFlyAsc + nFlyDesc )
1072 			{
1073 				// wenn ich genauso gross bin wie die Zeile, brauche ich mich
1074 				// nicht an der Zeile nicht weiter ausrichten, ich lasse
1075 				// dann auch den max. Ascent der Zeile unveraendert
1076 
1077 				SetRelPos( nFlyAsc );
1078 			}
1079             else if ( GetOrient() == text::VertOrientation::LINE_CENTER )
1080 				SetRelPos( ( GetGrfHeight() + nFlyAsc - nFlyDesc ) / 2 );
1081             else if ( GetOrient() == text::VertOrientation::LINE_TOP )
1082 				SetRelPos( nFlyAsc );
1083             else if ( GetOrient() == text::VertOrientation::LINE_BOTTOM )
1084 				SetRelPos( GetGrfHeight() - nFlyDesc );
1085 		}
1086 	}
1087 }
1088 
1089 void SwTxtFrm::StopAnimation( OutputDevice* pOut )
1090 {
1091 	ASSERT( HasAnimation(), "SwTxtFrm::StopAnimation: Which Animation?" );
1092 	if( HasPara() )
1093 	{
1094 		SwLineLayout *pLine = GetPara();
1095 		while( pLine )
1096 		{
1097 			SwLinePortion *pPor = pLine->GetPortion();
1098 			while( pPor )
1099 			{
1100 				if( pPor->IsGrfNumPortion() )
1101 					((SwGrfNumPortion*)pPor)->StopAnimation( pOut );
1102 				// Die Numerierungsportion sitzt immer vor dem ersten Zeichen,
1103 				// deshalb koennen wir abbrechen, sobald wir eine Portion mit
1104 				// einer Laenge > 0 erreicht haben.
1105 				pPor = pPor->GetLen() ? 0 : pPor->GetPortion();
1106 			}
1107 			pLine = pLine->GetLen() ? 0 : pLine->GetNext();
1108 		}
1109 	}
1110 }
1111 
1112 /*************************************************************************
1113  * SwCombinedPortion::SwCombinedPortion(..)
1114  * initializes the script array and clears the width array
1115  *************************************************************************/
1116 
1117 SwCombinedPortion::SwCombinedPortion( const XubString &rTxt )
1118 	 : SwFldPortion( rTxt )
1119 {
1120 	SetLen(1);
1121 	SetWhichPor( POR_COMBINED );
1122 	if( aExpand.Len() > 6 )
1123 		aExpand.Erase( 6 );
1124 	// Initialization of the scripttype array,
1125 	// the arrays of width and position are filled by the format function
1126 	if(	pBreakIt->GetBreakIter().is() )
1127 	{
1128 		sal_uInt8 nScr = SW_SCRIPTS;
1129 		for( sal_uInt16 i = 0; i < rTxt.Len(); ++i )
1130 		{
1131 			sal_uInt16 nScript = pBreakIt->GetBreakIter()->getScriptType( rTxt, i );
1132 			switch ( nScript ) {
1133 				case i18n::ScriptType::LATIN : nScr = SW_LATIN; break;
1134 				case i18n::ScriptType::ASIAN : nScr = SW_CJK; break;
1135 				case i18n::ScriptType::COMPLEX : nScr = SW_CTL; break;
1136 			}
1137 			aScrType[i] = nScr;
1138 		}
1139 	}
1140 	else
1141 	{
1142 		for( sal_uInt16 i = 0; i < 6; aScrType[i++] = 0 )
1143 			; // nothing
1144 	}
1145 	memset( &aWidth, 0, sizeof(aWidth) );
1146 }
1147 
1148 /*************************************************************************
1149  * SwCombinedPortion::Paint(..)
1150  *************************************************************************/
1151 
1152 void SwCombinedPortion::Paint( const SwTxtPaintInfo &rInf ) const
1153 {
1154 	ASSERT( GetLen() <= 1, "SwFldPortion::Paint: rest-portion polution?" );
1155 	if( Width() )
1156 	{
1157 		rInf.DrawBackBrush( *this );
1158 		rInf.DrawViewOpt( *this, POR_FLD );
1159 
1160         // do we have to repaint a post it portion?
1161         if( rInf.OnWin() && pPortion && !pPortion->Width() )
1162             pPortion->PrePaint( rInf, this );
1163 
1164 		sal_uInt16 nCount = aExpand.Len();
1165 		if( !nCount )
1166 			return;
1167 		ASSERT( nCount < 7, "Too much combined characters" );
1168 
1169 		// the first character of the second row
1170 		sal_uInt16 nTop = ( nCount + 1 ) / 2;
1171 
1172 		SwFont aTmpFont( *rInf.GetFont() );
1173 		aTmpFont.SetProportion( nProportion );	// a smaller font
1174 		SwFontSave aFontSave( rInf, &aTmpFont );
1175 
1176 		sal_uInt16 i = 0;
1177 		Point aOldPos = rInf.GetPos();
1178 		Point aOutPos( aOldPos.X(), aOldPos.Y() - nUpPos );// Y of the first row
1179 		while( i < nCount )
1180 		{
1181 			if( i == nTop ) // change the row
1182 				aOutPos.Y() = aOldPos.Y() + nLowPos;	// Y of the second row
1183 			aOutPos.X() = aOldPos.X() + aPos[i];		// X position
1184 			const sal_uInt8 nAct = aScrType[i];				// script type
1185 			aTmpFont.SetActual( nAct );
1186 			// if there're more than 4 characters to display, we choose fonts
1187 			// with 2/3 of the original font width.
1188 			if( aWidth[ nAct ] )
1189 			{
1190 				Size aTmpSz = aTmpFont.GetSize( nAct );
1191 				if( aTmpSz.Width() != aWidth[ nAct ] )
1192 				{
1193 					aTmpSz.Width() = aWidth[ nAct ];
1194 					aTmpFont.SetSize( aTmpSz, nAct );
1195 				}
1196 			}
1197 			((SwTxtPaintInfo&)rInf).SetPos( aOutPos );
1198 			rInf.DrawText( aExpand, *this, i, 1 );
1199 			++i;
1200 		}
1201 		// rInf is const, so we have to take back our manipulations
1202 		((SwTxtPaintInfo&)rInf).SetPos( aOldPos );
1203 	}
1204 }
1205 
1206 /*************************************************************************
1207  * SwCombinedPortion::Format(..)
1208  *************************************************************************/
1209 
1210 sal_Bool SwCombinedPortion::Format( SwTxtFormatInfo &rInf )
1211 {
1212 	sal_uInt16 nCount = aExpand.Len();
1213 	if( !nCount )
1214 	{
1215 		Width( 0 );
1216 		return sal_False;
1217 	}
1218 
1219 	ASSERT( nCount < 7, "Too much combined characters" );
1220 	// If there are leading "weak"-scripttyped characters in this portion,
1221 	// they get the actual scripttype.
1222 	sal_uInt16 i = 0;
1223 	while( i < nCount && SW_SCRIPTS == aScrType[i] )
1224 		aScrType[i++] = rInf.GetFont()->GetActual();
1225 	if( nCount > 4 )
1226 	{
1227 		// more than four? Ok, then we need the 2/3 font width
1228 		i = 0;
1229 		while( i < aExpand.Len() )
1230 		{
1231 			ASSERT( aScrType[i] < SW_SCRIPTS, "Combined: Script fault" );
1232 			if( !aWidth[ aScrType[i] ] )
1233 			{
1234 				rInf.GetOut()->SetFont( rInf.GetFont()->GetFnt( aScrType[i] ) );
1235                 aWidth[ aScrType[i] ] =
1236                         static_cast<sal_uInt16>(2 * rInf.GetOut()->GetFontMetric().GetSize().Width() / 3);
1237 			}
1238 			++i;
1239 		}
1240 	}
1241 
1242 	sal_uInt16 nTop = ( nCount + 1 ) / 2; // the first character of the second line
1243 	ViewShell *pSh = rInf.GetTxtFrm()->getRootFrm()->GetCurrShell();
1244 	SwFont aTmpFont( *rInf.GetFont() );
1245 	SwFontSave aFontSave( rInf, &aTmpFont );
1246 	nProportion = 55;
1247 	// In nMainAscent/Descent we store the ascent and descent
1248 	// of the original surrounding font
1249 	sal_uInt16 nMaxDescent, nMaxAscent, nMaxWidth;
1250     sal_uInt16 nMainDescent = rInf.GetFont()->GetHeight( pSh, *rInf.GetOut() );
1251     const sal_uInt16 nMainAscent = rInf.GetFont()->GetAscent( pSh, *rInf.GetOut() );
1252 	nMainDescent = nMainDescent - nMainAscent;
1253 	// we start with a 50% font, but if we notice that the combined portion
1254 	// becomes bigger than the surrounding font, we check 45% and maybe 40%.
1255 	do
1256 	{
1257 		nProportion -= 5;
1258 		aTmpFont.SetProportion( nProportion );
1259 		i = 0;
1260 		memset( &aPos, 0, sizeof(aPos) );
1261 		nMaxDescent = 0;
1262 		nMaxAscent = 0;
1263 		nMaxWidth = 0;
1264 		nUpPos = nLowPos = 0;
1265 
1266 		// Now we get the width of all characters.
1267 		// The ascent and the width of the first line are stored in the
1268 		// ascent member of the portion, the descent in nLowPos.
1269 		// The ascent, descent and width of the second line are stored in the
1270 		// local nMaxAscent, nMaxDescent and nMaxWidth variables.
1271 		while( i < nCount )
1272 		{
1273 			sal_uInt8 nScrp = aScrType[i];
1274 			aTmpFont.SetActual( nScrp );
1275 			if( aWidth[ nScrp ] )
1276 			{
1277 				Size aFontSize( aTmpFont.GetSize( nScrp ) );
1278 				aFontSize.Width() = aWidth[ nScrp ];
1279 				aTmpFont.SetSize( aFontSize, nScrp );
1280 			}
1281 
1282             SwDrawTextInfo aDrawInf( pSh, *rInf.GetOut(), 0, aExpand, i, 1 );
1283             Size aSize = aTmpFont._GetTxtSize( aDrawInf );
1284             sal_uInt16 nAsc = aTmpFont.GetAscent( pSh, *rInf.GetOut() );
1285             aPos[ i ] = (sal_uInt16)aSize.Width();
1286 			if( i == nTop ) // enter the second line
1287 			{
1288 				nLowPos = nMaxDescent;
1289 				Height( nMaxDescent + nMaxAscent );
1290 				Width( nMaxWidth );
1291 				SetAscent( nMaxAscent );
1292 				nMaxAscent = 0;
1293 				nMaxDescent = 0;
1294 				nMaxWidth = 0;
1295 			}
1296 			nMaxWidth = nMaxWidth + aPos[ i++ ];
1297 			if( nAsc > nMaxAscent )
1298 				nMaxAscent = nAsc;
1299 			if( aSize.Height() - nAsc > nMaxDescent )
1300                 nMaxDescent = static_cast<sal_uInt16>(aSize.Height() - nAsc);
1301 		}
1302 		// for one or two characters we double the width of the portion
1303 		if( nCount < 3 )
1304 		{
1305 			nMaxWidth *= 2;
1306 			Width( 2*Width() );
1307 			if( nCount < 2 )
1308 			{
1309 				Height( nMaxAscent + nMaxDescent );
1310 				nLowPos = nMaxDescent;
1311 			}
1312 		}
1313 		Height( Height() + nMaxDescent + nMaxAscent );
1314 		nUpPos = nMaxAscent;
1315 		SetAscent( Height() - nMaxDescent - nLowPos );
1316 	} while( nProportion > 40 && ( GetAscent() > nMainAscent ||
1317 									Height() - GetAscent() > nMainDescent ) );
1318 	// if the combined portion is smaller than the surrounding text,
1319 	// the portion grows. This looks better, if there's a character background.
1320 	if( GetAscent() < nMainAscent )
1321 	{
1322 		Height( Height() + nMainAscent - GetAscent() );
1323 		SetAscent( nMainAscent );
1324 	}
1325 	if( Height() < nMainAscent + nMainDescent )
1326 		Height( nMainAscent + nMainDescent );
1327 
1328 	// We calculate the x positions of the characters in both lines..
1329 	sal_uInt16 nTopDiff = 0;
1330 	sal_uInt16 nBotDiff = 0;
1331 	if( nMaxWidth > Width() )
1332 	{
1333 		nTopDiff = ( nMaxWidth - Width() ) / 2;
1334 		Width( nMaxWidth );
1335 	}
1336 	else
1337 		nBotDiff = ( Width() - nMaxWidth ) / 2;
1338 	switch( nTop)
1339 	{
1340 		case 3: aPos[1] = aPos[0] + nTopDiff;  // no break
1341 		case 2: aPos[nTop-1] = Width() - aPos[nTop-1];
1342 	}
1343 	aPos[0] = 0;
1344 	switch( nCount )
1345 	{
1346 		case 5: aPos[4] = aPos[3] + nBotDiff;	// no break
1347 		case 3: aPos[nTop] = nBotDiff;			break;
1348 		case 6: aPos[4] = aPos[3] + nBotDiff;	// no break
1349 		case 4: aPos[nTop] = 0;					// no break
1350 		case 2: aPos[nCount-1] = Width() - aPos[nCount-1];
1351 	}
1352 
1353 	// Does the combined portion fit the line?
1354 	const sal_Bool bFull = rInf.Width() < rInf.X() + Width();
1355 	if( bFull )
1356 	{
1357 		if( rInf.GetLineStart() == rInf.GetIdx() &&	(!rInf.GetLast()->InFldGrp()
1358 			|| !((SwFldPortion*)rInf.GetLast())->IsFollow() ) )
1359             Width( (sal_uInt16)( rInf.Width() - rInf.X() ) );
1360 		else
1361 		{
1362 			Truncate();
1363 			Width( 0 );
1364 			SetLen( 0 );
1365 			if( rInf.GetLast() )
1366 				rInf.GetLast()->FormatEOL( rInf );
1367 		}
1368 	}
1369 	return bFull;
1370 }
1371 
1372 /*************************************************************************
1373  * SwCombinedPortion::GetViewWidth(..)
1374  *************************************************************************/
1375 
1376 KSHORT SwCombinedPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const
1377 {
1378 	if( !GetLen() )	// for the dummy part at the end of the line, where
1379 		return 0;	// the combined portion doesn't fit.
1380 	return SwFldPortion::GetViewWidth( rInf );
1381 }
1382