1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/compatflags.hxx>
21 #include <svx/svdetc.hxx>
22 #include <svx/svdoutl.hxx>
23 #include <svx/svdpage.hxx>
24 #include <svx/svdotext.hxx>
25 #include <svx/svdmodel.hxx>
26 #include <svx/sdasitm.hxx>
27 #include <textchain.hxx>
28 #include <textchainflow.hxx>
29 #include <svx/sdtacitm.hxx>
30 #include <svx/sdtayitm.hxx>
31 #include <svx/sdtaiitm.hxx>
32 #include <svx/sdtaaitm.hxx>
33 #include <svx/xfillit0.hxx>
34 #include <svx/xbtmpit.hxx>
35 #include <basegfx/vector/b2dvector.hxx>
36 #include <sdr/primitive2d/sdrtextprimitive2d.hxx>
37 #include <drawinglayer/primitive2d/textprimitive2d.hxx>
38 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
39 #include <basegfx/range/b2drange.hxx>
40 #include <editeng/eeitem.hxx>
41 #include <editeng/editstat.hxx>
42 #include <tools/helpers.hxx>
43 #include <svl/itemset.hxx>
44 #include <drawinglayer/animation/animationtiming.hxx>
45 #include <basegfx/color/bcolor.hxx>
46 #include <vcl/svapp.hxx>
47 #include <editeng/escapementitem.hxx>
48 #include <editeng/svxenum.hxx>
49 #include <editeng/flditem.hxx>
50 #include <editeng/adjustitem.hxx>
51 #include <drawinglayer/primitive2d/texthierarchyprimitive2d.hxx>
52 #include <drawinglayer/primitive2d/wrongspellprimitive2d.hxx>
53 #include <drawinglayer/primitive2d/graphicprimitive2d.hxx>
54 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
55 #include <drawinglayer/primitive2d/unifiedtransparenceprimitive2d.hxx>
56 #include <svx/unoapi.hxx>
57 #include <drawinglayer/geometry/viewinformation2d.hxx>
58 #include <editeng/outlobj.hxx>
59 #include <basegfx/matrix/b2dhommatrixtools.hxx>
60 #include <sal/log.hxx>
61 #include <osl/diagnose.h>
63 using namespace com::sun::star
;
69 class impTextBreakupHandler
72 drawinglayer::primitive2d::Primitive2DContainer maTextPortionPrimitives
;
73 drawinglayer::primitive2d::Primitive2DContainer maLinePrimitives
;
74 drawinglayer::primitive2d::Primitive2DContainer maParagraphPrimitives
;
76 SdrOutliner
& mrOutliner
;
77 basegfx::B2DHomMatrix maNewTransformA
;
78 basegfx::B2DHomMatrix maNewTransformB
;
80 // the visible area for contour text decomposition
81 basegfx::B2DVector maScale
;
83 // ClipRange for BlockText decomposition; only text portions completely
84 // inside are to be accepted, so this is different from geometric clipping
85 // (which would allow e.g. upper parts of portions to remain). Only used for
86 // BlockText (see there)
87 basegfx::B2DRange maClipRange
;
89 DECL_LINK(decomposeContourTextPrimitive
, DrawPortionInfo
*, void);
90 DECL_LINK(decomposeBlockTextPrimitive
, DrawPortionInfo
*, void);
91 DECL_LINK(decomposeStretchTextPrimitive
, DrawPortionInfo
*, void);
93 DECL_LINK(decomposeContourBulletPrimitive
, DrawBulletInfo
*, void);
94 DECL_LINK(decomposeBlockBulletPrimitive
, DrawBulletInfo
*, void);
95 DECL_LINK(decomposeStretchBulletPrimitive
, DrawBulletInfo
*, void);
97 void impCreateTextPortionPrimitive(const DrawPortionInfo
& rInfo
);
98 static rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D
* pPrimitive
, const DrawPortionInfo
& rInfo
);
99 void impFlushTextPortionPrimitivesToLinePrimitives();
100 void impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara
);
101 void impHandleDrawPortionInfo(const DrawPortionInfo
& rInfo
);
102 void impHandleDrawBulletInfo(const DrawBulletInfo
& rInfo
);
105 explicit impTextBreakupHandler(SdrOutliner
& rOutliner
)
106 : mrOutliner(rOutliner
)
110 void decomposeContourTextPrimitive(const basegfx::B2DHomMatrix
& rNewTransformA
, const basegfx::B2DHomMatrix
& rNewTransformB
, const basegfx::B2DVector
& rScale
)
113 maNewTransformA
= rNewTransformA
;
114 maNewTransformB
= rNewTransformB
;
115 mrOutliner
.SetDrawPortionHdl(LINK(this, impTextBreakupHandler
, decomposeContourTextPrimitive
));
116 mrOutliner
.SetDrawBulletHdl(LINK(this, impTextBreakupHandler
, decomposeContourBulletPrimitive
));
117 mrOutliner
.StripPortions();
118 mrOutliner
.SetDrawPortionHdl(Link
<DrawPortionInfo
*,void>());
119 mrOutliner
.SetDrawBulletHdl(Link
<DrawBulletInfo
*,void>());
122 void decomposeBlockTextPrimitive(
123 const basegfx::B2DHomMatrix
& rNewTransformA
,
124 const basegfx::B2DHomMatrix
& rNewTransformB
,
125 const basegfx::B2DRange
& rClipRange
)
127 maNewTransformA
= rNewTransformA
;
128 maNewTransformB
= rNewTransformB
;
129 maClipRange
= rClipRange
;
130 mrOutliner
.SetDrawPortionHdl(LINK(this, impTextBreakupHandler
, decomposeBlockTextPrimitive
));
131 mrOutliner
.SetDrawBulletHdl(LINK(this, impTextBreakupHandler
, decomposeBlockBulletPrimitive
));
132 mrOutliner
.StripPortions();
133 mrOutliner
.SetDrawPortionHdl(Link
<DrawPortionInfo
*,void>());
134 mrOutliner
.SetDrawBulletHdl(Link
<DrawBulletInfo
*,void>());
137 void decomposeStretchTextPrimitive(const basegfx::B2DHomMatrix
& rNewTransformA
, const basegfx::B2DHomMatrix
& rNewTransformB
)
139 maNewTransformA
= rNewTransformA
;
140 maNewTransformB
= rNewTransformB
;
141 mrOutliner
.SetDrawPortionHdl(LINK(this, impTextBreakupHandler
, decomposeStretchTextPrimitive
));
142 mrOutliner
.SetDrawBulletHdl(LINK(this, impTextBreakupHandler
, decomposeStretchBulletPrimitive
));
143 mrOutliner
.StripPortions();
144 mrOutliner
.SetDrawPortionHdl(Link
<DrawPortionInfo
*,void>());
145 mrOutliner
.SetDrawBulletHdl(Link
<DrawBulletInfo
*,void>());
148 drawinglayer::primitive2d::Primitive2DContainer
extractPrimitive2DSequence();
151 void impTextBreakupHandler::impCreateTextPortionPrimitive(const DrawPortionInfo
& rInfo
)
153 if(rInfo
.maText
.isEmpty() || !rInfo
.mnTextLen
)
156 OUString caseMappedText
= rInfo
.mrFont
.CalcCaseMap( rInfo
.maText
);
157 basegfx::B2DVector aFontScaling
;
158 drawinglayer::attribute::FontAttribute
aFontAttribute(
159 drawinglayer::primitive2d::getFontAttributeFromVclFont(
164 basegfx::B2DHomMatrix aNewTransform
;
166 // add font scale to new transform
167 aNewTransform
.scale(aFontScaling
.getX(), aFontScaling
.getY());
169 // look for proportional font scaling, if necessary, scale accordingly
170 sal_Int8
nPropr(rInfo
.mrFont
.GetPropr());
171 const double fPropFontFactor(nPropr
/ 100.0);
173 aNewTransform
.scale(fPropFontFactor
, fPropFontFactor
);
176 if(rInfo
.mrFont
.GetOrientation())
178 aNewTransform
.rotate(-toRadians(rInfo
.mrFont
.GetOrientation()));
181 // look for escapement, if necessary, translate accordingly
182 if(rInfo
.mrFont
.GetEscapement())
184 sal_Int16
nEsc(rInfo
.mrFont
.GetEscapement());
186 if(DFLT_ESC_AUTO_SUPER
== nEsc
)
188 nEsc
= .8 * (100 - nPropr
);
189 assert (nEsc
== DFLT_ESC_SUPER
&& "I'm sure this formula needs to be changed, but how to confirm that???");
190 nEsc
= DFLT_ESC_SUPER
;
192 else if(DFLT_ESC_AUTO_SUB
== nEsc
)
194 nEsc
= .2 * -(100 - nPropr
);
195 assert (nEsc
== -20 && "I'm sure this formula needs to be changed, but how to confirm that???");
199 if(nEsc
> MAX_ESC_POS
)
203 else if(nEsc
< -MAX_ESC_POS
)
208 const double fEscapement(nEsc
/ -100.0);
209 aNewTransform
.translate(0.0, fEscapement
* aFontScaling
.getY());
213 aNewTransform
*= maNewTransformA
;
215 // apply local offset
216 aNewTransform
.translate(rInfo
.mrStartPos
.X(), rInfo
.mrStartPos
.Y());
218 // also apply embedding object's transform
219 aNewTransform
*= maNewTransformB
;
221 // prepare DXArray content. To make it independent from font size (and such from
222 // the text transformation), scale it to unit coordinates
223 ::std::vector
< double > aDXArray
;
225 if(!rInfo
.mpDXArray
.empty() && rInfo
.mnTextLen
)
227 aDXArray
.reserve(rInfo
.mnTextLen
);
229 for(sal_Int32 a
=0; a
< rInfo
.mnTextLen
; a
++)
231 aDXArray
.push_back(static_cast<double>(rInfo
.mpDXArray
[a
]));
235 ::std::vector
< sal_Bool
> aKashidaArray
;
237 if(!rInfo
.mpKashidaArray
.empty() && rInfo
.mnTextLen
)
239 aKashidaArray
.reserve(rInfo
.mnTextLen
);
241 for(sal_Int32 a
=0; a
< rInfo
.mnTextLen
; a
++)
243 aKashidaArray
.push_back(rInfo
.mpKashidaArray
[a
]);
247 // create complex text primitive and append
248 const Color
aFontColor(rInfo
.mrFont
.GetColor());
249 const basegfx::BColor
aBFontColor(aFontColor
.getBColor());
251 const Color
aTextFillColor(rInfo
.mrFont
.GetFillColor());
253 // prepare wordLineMode (for underline and strikeout)
254 // NOT for bullet texts. It is set (this may be an error by itself), but needs to be suppressed to hinder e.g. '1)'
255 // to be split which would not look like the original
256 const bool bWordLineMode(rInfo
.mrFont
.IsWordLineMode() && !rInfo
.mbEndOfBullet
);
258 // prepare new primitive
259 rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> pNewPrimitive
;
260 const bool bDecoratedIsNeeded(
261 LINESTYLE_NONE
!= rInfo
.mrFont
.GetOverline()
262 || LINESTYLE_NONE
!= rInfo
.mrFont
.GetUnderline()
263 || STRIKEOUT_NONE
!= rInfo
.mrFont
.GetStrikeout()
264 || FontEmphasisMark::NONE
!= (rInfo
.mrFont
.GetEmphasisMark() & FontEmphasisMark::Style
)
265 || FontRelief::NONE
!= rInfo
.mrFont
.GetRelief()
266 || rInfo
.mrFont
.IsShadow()
269 if(bDecoratedIsNeeded
)
271 // TextDecoratedPortionPrimitive2D needed, prepare some more data
272 // get overline and underline color. If it's on automatic (0xffffffff) use FontColor instead
273 const Color
aUnderlineColor(rInfo
.maTextLineColor
);
274 const basegfx::BColor
aBUnderlineColor((aUnderlineColor
== COL_AUTO
) ? aBFontColor
: aUnderlineColor
.getBColor());
275 const Color
aOverlineColor(rInfo
.maOverlineColor
);
276 const basegfx::BColor
aBOverlineColor((aOverlineColor
== COL_AUTO
) ? aBFontColor
: aOverlineColor
.getBColor());
278 // prepare overline and underline data
279 const drawinglayer::primitive2d::TextLine
eFontOverline(
280 drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo
.mrFont
.GetOverline()));
281 const drawinglayer::primitive2d::TextLine
eFontLineStyle(
282 drawinglayer::primitive2d::mapFontLineStyleToTextLine(rInfo
.mrFont
.GetUnderline()));
284 // check UnderlineAbove
285 const bool bUnderlineAbove(
286 drawinglayer::primitive2d::TEXT_LINE_NONE
!= eFontLineStyle
&& rInfo
.mrFont
.IsUnderlineAbove());
288 // prepare strikeout data
289 const drawinglayer::primitive2d::TextStrikeout
eTextStrikeout(
290 drawinglayer::primitive2d::mapFontStrikeoutToTextStrikeout(rInfo
.mrFont
.GetStrikeout()));
292 // prepare emphasis mark data
293 drawinglayer::primitive2d::TextEmphasisMark
eTextEmphasisMark(drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_NONE
);
295 switch(rInfo
.mrFont
.GetEmphasisMark() & FontEmphasisMark::Style
)
297 case FontEmphasisMark::Dot
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DOT
; break;
298 case FontEmphasisMark::Circle
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_CIRCLE
; break;
299 case FontEmphasisMark::Disc
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_DISC
; break;
300 case FontEmphasisMark::Accent
: eTextEmphasisMark
= drawinglayer::primitive2d::TEXT_FONT_EMPHASIS_MARK_ACCENT
; break;
304 const bool bEmphasisMarkAbove(rInfo
.mrFont
.GetEmphasisMark() & FontEmphasisMark::PosAbove
);
305 const bool bEmphasisMarkBelow(rInfo
.mrFont
.GetEmphasisMark() & FontEmphasisMark::PosBelow
);
307 // prepare font relief data
308 drawinglayer::primitive2d::TextRelief
eTextRelief(drawinglayer::primitive2d::TEXT_RELIEF_NONE
);
310 switch(rInfo
.mrFont
.GetRelief())
312 case FontRelief::Embossed
: eTextRelief
= drawinglayer::primitive2d::TEXT_RELIEF_EMBOSSED
; break;
313 case FontRelief::Engraved
: eTextRelief
= drawinglayer::primitive2d::TEXT_RELIEF_ENGRAVED
; break;
314 default : break; // RELIEF_NONE, FontRelief_FORCE_EQUAL_SIZE
317 // prepare shadow/outline data
318 const bool bShadow(rInfo
.mrFont
.IsShadow());
320 // TextDecoratedPortionPrimitive2D is needed, create one
321 pNewPrimitive
= new drawinglayer::primitive2d::TextDecoratedPortionPrimitive2D(
323 // attributes for TextSimplePortionPrimitive2D
328 std::vector(aDXArray
),
329 std::vector(aKashidaArray
),
331 rInfo
.mpLocale
? *rInfo
.mpLocale
: css::lang::Locale(),
335 // attributes for TextDecoratedPortionPrimitive2D
351 // TextSimplePortionPrimitive2D is enough
352 pNewPrimitive
= new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
357 std::vector(aDXArray
),
358 std::vector(aKashidaArray
),
359 std::move(aFontAttribute
),
360 rInfo
.mpLocale
? *rInfo
.mpLocale
: css::lang::Locale(),
367 if (aFontColor
.IsTransparent())
369 // Handle semi-transparent text for both the decorated and simple case here.
370 pNewPrimitive
= new drawinglayer::primitive2d::UnifiedTransparencePrimitive2D(
371 drawinglayer::primitive2d::Primitive2DContainer
{ pNewPrimitive
},
372 (255 - aFontColor
.GetAlpha()) / 255.0);
375 if(rInfo
.mbEndOfBullet
)
377 // embed in TextHierarchyBulletPrimitive2D
378 drawinglayer::primitive2d::Primitive2DReference
aNewReference(pNewPrimitive
);
379 drawinglayer::primitive2d::Primitive2DContainer aNewSequence
{ aNewReference
} ;
380 pNewPrimitive
= new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence
));
383 if(rInfo
.mpFieldData
)
385 pNewPrimitive
= impCheckFieldPrimitive(pNewPrimitive
.get(), rInfo
);
388 maTextPortionPrimitives
.push_back(pNewPrimitive
);
390 // support for WrongSpellVector. Create WrongSpellPrimitives as needed
391 if(!rInfo
.mpWrongSpellVector
|| aDXArray
.empty())
394 const sal_Int32
nSize(rInfo
.mpWrongSpellVector
->size());
395 const sal_Int32
nDXCount(aDXArray
.size());
396 const basegfx::BColor
aSpellColor(1.0, 0.0, 0.0); // red, hard coded
398 for(sal_Int32
a(0); a
< nSize
; a
++)
400 const EEngineData::WrongSpellClass
& rCandidate
= (*rInfo
.mpWrongSpellVector
)[a
];
402 if(rCandidate
.nStart
>= rInfo
.mnTextStart
&& rCandidate
.nEnd
>= rInfo
.mnTextStart
&& rCandidate
.nEnd
> rCandidate
.nStart
)
404 const sal_Int32
nStart(rCandidate
.nStart
- rInfo
.mnTextStart
);
405 const sal_Int32
nEnd(rCandidate
.nEnd
- rInfo
.mnTextStart
);
409 if(nStart
> 0 && nStart
- 1 < nDXCount
)
411 fStart
= aDXArray
[nStart
- 1];
414 if(nEnd
> 0 && nEnd
- 1 < nDXCount
)
416 fEnd
= aDXArray
[nEnd
- 1];
419 if(!basegfx::fTools::equal(fStart
, fEnd
))
424 // When the portion is RTL, mirror the redlining using the
425 // full portion width
426 const double fTextWidth(aDXArray
[aDXArray
.size() - 1]);
428 fStart
= fTextWidth
- fStart
;
429 fEnd
= fTextWidth
- fEnd
;
432 // if start < end, OutputDevice::DrawWaveLine() will
433 // think it is a rotated line, so we swap fStart and
434 // fEnd to avoid this.
435 std::swap(fStart
, fEnd
);
438 // need to take FontScaling out of values; it's already part of
439 // aNewTransform and would be double applied
440 const double fFontScaleX(aFontScaling
.getX() * fPropFontFactor
);
442 if(!basegfx::fTools::equal(fFontScaleX
, 1.0)
443 && !basegfx::fTools::equalZero(fFontScaleX
))
445 fStart
/= fFontScaleX
;
449 maTextPortionPrimitives
.push_back(new drawinglayer::primitive2d::WrongSpellPrimitive2D(
459 rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> impTextBreakupHandler::impCheckFieldPrimitive(drawinglayer::primitive2d::BasePrimitive2D
* pPrimitive
, const DrawPortionInfo
& rInfo
)
461 rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> xRet
= pPrimitive
;
462 if(rInfo
.mpFieldData
)
464 // Support for FIELD_SEQ_BEGIN, FIELD_SEQ_END. If used, create a TextHierarchyFieldPrimitive2D
465 // which holds the field type and, if applicable, the URL
466 const SvxURLField
* pURLField
= dynamic_cast< const SvxURLField
* >(rInfo
.mpFieldData
);
467 const SvxPageField
* pPageField
= dynamic_cast< const SvxPageField
* >(rInfo
.mpFieldData
);
469 // embed current primitive to a sequence
470 drawinglayer::primitive2d::Primitive2DContainer aSequence
;
475 aSequence
[0] = drawinglayer::primitive2d::Primitive2DReference(pPrimitive
);
480 // extended this to hold more of the contents of the original
481 // SvxURLField since that stuff is still used in HitTest and e.g. Calc
482 std::vector
< std::pair
< OUString
, OUString
>> meValues
;
483 meValues
.emplace_back("URL", pURLField
->GetURL());
484 meValues
.emplace_back("Representation", pURLField
->GetRepresentation());
485 meValues
.emplace_back("TargetFrame", pURLField
->GetTargetFrame());
486 meValues
.emplace_back("SvxURLFormat", OUString::number(static_cast<sal_uInt16
>(pURLField
->GetFormat())));
487 xRet
= new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence
), drawinglayer::primitive2d::FIELD_TYPE_URL
, &meValues
);
491 xRet
= new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence
), drawinglayer::primitive2d::FIELD_TYPE_PAGE
);
495 xRet
= new drawinglayer::primitive2d::TextHierarchyFieldPrimitive2D(std::move(aSequence
), drawinglayer::primitive2d::FIELD_TYPE_COMMON
);
502 void impTextBreakupHandler::impFlushTextPortionPrimitivesToLinePrimitives()
504 // only create a line primitive when we had content; there is no need for
505 // empty line primitives (contrary to paragraphs, see below).
506 if(!maTextPortionPrimitives
.empty())
508 maLinePrimitives
.push_back(new drawinglayer::primitive2d::TextHierarchyLinePrimitive2D(std::move(maTextPortionPrimitives
)));
512 void impTextBreakupHandler::impFlushLinePrimitivesToParagraphPrimitives(sal_Int32 nPara
)
514 sal_Int16 nDepth
= mrOutliner
.GetDepth(nPara
);
515 EBulletInfo eInfo
= mrOutliner
.GetBulletInfo(nPara
);
516 // Pass -1 to signal VclMetafileProcessor2D that there is no active
517 // bullets/numbering in this paragraph (i.e. this is normal text)
518 const sal_Int16
nOutlineLevel( eInfo
.bVisible
? nDepth
: -1);
520 // ALWAYS create a paragraph primitive, even when no content was added. This is done to
521 // have the correct paragraph count even with empty paragraphs. Those paragraphs will
522 // have an empty sub-PrimitiveSequence.
523 maParagraphPrimitives
.push_back(
524 new drawinglayer::primitive2d::TextHierarchyParagraphPrimitive2D(
525 std::move(maLinePrimitives
),
529 void impTextBreakupHandler::impHandleDrawPortionInfo(const DrawPortionInfo
& rInfo
)
531 impCreateTextPortionPrimitive(rInfo
);
533 if(rInfo
.mbEndOfLine
|| rInfo
.mbEndOfParagraph
)
535 impFlushTextPortionPrimitivesToLinePrimitives();
538 if(rInfo
.mbEndOfParagraph
)
540 impFlushLinePrimitivesToParagraphPrimitives(rInfo
.mnPara
);
544 void impTextBreakupHandler::impHandleDrawBulletInfo(const DrawBulletInfo
& rInfo
)
546 basegfx::B2DHomMatrix aNewTransform
;
548 // add size to new transform
549 aNewTransform
.scale(rInfo
.maBulletSize
.getWidth(), rInfo
.maBulletSize
.getHeight());
552 aNewTransform
*= maNewTransformA
;
554 // apply local offset
555 aNewTransform
.translate(rInfo
.maBulletPosition
.X(), rInfo
.maBulletPosition
.Y());
557 // also apply embedding object's transform
558 aNewTransform
*= maNewTransformB
;
560 // prepare empty GraphicAttr
561 const GraphicAttr aGraphicAttr
;
563 // create GraphicPrimitive2D
564 const drawinglayer::primitive2d::Primitive2DReference
aNewReference(new drawinglayer::primitive2d::GraphicPrimitive2D(
566 rInfo
.maBulletGraphicObject
,
569 // embed in TextHierarchyBulletPrimitive2D
570 drawinglayer::primitive2d::Primitive2DContainer aNewSequence
{ aNewReference
};
571 rtl::Reference
<drawinglayer::primitive2d::BasePrimitive2D
> pNewPrimitive
= new drawinglayer::primitive2d::TextHierarchyBulletPrimitive2D(std::move(aNewSequence
));
574 maTextPortionPrimitives
.push_back(pNewPrimitive
);
577 IMPL_LINK(impTextBreakupHandler
, decomposeContourTextPrimitive
, DrawPortionInfo
*, pInfo
, void)
579 // for contour text, ignore (clip away) all portions which are below
580 // the visible area given by maScale
581 if(pInfo
&& static_cast<double>(pInfo
->mrStartPos
.Y()) < maScale
.getY())
583 impHandleDrawPortionInfo(*pInfo
);
587 IMPL_LINK(impTextBreakupHandler
, decomposeBlockTextPrimitive
, DrawPortionInfo
*, pInfo
, void)
592 // Is clipping wanted? This is text clipping; only accept a portion
593 // if it's completely in the range
594 if(!maClipRange
.isEmpty())
596 // Test start position first; this allows to not get the text range at
597 // all if text is far outside
598 const basegfx::B2DPoint
aStartPosition(pInfo
->mrStartPos
.X(), pInfo
->mrStartPos
.Y());
600 if(!maClipRange
.isInside(aStartPosition
))
605 // Start position is inside. Get TextBoundRect and TopLeft next
606 drawinglayer::primitive2d::TextLayouterDevice aTextLayouterDevice
;
607 aTextLayouterDevice
.setFont(pInfo
->mrFont
);
609 const basegfx::B2DRange
aTextBoundRect(
610 aTextLayouterDevice
.getTextBoundRect(
611 pInfo
->maText
, pInfo
->mnTextStart
, pInfo
->mnTextLen
));
612 const basegfx::B2DPoint
aTopLeft(aTextBoundRect
.getMinimum() + aStartPosition
);
614 if(!maClipRange
.isInside(aTopLeft
))
619 // TopLeft is inside. Get BottomRight and check
620 const basegfx::B2DPoint
aBottomRight(aTextBoundRect
.getMaximum() + aStartPosition
);
622 if(!maClipRange
.isInside(aBottomRight
))
627 // all inside, clip was successful
629 impHandleDrawPortionInfo(*pInfo
);
632 IMPL_LINK(impTextBreakupHandler
, decomposeStretchTextPrimitive
, DrawPortionInfo
*, pInfo
, void)
636 impHandleDrawPortionInfo(*pInfo
);
640 IMPL_LINK(impTextBreakupHandler
, decomposeContourBulletPrimitive
, DrawBulletInfo
*, pInfo
, void)
644 impHandleDrawBulletInfo(*pInfo
);
648 IMPL_LINK(impTextBreakupHandler
, decomposeBlockBulletPrimitive
, DrawBulletInfo
*, pInfo
, void)
652 impHandleDrawBulletInfo(*pInfo
);
656 IMPL_LINK(impTextBreakupHandler
, decomposeStretchBulletPrimitive
, DrawBulletInfo
*, pInfo
, void)
660 impHandleDrawBulletInfo(*pInfo
);
664 drawinglayer::primitive2d::Primitive2DContainer
impTextBreakupHandler::extractPrimitive2DSequence()
666 if(!maTextPortionPrimitives
.empty())
668 // collect non-closed lines
669 impFlushTextPortionPrimitivesToLinePrimitives();
672 if(!maLinePrimitives
.empty())
674 // collect non-closed paragraphs
675 impFlushLinePrimitivesToParagraphPrimitives(mrOutliner
.GetParagraphCount() - 1);
678 return std::move(maParagraphPrimitives
);
680 } // end of anonymous namespace
683 // primitive decompositions
685 void SdrTextObj::impDecomposeContourTextPrimitive(
686 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
687 const drawinglayer::primitive2d::SdrContourTextPrimitive2D
& rSdrContourTextPrimitive
,
688 const drawinglayer::geometry::ViewInformation2D
& aViewInformation
) const
690 // decompose matrix to have position and size of text
691 basegfx::B2DVector aScale
, aTranslate
;
692 double fRotate
, fShearX
;
693 rSdrContourTextPrimitive
.getObjectTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
);
695 // prepare contour polygon, force to non-mirrored for laying out
696 basegfx::B2DPolyPolygon
aPolyPolygon(rSdrContourTextPrimitive
.getUnitPolyPolygon());
697 aPolyPolygon
.transform(basegfx::utils::createScaleB2DHomMatrix(fabs(aScale
.getX()), fabs(aScale
.getY())));
700 SolarMutexGuard aSolarGuard
;
701 SdrOutliner
& rOutliner
= ImpGetDrawOutliner();
702 const Size aNullSize
;
703 rOutliner
.SetPaperSize(aNullSize
);
704 rOutliner
.SetPolygon(aPolyPolygon
);
705 rOutliner
.SetUpdateLayout(true);
706 rOutliner
.SetText(rSdrContourTextPrimitive
.getOutlinerParaObject());
708 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
709 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
711 // prepare matrices to apply to newly created primitives
712 basegfx::B2DHomMatrix aNewTransformA
;
714 // mirroring. We are now in the polygon sizes. When mirroring in X and Y,
715 // move the null point which was top left to bottom right.
716 const bool bMirrorX(basegfx::fTools::less(aScale
.getX(), 0.0));
717 const bool bMirrorY(basegfx::fTools::less(aScale
.getY(), 0.0));
719 // in-between the translations of the single primitives will take place. Afterwards,
720 // the object's transformations need to be applied
721 const basegfx::B2DHomMatrix
aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
722 bMirrorX
? -1.0 : 1.0, bMirrorY
? -1.0 : 1.0,
723 fShearX
, fRotate
, aTranslate
.getX(), aTranslate
.getY()));
725 // now break up text primitives.
726 impTextBreakupHandler
aConverter(rOutliner
);
727 aConverter
.decomposeContourTextPrimitive(aNewTransformA
, aNewTransformB
, aScale
);
731 rOutliner
.setVisualizedPage(nullptr);
733 rTarget
= aConverter
.extractPrimitive2DSequence();
736 void SdrTextObj::impDecomposeAutoFitTextPrimitive(
737 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
738 const drawinglayer::primitive2d::SdrAutoFitTextPrimitive2D
& rSdrAutofitTextPrimitive
,
739 const drawinglayer::geometry::ViewInformation2D
& aViewInformation
) const
741 // decompose matrix to have position and size of text
742 basegfx::B2DVector aScale
, aTranslate
;
743 double fRotate
, fShearX
;
744 rSdrAutofitTextPrimitive
.getTextRangeTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
);
746 // use B2DRange aAnchorTextRange for calculations
747 basegfx::B2DRange
aAnchorTextRange(aTranslate
);
748 aAnchorTextRange
.expand(aTranslate
+ aScale
);
751 const SfxItemSet
& rTextItemSet
= rSdrAutofitTextPrimitive
.getSdrText()->GetItemSet();
752 SolarMutexGuard aSolarGuard
;
753 SdrOutliner
& rOutliner
= ImpGetDrawOutliner();
754 SdrTextVertAdjust eVAdj
= GetTextVerticalAdjust(rTextItemSet
);
755 SdrTextHorzAdjust eHAdj
= GetTextHorizontalAdjust(rTextItemSet
);
756 const EEControlBits
nOriginalControlWord(rOutliner
.GetControlWord());
757 const Size aNullSize
;
759 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
760 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
762 rOutliner
.SetControlWord(nOriginalControlWord
|EEControlBits::AUTOPAGESIZE
|EEControlBits::STRETCHING
);
763 rOutliner
.SetMinAutoPaperSize(aNullSize
);
764 rOutliner
.SetMaxAutoPaperSize(Size(1000000,1000000));
766 // That color needs to be restored on leaving this method
767 Color
aOriginalBackColor(rOutliner
.GetBackgroundColor());
768 setSuitableOutlinerBg(rOutliner
);
770 // add one to range sizes to get back to the old Rectangle and outliner measurements
771 const sal_uInt32
nAnchorTextWidth(FRound(aAnchorTextRange
.getWidth() + 1));
772 const sal_uInt32
nAnchorTextHeight(FRound(aAnchorTextRange
.getHeight() + 1));
773 const OutlinerParaObject
* pOutlinerParaObject
= rSdrAutofitTextPrimitive
.getSdrText()->GetOutlinerParaObject();
774 OSL_ENSURE(pOutlinerParaObject
, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
775 const bool bVerticalWriting(pOutlinerParaObject
->IsEffectivelyVertical());
776 const bool bTopToBottom(pOutlinerParaObject
->IsTopToBottom());
777 const Size
aAnchorTextSize(Size(nAnchorTextWidth
, nAnchorTextHeight
));
779 if(rSdrAutofitTextPrimitive
.getWordWrap() || IsTextFrame())
781 rOutliner
.SetMaxAutoPaperSize(aAnchorTextSize
);
784 if(SDRTEXTHORZADJUST_BLOCK
== eHAdj
&& !bVerticalWriting
)
786 rOutliner
.SetMinAutoPaperSize(Size(nAnchorTextWidth
, 0));
787 rOutliner
.SetMinColumnWrapHeight(nAnchorTextHeight
);
790 if(SDRTEXTVERTADJUST_BLOCK
== eVAdj
&& bVerticalWriting
)
792 rOutliner
.SetMinAutoPaperSize(Size(0, nAnchorTextHeight
));
793 rOutliner
.SetMinColumnWrapHeight(nAnchorTextWidth
);
796 rOutliner
.SetPaperSize(aNullSize
);
797 rOutliner
.SetUpdateLayout(true);
798 rOutliner
.SetText(*pOutlinerParaObject
);
799 ImpAutoFitText(rOutliner
,aAnchorTextSize
,bVerticalWriting
);
801 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
802 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
804 // now get back the layouted text size from outliner
805 const Size
aOutlinerTextSize(rOutliner
.GetPaperSize());
806 const basegfx::B2DVector
aOutlinerScale(aOutlinerTextSize
.Width(), aOutlinerTextSize
.Height());
807 basegfx::B2DVector
aAdjustTranslate(0.0, 0.0);
809 // correct horizontal translation using the now known text size
810 if(SDRTEXTHORZADJUST_CENTER
== eHAdj
|| SDRTEXTHORZADJUST_RIGHT
== eHAdj
)
812 const double fFree(aAnchorTextRange
.getWidth() - aOutlinerScale
.getX());
814 if(SDRTEXTHORZADJUST_CENTER
== eHAdj
)
816 aAdjustTranslate
.setX(fFree
/ 2.0);
819 if(SDRTEXTHORZADJUST_RIGHT
== eHAdj
)
821 aAdjustTranslate
.setX(fFree
);
825 // correct vertical translation using the now known text size
826 if(SDRTEXTVERTADJUST_CENTER
== eVAdj
|| SDRTEXTVERTADJUST_BOTTOM
== eVAdj
)
828 const double fFree(aAnchorTextRange
.getHeight() - aOutlinerScale
.getY());
830 if(SDRTEXTVERTADJUST_CENTER
== eVAdj
)
832 aAdjustTranslate
.setY(fFree
/ 2.0);
835 if(SDRTEXTVERTADJUST_BOTTOM
== eVAdj
)
837 aAdjustTranslate
.setY(fFree
);
841 // prepare matrices to apply to newly created primitives. aNewTransformA
842 // will get coordinates in aOutlinerScale size and positive in X, Y.
843 basegfx::B2DHomMatrix aNewTransformA
;
844 basegfx::B2DHomMatrix aNewTransformB
;
846 // translate relative to given primitive to get same rotation and shear
847 // as the master shape we are working on. For vertical, use the top-right
849 const double fStartInX(bVerticalWriting
&& bTopToBottom
? aAdjustTranslate
.getX() + aOutlinerScale
.getX() : aAdjustTranslate
.getX());
850 const double fStartInY(bVerticalWriting
&& !bTopToBottom
? aAdjustTranslate
.getY() + aOutlinerScale
.getY() : aAdjustTranslate
.getY());
851 aNewTransformA
.translate(fStartInX
, fStartInY
);
853 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
854 // move the null point which was top left to bottom right.
855 const bool bMirrorX(basegfx::fTools::less(aScale
.getX(), 0.0));
856 const bool bMirrorY(basegfx::fTools::less(aScale
.getY(), 0.0));
857 aNewTransformB
.scale(bMirrorX
? -1.0 : 1.0, bMirrorY
? -1.0 : 1.0);
859 // in-between the translations of the single primitives will take place. Afterwards,
860 // the object's transformations need to be applied
861 aNewTransformB
.shearX(fShearX
);
862 aNewTransformB
.rotate(fRotate
);
863 aNewTransformB
.translate(aTranslate
.getX(), aTranslate
.getY());
865 basegfx::B2DRange aClipRange
;
867 // now break up text primitives.
868 impTextBreakupHandler
aConverter(rOutliner
);
869 aConverter
.decomposeBlockTextPrimitive(aNewTransformA
, aNewTransformB
, aClipRange
);
872 rOutliner
.SetBackgroundColor(aOriginalBackColor
);
874 rOutliner
.setVisualizedPage(nullptr);
875 rOutliner
.SetControlWord(nOriginalControlWord
);
877 rTarget
= aConverter
.extractPrimitive2DSequence();
880 // Resolves: fdo#35779 set background color of this shape as the editeng background if there
881 // is one. Check the shape itself, then the host page, then that page's master page.
882 bool SdrObject::setSuitableOutlinerBg(::Outliner
& rOutliner
) const
884 const SfxItemSet
* pBackgroundFillSet
= getBackgroundFillSet();
885 if (drawing::FillStyle_NONE
!= pBackgroundFillSet
->Get(XATTR_FILLSTYLE
).GetValue())
887 Color
aColor(rOutliner
.GetBackgroundColor());
888 GetDraftFillColor(*pBackgroundFillSet
, aColor
);
889 rOutliner
.SetBackgroundColor(aColor
);
895 const SfxItemSet
* SdrObject::getBackgroundFillSet() const
897 const SfxItemSet
* pBackgroundFillSet
= &GetObjectItemSet();
899 if (drawing::FillStyle_NONE
== pBackgroundFillSet
->Get(XATTR_FILLSTYLE
).GetValue())
901 SdrPage
* pOwnerPage(getSdrPageFromSdrObject());
904 pBackgroundFillSet
= &pOwnerPage
->getSdrPageProperties().GetItemSet();
906 if (drawing::FillStyle_NONE
== pBackgroundFillSet
->Get(XATTR_FILLSTYLE
).GetValue())
908 if (!pOwnerPage
->IsMasterPage() && pOwnerPage
->TRG_HasMasterPage())
910 pBackgroundFillSet
= &pOwnerPage
->TRG_GetMasterPage().getSdrPageProperties().GetItemSet();
915 return pBackgroundFillSet
;
918 const Graphic
* SdrObject::getFillGraphic() const
920 if(IsGroupObject()) // Doesn't make sense, and GetObjectItemSet() asserts.
922 const SfxItemSet
* pBackgroundFillSet
= getBackgroundFillSet();
923 if (drawing::FillStyle_BITMAP
!= pBackgroundFillSet
->Get(XATTR_FILLSTYLE
).GetValue())
925 return &pBackgroundFillSet
->Get(XATTR_FILLBITMAP
).GetGraphicObject().GetGraphic();
928 void SdrTextObj::impDecomposeBlockTextPrimitive(
929 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
930 const drawinglayer::primitive2d::SdrBlockTextPrimitive2D
& rSdrBlockTextPrimitive
,
931 const drawinglayer::geometry::ViewInformation2D
& aViewInformation
) const
933 // decompose matrix to have position and size of text
934 basegfx::B2DVector aScale
, aTranslate
;
935 double fRotate
, fShearX
;
936 rSdrBlockTextPrimitive
.getTextRangeTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
);
938 // use B2DRange aAnchorTextRange for calculations
939 basegfx::B2DRange
aAnchorTextRange(aTranslate
);
940 aAnchorTextRange
.expand(aTranslate
+ aScale
);
943 const bool bIsCell(rSdrBlockTextPrimitive
.getCellText());
944 SolarMutexGuard aSolarGuard
;
945 SdrOutliner
& rOutliner
= ImpGetDrawOutliner();
946 SdrTextHorzAdjust eHAdj
= rSdrBlockTextPrimitive
.getSdrTextHorzAdjust();
947 SdrTextVertAdjust eVAdj
= rSdrBlockTextPrimitive
.getSdrTextVertAdjust();
948 const EEControlBits
nOriginalControlWord(rOutliner
.GetControlWord());
949 const Size aNullSize
;
951 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
952 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
953 rOutliner
.SetFixedCellHeight(rSdrBlockTextPrimitive
.isFixedCellHeight());
954 rOutliner
.SetControlWord(nOriginalControlWord
|EEControlBits::AUTOPAGESIZE
);
955 rOutliner
.SetMinAutoPaperSize(aNullSize
);
956 rOutliner
.SetMaxAutoPaperSize(Size(1000000,1000000));
958 // That color needs to be restored on leaving this method
959 Color
aOriginalBackColor(rOutliner
.GetBackgroundColor());
960 setSuitableOutlinerBg(rOutliner
);
962 // add one to range sizes to get back to the old Rectangle and outliner measurements
963 const sal_uInt32
nAnchorTextWidth(FRound(aAnchorTextRange
.getWidth() + 1));
964 const sal_uInt32
nAnchorTextHeight(FRound(aAnchorTextRange
.getHeight() + 1));
965 const bool bVerticalWriting(rSdrBlockTextPrimitive
.getOutlinerParaObject().IsEffectivelyVertical());
966 const bool bTopToBottom(rSdrBlockTextPrimitive
.getOutlinerParaObject().IsTopToBottom());
967 const Size
aAnchorTextSize(Size(nAnchorTextWidth
, nAnchorTextHeight
));
971 // cell text is formatted neither like a text object nor like an object
972 // text, so use a special setup here
973 rOutliner
.SetMaxAutoPaperSize(aAnchorTextSize
);
975 // #i106214# To work with an unchangeable PaperSize (CellSize in
976 // this case) Set(Min|Max)AutoPaperSize and SetPaperSize have to be used.
977 // #i106214# This was not completely correct; to still measure the real
978 // text height to allow vertical adjust (and vice versa for VerticalWritintg)
979 // only one aspect has to be set, but the other one to zero
982 // measure the horizontal text size
983 rOutliner
.SetMinAutoPaperSize(Size(0, aAnchorTextSize
.Height()));
987 // measure the vertical text size
988 rOutliner
.SetMinAutoPaperSize(Size(aAnchorTextSize
.Width(), 0));
991 rOutliner
.SetPaperSize(aAnchorTextSize
);
992 rOutliner
.SetUpdateLayout(true);
993 rOutliner
.SetText(rSdrBlockTextPrimitive
.getOutlinerParaObject());
997 // check if block text is used (only one of them can be true)
998 const bool bHorizontalIsBlock(SDRTEXTHORZADJUST_BLOCK
== eHAdj
&& !bVerticalWriting
);
999 const bool bVerticalIsBlock(SDRTEXTVERTADJUST_BLOCK
== eVAdj
&& bVerticalWriting
);
1001 // set minimal paper size horizontally/vertically if needed
1002 if(bHorizontalIsBlock
)
1004 rOutliner
.SetMinAutoPaperSize(Size(nAnchorTextWidth
, 0));
1005 rOutliner
.SetMinColumnWrapHeight(nAnchorTextHeight
);
1007 else if(bVerticalIsBlock
)
1009 rOutliner
.SetMinAutoPaperSize(Size(0, nAnchorTextHeight
));
1010 rOutliner
.SetMinColumnWrapHeight(nAnchorTextWidth
);
1013 if((rSdrBlockTextPrimitive
.getWordWrap() || IsTextFrame()) && !rSdrBlockTextPrimitive
.getUnlimitedPage())
1015 // #i103454# maximal paper size hor/ver needs to be limited to text
1016 // frame size. If it's block text, still allow the 'other' direction
1017 // to grow to get a correct real text size when using GetPaperSize().
1018 // When just using aAnchorTextSize as maximum, GetPaperSize()
1019 // would just return aAnchorTextSize again: this means, the wanted
1020 // 'measurement' of the real size of block text would not work
1021 Size
aMaxAutoPaperSize(aAnchorTextSize
);
1023 // Usual processing - always grow in one of directions
1024 bool bAllowGrowVertical
= !bVerticalWriting
;
1025 bool bAllowGrowHorizontal
= bVerticalWriting
;
1027 // Compatibility mode for tdf#99729
1028 if (getSdrModelFromSdrObject().GetCompatibilityFlag(
1029 SdrCompatibilityFlag::AnchoredTextOverflowLegacy
))
1031 bAllowGrowVertical
= bHorizontalIsBlock
;
1032 bAllowGrowHorizontal
= bVerticalIsBlock
;
1035 if (bAllowGrowVertical
)
1037 // allow to grow vertical for horizontal texts
1038 aMaxAutoPaperSize
.setHeight(1000000);
1040 else if (bAllowGrowHorizontal
)
1042 // allow to grow horizontal for vertical texts
1043 aMaxAutoPaperSize
.setWidth(1000000);
1046 rOutliner
.SetMaxAutoPaperSize(aMaxAutoPaperSize
);
1049 rOutliner
.SetPaperSize(aNullSize
);
1050 rOutliner
.SetUpdateLayout(true);
1051 rOutliner
.SetText(rSdrBlockTextPrimitive
.getOutlinerParaObject());
1054 rOutliner
.SetControlWord(nOriginalControlWord
);
1056 // now get back the layouted text size from outliner
1057 const Size
aOutlinerTextSize(rOutliner
.GetPaperSize());
1058 const basegfx::B2DVector
aOutlinerScale(aOutlinerTextSize
.Width(), aOutlinerTextSize
.Height());
1059 basegfx::B2DVector
aAdjustTranslate(0.0, 0.0);
1061 // For draw objects containing text correct hor/ver alignment if text is bigger
1062 // than the object itself. Without that correction, the text would always be
1063 // formatted to the left edge (or top edge when vertical) of the draw object.
1064 if(!IsTextFrame() && !bIsCell
)
1066 if(aAnchorTextRange
.getWidth() < aOutlinerScale
.getX() && !bVerticalWriting
)
1068 // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
1069 // else the alignment is wanted.
1070 if(SDRTEXTHORZADJUST_BLOCK
== eHAdj
)
1072 SvxAdjust eAdjust
= GetObjectItemSet().Get(EE_PARA_JUST
).GetAdjust();
1075 case SvxAdjust::Left
: eHAdj
= SDRTEXTHORZADJUST_LEFT
; break;
1076 case SvxAdjust::Right
: eHAdj
= SDRTEXTHORZADJUST_RIGHT
; break;
1077 case SvxAdjust::Center
: eHAdj
= SDRTEXTHORZADJUST_CENTER
; break;
1083 if(aAnchorTextRange
.getHeight() < aOutlinerScale
.getY() && bVerticalWriting
)
1085 // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
1086 // else the alignment is wanted.
1087 if(SDRTEXTVERTADJUST_BLOCK
== eVAdj
)
1089 eVAdj
= SDRTEXTVERTADJUST_CENTER
;
1094 // correct horizontal translation using the now known text size
1095 if(SDRTEXTHORZADJUST_CENTER
== eHAdj
|| SDRTEXTHORZADJUST_RIGHT
== eHAdj
)
1097 const double fFree(aAnchorTextRange
.getWidth() - aOutlinerScale
.getX());
1099 if(SDRTEXTHORZADJUST_CENTER
== eHAdj
)
1101 aAdjustTranslate
.setX(fFree
/ 2.0);
1104 if(SDRTEXTHORZADJUST_RIGHT
== eHAdj
)
1106 aAdjustTranslate
.setX(fFree
);
1110 const double fFreeVerticalSpace(aAnchorTextRange
.getHeight() - aOutlinerScale
.getY());
1111 bool bClipVerticalTextOverflow
= fFreeVerticalSpace
< 0
1112 && GetObjectItemSet().Get(SDRATTR_TEXT_CLIPVERTOVERFLOW
).GetValue();
1113 // correct vertical translation using the now known text size
1114 if((SDRTEXTVERTADJUST_CENTER
== eVAdj
|| SDRTEXTVERTADJUST_BOTTOM
== eVAdj
)
1115 && !bClipVerticalTextOverflow
)
1117 if(SDRTEXTVERTADJUST_CENTER
== eVAdj
)
1119 aAdjustTranslate
.setY(fFreeVerticalSpace
/ 2.0);
1122 if(SDRTEXTVERTADJUST_BOTTOM
== eVAdj
)
1124 aAdjustTranslate
.setY(fFreeVerticalSpace
);
1128 // prepare matrices to apply to newly created primitives. aNewTransformA
1129 // will get coordinates in aOutlinerScale size and positive in X, Y.
1130 // Translate relative to given primitive to get same rotation and shear
1131 // as the master shape we are working on. For vertical, use the top-right
1133 const double fStartInX(bVerticalWriting
&& bTopToBottom
? aAdjustTranslate
.getX() + aOutlinerScale
.getX() : aAdjustTranslate
.getX());
1134 const double fStartInY(bVerticalWriting
&& !bTopToBottom
? aAdjustTranslate
.getY() + aOutlinerScale
.getY() : aAdjustTranslate
.getY());
1135 basegfx::B2DHomMatrix
aNewTransformA(basegfx::utils::createTranslateB2DHomMatrix(fStartInX
, fStartInY
));
1137 // Apply the camera rotation. It have to be applied after adjustment of
1138 // the text (top, bottom, center, left, right).
1139 if(GetCameraZRotation() != 0)
1141 // First find the text rect.
1142 basegfx::B2DRange
aTextRectangle(/*x1=*/aTranslate
.getX() + aAdjustTranslate
.getX(),
1143 /*y1=*/aTranslate
.getY() + aAdjustTranslate
.getY(),
1144 /*x2=*/aTranslate
.getX() + aOutlinerScale
.getX() - aAdjustTranslate
.getX(),
1145 /*y2=*/aTranslate
.getY() + aOutlinerScale
.getY() - aAdjustTranslate
.getY());
1147 // Rotate the text from the center point.
1148 basegfx::B2DVector
aTranslateToCenter(aTextRectangle
.getWidth() / 2, aTextRectangle
.getHeight() / 2);
1149 aNewTransformA
.translate(-aTranslateToCenter
.getX(), -aTranslateToCenter
.getY());
1150 aNewTransformA
.rotate(basegfx::deg2rad(360.0 - GetCameraZRotation() ));
1151 aNewTransformA
.translate(aTranslateToCenter
.getX(), aTranslateToCenter
.getY());
1154 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1155 // move the null point which was top left to bottom right.
1156 const bool bMirrorX(basegfx::fTools::less(aScale
.getX(), 0.0));
1157 const bool bMirrorY(basegfx::fTools::less(aScale
.getY(), 0.0));
1159 // in-between the translations of the single primitives will take place. Afterwards,
1160 // the object's transformations need to be applied
1161 const basegfx::B2DHomMatrix
aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
1162 bMirrorX
? -1.0 : 1.0, bMirrorY
? -1.0 : 1.0,
1163 fShearX
, fRotate
, aTranslate
.getX(), aTranslate
.getY()));
1166 // create ClipRange (if needed)
1167 basegfx::B2DRange aClipRange
;
1168 if(bClipVerticalTextOverflow
)
1169 aClipRange
= {0, 0, std::numeric_limits
<double>::max(), aAnchorTextRange
.getHeight()};
1171 // now break up text primitives.
1172 impTextBreakupHandler
aConverter(rOutliner
);
1173 aConverter
.decomposeBlockTextPrimitive(aNewTransformA
, aNewTransformB
, aClipRange
);
1176 rOutliner
.SetBackgroundColor(aOriginalBackColor
);
1178 rOutliner
.setVisualizedPage(nullptr);
1180 rTarget
= aConverter
.extractPrimitive2DSequence();
1183 void SdrTextObj::impDecomposeStretchTextPrimitive(
1184 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
1185 const drawinglayer::primitive2d::SdrStretchTextPrimitive2D
& rSdrStretchTextPrimitive
,
1186 const drawinglayer::geometry::ViewInformation2D
& aViewInformation
) const
1188 // decompose matrix to have position and size of text
1189 basegfx::B2DVector aScale
, aTranslate
;
1190 double fRotate
, fShearX
;
1191 rSdrStretchTextPrimitive
.getTextRangeTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
);
1194 SolarMutexGuard aSolarGuard
;
1195 SdrOutliner
& rOutliner
= ImpGetDrawOutliner();
1196 const EEControlBits
nOriginalControlWord(rOutliner
.GetControlWord());
1197 const Size aNullSize
;
1199 rOutliner
.SetControlWord(nOriginalControlWord
|EEControlBits::STRETCHING
|EEControlBits::AUTOPAGESIZE
);
1200 rOutliner
.SetFixedCellHeight(rSdrStretchTextPrimitive
.isFixedCellHeight());
1201 rOutliner
.SetMinAutoPaperSize(aNullSize
);
1202 rOutliner
.SetMaxAutoPaperSize(Size(1000000,1000000));
1203 rOutliner
.SetPaperSize(aNullSize
);
1204 rOutliner
.SetUpdateLayout(true);
1205 rOutliner
.SetText(rSdrStretchTextPrimitive
.getOutlinerParaObject());
1207 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1208 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
1210 // now get back the laid out text size from outliner
1211 const Size
aOutlinerTextSize(rOutliner
.CalcTextSize());
1212 const basegfx::B2DVector
aOutlinerScale(
1213 aOutlinerTextSize
.Width() == tools::Long(0) ? 1.0 : aOutlinerTextSize
.Width(),
1214 aOutlinerTextSize
.Height() == tools::Long(0) ? 1.0 : aOutlinerTextSize
.Height());
1216 // prepare matrices to apply to newly created primitives
1217 basegfx::B2DHomMatrix aNewTransformA
;
1219 // #i101957# Check for vertical text. If used, aNewTransformA
1220 // needs to translate the text initially around object width to orient
1221 // it relative to the topper right instead of the topper left
1222 const bool bVertical(rSdrStretchTextPrimitive
.getOutlinerParaObject().IsEffectivelyVertical());
1223 const bool bTopToBottom(rSdrStretchTextPrimitive
.getOutlinerParaObject().IsTopToBottom());
1228 aNewTransformA
.translate(aScale
.getX(), 0.0);
1230 aNewTransformA
.translate(0.0, aScale
.getY());
1233 // calculate global char stretching scale parameters. Use non-mirrored sizes
1234 // to layout without mirroring
1235 const double fScaleX(fabs(aScale
.getX()) / aOutlinerScale
.getX());
1236 const double fScaleY(fabs(aScale
.getY()) / aOutlinerScale
.getY());
1237 rOutliner
.setGlobalScale(fScaleX
* 100.0, fScaleY
* 100.0, 100.0, 100.0);
1239 // When mirroring in X and Y,
1240 // move the null point which was top left to bottom right.
1241 const bool bMirrorX(basegfx::fTools::less(aScale
.getX(), 0.0));
1242 const bool bMirrorY(basegfx::fTools::less(aScale
.getY(), 0.0));
1244 // in-between the translations of the single primitives will take place. Afterwards,
1245 // the object's transformations need to be applied
1246 const basegfx::B2DHomMatrix
aNewTransformB(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
1247 bMirrorX
? -1.0 : 1.0, bMirrorY
? -1.0 : 1.0,
1248 fShearX
, fRotate
, aTranslate
.getX(), aTranslate
.getY()));
1250 // now break up text primitives.
1251 impTextBreakupHandler
aConverter(rOutliner
);
1252 aConverter
.decomposeStretchTextPrimitive(aNewTransformA
, aNewTransformB
);
1255 rOutliner
.SetControlWord(nOriginalControlWord
);
1257 rOutliner
.setVisualizedPage(nullptr);
1259 rTarget
= aConverter
.extractPrimitive2DSequence();
1263 // timing generators
1264 #define ENDLESS_LOOP (0xffffffff)
1265 #define ENDLESS_TIME (double(0xffffffff))
1266 #define PIXEL_DPI (96.0)
1268 void SdrTextObj::impGetBlinkTextTiming(drawinglayer::animation::AnimationEntryList
& rAnimList
) const
1270 if(SdrTextAniKind::Blink
!= GetTextAniKind())
1274 const SfxItemSet
& rSet
= GetObjectItemSet();
1275 const sal_uInt32
nRepeat(static_cast<sal_uInt32
>(rSet
.Get(SDRATTR_TEXT_ANICOUNT
).GetValue()));
1276 double fDelay(static_cast<double>(rSet
.Get(SDRATTR_TEXT_ANIDELAY
).GetValue()));
1284 // prepare loop and add
1285 drawinglayer::animation::AnimationEntryLoop
aLoop(nRepeat
? nRepeat
: ENDLESS_LOOP
);
1286 drawinglayer::animation::AnimationEntryFixed
aStart(fDelay
, 0.0);
1287 aLoop
.append(aStart
);
1288 drawinglayer::animation::AnimationEntryFixed
aEnd(fDelay
, 1.0);
1290 rAnimList
.append(aLoop
);
1292 // add stopped state if loop is not endless
1295 bool bVisibleWhenStopped(rSet
.Get(SDRATTR_TEXT_ANISTOPINSIDE
).GetValue());
1296 drawinglayer::animation::AnimationEntryFixed
aStop(ENDLESS_TIME
, bVisibleWhenStopped
? 0.0 : 1.0);
1297 rAnimList
.append(aStop
);
1301 static void impCreateScrollTiming(const SfxItemSet
& rSet
, drawinglayer::animation::AnimationEntryList
& rAnimList
, bool bForward
, double fTimeFullPath
, double fFrequency
)
1303 bool bVisibleWhenStopped(rSet
.Get(SDRATTR_TEXT_ANISTOPINSIDE
).GetValue());
1304 bool bVisibleWhenStarted(rSet
.Get(SDRATTR_TEXT_ANISTARTINSIDE
).GetValue());
1305 const sal_uInt32
nRepeat(rSet
.Get(SDRATTR_TEXT_ANICOUNT
).GetValue());
1307 if(bVisibleWhenStarted
)
1309 // move from center to outside
1310 drawinglayer::animation::AnimationEntryLinear
aInOut(fTimeFullPath
* 0.5, fFrequency
, 0.5, bForward
? 1.0 : 0.0);
1311 rAnimList
.append(aInOut
);
1314 // loop. In loop, move through
1315 drawinglayer::animation::AnimationEntryLoop
aLoop(nRepeat
? nRepeat
: ENDLESS_LOOP
);
1316 drawinglayer::animation::AnimationEntryLinear
aThrough(fTimeFullPath
, fFrequency
, bForward
? 0.0 : 1.0, bForward
? 1.0 : 0.0);
1317 aLoop
.append(aThrough
);
1318 rAnimList
.append(aLoop
);
1320 if(0 != nRepeat
&& bVisibleWhenStopped
)
1322 // move from outside to center
1323 drawinglayer::animation::AnimationEntryLinear
aOutIn(fTimeFullPath
* 0.5, fFrequency
, bForward
? 0.0 : 1.0, 0.5);
1324 rAnimList
.append(aOutIn
);
1326 // add timing for staying at the end
1327 drawinglayer::animation::AnimationEntryFixed
aEnd(ENDLESS_TIME
, 0.5);
1328 rAnimList
.append(aEnd
);
1332 static void impCreateAlternateTiming(const SfxItemSet
& rSet
, drawinglayer::animation::AnimationEntryList
& rAnimList
, double fRelativeTextLength
, bool bForward
, double fTimeFullPath
, double fFrequency
)
1334 if(basegfx::fTools::more(fRelativeTextLength
, 0.5))
1336 // this is the case when fTextLength > fFrameLength, text is bigger than animation frame.
1337 // In that case, correct direction
1338 bForward
= !bForward
;
1341 const double fStartPosition(bForward
? fRelativeTextLength
: 1.0 - fRelativeTextLength
);
1342 const double fEndPosition(bForward
? 1.0 - fRelativeTextLength
: fRelativeTextLength
);
1343 bool bVisibleWhenStarted(rSet
.Get(SDRATTR_TEXT_ANISTARTINSIDE
).GetValue());
1344 const sal_uInt32
nRepeat(rSet
.Get(SDRATTR_TEXT_ANICOUNT
).GetValue());
1346 if(!bVisibleWhenStarted
)
1348 // move from outside to center
1349 drawinglayer::animation::AnimationEntryLinear
aOutIn(fTimeFullPath
* 0.5, fFrequency
, bForward
? 0.0 : 1.0, 0.5);
1350 rAnimList
.append(aOutIn
);
1353 // loop. In loop, move out and in again. fInnerMovePath may be negative when text is bigger then frame,
1354 // so use absolute value
1355 const double fInnerMovePath(fabs(1.0 - (fRelativeTextLength
* 2.0)));
1356 const double fTimeForInnerPath(fTimeFullPath
* fInnerMovePath
);
1357 const double fHalfInnerPath(fTimeForInnerPath
* 0.5);
1358 const sal_uInt32
nDoubleRepeat(nRepeat
/ 2L);
1360 if(nDoubleRepeat
|| 0 == nRepeat
)
1362 // double forth and back loop
1363 drawinglayer::animation::AnimationEntryLoop
aLoop(nDoubleRepeat
? nDoubleRepeat
: ENDLESS_LOOP
);
1364 drawinglayer::animation::AnimationEntryLinear
aTime0(fHalfInnerPath
, fFrequency
, 0.5, fEndPosition
);
1365 aLoop
.append(aTime0
);
1366 drawinglayer::animation::AnimationEntryLinear
aTime1(fTimeForInnerPath
, fFrequency
, fEndPosition
, fStartPosition
);
1367 aLoop
.append(aTime1
);
1368 drawinglayer::animation::AnimationEntryLinear
aTime2(fHalfInnerPath
, fFrequency
, fStartPosition
, 0.5);
1369 aLoop
.append(aTime2
);
1370 rAnimList
.append(aLoop
);
1375 // repeat is uneven, so we need one more forth and back to center
1376 drawinglayer::animation::AnimationEntryLinear
aTime0(fHalfInnerPath
, fFrequency
, 0.5, fEndPosition
);
1377 rAnimList
.append(aTime0
);
1378 drawinglayer::animation::AnimationEntryLinear
aTime1(fHalfInnerPath
, fFrequency
, fEndPosition
, 0.5);
1379 rAnimList
.append(aTime1
);
1385 bool bVisibleWhenStopped(rSet
.Get(SDRATTR_TEXT_ANISTOPINSIDE
).GetValue());
1386 if(bVisibleWhenStopped
)
1388 // add timing for staying at the end
1389 drawinglayer::animation::AnimationEntryFixed
aEnd(ENDLESS_TIME
, 0.5);
1390 rAnimList
.append(aEnd
);
1394 // move from center to outside
1395 drawinglayer::animation::AnimationEntryLinear
aInOut(fTimeFullPath
* 0.5, fFrequency
, 0.5, bForward
? 1.0 : 0.0);
1396 rAnimList
.append(aInOut
);
1400 static void impCreateSlideTiming(const SfxItemSet
& rSet
, drawinglayer::animation::AnimationEntryList
& rAnimList
, bool bForward
, double fTimeFullPath
, double fFrequency
)
1402 // move in from outside, start outside
1403 const double fStartPosition(bForward
? 0.0 : 1.0);
1404 const sal_uInt32
nRepeat(rSet
.Get(SDRATTR_TEXT_ANICOUNT
).GetValue());
1406 // move from outside to center
1407 drawinglayer::animation::AnimationEntryLinear
aOutIn(fTimeFullPath
* 0.5, fFrequency
, fStartPosition
, 0.5);
1408 rAnimList
.append(aOutIn
);
1410 // loop. In loop, move out and in again
1411 if(nRepeat
> 1 || 0 == nRepeat
)
1413 drawinglayer::animation::AnimationEntryLoop
aLoop(nRepeat
? nRepeat
- 1 : ENDLESS_LOOP
);
1414 drawinglayer::animation::AnimationEntryLinear
aTime0(fTimeFullPath
* 0.5, fFrequency
, 0.5, fStartPosition
);
1415 aLoop
.append(aTime0
);
1416 drawinglayer::animation::AnimationEntryLinear
aTime1(fTimeFullPath
* 0.5, fFrequency
, fStartPosition
, 0.5);
1417 aLoop
.append(aTime1
);
1418 rAnimList
.append(aLoop
);
1421 // always visible when stopped, so add timing for staying at the end when not endless
1424 drawinglayer::animation::AnimationEntryFixed
aEnd(ENDLESS_TIME
, 0.5);
1425 rAnimList
.append(aEnd
);
1429 void SdrTextObj::impGetScrollTextTiming(drawinglayer::animation::AnimationEntryList
& rAnimList
, double fFrameLength
, double fTextLength
) const
1431 const SdrTextAniKind
eAniKind(GetTextAniKind());
1433 if(SdrTextAniKind::Scroll
!= eAniKind
&& SdrTextAniKind::Alternate
!= eAniKind
&& SdrTextAniKind::Slide
!= eAniKind
)
1436 // get data. Goal is to calculate fTimeFullPath which is the time needed to
1437 // move animation from (0.0) to (1.0) state
1438 const SfxItemSet
& rSet
= GetObjectItemSet();
1439 double fAnimationDelay(static_cast<double>(rSet
.Get(SDRATTR_TEXT_ANIDELAY
).GetValue()));
1440 double fSingleStepWidth(static_cast<double>(rSet
.Get(SDRATTR_TEXT_ANIAMOUNT
).GetValue()));
1441 const SdrTextAniDirection
eDirection(GetTextAniDirection());
1442 const bool bForward(SdrTextAniDirection::Right
== eDirection
|| SdrTextAniDirection::Down
== eDirection
);
1444 if(basegfx::fTools::equalZero(fAnimationDelay
))
1446 // default to 1/20 second
1447 fAnimationDelay
= 50.0;
1450 if(basegfx::fTools::less(fSingleStepWidth
, 0.0))
1452 // data is in pixels, convert to logic. Imply PIXEL_DPI dpi.
1453 // It makes no sense to keep the view-transformation centered
1454 // definitions, so get rid of them here.
1455 fSingleStepWidth
= (-fSingleStepWidth
* (2540.0 / PIXEL_DPI
));
1458 if(basegfx::fTools::equalZero(fSingleStepWidth
))
1460 // default to 1 millimeter
1461 fSingleStepWidth
= 100.0;
1464 // use the length of the full animation path and the number of steps
1465 // to get the full path time
1466 const double fFullPathLength(fFrameLength
+ fTextLength
);
1467 const double fNumberOfSteps(fFullPathLength
/ fSingleStepWidth
);
1468 double fTimeFullPath(fNumberOfSteps
* fAnimationDelay
);
1470 if(fTimeFullPath
< fAnimationDelay
)
1472 fTimeFullPath
= fAnimationDelay
;
1477 case SdrTextAniKind::Scroll
:
1479 impCreateScrollTiming(rSet
, rAnimList
, bForward
, fTimeFullPath
, fAnimationDelay
);
1482 case SdrTextAniKind::Alternate
:
1484 double fRelativeTextLength(fTextLength
/ (fFrameLength
+ fTextLength
));
1485 impCreateAlternateTiming(rSet
, rAnimList
, fRelativeTextLength
, bForward
, fTimeFullPath
, fAnimationDelay
);
1488 case SdrTextAniKind::Slide
:
1490 impCreateSlideTiming(rSet
, rAnimList
, bForward
, fTimeFullPath
, fAnimationDelay
);
1493 default : break; // SdrTextAniKind::NONE, SdrTextAniKind::Blink
1497 void SdrTextObj::impHandleChainingEventsDuringDecomposition(SdrOutliner
&rOutliner
) const
1499 if (GetTextChain()->GetNilChainingEvent(this))
1502 GetTextChain()->SetNilChainingEvent(this, true);
1504 TextChainFlow
aTxtChainFlow(const_cast<SdrTextObj
*>(this));
1508 // Some debug output
1509 size_t nObjCount(getSdrPageFromSdrObject()->GetObjCount());
1510 for (size_t i
= 0; i
< nObjCount
; i
++)
1512 SdrTextObj
* pCurObj(DynCastSdrTextObj(getSdrPageFromSdrObject()->GetObj(i
)));
1515 SAL_INFO("svx.chaining", "Working on TextBox " << i
);
1521 aTxtChainFlow
.CheckForFlowEvents(&rOutliner
);
1523 if (aTxtChainFlow
.IsUnderflow() && !IsInEditMode())
1525 // underflow-induced overflow
1526 aTxtChainFlow
.ExecuteUnderflow(&rOutliner
);
1527 bIsOverflow
= aTxtChainFlow
.IsOverflow();
1529 // standard overflow (no underflow before)
1530 bIsOverflow
= aTxtChainFlow
.IsOverflow();
1533 if (bIsOverflow
&& !IsInEditMode()) {
1534 // Initialize Chaining Outliner
1535 SdrOutliner
&rChainingOutl(getSdrModelFromSdrObject().GetChainingOutliner(this));
1536 ImpInitDrawOutliner( rChainingOutl
);
1537 rChainingOutl
.SetUpdateLayout(true);
1538 // We must pass the chaining outliner otherwise we would mess up decomposition
1539 aTxtChainFlow
.ExecuteOverflow(&rOutliner
, &rChainingOutl
);
1542 GetTextChain()->SetNilChainingEvent(this, false);
1545 void SdrTextObj::impDecomposeChainedTextPrimitive(
1546 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
1547 const drawinglayer::primitive2d::SdrChainedTextPrimitive2D
& rSdrChainedTextPrimitive
,
1548 const drawinglayer::geometry::ViewInformation2D
& aViewInformation
) const
1550 // decompose matrix to have position and size of text
1551 basegfx::B2DVector aScale
, aTranslate
;
1552 double fRotate
, fShearX
;
1553 rSdrChainedTextPrimitive
.getTextRangeTransform().decompose(aScale
, aTranslate
, fRotate
, fShearX
);
1555 // use B2DRange aAnchorTextRange for calculations
1556 basegfx::B2DRange
aAnchorTextRange(aTranslate
);
1557 aAnchorTextRange
.expand(aTranslate
+ aScale
);
1560 const SfxItemSet
& rTextItemSet
= rSdrChainedTextPrimitive
.getSdrText()->GetItemSet();
1561 SolarMutexGuard aSolarGuard
;
1562 SdrOutliner
& rOutliner
= ImpGetDrawOutliner();
1564 SdrTextVertAdjust eVAdj
= GetTextVerticalAdjust(rTextItemSet
);
1565 SdrTextHorzAdjust eHAdj
= GetTextHorizontalAdjust(rTextItemSet
);
1566 const EEControlBits
nOriginalControlWord(rOutliner
.GetControlWord());
1567 const Size aNullSize
;
1569 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1570 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
1572 rOutliner
.SetControlWord(nOriginalControlWord
|EEControlBits::AUTOPAGESIZE
|EEControlBits::STRETCHING
);
1573 rOutliner
.SetMinAutoPaperSize(aNullSize
);
1574 rOutliner
.SetMaxAutoPaperSize(Size(1000000,1000000));
1576 // add one to range sizes to get back to the old Rectangle and outliner measurements
1577 const sal_uInt32
nAnchorTextWidth(FRound(aAnchorTextRange
.getWidth() + 1));
1578 const sal_uInt32
nAnchorTextHeight(FRound(aAnchorTextRange
.getHeight() + 1));
1581 const OutlinerParaObject
* pOutlinerParaObject
= rSdrChainedTextPrimitive
.getSdrText()->GetOutlinerParaObject();
1582 OSL_ENSURE(pOutlinerParaObject
, "impDecomposeBlockTextPrimitive used with no OutlinerParaObject (!)");
1584 const bool bVerticalWriting(pOutlinerParaObject
->IsEffectivelyVertical());
1585 const bool bTopToBottom(pOutlinerParaObject
->IsTopToBottom());
1586 const Size
aAnchorTextSize(Size(nAnchorTextWidth
, nAnchorTextHeight
));
1590 rOutliner
.SetMaxAutoPaperSize(aAnchorTextSize
);
1593 if(SDRTEXTHORZADJUST_BLOCK
== eHAdj
&& !bVerticalWriting
)
1595 rOutliner
.SetMinAutoPaperSize(Size(nAnchorTextWidth
, 0));
1598 if(SDRTEXTVERTADJUST_BLOCK
== eVAdj
&& bVerticalWriting
)
1600 rOutliner
.SetMinAutoPaperSize(Size(0, nAnchorTextHeight
));
1603 rOutliner
.SetPaperSize(aNullSize
);
1604 rOutliner
.SetUpdateLayout(true);
1605 // Sets original text
1606 rOutliner
.SetText(*pOutlinerParaObject
);
1608 /* Begin overflow/underflow handling */
1610 impHandleChainingEventsDuringDecomposition(rOutliner
);
1612 /* End overflow/underflow handling */
1614 // set visualizing page at Outliner; needed e.g. for PageNumberField decomposition
1615 rOutliner
.setVisualizedPage(GetSdrPageFromXDrawPage(aViewInformation
.getVisualizedPage()));
1617 // now get back the layouted text size from outliner
1618 const Size
aOutlinerTextSize(rOutliner
.GetPaperSize());
1619 const basegfx::B2DVector
aOutlinerScale(aOutlinerTextSize
.Width(), aOutlinerTextSize
.Height());
1620 basegfx::B2DVector
aAdjustTranslate(0.0, 0.0);
1622 // correct horizontal translation using the now known text size
1623 if(SDRTEXTHORZADJUST_CENTER
== eHAdj
|| SDRTEXTHORZADJUST_RIGHT
== eHAdj
)
1625 const double fFree(aAnchorTextRange
.getWidth() - aOutlinerScale
.getX());
1627 if(SDRTEXTHORZADJUST_CENTER
== eHAdj
)
1629 aAdjustTranslate
.setX(fFree
/ 2.0);
1632 if(SDRTEXTHORZADJUST_RIGHT
== eHAdj
)
1634 aAdjustTranslate
.setX(fFree
);
1638 // correct vertical translation using the now known text size
1639 if(SDRTEXTVERTADJUST_CENTER
== eVAdj
|| SDRTEXTVERTADJUST_BOTTOM
== eVAdj
)
1641 const double fFree(aAnchorTextRange
.getHeight() - aOutlinerScale
.getY());
1643 if(SDRTEXTVERTADJUST_CENTER
== eVAdj
)
1645 aAdjustTranslate
.setY(fFree
/ 2.0);
1648 if(SDRTEXTVERTADJUST_BOTTOM
== eVAdj
)
1650 aAdjustTranslate
.setY(fFree
);
1654 // prepare matrices to apply to newly created primitives. aNewTransformA
1655 // will get coordinates in aOutlinerScale size and positive in X, Y.
1656 basegfx::B2DHomMatrix aNewTransformA
;
1657 basegfx::B2DHomMatrix aNewTransformB
;
1659 // translate relative to given primitive to get same rotation and shear
1660 // as the master shape we are working on. For vertical, use the top-right
1662 const double fStartInX(bVerticalWriting
&& bTopToBottom
? aAdjustTranslate
.getX() + aOutlinerScale
.getX() : aAdjustTranslate
.getX());
1663 const double fStartInY(bVerticalWriting
&& !bTopToBottom
? aAdjustTranslate
.getY() + aOutlinerScale
.getY() : aAdjustTranslate
.getY());
1664 aNewTransformA
.translate(fStartInX
, fStartInY
);
1666 // mirroring. We are now in aAnchorTextRange sizes. When mirroring in X and Y,
1667 // move the null point which was top left to bottom right.
1668 const bool bMirrorX(basegfx::fTools::less(aScale
.getX(), 0.0));
1669 const bool bMirrorY(basegfx::fTools::less(aScale
.getY(), 0.0));
1670 aNewTransformB
.scale(bMirrorX
? -1.0 : 1.0, bMirrorY
? -1.0 : 1.0);
1672 // in-between the translations of the single primitives will take place. Afterwards,
1673 // the object's transformations need to be applied
1674 aNewTransformB
.shearX(fShearX
);
1675 aNewTransformB
.rotate(fRotate
);
1676 aNewTransformB
.translate(aTranslate
.getX(), aTranslate
.getY());
1678 basegfx::B2DRange aClipRange
;
1680 // now break up text primitives.
1681 impTextBreakupHandler
aConverter(rOutliner
);
1682 aConverter
.decomposeBlockTextPrimitive(aNewTransformA
, aNewTransformB
, aClipRange
);
1686 rOutliner
.setVisualizedPage(nullptr);
1687 rOutliner
.SetControlWord(nOriginalControlWord
);
1689 rTarget
= aConverter
.extractPrimitive2DSequence();
1692 // Direct decomposer for text visualization when you already have a prepared
1693 // Outliner containing all the needed information
1694 void SdrTextObj::impDecomposeBlockTextPrimitiveDirect(
1695 drawinglayer::primitive2d::Primitive2DContainer
& rTarget
,
1696 SdrOutliner
& rOutliner
,
1697 const basegfx::B2DHomMatrix
& rNewTransformA
,
1698 const basegfx::B2DHomMatrix
& rNewTransformB
,
1699 const basegfx::B2DRange
& rClipRange
)
1701 impTextBreakupHandler
aConverter(rOutliner
);
1702 aConverter
.decomposeBlockTextPrimitive(rNewTransformA
, rNewTransformB
, rClipRange
);
1703 rTarget
.append(aConverter
.extractPrimitive2DSequence());
1706 double SdrTextObj::GetCameraZRotation() const
1708 const css::uno::Any
* pAny
;
1709 double fTextCameraZRotateAngle
= 0.0;
1710 const SfxItemSet
& rSet
= GetObjectItemSet();
1711 const SdrCustomShapeGeometryItem
& rGeometryItem(rSet
.Get(SDRATTR_CUSTOMSHAPE_GEOMETRY
));
1713 pAny
= rGeometryItem
.GetPropertyValueByName("TextCameraZRotateAngle");
1716 *pAny
>>= fTextCameraZRotateAngle
;
1718 return fTextCameraZRotateAngle
;
1721 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */