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