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