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