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 <Armin.Le.Grand@me.com>
Tested-by: Jenkins
This commit is contained in:
Armin Le Grand (Collabora)
2025-06-06 11:37:06 +02:00
committed by Armin Le Grand
parent c4b3f7a6f9
commit 782131d3a3
12 changed files with 710 additions and 648 deletions
+2
View File
@@ -29,6 +29,8 @@ $(eval $(call gb_CppunitTest_use_libraries,editeng_core, \
cppu \
cppuhelper \
docmodel \
drawinglayercore \
drawinglayer \
i18nlangtag \
i18nutil \
lng \
+2
View File
@@ -23,6 +23,8 @@ $(eval $(call gb_CppunitTest_use_libraries,editeng_editeng, \
cppu \
cppuhelper \
docmodel \
drawinglayercore \
drawinglayer \
i18nlangtag \
i18nutil \
lng \
+3
View File
@@ -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 \
@@ -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: */
+2
View File
@@ -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>
+1
View File
@@ -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;
+126
View File
@@ -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: */
+2 -78
View File
@@ -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;
-13
View File
@@ -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
{
+1
View File
@@ -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>
+1 -557
View File
@@ -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
@@ -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>