Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / svx / source / svdraw / svdotextdecomposition.cxx
blob8767e5d74fc7f182ad1d446c9592720c57f2717b
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>
54 // helpers
56 namespace
58 drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector)
60 const sal_Int32 nCount(rPrimitiveVector.size());
61 drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount);
63 for(sal_Int32 a(0L); a < nCount; a++)
65 aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]);
68 return aRetval;
71 class impTextBreakupHandler
73 private:
74 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maTextPortionPrimitives;
75 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maLinePrimitives;
76 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maParagraphPrimitives;
78 SdrOutliner& mrOutliner;
79 basegfx::B2DHomMatrix maNewTransformA;
80 basegfx::B2DHomMatrix maNewTransformB;
82 // the visible area for contour text decomposition
83 basegfx::B2DVector maScale;
85 // ClipRange for BlockText decomposition; only text portions completely
86 // inside are to be accepted, so this is different from geometric clipping
87 // (which would allow e.g. upper parts of portions to remain). Only used for
88 // BlockText (see there)
89 basegfx::B2DRange maClipRange;
91 DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* );
92 DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* );
93 DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* );
95 DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* );
96 DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* );
97 DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* );
99 bool impIsUnderlineAbove(const Font& rFont) const;
100 void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
101 drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const;
102 void impFlushTextPortionPrimitivesToLinePrimitives();
103 void impFlushLinePrimitivesToParagraphPrimitives();
104 void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
105 void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
107 public:
108 impTextBreakupHandler(SdrOutliner& rOutliner)
109 : maTextPortionPrimitives(),
110 maLinePrimitives(),
111 maParagraphPrimitives(),
112 mrOutliner(rOutliner),
113 maNewTransformA(),
114 maNewTransformB(),
115 maScale(),
116 maClipRange()
120 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
122 maScale = rScale;
123 maNewTransformA = rNewTransformA;
124 maNewTransformB = rNewTransformB;
125 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
126 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
127 mrOutliner.StripPortions();
128 mrOutliner.SetDrawPortionHdl(Link());
129 mrOutliner.SetDrawBulletHdl(Link());
132 void decomposeBlockTextPrimitive(
133 const basegfx::B2DHomMatrix& rNewTransformA,
134 const basegfx::B2DHomMatrix& rNewTransformB,
135 const basegfx::B2DRange& rClipRange)
137 maNewTransformA = rNewTransformA;
138 maNewTransformB = rNewTransformB;
139 maClipRange = rClipRange;
140 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
141 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
142 mrOutliner.StripPortions();
143 mrOutliner.SetDrawPortionHdl(Link());
144 mrOutliner.SetDrawBulletHdl(Link());
147 void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
149 maNewTransformA = rNewTransformA;
150 maNewTransformB = rNewTransformB;
151 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
152 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
153 mrOutliner.StripPortions();
154 mrOutliner.SetDrawPortionHdl(Link());
155 mrOutliner.SetDrawBulletHdl(Link());
158 drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence();
161 bool impTextBreakupHandler::impIsUnderlineAbove(const Font& rFont) const
163 if(!rFont.IsVertical())
165 return false;
168 if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
170 // the underline is right for Japanese only
171 return true;
174 return false;
177 void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
179 if(!rInfo.maText.isEmpty() && rInfo.mnTextLen)
181 OUString caseMappedText = rInfo.mrFont.CalcCaseMap( rInfo.maText );
182 basegfx::B2DVector aFontScaling;
183 drawinglayer::attribute::FontAttribute aFontAttribute(
184 drawinglayer::primitive2d::getFontAttributeFromVclFont(
185 aFontScaling,
186 rInfo.mrFont,
187 rInfo.IsRTL(),
188 false));
189 basegfx::B2DHomMatrix aNewTransform;
191 // add font scale to new transform
192 aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
194 // look for proportional font scaling, if necessary, scale accordingly
195 if(100 != rInfo.mrFont.GetPropr())
197 const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
198 aNewTransform.scale(fFactor, fFactor);
201 // apply font rotate
202 if(rInfo.mrFont.GetOrientation())
204 aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800);
207 // look for escapement, if necessary, translate accordingly
208 if(rInfo.mrFont.GetEscapement())
210 sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
212 if(DFLT_ESC_AUTO_SUPER == nEsc)
214 nEsc = 33;
216 else if(DFLT_ESC_AUTO_SUB == nEsc)
218 nEsc = -20;
221 if(nEsc > 100)
223 nEsc = 100;
225 else if(nEsc < -100)
227 nEsc = -100;
230 const double fEscapement(nEsc / -100.0);
231 aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
234 // apply transformA
235 aNewTransform *= maNewTransformA;
237 // apply local offset
238 aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
240 // also apply embedding object's transform
241 aNewTransform *= maNewTransformB;
243 // prepare DXArray content. To make it independent from font size (and such from
244 // the text transformation), scale it to unit coordinates
245 ::std::vector< double > aDXArray;
246 static bool bDisableTextArray(false);
248 if(!bDisableTextArray && rInfo.mpDXArray && rInfo.mnTextLen)
250 aDXArray.reserve(rInfo.mnTextLen);
252 for(sal_Int32 a=0; a < rInfo.mnTextLen; a++)
254 aDXArray.push_back((double)rInfo.mpDXArray[a]);
258 // create complex text primitive and append
259 const Color aFontColor(rInfo.mrFont.GetColor());
260 const basegfx::BColor aBFontColor(aFontColor.getBColor());
262 // prepare wordLineMode (for underline and strikeout)
263 // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
264 // to be split which would not look like the original
265 const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
267 // prepare new primitive
268 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = 0;
269 const bool bDecoratedIsNeeded(
270 UNDERLINE_NONE != rInfo.mrFont.GetOverline()
271 || UNDERLINE_NONE != rInfo.mrFont.GetUnderline()
272 || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
273 || EMPHASISMARK_NONE != (rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
274 || RELIEF_NONE != rInfo.mrFont.GetRelief()
275 || rInfo.mrFont.IsShadow()
276 || bWordLineMode);
278 if(bDecoratedIsNeeded)
280 // TextDecoratedPortionPrimitive2D needed, prepare some more data
281 // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
282 const Color aUnderlineColor(rInfo.maTextLineColor);
283 const basegfx::BColor aBUnderlineColor((0xffffffff == aUnderlineColor.GetColor()) ? aBFontColor : aUnderlineColor.getBColor());
284 const Color aOverlineColor(rInfo.maOverlineColor);
285 const basegfx::BColor aBOverlineColor((0xffffffff == aOverlineColor.GetColor()) ? aBFontColor : aOverlineColor.getBColor());
287 // prepare overline and underline data
288 const drawinglayer::primitive2d::TextLine eFontOverline(
289 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetOverline()));
290 const drawinglayer::primitive2d::TextLine eFontUnderline(
291 drawinglayer::primitive2d::mapFontUnderlineToTextLine(rInfo.mrFont.GetUnderline()));
293 // check UnderlineAbove
294 const bool bUnderlineAbove(
295 drawinglayer::primitive2d::TEXT_LINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont));
297 // prepare strikeout data
298 const drawinglayer::primitive2d::TextStrikeout eTextStrikeout(
299 drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo.mrFont.GetStrikeout()));
301 // prepare emphasis mark data
302 drawinglayer::primitive2d::TextEmphasisMark eTextEmphasisMark(drawinglayer::primitive2d::TEXT_EMPHASISMARK_NONE);
304 switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
306 case EMPHASISMARK_DOT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DOT; break;
307 case EMPHASISMARK_CIRCLE : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_CIRCLE; break;
308 case EMPHASISMARK_DISC : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_DISC; break;
309 case EMPHASISMARK_ACCENT : eTextEmphasisMark = drawinglayer::primitive2d::TEXT_EMPHASISMARK_ACCENT; break;
312 const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
313 const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
315 // prepare font relief data
316 drawinglayer::primitive2d::TextRelief eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE);
318 switch(rInfo.mrFont.GetRelief())
320 case RELIEF_EMBOSSED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED; break;
321 case RELIEF_ENGRAVED : eTextRelief = drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED; break;
322 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
325 // prepare shadow/outline data
326 const bool bShadow(rInfo.mrFont.IsShadow());
328 // TextDecoratedPortionPrimitive2D is needed, create one
329 pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
331 // attributes for TextSimplePortionPrimitive2D
332 aNewTransform,
333 caseMappedText,
334 rInfo.mnTextStart,
335 rInfo.mnTextLen,
336 aDXArray,
337 aFontAttribute,
338 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
339 aBFontColor,
341 // attributes for TextDecoratedPortionPrimitive2D
342 aBOverlineColor,
343 aBUnderlineColor,
344 eFontOverline,
345 eFontUnderline,
346 bUnderlineAbove,
347 eTextStrikeout,
348 bWordLineMode,
349 eTextEmphasisMark,
350 bEmphasisMarkAbove,
351 bEmphasisMarkBelow,
352 eTextRelief,
353 bShadow);
355 else
357 // TextSimplePortionPrimitive2D is enough
358 pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
359 aNewTransform,
360 caseMappedText,
361 rInfo.mnTextStart,
362 rInfo.mnTextLen,
363 aDXArray,
364 aFontAttribute,
365 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
366 aBFontColor,
367 rInfo.mbFilled,
368 rInfo.mnWidthToFill);
371 if(rInfo.mbEndOfBullet)
373 // embed in TextHierarchyBulletPrimitive2D
374 const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
375 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
376 pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
379 if(rInfo.mpFieldData)
381 pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo);
384 maTextPortionPrimitives.push_back(pNewPrimitive);
386 // support for WrongSpellVector. Create WrongSpellPrimitives as needed
387 if(rInfo.mpWrongSpellVector && !aDXArray.empty())
389 const sal_Int32 nSize(rInfo.mpWrongSpellVector->size());
390 const sal_Int32 nDXCount(aDXArray.size());
391 const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
393 for(sal_Int32 a(0); a < nSize; a++)
395 const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
397 if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
399 const sal_Int32 nStart(rCandidate.nStart - rInfo.mnTextStart);
400 const sal_Int32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
401 double fStart(0.0);
402 double fEnd(0.0);
404 if(nStart > 0 && nStart - 1 < nDXCount)
406 fStart = aDXArray[nStart - 1];
409 if(nEnd > 0 && nEnd - 1 < nDXCount)
411 fEnd = aDXArray[nEnd - 1];
414 if(!basegfx::fTools::equal(fStart, fEnd))
416 if(rInfo.IsRTL())
418 // #i98523#
419 // When the portion is RTL, mirror the redlining using the
420 // full portion width
421 const double fTextWidth(aDXArray[aDXArray.size() - 1]);
423 fStart = fTextWidth - fStart;
424 fEnd = fTextWidth - fEnd;
427 // need to take FontScaling out of values; it's already part of
428 // aNewTransform and would be double applied
429 const double fFontScaleX(aFontScaling.getX());
431 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
432 && !basegfx::fTools::equalZero(fFontScaleX))
434 fStart /= fFontScaleX;
435 fEnd /= fFontScaleX;
438 maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
439 aNewTransform,
440 fStart,
441 fEnd,
442 aSpellColor));
450 drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const
452 if(rInfo.mpFieldData)
454 // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
455 // which holds the field type and, if applicable, the URL
456 const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
457 const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
459 // embed current primitive to a sequence
460 drawinglayer::primitive2d::Primitive2DSequence aSequence;
462 if(pPrimitive)
464 aSequence.realloc(1);
465 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
468 if(pURLField)
470 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
472 else if(pPageField)
474 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, "");
476 else
478 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, "");
482 return pPrimitive;
485 void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
487 // only create a line primitive when we had content; there is no need for
488 // empty line primitives (contrary to paragraphs, see below).
489 if(!maTextPortionPrimitives.empty())
491 drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives));
492 maTextPortionPrimitives.clear();
493 maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence));
497 void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives()
499 // ALWAYS create a paragraph primitive, even when no content was added. This is done to
500 // have the correct paragraph count even with empty paragraphs. Those paragraphs will
501 // have an empty sub-PrimitiveSequence.
502 drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives));
503 maLinePrimitives.clear();
504 maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence));
507 void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
509 impCreateTextPortionPrimitive(rInfo);
511 if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
513 impFlushTextPortionPrimitivesToLinePrimitives();
516 if(rInfo.mbEndOfParagraph)
518 impFlushLinePrimitivesToParagraphPrimitives();
522 void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
524 basegfx::B2DHomMatrix aNewTransform;
526 // add size to new transform
527 aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
529 // apply transformA
530 aNewTransform *= maNewTransformA;
532 // apply local offset
533 aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
535 // also apply embedding object's transform
536 aNewTransform *= maNewTransformB;
538 // prepare empty GraphicAttr
539 const GraphicAttr aGraphicAttr;
541 // create GraphicPrimitive2D
542 const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
543 aNewTransform,
544 rInfo.maBulletGraphicObject,
545 aGraphicAttr));
547 // embed in TextHierarchyBulletPrimitive2D
548 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
549 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
551 // add to output
552 maTextPortionPrimitives.push_back(pNewPrimitive);
555 IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo)
557 // for contour text, ignore (clip away) all portions which are below
558 // the visible area given by maScale
559 if(pInfo && (double)pInfo->mrStartPos.Y() < maScale.getY())
561 impHandleDrawPortionInfo(*pInfo);
564 return 0;
567 IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo)
569 if(pInfo)
571 // Is clipping wanted? This is text clipping; only accept a portion
572 // if it's completely in the range
573 if(!maClipRange.isEmpty())
575 // Test start position first; this allows to not get the text range at
576 // all if text is far outside
577 const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
579 if(!maClipRange.isInside(aStartPosition))
581 return 0;
584 // Start position is inside. Get TextBoundRect and TopLeft next
585 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
586 aTextLayouterDevice.setFont(pInfo->mrFont);
588 const basegfx::B2DRange aTextBoundRect(
589 aTextLayouterDevice.getTextBoundRect(
590 pInfo->maText, pInfo->mnTextStart, pInfo->mnTextLen));
591 const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
593 if(!maClipRange.isInside(aTopLeft))
595 return 0;
598 // TopLeft is inside. Get BottomRight and check
599 const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
601 if(!maClipRange.isInside(aBottomRight))
603 return 0;
606 // all inside, clip was successful
608 impHandleDrawPortionInfo(*pInfo);
611 return 0;
614 IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo)
616 if(pInfo)
618 impHandleDrawPortionInfo(*pInfo);
621 return 0;
624 IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo)
626 if(pInfo)
628 impHandleDrawBulletInfo(*pInfo);
631 return 0;
634 IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo)
636 if(pInfo)
638 impHandleDrawBulletInfo(*pInfo);
641 return 0;
644 IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo)
646 if(pInfo)
648 impHandleDrawBulletInfo(*pInfo);
651 return 0;
654 drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence()
656 if(!maTextPortionPrimitives.empty())
658 // collect non-closed lines
659 impFlushTextPortionPrimitivesToLinePrimitives();
662 if(!maLinePrimitives.empty())
664 // collect non-closed paragraphs
665 impFlushLinePrimitivesToParagraphPrimitives();
668 return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives);
670 } // end of anonymous namespace
673 // primitive decompositions
675 void SdrTextObj::impDecomposeContourTextPrimitive(
676 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
677 const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
678 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
680 // decompose matrix to have position and size of text
681 basegfx::B2DVector aScale, aTranslate;
682 double fRotate, fShearX;
683 rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
685 // prepare contour polygon, force to non-mirrored for laying out
686 basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
687 aPolyPolygon.transform(basegfx::tools::createScaleB2DHomMatrix(fabs(aScale.getX()), fabs(aScale.getY())));
689 // prepare outliner
690 SdrOutliner& rOutliner = ImpGetDrawOutliner();
691 const Size aNullSize;
692 rOutliner.SetPaperSize(aNullSize);
693 rOutliner.SetPolygon(aPolyPolygon);
694 rOutliner.SetUpdateMode(true);
695 rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
697 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
698 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
700 // prepare matrices to apply to newly created primitives
701 basegfx::B2DHomMatrix aNewTransformA;
703 // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
704 // move the null point which was top left to bottom right.
705 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
706 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
708 // in-between the translations of the single primitives will take place. Afterwards,
709 // the object's transformations need to be applied
710 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
711 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
712 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
714 // now break up text primitives.
715 impTextBreakupHandler aConverter(rOutliner);
716 aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
718 // cleanup outliner
719 rOutliner.Clear();
720 rOutliner.setVisualizedPage(0);
722 rTarget = aConverter.getPrimitive2DSequence();
725 void SdrTextObj::impDecomposeAutoFitTextPrimitive(
726 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
727 const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
728 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
730 // decompose matrix to have position and size of text
731 basegfx::B2DVector aScale, aTranslate;
732 double fRotate, fShearX;
733 rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
735 // use B2DRange aAnchorTextRange for calculations
736 basegfx::B2DRange aAnchorTextRange(aTranslate);
737 aAnchorTextRange.expand(aTranslate + aScale);
739 // prepare outliner
740 const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
741 SdrOutliner& rOutliner = ImpGetDrawOutliner();
742 SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
743 SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
744 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
745 const Size aNullSize;
747 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
748 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
750 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE|EE_CNTRL_STRETCHING);
751 rOutliner.SetMinAutoPaperSize(aNullSize);
752 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
754 // add one to rage sizes to get back to the old Rectangle and outliner measurements
755 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
756 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
757 const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
758 OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
759 const bool bVerticalWritintg(pOutlinerParaObject->IsVertical());
760 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
762 if((rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame()))
764 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
767 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg)
769 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
772 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg)
774 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
777 rOutliner.SetPaperSize(aNullSize);
778 rOutliner.SetUpdateMode(true);
779 rOutliner.SetText(*pOutlinerParaObject);
780 ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWritintg);
782 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
783 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
785 // now get back the layouted text size from outliner
786 const Size aOutlinerTextSize(rOutliner.GetPaperSize());
787 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
788 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
790 // correct horizontal translation using the now known text size
791 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
793 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
795 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
797 aAdjustTranslate.setX(fFree / 2.0);
800 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
802 aAdjustTranslate.setX(fFree);
806 // correct vertical translation using the now known text size
807 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
809 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
811 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
813 aAdjustTranslate.setY(fFree / 2.0);
816 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
818 aAdjustTranslate.setY(fFree);
822 // prepare matrices to apply to newly created primitives. aNewTransformA
823 // will get coordinates in aOutlinerScale size and positive in X, Y.
824 basegfx::B2DHomMatrix aNewTransformA;
825 basegfx::B2DHomMatrix aNewTransformB;
827 // translate relative to given primitive to get same rotation and shear
828 // as the master shape we are working on. For vertical, use the top-right
829 // corner
830 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
831 aNewTransformA.translate(fStartInX, aAdjustTranslate.getY());
833 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
834 // move the null point which was top left to bottom right.
835 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
836 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
837 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
839 // in-between the translations of the single primitives will take place. Afterwards,
840 // the object's transformations need to be applied
841 aNewTransformB.shearX(fShearX);
842 aNewTransformB.rotate(fRotate);
843 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
845 basegfx::B2DRange aClipRange;
847 // now break up text primitives.
848 impTextBreakupHandler aConverter(rOutliner);
849 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
851 // cleanup outliner
852 rOutliner.Clear();
853 rOutliner.setVisualizedPage(0);
854 rOutliner.SetControlWord(nOriginalControlWord);
856 rTarget = aConverter.getPrimitive2DSequence();
859 void SdrTextObj::impDecomposeBlockTextPrimitive(
860 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
861 const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
862 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
864 // decompose matrix to have position and size of text
865 basegfx::B2DVector aScale, aTranslate;
866 double fRotate, fShearX;
867 rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
869 // use B2DRange aAnchorTextRange for calculations
870 basegfx::B2DRange aAnchorTextRange(aTranslate);
871 aAnchorTextRange.expand(aTranslate + aScale);
873 // prepare outliner
874 const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
875 SdrOutliner& rOutliner = ImpGetDrawOutliner();
876 SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
877 SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
878 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
879 const Size aNullSize;
881 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
882 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
883 rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
884 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE);
885 rOutliner.SetMinAutoPaperSize(aNullSize);
886 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
888 // Resolves: fdo#35779 set background color of this shape as the editeng background if there
889 // is one. Check the shape itself, then the host page, then that page's master page.
890 // That color needs to be restored on leaving this method
891 Color aOriginalBackColor(rOutliner.GetBackgroundColor());
892 const SfxItemSet* pBackgroundFillSet = &GetObjectItemSet();
894 if (XFILL_NONE == ((const XFillStyleItem&)pBackgroundFillSet->Get(XATTR_FILLSTYLE)).GetValue())
896 SdrPage *pOwnerPage = GetPage();
897 if (pOwnerPage)
899 pBackgroundFillSet = &pOwnerPage->getSdrPageProperties().GetItemSet();
901 if (XFILL_NONE == ((const XFillStyleItem&)pBackgroundFillSet->Get(XATTR_FILLSTYLE)).GetValue())
903 if (!pOwnerPage->IsMasterPage() && pOwnerPage->TRG_HasMasterPage())
905 pBackgroundFillSet = &pOwnerPage->TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
911 if (XFILL_NONE != ((const XFillStyleItem&)pBackgroundFillSet->Get(XATTR_FILLSTYLE)).GetValue())
913 Color aColor(rOutliner.GetBackgroundColor());
914 GetDraftFillColor(*pBackgroundFillSet, aColor);
915 rOutliner.SetBackgroundColor(aColor);
918 // add one to rage sizes to get back to the old Rectangle and outliner measurements
919 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
920 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
921 const bool bVerticalWritintg(rSdrBlockTextPrimitive.getOutlinerParaObject().IsVertical());
922 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
924 if(bIsCell)
926 // cell text is formated neither like a text object nor like a object
927 // text, so use a special setup here
928 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
930 // #i106214# To work with an unchangeable PaperSize (CellSize in
931 // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
932 // #i106214# This was not completely correct; to still measure the real
933 // text height to allow vertical adjust (and vice versa for VerticalWritintg)
934 // only one aspect has to be set, but the other one to zero
935 if(bVerticalWritintg)
937 // measure the horizontal text size
938 rOutliner.SetMinAutoPaperSize(Size(0, aAnchorTextSize.Height()));
940 else
942 // measure the vertical text size
943 rOutliner.SetMinAutoPaperSize(Size(aAnchorTextSize.Width(), 0));
946 rOutliner.SetPaperSize(aAnchorTextSize);
947 rOutliner.SetUpdateMode(true);
948 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
950 else
952 // check if block text is used (only one of them can be true)
953 const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg);
954 const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg);
956 // set minimal paper size horizontally/vertically if needed
957 if(bHorizontalIsBlock)
959 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
961 else if(bVerticalIsBlock)
963 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
966 if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
968 // #i103454# maximal paper size hor/ver needs to be limited to text
969 // frame size. If it's block text, still allow the 'other' direction
970 // to grow to get a correct real text size when using GetPaperSize().
971 // When just using aAnchorTextSize as maximum, GetPaperSize()
972 // would just return aAnchorTextSize again: this means, the wanted
973 // 'measurement' of the real size of block text would not work
974 Size aMaxAutoPaperSize(aAnchorTextSize);
976 if(bHorizontalIsBlock)
978 // allow to grow vertical for horizontal blocks
979 aMaxAutoPaperSize.setHeight(1000000);
981 else if(bVerticalIsBlock)
983 // allow to grow horizontal for vertical blocks
984 aMaxAutoPaperSize.setWidth(1000000);
987 rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
990 rOutliner.SetPaperSize(aNullSize);
991 rOutliner.SetUpdateMode(true);
992 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
995 rOutliner.SetControlWord(nOriginalControlWord);
997 // now get back the layouted text size from outliner
998 const Size aOutlinerTextSize(rOutliner.GetPaperSize());
999 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSize.Width(), aOutlinerTextSize.Height());
1000 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
1002 // For draw objects containing text correct hor/ver alignment if text is bigger
1003 // than the object itself. Without that correction, the text would always be
1004 // formatted to the left edge (or top edge when vertical) of the draw object.
1005 if(!IsTextFrame() && !bIsCell)
1007 if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg)
1009 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
1010 // else the alignment is wanted.
1011 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
1013 eHAdj = SDRTEXTHORZADJUST_CENTER;
1017 if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg)
1019 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
1020 // else the alignment is wanted.
1021 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
1023 eVAdj = SDRTEXTVERTADJUST_CENTER;
1028 // correct horizontal translation using the now known text size
1029 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
1031 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
1033 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
1035 aAdjustTranslate.setX(fFree / 2.0);
1038 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
1040 aAdjustTranslate.setX(fFree);
1044 // correct vertical translation using the now known text size
1045 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1047 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
1049 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
1051 aAdjustTranslate.setY(fFree / 2.0);
1054 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1056 aAdjustTranslate.setY(fFree);
1060 // prepare matrices to apply to newly created primitives. aNewTransformA
1061 // will get coordinates in aOutlinerScale size and positive in X, Y.
1062 // Translate relative to given primitive to get same rotation and shear
1063 // as the master shape we are working on. For vertical, use the top-right
1064 // corner
1065 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
1066 const basegfx::B2DTuple aAdjOffset(fStartInX, aAdjustTranslate.getY());
1067 basegfx::B2DHomMatrix aNewTransformA(basegfx::tools::createTranslateB2DHomMatrix(aAdjOffset.getX(), aAdjOffset.getY()));
1069 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1070 // move the null point which was top left to bottom right.
1071 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1072 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1074 // in-between the translations of the single primitives will take place. Afterwards,
1075 // the object's transformations need to be applied
1076 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1077 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1078 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1080 // create ClipRange (if needed)
1081 basegfx::B2DRange aClipRange;
1083 if(rSdrBlockTextPrimitive.getClipOnBounds())
1085 aClipRange.expand(-aAdjOffset);
1086 aClipRange.expand(basegfx::B2DTuple(aAnchorTextSize.Width(), aAnchorTextSize.Height()) - aAdjOffset);
1089 // now break up text primitives.
1090 impTextBreakupHandler aConverter(rOutliner);
1091 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
1093 // cleanup outliner
1094 rOutliner.SetBackgroundColor(aOriginalBackColor);
1095 rOutliner.Clear();
1096 rOutliner.setVisualizedPage(0);
1098 rTarget = aConverter.getPrimitive2DSequence();
1101 void SdrTextObj::impDecomposeStretchTextPrimitive(
1102 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1103 const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
1104 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
1106 // decompose matrix to have position and size of text
1107 basegfx::B2DVector aScale, aTranslate;
1108 double fRotate, fShearX;
1109 rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
1111 // use non-mirrored B2DRange aAnchorTextRange for calculations
1112 basegfx::B2DRange aAnchorTextRange(aTranslate);
1113 aAnchorTextRange.expand(aTranslate + aScale);
1115 // prepare outliner
1116 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1117 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
1118 const Size aNullSize;
1120 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_STRETCHING|EE_CNTRL_AUTOPAGESIZE);
1121 rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
1122 rOutliner.SetMinAutoPaperSize(aNullSize);
1123 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
1124 rOutliner.SetPaperSize(aNullSize);
1125 rOutliner.SetUpdateMode(true);
1126 rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
1128 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1129 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1131 // now get back the laid out text size from outliner
1132 const Size aOutlinerTextSize(rOutliner.CalcTextSize());
1133 const basegfx::B2DVector aOutlinerScale(
1134 basegfx::fTools::equalZero(aOutlinerTextSize.Width()) ? 1.0 : aOutlinerTextSize.Width(),
1135 basegfx::fTools::equalZero(aOutlinerTextSize.Height()) ? 1.0 : aOutlinerTextSize.Height());
1137 // prepare matrices to apply to newly created primitives
1138 basegfx::B2DHomMatrix aNewTransformA;
1140 // #i101957# Check for vertical text. If used, aNewTransformA
1141 // needs to translate the text initially around object width to orient
1142 // it relative to the topper right instead of the topper left
1143 const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsVertical());
1145 if(bVertical)
1147 aNewTransformA.translate(aScale.getX(), 0.0);
1150 // calculate global char stretching scale parameters. Use non-mirrored sizes
1151 // to layout without mirroring
1152 const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
1153 const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
1154 rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0));
1156 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1157 // move the null point which was top left to bottom right.
1158 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1159 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1161 // in-between the translations of the single primitives will take place. Afterwards,
1162 // the object's transformations need to be applied
1163 const basegfx::B2DHomMatrix aNewTransformB(basegfx::tools::createScaleShearXRotateTranslateB2DHomMatrix(
1164 bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0,
1165 fShearX, fRotate, aTranslate.getX(), aTranslate.getY()));
1167 // now break up text primitives.
1168 impTextBreakupHandler aConverter(rOutliner);
1169 aConverter.decomposeStretchTextPrimitive(aNewTransformA, aNewTransformB);
1171 // cleanup outliner
1172 rOutliner.SetControlWord(nOriginalControlWord);
1173 rOutliner.Clear();
1174 rOutliner.setVisualizedPage(0);
1176 rTarget = aConverter.getPrimitive2DSequence();
1180 // timing generators
1181 #define ENDLESS_LOOP (0xffffffff)
1182 #define ENDLESS_TIME ((double)0xffffffff)
1183 #define PIXEL_DPI (96.0)
1185 void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList) const
1187 if(SDRTEXTANI_BLINK == GetTextAniKind())
1189 // get values
1190 const SfxItemSet& rSet = GetObjectItemSet();
1191 const sal_uInt32 nRepeat((sal_uInt32)((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1192 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1193 double fDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1195 if(0.0 == fDelay)
1197 // use default
1198 fDelay = 250.0;
1201 // prepare loop and add
1202 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1203 drawinglayer::animation::AnimationEntryFixed aStart(fDelay, 0.0);
1204 aLoop.append(aStart);
1205 drawinglayer::animation::AnimationEntryFixed aEnd(fDelay, 1.0);
1206 aLoop.append(aEnd);
1207 rAnimList.append(aLoop);
1209 // add stopped state if loop is not endless
1210 if(0L != nRepeat)
1212 drawinglayer::animation::AnimationEntryFixed aStop(ENDLESS_TIME, bVisisbleWhenStopped ? 0.0 : 1.0);
1213 rAnimList.append(aStop);
1218 void impCreateScrollTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1220 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1221 bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1222 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1224 if(bVisisbleWhenStarted)
1226 // move from center to outside
1227 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1228 rAnimList.append(aInOut);
1231 // loop. In loop, move through
1232 if(nRepeat || 0L == nRepeat)
1234 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat : ENDLESS_LOOP);
1235 drawinglayer::animation::AnimationEntryLinear aThrough(fTimeFullPath, fFrequency, bForward ? 0.0 : 1.0, bForward ? 1.0 : 0.0);
1236 aLoop.append(aThrough);
1237 rAnimList.append(aLoop);
1240 if(0L != nRepeat && bVisisbleWhenStopped)
1242 // move from outside to center
1243 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1244 rAnimList.append(aOutIn);
1246 // add timing for staying at the end
1247 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1248 rAnimList.append(aEnd);
1252 void impCreateAlternateTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, double fRelativeTextLength, bool bForward, double fTimeFullPath, double fFrequency)
1254 if(basegfx::fTools::more(fRelativeTextLength, 0.5))
1256 // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
1257 // In that case, correct direction
1258 bForward = !bForward;
1261 const double fStartPosition(bForward ? fRelativeTextLength : 1.0 - fRelativeTextLength);
1262 const double fEndPosition(bForward ? 1.0 - fRelativeTextLength : fRelativeTextLength);
1263 bool bVisisbleWhenStopped(((SdrTextAniStopInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE)).GetValue());
1264 bool bVisisbleWhenStarted(((SdrTextAniStartInsideItem&)rSet.Get(SDRATTR_TEXT_ANISTOPINSIDE )).GetValue());
1265 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1267 if(!bVisisbleWhenStarted)
1269 // move from outside to center
1270 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, bForward ? 0.0 : 1.0, 0.5);
1271 rAnimList.append(aOutIn);
1274 // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
1275 // so use absolute value
1276 const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength * 2.0)));
1277 const double fTimeForInnerPath(fTimeFullPath * fInnerMovePath);
1278 const double fHalfInnerPath(fTimeForInnerPath * 0.5);
1279 const sal_uInt32 nDoubleRepeat(nRepeat / 2L);
1281 if(nDoubleRepeat || 0L == nRepeat)
1283 // double forth and back loop
1284 drawinglayer::animation::AnimationEntryLoop aLoop(nDoubleRepeat ? nDoubleRepeat : ENDLESS_LOOP);
1285 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1286 aLoop.append(aTime0);
1287 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeForInnerPath, fFrequency, fEndPosition, fStartPosition);
1288 aLoop.append(aTime1);
1289 drawinglayer::animation::AnimationEntryLinear aTime2(fHalfInnerPath, fFrequency, fStartPosition, 0.5);
1290 aLoop.append(aTime2);
1291 rAnimList.append(aLoop);
1294 if(nRepeat % 2L)
1296 // repeat is uneven, so we need one more forth and back to center
1297 drawinglayer::animation::AnimationEntryLinear aTime0(fHalfInnerPath, fFrequency, 0.5, fEndPosition);
1298 rAnimList.append(aTime0);
1299 drawinglayer::animation::AnimationEntryLinear aTime1(fHalfInnerPath, fFrequency, fEndPosition, 0.5);
1300 rAnimList.append(aTime1);
1303 if(0L != nRepeat)
1305 if(bVisisbleWhenStopped)
1307 // add timing for staying at the end
1308 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1309 rAnimList.append(aEnd);
1311 else
1313 // move from center to outside
1314 drawinglayer::animation::AnimationEntryLinear aInOut(fTimeFullPath * 0.5, fFrequency, 0.5, bForward ? 1.0 : 0.0);
1315 rAnimList.append(aInOut);
1320 void impCreateSlideTiming(const SfxItemSet& rSet, drawinglayer::animation::AnimationEntryList& rAnimList, bool bForward, double fTimeFullPath, double fFrequency)
1322 // move in from outside, start outside
1323 const double fStartPosition(bForward ? 0.0 : 1.0);
1324 const sal_uInt32 nRepeat(((SdrTextAniCountItem&)rSet.Get(SDRATTR_TEXT_ANICOUNT)).GetValue());
1326 // move from outside to center
1327 drawinglayer::animation::AnimationEntryLinear aOutIn(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1328 rAnimList.append(aOutIn);
1330 // loop. In loop, move out and in again
1331 if(nRepeat > 1L || 0L == nRepeat)
1333 drawinglayer::animation::AnimationEntryLoop aLoop(nRepeat ? nRepeat - 1L : ENDLESS_LOOP);
1334 drawinglayer::animation::AnimationEntryLinear aTime0(fTimeFullPath * 0.5, fFrequency, 0.5, fStartPosition);
1335 aLoop.append(aTime0);
1336 drawinglayer::animation::AnimationEntryLinear aTime1(fTimeFullPath * 0.5, fFrequency, fStartPosition, 0.5);
1337 aLoop.append(aTime1);
1338 rAnimList.append(aLoop);
1341 // always visible when stopped, so add timing for staying at the end when not endless
1342 if(0L != nRepeat)
1344 drawinglayer::animation::AnimationEntryFixed aEnd(ENDLESS_TIME, 0.5);
1345 rAnimList.append(aEnd);
1349 void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList& rAnimList, double fFrameLength, double fTextLength) const
1351 const SdrTextAniKind eAniKind(GetTextAniKind());
1353 if(SDRTEXTANI_SCROLL == eAniKind || SDRTEXTANI_ALTERNATE == eAniKind || SDRTEXTANI_SLIDE == eAniKind)
1355 // get data. Goal is to calculate fTimeFullPath which is the time needed to
1356 // move animation from (0.0) to (1.0) state
1357 const SfxItemSet& rSet = GetObjectItemSet();
1358 double fAnimationDelay((double)((SdrTextAniDelayItem&)rSet.Get(SDRATTR_TEXT_ANIDELAY)).GetValue());
1359 double fSingleStepWidth((double)((SdrTextAniAmountItem&)rSet.Get(SDRATTR_TEXT_ANIAMOUNT)).GetValue());
1360 const SdrTextAniDirection eDirection(GetTextAniDirection());
1361 const bool bForward(SDRTEXTANI_RIGHT == eDirection || SDRTEXTANI_DOWN == eDirection);
1363 if(basegfx::fTools::equalZero(fAnimationDelay))
1365 // default to 1/20 second
1366 fAnimationDelay = 50.0;
1369 if(basegfx::fTools::less(fSingleStepWidth, 0.0))
1371 // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
1372 // It makes no sense to keep the view-transformation centered
1373 // definitions, so get rid of them here.
1374 fSingleStepWidth = (-fSingleStepWidth * (2540.0 / PIXEL_DPI));
1377 if(basegfx::fTools::equalZero(fSingleStepWidth))
1379 // default to 1 millimeter
1380 fSingleStepWidth = 100.0;
1383 // use the length of the full animation path and the number of steps
1384 // to get the full path time
1385 const double fFullPathLength(fFrameLength + fTextLength);
1386 const double fNumberOfSteps(fFullPathLength / fSingleStepWidth);
1387 double fTimeFullPath(fNumberOfSteps * fAnimationDelay);
1389 if(fTimeFullPath < fAnimationDelay)
1391 fTimeFullPath = fAnimationDelay;
1394 switch(eAniKind)
1396 case SDRTEXTANI_SCROLL :
1398 impCreateScrollTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1399 break;
1401 case SDRTEXTANI_ALTERNATE :
1403 double fRelativeTextLength(fTextLength / (fFrameLength + fTextLength));
1404 impCreateAlternateTiming(rSet, rAnimList, fRelativeTextLength, bForward, fTimeFullPath, fAnimationDelay);
1405 break;
1407 case SDRTEXTANI_SLIDE :
1409 impCreateSlideTiming(rSet, rAnimList, bForward, fTimeFullPath, fAnimationDelay);
1410 break;
1412 default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK
1418 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */