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 #include <memory>
21 #include "eschesdo.hxx"
22 #include <o3tl/any.hxx>
23 #include <svx/svdobj.hxx>
24 #include <svx/unoapi.hxx>
25 #include <svx/unoshape.hxx>
26 #include <vcl/outdev.hxx>
27 #include <tools/poly.hxx>
28 #include <tools/debug.hxx>
29 #include <tools/diagnose_ex.h>
30 #include <tools/fract.hxx>
31 #include <svx/fmdpage.hxx>
32 #include <com/sun/star/awt/Rectangle.hpp>
33 #include <com/sun/star/text/XText.hpp>
34 #include <com/sun/star/text/TextContentAnchorType.hpp>
35 #include <com/sun/star/drawing/CircleKind.hpp>
36 #include <com/sun/star/drawing/FillStyle.hpp>
37 #include <comphelper/extract.hxx>
38 #include <com/sun/star/drawing/HomogenMatrix3.hpp>
39 #include <basegfx/matrix/b2dhommatrix.hxx>
40 
41 using namespace ::com::sun::star;
42 using namespace ::com::sun::star::beans;
43 using namespace ::com::sun::star::container;
44 using namespace ::com::sun::star::uno;
45 using namespace ::com::sun::star::drawing;
46 using namespace ::com::sun::star::text;
47 using namespace ::com::sun::star::task;
48 using namespace ::com::sun::star::style;
49 
50 #define EES_MAP_FRACTION 1440   // 1440 dpi
51 
ImplEESdrWriter(EscherEx & rEx)52 ImplEESdrWriter::ImplEESdrWriter( EscherEx& rEx )
53     : mpEscherEx(&rEx)
54     , maMapModeSrc(MapUnit::Map100thMM)
55     // PowerPoint: 576 dpi, WinWord: 1440 dpi, Excel: 1440 dpi
56     , maMapModeDest( MapUnit::MapInch, Point(), Fraction( 1, EES_MAP_FRACTION ), Fraction( 1, EES_MAP_FRACTION ) )
57     , mpPicStrm(nullptr)
58     , mpHostAppData(nullptr)
59     , mbIsTitlePossible(false)
60     , mpSdrPage( nullptr )
61 {
62 }
63 
64 
65 
ImplMapPoint(const Point & rPoint)66 Point ImplEESdrWriter::ImplMapPoint( const Point& rPoint )
67 {
68     return OutputDevice::LogicToLogic( rPoint, maMapModeSrc, maMapModeDest );
69 }
70 
ImplMapSize(const Size & rSize)71 Size ImplEESdrWriter::ImplMapSize( const Size& rSize )
72 {
73     Size aRetSize( OutputDevice::LogicToLogic( rSize, maMapModeSrc, maMapModeDest ) );
74 
75     if ( !aRetSize.Width() )
76         aRetSize.AdjustWidth( 1 );
77     if ( !aRetSize.Height() )
78         aRetSize.AdjustHeight( 1 );
79     return aRetSize;
80 }
81 
ImplFlipBoundingBox(ImplEESdrObject & rObj,EscherPropertyContainer & rPropOpt)82 void ImplEESdrWriter::ImplFlipBoundingBox( ImplEESdrObject& rObj, EscherPropertyContainer& rPropOpt )
83 {
84     sal_Int32 nAngle = rObj.GetAngle();
85     tools::Rectangle aRect( rObj.GetRect() );
86 
87     // for position calculations, we normalize the angle between 0 and 90 degrees
88     if ( nAngle < 0 )
89         nAngle = ( 36000 + nAngle ) % 36000;
90     if ( nAngle % 18000 == 0 )
91         nAngle = 0;
92     while ( nAngle > 9000 )
93         nAngle = ( 18000 - ( nAngle % 18000 ) );
94 
95     double fVal = static_cast<double>(nAngle) * F_PI18000;
96     double  fCos = cos( fVal );
97     double  fSin = sin( fVal );
98 
99     double  nWidthHalf = static_cast<double>(aRect.GetWidth()) / 2;
100     double  nHeightHalf = static_cast<double>(aRect.GetHeight()) / 2;
101 
102     // fdo#70838:
103     // when you rotate an object, the top-left corner of its bounding box is moved
104     // nXDiff and nYDiff pixels. To get their values we use these equations:
105     //
106     //   fSin * nHeightHalf + fCos * nWidthHalf  == nXDiff + nWidthHalf
107     //   fSin * nWidthHalf  + fCos * nHeightHalf == nYDiff + nHeightHalf
108 
109     double nXDiff = fSin * nHeightHalf + fCos * nWidthHalf  - nWidthHalf;
110     double nYDiff = fSin * nWidthHalf  + fCos * nHeightHalf - nHeightHalf;
111 
112     aRect.Move( static_cast<sal_Int32>(nXDiff), static_cast<sal_Int32>(nYDiff) );
113 
114     // calculate the proper angle value to be saved
115     nAngle = rObj.GetAngle();
116     if ( nAngle < 0 )
117         nAngle = ( 36000 + nAngle ) % 36000;
118     else
119         nAngle = ( 36000 - ( nAngle % 36000 ) );
120 
121     nAngle *= 655;
122     nAngle += 0x8000;
123     nAngle &=~0xffff;                                   // nAngle round to full degrees
124     rPropOpt.AddOpt( ESCHER_Prop_Rotation, nAngle );
125 
126     rObj.SetAngle( nAngle );
127     rObj.SetRect( aRect );
128 }
129 
130 
ImplWriteShape(ImplEESdrObject & rObj,EscherSolverContainer & rSolverContainer,const bool bOOxmlExport)131 sal_uInt32 ImplEESdrWriter::ImplWriteShape( ImplEESdrObject& rObj,
132                                 EscherSolverContainer& rSolverContainer,
133                                 const bool bOOxmlExport )
134 {
135     sal_uInt32 nShapeID = 0;
136     sal_uInt16 nShapeType = 0;
137     bool bDontWriteText = false;        // if a metafile is written as shape replacement, then the text is already part of the metafile
138     bool bAdditionalText = false;
139     sal_uInt32 nGrpShapeID = 0;
140     auto addShape = [this, &rObj, &rSolverContainer, &nShapeID, &nShapeType](sal_uInt16 nType, ShapeFlag nFlags)
141     {
142         nShapeType = nType;
143         nShapeID = mpEscherEx->GenerateShapeId();
144         rObj.SetShapeId( nShapeID );
145         mpEscherEx->AddShape( nType, nFlags, nShapeID );
146         rSolverContainer.AddShape( rObj.GetShapeRef(), nShapeID );
147     };
148 
149     do {
150         mpHostAppData = mpEscherEx->StartShape( rObj.GetShapeRef(), (mpEscherEx->GetGroupLevel() > 1) ? &rObj.GetRect() : nullptr );
151         if ( mpHostAppData && mpHostAppData->DontWriteShape() )
152             break;
153 
154         // #i51348# get shape name
155         OUString aShapeName;
156         if( const SdrObject* pSdrObj = rObj.GetSdrObject() )
157             if (!pSdrObj->GetName().isEmpty())
158                 aShapeName = pSdrObj->GetName();
159         uno::Reference< drawing::XShape> xShape = rObj.GetShapeRef();
160         if (xShape.is())
161         {
162             uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
163             if (xPropertySet.is())
164             {
165                 uno::Sequence<beans::PropertyValue> aGrabBag;
166                 uno::Reference< XPropertySetInfo > xPropInfo = xPropertySet->getPropertySetInfo();
167                 if ( xPropInfo.is() && xPropInfo->hasPropertyByName( "InteropGrabBag" ) )
168                 {
169                     xPropertySet->getPropertyValue( "InteropGrabBag" ) >>= aGrabBag;
170                     for (const beans::PropertyValue& rProp : std::as_const(aGrabBag))
171                     {
172                         if (rProp.Name == "mso-edit-as")
173                         {
174                             OUString rEditAs;
175                             rProp.Value >>= rEditAs;
176                             mpEscherEx->SetEditAs(rEditAs);
177                             break;
178                         }
179                     }
180                 }
181             }
182         }
183 
184         if( rObj.GetType() == "drawing.Group" )
185         {
186             Reference< XIndexAccess > xXIndexAccess( rObj.GetShapeRef(), UNO_QUERY );
187 
188             if( xXIndexAccess.is() && 0 != xXIndexAccess->getCount() )
189             {
190                 nShapeID = mpEscherEx->EnterGroup( aShapeName, &rObj.GetRect() );
191                 nShapeType = ESCHER_ShpInst_Min;
192 
193                 for( sal_uInt32 n = 0, nCnt = xXIndexAccess->getCount();
194                         n < nCnt; ++n )
195                 {
196                     ImplEESdrObject aObj( *this, *o3tl::doAccess<Reference<XShape>>(
197                                     xXIndexAccess->getByIndex( n )) );
198                     if( aObj.IsValid() )
199                     {
200                         aObj.SetOOXML(bOOxmlExport);
201                         ImplWriteShape( aObj, rSolverContainer, bOOxmlExport );
202                     }
203                 }
204                 mpEscherEx->LeaveGroup();
205             }
206             break;
207         }
208         rObj.SetAngle( rObj.ImplGetInt32PropertyValue( "RotateAngle" ));
209 
210         if( ( rObj.ImplGetPropertyValue( "IsFontwork" ) &&
211             ::cppu::any2bool( rObj.GetUsrAny() ) ) ||
212             rObj.GetType() == "drawing.Measure" )
213         {
214             rObj.SetType("drawing.dontknow");
215         }
216 
217         const css::awt::Size   aSize100thmm( rObj.GetShapeRef()->getSize() );
218         const css::awt::Point  aPoint100thmm( rObj.GetShapeRef()->getPosition() );
219         tools::Rectangle   aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) );
220         if ( !mpPicStrm )
221             mpPicStrm = mpEscherEx->QueryPictureStream();
222         EscherPropertyContainer aPropOpt( mpEscherEx->GetGraphicProvider(), mpPicStrm, aRect100thmm );
223 
224         // #i51348# shape name
225         if (!aShapeName.isEmpty())
226             aPropOpt.AddOpt( ESCHER_Prop_wzName, aShapeName );
227         if ( InteractionInfo* pInteraction = mpHostAppData ? mpHostAppData->GetInteractionInfo():nullptr )
228         {
229             const std::unique_ptr< SvMemoryStream >& pMemStrm = pInteraction->getHyperlinkRecord();
230             if (pMemStrm)
231             {
232                 aPropOpt.AddOpt(ESCHER_Prop_pihlShape, false, 0, *pMemStrm);
233             }
234             aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080008 );
235         }
236 
237         if ( rObj.GetType() == "drawing.Custom" )
238         {
239             mpEscherEx->OpenContainer( ESCHER_SpContainer );
240             ShapeFlag nMirrorFlags;
241 
242             OUString sCustomShapeType;
243             MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( rObj.GetShapeRef(), nMirrorFlags, sCustomShapeType, rObj.GetOOXML() );
244             if ( sCustomShapeType == "col-502ad400" || sCustomShapeType == "col-60da8460" )
245             {
246                 addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
247 
248                 if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "MetaFile", false ) )
249                 {
250                     aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
251                     aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );        // no fill
252                     aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 );        // no linestyle
253                     SdrObject* pObj = GetSdrObjectFromXShape( rObj.GetShapeRef() );
254                     if ( pObj )
255                     {
256                         tools::Rectangle aBound = pObj->GetCurrentBoundRect();
257                         Point aPosition( ImplMapPoint( aBound.TopLeft() ) );
258                         Size aSize( ImplMapSize( aBound.GetSize() ) );
259                         rObj.SetRect( tools::Rectangle( aPosition, aSize ) );
260                         rObj.SetAngle( 0 );
261                         bDontWriteText = true;
262                     }
263                 }
264             }
265             else
266             {
267                 const Reference< XPropertySet > xPropSet = rObj.mXPropSet;
268                 drawing::FillStyle eFS = drawing::FillStyle_NONE;
269                 if(xPropSet.is())
270                 {
271                     uno::Reference< XPropertySetInfo > xPropInfo = xPropSet->getPropertySetInfo();
272                     if ( xPropInfo.is() && xPropInfo->hasPropertyByName("FillStyle"))
273                         xPropSet->getPropertyValue("FillStyle") >>= eFS;
274                 }
275 
276                 if (eFS == drawing::FillStyle_BITMAP && eShapeType == mso_sptMax)
277                 {
278                     // We can't map this custom shape to a DOC preset and it has a bitmap fill.
279                     // Make sure that at least the bitmap fill is not lost.
280                     addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
281                     if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Bitmap", false, true, true, bOOxmlExport ) )
282                         aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
283                 }
284                 else
285                 {
286                     addShape(sal::static_int_cast< sal_uInt16 >(eShapeType),
287                              nMirrorFlags | ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor);
288                     aPropOpt.CreateCustomShapeProperties( eShapeType, rObj.GetShapeRef() );
289                     aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
290                     if ( rObj.ImplGetText() )
291                     {
292                         if ( !aPropOpt.IsFontWork() )
293                             aPropOpt.CreateTextProperties( rObj.mXPropSet, mpEscherEx->QueryTextID(
294                                 rObj.GetShapeRef(), rObj.GetShapeId() ), true, false );
295                     }
296                 }
297             }
298         }
299         else if ( rObj.GetType() == "drawing.Rectangle" )
300         {
301             mpEscherEx->OpenContainer( ESCHER_SpContainer );
302             sal_Int32 nRadius = rObj.ImplGetInt32PropertyValue("CornerRadius");
303             if( nRadius )
304             {
305                 nRadius = ImplMapSize( Size( nRadius, 0 )).Width();
306                 addShape( ESCHER_ShpInst_RoundRectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
307                 sal_Int32 nLength = rObj.GetRect().GetWidth();
308                 if ( nLength > rObj.GetRect().GetHeight() )
309                     nLength = rObj.GetRect().GetHeight();
310                 nLength >>= 1;
311                 if ( nRadius >= nLength || nLength == 0 )
312                     nRadius = 0x2a30;                           // 0x2a30 is PPTs maximum radius
313                 else
314                     nRadius = ( 0x2a30 * nRadius ) / nLength;
315                 aPropOpt.AddOpt( ESCHER_Prop_adjustValue, nRadius );
316             }
317             else
318             {
319                 addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
320             }
321             aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
322             if( rObj.ImplGetText() )
323                 aPropOpt.CreateTextProperties( rObj.mXPropSet,
324                     mpEscherEx->QueryTextID( rObj.GetShapeRef(),
325                         rObj.GetShapeId() ), false, false );
326         }
327         else if ( rObj.GetType() == "drawing.Ellipse" )
328         {
329             CircleKind  eCircleKind = CircleKind_FULL;
330             PolyStyle   ePolyKind = PolyStyle();
331             if ( rObj.ImplGetPropertyValue( "CircleKind" ) )
332             {
333                 eCircleKind = *o3tl::doAccess<CircleKind>(rObj.GetUsrAny());
334                 switch ( eCircleKind )
335                 {
336                     case CircleKind_SECTION :
337                     {
338                         ePolyKind = PolyStyle::Pie;
339                     }
340                     break;
341                     case CircleKind_ARC :
342                     {
343                         ePolyKind = PolyStyle::Arc;
344                     }
345                     break;
346 
347                     case CircleKind_CUT :
348                     {
349                         ePolyKind = PolyStyle::Chord;
350                     }
351                     break;
352 
353                     default:
354                         eCircleKind = CircleKind_FULL;
355                 }
356             }
357             if ( eCircleKind == CircleKind_FULL )
358             {
359                 mpEscherEx->OpenContainer( ESCHER_SpContainer );
360                 addShape( ESCHER_ShpInst_Ellipse, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
361                 aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
362             }
363             else
364             {
365                 sal_Int32 nStartAngle, nEndAngle;
366                 if ( !rObj.ImplGetPropertyValue( "CircleStartAngle" ) )
367                     break;
368                 nStartAngle = *o3tl::doAccess<sal_Int32>(rObj.GetUsrAny());
369                 if( !rObj.ImplGetPropertyValue( "CircleEndAngle" ) )
370                     break;
371                 nEndAngle = *o3tl::doAccess<sal_Int32>(rObj.GetUsrAny());
372 
373                 Point aStart, aEnd, aCenter;
374                 aStart.setX( static_cast<sal_Int32>( cos( nStartAngle * F_PI18000 ) * 100.0 ) );
375                 aStart.setY( - static_cast<sal_Int32>( sin( nStartAngle * F_PI18000 ) * 100.0 ) );
376                 aEnd.setX( static_cast<sal_Int32>( cos( nEndAngle * F_PI18000 ) * 100.0 ) );
377                 aEnd.setY( - static_cast<sal_Int32>( sin( nEndAngle * F_PI18000 ) * 100.0 ) );
378                 const tools::Rectangle& rRect = aRect100thmm;
379                 aCenter.setX( rRect.Left() + ( rRect.GetWidth() / 2 ) );
380                 aCenter.setY( rRect.Top() + ( rRect.GetHeight() / 2 ) );
381                 aStart.AdjustX(aCenter.X() );
382                 aStart.AdjustY(aCenter.Y() );
383                 aEnd.AdjustX(aCenter.X() );
384                 aEnd.AdjustY(aCenter.Y() );
385                 tools::Polygon aPolygon( rRect, aStart, aEnd, ePolyKind );
386                 if( rObj.GetAngle() )
387                 {
388                     aPolygon.Rotate( rRect.TopLeft(), Degree10(static_cast<sal_Int16>( rObj.GetAngle() / 10 )) );
389                     rObj.SetAngle( 0 );
390                 }
391                 mpEscherEx->OpenContainer( ESCHER_SpContainer );
392                 addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
393                 css::awt::Rectangle aNewRect;
394                 switch ( ePolyKind )
395                 {
396                     case PolyStyle::Pie :
397                     case PolyStyle::Chord :
398                     {
399                         aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect, &aPolygon );
400                         aPropOpt.CreateFillProperties( rObj.mXPropSet, true  );
401                     }
402                     break;
403 
404                     case PolyStyle::Arc :
405                     {
406                         aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect, &aPolygon );
407                         aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
408                     }
409                     break;
410                 }
411                 rObj.SetRect( tools::Rectangle( ImplMapPoint( Point( aNewRect.X, aNewRect.Y ) ),
412                                             ImplMapSize( Size( aNewRect.Width, aNewRect.Height ) ) ) );
413             }
414             if ( rObj.ImplGetText() )
415                 aPropOpt.CreateTextProperties( rObj.mXPropSet,
416                     mpEscherEx->QueryTextID( rObj.GetShapeRef(),
417                         rObj.GetShapeId() ), false, false );
418 
419         }
420         else if ( rObj.GetType() == "drawing.Control" )
421         {
422             const Reference< XPropertySet > xPropSet = rObj.mXPropSet;
423             const Reference<XPropertySetInfo> xPropInfo = xPropSet.is() ? xPropSet->getPropertySetInfo() : Reference<XPropertySetInfo>();
424             // This code is expected to be called only for DOCX/XLSX formats.
425             if (xPropInfo.is() && bOOxmlExport)
426             {
427                 bool bInline = false;
428                 if (xPropInfo->hasPropertyByName("AnchorType"))
429                 {
430                     text::TextContentAnchorType eAnchorType;
431                     xPropSet->getPropertyValue("AnchorType") >>= eAnchorType;
432                     bInline = eAnchorType == text::TextContentAnchorType_AS_CHARACTER;
433                 }
434 
435                 mpEscherEx->OpenContainer( ESCHER_SpContainer );
436                 if(bInline)
437                 {
438                     addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
439                 }
440                 else
441                 {
442                     addShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
443                 }
444             }
445             else
446                 break;
447         }
448         else if ( rObj.GetType() == "drawing.Connector" )
449         {
450             sal_uInt16 nSpType;
451             ShapeFlag nSpFlags;
452             css::awt::Rectangle aNewRect;
453             if ( ! aPropOpt.CreateConnectorProperties( rObj.GetShapeRef(),
454                             rSolverContainer, aNewRect, nSpType, nSpFlags ) )
455                 break;
456             rObj.SetRect( tools::Rectangle( ImplMapPoint( Point( aNewRect.X, aNewRect.Y ) ),
457                                         ImplMapSize( Size( aNewRect.Width, aNewRect.Height ) ) ) );
458 
459             mpEscherEx->OpenContainer( ESCHER_SpContainer );
460             addShape( nSpType, nSpFlags );
461         }
462         else if ( rObj.GetType() == "drawing.Measure" )
463         {
464             break;
465         }
466         else if ( rObj.GetType() == "drawing.Line" )
467         {
468             css::awt::Rectangle aNewRect;
469             aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_LINE, false, aNewRect );
470             //i27942: Poly/Lines/Bezier do not support text.
471 
472             mpEscherEx->OpenContainer( ESCHER_SpContainer );
473             ShapeFlag nFlags = ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor;
474             if( aNewRect.Height < 0 )
475                 nFlags |= ShapeFlag::FlipV;
476             if( aNewRect.Width < 0 )
477                 nFlags |= ShapeFlag::FlipH;
478 
479             addShape( ESCHER_ShpInst_Line, nFlags );
480             aPropOpt.AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex );
481             aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
482             rObj.SetAngle( 0 );
483         }
484         else if ( rObj.GetType() == "drawing.PolyPolygon" )
485         {
486             if( rObj.ImplHasText() )
487             {
488                 nGrpShapeID = ImplEnterAdditionalTextGroup( rObj.GetShapeRef(), &rObj.GetRect() );
489                 bAdditionalText = true;
490             }
491             mpEscherEx->OpenContainer( ESCHER_SpContainer );
492             addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
493             css::awt::Rectangle aNewRect;
494             aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect );
495             aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
496             rObj.SetAngle( 0 );
497         }
498         else if ( rObj.GetType() == "drawing.PolyLine" )
499         {
500             //i27942: Poly/Lines/Bezier do not support text.
501 
502             mpEscherEx->OpenContainer( ESCHER_SpContainer );
503             addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
504             css::awt::Rectangle aNewRect;
505             aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect );
506             aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
507             rObj.SetAngle( 0 );
508         }
509         else if ( rObj.GetType() == "drawing.OpenBezier" )
510         {
511             //i27942: Poly/Lines/Bezier do not support text.
512 
513             mpEscherEx->OpenContainer( ESCHER_SpContainer );
514             addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
515             css::awt::Rectangle aNewRect;
516             aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, true, aNewRect );
517             aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
518             rObj.SetAngle( 0 );
519         }
520         else if ( rObj.GetType() == "drawing.ClosedBezier" )
521         {
522             if ( rObj.ImplHasText() )
523             {
524                 nGrpShapeID = ImplEnterAdditionalTextGroup( rObj.GetShapeRef(), &rObj.GetRect() );
525                 bAdditionalText = true;
526             }
527             mpEscherEx->OpenContainer( ESCHER_SpContainer );
528             addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
529             css::awt::Rectangle aNewRect;
530             aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, true, aNewRect );
531             aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
532             rObj.SetAngle( 0 );
533         }
534         else if ( rObj.GetType() == "drawing.GraphicObject" )
535         {
536             mpEscherEx->OpenContainer( ESCHER_SpContainer );
537 
538             // a GraphicObject can also be a ClickMe element
539             if( rObj.IsEmptyPresObj() )
540             {
541                 addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveMaster | ShapeFlag::HaveAnchor );
542                 sal_uInt32 nTxtBxId = mpEscherEx->QueryTextID( rObj.GetShapeRef(),
543                                                         rObj.GetShapeId() );
544                 aPropOpt.AddOpt( ESCHER_Prop_lTxid, nTxtBxId );
545                 aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 );
546                 aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 );
547                 aPropOpt.AddOpt( ESCHER_Prop_hspMaster, 0 );
548             }
549             else
550             {
551                 if( rObj.ImplGetText() )
552                 {
553                     /* SJ #i34951#: because M. documents are not allowing GraphicObjects containing text, we
554                        have to create a simple Rectangle with fill bitmap instead (while not allowing BitmapMode_Repeat).
555                     */
556                     addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
557                     if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Graphic", true, true, false ) )
558                     {
559                         aPropOpt.AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone );
560                         aPropOpt.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle );
561                         aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 );
562                         aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x8000000 );
563                         aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 );
564                         if ( rObj.ImplGetText() )
565                             aPropOpt.CreateTextProperties( rObj.mXPropSet,
566                                 mpEscherEx->QueryTextID( rObj.GetShapeRef(),
567                                     rObj.GetShapeId() ), false, false );
568                     }
569                 }
570                 else
571                 {
572                     addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
573                     if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Graphic", false, true, true, bOOxmlExport ) )
574                         aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
575                 }
576             }
577         }
578         else if ( rObj.GetType() == "drawing.Text" )
579         {
580             mpEscherEx->OpenContainer( ESCHER_SpContainer );
581             addShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
582             aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
583             if( rObj.ImplGetText() )
584                 aPropOpt.CreateTextProperties( rObj.mXPropSet,
585                     mpEscherEx->QueryTextID( rObj.GetShapeRef(),
586                         rObj.GetShapeId() ) );
587         }
588         else if ( rObj.GetType() == "drawing.Page" )
589         {
590             mpEscherEx->OpenContainer( ESCHER_SpContainer );
591             addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
592             aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x40004 );
593             aPropOpt.AddOpt( ESCHER_Prop_fFillOK, 0x100001 );
594             aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110011 );
595             aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90008 );
596             aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x10001 );
597         }
598         else if ( rObj.GetType() == "drawing.Frame" )
599         {
600             break;
601         }
602         else if ( rObj.GetType() == "drawing.OLE2" )
603         {
604             mpEscherEx->OpenContainer( ESCHER_SpContainer );
605             if( rObj.IsEmptyPresObj() )
606             {
607                 addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveMaster | ShapeFlag::HaveAnchor );
608                 sal_uInt32 nTxtBxId = mpEscherEx->QueryTextID( rObj.GetShapeRef(),
609                                                         rObj.GetShapeId() );
610                 aPropOpt.AddOpt( ESCHER_Prop_lTxid, nTxtBxId );
611                 aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 );
612                 aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 );
613                 aPropOpt.AddOpt( ESCHER_Prop_hspMaster, 0 );
614             }
615             else
616             {
617                 //2do: could be made an option in HostAppData whether OLE object should be written or not
618                 const bool bAppOLE = true;
619                 addShape( ESCHER_ShpInst_PictureFrame,
620                     ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | (bAppOLE ? ShapeFlag::OLEShape : ShapeFlag::NONE) );
621                 if ( aPropOpt.CreateOLEGraphicProperties( rObj.GetShapeRef() ) )
622                 {
623                     if ( bAppOLE )
624                     {   // snooped from Xcl hex dump, nobody knows the trouble I have seen
625                         aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape,    0x00080008 );
626                         aPropOpt.AddOpt( ESCHER_Prop_pictureId,     0x00000001 );
627                         aPropOpt.AddOpt( ESCHER_Prop_fillColor,     0x08000041 );
628                         aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x08000041 );
629                         aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest,    0x00110010 );
630                         aPropOpt.AddOpt( ESCHER_Prop_lineColor,     0x08000040 );
631                         aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash,0x00080008 );
632                         aPropOpt.AddOpt( ESCHER_Prop_fPrint,            0x00080000 );
633                     }
634                     aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
635                 }
636             }
637         }
638         else if( '3' == rObj.GetType()[8] &&
639                  'D' == rObj.GetType()[9] )   // drawing.3D
640         {
641             // SceneObject, CubeObject, SphereObject, LatheObject, ExtrudeObject, PolygonObject
642             if ( !rObj.ImplGetPropertyValue( "Bitmap" ) )
643                 break;
644 
645             mpEscherEx->OpenContainer( ESCHER_SpContainer );
646             addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
647 
648             if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Bitmap", false ) )
649                 aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
650         }
651         else if ( rObj.GetType() == "drawing.Caption" )
652         {
653             rObj.SetAngle( 0 );
654             mpEscherEx->OpenContainer( ESCHER_SpContainer );
655             addShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
656             if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "MetaFile", false ) )
657                 aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
658         }
659         else if ( rObj.GetType() == "drawing.dontknow" )
660         {
661             rObj.SetAngle( 0 );
662             mpEscherEx->OpenContainer( ESCHER_SpContainer );
663             addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
664             if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "MetaFile", false ) )
665                 aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
666         }
667         else
668         {
669             break;
670         }
671         aPropOpt.CreateShadowProperties( rObj.mXPropSet );
672 
673         if( SDRLAYER_NOTFOUND != mpEscherEx->GetHellLayerId() &&
674             rObj.ImplGetPropertyValue( "LayerID" ) &&
675             *o3tl::doAccess<sal_uInt16>(rObj.GetUsrAny()) == sal_uInt8(mpEscherEx->GetHellLayerId()) )
676         {
677             aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x200020 );
678         }
679 
680         {
681             tools::Rectangle aRect( rObj.GetRect() );
682             aRect.Justify();
683             rObj.SetRect( aRect );
684         }
685 
686         if( rObj.GetAngle() )
687             ImplFlipBoundingBox( rObj, aPropOpt );
688 
689         aPropOpt.CreateShapeProperties( rObj.GetShapeRef() );
690         const SdrObject* sdrObj = rObj.GetSdrObject();
691         mpEscherEx->AddSdrObjectVMLObject(*sdrObj );
692         mpEscherEx->Commit( aPropOpt, rObj.GetRect());
693         if( mpEscherEx->GetGroupLevel() > 1 )
694             mpEscherEx->AddChildAnchor( rObj.GetRect() );
695 
696         if ( mpHostAppData )
697         {   //! with AdditionalText the App has to control whether these are written or not
698             mpHostAppData->WriteClientAnchor( *mpEscherEx, rObj.GetRect() );
699             mpHostAppData->WriteClientData( *mpEscherEx );
700             if ( !bDontWriteText )
701                 mpHostAppData->WriteClientTextbox( *mpEscherEx );
702         }
703         mpEscherEx->CloseContainer();       // ESCHER_SpContainer
704 
705         if( bAdditionalText )
706         {
707             mpEscherEx->EndShape( nShapeType, nShapeID );
708             ImplWriteAdditionalText( rObj );
709         }
710 
711     } while ( false );
712 
713     if ( bAdditionalText )
714         mpEscherEx->EndShape( ESCHER_ShpInst_Min, nGrpShapeID );
715     else
716         mpEscherEx->EndShape( nShapeType, nShapeID );
717     return nShapeID;
718 }
719 
ImplWriteAdditionalText(ImplEESdrObject & rObj)720 void ImplEESdrWriter::ImplWriteAdditionalText( ImplEESdrObject& rObj )
721 {
722     sal_uInt32 nShapeID = 0;
723     sal_uInt16 nShapeType = 0;
724     do
725     {
726         mpHostAppData = mpEscherEx->StartShape( rObj.GetShapeRef(), (mpEscherEx->GetGroupLevel() > 1) ? &rObj.GetRect() : nullptr );
727         if ( mpHostAppData && mpHostAppData->DontWriteShape() )
728             break;
729 
730         const css::awt::Size   aSize100thmm( rObj.GetShapeRef()->getSize() );
731         const css::awt::Point  aPoint100thmm( rObj.GetShapeRef()->getPosition() );
732         tools::Rectangle   aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) );
733         if ( !mpPicStrm )
734             mpPicStrm = mpEscherEx->QueryPictureStream();
735         EscherPropertyContainer aPropOpt( mpEscherEx->GetGraphicProvider(), mpPicStrm, aRect100thmm );
736         rObj.SetAngle( rObj.ImplGetInt32PropertyValue( "RotateAngle" ));
737         sal_Int32 nAngle = rObj.GetAngle();
738         if( rObj.GetType() == "drawing.Line" )
739         {
740 //2do: this does not work right
741             double fDist = hypot( rObj.GetRect().GetWidth(),
742                                     rObj.GetRect().GetHeight() );
743             rObj.SetRect( tools::Rectangle( Point(),
744                             Point( static_cast<sal_Int32>( fDist ), -1 ) ) );
745 
746             mpEscherEx->OpenContainer( ESCHER_SpContainer );
747             mpEscherEx->AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
748             if ( rObj.ImplGetText() )
749                 aPropOpt.CreateTextProperties( rObj.mXPropSet,
750                     mpEscherEx->QueryTextID( rObj.GetShapeRef(),
751                         rObj.GetShapeId() ) );
752 
753             aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 );
754             aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
755             aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x60006 );     // Size Shape To Fit Text
756             if ( nAngle < 0 )
757                 nAngle = ( 36000 + nAngle ) % 36000;
758             if ( nAngle )
759                 ImplFlipBoundingBox( rObj, aPropOpt );
760         }
761         else
762         {
763             mpEscherEx->OpenContainer( ESCHER_SpContainer );
764             nShapeID = mpEscherEx->GenerateShapeId();
765             nShapeType = ESCHER_ShpInst_TextBox;
766             mpEscherEx->AddShape( nShapeType, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor, nShapeID );
767             if ( rObj.ImplGetText() )
768                 aPropOpt.CreateTextProperties( rObj.mXPropSet,
769                     mpEscherEx->QueryTextID( rObj.GetShapeRef(),
770                         rObj.GetShapeId() ) );
771             aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 );
772             aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
773 
774             if( nAngle < 0 )
775                 nAngle = ( 36000 + nAngle ) % 36000;
776             else
777                 nAngle = ( 36000 - ( nAngle % 36000 ) );
778 
779             nAngle *= 655;
780             nAngle += 0x8000;
781             nAngle &=~0xffff;   // nAngle round to full degrees
782             aPropOpt.AddOpt( ESCHER_Prop_Rotation, nAngle );
783             mpEscherEx->SetGroupSnapRect( mpEscherEx->GetGroupLevel(),
784                                             rObj.GetRect() );
785             mpEscherEx->SetGroupLogicRect( mpEscherEx->GetGroupLevel(),
786                                             rObj.GetRect() );
787         }
788         rObj.SetAngle( nAngle );
789         aPropOpt.CreateShapeProperties( rObj.GetShapeRef() );
790         const SdrObject* sdrObj = rObj.GetSdrObject();
791         mpEscherEx->AddSdrObjectVMLObject(*sdrObj );
792         mpEscherEx->Commit( aPropOpt, rObj.GetRect());
793 
794         // write the childanchor
795         mpEscherEx->AddChildAnchor( rObj.GetRect() );
796 
797 #if defined EES_WRITE_EPP
798         // ClientAnchor
799         mpEscherEx->AddClientAnchor( maRect );
800         // ClientTextbox
801         mpEscherEx->OpenContainer( ESCHER_ClientTextbox );
802         mpEscherEx->AddAtom( 4, EPP_TextHeaderAtom );
803         *mpStrm << (sal_uInt32)EPP_TEXTTYPE_Other;                              // Text in a Shape
804         ImplWriteTextStyleAtom();
805         mpEscherEx->CloseContainer();   // ESCHER_ClientTextBox
806 #else // !EES_WRITE_EPP
807         if ( mpHostAppData )
808         {   //! the App has to control whether these are written or not
809             mpHostAppData->WriteClientAnchor( *mpEscherEx, rObj.GetRect() );
810             mpHostAppData->WriteClientData( *mpEscherEx );
811             mpHostAppData->WriteClientTextbox( *mpEscherEx );
812         }
813 #endif // EES_WRITE_EPP
814         mpEscherEx->CloseContainer();   // ESCHER_SpContainer
815     } while ( false );
816     mpEscherEx->LeaveGroup();
817     mpEscherEx->EndShape( nShapeType, nShapeID );
818 }
819 
820 
ImplEnterAdditionalTextGroup(const Reference<XShape> & rShape,const tools::Rectangle * pBoundRect)821 sal_uInt32 ImplEESdrWriter::ImplEnterAdditionalTextGroup( const Reference< XShape >& rShape,
822             const tools::Rectangle* pBoundRect )
823 {
824     mpHostAppData = mpEscherEx->EnterAdditionalTextGroup();
825     sal_uInt32 nGrpId = mpEscherEx->EnterGroup( pBoundRect );
826     mpHostAppData = mpEscherEx->StartShape( rShape, pBoundRect );
827     return nGrpId;
828 }
829 
830 
ImplInitPageValues()831 void ImplEESdrWriter::ImplInitPageValues()
832 {
833     mbIsTitlePossible = true;       // With more than one title PowerPoint will fail.
834 }
835 
ImplWritePage(EscherSolverContainer & rSolverContainer,bool ooxmlExport)836 void ImplEESdrWriter::ImplWritePage(
837             EscherSolverContainer& rSolverContainer, bool ooxmlExport )
838 {
839     ImplInitPageValues();
840 
841     const sal_uInt32 nShapes = mXShapes->getCount();
842     for( sal_uInt32 n = 0; n < nShapes; ++n )
843     {
844         ImplEESdrObject aObj( *this, *o3tl::doAccess<Reference<XShape>>(
845                                     mXShapes->getByIndex( n )) );
846         if( aObj.IsValid() )
847         {
848             ImplWriteShape( aObj, rSolverContainer, ooxmlExport );
849         }
850     }
851 }
852 
~ImplEESdrWriter()853 ImplEESdrWriter::~ImplEESdrWriter()
854 {
855     DBG_ASSERT( !mpSolverContainer, "ImplEESdrWriter::~ImplEESdrWriter: unwritten SolverContainer" );
856     Reference<css::lang::XComponent> xComp(mXDrawPage, UNO_QUERY);
857     if (xComp.is())
858         xComp->dispose();
859 }
860 
861 
ImplInitPage(const SdrPage & rPage)862 bool ImplEESdrWriter::ImplInitPage( const SdrPage& rPage )
863 {
864     rtl::Reference<SvxDrawPage> pSvxDrawPage;
865     if ( mpSdrPage != &rPage || !mXDrawPage.is() )
866     {
867         // eventually write SolverContainer of current page, deletes the Solver
868         ImplFlushSolverContainer();
869 
870         mpSdrPage = nullptr;
871         Reference<css::lang::XComponent> xOldDrawPage(mXDrawPage, UNO_QUERY);
872         if (xOldDrawPage.is())
873             xOldDrawPage->dispose();
874         mXDrawPage = pSvxDrawPage = new SvxFmDrawPage( const_cast<SdrPage*>(&rPage) );
875         mXShapes = mXDrawPage;
876         if ( !mXShapes.is() )
877             return false;
878         ImplInitPageValues();
879         mpSdrPage = &rPage;
880 
881         mpSolverContainer.reset( new EscherSolverContainer );
882     }
883     else
884         pSvxDrawPage = comphelper::getUnoTunnelImplementation<SvxDrawPage>(mXDrawPage);
885 
886     return pSvxDrawPage != nullptr;
887 }
888 
ImplInitUnoShapes(const Reference<XShapes> & rxShapes)889 bool ImplEESdrWriter::ImplInitUnoShapes( const Reference< XShapes >& rxShapes )
890 {
891     // eventually write SolverContainer of current page, deletes the Solver
892     ImplFlushSolverContainer();
893 
894     if( !rxShapes.is() )
895         return false;
896 
897     mpSdrPage = nullptr;
898     mXDrawPage.clear();
899     mXShapes = rxShapes;
900 
901     ImplInitPageValues();
902 
903     mpSolverContainer.reset( new EscherSolverContainer );
904     return true;
905 }
906 
ImplExitPage()907 void ImplEESdrWriter::ImplExitPage()
908 {
909     // close all groups before the solver container is written
910     while( mpEscherEx->GetGroupLevel() )
911         mpEscherEx->LeaveGroup();
912 
913     ImplFlushSolverContainer();
914     mpSdrPage = nullptr;   // reset page for next init
915 }
916 
917 
ImplFlushSolverContainer()918 void ImplEESdrWriter::ImplFlushSolverContainer()
919 {
920     if ( mpSolverContainer )
921     {
922         mpSolverContainer->WriteSolver( mpEscherEx->GetStream() );
923         mpSolverContainer.reset();
924     }
925 }
926 
ImplWriteCurrentPage(bool ooxmlExport)927 void ImplEESdrWriter::ImplWriteCurrentPage(bool ooxmlExport)
928 {
929     assert(mpSolverContainer && "ImplEESdrWriter::ImplWriteCurrentPage: no SolverContainer");
930     ImplWritePage( *mpSolverContainer, ooxmlExport );
931     ImplExitPage();
932 }
933 
ImplWriteTheShape(ImplEESdrObject & rObj,bool ooxmlExport)934 sal_uInt32 ImplEESdrWriter::ImplWriteTheShape( ImplEESdrObject& rObj , bool ooxmlExport )
935 {
936     assert(mpSolverContainer && "ImplEESdrWriter::ImplWriteShape: no SolverContainer");
937     return ImplWriteShape( rObj, *mpSolverContainer, ooxmlExport );
938 }
939 
AddSdrPage(const SdrPage & rPage,bool ooxmlExport)940 void EscherEx::AddSdrPage( const SdrPage& rPage, bool ooxmlExport )
941 {
942     if ( mpImplEESdrWriter->ImplInitPage( rPage ) )
943         mpImplEESdrWriter->ImplWriteCurrentPage(ooxmlExport);
944 }
945 
AddUnoShapes(const Reference<XShapes> & rxShapes,bool ooxmlExport)946 void EscherEx::AddUnoShapes( const Reference< XShapes >& rxShapes, bool ooxmlExport )
947 {
948     if ( mpImplEESdrWriter->ImplInitUnoShapes( rxShapes ) )
949         mpImplEESdrWriter->ImplWriteCurrentPage(ooxmlExport);
950 }
951 
AddSdrObject(const SdrObject & rObj,bool ooxmlExport)952 sal_uInt32 EscherEx::AddSdrObject( const SdrObject& rObj, bool ooxmlExport )
953 {
954     ImplEESdrObject aObj( *mpImplEESdrWriter, rObj, mbOOXML );
955     if( aObj.IsValid() )
956         return mpImplEESdrWriter->ImplWriteTheShape( aObj, ooxmlExport );
957     return 0;
958 }
959 
960 
EndSdrObjectPage()961 void EscherEx::EndSdrObjectPage()
962 {
963     mpImplEESdrWriter->ImplExitPage();
964 }
965 
StartShape(const Reference<XShape> &,const tools::Rectangle *)966 EscherExHostAppData* EscherEx::StartShape( const Reference< XShape >& /* rShape */, const tools::Rectangle* /*pChildAnchor*/ )
967 {
968     return nullptr;
969 }
970 
EndShape(sal_uInt16,sal_uInt32)971 void EscherEx::EndShape( sal_uInt16 /* nShapeType */, sal_uInt32 /* nShapeID */ )
972 {
973 }
974 
QueryTextID(const Reference<XShape> &,sal_uInt32)975 sal_uInt32 EscherEx::QueryTextID( const Reference< XShape >&, sal_uInt32 )
976 {
977     return 0;
978 }
979 
980 // add a dummy rectangle shape into the escher stream
AddDummyShape()981 sal_uInt32 EscherEx::AddDummyShape()
982 {
983     OpenContainer( ESCHER_SpContainer );
984     sal_uInt32 nShapeID = GenerateShapeId();
985     AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor, nShapeID );
986     CloseContainer();
987 
988     return nShapeID;
989 }
990 
991 // static
GetSdrObject(const Reference<XShape> & rShape)992 const SdrObject* EscherEx::GetSdrObject( const Reference< XShape >& rShape )
993 {
994     const SdrObject* pRet  = SdrObject::getSdrObjectFromXShape( rShape );
995     DBG_ASSERT( pRet, "EscherEx::GetSdrObject: no SdrObj" );
996     return pRet;
997 }
998 
999 
ImplEESdrObject(ImplEESdrWriter & rEx,const SdrObject & rObj,bool bOOXML)1000 ImplEESdrObject::ImplEESdrObject( ImplEESdrWriter& rEx,
1001                                     const SdrObject& rObj, bool bOOXML ) :
1002     mnShapeId( 0 ),
1003     mnTextSize( 0 ),
1004     mnAngle( 0 ),
1005     mbValid( false ),
1006     mbPresObj( false ),
1007     mbEmptyPresObj( false ),
1008     mbOOXML(bOOXML)
1009 {
1010     SdrPage* pPage = rObj.getSdrPageFromSdrObject();
1011     DBG_ASSERT( pPage, "ImplEESdrObject::ImplEESdrObject: no SdrPage" );
1012     if( pPage && rEx.ImplInitPage( *pPage ) )
1013     {
1014         // why not declare a const parameter if the object will
1015         // not be modified?
1016         mXShape.set( const_cast<SdrObject*>(&rObj)->getUnoShape(), UNO_QUERY );
1017         Init( rEx );
1018     }
1019 }
1020 
ImplEESdrObject(ImplEESdrWriter & rEx,const Reference<XShape> & rShape)1021 ImplEESdrObject::ImplEESdrObject( ImplEESdrWriter& rEx,
1022                                     const Reference< XShape >& rShape ) :
1023     mXShape( rShape ),
1024     mnShapeId( 0 ),
1025     mnTextSize( 0 ),
1026     mnAngle( 0 ),
1027     mbValid( false ),
1028     mbPresObj( false ),
1029     mbEmptyPresObj( false ),
1030     mbOOXML(false)
1031 {
1032     Init( rEx );
1033 }
1034 
1035 
~ImplEESdrObject()1036 ImplEESdrObject::~ImplEESdrObject()
1037 {
1038 }
1039 
getUnrotatedGroupBoundRange(const Reference<XShape> & rxShape)1040 static basegfx::B2DRange getUnrotatedGroupBoundRange(const Reference< XShape >& rxShape)
1041 {
1042     basegfx::B2DRange aRetval;
1043 
1044     try
1045     {
1046         if(rxShape.is())
1047         {
1048             if(rxShape->getShapeType() == "com.sun.star.drawing.GroupShape")
1049             {
1050                 // it's a group shape, iterate over children
1051                 const Reference< XIndexAccess > xXIndexAccess(rxShape, UNO_QUERY);
1052 
1053                 if(xXIndexAccess.is())
1054                 {
1055                     for(sal_uInt32 n(0), nCnt = xXIndexAccess->getCount(); n < nCnt; ++n)
1056                     {
1057                         const Reference< XShape > axShape(xXIndexAccess->getByIndex(n), UNO_QUERY);
1058 
1059                         if(axShape.is())
1060                         {
1061                             // we are calculating the bound for a group, correct rotation for sub-objects
1062                             // to get the unrotated bounds for the group
1063                             const basegfx::B2DRange aExtend(getUnrotatedGroupBoundRange(axShape));
1064 
1065                             aRetval.expand(aExtend);
1066                         }
1067                     }
1068                 }
1069             }
1070             else
1071             {
1072                 // iT#s a xShape, get its transformation
1073                 const Reference< XPropertySet > xPropSet(rxShape, UNO_QUERY);
1074 
1075                 if(xPropSet.is())
1076                 {
1077                     const Any aAny = xPropSet->getPropertyValue("Transformation");
1078 
1079                     if(aAny.hasValue())
1080                     {
1081                         HomogenMatrix3 aMatrix;
1082 
1083                         if(aAny >>= aMatrix)
1084                         {
1085                             basegfx::B2DHomMatrix aHomogenMatrix;
1086 
1087                             aHomogenMatrix.set(0, 0, aMatrix.Line1.Column1);
1088                             aHomogenMatrix.set(0, 1, aMatrix.Line1.Column2);
1089                             aHomogenMatrix.set(0, 2, aMatrix.Line1.Column3);
1090                             aHomogenMatrix.set(1, 0, aMatrix.Line2.Column1);
1091                             aHomogenMatrix.set(1, 1, aMatrix.Line2.Column2);
1092                             aHomogenMatrix.set(1, 2, aMatrix.Line2.Column3);
1093                             aHomogenMatrix.set(2, 0, aMatrix.Line3.Column1);
1094                             aHomogenMatrix.set(2, 1, aMatrix.Line3.Column2);
1095                             aHomogenMatrix.set(2, 2, aMatrix.Line3.Column3);
1096 
1097                             basegfx::B2DVector aScale, aTranslate;
1098                             double fRotate, fShearX;
1099 
1100                             // decompose transformation
1101                             aHomogenMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
1102 
1103                             // check if rotation needs to be corrected
1104                             if(!basegfx::fTools::equalZero(fRotate))
1105                             {
1106                                 // to correct, keep in mind that ppt graphics are rotated around their center
1107                                 const basegfx::B2DPoint aCenter(aHomogenMatrix * basegfx::B2DPoint(0.5, 0.5));
1108 
1109                                 aHomogenMatrix.translate(-aCenter.getX(), -aCenter.getY());
1110                                 aHomogenMatrix.rotate(-fRotate);
1111                                 aHomogenMatrix.translate(aCenter.getX(), aCenter.getY());
1112                             }
1113 
1114 
1115                             // check if shear needs to be corrected (always correct shear,
1116                             // ppt does not know about it)
1117                             if(!basegfx::fTools::equalZero(fShearX))
1118                             {
1119                                 const basegfx::B2DPoint aMinimum(aHomogenMatrix * basegfx::B2DPoint(0.0, 0.0));
1120 
1121                                 aHomogenMatrix.translate(-aMinimum.getX(), -aMinimum.getY());
1122                                 aHomogenMatrix.shearX(-fShearX);
1123                                 aHomogenMatrix.translate(aMinimum.getX(), aMinimum.getY());
1124                             }
1125 
1126                             // create range. It's no longer rotated (or sheared), so use
1127                             // minimum and maximum values
1128                             aRetval.expand(aHomogenMatrix * basegfx::B2DPoint(0.0, 0.0));
1129                             aRetval.expand(aHomogenMatrix * basegfx::B2DPoint(1.0, 1.0));
1130                         }
1131                     }
1132                 }
1133             }
1134         }
1135     }
1136     catch(css::uno::Exception&)
1137     {
1138     }
1139 
1140     return aRetval;
1141 }
1142 
Init(ImplEESdrWriter & rEx)1143 void ImplEESdrObject::Init( ImplEESdrWriter& rEx )
1144 {
1145     mXPropSet.set( mXShape, UNO_QUERY );
1146     if( !mXPropSet.is() )
1147         return;
1148 
1149     // detect name first to make below test (is group) work
1150     mType = mXShape->getShapeType();
1151     (void)mType.startsWith( "com.sun.star.", &mType );  // strip "com.sun.star."
1152     (void)mType.endsWith( "Shape", &mType );  // strip "Shape"
1153 
1154     if(GetType() == "drawing.Group")
1155     {
1156         // if it's a group, the unrotated range is needed for that group
1157         const basegfx::B2DRange aUnrotatedRange(getUnrotatedGroupBoundRange(mXShape));
1158         const Point aNewP(basegfx::fround(aUnrotatedRange.getMinX()), basegfx::fround(aUnrotatedRange.getMinY()));
1159         const Size aNewS(basegfx::fround(aUnrotatedRange.getWidth()), basegfx::fround(aUnrotatedRange.getHeight()));
1160 
1161         SetRect(rEx.ImplMapPoint(aNewP), rEx.ImplMapSize(aNewS));
1162     }
1163     else
1164     {
1165         // if it's no group, use position and size directly, rotated/sheared or not
1166         const Point aOldP(mXShape->getPosition().X, mXShape->getPosition().Y);
1167         const Size aOldS(mXShape->getSize().Width, mXShape->getSize().Height);
1168 
1169         SetRect(rEx.ImplMapPoint(aOldP), rEx.ImplMapSize(aOldS));
1170     }
1171 
1172     if( ImplGetPropertyValue( "IsPresentationObject" ) )
1173         mbPresObj = ::cppu::any2bool( mAny );
1174 
1175     if( mbPresObj && ImplGetPropertyValue( "IsEmptyPresentationObject" ) )
1176         mbEmptyPresObj = ::cppu::any2bool( mAny );
1177 
1178     mbValid = true;
1179 }
1180 
ImplGetPropertyValue(const OUString & rString)1181 bool ImplEESdrObject::ImplGetPropertyValue( const OUString& rString )
1182 {
1183     bool bRetValue = false;
1184     if( mbValid )
1185     {
1186         try
1187         {
1188             mAny = mXPropSet->getPropertyValue( rString );
1189             if( mAny.hasValue() )
1190                 bRetValue = true;
1191         }
1192         catch( const css::uno::Exception& )
1193         {
1194             bRetValue = false;
1195         }
1196     }
1197     return bRetValue;
1198 }
1199 
SetRect(const Point & rPos,const Size & rSz)1200 void ImplEESdrObject::SetRect( const Point& rPos, const Size& rSz )
1201 {
1202     maRect = tools::Rectangle( rPos, rSz );
1203 }
1204 
GetSdrObject() const1205 const SdrObject* ImplEESdrObject::GetSdrObject() const
1206 {
1207     return EscherEx::GetSdrObject( mXShape );
1208 }
1209 
1210 // loads and converts text from shape, result is saved in mnTextSize
ImplGetText()1211 sal_uInt32 ImplEESdrObject::ImplGetText()
1212 {
1213     Reference< XText > xXText( mXShape, UNO_QUERY );
1214     mnTextSize = 0;
1215     if (xXText.is())
1216     {
1217         try
1218         {
1219             mnTextSize = xXText->getString().getLength();
1220         }
1221         catch (const uno::RuntimeException&)
1222         {
1223             TOOLS_WARN_EXCEPTION("filter.ms", "ImplGetText");
1224         }
1225     }
1226     return mnTextSize;
1227 }
1228 
ImplHasText() const1229 bool ImplEESdrObject::ImplHasText() const
1230 {
1231     Reference< XText > xXText( mXShape, UNO_QUERY );
1232     return xXText.is() && !xXText->getString().isEmpty();
1233 }
1234 
1235 
SetOOXML(bool bOOXML)1236 void ImplEESdrObject::SetOOXML(bool bOOXML)
1237 {
1238     mbOOXML = bOOXML;
1239 }
1240 
1241 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1242