summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drawinglayer/source/primitive2d/Tools.cxx2
-rw-r--r--drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx117
-rw-r--r--drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx12
-rw-r--r--drawinglayer/source/primitive2d/textlayoutdevice.cxx45
-rw-r--r--drawinglayer/source/primitive2d/textprimitive2d.cxx51
-rw-r--r--drawinglayer/source/processor2d/cairopixelprocessor2d.cxx81
-rw-r--r--drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx7
-rw-r--r--include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx1
-rw-r--r--include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx10
-rw-r--r--include/drawinglayer/primitive2d/textlayoutdevice.hxx13
-rw-r--r--include/drawinglayer/primitive2d/textprimitive2d.hxx14
-rw-r--r--include/drawinglayer/processor2d/cairopixelprocessor2d.hxx2
-rw-r--r--include/vcl/outdev.hxx7
-rw-r--r--vcl/Library_vcl.mk1
-rw-r--r--vcl/source/outdev/EmphasisMarks.cxx100
15 files changed, 388 insertions, 75 deletions
diff --git a/drawinglayer/source/primitive2d/Tools.cxx b/drawinglayer/source/primitive2d/Tools.cxx
index 52545212b8cc..e1c3dfe96e80 100644
--- a/drawinglayer/source/primitive2d/Tools.cxx
+++ b/drawinglayer/source/primitive2d/Tools.cxx
@@ -241,6 +241,8 @@ OUString idToString(sal_uInt32 nId)
return u"BITMAPALPHAPRIMITIVE2D"_ustr;
case PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D:
return u"POLYPOLYGONALPHAGRADIENTPRIMITIVE2D"_ustr;
+ case PRIMITIVE2D_ID_TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D:
+ return u"TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D"_ustr;
default:
return OUString::number((nId >> 16) & 0xFF) + "|" + OUString::number(nId & 0xFF);
}
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
index 41a09c5968ea..d500e4476785 100644
--- a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -22,10 +22,14 @@
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#include <primitive2d/texteffectprimitive2d.hxx>
#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonHairlinePrimitive2D.hxx>
+#include <drawinglayer/primitive2d/PolyPolygonColorPrimitive2D.hxx>
+#include <drawinglayer/primitive2d/transformprimitive2d.hxx>
+#include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
#include <primitive2d/textlineprimitive2d.hxx>
#include <primitive2d/textstrikeoutprimitive2d.hxx>
#include <drawinglayer/primitive2d/textbreakuphelper.hxx>
-
+#include <vcl/vcllayout.hxx>
namespace drawinglayer::primitive2d
{
@@ -75,8 +79,10 @@ namespace drawinglayer::primitive2d
const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout());
+ const bool bEmphasisMarkUsed(TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark()
+ && (getEmphasisMarkAbove() || getEmphasisMarkBelow()));
- if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
+ if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed || bEmphasisMarkUsed))
{
// not used, return empty Primitive2DContainer
return maBufferedDecorationGeometry;
@@ -88,16 +94,9 @@ namespace drawinglayer::primitive2d
return maBufferedDecorationGeometry;
}
- // common preparations
- TextLayouterDevice aTextLayouter;
-
- // TextLayouterDevice is needed to get metrics for text decorations like
- // underline/strikeout/emphasis marks from it. For setup, the font size is needed
- aTextLayouter.setFontAttribute(
- getFontAttribute(),
- rDecTrans.getScale().getX(),
- rDecTrans.getScale().getY(),
- getLocale());
+ // common preparations - create TextLayouterDevice
+ primitive2d::TextLayouterDevice aTextLayouter;
+ createTextLayouter(aTextLayouter);
// get text width
double fTextWidth(0.0);
@@ -176,7 +175,99 @@ namespace drawinglayer::primitive2d
}
}
- // TODO: Handle Font Emphasis Above/Below
+ if (bEmphasisMarkUsed)
+ {
+ // create primitives for EmphasisMark visualization - we need a SalLayout
+ std::unique_ptr<SalLayout> pSalLayout(createSalLayout(aTextLayouter));
+
+ if (pSalLayout)
+ {
+ // placeholders for repeated content, only created once
+ Primitive2DReference aShape;
+ Primitive2DReference aRect1;
+ Primitive2DReference aRect2;
+
+ // space to collect primitives for EmphasisMark
+ Primitive2DContainer aEmphasisContent;
+
+ // callback collector will produce geometry alraeyd scaled, so
+ // prepare local transform without FontScale
+ const basegfx::B2DHomMatrix aObjTransformWithoutScale(
+ basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
+ rDecTrans.getShearX(), rDecTrans.getRotate(), rDecTrans.getTranslate()));
+
+ // the callback from OutputDevice::createEmphasisMarks providing the data
+ // for each EmphasisMark
+ auto aEmphasisCallback([this, &aShape, &aRect1, &aRect2, &aEmphasisContent, &aObjTransformWithoutScale](
+ const basegfx::B2DPoint& rOutPoint, const basegfx::B2DPolyPolygon& rShape,
+ bool isPolyLine, const tools::Rectangle& rRect1, const tools::Rectangle& rRect2)
+ {
+ // prepare complete ObjectTransform
+ const basegfx::B2DHomMatrix aTransform(
+ aObjTransformWithoutScale * basegfx::utils::createTranslateB2DHomMatrix(rOutPoint));
+
+ if (rShape.count())
+ {
+ // create PolyPolygon if provided
+ if (!aShape)
+ {
+ if (isPolyLine)
+ aShape = new PolyPolygonHairlinePrimitive2D(rShape, getFontColor());
+ else
+ aShape = new PolyPolygonColorPrimitive2D(rShape, getFontColor());
+ }
+
+ aEmphasisContent.push_back(
+ new TransformPrimitive2D(
+ aTransform,
+ Primitive2DContainer { aShape } ));
+ }
+
+ if (!rRect1.IsEmpty())
+ {
+ // create Rectangle1 if provided
+ if (!aRect1)
+ aRect1 = new FilledRectanglePrimitive2D(
+ basegfx::B2DRange(rRect1.Left(), rRect1.Top(), rRect1.Right(), rRect1.Bottom()), getFontColor());
+
+ aEmphasisContent.push_back(
+ new TransformPrimitive2D(
+ aTransform,
+ Primitive2DContainer { aRect1 } ));
+ }
+
+ if (!rRect2.IsEmpty())
+ {
+ // create Rectangle2 if provided
+ if (!aRect2)
+ aRect2 = new FilledRectanglePrimitive2D(
+ basegfx::B2DRange(rRect2.Left(), rRect2.Top(), rRect2.Right(), rRect2.Bottom()), getFontColor());
+
+ aEmphasisContent.push_back(
+ new TransformPrimitive2D(
+ aTransform,
+ Primitive2DContainer { aRect2 } ));
+ }
+ });
+
+ // call tooling method in vcl to generate the graphic representations
+ aTextLayouter.createEmphasisMarks(
+ *pSalLayout,
+ getTextEmphasisMark(),
+ getEmphasisMarkAbove(),
+ aEmphasisCallback);
+
+ if (!aEmphasisContent.empty())
+ {
+ // if we got graphic representations of EmphasisMark, add
+ // them to BufferedDecorationGeometry. Also embed them to
+ // a TextHierarchyEmphasisMarkPrimitive2D GroupPrimitive
+ // to be able to evtl. handle these in a special way
+ maBufferedDecorationGeometry.push_back(
+ new TextHierarchyEmphasisMarkPrimitive2D(std::move(aEmphasisContent)));
+ }
+ }
+ }
// append local result and return
return maBufferedDecorationGeometry;
diff --git a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
index 655918904cfb..73a21e3c5722 100644
--- a/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx
@@ -156,6 +156,18 @@ namespace drawinglayer::primitive2d
return PRIMITIVE2D_ID_TEXTHIERARCHYEDITPRIMITIVE2D;
}
+
+ TextHierarchyEmphasisMarkPrimitive2D::TextHierarchyEmphasisMarkPrimitive2D(Primitive2DContainer&& aContent)
+ : GroupPrimitive2D(std::move(aContent))
+ {
+ }
+
+ // provide unique ID
+ sal_uInt32 TextHierarchyEmphasisMarkPrimitive2D::getPrimitive2DID() const
+ {
+ return PRIMITIVE2D_ID_TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D;
+ }
+
} // end of namespace
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/drawinglayer/source/primitive2d/textlayoutdevice.cxx b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
index 3a6667d0461f..51c75f433ad6 100644
--- a/drawinglayer/source/primitive2d/textlayoutdevice.cxx
+++ b/drawinglayer/source/primitive2d/textlayoutdevice.cxx
@@ -365,11 +365,10 @@ std::vector<double> TextLayouterDevice::getTextArray(const OUString& rText, sal_
return aRetval;
}
-std::unique_ptr<SalLayout> TextLayouterDevice::getSalLayout(const OUString& rText,
- sal_uInt32 nIndex, sal_uInt32 nLength,
- const basegfx::B2DPoint& rStartPoint,
- const KernArray& rDXArray,
- std::span<const sal_Bool> pKashidaAry)
+std::unique_ptr<SalLayout>
+TextLayouterDevice::getSalLayout(const OUString& rText, sal_uInt32 nIndex, sal_uInt32 nLength,
+ const basegfx::B2DPoint& rStartPoint, const KernArray& rDXArray,
+ std::span<const sal_Bool> pKashidaAry) const
{
const SalLayoutGlyphs* pGlyphs(
SalLayoutGlyphsCache::self()->GetLayoutGlyphs(&mrDevice, rText, nIndex, nLength));
@@ -380,6 +379,42 @@ std::unique_ptr<SalLayout> TextLayouterDevice::getSalLayout(const OUString& rTex
SalLayoutFlags::NONE, nullptr, pGlyphs);
}
+void TextLayouterDevice::createEmphasisMarks(
+ SalLayout& rSalLayout, TextEmphasisMark aTextEmphasisMark, bool bAbove,
+ std::function<void(const basegfx::B2DPoint&, const basegfx::B2DPolyPolygon&, bool,
+ const tools::Rectangle&, const tools::Rectangle&)>
+ aCallback) const
+{
+ FontEmphasisMark nEmphasisMark(FontEmphasisMark::NONE);
+ double fEmphasisHeight(getTextHeight() * (250.0 / 1000.0));
+
+ switch (aTextEmphasisMark)
+ {
+ case TEXT_FONT_EMPHASIS_MARK_DOT:
+ nEmphasisMark = FontEmphasisMark::Dot;
+ break;
+ case TEXT_FONT_EMPHASIS_MARK_CIRCLE:
+ nEmphasisMark = FontEmphasisMark::Circle;
+ break;
+ case TEXT_FONT_EMPHASIS_MARK_DISC:
+ nEmphasisMark = FontEmphasisMark::Disc;
+ break;
+ case TEXT_FONT_EMPHASIS_MARK_ACCENT:
+ nEmphasisMark = FontEmphasisMark::Accent;
+ break;
+ default:
+ break;
+ }
+
+ if (bAbove)
+ nEmphasisMark |= FontEmphasisMark::PosAbove;
+ else
+ nEmphasisMark |= FontEmphasisMark::PosBelow;
+
+ mrDevice.createEmphasisMarks(nEmphasisMark, static_cast<tools::Long>(fEmphasisHeight),
+ rSalLayout, aCallback);
+}
+
// helper methods for vcl font handling
vcl::Font getVclFontFromFontAttribute(const attribute::FontAttribute& rFontAttribute,
diff --git a/drawinglayer/source/primitive2d/textprimitive2d.cxx b/drawinglayer/source/primitive2d/textprimitive2d.cxx
index 820b3d39f804..ec2d18a79204 100644
--- a/drawinglayer/source/primitive2d/textprimitive2d.cxx
+++ b/drawinglayer/source/primitive2d/textprimitive2d.cxx
@@ -25,6 +25,9 @@
#include <drawinglayer/primitive2d/groupprimitive2d.hxx>
#include <primitive2d/texteffectprimitive2d.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <vcl/vcllayout.hxx>
+#include <vcl/rendercontext/State.hxx>
+#include <vcl/kernarray.hxx>
#include <utility>
#include <osl/diagnose.h>
@@ -296,6 +299,54 @@ basegfx::B2DRange TextSimplePortionPrimitive2D::getB2DRange(
return maB2DRange;
}
+void TextSimplePortionPrimitive2D::createTextLayouter(TextLayouterDevice& rTextLayouter) const
+{
+ // decompose primitive-local matrix to get local font scaling
+ const basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
+
+ // create a TextLayouter to access encapsulated VCL Text/Font related tooling
+ rTextLayouter.setFontAttribute(getFontAttribute(), aDecTrans.getScale().getX(),
+ aDecTrans.getScale().getY(), getLocale());
+
+ if (getFontAttribute().getRTL())
+ {
+ vcl::text::ComplexTextLayoutFlags nRTLLayoutMode(
+ rTextLayouter.getLayoutMode() & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong);
+ nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl
+ | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
+ rTextLayouter.setLayoutMode(nRTLLayoutMode);
+ }
+ else
+ {
+ // tdf#101686: This is LTR text, but the output device may have RTL state.
+ vcl::text::ComplexTextLayoutFlags nLTRLayoutMode(rTextLayouter.getLayoutMode());
+ nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiRtl;
+ nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong;
+ rTextLayouter.setLayoutMode(nLTRLayoutMode);
+ }
+}
+
+std::unique_ptr<SalLayout>
+TextSimplePortionPrimitive2D::createSalLayout(TextLayouterDevice& rTextLayouter) const
+{
+ // create integer DXArray. As mentioned above we can act in the
+ // Text's local coordinate system without transformation at all
+ const ::std::vector<double>& rDXArray(getDXArray());
+ KernArray aDXArray;
+
+ if (!rDXArray.empty())
+ {
+ aDXArray.reserve(rDXArray.size());
+ for (auto const& elem : rDXArray)
+ aDXArray.push_back(basegfx::fround(elem));
+ }
+
+ // create SalLayout. No need for a position, as mentioned text can work
+ // without transformations, so start point is always 0,0
+ return rTextLayouter.getSalLayout(getText(), getTextPosition(), getTextLength(),
+ basegfx::B2DPoint(0.0, 0.0), aDXArray, getKashidaArray());
+}
+
// provide unique ID
sal_uInt32 TextSimplePortionPrimitive2D::getPrimitive2DID() const
{
diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
index 21a43e25dcfc..dfbce4af0c70 100644
--- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx
@@ -2913,9 +2913,8 @@ void CairoPixelProcessor2D::processTextDecoratedPortionPrimitive2D(
}
void CairoPixelProcessor2D::renderTextBackground(
- const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate,
- const primitive2d::TextLayouterDevice& rTextLayouter, const basegfx::B2DHomMatrix& rTransform,
- double fTextWidth)
+ const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate, double fAscent,
+ double fDescent, const basegfx::B2DHomMatrix& rTransform, double fTextWidth)
{
cairo_save(mpRT);
cairo_matrix_t aMatrix;
@@ -2925,8 +2924,7 @@ void CairoPixelProcessor2D::renderTextBackground(
const basegfx::BColor aFillColor(
maBColorModifierStack.getModifiedColor(rTextCandidate.getTextFillColor().getBColor()));
cairo_set_source_rgb(mpRT, aFillColor.getRed(), aFillColor.getGreen(), aFillColor.getBlue());
- cairo_rectangle(mpRT, 0.0, -rTextLayouter.getFontAscent(), fTextWidth,
- rTextLayouter.getTextHeight());
+ cairo_rectangle(mpRT, 0.0, -fAscent, fTextWidth, fAscent + fDescent);
cairo_fill(mpRT);
cairo_restore(mpRT);
}
@@ -2991,52 +2989,9 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate,
const primitive2d::TextDecoratedPortionPrimitive2D* pDecoratedCandidate)
{
- // decompose primitive-local matrix to get local font scaling
- const basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(
- rTextCandidate.getTextTransform());
-
- // create a TextLayouter to access encapsulated VCL Text/Font related tooling
primitive2d::TextLayouterDevice aTextLayouter;
- aTextLayouter.setFontAttribute(rTextCandidate.getFontAttribute(), aDecTrans.getScale().getX(),
- aDecTrans.getScale().getY(), rTextCandidate.getLocale());
- const basegfx::BColor aRGBFontColor(
- maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
- aTextLayouter.setTextColor(aRGBFontColor);
-
- if (rTextCandidate.getFontAttribute().getRTL())
- {
- vcl::text::ComplexTextLayoutFlags nRTLLayoutMode(
- aTextLayouter.getLayoutMode() & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong);
- nRTLLayoutMode |= vcl::text::ComplexTextLayoutFlags::BiDiRtl
- | vcl::text::ComplexTextLayoutFlags::TextOriginLeft;
- aTextLayouter.setLayoutMode(nRTLLayoutMode);
- }
- else
- {
- // tdf#101686: This is LTR text, but the output device may have RTL state.
- vcl::text::ComplexTextLayoutFlags nLTRLayoutMode(aTextLayouter.getLayoutMode());
- nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiRtl;
- nLTRLayoutMode = nLTRLayoutMode & ~vcl::text::ComplexTextLayoutFlags::BiDiStrong;
- aTextLayouter.setLayoutMode(nLTRLayoutMode);
- }
-
- // create integer DXArray. As mentioned above we can act in the
- // Text's local coordinate system without transformation at all
- const ::std::vector<double>& rDXArray(rTextCandidate.getDXArray());
- KernArray aDXArray;
-
- if (!rDXArray.empty())
- {
- aDXArray.reserve(rDXArray.size());
- for (auto const& elem : rDXArray)
- aDXArray.push_back(basegfx::fround(elem));
- }
-
- // create SalLayout. No need for a position, as mentioned text can work
- // without transformations, so start point is always 0,0
- std::unique_ptr<SalLayout> pSalLayout(aTextLayouter.getSalLayout(
- rTextCandidate.getText(), rTextCandidate.getTextPosition(), rTextCandidate.getTextLength(),
- basegfx::B2DPoint(0.0, 0.0), aDXArray, rTextCandidate.getKashidaArray()));
+ rTextCandidate.createTextLayouter(aTextLayouter);
+ std::unique_ptr<SalLayout> pSalLayout(rTextCandidate.createSalLayout(aTextLayouter));
if (!pSalLayout)
{
@@ -3046,6 +3001,8 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
}
// prepare local transformations
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(
+ rTextCandidate.getTextTransform());
const basegfx::B2DHomMatrix aObjTransformWithoutScale(
basegfx::utils::createShearXRotateTranslateB2DHomMatrix(
aDecTrans.getShearX(), aDecTrans.getRotate(), aDecTrans.getTranslate()));
@@ -3056,11 +3013,23 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
{
// render TextBackground first -> casts no shadow itself, so do independent of
// text shadow being activated
- renderTextBackground(rTextCandidate, aTextLayouter, aFullTextTransform,
+ double fAscent(aTextLayouter.getFontAscent());
+ double fDescent(aTextLayouter.getFontDescent());
+
+ if (nullptr != pDecoratedCandidate
+ && primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
+ != pDecoratedCandidate->getTextEmphasisMark())
+ {
+ if (pDecoratedCandidate->getEmphasisMarkAbove())
+ fAscent += aTextLayouter.getTextHeight() * (250.0 / 1000.0);
+ if (pDecoratedCandidate->getEmphasisMarkBelow())
+ fDescent += aTextLayouter.getTextHeight() * (250.0 / 1000.0);
+ }
+
+ renderTextBackground(rTextCandidate, fAscent, fDescent, aFullTextTransform,
pSalLayout->GetTextWidth());
}
- // basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(rTextCandidate.getTextTransform());
bool bHasTextRelief(false);
bool bHasShadow(false);
bool bHasOutline(false);
@@ -3077,7 +3046,9 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
bHasTextDecoration
= primitive2d::TEXT_LINE_NONE != pDecoratedCandidate->getFontOverline()
|| primitive2d::TEXT_LINE_NONE != pDecoratedCandidate->getFontUnderline()
- || primitive2d::TEXT_STRIKEOUT_NONE != pDecoratedCandidate->getTextStrikeout();
+ || primitive2d::TEXT_STRIKEOUT_NONE != pDecoratedCandidate->getTextStrikeout()
+ || primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
+ != pDecoratedCandidate->getTextEmphasisMark();
}
if (bHasShadow)
@@ -3122,11 +3093,11 @@ void CairoPixelProcessor2D::renderTextSimpleOrDecoratedPortionPrimitive2D(
if (bHasOutline)
{
// todo
- renderSalLayout(pSalLayout, aRGBFontColor, aFullTextTransform,
- getViewInformation2D().getUseAntiAliasing());
}
// render text
+ const basegfx::BColor aRGBFontColor(
+ maBColorModifierStack.getModifiedColor(rTextCandidate.getFontColor()));
renderSalLayout(pSalLayout, aRGBFontColor, aFullTextTransform,
getViewInformation2D().getUseAntiAliasing());
diff --git a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
index 4435db4bd583..a5f49e9e654b 100644
--- a/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
+++ b/drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx
@@ -903,6 +903,13 @@ void VclMetafileProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimi
static_cast<const primitive2d::ObjectInfoPrimitive2D&>(rCandidate));
break;
}
+ case PRIMITIVE2D_ID_TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D:
+ {
+ // EmphasisMarks are traditionally not added to Metafiles, see
+ // OutputDevice::ImplDrawEmphasisMarks which resets GDIMetaFile*
+ // while painting these, so just ignore these
+ break;
+ }
default:
{
// process recursively
diff --git a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx
index 015d0befe32f..8989f77c6b8b 100644
--- a/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx
+++ b/include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx
@@ -112,6 +112,7 @@
#define PRIMITIVE2D_ID_POLYPOLYGONRGBAPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 78)
#define PRIMITIVE2D_ID_BITMAPALPHAPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 79)
#define PRIMITIVE2D_ID_POLYPOLYGONALPHAGRADIENTPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 80)
+#define PRIMITIVE2D_ID_TEXTHIERARCHYEMPHASISMARKPRIMITIVE2D (PRIMITIVE2D_ID_RANGE_DRAWINGLAYER| 81)
// When you add a new primitive, please update the drawinglayer::primitive2d::idToString() function
// in drawinglayer/source/primitive2d/Tools.cxx.
diff --git a/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx b/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
index 62cb9099c985..0efebc0a7f0c 100644
--- a/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx
@@ -177,6 +177,16 @@ namespace drawinglayer::primitive2d
/// provide unique ID
virtual sal_uInt32 getPrimitive2DID() const override;
};
+
+ class DRAWINGLAYER_DLLPUBLIC TextHierarchyEmphasisMarkPrimitive2D final : public GroupPrimitive2D
+ {
+ public:
+ /// constructor
+ explicit TextHierarchyEmphasisMarkPrimitive2D(Primitive2DContainer&& aContent);
+
+ /// provide unique ID
+ virtual sal_uInt32 getPrimitive2DID() const override;
+ };
} // end of namespace drawinglayer::primitive2d
diff --git a/include/drawinglayer/primitive2d/textlayoutdevice.hxx b/include/drawinglayer/primitive2d/textlayoutdevice.hxx
index de7a1e214a2a..5a0849100142 100644
--- a/include/drawinglayer/primitive2d/textlayoutdevice.hxx
+++ b/include/drawinglayer/primitive2d/textlayoutdevice.hxx
@@ -22,9 +22,11 @@
#include <drawinglayer/drawinglayerdllapi.h>
#include <basegfx/range/b2drange.hxx>
+#include <drawinglayer/primitive2d/textenumsprimitive2d.hxx>
#include <vector>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <vcl/svapp.hxx>
+#include <tools/fontenum.hxx>
#include <span>
// predefines
@@ -37,6 +39,10 @@ namespace vcl
{
class Font;
}
+namespace vcl::font
+{
+class EmphasisMark;
+}
namespace tools
{
class Rectangle;
@@ -118,7 +124,12 @@ public:
sal_uInt32 nLength,
const basegfx::B2DPoint& rStartPoint,
const KernArray& rDXArray,
- std::span<const sal_Bool> pKashidaAry);
+ std::span<const sal_Bool> pKashidaAry) const;
+ void
+ createEmphasisMarks(SalLayout& rSalLayout, TextEmphasisMark aTextEmphasisMark, bool bAbove,
+ std::function<void(const basegfx::B2DPoint&, const basegfx::B2DPolyPolygon&,
+ bool, const tools::Rectangle&, const tools::Rectangle&)>
+ aCallback) const;
};
// helper methods for vcl font handling
diff --git a/include/drawinglayer/primitive2d/textprimitive2d.hxx b/include/drawinglayer/primitive2d/textprimitive2d.hxx
index b5cff99f4047..44c59ae47828 100644
--- a/include/drawinglayer/primitive2d/textprimitive2d.hxx
+++ b/include/drawinglayer/primitive2d/textprimitive2d.hxx
@@ -30,10 +30,18 @@
#include <tools/long.hxx>
#include <basegfx/color/bcolor.hxx>
#include <com/sun/star/lang/Locale.hpp>
+#include <memory>
#include <vector>
namespace drawinglayer::primitive2d
{
+class TextLayouterDevice;
+}
+
+class SalLayout;
+
+namespace drawinglayer::primitive2d
+{
/** TextSimplePortionPrimitive2D class
This is the basic primitive for representing a text portion. It contains
@@ -134,6 +142,12 @@ protected:
create2DDecomposition(const geometry::ViewInformation2D& rViewInformation) const override;
public:
+ /// helpers to create a TextLayouterDevice and SalLayout, e.g. needed for SDPRs
+ // and decompose. NOTE: the TextLayouterDevice is filled, but should always only
+ // be used temporary (do not try to buffer)
+ void createTextLayouter(TextLayouterDevice& rTextLayouter) const;
+ std::unique_ptr<SalLayout> createSalLayout(TextLayouterDevice& rTextLayouter) const;
+
/// constructor
TextSimplePortionPrimitive2D(basegfx::B2DHomMatrix aNewTransform, OUString aText,
sal_Int32 nTextPosition, sal_Int32 nTextLength,
diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
index d850b20aacd6..5edf5716212b 100644
--- a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
+++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx
@@ -127,7 +127,7 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : pub
const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate,
const primitive2d::TextDecoratedPortionPrimitive2D* pDecoratedCandidate);
void renderTextBackground(const primitive2d::TextSimplePortionPrimitive2D& rTextCandidate,
- const primitive2d::TextLayouterDevice& rTextLayouter,
+ double fAscent, double fDescent,
const basegfx::B2DHomMatrix& rTransform, double fTextWidth);
void renderSalLayout(const std::unique_ptr<SalLayout>& rSalLayout,
const basegfx::BColor& rTextColor, const basegfx::B2DHomMatrix& rTransform,
diff --git a/include/vcl/outdev.hxx b/include/vcl/outdev.hxx
index e353acd2938e..2564193c0cf4 100644
--- a/include/vcl/outdev.hxx
+++ b/include/vcl/outdev.hxx
@@ -1232,6 +1232,13 @@ private:
///@{
public:
+ /// tooling method to be able to access EmphasisMark data when needed
+ void createEmphasisMarks(
+ FontEmphasisMark nFontEmphasisMark,
+ tools::Long nEmphasisHeight,
+ SalLayout& rSalLayout,
+ std::function<void(const basegfx::B2DPoint&, const basegfx::B2DPolyPolygon&,
+ bool, const tools::Rectangle&, const tools::Rectangle&)> aCallback) const;
// tells whether this output device is RTL in an LTR UI or LTR in a RTL UI
SAL_DLLPRIVATE bool ImplIsAntiparallel() const ;
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk
index dd3d9d48c8e3..eaeecca9b35f 100644
--- a/vcl/Library_vcl.mk
+++ b/vcl/Library_vcl.mk
@@ -219,6 +219,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\
vcl/source/outdev/background \
vcl/source/outdev/eps \
vcl/source/outdev/outdev \
+ vcl/source/outdev/EmphasisMarks \
vcl/source/outdev/stack \
vcl/source/outdev/clipping \
vcl/source/outdev/fill \
diff --git a/vcl/source/outdev/EmphasisMarks.cxx b/vcl/source/outdev/EmphasisMarks.cxx
new file mode 100644
index 000000000000..2e1d57f93d9f
--- /dev/null
+++ b/vcl/source/outdev/EmphasisMarks.cxx
@@ -0,0 +1,100 @@
+/* -*- 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 <sal/config.h>
+#include <vcl/outdev.hxx>
+#include <font/EmphasisMark.hxx>
+#include <impglyphitem.hxx>
+#include <vcl/vcllayout.hxx>
+
+void OutputDevice::createEmphasisMarks(
+ FontEmphasisMark nEmphasisMark, tools::Long nEmphasisHeight, SalLayout& rSalLayout,
+ std::function<void(const basegfx::B2DPoint&, const basegfx::B2DPolyPolygon&, bool,
+ const tools::Rectangle&, const tools::Rectangle&)>
+ aCallback) const
+{
+ // tooling method to create geometry data for EmphasisMarks. It does the same
+ // as OutputDevice::ImplDrawEmphasisMarks, but feeds the data into the
+ // callback for further usage
+ vcl::font::EmphasisMark aEmphasisMark(nEmphasisMark, nEmphasisHeight, GetDPIY());
+
+ Point aOffset(0, 0);
+ Point aOffsetVert(0, 0);
+
+ if (nEmphasisMark & FontEmphasisMark::PosBelow)
+ {
+ aOffset.AdjustY(mpFontInstance->mxFontMetric->GetDescent() + aEmphasisMark.GetYOffset());
+ aOffsetVert = aOffset;
+ }
+ else
+ {
+ aOffset.AdjustY(-(mpFontInstance->mxFontMetric->GetAscent() + aEmphasisMark.GetYOffset()));
+ // Todo: use ideographic em-box or ideographic character face information.
+ aOffsetVert.AdjustY(-(mpFontInstance->mxFontMetric->GetAscent()
+ + mpFontInstance->mxFontMetric->GetDescent()
+ + aEmphasisMark.GetYOffset()));
+ }
+
+ tools::Long nEmphasisWidth2 = aEmphasisMark.GetWidth() / 2;
+ tools::Long nEmphasisHeight2 = nEmphasisHeight / 2;
+ aOffset += Point(nEmphasisWidth2, nEmphasisHeight2);
+
+ basegfx::B2DPolyPolygon aShape(aEmphasisMark.GetShape().getB2DPolyPolygon());
+
+ basegfx::B2DPoint aOutPoint;
+ basegfx::B2DRectangle aRectangle;
+ const GlyphItem* pGlyph;
+ const LogicalFontInstance* pGlyphFont;
+ int nStart = 0;
+ while (rSalLayout.GetNextGlyph(&pGlyph, aOutPoint, nStart, &pGlyphFont))
+ {
+ if (!pGlyph->GetGlyphBoundRect(pGlyphFont, aRectangle))
+ continue;
+
+ if (!pGlyph->IsSpacing())
+ {
+ Point aAdjPoint;
+ if (pGlyph->IsVertical())
+ {
+ aAdjPoint = aOffsetVert;
+ aAdjPoint.AdjustX((-pGlyph->origWidth() + aEmphasisMark.GetWidth()) / 2);
+ }
+ else
+ {
+ aAdjPoint = aOffset;
+ aAdjPoint.AdjustX(aRectangle.getMinX()
+ + (aRectangle.getWidth() - aEmphasisMark.GetWidth()) / 2);
+ }
+
+ if (mpFontInstance->mnOrientation)
+ {
+ Point aOriginPt(0, 0);
+ aOriginPt.RotateAround(aAdjPoint, mpFontInstance->mnOrientation);
+ }
+ aOutPoint.adjustX(aAdjPoint.X() - nEmphasisWidth2);
+ aOutPoint.adjustY(aAdjPoint.Y() - nEmphasisHeight2);
+
+ // use callback to propagate the data to where it was requested from
+ aCallback(aOutPoint, aShape, aEmphasisMark.IsShapePolyLine(), aEmphasisMark.GetRect1(),
+ aEmphasisMark.GetRect2());
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */