summaryrefslogtreecommitdiffstats
path: root/sw/source/core/text/porrst.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sw/source/core/text/porrst.cxx')
-rw-r--r--sw/source/core/text/porrst.cxx949
1 files changed, 949 insertions, 0 deletions
diff --git a/sw/source/core/text/porrst.cxx b/sw/source/core/text/porrst.cxx
new file mode 100644
index 0000000000..029adca753
--- /dev/null
+++ b/sw/source/core/text/porrst.cxx
@@ -0,0 +1,949 @@
+/* -*- 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 <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 <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);
+
+ // 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( NON_PRINTING_CHARACTER_COLOR );
+ 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 sal_uInt16) const
+{
+ return TextFrameIndex(0);
+}
+
+sal_uInt16 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(" ", *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( o3tl::narrowing<sal_uInt16>(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())
+ {
+ sw::mark::IMark const*const pMark = pIndex->GetMark();
+ if (dynamic_cast<const sw::mark::IBookmark*>(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().IsFieldShadings())
+ {
+ 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 );
+ 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("OpenSymbol", 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;
+
+ 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(static_cast<tools::Long>(mnHalfCharWidth) * -2 * (m_oColors.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(static_cast<tools::Long>(mnHalfCharWidth) * -(2 * m_nEnd - 1 + m_nPoint) );
+
+ const_cast< SwTextPaintInfo& >( rInf ).SetPos( aNewPos );
+
+ for ( const auto& it : m_oColors )
+ {
+ // 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 ? '[' : ']');
+
+ // 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 );
+ // 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_oColors )
+ {
+ 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_oColors.empty())
+ {
+ OUStringBuffer aStr;
+ for (const auto& rColor : m_oColors)
+ {
+ 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;
+}
+
+sal_uInt16 SwControlCharPortion::GetViewWidth( const SwTextSizeInfo& rInf ) const
+{
+ if( !mnViewWidth )
+ mnViewWidth = rInf.GetTextSize(OUString(' ')).Width();
+
+ return mnViewWidth;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */