xref: /AOO41X/main/sw/source/core/text/porlay.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_sw.hxx"
30 
31 
32 #include "errhdl.hxx"	// ASSERT
33 
34 #include "txtcfg.hxx"
35 #include "porlay.hxx"
36 #include "itrform2.hxx"
37 #include "porglue.hxx"
38 #include "porexp.hxx"	// SwQuoVadisPortion
39 #include "blink.hxx"	// pBlink
40 #include "redlnitr.hxx" // SwRedlineItr
41 #include "porfly.hxx"	// SwFlyCntPortion
42 #include <porrst.hxx>		// SwHangingPortion
43 #include <pormulti.hxx> 	// SwMultiPortion
44 #include <breakit.hxx>
45 #include <unicode/uchar.h>
46 #include <com/sun/star/i18n/ScriptType.hdl>
47 #include <com/sun/star/i18n/CTLScriptType.hdl>
48 #include <com/sun/star/i18n/WordType.hdl>
49 #include <paratr.hxx>
50 #include <editeng/adjitem.hxx>
51 #include <editeng/scripttypeitem.hxx>
52 #include <editeng/charhiddenitem.hxx>
53 #include <vcl/outdev.hxx>
54 #include <editeng/blnkitem.hxx>
55 #include <tools/multisel.hxx>
56 #include <unotools/charclass.hxx>
57 #include <i18npool/mslangid.hxx>
58 #include <charfmt.hxx>
59 #include <fchrfmt.hxx>
60 #include <docary.hxx>       // SwRedlineTbl
61 #include <redline.hxx>      // SwRedline
62 #include <section.hxx>
63 #include <switerator.hxx>
64 #include <IDocumentRedlineAccess.hxx>
65 #include <IDocumentSettingAccess.hxx>
66 #include <IDocumentContentOperations.hxx>
67 
68 using namespace ::com::sun::star;
69 using namespace i18n::ScriptType;
70 
71 //#ifdef BIDI
72 #include <unicode/ubidi.h>
73 #include <i18nutil/unicode.hxx>  //unicode::getUnicodeScriptType
74 
75 sal_Bool isAlefChar ( xub_Unicode cCh )
76 {
77    return ( cCh == 0x622 || cCh == 0x623 || cCh == 0x625 || cCh == 0x627 ||
78            cCh == 0x622 || cCh == 0x671 || cCh == 0x672 || cCh == 0x673 || cCh == 0x675 );
79 }
80 
81 sal_Bool isWawChar ( xub_Unicode cCh )
82 {
83    return ( cCh == 0x624 || cCh == 0x648 || cCh == 0x676 || cCh == 0x677 ||
84            ( cCh >= 0x6C4 &&  cCh <= 0x6CB ) || cCh == 0x6CF );
85 }
86 
87 sal_Bool isDalChar ( xub_Unicode cCh )
88 {
89    return ( cCh == 0x62F || cCh == 0x630 || cCh == 0x688 || cCh == 0x689 || cCh == 0x690 );
90 }
91 
92 sal_Bool isRehChar ( xub_Unicode cCh )
93 {
94    return ( cCh == 0x631 || cCh == 0x632 || ( cCh >= 0x691 && cCh <= 0x699 ));
95 }
96 
97 sal_Bool isTehMarbutaChar ( xub_Unicode cCh )
98 {
99    return ( cCh == 0x629 || cCh == 0x6C0 );
100 }
101 
102 sal_Bool isBaaChar ( xub_Unicode cCh )
103 {
104    return ( cCh == 0x628 || cCh == 0x62A || cCh == 0x62B || cCh == 0x679 || cCh == 0x680 );
105 }
106 
107 sal_Bool isYehChar ( xub_Unicode cCh )
108 {
109    return ( cCh == 0x626 || cCh == 0x649 || cCh == 0x64A || cCh == 0x678 || cCh == 0x6CC ||
110        cCh == 0x6CE || cCh == 0x6D0 || cCh == 0x6D1 );
111 }
112 
113 sal_Bool isSeenOrSadChar ( xub_Unicode cCh )
114 {
115    return ( ( cCh >= 0x633 && cCh <= 0x636 ) || ( cCh >= 0x69A && cCh <= 0x69E )
116            || cCh == 0x6FA || cCh == 0x6FB );
117 }
118 
119 sal_Bool isHahChar ( xub_Unicode cCh )
120 {
121    return ( ( cCh >= 0x62C && cCh <= 0x62E ) || ( cCh >= 0x681 && cCh <= 0x687 )
122            || cCh == 0x6BF );
123 }
124 
125 sal_Bool isAinChar ( xub_Unicode cCh )
126 {
127    return ( cCh == 0x639 || cCh == 0x63A || cCh == 0x6A0 || cCh == 0x6FC );
128 }
129 
130 sal_Bool isKafChar ( xub_Unicode cCh )
131 {
132    return ( cCh == 0x643 || ( cCh >= 0x6AC && cCh <= 0x6AE ) );
133 }
134 
135 sal_Bool isLamChar ( xub_Unicode cCh )
136 {
137    return ( cCh == 0x644 || ( cCh >= 0x6B5 && cCh <= 0x6B8 ) );
138 }
139 
140 sal_Bool isGafChar ( xub_Unicode cCh )
141 {
142    return ( cCh == 0x6A9 || cCh == 0x6AB ||( cCh >= 0x6AF && cCh <= 0x6B4 ) );
143 }
144 
145 sal_Bool isQafChar ( xub_Unicode cCh )
146 {
147    return ( cCh == 0x642 || cCh == 0x6A7 || cCh == 0x6A8  );
148 }
149 
150 sal_Bool isFeChar ( xub_Unicode cCh )
151 {
152    return ( cCh == 0x641 || ( cCh >= 0x6A1 && cCh <= 0x6A6 ) );
153 }
154 sal_Bool isTransparentChar ( xub_Unicode cCh )
155 {
156     return ( ( cCh >= 0x610 && cCh <= 0x61A ) ||
157             ( cCh >= 0x64B && cCh <= 0x65E ) ||
158             ( cCh == 0x670 ) ||
159             ( cCh >= 0x6D6 && cCh <= 0x6DC ) ||
160             ( cCh >= 0x6DF && cCh <= 0x6E4 ) ||
161             ( cCh >= 0x6E7 && cCh <= 0x6E8 ) ||
162             ( cCh >= 0x6EA && cCh <= 0x6ED ));
163 }
164 
165 /*************************************************************************
166  *                 lcl_IsLigature
167  *
168  * Checks if cCh + cNectCh builds a ligature (used for Kashidas)
169  *************************************************************************/
170 
171 sal_Bool lcl_IsLigature( xub_Unicode cCh, xub_Unicode cNextCh )
172 {
173             // Lam + Alef
174     return ( isLamChar ( cCh ) && isAlefChar ( cNextCh ));
175 }
176 
177 /*************************************************************************
178  *                 lcl_ConnectToPrev
179  *
180  * Checks if cCh is connectable to cPrevCh (used for Kashidas)
181  *************************************************************************/
182 
183 sal_Bool lcl_ConnectToPrev( xub_Unicode cCh, xub_Unicode cPrevCh )
184 {
185     // Alef, Dal, Thal, Reh, Zain, and Waw do not connect to the left
186     // Uh, there seem to be some more characters that are not connectable
187     // to the left. So we look for the characters that are actually connectable
188     // to the left. Here is the complete list of WH:
189 
190 	// (hennerdrewes):
191 	// added lam forms 0x06B5..0x06B8
192 	// added 0x6FA..0x6FC, according to unicode documentation, although not present in my fonts
193 	// added heh goal 0x6C1
194     sal_Bool bRet = 0x628 == cPrevCh ||
195                     ( 0x62A <= cPrevCh && cPrevCh <= 0x62E ) ||
196                   ( 0x633 <= cPrevCh && cPrevCh <= 0x647 ) ||
197                       0x649 == cPrevCh || // Alef Maksura does connect !!!
198                       0x64A == cPrevCh ||
199                     ( 0x678 <= cPrevCh && cPrevCh <= 0x687 ) ||
200                   ( 0x69A <= cPrevCh && cPrevCh <= 0x6C1 ) ||
201                   ( 0x6C3 <= cPrevCh && cPrevCh <= 0x6D3 ) ||
202 		          ( 0x6FA <= cPrevCh && cPrevCh <= 0x6FC )  ;
203 
204     // check for ligatures cPrevChar + cChar
205 	if( bRet )
206 		bRet = !lcl_IsLigature( cPrevCh, cCh );
207     return bRet;
208 }
209 
210 /*************************************************************************
211  *                 lcl_HasStrongLTR
212  *************************************************************************/
213  bool lcl_HasStrongLTR ( const String& rTxt, xub_StrLen nStart, xub_StrLen nEnd )
214  {
215      for ( xub_StrLen nCharIdx = nStart; nCharIdx < nEnd; ++nCharIdx )
216      {
217          const UCharDirection nCharDir = u_charDirection ( rTxt.GetChar ( nCharIdx ));
218          if ( nCharDir == U_LEFT_TO_RIGHT ||
219               nCharDir == U_LEFT_TO_RIGHT_EMBEDDING ||
220               nCharDir == U_LEFT_TO_RIGHT_OVERRIDE )
221              return true;
222      }
223      return false;
224  }
225 
226 /*************************************************************************
227  *				   SwLineLayout::~SwLineLayout()
228  *
229  * class SwLineLayout: Das Layout einer einzelnen Zeile. Dazu
230  * gehoeren vor allen Dingen die Dimension, die Anzahl der
231  * Character und der Wortzwischenraeume in der Zeile.
232  * Zeilenobjekte werden in einem eigenen Pool verwaltet, um zu
233  * erreichen, dass sie im Speicher moeglichst beeinander liegen
234  * (d.h. zusammen gepaged werden und den Speicher nicht
235  * fragmentieren).
236  *************************************************************************/
237 
238 SwLineLayout::~SwLineLayout()
239 {
240 	Truncate();
241 	if( GetNext() )
242 		delete GetNext();
243 	if( pBlink )
244 		pBlink->Delete( this );
245     delete pLLSpaceAdd;
246     if ( pKanaComp )
247         delete pKanaComp;
248 }
249 
250 /*************************************************************************
251  *				  virtual SwLineLayout::Insert()
252  *************************************************************************/
253 
254 SwLinePortion *SwLineLayout::Insert( SwLinePortion *pIns )
255 {
256 	// Erster Attributwechsel, Masse und Laengen
257 	// aus *pCurr in die erste Textportion kopieren.
258 	if( !pPortion )
259 	{
260 		if( GetLen() )
261 		{
262 			pPortion = new SwTxtPortion( *(SwLinePortion*)this );
263 			if( IsBlinking() && pBlink )
264 			{
265 				SetBlinking( sal_False );
266 				pBlink->Replace( this, pPortion );
267 			}
268 		}
269 		else
270 		{
271 			SetPortion( pIns );
272 			return pIns;
273 		}
274 	}
275 	// mit Skope aufrufen, sonst Rekursion !
276 	return pPortion->SwLinePortion::Insert( pIns );
277 }
278 
279 /*************************************************************************
280  *				  virtual SwLineLayout::Append()
281  *************************************************************************/
282 
283 SwLinePortion *SwLineLayout::Append( SwLinePortion *pIns )
284 {
285 	// Erster Attributwechsel, Masse und Laengen
286 	// aus *pCurr in die erste Textportion kopieren.
287 	if( !pPortion )
288 		pPortion = new SwTxtPortion( *(SwLinePortion*)this );
289 	// mit Skope aufrufen, sonst Rekursion !
290 	return pPortion->SwLinePortion::Append( pIns );
291 }
292 
293 /*************************************************************************
294  *				  virtual SwLineLayout::Format()
295  *************************************************************************/
296 
297 // fuer die Sonderbehandlung bei leeren Zeilen
298 
299 sal_Bool SwLineLayout::Format( SwTxtFormatInfo &rInf )
300 {
301 	if( GetLen() )
302 		return SwTxtPortion::Format( rInf );
303 	else
304 	{
305 		Height( rInf.GetTxtHeight() );
306 		return sal_True;
307 	}
308 }
309 
310 /*************************************************************************
311  *					  SwLineLayout::CalcLeftMargin()
312  *
313  * Wir sammeln alle FlyPortions am Anfang der Zeile zu einer MarginPortion.
314  *************************************************************************/
315 
316 SwMarginPortion *SwLineLayout::CalcLeftMargin()
317 {
318 	SwMarginPortion *pLeft = (GetPortion() && GetPortion()->IsMarginPortion()) ?
319 		(SwMarginPortion *)GetPortion() : 0;
320 	if( !GetPortion() )
321 		 SetPortion( new SwTxtPortion( *(SwLinePortion*)this ) );
322 	if( !pLeft )
323 	{
324 		pLeft = new SwMarginPortion( 0 );
325 		pLeft->SetPortion( GetPortion() );
326 		SetPortion( pLeft );
327 	}
328 	else
329 	{
330 		pLeft->Height( 0 );
331 		pLeft->Width( 0 );
332 		pLeft->SetLen( 0 );
333 		pLeft->SetAscent( 0 );
334 		pLeft->SetPortion( NULL );
335 		pLeft->SetFixWidth(0);
336 	}
337 
338 	SwLinePortion *pPos = pLeft->GetPortion();
339 	while( pPos )
340 	{
341 		DBG_LOOP;
342 		if( pPos->IsFlyPortion() )
343 		{
344 			// Die FlyPortion wird ausgesogen ...
345 			pLeft->Join( (SwGluePortion*)pPos );
346 			pPos = pLeft->GetPortion();
347 			if( GetpKanaComp() )
348 				GetKanaComp().Remove( 0, 1 );
349 		}
350 		else
351 			pPos = 0;
352 	}
353 	return pLeft;
354 }
355 
356 /*************************************************************************
357  *                    SwLineLayout::InitSpaceAdd()
358  *************************************************************************/
359 
360 void SwLineLayout::InitSpaceAdd()
361 {
362     if ( !pLLSpaceAdd )
363         CreateSpaceAdd();
364     else
365         SetLLSpaceAdd( 0, 0 );
366 }
367 
368 /*************************************************************************
369  *					  SwLineLayout::CreateSpaceAdd()
370  *************************************************************************/
371 
372 void SwLineLayout::CreateSpaceAdd( const long nInit )
373 {
374     pLLSpaceAdd = new std::vector<long>;
375     SetLLSpaceAdd( nInit, 0 );
376 }
377 
378 /*************************************************************************
379  * Local helper function. Returns true if there are only blanks
380  * in [nStt, nEnd[
381  *************************************************************************/
382 
383 bool lcl_HasOnlyBlanks( const XubString& rTxt, xub_StrLen nStt, xub_StrLen nEnd )
384 {
385     bool bBlankOnly = true;
386     while ( nStt < nEnd )
387     {
388         const xub_Unicode cChar = rTxt.GetChar( nStt++ );
389         if ( ' ' != cChar && 0x3000 != cChar )
390         {
391             bBlankOnly = false;
392             break;
393         }
394     }
395     return bBlankOnly;
396 }
397 
398 /*************************************************************************
399  *					  SwLineLayout::CalcLine()
400  *
401  * Aus FormatLine() ausgelagert.
402  *************************************************************************/
403 
404 void SwLineLayout::CalcLine( SwTxtFormatter &rLine, SwTxtFormatInfo &rInf )
405 {
406 	const KSHORT nLineWidth = rInf.RealWidth();
407 
408     KSHORT nFlyAscent = 0;
409     KSHORT nFlyHeight = 0;
410     KSHORT nFlyDescent = 0;
411 	sal_Bool bOnlyPostIts = sal_True;
412 	SetHanging( sal_False );
413 
414 	sal_Bool bTmpDummy = ( 0 == GetLen() );
415 	SwFlyCntPortion* pFlyCnt = 0;
416 	if( bTmpDummy )
417 	{
418 		nFlyAscent = 0;
419 		nFlyHeight = 0;
420 		nFlyDescent = 0;
421 	}
422 
423     // --> FME 2006-03-01 #i3952#
424     const bool bIgnoreBlanksAndTabsForLineHeightCalculation =
425             rInf.GetTxtFrm()->GetNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::IGNORE_TABS_AND_BLANKS_FOR_LINE_CALCULATION);
426 
427     bool bHasBlankPortion = false;
428     bool bHasOnlyBlankPortions = true;
429     // <--
430 
431 	if( pPortion )
432 	{
433 		SetCntnt( sal_False );
434 		if( pPortion->IsBreakPortion() )
435 		{
436 			SetLen( pPortion->GetLen() );
437 			if( GetLen() )
438 				bTmpDummy = sal_False;
439 		}
440 		else
441 		{
442 			Init( GetPortion() );
443 			SwLinePortion *pPos = pPortion;
444 			SwLinePortion *pLast = this;
445 			KSHORT nMaxDescent = 0;
446 
447 			//	Eine Gruppe ist ein Abschnitt in der Portion-Kette von
448 			//	pCurr oder einer Fix-Portion bis zum Ende bzw. zur naechsten
449 			//	Fix-Portion.
450 			while( pPos )
451 			{
452 				DBG_LOOP;
453 				ASSERT( POR_LIN != pPos->GetWhichPor(),
454 						"SwLineLayout::CalcLine: don't use SwLinePortions !" );
455 
456                 // Null-Portions werden eliminiert. Sie koennen entstehen,
457 				// wenn zwei FlyFrms ueberlappen.
458 				if( !pPos->Compress() )
459 				{
460 					// 8110: Hoehe und Ascent nur uebernehmen, wenn sonst in der
461 					// Zeile nichts mehr los ist.
462 					if( !pPos->GetPortion() )
463 					{
464 						if( !Height() )
465 							Height( pPos->Height() );
466 						if( !GetAscent() )
467 							SetAscent( pPos->GetAscent() );
468 					}
469 					delete pLast->Cut( pPos );
470 					pPos = pLast->GetPortion();
471 					continue;
472 				}
473 
474                 const xub_StrLen nPorSttIdx = rInf.GetLineStart() + nLineLength;
475                 nLineLength = nLineLength + pPos->GetLen();
476 				AddPrtWidth( pPos->Width() );
477 
478                 // --> FME 2006-03-01 #i3952#
479                 if ( bIgnoreBlanksAndTabsForLineHeightCalculation )
480                 {
481                     if ( pPos->InTabGrp() || pPos->IsHolePortion() ||
482                             ( pPos->IsTextPortion() &&
483                               lcl_HasOnlyBlanks( rInf.GetTxt(), nPorSttIdx, nPorSttIdx + pPos->GetLen() ) ) )
484                     {
485         				pLast = pPos;
486 	    			    pPos = pPos->GetPortion();
487                         bHasBlankPortion = true;
488                         continue;
489                     }
490                 }
491                 // <--
492 
493                 bHasOnlyBlankPortions = false;
494 
495 				// Es gab Attributwechsel: Laengen und Masse aufaddieren;
496 				// bzw.Maxima bilden.
497 
498                 KSHORT nPosHeight = pPos->Height();
499 				KSHORT nPosAscent = pPos->GetAscent();
500 
501 				ASSERT( nPosHeight >= nPosAscent,
502 						"SwLineLayout::CalcLine: bad ascent or height" );
503 
504                 if( pPos->IsHangingPortion() )
505 				{
506 					SetHanging( sal_True );
507 					rInf.GetParaPortion()->SetMargin( sal_True );
508 				}
509 
510 				// Damit ein Paragraphende-Zeichen nicht durch ein Descent zu einer
511 				// geaenderten Zeilenhoehe und zum Umformatieren fuehrt.
512 				if ( !pPos->IsBreakPortion() || !Height() )
513 				{
514 					bOnlyPostIts &= pPos->IsPostItsPortion();
515 
516                     if( bTmpDummy && !nLineLength )
517 					{
518 						if( pPos->IsFlyPortion() )
519 						{
520 							if( nFlyHeight < nPosHeight )
521 								nFlyHeight = nPosHeight;
522 							if( nFlyAscent < nPosAscent )
523 								nFlyAscent = nPosAscent;
524 							if( nFlyDescent < nPosHeight - nPosAscent )
525 								nFlyDescent = nPosHeight - nPosAscent;
526 						}
527 						else
528 						{
529 							if( pPos->InNumberGrp() )
530 							{
531 								KSHORT nTmp = rInf.GetFont()->GetAscent(
532                                                 rInf.GetVsh(), *rInf.GetOut() );
533 								if( nTmp > nPosAscent )
534 								{
535 									nPosHeight += nTmp - nPosAscent;
536 									nPosAscent = nTmp;
537 								}
538 								nTmp = rInf.GetFont()->GetHeight( rInf.GetVsh(),
539                                                                  *rInf.GetOut() );
540 								if( nTmp > nPosHeight )
541 									nPosHeight = nTmp;
542 							}
543 							Height( nPosHeight );
544 							nAscent = nPosAscent;
545 							nMaxDescent = nPosHeight - nPosAscent;
546 						}
547 					}
548 					else if( !pPos->IsFlyPortion() )
549 					{
550 						if( Height() < nPosHeight )
551 							Height( nPosHeight );
552 						if( pPos->IsFlyCntPortion() || ( pPos->IsMultiPortion()
553 							&& ((SwMultiPortion*)pPos)->HasFlyInCntnt() ) )
554 							rLine.SetFlyInCntBase();
555 						if( pPos->IsFlyCntPortion() &&
556 							((SwFlyCntPortion*)pPos)->GetAlign() )
557 						{
558 							((SwFlyCntPortion*)pPos)->SetMax( sal_False );
559 							if( !pFlyCnt || pPos->Height() > pFlyCnt->Height() )
560 								pFlyCnt = (SwFlyCntPortion*)pPos;
561 						}
562 						else
563 						{
564 							if( nAscent < nPosAscent )
565 								nAscent = nPosAscent;
566 							if( nMaxDescent < nPosHeight - nPosAscent )
567 								nMaxDescent = nPosHeight - nPosAscent;
568 						}
569 					}
570 				}
571 				else if( pPos->GetLen() )
572 					bTmpDummy = sal_False;
573 
574                 if( !HasCntnt() && !pPos->InNumberGrp() )
575 				{
576 					if ( pPos->InExpGrp() )
577 					{
578 						XubString aTxt;
579 						if( pPos->GetExpTxt( rInf, aTxt ) && aTxt.Len() )
580 							SetCntnt( sal_True );
581 					}
582                     else if( ( pPos->InTxtGrp() || pPos->IsMultiPortion() ) &&
583                              pPos->GetLen() )
584 						SetCntnt( sal_True );
585 				}
586 
587                 bTmpDummy = bTmpDummy && !HasCntnt() &&
588 							( !pPos->Width() || pPos->IsFlyPortion() );
589 
590 				pLast = pPos;
591 				pPos = pPos->GetPortion();
592 			}
593 
594             if( pFlyCnt )
595 			{
596 				if( pFlyCnt->Height() == Height() )
597 				{
598 					pFlyCnt->SetMax( sal_True );
599 					if( Height() > nMaxDescent + nAscent )
600 					{
601 						if( 3 == pFlyCnt->GetAlign() ) // Bottom
602 							nAscent = Height() - nMaxDescent;
603 						else if( 2 == pFlyCnt->GetAlign() ) // Center
604 							nAscent = ( Height() + nAscent - nMaxDescent ) / 2;
605 					}
606 					pFlyCnt->SetAscent( nAscent );
607 				}
608 			}
609 
610             if( bTmpDummy && nFlyHeight )
611 			{
612 				nAscent = nFlyAscent;
613 				if( nFlyDescent > nFlyHeight - nFlyAscent )
614 					Height( nFlyHeight + nFlyDescent );
615 				else
616 					Height( nFlyHeight );
617 			}
618 			else if( nMaxDescent > Height() - nAscent )
619 				Height( nMaxDescent + nAscent );
620 
621             if( bOnlyPostIts && !( bHasBlankPortion && bHasOnlyBlankPortions ) )
622 			{
623                 Height( rInf.GetFont()->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) );
624                 nAscent = rInf.GetFont()->GetAscent( rInf.GetVsh(), *rInf.GetOut() );
625 			}
626 		}
627 	}
628 	else
629     {
630 		SetCntnt( !bTmpDummy );
631 
632         // --> FME 2006-03-01 #i3952#
633         if ( bIgnoreBlanksAndTabsForLineHeightCalculation &&
634              lcl_HasOnlyBlanks( rInf.GetTxt(), rInf.GetLineStart(), rInf.GetLineStart() + GetLen() ) )
635         {
636             bHasBlankPortion = true;
637         }
638         // <--
639     }
640 
641     // --> FME 2006-03-01 #i3952#
642     if ( bHasBlankPortion && bHasOnlyBlankPortions )
643     {
644         sal_uInt16 nTmpAscent = GetAscent();
645         sal_uInt16 nTmpHeight = Height();
646         rLine.GetAttrHandler().GetDefaultAscentAndHeight( rInf.GetVsh(), *rInf.GetOut(), nTmpAscent, nTmpHeight );
647         SetAscent( nTmpAscent );
648         Height( nTmpHeight );
649     }
650     // <--
651 
652 	// Robust:
653 	if( nLineWidth < Width() )
654 		Width( nLineWidth );
655 	ASSERT( nLineWidth >= Width(), "SwLineLayout::CalcLine: line is bursting" );
656 	SetDummy( bTmpDummy );
657 	SetRedline( rLine.GetRedln() &&
658 		rLine.GetRedln()->CheckLine( rLine.GetStart(), rLine.GetEnd() ) );
659 }
660 
661 // --> OD 2005-05-20 #i47162# - add optional parameter <_bNoFlyCntPorAndLinePor>
662 // to control, if the fly content portions and line portion are considered.
663 void SwLineLayout::MaxAscentDescent( SwTwips& _orAscent,
664                                      SwTwips& _orDescent,
665                                      SwTwips& _orObjAscent,
666                                      SwTwips& _orObjDescent,
667                                      const SwLinePortion* _pDontConsiderPortion,
668                                      const bool _bNoFlyCntPorAndLinePor ) const
669 {
670     _orAscent = 0;
671     _orDescent = 0;
672     _orObjAscent = 0;
673     _orObjDescent = 0;
674 
675     const SwLinePortion* pTmpPortion = this;
676     if ( !pTmpPortion->GetLen() && pTmpPortion->GetPortion() )
677     {
678         pTmpPortion = pTmpPortion->GetPortion();
679     }
680 
681     while ( pTmpPortion )
682     {
683         if ( !pTmpPortion->IsBreakPortion() && !pTmpPortion->IsFlyPortion() &&
684              ( !_bNoFlyCntPorAndLinePor ||
685                ( !pTmpPortion->IsFlyCntPortion() &&
686                  !(pTmpPortion == this && pTmpPortion->GetPortion() ) ) ) )
687         {
688             SwTwips nPortionAsc = static_cast<SwTwips>(pTmpPortion->GetAscent());
689             SwTwips nPortionDesc = static_cast<SwTwips>(pTmpPortion->Height()) -
690                                    nPortionAsc;
691 
692             const sal_Bool bFlyCmp = pTmpPortion->IsFlyCntPortion() ?
693                                      static_cast<const SwFlyCntPortion*>(pTmpPortion)->IsMax() :
694                                      !( pTmpPortion == _pDontConsiderPortion );
695 
696             if ( bFlyCmp )
697             {
698                 _orObjAscent = Max( _orObjAscent, nPortionAsc );
699                 _orObjDescent = Max( _orObjDescent, nPortionDesc );
700             }
701 
702             if ( !pTmpPortion->IsFlyCntPortion() && !pTmpPortion->IsGrfNumPortion() )
703             {
704                 _orAscent = Max( _orAscent, nPortionAsc );
705                 _orDescent = Max( _orDescent, nPortionDesc );
706             }
707         }
708         pTmpPortion = pTmpPortion->GetPortion();
709     }
710 }
711 
712 /*************************************************************************
713  *						class SwCharRange
714  *************************************************************************/
715 
716 SwCharRange &SwCharRange::operator+=(const SwCharRange &rRange)
717 {
718 	if(0 != rRange.nLen ) {
719 		if(0 == nLen) {
720 			nStart = rRange.nStart;
721 			nLen = rRange.nLen ;
722 		}
723 		else {
724 			if(rRange.nStart + rRange.nLen > nStart + nLen) {
725 				nLen = rRange.nStart + rRange.nLen - nStart;
726 			}
727 			if(rRange.nStart < nStart) {
728 				nLen += nStart - rRange.nStart;
729 				nStart = rRange.nStart;
730 			}
731 		}
732 	}
733 	return *this;
734 }
735 
736 /*************************************************************************
737  *                      SwScriptInfo::SwScriptInfo()
738  *************************************************************************/
739 SwScriptInfo::SwScriptInfo() :
740     nInvalidityPos( 0 ),
741     nDefaultDir( 0 )
742 {
743 };
744 
745 /*************************************************************************
746  *                      SwScriptInfo::~SwScriptInfo()
747  *************************************************************************/
748 SwScriptInfo::~SwScriptInfo()
749 {
750 }
751 
752 /*************************************************************************
753  *                     SwScriptInfo::WhichFont()
754  *
755  * Converts i18n Script Type (LATIN, ASIAN, COMPLEX, WEAK) to
756  * Sw Script Types (SW_LATIN, SW_CJK, SW_CTL), used to identify the font
757  *************************************************************************/
758 sal_uInt8 SwScriptInfo::WhichFont( xub_StrLen nIdx, const String* pTxt, const SwScriptInfo* pSI )
759 {
760     ASSERT( pTxt || pSI,"How should I determine the script type?" );
761     sal_uInt16 nScript;
762 
763     // First we try to use our SwScriptInfo
764     if ( pSI )
765         nScript = pSI->ScriptType( nIdx );
766     else
767         // Ok, we have to ask the break iterator
768         nScript = pBreakIt->GetRealScriptOfText( *pTxt, nIdx );
769 
770     switch ( nScript ) {
771         case i18n::ScriptType::LATIN : return SW_LATIN;
772         case i18n::ScriptType::ASIAN : return SW_CJK;
773         case i18n::ScriptType::COMPLEX : return SW_CTL;
774     }
775 
776     ASSERT( sal_False, "Somebody tells lies about the script type!" );
777     return SW_LATIN;
778 }
779 
780 /*************************************************************************
781  *						SwScriptInfo::InitScriptInfo()
782  *
783  * searches for script changes in rTxt and stores them
784  *************************************************************************/
785 
786 void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode )
787 {
788     InitScriptInfo( rNode, nDefaultDir == UBIDI_RTL );
789 }
790 
791 void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode, sal_Bool bRTL )
792 {
793     if( !pBreakIt->GetBreakIter().is() )
794 		return;
795 
796     const String& rTxt = rNode.GetTxt();
797 
798     //
799     // HIDDEN TEXT INFORMATION
800     //
801     Range aRange( 0, rTxt.Len() ? rTxt.Len() - 1 : 0 );
802     MultiSelection aHiddenMulti( aRange );
803     CalcHiddenRanges( rNode, aHiddenMulti );
804 
805     aHiddenChg.clear();
806     sal_uInt16 i = 0;
807     for( i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
808     {
809         const Range& rRange = aHiddenMulti.GetRange( i );
810         const xub_StrLen nStart = (xub_StrLen)rRange.Min();
811         const xub_StrLen nEnd = (xub_StrLen)rRange.Max() + 1;
812 
813         aHiddenChg.push_back( nStart );
814         aHiddenChg.push_back( nEnd );
815     }
816 
817     //
818     // SCRIPT AND SCRIPT RELATED INFORMATION
819     //
820 
821     xub_StrLen nChg = nInvalidityPos;
822 
823     // STRING_LEN means the data structure is up to date
824 	nInvalidityPos = STRING_LEN;
825 
826     // this is the default direction
827     nDefaultDir = static_cast<sal_uInt8>(bRTL ? UBIDI_RTL : UBIDI_LTR);
828 
829     // counter for script info arrays
830     sal_uInt16 nCnt = 0;
831     // counter for compression information arrays
832     sal_uInt16 nCntComp = 0;
833     // counter for kashida array
834     sal_uInt16 nCntKash = 0;
835 
836     sal_uInt8 nScript = i18n::ScriptType::LATIN;
837 
838     // compression type
839     const SwCharCompressType aCompEnum = rNode.getIDocumentSettingAccess()->getCharacterCompressionType();
840 
841     // justification type
842     const sal_Bool bAdjustBlock = SVX_ADJUST_BLOCK ==
843                                   rNode.GetSwAttrSet().GetAdjust().GetAdjust();
844 
845     //
846     // FIND INVALID RANGES IN SCRIPT INFO ARRAYS:
847     //
848 
849     if( nChg )
850 	{
851         // if change position = 0 we do not use any data from the arrays
852         // because by deleting all characters of the first group at the beginning
853         // of a paragraph nScript is set to a wrong value
854         ASSERT( CountScriptChg(), "Where're my changes of script?" );
855 		while( nCnt < CountScriptChg() )
856 		{
857             if ( nChg > GetScriptChg( nCnt ) )
858                 nCnt++;
859             else
860             {
861                 nScript = GetScriptType( nCnt );
862                 break;
863             }
864 		}
865         if( CHARCOMPRESS_NONE != aCompEnum )
866 		{
867             while( nCntComp < CountCompChg() )
868 			{
869                 if ( nChg > GetCompStart( nCntComp ) )
870                     nCntComp++;
871                 else
872                     break;
873             }
874 		}
875         if ( bAdjustBlock )
876         {
877             while( nCntKash < CountKashida() )
878             {
879                 if ( nChg > GetKashida( nCntKash ) )
880                     nCntKash++;
881                 else
882                     break;
883             }
884         }
885     }
886 
887     //
888     // ADJUST nChg VALUE:
889     //
890 
891     // by stepping back one position we know that we are inside a group
892     // declared as an nScript group
893     if ( nChg )
894         --nChg;
895 
896     const xub_StrLen nGrpStart = nCnt ? GetScriptChg( nCnt - 1 ) : 0;
897 
898     // we go back in our group until we reach the first character of
899     // type nScript
900     while ( nChg > nGrpStart &&
901             nScript != pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) )
902         --nChg;
903 
904     // If we are at the start of a group, we do not trust nScript,
905     // we better get nScript from the breakiterator:
906     if ( nChg == nGrpStart )
907         nScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg );
908 
909     //
910     // INVALID DATA FROM THE SCRIPT INFO ARRAYS HAS TO BE DELETED:
911     //
912 
913     // remove invalid entries from script information arrays
914     const size_t nScriptRemove = aScriptChg.size() - nCnt;
915     aScriptChg.erase( aScriptChg.begin() + nCnt, aScriptChg.end() );
916     aScriptType.erase( aScriptType.begin() + nCnt, aScriptType.begin() + (nCnt + nScriptRemove) );
917 
918     // get the start of the last compression group
919     sal_uInt16 nLastCompression = nChg;
920 	if( nCntComp )
921 	{
922 		--nCntComp;
923         nLastCompression = GetCompStart( nCntComp );
924         if( nChg >= nLastCompression + GetCompLen( nCntComp ) )
925 		{
926             nLastCompression = nChg;
927 			++nCntComp;
928 		}
929     }
930 
931     // remove invalid entries from compression information arrays
932     const size_t nCompRemove = aCompChg.size() - nCntComp;
933     aCompChg.erase( aCompChg.begin() + nCntComp, aCompChg.end() );
934     aCompLen.erase( aCompLen.begin() + nCntComp, aCompLen.begin() + (nCntComp + nCompRemove) );
935     aCompType.erase( aCompType.begin() + nCntComp, aCompType.end() );
936 
937     // get the start of the last kashida group
938     sal_uInt16 nLastKashida = nChg;
939     if( nCntKash && i18n::ScriptType::COMPLEX == nScript )
940     {
941         --nCntKash;
942         nLastKashida = GetKashida( nCntKash );
943     }
944 
945     // remove invalid entries from kashida array
946     aKashida.erase( aKashida.begin() + nCntKash, aKashida.end() );
947 
948     //
949     // TAKE CARE OF WEAK CHARACTERS: WE MUST FIND AN APPROPRIATE
950     // SCRIPT FOR WEAK CHARACTERS AT THE BEGINNING OF A PARAGRAPH
951     //
952 
953     if( WEAK == pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg ) )
954 	{
955         // If the beginning of the current group is weak, this means that
956         // all of the characters in this grounp are weak. We have to assign
957         // the scripts to these characters depending on the fonts which are
958         // set for these characters to display them.
959         xub_StrLen nEnd =
960                 (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rTxt, nChg, WEAK );
961 
962         if( nEnd > rTxt.Len() )
963             nEnd = rTxt.Len();
964 
965         nScript = (sal_uInt8)GetI18NScriptTypeOfLanguage( (sal_uInt16)GetAppLanguage() );
966 
967         ASSERT( i18n::ScriptType::LATIN == nScript ||
968                 i18n::ScriptType::ASIAN == nScript ||
969                 i18n::ScriptType::COMPLEX == nScript, "Wrong default language" );
970 
971         nChg = nEnd;
972 
973         // Get next script type or set to weak in order to exit
974         sal_uInt8 nNextScript = ( nEnd < rTxt.Len() ) ?
975            (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rTxt, nEnd ) :
976            (sal_uInt8)WEAK;
977 
978         if ( nScript != nNextScript )
979         {
980             aScriptChg.insert( aScriptChg.begin() + nCnt, nEnd );
981             aScriptType.insert( aScriptType.begin() + nCnt, nScript );
982             nCnt++;
983             nScript = nNextScript;
984         }
985     }
986 
987     //
988     // UPDATE THE SCRIPT INFO ARRAYS:
989     //
990 
991     while ( nChg < rTxt.Len() || ( aScriptChg.empty() && !rTxt.Len() ) )
992     {
993         ASSERT( i18n::ScriptType::WEAK != nScript,
994                 "Inserting WEAK into SwScriptInfo structure" );
995         ASSERT( STRING_LEN != nChg, "65K? Strange length of script section" );
996 
997         xub_StrLen nSearchStt = nChg;
998         nChg = (xub_StrLen)pBreakIt->GetBreakIter()->endOfScript( rTxt, nSearchStt, nScript );
999 
1000         if ( nChg > rTxt.Len() )
1001             nChg = rTxt.Len();
1002 
1003         // --> FME 2008-09-17 #i28203#
1004         // for 'complex' portions, we make sure that a portion does not contain more
1005         // than one script:
1006         if( i18n::ScriptType::COMPLEX == nScript && pBreakIt->GetScriptTypeDetector().is() )
1007         {
1008             const short nScriptType = pBreakIt->GetScriptTypeDetector()->getCTLScriptType( rTxt, nSearchStt );
1009             xub_StrLen nNextCTLScriptStart = nSearchStt;
1010             short nCurrentScriptType = nScriptType;
1011             while( com::sun::star::i18n::CTLScriptType::CTL_UNKNOWN == nCurrentScriptType || nScriptType == nCurrentScriptType )
1012             {
1013                 nNextCTLScriptStart = (xub_StrLen)pBreakIt->GetScriptTypeDetector()->endOfCTLScriptType( rTxt, nNextCTLScriptStart );
1014                 if( nNextCTLScriptStart < rTxt.Len() && nNextCTLScriptStart < nChg )
1015                     nCurrentScriptType = pBreakIt->GetScriptTypeDetector()->getCTLScriptType( rTxt, nNextCTLScriptStart );
1016                 else
1017                     break;
1018             }
1019             nChg = Min( nChg, nNextCTLScriptStart );
1020         }
1021         // <--
1022 
1023         // special case for dotted circle since it can be used with complex
1024         // before a mark, so we want it associated with the mark's script
1025         if (nChg < rTxt.Len() && nChg > 0 && (i18n::ScriptType::WEAK ==
1026             pBreakIt->GetBreakIter()->getScriptType(rTxt,nChg - 1)))
1027         {
1028             int8_t nType = u_charType(rTxt.GetChar(nChg) );
1029             if (nType == U_NON_SPACING_MARK || nType == U_ENCLOSING_MARK ||
1030                 nType == U_COMBINING_SPACING_MARK )
1031             {
1032                 aScriptChg.insert( aScriptChg.begin() + nCnt, nChg - 1 );
1033             }
1034             else
1035             {
1036                 aScriptChg.insert( aScriptChg.begin() + nCnt, nChg );
1037             }
1038         }
1039         else
1040         {
1041             aScriptChg.insert( aScriptChg.begin() + nCnt, nChg );
1042         }
1043         aScriptType.insert( aScriptType.begin() + nCnt, nScript );
1044         nCnt++;
1045 
1046         // if current script is asian, we search for compressable characters
1047         // in this range
1048         if ( CHARCOMPRESS_NONE != aCompEnum &&
1049              i18n::ScriptType::ASIAN == nScript )
1050         {
1051             sal_uInt8 ePrevState = NONE;
1052             sal_uInt8 eState;
1053             sal_uInt16 nPrevChg = nLastCompression;
1054 
1055             while ( nLastCompression < nChg )
1056             {
1057                 xub_Unicode cChar = rTxt.GetChar( nLastCompression );
1058 
1059                 // examine current character
1060                 switch ( cChar )
1061                 {
1062                 // Left punctuation found
1063                 case 0x3008: case 0x300A: case 0x300C: case 0x300E:
1064                 case 0x3010: case 0x3014: case 0x3016: case 0x3018:
1065                 case 0x301A: case 0x301D:
1066                     eState = SPECIAL_LEFT;
1067                     break;
1068                 // Right punctuation found
1069                 case 0x3001: case 0x3002: case 0x3009: case 0x300B:
1070                 case 0x300D: case 0x300F: case 0x3011: case 0x3015:
1071                 case 0x3017: case 0x3019: case 0x301B: case 0x301E:
1072                 case 0x301F:
1073                     eState = SPECIAL_RIGHT;
1074                     break;
1075                 default:
1076                     eState = static_cast<sal_uInt8>( ( 0x3040 <= cChar && 0x3100 > cChar ) ? KANA : NONE );
1077                 }
1078 
1079                 // insert range of compressable characters
1080                 if( ePrevState != eState )
1081                 {
1082                     if ( ePrevState != NONE )
1083                     {
1084                         // insert start and type
1085                         if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum ||
1086                              ePrevState != KANA )
1087                         {
1088                             aCompChg.insert( aCompChg.begin() + nCntComp, nPrevChg );
1089                             sal_uInt8 nTmpType = ePrevState;
1090                             aCompType.insert( aCompType.begin() + nCntComp, nTmpType );
1091                             aCompLen.insert( aCompLen.begin() + nCntComp, nLastCompression - nPrevChg );
1092                             nCntComp++;
1093                         }
1094                     }
1095 
1096                     ePrevState = eState;
1097                     nPrevChg = nLastCompression;
1098                 }
1099 
1100                 nLastCompression++;
1101             }
1102 
1103             // we still have to examine last entry
1104             if ( ePrevState != NONE )
1105             {
1106                 // insert start and type
1107                 if ( CHARCOMPRESS_PUNCTUATION_KANA == aCompEnum ||
1108                      ePrevState != KANA )
1109                 {
1110                     aCompChg.insert( aCompChg.begin() + nCntComp, nPrevChg );
1111                     sal_uInt8 nTmpType = ePrevState;
1112                     aCompType.insert( aCompType.begin() + nCntComp, nTmpType );
1113                     aCompLen.insert( aCompLen.begin() + nCntComp, nLastCompression - nPrevChg );
1114                     nCntComp++;
1115                 }
1116             }
1117         }
1118 
1119         // we search for connecting opportunities (kashida)
1120         else if ( bAdjustBlock && i18n::ScriptType::COMPLEX == nScript )
1121         {
1122             SwScanner aScanner( rNode, rNode.GetTxt(), 0, 0,
1123                                 i18n::WordType::DICTIONARY_WORD,
1124                                 nLastKashida, nChg );
1125 
1126             // the search has to be performed on a per word base
1127             while ( aScanner.NextWord() )
1128             {
1129                 const XubString& rWord = aScanner.GetWord();
1130 
1131                 xub_StrLen nIdx = 0;
1132                 xub_StrLen nKashidaPos = STRING_LEN;
1133                 xub_Unicode cCh;
1134                 xub_Unicode cPrevCh = 0;
1135 
1136                 sal_uInt16 nPriorityLevel = 7; // 0..6 = level found
1137                                            // 7 not found
1138 
1139 				xub_StrLen nWordLen = rWord.Len();
1140 
1141 				// ignore trailing vowel chars
1142 				while( nWordLen && isTransparentChar( rWord.GetChar( nWordLen - 1 )))
1143                     --nWordLen;
1144 
1145                 while (nIdx < nWordLen)
1146                 {
1147                     cCh = rWord.GetChar( nIdx );
1148 
1149                     // 1. Priority:
1150                     // after user inserted kashida
1151                     if ( 0x640 == cCh )
1152                     {
1153                         nKashidaPos = aScanner.GetBegin() + nIdx;
1154                         nPriorityLevel = 0;
1155                     }
1156 
1157                     // 2. Priority:
1158                     // after a Seen or Sad
1159                     if (nPriorityLevel >= 1 && nIdx < nWordLen - 1)
1160                     {
1161                         if( isSeenOrSadChar( cCh )
1162                          && (rWord.GetChar( nIdx+1 ) != 0x200C) ) // #i98410#: prevent ZWNJ expansion
1163                         {
1164                             nKashidaPos  = aScanner.GetBegin() + nIdx;
1165                             nPriorityLevel = 1;
1166                         }
1167                     }
1168 
1169                     // 3. Priority:
1170                     // before final form of Teh Marbuta, Hah, Dal
1171                     if ( nPriorityLevel >= 2 && nIdx > 0 )
1172                     {
1173                         if ( isTehMarbutaChar ( cCh ) || // Teh Marbuta (right joining)
1174                              isDalChar ( cCh ) ||        // Dal (right joining) final form may appear in the middle of word
1175                              ( isHahChar ( cCh ) && nIdx == nWordLen - 1))  // Hah (dual joining) only at end of word
1176                         {
1177 
1178                             ASSERT( 0 != cPrevCh, "No previous character" )
1179                             // check if character is connectable to previous character,
1180                             if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1181                             {
1182                                 nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1183                                 nPriorityLevel = 2;
1184                             }
1185                         }
1186                     }
1187 
1188                     // 4. Priority:
1189                     // before final form of Alef, Lam or Kaf
1190                     if ( nPriorityLevel >= 3 && nIdx > 0 )
1191                     {
1192                         if ( isAlefChar ( cCh ) ||   // Alef (right joining) final form may appear in the middle of word
1193                              (( isLamChar ( cCh ) || // Lam
1194                               isKafChar ( cCh )   || // Kaf (both dual joining)
1195                               isGafChar ( cCh ) )
1196                               && nIdx == nWordLen - 1))  // only at end of word
1197                         {
1198                             ASSERT( 0 != cPrevCh, "No previous character" )
1199                             // check if character is connectable to previous character,
1200                             if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1201                             {
1202                                 nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1203                                 nPriorityLevel = 3;
1204                             }
1205                         }
1206                     }
1207 
1208                     // 5. Priority:
1209                     // before media Bah
1210                     if ( nPriorityLevel >= 4 && nIdx > 0 && nIdx < nWordLen - 1 )
1211                     {
1212                         if ( isBaaChar ( cCh )) // Bah
1213                         {
1214                             // check if next character is Reh, Yeh or Alef Maksura
1215                             xub_Unicode cNextCh = rWord.GetChar( nIdx + 1 );
1216                             if ( isRehChar ( cNextCh ) || isYehChar ( cNextCh ))
1217                            {
1218                                 ASSERT( 0 != cPrevCh, "No previous character" )
1219                                 // check if character is connectable to previous character,
1220                                 if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1221                                 {
1222                                     nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1223                                     nPriorityLevel = 4;
1224                                 }
1225                             }
1226                         }
1227                     }
1228 
1229                     // 6. Priority:
1230                     // before the final form of Waw, Ain, Qaf and Fa
1231                     if ( nPriorityLevel >= 5 && nIdx > 0 )
1232                     {
1233                         if ( isWawChar ( cCh )   || // Wav (right joining)
1234                                                     // final form may appear in the middle of word
1235                              (( isAinChar ( cCh ) ||  // Ain (dual joining)
1236                                 isQafChar ( cCh ) ||  // Qaf (dual joining)
1237                                 isFeChar  ( cCh ) )   // Feh (dual joining)
1238                                 && nIdx == nWordLen - 1))  // only at end of word
1239                         {
1240                             ASSERT( 0 != cPrevCh, "No previous character" )
1241                             // check if character is connectable to previous character,
1242                             if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1243                             {
1244                                 nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1245                                 nPriorityLevel = 5;
1246                             }
1247                         }
1248                     }
1249 
1250                     // other connecting possibilities
1251                     if ( nPriorityLevel >= 6 && nIdx > 0 )
1252                     {
1253                         // remaining right joiners
1254                         // Reh, Zain, Thal,
1255                         if ( isRehChar ( cCh ) ||   // Reh Zain (right joining)
1256                                                     // final form may appear in the middle of word
1257                              ( 0x60C <= cCh && 0x6FE >= cCh // all others
1258                               && nIdx == nWordLen - 1))   // only at end of word
1259                         {
1260                             ASSERT( 0 != cPrevCh, "No previous character" )
1261                             // check if character is connectable to previous character,
1262                             if ( lcl_ConnectToPrev( cCh, cPrevCh ) )
1263                             {
1264                                 nKashidaPos = aScanner.GetBegin() + nIdx - 1;
1265                                 nPriorityLevel = 6;
1266                             }
1267                         }
1268                     }
1269 
1270                     // Do not consider Fathatan, Dammatan, Kasratan, Fatha,
1271                     // Damma, Kasra, Shadda and Sukun when checking if
1272                     // a character can be connected to previous character.
1273                     if ( !isTransparentChar ( cCh) )
1274                         cPrevCh = cCh;
1275 
1276                    ++nIdx;
1277                 } // end of current word
1278 
1279                 if ( STRING_LEN != nKashidaPos )
1280                 {
1281                     aKashida.insert( aKashida.begin() + nCntKash, nKashidaPos);
1282                     nCntKash++;
1283                 }
1284             } // end of kashida search
1285         }
1286 
1287         if ( nChg < rTxt.Len() )
1288             nScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rTxt, nChg );
1289 
1290         nLastCompression = nChg;
1291         nLastKashida = nChg;
1292     };
1293 
1294 #ifdef DBG_UTIL
1295     // check kashida data
1296     long nTmpKashidaPos = -1;
1297     sal_Bool bWrongKash = sal_False;
1298     for (i = 0; i < aKashida.size(); ++i )
1299     {
1300         long nCurrKashidaPos = GetKashida( i );
1301         if ( nCurrKashidaPos <= nTmpKashidaPos )
1302         {
1303             bWrongKash = sal_True;
1304             break;
1305         }
1306         nTmpKashidaPos = nCurrKashidaPos;
1307     }
1308     ASSERT( ! bWrongKash, "Kashida array contains wrong data" )
1309 #endif
1310 
1311     // remove invalid entries from direction information arrays
1312     aDirChg.clear();
1313     aDirType.clear();
1314 
1315     // Perform Unicode Bidi Algorithm for text direction information
1316     bool bPerformUBA = UBIDI_LTR != nDefaultDir;
1317     nCnt = 0;
1318     while( !bPerformUBA && nCnt < CountScriptChg() )
1319     {
1320         if ( i18n::ScriptType::COMPLEX == GetScriptType( nCnt++ ) )
1321             bPerformUBA = true;
1322     }
1323 
1324     // do not call the unicode bidi algorithm if not required
1325     if ( bPerformUBA )
1326     {
1327         UpdateBidiInfo( rTxt );
1328 
1329         // #i16354# Change script type for RTL text to CTL:
1330         // 1. All text in RTL runs will use the CTL font
1331         // #i89825# change the script type also to CTL (hennerdrewes)
1332         // 2. Text in embedded LTR runs that does not have any strong LTR characters (numbers!)
1333         for ( size_t nDirIdx = 0; nDirIdx < aDirChg.size(); ++nDirIdx )
1334         {
1335             const sal_uInt8 nCurrDirType = GetDirType( nDirIdx );
1336                 // nStart ist start of RTL run:
1337                 const xub_StrLen nStart = nDirIdx > 0 ? GetDirChg( nDirIdx - 1 ) : 0;
1338                 // nEnd is end of RTL run:
1339                 const xub_StrLen nEnd = GetDirChg( nDirIdx );
1340 
1341             if ( nCurrDirType % 2 == UBIDI_RTL  || // text in RTL run
1342                 ( nCurrDirType > UBIDI_LTR && !lcl_HasStrongLTR( rTxt, nStart, nEnd ) ) ) // non-strong text in embedded LTR run
1343             {
1344                 // nScriptIdx points into the ScriptArrays:
1345                 size_t nScriptIdx = 0;
1346 
1347                 // Skip entries in ScriptArray which are not inside the RTL run:
1348                 // Make nScriptIdx become the index of the script group with
1349                 // 1. nStartPosOfGroup <= nStart and
1350                 // 2. nEndPosOfGroup > nStart
1351                 while ( GetScriptChg( nScriptIdx ) <= nStart )
1352                     ++nScriptIdx;
1353 
1354                 const xub_StrLen nStartPosOfGroup = nScriptIdx ? GetScriptChg( nScriptIdx - 1 ) : 0;
1355                 const sal_uInt8 nScriptTypeOfGroup = GetScriptType( nScriptIdx );
1356 
1357                 ASSERT( nStartPosOfGroup <= nStart && GetScriptChg( nScriptIdx ) > nStart,
1358                         "Script override with CTL font trouble" )
1359 
1360                 // Check if we have to insert a new script change at
1361                 // position nStart. If nStartPosOfGroup < nStart,
1362                 // we have to insert a new script change:
1363                 if ( nStart > 0 && nStartPosOfGroup < nStart )
1364                 {
1365                     aScriptChg.insert( aScriptChg.begin() + nScriptIdx, nStart );
1366                     aScriptType.insert( aScriptType.begin() + nScriptIdx, nScriptTypeOfGroup );
1367                     ++nScriptIdx;
1368                 }
1369 
1370                 // Remove entries in ScriptArray which end inside the RTL run:
1371                 while ( nScriptIdx < aScriptChg.size() && GetScriptChg( nScriptIdx ) <= nEnd )
1372                 {
1373                     aScriptChg.erase( aScriptChg.begin() + nScriptIdx );
1374                     aScriptType.erase( aScriptType.begin() + nScriptIdx );
1375                 }
1376 
1377                 // Insert a new entry in ScriptArray for the end of the RTL run:
1378                 aScriptChg.insert( aScriptChg.begin() + nScriptIdx, nEnd );
1379                 aScriptType.insert( aScriptType.begin() + nScriptIdx, i18n::ScriptType::COMPLEX );
1380 
1381 #if OSL_DEBUG_LEVEL > 1
1382                 sal_uInt8 nScriptType;
1383                 sal_uInt8 nLastScriptType = i18n::ScriptType::WEAK;
1384                 xub_StrLen nScriptChg;
1385                 xub_StrLen nLastScriptChg = 0;
1386                 (void) nLastScriptChg;
1387                 (void) nLastScriptType;
1388 
1389                 for ( size_t i2 = 0; i2 < aScriptChg.size(); ++i2 )
1390                 {
1391                     nScriptChg = GetScriptChg( i2 );
1392                     nScriptType = GetScriptType( i2 );
1393                     ASSERT( nLastScriptType != nScriptType &&
1394                             nLastScriptChg < nScriptChg,
1395                             "Heavy InitScriptType() confusion" )
1396                 }
1397 #endif
1398             }
1399         }
1400     }
1401 }
1402 
1403 void SwScriptInfo::UpdateBidiInfo( const String& rTxt )
1404 {
1405     // remove invalid entries from direction information arrays
1406     aDirChg.clear();
1407     aDirType.clear();
1408 
1409     //
1410     // Bidi functions from icu 2.0
1411     //
1412     UErrorCode nError = U_ZERO_ERROR;
1413     UBiDi* pBidi = ubidi_openSized( rTxt.Len(), 0, &nError );
1414     nError = U_ZERO_ERROR;
1415 
1416     ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(rTxt.GetBuffer()), rTxt.Len(),	// UChar != sal_Unicode in MinGW
1417                    nDefaultDir, NULL, &nError );
1418     nError = U_ZERO_ERROR;
1419     long nCount = ubidi_countRuns( pBidi, &nError );
1420     int32_t nStart = 0;
1421     int32_t nEnd;
1422     UBiDiLevel nCurrDir;
1423     // counter for direction information arrays
1424 
1425     for ( sal_uInt16 nIdx = 0; nIdx < nCount; ++nIdx )
1426     {
1427         ubidi_getLogicalRun( pBidi, nStart, &nEnd, &nCurrDir );
1428         aDirChg.push_back( (sal_uInt16)nEnd );
1429         aDirType.push_back( (sal_uInt8)nCurrDir );
1430         nStart = nEnd;
1431     }
1432 
1433     ubidi_close( pBidi );
1434 }
1435 
1436 
1437 /*************************************************************************
1438  *						  SwScriptInfo::NextScriptChg(..)
1439  * returns the position of the next character which belongs to another script
1440  * than the character of the actual (input) position.
1441  * If there's no script change until the end of the paragraph, it will return
1442  * STRING_LEN.
1443  * Scripts are Asian (Chinese, Japanese, Korean),
1444  * 			   Latin ( English etc.)
1445  *         and Complex ( Hebrew, Arabian )
1446  *************************************************************************/
1447 
1448 xub_StrLen SwScriptInfo::NextScriptChg( const xub_StrLen nPos )  const
1449 {
1450     sal_uInt16 nEnd = CountScriptChg();
1451     for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1452     {
1453 		if( nPos < GetScriptChg( nX ) )
1454 			return GetScriptChg( nX );
1455     }
1456 
1457 	return STRING_LEN;
1458 }
1459 
1460 /*************************************************************************
1461  *						  SwScriptInfo::ScriptType(..)
1462  * returns the script of the character at the input position
1463  *************************************************************************/
1464 
1465 sal_uInt8 SwScriptInfo::ScriptType( const xub_StrLen nPos ) const
1466 {
1467     sal_uInt16 nEnd = CountScriptChg();
1468     for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1469     {
1470         if( nPos < GetScriptChg( nX ) )
1471 			return GetScriptType( nX );
1472     }
1473 
1474     // the default is the application language script
1475     return (sal_uInt8)GetI18NScriptTypeOfLanguage( (sal_uInt16)GetAppLanguage() );
1476 }
1477 
1478 xub_StrLen SwScriptInfo::NextDirChg( const xub_StrLen nPos,
1479                                      const sal_uInt8* pLevel )  const
1480 {
1481     sal_uInt8 nCurrDir = pLevel ? *pLevel : 62;
1482     sal_uInt16 nEnd = CountDirChg();
1483     for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1484     {
1485         if( nPos < GetDirChg( nX ) &&
1486             ( nX + 1 == nEnd || GetDirType( nX + 1 ) <= nCurrDir ) )
1487             return GetDirChg( nX );
1488     }
1489 
1490 	return STRING_LEN;
1491 }
1492 
1493 sal_uInt8 SwScriptInfo::DirType( const xub_StrLen nPos ) const
1494 {
1495     sal_uInt16 nEnd = CountDirChg();
1496     for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1497     {
1498         if( nPos < GetDirChg( nX ) )
1499             return GetDirType( nX );
1500     }
1501 
1502     return 0;
1503 }
1504 
1505 /*************************************************************************
1506  *                        SwScriptInfo::MaskHiddenRanges(..)
1507  * Takes a string and replaced the hidden ranges with cChar.
1508  **************************************************************************/
1509 
1510 sal_uInt16 SwScriptInfo::MaskHiddenRanges( const SwTxtNode& rNode, XubString& rText,
1511                                        const xub_StrLen nStt, const xub_StrLen nEnd,
1512                                        const xub_Unicode cChar )
1513 {
1514     ASSERT( rNode.GetTxt().Len() == rText.Len(), "MaskHiddenRanges, string len mismatch" )
1515 
1516     PositionList aList;
1517     xub_StrLen nHiddenStart;
1518     xub_StrLen nHiddenEnd;
1519     sal_uInt16 nNumOfHiddenChars = 0;
1520     GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
1521     PositionList::const_reverse_iterator rFirst( aList.end() );
1522     PositionList::const_reverse_iterator rLast( aList.begin() );
1523     while ( rFirst != rLast )
1524     {
1525         nHiddenEnd = *(rFirst++);
1526         nHiddenStart = *(rFirst++);
1527 
1528         if ( nHiddenEnd < nStt || nHiddenStart > nEnd )
1529             continue;
1530 
1531         while ( nHiddenStart < nHiddenEnd && nHiddenStart < nEnd )
1532         {
1533             if ( nHiddenStart >= nStt && nHiddenStart < nEnd )
1534             {
1535                 rText.SetChar( nHiddenStart, cChar );
1536                 ++nNumOfHiddenChars;
1537             }
1538             ++nHiddenStart;
1539         }
1540     }
1541 
1542     return nNumOfHiddenChars;
1543 }
1544 
1545 /*************************************************************************
1546  *                        SwScriptInfo::DeleteHiddenRanges(..)
1547  * Takes a SwTxtNode and deletes the hidden ranges from the node.
1548  **************************************************************************/
1549 
1550 void SwScriptInfo::DeleteHiddenRanges( SwTxtNode& rNode )
1551 {
1552     PositionList aList;
1553     xub_StrLen nHiddenStart;
1554     xub_StrLen nHiddenEnd;
1555     GetBoundsOfHiddenRange( rNode, 0, nHiddenStart, nHiddenEnd, &aList );
1556     PositionList::const_reverse_iterator rFirst( aList.end() );
1557     PositionList::const_reverse_iterator rLast( aList.begin() );
1558     while ( rFirst != rLast )
1559     {
1560         nHiddenEnd = *(rFirst++);
1561         nHiddenStart = *(rFirst++);
1562 
1563         SwPaM aPam( rNode, nHiddenStart, rNode, nHiddenEnd );
1564         rNode.getIDocumentContentOperations()->DeleteRange( aPam );
1565     }
1566 }
1567 
1568 /*************************************************************************
1569  *                        SwScriptInfo::GetBoundsOfHiddenRange(..)
1570  * static version
1571  **************************************************************************/
1572 
1573 bool SwScriptInfo::GetBoundsOfHiddenRange( const SwTxtNode& rNode, xub_StrLen nPos,
1574                                            xub_StrLen& rnStartPos, xub_StrLen& rnEndPos,
1575                                            PositionList* pList )
1576 {
1577     rnStartPos = STRING_LEN;
1578     rnEndPos = 0;
1579 
1580     bool bNewContainsHiddenChars = false;
1581 
1582     //
1583     // Optimization: First examine the flags at the text node:
1584     //
1585     if ( !rNode.IsCalcHiddenCharFlags() )
1586     {
1587         bool bWholePara = rNode.HasHiddenCharAttribute( true );
1588         bool bContainsHiddenChars = rNode.HasHiddenCharAttribute( false );
1589         if ( !bContainsHiddenChars )
1590             return false;
1591 
1592         if ( bWholePara )
1593         {
1594             if ( pList )
1595             {
1596                 pList->push_back( 0 );
1597                 pList->push_back( rNode.GetTxt().Len() );
1598             }
1599 
1600             rnStartPos = 0;
1601             rnEndPos = rNode.GetTxt().Len();
1602             return true;
1603         }
1604     }
1605 
1606     const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo( rNode );
1607     if ( pSI )
1608     {
1609         //
1610         // Check first, if we have a valid SwScriptInfo object for this text node:
1611         //
1612         bNewContainsHiddenChars = pSI->GetBoundsOfHiddenRange( nPos, rnStartPos, rnEndPos, pList );
1613         const bool bNewHiddenCharsHidePara = ( rnStartPos == 0 && rnEndPos >= rNode.GetTxt().Len() );
1614         rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
1615     }
1616     else
1617     {
1618         //
1619         // No valid SwScriptInfo Object, we have to do it the hard way:
1620         //
1621         Range aRange( 0, rNode.GetTxt().Len() ? rNode.GetTxt().Len() - 1 : 0 );
1622         MultiSelection aHiddenMulti( aRange );
1623         SwScriptInfo::CalcHiddenRanges( rNode, aHiddenMulti );
1624         for( sal_uInt16 i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
1625         {
1626             const Range& rRange = aHiddenMulti.GetRange( i );
1627             const xub_StrLen nHiddenStart = (xub_StrLen)rRange.Min();
1628             const xub_StrLen nHiddenEnd = (xub_StrLen)rRange.Max() + 1;
1629 
1630             if ( nHiddenStart > nPos )
1631                 break;
1632             else if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1633             {
1634                 rnStartPos = nHiddenStart;
1635                 rnEndPos   = Min( nHiddenEnd, rNode.GetTxt().Len() );
1636                 break;
1637             }
1638         }
1639 
1640         if ( pList )
1641         {
1642             for( sal_uInt16 i = 0; i < aHiddenMulti.GetRangeCount(); ++i )
1643             {
1644                 const Range& rRange = aHiddenMulti.GetRange( i );
1645                 pList->push_back( (xub_StrLen)rRange.Min() );
1646                 pList->push_back( (xub_StrLen)rRange.Max() + 1 );
1647             }
1648         }
1649 
1650         bNewContainsHiddenChars = aHiddenMulti.GetRangeCount() > 0;
1651     }
1652 
1653     return bNewContainsHiddenChars;
1654 }
1655 
1656 /*************************************************************************
1657  *                        SwScriptInfo::GetBoundsOfHiddenRange(..)
1658  * non-static version
1659  **************************************************************************/
1660 
1661 bool SwScriptInfo::GetBoundsOfHiddenRange( xub_StrLen nPos, xub_StrLen& rnStartPos,
1662                                            xub_StrLen& rnEndPos, PositionList* pList ) const
1663 {
1664     rnStartPos = STRING_LEN;
1665     rnEndPos = 0;
1666 
1667     sal_uInt16 nEnd = CountHiddenChg();
1668     for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1669     {
1670         const xub_StrLen nHiddenStart = GetHiddenChg( nX++ );
1671         const xub_StrLen nHiddenEnd = GetHiddenChg( nX );
1672 
1673         if ( nHiddenStart > nPos )
1674             break;
1675         else if ( nHiddenStart <= nPos && nPos < nHiddenEnd )
1676         {
1677             rnStartPos = nHiddenStart;
1678             rnEndPos   = nHiddenEnd;
1679             break;
1680         }
1681     }
1682 
1683     if ( pList )
1684     {
1685         for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1686         {
1687             pList->push_back( GetHiddenChg( nX++ ) );
1688             pList->push_back( GetHiddenChg( nX ) );
1689         }
1690     }
1691 
1692     return CountHiddenChg() > 0;
1693 }
1694 
1695 /*************************************************************************
1696  *                        SwScriptInfo::IsInHiddenRange()
1697  **************************************************************************/
1698 
1699 bool SwScriptInfo::IsInHiddenRange( const SwTxtNode& rNode, xub_StrLen nPos )
1700 {
1701     xub_StrLen nStartPos;
1702     xub_StrLen nEndPos;
1703     SwScriptInfo::GetBoundsOfHiddenRange( rNode, nPos, nStartPos, nEndPos );
1704     return nStartPos != STRING_LEN;
1705 }
1706 
1707 
1708 #if OSL_DEBUG_LEVEL > 1
1709 /*************************************************************************
1710  *                        SwScriptInfo::CompType(..)
1711  * returns the type of the compressed character
1712  *************************************************************************/
1713 
1714 sal_uInt8 SwScriptInfo::CompType( const xub_StrLen nPos ) const
1715 {
1716     sal_uInt16 nEnd = CountCompChg();
1717     for( sal_uInt16 nX = 0; nX < nEnd; ++nX )
1718     {
1719         xub_StrLen nChg = GetCompStart( nX );
1720 
1721         if ( nPos < nChg )
1722             return NONE;
1723 
1724         if( nPos < nChg + GetCompLen( nX ) )
1725             return GetCompType( nX );
1726     }
1727     return NONE;
1728 }
1729 #endif
1730 
1731 /*************************************************************************
1732  *                      SwScriptInfo::HasKana()
1733  * returns, if there are compressable kanas or specials
1734  * betwenn nStart and nEnd
1735  *************************************************************************/
1736 
1737 sal_uInt16 SwScriptInfo::HasKana( xub_StrLen nStart, const xub_StrLen nLen ) const
1738 {
1739     sal_uInt16 nCnt = CountCompChg();
1740     xub_StrLen nEnd = nStart + nLen;
1741 
1742     for( sal_uInt16 nX = 0; nX < nCnt; ++nX )
1743     {
1744         xub_StrLen nKanaStart  = GetCompStart( nX );
1745         xub_StrLen nKanaEnd = nKanaStart + GetCompLen( nX );
1746 
1747         if ( nKanaStart >= nEnd )
1748             return USHRT_MAX;
1749 
1750         if ( nStart < nKanaEnd )
1751             return nX;
1752     }
1753 
1754     return USHRT_MAX;
1755 }
1756 
1757 /*************************************************************************
1758  *                      SwScriptInfo::Compress()
1759  *************************************************************************/
1760 
1761 long SwScriptInfo::Compress( sal_Int32* pKernArray, xub_StrLen nIdx, xub_StrLen nLen,
1762                              const sal_uInt16 nCompress, const sal_uInt16 nFontHeight,
1763                              Point* pPoint ) const
1764 {
1765 	ASSERT( nCompress, "Compression without compression?!" );
1766 	ASSERT( nLen, "Compression without text?!" );
1767     sal_uInt16 nCompCount = CountCompChg();
1768 
1769     // In asian typography, there are full width and half width characters.
1770     // Full width punctuation characters can be compressed by 50 %
1771     // to determine this, we compare the font width with 75 % of its height
1772     sal_uInt16 nMinWidth = ( 3 * nFontHeight ) / 4;
1773 
1774     sal_uInt16 nCompIdx = HasKana( nIdx, nLen );
1775 
1776     if ( USHRT_MAX == nCompIdx )
1777         return 0;
1778 
1779     xub_StrLen nChg = GetCompStart( nCompIdx );
1780     xub_StrLen nCompLen = GetCompLen( nCompIdx );
1781     sal_uInt16 nI = 0;
1782     nLen = nLen + nIdx;
1783 
1784     if( nChg > nIdx )
1785     {
1786         nI = nChg - nIdx;
1787         nIdx = nChg;
1788     }
1789     else if( nIdx < nChg + nCompLen )
1790         nCompLen -= nIdx - nChg;
1791 
1792     if( nIdx > nLen || nCompIdx >= nCompCount )
1793 		return 0;
1794 
1795     long nSub = 0;
1796 	long nLast = nI ? pKernArray[ nI - 1 ] : 0;
1797 	do
1798 	{
1799         sal_uInt16 nType = GetCompType( nCompIdx );
1800 #if OSL_DEBUG_LEVEL > 1
1801         ASSERT( nType == CompType( nIdx ), "Gimme the right type!" );
1802 #endif
1803 		nCompLen = nCompLen + nIdx;
1804 		if( nCompLen > nLen )
1805 			nCompLen = nLen;
1806 
1807         // are we allowed to compress the character?
1808         if ( pKernArray[ nI ] - nLast < nMinWidth )
1809         {
1810             nIdx++; nI++;
1811         }
1812         else
1813         {
1814             while( nIdx < nCompLen )
1815             {
1816                 ASSERT( SwScriptInfo::NONE != nType, "None compression?!" );
1817 
1818                 // nLast is width of current character
1819                 nLast -= pKernArray[ nI ];
1820 
1821                 nLast *= nCompress;
1822                 long nMove = 0;
1823                 if( SwScriptInfo::KANA != nType )
1824                 {
1825                     nLast /= 20000;
1826                     if( pPoint && SwScriptInfo::SPECIAL_LEFT == nType )
1827                     {
1828                         if( nI )
1829                             nMove = nLast;
1830                         else
1831                         {
1832                             pPoint->X() += nLast;
1833                             nLast = 0;
1834                         }
1835                     }
1836                 }
1837                 else
1838                     nLast /= 100000;
1839                 nSub -= nLast;
1840                 nLast = pKernArray[ nI ];
1841                 if( nMove )
1842                     pKernArray[ nI - 1 ] += nMove;
1843                 pKernArray[ nI++ ] -= nSub;
1844                 ++nIdx;
1845             }
1846         }
1847 
1848         if( nIdx < nLen )
1849 		{
1850             xub_StrLen nTmpChg;
1851 			if( ++nCompIdx < nCompCount )
1852 			{
1853                 nTmpChg = GetCompStart( nCompIdx );
1854                 if( nTmpChg > nLen )
1855                     nTmpChg = nLen;
1856                 nCompLen = GetCompLen( nCompIdx );
1857 			}
1858 			else
1859                 nTmpChg = nLen;
1860             while( nIdx < nTmpChg )
1861 			{
1862 				nLast = pKernArray[ nI ];
1863 				pKernArray[ nI++ ] -= nSub;
1864 				++nIdx;
1865 			}
1866 		}
1867 		else
1868 			break;
1869 	} while( nIdx < nLen );
1870 	return nSub;
1871 }
1872 
1873 /*************************************************************************
1874  *                      SwScriptInfo::KashidaJustify()
1875  *************************************************************************/
1876 
1877 // Note on calling KashidaJustify():
1878 // Kashida positions may be marked as invalid. Therefore KashidaJustify may return the clean
1879 // total number of kashida positions, or the number of kashida positions after some positions
1880 // have been dropped, depending on the state of the aKashidaInvalid array.
1881 
1882 sal_uInt16 SwScriptInfo::KashidaJustify( sal_Int32* pKernArray,
1883                                     sal_Int32* pScrArray,
1884                                     xub_StrLen nStt,
1885                                     xub_StrLen nLen,
1886                                     long nSpaceAdd ) const
1887 {
1888     ASSERT( nLen, "Kashida justification without text?!" )
1889 
1890     if( !IsKashidaLine(nStt))
1891         return STRING_LEN;
1892 
1893     // evaluate kashida informatin in collected in SwScriptInfo
1894 
1895     sal_uInt16 nCntKash = 0;
1896     while( nCntKash < CountKashida() )
1897     {
1898         if ( nStt <= GetKashida( nCntKash ) )
1899             break;
1900         else
1901             nCntKash++;
1902     }
1903 
1904     const xub_StrLen nEnd = nStt + nLen;
1905 
1906     sal_uInt16 nCntKashEnd = nCntKash;
1907     while ( nCntKashEnd < CountKashida() )
1908     {
1909        if ( nEnd <= GetKashida( nCntKashEnd ) )
1910             break;
1911         else
1912             nCntKashEnd++;
1913     }
1914 
1915     sal_uInt16 nActualKashCount = nCntKashEnd - nCntKash;
1916     for ( sal_uInt16 i = nCntKash; i < nCntKashEnd; ++i )
1917     {
1918         if ( nActualKashCount && !IsKashidaValid ( i ) )
1919             --nActualKashCount;
1920     }
1921 
1922     if ( !pKernArray )
1923         return nActualKashCount;
1924 
1925     // do nothing if there is no more kashida
1926     if ( nCntKash < CountKashida() )
1927     {
1928         // skip any invalid kashidas
1929         while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd )
1930             ++nCntKash;
1931 
1932         xub_StrLen nKashidaPos = GetKashida( nCntKash );
1933         xub_StrLen nIdx = nKashidaPos;
1934         long nKashAdd = nSpaceAdd;
1935 
1936         while ( nIdx < nEnd )
1937         {
1938             sal_uInt16 nArrayPos = nIdx - nStt;
1939 
1940             // next kashida position
1941             ++nCntKash;
1942             while ( ! IsKashidaValid ( nCntKash ) && nCntKash < nCntKashEnd )
1943                 ++nCntKash;
1944 
1945             nIdx = nCntKash < CountKashida() && IsKashidaValid ( nCntKash ) ? GetKashida( nCntKash ) : nEnd;
1946             if ( nIdx > nEnd )
1947                 nIdx = nEnd;
1948 
1949             const sal_uInt16 nArrayEnd = nIdx - nStt;
1950 
1951             while ( nArrayPos < nArrayEnd )
1952             {
1953                 pKernArray[ nArrayPos ] += nKashAdd;
1954                 if ( pScrArray )
1955                     pScrArray[ nArrayPos ] += nKashAdd;
1956                 ++nArrayPos;
1957             }
1958             nKashAdd += nSpaceAdd;
1959         }
1960     }
1961 
1962     return 0;
1963 }
1964 
1965 /*************************************************************************
1966  *                      SwScriptInfo::IsArabicText()
1967  *
1968  * Checks if the current text is 'Arabic' text. Note that only the first
1969  * character has to be checked because a ctl portion only contains one
1970  * script, see NewTxtPortion
1971  *************************************************************************/
1972 sal_Bool SwScriptInfo::IsArabicText( const XubString& rTxt, xub_StrLen nStt, xub_StrLen nLen )
1973 {
1974     using namespace ::com::sun::star::i18n;
1975     static ScriptTypeList typeList[] = {
1976         { UnicodeScript_kArabic, UnicodeScript_kArabic, UnicodeScript_kArabic },        // 11,
1977         { UnicodeScript_kScriptCount, UnicodeScript_kScriptCount, UnicodeScript_kScriptCount }    // 88
1978     };
1979 
1980     // go forward if current position does not hold a regular character:
1981     const CharClass& rCC = GetAppCharClass();
1982     sal_Int32 nIdx = nStt;
1983     const xub_StrLen nEnd = nStt + nLen;
1984     while ( nIdx < nEnd && !rCC.isLetterNumeric( rTxt, (xub_StrLen)nIdx ) )
1985     {
1986         ++nIdx;
1987     }
1988 
1989     if( nIdx == nEnd )
1990     {
1991         // no regular character found in this portion. Go backward:
1992         --nIdx;
1993         while ( nIdx >= 0 && !rCC.isLetterNumeric( rTxt, (xub_StrLen)nIdx ) )
1994         {
1995             --nIdx;
1996         }
1997     }
1998 
1999     if( nIdx >= 0 )
2000     {
2001         const xub_Unicode cCh = rTxt.GetChar( (xub_StrLen)nIdx );
2002         const sal_Int16 type = unicode::getUnicodeScriptType( cCh, typeList, UnicodeScript_kScriptCount );
2003         return type == UnicodeScript_kArabic;
2004     }
2005 	return sal_False;
2006 }
2007 
2008 /*************************************************************************
2009  *                      SwScriptInfo::IsKashidaValid()
2010  *************************************************************************/
2011 
2012 sal_Bool SwScriptInfo::IsKashidaValid ( xub_StrLen nKashPos ) const
2013 {
2014     for ( size_t i = 0; i < aKashidaInvalid.size(); ++i )
2015     {
2016         if ( aKashidaInvalid [ i ] == nKashPos )
2017             return false;
2018     }
2019     return true;
2020 }
2021 
2022 /*************************************************************************
2023  *                      SwScriptInfo::ClearKashidaInvalid()
2024  *************************************************************************/
2025 
2026 void SwScriptInfo::ClearKashidaInvalid ( xub_StrLen nKashPos )
2027 {
2028     for ( size_t i = 0; i < aKashidaInvalid.size(); ++i )
2029     {
2030         if ( aKashidaInvalid [ i ] == nKashPos )
2031         {
2032            aKashidaInvalid.erase ( aKashidaInvalid.begin() + i );
2033            return;
2034         }
2035     }
2036 }
2037 
2038 /*************************************************************************
2039  *                      SwScriptInfo::MarkOrClearKashidaInvalid()
2040  *************************************************************************/
2041 // bMark == true:
2042 // marks the first valid kashida in the given text range as invalid
2043 
2044 // bMark == false:
2045 // clears all kashida invalid flags in the given text range
2046 
2047 bool SwScriptInfo::MarkOrClearKashidaInvalid ( xub_StrLen nStt, xub_StrLen nLen, bool bMark, xub_StrLen nMarkCount )
2048 {
2049     sal_uInt16 nCntKash = 0;
2050     while( nCntKash < CountKashida() )
2051     {
2052         if ( nStt <= GetKashida( nCntKash ) )
2053             break;
2054         else
2055             nCntKash++;
2056     }
2057 
2058     const xub_StrLen nEnd = nStt + nLen;
2059 
2060     while ( nCntKash < CountKashida() )
2061     {
2062         if ( nEnd <= GetKashida( nCntKash ) )
2063             break;
2064         else
2065         {
2066             if(bMark)
2067             {
2068                 if ( IsKashidaValid ( nCntKash ) )
2069                 {
2070                     MarkKashidaInvalid ( nCntKash );
2071                     --nMarkCount;
2072                     if(!nMarkCount)
2073                        return true;
2074                 }
2075             }
2076             else
2077             {
2078                 ClearKashidaInvalid ( nCntKash );
2079             }
2080             nCntKash++;
2081         }
2082     }
2083     return false;
2084 }
2085 
2086 void SwScriptInfo::MarkKashidaInvalid ( xub_StrLen nKashPos )
2087 {
2088     aKashidaInvalid.push_back( nKashPos );
2089 }
2090 
2091 /*************************************************************************
2092  *                      SwScriptInfo::GetKashidaPositions()
2093  *************************************************************************/
2094 // retrieve the kashida positions in the given text range
2095 sal_uInt16 SwScriptInfo::GetKashidaPositions ( xub_StrLen nStt, xub_StrLen nLen,
2096                                            xub_StrLen* pKashidaPosition )
2097 {
2098     sal_uInt16 nCntKash = 0;
2099     while( nCntKash < CountKashida() )
2100     {
2101         if ( nStt <= GetKashida( nCntKash ) )
2102             break;
2103         else
2104             nCntKash++;
2105     }
2106 
2107     const xub_StrLen nEnd = nStt + nLen;
2108 
2109     sal_uInt16 nCntKashEnd = nCntKash;
2110     while ( nCntKashEnd < CountKashida() )
2111     {
2112        if ( nEnd <= GetKashida( nCntKashEnd ) )
2113             break;
2114         else
2115         {
2116             pKashidaPosition [ nCntKashEnd - nCntKash ] = GetKashida ( nCntKashEnd );
2117             nCntKashEnd++;
2118         }
2119     }
2120     return nCntKashEnd - nCntKash;
2121 }
2122 
2123 void SwScriptInfo::SetNoKashidaLine ( xub_StrLen nStt, xub_StrLen nLen )
2124 {
2125     aNoKashidaLine.push_back( nStt );
2126     aNoKashidaLineEnd.push_back( nStt+nLen );
2127 }
2128 
2129 /*************************************************************************
2130  *                      SwScriptInfo::IsKashidaLine()
2131  *************************************************************************/
2132 // determines if the line uses kashida justification
2133 
2134 bool SwScriptInfo::IsKashidaLine ( xub_StrLen nCharIdx ) const
2135 {
2136    for( size_t i = 0; i < aNoKashidaLine.size(); ++i )
2137     {
2138        if( nCharIdx >= aNoKashidaLine[ i ] && nCharIdx < aNoKashidaLineEnd[ i ])
2139            return false;
2140     }
2141    return true;
2142 }
2143 /*************************************************************************
2144  *                      SwScriptInfo::ClearKashidaLine()
2145  *************************************************************************/
2146 
2147 void SwScriptInfo::ClearNoKashidaLine ( xub_StrLen nStt, xub_StrLen nLen )
2148 {
2149    size_t i = 0;
2150    while( i < aNoKashidaLine.size())
2151    {
2152        if( nStt + nLen >= aNoKashidaLine[ i ] && nStt < aNoKashidaLineEnd [ i ] )
2153        {
2154            aNoKashidaLine.erase(aNoKashidaLine.begin() + i);
2155            aNoKashidaLineEnd.erase(aNoKashidaLineEnd.begin() + i);
2156        }
2157        else
2158            ++i;
2159    }
2160 }
2161 
2162 /*************************************************************************
2163  *                      SwScriptInfo::MarkKashidasInvalid()
2164  *************************************************************************/
2165 // mark the given character indices as invalid kashida positions
2166 bool SwScriptInfo::MarkKashidasInvalid ( xub_StrLen nCnt, xub_StrLen* pKashidaPositions )
2167 {
2168    ASSERT( pKashidaPositions && nCnt > 0, "Where are kashidas?" )
2169 
2170    sal_uInt16 nCntKash = 0;
2171    xub_StrLen nKashidaPosIdx = 0;
2172 
2173     while ( nCntKash < CountKashida() && nKashidaPosIdx < nCnt )
2174     {
2175        if ( pKashidaPositions [nKashidaPosIdx] > GetKashida( nCntKash ) )
2176        {
2177            nCntKash++;
2178            continue;
2179        }
2180 
2181         if ( pKashidaPositions [nKashidaPosIdx] == GetKashida( nCntKash ) && IsKashidaValid ( nCntKash ) )
2182        {
2183             MarkKashidaInvalid ( nCntKash );
2184        }
2185        else
2186            return false; // something is wrong
2187        nKashidaPosIdx++;
2188    }
2189    return true;
2190 }
2191 
2192 /*************************************************************************
2193  *                      SwScriptInfo::ThaiJustify()
2194  *************************************************************************/
2195 
2196 sal_uInt16 SwScriptInfo::ThaiJustify( const XubString& rTxt, sal_Int32* pKernArray,
2197                                   sal_Int32* pScrArray, xub_StrLen nStt,
2198                                   xub_StrLen nLen, xub_StrLen nNumberOfBlanks,
2199                                   long nSpaceAdd )
2200 {
2201     ASSERT( nStt + nLen <= rTxt.Len(), "String in ThaiJustify too small" )
2202 
2203     SwTwips nNumOfTwipsToDistribute = nSpaceAdd * nNumberOfBlanks /
2204                                       SPACING_PRECISION_FACTOR;
2205 
2206     long nSpaceSum = 0;
2207     sal_uInt16 nCnt = 0;
2208 
2209     for ( sal_uInt16 nI = 0; nI < nLen; ++nI )
2210     {
2211         const xub_Unicode cCh = rTxt.GetChar( nStt + nI );
2212 
2213         // check if character is not above or below base
2214         if ( ( 0xE34 > cCh || cCh > 0xE3A ) &&
2215              ( 0xE47 > cCh || cCh > 0xE4E ) && cCh != 0xE31 )
2216         {
2217             if ( nNumberOfBlanks > 0 )
2218             {
2219                 nSpaceAdd = nNumOfTwipsToDistribute / nNumberOfBlanks;
2220                 --nNumberOfBlanks;
2221                 nNumOfTwipsToDistribute -= nSpaceAdd;
2222             }
2223             nSpaceSum += nSpaceAdd;
2224             ++nCnt;
2225         }
2226 
2227         if ( pKernArray ) pKernArray[ nI ] += nSpaceSum;
2228         if ( pScrArray ) pScrArray[ nI ] += nSpaceSum;
2229     }
2230 
2231     return nCnt;
2232 }
2233 
2234 /*************************************************************************
2235  *                      SwScriptInfo::GetScriptInfo()
2236  *************************************************************************/
2237 
2238 SwScriptInfo* SwScriptInfo::GetScriptInfo( const SwTxtNode& rTNd,
2239                                            sal_Bool bAllowInvalid )
2240 {
2241     SwIterator<SwTxtFrm,SwTxtNode> aIter( rTNd );
2242     SwScriptInfo* pScriptInfo = 0;
2243 
2244     for( SwTxtFrm* pLast = aIter.First(); pLast; pLast = aIter.Next() )
2245     {
2246             pScriptInfo = (SwScriptInfo*)pLast->GetScriptInfo();
2247             if ( pScriptInfo )
2248             {
2249                 if ( !bAllowInvalid && STRING_LEN != pScriptInfo->GetInvalidity() )
2250                     pScriptInfo = 0;
2251                 else break;
2252             }
2253         }
2254 
2255     return pScriptInfo;
2256 }
2257 
2258 /*************************************************************************
2259  *                      SwParaPortion::SwParaPortion()
2260  *************************************************************************/
2261 SwParaPortion::SwParaPortion()
2262 {
2263 	FormatReset();
2264 	bFlys = bFtnNum = bMargin = sal_False;
2265 	SetWhichPor( POR_PARA );
2266 }
2267 
2268 /*************************************************************************
2269  *                      SwParaPortion::~SwParaPortion()
2270  *************************************************************************/
2271 SwParaPortion::~SwParaPortion()
2272 {
2273 }
2274 
2275 /*************************************************************************
2276  *						SwParaPortion::GetParLen()
2277  *************************************************************************/
2278 xub_StrLen SwParaPortion::GetParLen() const
2279 {
2280 	xub_StrLen nLen = 0;
2281 	const SwLineLayout *pLay = this;
2282 	while( pLay )
2283 	{
2284 		DBG_LOOP;
2285 		nLen = nLen + pLay->GetLen();
2286 		pLay = pLay->GetNext();
2287 	}
2288 	return nLen;
2289 }
2290 
2291 /*************************************************************************
2292  *						SwParaPortion::FindDropPortion()
2293  *************************************************************************/
2294 
2295 const SwDropPortion *SwParaPortion::FindDropPortion() const
2296 {
2297 	const SwLineLayout *pLay = this;
2298 	while( pLay && pLay->IsDummy() )
2299 		pLay = pLay->GetNext();
2300 	while( pLay )
2301 	{
2302 		const SwLinePortion *pPos = pLay->GetPortion();
2303 		while ( pPos && !pPos->GetLen() )
2304 			pPos = pPos->GetPortion();
2305 		if( pPos && pPos->IsDropPortion() )
2306 			return (SwDropPortion *)pPos;
2307 		pLay = pLay->GetLen() ? NULL : pLay->GetNext();
2308 	}
2309 	return NULL;
2310 }
2311 
2312 /*************************************************************************
2313  *						SwLineLayout::Init()
2314  *************************************************************************/
2315 
2316 void SwLineLayout::Init( SwLinePortion* pNextPortion )
2317 {
2318 	Height( 0 );
2319 	Width( 0 );
2320 	SetLen( 0 );
2321 	SetAscent( 0 );
2322     SetRealHeight( 0 );
2323 	SetPortion( pNextPortion );
2324 }
2325 
2326 /*-----------------16.11.00 11:04-------------------
2327  * HangingMargin()
2328  * looks for hanging punctuation portions in the paragraph
2329  * and return the maximum right offset of them.
2330  * If no such portion is found, the Margin/Hanging-flags will be atualized.
2331  * --------------------------------------------------*/
2332 
2333 SwTwips SwLineLayout::_GetHangingMargin() const
2334 {
2335 	SwLinePortion* pPor = GetPortion();
2336 	sal_Bool bFound = sal_False;
2337 	SwTwips nDiff = 0;
2338 	while( pPor)
2339 	{
2340 		if( pPor->IsHangingPortion() )
2341 		{
2342 			nDiff = ((SwHangingPortion*)pPor)->GetInnerWidth() - pPor->Width();
2343 			if( nDiff )
2344 				bFound = sal_True;
2345 		}
2346         // the last post its portion
2347         else if ( pPor->IsPostItsPortion() && ! pPor->GetPortion() )
2348             nDiff = nAscent;
2349 
2350         pPor = pPor->GetPortion();
2351 	}
2352 	if( !bFound ) // actualize the hanging-flag
2353 		((SwLineLayout*)this)->SetHanging( sal_False );
2354 	return nDiff;
2355 }
2356 
2357 SwTwips SwTxtFrm::HangingMargin() const
2358 {
2359 	ASSERT( HasPara(), "Don't call me without a paraportion" );
2360 	if( !GetPara()->IsMargin() )
2361 		return 0;
2362 	const SwLineLayout* pLine = GetPara();
2363 	SwTwips nRet = 0;
2364 	do
2365 	{
2366 		SwTwips nDiff = pLine->GetHangingMargin();
2367 		if( nDiff > nRet )
2368 			nRet = nDiff;
2369 		pLine = pLine->GetNext();
2370 	} while ( pLine );
2371 	if( !nRet ) // actualize the margin-flag
2372 		((SwParaPortion*)GetPara())->SetMargin( sal_False );
2373 	return nRet;
2374 }
2375 
2376 
2377 /*************************************************************************
2378  *                      SwScriptInfo::CalcHiddenRanges()
2379  *
2380  * Returns a MultiSection indicating the hidden ranges.
2381  *************************************************************************/
2382 
2383 void SwScriptInfo::CalcHiddenRanges( const SwTxtNode& rNode, MultiSelection& rHiddenMulti )
2384 {
2385     const SfxPoolItem* pItem = 0;
2386     if( SFX_ITEM_SET == rNode.GetSwAttrSet().GetItemState( RES_CHRATR_HIDDEN, sal_True, &pItem ) &&
2387         ((SvxCharHiddenItem*)pItem)->GetValue() )
2388     {
2389         rHiddenMulti.SelectAll();
2390     }
2391 
2392     const SwpHints* pHints = rNode.GetpSwpHints();
2393     const SwTxtAttr* pTxtAttr = 0;
2394 
2395     if( pHints )
2396     {
2397         MSHORT nTmp = 0;
2398 
2399         while( nTmp < pHints->GetStartCount() )
2400         {
2401             pTxtAttr = pHints->GetStart( nTmp++ );
2402             const SvxCharHiddenItem* pHiddenItem = static_cast<const SvxCharHiddenItem*>( CharFmt::GetItem( *pTxtAttr, RES_CHRATR_HIDDEN ) );
2403             if( pHiddenItem )
2404             {
2405                 xub_StrLen nSt = *pTxtAttr->GetStart();
2406                 xub_StrLen nEnd = *pTxtAttr->GetEnd();
2407                 if( nEnd > nSt )
2408                 {
2409                     Range aTmp( nSt, nEnd - 1 );
2410                     rHiddenMulti.Select( aTmp, pHiddenItem->GetValue() );
2411                 }
2412             }
2413         }
2414     }
2415 
2416     // If there are any hidden ranges in the current text node, we have
2417     // to unhide the redlining ranges:
2418     const IDocumentRedlineAccess& rIDRA = *rNode.getIDocumentRedlineAccess();
2419     if ( rHiddenMulti.GetRangeCount() && IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineMode() ) )
2420     {
2421         sal_uInt16 nAct = rIDRA.GetRedlinePos( rNode, USHRT_MAX );
2422 
2423         for ( ; nAct < rIDRA.GetRedlineTbl().Count(); nAct++ )
2424         {
2425             const SwRedline* pRed = rIDRA.GetRedlineTbl()[ nAct ];
2426 
2427             if ( pRed->Start()->nNode > rNode.GetIndex() )
2428                 break;
2429 
2430             xub_StrLen nRedlStart;
2431             xub_StrLen nRedlnEnd;
2432             pRed->CalcStartEnd( rNode.GetIndex(), nRedlStart, nRedlnEnd );
2433             if ( nRedlnEnd > nRedlStart )
2434             {
2435                 Range aTmp( nRedlStart, nRedlnEnd - 1 );
2436                 rHiddenMulti.Select( aTmp, false );
2437             }
2438         }
2439     }
2440 
2441     //
2442     // We calculated a lot of stuff. Finally we can update the flags at the text node.
2443     //
2444     const bool bNewContainsHiddenChars = rHiddenMulti.GetRangeCount() > 0;
2445     bool bNewHiddenCharsHidePara = false;
2446     if ( bNewContainsHiddenChars )
2447     {
2448         const Range& rRange = rHiddenMulti.GetRange( 0 );
2449         const xub_StrLen nHiddenStart = (xub_StrLen)rRange.Min();
2450         const xub_StrLen nHiddenEnd = (xub_StrLen)rRange.Max() + 1;
2451         bNewHiddenCharsHidePara = ( nHiddenStart == 0 && nHiddenEnd >= rNode.GetTxt().Len() );
2452     }
2453     rNode.SetHiddenCharAttribute( bNewHiddenCharsHidePara, bNewContainsHiddenChars );
2454 }
2455 
2456