xref: /trunk/main/drawinglayer/source/primitive2d/textdecoratedprimitive2d.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
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