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>
61 #include <drawinglayer/geometry/viewinformation2d.hxx>
62 #include <svx/outlobj.hxx>
64 //////////////////////////////////////////////////////////////////////////////
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
]);
82 static drawinglayer::primitive2d::FontUnderline
mapTextLineStyle(FontUnderline 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
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
);
145 impTextBreakupHandler(SdrOutliner
& rOutliner
)
146 : maTextPortionPrimitives(),
148 maParagraphPrimitives(),
149 mrOutliner(rOutliner
),
157 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix
& rNewTransformA
, const basegfx::B2DHomMatrix
& rNewTransformB
, const basegfx::B2DVector
& 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())
205 if((LANGUAGE_JAPANESE
== rFont
.GetLanguage()) || (LANGUAGE_JAPANESE
== rFont
.GetCJKContextLanguage()))
207 // the underline is right for Japanese only
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(
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
);
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
)
252 else if(DFLT_ESC_AUTO_SUB
== nEsc
)
266 const double fEscapement(nEsc
/ -100.0);
267 aNewTransform
.translate(0.0, fEscapement
* aFontScaling
.getY());
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
379 rInfo
.mpLocale
? *rInfo
.mpLocale
: ::com::sun::star::lang::Locale(),
382 // attributes for TextDecoratedPortionPrimitive2D
398 // TextSimplePortionPrimitive2D is enough
399 pNewPrimitive
= new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
406 rInfo
.mpLocale
? *rInfo
.mpLocale
: ::com::sun::star::lang::Locale(),
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
);
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
))
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
;
477 maTextPortionPrimitives
.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
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
;
503 aSequence
.realloc(1);
504 aSequence
[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive
);
509 pPrimitive
= new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence
, drawinglayer::primitive2d::FIELD_TYPE_URL
, pURLField
->GetURL());
513 pPrimitive
= new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence
, drawinglayer::primitive2d::FIELD_TYPE_PAGE
, String());
517 pPrimitive
= new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(aSequence
, drawinglayer::primitive2d::FIELD_TYPE_COMMON
, String());
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());
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(
583 rInfo
.maBulletGraphicObject
,
586 // embed in TextHierarchyBulletPrimitive2D
587 const drawinglayer::primitive2d::Primitive2DSequence
aNewSequence(&aNewReference
, 1);
588 drawinglayer::primitive2d::BasePrimitive2D
* pNewPrimitive
= new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(aNewSequence
);
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
);
606 IMPL_LINK(impTextBreakupHandler
, decomposeBlockTextPrimitive
, DrawPortionInfo
*, 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
))
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
))
637 // TopLeft is inside. Get BottomRight and check
638 const basegfx::B2DPoint
aBottomRight(aTextBoundRect
.getMaximum() + aStartPosition
);
640 if(!maClipRange
.isInside(aBottomRight
))
645 // all inside, clip was successful
647 impHandleDrawPortionInfo(*pInfo
);
653 IMPL_LINK(impTextBreakupHandler
, decomposeStretchTextPrimitive
, DrawPortionInfo
*, pInfo
)
657 impHandleDrawPortionInfo(*pInfo
);
663 IMPL_LINK(impTextBreakupHandler
, decomposeContourBulletPrimitive
, DrawBulletInfo
*, pInfo
)
667 impHandleDrawBulletInfo(*pInfo
);
673 IMPL_LINK(impTextBreakupHandler
, decomposeBlockBulletPrimitive
, DrawBulletInfo
*, pInfo
)
677 impHandleDrawBulletInfo(*pInfo
);
683 IMPL_LINK(impTextBreakupHandler
, decomposeStretchBulletPrimitive
, DrawBulletInfo
*, pInfo
)
687 impHandleDrawBulletInfo(*pInfo
);
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
);
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
);
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
);
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
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
);
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
);
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
));
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
);
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
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
;
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
);
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
);
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());
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
);
1172 rOutliner
.SetControlWord(nOriginalControlWord
);
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())
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());
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);
1207 rAnimList
.append(aLoop
);
1209 // add stopped state if loop is not endless
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
);
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
);
1305 if(bVisisbleWhenStopped
)
1307 // add timing for staying at the end
1308 drawinglayer::animation::AnimationEntryFixed
aEnd(ENDLESS_TIME
, 0.5);
1309 rAnimList
.append(aEnd
);
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
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
;
1396 case SDRTEXTANI_SCROLL
:
1398 impCreateScrollTiming(rSet
, rAnimList
, bForward
, fTimeFullPath
, fAnimationDelay
);
1401 case SDRTEXTANI_ALTERNATE
:
1403 double fRelativeTextLength(fTextLength
/ (fFrameLength
+ fTextLength
));
1404 impCreateAlternateTiming(rSet
, rAnimList
, fRelativeTextLength
, bForward
, fTimeFullPath
, fAnimationDelay
);
1407 case SDRTEXTANI_SLIDE
:
1409 impCreateSlideTiming(rSet
, rAnimList
, bForward
, fTimeFullPath
, fAnimationDelay
);
1412 default : break; // SDRTEXTANI_NONE, SDRTEXTANI_BLINK
1417 //////////////////////////////////////////////////////////////////////////////