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