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 <drawinglayer/primitive2d/textprimitive2d.hxx>
36 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
37 #include <basegfx/range/b2drange.hxx>
38 #include <editeng/editstat.hxx>
39 #include <vcl/salbtype.hxx>
40 #include <svx/sdtfchim.hxx>
41 #include <svl/itemset.hxx>
42 #include <basegfx/polygon/b2dpolygontools.hxx>
43 #include <basegfx/polygon/b2dpolygon.hxx>
44 #include <drawinglayer/animation/animationtiming.hxx>
45 #include <basegfx/color/bcolor.hxx>
46 #include <vcl/svapp.hxx>
47 #include <editeng/eeitemid.hxx>
48 #include <editeng/escpitem.hxx>
49 #include <editeng/svxenum.hxx>
50 #include <editeng/flditem.hxx>
51 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
52 #include <vcl/metaact.hxx>
53 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
54 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
55 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
56 #include <svx/unoapi.hxx>
57 #include <drawinglayer/geometry/viewinformation2d.hxx>
58 #include <editeng/outlobj.hxx>
59 #include <basegfx/matrix/b2dhommatrixtools.hxx>
60 
61 //////////////////////////////////////////////////////////////////////////////
62 // helpers
63 
64 namespace
65 {
66     drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector)
67     {
68 		const sal_Int32 nCount(rPrimitiveVector.size());
69 		drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount);
70 
71 	    for(sal_Int32 a(0L); a < nCount; a++)
72 	    {
73 		    aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]);
74 	    }
75 
76         return aRetval;
77     }
78 
79     class impTextBreakupHandler
80 	{
81     private:
82 		std::vector< drawinglayer::primitive2d::BasePrimitive2D* >	maTextPortionPrimitives;
83 		std::vector< drawinglayer::primitive2d::BasePrimitive2D* >	maLinePrimitives;
84 		std::vector< drawinglayer::primitive2d::BasePrimitive2D* >	maParagraphPrimitives;
85 
86 		SdrOutliner&												mrOutliner;
87 		basegfx::B2DHomMatrix										maNewTransformA;
88 		basegfx::B2DHomMatrix										maNewTransformB;
89 
90         // the visible area for contour text decomposition
91         basegfx::B2DVector                                          maScale;
92 
93 		// #SJ# ClipRange for BlockText decomposition; only text portions completely
94 		// inside are to be accepted, so this is different from geometric clipping
95 		// (which would allow e.g. upper parts of portions to remain). Only used for
96 		// BlockText (see there)
97 		basegfx::B2DRange                                           maClipRange;
98 
99 		DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* );
100 		DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* );
101 		DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* );
102 
103 		DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* );
104 		DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* );
105 		DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* );
106 
107 		bool impIsUnderlineAbove(const Font& rFont) const;
108 		void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
109     	drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const;
110         void impFlushTextPortionPrimitivesToLinePrimitives();
111         void impFlushLinePrimitivesToParagraphPrimitives();
112 		void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
113 		void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
114 
115 	public:
116 		impTextBreakupHandler(SdrOutliner& rOutliner)
117 		:	maTextPortionPrimitives(),
118 			maLinePrimitives(),
119 			maParagraphPrimitives(),
120 			mrOutliner(rOutliner),
121 			maNewTransformA(),
122 			maNewTransformB(),
123 			maScale(),
124 			maClipRange()
125 		{
126 		}
127 
128 		void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
129 		{
130             maScale = rScale;
131 			maNewTransformA = rNewTransformA;
132 			maNewTransformB = rNewTransformB;
133 			mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
134 			mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
135 			mrOutliner.StripPortions();
136 			mrOutliner.SetDrawPortionHdl(Link());
137 			mrOutliner.SetDrawBulletHdl(Link());
138 		}
139 
140 		void decomposeBlockTextPrimitive(
141 			const basegfx::B2DHomMatrix& rNewTransformA,
142 			const basegfx::B2DHomMatrix& rNewTransformB,
143 			const basegfx::B2DRange& rClipRange)
144 		{
145 			maNewTransformA = rNewTransformA;
146 			maNewTransformB = rNewTransformB;
147 			maClipRange = rClipRange;
148 			mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
149 			mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
150 			mrOutliner.StripPortions();
151 			mrOutliner.SetDrawPortionHdl(Link());
152 			mrOutliner.SetDrawBulletHdl(Link());
153 		}
154 
155 		void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
156 		{
157 			maNewTransformA = rNewTransformA;
158 			maNewTransformB = rNewTransformB;
159 			mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
160 			mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
161 			mrOutliner.StripPortions();
162 			mrOutliner.SetDrawPortionHdl(Link());
163 			mrOutliner.SetDrawBulletHdl(Link());
164 		}
165 
166 		drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence();
167 	};
168 
169 	bool impTextBreakupHandler::impIsUnderlineAbove(const Font& rFont) const
170 	{
171 		if(!rFont.IsVertical())
172 		{
173 			return false;
174 		}
175 
176 		if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
177 		{
178 			// the underline is right for Japanese only
179 			return true;
180 		}
181 
182 		return false;
183 	}
184 
185 	void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
186 	{
187 		if(rInfo.mrText.Len() && rInfo.mnTextLen)
188 		{
189 			basegfx::B2DVector aFontScaling;
190 			drawinglayer::attribute::FontAttribute aFontAttribute(
191                 drawinglayer::primitive2d::getFontAttributeFromVclFont(
192                     aFontScaling,
193                     rInfo.mrFont,
194                     rInfo.IsRTL(),
195                     false));
196 			basegfx::B2DHomMatrix aNewTransform;
197 
198             // add font scale to new transform
199 			aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
200 
201 			// look for proportional font scaling, evtl scale accordingly
202             if(100 != rInfo.mrFont.GetPropr())
203 			{
204 				const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
205 				aNewTransform.scale(fFactor, fFactor);
206 			}
207 
208 			// apply font rotate
209 			if(rInfo.mrFont.GetOrientation())
210 			{
211 				aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800);
212 			}
213 
214 			// look for escapement, evtl translate accordingly
215 			if(rInfo.mrFont.GetEscapement())
216 			{
217 				sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
218 
219 				if(DFLT_ESC_AUTO_SUPER == nEsc)
220 				{
221 					nEsc = 33;
222 				}
223 				else if(DFLT_ESC_AUTO_SUB == nEsc)
224 				{
225 					nEsc = -20;
226 				}
227 
228 				if(nEsc > 100)
229 				{
230 					nEsc = 100;
231 				}
232 				else if(nEsc < -100)
233 				{
234 					nEsc = -100;
235 				}
236 
237 				const double fEscapement(nEsc / -100.0);
238 				aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
239 			}
240 
241 			// apply transformA
242 			aNewTransform *= maNewTransformA;
243 
244 			// apply local offset
245 			aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
246 
247 			// also apply embedding object's transform
248 			aNewTransform *= maNewTransformB;
249 
250 			// prepare DXArray content. To make it independent from font size (and such from
251 			// the text transformation), scale it to unit coordinates
252 			::std::vector< double > aDXArray;
253 			static bool bDisableTextArray(false);
254 
255 			if(!bDisableTextArray && rInfo.mpDXArray && rInfo.mnTextLen)
256 			{
257 				aDXArray.reserve(rInfo.mnTextLen);
258 
259                 for(xub_StrLen a(0); a < rInfo.mnTextLen; a++)
260 			    {
261 				    aDXArray.push_back((double)rInfo.mpDXArray[a]);
262 			    }
263 			}
264 
265 			// create complex text primitive and append
266 			const Color aFontColor(rInfo.mrFont.GetColor());
267             const basegfx::BColor aBFontColor(aFontColor.getBColor());
268 
269 			// prepare wordLineMode (for underline and strikeout)
270 			// NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
271 			// to be splitted which would not look like the original
272 			const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
273 
274 			// prepare new primitive
275 			drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = 0;
276 			const bool bDecoratedIsNeeded(
277 				   UNDERLINE_NONE != rInfo.mrFont.GetOverline()
278 				|| UNDERLINE_NONE != rInfo.mrFont.GetUnderline()
279 				|| STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
280 				|| EMPHASISMARK_NONE != (rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
281 				|| RELIEF_NONE != rInfo.mrFont.GetRelief()
282 				|| rInfo.mrFont.IsShadow()
283                 || bWordLineMode);
284 
285 			if(bDecoratedIsNeeded)
286 			{
287 				// TextDecoratedPortionPrimitive2D needed, prepare some more data
288 				// get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
289                 const Color aUnderlineColor(rInfo.maTextLineColor);
290                 const basegfx::BColor aBUnderlineColor((0xffffffff == aUnderlineColor.GetColor()) ? aBFontColor : aUnderlineColor.getBColor());
291                 const Color aOverlineColor(rInfo.maOverlineColor);
292                 const basegfx::BColor aBOverlineColor((0xffffffff == aOverlineColor.GetColor()) ? aBFontColor : aOverlineColor.getBColor());
293 
294                 // prepare overline and underline data
295                 const drawinglayer::primitive2d::TextLine eFontOverline(
296 					drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetOverline()));
297                 const drawinglayer::primitive2d::TextLine eFontUnderline(
298 					drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetUnderline()));
299 
300                 // check UndelineAbove
301 				const bool bUnderlineAbove(
302 					drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont));
303 
304 				// prepare strikeout data
305 				const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
306 					drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
307 
308 				// prepare emphasis mark data
309 				drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
310 
311 				switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
312 				{
313 					case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
314 					case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
315 					case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
316 					case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
317 				}
318 
319 				const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
320 				const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
321 
322 				// prepare font relief data
323 				drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
324 
325 				switch(rInfo.mrFont.GetRelief())
326 				{
327 					case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
328 					case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
329 					default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
330 				}
331 
332 				// prepare shadow/outline data
333 				const bool bShadow(rInfo.mrFont.IsShadow());
334 
335 				// TextDecoratedPortionPrimitive2D is needed, create one
336                 pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
337 
338 					// attributes for TextSimplePortionPrimitive2D
339 					aNewTransform,
340 					rInfo.mrText,
341 					rInfo.mnTextStart,
342 					rInfo.mnTextLen,
343 					aDXArray,
344 					aFontAttribute,
345 					rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
346 					aBFontColor,
347 
348 					// attributes for TextDecoratedPortionPrimitive2D
349                     aBOverlineColor,
350                     aBUnderlineColor,
351                     eFontOverline,
352                     eFontUnderline,
353 					bUnderlineAbove,
354 					eTextStrikeout,
355 					bWordLineMode,
356 					eTextEmphasisMark,
357 					bEmphasisMarkAbove,
358 					bEmphasisMarkBelow,
359 					eTextRelief,
360 					bShadow);
361 			}
362 			else
363 			{
364 				// TextSimplePortionPrimitive2D is enough
365 				pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
366 					aNewTransform,
367 					rInfo.mrText,
368 					rInfo.mnTextStart,
369 					rInfo.mnTextLen,
370 					aDXArray,
371 					aFontAttribute,
372 					rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
373 					aBFontColor);
374 			}
375 
376             if(rInfo.mbEndOfBullet)
377             {
378                 // embed in TextHierarchyBulletPrimitive2D
379 	            const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
380 	            const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
381 			    pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
382             }
383 
384             if(rInfo.mpFieldData)
385             {
386                 pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo);
387             }
388 
389             maTextPortionPrimitives.push_back(pNewPrimitive);
390 
391             // support for WrongSpellVector. Create WrongSpellPrimitives as needed
392             if(rInfo.mpWrongSpellVector && !aDXArray.empty())
393             {
394                 const sal_uInt32 nSize(rInfo.mpWrongSpellVector->size());
395                 const sal_uInt32 nDXCount(aDXArray.size());
396                 const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
397 
398                 for(sal_uInt32 a(0); a < nSize; a++)
399                 {
400                     const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
401 
402                     if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
403                     {
404                         const sal_uInt32 nStart(rCandidate.nStart - rInfo.mnTextStart);
405                         const sal_uInt32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
406                         double fStart(0.0);
407                         double fEnd(0.0);
408 
409                         if(nStart > 0 && nStart - 1 < nDXCount)
410                         {
411                             fStart = aDXArray[nStart - 1];
412                         }
413 
414                         if(nEnd > 0 && nEnd - 1 < nDXCount)
415                         {
416                             fEnd = aDXArray[nEnd - 1];
417                         }
418 
419                         if(!basegfx::fTools::equal(fStart, fEnd))
420                         {
421                             if(rInfo.IsRTL())
422                             {
423                                 // #i98523#
424                                 // When the portion is RTL, mirror the redlining using the
425                                 // full portion width
426                                 const double fTextWidth(aDXArray[aDXArray.size() - 1]);
427 
428                                 fStart = fTextWidth - fStart;
429                                 fEnd = fTextWidth - fEnd;
430                             }
431 
432                             // need to take FontScaling out of values; it's already part of
433                             // aNewTransform and would be double applied
434                             const double fFontScaleX(aFontScaling.getX());
435 
436                             if(!basegfx::fTools::equal(fFontScaleX, 1.0)
437                                 && !basegfx::fTools::equalZero(fFontScaleX))
438                             {
439                                 fStart /= fFontScaleX;
440                                 fEnd /= fFontScaleX;
441                             }
442 
443                             maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
444                                 aNewTransform,
445                                 fStart,
446                                 fEnd,
447                                 aSpellColor));
448                         }
449                     }
450                 }
451             }
452 		}
453 	}
454 
455 	drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const
456     {
457         if(rInfo.mpFieldData)
458         {
459             // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
460 			// which holds the field type and evtl. the URL
461             const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
462             const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
463 
464 			// embed current primitive to a sequence
465             drawinglayer::primitive2d::Primitive2DSequence aSequence;
466 
467 			if(pPrimitive)
468 			{
469 				aSequence.realloc(1);
470 				aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
471 			}
472 
473 			if(pURLField)
474 			{
475 				pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
476 			}
477 			else if(pPageField)
478 			{
479 				pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, String());
480 			}
481 			else
482 			{
483 				pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, String());
484 			}
485         }
486 
487         return pPrimitive;
488     }
489 
490     void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
491     {
492 		// only create a line primitive when we had content; there is no need for
493 		// empty line primitives (contrary to paragraphs, see below).
494 		if(!maTextPortionPrimitives.empty())
495 		{
496 	        drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives));
497 		    maTextPortionPrimitives.clear();
498 			maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence));
499 		}
500     }
501 
502     void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives()
503     {
504 		// ALWAYS create a paragraph primitive, even when no content was added. This is done to
505 		// have the correct paragraph count even with empty paragraphs. Those paragraphs will
506 		// have an empty sub-PrimitiveSequence.
507         drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives));
508         maLinePrimitives.clear();
509         maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence));
510     }
511 
512     void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
513     {
514         impCreateTextPortionPrimitive(rInfo);
515 
516         if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
517         {
518             impFlushTextPortionPrimitivesToLinePrimitives();
519         }
520 
521         if(rInfo.mbEndOfParagraph)
522         {
523             impFlushLinePrimitivesToParagraphPrimitives();
524         }
525     }
526 
527     void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
528     {
529 		basegfx::B2DHomMatrix aNewTransform;
530 
531 		// add size to new transform
532 		aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
533 
534 		// apply transformA
535 		aNewTransform *= maNewTransformA;
536 
537 		// apply local offset
538 		aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
539 
540 		// also apply embedding object's transform
541 		aNewTransform *= maNewTransformB;
542 
543         // prepare empty GraphicAttr
544         const GraphicAttr aGraphicAttr;
545 
546         // create GraphicPrimitive2D
547         const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
548 			aNewTransform,
549             rInfo.maBulletGraphicObject,
550             aGraphicAttr));
551 
552         // embed in TextHierarchyBulletPrimitive2D
553         const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
554 	    drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
555 
556         // add to output
557         maTextPortionPrimitives.push_back(pNewPrimitive);
558     }
559 
560 	IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo)
561 	{
562         // for contour text, ignore (clip away) all portions which are below
563         // the visible area given by maScale
564 		if(pInfo && (double)pInfo->mrStartPos.Y() < maScale.getY())
565 		{
566             impHandleDrawPortionInfo(*pInfo);
567 		}
568 
569 		return 0;
570 	}
571 
572 	IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo)
573 	{
574 		if(pInfo)
575 		{
576 			// #SJ# Is clipping wanted? This is text clipping; only accept a portion
577 			// if it's completely in the range
578 			if(!maClipRange.isEmpty())
579 			{
580 				// Test start position first; this allows to not get the text range at
581 				// all if text is far outside
582 				const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
583 
584 				if(!maClipRange.isInside(aStartPosition))
585 				{
586 					return 0;
587 				}
588 
589 				// Start position is inside. Get TextBoundRect and TopLeft next
590 				drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
591 				aTextLayouterDevice.setFont(pInfo->mrFont);
592 
593 				const basegfx::B2DRange aTextBoundRect(
594 					aTextLayouterDevice.getTextBoundRect(
595 						pInfo->mrText, pInfo->mnTextStart, pInfo->mnTextLen));
596 				const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
597 
598 				if(!maClipRange.isInside(aTopLeft))
599 				{
600 					return 0;
601 				}
602 
603 				// TopLeft is inside. Get BottomRight and check
604 				const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
605 
606 				if(!maClipRange.isInside(aBottomRight))
607 				{
608 					return 0;
609 				}
610 
611 				// all inside, clip was successful
612 			}
613             impHandleDrawPortionInfo(*pInfo);
614 		}
615 
616 		return 0;
617 	}
618 
619 	IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo)
620 	{
621 		if(pInfo)
622 		{
623             impHandleDrawPortionInfo(*pInfo);
624 		}
625 
626 		return 0;
627 	}
628 
629 	IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo)
630 	{
631 		if(pInfo)
632 		{
633             impHandleDrawBulletInfo(*pInfo);
634 		}
635 
636 		return 0;
637 	}
638 
639 	IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo)
640 	{
641 		if(pInfo)
642 		{
643             impHandleDrawBulletInfo(*pInfo);
644 		}
645 
646 		return 0;
647 	}
648 
649 	IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo)
650 	{
651 		if(pInfo)
652 		{
653             impHandleDrawBulletInfo(*pInfo);
654 		}
655 
656 		return 0;
657 	}
658 
659     drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence()
660 	{
661         if(!maTextPortionPrimitives.empty())
662         {
663             // collect non-closed lines
664             impFlushTextPortionPrimitivesToLinePrimitives();
665         }
666 
667         if(!maLinePrimitives.empty())
668         {
669             // collect non-closed paragraphs
670             impFlushLinePrimitivesToParagraphPrimitives();
671         }
672 
673         return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives);
674 	}
675 } // end of anonymous namespace
676 
677 //////////////////////////////////////////////////////////////////////////////
678 // primitive decompositions
679 
680 void SdrTextObj::impDecomposeContourTextPrimitive(
681 	drawinglayer::primitive2d::Primitive2DSequence& rTarget,
682 	const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
683 	const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
684 {
685     // decompose matrix to have position and size of text
686 	basegfx::B2DVector aScale, aTranslate;
687 	double fRotate, fShearX;
688 	rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
689 
690 	// prepare contour polygon, force to non-mirrored for layouting
691 	basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
692 	aPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
693 
694 	// prepare outliner
695 	SdrOutliner& rOutliner = ImpGetDrawOutliner();
696 	const Size aNullSize;
697 	rOutliner.SetPaperSize(aNullSize);
698 	rOutliner.SetPolygon(aPolyPolygon);
699 	rOutliner.SetUpdateMode(true);
700 	rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
701 
702 	// set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
703 	rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
704 
705 	// prepare matrices to apply to newly created primitives
706 	basegfx::B2DHomMatrix aNewTransformA;
707 
708 	// mirroring. We are now in the polygon sizes. When mirroring in X and Y,
709 	// move the null point which was top left to bottom right.
710 	const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
711 	const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
712 
713 	// in-between the translations of the single primitives will take place. Afterwards,
714 	// the object's transformations need to be applied
715 	const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
716 		bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
717 		fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
718 
719 	// now break up text primitives.
720 	impTextBreakupHandler aConverter(rOutliner);
721 	aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
722 
723 	// cleanup outliner
724 	rOutliner.Clear();
725 	rOutliner.setVisualizedPage(0);
726 
727 	rTarget = aConverter.getPrimitive2DSequence();
728 }
729 
730 void SdrTextObj::impDecomposeBlockTextPrimitive(
731 	drawinglayer::primitive2d::Primitive2DSequence& rTarget,
732 	const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
733 	const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
734 {
735     // decompose matrix to have position and size of text
736 	basegfx::B2DVector aScale, aTranslate;
737 	double fRotate, fShearX;
738 	rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
739 
740 	// use B2DRange aAnchorTextRange for calculations
741 	basegfx::B2DRange aAnchorTextRange(aTranslate);
742 	aAnchorTextRange.expand(aTranslate + aScale);
743 
744 	// prepare outliner
745 	const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
746 	SdrOutliner& rOutliner = ImpGetDrawOutliner();
747 	SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
748 	SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
749 	const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
750 	const Size aNullSize;
751 
752 	// set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
753 	rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
754 	rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
755 	rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE);
756 	rOutliner.SetMinAutoPaperSize(aNullSize);
757 	rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
758 
759 	// add one to rage sizes to get back to the old Rectangle and outliner measurements
760 	const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
761 	const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
762 	const bool bVerticalWritintg(rSdrBlockTextPrimitive.getOutlinerParaObject().IsVertical());
763 	const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
764 
765 	if(bIsCell)
766 	{
767 		// cell text is formated neither like a text object nor like a object
768 		// text, so use a special setup here
769 		rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
770 
771         // #i106214# To work with an unchangeable PaperSize (CellSize in
772         // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
773         // #i106214# This was not completely correct; to still measure the real
774         // text height to allow vertical adjust (and vice versa for VerticalWritintg)
775         // only one aspect has to be set, but the other one to zero
776         if(bVerticalWritintg)
777         {
778             // measure the horizontal text size
779     		rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
780         }
781         else
782         {
783             // measure the vertical text size
784     		rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
785         }
786 
787         rOutliner.SetPaperSize(aAnchorTextSize);
788 		rOutliner.SetUpdateMode(true);
789 		rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
790 	}
791 	else
792 	{
793 	    // check if block text is used (only one of them can be true)
794 	    const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg);
795 	    const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg);
796 
797         // set minimal paper size hor/ver if needed
798 	    if(bHorizontalIsBlock)
799 	    {
800 		    rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
801 	    }
802 	    else if(bVerticalIsBlock)
803 	    {
804 		    rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
805 	    }
806 
807         if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
808 		{
809             // #i103454# maximal paper size hor/ver needs to be limited to text
810             // frame size. If it's block text, still allow the 'other' direction
811             // to grow to get a correct real text size when using GetPaperSize().
812             // When just using aAnchorTextSize as maximum, GetPaperSize()
813             // would just return aAnchorTextSize again: this means, the wanted
814             // 'measurement' of the real size of block text would not work
815         	Size aMaxAutoPaperSize(aAnchorTextSize);
816 
817 		    if(bHorizontalIsBlock)
818 		    {
819                 // allow to grow vertical for horizontal blocks
820                 aMaxAutoPaperSize.setHeight(1000000);
821 		    }
822 		    else if(bVerticalIsBlock)
823 		    {
824                 // allow to grow horizontal for vertical blocks
825                 aMaxAutoPaperSize.setWidth(1000000);
826 		    }
827 
828             rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
829 		}
830 
831 		rOutliner.SetPaperSize(aNullSize);
832 		rOutliner.SetUpdateMode(true);
833 		rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
834 	}
835 
836     rOutliner.SetControlWord(nOriginalControlWord);
837 
838 	// now get back the layouted text size from outliner
839 	const Size aOutlinerTextSiz(rOutliner.GetPaperSize());
840 	const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height());
841 	basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
842 
843 	// For draw objects containing text correct hor/ver alignment if text is bigger
844 	// than the object itself. Without that correction, the text would always be
845 	// formatted to the left edge (or top edge when vertical) of the draw object.
846 	if(!IsTextFrame() && !bIsCell)
847 	{
848 		if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg)
849 		{
850 			// Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
851 			// else the alignment is wanted.
852 			if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
853 			{
854 				eHAdj = SDRTEXTHORZADJUST_CENTER;
855 			}
856 		}
857 
858 		if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg)
859 		{
860 			// Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
861 			// else the alignment is wanted.
862 			if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
863 			{
864 				eVAdj = SDRTEXTVERTADJUST_CENTER;
865 			}
866 		}
867 	}
868 
869 	// correct horizontal translation using the now known text size
870 	if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
871 	{
872 		const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
873 
874 		if(SDRTEXTHORZADJUST_CENTER == eHAdj)
875 		{
876 			aAdjustTranslate.setX(fFree / 2.0);
877 		}
878 
879 		if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
880 		{
881 			aAdjustTranslate.setX(fFree);
882 		}
883 	}
884 
885 	// correct vertical translation using the now known text size
886 	if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
887 	{
888 		const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
889 
890 		if(SDRTEXTVERTADJUST_CENTER == eVAdj)
891 		{
892 			aAdjustTranslate.setY(fFree / 2.0);
893 		}
894 
895 		if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
896 		{
897 			aAdjustTranslate.setY(fFree);
898 		}
899 	}
900 
901 	// prepare matrices to apply to newly created primitives. aNewTransformA
902 	// will get coordinates in aOutlinerScale size and positive in X, Y.
903 	// Translate relative to given primitive to get same rotation and shear
904 	// as the master shape we are working on. For vertical, use the top-right
905 	// corner
906 	const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
907 	const basegfx::B2DTuple aAdjOffset(fStartInX, aAdjustTranslate.getY());
908     basegfx::B2DHomMatrix aNewTransformA(basegfx::tools::createTranslateB2DHomMatrix(aAdjOffset.getX(), aAdjOffset.getY()));
909 
910 	// mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
911 	// move the null point which was top left to bottom right.
912 	const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
913 	const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
914 
915 	// in-between the translations of the single primitives will take place. Afterwards,
916 	// the object's transformations need to be applied
917 	const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
918 		bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
919 		fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
920 
921 	// #SJ# create ClipRange (if needed)
922 	basegfx::B2DRange aClipRange;
923 
924 	if(rSdrBlockTextPrimitive.getClipOnBounds())
925 	{
926 		aClipRange.expand(-aAdjOffset);
927 		aClipRange.expand(basegfx::B2DTuple(aAnchorTextSize.Width(), aAnchorTextSize.Height()) - aAdjOffset);
928 	}
929 
930 	// now break up text primitives.
931 	impTextBreakupHandler aConverter(rOutliner);
932 	aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
933 
934 	// cleanup outliner
935 	rOutliner.Clear();
936 	rOutliner.setVisualizedPage(0);
937 
938 	rTarget = aConverter.getPrimitive2DSequence();
939 }
940 
941 void SdrTextObj::impDecomposeStretchTextPrimitive(
942 	drawinglayer::primitive2d::Primitive2DSequence& rTarget,
943 	const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
944 	const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
945 {
946     // decompose matrix to have position and size of text
947 	basegfx::B2DVector aScale, aTranslate;
948 	double fRotate, fShearX;
949 	rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
950 
951 	// use non-mirrored B2DRange aAnchorTextRange for calculations
952 	basegfx::B2DRange aAnchorTextRange(aTranslate);
953 	aAnchorTextRange.expand(aTranslate + aScale);
954 
955 	// prepare outliner
956 	SdrOutliner& rOutliner = ImpGetDrawOutliner();
957 	const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
958 	const Size aNullSize;
959 
960 	rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_STRETCHING|EE_CNTRL_AUTOPAGESIZE);
961 	rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
962 	rOutliner.SetMinAutoPaperSize(aNullSize);
963 	rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
964 	rOutliner.SetPaperSize(aNullSize);
965 	rOutliner.SetUpdateMode(true);
966 	rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
967 
968 	// set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
969 	rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
970 
971 	// now get back the layouted text size from outliner
972 	const Size aOutlinerTextSiz(rOutliner.CalcTextSize());
973 	const basegfx::B2DVector aOutlinerScale(
974 		basegfx::fTools::equalZero(aOutlinerTextSiz.Width()) ? 1.0 : aOutlinerTextSiz.Width(),
975 		basegfx::fTools::equalZero(aOutlinerTextSiz.Height()) ? 1.0 : aOutlinerTextSiz.Height());
976 
977 	// prepare matrices to apply to newly created primitives
978 	basegfx::B2DHomMatrix aNewTransformA;
979 
980     // #i101957# Check for vertical text. If used, aNewTransformA
981     // needs to translate the text initially around object width to orient
982     // it relative to the topper right instead of the topper left
983     const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsVertical());
984 
985     if(bVertical)
986     {
987 	    aNewTransformA.translate(aScale.getX(), 0.0);
988     }
989 
990     // calculate global char stretching scale parameters. Use non-mirrored sizes
991 	// to layout without mirroring
992 	const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
993 	const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
994 	rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0));
995 
996 	// mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
997 	// move the null point which was top left to bottom right.
998 	const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
999 	const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1000 
1001 	// in-between the translations of the single primitives will take place. Afterwards,
1002 	// the object's transformations need to be applied
1003 	const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1004 		bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1005 		fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1006 
1007 	// now break up text primitives.
1008 	impTextBreakupHandler aConverter(rOutliner);
1009 	aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
1010 
1011 	// cleanup outliner
1012 	rOutliner.SetControlWord(nOriginalControlWord);
1013 	rOutliner.Clear();
1014 	rOutliner.setVisualizedPage(0);
1015 
1016 	rTarget = aConverter.getPrimitive2DSequence();
1017 }
1018 
1019 //////////////////////////////////////////////////////////////////////////////
1020 // timing generators
1021 #define ENDLESS_LOOP	(0xffffffff)
1022 #define ENDLESS_TIME	((double)0xffffffff)
1023 #define PIXEL_DPI		(96.0)
1024 
1025 void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
1026 {
1027 	if(SDRTEXTANI_BLINK == GetTextAniKind())
1028 	{
1029 		// get values
1030 		const SfxItemSet& rSet = GetObjectItemSet();
1031 		const sal_uInt32 nRepeat((sal_uInt32)((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1032 		bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1033 		double fDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1034 
1035 		if(0.0 == fDelay)
1036 		{
1037 			// use default
1038 			fDelay = 250.0;
1039 		}
1040 
1041 		// prepare loop and add
1042 		drawinglayer::animation::AnimationEntryLoop  aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1043         drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0);
1044 		aLoop.append(aStart);
1045         drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0);
1046 		aLoop.append(aEnd);
1047 		rAnimList.append(aLoop);
1048 
1049 		// add stopped state if loop is not endless
1050 		if(0L != nRepeat)
1051 		{
1052             drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisisbleWhenStopped ? 0.0 : 1.0);
1053 			rAnimList.append(aStop);
1054 		}
1055 	}
1056 }
1057 
1058 void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1059 {
1060 	bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1061 	bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1062 	const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1063 
1064 	if(bVisisbleWhenStarted)
1065 	{
1066 		// move from center to outside
1067         drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1068 		rAnimList.append(aInOut);
1069 	}
1070 
1071 	// loop. In loop, move through
1072 	if(nRepeat || 0L == nRepeat)
1073 	{
1074 		drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1075         drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
1076 		aLoop.append(aThrough);
1077 		rAnimList.append(aLoop);
1078 	}
1079 
1080 	if(0L != nRepeat && bVisisbleWhenStopped)
1081 	{
1082 		// move from outside to center
1083         drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1084 		rAnimList.append(aOutIn);
1085 
1086 		// add timing for staying at the end
1087         drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1088 		rAnimList.append(aEnd);
1089 	}
1090 }
1091 
1092 void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
1093 {
1094 	if(basegfx::fTools::more(fRelativeTextLength, 0.5))
1095 	{
1096 		// this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
1097 		// In that case, correct direction
1098 		bForward = !bForward;
1099 	}
1100 
1101 	const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
1102 	const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
1103 	bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1104 	bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1105 	const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1106 
1107 	if(!bVisisbleWhenStarted)
1108 	{
1109 		// move from outside to center
1110         drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1111 		rAnimList.append(aOutIn);
1112 	}
1113 
1114 	// loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
1115 	// so use absolute value
1116 	const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
1117 	const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
1118 	const double fHalfInnerPath(fTimeForInnerPath * 0.5);
1119 	const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
1120 
1121 	if(nDoubleRepeat || 0L == nRepeat)
1122 	{
1123 		// double forth and back loop
1124 		drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
1125         drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1126 		aLoop.append(aTime0);
1127         drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
1128 		aLoop.append(aTime1);
1129         drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
1130 		aLoop.append(aTime2);
1131 		rAnimList.append(aLoop);
1132 	}
1133 
1134 	if(nRepeat % 2L)
1135 	{
1136 		// repeat is uneven, so we need one more forth and back to center
1137         drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1138 		rAnimList.append(aTime0);
1139         drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
1140 		rAnimList.append(aTime1);
1141 	}
1142 
1143 	if(0L != nRepeat)
1144 	{
1145 		if(bVisisbleWhenStopped)
1146 		{
1147 			// add timing for staying at the end
1148             drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1149 			rAnimList.append(aEnd);
1150 		}
1151 		else
1152 		{
1153 			// move from center to outside
1154             drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1155 			rAnimList.append(aInOut);
1156 		}
1157 	}
1158 }
1159 
1160 void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1161 {
1162 	// move in from outside, start outside
1163 	const double fStartPosition(bForward ? 0.0 : 1.0);
1164 	const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1165 
1166 	// move from outside to center
1167     drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1168 	rAnimList.append(aOutIn);
1169 
1170 	// loop. In loop, move out and in again
1171 	if(nRepeat > 1L || 0L == nRepeat)
1172 	{
1173 		drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1L : ENDLESS_LOOP);
1174         drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
1175 		aLoop.append(aTime0);
1176         drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1177 		aLoop.append(aTime1);
1178 		rAnimList.append(aLoop);
1179 	}
1180 
1181 	// always visible when stopped, so add timing for staying at the end when not endless
1182 	if(0L != nRepeat)
1183 	{
1184         drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1185 		rAnimList.append(aEnd);
1186 	}
1187 }
1188 
1189 void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
1190 {
1191 	const SdrTextAniKind eAniKind(GetTextAniKind());
1192 
1193 	if(SDRTEXTANI_SCROLL == eAniKind || SDRTEXTANI_ALTERNATE == eAniKind || SDRTEXTANI_SLIDE == eAniKind)
1194 	{
1195 		// get data. Goal is to calculate fTimeFullPath which is the time needed to
1196 		// move animation from (0.0) to (1.0) state
1197 		const SfxItemSet& rSet = GetObjectItemSet();
1198 		double fAnimationDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1199 		double fSingleStepWidth((double)((SdrTextAniAmountItem&)rSet.Get(SDRATTR_TEXT_ANIAMOUNT)).GetValue());
1200 		const SdrTextAniDirection eDirection(GetTextAniDirection());
1201 		const bool bForward(SDRTEXTANI_RIGHT == eDirection || SDRTEXTANI_DOWN == eDirection);
1202 
1203 		if(basegfx::fTools::equalZero(fAnimationDelay))
1204 		{
1205 			// default to 1/20 second
1206 			fAnimationDelay = 50.0;
1207 		}
1208 
1209 		if(basegfx::fTools::less(fSingleStepWidth, 0.0))
1210 		{
1211 			// data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
1212 			// It makes no sense to keep the view-transformation centered
1213 			// definitions, so get rid of them here.
1214 			fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
1215 		}
1216 
1217 		if(basegfx::fTools::equalZero(fSingleStepWidth))
1218 		{
1219 			// default to 1 milimeter
1220 			fSingleStepWidth = 100.0;
1221 		}
1222 
1223 		// use the length of the full animation path and the number of steps
1224 		// to get the full path time
1225 		const double fFullPathLength(fFrameLength + fTextLength);
1226 		const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
1227 		double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
1228 
1229 		if(fTimeFullPath < fAnimationDelay)
1230 		{
1231 			fTimeFullPath = fAnimationDelay;
1232 		}
1233 
1234 		switch(eAniKind)
1235 		{
1236 			case SDRTEXTANI_SCROLL :
1237 			{
1238 				impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1239 				break;
1240 			}
1241 			case SDRTEXTANI_ALTERNATE :
1242 			{
1243 				double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
1244 				impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
1245 				break;
1246 			}
1247 			case SDRTEXTANI_SLIDE :
1248 			{
1249 				impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1250 				break;
1251 			}
1252 			default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK
1253 		}
1254 	}
1255 }
1256 
1257 //////////////////////////////////////////////////////////////////////////////
1258 // eof
1259