1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sw.hxx" 26 27 28 #include <ctype.h> 29 30 #ifndef _COM_SUN_STAR_I18N_SCRIPTTYPE_HDL_ 31 #include <com/sun/star/i18n/ScriptType.hdl> 32 #endif 33 #include <hintids.hxx> // CH_TXTATR 34 #include <errhdl.hxx> // ASSERT 35 #include <SwPortionHandler.hxx> 36 #include <txtcfg.hxx> 37 #include <porlay.hxx> 38 #include <inftxt.hxx> 39 #include <guess.hxx> // SwTxtGuess, Zeilenumbruch 40 #include <porglue.hxx> 41 #include <portab.hxx> // pLastTab-> 42 #include <porfld.hxx> // SwFldPortion 43 #include <wrong.hxx> 44 #include <viewsh.hxx> 45 #include <IDocumentSettingAccess.hxx> 46 #include <viewopt.hxx> // SwViewOptions 47 48 #include <IMark.hxx> 49 #include <pam.hxx> 50 #include <doc.hxx> 51 #include <xmloff/odffields.hxx> 52 #include <vcl/pdfextoutdevdata.hxx> 53 54 #if OSL_DEBUG_LEVEL > 1 55 const sal_Char *GetLangName( const MSHORT nLang ); 56 #endif 57 58 using namespace ::sw::mark; 59 using namespace ::com::sun::star; 60 using namespace ::com::sun::star::i18n::ScriptType; 61 62 /************************************************************************* 63 * lcl_AddSpace 64 * Returns for how many characters an extra space has to be added 65 * (for justified alignment). 66 *************************************************************************/ 67 68 sal_uInt16 lcl_AddSpace( const SwTxtSizeInfo &rInf, const XubString* pStr, 69 const SwLinePortion& rPor ) 70 { 71 xub_StrLen nPos, nEnd; 72 const SwScriptInfo* pSI = 0; 73 74 if ( pStr ) 75 { 76 // passing a string means we are inside a field 77 nPos = 0; 78 nEnd = pStr->Len(); 79 } 80 else 81 { 82 nPos = rInf.GetIdx(); 83 nEnd = rInf.GetIdx() + rPor.GetLen(); 84 pStr = &rInf.GetTxt(); 85 pSI = &((SwParaPortion*)rInf.GetParaPortion())->GetScriptInfo(); 86 } 87 88 sal_uInt16 nCnt = 0; 89 sal_uInt8 nScript = 0; 90 91 // If portion consists of Asian characters and language is not 92 // Korean, we add extra space to each character. 93 // first we get the script type 94 if ( pSI ) 95 nScript = pSI->ScriptType( nPos ); 96 else if ( pBreakIt->GetBreakIter().is() ) 97 nScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( *pStr, nPos ); 98 99 // Note: rInf.GetIdx() can differ from nPos, 100 // e.g., when rPor is a field portion. nPos referes to the string passed 101 // to the function, rInf.GetIdx() referes to the original string. 102 103 // We try to find out which justification mode is required. This is done by 104 // evaluating the script type and the language attribute set for this portion 105 106 // Asian Justification: Each character get some extra space 107 if ( nEnd > nPos && ASIAN == nScript ) 108 { 109 LanguageType aLang = 110 rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript ); 111 112 if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang ) 113 { 114 const SwLinePortion* pPor = rPor.GetPortion(); 115 if ( pPor && ( pPor->IsKernPortion() || 116 pPor->IsControlCharPortion() || 117 pPor->IsPostItsPortion() ) ) 118 pPor = pPor->GetPortion(); 119 120 nCnt += nEnd - nPos; 121 122 if ( !pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() || 123 pPor->IsBreakPortion() ) 124 --nCnt; 125 126 return nCnt; 127 } 128 } 129 130 // Kashida Justification: Insert Kashidas 131 if ( nEnd > nPos && pSI && COMPLEX == nScript ) 132 { 133 if ( SwScriptInfo::IsArabicText( *pStr, nPos, nEnd - nPos ) && pSI->CountKashida() ) 134 { 135 const sal_uInt16 nKashRes = pSI->KashidaJustify( 0, 0, nPos, nEnd - nPos ); 136 // i60591: need to check result of KashidaJustify 137 // determine if kashida justification is applicable 138 if( nKashRes != STRING_LEN ) 139 return nKashRes; 140 } 141 } 142 143 // Thai Justification: Each character cell gets some extra space 144 if ( nEnd > nPos && COMPLEX == nScript ) 145 { 146 LanguageType aLang = 147 rInf.GetTxtFrm()->GetTxtNode()->GetLang( rInf.GetIdx(), 1, nScript ); 148 149 if ( LANGUAGE_THAI == aLang ) 150 { 151 nCnt = SwScriptInfo::ThaiJustify( *pStr, 0, 0, nPos, nEnd - nPos ); 152 153 const SwLinePortion* pPor = rPor.GetPortion(); 154 if ( pPor && ( pPor->IsKernPortion() || 155 pPor->IsControlCharPortion() || 156 pPor->IsPostItsPortion() ) ) 157 pPor = pPor->GetPortion(); 158 159 if ( nCnt && ( ! pPor || pPor->IsHolePortion() || pPor->InFixMargGrp() ) ) 160 --nCnt; 161 162 return nCnt; 163 } 164 } 165 166 // Here starts the good old "Look for blanks and add space to them" part. 167 // Note: We do not want to add space to an isolated latin blank in front 168 // of some complex characters in RTL environment 169 const sal_Bool bDoNotAddSpace = 170 LATIN == nScript && ( nEnd == nPos + 1 ) && pSI && 171 ( i18n::ScriptType::COMPLEX == 172 pSI->ScriptType( nPos + 1 ) ) && 173 rInf.GetTxtFrm() && rInf.GetTxtFrm()->IsRightToLeft(); 174 175 if ( bDoNotAddSpace ) 176 return nCnt; 177 178 for ( ; nPos < nEnd; ++nPos ) 179 { 180 if( CH_BLANK == pStr->GetChar( nPos ) ) 181 ++nCnt; 182 } 183 184 // We still have to examine the next character: 185 // If the next character is ASIAN and not KOREAN we have 186 // to add an extra space 187 // nPos referes to the original string, even if a field string has 188 // been passed to this function 189 nPos = rInf.GetIdx() + rPor.GetLen(); 190 if ( nPos < rInf.GetTxt().Len() ) 191 { 192 sal_uInt8 nNextScript = 0; 193 const SwLinePortion* pPor = rPor.GetPortion(); 194 if ( pPor && pPor->IsKernPortion() ) 195 pPor = pPor->GetPortion(); 196 197 if ( ! pBreakIt->GetBreakIter().is() || ! pPor || pPor->InFixMargGrp() ) 198 return nCnt; 199 200 // next character is inside a field? 201 if ( CH_TXTATR_BREAKWORD == rInf.GetChar( nPos ) && pPor->InExpGrp() ) 202 { 203 sal_Bool bOldOnWin = rInf.OnWin(); 204 ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False ); 205 206 XubString aStr( aEmptyStr ); 207 pPor->GetExpTxt( rInf, aStr ); 208 ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin ); 209 210 nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( aStr, 0 ); 211 } 212 else 213 nNextScript = (sal_uInt8)pBreakIt->GetBreakIter()->getScriptType( rInf.GetTxt(), nPos ); 214 215 if( ASIAN == nNextScript ) 216 { 217 LanguageType aLang = 218 rInf.GetTxtFrm()->GetTxtNode()->GetLang( nPos, 1, nNextScript ); 219 220 if ( LANGUAGE_KOREAN != aLang && LANGUAGE_KOREAN_JOHAB != aLang ) 221 ++nCnt; 222 } 223 } 224 225 return nCnt; 226 } 227 228 /************************************************************************* 229 * class SwTxtPortion 230 *************************************************************************/ 231 232 SwTxtPortion::SwTxtPortion( const SwLinePortion &rPortion ) 233 : SwLinePortion( rPortion ) 234 { 235 SetWhichPor( POR_TXT ); 236 } 237 238 /************************************************************************* 239 * SwTxtPortion::BreakCut() 240 *************************************************************************/ 241 242 void SwTxtPortion::BreakCut( SwTxtFormatInfo &rInf, const SwTxtGuess &rGuess ) 243 { 244 // Das Wort/Zeichen ist groesser als die Zeile 245 // Sonderfall Nr.1: Das Wort ist groesser als die Zeile 246 // Wir kappen... 247 const KSHORT nLineWidth = (KSHORT)(rInf.Width() - rInf.X()); 248 xub_StrLen nLen = rGuess.CutPos() - rInf.GetIdx(); 249 if( nLen ) 250 { 251 // special case: guess does not always provide the correct 252 // width, only in common cases. 253 if ( !rGuess.BreakWidth() ) 254 { 255 rInf.SetLen( nLen ); 256 SetLen( nLen ); 257 CalcTxtSize( rInf ); 258 259 // changing these values requires also changing them in 260 // guess.cxx 261 KSHORT nItalic = 0; 262 if( ITALIC_NONE != rInf.GetFont()->GetItalic() && !rInf.NotEOL() ) 263 { 264 nItalic = Height() / 12; 265 } 266 Width( Width() + nItalic ); 267 } 268 else 269 { 270 Width( rGuess.BreakWidth() ); 271 SetLen( nLen ); 272 } 273 } 274 // special case: first character does not fit to line 275 else if ( rGuess.CutPos() == rInf.GetLineStart() ) 276 { 277 SetLen( 1 ); 278 Width( nLineWidth ); 279 } 280 else 281 { 282 SetLen( 0 ); 283 Width( 0 ); 284 } 285 } 286 287 /************************************************************************* 288 * SwTxtPortion::BreakUnderflow() 289 *************************************************************************/ 290 291 void SwTxtPortion::BreakUnderflow( SwTxtFormatInfo &rInf ) 292 { 293 Truncate(); 294 Height( 0 ); 295 Width( 0 ); 296 SetLen( 0 ); 297 SetAscent( 0 ); 298 rInf.SetUnderFlow( this ); 299 } 300 301 /************************************************************************* 302 * SwTxtPortion::_Format() 303 *************************************************************************/ 304 305 sal_Bool lcl_HasContent( const SwFldPortion& rFld, SwTxtFormatInfo &rInf ) 306 { 307 String aTxt; 308 return rFld.GetExpTxt( rInf, aTxt ) && aTxt.Len(); 309 } 310 311 sal_Bool SwTxtPortion::_Format( SwTxtFormatInfo &rInf ) 312 { 313 // 5744: wenn nur der Trennstrich nicht mehr passt, 314 // muss trotzdem das Wort umgebrochen werden, ansonsten return sal_True! 315 if( rInf.IsUnderFlow() && rInf.GetSoftHyphPos() ) 316 { 317 // soft hyphen portion has triggered an underflow event because 318 // of an alternative spelling position 319 sal_Bool bFull = sal_False; 320 const sal_Bool bHyph = rInf.ChgHyph( sal_True ); 321 if( rInf.IsHyphenate() ) 322 { 323 SwTxtGuess aGuess; 324 // check for alternative spelling left from the soft hyphen 325 // this should usually be true but 326 aGuess.AlternativeSpelling( rInf, rInf.GetSoftHyphPos() - 1 ); 327 bFull = CreateHyphen( rInf, aGuess ); 328 ASSERT( bFull, "Problem with hyphenation!!!" ); 329 } 330 rInf.ChgHyph( bHyph ); 331 rInf.SetSoftHyphPos( 0 ); 332 return bFull; 333 } 334 335 SwTxtGuess aGuess; 336 const sal_Bool bFull = !aGuess.Guess( *this, rInf, Height() ); 337 338 // these are the possible cases: 339 // A Portion fits to current line 340 // B Portion does not fit to current line but a possible line break 341 // within the portion has been found by the break iterator, 2 subcases 342 // B1 break is hyphen 343 // B2 break is word end 344 // C Portion does not fit to current line and no possible line break 345 // has been found by break iterator, 2 subcases: 346 // C1 break iterator found a possible line break in portion before us 347 // ==> this break is used (underflow) 348 // C2 break iterator does not found a possible line break at all: 349 // ==> line break 350 351 // case A: line not yet full 352 if ( !bFull ) 353 { 354 Width( aGuess.BreakWidth() ); 355 // Vorsicht ! 356 if( !InExpGrp() || InFldGrp() ) 357 SetLen( rInf.GetLen() ); 358 359 short nKern = rInf.GetFont()->CheckKerning(); 360 if( nKern > 0 && rInf.Width() < rInf.X() + Width() + nKern ) 361 { 362 nKern = (short)(rInf.Width() - rInf.X() - Width() - 1); 363 if( nKern < 0 ) 364 nKern = 0; 365 } 366 if( nKern ) 367 new SwKernPortion( *this, nKern ); 368 } 369 // special case: hanging portion 370 else if( bFull && aGuess.GetHangingPortion() ) 371 { 372 Width( aGuess.BreakWidth() ); 373 SetLen( aGuess.BreakPos() - rInf.GetIdx() ); 374 Insert( aGuess.GetHangingPortion() ); 375 aGuess.GetHangingPortion()->SetAscent( GetAscent() ); 376 aGuess.ClearHangingPortion(); 377 } 378 // breakPos >= index 379 else if ( aGuess.BreakPos() >= rInf.GetIdx() && aGuess.BreakPos() != STRING_LEN ) 380 { 381 // case B1 382 if( aGuess.HyphWord().is() && aGuess.BreakPos() > rInf.GetLineStart() 383 && ( aGuess.BreakPos() > rInf.GetIdx() || 384 ( rInf.GetLast() && ! rInf.GetLast()->IsFlyPortion() ) ) ) 385 { 386 CreateHyphen( rInf, aGuess ); 387 if ( rInf.GetFly() ) 388 rInf.GetRoot()->SetMidHyph( sal_True ); 389 else 390 rInf.GetRoot()->SetEndHyph( sal_True ); 391 } 392 // case C1 393 // - Footnote portions with fake line start (i.e., not at beginning of line) 394 // should keep together with the text portion. (Note: no keep together 395 // with only footnote portions. 396 // - TabPortions not at beginning of line should keep together with the 397 // text portion, if they are not followed by a blank 398 // (work around different definition of tab stop character - breaking or 399 // non breaking character - in compatibility mode) 400 else if ( ( IsFtnPortion() && rInf.IsFakeLineStart() && 401 // --> OD 2010-01-29 #b6921213# 402 rInf.IsOtherThanFtnInside() ) || 403 // <-- 404 ( rInf.GetLast() && 405 rInf.GetTxtFrm()->GetTxtNode()->getIDocumentSettingAccess()->get(IDocumentSettingAccess::TAB_COMPAT) && 406 rInf.GetLast()->InTabGrp() && 407 rInf.GetLineStart() + rInf.GetLast()->GetLen() < rInf.GetIdx() && 408 aGuess.BreakPos() == rInf.GetIdx() && 409 CH_BLANK != rInf.GetChar( rInf.GetIdx() ) && 410 0x3000 != rInf.GetChar( rInf.GetIdx() ) ) ) 411 BreakUnderflow( rInf ); 412 // case B2 413 else if( rInf.GetIdx() > rInf.GetLineStart() || 414 aGuess.BreakPos() > rInf.GetIdx() || 415 // this is weird: during formatting the follow of a field 416 // the values rInf.GetIdx and rInf.GetLineStart are replaced 417 // IsFakeLineStart indicates GetIdx > GetLineStart 418 rInf.IsFakeLineStart() || 419 rInf.GetFly() || 420 rInf.IsFirstMulti() || 421 ( rInf.GetLast() && 422 ( rInf.GetLast()->IsFlyPortion() || 423 ( rInf.GetLast()->InFldGrp() && 424 ! rInf.GetLast()->InNumberGrp() && 425 ! rInf.GetLast()->IsErgoSumPortion() && 426 lcl_HasContent(*((SwFldPortion*)rInf.GetLast()),rInf ) ) ) ) ) 427 { 428 if ( rInf.X() + aGuess.BreakWidth() <= rInf.Width() ) 429 Width( aGuess.BreakWidth() ); 430 else 431 // this actually should not happen 432 Width( KSHORT(rInf.Width() - rInf.X()) ); 433 434 SetLen( aGuess.BreakPos() - rInf.GetIdx() ); 435 436 ASSERT( aGuess.BreakStart() >= aGuess.FieldDiff(), 437 "Trouble with expanded field portions during line break" ); 438 const xub_StrLen nRealStart = aGuess.BreakStart() - aGuess.FieldDiff(); 439 if( aGuess.BreakPos() < nRealStart && !InExpGrp() ) 440 { 441 SwHolePortion *pNew = new SwHolePortion( *this ); 442 pNew->SetLen( nRealStart - aGuess.BreakPos() ); 443 Insert( pNew ); 444 } 445 } 446 else // case C2, last exit 447 BreakCut( rInf, aGuess ); 448 } 449 // breakPos < index or no breakpos at all 450 else 451 { 452 sal_Bool bFirstPor = rInf.GetLineStart() == rInf.GetIdx(); 453 if( aGuess.BreakPos() != STRING_LEN && 454 aGuess.BreakPos() != rInf.GetLineStart() && 455 ( !bFirstPor || rInf.GetFly() || rInf.GetLast()->IsFlyPortion() || 456 rInf.IsFirstMulti() ) && 457 ( !rInf.GetLast()->IsBlankPortion() || ((SwBlankPortion*) 458 rInf.GetLast())->MayUnderFlow( rInf, rInf.GetIdx()-1, sal_True ))) 459 { // case C1 (former BreakUnderflow()) 460 BreakUnderflow( rInf ); 461 } 462 else 463 // case C2, last exit 464 BreakCut( rInf, aGuess ); 465 } 466 467 return bFull; 468 } 469 470 /************************************************************************* 471 * virtual SwTxtPortion::Format() 472 *************************************************************************/ 473 474 475 476 sal_Bool SwTxtPortion::Format( SwTxtFormatInfo &rInf ) 477 { 478 #if OSL_DEBUG_LEVEL > 1 479 const XubString aDbgTxt( rInf.GetTxt().Copy( rInf.GetIdx(), rInf.GetLen() ) ); 480 #endif 481 482 if( rInf.X() > rInf.Width() || (!GetLen() && !InExpGrp()) ) 483 { 484 Height( 0 ); 485 Width( 0 ); 486 SetLen( 0 ); 487 SetAscent( 0 ); 488 SetPortion( NULL ); // ???? 489 return sal_True; 490 } 491 492 ASSERT( rInf.RealWidth() || (rInf.X() == rInf.Width()), 493 "SwTxtPortion::Format: missing real width" ); 494 ASSERT( Height(), "SwTxtPortion::Format: missing height" ); 495 496 return _Format( rInf ); 497 } 498 499 /************************************************************************* 500 * virtual SwTxtPortion::FormatEOL() 501 *************************************************************************/ 502 503 // Format end of line 504 // 5083: Es kann schon manchmal unguenstige Faelle geben... 505 // "vom {Nikolaus}", Nikolaus bricht um "vom " wird im Blocksatz 506 // zu "vom" und " ", wobei der Glue expandiert wird, statt in die 507 // MarginPortion aufzugehen. 508 // rInf.nIdx steht auf dem naechsten Wort, nIdx-1 ist der letzte 509 // Buchstabe der Portion. 510 511 512 513 void SwTxtPortion::FormatEOL( SwTxtFormatInfo &rInf ) 514 { 515 if( ( !GetPortion() || ( GetPortion()->IsKernPortion() && 516 !GetPortion()->GetPortion() ) ) && GetLen() && 517 rInf.GetIdx() < rInf.GetTxt().Len() && 518 1 < rInf.GetIdx() && ' ' == rInf.GetChar( rInf.GetIdx() - 1 ) 519 && !rInf.GetLast()->IsHolePortion() ) 520 { 521 // calculate number of blanks 522 xub_StrLen nX = rInf.GetIdx() - 1; 523 sal_uInt16 nHoleLen = 1; 524 while( nX && nHoleLen < GetLen() && CH_BLANK == rInf.GetChar( --nX ) ) 525 nHoleLen++; 526 527 // Erst uns einstellen und dann Inserten, weil wir ja auch ein 528 // SwLineLayout sein koennten. 529 KSHORT nBlankSize; 530 if( nHoleLen == GetLen() ) 531 nBlankSize = Width(); 532 else 533 nBlankSize = nHoleLen * rInf.GetTxtSize( ' ' ).Width(); 534 Width( Width() - nBlankSize ); 535 rInf.X( rInf.X() - nBlankSize ); 536 SetLen( GetLen() - nHoleLen ); 537 SwLinePortion *pHole = new SwHolePortion( *this ); 538 ( (SwHolePortion *)pHole )->SetBlankWidth( nBlankSize ); 539 ( (SwHolePortion *)pHole )->SetLen( nHoleLen ); 540 Insert( pHole ); 541 } 542 } 543 544 /************************************************************************* 545 * virtual SwTxtPortion::GetCrsrOfst() 546 *************************************************************************/ 547 548 549 550 xub_StrLen SwTxtPortion::GetCrsrOfst( const KSHORT nOfst ) const 551 { 552 ASSERT( !this, "SwTxtPortion::GetCrsrOfst: don't use this method!" ); 553 return SwLinePortion::GetCrsrOfst( nOfst ); 554 } 555 556 /************************************************************************* 557 * virtual SwTxtPortion::GetTxtSize() 558 *************************************************************************/ 559 // Das GetTxtSize() geht davon aus, dass die eigene Laenge korrekt ist 560 561 SwPosSize SwTxtPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const 562 { 563 return rInf.GetTxtSize(); 564 } 565 566 /************************************************************************* 567 * virtual SwTxtPortion::Paint() 568 *************************************************************************/ 569 570 571 572 void SwTxtPortion::Paint( const SwTxtPaintInfo &rInf ) const 573 { 574 if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDEND==rInf.GetTxt().GetChar(rInf.GetIdx())) 575 { 576 rInf.DrawBackBrush( *this ); 577 const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDEND); 578 rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false ); 579 } 580 else if (rInf.OnWin() && 1==rInf.GetLen() && CH_TXT_ATR_FIELDSTART==rInf.GetTxt().GetChar(rInf.GetIdx())) 581 { 582 rInf.DrawBackBrush( *this ); 583 const XubString aTxt = XubString::CreateFromAscii(CH_TXT_ATR_SUBST_FIELDSTART); 584 rInf.DrawText( aTxt, *this, 0, aTxt.Len(), false ); 585 } 586 else if( GetLen() ) 587 { 588 rInf.DrawBackBrush( *this ); 589 590 // do we have to repaint a post it portion? 591 if( rInf.OnWin() && pPortion && !pPortion->Width() ) 592 pPortion->PrePaint( rInf, this ); 593 594 const SwWrongList *pWrongList = rInf.GetpWrongList(); 595 const SwWrongList *pGrammarCheckList = rInf.GetGrammarCheckList(); 596 // SMARTTAGS 597 const SwWrongList *pSmarttags = rInf.GetSmartTags(); 598 599 const bool bWrong = 0 != pWrongList; 600 const bool bGrammarCheck = 0 != pGrammarCheckList; 601 const bool bSmartTags = 0 != pSmarttags; 602 603 if ( bWrong || bSmartTags || bGrammarCheck ) 604 rInf.DrawMarkedText( *this, rInf.GetLen(), sal_False, bWrong, bSmartTags, bGrammarCheck ); 605 else 606 rInf.DrawText( *this, rInf.GetLen(), sal_False ); 607 } 608 } 609 610 /************************************************************************* 611 * virtual SwTxtPortion::GetExpTxt() 612 *************************************************************************/ 613 614 615 616 sal_Bool SwTxtPortion::GetExpTxt( const SwTxtSizeInfo &, XubString & ) const 617 { 618 return sal_False; 619 } 620 621 /************************************************************************* 622 * xub_StrLen SwTxtPortion::GetSpaceCnt() 623 * long SwTxtPortion::CalcSpacing() 624 * sind fuer den Blocksatz zustaendig und ermitteln die Anzahl der Blanks 625 * und den daraus resultierenden zusaetzlichen Zwischenraum 626 *************************************************************************/ 627 628 xub_StrLen SwTxtPortion::GetSpaceCnt( const SwTxtSizeInfo &rInf, 629 xub_StrLen& rCharCnt ) const 630 { 631 xub_StrLen nCnt = 0; 632 xub_StrLen nPos = 0; 633 if ( InExpGrp() ) 634 { 635 if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() ) 636 { 637 // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank 638 // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen 639 sal_Bool bOldOnWin = rInf.OnWin(); 640 ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False ); 641 642 XubString aStr( aEmptyStr ); 643 GetExpTxt( rInf, aStr ); 644 ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin ); 645 646 nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this ); 647 nPos = aStr.Len(); 648 } 649 } 650 else if( !IsDropPortion() ) 651 { 652 nCnt = nCnt + lcl_AddSpace( rInf, 0, *this ); 653 nPos = GetLen(); 654 } 655 rCharCnt = rCharCnt + nPos; 656 return nCnt; 657 } 658 659 long SwTxtPortion::CalcSpacing( long nSpaceAdd, const SwTxtSizeInfo &rInf ) const 660 { 661 xub_StrLen nCnt = 0; 662 663 if ( InExpGrp() ) 664 { 665 if( !IsBlankPortion() && !InNumberGrp() && !IsCombinedPortion() ) 666 { 667 // Bei OnWin() wird anstatt eines Leerstrings gern mal ein Blank 668 // zurueckgeliefert, das koennen wir hier aber gar nicht gebrauchen 669 sal_Bool bOldOnWin = rInf.OnWin(); 670 ((SwTxtSizeInfo &)rInf).SetOnWin( sal_False ); 671 672 XubString aStr( aEmptyStr ); 673 GetExpTxt( rInf, aStr ); 674 ((SwTxtSizeInfo &)rInf).SetOnWin( bOldOnWin ); 675 if( nSpaceAdd > 0 ) 676 nCnt = nCnt + lcl_AddSpace( rInf, &aStr, *this ); 677 else 678 { 679 nSpaceAdd = -nSpaceAdd; 680 nCnt = aStr.Len(); 681 } 682 } 683 } 684 else if( !IsDropPortion() ) 685 { 686 if( nSpaceAdd > 0 ) 687 nCnt = nCnt + lcl_AddSpace( rInf, 0, *this ); 688 else 689 { 690 nSpaceAdd = -nSpaceAdd; 691 nCnt = GetLen(); 692 SwLinePortion* pPor = GetPortion(); 693 694 // we do not want an extra space in front of margin portions 695 if ( nCnt ) 696 { 697 while ( pPor && !pPor->Width() && ! pPor->IsHolePortion() ) 698 pPor = pPor->GetPortion(); 699 700 if ( !pPor || pPor->InFixMargGrp() || pPor->IsHolePortion() ) 701 --nCnt; 702 } 703 } 704 } 705 706 return nCnt * nSpaceAdd / SPACING_PRECISION_FACTOR; 707 } 708 709 /************************************************************************* 710 * virtual SwTxtPortion::HandlePortion() 711 *************************************************************************/ 712 713 void SwTxtPortion::HandlePortion( SwPortionHandler& rPH ) const 714 { 715 rPH.Text( GetLen(), GetWhichPor() ); 716 } 717 718 719 SwTxtInputFldPortion::SwTxtInputFldPortion() 720 : SwTxtPortion() 721 , mbContainsInputFieldStart( false ) 722 , mbContainsInputFieldEnd( false ) 723 { 724 SetWhichPor( POR_INPUTFLD ); 725 } 726 727 728 sal_Bool SwTxtInputFldPortion::Format( SwTxtFormatInfo &rInf ) 729 { 730 mbContainsInputFieldStart = 731 rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART; 732 mbContainsInputFieldEnd = 733 rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND; 734 735 sal_Bool bRet = sal_False; 736 if ( rInf.GetLen() == 1 737 && ( mbContainsInputFieldStart || mbContainsInputFieldEnd ) ) 738 { 739 Width( 0 ); 740 } 741 else 742 { 743 SwTxtSlot aFormatTxt( &rInf, this, true, true, 0 ); 744 if ( rInf.GetLen() == 0 ) 745 { 746 Width( 0 ); 747 } 748 else 749 { 750 bRet = SwTxtPortion::Format( rInf ); 751 752 if ( mbContainsInputFieldEnd ) 753 { 754 // adjust portion length accordingly, if complete text fits into the portion 755 if ( GetLen() == rInf.GetLen() ) 756 { 757 SetLen( GetLen() + 1 ); 758 } 759 } 760 761 if ( mbContainsInputFieldStart ) 762 { 763 // adjust portion length accordingly 764 SetLen( GetLen() + 1 ); 765 } 766 } 767 } 768 769 return bRet; 770 } 771 772 void SwTxtInputFldPortion::Paint( const SwTxtPaintInfo &rInf ) const 773 { 774 if ( Width() ) 775 { 776 rInf.DrawViewOpt( *this, POR_INPUTFLD ); 777 static sal_Char sSpace = ' '; 778 SwTxtSlot aPaintTxt( &rInf, this, true, true, 779 ContainsOnlyDummyChars() ? &sSpace : 0 ); 780 SwTxtPortion::Paint( rInf ); 781 } 782 } 783 784 sal_Bool SwTxtInputFldPortion::GetExpTxt( const SwTxtSizeInfo &rInf, XubString &rTxt ) const 785 { 786 xub_StrLen nIdx = rInf.GetIdx(); 787 xub_StrLen nLen = rInf.GetLen(); 788 if ( rInf.GetChar( rInf.GetIdx() ) == CH_TXT_ATR_INPUTFIELDSTART ) 789 { 790 ++nIdx; 791 --nLen; 792 } 793 if ( rInf.GetChar( rInf.GetIdx() + rInf.GetLen() - 1 ) == CH_TXT_ATR_INPUTFIELDEND ) 794 { 795 --nLen; 796 } 797 rTxt = rInf.GetTxt().Copy( nIdx, nLen ); 798 799 return sal_True; 800 } 801 802 803 SwPosSize SwTxtInputFldPortion::GetTxtSize( const SwTxtSizeInfo &rInf ) const 804 { 805 SwTxtSlot aFormatTxt( &rInf, this, true, false, 0 ); 806 if ( rInf.GetLen() == 0 ) 807 { 808 return SwPosSize( 0, 0 ); 809 } 810 811 return rInf.GetTxtSize(); 812 } 813 814 815 KSHORT SwTxtInputFldPortion::GetViewWidth( const SwTxtSizeInfo &rInf ) const 816 { 817 if( !Width() 818 && ContainsOnlyDummyChars() 819 && !rInf.GetOpt().IsPagePreview() 820 && !rInf.GetOpt().IsReadonly() 821 && SwViewOption::IsFieldShadings() ) 822 { 823 return rInf.GetTxtSize( ' ' ).Width(); 824 } 825 826 return SwTxtPortion::GetViewWidth( rInf ); 827 } 828 829 bool SwTxtInputFldPortion::ContainsOnlyDummyChars() const 830 { 831 return GetLen() <= 2 832 && mbContainsInputFieldStart 833 && mbContainsInputFieldEnd; 834 } 835 836 /************************************************************************* 837 * class SwHolePortion 838 *************************************************************************/ 839 840 841 842 SwHolePortion::SwHolePortion( const SwTxtPortion &rPor ) 843 : nBlankWidth( 0 ) 844 { 845 SetLen( 1 ); 846 Height( rPor.Height() ); 847 SetAscent( rPor.GetAscent() ); 848 SetWhichPor( POR_HOLE ); 849 } 850 851 SwLinePortion *SwHolePortion::Compress() { return this; } 852 853 /************************************************************************* 854 * virtual SwHolePortion::Paint() 855 *************************************************************************/ 856 857 858 859 void SwHolePortion::Paint( const SwTxtPaintInfo &rInf ) const 860 { 861 if( !rInf.GetOut() ) 862 return; 863 864 // #i16816# export stuff only needed for tagged pdf support 865 const vcl::PDFExtOutDevData* pPDFExt = dynamic_cast<const vcl::PDFExtOutDevData*>( rInf.GetOut()->GetExtOutDevData() ); 866 if( !pPDFExt || !pPDFExt->GetIsExportTaggedPDF()) 867 return; 868 869 // #i68503# the hole must have no decoration for a consistent visual appearance 870 const SwFont* pOrigFont = rInf.GetFont(); 871 SwFont* pHoleFont = NULL; 872 SwFontSave* pFontSave = NULL; 873 if( pOrigFont->GetUnderline() != UNDERLINE_NONE 874 || pOrigFont->GetOverline() != UNDERLINE_NONE 875 || pOrigFont->GetStrikeout() != STRIKEOUT_NONE ) 876 { 877 pHoleFont = new SwFont( *pOrigFont ); 878 pHoleFont->SetUnderline( UNDERLINE_NONE ); 879 pHoleFont->SetOverline( UNDERLINE_NONE ); 880 pHoleFont->SetStrikeout( STRIKEOUT_NONE ); 881 pFontSave = new SwFontSave( rInf, pHoleFont ); 882 } 883 884 const XubString aTxt( ' ' ); 885 rInf.DrawText( aTxt, *this, 0, 1, false ); 886 887 delete pFontSave; 888 delete pHoleFont; 889 } 890 891 /************************************************************************* 892 * virtual SwHolePortion::Format() 893 *************************************************************************/ 894 895 896 897 sal_Bool SwHolePortion::Format( SwTxtFormatInfo &rInf ) 898 { 899 return rInf.IsFull() || rInf.X() >= rInf.Width(); 900 } 901 902 /************************************************************************* 903 * virtual SwHolePortion::HandlePortion() 904 *************************************************************************/ 905 906 void SwHolePortion::HandlePortion( SwPortionHandler& rPH ) const 907 { 908 rPH.Text( GetLen(), GetWhichPor() ); 909 } 910 911 void SwFieldMarkPortion::Paint( const SwTxtPaintInfo & /*rInf*/) const 912 { 913 // These shouldn't be painted! 914 //SwTxtPortion::Paint(rInf); 915 } 916 917 sal_Bool SwFieldMarkPortion::Format( SwTxtFormatInfo & ) 918 { 919 sal_Bool ret=0; 920 Width(0); 921 return ret; 922 } 923 924 namespace { 925 static sal_Int32 getCurrentListIndex( IFieldmark* pBM, 926 ::rtl::OUString* io_pCurrentText = NULL ) 927 { 928 const IFieldmark::parameter_map_t* const pParameters = pBM->GetParameters(); 929 sal_Int32 nCurrentIdx = 0; 930 const IFieldmark::parameter_map_t::const_iterator pResult = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_RESULT)); 931 if(pResult != pParameters->end()) 932 pResult->second >>= nCurrentIdx; 933 if(io_pCurrentText) 934 { 935 const IFieldmark::parameter_map_t::const_iterator pListEntries = pParameters->find(::rtl::OUString::createFromAscii(ODF_FORMDROPDOWN_LISTENTRY)); 936 if(pListEntries != pParameters->end()) 937 { 938 uno::Sequence< ::rtl::OUString > vListEntries; 939 pListEntries->second >>= vListEntries; 940 if(nCurrentIdx < vListEntries.getLength()) 941 *io_pCurrentText = vListEntries[nCurrentIdx]; 942 } 943 } 944 return nCurrentIdx; 945 } 946 } 947 948 //FIXME Fieldbk 949 void SwFieldFormPortion::Paint( const SwTxtPaintInfo& rInf ) const 950 { 951 SwTxtNode* pNd = const_cast<SwTxtNode*>(rInf.GetTxtFrm()->GetTxtNode()); 952 const SwDoc *doc=pNd->GetDoc(); 953 SwIndex aIndex( pNd, rInf.GetIdx() ); 954 SwPosition aPosition(*pNd, aIndex); 955 956 IFieldmark* pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition ); 957 958 OSL_ENSURE( pBM, 959 "SwFieldFormPortion::Paint(..)" 960 " - Where is my form field bookmark???"); 961 962 if ( pBM != NULL ) 963 { 964 if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) ) 965 { // a checkbox... 966 ICheckboxFieldmark* pCheckboxFm = dynamic_cast< ICheckboxFieldmark* >(pBM); 967 bool checked = pCheckboxFm->IsChecked(); 968 rInf.DrawCheckBox(*this, checked); 969 } 970 else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) ) 971 { // a list... 972 rtl::OUString aTxt; 973 rInf.DrawViewOpt( *this, POR_FLD ); 974 rInf.DrawText( aTxt, *this, 0, 0/*aTxt.getLength()*/, false ); 975 } 976 else 977 { 978 assert(0); // unknown type... 979 } 980 } 981 } 982 983 sal_Bool SwFieldFormPortion::Format( SwTxtFormatInfo & rInf ) 984 { 985 sal_Bool ret = 0; 986 SwTxtNode *pNd = const_cast < SwTxtNode * >( rInf.GetTxtFrm( )->GetTxtNode( ) ); 987 const SwDoc *doc = pNd->GetDoc( ); 988 SwIndex aIndex( pNd, rInf.GetIdx( ) ); 989 SwPosition aPosition( *pNd, aIndex ); 990 IFieldmark *pBM = doc->getIDocumentMarkAccess( )->getFieldmarkFor( aPosition ); 991 ASSERT( pBM != NULL, "Where is my form field bookmark???" ); 992 if ( pBM != NULL ) 993 { 994 if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMCHECKBOX ) ) 995 { 996 Width( rInf.GetTxtHeight( ) ); 997 Height( rInf.GetTxtHeight( ) ); 998 SetAscent( rInf.GetAscent( ) ); 999 } 1000 else if ( pBM->GetFieldname( ).equalsAscii( ODF_FORMDROPDOWN ) ) 1001 { 1002 ::rtl::OUString aTxt; 1003 getCurrentListIndex( pBM, &aTxt ); 1004 SwPosSize aPosSize = rInf.GetTxtSize( aTxt ); 1005 Width( aPosSize.Width( ) ); 1006 Height( aPosSize.Height( ) ); 1007 SetAscent( rInf.GetAscent( ) ); 1008 } 1009 else 1010 { 1011 assert( 0 ); // unknown type... 1012 } 1013 } 1014 return ret; 1015 } 1016 1017 1018