update credits
[LibreOffice.git] / svx / source / svdraw / svdotextdecomposition.cxx
blob0d53e44dc353189bd03425f368443b21ee38a5ec
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <svx/svdotext.hxx>
22 #include <svx/svdoutl.hxx>
23 #include <basegfx/vector/b2dvector.hxx>
24 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
25 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
26 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
27 #include <basegfx/range/b2drange.hxx>
28 #include <editeng/editstat.hxx>
29 #include <tools/helpers.hxx>
30 #include <svx/sdtfchim.hxx>
31 #include <svl/itemset.hxx>
32 #include <basegfx/polygon/b2dpolygontools.hxx>
33 #include <basegfx/polygon/b2dpolygon.hxx>
34 #include <drawinglayer/animation/animationtiming.hxx>
35 #include <basegfx/color/bcolor.hxx>
36 #include <vcl/svapp.hxx>
37 #include <editeng/eeitemid.hxx>
38 #include <editeng/escapementitem.hxx>
39 #include <editeng/svxenum.hxx>
40 #include <editeng/flditem.hxx>
41 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
42 #include <vcl/metaact.hxx>
43 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
44 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
45 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
46 #include <svx/unoapi.hxx>
47 #include <drawinglayer/geometry/viewinformation2d.hxx>
48 #include <editeng/outlobj.hxx>
49 #include <basegfx/matrix/b2dhommatrixtools.hxx>
51 //////////////////////////////////////////////////////////////////////////////
52 // helpers
54 namespace
56 drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector)
58 const sal_Int32 nCount(rPrimitiveVector.size());
59 drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount);
61 for(sal_Int32 a(0L); a < nCount; a++)
63 aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]);
66 return aRetval;
69 class impTextBreakupHandler
71 private:
72 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maTextPortionPrimitives;
73 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maLinePrimitives;
74 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maParagraphPrimitives;
76 SdrOutliner& mrOutliner;
77 basegfx::B2DHomMatrix maNewTransformA;
78 basegfx::B2DHomMatrix maNewTransformB;
80 // the visible area for contour text decomposition
81 basegfx::B2DVector maScale;
83 // ClipRange for BlockText decomposition; only text portions completely
84 // inside are to be accepted, so this is different from geometric clipping
85 // (which would allow e.g. upper parts of portions to remain). Only used for
86 // BlockText (see there)
87 basegfx::B2DRange maClipRange;
89 DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* );
90 DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* );
91 DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* );
93 DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* );
94 DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* );
95 DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* );
97 bool impIsUnderlineAbove(const Font& rFont) const;
98 void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
99 drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const;
100 void impFlushTextPortionPrimitivesToLinePrimitives();
101 void impFlushLinePrimitivesToParagraphPrimitives();
102 void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
103 void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
105 public:
106 impTextBreakupHandler(SdrOutliner& rOutliner)
107 : maTextPortionPrimitives(),
108 maLinePrimitives(),
109 maParagraphPrimitives(),
110 mrOutliner(rOutliner),
111 maNewTransformA(),
112 maNewTransformB(),
113 maScale(),
114 maClipRange()
118 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
120 maScale = rScale;
121 maNewTransformA = rNewTransformA;
122 maNewTransformB = rNewTransformB;
123 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
124 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
125 mrOutliner.StripPortions();
126 mrOutliner.SetDrawPortionHdl(Link());
127 mrOutliner.SetDrawBulletHdl(Link());
130 void decomposeBlockTextPrimitive(
131 const basegfx::B2DHomMatrix& rNewTransformA,
132 const basegfx::B2DHomMatrix& rNewTransformB,
133 const basegfx::B2DRange& rClipRange)
135 maNewTransformA = rNewTransformA;
136 maNewTransformB = rNewTransformB;
137 maClipRange = rClipRange;
138 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
139 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
140 mrOutliner.StripPortions();
141 mrOutliner.SetDrawPortionHdl(Link());
142 mrOutliner.SetDrawBulletHdl(Link());
145 void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
147 maNewTransformA = rNewTransformA;
148 maNewTransformB = rNewTransformB;
149 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
150 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
151 mrOutliner.StripPortions();
152 mrOutliner.SetDrawPortionHdl(Link());
153 mrOutliner.SetDrawBulletHdl(Link());
156 drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence();
159 bool impTextBreakupHandler::impIsUnderlineAbove(const Font& rFont) const
161 if(!rFont.IsVertical())
163 return false;
166 if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
168 // the underline is right for Japanese only
169 return true;
172 return false;
175 void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
177 if(rInfo.mrText.Len() && rInfo.mnTextLen)
179 basegfx::B2DVector aFontScaling;
180 drawinglayer::attribute::FontAttribute aFontAttribute(
181 drawinglayer::primitive2d::getFontAttributeFromVclFont(
182 aFontScaling,
183 rInfo.mrFont,
184 rInfo.IsRTL(),
185 false));
186 basegfx::B2DHomMatrix aNewTransform;
188 // add font scale to new transform
189 aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
191 // look for proportional font scaling, if necessary, scale accordingly
192 if(100 != rInfo.mrFont.GetPropr())
194 const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
195 aNewTransform.scale(fFactor, fFactor);
198 // apply font rotate
199 if(rInfo.mrFont.GetOrientation())
201 aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800);
204 // look for escapement, if necessary, translate accordingly
205 if(rInfo.mrFont.GetEscapement())
207 sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
209 if(DFLT_ESC_AUTO_SUPER == nEsc)
211 nEsc = 33;
213 else if(DFLT_ESC_AUTO_SUB == nEsc)
215 nEsc = -20;
218 if(nEsc > 100)
220 nEsc = 100;
222 else if(nEsc < -100)
224 nEsc = -100;
227 const double fEscapement(nEsc / -100.0);
228 aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
231 // apply transformA
232 aNewTransform *= maNewTransformA;
234 // apply local offset
235 aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
237 // also apply embedding object's transform
238 aNewTransform *= maNewTransformB;
240 // prepare DXArray content. To make it independent from font size (and such from
241 // the text transformation), scale it to unit coordinates
242 ::std::vector< double > aDXArray;
243 static bool bDisableTextArray(false);
245 if(!bDisableTextArray && rInfo.mpDXArray && rInfo.mnTextLen)
247 aDXArray.reserve(rInfo.mnTextLen);
249 for(xub_StrLen a(0); a < rInfo.mnTextLen; a++)
251 aDXArray.push_back((double)rInfo.mpDXArray[a]);
255 // create complex text primitive and append
256 const Color aFontColor(rInfo.mrFont.GetColor());
257 const basegfx::BColor aBFontColor(aFontColor.getBColor());
259 // prepare wordLineMode (for underline and strikeout)
260 // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
261 // to be split which would not look like the original
262 const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
264 // prepare new primitive
265 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = 0;
266 const bool bDecoratedIsNeeded(
267 UNDERLINE_NONE != rInfo.mrFont.GetOverline()
268 || UNDERLINE_NONE != rInfo.mrFont.GetUnderline()
269 || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
270 || EMPHASISMARK_NONE != (rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
271 || RELIEF_NONE != rInfo.mrFont.GetRelief()
272 || rInfo.mrFont.IsShadow()
273 || bWordLineMode);
275 if(bDecoratedIsNeeded)
277 // TextDecoratedPortionPrimitive2D needed, prepare some more data
278 // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
279 const Color aUnderlineColor(rInfo.maTextLineColor);
280 const basegfx::BColor aBUnderlineColor((0xffffffff == aUnderlineColor.GetColor()) ? aBFontColor : aUnderlineColor.getBColor());
281 const Color aOverlineColor(rInfo.maOverlineColor);
282 const basegfx::BColor aBOverlineColor((0xffffffff == aOverlineColor.GetColor()) ? aBFontColor : aOverlineColor.getBColor());
284 // prepare overline and underline data
285 const drawinglayer::primitive2d::TextLine eFontOverline(
286 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetOverline()));
287 const drawinglayer::primitive2d::TextLine eFontUnderline(
288 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetUnderline()));
290 // check UnderlineAbove
291 const bool bUnderlineAbove(
292 drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont));
294 // prepare strikeout data
295 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
296 drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
298 // prepare emphasis mark data
299 drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
301 switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
303 case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
304 case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
305 case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
306 case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
309 const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
310 const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
312 // prepare font relief data
313 drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
315 switch(rInfo.mrFont.GetRelief())
317 case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
318 case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
319 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
322 // prepare shadow/outline data
323 const bool bShadow(rInfo.mrFont.IsShadow());
325 // TextDecoratedPortionPrimitive2D is needed, create one
326 pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
328 // attributes for TextSimplePortionPrimitive2D
329 aNewTransform,
330 rInfo.mrText,
331 rInfo.mnTextStart,
332 rInfo.mnTextLen,
333 aDXArray,
334 aFontAttribute,
335 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
336 aBFontColor,
338 // attributes for TextDecoratedPortionPrimitive2D
339 aBOverlineColor,
340 aBUnderlineColor,
341 eFontOverline,
342 eFontUnderline,
343 bUnderlineAbove,
344 eTextStrikeout,
345 bWordLineMode,
346 eTextEmphasisMark,
347 bEmphasisMarkAbove,
348 bEmphasisMarkBelow,
349 eTextRelief,
350 bShadow);
352 else
354 // TextSimplePortionPrimitive2D is enough
355 pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
356 aNewTransform,
357 rInfo.mrText,
358 rInfo.mnTextStart,
359 rInfo.mnTextLen,
360 aDXArray,
361 aFontAttribute,
362 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
363 aBFontColor,
364 rInfo.mbFilled,
365 rInfo.mnWidthToFill);
368 if(rInfo.mbEndOfBullet)
370 // embed in TextHierarchyBulletPrimitive2D
371 const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
372 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
373 pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
376 if(rInfo.mpFieldData)
378 pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo);
381 maTextPortionPrimitives.push_back(pNewPrimitive);
383 // support for WrongSpellVector. Create WrongSpellPrimitives as needed
384 if(rInfo.mpWrongSpellVector && !aDXArray.empty())
386 const sal_uInt32 nSize(rInfo.mpWrongSpellVector->size());
387 const sal_uInt32 nDXCount(aDXArray.size());
388 const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
390 for(sal_uInt32 a(0); a < nSize; a++)
392 const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
394 if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
396 const sal_uInt32 nStart(rCandidate.nStart - rInfo.mnTextStart);
397 const sal_uInt32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
398 double fStart(0.0);
399 double fEnd(0.0);
401 if(nStart > 0 && nStart - 1 < nDXCount)
403 fStart = aDXArray[nStart - 1];
406 if(nEnd > 0 && nEnd - 1 < nDXCount)
408 fEnd = aDXArray[nEnd - 1];
411 if(!basegfx::fTools::equal(fStart, fEnd))
413 if(rInfo.IsRTL())
415 // #i98523#
416 // When the portion is RTL, mirror the redlining using the
417 // full portion width
418 const double fTextWidth(aDXArray[aDXArray.size() - 1]);
420 fStart = fTextWidth - fStart;
421 fEnd = fTextWidth - fEnd;
424 // need to take FontScaling out of values; it's already part of
425 // aNewTransform and would be double applied
426 const double fFontScaleX(aFontScaling.getX());
428 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
429 && !basegfx::fTools::equalZero(fFontScaleX))
431 fStart /= fFontScaleX;
432 fEnd /= fFontScaleX;
435 maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
436 aNewTransform,
437 fStart,
438 fEnd,
439 aSpellColor));
447 drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const
449 if(rInfo.mpFieldData)
451 // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
452 // which holds the field type and, if applicable, the URL
453 const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
454 const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
456 // embed current primitive to a sequence
457 drawinglayer::primitive2d::Primitive2DSequence aSequence;
459 if(pPrimitive)
461 aSequence.realloc(1);
462 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
465 if(pURLField)
467 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
469 else if(pPageField)
471 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, String());
473 else
475 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, String());
479 return pPrimitive;
482 void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
484 // only create a line primitive when we had content; there is no need for
485 // empty line primitives (contrary to paragraphs, see below).
486 if(!maTextPortionPrimitives.empty())
488 drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives));
489 maTextPortionPrimitives.clear();
490 maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence));
494 void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives()
496 // ALWAYS create a paragraph primitive, even when no content was added. This is done to
497 // have the correct paragraph count even with empty paragraphs. Those paragraphs will
498 // have an empty sub-PrimitiveSequence.
499 drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives));
500 maLinePrimitives.clear();
501 maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence));
504 void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
506 impCreateTextPortionPrimitive(rInfo);
508 if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
510 impFlushTextPortionPrimitivesToLinePrimitives();
513 if(rInfo.mbEndOfParagraph)
515 impFlushLinePrimitivesToParagraphPrimitives();
519 void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
521 basegfx::B2DHomMatrix aNewTransform;
523 // add size to new transform
524 aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
526 // apply transformA
527 aNewTransform *= maNewTransformA;
529 // apply local offset
530 aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
532 // also apply embedding object's transform
533 aNewTransform *= maNewTransformB;
535 // prepare empty GraphicAttr
536 const GraphicAttr aGraphicAttr;
538 // create GraphicPrimitive2D
539 const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
540 aNewTransform,
541 rInfo.maBulletGraphicObject,
542 aGraphicAttr));
544 // embed in TextHierarchyBulletPrimitive2D
545 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
546 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
548 // add to output
549 maTextPortionPrimitives.push_back(pNewPrimitive);
552 IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo)
554 // for contour text, ignore (clip away) all portions which are below
555 // the visible area given by maScale
556 if(pInfo && (double)pInfo->mrStartPos.Y() < maScale.getY())
558 impHandleDrawPortionInfo(*pInfo);
561 return 0;
564 IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo)
566 if(pInfo)
568 // Is clipping wanted? This is text clipping; only accept a portion
569 // if it's completely in the range
570 if(!maClipRange.isEmpty())
572 // Test start position first; this allows to not get the text range at
573 // all if text is far outside
574 const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
576 if(!maClipRange.isInside(aStartPosition))
578 return 0;
581 // Start position is inside. Get TextBoundRect and TopLeft next
582 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
583 aTextLayouterDevice.setFont(pInfo->mrFont);
585 const basegfx::B2DRange aTextBoundRect(
586 aTextLayouterDevice.getTextBoundRect(
587 pInfo->mrText, pInfo->mnTextStart, pInfo->mnTextLen));
588 const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
590 if(!maClipRange.isInside(aTopLeft))
592 return 0;
595 // TopLeft is inside. Get BottomRight and check
596 const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
598 if(!maClipRange.isInside(aBottomRight))
600 return 0;
603 // all inside, clip was successful
605 impHandleDrawPortionInfo(*pInfo);
608 return 0;
611 IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo)
613 if(pInfo)
615 impHandleDrawPortionInfo(*pInfo);
618 return 0;
621 IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo)
623 if(pInfo)
625 impHandleDrawBulletInfo(*pInfo);
628 return 0;
631 IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo)
633 if(pInfo)
635 impHandleDrawBulletInfo(*pInfo);
638 return 0;
641 IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo)
643 if(pInfo)
645 impHandleDrawBulletInfo(*pInfo);
648 return 0;
651 drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence()
653 if(!maTextPortionPrimitives.empty())
655 // collect non-closed lines
656 impFlushTextPortionPrimitivesToLinePrimitives();
659 if(!maLinePrimitives.empty())
661 // collect non-closed paragraphs
662 impFlushLinePrimitivesToParagraphPrimitives();
665 return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives);
667 } // end of anonymous namespace
669 //////////////////////////////////////////////////////////////////////////////
670 // primitive decompositions
672 void SdrTextObj::impDecomposeContourTextPrimitive(
673 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
674 const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
675 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
677 // decompose matrix to have position and size of text
678 basegfx::B2DVector aScale, aTranslate;
679 double fRotate, fShearX;
680 rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
682 // prepare contour polygon, force to non-mirrored for laying out
683 basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
684 aPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
686 // prepare outliner
687 SdrOutliner& rOutliner = ImpGetDrawOutliner();
688 const Size aNullSize;
689 rOutliner.SetPaperSize(aNullSize);
690 rOutliner.SetPolygon(aPolyPolygon);
691 rOutliner.SetUpdateMode(true);
692 rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
694 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
695 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
697 // prepare matrices to apply to newly created primitives
698 basegfx::B2DHomMatrix aNewTransformA;
700 // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
701 // move the null point which was top left to bottom right.
702 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
703 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
705 // in-between the translations of the single primitives will take place. Afterwards,
706 // the object's transformations need to be applied
707 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
708 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
709 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
711 // now break up text primitives.
712 impTextBreakupHandler aConverter(rOutliner);
713 aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
715 // cleanup outliner
716 rOutliner.Clear();
717 rOutliner.setVisualizedPage(0);
719 rTarget = aConverter.getPrimitive2DSequence();
722 void SdrTextObj::impDecomposeAutoFitTextPrimitive(
723 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
724 const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
725 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
727 // decompose matrix to have position and size of text
728 basegfx::B2DVector aScale, aTranslate;
729 double fRotate, fShearX;
730 rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
732 // use B2DRange aAnchorTextRange for calculations
733 basegfx::B2DRange aAnchorTextRange(aTranslate);
734 aAnchorTextRange.expand(aTranslate + aScale);
736 // prepare outliner
737 const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
738 SdrOutliner& rOutliner = ImpGetDrawOutliner();
739 SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
740 SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
741 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
742 const Size aNullSize;
744 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
745 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
747 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE|EE_CNTRL_STRETCHING);
748 rOutliner.SetMinAutoPaperSize(aNullSize);
749 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
751 // add one to rage sizes to get back to the old Rectangle and outliner measurements
752 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
753 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
754 const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
755 OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
756 const bool bVerticalWritintg(pOutlinerParaObject->IsVertical());
757 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
759 if((rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame()))
761 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
764 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg)
766 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
769 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg)
771 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
774 rOutliner.SetPaperSize(aNullSize);
775 rOutliner.SetUpdateMode(true);
776 rOutliner.SetText(*pOutlinerParaObject);
777 ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWritintg);
779 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
780 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
782 // now get back the layouted text size from outliner
783 const Size aOutlinerTextSiz(rOutliner.GetPaperSize());
784 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height());
785 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
787 // correct horizontal translation using the now known text size
788 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
790 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
792 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
794 aAdjustTranslate.setX(fFree / 2.0);
797 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
799 aAdjustTranslate.setX(fFree);
803 // correct vertical translation using the now known text size
804 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
806 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
808 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
810 aAdjustTranslate.setY(fFree / 2.0);
813 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
815 aAdjustTranslate.setY(fFree);
819 // prepare matrices to apply to newly created primitives. aNewTransformA
820 // will get coordinates in aOutlinerScale size and positive in X, Y.
821 basegfx::B2DHomMatrix aNewTransformA;
822 basegfx::B2DHomMatrix aNewTransformB;
824 // translate relative to given primitive to get same rotation and shear
825 // as the master shape we are working on. For vertical, use the top-right
826 // corner
827 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
828 aNewTransformA.translate(fStartInX, aAdjustTranslate.getY());
830 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
831 // move the null point which was top left to bottom right.
832 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
833 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
834 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
836 // in-between the translations of the single primitives will take place. Afterwards,
837 // the object's transformations need to be applied
838 aNewTransformB.shearX(fShearX);
839 aNewTransformB.rotate(fRotate);
840 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
842 basegfx::B2DRange aClipRange;
844 // now break up text primitives.
845 impTextBreakupHandler aConverter(rOutliner);
846 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
848 // cleanup outliner
849 rOutliner.Clear();
850 rOutliner.setVisualizedPage(0);
851 rOutliner.SetControlWord(nOriginalControlWord);
853 rTarget = aConverter.getPrimitive2DSequence();
856 void SdrTextObj::impDecomposeBlockTextPrimitive(
857 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
858 const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
859 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
861 // decompose matrix to have position and size of text
862 basegfx::B2DVector aScale, aTranslate;
863 double fRotate, fShearX;
864 rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
866 // use B2DRange aAnchorTextRange for calculations
867 basegfx::B2DRange aAnchorTextRange(aTranslate);
868 aAnchorTextRange.expand(aTranslate + aScale);
870 // prepare outliner
871 const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
872 SdrOutliner& rOutliner = ImpGetDrawOutliner();
873 SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
874 SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
875 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
876 const Size aNullSize;
878 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
879 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
880 rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
881 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE);
882 rOutliner.SetMinAutoPaperSize(aNullSize);
883 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
885 // add one to rage sizes to get back to the old Rectangle and outliner measurements
886 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
887 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
888 const bool bVerticalWritintg(rSdrBlockTextPrimitive.getOutlinerParaObject().IsVertical());
889 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
891 if(bIsCell)
893 // cell text is formated neither like a text object nor like a object
894 // text, so use a special setup here
895 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
897 // #i106214# To work with an unchangeable PaperSize (CellSize in
898 // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
899 // #i106214# This was not completely correct; to still measure the real
900 // text height to allow vertical adjust (and vice versa for VerticalWritintg)
901 // only one aspect has to be set, but the other one to zero
902 if(bVerticalWritintg)
904 // measure the horizontal text size
905 rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
907 else
909 // measure the vertical text size
910 rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
913 rOutliner.SetPaperSize(aAnchorTextSize);
914 rOutliner.SetUpdateMode(true);
915 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
917 else
919 // check if block text is used (only one of them can be true)
920 const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg);
921 const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg);
923 // set minimal paper size horizontally/vertically if needed
924 if(bHorizontalIsBlock)
926 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
928 else if(bVerticalIsBlock)
930 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
933 if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
935 // #i103454# maximal paper size hor/ver needs to be limited to text
936 // frame size. If it's block text, still allow the 'other' direction
937 // to grow to get a correct real text size when using GetPaperSize().
938 // When just using aAnchorTextSize as maximum, GetPaperSize()
939 // would just return aAnchorTextSize again: this means, the wanted
940 // 'measurement' of the real size of block text would not work
941 Size aMaxAutoPaperSize(aAnchorTextSize);
943 if(bHorizontalIsBlock)
945 // allow to grow vertical for horizontal blocks
946 aMaxAutoPaperSize.setHeight(1000000);
948 else if(bVerticalIsBlock)
950 // allow to grow horizontal for vertical blocks
951 aMaxAutoPaperSize.setWidth(1000000);
954 rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
957 rOutliner.SetPaperSize(aNullSize);
958 rOutliner.SetUpdateMode(true);
959 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
962 rOutliner.SetControlWord(nOriginalControlWord);
964 // now get back the layouted text size from outliner
965 const Size aOutlinerTextSiz(rOutliner.GetPaperSize());
966 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height());
967 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
969 // For draw objects containing text correct hor/ver alignment if text is bigger
970 // than the object itself. Without that correction, the text would always be
971 // formatted to the left edge (or top edge when vertical) of the draw object.
972 if(!IsTextFrame() && !bIsCell)
974 if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg)
976 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
977 // else the alignment is wanted.
978 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
980 eHAdj = SDRTEXTHORZADJUST_CENTER;
984 if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg)
986 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
987 // else the alignment is wanted.
988 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
990 eVAdj = SDRTEXTVERTADJUST_CENTER;
995 // correct horizontal translation using the now known text size
996 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
998 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
1000 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
1002 aAdjustTranslate.setX(fFree / 2.0);
1005 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
1007 aAdjustTranslate.setX(fFree);
1011 // correct vertical translation using the now known text size
1012 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1014 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
1016 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
1018 aAdjustTranslate.setY(fFree / 2.0);
1021 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1023 aAdjustTranslate.setY(fFree);
1027 // prepare matrices to apply to newly created primitives. aNewTransformA
1028 // will get coordinates in aOutlinerScale size and positive in X, Y.
1029 // Translate relative to given primitive to get same rotation and shear
1030 // as the master shape we are working on. For vertical, use the top-right
1031 // corner
1032 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
1033 const basegfx::B2DTuple aAdjOffset(fStartInX, aAdjustTranslate.getY());
1034 basegfx::B2DHomMatrix aNewTransformA(basegfx::tools::createTranslateB2DHomMatrix(aAdjOffset.getX(), aAdjOffset.getY()));
1036 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1037 // move the null point which was top left to bottom right.
1038 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1039 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1041 // in-between the translations of the single primitives will take place. Afterwards,
1042 // the object's transformations need to be applied
1043 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1044 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1045 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1047 // create ClipRange (if needed)
1048 basegfx::B2DRange aClipRange;
1050 if(rSdrBlockTextPrimitive.getClipOnBounds())
1052 aClipRange.expand(-aAdjOffset);
1053 aClipRange.expand(basegfx::B2DTuple(aAnchorTextSize.Width(), aAnchorTextSize.Height()) - aAdjOffset);
1056 // now break up text primitives.
1057 impTextBreakupHandler aConverter(rOutliner);
1058 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
1060 // cleanup outliner
1061 rOutliner.Clear();
1062 rOutliner.setVisualizedPage(0);
1064 rTarget = aConverter.getPrimitive2DSequence();
1067 void SdrTextObj::impDecomposeStretchTextPrimitive(
1068 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1069 const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
1070 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
1072 // decompose matrix to have position and size of text
1073 basegfx::B2DVector aScale, aTranslate;
1074 double fRotate, fShearX;
1075 rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
1077 // use non-mirrored B2DRange aAnchorTextRange for calculations
1078 basegfx::B2DRange aAnchorTextRange(aTranslate);
1079 aAnchorTextRange.expand(aTranslate + aScale);
1081 // prepare outliner
1082 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1083 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
1084 const Size aNullSize;
1086 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_STRETCHING|EE_CNTRL_AUTOPAGESIZE);
1087 rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
1088 rOutliner.SetMinAutoPaperSize(aNullSize);
1089 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
1090 rOutliner.SetPaperSize(aNullSize);
1091 rOutliner.SetUpdateMode(true);
1092 rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
1094 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1095 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1097 // now get back the laid out text size from outliner
1098 const Size aOutlinerTextSiz(rOutliner.CalcTextSize());
1099 const basegfx::B2DVector aOutlinerScale(
1100 basegfx::fTools::equalZero(aOutlinerTextSiz.Width()) ? 1.0 : aOutlinerTextSiz.Width(),
1101 basegfx::fTools::equalZero(aOutlinerTextSiz.Height()) ? 1.0 : aOutlinerTextSiz.Height());
1103 // prepare matrices to apply to newly created primitives
1104 basegfx::B2DHomMatrix aNewTransformA;
1106 // #i101957# Check for vertical text. If used, aNewTransformA
1107 // needs to translate the text initially around object width to orient
1108 // it relative to the topper right instead of the topper left
1109 const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsVertical());
1111 if(bVertical)
1113 aNewTransformA.translate(aScale.getX(), 0.0);
1116 // calculate global char stretching scale parameters. Use non-mirrored sizes
1117 // to layout without mirroring
1118 const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
1119 const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
1120 rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0));
1122 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1123 // move the null point which was top left to bottom right.
1124 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1125 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1127 // in-between the translations of the single primitives will take place. Afterwards,
1128 // the object's transformations need to be applied
1129 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1130 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1131 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1133 // now break up text primitives.
1134 impTextBreakupHandler aConverter(rOutliner);
1135 aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
1137 // cleanup outliner
1138 rOutliner.SetControlWord(nOriginalControlWord);
1139 rOutliner.Clear();
1140 rOutliner.setVisualizedPage(0);
1142 rTarget = aConverter.getPrimitive2DSequence();
1145 //////////////////////////////////////////////////////////////////////////////
1146 // timing generators
1147 #define ENDLESS_LOOP (0xffffffff)
1148 #define ENDLESS_TIME ((double)0xffffffff)
1149 #define PIXEL_DPI (96.0)
1151 void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
1153 if(SDRTEXTANI_BLINK == GetTextAniKind())
1155 // get values
1156 const SfxItemSet& rSet = GetObjectItemSet();
1157 const sal_uInt32 nRepeat((sal_uInt32)((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1158 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1159 double fDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1161 if(0.0 == fDelay)
1163 // use default
1164 fDelay = 250.0;
1167 // prepare loop and add
1168 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1169 drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0);
1170 aLoop.append(aStart);
1171 drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0);
1172 aLoop.append(aEnd);
1173 rAnimList.append(aLoop);
1175 // add stopped state if loop is not endless
1176 if(0L != nRepeat)
1178 drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisisbleWhenStopped ? 0.0 : 1.0);
1179 rAnimList.append(aStop);
1184 void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1186 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1187 bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1188 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1190 if(bVisisbleWhenStarted)
1192 // move from center to outside
1193 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1194 rAnimList.append(aInOut);
1197 // loop. In loop, move through
1198 if(nRepeat || 0L == nRepeat)
1200 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1201 drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
1202 aLoop.append(aThrough);
1203 rAnimList.append(aLoop);
1206 if(0L != nRepeat && bVisisbleWhenStopped)
1208 // move from outside to center
1209 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1210 rAnimList.append(aOutIn);
1212 // add timing for staying at the end
1213 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1214 rAnimList.append(aEnd);
1218 void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
1220 if(basegfx::fTools::more(fRelativeTextLength, 0.5))
1222 // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
1223 // In that case, correct direction
1224 bForward = !bForward;
1227 const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
1228 const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
1229 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1230 bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1231 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1233 if(!bVisisbleWhenStarted)
1235 // move from outside to center
1236 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1237 rAnimList.append(aOutIn);
1240 // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
1241 // so use absolute value
1242 const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
1243 const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
1244 const double fHalfInnerPath(fTimeForInnerPath * 0.5);
1245 const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
1247 if(nDoubleRepeat || 0L == nRepeat)
1249 // double forth and back loop
1250 drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
1251 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1252 aLoop.append(aTime0);
1253 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
1254 aLoop.append(aTime1);
1255 drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
1256 aLoop.append(aTime2);
1257 rAnimList.append(aLoop);
1260 if(nRepeat % 2L)
1262 // repeat is uneven, so we need one more forth and back to center
1263 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1264 rAnimList.append(aTime0);
1265 drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
1266 rAnimList.append(aTime1);
1269 if(0L != nRepeat)
1271 if(bVisisbleWhenStopped)
1273 // add timing for staying at the end
1274 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1275 rAnimList.append(aEnd);
1277 else
1279 // move from center to outside
1280 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1281 rAnimList.append(aInOut);
1286 void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1288 // move in from outside, start outside
1289 const double fStartPosition(bForward ? 0.0 : 1.0);
1290 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1292 // move from outside to center
1293 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1294 rAnimList.append(aOutIn);
1296 // loop. In loop, move out and in again
1297 if(nRepeat > 1L || 0L == nRepeat)
1299 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1L : ENDLESS_LOOP);
1300 drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
1301 aLoop.append(aTime0);
1302 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1303 aLoop.append(aTime1);
1304 rAnimList.append(aLoop);
1307 // always visible when stopped, so add timing for staying at the end when not endless
1308 if(0L != nRepeat)
1310 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1311 rAnimList.append(aEnd);
1315 void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
1317 const SdrTextAniKind eAniKind(GetTextAniKind());
1319 if(SDRTEXTANI_SCROLL == eAniKind || SDRTEXTANI_ALTERNATE == eAniKind || SDRTEXTANI_SLIDE == eAniKind)
1321 // get data. Goal is to calculate fTimeFullPath which is the time needed to
1322 // move animation from (0.0) to (1.0) state
1323 const SfxItemSet& rSet = GetObjectItemSet();
1324 double fAnimationDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1325 double fSingleStepWidth((double)((SdrTextAniAmountItem&)rSet.Get(SDRATTR_TEXT_ANIAMOUNT)).GetValue());
1326 const SdrTextAniDirection eDirection(GetTextAniDirection());
1327 const bool bForward(SDRTEXTANI_RIGHT == eDirection || SDRTEXTANI_DOWN == eDirection);
1329 if(basegfx::fTools::equalZero(fAnimationDelay))
1331 // default to 1/20 second
1332 fAnimationDelay = 50.0;
1335 if(basegfx::fTools::less(fSingleStepWidth, 0.0))
1337 // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
1338 // It makes no sense to keep the view-transformation centered
1339 // definitions, so get rid of them here.
1340 fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
1343 if(basegfx::fTools::equalZero(fSingleStepWidth))
1345 // default to 1 millimeter
1346 fSingleStepWidth = 100.0;
1349 // use the length of the full animation path and the number of steps
1350 // to get the full path time
1351 const double fFullPathLength(fFrameLength + fTextLength);
1352 const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
1353 double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
1355 if(fTimeFullPath < fAnimationDelay)
1357 fTimeFullPath = fAnimationDelay;
1360 switch(eAniKind)
1362 case SDRTEXTANI_SCROLL :
1364 impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1365 break;
1367 case SDRTEXTANI_ALTERNATE :
1369 double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
1370 impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
1371 break;
1373 case SDRTEXTANI_SLIDE :
1375 impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1376 break;
1378 default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK
1384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */