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