/* -*- 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 <editeng/lspcitem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/escapementitem.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/pgrditem.hxx>
#include <editeng/fontitem.hxx>
#include <vcl/svapp.hxx>
#include <comphelper/scopeguard.hxx>
#include <viewsh.hxx>
#include <viewopt.hxx>
#include <ndtxt.hxx>
#include <pagefrm.hxx>
#include <paratr.hxx>
#include <SwPortionHandler.hxx>
#include "porrst.hxx"
#include "inftxt.hxx"
#include "txtpaint.hxx"
#include <swfntcch.hxx>
#include <tgrditem.hxx>
#include <pagedesc.hxx>
#include <frmatr.hxx>
#include "redlnitr.hxx"
#include "atrhndl.hxx"
#include <rootfrm.hxx>
#include <formatlinebreak.hxx>
#include <txatbase.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <IDocumentSettingAccess.hxx>
#include <IDocumentDeviceAccess.hxx>
#include <IDocumentLayoutAccess.hxx>
#include <crsrsh.hxx>
#include <swtypes.hxx>
#include <strings.hrc>
#include <flyfrms.hxx>
#include <bodyfrm.hxx>
SwTmpEndPortion::SwTmpEndPortion( const SwLinePortion &rPortion,
const FontLineStyle eUL,
const FontStrikeout eStrkout,
const Color& rCol ) :
m_eUnderline( eUL ), m_eStrikeout( eStrkout ), m_aColor( rCol )
{
Height( rPortion.Height() );
SetAscent( rPortion.GetAscent() );
SetWhichPor( PortionType::TempEnd );
}
void SwTmpEndPortion::Paint( const SwTextPaintInfo &rInf ) const
{
if (!(rInf.OnWin() && rInf.GetOpt().IsParagraph()))
return;
const SwFont* pOldFnt = rInf.GetFont();
SwFont aFont(*pOldFnt);
const SwDoc& rDoc = rInf.GetTextFrame()->GetDoc();
if (aFont.IsSymbol(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()))
{
const SvxFontItem& rFontItem = rDoc.GetDefault(RES_CHRATR_FONT);
aFont.SetName( rFontItem.GetFamilyName(), SwFontScript::Latin );
aFont.SetStyleName( rFontItem.GetStyleName(), SwFontScript::Latin );
aFont.SetFamily( rFontItem.GetFamily(), SwFontScript::Latin );
aFont.SetPitch( rFontItem.GetPitch(), SwFontScript::Latin );
aFont.SetCharSet( rFontItem.GetCharSet(), SwFontScript::Latin );
}
// Paint strikeout/underline based on redline color and settings
// (with an extra pilcrow in the background, because there is
// no SetStrikeoutColor(), also SetUnderColor() doesn't work()).
if ( m_eUnderline != LINESTYLE_NONE || m_eStrikeout != STRIKEOUT_NONE )
{
aFont.SetColor( m_aColor );
aFont.SetUnderline( m_eUnderline );
aFont.SetStrikeout( m_eStrikeout );
const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
// draw the pilcrow with strikeout/underline in redline color
rInf.DrawText(CH_PAR, *this);
}
aFont.SetColor( SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor() );
aFont.SetStrikeout( STRIKEOUT_NONE );
aFont.SetUnderline( LINESTYLE_NONE );
const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
// draw the pilcrow
rInf.DrawText(CH_PAR, *this);
const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
}
SwBreakPortion::SwBreakPortion( const SwLinePortion &rPortion, const SwTextAttr* pAttr )
: SwLinePortion( rPortion )
{
mnLineLength = TextFrameIndex(1);
m_eRedline = RedlineType::None;
SetWhichPor( PortionType::Break );
m_eClear = SwLineBreakClear::NONE;
if (pAttr && pAttr->Which() == RES_TXTATR_LINEBREAK)
{
m_eClear = pAttr->GetLineBreak().GetValue();
}
m_nTextHeight = 0;
}
TextFrameIndex SwBreakPortion::GetModelPositionForViewPoint(const SwTwips) const
{
return TextFrameIndex(0);
}
SwTwips SwBreakPortion::GetViewWidth(const SwTextSizeInfo&) const { return 0; }
SwLinePortion *SwBreakPortion::Compress()
{ return (GetNextPortion() && GetNextPortion()->InTextGrp() ? nullptr : this); }
void SwBreakPortion::Paint( const SwTextPaintInfo &rInf ) const
{
if( !(rInf.OnWin() && rInf.GetOpt().IsLineBreak()) )
return;
// Reduce height to text height for the duration of the print, so the vertical height will look
// correct for the line break character, even for clearing breaks.
SwTwips nHeight = Height();
SwTwips nVertPosOffset = (nHeight - m_nTextHeight) / 2;
auto pPortion = const_cast<SwBreakPortion*>(this);
pPortion->Height(m_nTextHeight, false);
if (rInf.GetTextFrame()->IsVertical())
{
// Compensate for the offset done in SwTextCursor::AdjustBaseLine() for the vertical case.
const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() + nVertPosOffset);
}
comphelper::ScopeGuard g(
[pPortion, nHeight, &rInf, nVertPosOffset]
{
if (rInf.GetTextFrame()->IsVertical())
{
const_cast<SwTextPaintInfo&>(rInf).Y(rInf.Y() - nVertPosOffset);
}
pPortion->Height(nHeight, false);
});
rInf.DrawLineBreak( *this );
// paint redlining
if (m_eRedline == RedlineType::None)
return;
sal_Int16 nNoBreakWidth = rInf.GetTextSize(S_NOBREAK_FOR_REDLINE).Width();
if ( nNoBreakWidth > 0 )
{
// approximate portion size with multiple no-break spaces
// and draw these spaces (at least a single one) by DrawText
// painting the requested redline underline/strikeout
sal_Int16 nSpaces = (LINE_BREAK_WIDTH + nNoBreakWidth/2) / nNoBreakWidth;
OUStringBuffer aBuf(S_NOBREAK_FOR_REDLINE);
for (sal_Int16 i = 1; i < nSpaces; ++i)
aBuf.append(S_NOBREAK_FOR_REDLINE);
const SwFont* pOldFnt = rInf.GetFont();
SwFont aFont(*pOldFnt);
if (m_eRedline == RedlineType::Delete)
aFont.SetUnderline( LINESTYLE_NONE );
else
aFont.SetStrikeout( STRIKEOUT_NONE );
const_cast<SwTextPaintInfo&>(rInf).SetFont(&aFont);
rInf.DrawText(aBuf.makeStringAndClear(), *this);
const_cast<SwTextPaintInfo&>(rInf).SetFont(const_cast<SwFont*>(pOldFnt));
}
}
bool SwBreakPortion::Format( SwTextFormatInfo &rInf )
{
const SwLinePortion *pRoot = rInf.GetRoot();
Width( 0 );
Height( pRoot->Height() );
m_nTextHeight = Height();
// See if this is a clearing break. If so, calculate how much we need to "jump down" so the next
// line can again use the full text width.
SwLineBreakClear eClear = m_eClear;
if (rInf.GetTextFrame()->IsRightToLeft() && eClear != SwLineBreakClear::ALL)
{
// RTL ignores left/right breaks.
eClear = SwLineBreakClear::NONE;
}
if (eClear != SwLineBreakClear::NONE)
{
SwTextFly& rTextFly = rInf.GetTextFly();
if (rTextFly.IsOn())
{
SwTwips nHeight = rTextFly.GetMaxBottom(*this, rInf) - rInf.Y();
if (nHeight > Height())
{
Height(nHeight, /*bText=*/false);
}
}
}
SetAscent( pRoot->GetAscent() );
if (rInf.GetIdx() + TextFrameIndex(1) == TextFrameIndex(rInf.GetText().getLength()))
rInf.SetNewLine( true );
return true;
}
void SwBreakPortion::HandlePortion( SwPortionHandler& rPH ) const
{
rPH.Text( GetLen(), GetWhichPor() );
}
void SwBreakPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex&
nOffset) const
{
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBreakPortion"));
dumpAsXmlAttributes(pWriter, rText, nOffset);
nOffset += GetLen();
(void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("text-height"),
BAD_CAST(OString::number(m_nTextHeight).getStr()));
(void)xmlTextWriterEndElement(pWriter);
}
SwLineBreakClear SwBreakPortion::GetClear() const { return m_eClear; }
SwKernPortion::SwKernPortion( SwLinePortion &rPortion, short nKrn,
bool bBG, bool bGK ) :
m_nKern( nKrn ), m_bBackground( bBG ), m_bGridKern( bGK )
{
Height( rPortion.Height() );
SetAscent( rPortion.GetAscent() );
mnLineLength = TextFrameIndex(0);
SetWhichPor( PortionType::Kern );
if( m_nKern > 0 )
Width( m_nKern );
rPortion.Insert( this );
}
SwKernPortion::SwKernPortion( const SwLinePortion& rPortion ) :
m_nKern( 0 ), m_bBackground( false ), m_bGridKern( true )
{
Height( rPortion.Height() );
SetAscent( rPortion.GetAscent() );
mnLineLength = TextFrameIndex(0);
SetWhichPor( PortionType::Kern );
}
void SwKernPortion::Paint( const SwTextPaintInfo &rInf ) const
{
if( !Width() )
return;
// bBackground is set for Kerning Portions between two fields
if ( m_bBackground )
rInf.DrawViewOpt( *this, PortionType::Field );
rInf.DrawBackBrush( *this );
if (GetJoinBorderWithNext() ||GetJoinBorderWithPrev())
rInf.DrawBorder( *this );
// do we have to repaint a post it portion?
if( rInf.OnWin() && mpNextPortion && !mpNextPortion->Width() )
mpNextPortion->PrePaint( rInf, this );
if( rInf.GetFont()->IsPaintBlank() )
{
SwRect aClipRect;
rInf.CalcRect( *this, &aClipRect );
SwSaveClip aClip( const_cast<OutputDevice*>(rInf.GetOut()) );
aClip.ChgClip( aClipRect );
rInf.DrawText(u" "_ustr, *this, TextFrameIndex(0), TextFrameIndex(2), true );
}
}
void SwKernPortion::FormatEOL( SwTextFormatInfo &rInf )
{
if ( m_bGridKern )
return;
if( rInf.GetLast() == this )
rInf.SetLast( FindPrevPortion( rInf.GetRoot() ) );
if( m_nKern < 0 )
Width( -m_nKern );
else
Width( 0 );
rInf.GetLast()->FormatEOL( rInf );
}
SwArrowPortion::SwArrowPortion( const SwLinePortion &rPortion ) :
m_bLeft( true )
{
Height( rPortion.Height() );
SetAscent( rPortion.GetAscent() );
mnLineLength = TextFrameIndex(0);
SetWhichPor( PortionType::Arrow );
}
SwArrowPortion::SwArrowPortion( const SwTextPaintInfo &rInf )
: m_bLeft( false )
{
Height(rInf.GetTextFrame()->getFramePrintArea().Height());
m_aPos.setX( rInf.GetTextFrame()->getFrameArea().Left() +
rInf.GetTextFrame()->getFramePrintArea().Right() );
m_aPos.setY( rInf.GetTextFrame()->getFrameArea().Top() +
rInf.GetTextFrame()->getFramePrintArea().Bottom() );
SetWhichPor( PortionType::Arrow );
}
void SwArrowPortion::Paint( const SwTextPaintInfo &rInf ) const
{
const_cast<SwArrowPortion*>(this)->m_aPos = rInf.GetPos();
}
SwLinePortion *SwArrowPortion::Compress() { return this; }
SwTwips SwTextFrame::EmptyHeight() const
{
if (IsCollapse()) {
SwViewShell *pSh = getRootFrame()->GetCurrShell();
if ( auto pCrSh = dynamic_cast<SwCursorShell*>( pSh ) ) {
// this is called during formatting so avoid recursive layout
SwContentFrame const*const pCurrFrame = pCrSh->GetCurrFrame(false);
if (pCurrFrame==static_cast<SwContentFrame const *>(this)) {
// do nothing
} else {
return 1;
}
} else {
return 1;
}
}
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::EmptyHeight with swapped frame" );
std::unique_ptr<SwFont> pFnt;
const SwTextNode& rTextNode = *GetTextNodeForParaProps();
const IDocumentSettingAccess* pIDSA = rTextNode.getIDocumentSettingAccess();
SwViewShell *pSh = getRootFrame()->GetCurrShell();
if ( rTextNode.HasSwAttrSet() )
{
const SwAttrSet *pAttrSet = &( rTextNode.GetSwAttrSet() );
pFnt.reset(new SwFont( pAttrSet, pIDSA ));
}
else
{
SwFontAccess aFontAccess( &rTextNode.GetAnyFormatColl(), pSh);
pFnt.reset(new SwFont( aFontAccess.Get()->GetFont() ));
pFnt->CheckFontCacheId( pSh, pFnt->GetActual() );
}
if ( IsVertical() )
pFnt->SetVertical( 2700_deg10 );
OutputDevice* pOut = pSh ? pSh->GetOut() : nullptr;
if ( !pOut || !pSh->GetViewOptions()->getBrowseMode() ||
pSh->GetViewOptions()->IsPrtFormat() )
{
pOut = rTextNode.getIDocumentDeviceAccess().getReferenceDevice(true);
}
const IDocumentRedlineAccess& rIDRA = rTextNode.getIDocumentRedlineAccess();
if (IDocumentRedlineAccess::IsShowChanges(rIDRA.GetRedlineFlags())
&& !getRootFrame()->IsHideRedlines())
{
const SwRedlineTable::size_type nRedlPos = rIDRA.GetRedlinePos( rTextNode, RedlineType::Any );
if( SwRedlineTable::npos != nRedlPos )
{
SwAttrHandler aAttrHandler;
aAttrHandler.Init(rTextNode.GetSwAttrSet(),
*rTextNode.getIDocumentSettingAccess());
SwRedlineItr aRedln( rTextNode, *pFnt, aAttrHandler,
nRedlPos, SwRedlineItr::Mode::Show);
}
}
SwTwips nRet;
if( !pOut )
nRet = IsVertical() ?
getFramePrintArea().SSize().Width() + 1 :
getFramePrintArea().SSize().Height() + 1;
else
{
pFnt->SetFntChg( true );
pFnt->ChgPhysFnt( pSh, *pOut );
nRet = pFnt->GetHeight( pSh, *pOut );
}
return nRet;
}
bool SwTextFrame::FormatEmpty()
{
OSL_ENSURE( ! IsVertical() || ! IsSwapped(),"SwTextFrame::FormatEmpty with swapped frame" );
bool bCollapse = EmptyHeight( ) == 1 && IsCollapse( );
// sw_redlinehide: just disable FormatEmpty optimisation for now
// Split fly frames: non-last parts of the anchor want this optimization to clear the old
// content.
SwFlyAtContentFrame* pNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj();
bool bHasNonLastSplitFlyDrawObj = pNonLastSplitFlyDrawObj != nullptr;
if (pNonLastSplitFlyDrawObj && pNonLastSplitFlyDrawObj->IsWrapOnAllPages())
{
// Split fly: the anchor is non-empty on all pages in the "wrap on all pages" case.
bHasNonLastSplitFlyDrawObj = false;
}
if ((HasFollow() && !bHasNonLastSplitFlyDrawObj) || GetMergedPara() || (GetTextNodeFirst()->GetpSwpHints() && !bHasNonLastSplitFlyDrawObj) ||
nullptr != GetTextNodeForParaProps()->GetNumRule() ||
GetTextNodeFirst()->HasHiddenCharAttribute(true) ||
IsInFootnote() || ( HasPara() && GetPara()->IsPrepMustFit() ) )
return false;
const SwAttrSet& aSet = GetTextNodeForParaProps()->GetSwAttrSet();
const SvxAdjust nAdjust = aSet.GetAdjust().GetAdjust();
if( !bCollapse && ( ( ( ! IsRightToLeft() && ( SvxAdjust::Left != nAdjust ) ) ||
( IsRightToLeft() && ( SvxAdjust::Right != nAdjust ) ) ) ||
aSet.GetRegister().GetValue() ) )
return false;
const SvxLineSpacingItem &rSpacing = aSet.GetLineSpacing();
if( !bCollapse && ( SvxLineSpaceRule::Min == rSpacing.GetLineSpaceRule() ||
SvxLineSpaceRule::Fix == rSpacing.GetLineSpaceRule() ||
aSet.GetFirstLineIndent().IsAutoFirst()))
{
return false;
}
SwTextFly aTextFly( this );
SwRect aRect;
bool bFirstFlyCheck = 0 != getFramePrintArea().Height();
if ( !bCollapse && bFirstFlyCheck &&
aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) && !bHasNonLastSplitFlyDrawObj )
return false;
if (IsEmptyWithSplitFly())
{
// We don't want this optimization in case the paragraph is not really empty, because it has
// a fly frame and it also needs space for the empty paragraph in a next line.
return false;
}
// only need to check one node because of early return on GetMerged()
for (SwContentIndex const* pIndex = GetTextNodeFirst()->GetFirstIndex();
pIndex; pIndex = pIndex->GetNext())
{
if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark)
continue;
auto const pMark = static_cast<sw::mark::MarkBase const*>(pIndex->GetOwner());
if (dynamic_cast<const sw::mark::Bookmark*>(pMark) != nullptr)
{ // need bookmark portions!
return false;
}
}
SwTwips nHeight = EmptyHeight();
if (aSet.GetParaGrid().GetValue() &&
IsInDocBody() )
{
SwTextGridItem const*const pGrid(GetGridItem(FindPageFrame()));
if ( pGrid )
nHeight = pGrid->GetBaseHeight() + pGrid->GetRubyHeight();
}
SwRectFnSet aRectFnSet(this);
SwTwips nChg = nHeight - aRectFnSet.GetHeight(getFramePrintArea());
const SwBodyFrame* pBody = FindBodyFrame();
if (pNonLastSplitFlyDrawObj && pBody)
{
// See if we need to increase the text frame height due to split flys. This is necessary for
// anchors of inner floating tables, where moving to a next page moves indirectly, so we
// want a correct text frame height.
SwTwips nFrameBottom = aRectFnSet.GetBottom(getFrameArea()) + nChg;
SwTwips nFlyBottom = aRectFnSet.GetBottom(pNonLastSplitFlyDrawObj->getFrameArea());
SwTwips nBodyBottom = aRectFnSet.GetBottom(pBody->getFrameArea());
if (nFlyBottom > nBodyBottom)
{
// This is the legacy case where flys may overlap with footer frames.
nFlyBottom = nBodyBottom;
}
if (pNonLastSplitFlyDrawObj->isFrameAreaPositionValid() && nFlyBottom > nFrameBottom)
{
nChg += (nFlyBottom - nFrameBottom);
}
}
if( !nChg )
SetUndersized( false );
AdjustFrame( nChg );
if (GetHasRotatedPortions())
{
ClearPara();
SetHasRotatedPortions(false);
}
RemoveFromCache();
if( !IsEmpty() )
{
SetEmpty( true );
SetCompletePaint();
}
if( !bCollapse && !bFirstFlyCheck &&
aTextFly.IsOn() && aTextFly.IsAnyObj( aRect ) )
return false;
// #i35635# - call method <HideAndShowObjects()>
// to assure that objects anchored at the empty paragraph are
// correctly visible resp. invisible.
HideAndShowObjects();
return true;
}
bool SwTextFrame::FillRegister( SwTwips& rRegStart, sal_uInt16& rRegDiff )
{
const SwFrame *pFrame = this;
rRegDiff = 0;
while( !( ( SwFrameType::Body | SwFrameType::Fly )
& pFrame->GetType() ) && pFrame->GetUpper() )
pFrame = pFrame->GetUpper();
if( ( SwFrameType::Body| SwFrameType::Fly ) & pFrame->GetType() )
{
SwRectFnSet aRectFnSet(pFrame);
rRegStart = aRectFnSet.GetPrtTop(*pFrame);
pFrame = pFrame->FindPageFrame();
if( pFrame->IsPageFrame() )
{
SwPageDesc* pDesc = const_cast<SwPageFrame*>(static_cast<const SwPageFrame*>(pFrame))->FindPageDesc();
if( pDesc )
{
rRegDiff = pDesc->GetRegHeight();
if( !rRegDiff )
{
const SwTextFormatColl *pFormat = pDesc->GetRegisterFormatColl();
if( pFormat )
{
const SvxLineSpacingItem &rSpace = pFormat->GetLineSpacing();
if( SvxLineSpaceRule::Fix == rSpace.GetLineSpaceRule() )
{
rRegDiff = rSpace.GetLineHeight();
pDesc->SetRegHeight( rRegDiff );
pDesc->SetRegAscent( ( 4 * rRegDiff ) / 5 );
}
else
{
SwViewShell *pSh = getRootFrame()->GetCurrShell();
SwFontAccess aFontAccess( pFormat, pSh );
SwFont aFnt( aFontAccess.Get()->GetFont() );
OutputDevice *pOut = nullptr;
if( !pSh || !pSh->GetViewOptions()->getBrowseMode() ||
pSh->GetViewOptions()->IsPrtFormat() )
pOut = GetDoc().getIDocumentDeviceAccess().getReferenceDevice( true );
if( pSh && !pOut )
pOut = pSh->GetWin()->GetOutDev();
if( !pOut )
pOut = Application::GetDefaultDevice();
MapMode aOldMap( pOut->GetMapMode() );
pOut->SetMapMode( MapMode( MapUnit::MapTwip ) );
aFnt.ChgFnt( pSh, *pOut );
rRegDiff = aFnt.GetHeight( pSh, *pOut );
sal_uInt16 nNetHeight = rRegDiff;
switch( rSpace.GetLineSpaceRule() )
{
case SvxLineSpaceRule::Auto:
break;
case SvxLineSpaceRule::Min:
{
if( rRegDiff < rSpace.GetLineHeight() )
rRegDiff = rSpace.GetLineHeight();
break;
}
default:
OSL_FAIL( ": unknown LineSpaceRule" );
}
switch( rSpace.GetInterLineSpaceRule() )
{
case SvxInterLineSpaceRule::Off:
break;
case SvxInterLineSpaceRule::Prop:
{
tools::Long nTmp = rSpace.GetPropLineSpace();
if( nTmp < 50 )
nTmp = nTmp ? 50 : 100;
nTmp *= rRegDiff;
nTmp /= 100;
if( !nTmp )
++nTmp;
rRegDiff = o3tl::narrowing<sal_uInt16>(nTmp);
nNetHeight = rRegDiff;
break;
}
case SvxInterLineSpaceRule::Fix:
{
rRegDiff = rRegDiff + rSpace.GetInterLineSpace();
nNetHeight = rRegDiff;
break;
}
default: OSL_FAIL( ": unknown InterLineSpaceRule" );
}
pDesc->SetRegHeight( rRegDiff );
pDesc->SetRegAscent( rRegDiff - nNetHeight +
aFnt.GetAscent( pSh, *pOut ) );
pOut->SetMapMode( aOldMap );
}
}
}
const tools::Long nTmpDiff = pDesc->GetRegAscent() - rRegDiff;
if ( aRectFnSet.IsVert() )
rRegStart -= nTmpDiff;
else
rRegStart += nTmpDiff;
}
}
}
return ( 0 != rRegDiff );
}
void SwHiddenTextPortion::Paint( const SwTextPaintInfo & rInf) const
{
#ifdef DBG_UTIL
OutputDevice* pOut = const_cast<OutputDevice*>(rInf.GetOut());
Color aCol( rInf.GetOpt().GetFieldShadingsColor() );
Color aOldColor( pOut->GetFillColor() );
pOut->SetFillColor( aCol );
Point aPos( rInf.GetPos() );
aPos.AdjustY( -150 );
aPos.AdjustX( -25 );
SwRect aRect( aPos, Size( 100, 200 ) );
pOut->DrawRect( aRect.SVRect() );
pOut->SetFillColor( aOldColor );
#else
(void)rInf;
#endif
}
bool SwHiddenTextPortion::Format( SwTextFormatInfo &rInf )
{
Width( 0 );
rInf.GetTextFrame()->HideFootnotes( rInf.GetIdx(), rInf.GetIdx() + GetLen() );
return false;
};
bool SwControlCharPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
OUString & rOutString, SwFont & rTmpFont, int &) const
{
if (mcChar == CHAR_WJ || !rTextPaintInfo.GetOpt().IsViewMetaChars())
{
return false;
}
switch (mcChar)
{
case CHAR_ZWSP:
rOutString = "/"; break;
// case CHAR_LRM :
// rText = sal_Unicode(0x2514); break;
// case CHAR_RLM :
// rText = sal_Unicode(0x2518); break;
default:
assert(false);
break;
}
rTmpFont.SetEscapement( CHAR_ZWSP == mcChar ? DFLT_ESC_AUTO_SUB : -25 );
rTmpFont.SetColor( SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor() );
const sal_uInt16 nProp = 40;
rTmpFont.SetProportion( nProp ); // a smaller font
return true;
}
bool SwBookmarkPortion::DoPaint(SwTextPaintInfo const& rTextPaintInfo,
OUString & rOutString, SwFont & rFont, int & rDeltaY) const
{
// custom color is visible without field shading, too
if (!rTextPaintInfo.GetOpt().IsShowBookmarks())
{
return false;
}
rOutString = OUStringChar(mcChar);
// init font: we want OpenSymbol to ensure it doesn't look too crazy;
// thin and a bit higher than the surrounding text
auto const nOrigAscent(rFont.GetAscent(rTextPaintInfo.GetVsh(), *rTextPaintInfo.GetOut()));
rFont.SetName(u"OpenSymbol"_ustr, rFont.GetActual());
Size aSize(rFont.GetSize(rFont.GetActual()));
// use also the external leading (line gap) of the portion, but don't use
// 100% of it because i can't figure out how to baseline align that
assert(aSize.Height() != 0);
auto const nFactor = aSize.Height() > 0 ? (Height() * 95) / aSize.Height() : Height();
rFont.SetProportion(nFactor);
rFont.SetWeight(WEIGHT_THIN, rFont.GetActual());
rFont.SetColor(rTextPaintInfo.GetOpt().GetFieldShadingsColor());
// reset these to default...
rFont.SetAlign(ALIGN_BASELINE);
rFont.SetUnderline(LINESTYLE_NONE);
rFont.SetOverline(LINESTYLE_NONE);
rFont.SetStrikeout(STRIKEOUT_NONE);
rFont.SetOutline(false);
rFont.SetShadow(false);
rFont.SetTransparent(false);
rFont.SetEmphasisMark(FontEmphasisMark::NONE);
rFont.SetEscapement(0);
rFont.SetPitch(PITCH_DONTKNOW, rFont.GetActual());
rFont.SetRelief(FontRelief::NONE);
// adjust Y position to account for different baselines of the fonts
auto const nOSAscent(rFont.GetAscent(rTextPaintInfo.GetVsh(), *rTextPaintInfo.GetOut()));
rDeltaY = nOSAscent - nOrigAscent;
return true;
}
void SwControlCharPortion::Paint( const SwTextPaintInfo &rInf ) const
{
if ( !Width() ) // is only set during prepaint mode
return;
rInf.DrawViewOpt(*this, GetWhichPor());
int deltaY(0);
SwFont aTmpFont( *rInf.GetFont() );
OUString aOutString;
if (!(rInf.OnWin()
&& !rInf.GetOpt().IsPagePreview()
&& !rInf.GetOpt().IsReadonly()
&& DoPaint(rInf, aOutString, aTmpFont, deltaY)))
return;
SwFontSave aFontSave( rInf, &aTmpFont );
if ( !mnHalfCharWidth )
mnHalfCharWidth = rInf.GetTextSize( aOutString ).Width() / 2;
Point aOldPos = rInf.GetPos();
Point aNewPos( aOldPos );
auto const deltaX((Width() / 2) - mnHalfCharWidth);
switch (rInf.GetFont()->GetOrientation(rInf.GetTextFrame()->IsVertical()).get())
{
case 0:
aNewPos.AdjustX(deltaX);
aNewPos.AdjustY(deltaY);
break;
case 900:
aNewPos.AdjustY(-deltaX);
aNewPos.AdjustX(deltaY);
break;
case 2700:
aNewPos.AdjustY(deltaX);
aNewPos.AdjustX(-deltaY);
break;
default:
assert(false);
break;
}
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
rInf.DrawText( aOutString, *this );
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos );
}
void SwBookmarkPortion::Paint( const SwTextPaintInfo &rInf ) const
{
if ( !Width() ) // is only set during prepaint mode
return;
rInf.DrawViewOpt(*this, GetWhichPor());
int deltaY(0);
SwFont aTmpFont( *rInf.GetFont() );
OUString aOutString;
if (!(rInf.OnWin()
&& !rInf.GetOpt().IsPagePreview()
&& !rInf.GetOpt().IsReadonly()
&& DoPaint(rInf, aOutString, aTmpFont, deltaY)))
return;
SwFontSave aFontSave( rInf, &aTmpFont );
if ( !mnHalfCharWidth )
mnHalfCharWidth = rInf.GetTextSize( aOutString ).Width() / 2;
auto nHeight = rInf.GetTextSize( aOutString ).Height();
Point aOldPos = rInf.GetPos();
Point aNewPos( aOldPos );
auto const deltaX((Width() / 2) - mnHalfCharWidth);
switch (rInf.GetFont()->GetOrientation(rInf.GetTextFrame()->IsVertical()).get())
{
case 0:
aNewPos.AdjustX(deltaX);
aNewPos.AdjustY(deltaY);
break;
case 900:
aNewPos.AdjustY(-deltaX);
aNewPos.AdjustX(deltaY);
break;
case 2700:
aNewPos.AdjustY(deltaX);
aNewPos.AdjustX(-deltaY);
break;
default:
assert(false);
break;
}
// draw end marks before the character position
if ( m_nStart == 0 || m_nEnd == 0 )
{
// single type boundary marks are there outside of the bookmark text
// some |text| here
// [[ ]]
if (m_nStart > 1)
aNewPos.AdjustX(mnHalfCharWidth * -2 * (m_aColors.size() - 1));
}
else if ( m_nStart != 0 && m_nEnd != 0 )
// both end and start boundary marks: adjust them around the bookmark position
// |te|xt|
// ]] [[
aNewPos.AdjustX(mnHalfCharWidth * -(2 * m_nEnd - 1 + m_nPoint) );
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
SwTwips nTypePos = 0; // shift to the position of the next rdf:type label
sal_Int32 nDirection = -1; // start with the closing brackets
bool bStart = true;
for ( const auto& it : m_aColors )
{
// set bold for custom colored bookmark symbol
// and draw multiple symbols showing all custom colors
aTmpFont.SetWeight( COL_TRANSPARENT == std::get<1>(it) ? WEIGHT_THIN : WEIGHT_BOLD, aTmpFont.GetActual() );
aTmpFont.SetColor( COL_TRANSPARENT == std::get<1>(it) ? rInf.GetOpt().GetFieldShadingsColor() : std::get<1>(it) );
aOutString = OUString(std::get<0>(it) == SwScriptInfo::MarkKind::Start ? '[' : ']');
if (nDirection == -1 && std::get<0>(it) != SwScriptInfo::MarkKind::End)
{
nDirection = 1;
nTypePos = mnHalfCharWidth * 2; // start label after the opening bracket
}
// vertical rdf:type label position for the opening and closing brackets
sal_Int32 fPos = std::get<0>(it) == SwScriptInfo::MarkKind::Start
? -0.6 * nHeight
: 0.3 * nHeight;
// MarkKind::Point: drawn I-beam (e.g. U+2336) as overlapping ][
if ( std::get<0>(it) == SwScriptInfo::MarkKind::Point )
{
aNewPos.AdjustX(-mnHalfCharWidth * 5/16);
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
rInf.DrawText( aOutString, *this );
// when the overlapping vertical lines are 50 pixel width on the screen,
// this distance (half width * 5/8) still results precise overlapping
aNewPos.AdjustX(mnHalfCharWidth * 5/8);
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
aOutString = OUString('[');
}
rInf.DrawText( aOutString, *this );
// show rdf:type labels, left-aligned top position after the opening brackets
// right-aligned bottom position before the closing brackets
// if there are multiple opening or closing brackets, collect
// their length in nTypePos to show non-overlapping labels
OUString sType = std::get<3>(it);
if ( !sType.isEmpty() )
{
Size aTmpSz = aTmpFont.GetSize( SwFontScript::Latin );
auto origSize = aTmpSz;
// calculate label size
aTmpSz.setHeight( ( 100 * aTmpSz.Height() ) / 250 );
aTmpSz.setWidth( ( 100 * aTmpSz.Width() ) / 250 );
if ( aTmpSz.Width() || aTmpSz.Height() )
{
aTmpFont.SetSize( aTmpSz, SwFontScript::Latin );
aNewPos.AdjustY(fPos);
if ( nDirection == -1 )
{
if (bStart)
{
nTypePos += rInf.GetTextSize( sType ).Width();
bStart = false;
}
else
nTypePos += rInf.GetTextSize( sType + " " ).Width() + 2 * mnHalfCharWidth;
}
aNewPos.AdjustX( nDirection * nTypePos );
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
rInf.DrawText( sType, *this );
// restore original position
aNewPos.AdjustX( -nDirection * nTypePos );
if ( nDirection == 1 )
nTypePos += rInf.GetTextSize( sType + " " ).Width() - mnHalfCharWidth * 2;
aNewPos.AdjustY(-fPos);
}
// restore original text size
aTmpSz.setHeight(origSize.Height());
aTmpSz.setWidth(origSize.Width());
aTmpFont.SetSize( origSize, SwFontScript::Latin );
}
// place the next symbol after the previous one
// TODO: fix orientation and start/end
aNewPos.AdjustX(mnHalfCharWidth * 2);
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
}
const_cast< SwTextPaintInfo& >( rInf ).SetPos( aOldPos );
}
void SwBookmarkPortion::HandlePortion( SwPortionHandler& rPH ) const
{
OUStringBuffer aStr;
for ( const auto& it : m_aColors )
{
aStr.append("#" + std::get<2>(it) + " " + SwResId(STR_BOOKMARK_DEF_NAME));
switch (std::get<0>(it))
{
case SwScriptInfo::MarkKind::Point:
break;
case SwScriptInfo::MarkKind::Start:
aStr.append(" " + SwResId(STR_CAPTION_BEGINNING));
break;
case SwScriptInfo::MarkKind::End:
aStr.append(" " + SwResId(STR_CAPTION_END));
break;
}
}
rPH.Special( GetLen(), aStr.makeStringAndClear(), GetWhichPor() );
}
void SwBookmarkPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, TextFrameIndex& nOffset) const
{
(void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwBookmarkPortion"));
dumpAsXmlAttributes(pWriter, rText, nOffset);
nOffset += GetLen();
if (!m_aColors.empty())
{
OUStringBuffer aStr;
for (const auto& rColor : m_aColors)
{
aStr.append("#" + std::get<2>(rColor) + " " + SwResId(STR_BOOKMARK_DEF_NAME));
switch (std::get<0>(rColor))
{
case SwScriptInfo::MarkKind::Point:
break;
case SwScriptInfo::MarkKind::Start:
aStr.append(" " + SwResId(STR_CAPTION_BEGINNING));
break;
case SwScriptInfo::MarkKind::End:
aStr.append(" " + SwResId(STR_CAPTION_END));
break;
}
}
(void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("colors"),
BAD_CAST(aStr.makeStringAndClear().toUtf8().getStr()));
}
(void)xmlTextWriterEndElement(pWriter);
}
bool SwControlCharPortion::Format( SwTextFormatInfo &rInf )
{
const SwLinePortion* pRoot = rInf.GetRoot();
Width( 0 );
Height( pRoot->Height() );
SetAscent( pRoot->GetAscent() );
return false;
}
SwTwips SwControlCharPortion::GetViewWidth(const SwTextSizeInfo& rInf) const
{
if( !mnViewWidth )
mnViewWidth = rInf.GetTextSize(OUString(' ')).Width();
return mnViewWidth;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V560 A part of conditional expression is always true: m_nEnd != 0.
↑ V560 A part of conditional expression is always true: m_nStart != 0.
↑ V1004 The 'pSh' pointer was used unsafely after it was verified against nullptr. Check lines: 383, 384.