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/polygonprimitive2d.hxx>
22 #include <drawinglayer/attribute/strokeattribute.hxx>
23 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
24 #include <basegfx/matrix/b2dhommatrixtools.hxx>
25 #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx>
26 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
29 #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
30 #include <drawinglayer/primitive2d/textbreakuphelper.hxx>
33 namespace drawinglayer
37 void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
38 std::vector
< Primitive2DReference
>& rTarget
,
39 basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose
const & rDecTrans
,
40 const OUString
& rText
,
41 sal_Int32 nTextPosition
,
42 sal_Int32 nTextLength
,
43 const std::vector
< double >& rDXArray
,
44 const attribute::FontAttribute
& rFontAttribute
) const
46 // create the SimpleTextPrimitive needed in any case
47 rTarget
.push_back(Primitive2DReference(
48 new TextSimplePortionPrimitive2D(
49 rDecTrans
.getB2DHomMatrix(),
58 // see if something else needs to be done
59 const bool bOverlineUsed(TEXT_LINE_NONE
!= getFontOverline());
60 const bool bUnderlineUsed(TEXT_LINE_NONE
!= getFontUnderline());
61 const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE
!= getTextStrikeout());
63 if(bUnderlineUsed
|| bStrikeoutUsed
|| bOverlineUsed
)
65 // common preparations
66 TextLayouterDevice aTextLayouter
;
68 // TextLayouterDevice is needed to get metrics for text decorations like
69 // underline/strikeout/emphasis marks from it. For setup, the font size is needed
70 aTextLayouter
.setFontAttribute(
72 rDecTrans
.getScale().getX(),
73 rDecTrans
.getScale().getY(),
77 double fTextWidth(0.0);
81 fTextWidth
= aTextLayouter
.getTextWidth(rText
, nTextPosition
, nTextLength
);
85 fTextWidth
= rDXArray
.back() * rDecTrans
.getScale().getX();
86 const double fFontScaleX(rDecTrans
.getScale().getX());
88 if(!basegfx::fTools::equal(fFontScaleX
, 1.0)
89 && !basegfx::fTools::equalZero(fFontScaleX
))
91 // need to take FontScaling out of the DXArray
92 fTextWidth
/= fFontScaleX
;
98 // create primitive geometry for overline
99 rTarget
.push_back(Primitive2DReference(
100 new TextLinePrimitive2D(
101 rDecTrans
.getB2DHomMatrix(),
103 aTextLayouter
.getOverlineOffset(),
104 aTextLayouter
.getOverlineHeight(),
106 getOverlineColor())));
111 // create primitive geometry for underline
112 rTarget
.push_back(Primitive2DReference(
113 new TextLinePrimitive2D(
114 rDecTrans
.getB2DHomMatrix(),
116 aTextLayouter
.getUnderlineOffset(),
117 aTextLayouter
.getUnderlineHeight(),
119 getTextlineColor())));
124 // create primitive geometry for strikeout
125 if(TEXT_STRIKEOUT_SLASH
== getTextStrikeout() || TEXT_STRIKEOUT_X
== getTextStrikeout())
127 // strikeout with character
128 const sal_Unicode
aStrikeoutChar(TEXT_STRIKEOUT_SLASH
== getTextStrikeout() ? '/' : 'X');
130 rTarget
.push_back(Primitive2DReference(
131 new TextCharacterStrikeoutPrimitive2D(
132 rDecTrans
.getB2DHomMatrix(),
141 // strikeout with geometry
142 rTarget
.push_back(Primitive2DReference(
143 new TextGeometryStrikeoutPrimitive2D(
144 rDecTrans
.getB2DHomMatrix(),
147 aTextLayouter
.getUnderlineHeight(),
148 aTextLayouter
.getStrikeoutOffset(),
149 getTextStrikeout())));
154 // TODO: Handle Font Emphasis Above/Below
157 void TextDecoratedPortionPrimitive2D::create2DDecomposition(Primitive2DContainer
& rContainer
, const geometry::ViewInformation2D
& /*rViewInformation*/) const
159 if(getWordLineMode())
161 // support for single word mode; split to single word primitives
162 // using TextBreakupHelper
163 const TextBreakupHelper
aTextBreakupHelper(*this);
164 const Primitive2DContainer
& aBroken(aTextBreakupHelper
.getResult(BreakupUnit::Word
));
168 // was indeed split to several words, use as result
169 rContainer
.insert(rContainer
.end(), aBroken
.begin(), aBroken
.end());
174 // no split, was already a single word. Continue to
175 // decompose local entity
178 std::vector
< Primitive2DReference
> aNewPrimitives
;
179 basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose
aDecTrans(getTextTransform());
180 Primitive2DContainer aRetval
;
182 // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
184 // prepare new font attributes WITHOUT outline
185 const attribute::FontAttribute
aNewFontAttribute(
186 getFontAttribute().getFamilyName(),
187 getFontAttribute().getStyleName(),
188 getFontAttribute().getWeight(),
189 getFontAttribute().getSymbol(),
190 getFontAttribute().getVertical(),
191 getFontAttribute().getItalic(),
192 getFontAttribute().getMonospaced(),
193 false, // no outline anymore, handled locally
194 getFontAttribute().getRTL(),
195 getFontAttribute().getBiDiStrong());
197 // handle as one word
198 impCreateGeometryContent(aNewPrimitives
, aDecTrans
, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute
);
200 // convert to Primitive2DSequence
201 const sal_uInt32
nMemberCount(aNewPrimitives
.size());
205 aRetval
.resize(nMemberCount
);
207 for(sal_uInt32
a(0); a
< nMemberCount
; a
++)
209 aRetval
[a
] = aNewPrimitives
[a
];
213 // Handle Shadow, Outline and TextRelief
216 // outline AND shadow depend on NO TextRelief (see dialog)
217 const bool bHasTextRelief(TEXT_RELIEF_NONE
!= getTextRelief());
218 const bool bHasShadow(!bHasTextRelief
&& getShadow());
219 const bool bHasOutline(!bHasTextRelief
&& getFontAttribute().getOutline());
221 if(bHasShadow
|| bHasTextRelief
|| bHasOutline
)
223 Primitive2DReference aShadow
;
227 // create shadow with current content (in aRetval). Text shadow
228 // is constant, relative to font size, rotated with the text and has a
230 // shadow parameter values
231 static const double fFactor(1.0 / 24.0);
232 const double fTextShadowOffset(aDecTrans
.getScale().getY() * fFactor
);
233 static basegfx::BColor
aShadowColor(0.3, 0.3, 0.3);
235 // preapare shadow transform matrix
236 const basegfx::B2DHomMatrix
aShadowTransform(basegfx::utils::createTranslateB2DHomMatrix(
237 fTextShadowOffset
, fTextShadowOffset
));
239 // create shadow primitive
240 aShadow
= new ShadowPrimitive2D(
248 // create emboss using an own helper primitive since this will
250 const basegfx::BColor
aBBlack(0.0, 0.0, 0.0);
251 const bool bDefaultTextColor(aBBlack
== getFontColor());
252 TextEffectStyle2D
aTextEffectStyle2D(TextEffectStyle2D::ReliefEmbossed
);
254 if(bDefaultTextColor
)
256 if(TEXT_RELIEF_ENGRAVED
== getTextRelief())
258 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEngravedDefault
;
262 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEmbossedDefault
;
267 if(TEXT_RELIEF_ENGRAVED
== getTextRelief())
269 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEngraved
;
273 aTextEffectStyle2D
= TextEffectStyle2D::ReliefEmbossed
;
277 Primitive2DReference
aNewTextEffect(new TextEffectPrimitive2D(
279 aDecTrans
.getTranslate(),
280 aDecTrans
.getRotate(),
281 aTextEffectStyle2D
));
282 aRetval
= Primitive2DContainer
{ aNewTextEffect
};
286 // create outline using an own helper primitive since this will
288 Primitive2DReference
aNewTextEffect(new TextEffectPrimitive2D(
290 aDecTrans
.getTranslate(),
291 aDecTrans
.getRotate(),
292 TextEffectStyle2D::Outline
));
293 aRetval
= Primitive2DContainer
{ aNewTextEffect
};
298 // put shadow in front if there is one to paint timely before
299 // but placed behind content
300 aRetval
.insert(aRetval
.begin(), aShadow
);
305 rContainer
.insert(rContainer
.end(), aRetval
.begin(), aRetval
.end());
308 TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
309 // TextSimplePortionPrimitive2D parameters
310 const basegfx::B2DHomMatrix
& rNewTransform
,
311 const OUString
& rText
,
312 sal_Int32 nTextPosition
,
313 sal_Int32 nTextLength
,
314 const std::vector
< double >& rDXArray
,
315 const attribute::FontAttribute
& rFontAttribute
,
316 const css::lang::Locale
& rLocale
,
317 const basegfx::BColor
& rFontColor
,
318 const Color
& rFillColor
,
321 const basegfx::BColor
& rOverlineColor
,
322 const basegfx::BColor
& rTextlineColor
,
323 TextLine eFontOverline
,
324 TextLine eFontUnderline
,
325 bool bUnderlineAbove
,
326 TextStrikeout eTextStrikeout
,
328 TextEmphasisMark eTextEmphasisMark
,
329 bool bEmphasisMarkAbove
,
330 bool bEmphasisMarkBelow
,
331 TextRelief eTextRelief
,
333 : TextSimplePortionPrimitive2D(rNewTransform
, rText
, nTextPosition
, nTextLength
, rDXArray
, rFontAttribute
, rLocale
, rFontColor
, false, 0, rFillColor
),
334 maOverlineColor(rOverlineColor
),
335 maTextlineColor(rTextlineColor
),
336 meFontOverline(eFontOverline
),
337 meFontUnderline(eFontUnderline
),
338 meTextStrikeout(eTextStrikeout
),
339 meTextEmphasisMark(eTextEmphasisMark
),
340 meTextRelief(eTextRelief
),
341 mbUnderlineAbove(bUnderlineAbove
),
342 mbWordLineMode(bWordLineMode
),
343 mbEmphasisMarkAbove(bEmphasisMarkAbove
),
344 mbEmphasisMarkBelow(bEmphasisMarkBelow
),
349 bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D
& rPrimitive
) const
351 if(TextSimplePortionPrimitive2D::operator==(rPrimitive
))
353 const TextDecoratedPortionPrimitive2D
& rCompare
= static_cast<const TextDecoratedPortionPrimitive2D
&>(rPrimitive
);
355 return (getOverlineColor() == rCompare
.getOverlineColor()
356 && getTextlineColor() == rCompare
.getTextlineColor()
357 && getFontOverline() == rCompare
.getFontOverline()
358 && getFontUnderline() == rCompare
.getFontUnderline()
359 && getTextStrikeout() == rCompare
.getTextStrikeout()
360 && getTextEmphasisMark() == rCompare
.getTextEmphasisMark()
361 && getTextRelief() == rCompare
.getTextRelief()
362 && getUnderlineAbove() == rCompare
.getUnderlineAbove()
363 && getWordLineMode() == rCompare
.getWordLineMode()
364 && getEmphasisMarkAbove() == rCompare
.getEmphasisMarkAbove()
365 && getEmphasisMarkBelow() == rCompare
.getEmphasisMarkBelow()
366 && getShadow() == rCompare
.getShadow());
373 // Added missing implementation. Decorations may (will) stick out of the text's
374 // inking area, so add them if needed
375 basegfx::B2DRange
TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D
& rViewInformation
) const
377 // check if this needs to be a TextDecoratedPortionPrimitive2D or
378 // if a TextSimplePortionPrimitive2D would be sufficient
379 if (TEXT_LINE_NONE
!= getFontOverline()
380 || TEXT_LINE_NONE
!= getFontUnderline()
381 || TEXT_STRIKEOUT_NONE
!= getTextStrikeout()
382 || TEXT_FONT_EMPHASIS_MARK_NONE
!= getTextEmphasisMark()
383 || TEXT_RELIEF_NONE
!= getTextRelief()
386 // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
387 // the own local decomposition for computation and thus creates all necessary
389 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation
);
393 // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
394 return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation
);
399 ImplPrimitive2DIDBlock(TextDecoratedPortionPrimitive2D
, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
)
401 } // end of namespace primitive2d
402 } // end of namespace drawinglayer
404 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */