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