xref: /core/svx/source/svdraw/svdotext.cxx (revision 1457699096a5f6b25f7a32582e5bdec9deeb0898)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
21 #include <comphelper/string.hxx>
22 #include <svl/stritem.hxx>
23 #include <svx/svdotext.hxx>
24 #include <svx/svdpage.hxx>
25 #include <svx/svdoutl.hxx>
26 #include <svx/svdmodel.hxx>
27 #include <svx/dialmgr.hxx>
28 #include <svx/strings.hrc>
29 #include <editeng/writingmodeitem.hxx>
30 #include <svx/sdtfchim.hxx>
31 #include <editeng/editdata.hxx>
32 #include <editeng/editstat.hxx>
33 #include <editeng/outlobj.hxx>
34 #include <editeng/editobj.hxx>
35 #include <editeng/outliner.hxx>
36 #include <textchain.hxx>
37 #include <textchainflow.hxx>
38 #include <tools/helpers.hxx>
39 #include <svx/sderitm.hxx>
40 #include <svx/sdooitm.hxx>
41 #include <svx/sdshitm.hxx>
42 #include <svx/sdtagitm.hxx>
43 #include <svx/sdtfsitm.hxx>
44 #include <svx/sdtmfitm.hxx>
45 #include <svx/xtextit0.hxx>
46 #include <sdr/properties/textproperties.hxx>
47 #include <sdr/contact/viewcontactoftextobj.hxx>
48 #include <basegfx/tuple/b2dtuple.hxx>
49 #include <basegfx/matrix/b2dhommatrix.hxx>
50 #include <vcl/gdimtf.hxx>
51 #include <vcl/virdev.hxx>
52 #include <basegfx/matrix/b2dhommatrixtools.hxx>
53 #include <sal/log.hxx>
54 #include <o3tl/unit_conversion.hxx>
55 #include <o3tl/temporary.hxx>
56 #include <comphelper/configuration.hxx>
57 #include <editeng/eeitem.hxx>
58 #include <editeng/fhgtitem.hxx>
59 
60 using namespace com::sun::star;
61 
62 // BaseProperties section
CreateObjectSpecificProperties()63 std::unique_ptr<sdr::properties::BaseProperties> SdrTextObj::CreateObjectSpecificProperties()
64 {
65     return std::make_unique<sdr::properties::TextProperties>(*this);
66 }
67 
68 // DrawContact section
CreateObjectSpecificViewContact()69 std::unique_ptr<sdr::contact::ViewContact> SdrTextObj::CreateObjectSpecificViewContact()
70 {
71     return std::make_unique<sdr::contact::ViewContactOfTextObj>(*this);
72 }
73 
SdrTextObj(SdrModel & rSdrModel,const tools::Rectangle & rRectangle,std::optional<SdrObjKind> oeTextKind)74 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, const tools::Rectangle& rRectangle, std::optional<SdrObjKind> oeTextKind)
75     : SdrAttrObj(rSdrModel)
76     , mpEditingOutliner(nullptr)
77     , meTextKind(oeTextKind ? *oeTextKind : SdrObjKind::Text)
78     , maTextEditOffset(Point())
79     , mbTextFrame(false)
80     , mbNoShear(false)
81     , mbTextSizeDirty(false)
82     , mbInEditMode(false)
83     , mbDisableAutoWidthOnDragging(false)
84     , mbTextAnimationAllowed(true)
85     , mbInDownScale(false)
86 {
87     if (!rRectangle.IsEmpty())
88     {
89         tools::Rectangle aRectangle(rRectangle);
90         ImpJustifyRect(aRectangle);
91         setRectangle(aRectangle);
92     }
93 
94     if (oeTextKind)
95     {
96         mbTextFrame = true;
97         mbNoShear = true;
98     }
99 
100     mbSupportTextIndentingOnLineWidthChange = true;
101 }
102 
SdrTextObj(SdrModel & rSdrModel,SdrTextObj const & rSource)103 SdrTextObj::SdrTextObj(SdrModel& rSdrModel, SdrTextObj const & rSource)
104     : SdrAttrObj(rSdrModel, rSource)
105     , mpEditingOutliner(nullptr)
106     , meTextKind(rSource.meTextKind)
107     , maTextEditOffset(Point(0, 0))
108     , mbTextFrame(rSource.mbTextFrame)
109     , mbNoShear(rSource.mbNoShear)
110     , mbTextSizeDirty(rSource.mbTextSizeDirty)
111     , mbInEditMode(false)
112     , mbDisableAutoWidthOnDragging(rSource.mbDisableAutoWidthOnDragging)
113     , mbTextAnimationAllowed(true)
114     , mbInDownScale(false)
115 {
116     // #i25616#
117     mbSupportTextIndentingOnLineWidthChange = true;
118 
119     maRectangle = rSource.maRectangle;
120     maGeo = rSource.maGeo;
121     maTextSize = rSource.maTextSize;
122 
123     // Not all of the necessary parameters were copied yet.
124     SdrText* pText = getActiveText();
125 
126     if( pText && rSource.HasText() )
127     {
128         // before pNewOutlinerParaObject was created the same, but
129         // set at mpText (outside this scope), but mpText might be
130         // empty (this operator== seems not prepared for MultiText
131         // objects). In the current form it makes only sense to
132         // create locally and use locally on a known existing SdrText
133         const Outliner* pEO = rSource.mpEditingOutliner;
134         std::optional<OutlinerParaObject> pNewOutlinerParaObject;
135 
136         if (pEO!=nullptr)
137         {
138             pNewOutlinerParaObject = pEO->CreateParaObject();
139         }
140         else if (nullptr != rSource.getActiveText()->GetOutlinerParaObject())
141         {
142             pNewOutlinerParaObject = *rSource.getActiveText()->GetOutlinerParaObject();
143         }
144 
145         pText->SetOutlinerParaObject( std::move(pNewOutlinerParaObject) );
146     }
147 
148     ImpSetTextStyleSheetListeners();
149 }
150 
~SdrTextObj()151 SdrTextObj::~SdrTextObj()
152 {
153     mxText.clear();
154     ImpDeregisterLink();
155 }
156 
FitFrameToTextSize()157 void SdrTextObj::FitFrameToTextSize()
158 {
159     ImpJustifyRect(maRectangle);
160 
161     SdrText* pText = getActiveText();
162     if(pText==nullptr || !pText->GetOutlinerParaObject())
163         return;
164 
165     SdrOutliner& rOutliner=ImpGetDrawOutliner();
166     rOutliner.SetPaperSize(Size(getRectangle().Right() - getRectangle().Left(), getRectangle().Bottom() - getRectangle().Top()));
167     rOutliner.SetUpdateLayout(true);
168     rOutliner.SetText(*pText->GetOutlinerParaObject());
169     Size aNewSize(rOutliner.CalcTextSize());
170     rOutliner.Clear();
171     aNewSize.AdjustWidth( 1 ); // because of possible rounding errors
172     aNewSize.AdjustWidth(GetTextLeftDistance()+GetTextRightDistance() );
173     aNewSize.AdjustHeight(GetTextUpperDistance()+GetTextLowerDistance() );
174     tools::Rectangle aNewRect(getRectangle());
175     aNewRect.SetSize(aNewSize);
176     ImpJustifyRect(aNewRect);
177 
178     if (aNewRect != getRectangle())
179         SetLogicRect(aNewRect);
180 }
181 
NbcSetText(const OUString & rStr)182 void SdrTextObj::NbcSetText(const OUString& rStr)
183 {
184     SdrOutliner& rOutliner=ImpGetDrawOutliner();
185     rOutliner.SetStyleSheet( 0, GetStyleSheet());
186     rOutliner.SetText(rStr,rOutliner.GetParagraph( 0 ));
187     std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
188     NbcSetOutlinerParaObject(std::move(pNewText));
189     mbTextSizeDirty=true;
190 }
191 
SetText(const OUString & rStr)192 void SdrTextObj::SetText(const OUString& rStr)
193 {
194     tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
195     NbcSetText(rStr);
196     SetChanged();
197     BroadcastObjectChange();
198     SendUserCall(SdrUserCallType::Resize,aBoundRect0);
199 }
200 
NbcSetText(SvStream & rInput,const OUString & rBaseURL,EETextFormat eFormat)201 void SdrTextObj::NbcSetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
202 {
203     SdrOutliner& rOutliner=ImpGetDrawOutliner();
204     rOutliner.SetStyleSheet( 0, GetStyleSheet());
205     rOutliner.Read(rInput,rBaseURL,eFormat);
206     std::optional<OutlinerParaObject> pNewText=rOutliner.CreateParaObject();
207     rOutliner.SetUpdateLayout(true);
208     Size aSize(rOutliner.CalcTextSize());
209     rOutliner.Clear();
210     NbcSetOutlinerParaObject(std::move(pNewText));
211     maTextSize=aSize;
212     mbTextSizeDirty=false;
213 }
214 
SetText(SvStream & rInput,const OUString & rBaseURL,EETextFormat eFormat)215 void SdrTextObj::SetText(SvStream& rInput, const OUString& rBaseURL, EETextFormat eFormat)
216 {
217     tools::Rectangle aBoundRect0; if (m_pUserCall!=nullptr) aBoundRect0=GetLastBoundRect();
218     NbcSetText(rInput,rBaseURL,eFormat);
219     SetChanged();
220     BroadcastObjectChange();
221     SendUserCall(SdrUserCallType::Resize,aBoundRect0);
222 }
223 
GetTextSize() const224 const Size& SdrTextObj::GetTextSize() const
225 {
226     if (mbTextSizeDirty)
227     {
228         Size aSiz;
229         SdrText* pText = getActiveText();
230         if( pText && pText->GetOutlinerParaObject ())
231         {
232             SdrOutliner& rOutliner=ImpGetDrawOutliner();
233             rOutliner.SetText(*pText->GetOutlinerParaObject());
234             rOutliner.SetUpdateLayout(true);
235             aSiz=rOutliner.CalcTextSize();
236             rOutliner.Clear();
237         }
238         // casting to nonconst twice
239         const_cast<SdrTextObj*>(this)->maTextSize = aSiz;
240         const_cast<SdrTextObj*>(this)->mbTextSizeDirty = false;
241     }
242     return maTextSize;
243 }
244 
IsAutoGrowHeight() const245 bool SdrTextObj::IsAutoGrowHeight() const
246 {
247     if(!mbTextFrame)
248         return false; // AutoGrow only together with TextFrames
249 
250     const SfxItemSet& rSet = GetObjectItemSet();
251     bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
252 
253     if(bRet)
254     {
255         SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
256 
257         if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
258         {
259             SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
260 
261             if(eDirection == SdrTextAniDirection::Up || eDirection == SdrTextAniDirection::Down)
262             {
263                 bRet = false;
264             }
265         }
266     }
267     return bRet;
268 }
269 
IsAutoGrowWidth() const270 bool SdrTextObj::IsAutoGrowWidth() const
271 {
272     if (!mbTextFrame)
273         return false; // AutoGrow only together with TextFrames
274 
275     const SfxItemSet& rSet = GetObjectItemSet();
276     bool bRet = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
277 
278     bool bInEditMOde = IsInEditMode();
279 
280     if(!bInEditMOde && bRet)
281     {
282         SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
283 
284         if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
285         {
286             SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
287 
288             if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
289             {
290                 bRet = false;
291             }
292         }
293     }
294     return bRet;
295 }
296 
GetTextHorizontalAdjust() const297 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust() const
298 {
299     return GetTextHorizontalAdjust(GetObjectItemSet());
300 }
301 
GetTextHorizontalAdjust(const SfxItemSet & rSet) const302 SdrTextHorzAdjust SdrTextObj::GetTextHorizontalAdjust(const SfxItemSet& rSet) const
303 {
304     if(IsContourTextFrame())
305         return SDRTEXTHORZADJUST_BLOCK;
306 
307     SdrTextHorzAdjust eRet = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
308 
309     bool bInEditMode = IsInEditMode();
310 
311     if(!bInEditMode && eRet == SDRTEXTHORZADJUST_BLOCK)
312     {
313         SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
314 
315         if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
316         {
317             SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
318 
319             if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
320             {
321                 eRet = SDRTEXTHORZADJUST_LEFT;
322             }
323         }
324     }
325 
326     return eRet;
327 } // defaults: BLOCK (justify) for text frame, CENTER for captions of drawing objects
328 
GetTextVerticalAdjust() const329 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust() const
330 {
331     return GetTextVerticalAdjust(GetObjectItemSet());
332 }
333 
GetTextVerticalAdjust(const SfxItemSet & rSet) const334 SdrTextVertAdjust SdrTextObj::GetTextVerticalAdjust(const SfxItemSet& rSet) const
335 {
336     if(IsContourTextFrame())
337         return SDRTEXTVERTADJUST_TOP;
338 
339     // Take care for vertical text animation here
340     SdrTextVertAdjust eRet = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
341     bool bInEditMode = IsInEditMode();
342 
343     // Take care for vertical text animation here
344     if(!bInEditMode && eRet == SDRTEXTVERTADJUST_BLOCK)
345     {
346         SdrTextAniKind eAniKind = rSet.Get(SDRATTR_TEXT_ANIKIND).GetValue();
347 
348         if(eAniKind == SdrTextAniKind::Scroll || eAniKind == SdrTextAniKind::Alternate || eAniKind == SdrTextAniKind::Slide)
349         {
350             SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
351 
352             if(eDirection == SdrTextAniDirection::Left || eDirection == SdrTextAniDirection::Right)
353             {
354                 eRet = SDRTEXTVERTADJUST_TOP;
355             }
356         }
357     }
358 
359     return eRet;
360 } // defaults: TOP for text frame, CENTER for captions of drawing objects
361 
ImpJustifyRect(tools::Rectangle & rRect)362 void SdrTextObj::ImpJustifyRect(tools::Rectangle& rRect)
363 {
364     if (!rRect.IsEmpty()) {
365         rRect.Normalize();
366         if (rRect.Left()==rRect.Right()) rRect.AdjustRight( 1 );
367         if (rRect.Top()==rRect.Bottom()) rRect.AdjustBottom( 1 );
368     }
369 }
370 
ImpCheckShear()371 void SdrTextObj::ImpCheckShear()
372 {
373     if (mbNoShear && maGeo.m_nShearAngle)
374     {
375         maGeo.m_nShearAngle = 0_deg100;
376         maGeo.mfTanShearAngle = 0;
377     }
378 }
379 
TakeObjInfo(SdrObjTransformInfoRec & rInfo) const380 void SdrTextObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
381 {
382     bool bNoTextFrame=!IsTextFrame();
383     rInfo.bResizeFreeAllowed=bNoTextFrame || ((maGeo.m_nRotationAngle.get() % 9000) == 0);
384     rInfo.bResizePropAllowed=true;
385     rInfo.bRotateFreeAllowed=true;
386     rInfo.bRotate90Allowed  =true;
387     rInfo.bMirrorFreeAllowed=bNoTextFrame;
388     rInfo.bMirror45Allowed  =bNoTextFrame;
389     rInfo.bMirror90Allowed  =bNoTextFrame;
390 
391     // allow transparency
392     rInfo.bTransparenceAllowed = true;
393 
394     rInfo.bShearAllowed     =bNoTextFrame;
395     rInfo.bEdgeRadiusAllowed=true;
396     bool bCanConv=ImpCanConvTextToCurve();
397     rInfo.bCanConvToPath    =bCanConv;
398     rInfo.bCanConvToPoly    =bCanConv;
399     rInfo.bCanConvToPathLineToArea=bCanConv;
400     rInfo.bCanConvToPolyLineToArea=bCanConv;
401     rInfo.bCanConvToContour = (rInfo.bCanConvToPoly || LineGeometryUsageIsNecessary());
402 }
403 
GetObjIdentifier() const404 SdrObjKind SdrTextObj::GetObjIdentifier() const
405 {
406     return meTextKind;
407 }
408 
HasTextImpl(SdrOutliner const * pOutliner)409 bool SdrTextObj::HasTextImpl( SdrOutliner const * pOutliner )
410 {
411     bool bRet=false;
412     if(pOutliner)
413     {
414         Paragraph* p1stPara=pOutliner->GetParagraph( 0 );
415         sal_Int32 nParaCount=pOutliner->GetParagraphCount();
416         if(p1stPara==nullptr)
417             nParaCount=0;
418 
419         if(nParaCount==1)
420         {
421             // if it is only one paragraph, check if that paragraph is empty
422             if( pOutliner->GetText(p1stPara).isEmpty() )
423                 nParaCount = 0;
424         }
425 
426         bRet= nParaCount!=0;
427     }
428     return bRet;
429 }
430 
handlePageChange(SdrPage * pOldPage,SdrPage * pNewPage)431 void SdrTextObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage)
432 {
433     const bool bRemove(pNewPage == nullptr && pOldPage != nullptr);
434     const bool bInsert(pNewPage != nullptr && pOldPage == nullptr);
435     const bool bLinked(IsLinkedText());
436 
437     if (bLinked && bRemove)
438     {
439         ImpDeregisterLink();
440     }
441 
442     // call parent
443     SdrAttrObj::handlePageChange(pOldPage, pNewPage);
444 
445     if (bLinked && bInsert)
446     {
447         ImpRegisterLink();
448     }
449 }
450 
NbcSetEckenradius(tools::Long nRad)451 void SdrTextObj::NbcSetEckenradius(tools::Long nRad)
452 {
453     SetObjectItem(makeSdrEckenradiusItem(nRad));
454 }
455 
456 // #115391# This implementation is based on the object size (aRect) and the
457 // states of IsAutoGrowWidth/Height to correctly set TextMinFrameWidth/Height
AdaptTextMinSize()458 void SdrTextObj::AdaptTextMinSize()
459 {
460     if (!mbTextFrame)
461         // Only do this for text frame.
462         return;
463 
464     if (getSdrModelFromSdrObject().IsPasteResize())
465         // Don't do this during paste resize.
466         return;
467 
468     const bool bW = IsAutoGrowWidth();
469     const bool bH = IsAutoGrowHeight();
470 
471     if (!bW && !bH)
472         // No auto grow requested.  Bail out.
473         return;
474 
475     SfxItemSet aSet(SfxItemSet::makeFixedSfxItemSet<SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
476                                                     // contains SDRATTR_TEXT_MAXFRAMEWIDTH
477                                                     SDRATTR_TEXT_MINFRAMEWIDTH, SDRATTR_TEXT_AUTOGROWWIDTH>(*GetObjectItemSet().GetPool()));
478 
479     if(bW)
480     {
481         // Set minimum width.
482         const tools::Long nDist = GetTextLeftDistance() + GetTextRightDistance();
483         const tools::Long nW = std::max<tools::Long>(0, getRectangle().GetWidth() - 1 - nDist); // text width without margins
484 
485         aSet.Put(makeSdrTextMinFrameWidthItem(nW));
486 
487         if(!IsVerticalWriting() && mbDisableAutoWidthOnDragging)
488         {
489             mbDisableAutoWidthOnDragging = true;
490             aSet.Put(makeSdrTextAutoGrowWidthItem(false));
491         }
492     }
493 
494     if(bH)
495     {
496         // Set Minimum height.
497         const tools::Long nDist = GetTextUpperDistance() + GetTextLowerDistance();
498         const tools::Long nH = std::max<tools::Long>(0, getRectangle().GetHeight() - 1 - nDist); // text height without margins
499 
500         aSet.Put(makeSdrTextMinFrameHeightItem(nH));
501 
502         if(IsVerticalWriting() && mbDisableAutoWidthOnDragging)
503         {
504             mbDisableAutoWidthOnDragging = false;
505             aSet.Put(makeSdrTextAutoGrowHeightItem(false));
506         }
507     }
508 
509     SetObjectItemSet(aSet);
510 }
511 
ImpSetContourPolygon(SdrOutliner & rOutliner,tools::Rectangle const & rAnchorRect,bool bLineWidth) const512 void SdrTextObj::ImpSetContourPolygon( SdrOutliner& rOutliner, tools::Rectangle const & rAnchorRect, bool bLineWidth ) const
513 {
514     basegfx::B2DPolyPolygon aXorPolyPolygon(TakeXorPoly());
515     std::optional<basegfx::B2DPolyPolygon> pContourPolyPolygon;
516     basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
517         -rAnchorRect.Left(), -rAnchorRect.Top()));
518 
519     if(maGeo.m_nRotationAngle)
520     {
521         // Unrotate!
522         aMatrix.rotate(-toRadians(maGeo.m_nRotationAngle));
523     }
524 
525     aXorPolyPolygon.transform(aMatrix);
526 
527     if( bLineWidth )
528     {
529         // Take line width into account.
530         // When doing the hit test, avoid this. (Performance!)
531         pContourPolyPolygon.emplace();
532 
533         // test if shadow needs to be avoided for TakeContour()
534         const SfxItemSet& rSet = GetObjectItemSet();
535         bool bShadowOn = rSet.Get(SDRATTR_SHADOW).GetValue();
536 
537         // #i33696#
538         // Remember TextObject currently set at the DrawOutliner, it WILL be
539         // replaced during calculating the outline since it uses an own paint
540         // and that one uses the DrawOutliner, too.
541         const SdrTextObj* pLastTextObject = rOutliner.GetTextObj();
542 
543         if(bShadowOn)
544         {
545             // force shadow off
546             rtl::Reference<SdrTextObj> pCopy = SdrObject::Clone(*this, getSdrModelFromSdrObject());
547             pCopy->SetMergedItem(makeSdrShadowItem(false));
548             *pContourPolyPolygon = pCopy->TakeContour();
549         }
550         else
551         {
552             *pContourPolyPolygon = TakeContour();
553         }
554 
555         // #i33696#
556         // restore remembered text object
557         if(pLastTextObject != rOutliner.GetTextObj())
558         {
559             rOutliner.SetTextObj(pLastTextObject);
560         }
561 
562         pContourPolyPolygon->transform(aMatrix);
563     }
564 
565     rOutliner.SetPolygon(aXorPolyPolygon, pContourPolyPolygon ? &*pContourPolyPolygon : nullptr);
566 }
567 
TakeUnrotatedSnapRect(tools::Rectangle & rRect) const568 void SdrTextObj::TakeUnrotatedSnapRect(tools::Rectangle& rRect) const
569 {
570     rRect = getRectangle();
571 }
572 
573 // See also: <unnamed>::getTextAnchorRange in svx/source/sdr/primitive2d/sdrdecompositiontools.cxx
AdjustRectToTextDistance(tools::Rectangle & rAnchorRect,double fExtraRot) const574 void SdrTextObj::AdjustRectToTextDistance(tools::Rectangle& rAnchorRect, double fExtraRot) const
575 {
576     const tools::Long nLeftDist = GetTextLeftDistance();
577     const tools::Long nRightDist = GetTextRightDistance();
578     const tools::Long nUpperDist = GetTextUpperDistance();
579     const tools::Long nLowerDist = GetTextLowerDistance();
580     if (!IsVerticalWriting())
581     {
582         if (fExtraRot == 180.0)
583         {
584             rAnchorRect.AdjustLeft(nLeftDist);
585             rAnchorRect.AdjustTop(-nUpperDist);
586             rAnchorRect.AdjustRight(-nRightDist);
587             rAnchorRect.AdjustBottom(nLowerDist);
588         }
589         else
590         {
591             rAnchorRect.AdjustLeft(nLeftDist);
592             rAnchorRect.AdjustTop(nUpperDist);
593             rAnchorRect.AdjustRight(-nRightDist);
594             rAnchorRect.AdjustBottom(-nLowerDist);
595         }
596     }
597     else if (IsTopToBottom())
598     {
599         rAnchorRect.AdjustLeft(nLowerDist);
600         rAnchorRect.AdjustTop(nLeftDist);
601         rAnchorRect.AdjustRight(-nUpperDist);
602         rAnchorRect.AdjustBottom(-nRightDist);
603     }
604     else
605     {
606         rAnchorRect.AdjustLeft(nUpperDist);
607         rAnchorRect.AdjustTop(nRightDist);
608         rAnchorRect.AdjustRight(-nLowerDist);
609         rAnchorRect.AdjustBottom(-nLeftDist);
610     }
611 
612     // Since sizes may be bigger than the object bounds it is necessary to
613     // justify the rect now.
614     ImpJustifyRect(rAnchorRect);
615 }
616 
TakeTextAnchorRect(tools::Rectangle & rAnchorRect) const617 void SdrTextObj::TakeTextAnchorRect(tools::Rectangle& rAnchorRect) const
618 {
619     tools::Rectangle aAnkRect(getRectangle()); // the rectangle in which we anchor
620     bool bFrame=IsTextFrame();
621     if (!bFrame) {
622         TakeUnrotatedSnapRect(aAnkRect);
623     }
624     Point aRotateRef(aAnkRect.TopLeft());
625     AdjustRectToTextDistance(aAnkRect);
626 
627     if (bFrame) {
628         // TODO: Optimize this.
629         if (aAnkRect.GetWidth()<2) aAnkRect.SetRight(aAnkRect.Left()+1 ); // minimum size h and v: 2 px
630         if (aAnkRect.GetHeight()<2) aAnkRect.SetBottom(aAnkRect.Top()+1 );
631     }
632     if (maGeo.m_nRotationAngle) {
633         Point aTmpPt(aAnkRect.TopLeft());
634         RotatePoint(aTmpPt,aRotateRef,maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
635         aTmpPt-=aAnkRect.TopLeft();
636         aAnkRect.Move(aTmpPt.X(),aTmpPt.Y());
637     }
638     rAnchorRect=aAnkRect;
639 }
640 
TakeTextRect(SdrOutliner & rOutliner,tools::Rectangle & rTextRect,bool bNoEditText,tools::Rectangle * pAnchorRect,bool bLineWidth) const641 void SdrTextObj::TakeTextRect( SdrOutliner& rOutliner, tools::Rectangle& rTextRect, bool bNoEditText,
642                                tools::Rectangle* pAnchorRect, bool bLineWidth ) const
643 {
644     tools::Rectangle aAnkRect; // the rectangle in which we anchor
645     TakeTextAnchorRect(aAnkRect);
646     SdrTextVertAdjust eVAdj=GetTextVerticalAdjust();
647     SdrTextHorzAdjust eHAdj=GetTextHorizontalAdjust();
648     SdrTextAniKind      eAniKind=GetTextAniKind();
649     SdrTextAniDirection eAniDirection=GetTextAniDirection();
650 
651     bool bFitToSize(IsFitToSize());
652     bool bContourFrame=IsContourTextFrame();
653 
654     bool bFrame=IsTextFrame();
655     EEControlBits nStat0=rOutliner.GetControlWord();
656     Size aNullSize;
657     if (!bContourFrame)
658     {
659         rOutliner.SetControlWord(nStat0|EEControlBits::AUTOPAGESIZE);
660         rOutliner.SetMinAutoPaperSize(aNullSize);
661         rOutliner.SetMaxAutoPaperSize(Size(1000000,1000000));
662     }
663 
664     if (!bFitToSize && !bContourFrame)
665     {
666         tools::Long nAnkWdt=aAnkRect.GetWidth();
667         tools::Long nAnkHgt=aAnkRect.GetHeight();
668         if (bFrame)
669         {
670             tools::Long nWdt=nAnkWdt;
671             tools::Long nHgt=nAnkHgt;
672 
673             bool bInEditMode = IsInEditMode();
674 
675             if (!bInEditMode && (eAniKind==SdrTextAniKind::Scroll || eAniKind==SdrTextAniKind::Alternate || eAniKind==SdrTextAniKind::Slide))
676             {
677                 // unlimited paper size for ticker text
678                 if (eAniDirection==SdrTextAniDirection::Left || eAniDirection==SdrTextAniDirection::Right) nWdt=1000000;
679                 if (eAniDirection==SdrTextAniDirection::Up || eAniDirection==SdrTextAniDirection::Down) nHgt=1000000;
680             }
681 
682             bool bChainedFrame = IsChainable();
683             // Might be required for overflow check working: do limit height to frame if box is chainable.
684             if (!bChainedFrame) {
685                 // #i119885# Do not limit/force height to geometrical frame (vice versa for vertical writing)
686 
687                 if(IsVerticalWriting())
688                 {
689                     nWdt = 1000000;
690                 }
691                 else
692                 {
693                     nHgt = 1000000;
694                 }
695             }
696 
697             rOutliner.SetMaxAutoPaperSize(Size(nWdt,nHgt));
698         }
699 
700         // New try with _BLOCK for hor and ver after completely
701         // supporting full width for vertical text.
702         if(SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
703         {
704             rOutliner.SetMinAutoPaperSize(Size(nAnkWdt, 0));
705             rOutliner.SetMinColumnWrapHeight(nAnkHgt);
706         }
707 
708         if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
709         {
710             rOutliner.SetMinAutoPaperSize(Size(0, nAnkHgt));
711             rOutliner.SetMinColumnWrapHeight(nAnkWdt);
712         }
713     }
714 
715     rOutliner.SetPaperSize(aNullSize);
716     if (bContourFrame)
717         ImpSetContourPolygon( rOutliner, aAnkRect, bLineWidth );
718 
719     // put text into the outliner, if available from the edit outliner
720     SdrText* pText = getActiveText();
721     OutlinerParaObject* pOutlinerParaObject = pText ? pText->GetOutlinerParaObject() : nullptr;
722     std::optional<OutlinerParaObject> pPara;
723     if (mpEditingOutliner && !bNoEditText)
724         pPara = mpEditingOutliner->CreateParaObject();
725     else if (pOutlinerParaObject)
726         pPara = *pOutlinerParaObject;
727 
728     if (pPara)
729     {
730         const bool bHitTest(&getSdrModelFromSdrObject().GetHitTestOutliner() == &rOutliner);
731         const SdrTextObj* pTestObj = rOutliner.GetTextObj();
732 
733         if( !pTestObj || !bHitTest || pTestObj != this ||
734             pTestObj->GetOutlinerParaObject() != pOutlinerParaObject )
735         {
736             if( bHitTest ) // #i33696# take back fix #i27510#
737             {
738                 rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
739                 rOutliner.SetTextObj( this );
740             }
741 
742             rOutliner.SetText(*pPara);
743         }
744     }
745     else
746     {
747         rOutliner.SetTextObj( nullptr );
748     }
749 
750     rOutliner.SetUpdateLayout(true);
751     rOutliner.SetControlWord(nStat0);
752 
753     if( pText )
754         pText->CheckPortionInfo(rOutliner);
755 
756     Point aTextPos(aAnkRect.TopLeft());
757     Size aTextSiz(rOutliner.GetPaperSize()); // GetPaperSize() adds a little tolerance, right?
758 
759     // For draw objects containing text correct hor/ver alignment if text is bigger
760     // than the object itself. Without that correction, the text would always be
761     // formatted to the left edge (or top edge when vertical) of the draw object.
762     if(!IsTextFrame())
763     {
764         if(aAnkRect.GetWidth() < aTextSiz.Width() && !IsVerticalWriting())
765         {
766             // Horizontal case here. Correct only if eHAdj == SDRTEXTHORZADJUST_BLOCK,
767             // else the alignment is wanted.
768             if(SDRTEXTHORZADJUST_BLOCK == eHAdj)
769             {
770                 eHAdj = SDRTEXTHORZADJUST_CENTER;
771             }
772         }
773 
774         if(aAnkRect.GetHeight() < aTextSiz.Height() && IsVerticalWriting())
775         {
776             // Vertical case here. Correct only if eHAdj == SDRTEXTVERTADJUST_BLOCK,
777             // else the alignment is wanted.
778             if(SDRTEXTVERTADJUST_BLOCK == eVAdj)
779             {
780                 eVAdj = SDRTEXTVERTADJUST_CENTER;
781             }
782         }
783     }
784 
785     if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
786     {
787         tools::Long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
788         if (eHAdj==SDRTEXTHORZADJUST_CENTER)
789             aTextPos.AdjustX(nFreeWdt/2 );
790         if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
791             aTextPos.AdjustX(nFreeWdt );
792     }
793     if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
794     {
795         tools::Long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
796         if (eVAdj==SDRTEXTVERTADJUST_CENTER)
797             aTextPos.AdjustY(nFreeHgt/2 );
798         if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
799             aTextPos.AdjustY(nFreeHgt );
800     }
801     if (maGeo.m_nRotationAngle)
802         RotatePoint(aTextPos,aAnkRect.TopLeft(),maGeo.mfSinRotationAngle,maGeo.mfCosRotationAngle);
803 
804     if (pAnchorRect)
805         *pAnchorRect=aAnkRect;
806 
807     // rTextRect might not be correct in some cases at ContourFrame
808     rTextRect=tools::Rectangle(aTextPos,aTextSiz);
809     if (bContourFrame)
810         rTextRect=aAnkRect;
811 }
812 
CanCreateEditOutlinerParaObject() const813 bool SdrTextObj::CanCreateEditOutlinerParaObject() const
814 {
815     if( HasTextImpl( mpEditingOutliner ) )
816     {
817         return mpEditingOutliner->GetParagraphCount() > 0;
818     }
819     return false;
820 }
821 
CreateEditOutlinerParaObject() const822 std::optional<OutlinerParaObject> SdrTextObj::CreateEditOutlinerParaObject() const
823 {
824     std::optional<OutlinerParaObject> pPara;
825     if( HasTextImpl( mpEditingOutliner ) )
826     {
827         sal_Int32 nParaCount = mpEditingOutliner->GetParagraphCount();
828         pPara = mpEditingOutliner->CreateParaObject(0, nParaCount);
829     }
830     return pPara;
831 }
832 
ImpSetCharStretching(SdrOutliner & rOutliner,const Size & rTextSize,const Size & rShapeSize,Fraction & rFitXCorrection)833 void SdrTextObj::ImpSetCharStretching(SdrOutliner& rOutliner, const Size& rTextSize, const Size& rShapeSize, Fraction& rFitXCorrection)
834 {
835     OutputDevice* pOut = rOutliner.GetRefDevice();
836     bool bNoStretching(false);
837 
838     if(pOut && pOut->GetOutDevType() == OUTDEV_PRINTER)
839     {
840         // check whether CharStretching is possible at all
841         GDIMetaFile* pMtf = pOut->GetConnectMetaFile();
842         OUString aTestString(u'J');
843 
844         if(pMtf && (!pMtf->IsRecord() || pMtf->IsPause()))
845             pMtf = nullptr;
846 
847         if(pMtf)
848             pMtf->Pause(true);
849 
850         vcl::Font aOriginalFont(pOut->GetFont());
851         vcl::Font aTmpFont( OutputDevice::GetDefaultFont( DefaultFontType::SERIF, LANGUAGE_SYSTEM, GetDefaultFontFlags::OnlyOne ) );
852 
853         aTmpFont.SetFontSize(Size(0,100));
854         pOut->SetFont(aTmpFont);
855         Size aSize1(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
856         aTmpFont.SetFontSize(Size(800,100));
857         pOut->SetFont(aTmpFont);
858         Size aSize2(pOut->GetTextWidth(aTestString), pOut->GetTextHeight());
859         pOut->SetFont(aOriginalFont);
860 
861         if(pMtf)
862             pMtf->Pause(false);
863 
864         bNoStretching = (aSize1 == aSize2);
865 
866 #ifdef _WIN32
867         // Windows zooms the font proportionally when using Size(100,500),
868         // we don't like that.
869         if(aSize2.Height() >= aSize1.Height() * 2)
870         {
871             bNoStretching = true;
872         }
873 #endif
874     }
875 
876     rOutliner.setRoundFontSizeToPt(false);
877 
878     unsigned nLoopCount=0;
879     bool bNoMoreLoop = false;
880     tools::Long nXDiff0=0x7FFFFFFF;
881     tools::Long nWantWdt=rShapeSize.Width();
882     tools::Long nIsWdt=rTextSize.Width();
883     if (nIsWdt==0) nIsWdt=1;
884 
885     tools::Long nWantHgt=rShapeSize.Height();
886     tools::Long nIsHgt=rTextSize.Height();
887     if (nIsHgt==0) nIsHgt=1;
888 
889     tools::Long nXTolPl=nWantWdt/100; // tolerance: +1%
890     tools::Long nXTolMi=nWantWdt/25;  // tolerance: -4%
891     tools::Long nXCorr =nWantWdt/20;  // correction scale: 5%
892 
893     double nX = nWantWdt / double(nIsWdt); // calculate X stretching
894     double nY = nWantHgt / double(nIsHgt); // calculate Y stretching
895     bool bChkX = true;
896     if (bNoStretching)
897     { // might only be possible proportionally
898         if (nX > nY)
899         {
900             nX = nY;
901             bChkX = false;
902         }
903         else
904         {
905             nY = nX;
906         }
907     }
908 
909     while (nLoopCount<5 && !bNoMoreLoop)
910     {
911         if (nX < 0.0)
912             nX = -nX;
913         if (nX < 0.01)
914         {
915             nX = 0.01;
916             bNoMoreLoop = true;
917         }
918         if (nX > 655.35)
919         {
920             nX = 655.35;
921             bNoMoreLoop = true;
922         }
923 
924         if (nY < 0.0)
925         {
926             nY = -nY;
927         }
928         if (nY < 0.01)
929         {
930             nY = 0.01;
931             bNoMoreLoop = true;
932         }
933         if (nY > 655.35)
934         {
935             nY = 655.35;
936             bNoMoreLoop = true;
937         }
938 
939         // exception, there is no text yet (horizontal case)
940         if (nIsWdt <= 1)
941         {
942             nX = nY;
943             bNoMoreLoop = true;
944         }
945 
946         // exception, there is no text yet (vertical case)
947         if (nIsHgt <= 1)
948         {
949             nY = nX;
950             bNoMoreLoop = true;
951         }
952         rOutliner.setScalingParameters({nX, nY});
953         nLoopCount++;
954         Size aSiz(rOutliner.CalcTextSize());
955         tools::Long nXDiff = aSiz.Width() - nWantWdt;
956         rFitXCorrection=Fraction(nWantWdt,aSiz.Width());
957         if (((nXDiff>=nXTolMi || !bChkX) && nXDiff<=nXTolPl) || nXDiff==nXDiff0) {
958             bNoMoreLoop = true;
959         } else {
960             // correct stretching factors
961             tools::Long nMul = nWantWdt;
962             tools::Long nDiv = aSiz.Width();
963             if (std::abs(nXDiff) <= 2 * nXCorr)
964             {
965                 if (nMul > nDiv)
966                     nDiv += (nMul - nDiv) / 2.0; // but only add half of what we calculated,
967                 else
968                     nMul += (nDiv - nMul) / 2.0;// because the EditEngine calculates wrongly later on
969             }
970             nX = nX * double(nMul) / double(nDiv);
971             if (bNoStretching)
972                 nY = nX;
973         }
974         nXDiff0 = nXDiff;
975     }
976 }
977 
TakeObjNameSingul() const978 OUString SdrTextObj::TakeObjNameSingul() const
979 {
980     OUString aStr;
981 
982     switch(meTextKind)
983     {
984         case SdrObjKind::OutlineText:
985         {
986             aStr = SvxResId(STR_ObjNameSingulOUTLINETEXT);
987             break;
988         }
989 
990         case SdrObjKind::TitleText  :
991         {
992             aStr = SvxResId(STR_ObjNameSingulTITLETEXT);
993             break;
994         }
995 
996         default:
997         {
998             if(IsLinkedText())
999                 aStr = SvxResId(STR_ObjNameSingulTEXTLNK);
1000             else
1001                 aStr = SvxResId(STR_ObjNameSingulTEXT);
1002             break;
1003         }
1004     }
1005 
1006     OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1007     if(pOutlinerParaObject && meTextKind != SdrObjKind::OutlineText)
1008     {
1009         // shouldn't currently cause any problems at OUTLINETEXT
1010         OUString aStr2(comphelper::string::stripStart(pOutlinerParaObject->GetTextObject().GetText(0), ' '));
1011 
1012         // avoid non expanded text portions in object name
1013         // (second condition is new)
1014         if(!aStr2.isEmpty() && aStr2.indexOf(u'\x00FF') == -1)
1015         {
1016             // space between ResStr and content text
1017             aStr += " \'";
1018 
1019             if(aStr2.getLength() > 10)
1020             {
1021                 aStr2 = OUString::Concat(aStr2.subView(0, 8)) + "...";
1022             }
1023 
1024             aStr += aStr2 + "\'";
1025         }
1026     }
1027 
1028     OUString sName(aStr);
1029 
1030     OUString aName(GetName());
1031     if (!aName.isEmpty())
1032         sName += " '" + aName + "'";
1033 
1034     return sName;
1035 }
1036 
TakeObjNamePlural() const1037 OUString SdrTextObj::TakeObjNamePlural() const
1038 {
1039     OUString sName;
1040     switch (meTextKind)
1041     {
1042         case SdrObjKind::OutlineText: sName=SvxResId(STR_ObjNamePluralOUTLINETEXT); break;
1043         case SdrObjKind::TitleText  : sName=SvxResId(STR_ObjNamePluralTITLETEXT);   break;
1044         default: {
1045             if (IsLinkedText()) {
1046                 sName=SvxResId(STR_ObjNamePluralTEXTLNK);
1047             } else {
1048                 sName=SvxResId(STR_ObjNamePluralTEXT);
1049             }
1050         } break;
1051     } // switch
1052     return sName;
1053 }
1054 
CloneSdrObject(SdrModel & rTargetModel) const1055 rtl::Reference<SdrObject> SdrTextObj::CloneSdrObject(SdrModel& rTargetModel) const
1056 {
1057     return new SdrTextObj(rTargetModel, *this);
1058 }
1059 
TakeXorPoly() const1060 basegfx::B2DPolyPolygon SdrTextObj::TakeXorPoly() const
1061 {
1062     tools::Polygon aPol(getRectangle());
1063     if (maGeo.m_nShearAngle)
1064         ShearPoly(aPol, getRectangle().TopLeft(), maGeo.mfTanShearAngle);
1065     if (maGeo.m_nRotationAngle)
1066         RotatePoly(aPol, getRectangle().TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1067 
1068     basegfx::B2DPolyPolygon aRetval;
1069     aRetval.append(aPol.getB2DPolygon());
1070     return aRetval;
1071 }
1072 
TakeContour() const1073 basegfx::B2DPolyPolygon SdrTextObj::TakeContour() const
1074 {
1075     basegfx::B2DPolyPolygon aRetval(SdrAttrObj::TakeContour());
1076 
1077     // and now add the BoundRect of the text, if necessary
1078     if ( GetOutlinerParaObject() && !IsFontwork() && !IsContourTextFrame() )
1079     {
1080         // using Clone()-Paint() strategy inside TakeContour() leaves a destroyed
1081         // SdrObject as pointer in DrawOutliner. Set *this again in fetching the outliner
1082         // in every case
1083         SdrOutliner& rOutliner=ImpGetDrawOutliner();
1084 
1085         tools::Rectangle aAnchor2;
1086         tools::Rectangle aR;
1087         TakeTextRect(rOutliner,aR,false,&aAnchor2);
1088         rOutliner.Clear();
1089         bool bFitToSize(IsFitToSize());
1090         if (bFitToSize) aR=aAnchor2;
1091         tools::Polygon aPol(aR);
1092         if (maGeo.m_nRotationAngle) RotatePoly(aPol,aR.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1093 
1094         aRetval.append(aPol.getB2DPolygon());
1095     }
1096 
1097     return aRetval;
1098 }
1099 
RecalcSnapRect()1100 void SdrTextObj::RecalcSnapRect()
1101 {
1102     if (maGeo.m_nRotationAngle || maGeo.m_nShearAngle)
1103     {
1104         maSnapRect = Rect2Poly(getRectangle(), maGeo).GetBoundRect();
1105     } else {
1106         maSnapRect = getRectangle();
1107     }
1108 }
1109 
GetSnapPointCount() const1110 sal_uInt32 SdrTextObj::GetSnapPointCount() const
1111 {
1112     return 4;
1113 }
1114 
GetSnapPoint(sal_uInt32 i) const1115 Point SdrTextObj::GetSnapPoint(sal_uInt32 i) const
1116 {
1117     Point aP;
1118     const auto& rRectangle = getRectangle();
1119     switch (i) {
1120         case 0: aP = rRectangle.TopLeft(); break;
1121         case 1: aP = rRectangle.TopRight(); break;
1122         case 2: aP = rRectangle.BottomLeft(); break;
1123         case 3: aP = rRectangle.BottomRight(); break;
1124         default: aP = rRectangle.Center(); break;
1125     }
1126     if (maGeo.m_nShearAngle)
1127         ShearPoint(aP, rRectangle.TopLeft(), maGeo.mfTanShearAngle);
1128     if (maGeo.m_nRotationAngle)
1129         RotatePoint(aP, rRectangle.TopLeft(), maGeo.mfSinRotationAngle, maGeo.mfCosRotationAngle);
1130     return aP;
1131 }
1132 
1133 // Extracted from ImpGetDrawOutliner()
ImpInitDrawOutliner(SdrOutliner & rOutl) const1134 void SdrTextObj::ImpInitDrawOutliner( SdrOutliner& rOutl ) const
1135 {
1136     rOutl.SetUpdateLayout(false);
1137     OutlinerMode nOutlinerMode = OutlinerMode::OutlineObject;
1138     if ( !IsOutlText() )
1139         nOutlinerMode = OutlinerMode::TextObject;
1140     rOutl.Init( nOutlinerMode );
1141 
1142     rOutl.resetScalingParameters();
1143 
1144     EEControlBits nStat=rOutl.GetControlWord();
1145     nStat &= ~EEControlBits(EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE);
1146     rOutl.SetControlWord(nStat);
1147     Size aMaxSize(100000,100000);
1148     rOutl.SetMinAutoPaperSize(Size());
1149     rOutl.SetMaxAutoPaperSize(aMaxSize);
1150     rOutl.SetPaperSize(aMaxSize);
1151     rOutl.ClearPolygon();
1152 }
1153 
ImpGetDrawOutliner() const1154 SdrOutliner& SdrTextObj::ImpGetDrawOutliner() const
1155 {
1156     SdrOutliner& rOutl(getSdrModelFromSdrObject().GetDrawOutliner(this));
1157 
1158     // Code extracted to ImpInitDrawOutliner()
1159     ImpInitDrawOutliner( rOutl );
1160 
1161     return rOutl;
1162 }
1163 
1164 // Extracted from Paint()
ImpSetupDrawOutlinerForPaint(bool bContourFrame,SdrOutliner & rOutliner,tools::Rectangle & rTextRect,tools::Rectangle & rAnchorRect,tools::Rectangle & rPaintRect,Fraction & rFitXCorrection) const1165 void SdrTextObj::ImpSetupDrawOutlinerForPaint( bool             bContourFrame,
1166                                                SdrOutliner&     rOutliner,
1167                                                tools::Rectangle&       rTextRect,
1168                                                tools::Rectangle&       rAnchorRect,
1169                                                tools::Rectangle&       rPaintRect,
1170                                                Fraction&        rFitXCorrection ) const
1171 {
1172     if (!bContourFrame)
1173     {
1174         // FitToSize can't be used together with ContourFrame for now
1175         if (IsFitToSize() || IsAutoFit())
1176         {
1177             EEControlBits nStat=rOutliner.GetControlWord();
1178             nStat|=EEControlBits::STRETCHING|EEControlBits::AUTOPAGESIZE;
1179             rOutliner.SetControlWord(nStat);
1180         }
1181     }
1182     rOutliner.SetFixedCellHeight(GetMergedItem(SDRATTR_TEXT_USEFIXEDCELLHEIGHT).GetValue());
1183     TakeTextRect(rOutliner, rTextRect, false, &rAnchorRect);
1184     rPaintRect = rTextRect;
1185 
1186     if (bContourFrame)
1187         return;
1188 
1189     // FitToSize can't be used together with ContourFrame for now
1190     if (IsFitToSize())
1191     {
1192         ImpSetCharStretching(rOutliner,rTextRect.GetSize(),rAnchorRect.GetSize(),rFitXCorrection);
1193         rPaintRect=rAnchorRect;
1194     }
1195     else if (IsAutoFit())
1196     {
1197         setupAutoFitText(rOutliner);
1198     }
1199 }
1200 
GetFontScale() const1201 double SdrTextObj::GetFontScale() const
1202 {
1203     SdrOutliner& rOutliner = ImpGetDrawOutliner();
1204     // This eventually calls setupAutoFitText
1205     UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
1206 
1207     return rOutliner.getScalingParameters().fFontY;
1208 }
1209 
GetSpacingScale() const1210 double SdrTextObj::GetSpacingScale() const
1211 {
1212     SdrOutliner& rOutliner = ImpGetDrawOutliner();
1213     // This eventually calls setupAutoFitText
1214     UpdateOutlinerFormatting(rOutliner, o3tl::temporary(tools::Rectangle()));
1215 
1216     return rOutliner.getScalingParameters().fSpacingY;
1217 }
1218 
setupAutoFitText(SdrOutliner & rOutliner) const1219 void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner) const
1220 {
1221     const Size aShapeSize = GetSnapRect().GetSize();
1222     Size aSize(aShapeSize.Width() - GetTextLeftDistance() - GetTextRightDistance(),
1223                aShapeSize.Height() - GetTextUpperDistance() - GetTextLowerDistance());
1224 
1225     setupAutoFitText(rOutliner, aSize);
1226 }
setupAutoFitText(SdrOutliner & rOutliner,const Size & rTextBoxSize) const1227 void SdrTextObj::setupAutoFitText(SdrOutliner& rOutliner, const Size& rTextBoxSize) const
1228 {
1229     rOutliner.setRoundFontSizeToPt(true); // We need to round the font size nearest integer pt size
1230     rOutliner.SetMaxAutoPaperSize(rTextBoxSize);
1231     rOutliner.SetPaperSize(rTextBoxSize);
1232 
1233     const SdrTextFitToSizeTypeItem& rItem = GetObjectItem(SDRATTR_TEXT_FITTOSIZE);
1234 
1235     double fFontScale = rItem.getFontScale();
1236     double fSpacingScale = rItem.getSpacingScale();
1237 
1238     if (fFontScale > 0.0 && fSpacingScale > 0.0 && !mbInEditMode)
1239     {
1240         rOutliner.setScalingParameters({ fFontScale, fFontScale, 1.0, fSpacingScale });
1241     }
1242     else
1243     {
1244         rOutliner.resetScalingParameters();
1245     }
1246 
1247     rOutliner.QuickFormatDoc();
1248 }
1249 
SetupOutlinerFormatting(SdrOutliner & rOutl,tools::Rectangle & rPaintRect) const1250 void SdrTextObj::SetupOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1251 {
1252     ImpInitDrawOutliner( rOutl );
1253     UpdateOutlinerFormatting( rOutl, rPaintRect );
1254 }
1255 
UpdateOutlinerFormatting(SdrOutliner & rOutl,tools::Rectangle & rPaintRect) const1256 void SdrTextObj::UpdateOutlinerFormatting( SdrOutliner& rOutl, tools::Rectangle& rPaintRect ) const
1257 {
1258     tools::Rectangle aTextRect;
1259     tools::Rectangle aAnchorRect;
1260     Fraction aFitXCorrection(1,1);
1261 
1262     const bool bContourFrame(IsContourTextFrame());
1263     const MapMode aMapMode(getSdrModelFromSdrObject().GetScaleUnit());
1264 
1265     rOutl.SetRefMapMode(aMapMode);
1266     ImpSetupDrawOutlinerForPaint(
1267         bContourFrame,
1268         rOutl,
1269         aTextRect,
1270         aAnchorRect,
1271         rPaintRect,
1272         aFitXCorrection);
1273 }
1274 
1275 
GetOutlinerParaObject() const1276 OutlinerParaObject* SdrTextObj::GetOutlinerParaObject() const
1277 {
1278     SdrText* pText = getActiveText();
1279     if( pText )
1280         return pText->GetOutlinerParaObject();
1281     else
1282         return nullptr;
1283 }
1284 
NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject,bool bAdjustTextFrameWidthAndHeight)1285 void SdrTextObj::NbcSetOutlinerParaObject(std::optional<OutlinerParaObject> pTextObject, bool bAdjustTextFrameWidthAndHeight)
1286 {
1287     NbcSetOutlinerParaObjectForText( std::move(pTextObject), getActiveText(), bAdjustTextFrameWidthAndHeight );
1288 }
1289 
1290 namespace
1291 {
IsAutoGrow(const SdrTextObj & rObj)1292     bool IsAutoGrow(const SdrTextObj& rObj)
1293     {
1294         bool bAutoGrow = rObj.IsAutoGrowHeight() || rObj.IsAutoGrowWidth();
1295         return bAutoGrow && !comphelper::IsFuzzing();
1296     }
1297 }
1298 
NbcSetOutlinerParaObjectForText(std::optional<OutlinerParaObject> pTextObject,SdrText * pText,bool bAdjustTextFrameWidthAndHeight)1299 void SdrTextObj::NbcSetOutlinerParaObjectForText( std::optional<OutlinerParaObject> pTextObject, SdrText* pText, bool bAdjustTextFrameWidthAndHeight )
1300 {
1301     if( pText )
1302         pText->SetOutlinerParaObject( std::move(pTextObject) );
1303 
1304     if (pText && pText->GetOutlinerParaObject())
1305     {
1306         SvxWritingModeItem aWritingMode(pText->GetOutlinerParaObject()->IsEffectivelyVertical() && pText->GetOutlinerParaObject()->IsTopToBottom()
1307             ? css::text::WritingMode_TB_RL
1308             : css::text::WritingMode_LR_TB,
1309             SDRATTR_TEXTDIRECTION);
1310         GetProperties().SetObjectItemDirect(aWritingMode);
1311     }
1312 
1313     SetTextSizeDirty();
1314     if (IsTextFrame() && IsAutoGrow(*this) && bAdjustTextFrameWidthAndHeight)
1315     { // adapt text frame!
1316         NbcAdjustTextFrameWidthAndHeight();
1317     }
1318     if (!IsTextFrame())
1319     {
1320         // the SnapRect keeps its size
1321         SetBoundAndSnapRectsDirty(true);
1322     }
1323 
1324     // always invalidate BoundRect on change
1325     SetBoundRectDirty();
1326     ActionChanged();
1327 
1328     ImpSetTextStyleSheetListeners();
1329 }
1330 
NbcReformatText()1331 void SdrTextObj::NbcReformatText()
1332 {
1333     SdrText* pText = getActiveText();
1334     if( !(pText && pText->GetOutlinerParaObject()) )
1335         return;
1336 
1337     pText->ReformatText();
1338     if (mbTextFrame)
1339     {
1340         NbcAdjustTextFrameWidthAndHeight();
1341     }
1342     else
1343     {
1344         // the SnapRect keeps its size
1345         SetBoundRectDirty();
1346         SetBoundAndSnapRectsDirty(/*bNotMyself*/true);
1347     }
1348     SetTextSizeDirty();
1349     ActionChanged();
1350     // i22396
1351     // Necessary here since we have no compare operator at the outliner
1352     // para object which may detect changes regarding the combination
1353     // of outliner para data and configuration (e.g., change of
1354     // formatting of text numerals)
1355     GetViewContact().flushViewObjectContacts(false);
1356 }
1357 
NewGeoData() const1358 std::unique_ptr<SdrObjGeoData> SdrTextObj::NewGeoData() const
1359 {
1360     return std::make_unique<SdrTextObjGeoData>();
1361 }
1362 
SaveGeoData(SdrObjGeoData & rGeo) const1363 void SdrTextObj::SaveGeoData(SdrObjGeoData& rGeo) const
1364 {
1365     SdrAttrObj::SaveGeoData(rGeo);
1366     SdrTextObjGeoData& rTGeo=static_cast<SdrTextObjGeoData&>(rGeo);
1367     rTGeo.maRect = getRectangle();
1368     rTGeo.maGeo = maGeo;
1369 }
1370 
RestoreGeoData(const SdrObjGeoData & rGeo)1371 void SdrTextObj::RestoreGeoData(const SdrObjGeoData& rGeo)
1372 { // RectsDirty is called by SdrObject
1373     SdrAttrObj::RestoreGeoData(rGeo);
1374     const SdrTextObjGeoData& rTGeo=static_cast<const SdrTextObjGeoData&>(rGeo);
1375     NbcSetLogicRect(rTGeo.maRect);
1376     maGeo = rTGeo.maGeo;
1377     SetTextSizeDirty();
1378 }
1379 
GetFitToSize() const1380 drawing::TextFitToSizeType SdrTextObj::GetFitToSize() const
1381 {
1382     drawing::TextFitToSizeType eType = drawing::TextFitToSizeType_NONE;
1383 
1384     if(!IsAutoGrowWidth())
1385         eType = GetObjectItem(SDRATTR_TEXT_FITTOSIZE).GetValue();
1386 
1387     return eType;
1388 }
1389 
GetGeoRect() const1390 const tools::Rectangle& SdrTextObj::GetGeoRect() const
1391 {
1392     return getRectangle();
1393 }
1394 
ForceOutlinerParaObject()1395 void SdrTextObj::ForceOutlinerParaObject()
1396 {
1397     SdrText* pText = getActiveText();
1398     if( pText && (pText->GetOutlinerParaObject() == nullptr) )
1399     {
1400         OutlinerMode nOutlMode = OutlinerMode::TextObject;
1401         if( IsTextFrame() && meTextKind == SdrObjKind::OutlineText )
1402             nOutlMode = OutlinerMode::OutlineObject;
1403 
1404         pText->ForceOutlinerParaObject( nOutlMode );
1405     }
1406 }
1407 
GetTextChain() const1408 TextChain *SdrTextObj::GetTextChain() const
1409 {
1410     //if (!IsChainable())
1411     //    return NULL;
1412 
1413     return getSdrModelFromSdrObject().GetTextChain();
1414 }
1415 
IsVerticalWriting() const1416 bool SdrTextObj::IsVerticalWriting() const
1417 {
1418     if(mpEditingOutliner)
1419     {
1420         return mpEditingOutliner->IsVertical();
1421     }
1422 
1423     OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1424     if(pOutlinerParaObject)
1425     {
1426         return pOutlinerParaObject->IsEffectivelyVertical();
1427     }
1428 
1429     return false;
1430 }
1431 
SetVerticalWriting(bool bVertical)1432 void SdrTextObj::SetVerticalWriting(bool bVertical)
1433 {
1434     OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject();
1435 
1436     if( !pOutlinerParaObject && bVertical )
1437     {
1438         // we only need to force an outliner para object if the default of
1439         // horizontal text is changed
1440         ForceOutlinerParaObject();
1441         pOutlinerParaObject = GetOutlinerParaObject();
1442     }
1443 
1444     if (!pOutlinerParaObject ||
1445         (pOutlinerParaObject->IsEffectivelyVertical() == bVertical))
1446         return;
1447 
1448     // get item settings
1449     const SfxItemSet& rSet = GetObjectItemSet();
1450     bool bAutoGrowWidth = rSet.Get(SDRATTR_TEXT_AUTOGROWWIDTH).GetValue();
1451     bool bAutoGrowHeight = rSet.Get(SDRATTR_TEXT_AUTOGROWHEIGHT).GetValue();
1452 
1453     // Also exchange hor/ver adjust items
1454     SdrTextHorzAdjust eHorz = rSet.Get(SDRATTR_TEXT_HORZADJUST).GetValue();
1455     SdrTextVertAdjust eVert = rSet.Get(SDRATTR_TEXT_VERTADJUST).GetValue();
1456 
1457     // rescue object size
1458     tools::Rectangle aObjectRect = GetSnapRect();
1459 
1460     // prepare ItemSet to set exchanged width and height items
1461     SfxItemSet aNewSet(SfxItemSet::makeFixedSfxItemSet<SDRATTR_TEXT_AUTOGROWHEIGHT, SDRATTR_TEXT_AUTOGROWHEIGHT,
1462                                                        // Expanded item ranges to also support hor and ver adjust.
1463                                                        SDRATTR_TEXT_VERTADJUST, SDRATTR_TEXT_VERTADJUST,
1464                                                        SDRATTR_TEXT_AUTOGROWWIDTH, SDRATTR_TEXT_HORZADJUST>(*rSet.GetPool()));
1465 
1466     aNewSet.Put(rSet);
1467     aNewSet.Put(makeSdrTextAutoGrowWidthItem(bAutoGrowHeight));
1468     aNewSet.Put(makeSdrTextAutoGrowHeightItem(bAutoGrowWidth));
1469 
1470     // Exchange horz and vert adjusts
1471     switch (eVert)
1472     {
1473         case SDRTEXTVERTADJUST_TOP: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); break;
1474         case SDRTEXTVERTADJUST_CENTER: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_CENTER)); break;
1475         case SDRTEXTVERTADJUST_BOTTOM: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); break;
1476         case SDRTEXTVERTADJUST_BLOCK: aNewSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); break;
1477     }
1478     switch (eHorz)
1479     {
1480         case SDRTEXTHORZADJUST_LEFT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); break;
1481         case SDRTEXTHORZADJUST_CENTER: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); break;
1482         case SDRTEXTHORZADJUST_RIGHT: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); break;
1483         case SDRTEXTHORZADJUST_BLOCK: aNewSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); break;
1484     }
1485 
1486     SetObjectItemSet(aNewSet);
1487 
1488     pOutlinerParaObject = GetOutlinerParaObject();
1489     if (pOutlinerParaObject)
1490     {
1491         // set ParaObject orientation accordingly
1492         pOutlinerParaObject->SetVertical(bVertical);
1493     }
1494 
1495     // restore object size
1496     SetSnapRect(aObjectRect);
1497 }
1498 
IsTopToBottom() const1499 bool SdrTextObj::IsTopToBottom() const
1500 {
1501     if (mpEditingOutliner)
1502         return mpEditingOutliner->IsTopToBottom();
1503 
1504     if (OutlinerParaObject* pOutlinerParaObject = GetOutlinerParaObject())
1505         return pOutlinerParaObject->IsTopToBottom();
1506 
1507     return false;
1508 }
1509 
1510 // transformation interface for StarOfficeAPI. This implements support for
1511 // homogeneous 3x3 matrices containing the transformation of the SdrObject. At the
1512 // moment it contains a shearX, rotation and translation, but for setting all linear
1513 // transforms like Scale, ShearX, ShearY, Rotate and Translate are supported.
1514 
1515 
1516 // gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
1517 // with the base geometry and returns TRUE. Otherwise it returns FALSE.
TRGetBaseGeometry(basegfx::B2DHomMatrix & rMatrix,basegfx::B2DPolyPolygon &) const1518 bool SdrTextObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& /*rPolyPolygon*/) const
1519 {
1520     // get turn and shear
1521     double fRotate = toRadians(maGeo.m_nRotationAngle);
1522     double fShearX = toRadians(maGeo.m_nShearAngle);
1523 
1524     // get aRect, this is the unrotated snaprect
1525     tools::Rectangle aRectangle(getRectangle());
1526 
1527     // fill other values
1528     basegfx::B2DTuple aScale(aRectangle.GetWidth(), aRectangle.GetHeight());
1529     basegfx::B2DTuple aTranslate(aRectangle.Left(), aRectangle.Top());
1530 
1531     // position maybe relative to anchorpos, convert
1532     if( getSdrModelFromSdrObject().IsWriter() )
1533     {
1534         if(GetAnchorPos().X() || GetAnchorPos().Y())
1535         {
1536             aTranslate -= basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1537         }
1538     }
1539 
1540     // build matrix
1541     rMatrix = basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix(
1542         aScale,
1543         basegfx::fTools::equalZero(fShearX) ? 0.0 : tan(fShearX),
1544         -fRotate,
1545         aTranslate);
1546 
1547     return false;
1548 }
1549 
1550 // sets the base geometry of the object using infos contained in the homogeneous 3x3 matrix.
1551 // If it's an SdrPathObj it will use the provided geometry information. The Polygon has
1552 // to use (0,0) as upper left and will be scaled to the given size in the matrix.
TRSetBaseGeometry(const basegfx::B2DHomMatrix & rMatrix,const basegfx::B2DPolyPolygon &)1553 void SdrTextObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& /*rPolyPolygon*/)
1554 {
1555     // break up matrix
1556     basegfx::B2DTuple aScale;
1557     basegfx::B2DTuple aTranslate;
1558     double fRotate(0.0);
1559     double fShearX(0.0);
1560     rMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
1561 
1562     // flip?
1563     bool bFlipX = aScale.getX() < 0.0,
1564          bFlipY = aScale.getY() < 0.0;
1565     if (bFlipX)
1566     {
1567         aScale.setX(fabs(aScale.getX()));
1568     }
1569     if (bFlipY)
1570     {
1571         aScale.setY(fabs(aScale.getY()));
1572     }
1573 
1574     // reset object shear and rotations
1575     maGeo.m_nRotationAngle = 0_deg100;
1576     maGeo.RecalcSinCos();
1577     maGeo.m_nShearAngle = 0_deg100;
1578     maGeo.RecalcTan();
1579 
1580     // if anchor is used, make position relative to it
1581     if( getSdrModelFromSdrObject().IsWriter() )
1582     {
1583         if(GetAnchorPos().X() || GetAnchorPos().Y())
1584         {
1585             aTranslate += basegfx::B2DTuple(GetAnchorPos().X(), GetAnchorPos().Y());
1586         }
1587     }
1588 
1589     // build and set BaseRect (use scale)
1590     Size aSize(basegfx::fround<tools::Long>(aScale.getX()),
1591                basegfx::fround<tools::Long>(aScale.getY()));
1592     tools::Rectangle aBaseRect(Point(), aSize);
1593     SetSnapRect(aBaseRect);
1594 
1595     // flip?
1596     if (bFlipX)
1597     {
1598         Mirror(Point(), Point(0, 1));
1599     }
1600     if (bFlipY)
1601     {
1602         Mirror(Point(), Point(1, 0));
1603     }
1604 
1605     // shear?
1606     if(!basegfx::fTools::equalZero(fShearX))
1607     {
1608         GeoStat aGeoStat;
1609         aGeoStat.m_nShearAngle = Degree100(basegfx::fround(basegfx::rad2deg<100>(atan(fShearX))));
1610         aGeoStat.RecalcTan();
1611         Shear(Point(), aGeoStat.m_nShearAngle, aGeoStat.mfTanShearAngle, false);
1612     }
1613 
1614     // rotation?
1615     if(!basegfx::fTools::equalZero(fRotate))
1616     {
1617         GeoStat aGeoStat;
1618 
1619         // #i78696#
1620         // fRotate is matematically correct, but aGeoStat.nRotationAngle is
1621         // mirrored -> mirror value here
1622         aGeoStat.m_nRotationAngle = NormAngle36000(Degree100(basegfx::fround(-basegfx::rad2deg<100>(fRotate))));
1623         aGeoStat.RecalcSinCos();
1624         Rotate(Point(), aGeoStat.m_nRotationAngle, aGeoStat.mfSinRotationAngle, aGeoStat.mfCosRotationAngle);
1625     }
1626 
1627     // translate?
1628     if(!aTranslate.equalZero())
1629     {
1630         Move(Size(basegfx::fround<tools::Long>(aTranslate.getX()),
1631                   basegfx::fround<tools::Long>(aTranslate.getY())));
1632     }
1633 }
1634 
IsReallyEdited() const1635 bool SdrTextObj::IsReallyEdited() const
1636 {
1637     return mpEditingOutliner && mpEditingOutliner->IsModified();
1638 }
1639 
1640 // moved inlines here form hxx
1641 
GetEckenradius() const1642 tools::Long SdrTextObj::GetEckenradius() const
1643 {
1644     return GetObjectItemSet().Get(SDRATTR_CORNER_RADIUS).GetValue();
1645 }
1646 
GetMinTextFrameHeight() const1647 tools::Long SdrTextObj::GetMinTextFrameHeight() const
1648 {
1649     return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEHEIGHT).GetValue();
1650 }
1651 
GetMaxTextFrameHeight() const1652 tools::Long SdrTextObj::GetMaxTextFrameHeight() const
1653 {
1654     return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEHEIGHT).GetValue();
1655 }
1656 
GetMinTextFrameWidth() const1657 tools::Long SdrTextObj::GetMinTextFrameWidth() const
1658 {
1659     return GetObjectItemSet().Get(SDRATTR_TEXT_MINFRAMEWIDTH).GetValue();
1660 }
1661 
GetMaxTextFrameWidth() const1662 tools::Long SdrTextObj::GetMaxTextFrameWidth() const
1663 {
1664     return GetObjectItemSet().Get(SDRATTR_TEXT_MAXFRAMEWIDTH).GetValue();
1665 }
1666 
IsFontwork() const1667 bool SdrTextObj::IsFontwork() const
1668 {
1669     return !mbTextFrame // Default is FALSE
1670         && GetObjectItemSet().Get(XATTR_FORMTXTSTYLE).GetValue() != XFormTextStyle::NONE;
1671 }
1672 
IsHideContour() const1673 bool SdrTextObj::IsHideContour() const
1674 {
1675     return !mbTextFrame // Default is: no, don't HideContour; HideContour not together with TextFrames
1676         && GetObjectItemSet().Get(XATTR_FORMTXTHIDEFORM).GetValue();
1677 }
1678 
IsContourTextFrame() const1679 bool SdrTextObj::IsContourTextFrame() const
1680 {
1681     return !mbTextFrame // ContourFrame not together with normal TextFrames
1682         && GetObjectItemSet().Get(SDRATTR_TEXT_CONTOURFRAME).GetValue();
1683 }
1684 
GetTextLeftDistance() const1685 tools::Long SdrTextObj::GetTextLeftDistance() const
1686 {
1687     return GetObjectItemSet().Get(SDRATTR_TEXT_LEFTDIST).GetValue();
1688 }
1689 
GetTextRightDistance() const1690 tools::Long SdrTextObj::GetTextRightDistance() const
1691 {
1692     return GetObjectItemSet().Get(SDRATTR_TEXT_RIGHTDIST).GetValue();
1693 }
1694 
GetTextUpperDistance() const1695 tools::Long SdrTextObj::GetTextUpperDistance() const
1696 {
1697     return GetObjectItemSet().Get(SDRATTR_TEXT_UPPERDIST).GetValue();
1698 }
1699 
GetTextLowerDistance() const1700 tools::Long SdrTextObj::GetTextLowerDistance() const
1701 {
1702     return GetObjectItemSet().Get(SDRATTR_TEXT_LOWERDIST).GetValue();
1703 }
1704 
GetTextAniKind() const1705 SdrTextAniKind SdrTextObj::GetTextAniKind() const
1706 {
1707     return GetObjectItemSet().Get(SDRATTR_TEXT_ANIKIND).GetValue();
1708 }
1709 
GetTextAniDirection() const1710 SdrTextAniDirection SdrTextObj::GetTextAniDirection() const
1711 {
1712     return GetObjectItemSet().Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1713 }
1714 
HasTextColumnsNumber() const1715 bool SdrTextObj::HasTextColumnsNumber() const
1716 {
1717     return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_NUMBER);
1718 }
1719 
GetTextColumnsNumber() const1720 sal_Int16 SdrTextObj::GetTextColumnsNumber() const
1721 {
1722     return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_NUMBER).GetValue();
1723 }
1724 
SetTextColumnsNumber(sal_Int16 nColumns)1725 void SdrTextObj::SetTextColumnsNumber(sal_Int16 nColumns)
1726 {
1727     SetObjectItem(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, nColumns));
1728 }
1729 
HasTextColumnsSpacing() const1730 bool SdrTextObj::HasTextColumnsSpacing() const
1731 {
1732     return GetObjectItemSet().HasItem(SDRATTR_TEXTCOLUMNS_SPACING);
1733 }
1734 
GetTextColumnsSpacing() const1735 sal_Int32 SdrTextObj::GetTextColumnsSpacing() const
1736 {
1737     return GetObjectItemSet().Get(SDRATTR_TEXTCOLUMNS_SPACING).GetValue();
1738 }
1739 
SetTextColumnsSpacing(sal_Int32 nSpacing)1740 void SdrTextObj::SetTextColumnsSpacing(sal_Int32 nSpacing)
1741 {
1742     SetObjectItem(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing));
1743 }
1744 
1745 // Get necessary data for text scroll animation. ATM base it on a Text-Metafile and a
1746 // painting rectangle. Rotation is excluded from the returned values.
GetTextScrollMetaFileAndRectangle(tools::Rectangle & rScrollRectangle,tools::Rectangle & rPaintRectangle)1747 GDIMetaFile* SdrTextObj::GetTextScrollMetaFileAndRectangle(
1748     tools::Rectangle& rScrollRectangle, tools::Rectangle& rPaintRectangle)
1749 {
1750     GDIMetaFile* pRetval = nullptr;
1751     SdrOutliner& rOutliner = ImpGetDrawOutliner();
1752     tools::Rectangle aTextRect;
1753     tools::Rectangle aAnchorRect;
1754     tools::Rectangle aPaintRect;
1755     Fraction aFitXCorrection(1,1);
1756     bool bContourFrame(IsContourTextFrame());
1757 
1758     // get outliner set up. To avoid getting a somehow rotated MetaFile,
1759     // temporarily disable object rotation.
1760     Degree100 nAngle(maGeo.m_nRotationAngle);
1761     maGeo.m_nRotationAngle = 0_deg100;
1762     ImpSetupDrawOutlinerForPaint( bContourFrame, rOutliner, aTextRect, aAnchorRect, aPaintRect, aFitXCorrection );
1763     maGeo.m_nRotationAngle = nAngle;
1764 
1765     tools::Rectangle aScrollFrameRect(aPaintRect);
1766     const SfxItemSet& rSet = GetObjectItemSet();
1767     SdrTextAniDirection eDirection = rSet.Get(SDRATTR_TEXT_ANIDIRECTION).GetValue();
1768 
1769     if(SdrTextAniDirection::Left == eDirection || SdrTextAniDirection::Right == eDirection)
1770     {
1771         aScrollFrameRect.SetLeft( aAnchorRect.Left() );
1772         aScrollFrameRect.SetRight( aAnchorRect.Right() );
1773     }
1774 
1775     if(SdrTextAniDirection::Up == eDirection || SdrTextAniDirection::Down == eDirection)
1776     {
1777         aScrollFrameRect.SetTop( aAnchorRect.Top() );
1778         aScrollFrameRect.SetBottom( aAnchorRect.Bottom() );
1779     }
1780 
1781     // create the MetaFile
1782     pRetval = new GDIMetaFile;
1783     ScopedVclPtrInstance< VirtualDevice > pBlackHole;
1784     pBlackHole->EnableOutput(false);
1785     pRetval->Record(pBlackHole);
1786     Point aPaintPos = aPaintRect.TopLeft();
1787 
1788     rOutliner.DrawText_ToPosition(*pBlackHole, aPaintPos);
1789 
1790     pRetval->Stop();
1791     pRetval->WindStart();
1792 
1793     // return PaintRectanglePixel and pRetval;
1794     rScrollRectangle = aScrollFrameRect;
1795     rPaintRectangle = aPaintRect;
1796 
1797     return pRetval;
1798 }
1799 
1800 // Access to TextAnimationAllowed flag
IsAutoFit() const1801 bool SdrTextObj::IsAutoFit() const
1802 {
1803     return GetFitToSize() == drawing::TextFitToSizeType_AUTOFIT;
1804 }
1805 
IsFitToSize() const1806 bool SdrTextObj::IsFitToSize() const
1807 {
1808     const drawing::TextFitToSizeType eFit = GetFitToSize();
1809     return (eFit == drawing::TextFitToSizeType_PROPORTIONAL
1810          || eFit == drawing::TextFitToSizeType_ALLLINES);
1811 }
1812 
SetTextAnimationAllowed(bool bNew)1813 void SdrTextObj::SetTextAnimationAllowed(bool bNew)
1814 {
1815     if(mbTextAnimationAllowed != bNew)
1816     {
1817         mbTextAnimationAllowed = bNew;
1818         ActionChanged();
1819     }
1820 }
1821 
1822 /** called from the SdrObjEditView during text edit when the status of the edit outliner changes */
onEditOutlinerStatusEvent(EditStatus * pEditStatus)1823 void SdrTextObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
1824 {
1825     const EditStatusFlags nStat = pEditStatus->GetStatusWord();
1826     const bool bGrowX = bool(nStat & EditStatusFlags::TEXTWIDTHCHANGED);
1827     const bool bGrowY = bool(nStat & EditStatusFlags::TextHeightChanged);
1828     if(!(mbTextFrame && (bGrowX || bGrowY)))
1829         return;
1830 
1831     if ((bGrowX && IsAutoGrowWidth()) || (bGrowY && IsAutoGrowHeight()))
1832     {
1833         AdjustTextFrameWidthAndHeight();
1834     }
1835     else if ( (IsAutoFit() || IsFitToSize()) && !mbInDownScale)
1836     {
1837         assert(mpEditingOutliner);
1838         mbInDownScale = true;
1839 
1840         // Need to reset scaling so it searches for the fitting size again
1841         mpEditingOutliner->resetScalingParameters();
1842 
1843         // sucks that we cannot disable paints via
1844         // mpEditingOutliner->SetUpdateMode(FALSE) - but EditEngine skips
1845         // formatting as well, then.
1846         setupAutoFitText(*mpEditingOutliner);
1847         mbInDownScale = false;
1848     }
1849 }
1850 
1851 /* Begin chaining code */
1852 
1853 // XXX: Make it a method somewhere?
ImpGetObjByName(SdrObjList const * pObjList,std::u16string_view aObjName)1854 static SdrObject *ImpGetObjByName(SdrObjList const *pObjList, std::u16string_view aObjName)
1855 {
1856     // scan the whole list
1857     for (const rtl::Reference<SdrObject>& pCurObj : *pObjList)
1858         if (pCurObj->GetName() == aObjName)
1859             return pCurObj.get();
1860     // not found
1861     return nullptr;
1862 }
1863 
1864 // XXX: Make it a (private) method of SdrTextObj
ImpUpdateChainLinks(SdrTextObj * pTextObj,std::u16string_view aNextLinkName)1865 static void ImpUpdateChainLinks(SdrTextObj *pTextObj, std::u16string_view aNextLinkName)
1866 {
1867     // XXX: Current implementation constraints text boxes to be on the same page
1868 
1869     // No next link
1870     if (aNextLinkName.empty()) {
1871         pTextObj->SetNextLinkInChain(nullptr);
1872         return;
1873     }
1874 
1875     SdrPage *pPage(pTextObj->getSdrPageFromSdrObject());
1876     assert(pPage);
1877     SdrTextObj *pNextTextObj = DynCastSdrTextObj
1878                                 (ImpGetObjByName(pPage, aNextLinkName));
1879     if (!pNextTextObj) {
1880         SAL_INFO("svx.chaining", "[CHAINING] Can't find object as next link.");
1881         return;
1882     }
1883 
1884     pTextObj->SetNextLinkInChain(pNextTextObj);
1885 }
1886 
IsChainable() const1887 bool SdrTextObj::IsChainable() const
1888 {
1889     // Read it as item
1890     const SfxItemSet& rSet = GetObjectItemSet();
1891     OUString aNextLinkName = rSet.Get(SDRATTR_TEXT_CHAINNEXTNAME).GetValue();
1892 
1893     // Update links if any inconsistency is found
1894     bool bNextLinkUnsetYet = !aNextLinkName.isEmpty() && !mpNextInChain;
1895     bool bInconsistentNextLink = mpNextInChain && mpNextInChain->GetName() != aNextLinkName;
1896     // if the link is not set despite there should be one OR if it has changed
1897     if (bNextLinkUnsetYet || bInconsistentNextLink) {
1898         ImpUpdateChainLinks(const_cast<SdrTextObj *>(this), aNextLinkName);
1899     }
1900 
1901     return !aNextLinkName.isEmpty(); // XXX: Should we also check for GetNilChainingEvent? (see old code below)
1902 
1903 /*
1904     // Check that no overflow is going on
1905     if (!GetTextChain() || GetTextChain()->GetNilChainingEvent(this))
1906         return false;
1907 */
1908 }
1909 
onChainingEvent()1910 void SdrTextObj::onChainingEvent()
1911 {
1912     if (!mpEditingOutliner)
1913         return;
1914 
1915     // Outliner for text transfer
1916     SdrOutliner &aDrawOutliner = ImpGetDrawOutliner();
1917 
1918     EditingTextChainFlow aTxtChainFlow(this);
1919     aTxtChainFlow.CheckForFlowEvents(mpEditingOutliner);
1920 
1921     if (aTxtChainFlow.IsOverflow()) {
1922         SAL_INFO("svx.chaining", "[CHAINING] Overflow going on");
1923         // One outliner is for non-overflowing text, the other for overflowing text
1924         // We remove text directly from the editing outliner
1925         aTxtChainFlow.ExecuteOverflow(mpEditingOutliner, &aDrawOutliner);
1926     } else if (aTxtChainFlow.IsUnderflow()) {
1927         SAL_INFO("svx.chaining", "[CHAINING] Underflow going on");
1928         // underflow-induced overflow
1929         aTxtChainFlow.ExecuteUnderflow(&aDrawOutliner);
1930         bool bIsOverflowFromUnderflow = aTxtChainFlow.IsOverflow();
1931         // handle overflow
1932         if (bIsOverflowFromUnderflow) {
1933             SAL_INFO("svx.chaining", "[CHAINING] Overflow going on (underflow induced)");
1934             // prevents infinite loops when setting text for editing outliner
1935             aTxtChainFlow.ExecuteOverflow(&aDrawOutliner, &aDrawOutliner);
1936         }
1937     }
1938 }
1939 
GetNextLinkInChain() const1940 SdrTextObj* SdrTextObj::GetNextLinkInChain() const
1941 {
1942     /*
1943     if (GetTextChain())
1944         return GetTextChain()->GetNextLink(this);
1945 
1946     return NULL;
1947     */
1948 
1949     return mpNextInChain;
1950 }
1951 
SetNextLinkInChain(SdrTextObj * pNextObj)1952 void SdrTextObj::SetNextLinkInChain(SdrTextObj *pNextObj)
1953 {
1954     // Basically a doubly linked list implementation
1955 
1956     SdrTextObj *pOldNextObj = mpNextInChain;
1957 
1958     // Replace next link
1959     mpNextInChain = pNextObj;
1960     // Deal with old next link's prev link
1961     if (pOldNextObj) {
1962         pOldNextObj->mpPrevInChain = nullptr;
1963     }
1964 
1965     // Deal with new next link's prev link
1966     if (mpNextInChain) {
1967         // If there is a prev already at all and this is not already the current object
1968         if (mpNextInChain->mpPrevInChain &&
1969             mpNextInChain->mpPrevInChain != this)
1970             mpNextInChain->mpPrevInChain->mpNextInChain = nullptr;
1971         mpNextInChain->mpPrevInChain = this;
1972     }
1973 
1974     // TODO: Introduce check for circular chains
1975 
1976 }
1977 
GetPrevLinkInChain() const1978 SdrTextObj* SdrTextObj::GetPrevLinkInChain() const
1979 {
1980     /*
1981     if (GetTextChain())
1982         return GetTextChain()->GetPrevLink(this);
1983 
1984     return NULL;
1985     */
1986 
1987     return mpPrevInChain;
1988 }
1989 
GetPreventChainable() const1990 bool SdrTextObj::GetPreventChainable() const
1991 {
1992     // Prevent chaining it 1) during dragging && 2) when we are editing next link
1993     return mbIsUnchainableClone || (GetNextLinkInChain() && GetNextLinkInChain()->IsInEditMode());
1994 }
1995 
getFullDragClone() const1996 rtl::Reference<SdrObject> SdrTextObj::getFullDragClone() const
1997 {
1998     rtl::Reference<SdrObject> pClone = SdrAttrObj::getFullDragClone();
1999     SdrTextObj *pTextObjClone = DynCastSdrTextObj(pClone.get());
2000     if (pTextObjClone != nullptr) {
2001         // Avoid transferring of text for chainable object during dragging
2002         pTextObjClone->mbIsUnchainableClone = true;
2003     }
2004 
2005     return pClone;
2006  }
2007 
2008 /* End chaining code */
2009 
2010 /** returns the currently active text. */
getActiveText() const2011 SdrText* SdrTextObj::getActiveText() const
2012 {
2013     if( !mxText )
2014         return getText( 0 );
2015     else
2016         return mxText.get();
2017 }
2018 
2019 /** returns the nth available text. */
getText(sal_Int32 nIndex) const2020 SdrText* SdrTextObj::getText( sal_Int32 nIndex ) const
2021 {
2022     if( nIndex == 0 )
2023     {
2024         if( !mxText )
2025             const_cast< SdrTextObj* >(this)->mxText = new SdrText( *const_cast< SdrTextObj* >(this) );
2026         return mxText.get();
2027     }
2028     else
2029     {
2030         return nullptr;
2031     }
2032 }
2033 
2034 /** returns the number of texts available for this object. */
getTextCount() const2035 sal_Int32 SdrTextObj::getTextCount() const
2036 {
2037     return 1;
2038 }
2039 
2040 /** changes the current active text */
setActiveText(sal_Int32)2041 void SdrTextObj::setActiveText( sal_Int32 /*nIndex*/ )
2042 {
2043 }
2044 
2045 /** returns the index of the text that contains the given point or -1 */
CheckTextHit(const Point &) const2046 sal_Int32 SdrTextObj::CheckTextHit(const Point& /*rPnt*/) const
2047 {
2048     return 0;
2049 }
2050 
SetObjectItemNoBroadcast(const SfxPoolItem & rItem)2051 void SdrTextObj::SetObjectItemNoBroadcast(const SfxPoolItem& rItem)
2052 {
2053     static_cast< sdr::properties::TextProperties& >(GetProperties()).SetObjectItemNoBroadcast(rItem);
2054 }
2055 
2056 
2057 // The concept of the text object:
2058 // ~~~~~~~~~~~~~~~~~~~~~~~~
2059 // Attributes/Variations:
2060 // - bool text frame / graphics object with caption
2061 // - bool FontWork                 (if it is not a text frame and not a ContourTextFrame)
2062 // - bool ContourTextFrame         (if it is not a text frame and not Fontwork)
2063 // - long rotation angle               (if it is not FontWork)
2064 // - long text frame margins           (if it is not FontWork)
2065 // - bool FitToSize                (if it is not FontWork)
2066 // - bool AutoGrowingWidth/Height  (if it is not FitToSize and not FontWork)
2067 // - long Min/MaxFrameWidth/Height     (if AutoGrowingWidth/Height)
2068 // - enum horizontal text anchoring left,center,right,justify/block,Stretch(ni)
2069 // - enum vertical text anchoring top, middle, bottom, block, stretch(ni)
2070 // - enum ticker text                  (if it is not FontWork)
2071 
2072 // Every derived object is either a text frame (mbTextFrame=true)
2073 // or a drawing object with a caption (mbTextFrame=false).
2074 
2075 // Default anchoring for text frames:
2076 //   SDRTEXTHORZADJUST_BLOCK, SDRTEXTVERTADJUST_TOP
2077 //   = static Pool defaults
2078 // Default anchoring for drawing objects with a caption:
2079 //   SDRTEXTHORZADJUST_CENTER, SDRTEXTVERTADJUST_CENTER
2080 //   via "hard" attribution of SdrAttrObj
2081 
2082 // Every object derived from SdrTextObj must return an "UnrotatedSnapRect"
2083 // (->TakeUnrotatedSnapRect()) (the reference point for the rotation is the top
2084 // left of the rectangle (maGeo.nRotationAngle)) which is the basis for anchoring
2085 // text. We then subtract the text frame margins from this rectangle, as a re-
2086 // sult we get the anchoring area (->TakeTextAnchorRect()). Within this area, we
2087 // calculate the anchoring point and the painting area, depending on the hori-
2088 // zontal and vertical adjustment of the text (SdrTextVertAdjust,
2089 // SdrTextHorzAdjust).
2090 // In the case of drawing objects with a caption the painting area might well
2091 // be larger than the anchoring area, for text frames on the other hand, it is
2092 // always of the same or a smaller size (except when there are negative text
2093 // frame margins).
2094 
2095 // FitToSize takes priority over text anchoring and AutoGrowHeight/Width. When
2096 // FitToSize is turned on, the painting area is always equal to the anchoring
2097 // area. Additionally, FitToSize doesn't allow automatic line breaks.
2098 
2099 // ContourTextFrame:
2100 // - long rotation angle
2101 // - long text frame margins (maybe later)
2102 // - bool FitToSize (maybe later)
2103 // - bool AutoGrowingWidth/Height (maybe much later)
2104 // - long Min/MaxFrameWidth/Height (maybe much later)
2105 // - enum horizontal text anchoring (maybe later, for now: left, centered)
2106 // - enum vertical text anchoring (maybe later, for now: top)
2107 // - enum ticker text (maybe later, maybe even with correct clipping)
2108 
2109 // When making changes, check these:
2110 // - Paint
2111 // - HitTest
2112 // - ConvertToPoly
2113 // - Edit
2114 // - Printing, Saving, Painting in neighboring View while editing
2115 // - ModelChanged (e. g. through a neighboring View or rulers) while editing
2116 // - FillColorChanged while editing
2117 // - and many more...
2118 
2119 
2120 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2121