diff options
-rw-r--r-- | drawinglayer/source/primitive2d/Tools.cxx | 2 | ||||
-rw-r--r-- | drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx | 117 | ||||
-rw-r--r-- | drawinglayer/source/primitive2d/texthierarchyprimitive2d.cxx | 12 | ||||
-rw-r--r-- | drawinglayer/source/primitive2d/textlayoutdevice.cxx | 45 | ||||
-rw-r--r-- | drawinglayer/source/primitive2d/textprimitive2d.cxx | 51 | ||||
-rw-r--r-- | drawinglayer/source/processor2d/cairopixelprocessor2d.cxx | 81 | ||||
-rw-r--r-- | drawinglayer/source/processor2d/vclmetafileprocessor2d.cxx | 7 | ||||
-rw-r--r-- | include/drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx | 1 | ||||
-rw-r--r-- | include/drawinglayer/primitive2d/texthierarchyprimitive2d.hxx | 10 | ||||
-rw-r--r-- | include/drawinglayer/primitive2d/textlayoutdevice.hxx | 13 | ||||
-rw-r--r-- | include/drawinglayer/primitive2d/textprimitive2d.hxx | 14 | ||||
-rw-r--r-- | include/drawinglayer/processor2d/cairopixelprocessor2d.hxx | 2 | ||||
-rw-r--r-- | include/vcl/outdev.hxx | 7 | ||||
-rw-r--r-- | vcl/Library_vcl.mk | 1 | ||||
-rw-r--r-- | vcl/source/outdev/EmphasisMarks.cxx | 100 |
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: */ |