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