/* -*- 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 .
*/
void SwAttrIter::Chg( SwTextAttr const *pHt )
{
assert(pHt && m_pFont && "No attribute of font available for change"); if( m_pRedline && m_pRedline->IsOn() )
m_pRedline->ChangeTextAttr( m_pFont, *pHt, true ); else
m_aAttrHandler.PushAndChg( *pHt, *m_pFont );
m_nChgCnt++;
}
void SwAttrIter::Rst( SwTextAttr const *pHt )
{
assert(pHt && m_pFont && "No attribute of font available for reset"); // get top from stack after removing pHt if( m_pRedline && m_pRedline->IsOn() )
m_pRedline->ChangeTextAttr( m_pFont, *pHt, false ); else
m_aAttrHandler.PopAndChg( *pHt, *m_pFont );
m_nChgCnt--;
}
/** * Returns the attribute for a position * * Only if the attribute is exactly at the position @param nPos and * does not have an EndIndex * * We need this function for attributes which should alter formatting without * changing the content of the string. * Such "degenerated" attributes are e.g.: fields which retain expanded text and * line-bound Frames. * In order to avoid ambiguities between different such attributes, we insert a * special character at the start of the string, when creating such an attribute. * The Formatter later on encounters such a special character and retrieves the * degenerate attribute via GetAttr().
*/
SwTextAttr *SwAttrIter::GetAttr(TextFrameIndex const nPosition) const
{
std::pair<SwTextNode const*, sal_Int32> const pos( m_pMergedPara
? sw::MapViewToModel(*m_pMergedPara, nPosition)
: std::make_pair(m_pTextNode, sal_Int32(nPosition))); return pos.first->GetTextAttrForCharAt(pos.second);
}
bool SwAttrIter::SeekAndChgAttrIter(TextFrameIndex const nNewPos, OutputDevice* pOut)
{
std::pair<SwTextNode const*, sal_Int32> const pos{SeekNewPos(nNewPos, nullptr)}; bool bChg = m_nStartIndex && pos.first == m_pTextNode && pos.second == m_nPosition
? m_pFont->IsFntChg()
: Seek( nNewPos ); if ( m_pLastOut.get() != pOut )
{
m_pLastOut = pOut;
m_pFont->SetFntChg( true );
bChg = true;
} if( bChg )
{ // if the change counter is zero, we know the cache id of the wanted font if ( !m_nChgCnt && !m_nPropFont )
m_pFont->SetFontCacheId( m_aFontCacheIds[ m_pFont->GetActual() ],
m_aFontIdx[ m_pFont->GetActual() ], m_pFont->GetActual() );
m_pFont->ChgPhysFnt( m_pViewShell, *pOut );
}
SwpHints const*const pHints(m_pTextNode->GetpSwpHints()); if (pHints && !bParaFont)
{
SwTextAttr *pTextAttr; // While we've not reached the end of the StartArray && the TextAttribute starts at position 0... while ((m_nStartIndex < pHints->Count()) &&
!((pTextAttr = pHints->Get(m_nStartIndex))->GetStart()))
{ // open the TextAttributes
Chg( pTextAttr );
m_nStartIndex++;
}
}
bool bChg = m_pFont->IsFntChg(); if ( m_pLastOut.get() != pOut )
{
m_pLastOut = pOut;
m_pFont->SetFntChg( true );
bChg = true;
} if( bChg )
{ // if the application counter is zero, we know the cache id of the wanted font if ( !m_nChgCnt && !m_nPropFont )
m_pFont->SetFontCacheId( m_aFontCacheIds[ m_pFont->GetActual() ],
m_aFontIdx[ m_pFont->GetActual() ], m_pFont->GetActual() );
m_pFont->ChgPhysFnt( m_pViewShell, *pOut );
} return bChg;
}
if ( m_nStartIndex ) // If attributes have been opened at all ...
{ // Close attributes that are currently open, but stop at nNewPos+1
// As long as we've not yet reached the end of EndArray and the // TextAttribute ends before or at the new position ... while ((m_nEndIndex < nHintsCount) &&
((pTextAttr = pHints->GetSortedByEnd(m_nEndIndex))->GetAnyEnd() <= nNewPos))
{ // Close the TextAttributes, whose StartPos were before or at // the old nPos and are currently open if (pTextAttr->GetStart() <= nOldPos) Rst( pTextAttr );
m_nEndIndex++;
}
} else// skip the not opened ends
{ while ((m_nEndIndex < nHintsCount) &&
(pHints->GetSortedByEnd(m_nEndIndex)->GetAnyEnd() <= nNewPos))
{
m_nEndIndex++;
}
}
// As long as we've not yet reached the end of EndArray and the // TextAttribute ends before or at the new position... while ((m_nStartIndex < nHintsCount) &&
((pTextAttr = pHints->Get(m_nStartIndex))->GetStart() <= nNewPos))
{
// open the TextAttributes, whose ends lie behind the new position if ( pTextAttr->GetAnyEnd() > nNewPos ) Chg( pTextAttr );
m_nStartIndex++;
}
}
void SwAttrIter::SeekToEnd()
{ if (m_pTextNode->GetDoc().getIDocumentSettingAccess().get(
DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
{
SfxItemPool & rPool{const_cast<SwAttrPool&>(m_pTextNode->GetDoc().GetAttrPool())};
SwFormatAutoFormat const& rListAutoFormat{m_pTextNode->GetAttr(RES_PARATR_LIST_AUTOFMT)};
std::shared_ptr<SfxItemSet> const pSet{rListAutoFormat.GetStyleHandle()}; if (!pSet)
{ return;
} if (pSet->HasItem(RES_TXTATR_CHARFMT))
{
SwFormatCharFormat const& rCharFormat{pSet->Get(RES_TXTATR_CHARFMT)};
m_pEndCharFormatAttr.reset(new SwTextAttrEnd{
SfxPoolItemHolder{rPool, &rCharFormat}, -1, -1});
Chg(m_pEndCharFormatAttr.get());
} // note: RES_TXTATR_CHARFMT should be cleared here but it looks like // SwAttrHandler only looks at RES_CHRATR_* anyway
m_pEndAutoFormatAttr.reset(new SwTextAttrEnd{
SfxPoolItemHolder{rPool, &rListAutoFormat}, -1, -1});
Chg(m_pEndAutoFormatAttr.get());
}
}
bool isToEnd{false}; if (m_pMergedPara)
{ if (m_pMergedPara->extents.empty())
{
isToEnd = true;
assert(m_pMergedPara->pLastNode == newPos.first);
} else
{ autoconst& rLast{m_pMergedPara->extents.back()};
isToEnd = rLast.pNode == newPos.first && rLast.nEnd == newPos.second; // for text formatting: use *last* node if all text is hidden if (isToEnd
&& m_pMergedPara->pLastNode != newPos.first // implies there is hidden text
&& m_pViewShell->GetLayout()->GetParagraphBreakMode() == sw::ParagraphBreakMode::Hidden
&& m_pTextNode->GetDoc().getIDocumentSettingAccess().get(
DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_EMPTY_LINE_AT_END_OF_PARAGRAPH))
{
TextFrameIndex nHiddenStart(COMPLETE_STRING);
TextFrameIndex nHiddenEnd(0);
m_pScriptInfo->GetBoundsOfHiddenRange(TextFrameIndex(0), nHiddenStart, nHiddenEnd); if (TextFrameIndex(0) == nHiddenStart
&& TextFrameIndex(m_pMergedPara->mergedText.getLength()) <= nHiddenEnd)
{
newPos.first = m_pMergedPara->pLastNode;
newPos.second = m_pMergedPara->pLastNode->Len();
}
}
}
} else
{
isToEnd = newPos.second == m_pTextNode->Len();
} if (o_pIsToEnd)
{
*o_pIsToEnd = isToEnd;
}
return newPos;
}
bool SwAttrIter::Seek(TextFrameIndex const nNewPos)
{ // note: nNewPos isn't necessarily an index returned from GetNextAttr bool isToEnd{false};
std::pair<SwTextNode const*, sal_Int32> const newPos{SeekNewPos(nNewPos, &isToEnd)};
if ( m_pRedline && m_pRedline->ExtOn() )
m_pRedline->LeaveExtend(*m_pFont, newPos.first->GetIndex(), newPos.second); if (m_pTextNode->GetIndex() < newPos.first->GetIndex())
{ // Skipping to a different node - first seek until the end of this node // to get rid of all hint items if (m_pTextNode->GetpSwpHints())
{
sal_Int32 nPos(m_nPosition); do
{
sal_Int32 const nOldPos(nPos);
nPos = GetNextAttrImpl(m_pTextNode, m_nStartIndex, m_nEndIndex, nPos); if (nPos <= m_pTextNode->Len())
{
SeekFwd(nOldPos, nPos);
} else
{
SeekFwd(nOldPos, m_pTextNode->Len());
}
} while (nPos < m_pTextNode->Len());
} // Unapply current para items: // the SwAttrHandler doesn't appear to be capable of *unapplying* // items at all; it can only apply a previously effective item. // So do this by recreating the font from scratch. // Apply new para items:
assert(m_pMergedPara);
InitFontAndAttrHandler(*m_pMergedPara->pParaPropsNode, *newPos.first,
m_pMergedPara->mergedText, nullptr, nullptr); // reset to next
m_pTextNode = newPos.first;
m_nStartIndex = 0;
m_nEndIndex = 0;
m_nPosition = 0;
assert(m_pRedline);
}
// sw_redlinehide: Seek(0) must move before the first character, which // has a special case where the first node starts with delete redline. if ((!nNewPos && !m_pMergedPara)
|| newPos.first != m_pTextNode
|| newPos.second < m_nPosition)
{ if (m_pMergedPara)
{ if (m_pTextNode != newPos.first)
{
m_pTextNode = newPos.first; // sw_redlinehide: hope it's okay to use the current text node // here; the AttrHandler shouldn't care about non-char items
InitFontAndAttrHandler(*m_pMergedPara->pParaPropsNode, *m_pTextNode,
m_pMergedPara->mergedText, nullptr, nullptr);
}
} // also reset it if the RES_PARATR_LIST_AUTOFMT has been applied! if (m_pMergedPara || m_pTextNode->GetpSwpHints() || m_pEndAutoFormatAttr)
{ if( m_pRedline )
m_pRedline->Clear( nullptr );
// reset font to its original state
m_aAttrHandler.Reset();
m_aAttrHandler.ResetFont( *m_pFont );
m_pEndCharFormatAttr.reset();
m_pEndAutoFormatAttr.reset();
// Attention! // resetting the font here makes it necessary to apply any // changes for extended input directly to the font if ( m_pRedline && m_pRedline->ExtOn() )
{
m_pRedline->UpdateExtFont( *m_pFont );
++m_nChgCnt;
}
}
}
if (m_pTextNode->GetpSwpHints())
{ if (m_pMergedPara)
{ // iterate hint by hint: SeekFwd does not mix ends and starts, // it always applies all the starts last, so it must be called once // per position where hints start/end!
sal_Int32 nPos(m_nPosition); do
{
sal_Int32 const nOldPos(nPos);
nPos = GetNextAttrImpl(m_pTextNode, m_nStartIndex, m_nEndIndex, nPos); if (nPos <= newPos.second)
{
SeekFwd(nOldPos, nPos);
} else
{
SeekFwd(nOldPos, newPos.second);
}
} while (nPos < newPos.second);
} else
{
SeekFwd(m_nPosition, newPos.second);
}
}
if (isToEnd && !m_pEndAutoFormatAttr)
{
SeekToEnd();
}
// if return false: portion ends at start of redline, indexes unchanged // if return true: portion end not known (past end of redline), indexes point to first hint past end of redline staticbool CanSkipOverRedline(
SwTextNode const& rStartNode, sal_Int32 const nStartRedline,
SwRangeRedline const& rRedline,
size_t & rStartIndex, size_t & rEndIndex, boolconst isTheAnswerYes)
{
size_t nStartIndex(rStartIndex);
size_t nEndIndex(rEndIndex);
SwPosition const*const pRLEnd(rRedline.End()); if (!pRLEnd->GetNode().IsTextNode() // if fully deleted...
|| pRLEnd->GetContentIndex() == pRLEnd->GetNode().GetTextNode()->Len())
{ // shortcut: nothing follows redline // current state is end state returnfalse;
}
std::vector<SwTextAttr*> activeCharFmts; // can't compare the SwFont that's stored somewhere, it doesn't have compare // operator, so try to recreate the situation with some temp arrays here
SfxPoolItem const* activeCharAttrsStart[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, }; if (rStartNode != pRLEnd->GetNode())
{ // nodes' attributes are only needed if there are different nodes
InsertCharAttrs(activeCharAttrsStart, rStartNode.GetSwAttrSet());
} if (SwpHints const*const pStartHints = rStartNode.GetpSwpHints())
{ // check hint ends of hints that start before and end within
sal_Int32 const nRedlineEnd(rStartNode == pRLEnd->GetNode()
? pRLEnd->GetContentIndex()
: rStartNode.Len()); for ( ; nEndIndex < pStartHints->Count(); ++nEndIndex)
{
SwTextAttr *const pAttr(pStartHints->GetSortedByEnd(nEndIndex)); if (!pAttr->End())
{ continue;
} if (nRedlineEnd < *pAttr->End())
{ break;
} if (nStartRedline <= pAttr->GetStart())
{ continue;
} if (pAttr->IsFormatIgnoreEnd())
{ continue;
} switch (pAttr->Which())
{ // if any of these ends inside RL then we need a new portion case RES_TXTATR_REFMARK: case RES_TXTATR_TOXMARK: case RES_TXTATR_META: // actually these 2 aren't allowed to overlap ??? case RES_TXTATR_METAFIELD: case RES_TXTATR_INETFMT: case RES_TXTATR_CJK_RUBY: case RES_TXTATR_INPUTFIELD: case RES_TXTATR_CONTENTCONTROL:
{ if (!isTheAnswerYes) returnfalse; // always break
} break; // these are guaranteed not to overlap // and come in order of application case RES_TXTATR_AUTOFMT: case RES_TXTATR_CHARFMT:
{ if (pAttr->Which() == RES_TXTATR_CHARFMT)
{
activeCharFmts.push_back(pAttr);
} // pure formatting hints may end inside the redline & // start again inside the redline, which must not cause // a new text portion if they have the same items - so // store the effective items & compare all at the end
SfxItemSet const& rSet((pAttr->Which() == RES_TXTATR_CHARFMT)
? static_cast<SfxItemSet const&>(pAttr->GetCharFormat().GetCharFormat()->GetAttrSet())
: *pAttr->GetAutoFormat().GetStyleHandle());
InsertCharAttrs(activeCharAttrsStart, rSet);
} break; // SwTextNode::SetAttr puts it into AUTOFMT which is quite // sensible so it doesn't actually exist as a hint case RES_TXTATR_UNKNOWN_CONTAINER: default: assert(false);
}
}
assert(nEndIndex == pStartHints->Count() ||
pRLEnd->GetContentIndex() < pStartHints->GetSortedByEnd(nEndIndex)->GetAnyEnd());
}
// treat para properties as text properties // ... with the FormatToTextAttr we get autofmts that correspond to the *effective* attr set difference // effective attr set: para props + charfmts + autofmt *in that order* // ... and the charfmt must be *nominally* the same
SfxPoolItem const* activeCharAttrsEnd[RES_CHRATR_END - RES_CHRATR_BEGIN + 1] = { nullptr, }; if (rStartNode != pRLEnd->GetNode())
{ // nodes' attributes are only needed if there are different nodes
InsertCharAttrs(activeCharAttrsEnd,
pRLEnd->GetNode().GetTextNode()->GetSwAttrSet());
}
if (SwpHints *const pEndHints = pRLEnd->GetNode().GetTextNode()->GetpSwpHints())
{ // check hint starts of hints that start within and end after #ifndef NDEBUG
sal_Int32 const nRedlineStart(rStartNode == pRLEnd->GetNode()
? nStartRedline
: 0); #endif for ( ; nStartIndex < pEndHints->Count(); ++nStartIndex)
{
SwTextAttr *const pAttr(pEndHints->Get(nStartIndex)); // compare with < here, not <=, to get the effective formatting // of the 1st char after the redline; should not cause problems // with consecutive delete redlines because those are handed by // GetNextRedln() and here we have the last end pos. if (pRLEnd->GetContentIndex() < pAttr->GetStart())
{ break;
} if (!pAttr->End()) continue; if (pAttr->IsFormatIgnoreStart())
{ continue;
}
assert(nRedlineStart <= pAttr->GetStart()); // we wouldn't be here otherwise? if (*pAttr->End() <= pRLEnd->GetContentIndex())
{ continue;
} switch (pAttr->Which())
{ case RES_TXTATR_REFMARK: case RES_TXTATR_TOXMARK: case RES_TXTATR_META: // actually these 2 aren't allowed to overlap ??? case RES_TXTATR_METAFIELD: case RES_TXTATR_INETFMT: case RES_TXTATR_CJK_RUBY: case RES_TXTATR_INPUTFIELD: case RES_TXTATR_CONTENTCONTROL:
{ if (!isTheAnswerYes) returnfalse;
} break; case RES_TXTATR_AUTOFMT: case RES_TXTATR_CHARFMT:
{ // char formats must be *nominally* the same if (pAttr->Which() == RES_TXTATR_CHARFMT)
{ auto iter = std::find_if(activeCharFmts.begin(), activeCharFmts.end(),
[&pAttr](const SwTextAttr* pCharFmt) { return *pCharFmt == *pAttr; }); if (iter != activeCharFmts.end())
activeCharFmts.erase(iter); elseif (!isTheAnswerYes) returnfalse;
}
SfxItemSet const& rSet((pAttr->Which() == RES_TXTATR_CHARFMT)
? static_cast<SfxItemSet const&>(pAttr->GetCharFormat().GetCharFormat()->GetAttrSet())
: *pAttr->GetAutoFormat().GetStyleHandle());
InsertCharAttrs(activeCharAttrsEnd, rSet);
} break; // SwTextNode::SetAttr puts it into AUTOFMT which is quite // sensible so it doesn't actually exist as a hint case RES_TXTATR_UNKNOWN_CONTAINER: default: assert(false);
}
} if (rStartNode != pRLEnd->GetNode())
{ // need to iterate the nEndIndex forward too so the loop in the // caller can look for the right ends in the next iteration for (nEndIndex = 0; nEndIndex < pEndHints->Count(); ++nEndIndex)
{
SwTextAttr *const pAttr(pEndHints->GetSortedByEnd(nEndIndex)); if (!pAttr->End()) continue; if (pRLEnd->GetContentIndex() < *pAttr->End())
{ break;
}
}
}
}
// if we didn't find a matching start for any end, then it really ends inside if (!activeCharFmts.empty())
{ if (!isTheAnswerYes) returnfalse;
} for (size_t i = 0; i < SAL_N_ELEMENTS(activeCharAttrsStart); ++i)
{ // all of these should be shareable (but we have no SfxItemPool to check it here) // assert(!activeCharAttrsStart[i] || activeCharAttrsStart[i]->GetItemPool()->Shareable(*activeCharAttrsStart[i])); if (!SfxPoolItem::areSame(activeCharAttrsStart[i], activeCharAttrsEnd[i]))
{ if (!isTheAnswerYes) returnfalse;
}
}
rStartIndex = nStartIndex;
rEndIndex = nEndIndex; returntrue;
}
static sal_Int32 GetNextAttrImpl(SwTextNode const*const pTextNode,
size_t const nStartIndex, size_t const nEndIndex,
sal_Int32 const nPosition)
{ // note: this used to be COMPLETE_STRING, but was set to Len() + 1 below, // which is rather silly, so set it to Len() instead
sal_Int32 nNext = pTextNode->Len(); if (SwpHints const*const pHints = pTextNode->GetpSwpHints())
{ // are there attribute starts left? for (size_t i = nStartIndex; i < pHints->Count(); ++i)
{
SwTextAttr *const pAttr(pHints->Get(i)); if (!pAttr->IsFormatIgnoreStart())
{
nNext = pAttr->GetStart(); break;
}
} // are there attribute ends left? for (size_t i = nEndIndex; i < pHints->Count(); ++i)
{
SwTextAttr *const pAttr(pHints->GetSortedByEnd(i)); if (!pAttr->IsFormatIgnoreEnd())
{
sal_Int32 const nNextEnd = pAttr->GetAnyEnd();
nNext = std::min(nNext, nNextEnd); // pick nearest one break;
}
}
} // TODO: maybe use hints like FieldHints for this instead of looking at the text... const sal_Int32 l = std::min(nNext, pTextNode->Len());
sal_Int32 p = nPosition; const sal_Unicode* pStr = pTextNode->GetText().getStr(); while (p < l)
{
sal_Unicode aChar = pStr[p]; switch (aChar)
{ case CH_TXT_ATR_FORMELEMENT: case CH_TXT_ATR_FIELDSTART: case CH_TXT_ATR_FIELDSEP: case CH_TXT_ATR_FIELDEND: goto break_; // sigh... default:
++p;
}
}
break_:
assert(p <= nNext); if (p < l)
{ // found a CH_TXT_ATR_FIELD*: if it's same as current position, // skip behind it so that both before- and after-positions are returned
nNext = (nPosition < p) ? p : p + 1;
} return nNext;
}
for (size_t i = 0; i < pHints->Count(); ++i)
{
SwTextAttr* const pAttr(pHints->Get(i)); if (HasFormatBreakAttribute(&stTracker, pAttr))
{ if (i >= nStartIndex)
{
nNext = pAttr->GetStart(); break;
}
}
}
class SwMinMaxNodeArgs
{ public:
sal_uLong m_nMaxWidth; // sum of all frame widths
tools::Long m_nMinWidth; // biggest frame
tools::Long m_nLeftRest; // space not already covered by frames in the left margin
tools::Long m_nRightRest; // space not already covered by frames in the right margin
tools::Long m_nLeftDiff; // Min/Max-difference of the frame in the left margin
tools::Long m_nRightDiff; // Min/Max-difference of the frame in the right margin
SwNodeOffset m_nIndex; // index of the node void Minimum( tools::Long nNew ) { if (nNew > m_nMinWidth)
m_nMinWidth = nNew;
}
};
// Frames, which are left- or right-aligned are only party considered // when calculating the maximum, since the border is already being considered. // Only if the frame extends into the text body, this part is being added switch( eHoriOri )
{ case text::HoriOrientation::RIGHT:
{ if( nDiff )
{
rIn.m_nRightRest -= rIn.m_nRightDiff;
rIn.m_nRightDiff = nDiff;
} if( text::RelOrientation::FRAME != rOrient.GetRelationOrient() )
{ if (rIn.m_nRightRest > 0)
rIn.m_nRightRest = 0;
}
rIn.m_nRightRest -= nMin; break;
} case text::HoriOrientation::LEFT:
{ if( nDiff )
{
rIn.m_nLeftRest -= rIn.m_nLeftDiff;
rIn.m_nLeftDiff = nDiff;
} if (text::RelOrientation::FRAME != rOrient.GetRelationOrient() && rIn.m_nLeftRest < 0)
rIn.m_nLeftRest = 0;
rIn.m_nLeftRest -= nMin; break;
} default:
{
rIn.m_nMaxWidth += nMax;
rIn.Minimum(nMin);
}
}
}
#define FLYINCNT_MIN_WIDTH 284
/** * Changing this method very likely requires changing of GetScalingOfSelectedText * This one is called exclusively from import filters, so there is no layout.
*/ void SwTextNode::GetMinMaxSize( SwNodeOffset nIndex, sal_uLong& rMin, sal_uLong &rMax,
sal_uLong& rAbsMin ) const
{
SwViewShell const * pSh = GetDoc().getIDocumentLayoutAccess().GetCurrentViewShell();
OutputDevice* pOut = nullptr; if( pSh )
pOut = pSh->GetWin()->GetOutDev(); if( !pOut )
pOut = Application::GetDefaultDevice();
switch( pHint->Which() )
{ case RES_TXTATR_FLYCNT :
{
SwFrameFormat *pFrameFormat = pHint->GetFlyCnt().GetFrameFormat(); const SvxLRSpaceItem &rLR = pFrameFormat->GetLRSpace(); if( RES_DRAWFRMFMT == pFrameFormat->Which() )
{ const SdrObject* pSObj = pFrameFormat->FindSdrObject(); if( pSObj )
nCurrentWidth = pSObj->GetCurrentBoundRect().GetWidth(); else
nCurrentWidth = 0;
} else
{ const SwFormatFrameSize& rTmpSize = pFrameFormat->GetFrameSize(); if( RES_FLYFRMFMT == pFrameFormat->Which()
&& rTmpSize.GetWidthPercent() )
{ // This is a hack for the following situation: In the paragraph there's a // text frame with relative size. Then let's take 0.5 cm as minimum width // and USHRT_MAX as maximum width // It were cleaner and maybe necessary later on to iterate over the content // of the text frame and call GetMinMaxSize recursively
nCurrentWidth = FLYINCNT_MIN_WIDTH; // 0.5 cm
rMax = std::max(rMax, sal_uLong(USHRT_MAX));
} else
nCurrentWidth = pFrameFormat->GetFrameSize().GetWidth();
}
nCurrentWidth += rLR.ResolveLeft({});
nCurrentWidth += rLR.ResolveRight({});
aArg.m_nWordAdd = nOldWidth + nOldAdd;
aArg.m_nWordWidth = nCurrentWidth;
aArg.m_nRowWidth += nCurrentWidth; if (static_cast<tools::Long>(rAbsMin) < aArg.m_nWordWidth)
rAbsMin = aArg.m_nWordWidth;
aArg.Minimum(aArg.m_nWordWidth + aArg.m_nWordAdd); break;
} case RES_TXTATR_FTN :
{ const OUString aText = pHint->GetFootnote().GetNumStr(); if( lcl_MinMaxString( aArg, aIter.GetFnt(), aText, 0,
aText.getLength() ) )
nAdd = 20; break;
}
case RES_TXTATR_FIELD : case RES_TXTATR_ANNOTATION :
{
SwField *pField = const_cast<SwField*>(pHint->GetFormatField().GetField()); const OUString aText = pField->ExpandField(true, nullptr); if( lcl_MinMaxString( aArg, aIter.GetFnt(), aText, 0,
aText.getLength() ) )
nAdd = 20; break;
} default:
aArg.m_nWordWidth = nOldWidth;
aArg.m_nWordAdd = nOldAdd;
}
aIter.SeekAndChgAttrIter( ++nIdx, pOut );
} break; case CH_TXT_ATR_INPUTFIELDSTART: case CH_TXT_ATR_INPUTFIELDEND: case CH_TXT_ATR_FORMELEMENT: case CH_TXT_ATR_FIELDSTART: case CH_TXT_ATR_FIELDSEP: case CH_TXT_ATR_FIELDEND:
{ // just skip it and continue with the content...
aIter.SeekAndChgAttrIter( ++nIdx, pOut );
} break;
}
} if (static_cast<tools::Long>(rMax) < aArg.m_nRowWidth)
rMax = aArg.m_nRowWidth;
nLROffset += rRightMargin.ResolveRight({});
rAbsMin += nLROffset;
rAbsMin += nAdd;
rMin += nLROffset;
rMin += nAdd; if (static_cast<tools::Long>(rMin) < aNodeArgs.m_nMinWidth)
rMin = aNodeArgs.m_nMinWidth; if (static_cast<tools::Long>(rAbsMin) < aNodeArgs.m_nMinWidth)
rAbsMin = aNodeArgs.m_nMinWidth;
rMax += aNodeArgs.m_nMaxWidth;
rMax += nLROffset;
rMax += nAdd; if( rMax < rMin ) // e.g. Frames with flow through only contribute to the minimum
rMax = rMin;
pOut->SetMapMode( aOldMap );
}
/** * Calculates the width of the text part specified by nStart and nEnd, * the height of the line containing nStart is divided by this width, * indicating the scaling factor, if the text part is rotated. * Having CH_BREAKs in the text part, this method returns the scaling * factor for the longest of the text parts separated by the CH_BREAK * * Changing this method very likely requires changing of "GetMinMaxSize"
*/
sal_uInt16 SwTextFrame::GetScalingOfSelectedText(
TextFrameIndex nStart, TextFrameIndex nEnd)
{
assert(GetOffset() <= nStart && (!GetFollow() || nStart < GetFollow()->GetOffset()));
SwViewShell const*const pSh = getRootFrame()->GetCurrShell();
assert(pSh);
OutputDevice *const pOut = &pSh->GetRefDev();
assert(pOut);
// We do not want scaling attributes to be considered during this // calculation. For this, we push a temporary scaling attribute with // scaling value 100 and priority flag on top of the scaling stack
SwAttrHandler& rAH = aIter.GetAttrHandler();
SvxCharScaleWidthItem aItem(100, RES_CHRATR_SCALEW);
SwTextAttrEnd aAttr( SfxPoolItemHolder(getRootFrame()->GetCurrShell()->GetAttrPool(), &aItem), 0, COMPLETE_STRING );
aAttr.SetPriorityAttr( true );
rAH.PushAndChg( aAttr, *(aIter.GetFnt()) );
TextFrameIndex nIdx = nStart;
sal_uLong nWidth = 0;
sal_uLong nProWidth = 0;
while( nIdx < nEnd )
{
aIter.SeekAndChgAttrIter( nIdx, pOut );
// scan for end of portion
TextFrameIndex const nNextChg = std::min(aIter.GetNextAttr(), aScriptInfo.NextScriptChg(nIdx));
default:
{ // any suggestions for a default action?
}
} // end of switch
nIdx++;
} elseif (CH_TXT_ATR_INPUTFIELDSTART == cChar ||
CH_TXT_ATR_INPUTFIELDEND == cChar ||
CH_TXT_ATR_FORMELEMENT == cChar ||
CH_TXT_ATR_FIELDSTART == cChar ||
CH_TXT_ATR_FIELDSEP == cChar ||
CH_TXT_ATR_FIELDEND == cChar)
{ // just skip it and continue with the content...
++nIdx;
}
} // end of while
nWidth = std::max( nWidth, nProWidth );
// search for the line containing nStart if (HasPara())
{
SwTextInfo aInf(this);
SwTextIter aLine(this, &aInf);
aLine.CharToLine( nStart );
pOut->SetMapMode( aOldMap ); return o3tl::narrowing<sal_uInt16>( nWidth ?
( ( 100 * aLine.GetCurr()->Height() ) / nWidth ) : 0 );
} // no frame or no paragraph, we take the height of the character // at nStart as line height
aIter.SeekAndChgAttrIter( nStart, pOut );
pOut->SetMapMode( aOldMap );
if (mnOffset != pFollow->GetOffset())
{ return nullptr;
}
// At this point we know what we're part of a chain that is an anchor for split fly frames, but // we're not the last one. See if we have a matching fly.
// Look up the master of the anchor. const SwTextFrame* pAnchor = this; while (pAnchor->IsFollow())
{
pAnchor = pAnchor->FindMaster();
} for (constauto& pFly : pAnchor->GetSplitFlyDrawObjs())
{ // Nominally all flys are anchored in the master; see if this fly is effectively anchored in // us.
SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame(); if (pFlyAnchor != this)
{ continue;
} if (pFly->GetFollow())
{ return pFly;
}
}
return nullptr;
}
bool SwTextFrame::IsEmptyMasterWithSplitFly() const
{ if (!IsEmptyMaster())
{ returnfalse;
}
if (!m_pDrawObjs || m_pDrawObjs->size() != 1)
{ returnfalse;
}
// It has a split fly anchored to it. if (pFlyFrame->GetFrameFormat()->GetVertOrient().GetPos() >= 0)
{ returnfalse;
}
// Negative vertical offset means that visually it already may have a first line. // Consider that, we may need to split the frame, so the fly frame is on one page and the empty // paragraph's frame is on a next page. returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.