/* -*- 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 "portab.hxx" #include #include #include #include #include #include #include #include #include #include #include #include "porfly.hxx" #include "porlay.hxx" #include "inftxt.hxx" #include #include /** * class SwFlyPortion => we expect a frame-locale SwRect! */ void SwFlyPortion::Paint( const SwTextPaintInfo& ) const { } bool SwFlyPortion::Format( SwTextFormatInfo &rInf ) { OSL_ENSURE( GetFix() >= rInf.X(), "SwFlyPortion::Format" ); // tabs must be expanded if( rInf.GetLastTab() ) rInf.GetLastTab()->FormatEOL( rInf ); rInf.GetLast()->FormatEOL( rInf ); PrtWidth( static_cast(GetFix() - rInf.X() + PrtWidth()) ); if( !Width() ) { OSL_ENSURE( Width(), "+SwFlyPortion::Format: a fly is a fly is a fly" ); Width(1); } // resetting rInf.SetFly( nullptr ); rInf.Width( rInf.RealWidth() ); rInf.GetParaPortion()->SetFly(); // trailing blank: if( rInf.GetIdx() < TextFrameIndex(rInf.GetText().getLength()) && TextFrameIndex(1) < rInf.GetIdx() && !rInf.GetRest() && ' ' == rInf.GetChar( rInf.GetIdx() ) && ' ' != rInf.GetChar(rInf.GetIdx() - TextFrameIndex(1)) && ( !rInf.GetLast() || !rInf.GetLast()->IsBreakPortion() ) ) { SetBlankWidth( rInf.GetTextSize(OUString(' ')).Width() ); SetLen(TextFrameIndex(1)); } const sal_uInt16 nNewWidth = static_cast(rInf.X() + PrtWidth()); if( rInf.Width() <= nNewWidth ) { Truncate(); if( nNewWidth > rInf.Width() ) { PrtWidth( nNewWidth - rInf.Width() ); SetFixWidth( PrtWidth() ); } return true; } return false; } bool SwFlyCntPortion::Format( SwTextFormatInfo &rInf ) { bool bFull = rInf.Width() < rInf.X() + PrtWidth(); if( bFull ) { // If the line is full, and the character-bound frame is at // the beginning of a line // If it is not possible to side step into a Fly // "Begin of line" criteria ( ! rInf.X() ) has to be extended. // KerningPortions at beginning of line, e.g., for grid layout // must be considered. const SwLinePortion* pLastPor = rInf.GetLast(); const sal_uInt16 nLeft = ( pLastPor && ( pLastPor->IsKernPortion() || pLastPor->IsErgoSumPortion() ) ) ? pLastPor->Width() : 0; if( nLeft == rInf.X() && ! rInf.GetFly() ) { Width( rInf.Width() ); bFull = false; // so that notes can still be placed in this line } else { if( !rInf.GetFly() ) rInf.SetNewLine( true ); Width(0); SetAscent(0); SetLen(TextFrameIndex(0)); if( rInf.GetLast() ) rInf.GetLast()->FormatEOL( rInf ); return bFull; } } rInf.GetParaPortion()->SetFly(); return bFull; } //TODO: improve documentation /** move character-bound objects inside the given area * * This allows moving those objects from Master to Follow, or vice versa. * * @param pNew * @param nStart * @param nEnd */ void SwTextFrame::MoveFlyInCnt(SwTextFrame *pNew, TextFrameIndex const nStart, TextFrameIndex const nEnd) { SwSortedObjs *pObjs = GetDrawObjs(); if ( nullptr == pObjs ) return; for ( size_t i = 0; GetDrawObjs() && i < pObjs->size(); ++i ) { // Consider changed type of entries SwAnchoredObject* pAnchoredObj = (*pObjs)[i]; const SwFormatAnchor& rAnch = pAnchoredObj->GetFrameFormat().GetAnchor(); if (rAnch.GetAnchorId() == RndStdIds::FLY_AS_CHAR) { const SwPosition* pPos = rAnch.GetContentAnchor(); TextFrameIndex const nIndex(MapModelToViewPos(*pPos)); if (nStart <= nIndex && nIndex < nEnd) { if ( auto pFlyFrame = dynamic_cast( pAnchoredObj ) ) { RemoveFly( pFlyFrame ); pNew->AppendFly( pFlyFrame ); } else if ( dynamic_cast< const SwAnchoredDrawObject *>( pAnchoredObj ) != nullptr ) { RemoveDrawObj( *pAnchoredObj ); pNew->AppendDrawObj( *pAnchoredObj ); } --i; } } } } TextFrameIndex SwTextFrame::CalcFlyPos( SwFrameFormat const * pSearch ) { sw::MergedAttrIter iter(*this); for (SwTextAttr const* pHt = iter.NextAttr(); pHt; pHt = iter.NextAttr()) { if( RES_TXTATR_FLYCNT == pHt->Which() ) { SwFrameFormat* pFrameFormat = pHt->GetFlyCnt().GetFrameFormat(); if( pFrameFormat == pSearch ) { return TextFrameIndex(pHt->GetStart()); } } } OSL_ENSURE(false, "CalcFlyPos: Not Found!"); return TextFrameIndex(COMPLETE_STRING); } void sw::FlyContentPortion::Paint(const SwTextPaintInfo& rInf) const { // Baseline output // Re-paint everything at a CompletePaint call SwRect aRepaintRect(rInf.GetPaintRect()); if(rInf.GetTextFrame()->IsRightToLeft()) rInf.GetTextFrame()->SwitchLTRtoRTL(aRepaintRect); if(rInf.GetTextFrame()->IsVertical()) rInf.GetTextFrame()->SwitchHorizontalToVertical(aRepaintRect); if(!((m_pFly->IsCompletePaint() || m_pFly->getFrameArea().IsOver(aRepaintRect)) && SwFlyFrame::IsPaint(m_pFly->GetVirtDrawObj(), m_pFly->getRootFrame()->GetCurrShell()))) return; SwRect aRect(m_pFly->getFrameArea()); if(!m_pFly->IsCompletePaint()) aRect.Intersection_(aRepaintRect); // GetFlyFrame() may change the layout mode at the output device. { SwLayoutModeModifier aLayoutModeModifier(*rInf.GetOut()); m_pFly->PaintSwFrame(const_cast(*rInf.GetOut()), aRect); // track changes: cross out the image, if it is deleted const SwFrame *pFrame = m_pFly->Lower(); if ( IsDeleted() && pFrame ) { SwRect aPaintRect( pFrame->GetPaintArea() ); const AntialiasingFlags nFormerAntialiasing( rInf.GetOut()->GetAntialiasing() ); const bool bIsAntiAliasing = officecfg::Office::Common::Drawinglayer::AntiAliasing::get(); if ( bIsAntiAliasing ) const_cast(*rInf.GetOut()).SetAntialiasing(AntialiasingFlags::Enable); tools::Long startX = aPaintRect.Left( ), endX = aPaintRect.Right(); tools::Long startY = aPaintRect.Top( ), endY = aPaintRect.Bottom(); const_cast(*rInf.GetOut()).SetLineColor(NON_PRINTING_CHARACTER_COLOR); const_cast(*rInf.GetOut()).DrawLine(Point(startX, startY), Point(endX, endY)); const_cast(*rInf.GetOut()).DrawLine(Point(startX, endY), Point(endX, startY)); if ( bIsAntiAliasing ) const_cast(*rInf.GetOut()).SetAntialiasing(nFormerAntialiasing); } } const_cast(rInf).GetRefDev()->SetLayoutMode(rInf.GetOut()->GetLayoutMode()); // As the OutputDevice might be anything, the font must be re-selected. // Being in const method should not be a problem. const_cast(rInf).SelectFont(); assert(rInf.GetVsh()); SAL_WARN_IF(rInf.GetVsh()->GetOut() != rInf.GetOut(), "sw.core", "SwFlyCntPortion::Paint: Outdev has changed"); if(rInf.GetVsh()) const_cast(rInf).SetOut(rInf.GetVsh()->GetOut()); } void sw::DrawFlyCntPortion::Paint(const SwTextPaintInfo&) const { if(!m_pContact->GetAnchorFrame()) { // No direct positioning of the drawing object is needed m_pContact->ConnectToLayout(); } } /** * Use the dimensions of pFly->OutRect() */ SwFlyCntPortion::SwFlyCntPortion() : m_bMax(false) , m_eAlign(sw::LineAlign::NONE) { mnLineLength = TextFrameIndex(1); SetWhichPor(PortionType::FlyCnt); } sw::FlyContentPortion::FlyContentPortion(SwFlyInContentFrame* pFly) : m_pFly(pFly) { SAL_WARN_IF(!pFly, "sw.core", "SwFlyCntPortion::SwFlyCntPortion: no SwFlyInContentFrame!"); } sw::DrawFlyCntPortion::DrawFlyCntPortion(SwFrameFormat const & rFormat) : m_pContact(nullptr) { rFormat.CallSwClientNotify(sw::CreatePortionHint(&m_pContact)); assert(m_pContact); } sw::FlyContentPortion* sw::FlyContentPortion::Create(const SwTextFrame& rFrame, SwFlyInContentFrame* pFly, const Point& rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags) { auto pNew(new sw::FlyContentPortion(pFly)); pNew->SetBase(rFrame, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags | AsCharFlags::UlSpace | AsCharFlags::Init); return pNew; } sw::DrawFlyCntPortion* sw::DrawFlyCntPortion::Create(const SwTextFrame& rFrame, SwFrameFormat const & rFormat, const Point& rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags) { auto pNew(new DrawFlyCntPortion(rFormat)); pNew->SetBase(rFrame, rBase, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc, nFlags | AsCharFlags::UlSpace | AsCharFlags::Init); return pNew; } sw::DrawFlyCntPortion::~DrawFlyCntPortion() {}; sw::FlyContentPortion::~FlyContentPortion() {}; SdrObject* sw::FlyContentPortion::GetSdrObj(const SwTextFrame&) { return m_pFly->GetVirtDrawObj(); } SdrObject* sw::DrawFlyCntPortion::GetSdrObj(const SwTextFrame& rFrame) { SdrObject* pSdrObj; // Determine drawing object ('master' or 'virtual') by frame pSdrObj = m_pContact->GetDrawObjectByAnchorFrame(rFrame); if(!pSdrObj) { SAL_WARN("sw.core", "SwFlyCntPortion::SetBase(..) - No drawing object found by GetDrawObjectByAnchorFrame( rFrame )>"); pSdrObj = m_pContact->GetMaster(); } // Call to assure that flag at // the and at the instance are // correctly set if(pSdrObj) m_pContact->GetAnchoredObj(pSdrObj)->MakeObjPos(); return pSdrObj; } /** * After setting the RefPoints, the ascent needs to be recalculated * because it is dependent on RelPos * * @param rBase CAUTION: needs to be an absolute value! */ void SwFlyCntPortion::SetBase( const SwTextFrame& rFrame, const Point &rBase, tools::Long nLnAscent, tools::Long nLnDescent, tools::Long nFlyAsc, tools::Long nFlyDesc, AsCharFlags nFlags ) { // Use new class to position object // Determine drawing object SdrObject* pSdrObj = GetSdrObj(rFrame); if (!pSdrObj) return; // position object objectpositioning::SwAsCharAnchoredObjectPosition aObjPositioning( *pSdrObj, rBase, nFlags, nLnAscent, nLnDescent, nFlyAsc, nFlyDesc ); // Scope of local variable { SwObjPositioningInProgress aObjPosInProgress( *pSdrObj ); aObjPositioning.CalcPosition(); } SwFrameFormat* pShape = FindFrameFormat(pSdrObj); const SwFormatAnchor& rAnchor(pShape->GetAnchor()); if (rAnchor.GetAnchorId() == RndStdIds::FLY_AS_CHAR) { // This is an inline draw shape, see if it has a textbox. SwFrameFormat* pTextBox = SwTextBoxHelper::getOtherTextBoxFormat(pShape, RES_DRAWFRMFMT); if (pTextBox) { // It has, so look up its text rectangle, and adjust the position // of the textbox accordingly. // Both rectangles are absolute, SwFormatHori/VertOrient's position // is relative to the print area of the anchor text frame. tools::Rectangle aTextRectangle = SwTextBoxHelper::getTextRectangle(pShape); tools::Long nXoffs = SwTextBoxHelper::getTextRectangle(pShape, false).getX(); const auto aPos(pShape->GetAnchor().GetContentAnchor()); SwFormatVertOrient aVert(pTextBox->GetVertOrient()); SwFormatHoriOrient aHori(pTextBox->GetHoriOrient()); // tdf#138598 Replace vertical alignment of As_char textboxes in footer // tdf#140158 Remove horizontal positioning of As_char textboxes, because // the anchor moving does the same for it. if (!aPos->nNode.GetNode().FindFooterStartNode()) { aVert.SetVertOrient(css::text::VertOrientation::NONE); aVert.SetRelationOrient(css::text::RelOrientation::FRAME); sal_Int32 const nTop = aTextRectangle.getY() - rFrame.getFrameArea().Top() - rFrame.getFramePrintArea().Top(); aVert.SetPos(nTop); } else { aVert.SetVertOrient(css::text::VertOrientation::NONE); aVert.SetPos(SwTextBoxHelper::getTextRectangle(pShape, false).getY()); } SwFormatAnchor aNewTxBxAnchor(pTextBox->GetAnchor()); aNewTxBxAnchor.SetAnchor(aPos); aHori.SetPos(nXoffs); pTextBox->LockModify(); pTextBox->SetFormatAttr(aNewTxBxAnchor); pTextBox->SetFormatAttr(aVert); pTextBox->SetFormatAttr(aHori); pTextBox->UnlockModify(); } } SetAlign( aObjPositioning.GetLineAlignment() ); m_aRef = aObjPositioning.GetAnchorPos(); if( nFlags & AsCharFlags::Rotate ) SvXSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() ); else SvLSize( aObjPositioning.GetObjBoundRectInclSpacing().SSize() ); if( Height() ) { // GetRelPosY returns the relative position to baseline (if 0, the // upper border of the FlyCnt if on the baseline of a line) SwTwips nRelPos = aObjPositioning.GetRelPosY(); if ( nRelPos < 0 ) { mnAscent = static_cast(-nRelPos); if( mnAscent > Height() ) Height( mnAscent ); } else { mnAscent = 0; Height( Height() + static_cast(nRelPos) ); } } else { Height( 1 ); mnAscent = 0; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */