diff options
Diffstat (limited to 'drawinglayer/source/primitive2d')
5 files changed, 209 insertions, 18 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 { |