1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
21 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
22 #include <basegfx/matrix/b2dhommatrixtools.hxx>
23 #include <primitive2d/texteffectprimitive2d.hxx>
24 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
25 #include <primitive2d/textlineprimitive2d.hxx>
26 #include <primitive2d/textstrikeoutprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
30 namespace drawinglayer::primitive2d
32 void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
33 Primitive2DContainer
& rTarget
,
34 basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose
const & rDecTrans
,
35 const OUString
& rText
,
36 sal_Int32 nTextPosition
,
37 sal_Int32 nTextLength
,
38 const std::vector
< double >& rDXArray
,
39 const std::vector
< sal_Bool
>& rKashidaArray
,
40 const attribute::FontAttribute
& rFontAttribute
) const
42 // create the SimpleTextPrimitive needed in any case
43 rTarget
.push_back(Primitive2DReference(
44 new TextSimplePortionPrimitive2D(
45 rDecTrans
.getB2DHomMatrix(),
49 std::vector(rDXArray
),
50 std::vector(rKashidaArray
),
55 // see if something else needs to be done
56 const bool bOverlineUsed(TEXT_LINE_NONE
!= getFontOverline());
57 const bool bUnderlineUsed(TEXT_LINE_NONE
!= getFontUnderline());
58 const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE
!= getTextStrikeout());
60 if(!(bUnderlineUsed
|| bStrikeoutUsed
|| bOverlineUsed
))
63 // common preparations
64 TextLayouterDevice aTextLayouter
;
66 // TextLayouterDevice is needed to get metrics for text decorations like
67 // underline/strikeout/emphasis marks from it. For setup, the font size is needed
68 aTextLayouter
.setFontAttribute(
70 rDecTrans
.getScale().getX(),
71 rDecTrans
.getScale().getY(),
75 double fTextWidth(0.0);
79 fTextWidth
= aTextLayouter
.getTextWidth(rText
, nTextPosition
, nTextLength
);
83 fTextWidth
= rDXArray
.back() * rDecTrans
.getScale().getX();
84 const double fFontScaleX(rDecTrans
.getScale().getX());
86 if(!basegfx::fTools::equal(fFontScaleX
, 1.0)
87 && !basegfx::fTools::equalZero(fFontScaleX
))
89 // need to take FontScaling out of the DXArray
90 fTextWidth
/= fFontScaleX
;
96 // create primitive geometry for overline
97 rTarget
.push_back(Primitive2DReference(
98 new TextLinePrimitive2D(
99 rDecTrans
.getB2DHomMatrix(),
101 aTextLayouter
.getOverlineOffset(),
102 aTextLayouter
.getOverlineHeight(),
104 getOverlineColor())));
109 // create primitive geometry for underline
110 rTarget
.push_back(Primitive2DReference(
111 new TextLinePrimitive2D(
112 rDecTrans
.getB2DHomMatrix(),
114 aTextLayouter
.getUnderlineOffset(),
115 aTextLayouter
.getUnderlineHeight(),
117 getTextlineColor())));
123 // create primitive geometry for strikeout
124 if(TEXT_STRIKEOUT_SLASH
== getTextStrikeout() || TEXT_STRIKEOUT_X
== getTextStrikeout())
126 // strikeout with character
127 const sal_Unicode
aStrikeoutChar(TEXT_STRIKEOUT_SLASH
== getTextStrikeout() ? '/' : 'X');
129 rTarget
.push_back(Primitive2DReference(
130 new TextCharacterStrikeoutPrimitive2D(
131 rDecTrans
.getB2DHomMatrix(),
140 // strikeout with geometry
141 rTarget
.push_back(Primitive2DReference(
142 new TextGeometryStrikeoutPrimitive2D(
143 rDecTrans
.getB2DHomMatrix(),
146 aTextLayouter
.getUnderlineHeight(),
147 aTextLayouter
.getStrikeoutOffset(),
148 getTextStrikeout())));
151 // TODO: Handle Font Emphasis Above/Below
154 void TextDecoratedPortionPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
156 if(getWordLineMode())
158 // support for single word mode; split to single word primitives
159 // using TextBreakupHelper
160 TextBreakupHelper
aTextBreakupHelper(*this);
161 Primitive2DContainer
aBroken(aTextBreakupHelper
.extractResult(BreakupUnit::Word
));
165 // was indeed split to several words, use as result
166 rContainer
.append(std::move(aBroken
));
171 // no split, was already a single word. Continue to
172 // decompose local entity
175 basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose
aDecTrans(getTextTransform());
176 Primitive2DContainer aRetval
;
178 // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
180 // prepare new font attributes WITHOUT outline
181 const attribute::FontAttribute
aNewFontAttribute(
182 getFontAttribute().getFamilyName(),
183 getFontAttribute().getStyleName(),
184 getFontAttribute().getWeight(),
185 getFontAttribute().getSymbol(),
186 getFontAttribute().getVertical(),
187 getFontAttribute().getItalic(),
188 getFontAttribute().getMonospaced(),
189 false, // no outline anymore, handled locally
190 getFontAttribute().getRTL(),
191 getFontAttribute().getBiDiStrong());
193 // handle as one word
194 impCreateGeometryContent(aRetval
, aDecTrans
, getText(), getTextPosition(), getTextLength(), getDXArray(), getKashidaArray(), aNewFontAttribute
);
196 // Handle Shadow, Outline and TextRelief
199 // outline AND shadow depend on NO TextRelief (see dialog)
200 const bool bHasTextRelief(TEXT_RELIEF_NONE
!= getTextRelief());
201 const bool bHasShadow(!bHasTextRelief
&& getShadow());
202 const bool bHasOutline(!bHasTextRelief
&& getFontAttribute().getOutline());
204 if(bHasShadow
|| bHasTextRelief
|| bHasOutline
)
206 Primitive2DReference aShadow
;
210 // create shadow with current content (in aRetval). Text shadow
211 // is constant, relative to font size, rotated with the text and has a
213 // shadow parameter values
214 static const double fFactor(1.0 / 24.0);
215 const double fTextShadowOffset(aDecTrans
.getScale().getY() * fFactor
);
216 static basegfx::BColor
aShadowColor(0.3, 0.3, 0.3);
218 // prepare shadow transform matrix
219 const basegfx::B2DHomMatrix
aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix(
220 fTextShadowOffset
, fTextShadowOffset
));
222 // create shadow primitive
223 aShadow
= new ShadowPrimitive2D(
226 0, // fShadowBlur = 0, there's no blur for text shadow yet.
227 Primitive2DContainer(aRetval
));
232 // create emboss using an own helper primitive since this will
234 const basegfx::BColor
aBBlack(0.0, 0.0, 0.0);
235 const bool bDefaultTextColor(aBBlack
== getFontColor());
236 TextEffectStyle2D
aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed
);
238 if(bDefaultTextColor
)
240 if(TEXT_RELIEF_ENGRAVED
== getTextRelief())
242 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEngravedDefault
;
246 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEmbossedDefault
;
251 if(TEXT_RELIEF_ENGRAVED
== getTextRelief())
253 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEngraved
;
257 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEmbossed
;
261 Primitive2DReference
aNewTextEffect(new TextEffectPrimitive2D(
263 aDecTrans
.getTranslate(),
264 aDecTrans
.getRotate(),
265 aTextEffectStyle2D
));
266 aRetval
= Primitive2DContainer
{ aNewTextEffect
};
270 // create outline using an own helper primitive since this will
272 Primitive2DReference
aNewTextEffect(new TextEffectPrimitive2D(
274 aDecTrans
.getTranslate(),
275 aDecTrans
.getRotate(),
276 TextEffectStyle2D::Outline
));
277 aRetval
= Primitive2DContainer
{ aNewTextEffect
};
282 // put shadow in front if there is one to paint timely before
283 // but placed behind content
284 aRetval
.insert(aRetval
.begin(), aShadow
);
289 rContainer
.append(std::move(aRetval
));
292 TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
293 // TextSimplePortionPrimitive2D parameters
294 const basegfx::B2DHomMatrix
& rNewTransform
,
295 const OUString
& rText
,
296 sal_Int32 nTextPosition
,
297 sal_Int32 nTextLength
,
298 std::vector
< double >&& rDXArray
,
299 std::vector
< sal_Bool
>&& rKashidaArray
,
300 const attribute::FontAttribute
& rFontAttribute
,
301 const css::lang::Locale
& rLocale
,
302 const basegfx::BColor
& rFontColor
,
303 const Color
& rFillColor
,
306 const basegfx::BColor
& rOverlineColor
,
307 const basegfx::BColor
& rTextlineColor
,
308 TextLine eFontOverline
,
309 TextLine eFontUnderline
,
310 bool bUnderlineAbove
,
311 TextStrikeout eTextStrikeout
,
313 TextEmphasisMark eTextEmphasisMark
,
314 bool bEmphasisMarkAbove
,
315 bool bEmphasisMarkBelow
,
316 TextRelief eTextRelief
,
318 : TextSimplePortionPrimitive2D(rNewTransform
, rText
, nTextPosition
, nTextLength
, std::move(rDXArray
), std::move(rKashidaArray
), rFontAttribute
, rLocale
, rFontColor
, false, 0, rFillColor
),
319 maOverlineColor(rOverlineColor
),
320 maTextlineColor(rTextlineColor
),
321 meFontOverline(eFontOverline
),
322 meFontUnderline(eFontUnderline
),
323 meTextStrikeout(eTextStrikeout
),
324 meTextEmphasisMark(eTextEmphasisMark
),
325 meTextRelief(eTextRelief
),
326 mbUnderlineAbove(bUnderlineAbove
),
327 mbWordLineMode(bWordLineMode
),
328 mbEmphasisMarkAbove(bEmphasisMarkAbove
),
329 mbEmphasisMarkBelow(bEmphasisMarkBelow
),
334 bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
336 if(TextSimplePortionPrimitive2D::operator==(rPrimitive
))
338 const TextDecoratedPortionPrimitive2D
& rCompare
= static_cast<const TextDecoratedPortionPrimitive2D
&>(rPrimitive
);
340 return (getOverlineColor() == rCompare
.getOverlineColor()
341 && getTextlineColor() == rCompare
.getTextlineColor()
342 && getFontOverline() == rCompare
.getFontOverline()
343 && getFontUnderline() == rCompare
.getFontUnderline()
344 && getTextStrikeout() == rCompare
.getTextStrikeout()
345 && getTextEmphasisMark() == rCompare
.getTextEmphasisMark()
346 && getTextRelief() == rCompare
.getTextRelief()
347 && getUnderlineAbove() == rCompare
.getUnderlineAbove()
348 && getWordLineMode() == rCompare
.getWordLineMode()
349 && getEmphasisMarkAbove() == rCompare
.getEmphasisMarkAbove()
350 && getEmphasisMarkBelow() == rCompare
.getEmphasisMarkBelow()
351 && getShadow() == rCompare
.getShadow());
358 // Added missing implementation. Decorations may (will) stick out of the text's
359 // inking area, so add them if needed
360 basegfx::B2DRange
TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
362 // check if this needs to be a TextDecoratedPortionPrimitive2D or
363 // if a TextSimplePortionPrimitive2D would be sufficient
364 if (TEXT_LINE_NONE
!= getFontOverline()
365 || TEXT_LINE_NONE
!= getFontUnderline()
366 || TEXT_STRIKEOUT_NONE
!= getTextStrikeout()
367 || TEXT_FONT_EMPHASIS_MARK_NONE
!= getTextEmphasisMark()
368 || TEXT_RELIEF_NONE
!= getTextRelief()
371 // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
372 // the own local decomposition for computation and thus creates all necessary
374 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation
);
378 // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
379 return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation
);
384 sal_uInt32
TextDecoratedPortionPrimitive2D::getPrimitive2DID() const
386 return PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
;
389 } // end of namespace
391 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */