1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_drawinglayer.hxx"
30 
31 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
32 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
33 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
34 #include <drawinglayer/attribute/strokeattribute.hxx>
35 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
36 #include <basegfx/matrix/b2dhommatrixtools.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <com/sun/star/i18n/WordType.hpp>
39 #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
41 #include <com/sun/star/i18n/XBreakIterator.hpp>
42 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
43 #include <drawinglayer/primitive2d/textlineprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/textstrikeoutprimitive2d.hxx>
45 
46 //////////////////////////////////////////////////////////////////////////////
47 
48 namespace drawinglayer
49 {
50 	namespace primitive2d
51 	{
52         void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
53             std::vector< Primitive2DReference >& rTarget,
54             basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans,
55             const String& rText,
56 			xub_StrLen aTextPosition,
57 			xub_StrLen aTextLength,
58             const ::std::vector< double >& rDXArray,
59             const attribute::FontAttribute& rFontAttribute) const
60         {
61             // create the SimpleTextPrimitive needed in any case
62 	        rTarget.push_back(Primitive2DReference(
63                 new TextSimplePortionPrimitive2D(
64                     rDecTrans.getB2DHomMatrix(),
65                     rText,
66 				    aTextPosition,
67 				    aTextLength,
68                     rDXArray,
69                     rFontAttribute,
70                     getLocale(),
71                     getFontColor())));
72 
73 			// see if something else needs to be done
74             const bool bOverlineUsed(TEXT_LINE_NONE != getFontOverline());
75             const bool bUnderlineUsed(TEXT_LINE_NONE != getFontUnderline());
76 			const bool bStrikeoutUsed(TEXT_STRIKEOUT_NONE != getTextStrikeout());
77 
78             if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
79 			{
80 				// common preparations
81 			    TextLayouterDevice aTextLayouter;
82 
83                 // TextLayouterDevice is needed to get metrics for text decorations like
84                 // underline/strikeout/emphasis marks from it. For setup, the font size is needed
85 			    aTextLayouter.setFontAttribute(
86                     getFontAttribute(),
87                     rDecTrans.getScale().getX(),
88                     rDecTrans.getScale().getY(),
89                     getLocale());
90 
91 				// get text width
92 				double fTextWidth(0.0);
93 
94 				if(rDXArray.empty())
95 				{
96 					fTextWidth = aTextLayouter.getTextWidth(rText, aTextPosition, aTextLength);
97 				}
98 				else
99 				{
100 					fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
101                     const double fFontScaleX(rDecTrans.getScale().getX());
102 
103                     if(!basegfx::fTools::equal(fFontScaleX, 1.0)
104                         && !basegfx::fTools::equalZero(fFontScaleX))
105                     {
106                         // need to take FontScaling out of the DXArray
107                         fTextWidth /= fFontScaleX;
108                     }
109 				}
110 
111                 if(bOverlineUsed)
112                 {
113                     // create primitive geometry for overline
114 				    rTarget.push_back(Primitive2DReference(
115                         new TextLinePrimitive2D(
116                             rDecTrans.getB2DHomMatrix(),
117                             fTextWidth,
118                             aTextLayouter.getOverlineOffset(),
119                             aTextLayouter.getOverlineHeight(),
120                             getFontOverline(),
121                             getOverlineColor())));
122                 }
123 
124 				if(bUnderlineUsed)
125 				{
126 					// create primitive geometry for underline
127 				    rTarget.push_back(Primitive2DReference(
128                         new TextLinePrimitive2D(
129                             rDecTrans.getB2DHomMatrix(),
130                             fTextWidth,
131                             aTextLayouter.getUnderlineOffset(),
132                             aTextLayouter.getUnderlineHeight(),
133                             getFontUnderline(),
134                             getTextlineColor())));
135                 }
136 
137 				if(bStrikeoutUsed)
138 				{
139 					// create primitive geometry for strikeout
140                     if(TEXT_STRIKEOUT_SLASH == getTextStrikeout() || TEXT_STRIKEOUT_X == getTextStrikeout())
141                     {
142                         // strikeout with character
143                         const sal_Unicode aStrikeoutChar(TEXT_STRIKEOUT_SLASH == getTextStrikeout() ? '/' : 'X');
144 
145                         rTarget.push_back(Primitive2DReference(
146                             new TextCharacterStrikeoutPrimitive2D(
147                                 rDecTrans.getB2DHomMatrix(),
148                                 fTextWidth,
149                                 getFontColor(),
150                                 aStrikeoutChar,
151                                 getFontAttribute(),
152                                 getLocale())));
153                     }
154                     else
155                     {
156                         // strikeout with geometry
157                         rTarget.push_back(Primitive2DReference(
158                             new TextGeometryStrikeoutPrimitive2D(
159                                 rDecTrans.getB2DHomMatrix(),
160                                 fTextWidth,
161                                 getFontColor(),
162                                 aTextLayouter.getUnderlineHeight(),
163                                 aTextLayouter.getStrikeoutOffset(),
164                                 getTextStrikeout())));
165                     }
166 				}
167 			}
168 
169             // TODO: Handle Font Emphasis Above/Below
170         }
171 
172 		void TextDecoratedPortionPrimitive2D::impCorrectTextBoundary(::com::sun::star::i18n::Boundary& rNextWordBoundary) const
173 		{
174 			// truncate aNextWordBoundary to min/max possible values. This is necessary since the word start may be
175 			// before/after getTextPosition() when a long string is the content and getTextPosition()
176 			// is right inside a word. Same for end.
177 			const sal_Int32 aMinPos(static_cast< sal_Int32 >(getTextPosition()));
178 			const sal_Int32 aMaxPos(aMinPos + static_cast< sal_Int32 >(getTextLength()));
179 
180 			if(rNextWordBoundary.startPos < aMinPos)
181 			{
182 				rNextWordBoundary.startPos = aMinPos;
183 			}
184 			else if(rNextWordBoundary.startPos > aMaxPos)
185 			{
186 				rNextWordBoundary.startPos = aMaxPos;
187 			}
188 
189 			if(rNextWordBoundary.endPos < aMinPos)
190 			{
191 				rNextWordBoundary.endPos = aMinPos;
192 			}
193 			else if(rNextWordBoundary.endPos > aMaxPos)
194 			{
195 				rNextWordBoundary.endPos = aMaxPos;
196 			}
197 		}
198 
199         void TextDecoratedPortionPrimitive2D::impSplitSingleWords(
200             std::vector< Primitive2DReference >& rTarget,
201             basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose& rDecTrans) const
202         {
203             // break iterator support
204             // made static so it only needs to be fetched once, even with many single
205             // constructed VclMetafileProcessor2D. It's still incarnated on demand,
206             // but exists for OOo runtime now by purpose.
207             static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xLocalBreakIterator;
208 
209 			if(!xLocalBreakIterator.is())
210             {
211                 ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory());
212                 xLocalBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY);
213             }
214 
215             if(xLocalBreakIterator.is() && getTextLength())
216             {
217                 // init word iterator, get first word and truncate to possibilities
218                 ::com::sun::star::i18n::Boundary aNextWordBoundary(xLocalBreakIterator->getWordBoundary(
219                     getText(), getTextPosition(), getLocale(), ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True));
220 
221                 if(aNextWordBoundary.endPos == getTextPosition())
222                 {
223                     // backward hit, force next word
224                     aNextWordBoundary = xLocalBreakIterator->getWordBoundary(
225                         getText(), getTextPosition() + 1, getLocale(), ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True);
226                 }
227 
228 				impCorrectTextBoundary(aNextWordBoundary);
229 
230 				// prepare new font attributes WITHOUT outline
231                 const attribute::FontAttribute aNewFontAttribute(
232                     getFontAttribute().getFamilyName(),
233                     getFontAttribute().getStyleName(),
234                     getFontAttribute().getWeight(),
235                     getFontAttribute().getSymbol(),
236                     getFontAttribute().getVertical(),
237                     getFontAttribute().getItalic(),
238                     false,             // no outline anymore, handled locally
239                     getFontAttribute().getRTL(),
240                     getFontAttribute().getBiDiStrong());
241 
242 				if(aNextWordBoundary.startPos == getTextPosition() && aNextWordBoundary.endPos == getTextLength())
243 				{
244 					// it IS only a single word, handle as one word
245 	                impCreateGeometryContent(rTarget, rDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
246 				}
247 				else
248 				{
249 					// prepare TextLayouter
250 					const bool bNoDXArray(getDXArray().empty());
251 					TextLayouterDevice aTextLayouter;
252 
253 					if(bNoDXArray)
254 					{
255 						// ..but only completely when no DXArray
256 						aTextLayouter.setFontAttribute(
257                             getFontAttribute(),
258                             rDecTrans.getScale().getX(),
259                             rDecTrans.getScale().getY(),
260                             getLocale());
261 					}
262 
263 					// do iterate over single words
264 					while(aNextWordBoundary.startPos != aNextWordBoundary.endPos)
265 					{
266 						// prepare values for new portion
267 						const xub_StrLen nNewTextStart(static_cast< xub_StrLen >(aNextWordBoundary.startPos));
268 						const xub_StrLen nNewTextEnd(static_cast< xub_StrLen >(aNextWordBoundary.endPos));
269 
270 						// prepare transform for the single word
271 						basegfx::B2DHomMatrix aNewTransform;
272 						::std::vector< double > aNewDXArray;
273 						const bool bNewStartIsNotOldStart(nNewTextStart > getTextPosition());
274 
275 						if(!bNoDXArray)
276 						{
277 							// prepare new DXArray for the single word
278 							aNewDXArray = ::std::vector< double >(
279 								getDXArray().begin() + static_cast< sal_uInt32 >(nNewTextStart - getTextPosition()),
280 								getDXArray().begin() + static_cast< sal_uInt32 >(nNewTextEnd - getTextPosition()));
281 						}
282 
283 						if(bNewStartIsNotOldStart)
284 						{
285 							// needs to be moved to a new start position
286 							double fOffset(0.0);
287 
288 							if(bNoDXArray)
289 							{
290 								// evaluate using TextLayouter
291 								fOffset = aTextLayouter.getTextWidth(getText(), getTextPosition(), nNewTextStart);
292 							}
293 							else
294 							{
295 								// get from DXArray
296 								const sal_uInt32 nIndex(static_cast< sal_uInt32 >(nNewTextStart - getTextPosition()));
297 								fOffset = getDXArray()[nIndex - 1];
298 							}
299 
300                             // need offset without FontScale for building the new transformation. The
301                             // new transformation will be multiplied with the current text transformation
302                             // so FontScale would be double
303 							double fOffsetNoScale(fOffset);
304                             const double fFontScaleX(rDecTrans.getScale().getX());
305 
306                             if(!basegfx::fTools::equal(fFontScaleX, 1.0)
307                                 && !basegfx::fTools::equalZero(fFontScaleX))
308                             {
309                                 fOffsetNoScale /= fFontScaleX;
310                             }
311 
312 							// apply needed offset to transformation
313                             aNewTransform.translate(fOffsetNoScale, 0.0);
314 
315 							if(!bNoDXArray)
316 							{
317 								// DXArray values need to be corrected with the offset, too. Here,
318                                 // take the scaled offset since the DXArray is scaled
319 								const sal_uInt32 nArraySize(aNewDXArray.size());
320 
321 								for(sal_uInt32 a(0); a < nArraySize; a++)
322 								{
323 									aNewDXArray[a] -= fOffset;
324 								}
325 							}
326 						}
327 
328 						// add text transformation to new transformation
329 						aNewTransform *= rDecTrans.getB2DHomMatrix();
330 
331 						// create geometry content for the single word. Do not forget
332 						// to use the new transformation
333 						basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(aNewTransform);
334 
335 						impCreateGeometryContent(rTarget, aDecTrans, getText(), nNewTextStart,
336 							nNewTextEnd - nNewTextStart, aNewDXArray, aNewFontAttribute);
337 
338                         if(aNextWordBoundary.endPos >= getTextPosition() + getTextLength())
339                         {
340                             // end reached
341                             aNextWordBoundary.startPos = aNextWordBoundary.endPos;
342                         }
343                         else
344                         {
345                             // get new word portion
346                             const sal_Int32 nLastEndPos(aNextWordBoundary.endPos);
347 
348                             aNextWordBoundary = xLocalBreakIterator->getWordBoundary(
349                                 getText(), aNextWordBoundary.endPos, getLocale(),
350                                 ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True);
351 
352                             if(nLastEndPos == aNextWordBoundary.endPos)
353                             {
354                                 // backward hit, force next word
355                                 aNextWordBoundary = xLocalBreakIterator->getWordBoundary(
356                                     getText(), nLastEndPos + 1, getLocale(),
357                                     ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True);
358                             }
359 
360                             impCorrectTextBoundary(aNextWordBoundary);
361                         }
362 					}
363 				}
364             }
365         }
366 
367 		Primitive2DSequence TextDecoratedPortionPrimitive2D::create2DDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
368         {
369             std::vector< Primitive2DReference > aNewPrimitives;
370             basegfx::tools::B2DHomMatrixBufferedOnDemandDecompose aDecTrans(getTextTransform());
371             Primitive2DSequence aRetval;
372 
373             // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
374             // Strikeout, etc...
375             if(getWordLineMode())
376             {
377                 // support for single word mode
378                 impSplitSingleWords(aNewPrimitives, aDecTrans);
379             }
380             else
381             {
382                 // prepare new font attributes WITHOUT outline
383                 const attribute::FontAttribute aNewFontAttribute(
384                     getFontAttribute().getFamilyName(),
385                     getFontAttribute().getStyleName(),
386                     getFontAttribute().getWeight(),
387                     getFontAttribute().getSymbol(),
388                     getFontAttribute().getVertical(),
389                     getFontAttribute().getItalic(),
390                     false,             // no outline anymore, handled locally
391                     getFontAttribute().getRTL(),
392                     getFontAttribute().getBiDiStrong());
393 
394 				// handle as one word
395                 impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttribute);
396             }
397 
398             // convert to Primitive2DSequence
399             const sal_uInt32 nMemberCount(aNewPrimitives.size());
400 
401 			if(nMemberCount)
402             {
403                 aRetval.realloc(nMemberCount);
404 
405                 for(sal_uInt32 a(0); a < nMemberCount; a++)
406                 {
407                     aRetval[a] = aNewPrimitives[a];
408                 }
409             }
410 
411             // Handle Shadow, Outline and TextRelief
412             if(aRetval.hasElements())
413             {
414                 // outline AND shadow depend on NO TextRelief (see dialog)
415                 const bool bHasTextRelief(TEXT_RELIEF_NONE != getTextRelief());
416                 const bool bHasShadow(!bHasTextRelief && getShadow());
417                 const bool bHasOutline(!bHasTextRelief && getFontAttribute().getOutline());
418 
419                 if(bHasShadow || bHasTextRelief || bHasOutline)
420                 {
421                     Primitive2DReference aShadow;
422 
423                     if(bHasShadow)
424                     {
425                         // create shadow with current content (in aRetval). Text shadow
426                         // is constant, relative to font size, rotated with the text and has a
427                         // constant color.
428                         // shadow parameter values
429                         static double fFactor(1.0 / 24.0);
430                         const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
431                         static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);
432 
433                         // preapare shadow transform matrix
434                         const basegfx::B2DHomMatrix aShadowTransform(basegfx::tools::createTranslateB2DHomMatrix(
435                             fTextShadowOffset, fTextShadowOffset));
436 
437                         // create shadow primitive
438                         aShadow = Primitive2DReference(new ShadowPrimitive2D(
439                             aShadowTransform,
440                             aShadowColor,
441                             aRetval));
442                     }
443 
444                     if(bHasTextRelief)
445                     {
446                         // create emboss using an own helper primitive since this will
447                         // be view-dependent
448 						const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
449 						const bool bDefaultTextColor(aBBlack == getFontColor());
450 						TextEffectStyle2D aTextEffectStyle2D(TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED);
451 
452 						if(bDefaultTextColor)
453 						{
454 							if(TEXT_RELIEF_ENGRAVED == getTextRelief())
455 							{
456 								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED_DEFAULT;
457 							}
458 							else
459 							{
460 								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED_DEFAULT;
461 							}
462 						}
463 						else
464 						{
465 							if(TEXT_RELIEF_ENGRAVED == getTextRelief())
466 							{
467 								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED;
468 							}
469 							else
470 							{
471 								aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED;
472 							}
473 						}
474 
475 						Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
476 							aRetval,
477 							aDecTrans.getTranslate(),
478 							aDecTrans.getRotate(),
479                             aTextEffectStyle2D));
480                         aRetval = Primitive2DSequence(&aNewTextEffect, 1);
481                     }
482                     else if(bHasOutline)
483                     {
484                         // create outline using an own helper primitive since this will
485                         // be view-dependent
486                         Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
487 							aRetval,
488 							aDecTrans.getTranslate(),
489 							aDecTrans.getRotate(),
490 							TEXTEFFECTSTYLE2D_OUTLINE));
491                         aRetval = Primitive2DSequence(&aNewTextEffect, 1);
492                     }
493 
494                     if(aShadow.is())
495                     {
496                         // put shadow in front if there is one to paint timely before
497                         // but placed behind content
498                         const Primitive2DSequence aContent(aRetval);
499                         aRetval = Primitive2DSequence(&aShadow, 1);
500                         appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aContent);
501                     }
502                 }
503             }
504 
505             return aRetval;
506         }
507 
508         TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
509 
510             // TextSimplePortionPrimitive2D parameters
511 			const basegfx::B2DHomMatrix& rNewTransform,
512 			const String& rText,
513 			xub_StrLen aTextPosition,
514 			xub_StrLen aTextLength,
515 			const ::std::vector< double >& rDXArray,
516             const attribute::FontAttribute& rFontAttribute,
517             const ::com::sun::star::lang::Locale& rLocale,
518 			const basegfx::BColor& rFontColor,
519 
520             // local parameters
521             const basegfx::BColor& rOverlineColor,
522             const basegfx::BColor& rTextlineColor,
523             TextLine eFontOverline,
524             TextLine eFontUnderline,
525 			bool bUnderlineAbove,
526 			TextStrikeout eTextStrikeout,
527 			bool bWordLineMode,
528 			TextEmphasisMark eTextEmphasisMark,
529 			bool bEmphasisMarkAbove,
530 			bool bEmphasisMarkBelow,
531 			TextRelief eTextRelief,
532 			bool bShadow)
533 		:	TextSimplePortionPrimitive2D(rNewTransform, rText, aTextPosition, aTextLength, rDXArray, rFontAttribute, rLocale, rFontColor),
534             maOverlineColor(rOverlineColor),
535             maTextlineColor(rTextlineColor),
536             meFontOverline(eFontOverline),
537             meFontUnderline(eFontUnderline),
538 			meTextStrikeout(eTextStrikeout),
539 			meTextEmphasisMark(eTextEmphasisMark),
540 			meTextRelief(eTextRelief),
541 			mbUnderlineAbove(bUnderlineAbove),
542 			mbWordLineMode(bWordLineMode),
543 			mbEmphasisMarkAbove(bEmphasisMarkAbove),
544 			mbEmphasisMarkBelow(bEmphasisMarkBelow),
545 			mbShadow(bShadow)
546 		{
547 		}
548 
549 		bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
550 		{
551 			if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
552 			{
553 				const TextDecoratedPortionPrimitive2D& rCompare = (TextDecoratedPortionPrimitive2D&)rPrimitive;
554 
555                 return (getOverlineColor() == rCompare.getOverlineColor()
556                     && getTextlineColor() == rCompare.getTextlineColor()
557                     && getFontOverline() == rCompare.getFontOverline()
558                     && getFontUnderline() == rCompare.getFontUnderline()
559 					&& getTextStrikeout() == rCompare.getTextStrikeout()
560 					&& getTextEmphasisMark() == rCompare.getTextEmphasisMark()
561 					&& getTextRelief() == rCompare.getTextRelief()
562 					&& getUnderlineAbove() == rCompare.getUnderlineAbove()
563 					&& getWordLineMode() == rCompare.getWordLineMode()
564 					&& getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
565 					&& getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
566 					&& getShadow() == rCompare.getShadow());
567 			}
568 
569 			return false;
570 		}
571 
572         // #i96475#
573         // Added missing implementation. Decorations may (will) stick out of the text's
574         // inking area, so add them if needed
575 		basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
576 		{
577 			const bool bDecoratedIsNeeded(
578                 TEXT_LINE_NONE != getFontOverline()
579              || TEXT_LINE_NONE != getFontUnderline()
580              || TEXT_STRIKEOUT_NONE != getTextStrikeout()
581              || TEXT_EMPHASISMARK_NONE != getTextEmphasisMark()
582              || TEXT_RELIEF_NONE != getTextRelief()
583              || getShadow());
584 
585             if(bDecoratedIsNeeded)
586             {
587                 // decoration is used, fallback to BufferedDecompositionPrimitive2D::getB2DRange which uses
588                 // the own local decomposition for computation and thus creates all necessary
589                 // geometric objects
590                 return BufferedDecompositionPrimitive2D::getB2DRange(rViewInformation);
591             }
592             else
593             {
594                 // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
595                 return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
596             }
597         }
598 
599 		// provide unique ID
600 		ImplPrimitrive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
601 
602 	} // end of namespace primitive2d
603 } // end of namespace drawinglayer
604 
605 //////////////////////////////////////////////////////////////////////////////
606 // eof
607