1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <hintids.hxx> 21 #include <fmtfld.hxx> 22 #include <txtfld.hxx> 23 #include <charfmt.hxx> 24 #include <fmtautofmt.hxx> 25 26 #include <viewsh.hxx> 27 #include <doc.hxx> 28 #include <rootfrm.hxx> 29 #include <pagefrm.hxx> 30 #include <ndtxt.hxx> 31 #include <fldbas.hxx> 32 #include <viewopt.hxx> 33 #include <flyfrm.hxx> 34 #include <viewimp.hxx> 35 #include <swfont.hxx> 36 #include <swmodule.hxx> 37 #include "porfld.hxx" 38 #include "porftn.hxx" 39 #include "porref.hxx" 40 #include "portox.hxx" 41 #include "porfly.hxx" 42 #include "itrform2.hxx" 43 #include <chpfld.hxx> 44 #include <dbfld.hxx> 45 #include <expfld.hxx> 46 #include <docufld.hxx> 47 #include <pagedesc.hxx> 48 #include <fmtmeta.hxx> 49 #include <reffld.hxx> 50 #include <flddat.hxx> 51 #include <IDocumentSettingAccess.hxx> 52 #include <IDocumentRedlineAccess.hxx> 53 #include <redline.hxx> 54 #include <sfx2/docfile.hxx> 55 #include <svl/grabbagitem.hxx> 56 #include <svl/itemiter.hxx> 57 #include <svl/whiter.hxx> 58 #include <editeng/colritem.hxx> 59 #include <editeng/udlnitem.hxx> 60 #include <editeng/crossedoutitem.hxx> 61 62 static bool lcl_IsInBody( SwFrame const *pFrame ) 63 { 64 if ( pFrame->IsInDocBody() ) 65 return true; 66 else 67 { 68 const SwFrame *pTmp = pFrame; 69 const SwFlyFrame *pFly; 70 while ( nullptr != (pFly = pTmp->FindFlyFrame()) ) 71 pTmp = pFly->GetAnchorFrame(); 72 return pTmp->IsInDocBody(); 73 } 74 } 75 76 SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf, 77 const SwTextAttr *pHint ) const 78 { 79 SwExpandPortion *pRet = nullptr; 80 SwFrame *pFrame = m_pFrame; 81 SwField *pField = const_cast<SwField*>(pHint->GetFormatField().GetField()); 82 const bool bName = rInf.GetOpt().IsFieldName(); 83 84 SwCharFormat* pChFormat = nullptr; 85 bool bNewFlyPor = false; 86 sal_uInt16 subType = 0; 87 88 // set language 89 const_cast<SwTextFormatter*>(this)->SeekAndChg( rInf ); 90 if (pField->GetLanguage() != GetFnt()->GetLanguage()) 91 { 92 pField->SetLanguage( GetFnt()->GetLanguage() ); 93 // let the visual note know about its new language 94 if (pField->GetTyp()->Which()==SwFieldIds::Postit) 95 const_cast<SwFormatField*> (&pHint->GetFormatField())->Broadcast( SwFormatFieldHint( &pHint->GetFormatField(), SwFormatFieldHintWhich::LANGUAGE ) ); 96 } 97 98 SwViewShell *pSh = rInf.GetVsh(); 99 SwDoc *const pDoc( pSh ? pSh->GetDoc() : nullptr ); 100 bool const bInClipboard( pDoc == nullptr || pDoc->IsClipBoard() ); 101 bool bPlaceHolder = false; 102 103 switch( pField->GetTyp()->Which() ) 104 { 105 case SwFieldIds::Script: 106 case SwFieldIds::Postit: 107 pRet = new SwPostItsPortion( SwFieldIds::Script == pField->GetTyp()->Which() ); 108 break; 109 110 case SwFieldIds::CombinedChars: 111 { 112 if( bName ) 113 pRet = new SwFieldPortion( pField->GetFieldName() ); 114 else 115 pRet = new SwCombinedPortion( pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 116 } 117 break; 118 119 case SwFieldIds::HiddenText: 120 { 121 OUString const aStr( bName 122 ? pField->GetFieldName() 123 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 124 pRet = new SwHiddenPortion(aStr); 125 } 126 break; 127 128 case SwFieldIds::Chapter: 129 if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) 130 { 131 static_cast<SwChapterField*>(pField)->ChangeExpansion(*pFrame, 132 &static_txtattr_cast<SwTextField const*>(pHint)->GetTextNode()); 133 } 134 { 135 OUString const aStr( bName 136 ? pField->GetFieldName() 137 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 138 pRet = new SwFieldPortion( aStr ); 139 } 140 break; 141 142 case SwFieldIds::DocStat: 143 if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) 144 { 145 static_cast<SwDocStatField*>(pField)->ChangeExpansion( pFrame ); 146 } 147 { 148 OUString const aStr( bName 149 ? pField->GetFieldName() 150 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 151 pRet = new SwFieldPortion( aStr ); 152 } 153 static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGECOUNTFLD; 154 break; 155 156 case SwFieldIds::PageNumber: 157 { 158 if( !bName && pSh && pSh->GetLayout() && !pSh->Imp()->IsUpdateExpFields() ) 159 { 160 SwPageNumberFieldType *pPageNr = static_cast<SwPageNumberFieldType *>(pField->GetTyp()); 161 162 const SwRootFrame* pTmpRootFrame = pSh->GetLayout(); 163 const bool bVirt = pTmpRootFrame->IsVirtPageNum(); 164 165 sal_uInt16 nVirtNum = pFrame->GetVirtPageNum(); 166 sal_uInt16 nNumPages = pTmpRootFrame->GetPageNum(); 167 SvxNumType nNumFormat = SvxNumType(-1); 168 if(SVX_NUM_PAGEDESC == pField->GetFormat()) 169 nNumFormat = pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType(); 170 static_cast<SwPageNumberField*>(pField) 171 ->ChangeExpansion(nVirtNum, nNumPages); 172 pPageNr->ChangeExpansion(pDoc, 173 bVirt, nNumFormat != SvxNumType(-1) ? &nNumFormat : nullptr); 174 } 175 { 176 OUString const aStr( bName 177 ? pField->GetFieldName() 178 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 179 pRet = new SwFieldPortion( aStr ); 180 } 181 static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGENUMBERFLD; 182 break; 183 } 184 case SwFieldIds::GetExp: 185 { 186 if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) 187 { 188 SwGetExpField* pExpField = static_cast<SwGetExpField*>(pField); 189 if( !::lcl_IsInBody( pFrame ) ) 190 { 191 pExpField->ChgBodyTextFlag( false ); 192 pExpField->ChangeExpansion(*pFrame, 193 *static_txtattr_cast<SwTextField const*>(pHint)); 194 } 195 else if( !pExpField->IsInBodyText() ) 196 { 197 // Was something else previously, thus: expand first, then convert it! 198 pExpField->ChangeExpansion(*pFrame, 199 *static_txtattr_cast<SwTextField const*>(pHint)); 200 pExpField->ChgBodyTextFlag( true ); 201 } 202 } 203 { 204 OUString const aStr( bName 205 ? pField->GetFieldName() 206 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 207 pRet = new SwFieldPortion( aStr ); 208 } 209 break; 210 } 211 case SwFieldIds::Database: 212 { 213 if( !bName ) 214 { 215 SwDBField* pDBField = static_cast<SwDBField*>(pField); 216 pDBField->ChgBodyTextFlag( ::lcl_IsInBody( pFrame ) ); 217 } 218 { 219 OUString const aStr( bName 220 ? pField->GetFieldName() 221 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 222 pRet = new SwFieldPortion(aStr); 223 } 224 break; 225 } 226 case SwFieldIds::RefPageGet: 227 if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) 228 { 229 static_cast<SwRefPageGetField*>(pField)->ChangeExpansion(*pFrame, 230 static_txtattr_cast<SwTextField const*>(pHint)); 231 } 232 { 233 OUString const aStr( bName 234 ? pField->GetFieldName() 235 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 236 pRet = new SwFieldPortion(aStr); 237 } 238 break; 239 240 case SwFieldIds::JumpEdit: 241 if( !bName ) 242 pChFormat = static_cast<SwJumpEditField*>(pField)->GetCharFormat(); 243 bNewFlyPor = true; 244 bPlaceHolder = true; 245 break; 246 case SwFieldIds::GetRef: 247 subType = static_cast<SwGetRefField*>(pField)->GetSubType(); 248 { 249 OUString const str( bName 250 ? pField->GetFieldName() 251 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 252 pRet = new SwFieldPortion(str); 253 } 254 if( subType == REF_BOOKMARK ) 255 static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_BOOKMARKFLD; 256 else if( subType == REF_SETREFATTR ) 257 static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_SETREFATTRFLD; 258 break; 259 case SwFieldIds::DateTime: 260 subType = static_cast<SwDateTimeField*>(pField)->GetSubType(); 261 { 262 OUString const str( bName 263 ? pField->GetFieldName() 264 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 265 pRet = new SwFieldPortion(str); 266 } 267 if( subType & DATEFLD ) 268 static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_DATEFLD; 269 else if( subType & TIMEFLD ) 270 static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_TIMEFLD; 271 break; 272 default: 273 { 274 OUString const aStr( bName 275 ? pField->GetFieldName() 276 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 277 pRet = new SwFieldPortion(aStr); 278 } 279 } 280 281 if( bNewFlyPor ) 282 { 283 std::unique_ptr<SwFont> pTmpFnt; 284 if( !bName ) 285 { 286 pTmpFnt.reset(new SwFont( *m_pFont )); 287 pTmpFnt->SetDiffFnt(&pChFormat->GetAttrSet(), &m_pFrame->GetDoc().getIDocumentSettingAccess()); 288 } 289 OUString const aStr( bName 290 ? pField->GetFieldName() 291 : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); 292 pRet = new SwFieldPortion(aStr, std::move(pTmpFnt), bPlaceHolder); 293 } 294 295 return pRet; 296 } 297 298 static SwFieldPortion * lcl_NewMetaPortion(SwTextAttr & rHint, const bool bPrefix) 299 { 300 ::sw::Meta *const pMeta( 301 static_cast<SwFormatMeta &>(rHint.GetAttr()).GetMeta() ); 302 OUString fix; 303 ::sw::MetaField *const pField( dynamic_cast< ::sw::MetaField * >(pMeta) ); 304 OSL_ENSURE(pField, "lcl_NewMetaPortion: no meta field?"); 305 if (pField) 306 { 307 OUString color; 308 pField->GetPrefixAndSuffix(bPrefix ? &fix : nullptr, bPrefix ? nullptr : &fix, &color); 309 } 310 return new SwFieldPortion( fix ); 311 } 312 313 /** 314 * Try to create a new portion with zero length, for an end of a hint 315 * (where there is no CH_TXTATR). Because there may be multiple hint ends at a 316 * given index, m_pByEndIter is used to keep track of the already created 317 * portions. But the portions created here may actually be deleted again, 318 * due to Underflow. In that case, m_pByEndIter must be decremented, 319 * so the portion will be created again on the next line. 320 */ 321 SwExpandPortion * SwTextFormatter::TryNewNoLengthPortion(SwTextFormatInfo const & rInfo) 322 { 323 const TextFrameIndex nIdx(rInfo.GetIdx()); 324 325 // sw_redlinehide: because there is a dummy character at the start of these 326 // hints, it's impossible to have ends of hints from different nodes at the 327 // same view position, so it's sufficient to check the hints of the current 328 // node. However, m_pByEndIter exists for the whole text frame, so 329 // it's necessary to iterate all hints for that purpose... 330 if (!m_pByEndIter) 331 { 332 m_pByEndIter.reset(new sw::MergedAttrIterByEnd(*rInfo.GetTextFrame())); 333 } 334 SwTextNode const* pNode(nullptr); 335 for (SwTextAttr const* pHint = m_pByEndIter->NextAttr(pNode); pHint; 336 pHint = m_pByEndIter->NextAttr(pNode)) 337 { 338 SwTextAttr & rHint(const_cast<SwTextAttr&>(*pHint)); 339 TextFrameIndex const nEnd( 340 rInfo.GetTextFrame()->MapModelToView(pNode, rHint.GetAnyEnd())); 341 if (nEnd > nIdx) 342 { 343 m_pByEndIter->PrevAttr(); 344 break; 345 } 346 if (nEnd == nIdx) 347 { 348 if (RES_TXTATR_METAFIELD == rHint.Which()) 349 { 350 SwFieldPortion *const pPortion( 351 lcl_NewMetaPortion(rHint, false)); 352 pPortion->SetNoLength(); // no CH_TXTATR at hint end! 353 return pPortion; 354 } 355 } 356 } 357 return nullptr; 358 } 359 360 SwLinePortion *SwTextFormatter::NewExtraPortion( SwTextFormatInfo &rInf ) 361 { 362 SwTextAttr *pHint = GetAttr( rInf.GetIdx() ); 363 SwLinePortion *pRet = nullptr; 364 if( !pHint ) 365 { 366 pRet = new SwTextPortion; 367 pRet->SetLen(TextFrameIndex(1)); 368 rInf.SetLen(TextFrameIndex(1)); 369 return pRet; 370 } 371 372 switch( pHint->Which() ) 373 { 374 case RES_TXTATR_FLYCNT : 375 { 376 pRet = NewFlyCntPortion( rInf, pHint ); 377 break; 378 } 379 case RES_TXTATR_FTN : 380 { 381 pRet = NewFootnotePortion( rInf, pHint ); 382 break; 383 } 384 case RES_TXTATR_FIELD : 385 case RES_TXTATR_ANNOTATION : 386 { 387 pRet = NewFieldPortion( rInf, pHint ); 388 break; 389 } 390 case RES_TXTATR_REFMARK : 391 { 392 pRet = new SwIsoRefPortion; 393 break; 394 } 395 case RES_TXTATR_TOXMARK : 396 { 397 pRet = new SwIsoToxPortion; 398 break; 399 } 400 case RES_TXTATR_METAFIELD: 401 { 402 pRet = lcl_NewMetaPortion( *pHint, true ); 403 break; 404 } 405 default: ; 406 } 407 if( !pRet ) 408 { 409 auto pFieldPortion = new SwFieldPortion( "" ); 410 if (pHint->Which() == RES_TXTATR_CONTENTCONTROL) 411 { 412 pFieldPortion->SetContentControl(true); 413 } 414 pRet = pFieldPortion; 415 rInf.SetLen(TextFrameIndex(1)); 416 } 417 return pRet; 418 } 419 420 /** 421 * OOXML spec says that w:rPr inside w:pPr specifies formatting for the paragraph mark symbol (i.e. the control 422 * character than can be configured to be shown). However, in practice MSO also uses it as direct formatting 423 * for numbering in that paragraph. I don't know if the problem is in the spec or in MSWord. 424 */ 425 static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextFormatInfo& rInf, 426 const IDocumentSettingAccess* pIDSA, 427 const SwAttrSet* pFormat) 428 { 429 if( !pIDSA->get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING )) 430 return; 431 432 SwFormatAutoFormat const& rListAutoFormat(rInf.GetTextFrame()->GetTextNodeForParaProps()->GetAttr(RES_PARATR_LIST_AUTOFMT)); 433 std::shared_ptr<SfxItemSet> pSet(rListAutoFormat.GetStyleHandle()); 434 435 // TODO remove this fallback (for WW8/RTF) 436 bool isDOCX = pIDSA->get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS); 437 if (!isDOCX && !pSet) 438 { 439 TextFrameIndex const nTextLen(rInf.GetTextFrame()->GetText().getLength()); 440 SwTextNode const* pNode(nullptr); 441 sw::MergedAttrIterReverse iter(*rInf.GetTextFrame()); 442 for (SwTextAttr const* pHint = iter.PrevAttr(&pNode); pHint; 443 pHint = iter.PrevAttr(&pNode)) 444 { 445 TextFrameIndex const nHintEnd( 446 rInf.GetTextFrame()->MapModelToView(pNode, pHint->GetAnyEnd())); 447 if (nHintEnd < nTextLen) 448 { 449 break; // only those at para end are interesting 450 } 451 // Formatting for the paragraph mark is usually set to apply only to the 452 // (non-existent) extra character at end of the text node, but there can be 453 // other hints too (ending at nTextLen), so look for all matching hints. 454 // Still the (non-existent) extra character at the end is preferred if it exists. 455 if (pHint->Which() == RES_TXTATR_AUTOFMT) 456 { 457 pSet = pHint->GetAutoFormat().GetStyleHandle(); 458 // When we find an empty hint (start == end) we got what we are looking for. 459 if (pHint->GetStart() == *pHint->End()) 460 break; 461 } 462 } 463 } 464 465 // Check each item and in case it should be ignored, then clear it. 466 if (!pSet) 467 return; 468 469 std::unique_ptr<SfxItemSet> const pCleanedSet = pSet->Clone(); 470 471 if (pCleanedSet->HasItem(RES_TXTATR_CHARFMT)) 472 { 473 // Insert attributes of referenced char format into current set 474 const SwFormatCharFormat& rCharFormat = pCleanedSet->Get(RES_TXTATR_CHARFMT); 475 const SwAttrSet& rStyleAttrs = static_cast<const SwCharFormat *>(rCharFormat.GetRegisteredIn())->GetAttrSet(); 476 SfxWhichIter aIter(rStyleAttrs); 477 sal_uInt16 nWhich = aIter.FirstWhich(); 478 while (nWhich) 479 { 480 if (!SwTextNode::IsIgnoredCharFormatForNumbering(nWhich, /*bIsCharStyle=*/true) 481 && !pCleanedSet->HasItem(nWhich) 482 && !(pFormat && pFormat->HasItem(nWhich)) ) 483 { 484 // Copy from parent sets only allowed items which will not overwrite 485 // values explicitly defined in current set (pCleanedSet) or in pFormat 486 if (const SfxPoolItem* pItem = rStyleAttrs.GetItem(nWhich, true)) 487 pCleanedSet->Put(*pItem); 488 } 489 nWhich = aIter.NextWhich(); 490 } 491 492 // It is not required here anymore, all referenced items are inserted 493 pCleanedSet->ClearItem(RES_TXTATR_CHARFMT); 494 }; 495 496 SfxItemIter aIter(*pSet); 497 const SfxPoolItem* pItem = aIter.GetCurItem(); 498 while (pItem) 499 { 500 if (SwTextNode::IsIgnoredCharFormatForNumbering(pItem->Which())) 501 pCleanedSet->ClearItem(pItem->Which()); 502 else if (pFormat && pFormat->HasItem(pItem->Which())) 503 pCleanedSet->ClearItem(pItem->Which()); 504 else if (pItem->Which() == RES_CHRATR_BACKGROUND) 505 { 506 bool bShadingWasImported = false; 507 // If Shading was imported, it should not be converted to a Highlight, 508 // but remain as Shading which is ignored for numbering. 509 if (pCleanedSet->HasItem(RES_CHRATR_GRABBAG)) 510 { 511 SfxGrabBagItem aGrabBag = pCleanedSet->Get(RES_CHRATR_GRABBAG, /*bSrchInParent=*/false); 512 std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); 513 auto aIterator = rMap.find("CharShadingMarker"); 514 if (aIterator != rMap.end()) 515 aIterator->second >>= bShadingWasImported; 516 } 517 518 // If used, BACKGROUND is converted to HIGHLIGHT. So also ignore if a highlight already exists. 519 if (bShadingWasImported 520 || pCleanedSet->HasItem(RES_CHRATR_HIGHLIGHT) 521 || (pFormat && pFormat->HasItem(RES_CHRATR_HIGHLIGHT))) 522 { 523 pCleanedSet->ClearItem(pItem->Which()); 524 } 525 } 526 pItem = aIter.NextItem(); 527 }; 528 529 // SetDiffFnt resets the background color (why?), so capture it and re-apply if it had a value, 530 // because an existing value should override anything inherited from the paragraph marker. 531 const std::optional<Color> oFontBackColor = pNumFnt->GetBackColor(); 532 // The same is true for the highlight color. 533 const Color aHighlight = pNumFnt->GetHighlightColor(); 534 535 pNumFnt->SetDiffFnt(pCleanedSet.get(), pIDSA); 536 537 if (oFontBackColor) 538 pNumFnt->SetBackColor(oFontBackColor); 539 if (aHighlight != COL_TRANSPARENT) 540 pNumFnt->SetHighlightColor(aHighlight); 541 } 542 543 static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode, 544 bool& bIsMoved ) 545 { 546 const SwDoc& rDoc = rTextNode.GetDoc(); 547 SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( rTextNode, RedlineType::Any ); 548 549 if( SwRedlineTable::npos != nRedlPos ) 550 { 551 const SwNodeOffset nNdIdx = rTextNode.GetIndex(); 552 const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); 553 for( ; nRedlPos < rTable.size() ; ++nRedlPos ) 554 { 555 const SwRangeRedline* pTmp = rTable[ nRedlPos ]; 556 if( RedlineType::Delete == pTmp->GetType() || 557 RedlineType::Insert == pTmp->GetType() ) 558 { 559 const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End(); 560 if( pRStt->nNode <= nNdIdx && pREnd->nNode > nNdIdx ) 561 { 562 bIsMoved = pTmp->IsMoved(); 563 return pTmp; 564 } 565 } 566 } 567 } 568 return nullptr; 569 } 570 571 static bool lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextNode, const std::unique_ptr<SwFont>& pNumFnt ) 572 { 573 if ( rInf.GetVsh()->GetLayout()->IsHideRedlines() ) 574 return false; 575 576 bool bIsMoved; 577 const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode, bIsMoved ); 578 if (!pRedlineNum) 579 return false; 580 581 // moved text: dark green with double underline or strikethrough 582 if ( bIsMoved ) 583 { 584 pNumFnt->SetColor(COL_GREEN); 585 if ( RedlineType::Delete == pRedlineNum->GetType() ) 586 pNumFnt->SetStrikeout(STRIKEOUT_DOUBLE); 587 else 588 pNumFnt->SetUnderline(LINESTYLE_DOUBLE); 589 return true; 590 } 591 592 SwAttrPool& rPool = rInf.GetVsh()->GetDoc()->GetAttrPool(); 593 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool); 594 595 std::size_t aAuthor = (1 < pRedlineNum->GetStackCount()) 596 ? pRedlineNum->GetAuthor( 1 ) 597 : pRedlineNum->GetAuthor(); 598 599 if ( RedlineType::Delete == pRedlineNum->GetType() ) 600 SW_MOD()->GetDeletedAuthorAttr(aAuthor, aSet); 601 else 602 SW_MOD()->GetInsertAuthorAttr(aAuthor, aSet); 603 604 if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR)) 605 pNumFnt->SetColor(pItem->GetValue()); 606 if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE)) 607 pNumFnt->SetUnderline(pItem->GetLineStyle()); 608 if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT)) 609 pNumFnt->SetStrikeout( pItem->GetStrikeout() ); 610 611 return true; 612 } 613 614 SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) const 615 { 616 if( rInf.IsNumDone() || rInf.GetTextStart() != m_nStart 617 || rInf.GetTextStart() != rInf.GetIdx() ) 618 return nullptr; 619 620 SwNumberPortion *pRet = nullptr; 621 // sw_redlinehide: at this point it's certain that pTextNd is the node with 622 // the numbering of the frame; only the actual number-vector (GetNumString) 623 // depends on the hide-mode in the layout so other calls don't need to care 624 const SwTextNode *const pTextNd = GetTextFrame()->GetTextNodeForParaProps(); 625 const SwNumRule* pNumRule = pTextNd->GetNumRule(); 626 627 // Has a "valid" number? 628 // sw_redlinehide: check that pParaPropsNode is the correct one 629 assert(pTextNd->IsNumbered(m_pFrame->getRootFrame()) == pTextNd->IsNumbered(nullptr)); 630 if (pTextNd->IsNumbered(m_pFrame->getRootFrame()) && pTextNd->IsCountedInList()) 631 { 632 int nLevel = pTextNd->GetActualListLevel(); 633 634 if (nLevel < 0) 635 nLevel = 0; 636 637 if (nLevel >= MAXLEVEL) 638 nLevel = MAXLEVEL - 1; 639 640 const SwNumFormat &rNumFormat = pNumRule->Get( nLevel ); 641 const bool bLeft = SvxAdjust::Left == rNumFormat.GetNumAdjust(); 642 const bool bCenter = SvxAdjust::Center == rNumFormat.GetNumAdjust(); 643 const bool bLabelAlignmentPosAndSpaceModeActive( 644 rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ); 645 const sal_uInt16 nMinDist = bLabelAlignmentPosAndSpaceModeActive 646 ? 0 : rNumFormat.GetCharTextDistance(); 647 648 if( SVX_NUM_BITMAP == rNumFormat.GetNumberingType() ) 649 { 650 OUString referer; 651 if (auto const sh1 = rInf.GetVsh()) { 652 if (auto const doc = sh1->GetDoc()) { 653 auto const sh2 = doc->GetPersist(); 654 if (sh2 != nullptr && sh2->HasName()) { 655 referer = sh2->GetMedium()->GetName(); 656 } 657 } 658 } 659 pRet = new SwGrfNumPortion( pTextNd->GetLabelFollowedBy(), 660 rNumFormat.GetBrush(), referer, 661 rNumFormat.GetGraphicOrientation(), 662 rNumFormat.GetGraphicSize(), 663 bLeft, bCenter, nMinDist, 664 bLabelAlignmentPosAndSpaceModeActive ); 665 tools::Long nTmpA = rInf.GetLast()->GetAscent(); 666 tools::Long nTmpD = rInf.GetLast()->Height() - nTmpA; 667 if( !rInf.IsTest() ) 668 static_cast<SwGrfNumPortion*>(pRet)->SetBase( nTmpA, nTmpD, nTmpA, nTmpD ); 669 } 670 else 671 { 672 // The SwFont is created dynamically and passed in the ctor, 673 // as the CharFormat only returns an SV-Font. 674 // In the dtor of SwNumberPortion, the SwFont is deleted. 675 const SwAttrSet* pFormat = rNumFormat.GetCharFormat() ? 676 &rNumFormat.GetCharFormat()->GetAttrSet() : 677 nullptr; 678 const IDocumentSettingAccess* pIDSA = pTextNd->getIDocumentSettingAccess(); 679 680 if( SVX_NUM_CHAR_SPECIAL == rNumFormat.GetNumberingType() ) 681 { 682 const std::optional<vcl::Font> pFormatFnt = rNumFormat.GetBulletFont(); 683 684 // Build a new bullet font basing on the current paragraph font: 685 std::unique_ptr<SwFont> pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA )); 686 687 // #i53199# 688 if ( !pIDSA->get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) ) 689 { 690 // i18463: 691 // Underline style of paragraph font should not be considered 692 // Overline style of paragraph font should not be considered 693 // Weight style of paragraph font should not be considered 694 // Posture style of paragraph font should not be considered 695 pNumFnt->SetUnderline( LINESTYLE_NONE ); 696 pNumFnt->SetOverline( LINESTYLE_NONE ); 697 pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::Latin ); 698 pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CJK ); 699 pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CTL ); 700 pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::Latin ); 701 pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CJK ); 702 pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CTL ); 703 } 704 705 // Apply the explicit attributes from the character style 706 // associated with the numbering to the new bullet font. 707 if( pFormat ) 708 pNumFnt->SetDiffFnt( pFormat, pIDSA ); 709 710 checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat); 711 712 if ( pFormatFnt ) 713 { 714 const SwFontScript nAct = pNumFnt->GetActual(); 715 pNumFnt->SetFamily( pFormatFnt->GetFamilyType(), nAct ); 716 pNumFnt->SetName( pFormatFnt->GetFamilyName(), nAct ); 717 pNumFnt->SetStyleName( pFormatFnt->GetStyleName(), nAct ); 718 pNumFnt->SetCharSet( pFormatFnt->GetCharSet(), nAct ); 719 pNumFnt->SetPitch( pFormatFnt->GetPitch(), nAct ); 720 } 721 722 // we do not allow a vertical font 723 pNumFnt->SetVertical( pNumFnt->GetOrientation(), 724 m_pFrame->IsVertical() ); 725 726 lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ); 727 728 // --> OD 2008-01-23 #newlistelevelattrs# 729 if (rNumFormat.GetBulletChar()) 730 { 731 pRet = new SwBulletPortion(rNumFormat.GetBulletChar(), 732 pTextNd->GetLabelFollowedBy(), 733 std::move(pNumFnt), 734 bLeft, bCenter, nMinDist, 735 bLabelAlignmentPosAndSpaceModeActive); 736 } 737 } 738 else 739 { 740 // Show Changes mode shows the actual numbering (SwListRedlineType::HIDDEN) and 741 // the original one (SwListRedlineType::ORIGTEXT) instead of the fake numbering 742 // (SwListRedlineType::SHOW, which counts removed and inserted numbered paragraphs 743 // in a single list) 744 bool bHasHiddenNum = false; 745 OUString aText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::HIDDEN) ); 746 const SwDoc& rDoc = pTextNd->GetDoc(); 747 const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); 748 if ( rTable.size() && !rInf.GetVsh()->GetLayout()->IsHideRedlines() ) 749 { 750 OUString aHiddenText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::ORIGTEXT) ); 751 752 if ( !aText.isEmpty() || !aHiddenText.isEmpty() ) 753 { 754 if (aText != aHiddenText && !aHiddenText.isEmpty()) 755 { 756 bHasHiddenNum = true; 757 // show also original number after the actual one enclosed in [ and ], 758 // and replace tabulator with space to avoid messy indentation 759 // resulted by the longer numbering, e.g. "1.[2.]" instead of "1.". 760 aText = aText + "[" + aHiddenText + "]" 761 + pTextNd->GetLabelFollowedBy().replaceAll("\t", " "); 762 } 763 else if (!aText.isEmpty()) 764 aText += pTextNd->GetLabelFollowedBy(); 765 } 766 } 767 else if (!aText.isEmpty()) 768 aText += pTextNd->GetLabelFollowedBy(); 769 770 // Not just an optimization ... 771 // A number portion without text will be assigned a width of 0. 772 // The succeeding text portion will flow into the BreakCut in the BreakLine, 773 // although we have rInf.GetLast()->GetFlyPortion()! 774 if( !aText.isEmpty() ) 775 { 776 777 // Build a new numbering font basing on the current paragraph font: 778 std::unique_ptr<SwFont> pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA )); 779 780 // #i53199# 781 if ( !pIDSA->get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) ) 782 { 783 // i18463: 784 // Underline style of paragraph font should not be considered 785 pNumFnt->SetUnderline( LINESTYLE_NONE ); 786 // Overline style of paragraph font should not be considered 787 pNumFnt->SetOverline( LINESTYLE_NONE ); 788 } 789 790 // Apply the explicit attributes from the character style 791 // associated with the numbering to the new bullet font. 792 if( pFormat ) 793 pNumFnt->SetDiffFnt( pFormat, pIDSA ); 794 795 checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat); 796 797 if ( !lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ) && bHasHiddenNum ) 798 pNumFnt->SetColor(NON_PRINTING_CHARACTER_COLOR); 799 800 // we do not allow a vertical font 801 pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() ); 802 803 pRet = new SwNumberPortion( aText, std::move(pNumFnt), 804 bLeft, bCenter, nMinDist, 805 bLabelAlignmentPosAndSpaceModeActive ); 806 } 807 } 808 } 809 } 810 return pRet; 811 } 812 813 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 814
