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 #include "hintids.hxx" 32 33 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ 34 #include <com/sun/star/i18n/ScriptType.hdl> 35 #endif 36 #include <editeng/lspcitem.hxx> 37 #include <txtftn.hxx> 38 #include <fmtftn.hxx> 39 #include <ftninfo.hxx> 40 #include <charfmt.hxx> 41 #include <editeng/charrotateitem.hxx> 42 #include <layfrm.hxx> // GetFrmRstHeight, etc 43 #include <viewsh.hxx> 44 #include <viewopt.hxx> // SwViewOptions 45 #include <paratr.hxx> // SwFmtDrop 46 #include <txtcfg.hxx> 47 #include <itrform2.hxx> 48 #include <porrst.hxx> 49 #include <portab.hxx> // pLastTab-> 50 #include <porfly.hxx> // CalcFlyWidth 51 #include <portox.hxx> // WhichTxtPortion 52 #include <porref.hxx> // WhichTxtPortion 53 #include <porfld.hxx> // SwNumberPortion fuer CalcAscent() 54 #include <porftn.hxx> // SwFtnPortion 55 #include <porhyph.hxx> 56 #include <guess.hxx> 57 #include <blink.hxx> // pBlink 58 #include <ftnfrm.hxx> // WhichFirstPortion() -> mal Verlagern. 59 #include <redlnitr.hxx> // SwRedlineItr 60 #include <pagefrm.hxx> 61 #include <pagedesc.hxx> // SwPageDesc 62 #include <tgrditem.hxx> 63 #include <doc.hxx> // SwDoc 64 #include <pormulti.hxx> // SwMultiPortion 65 #define _SVSTDARR_LONGS 66 #include <svl/svstdarr.hxx> 67 #include <unotools/charclass.hxx> 68 69 #if OSL_DEBUG_LEVEL > 1 70 #include <ndtxt.hxx> // pSwpHints, Ausgabeoperator 71 #endif 72 73 using namespace ::com::sun::star; 74 75 extern sal_Bool IsUnderlineBreak( const SwLinePortion& rPor, const SwFont& rFnt ); 76 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos ); 77 78 #define MAX_TXTPORLEN 300 79 80 inline void ClearFly( SwTxtFormatInfo &rInf ) 81 { 82 if( rInf.GetFly() ) 83 { 84 delete rInf.GetFly(); 85 rInf.SetFly(0); 86 } 87 } 88 89 /************************************************************************* 90 * SwTxtFormatter::CtorInitTxtFormatter() 91 *************************************************************************/ 92 93 void SwTxtFormatter::CtorInitTxtFormatter( SwTxtFrm *pNewFrm, SwTxtFormatInfo *pNewInf ) 94 { 95 CtorInitTxtPainter( pNewFrm, pNewInf ); 96 pInf = pNewInf; 97 pDropFmt = GetInfo().GetDropFmt(); 98 pMulti = NULL; 99 100 bOnceMore = sal_False; 101 bFlyInCntBase = sal_False; 102 bChanges = sal_False; 103 bTruncLines = sal_False; 104 nCntEndHyph = 0; 105 nCntMidHyph = 0; 106 nLeftScanIdx = STRING_LEN; 107 nRightScanIdx = 0; 108 m_nHintEndIndex = 0; 109 110 if( nStart > GetInfo().GetTxt().Len() ) 111 { 112 ASSERT( !this, "+SwTxtFormatter::CTOR: bad offset" ); 113 nStart = GetInfo().GetTxt().Len(); 114 } 115 116 } 117 118 /************************************************************************* 119 * SwTxtFormatter::DTOR 120 *************************************************************************/ 121 122 SwTxtFormatter::~SwTxtFormatter() 123 { 124 // Auesserst unwahrscheinlich aber denkbar. 125 // z.B.: Feld spaltet sich auf, Widows schlagen zu 126 if( GetInfo().GetRest() ) 127 { 128 delete GetInfo().GetRest(); 129 GetInfo().SetRest(0); 130 } 131 } 132 133 /************************************************************************* 134 * SwTxtFormatter::Insert() 135 *************************************************************************/ 136 137 void SwTxtFormatter::Insert( SwLineLayout *pLay ) 138 { 139 // Einfuegen heute mal ausnahmsweise hinter dem aktuellen Element. 140 if ( pCurr ) 141 { 142 pLay->SetNext( pCurr->GetNext() ); 143 pCurr->SetNext( pLay ); 144 } 145 else 146 pCurr = pLay; 147 } 148 149 /************************************************************************* 150 * SwTxtFormatter::GetFrmRstHeight() 151 *************************************************************************/ 152 153 KSHORT SwTxtFormatter::GetFrmRstHeight() const 154 { 155 // 8725: Uns interessiert die Resthoehe bezogen auf die Seite. 156 // Wenn wir in einer Tabelle stehen, dann ist pFrm->GetUpper() nicht 157 // die Seite. GetFrmRstHeight() wird im Zusammenhang mit den Ftn 158 // gerufen. 159 // Falsch: const SwFrm *pUpper = pFrm->GetUpper(); 160 const SwFrm *pPage = (const SwFrm*)pFrm->FindPageFrm(); 161 const SwTwips nHeight = pPage->Frm().Top() 162 + pPage->Prt().Top() 163 + pPage->Prt().Height() - Y(); 164 if( 0 > nHeight ) 165 return pCurr->Height(); 166 else 167 return KSHORT( nHeight ); 168 } 169 170 /************************************************************************* 171 * SwTxtFormatter::UnderFlow() 172 *************************************************************************/ 173 174 SwLinePortion *SwTxtFormatter::UnderFlow( SwTxtFormatInfo &rInf ) 175 { 176 // Werte sichern und rInf initialisieren. 177 SwLinePortion *pUnderFlow = rInf.GetUnderFlow(); 178 if( !pUnderFlow ) 179 return 0; 180 181 // Wir formatieren rueckwaerts, d.h. dass Attributwechsel in der 182 // naechsten Zeile durchaus noch einmal drankommen koennen. 183 // Zu beobachten in 8081.sdw, wenn man in der ersten Zeile Text eingibt. 184 185 const xub_StrLen nSoftHyphPos = rInf.GetSoftHyphPos(); 186 const xub_StrLen nUnderScorePos = rInf.GetUnderScorePos(); 187 188 // 8358, 8359: Flys sichern und auf 0 setzen, sonst GPF 189 // 3983: Nicht ClearFly(rInf) ! 190 SwFlyPortion *pFly = rInf.GetFly(); 191 rInf.SetFly( 0 ); 192 193 FeedInf( rInf ); 194 rInf.SetLast( pCurr ); 195 // pUnderFlow braucht nicht deletet werden, weil es im folgenden 196 // Truncate() untergehen wird. 197 rInf.SetUnderFlow(0); 198 rInf.SetSoftHyphPos( nSoftHyphPos ); 199 rInf.SetUnderScorePos( nUnderScorePos ); 200 rInf.SetPaintOfst( GetLeftMargin() ); 201 202 // Wir suchen die Portion mit der Unterlaufposition 203 SwLinePortion *pPor = pCurr->GetFirstPortion(); 204 if( pPor != pUnderFlow ) 205 { 206 // pPrev wird die letzte Portion vor pUnderFlow, 207 // die noch eine echte Breite hat. 208 // Ausnahme: SoftHyphPortions duerfen dabei natuerlich 209 // nicht vergessen werden, obwohl sie keine Breite haben. 210 SwLinePortion *pTmpPrev = pPor; 211 while( pPor && pPor != pUnderFlow ) 212 { 213 DBG_LOOP; 214 if( !pPor->IsKernPortion() && 215 ( pPor->Width() || pPor->IsSoftHyphPortion() ) ) 216 { 217 while( pTmpPrev != pPor ) 218 { 219 pTmpPrev->Move( rInf ); 220 rInf.SetLast( pTmpPrev ); 221 pTmpPrev = pTmpPrev->GetPortion(); 222 ASSERT( pTmpPrev, "UnderFlow: Loosing control!" ); 223 }; 224 } 225 pPor = pPor->GetPortion(); 226 } 227 pPor = pTmpPrev; 228 if( pPor && // Flies + Initialen werden nicht beim UnderFlow mitgenommen 229 ( pPor->IsFlyPortion() || pPor->IsDropPortion() || 230 pPor->IsFlyCntPortion() ) ) 231 { 232 pPor->Move( rInf ); 233 rInf.SetLast( pPor ); 234 rInf.SetStopUnderFlow( sal_True ); 235 pPor = pUnderFlow; 236 } 237 } 238 239 // Was? Die Unterlaufsituation ist nicht in der Portion-Kette ? 240 ASSERT( pPor, "SwTxtFormatter::UnderFlow: overflow but underflow" ); 241 242 // OD 2004-05-26 #i29529# - correction: no delete of footnotes 243 // if( rInf.IsFtnInside() && pPor && !rInf.IsQuick() ) 244 // { 245 // SwLinePortion *pTmp = pPor->GetPortion(); 246 // while( pTmp ) 247 // { 248 // if( pTmp->IsFtnPortion() ) 249 // ((SwFtnPortion*)pTmp)->ClearFtn(); 250 // pTmp = pTmp->GetPortion(); 251 // } 252 // } 253 254 /*-----------------14.12.94 09:45------------------- 255 * 9849: Schnellschuss 256 * --------------------------------------------------*/ 257 if ( pPor==rInf.GetLast() ) 258 { 259 // Hier landen wir, wenn die UnderFlow-ausloesende Portion sich 260 // ueber die ganze Zeile erstreckt, z. B. wenn ein Wort ueber 261 // mehrere Zeilen geht und in der zweiten Zeile in einen Fly 262 // hineinlaeuft! 263 rInf.SetFly( pFly ); // wg. 28300 264 pPor->Truncate(); 265 return pPor; // Reicht das? 266 } 267 /*--------------------------------------------------- 268 * Ende des Schnellschusses wg. 9849 269 * --------------------------------------------------*/ 270 271 // 4656: X + Width == 0 bei SoftHyph > Zeile ?! 272 if( !pPor || !(rInf.X() + pPor->Width()) ) 273 { 274 delete pFly; 275 return 0; 276 } 277 278 // Vorbereitungen auf's Format() 279 // Wir muessen die Kette hinter pLast abknipsen, weil 280 // nach dem Format() ein Insert erfolgt. 281 SeekAndChg( rInf ); 282 283 // line width is adjusted, so that pPor does not fit to current 284 // line anymore 285 rInf.Width( (sal_uInt16)(rInf.X() + (pPor->Width() ? pPor->Width() - 1 : 0)) ); 286 rInf.SetLen( pPor->GetLen() ); 287 rInf.SetFull( sal_False ); 288 if( pFly ) 289 { 290 // Aus folgendem Grund muss die FlyPortion neu berechnet werden: 291 // Wenn durch einen grossen Font in der Mitte der Zeile die Grundlinie 292 // abgesenkt wird und dadurch eine Ueberlappung mit eine Fly entsteht, 293 // so hat die FlyPortion eine falsche Groesse/Fixsize. 294 rInf.SetFly( pFly ); 295 CalcFlyWidth( rInf ); 296 } 297 rInf.GetLast()->SetPortion(0); 298 299 // Eine Ausnahme bildet das SwLineLayout, dass sich beim 300 // ersten Portionwechsel aufspaltet. Hier nun der umgekehrte Weg: 301 if( rInf.GetLast() == pCurr ) 302 { 303 if( pPor->InTxtGrp() && !pPor->InExpGrp() ) 304 { 305 MSHORT nOldWhich = pCurr->GetWhichPor(); 306 *(SwLinePortion*)pCurr = *pPor; 307 pCurr->SetPortion( pPor->GetPortion() ); 308 pCurr->SetWhichPor( nOldWhich ); 309 pPor->SetPortion( 0 ); 310 delete pPor; 311 pPor = pCurr; 312 } 313 } 314 pPor->Truncate(); 315 SwLinePortion *const pRest( rInf.GetRest() ); 316 if (pRest && pRest->InFldGrp() && 317 static_cast<SwFldPortion*>(pRest)->IsNoLength()) 318 { 319 // HACK: decrement again, so we pick up the suffix in next line! 320 --m_nHintEndIndex; 321 } 322 delete pRest; 323 rInf.SetRest(0); 324 return pPor; 325 } 326 327 /************************************************************************* 328 * SwTxtFormatter::InsertPortion() 329 *************************************************************************/ 330 331 void SwTxtFormatter::InsertPortion( SwTxtFormatInfo &rInf, 332 SwLinePortion *pPor ) const 333 { 334 // Die neue Portion wird eingefuegt, 335 // bei dem LineLayout ist allerdings alles anders... 336 if( pPor == pCurr ) 337 { 338 if ( pCurr->GetPortion() ) 339 { 340 pPor = pCurr->GetPortion(); 341 } 342 343 // --> OD 2010-07-07 #i112181# 344 rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() ); 345 // <-- 346 } 347 else 348 { 349 SwLinePortion *pLast = rInf.GetLast(); 350 if( pLast->GetPortion() ) 351 { 352 while( pLast->GetPortion() ) 353 pLast = pLast->GetPortion(); 354 rInf.SetLast( pLast ); 355 } 356 pLast->Insert( pPor ); 357 358 rInf.SetOtherThanFtnInside( rInf.IsOtherThanFtnInside() || !pPor->IsFtnPortion() ); 359 360 // Maxima anpassen: 361 if( pCurr->Height() < pPor->Height() ) 362 pCurr->Height( pPor->Height() ); 363 if( pCurr->GetAscent() < pPor->GetAscent() ) 364 pCurr->SetAscent( pPor->GetAscent() ); 365 } 366 367 // manchmal werden ganze Ketten erzeugt (z.B. durch Hyphenate) 368 rInf.SetLast( pPor ); 369 while( pPor ) 370 { 371 DBG_LOOP; 372 pPor->Move( rInf ); 373 rInf.SetLast( pPor ); 374 pPor = pPor->GetPortion(); 375 } 376 } 377 378 /************************************************************************* 379 * SwTxtFormatter::BuildPortion() 380 *************************************************************************/ 381 382 void SwTxtFormatter::BuildPortions( SwTxtFormatInfo &rInf ) 383 { 384 ASSERT( rInf.GetTxt().Len() < STRING_LEN, 385 "SwTxtFormatter::BuildPortions: bad text length in info" ); 386 387 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() ); 388 389 // Erst NewTxtPortion() entscheidet, ob pCurr in pPor landet. 390 // Wir muessen in jedem Fall dafuer sorgen, dass der Font eingestellt 391 // wird. In CalcAscent geschieht dies automatisch. 392 rInf.SetLast( pCurr ); 393 rInf.ForcedLeftMargin( 0 ); 394 395 ASSERT( pCurr->FindLastPortion() == pCurr, "pLast supposed to equal pCurr" ); 396 397 if( !pCurr->GetAscent() && !pCurr->Height() ) 398 CalcAscent( rInf, pCurr ); 399 400 SeekAndChg( rInf ); 401 402 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt. 403 ASSERT( !rInf.X() || pMulti, "SwTxtFormatter::BuildPortion X=0?" ); 404 CalcFlyWidth( rInf ); 405 SwFlyPortion *pFly = rInf.GetFly(); 406 if( pFly ) 407 { 408 if ( 0 < pFly->Fix() ) 409 ClearFly( rInf ); 410 else 411 rInf.SetFull(sal_True); 412 } 413 414 SwLinePortion *pPor = NewPortion( rInf ); 415 416 // Asian grid stuff 417 GETGRID( pFrm->FindPageFrm() ) 418 const sal_Bool bHasGrid = pGrid && rInf.SnapToGrid() && 419 GRID_LINES_CHARS == pGrid->GetGridType(); 420 421 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); 422 const sal_uInt16 nGridWidth = bHasGrid ? 423 GETGRIDWIDTH(pGrid,pDoc) : 0; //for textgrid refactor 424 425 // used for grid mode only: 426 // the pointer is stored, because after formatting of non-asian text, 427 // the width of the kerning portion has to be adjusted 428 SwKernPortion* pGridKernPortion = 0; 429 430 sal_Bool bFull; 431 SwTwips nUnderLineStart = 0; 432 rInf.Y( Y() ); 433 434 while( pPor && !rInf.IsStop() ) 435 { 436 ASSERT( rInf.GetLen() < STRING_LEN && 437 rInf.GetIdx() <= rInf.GetTxt().Len(), 438 "SwTxtFormatter::BuildPortions: bad length in info" ); 439 DBG_LOOP; 440 441 // We have to check the script for fields in order to set the 442 // correct nActual value for the font. 443 if( pPor->InFldGrp() ) 444 ((SwFldPortion*)pPor)->CheckScript( rInf ); 445 446 if( ! bHasGrid && rInf.HasScriptSpace() && 447 rInf.GetLast() && rInf.GetLast()->InTxtGrp() && 448 rInf.GetLast()->Width() && !rInf.GetLast()->InNumberGrp() ) 449 { 450 sal_uInt8 nNxtActual = rInf.GetFont()->GetActual(); 451 sal_uInt8 nLstActual = nNxtActual; 452 sal_uInt16 nLstHeight = (sal_uInt16)rInf.GetFont()->GetHeight(); 453 sal_Bool bAllowBefore = sal_False; 454 sal_Bool bAllowBehind = sal_False; 455 const CharClass& rCC = GetAppCharClass(); 456 457 // are there any punctuation characters on both sides 458 // of the kerning portion? 459 if ( pPor->InFldGrp() ) 460 { 461 XubString aAltTxt; 462 if ( ((SwFldPortion*)pPor)->GetExpTxt( rInf, aAltTxt ) && 463 aAltTxt.Len() ) 464 { 465 bAllowBehind = rCC.isLetterNumeric( aAltTxt, 0 ); 466 467 const SwFont* pTmpFnt = ((SwFldPortion*)pPor)->GetFont(); 468 if ( pTmpFnt ) 469 nNxtActual = pTmpFnt->GetActual(); 470 } 471 } 472 else 473 bAllowBehind = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() ); 474 475 const SwLinePortion* pLast = rInf.GetLast(); 476 if ( bAllowBehind && pLast ) 477 { 478 if ( pLast->InFldGrp() ) 479 { 480 XubString aAltTxt; 481 if ( ((SwFldPortion*)pLast)->GetExpTxt( rInf, aAltTxt ) && 482 aAltTxt.Len() ) 483 { 484 bAllowBefore = rCC.isLetterNumeric( aAltTxt, aAltTxt.Len() - 1 ); 485 486 const SwFont* pTmpFnt = ((SwFldPortion*)pLast)->GetFont(); 487 if ( pTmpFnt ) 488 { 489 nLstActual = pTmpFnt->GetActual(); 490 nLstHeight = (sal_uInt16)pTmpFnt->GetHeight(); 491 } 492 } 493 } 494 else if ( rInf.GetIdx() ) 495 { 496 bAllowBefore = rCC.isLetterNumeric( rInf.GetTxt(), rInf.GetIdx() - 1 ); 497 // Note: ScriptType returns values in [1,4] 498 if ( bAllowBefore ) 499 nLstActual = pScriptInfo->ScriptType( rInf.GetIdx() - 1 ) - 1; 500 } 501 502 nLstHeight /= 5; 503 // does the kerning portion still fit into the line? 504 if( bAllowBefore && ( nLstActual != nNxtActual ) && 505 nLstHeight && rInf.X() + nLstHeight <= rInf.Width() ) 506 { 507 SwKernPortion* pKrn = 508 new SwKernPortion( *rInf.GetLast(), nLstHeight, 509 pLast->InFldGrp() && pPor->InFldGrp() ); 510 rInf.GetLast()->SetPortion( NULL ); 511 InsertPortion( rInf, pKrn ); 512 } 513 } 514 } 515 else if ( bHasGrid && ! pGridKernPortion && ! pMulti ) 516 { 517 // insert a grid kerning portion 518 if ( ! pGridKernPortion ) 519 pGridKernPortion = pPor->IsKernPortion() ? 520 (SwKernPortion*)pPor : 521 new SwKernPortion( *pCurr ); 522 523 // if we have a new GridKernPortion, we initially calculate 524 // its size so that its ends on the grid 525 const SwPageFrm* pPageFrm = pFrm->FindPageFrm(); 526 const SwLayoutFrm* pBody = pPageFrm->FindBodyCont(); 527 SWRECTFN( pPageFrm ) 528 529 const long nGridOrigin = pBody ? 530 (pBody->*fnRect->fnGetPrtLeft)() : 531 (pPageFrm->*fnRect->fnGetPrtLeft)(); 532 533 SwTwips nStartX = rInf.X() + GetLeftMargin(); 534 if ( bVert ) 535 { 536 Point aPoint( nStartX, 0 ); 537 pFrm->SwitchHorizontalToVertical( aPoint ); 538 nStartX = aPoint.Y(); 539 } 540 541 const SwTwips nOfst = nStartX - nGridOrigin; 542 if ( nOfst ) 543 { 544 const sal_uLong i = ( nOfst > 0 ) ? 545 ( ( nOfst - 1 ) / nGridWidth + 1 ) : 546 0; 547 const SwTwips nKernWidth = i * nGridWidth - nOfst; 548 const SwTwips nRestWidth = rInf.Width() - rInf.X(); 549 550 if ( nKernWidth <= nRestWidth ) 551 pGridKernPortion->Width( (sal_uInt16)nKernWidth ); 552 } 553 554 if ( pGridKernPortion != pPor ) 555 InsertPortion( rInf, pGridKernPortion ); 556 } 557 558 // the multi-portion has it's own format function 559 if( pPor->IsMultiPortion() && ( !pMulti || pMulti->IsBidi() ) ) 560 bFull = BuildMultiPortion( rInf, *((SwMultiPortion*)pPor) ); 561 else 562 bFull = pPor->Format( rInf ); 563 564 if( rInf.IsRuby() && !rInf.GetRest() ) 565 bFull = sal_True; 566 567 // if we are underlined, we store the beginning of this underlined 568 // segment for repaint optimization 569 if ( UNDERLINE_NONE != pFnt->GetUnderline() && ! nUnderLineStart ) 570 nUnderLineStart = GetLeftMargin() + rInf.X(); 571 572 if ( pPor->IsFlyPortion() ) 573 pCurr->SetFly( sal_True ); 574 // some special cases, where we have to take care for the repaint 575 // offset: 576 // 1. Underlined portions due to special underline feature 577 // 2. Right Tab 578 // 3. BidiPortions 579 // 4. other Multiportions 580 // 5. DropCaps 581 // 6. Grid Mode 582 else if ( ( ! rInf.GetPaintOfst() || nUnderLineStart < rInf.GetPaintOfst() ) && 583 // 1. Underlined portions 584 nUnderLineStart && 585 // reformat is at end of an underlined portion and next portion 586 // is not underlined 587 ( ( rInf.GetReformatStart() == rInf.GetIdx() && 588 UNDERLINE_NONE == pFnt->GetUnderline() 589 ) || 590 // reformat is inside portion and portion is underlined 591 ( rInf.GetReformatStart() >= rInf.GetIdx() && 592 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() && 593 UNDERLINE_NONE != pFnt->GetUnderline() ) ) ) 594 rInf.SetPaintOfst( nUnderLineStart ); 595 else if ( ! rInf.GetPaintOfst() && 596 // 2. Right Tab 597 ( ( pPor->InTabGrp() && !pPor->IsTabLeftPortion() ) || 598 // 3. BidiPortions 599 ( pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsBidi() ) || 600 // 4. Multi Portion and 5. Drop Caps 601 ( ( pPor->IsDropPortion() || pPor->IsMultiPortion() ) && 602 rInf.GetReformatStart() >= rInf.GetIdx() && 603 rInf.GetReformatStart() <= rInf.GetIdx() + pPor->GetLen() ) 604 // 6. Grid Mode 605 || ( bHasGrid && SW_CJK != pFnt->GetActual() ) 606 ) 607 ) 608 // we store the beginning of the critical portion as our 609 // paint offset 610 rInf.SetPaintOfst( GetLeftMargin() + rInf.X() ); 611 612 // under one of these conditions we are allowed to delete the 613 // start of the underline portion 614 if ( IsUnderlineBreak( *pPor, *pFnt ) ) 615 nUnderLineStart = 0; 616 617 if( pPor->IsFlyCntPortion() || ( pPor->IsMultiPortion() && 618 ((SwMultiPortion*)pPor)->HasFlyInCntnt() ) ) 619 SetFlyInCntBase(); 620 // 5964: bUnderFlow muss zurueckgesetzt werden, sonst wird beim 621 // naechsten Softhyphen wieder umgebrochen! 622 if ( !bFull ) 623 { 624 rInf.ClrUnderFlow(); 625 if( ! bHasGrid && rInf.HasScriptSpace() && pPor->InTxtGrp() && 626 pPor->GetLen() && !pPor->InFldGrp() ) 627 { 628 // The distance between two different scripts is set 629 // to 20% of the fontheight. 630 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen(); 631 if( nTmp == pScriptInfo->NextScriptChg( nTmp - 1 ) && 632 nTmp != rInf.GetTxt().Len() ) 633 { 634 sal_uInt16 nDist = (sal_uInt16)(rInf.GetFont()->GetHeight()/5); 635 636 if( nDist ) 637 { 638 // we do not want a kerning portion if any end 639 // would be a punctuation character 640 const CharClass& rCC = GetAppCharClass(); 641 if ( rCC.isLetterNumeric( rInf.GetTxt(), nTmp - 1 ) && 642 rCC.isLetterNumeric( rInf.GetTxt(), nTmp ) ) 643 { 644 // does the kerning portion still fit into the line? 645 if ( rInf.X() + pPor->Width() + nDist <= rInf.Width() ) 646 new SwKernPortion( *pPor, nDist ); 647 else 648 bFull = sal_True; 649 } 650 } 651 } 652 } 653 } 654 655 if ( bHasGrid && pPor != pGridKernPortion && ! pMulti ) 656 { 657 xub_StrLen nTmp = rInf.GetIdx() + pPor->GetLen(); 658 const SwTwips nRestWidth = rInf.Width() - rInf.X() - pPor->Width(); 659 660 const sal_uInt8 nCurrScript = pFnt->GetActual(); // pScriptInfo->ScriptType( rInf.GetIdx() ); 661 const sal_uInt8 nNextScript = nTmp >= rInf.GetTxt().Len() ? 662 SW_CJK : 663 SwScriptInfo::WhichFont( nTmp, 0, pScriptInfo ); 664 665 // snap non-asian text to grid if next portion is ASIAN or 666 // there are no more portions in this line 667 // be careful when handling an underflow event: the gridkernportion 668 // could have been deleted 669 if ( nRestWidth > 0 && SW_CJK != nCurrScript && 670 ! rInf.IsUnderFlow() && ( bFull || SW_CJK == nNextScript ) ) 671 { 672 ASSERT( pGridKernPortion, "No GridKernPortion available" ) 673 674 // calculate size 675 SwLinePortion* pTmpPor = pGridKernPortion->GetPortion(); 676 sal_uInt16 nSumWidth = pPor->Width(); 677 while ( pTmpPor ) 678 { 679 nSumWidth = nSumWidth + pTmpPor->Width(); 680 pTmpPor = pTmpPor->GetPortion(); 681 } 682 683 const sal_uInt16 i = nSumWidth ? 684 ( nSumWidth - 1 ) / nGridWidth + 1 : 685 0; 686 const SwTwips nTmpWidth = i * nGridWidth; 687 const SwTwips nKernWidth = Min( (SwTwips)(nTmpWidth - nSumWidth), 688 nRestWidth ); 689 const sal_uInt16 nKernWidth_1 = (sal_uInt16)(nKernWidth / 2); 690 691 ASSERT( nKernWidth <= nRestWidth, 692 "Not enough space left for adjusting non-asian text in grid mode" ) 693 694 pGridKernPortion->Width( pGridKernPortion->Width() + nKernWidth_1 ); 695 rInf.X( rInf.X() + nKernWidth_1 ); 696 697 if ( ! bFull ) 698 new SwKernPortion( *pPor, (short)(nKernWidth - nKernWidth_1), 699 sal_False, sal_True ); 700 701 pGridKernPortion = 0; 702 } 703 else if ( pPor->IsMultiPortion() || pPor->InFixMargGrp() || 704 pPor->IsFlyCntPortion() || pPor->InNumberGrp() || 705 pPor->InFldGrp() || nCurrScript != nNextScript ) 706 // next portion should snap to grid 707 pGridKernPortion = 0; 708 } 709 710 rInf.SetFull( bFull ); 711 712 // Restportions von mehrzeiligen Feldern haben bisher noch 713 // nicht den richtigen Ascent. 714 if ( !pPor->GetLen() && !pPor->IsFlyPortion() 715 && !pPor->IsGrfNumPortion() && ! pPor->InNumberGrp() 716 && !pPor->IsMultiPortion() ) 717 CalcAscent( rInf, pPor ); 718 719 InsertPortion( rInf, pPor ); 720 pPor = NewPortion( rInf ); 721 } 722 723 if( !rInf.IsStop() ) 724 { 725 // der letzte rechte, zentrierte, dezimale Tab 726 SwTabPortion *pLastTab = rInf.GetLastTab(); 727 if( pLastTab ) 728 pLastTab->FormatEOL( rInf ); 729 else if( rInf.GetLast() && rInf.LastKernPortion() ) 730 rInf.GetLast()->FormatEOL( rInf ); 731 } 732 if( pCurr->GetPortion() && pCurr->GetPortion()->InNumberGrp() 733 && ((SwNumberPortion*)pCurr->GetPortion())->IsHide() ) 734 rInf.SetNumDone( sal_False ); 735 736 // 3260, 3860: Fly auf jeden Fall loeschen! 737 ClearFly( rInf ); 738 } 739 740 /************************************************************************* 741 * SwTxtFormatter::CalcAdjustLine() 742 *************************************************************************/ 743 744 void SwTxtFormatter::CalcAdjustLine( SwLineLayout *pCurrent ) 745 { 746 if( SVX_ADJUST_LEFT != GetAdjust() && !pMulti) 747 { 748 pCurrent->SetFormatAdj(sal_True); 749 if( IsFlyInCntBase() ) 750 { 751 CalcAdjLine( pCurrent ); 752 // 23348: z.B. bei zentrierten Flys muessen wir den RefPoint 753 // auf jeden Fall umsetzen, deshalb bAllWays = sal_True 754 UpdatePos( pCurrent, GetTopLeft(), GetStart(), sal_True ); 755 } 756 } 757 } 758 759 /************************************************************************* 760 * SwTxtFormatter::CalcAscent() 761 *************************************************************************/ 762 763 void SwTxtFormatter::CalcAscent( SwTxtFormatInfo &rInf, SwLinePortion *pPor ) 764 { 765 if ( pPor->InFldGrp() && ((SwFldPortion*)pPor)->GetFont() ) 766 { 767 // Numerierungen + InterNetFlds koennen einen eigenen Font beinhalten, 768 // dann ist ihre Groesse unabhaengig von harten Attributierungen. 769 SwFont* pFldFnt = ((SwFldPortion*)pPor)->pFnt; 770 SwFontSave aSave( rInf, pFldFnt ); 771 ((SwFldPortion*)pPor)->Height( pFldFnt->GetHeight( rInf.GetVsh(), *rInf.GetOut() ) ); 772 ((SwFldPortion*)pPor)->SetAscent( pFldFnt->GetAscent( rInf.GetVsh(), *rInf.GetOut() ) ); 773 } 774 // --> OD 2008-06-05 #i89179# 775 // tab portion representing the list tab of a list label gets the 776 // same height and ascent as the corresponding number portion 777 else if ( pPor->InTabGrp() && pPor->GetLen() == 0 && 778 rInf.GetLast() && rInf.GetLast()->InNumberGrp() && 779 static_cast<const SwNumberPortion*>(rInf.GetLast())->HasFont() ) 780 { 781 const SwLinePortion* pLast = rInf.GetLast(); 782 pPor->Height( pLast->Height() ); 783 pPor->SetAscent( pLast->GetAscent() ); 784 } 785 // <-- 786 else 787 { 788 const SwLinePortion *pLast = rInf.GetLast(); 789 sal_Bool bChg; 790 791 // Fallunterscheidung: in leeren Zeilen werden die Attribute 792 // per SeekStart angeschaltet. 793 const sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx(); 794 if ( pPor->IsQuoVadisPortion() ) 795 bChg = SeekStartAndChg( rInf, sal_True ); 796 else 797 { 798 if( bFirstPor ) 799 { 800 if( rInf.GetTxt().Len() ) 801 { 802 if ( pPor->GetLen() || !rInf.GetIdx() 803 || ( pCurr != pLast && !pLast->IsFlyPortion() ) 804 || !pCurr->IsRest() ) // statt !rInf.GetRest() 805 bChg = SeekAndChg( rInf ); 806 else 807 bChg = SeekAndChgBefore( rInf ); 808 } 809 else if ( pMulti ) 810 // do not open attributes starting at 0 in empty multi 811 // portions (rotated numbering followed by a footnote 812 // can cause trouble, because the footnote attribute 813 // starts at 0, but if we open it, the attribute handler 814 // cannot handle it. 815 bChg = sal_False; 816 else 817 bChg = SeekStartAndChg( rInf ); 818 } 819 else 820 bChg = SeekAndChg( rInf ); 821 } 822 if( bChg || bFirstPor || !pPor->GetAscent() 823 || !rInf.GetLast()->InTxtGrp() ) 824 { 825 pPor->SetAscent( rInf.GetAscent() ); 826 pPor->Height( rInf.GetTxtHeight() ); 827 } 828 else 829 { 830 pPor->Height( pLast->Height() ); 831 pPor->SetAscent( pLast->GetAscent() ); 832 } 833 } 834 } 835 836 /************************************************************************* 837 * class SwMetaPortion 838 *************************************************************************/ 839 840 class SwMetaPortion : public SwTxtPortion 841 { 842 public: 843 inline SwMetaPortion() { SetWhichPor( POR_META ); } 844 virtual void Paint( const SwTxtPaintInfo &rInf ) const; 845 // OUTPUT_OPERATOR 846 }; 847 848 //CLASSIO( SwMetaPortion ) 849 850 /************************************************************************* 851 * virtual SwMetaPortion::Paint() 852 *************************************************************************/ 853 854 void SwMetaPortion::Paint( const SwTxtPaintInfo &rInf ) const 855 { 856 if ( Width() ) 857 { 858 rInf.DrawViewOpt( *this, POR_META ); 859 SwTxtPortion::Paint( rInf ); 860 } 861 } 862 863 864 /************************************************************************* 865 * SwTxtFormatter::WhichTxtPor() 866 *************************************************************************/ 867 868 SwTxtPortion *SwTxtFormatter::WhichTxtPor( SwTxtFormatInfo &rInf ) const 869 { 870 SwTxtPortion *pPor = 0; 871 if( GetFnt()->IsTox() ) 872 pPor = new SwToxPortion; 873 else 874 { 875 if( GetFnt()->IsRef() ) 876 pPor = new SwRefPortion; 877 else if (GetFnt()->IsMeta()) 878 { 879 pPor = new SwMetaPortion; 880 } 881 else 882 { 883 // Erst zum Schluss ! 884 // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben, 885 // z.B. bei nicht darstellbaren Zeichen. 886 if( rInf.GetLen() > 0 ) 887 { 888 if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART ) 889 pPor = new SwFieldMarkPortion(); 890 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND ) 891 pPor = new SwFieldMarkPortion(); 892 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT ) 893 pPor = new SwFieldFormPortion(); 894 } 895 if( !pPor ) 896 { 897 if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() ) 898 pPor = pCurr; 899 else 900 { 901 pPor = new SwTxtPortion; 902 if( GetFnt()->IsURL() ) 903 pPor->SetWhichPor( POR_URL ); 904 } 905 } 906 } 907 } 908 return pPor; 909 } 910 911 /************************************************************************* 912 * SwTxtFormatter::NewTxtPortion() 913 *************************************************************************/ 914 // Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert: 915 // 1) Tabs 916 // 2) Linebreaks 917 // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD 918 // 4) naechster Attributwechsel 919 920 SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf ) 921 { 922 // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr 923 // Wenn pCurr nicht von SwTxtPortion abgeleitet ist, 924 // muessen wir duplizieren ... 925 Seek( rInf.GetIdx() ); 926 SwTxtPortion *pPor = WhichTxtPor( rInf ); 927 928 // until next attribute change: 929 const xub_StrLen nNextAttr = GetNextAttr(); 930 xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() ); 931 932 // end of script type: 933 const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() ); 934 nNextChg = Min( nNextChg, nNextScript ); 935 936 // end of direction: 937 const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() ); 938 nNextChg = Min( nNextChg, nNextDir ); 939 940 // 7515, 7516, 3470, 6441 : Turbo-Boost 941 // Es wird unterstellt, dass die Buchstaben eines Fonts nicht 942 // groesser als doppelt so breit wie hoch sind. 943 // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen. 944 // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe 945 // ergibt sich erst im CalcAscent! 946 // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times 947 // New Roman besitzt einen Ascent von 182, eine Hoehe von 200 948 // und eine Breite von 53! Daraus folgt, dass eine Zeile mit 949 // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von 950 // Faktor 2 auf 8 (wg. negativen Kernings). 951 952 pPor->SetLen(1); 953 CalcAscent( rInf, pPor ); 954 955 const SwFont* pTmpFnt = rInf.GetFont(); 956 KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ), 957 KSHORT( pPor->GetAscent() ) ) / 8; 958 if ( !nExpect ) 959 nExpect = 1; 960 nExpect = (sal_uInt16)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect)); 961 if( nExpect > rInf.GetIdx() && nNextChg > nExpect ) 962 nNextChg = Min( nExpect, rInf.GetTxt().Len() ); 963 964 // we keep an invariant during method calls: 965 // there are no portion ending characters like hard spaces 966 // or tabs in [ nLeftScanIdx, nRightScanIdx ] 967 if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx ) 968 { 969 if ( nNextChg > nRightScanIdx ) 970 nNextChg = nRightScanIdx = 971 rInf.ScanPortionEnd( nRightScanIdx, nNextChg ); 972 } 973 else 974 { 975 nLeftScanIdx = rInf.GetIdx(); 976 nNextChg = nRightScanIdx = 977 rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg ); 978 } 979 980 pPor->SetLen( nNextChg - rInf.GetIdx() ); 981 rInf.SetLen( pPor->GetLen() ); 982 return pPor; 983 } 984 985 986 /************************************************************************* 987 * SwTxtFormatter::WhichFirstPortion() 988 *************************************************************************/ 989 990 SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf) 991 { 992 SwLinePortion *pPor = 0; 993 994 if( rInf.GetRest() ) 995 { 996 // 5010: Tabs und Felder 997 if( '\0' != rInf.GetHookChar() ) 998 return 0; 999 1000 pPor = rInf.GetRest(); 1001 if( pPor->IsErgoSumPortion() ) 1002 rInf.SetErgoDone(sal_True); 1003 else 1004 if( pPor->IsFtnNumPortion() ) 1005 rInf.SetFtnDone(sal_True); 1006 else 1007 if( pPor->InNumberGrp() ) 1008 rInf.SetNumDone(sal_True); 1009 if( pPor ) 1010 { 1011 rInf.SetRest(0); 1012 pCurr->SetRest( sal_True ); 1013 return pPor; 1014 } 1015 } 1016 1017 // ???? und ????: im Follow duerfen wir schon stehen, 1018 // entscheidend ist, ob pFrm->GetOfst() == 0 ist! 1019 if( rInf.GetIdx() ) 1020 { 1021 // Nun koennen auch FtnPortions und ErgoSumPortions 1022 // verlaengert werden. 1023 1024 // 1) Die ErgoSumTexte 1025 if( !rInf.IsErgoDone() ) 1026 { 1027 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) 1028 pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); 1029 rInf.SetErgoDone( sal_True ); 1030 } 1031 1032 // 2) Arrow portions 1033 if( !pPor && !rInf.IsArrowDone() ) 1034 { 1035 if( pFrm->GetOfst() && !pFrm->IsFollow() && 1036 rInf.GetIdx() == pFrm->GetOfst() ) 1037 pPor = new SwArrowPortion( *pCurr ); 1038 rInf.SetArrowDone( sal_True ); 1039 } 1040 1041 // 3) Kerning portions at beginning of line in grid mode 1042 if ( ! pPor && ! pCurr->GetPortion() ) 1043 { 1044 GETGRID( GetTxtFrm()->FindPageFrm() ) 1045 if ( pGrid ) 1046 pPor = new SwKernPortion( *pCurr ); 1047 } 1048 1049 // 4) Die Zeilenreste (mehrzeilige Felder) 1050 if( !pPor ) 1051 { 1052 pPor = rInf.GetRest(); 1053 // 6922: Nur bei pPor natuerlich. 1054 if( pPor ) 1055 { 1056 pCurr->SetRest( sal_True ); 1057 rInf.SetRest(0); 1058 } 1059 } 1060 } 1061 else 1062 { 1063 // 5) Die Fussnotenzahlen 1064 if( !rInf.IsFtnDone() ) 1065 { 1066 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), 1067 "Rotated number portion trouble" ) 1068 1069 sal_Bool bFtnNum = pFrm->IsFtnNumFrm(); 1070 rInf.GetParaPortion()->SetFtnNum( bFtnNum ); 1071 if( bFtnNum ) 1072 pPor = (SwLinePortion*)NewFtnNumPortion( rInf ); 1073 rInf.SetFtnDone( sal_True ); 1074 } 1075 1076 // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster, 1077 // entscheidend ist, ob der SwFtnFrm ein Follow ist. 1078 if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() ) 1079 { 1080 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) 1081 pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); 1082 rInf.SetErgoDone( sal_True ); 1083 } 1084 1085 // 7) Die Numerierungen 1086 if( !rInf.IsNumDone() && !pPor ) 1087 { 1088 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), 1089 "Rotated number portion trouble" ) 1090 1091 // Wenn wir im Follow stehen, dann natuerlich nicht. 1092 if( GetTxtFrm()->GetTxtNode()->GetNumRule() ) 1093 pPor = (SwLinePortion*)NewNumberPortion( rInf ); 1094 rInf.SetNumDone( sal_True ); 1095 } 1096 // 8) Die DropCaps 1097 if( !pPor && GetDropFmt() && ! rInf.IsMulti() ) 1098 pPor = (SwLinePortion*)NewDropPortion( rInf ); 1099 1100 // 9) Kerning portions at beginning of line in grid mode 1101 if ( !pPor && !pCurr->GetPortion() ) 1102 { 1103 GETGRID( GetTxtFrm()->FindPageFrm() ) 1104 if ( pGrid ) 1105 pPor = new SwKernPortion( *pCurr ); 1106 } 1107 } 1108 1109 // 10) Decimal tab portion at the beginning of each line in table cells 1110 if ( !pPor && !pCurr->GetPortion() && 1111 GetTxtFrm()->IsInTab() && 1112 GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) ) 1113 { 1114 pPor = NewTabPortion( rInf, true ); 1115 } 1116 1117 // 11) suffix of meta-field 1118 if (!pPor) 1119 { 1120 pPor = TryNewNoLengthPortion(rInf); 1121 } 1122 1123 return pPor; 1124 } 1125 1126 sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr ) 1127 { 1128 if( !pCurr->GetNext() ) 1129 return sal_False; 1130 const SwLinePortion *pPor = pCurr->GetNext()->GetPortion(); 1131 sal_Bool bRet = sal_False; 1132 while( pPor && !bRet ) 1133 { 1134 bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) || 1135 (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld()); 1136 if( !pPor->GetLen() ) 1137 break; 1138 pPor = pPor->GetPortion(); 1139 } 1140 return bRet; 1141 } 1142 1143 /************************************************************************* 1144 * SwTxtFormatter::NewPortion() 1145 *************************************************************************/ 1146 1147 /* NewPortion stellt rInf.nLen ein. 1148 * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr, 1149 * attrwechsel. 1150 * Drei Faelle koennen eintreten: 1151 * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert 1152 * -> return 0; 1153 * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert 1154 * -> Breite neu einstellen und return new FlyPortion 1155 * 3) Es muss eine neue Portion gebaut werden. 1156 * -> CalcFlyWidth emuliert ggf. die Breite und return Portion 1157 */ 1158 1159 SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf ) 1160 { 1161 // Underflow hat Vorrang 1162 rInf.SetStopUnderFlow( sal_False ); 1163 if( rInf.GetUnderFlow() ) 1164 { 1165 ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" ); 1166 return UnderFlow( rInf ); 1167 } 1168 1169 // Wenn die Zeile voll ist, koennten noch Flys oder 1170 // UnderFlow-LinePortions warten ... 1171 if( rInf.IsFull() ) 1172 { 1173 // ????: LineBreaks und Flys (bug05.sdw) 1174 // 8450: IsDummy() 1175 if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) ) 1176 return 0; 1177 1178 // Wenn der Text an den Fly gestossen ist, oder wenn 1179 // der Fly als erstes drankommt, weil er ueber dem linken 1180 // Rand haengt, wird GetFly() returnt. 1181 // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's 1182 // naturgemaesz eine 0. 1183 if( rInf.GetFly() ) 1184 { 1185 if( rInf.GetLast()->IsBreakPortion() ) 1186 { 1187 delete rInf.GetFly(); 1188 rInf.SetFly( 0 ); 1189 } 1190 1191 return rInf.GetFly(); 1192 } 1193 // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den 1194 // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest 1195 // bekanntgeben, damit SwTxtFrm::Format nicht abbricht 1196 // (die Textmasse wurde ja durchformatiert). 1197 if( rInf.GetRest() ) 1198 rInf.SetNewLine( sal_True ); 1199 else 1200 { 1201 // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt, 1202 // jetzt aber kein Rest mehr anliegt, 1203 // muss sie auf jeden Fall neu formatiert werden! 1204 if( lcl_OldFieldRest( GetCurr() ) ) 1205 rInf.SetNewLine( sal_True ); 1206 else 1207 { 1208 SwLinePortion *pFirst = WhichFirstPortion( rInf ); 1209 if( pFirst ) 1210 { 1211 rInf.SetNewLine( sal_True ); 1212 if( pFirst->InNumberGrp() ) 1213 rInf.SetNumDone( sal_False) ; 1214 delete pFirst; 1215 } 1216 } 1217 } 1218 1219 return 0; 1220 } 1221 1222 SwLinePortion *pPor = WhichFirstPortion( rInf ); 1223 1224 // Check for Hidden Portion: 1225 if ( !pPor ) 1226 { 1227 xub_StrLen nEnd = rInf.GetIdx(); 1228 if ( lcl_BuildHiddenPortion( rInf, nEnd ) ) 1229 pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() ); 1230 } 1231 1232 if( !pPor ) 1233 { 1234 if( ( !pMulti || pMulti->IsBidi() ) && 1235 // --> FME 2005-02-14 #i42734# 1236 // No multi portion if there is a hook character waiting: 1237 ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) ) 1238 // <-- 1239 { 1240 // We open a multiportion part, if we enter a multi-line part 1241 // of the paragraph. 1242 xub_StrLen nEnd = rInf.GetIdx(); 1243 SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti ); 1244 if( pCreate ) 1245 { 1246 SwMultiPortion* pTmp = NULL; 1247 1248 if ( SW_MC_BIDI == pCreate->nId ) 1249 pTmp = new SwBidiPortion( nEnd, pCreate->nLevel ); 1250 else if ( SW_MC_RUBY == pCreate->nId ) 1251 { 1252 Seek( rInf.GetIdx() ); 1253 sal_Bool bRubyTop; 1254 sal_Bool* pRubyPos = 0; 1255 1256 if ( rInf.SnapToGrid() ) 1257 { 1258 GETGRID( GetTxtFrm()->FindPageFrm() ) 1259 if ( pGrid ) 1260 { 1261 bRubyTop = ! pGrid->GetRubyTextBelow(); 1262 pRubyPos = &bRubyTop; 1263 } 1264 } 1265 1266 pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(), 1267 *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(), 1268 nEnd, 0, pRubyPos ); 1269 } 1270 else if( SW_MC_ROTATE == pCreate->nId ) 1271 pTmp = new SwRotatedPortion( *pCreate, nEnd, 1272 GetTxtFrm()->IsRightToLeft() ); 1273 else 1274 pTmp = new SwDoubleLinePortion( *pCreate, nEnd ); 1275 1276 delete pCreate; 1277 CalcFlyWidth( rInf ); 1278 1279 return pTmp; 1280 } 1281 } 1282 // 5010: Tabs und Felder 1283 xub_Unicode cChar = rInf.GetHookChar(); 1284 1285 if( cChar ) 1286 { 1287 /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das 1288 * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile 1289 * gewandert ist ( so geschehen hinter Rahmen ). 1290 * Wenn allerdings eine FldPortion im Rest wartet, muessen wir 1291 * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei 1292 * DezimalTabs und Feldern (22615) 1293 */ 1294 if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() ) 1295 cChar = rInf.GetChar( rInf.GetIdx() ); 1296 rInf.ClearHookChar(); 1297 } 1298 else 1299 { 1300 if( rInf.GetIdx() >= rInf.GetTxt().Len() ) 1301 { 1302 rInf.SetFull(sal_True); 1303 CalcFlyWidth( rInf ); 1304 return pPor; 1305 } 1306 cChar = rInf.GetChar( rInf.GetIdx() ); 1307 } 1308 1309 switch( cChar ) 1310 { 1311 case CH_TAB: 1312 pPor = NewTabPortion( rInf, false ); break; 1313 1314 case CH_BREAK: 1315 pPor = new SwBreakPortion( *rInf.GetLast() ); break; 1316 1317 case CHAR_SOFTHYPHEN: // soft hyphen 1318 pPor = new SwSoftHyphPortion; break; 1319 1320 case CHAR_HARDBLANK: // no-break space 1321 pPor = new SwBlankPortion( ' ' ); break; 1322 1323 case CHAR_HARDHYPHEN: // non-breaking hyphen 1324 pPor = new SwBlankPortion( '-' ); break; 1325 1326 case CHAR_ZWSP: // zero width space 1327 case CHAR_ZWNBSP : // word joiner 1328 // case CHAR_RLM : // right to left mark 1329 // case CHAR_LRM : // left to right mark 1330 pPor = new SwControlCharPortion( cChar ); break; 1331 1332 case CH_TXTATR_BREAKWORD: 1333 case CH_TXTATR_INWORD: 1334 if( rInf.HasHint( rInf.GetIdx() ) ) 1335 { 1336 pPor = NewExtraPortion( rInf ); 1337 break; 1338 } 1339 // No break 1340 default : 1341 { 1342 SwTabPortion* pLastTabPortion = rInf.GetLastTab(); 1343 if ( pLastTabPortion && cChar == rInf.GetTabDecimal() ) 1344 { 1345 // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full: 1346 // We have a decimal tab portion in the line and the next character has to be 1347 // aligned at the tab stop position. We store the width from the beginning of 1348 // the tab stop portion up to the portion containint the decimal separator: 1349 if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ && 1350 POR_TABDECIMAL == pLastTabPortion->GetWhichPor() ) 1351 { 1352 ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" ) 1353 const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = (sal_uInt16)(rInf.X() - pLastTabPortion->Fix() ); 1354 static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition ); 1355 rInf.SetTabDecimal( 0 ); 1356 } 1357 // <-- 1358 else 1359 rInf.SetFull( rInf.GetLastTab()->Format( rInf ) ); 1360 } 1361 1362 if( rInf.GetRest() ) 1363 { 1364 if( rInf.IsFull() ) 1365 { 1366 rInf.SetNewLine(sal_True); 1367 return 0; 1368 } 1369 pPor = rInf.GetRest(); 1370 rInf.SetRest(0); 1371 } 1372 else 1373 { 1374 if( rInf.IsFull() ) 1375 return 0; 1376 pPor = NewTxtPortion( rInf ); 1377 } 1378 break; 1379 } 1380 } 1381 1382 // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht, 1383 // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet 1384 // hat, weil z.B. ein Tab enthalten ist. 1385 if( pPor && rInf.GetRest() ) 1386 pPor->SetLen( 0 ); 1387 1388 // robust: 1389 if( !pPor || rInf.IsStop() ) 1390 { 1391 delete pPor; 1392 return 0; 1393 } 1394 } 1395 1396 // Special portions containing numbers (footnote anchor, footnote number, 1397 // numbering) can be contained in a rotated portion, if the user 1398 // choose a rotated character attribute. 1399 if ( pPor && ! pMulti ) 1400 { 1401 if ( pPor->IsFtnPortion() ) 1402 { 1403 const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn(); 1404 1405 if ( pTxtFtn ) 1406 { 1407 SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn(); 1408 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); 1409 const SwEndNoteInfo* pInfo; 1410 if( rFtn.IsEndNote() ) 1411 pInfo = &pDoc->GetEndNoteInfo(); 1412 else 1413 pInfo = &pDoc->GetFtnInfo(); 1414 const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet(); 1415 1416 const SfxPoolItem* pItem; 1417 sal_uInt16 nDir = 0; 1418 if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE, 1419 sal_True, &pItem )) 1420 nDir = ((SvxCharRotateItem*)pItem)->GetValue(); 1421 1422 if ( 0 != nDir ) 1423 { 1424 delete pPor; 1425 pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ? 1426 DIR_BOTTOM2TOP : 1427 DIR_TOP2BOTTOM ); 1428 } 1429 } 1430 } 1431 else if ( pPor->InNumberGrp() ) 1432 { 1433 const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont(); 1434 1435 if ( pNumFnt ) 1436 { 1437 sal_uInt16 nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() ); 1438 if ( 0 != nDir ) 1439 { 1440 delete pPor; 1441 pPor = new SwRotatedPortion( 0, 900 == nDir ? 1442 DIR_BOTTOM2TOP : 1443 DIR_TOP2BOTTOM ); 1444 1445 rInf.SetNumDone( sal_False ); 1446 rInf.SetFtnDone( sal_False ); 1447 } 1448 } 1449 } 1450 } 1451 1452 // Der Font wird im Outputdevice eingestellt, 1453 // der Ascent und die Hoehe werden berechnet. 1454 if( !pPor->GetAscent() && !pPor->Height() ) 1455 CalcAscent( rInf, pPor ); 1456 rInf.SetLen( pPor->GetLen() ); 1457 1458 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt. 1459 CalcFlyWidth( rInf ); 1460 1461 // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige 1462 // Werte bereithalten muss: 1463 if( !pCurr->Height() ) 1464 { 1465 ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" ); 1466 pCurr->Height( pPor->Height() ); 1467 pCurr->SetAscent( pPor->GetAscent() ); 1468 } 1469 1470 ASSERT( !pPor || pPor->Height(), 1471 "SwTxtFormatter::NewPortion: something went wrong"); 1472 if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() ) 1473 { 1474 delete pPor; 1475 pPor = rInf.GetFly(); 1476 } 1477 return pPor; 1478 } 1479 1480 /************************************************************************* 1481 * SwTxtFormatter::FormatLine() 1482 *************************************************************************/ 1483 1484 xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos ) 1485 { 1486 ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), 1487 "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" ); 1488 1489 // For the formatting routines, we set pOut to the reference device. 1490 SwHookOut aHook( GetInfo() ); 1491 if( GetInfo().GetLen() < GetInfo().GetTxt().Len() ) 1492 GetInfo().SetLen( GetInfo().GetTxt().Len() ); 1493 1494 sal_Bool bBuild = sal_True; 1495 SetFlyInCntBase( sal_False ); 1496 GetInfo().SetLineHeight( 0 ); 1497 GetInfo().SetLineNettoHeight( 0 ); 1498 1499 // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden 1500 // und auch bei geaendertem Ascent (Absenken der Grundlinie). 1501 const KSHORT nOldHeight = pCurr->Height(); 1502 const KSHORT nOldAscent = pCurr->GetAscent(); 1503 1504 pCurr->SetEndHyph( sal_False ); 1505 pCurr->SetMidHyph( sal_False ); 1506 1507 // fly positioning can make it necessary format a line several times 1508 // for this, we have to keep a copy of our rest portion 1509 SwLinePortion* pFld = GetInfo().GetRest(); 1510 SwFldPortion* pSaveFld = 0; 1511 1512 if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() ) 1513 pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) ); 1514 1515 // for an optimal repaint rectangle, we want to compare fly portions 1516 // before and after the BuildPortions call 1517 const sal_Bool bOptimizeRepaint = AllowRepaintOpt(); 1518 const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen(); 1519 SvLongs* pFlyStart = 0; 1520 1521 // these are the conditions for a fly position comparison 1522 if ( bOptimizeRepaint && pCurr->IsFly() ) 1523 { 1524 pFlyStart = new SvLongs; 1525 SwLinePortion* pPor = pCurr->GetFirstPortion(); 1526 long nPOfst = 0; 1527 sal_uInt16 nCnt = 0; 1528 1529 while ( pPor ) 1530 { 1531 if ( pPor->IsFlyPortion() ) 1532 // insert start value of fly portion 1533 pFlyStart->Insert( nPOfst, nCnt++ ); 1534 1535 nPOfst += pPor->Width(); 1536 pPor = pPor->GetPortion(); 1537 } 1538 } 1539 1540 // Hier folgt bald die Unterlaufpruefung. 1541 while( bBuild ) 1542 { 1543 GetInfo().SetFtnInside( sal_False ); 1544 GetInfo().SetOtherThanFtnInside( sal_False ); 1545 1546 // These values must not be reset by FormatReset(); 1547 sal_Bool bOldNumDone = GetInfo().IsNumDone(); 1548 sal_Bool bOldArrowDone = GetInfo().IsArrowDone(); 1549 sal_Bool bOldErgoDone = GetInfo().IsErgoDone(); 1550 1551 // besides other things, this sets the repaint offset to 0 1552 FormatReset( GetInfo() ); 1553 1554 GetInfo().SetNumDone( bOldNumDone ); 1555 GetInfo().SetArrowDone( bOldArrowDone ); 1556 GetInfo().SetErgoDone( bOldErgoDone ); 1557 1558 // build new portions for this line 1559 BuildPortions( GetInfo() ); 1560 1561 if( GetInfo().IsStop() ) 1562 { 1563 pCurr->SetLen( 0 ); 1564 pCurr->Height( GetFrmRstHeight() + 1 ); 1565 pCurr->SetRealHeight( GetFrmRstHeight() + 1 ); 1566 pCurr->Width(0); 1567 pCurr->Truncate(); 1568 return nStartPos; 1569 } 1570 else if( GetInfo().IsDropInit() ) 1571 { 1572 DropInit(); 1573 GetInfo().SetDropInit( sal_False ); 1574 } 1575 1576 pCurr->CalcLine( *this, GetInfo() ); 1577 CalcRealHeight( GetInfo().IsNewLine() ); 1578 1579 if ( IsFlyInCntBase() && !IsQuick() ) 1580 { 1581 KSHORT nTmpAscent, nTmpHeight; 1582 CalcAscentAndHeight( nTmpAscent, nTmpHeight ); 1583 AlignFlyInCntBase( Y() + long( nTmpAscent ) ); 1584 pCurr->CalcLine( *this, GetInfo() ); 1585 CalcRealHeight(); 1586 } 1587 1588 // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird 1589 if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() ) 1590 { 1591 pCurr->SetRealHeight( GetInfo().GetLineHeight() ); 1592 bBuild = sal_False; 1593 } 1594 else 1595 { 1596 bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow(GetInfo()) ) 1597 || GetInfo().CheckFtnPortion(pCurr); 1598 if( bBuild ) 1599 { 1600 GetInfo().SetNumDone( bOldNumDone ); 1601 GetInfo().ResetMaxWidthDiff(); 1602 1603 // delete old rest 1604 if ( GetInfo().GetRest() ) 1605 { 1606 delete GetInfo().GetRest(); 1607 GetInfo().SetRest( 0 ); 1608 } 1609 1610 // set original rest portion 1611 if ( pSaveFld ) 1612 GetInfo().SetRest( new SwFldPortion( *pSaveFld ) ); 1613 1614 pCurr->SetLen( 0 ); 1615 pCurr->Width(0); 1616 pCurr->Truncate(); 1617 } 1618 } 1619 } 1620 1621 // calculate optimal repaint rectangle 1622 if ( bOptimizeRepaint ) 1623 { 1624 GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) ); 1625 if ( pFlyStart ) 1626 delete pFlyStart; 1627 } 1628 else 1629 // Special case: We do not allow an optimitation of the repaint 1630 // area, but during formatting the repaint offset is set to indicate 1631 // a maximum value for the offset. This value has to be reset: 1632 GetInfo().SetPaintOfst( 0 ); 1633 1634 // This corrects the start of the reformat range if something has 1635 // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt 1636 // will give us a wrong result if we have to reformat another line 1637 GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() ); 1638 1639 // delete master copy of rest portion 1640 if ( pSaveFld ) 1641 delete pSaveFld; 1642 1643 xub_StrLen nNewStart = nStartPos + pCurr->GetLen(); 1644 1645 // adjust text if kana compression is enabled 1646 if ( GetInfo().CompressLine() ) 1647 { 1648 SwTwips nRepaintOfst = CalcKanaAdj( pCurr ); 1649 1650 // adjust repaint offset 1651 if ( nRepaintOfst < GetInfo().GetPaintOfst() ) 1652 GetInfo().SetPaintOfst( nRepaintOfst ); 1653 } 1654 1655 CalcAdjustLine( pCurr ); 1656 1657 if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() ) 1658 { 1659 SetFlyInCntBase(); 1660 GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling 1661 // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind 1662 // auch formatiert werden. 1663 GetInfo().SetShift( sal_True ); 1664 } 1665 1666 if ( IsFlyInCntBase() && !IsQuick() ) 1667 UpdatePos( pCurr, GetTopLeft(), GetStart() ); 1668 1669 return nNewStart; 1670 } 1671 1672 /************************************************************************* 1673 * SwTxtFormatter::RecalcRealHeight() 1674 *************************************************************************/ 1675 1676 void SwTxtFormatter::RecalcRealHeight() 1677 { 1678 sal_Bool bMore = sal_True; 1679 while(bMore) 1680 { 1681 DBG_LOOP; 1682 CalcRealHeight(); 1683 bMore = Next() != 0; 1684 } 1685 } 1686 1687 /************************************************************************* 1688 * SwTxtFormatter::CalcRealHeight() 1689 *************************************************************************/ 1690 1691 void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine ) 1692 { 1693 KSHORT nLineHeight = pCurr->Height(); 1694 pCurr->SetClipping( sal_False ); 1695 1696 GETGRID( pFrm->FindPageFrm() ) 1697 if ( pGrid && GetInfo().SnapToGrid() ) 1698 { 1699 const sal_uInt16 nGridWidth = pGrid->GetBaseHeight(); 1700 const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight(); 1701 const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow(); 1702 1703 nLineHeight = nGridWidth + nRubyHeight; 1704 sal_uInt16 nLineDist = nLineHeight; 1705 1706 while ( pCurr->Height() > nLineHeight ) 1707 nLineHeight = nLineHeight + nLineDist; 1708 1709 KSHORT nAsc = pCurr->GetAscent() + 1710 ( bRubyTop ? 1711 ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 : 1712 ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 ); 1713 1714 pCurr->Height( nLineHeight ); 1715 pCurr->SetAscent( nAsc ); 1716 pInf->GetParaPortion()->SetFixLineHeight(); 1717 1718 // we ignore any line spacing options except from ... 1719 const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing(); 1720 if ( ! IsParaLine() && pSpace && 1721 SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() ) 1722 { 1723 sal_uLong nTmp = pSpace->GetPropLineSpace(); 1724 1725 if( nTmp < 100 ) 1726 nTmp = 100; 1727 1728 nTmp *= nLineHeight; 1729 nLineHeight = (sal_uInt16)(nTmp / 100); 1730 } 1731 1732 pCurr->SetRealHeight( nLineHeight ); 1733 return; 1734 } 1735 1736 // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese 1737 // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere 1738 // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem 1739 // Shift-Return), die das Register durchaus beachten soll. 1740 if( !pCurr->IsDummy() || ( !pCurr->GetNext() && 1741 GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) ) 1742 { 1743 const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing(); 1744 if( pSpace ) 1745 { 1746 switch( pSpace->GetLineSpaceRule() ) 1747 { 1748 case SVX_LINE_SPACE_AUTO: 1749 break; 1750 case SVX_LINE_SPACE_MIN: 1751 { 1752 if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) ) 1753 nLineHeight = pSpace->GetLineHeight(); 1754 break; 1755 } 1756 case SVX_LINE_SPACE_FIX: 1757 { 1758 nLineHeight = pSpace->GetLineHeight(); 1759 KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80% 1760 if( nAsc < pCurr->GetAscent() || 1761 nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() ) 1762 pCurr->SetClipping( sal_True ); 1763 pCurr->Height( nLineHeight ); 1764 pCurr->SetAscent( nAsc ); 1765 pInf->GetParaPortion()->SetFixLineHeight(); 1766 } 1767 break; 1768 default: ASSERT( sal_False, ": unknown LineSpaceRule" ); 1769 } 1770 if( !IsParaLine() ) 1771 switch( pSpace->GetInterLineSpaceRule() ) 1772 { 1773 case SVX_INTER_LINE_SPACE_OFF: 1774 break; 1775 case SVX_INTER_LINE_SPACE_PROP: 1776 { 1777 long nTmp = pSpace->GetPropLineSpace(); 1778 // 50% ist das Minimum, bei 0% schalten wir auf 1779 // den Defaultwert 100% um ... 1780 if( nTmp < 50 ) 1781 nTmp = nTmp ? 50 : 100; 1782 1783 nTmp *= nLineHeight; 1784 nTmp /= 100; 1785 if( !nTmp ) 1786 ++nTmp; 1787 nLineHeight = (KSHORT)nTmp; 1788 break; 1789 } 1790 case SVX_INTER_LINE_SPACE_FIX: 1791 { 1792 nLineHeight = nLineHeight + pSpace->GetInterLineSpace(); 1793 break; 1794 } 1795 default: ASSERT( sal_False, ": unknown InterLineSpaceRule" ); 1796 } 1797 } 1798 #if OSL_DEBUG_LEVEL > 1 1799 KSHORT nDummy = nLineHeight + 1; 1800 (void)nDummy; 1801 #endif 1802 1803 if( IsRegisterOn() ) 1804 { 1805 SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height(); 1806 SWRECTFN( pFrm ) 1807 if ( bVert ) 1808 nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY ); 1809 nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() ); 1810 KSHORT nDiff = KSHORT( nTmpY % RegDiff() ); 1811 if( nDiff ) 1812 nLineHeight += RegDiff() - nDiff; 1813 } 1814 } 1815 pCurr->SetRealHeight( nLineHeight ); 1816 } 1817 1818 /************************************************************************* 1819 * SwTxtFormatter::FeedInf() 1820 *************************************************************************/ 1821 1822 void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const 1823 { 1824 // 3260, 3860: Fly auf jeden Fall loeschen! 1825 ClearFly( rInf ); 1826 rInf.Init(); 1827 1828 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() ); 1829 rInf.SetRoot( pCurr ); 1830 rInf.SetLineStart( nStart ); 1831 rInf.SetIdx( nStart ); 1832 1833 // Handle overflows: 1834 // --> FME 2004-11-25 #i34348# Changed type from sal_uInt16 to SwTwips 1835 SwTwips nTmpLeft = Left(); 1836 SwTwips nTmpRight = Right(); 1837 SwTwips nTmpFirst = FirstLeft(); 1838 // <-- 1839 1840 if ( nTmpLeft > USHRT_MAX || 1841 nTmpRight > USHRT_MAX || 1842 nTmpFirst > USHRT_MAX ) 1843 { 1844 SWRECTFN( rInf.GetTxtFrm() ) 1845 nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)(); 1846 nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)(); 1847 nTmpFirst = nTmpLeft; 1848 } 1849 1850 rInf.Left( nTmpLeft ); 1851 rInf.Right( nTmpRight ); 1852 rInf.First( nTmpFirst ); 1853 1854 rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) ); 1855 rInf.Width( rInf.RealWidth() ); 1856 if( ((SwTxtFormatter*)this)->GetRedln() ) 1857 { 1858 ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() ); 1859 ((SwTxtFormatter*)this)->GetRedln()->Reset(); 1860 } 1861 } 1862 1863 /************************************************************************* 1864 * SwTxtFormatter::FormatReset() 1865 *************************************************************************/ 1866 1867 void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf ) 1868 { 1869 pCurr->Truncate(); 1870 pCurr->Init(); 1871 if( pBlink && pCurr->IsBlinking() ) 1872 pBlink->Delete( pCurr ); 1873 1874 // delete pSpaceAdd und pKanaComp 1875 pCurr->FinishSpaceAdd(); 1876 pCurr->FinishKanaComp(); 1877 pCurr->ResetFlags(); 1878 FeedInf( rInf ); 1879 } 1880 1881 /************************************************************************* 1882 * SwTxtFormatter::CalcOnceMore() 1883 *************************************************************************/ 1884 1885 sal_Bool SwTxtFormatter::CalcOnceMore() 1886 { 1887 if( pDropFmt ) 1888 { 1889 const KSHORT nOldDrop = GetDropHeight(); 1890 CalcDropHeight( pDropFmt->GetLines() ); 1891 bOnceMore = nOldDrop != GetDropHeight(); 1892 } 1893 else 1894 bOnceMore = sal_False; 1895 return bOnceMore; 1896 } 1897 1898 /************************************************************************* 1899 * SwTxtFormatter::CalcBottomLine() 1900 *************************************************************************/ 1901 1902 SwTwips SwTxtFormatter::CalcBottomLine() const 1903 { 1904 SwTwips nRet = Y() + GetLineHeight(); 1905 SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom(); 1906 if( nMin && ++nMin > nRet ) 1907 { 1908 SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height() 1909 - pFrm->Prt().Top(); 1910 if( nRet + nDist < nMin ) 1911 { 1912 sal_Bool bRepaint = HasTruncLines() && 1913 GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1; 1914 nRet = nMin - nDist; 1915 if( bRepaint ) 1916 { 1917 ((SwRepaint*)GetInfo().GetParaPortion() 1918 ->GetRepaint())->Bottom( nRet-1 ); 1919 ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 ); 1920 } 1921 } 1922 } 1923 return nRet; 1924 } 1925 1926 /************************************************************************* 1927 * SwTxtFormatter::_CalcFitToContent() 1928 * 1929 * FME/OD: This routine does a limited text formatting. 1930 *************************************************************************/ 1931 1932 SwTwips SwTxtFormatter::_CalcFitToContent() 1933 { 1934 FormatReset( GetInfo() ); 1935 BuildPortions( GetInfo() ); 1936 pCurr->CalcLine( *this, GetInfo() ); 1937 return pCurr->Width(); 1938 } 1939 1940 /************************************************************************* 1941 * SwTxtFormatter::AllowRepaintOpt() 1942 * 1943 * determines if the calculation of a repaint offset is allowed 1944 * otherwise each line is painted from 0 (this is a copy of the beginning 1945 * of the former SwTxtFormatter::Recycle() function 1946 *************************************************************************/ 1947 sal_Bool SwTxtFormatter::AllowRepaintOpt() const 1948 { 1949 // reformat position in front of current line? Only in this case 1950 // we want to set the repaint offset 1951 sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() && 1952 pCurr->GetLen(); 1953 1954 // a special case is the last line of a block adjusted paragraph: 1955 if ( bOptimizeRepaint ) 1956 { 1957 switch( GetAdjust() ) 1958 { 1959 case SVX_ADJUST_BLOCK: 1960 { 1961 if( IsLastBlock() || IsLastCenter() ) 1962 bOptimizeRepaint = sal_False; 1963 else 1964 { 1965 // ????: Blank in der letzten Masterzeile (blocksat.sdw) 1966 bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow(); 1967 if ( bOptimizeRepaint ) 1968 { 1969 SwLinePortion *pPos = pCurr->GetFirstPortion(); 1970 while ( pPos && !pPos->IsFlyPortion() ) 1971 pPos = pPos->GetPortion(); 1972 bOptimizeRepaint = !pPos; 1973 } 1974 } 1975 break; 1976 } 1977 case SVX_ADJUST_CENTER: 1978 case SVX_ADJUST_RIGHT: 1979 bOptimizeRepaint = sal_False; 1980 break; 1981 default: ; 1982 } 1983 } 1984 1985 // Schon wieder ein Sonderfall: unsichtbare SoftHyphs 1986 const xub_StrLen nReformat = GetInfo().GetReformatStart(); 1987 if( bOptimizeRepaint && STRING_LEN != nReformat ) 1988 { 1989 const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat ); 1990 bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh ) 1991 || ! GetInfo().HasHint( nReformat ); 1992 } 1993 1994 return bOptimizeRepaint; 1995 } 1996 1997 /************************************************************************* 1998 * SwTxtFormatter::CalcOptRepaint() 1999 * 2000 * calculates an optimal repaint offset for the current line 2001 *************************************************************************/ 2002 long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd, 2003 const SvLongs* pFlyStart ) 2004 { 2005 if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() ) 2006 // the reformat position is behind our new line, that means 2007 // something of our text has moved to the next line 2008 return 0; 2009 2010 xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd ); 2011 2012 // in case we do not have any fly in our line, our repaint position 2013 // is the changed position - 1 2014 if ( ! pFlyStart && ! pCurr->IsFly() ) 2015 { 2016 // this is the maximum repaint offset determined during formatting 2017 // for example: the beginning of the first right tab stop 2018 // if this value is 0, this means that we do not have an upper 2019 // limit for the repaint offset 2020 const long nFormatRepaint = GetInfo().GetPaintOfst(); 2021 2022 if ( nReformat < GetInfo().GetLineStart() + 3 ) 2023 return 0; 2024 2025 // step back two positions for smoother repaint 2026 nReformat -= 2; 2027 2028 #ifndef QUARTZ 2029 #ifndef ENABLE_GRAPHITE 2030 // --> FME 2004-09-27 #i28795#, #i34607#, #i38388# 2031 // step back six(!) more characters for complex scripts 2032 // this is required e.g., for Khmer (thank you, Javier!) 2033 const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo(); 2034 xub_StrLen nMaxContext = 0; 2035 if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) ) 2036 nMaxContext = 6; 2037 #else 2038 // Some Graphite fonts need context for scripts not marked as complex 2039 static const xub_StrLen nMaxContext = 10; 2040 #endif 2041 #else 2042 // some fonts like Quartz's Zapfino need more context 2043 // TODO: query FontInfo for maximum unicode context 2044 static const xub_StrLen nMaxContext = 8; 2045 #endif 2046 if( nMaxContext > 0 ) 2047 { 2048 if ( nReformat > GetInfo().GetLineStart() + nMaxContext ) 2049 nReformat = nReformat - nMaxContext; 2050 else 2051 nReformat = GetInfo().GetLineStart(); 2052 } 2053 // <-- 2054 2055 // Weird situation: Our line used to end with a hole portion 2056 // and we delete some characters at the end of our line. We have 2057 // to take care for repainting the blanks which are not anymore 2058 // covered by the hole portion 2059 while ( nReformat > GetInfo().GetLineStart() && 2060 CH_BLANK == GetInfo().GetChar( nReformat ) ) 2061 --nReformat; 2062 2063 ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" ); 2064 SwRect aRect; 2065 2066 // Note: GetChareRect is not const. It definitely changes the 2067 // bMulti flag. We have to save and resore the old value. 2068 sal_Bool bOldMulti = GetInfo().IsMulti(); 2069 GetCharRect( &aRect, nReformat ); 2070 GetInfo().SetMulti( bOldMulti ); 2071 2072 return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) : 2073 aRect.Left(); 2074 } 2075 else 2076 { 2077 // nReformat may be wrong, if something around flys has changed: 2078 // we compare the former and the new fly positions in this line 2079 // if anything has changed, we carefully have to adjust the right 2080 // repaint position 2081 long nPOfst = 0; 2082 sal_uInt16 nCnt = 0; 2083 sal_uInt16 nX = 0; 2084 sal_uInt16 nIdx = GetInfo().GetLineStart(); 2085 SwLinePortion* pPor = pCurr->GetFirstPortion(); 2086 2087 while ( pPor ) 2088 { 2089 if ( pPor->IsFlyPortion() ) 2090 { 2091 // compare start of fly with former start of fly 2092 if ( pFlyStart && 2093 nCnt < pFlyStart->Count() && 2094 nX == (*pFlyStart)[ nCnt ] && 2095 nIdx < nReformat 2096 ) 2097 // found fix position, nothing has changed left from nX 2098 nPOfst = nX + pPor->Width(); 2099 else 2100 break; 2101 2102 nCnt++; 2103 } 2104 nX = nX + pPor->Width(); 2105 nIdx = nIdx + pPor->GetLen(); 2106 pPor = pPor->GetPortion(); 2107 } 2108 2109 return nPOfst + GetLeftMargin(); 2110 } 2111 } 2112 2113 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos ) 2114 { 2115 // Only if hidden text should not be shown: 2116 // if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() ) 2117 const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar(); 2118 const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( sal_True ) && rInf.GetOpt().IsPrinting(); 2119 if (bShowInDocView || bShowForPrinting) 2120 return false; 2121 2122 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo(); 2123 xub_StrLen nHiddenStart; 2124 xub_StrLen nHiddenEnd; 2125 rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd ); 2126 if ( nHiddenEnd ) 2127 { 2128 rPos = nHiddenEnd; 2129 return true; 2130 } 2131 2132 return false; 2133 } 2134