Update ooo320-m1
[ooovba.git] / svx / source / svdraw / svdotextdecomposition.cxx
blobd870c91181c0b31ab4f01480d1fdddf81a678806
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: svdotextdecomposition.cxx,v $
11 * $Revision: 1.2.18.2 $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_svx.hxx"
35 #include <svx/svdotext.hxx>
36 #include <svx/svdoutl.hxx>
37 #include <basegfx/vector/b2dvector.hxx>
38 #include <svx/sdr/primitive2d/sdrtextprimitive2d.hxx>
39 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
40 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
41 #include <basegfx/range/b2drange.hxx>
42 #include <editstat.hxx>
43 #include <vcl/salbtype.hxx>
44 #include <svx/sdtfchim.hxx>
45 #include <svtools/itemset.hxx>
46 #include <basegfx/polygon/b2dpolygontools.hxx>
47 #include <basegfx/polygon/b2dpolygon.hxx>
48 #include <drawinglayer/animation/animationtiming.hxx>
49 #include <basegfx/color/bcolor.hxx>
50 #include <vcl/svapp.hxx>
51 #include <svx/eeitemid.hxx>
52 #include <svx/escpitem.hxx>
53 #include <svx/svxenum.hxx>
54 #include <svx/flditem.hxx>
55 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
56 #include <vcl/metaact.hxx>
57 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
58 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
59 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
60 #include <unoapi.hxx>
61 #include <drawinglayer/geometry/viewinformation2d.hxx>
62 #include <svx/outlobj.hxx>
64 //////////////////////////////////////////////////////////////////////////////
65 // helpers
67 namespace
69 drawinglayer::primitive2d::Primitive2DSequence impConvertVectorToPrimitive2DSequence(const std::vector< drawinglayer::primitive2d::BasePrimitive2D* >& rPrimitiveVector)
71 const sal_Int32 nCount(rPrimitiveVector.size());
72 drawinglayer::primitive2d::Primitive2DSequence aRetval(nCount);
74 for(sal_Int32 a(0L); a < nCount; a++)
76 aRetval[a] = drawinglayer::primitive2d::Primitive2DReference(rPrimitiveVector[a]);
79 return aRetval;
82 static drawinglayer::primitive2d::FontUnderline mapTextLineStyle(FontUnderline eLineStyle)
84 switch(eLineStyle)
86 case UNDERLINE_SINGLE: return drawinglayer::primitive2d::FONT_UNDERLINE_SINGLE;
87 case UNDERLINE_DOUBLE: return drawinglayer::primitive2d::FONT_UNDERLINE_DOUBLE;
88 case UNDERLINE_DOTTED: return drawinglayer::primitive2d::FONT_UNDERLINE_DOTTED;
89 case UNDERLINE_DASH: return drawinglayer::primitive2d::FONT_UNDERLINE_DASH;
90 case UNDERLINE_LONGDASH: return drawinglayer::primitive2d::FONT_UNDERLINE_LONGDASH;
91 case UNDERLINE_DASHDOT: return drawinglayer::primitive2d::FONT_UNDERLINE_DASHDOT;
92 case UNDERLINE_DASHDOTDOT: return drawinglayer::primitive2d::FONT_UNDERLINE_DASHDOTDOT;
93 case UNDERLINE_SMALLWAVE: return drawinglayer::primitive2d::FONT_UNDERLINE_SMALLWAVE;
94 case UNDERLINE_WAVE: return drawinglayer::primitive2d::FONT_UNDERLINE_WAVE;
95 case UNDERLINE_DOUBLEWAVE: return drawinglayer::primitive2d::FONT_UNDERLINE_DOUBLEWAVE;
96 case UNDERLINE_BOLD: return drawinglayer::primitive2d::FONT_UNDERLINE_BOLD;
97 case UNDERLINE_BOLDDOTTED: return drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDOTTED;
98 case UNDERLINE_BOLDDASH: return drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDASH;
99 case UNDERLINE_BOLDLONGDASH: return drawinglayer::primitive2d::FONT_UNDERLINE_BOLDLONGDASH;
100 case UNDERLINE_BOLDDASHDOT: return drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDASHDOT;
101 case UNDERLINE_BOLDDASHDOTDOT: return drawinglayer::primitive2d::FONT_UNDERLINE_BOLDDASHDOTDOT;
102 case UNDERLINE_BOLDWAVE: return drawinglayer::primitive2d::FONT_UNDERLINE_BOLDWAVE;
103 // FontUnderline_FORCE_EQUAL_SIZE, UNDERLINE_DONTKNOW, UNDERLINE_NONE
104 default: return drawinglayer::primitive2d::FONT_UNDERLINE_NONE;
108 class impTextBreakupHandler
110 private:
111 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maTextPortionPrimitives;
112 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maLinePrimitives;
113 std::vector< drawinglayer::primitive2d::BasePrimitive2D* > maParagraphPrimitives;
115 SdrOutliner& mrOutliner;
116 basegfx::B2DHomMatrix maNewTransformA;
117 basegfx::B2DHomMatrix maNewTransformB;
119 // the visible area for contour text decomposition
120 basegfx::B2DVector maScale;
122 // #SJ# ClipRange for BlockText decomposition; only text portions completely
123 // inside are to be accepted, so this is different from geometric clipping
124 // (which would allow e.g. upper parts of portions to remain). Only used for
125 // BlockText (see there)
126 basegfx::B2DRange maClipRange;
128 DECL_LINK(decomposeContourTextPrimitive, DrawPortionInfo* );
129 DECL_LINK(decomposeBlockTextPrimitive, DrawPortionInfo* );
130 DECL_LINK(decomposeStretchTextPrimitive, DrawPortionInfo* );
132 DECL_LINK(decomposeContourBulletPrimitive, DrawBulletInfo* );
133 DECL_LINK(decomposeBlockBulletPrimitive, DrawBulletInfo* );
134 DECL_LINK(decomposeStretchBulletPrimitive, DrawBulletInfo* );
136 bool impIsUnderlineAbove(const Font& rFont) const;
137 void impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo);
138 drawinglayer::primitive2d::BasePrimitive2D* impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const;
139 void impFlushTextPortionPrimitivesToLinePrimitives();
140 void impFlushLinePrimitivesToParagraphPrimitives();
141 void impHandleDrawPortionInfo(const DrawPortionInfo& rInfo);
142 void impHandleDrawBulletInfo(const DrawBulletInfo& rInfo);
144 public:
145 impTextBreakupHandler(SdrOutliner& rOutliner)
146 : maTextPortionPrimitives(),
147 maLinePrimitives(),
148 maParagraphPrimitives(),
149 mrOutliner(rOutliner),
150 maNewTransformA(),
151 maNewTransformB(),
152 maScale(),
153 maClipRange()
157 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB, const basegfx::B2DVector& rScale)
159 maScale = rScale;
160 maNewTransformA = rNewTransformA;
161 maNewTransformB = rNewTransformB;
162 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeContourTextPrimitive));
163 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeContourBulletPrimitive));
164 mrOutliner.StripPortions();
165 mrOutliner.SetDrawPortionHdl(Link());
166 mrOutliner.SetDrawBulletHdl(Link());
169 void decomposeBlockTextPrimitive(
170 const basegfx::B2DHomMatrix& rNewTransformA,
171 const basegfx::B2DHomMatrix& rNewTransformB,
172 const basegfx::B2DRange& rClipRange)
174 maNewTransformA = rNewTransformA;
175 maNewTransformB = rNewTransformB;
176 maClipRange = rClipRange;
177 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeBlockTextPrimitive));
178 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeBlockBulletPrimitive));
179 mrOutliner.StripPortions();
180 mrOutliner.SetDrawPortionHdl(Link());
181 mrOutliner.SetDrawBulletHdl(Link());
184 void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix& rNewTransformA, const basegfx::B2DHomMatrix& rNewTransformB)
186 maNewTransformA = rNewTransformA;
187 maNewTransformB = rNewTransformB;
188 mrOutliner.SetDrawPortionHdl(LINK(this, impTextBreakupHandler, decomposeStretchTextPrimitive));
189 mrOutliner.SetDrawBulletHdl(LINK(this, impTextBreakupHandler, decomposeStretchBulletPrimitive));
190 mrOutliner.StripPortions();
191 mrOutliner.SetDrawPortionHdl(Link());
192 mrOutliner.SetDrawBulletHdl(Link());
195 drawinglayer::primitive2d::Primitive2DSequence getPrimitive2DSequence();
198 bool impTextBreakupHandler::impIsUnderlineAbove(const Font& rFont) const
200 if(!rFont.IsVertical())
202 return false;
205 if((LANGUAGE_JAPANESE == rFont.GetLanguage()) || (LANGUAGE_JAPANESE == rFont.GetCJKContextLanguage()))
207 // the underline is right for Japanese only
208 return true;
211 return false;
214 void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo& rInfo)
216 if(rInfo.mrText.Len() && rInfo.mnTextLen)
218 basegfx::B2DVector aFontScaling;
219 drawinglayer::primitive2d::FontAttributes aFontAttributes(
220 drawinglayer::primitive2d::getFontAttributesFromVclFont(
221 aFontScaling,
222 rInfo.mrFont,
223 rInfo.IsRTL(),
224 false));
225 basegfx::B2DHomMatrix aNewTransform;
227 // add font scale to new transform
228 aNewTransform.scale(aFontScaling.getX(), aFontScaling.getY());
230 // look for proportional font scaling, evtl scale accordingly
231 if(100 != rInfo.mrFont.GetPropr())
233 const double fFactor(rInfo.mrFont.GetPropr() / 100.0);
234 aNewTransform.scale(fFactor, fFactor);
237 // apply font rotate
238 if(rInfo.mrFont.GetOrientation())
240 aNewTransform.rotate(-rInfo.mrFont.GetOrientation() * F_PI1800);
243 // look for escapement, evtl translate accordingly
244 if(rInfo.mrFont.GetEscapement())
246 sal_Int16 nEsc(rInfo.mrFont.GetEscapement());
248 if(DFLT_ESC_AUTO_SUPER == nEsc)
250 nEsc = 33;
252 else if(DFLT_ESC_AUTO_SUB == nEsc)
254 nEsc = -20;
257 if(nEsc > 100)
259 nEsc = 100;
261 else if(nEsc < -100)
263 nEsc = -100;
266 const double fEscapement(nEsc / -100.0);
267 aNewTransform.translate(0.0, fEscapement * aFontScaling.getY());
270 // apply transformA
271 aNewTransform *= maNewTransformA;
273 // apply local offset
274 aNewTransform.translate(rInfo.mrStartPos.X(), rInfo.mrStartPos.Y());
276 // also apply embedding object's transform
277 aNewTransform *= maNewTransformB;
279 // prepare DXArray content. To make it independent from font size (and such from
280 // the text transformation), scale it to unit coordinates
281 ::std::vector< double > aDXArray;
282 static bool bDisableTextArray(false);
284 if(!bDisableTextArray && rInfo.mpDXArray && rInfo.mnTextLen)
286 aDXArray.reserve(rInfo.mnTextLen);
288 for(xub_StrLen a(0); a < rInfo.mnTextLen; a++)
290 aDXArray.push_back((double)rInfo.mpDXArray[a]);
294 // create complex text primitive and append
295 const Color aFontColor(rInfo.mrFont.GetColor());
296 const basegfx::BColor aBFontColor(aFontColor.getBColor());
298 // prepare new primitive
299 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = 0;
300 const bool bDecoratedIsNeeded(
301 UNDERLINE_NONE != rInfo.mrFont.GetOverline()
302 || UNDERLINE_NONE != rInfo.mrFont.GetUnderline()
303 || STRIKEOUT_NONE != rInfo.mrFont.GetStrikeout()
304 || EMPHASISMARK_NONE != (rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
305 || RELIEF_NONE != rInfo.mrFont.GetRelief()
306 || rInfo.mrFont.IsShadow());
308 if(bDecoratedIsNeeded)
310 // TextDecoratedPortionPrimitive2D needed, prepare some more data
311 // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
312 const Color aUnderlineColor(rInfo.maTextLineColor);
313 const basegfx::BColor aBUnderlineColor((0xffffffff == aUnderlineColor.GetColor()) ? aBFontColor : aUnderlineColor.getBColor());
314 const Color aOverlineColor(rInfo.maOverlineColor);
315 const basegfx::BColor aBOverlineColor((0xffffffff == aOverlineColor.GetColor()) ? aBFontColor : aOverlineColor.getBColor());
317 // prepare overline and underline data
318 const drawinglayer::primitive2d::FontUnderline eFontOverline(mapTextLineStyle(rInfo.mrFont.GetOverline()));
319 const drawinglayer::primitive2d::FontUnderline eFontUnderline(mapTextLineStyle(rInfo.mrFont.GetUnderline()));
321 // check UndelineAbove
322 const bool bUnderlineAbove(drawinglayer::primitive2d::FONT_UNDERLINE_NONE != eFontUnderline && impIsUnderlineAbove(rInfo.mrFont));
324 // prepare strikeout data
325 drawinglayer::primitive2d::FontStrikeout eFontStrikeout(drawinglayer::primitive2d::FONT_STRIKEOUT_NONE);
327 switch(rInfo.mrFont.GetStrikeout())
329 case STRIKEOUT_SINGLE: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_SINGLE; break;
330 case STRIKEOUT_DOUBLE: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_DOUBLE; break;
331 case STRIKEOUT_BOLD: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_BOLD; break;
332 case STRIKEOUT_SLASH: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_SLASH; break;
333 case STRIKEOUT_X: eFontStrikeout = drawinglayer::primitive2d::FONT_STRIKEOUT_X; break;
334 default : break; // FontStrikeout_FORCE_EQUAL_SIZE, STRIKEOUT_NONE, STRIKEOUT_DONTKNOW
337 // prepare wordLineMode (for underline and strikeout)
338 // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
339 // to be splitted which would not look like the original
340 const bool bWordLineMode(rInfo.mrFont.IsWordLineMode() && !rInfo.mbEndOfBullet);
342 // prepare emphasis mark data
343 drawinglayer::primitive2d::FontEmphasisMark eFontEmphasisMark(drawinglayer::primitive2d::FONT_EMPHASISMARK_NONE);
345 switch(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_STYLE)
347 case EMPHASISMARK_DOT : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_DOT; break;
348 case EMPHASISMARK_CIRCLE : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_CIRCLE; break;
349 case EMPHASISMARK_DISC : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_DISC; break;
350 case EMPHASISMARK_ACCENT : eFontEmphasisMark = drawinglayer::primitive2d::FONT_EMPHASISMARK_ACCENT; break;
353 const bool bEmphasisMarkAbove(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_ABOVE);
354 const bool bEmphasisMarkBelow(rInfo.mrFont.GetEmphasisMark() & EMPHASISMARK_POS_BELOW);
356 // prepare font relief data
357 drawinglayer::primitive2d::FontRelief eFontRelief(drawinglayer::primitive2d::FONT_RELIEF_NONE);
359 switch(rInfo.mrFont.GetRelief())
361 case RELIEF_EMBOSSED : eFontRelief = drawinglayer::primitive2d::FONT_RELIEF_EMBOSSED; break;
362 case RELIEF_ENGRAVED : eFontRelief = drawinglayer::primitive2d::FONT_RELIEF_ENGRAVED; break;
363 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
366 // prepare shadow/outline data
367 const bool bShadow(rInfo.mrFont.IsShadow());
369 // TextDecoratedPortionPrimitive2D is needed, create one
370 pNewPrimitive = new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
372 // attributes for TextSimplePortionPrimitive2D
373 aNewTransform,
374 rInfo.mrText,
375 rInfo.mnTextStart,
376 rInfo.mnTextLen,
377 aDXArray,
378 aFontAttributes,
379 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
380 aBFontColor,
382 // attributes for TextDecoratedPortionPrimitive2D
383 aBOverlineColor,
384 aBUnderlineColor,
385 eFontOverline,
386 eFontUnderline,
387 bUnderlineAbove,
388 eFontStrikeout,
389 bWordLineMode,
390 eFontEmphasisMark,
391 bEmphasisMarkAbove,
392 bEmphasisMarkBelow,
393 eFontRelief,
394 bShadow);
396 else
398 // TextSimplePortionPrimitive2D is enough
399 pNewPrimitive = new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
400 aNewTransform,
401 rInfo.mrText,
402 rInfo.mnTextStart,
403 rInfo.mnTextLen,
404 aDXArray,
405 aFontAttributes,
406 rInfo.mpLocale ? *rInfo.mpLocale : ::com::sun::star::lang::Locale(),
407 aBFontColor);
410 if(rInfo.mbEndOfBullet)
412 // embed in TextHierarchyBulletPrimitive2D
413 const drawinglayer::primitive2d::Primitive2DReference aNewReference(pNewPrimitive);
414 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
415 pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
418 if(rInfo.mpFieldData)
420 pNewPrimitive = impCheckFieldPrimitive(pNewPrimitive, rInfo);
423 maTextPortionPrimitives.push_back(pNewPrimitive);
425 // support for WrongSpellVector. Create WrongSpellPrimitives as needed
426 if(rInfo.mpWrongSpellVector && !aDXArray.empty())
428 const sal_uInt32 nSize(rInfo.mpWrongSpellVector->size());
429 const sal_uInt32 nDXCount(aDXArray.size());
430 const basegfx::BColor aSpellColor(1.0, 0.0, 0.0); // red, hard coded
432 for(sal_uInt32 a(0); a < nSize; a++)
434 const EEngineData::WrongSpellClass& rCandidate = (*rInfo.mpWrongSpellVector)[a];
436 if(rCandidate.nStart >= rInfo.mnTextStart && rCandidate.nEnd >= rInfo.mnTextStart && rCandidate.nEnd > rCandidate.nStart)
438 const sal_uInt32 nStart(rCandidate.nStart - rInfo.mnTextStart);
439 const sal_uInt32 nEnd(rCandidate.nEnd - rInfo.mnTextStart);
440 double fStart(0.0);
441 double fEnd(0.0);
443 if(nStart > 0 && nStart - 1 < nDXCount)
445 fStart = aDXArray[nStart - 1];
448 if(nEnd > 0 && nEnd - 1 < nDXCount)
450 fEnd = aDXArray[nEnd - 1];
453 if(!basegfx::fTools::equal(fStart, fEnd))
455 if(rInfo.IsRTL())
457 // #i98523#
458 // When the portion is RTL, mirror the redlining using the
459 // full portion width
460 const double fTextWidth(aDXArray[aDXArray.size() - 1]);
462 fStart = fTextWidth - fStart;
463 fEnd = fTextWidth - fEnd;
466 // need to take FontScaling out of values; it's already part of
467 // aNewTransform and would be double applied
468 const double fFontScaleX(aFontScaling.getX());
470 if(!basegfx::fTools::equal(fFontScaleX, 1.0)
471 && !basegfx::fTools::equalZero(fFontScaleX))
473 fStart /= fFontScaleX;
474 fEnd /= fFontScaleX;
477 maTextPortionPrimitives.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
478 aNewTransform,
479 fStart,
480 fEnd,
481 aSpellColor));
489 drawinglayer::primitive2d::BasePrimitive2D* impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D* pPrimitive, const DrawPortionInfo& rInfo) const
491 if(rInfo.mpFieldData)
493 // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
494 // which holds the field type and evtl. the URL
495 const SvxURLField* pURLField = dynamic_cast< const SvxURLField* >(rInfo.mpFieldData);
496 const SvxPageField* pPageField = dynamic_cast< const SvxPageField* >(rInfo.mpFieldData);
498 // embed current primitive to a sequence
499 drawinglayer::primitive2d::Primitive2DSequence aSequence;
501 if(pPrimitive)
503 aSequence.realloc(1);
504 aSequence[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive);
507 if(pURLField)
509 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_URL, pURLField->GetURL());
511 else if(pPageField)
513 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_PAGE, String());
515 else
517 pPrimitive = new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence, drawinglayer::primitive2d::FIELD_TYPE_COMMON, String());
521 return pPrimitive;
524 void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
526 // only create a line primitive when we had content; there is no need for
527 // empty line primitives (contrary to paragraphs, see below).
528 if(maTextPortionPrimitives.size())
530 drawinglayer::primitive2d::Primitive2DSequence aLineSequence(impConvertVectorToPrimitive2DSequence(maTextPortionPrimitives));
531 maTextPortionPrimitives.clear();
532 maLinePrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(aLineSequence));
536 void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives()
538 // ALWAYS create a paragraph primitive, even when no content was added. This is done to
539 // have the correct paragraph count even with empty paragraphs. Those paragraphs will
540 // have an empty sub-PrimitiveSequence.
541 drawinglayer::primitive2d::Primitive2DSequence aParagraphSequence(impConvertVectorToPrimitive2DSequence(maLinePrimitives));
542 maLinePrimitives.clear();
543 maParagraphPrimitives.push_back(new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(aParagraphSequence));
546 void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo& rInfo)
548 impCreateTextPortionPrimitive(rInfo);
550 if(rInfo.mbEndOfLine || rInfo.mbEndOfParagraph)
552 impFlushTextPortionPrimitivesToLinePrimitives();
555 if(rInfo.mbEndOfParagraph)
557 impFlushLinePrimitivesToParagraphPrimitives();
561 void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo& rInfo)
563 basegfx::B2DHomMatrix aNewTransform;
565 // add size to new transform
566 aNewTransform.scale(rInfo.maBulletSize.getWidth(), rInfo.maBulletSize.getHeight());
568 // apply transformA
569 aNewTransform *= maNewTransformA;
571 // apply local offset
572 aNewTransform.translate(rInfo.maBulletPosition.X(), rInfo.maBulletPosition.Y());
574 // also apply embedding object's transform
575 aNewTransform *= maNewTransformB;
577 // prepare empty GraphicAttr
578 const GraphicAttr aGraphicAttr;
580 // create GraphicPrimitive2D
581 const drawinglayer::primitive2d::Primitive2DReference aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
582 aNewTransform,
583 rInfo.maBulletGraphicObject,
584 aGraphicAttr));
586 // embed in TextHierarchyBulletPrimitive2D
587 const drawinglayer::primitive2d::Primitive2DSequence aNewSequence(&aNewReference, 1);
588 drawinglayer::primitive2d::BasePrimitive2D* pNewPrimitive = new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence);
590 // add to output
591 maTextPortionPrimitives.push_back(pNewPrimitive);
594 IMPL_LINK(impTextBreakupHandler, decomposeContourTextPrimitive, DrawPortionInfo*, pInfo)
596 // for contour text, ignore (clip away) all portions which are below
597 // the visible area given by maScale
598 if(pInfo && (double)pInfo->mrStartPos.Y() < maScale.getY())
600 impHandleDrawPortionInfo(*pInfo);
603 return 0;
606 IMPL_LINK(impTextBreakupHandler, decomposeBlockTextPrimitive, DrawPortionInfo*, pInfo)
608 if(pInfo)
610 // #SJ# Is clipping wanted? This is text clipping; only accept a portion
611 // if it's completely in the range
612 if(!maClipRange.isEmpty())
614 // Test start position first; this allows to not get the text range at
615 // all if text is far outside
616 const basegfx::B2DPoint aStartPosition(pInfo->mrStartPos.X(), pInfo->mrStartPos.Y());
618 if(!maClipRange.isInside(aStartPosition))
620 return 0;
623 // Start position is inside. Get TextBoundRect and TopLeft next
624 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice;
625 aTextLayouterDevice.setFont(pInfo->mrFont);
627 const basegfx::B2DRange aTextBoundRect(
628 aTextLayouterDevice.getTextBoundRect(
629 pInfo->mrText, pInfo->mnTextStart, pInfo->mnTextLen));
630 const basegfx::B2DPoint aTopLeft(aTextBoundRect.getMinimum() + aStartPosition);
632 if(!maClipRange.isInside(aTopLeft))
634 return 0;
637 // TopLeft is inside. Get BottomRight and check
638 const basegfx::B2DPoint aBottomRight(aTextBoundRect.getMaximum() + aStartPosition);
640 if(!maClipRange.isInside(aBottomRight))
642 return 0;
645 // all inside, clip was successful
647 impHandleDrawPortionInfo(*pInfo);
650 return 0;
653 IMPL_LINK(impTextBreakupHandler, decomposeStretchTextPrimitive, DrawPortionInfo*, pInfo)
655 if(pInfo)
657 impHandleDrawPortionInfo(*pInfo);
660 return 0;
663 IMPL_LINK(impTextBreakupHandler, decomposeContourBulletPrimitive, DrawBulletInfo*, pInfo)
665 if(pInfo)
667 impHandleDrawBulletInfo(*pInfo);
670 return 0;
673 IMPL_LINK(impTextBreakupHandler, decomposeBlockBulletPrimitive, DrawBulletInfo*, pInfo)
675 if(pInfo)
677 impHandleDrawBulletInfo(*pInfo);
680 return 0;
683 IMPL_LINK(impTextBreakupHandler, decomposeStretchBulletPrimitive, DrawBulletInfo*, pInfo)
685 if(pInfo)
687 impHandleDrawBulletInfo(*pInfo);
690 return 0;
693 drawinglayer::primitive2d::Primitive2DSequence impTextBreakupHandler::getPrimitive2DSequence()
695 if(maTextPortionPrimitives.size())
697 // collect non-closed lines
698 impFlushTextPortionPrimitivesToLinePrimitives();
701 if(maLinePrimitives.size())
703 // collect non-closed paragraphs
704 impFlushLinePrimitivesToParagraphPrimitives();
707 return impConvertVectorToPrimitive2DSequence(maParagraphPrimitives);
709 } // end of anonymous namespace
711 //////////////////////////////////////////////////////////////////////////////
712 // primitive decompositions
714 void SdrTextObj::impDecomposeContourTextPrimitive(
715 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
716 const drawinglayer::primitive2d::SdrContourTextPrimitive2D& rSdrContourTextPrimitive,
717 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
719 // decompose matrix to have position and size of text
720 basegfx::B2DVector aScale, aTranslate;
721 double fRotate, fShearX;
722 rSdrContourTextPrimitive.getObjectTransform().decompose(aScale, aTranslate, fRotate, fShearX);
724 // prepare contour polygon, force to non-mirrored for layouting
725 basegfx::B2DPolyPolygon aPolyPolygon(rSdrContourTextPrimitive.getUnitPolyPolygon());
726 basegfx::B2DHomMatrix aTransform;
727 aTransform.scale(fabs(aScale.getX()), fabs(aScale.getY()));
728 aPolyPolygon.transform(aTransform);
730 // prepare outliner
731 SdrOutliner& rOutliner = ImpGetDrawOutliner();
732 const Size aNullSize;
733 rOutliner.SetPaperSize(aNullSize);
734 rOutliner.SetPolygon(aPolyPolygon);
735 rOutliner.SetUpdateMode(true);
736 rOutliner.SetText(rSdrContourTextPrimitive.getOutlinerParaObject());
738 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
739 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
741 // prepare matrices to apply to newly created primitives
742 basegfx::B2DHomMatrix aNewTransformA;
743 basegfx::B2DHomMatrix aNewTransformB;
745 // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
746 // move the null point which was top left to bottom right.
747 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
748 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
749 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
751 // in-between the translations of the single primitives will take place. Afterwards,
752 // the object's transformations need to be applied
753 aNewTransformB.shearX(fShearX);
754 aNewTransformB.rotate(fRotate);
755 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
757 // now break up text primitives.
758 impTextBreakupHandler aConverter(rOutliner);
759 aConverter.decomposeContourTextPrimitive(aNewTransformA, aNewTransformB, aScale);
761 // cleanup outliner
762 rOutliner.Clear();
763 rOutliner.setVisualizedPage(0);
765 rTarget = aConverter.getPrimitive2DSequence();
768 void SdrTextObj::impDecomposeAutoFitTextPrimitive(
769 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
770 const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D& rSdrAutofitTextPrimitive,
771 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
773 // decompose matrix to have position and size of text
774 basegfx::B2DVector aScale, aTranslate;
775 double fRotate, fShearX;
776 rSdrAutofitTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
778 // use B2DRange aAnchorTextRange for calculations
779 basegfx::B2DRange aAnchorTextRange(aTranslate);
780 aAnchorTextRange.expand(aTranslate + aScale);
782 // prepare outliner
783 const SfxItemSet& rTextItemSet = rSdrAutofitTextPrimitive.getSdrText()->GetItemSet();
784 SdrOutliner& rOutliner = ImpGetDrawOutliner();
785 SdrTextVertAdjust eVAdj = GetTextVerticalAdjust(rTextItemSet);
786 SdrTextHorzAdjust eHAdj = GetTextHorizontalAdjust(rTextItemSet);
787 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
788 const Size aNullSize;
790 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
791 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
793 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE|EE_CNTRL_STRETCHING);
794 rOutliner.SetMinAutoPaperSize(aNullSize);
795 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
797 // add one to rage sizes to get back to the old Rectangle and outliner measurements
798 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
799 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
800 const OutlinerParaObject* pOutlinerParaObject = rSdrAutofitTextPrimitive.getSdrText()->GetOutlinerParaObject();
801 OSL_ENSURE(pOutlinerParaObject, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
802 const bool bVerticalWritintg(pOutlinerParaObject->IsVertical());
803 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
805 if((rSdrAutofitTextPrimitive.getWordWrap() || IsTextFrame()))
807 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
810 if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg)
812 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
815 if(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg)
817 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
820 rOutliner.SetPaperSize(aNullSize);
821 rOutliner.SetUpdateMode(true);
822 rOutliner.SetText(*pOutlinerParaObject);
823 ImpAutoFitText(rOutliner,aAnchorTextSize,bVerticalWritintg);
825 // now get back the layouted text size from outliner
826 const Size aOutlinerTextSiz(rOutliner.GetPaperSize());
827 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height());
828 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
830 // correct horizontal translation using the now known text size
831 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
833 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
835 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
837 aAdjustTranslate.setX(fFree / 2.0);
840 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
842 aAdjustTranslate.setX(fFree);
846 // correct vertical translation using the now known text size
847 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
849 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
851 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
853 aAdjustTranslate.setY(fFree / 2.0);
856 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
858 aAdjustTranslate.setY(fFree);
862 // prepare matrices to apply to newly created primitives. aNewTransformA
863 // will get coordinates in aOutlinerScale size and positive in X, Y.
864 basegfx::B2DHomMatrix aNewTransformA;
865 basegfx::B2DHomMatrix aNewTransformB;
867 // translate relative to given primitive to get same rotation and shear
868 // as the master shape we are working on. For vertical, use the top-right
869 // corner
870 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
871 aNewTransformA.translate(fStartInX, aAdjustTranslate.getY());
873 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
874 // move the null point which was top left to bottom right.
875 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
876 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
877 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
879 // in-between the translations of the single primitives will take place. Afterwards,
880 // the object's transformations need to be applied
881 aNewTransformB.shearX(fShearX);
882 aNewTransformB.rotate(fRotate);
883 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
885 basegfx::B2DRange aClipRange;
887 // now break up text primitives.
888 impTextBreakupHandler aConverter(rOutliner);
889 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
891 // cleanup outliner
892 rOutliner.Clear();
893 rOutliner.setVisualizedPage(0);
894 rOutliner.SetControlWord(nOriginalControlWord);
896 rTarget = aConverter.getPrimitive2DSequence();
899 void SdrTextObj::impDecomposeBlockTextPrimitive(
900 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
901 const drawinglayer::primitive2d::SdrBlockTextPrimitive2D& rSdrBlockTextPrimitive,
902 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
904 // decompose matrix to have position and size of text
905 basegfx::B2DVector aScale, aTranslate;
906 double fRotate, fShearX;
907 rSdrBlockTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
909 // use B2DRange aAnchorTextRange for calculations
910 basegfx::B2DRange aAnchorTextRange(aTranslate);
911 aAnchorTextRange.expand(aTranslate + aScale);
913 // prepare outliner
914 const bool bIsCell(rSdrBlockTextPrimitive.getCellText());
915 SdrOutliner& rOutliner = ImpGetDrawOutliner();
916 SdrTextHorzAdjust eHAdj = rSdrBlockTextPrimitive.getSdrTextHorzAdjust();
917 SdrTextVertAdjust eVAdj = rSdrBlockTextPrimitive.getSdrTextVertAdjust();
918 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
919 const Size aNullSize;
921 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
922 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
923 rOutliner.SetFixedCellHeight(rSdrBlockTextPrimitive.isFixedCellHeight());
924 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_AUTOPAGESIZE);
925 rOutliner.SetMinAutoPaperSize(aNullSize);
926 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
928 // add one to rage sizes to get back to the old Rectangle and outliner measurements
929 const sal_uInt32 nAnchorTextWidth(FRound(aAnchorTextRange.getWidth() + 1L));
930 const sal_uInt32 nAnchorTextHeight(FRound(aAnchorTextRange.getHeight() + 1L));
931 const bool bVerticalWritintg(rSdrBlockTextPrimitive.getOutlinerParaObject().IsVertical());
932 const Size aAnchorTextSize(Size(nAnchorTextWidth, nAnchorTextHeight));
934 if(bIsCell)
936 // cell text is formated neither like a text object nor like a object
937 // text, so use a special setup here
938 rOutliner.SetMinAutoPaperSize(aNullSize);
939 rOutliner.SetMaxAutoPaperSize(aAnchorTextSize);
940 rOutliner.SetPaperSize(aAnchorTextSize);
941 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
942 rOutliner.SetUpdateMode(TRUE);
943 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
944 rOutliner.SetUpdateMode(TRUE);
945 rOutliner.SetControlWord(nOriginalControlWord);
947 else
949 // check if block text is used (only one of them can be true)
950 const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK == eHAdj && !bVerticalWritintg);
951 const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK == eVAdj && bVerticalWritintg);
953 if((rSdrBlockTextPrimitive.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive.getUnlimitedPage())
955 // #i103454# maximal paper size hor/ver needs to be limited to text
956 // frame size. If it's block text, still allow the 'other' direction
957 // to grow to get a correct real text size when using GetPaperSize().
958 // When just using aAnchorTextSize as maximum, GetPaperSize()
959 // would just return aAnchorTextSize again: this means, the wanted
960 // 'measurement' of the real size of block text would not work
961 Size aMaxAutoPaperSize(aAnchorTextSize);
963 if(bHorizontalIsBlock)
965 // allow to grow vertical for horizontal blocks
966 aMaxAutoPaperSize.setHeight(1000000);
968 else if(bVerticalIsBlock)
970 // allow to grow horizontal for vertical blocks
971 aMaxAutoPaperSize.setWidth(1000000);
974 rOutliner.SetMaxAutoPaperSize(aMaxAutoPaperSize);
977 // set minimal paper size hor/ver if needed
978 if(bHorizontalIsBlock)
980 rOutliner.SetMinAutoPaperSize(Size(nAnchorTextWidth, 0));
982 else if(bVerticalIsBlock)
984 rOutliner.SetMinAutoPaperSize(Size(0, nAnchorTextHeight));
987 rOutliner.SetPaperSize(aNullSize);
988 rOutliner.SetUpdateMode(true);
989 rOutliner.SetText(rSdrBlockTextPrimitive.getOutlinerParaObject());
990 rOutliner.SetControlWord(nOriginalControlWord);
993 // now get back the layouted text size from outliner
994 const Size aOutlinerTextSiz(rOutliner.GetPaperSize());
995 const basegfx::B2DVector aOutlinerScale(aOutlinerTextSiz.Width(), aOutlinerTextSiz.Height());
996 basegfx::B2DVector aAdjustTranslate(0.0, 0.0);
998 // For draw objects containing text correct hor/ver alignment if text is bigger
999 // than the object itself. Without that correction, the text would always be
1000 // formatted to the left edge (or top edge when vertical) of the draw object.
1001 if(!IsTextFrame() && !bIsCell)
1003 if(aAnchorTextRange.getWidth() < aOutlinerScale.getX() && !bVerticalWritintg)
1005 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
1006 // else the alignment is wanted.
1007 if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
1009 eHAdj = SDRTEXTHORZADJUST_CENTER;
1013 if(aAnchorTextRange.getHeight() < aOutlinerScale.getY() && bVerticalWritintg)
1015 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
1016 // else the alignment is wanted.
1017 if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
1019 eVAdj = SDRTEXTVERTADJUST_CENTER;
1024 // correct horizontal translation using the now known text size
1025 if(SDRTEXTHORZADJUST_CENTER == eHAdj || SDRTEXTHORZADJUST_RIGHT == eHAdj)
1027 const double fFree(aAnchorTextRange.getWidth() - aOutlinerScale.getX());
1029 if(SDRTEXTHORZADJUST_CENTER == eHAdj)
1031 aAdjustTranslate.setX(fFree / 2.0);
1034 if(SDRTEXTHORZADJUST_RIGHT == eHAdj)
1036 aAdjustTranslate.setX(fFree);
1040 // correct vertical translation using the now known text size
1041 if(SDRTEXTVERTADJUST_CENTER == eVAdj || SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1043 const double fFree(aAnchorTextRange.getHeight() - aOutlinerScale.getY());
1045 if(SDRTEXTVERTADJUST_CENTER == eVAdj)
1047 aAdjustTranslate.setY(fFree / 2.0);
1050 if(SDRTEXTVERTADJUST_BOTTOM == eVAdj)
1052 aAdjustTranslate.setY(fFree);
1056 // prepare matrices to apply to newly created primitives. aNewTransformA
1057 // will get coordinates in aOutlinerScale size and positive in X, Y.
1058 basegfx::B2DHomMatrix aNewTransformA;
1059 basegfx::B2DHomMatrix aNewTransformB;
1061 // translate relative to given primitive to get same rotation and shear
1062 // as the master shape we are working on. For vertical, use the top-right
1063 // corner
1064 const double fStartInX(bVerticalWritintg ? aAdjustTranslate.getX() + aOutlinerScale.getX() : aAdjustTranslate.getX());
1065 aNewTransformA.translate(fStartInX, aAdjustTranslate.getY());
1067 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1068 // move the null point which was top left to bottom right.
1069 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1070 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1071 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
1073 // in-between the translations of the single primitives will take place. Afterwards,
1074 // the object's transformations need to be applied
1075 aNewTransformB.shearX(fShearX);
1076 aNewTransformB.rotate(fRotate);
1077 aNewTransformB.translate(aTranslate.getX(), aTranslate.getY());
1079 // #SJ# create ClipRange (if needed)
1080 basegfx::B2DRange aClipRange;
1082 if(bIsCell)
1084 aClipRange.expand(basegfx::B2DTuple(0.0, 0.0));
1085 aClipRange.expand(basegfx::B2DTuple(aAnchorTextSize.Width(), aAnchorTextSize.Height()));
1088 // now break up text primitives.
1089 impTextBreakupHandler aConverter(rOutliner);
1090 aConverter.decomposeBlockTextPrimitive(aNewTransformA, aNewTransformB, aClipRange);
1092 // cleanup outliner
1093 rOutliner.Clear();
1094 rOutliner.setVisualizedPage(0);
1096 rTarget = aConverter.getPrimitive2DSequence();
1099 void SdrTextObj::impDecomposeStretchTextPrimitive(
1100 drawinglayer::primitive2d::Primitive2DSequence& rTarget,
1101 const drawinglayer::primitive2d::SdrStretchTextPrimitive2D& rSdrStretchTextPrimitive,
1102 const drawinglayer::geometry::ViewInformation2D& aViewInformation) const
1104 // decompose matrix to have position and size of text
1105 basegfx::B2DVector aScale, aTranslate;
1106 double fRotate, fShearX;
1107 rSdrStretchTextPrimitive.getTextRangeTransform().decompose(aScale, aTranslate, fRotate, fShearX);
1109 // use non-mirrored B2DRange aAnchorTextRange for calculations
1110 basegfx::B2DRange aAnchorTextRange(aTranslate);
1111 aAnchorTextRange.expand(aTranslate + aScale);
1113 // prepare outliner
1114 SdrOutliner& rOutliner = ImpGetDrawOutliner();
1115 const sal_uInt32 nOriginalControlWord(rOutliner.GetControlWord());
1116 const Size aNullSize;
1118 rOutliner.SetControlWord(nOriginalControlWord|EE_CNTRL_STRETCHING|EE_CNTRL_AUTOPAGESIZE);
1119 rOutliner.SetFixedCellHeight(rSdrStretchTextPrimitive.isFixedCellHeight());
1120 rOutliner.SetMinAutoPaperSize(aNullSize);
1121 rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
1122 rOutliner.SetPaperSize(aNullSize);
1123 rOutliner.SetUpdateMode(true);
1124 rOutliner.SetText(rSdrStretchTextPrimitive.getOutlinerParaObject());
1126 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1127 rOutliner.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation.getVisualizedPage()));
1129 // now get back the layouted text size from outliner
1130 const Size aOutlinerTextSiz(rOutliner.CalcTextSize());
1131 const basegfx::B2DVector aOutlinerScale(
1132 basegfx::fTools::equalZero(aOutlinerTextSiz.Width()) ? 1.0 : aOutlinerTextSiz.Width(),
1133 basegfx::fTools::equalZero(aOutlinerTextSiz.Height()) ? 1.0 : aOutlinerTextSiz.Height());
1135 // prepare matrices to apply to newly created primitives
1136 basegfx::B2DHomMatrix aNewTransformA;
1137 basegfx::B2DHomMatrix aNewTransformB;
1139 // #i101957# Check for vertical text. If used, aNewTransformA
1140 // needs to translate the text initially around object width to orient
1141 // it relative to the topper right instead of the topper left
1142 const bool bVertical(rSdrStretchTextPrimitive.getOutlinerParaObject().IsVertical());
1144 if(bVertical)
1146 aNewTransformA.translate(aScale.getX(), 0.0);
1149 // calculate global char stretching scale parameters. Use non-mirrored sizes
1150 // to layout without mirroring
1151 const double fScaleX(fabs(aScale.getX()) / aOutlinerScale.getX());
1152 const double fScaleY(fabs(aScale.getY()) / aOutlinerScale.getY());
1153 rOutliner.SetGlobalCharStretching((sal_Int16)FRound(fScaleX * 100.0), (sal_Int16)FRound(fScaleY * 100.0));
1155 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1156 // move the null point which was top left to bottom right.
1157 const bool bMirrorX(basegfx::fTools::less(aScale.getX(), 0.0));
1158 const bool bMirrorY(basegfx::fTools::less(aScale.getY(), 0.0));
1159 aNewTransformB.scale(bMirrorX ? -1.0 : 1.0, bMirrorY ? -1.0 : 1.0);
1161 // in-between the translations of the single primitives will take place. Afterwards,
1162 // the object's transformations need to be applied
1163 aNewTransformB.shearX(fShearX);
1164 aNewTransformB.rotate(fRotate);
1165 aNewTransformB.translate(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();
1179 //////////////////////////////////////////////////////////////////////////////
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 milimeter
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
1417 //////////////////////////////////////////////////////////////////////////////
1418 // eof