xref: /trunk/main/svx/source/svdraw/svdotxtr.cxx (revision 61dff127b6698e0bae836c8aedd6ec62111483d1)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_svx.hxx"
30 
31 #include <svx/svdotext.hxx>
32 #include "svx/svditext.hxx"
33 #include <svx/svdtrans.hxx>
34 #include <svx/svdogrp.hxx>
35 #include <svx/svdopath.hxx>
36 #include <svx/svdoutl.hxx>
37 #include <svx/svdpage.hxx>   // fuer Convert
38 #include <svx/svdmodel.hxx>  // fuer Convert
39 #include <editeng/outliner.hxx>
40 #include <svx/sdr/properties/itemsettools.hxx>
41 #include <svx/sdr/properties/properties.hxx>
42 #include <basegfx/polygon/b2dpolypolygontools.hxx>
43 #include <svl/itemset.hxx>
44 #include <svx/svditer.hxx>
45 #include <drawinglayer/processor2d/textaspolygonextractor2d.hxx>
46 #include <svx/sdr/contact/viewcontact.hxx>
47 #include <svx/xflclit.hxx>
48 #include <svx/xlnclit.hxx>
49 #include <svx/xlnwtit.hxx>
50 
51 ////////////////////////////////////////////////////////////////////////////////////////////////////
52 //
53 //  @@@@@@ @@@@@ @@   @@ @@@@@@  @@@@  @@@@@  @@@@@@
54 //    @@   @@    @@@ @@@   @@   @@  @@ @@  @@     @@
55 //    @@   @@     @@@@@    @@   @@  @@ @@  @@     @@
56 //    @@   @@@@    @@@     @@   @@  @@ @@@@@      @@
57 //    @@   @@     @@@@@    @@   @@  @@ @@  @@     @@
58 //    @@   @@    @@@ @@@   @@   @@  @@ @@  @@ @@  @@
59 //    @@   @@@@@ @@   @@   @@    @@@@  @@@@@   @@@@
60 //
61 //  Transformationen
62 //
63 ////////////////////////////////////////////////////////////////////////////////////////////////////
64 
65 void SdrTextObj::NbcSetSnapRect(const Rectangle& rRect)
66 {
67     if (aGeo.nDrehWink!=0 || aGeo.nShearWink!=0) {
68         Rectangle aSR0(GetSnapRect());
69         long nWdt0=aSR0.Right()-aSR0.Left();
70         long nHgt0=aSR0.Bottom()-aSR0.Top();
71         long nWdt1=rRect.Right()-rRect.Left();
72         long nHgt1=rRect.Bottom()-rRect.Top();
73         SdrTextObj::NbcResize(maSnapRect.TopLeft(),Fraction(nWdt1,nWdt0),Fraction(nHgt1,nHgt0));
74         SdrTextObj::NbcMove(Size(rRect.Left()-aSR0.Left(),rRect.Top()-aSR0.Top()));
75     } else {
76         long nHDist=GetTextLeftDistance()+GetTextRightDistance();
77         long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
78         long nTWdt0=aRect.GetWidth ()-1-nHDist; if (nTWdt0<0) nTWdt0=0;
79         long nTHgt0=aRect.GetHeight()-1-nVDist; if (nTHgt0<0) nTHgt0=0;
80         long nTWdt1=rRect.GetWidth ()-1-nHDist; if (nTWdt1<0) nTWdt1=0;
81         long nTHgt1=rRect.GetHeight()-1-nVDist; if (nTHgt1<0) nTHgt1=0;
82         aRect=rRect;
83         ImpJustifyRect(aRect);
84         if (bTextFrame && (pModel==NULL || !pModel->IsPasteResize())) { // #51139#
85             if (nTWdt0!=nTWdt1 && IsAutoGrowWidth() ) NbcSetMinTextFrameWidth(nTWdt1);
86             if (nTHgt0!=nTHgt1 && IsAutoGrowHeight()) NbcSetMinTextFrameHeight(nTHgt1);
87             if (GetFitToSize()==SDRTEXTFIT_RESIZEATTR) {
88                 NbcResizeTextAttributes(Fraction(nTWdt1,nTWdt0),Fraction(nTHgt1,nTHgt0));
89             }
90             NbcAdjustTextFrameWidthAndHeight();
91         }
92         ImpCheckShear();
93         SetRectsDirty();
94     }
95 }
96 
97 const Rectangle& SdrTextObj::GetLogicRect() const
98 {
99     return aRect;
100 }
101 
102 void SdrTextObj::NbcSetLogicRect(const Rectangle& rRect)
103 {
104     long nHDist=GetTextLeftDistance()+GetTextRightDistance();
105     long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
106     long nTWdt0=aRect.GetWidth ()-1-nHDist; if (nTWdt0<0) nTWdt0=0;
107     long nTHgt0=aRect.GetHeight()-1-nVDist; if (nTHgt0<0) nTHgt0=0;
108     long nTWdt1=rRect.GetWidth ()-1-nHDist; if (nTWdt1<0) nTWdt1=0;
109     long nTHgt1=rRect.GetHeight()-1-nVDist; if (nTHgt1<0) nTHgt1=0;
110     aRect=rRect;
111     ImpJustifyRect(aRect);
112     if (bTextFrame) {
113         if (nTWdt0!=nTWdt1 && IsAutoGrowWidth() ) NbcSetMinTextFrameWidth(nTWdt1);
114         if (nTHgt0!=nTHgt1 && IsAutoGrowHeight()) NbcSetMinTextFrameHeight(nTHgt1);
115         if (GetFitToSize()==SDRTEXTFIT_RESIZEATTR) {
116             NbcResizeTextAttributes(Fraction(nTWdt1,nTWdt0),Fraction(nTHgt1,nTHgt0));
117         }
118         NbcAdjustTextFrameWidthAndHeight();
119     }
120     SetRectsDirty();
121 }
122 
123 long SdrTextObj::GetRotateAngle() const
124 {
125     return aGeo.nDrehWink;
126 }
127 
128 long SdrTextObj::GetShearAngle(FASTBOOL /*bVertical*/) const
129 {
130     return aGeo.nShearWink;
131 }
132 
133 void SdrTextObj::NbcMove(const Size& rSiz)
134 {
135     MoveRect(aRect,rSiz);
136     MoveRect(aOutRect,rSiz);
137     MoveRect(maSnapRect,rSiz);
138     SetRectsDirty(sal_True);
139 }
140 
141 void SdrTextObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
142 {
143     FASTBOOL bNoShearMerk=aGeo.nShearWink==0;
144     FASTBOOL bRota90Merk=bNoShearMerk && aGeo.nDrehWink % 9000 ==0;
145     long nHDist=GetTextLeftDistance()+GetTextRightDistance();
146     long nVDist=GetTextUpperDistance()+GetTextLowerDistance();
147     long nTWdt0=aRect.GetWidth ()-1-nHDist; if (nTWdt0<0) nTWdt0=0;
148     long nTHgt0=aRect.GetHeight()-1-nVDist; if (nTHgt0<0) nTHgt0=0;
149     FASTBOOL bXMirr=(xFact.GetNumerator()<0) != (xFact.GetDenominator()<0);
150     FASTBOOL bYMirr=(yFact.GetNumerator()<0) != (yFact.GetDenominator()<0);
151     if (bXMirr || bYMirr) {
152         Point aRef1(GetSnapRect().Center());
153         if (bXMirr) {
154             Point aRef2(aRef1);
155             aRef2.Y()++;
156             NbcMirrorGluePoints(aRef1,aRef2);
157         }
158         if (bYMirr) {
159             Point aRef2(aRef1);
160             aRef2.X()++;
161             NbcMirrorGluePoints(aRef1,aRef2);
162         }
163     }
164 
165     if (aGeo.nDrehWink==0 && aGeo.nShearWink==0) {
166         ResizeRect(aRect,rRef,xFact,yFact);
167         if (bYMirr) {
168             aRect.Justify();
169             aRect.Move(aRect.Right()-aRect.Left(),aRect.Bottom()-aRect.Top());
170             aGeo.nDrehWink=18000;
171             aGeo.RecalcSinCos();
172         }
173     }
174     else
175     {
176         // #100663# aRect is NOT initialized for lines (polgon objects with two
177         // exceptionally handled points). Thus, after this call the text rotaion is
178         // gone. This error must be present since day one of this old drawing layer.
179         // It's astonishing that noone discovered it earlier.
180         // Polygon aPol(Rect2Poly(aRect,aGeo));
181         // Polygon aPol(Rect2Poly(GetSnapRect(), aGeo));
182 
183         // #101412# go back to old method, side effects are impossible
184         // to calculate.
185         Polygon aPol(Rect2Poly(aRect,aGeo));
186 
187         for(sal_uInt16 a(0); a < aPol.GetSize(); a++)
188         {
189              ResizePoint(aPol[a], rRef, xFact, yFact);
190         }
191 
192         if(bXMirr != bYMirr)
193         {
194             // Polygon wenden und etwas schieben
195             Polygon aPol0(aPol);
196 
197             aPol[0] = aPol0[1];
198             aPol[1] = aPol0[0];
199             aPol[2] = aPol0[3];
200             aPol[3] = aPol0[2];
201             aPol[4] = aPol0[1];
202         }
203 
204         Poly2Rect(aPol, aRect, aGeo);
205     }
206 
207     if (bRota90Merk) {
208         FASTBOOL bRota90=aGeo.nDrehWink % 9000 ==0;
209         if (!bRota90) { // Scheinbar Rundungsfehler: Korregieren
210             long a=NormAngle360(aGeo.nDrehWink);
211             if (a<4500) a=0;
212             else if (a<13500) a=9000;
213             else if (a<22500) a=18000;
214             else if (a<31500) a=27000;
215             else a=0;
216             aGeo.nDrehWink=a;
217             aGeo.RecalcSinCos();
218         }
219         if (bNoShearMerk!=(aGeo.nShearWink==0)) { // Shear ggf. korregieren wg. Rundungsfehler
220             aGeo.nShearWink=0;
221             aGeo.RecalcTan();
222         }
223     }
224 
225     ImpJustifyRect(aRect);
226     long nTWdt1=aRect.GetWidth ()-1-nHDist; if (nTWdt1<0) nTWdt1=0;
227     long nTHgt1=aRect.GetHeight()-1-nVDist; if (nTHgt1<0) nTHgt1=0;
228     if (bTextFrame && (pModel==NULL || !pModel->IsPasteResize())) { // #51139#
229         if (nTWdt0!=nTWdt1 && IsAutoGrowWidth() ) NbcSetMinTextFrameWidth(nTWdt1);
230         if (nTHgt0!=nTHgt1 && IsAutoGrowHeight()) NbcSetMinTextFrameHeight(nTHgt1);
231         if (GetFitToSize()==SDRTEXTFIT_RESIZEATTR) {
232             NbcResizeTextAttributes(Fraction(nTWdt1,nTWdt0),Fraction(nTHgt1,nTHgt0));
233         }
234         NbcAdjustTextFrameWidthAndHeight();
235     }
236     ImpCheckShear();
237     SetRectsDirty();
238 }
239 
240 void SdrTextObj::NbcRotate(const Point& rRef, long nWink, double sn, double cs)
241 {
242     SetGlueReallyAbsolute(sal_True);
243     long dx=aRect.Right()-aRect.Left();
244     long dy=aRect.Bottom()-aRect.Top();
245     Point aP(aRect.TopLeft());
246     RotatePoint(aP,rRef,sn,cs);
247     aRect.Left()=aP.X();
248     aRect.Top()=aP.Y();
249     aRect.Right()=aRect.Left()+dx;
250     aRect.Bottom()=aRect.Top()+dy;
251     if (aGeo.nDrehWink==0) {
252         aGeo.nDrehWink=NormAngle360(nWink);
253         aGeo.nSin=sn;
254         aGeo.nCos=cs;
255     } else {
256         aGeo.nDrehWink=NormAngle360(aGeo.nDrehWink+nWink);
257         aGeo.RecalcSinCos();
258     }
259     SetRectsDirty();
260     NbcRotateGluePoints(rRef,nWink,sn,cs);
261     SetGlueReallyAbsolute(sal_False);
262 }
263 
264 void SdrTextObj::NbcShear(const Point& rRef, long nWink, double tn, FASTBOOL bVShear)
265 {
266     SetGlueReallyAbsolute(sal_True);
267 
268     // #75889# when this is a SdrPathObj aRect maybe not initialized
269     Polygon aPol(Rect2Poly(aRect.IsEmpty() ? GetSnapRect() : aRect, aGeo));
270 
271     sal_uInt16 nPointCount=aPol.GetSize();
272     for (sal_uInt16 i=0; i<nPointCount; i++) {
273          ShearPoint(aPol[i],rRef,tn,bVShear);
274     }
275     Poly2Rect(aPol,aRect,aGeo);
276     ImpJustifyRect(aRect);
277     if (bTextFrame) {
278         NbcAdjustTextFrameWidthAndHeight();
279     }
280     ImpCheckShear();
281     SetRectsDirty();
282     NbcShearGluePoints(rRef,nWink,tn,bVShear);
283     SetGlueReallyAbsolute(sal_False);
284 }
285 
286 void SdrTextObj::NbcMirror(const Point& rRef1, const Point& rRef2)
287 {
288     SetGlueReallyAbsolute(sal_True);
289     FASTBOOL bNoShearMerk=aGeo.nShearWink==0;
290     FASTBOOL bRota90Merk=sal_False;
291     if (bNoShearMerk &&
292         (rRef1.X()==rRef2.X() || rRef1.Y()==rRef2.Y() ||
293          Abs(rRef1.X()-rRef2.X())==Abs(rRef1.Y()-rRef2.Y()))) {
294         bRota90Merk=aGeo.nDrehWink % 9000 ==0;
295     }
296     Polygon aPol(Rect2Poly(aRect,aGeo));
297     sal_uInt16 i;
298     sal_uInt16 nPntAnz=aPol.GetSize();
299     for (i=0; i<nPntAnz; i++) {
300          MirrorPoint(aPol[i],rRef1,rRef2);
301     }
302     // Polygon wenden und etwas schieben
303     Polygon aPol0(aPol);
304     aPol[0]=aPol0[1];
305     aPol[1]=aPol0[0];
306     aPol[2]=aPol0[3];
307     aPol[3]=aPol0[2];
308     aPol[4]=aPol0[1];
309     Poly2Rect(aPol,aRect,aGeo);
310 
311     if (bRota90Merk) {
312         FASTBOOL bRota90=aGeo.nDrehWink % 9000 ==0;
313         if (bRota90Merk && !bRota90) { // Scheinbar Rundungsfehler: Korregieren
314             long a=NormAngle360(aGeo.nDrehWink);
315             if (a<4500) a=0;
316             else if (a<13500) a=9000;
317             else if (a<22500) a=18000;
318             else if (a<31500) a=27000;
319             else a=0;
320             aGeo.nDrehWink=a;
321             aGeo.RecalcSinCos();
322         }
323     }
324     if (bNoShearMerk!=(aGeo.nShearWink==0)) { // Shear ggf. korregieren wg. Rundungsfehler
325         aGeo.nShearWink=0;
326         aGeo.RecalcTan();
327     }
328 
329     ImpJustifyRect(aRect);
330     if (bTextFrame) {
331         NbcAdjustTextFrameWidthAndHeight();
332     }
333     ImpCheckShear();
334     SetRectsDirty();
335     NbcMirrorGluePoints(rRef1,rRef2);
336     SetGlueReallyAbsolute(sal_False);
337 }
338 
339 //////////////////////////////////////////////////////////////////////////////
340 
341 SdrObject* SdrTextObj::ImpConvertContainedTextToSdrPathObjs(bool bToPoly) const
342 {
343     SdrObject* pRetval = 0;
344 
345     if(!ImpCanConvTextToCurve())
346     {
347         // suppress HelpTexts from PresObj's
348         return 0;
349     }
350 
351     // get primitives
352     const drawinglayer::primitive2d::Primitive2DSequence xSequence(GetViewContact().getViewIndependentPrimitive2DSequence());
353 
354     if(xSequence.hasElements())
355     {
356         // create an extractor with neutral ViewInformation
357         const drawinglayer::geometry::ViewInformation2D aViewInformation2D;
358         drawinglayer::processor2d::TextAsPolygonExtractor2D aExtractor(aViewInformation2D);
359 
360         // extract text as polygons
361         aExtractor.process(xSequence);
362 
363         // get results
364         const drawinglayer::processor2d::TextAsPolygonDataNodeVector& rResult = aExtractor.getTarget();
365         const sal_uInt32 nResultCount(rResult.size());
366 
367         if(nResultCount)
368         {
369             // prepare own target
370             SdrObjGroup* pGroup = new SdrObjGroup();
371             SdrObjList* pObjectList = pGroup->GetSubList();
372 
373             // process results
374             for(sal_uInt32 a(0); a < nResultCount; a++)
375             {
376                 const drawinglayer::processor2d::TextAsPolygonDataNode& rCandidate = rResult[a];
377                 basegfx::B2DPolyPolygon aPolyPolygon(rCandidate.getB2DPolyPolygon());
378 
379                 if(aPolyPolygon.count())
380                 {
381                     // take care of wanted polygon type
382                     if(bToPoly)
383                     {
384                         if(aPolyPolygon.areControlPointsUsed())
385                         {
386                             aPolyPolygon = basegfx::tools::adaptiveSubdivideByAngle(aPolyPolygon);
387                         }
388                     }
389                     else
390                     {
391                         if(!aPolyPolygon.areControlPointsUsed())
392                         {
393                             aPolyPolygon = basegfx::tools::expandToCurve(aPolyPolygon);
394                         }
395                     }
396 
397                     // create ItemSet with object attributes
398                     SfxItemSet aAttributeSet(GetObjectItemSet());
399                     SdrPathObj* pPathObj = 0;
400 
401                     // always clear objectshadow; this is included in the extraction
402                     aAttributeSet.Put(SdrShadowItem(false));
403 
404                     if(rCandidate.getIsFilled())
405                     {
406                         // set needed items
407                         aAttributeSet.Put(XFillColorItem(String(), Color(rCandidate.getBColor())));
408                         aAttributeSet.Put(XLineStyleItem(XLINE_NONE));
409                         aAttributeSet.Put(XFillStyleItem(XFILL_SOLID));
410 
411                         // create filled SdrPathObj
412                         pPathObj = new SdrPathObj(OBJ_PATHFILL, aPolyPolygon);
413                     }
414                     else
415                     {
416                         // set needed items
417                         aAttributeSet.Put(XLineColorItem(String(), Color(rCandidate.getBColor())));
418                         aAttributeSet.Put(XLineStyleItem(XLINE_SOLID));
419                         aAttributeSet.Put(XLineWidthItem(0));
420                         aAttributeSet.Put(XFillStyleItem(XFILL_NONE));
421 
422                         // create line SdrPathObj
423                         pPathObj = new SdrPathObj(OBJ_PATHLINE, aPolyPolygon);
424                     }
425 
426                     // copy basic information from original
427                     pPathObj->ImpSetAnchorPos(GetAnchorPos());
428                     pPathObj->NbcSetLayer(GetLayer());
429 
430                     if(GetModel())
431                     {
432                         pPathObj->SetModel(GetModel());
433                         pPathObj->NbcSetStyleSheet(GetStyleSheet(), true);
434                     }
435 
436                     // apply prepared ItemSet and add to target
437                     pPathObj->SetMergedItemSet(aAttributeSet);
438                     pObjectList->InsertObject(pPathObj);
439                 }
440             }
441 
442             // postprocess; if no result and/or only one object, simplify
443             if(!pObjectList->GetObjCount())
444             {
445                 delete pGroup;
446             }
447             else if(1 == pObjectList->GetObjCount())
448             {
449                 pRetval = pObjectList->RemoveObject(0);
450                 delete pGroup;
451             }
452             else
453             {
454                 pRetval = pGroup;
455             }
456         }
457     }
458 
459     return pRetval;
460 }
461 
462 //////////////////////////////////////////////////////////////////////////////
463 
464 SdrObject* SdrTextObj::DoConvertToPolyObj(sal_Bool bBezier, bool bAddText) const
465 {
466     if(bAddText)
467     {
468         return ImpConvertContainedTextToSdrPathObjs(!bBezier);
469     }
470 
471     return 0;
472 }
473 
474 bool SdrTextObj::ImpCanConvTextToCurve() const
475 {
476     return !IsOutlText();
477 }
478 
479 SdrObject* SdrTextObj::ImpConvertMakeObj(const basegfx::B2DPolyPolygon& rPolyPolygon, sal_Bool bClosed, sal_Bool bBezier, sal_Bool bNoSetAttr) const
480 {
481     SdrObjKind ePathKind = bClosed ? OBJ_PATHFILL : OBJ_PATHLINE;
482     basegfx::B2DPolyPolygon aB2DPolyPolygon(rPolyPolygon);
483 
484     // #i37011#
485     if(!bBezier)
486     {
487         aB2DPolyPolygon = basegfx::tools::adaptiveSubdivideByAngle(aB2DPolyPolygon);
488         ePathKind = bClosed ? OBJ_POLY : OBJ_PLIN;
489     }
490 
491     SdrPathObj* pPathObj = new SdrPathObj(ePathKind, aB2DPolyPolygon);
492 
493     if(bBezier)
494     {
495         // create bezier curves
496         pPathObj->SetPathPoly(basegfx::tools::expandToCurve(pPathObj->GetPathPoly()));
497     }
498 
499     if(pPathObj)
500     {
501         pPathObj->ImpSetAnchorPos(aAnchor);
502         pPathObj->NbcSetLayer(SdrLayerID(GetLayer()));
503 
504         if(pModel)
505         {
506             pPathObj->SetModel(pModel);
507 
508             if(!bNoSetAttr)
509             {
510                 sdr::properties::ItemChangeBroadcaster aC(*pPathObj);
511 
512                 pPathObj->ClearMergedItem();
513                 pPathObj->SetMergedItemSet(GetObjectItemSet());
514                 pPathObj->GetProperties().BroadcastItemChange(aC);
515                 pPathObj->NbcSetStyleSheet(GetStyleSheet(), sal_True);
516             }
517         }
518     }
519 
520     return pPathObj;
521 }
522 
523 SdrObject* SdrTextObj::ImpConvertAddText(SdrObject* pObj, FASTBOOL bBezier) const
524 {
525     if(!ImpCanConvTextToCurve())
526     {
527         return pObj;
528     }
529 
530     SdrObject* pText = ImpConvertContainedTextToSdrPathObjs(!bBezier);
531 
532     if(!pText)
533     {
534         return pObj;
535     }
536 
537     if(!pObj)
538     {
539         return pText;
540     }
541 
542     if(pText->IsGroupObject())
543     {
544         // is already group object, add partial shape in front
545         SdrObjList* pOL=pText->GetSubList();
546         pOL->InsertObject(pObj,0);
547 
548         return pText;
549     }
550     else
551     {
552         // not yet a group, create one and add partial and new shapes
553         SdrObjGroup* pGrp=new SdrObjGroup;
554         SdrObjList* pOL=pGrp->GetSubList();
555         pOL->InsertObject(pObj);
556         pOL->InsertObject(pText);
557 
558         return pGrp;
559     }
560 }
561 
562 //////////////////////////////////////////////////////////////////////////////
563 // eof
564