Update ooo320-m1
[ooovba.git] / drawinglayer / source / primitive2d / textdecoratedprimitive2d.cxx
blob94059228b140a091d5cbef6dd4388cf1bc0f6fac
1 /*************************************************************************
3 * OpenOffice.org - a multi-platform office productivity suite
5 * $RCSfile: textdecoratedprimitive2d.cxx,v $
7 * $Revision: 1.12 $
9 * last change: $Author: aw $ $Date: 2008-05-27 14:11:20 $
11 * The Contents of this file are made available subject to
12 * the terms of GNU Lesser General Public License Version 2.1.
15 * GNU Lesser General Public License Version 2.1
16 * =============================================
17 * Copyright 2005 by Sun Microsystems, Inc.
18 * 901 San Antonio Road, Palo Alto, CA 94303, USA
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License version 2.1, as published by the Free Software Foundation.
24 * This library is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 * Lesser General Public License for more details.
29 * You should have received a copy of the GNU Lesser General Public
30 * License along with this library; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
32 * MA 02111-1307 USA
34 ************************************************************************/
36 // MARKER(update_precomp.py): autogen include statement, do not remove
37 #include "precompiled_drawinglayer.hxx"
39 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
41 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
42 #include <drawinglayer/attribute/strokeattribute.hxx>
43 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
44 #include <basegfx/matrix/b2dhommatrixtools.hxx>
45 #include <comphelper/processfactory.hxx>
46 #include <com/sun/star/i18n/WordType.hpp>
47 #include <drawinglayer/primitive2d/texteffectprimitive2d.hxx>
48 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
49 #include <com/sun/star/i18n/XBreakIterator.hpp>
50 #include <drawinglayer/primitive2d/transformprimitive2d.hxx>
52 //////////////////////////////////////////////////////////////////////////////
54 namespace drawinglayer
56 namespace primitive2d
58 void TextDecoratedPortionPrimitive2D::impCreateTextLine(
59 std::vector< Primitive2DReference >& rTarget,
60 basegfx::DecomposedB2DHomMatrixContainer& rDecTrans,
61 const basegfx::B2DHomMatrix &rUnscaledTransform,
62 FontUnderline eLineStyle,
63 double fLineOffset,
64 double fLineHeight,
65 double fLineWidth,
66 const basegfx::BColor& rLineColor) const
68 bool bDoubleLine(false);
69 bool bWaveLine(false);
70 bool bBoldLine(false);
71 const int* pDotDashArray(0);
72 basegfx::B2DLineJoin eLineJoin(basegfx::B2DLINEJOIN_NONE);
74 static const int aDottedArray[] = { 1, 1, 0}; // DOTTED LINE
75 static const int aDotDashArray[] = { 1, 1, 4, 1, 0}; // DASHDOT
76 static const int aDashDotDotArray[] = { 1, 1, 1, 1, 4, 1, 0}; // DASHDOTDOT
77 static const int aDashedArray[] = { 5, 2, 0}; // DASHED LINE
78 static const int aLongDashArray[] = { 7, 2, 0}; // LONGDASH
80 switch(eLineStyle)
82 default: // case FONT_UNDERLINE_SINGLE:
84 break;
86 case FONT_UNDERLINE_DOUBLE:
88 bDoubleLine = true;
89 break;
91 case FONT_UNDERLINE_DOTTED:
93 pDotDashArray = aDottedArray;
94 break;
96 case FONT_UNDERLINE_DASH:
98 pDotDashArray = aDashedArray;
99 break;
101 case FONT_UNDERLINE_LONGDASH:
103 pDotDashArray = aLongDashArray;
104 break;
106 case FONT_UNDERLINE_DASHDOT:
108 pDotDashArray = aDotDashArray;
109 break;
111 case FONT_UNDERLINE_DASHDOTDOT:
113 pDotDashArray = aDashDotDotArray;
114 break;
116 case FONT_UNDERLINE_SMALLWAVE:
118 bWaveLine = true;
119 break;
121 case FONT_UNDERLINE_WAVE:
123 bWaveLine = true;
124 break;
126 case FONT_UNDERLINE_DOUBLEWAVE:
128 bDoubleLine = true;
129 bWaveLine = true;
130 break;
132 case FONT_UNDERLINE_BOLD:
134 bBoldLine = true;
135 break;
137 case FONT_UNDERLINE_BOLDDOTTED:
139 bBoldLine = true;
140 pDotDashArray = aDottedArray;
141 break;
143 case FONT_UNDERLINE_BOLDDASH:
145 bBoldLine = true;
146 pDotDashArray = aDashedArray;
147 break;
149 case FONT_UNDERLINE_BOLDLONGDASH:
151 bBoldLine = true;
152 pDotDashArray = aLongDashArray;
153 break;
155 case FONT_UNDERLINE_BOLDDASHDOT:
157 bBoldLine = true;
158 pDotDashArray = aDotDashArray;
159 break;
161 case FONT_UNDERLINE_BOLDDASHDOTDOT:
163 bBoldLine = true;
164 pDotDashArray = aDashDotDotArray;
165 break;
167 case FONT_UNDERLINE_BOLDWAVE:
169 bWaveLine = true;
170 bBoldLine = true;
171 break;
175 if(bBoldLine)
177 fLineHeight *= 2.0;
180 if(bDoubleLine)
182 fLineOffset -= 0.50 * fLineHeight;
183 fLineHeight *= 0.64;
186 if(bWaveLine)
188 eLineJoin = basegfx::B2DLINEJOIN_ROUND;
189 fLineHeight *= 0.25;
192 // prepare Line and Stroke Attributes
193 const attribute::LineAttribute aLineAttribute(rLineColor, fLineHeight, eLineJoin);
194 attribute::StrokeAttribute aStrokeAttribute;
196 if(pDotDashArray)
198 ::std::vector< double > aDoubleArray;
200 for(const int* p = pDotDashArray; *p; ++p)
202 aDoubleArray.push_back((double)(*p) * fLineHeight);
205 aStrokeAttribute = attribute::StrokeAttribute(aDoubleArray);
208 // create base polygon and new primitive
209 basegfx::B2DPolygon aLine;
210 Primitive2DReference aNewPrimitive;
212 aLine.append(basegfx::B2DPoint(0.0, fLineOffset));
213 aLine.append(basegfx::B2DPoint(fLineWidth, fLineOffset));
214 aLine.transform(rUnscaledTransform);
216 if(bWaveLine)
218 double fWaveWidth(10.6 * fLineHeight);
220 if(FONT_UNDERLINE_SMALLWAVE == eLineStyle)
222 fWaveWidth *= 0.7;
224 else if(FONT_UNDERLINE_WAVE == eLineStyle)
226 // extra multiply to get the same WaveWidth as with the bold version
227 fWaveWidth *= 2.0;
230 aNewPrimitive = Primitive2DReference(new PolygonWavePrimitive2D(aLine, aLineAttribute, aStrokeAttribute, fWaveWidth, fWaveWidth * 0.5));
232 else
234 aNewPrimitive = Primitive2DReference(new PolygonStrokePrimitive2D(aLine, aLineAttribute, aStrokeAttribute));
237 // add primitive
238 rTarget.push_back(aNewPrimitive);
240 if(bDoubleLine)
242 // double line, create 2nd primitive with offset using TransformPrimitive based on
243 // already created NewPrimitive
244 double fLineDist(2.3 * fLineHeight);
246 if(bWaveLine)
248 fLineDist = 6.3 * fLineHeight;
251 basegfx::B2DHomMatrix aTransform;
253 // move base point of text to 0.0 and de-rotate
254 aTransform.translate(-rDecTrans.getTranslate().getX(), -rDecTrans.getTranslate().getY());
255 aTransform.rotate(-rDecTrans.getRotate());
257 // translate in Y by offset
258 aTransform.translate(0.0, fLineDist);
260 // move back and rotate
261 aTransform.rotate(rDecTrans.getRotate());
262 aTransform.translate(rDecTrans.getTranslate().getX(), rDecTrans.getTranslate().getY());
264 // add transform primitive
265 const Primitive2DSequence aContent(&aNewPrimitive, 1);
266 rTarget.push_back(Primitive2DReference(new TransformPrimitive2D(aTransform, aContent)));
270 void TextDecoratedPortionPrimitive2D::impCreateGeometryContent(
271 std::vector< Primitive2DReference >& rTarget,
272 basegfx::DecomposedB2DHomMatrixContainer& rDecTrans,
273 const String& rText,
274 xub_StrLen aTextPosition,
275 xub_StrLen aTextLength,
276 const ::std::vector< double >& rDXArray,
277 const FontAttributes& rFontAttributes) const
279 // create the SimpleTextPrimitive needed in any case
280 rTarget.push_back(Primitive2DReference(
281 new TextSimplePortionPrimitive2D(
282 rDecTrans.getB2DHomMatrix(),
283 rText,
284 aTextPosition,
285 aTextLength,
286 rDXArray,
287 rFontAttributes,
288 getLocale(),
289 getFontColor())));
291 // see if something else needs to be done
292 const bool bOverlineUsed(FONT_UNDERLINE_NONE != getFontOverline());
293 const bool bUnderlineUsed(FONT_UNDERLINE_NONE != getFontUnderline());
294 const bool bStrikeoutUsed(FONT_STRIKEOUT_NONE != getFontStrikeout());
296 if(bUnderlineUsed || bStrikeoutUsed || bOverlineUsed)
298 // common preparations
299 basegfx::B2DHomMatrix aUnscaledTransform;
300 TextLayouterDevice aTextLayouter;
302 // unscaled is needed since scale contains already the font size
303 aUnscaledTransform.shearX(rDecTrans.getShearX());
304 aUnscaledTransform.rotate(rDecTrans.getRotate());
305 aUnscaledTransform.translate(rDecTrans.getTranslate().getX(), rDecTrans.getTranslate().getY());
307 // TextLayouterDevice is needed to get metrics for text decorations like
308 // underline/strikeout/emphasis marks from it. For setup, the font size is needed
309 aTextLayouter.setFontAttributes(
310 getFontAttributes(),
311 rDecTrans.getScale().getX(),
312 rDecTrans.getScale().getY(),
313 getLocale());
315 // get text width
316 double fTextWidth(0.0);
318 if(rDXArray.empty())
320 fTextWidth = aTextLayouter.getTextWidth(rText, aTextPosition, aTextLength);
322 else
324 fTextWidth = rDXArray.back() * rDecTrans.getScale().getX();
325 const double fFontScaleX(rDecTrans.getScale().getX());
327 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
328 && !basegfx::fTools::equalZero(fFontScaleX))
330 // need to take FontScaling out of the DXArray
331 fTextWidth /= fFontScaleX;
335 if(bOverlineUsed)
337 // create primitive geometry for overline
338 impCreateTextLine(rTarget, rDecTrans, aUnscaledTransform, getFontOverline(), aTextLayouter.getOverlineOffset(),
339 aTextLayouter.getOverlineHeight(), fTextWidth, getOverlineColor());
342 if(bUnderlineUsed)
344 // create primitive geometry for underline
345 impCreateTextLine(rTarget, rDecTrans, aUnscaledTransform, getFontUnderline(), aTextLayouter.getUnderlineOffset(),
346 aTextLayouter.getUnderlineHeight(), fTextWidth, getTextlineColor());
349 if(bStrikeoutUsed)
351 // create primitive geometry for strikeout
352 if(FONT_STRIKEOUT_SLASH == getFontStrikeout() || FONT_STRIKEOUT_X == getFontStrikeout())
354 // strikeout with character
355 const sal_Unicode aStrikeoutChar(FONT_STRIKEOUT_SLASH == getFontStrikeout() ? '/' : 'X');
356 const String aSingleCharString(aStrikeoutChar);
357 const double fStrikeCharWidth(aTextLayouter.getTextWidth(aSingleCharString, 0, 1));
358 const double fStrikeCharCount(fabs(fTextWidth/fStrikeCharWidth));
359 const sal_uInt32 nStrikeCharCount(static_cast< sal_uInt32 >(fStrikeCharCount + 0.5));
360 std::vector<double> aDXArray(nStrikeCharCount);
361 String aStrikeoutString;
363 for(sal_uInt32 a(0); a < nStrikeCharCount; a++)
365 aStrikeoutString += aSingleCharString;
366 aDXArray[a] = (a + 1) * fStrikeCharWidth;
369 rTarget.push_back(Primitive2DReference(
370 new TextSimplePortionPrimitive2D(
371 rDecTrans.getB2DHomMatrix(),
372 aStrikeoutString,
374 aStrikeoutString.Len(),
375 aDXArray,
376 rFontAttributes,
377 getLocale(),
378 getFontColor())));
380 else
382 // strikeout with geometry
383 double fStrikeoutHeight(aTextLayouter.getUnderlineHeight());
384 double fStrikeoutOffset(aTextLayouter.getStrikeoutOffset());
385 bool bDoubleLine(false);
387 // set line attribute
388 switch(getFontStrikeout())
390 default : // case primitive2d::FONT_STRIKEOUT_SINGLE:
392 break;
394 case primitive2d::FONT_STRIKEOUT_DOUBLE:
396 bDoubleLine = true;
397 break;
399 case primitive2d::FONT_STRIKEOUT_BOLD:
401 fStrikeoutHeight *= 2.0;
402 break;
406 if(bDoubleLine)
408 fStrikeoutOffset -= 0.50 * fStrikeoutHeight;
409 fStrikeoutHeight *= 0.64;
412 // create base polygon and new primitive
413 basegfx::B2DPolygon aStrikeoutLine;
415 aStrikeoutLine.append(basegfx::B2DPoint(0.0, -fStrikeoutOffset));
416 aStrikeoutLine.append(basegfx::B2DPoint(fTextWidth, -fStrikeoutOffset));
417 aStrikeoutLine.transform(aUnscaledTransform);
419 const attribute::LineAttribute aLineAttribute(getFontColor(), fStrikeoutHeight, basegfx::B2DLINEJOIN_NONE);
420 Primitive2DReference aNewPrimitive(new PolygonStrokePrimitive2D(aStrikeoutLine, aLineAttribute));
422 // add primitive
423 rTarget.push_back(aNewPrimitive);
425 if(bDoubleLine)
427 // double line, create 2nd primitive with offset using TransformPrimitive based on
428 // already created NewPrimitive
429 const double fLineDist(2.0 * fStrikeoutHeight);
430 basegfx::B2DHomMatrix aTransform;
432 // move base point of text to 0.0 and de-rotate
433 aTransform.translate(-rDecTrans.getTranslate().getX(), -rDecTrans.getTranslate().getY());
434 aTransform.rotate(-rDecTrans.getRotate());
436 // translate in Y by offset
437 aTransform.translate(0.0, -fLineDist);
439 // move back and rotate
440 aTransform.rotate(rDecTrans.getRotate());
441 aTransform.translate(rDecTrans.getTranslate().getX(), rDecTrans.getTranslate().getY());
443 // add transform primitive
444 const Primitive2DSequence aContent(&aNewPrimitive, 1);
445 rTarget.push_back(Primitive2DReference(new TransformPrimitive2D(aTransform, aContent)));
451 // TODO: Handle Font Emphasis Above/Below
454 void TextDecoratedPortionPrimitive2D::impCorrectTextBoundary(::com::sun::star::i18n::Boundary& rNextWordBoundary) const
456 // truncate aNextWordBoundary to min/max possible values. This is necessary since the word start may be
457 // before/after getTextPosition() when a long string is the content and getTextPosition()
458 // is right inside a word. Same for end.
459 const sal_Int32 aMinPos(static_cast< sal_Int32 >(getTextPosition()));
460 const sal_Int32 aMaxPos(aMinPos + static_cast< sal_Int32 >(getTextLength()));
462 if(rNextWordBoundary.startPos < aMinPos)
464 rNextWordBoundary.startPos = aMinPos;
466 else if(rNextWordBoundary.startPos > aMaxPos)
468 rNextWordBoundary.startPos = aMaxPos;
471 if(rNextWordBoundary.endPos < aMinPos)
473 rNextWordBoundary.endPos = aMinPos;
475 else if(rNextWordBoundary.endPos > aMaxPos)
477 rNextWordBoundary.endPos = aMaxPos;
481 void TextDecoratedPortionPrimitive2D::impSplitSingleWords(
482 std::vector< Primitive2DReference >& rTarget,
483 basegfx::DecomposedB2DHomMatrixContainer& rDecTrans) const
485 // break iterator support
486 // made static so it only needs to be fetched once, even with many single
487 // constructed VclMetafileProcessor2D. It's still incarnated on demand,
488 // but exists for OOo runtime now by purpose.
489 static ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XBreakIterator > xLocalBreakIterator;
491 if(!xLocalBreakIterator.is())
493 ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xMSF(::comphelper::getProcessServiceFactory());
494 xLocalBreakIterator.set(xMSF->createInstance(rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator")), ::com::sun::star::uno::UNO_QUERY);
497 if(xLocalBreakIterator.is() && getTextLength())
499 // init word iterator, get first word and truncate to possibilities
500 ::com::sun::star::i18n::Boundary aNextWordBoundary(xLocalBreakIterator->getWordBoundary(
501 getText(), getTextPosition(), getLocale(), ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True));
503 if(aNextWordBoundary.endPos == getTextPosition())
505 // backward hit, force next word
506 aNextWordBoundary = xLocalBreakIterator->getWordBoundary(
507 getText(), getTextPosition() + 1, getLocale(), ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True);
510 impCorrectTextBoundary(aNextWordBoundary);
512 // prepare new font attributes WITHOUT outline
513 const FontAttributes aNewFontAttributes(
514 getFontAttributes().getFamilyName(),
515 getFontAttributes().getStyleName(),
516 getFontAttributes().getWeight(),
517 getFontAttributes().getSymbol(),
518 getFontAttributes().getVertical(),
519 getFontAttributes().getItalic(),
520 false, // no outline anymore, handled locally
521 getFontAttributes().getRTL(),
522 getFontAttributes().getBiDiStrong());
524 if(aNextWordBoundary.startPos == getTextPosition() && aNextWordBoundary.endPos == getTextLength())
526 // it IS only a single word, handle as one word
527 impCreateGeometryContent(rTarget, rDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttributes);
529 else
531 // prepare TextLayouter
532 const bool bNoDXArray(getDXArray().empty());
533 TextLayouterDevice aTextLayouter;
535 if(bNoDXArray)
537 // ..but only completely when no DXArray
538 aTextLayouter.setFontAttributes(
539 getFontAttributes(),
540 rDecTrans.getScale().getX(),
541 rDecTrans.getScale().getY(),
542 getLocale());
545 // do iterate over single words
546 while(aNextWordBoundary.startPos != aNextWordBoundary.endPos)
548 // prepare values for new portion
549 const xub_StrLen nNewTextStart(static_cast< xub_StrLen >(aNextWordBoundary.startPos));
550 const xub_StrLen nNewTextEnd(static_cast< xub_StrLen >(aNextWordBoundary.endPos));
552 // prepare transform for the single word
553 basegfx::B2DHomMatrix aNewTransform;
554 ::std::vector< double > aNewDXArray;
555 const bool bNewStartIsNotOldStart(nNewTextStart > getTextPosition());
557 if(!bNoDXArray)
559 // prepare new DXArray for the single word
560 aNewDXArray = ::std::vector< double >(
561 getDXArray().begin() + static_cast< sal_uInt32 >(nNewTextStart - getTextPosition()),
562 getDXArray().begin() + static_cast< sal_uInt32 >(nNewTextEnd - getTextPosition()));
565 if(bNewStartIsNotOldStart)
567 // needs to be moved to a new start position
568 double fOffset(0.0);
570 if(bNoDXArray)
572 // evaluate using TextLayouter
573 fOffset = aTextLayouter.getTextWidth(getText(), getTextPosition(), nNewTextStart);
575 else
577 // get from DXArray
578 const sal_uInt32 nIndex(static_cast< sal_uInt32 >(nNewTextStart - getTextPosition()));
579 fOffset = getDXArray()[nIndex - 1];
582 // need offset without FontScale for building the new transformation. The
583 // new transformation will be multiplied with the current text transformation
584 // so FontScale would be double
585 double fOffsetNoScale(fOffset);
586 const double fFontScaleX(rDecTrans.getScale().getX());
588 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
589 && !basegfx::fTools::equalZero(fFontScaleX))
591 fOffsetNoScale /= fFontScaleX;
594 // apply needed offset to transformation
595 aNewTransform.translate(fOffsetNoScale, 0.0);
597 if(!bNoDXArray)
599 // DXArray values need to be corrected with the offset, too. Here,
600 // take the scaled offset since the DXArray is scaled
601 const sal_uInt32 nArraySize(aNewDXArray.size());
603 for(sal_uInt32 a(0); a < nArraySize; a++)
605 aNewDXArray[a] -= fOffset;
610 // add text transformation to new transformation
611 aNewTransform *= rDecTrans.getB2DHomMatrix();
613 // create geometry content for the single word. Do not forget
614 // to use the new transformation
615 basegfx::DecomposedB2DHomMatrixContainer aDecTrans(aNewTransform);
617 impCreateGeometryContent(rTarget, aDecTrans, getText(), nNewTextStart,
618 nNewTextEnd - nNewTextStart, aNewDXArray, aNewFontAttributes);
620 if(aNextWordBoundary.endPos >= getTextPosition() + getTextLength())
622 // end reached
623 aNextWordBoundary.startPos = aNextWordBoundary.endPos;
625 else
627 // get new word portion
628 const sal_Int32 nLastEndPos(aNextWordBoundary.endPos);
630 aNextWordBoundary = xLocalBreakIterator->getWordBoundary(
631 getText(), aNextWordBoundary.endPos, getLocale(),
632 ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True);
634 if(nLastEndPos == aNextWordBoundary.endPos)
636 // backward hit, force next word
637 aNextWordBoundary = xLocalBreakIterator->getWordBoundary(
638 getText(), nLastEndPos + 1, getLocale(),
639 ::com::sun::star::i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True);
642 impCorrectTextBoundary(aNextWordBoundary);
649 Primitive2DSequence TextDecoratedPortionPrimitive2D::createLocalDecomposition(const geometry::ViewInformation2D& /*rViewInformation*/) const
651 std::vector< Primitive2DReference > aNewPrimitives;
652 basegfx::DecomposedB2DHomMatrixContainer aDecTrans(getTextTransform());
653 Primitive2DSequence aRetval;
655 // create basic geometry such as SimpleTextPrimitive, Overline, Underline,
656 // Strikeout, etc...
657 if(getWordLineMode())
659 // support for single word mode
660 impSplitSingleWords(aNewPrimitives, aDecTrans);
662 else
664 // prepare new font attributes WITHOUT outline
665 const FontAttributes aNewFontAttributes(
666 getFontAttributes().getFamilyName(),
667 getFontAttributes().getStyleName(),
668 getFontAttributes().getWeight(),
669 getFontAttributes().getSymbol(),
670 getFontAttributes().getVertical(),
671 getFontAttributes().getItalic(),
672 false, // no outline anymore, handled locally
673 getFontAttributes().getRTL(),
674 getFontAttributes().getBiDiStrong());
676 // handle as one word
677 impCreateGeometryContent(aNewPrimitives, aDecTrans, getText(), getTextPosition(), getTextLength(), getDXArray(), aNewFontAttributes);
680 // convert to Primitive2DSequence
681 const sal_uInt32 nMemberCount(aNewPrimitives.size());
683 if(nMemberCount)
685 aRetval.realloc(nMemberCount);
687 for(sal_uInt32 a(0); a < nMemberCount; a++)
689 aRetval[a] = aNewPrimitives[a];
693 // Handle Shadow, Outline and FontRelief
694 if(aRetval.hasElements())
696 // outline AND shadow depend on NO FontRelief (see dialog)
697 const bool bHasFontRelief(FONT_RELIEF_NONE != getFontRelief());
698 const bool bHasShadow(!bHasFontRelief && getShadow());
699 const bool bHasOutline(!bHasFontRelief && getFontAttributes().getOutline());
701 if(bHasShadow || bHasFontRelief || bHasOutline)
703 Primitive2DReference aShadow;
705 if(bHasShadow)
707 // create shadow with current content (in aRetval). Text shadow
708 // is constant, relative to font size, rotated with the text and has a
709 // constant color.
710 // shadow parameter values
711 static double fFactor(1.0 / 24.0);
712 const double fTextShadowOffset(aDecTrans.getScale().getY() * fFactor);
713 static basegfx::BColor aShadowColor(0.3, 0.3, 0.3);
715 // preapare shadow transform matrix
716 basegfx::B2DHomMatrix aShadowTransform;
717 aShadowTransform.translate(fTextShadowOffset, fTextShadowOffset);
719 // create shadow primitive
720 aShadow = Primitive2DReference(new ShadowPrimitive2D(
721 aShadowTransform,
722 aShadowColor,
723 aRetval));
726 if(bHasFontRelief)
728 // create emboss using an own helper primitive since this will
729 // be view-dependent
730 const basegfx::BColor aBBlack(0.0, 0.0, 0.0);
731 const bool bDefaultTextColor(aBBlack == getFontColor());
732 TextEffectStyle2D aTextEffectStyle2D(TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED);
734 if(bDefaultTextColor)
736 if(FONT_RELIEF_ENGRAVED == getFontRelief())
738 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED_DEFAULT;
740 else
742 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED_DEFAULT;
745 else
747 if(FONT_RELIEF_ENGRAVED == getFontRelief())
749 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_ENGRAVED;
751 else
753 aTextEffectStyle2D = TEXTEFFECTSTYLE2D_RELIEF_EMBOSSED;
757 Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
758 aRetval,
759 aDecTrans.getTranslate(),
760 aDecTrans.getRotate(),
761 aTextEffectStyle2D));
762 aRetval = Primitive2DSequence(&aNewTextEffect, 1);
764 else if(bHasOutline)
766 // create outline using an own helper primitive since this will
767 // be view-dependent
768 Primitive2DReference aNewTextEffect(new TextEffectPrimitive2D(
769 aRetval,
770 aDecTrans.getTranslate(),
771 aDecTrans.getRotate(),
772 TEXTEFFECTSTYLE2D_OUTLINE));
773 aRetval = Primitive2DSequence(&aNewTextEffect, 1);
776 if(aShadow.is())
778 // put shadow in front if there is one to paint timely before
779 // but placed behind content
780 const Primitive2DSequence aContent(aRetval);
781 aRetval = Primitive2DSequence(&aShadow, 1);
782 appendPrimitive2DSequenceToPrimitive2DSequence(aRetval, aContent);
787 return aRetval;
790 TextDecoratedPortionPrimitive2D::TextDecoratedPortionPrimitive2D(
792 // TextSimplePortionPrimitive2D parameters
793 const basegfx::B2DHomMatrix& rNewTransform,
794 const String& rText,
795 xub_StrLen aTextPosition,
796 xub_StrLen aTextLength,
797 const ::std::vector< double >& rDXArray,
798 const FontAttributes& rFontAttributes,
799 const ::com::sun::star::lang::Locale& rLocale,
800 const basegfx::BColor& rFontColor,
802 // local parameters
803 const basegfx::BColor& rOverlineColor,
804 const basegfx::BColor& rTextlineColor,
805 FontUnderline eFontOverline,
806 FontUnderline eFontUnderline,
807 bool bUnderlineAbove,
808 FontStrikeout eFontStrikeout,
809 bool bWordLineMode,
810 FontEmphasisMark eFontEmphasisMark,
811 bool bEmphasisMarkAbove,
812 bool bEmphasisMarkBelow,
813 FontRelief eFontRelief,
814 bool bShadow)
815 : TextSimplePortionPrimitive2D(rNewTransform, rText, aTextPosition, aTextLength, rDXArray, rFontAttributes, rLocale, rFontColor),
816 maOverlineColor(rOverlineColor),
817 maTextlineColor(rTextlineColor),
818 meFontOverline(eFontOverline),
819 meFontUnderline(eFontUnderline),
820 meFontStrikeout(eFontStrikeout),
821 meFontEmphasisMark(eFontEmphasisMark),
822 meFontRelief(eFontRelief),
823 mbUnderlineAbove(bUnderlineAbove),
824 mbWordLineMode(bWordLineMode),
825 mbEmphasisMarkAbove(bEmphasisMarkAbove),
826 mbEmphasisMarkBelow(bEmphasisMarkBelow),
827 mbShadow(bShadow)
831 bool TextDecoratedPortionPrimitive2D::operator==(const BasePrimitive2D& rPrimitive) const
833 if(TextSimplePortionPrimitive2D::operator==(rPrimitive))
835 const TextDecoratedPortionPrimitive2D& rCompare = (TextDecoratedPortionPrimitive2D&)rPrimitive;
837 return (getOverlineColor() == rCompare.getOverlineColor()
838 && getTextlineColor() == rCompare.getTextlineColor()
839 && getFontOverline() == rCompare.getFontOverline()
840 && getFontUnderline() == rCompare.getFontUnderline()
841 && getFontStrikeout() == rCompare.getFontStrikeout()
842 && getFontEmphasisMark() == rCompare.getFontEmphasisMark()
843 && getFontRelief() == rCompare.getFontRelief()
844 && getUnderlineAbove() == rCompare.getUnderlineAbove()
845 && getWordLineMode() == rCompare.getWordLineMode()
846 && getEmphasisMarkAbove() == rCompare.getEmphasisMarkAbove()
847 && getEmphasisMarkBelow() == rCompare.getEmphasisMarkBelow()
848 && getShadow() == rCompare.getShadow());
851 return false;
854 // #i96475#
855 // Added missing implementation. Decorations may (will) stick out of the text's
856 // inking area, so add them if needed
857 basegfx::B2DRange TextDecoratedPortionPrimitive2D::getB2DRange(const geometry::ViewInformation2D& rViewInformation) const
859 const bool bDecoratedIsNeeded(
860 FONT_UNDERLINE_NONE != getFontOverline()
861 || FONT_UNDERLINE_NONE != getFontUnderline()
862 || FONT_STRIKEOUT_NONE != getFontStrikeout()
863 || FONT_EMPHASISMARK_NONE != getFontEmphasisMark()
864 || FONT_RELIEF_NONE != getFontRelief()
865 || getShadow());
867 if(bDecoratedIsNeeded)
869 // decoration is used, fallback to BasePrimitive2D::getB2DRange which uses
870 // the own local decomposition for computation and thus creates all necessary
871 // geometric objects
872 return BasePrimitive2D::getB2DRange(rViewInformation);
874 else
876 // no relevant decoration used, fallback to TextSimplePortionPrimitive2D::getB2DRange
877 return TextSimplePortionPrimitive2D::getB2DRange(rViewInformation);
881 // provide unique ID
882 ImplPrimitrive2DIDBlock(TextDecoratedPortionPrimitive2D, PRIMITIVE2D_ID_TEXTDECORATEDPORTIONPRIMITIVE2D)
884 } // end of namespace primitive2d
885 } // end of namespace drawinglayer
887 //////////////////////////////////////////////////////////////////////////////
888 // eof