LibreOffice Module drawinglayer (master) 1
textdecoratedprimitive2d.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
28
29
31{
33 Primitive2DContainer& rTarget,
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
41 {
42 // create the SimpleTextPrimitive needed in any case
45 rDecTrans.getB2DHomMatrix(),
46 rText,
47 nTextPosition,
48 nTextLength,
49 std::vector(rDXArray),
50 std::vector(rKashidaArray),
51 rFontAttribute,
52 getLocale(),
53 getFontColor())));
54
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());
59
60 if(!(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed))
61 return;
62
63 // common preparations
64 TextLayouterDevice aTextLayouter;
65
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(),
72 getLocale());
73
74 // get text width
75 double fTextWidth(0.0);
76
77 if(rDXArray.empty())
78 {
79 fTextWidth = aTextLayouter.getTextWidth(rText, nTextPosition, nTextLength);
80 }
81 else
82 {
83 fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
84 const double fFontScaleX(rDecTrans.getScale().getX());
85
86 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
87 && !basegfx::fTools::equalZero(fFontScaleX))
88 {
89 // need to take FontScaling out of the DXArray
90 fTextWidth /= fFontScaleX;
91 }
92 }
93
94 if(bOverlineUsed)
95 {
96 // create primitive geometry for overline
99 rDecTrans.getB2DHomMatrix(),
100 fTextWidth,
101 aTextLayouter.getOverlineOffset(),
102 aTextLayouter.getOverlineHeight(),
104 getOverlineColor())));
105 }
106
107 if(bUnderlineUsed)
108 {
109 // create primitive geometry for underline
112 rDecTrans.getB2DHomMatrix(),
113 fTextWidth,
114 aTextLayouter.getUnderlineOffset(),
115 aTextLayouter.getUnderlineHeight(),
117 getTextlineColor())));
118 }
119
120 if(!bStrikeoutUsed)
121 return;
122
123 // create primitive geometry for strikeout
125 {
126 // strikeout with character
127 const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');
128
131 rDecTrans.getB2DHomMatrix(),
132 fTextWidth,
133 getFontColor(),
134 aStrikeoutChar,
136 getLocale())));
137 }
138 else
139 {
140 // strikeout with geometry
143 rDecTrans.getB2DHomMatrix(),
144 fTextWidth,
145 getFontColor(),
146 aTextLayouter.getUnderlineHeight(),
147 aTextLayouter.getStrikeoutOffset(),
148 getTextStrikeout())));
149 }
150
151 // TODO: Handle Font Emphasis Above/Below
152 }
153
155 {
156 if(getWordLineMode())
157 {
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));
162
163 if(!aBroken.empty())
164 {
165 // was indeed split to several words, use as result
166 rContainer.append(std::move(aBroken));
167 return;
168 }
169 else
170 {
171 // no split, was already a single word. Continue to
172 // decompose local entity
173 }
174 }
176 Primitive2DContainer aRetval;
177
178 // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
179 // Strikeout, etc...
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());
192
193 // handle as one word
194 impCreateGeometryContent(aRetval, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), getKashidaArray(), aNewFontAttribute);
195
196 // Handle Shadow, Outline and TextRelief
197 if(!aRetval.empty())
198 {
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());
203
204 if(bHasShadow || bHasTextRelief || bHasOutline)
205 {
206 Primitive2DReference aShadow;
207
208 if(bHasShadow)
209 {
210 // create shadow with current content (in aRetval). Text shadow
211 // is constant, relative to font size, rotated with the text and has a
212 // constant color.
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);
217
218 // prepare shadow transform matrix
220 fTextShadowOffset, fTextShadowOffset));
221
222 // create shadow primitive
223 aShadow = new ShadowPrimitive2D(
224 aShadowTransform,
225 aShadowColor,
226 0, // fShadowBlur = 0, there's no blur for text shadow yet.
227 Primitive2DContainer(aRetval));
228 }
229
230 if(bHasTextRelief)
231 {
232 // create emboss using an own helper primitive since this will
233 // be view-dependent
234 const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
235 const bool bDefaultTextColor(aBBlack == getFontColor());
237
238 if(bDefaultTextColor)
239 {
241 {
242 aTextEffectStyle2D = TextEffectStyle2D::ReliefEngravedDefault;
243 }
244 else
245 {
246 aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossedDefault;
247 }
248 }
249 else
250 {
252 {
253 aTextEffectStyle2D = TextEffectStyle2D::ReliefEngraved;
254 }
255 else
256 {
257 aTextEffectStyle2D = TextEffectStyle2D::ReliefEmbossed;
258 }
259 }
260
262 std::move(aRetval),
263 aDecTrans.getTranslate(),
264 aDecTrans.getRotate(),
265 aTextEffectStyle2D));
266 aRetval = Primitive2DContainer { aNewTextEffect };
267 }
268 else if(bHasOutline)
269 {
270 // create outline using an own helper primitive since this will
271 // be view-dependent
273 std::move(aRetval),
274 aDecTrans.getTranslate(),
275 aDecTrans.getRotate(),
277 aRetval = Primitive2DContainer { aNewTextEffect };
278 }
279
280 if(aShadow.is())
281 {
282 // put shadow in front if there is one to paint timely before
283 // but placed behind content
284 aRetval.insert(aRetval.begin(), aShadow);
285 }
286 }
287 }
288
289 rContainer.append(std::move(aRetval));
290 }
291
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,
304
305 // local parameters
306 const basegfx::BColor& rOverlineColor,
307 const basegfx::BColor& rTextlineColor,
308 TextLine eFontOverline,
309 TextLine eFontUnderline,
310 bool bUnderlineAbove,
311 TextStrikeout eTextStrikeout,
312 bool bWordLineMode,
313 TextEmphasisMark eTextEmphasisMark,
314 bool bEmphasisMarkAbove,
315 bool bEmphasisMarkBelow,
316 TextRelief eTextRelief,
317 bool bShadow)
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),
330 mbShadow(bShadow)
331 {
332 }
333
335 {
336 if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
337 {
338 const TextDecoratedPortionPrimitive2D& rCompare = static_cast<const TextDecoratedPortionPrimitive2D&>(rPrimitive);
339
340 return (getOverlineColor() == rCompare.getOverlineColor()
341 && getTextlineColor() == rCompare.getTextlineColor()
342 && getFontOverline() == rCompare.getFontOverline()
343 && getFontUnderline() == rCompare.getFontUnderline()
344 && getTextStrikeout() == rCompare.getTextStrikeout()
346 && getTextRelief() == rCompare.getTextRelief()
347 && getUnderlineAbove() == rCompare.getUnderlineAbove()
348 && getWordLineMode() == rCompare.getWordLineMode()
351 && getShadow() == rCompare.getShadow());
352 }
353
354 return false;
355 }
356
357 // #i96475#
358 // Added missing implementation. Decorations may (will) stick out of the text's
359 // inking area, so add them if needed
361 {
362 // check if this needs to be a TextDecoratedPortionPrimitive2D or
363 // if a TextSimplePortionPrimitive2D would be sufficient
369 || getShadow())
370 {
371 // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
372 // the own local decomposition for computation and thus creates all necessary
373 // geometric objects
374 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
375 }
376 else
377 {
378 // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
379 return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
380 }
381 }
382
383 // provide unique ID
385 {
387 }
388
389} // end of namespace
390
391/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool bHasShadow
TYPE getX() const
TYPE getY() const
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const
The default implementation will use getDecomposition results to create the range.
Primitive2DContainer extractResult(BreakupUnit aBreakupUnit=BreakupUnit::Character)
get result
virtual sal_uInt32 getPrimitive2DID() const override
provide unique ID
virtual bool operator==(const BasePrimitive2D &rPrimitive) const override
compare operator
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
virtual void create2DDecomposition(Primitive2DContainer &rContainer, const geometry::ViewInformation2D &rViewInformation) const override
local decomposition.
void impCreateGeometryContent(Primitive2DContainer &rTarget, basegfx::utils::B2DHomMatrixBufferedOnDemandDecompose const &rDecTrans, const OUString &rText, sal_Int32 nTextPosition, sal_Int32 nTextLength, const ::std::vector< double > &rDXArray, const ::std::vector< sal_Bool > &rKashidaArray, const attribute::FontAttribute &rFontAttribute) const
helper methods
TextDecoratedPortionPrimitive2D(const basegfx::B2DHomMatrix &rNewTransform, const OUString &rText, sal_Int32 nTextPosition, sal_Int32 nTextLength, std::vector< double > &&rDXArray, std::vector< sal_Bool > &&rKashidaArray, const attribute::FontAttribute &rFontAttribute, const css::lang::Locale &rLocale, const basegfx::BColor &rFontColor, const Color &rFillColor, const basegfx::BColor &rOverlineColor, const basegfx::BColor &rTextlineColor, TextLine eFontOverline=TEXT_LINE_NONE, TextLine eFontUnderline=TEXT_LINE_NONE, bool bUnderlineAbove=false, TextStrikeout eTextStrikeout=TEXT_STRIKEOUT_NONE, bool bWordLineMode=false, TextEmphasisMark eTextEmphasisMark=TEXT_FONT_EMPHASIS_MARK_NONE, bool bEmphasisMarkAbove=true, bool bEmphasisMarkBelow=false, TextRelief eTextRelief=TEXT_RELIEF_NONE, bool bShadow=false)
constructor
void setFontAttribute(const attribute::FontAttribute &rFontAttribute, double fFontScaleX, double fFontScaleY, const css::lang::Locale &rLocale)
double getTextWidth(const OUString &rText, sal_uInt32 nIndex, sal_uInt32 nLength) const
TextSimplePortionPrimitive2D(basegfx::B2DHomMatrix aNewTransform, OUString aText, sal_Int32 nTextPosition, sal_Int32 nTextLength, std::vector< double > &&rDXArray, std::vector< sal_Bool > &&rKashidaArray, attribute::FontAttribute aFontAttribute, css::lang::Locale aLocale, const basegfx::BColor &rFontColor, bool bFilled=false, tools::Long nWidthToFill=0, const Color &rTextFillColor=COL_TRANSPARENT)
constructor
virtual basegfx::B2DRange getB2DRange(const geometry::ViewInformation2D &rViewInformation) const override
get range
const ::std::vector< sal_Bool > & getKashidaArray() const
const ::std::vector< double > & getDXArray() const
const attribute::FontAttribute & getFontAttribute() const
const basegfx::B2DHomMatrix & getTextTransform() const
data read access
#define PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D
FilterGroup & rTarget
bool equalZero(const T &rfVal)
bool equal(T const &rfValA, T const &rfValB)
B2DHomMatrix createTranslateB2DHomMatrix(double fTranslateX, double fTranslateY)
rtl::Reference< BasePrimitive2D > Primitive2DReference
Definition: CommonTypes.hxx:27
TextEffectStyle2D
TextEffectStyle2D definition.
TextRelief
TextRelief definition.
TextEmphasisMark
TextEmphasisMark definition.
TextStrikeout
FontStrikeout definition.
sal_uInt16 sal_Unicode