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