build fix
[LibreOffice.git] / filter / source / msfilter / escherex.cxx
blob9dc283e501fff0cd46e432ac7677796af7a1f7ab
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "eschesdo.hxx"
21 #include <o3tl/any.hxx>
22 #include <svx/svdxcgv.hxx>
23 #include <svx/svdomedia.hxx>
24 #include <svx/xflftrit.hxx>
25 #include <filter/msfilter/escherex.hxx>
26 #include <filter/msfilter/util.hxx>
27 #include <svx/unoapi.hxx>
28 #include <svx/svdobj.hxx>
29 #include <svx/svdoashp.hxx>
30 #include <svx/svdoole2.hxx>
31 #include <svx/svdmodel.hxx>
32 #include <editeng/outlobj.hxx>
33 #include <vcl/gradient.hxx>
34 #include <vcl/graph.hxx>
35 #include <vcl/cvtgrf.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/wrkwin.hxx>
38 #include <tools/stream.hxx>
39 #include <tools/zcodec.hxx>
40 #include <svx/svdopath.hxx>
41 #include <stdlib.h>
42 #include <vcl/graphicfilter.hxx>
43 #include "svx/EnhancedCustomShapeTypeNames.hxx"
44 #include "svx/EnhancedCustomShapeGeometry.hxx"
45 #include <svx/EnhancedCustomShapeFunctionParser.hxx>
46 #include "svx/EnhancedCustomShape2d.hxx"
47 #include <com/sun/star/beans/PropertyValues.hpp>
48 #include <com/sun/star/beans/XPropertyState.hpp>
49 #include <com/sun/star/awt/GradientStyle.hpp>
50 #include <com/sun/star/awt/RasterOperation.hpp>
51 #include <com/sun/star/awt/Gradient.hpp>
52 #include <com/sun/star/drawing/LineStyle.hpp>
53 #include <com/sun/star/drawing/LineJoint.hpp>
54 #include <com/sun/star/drawing/LineCap.hpp>
55 #include <com/sun/star/drawing/FillStyle.hpp>
56 #include <com/sun/star/drawing/LineDash.hpp>
57 #include <com/sun/star/drawing/BezierPoint.hpp>
58 #include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
59 #include <com/sun/star/drawing/ConnectorType.hpp>
60 #include <com/sun/star/drawing/ConnectionType.hpp>
61 #include <com/sun/star/drawing/CircleKind.hpp>
62 #include <com/sun/star/drawing/PointSequence.hpp>
63 #include <com/sun/star/drawing/FlagSequence.hpp>
64 #include <com/sun/star/drawing/PolygonFlags.hpp>
65 #include <com/sun/star/text/WritingMode.hpp>
66 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
67 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
68 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
69 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
70 #include <com/sun/star/drawing/EnhancedCustomShapeGluePointType.hpp>
71 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
72 #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
73 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
74 #include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp>
75 #include <com/sun/star/drawing/ProjectionMode.hpp>
76 #include <com/sun/star/text/XSimpleText.hpp>
77 #include <com/sun/star/drawing/ShadeMode.hpp>
78 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
79 #include <vcl/hatch.hxx>
80 #include <com/sun/star/awt/XGraphics.hpp>
81 #include <com/sun/star/awt/FontSlant.hpp>
82 #include <com/sun/star/awt/FontWeight.hpp>
83 #include <com/sun/star/drawing/ColorMode.hpp>
84 #include <com/sun/star/drawing/Position3D.hpp>
85 #include <com/sun/star/drawing/Direction3D.hpp>
86 #include <com/sun/star/drawing/Hatch.hpp>
87 #include <com/sun/star/text/GraphicCrop.hpp>
88 #include <unotools/ucbstreamhelper.hxx>
89 #include <unotools/localfilehelper.hxx>
90 #include <comphelper/string.hxx>
91 #include <toolkit/helper/vclunohelper.hxx>
92 #include <vcl/virdev.hxx>
93 #include <rtl/crc.h>
94 #include <rtl/strbuf.hxx>
95 #include <memory>
97 using namespace ::com::sun::star;
100 EscherExContainer::EscherExContainer( SvStream& rSt, const sal_uInt16 nRecType, const sal_uInt16 nInstance ) :
101 rStrm ( rSt )
103 rStrm.WriteUInt32( ( 0xf | ( nInstance << 4 ) ) | ( nRecType << 16 ) ).WriteUInt32( 0 );
104 nContPos = rStrm.Tell();
106 EscherExContainer::~EscherExContainer()
108 sal_uInt32 nPos = rStrm.Tell();
109 sal_uInt32 nSize= nPos - nContPos;
110 if ( nSize )
112 rStrm.Seek( nContPos - 4 );
113 rStrm.WriteUInt32( nSize );
114 rStrm.Seek( nPos );
118 EscherExAtom::EscherExAtom( SvStream& rSt, const sal_uInt16 nRecType, const sal_uInt16 nInstance, const sal_uInt8 nVersion ) :
119 rStrm ( rSt )
121 rStrm.WriteUInt32( ( nVersion | ( nInstance << 4 ) ) | ( nRecType << 16 ) ).WriteUInt32( 0 );
122 nContPos = rStrm.Tell();
124 EscherExAtom::~EscherExAtom()
126 sal_uInt32 nPos = rStrm.Tell();
127 sal_uInt32 nSize= nPos - nContPos;
128 if ( nSize )
130 rStrm.Seek( nContPos - 4 );
131 rStrm.WriteUInt32( nSize );
132 rStrm.Seek( nPos );
136 EscherExClientRecord_Base::~EscherExClientRecord_Base()
140 EscherExClientAnchor_Base::~EscherExClientAnchor_Base()
144 EscherPropertyContainer::EscherPropertyContainer(
145 EscherGraphicProvider * pGraphProv, SvStream * pPiOutStrm,
146 Rectangle * pBoundRect):
147 pGraphicProvider(pGraphProv),
148 pPicOutStrm(pPiOutStrm),
149 pShapeBoundRect(pBoundRect),
150 nSortCount(0),
151 nSortBufSize(64),
152 nCountCount(0),
153 nCountSize(0),
154 pSortStruct(new EscherPropSortStruct[nSortBufSize]),
155 bHasComplexData(false)
158 EscherPropertyContainer::EscherPropertyContainer()
159 : EscherPropertyContainer(nullptr, nullptr, nullptr)
162 EscherPropertyContainer::EscherPropertyContainer(
163 EscherGraphicProvider& rGraphProv,
164 SvStream* pPiOutStrm,
165 Rectangle& rBoundRect ) :
166 EscherPropertyContainer(&rGraphProv, pPiOutStrm, &rBoundRect)
169 EscherPropertyContainer::~EscherPropertyContainer()
171 if ( bHasComplexData )
173 while ( nSortCount-- )
174 delete[] pSortStruct[ nSortCount ].pBuf;
176 delete[] pSortStruct;
179 void EscherPropertyContainer::AddOpt( sal_uInt16 nPropID, sal_uInt32 nPropValue, bool bBlib )
181 AddOpt( nPropID, bBlib, nPropValue, nullptr, 0 );
184 void EscherPropertyContainer::AddOpt( sal_uInt16 nPropID, const OUString& rString )
186 sal_Int32 j, i, nLen = rString.getLength() * 2 + 2;
187 sal_uInt8* pBuf = new sal_uInt8[ nLen ];
188 for ( j = i = 0; i < rString.getLength(); i++ )
190 sal_uInt16 nChar = (sal_uInt16)rString[ i ];
191 pBuf[ j++ ] = (sal_uInt8)nChar;
192 pBuf[ j++ ] = (sal_uInt8)( nChar >> 8 );
194 pBuf[ j++ ] = 0;
195 pBuf[ j++ ] = 0;
196 AddOpt( nPropID, true, nLen, pBuf, nLen );
199 void EscherPropertyContainer::AddOpt( sal_uInt16 nPropID, bool bBlib, sal_uInt32 nPropValue, sal_uInt8* pProp, sal_uInt32 nPropSize )
201 if ( bBlib ) // bBlib is only valid when fComplex = 0
202 nPropID |= 0x4000;
203 if ( pProp )
204 nPropID |= 0x8000; // fComplex = sal_True;
206 sal_uInt32 i;
207 for( i = 0; i < nSortCount; i++ )
209 if ( ( pSortStruct[ i ].nPropId &~0xc000 ) == ( nPropID &~0xc000 ) ) // check, whether the Property only gets replaced
211 pSortStruct[ i ].nPropId = nPropID;
212 if ( pSortStruct[ i ].pBuf )
214 nCountSize -= pSortStruct[ i ].nPropSize;
215 delete[] pSortStruct[ i ].pBuf;
217 pSortStruct[ i ].pBuf = pProp;
218 pSortStruct[ i ].nPropSize = nPropSize;
219 pSortStruct[ i ].nPropValue = nPropValue;
220 if ( pProp )
221 nCountSize += nPropSize;
222 return;
225 nCountCount++;
226 nCountSize += 6;
227 if ( nSortCount == nSortBufSize ) // increase buffer
229 nSortBufSize <<= 1;
230 EscherPropSortStruct* pTemp = new EscherPropSortStruct[ nSortBufSize ];
231 for( i = 0; i < nSortCount; i++ )
233 pTemp[ i ] = pSortStruct[ i ];
235 delete[] pSortStruct;
236 pSortStruct = pTemp;
238 pSortStruct[ nSortCount ].nPropId = nPropID; // insert property
239 pSortStruct[ nSortCount ].pBuf = pProp;
240 pSortStruct[ nSortCount ].nPropSize = nPropSize;
241 pSortStruct[ nSortCount++ ].nPropValue = nPropValue;
243 if ( pProp )
245 nCountSize += nPropSize;
246 bHasComplexData = true;
250 bool EscherPropertyContainer::GetOpt( sal_uInt16 nPropId, sal_uInt32& rPropValue ) const
252 EscherPropSortStruct aPropStruct;
254 if ( GetOpt( nPropId, aPropStruct ) )
256 rPropValue = aPropStruct.nPropValue;
257 return true;
259 return false;
262 bool EscherPropertyContainer::GetOpt( sal_uInt16 nPropId, EscherPropSortStruct& rPropValue ) const
264 for( sal_uInt32 i = 0; i < nSortCount; i++ )
266 if ( ( pSortStruct[ i ].nPropId &~0xc000 ) == ( nPropId &~0xc000 ) )
268 rPropValue = pSortStruct[ i ];
269 return true;
272 return false;
275 EscherProperties EscherPropertyContainer::GetOpts() const
277 EscherProperties aVector;
279 for ( sal_uInt32 i = 0; i < nSortCount; ++i )
280 aVector.push_back( pSortStruct[ i ] );
282 return aVector;
285 extern "C" int SAL_CALL EscherPropSortFunc( const void* p1, const void* p2 )
287 sal_Int16 nID1 = static_cast<EscherPropSortStruct const *>(p1)->nPropId &~0xc000;
288 sal_Int16 nID2 = static_cast<EscherPropSortStruct const *>(p2)->nPropId &~0xc000;
290 if( nID1 < nID2 )
291 return -1;
292 else if( nID1 > nID2 )
293 return 1;
294 else
295 return 0;
298 void EscherPropertyContainer::Commit( SvStream& rSt, sal_uInt16 nVersion, sal_uInt16 nRecType )
300 rSt.WriteUInt16( ( nCountCount << 4 ) | ( nVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nCountSize );
301 if ( nSortCount )
303 qsort( pSortStruct, nSortCount, sizeof( EscherPropSortStruct ), EscherPropSortFunc );
304 sal_uInt32 i;
306 for ( i = 0; i < nSortCount; i++ )
308 sal_uInt32 nPropValue = pSortStruct[ i ].nPropValue;
309 sal_uInt16 nPropId = pSortStruct[ i ].nPropId;
311 rSt.WriteUInt16( nPropId )
312 .WriteUInt32( nPropValue );
314 if ( bHasComplexData )
316 for ( i = 0; i < nSortCount; i++ )
318 if ( pSortStruct[ i ].pBuf )
319 rSt.WriteBytes(pSortStruct[i].pBuf, pSortStruct[i].nPropSize);
325 bool EscherPropertyContainer::IsFontWork() const
327 sal_uInt32 nTextPathFlags = 0;
328 GetOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags );
329 return ( nTextPathFlags & 0x4000 ) != 0;
332 sal_uInt32 EscherPropertyContainer::ImplGetColor( const sal_uInt32 nSOColor, bool bSwap )
334 if ( bSwap )
336 sal_uInt32 nColor = nSOColor & 0xff00; // green
337 nColor |= (sal_uInt8)( nSOColor ) << 16; // red
338 nColor |= (sal_uInt8)( nSOColor >> 16 ); // blue
339 return nColor;
341 else
342 return nSOColor & 0xffffff;
345 sal_uInt32 EscherPropertyContainer::GetGradientColor(
346 const css::awt::Gradient* pGradient,
347 sal_uInt32 nStartColor )
349 sal_uInt32 nIntensity = 100;
350 Color aColor;
352 if ( pGradient )
354 if ( nStartColor & 1 )
356 nIntensity = pGradient->StartIntensity;
357 aColor = pGradient->StartColor;
359 else
361 nIntensity = pGradient->EndIntensity;
362 aColor = pGradient->EndColor;
365 sal_uInt32 nRed = ( aColor.GetRed() * nIntensity ) / 100;
366 sal_uInt32 nGreen = ( ( aColor.GetGreen() * nIntensity ) / 100 ) << 8;
367 sal_uInt32 nBlue = ( ( aColor.GetBlue() * nIntensity ) / 100 ) << 16;
368 return nRed | nGreen | nBlue;
371 void EscherPropertyContainer::CreateGradientProperties(
372 const css::awt::Gradient & rGradient )
374 sal_uInt32 nFillType = ESCHER_FillShadeScale;
375 sal_uInt32 nAngle = 0;
376 sal_uInt32 nFillFocus = 0;
377 sal_uInt32 nFillLR = 0;
378 sal_uInt32 nFillTB = 0;
379 sal_uInt32 nFirstColor = 0;
380 bool bWriteFillTo = false;
382 switch ( rGradient.Style )
384 case css::awt::GradientStyle_LINEAR :
385 case css::awt::GradientStyle_AXIAL :
387 nFillType = ESCHER_FillShadeScale;
388 nAngle = (rGradient.Angle * 0x10000) / 10;
389 nFillFocus = (sal::static_int_cast<int>(rGradient.Style) ==
390 sal::static_int_cast<int>(GradientStyle::Linear)) ? 0 : 50;
392 break;
393 case css::awt::GradientStyle_RADIAL :
394 case css::awt::GradientStyle_ELLIPTICAL :
395 case css::awt::GradientStyle_SQUARE :
396 case css::awt::GradientStyle_RECT :
398 nFillLR = (rGradient.XOffset * 0x10000) / 100;
399 nFillTB = (rGradient.YOffset * 0x10000) / 100;
400 if ( ((nFillLR > 0) && (nFillLR < 0x10000)) || ((nFillTB > 0) && (nFillTB < 0x10000)) )
401 nFillType = ESCHER_FillShadeShape;
402 else
403 nFillType = ESCHER_FillShadeCenter;
404 nFirstColor = 1;
405 bWriteFillTo = true;
407 break;
408 case css::awt::GradientStyle_MAKE_FIXED_SIZE : break;
410 AddOpt( ESCHER_Prop_fillType, nFillType );
411 AddOpt( ESCHER_Prop_fillAngle, nAngle );
412 AddOpt( ESCHER_Prop_fillColor, GetGradientColor( &rGradient, nFirstColor ) );
413 AddOpt( ESCHER_Prop_fillBackColor, GetGradientColor( &rGradient, nFirstColor ^ 1 ) );
414 AddOpt( ESCHER_Prop_fillFocus, nFillFocus );
415 if ( bWriteFillTo )
417 AddOpt( ESCHER_Prop_fillToLeft, nFillLR );
418 AddOpt( ESCHER_Prop_fillToTop, nFillTB );
419 AddOpt( ESCHER_Prop_fillToRight, nFillLR );
420 AddOpt( ESCHER_Prop_fillToBottom, nFillTB );
424 void EscherPropertyContainer::CreateGradientProperties(
425 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet , bool bTransparentGradient)
427 css::uno::Any aAny;
428 css::awt::Gradient const * pGradient = nullptr;
430 sal_uInt32 nFillType = ESCHER_FillShadeScale;
431 sal_Int32 nAngle = 0;
432 sal_uInt32 nFillFocus = 0;
433 sal_uInt32 nFillLR = 0;
434 sal_uInt32 nFillTB = 0;
435 sal_uInt32 nFirstColor = 0;// like the control var nChgColors in import logic
436 bool bWriteFillTo = false;
438 // Transparency gradient: Means the third setting in transparency page is set
439 if (bTransparentGradient && EscherPropertyValueHelper::GetPropertyValue(
440 aAny, rXPropSet, "FillTransparenceGradient" ) )
442 pGradient = o3tl::doAccess<css::awt::Gradient>(aAny);
444 css::uno::Any aAnyTemp;
445 const rtl::OUString aPropName( "FillStyle" );
446 if ( EscherPropertyValueHelper::GetPropertyValue(
447 aAnyTemp, rXPropSet, aPropName ) )
449 css::drawing::FillStyle eFS;
450 if ( ! ( aAnyTemp >>= eFS ) )
451 eFS = css::drawing::FillStyle_SOLID;
452 // solid and transparency
453 if ( eFS == css::drawing::FillStyle_SOLID)
455 if ( EscherPropertyValueHelper::GetPropertyValue(
456 aAnyTemp, rXPropSet, "FillColor" ) )
458 const_cast<css::awt::Gradient *>(pGradient)->StartColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAnyTemp), false );
459 const_cast<css::awt::Gradient *>(pGradient)->EndColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAnyTemp), false );
462 // gradient and transparency.
463 else if( eFS == css::drawing::FillStyle_GRADIENT )
465 if ( EscherPropertyValueHelper::GetPropertyValue(
466 aAny, rXPropSet, "FillGradient" ) )
467 pGradient = o3tl::doAccess<css::awt::Gradient>(aAny);
472 // Not transparency gradient
473 else if ( EscherPropertyValueHelper::GetPropertyValue(
474 aAny, rXPropSet, "FillGradient" ) )
476 pGradient = o3tl::doAccess<css::awt::Gradient>(aAny);
479 if ( pGradient )
481 switch ( pGradient->Style )
483 case css::awt::GradientStyle_LINEAR :
484 case css::awt::GradientStyle_AXIAL :
486 nFillType = ESCHER_FillShadeScale;
487 nAngle = pGradient->Angle;
488 while ( nAngle > 0 ) nAngle -= 3600;
489 while ( nAngle <= -3600 ) nAngle += 3600;
490 // Value of the real number = Integral + (Fractional / 65536.0)
491 nAngle = ( nAngle * 0x10000) / 10;
493 nFillFocus = (pGradient->Style == css::awt::GradientStyle_LINEAR) ?
494 ( pGradient->XOffset + pGradient->YOffset )/2 : -50;
495 if( !nFillFocus )
496 nFirstColor=nFirstColor ^ 1;
497 if ( !nAngle )
498 nFirstColor=nFirstColor ^ 1;
500 break;
501 case css::awt::GradientStyle_RADIAL :
502 case css::awt::GradientStyle_ELLIPTICAL :
503 case css::awt::GradientStyle_SQUARE :
504 case css::awt::GradientStyle_RECT :
506 // according to the import logic and rect type fill** value
507 nFillLR = (pGradient->XOffset * 0x10000) / 100;
508 nFillTB = (pGradient->YOffset * 0x10000) / 100;
509 if ( ((nFillLR > 0) && (nFillLR < 0x10000)) || ((nFillTB > 0) && (nFillTB < 0x10000)) )
510 nFillType = ESCHER_FillShadeShape;
511 else
512 nFillType = ESCHER_FillShadeCenter;
513 nFirstColor = 1;
514 bWriteFillTo = true;
516 break;
517 default: break;
521 AddOpt( ESCHER_Prop_fillType, nFillType );
522 AddOpt( ESCHER_Prop_fillAngle, nAngle );
523 AddOpt( ESCHER_Prop_fillColor, GetGradientColor( pGradient, nFirstColor ) );
524 AddOpt( ESCHER_Prop_fillBackColor, GetGradientColor( pGradient, nFirstColor ^ 1 ) );
525 AddOpt( ESCHER_Prop_fillFocus, nFillFocus );
526 if ( bWriteFillTo )
528 // according to rect type fillTo** value
529 if(nFillLR)
531 AddOpt( ESCHER_Prop_fillToLeft, nFillLR );
532 AddOpt( ESCHER_Prop_fillToRight, nFillLR );
534 if(nFillTB)
536 AddOpt( ESCHER_Prop_fillToTop, nFillTB );
537 AddOpt( ESCHER_Prop_fillToBottom, nFillTB );
541 // Transparency gradient
542 if (bTransparentGradient && EscherPropertyValueHelper::GetPropertyValue(
543 aAny, rXPropSet, "FillTransparenceGradient" ) )
545 pGradient = o3tl::doAccess<css::awt::Gradient>(aAny);
546 if ( pGradient )
548 sal_uInt32 nBlue = GetGradientColor( pGradient, nFirstColor ) >> 16;
549 AddOpt( ESCHER_Prop_fillOpacity,( ( 100 - ( nBlue * 100 / 255 ) ) << 16 ) / 100 );
550 nBlue = GetGradientColor( pGradient, nFirstColor ^ 1 ) >>16 ;
551 AddOpt( ESCHER_Prop_fillBackOpacity,( ( 100 - ( nBlue * 100 / 255 ) ) << 16 )/ 100 );
556 void EscherPropertyContainer::CreateFillProperties(
557 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
558 bool bEdge , const css::uno::Reference< css::drawing::XShape > & rXShape )
560 if ( rXShape.is() )
562 SdrObject* pObj = GetSdrObjectFromXShape( rXShape );
563 if ( pObj )
565 SfxItemSet aAttr( pObj->GetMergedItemSet() );
566 // tranparency with gradient. Means the third setting in transparency page is set
567 bool bTransparentGradient = ( aAttr.GetItemState( XATTR_FILLFLOATTRANSPARENCE ) == SfxItemState::SET ) &&
568 static_cast<const XFillFloatTransparenceItem&>( aAttr.Get( XATTR_FILLFLOATTRANSPARENCE ) ).IsEnabled();
569 CreateFillProperties( rXPropSet, bEdge, bTransparentGradient );
574 void EscherPropertyContainer::CreateFillProperties(
575 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
576 bool bEdge , bool bTransparentGradient)
579 css::uno::Any aAny;
580 AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone );
581 AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle );
582 const OUString aPropName( "FillStyle" );
584 if ( EscherPropertyValueHelper::GetPropertyValue(
585 aAny, rXPropSet, aPropName ) )
587 css::drawing::FillStyle eFS;
588 if ( ! ( aAny >>= eFS ) )
589 eFS = css::drawing::FillStyle_SOLID;
590 sal_uInt32 nFillBackColor = 0;
591 switch( eFS )
593 case css::drawing::FillStyle_GRADIENT :
595 CreateGradientProperties( rXPropSet , bTransparentGradient );
596 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 );
598 break;
600 case css::drawing::FillStyle_BITMAP :
602 CreateGraphicProperties( rXPropSet, "FillBitmapURL", true );
603 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 );
604 AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor );
606 break;
607 case css::drawing::FillStyle_HATCH :
609 CreateGraphicProperties( rXPropSet, "FillHatch", true );
611 break;
612 case css::drawing::FillStyle_SOLID :
613 default:
615 if ( bTransparentGradient )
616 CreateGradientProperties( rXPropSet , bTransparentGradient );
617 else
619 css::beans::PropertyState ePropState = EscherPropertyValueHelper::GetPropertyState(
620 rXPropSet, aPropName );
621 if ( ePropState == css::beans::PropertyState_DIRECT_VALUE )
622 AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid );
624 if ( EscherPropertyValueHelper::GetPropertyValue(
625 aAny, rXPropSet, "FillColor" ) )
627 sal_uInt32 nFillColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny) );
628 nFillBackColor = nFillColor ^ 0xffffff;
629 AddOpt( ESCHER_Prop_fillColor, nFillColor );
631 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100010 );
632 AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor );
634 break;
636 case css::drawing::FillStyle_NONE :
637 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
638 break;
640 if ( eFS != css::drawing::FillStyle_NONE )
642 sal_uInt16 nTransparency = ( EscherPropertyValueHelper::GetPropertyValue(
643 aAny, rXPropSet, "FillTransparence", true ) )
644 ? *o3tl::doAccess<sal_Int16>(aAny) : 0;
645 if ( nTransparency )
646 AddOpt( ESCHER_Prop_fillOpacity, ( ( 100 - nTransparency ) << 16 ) / 100 );
649 CreateLineProperties( rXPropSet, bEdge );
652 void EscherPropertyContainer::CreateTextProperties(
653 const uno::Reference< beans::XPropertySet > & rXPropSet, sal_uInt32 nTextId,
654 const bool bIsCustomShape, const bool bIsTextFrame )
656 uno::Any aAny;
657 text::WritingMode eWM( text::WritingMode_LR_TB );
658 drawing::TextVerticalAdjust eVA( drawing::TextVerticalAdjust_TOP );
659 drawing::TextHorizontalAdjust eHA( drawing::TextHorizontalAdjust_LEFT );
661 sal_Int32 nLeft ( 0 );
662 sal_Int32 nTop ( 0 );
663 sal_Int32 nRight ( 0 );
664 sal_Int32 nBottom ( 0 );
666 // used with normal shapes:
667 bool bAutoGrowWidth ( false );
668 const bool bAutoGrowHeight ( false ); //#ii63936 not setting autogrowheight, because minframeheight would be ignored
669 // used with ashapes:
670 bool bWordWrap ( false );
671 bool bAutoGrowSize ( false );
673 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextWritingMode", true ) )
674 aAny >>= eWM;
675 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextVerticalAdjust", true ) )
676 aAny >>= eVA;
677 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextHorizontalAdjust", true ) )
678 aAny >>= eHA;
679 if ( bIsCustomShape )
681 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextWordWrap" ) )
682 aAny >>= bWordWrap;
683 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowHeight", true ) )
684 aAny >>= bAutoGrowSize;
686 else if ( bIsTextFrame )
688 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowWidth", true ) )
689 aAny >>= bAutoGrowWidth;
691 // i63936 not setting autogrowheight, because otherwise
692 // the minframeheight of the text will be ignored
694 // if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowHeight", sal_True ) )
695 // aAny >>= bAutoGrowHeight;
697 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextLeftDistance" ) )
698 aAny >>= nLeft;
699 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextUpperDistance" ) )
700 aAny >>= nTop;
701 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextRightDistance" ) )
702 aAny >>= nRight;
703 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextLowerDistance" ) )
704 aAny >>= nBottom;
706 ESCHER_AnchorText eAnchor = ESCHER_AnchorTop;
707 ESCHER_WrapMode eWrapMode = ESCHER_WrapSquare;
708 sal_uInt32 nTextAttr = 0x40004; // rotate text with shape
710 if ( eWM == text::WritingMode_TB_RL )
711 { // vertical writing
712 switch ( eHA )
714 case drawing::TextHorizontalAdjust_LEFT :
715 eAnchor = ESCHER_AnchorBottom;
716 break;
717 case drawing::TextHorizontalAdjust_CENTER :
718 eAnchor = ESCHER_AnchorMiddle;
719 break;
720 default :
721 case drawing::TextHorizontalAdjust_BLOCK :
722 case drawing::TextHorizontalAdjust_RIGHT :
723 eAnchor = ESCHER_AnchorTop;
724 break;
726 if ( eVA == drawing::TextVerticalAdjust_CENTER )
728 switch ( eAnchor )
730 case ESCHER_AnchorMiddle :
731 eAnchor = ESCHER_AnchorMiddleCentered;
732 break;
733 case ESCHER_AnchorBottom :
734 eAnchor = ESCHER_AnchorBottomCentered;
735 break;
736 default :
737 case ESCHER_AnchorTop :
738 eAnchor = ESCHER_AnchorTopCentered;
739 break;
742 if ( bIsCustomShape )
744 if ( bWordWrap )
745 eWrapMode = ESCHER_WrapSquare;
746 else
747 eWrapMode = ESCHER_WrapNone;
748 if ( bAutoGrowSize )
749 nTextAttr |= 0x20002;
751 else
753 if ( bAutoGrowHeight )
754 eWrapMode = ESCHER_WrapNone;
755 if ( bAutoGrowWidth )
756 nTextAttr |= 0x20002;
759 AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflTtoBA ); // rotate text within shape by 90
761 else
762 { // normal from left to right
763 switch ( eVA )
765 case drawing::TextVerticalAdjust_CENTER :
766 eAnchor = ESCHER_AnchorMiddle;
767 break;
769 case drawing::TextVerticalAdjust_BOTTOM :
770 eAnchor = ESCHER_AnchorBottom;
771 break;
773 default :
774 case drawing::TextVerticalAdjust_TOP :
775 eAnchor = ESCHER_AnchorTop;
776 break;
778 if ( eHA == drawing::TextHorizontalAdjust_CENTER )
780 switch( eAnchor )
782 case ESCHER_AnchorMiddle :
783 eAnchor = ESCHER_AnchorMiddleCentered;
784 break;
785 case ESCHER_AnchorBottom :
786 eAnchor = ESCHER_AnchorBottomCentered;
787 break;
788 case ESCHER_AnchorTop :
789 eAnchor = ESCHER_AnchorTopCentered;
790 break;
791 default: break;
794 if ( bIsCustomShape )
796 if ( bWordWrap )
797 eWrapMode = ESCHER_WrapSquare;
798 else
799 eWrapMode = ESCHER_WrapNone;
800 if ( bAutoGrowSize )
801 nTextAttr |= 0x20002;
803 else
805 if ( bAutoGrowWidth )
806 eWrapMode = ESCHER_WrapNone;
807 if ( bAutoGrowHeight )
808 nTextAttr |= 0x20002;
811 AddOpt( ESCHER_Prop_dxTextLeft, nLeft * 360 );
812 AddOpt( ESCHER_Prop_dxTextRight, nRight * 360 );
813 AddOpt( ESCHER_Prop_dyTextTop, nTop * 360 );
814 AddOpt( ESCHER_Prop_dyTextBottom, nBottom * 360 );
816 AddOpt( ESCHER_Prop_WrapText, eWrapMode );
817 AddOpt( ESCHER_Prop_AnchorText, eAnchor );
818 AddOpt( ESCHER_Prop_FitTextToShape, nTextAttr );
820 if ( nTextId )
821 AddOpt( ESCHER_Prop_lTxid, nTextId );
823 // n#404221: In case of rotation we need to write the txtflTextFlow
824 // attribute too.
825 // fdo#58204: not custom shapes (TODO: other cases when it doesn't work?)
826 if (bIsTextFrame && !bIsCustomShape)
828 sal_uInt16 nAngle = EscherPropertyValueHelper::GetPropertyValue(
829 aAny, rXPropSet, "RotateAngle", true ) ?
830 (sal_uInt16)( ( *o3tl::doAccess<sal_Int32>(aAny) ) + 5 ) / 10 : 0;
831 if (nAngle==900)
833 AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflBtoT );
835 if (nAngle==2700)
837 AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflTtoBA );
842 bool EscherPropertyContainer::GetLineArrow( const bool bLineStart,
843 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
844 ESCHER_LineEnd& reLineEnd, sal_Int32& rnArrowLength, sal_Int32& rnArrowWidth )
846 const OUString sLine ( bLineStart ? OUString("LineStart") : OUString("LineEnd") );
847 const OUString sLineName ( bLineStart ? OUString("LineStartName") : OUString("LineEndName") );
849 bool bIsArrow = false;
851 css::uno::Any aAny;
852 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, sLine ) )
854 tools::PolyPolygon aPolyPoly( EscherPropertyContainer::GetPolyPolygon( aAny ) );
855 if ( aPolyPoly.Count() && aPolyPoly[ 0 ].GetSize() )
857 bIsArrow = true;
859 reLineEnd = ESCHER_LineArrowEnd;
860 rnArrowLength = 1;
861 rnArrowWidth = 1;
863 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, sLineName ) )
865 OUString aArrowStartName = *o3tl::doAccess<OUString>(aAny);
866 sal_Int16 nWhich = bLineStart ? XATTR_LINESTART : XATTR_LINEEND;
868 OUString aApiName = SvxUnogetApiNameForItem(nWhich, aArrowStartName);
869 bool bIsMapped = true;
870 if ( !aApiName.isEmpty() )
873 // TODO: calculate the best option for ArrowLength and ArrowWidth
874 if ( aApiName == "Arrow concave" )
875 reLineEnd = ESCHER_LineArrowStealthEnd;
876 else if ( aApiName == "Square 45" )
877 reLineEnd = ESCHER_LineArrowDiamondEnd;
878 else if ( aApiName == "Small Arrow" )
879 reLineEnd = ESCHER_LineArrowEnd;
880 else if ( aApiName == "Dimension Lines" )
882 rnArrowLength = 0;
883 rnArrowWidth = 2;
884 reLineEnd = ESCHER_LineArrowOvalEnd;
886 else if ( aApiName == "Double Arrow" )
887 reLineEnd = ESCHER_LineArrowEnd;
888 else if ( aApiName == "Rounded short Arrow" )
889 reLineEnd = ESCHER_LineArrowEnd;
890 else if ( aApiName == "Symmetric Arrow" )
891 reLineEnd = ESCHER_LineArrowEnd;
892 else if ( aApiName == "Line Arrow" )
893 reLineEnd = ESCHER_LineArrowOpenEnd;
894 else if ( aApiName == "Rounded large Arrow" )
895 reLineEnd = ESCHER_LineArrowEnd;
896 else if ( aApiName == "Circle" )
897 reLineEnd = ESCHER_LineArrowOvalEnd;
898 else if ( aApiName == "Square" )
899 reLineEnd = ESCHER_LineArrowDiamondEnd;
900 else if ( aApiName == "Arrow" )
901 reLineEnd = ESCHER_LineArrowEnd;
902 else
903 bIsMapped = false;
906 if ( !bIsMapped && comphelper::string::getTokenCount(aArrowStartName, ' ') == 2 )
908 bool b = true;
909 OUString aArrowName( aArrowStartName.getToken( 0, ' ' ) );
910 if ( aArrowName == "msArrowEnd" )
911 reLineEnd = ESCHER_LineArrowEnd;
912 else if ( aArrowName == "msArrowOpenEnd" )
913 reLineEnd = ESCHER_LineArrowOpenEnd;
914 else if ( aArrowName == "msArrowStealthEnd" )
915 reLineEnd = ESCHER_LineArrowStealthEnd;
916 else if ( aArrowName == "msArrowDiamondEnd" )
917 reLineEnd = ESCHER_LineArrowDiamondEnd;
918 else if ( aArrowName == "msArrowOvalEnd" )
919 reLineEnd = ESCHER_LineArrowOvalEnd;
920 else
921 b = false;
923 // now we have the arrow, and try to determine the arrow size;
924 if ( b )
926 OUString aArrowSize( aArrowStartName.getToken( 1, ' ' ) );
927 sal_Int32 nArrowSize = aArrowSize.toInt32();
928 rnArrowWidth = ( nArrowSize - 1 ) / 3;
929 rnArrowLength = nArrowSize - ( rnArrowWidth * 3 ) - 1;
935 return bIsArrow;
938 void EscherPropertyContainer::CreateLineProperties(
939 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
940 bool bEdge )
942 css::uno::Any aAny;
943 sal_uInt32 nLineFlags = 0x80008;
945 ESCHER_LineEnd eLineEnd;
946 sal_Int32 nArrowLength;
947 sal_Int32 nArrowWidth;
949 bool bSwapLineEnds = false;
950 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "CircleKind", true ) )
952 css::drawing::CircleKind eCircleKind;
953 if ( aAny >>= eCircleKind )
955 if ( eCircleKind == css::drawing::CircleKind_ARC )
956 bSwapLineEnds = true;
959 if ( GetLineArrow( !bSwapLineEnds, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
961 AddOpt( ESCHER_Prop_lineStartArrowLength, nArrowLength );
962 AddOpt( ESCHER_Prop_lineStartArrowWidth, nArrowWidth );
963 AddOpt( ESCHER_Prop_lineStartArrowhead, eLineEnd );
964 nLineFlags |= 0x100010;
966 if ( GetLineArrow( bSwapLineEnds, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
968 AddOpt( ESCHER_Prop_lineEndArrowLength, nArrowLength );
969 AddOpt( ESCHER_Prop_lineEndArrowWidth, nArrowWidth );
970 AddOpt( ESCHER_Prop_lineEndArrowhead, eLineEnd );
971 nLineFlags |= 0x100010;
974 // support LineCaps
975 if(EscherPropertyValueHelper::GetPropertyValue(aAny, rXPropSet, "LineCap"))
977 css::drawing::LineCap aLineCap(css::drawing::LineCap_BUTT);
979 if(aAny >>= aLineCap)
981 switch (aLineCap)
983 default: /* css::drawing::LineCap_BUTT */
985 AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapFlat);
986 break;
988 case css::drawing::LineCap_ROUND:
990 AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapRound);
991 break;
993 case css::drawing::LineCap_SQUARE:
995 AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapSquare);
996 break;
1002 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineStyle" ) )
1004 css::drawing::LineStyle eLS;
1005 if ( aAny >>= eLS )
1007 switch ( eLS )
1009 case css::drawing::LineStyle_NONE :
1010 AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); // 80000
1011 break;
1013 case css::drawing::LineStyle_DASH :
1015 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineDash" ) )
1017 ESCHER_LineDashing eDash = ESCHER_LineSolid;
1018 auto pLineDash = o3tl::doAccess<css::drawing::LineDash>(aAny);
1019 sal_Int32 nDistance = pLineDash->Distance << 1;
1020 switch ( pLineDash->Style )
1022 case css::drawing::DashStyle_ROUND :
1023 case css::drawing::DashStyle_ROUNDRELATIVE :
1024 AddOpt( ESCHER_Prop_lineEndCapStyle, 0 ); // set Style Round
1025 break;
1026 default : break;
1028 if ( ((!(pLineDash->Dots )) || (!(pLineDash->Dashes )) ) || ( pLineDash->DotLen == pLineDash->DashLen ) )
1030 sal_Int32 nLen = pLineDash->DotLen;
1031 if ( pLineDash->Dashes )
1032 nLen = pLineDash->DashLen;
1034 if ( nLen >= nDistance )
1035 eDash = ESCHER_LineLongDashGEL;
1036 else if ( pLineDash->Dots )
1037 eDash = ESCHER_LineDotSys;
1038 else
1039 eDash = ESCHER_LineDashGEL;
1041 else // X Y
1043 if ( pLineDash->Dots != pLineDash->Dashes )
1045 if ( ( pLineDash->DashLen > nDistance ) || ( pLineDash->DotLen > nDistance ) )
1046 eDash = ESCHER_LineLongDashDotDotGEL;
1047 else
1048 eDash = ESCHER_LineDashDotDotSys;
1050 else // X Y Y
1052 if ( ( pLineDash->DashLen > nDistance ) || ( pLineDash->DotLen > nDistance ) )
1053 eDash = ESCHER_LineLongDashDotGEL;
1054 else
1055 eDash = ESCHER_LineDashDotGEL;
1059 AddOpt( ESCHER_Prop_lineDashing, eDash );
1062 SAL_FALLTHROUGH;
1063 case css::drawing::LineStyle_SOLID :
1064 default:
1066 AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags );
1068 break;
1071 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineColor" ) )
1073 sal_uInt32 nLineColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny) );
1074 AddOpt( ESCHER_Prop_lineColor, nLineColor );
1075 AddOpt( ESCHER_Prop_lineBackColor, nLineColor ^ 0xffffff );
1079 sal_uInt32 nLineSize = ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineWidth" ) )
1080 ? *o3tl::doAccess<sal_uInt32>(aAny) : 0;
1081 if ( nLineSize > 1 )
1082 AddOpt( ESCHER_Prop_lineWidth, nLineSize * 360 ); // 100TH MM -> PT , 1PT = 12700 EMU
1084 ESCHER_LineJoin eLineJoin = ESCHER_LineJoinMiter;
1085 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineJoint", true ) )
1087 css::drawing::LineJoint eLJ;
1088 if ( aAny >>= eLJ )
1090 switch ( eLJ )
1092 case css::drawing::LineJoint_NONE :
1093 case css::drawing::LineJoint_BEVEL :
1094 eLineJoin = ESCHER_LineJoinBevel;
1095 break;
1096 default:
1097 case css::drawing::LineJoint_MIDDLE :
1098 case css::drawing::LineJoint_MITER :
1099 eLineJoin = ESCHER_LineJoinMiter;
1100 break;
1101 case css::drawing::LineJoint_ROUND :
1102 eLineJoin = ESCHER_LineJoinRound;
1103 break;
1107 AddOpt( ESCHER_Prop_lineJoinStyle, eLineJoin );
1109 if ( EscherPropertyValueHelper::GetPropertyValue(
1110 aAny, rXPropSet, "LineTransparence", true ) )
1112 sal_Int16 nTransparency = 0;
1113 if ( aAny >>= nTransparency )
1114 AddOpt( ESCHER_Prop_lineOpacity, ( ( 100 - nTransparency ) << 16 ) / 100 );
1118 if ( !bEdge )
1120 AddOpt( ESCHER_Prop_fFillOK, 0x1001 );
1121 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
1125 static Size lcl_SizeToEmu(Size aPrefSize, const MapMode& aPrefMapMode)
1127 Size aRetSize;
1128 if (aPrefMapMode == MapUnit::MapPixel)
1129 aRetSize = Application::GetDefaultDevice()->PixelToLogic( aPrefSize, MapUnit::Map100thMM );
1130 else
1131 aRetSize = OutputDevice::LogicToLogic( aPrefSize, aPrefMapMode, MapUnit::Map100thMM );
1132 return aRetSize;
1135 void EscherPropertyContainer::ImplCreateGraphicAttributes( const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
1136 sal_uInt32 nBlibId, bool bCreateCroppingAttributes )
1138 css::uno::Any aAny;
1140 sal_uInt32 nPicFlags = 0;
1141 css::drawing::ColorMode eColorMode( css::drawing::ColorMode_STANDARD );
1142 sal_Int16 nLuminance = 0;
1143 sal_Int32 nContrast = 0;
1145 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "GraphicColorMode" ) )
1146 aAny >>= eColorMode;
1147 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustLuminance" ) )
1148 aAny >>= nLuminance;
1149 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustContrast" ) )
1151 sal_Int16 nC = sal_Int16();
1152 aAny >>= nC;
1153 nContrast = nC;
1156 if ( eColorMode == css::drawing::ColorMode_WATERMARK )
1158 eColorMode = css::drawing::ColorMode_STANDARD;
1159 nLuminance += 70;
1160 if ( nLuminance > 100 )
1161 nLuminance = 100;
1162 nContrast -= 70;
1163 if ( nContrast < -100 )
1164 nContrast = -100;
1166 if ( eColorMode == css::drawing::ColorMode_GREYS )
1167 nPicFlags |= 0x40004;
1168 else if ( eColorMode == css::drawing::ColorMode_MONO )
1169 nPicFlags |= 0x60006;
1171 if ( nContrast )
1173 nContrast += 100;
1174 if ( nContrast == 100)
1175 nContrast = 0x10000;
1176 else if ( nContrast < 100 )
1178 nContrast *= 0x10000;
1179 nContrast /= 100;
1181 else if ( nContrast < 200 )
1182 nContrast = ( 100 * 0x10000 ) / ( 200 - nContrast );
1183 else
1184 nContrast = 0x7fffffff;
1185 AddOpt( ESCHER_Prop_pictureContrast, nContrast );
1187 if ( nLuminance )
1188 AddOpt( ESCHER_Prop_pictureBrightness, nLuminance * 327 );
1189 if ( nPicFlags )
1190 AddOpt( ESCHER_Prop_pictureActive, nPicFlags );
1192 if ( bCreateCroppingAttributes && pGraphicProvider )
1194 Size aPrefSize;
1195 MapMode aPrefMapMode;
1196 if ( pGraphicProvider->GetPrefSize( nBlibId, aPrefSize, aPrefMapMode ) )
1198 Size aCropSize(lcl_SizeToEmu(aPrefSize, aPrefMapMode));
1199 if ( aCropSize.Width() && aCropSize.Height() )
1201 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "GraphicCrop" ) )
1203 css::text::GraphicCrop aGraphCrop;
1204 if ( aAny >>= aGraphCrop )
1206 if ( aGraphCrop.Left )
1208 sal_uInt32 nLeft = ( aGraphCrop.Left * 65536 ) / aCropSize.Width();
1209 AddOpt( ESCHER_Prop_cropFromLeft, nLeft );
1211 if ( aGraphCrop.Top )
1213 sal_uInt32 nTop = ( aGraphCrop.Top * 65536 ) / aCropSize.Height();
1214 AddOpt( ESCHER_Prop_cropFromTop, nTop );
1216 if ( aGraphCrop.Right )
1218 sal_uInt32 nRight = ( aGraphCrop.Right * 65536 ) / aCropSize.Width();
1219 AddOpt( ESCHER_Prop_cropFromRight, nRight );
1221 if ( aGraphCrop.Bottom )
1223 sal_uInt32 nBottom = ( aGraphCrop.Bottom * 65536 ) / aCropSize.Height();
1224 AddOpt( ESCHER_Prop_cropFromBottom, nBottom );
1233 void EscherPropertyContainer::CreateShapeProperties( const css::uno::Reference< css::drawing::XShape > & rXShape )
1235 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
1236 if ( aXPropSet.is() )
1238 bool bVal = false;
1239 css::uno::Any aAny;
1240 sal_uInt32 nShapeAttr = 0;
1241 if (EscherPropertyValueHelper::GetPropertyValue(aAny, aXPropSet, "Visible", true) && (aAny >>= bVal))
1243 if ( !bVal )
1244 nShapeAttr |= 0x20002; // set fHidden = true
1246 if (EscherPropertyValueHelper::GetPropertyValue(aAny, aXPropSet, "Printable", true) && (aAny >>= bVal))
1248 if ( !bVal )
1249 nShapeAttr |= 0x10000; // set fPrint = false;
1251 if ( nShapeAttr )
1252 AddOpt( ESCHER_Prop_fPrint, nShapeAttr );
1256 bool EscherPropertyContainer::CreateOLEGraphicProperties(
1257 const css::uno::Reference< css::drawing::XShape > & rXShape )
1259 bool bRetValue = false;
1261 if ( rXShape.is() )
1263 SdrObject* pSdrOLE2( GetSdrObjectFromXShape( rXShape ) ); // SJ: leaving unoapi, because currently there is
1264 if ( pSdrOLE2 && nullptr != dynamic_cast<const SdrOle2Obj* > (pSdrOLE2) ) // no access to the native graphic object
1266 const Graphic* pGraphic = static_cast<SdrOle2Obj*>(pSdrOLE2)->GetGraphic();
1267 if ( pGraphic )
1269 GraphicObject aGraphicObject( *pGraphic );
1270 bRetValue = CreateGraphicProperties( rXShape,aGraphicObject );
1274 return bRetValue;
1277 bool EscherPropertyContainer::CreateGraphicProperties( const css::uno::Reference< css::drawing::XShape > & rXShape, const GraphicObject& rGraphicObj )
1279 bool bRetValue = false;
1280 OString aUniqueId( rGraphicObj.GetUniqueID() );
1281 if ( !aUniqueId.isEmpty() )
1283 AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture );
1284 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
1286 if ( pGraphicProvider && pPicOutStrm && pShapeBoundRect && aXPropSet.is() )
1288 css::uno::Any aAny;
1289 std::unique_ptr< css::awt::Rectangle> pVisArea;
1290 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "VisibleArea" ) )
1292 pVisArea.reset(new css::awt::Rectangle);
1293 aAny >>= (*pVisArea);
1295 Rectangle aRect( Point( 0, 0 ), pShapeBoundRect->GetSize() );
1296 sal_uInt32 nBlibId = pGraphicProvider->GetBlibID( *pPicOutStrm, aUniqueId, aRect, pVisArea.get() );
1297 if ( nBlibId )
1299 AddOpt( ESCHER_Prop_pib, nBlibId, true );
1300 ImplCreateGraphicAttributes( aXPropSet, nBlibId, false );
1301 bRetValue = true;
1305 return bRetValue;
1308 bool EscherPropertyContainer::CreateMediaGraphicProperties(
1309 const css::uno::Reference< css::drawing::XShape > & rXShape )
1311 bool bRetValue = false;
1312 if ( rXShape.is() )
1314 SdrObject* pSdrMedia( GetSdrObjectFromXShape( rXShape ) ); // SJ: leaving unoapi, because currently there is
1315 if ( dynamic_cast<const SdrMediaObj* >(pSdrMedia) != nullptr ) // no access to the native graphic object
1317 GraphicObject aGraphicObject( static_cast<SdrMediaObj*>(pSdrMedia)->getSnapshot() );
1318 bRetValue = CreateGraphicProperties( rXShape, aGraphicObject );
1321 return bRetValue;
1324 bool EscherPropertyContainer::ImplCreateEmbeddedBmp( const OString& rUniqueId )
1326 if( !rUniqueId.isEmpty() )
1328 EscherGraphicProvider aProvider;
1329 SvMemoryStream aMemStrm;
1330 Rectangle aRect;
1331 if ( aProvider.GetBlibID( aMemStrm, rUniqueId, aRect ) )
1333 // grab BLIP from stream and insert directly as complex property
1334 // ownership of stream memory goes to complex property
1335 aMemStrm.ObjectOwnsMemory( false );
1336 sal_uInt8 const * pBuf = static_cast<sal_uInt8 const *>(aMemStrm.GetData());
1337 sal_uInt32 nSize = aMemStrm.Seek( STREAM_SEEK_TO_END );
1338 AddOpt( ESCHER_Prop_fillBlip, true, nSize, const_cast<sal_uInt8 *>(pBuf), nSize );
1339 return true;
1342 return false;
1345 void EscherPropertyContainer::CreateEmbeddedBitmapProperties(
1346 const OUString& rBitmapUrl, css::drawing::BitmapMode eBitmapMode )
1348 OUString aVndUrl( "vnd.sun.star.GraphicObject:" );
1349 sal_Int32 nIndex = rBitmapUrl.indexOf( aVndUrl );
1350 if( nIndex != -1 )
1352 nIndex += aVndUrl.getLength();
1353 if( rBitmapUrl.getLength() > nIndex )
1355 OString aUniqueId(OUStringToOString(rBitmapUrl.copy(nIndex, rBitmapUrl.getLength() - nIndex), RTL_TEXTENCODING_UTF8));
1356 bool bRetValue = ImplCreateEmbeddedBmp( aUniqueId );
1357 if( bRetValue )
1359 // bitmap mode property
1360 bool bRepeat = eBitmapMode == css::drawing::BitmapMode_REPEAT;
1361 AddOpt( ESCHER_Prop_fillType, bRepeat ? ESCHER_FillTexture : ESCHER_FillPicture );
1368 namespace {
1370 GraphicObject lclDrawHatch( const css::drawing::Hatch& rHatch, const Color& rBackColor, bool bFillBackground, const Rectangle& rRect )
1372 // #i121183# For hatch, do no longer create a bitmap with the fixed size of 28x28 pixels. Also
1373 // do not create a bitmap in page size, that would explode file sizes (and have no good quality).
1374 // Better use a MetaFile graphic in page size; thus we have good quality due to vector format and
1375 // no bit file sizes.
1376 ScopedVclPtrInstance< VirtualDevice > pVDev;
1377 GDIMetaFile aMtf;
1379 pVDev->SetOutputSizePixel(Size(2, 2));
1380 pVDev->EnableOutput(false);
1381 pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
1382 aMtf.Clear();
1383 aMtf.Record(pVDev);
1384 pVDev->SetLineColor();
1385 pVDev->SetFillColor(bFillBackground ? rBackColor : Color(COL_TRANSPARENT));
1386 pVDev->DrawRect(rRect);
1387 pVDev->DrawHatch(tools::PolyPolygon(rRect), Hatch((HatchStyle)rHatch.Style, Color(rHatch.Color), rHatch.Distance, (sal_uInt16)rHatch.Angle));
1388 aMtf.Stop();
1389 aMtf.WindStart();
1390 aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
1391 aMtf.SetPrefSize(rRect.GetSize());
1393 return GraphicObject(Graphic(aMtf));
1396 } // namespace
1399 void EscherPropertyContainer::CreateEmbeddedHatchProperties( const css::drawing::Hatch& rHatch, const Color& rBackColor, bool bFillBackground )
1401 const Rectangle aRect(pShapeBoundRect ? *pShapeBoundRect : Rectangle(Point(0,0), Size(28000, 21000)));
1402 GraphicObject aGraphicObject = lclDrawHatch( rHatch, rBackColor, bFillBackground, aRect );
1403 OString aUniqueId = aGraphicObject.GetUniqueID();
1404 bool bRetValue = ImplCreateEmbeddedBmp( aUniqueId );
1405 if ( bRetValue )
1406 AddOpt( ESCHER_Prop_fillType, ESCHER_FillTexture );
1410 bool EscherPropertyContainer::CreateGraphicProperties(
1411 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
1412 const OUString& rSource, const bool bCreateFillBitmap, const bool bCreateCroppingAttributes,
1413 const bool bFillBitmapModeAllowed, const bool bOOxmlExport )
1415 bool bRetValue = false;
1416 bool bCreateFillStyles = false;
1418 std::unique_ptr<GraphicAttr> pGraphicAttr;
1419 GraphicObject aGraphicObject;
1420 OUString aGraphicUrl;
1421 OString aUniqueId;
1423 css::drawing::BitmapMode eBitmapMode( css::drawing::BitmapMode_NO_REPEAT );
1424 css::uno::Any aAny;
1426 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, rSource ) )
1428 bool bMirrored = false;
1429 bool bRotate = true;
1430 bool bIsGraphicMtf = false;
1431 sal_Int16 nTransparency(0);
1432 sal_Int16 nRed(0);
1433 sal_Int16 nGreen(0);
1434 sal_Int16 nBlue(0);
1435 double fGamma(1.0);
1437 sal_uInt16 nAngle = 0;
1438 if ( rSource == "MetaFile" )
1440 auto & aSeq = *o3tl::doAccess<css::uno::Sequence<sal_Int8>>(aAny);
1441 const sal_Int8* pAry = aSeq.getConstArray();
1442 sal_uInt32 nAryLen = aSeq.getLength();
1444 // the metafile is already rotated
1445 bRotate = false;
1447 if ( pAry && nAryLen )
1449 Graphic aGraphic;
1450 SvMemoryStream aTemp( const_cast<sal_Int8 *>(pAry), nAryLen, StreamMode::READ );
1451 sal_uInt32 nErrCode = GraphicConverter::Import( aTemp, aGraphic, ConvertDataFormat::WMF );
1452 if ( nErrCode == ERRCODE_NONE )
1454 aGraphicObject = aGraphic;
1455 aUniqueId = aGraphicObject.GetUniqueID();
1456 bIsGraphicMtf = aGraphicObject.GetType() == GraphicType::GdiMetafile;
1460 else if ( rSource == "Bitmap" )
1462 css::uno::Reference< css::awt::XBitmap >xBitmap(
1463 aAny, css::uno::UNO_QUERY);
1464 if (xBitmap.is())
1466 css::uno::Reference< css::awt::XBitmap > xBmp;
1467 if ( aAny >>= xBmp )
1469 BitmapEx aBitmapEx( VCLUnoHelper::GetBitmap( xBmp ) );
1470 Graphic aGraphic( aBitmapEx );
1471 aGraphicObject = aGraphic;
1472 aUniqueId = aGraphicObject.GetUniqueID();
1473 bIsGraphicMtf = aGraphicObject.GetType() == GraphicType::GdiMetafile;
1477 else if ( rSource == "FillBitmapURL" )
1479 aGraphicUrl = *o3tl::doAccess<OUString>(aAny);
1481 else if ( rSource == "GraphicURL" )
1483 aGraphicUrl = *o3tl::doAccess<OUString>(aAny);
1484 bCreateFillStyles = true;
1486 else if ( rSource == "FillHatch" )
1488 css::drawing::Hatch aHatch;
1489 if ( aAny >>= aHatch )
1491 Color aBackColor;
1492 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillColor" ) )
1494 aBackColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny), false );
1496 bool bFillBackground = false;
1497 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBackground", true ) )
1499 aAny >>= bFillBackground;
1502 const Rectangle aRect(Point(0, 0), pShapeBoundRect ? pShapeBoundRect->GetSize() : Size(28000, 21000));
1503 aGraphicObject = lclDrawHatch( aHatch, aBackColor, bFillBackground, aRect );
1504 aUniqueId = aGraphicObject.GetUniqueID();
1505 eBitmapMode = css::drawing::BitmapMode_REPEAT;
1506 bIsGraphicMtf = aGraphicObject.GetType() == GraphicType::GdiMetafile;
1510 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "IsMirrored", true ) )
1511 aAny >>= bMirrored;
1513 // #121074# transparency of graphic is not supported in MS formats, get and apply it
1514 // in the GetTransformedGraphic call in GetBlibID
1515 if(EscherPropertyValueHelper::GetPropertyValue(aAny, rXPropSet, "Transparency"))
1517 aAny >>= nTransparency;
1520 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustRed" ) )
1522 aAny >>= nRed;
1525 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustGreen" ) )
1527 aAny >>= nGreen;
1530 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustBlue" ) )
1532 aAny >>= nBlue;
1535 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "Gamma" ) )
1537 aAny >>= fGamma;
1540 if ( bCreateFillBitmap && bFillBitmapModeAllowed )
1542 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapMode", true ) )
1543 aAny >>= eBitmapMode;
1545 else
1547 nAngle = bRotate && EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "RotateAngle", true )
1548 ? (sal_uInt16)( ( *o3tl::doAccess<sal_Int32>(aAny) ) + 5 ) / 10
1549 : 0;
1552 if ( aGraphicUrl.getLength() )
1554 OUString aVndUrl( "vnd.sun.star.GraphicObject:" );
1555 sal_Int32 nIndex = aGraphicUrl.indexOf( aVndUrl );
1556 if ( nIndex != -1 )
1558 nIndex = nIndex + aVndUrl.getLength();
1559 if ( aGraphicUrl.getLength() > nIndex )
1560 aUniqueId = OUStringToOString(aGraphicUrl.copy(nIndex, aGraphicUrl.getLength() - nIndex), RTL_TEXTENCODING_UTF8);
1562 else
1564 // externally, linked graphic? convert to embedded
1565 // one, if transformations are needed. this is because
1566 // everything < msoxp cannot even handle rotated
1567 // bitmaps.
1568 // And check whether the graphic link target is
1569 // actually supported by mso.
1570 INetURLObject aTmp( aGraphicUrl );
1571 GraphicDescriptor aDescriptor(aTmp);
1572 aDescriptor.Detect();
1573 const GraphicFileFormat nFormat = aDescriptor.GetFileFormat();
1575 // can MSO handle it?
1576 if ( bMirrored || nAngle || nTransparency || nRed || nGreen || nBlue || (1.0 != fGamma) ||
1577 (nFormat != GraphicFileFormat::BMP &&
1578 nFormat != GraphicFileFormat::GIF &&
1579 nFormat != GraphicFileFormat::JPG &&
1580 nFormat != GraphicFileFormat::PNG &&
1581 nFormat != GraphicFileFormat::TIF &&
1582 nFormat != GraphicFileFormat::PCT &&
1583 nFormat != GraphicFileFormat::WMF &&
1584 nFormat != GraphicFileFormat::EMF) )
1586 std::unique_ptr<SvStream> pIn(::utl::UcbStreamHelper::CreateStream(
1587 aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ));
1588 if ( pIn )
1590 Graphic aGraphic;
1591 sal_uInt32 nErrCode = GraphicConverter::Import( *pIn, aGraphic );
1593 if ( nErrCode == ERRCODE_NONE )
1595 // no
1596 aGraphicObject = aGraphic;
1597 aUniqueId = aGraphicObject.GetUniqueID();
1599 // else: simply keep the graphic link
1602 if ( aUniqueId.isEmpty() )
1604 if ( pGraphicProvider )
1606 const OUString& rBaseURI( pGraphicProvider->GetBaseURI() );
1607 INetURLObject aBaseURI( rBaseURI );
1608 if( aBaseURI.GetProtocol() == aTmp.GetProtocol() )
1610 OUString aRelUrl( INetURLObject::GetRelURL( rBaseURI, aGraphicUrl ) );
1611 if ( !aRelUrl.isEmpty() )
1612 aGraphicUrl = aRelUrl;
1619 if ( aGraphicUrl.getLength() || !aUniqueId.isEmpty() )
1621 if(bMirrored || nTransparency || nRed || nGreen || nBlue || (1.0 != fGamma))
1623 pGraphicAttr.reset(new GraphicAttr);
1625 if(bMirrored)
1627 pGraphicAttr->SetMirrorFlags(BmpMirrorFlags::Horizontal);
1630 if(nTransparency)
1632 pGraphicAttr->SetTransparency((nTransparency * 255) / 100);
1635 if(nRed)
1637 pGraphicAttr->SetChannelR(nRed);
1640 if(nGreen)
1642 pGraphicAttr->SetChannelG(nGreen);
1645 if(nBlue)
1647 pGraphicAttr->SetChannelB(nBlue);
1650 if(1.0 != fGamma)
1652 pGraphicAttr->SetGamma(fGamma);
1656 if(nAngle && bIsGraphicMtf)
1658 AddOpt( ESCHER_Prop_Rotation, ( ( ((sal_Int32)nAngle << 16 ) / 10 ) + 0x8000 ) &~ 0xffff );
1661 if ( eBitmapMode == css::drawing::BitmapMode_REPEAT )
1663 sal_Int32 nSizeX = 0,nSizeY = 0,nOffsetX = 0,nOffsetY = 0,nPosOffsetX = 0,nPosOffsetY = 0;
1664 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapSizeX", true ) )
1666 aAny >>= nSizeX;
1668 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapSizeY", true ) )
1670 aAny >>= nSizeY;
1672 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapOffsetX", true ) )
1674 aAny >>= nOffsetX;
1676 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapOffsetY", true ) )
1678 aAny >>= nOffsetY;
1680 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapPositionOffsetX", true ) )
1682 aAny >>= nPosOffsetX;
1684 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapPositionOffsetY", true ) )
1686 aAny >>= nPosOffsetY;
1688 if(nSizeX == -100 && nSizeY == -100 && nOffsetX == 0 && nOffsetY == 0 && nPosOffsetX == 0 && nPosOffsetY == 0)
1689 AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture );
1690 else
1691 AddOpt( ESCHER_Prop_fillType, ESCHER_FillTexture );
1693 else
1694 AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture );
1696 if ( !aUniqueId.isEmpty() )
1698 // write out embedded graphic
1699 if ( pGraphicProvider && pPicOutStrm && pShapeBoundRect )
1701 Rectangle aRect( Point( 0, 0 ), pShapeBoundRect->GetSize() );
1702 const sal_uInt32 nBlibId(pGraphicProvider->GetBlibID(*pPicOutStrm, aUniqueId, aRect, nullptr, pGraphicAttr.get()));
1704 if(nBlibId)
1706 if(bCreateFillBitmap)
1708 AddOpt(ESCHER_Prop_fillBlip, nBlibId, true);
1710 else
1712 AddOpt( ESCHER_Prop_pib, nBlibId, true );
1713 ImplCreateGraphicAttributes( rXPropSet, nBlibId, bCreateCroppingAttributes );
1716 bRetValue = true;
1719 else
1721 EscherGraphicProvider aProvider;
1722 SvMemoryStream aMemStrm;
1723 Rectangle aRect;
1725 if ( aProvider.GetBlibID( aMemStrm, aUniqueId, aRect, nullptr, pGraphicAttr.get(), bOOxmlExport ) )
1727 // grab BLIP from stream and insert directly as complex property
1728 // ownership of stream memory goes to complex property
1729 aMemStrm.ObjectOwnsMemory( false );
1730 sal_uInt8 const * pBuf = static_cast<sal_uInt8 const *>(aMemStrm.GetData());
1731 sal_uInt32 nSize = aMemStrm.Seek( STREAM_SEEK_TO_END );
1732 AddOpt( ESCHER_Prop_fillBlip, true, nSize, const_cast<sal_uInt8 *>(pBuf), nSize );
1733 bRetValue = true;
1737 // write out link to graphic
1738 else
1740 OSL_ASSERT(aGraphicUrl.getLength());
1742 AddOpt( ESCHER_Prop_pibName, aGraphicUrl );
1743 sal_uInt32 nPibFlags=0;
1744 GetOpt( ESCHER_Prop_pibFlags, nPibFlags );
1745 AddOpt( ESCHER_Prop_pibFlags,
1746 ESCHER_BlipFlagLinkToFile|ESCHER_BlipFlagFile|ESCHER_BlipFlagDoNotSave | nPibFlags );
1750 pGraphicAttr.reset();
1751 if ( bCreateFillStyles )
1752 CreateFillProperties( rXPropSet, true );
1754 return bRetValue;
1757 tools::PolyPolygon EscherPropertyContainer::GetPolyPolygon( const css::uno::Reference< css::drawing::XShape > & rXShape )
1759 tools::PolyPolygon aRetPolyPoly;
1760 css::uno::Reference< css::beans::XPropertySet > aXPropSet;
1761 css::uno::Any aAny( rXShape->queryInterface(
1762 cppu::UnoType<css::beans::XPropertySet>::get()));
1764 OUString sPolyPolygonBezier ( "PolyPolygonBezier" );
1765 OUString sPolyPolygon ( "PolyPolygon" );
1766 OUString sPolygon ( "Polygon" );
1768 if ( aAny >>= aXPropSet )
1770 bool bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, sPolyPolygonBezier, true );
1771 if ( !bHasProperty )
1772 bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, sPolyPolygon, true );
1773 if ( !bHasProperty )
1774 bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, sPolygon, true );
1775 if ( bHasProperty )
1776 aRetPolyPoly = GetPolyPolygon( aAny );
1778 return aRetPolyPoly;
1781 tools::PolyPolygon EscherPropertyContainer::GetPolyPolygon( const css::uno::Any& rAny )
1783 bool bNoError = true;
1785 tools::Polygon aPolygon;
1786 tools::PolyPolygon aPolyPolygon;
1788 if ( rAny.getValueType() == cppu::UnoType<css::drawing::PolyPolygonBezierCoords>::get())
1790 auto pSourcePolyPolygon
1791 = o3tl::doAccess<css::drawing::PolyPolygonBezierCoords>(rAny);
1792 sal_uInt16 nOuterSequenceCount = (sal_uInt16)pSourcePolyPolygon->Coordinates.getLength();
1794 // get pointer of inner sequences
1795 css::drawing::PointSequence const * pOuterSequence = pSourcePolyPolygon->Coordinates.getConstArray();
1796 css::drawing::FlagSequence const * pOuterFlags = pSourcePolyPolygon->Flags.getConstArray();
1798 bNoError = pOuterSequence && pOuterFlags;
1799 if ( bNoError )
1801 sal_uInt16 a, b, nInnerSequenceCount;
1802 css::awt::Point const * pArray;
1804 // this will be a polygon set
1805 for ( a = 0; a < nOuterSequenceCount; a++ )
1807 css::drawing::PointSequence const * pInnerSequence = pOuterSequence++;
1808 css::drawing::FlagSequence const * pInnerFlags = pOuterFlags++;
1810 bNoError = pInnerSequence && pInnerFlags;
1811 if ( bNoError )
1813 // get pointer to arrays
1814 pArray = pInnerSequence->getConstArray();
1815 css::drawing::PolygonFlags const * pFlags = pInnerFlags->getConstArray();
1817 if ( pArray && pFlags )
1819 nInnerSequenceCount = (sal_uInt16)pInnerSequence->getLength();
1820 aPolygon = tools::Polygon( nInnerSequenceCount );
1821 for( b = 0; b < nInnerSequenceCount; b++)
1823 css::drawing::PolygonFlags ePolyFlags = *pFlags++;
1824 css::awt::Point aPoint( *(pArray++) );
1825 aPolygon[ b ] = Point( aPoint.X, aPoint.Y );
1826 aPolygon.SetFlags( b, static_cast<PolyFlags>(ePolyFlags) );
1828 if ( ePolyFlags == css::drawing::PolygonFlags_CONTROL )
1829 continue;
1831 aPolyPolygon.Insert( aPolygon );
1837 else if ( auto pSourcePolyPolygon = o3tl::tryAccess<css::drawing::PointSequenceSequence>(rAny) )
1839 sal_uInt16 nOuterSequenceCount = (sal_uInt16)pSourcePolyPolygon->getLength();
1841 // get pointer to inner sequences
1842 css::drawing::PointSequence const * pOuterSequence = pSourcePolyPolygon->getConstArray();
1843 bNoError = pOuterSequence != nullptr;
1844 if ( bNoError )
1846 sal_uInt16 a, b, nInnerSequenceCount;
1848 // this will be a polygon set
1849 for( a = 0; a < nOuterSequenceCount; a++ )
1851 css::drawing::PointSequence const * pInnerSequence = pOuterSequence++;
1852 bNoError = pInnerSequence != nullptr;
1853 if ( bNoError )
1855 // get pointer to arrays
1856 css::awt::Point const * pArray =
1857 pInnerSequence->getConstArray();
1858 if ( pArray != nullptr )
1860 nInnerSequenceCount = (sal_uInt16)pInnerSequence->getLength();
1861 aPolygon = tools::Polygon( nInnerSequenceCount );
1862 for( b = 0; b < nInnerSequenceCount; b++)
1864 aPolygon[ b ] = Point( pArray->X, pArray->Y );
1865 pArray++;
1867 aPolyPolygon.Insert( aPolygon );
1873 else if ( auto pInnerSequence = o3tl::tryAccess<css::drawing::PointSequence>(rAny) )
1875 bNoError = pInnerSequence != nullptr;
1876 if ( bNoError )
1878 sal_uInt16 a, nInnerSequenceCount;
1880 // get pointer to arrays
1881 css::awt::Point const * pArray = pInnerSequence->getConstArray();
1882 if ( pArray != nullptr )
1884 nInnerSequenceCount = (sal_uInt16)pInnerSequence->getLength();
1885 aPolygon = tools::Polygon( nInnerSequenceCount );
1886 for( a = 0; a < nInnerSequenceCount; a++)
1888 aPolygon[ a ] = Point( pArray->X, pArray->Y );
1889 pArray++;
1891 aPolyPolygon.Insert( aPolygon );
1895 return aPolyPolygon;
1898 bool EscherPropertyContainer::CreatePolygonProperties(
1899 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
1900 sal_uInt32 nFlags,
1901 bool bBezier,
1902 css::awt::Rectangle& rGeoRect,
1903 tools::Polygon* pPolygon )
1905 bool bRetValue = true;
1906 bool bLine = ( nFlags & ESCHER_CREATEPOLYGON_LINE ) != 0;
1908 tools::PolyPolygon aPolyPolygon;
1910 if ( pPolygon )
1911 aPolyPolygon.Insert( *pPolygon );
1912 else
1914 css::uno::Any aAny;
1915 bRetValue = EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet,
1916 ( bBezier ) ? OUString("PolyPolygonBezier") : OUString("PolyPolygon"), true );
1917 if ( bRetValue )
1919 aPolyPolygon = GetPolyPolygon( aAny );
1920 bRetValue = aPolyPolygon.Count() != 0;
1923 if ( bRetValue )
1925 if ( bLine )
1927 if ( ( aPolyPolygon.Count() == 1 ) && ( aPolyPolygon[ 0 ].GetSize() == 2 ) )
1929 const tools::Polygon& rPoly = aPolyPolygon[ 0 ];
1930 rGeoRect = css::awt::Rectangle(
1931 rPoly[ 0 ].X(),
1932 rPoly[ 0 ].Y(),
1933 rPoly[ 1 ].X() - rPoly[ 0 ].X(),
1934 rPoly[ 1 ].Y() - rPoly[ 0 ].Y() );
1936 else
1937 bRetValue = false;
1939 else
1941 tools::Polygon aPolygon;
1943 sal_uInt16 nPolyCount = aPolyPolygon.Count();
1944 sal_uInt32 nTotalPoints(0), nTotalBezPoints(0);
1945 Rectangle aRect( aPolyPolygon.GetBoundRect() );
1946 rGeoRect = css::awt::Rectangle( aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight() );
1948 for (sal_uInt16 i = 0; i < nPolyCount; ++i)
1950 sal_uInt16 k = aPolyPolygon[ i ].GetSize();
1951 nTotalPoints += k;
1952 for (sal_uInt16 j = 0; j < k; ++j)
1954 if ( aPolyPolygon[ i ].GetFlags( j ) != PolyFlags::Control )
1955 nTotalBezPoints++;
1958 sal_uInt32 nVerticesBufSize = ( nTotalPoints << 2 ) + 6;
1959 sal_uInt8* pVerticesBuf = new sal_uInt8[ nVerticesBufSize ];
1962 sal_uInt32 nSegmentBufSize = ( ( nTotalBezPoints << 2 ) + 8 );
1963 if ( nPolyCount > 1 )
1964 nSegmentBufSize += ( nPolyCount << 1 );
1965 sal_uInt8* pSegmentBuf = new sal_uInt8[ nSegmentBufSize ];
1967 sal_uInt8* pPtr = pVerticesBuf;
1968 *pPtr++ = (sal_uInt8)( nTotalPoints ); // Little endian
1969 *pPtr++ = (sal_uInt8)( nTotalPoints >> 8 );
1970 *pPtr++ = (sal_uInt8)( nTotalPoints );
1971 *pPtr++ = (sal_uInt8)( nTotalPoints >> 8 );
1972 *pPtr++ = (sal_uInt8)0xf0;
1973 *pPtr++ = (sal_uInt8)0xff;
1975 for (sal_uInt16 j = 0; j < nPolyCount; ++j)
1977 aPolygon = aPolyPolygon[ j ];
1978 sal_uInt16 nPoints = aPolygon.GetSize();
1979 for (sal_uInt16 i = 0; i < nPoints; ++i) // write points from polygon to buffer
1981 Point aPoint = aPolygon[ i ];
1982 aPoint.X() -= rGeoRect.X;
1983 aPoint.Y() -= rGeoRect.Y;
1985 *pPtr++ = (sal_uInt8)( aPoint.X() );
1986 *pPtr++ = (sal_uInt8)( aPoint.X() >> 8 );
1987 *pPtr++ = (sal_uInt8)( aPoint.Y() );
1988 *pPtr++ = (sal_uInt8)( aPoint.Y() >> 8 );
1992 pPtr = pSegmentBuf;
1993 *pPtr++ = (sal_uInt8)( ( nSegmentBufSize - 6 ) >> 1 );
1994 *pPtr++ = (sal_uInt8)( ( nSegmentBufSize - 6 ) >> 9 );
1995 *pPtr++ = (sal_uInt8)( ( nSegmentBufSize - 6 ) >> 1 );
1996 *pPtr++ = (sal_uInt8)( ( nSegmentBufSize - 6 ) >> 9 );
1997 *pPtr++ = (sal_uInt8)2;
1998 *pPtr++ = (sal_uInt8)0;
2000 for (sal_uInt16 j = 0; j < nPolyCount; ++j)
2002 *pPtr++ = 0x0; // Polygon start
2003 *pPtr++ = 0x40;
2004 aPolygon = aPolyPolygon[ j ];
2005 sal_uInt16 nPoints = aPolygon.GetSize();
2006 for (sal_uInt16 i = 0; i < nPoints; ++i) // write Polyflags to Buffer
2008 *pPtr++ = 0;
2009 if ( bBezier )
2010 *pPtr++ = 0xb3;
2011 else
2012 *pPtr++ = 0xac;
2013 if ( ( i + 1 ) != nPoints )
2015 *pPtr++ = 1;
2016 if ( aPolygon.GetFlags( i + 1 ) == PolyFlags::Control )
2018 *pPtr++ = 0x20;
2019 i += 2;
2021 else
2022 *pPtr++ = 0;
2025 if ( nPolyCount > 1 )
2027 *pPtr++ = 1; // end of polygon
2028 *pPtr++ = 0x60;
2031 *pPtr++ = 0;
2032 *pPtr++ = 0x80;
2034 AddOpt( ESCHER_Prop_geoRight, rGeoRect.Width );
2035 AddOpt( ESCHER_Prop_geoBottom, rGeoRect.Height );
2037 AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex );
2038 AddOpt( ESCHER_Prop_pVertices, true, nVerticesBufSize - 6, pVerticesBuf, nVerticesBufSize );
2039 AddOpt( ESCHER_Prop_pSegmentInfo, true, nSegmentBufSize, pSegmentBuf, nSegmentBufSize );
2042 return bRetValue;
2047 in MS,the connector including 9 types :
2048 "straightConnector1",
2049 "bentConnector2","bentConnector3","bentConnector4","bentConnector5"
2050 "curvedConnector2","curvedConnector3","curvedConnector4","curvedConnector5"
2051 in AOO,including 4 types:"standard","lines","line","curve"
2052 when save as MS file, the connector must be convert to corresponding type.
2053 "line" and "lines" <-> "straightConnector1"
2054 "standard" <-> "bentConnector2-5"
2055 "curve" <-> "curvedConnector2-5"
2057 sal_Int32 lcl_GetAdjustValueCount( const XPolygon& rPoly )
2059 int nRet = 0;
2060 switch ( rPoly.GetSize() )
2062 case 2 :
2063 case 3:
2064 nRet = 0;
2065 break;
2066 case 4:
2067 nRet = 1;
2068 break;
2069 case 5:
2070 nRet = 2;
2071 break;
2072 default:
2073 if ( rPoly.GetSize()>=6 )
2074 nRet = 3;
2075 break;
2077 return nRet;
2080 // Adjust value decide the position which connector should turn a corner
2081 sal_Int32 lcl_GetConnectorAdjustValue ( const XPolygon& rPoly, sal_uInt16 nIndex )
2083 sal_uInt16 k = rPoly.GetSize();
2084 OSL_ASSERT ( k >= ( 3 + nIndex ) );
2086 Point aPt;
2087 Point aStart = rPoly[0];
2088 Point aEnd = rPoly[k-1];
2089 if ( aEnd.Y() == aStart.Y() )
2090 aEnd.Y() = aStart.Y() +4;
2091 if ( aEnd.X() == aStart.X() )
2092 aEnd.X() = aStart.X() +4;
2094 bool bVertical = ( rPoly[1].X()-aStart.X() ) == 0 ;
2095 // vertical and horizon alternate
2096 if ( nIndex%2 == 1 ) bVertical = !bVertical;
2097 aPt = rPoly[ nIndex + 1];
2099 sal_Int32 nAdjustValue;
2100 if ( bVertical )
2101 nAdjustValue = ( aPt.Y()-aStart.Y())* 21600 /(aEnd.Y()-aStart.Y());
2102 else
2103 nAdjustValue = ( aPt.X()-aStart.X() )* 21600 /(aEnd.X()-aStart.X());
2105 return nAdjustValue;
2109 void lcl_Rotate(sal_Int32 nAngle, Point center, Point& pt)
2111 while ( nAngle<0)
2112 nAngle +=36000;
2113 while (nAngle>=36000)
2114 nAngle -=36000;
2116 int cs, sn;
2117 switch (nAngle)
2119 case 0:
2120 cs =1;
2121 sn =0;
2122 break;
2123 case 9000:
2124 cs =0;
2125 sn =1;
2126 break;
2127 case 18000:
2128 cs = -1;
2129 sn = 0;
2130 break;
2131 case 27000:
2132 cs = 0;
2133 sn = -1;
2134 break;
2135 default:
2136 return;
2137 break;
2139 sal_Int32 x0 =pt.X()-center.X();
2140 sal_Int32 y0 =pt.Y()-center.Y();
2141 pt.X()=center.X()+ x0*cs-y0*sn;
2142 pt.Y()=center.Y()+ y0*cs+x0*sn;
2145 FlipV defines that the shape will be flipped vertically about the center of its bounding box.
2146 Generally, draw the connector from top to bottom, from left to right when meet the adjust value,
2147 but when (X1>X2 or Y1>Y2),the draw director must be reverse, FlipV or FlipH should be set to true.
2149 bool lcl_GetAngle(tools::Polygon &rPoly,sal_uInt16& rShapeFlags,sal_Int32& nAngle )
2151 Point aStart = rPoly[0];
2152 Point aEnd = rPoly[rPoly.GetSize()-1];
2153 nAngle = ( rPoly[1].X() == aStart.X() ) ? 9000: 0 ;
2154 Point p1(aStart.X(),aStart.Y());
2155 Point p2(aEnd.X(),aEnd.Y());
2156 if ( nAngle )
2158 Point center((aEnd.X()+aStart.X())>>1,(aEnd.Y()+aStart.Y())>>1);
2159 lcl_Rotate(-nAngle, center,p1);
2160 lcl_Rotate(-nAngle, center,p2);
2162 if ( p1.X() > p2.X() )
2164 if ( nAngle )
2165 rShapeFlags |= SHAPEFLAG_FLIPV;
2166 else
2167 rShapeFlags |= SHAPEFLAG_FLIPH;
2170 if ( p1.Y() > p2.Y() )
2172 if ( nAngle )
2173 rShapeFlags |= SHAPEFLAG_FLIPH;
2174 else
2175 rShapeFlags |= SHAPEFLAG_FLIPV;
2178 if ( (rShapeFlags&SHAPEFLAG_FLIPH) && (rShapeFlags&SHAPEFLAG_FLIPV) )
2180 rShapeFlags &= ~( SHAPEFLAG_FLIPH | SHAPEFLAG_FLIPV );
2181 nAngle +=18000;
2184 if ( nAngle )
2186 // Set angle properties
2187 nAngle *= 655;
2188 nAngle += 0x8000;
2189 nAngle &=~0xffff; // round nAngle to whole number of degrees
2190 return true;
2192 return false;
2194 bool EscherPropertyContainer::CreateConnectorProperties(
2195 const css::uno::Reference< css::drawing::XShape > & rXShape,
2196 EscherSolverContainer& rSolverContainer, css::awt::Rectangle& rGeoRect,
2197 sal_uInt16& rShapeType, sal_uInt16& rShapeFlags )
2199 bool bRetValue = false;
2200 rShapeType = rShapeFlags = 0;
2202 if ( rXShape.is() )
2204 css::awt::Point aStartPoint, aEndPoint;
2205 css::uno::Reference< css::beans::XPropertySet > aXPropSet;
2206 css::uno::Reference< css::drawing::XShape > aShapeA, aShapeB;
2207 css::uno::Any aAny( rXShape->queryInterface( cppu::UnoType<css::beans::XPropertySet>::get()));
2208 if ( aAny >>= aXPropSet )
2210 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeKind", true ) )
2212 css::drawing::ConnectorType eCt;
2213 aAny >>= eCt;
2214 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeStartPoint" ) )
2216 aStartPoint = *o3tl::doAccess<css::awt::Point>(aAny);
2217 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeEndPoint" ) )
2219 aEndPoint = *o3tl::doAccess<css::awt::Point>(aAny);
2221 rShapeFlags = SHAPEFLAG_HAVEANCHOR | SHAPEFLAG_HAVESPT | SHAPEFLAG_CONNECTOR;
2222 rGeoRect = css::awt::Rectangle( aStartPoint.X, aStartPoint.Y,
2223 ( aEndPoint.X - aStartPoint.X ) + 1, ( aEndPoint.Y - aStartPoint.Y ) + 1 );
2224 // set standard's FLIP in below code
2225 if ( eCt != css::drawing::ConnectorType_STANDARD)
2227 if ( rGeoRect.Height < 0 ) // justify
2229 rShapeFlags |= SHAPEFLAG_FLIPV;
2230 rGeoRect.Y = aEndPoint.Y;
2231 rGeoRect.Height = -rGeoRect.Height;
2233 if ( rGeoRect.Width < 0 )
2235 rShapeFlags |= SHAPEFLAG_FLIPH;
2236 rGeoRect.X = aEndPoint.X;
2237 rGeoRect.Width = -rGeoRect.Width;
2240 sal_uInt32 nAdjustValue1, nAdjustValue2, nAdjustValue3;
2241 nAdjustValue1 = nAdjustValue2 = nAdjustValue3 = 0x2a30;
2243 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeStartConnection" ) )
2244 aAny >>= aShapeA;
2245 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeEndConnection" ) )
2246 aAny >>= aShapeB;
2247 rSolverContainer.AddConnector( rXShape, aStartPoint, aShapeA, aEndPoint, aShapeB );
2248 switch ( eCt )
2250 case css::drawing::ConnectorType_CURVE :
2252 rShapeType = ESCHER_ShpInst_CurvedConnector3;
2253 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleCurved );
2254 AddOpt( ESCHER_Prop_adjustValue, nAdjustValue1 );
2255 AddOpt( ESCHER_Prop_adjust2Value, -(sal_Int32)nAdjustValue2 );
2257 break;
2259 case css::drawing::ConnectorType_STANDARD :// Connector 2->5
2261 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "PolyPolygonBezier" ) )
2263 tools::PolyPolygon aPolyPolygon = GetPolyPolygon( aAny );
2264 tools::Polygon aPoly;
2265 if ( aPolyPolygon.Count() > 0 )
2267 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleBent );
2268 aPoly = aPolyPolygon[ 0 ];
2269 sal_Int32 nAdjCount = lcl_GetAdjustValueCount( aPoly );
2270 rShapeType = ( sal_uInt16 )( ESCHER_ShpInst_BentConnector2 + nAdjCount);
2271 for ( sal_Int32 i = 0 ; i < nAdjCount; ++ i)
2272 AddOpt( (sal_uInt16) ( ESCHER_Prop_adjustValue+i) , lcl_GetConnectorAdjustValue( aPoly, i ) );
2274 sal_Int32 nAngle=0;
2275 if (lcl_GetAngle(aPoly,rShapeFlags,nAngle ))
2277 AddOpt( ESCHER_Prop_Rotation, nAngle );
2280 else
2282 rShapeType = ESCHER_ShpInst_BentConnector3;
2283 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleBent );
2286 break;
2287 default:
2288 case css::drawing::ConnectorType_LINE :
2289 case css::drawing::ConnectorType_LINES : // Connector 2->5
2291 rShapeType = ESCHER_ShpInst_StraightConnector1;
2292 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleStraight );
2294 break;
2296 CreateLineProperties( aXPropSet, false );
2297 bRetValue = true;
2303 return bRetValue;
2306 void EscherPropertyContainer::CreateShadowProperties(
2307 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet )
2309 css::uno::Any aAny;
2311 bool bHasShadow = false; // shadow is possible only if at least a fillcolor, linecolor or graphic is set
2312 sal_uInt32 nLineFlags = 0; // default : shape has no line
2313 sal_uInt32 nFillFlags = 0x10; // shape is filled
2315 GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags );
2316 GetOpt( ESCHER_Prop_fNoFillHitTest, nFillFlags );
2318 sal_uInt32 nDummy;
2319 bool bGraphic = GetOpt( DFF_Prop_pib, nDummy ) || GetOpt( DFF_Prop_pibName, nDummy ) || GetOpt( DFF_Prop_pibFlags, nDummy );
2321 sal_uInt32 nShadowFlags = 0x20000;
2322 if ( ( nLineFlags & 8 ) || ( nFillFlags & 0x10 ) || bGraphic )
2324 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "Shadow", true ) )
2326 if ( aAny >>= bHasShadow )
2328 if ( bHasShadow )
2330 nShadowFlags |= 2;
2331 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowColor" ) )
2332 AddOpt( ESCHER_Prop_shadowColor, ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny) ) );
2333 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowXDistance" ) )
2334 AddOpt( ESCHER_Prop_shadowOffsetX, *o3tl::doAccess<sal_Int32>(aAny) * 360 );
2335 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowYDistance" ) )
2336 AddOpt( ESCHER_Prop_shadowOffsetY, *o3tl::doAccess<sal_Int32>(aAny) * 360 );
2337 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowTransparence" ) )
2338 AddOpt( ESCHER_Prop_shadowOpacity, 0x10000 - (((sal_uInt32)*o3tl::doAccess<sal_uInt16>(aAny)) * 655 ) );
2343 AddOpt( ESCHER_Prop_fshadowObscured, nShadowFlags );
2346 sal_Int32 EscherPropertyContainer::GetValueForEnhancedCustomShapeParameter( const css::drawing::EnhancedCustomShapeParameter& rParameter,
2347 const std::vector< sal_Int32 >& rEquationOrder, bool bAdjustTrans )
2349 sal_Int32 nValue = 0;
2350 if ( rParameter.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
2352 double fValue(0.0);
2353 if ( rParameter.Value >>= fValue )
2354 nValue = (sal_Int32)fValue;
2356 else
2357 rParameter.Value >>= nValue;
2359 switch( rParameter.Type )
2361 case css::drawing::EnhancedCustomShapeParameterType::EQUATION :
2363 size_t nIndex = (size_t) nValue;
2364 OSL_ASSERT(nIndex < rEquationOrder.size());
2365 if ( nIndex < rEquationOrder.size() )
2367 nValue = (sal_uInt16)rEquationOrder[ nIndex ];
2368 nValue |= (sal_uInt32)0x80000000;
2371 break;
2372 case css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT:
2374 if(bAdjustTrans)
2376 sal_uInt32 nAdjustValue = 0;
2377 bool bGot = GetOpt((sal_uInt16)( DFF_Prop_adjustValue + nValue ), nAdjustValue);
2378 if(bGot) nValue = (sal_Int32)nAdjustValue;
2381 break;
2382 case css::drawing::EnhancedCustomShapeParameterType::NORMAL :
2383 default:
2384 break;
2385 /* not sure if it is allowed to set following values
2386 (but they are not yet used)
2387 case css::drawing::EnhancedCustomShapeParameterType::BOTTOM :
2388 case css::drawing::EnhancedCustomShapeParameterType::RIGHT :
2389 case css::drawing::EnhancedCustomShapeParameterType::TOP :
2390 case css::drawing::EnhancedCustomShapeParameterType::LEFT :
2393 return nValue;
2396 bool GetValueForEnhancedCustomShapeHandleParameter( sal_Int32& nRetValue, const css::drawing::EnhancedCustomShapeParameter& rParameter )
2398 bool bSpecial = false;
2399 nRetValue = 0;
2400 if ( rParameter.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
2402 double fValue(0.0);
2403 if ( rParameter.Value >>= fValue )
2404 nRetValue = (sal_Int32)fValue;
2406 else
2407 rParameter.Value >>= nRetValue;
2409 switch( rParameter.Type )
2411 case css::drawing::EnhancedCustomShapeParameterType::EQUATION :
2413 nRetValue += 3;
2414 bSpecial = true;
2416 break;
2417 case css::drawing::EnhancedCustomShapeParameterType::ADJUSTMENT :
2419 nRetValue += 0x100;
2420 bSpecial = true;
2422 break;
2423 case css::drawing::EnhancedCustomShapeParameterType::TOP :
2424 case css::drawing::EnhancedCustomShapeParameterType::LEFT :
2426 nRetValue = 0;
2427 bSpecial = true;
2429 break;
2430 case css::drawing::EnhancedCustomShapeParameterType::RIGHT :
2431 case css::drawing::EnhancedCustomShapeParameterType::BOTTOM :
2433 nRetValue = 1;
2434 bSpecial = true;
2436 break;
2437 case css::drawing::EnhancedCustomShapeParameterType::NORMAL :
2441 break;
2443 return bSpecial;
2446 void ConvertEnhancedCustomShapeEquation( SdrObjCustomShape* pCustoShape,
2447 std::vector< EnhancedCustomShapeEquation >& rEquations, std::vector< sal_Int32 >& rEquationOrder )
2449 if ( pCustoShape )
2451 uno::Sequence< OUString > sEquationSource;
2452 const SdrCustomShapeGeometryItem& rGeometryItem = static_cast<const SdrCustomShapeGeometryItem&>(
2453 pCustoShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
2454 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "Equations" );
2455 if ( pAny )
2456 *pAny >>= sEquationSource;
2457 sal_Int32 nEquationSourceCount = sEquationSource.getLength();
2458 if ( nEquationSourceCount && (nEquationSourceCount <= 128) )
2460 sal_Int32 i;
2461 for ( i = 0; i < nEquationSourceCount; i++ )
2463 EnhancedCustomShape2d aCustoShape2d( pCustoShape );
2466 std::shared_ptr< EnhancedCustomShape::ExpressionNode > aExpressNode(
2467 EnhancedCustomShape::FunctionParser::parseFunction( sEquationSource[ i ], aCustoShape2d ) );
2468 css::drawing::EnhancedCustomShapeParameter aPara( aExpressNode->fillNode( rEquations, nullptr, 0 ) );
2469 if ( aPara.Type != css::drawing::EnhancedCustomShapeParameterType::EQUATION )
2471 EnhancedCustomShapeEquation aEquation;
2472 aEquation.nOperation = 0;
2473 EnhancedCustomShape::FillEquationParameter( aPara, 0, aEquation );
2474 rEquations.push_back( aEquation );
2477 catch ( const EnhancedCustomShape::ParseError& )
2479 EnhancedCustomShapeEquation aEquation; // ups, we should not be here,
2480 aEquation.nOperation = 0; // creating a default equation with value 1
2481 aEquation.nPara[ 0 ] = 1; // hoping that this will not break anything
2482 rEquations.push_back( aEquation );
2484 catch ( ... )
2486 EnhancedCustomShapeEquation aEquation; // #i112309# EnhancedCustomShape::Parse error
2487 aEquation.nOperation = 0; // not catched on linux platform
2488 aEquation.nPara[ 0 ] = 1;
2489 rEquations.push_back( aEquation );
2491 rEquationOrder.push_back( rEquations.size() - 1 );
2493 // now updating our old equation indices, they are marked with a bit in the hiword of nOperation
2494 std::vector< EnhancedCustomShapeEquation >::iterator aIter( rEquations.begin() );
2495 std::vector< EnhancedCustomShapeEquation >::iterator aEnd ( rEquations.end() );
2496 while( aIter != aEnd )
2498 sal_uInt32 nMask = 0x20000000;
2499 for( i = 0; i < 3; i++ )
2501 if ( aIter->nOperation & nMask )
2503 aIter->nOperation ^= nMask;
2504 const size_t nIndex(aIter->nPara[ i ] & 0x3ff);
2506 // #i124661# check index access, there are cases where this is out of bound leading
2507 // to errors up to crashes when executed
2508 if(nIndex < rEquationOrder.size())
2510 aIter->nPara[ i ] = rEquationOrder[ nIndex ] | 0x400;
2512 else
2514 OSL_ENSURE(false, "Attempted out of bound access to rEquationOrder of CustomShape (!)");
2517 nMask <<= 1;
2519 ++aIter;
2525 bool EscherPropertyContainer::IsDefaultObject( SdrObjCustomShape* pCustoShape , const MSO_SPT eShapeType )
2527 bool bIsDefaultObject = false;
2528 switch(eShapeType)
2530 // if the custom shape is not default shape of ppt, return sal_Fasle;
2531 case mso_sptTearDrop:
2532 return bIsDefaultObject;
2534 default:
2535 break;
2538 if ( pCustoShape )
2540 if ( pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::Equations )
2541 && pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::Viewbox )
2542 && pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::Path )
2543 && pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::Gluepoints )
2544 && pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::Segments )
2545 && pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::StretchX )
2546 && pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::StretchY )
2547 && pCustoShape->IsDefaultGeometry( SdrObjCustomShape::DefaultType::TextFrames ) )
2548 bIsDefaultObject = true;
2551 return bIsDefaultObject;
2554 void EscherPropertyContainer::LookForPolarHandles( const MSO_SPT eShapeType, sal_Int32& nAdjustmentsWhichNeedsToBeConverted )
2556 const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eShapeType );
2557 if ( pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles )
2559 sal_Int32 k, nkCount = pDefCustomShape->nHandles;
2560 const SvxMSDffHandle* pData = pDefCustomShape->pHandles;
2561 for ( k = 0; k < nkCount; k++, pData++ )
2563 if ( pData->nFlags & SvxMSDffHandleFlags::POLAR )
2565 if ( ( pData->nPositionY >= 0x256 ) || ( pData->nPositionY <= 0x107 ) )
2566 nAdjustmentsWhichNeedsToBeConverted |= ( 1 << k );
2572 bool EscherPropertyContainer::GetAdjustmentValue( const css::drawing::EnhancedCustomShapeAdjustmentValue & rkProp, sal_Int32 nIndex, sal_Int32 nAdjustmentsWhichNeedsToBeConverted, sal_Int32& nValue )
2574 if ( rkProp.State != beans::PropertyState_DIRECT_VALUE )
2575 return false;
2577 bool bUseFixedFloat = ( nAdjustmentsWhichNeedsToBeConverted & ( 1 << nIndex ) ) != 0;
2578 if ( rkProp.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
2580 double fValue(0.0);
2581 rkProp.Value >>= fValue;
2582 if ( bUseFixedFloat )
2583 fValue *= 65536.0;
2584 nValue = (sal_Int32)fValue;
2586 else
2588 rkProp.Value >>= nValue;
2589 if ( bUseFixedFloat )
2590 nValue <<= 16;
2593 return true;
2596 void EscherPropertyContainer::CreateCustomShapeProperties( const MSO_SPT eShapeType, const uno::Reference< drawing::XShape > & rXShape )
2598 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
2599 if ( aXPropSet.is() )
2601 SdrObjCustomShape* pCustoShape = static_cast<SdrObjCustomShape*>(GetSdrObjectFromXShape( rXShape ));
2602 if ( !pCustoShape ) return;
2603 const OUString sCustomShapeGeometry( "CustomShapeGeometry" );
2604 uno::Any aGeoPropSet = aXPropSet->getPropertyValue( sCustomShapeGeometry );
2605 uno::Sequence< beans::PropertyValue > aGeoPropSeq;
2606 if ( aGeoPropSet >>= aGeoPropSeq )
2608 const OUString sViewBox ( "ViewBox" );
2609 const OUString sTextRotateAngle ( "TextRotateAngle" );
2610 const OUString sExtrusion ( "Extrusion" );
2611 const OUString sEquations ( "Equations" );
2612 const OUString sPath ( "Path" );
2613 const OUString sTextPath ( "TextPath" );
2614 const OUString sHandles ( "Handles" );
2615 const OUString sAdjustmentValues ( "AdjustmentValues" );
2617 bool bAdjustmentValuesProp = false;
2618 uno::Any aAdjustmentValuesProp;
2619 bool bPathCoordinatesProp = false;
2620 uno::Any aPathCoordinatesProp;
2622 sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
2623 uno::Sequence< beans::PropertyValues > aHandlesPropSeq;
2624 bool bPredefinedHandlesUsed = true;
2625 bool bIsDefaultObject = IsDefaultObject( pCustoShape , eShapeType);
2627 // convert property "Equations" into std::vector< EnhancedCustomShapeEquationEquation >
2628 std::vector< EnhancedCustomShapeEquation > aEquations;
2629 std::vector< sal_Int32 > aEquationOrder;
2630 ConvertEnhancedCustomShapeEquation( pCustoShape, aEquations, aEquationOrder );
2632 sal_Int32 i, nCount = aGeoPropSeq.getLength();
2633 for ( i = 0; i < nCount; i++ )
2635 const beans::PropertyValue& rProp = aGeoPropSeq[ i ];
2636 if ( rProp.Name.equals( sViewBox ) )
2638 if ( !bIsDefaultObject )
2640 awt::Rectangle aViewBox;
2641 if ( rProp.Value >>= aViewBox )
2643 AddOpt( DFF_Prop_geoLeft, aViewBox.X );
2644 AddOpt( DFF_Prop_geoTop, aViewBox.Y );
2645 AddOpt( DFF_Prop_geoRight, aViewBox.X + aViewBox.Width );
2646 AddOpt( DFF_Prop_geoBottom,aViewBox.Y + aViewBox.Height );
2650 else if ( rProp.Name.equals( sTextRotateAngle ) )
2652 double f = 0;
2653 if ( rProp.Value >>= f )
2655 double fTextRotateAngle = fmod( f, 360.0 );
2656 if ( fTextRotateAngle < 0 )
2657 fTextRotateAngle = 360 + fTextRotateAngle;
2658 if ( ( fTextRotateAngle < 271.0 ) && ( fTextRotateAngle > 269.0 ) )
2659 AddOpt( DFF_Prop_cdirFont, mso_cdir90 );
2660 else if ( ( fTextRotateAngle < 181.0 ) && ( fTextRotateAngle > 179.0 ) )
2661 AddOpt( DFF_Prop_cdirFont, mso_cdir180 );
2662 else if ( ( fTextRotateAngle < 91.0 ) && ( fTextRotateAngle > 79.0 ) )
2663 AddOpt( DFF_Prop_cdirFont, mso_cdir270 );
2666 else if ( rProp.Name.equals( sExtrusion ) )
2668 uno::Sequence< beans::PropertyValue > aExtrusionPropSeq;
2669 if ( rProp.Value >>= aExtrusionPropSeq )
2671 sal_uInt32 nLightFaceFlagsOrg, nLightFaceFlags;
2672 sal_uInt32 nFillHarshFlagsOrg, nFillHarshFlags;
2673 nLightFaceFlagsOrg = nLightFaceFlags = 0x000001;
2674 nFillHarshFlagsOrg = nFillHarshFlags = 0x00001e;
2675 if ( GetOpt( DFF_Prop_fc3DLightFace, nLightFaceFlags ) )
2676 nLightFaceFlagsOrg = nLightFaceFlags;
2677 if ( GetOpt( DFF_Prop_fc3DFillHarsh, nFillHarshFlags ) )
2678 nFillHarshFlagsOrg = nFillHarshFlags;
2680 sal_Int32 r, nrCount = aExtrusionPropSeq.getLength();
2681 for ( r = 0; r < nrCount; r++ )
2683 const beans::PropertyValue& rrProp = aExtrusionPropSeq[ r ];
2684 const OUString sExtrusionBrightness ( "Brightness" );
2685 const OUString sExtrusionDepth ( "Depth" );
2686 const OUString sExtrusionDiffusion ( "Diffusion" );
2687 const OUString sExtrusionNumberOfLineSegments ( "NumberOfLineSegments" );
2688 const OUString sExtrusionLightFace ( "LightFace" );
2689 const OUString sExtrusionFirstLightHarsh ( "FirstLightHarsh" );
2690 const OUString sExtrusionSecondLightHarsh ( "SecondLightHarsh" );
2691 const OUString sExtrusionFirstLightLevel ( "FirstLightLevel" );
2692 const OUString sExtrusionSecondLightLevel ( "SecondLightLevel" );
2693 const OUString sExtrusionFirstLightDirection ( "FirstLightDirection" );
2694 const OUString sExtrusionSecondLightDirection ( "SecondLightDirection" );
2695 const OUString sExtrusionMetal ( "Metal" );
2696 const OUString sExtrusionShadeMode ( "ShadeMode" );
2697 const OUString sExtrusionRotateAngle ( "RotateAngle" );
2698 const OUString sExtrusionRotationCenter ( "RotationCenter" );
2699 const OUString sExtrusionShininess ( "Shininess" );
2700 const OUString sExtrusionSkew ( "Skew" );
2701 const OUString sExtrusionSpecularity ( "Specularity" );
2702 const OUString sExtrusionProjectionMode ( "ProjectionMode" );
2703 const OUString sExtrusionViewPoint ( "ViewPoint" );
2704 const OUString sExtrusionOrigin ( "Origin" );
2705 const OUString sExtrusionColor ( "Color" );
2707 if ( rrProp.Name.equals( sExtrusion ) )
2709 bool bExtrusionOn;
2710 if ( rrProp.Value >>= bExtrusionOn )
2712 nLightFaceFlags |= 0x80000;
2713 if ( bExtrusionOn )
2714 nLightFaceFlags |= 8;
2715 else
2716 nLightFaceFlags &=~8;
2719 else if ( rrProp.Name.equals( sExtrusionBrightness ) )
2721 double fExtrusionBrightness = 0;
2722 if ( rrProp.Value >>= fExtrusionBrightness )
2723 AddOpt( DFF_Prop_c3DAmbientIntensity, (sal_Int32)( fExtrusionBrightness * 655.36 ) );
2725 else if ( rrProp.Name.equals( sExtrusionDepth ) )
2727 double fDepth = 0;
2728 double fFraction = 0;
2729 css::drawing::EnhancedCustomShapeParameterPair aDepthParaPair;
2730 if ( ( rrProp.Value >>= aDepthParaPair ) && ( aDepthParaPair.First.Value >>= fDepth ) && ( aDepthParaPair.Second.Value >>= fFraction ) )
2732 double fForeDepth = fDepth * fFraction;
2733 double fBackDepth = fDepth - fForeDepth;
2735 fBackDepth *= 360.0;
2736 AddOpt( DFF_Prop_c3DExtrudeBackward, (sal_Int32)fBackDepth );
2738 if ( fForeDepth != 0.0 )
2740 fForeDepth *= 360.0;
2741 AddOpt( DFF_Prop_c3DExtrudeForward, (sal_Int32)fForeDepth );
2745 else if ( rrProp.Name.equals( sExtrusionDiffusion ) )
2747 double fExtrusionDiffusion = 0;
2748 if ( rrProp.Value >>= fExtrusionDiffusion )
2749 AddOpt( DFF_Prop_c3DDiffuseAmt, (sal_Int32)( fExtrusionDiffusion * 655.36 ) );
2751 else if ( rrProp.Name.equals( sExtrusionNumberOfLineSegments ) )
2753 sal_Int32 nExtrusionNumberOfLineSegments = 0;
2754 if ( rrProp.Value >>= nExtrusionNumberOfLineSegments )
2755 AddOpt( DFF_Prop_c3DTolerance, nExtrusionNumberOfLineSegments );
2757 else if ( rrProp.Name.equals( sExtrusionLightFace ) )
2759 bool bExtrusionLightFace;
2760 if ( rrProp.Value >>= bExtrusionLightFace )
2762 nLightFaceFlags |= 0x10000;
2763 if ( bExtrusionLightFace )
2764 nLightFaceFlags |= 1;
2765 else
2766 nLightFaceFlags &=~1;
2769 else if ( rrProp.Name.equals( sExtrusionFirstLightHarsh ) )
2771 bool bExtrusionFirstLightHarsh;
2772 if ( rrProp.Value >>= bExtrusionFirstLightHarsh )
2774 nFillHarshFlags |= 0x20000;
2775 if ( bExtrusionFirstLightHarsh )
2776 nFillHarshFlags |= 2;
2777 else
2778 nFillHarshFlags &=~2;
2781 else if ( rrProp.Name.equals( sExtrusionSecondLightHarsh ) )
2783 bool bExtrusionSecondLightHarsh;
2784 if ( rrProp.Value >>= bExtrusionSecondLightHarsh )
2786 nFillHarshFlags |= 0x10000;
2787 if ( bExtrusionSecondLightHarsh )
2788 nFillHarshFlags |= 1;
2789 else
2790 nFillHarshFlags &=~1;
2793 else if ( rrProp.Name.equals( sExtrusionFirstLightLevel ) )
2795 double fExtrusionFirstLightLevel = 0;
2796 if ( rrProp.Value >>= fExtrusionFirstLightLevel )
2797 AddOpt( DFF_Prop_c3DKeyIntensity, (sal_Int32)( fExtrusionFirstLightLevel * 655.36 ) );
2799 else if ( rrProp.Name.equals( sExtrusionSecondLightLevel ) )
2801 double fExtrusionSecondLightLevel = 0;
2802 if ( rrProp.Value >>= fExtrusionSecondLightLevel )
2803 AddOpt( DFF_Prop_c3DFillIntensity, (sal_Int32)( fExtrusionSecondLightLevel * 655.36 ) );
2805 else if ( rrProp.Name.equals( sExtrusionFirstLightDirection ) )
2807 drawing::Direction3D aExtrusionFirstLightDirection;
2808 if ( rrProp.Value >>= aExtrusionFirstLightDirection )
2810 AddOpt( DFF_Prop_c3DKeyX, (sal_Int32)aExtrusionFirstLightDirection.DirectionX );
2811 AddOpt( DFF_Prop_c3DKeyY, (sal_Int32)aExtrusionFirstLightDirection.DirectionY );
2812 AddOpt( DFF_Prop_c3DKeyZ, (sal_Int32)aExtrusionFirstLightDirection.DirectionZ );
2815 else if ( rrProp.Name.equals( sExtrusionSecondLightDirection ) )
2817 drawing::Direction3D aExtrusionSecondLightPosition;
2818 if ( rrProp.Value >>= aExtrusionSecondLightPosition )
2820 AddOpt( DFF_Prop_c3DFillX, (sal_Int32)aExtrusionSecondLightPosition.DirectionX );
2821 AddOpt( DFF_Prop_c3DFillY, (sal_Int32)aExtrusionSecondLightPosition.DirectionY );
2822 AddOpt( DFF_Prop_c3DFillZ, (sal_Int32)aExtrusionSecondLightPosition.DirectionZ );
2825 else if ( rrProp.Name.equals( sExtrusionMetal ) )
2827 bool bExtrusionMetal;
2828 if ( rrProp.Value >>= bExtrusionMetal )
2830 nLightFaceFlags |= 0x40000;
2831 if ( bExtrusionMetal )
2832 nLightFaceFlags |= 4;
2833 else
2834 nLightFaceFlags &=~4;
2837 else if ( rrProp.Name.equals( sExtrusionShadeMode ) )
2839 drawing::ShadeMode eExtrusionShadeMode;
2840 if ( rrProp.Value >>= eExtrusionShadeMode )
2842 sal_uInt32 nRenderMode;
2843 switch( eExtrusionShadeMode )
2845 default:
2846 case drawing::ShadeMode_FLAT :
2847 case drawing::ShadeMode_PHONG :
2848 case drawing::ShadeMode_SMOOTH :
2849 nRenderMode = mso_FullRender;
2850 break;
2851 case drawing::ShadeMode_DRAFT :
2853 nRenderMode = mso_Wireframe;
2855 break;
2857 AddOpt( DFF_Prop_c3DRenderMode, nRenderMode );
2860 else if ( rrProp.Name.equals( sExtrusionRotateAngle ) )
2862 double fExtrusionAngleX = 0;
2863 double fExtrusionAngleY = 0;
2864 css::drawing::EnhancedCustomShapeParameterPair aRotateAnglePair;
2865 if ( ( rrProp.Value >>= aRotateAnglePair ) && ( aRotateAnglePair.First.Value >>= fExtrusionAngleX ) && ( aRotateAnglePair.Second.Value >>= fExtrusionAngleY ) )
2867 fExtrusionAngleX *= 65536;
2868 fExtrusionAngleY *= 65536;
2869 AddOpt( DFF_Prop_c3DXRotationAngle, (sal_Int32)fExtrusionAngleX );
2870 AddOpt( DFF_Prop_c3DYRotationAngle, (sal_Int32)fExtrusionAngleY );
2873 else if ( rrProp.Name.equals( sExtrusionRotationCenter ) )
2875 drawing::Direction3D aExtrusionRotationCenter;
2876 if ( rrProp.Value >>= aExtrusionRotationCenter )
2878 AddOpt( DFF_Prop_c3DRotationCenterX, (sal_Int32)( aExtrusionRotationCenter.DirectionX * 360.0 ) );
2879 AddOpt( DFF_Prop_c3DRotationCenterY, (sal_Int32)( aExtrusionRotationCenter.DirectionY * 360.0 ) );
2880 AddOpt( DFF_Prop_c3DRotationCenterZ, (sal_Int32)( aExtrusionRotationCenter.DirectionZ * 360.0 ) );
2881 nFillHarshFlags &=~8; // don't use AutoRotationCenter;
2884 else if ( rrProp.Name.equals( sExtrusionShininess ) )
2886 double fExtrusionShininess = 0;
2887 if ( rrProp.Value >>= fExtrusionShininess )
2888 AddOpt( DFF_Prop_c3DShininess, (sal_Int32)( fExtrusionShininess * 655.36 ) );
2890 else if ( rrProp.Name.equals( sExtrusionSkew ) )
2892 double fSkewAmount = 0;
2893 double fSkewAngle = 0;
2894 css::drawing::EnhancedCustomShapeParameterPair aSkewParaPair;
2895 if ( ( rrProp.Value >>= aSkewParaPair ) && ( aSkewParaPair.First.Value >>= fSkewAmount ) && ( aSkewParaPair.Second.Value >>= fSkewAngle ) )
2897 AddOpt( DFF_Prop_c3DSkewAmount, (sal_Int32)fSkewAmount );
2898 AddOpt( DFF_Prop_c3DSkewAngle, (sal_Int32)( fSkewAngle * 65536 ) );
2901 else if ( rrProp.Name.equals( sExtrusionSpecularity ) )
2903 double fExtrusionSpecularity = 0;
2904 if ( rrProp.Value >>= fExtrusionSpecularity )
2905 AddOpt( DFF_Prop_c3DSpecularAmt, (sal_Int32)( fExtrusionSpecularity * 1333 ) );
2907 else if ( rrProp.Name.equals( sExtrusionProjectionMode ) )
2909 drawing::ProjectionMode eExtrusionProjectionMode;
2910 if ( rrProp.Value >>= eExtrusionProjectionMode )
2912 nFillHarshFlags |= 0x40000;
2913 if ( eExtrusionProjectionMode == drawing::ProjectionMode_PARALLEL )
2914 nFillHarshFlags |= 4;
2915 else
2916 nFillHarshFlags &=~4;
2919 else if ( rrProp.Name.equals( sExtrusionViewPoint ) )
2921 drawing::Position3D aExtrusionViewPoint;
2922 if ( rrProp.Value >>= aExtrusionViewPoint )
2924 aExtrusionViewPoint.PositionX *= 360.0;
2925 aExtrusionViewPoint.PositionY *= 360.0;
2926 aExtrusionViewPoint.PositionZ *= 360.0;
2927 AddOpt( DFF_Prop_c3DXViewpoint, (sal_Int32)aExtrusionViewPoint.PositionX );
2928 AddOpt( DFF_Prop_c3DYViewpoint, (sal_Int32)aExtrusionViewPoint.PositionY );
2929 AddOpt( DFF_Prop_c3DZViewpoint, (sal_Int32)aExtrusionViewPoint.PositionZ );
2932 else if ( rrProp.Name.equals( sExtrusionOrigin ) )
2934 double fExtrusionOriginX = 0;
2935 double fExtrusionOriginY = 0;
2936 css::drawing::EnhancedCustomShapeParameterPair aOriginPair;
2937 if ( ( rrProp.Value >>= aOriginPair ) && ( aOriginPair.First.Value >>= fExtrusionOriginX ) && ( aOriginPair.Second.Value >>= fExtrusionOriginY ) )
2939 AddOpt( DFF_Prop_c3DOriginX, (sal_Int32)( fExtrusionOriginX * 65536 ) );
2940 AddOpt( DFF_Prop_c3DOriginY, (sal_Int32)( fExtrusionOriginY * 65536 ) );
2943 else if ( rrProp.Name.equals( sExtrusionColor ) )
2945 bool bExtrusionColor;
2946 if ( rrProp.Value >>= bExtrusionColor )
2948 nLightFaceFlags |= 0x20000;
2949 if ( bExtrusionColor )
2951 nLightFaceFlags |= 2;
2952 uno::Any aFillColor2;
2953 if ( EscherPropertyValueHelper::GetPropertyValue( aFillColor2, aXPropSet, "FillColor2", true ) )
2955 sal_uInt32 nFillColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aFillColor2) );
2956 AddOpt( DFF_Prop_c3DExtrusionColor, nFillColor );
2959 else
2960 nLightFaceFlags &=~2;
2964 if ( nLightFaceFlags != nLightFaceFlagsOrg )
2965 AddOpt( DFF_Prop_fc3DLightFace, nLightFaceFlags );
2966 if ( nFillHarshFlags != nFillHarshFlagsOrg )
2967 AddOpt( DFF_Prop_fc3DFillHarsh, nFillHarshFlags );
2970 else if ( rProp.Name.equals( sEquations ) )
2972 if ( !bIsDefaultObject )
2974 sal_uInt16 nElements = (sal_uInt16)aEquations.size();
2975 if ( nElements )
2977 sal_uInt16 nElementSize = 8;
2978 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
2979 SvMemoryStream aOut( nStreamSize );
2980 aOut.WriteUInt16( nElements )
2981 .WriteUInt16( nElements )
2982 .WriteUInt16( nElementSize );
2984 std::vector< EnhancedCustomShapeEquation >::const_iterator aIter( aEquations.begin() );
2985 std::vector< EnhancedCustomShapeEquation >::const_iterator aEnd ( aEquations.end() );
2986 while( aIter != aEnd )
2988 aOut.WriteUInt16( aIter->nOperation )
2989 .WriteInt16( aIter->nPara[ 0 ] )
2990 .WriteInt16( aIter->nPara[ 1 ] )
2991 .WriteInt16( aIter->nPara[ 2 ] );
2992 ++aIter;
2994 sal_uInt8* pBuf = new sal_uInt8[ nStreamSize ];
2995 memcpy( pBuf, aOut.GetData(), nStreamSize );
2996 AddOpt( DFF_Prop_pFormulas, true, nStreamSize - 6, pBuf, nStreamSize );
2998 else
3000 sal_uInt8* pBuf = new sal_uInt8[ 1 ];
3001 AddOpt( DFF_Prop_pFormulas, true, 0, pBuf, 0 );
3005 else if ( rProp.Name.equals( sPath ) )
3007 uno::Sequence< beans::PropertyValue > aPathPropSeq;
3008 if ( rProp.Value >>= aPathPropSeq )
3010 sal_uInt32 nPathFlags, nPathFlagsOrg;
3011 nPathFlagsOrg = nPathFlags = 0x39;
3012 if ( GetOpt( DFF_Prop_fFillOK, nPathFlags ) )
3013 nPathFlagsOrg = nPathFlags;
3015 sal_Int32 r, nrCount = aPathPropSeq.getLength();
3016 for ( r = 0; r < nrCount; r++ )
3018 const beans::PropertyValue& rrProp = aPathPropSeq[ r ];
3019 const OUString sPathExtrusionAllowed ( "ExtrusionAllowed" );
3020 const OUString sPathConcentricGradientFillAllowed ( "ConcentricGradientFillAllowed" );
3021 const OUString sPathTextPathAllowed ( "TextPathAllowed" );
3022 const OUString sPathCoordinates ( "Coordinates" );
3023 const OUString sPathGluePoints ( "GluePoints" );
3024 const OUString sPathGluePointType ( "GluePointType" );
3025 const OUString sPathSegments ( "Segments" );
3026 const OUString sPathStretchX ( "StretchX" );
3027 const OUString sPathStretchY ( "StretchY" );
3028 const OUString sPathTextFrames ( "TextFrames" );
3030 if ( rrProp.Name.equals( sPathExtrusionAllowed ) )
3032 bool bExtrusionAllowed;
3033 if ( rrProp.Value >>= bExtrusionAllowed )
3035 nPathFlags |= 0x100000;
3036 if ( bExtrusionAllowed )
3037 nPathFlags |= 16;
3038 else
3039 nPathFlags &=~16;
3042 else if ( rrProp.Name.equals( sPathConcentricGradientFillAllowed ) )
3044 bool bConcentricGradientFillAllowed;
3045 if ( rrProp.Value >>= bConcentricGradientFillAllowed )
3047 nPathFlags |= 0x20000;
3048 if ( bConcentricGradientFillAllowed )
3049 nPathFlags |= 2;
3050 else
3051 nPathFlags &=~2;
3054 else if ( rrProp.Name.equals( sPathTextPathAllowed ) )
3056 bool bTextPathAllowed;
3057 if ( rrProp.Value >>= bTextPathAllowed )
3059 nPathFlags |= 0x40000;
3060 if ( bTextPathAllowed )
3061 nPathFlags |= 4;
3062 else
3063 nPathFlags &=~4;
3066 else if ( rrProp.Name.equals( sPathCoordinates ) )
3068 if ( !bIsDefaultObject )
3070 aPathCoordinatesProp = rrProp.Value;
3071 bPathCoordinatesProp = true;
3074 else if ( rrProp.Name.equals( sPathGluePoints ) )
3076 if ( !bIsDefaultObject )
3078 css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> aGluePoints;
3079 if ( rrProp.Value >>= aGluePoints )
3081 // creating the vertices
3082 sal_uInt16 nElements = (sal_uInt16)aGluePoints.getLength();
3083 if ( nElements )
3085 sal_uInt16 j, nElementSize = 8;
3086 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3087 SvMemoryStream aOut( nStreamSize );
3088 aOut.WriteUInt16( nElements )
3089 .WriteUInt16( nElements )
3090 .WriteUInt16( nElementSize );
3091 for( j = 0; j < nElements; j++ )
3093 sal_Int32 X = GetValueForEnhancedCustomShapeParameter( aGluePoints[ j ].First, aEquationOrder );
3094 sal_Int32 Y = GetValueForEnhancedCustomShapeParameter( aGluePoints[ j ].Second, aEquationOrder );
3095 aOut.WriteInt32( X )
3096 .WriteInt32( Y );
3098 sal_uInt8* pBuf = new sal_uInt8[ nStreamSize ];
3099 memcpy( pBuf, aOut.GetData(), nStreamSize );
3100 AddOpt( DFF_Prop_connectorPoints, true, nStreamSize - 6, pBuf, nStreamSize ); // -6
3102 else
3104 sal_uInt8* pBuf = new sal_uInt8[ 1 ];
3105 AddOpt( DFF_Prop_connectorPoints, true, 0, pBuf, 0 );
3110 else if ( rrProp.Name.equals( sPathGluePointType ) )
3112 sal_Int16 nGluePointType = sal_Int16();
3113 if ( rrProp.Value >>= nGluePointType )
3114 AddOpt( DFF_Prop_connectorType, (sal_uInt16)nGluePointType );
3116 else if ( rrProp.Name.equals( sPathSegments ) )
3118 if ( !bIsDefaultObject )
3120 css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > aSegments;
3121 if ( rrProp.Value >>= aSegments )
3123 // creating seginfo
3124 if ( (sal_uInt16)aSegments.getLength() )
3126 sal_uInt16 j, nElements = (sal_uInt16)aSegments.getLength();
3127 sal_uInt16 nElementSize = 2;
3128 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3129 SvMemoryStream aOut( nStreamSize );
3130 aOut.WriteUInt16( nElements )
3131 .WriteUInt16( nElements )
3132 .WriteUInt16( nElementSize );
3133 for ( j = 0; j < nElements; j++ )
3135 // The segment type is stored in the upper 3 bits
3136 // and segment count is stored in the lower 13
3137 // bits.
3139 // If the segment type is msopathEscape, the lower 13 bits
3140 // are divided in a 5 bit escape code and 8 bit
3141 // vertex count (not segment count!)
3142 sal_uInt16 nVal = (sal_uInt16)aSegments[ j ].Count;
3143 switch( aSegments[ j ].Command )
3145 case css::drawing::EnhancedCustomShapeSegmentCommand::UNKNOWN :
3146 case css::drawing::EnhancedCustomShapeSegmentCommand::LINETO :
3147 break;
3148 case css::drawing::EnhancedCustomShapeSegmentCommand::MOVETO :
3149 nVal = (msopathMoveTo << 13);
3150 break;
3151 case css::drawing::EnhancedCustomShapeSegmentCommand::CURVETO :
3153 nVal |= (msopathCurveTo << 13);
3155 break;
3156 case css::drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH :
3158 nVal = 1;
3159 nVal |= (msopathClose << 13);
3161 break;
3162 case css::drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH :
3164 nVal = (msopathEnd << 13);
3166 break;
3167 case css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL :
3169 nVal = (msopathEscape << 13) | (5 << 8);
3171 break;
3172 case css::drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE :
3174 nVal = (msopathEscape << 13) | (11 << 8);
3176 break;
3177 case css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
3179 nVal *= 3;
3180 nVal |= (msopathEscape << 13) | (1 << 8);
3182 break;
3183 case css::drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
3185 nVal *= 3;
3186 nVal |= (msopathEscape << 13) | (2 << 8);
3188 break;
3189 case css::drawing::EnhancedCustomShapeSegmentCommand::ARCTO :
3191 nVal <<= 2;
3192 nVal |= (msopathEscape << 13) | (3 << 8);
3194 break;
3195 case css::drawing::EnhancedCustomShapeSegmentCommand::ARC :
3197 nVal <<= 2;
3198 nVal |= (msopathEscape << 13) | (4 << 8);
3200 break;
3201 case css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
3203 nVal <<= 2;
3204 nVal |= (msopathEscape << 13) | (5 << 8);
3206 break;
3207 case css::drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
3209 nVal <<= 2;
3210 nVal |= (msopathEscape << 13) | (6 << 8);
3212 break;
3213 case css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
3215 nVal |= (msopathEscape << 13) | (7 << 8);
3217 break;
3218 case css::drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
3220 nVal |= (msopathEscape << 13) | (8 << 8);
3222 break;
3224 aOut.WriteUInt16( nVal );
3226 sal_uInt8* pBuf = new sal_uInt8[ nStreamSize ];
3227 memcpy( pBuf, aOut.GetData(), nStreamSize );
3228 AddOpt( DFF_Prop_pSegmentInfo, false, nStreamSize - 6, pBuf, nStreamSize );
3230 else
3232 sal_uInt8* pBuf = new sal_uInt8[ 1 ];
3233 AddOpt( DFF_Prop_pSegmentInfo, true, 0, pBuf, 0 );
3238 else if ( rrProp.Name.equals( sPathStretchX ) )
3240 if ( !bIsDefaultObject )
3242 sal_Int32 nStretchX = 0;
3243 if ( rrProp.Value >>= nStretchX )
3244 AddOpt( DFF_Prop_stretchPointX, nStretchX );
3247 else if ( rrProp.Name.equals( sPathStretchY ) )
3249 if ( !bIsDefaultObject )
3251 sal_Int32 nStretchY = 0;
3252 if ( rrProp.Value >>= nStretchY )
3253 AddOpt( DFF_Prop_stretchPointY, nStretchY );
3256 else if ( rrProp.Name.equals( sPathTextFrames ) )
3258 if ( !bIsDefaultObject )
3260 css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > aPathTextFrames;
3261 if ( rrProp.Value >>= aPathTextFrames )
3263 if ( (sal_uInt16)aPathTextFrames.getLength() )
3265 sal_uInt16 j, nElements = (sal_uInt16)aPathTextFrames.getLength();
3266 sal_uInt16 nElementSize = 16;
3267 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3268 SvMemoryStream aOut( nStreamSize );
3269 aOut.WriteUInt16( nElements )
3270 .WriteUInt16( nElements )
3271 .WriteUInt16( nElementSize );
3272 for ( j = 0; j < nElements; j++ )
3274 sal_Int32 nLeft = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].TopLeft.First, aEquationOrder );
3275 sal_Int32 nTop = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].TopLeft.Second, aEquationOrder );
3276 sal_Int32 nRight = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].BottomRight.First, aEquationOrder );
3277 sal_Int32 nBottom = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].BottomRight.Second, aEquationOrder );
3279 aOut.WriteInt32( nLeft )
3280 .WriteInt32( nTop )
3281 .WriteInt32( nRight )
3282 .WriteInt32( nBottom );
3284 sal_uInt8* pBuf = new sal_uInt8[ nStreamSize ];
3285 memcpy( pBuf, aOut.GetData(), nStreamSize );
3286 AddOpt( DFF_Prop_textRectangles, true, nStreamSize - 6, pBuf, nStreamSize );
3288 else
3290 sal_uInt8* pBuf = new sal_uInt8[ 1 ];
3291 AddOpt( DFF_Prop_textRectangles, true, 0, pBuf, 0 );
3297 if ( nPathFlags != nPathFlagsOrg )
3298 AddOpt( DFF_Prop_fFillOK, nPathFlags );
3301 else if ( rProp.Name.equals( sTextPath ) )
3303 uno::Sequence< beans::PropertyValue > aTextPathPropSeq;
3304 if ( rProp.Value >>= aTextPathPropSeq )
3306 sal_uInt32 nTextPathFlagsOrg, nTextPathFlags;
3307 nTextPathFlagsOrg = nTextPathFlags = 0xffff1000; // default
3308 if ( GetOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags ) )
3309 nTextPathFlagsOrg = nTextPathFlags;
3311 sal_Int32 r, nrCount = aTextPathPropSeq.getLength();
3312 for ( r = 0; r < nrCount; r++ )
3314 const beans::PropertyValue& rrProp = aTextPathPropSeq[ r ];
3315 const OUString sTextPathMode ( "TextPathMode" );
3316 const OUString sTextPathScaleX ( "ScaleX" );
3317 const OUString sSameLetterHeights ( "SameLetterHeights" );
3319 if ( rrProp.Name.equals( sTextPath ) )
3321 bool bTextPathOn;
3322 if ( rrProp.Value >>= bTextPathOn )
3324 nTextPathFlags |= 0x40000000;
3325 if ( bTextPathOn )
3327 nTextPathFlags |= 0x4000;
3329 sal_uInt32 nPathFlags = 0x39;
3330 GetOpt( DFF_Prop_fFillOK, nPathFlags ); // SJ: can be removed if we are supporting the TextPathAllowed property in XML
3331 nPathFlags |= 0x40004;
3332 AddOpt( DFF_Prop_fFillOK, nPathFlags );
3334 else
3335 nTextPathFlags &=~0x4000;
3338 else if ( rrProp.Name.equals( sTextPathMode ) )
3340 css::drawing::EnhancedCustomShapeTextPathMode eTextPathMode;
3341 if ( rrProp.Value >>= eTextPathMode )
3343 nTextPathFlags |= 0x05000000;
3344 nTextPathFlags &=~0x500; // TextPathMode_NORMAL
3345 if ( eTextPathMode == css::drawing::EnhancedCustomShapeTextPathMode_PATH )
3346 nTextPathFlags |= 0x100;
3347 else if ( eTextPathMode == css::drawing::EnhancedCustomShapeTextPathMode_SHAPE )
3348 nTextPathFlags |= 0x500;
3351 else if ( rrProp.Name.equals( sTextPathScaleX ) )
3353 bool bTextPathScaleX;
3354 if ( rrProp.Value >>= bTextPathScaleX )
3356 nTextPathFlags |= 0x00400000;
3357 if ( bTextPathScaleX )
3358 nTextPathFlags |= 0x40;
3359 else
3360 nTextPathFlags &=~0x40;
3363 else if ( rrProp.Name.equals( sSameLetterHeights ) )
3365 bool bSameLetterHeights;
3366 if ( rrProp.Value >>= bSameLetterHeights )
3368 nTextPathFlags |= 0x00800000;
3369 if ( bSameLetterHeights )
3370 nTextPathFlags |= 0x80;
3371 else
3372 nTextPathFlags &=~0x80;
3376 if ( nTextPathFlags & 0x4000 ) // Is FontWork ?
3378 // FontWork Text
3379 OUString aText;
3380 uno::Reference< text::XSimpleText > xText( rXShape, uno::UNO_QUERY );
3381 if ( xText.is() )
3382 aText = xText->getString();
3383 if ( aText.isEmpty() )
3384 aText = "your text"; // TODO: moving into a resource
3385 AddOpt( DFF_Prop_gtextUNICODE, aText );
3387 // FontWork Font
3388 OUString aFontName;
3389 const OUString sCharFontName( "CharFontName" );
3390 uno::Any aAny = aXPropSet->getPropertyValue( sCharFontName );
3391 aAny >>= aFontName;
3392 if ( aFontName.isEmpty() )
3393 aFontName = "Arial Black";
3394 AddOpt( DFF_Prop_gtextFont, aFontName );
3396 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharScaleWidth", true ) )
3398 sal_Int16 nCharScaleWidth = 100;
3399 if ( aAny >>= nCharScaleWidth )
3401 if ( nCharScaleWidth != 100 )
3403 sal_Int32 nVal = nCharScaleWidth * 655;
3404 AddOpt( DFF_Prop_gtextSpacing, nVal );
3408 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharHeight", true ) )
3410 float fCharHeight = 0.0;
3411 if ( aAny >>= fCharHeight )
3413 sal_Int32 nTextSize = static_cast< sal_Int32 > ( fCharHeight * 65536 );
3414 AddOpt(ESCHER_Prop_gtextSize, nTextSize);
3417 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharKerning", true ) )
3419 sal_Int16 nCharKerning = sal_Int16();
3420 if ( aAny >>= nCharKerning )
3422 nTextPathFlags |= 0x10000000;
3423 if ( nCharKerning )
3424 nTextPathFlags |= 0x1000;
3425 else
3426 nTextPathFlags &=~0x1000;
3429 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharPosture", true ) )
3431 awt::FontSlant eFontSlant;
3432 if ( aAny >>= eFontSlant )
3434 nTextPathFlags |= 0x100010;
3435 if ( eFontSlant != awt::FontSlant_NONE )
3436 nTextPathFlags |= 0x10;
3437 else
3438 nTextPathFlags &=~0x10;
3441 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharWeight", true ) )
3443 float fFontWidth = 0;
3444 if ( aAny >>= fFontWidth )
3446 nTextPathFlags |= 0x200020;
3447 if ( fFontWidth > awt::FontWeight::NORMAL )
3448 nTextPathFlags |= 0x20;
3449 else
3450 nTextPathFlags &=~0x20;
3453 // export gTextAlign attr
3454 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "TextHorizontalAdjust", true ) )
3456 MSO_GeoTextAlign gTextAlign = mso_alignTextCenter;
3457 drawing::TextHorizontalAdjust eHA( drawing::TextHorizontalAdjust_LEFT );
3458 aAny >>= eHA;
3459 switch( eHA )
3461 case drawing::TextHorizontalAdjust_LEFT :
3462 gTextAlign = mso_alignTextLeft;
3463 break;
3464 case drawing::TextHorizontalAdjust_CENTER:
3465 gTextAlign = mso_alignTextCenter;
3466 break;
3467 case drawing::TextHorizontalAdjust_RIGHT:
3468 gTextAlign = mso_alignTextRight;
3469 break;
3470 case drawing::TextHorizontalAdjust_BLOCK:
3472 SdrFitToSizeType eFTS( static_cast<const SdrTextFitToSizeTypeItem&>(pCustoShape->GetMergedItem( SDRATTR_TEXT_FITTOSIZE )).GetValue() );
3473 if (eFTS == SdrFitToSizeType::AllLines ||
3474 eFTS == SdrFitToSizeType::Proportional)
3476 gTextAlign = mso_alignTextStretch;
3478 else
3480 gTextAlign = mso_alignTextWordJust;
3482 break;
3484 default:
3485 break;
3487 AddOpt(DFF_Prop_gtextAlign,gTextAlign);
3490 if((nTextPathFlags & 0x4000) != 0) // Is Font work
3492 OutlinerParaObject* pOutlinerParaObject = pCustoShape->GetOutlinerParaObject();
3493 if ( pOutlinerParaObject && pOutlinerParaObject->IsVertical() )
3494 nTextPathFlags |= 0x2000;
3497 // Use gtextFStretch for Watermark like MSO does
3498 nTextPathFlags |= use_gtextFBestFit | gtextFBestFit
3499 | use_gtextFStretch | gtextFStretch
3500 | use_gtextFShrinkFit | gtextFShrinkFit;
3502 if ( nTextPathFlags != nTextPathFlagsOrg )
3503 AddOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags );
3506 else if ( rProp.Name.equals( sHandles ) )
3508 if ( !bIsDefaultObject )
3510 bPredefinedHandlesUsed = false;
3511 if ( rProp.Value >>= aHandlesPropSeq )
3513 sal_uInt16 nElements = (sal_uInt16)aHandlesPropSeq.getLength();
3514 if ( nElements )
3516 sal_uInt16 k, j, nElementSize = 36;
3517 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3518 SvMemoryStream aOut( nStreamSize );
3519 aOut.WriteUInt16( nElements )
3520 .WriteUInt16( nElements )
3521 .WriteUInt16( nElementSize );
3523 for ( k = 0; k < nElements; k++ )
3525 sal_uInt32 nFlags = 0;
3526 sal_Int32 nXPosition = 0;
3527 sal_Int32 nYPosition = 0;
3528 sal_Int32 nXMap = 0;
3529 sal_Int32 nYMap = 0;
3530 sal_Int32 nXRangeMin = 0x80000000;
3531 sal_Int32 nXRangeMax = 0x7fffffff;
3532 sal_Int32 nYRangeMin = 0x80000000;
3533 sal_Int32 nYRangeMax = 0x7fffffff;
3535 const uno::Sequence< beans::PropertyValue >& rPropSeq = aHandlesPropSeq[ k ];
3536 for ( j = 0; j < rPropSeq.getLength(); j++ )
3538 const beans::PropertyValue& rPropVal = rPropSeq[ j ];
3540 const OUString sPosition ( "Position" );
3541 const OUString sMirroredX ( "MirroredX" );
3542 const OUString sMirroredY ( "MirroredY" );
3543 const OUString sSwitched ( "Switched" );
3544 const OUString sPolar ( "Polar" );
3545 const OUString sRadiusRangeMinimum ( "RadiusRangeMinimum" );
3546 const OUString sRadiusRangeMaximum ( "RadiusRangeMaximum" );
3547 const OUString sRangeXMinimum ( "RangeXMinimum" );
3548 const OUString sRangeXMaximum ( "RangeXMaximum" );
3549 const OUString sRangeYMinimum ( "RangeYMinimum" );
3550 const OUString sRangeYMaximum ( "RangeYMaximum" );
3552 if ( rPropVal.Name.equals( sPosition ) )
3554 css::drawing::EnhancedCustomShapeParameterPair aPosition;
3555 if ( rPropVal.Value >>= aPosition )
3557 GetValueForEnhancedCustomShapeHandleParameter( nXPosition, aPosition.First );
3558 GetValueForEnhancedCustomShapeHandleParameter( nYPosition, aPosition.Second );
3561 else if ( rPropVal.Name.equals( sMirroredX ) )
3563 bool bMirroredX;
3564 if ( rPropVal.Value >>= bMirroredX )
3566 if ( bMirroredX )
3567 nFlags |= 1;
3570 else if ( rPropVal.Name.equals( sMirroredY ) )
3572 bool bMirroredY;
3573 if ( rPropVal.Value >>= bMirroredY )
3575 if ( bMirroredY )
3576 nFlags |= 2;
3579 else if ( rPropVal.Name.equals( sSwitched ) )
3581 bool bSwitched;
3582 if ( rPropVal.Value >>= bSwitched )
3584 if ( bSwitched )
3585 nFlags |= 4;
3588 else if ( rPropVal.Name.equals( sPolar ) )
3590 css::drawing::EnhancedCustomShapeParameterPair aPolar;
3591 if ( rPropVal.Value >>= aPolar )
3593 if ( GetValueForEnhancedCustomShapeHandleParameter( nXMap, aPolar.First ) )
3594 nFlags |= 0x800;
3595 if ( GetValueForEnhancedCustomShapeHandleParameter( nYMap, aPolar.Second ) )
3596 nFlags |= 0x1000;
3597 nFlags |= 8;
3600 else if ( rPropVal.Name.equals( sRadiusRangeMinimum ) )
3602 nYRangeMin = (sal_Int32)0xff4c0000; // the range of angles seems to be a not
3603 nYRangeMax = (sal_Int32)0x00b40000; // used feature, so we are defaulting this
3605 css::drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
3606 if ( rPropVal.Value >>= aRadiusRangeMinimum )
3608 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMin, aRadiusRangeMinimum ) )
3609 nFlags |= 0x80;
3610 nFlags |= 0x2000;
3613 else if ( rPropVal.Name.equals( sRadiusRangeMaximum ) )
3615 nYRangeMin = (sal_Int32)0xff4c0000; // the range of angles seems to be a not
3616 nYRangeMax = (sal_Int32)0x00b40000; // used feature, so we are defaulting this
3618 css::drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
3619 if ( rPropVal.Value >>= aRadiusRangeMaximum )
3621 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMax, aRadiusRangeMaximum ) )
3622 nFlags |= 0x100;
3623 nFlags |= 0x2000;
3626 else if ( rPropVal.Name.equals( sRangeXMinimum ) )
3628 css::drawing::EnhancedCustomShapeParameter aXRangeMinimum;
3629 if ( rPropVal.Value >>= aXRangeMinimum )
3631 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMin, aXRangeMinimum ) )
3632 nFlags |= 0x80;
3633 nFlags |= 0x20;
3636 else if ( rPropVal.Name.equals( sRangeXMaximum ) )
3638 css::drawing::EnhancedCustomShapeParameter aXRangeMaximum;
3639 if ( rPropVal.Value >>= aXRangeMaximum )
3641 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMax, aXRangeMaximum ) )
3642 nFlags |= 0x100;
3643 nFlags |= 0x20;
3646 else if ( rPropVal.Name.equals( sRangeYMinimum ) )
3648 css::drawing::EnhancedCustomShapeParameter aYRangeMinimum;
3649 if ( rPropVal.Value >>= aYRangeMinimum )
3651 if ( GetValueForEnhancedCustomShapeHandleParameter( nYRangeMin, aYRangeMinimum ) )
3652 nFlags |= 0x200;
3653 nFlags |= 0x20;
3656 else if ( rPropVal.Name.equals( sRangeYMaximum ) )
3658 css::drawing::EnhancedCustomShapeParameter aYRangeMaximum;
3659 if ( rPropVal.Value >>= aYRangeMaximum )
3661 if ( GetValueForEnhancedCustomShapeHandleParameter( nYRangeMax, aYRangeMaximum ) )
3662 nFlags |= 0x400;
3663 nFlags |= 0x20;
3667 aOut.WriteUInt32( nFlags )
3668 .WriteInt32( nXPosition )
3669 .WriteInt32( nYPosition )
3670 .WriteInt32( nXMap )
3671 .WriteInt32( nYMap )
3672 .WriteInt32( nXRangeMin )
3673 .WriteInt32( nXRangeMax )
3674 .WriteInt32( nYRangeMin )
3675 .WriteInt32( nYRangeMax );
3677 if ( nFlags & 8 )
3678 nAdjustmentsWhichNeedsToBeConverted |= ( 1 << ( nYPosition - 0x100 ) );
3680 sal_uInt8* pBuf = new sal_uInt8[ nStreamSize ];
3681 memcpy( pBuf, aOut.GetData(), nStreamSize );
3682 AddOpt( DFF_Prop_Handles, true, nStreamSize - 6, pBuf, nStreamSize );
3684 else
3686 sal_uInt8* pBuf = new sal_uInt8[ 1 ];
3687 AddOpt( DFF_Prop_Handles, true, 0, pBuf, 0 );
3692 else if ( rProp.Name.equals( sAdjustmentValues ) )
3694 // it is required, that the information which handle is polar has already be read,
3695 // so we are able to change the polar value to a fixed float
3696 aAdjustmentValuesProp = rProp.Value;
3697 bAdjustmentValuesProp = true;
3700 if ( bAdjustmentValuesProp )
3702 uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
3703 if ( aAdjustmentValuesProp >>= aAdjustmentSeq )
3705 if ( bPredefinedHandlesUsed )
3706 LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );
3708 sal_Int32 k, nValue = 0, nAdjustmentValues = aAdjustmentSeq.getLength();
3709 for ( k = 0; k < nAdjustmentValues; k++ )
3710 if( GetAdjustmentValue( aAdjustmentSeq[ k ], k, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
3711 AddOpt( (sal_uInt16)( DFF_Prop_adjustValue + k ), (sal_uInt32)nValue );
3714 if( bPathCoordinatesProp )
3716 css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aCoordinates;
3717 if ( aPathCoordinatesProp >>= aCoordinates )
3719 // creating the vertices
3720 if (aCoordinates.getLength() > 0)
3722 sal_uInt16 j, nElements = (sal_uInt16)aCoordinates.getLength();
3723 sal_uInt16 nElementSize = 8;
3724 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3725 SvMemoryStream aOut( nStreamSize );
3726 aOut.WriteUInt16( nElements )
3727 .WriteUInt16( nElements )
3728 .WriteUInt16( nElementSize );
3729 for( j = 0; j < nElements; j++ )
3731 sal_Int32 X = GetValueForEnhancedCustomShapeParameter( aCoordinates[ j ].First, aEquationOrder, true );
3732 sal_Int32 Y = GetValueForEnhancedCustomShapeParameter( aCoordinates[ j ].Second, aEquationOrder, true );
3733 aOut.WriteInt32( X )
3734 .WriteInt32( Y );
3736 sal_uInt8* pBuf = new sal_uInt8[ nStreamSize ];
3737 memcpy( pBuf, aOut.GetData(), nStreamSize );
3738 AddOpt( DFF_Prop_pVertices, true, nStreamSize - 6, pBuf, nStreamSize ); // -6
3740 else
3742 sal_uInt8* pBuf = new sal_uInt8[ 1 ];
3743 AddOpt( DFF_Prop_pVertices, true, 0, pBuf, 0 );
3751 MSO_SPT EscherPropertyContainer::GetCustomShapeType( const uno::Reference< drawing::XShape > & rXShape, sal_uInt32& nMirrorFlags, OUString& rShapeType, bool bOOXML )
3753 MSO_SPT eShapeType = mso_sptNil;
3754 nMirrorFlags = 0;
3755 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
3756 if ( aXPropSet.is() )
3760 const OUString sCustomShapeGeometry( "CustomShapeGeometry" );
3761 uno::Any aGeoPropSet = aXPropSet->getPropertyValue( sCustomShapeGeometry );
3762 uno::Sequence< beans::PropertyValue > aGeoPropSeq;
3763 if ( aGeoPropSet >>= aGeoPropSeq )
3765 sal_Int32 i, nCount = aGeoPropSeq.getLength();
3766 for ( i = 0; i < nCount; i++ )
3768 const beans::PropertyValue& rProp = aGeoPropSeq[ i ];
3769 if ( rProp.Name == "Type" )
3771 if ( rProp.Value >>= rShapeType )
3773 if (bOOXML)
3775 // In case of VML export, try to handle the
3776 // ooxml- prefix in rShapeType. If that fails,
3777 // just do the same as the binary export.
3778 OString aType = OUStringToOString(rShapeType, RTL_TEXTENCODING_UTF8);
3779 eShapeType = msfilter::util::GETVMLShapeType(aType);
3780 if (eShapeType == mso_sptNil)
3781 eShapeType = EnhancedCustomShapeTypeNames::Get(rShapeType);
3783 else
3784 eShapeType = EnhancedCustomShapeTypeNames::Get( rShapeType );
3787 else if ( rProp.Name == "MirroredX" )
3789 bool bMirroredX;
3790 if ( ( rProp.Value >>= bMirroredX ) && bMirroredX )
3791 nMirrorFlags |= SHAPEFLAG_FLIPH;
3793 else if ( rProp.Name == "MirroredY" )
3795 bool bMirroredY;
3796 if ( ( rProp.Value >>= bMirroredY ) && bMirroredY )
3797 nMirrorFlags |= SHAPEFLAG_FLIPV;
3802 catch( const css::uno::Exception& )
3806 return eShapeType;
3810 // Implement for form control export
3811 bool EscherPropertyContainer::CreateBlipPropertiesforOLEControl(const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, const css::uno::Reference< css::drawing::XShape > & rXShape)
3813 SdrObject* pShape = GetSdrObjectFromXShape( rXShape );
3814 if ( pShape )
3816 SdrModel* pMod = pShape->GetModel();
3817 Graphic aGraphic(SdrExchangeView::GetObjGraphic( pMod, pShape));
3819 GraphicObject aGraphicObject = aGraphic;
3820 OString aUniqueId = aGraphicObject.GetUniqueID();
3821 if ( aUniqueId.getLength() )
3823 if ( pGraphicProvider && pPicOutStrm && pShapeBoundRect )
3825 Rectangle aRect( Point( 0, 0 ), pShapeBoundRect->GetSize() );
3827 sal_uInt32 nBlibId = pGraphicProvider->GetBlibID( *pPicOutStrm, aUniqueId, aRect );
3828 if ( nBlibId )
3830 AddOpt( ESCHER_Prop_pib, nBlibId, true );
3831 ImplCreateGraphicAttributes( rXPropSet, nBlibId, false );
3832 return true;
3838 return false;
3841 EscherPersistTable::EscherPersistTable()
3845 EscherPersistTable::~EscherPersistTable()
3847 for(EscherPersistEntry* i : maPersistTable) {
3848 delete i;
3852 bool EscherPersistTable::PtIsID( sal_uInt32 nID )
3854 for(EscherPersistEntry* pPtr : maPersistTable) {
3855 if ( pPtr->mnID == nID ) {
3856 return true;
3859 return false;
3862 void EscherPersistTable::PtInsert( sal_uInt32 nID, sal_uInt32 nOfs )
3864 maPersistTable.push_back( new EscherPersistEntry( nID, nOfs ) );
3867 void EscherPersistTable::PtDelete( sal_uInt32 nID )
3869 ::std::vector< EscherPersistEntry* >::iterator it = maPersistTable.begin();
3870 for( ; it != maPersistTable.end() ; ++it )
3872 if ( (*it)->mnID == nID ) {
3873 delete *it;
3874 maPersistTable.erase( it );
3875 break;
3880 sal_uInt32 EscherPersistTable::PtGetOffsetByID( sal_uInt32 nID )
3882 for(EscherPersistEntry* pPtr : maPersistTable) {
3883 if ( pPtr->mnID == nID ) {
3884 return pPtr->mnOffset;
3887 return 0;
3890 void EscherPersistTable::PtReplace( sal_uInt32 nID, sal_uInt32 nOfs )
3892 for(EscherPersistEntry* pPtr : maPersistTable) {
3893 if ( pPtr->mnID == nID ) {
3894 pPtr->mnOffset = nOfs;
3895 return;
3900 void EscherPersistTable::PtReplaceOrInsert( sal_uInt32 nID, sal_uInt32 nOfs )
3902 for(EscherPersistEntry* pPtr : maPersistTable) {
3903 if ( pPtr->mnID == nID ) {
3904 pPtr->mnOffset = nOfs;
3905 return;
3908 PtInsert( nID, nOfs );
3911 bool EscherPropertyValueHelper::GetPropertyValue(
3912 css::uno::Any& rAny,
3913 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
3914 const OUString& rString,
3915 bool bTestPropertyAvailability)
3917 bool bRetValue = true;
3918 if ( bTestPropertyAvailability )
3920 bRetValue = false;
3923 css::uno::Reference< css::beans::XPropertySetInfo >
3924 aXPropSetInfo( rXPropSet->getPropertySetInfo() );
3925 if ( aXPropSetInfo.is() )
3926 bRetValue = aXPropSetInfo->hasPropertyByName( rString );
3928 catch( const css::uno::Exception& )
3930 bRetValue = false;
3933 if ( bRetValue )
3937 rAny = rXPropSet->getPropertyValue( rString );
3938 if ( !rAny.hasValue() )
3939 bRetValue = false;
3941 catch( const css::uno::Exception& )
3943 bRetValue = false;
3946 return bRetValue;
3949 css::beans::PropertyState EscherPropertyValueHelper::GetPropertyState(
3950 const css::uno::Reference< css::beans::XPropertySet > & rXPropSet,
3951 const OUString& rPropertyName )
3953 css::beans::PropertyState eRetValue = css::beans::PropertyState_AMBIGUOUS_VALUE;
3956 css::uno::Reference< css::beans::XPropertyState > aXPropState
3957 ( rXPropSet, css::uno::UNO_QUERY );
3958 if ( aXPropState.is() )
3959 eRetValue = aXPropState->getPropertyState( rPropertyName );
3961 catch( const css::uno::Exception& )
3964 return eRetValue;
3967 EscherBlibEntry::EscherBlibEntry( sal_uInt32 nPictureOffset, const GraphicObject& rObject, const OString& rId,
3968 const GraphicAttr* pGraphicAttr ) :
3969 mnPictureOffset ( nPictureOffset ),
3970 mnRefCount ( 1 ),
3971 mnSizeExtra ( 0 ),
3972 maPrefSize ( rObject.GetPrefSize() ),
3973 maPrefMapMode ( rObject.GetPrefMapMode() ),
3974 mbIsEmpty ( true )
3976 mbIsNativeGraphicPossible = ( pGraphicAttr == nullptr );
3977 meBlibType = UNKNOWN;
3978 mnSize = 0;
3980 sal_uInt32 nLen = static_cast<sal_uInt32>(rId.getLength());
3981 const sal_Char* pData = rId.getStr();
3982 GraphicType eType( rObject.GetType() );
3983 if ( nLen && pData && ( eType != GraphicType::NONE ) )
3985 mnIdentifier[ 0 ] = rtl_crc32( 0,pData, nLen );
3986 mnIdentifier[ 1 ] = 0;
3988 if ( pGraphicAttr )
3990 if ( pGraphicAttr->IsSpecialDrawMode()
3991 || pGraphicAttr->IsMirrored()
3992 || pGraphicAttr->IsCropped()
3993 || pGraphicAttr->IsRotated()
3994 || pGraphicAttr->IsTransparent()
3995 || pGraphicAttr->IsAdjusted() )
3997 SvMemoryStream aSt( sizeof( GraphicAttr ) );
3998 aSt.WriteUInt16( pGraphicAttr->GetDrawMode() )
3999 .WriteUInt32( static_cast<sal_uInt32>(pGraphicAttr->GetMirrorFlags()) )
4000 .WriteInt32( pGraphicAttr->GetLeftCrop() )
4001 .WriteInt32( pGraphicAttr->GetTopCrop() )
4002 .WriteInt32( pGraphicAttr->GetRightCrop() )
4003 .WriteInt32( pGraphicAttr->GetBottomCrop() )
4004 .WriteUInt16( pGraphicAttr->GetRotation() )
4005 .WriteInt16( pGraphicAttr->GetLuminance() )
4006 .WriteInt16( pGraphicAttr->GetContrast() )
4007 .WriteInt16( pGraphicAttr->GetChannelR() )
4008 .WriteInt16( pGraphicAttr->GetChannelG() )
4009 .WriteInt16( pGraphicAttr->GetChannelB() )
4010 .WriteDouble( pGraphicAttr->GetGamma() );
4011 aSt.WriteBool( pGraphicAttr->IsInvert() )
4012 .WriteUChar( pGraphicAttr->GetTransparency() );
4013 mnIdentifier[ 1 ] = rtl_crc32( 0, aSt.GetData(), aSt.Tell() );
4015 else
4016 mbIsNativeGraphicPossible = true;
4018 sal_uInt32 i, nTmp, n1, n2;
4019 n1 = n2 = 0;
4020 for ( i = 0; i < nLen; i++ )
4022 nTmp = n2 >> 28; // rotating 4 bit
4023 n2 <<= 4;
4024 n2 |= n1 >> 28;
4025 n1 <<= 4;
4026 n1 |= nTmp;
4027 n1 ^= *pData++ - '0';
4029 mnIdentifier[ 2 ] = n1;
4030 mnIdentifier[ 3 ] = n2;
4031 mbIsEmpty = false;
4035 void EscherBlibEntry::WriteBlibEntry( SvStream& rSt, bool bWritePictureOffset, sal_uInt32 nResize )
4037 sal_uInt32 nPictureOffset = ( bWritePictureOffset ) ? mnPictureOffset : 0;
4039 rSt.WriteUInt32( ( ESCHER_BSE << 16 ) | ( ( (sal_uInt16)meBlibType << 4 ) | 2 ) )
4040 .WriteUInt32( 36 + nResize )
4041 .WriteUChar( meBlibType );
4043 switch ( meBlibType )
4045 case EMF :
4046 case WMF : // converting EMF/WMF on OS2 to Pict
4047 rSt.WriteUChar( PICT );
4048 break;
4049 default:
4050 rSt.WriteUChar( meBlibType );
4053 rSt.WriteBytes(&mnIdentifier[0], 16);
4054 rSt.WriteUInt16( 0 )
4055 .WriteUInt32( mnSize + mnSizeExtra )
4056 .WriteUInt32( mnRefCount )
4057 .WriteUInt32( nPictureOffset )
4058 .WriteUInt32( 0 );
4061 EscherBlibEntry::~EscherBlibEntry()
4065 bool EscherBlibEntry::operator==( const EscherBlibEntry& rEscherBlibEntry ) const
4067 for ( int i = 0; i < 3; i++ )
4069 if ( mnIdentifier[ i ] != rEscherBlibEntry.mnIdentifier[ i ] )
4070 return false;
4072 return true;
4075 EscherGraphicProvider::EscherGraphicProvider( sal_uInt32 nFlags ) :
4076 mnFlags ( nFlags ),
4077 mpBlibEntrys ( nullptr ),
4078 mnBlibBufSize ( 0 ),
4079 mnBlibEntrys ( 0 )
4083 EscherGraphicProvider::~EscherGraphicProvider()
4085 for ( sal_uInt32 i = 0; i < mnBlibEntrys; delete mpBlibEntrys[ i++ ] ) ;
4086 delete[] mpBlibEntrys;
4089 void EscherGraphicProvider::SetNewBlipStreamOffset( sal_Int32 nOffset )
4091 for( sal_uInt32 i = 0; i < mnBlibEntrys; i++ )
4093 EscherBlibEntry* pBlibEntry = mpBlibEntrys[ i ];
4094 pBlibEntry->mnPictureOffset += nOffset;
4098 sal_uInt32 EscherGraphicProvider::ImplInsertBlib( EscherBlibEntry* p_EscherBlibEntry )
4100 if ( mnBlibBufSize == mnBlibEntrys )
4102 mnBlibBufSize += 64;
4103 EscherBlibEntry** pTemp = new EscherBlibEntry*[ mnBlibBufSize ];
4104 for ( sal_uInt32 i = 0; i < mnBlibEntrys; i++ )
4106 pTemp[ i ] = mpBlibEntrys[ i ];
4108 delete[] mpBlibEntrys;
4109 mpBlibEntrys = pTemp;
4111 mpBlibEntrys[ mnBlibEntrys++ ] = p_EscherBlibEntry;
4112 return mnBlibEntrys;
4115 sal_uInt32 EscherGraphicProvider::GetBlibStoreContainerSize( SvStream* pMergePicStreamBSE ) const
4117 sal_uInt32 nSize = 44 * mnBlibEntrys + 8;
4118 if ( pMergePicStreamBSE )
4120 for ( sal_uInt32 i = 0; i < mnBlibEntrys; i++ )
4121 nSize += mpBlibEntrys[ i ]->mnSize + mpBlibEntrys[ i ]->mnSizeExtra;
4123 return nSize;
4126 void EscherGraphicProvider::WriteBlibStoreEntry(SvStream& rSt,
4127 sal_uInt32 nBlipId, bool bWritePictureOffSet, sal_uInt32 nResize)
4129 if (nBlipId > mnBlibEntrys || nBlipId == 0)
4130 return;
4131 mpBlibEntrys[nBlipId-1]->WriteBlibEntry(rSt, bWritePictureOffSet, nResize);
4134 void EscherGraphicProvider::WriteBlibStoreContainer( SvStream& rSt, SvStream* pMergePicStreamBSE )
4136 sal_uInt32 nSize = GetBlibStoreContainerSize( pMergePicStreamBSE );
4137 if ( nSize )
4139 rSt.WriteUInt32( ( ESCHER_BstoreContainer << 16 ) | 0x1f )
4140 .WriteUInt32( nSize - 8 );
4142 if ( pMergePicStreamBSE )
4144 sal_uInt32 i, nBlipSize, nOldPos = pMergePicStreamBSE->Tell();
4145 const sal_uInt32 nBuf = 0x40000; // 256KB buffer
4146 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ nBuf ]);
4148 for ( i = 0; i < mnBlibEntrys; i++ )
4150 EscherBlibEntry* pBlibEntry = mpBlibEntrys[ i ];
4152 ESCHER_BlibType nBlibType = pBlibEntry->meBlibType;
4153 nBlipSize = pBlibEntry->mnSize + pBlibEntry->mnSizeExtra;
4154 pBlibEntry->WriteBlibEntry( rSt, false, nBlipSize );
4156 // BLIP
4157 pMergePicStreamBSE->Seek( pBlibEntry->mnPictureOffset );
4158 sal_uInt16 n16;
4159 // record version and instance
4160 pMergePicStreamBSE->ReadUInt16( n16 );
4161 rSt.WriteUInt16( n16 );
4162 // record type
4163 pMergePicStreamBSE->ReadUInt16( n16 );
4164 rSt.WriteUInt16( ESCHER_BlipFirst + nBlibType );
4165 DBG_ASSERT( n16 == ESCHER_BlipFirst + nBlibType , "EscherGraphicProvider::WriteBlibStoreContainer: BLIP record types differ" );
4166 sal_uInt32 n32;
4167 // record size
4168 pMergePicStreamBSE->ReadUInt32( n32 );
4169 nBlipSize -= 8;
4170 rSt.WriteUInt32( nBlipSize );
4171 DBG_ASSERT( nBlipSize == n32, "EscherGraphicProvider::WriteBlibStoreContainer: BLIP sizes differ" );
4172 // record
4173 while ( nBlipSize )
4175 sal_uInt32 nBytes = ( nBlipSize > nBuf ? nBuf : nBlipSize );
4176 pMergePicStreamBSE->ReadBytes(pBuf.get(), nBytes);
4177 rSt.WriteBytes(pBuf.get(), nBytes);
4178 nBlipSize -= nBytes;
4181 pMergePicStreamBSE->Seek( nOldPos );
4183 else
4185 for ( sal_uInt32 i = 0; i < mnBlibEntrys; i++ )
4186 mpBlibEntrys[ i ]->WriteBlibEntry( rSt, true );
4191 bool EscherGraphicProvider::GetPrefSize( const sal_uInt32 nBlibId, Size& rPrefSize, MapMode& rPrefMapMode )
4193 bool bInRange = nBlibId && ( ( nBlibId - 1 ) < mnBlibEntrys );
4194 if ( bInRange )
4196 EscherBlibEntry* pEntry = mpBlibEntrys[ nBlibId - 1 ];
4197 rPrefSize = pEntry->maPrefSize;
4198 rPrefMapMode = pEntry->maPrefMapMode;
4200 return bInRange;
4203 sal_uInt32 EscherGraphicProvider::GetBlibID( SvStream& rPicOutStrm, const OString& rId,
4204 const Rectangle& /* rBoundRect */, const css::awt::Rectangle* pVisArea,
4205 const GraphicAttr* pGraphicAttr, const bool bOOxmlExport )
4207 sal_uInt32 nBlibId = 0;
4208 GraphicObject aGraphicObject( rId );
4210 EscherBlibEntry* p_EscherBlibEntry = new EscherBlibEntry( rPicOutStrm.Tell(), aGraphicObject, rId, pGraphicAttr );
4211 if ( !p_EscherBlibEntry->IsEmpty() )
4213 for ( sal_uInt32 i = 0; i < mnBlibEntrys; i++ )
4215 if ( *( mpBlibEntrys[ i ] ) == *p_EscherBlibEntry )
4217 mpBlibEntrys[ i ]->mnRefCount++;
4218 delete p_EscherBlibEntry;
4219 return i + 1;
4223 bool bUseNativeGraphic( false );
4225 Graphic aGraphic( aGraphicObject.GetTransformedGraphic( pGraphicAttr ) );
4226 GfxLink aGraphicLink;
4227 SvMemoryStream aStream;
4229 const sal_uInt8* pGraphicAry = nullptr;
4231 if ( p_EscherBlibEntry->mbIsNativeGraphicPossible && aGraphic.IsLink() )
4233 aGraphicLink = aGraphic.GetLink();
4235 p_EscherBlibEntry->mnSize = aGraphicLink.GetDataSize();
4236 pGraphicAry = aGraphicLink.GetData();
4238 if ( p_EscherBlibEntry->mnSize && pGraphicAry )
4240 switch ( aGraphicLink.GetType() )
4242 case GfxLinkType::NativeJpg : p_EscherBlibEntry->meBlibType = PEG; break;
4243 case GfxLinkType::NativePng : p_EscherBlibEntry->meBlibType = PNG; break;
4245 // #i15508# added BMP type for better exports; need to check this
4246 // checked - does not work that way, so keep out for now. It may
4247 // work somehow with direct DIB data, but that would need to be checked
4248 // carefully
4249 // for more comments please check RtfAttributeOutput::FlyFrameGraphic
4251 // case GfxLinkType::NativeBmp : p_EscherBlibEntry->meBlibType = DIB; break;
4253 case GfxLinkType::NativeWmf :
4255 if ( pGraphicAry && ( p_EscherBlibEntry->mnSize > 0x2c ) )
4257 if ( ( pGraphicAry[ 0x28 ] == 0x20 ) && ( pGraphicAry[ 0x29 ] == 0x45 ) // check the magic
4258 && ( pGraphicAry[ 0x2a ] == 0x4d ) && ( pGraphicAry[ 0x2b ] == 0x46 ) ) // number ( emf detection )
4260 p_EscherBlibEntry->meBlibType = EMF;
4262 else
4264 p_EscherBlibEntry->meBlibType = WMF;
4265 if ( ( pGraphicAry[ 0 ] == 0xd7 ) && ( pGraphicAry[ 1 ] == 0xcd )
4266 && ( pGraphicAry[ 2 ] == 0xc6 ) && ( pGraphicAry[ 3 ] == 0x9a ) )
4267 { // we have to get rid of the metafileheader
4268 pGraphicAry += 22;
4269 p_EscherBlibEntry->mnSize -= 22;
4274 break;
4275 default: break;
4277 if ( p_EscherBlibEntry->meBlibType != UNKNOWN )
4278 bUseNativeGraphic = true;
4281 if ( !bUseNativeGraphic )
4283 GraphicType eGraphicType = aGraphic.GetType();
4284 if ( ( eGraphicType == GraphicType::Bitmap ) || ( eGraphicType == GraphicType::GdiMetafile ) )
4286 sal_uInt32 nErrCode;
4287 if ( !aGraphic.IsAnimated() )
4288 nErrCode = GraphicConverter::Export( aStream, aGraphic, ( eGraphicType == GraphicType::Bitmap ) ? ConvertDataFormat::PNG : ConvertDataFormat::EMF );
4289 else
4290 { // to store a animation, a gif has to be included into the msOG chunk of a png #I5583#
4291 GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
4292 SvMemoryStream aGIFStream;
4293 const char* pString = "MSOFFICE9.0";
4294 aGIFStream.WriteBytes(pString, strlen(pString));
4295 nErrCode = rFilter.ExportGraphic( aGraphic, OUString(), aGIFStream,
4296 rFilter.GetExportFormatNumberForShortName( "GIF" ) );
4297 SAL_WARN_IF(
4298 nErrCode != ERRCODE_NONE, "filter.ms",
4299 "ExportGraphic to GIF failed with " << nErrCode);
4300 if (nErrCode == ERRCODE_NONE)
4302 css::uno::Sequence< css::beans::PropertyValue > aFilterData( 1 );
4303 css::uno::Sequence< css::beans::PropertyValue > aAdditionalChunkSequence( 1 );
4304 sal_uInt32 nGIFSreamLen = aGIFStream.Tell();
4305 css::uno::Sequence< sal_Int8 > aGIFSeq( nGIFSreamLen );
4306 sal_Int8* pSeq = aGIFSeq.getArray();
4307 aGIFStream.Seek( STREAM_SEEK_TO_BEGIN );
4308 aGIFStream.ReadBytes(pSeq, nGIFSreamLen);
4309 css::beans::PropertyValue aChunkProp, aFilterProp;
4310 aChunkProp.Name = "msOG";
4311 aChunkProp.Value <<= aGIFSeq;
4312 aAdditionalChunkSequence[ 0 ] = aChunkProp;
4313 aFilterProp.Name = "AdditionalChunks";
4314 aFilterProp.Value <<= aAdditionalChunkSequence;
4315 aFilterData[ 0 ] = aFilterProp;
4316 nErrCode = rFilter.ExportGraphic( aGraphic, OUString(), aStream,
4317 rFilter.GetExportFormatNumberForShortName( "PNG" ), &aFilterData );
4320 if ( nErrCode == ERRCODE_NONE )
4322 p_EscherBlibEntry->meBlibType = ( eGraphicType == GraphicType::Bitmap ) ? PNG : EMF;
4323 aStream.Seek( STREAM_SEEK_TO_END );
4324 p_EscherBlibEntry->mnSize = aStream.Tell();
4325 pGraphicAry = static_cast<sal_uInt8 const *>(aStream.GetData());
4330 ESCHER_BlibType eBlibType = p_EscherBlibEntry->meBlibType;
4331 if ( p_EscherBlibEntry->mnSize && pGraphicAry && ( eBlibType != UNKNOWN ) )
4333 sal_uInt32 nExtra, nAtomSize = 0;
4334 sal_uInt32 nInstance, nUncompressedSize = p_EscherBlibEntry->mnSize;
4336 if ( mnFlags & E_GRAPH_PROV_USE_INSTANCES )
4338 rPicOutStrm.WriteUInt32( 0x7f90000 | (sal_uInt16)( mnBlibEntrys << 4 ) )
4339 .WriteUInt32( 0 );
4340 nAtomSize = rPicOutStrm.Tell();
4341 if ( eBlibType == PNG )
4342 rPicOutStrm.WriteUInt16( 0x0606 );
4343 else if ( eBlibType == WMF )
4344 rPicOutStrm.WriteUInt16( 0x0403 );
4345 else if ( eBlibType == EMF )
4346 rPicOutStrm.WriteUInt16( 0x0402 );
4347 else if ( eBlibType == PEG )
4348 rPicOutStrm.WriteUInt16( 0x0505 );
4351 // fdo#69607 do not compress WMF files if we are in OOXML export
4352 if ( ( eBlibType == PEG ) || ( eBlibType == PNG ) // || ( eBlibType == DIB )) // #i15508#
4353 || ( ( ( eBlibType == WMF ) || ( eBlibType == EMF ) ) && bOOxmlExport ) )
4355 nExtra = 17;
4356 p_EscherBlibEntry->mnSizeExtra = nExtra + 8;
4358 // #i15508# type see SvxMSDffManager::GetBLIPDirect (checked, does not work this way)
4359 // see RtfAttributeOutput::FlyFrameGraphic for more comments
4360 // maybe it would work with direct DIB data, but that would need thorough testing
4361 if( eBlibType == PNG )
4363 nInstance = 0xf01e6e00;
4365 else // if( eBlibType == PEG )
4367 nInstance = 0xf01d46a0;
4369 //else // eBlibType == DIB
4371 // nInstance = 0xf01d7A80;
4374 // #i15508#
4375 //nInstance = ( eBlibType == PNG ) ? 0xf01e6e00 : 0xf01d46a0;
4378 rPicOutStrm.WriteUInt32( nInstance ).WriteUInt32( p_EscherBlibEntry->mnSize + nExtra );
4379 rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16);
4380 rPicOutStrm.WriteUChar( 0xff );
4381 rPicOutStrm.WriteBytes(pGraphicAry, p_EscherBlibEntry->mnSize);
4383 else
4385 ZCodec aZCodec( 0x8000, 0x8000 );
4386 aZCodec.BeginCompression();
4387 SvMemoryStream aDestStrm;
4388 aZCodec.Write( aDestStrm, pGraphicAry, p_EscherBlibEntry->mnSize );
4389 aZCodec.EndCompression();
4390 aDestStrm.Seek( STREAM_SEEK_TO_END );
4391 p_EscherBlibEntry->mnSize = aDestStrm.Tell();
4392 pGraphicAry = static_cast<sal_uInt8 const *>(aDestStrm.GetData());
4393 if ( p_EscherBlibEntry->mnSize && pGraphicAry )
4395 nExtra = eBlibType == WMF ? 0x42 : 0x32; // !EMF -> no change
4396 p_EscherBlibEntry->mnSizeExtra = nExtra + 8;
4397 nInstance = ( eBlibType == WMF ) ? 0xf01b2170 : 0xf01a3d40; // !EMF -> no change
4398 rPicOutStrm.WriteUInt32( nInstance ).WriteUInt32( p_EscherBlibEntry->mnSize + nExtra );
4399 if ( eBlibType == WMF ) // !EMF -> no change
4400 rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16);
4401 rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16);
4404 ##913##
4405 For Word the stored size of the graphic is critical the
4406 metafile boundaries must match the actual graphics
4407 boundaries, and the width and height must be in EMU's
4409 If you don't do it this way then objects edited in the
4410 msoffice app may show strange behaviour as the size jumps
4411 around, and the original size and scaling factor in word
4412 will be a very strange figure
4414 sal_uInt32 nPrefWidth = p_EscherBlibEntry->maPrefSize.Width();
4415 sal_uInt32 nPrefHeight = p_EscherBlibEntry->maPrefSize.Height();
4416 sal_uInt32 nWidth, nHeight;
4417 if ( pVisArea )
4419 nWidth = pVisArea->Width * 360;
4420 nHeight = pVisArea->Height * 360;
4422 else
4424 Size aPrefSize(lcl_SizeToEmu(p_EscherBlibEntry->maPrefSize, p_EscherBlibEntry->maPrefMapMode));
4425 nWidth = aPrefSize.Width() * 360;
4426 nHeight = aPrefSize.Height() * 360;
4428 rPicOutStrm.WriteUInt32( nUncompressedSize ) // WMFSize without FileHeader
4429 .WriteInt32( 0 ) // since we can't find out anymore what the original size of
4430 .WriteInt32( 0 ) // the WMF (without Fileheader) was we write 10cm / x
4431 .WriteUInt32( nPrefWidth )
4432 .WriteUInt32( nPrefHeight )
4433 .WriteUInt32( nWidth )
4434 .WriteUInt32( nHeight )
4435 .WriteUInt32( p_EscherBlibEntry->mnSize )
4436 .WriteUInt16( 0xfe00 ); // compression Flags
4437 rPicOutStrm.WriteBytes(pGraphicAry, p_EscherBlibEntry->mnSize);
4440 if ( nAtomSize )
4442 sal_uInt32 nPos = rPicOutStrm.Tell();
4443 rPicOutStrm.Seek( nAtomSize - 4 );
4444 rPicOutStrm.WriteUInt32( nPos - nAtomSize );
4445 rPicOutStrm.Seek( nPos );
4447 nBlibId = ImplInsertBlib( p_EscherBlibEntry );
4448 p_EscherBlibEntry = nullptr;
4451 delete p_EscherBlibEntry;
4452 return nBlibId;
4455 struct EscherConnectorRule
4457 sal_uInt32 nRuleId;
4458 sal_uInt32 nShapeA; // SPID of shape A
4459 sal_uInt32 nShapeB; // SPID of shape B
4460 sal_uInt32 nShapeC; // SPID of connector shape
4461 sal_uInt32 ncptiA; // Connection site Index of shape A
4462 sal_uInt32 ncptiB; // Connection site Index of shape B
4465 struct EscherShapeListEntry
4467 css::uno::Reference< css::drawing::XShape > aXShape;
4468 sal_uInt32 n_EscherId;
4470 EscherShapeListEntry( const css::uno::Reference
4471 < css::drawing::XShape > & rShape, sal_uInt32 nId ) :
4472 aXShape ( rShape ),
4473 n_EscherId ( nId ) {}
4476 sal_uInt32 EscherConnectorListEntry::GetClosestPoint( const tools::Polygon& rPoly, const css::awt::Point& rPoint )
4478 sal_uInt16 nCount = rPoly.GetSize();
4479 sal_uInt16 nClosest = nCount;
4480 double fDist = (sal_uInt32)0xffffffff;
4481 while( nCount-- )
4483 double fDistance = hypot( rPoint.X - rPoly[ nCount ].X(), rPoint.Y - rPoly[ nCount ].Y() );
4484 if ( fDistance < fDist )
4486 nClosest = nCount;
4487 fDist = fDistance;
4490 return nClosest;
4494 // for rectangles for ellipses for polygons
4496 // nRule = 0 ->Top 0 ->Top nRule = Index auf ein (Poly)Polygon Punkt
4497 // 1 ->Left 2 ->Left
4498 // 2 ->Bottom 4 ->Bottom
4499 // 3 ->Right 6 ->Right
4501 sal_uInt32 EscherConnectorListEntry::GetConnectorRule( bool bFirst )
4503 sal_uInt32 nRule = 0;
4505 css::uno::Any aAny;
4506 css::awt::Point aRefPoint( ( bFirst ) ? maPointA : maPointB );
4507 css::uno::Reference< css::drawing::XShape >
4508 aXShape( ( bFirst ) ? mXConnectToA : mXConnectToB );
4510 OUString aString(aXShape->getShapeType());
4511 OStringBuffer aBuf(OUStringToOString(aString, RTL_TEXTENCODING_UTF8));
4512 aBuf.remove( 0, 13 ); // removing "com.sun.star."
4513 sal_Int16 nPos = aBuf.toString().indexOf("Shape");
4514 aBuf.remove(nPos, 5);
4515 OString aType = aBuf.makeStringAndClear();
4517 css::uno::Reference< css::beans::XPropertySet >
4518 aPropertySet( aXShape, css::uno::UNO_QUERY );
4520 if ((aType == OString( "drawing.PolyPolygon" )) || (aType == OString( "drawing.PolyLine" )))
4522 if ( aPropertySet.is() )
4524 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet, "PolyPolygon" ) )
4526 auto pSourcePolyPolygon =
4527 o3tl::doAccess<css::drawing::PointSequenceSequence>(aAny);
4528 sal_Int32 nOuterSequenceCount = pSourcePolyPolygon->getLength();
4529 css::drawing::PointSequence const * pOuterSequence = pSourcePolyPolygon->getConstArray();
4531 if ( pOuterSequence )
4533 sal_Int32 a, b, nIndex = 0;
4534 sal_uInt32 nDistance = 0xffffffff;
4535 for( a = 0; a < nOuterSequenceCount; a++ )
4537 css::drawing::PointSequence const * pInnerSequence = pOuterSequence++;
4538 if ( pInnerSequence )
4540 css::awt::Point const * pArray = pInnerSequence->getConstArray();
4541 if ( pArray )
4543 for ( b = 0; b < pInnerSequence->getLength(); b++, nIndex++, pArray++ )
4545 sal_uInt32 nDist = (sal_uInt32)hypot( aRefPoint.X - pArray->X, aRefPoint.Y - pArray->Y );
4546 if ( nDist < nDistance )
4548 nRule = nIndex;
4549 nDistance = nDist;
4559 else if ((aType == OString( "drawing.OpenBezier" )) || (aType == OString( "drawing.OpenFreeHand" )) || (aType == OString( "drawing.PolyLinePath" ))
4560 || (aType == OString( "drawing.ClosedBezier" )) || ( aType == OString( "drawing.ClosedFreeHand" )) || (aType == OString( "drawing.PolyPolygonPath" )) )
4562 css::uno::Reference< css::beans::XPropertySet >
4563 aPropertySet2( aXShape, css::uno::UNO_QUERY );
4564 if ( aPropertySet2.is() )
4566 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet2, "PolyPolygonBezier" ) )
4568 auto pSourcePolyPolygon =
4569 o3tl::doAccess<css::drawing::PolyPolygonBezierCoords>(aAny);
4570 sal_Int32 nOuterSequenceCount = pSourcePolyPolygon->Coordinates.getLength();
4572 // Zeiger auf innere sequences holen
4573 css::drawing::PointSequence const * pOuterSequence =
4574 pSourcePolyPolygon->Coordinates.getConstArray();
4575 css::drawing::FlagSequence const * pOuterFlags =
4576 pSourcePolyPolygon->Flags.getConstArray();
4578 if ( pOuterSequence && pOuterFlags )
4580 sal_Int32 a, b, nIndex = 0;
4581 sal_uInt32 nDistance = 0xffffffff;
4583 for ( a = 0; a < nOuterSequenceCount; a++ )
4585 css::drawing::PointSequence const * pInnerSequence = pOuterSequence++;
4586 css::drawing::FlagSequence const * pInnerFlags = pOuterFlags++;
4587 if ( pInnerSequence && pInnerFlags )
4589 css::awt::Point const * pArray = pInnerSequence->getConstArray();
4590 css::drawing::PolygonFlags const * pFlags = pInnerFlags->getConstArray();
4591 if ( pArray && pFlags )
4593 for ( b = 0; b < pInnerSequence->getLength(); b++, pArray++ )
4595 css::drawing::PolygonFlags ePolyFlags = *pFlags++;
4596 if ( ePolyFlags == css::drawing::PolygonFlags_CONTROL )
4597 continue;
4598 sal_uInt32 nDist = (sal_uInt32)hypot( aRefPoint.X - pArray->X, aRefPoint.Y - pArray->Y );
4599 if ( nDist < nDistance )
4601 nRule = nIndex;
4602 nDistance = nDist;
4604 nIndex++;
4613 else
4615 bool bRectangularConnection = true;
4617 if (aType == "drawing.Custom")
4619 SdrObject* pCustoShape( GetSdrObjectFromXShape( aXShape ) );
4620 if ( dynamic_cast<const SdrObjCustomShape* >(pCustoShape) != nullptr )
4622 const SdrCustomShapeGeometryItem& rGeometryItem = static_cast<const SdrCustomShapeGeometryItem&>(
4623 pCustoShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
4625 const OUString sPath( "Path" );
4626 const OUString sType( "Type" );
4627 const OUString sGluePointType( "GluePointType" );
4629 OUString sShapeType;
4630 const uno::Any* pType = rGeometryItem.GetPropertyValueByName( sType );
4631 if ( pType )
4632 *pType >>= sShapeType;
4633 MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
4635 uno::Any* pGluePointType = ((SdrCustomShapeGeometryItem&)rGeometryItem).GetPropertyValueByName( sPath, sGluePointType );
4637 sal_Int16 nGluePointType = sal_Int16();
4638 if ( !( pGluePointType &&
4639 ( *pGluePointType >>= nGluePointType ) ) )
4640 nGluePointType = GetCustomShapeConnectionTypeDefault( eSpType );
4642 if ( nGluePointType == css::drawing::EnhancedCustomShapeGluePointType::CUSTOM )
4644 const SdrGluePointList* pList = pCustoShape->GetGluePointList();
4645 if ( pList )
4647 tools::Polygon aPoly;
4648 sal_uInt16 nNum, nAnz = pList->GetCount();
4649 if ( nAnz )
4651 for ( nNum = 0; nNum < nAnz; nNum++ )
4653 const SdrGluePoint& rGP = (*pList)[ nNum ];
4654 Point aPt( rGP.GetAbsolutePos( *pCustoShape ) );
4655 aPoly.Insert( POLY_APPEND, aPt );
4657 nRule = GetClosestPoint( aPoly, aRefPoint );
4658 bRectangularConnection = false;
4662 else if ( nGluePointType == css::drawing::EnhancedCustomShapeGluePointType::SEGMENTS )
4664 SdrObject* pPoly = pCustoShape->DoConvertToPolyObj( true, true );
4665 if ( dynamic_cast<const SdrPathObj* >( pPoly ) != nullptr )
4667 sal_Int16 a, b, nIndex = 0;
4668 sal_uInt32 nDistance = 0xffffffff;
4670 // #i74631# use explicit constructor here. Also XPolyPolygon is not necessary,
4671 // reducing to PolyPolygon
4672 const tools::PolyPolygon aPolyPoly(static_cast<SdrPathObj*>(pPoly)->GetPathPoly());
4674 for ( a = 0; a < aPolyPoly.Count(); a++ )
4676 const tools::Polygon& rPoly = aPolyPoly.GetObject( a );
4677 for ( b = 0; b < rPoly.GetSize(); b++ )
4679 if ( rPoly.GetFlags( b ) != PolyFlags::Normal )
4680 continue;
4681 const Point& rPt = rPoly[ b ];
4682 sal_uInt32 nDist = (sal_uInt32)hypot( aRefPoint.X - rPt.X(), aRefPoint.Y - rPt.Y() );
4683 if ( nDist < nDistance )
4685 nRule = nIndex;
4686 nDistance = nDist;
4688 nIndex++;
4691 if ( nDistance != 0xffffffff )
4692 bRectangularConnection = false;
4697 if ( bRectangularConnection )
4699 css::awt::Point aPoint( aXShape->getPosition() );
4700 css::awt::Size aSize( aXShape->getSize() );
4702 Rectangle aRect( Point( aPoint.X, aPoint.Y ), Size( aSize.Width, aSize.Height ) );
4703 Point aCenter( aRect.Center() );
4704 tools::Polygon aPoly( 4 );
4706 aPoly[ 0 ] = Point( aCenter.X(), aRect.Top() );
4707 aPoly[ 1 ] = Point( aRect.Left(), aCenter.Y() );
4708 aPoly[ 2 ] = Point( aCenter.X(), aRect.Bottom() );
4709 aPoly[ 3 ] = Point( aRect.Right(), aCenter.Y() );
4711 sal_Int32 nAngle = ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet, "RotateAngle", true ) )
4712 ? *o3tl::doAccess<sal_Int32>(aAny) : 0;
4713 if ( nAngle )
4714 aPoly.Rotate( aRect.TopLeft(), (sal_uInt16)( ( nAngle + 5 ) / 10 ) );
4715 nRule = GetClosestPoint( aPoly, aRefPoint );
4717 if (aType == OString( "drawing.Ellipse" ))
4718 nRule <<= 1; // In PPT an ellipse has 8 ways to connect
4721 return nRule;
4724 EscherSolverContainer::~EscherSolverContainer()
4726 for(EscherShapeListEntry* i : maShapeList) {
4727 delete i;
4729 for(EscherConnectorListEntry* i : maConnectorList) {
4730 delete i;
4734 void EscherSolverContainer::AddShape( const css::uno::Reference< css::drawing::XShape > & rXShape, sal_uInt32 nId )
4736 maShapeList.push_back( new EscherShapeListEntry( rXShape, nId ) );
4739 void EscherSolverContainer::AddConnector(
4740 const css::uno::Reference< css::drawing::XShape > & rConnector,
4741 const css::awt::Point& rPA,
4742 css::uno::Reference< css::drawing::XShape > & rConA,
4743 const css::awt::Point& rPB,
4744 css::uno::Reference< css::drawing::XShape > & rConB
4747 maConnectorList.push_back( new EscherConnectorListEntry( rConnector, rPA, rConA, rPB, rConB ) );
4750 sal_uInt32 EscherSolverContainer::GetShapeId( const css::uno::Reference< css::drawing::XShape > & rXShape ) const
4752 for (EscherShapeListEntry* pPtr : maShapeList)
4754 if ( rXShape == pPtr->aXShape )
4755 return pPtr->n_EscherId;
4757 return 0;
4760 void EscherSolverContainer::WriteSolver( SvStream& rStrm )
4762 sal_uInt32 nCount = maConnectorList.size();
4763 if ( nCount )
4765 sal_uInt32 nRecHdPos, nCurrentPos, nSize;
4766 rStrm .WriteUInt16( ( nCount << 4 ) | 0xf ) // open an ESCHER_SolverContainer
4767 .WriteUInt16( ESCHER_SolverContainer )
4768 .WriteUInt32( 0 );
4770 nRecHdPos = rStrm.Tell() - 4;
4772 EscherConnectorRule aConnectorRule;
4773 aConnectorRule.nRuleId = 2;
4774 for (EscherConnectorListEntry* pPtr : maConnectorList)
4776 aConnectorRule.ncptiA = aConnectorRule.ncptiB = 0xffffffff;
4777 aConnectorRule.nShapeC = GetShapeId( pPtr->mXConnector );
4778 aConnectorRule.nShapeA = GetShapeId( pPtr->mXConnectToA );
4779 aConnectorRule.nShapeB = GetShapeId( pPtr->mXConnectToB );
4781 if ( aConnectorRule.nShapeC )
4783 if ( aConnectorRule.nShapeA )
4784 aConnectorRule.ncptiA = pPtr->GetConnectorRule( true );
4785 if ( aConnectorRule.nShapeB )
4786 aConnectorRule.ncptiB = pPtr->GetConnectorRule( false );
4788 rStrm .WriteUInt32( ( ESCHER_ConnectorRule << 16 ) | 1 ) // atom hd
4789 .WriteUInt32( 24 )
4790 .WriteUInt32( aConnectorRule.nRuleId )
4791 .WriteUInt32( aConnectorRule.nShapeA )
4792 .WriteUInt32( aConnectorRule.nShapeB )
4793 .WriteUInt32( aConnectorRule.nShapeC )
4794 .WriteUInt32( aConnectorRule.ncptiA )
4795 .WriteUInt32( aConnectorRule.ncptiB );
4797 aConnectorRule.nRuleId += 2;
4800 nCurrentPos = rStrm.Tell(); // close the ESCHER_SolverContainer
4801 nSize = ( nCurrentPos - nRecHdPos ) - 4;
4802 rStrm.Seek( nRecHdPos );
4803 rStrm.WriteUInt32( nSize );
4804 rStrm.Seek( nCurrentPos );
4808 EscherExGlobal::EscherExGlobal( sal_uInt32 nGraphicProvFlags ) :
4809 EscherGraphicProvider( nGraphicProvFlags ),
4810 mpPicStrm( nullptr ),
4811 mbHasDggCont( false ),
4812 mbPicStrmQueried( false )
4816 EscherExGlobal::~EscherExGlobal()
4820 sal_uInt32 EscherExGlobal::GenerateDrawingId()
4822 // new drawing starts a new cluster in the cluster table (cluster identifiers are one-based)
4823 sal_uInt32 nClusterId = static_cast< sal_uInt32 >( maClusterTable.size() + 1 );
4824 // drawing identifiers are one-based
4825 sal_uInt32 nDrawingId = static_cast< sal_uInt32 >( maDrawingInfos.size() + 1 );
4826 // prepare new entries in the tables
4827 maClusterTable.push_back( ClusterEntry( nDrawingId ) );
4828 maDrawingInfos.push_back( DrawingInfo( nClusterId ) );
4829 // return the new drawing identifier
4830 return nDrawingId;
4833 sal_uInt32 EscherExGlobal::GenerateShapeId( sal_uInt32 nDrawingId, bool bIsInSpgr )
4835 // drawing identifier is one-based
4836 // make sure the drawing is valid (bnc#656503)
4837 if ( nDrawingId == 0 )
4838 return 0;
4839 // create index from the identifier
4840 size_t nDrawingIdx = nDrawingId - 1;
4841 OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GenerateShapeId - invalid drawing ID" );
4842 if( nDrawingIdx >= maDrawingInfos.size() )
4843 return 0;
4844 DrawingInfo& rDrawingInfo = maDrawingInfos[ nDrawingIdx ];
4846 // cluster identifier in drawing info struct is one-based
4847 ClusterEntry* pClusterEntry = &maClusterTable[ rDrawingInfo.mnClusterId - 1 ];
4849 // check cluster overflow, create new cluster entry
4850 if( pClusterEntry->mnNextShapeId == DFF_DGG_CLUSTER_SIZE )
4852 // start a new cluster in the cluster table
4853 maClusterTable.push_back( ClusterEntry( nDrawingId ) );
4854 pClusterEntry = &maClusterTable.back();
4855 // new size of maClusterTable is equal to one-based identifier of the new cluster
4856 rDrawingInfo.mnClusterId = static_cast< sal_uInt32 >( maClusterTable.size() );
4859 // build shape identifier from cluster identifier and next free cluster shape identifier
4860 rDrawingInfo.mnLastShapeId = static_cast< sal_uInt32 >( rDrawingInfo.mnClusterId * DFF_DGG_CLUSTER_SIZE + pClusterEntry->mnNextShapeId );
4861 // update free shape identifier in cluster entry
4862 ++pClusterEntry->mnNextShapeId;
4863 /* Old code has counted the shapes only, if we are in a SPGRCONTAINER. Is
4864 this really intended? Maybe it's always true... */
4865 if( bIsInSpgr )
4866 ++rDrawingInfo.mnShapeCount;
4868 // return the new shape identifier
4869 return rDrawingInfo.mnLastShapeId;
4872 sal_uInt32 EscherExGlobal::GetDrawingShapeCount( sal_uInt32 nDrawingId ) const
4874 size_t nDrawingIdx = nDrawingId - 1;
4875 OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GetDrawingShapeCount - invalid drawing ID" );
4876 return (nDrawingIdx < maDrawingInfos.size()) ? maDrawingInfos[ nDrawingIdx ].mnShapeCount : 0;
4879 sal_uInt32 EscherExGlobal::GetLastShapeId( sal_uInt32 nDrawingId ) const
4881 size_t nDrawingIdx = nDrawingId - 1;
4882 OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GetLastShapeId - invalid drawing ID" );
4883 return (nDrawingIdx < maDrawingInfos.size()) ? maDrawingInfos[ nDrawingIdx ].mnLastShapeId : 0;
4886 sal_uInt32 EscherExGlobal::GetDggAtomSize() const
4888 // 8 bytes header, 16 bytes fixed DGG data, 8 bytes for each cluster
4889 return static_cast< sal_uInt32 >( 24 + 8 * maClusterTable.size() );
4892 void EscherExGlobal::WriteDggAtom( SvStream& rStrm ) const
4894 sal_uInt32 nDggSize = GetDggAtomSize();
4896 // write the DGG record header (do not include the 8 bytes of the header in the data size)
4897 rStrm.WriteUInt32( ESCHER_Dgg << 16 ).WriteUInt32( nDggSize - 8 );
4899 // calculate and write the fixed DGG data
4900 sal_uInt32 nShapeCount = 0;
4901 sal_uInt32 nLastShapeId = 0;
4902 for( DrawingInfoVector::const_iterator aIt = maDrawingInfos.begin(), aEnd = maDrawingInfos.end(); aIt != aEnd; ++aIt )
4904 nShapeCount += aIt->mnShapeCount;
4905 nLastShapeId = ::std::max( nLastShapeId, aIt->mnLastShapeId );
4907 // the non-existing cluster with index #0 is counted too
4908 sal_uInt32 nClusterCount = static_cast< sal_uInt32 >( maClusterTable.size() + 1 );
4909 sal_uInt32 nDrawingCount = static_cast< sal_uInt32 >( maDrawingInfos.size() );
4910 rStrm.WriteUInt32( nLastShapeId ).WriteUInt32( nClusterCount ).WriteUInt32( nShapeCount ).WriteUInt32( nDrawingCount );
4912 // write the cluster table
4913 for( ClusterTable::const_iterator aIt = maClusterTable.begin(), aEnd = maClusterTable.end(); aIt != aEnd; ++aIt )
4914 rStrm.WriteUInt32( aIt->mnDrawingId ).WriteUInt32( aIt->mnNextShapeId );
4917 SvStream* EscherExGlobal::QueryPictureStream()
4919 if( !mbPicStrmQueried )
4921 mpPicStrm = ImplQueryPictureStream();
4922 mbPicStrmQueried = true;
4924 return mpPicStrm;
4927 SvStream* EscherExGlobal::ImplQueryPictureStream()
4929 return nullptr;
4932 // Implementation of an empty stream that silently succeeds, but does nothing.
4934 // In fact, this is a hack. The right solution is to abstract EscherEx to be
4935 // able to work without SvStream; but at the moment it is better to live with
4936 // this I guess.
4937 class SvNullStream : public SvStream
4939 protected:
4940 virtual std::size_t GetData( void* pData, std::size_t nSize ) override { memset( pData, 0, nSize ); return nSize; }
4941 virtual std::size_t PutData( const void*, std::size_t nSize ) override { return nSize; }
4942 virtual sal_uInt64 SeekPos( sal_uInt64 nPos ) override { return nPos; }
4943 virtual void SetSize( sal_uInt64 ) override {}
4944 virtual void FlushData() override {}
4946 public:
4947 SvNullStream() : SvStream() {}
4948 virtual ~SvNullStream() override {}
4951 EscherEx::EscherEx(const std::shared_ptr<EscherExGlobal>& rxGlobal, SvStream* pOutStrm, bool bOOXML)
4952 : mxGlobal(rxGlobal)
4953 , mpOutStrm(pOutStrm)
4954 , mbOwnsStrm(false)
4955 , mnCurrentDg(0)
4956 , mnCountOfs(0)
4957 , mnGroupLevel(0)
4958 , mnHellLayerId(USHRT_MAX)
4959 , mbEscherSpgr(false)
4960 , mbEscherDg(false)
4961 , mbOOXML(bOOXML)
4963 if (!mpOutStrm)
4965 mpOutStrm = new SvNullStream();
4966 mbOwnsStrm = true;
4968 mnStrmStartOfs = mpOutStrm->Tell();
4969 mpImplEESdrWriter.reset( new ImplEESdrWriter( *this ) );
4972 EscherEx::~EscherEx()
4974 if (mbOwnsStrm)
4975 delete mpOutStrm;
4978 void EscherEx::Flush( SvStream* pPicStreamMergeBSE /* = NULL */ )
4980 if ( mxGlobal->HasDggContainer() )
4982 // store the current stream position at ESCHER_Persist_CurrentPosition key
4983 PtReplaceOrInsert( ESCHER_Persist_CurrentPosition, mpOutStrm->Tell() );
4984 if ( DoSeek( ESCHER_Persist_Dgg ) )
4986 /* The DGG record is still not written. ESCHER_Persist_Dgg seeks
4987 to the place where the complete record has to be inserted. */
4988 InsertAtCurrentPos( mxGlobal->GetDggAtomSize() );
4989 mxGlobal->WriteDggAtom( *mpOutStrm );
4991 if ( mxGlobal->HasGraphics() )
4993 /* Calculate the total size of the BSTORECONTAINER including
4994 all BSE records containing the picture data contained in
4995 the passed in pPicStreamMergeBSE. */
4996 sal_uInt32 nBSCSize = mxGlobal->GetBlibStoreContainerSize( pPicStreamMergeBSE );
4997 if ( nBSCSize > 0 )
4999 InsertAtCurrentPos( nBSCSize );
5000 mxGlobal->WriteBlibStoreContainer( *mpOutStrm, pPicStreamMergeBSE );
5004 /* Forget the stream position stored for the DGG which is invalid
5005 after the call to InsertAtCurrentPos() anyway. */
5006 PtDelete( ESCHER_Persist_Dgg );
5008 // seek to initial position (may be different due to inserted DGG and BLIPs)
5009 mpOutStrm->Seek( PtGetOffsetByID( ESCHER_Persist_CurrentPosition ) );
5013 void EscherEx::InsertAtCurrentPos( sal_uInt32 nBytes )
5015 sal_uInt32 nSize, nType, nSource, nBufSize, nToCopy, nCurPos = mpOutStrm->Tell();
5017 // adjust persist table
5018 for(EscherPersistEntry* pPtr : maPersistTable) {
5019 sal_uInt32 nOfs = pPtr->mnOffset;
5020 if ( nOfs >= nCurPos ) {
5021 pPtr->mnOffset += nBytes;
5025 // adapt container and atom sizes
5026 mpOutStrm->Seek( mnStrmStartOfs );
5027 while ( mpOutStrm->Tell() < nCurPos )
5029 mpOutStrm->ReadUInt32( nType ).ReadUInt32( nSize );
5030 sal_uInt32 nEndOfRecord = mpOutStrm->Tell() + nSize;
5031 bool bContainer = (nType & 0x0F) == 0x0F;
5032 /* Expand the record, if the insertion position is inside, or if the
5033 position is at the end of a container (expands always), or at the
5034 end of an atom and bExpandEndOfAtom is set. */
5035 if ( (nCurPos < nEndOfRecord) || ((nCurPos == nEndOfRecord) && bContainer) )
5037 mpOutStrm->SeekRel( -4 );
5038 mpOutStrm->WriteUInt32( nSize + nBytes );
5039 if ( !bContainer )
5040 mpOutStrm->SeekRel( nSize );
5042 else
5043 mpOutStrm->SeekRel( nSize );
5045 for (std::vector< sal_uInt32 >::iterator aIter( mOffsets.begin() ), aEnd( mOffsets.end() ); aIter != aEnd ; ++aIter)
5047 if ( *aIter > nCurPos )
5048 *aIter += nBytes;
5050 mpOutStrm->Seek( STREAM_SEEK_TO_END );
5051 nSource = mpOutStrm->Tell();
5052 nToCopy = nSource - nCurPos; // increase the size of the tream by nBytes
5053 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ 0x40000 ]); // 256KB Buffer
5054 while ( nToCopy )
5056 nBufSize = ( nToCopy >= 0x40000 ) ? 0x40000 : nToCopy;
5057 nToCopy -= nBufSize;
5058 nSource -= nBufSize;
5059 mpOutStrm->Seek( nSource );
5060 mpOutStrm->ReadBytes(pBuf.get(), nBufSize);
5061 mpOutStrm->Seek( nSource + nBytes );
5062 mpOutStrm->WriteBytes(pBuf.get(), nBufSize);
5064 mpOutStrm->Seek( nCurPos );
5067 void EscherEx::InsertPersistOffset( sal_uInt32 nKey, sal_uInt32 nOffset )
5069 PtInsert( ESCHER_Persist_PrivateEntry | nKey, nOffset );
5072 void EscherEx::ReplacePersistOffset( sal_uInt32 nKey, sal_uInt32 nOffset )
5074 PtReplace( ESCHER_Persist_PrivateEntry | nKey, nOffset );
5077 void EscherEx::SetEditAs( const OUString& rEditAs )
5079 mEditAs = rEditAs;
5082 sal_uInt32 EscherEx::GetPersistOffset( sal_uInt32 nKey )
5084 return PtGetOffsetByID( ESCHER_Persist_PrivateEntry | nKey );
5087 bool EscherEx::DoSeek( sal_uInt32 nKey )
5089 sal_uInt32 nPos = PtGetOffsetByID( nKey );
5090 if ( nPos )
5091 mpOutStrm->Seek( nPos );
5092 else
5094 if (! PtIsID( nKey ) )
5095 return false;
5096 mpOutStrm->Seek( 0 );
5098 return true;
5101 bool EscherEx::SeekToPersistOffset( sal_uInt32 nKey )
5103 return DoSeek( ESCHER_Persist_PrivateEntry | nKey );
5106 void EscherEx::InsertAtPersistOffset( sal_uInt32 nKey, sal_uInt32 nValue )
5108 sal_uInt32 nOldPos = mpOutStrm->Tell();
5109 bool bRetValue = SeekToPersistOffset( nKey );
5110 if ( bRetValue )
5112 mpOutStrm->WriteUInt32( nValue );
5113 mpOutStrm->Seek( nOldPos );
5117 void EscherEx::OpenContainer( sal_uInt16 nEscherContainer, int nRecInstance )
5119 mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | 0xf ).WriteUInt16( nEscherContainer ).WriteUInt32( 0 );
5120 mOffsets.push_back( mpOutStrm->Tell() - 4 );
5121 mRecTypes.push_back( nEscherContainer );
5122 switch( nEscherContainer )
5124 case ESCHER_DggContainer :
5126 mxGlobal->SetDggContainer();
5127 mnCurrentDg = 0;
5128 /* Remember the current position as start position of the DGG
5129 record and BSTORECONTAINER, but do not write them actually.
5130 This will be done later in Flush() when the number of drawings,
5131 the size and contents of the FIDCL cluster table, and the size
5132 of the BLIP container are known. */
5133 PtReplaceOrInsert( ESCHER_Persist_Dgg, mpOutStrm->Tell() );
5135 break;
5137 case ESCHER_DgContainer :
5139 if ( mxGlobal->HasDggContainer() )
5141 if ( !mbEscherDg )
5143 mbEscherDg = true;
5144 mnCurrentDg = mxGlobal->GenerateDrawingId();
5145 AddAtom( 8, ESCHER_Dg, 0, mnCurrentDg );
5146 PtReplaceOrInsert( ESCHER_Persist_Dg | mnCurrentDg, mpOutStrm->Tell() );
5147 mpOutStrm->WriteUInt32( 0 ) // The number of shapes in this drawing
5148 .WriteUInt32( 0 ); // The last MSOSPID given to an SP in this DG
5152 break;
5154 case ESCHER_SpgrContainer :
5156 if ( mbEscherDg )
5158 mbEscherSpgr = true;
5161 break;
5163 case ESCHER_SpContainer :
5166 break;
5168 default:
5169 break;
5173 void EscherEx::CloseContainer()
5175 sal_uInt32 nSize, nPos = mpOutStrm->Tell();
5176 nSize = ( nPos - mOffsets.back() ) - 4;
5177 mpOutStrm->Seek( mOffsets.back() );
5178 mpOutStrm->WriteUInt32( nSize );
5180 switch( mRecTypes.back() )
5182 case ESCHER_DgContainer :
5184 if ( mbEscherDg )
5186 mbEscherDg = false;
5187 if ( DoSeek( ESCHER_Persist_Dg | mnCurrentDg ) )
5188 mpOutStrm->WriteUInt32( mxGlobal->GetDrawingShapeCount( mnCurrentDg ) ).WriteUInt32( mxGlobal->GetLastShapeId( mnCurrentDg ) );
5191 break;
5193 case ESCHER_SpgrContainer :
5195 if ( mbEscherSpgr )
5197 mbEscherSpgr = false;
5201 break;
5203 default:
5204 break;
5206 mOffsets.pop_back();
5207 mRecTypes.pop_back();
5208 mpOutStrm->Seek( nPos );
5211 void EscherEx::BeginAtom()
5213 mnCountOfs = mpOutStrm->Tell();
5214 mpOutStrm->WriteUInt32( 0 ).WriteUInt32( 0 ); // record header wird spaeter geschrieben
5217 void EscherEx::EndAtom( sal_uInt16 nRecType, int nRecVersion, int nRecInstance )
5219 sal_uInt32 nOldPos = mpOutStrm->Tell();
5220 mpOutStrm->Seek( mnCountOfs );
5221 sal_uInt32 nSize = nOldPos - mnCountOfs;
5222 mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | ( nRecVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nSize - 8 );
5223 mpOutStrm->Seek( nOldPos );
5226 void EscherEx::AddAtom( sal_uInt32 nAtomSize, sal_uInt16 nRecType, int nRecVersion, int nRecInstance )
5228 mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | ( nRecVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nAtomSize );
5231 void EscherEx::AddChildAnchor( const Rectangle& rRect )
5233 AddAtom( 16, ESCHER_ChildAnchor );
5234 mpOutStrm ->WriteInt32( rRect.Left() )
5235 .WriteInt32( rRect.Top() )
5236 .WriteInt32( rRect.Right() )
5237 .WriteInt32( rRect.Bottom() );
5240 void EscherEx::AddClientAnchor( const Rectangle& rRect )
5242 AddAtom( 8, ESCHER_ClientAnchor );
5243 mpOutStrm->WriteInt16( rRect.Top() )
5244 .WriteInt16( rRect.Left() )
5245 .WriteInt16( rRect.GetWidth() + rRect.Left() )
5246 .WriteInt16( rRect.GetHeight() + rRect.Top() );
5249 EscherExHostAppData* EscherEx::EnterAdditionalTextGroup()
5251 return nullptr;
5254 sal_uInt32 EscherEx::EnterGroup( const OUString& rShapeName, const Rectangle* pBoundRect )
5256 Rectangle aRect;
5257 if( pBoundRect )
5258 aRect = *pBoundRect;
5260 OpenContainer( ESCHER_SpgrContainer );
5261 OpenContainer( ESCHER_SpContainer );
5262 AddAtom( 16, ESCHER_Spgr, 1 );
5263 PtReplaceOrInsert( ESCHER_Persist_Grouping_Snap | mnGroupLevel,
5264 mpOutStrm->Tell() );
5265 mpOutStrm ->WriteInt32( aRect.Left() ) // Bounding box for the grouped shapes to which they will be attached
5266 .WriteInt32( aRect.Top() )
5267 .WriteInt32( aRect.Right() )
5268 .WriteInt32( aRect.Bottom() );
5270 sal_uInt32 nShapeId = GenerateShapeId();
5271 if ( !mnGroupLevel )
5272 AddShape( ESCHER_ShpInst_Min, 5, nShapeId ); // Flags: Group | Patriarch
5273 else
5275 AddShape( ESCHER_ShpInst_Min, 0x201, nShapeId ); // Flags: Group | HaveAnchor
5276 EscherPropertyContainer aPropOpt;
5277 aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x00040004 );
5278 aPropOpt.AddOpt( ESCHER_Prop_dxWrapDistLeft, 0 );
5279 aPropOpt.AddOpt( ESCHER_Prop_dxWrapDistRight, 0 );
5281 // #i51348# shape name
5282 if( rShapeName.getLength() > 0 )
5283 aPropOpt.AddOpt( ESCHER_Prop_wzName, rShapeName );
5285 Commit( aPropOpt, aRect );
5286 if ( mnGroupLevel > 1 )
5287 AddChildAnchor( aRect );
5289 EscherExHostAppData* pAppData = mpImplEESdrWriter->ImplGetHostData();
5290 if( pAppData )
5292 if ( mnGroupLevel <= 1 )
5293 pAppData->WriteClientAnchor( *this, aRect );
5294 pAppData->WriteClientData( *this );
5297 CloseContainer(); // ESCHER_SpContainer
5298 mnGroupLevel++;
5299 return nShapeId;
5302 sal_uInt32 EscherEx::EnterGroup( const Rectangle* pBoundRect )
5304 return EnterGroup( OUString(), pBoundRect );
5307 void EscherEx::SetGroupSnapRect( sal_uInt32 nGroupLevel, const Rectangle& rRect )
5309 if ( nGroupLevel )
5311 sal_uInt32 nCurrentPos = mpOutStrm->Tell();
5312 if ( DoSeek( ESCHER_Persist_Grouping_Snap | ( nGroupLevel - 1 ) ) )
5314 mpOutStrm ->WriteInt32( rRect.Left() ) // Bounding box for the grouped shapes to which they will be attached
5315 .WriteInt32( rRect.Top() )
5316 .WriteInt32( rRect.Right() )
5317 .WriteInt32( rRect.Bottom() );
5318 mpOutStrm->Seek( nCurrentPos );
5323 void EscherEx::SetGroupLogicRect( sal_uInt32 nGroupLevel, const Rectangle& rRect )
5325 if ( nGroupLevel )
5327 sal_uInt32 nCurrentPos = mpOutStrm->Tell();
5328 if ( DoSeek( ESCHER_Persist_Grouping_Logic | ( nGroupLevel - 1 ) ) )
5330 mpOutStrm->WriteInt16( rRect.Top() ).WriteInt16( rRect.Left() ).WriteInt16( rRect.Right() ).WriteInt16( rRect.Bottom() );
5331 mpOutStrm->Seek( nCurrentPos );
5336 void EscherEx::LeaveGroup()
5338 --mnGroupLevel;
5339 PtDelete( ESCHER_Persist_Grouping_Snap | mnGroupLevel );
5340 PtDelete( ESCHER_Persist_Grouping_Logic | mnGroupLevel );
5341 CloseContainer();
5344 void EscherEx::AddShape( sal_uInt32 nShpInstance, sal_uInt32 nFlags, sal_uInt32 nShapeID )
5346 AddAtom( 8, ESCHER_Sp, 2, nShpInstance );
5348 if ( !nShapeID )
5349 nShapeID = GenerateShapeId();
5351 if ( nFlags ^ 1 ) // is this a group shape ?
5352 { // if not
5353 if ( mnGroupLevel > 1 )
5354 nFlags |= 2; // this not a topmost shape
5356 mpOutStrm->WriteUInt32( nShapeID ).WriteUInt32( nFlags );
5359 void EscherEx::Commit( EscherPropertyContainer& rProps, const Rectangle& )
5361 rProps.Commit( GetStream() );
5364 sal_uInt32 EscherEx::GetColor( const sal_uInt32 nSOColor )
5366 sal_uInt32 nColor = nSOColor & 0xff00; // Green
5367 nColor |= (sal_uInt8)( nSOColor ) << 16; // Red
5368 nColor |= (sal_uInt8)( nSOColor >> 16 ); // Blue
5369 return nColor;
5372 sal_uInt32 EscherEx::GetColor( const Color& rSOColor, bool bSwap )
5374 sal_uInt32 nColor = ( rSOColor.GetRed() << 16 );
5375 nColor |= ( rSOColor.GetGreen() << 8 );
5376 nColor |= rSOColor.GetBlue();
5378 if ( !bSwap )
5379 nColor = GetColor( nColor );
5381 return nColor;
5384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */