drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx | 2 editeng/inc/outleeng.hxx | 4 editeng/source/editeng/StripPortionsHelper.cxx | 267 ++++++++---- editeng/source/editeng/editeng.cxx | 13 editeng/source/editeng/impedit.hxx | 5 editeng/source/editeng/impedit3.cxx | 43 - editeng/source/outliner/outleeng.cxx | 7 editeng/source/outliner/outliner.cxx | 23 - include/editeng/StripPortionsHelper.hxx | 44 + include/editeng/editeng.hxx | 9 include/editeng/outliner.hxx | 10 include/svx/svdotext.hxx | 9 include/svx/svdoutl.hxx | 47 ++ sc/source/ui/view/tabview3.cxx | 28 - svx/source/svdraw/svdedxv.cxx | 6 svx/source/svdraw/svdotextdecomposition.cxx | 288 +------------ svx/source/svdraw/svdotextpathdecomposition.cxx | 23 - svx/source/svdraw/svdoutl.cxx | 105 ++++ 18 files changed, 476 insertions(+), 457 deletions(-)
New commits: commit 04bd455e36b054001e08a0a3256d508a009ffef3 Author: Armin Le Grand (Collabora) <armin.le.gr...@me.com> AuthorDate: Thu Jun 26 14:18:30 2025 +0200 Commit: Armin Le Grand <armin.le.gr...@me.com> CommitDate: Thu Jun 26 23:18:44 2025 +0200 StripPortions: Move tooling closer to EditEngine/Outliner II Moved central stuff needed for decompose to Primitives closer to EditEngine/Outliner. There is now a StripPortionsHelper as abstract base class with two virtual methods replacing the lambdas used before. That is easily implementable to all needs. Adapted all usages. Also the methods for creating needed Primitives from DrawPortionInfo or DrawBulletInfo is now local/private in EditEngine. This step should still change nothing in functionality, but is more preparation to be able to directly create Primitives from Outliner/EditEngine setups for paint and (re)use. Change-Id: Ie2bcebf4030128f88be229d789944cc2842eafb6 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/187045 Tested-by: Jenkins Reviewed-by: Armin Le Grand <armin.le.gr...@me.com> diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx index 80436df8cbdd..dc2383c29411 100644 --- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx @@ -1458,7 +1458,7 @@ void VclMetafileProcessor2D::processTextHierarchyBulletPrimitive2D( // process recursively and add MetaFile comment process(rBulletPrimitive); - // in Outliner::PaintBullet(), a MetafileComment for bullets is added, too. The + // in Outliner::PaintOrStripBullet(), a MetafileComment for bullets is added, too. The // "XTEXT_EOC" is used, use here, too. mpMetaFile->AddAction(new MetaCommentAction("XTEXT_EOC"_ostr)); diff --git a/editeng/inc/outleeng.hxx b/editeng/inc/outleeng.hxx index 5415c26810fb..b9def51b9ff0 100644 --- a/editeng/inc/outleeng.hxx +++ b/editeng/inc/outleeng.hxx @@ -35,9 +35,7 @@ public: OutlinerEditEng( Outliner* pOwner, SfxItemPool* pPool ); virtual ~OutlinerEditEng() override; - virtual void PaintingFirstLine(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet) override; + virtual void ProcessFirstLineOfParagraph(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, StripPortionsHelper* pStripPortionsHelper) override; virtual void ParagraphInserted( sal_Int32 nNewParagraph ) override; virtual void ParagraphDeleted( sal_Int32 nDeletedParagraph ) override; diff --git a/editeng/source/editeng/StripPortionsHelper.cxx b/editeng/source/editeng/StripPortionsHelper.cxx index 2d9312710860..61e9e939ca69 100644 --- a/editeng/source/editeng/StripPortionsHelper.cxx +++ b/editeng/source/editeng/StripPortionsHelper.cxx @@ -30,7 +30,7 @@ #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> #include <drawinglayer/primitive2d/graphicprimitive2d.hxx> -// anonymous helpers +// anonymous Outline/EditEngine decompose helpers namespace { rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> @@ -83,77 +83,6 @@ CheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, return xRet; } -class DoCapitalsDrawPortionInfo : public SvxDoCapitals -{ -private: - drawinglayer::primitive2d::Primitive2DContainer& mrTarget; - const basegfx::B2DHomMatrix& mrNewTransformA; - const basegfx::B2DHomMatrix& mrNewTransformB; - const DrawPortionInfo& m_rInfo; - SvxFont m_aFont; - -public: - DoCapitalsDrawPortionInfo(drawinglayer::primitive2d::Primitive2DContainer& rTarget, - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const DrawPortionInfo& rInfo) - : SvxDoCapitals(rInfo.maText, rInfo.mnTextStart, rInfo.mnTextLen) - , mrTarget(rTarget) - , mrNewTransformA(rNewTransformA) - , mrNewTransformB(rNewTransformB) - , m_rInfo(rInfo) - , m_aFont(rInfo.mrFont) - { - assert(!m_rInfo.mpDXArray.empty()); - - /* turn all these off as they are handled outside subportions for the whole portion */ - m_aFont.SetTransparent(false); - m_aFont.SetUnderline(LINESTYLE_NONE); - m_aFont.SetOverline(LINESTYLE_NONE); - m_aFont.SetStrikeout(STRIKEOUT_NONE); - - m_aFont.SetCaseMap(SvxCaseMap::NotMapped); /* otherwise this would call itself */ - } - virtual void Do(const OUString& rSpanTxt, const sal_Int32 nSpanIdx, const sal_Int32 nSpanLen, - const bool bUpper) override - { - sal_uInt8 nProp(0); - if (!bUpper) - { - nProp = m_aFont.GetPropr(); - m_aFont.SetProprRel(SMALL_CAPS_PERCENTAGE); - } - - sal_Int32 nStartOffset = nSpanIdx - nIdx; - double nStartX = nStartOffset ? m_rInfo.mpDXArray[nStartOffset - 1] : 0; - - Point aStartPos(m_rInfo.mrStartPos.X() + nStartX, m_rInfo.mrStartPos.Y()); - - KernArray aDXArray; - aDXArray.resize(nSpanLen); - for (sal_Int32 i = 0; i < nSpanLen; ++i) - aDXArray[i] = m_rInfo.mpDXArray[nStartOffset + i] - nStartX; - - auto aKashidaArray = !m_rInfo.mpKashidaArray.empty() - ? std::span<const sal_Bool>( - m_rInfo.mpKashidaArray.data() + nStartOffset, nSpanLen) - : std::span<const sal_Bool>(); - - DrawPortionInfo aInfo(aStartPos, rSpanTxt, nSpanIdx, nSpanLen, aDXArray, aKashidaArray, - m_aFont, m_rInfo.mnPara, m_rInfo.mnBiDiLevel, - nullptr, /* no spelling in subportion, handled outside */ - nullptr, /* no field in subportion, handled outside */ - false, false, false, m_rInfo.mpLocale, m_rInfo.maOverlineColor, - m_rInfo.maTextLineColor); - - CreateTextPortionPrimitivesFromDrawPortionInfo(mrTarget, mrNewTransformA, mrNewTransformB, - aInfo); - - if (!bUpper) - m_aFont.SetPropr(nProp); - } -}; - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> buildTextPortionPrimitive(const DrawPortionInfo& rInfo, const OUString& rText, const drawinglayer::attribute::FontAttribute& rFontAttribute, @@ -287,9 +216,42 @@ buildTextPortionPrimitive(const DrawPortionInfo& rInfo, const OUString& rText, return pNewPrimitive; } -} // end of anonymous namespace -// Outliner helpers +class DoCapitalsDrawPortionInfo : public SvxDoCapitals +{ +private: + drawinglayer::primitive2d::Primitive2DContainer& mrTarget; + const basegfx::B2DHomMatrix& mrNewTransformA; + const basegfx::B2DHomMatrix& mrNewTransformB; + const DrawPortionInfo& m_rInfo; + SvxFont m_aFont; + +public: + DoCapitalsDrawPortionInfo(drawinglayer::primitive2d::Primitive2DContainer& rTarget, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB, + const DrawPortionInfo& rInfo) + : SvxDoCapitals(rInfo.maText, rInfo.mnTextStart, rInfo.mnTextLen) + , mrTarget(rTarget) + , mrNewTransformA(rNewTransformA) + , mrNewTransformB(rNewTransformB) + , m_rInfo(rInfo) + , m_aFont(rInfo.mrFont) + { + assert(!m_rInfo.mpDXArray.empty()); + + /* turn all these off as they are handled outside subportions for the whole portion */ + m_aFont.SetTransparent(false); + m_aFont.SetUnderline(LINESTYLE_NONE); + m_aFont.SetOverline(LINESTYLE_NONE); + m_aFont.SetStrikeout(STRIKEOUT_NONE); + + m_aFont.SetCaseMap(SvxCaseMap::NotMapped); /* otherwise this would call itself */ + } + virtual void Do(const OUString& rSpanTxt, const sal_Int32 nSpanIdx, const sal_Int32 nSpanLen, + const bool bUpper) override; +}; + void CreateTextPortionPrimitivesFromDrawPortionInfo( drawinglayer::primitive2d::Primitive2DContainer& rTarget, const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, @@ -530,6 +492,44 @@ void CreateTextPortionPrimitivesFromDrawPortionInfo( } } +void DoCapitalsDrawPortionInfo::Do(const OUString& rSpanTxt, const sal_Int32 nSpanIdx, + const sal_Int32 nSpanLen, const bool bUpper) +{ + sal_uInt8 nProp(0); + if (!bUpper) + { + nProp = m_aFont.GetPropr(); + m_aFont.SetProprRel(SMALL_CAPS_PERCENTAGE); + } + + sal_Int32 nStartOffset = nSpanIdx - nIdx; + double nStartX = nStartOffset ? m_rInfo.mpDXArray[nStartOffset - 1] : 0; + + Point aStartPos(m_rInfo.mrStartPos.X() + nStartX, m_rInfo.mrStartPos.Y()); + + KernArray aDXArray; + aDXArray.resize(nSpanLen); + for (sal_Int32 i = 0; i < nSpanLen; ++i) + aDXArray[i] = m_rInfo.mpDXArray[nStartOffset + i] - nStartX; + + auto aKashidaArray + = !m_rInfo.mpKashidaArray.empty() + ? std::span<const sal_Bool>(m_rInfo.mpKashidaArray.data() + nStartOffset, nSpanLen) + : std::span<const sal_Bool>(); + + DrawPortionInfo aInfo( + aStartPos, rSpanTxt, nSpanIdx, nSpanLen, aDXArray, aKashidaArray, m_aFont, m_rInfo.mnPara, + m_rInfo.mnBiDiLevel, nullptr, /* no spelling in subportion, handled outside */ + nullptr, /* no field in subportion, handled outside */ + false, false, false, m_rInfo.mpLocale, m_rInfo.maOverlineColor, m_rInfo.maTextLineColor); + + CreateTextPortionPrimitivesFromDrawPortionInfo(mrTarget, mrNewTransformA, mrNewTransformB, + aInfo); + + if (!bUpper) + m_aFont.SetPropr(nProp); +} + void CreateDrawBulletPrimitivesFromDrawBulletInfo( drawinglayer::primitive2d::Primitive2DContainer& rTarget, const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, @@ -565,5 +565,124 @@ void CreateDrawBulletPrimitivesFromDrawBulletInfo( // add to output rTarget.push_back(pNewPrimitive); } +} // end of anonymous namespace + +void TextHierarchyBreakup::flushTextPortionPrimitivesToLinePrimitives() +{ + // only create a line primitive when we had content; there is no need for + // empty line primitives (contrary to paragraphs, see below). + if (!maTextPortionPrimitives.empty()) + { + maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D( + std::move(maTextPortionPrimitives))); + } +} + +sal_Int16 TextHierarchyBreakup::getOutlineLevelFromParagraph(sal_Int32 /*nPara*/) const +{ + return -1; +} + +sal_Int32 TextHierarchyBreakup::getParagraphCount() const { return 0; } + +void TextHierarchyBreakup::flushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara) +{ + // ALWAYS create a paragraph primitive, even when no content was added. This is done to + // have the correct paragraph count even with empty paragraphs. Those paragraphs will + // have an empty sub-PrimitiveSequence. + maParagraphPrimitives.push_back( + new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D( + std::move(maLinePrimitives), getOutlineLevelFromParagraph(nPara))); +} + +void TextHierarchyBreakup::processDrawPortionInfo(const DrawPortionInfo& rDrawPortionInfo) +{ + CreateTextPortionPrimitivesFromDrawPortionInfo(maTextPortionPrimitives, maNewTransformA, + maNewTransformB, rDrawPortionInfo); + + if (rDrawPortionInfo.mbEndOfLine || rDrawPortionInfo.mbEndOfParagraph) + { + flushTextPortionPrimitivesToLinePrimitives(); + } + + if (rDrawPortionInfo.mbEndOfParagraph) + { + flushLinePrimitivesToParagraphPrimitives(rDrawPortionInfo.mnPara); + } +} + +void TextHierarchyBreakup::processDrawBulletInfo(const DrawBulletInfo& rDrawBulletInfo) +{ + CreateDrawBulletPrimitivesFromDrawBulletInfo(maTextPortionPrimitives, maNewTransformA, + maNewTransformB, rDrawBulletInfo); + basegfx::B2DHomMatrix aNewTransform; + + // add size to new transform + aNewTransform.scale(rDrawBulletInfo.maBulletSize.getWidth(), + rDrawBulletInfo.maBulletSize.getHeight()); + + // apply transformA + aNewTransform *= maNewTransformA; + + // apply local offset + aNewTransform.translate(rDrawBulletInfo.maBulletPosition.X(), + rDrawBulletInfo.maBulletPosition.Y()); + + // also apply embedding object's transform + aNewTransform *= maNewTransformB; + + // prepare empty GraphicAttr + const GraphicAttr aGraphicAttr; + + // create GraphicPrimitive2D + const drawinglayer::primitive2d::Primitive2DReference aNewReference( + new drawinglayer::primitive2d::GraphicPrimitive2D( + aNewTransform, rDrawBulletInfo.maBulletGraphicObject, aGraphicAttr)); + + // embed in TextHierarchyBulletPrimitive2D + drawinglayer::primitive2d::Primitive2DContainer aNewSequence{ aNewReference }; + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive + = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence)); + + // add to output + maTextPortionPrimitives.push_back(pNewPrimitive); +} + +TextHierarchyBreakup::TextHierarchyBreakup() + : maTextPortionPrimitives() + , maLinePrimitives() + , maParagraphPrimitives() + , maNewTransformA() + , maNewTransformB() +{ +} + +TextHierarchyBreakup::TextHierarchyBreakup(const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB) + : maTextPortionPrimitives() + , maLinePrimitives() + , maParagraphPrimitives() + , maNewTransformA(rNewTransformA) + , maNewTransformB(rNewTransformB) +{ +} + +const drawinglayer::primitive2d::Primitive2DContainer& +TextHierarchyBreakup::getTextPortionPrimitives() +{ + if (!maTextPortionPrimitives.empty()) + { + // collect non-closed lines + flushTextPortionPrimitivesToLinePrimitives(); + } + + if (!maLinePrimitives.empty()) + { + // collect non-closed paragraphs + flushLinePrimitivesToParagraphPrimitives(getParagraphCount() - 1); + } + + return maParagraphPrimitives; +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/editeng/editeng.cxx b/editeng/source/editeng/editeng.cxx index 8a8a3029d6e1..3ffbecc83842 100644 --- a/editeng/source/editeng/editeng.cxx +++ b/editeng/source/editeng/editeng.cxx @@ -1071,13 +1071,8 @@ SvxFont EditEngine::GetStandardSvxFont( sal_Int32 nPara ) return pNode->GetCharAttribs().GetDefFont(); } -void EditEngine::StripPortions( - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet) +void EditEngine::StripPortions(StripPortionsHelper& rStripPortionsHelper) { - if (!rDrawPortion && !rDrawBullet) - return; - ScopedVclPtrInstance< VirtualDevice > aTmpDev; tools::Rectangle aBigRect( Point( 0, 0 ), Size( 0x7FFFFFFF, 0x7FFFFFFF ) ); if ( IsEffectivelyVertical() ) @@ -1094,7 +1089,7 @@ void EditEngine::StripPortions( } } - getImpl().Paint(*aTmpDev, aBigRect, Point(), 0_deg10, rDrawPortion, rDrawBullet); + getImpl().PaintOrStrip(*aTmpDev, aBigRect, Point(), 0_deg10, &rStripPortionsHelper); } void EditEngine::GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ) @@ -1584,9 +1579,7 @@ EditEngine::CreateTransferable(const ESelection& rSelection) // ====================== Virtual Methods ======================== -void EditEngine::PaintingFirstLine(sal_Int32, const Point&, const Point&, Degree10, OutputDevice&, - const std::function<void(const DrawPortionInfo&)>&, - const std::function<void(const DrawBulletInfo&)>&) +void EditEngine::ProcessFirstLineOfParagraph(sal_Int32, const Point&, const Point&, Degree10, OutputDevice&, StripPortionsHelper*) { } diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index e91e2f4a5191..1119d9b064f9 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -1004,10 +1004,7 @@ public: void UpdateViews( EditView* pCurView = nullptr ); Point CalculateTextPaintStartPosition(ImpEditView& rView) const; void Paint( ImpEditView* pView, const tools::Rectangle& rRect, OutputDevice* pTargetDevice ); - void Paint( - OutputDevice& rOutDev, tools::Rectangle aClipRect, Point aStartPos, Degree10 nOrientation = 0_deg10, - const std::function<void(const DrawPortionInfo&)>& rDrawPortion = std::function<void(const DrawPortionInfo&)>(), - const std::function<void(const DrawBulletInfo&)>& rDrawBullet = std::function<void(const DrawBulletInfo&)>()); + void PaintOrStrip( OutputDevice& rOutDev, tools::Rectangle aClipRect, Point aStartPos, Degree10 nOrientation = 0_deg10, StripPortionsHelper* pStripPortionsHelper = nullptr); bool MouseButtonUp( const MouseEvent& rMouseEvent, EditView* pView ); bool MouseButtonDown( const MouseEvent& rMouseEvent, EditView* pView ); diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 8eb2f8ee73b0..d4eda028ac23 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -3365,7 +3365,7 @@ void ImpEditEngine::Draw( OutputDevice& rOutDev, const Point& rStartPos, Degree1 aStartPos.AdjustX(GetPaperSize().Width() ); rStartPos.RotateAround(aStartPos, nOrientation); } - Paint(rOutDev, aBigRect, aStartPos, nOrientation); + PaintOrStrip(rOutDev, aBigRect, aStartPos, nOrientation); if (rOutDev.GetConnectMetaFile()) rOutDev.Pop(); } @@ -3428,7 +3428,7 @@ void ImpEditEngine::Draw( OutputDevice& rOutDev, const tools::Rectangle& rOutRec } } - Paint(rOutDev, aOutRect, aStartPos); + PaintOrStrip(rOutDev, aOutRect, aStartPos); if ( bMetafile ) rOutDev.Pop(); @@ -3439,11 +3439,9 @@ void ImpEditEngine::Draw( OutputDevice& rOutDev, const tools::Rectangle& rOutRec } // TODO: use IterateLineAreas in ImpEditEngine::Paint, to avoid algorithm duplication -void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Point aStartPos, Degree10 nOrientation, - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet) +void ImpEditEngine::PaintOrStrip( OutputDevice& rOutDev, tools::Rectangle aClipRect, Point aStartPos, Degree10 nOrientation, StripPortionsHelper* pStripPortionsHelper) { - if ( !IsUpdateLayout() && !rDrawPortion ) + if ( !IsUpdateLayout() && !pStripPortionsHelper ) return; if ( !IsFormatted() ) @@ -3527,14 +3525,13 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po // Why not just also call when stripping portions? This will give the correct values // and needs no position corrections in OutlinerEditEng::DrawingText which tries to call - // PaintBullet correctly; exactly what GetEditEnginePtr()->PaintingFirstLine + // PaintOrStripBullet correctly; exactly what GetEditEnginePtr()->ProcessFirstLineOfParagraph // does, too. No change for not-layouting (painting). if(0 == nLine) // && !bStripOnly) { Point aLineStart(aStartPos); adjustYDirectionAware(aLineStart, -nLineHeight); - GetEditEnginePtr()->PaintingFirstLine(nParaPortion, aLineStart, aOrigin, nOrientation, rOutDev, - rDrawPortion, rDrawBullet); + GetEditEnginePtr()->ProcessFirstLineOfParagraph(nParaPortion, aLineStart, aOrigin, nOrientation, rOutDev, pStripPortionsHelper);//, rDrawPortion, rDrawBullet); // Remember whether a bullet was painted. const SfxBoolItem& rBulletState = mpEditEngine->GetParaAttrib(nParaPortion, EE_PARA_BULLETSTATE); @@ -3576,9 +3573,8 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po { SeekCursor(rParaPortion.GetNode(), nIndex, aTmpFont, &rOutDev); - const auto* pRuby - = static_cast<const SvxRubyItem*>(pRubyAttr->GetItem()); - if (rDrawPortion) + const auto* pRuby = static_cast<const SvxRubyItem*>(pRubyAttr->GetItem()); + if (pStripPortionsHelper) { const bool bEndOfLine(nPortion == pLine->GetEndPortion()); const bool bEndOfParagraph(bEndOfLine && nLine + 1 == nLines); @@ -3598,8 +3594,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po {}, {}, aTmpFont, nParaPortion, 0, nullptr, nullptr, bEndOfLine, bEndOfParagraph, false, nullptr, aOverlineColor, aTextLineColor); - rDrawPortion(aInfo); - + pStripPortionsHelper->processDrawPortionInfo(aInfo); aTmpFont.SetFontSize(nPrevSz); } } @@ -3759,7 +3754,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po //It is not perfect, it still use lineBreaksList, so it won’t seek //word ends to wrap text there, but it would be difficult to change //this due to needed adaptations in EditEngine - if (rDrawPortion && !bParsingFields && pExtraInfo && !pExtraInfo->lineBreaksList.empty()) + if (pStripPortionsHelper && !bParsingFields && pExtraInfo && !pExtraInfo->lineBreaksList.empty()) { bParsingFields = true; itSubLines = pExtraInfo->lineBreaksList.begin(); @@ -3863,7 +3858,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po if (rTextPortion.IsRightToLeft()) aRedLineTmpPos.AdjustX(rTextPortion.GetSize().Width() ); - if ( rDrawPortion ) + if (pStripPortionsHelper) { EEngineData::WrongSpellVector aWrongSpellVector; @@ -3956,7 +3951,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po &aLocale, aOverlineColor, aTextLineColor); - rDrawPortion(aInfo); + pStripPortionsHelper->processDrawPortionInfo(aInfo); // #108052# remember that EOP is written already for this ParaPortion if(bEndOfParagraph) @@ -4181,7 +4176,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po comphelper::string::padToLength(aBuf, nChars, rTextPortion.GetExtraValue()); OUString aText(aBuf.makeStringAndClear()); - if ( rDrawPortion ) + if (pStripPortionsHelper) { // create EOL and EOP bools const bool bEndOfLine(nPortion == pLine->GetEndPortion()); @@ -4200,7 +4195,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po nullptr, aOverlineColor, aTextLineColor); - rDrawPortion(aInfo); + pStripPortionsHelper->processDrawPortionInfo(aInfo); } else { @@ -4209,7 +4204,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po rOutDev.DrawStretchText( aTmpPos, rTextPortion.GetSize().Width(), aText ); } } - else if ( rDrawPortion ) + else if (pStripPortionsHelper) { // #i108052# When stripping, a callback for _empty_ paragraphs is also needed. // This was optimized away (by not rendering the space-only tab portion), so do @@ -4229,7 +4224,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po nullptr, aOverlineColor, aTextLineColor); - rDrawPortion(aInfo); + pStripPortionsHelper->processDrawPortionInfo(aInfo); } } break; @@ -4265,7 +4260,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po // that the reason for #i108052# was fixed/removed again, so this is a try to fix // the number of paragraphs (and counting empty ones) now independent from the // changes in EditEngine behaviour. - if(!bEndOfParagraphWritten && !bPaintBullet && rDrawPortion) + if(!bEndOfParagraphWritten && !bPaintBullet && pStripPortionsHelper) { const Color aOverlineColor(rOutDev.GetOverlineColor()); const Color aTextLineColor(rOutDev.GetTextLineColor()); @@ -4279,7 +4274,7 @@ void ImpEditEngine::Paint( OutputDevice& rOutDev, tools::Rectangle aClipRect, Po nullptr, aOverlineColor, aTextLineColor); - rDrawPortion(aInfo); + pStripPortionsHelper->processDrawPortionInfo(aInfo); } } else @@ -4348,7 +4343,7 @@ void ImpEditEngine::Paint( ImpEditView* pView, const tools::Rectangle& rRect, Ou vcl::Region aOldRegion = rTarget.GetClipRegion(); rTarget.IntersectClipRegion( aClipRect ); - Paint(rTarget, aClipRect, aStartPos); + PaintOrStrip(rTarget, aClipRect, aStartPos); if ( bClipRegion ) rTarget.SetClipRegion( aOldRegion ); diff --git a/editeng/source/outliner/outleeng.cxx b/editeng/source/outliner/outleeng.cxx index 68e3dcca07ae..a4c193eb66ab 100644 --- a/editeng/source/outliner/outleeng.cxx +++ b/editeng/source/outliner/outleeng.cxx @@ -39,10 +39,7 @@ OutlinerEditEng::~OutlinerEditEng() { } -void OutlinerEditEng::PaintingFirstLine(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet -) +void OutlinerEditEng::ProcessFirstLineOfParagraph(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, StripPortionsHelper* pStripPortionsHelper)//, { if( GetControlWord() & EEControlBits::OUTLINER ) { @@ -50,7 +47,7 @@ void OutlinerEditEng::PaintingFirstLine(sal_Int32 nPara, const Point& rStartPos, pOwner->maPaintFirstLineHdl.Call( &aInfo ); } - pOwner->PaintBullet(nPara, rStartPos, rOrigin, nOrientation, rOutDev, rDrawPortion, rDrawBullet); + pOwner->PaintOrStripBullet(nPara, rStartPos, rOrigin, nOrientation, rOutDev, pStripPortionsHelper); } const SvxNumberFormat* OutlinerEditEng::GetNumberFormat( sal_Int32 nPara ) const diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx index 785ed710ff7d..f9f5ac0f72ab 100644 --- a/editeng/source/outliner/outliner.cxx +++ b/editeng/source/outliner/outliner.cxx @@ -870,11 +870,10 @@ vcl::Font Outliner::ImpCalcBulletFont( sal_Int32 nPara ) const return aBulletFont; } -void Outliner::PaintBullet( +void Outliner::PaintOrStripBullet( sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet) + StripPortionsHelper* pStripPortionsHelper) { bool bDrawBullet = false; @@ -958,7 +957,7 @@ void Outliner::PaintBullet( nLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl | vcl::text::ComplexTextLayoutFlags::TextOriginLeft | vcl::text::ComplexTextLayoutFlags::BiDiStrong; rOutDev.SetLayoutMode( nLayoutMode ); - if(rDrawPortion) + if (pStripPortionsHelper) { const SvxFont aSvxFont(rOutDev.GetFont()); KernArray aBuf; @@ -974,7 +973,7 @@ void Outliner::PaintBullet( const DrawPortionInfo aInfo( aTextPos, pPara->GetText(), 0, pPara->GetText().getLength(), aBuf, {}, aSvxFont, nPara, bRightToLeftPara ? 1 : 0, nullptr, nullptr, false, false, true, nullptr, Color(), Color()); - rDrawPortion(aInfo); + pStripPortionsHelper->processDrawPortionInfo(aInfo); } else { @@ -1010,7 +1009,7 @@ void Outliner::PaintBullet( } } - if(rDrawBullet) + if (pStripPortionsHelper) { // call something analog to aDrawPortionHdl (if set) and feed it something // analog to DrawPortionInfo... @@ -1020,7 +1019,7 @@ void Outliner::PaintBullet( *pFmt->GetBrush()->GetGraphicObject(), aBulletPos, pPara->aBulSize); - rDrawBullet(aDrawBulletInfo); + pStripPortionsHelper->processDrawBulletInfo(aDrawBulletInfo); } else { @@ -1031,7 +1030,7 @@ void Outliner::PaintBullet( } // In case of collapsed subparagraphs paint a line before the text. - if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) || rDrawPortion || nOrientation ) + if( !pParaList->HasChildren(pPara) || pParaList->HasVisibleChildren(pPara) || pStripPortionsHelper || nOrientation ) return; tools::Long nWidth = rOutDev.PixelToLogic( Size( 10, 0 ) ).Width(); @@ -1514,7 +1513,7 @@ tools::Rectangle Outliner::ImpCalcBulletArea( sal_Int32 nPara, bool bAdjust, boo ParagraphInfos aInfos = pEditEngine->GetParagraphInfos( nPara ); if ( aInfos.bValid ) { - aTopLeft.setY( /* aInfos.nFirstLineOffset + */ // nFirstLineOffset is already added to the StartPos (PaintBullet) from the EditEngine + aTopLeft.setY( /* aInfos.nFirstLineOffset + */ // nFirstLineOffset is already added to the StartPos (PaintOrStripBullet) from the EditEngine aInfos.nFirstLineHeight - aInfos.nFirstLineTextHeight + aInfos.nFirstLineTextHeight / 2 - aBulletSize.Height() / 2 ); @@ -1636,11 +1635,9 @@ void Outliner::Remove( Paragraph const * pPara, sal_Int32 nParaCount ) } } -void Outliner::StripPortions( - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet) +void Outliner::StripPortions(StripPortionsHelper& rStripPortionsHelper) { - pEditEngine->StripPortions(rDrawPortion, rDrawBullet); + pEditEngine->StripPortions(rStripPortionsHelper); } bool Outliner::RemovingPagesHdl( OutlinerView* pView ) diff --git a/include/editeng/StripPortionsHelper.hxx b/include/editeng/StripPortionsHelper.hxx index c9129cfd0d65..7da5cc88d516 100644 --- a/include/editeng/StripPortionsHelper.hxx +++ b/include/editeng/StripPortionsHelper.hxx @@ -30,6 +30,8 @@ #include <editeng/svxfont.hxx> #include <editeng/eedata.hxx> #include <editeng/flditem.hxx> +#include <drawinglayer/primitive2d/Primitive2DContainer.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> namespace com::sun::star::lang { @@ -103,23 +105,37 @@ public: } }; -namespace drawinglayer::primitive2d +class EDITENG_DLLPUBLIC StripPortionsHelper { -class Primitive2DContainer; -} -namespace basegfx +public: + virtual void processDrawPortionInfo(const DrawPortionInfo&) = 0; + virtual void processDrawBulletInfo(const DrawBulletInfo&) = 0; +}; + +class EDITENG_DLLPUBLIC TextHierarchyBreakup : public StripPortionsHelper { -class B2DHomMatrix; -} + drawinglayer::primitive2d::Primitive2DContainer maTextPortionPrimitives; + drawinglayer::primitive2d::Primitive2DContainer maLinePrimitives; + drawinglayer::primitive2d::Primitive2DContainer maParagraphPrimitives; + basegfx::B2DHomMatrix maNewTransformA; + basegfx::B2DHomMatrix maNewTransformB; -void EDITENG_DLLPUBLIC CreateTextPortionPrimitivesFromDrawPortionInfo( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, - const DrawPortionInfo& rInfo); -void EDITENG_DLLPUBLIC CreateDrawBulletPrimitivesFromDrawBulletInfo( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, - const DrawBulletInfo& rInfo); +protected: + void flushTextPortionPrimitivesToLinePrimitives(); + virtual sal_Int16 getOutlineLevelFromParagraph(sal_Int32 nPara) const; + virtual sal_Int32 getParagraphCount() const; + void flushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara); + +public: + virtual void processDrawPortionInfo(const DrawPortionInfo& rDrawPortionInfo); + virtual void processDrawBulletInfo(const DrawBulletInfo& rDrawBulletInfo); + + TextHierarchyBreakup(); + TextHierarchyBreakup(const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB); + + const drawinglayer::primitive2d::Primitive2DContainer& getTextPortionPrimitives(); +}; #endif // INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX diff --git a/include/editeng/editeng.hxx b/include/editeng/editeng.hxx index 3b0277f9335d..c50203fe4665 100644 --- a/include/editeng/editeng.hxx +++ b/include/editeng/editeng.hxx @@ -116,6 +116,7 @@ enum class TransliterationFlags; class LinkParamNone; class DrawPortionInfo; class DrawBulletInfo; +class StripPortionsHelper; /** values for: SfxItemSet GetAttribs( const ESelection& rSel, EditEngineAttribs nOnlyHardAttrib = EditEngineAttribs::All ); @@ -369,9 +370,7 @@ public: bool IsInSelectionMode() const; - void StripPortions( - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet); + void StripPortions(StripPortionsHelper& rStripPortionsHelper); void GetPortions( sal_Int32 nPara, std::vector<sal_Int32>& rList ); SAL_DLLPRIVATE tools::Long GetFirstLineStartX( sal_Int32 nParagraph ); @@ -492,9 +491,7 @@ public: SAL_DLLPRIVATE void SetBeginPasteOrDropHdl( const Link<PasteOrDropInfos&,void>& rLink ); SAL_DLLPRIVATE void SetEndPasteOrDropHdl( const Link<PasteOrDropInfos&,void>& rLink ); - virtual void PaintingFirstLine(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet); + virtual void ProcessFirstLineOfParagraph(sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, StripPortionsHelper* pStripPortionsHelper); virtual void ParagraphInserted( sal_Int32 nNewParagraph ); virtual void ParagraphDeleted( sal_Int32 nDeletedParagraph ); diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx index b2eb8b97bc1a..179e678a2add 100644 --- a/include/editeng/outliner.hxx +++ b/include/editeng/outliner.hxx @@ -82,6 +82,7 @@ enum class TextRotation; enum class SdrCompatibilityFlag; class DrawPortionInfo; class DrawBulletInfo; +class StripPortionsHelper; namespace com::sun::star::linguistic2 { class XSpellChecker1; @@ -577,12 +578,11 @@ protected: SAL_DLLPRIVATE void StyleSheetChanged( SfxStyleSheet const * pStyle ); SAL_DLLPRIVATE void InvalidateBullet(sal_Int32 nPara); - SAL_DLLPRIVATE void PaintBullet( + SAL_DLLPRIVATE void PaintOrStripBullet( sal_Int32 nPara, const Point& rStartPos, const Point& rOrigin, Degree10 nOrientation, OutputDevice& rOutDev, - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet); + StripPortionsHelper* pStripPortionsHelper); // used by OutlinerEditEng. Allows Outliner objects to provide // bullet access to the EditEngine. @@ -747,9 +747,7 @@ public: OUString const & GetWordDelimiters() const; OUString GetWord( const EPaM& rPos ); - void StripPortions( - const std::function<void(const DrawPortionInfo&)>& rDrawPortion, - const std::function<void(const DrawBulletInfo&)>& rDrawBullet); + void StripPortions(StripPortionsHelper& rStripPortionsHelper); Size CalcTextSize(); diff --git a/include/svx/svdotext.hxx b/include/svx/svdotext.hxx index 70a6de3d5bec..872474fc0cef 100644 --- a/include/svx/svdotext.hxx +++ b/include/svx/svdotext.hxx @@ -629,15 +629,6 @@ public: void impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const; void impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const; - // Direct decomposer for text visualization when you already have a prepared - // Outliner containing all the needed information - static void impDecomposeBlockTextPrimitiveDirect( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - SdrOutliner& rOutliner, - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const basegfx::B2DRange& rClipRange); - /** returns false if the given pointer is NULL or if the given SdrOutliner contains no text. Also checks for one empty paragraph. diff --git a/include/svx/svdoutl.hxx b/include/svx/svdoutl.hxx index 422399e17c23..5ef5985ef26b 100644 --- a/include/svx/svdoutl.hxx +++ b/include/svx/svdoutl.hxx @@ -23,6 +23,7 @@ #include <optional> #include <svx/svxdllapi.h> #include <unotools/weakref.hxx> +#include <editeng/StripPortionsHelper.hxx> class SdrTextObj; class SdrPage; @@ -51,5 +52,51 @@ public: virtual std::optional<bool> GetCompatFlag(SdrCompatibilityFlag eFlag) const override; }; +class TextHierarchyBreakupOutliner : public TextHierarchyBreakup +{ + SdrOutliner& mrOutliner; + +protected: + virtual sal_Int16 getOutlineLevelFromParagraph(sal_Int32 nPara) const; + virtual sal_Int32 getParagraphCount() const; + +public: + TextHierarchyBreakupOutliner( + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB); +}; + +class TextHierarchyBreakupBlockText : public TextHierarchyBreakupOutliner +{ + // ClipRange for BlockText decomposition; only text portions completely + // inside are to be accepted, so this is different from geometric clipping + // (which would allow e.g. upper parts of portions to remain) + const basegfx::B2DRange& mrClipRange; + +public: + virtual void processDrawPortionInfo(const DrawPortionInfo& rDrawPortionInfo); + + TextHierarchyBreakupBlockText( + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB, + const basegfx::B2DRange& rClipRange); +}; + +class TextHierarchyBreakupContourText : public TextHierarchyBreakupOutliner +{ + // the visible area for contour text decomposition + basegfx::B2DVector maScale; + +public: + virtual void processDrawPortionInfo(const DrawPortionInfo& rDrawPortionInfo); + + TextHierarchyBreakupContourText( + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB, + const basegfx::B2DVector& rScale); +}; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx index 1bbb272cced5..4509f7dc31a8 100644 --- a/sc/source/ui/view/tabview3.cxx +++ b/sc/source/ui/view/tabview3.cxx @@ -2299,32 +2299,18 @@ drawinglayer::primitive2d::Primitive2DContainer ScTextEditOverlayObject::createO const EditView* pEditView(rScViewData.GetEditView(maScSplitPos)); assert(pEditView && "NO access to EditView in ScTextEditOverlayObject!"); - // use no transformations. The result will be in logic coordinates - // based on aEditRectangle and the EditEngine setup, see - // ScViewData::SetEditEngine - basegfx::B2DHomMatrix aNewTransformA; - basegfx::B2DHomMatrix aNewTransformB; - // get text data in LogicMode OutputDevice& rOutDev(pEditView->GetOutputDevice()); const MapMode aOrig(rOutDev.GetMapMode()); rOutDev.SetMapMode(rScViewData.GetLogicMode()); - pEditView->getEditEngine().StripPortions( - [&aRetval, &aNewTransformA, &aNewTransformB](const DrawPortionInfo& rInfo){ - CreateTextPortionPrimitivesFromDrawPortionInfo( - aRetval, - aNewTransformA, - aNewTransformB, - rInfo); - }, - [&aRetval, &aNewTransformA, &aNewTransformB](const DrawBulletInfo& rInfo){ - CreateDrawBulletPrimitivesFromDrawBulletInfo( - aRetval, - aNewTransformA, - aNewTransformB, - rInfo); - }); + // StripPortions from EditEngine. + // use no transformations. The result will be in logic coordinates + // based on aEditRectangle and the EditEngine setup, see + // ScViewData::SetEditEngine + TextHierarchyBreakup aBreakup; + pEditView->getEditEngine().StripPortions(aBreakup); + aRetval = aBreakup.getTextPortionPrimitives(); rOutDev.SetMapMode(aOrig); return aRetval; diff --git a/svx/source/svdraw/svdedxv.cxx b/svx/source/svdraw/svdedxv.cxx index 3fd2c4a345cb..9f5ad0ea3695 100644 --- a/svx/source/svdraw/svdedxv.cxx +++ b/svx/source/svdraw/svdedxv.cxx @@ -783,8 +783,10 @@ void TextEditOverlayObject::checkDataChange(const basegfx::B2DRange& rMinTextEdi // of this mechanism, it *may* be possible to buffer layouted // primitives per ParaPortion with/in/dependent on the EditEngine // content if needed. For now, get and compare - SdrTextObj::impDecomposeBlockTextPrimitiveDirect( - aNewTextPrimitives, *pSdrOutliner, aNewTransformA, aNewTransformB, aClipRange); + TextHierarchyBreakupBlockText aBreakup(*pSdrOutliner, aNewTransformA, aNewTransformB, + aClipRange); + pSdrOutliner->StripPortions(aBreakup); + aNewTextPrimitives.append(aBreakup.getTextPortionPrimitives()); if (aNewTextPrimitives != maTextPrimitives) { diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx index a90694947368..7a6001d628e4 100644 --- a/svx/source/svdraw/svdotextdecomposition.cxx +++ b/svx/source/svdraw/svdotextdecomposition.cxx @@ -61,225 +61,6 @@ using namespace com::sun::star; -namespace -{ - class impTextBreakupHandler - { - private: - drawinglayer::primitive2d::Primitive2DContainer maTextPortionPrimitives; - drawinglayer::primitive2d::Primitive2DContainer maLinePrimitives; - drawinglayer::primitive2d::Primitive2DContainer maParagraphPrimitives; - - SdrOutliner& mrOutliner; - basegfx::B2DHomMatrix maNewTransformA; - basegfx::B2DHomMatrix maNewTransformB; - - // the visible area for contour text decomposition - basegfx::B2DVector maScale; - - // ClipRange for BlockText decomposition; only text portions completely - // inside are to be accepted, so this is different from geometric clipping - // (which would allow e.g. upper parts of portions to remain). Only used for - // BlockText (see there) - basegfx::B2DRange maClipRange; - - void impFlushTextPortionPrimitivesToLinePrimitives(); - void impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara); - void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo); - void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo); - - public: - explicit impTextBreakupHandler(SdrOutliner& rOutliner) - : mrOutliner(rOutliner) - { - } - - void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale) - { - maScale = rScale; - maNewTransformA = rNewTransformA; - maNewTransformB = rNewTransformB; - - mrOutliner.StripPortions( - [this](const DrawPortionInfo& rInfo){ - // for contour text, ignore (clip away) all portions which are below - // the visible area given by maScale - if(static_cast<double>(rInfo.mrStartPos.Y()) < maScale.getY()) - { - impHandleDrawPortionInfo(rInfo); - } - }, - [this](const DrawBulletInfo& rInfo){ impHandleDrawBulletInfo(rInfo); }); - } - - void decomposeBlockTextPrimitive( - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const basegfx::B2DRange& rClipRange) - { - maNewTransformA = rNewTransformA; - maNewTransformB = rNewTransformB; - maClipRange = rClipRange; - - mrOutliner.StripPortions( - [this](const DrawPortionInfo& rInfo){ - // Is clipping wanted? This is text clipping; only accept a portion - // if it's completely in the range - if(!maClipRange.isEmpty()) - { - // Test start position first; this allows to not get the text range at - // all if text is far outside - const basegfx::B2DPoint aStartPosition(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()); - - if(!maClipRange.isInside(aStartPosition)) - { - return; - } - - // Start position is inside. Get TextBoundRect and TopLeft next - drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; - aTextLayouterDevice.setFont(rInfo.mrFont); - - const basegfx::B2DRange aTextBoundRect( - aTextLayouterDevice.getTextBoundRect( - rInfo.maText, rInfo.mnTextStart, rInfo.mnTextLen)); - const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition); - - if(!maClipRange.isInside(aTopLeft)) - { - return; - } - - // TopLeft is inside. Get BottomRight and check - const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition); - - if(!maClipRange.isInside(aBottomRight)) - { - return; - } - - // all inside, clip was successful - } - impHandleDrawPortionInfo(rInfo); - }, - [this](const DrawBulletInfo& rInfo){ impHandleDrawBulletInfo(rInfo); }); - } - - void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB) - { - maNewTransformA = rNewTransformA; - maNewTransformB = rNewTransformB; - - mrOutliner.StripPortions( - [this](const DrawPortionInfo& rInfo){ impHandleDrawPortionInfo(rInfo); }, - [this](const DrawBulletInfo& rInfo){ impHandleDrawBulletInfo(rInfo); }); - } - - drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence(); - }; - - void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives() - { - // only create a line primitive when we had content; there is no need for - // empty line primitives (contrary to paragraphs, see below). - if(!maTextPortionPrimitives.empty()) - { - maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(std::move(maTextPortionPrimitives))); - } - } - - void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara) - { - sal_Int16 nDepth = mrOutliner.GetDepth(nPara); - EBulletInfo eInfo = mrOutliner.GetBulletInfo(nPara); - // Pass -1 to signal VclMetafileProcessor2D that there is no active - // bullets/numbering in this paragraph (i.e. this is normal text) - const sal_Int16 nOutlineLevel( eInfo.bVisible ? nDepth : -1); - - // ALWAYS create a paragraph primitive, even when no content was added. This is done to - // have the correct paragraph count even with empty paragraphs. Those paragraphs will - // have an empty sub-PrimitiveSequence. - maParagraphPrimitives.push_back( - new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D( - std::move(maLinePrimitives), - nOutlineLevel)); - } - - void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo) - { - CreateTextPortionPrimitivesFromDrawPortionInfo( - maTextPortionPrimitives, - maNewTransformA, - maNewTransformB, - rInfo); - - if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph) - { - impFlushTextPortionPrimitivesToLinePrimitives(); - } - - if(rInfo.mbEndOfParagraph) - { - impFlushLinePrimitivesToParagraphPrimitives(rInfo.mnPara); - } - } - - void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo) - { - CreateDrawBulletPrimitivesFromDrawBulletInfo( - maTextPortionPrimitives, - maNewTransformA, - maNewTransformB, - rInfo); - basegfx::B2DHomMatrix aNewTransform; - - // add size to new transform - aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight()); - - // apply transformA - aNewTransform *= maNewTransformA; - - // apply local offset - aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y()); - - // also apply embedding object's transform - aNewTransform *= maNewTransformB; - - // prepare empty GraphicAttr - const GraphicAttr aGraphicAttr; - - // create GraphicPrimitive2D - const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D( - aNewTransform, - rInfo.maBulletGraphicObject, - aGraphicAttr)); - - // embed in TextHierarchyBulletPrimitive2D - drawinglayer::primitive2d::Primitive2DContainer aNewSequence { aNewReference }; - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence)); - - // add to output - maTextPortionPrimitives.push_back(pNewPrimitive); - } - - drawinglayer::primitive2d::Primitive2DContainer impTextBreakupHandler::extractPrimitive2DSequence() - { - if(!maTextPortionPrimitives.empty()) - { - // collect non-closed lines - impFlushTextPortionPrimitivesToLinePrimitives(); - } - - if(!maLinePrimitives.empty()) - { - // collect non-closed paragraphs - impFlushLinePrimitivesToParagraphPrimitives(mrOutliner.GetParagraphCount() - 1); - } - - return std::move(maParagraphPrimitives); - } -} // end of anonymous namespace - // primitive decompositions void SdrTextObj::impDecomposeContourTextPrimitive( drawinglayer::primitive2d::Primitive2DContainer& rTarget, @@ -370,15 +151,17 @@ void SdrTextObj::impDecomposeContourTextPrimitive( // now break up text primitives. If it has a fat stroke, createTextPrimitive() has created a // ScaledUnitPolyPolygon. Thus aPolyPolygon might be smaller than aScale from aObjectMatrix. We // use this smaller size for the text area, otherwise the text will reach into the stroke. - impTextBreakupHandler aConverter(rOutliner); - aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, - aPolyPolygon.getB2DRange().getRange()); + TextHierarchyBreakupContourText aBreakup( + rOutliner, + aNewTransformA, + aNewTransformB, + aPolyPolygon.getB2DRange().getRange()); + rOutliner.StripPortions(aBreakup); + rTarget = aBreakup.getTextPortionPrimitives(); // cleanup outliner rOutliner.Clear(); rOutliner.setVisualizedPage(nullptr); - - rTarget = aConverter.extractPrimitive2DSequence(); } void SdrTextObj::impDecomposeAutoFitTextPrimitive( @@ -519,16 +302,19 @@ void SdrTextObj::impDecomposeAutoFitTextPrimitive( basegfx::B2DRange aClipRange; // now break up text primitives. - impTextBreakupHandler aConverter(rOutliner); - aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange); + TextHierarchyBreakupBlockText aBreakup( + rOutliner, + aNewTransformA, + aNewTransformB, + aClipRange); + rOutliner.StripPortions(aBreakup); + rTarget = aBreakup.getTextPortionPrimitives(); // cleanup outliner rOutliner.SetBackgroundColor(aOriginalBackColor); rOutliner.Clear(); rOutliner.setVisualizedPage(nullptr); rOutliner.SetControlWord(nOriginalControlWord); - - rTarget = aConverter.extractPrimitive2DSequence(); } // Resolves: fdo#35779 set background color of this shape as the editeng background if there @@ -833,15 +619,18 @@ void SdrTextObj::impDecomposeBlockTextPrimitive( aClipRange = {0, 0, std::numeric_limits<double>::max(), aAnchorTextRange.getHeight()}; // now break up text primitives. - impTextBreakupHandler aConverter(rOutliner); - aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange); + TextHierarchyBreakupBlockText aBreakup( + rOutliner, + aNewTransformA, + aNewTransformB, + aClipRange); + rOutliner.StripPortions(aBreakup); + rTarget = aBreakup.getTextPortionPrimitives(); // cleanup outliner rOutliner.SetBackgroundColor(aOriginalBackColor); rOutliner.Clear(); rOutliner.setVisualizedPage(nullptr); - - rTarget = aConverter.extractPrimitive2DSequence(); } void SdrTextObj::impDecomposeStretchTextPrimitive( @@ -914,15 +703,17 @@ void SdrTextObj::impDecomposeStretchTextPrimitive( fShearX, fRotate, aTranslate.getX(), aTranslate.getY())); // now break up text primitives. - impTextBreakupHandler aConverter(rOutliner); - aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB); + TextHierarchyBreakupOutliner aBreakup( + rOutliner, + aNewTransformA, + aNewTransformB); + rOutliner.StripPortions(aBreakup); + rTarget = aBreakup.getTextPortionPrimitives(); // cleanup outliner rOutliner.SetControlWord(nOriginalControlWord); rOutliner.Clear(); rOutliner.setVisualizedPage(nullptr); - - rTarget = aConverter.extractPrimitive2DSequence(); } @@ -1341,29 +1132,18 @@ void SdrTextObj::impDecomposeChainedTextPrimitive( basegfx::B2DRange aClipRange; // now break up text primitives. - impTextBreakupHandler aConverter(rOutliner); - aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange); + TextHierarchyBreakupBlockText aBreakup( + rOutliner, + aNewTransformA, + aNewTransformB, + aClipRange); + rOutliner.StripPortions(aBreakup); + rTarget = aBreakup.getTextPortionPrimitives(); // cleanup outliner rOutliner.Clear(); rOutliner.setVisualizedPage(nullptr); rOutliner.SetControlWord(nOriginalControlWord); - - rTarget = aConverter.extractPrimitive2DSequence(); -} - -// Direct decomposer for text visualization when you already have a prepared -// Outliner containing all the needed information -void SdrTextObj::impDecomposeBlockTextPrimitiveDirect( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - SdrOutliner& rOutliner, - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const basegfx::B2DRange& rClipRange) -{ - impTextBreakupHandler aConverter(rOutliner); - aConverter.decomposeBlockTextPrimitive(rNewTransformA, rNewTransformB, rClipRange); - rTarget.append(aConverter.extractPrimitive2DSequence()); } double SdrTextObj::GetCameraZRotation() const diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx b/svx/source/svdraw/svdotextpathdecomposition.cxx index 8d3876f57810..14c9693b3d64 100644 --- a/svx/source/svdraw/svdotextpathdecomposition.cxx +++ b/svx/source/svdraw/svdotextpathdecomposition.cxx @@ -149,24 +149,24 @@ namespace namespace { - class impTextBreakupHandler + class TextHierarchyBreakupPathTextPortions : public StripPortionsHelper { - SdrOutliner& mrOutliner; ::std::vector< impPathTextPortion > maPathTextPortions; public: - explicit impTextBreakupHandler(SdrOutliner& rOutliner) - : mrOutliner(rOutliner) + virtual void processDrawPortionInfo(const DrawPortionInfo& rDrawPortionInfo) { + // extract and add data for TextOnPath further processing + maPathTextPortions.emplace_back(rDrawPortionInfo); } - const ::std::vector< impPathTextPortion >& decompositionPathTextPrimitive() + virtual void processDrawBulletInfo(const DrawBulletInfo&) { - // strip portions to maPathTextPortions - mrOutliner.StripPortions( - [this](const DrawPortionInfo& rInfo){ maPathTextPortions.emplace_back(rInfo); }, - std::function<void(const DrawBulletInfo&)>()); + // nothing to do here, bullets are for now ignored for TextOnLine + } + const ::std::vector< impPathTextPortion >& sortAndGetPathTextPortions() + { if(!maPathTextPortions.empty()) { // sort portions by paragraph, x and y @@ -635,8 +635,9 @@ void SdrTextObj::impDecomposePathTextPrimitive( rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage())); // now break up to text portions - impTextBreakupHandler aConverter(rOutliner); - const ::std::vector< impPathTextPortion > rPathTextPortions = aConverter.decompositionPathTextPrimitive(); + TextHierarchyBreakupPathTextPortions aBreakup; + rOutliner.StripPortions(aBreakup); + const ::std::vector< impPathTextPortion > rPathTextPortions(aBreakup.sortAndGetPathTextPortions()); if(!rPathTextPortions.empty()) { diff --git a/svx/source/svdraw/svdoutl.cxx b/svx/source/svdraw/svdoutl.cxx index 2002b5f05392..7b4cf2499e36 100644 --- a/svx/source/svdraw/svdoutl.cxx +++ b/svx/source/svdraw/svdoutl.cxx @@ -27,6 +27,7 @@ #include <svl/itempool.hxx> #include <editeng/editview.hxx> #include <editeng/editeng.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> SdrOutliner::SdrOutliner( SfxItemPool* pItemPool, OutlinerMode nMode ) @@ -121,4 +122,108 @@ std::optional<bool> SdrOutliner::GetCompatFlag(SdrCompatibilityFlag eFlag) const return {}; } +sal_Int16 TextHierarchyBreakupOutliner::getOutlineLevelFromParagraph(sal_Int32 nPara) const +{ + sal_Int16 nDepth(mrOutliner.GetDepth(nPara)); + EBulletInfo eInfo(mrOutliner.GetBulletInfo(nPara)); + // Pass -1 to signal VclMetafileProcessor2D that there is no active + // bullets/numbering in this paragraph (i.e. this is normal text) + return eInfo.bVisible ? nDepth : -1; +} + +sal_Int32 TextHierarchyBreakupOutliner::getParagraphCount() const +{ + return mrOutliner.GetParagraphCount(); +} + +TextHierarchyBreakupOutliner::TextHierarchyBreakupOutliner( + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB) +: TextHierarchyBreakup( + rNewTransformA, + rNewTransformB) +, mrOutliner(rOutliner) +{ +} + +void TextHierarchyBreakupBlockText::processDrawPortionInfo(const DrawPortionInfo& rDrawPortionInfo) +{ + // Is clipping wanted? This is text clipping; only accept a portion + // if it's completely in the range + if(!mrClipRange.isEmpty()) + { + // Test start position first; this allows to not get the text range at + // all if text is far outside + const basegfx::B2DPoint aStartPosition(rDrawPortionInfo.mrStartPos.X(), rDrawPortionInfo.mrStartPos.Y()); + + if(!mrClipRange.isInside(aStartPosition)) + { + return; + } + + // Start position is inside. Get TextBoundRect and TopLeft next + drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice; + aTextLayouterDevice.setFont(rDrawPortionInfo.mrFont); + + const basegfx::B2DRange aTextBoundRect( + aTextLayouterDevice.getTextBoundRect( + rDrawPortionInfo.maText, rDrawPortionInfo.mnTextStart, rDrawPortionInfo.mnTextLen)); + const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition); + + if(!mrClipRange.isInside(aTopLeft)) + { + return; + } + + // TopLeft is inside. Get BottomRight and check + const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition); + + if(!mrClipRange.isInside(aBottomRight)) + { + return; + } + + // all inside, clip was successful + } + + TextHierarchyBreakupOutliner::processDrawPortionInfo(rDrawPortionInfo); +} + +TextHierarchyBreakupBlockText::TextHierarchyBreakupBlockText( + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB, + const basegfx::B2DRange& rClipRange) +: TextHierarchyBreakupOutliner( + rOutliner, + rNewTransformA, + rNewTransformB) +, mrClipRange(rClipRange) +{ +} + +void TextHierarchyBreakupContourText::processDrawPortionInfo(const DrawPortionInfo& rDrawPortionInfo) + { + // for contour text, ignore (clip away) all portions which are below + // the visible area given by maScale + if(static_cast<double>(rDrawPortionInfo.mrStartPos.Y()) < maScale.getY()) + { + TextHierarchyBreakupOutliner::processDrawPortionInfo(rDrawPortionInfo); + } + } + +TextHierarchyBreakupContourText::TextHierarchyBreakupContourText( + SdrOutliner& rOutliner, + const basegfx::B2DHomMatrix& rNewTransformA, + const basegfx::B2DHomMatrix& rNewTransformB, + const basegfx::B2DVector& rScale) +: TextHierarchyBreakupOutliner( + rOutliner, + rNewTransformA, + rNewTransformB) +, maScale(rScale) +{ +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */