bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / svdraw / svdotextdecomposition.cxx
blob756b59a8b8f6db8a34b5ee97a0852a97820b1dc8
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/svdetc.hxx>
22 #include <svx/svdoutl.hxx>
23 #include <svx/svdpage.hxx>
24 #include <svx/svdotext.hxx>
25 #include <basegfx/vector/b2dvector.hxx>
26 #include <sdr/primitive2d/sdrtextprimitive2d.hxx>
27 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
28 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
29 #include <basegfx/range/b2drange.hxx>
30 #include <editeng/editstat.hxx>
31 #include <tools/helpers.hxx>
32 #include <svx/sdtfchim.hxx>
33 #include <svl/itemset.hxx>
34 #include <basegfx/polygon/b2dpolygontools.hxx>
35 #include <basegfx/polygon/b2dpolygon.hxx>
36 #include <drawinglayer/animation/animationtiming.hxx>
37 #include <basegfx/color/bcolor.hxx>
38 #include <vcl/svapp.hxx>
39 #include <editeng/eeitemid.hxx>
40 #include <editeng/escapementitem.hxx>
41 #include <editeng/svxenum.hxx>
42 #include <editeng/flditem.hxx>
43 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
44 #include <vcl/metaact.hxx>
45 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
46 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
47 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
48 #include <svx/unoapi.hxx>
49 #include <drawinglayer/geometry/viewinformation2d.hxx>
50 #include <editeng/outlobj.hxx>
51 #include <basegfx/matrix/b2dhommatrixtools.hxx>
53 using namespace com::sun::star;
55 // helpers
57 namespace
59 drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector)
61 const sal_Int32 nCount(rPrimitiveVector.size());
62 drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount);
64 for(sal_Int32 a(0L); a < nCount; a++)
66 aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]);
69 return aRetval;
72 class impTextBreakupHandler
74 private:
75 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maTextPortionPrimitives;
76 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maLinePrimitives;
77 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maParagraphPrimitives;
79 SdrOutliner& mrOutliner;
80 basegfx::B2DHomMatrix maNewTransformA;
81 basegfx::B2DHomMatrix maNewTransformB;
83 // the visible area for contour text decomposition
84 basegfx::B2DVector maScale;
86 // ClipRange for BlockText decomposition; only text portions completely
87 // inside are to be accepted, so this is different from geometric clipping
88 // (which would allow e.g. upper parts of portions to remain). Only used for
89 // BlockText (see there)
90 basegfx::B2DRange maClipRange;
92 DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* );
93 DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* );
94 DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* );
96 DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* );
97 DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* );
98 DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* );
100 static bool impIsUnderlineAbove(const vcl::Font& rFont);
101 void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
102 static drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo);
103 void impFlushTextPortionPrimitivesToLinePrimitives();
104 void impFlushLinePrimitivesToParagraphPrimitives();
105 void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
106 void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
108 public:
109 impTextBreakupHandler(SdrOutliner& rOutliner)
110 : maTextPortionPrimitives(),
111 maLinePrimitives(),
112 maParagraphPrimitives(),
113 mrOutliner(rOutliner),
114 maNewTransformA(),
115 maNewTransformB(),
116 maScale(),
117 maClipRange()
121 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
123 maScale = rScale;
124 maNewTransformA = rNewTransformA;
125 maNewTransformB = rNewTransformB;
126 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
127 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
128 mrOutliner.StripPortions();
129 mrOutliner.SetDrawPortionHdl(Link<>());
130 mrOutliner.SetDrawBulletHdl(Link<>());
133 void decomposeBlockTextPrimitive(
134 const basegfx::B2DHomMatrix& rNewTransformA,
135 const basegfx::B2DHomMatrix& rNewTransformB,
136 const basegfx::B2DRange& rClipRange)
138 maNewTransformA = rNewTransformA;
139 maNewTransformB = rNewTransformB;
140 maClipRange = rClipRange;
141 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
142 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
143 mrOutliner.StripPortions();
144 mrOutliner.SetDrawPortionHdl(Link<>());
145 mrOutliner.SetDrawBulletHdl(Link<>());
148 void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
150 maNewTransformA = rNewTransformA;
151 maNewTransformB = rNewTransformB;
152 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
153 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
154 mrOutliner.StripPortions();
155 mrOutliner.SetDrawPortionHdl(Link<>());
156 mrOutliner.SetDrawBulletHdl(Link<>());
159 drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence();
162 bool impTextBreakupHandler::impIsUnderlineAbove(const vcl::Font& rFont)
164 if(!rFont.IsVertical())
166 return false;
169 if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
171 // the underline is right for Japanese only
172 return true;
175 return false;
178 void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
180 if(!rInfo.maText.isEmpty() && rInfo.mnTextLen)
182 OUString caseMappedText = rInfo.mrFont.CalcCaseMap( rInfo.maText );
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;
192 // add font scale to new transform
193 aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
195 // look for proportional font scaling, if necessary, scale accordingly
196 if(100 != rInfo.mrFont.GetPropr())
198 const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
199 aNewTransform.scale(fFactor, fFactor);
202 // apply font rotate
203 if(rInfo.mrFont.GetOrientation())
205 aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800);
208 // look for escapement, if necessary, translate accordingly
209 if(rInfo.mrFont.GetEscapement())
211 sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
213 if(DFLT_ESC_AUTO_SUPER == nEsc)
215 nEsc = 33;
217 else if(DFLT_ESC_AUTO_SUB == nEsc)
219 nEsc = -20;
222 if(nEsc > 100)
224 nEsc = 100;
226 else if(nEsc < -100)
228 nEsc = -100;
231 const double fEscapement(nEsc / -100.0);
232 aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
235 // apply transformA
236 aNewTransform *= maNewTransformA;
238 // apply local offset
239 aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
241 // also apply embedding object's transform
242 aNewTransform *= maNewTransformB;
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);
249 if(!bDisableTextArray && rInfo.mpDXArray && rInfo.mnTextLen)
251 aDXArray.reserve(rInfo.mnTextLen);
253 for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
255 aDXArray.push_back((double)rInfo.mpDXArray[a]);
259 // create complex text primitive and append
260 const Color aFontColor(rInfo.mrFont.GetColor());
261 const basegfx::BColor aBFontColor(aFontColor.getBColor());
263 const Color aTextFillColor(rInfo.mrFont.GetFillColor());
265 // prepare wordLineMode (for underline and strikeout)
266 // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
267 // to be split which would not look like the original
268 const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
270 // prepare new primitive
271 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = 0;
272 const bool bDecoratedIsNeeded(
273 UNDERLINE_NONE != rInfo.mrFont.GetOverline()
274 || UNDERLINE_NONE != rInfo.mrFont.GetUnderline()
275 || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
276 || EMPHASISMARK_NONE != (rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
277 || RELIEF_NONE != rInfo.mrFont.GetRelief()
278 || rInfo.mrFont.IsShadow()
279 || bWordLineMode);
281 if(bDecoratedIsNeeded)
283 // TextDecoratedPortionPrimitive2D needed, prepare some more data
284 // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
285 const Color aUnderlineColor(rInfo.maTextLineColor);
286 const basegfx::BColor aBUnderlineColor((0xffffffff == aUnderlineColor.GetColor()) ? aBFontColor : aUnderlineColor.getBColor());
287 const Color aOverlineColor(rInfo.maOverlineColor);
288 const basegfx::BColor aBOverlineColor((0xffffffff == aOverlineColor.GetColor()) ? aBFontColor : aOverlineColor.getBColor());
290 // prepare overline and underline data
291 const drawinglayer::primitive2d::TextLine eFontOverline(
292 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetOverline()));
293 const drawinglayer::primitive2d::TextLine eFontUnderline(
294 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetUnderline()));
296 // check UnderlineAbove
297 const bool bUnderlineAbove(
298 drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont));
300 // prepare strikeout data
301 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
302 drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
304 // prepare emphasis mark data
305 drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
307 switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
309 case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
310 case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
311 case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
312 case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
315 const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
316 const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
318 // prepare font relief data
319 drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
321 switch(rInfo.mrFont.GetRelief())
323 case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
324 case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
325 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
328 // prepare shadow/outline data
329 const bool bShadow(rInfo.mrFont.IsShadow());
331 // TextDecoratedPortionPrimitive2D is needed, create one
332 pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
334 // attributes for TextSimplePortionPrimitive2D
335 aNewTransform,
336 caseMappedText,
337 rInfo.mnTextStart,
338 rInfo.mnTextLen,
339 aDXArray,
340 aFontAttribute,
341 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
342 aBFontColor,
343 aTextFillColor,
345 // attributes for TextDecoratedPortionPrimitive2D
346 aBOverlineColor,
347 aBUnderlineColor,
348 eFontOverline,
349 eFontUnderline,
350 bUnderlineAbove,
351 eTextStrikeout,
352 bWordLineMode,
353 eTextEmphasisMark,
354 bEmphasisMarkAbove,
355 bEmphasisMarkBelow,
356 eTextRelief,
357 bShadow);
359 else
361 // TextSimplePortionPrimitive2D is enough
362 pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
363 aNewTransform,
364 caseMappedText,
365 rInfo.mnTextStart,
366 rInfo.mnTextLen,
367 aDXArray,
368 aFontAttribute,
369 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
370 aBFontColor,
371 rInfo.mbFilled,
372 rInfo.mnWidthToFill,
373 aTextFillColor);
376 if(rInfo.mbEndOfBullet)
378 // embed in TextHierarchyBulletPrimitive2D
379 const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
380 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
381 pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
384 if(rInfo.mpFieldData)
386 pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo);
389 maTextPortionPrimitives.push_back(pNewPrimitive);
391 // support for WrongSpellVector. Create WrongSpellPrimitives as needed
392 if(rInfo.mpWrongSpellVector && !aDXArray.empty())
394 const sal_Int32 nSize(rInfo.mpWrongSpellVector->size());
395 const sal_Int32 nDXCount(aDXArray.size());
396 const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
398 for(sal_Int32 a(0); a < nSize; a++)
400 const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
402 if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
404 const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart);
405 const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
406 double fStart(0.0);
407 double fEnd(0.0);
409 if(nStart > 0 && nStart - 1 < nDXCount)
411 fStart = aDXArray[nStart - 1];
414 if(nEnd > 0 && nEnd - 1 < nDXCount)
416 fEnd = aDXArray[nEnd - 1];
419 if(!basegfx::fTools::equal(fStart, fEnd))
421 if(rInfo.IsRTL())
423 // #i98523#
424 // When the portion is RTL, mirror the redlining using the
425 // full portion width
426 const double fTextWidth(aDXArray[aDXArray.size() - 1]);
428 fStart = fTextWidth - fStart;
429 fEnd = fTextWidth - fEnd;
432 // need to take FontScaling out of values; it's already part of
433 // aNewTransform and would be double applied
434 const double fFontScaleX(aFontScaling.getX());
436 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
437 && !basegfx::fTools::equalZero(fFontScaleX))
439 fStart /= fFontScaleX;
440 fEnd /= fFontScaleX;
443 maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
444 aNewTransform,
445 fStart,
446 fEnd,
447 aSpellColor));
455 drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo)
457 if(rInfo.mpFieldData)
459 // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
460 // which holds the field type and, if applicable, the URL
461 const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
462 const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
464 // embed current primitive to a sequence
465 drawinglayer::primitive2d::Primitive2DSequence aSequence;
467 if(pPrimitive)
469 aSequence.realloc(1);
470 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
473 if(pURLField)
475 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
477 else if(pPageField)
479 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, "");
481 else
483 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, "");
487 return pPrimitive;
490 void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
492 // only create a line primitive when we had content; there is no need for
493 // empty line primitives (contrary to paragraphs, see below).
494 if(!maTextPortionPrimitives.empty())
496 drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives));
497 maTextPortionPrimitives.clear();
498 maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence));
502 void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives()
504 // ALWAYS create a paragraph primitive, even when no content was added. This is done to
505 // have the correct paragraph count even with empty paragraphs. Those paragraphs will
506 // have an empty sub-PrimitiveSequence.
507 drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives));
508 maLinePrimitives.clear();
509 maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence));
512 void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
514 impCreateTextPortionPrimitive(rInfo);
516 if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
518 impFlushTextPortionPrimitivesToLinePrimitives();
521 if(rInfo.mbEndOfParagraph)
523 impFlushLinePrimitivesToParagraphPrimitives();
527 void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
529 basegfx::B2DHomMatrix aNewTransform;
531 // add size to new transform
532 aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
534 // apply transformA
535 aNewTransform *= maNewTransformA;
537 // apply local offset
538 aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
540 // also apply embedding object's transform
541 aNewTransform *= maNewTransformB;
543 // prepare empty GraphicAttr
544 const GraphicAttr aGraphicAttr;
546 // create GraphicPrimitive2D
547 const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
548 aNewTransform,
549 rInfo.maBulletGraphicObject,
550 aGraphicAttr));
552 // embed in TextHierarchyBulletPrimitive2D
553 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
554 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
556 // add to output
557 maTextPortionPrimitives.push_back(pNewPrimitive);
560 IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo)
562 // for contour text, ignore (clip away) all portions which are below
563 // the visible area given by maScale
564 if(pInfo && (double)pInfo->mrStartPos.Y() < maScale.getY())
566 impHandleDrawPortionInfo(*pInfo);
569 return 0;
572 IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo)
574 if(pInfo)
576 // Is clipping wanted? This is text clipping; only accept a portion
577 // if it's completely in the range
578 if(!maClipRange.isEmpty())
580 // Test start position first; this allows to not get the text range at
581 // all if text is far outside
582 const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
584 if(!maClipRange.isInside(aStartPosition))
586 return 0;
589 // Start position is inside. Get TextBoundRect and TopLeft next
590 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
591 aTextLayouterDevice.setFont(pInfo->mrFont);
593 const basegfx::B2DRange aTextBoundRect(
594 aTextLayouterDevice.getTextBoundRect(
595 pInfo->maText, pInfo->mnTextStart, pInfo->mnTextLen));
596 const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
598 if(!maClipRange.isInside(aTopLeft))
600 return 0;
603 // TopLeft is inside. Get BottomRight and check
604 const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
606 if(!maClipRange.isInside(aBottomRight))
608 return 0;
611 // all inside, clip was successful
613 impHandleDrawPortionInfo(*pInfo);
616 return 0;
619 IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo)
621 if(pInfo)
623 impHandleDrawPortionInfo(*pInfo);
626 return 0;
629 IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo)
631 if(pInfo)
633 impHandleDrawBulletInfo(*pInfo);
636 return 0;
639 IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo)
641 if(pInfo)
643 impHandleDrawBulletInfo(*pInfo);
646 return 0;
649 IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo)
651 if(pInfo)
653 impHandleDrawBulletInfo(*pInfo);
656 return 0;
659 drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence()
661 if(!maTextPortionPrimitives.empty())
663 // collect non-closed lines
664 impFlushTextPortionPrimitivesToLinePrimitives();
667 if(!maLinePrimitives.empty())
669 // collect non-closed paragraphs
670 impFlushLinePrimitivesToParagraphPrimitives();
673 return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives);
675 } // end of anonymous namespace
678 // primitive decompositions
680 void SdrTextObj::impDecomposeContourTextPrimitive(
681 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
682 const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
683 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
685 // decompose matrix to have position and size of text
686 basegfx::B2DVector aScale, aTranslate;
687 double fRotate, fShearX;
688 rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
690 // prepare contour polygon, force to non-mirrored for laying out
691 basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
692 aPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
694 // prepare outliner
695 SdrOutliner& rOutliner = ImpGetDrawOutliner();
696 const Size aNullSize;
697 rOutliner.SetPaperSize(aNullSize);
698 rOutliner.SetPolygon(aPolyPolygon);
699 rOutliner.SetUpdateMode(true);
700 rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
702 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
703 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
705 // prepare matrices to apply to newly created primitives
706 basegfx::B2DHomMatrix aNewTransformA;
708 // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
709 // move the null point which was top left to bottom right.
710 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
711 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
713 // in-between the translations of the single primitives will take place. Afterwards,
714 // the object's transformations need to be applied
715 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
716 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
717 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
719 // now break up text primitives.
720 impTextBreakupHandler aConverter(rOutliner);
721 aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
723 // cleanup outliner
724 rOutliner.Clear();
725 rOutliner.setVisualizedPage(0);
727 rTarget = aConverter.getPrimitive2DSequence();
730 void SdrTextObj::impDecomposeAutoFitTextPrimitive(
731 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
732 const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
733 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
735 // decompose matrix to have position and size of text
736 basegfx::B2DVector aScale, aTranslate;
737 double fRotate, fShearX;
738 rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
740 // use B2DRange aAnchorTextRange for calculations
741 basegfx::B2DRange aAnchorTextRange(aTranslate);
742 aAnchorTextRange.expand(aTranslate + aScale);
744 // prepare outliner
745 const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
746 SdrOutliner& rOutliner = ImpGetDrawOutliner();
747 SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
748 SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
749 const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
750 const Size aNullSize;
752 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
753 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
755 rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE|EEControlBits::STRETCHING);
756 rOutliner.SetMinAutoPaperSize(aNullSize);
757 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
759 // add one to rage sizes to get back to the old Rectangle and outliner measurements
760 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
761 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
762 const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
763 OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
764 const bool bVerticalWritintg(pOutlinerParaObject->IsVertical());
765 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
767 if((rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame()))
769 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
772 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg)
774 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
777 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg)
779 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
782 rOutliner.SetPaperSize(aNullSize);
783 rOutliner.SetUpdateMode(true);
784 rOutliner.SetText(*pOutlinerParaObject);
785 ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWritintg);
787 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
788 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
790 // now get back the layouted text size from outliner
791 const Size aOutlinerTextSize(rOutliner.GetPaperSize());
792 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
793 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
795 // correct horizontal translation using the now known text size
796 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
798 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
800 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
802 aAdjustTranslate.setX(fFree / 2.0);
805 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
807 aAdjustTranslate.setX(fFree);
811 // correct vertical translation using the now known text size
812 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
814 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
816 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
818 aAdjustTranslate.setY(fFree / 2.0);
821 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
823 aAdjustTranslate.setY(fFree);
827 // prepare matrices to apply to newly created primitives. aNewTransformA
828 // will get coordinates in aOutlinerScale size and positive in X, Y.
829 basegfx::B2DHomMatrix aNewTransformA;
830 basegfx::B2DHomMatrix aNewTransformB;
832 // translate relative to given primitive to get same rotation and shear
833 // as the master shape we are working on. For vertical, use the top-right
834 // corner
835 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
836 aNewTransformA.translate(fStartInX, aAdjustTranslate.getY());
838 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
839 // move the null point which was top left to bottom right.
840 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
841 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
842 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
844 // in-between the translations of the single primitives will take place. Afterwards,
845 // the object's transformations need to be applied
846 aNewTransformB.shearX(fShearX);
847 aNewTransformB.rotate(fRotate);
848 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
850 basegfx::B2DRange aClipRange;
852 // now break up text primitives.
853 impTextBreakupHandler aConverter(rOutliner);
854 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
856 // cleanup outliner
857 rOutliner.Clear();
858 rOutliner.setVisualizedPage(0);
859 rOutliner.SetControlWord(nOriginalControlWord);
861 rTarget = aConverter.getPrimitive2DSequence();
864 void SdrTextObj::impDecomposeBlockTextPrimitive(
865 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
866 const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
867 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
869 // decompose matrix to have position and size of text
870 basegfx::B2DVector aScale, aTranslate;
871 double fRotate, fShearX;
872 rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
874 // use B2DRange aAnchorTextRange for calculations
875 basegfx::B2DRange aAnchorTextRange(aTranslate);
876 aAnchorTextRange.expand(aTranslate + aScale);
878 // prepare outliner
879 const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
880 SdrOutliner& rOutliner = ImpGetDrawOutliner();
881 SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
882 SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
883 const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
884 const Size aNullSize;
886 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
887 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
888 rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
889 rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::AUTOPAGESIZE);
890 rOutliner.SetMinAutoPaperSize(aNullSize);
891 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
893 // Resolves: fdo#35779 set background color of this shape as the editeng background if there
894 // is one. Check the shape itself, then the host page, then that page's master page.
895 // That color needs to be restored on leaving this method
896 Color aOriginalBackColor(rOutliner.GetBackgroundColor());
897 const SfxItemSet* pBackgroundFillSet = &GetObjectItemSet();
899 if (drawing::FillStyle_NONE == static_cast<const XFillStyleItem&>(pBackgroundFillSet->Get(XATTR_FILLSTYLE)).GetValue())
901 SdrPage *pOwnerPage = GetPage();
902 if (pOwnerPage)
904 pBackgroundFillSet = &pOwnerPage->getSdrPageProperties().GetItemSet();
906 if (drawing::FillStyle_NONE == static_cast<const XFillStyleItem&>(pBackgroundFillSet->Get(XATTR_FILLSTYLE)).GetValue())
908 if (!pOwnerPage->IsMasterPage() && pOwnerPage->TRG_HasMasterPage())
910 pBackgroundFillSet = &pOwnerPage->TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
916 if (drawing::FillStyle_NONE != static_cast<const XFillStyleItem&>(pBackgroundFillSet->Get(XATTR_FILLSTYLE)).GetValue())
918 Color aColor(rOutliner.GetBackgroundColor());
919 GetDraftFillColor(*pBackgroundFillSet, aColor);
920 rOutliner.SetBackgroundColor(aColor);
923 // add one to rage sizes to get back to the old Rectangle and outliner measurements
924 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
925 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
926 const bool bVerticalWritintg(rSdrBlockTextPrimitive.getOutlinerParaObject().IsVertical());
927 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
929 if(bIsCell)
931 // cell text is formatted neither like a text object nor like a object
932 // text, so use a special setup here
933 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
935 // #i106214# To work with an unchangeable PaperSize (CellSize in
936 // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
937 // #i106214# This was not completely correct; to still measure the real
938 // text height to allow vertical adjust (and vice versa for VerticalWritintg)
939 // only one aspect has to be set, but the other one to zero
940 if(bVerticalWritintg)
942 // measure the horizontal text size
943 rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
945 else
947 // measure the vertical text size
948 rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
951 rOutliner.SetPaperSize(aAnchorTextSize);
952 rOutliner.SetUpdateMode(true);
953 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
955 else
957 // check if block text is used (only one of them can be true)
958 const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg);
959 const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg);
961 // set minimal paper size horizontally/vertically if needed
962 if(bHorizontalIsBlock)
964 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
966 else if(bVerticalIsBlock)
968 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
971 if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
973 // #i103454# maximal paper size hor/ver needs to be limited to text
974 // frame size. If it's block text, still allow the 'other' direction
975 // to grow to get a correct real text size when using GetPaperSize().
976 // When just using aAnchorTextSize as maximum, GetPaperSize()
977 // would just return aAnchorTextSize again: this means, the wanted
978 // 'measurement' of the real size of block text would not work
979 Size aMaxAutoPaperSize(aAnchorTextSize);
981 if(bHorizontalIsBlock)
983 // allow to grow vertical for horizontal blocks
984 aMaxAutoPaperSize.setHeight(1000000);
986 else if(bVerticalIsBlock)
988 // allow to grow horizontal for vertical blocks
989 aMaxAutoPaperSize.setWidth(1000000);
992 rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
995 rOutliner.SetPaperSize(aNullSize);
996 rOutliner.SetUpdateMode(true);
997 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
1000 rOutliner.SetControlWord(nOriginalControlWord);
1002 // now get back the layouted text size from outliner
1003 const Size aOutlinerTextSize(rOutliner.GetPaperSize());
1004 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
1005 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
1007 // For draw objects containing text correct hor/ver alignment if text is bigger
1008 // than the object itself. Without that correction, the text would always be
1009 // formatted to the left edge (or top edge when vertical) of the draw object.
1010 if(!IsTextFrame() && !bIsCell)
1012 if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg)
1014 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
1015 // else the alignment is wanted.
1016 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
1018 eHAdj = SDRTEXTHORZADJUST_CENTER;
1022 if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg)
1024 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
1025 // else the alignment is wanted.
1026 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
1028 eVAdj = SDRTEXTVERTADJUST_CENTER;
1033 // correct horizontal translation using the now known text size
1034 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
1036 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
1038 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
1040 aAdjustTranslate.setX(fFree / 2.0);
1043 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
1045 aAdjustTranslate.setX(fFree);
1049 // correct vertical translation using the now known text size
1050 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1052 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
1054 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
1056 aAdjustTranslate.setY(fFree / 2.0);
1059 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1061 aAdjustTranslate.setY(fFree);
1065 // prepare matrices to apply to newly created primitives. aNewTransformA
1066 // will get coordinates in aOutlinerScale size and positive in X, Y.
1067 // Translate relative to given primitive to get same rotation and shear
1068 // as the master shape we are working on. For vertical, use the top-right
1069 // corner
1070 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
1071 const basegfx::B2DTuple aAdjOffset(fStartInX, aAdjustTranslate.getY());
1072 basegfx::B2DHomMatrix aNewTransformA(basegfx::tools::createTranslateB2DHomMatrix(aAdjOffset.getX(), aAdjOffset.getY()));
1074 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1075 // move the null point which was top left to bottom right.
1076 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1077 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1079 // in-between the translations of the single primitives will take place. Afterwards,
1080 // the object's transformations need to be applied
1081 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1082 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1083 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1085 // create ClipRange (if needed)
1086 basegfx::B2DRange aClipRange;
1088 if(rSdrBlockTextPrimitive.getClipOnBounds())
1090 aClipRange.expand(-aAdjOffset);
1091 aClipRange.expand(basegfx::B2DTuple(aAnchorTextSize.Width(), aAnchorTextSize.Height()) - aAdjOffset);
1094 // now break up text primitives.
1095 impTextBreakupHandler aConverter(rOutliner);
1096 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
1098 // cleanup outliner
1099 rOutliner.SetBackgroundColor(aOriginalBackColor);
1100 rOutliner.Clear();
1101 rOutliner.setVisualizedPage(0);
1103 rTarget = aConverter.getPrimitive2DSequence();
1106 void SdrTextObj::impDecomposeStretchTextPrimitive(
1107 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1108 const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
1109 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
1111 // decompose matrix to have position and size of text
1112 basegfx::B2DVector aScale, aTranslate;
1113 double fRotate, fShearX;
1114 rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
1116 // use non-mirrored B2DRange aAnchorTextRange for calculations
1117 basegfx::B2DRange aAnchorTextRange(aTranslate);
1118 aAnchorTextRange.expand(aTranslate + aScale);
1120 // prepare outliner
1121 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1122 const EEControlBits nOriginalControlWord(rOutliner.GetControlWord());
1123 const Size aNullSize;
1125 rOutliner.SetControlWord(nOriginalControlWord|EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
1126 rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
1127 rOutliner.SetMinAutoPaperSize(aNullSize);
1128 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
1129 rOutliner.SetPaperSize(aNullSize);
1130 rOutliner.SetUpdateMode(true);
1131 rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
1133 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1134 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1136 // now get back the laid out text size from outliner
1137 const Size aOutlinerTextSize(rOutliner.CalcTextSize());
1138 const basegfx::B2DVector aOutlinerScale(
1139 basegfx::fTools::equalZero(aOutlinerTextSize.Width()) ? 1.0 : aOutlinerTextSize.Width(),
1140 basegfx::fTools::equalZero(aOutlinerTextSize.Height()) ? 1.0 : aOutlinerTextSize.Height());
1142 // prepare matrices to apply to newly created primitives
1143 basegfx::B2DHomMatrix aNewTransformA;
1145 // #i101957# Check for vertical text. If used, aNewTransformA
1146 // needs to translate the text initially around object width to orient
1147 // it relative to the topper right instead of the topper left
1148 const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsVertical());
1150 if(bVertical)
1152 aNewTransformA.translate(aScale.getX(), 0.0);
1155 // calculate global char stretching scale parameters. Use non-mirrored sizes
1156 // to layout without mirroring
1157 const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
1158 const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
1159 rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0));
1161 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1162 // move the null point which was top left to bottom right.
1163 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1164 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1166 // in-between the translations of the single primitives will take place. Afterwards,
1167 // the object's transformations need to be applied
1168 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1169 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1170 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1172 // now break up text primitives.
1173 impTextBreakupHandler aConverter(rOutliner);
1174 aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
1176 // cleanup outliner
1177 rOutliner.SetControlWord(nOriginalControlWord);
1178 rOutliner.Clear();
1179 rOutliner.setVisualizedPage(0);
1181 rTarget = aConverter.getPrimitive2DSequence();
1185 // timing generators
1186 #define ENDLESS_LOOP (0xffffffff)
1187 #define ENDLESS_TIME ((double)0xffffffff)
1188 #define PIXEL_DPI (96.0)
1190 void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
1192 if(SDRTEXTANI_BLINK == GetTextAniKind())
1194 // get values
1195 const SfxItemSet& rSet = GetObjectItemSet();
1196 const sal_uInt32 nRepeat((sal_uInt32)static_cast<const SdrTextAniCountItem&>(rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1197 double fDelay((double)static_cast<const SdrTextAniDelayItem&>(rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1199 if(0.0 == fDelay)
1201 // use default
1202 fDelay = 250.0;
1205 // prepare loop and add
1206 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1207 drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0);
1208 aLoop.append(aStart);
1209 drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0);
1210 aLoop.append(aEnd);
1211 rAnimList.append(aLoop);
1213 // add stopped state if loop is not endless
1214 if(0L != nRepeat)
1216 bool bVisisbleWhenStopped(static_cast<const SdrTextAniStopInsideItem&>(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1217 drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisisbleWhenStopped ? 0.0 : 1.0);
1218 rAnimList.append(aStop);
1223 void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1225 bool bVisisbleWhenStopped(static_cast<const SdrTextAniStopInsideItem&>(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1226 bool bVisisbleWhenStarted(static_cast<const SdrTextAniStartInsideItem&>(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE)).GetValue());
1227 const sal_uInt32 nRepeat(static_cast<const SdrTextAniCountItem&>(rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1229 if(bVisisbleWhenStarted)
1231 // move from center to outside
1232 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1233 rAnimList.append(aInOut);
1236 // loop. In loop, move through
1237 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1238 drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
1239 aLoop.append(aThrough);
1240 rAnimList.append(aLoop);
1242 if(0L != nRepeat && bVisisbleWhenStopped)
1244 // move from outside to center
1245 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1246 rAnimList.append(aOutIn);
1248 // add timing for staying at the end
1249 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1250 rAnimList.append(aEnd);
1254 void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
1256 if(basegfx::fTools::more(fRelativeTextLength, 0.5))
1258 // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
1259 // In that case, correct direction
1260 bForward = !bForward;
1263 const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
1264 const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
1265 bool bVisisbleWhenStarted(static_cast<const SdrTextAniStartInsideItem&>(rSet.Get(SDRATTR_TEXT_ANISTARTINSIDE)).GetValue());
1266 const sal_uInt32 nRepeat(static_cast<const SdrTextAniCountItem&>(rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1268 if(!bVisisbleWhenStarted)
1270 // move from outside to center
1271 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1272 rAnimList.append(aOutIn);
1275 // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
1276 // so use absolute value
1277 const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
1278 const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
1279 const double fHalfInnerPath(fTimeForInnerPath * 0.5);
1280 const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
1282 if(nDoubleRepeat || 0L == nRepeat)
1284 // double forth and back loop
1285 drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
1286 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1287 aLoop.append(aTime0);
1288 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
1289 aLoop.append(aTime1);
1290 drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
1291 aLoop.append(aTime2);
1292 rAnimList.append(aLoop);
1295 if(nRepeat % 2L)
1297 // repeat is uneven, so we need one more forth and back to center
1298 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1299 rAnimList.append(aTime0);
1300 drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
1301 rAnimList.append(aTime1);
1304 if(0L != nRepeat)
1306 bool bVisisbleWhenStopped(static_cast<const SdrTextAniStopInsideItem&>(rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1307 if(bVisisbleWhenStopped)
1309 // add timing for staying at the end
1310 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1311 rAnimList.append(aEnd);
1313 else
1315 // move from center to outside
1316 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1317 rAnimList.append(aInOut);
1322 void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1324 // move in from outside, start outside
1325 const double fStartPosition(bForward ? 0.0 : 1.0);
1326 const sal_uInt32 nRepeat(static_cast<const SdrTextAniCountItem&>(rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1328 // move from outside to center
1329 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1330 rAnimList.append(aOutIn);
1332 // loop. In loop, move out and in again
1333 if(nRepeat > 1L || 0L == nRepeat)
1335 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1L : ENDLESS_LOOP);
1336 drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
1337 aLoop.append(aTime0);
1338 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1339 aLoop.append(aTime1);
1340 rAnimList.append(aLoop);
1343 // always visible when stopped, so add timing for staying at the end when not endless
1344 if(0L != nRepeat)
1346 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1347 rAnimList.append(aEnd);
1351 void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
1353 const SdrTextAniKind eAniKind(GetTextAniKind());
1355 if(SDRTEXTANI_SCROLL == eAniKind || SDRTEXTANI_ALTERNATE == eAniKind || SDRTEXTANI_SLIDE == eAniKind)
1357 // get data. Goal is to calculate fTimeFullPath which is the time needed to
1358 // move animation from (0.0) to (1.0) state
1359 const SfxItemSet& rSet = GetObjectItemSet();
1360 double fAnimationDelay((double)static_cast<const SdrTextAniDelayItem&>(rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1361 double fSingleStepWidth((double)static_cast<const SdrTextAniAmountItem&>(rSet.Get(SDRATTR_TEXT_ANIAMOUNT)).GetValue());
1362 const SdrTextAniDirection eDirection(GetTextAniDirection());
1363 const bool bForward(SDRTEXTANI_RIGHT == eDirection || SDRTEXTANI_DOWN == eDirection);
1365 if(basegfx::fTools::equalZero(fAnimationDelay))
1367 // default to 1/20 second
1368 fAnimationDelay = 50.0;
1371 if(basegfx::fTools::less(fSingleStepWidth, 0.0))
1373 // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
1374 // It makes no sense to keep the view-transformation centered
1375 // definitions, so get rid of them here.
1376 fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
1379 if(basegfx::fTools::equalZero(fSingleStepWidth))
1381 // default to 1 millimeter
1382 fSingleStepWidth = 100.0;
1385 // use the length of the full animation path and the number of steps
1386 // to get the full path time
1387 const double fFullPathLength(fFrameLength + fTextLength);
1388 const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
1389 double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
1391 if(fTimeFullPath < fAnimationDelay)
1393 fTimeFullPath = fAnimationDelay;
1396 switch(eAniKind)
1398 case SDRTEXTANI_SCROLL :
1400 impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1401 break;
1403 case SDRTEXTANI_ALTERNATE :
1405 double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
1406 impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
1407 break;
1409 case SDRTEXTANI_SLIDE :
1411 impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1412 break;
1414 default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK
1420 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */