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