/* -*- 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 <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 <txtatr.hxx>
#include <swfont.hxx>
#include <fntcache.hxx>
#include "porfld.hxx"
#include "porftn.hxx"
#include "porref.hxx"
#include "portox.hxx"
#include "porhyph.hxx"
#include "porfly.hxx"
#include "itrform2.hxx"
#include <chpfld.hxx>
#include <dbfld.hxx>
#include <expfld.hxx>
#include <docufld.hxx>
#include <pagedesc.hxx>
#include "pormulti.hxx"
#include <fmtmeta.hxx>
#include <reffld.hxx>
#include <flddat.hxx>
#include <fmtautofmt.hxx>
#include <IDocumentSettingAccess.hxx>
#include <svl/itemiter.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) );
}
break;
case SwFieldIds::HiddenText:
{
OUString const aStr( bName
? pField->GetFieldName()
: pField->ExpandField(bInClipboard) );
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) );
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) );
pRet = new SwFieldPortion( aStr );
}
static_cast<SwFieldPortion*>(pRet)->m_nAttrFieldType= ATTR_PAGECOOUNTFLD;
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) );
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) );
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) );
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) );
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) );
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) );
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) );
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) );
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)
{
pField->GetPrefixAndSuffix(bPrefix ? &fix : nullptr, bPrefix ? nullptr : &fix);
}
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_nHintEndIndex 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_nHintEndIndex 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_nHintEndIndex exists for the whole text frame, so
// it's necessary to iterate all hints for that purpose...
SwTextNode const* pNode(nullptr);
sw::MergedAttrIterByEnd iter(*rInfo.GetTextFrame());
size_t i(0);
for (SwTextAttr const* pHint = iter.NextAttr(&pNode); pHint;
pHint = iter.NextAttr(&pNode))
{
if (i++ < m_nHintEndIndex)
{
continue; // skip ones that were handled earlier
}
SwTextAttr & rHint(const_cast<SwTextAttr&>(*pHint));
TextFrameIndex const nEnd(
rInfo.GetTextFrame()->MapModelToView(pNode, *rHint.GetAnyEnd()));
if (nEnd > nIdx)
{
break;
}
++m_nHintEndIndex;
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 )
{
const OUString aNothing;
pRet = new SwFieldPortion( aNothing );
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 )
{
if( !pIDSA->get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING ))
return;
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 nHintStart(
rInf.GetTextFrame()->MapModelToView(pNode, pHint->GetStart()));
if (nHintStart < nTextLen)
{
break; // only those at para end are interesting
}
// Formatting for the paragraph mark is set to apply only to the
// (non-existent) extra character at end of the text node.
if (pHint->Which() == RES_TXTATR_AUTOFMT
&& pHint->GetStart() == *pHint->End())
{
std::shared_ptr<SfxItemSet> pSet(pHint->GetAutoFormat().GetStyleHandle());
// Check each item and in case it should be ignored, then clear it.
std::unique_ptr<SfxItemSet> pCleanedSet;
if (pSet.get())
{
pCleanedSet = pSet->Clone();
SfxItemIter aIter(*pSet);
const SfxPoolItem* pItem = aIter.GetCurItem();
while (true)
{
if (SwTextNode::IsIgnoredCharFormatForNumbering(pItem->Which()))
pCleanedSet->ClearItem(pItem->Which());
if (aIter.IsAtEnd())
break;
pItem = aIter.NextItem();
}
}
// Highlightcolor also needed to be untouched, but we can't have that just by clearing the item
Color nSaveHighlight = pNumFnt->GetHighlightColor();
pNumFnt->SetDiffFnt(pCleanedSet.get(), pIDSA);
pNumFnt->SetHighlightColor(nSaveHighlight);
}
}
}
SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) const
{
if( rInf.IsNumDone() || rInf.GetTextStart() != m_nStart
|| rInf.GetTextStart() != rInf.GetIdx() )
return nullptr;
SwNumberPortion *pRet = nullptr;
const SwTextNode *const pTextNd = GetTextFrame()->GetTextNodeForParaProps();
const SwNumRule* pNumRule = pTextNd->GetNumRule();
// Has a "valid" number?
if( pTextNd->IsNumbered() && 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() )
{
pRet = new SwGrfNumPortion( pTextNd->GetLabelFollowedBy(),
rNumFormat.GetBrush(),
rNumFormat.GetGraphicOrientation(),
rNumFormat.GetGraphicSize(),
bLeft, bCenter, nMinDist,
bLabelAlignmentPosAndSpaceModeActive );
long nTmpA = rInf.GetLast()->GetAscent();
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 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 );
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() );
// --> OD 2008-01-23 #newlistelevelattrs#
pRet = new SwBulletPortion( rNumFormat.GetBulletChar(),
pTextNd->GetLabelFollowedBy(),
std::move(pNumFnt),
bLeft, bCenter, nMinDist,
bLabelAlignmentPosAndSpaceModeActive );
}
else
{
OUString aText( pTextNd->GetNumString() );
if ( !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 ));
// #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 );
// 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: */
↑ V773 The 'pRet' pointer was assigned values twice without releasing the memory. A memory leak is possible.