editeng/CppunitTest_editeng_core.mk | 2 editeng/CppunitTest_editeng_editeng.mk | 2 editeng/Library_editeng.mk | 3 editeng/source/editeng/StripPortionsHelper.cxx | 569 ++++++++++++++++++++++++ editeng/source/editeng/impedit3.cxx | 2 editeng/source/outliner/outliner.cxx | 1 include/editeng/StripPortionsHelper.hxx | 126 +++++ include/editeng/outliner.hxx | 80 --- include/svx/svdotext.hxx | 13 sc/source/ui/view/tabview3.cxx | 1 svx/source/svdraw/svdotextdecomposition.cxx | 558 ----------------------- svx/source/svdraw/svdotextpathdecomposition.cxx | 1 12 files changed, 710 insertions(+), 648 deletions(-)
New commits: commit 782131d3a3574290edbe3a15d7f85d7b99dcd3fb Author: Armin Le Grand (Collabora) <[email protected]> AuthorDate: Fri May 30 15:50:41 2025 +0200 Commit: Armin Le Grand <[email protected]> CommitDate: Fri Jun 6 11:37:06 2025 +0200 StripPortions: Move tooling closer to EditEngine/Outliner EditEngine/Outliner::StripPortions is used with some lambda callbacks to convert Text to Primitives. The tooling for this is currently located in svx due to historical reasons (when I initially did that). An EditEngine/Outliner gets configured, this is dependent of the object type to handle (see TextBreakupHandler in svx and other places). Then the OutlinerParaObject gets set - the model data. EditEngine::StripPortions then initially uses ImpEditEngine::Paint which layouts all that and formats it. For all Text (or Graphics e.g. Bullets) those callbacks are used - if given. If not given it *still* does a direct paint to a given OutputDevice to stay compatible with legacy usages. Thus ImpEditEngine::Paint is currently kind of an hybrid: It needs to be capable of both: For Primitive creation it extracts data to DrawPortionInfo or DrawBulletInfo and calls the lambda callback where (e.g. in svx) the Primitives are created as needed based on that data. For Paint that data is used to Paint using the given OutputDevice. This means up to now for doing changes both had to be supported in that method which of course hosts the danger of getting unequal outputs when not being very careful. The DirectPaint is now rarely used (AFAIK only in some UI elements, e.g. WeldEditView stuff). The goal is to go in a direction where it would be possible to do those remaining Paint calls direcly inside ImpEditEngine using primitives, or - alternatively - change all remaining places to use a unified StripPortions method and send the result to a PrimitiveRenderer or buffer/resue it. This change is a 1st step to get the preconditions but does not change functionality yet. It moves that tooling to where it is used. Change-Id: I7225eab9a0dfd3d50cd3d1d8fe386dfb99ec42ef Reviewed-on: https://gerrit.libreoffice.org/c/core/+/186045 Reviewed-by: Armin Le Grand <[email protected]> Tested-by: Jenkins diff --git a/editeng/CppunitTest_editeng_core.mk b/editeng/CppunitTest_editeng_core.mk index 64f17ae3ba4a..8852a3c6ffdd 100644 --- a/editeng/CppunitTest_editeng_core.mk +++ b/editeng/CppunitTest_editeng_core.mk @@ -29,6 +29,8 @@ $(eval $(call gb_CppunitTest_use_libraries,editeng_core, \ cppu \ cppuhelper \ docmodel \ + drawinglayercore \ + drawinglayer \ i18nlangtag \ i18nutil \ lng \ diff --git a/editeng/CppunitTest_editeng_editeng.mk b/editeng/CppunitTest_editeng_editeng.mk index 6c2d20917e86..5e6096adc051 100644 --- a/editeng/CppunitTest_editeng_editeng.mk +++ b/editeng/CppunitTest_editeng_editeng.mk @@ -23,6 +23,8 @@ $(eval $(call gb_CppunitTest_use_libraries,editeng_editeng, \ cppu \ cppuhelper \ docmodel \ + drawinglayercore \ + drawinglayer \ i18nlangtag \ i18nutil \ lng \ diff --git a/editeng/Library_editeng.mk b/editeng/Library_editeng.mk index 05ced9a8614a..1def031b017b 100644 --- a/editeng/Library_editeng.mk +++ b/editeng/Library_editeng.mk @@ -73,6 +73,7 @@ $(eval $(call gb_Library_add_exception_objects,editeng,\ editeng/source/editeng/impedit5 \ editeng/source/editeng/misspellrange \ editeng/source/editeng/section \ + editeng/source/editeng/StripPortionsHelper \ editeng/source/editeng/textconv \ editeng/source/editeng/TextPortionList \ editeng/source/items/borderline \ @@ -142,6 +143,8 @@ $(eval $(call gb_Library_use_libraries,editeng,\ xo \ basegfx \ docmodel \ + drawinglayercore \ + drawinglayer \ lng \ svt \ tk \ diff --git a/editeng/source/editeng/StripPortionsHelper.cxx b/editeng/source/editeng/StripPortionsHelper.cxx new file mode 100644 index 000000000000..2d9312710860 --- /dev/null +++ b/editeng/source/editeng/StripPortionsHelper.cxx @@ -0,0 +1,569 @@ +/* -*- 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/StripPortionsHelper.hxx> +#include <drawinglayer/attribute/fontattribute.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <editeng/escapementitem.hxx> +#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> +#include <editeng/smallcaps.hxx> +#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> +#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> +#include <drawinglayer/primitive2d/textbreakuphelper.hxx> +#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> +#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> +#include <drawinglayer/primitive2d/graphicprimitive2d.hxx> + +// anonymous helpers +namespace +{ +rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> +CheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, + const DrawPortionInfo& rInfo) +{ + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> xRet = pPrimitive; + if (rInfo.mpFieldData) + { + // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D + // which holds the field type and, if applicable, the URL + const SvxURLField* pURLField = dynamic_cast<const SvxURLField*>(rInfo.mpFieldData); + const SvxPageField* pPageField = dynamic_cast<const SvxPageField*>(rInfo.mpFieldData); + + // embed current primitive to a sequence + drawinglayer::primitive2d::Primitive2DContainer aSequence; + + if (pPrimitive) + { + aSequence.resize(1); + aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive); + } + + if (pURLField) + { + // extended this to hold more of the contents of the original + // SvxURLField since that stuff is still used in HitTest and e.g. Calc + std::vector<std::pair<OUString, OUString>> meValues; + meValues.emplace_back("URL", pURLField->GetURL()); + meValues.emplace_back("Representation", pURLField->GetRepresentation()); + meValues.emplace_back("TargetFrame", pURLField->GetTargetFrame()); + meValues.emplace_back("AltText", pURLField->GetName()); + meValues.emplace_back( + "SvxURLFormat", OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat()))); + xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D( + std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues); + } + else if (pPageField) + { + xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D( + std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_PAGE); + } + else + { + xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D( + std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_COMMON); + } + } + + 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, + const std::vector<double>& rDXArray, + const basegfx::B2DHomMatrix& rNewTransform) +{ + ::std::vector<sal_Bool> aKashidaArray; + + if (!rInfo.mpKashidaArray.empty() && rInfo.mnTextLen) + { + aKashidaArray.reserve(rInfo.mnTextLen); + + for (sal_Int32 a = 0; a < rInfo.mnTextLen; a++) + { + aKashidaArray.push_back(rInfo.mpKashidaArray[a]); + } + } + + // create complex text primitive and append + const Color aFontColor(rInfo.mrFont.GetColor()); + const basegfx::BColor aBFontColor(aFontColor.getBColor()); + + const Color aTextFillColor(rInfo.mrFont.GetFillColor()); + + // prepare wordLineMode (for underline and strikeout) + // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)' + // to be split which would not look like the original + const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet); + + // prepare new primitive + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive; + const bool bDecoratedIsNeeded( + LINESTYLE_NONE != rInfo.mrFont.GetOverline() + || LINESTYLE_NONE != rInfo.mrFont.GetUnderline() + || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout() + || FontEmphasisMark::NONE != (rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style) + || FontRelief::NONE != rInfo.mrFont.GetRelief() || rInfo.mrFont.IsShadow() + || bWordLineMode); + + if (bDecoratedIsNeeded) + { + // TextDecoratedPortionPrimitive2D needed, prepare some more data + // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead + const Color aUnderlineColor(rInfo.maTextLineColor); + const basegfx::BColor aBUnderlineColor( + (aUnderlineColor == COL_AUTO) ? aBFontColor : aUnderlineColor.getBColor()); + const Color aOverlineColor(rInfo.maOverlineColor); + const basegfx::BColor aBOverlineColor( + (aOverlineColor == COL_AUTO) ? aBFontColor : aOverlineColor.getBColor()); + + // prepare overline and underline data + const drawinglayer::primitive2d::TextLine eFontOverline( + drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetOverline())); + const drawinglayer::primitive2d::TextLine eFontLineStyle( + drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetUnderline())); + + // check UnderlineAbove + const bool bUnderlineAbove(drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle + && rInfo.mrFont.IsUnderlineAbove()); + + // prepare strikeout data + const drawinglayer::primitive2d::TextStrikeout eTextStrikeout( + drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout( + rInfo.mrFont.GetStrikeout())); + + // prepare emphasis mark data + drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark( + drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE); + + switch (rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style) + { + case FontEmphasisMark::Dot: + eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; + break; + case FontEmphasisMark::Circle: + eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; + break; + case FontEmphasisMark::Disc: + eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; + break; + case FontEmphasisMark::Accent: + eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; + break; + default: + break; + } + + const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosAbove); + const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosBelow); + + // prepare font relief data + drawinglayer::primitive2d::TextRelief eTextRelief( + drawinglayer::primitive2d::TEXT_RELIEF_NONE); + + switch (rInfo.mrFont.GetRelief()) + { + case FontRelief::Embossed: + eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; + break; + case FontRelief::Engraved: + eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; + break; + default: + break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE + } + + // prepare shadow/outline data + const bool bShadow(rInfo.mrFont.IsShadow()); + + // TextDecoratedPortionPrimitive2D is needed, create one + pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( + + // attributes for TextSimplePortionPrimitive2D + rNewTransform, rText, rInfo.mnTextStart, rInfo.mnTextLen, std::vector(rDXArray), + std::move(aKashidaArray), rFontAttribute, + rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(), aBFontColor, aTextFillColor, + + // attributes for TextDecoratedPortionPrimitive2D + aBOverlineColor, aBUnderlineColor, eFontOverline, eFontLineStyle, bUnderlineAbove, + eTextStrikeout, bWordLineMode, eTextEmphasisMark, bEmphasisMarkAbove, + bEmphasisMarkBelow, eTextRelief, bShadow); + } + else + { + // TextSimplePortionPrimitive2D is enough + pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + rNewTransform, rText, rInfo.mnTextStart, rInfo.mnTextLen, std::vector(rDXArray), + std::move(aKashidaArray), rFontAttribute, + rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(), aBFontColor, aTextFillColor); + } + + return pNewPrimitive; +} +} // end of anonymous namespace + +// Outliner helpers +void CreateTextPortionPrimitivesFromDrawPortionInfo( + drawinglayer::primitive2d::Primitive2DContainer& rTarget, + const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, + const DrawPortionInfo& rInfo) +{ + if (rInfo.maText.isEmpty() || !rInfo.mnTextLen) + return; + + basegfx::B2DVector aFontScaling; + drawinglayer::attribute::FontAttribute aFontAttribute( + drawinglayer::primitive2d::getFontAttributeFromVclFont(aFontScaling, rInfo.mrFont, + rInfo.IsRTL(), false)); + basegfx::B2DHomMatrix aNewTransform; + + // add font scale to new transform + aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY()); + + // look for proportional font scaling, if necessary, scale accordingly + sal_Int8 nPropr(rInfo.mrFont.GetPropr()); + const double fPropFontFactor(nPropr / 100.0); + if (100 != nPropr) + aNewTransform.scale(fPropFontFactor, fPropFontFactor); + + // apply font rotate + if (rInfo.mrFont.GetOrientation()) + { + aNewTransform.rotate(-toRadians(rInfo.mrFont.GetOrientation())); + } + + // look for escapement, if necessary, translate accordingly + if (rInfo.mrFont.GetEscapement()) + { + sal_Int16 nEsc(rInfo.mrFont.GetEscapement()); + + if (DFLT_ESC_AUTO_SUPER == nEsc) + { + nEsc = .8 * (100 - nPropr); + assert(nEsc == DFLT_ESC_SUPER + && "I'm sure this formula needs to be changed, but how to confirm that???"); + nEsc = DFLT_ESC_SUPER; + } + else if (DFLT_ESC_AUTO_SUB == nEsc) + { + nEsc = .2 * -(100 - nPropr); + assert(nEsc == -20 + && "I'm sure this formula needs to be changed, but how to confirm that???"); + nEsc = -20; + } + + if (nEsc > MAX_ESC_POS) + { + nEsc = MAX_ESC_POS; + } + else if (nEsc < -MAX_ESC_POS) + { + nEsc = -MAX_ESC_POS; + } + + const double fEscapement(nEsc / -100.0); + aNewTransform.translate(0.0, fEscapement * aFontScaling.getY()); + } + + // apply transformA + aNewTransform *= rNewTransformA; + + // apply local offset + aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()); + + // also apply embedding object's transform + aNewTransform *= rNewTransformB; + + // prepare DXArray content. To make it independent from font size (and such from + // the text transformation), scale it to unit coordinates + ::std::vector<double> aDXArray; + + if (!rInfo.mpDXArray.empty()) + { + aDXArray.reserve(rInfo.mnTextLen); + for (sal_Int32 a = 0; a < rInfo.mnTextLen; a++) + { + aDXArray.push_back(rInfo.mpDXArray[a]); + } + } + + OUString caseMappedText = rInfo.mrFont.CalcCaseMap(rInfo.maText); + rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive( + buildTextPortionPrimitive(rInfo, caseMappedText, aFontAttribute, aDXArray, aNewTransform)); + + bool bSmallCaps = rInfo.mrFont.IsCapital(); + if (bSmallCaps && rInfo.mpDXArray.empty()) + { + SAL_WARN("svx", "SmallCaps requested with DXArray, abandoning"); + bSmallCaps = false; + } + if (bSmallCaps) + { + // rerun with each sub-portion + DoCapitalsDrawPortionInfo aDoDrawPortionInfo(rTarget, rNewTransformA, rNewTransformB, + rInfo); + rInfo.mrFont.DoOnCapitals(aDoDrawPortionInfo); + + // transfer collected primitives from rTarget to a new container + drawinglayer::primitive2d::Primitive2DContainer aContainer = std::move(rTarget); + + // Take any decoration for the whole formatted portion and keep it to get continuous over/under/strike-through + if (pNewPrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D) + { + const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* pTCPP + = static_cast<const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>( + pNewPrimitive.get()); + + if (pTCPP->getWordLineMode()) // single word mode: 'Individual words' in UI + { + // Split to single word primitives using TextBreakupHelper + drawinglayer::primitive2d::TextBreakupHelper aTextBreakupHelper(*pTCPP); + drawinglayer::primitive2d::Primitive2DContainer aBroken( + aTextBreakupHelper.extractResult(drawinglayer::primitive2d::BreakupUnit::Word)); + for (auto& rPortion : aBroken) + { + assert(rPortion->getPrimitive2DID() + == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D + && "TextBreakupHelper generates same output primitive type as input"); + + const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* pPortion + = static_cast< + const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>( + rPortion.get()); + + // create and add decoration + const drawinglayer::primitive2d::Primitive2DContainer& + rDecorationGeometryContent(pPortion->getOrCreateDecorationGeometryContent( + pPortion->getTextTransform(), caseMappedText, + pPortion->getTextPosition(), pPortion->getTextLength(), + pPortion->getDXArray())); + + aContainer.insert(aContainer.end(), rDecorationGeometryContent.begin(), + rDecorationGeometryContent.end()); + } + } + else + { + // create and add decoration + const drawinglayer::primitive2d::Primitive2DContainer& rDecorationGeometryContent( + pTCPP->getOrCreateDecorationGeometryContent(pTCPP->getTextTransform(), + caseMappedText, rInfo.mnTextStart, + rInfo.mnTextLen, aDXArray)); + + aContainer.insert(aContainer.end(), rDecorationGeometryContent.begin(), + rDecorationGeometryContent.end()); + } + } + + pNewPrimitive = new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer)); + } + + const Color aFontColor(rInfo.mrFont.GetColor()); + if (aFontColor.IsTransparent()) + { + // Handle semi-transparent text for both the decorated and simple case here. + pNewPrimitive = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( + drawinglayer::primitive2d::Primitive2DContainer{ pNewPrimitive }, + (255 - aFontColor.GetAlpha()) / 255.0); + } + + if (rInfo.mbEndOfBullet) + { + // embed in TextHierarchyBulletPrimitive2D + drawinglayer::primitive2d::Primitive2DContainer aNewSequence{ pNewPrimitive }; + pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D( + std::move(aNewSequence)); + } + + if (rInfo.mpFieldData) + { + pNewPrimitive = CheckFieldPrimitive(pNewPrimitive.get(), rInfo); + } + + rTarget.push_back(pNewPrimitive); + + // support for WrongSpellVector. Create WrongSpellPrimitives as needed + if (!rInfo.mpWrongSpellVector || aDXArray.empty()) + return; + + const sal_Int32 nSize(rInfo.mpWrongSpellVector->size()); + const sal_Int32 nDXCount(aDXArray.size()); + const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded + + for (sal_Int32 a(0); a < nSize; a++) + { + const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a]; + + if (rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart + && rCandidate.nEnd > rCandidate.nStart) + { + const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart); + const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart); + double fStart(0.0); + double fEnd(0.0); + + if (nStart > 0 && nStart - 1 < nDXCount) + { + fStart = aDXArray[nStart - 1]; + } + + if (nEnd > 0 && nEnd - 1 < nDXCount) + { + fEnd = aDXArray[nEnd - 1]; + } + + if (!basegfx::fTools::equal(fStart, fEnd)) + { + if (rInfo.IsRTL()) + { + // #i98523# + // When the portion is RTL, mirror the redlining using the + // full portion width + const double fTextWidth(aDXArray[aDXArray.size() - 1]); + + fStart = fTextWidth - fStart; + fEnd = fTextWidth - fEnd; + } + + // need to take FontScaling out of values; it's already part of + // aNewTransform and would be double applied + const double fFontScaleX(aFontScaling.getX() * fPropFontFactor); + + if (!basegfx::fTools::equal(fFontScaleX, 1.0) + && !basegfx::fTools::equalZero(fFontScaleX)) + { + fStart /= fFontScaleX; + fEnd /= fFontScaleX; + } + + rTarget.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D( + aNewTransform, fStart, fEnd, aSpellColor)); + } + } + } +} + +void CreateDrawBulletPrimitivesFromDrawBulletInfo( + drawinglayer::primitive2d::Primitive2DContainer& rTarget, + const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, + const DrawBulletInfo& rInfo) +{ + basegfx::B2DHomMatrix aNewTransform; + + // add size to new transform + aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight()); + + // apply transformA + aNewTransform *= rNewTransformA; + + // apply local offset + aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y()); + + // also apply embedding object's transform + aNewTransform *= rNewTransformB; + + // 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 + rTarget.push_back(pNewPrimitive); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 01159e457e21..8eb2f8ee73b0 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -47,8 +47,10 @@ #include <editeng/scriptspaceitem.hxx> #include <editeng/charscaleitem.hxx> #include <editeng/numitem.hxx> +#include <editeng/StripPortionsHelper.hxx> #include <outleeng.hxx> #include <TextPortion.hxx> +#include <tools/gen.hxx> #include <svtools/colorcfg.hxx> #include <svl/ctloptions.hxx> diff --git a/editeng/source/outliner/outliner.cxx b/editeng/source/outliner/outliner.cxx index bef30c2ecb01..785ed710ff7d 100644 --- a/editeng/source/outliner/outliner.cxx +++ b/editeng/source/outliner/outliner.cxx @@ -50,6 +50,7 @@ #include <o3tl/string_view.hxx> #include <o3tl/temporary.hxx> #include <osl/diagnose.h> +#include <editeng/StripPortionsHelper.hxx> #include <memory> using std::advance; diff --git a/include/editeng/StripPortionsHelper.hxx b/include/editeng/StripPortionsHelper.hxx new file mode 100644 index 000000000000..c9129cfd0d65 --- /dev/null +++ b/include/editeng/StripPortionsHelper.hxx @@ -0,0 +1,126 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX +#define INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX + +#include <editeng/editengdllapi.h> + +#include <tools/gen.hxx> +#include <tools/color.hxx> +#include <rtl/ustring.hxx> +#include <vcl/kernarray.hxx> +#include <vcl/GraphicObject.hxx> +#include <editeng/svxfont.hxx> +#include <editeng/eedata.hxx> +#include <editeng/flditem.hxx> + +namespace com::sun::star::lang +{ +struct Locale; +} + +class EDITENG_DLLPUBLIC DrawPortionInfo +{ +public: + const Point& mrStartPos; + const OUString maText; + sal_Int32 mnTextStart; + sal_Int32 mnTextLen; + KernArraySpan mpDXArray; + std::span<const sal_Bool> mpKashidaArray; + const SvxFont& mrFont; + sal_Int32 mnPara; + sal_uInt8 mnBiDiLevel; + const EEngineData::WrongSpellVector* mpWrongSpellVector; + const SvxFieldData* mpFieldData; + bool mbEndOfLine : 1; + bool mbEndOfParagraph : 1; + bool mbEndOfBullet : 1; + const com::sun::star::lang::Locale* mpLocale; + const Color maOverlineColor; + const Color maTextLineColor; + + bool IsRTL() const { return mnBiDiLevel % 2 == 1; } + + DrawPortionInfo(const Point& rPos, OUString aTxt, sal_Int32 nTxtStart, sal_Int32 nTxtLen, + KernArraySpan pDXArr, std::span<const sal_Bool> pKashidaArr, + const SvxFont& rFnt, sal_Int32 nPar, sal_uInt8 nBiDiLevel, + const EEngineData::WrongSpellVector* pWrongSpellVector, + const SvxFieldData* pFieldData, bool bEndOfLine, bool bEndOfParagraph, + bool bEndOfBullet, const com::sun::star::lang::Locale* pLocale, + const Color& rOverlineColor, const Color& rTextLineColor) + : mrStartPos(rPos) + , maText(std::move(aTxt)) + , mnTextStart(nTxtStart) + , mnTextLen(nTxtLen) + , mpDXArray(pDXArr) + , mpKashidaArray(pKashidaArr) + , mrFont(rFnt) + , mnPara(nPar) + , mnBiDiLevel(nBiDiLevel) + , mpWrongSpellVector(pWrongSpellVector) + , mpFieldData(pFieldData) + , mbEndOfLine(bEndOfLine) + , mbEndOfParagraph(bEndOfParagraph) + , mbEndOfBullet(bEndOfBullet) + , mpLocale(pLocale) + , maOverlineColor(rOverlineColor) + , maTextLineColor(rTextLineColor) + { + } +}; + +class EDITENG_DLLPUBLIC DrawBulletInfo +{ +public: + const GraphicObject maBulletGraphicObject; + Point maBulletPosition; + Size maBulletSize; + + DrawBulletInfo(const GraphicObject& rBulletGraphicObject, const Point& rBulletPosition, + const Size& rBulletSize) + : maBulletGraphicObject(rBulletGraphicObject) + , maBulletPosition(rBulletPosition) + , maBulletSize(rBulletSize) + { + } +}; + +namespace drawinglayer::primitive2d +{ +class Primitive2DContainer; +} +namespace basegfx +{ +class B2DHomMatrix; +} + +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); + +#endif // INCLUDED_EDITENG_STRIPPORTIONSHELPER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/include/editeng/outliner.hxx b/include/editeng/outliner.hxx index 8be662616ce4..b2eb8b97bc1a 100644 --- a/include/editeng/outliner.hxx +++ b/include/editeng/outliner.hxx @@ -80,6 +80,8 @@ enum class PointerStyle; class SvxNumRule; enum class TextRotation; enum class SdrCompatibilityFlag; +class DrawPortionInfo; +class DrawBulletInfo; namespace com::sun::star::linguistic2 { class XSpellChecker1; @@ -399,84 +401,6 @@ void EDITENG_DLLPUBLIC ReplaceTextWithSynonym( EditView &rEditView, const OUStr typedef ::std::vector< OutlinerView* > ViewList; -class EDITENG_DLLPUBLIC DrawPortionInfo -{ -public: - const Point& mrStartPos; - const OUString maText; - sal_Int32 mnTextStart; - sal_Int32 mnTextLen; - KernArraySpan mpDXArray; - std::span<const sal_Bool> mpKashidaArray; - const SvxFont& mrFont; - sal_Int32 mnPara; - sal_uInt8 mnBiDiLevel; - const EEngineData::WrongSpellVector* mpWrongSpellVector; - const SvxFieldData* mpFieldData; - bool mbEndOfLine : 1; - bool mbEndOfParagraph : 1; - bool mbEndOfBullet : 1; - const css::lang::Locale* mpLocale; - const Color maOverlineColor; - const Color maTextLineColor; - - bool IsRTL() const { return mnBiDiLevel % 2 == 1; } - - DrawPortionInfo( - const Point& rPos, - OUString aTxt, - sal_Int32 nTxtStart, - sal_Int32 nTxtLen, - KernArraySpan pDXArr, - std::span<const sal_Bool> pKashidaArr, - const SvxFont& rFnt, - sal_Int32 nPar, - sal_uInt8 nBiDiLevel, - const EEngineData::WrongSpellVector* pWrongSpellVector, - const SvxFieldData* pFieldData, - bool bEndOfLine, - bool bEndOfParagraph, - bool bEndOfBullet, - const css::lang::Locale* pLocale, - const Color& rOverlineColor, - const Color& rTextLineColor) - : mrStartPos(rPos), - maText(std::move(aTxt)), - mnTextStart(nTxtStart), - mnTextLen(nTxtLen), - mpDXArray(pDXArr), - mpKashidaArray(pKashidaArr), - mrFont(rFnt), - mnPara(nPar), - mnBiDiLevel(nBiDiLevel), - mpWrongSpellVector(pWrongSpellVector), - mpFieldData(pFieldData), - mbEndOfLine(bEndOfLine), - mbEndOfParagraph(bEndOfParagraph), - mbEndOfBullet(bEndOfBullet), - mpLocale(pLocale), - maOverlineColor(rOverlineColor), - maTextLineColor(rTextLineColor) - {} -}; - -class EDITENG_DLLPUBLIC DrawBulletInfo -{ -public: - const GraphicObject maBulletGraphicObject; - Point maBulletPosition; - Size maBulletSize; - - DrawBulletInfo( - const GraphicObject& rBulletGraphicObject, - const Point& rBulletPosition, - const Size& rBulletSize) - : maBulletGraphicObject(rBulletGraphicObject), - maBulletPosition(rBulletPosition), - maBulletSize(rBulletSize) - {} -}; - struct EDITENG_DLLPUBLIC PaintFirstLineInfo { sal_Int32 mnPara; diff --git a/include/svx/svdotext.hxx b/include/svx/svdotext.hxx index dea64305df03..5466d7f3b306 100644 --- a/include/svx/svdotext.hxx +++ b/include/svx/svdotext.hxx @@ -116,19 +116,6 @@ namespace sdr::properties class CellProperties; } -class DrawPortionInfo; -class DrawBulletInfo; -void SVXCORE_DLLPUBLIC CreateTextPortionPrimitivesFromDrawPortionInfo( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const DrawPortionInfo& rInfo); -void SVXCORE_DLLPUBLIC CreateDrawBulletPrimitivesFromDrawBulletInfo( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const DrawBulletInfo& rInfo); - // SdrTextObj class SVXCORE_DLLPUBLIC SdrTextObj : public SdrAttrObj, public svx::ITextProvider { diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx index 497da7c9dd5f..13383f15a4c2 100644 --- a/sc/source/ui/view/tabview3.cxx +++ b/sc/source/ui/view/tabview3.cxx @@ -22,6 +22,7 @@ #include <scitems.hxx> #include <editeng/editview.hxx> +#include <editeng/StripPortionsHelper.hxx> #include <svx/fmshell.hxx> #include <svx/sdr/overlay/overlaymanager.hxx> #include <svx/svdoole2.hxx> diff --git a/svx/source/svdraw/svdotextdecomposition.cxx b/svx/source/svdraw/svdotextdecomposition.cxx index b8dc0eb0448d..a90694947368 100644 --- a/svx/source/svdraw/svdotextdecomposition.cxx +++ b/svx/source/svdraw/svdotextdecomposition.cxx @@ -34,14 +34,11 @@ #include <svx/xbtmpit.hxx> #include <basegfx/vector/b2dvector.hxx> #include <sdr/primitive2d/sdrtextprimitive2d.hxx> -#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx> -#include <drawinglayer/primitive2d/textbreakuphelper.hxx> -#include <drawinglayer/primitive2d/textprimitive2d.hxx> -#include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx> #include <basegfx/range/b2drange.hxx> #include <editeng/eeitem.hxx> #include <editeng/editstat.hxx> #include <editeng/smallcaps.hxx> +#include <editeng/StripPortionsHelper.hxx> #include <tools/helpers.hxx> #include <svl/itemset.hxx> #include <drawinglayer/animation/animationtiming.hxx> @@ -52,10 +49,8 @@ #include <editeng/flditem.hxx> #include <editeng/adjustitem.hxx> #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx> -#include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx> #include <drawinglayer/primitive2d/graphicprimitive2d.hxx> #include <drawinglayer/primitive2d/textlayoutdevice.hxx> -#include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx> #include <svx/unoapi.hxx> #include <drawinglayer/geometry/viewinformation2d.hxx> #include <editeng/outlobj.hxx> @@ -66,418 +61,6 @@ using namespace com::sun::star; -// helpers -namespace -{ - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> buildTextPortionPrimitive(const DrawPortionInfo& rInfo, const OUString& rText, - const drawinglayer::attribute::FontAttribute& rFontAttribute, - const std::vector<double>& rDXArray, - const basegfx::B2DHomMatrix& rNewTransform); - - // static rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo); - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> CheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) - { - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> xRet = pPrimitive; - if(rInfo.mpFieldData) - { - // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D - // which holds the field type and, if applicable, the URL - const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData); - const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData); - - // embed current primitive to a sequence - drawinglayer::primitive2d::Primitive2DContainer aSequence; - - if(pPrimitive) - { - aSequence.resize(1); - aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive); - } - - if(pURLField) - { - // extended this to hold more of the contents of the original - // SvxURLField since that stuff is still used in HitTest and e.g. Calc - std::vector< std::pair< OUString, OUString>> meValues; - meValues.emplace_back("URL", pURLField->GetURL()); - meValues.emplace_back("Representation", pURLField->GetRepresentation()); - meValues.emplace_back("TargetFrame", pURLField->GetTargetFrame()); - meValues.emplace_back("AltText", pURLField->GetName()); - meValues.emplace_back("SvxURLFormat", OUString::number(static_cast<sal_uInt16>(pURLField->GetFormat()))); - xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_URL, &meValues); - } - else if(pPageField) - { - xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_PAGE); - } - else - { - xRet = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence), drawinglayer::primitive2d::FIELD_TYPE_COMMON); - } - } - - 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); - } - }; -} // end of anonymous namespace - -void CreateTextPortionPrimitivesFromDrawPortionInfo( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const DrawPortionInfo& rInfo) -{ - if(rInfo.maText.isEmpty() || !rInfo.mnTextLen) - return; - - basegfx::B2DVector aFontScaling; - drawinglayer::attribute::FontAttribute aFontAttribute( - drawinglayer::primitive2d::getFontAttributeFromVclFont( - aFontScaling, - rInfo.mrFont, - rInfo.IsRTL(), - false)); - basegfx::B2DHomMatrix aNewTransform; - - // add font scale to new transform - aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY()); - - // look for proportional font scaling, if necessary, scale accordingly - sal_Int8 nPropr(rInfo.mrFont.GetPropr()); - const double fPropFontFactor(nPropr / 100.0); - if (100 != nPropr) - aNewTransform.scale(fPropFontFactor, fPropFontFactor); - - // apply font rotate - if(rInfo.mrFont.GetOrientation()) - { - aNewTransform.rotate(-toRadians(rInfo.mrFont.GetOrientation())); - } - - // look for escapement, if necessary, translate accordingly - if(rInfo.mrFont.GetEscapement()) - { - sal_Int16 nEsc(rInfo.mrFont.GetEscapement()); - - if(DFLT_ESC_AUTO_SUPER == nEsc) - { - nEsc = .8 * (100 - nPropr); - assert (nEsc == DFLT_ESC_SUPER && "I'm sure this formula needs to be changed, but how to confirm that???"); - nEsc = DFLT_ESC_SUPER; - } - else if(DFLT_ESC_AUTO_SUB == nEsc) - { - nEsc = .2 * -(100 - nPropr); - assert (nEsc == -20 && "I'm sure this formula needs to be changed, but how to confirm that???"); - nEsc = -20; - } - - if(nEsc > MAX_ESC_POS) - { - nEsc = MAX_ESC_POS; - } - else if(nEsc < -MAX_ESC_POS) - { - nEsc = -MAX_ESC_POS; - } - - const double fEscapement(nEsc / -100.0); - aNewTransform.translate(0.0, fEscapement * aFontScaling.getY()); - } - - // apply transformA - aNewTransform *= rNewTransformA; - - // apply local offset - aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()); - - // also apply embedding object's transform - aNewTransform *= rNewTransformB; - - // prepare DXArray content. To make it independent from font size (and such from - // the text transformation), scale it to unit coordinates - ::std::vector< double > aDXArray; - - if (!rInfo.mpDXArray.empty()) - { - aDXArray.reserve(rInfo.mnTextLen); - for(sal_Int32 a=0; a < rInfo.mnTextLen; a++) - { - aDXArray.push_back(rInfo.mpDXArray[a]); - } - } - - OUString caseMappedText = rInfo.mrFont.CalcCaseMap(rInfo.maText); - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive(buildTextPortionPrimitive(rInfo, caseMappedText, - aFontAttribute, - aDXArray, aNewTransform)); - - bool bSmallCaps = rInfo.mrFont.IsCapital(); - if (bSmallCaps && rInfo.mpDXArray.empty()) - { - SAL_WARN("svx", "SmallCaps requested with DXArray, abandoning"); - bSmallCaps = false; - } - if (bSmallCaps) - { - // rerun with each sub-portion - DoCapitalsDrawPortionInfo aDoDrawPortionInfo( - rTarget, - rNewTransformA, - rNewTransformB, - rInfo); - rInfo.mrFont.DoOnCapitals(aDoDrawPortionInfo); - - // transfer collected primitives from rTarget to a new container - drawinglayer::primitive2d::Primitive2DContainer aContainer = std::move(rTarget); - - // Take any decoration for the whole formatted portion and keep it to get continuous over/under/strike-through - if (pNewPrimitive->getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D) - { - const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* pTCPP = - static_cast<const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>(pNewPrimitive.get()); - - if (pTCPP->getWordLineMode()) // single word mode: 'Individual words' in UI - { - // Split to single word primitives using TextBreakupHelper - drawinglayer::primitive2d::TextBreakupHelper aTextBreakupHelper(*pTCPP); - drawinglayer::primitive2d::Primitive2DContainer aBroken(aTextBreakupHelper.extractResult(drawinglayer::primitive2d::BreakupUnit::Word)); - for (auto& rPortion : aBroken) - { - assert(rPortion->getPrimitive2DID() == PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D && - "TextBreakupHelper generates same output primitive type as input"); - - const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D* pPortion = - static_cast<const drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D*>(rPortion.get()); - - // create and add decoration - const drawinglayer::primitive2d::Primitive2DContainer& rDecorationGeometryContent( - pPortion->getOrCreateDecorationGeometryContent( - pPortion->getTextTransform(), - caseMappedText, - pPortion->getTextPosition(), - pPortion->getTextLength(), - pPortion->getDXArray())); - - aContainer.insert(aContainer.end(), rDecorationGeometryContent.begin(), rDecorationGeometryContent.end()); - } - } - else - { - // create and add decoration - const drawinglayer::primitive2d::Primitive2DContainer& rDecorationGeometryContent( - pTCPP->getOrCreateDecorationGeometryContent( - pTCPP->getTextTransform(), - caseMappedText, - rInfo.mnTextStart, - rInfo.mnTextLen, - aDXArray)); - - aContainer.insert(aContainer.end(), rDecorationGeometryContent.begin(), rDecorationGeometryContent.end()); - } - } - - pNewPrimitive = new drawinglayer::primitive2d::GroupPrimitive2D(std::move(aContainer)); - } - - const Color aFontColor(rInfo.mrFont.GetColor()); - if (aFontColor.IsTransparent()) - { - // Handle semi-transparent text for both the decorated and simple case here. - pNewPrimitive = new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D( - drawinglayer::primitive2d::Primitive2DContainer{ pNewPrimitive }, - (255 - aFontColor.GetAlpha()) / 255.0); - } - - if(rInfo.mbEndOfBullet) - { - // embed in TextHierarchyBulletPrimitive2D - drawinglayer::primitive2d::Primitive2DContainer aNewSequence { pNewPrimitive }; - pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence)); - } - - if(rInfo.mpFieldData) - { - pNewPrimitive = CheckFieldPrimitive(pNewPrimitive.get(), rInfo); - } - - rTarget.push_back(pNewPrimitive); - - // support for WrongSpellVector. Create WrongSpellPrimitives as needed - if(!rInfo.mpWrongSpellVector || aDXArray.empty()) - return; - - const sal_Int32 nSize(rInfo.mpWrongSpellVector->size()); - const sal_Int32 nDXCount(aDXArray.size()); - const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded - - for(sal_Int32 a(0); a < nSize; a++) - { - const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a]; - - if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart) - { - const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart); - const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart); - double fStart(0.0); - double fEnd(0.0); - - if(nStart > 0 && nStart - 1 < nDXCount) - { - fStart = aDXArray[nStart - 1]; - } - - if(nEnd > 0 && nEnd - 1 < nDXCount) - { - fEnd = aDXArray[nEnd - 1]; - } - - if(!basegfx::fTools::equal(fStart, fEnd)) - { - if(rInfo.IsRTL()) - { - // #i98523# - // When the portion is RTL, mirror the redlining using the - // full portion width - const double fTextWidth(aDXArray[aDXArray.size() - 1]); - - fStart = fTextWidth - fStart; - fEnd = fTextWidth - fEnd; - } - - // need to take FontScaling out of values; it's already part of - // aNewTransform and would be double applied - const double fFontScaleX(aFontScaling.getX() * fPropFontFactor); - - if(!basegfx::fTools::equal(fFontScaleX, 1.0) - && !basegfx::fTools::equalZero(fFontScaleX)) - { - fStart /= fFontScaleX; - fEnd /= fFontScaleX; - } - - rTarget.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D( - aNewTransform, - fStart, - fEnd, - aSpellColor)); - } - } - } -} - -void CreateDrawBulletPrimitivesFromDrawBulletInfo( - drawinglayer::primitive2d::Primitive2DContainer& rTarget, - const basegfx::B2DHomMatrix& rNewTransformA, - const basegfx::B2DHomMatrix& rNewTransformB, - const DrawBulletInfo& rInfo) -{ - basegfx::B2DHomMatrix aNewTransform; - - // add size to new transform - aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight()); - - // apply transformA - aNewTransform *= rNewTransformA; - - // apply local offset - aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y()); - - // also apply embedding object's transform - aNewTransform *= rNewTransformB; - - // 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 - rTarget.push_back(pNewPrimitive); -} - namespace { class impTextBreakupHandler @@ -595,145 +178,6 @@ namespace drawinglayer::primitive2d::Primitive2DContainer extractPrimitive2DSequence(); }; - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> buildTextPortionPrimitive( - const DrawPortionInfo& rInfo, const OUString& rText, - const drawinglayer::attribute::FontAttribute& rFontAttribute, - const std::vector<double>& rDXArray, - const basegfx::B2DHomMatrix& rNewTransform) - { - ::std::vector< sal_Bool > aKashidaArray; - - if(!rInfo.mpKashidaArray.empty() && rInfo.mnTextLen) - { - aKashidaArray.reserve(rInfo.mnTextLen); - - for(sal_Int32 a=0; a < rInfo.mnTextLen; a++) - { - aKashidaArray.push_back(rInfo.mpKashidaArray[a]); - } - } - - // create complex text primitive and append - const Color aFontColor(rInfo.mrFont.GetColor()); - const basegfx::BColor aBFontColor(aFontColor.getBColor()); - - const Color aTextFillColor(rInfo.mrFont.GetFillColor()); - - // prepare wordLineMode (for underline and strikeout) - // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)' - // to be split which would not look like the original - const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet); - - // prepare new primitive - rtl::Reference<drawinglayer::primitive2d::BasePrimitive2D> pNewPrimitive; - const bool bDecoratedIsNeeded( - LINESTYLE_NONE != rInfo.mrFont.GetOverline() - || LINESTYLE_NONE != rInfo.mrFont.GetUnderline() - || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout() - || FontEmphasisMark::NONE != (rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style) - || FontRelief::NONE != rInfo.mrFont.GetRelief() - || rInfo.mrFont.IsShadow() - || bWordLineMode); - - if(bDecoratedIsNeeded) - { - // TextDecoratedPortionPrimitive2D needed, prepare some more data - // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead - const Color aUnderlineColor(rInfo.maTextLineColor); - const basegfx::BColor aBUnderlineColor((aUnderlineColor == COL_AUTO) ? aBFontColor : aUnderlineColor.getBColor()); - const Color aOverlineColor(rInfo.maOverlineColor); - const basegfx::BColor aBOverlineColor((aOverlineColor == COL_AUTO) ? aBFontColor : aOverlineColor.getBColor()); - - // prepare overline and underline data - const drawinglayer::primitive2d::TextLine eFontOverline( - drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetOverline())); - const drawinglayer::primitive2d::TextLine eFontLineStyle( - drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo.mrFont.GetUnderline())); - - // check UnderlineAbove - const bool bUnderlineAbove( - drawinglayer::primitive2d::TEXT_LINE_NONE != eFontLineStyle && rInfo.mrFont.IsUnderlineAbove()); - - // prepare strikeout data - const drawinglayer::primitive2d::TextStrikeout eTextStrikeout( - drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout())); - - // prepare emphasis mark data - drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE); - - switch(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::Style) - { - case FontEmphasisMark::Dot : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT; break; - case FontEmphasisMark::Circle : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE; break; - case FontEmphasisMark::Disc : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC; break; - case FontEmphasisMark::Accent : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT; break; - default: break; - } - - const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosAbove); - const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & FontEmphasisMark::PosBelow); - - // prepare font relief data - drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE); - - switch(rInfo.mrFont.GetRelief()) - { - case FontRelief::Embossed : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break; - case FontRelief::Engraved : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break; - default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE - } - - // prepare shadow/outline data - const bool bShadow(rInfo.mrFont.IsShadow()); - - // TextDecoratedPortionPrimitive2D is needed, create one - pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D( - - // attributes for TextSimplePortionPrimitive2D - rNewTransform, - rText, - rInfo.mnTextStart, - rInfo.mnTextLen, - std::vector(rDXArray), - std::move(aKashidaArray), - rFontAttribute, - rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(), - aBFontColor, - aTextFillColor, - - // attributes for TextDecoratedPortionPrimitive2D - aBOverlineColor, - aBUnderlineColor, - eFontOverline, - eFontLineStyle, - bUnderlineAbove, - eTextStrikeout, - bWordLineMode, - eTextEmphasisMark, - bEmphasisMarkAbove, - bEmphasisMarkBelow, - eTextRelief, - bShadow); - } - else - { - // TextSimplePortionPrimitive2D is enough - pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( - rNewTransform, - rText, - rInfo.mnTextStart, - rInfo.mnTextLen, - std::vector(rDXArray), - std::move(aKashidaArray), - rFontAttribute, - rInfo.mpLocale ? *rInfo.mpLocale : css::lang::Locale(), - aBFontColor, - aTextFillColor); - } - - return pNewPrimitive; - } - void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives() { // only create a line primitive when we had content; there is no need for diff --git a/svx/source/svdraw/svdotextpathdecomposition.cxx b/svx/source/svdraw/svdotextpathdecomposition.cxx index 142190a60327..8d3876f57810 100644 --- a/svx/source/svdraw/svdotextpathdecomposition.cxx +++ b/svx/source/svdraw/svdotextpathdecomposition.cxx @@ -33,6 +33,7 @@ #include <drawinglayer/primitive2d/textlayoutdevice.hxx> #include <drawinglayer/primitive2d/textprimitive2d.hxx> #include <basegfx/color/bcolor.hxx> +#include <editeng/StripPortionsHelper.hxx> // primitive decomposition helpers #include <drawinglayer/attribute/strokeattribute.hxx>
