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