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 pPor = new SwToxPortion; 869 else 870 { 871 if( GetFnt()->IsRef() ) 872 pPor = new SwRefPortion; 873 else if (GetFnt()->IsMeta()) 874 { 875 pPor = new SwMetaPortion; 876 } 877 else 878 { 879 // Erst zum Schluss ! 880 // Wenn pCurr keine Breite hat, kann sie trotzdem schon Inhalt haben, 881 // z.B. bei nicht darstellbaren Zeichen. 882 if( rInf.GetLen() > 0 ) 883 { 884 if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDSTART ) 885 pPor = new SwFieldMarkPortion(); 886 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FIELDEND ) 887 pPor = new SwFieldMarkPortion(); 888 else if( rInf.GetTxt().GetChar(rInf.GetIdx())==CH_TXT_ATR_FORMELEMENT ) 889 pPor = new SwFieldFormPortion(); 890 } 891 if( !pPor ) 892 { 893 if( !rInf.X() && !pCurr->GetPortion() && !pCurr->GetLen() && !GetFnt()->IsURL() ) 894 pPor = pCurr; 895 else 896 { 897 pPor = new SwTxtPortion; 898 if( GetFnt()->IsURL() ) 899 pPor->SetWhichPor( POR_URL ); 900 } 901 } 902 } 903 } 904 return pPor; 905 } 906 907 /************************************************************************* 908 * SwTxtFormatter::NewTxtPortion() 909 *************************************************************************/ 910 // Die Laenge wird ermittelt, folgende Portion-Grenzen sind definiert: 911 // 1) Tabs 912 // 2) Linebreaks 913 // 3) CH_TXTATR_BREAKWORD / CH_TXTATR_INWORD 914 // 4) naechster Attributwechsel 915 916 SwTxtPortion *SwTxtFormatter::NewTxtPortion( SwTxtFormatInfo &rInf ) 917 { 918 // Wenn wir am Zeilenbeginn stehen, nehmen wir pCurr 919 // Wenn pCurr nicht von SwTxtPortion abgeleitet ist, 920 // muessen wir duplizieren ... 921 Seek( rInf.GetIdx() ); 922 SwTxtPortion *pPor = WhichTxtPor( rInf ); 923 924 // until next attribute change: 925 const xub_StrLen nNextAttr = GetNextAttr(); 926 xub_StrLen nNextChg = Min( nNextAttr, rInf.GetTxt().Len() ); 927 928 // end of script type: 929 const xub_StrLen nNextScript = pScriptInfo->NextScriptChg( rInf.GetIdx() ); 930 nNextChg = Min( nNextChg, nNextScript ); 931 932 // end of direction: 933 const xub_StrLen nNextDir = pScriptInfo->NextDirChg( rInf.GetIdx() ); 934 nNextChg = Min( nNextChg, nNextDir ); 935 936 // 7515, 7516, 3470, 6441 : Turbo-Boost 937 // Es wird unterstellt, dass die Buchstaben eines Fonts nicht 938 // groesser als doppelt so breit wie hoch sind. 939 // 7659: Ganz verrueckt: man muss sich auf den Ascent beziehen. 940 // Falle: GetSize() enthaelt die Wunschhoehe, die reale Hoehe 941 // ergibt sich erst im CalcAscent! 942 // 7697: Das Verhaeltnis ist noch krasser: ein Blank im Times 943 // New Roman besitzt einen Ascent von 182, eine Hoehe von 200 944 // und eine Breite von 53! Daraus folgt, dass eine Zeile mit 945 // vielen Blanks falsch eingeschaetzt wird. Wir erhoehen von 946 // Faktor 2 auf 8 (wg. negativen Kernings). 947 948 pPor->SetLen(1); 949 CalcAscent( rInf, pPor ); 950 951 const SwFont* pTmpFnt = rInf.GetFont(); 952 KSHORT nExpect = Min( KSHORT( ((Font *)pTmpFnt)->GetSize().Height() ), 953 KSHORT( pPor->GetAscent() ) ) / 8; 954 if ( !nExpect ) 955 nExpect = 1; 956 nExpect = (sal_uInt16)(rInf.GetIdx() + ((rInf.Width() - rInf.X()) / nExpect)); 957 if( nExpect > rInf.GetIdx() && nNextChg > nExpect ) 958 nNextChg = Min( nExpect, rInf.GetTxt().Len() ); 959 960 // we keep an invariant during method calls: 961 // there are no portion ending characters like hard spaces 962 // or tabs in [ nLeftScanIdx, nRightScanIdx ] 963 if ( nLeftScanIdx <= rInf.GetIdx() && rInf.GetIdx() <= nRightScanIdx ) 964 { 965 if ( nNextChg > nRightScanIdx ) 966 nNextChg = nRightScanIdx = 967 rInf.ScanPortionEnd( nRightScanIdx, nNextChg ); 968 } 969 else 970 { 971 nLeftScanIdx = rInf.GetIdx(); 972 nNextChg = nRightScanIdx = 973 rInf.ScanPortionEnd( rInf.GetIdx(), nNextChg ); 974 } 975 976 pPor->SetLen( nNextChg - rInf.GetIdx() ); 977 rInf.SetLen( pPor->GetLen() ); 978 return pPor; 979 } 980 981 982 /************************************************************************* 983 * SwTxtFormatter::WhichFirstPortion() 984 *************************************************************************/ 985 986 SwLinePortion *SwTxtFormatter::WhichFirstPortion(SwTxtFormatInfo &rInf) 987 { 988 SwLinePortion *pPor = 0; 989 990 if( rInf.GetRest() ) 991 { 992 // 5010: Tabs und Felder 993 if( '\0' != rInf.GetHookChar() ) 994 return 0; 995 996 pPor = rInf.GetRest(); 997 if( pPor->IsErgoSumPortion() ) 998 rInf.SetErgoDone(sal_True); 999 else 1000 if( pPor->IsFtnNumPortion() ) 1001 rInf.SetFtnDone(sal_True); 1002 else 1003 if( pPor->InNumberGrp() ) 1004 rInf.SetNumDone(sal_True); 1005 if( pPor ) 1006 { 1007 rInf.SetRest(0); 1008 pCurr->SetRest( sal_True ); 1009 return pPor; 1010 } 1011 } 1012 1013 // ???? und ????: im Follow duerfen wir schon stehen, 1014 // entscheidend ist, ob pFrm->GetOfst() == 0 ist! 1015 if( rInf.GetIdx() ) 1016 { 1017 // Nun koennen auch FtnPortions und ErgoSumPortions 1018 // verlaengert werden. 1019 1020 // 1) Die ErgoSumTexte 1021 if( !rInf.IsErgoDone() ) 1022 { 1023 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) 1024 pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); 1025 rInf.SetErgoDone( sal_True ); 1026 } 1027 1028 // 2) Arrow portions 1029 if( !pPor && !rInf.IsArrowDone() ) 1030 { 1031 if( pFrm->GetOfst() && !pFrm->IsFollow() && 1032 rInf.GetIdx() == pFrm->GetOfst() ) 1033 pPor = new SwArrowPortion( *pCurr ); 1034 rInf.SetArrowDone( sal_True ); 1035 } 1036 1037 // 3) Kerning portions at beginning of line in grid mode 1038 if ( ! pPor && ! pCurr->GetPortion() ) 1039 { 1040 GETGRID( GetTxtFrm()->FindPageFrm() ) 1041 if ( pGrid ) 1042 pPor = new SwKernPortion( *pCurr ); 1043 } 1044 1045 // 4) Die Zeilenreste (mehrzeilige Felder) 1046 if( !pPor ) 1047 { 1048 pPor = rInf.GetRest(); 1049 // 6922: Nur bei pPor natuerlich. 1050 if( pPor ) 1051 { 1052 pCurr->SetRest( sal_True ); 1053 rInf.SetRest(0); 1054 } 1055 } 1056 } 1057 else 1058 { 1059 // 5) Die Fussnotenzahlen 1060 if( !rInf.IsFtnDone() ) 1061 { 1062 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), 1063 "Rotated number portion trouble" ) 1064 1065 sal_Bool bFtnNum = pFrm->IsFtnNumFrm(); 1066 rInf.GetParaPortion()->SetFtnNum( bFtnNum ); 1067 if( bFtnNum ) 1068 pPor = (SwLinePortion*)NewFtnNumPortion( rInf ); 1069 rInf.SetFtnDone( sal_True ); 1070 } 1071 1072 // 6) Die ErgoSumTexte gibt es natuerlich auch im TextMaster, 1073 // entscheidend ist, ob der SwFtnFrm ein Follow ist. 1074 if( !rInf.IsErgoDone() && !pPor && ! rInf.IsMulti() ) 1075 { 1076 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() ) 1077 pPor = (SwLinePortion*)NewErgoSumPortion( rInf ); 1078 rInf.SetErgoDone( sal_True ); 1079 } 1080 1081 // 7) Die Numerierungen 1082 if( !rInf.IsNumDone() && !pPor ) 1083 { 1084 ASSERT( ( ! rInf.IsMulti() && ! pMulti ) || pMulti->HasRotation(), 1085 "Rotated number portion trouble" ) 1086 1087 // Wenn wir im Follow stehen, dann natuerlich nicht. 1088 if( GetTxtFrm()->GetTxtNode()->GetNumRule() ) 1089 pPor = (SwLinePortion*)NewNumberPortion( rInf ); 1090 rInf.SetNumDone( sal_True ); 1091 } 1092 // 8) Die DropCaps 1093 if( !pPor && GetDropFmt() && ! rInf.IsMulti() ) 1094 pPor = (SwLinePortion*)NewDropPortion( rInf ); 1095 1096 // 9) Kerning portions at beginning of line in grid mode 1097 if ( !pPor && !pCurr->GetPortion() ) 1098 { 1099 GETGRID( GetTxtFrm()->FindPageFrm() ) 1100 if ( pGrid ) 1101 pPor = new SwKernPortion( *pCurr ); 1102 } 1103 } 1104 1105 // 10) Decimal tab portion at the beginning of each line in table cells 1106 if ( !pPor && !pCurr->GetPortion() && 1107 GetTxtFrm()->IsInTab() && 1108 GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) ) 1109 { 1110 pPor = NewTabPortion( rInf, true ); 1111 } 1112 1113 // 11) suffix of meta-field 1114 if (!pPor) 1115 { 1116 pPor = TryNewNoLengthPortion(rInf); 1117 } 1118 1119 return pPor; 1120 } 1121 1122 sal_Bool lcl_OldFieldRest( const SwLineLayout* pCurr ) 1123 { 1124 if( !pCurr->GetNext() ) 1125 return sal_False; 1126 const SwLinePortion *pPor = pCurr->GetNext()->GetPortion(); 1127 sal_Bool bRet = sal_False; 1128 while( pPor && !bRet ) 1129 { 1130 bRet = (pPor->InFldGrp() && ((SwFldPortion*)pPor)->IsFollow()) || 1131 (pPor->IsMultiPortion() && ((SwMultiPortion*)pPor)->IsFollowFld()); 1132 if( !pPor->GetLen() ) 1133 break; 1134 pPor = pPor->GetPortion(); 1135 } 1136 return bRet; 1137 } 1138 1139 /************************************************************************* 1140 * SwTxtFormatter::NewPortion() 1141 *************************************************************************/ 1142 1143 /* NewPortion stellt rInf.nLen ein. 1144 * Eine SwTxtPortion wird begrenzt durch ein tab, break, txtatr, 1145 * attrwechsel. 1146 * Drei Faelle koennen eintreten: 1147 * 1) Die Zeile ist voll und der Umbruch wurde nicht emuliert 1148 * -> return 0; 1149 * 2) Die Zeile ist voll und es wurde ein Umbruch emuliert 1150 * -> Breite neu einstellen und return new FlyPortion 1151 * 3) Es muss eine neue Portion gebaut werden. 1152 * -> CalcFlyWidth emuliert ggf. die Breite und return Portion 1153 */ 1154 1155 SwLinePortion *SwTxtFormatter::NewPortion( SwTxtFormatInfo &rInf ) 1156 { 1157 // Underflow hat Vorrang 1158 rInf.SetStopUnderFlow( sal_False ); 1159 if( rInf.GetUnderFlow() ) 1160 { 1161 ASSERT( rInf.IsFull(), "SwTxtFormatter::NewPortion: underflow but not full" ); 1162 return UnderFlow( rInf ); 1163 } 1164 1165 // Wenn die Zeile voll ist, koennten noch Flys oder 1166 // UnderFlow-LinePortions warten ... 1167 if( rInf.IsFull() ) 1168 { 1169 // ????: LineBreaks und Flys (bug05.sdw) 1170 // 8450: IsDummy() 1171 if( rInf.IsNewLine() && (!rInf.GetFly() || !pCurr->IsDummy()) ) 1172 return 0; 1173 1174 // Wenn der Text an den Fly gestossen ist, oder wenn 1175 // der Fly als erstes drankommt, weil er ueber dem linken 1176 // Rand haengt, wird GetFly() returnt. 1177 // Wenn IsFull() und kein GetFly() vorhanden ist, gibt's 1178 // naturgemaesz eine 0. 1179 if( rInf.GetFly() ) 1180 { 1181 if( rInf.GetLast()->IsBreakPortion() ) 1182 { 1183 delete rInf.GetFly(); 1184 rInf.SetFly( 0 ); 1185 } 1186 1187 return rInf.GetFly(); 1188 } 1189 // Ein fieser Sonderfall: ein Rahmen ohne Umlauf kreuzt den 1190 // Ftn-Bereich. Wir muessen die Ftn-Portion als Zeilenrest 1191 // bekanntgeben, damit SwTxtFrm::Format nicht abbricht 1192 // (die Textmasse wurde ja durchformatiert). 1193 if( rInf.GetRest() ) 1194 rInf.SetNewLine( sal_True ); 1195 else 1196 { 1197 // Wenn die naechste Zeile mit einem Rest eines Feldes beginnt, 1198 // jetzt aber kein Rest mehr anliegt, 1199 // muss sie auf jeden Fall neu formatiert werden! 1200 if( lcl_OldFieldRest( GetCurr() ) ) 1201 rInf.SetNewLine( sal_True ); 1202 else 1203 { 1204 SwLinePortion *pFirst = WhichFirstPortion( rInf ); 1205 if( pFirst ) 1206 { 1207 rInf.SetNewLine( sal_True ); 1208 if( pFirst->InNumberGrp() ) 1209 rInf.SetNumDone( sal_False) ; 1210 delete pFirst; 1211 } 1212 } 1213 } 1214 1215 return 0; 1216 } 1217 1218 SwLinePortion *pPor = WhichFirstPortion( rInf ); 1219 1220 // Check for Hidden Portion: 1221 if ( !pPor ) 1222 { 1223 xub_StrLen nEnd = rInf.GetIdx(); 1224 if ( lcl_BuildHiddenPortion( rInf, nEnd ) ) 1225 pPor = new SwHiddenTextPortion( nEnd - rInf.GetIdx() ); 1226 } 1227 1228 if( !pPor ) 1229 { 1230 if( ( !pMulti || pMulti->IsBidi() ) && 1231 // --> FME 2005-02-14 #i42734# 1232 // No multi portion if there is a hook character waiting: 1233 ( !rInf.GetRest() || '\0' == rInf.GetHookChar() ) ) 1234 // <-- 1235 { 1236 // We open a multiportion part, if we enter a multi-line part 1237 // of the paragraph. 1238 xub_StrLen nEnd = rInf.GetIdx(); 1239 SwMultiCreator* pCreate = rInf.GetMultiCreator( nEnd, pMulti ); 1240 if( pCreate ) 1241 { 1242 SwMultiPortion* pTmp = NULL; 1243 1244 if ( SW_MC_BIDI == pCreate->nId ) 1245 pTmp = new SwBidiPortion( nEnd, pCreate->nLevel ); 1246 else if ( SW_MC_RUBY == pCreate->nId ) 1247 { 1248 Seek( rInf.GetIdx() ); 1249 sal_Bool bRubyTop; 1250 sal_Bool* pRubyPos = 0; 1251 1252 if ( rInf.SnapToGrid() ) 1253 { 1254 GETGRID( GetTxtFrm()->FindPageFrm() ) 1255 if ( pGrid ) 1256 { 1257 bRubyTop = ! pGrid->GetRubyTextBelow(); 1258 pRubyPos = &bRubyTop; 1259 } 1260 } 1261 1262 pTmp = new SwRubyPortion( *pCreate, *rInf.GetFont(), 1263 *GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess(), 1264 nEnd, 0, pRubyPos ); 1265 } 1266 else if( SW_MC_ROTATE == pCreate->nId ) 1267 pTmp = new SwRotatedPortion( *pCreate, nEnd, 1268 GetTxtFrm()->IsRightToLeft() ); 1269 else 1270 pTmp = new SwDoubleLinePortion( *pCreate, nEnd ); 1271 1272 delete pCreate; 1273 CalcFlyWidth( rInf ); 1274 1275 return pTmp; 1276 } 1277 } 1278 // 5010: Tabs und Felder 1279 xub_Unicode cChar = rInf.GetHookChar(); 1280 1281 if( cChar ) 1282 { 1283 /* Wir holen uns nocheinmal cChar, um sicherzustellen, dass das 1284 * Tab jetzt wirklich ansteht und nicht auf die naechste Zeile 1285 * gewandert ist ( so geschehen hinter Rahmen ). 1286 * Wenn allerdings eine FldPortion im Rest wartet, muessen wir 1287 * das cChar natuerlich aus dem Feldinhalt holen, z.B. bei 1288 * DezimalTabs und Feldern (22615) 1289 */ 1290 if( !rInf.GetRest() || !rInf.GetRest()->InFldGrp() ) 1291 cChar = rInf.GetChar( rInf.GetIdx() ); 1292 rInf.ClearHookChar(); 1293 } 1294 else 1295 { 1296 if( rInf.GetIdx() >= rInf.GetTxt().Len() ) 1297 { 1298 rInf.SetFull(sal_True); 1299 CalcFlyWidth( rInf ); 1300 return pPor; 1301 } 1302 cChar = rInf.GetChar( rInf.GetIdx() ); 1303 } 1304 1305 switch( cChar ) 1306 { 1307 case CH_TAB: 1308 pPor = NewTabPortion( rInf, false ); break; 1309 1310 case CH_BREAK: 1311 pPor = new SwBreakPortion( *rInf.GetLast() ); break; 1312 1313 case CHAR_SOFTHYPHEN: // soft hyphen 1314 pPor = new SwSoftHyphPortion; break; 1315 1316 case CHAR_HARDBLANK: // no-break space 1317 pPor = new SwBlankPortion( ' ' ); break; 1318 1319 case CHAR_HARDHYPHEN: // non-breaking hyphen 1320 pPor = new SwBlankPortion( '-' ); break; 1321 1322 case CHAR_ZWSP: // zero width space 1323 case CHAR_ZWNBSP : // word joiner 1324 // case CHAR_RLM : // right to left mark 1325 // case CHAR_LRM : // left to right mark 1326 pPor = new SwControlCharPortion( cChar ); break; 1327 1328 case CH_TXTATR_BREAKWORD: 1329 case CH_TXTATR_INWORD: 1330 if( rInf.HasHint( rInf.GetIdx() ) ) 1331 { 1332 pPor = NewExtraPortion( rInf ); 1333 break; 1334 } 1335 // No break 1336 default : 1337 { 1338 SwTabPortion* pLastTabPortion = rInf.GetLastTab(); 1339 if ( pLastTabPortion && cChar == rInf.GetTabDecimal() ) 1340 { 1341 // --> FME 2005-12-19 #127428# Abandon dec. tab position if line is full: 1342 // We have a decimal tab portion in the line and the next character has to be 1343 // aligned at the tab stop position. We store the width from the beginning of 1344 // the tab stop portion up to the portion containint the decimal separator: 1345 if ( GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) /*rInf.GetVsh()->IsTabCompat();*/ && 1346 POR_TABDECIMAL == pLastTabPortion->GetWhichPor() ) 1347 { 1348 ASSERT( rInf.X() >= pLastTabPortion->Fix(), "Decimal tab stop position cannot be calculated" ) 1349 const sal_uInt16 nWidthOfPortionsUpToDecimalPosition = (sal_uInt16)(rInf.X() - pLastTabPortion->Fix() ); 1350 static_cast<SwTabDecimalPortion*>(pLastTabPortion)->SetWidthOfPortionsUpToDecimalPosition( nWidthOfPortionsUpToDecimalPosition ); 1351 rInf.SetTabDecimal( 0 ); 1352 } 1353 // <-- 1354 else 1355 rInf.SetFull( rInf.GetLastTab()->Format( rInf ) ); 1356 } 1357 1358 if( rInf.GetRest() ) 1359 { 1360 if( rInf.IsFull() ) 1361 { 1362 rInf.SetNewLine(sal_True); 1363 return 0; 1364 } 1365 pPor = rInf.GetRest(); 1366 rInf.SetRest(0); 1367 } 1368 else 1369 { 1370 if( rInf.IsFull() ) 1371 return 0; 1372 pPor = NewTxtPortion( rInf ); 1373 } 1374 break; 1375 } 1376 } 1377 1378 // Wenn eine Portion erzeugt wird, obwohl eine RestPortion ansteht, 1379 // dann haben wir es mit einem Feld zu tun, das sich aufgesplittet 1380 // hat, weil z.B. ein Tab enthalten ist. 1381 if( pPor && rInf.GetRest() ) 1382 pPor->SetLen( 0 ); 1383 1384 // robust: 1385 if( !pPor || rInf.IsStop() ) 1386 { 1387 delete pPor; 1388 return 0; 1389 } 1390 } 1391 1392 // Special portions containing numbers (footnote anchor, footnote number, 1393 // numbering) can be contained in a rotated portion, if the user 1394 // choose a rotated character attribute. 1395 if ( pPor && ! pMulti ) 1396 { 1397 if ( pPor->IsFtnPortion() ) 1398 { 1399 const SwTxtFtn* pTxtFtn = ((SwFtnPortion*)pPor)->GetTxtFtn(); 1400 1401 if ( pTxtFtn ) 1402 { 1403 SwFmtFtn& rFtn = (SwFmtFtn&)pTxtFtn->GetFtn(); 1404 const SwDoc *pDoc = rInf.GetTxtFrm()->GetNode()->GetDoc(); 1405 const SwEndNoteInfo* pInfo; 1406 if( rFtn.IsEndNote() ) 1407 pInfo = &pDoc->GetEndNoteInfo(); 1408 else 1409 pInfo = &pDoc->GetFtnInfo(); 1410 const SwAttrSet& rSet = pInfo->GetAnchorCharFmt((SwDoc&)*pDoc)->GetAttrSet(); 1411 1412 const SfxPoolItem* pItem; 1413 sal_uInt16 nDir = 0; 1414 if( SFX_ITEM_SET == rSet.GetItemState( RES_CHRATR_ROTATE, 1415 sal_True, &pItem )) 1416 nDir = ((SvxCharRotateItem*)pItem)->GetValue(); 1417 1418 if ( 0 != nDir ) 1419 { 1420 delete pPor; 1421 pPor = new SwRotatedPortion( rInf.GetIdx() + 1, 900 == nDir ? 1422 DIR_BOTTOM2TOP : 1423 DIR_TOP2BOTTOM ); 1424 } 1425 } 1426 } 1427 else if ( pPor->InNumberGrp() ) 1428 { 1429 const SwFont* pNumFnt = ((SwFldPortion*)pPor)->GetFont(); 1430 1431 if ( pNumFnt ) 1432 { 1433 sal_uInt16 nDir = pNumFnt->GetOrientation( rInf.GetTxtFrm()->IsVertical() ); 1434 if ( 0 != nDir ) 1435 { 1436 delete pPor; 1437 pPor = new SwRotatedPortion( 0, 900 == nDir ? 1438 DIR_BOTTOM2TOP : 1439 DIR_TOP2BOTTOM ); 1440 1441 rInf.SetNumDone( sal_False ); 1442 rInf.SetFtnDone( sal_False ); 1443 } 1444 } 1445 } 1446 } 1447 1448 // Der Font wird im Outputdevice eingestellt, 1449 // der Ascent und die Hoehe werden berechnet. 1450 if( !pPor->GetAscent() && !pPor->Height() ) 1451 CalcAscent( rInf, pPor ); 1452 rInf.SetLen( pPor->GetLen() ); 1453 1454 // In CalcFlyWidth wird Width() verkuerzt, wenn eine FlyPortion vorliegt. 1455 CalcFlyWidth( rInf ); 1456 1457 // Man darf nicht vergessen, dass pCurr als GetLast() vernuenftige 1458 // Werte bereithalten muss: 1459 if( !pCurr->Height() ) 1460 { 1461 ASSERT( pCurr->Height(), "SwTxtFormatter::NewPortion: limbo dance" ); 1462 pCurr->Height( pPor->Height() ); 1463 pCurr->SetAscent( pPor->GetAscent() ); 1464 } 1465 1466 ASSERT( !pPor || pPor->Height(), 1467 "SwTxtFormatter::NewPortion: something went wrong"); 1468 if( pPor->IsPostItsPortion() && rInf.X() >= rInf.Width() && rInf.GetFly() ) 1469 { 1470 delete pPor; 1471 pPor = rInf.GetFly(); 1472 } 1473 return pPor; 1474 } 1475 1476 /************************************************************************* 1477 * SwTxtFormatter::FormatLine() 1478 *************************************************************************/ 1479 1480 xub_StrLen SwTxtFormatter::FormatLine( const xub_StrLen nStartPos ) 1481 { 1482 ASSERT( ! pFrm->IsVertical() || pFrm->IsSwapped(), 1483 "SwTxtFormatter::FormatLine( nStartPos ) with unswapped frame" ); 1484 1485 // For the formatting routines, we set pOut to the reference device. 1486 SwHookOut aHook( GetInfo() ); 1487 if( GetInfo().GetLen() < GetInfo().GetTxt().Len() ) 1488 GetInfo().SetLen( GetInfo().GetTxt().Len() ); 1489 1490 sal_Bool bBuild = sal_True; 1491 SetFlyInCntBase( sal_False ); 1492 GetInfo().SetLineHeight( 0 ); 1493 GetInfo().SetLineNettoHeight( 0 ); 1494 1495 // Recycling muss bei geaenderter Zeilenhoehe unterdrueckt werden 1496 // und auch bei geaendertem Ascent (Absenken der Grundlinie). 1497 const KSHORT nOldHeight = pCurr->Height(); 1498 const KSHORT nOldAscent = pCurr->GetAscent(); 1499 1500 pCurr->SetEndHyph( sal_False ); 1501 pCurr->SetMidHyph( sal_False ); 1502 1503 // fly positioning can make it necessary format a line several times 1504 // for this, we have to keep a copy of our rest portion 1505 SwLinePortion* pFld = GetInfo().GetRest(); 1506 SwFldPortion* pSaveFld = 0; 1507 1508 if ( pFld && pFld->InFldGrp() && !pFld->IsFtnPortion() ) 1509 pSaveFld = new SwFldPortion( *((SwFldPortion*)pFld) ); 1510 1511 // for an optimal repaint rectangle, we want to compare fly portions 1512 // before and after the BuildPortions call 1513 const sal_Bool bOptimizeRepaint = AllowRepaintOpt(); 1514 const xub_StrLen nOldLineEnd = nStartPos + pCurr->GetLen(); 1515 SvLongs* pFlyStart = 0; 1516 1517 // these are the conditions for a fly position comparison 1518 if ( bOptimizeRepaint && pCurr->IsFly() ) 1519 { 1520 pFlyStart = new SvLongs; 1521 SwLinePortion* pPor = pCurr->GetFirstPortion(); 1522 long nPOfst = 0; 1523 sal_uInt16 nCnt = 0; 1524 1525 while ( pPor ) 1526 { 1527 if ( pPor->IsFlyPortion() ) 1528 // insert start value of fly portion 1529 pFlyStart->Insert( nPOfst, nCnt++ ); 1530 1531 nPOfst += pPor->Width(); 1532 pPor = pPor->GetPortion(); 1533 } 1534 } 1535 1536 // Hier folgt bald die Unterlaufpruefung. 1537 while( bBuild ) 1538 { 1539 GetInfo().SetFtnInside( sal_False ); 1540 GetInfo().SetOtherThanFtnInside( sal_False ); 1541 1542 // These values must not be reset by FormatReset(); 1543 sal_Bool bOldNumDone = GetInfo().IsNumDone(); 1544 sal_Bool bOldArrowDone = GetInfo().IsArrowDone(); 1545 sal_Bool bOldErgoDone = GetInfo().IsErgoDone(); 1546 1547 // besides other things, this sets the repaint offset to 0 1548 FormatReset( GetInfo() ); 1549 1550 GetInfo().SetNumDone( bOldNumDone ); 1551 GetInfo().SetArrowDone( bOldArrowDone ); 1552 GetInfo().SetErgoDone( bOldErgoDone ); 1553 1554 // build new portions for this line 1555 BuildPortions( GetInfo() ); 1556 1557 if( GetInfo().IsStop() ) 1558 { 1559 pCurr->SetLen( 0 ); 1560 pCurr->Height( GetFrmRstHeight() + 1 ); 1561 pCurr->SetRealHeight( GetFrmRstHeight() + 1 ); 1562 pCurr->Width(0); 1563 pCurr->Truncate(); 1564 return nStartPos; 1565 } 1566 else if( GetInfo().IsDropInit() ) 1567 { 1568 DropInit(); 1569 GetInfo().SetDropInit( sal_False ); 1570 } 1571 1572 pCurr->CalcLine( *this, GetInfo() ); 1573 CalcRealHeight( GetInfo().IsNewLine() ); 1574 1575 if ( IsFlyInCntBase() && !IsQuick() ) 1576 { 1577 KSHORT nTmpAscent, nTmpHeight; 1578 CalcAscentAndHeight( nTmpAscent, nTmpHeight ); 1579 AlignFlyInCntBase( Y() + long( nTmpAscent ) ); 1580 pCurr->CalcLine( *this, GetInfo() ); 1581 CalcRealHeight(); 1582 } 1583 1584 // bBuild entscheidet, ob noch eine Ehrenrunde gedreht wird 1585 if ( pCurr->GetRealHeight() <= GetInfo().GetLineHeight() ) 1586 { 1587 pCurr->SetRealHeight( GetInfo().GetLineHeight() ); 1588 bBuild = sal_False; 1589 } 1590 else 1591 { 1592 bBuild = ( GetInfo().GetTxtFly()->IsOn() && ChkFlyUnderflow(GetInfo()) ) 1593 || GetInfo().CheckFtnPortion(pCurr); 1594 if( bBuild ) 1595 { 1596 GetInfo().SetNumDone( bOldNumDone ); 1597 GetInfo().ResetMaxWidthDiff(); 1598 1599 // delete old rest 1600 if ( GetInfo().GetRest() ) 1601 { 1602 delete GetInfo().GetRest(); 1603 GetInfo().SetRest( 0 ); 1604 } 1605 1606 // set original rest portion 1607 if ( pSaveFld ) 1608 GetInfo().SetRest( new SwFldPortion( *pSaveFld ) ); 1609 1610 pCurr->SetLen( 0 ); 1611 pCurr->Width(0); 1612 pCurr->Truncate(); 1613 } 1614 } 1615 } 1616 1617 // calculate optimal repaint rectangle 1618 if ( bOptimizeRepaint ) 1619 { 1620 GetInfo().SetPaintOfst( CalcOptRepaint( nOldLineEnd, pFlyStart ) ); 1621 if ( pFlyStart ) 1622 delete pFlyStart; 1623 } 1624 else 1625 // Special case: We do not allow an optimitation of the repaint 1626 // area, but during formatting the repaint offset is set to indicate 1627 // a maximum value for the offset. This value has to be reset: 1628 GetInfo().SetPaintOfst( 0 ); 1629 1630 // This corrects the start of the reformat range if something has 1631 // moved to the next line. Otherwise IsFirstReformat in AllowRepaintOpt 1632 // will give us a wrong result if we have to reformat another line 1633 GetInfo().GetParaPortion()->GetReformat()->LeftMove( GetInfo().GetIdx() ); 1634 1635 // delete master copy of rest portion 1636 if ( pSaveFld ) 1637 delete pSaveFld; 1638 1639 xub_StrLen nNewStart = nStartPos + pCurr->GetLen(); 1640 1641 // adjust text if kana compression is enabled 1642 if ( GetInfo().CompressLine() ) 1643 { 1644 SwTwips nRepaintOfst = CalcKanaAdj( pCurr ); 1645 1646 // adjust repaint offset 1647 if ( nRepaintOfst < GetInfo().GetPaintOfst() ) 1648 GetInfo().SetPaintOfst( nRepaintOfst ); 1649 } 1650 1651 CalcAdjustLine( pCurr ); 1652 1653 if( nOldHeight != pCurr->Height() || nOldAscent != pCurr->GetAscent() ) 1654 { 1655 SetFlyInCntBase(); 1656 GetInfo().SetPaintOfst( 0 ); //geaenderte Zeilenhoehe => kein Recycling 1657 // alle weiteren Zeilen muessen gepaintet und, wenn Flys im Spiel sind 1658 // auch formatiert werden. 1659 GetInfo().SetShift( sal_True ); 1660 } 1661 1662 if ( IsFlyInCntBase() && !IsQuick() ) 1663 UpdatePos( pCurr, GetTopLeft(), GetStart() ); 1664 1665 return nNewStart; 1666 } 1667 1668 /************************************************************************* 1669 * SwTxtFormatter::RecalcRealHeight() 1670 *************************************************************************/ 1671 1672 void SwTxtFormatter::RecalcRealHeight() 1673 { 1674 sal_Bool bMore = sal_True; 1675 while(bMore) 1676 { 1677 DBG_LOOP; 1678 CalcRealHeight(); 1679 bMore = Next() != 0; 1680 } 1681 } 1682 1683 /************************************************************************* 1684 * SwTxtFormatter::CalcRealHeight() 1685 *************************************************************************/ 1686 1687 void SwTxtFormatter::CalcRealHeight( sal_Bool bNewLine ) 1688 { 1689 KSHORT nLineHeight = pCurr->Height(); 1690 pCurr->SetClipping( sal_False ); 1691 1692 GETGRID( pFrm->FindPageFrm() ) 1693 if ( pGrid && GetInfo().SnapToGrid() ) 1694 { 1695 const sal_uInt16 nGridWidth = pGrid->GetBaseHeight(); 1696 const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight(); 1697 const sal_Bool bRubyTop = ! pGrid->GetRubyTextBelow(); 1698 1699 nLineHeight = nGridWidth + nRubyHeight; 1700 sal_uInt16 nLineDist = nLineHeight; 1701 1702 while ( pCurr->Height() > nLineHeight ) 1703 nLineHeight = nLineHeight + nLineDist; 1704 1705 KSHORT nAsc = pCurr->GetAscent() + 1706 ( bRubyTop ? 1707 ( nLineHeight - pCurr->Height() + nRubyHeight ) / 2 : 1708 ( nLineHeight - pCurr->Height() - nRubyHeight ) / 2 ); 1709 1710 pCurr->Height( nLineHeight ); 1711 pCurr->SetAscent( nAsc ); 1712 pInf->GetParaPortion()->SetFixLineHeight(); 1713 1714 // we ignore any line spacing options except from ... 1715 const SvxLineSpacingItem* pSpace = aLineInf.GetLineSpacing(); 1716 if ( ! IsParaLine() && pSpace && 1717 SVX_INTER_LINE_SPACE_PROP == pSpace->GetInterLineSpaceRule() ) 1718 { 1719 sal_uLong nTmp = pSpace->GetPropLineSpace(); 1720 1721 if( nTmp < 100 ) 1722 nTmp = 100; 1723 1724 nTmp *= nLineHeight; 1725 nLineHeight = (sal_uInt16)(nTmp / 100); 1726 } 1727 1728 pCurr->SetRealHeight( nLineHeight ); 1729 return; 1730 } 1731 1732 // Das Dummyflag besitzen Zeilen, die nur Flyportions enthalten, diese 1733 // sollten kein Register etc. beachten. Dummerweise hat kann es eine leere 1734 // Zeile am Absatzende geben (bei leeren Abs?tzen oder nach einem 1735 // Shift-Return), die das Register durchaus beachten soll. 1736 if( !pCurr->IsDummy() || ( !pCurr->GetNext() && 1737 GetStart() >= GetTxtFrm()->GetTxt().Len() && !bNewLine ) ) 1738 { 1739 const SvxLineSpacingItem *pSpace = aLineInf.GetLineSpacing(); 1740 if( pSpace ) 1741 { 1742 switch( pSpace->GetLineSpaceRule() ) 1743 { 1744 case SVX_LINE_SPACE_AUTO: 1745 break; 1746 case SVX_LINE_SPACE_MIN: 1747 { 1748 if( nLineHeight < KSHORT( pSpace->GetLineHeight() ) ) 1749 nLineHeight = pSpace->GetLineHeight(); 1750 break; 1751 } 1752 case SVX_LINE_SPACE_FIX: 1753 { 1754 nLineHeight = pSpace->GetLineHeight(); 1755 KSHORT nAsc = ( 4 * nLineHeight ) / 5; // 80% 1756 if( nAsc < pCurr->GetAscent() || 1757 nLineHeight - nAsc < pCurr->Height() - pCurr->GetAscent() ) 1758 pCurr->SetClipping( sal_True ); 1759 pCurr->Height( nLineHeight ); 1760 pCurr->SetAscent( nAsc ); 1761 pInf->GetParaPortion()->SetFixLineHeight(); 1762 } 1763 break; 1764 default: ASSERT( sal_False, ": unknown LineSpaceRule" ); 1765 } 1766 if( !IsParaLine() ) 1767 switch( pSpace->GetInterLineSpaceRule() ) 1768 { 1769 case SVX_INTER_LINE_SPACE_OFF: 1770 break; 1771 case SVX_INTER_LINE_SPACE_PROP: 1772 { 1773 long nTmp = pSpace->GetPropLineSpace(); 1774 // 50% ist das Minimum, bei 0% schalten wir auf 1775 // den Defaultwert 100% um ... 1776 if( nTmp < 50 ) 1777 nTmp = nTmp ? 50 : 100; 1778 1779 nTmp *= nLineHeight; 1780 nTmp /= 100; 1781 if( !nTmp ) 1782 ++nTmp; 1783 nLineHeight = (KSHORT)nTmp; 1784 break; 1785 } 1786 case SVX_INTER_LINE_SPACE_FIX: 1787 { 1788 nLineHeight = nLineHeight + pSpace->GetInterLineSpace(); 1789 break; 1790 } 1791 default: ASSERT( sal_False, ": unknown InterLineSpaceRule" ); 1792 } 1793 } 1794 #if OSL_DEBUG_LEVEL > 1 1795 KSHORT nDummy = nLineHeight + 1; 1796 (void)nDummy; 1797 #endif 1798 1799 if( IsRegisterOn() ) 1800 { 1801 SwTwips nTmpY = Y() + pCurr->GetAscent() + nLineHeight - pCurr->Height(); 1802 SWRECTFN( pFrm ) 1803 if ( bVert ) 1804 nTmpY = pFrm->SwitchHorizontalToVertical( nTmpY ); 1805 nTmpY = (*fnRect->fnYDiff)( nTmpY, RegStart() ); 1806 KSHORT nDiff = KSHORT( nTmpY % RegDiff() ); 1807 if( nDiff ) 1808 nLineHeight += RegDiff() - nDiff; 1809 } 1810 } 1811 pCurr->SetRealHeight( nLineHeight ); 1812 } 1813 1814 /************************************************************************* 1815 * SwTxtFormatter::FeedInf() 1816 *************************************************************************/ 1817 1818 void SwTxtFormatter::FeedInf( SwTxtFormatInfo &rInf ) const 1819 { 1820 // 3260, 3860: Fly auf jeden Fall loeschen! 1821 ClearFly( rInf ); 1822 rInf.Init(); 1823 1824 rInf.ChkNoHyph( CntEndHyph(), CntMidHyph() ); 1825 rInf.SetRoot( pCurr ); 1826 rInf.SetLineStart( nStart ); 1827 rInf.SetIdx( nStart ); 1828 1829 // Handle overflows: 1830 // --> FME 2004-11-25 #i34348# Changed type from sal_uInt16 to SwTwips 1831 SwTwips nTmpLeft = Left(); 1832 SwTwips nTmpRight = Right(); 1833 SwTwips nTmpFirst = FirstLeft(); 1834 // <-- 1835 1836 if ( nTmpLeft > USHRT_MAX || 1837 nTmpRight > USHRT_MAX || 1838 nTmpFirst > USHRT_MAX ) 1839 { 1840 SWRECTFN( rInf.GetTxtFrm() ) 1841 nTmpLeft = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetLeft)(); 1842 nTmpRight = (rInf.GetTxtFrm()->Frm().*fnRect->fnGetRight)(); 1843 nTmpFirst = nTmpLeft; 1844 } 1845 1846 rInf.Left( nTmpLeft ); 1847 rInf.Right( nTmpRight ); 1848 rInf.First( nTmpFirst ); 1849 1850 rInf.RealWidth( KSHORT(rInf.Right()) - KSHORT(GetLeftMargin()) ); 1851 rInf.Width( rInf.RealWidth() ); 1852 if( ((SwTxtFormatter*)this)->GetRedln() ) 1853 { 1854 ((SwTxtFormatter*)this)->GetRedln()->Clear( ((SwTxtFormatter*)this)->GetFnt() ); 1855 ((SwTxtFormatter*)this)->GetRedln()->Reset(); 1856 } 1857 } 1858 1859 /************************************************************************* 1860 * SwTxtFormatter::FormatReset() 1861 *************************************************************************/ 1862 1863 void SwTxtFormatter::FormatReset( SwTxtFormatInfo &rInf ) 1864 { 1865 pCurr->Truncate(); 1866 pCurr->Init(); 1867 if( pBlink && pCurr->IsBlinking() ) 1868 pBlink->Delete( pCurr ); 1869 1870 // delete pSpaceAdd und pKanaComp 1871 pCurr->FinishSpaceAdd(); 1872 pCurr->FinishKanaComp(); 1873 pCurr->ResetFlags(); 1874 FeedInf( rInf ); 1875 } 1876 1877 /************************************************************************* 1878 * SwTxtFormatter::CalcOnceMore() 1879 *************************************************************************/ 1880 1881 sal_Bool SwTxtFormatter::CalcOnceMore() 1882 { 1883 if( pDropFmt ) 1884 { 1885 const KSHORT nOldDrop = GetDropHeight(); 1886 CalcDropHeight( pDropFmt->GetLines() ); 1887 bOnceMore = nOldDrop != GetDropHeight(); 1888 } 1889 else 1890 bOnceMore = sal_False; 1891 return bOnceMore; 1892 } 1893 1894 /************************************************************************* 1895 * SwTxtFormatter::CalcBottomLine() 1896 *************************************************************************/ 1897 1898 SwTwips SwTxtFormatter::CalcBottomLine() const 1899 { 1900 SwTwips nRet = Y() + GetLineHeight(); 1901 SwTwips nMin = GetInfo().GetTxtFly()->GetMinBottom(); 1902 if( nMin && ++nMin > nRet ) 1903 { 1904 SwTwips nDist = pFrm->Frm().Height() - pFrm->Prt().Height() 1905 - pFrm->Prt().Top(); 1906 if( nRet + nDist < nMin ) 1907 { 1908 sal_Bool bRepaint = HasTruncLines() && 1909 GetInfo().GetParaPortion()->GetRepaint()->Bottom() == nRet-1; 1910 nRet = nMin - nDist; 1911 if( bRepaint ) 1912 { 1913 ((SwRepaint*)GetInfo().GetParaPortion() 1914 ->GetRepaint())->Bottom( nRet-1 ); 1915 ((SwTxtFormatInfo&)GetInfo()).SetPaintOfst( 0 ); 1916 } 1917 } 1918 } 1919 return nRet; 1920 } 1921 1922 /************************************************************************* 1923 * SwTxtFormatter::_CalcFitToContent() 1924 * 1925 * FME/OD: This routine does a limited text formatting. 1926 *************************************************************************/ 1927 1928 SwTwips SwTxtFormatter::_CalcFitToContent() 1929 { 1930 FormatReset( GetInfo() ); 1931 BuildPortions( GetInfo() ); 1932 pCurr->CalcLine( *this, GetInfo() ); 1933 return pCurr->Width(); 1934 } 1935 1936 /************************************************************************* 1937 * SwTxtFormatter::AllowRepaintOpt() 1938 * 1939 * determines if the calculation of a repaint offset is allowed 1940 * otherwise each line is painted from 0 (this is a copy of the beginning 1941 * of the former SwTxtFormatter::Recycle() function 1942 *************************************************************************/ 1943 sal_Bool SwTxtFormatter::AllowRepaintOpt() const 1944 { 1945 // reformat position in front of current line? Only in this case 1946 // we want to set the repaint offset 1947 sal_Bool bOptimizeRepaint = nStart < GetInfo().GetReformatStart() && 1948 pCurr->GetLen(); 1949 1950 // a special case is the last line of a block adjusted paragraph: 1951 if ( bOptimizeRepaint ) 1952 { 1953 switch( GetAdjust() ) 1954 { 1955 case SVX_ADJUST_BLOCK: 1956 { 1957 if( IsLastBlock() || IsLastCenter() ) 1958 bOptimizeRepaint = sal_False; 1959 else 1960 { 1961 // ????: Blank in der letzten Masterzeile (blocksat.sdw) 1962 bOptimizeRepaint = 0 == pCurr->GetNext() && !pFrm->GetFollow(); 1963 if ( bOptimizeRepaint ) 1964 { 1965 SwLinePortion *pPos = pCurr->GetFirstPortion(); 1966 while ( pPos && !pPos->IsFlyPortion() ) 1967 pPos = pPos->GetPortion(); 1968 bOptimizeRepaint = !pPos; 1969 } 1970 } 1971 break; 1972 } 1973 case SVX_ADJUST_CENTER: 1974 case SVX_ADJUST_RIGHT: 1975 bOptimizeRepaint = sal_False; 1976 break; 1977 default: ; 1978 } 1979 } 1980 1981 // Schon wieder ein Sonderfall: unsichtbare SoftHyphs 1982 const xub_StrLen nReformat = GetInfo().GetReformatStart(); 1983 if( bOptimizeRepaint && STRING_LEN != nReformat ) 1984 { 1985 const xub_Unicode cCh = GetInfo().GetTxt().GetChar( nReformat ); 1986 bOptimizeRepaint = ( CH_TXTATR_BREAKWORD != cCh && CH_TXTATR_INWORD != cCh ) 1987 || ! GetInfo().HasHint( nReformat ); 1988 } 1989 1990 return bOptimizeRepaint; 1991 } 1992 1993 /************************************************************************* 1994 * SwTxtFormatter::CalcOptRepaint() 1995 * 1996 * calculates an optimal repaint offset for the current line 1997 *************************************************************************/ 1998 long SwTxtFormatter::CalcOptRepaint( xub_StrLen nOldLineEnd, 1999 const SvLongs* pFlyStart ) 2000 { 2001 if ( GetInfo().GetIdx() < GetInfo().GetReformatStart() ) 2002 // the reformat position is behind our new line, that means 2003 // something of our text has moved to the next line 2004 return 0; 2005 2006 xub_StrLen nReformat = Min( GetInfo().GetReformatStart(), nOldLineEnd ); 2007 2008 // in case we do not have any fly in our line, our repaint position 2009 // is the changed position - 1 2010 if ( ! pFlyStart && ! pCurr->IsFly() ) 2011 { 2012 // this is the maximum repaint offset determined during formatting 2013 // for example: the beginning of the first right tab stop 2014 // if this value is 0, this means that we do not have an upper 2015 // limit for the repaint offset 2016 const long nFormatRepaint = GetInfo().GetPaintOfst(); 2017 2018 if ( nReformat < GetInfo().GetLineStart() + 3 ) 2019 return 0; 2020 2021 // step back two positions for smoother repaint 2022 nReformat -= 2; 2023 2024 #ifndef QUARTZ 2025 #ifndef ENABLE_GRAPHITE 2026 // --> FME 2004-09-27 #i28795#, #i34607#, #i38388# 2027 // step back six(!) more characters for complex scripts 2028 // this is required e.g., for Khmer (thank you, Javier!) 2029 const SwScriptInfo& rSI = GetInfo().GetParaPortion()->GetScriptInfo(); 2030 xub_StrLen nMaxContext = 0; 2031 if( ::i18n::ScriptType::COMPLEX == rSI.ScriptType( nReformat ) ) 2032 nMaxContext = 6; 2033 #else 2034 // Some Graphite fonts need context for scripts not marked as complex 2035 static const xub_StrLen nMaxContext = 10; 2036 #endif 2037 #else 2038 // some fonts like Quartz's Zapfino need more context 2039 // TODO: query FontInfo for maximum unicode context 2040 static const xub_StrLen nMaxContext = 8; 2041 #endif 2042 if( nMaxContext > 0 ) 2043 { 2044 if ( nReformat > GetInfo().GetLineStart() + nMaxContext ) 2045 nReformat = nReformat - nMaxContext; 2046 else 2047 nReformat = GetInfo().GetLineStart(); 2048 } 2049 // <-- 2050 2051 // Weird situation: Our line used to end with a hole portion 2052 // and we delete some characters at the end of our line. We have 2053 // to take care for repainting the blanks which are not anymore 2054 // covered by the hole portion 2055 while ( nReformat > GetInfo().GetLineStart() && 2056 CH_BLANK == GetInfo().GetChar( nReformat ) ) 2057 --nReformat; 2058 2059 ASSERT( nReformat < GetInfo().GetIdx(), "Reformat too small for me!" ); 2060 SwRect aRect; 2061 2062 // Note: GetChareRect is not const. It definitely changes the 2063 // bMulti flag. We have to save and resore the old value. 2064 sal_Bool bOldMulti = GetInfo().IsMulti(); 2065 GetCharRect( &aRect, nReformat ); 2066 GetInfo().SetMulti( bOldMulti ); 2067 2068 return nFormatRepaint ? Min( aRect.Left(), nFormatRepaint ) : 2069 aRect.Left(); 2070 } 2071 else 2072 { 2073 // nReformat may be wrong, if something around flys has changed: 2074 // we compare the former and the new fly positions in this line 2075 // if anything has changed, we carefully have to adjust the right 2076 // repaint position 2077 long nPOfst = 0; 2078 sal_uInt16 nCnt = 0; 2079 sal_uInt16 nX = 0; 2080 sal_uInt16 nIdx = GetInfo().GetLineStart(); 2081 SwLinePortion* pPor = pCurr->GetFirstPortion(); 2082 2083 while ( pPor ) 2084 { 2085 if ( pPor->IsFlyPortion() ) 2086 { 2087 // compare start of fly with former start of fly 2088 if ( pFlyStart && 2089 nCnt < pFlyStart->Count() && 2090 nX == (*pFlyStart)[ nCnt ] && 2091 nIdx < nReformat 2092 ) 2093 // found fix position, nothing has changed left from nX 2094 nPOfst = nX + pPor->Width(); 2095 else 2096 break; 2097 2098 nCnt++; 2099 } 2100 nX = nX + pPor->Width(); 2101 nIdx = nIdx + pPor->GetLen(); 2102 pPor = pPor->GetPortion(); 2103 } 2104 2105 return nPOfst + GetLeftMargin(); 2106 } 2107 } 2108 2109 bool lcl_BuildHiddenPortion( const SwTxtSizeInfo& rInf, xub_StrLen &rPos ) 2110 { 2111 // Only if hidden text should not be shown: 2112 // if ( rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar() ) 2113 const bool bShowInDocView = rInf.GetVsh() && rInf.GetVsh()->GetWin() && rInf.GetOpt().IsShowHiddenChar(); 2114 const bool bShowForPrinting = rInf.GetOpt().IsShowHiddenChar( sal_True ) && rInf.GetOpt().IsPrinting(); 2115 if (bShowInDocView || bShowForPrinting) 2116 return false; 2117 2118 const SwScriptInfo& rSI = rInf.GetParaPortion()->GetScriptInfo(); 2119 xub_StrLen nHiddenStart; 2120 xub_StrLen nHiddenEnd; 2121 rSI.GetBoundsOfHiddenRange( rPos, nHiddenStart, nHiddenEnd ); 2122 if ( nHiddenEnd ) 2123 { 2124 rPos = nHiddenEnd; 2125 return true; 2126 } 2127 2128 return false; 2129 } 2130