Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / drawinglayer / source / primitive2d / textdecoratedprimitive2d.cxx
blob643482ac75d9fcaa99ff53fa5b2937892c9fc773
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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
35 namespace primitive2d
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(),
50 rText,
51 nTextPosition,
52 nTextLength,
53 rDXArray,
54 rFontAttribute,
55 getLocale(),
56 getFontColor())));
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(
71 getFontAttribute(),
72 rDecTrans.getScale().getX(),
73 rDecTrans.getScale().getY(),
74 getLocale());
76 // get text width
77 double fTextWidth(0.0);
79 if(rDXArray.empty())
81 fTextWidth = aTextLayouter.getTextWidth(rText, nTextPosition, nTextLength);
83 else
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;
96 if(bOverlineUsed)
98 // create primitive geometry for overline
99 rTarget.push_back(Primitive2DReference(
100 new TextLinePrimitive2D(
101 rDecTrans.getB2DHomMatrix(),
102 fTextWidth,
103 aTextLayouter.getOverlineOffset(),
104 aTextLayouter.getOverlineHeight(),
105 getFontOverline(),
106 getOverlineColor())));
109 if(bUnderlineUsed)
111 // create primitive geometry for underline
112 rTarget.push_back(Primitive2DReference(
113 new TextLinePrimitive2D(
114 rDecTrans.getB2DHomMatrix(),
115 fTextWidth,
116 aTextLayouter.getUnderlineOffset(),
117 aTextLayouter.getUnderlineHeight(),
118 getFontUnderline(),
119 getTextlineColor())));
122 if(bStrikeoutUsed)
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(),
133 fTextWidth,
134 getFontColor(),
135 aStrikeoutChar,
136 getFontAttribute(),
137 getLocale())));
139 else
141 // strikeout with geometry
142 rTarget.push_back(Primitive2DReference(
143 new TextGeometryStrikeoutPrimitive2D(
144 rDecTrans.getB2DHomMatrix(),
145 fTextWidth,
146 getFontColor(),
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));
166 if(!aBroken.empty())
168 // was indeed split to several words, use as result
169 rContainer.insert(rContainer.end(), aBroken.begin(), aBroken.end());
170 return;
172 else
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,
183 // Strikeout, etc...
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());
203 if(nMemberCount)
205 aRetval.resize(nMemberCount);
207 for(sal_uInt32 a(0); a < nMemberCount; a++)
209 aRetval[a] = aNewPrimitives[a];
213 // Handle Shadow, Outline and TextRelief
214 if(!aRetval.empty())
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;
225 if(bHasShadow)
227 // create shadow with current content (in aRetval). Text shadow
228 // is constant, relative to font size, rotated with the text and has a
229 // constant color.
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(
241 aShadowTransform,
242 aShadowColor,
243 aRetval);
246 if(bHasTextRelief)
248 // create emboss using an own helper primitive since this will
249 // be view-dependent
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;
260 else
262 aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault;
265 else
267 if(TEXT_RELIEF_ENGRAVED == getTextRelief())
269 aTextEffectStyle2D = TextEffectStyle2D::ReliefEngraved;
271 else
273 aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossed;
277 Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
278 aRetval,
279 aDecTrans.getTranslate(),
280 aDecTrans.getRotate(),
281 aTextEffectStyle2D));
282 aRetval = Primitive2DContainer { aNewTextEffect };
284 else if(bHasOutline)
286 // create outline using an own helper primitive since this will
287 // be view-dependent
288 Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
289 aRetval,
290 aDecTrans.getTranslate(),
291 aDecTrans.getRotate(),
292 TextEffectStyle2D::Outline));
293 aRetval = Primitive2DContainer { aNewTextEffect };
296 if(aShadow.is())
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,
320 // local parameters
321 const basegfx::BColor& rOverlineColor,
322 const basegfx::BColor& rTextlineColor,
323 TextLine eFontOverline,
324 TextLine eFontUnderline,
325 bool bUnderlineAbove,
326 TextStrikeout eTextStrikeout,
327 bool bWordLineMode,
328 TextEmphasisMark eTextEmphasisMark,
329 bool bEmphasisMarkAbove,
330 bool bEmphasisMarkBelow,
331 TextRelief eTextRelief,
332 bool bShadow)
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),
345 mbShadow(bShadow)
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());
369 return false;
372 // #i96475#
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()
384 || getShadow())
386 // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
387 // the own local decomposition for computation and thus creates all necessary
388 // geometric objects
389 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
391 else
393 // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
394 return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
398 // provide unique ID
399 ImplPrimitive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
401 } // end of namespace primitive2d
402 } // end of namespace drawinglayer
404 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */