1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 
31 #include <svx/svdotext.hxx>
32 #include <svx/svdoutl.hxx>
33 #include <basegfx/vector/b2dvector.hxx>
34 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
35 #include <basegfx/range/b2drange.hxx>
36 #include <vcl/salbtype.hxx>
37 #include <svl/itemset.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/polygon/b2dpolygon.hxx>
40 #include <algorithm>
41 #include <svx/xtextit.hxx>
42 #include <svx/xftshtit.hxx>
43 #include <vcl/virdev.hxx>
44 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
45 #include <com/sun/star/i18n/ScriptType.hdl>
46 #include <com/sun/star/i18n/XBreakIterator.hpp>
47 #include <comphelper/processfactory.hxx>
48 #include <com/sun/star/i18n/CharacterIteratorMode.hdl>
49 #include <editeng/unolingu.hxx>
50 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
51 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
52 #include <basegfx/color/bcolor.hxx>
53 
54 //////////////////////////////////////////////////////////////////////////////
55 // primitive decomposition helpers
56 
57 #include <basegfx/polygon/b2dlinegeometry.hxx>
58 #include <drawinglayer/attribute/strokeattribute.hxx>
59 #include <svx/xlnclit.hxx>
60 #include <svx/xlntrit.hxx>
61 #include <svx/xlnwtit.hxx>
62 #include <svx/xlinjoit.hxx>
63 #include <svx/xlndsit.hxx>
64 #include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
65 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
66 #include <editeng/editstat.hxx>
67 #include <svx/unoapi.hxx>
68 #include <drawinglayer/geometry/viewinformation2d.hxx>
69 #include <svx/sdr/attribute/sdrformtextoutlineattribute.hxx>
70 
71 //////////////////////////////////////////////////////////////////////////////
72 
73 using namespace ::com::sun::star::uno;
74 using namespace ::com::sun::star::lang;
75 using namespace ::com::sun::star::i18n;
76 
77 //////////////////////////////////////////////////////////////////////////////
78 // PathTextPortion helper
79 
80 namespace
81 {
82 	class impPathTextPortion
83 	{
84 		basegfx::B2DVector							maOffset;
85 		String										maText;
86 		xub_StrLen									mnTextStart;
87 		xub_StrLen									mnTextLength;
88 		sal_uInt16									mnParagraph;
89 		xub_StrLen									mnIndex;
90 		SvxFont										maFont;
91 		::std::vector< double >						maDblDXArray;	// double DXArray, font size independent -> unit coordinate system
92         ::com::sun::star::lang::Locale				maLocale;
93 
94 		// bitfield
95 		unsigned									mbRTL : 1;
96 
97 	public:
98 		impPathTextPortion(DrawPortionInfo& rInfo)
99 		:	maOffset(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y()),
100 			maText(rInfo.mrText),
101 			mnTextStart(rInfo.mnTextStart),
102 			mnTextLength(rInfo.mnTextLen),
103 			mnParagraph(rInfo.mnPara),
104 			mnIndex(rInfo.mnIndex),
105 			maFont(rInfo.mrFont),
106             maDblDXArray(),
107 			maLocale(rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale()),
108 			mbRTL(rInfo.mrFont.IsVertical() ? false : rInfo.IsRTL())
109 		{
110 			if(mnTextLength && rInfo.mpDXArray)
111 			{
112 				maDblDXArray.reserve(mnTextLength);
113 
114 				for(xub_StrLen a(0); a < mnTextLength; a++)
115 				{
116 					maDblDXArray.push_back((double)rInfo.mpDXArray[a]);
117 				}
118 			}
119 		}
120 
121 		// for ::std::sort
122 		bool operator<(const impPathTextPortion& rComp) const
123 		{
124 			if(mnParagraph < rComp.mnParagraph)
125 			{
126 				return true;
127 			}
128 
129 			if(maOffset.getX() < rComp.maOffset.getX())
130 			{
131 				return true;
132 			}
133 
134 			return (maOffset.getY() < rComp.maOffset.getY());
135 		}
136 
137 		const basegfx::B2DVector& getOffset() const { return maOffset; }
138 		const String& getText() const { return maText; }
139 		xub_StrLen getTextStart() const { return mnTextStart; }
140 		xub_StrLen getTextLength() const { return mnTextLength; }
141 		sal_uInt16 getParagraph() const { return mnParagraph; }
142 		xub_StrLen getIndex() const { return mnIndex; }
143 		const SvxFont& getFont() const { return maFont; }
144 		bool isRTL() const { return mbRTL; }
145 		const ::std::vector< double >& getDoubleDXArray() const { return maDblDXArray; }
146         const ::com::sun::star::lang::Locale& getLocale() const { return maLocale; }
147 
148 		xub_StrLen getPortionIndex(xub_StrLen nIndex, xub_StrLen nLength) const
149 		{
150 			if(mbRTL)
151 			{
152 				return (mnTextStart + (mnTextLength - (nIndex + nLength)));
153 			}
154 			else
155 			{
156 				return (mnTextStart + nIndex);
157 			}
158 		}
159 
160 		double getDisplayLength(xub_StrLen nIndex, xub_StrLen nLength) const
161 		{
162 			drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
163 			double fRetval(0.0);
164 
165 			if(maFont.IsVertical())
166 			{
167 				fRetval = aTextLayouter.getTextHeight() * (double)nLength;
168 			}
169 			else
170 			{
171 				fRetval = aTextLayouter.getTextWidth(maText, getPortionIndex(nIndex, nLength), nLength);
172 			}
173 
174 			return fRetval;
175 		}
176 	};
177 } // end of anonymous namespace
178 
179 //////////////////////////////////////////////////////////////////////////////
180 // TextBreakup helper
181 
182 namespace
183 {
184 	class impTextBreakupHandler
185 	{
186 		SdrOutliner&								mrOutliner;
187 		::std::vector< impPathTextPortion >			maPathTextPortions;
188 
189 		DECL_LINK(decompositionPathTextPrimitive, DrawPortionInfo* );
190 
191 	public:
192 		impTextBreakupHandler(SdrOutliner& rOutliner)
193 		:	mrOutliner(rOutliner)
194 		{
195 		}
196 
197 		const ::std::vector< impPathTextPortion >& decompositionPathTextPrimitive()
198 		{
199 			// strip portions to maPathTextPortions
200 			mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decompositionPathTextPrimitive));
201 			mrOutliner.StripPortions();
202 
203 			if(!maPathTextPortions.empty())
204 			{
205 				// sort portions by paragraph, x and y
206 				::std::sort(maPathTextPortions.begin(), maPathTextPortions.end());
207 			}
208 
209 			return maPathTextPortions;
210 		}
211 	};
212 
213 	IMPL_LINK(impTextBreakupHandler, decompositionPathTextPrimitive, DrawPortionInfo*, pInfo)
214 	{
215 		maPathTextPortions.push_back(impPathTextPortion(*pInfo));
216 		return 0;
217 	}
218 } // end of anonymous namespace
219 
220 //////////////////////////////////////////////////////////////////////////////
221 // TextBreakup one poly and one paragraph helper
222 
223 namespace
224 {
225 	class impPolygonParagraphHandler
226 	{
227         const drawinglayer::attribute::SdrFormTextAttribute			maSdrFormTextAttribute;	// FormText parameters
228 		std::vector< drawinglayer::primitive2d::BasePrimitive2D* >&	mrDecomposition;		// destination primitive list
229 		std::vector< drawinglayer::primitive2d::BasePrimitive2D* >&	mrShadowDecomposition;	// destination primitive list for shadow
230 		Reference < com::sun::star::i18n::XBreakIterator >			mxBreak;				// break iterator
231 
232 		double getParagraphTextLength(const ::std::vector< const impPathTextPortion* >& rTextPortions)
233 		{
234 			drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
235 			double fRetval(0.0);
236 
237 			for(sal_uInt32 a(0L); a < rTextPortions.size(); a++)
238 			{
239 				const impPathTextPortion* pCandidate = rTextPortions[a];
240 
241 				if(pCandidate && pCandidate->getTextLength())
242 				{
243 					aTextLayouter.setFont(pCandidate->getFont());
244 					fRetval += pCandidate->getDisplayLength(0L, pCandidate->getTextLength());
245 				}
246 			}
247 
248 			return fRetval;
249 		}
250 
251 		xub_StrLen getNextGlyphLen(const impPathTextPortion* pCandidate, xub_StrLen nPosition, const ::com::sun::star::lang::Locale& rFontLocale)
252 		{
253 			xub_StrLen nNextGlyphLen(1);
254 
255 			if(mxBreak.is())
256 			{
257 				sal_Int32 nDone(0L);
258 				nNextGlyphLen = (xub_StrLen)mxBreak->nextCharacters(pCandidate->getText(), nPosition,
259 					rFontLocale, CharacterIteratorMode::SKIPCELL, 1, nDone) - nPosition;
260 			}
261 
262 			return nNextGlyphLen;
263 		}
264 
265 	public:
266 		impPolygonParagraphHandler(
267 			const drawinglayer::attribute::SdrFormTextAttribute& rSdrFormTextAttribute,
268 			std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rDecomposition,
269 			std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rShadowDecomposition)
270 		:	maSdrFormTextAttribute(rSdrFormTextAttribute),
271 			mrDecomposition(rDecomposition),
272 			mrShadowDecomposition(rShadowDecomposition)
273 		{
274 			// prepare BreakIterator
275 			Reference < XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
276 			Reference < XInterface > xInterface = xMSF->createInstance(::rtl::OUString::createFromAscii("com.sun.star.i18n.BreakIterator"));
277 
278 			if(xInterface.is())
279 			{
280 				Any x = xInterface->queryInterface(::getCppuType((const Reference< XBreakIterator >*)0));
281 				x >>= mxBreak;
282 			}
283 		}
284 
285 		void HandlePair(const basegfx::B2DPolygon rPolygonCandidate, const ::std::vector< const impPathTextPortion* >& rTextPortions)
286 		{
287 			// prepare polygon geometry, take into account as many parameters as possible
288 			basegfx::B2DPolygon aPolygonCandidate(rPolygonCandidate);
289 			const double fPolyLength(basegfx::tools::getLength(aPolygonCandidate));
290 			double fPolyEnd(fPolyLength);
291 			double fPolyStart(0.0);
292 			double fAutosizeScaleFactor(1.0);
293 			bool bAutosizeScale(false);
294 
295 			if(maSdrFormTextAttribute.getFormTextMirror())
296 			{
297 				aPolygonCandidate.flip();
298 			}
299 
300 			if(maSdrFormTextAttribute.getFormTextStart()
301                 && (XFT_LEFT == maSdrFormTextAttribute.getFormTextAdjust()
302                     || XFT_RIGHT == maSdrFormTextAttribute.getFormTextAdjust()))
303 			{
304 				if(XFT_LEFT == maSdrFormTextAttribute.getFormTextAdjust())
305 				{
306 					fPolyStart += maSdrFormTextAttribute.getFormTextStart();
307 
308 					if(fPolyStart > fPolyEnd)
309 					{
310 						fPolyStart = fPolyEnd;
311 					}
312 				}
313 				else
314 				{
315 					fPolyEnd -= maSdrFormTextAttribute.getFormTextStart();
316 
317 					if(fPolyEnd < fPolyStart)
318 					{
319 						fPolyEnd = fPolyStart;
320 					}
321 				}
322 			}
323 
324 			if(XFT_LEFT != maSdrFormTextAttribute.getFormTextAdjust())
325 			{
326 				// calculate total text length of this paragraph, some layout needs to be done
327 				const double fParagraphTextLength(getParagraphTextLength(rTextPortions));
328 
329 				// check if text is too long for paragraph. If yes, handle as if left aligned (default),
330 				// but still take care of XFT_AUTOSIZE in that case
331 				const bool bTextTooLong(fParagraphTextLength > (fPolyEnd - fPolyStart));
332 
333 				if(XFT_RIGHT == maSdrFormTextAttribute.getFormTextAdjust())
334 				{
335 					if(!bTextTooLong)
336 					{
337 						// if right aligned, add difference to polygon start
338 						fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength);
339 					}
340 				}
341 				else if(XFT_CENTER == maSdrFormTextAttribute.getFormTextAdjust())
342 				{
343 					if(!bTextTooLong)
344 					{
345 						// if centered, add half of difference to polygon start
346 						fPolyStart += ((fPolyEnd - fPolyStart) - fParagraphTextLength) / 2.0;
347 					}
348 				}
349 				else if(XFT_AUTOSIZE == maSdrFormTextAttribute.getFormTextAdjust())
350 				{
351 					// if scale, prepare scale factor between curve length and text length
352 					if(0.0 != fParagraphTextLength)
353 					{
354 						fAutosizeScaleFactor = (fPolyEnd - fPolyStart) / fParagraphTextLength;
355 						bAutosizeScale = true;
356 					}
357 				}
358 			}
359 
360 			// handle text portions for this paragraph
361 			for(sal_uInt32 a(0L); a < rTextPortions.size() && fPolyStart < fPolyEnd; a++)
362 			{
363 				const impPathTextPortion* pCandidate = rTextPortions[a];
364 				basegfx::B2DVector aFontScaling;
365 				const drawinglayer::attribute::FontAttribute aCandidateFontAttribute(
366                     drawinglayer::primitive2d::getFontAttributeFromVclFont(
367                         aFontScaling,
368                         pCandidate->getFont(),
369                         pCandidate->isRTL(),
370                         false));
371 
372 				if(pCandidate && pCandidate->getTextLength())
373 				{
374 					drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
375 					aTextLayouter.setFont(pCandidate->getFont());
376 					xub_StrLen nUsedTextLength(0);
377 
378 					while(nUsedTextLength < pCandidate->getTextLength() && fPolyStart < fPolyEnd)
379 					{
380 						xub_StrLen nNextGlyphLen(getNextGlyphLen(pCandidate, pCandidate->getTextStart() + nUsedTextLength, pCandidate->getLocale()));
381 
382 						// prepare portion length. Takes RTL sections into account.
383 						double fPortionLength(pCandidate->getDisplayLength(nUsedTextLength, nNextGlyphLen));
384 
385 						if(bAutosizeScale)
386 						{
387 							// when autosize scaling, expand portion length
388 							fPortionLength *= fAutosizeScaleFactor;
389 						}
390 
391 						// create transformation
392 						basegfx::B2DHomMatrix aNewTransformA, aNewTransformB, aNewShadowTransform;
393 						basegfx::B2DPoint aStartPos(basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart, fPolyLength));
394 						basegfx::B2DPoint aEndPos(aStartPos);
395 
396 						// add font scaling
397 						aNewTransformA.scale(aFontScaling.getX(), aFontScaling.getY());
398 
399 						// prepare scaling of text primitive
400 						if(bAutosizeScale)
401 						{
402 							// when autosize scaling, expand text primitive scaling to it
403 							aNewTransformA.scale(fAutosizeScaleFactor, fAutosizeScaleFactor);
404 						}
405 
406 						// eventually create shadow primitives from aDecomposition and add to rDecomposition
407 						const bool bShadow(XFTSHADOW_NONE != maSdrFormTextAttribute.getFormTextShadow());
408 
409 						if(bShadow)
410 						{
411 							if(XFTSHADOW_NORMAL == maSdrFormTextAttribute.getFormTextShadow())
412 							{
413 								aNewShadowTransform.translate(
414                                     maSdrFormTextAttribute.getFormTextShdwXVal(),
415                                     -maSdrFormTextAttribute.getFormTextShdwYVal());
416 							}
417 							else // XFTSHADOW_SLANT
418 							{
419 								double fScaleValue(maSdrFormTextAttribute.getFormTextShdwYVal() / 100.0);
420 								double fShearValue(-maSdrFormTextAttribute.getFormTextShdwXVal() * F_PI1800);
421 
422 								aNewShadowTransform.scale(1.0, fScaleValue);
423 								aNewShadowTransform.shearX(sin(fShearValue));
424 								aNewShadowTransform.scale(1.0, cos(fShearValue));
425 							}
426 						}
427 
428 						switch(maSdrFormTextAttribute.getFormTextStyle())
429 						{
430 							case XFT_ROTATE :
431 							{
432 								aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
433 								const basegfx::B2DVector aDirection(aEndPos - aStartPos);
434 								aNewTransformB.rotate(atan2(aDirection.getY(), aDirection.getX()));
435 								aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
436 
437 								break;
438 							}
439 							case XFT_UPRIGHT :
440 							{
441 								aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
442 
443 								break;
444 							}
445 							case XFT_SLANTX :
446 							{
447 								aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
448 								const basegfx::B2DVector aDirection(aEndPos - aStartPos);
449 								const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
450                                 const double fSin(sin(fShearValue));
451                                 const double fCos(cos(fShearValue));
452 
453    								aNewTransformB.shearX(-fSin);
454 
455 								// Scale may lead to objects without height since fCos == 0.0 is possible.
456 								// Renderers need to handle that, it's not a forbidden value and does not
457 								// need to be avoided
458                                 aNewTransformB.scale(1.0, fCos);
459                                 aNewTransformB.translate(aStartPos.getX() - (fPortionLength / 2.0), aStartPos.getY());
460 
461 								break;
462 							}
463 							case XFT_SLANTY :
464 							{
465 								aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
466 								const basegfx::B2DVector aDirection(aEndPos - aStartPos);
467 								const double fShearValue(atan2(aDirection.getY(), aDirection.getX()));
468                                 const double fCos(cos(fShearValue));
469                                 const double fTan(tan(fShearValue));
470 
471 								// shear to 'stand' on the curve
472 								aNewTransformB.shearY(fTan);
473 
474 								// scale in X to make as tight as needed. As with XFT_SLANT_X, this may
475 								// lead to primitives without width which the renderers will handle
476    								aNewTransformA.scale(fCos, 1.0);
477 
478 								aNewTransformB.translate(aStartPos.getX(), aStartPos.getY());
479 
480 								break;
481 							}
482 							default : break; // XFT_NONE
483 						}
484 
485 						// distance from path?
486 						if(maSdrFormTextAttribute.getFormTextDistance())
487 						{
488 							if(aEndPos.equal(aStartPos))
489 							{
490 								aEndPos = basegfx::tools::getPositionAbsolute(aPolygonCandidate, fPolyStart + fPortionLength, fPolyLength);
491 							}
492 
493 							// use back vector (aStartPos - aEndPos) here to get mirrored perpendicular as in old stuff
494 							const basegfx::B2DVector aPerpendicular(
495                                 basegfx::getNormalizedPerpendicular(aStartPos - aEndPos) *
496                                 maSdrFormTextAttribute.getFormTextDistance());
497 							aNewTransformB.translate(aPerpendicular.getX(), aPerpendicular.getY());
498 						}
499 
500 						if(pCandidate->getText().Len() && nNextGlyphLen)
501 						{
502 							const xub_StrLen nPortionIndex(pCandidate->getPortionIndex(nUsedTextLength, nNextGlyphLen));
503 							::std::vector< double > aNewDXArray;
504 
505 							if(nNextGlyphLen > 1 && pCandidate->getDoubleDXArray().size())
506 							{
507 								// copy DXArray for portion
508 								aNewDXArray.insert(
509 									aNewDXArray.begin(),
510 									pCandidate->getDoubleDXArray().begin() + nPortionIndex,
511 									pCandidate->getDoubleDXArray().begin() + (nPortionIndex + nNextGlyphLen));
512 
513 								if(nPortionIndex > 0)
514 								{
515 									// adapt to portion start
516 									double fDXOffset= *(pCandidate->getDoubleDXArray().begin() + (nPortionIndex - 1));
517 									::std::transform(
518 										aNewDXArray.begin(), aNewDXArray.end(),
519 										aNewDXArray.begin(), ::std::bind2nd(::std::minus<double>(), fDXOffset));
520 								}
521 
522 								if(bAutosizeScale)
523 								{
524 									// when autosize scaling, adapt to DXArray, too
525 									::std::transform(
526 										aNewDXArray.begin(), aNewDXArray.end(),
527 										aNewDXArray.begin(), ::std::bind2nd(::std::multiplies<double>(), fAutosizeScaleFactor));
528 								}
529 							}
530 
531                             if(bShadow)
532 						    {
533     						    // shadow primitive creation
534 							    const Color aShadowColor(maSdrFormTextAttribute.getFormTextShdwColor());
535 							    const basegfx::BColor aRGBShadowColor(aShadowColor.getBColor());
536 
537 							    drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pNew =
538                                     new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
539 								        aNewTransformB * aNewShadowTransform * aNewTransformA,
540 								        pCandidate->getText(),
541 								        nPortionIndex,
542 								        nNextGlyphLen,
543 								        aNewDXArray,
544 								        aCandidateFontAttribute,
545                                         pCandidate->getLocale(),
546 								        aRGBShadowColor);
547 
548 							    mrShadowDecomposition.push_back(pNew);
549 						    }
550 
551 						    {
552     						    // primitive creation
553 							    const Color aColor(pCandidate->getFont().GetColor());
554 							    const basegfx::BColor aRGBColor(aColor.getBColor());
555 
556 							    drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pNew =
557                                     new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
558 								        aNewTransformB * aNewTransformA,
559 								        pCandidate->getText(),
560 								        nPortionIndex,
561 								        nNextGlyphLen,
562 								        aNewDXArray,
563 								        aCandidateFontAttribute,
564                                         pCandidate->getLocale(),
565 								        aRGBColor);
566 
567 							    mrDecomposition.push_back(pNew);
568 						    }
569                         }
570 
571 						// consume from portion // no += here, xub_StrLen is sal_uInt16 and the compiler will gererate a warning here
572 						nUsedTextLength = nUsedTextLength + nNextGlyphLen;
573 
574 						// consume from polygon
575 						fPolyStart += fPortionLength;
576 					}
577 				}
578 			}
579 		}
580 	};
581 } // end of anonymous namespace
582 
583 //////////////////////////////////////////////////////////////////////////////
584 // primitive decomposition helpers
585 
586 namespace
587 {
588 	void impAddPolygonStrokePrimitives(
589 		const basegfx::B2DPolyPolygonVector& rB2DPolyPolyVector,
590 		const basegfx::B2DHomMatrix& rTransform,
591 		const drawinglayer::attribute::LineAttribute& rLineAttribute,
592 		const drawinglayer::attribute::StrokeAttribute& rStrokeAttribute,
593 		std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rTarget)
594 	{
595 		for(basegfx::B2DPolyPolygonVector::const_iterator aPolygon(rB2DPolyPolyVector.begin()); aPolygon != rB2DPolyPolyVector.end(); aPolygon++)
596 		{
597 			// prepare PolyPolygons
598 			basegfx::B2DPolyPolygon aB2DPolyPolygon = *aPolygon;
599 			aB2DPolyPolygon.transform(rTransform);
600 
601 			for(sal_uInt32 a(0L); a < aB2DPolyPolygon.count(); a++)
602 			{
603 				// create one primitive per polygon
604 				drawinglayer::primitive2d::PolygonStrokePrimitive2D* pNew =
605                     new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
606                         aB2DPolyPolygon.getB2DPolygon(a), rLineAttribute, rStrokeAttribute);
607 				rTarget.push_back(pNew);
608 			}
609 		}
610 	}
611 
612 	drawinglayer::primitive2d::Primitive2DSequence impAddPathTextOutlines(
613 		const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rSource,
614         const drawinglayer::attribute::SdrFormTextOutlineAttribute& rOutlineAttribute)
615 	{
616 		std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aNewPrimitives;
617 
618 		for(sal_uInt32 a(0L); a < rSource.size(); a++)
619 		{
620 			const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* pTextCandidate = dynamic_cast< const drawinglayer::primitive2d::TextSimplePortionPrimitive2D* >(rSource[a]);
621 
622 			if(pTextCandidate)
623 			{
624 				basegfx::B2DPolyPolygonVector aB2DPolyPolyVector;
625 			    basegfx::B2DHomMatrix aPolygonTransform;
626 
627                 // get text outlines and their object transformation
628                 pTextCandidate->getTextOutlinesAndTransformation(aB2DPolyPolyVector, aPolygonTransform);
629 
630 				if(!aB2DPolyPolyVector.empty())
631                 {
632 				    // create stroke primitives
633 				    std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aStrokePrimitives;
634 				    impAddPolygonStrokePrimitives(
635                         aB2DPolyPolyVector,
636                         aPolygonTransform,
637                         rOutlineAttribute.getLineAttribute(),
638                         rOutlineAttribute.getStrokeAttribute(),
639                         aStrokePrimitives);
640 				    const sal_uInt32 nStrokeCount(aStrokePrimitives.size());
641 
642 				    if(nStrokeCount)
643 				    {
644 					    if(rOutlineAttribute.getTransparence())
645 					    {
646 						    // create UnifiedTransparencePrimitive2D
647 						    drawinglayer::primitive2d::Primitive2DSequence aStrokePrimitiveSequence(nStrokeCount);
648 
649 						    for(sal_uInt32 b(0L); b < nStrokeCount; b++)
650 						    {
651 							    aStrokePrimitiveSequence[b] = drawinglayer::primitive2d::Primitive2DReference(aStrokePrimitives[b]);
652 						    }
653 
654 						    drawinglayer::primitive2d::UnifiedTransparencePrimitive2D* pNew2 =
655                                 new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
656                                     aStrokePrimitiveSequence,
657                                     (double)rOutlineAttribute.getTransparence() / 100.0);
658 						    aNewPrimitives.push_back(pNew2);
659 					    }
660 					    else
661 					    {
662 						    // add polygons to rDecomposition as polygonStrokePrimitives
663 						    aNewPrimitives.insert(aNewPrimitives.end(), aStrokePrimitives.begin(), aStrokePrimitives.end());
664 					    }
665                     }
666 				}
667 			}
668 		}
669 
670 		const sal_uInt32 nNewCount(aNewPrimitives.size());
671 
672 		if(nNewCount)
673 		{
674 			drawinglayer::primitive2d::Primitive2DSequence aRetval(nNewCount);
675 
676 			for(sal_uInt32 a(0L); a < nNewCount; a++)
677 			{
678 				aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(aNewPrimitives[a]);
679 			}
680 
681 			return aRetval;
682 		}
683 		else
684 		{
685 			return drawinglayer::primitive2d::Primitive2DSequence();
686 		}
687 	}
688 } // end of anonymous namespace
689 
690 //////////////////////////////////////////////////////////////////////////////
691 // primitive decomposition
692 
693 void SdrTextObj::impDecomposePathTextPrimitive(
694 	drawinglayer::primitive2d::Primitive2DSequence& rTarget,
695 	const drawinglayer::primitive2d::SdrPathTextPrimitive2D& rSdrPathTextPrimitive,
696 	const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
697 {
698 	drawinglayer::primitive2d::Primitive2DSequence aRetvalA;
699 	drawinglayer::primitive2d::Primitive2DSequence aRetvalB;
700 
701 	// prepare outliner
702 	SdrOutliner& rOutliner = ImpGetDrawOutliner();
703 	rOutliner.SetUpdateMode(true);
704 	rOutliner.Clear();
705 	rOutliner.SetPaperSize(Size(LONG_MAX,LONG_MAX));
706 	rOutliner.SetText(rSdrPathTextPrimitive.getOutlinerParaObject());
707 
708 	// set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
709 	rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
710 
711 	// now break up to text portions
712 	impTextBreakupHandler aConverter(rOutliner);
713 	const ::std::vector< impPathTextPortion > rPathTextPortions = aConverter.decompositionPathTextPrimitive();
714 
715 	if(!rPathTextPortions.empty())
716 	{
717 		// get FormText and polygon values
718         const drawinglayer::attribute::SdrFormTextAttribute& rFormTextAttribute = rSdrPathTextPrimitive.getSdrFormTextAttribute();
719 		const basegfx::B2DPolyPolygon& rPathPolyPolygon(rSdrPathTextPrimitive.getPathPolyPolygon());
720 
721 		// get loop count
722 		sal_uInt32 nLoopCount(rPathPolyPolygon.count());
723 
724 		if(rOutliner.GetParagraphCount() < nLoopCount)
725 		{
726 			nLoopCount = rOutliner.GetParagraphCount();
727 		}
728 
729 		if(nLoopCount)
730 		{
731 			// prepare common decomposition stuff
732 			std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aRegularDecomposition;
733 			std::vector< drawinglayer::primitive2d::BasePrimitive2D* > aShadowDecomposition;
734 			impPolygonParagraphHandler aPolygonParagraphHandler(
735                 rFormTextAttribute,
736 				aRegularDecomposition,
737 				aShadowDecomposition);
738 			sal_uInt32 a;
739 
740 			for(a = 0L; a < nLoopCount; a++)
741 			{
742 				// filter text portions for this paragraph
743 				::std::vector< const impPathTextPortion* > aParagraphTextPortions;
744 
745 				for(sal_uInt32 b(0L); b < rPathTextPortions.size(); b++)
746 				{
747 					const impPathTextPortion& rCandidate = rPathTextPortions[b];
748 
749 					if(rCandidate.getParagraph() == a)
750 					{
751 						aParagraphTextPortions.push_back(&rCandidate);
752 					}
753 				}
754 
755 				// handle data pair polygon/ParagraphTextPortions
756 				if(!aParagraphTextPortions.empty())
757 				{
758 					aPolygonParagraphHandler.HandlePair(rPathPolyPolygon.getB2DPolygon(a), aParagraphTextPortions);
759 				}
760 			}
761 
762 			const sal_uInt32 nShadowCount(aShadowDecomposition.size());
763 			const sal_uInt32 nRegularCount(aRegularDecomposition.size());
764 
765 			if(nShadowCount)
766 			{
767 				// add shadow primitives to decomposition
768 				aRetvalA.realloc(nShadowCount);
769 
770 				for(a = 0L; a < nShadowCount; a++)
771 				{
772 					aRetvalA[a] = drawinglayer::primitive2d::Primitive2DReference(aShadowDecomposition[a]);
773 				}
774 
775 				// evtl. add shadow outlines
776 				if(rFormTextAttribute.getFormTextOutline()
777 					&& !rFormTextAttribute.getShadowOutline().isDefault())
778 				{
779 					const drawinglayer::primitive2d::Primitive2DSequence aOutlines(
780                         impAddPathTextOutlines(
781 							aShadowDecomposition,
782 							rFormTextAttribute.getShadowOutline()));
783 
784 					drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(aRetvalA, aOutlines);
785 				}
786 			}
787 
788 			if(nRegularCount)
789 			{
790 				// add normal primitives to decomposition
791 				aRetvalB.realloc(nRegularCount);
792 
793 				for(a = 0L; a < nRegularCount; a++)
794 				{
795 					aRetvalB[a] = drawinglayer::primitive2d::Primitive2DReference(aRegularDecomposition[a]);
796 				}
797 
798 				// evtl. add outlines
799 				if(rFormTextAttribute.getFormTextOutline()
800 					&& !rFormTextAttribute.getOutline().isDefault())
801 				{
802 					const drawinglayer::primitive2d::Primitive2DSequence aOutlines(
803                         impAddPathTextOutlines(
804 							aRegularDecomposition,
805 							rFormTextAttribute.getOutline()));
806 
807 					drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(aRetvalB, aOutlines);
808 				}
809 			}
810 		}
811 	}
812 
813 	// cleanup outliner
814 	rOutliner.SetDrawPortionHdl(Link());
815 	rOutliner.Clear();
816 	rOutliner.setVisualizedPage(0);
817 
818 	// concatenate all results
819 	drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aRetvalA);
820 	drawinglayer::primitive2d::appendPrimitive2DSequenceToPrimitive2DSequence(rTarget, aRetvalB);
821 }
822 
823 //////////////////////////////////////////////////////////////////////////////
824 // eof
825