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 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 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 83 sal_Bool isDalChar ( xub_Unicode cCh ) 84 { 85 return ( cCh == 0x62F || cCh == 0x630 || cCh == 0x688 || cCh == 0x689 || cCh == 0x690 ); 86 } 87 88 sal_Bool isRehChar ( xub_Unicode cCh ) 89 { 90 return ( cCh == 0x631 || cCh == 0x632 || ( cCh >= 0x691 && cCh <= 0x699 )); 91 } 92 93 sal_Bool isTehMarbutaChar ( xub_Unicode cCh ) 94 { 95 return ( cCh == 0x629 || cCh == 0x6C0 ); 96 } 97 98 sal_Bool isBaaChar ( xub_Unicode cCh ) 99 { 100 return ( cCh == 0x628 || cCh == 0x62A || cCh == 0x62B || cCh == 0x679 || cCh == 0x680 ); 101 } 102 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 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 115 sal_Bool isHahChar ( xub_Unicode cCh ) 116 { 117 return ( ( cCh >= 0x62C && cCh <= 0x62E ) || ( cCh >= 0x681 && cCh <= 0x687 ) 118 || cCh == 0x6BF ); 119 } 120 121 sal_Bool isAinChar ( xub_Unicode cCh ) 122 { 123 return ( cCh == 0x639 || cCh == 0x63A || cCh == 0x6A0 || cCh == 0x6FC ); 124 } 125 126 sal_Bool isKafChar ( xub_Unicode cCh ) 127 { 128 return ( cCh == 0x643 || ( cCh >= 0x6AC && cCh <= 0x6AE ) ); 129 } 130 131 sal_Bool isLamChar ( xub_Unicode cCh ) 132 { 133 return ( cCh == 0x644 || ( cCh >= 0x6B5 && cCh <= 0x6B8 ) ); 134 } 135 136 sal_Bool isGafChar ( xub_Unicode cCh ) 137 { 138 return ( cCh == 0x6A9 || cCh == 0x6AB ||( cCh >= 0x6AF && cCh <= 0x6B4 ) ); 139 } 140 141 sal_Bool isQafChar ( xub_Unicode cCh ) 142 { 143 return ( cCh == 0x642 || cCh == 0x6A7 || cCh == 0x6A8 ); 144 } 145 146 sal_Bool isFeChar ( xub_Unicode cCh ) 147 { 148 return ( cCh == 0x641 || ( cCh >= 0x6A1 && cCh <= 0x6A6 ) ); 149 } 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 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 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 *************************************************************************/ 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 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 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 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 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 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 356 void SwLineLayout::InitSpaceAdd() 357 { 358 if ( !pLLSpaceAdd ) 359 CreateSpaceAdd(); 360 else 361 SetLLSpaceAdd( 0, 0 ); 362 } 363 364 /************************************************************************* 365 * SwLineLayout::CreateSpaceAdd() 366 *************************************************************************/ 367 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 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 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. 659 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 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 *************************************************************************/ 735 SwScriptInfo::SwScriptInfo() : 736 nInvalidityPos( 0 ), 737 nDefaultDir( 0 ) 738 { 739 }; 740 741 /************************************************************************* 742 * SwScriptInfo::~SwScriptInfo() 743 *************************************************************************/ 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 *************************************************************************/ 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 782 void SwScriptInfo::InitScriptInfo( const SwTxtNode& rNode ) 783 { 784 InitScriptInfo( rNode, nDefaultDir == UBIDI_RTL ); 785 } 786 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 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 1444 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 1461 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 1474 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 1489 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 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 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 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 1657 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 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 1710 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 1733 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 1757 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 1878 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 *************************************************************************/ 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 2008 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 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 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 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 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 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 2130 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 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 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 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 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 *************************************************************************/ 2257 SwParaPortion::SwParaPortion() 2258 { 2259 FormatReset(); 2260 bFlys = bFtnNum = bMargin = sal_False; 2261 SetWhichPor( POR_PARA ); 2262 } 2263 2264 /************************************************************************* 2265 * SwParaPortion::~SwParaPortion() 2266 *************************************************************************/ 2267 SwParaPortion::~SwParaPortion() 2268 { 2269 } 2270 2271 /************************************************************************* 2272 * SwParaPortion::GetParLen() 2273 *************************************************************************/ 2274 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 2291 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 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 2329 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 2353 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 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 xub_StrLen nSt = *pTxtAttr->GetStart(); 2402 xub_StrLen nEnd = *pTxtAttr->GetEnd(); 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