/* -*- 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 #include #include #include #include #include "pormulti.hxx" #include #include #include "porfld.hxx" #include "porrst.hxx" #include "itrtxt.hxx" #include void SwTextIter::CtorInitTextIter( SwTextFrame *pNewFrame, SwTextInfo *pNewInf ) { assert(pNewFrame->GetPara()); CtorInitAttrIter( *pNewFrame->GetTextNodeFirst(), pNewFrame->GetPara()->GetScriptInfo(), pNewFrame ); SwTextNode const*const pNode = pNewFrame->GetTextNodeForParaProps(); m_pFrame = pNewFrame; m_pInf = pNewInf; m_aLineInf.CtorInitLineInfo( pNode->GetSwAttrSet(), *pNode ); m_nFrameStart = m_pFrame->getFrameArea().Pos().Y() + m_pFrame->getFramePrintArea().Pos().Y(); SwTextIter::Init(); // Order is important: only execute FillRegister if GetValue!=0 m_bRegisterOn = pNode->GetSwAttrSet().GetRegister().GetValue() && m_pFrame->FillRegister( m_nRegStart, m_nRegDiff ); } void SwTextIter::Init() { m_pCurr = m_pInf->GetParaPortion(); m_nStart = m_pInf->GetTextStart(); m_nY = m_nFrameStart; m_bPrev = true; m_pPrev = nullptr; m_nLineNr = 1; } void SwTextIter::CalcAscentAndHeight( SwTwips &rAscent, SwTwips &rHeight ) const { rHeight = GetLineHeight(); rAscent = m_pCurr->GetAscent() + rHeight - m_pCurr->Height(); } SwLineLayout *SwTextIter::GetPrev_() { m_pPrev = nullptr; m_bPrev = true; SwLineLayout *pLay = m_pInf->GetParaPortion(); if( m_pCurr == pLay ) return nullptr; while( pLay->GetNext() != m_pCurr ) pLay = pLay->GetNext(); m_pPrev = pLay; return m_pPrev; } const SwLineLayout *SwTextIter::GetPrev() { if(! m_bPrev) GetPrev_(); return m_pPrev; } const SwLineLayout *SwTextIter::Prev() { if( !m_bPrev ) GetPrev_(); if( m_pPrev ) { m_bPrev = false; m_pCurr = m_pPrev; m_nStart = m_nStart - m_pCurr->GetLen(); m_nY = m_nY - GetLineHeight(); if( !m_pCurr->IsDummy() && !(--m_nLineNr) ) ++m_nLineNr; return m_pCurr; } else return nullptr; } const SwLineLayout *SwTextIter::Next() { if(m_pCurr->GetNext()) { m_pPrev = m_pCurr; m_bPrev = true; m_nStart = m_nStart + m_pCurr->GetLen(); m_nY += GetLineHeight(); if( m_pCurr->GetLen() || ( m_nLineNr>1 && !m_pCurr->IsDummy() ) ) ++m_nLineNr; m_pCurr = m_pCurr->GetNext(); return m_pCurr; } else return nullptr; } const SwLineLayout *SwTextIter::NextLine() { const SwLineLayout *pNext = Next(); while( pNext && pNext->IsDummy() && pNext->GetNext() ) { pNext = Next(); } return pNext; } const SwLineLayout *SwTextIter::GetNextLine() const { const SwLineLayout *pNext = m_pCurr->GetNext(); while( pNext && pNext->IsDummy() && pNext->GetNext() ) { pNext = pNext->GetNext(); } return pNext; } const SwLineLayout *SwTextIter::GetPrevLine() { const SwLineLayout *pRoot = m_pInf->GetParaPortion(); if( pRoot == m_pCurr ) return nullptr; const SwLineLayout *pLay = pRoot; while( pLay->GetNext() != m_pCurr ) pLay = pLay->GetNext(); if( pLay->IsDummy() ) { const SwLineLayout *pTmp = pRoot; pLay = pRoot->IsDummy() ? nullptr : pRoot; while( pTmp->GetNext() != m_pCurr ) { if( !pTmp->IsDummy() ) pLay = pTmp; pTmp = pTmp->GetNext(); } } // If nothing has changed, then there are only dummy's return pLay; } const SwLineLayout *SwTextIter::PrevLine() { const SwLineLayout *pMyPrev = Prev(); if( !pMyPrev ) return nullptr; const SwLineLayout *pLast = pMyPrev; while( pMyPrev && pMyPrev->IsDummy() ) { pLast = pMyPrev; pMyPrev = Prev(); } return pMyPrev ? pMyPrev : pLast; } void SwTextIter::Bottom() { while( Next() ) { // nothing } } void SwTextIter::CharToLine(TextFrameIndex const nChar) { while( m_nStart + m_pCurr->GetLen() <= nChar && Next() ) ; while( m_nStart > nChar && Prev() ) ; } // 1170: takes into account ambiguities: const SwLineLayout *SwTextCursor::CharCursorToLine(TextFrameIndex const nPosition) { CharToLine( nPosition ); if( nPosition != m_nStart ) s_bRightMargin = false; bool bPrevious = s_bRightMargin && m_pCurr->GetLen() && GetPrev() && GetPrev()->GetLen(); if (bPrevious && nPosition && CH_BREAK == GetInfo().GetChar(nPosition - TextFrameIndex(1))) bPrevious = false; return bPrevious ? PrevLine() : m_pCurr; } SwTwips SwTextCursor::AdjustBaseLine( const SwLineLayout& rLine, const SwLinePortion* pPor, SwTwips nPorHeight, SwTwips nPorAscent, const bool bAutoToCentered ) const { if ( pPor ) { nPorHeight = pPor->Height(); nPorAscent = pPor->GetAscent(); } SwTwips nOfst = rLine.GetRealHeight() - rLine.Height(); SwTextGridItem const*const pGrid(GetGridItem(m_pFrame->FindPageFrame())); if ( pGrid && GetInfo().SnapToGrid() && pGrid->IsSquaredMode() ) { const sal_uInt16 nRubyHeight = pGrid->GetRubyHeight(); const bool bRubyTop = ! pGrid->GetRubyTextBelow(); if ( GetInfo().IsMulti() ) // we are inside the GetCharRect recursion for multi portions // we center the portion in its surrounding line nOfst = ( m_pCurr->Height() - nPorHeight ) / 2 + nPorAscent; else { // We have to take care for ruby portions. // The ruby portion is NOT centered nOfst = nOfst + nPorAscent; if ( ! pPor || ! pPor->IsMultiPortion() || ! static_cast(pPor)->IsRuby() ) { // Portions which are bigger than on grid distance are // centered inside the whole line. //for text refactor const sal_uInt16 nLineNet = rLine.Height() - nRubyHeight; //const sal_uInt16 nLineNet = ( nPorHeight > nGridWidth ) ? // rLine.Height() - nRubyHeight : // nGridWidth; nOfst += ( nLineNet - nPorHeight ) / 2; if ( bRubyTop ) nOfst += nRubyHeight; } } } else { switch ( GetLineInfo().GetVertAlign() ) { case SvxParaVertAlignItem::Align::Top : nOfst = nOfst + nPorAscent; break; case SvxParaVertAlignItem::Align::Center : OSL_ENSURE( rLine.Height() >= nPorHeight, "Portion height > Line height"); nOfst += ( rLine.Height() - nPorHeight ) / 2 + nPorAscent; break; case SvxParaVertAlignItem::Align::Bottom : nOfst += rLine.Height() - nPorHeight + nPorAscent; break; case SvxParaVertAlignItem::Align::Automatic : if ( bAutoToCentered || GetInfo().GetTextFrame()->IsVertical() ) { // Vertical text has these cases to calculate the baseline: // - Implicitly TB and RL: the origo is the top right corner, offset is the // ascent. // - (Implicitly TB and) LR: the origo is the top left corner, offset is the // descent. // - BT and LR: the origo is the bottom left corner, offset is the ascent. if (GetInfo().GetTextFrame()->IsVertLR() && !GetInfo().GetTextFrame()->IsVertLRBT()) nOfst += rLine.Height() - ( rLine.Height() - nPorHeight ) / 2 - nPorAscent; else { SwTwips nLineHeight = 0; bool bHadClearingBreak = false; if (GetInfo().GetTextFrame()->IsVertical()) { // Ignore the height of clearing break portions in the automatic // alignment case. const SwLinePortion* pLinePor = rLine.GetFirstPortion(); while (pLinePor) { bool bClearingBreak = false; if (pLinePor->IsBreakPortion()) { auto pBreakPortion = static_cast(pLinePor); bClearingBreak = pBreakPortion->GetClear() != SwLineBreakClear::NONE; if (bClearingBreak) { bHadClearingBreak = true; } } if (!bClearingBreak && pLinePor->Height() > nLineHeight) { nLineHeight = pLinePor->Height(); } pLinePor = pLinePor->GetNextPortion(); } } if (!bHadClearingBreak) { nLineHeight = rLine.Height(); } nOfst += ( nLineHeight - nPorHeight ) / 2 + nPorAscent; } break; } [[fallthrough]]; case SvxParaVertAlignItem::Align::Baseline : // base line if (pPor && pPor->GetHangingBaseline()) { nOfst += rLine.GetAscent() // Romn baseline of the line. - rLine.GetHangingBaseline() // Hanging baseline of the line. + pPor->GetHangingBaseline(); // Romn baseline of the portion. } else nOfst = nOfst + rLine.GetAscent(); break; } } return nOfst; } void SwTextIter::TwipsToLine( const SwTwips y) { while( m_nY + GetLineHeight() <= y && Next() ) ; while( m_nY > y && Prev() ) ; } // Local helper function to check, if pCurr needs a field rest portion: static bool lcl_NeedsFieldRest( const SwLineLayout* pCurr ) { const SwLinePortion *pPor = pCurr->GetNextPortion(); bool bRet = false; while( pPor && !bRet ) { bRet = pPor->InFieldGrp() && static_cast(pPor)->HasFollow(); if( !pPor->GetNextPortion() || !pPor->GetNextPortion()->InFieldGrp() ) break; pPor = pPor->GetNextPortion(); } return bRet; } void SwTextIter::TruncLines( bool bNoteFollow ) { SwLineLayout *pDel = m_pCurr->GetNext(); TextFrameIndex const nEnd = m_nStart + m_pCurr->GetLen(); if( pDel ) { m_pCurr->SetNext( nullptr ); if (MaybeHasHints() && bNoteFollow) { GetInfo().GetParaPortion()->SetFollowField( pDel->IsRest() || lcl_NeedsFieldRest( m_pCurr ) ); // bug 88534: wrong positioning of flys SwTextFrame* pFollow = GetTextFrame()->GetFollow(); if ( pFollow && ! pFollow->IsLocked() && nEnd == pFollow->GetOffset() ) { TextFrameIndex nRangeEnd = nEnd; SwLineLayout* pLine = pDel; // determine range to be searched for flys anchored as characters while ( pLine ) { nRangeEnd = nRangeEnd + pLine->GetLen(); pLine = pLine->GetNext(); } // examine hints in range nEnd - (nEnd + nRangeChar) SwTextNode const* pNode(nullptr); sw::MergedAttrIter iter(*GetTextFrame()); for (SwTextAttr const* pHt = iter.NextAttr(&pNode); pHt; pHt = iter.NextAttr(&pNode)) { if( RES_TXTATR_FLYCNT == pHt->Which() ) { // check if hint is in our range TextFrameIndex const nTmpPos( GetTextFrame()->MapModelToView(pNode, pHt->GetStart())); if ( nEnd <= nTmpPos && nTmpPos < nRangeEnd ) pFollow->InvalidateRange_( SwCharRange( nTmpPos, nTmpPos ) ); } } } } delete pDel; } if( m_pCurr->IsDummy() && !m_pCurr->GetLen() && m_nStart < TextFrameIndex(GetTextFrame()->GetText().getLength())) { m_pCurr->SetRealHeight( 1 ); } if (MaybeHasHints()) m_pFrame->RemoveFootnote( nEnd ); } void SwTextIter::CntHyphens( sal_uInt8 &nEndCnt, sal_uInt8 &nMidCnt) const { nEndCnt = 0; nMidCnt = 0; if ( m_bPrev && m_pPrev && !m_pPrev->IsEndHyph() && !m_pPrev->IsMidHyph() ) return; SwLineLayout *pLay = m_pInf->GetParaPortion(); if( m_pCurr == pLay ) return; while( pLay != m_pCurr ) { if ( pLay->IsEndHyph() ) nEndCnt++; else nEndCnt = 0; if ( pLay->IsMidHyph() ) nMidCnt++; else nMidCnt = 0; pLay = pLay->GetNext(); } } // Change current output device to formatting device, this has to be done before // formatting. SwHookOut::SwHookOut( SwTextSizeInfo& rInfo ) : pInf( &rInfo ), pOut( rInfo.GetOut() ), bOnWin( rInfo.OnWin() ) { OSL_ENSURE( rInfo.GetRefDev(), "No reference device for text formatting" ); // set new values rInfo.SetOut( rInfo.GetRefDev() ); rInfo.SetOnWin( false ); } SwHookOut::~SwHookOut() { pInf->SetOut( pOut ); pInf->SetOnWin( bOnWin ); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */