summaryrefslogtreecommitdiffstats
path: root/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx')
-rw-r--r--drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx398
1 files changed, 398 insertions, 0 deletions
diff --git a/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
new file mode 100644
index 000000000..28e9eccda
--- /dev/null
+++ b/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx
@@ -0,0 +1,398 @@
+/* -*- 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 <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
+#include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <primitive2d/texteffectprimitive2d.hxx>
+#include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
+#include <primitive2d/textlineprimitive2d.hxx>
+#include <primitive2d/textstrikeoutprimitive2d.hxx>
+#include <drawinglayer/primitive2d/textbreakuphelper.hxx>
+
+
+namespace drawinglayer::primitive2d
+{
+ void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
+ std::vector< Primitive2DReference >& rTarget,
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const & rDecTrans,
+ const OUString& rText,
+ sal_Int32 nTextPosition,
+ sal_Int32 nTextLength,
+ const std::vector< double >& rDXArray,
+ const attribute::FontAttribute& rFontAttribute) const
+ {
+ // create the SimpleTextPrimitive needed in any case
+ rTarget.push_back(Primitive2DReference(
+ new TextSimplePortionPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ rText,
+ nTextPosition,
+ nTextLength,
+ rDXArray,
+ rFontAttribute,
+ getLocale(),
+ getFontColor())));
+
+ // see if something else needs to be done
+ const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
+ const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
+ const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout());
+
+ if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
+ return;
+
+ // 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());
+
+ // get text width
+ double fTextWidth(0.0);
+
+ if(rDXArray.empty())
+ {
+ fTextWidth = aTextLayouter.getTextWidth(rText, nTextPosition, nTextLength);
+ }
+ else
+ {
+ fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
+ const double fFontScaleX(rDecTrans.getScale().getX());
+
+ if(!basegfx::fTools::equal(fFontScaleX, 1.0)
+ && !basegfx::fTools::equalZero(fFontScaleX))
+ {
+ // need to take FontScaling out of the DXArray
+ fTextWidth /= fFontScaleX;
+ }
+ }
+
+ if(bOverlineUsed)
+ {
+ // create primitive geometry for overline
+ rTarget.push_back(Primitive2DReference(
+ new TextLinePrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ aTextLayouter.getOverlineOffset(),
+ aTextLayouter.getOverlineHeight(),
+ getFontOverline(),
+ getOverlineColor())));
+ }
+
+ if(bUnderlineUsed)
+ {
+ // create primitive geometry for underline
+ rTarget.push_back(Primitive2DReference(
+ new TextLinePrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ aTextLayouter.getUnderlineOffset(),
+ aTextLayouter.getUnderlineHeight(),
+ getFontUnderline(),
+ getTextlineColor())));
+ }
+
+ if(!bStrikeoutUsed)
+ return;
+
+ // create primitive geometry for strikeout
+ if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout())
+ {
+ // strikeout with character
+ const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');
+
+ rTarget.push_back(Primitive2DReference(
+ new TextCharacterStrikeoutPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ getFontColor(),
+ aStrikeoutChar,
+ getFontAttribute(),
+ getLocale())));
+ }
+ else
+ {
+ // strikeout with geometry
+ rTarget.push_back(Primitive2DReference(
+ new TextGeometryStrikeoutPrimitive2D(
+ rDecTrans.getB2DHomMatrix(),
+ fTextWidth,
+ getFontColor(),
+ aTextLayouter.getUnderlineHeight(),
+ aTextLayouter.getStrikeoutOffset(),
+ getTextStrikeout())));
+ }
+
+ // TODO: Handle Font Emphasis Above/Below
+ }
+
+ void TextDecoratedPortionPrimitive2D::create2DDecomposition(Primitive2DContainer& rContainer, const geometry::ViewInformation2D& /*rViewInformation*/) const
+ {
+ if(getWordLineMode())
+ {
+ // support for single word mode; split to single word primitives
+ // using TextBreakupHelper
+ const TextBreakupHelper aTextBreakupHelper(*this);
+ const Primitive2DContainer& aBroken(aTextBreakupHelper.getResult(BreakupUnit::Word));
+
+ if(!aBroken.empty())
+ {
+ // was indeed split to several words, use as result
+ rContainer.insert(rContainer.end(), aBroken.begin(), aBroken.end());
+ return;
+ }
+ else
+ {
+ // no split, was already a single word. Continue to
+ // decompose local entity
+ }
+ }
+ std::vector< Primitive2DReference > aNewPrimitives;
+ basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
+ Primitive2DContainer aRetval;
+
+ // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
+ // Strikeout, etc...
+ // prepare new font attributes WITHOUT outline
+ const attribute::FontAttribute aNewFontAttribute(
+ getFontAttribute().getFamilyName(),
+ getFontAttribute().getStyleName(),
+ getFontAttribute().getWeight(),
+ getFontAttribute().getSymbol(),
+ getFontAttribute().getVertical(),
+ getFontAttribute().getItalic(),
+ getFontAttribute().getMonospaced(),
+ false, // no outline anymore, handled locally
+ getFontAttribute().getRTL(),
+ getFontAttribute().getBiDiStrong());
+
+ // handle as one word
+ impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
+
+ // convert to Primitive2DSequence
+ const sal_uInt32 nMemberCount(aNewPrimitives.size());
+
+ if(nMemberCount)
+ {
+ aRetval.resize(nMemberCount);
+
+ for(sal_uInt32 a(0); a < nMemberCount; a++)
+ {
+ aRetval[a] = aNewPrimitives[a];
+ }
+ }
+
+ // Handle Shadow, Outline and TextRelief
+ if(!aRetval.empty())
+ {
+ // outline AND shadow depend on NO TextRelief (see dialog)
+ const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
+ const bool bHasShadow(!bHasTextRelief && getShadow());
+ const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());
+
+ if(bHasShadow || bHasTextRelief || bHasOutline)
+ {
+ Primitive2DReference aShadow;
+
+ if(bHasShadow)
+ {
+ // create shadow with current content (in aRetval). Text shadow
+ // is constant, relative to font size, rotated with the text and has a
+ // constant color.
+ // shadow parameter values
+ static const double fFactor(1.0 / 24.0);
+ const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
+ static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);
+
+ // prepare shadow transform matrix
+ const basegfx::B2DHomMatrix aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix(
+ fTextShadowOffset, fTextShadowOffset));
+
+ // create shadow primitive
+ aShadow = new ShadowPrimitive2D(
+ aShadowTransform,
+ aShadowColor,
+ aRetval);
+ }
+
+ if(bHasTextRelief)
+ {
+ // create emboss using an own helper primitive since this will
+ // be view-dependent
+ const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
+ const bool bDefaultTextColor(aBBlack == getFontColor());
+ TextEffectStyle2D aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed);
+
+ if(bDefaultTextColor)
+ {
+ if(TEXT_RELIEF_ENGRAVED == getTextRelief())
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault;
+ }
+ else
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault;
+ }
+ }
+ else
+ {
+ if(TEXT_RELIEF_ENGRAVED == getTextRelief())
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEngraved;
+ }
+ else
+ {
+ aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossed;
+ }
+ }
+
+ Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ aRetval,
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ aTextEffectStyle2D));
+ aRetval = Primitive2DContainer { aNewTextEffect };
+ }
+ else if(bHasOutline)
+ {
+ // create outline using an own helper primitive since this will
+ // be view-dependent
+ Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
+ aRetval,
+ aDecTrans.getTranslate(),
+ aDecTrans.getRotate(),
+ TextEffectStyle2D::Outline));
+ aRetval = Primitive2DContainer { aNewTextEffect };
+ }
+
+ if(aShadow.is())
+ {
+ // put shadow in front if there is one to paint timely before
+ // but placed behind content
+ aRetval.insert(aRetval.begin(), aShadow);
+ }
+ }
+ }
+
+ rContainer.insert(rContainer.end(), aRetval.begin(), aRetval.end());
+ }
+
+ TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
+ // TextSimplePortionPrimitive2D parameters
+ const basegfx::B2DHomMatrix& rNewTransform,
+ const OUString& rText,
+ sal_Int32 nTextPosition,
+ sal_Int32 nTextLength,
+ const std::vector< double >& rDXArray,
+ const attribute::FontAttribute& rFontAttribute,
+ const css::lang::Locale& rLocale,
+ const basegfx::BColor& rFontColor,
+ const Color& rFillColor,
+
+ // local parameters
+ const basegfx::BColor& rOverlineColor,
+ const basegfx::BColor& rTextlineColor,
+ TextLine eFontOverline,
+ TextLine eFontUnderline,
+ bool bUnderlineAbove,
+ TextStrikeout eTextStrikeout,
+ bool bWordLineMode,
+ TextEmphasisMark eTextEmphasisMark,
+ bool bEmphasisMarkAbove,
+ bool bEmphasisMarkBelow,
+ TextRelief eTextRelief,
+ bool bShadow)
+ : TextSimplePortionPrimitive2D(rNewTransform, rText, nTextPosition, nTextLength, rDXArray, rFontAttribute, rLocale, rFontColor, false, 0, rFillColor),
+ maOverlineColor(rOverlineColor),
+ maTextlineColor(rTextlineColor),
+ meFontOverline(eFontOverline),
+ meFontUnderline(eFontUnderline),
+ meTextStrikeout(eTextStrikeout),
+ meTextEmphasisMark(eTextEmphasisMark),
+ meTextRelief(eTextRelief),
+ mbUnderlineAbove(bUnderlineAbove),
+ mbWordLineMode(bWordLineMode),
+ mbEmphasisMarkAbove(bEmphasisMarkAbove),
+ mbEmphasisMarkBelow(bEmphasisMarkBelow),
+ mbShadow(bShadow)
+ {
+ }
+
+ bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
+ {
+ if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
+ {
+ const TextDecoratedPortionPrimitive2D& rCompare = static_cast<const TextDecoratedPortionPrimitive2D&>(rPrimitive);
+
+ return (getOverlineColor() == rCompare.getOverlineColor()
+ && getTextlineColor() == rCompare.getTextlineColor()
+ && getFontOverline() == rCompare.getFontOverline()
+ && getFontUnderline() == rCompare.getFontUnderline()
+ && getTextStrikeout() == rCompare.getTextStrikeout()
+ && getTextEmphasisMark() == rCompare.getTextEmphasisMark()
+ && getTextRelief() == rCompare.getTextRelief()
+ && getUnderlineAbove() == rCompare.getUnderlineAbove()
+ && getWordLineMode() == rCompare.getWordLineMode()
+ && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
+ && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
+ && getShadow() == rCompare.getShadow());
+ }
+
+ return false;
+ }
+
+ // #i96475#
+ // Added missing implementation. Decorations may (will) stick out of the text's
+ // inking area, so add them if needed
+ basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
+ {
+ // check if this needs to be a TextDecoratedPortionPrimitive2D or
+ // if a TextSimplePortionPrimitive2D would be sufficient
+ if (TEXT_LINE_NONE != getFontOverline()
+ || TEXT_LINE_NONE != getFontUnderline()
+ || TEXT_STRIKEOUT_NONE != getTextStrikeout()
+ || TEXT_FONT_EMPHASIS_MARK_NONE != getTextEmphasisMark()
+ || TEXT_RELIEF_NONE != getTextRelief()
+ || getShadow())
+ {
+ // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
+ // the own local decomposition for computation and thus creates all necessary
+ // geometric objects
+ return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ else
+ {
+ // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
+ return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
+ }
+ }
+
+ // provide unique ID
+ ImplPrimitive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
+
+} // end of namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */