diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sw/source/core/text/txtfld.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sw/source/core/text/txtfld.cxx')
-rw-r--r-- | sw/source/core/text/txtfld.cxx | 836 |
1 files changed, 836 insertions, 0 deletions
diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx new file mode 100644 index 000000000..ede8e30f2 --- /dev/null +++ b/sw/source/core/text/txtfld.cxx @@ -0,0 +1,836 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <hintids.hxx> +#include <fmtfld.hxx> +#include <txtfld.hxx> +#include <charfmt.hxx> +#include <fmtautofmt.hxx> + +#include <viewsh.hxx> +#include <doc.hxx> +#include <rootfrm.hxx> +#include <pagefrm.hxx> +#include <ndtxt.hxx> +#include <fldbas.hxx> +#include <viewopt.hxx> +#include <flyfrm.hxx> +#include <viewimp.hxx> +#include <swfont.hxx> +#include <swmodule.hxx> +#include "porfld.hxx" +#include "porftn.hxx" +#include "porref.hxx" +#include "portox.hxx" +#include "porfly.hxx" +#include "itrform2.hxx" +#include <chpfld.hxx> +#include <dbfld.hxx> +#include <expfld.hxx> +#include <docufld.hxx> +#include <pagedesc.hxx> +#include <fmtmeta.hxx> +#include <reffld.hxx> +#include <flddat.hxx> +#include <IDocumentSettingAccess.hxx> +#include <IDocumentRedlineAccess.hxx> +#include <redline.hxx> +#include <sfx2/docfile.hxx> +#include <svl/grabbagitem.hxx> +#include <svl/itemiter.hxx> +#include <svl/whiter.hxx> +#include <editeng/colritem.hxx> +#include <editeng/udlnitem.hxx> +#include <editeng/crossedoutitem.hxx> + +static bool lcl_IsInBody( SwFrame const *pFrame ) +{ + if ( pFrame->IsInDocBody() ) + return true; + else + { + const SwFrame *pTmp = pFrame; + const SwFlyFrame *pFly; + while ( nullptr != (pFly = pTmp->FindFlyFrame()) ) + pTmp = pFly->GetAnchorFrame(); + return pTmp->IsInDocBody(); + } +} + +SwExpandPortion *SwTextFormatter::NewFieldPortion( SwTextFormatInfo &rInf, + const SwTextAttr *pHint ) const +{ + SwExpandPortion *pRet = nullptr; + SwFrame *pFrame = m_pFrame; + SwField *pField = const_cast<SwField*>(pHint->GetFormatField().GetField()); + const bool bName = rInf.GetOpt().IsFieldName(); + + SwCharFormat* pChFormat = nullptr; + bool bNewFlyPor = false; + sal_uInt16 subType = 0; + + // set language + const_cast<SwTextFormatter*>(this)->SeekAndChg( rInf ); + if (pField->GetLanguage() != GetFnt()->GetLanguage()) + { + pField->SetLanguage( GetFnt()->GetLanguage() ); + // let the visual note know about its new language + if (pField->GetTyp()->Which()==SwFieldIds::Postit) + const_cast<SwFormatField*> (&pHint->GetFormatField())->Broadcast( SwFormatFieldHint( &pHint->GetFormatField(), SwFormatFieldHintWhich::LANGUAGE ) ); + } + + SwViewShell *pSh = rInf.GetVsh(); + SwDoc *const pDoc( pSh ? pSh->GetDoc() : nullptr ); + bool const bInClipboard( pDoc == nullptr || pDoc->IsClipBoard() ); + bool bPlaceHolder = false; + + switch( pField->GetTyp()->Which() ) + { + case SwFieldIds::Script: + case SwFieldIds::Postit: + pRet = new SwPostItsPortion( SwFieldIds::Script == pField->GetTyp()->Which() ); + break; + + case SwFieldIds::CombinedChars: + { + if( bName ) + pRet = new SwFieldPortion( pField->GetFieldName() ); + else + pRet = new SwCombinedPortion( pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + } + break; + + case SwFieldIds::HiddenText: + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwHiddenPortion(aStr); + } + break; + + case SwFieldIds::Chapter: + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) + { + static_cast<SwChapterField*>(pField)->ChangeExpansion(*pFrame, + &static_txtattr_cast<SwTextField const*>(pHint)->GetTextNode()); + } + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion( aStr ); + } + break; + + case SwFieldIds::DocStat: + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) + { + static_cast<SwDocStatField*>(pField)->ChangeExpansion( pFrame ); + } + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion( aStr ); + } + static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGECOUNTFLD; + break; + + case SwFieldIds::PageNumber: + { + if( !bName && pSh && pSh->GetLayout() && !pSh->Imp()->IsUpdateExpFields() ) + { + SwPageNumberFieldType *pPageNr = static_cast<SwPageNumberFieldType *>(pField->GetTyp()); + + const SwRootFrame* pTmpRootFrame = pSh->GetLayout(); + const bool bVirt = pTmpRootFrame->IsVirtPageNum(); + + sal_uInt16 nVirtNum = pFrame->GetVirtPageNum(); + sal_uInt16 nNumPages = pTmpRootFrame->GetPageNum(); + SvxNumType nNumFormat = SvxNumType(-1); + if(SVX_NUM_PAGEDESC == pField->GetFormat()) + nNumFormat = pFrame->FindPageFrame()->GetPageDesc()->GetNumType().GetNumberingType(); + static_cast<SwPageNumberField*>(pField) + ->ChangeExpansion(nVirtNum, nNumPages); + pPageNr->ChangeExpansion(pDoc, + bVirt, nNumFormat != SvxNumType(-1) ? &nNumFormat : nullptr); + } + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion( aStr ); + } + static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGENUMBERFLD; + break; + } + case SwFieldIds::GetExp: + { + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) + { + SwGetExpField* pExpField = static_cast<SwGetExpField*>(pField); + if( !::lcl_IsInBody( pFrame ) ) + { + pExpField->ChgBodyTextFlag( false ); + pExpField->ChangeExpansion(*pFrame, + *static_txtattr_cast<SwTextField const*>(pHint)); + } + else if( !pExpField->IsInBodyText() ) + { + // Was something else previously, thus: expand first, then convert it! + pExpField->ChangeExpansion(*pFrame, + *static_txtattr_cast<SwTextField const*>(pHint)); + pExpField->ChgBodyTextFlag( true ); + } + } + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion( aStr ); + } + break; + } + case SwFieldIds::Database: + { + if( !bName ) + { + SwDBField* pDBField = static_cast<SwDBField*>(pField); + pDBField->ChgBodyTextFlag( ::lcl_IsInBody( pFrame ) ); + } + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion(aStr); + } + break; + } + case SwFieldIds::RefPageGet: + if( !bName && pSh && !pSh->Imp()->IsUpdateExpFields() ) + { + static_cast<SwRefPageGetField*>(pField)->ChangeExpansion(*pFrame, + static_txtattr_cast<SwTextField const*>(pHint)); + } + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion(aStr); + } + break; + + case SwFieldIds::JumpEdit: + if( !bName ) + pChFormat = static_cast<SwJumpEditField*>(pField)->GetCharFormat(); + bNewFlyPor = true; + bPlaceHolder = true; + break; + case SwFieldIds::GetRef: + subType = static_cast<SwGetRefField*>(pField)->GetSubType(); + { + OUString const str( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion(str); + } + if( subType == REF_BOOKMARK ) + static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_BOOKMARKFLD; + else if( subType == REF_SETREFATTR ) + static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_SETREFATTRFLD; + break; + case SwFieldIds::DateTime: + subType = static_cast<SwDateTimeField*>(pField)->GetSubType(); + { + OUString const str( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion(str); + } + if( subType & DATEFLD ) + static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_DATEFLD; + else if( subType & TIMEFLD ) + static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType = ATTR_TIMEFLD; + break; + default: + { + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion(aStr); + } + } + + if( bNewFlyPor ) + { + std::unique_ptr<SwFont> pTmpFnt; + if( !bName ) + { + pTmpFnt.reset(new SwFont( *m_pFont )); + pTmpFnt->SetDiffFnt(&pChFormat->GetAttrSet(), &m_pFrame->GetDoc().getIDocumentSettingAccess()); + } + OUString const aStr( bName + ? pField->GetFieldName() + : pField->ExpandField(bInClipboard, pFrame->getRootFrame()) ); + pRet = new SwFieldPortion(aStr, std::move(pTmpFnt), bPlaceHolder); + } + + return pRet; +} + +static SwFieldPortion * lcl_NewMetaPortion(SwTextAttr & rHint, const bool bPrefix) +{ + ::sw::Meta *const pMeta( + static_cast<SwFormatMeta &>(rHint.GetAttr()).GetMeta() ); + OUString fix; + ::sw::MetaField *const pField( dynamic_cast< ::sw::MetaField * >(pMeta) ); + OSL_ENSURE(pField, "lcl_NewMetaPortion: no meta field?"); + if (pField) + { + OUString color; + pField->GetPrefixAndSuffix(bPrefix ? &fix : nullptr, bPrefix ? nullptr : &fix, &color); + } + return new SwFieldPortion( fix ); +} + +/** + * Try to create a new portion with zero length, for an end of a hint + * (where there is no CH_TXTATR). Because there may be multiple hint ends at a + * given index, m_pByEndIter is used to keep track of the already created + * portions. But the portions created here may actually be deleted again, + * due to Underflow. In that case, m_pByEndIter must be decremented, + * so the portion will be created again on the next line. + */ +SwExpandPortion * SwTextFormatter::TryNewNoLengthPortion(SwTextFormatInfo const & rInfo) +{ + const TextFrameIndex nIdx(rInfo.GetIdx()); + + // sw_redlinehide: because there is a dummy character at the start of these + // hints, it's impossible to have ends of hints from different nodes at the + // same view position, so it's sufficient to check the hints of the current + // node. However, m_pByEndIter exists for the whole text frame, so + // it's necessary to iterate all hints for that purpose... + if (!m_pByEndIter) + { + m_pByEndIter.reset(new sw::MergedAttrIterByEnd(*rInfo.GetTextFrame())); + } + SwTextNode const* pNode(nullptr); + for (SwTextAttr const* pHint = m_pByEndIter->NextAttr(pNode); pHint; + pHint = m_pByEndIter->NextAttr(pNode)) + { + SwTextAttr & rHint(const_cast<SwTextAttr&>(*pHint)); + TextFrameIndex const nEnd( + rInfo.GetTextFrame()->MapModelToView(pNode, rHint.GetAnyEnd())); + if (nEnd > nIdx) + { + m_pByEndIter->PrevAttr(); + break; + } + if (nEnd == nIdx) + { + if (RES_TXTATR_METAFIELD == rHint.Which()) + { + SwFieldPortion *const pPortion( + lcl_NewMetaPortion(rHint, false)); + pPortion->SetNoLength(); // no CH_TXTATR at hint end! + return pPortion; + } + } + } + return nullptr; +} + +SwLinePortion *SwTextFormatter::NewExtraPortion( SwTextFormatInfo &rInf ) +{ + SwTextAttr *pHint = GetAttr( rInf.GetIdx() ); + SwLinePortion *pRet = nullptr; + if( !pHint ) + { + pRet = new SwTextPortion; + pRet->SetLen(TextFrameIndex(1)); + rInf.SetLen(TextFrameIndex(1)); + return pRet; + } + + switch( pHint->Which() ) + { + case RES_TXTATR_FLYCNT : + { + pRet = NewFlyCntPortion( rInf, pHint ); + break; + } + case RES_TXTATR_FTN : + { + pRet = NewFootnotePortion( rInf, pHint ); + break; + } + case RES_TXTATR_FIELD : + case RES_TXTATR_ANNOTATION : + { + pRet = NewFieldPortion( rInf, pHint ); + break; + } + case RES_TXTATR_REFMARK : + { + pRet = new SwIsoRefPortion; + break; + } + case RES_TXTATR_TOXMARK : + { + pRet = new SwIsoToxPortion; + break; + } + case RES_TXTATR_METAFIELD: + { + pRet = lcl_NewMetaPortion( *pHint, true ); + break; + } + default: ; + } + if( !pRet ) + { + auto pFieldPortion = new SwFieldPortion( "" ); + if (pHint->Which() == RES_TXTATR_CONTENTCONTROL) + { + pFieldPortion->SetContentControl(true); + } + pRet = pFieldPortion; + rInf.SetLen(TextFrameIndex(1)); + } + return pRet; +} + +/** + * OOXML spec says that w:rPr inside w:pPr specifies formatting for the paragraph mark symbol (i.e. the control + * character than can be configured to be shown). However, in practice MSO also uses it as direct formatting + * for numbering in that paragraph. I don't know if the problem is in the spec or in MSWord. + */ +static void checkApplyParagraphMarkFormatToNumbering(SwFont* pNumFnt, SwTextFormatInfo& rInf, + const IDocumentSettingAccess* pIDSA, + const SwAttrSet* pFormat) +{ + if( !pIDSA->get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING )) + return; + + SwFormatAutoFormat const& rListAutoFormat(rInf.GetTextFrame()->GetTextNodeForParaProps()->GetAttr(RES_PARATR_LIST_AUTOFMT)); + std::shared_ptr<SfxItemSet> pSet(rListAutoFormat.GetStyleHandle()); + + // TODO remove this fallback (for WW8/RTF) + bool isDOCX = pIDSA->get(DocumentSettingId::ADD_VERTICAL_FLY_OFFSETS); + if (!isDOCX && !pSet) + { + TextFrameIndex const nTextLen(rInf.GetTextFrame()->GetText().getLength()); + SwTextNode const* pNode(nullptr); + sw::MergedAttrIterReverse iter(*rInf.GetTextFrame()); + for (SwTextAttr const* pHint = iter.PrevAttr(&pNode); pHint; + pHint = iter.PrevAttr(&pNode)) + { + TextFrameIndex const nHintEnd( + rInf.GetTextFrame()->MapModelToView(pNode, pHint->GetAnyEnd())); + if (nHintEnd < nTextLen) + { + break; // only those at para end are interesting + } + // Formatting for the paragraph mark is usually set to apply only to the + // (non-existent) extra character at end of the text node, but there can be + // other hints too (ending at nTextLen), so look for all matching hints. + // Still the (non-existent) extra character at the end is preferred if it exists. + if (pHint->Which() == RES_TXTATR_AUTOFMT) + { + pSet = pHint->GetAutoFormat().GetStyleHandle(); + // When we find an empty hint (start == end) we got what we are looking for. + if (pHint->GetStart() == *pHint->End()) + break; + } + } + } + + // Check each item and in case it should be ignored, then clear it. + if (!pSet) + return; + + std::unique_ptr<SfxItemSet> const pCleanedSet = pSet->Clone(); + + if (pCleanedSet->HasItem(RES_TXTATR_CHARFMT)) + { + // Insert attributes of referenced char format into current set + const SwFormatCharFormat& rCharFormat = pCleanedSet->Get(RES_TXTATR_CHARFMT); + const SwAttrSet& rStyleAttrs = static_cast<const SwCharFormat *>(rCharFormat.GetRegisteredIn())->GetAttrSet(); + SfxWhichIter aIter(rStyleAttrs); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + if (!SwTextNode::IsIgnoredCharFormatForNumbering(nWhich, /*bIsCharStyle=*/true) + && !pCleanedSet->HasItem(nWhich) + && !(pFormat && pFormat->HasItem(nWhich)) ) + { + // Copy from parent sets only allowed items which will not overwrite + // values explicitly defined in current set (pCleanedSet) or in pFormat + if (const SfxPoolItem* pItem = rStyleAttrs.GetItem(nWhich, true)) + pCleanedSet->Put(*pItem); + } + nWhich = aIter.NextWhich(); + } + + // It is not required here anymore, all referenced items are inserted + pCleanedSet->ClearItem(RES_TXTATR_CHARFMT); + }; + + SfxItemIter aIter(*pSet); + const SfxPoolItem* pItem = aIter.GetCurItem(); + while (pItem) + { + if (SwTextNode::IsIgnoredCharFormatForNumbering(pItem->Which())) + pCleanedSet->ClearItem(pItem->Which()); + else if (pFormat && pFormat->HasItem(pItem->Which())) + pCleanedSet->ClearItem(pItem->Which()); + else if (pItem->Which() == RES_CHRATR_BACKGROUND) + { + bool bShadingWasImported = false; + // If Shading was imported, it should not be converted to a Highlight, + // but remain as Shading which is ignored for numbering. + if (pCleanedSet->HasItem(RES_CHRATR_GRABBAG)) + { + SfxGrabBagItem aGrabBag = pCleanedSet->Get(RES_CHRATR_GRABBAG, /*bSrchInParent=*/false); + std::map<OUString, css::uno::Any>& rMap = aGrabBag.GetGrabBag(); + auto aIterator = rMap.find("CharShadingMarker"); + if (aIterator != rMap.end()) + aIterator->second >>= bShadingWasImported; + } + + // If used, BACKGROUND is converted to HIGHLIGHT. So also ignore if a highlight already exists. + if (bShadingWasImported + || pCleanedSet->HasItem(RES_CHRATR_HIGHLIGHT) + || (pFormat && pFormat->HasItem(RES_CHRATR_HIGHLIGHT))) + { + pCleanedSet->ClearItem(pItem->Which()); + } + } + pItem = aIter.NextItem(); + }; + + // SetDiffFnt resets the background color (why?), so capture it and re-apply if it had a value, + // because an existing value should override anything inherited from the paragraph marker. + const std::optional<Color> oFontBackColor = pNumFnt->GetBackColor(); + // The same is true for the highlight color. + const Color aHighlight = pNumFnt->GetHighlightColor(); + + pNumFnt->SetDiffFnt(pCleanedSet.get(), pIDSA); + + if (oFontBackColor) + pNumFnt->SetBackColor(oFontBackColor); + if (aHighlight != COL_TRANSPARENT) + pNumFnt->SetHighlightColor(aHighlight); +} + +static const SwRangeRedline* lcl_GetRedlineAtNodeInsertionOrDeletion( const SwTextNode& rTextNode, + bool& bIsMoved ) +{ + const SwDoc& rDoc = rTextNode.GetDoc(); + SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( rTextNode, RedlineType::Any ); + + if( SwRedlineTable::npos != nRedlPos ) + { + const SwNodeOffset nNdIdx = rTextNode.GetIndex(); + const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); + for( ; nRedlPos < rTable.size() ; ++nRedlPos ) + { + const SwRangeRedline* pTmp = rTable[ nRedlPos ]; + if( RedlineType::Delete == pTmp->GetType() || + RedlineType::Insert == pTmp->GetType() ) + { + const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End(); + if( pRStt->nNode <= nNdIdx && pREnd->nNode > nNdIdx ) + { + bIsMoved = pTmp->IsMoved(); + return pTmp; + } + } + } + } + return nullptr; +} + +static bool lcl_setRedlineAttr( SwTextFormatInfo &rInf, const SwTextNode& rTextNode, const std::unique_ptr<SwFont>& pNumFnt ) +{ + if ( rInf.GetVsh()->GetLayout()->IsHideRedlines() ) + return false; + + bool bIsMoved; + const SwRangeRedline* pRedlineNum = lcl_GetRedlineAtNodeInsertionOrDeletion( rTextNode, bIsMoved ); + if (!pRedlineNum) + return false; + + // moved text: dark green with double underline or strikethrough + if ( bIsMoved ) + { + pNumFnt->SetColor(COL_GREEN); + if ( RedlineType::Delete == pRedlineNum->GetType() ) + pNumFnt->SetStrikeout(STRIKEOUT_DOUBLE); + else + pNumFnt->SetUnderline(LINESTYLE_DOUBLE); + return true; + } + + SwAttrPool& rPool = rInf.GetVsh()->GetDoc()->GetAttrPool(); + SfxItemSetFixed<RES_CHRATR_BEGIN, RES_CHRATR_END-1> aSet(rPool); + + std::size_t aAuthor = (1 < pRedlineNum->GetStackCount()) + ? pRedlineNum->GetAuthor( 1 ) + : pRedlineNum->GetAuthor(); + + if ( RedlineType::Delete == pRedlineNum->GetType() ) + SW_MOD()->GetDeletedAuthorAttr(aAuthor, aSet); + else + SW_MOD()->GetInsertAuthorAttr(aAuthor, aSet); + + if (const SvxColorItem* pItem = aSet.GetItemIfSet(RES_CHRATR_COLOR)) + pNumFnt->SetColor(pItem->GetValue()); + if (const SvxUnderlineItem* pItem = aSet.GetItemIfSet(RES_CHRATR_UNDERLINE)) + pNumFnt->SetUnderline(pItem->GetLineStyle()); + if (const SvxCrossedOutItem* pItem = aSet.GetItemIfSet(RES_CHRATR_CROSSEDOUT)) + pNumFnt->SetStrikeout( pItem->GetStrikeout() ); + + return true; +} + +SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) const +{ + if( rInf.IsNumDone() || rInf.GetTextStart() != m_nStart + || rInf.GetTextStart() != rInf.GetIdx() ) + return nullptr; + + SwNumberPortion *pRet = nullptr; + // sw_redlinehide: at this point it's certain that pTextNd is the node with + // the numbering of the frame; only the actual number-vector (GetNumString) + // depends on the hide-mode in the layout so other calls don't need to care + const SwTextNode *const pTextNd = GetTextFrame()->GetTextNodeForParaProps(); + const SwNumRule* pNumRule = pTextNd->GetNumRule(); + + // Has a "valid" number? + // sw_redlinehide: check that pParaPropsNode is the correct one + assert(pTextNd->IsNumbered(m_pFrame->getRootFrame()) == pTextNd->IsNumbered(nullptr)); + if (pTextNd->IsNumbered(m_pFrame->getRootFrame()) && pTextNd->IsCountedInList()) + { + int nLevel = pTextNd->GetActualListLevel(); + + if (nLevel < 0) + nLevel = 0; + + if (nLevel >= MAXLEVEL) + nLevel = MAXLEVEL - 1; + + const SwNumFormat &rNumFormat = pNumRule->Get( nLevel ); + const bool bLeft = SvxAdjust::Left == rNumFormat.GetNumAdjust(); + const bool bCenter = SvxAdjust::Center == rNumFormat.GetNumAdjust(); + const bool bLabelAlignmentPosAndSpaceModeActive( + rNumFormat.GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT ); + const sal_uInt16 nMinDist = bLabelAlignmentPosAndSpaceModeActive + ? 0 : rNumFormat.GetCharTextDistance(); + + if( SVX_NUM_BITMAP == rNumFormat.GetNumberingType() ) + { + OUString referer; + if (auto const sh1 = rInf.GetVsh()) { + if (auto const doc = sh1->GetDoc()) { + auto const sh2 = doc->GetPersist(); + if (sh2 != nullptr && sh2->HasName()) { + referer = sh2->GetMedium()->GetName(); + } + } + } + pRet = new SwGrfNumPortion( pTextNd->GetLabelFollowedBy(), + rNumFormat.GetBrush(), referer, + rNumFormat.GetGraphicOrientation(), + rNumFormat.GetGraphicSize(), + bLeft, bCenter, nMinDist, + bLabelAlignmentPosAndSpaceModeActive ); + tools::Long nTmpA = rInf.GetLast()->GetAscent(); + tools::Long nTmpD = rInf.GetLast()->Height() - nTmpA; + if( !rInf.IsTest() ) + static_cast<SwGrfNumPortion*>(pRet)->SetBase( nTmpA, nTmpD, nTmpA, nTmpD ); + } + else + { + // The SwFont is created dynamically and passed in the ctor, + // as the CharFormat only returns an SV-Font. + // In the dtor of SwNumberPortion, the SwFont is deleted. + const SwAttrSet* pFormat = rNumFormat.GetCharFormat() ? + &rNumFormat.GetCharFormat()->GetAttrSet() : + nullptr; + const IDocumentSettingAccess* pIDSA = pTextNd->getIDocumentSettingAccess(); + + if( SVX_NUM_CHAR_SPECIAL == rNumFormat.GetNumberingType() ) + { + const std::optional<vcl::Font> pFormatFnt = rNumFormat.GetBulletFont(); + + // Build a new bullet font basing on the current paragraph font: + std::unique_ptr<SwFont> pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA )); + + // #i53199# + if ( !pIDSA->get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) ) + { + // i18463: + // Underline style of paragraph font should not be considered + // Overline style of paragraph font should not be considered + // Weight style of paragraph font should not be considered + // Posture style of paragraph font should not be considered + pNumFnt->SetUnderline( LINESTYLE_NONE ); + pNumFnt->SetOverline( LINESTYLE_NONE ); + pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::Latin ); + pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CJK ); + pNumFnt->SetItalic( ITALIC_NONE, SwFontScript::CTL ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::Latin ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CJK ); + pNumFnt->SetWeight( WEIGHT_NORMAL, SwFontScript::CTL ); + } + + // Apply the explicit attributes from the character style + // associated with the numbering to the new bullet font. + if( pFormat ) + pNumFnt->SetDiffFnt( pFormat, pIDSA ); + + checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat); + + if ( pFormatFnt ) + { + const SwFontScript nAct = pNumFnt->GetActual(); + pNumFnt->SetFamily( pFormatFnt->GetFamilyType(), nAct ); + pNumFnt->SetName( pFormatFnt->GetFamilyName(), nAct ); + pNumFnt->SetStyleName( pFormatFnt->GetStyleName(), nAct ); + pNumFnt->SetCharSet( pFormatFnt->GetCharSet(), nAct ); + pNumFnt->SetPitch( pFormatFnt->GetPitch(), nAct ); + } + + // we do not allow a vertical font + pNumFnt->SetVertical( pNumFnt->GetOrientation(), + m_pFrame->IsVertical() ); + + lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ); + + // --> OD 2008-01-23 #newlistelevelattrs# + if (rNumFormat.GetBulletChar()) + { + pRet = new SwBulletPortion(rNumFormat.GetBulletChar(), + pTextNd->GetLabelFollowedBy(), + std::move(pNumFnt), + bLeft, bCenter, nMinDist, + bLabelAlignmentPosAndSpaceModeActive); + } + } + else + { + // Show Changes mode shows the actual numbering (SwListRedlineType::HIDDEN) and + // the original one (SwListRedlineType::ORIGTEXT) instead of the fake numbering + // (SwListRedlineType::SHOW, which counts removed and inserted numbered paragraphs + // in a single list) + bool bHasHiddenNum = false; + OUString aText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::HIDDEN) ); + const SwDoc& rDoc = pTextNd->GetDoc(); + const SwRedlineTable& rTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); + if ( rTable.size() && !rInf.GetVsh()->GetLayout()->IsHideRedlines() ) + { + OUString aHiddenText( pTextNd->GetNumString(true, MAXLEVEL, m_pFrame->getRootFrame(), SwListRedlineType::ORIGTEXT) ); + + if ( !aText.isEmpty() || !aHiddenText.isEmpty() ) + { + if (aText != aHiddenText && !aHiddenText.isEmpty()) + { + bHasHiddenNum = true; + // show also original number after the actual one enclosed in [ and ], + // and replace tabulator with space to avoid messy indentation + // resulted by the longer numbering, e.g. "1.[2.]" instead of "1.". + aText = aText + "[" + aHiddenText + "]" + + pTextNd->GetLabelFollowedBy().replaceAll("\t", " "); + } + else if (!aText.isEmpty()) + aText += pTextNd->GetLabelFollowedBy(); + } + } + else if (pTextNd->getIDocumentSettingAccess()->get(DocumentSettingId::NO_NUMBERING_SHOW_FOLLOWBY) + || !aText.isEmpty()) + aText += pTextNd->GetLabelFollowedBy(); + + // Not just an optimization ... + // A number portion without text will be assigned a width of 0. + // The succeeding text portion will flow into the BreakCut in the BreakLine, + // although we have rInf.GetLast()->GetFlyPortion()! + if( !aText.isEmpty() ) + { + + // Build a new numbering font basing on the current paragraph font: + std::unique_ptr<SwFont> pNumFnt(new SwFont( &rInf.GetCharAttr(), pIDSA )); + + const SwTextNode& rTextNode = *rInf.GetTextFrame()->GetTextNodeForParaProps(); + if (const SwpHints* pHints = rTextNode.GetpSwpHints()) + { + // Also look for an empty character hint that sits at the paragraph end: + for (size_t i = 0; i < pHints->Count(); ++i) + { + const SwTextAttr* pHint = pHints->GetSortedByEnd(i); + if (pHint->Which() == RES_TXTATR_AUTOFMT && pHint->GetEnd() + && pHint->GetStart() == *pHint->GetEnd() + && pHint->GetStart() == rTextNode.GetText().getLength()) + { + std::shared_ptr<SfxItemSet> pSet + = pHint->GetAutoFormat().GetStyleHandle(); + if (pSet) + { + pNumFnt->SetDiffFnt(pSet.get(), pIDSA); + break; + } + } + } + } + + // #i53199# + if ( !pIDSA->get(DocumentSettingId::DO_NOT_RESET_PARA_ATTRS_FOR_NUM_FONT) ) + { + // i18463: + // Underline style of paragraph font should not be considered + pNumFnt->SetUnderline( LINESTYLE_NONE ); + // Overline style of paragraph font should not be considered + pNumFnt->SetOverline( LINESTYLE_NONE ); + } + + // Apply the explicit attributes from the character style + // associated with the numbering to the new bullet font. + if( pFormat ) + pNumFnt->SetDiffFnt( pFormat, pIDSA ); + + checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat); + + if ( !lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ) && bHasHiddenNum ) + pNumFnt->SetColor(NON_PRINTING_CHARACTER_COLOR); + + // we do not allow a vertical font + pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() ); + + pRet = new SwNumberPortion( aText, std::move(pNumFnt), + bLeft, bCenter, nMinDist, + bLabelAlignmentPosAndSpaceModeActive ); + } + } + } + } + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |