android: Update app-specific/MIME type icons
[LibreOffice.git] / filter / source / msfilter / escherex.cxx
blob6ab8eb8445800d345c4c06f2ddc55084786009f7
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 <o3tl/string_view.hxx>
23 #include <svx/svdxcgv.hxx>
24 #include <svx/svdomedia.hxx>
25 #include <svx/xflftrit.hxx>
26 #include <filter/msfilter/escherex.hxx>
27 #include <filter/msfilter/util.hxx>
28 #include <svx/unoapi.hxx>
29 #include <svx/svdobj.hxx>
30 #include <svx/svdoashp.hxx>
31 #include <svx/svdoole2.hxx>
32 #include <svx/sdtfsitm.hxx>
33 #include <editeng/outlobj.hxx>
34 #include <utility>
35 #include <vcl/graph.hxx>
36 #include <vcl/cvtgrf.hxx>
37 #include <vcl/svapp.hxx>
38 #include <tools/debug.hxx>
39 #include <tools/stream.hxx>
40 #include <tools/zcodec.hxx>
41 #include <tools/urlobj.hxx>
42 #include <svx/svdopath.hxx>
43 #include <stdlib.h>
44 #include <vcl/graphicfilter.hxx>
45 #include <svx/EnhancedCustomShapeTypeNames.hxx>
46 #include <svx/EnhancedCustomShapeGeometry.hxx>
47 #include <svx/EnhancedCustomShapeFunctionParser.hxx>
48 #include <svx/EnhancedCustomShape2d.hxx>
49 #include <com/sun/star/beans/PropertyValues.hpp>
50 #include <com/sun/star/beans/XPropertySet.hpp>
51 #include <com/sun/star/beans/XPropertyState.hpp>
52 #include <com/sun/star/awt/GradientStyle.hpp>
53 #include <com/sun/star/awt/Gradient.hpp>
54 #include <com/sun/star/drawing/LineStyle.hpp>
55 #include <com/sun/star/drawing/LineJoint.hpp>
56 #include <com/sun/star/drawing/LineCap.hpp>
57 #include <com/sun/star/drawing/FillStyle.hpp>
58 #include <com/sun/star/drawing/LineDash.hpp>
59 #include <com/sun/star/drawing/PolyPolygonBezierCoords.hpp>
60 #include <com/sun/star/drawing/ConnectorType.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/FontSlant.hpp>
81 #include <com/sun/star/awt/FontWeight.hpp>
82 #include <com/sun/star/drawing/ColorMode.hpp>
83 #include <com/sun/star/drawing/Position3D.hpp>
84 #include <com/sun/star/drawing/Direction3D.hpp>
85 #include <com/sun/star/drawing/Hatch.hpp>
86 #include <com/sun/star/graphic/XGraphic.hpp>
87 #include <com/sun/star/text/GraphicCrop.hpp>
88 #include <unotools/ucbstreamhelper.hxx>
89 #include <comphelper/string.hxx>
90 #include <vcl/virdev.hxx>
91 #include <rtl/crc.h>
92 #include <rtl/strbuf.hxx>
93 #include <sal/log.hxx>
94 #include <basegfx/polygon/b2dpolypolygontools.hxx>
95 #include <basegfx/polygon/b2dpolygontools.hxx>
96 #include <basegfx/numeric/ftools.hxx>
97 #include <osl/diagnose.h>
99 #include <algorithm>
100 #include <memory>
102 using namespace css;
104 EscherExContainer::EscherExContainer( SvStream& rSt, const sal_uInt16 nRecType, const sal_uInt16 nInstance ) :
105 rStrm ( rSt )
107 rStrm.WriteUInt32( ( 0xf | ( nInstance << 4 ) ) | ( nRecType << 16 ) ).WriteUInt32( 0 );
108 nContPos = rStrm.Tell();
110 EscherExContainer::~EscherExContainer()
112 sal_uInt32 nPos = rStrm.Tell();
113 sal_uInt32 nSize= nPos - nContPos;
114 if ( nSize )
116 rStrm.Seek( nContPos - 4 );
117 rStrm.WriteUInt32( nSize );
118 rStrm.Seek( nPos );
122 EscherExAtom::EscherExAtom( SvStream& rSt, const sal_uInt16 nRecType, const sal_uInt16 nInstance, const sal_uInt8 nVersion ) :
123 rStrm ( rSt )
125 rStrm.WriteUInt32( ( nVersion | ( nInstance << 4 ) ) | ( nRecType << 16 ) ).WriteUInt32( 0 );
126 nContPos = rStrm.Tell();
128 EscherExAtom::~EscherExAtom()
130 sal_uInt32 nPos = rStrm.Tell();
131 sal_uInt32 nSize= nPos - nContPos;
132 if ( nSize )
134 rStrm.Seek( nContPos - 4 );
135 rStrm.WriteUInt32( nSize );
136 rStrm.Seek( nPos );
140 EscherExClientRecord_Base::~EscherExClientRecord_Base()
144 EscherExClientAnchor_Base::~EscherExClientAnchor_Base()
148 EscherPropertyContainer::EscherPropertyContainer(
149 EscherGraphicProvider * pGraphProv, SvStream * pPiOutStrm,
150 tools::Rectangle * pBoundRect):
151 pGraphicProvider(pGraphProv),
152 pPicOutStrm(pPiOutStrm),
153 pShapeBoundRect(pBoundRect),
154 nCountCount(0),
155 nCountSize(0),
156 bHasComplexData(false)
158 pSortStruct.reserve(64);
161 EscherPropertyContainer::EscherPropertyContainer()
162 : EscherPropertyContainer(nullptr, nullptr, nullptr)
165 EscherPropertyContainer::EscherPropertyContainer(
166 EscherGraphicProvider& rGraphProv,
167 SvStream* pPiOutStrm,
168 tools::Rectangle& rBoundRect ) :
169 EscherPropertyContainer(&rGraphProv, pPiOutStrm, &rBoundRect)
172 EscherPropertyContainer::~EscherPropertyContainer()
176 void EscherPropertyContainer::AddOpt(
177 sal_uInt16 nPropID,
178 bool bBlib,
179 sal_uInt32 nSizeReduction,
180 SvMemoryStream& rStream)
182 sal_uInt8 const* pBuf(static_cast<sal_uInt8 const *>(rStream.GetData()));
183 const sal_uInt64 nSize(rStream.GetSize());
184 std::vector<sal_uInt8> aBuf;
185 aBuf.reserve(nSize);
187 for(sal_uInt64 a(0); a < nSize; a++)
189 aBuf.push_back(*pBuf++);
192 sal_uInt32 nPropValue(static_cast<sal_uInt32>(nSize));
194 if(0 != nSizeReduction && nPropValue > nSizeReduction)
196 nPropValue -= nSizeReduction;
199 AddOpt(nPropID, bBlib, nPropValue, aBuf);
202 void EscherPropertyContainer::AddOpt(
203 sal_uInt16 nPropID,
204 sal_uInt32 nPropValue,
205 bool bBlib)
207 AddOpt(nPropID, bBlib, nPropValue, std::vector<sal_uInt8>());
210 void EscherPropertyContainer::AddOpt(
211 sal_uInt16 nPropID,
212 std::u16string_view rString)
214 std::vector<sal_uInt8> aBuf;
215 aBuf.reserve(rString.size() * 2 + 2);
217 for(size_t i(0); i < rString.size(); i++)
219 const sal_Unicode nUnicode(rString[i]);
220 aBuf.push_back(static_cast<sal_uInt8>(nUnicode));
221 aBuf.push_back(static_cast<sal_uInt8>(nUnicode >> 8));
224 aBuf.push_back(0);
225 aBuf.push_back(0);
227 AddOpt(nPropID, true, aBuf.size(), aBuf);
230 void EscherPropertyContainer::AddOpt(
231 sal_uInt16 nPropID,
232 bool bBlib,
233 sal_uInt32 nPropValue,
234 const std::vector<sal_uInt8>& rProp)
236 if ( bBlib ) // bBlib is only valid when fComplex = 0
237 nPropID |= 0x4000;
238 if ( !rProp.empty() )
239 nPropID |= 0x8000; // fComplex = sal_True;
241 for( size_t i = 0; i < pSortStruct.size(); i++ )
243 if ( ( pSortStruct[ i ].nPropId &~0xc000 ) == ( nPropID &~0xc000 ) ) // check, whether the Property only gets replaced
245 pSortStruct[ i ].nPropId = nPropID;
246 if ( !pSortStruct[ i ].nProp.empty() )
248 nCountSize -= pSortStruct[ i ].nProp.size();
250 pSortStruct[ i ].nProp = rProp;
251 pSortStruct[ i ].nPropValue = nPropValue;
252 if ( !rProp.empty() )
253 nCountSize += rProp.size();
254 return;
257 nCountCount++;
258 nCountSize += 6;
259 pSortStruct.emplace_back();
260 pSortStruct.back().nPropId = nPropID; // insert property
261 pSortStruct.back().nProp = rProp;
262 pSortStruct.back().nPropValue = nPropValue;
264 if ( !rProp.empty() )
266 nCountSize += rProp.size();
267 bHasComplexData = true;
271 bool EscherPropertyContainer::GetOpt( sal_uInt16 nPropId, sal_uInt32& rPropValue ) const
273 EscherPropSortStruct aPropStruct;
275 if ( GetOpt( nPropId, aPropStruct ) )
277 rPropValue = aPropStruct.nPropValue;
278 return true;
280 return false;
283 bool EscherPropertyContainer::GetOpt( sal_uInt16 nPropId, EscherPropSortStruct& rPropValue ) const
285 for( size_t i = 0; i < pSortStruct.size(); i++ )
287 if ( ( pSortStruct[ i ].nPropId &~0xc000 ) == ( nPropId &~0xc000 ) )
289 rPropValue = pSortStruct[ i ];
290 return true;
293 return false;
296 const EscherProperties & EscherPropertyContainer::GetOpts() const
298 return pSortStruct;
301 extern "C" {
303 static int EscherPropSortFunc( const void* p1, const void* p2 )
305 sal_Int16 nID1 = static_cast<EscherPropSortStruct const *>(p1)->nPropId &~0xc000;
306 sal_Int16 nID2 = static_cast<EscherPropSortStruct const *>(p2)->nPropId &~0xc000;
308 if( nID1 < nID2 )
309 return -1;
310 else if( nID1 > nID2 )
311 return 1;
312 else
313 return 0;
318 void EscherPropertyContainer::Commit( SvStream& rSt, sal_uInt16 nVersion, sal_uInt16 nRecType )
320 rSt.WriteUInt16( ( nCountCount << 4 ) | ( nVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nCountSize );
321 if ( pSortStruct.empty() )
322 return;
324 qsort( pSortStruct.data(), pSortStruct.size(), sizeof( EscherPropSortStruct ), EscherPropSortFunc );
326 for ( size_t i = 0; i < pSortStruct.size(); i++ )
328 sal_uInt32 nPropValue = pSortStruct[ i ].nPropValue;
329 sal_uInt16 nPropId = pSortStruct[ i ].nPropId;
331 rSt.WriteUInt16( nPropId )
332 .WriteUInt32( nPropValue );
334 if ( bHasComplexData )
336 for ( size_t i = 0; i < pSortStruct.size(); i++ )
338 if ( !pSortStruct[ i ].nProp.empty() )
339 rSt.WriteBytes(
340 pSortStruct[i].nProp.data(),
341 pSortStruct[i].nProp.size());
346 bool EscherPropertyContainer::IsFontWork() const
348 sal_uInt32 nTextPathFlags = 0;
349 GetOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags );
350 return ( nTextPathFlags & 0x4000 ) != 0;
353 sal_uInt32 EscherPropertyContainer::ImplGetColor( const sal_uInt32 nSOColor, bool bSwap )
355 if ( bSwap )
357 sal_uInt32 nColor = nSOColor & 0xff00; // green
358 nColor |= static_cast<sal_uInt8>(nSOColor) << 16; // red
359 nColor |= static_cast<sal_uInt8>( nSOColor >> 16 ); // blue
360 return nColor;
362 else
363 return nSOColor & 0xffffff;
366 sal_uInt32 EscherPropertyContainer::GetGradientColor(
367 const awt::Gradient* pGradient,
368 sal_uInt32 nStartColor )
370 sal_uInt32 nIntensity = 100;
371 Color aColor;
373 if ( pGradient )
375 if ( nStartColor & 1 )
377 nIntensity = pGradient->StartIntensity;
378 aColor = Color(ColorTransparency, pGradient->StartColor);
380 else
382 nIntensity = pGradient->EndIntensity;
383 aColor = Color(ColorTransparency, pGradient->EndColor);
386 sal_uInt32 nRed = ( aColor.GetRed() * nIntensity ) / 100;
387 sal_uInt32 nGreen = ( ( aColor.GetGreen() * nIntensity ) / 100 ) << 8;
388 sal_uInt32 nBlue = ( ( aColor.GetBlue() * nIntensity ) / 100 ) << 16;
389 return nRed | nGreen | nBlue;
392 void EscherPropertyContainer::CreateGradientProperties(
393 const awt::Gradient & rGradient )
395 sal_uInt32 nFillType = ESCHER_FillShadeScale;
396 sal_uInt32 nAngle = 0;
397 sal_uInt32 nFillFocus = 0;
398 sal_uInt32 nFillLR = 0;
399 sal_uInt32 nFillTB = 0;
400 sal_uInt32 nFirstColor = 0;
401 bool bWriteFillTo = false;
403 switch ( rGradient.Style )
405 case awt::GradientStyle_LINEAR :
406 case awt::GradientStyle_AXIAL :
408 nFillType = ESCHER_FillShadeScale;
409 nAngle = (rGradient.Angle * 0x10000) / 10;
410 nFillFocus = (sal::static_int_cast<int>(rGradient.Style) ==
411 sal::static_int_cast<int>(css::awt::GradientStyle_LINEAR)) ? 0 : 50;
413 break;
414 case awt::GradientStyle_RADIAL :
415 case awt::GradientStyle_ELLIPTICAL :
416 case awt::GradientStyle_SQUARE :
417 case awt::GradientStyle_RECT :
419 nFillLR = (rGradient.XOffset * 0x10000) / 100;
420 nFillTB = (rGradient.YOffset * 0x10000) / 100;
421 if ( ((nFillLR > 0) && (nFillLR < 0x10000)) || ((nFillTB > 0) && (nFillTB < 0x10000)) )
422 nFillType = ESCHER_FillShadeShape;
423 else
424 nFillType = ESCHER_FillShadeCenter;
425 nFirstColor = 1;
426 bWriteFillTo = true;
428 break;
429 case awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE : break;
431 AddOpt( ESCHER_Prop_fillType, nFillType );
432 AddOpt( ESCHER_Prop_fillAngle, nAngle );
433 AddOpt( ESCHER_Prop_fillColor, GetGradientColor( &rGradient, nFirstColor ) );
434 AddOpt( ESCHER_Prop_fillBackColor, GetGradientColor( &rGradient, nFirstColor ^ 1 ) );
435 AddOpt( ESCHER_Prop_fillFocus, nFillFocus );
436 if ( bWriteFillTo )
438 AddOpt( ESCHER_Prop_fillToLeft, nFillLR );
439 AddOpt( ESCHER_Prop_fillToTop, nFillTB );
440 AddOpt( ESCHER_Prop_fillToRight, nFillLR );
441 AddOpt( ESCHER_Prop_fillToBottom, nFillTB );
445 void EscherPropertyContainer::CreateGradientProperties(
446 const uno::Reference<beans::XPropertySet> & rXPropSet , bool bTransparentGradient)
448 uno::Any aAny;
449 awt::Gradient const * pGradient = nullptr;
451 sal_uInt32 nFillType = ESCHER_FillShadeScale;
452 sal_Int32 nAngle = 0;
453 sal_uInt32 nFillFocus = 0;
454 sal_uInt32 nFillLR = 0;
455 sal_uInt32 nFillTB = 0;
456 sal_uInt32 nFirstColor = 0;// like the control var nChgColors in import logic
457 bool bWriteFillTo = false;
459 // Transparency gradient: Means the third setting in transparency page is set
460 if (bTransparentGradient && EscherPropertyValueHelper::GetPropertyValue(
461 aAny, rXPropSet, "FillTransparenceGradient" ) )
463 pGradient = o3tl::doAccess<awt::Gradient>(aAny);
465 uno::Any aAnyTemp;
466 if ( EscherPropertyValueHelper::GetPropertyValue(
467 aAnyTemp, rXPropSet, "FillStyle" ) )
469 drawing::FillStyle eFS;
470 if ( ! ( aAnyTemp >>= eFS ) )
471 eFS = drawing::FillStyle_SOLID;
472 // solid and transparency
473 if ( eFS == drawing::FillStyle_SOLID)
475 if ( EscherPropertyValueHelper::GetPropertyValue(
476 aAnyTemp, rXPropSet, "FillColor" ) )
478 const_cast<awt::Gradient *>(pGradient)->StartColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAnyTemp), false );
479 const_cast<awt::Gradient *>(pGradient)->EndColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAnyTemp), false );
482 // gradient and transparency.
483 else if( eFS == drawing::FillStyle_GRADIENT )
485 if ( EscherPropertyValueHelper::GetPropertyValue(
486 aAny, rXPropSet, "FillGradient" ) )
487 pGradient = o3tl::doAccess<awt::Gradient>(aAny);
492 // Not transparency gradient
493 else if ( EscherPropertyValueHelper::GetPropertyValue(
494 aAny, rXPropSet, "FillGradient" ) )
496 pGradient = o3tl::doAccess<awt::Gradient>(aAny);
499 if ( pGradient )
501 switch ( pGradient->Style )
503 case awt::GradientStyle_LINEAR :
504 case awt::GradientStyle_AXIAL :
506 nFillType = ESCHER_FillShadeScale;
507 nAngle = pGradient->Angle;
508 while ( nAngle > 0 ) nAngle -= 3600;
509 while ( nAngle <= -3600 ) nAngle += 3600;
510 // Value of the real number = Integral + (Fractional / 65536.0)
511 nAngle = ( nAngle * 0x10000) / 10;
513 nFillFocus = (pGradient->Style == awt::GradientStyle_LINEAR) ?
514 ( pGradient->XOffset + pGradient->YOffset )/2 : -50;
515 if( !nFillFocus )
516 nFirstColor=nFirstColor ^ 1;
517 if ( !nAngle )
518 nFirstColor=nFirstColor ^ 1;
520 break;
521 case awt::GradientStyle_RADIAL :
522 case awt::GradientStyle_ELLIPTICAL :
523 case awt::GradientStyle_SQUARE :
524 case awt::GradientStyle_RECT :
526 // according to the import logic and rect type fill** value
527 nFillLR = (pGradient->XOffset * 0x10000) / 100;
528 nFillTB = (pGradient->YOffset * 0x10000) / 100;
529 if ( ((nFillLR > 0) && (nFillLR < 0x10000)) || ((nFillTB > 0) && (nFillTB < 0x10000)) )
530 nFillType = ESCHER_FillShadeShape;
531 else
532 nFillType = ESCHER_FillShadeCenter;
533 nFirstColor = 1;
534 bWriteFillTo = true;
536 break;
537 default: break;
541 AddOpt( ESCHER_Prop_fillType, nFillType );
542 AddOpt( ESCHER_Prop_fillAngle, nAngle );
543 AddOpt( ESCHER_Prop_fillColor, GetGradientColor( pGradient, nFirstColor ) );
544 AddOpt( ESCHER_Prop_fillBackColor, GetGradientColor( pGradient, nFirstColor ^ 1 ) );
545 AddOpt( ESCHER_Prop_fillFocus, nFillFocus );
546 if ( bWriteFillTo )
548 // according to rect type fillTo** value
549 if(nFillLR)
551 AddOpt( ESCHER_Prop_fillToLeft, nFillLR );
552 AddOpt( ESCHER_Prop_fillToRight, nFillLR );
554 if(nFillTB)
556 AddOpt( ESCHER_Prop_fillToTop, nFillTB );
557 AddOpt( ESCHER_Prop_fillToBottom, nFillTB );
561 // Transparency gradient
562 if (bTransparentGradient && EscherPropertyValueHelper::GetPropertyValue(
563 aAny, rXPropSet, "FillTransparenceGradient" ) )
565 pGradient = o3tl::doAccess<awt::Gradient>(aAny);
566 if ( pGradient )
568 sal_uInt32 nBlue = GetGradientColor( pGradient, nFirstColor ) >> 16;
569 AddOpt( ESCHER_Prop_fillOpacity,( ( 100 - ( nBlue * 100 / 255 ) ) << 16 ) / 100 );
570 nBlue = GetGradientColor( pGradient, nFirstColor ^ 1 ) >>16 ;
571 AddOpt( ESCHER_Prop_fillBackOpacity,( ( 100 - ( nBlue * 100 / 255 ) ) << 16 )/ 100 );
576 void EscherPropertyContainer::CreateFillProperties(
577 const uno::Reference<beans::XPropertySet> & rXPropSet,
578 bool bEdge , const uno::Reference<drawing::XShape> & rXShape )
580 if ( rXShape.is() )
582 SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rXShape);
583 if ( pObj )
585 const SfxItemSet& aAttr( pObj->GetMergedItemSet() );
586 // transparency with gradient. Means the third setting in transparency page is set
587 bool bTransparentGradient = ( aAttr.GetItemState( XATTR_FILLFLOATTRANSPARENCE ) == SfxItemState::SET ) &&
588 aAttr.Get( XATTR_FILLFLOATTRANSPARENCE ).IsEnabled();
589 CreateFillProperties( rXPropSet, bEdge, bTransparentGradient );
594 void EscherPropertyContainer::CreateFillProperties(
595 const uno::Reference<beans::XPropertySet> & rXPropSet,
596 bool bEdge , bool bTransparentGradient)
599 uno::Any aAny;
600 AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone );
601 AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle );
602 static const OUStringLiteral aPropName( u"FillStyle" );
604 if ( EscherPropertyValueHelper::GetPropertyValue(
605 aAny, rXPropSet, aPropName ) )
607 drawing::FillStyle eFS;
608 if ( ! ( aAny >>= eFS ) )
609 eFS = drawing::FillStyle_SOLID;
610 sal_uInt32 nFillBackColor = 0;
611 switch( eFS )
613 case drawing::FillStyle_GRADIENT :
615 CreateGradientProperties( rXPropSet , bTransparentGradient );
616 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 );
618 break;
620 case drawing::FillStyle_BITMAP :
622 CreateGraphicProperties(rXPropSet, "FillBitmap", true);
623 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 );
624 AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor );
626 break;
627 case drawing::FillStyle_HATCH :
629 CreateGraphicProperties( rXPropSet, "FillHatch", true );
631 break;
632 case drawing::FillStyle_SOLID :
633 default:
635 if ( bTransparentGradient )
636 CreateGradientProperties( rXPropSet , bTransparentGradient );
637 else
639 beans::PropertyState ePropState = EscherPropertyValueHelper::GetPropertyState(
640 rXPropSet, aPropName );
641 if ( ePropState == beans::PropertyState_DIRECT_VALUE )
642 AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid );
644 if ( EscherPropertyValueHelper::GetPropertyValue(
645 aAny, rXPropSet, "FillColor" ) )
647 sal_uInt32 nFillColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny) );
648 nFillBackColor = nFillColor ^ 0xffffff;
649 AddOpt( ESCHER_Prop_fillColor, nFillColor );
651 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100010 );
652 AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor );
654 break;
656 case drawing::FillStyle_NONE :
657 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
658 break;
660 if ( eFS != drawing::FillStyle_NONE )
662 sal_uInt16 nTransparency = ( EscherPropertyValueHelper::GetPropertyValue(
663 aAny, rXPropSet, "FillTransparence", true ) )
664 ? *o3tl::doAccess<sal_Int16>(aAny) : 0;
665 if ( nTransparency )
666 AddOpt( ESCHER_Prop_fillOpacity, ( ( 100 - nTransparency ) << 16 ) / 100 );
669 CreateLineProperties( rXPropSet, bEdge );
672 void EscherPropertyContainer::CreateTextProperties(
673 const uno::Reference< beans::XPropertySet > & rXPropSet, sal_uInt32 nTextId,
674 const bool bIsCustomShape, const bool bIsTextFrame )
676 uno::Any aAny;
677 text::WritingMode eWM( text::WritingMode_LR_TB );
678 drawing::TextVerticalAdjust eVA( drawing::TextVerticalAdjust_TOP );
679 drawing::TextHorizontalAdjust eHA( drawing::TextHorizontalAdjust_LEFT );
681 sal_Int32 nLeft ( 0 );
682 sal_Int32 nTop ( 0 );
683 sal_Int32 nRight ( 0 );
684 sal_Int32 nBottom ( 0 );
686 // used with normal shapes:
687 bool bAutoGrowWidth ( false );
688 const bool bAutoGrowHeight ( false ); //#ii63936 not setting autogrowheight, because minframeheight would be ignored
689 // used with ashapes:
690 bool bWordWrap ( false );
691 bool bAutoGrowSize ( false );
693 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextWritingMode", true ) )
694 aAny >>= eWM;
695 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextVerticalAdjust", true ) )
696 aAny >>= eVA;
697 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextHorizontalAdjust", true ) )
698 aAny >>= eHA;
699 if ( bIsCustomShape )
701 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextWordWrap" ) )
702 aAny >>= bWordWrap;
703 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowHeight", true ) )
704 aAny >>= bAutoGrowSize;
706 else if ( bIsTextFrame )
708 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowWidth", true ) )
709 aAny >>= bAutoGrowWidth;
711 // i63936 not setting autogrowheight, because otherwise
712 // the minframeheight of the text will be ignored
714 // if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowHeight", sal_True ) )
715 // aAny >>= bAutoGrowHeight;
717 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextLeftDistance" ) )
718 aAny >>= nLeft;
719 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextUpperDistance" ) )
720 aAny >>= nTop;
721 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextRightDistance" ) )
722 aAny >>= nRight;
723 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextLowerDistance" ) )
724 aAny >>= nBottom;
726 ESCHER_AnchorText eAnchor = ESCHER_AnchorTop;
727 ESCHER_WrapMode eWrapMode = ESCHER_WrapSquare;
728 sal_uInt32 nTextAttr = 0x40004; // rotate text with shape
730 if ( eWM == text::WritingMode_TB_RL )
731 { // vertical writing
732 switch ( eHA )
734 case drawing::TextHorizontalAdjust_LEFT :
735 eAnchor = ESCHER_AnchorBottom;
736 break;
737 case drawing::TextHorizontalAdjust_CENTER :
738 eAnchor = ESCHER_AnchorMiddle;
739 break;
740 default :
741 case drawing::TextHorizontalAdjust_BLOCK :
742 case drawing::TextHorizontalAdjust_RIGHT :
743 eAnchor = ESCHER_AnchorTop;
744 break;
746 if ( eVA == drawing::TextVerticalAdjust_CENTER )
748 switch ( eAnchor )
750 case ESCHER_AnchorMiddle :
751 eAnchor = ESCHER_AnchorMiddleCentered;
752 break;
753 case ESCHER_AnchorBottom :
754 eAnchor = ESCHER_AnchorBottomCentered;
755 break;
756 default :
757 case ESCHER_AnchorTop :
758 eAnchor = ESCHER_AnchorTopCentered;
759 break;
762 if ( bIsCustomShape )
764 if ( bWordWrap )
765 eWrapMode = ESCHER_WrapSquare;
766 else
767 eWrapMode = ESCHER_WrapNone;
768 if ( bAutoGrowSize )
769 nTextAttr |= 0x20002;
771 else
773 if ( bAutoGrowHeight )
774 eWrapMode = ESCHER_WrapNone;
775 if ( bAutoGrowWidth )
776 nTextAttr |= 0x20002;
779 AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflTtoBA ); // rotate text within shape by 90
781 else
782 { // normal from left to right
783 switch ( eVA )
785 case drawing::TextVerticalAdjust_CENTER :
786 eAnchor = ESCHER_AnchorMiddle;
787 break;
789 case drawing::TextVerticalAdjust_BOTTOM :
790 eAnchor = ESCHER_AnchorBottom;
791 break;
793 default :
794 case drawing::TextVerticalAdjust_TOP :
795 eAnchor = ESCHER_AnchorTop;
796 break;
798 if ( eHA == drawing::TextHorizontalAdjust_CENTER )
800 switch( eAnchor )
802 case ESCHER_AnchorMiddle :
803 eAnchor = ESCHER_AnchorMiddleCentered;
804 break;
805 case ESCHER_AnchorBottom :
806 eAnchor = ESCHER_AnchorBottomCentered;
807 break;
808 case ESCHER_AnchorTop :
809 eAnchor = ESCHER_AnchorTopCentered;
810 break;
811 default: break;
814 if ( bIsCustomShape )
816 if ( bWordWrap )
817 eWrapMode = ESCHER_WrapSquare;
818 else
819 eWrapMode = ESCHER_WrapNone;
820 if ( bAutoGrowSize )
821 nTextAttr |= 0x20002;
823 else
825 if ( bAutoGrowWidth )
826 eWrapMode = ESCHER_WrapNone;
827 if ( bAutoGrowHeight )
828 nTextAttr |= 0x20002;
831 AddOpt( ESCHER_Prop_dxTextLeft, nLeft * 360 );
832 AddOpt( ESCHER_Prop_dxTextRight, nRight * 360 );
833 AddOpt( ESCHER_Prop_dyTextTop, nTop * 360 );
834 AddOpt( ESCHER_Prop_dyTextBottom, nBottom * 360 );
836 AddOpt( ESCHER_Prop_WrapText, eWrapMode );
837 AddOpt( ESCHER_Prop_AnchorText, eAnchor );
838 AddOpt( ESCHER_Prop_FitTextToShape, nTextAttr );
840 if ( nTextId )
841 AddOpt( ESCHER_Prop_lTxid, nTextId );
843 // n#404221: In case of rotation we need to write the txtflTextFlow
844 // attribute too.
845 // fdo#58204: not custom shapes (TODO: other cases when it doesn't work?)
846 if (!bIsTextFrame || bIsCustomShape)
847 return;
849 sal_uInt16 nAngle = EscherPropertyValueHelper::GetPropertyValue(
850 aAny, rXPropSet, "RotateAngle", true ) ?
851 static_cast<sal_uInt16>( ( *o3tl::doAccess<sal_Int32>(aAny) ) + 5 ) / 10 : 0;
852 if (nAngle==900)
854 AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflBtoT );
856 if (nAngle==2700)
858 AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflTtoBA );
862 bool EscherPropertyContainer::GetLineArrow( const bool bLineStart,
863 const uno::Reference<beans::XPropertySet> & rXPropSet,
864 ESCHER_LineEnd& reLineEnd, sal_Int32& rnArrowLength, sal_Int32& rnArrowWidth )
866 const OUString sLine ( bLineStart ? OUString("LineStart") : OUString("LineEnd") );
867 const OUString sLineName ( bLineStart ? OUString("LineStartName") : OUString("LineEndName") );
869 bool bIsArrow = false;
871 uno::Any aAny;
872 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, sLine ) )
874 tools::PolyPolygon aPolyPoly( EscherPropertyContainer::GetPolyPolygon( aAny ) );
875 if ( aPolyPoly.Count() && aPolyPoly[ 0 ].GetSize() )
877 bIsArrow = true;
879 reLineEnd = ESCHER_LineArrowEnd;
880 rnArrowLength = 1;
881 rnArrowWidth = 1;
883 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, sLineName ) )
885 OUString aArrowStartName = *o3tl::doAccess<OUString>(aAny);
886 sal_uInt16 nWhich = bLineStart ? sal_uInt16(XATTR_LINESTART) : sal_uInt16(XATTR_LINEEND);
888 // remove extra space separated number
889 sal_Int32 nPos = aArrowStartName.lastIndexOf(' ');
890 if (nPos > -1 && aArrowStartName.lastIndexOf(' ', nPos) > -1)
891 aArrowStartName = aArrowStartName.copy(0, nPos);
893 OUString aApiName = SvxUnogetApiNameForItem(nWhich, aArrowStartName);
894 bool bIsMapped = true;
895 if ( !aApiName.isEmpty() )
898 // TODO: calculate the best option for ArrowLength and ArrowWidth
899 if ( aApiName == "Arrow concave" )
900 reLineEnd = ESCHER_LineArrowStealthEnd;
901 else if ( aApiName == "Square 45" )
902 reLineEnd = ESCHER_LineArrowDiamondEnd;
903 else if ( aApiName == "Small Arrow" )
904 reLineEnd = ESCHER_LineArrowEnd;
905 else if ( aApiName == "Dimension Lines" )
907 rnArrowLength = 0;
908 rnArrowWidth = 2;
909 reLineEnd = ESCHER_LineArrowOvalEnd;
911 else if ( aApiName == "Double Arrow" )
912 reLineEnd = ESCHER_LineArrowEnd;
913 else if ( aApiName == "Rounded short Arrow" )
914 reLineEnd = ESCHER_LineArrowEnd;
915 else if ( aApiName == "Symmetric Arrow" )
916 reLineEnd = ESCHER_LineArrowEnd;
917 else if ( aApiName == "Line Arrow" )
918 reLineEnd = ESCHER_LineArrowOpenEnd;
919 else if ( aApiName == "Rounded large Arrow" )
920 reLineEnd = ESCHER_LineArrowEnd;
921 else if ( aApiName == "Circle" )
922 reLineEnd = ESCHER_LineArrowOvalEnd;
923 else if ( aApiName == "Square" )
924 reLineEnd = ESCHER_LineArrowDiamondEnd;
925 else if ( aApiName == "Arrow" )
926 reLineEnd = ESCHER_LineArrowEnd;
927 else
928 bIsMapped = false;
931 if ( !bIsMapped && comphelper::string::getTokenCount(aArrowStartName, ' ') == 2 )
933 sal_Int32 nIdx{ 0 };
934 std::u16string_view aArrowName( o3tl::getToken(aArrowStartName, 0, ' ', nIdx ) );
935 if ( aArrowName == u"msArrowEnd" )
936 reLineEnd = ESCHER_LineArrowEnd;
937 else if ( aArrowName == u"msArrowOpenEnd" )
938 reLineEnd = ESCHER_LineArrowOpenEnd;
939 else if ( aArrowName == u"msArrowStealthEnd" )
940 reLineEnd = ESCHER_LineArrowStealthEnd;
941 else if ( aArrowName == u"msArrowDiamondEnd" )
942 reLineEnd = ESCHER_LineArrowDiamondEnd;
943 else if ( aArrowName == u"msArrowOvalEnd" )
944 reLineEnd = ESCHER_LineArrowOvalEnd;
945 else
946 nIdx = -1;
948 // now we have the arrow, and try to determine the arrow size;
949 if ( nIdx>0 )
951 std::u16string_view aArrowSize = o3tl::getToken(aArrowStartName, 0, ' ', nIdx );
952 sal_Int32 nArrowSize = o3tl::toInt32(aArrowSize);
953 rnArrowWidth = ( nArrowSize - 1 ) / 3;
954 rnArrowLength = nArrowSize - ( rnArrowWidth * 3 ) - 1;
960 return bIsArrow;
963 void EscherPropertyContainer::CreateLineProperties(
964 const uno::Reference<beans::XPropertySet> & rXPropSet, bool bEdge )
966 uno::Any aAny;
967 sal_uInt32 nLineFlags = 0x80008;
969 ESCHER_LineEnd eLineEnd;
970 sal_Int32 nArrowLength;
971 sal_Int32 nArrowWidth;
973 bool bSwapLineEnds = false;
974 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "CircleKind", true ) )
976 drawing::CircleKind eCircleKind;
977 if ( aAny >>= eCircleKind )
979 if ( eCircleKind == drawing::CircleKind_ARC )
980 bSwapLineEnds = true;
983 if ( GetLineArrow( !bSwapLineEnds, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
985 AddOpt( ESCHER_Prop_lineStartArrowLength, nArrowLength );
986 AddOpt( ESCHER_Prop_lineStartArrowWidth, nArrowWidth );
987 AddOpt( ESCHER_Prop_lineStartArrowhead, eLineEnd );
988 nLineFlags |= 0x100010;
990 if ( GetLineArrow( bSwapLineEnds, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
992 AddOpt( ESCHER_Prop_lineEndArrowLength, nArrowLength );
993 AddOpt( ESCHER_Prop_lineEndArrowWidth, nArrowWidth );
994 AddOpt( ESCHER_Prop_lineEndArrowhead, eLineEnd );
995 nLineFlags |= 0x100010;
998 // support LineCaps
999 if(EscherPropertyValueHelper::GetPropertyValue(aAny, rXPropSet, "LineCap"))
1001 drawing::LineCap aLineCap(drawing::LineCap_BUTT);
1003 if(aAny >>= aLineCap)
1005 switch (aLineCap)
1007 default: /* drawing::LineCap_BUTT */
1009 AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapFlat);
1010 break;
1012 case drawing::LineCap_ROUND:
1014 AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapRound);
1015 break;
1017 case drawing::LineCap_SQUARE:
1019 AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapSquare);
1020 break;
1026 sal_uInt32 nLineWidth = ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineWidth" ) )
1027 ? *o3tl::doAccess<sal_uInt32>(aAny) : 0;
1028 if ( nLineWidth > 1 )
1029 AddOpt( ESCHER_Prop_lineWidth, nLineWidth * 360 ); // 100TH MM -> PT , 1PT = 12700 EMU
1031 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineStyle" ) )
1033 drawing::LineStyle eLS;
1034 if ( aAny >>= eLS )
1036 switch ( eLS )
1038 case drawing::LineStyle_NONE :
1039 AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); // 80000
1040 break;
1042 case drawing::LineStyle_DASH :
1044 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineDash" ) )
1046 ESCHER_LineDashing eDash = ESCHER_LineSolid;
1047 auto pLineDash = o3tl::doAccess<drawing::LineDash>(aAny);
1048 switch ( pLineDash->Style )
1050 case drawing::DashStyle_ROUND :
1051 case drawing::DashStyle_ROUNDRELATIVE :
1052 AddOpt( ESCHER_Prop_lineEndCapStyle, 0 ); // set Style Round
1053 break;
1054 default : break;
1056 // Try to detect exact prstDash styles. Use a similar method as in oox export.
1057 // Map it to a roughly fitting prstDash in other cases.
1058 bool bIsConverted = false;
1059 bool bIsRelative = pLineDash->Style == drawing::DashStyle_RECTRELATIVE
1060 || pLineDash->Style == drawing::DashStyle_ROUNDRELATIVE;
1061 sal_Int16 nDashes = pLineDash->Dashes;
1062 sal_Int16 nDots = pLineDash->Dots;
1063 sal_Int32 nDashLen = pLineDash->DashLen;
1064 sal_Int32 nDotLen = pLineDash->DotLen;
1065 sal_Int32 nDistance = pLineDash->Distance;
1067 // Caution! The names are misleading. "dot" is always the first dash and "dash"
1068 // the second one, regardless of the actual length. All prstDash
1069 // definitions start with the longer dash and have exact one longer dash.
1070 // Preset line style definitions for binary format are the same as for OOXML.
1071 if (bIsRelative && nDots == 1)
1073 // I'm not sure that LO always uses 100%, because in case of absolute values, LO
1074 // sets length to 0 but treats it as 100%, if the attribute is missing in ODF.
1075 // So to be sure set 100% explicitly in case of relative too.
1076 if (nDashes > 0 && nDashLen == 0)
1077 nDashLen = 100;
1078 if (nDotLen == 0)
1079 nDotLen = 100;
1080 bIsConverted = true;
1081 if (nDotLen == 100 && nDashes == 0 && nDashLen == 0 && nDistance == 300)
1082 eDash = ESCHER_LineDotGEL;
1083 else if (nDotLen == 400 && nDashes == 0 && nDashLen == 0 && nDistance == 300)
1084 eDash = ESCHER_LineDashGEL;
1085 else if (nDotLen == 400 && nDashes == 1 && nDashLen == 100 && nDistance == 300)
1086 eDash = ESCHER_LineDashDotGEL;
1087 else if (nDotLen == 800 && nDashes == 0 && nDashLen == 0 && nDistance == 300)
1088 eDash = ESCHER_LineLongDashGEL;
1089 else if (nDotLen == 800 && nDashes == 1 && nDashLen == 100 && nDistance == 300)
1090 eDash = ESCHER_LineLongDashDotGEL;
1091 else if (nDotLen == 800 && nDashes == 2 && nDashLen == 100 && nDistance == 300)
1092 eDash = ESCHER_LineLongDashDotDotGEL;
1093 else if (nDotLen == 100 && nDashes == 0 && nDashLen == 0 && nDistance == 100)
1094 eDash = ESCHER_LineDotSys;
1095 else if (nDotLen == 300 && nDashes == 0 && nDashLen == 0 && nDistance == 100)
1096 eDash = ESCHER_LineDashSys;
1097 else if (nDotLen == 300 && nDashes == 1 && nDashLen == 100 && nDistance == 100)
1098 eDash = ESCHER_LineDashDotSys;
1099 else if (nDotLen == 300 && nDashes == 2 && nDashLen == 100 && nDistance == 100)
1100 eDash = ESCHER_LineDashDotDotSys;
1101 else
1102 bIsConverted = false;
1105 if (!bIsConverted)
1106 { // Map the style roughly to preset line styles.
1107 if (((!(pLineDash->Dots)) || (!(pLineDash->Dashes)))
1108 || (pLineDash->DotLen == pLineDash->DashLen))
1110 sal_Int32 nLen = pLineDash->DotLen;
1111 if (pLineDash->Dashes)
1112 nLen = pLineDash->DashLen;
1113 if (nLen >= nDistance)
1114 eDash = ESCHER_LineLongDashGEL;
1115 else if (pLineDash->Dots)
1116 eDash = ESCHER_LineDotSys;
1117 else
1118 eDash = ESCHER_LineDashGEL;
1120 else // X Y
1122 if (pLineDash->Dots != pLineDash->Dashes)
1124 if ((pLineDash->DashLen > nDistance) || (pLineDash->DotLen > nDistance))
1125 eDash = ESCHER_LineLongDashDotDotGEL;
1126 else
1127 eDash = ESCHER_LineDashDotDotSys;
1129 else // X Y Y
1131 if ((pLineDash->DashLen > nDistance) || (pLineDash->DotLen > nDistance))
1132 eDash = ESCHER_LineLongDashDotGEL;
1133 else
1134 eDash = ESCHER_LineDashDotGEL;
1138 AddOpt( ESCHER_Prop_lineDashing, eDash );
1141 [[fallthrough]];
1142 case drawing::LineStyle_SOLID :
1143 default:
1145 AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags );
1147 break;
1150 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineColor" ) )
1152 sal_uInt32 nLineColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny) );
1153 AddOpt( ESCHER_Prop_lineColor, nLineColor );
1154 AddOpt( ESCHER_Prop_lineBackColor, nLineColor ^ 0xffffff );
1158 ESCHER_LineJoin eLineJoin = ESCHER_LineJoinMiter;
1159 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineJoint", true ) )
1161 drawing::LineJoint eLJ;
1162 if ( aAny >>= eLJ )
1164 switch ( eLJ )
1166 case drawing::LineJoint_NONE :
1167 case drawing::LineJoint_BEVEL :
1168 eLineJoin = ESCHER_LineJoinBevel;
1169 break;
1170 default:
1171 case drawing::LineJoint_MIDDLE :
1172 case drawing::LineJoint_MITER :
1173 eLineJoin = ESCHER_LineJoinMiter;
1174 break;
1175 case drawing::LineJoint_ROUND :
1176 eLineJoin = ESCHER_LineJoinRound;
1177 break;
1181 AddOpt( ESCHER_Prop_lineJoinStyle, eLineJoin );
1183 if ( EscherPropertyValueHelper::GetPropertyValue(
1184 aAny, rXPropSet, "LineTransparence", true ) )
1186 sal_Int16 nTransparency = 0;
1187 if ( aAny >>= nTransparency )
1188 AddOpt( ESCHER_Prop_lineOpacity, ( ( 100 - nTransparency ) << 16 ) / 100 );
1192 if ( !bEdge )
1194 AddOpt( ESCHER_Prop_fFillOK, 0x1001 );
1195 AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
1199 static Size lcl_SizeToEmu(Size aPrefSize, const MapMode& aPrefMapMode)
1201 Size aRetSize;
1202 if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel)
1203 aRetSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM));
1204 else
1205 aRetSize = OutputDevice::LogicToLogic(aPrefSize, aPrefMapMode, MapMode(MapUnit::Map100thMM));
1206 return aRetSize;
1209 void EscherPropertyContainer::ImplCreateGraphicAttributes( const uno::Reference<beans::XPropertySet> & rXPropSet,
1210 sal_uInt32 nBlibId, bool bCreateCroppingAttributes )
1212 uno::Any aAny;
1214 sal_uInt32 nPicFlags = 0;
1215 drawing::ColorMode eColorMode( drawing::ColorMode_STANDARD );
1216 sal_Int16 nLuminance = 0;
1217 sal_Int32 nContrast = 0;
1219 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "GraphicColorMode" ) )
1220 aAny >>= eColorMode;
1221 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustLuminance" ) )
1222 aAny >>= nLuminance;
1223 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustContrast" ) )
1225 sal_Int16 nC = sal_Int16();
1226 aAny >>= nC;
1227 nContrast = nC;
1230 if ( eColorMode == drawing::ColorMode_WATERMARK )
1232 eColorMode = drawing::ColorMode_STANDARD;
1233 nLuminance += 70;
1234 if ( nLuminance > 100 )
1235 nLuminance = 100;
1236 nContrast -= 70;
1237 if ( nContrast < -100 )
1238 nContrast = -100;
1240 if ( eColorMode == drawing::ColorMode_GREYS )
1241 nPicFlags |= 0x40004;
1242 else if ( eColorMode == drawing::ColorMode_MONO )
1243 nPicFlags |= 0x60006;
1245 if ( nContrast )
1247 nContrast += 100;
1248 if ( nContrast == 100)
1249 nContrast = 0x10000;
1250 else if ( nContrast < 100 )
1252 nContrast *= 0x10000;
1253 nContrast /= 100;
1255 else if ( nContrast < 200 )
1256 nContrast = ( 100 * 0x10000 ) / ( 200 - nContrast );
1257 else
1258 nContrast = 0x7fffffff;
1259 AddOpt( ESCHER_Prop_pictureContrast, nContrast );
1261 if ( nLuminance )
1262 AddOpt( ESCHER_Prop_pictureBrightness, nLuminance * 327 );
1263 if ( nPicFlags )
1264 AddOpt( ESCHER_Prop_pictureActive, nPicFlags );
1266 if ( !(bCreateCroppingAttributes && pGraphicProvider) )
1267 return;
1269 Size aPrefSize;
1270 MapMode aPrefMapMode;
1271 if ( !pGraphicProvider->GetPrefSize( nBlibId, aPrefSize, aPrefMapMode ) )
1272 return;
1274 Size aCropSize(lcl_SizeToEmu(aPrefSize, aPrefMapMode));
1275 if ( !(aCropSize.Width() && aCropSize.Height()) )
1276 return;
1278 if ( !EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "GraphicCrop" ) )
1279 return;
1281 text::GraphicCrop aGraphCrop;
1282 if ( !(aAny >>= aGraphCrop) )
1283 return;
1285 if ( aGraphCrop.Left )
1287 sal_uInt32 nLeft = ( aGraphCrop.Left * 65536 ) / aCropSize.Width();
1288 AddOpt( ESCHER_Prop_cropFromLeft, nLeft );
1290 if ( aGraphCrop.Top )
1292 sal_uInt32 nTop = ( aGraphCrop.Top * 65536 ) / aCropSize.Height();
1293 AddOpt( ESCHER_Prop_cropFromTop, nTop );
1295 if ( aGraphCrop.Right )
1297 sal_uInt32 nRight = ( aGraphCrop.Right * 65536 ) / aCropSize.Width();
1298 AddOpt( ESCHER_Prop_cropFromRight, nRight );
1300 if ( aGraphCrop.Bottom )
1302 sal_uInt32 nBottom = ( aGraphCrop.Bottom * 65536 ) / aCropSize.Height();
1303 AddOpt( ESCHER_Prop_cropFromBottom, nBottom );
1307 void EscherPropertyContainer::CreateShapeProperties( const uno::Reference<drawing::XShape> & rXShape )
1309 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
1310 if ( !aXPropSet.is() )
1311 return;
1313 bool bVisible = false;
1314 bool bPrintable = false;
1315 uno::Any aAny;
1316 sal_uInt32 nShapeAttr = 0;
1317 if (EscherPropertyValueHelper::GetPropertyValue(aAny, aXPropSet, "Visible", true) && (aAny >>= bVisible))
1319 if ( !bVisible )
1320 nShapeAttr |= 0x20002; // set fHidden = true
1322 // This property (fPrint) isn't used in Excel anymore, leaving it for legacy reasons
1323 // one change, based on XLSX: hidden implies not printed, let's not export the fPrint property in that case
1324 if (bVisible && EscherPropertyValueHelper::GetPropertyValue(aAny, aXPropSet, "Printable", true) && (aAny >>= bPrintable))
1326 if ( !bPrintable )
1327 nShapeAttr |= 0x10000; // set fPrint = false;
1329 if ( nShapeAttr )
1330 AddOpt( ESCHER_Prop_fPrint, nShapeAttr );
1333 bool EscherPropertyContainer::CreateOLEGraphicProperties(const uno::Reference<drawing::XShape> & rXShape)
1335 bool bRetValue = false;
1337 if ( rXShape.is() )
1339 SdrObject* pObject = SdrObject::getSdrObjectFromXShape(rXShape); // SJ: leaving unoapi, because currently there is
1340 if (auto pOle2Obj = dynamic_cast<const SdrOle2Obj*>(pObject)) // no access to the native graphic object
1342 const Graphic* pGraphic = pOle2Obj->GetGraphic();
1343 if (pGraphic)
1345 GraphicObject aGraphicObject(*pGraphic);
1346 bRetValue = CreateGraphicProperties(rXShape, aGraphicObject);
1350 return bRetValue;
1353 bool EscherPropertyContainer::CreateGraphicProperties(const uno::Reference<drawing::XShape> & rXShape, const GraphicObject& rGraphicObj)
1355 bool bRetValue = false;
1356 OString aUniqueId(rGraphicObj.GetUniqueID());
1357 if ( !aUniqueId.isEmpty() )
1359 AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture );
1360 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
1362 if ( pGraphicProvider && pPicOutStrm && pShapeBoundRect && aXPropSet.is() )
1364 uno::Any aAny;
1365 std::unique_ptr<awt::Rectangle> pVisArea;
1366 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "VisibleArea" ) )
1368 pVisArea.reset(new awt::Rectangle);
1369 aAny >>= *pVisArea;
1371 sal_uInt32 nBlibId = pGraphicProvider->GetBlibID( *pPicOutStrm, rGraphicObj, pVisArea.get() );
1372 if ( nBlibId )
1374 AddOpt( ESCHER_Prop_pib, nBlibId, true );
1375 ImplCreateGraphicAttributes( aXPropSet, nBlibId, false );
1376 bRetValue = true;
1380 return bRetValue;
1383 bool EscherPropertyContainer::CreateMediaGraphicProperties(const uno::Reference<drawing::XShape> & rXShape)
1385 bool bRetValue = false;
1386 if ( rXShape.is() )
1388 SdrObject* pSdrObject(SdrObject::getSdrObjectFromXShape(rXShape)); // SJ: leaving unoapi, because currently there is
1389 if (auto pSdrMediaObj = dynamic_cast<const SdrMediaObj*>(pSdrObject)) // no access to the native graphic object
1391 GraphicObject aGraphicObject(pSdrMediaObj->getSnapshot());
1392 bRetValue = CreateGraphicProperties(rXShape, aGraphicObject);
1395 return bRetValue;
1398 bool EscherPropertyContainer::ImplCreateEmbeddedBmp(GraphicObject const & rGraphicObject)
1400 if (rGraphicObject.GetType() != GraphicType::NONE)
1402 EscherGraphicProvider aProvider;
1403 SvMemoryStream aMemStrm;
1405 if (aProvider.GetBlibID( aMemStrm, rGraphicObject))
1407 AddOpt(ESCHER_Prop_fillBlip, true, 0, aMemStrm);
1408 return true;
1411 return false;
1414 void EscherPropertyContainer::CreateEmbeddedBitmapProperties(
1415 uno::Reference<awt::XBitmap> const & rxBitmap, drawing::BitmapMode eBitmapMode )
1417 uno::Reference<graphic::XGraphic> xGraphic(rxBitmap, uno::UNO_QUERY);
1418 if (!xGraphic.is())
1419 return;
1420 Graphic aGraphic(xGraphic);
1421 if (aGraphic.IsNone())
1422 return;
1423 GraphicObject aGraphicObject(std::move(aGraphic));
1424 if (aGraphicObject.GetType() == GraphicType::NONE)
1425 return;
1426 if (ImplCreateEmbeddedBmp(aGraphicObject))
1428 // bitmap mode property
1429 bool bRepeat = eBitmapMode == drawing::BitmapMode_REPEAT;
1430 AddOpt( ESCHER_Prop_fillType, bRepeat ? ESCHER_FillTexture : ESCHER_FillPicture );
1434 namespace {
1436 Graphic lclDrawHatch( const drawing::Hatch& rHatch, const Color& rBackColor, bool bFillBackground, const tools::Rectangle& rRect )
1438 // #i121183# For hatch, do no longer create a bitmap with the fixed size of 28x28 pixels. Also
1439 // do not create a bitmap in page size, that would explode file sizes (and have no good quality).
1440 // Better use a MetaFile graphic in page size; thus we have good quality due to vector format and
1441 // no bit file sizes.
1442 ScopedVclPtrInstance< VirtualDevice > pVDev;
1443 GDIMetaFile aMtf;
1445 pVDev->SetOutputSizePixel(Size(2, 2));
1446 pVDev->EnableOutput(false);
1447 pVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
1448 aMtf.Clear();
1449 aMtf.Record(pVDev);
1450 pVDev->SetLineColor();
1451 pVDev->SetFillColor(bFillBackground ? rBackColor : COL_TRANSPARENT);
1452 pVDev->DrawRect(rRect);
1453 pVDev->DrawHatch(tools::PolyPolygon(rRect), Hatch(static_cast<HatchStyle>(rHatch.Style), Color(ColorTransparency, rHatch.Color), rHatch.Distance,
1454 Degree10(rHatch.Angle)));
1455 aMtf.Stop();
1456 aMtf.WindStart();
1457 aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
1458 aMtf.SetPrefSize(rRect.GetSize());
1460 return Graphic(aMtf);
1463 } // namespace
1465 void EscherPropertyContainer::CreateEmbeddedHatchProperties(const drawing::Hatch& rHatch, const Color& rBackColor, bool bFillBackground )
1467 const tools::Rectangle aRect(pShapeBoundRect ? *pShapeBoundRect : tools::Rectangle(Point(0,0), Size(28000, 21000)));
1468 Graphic aGraphic(lclDrawHatch(rHatch, rBackColor, bFillBackground, aRect));
1469 GraphicObject aGraphicObject(std::move(aGraphic));
1471 if (ImplCreateEmbeddedBmp(aGraphicObject))
1472 AddOpt( ESCHER_Prop_fillType, ESCHER_FillTexture );
1475 bool EscherPropertyContainer::CreateGraphicProperties(const uno::Reference<beans::XPropertySet> & rXPropSet,
1476 const OUString& rSource,
1477 const bool bCreateFillBitmap,
1478 const bool bCreateCroppingAttributes,
1479 const bool bFillBitmapModeAllowed,
1480 const bool bOOxmlExport )
1482 bool bRetValue = false;
1483 bool bCreateFillStyles = false;
1485 std::unique_ptr<GraphicAttr> pGraphicAttr;
1486 uno::Reference<graphic::XGraphic> xGraphic;
1488 uno::Any aAny;
1490 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, rSource ) )
1492 bool bMirrored = false;
1493 bool bRotate = true;
1494 bool bIsGraphicMtf = false;
1495 sal_Int16 nTransparency(0);
1496 sal_Int16 nRed(0);
1497 sal_Int16 nGreen(0);
1498 sal_Int16 nBlue(0);
1499 double fGamma(1.0);
1500 drawing::BitmapMode eBitmapMode(drawing::BitmapMode_NO_REPEAT);
1501 OUString aGraphicUrl;
1503 sal_uInt16 nAngle = 0;
1504 if ( rSource == "MetaFile" )
1506 auto & aSeq = *o3tl::doAccess<uno::Sequence<sal_Int8>>(aAny);
1507 const sal_Int8* pArray = aSeq.getConstArray();
1508 sal_uInt32 nArrayLength = aSeq.getLength();
1510 // the metafile is already rotated
1511 bRotate = false;
1513 if (pArray && nArrayLength)
1515 Graphic aGraphic;
1516 SvMemoryStream aStream(const_cast<sal_Int8 *>(pArray), nArrayLength, StreamMode::READ);
1517 ErrCode nErrCode = GraphicConverter::Import(aStream, aGraphic, ConvertDataFormat::WMF);
1518 if ( nErrCode == ERRCODE_NONE )
1520 xGraphic = aGraphic.GetXGraphic();
1521 bIsGraphicMtf = aGraphic.GetType() == GraphicType::GdiMetafile;
1525 else if (rSource == "Bitmap" || rSource == "FillBitmap")
1527 auto xBitmap = aAny.get<uno::Reference<awt::XBitmap>>();
1528 if (xBitmap.is())
1530 xGraphic.set(xBitmap, uno::UNO_QUERY);
1531 Graphic aGraphic(xGraphic);
1532 bIsGraphicMtf = aGraphic.GetType() == GraphicType::GdiMetafile;
1535 else if ( rSource == "Graphic" )
1537 xGraphic = aAny.get<uno::Reference<graphic::XGraphic>>();
1538 bCreateFillStyles = true;
1540 else if ( rSource == "FillHatch" )
1542 drawing::Hatch aHatch;
1543 if ( aAny >>= aHatch )
1545 Color aBackColor;
1546 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillColor" ) )
1548 aBackColor = Color(ColorTransparency, ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny), false ));
1550 bool bFillBackground = false;
1551 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBackground", true ) )
1553 aAny >>= bFillBackground;
1556 const tools::Rectangle aRect(Point(0, 0), pShapeBoundRect ? pShapeBoundRect->GetSize() : Size(28000, 21000));
1557 Graphic aGraphic(lclDrawHatch(aHatch, aBackColor, bFillBackground, aRect));
1558 xGraphic = aGraphic.GetXGraphic();
1559 eBitmapMode = drawing::BitmapMode_REPEAT;
1560 bIsGraphicMtf = aGraphic.GetType() == GraphicType::GdiMetafile;
1564 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "IsMirrored", true ) )
1565 aAny >>= bMirrored;
1567 // #121074# transparency of graphic is not supported in MS formats, get and apply it
1568 // in the GetTransformedGraphic call in GetBlibID
1569 if(EscherPropertyValueHelper::GetPropertyValue(aAny, rXPropSet, "Transparency"))
1571 aAny >>= nTransparency;
1574 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustRed" ) )
1576 aAny >>= nRed;
1579 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustGreen" ) )
1581 aAny >>= nGreen;
1584 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustBlue" ) )
1586 aAny >>= nBlue;
1589 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "Gamma" ) )
1591 aAny >>= fGamma;
1594 if ( bCreateFillBitmap && bFillBitmapModeAllowed )
1596 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapMode", true ) )
1597 aAny >>= eBitmapMode;
1599 else
1601 nAngle = bRotate && EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "RotateAngle", true )
1602 ? static_cast<sal_uInt16>( ( *o3tl::doAccess<sal_Int32>(aAny) ) + 5 ) / 10
1603 : 0;
1606 if (xGraphic.is())
1608 Graphic aGraphic(xGraphic);
1609 aGraphicUrl = aGraphic.getOriginURL();
1612 if (!aGraphicUrl.isEmpty())
1614 bool bConverted = false;
1616 // externally, linked graphic? convert to embedded
1617 // one, if transformations are needed. this is because
1618 // everything < msoxp cannot even handle rotated
1619 // bitmaps.
1620 // And check whether the graphic link target is
1621 // actually supported by mso.
1622 INetURLObject aTmp( aGraphicUrl );
1623 GraphicDescriptor aDescriptor(aTmp);
1624 (void)aDescriptor.Detect();
1625 const GraphicFileFormat nFormat = aDescriptor.GetFileFormat();
1627 // can MSO handle it?
1628 if ( bMirrored || nAngle || nTransparency || nRed || nGreen || nBlue || (1.0 != fGamma) ||
1629 (nFormat != GraphicFileFormat::BMP &&
1630 nFormat != GraphicFileFormat::GIF &&
1631 nFormat != GraphicFileFormat::JPG &&
1632 nFormat != GraphicFileFormat::PNG &&
1633 nFormat != GraphicFileFormat::TIF &&
1634 nFormat != GraphicFileFormat::PCT &&
1635 nFormat != GraphicFileFormat::WMF &&
1636 nFormat != GraphicFileFormat::WMZ &&
1637 nFormat != GraphicFileFormat::EMF &&
1638 nFormat != GraphicFileFormat::EMZ) )
1640 std::unique_ptr<SvStream> pIn(::utl::UcbStreamHelper::CreateStream(
1641 aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ));
1642 if ( pIn )
1644 Graphic aGraphic;
1645 ErrCode nErrCode = GraphicConverter::Import( *pIn, aGraphic );
1647 if ( nErrCode == ERRCODE_NONE )
1649 xGraphic = aGraphic.GetXGraphic();
1650 bConverted = true;
1652 // else: simply keep the graphic link
1656 if (!bConverted && pGraphicProvider )
1658 const OUString& rBaseURI( pGraphicProvider->GetBaseURI() );
1659 INetURLObject aBaseURI( rBaseURI );
1660 if( aBaseURI.GetProtocol() == aTmp.GetProtocol() )
1662 OUString aRelUrl( INetURLObject::GetRelURL( rBaseURI, aGraphicUrl ) );
1663 if ( !aRelUrl.isEmpty() )
1664 aGraphicUrl = aRelUrl;
1669 if (!aGraphicUrl.isEmpty() || xGraphic.is())
1671 if(bMirrored || nTransparency || nRed || nGreen || nBlue || (1.0 != fGamma))
1673 pGraphicAttr.reset(new GraphicAttr);
1675 if(bMirrored)
1677 pGraphicAttr->SetMirrorFlags(BmpMirrorFlags::Horizontal);
1680 if(nTransparency)
1682 pGraphicAttr->SetAlpha(255 - (nTransparency * 255) / 100);
1685 if(nRed)
1687 pGraphicAttr->SetChannelR(nRed);
1690 if(nGreen)
1692 pGraphicAttr->SetChannelG(nGreen);
1695 if(nBlue)
1697 pGraphicAttr->SetChannelB(nBlue);
1700 if(1.0 != fGamma)
1702 pGraphicAttr->SetGamma(fGamma);
1706 if(nAngle && bIsGraphicMtf)
1708 AddOpt( ESCHER_Prop_Rotation, ( ( (static_cast<sal_Int32>(nAngle) << 16 ) / 10 ) + 0x8000 ) &~ 0xffff );
1711 if ( eBitmapMode == drawing::BitmapMode_REPEAT )
1713 sal_Int32 nSizeX = 0,nSizeY = 0,nOffsetX = 0,nOffsetY = 0,nPosOffsetX = 0,nPosOffsetY = 0;
1714 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapSizeX", true ) )
1716 aAny >>= nSizeX;
1718 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapSizeY", true ) )
1720 aAny >>= nSizeY;
1722 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapOffsetX", true ) )
1724 aAny >>= nOffsetX;
1726 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapOffsetY", true ) )
1728 aAny >>= nOffsetY;
1730 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapPositionOffsetX", true ) )
1732 aAny >>= nPosOffsetX;
1734 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapPositionOffsetY", true ) )
1736 aAny >>= nPosOffsetY;
1738 if(nSizeX == -100 && nSizeY == -100 && nOffsetX == 0 && nOffsetY == 0 && nPosOffsetX == 0 && nPosOffsetY == 0)
1739 AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture );
1740 else
1741 AddOpt( ESCHER_Prop_fillType, ESCHER_FillTexture );
1743 else
1744 AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture );
1746 if (xGraphic.is())
1748 Graphic aGraphic(xGraphic);
1749 if (!aGraphic.getOriginURL().isEmpty())
1751 AddOpt(ESCHER_Prop_pibName, aGraphicUrl);
1752 sal_uInt32 nPibFlags = 0;
1753 GetOpt(ESCHER_Prop_pibFlags, nPibFlags);
1754 AddOpt(ESCHER_Prop_pibFlags, ESCHER_BlipFlagLinkToFile | ESCHER_BlipFlagFile | ESCHER_BlipFlagDoNotSave | nPibFlags);
1756 else if (pGraphicProvider && pPicOutStrm && pShapeBoundRect) // write out embedded graphic
1758 GraphicObject aGraphicObject(aGraphic);
1759 const sal_uInt32 nBlibId(pGraphicProvider->GetBlibID(*pPicOutStrm, aGraphicObject, nullptr, pGraphicAttr.get()));
1761 if(nBlibId)
1763 if(bCreateFillBitmap)
1765 AddOpt(ESCHER_Prop_fillBlip, nBlibId, true);
1767 else
1769 AddOpt( ESCHER_Prop_pib, nBlibId, true );
1770 ImplCreateGraphicAttributes( rXPropSet, nBlibId, bCreateCroppingAttributes );
1773 bRetValue = true;
1776 else
1778 EscherGraphicProvider aProvider;
1779 SvMemoryStream aMemStrm;
1780 GraphicObject aGraphicObject(std::move(aGraphic));
1782 if (aProvider.GetBlibID(aMemStrm, aGraphicObject, nullptr, pGraphicAttr.get(), bOOxmlExport))
1784 AddOpt(ESCHER_Prop_fillBlip, true, 0, aMemStrm);
1785 bRetValue = true;
1791 pGraphicAttr.reset();
1792 if ( bCreateFillStyles )
1793 CreateFillProperties( rXPropSet, true );
1795 return bRetValue;
1798 tools::PolyPolygon EscherPropertyContainer::GetPolyPolygon( const uno::Reference< drawing::XShape > & rXShape )
1800 tools::PolyPolygon aRetPolyPoly;
1801 uno::Reference< beans::XPropertySet > aXPropSet;
1802 uno::Any aAny( rXShape->queryInterface(
1803 cppu::UnoType<beans::XPropertySet>::get()));
1805 if ( aAny >>= aXPropSet )
1807 bool bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "PolyPolygonBezier", true );
1808 if ( !bHasProperty )
1809 bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "PolyPolygon", true );
1810 if ( !bHasProperty )
1811 bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "Polygon", true );
1812 if ( bHasProperty )
1813 aRetPolyPoly = GetPolyPolygon( aAny );
1815 return aRetPolyPoly;
1818 // adapting to basegfx::B2DPolyPolygon now, has no sense to do corrections in the
1819 // old tools::PolyPolygon creation code. Convert to that at return time
1820 tools::PolyPolygon EscherPropertyContainer::GetPolyPolygon( const uno::Any& rAny )
1822 basegfx::B2DPolyPolygon aRetval;
1824 if(auto pBCC = o3tl::tryAccess<drawing::PolyPolygonBezierCoords>(rAny))
1826 aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon(*pBCC);
1828 else if(auto pCC = o3tl::tryAccess<drawing::PointSequenceSequence>(rAny))
1830 aRetval = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(*pCC);
1832 else if(auto pC = o3tl::tryAccess<drawing::PointSequence>(rAny))
1834 aRetval.append(basegfx::utils::UnoPointSequenceToB2DPolygon(*pC));
1837 basegfx::B2DPolyPolygon aRetval2;
1839 for(sal_uInt32 a(0); a < aRetval.count(); a++)
1841 if(0 != aRetval.getB2DPolygon(a).count())
1843 aRetval2.append(aRetval.getB2DPolygon(a));
1847 return tools::PolyPolygon(aRetval2);
1850 bool EscherPropertyContainer::CreatePolygonProperties(
1851 const uno::Reference<beans::XPropertySet> & rXPropSet,
1852 sal_uInt32 nFlags,
1853 bool bBezier,
1854 awt::Rectangle& rGeoRect,
1855 tools::Polygon const * pPolygon )
1857 tools::PolyPolygon aPolyPolygon;
1859 if(nullptr != pPolygon)
1861 aPolyPolygon.Insert(*pPolygon);
1863 else
1865 uno::Any aAny;
1867 if(EscherPropertyValueHelper::GetPropertyValue(
1868 aAny,
1869 rXPropSet,
1870 bBezier ? OUString("PolyPolygonBezier") : OUString("PolyPolygon"),
1871 true))
1873 aPolyPolygon = GetPolyPolygon(aAny);
1875 else
1877 return false;
1881 if(0 == aPolyPolygon.Count())
1883 return false;
1886 if(0 != (nFlags & ESCHER_CREATEPOLYGON_LINE))
1888 if((1 == aPolyPolygon.Count()) && (2 == aPolyPolygon[0].GetSize()))
1890 const tools::Polygon& rPoly(aPolyPolygon[0]);
1892 rGeoRect = awt::Rectangle(
1893 rPoly[0].X(),
1894 rPoly[0].Y(),
1895 rPoly[1].X() - rPoly[0].X(),
1896 rPoly[1].Y() - rPoly[0].Y());
1898 return true;
1901 return false;
1904 const tools::Rectangle aRect(aPolyPolygon.GetBoundRect());
1906 rGeoRect = awt::Rectangle(
1907 aRect.Left(),
1908 aRect.Top(),
1909 aRect.GetWidth(),
1910 aRect.GetHeight());
1912 const sal_uInt16 nPolyCount(aPolyPolygon.Count());
1913 sal_uInt32 nTotalPoints(0);
1915 std::vector< sal_uInt8 > aVertices
1917 0, 0, 0, 0,
1918 static_cast<sal_uInt8>(0xf0),
1919 static_cast<sal_uInt8>(0xff)
1922 std::vector< sal_uInt8 > aSegments
1924 0, 0, 0, 0,
1925 static_cast<sal_uInt8>(2),
1926 static_cast<sal_uInt8>(0)
1929 for(sal_uInt16 j(0); j < nPolyCount; ++j)
1931 const tools::Polygon aPolygon(aPolyPolygon[j]);
1932 const sal_uInt16 nPoints(aPolygon.GetSize());
1934 if(0 == nPoints)
1936 continue;
1939 // Polygon start
1940 aSegments.push_back(static_cast<sal_uInt8>(0x0));
1941 aSegments.push_back(static_cast<sal_uInt8>(0x40));
1943 sal_uInt16 nSegmentIgnoreCounter(0);
1945 // write points from polygon to buffer
1946 for(sal_uInt16 i(0); i < nPoints; ++i)
1948 Point aPoint(aPolygon[i]);
1950 aPoint.AdjustX(-(rGeoRect.X));
1951 aPoint.AdjustY(-(rGeoRect.Y));
1953 aVertices.push_back(static_cast<sal_uInt8>(aPoint.X()));
1954 aVertices.push_back(static_cast<sal_uInt8>(aPoint.X() >> 8));
1955 aVertices.push_back(static_cast<sal_uInt8>(aPoint.Y()));
1956 aVertices.push_back(static_cast<sal_uInt8>(aPoint.Y() >> 8));
1958 nTotalPoints++;
1960 if(0 != nSegmentIgnoreCounter)
1962 nSegmentIgnoreCounter--;
1964 else
1966 aSegments.push_back(static_cast<sal_uInt8>(0));
1968 if(bBezier)
1970 aSegments.push_back(static_cast<sal_uInt8>(0xb3));
1972 else
1974 aSegments.push_back(static_cast<sal_uInt8>(0xac));
1977 if(i + 1 == nPoints)
1979 if(nPolyCount > 1)
1981 // end of polygon
1982 aSegments.push_back(static_cast<sal_uInt8>(1));
1983 aSegments.push_back(static_cast<sal_uInt8>(0x60));
1986 else
1988 aSegments.push_back(static_cast<sal_uInt8>(1));
1990 if(PolyFlags::Control == aPolygon.GetFlags(i + 1))
1992 aSegments.push_back(static_cast<sal_uInt8>(0x20));
1993 nSegmentIgnoreCounter = 2;
1995 else
1997 aSegments.push_back(static_cast<sal_uInt8>(0));
2004 if(0 == nTotalPoints || aSegments.size() < 6 || aVertices.size() < 6)
2005 return false;
2007 // Little endian
2008 aVertices[0] = static_cast<sal_uInt8>(nTotalPoints);
2009 aVertices[1] = static_cast<sal_uInt8>(nTotalPoints >> 8);
2010 aVertices[2] = static_cast<sal_uInt8>(nTotalPoints);
2011 aVertices[3] = static_cast<sal_uInt8>(nTotalPoints >> 8);
2013 aSegments.push_back(static_cast<sal_uInt8>(0));
2014 aSegments.push_back(static_cast<sal_uInt8>(0x80));
2016 const sal_uInt32 nSegmentBufSize(aSegments.size() - 6);
2017 aSegments[0] = static_cast<sal_uInt8>(nSegmentBufSize >> 1);
2018 aSegments[1] = static_cast<sal_uInt8>(nSegmentBufSize >> 9);
2019 aSegments[2] = static_cast<sal_uInt8>(nSegmentBufSize >> 1);
2020 aSegments[3] = static_cast<sal_uInt8>(nSegmentBufSize >> 9);
2022 AddOpt(
2023 ESCHER_Prop_geoRight,
2024 rGeoRect.Width);
2025 AddOpt(
2026 ESCHER_Prop_geoBottom,
2027 rGeoRect.Height);
2028 AddOpt(
2029 ESCHER_Prop_shapePath,
2030 ESCHER_ShapeComplex);
2031 AddOpt(
2032 ESCHER_Prop_pVertices,
2033 true,
2034 aVertices.size() - 6,
2035 aVertices);
2036 AddOpt(
2037 ESCHER_Prop_pSegmentInfo,
2038 true,
2039 aSegments.size(),
2040 aSegments);
2042 return true;
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 static 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 static 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.setY( aStart.Y() +4 );
2091 if ( aEnd.X() == aStart.X() )
2092 aEnd.setX( 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 static void lcl_Rotate(Degree100 nAngle, Point center, Point& pt)
2111 nAngle = NormAngle36000(nAngle);
2113 int cs, sn;
2114 switch (nAngle.get())
2116 case 0:
2117 cs =1;
2118 sn =0;
2119 break;
2120 case 9000:
2121 cs =0;
2122 sn =1;
2123 break;
2124 case 18000:
2125 cs = -1;
2126 sn = 0;
2127 break;
2128 case 27000:
2129 cs = 0;
2130 sn = -1;
2131 break;
2132 default:
2133 return;
2135 sal_Int32 x0 =pt.X()-center.X();
2136 sal_Int32 y0 =pt.Y()-center.Y();
2137 pt.setX(center.X()+ x0*cs-y0*sn );
2138 pt.setY(center.Y()+ y0*cs+x0*sn );
2141 FlipV defines that the shape will be flipped vertically about the center of its bounding box.
2142 Generally, draw the connector from top to bottom, from left to right when meet the adjust value,
2143 but when (X1>X2 or Y1>Y2),the draw director must be reverse, FlipV or FlipH should be set to true.
2145 static bool lcl_GetAngle(tools::Polygon &rPoly, ShapeFlag& rShapeFlags,sal_Int32& nAngle )
2147 Point aStart = rPoly[0];
2148 Point aEnd = rPoly[rPoly.GetSize()-1];
2149 nAngle = ( rPoly[1].X() == aStart.X() ) ? 9000: 0 ;
2150 Point p1(aStart.X(),aStart.Y());
2151 Point p2(aEnd.X(),aEnd.Y());
2152 if ( nAngle )
2154 Point center((aEnd.X()+aStart.X())>>1,(aEnd.Y()+aStart.Y())>>1);
2155 lcl_Rotate(Degree100(-nAngle), center,p1);
2156 lcl_Rotate(Degree100(-nAngle), center,p2);
2158 if ( p1.X() > p2.X() )
2160 if ( nAngle )
2161 rShapeFlags |= ShapeFlag::FlipV;
2162 else
2163 rShapeFlags |= ShapeFlag::FlipH;
2166 if ( p1.Y() > p2.Y() )
2168 if ( nAngle )
2169 rShapeFlags |= ShapeFlag::FlipH;
2170 else
2171 rShapeFlags |= ShapeFlag::FlipV;
2174 if ( (rShapeFlags&ShapeFlag::FlipH) && (rShapeFlags&ShapeFlag::FlipV) )
2176 rShapeFlags &= ~ShapeFlag( ShapeFlag::FlipH | ShapeFlag::FlipV );
2177 nAngle +=18000;
2180 if ( nAngle )
2182 // Set angle properties
2183 nAngle *= 655;
2184 nAngle += 0x8000;
2185 nAngle &=~0xffff; // round nAngle to whole number of degrees
2186 return true;
2188 return false;
2190 bool EscherPropertyContainer::CreateConnectorProperties(
2191 const uno::Reference<drawing::XShape> & rXShape,
2192 EscherSolverContainer& rSolverContainer, awt::Rectangle& rGeoRect,
2193 sal_uInt16& rShapeType, ShapeFlag& rShapeFlags )
2195 bool bRetValue = false;
2196 rShapeType = 0;
2197 rShapeFlags = ShapeFlag::NONE;
2199 if ( rXShape.is() )
2201 uno::Reference<beans::XPropertySet> aXPropSet;
2202 uno::Reference<drawing::XShape> aShapeA, aShapeB;
2203 uno::Any aAny( rXShape->queryInterface( cppu::UnoType<beans::XPropertySet>::get()));
2204 if ( aAny >>= aXPropSet )
2206 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeKind", true ) )
2208 drawing::ConnectorType eCt;
2209 aAny >>= eCt;
2210 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeStartPoint" ) )
2212 awt::Point aStartPoint = *o3tl::doAccess<awt::Point>(aAny);
2213 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeEndPoint" ) )
2215 awt::Point aEndPoint = *o3tl::doAccess<awt::Point>(aAny);
2217 rShapeFlags = ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Connector;
2218 rGeoRect = awt::Rectangle( aStartPoint.X, aStartPoint.Y,
2219 ( aEndPoint.X - aStartPoint.X ) + 1, ( aEndPoint.Y - aStartPoint.Y ) + 1 );
2220 // set standard's FLIP in below code
2221 if ( eCt != drawing::ConnectorType_STANDARD)
2223 if ( rGeoRect.Height < 0 ) // justify
2225 rShapeFlags |= ShapeFlag::FlipV;
2226 rGeoRect.Y = aEndPoint.Y;
2227 rGeoRect.Height = -rGeoRect.Height;
2229 if ( rGeoRect.Width < 0 )
2231 rShapeFlags |= ShapeFlag::FlipH;
2232 rGeoRect.X = aEndPoint.X;
2233 rGeoRect.Width = -rGeoRect.Width;
2236 sal_uInt32 nAdjustValue1, nAdjustValue2;
2237 nAdjustValue1 = nAdjustValue2 = 0x2a30;
2239 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeStartConnection" ) )
2240 aAny >>= aShapeA;
2241 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeEndConnection" ) )
2242 aAny >>= aShapeB;
2243 rSolverContainer.AddConnector( rXShape, aStartPoint, aShapeA, aEndPoint, aShapeB );
2244 switch ( eCt )
2246 case drawing::ConnectorType_CURVE :
2248 rShapeType = ESCHER_ShpInst_CurvedConnector3;
2249 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleCurved );
2250 AddOpt( ESCHER_Prop_adjustValue, nAdjustValue1 );
2251 AddOpt( ESCHER_Prop_adjust2Value, -static_cast<sal_Int32>(nAdjustValue2) );
2253 break;
2255 case drawing::ConnectorType_STANDARD :// Connector 2->5
2257 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "PolyPolygonBezier" ) )
2259 tools::PolyPolygon aPolyPolygon = GetPolyPolygon( aAny );
2260 tools::Polygon aPoly;
2261 if ( aPolyPolygon.Count() > 0 )
2263 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleBent );
2264 aPoly = aPolyPolygon[ 0 ];
2265 sal_Int32 nAdjCount = lcl_GetAdjustValueCount( aPoly );
2266 rShapeType = static_cast<sal_uInt16>( ESCHER_ShpInst_BentConnector2 + nAdjCount);
2267 for ( sal_Int32 i = 0 ; i < nAdjCount; ++ i)
2268 AddOpt( static_cast<sal_uInt16>( ESCHER_Prop_adjustValue+i) , lcl_GetConnectorAdjustValue( aPoly, i ) );
2270 sal_Int32 nAngle=0;
2271 if (lcl_GetAngle(aPoly,rShapeFlags,nAngle ))
2273 AddOpt( ESCHER_Prop_Rotation, nAngle );
2276 else
2278 rShapeType = ESCHER_ShpInst_BentConnector3;
2279 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleBent );
2282 break;
2283 default:
2284 case drawing::ConnectorType_LINE :
2285 case drawing::ConnectorType_LINES : // Connector 2->5
2287 rShapeType = ESCHER_ShpInst_StraightConnector1;
2288 AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleStraight );
2290 break;
2292 CreateLineProperties( aXPropSet, false );
2293 bRetValue = true;
2299 return bRetValue;
2302 void EscherPropertyContainer::CreateShadowProperties(
2303 const uno::Reference<beans::XPropertySet> & rXPropSet )
2305 uno::Any aAny;
2307 sal_uInt32 nLineFlags = 0; // default : shape has no line
2308 sal_uInt32 nFillFlags = 0x10; // shape is filled
2310 GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags );
2311 GetOpt( ESCHER_Prop_fNoFillHitTest, nFillFlags );
2313 sal_uInt32 nDummy;
2314 bool bGraphic = GetOpt( DFF_Prop_pib, nDummy ) || GetOpt( DFF_Prop_pibName, nDummy ) || GetOpt( DFF_Prop_pibFlags, nDummy );
2316 sal_uInt32 nShadowFlags = 0x20000;
2317 if ( ( nLineFlags & 8 ) || ( nFillFlags & 0x10 ) || bGraphic )
2319 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "Shadow", true ) )
2321 bool bHasShadow = false; // shadow is possible only if at least a fillcolor, linecolor or graphic is set
2322 if ( (aAny >>= bHasShadow) && bHasShadow )
2324 nShadowFlags |= 2;
2325 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowColor" ) )
2326 AddOpt( ESCHER_Prop_shadowColor, ImplGetColor( *o3tl::doAccess<sal_uInt32>(aAny) ) );
2327 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowXDistance" ) )
2328 AddOpt( ESCHER_Prop_shadowOffsetX, *o3tl::doAccess<sal_Int32>(aAny) * 360 );
2329 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowYDistance" ) )
2330 AddOpt( ESCHER_Prop_shadowOffsetY, *o3tl::doAccess<sal_Int32>(aAny) * 360 );
2331 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowTransparence" ) )
2332 AddOpt( ESCHER_Prop_shadowOpacity, 0x10000 - (static_cast<sal_uInt32>(*o3tl::doAccess<sal_uInt16>(aAny)) * 655 ) );
2336 AddOpt( ESCHER_Prop_fshadowObscured, nShadowFlags );
2339 sal_Int32 EscherPropertyContainer::GetValueForEnhancedCustomShapeParameter( const drawing::EnhancedCustomShapeParameter& rParameter,
2340 const std::vector< sal_Int32 >& rEquationOrder, bool bAdjustTrans )
2342 sal_Int32 nValue = 0;
2343 if ( rParameter.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
2345 double fValue(0.0);
2346 if ( rParameter.Value >>= fValue )
2347 nValue = static_cast<sal_Int32>(fValue);
2349 else
2350 rParameter.Value >>= nValue;
2352 switch( rParameter.Type )
2354 case drawing::EnhancedCustomShapeParameterType::EQUATION :
2356 size_t nIndex = static_cast<size_t>(nValue);
2357 OSL_ASSERT(nIndex < rEquationOrder.size());
2358 if ( nIndex < rEquationOrder.size() )
2360 nValue = static_cast<sal_uInt16>(rEquationOrder[ nIndex ]);
2361 nValue |= sal_uInt32(0x80000000);
2364 break;
2365 case drawing::EnhancedCustomShapeParameterType::ADJUSTMENT:
2367 if(bAdjustTrans)
2369 sal_uInt32 nAdjustValue = 0;
2370 bool bGot = GetOpt(static_cast<sal_uInt16>( DFF_Prop_adjustValue + nValue ), nAdjustValue);
2371 if(bGot) nValue = static_cast<sal_Int32>(nAdjustValue);
2374 break;
2375 case drawing::EnhancedCustomShapeParameterType::NORMAL :
2376 default:
2377 break;
2378 /* not sure if it is allowed to set following values
2379 (but they are not yet used)
2380 case drawing::EnhancedCustomShapeParameterType::BOTTOM :
2381 case drawing::EnhancedCustomShapeParameterType::RIGHT :
2382 case drawing::EnhancedCustomShapeParameterType::TOP :
2383 case drawing::EnhancedCustomShapeParameterType::LEFT :
2386 return nValue;
2389 static bool GetValueForEnhancedCustomShapeHandleParameter( sal_Int32& nRetValue, const drawing::EnhancedCustomShapeParameter& rParameter )
2391 bool bSpecial = false;
2392 nRetValue = 0;
2393 if ( rParameter.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
2395 double fValue(0.0);
2396 if ( rParameter.Value >>= fValue )
2397 nRetValue = static_cast<sal_Int32>(fValue);
2399 else
2400 rParameter.Value >>= nRetValue;
2402 switch( rParameter.Type )
2404 case drawing::EnhancedCustomShapeParameterType::EQUATION :
2406 nRetValue += 3;
2407 bSpecial = true;
2409 break;
2410 case drawing::EnhancedCustomShapeParameterType::ADJUSTMENT :
2412 nRetValue += 0x100;
2413 bSpecial = true;
2415 break;
2416 case drawing::EnhancedCustomShapeParameterType::TOP :
2417 case drawing::EnhancedCustomShapeParameterType::LEFT :
2419 nRetValue = 0;
2420 bSpecial = true;
2422 break;
2423 case drawing::EnhancedCustomShapeParameterType::RIGHT :
2424 case drawing::EnhancedCustomShapeParameterType::BOTTOM :
2426 nRetValue = 1;
2427 bSpecial = true;
2429 break;
2430 case drawing::EnhancedCustomShapeParameterType::NORMAL :
2434 break;
2436 return bSpecial;
2439 static void ConvertEnhancedCustomShapeEquation(
2440 const SdrObjCustomShape& rSdrObjCustomShape,
2441 std::vector< EnhancedCustomShapeEquation >& rEquations,
2442 std::vector< sal_Int32 >& rEquationOrder )
2444 uno::Sequence< OUString > sEquationSource;
2445 const SdrCustomShapeGeometryItem& rGeometryItem =
2446 rSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
2447 const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "Equations" );
2448 if ( pAny )
2449 *pAny >>= sEquationSource;
2450 sal_Int32 nEquationSourceCount = sEquationSource.getLength();
2451 if ( !(nEquationSourceCount && (nEquationSourceCount <= 128)) )
2452 return;
2454 sal_Int32 i;
2455 for ( i = 0; i < nEquationSourceCount; i++ )
2457 EnhancedCustomShape2d aCustomShape2d(
2458 const_cast< SdrObjCustomShape& >(rSdrObjCustomShape));
2461 std::shared_ptr< EnhancedCustomShape::ExpressionNode > aExpressNode(
2462 EnhancedCustomShape::FunctionParser::parseFunction(
2463 sEquationSource[ i ], aCustomShape2d));
2464 drawing::EnhancedCustomShapeParameter aPara( aExpressNode->fillNode( rEquations, nullptr, 0 ) );
2465 if ( aPara.Type != drawing::EnhancedCustomShapeParameterType::EQUATION )
2467 EnhancedCustomShapeEquation aEquation;
2468 aEquation.nOperation = 0;
2469 EnhancedCustomShape::FillEquationParameter( aPara, 0, aEquation );
2470 rEquations.push_back( aEquation );
2473 catch ( const EnhancedCustomShape::ParseError& )
2475 EnhancedCustomShapeEquation aEquation; // ups, we should not be here,
2476 aEquation.nOperation = 0; // creating a default equation with value 1
2477 aEquation.nPara[ 0 ] = 1; // hoping that this will not break anything
2478 rEquations.push_back( aEquation );
2480 catch ( ... )
2482 EnhancedCustomShapeEquation aEquation; // #i112309# EnhancedCustomShape::Parse error
2483 aEquation.nOperation = 0; // not caught on linux platform
2484 aEquation.nPara[ 0 ] = 1;
2485 rEquations.push_back( aEquation );
2487 rEquationOrder.push_back( rEquations.size() - 1 );
2489 // now updating our old equation indices, they are marked with a bit in the hiword of nOperation
2490 for (auto & equation : rEquations)
2492 sal_uInt32 nMask = 0x20000000;
2493 for( i = 0; i < 3; i++ )
2495 if ( equation.nOperation & nMask )
2497 equation.nOperation ^= nMask;
2498 const size_t nIndex(equation.nPara[ i ] & 0x3ff);
2500 // #i124661# check index access, there are cases where this is out of bound leading
2501 // to errors up to crashes when executed
2502 if(nIndex < rEquationOrder.size())
2504 equation.nPara[ i ] = rEquationOrder[ nIndex ] | 0x400;
2506 else
2508 OSL_ENSURE(false, "Attempted out of bound access to rEquationOrder of CustomShape (!)");
2511 nMask <<= 1;
2516 bool EscherPropertyContainer::IsDefaultObject(
2517 const SdrObjCustomShape& rSdrObjCustomShape,
2518 const MSO_SPT eShapeType)
2520 switch(eShapeType)
2522 // if the custom shape is not default shape of ppt, return false;
2523 case mso_sptTearDrop:
2524 return false;
2526 default:
2527 break;
2530 return rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Equations )
2531 && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Viewbox )
2532 && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Path )
2533 && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Gluepoints )
2534 && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Segments )
2535 && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::StretchX )
2536 && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::StretchY )
2537 && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::TextFrames );
2540 void EscherPropertyContainer::LookForPolarHandles( const MSO_SPT eShapeType, sal_Int32& nAdjustmentsWhichNeedsToBeConverted )
2542 const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eShapeType );
2543 if ( !(pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles) )
2544 return;
2546 sal_Int32 k, nkCount = pDefCustomShape->nHandles;
2547 const SvxMSDffHandle* pData = pDefCustomShape->pHandles;
2548 for ( k = 0; k < nkCount; k++, pData++ )
2550 if ( pData->nFlags & SvxMSDffHandleFlags::POLAR )
2552 if ( ( pData->nPositionY >= 0x256 ) || ( pData->nPositionY <= 0x107 ) )
2553 nAdjustmentsWhichNeedsToBeConverted |= ( 1 << k );
2558 bool EscherPropertyContainer::GetAdjustmentValue( const drawing::EnhancedCustomShapeAdjustmentValue & rkProp, sal_Int32 nIndex, sal_Int32 nAdjustmentsWhichNeedsToBeConverted, sal_Int32& nValue )
2560 if ( rkProp.State != beans::PropertyState_DIRECT_VALUE )
2561 return false;
2563 bool bUseFixedFloat = ( nAdjustmentsWhichNeedsToBeConverted & ( 1 << nIndex ) ) != 0;
2564 if ( rkProp.Value.getValueTypeClass() == uno::TypeClass_DOUBLE )
2566 double fValue(0.0);
2567 rkProp.Value >>= fValue;
2568 if ( bUseFixedFloat )
2569 fValue *= 65536.0;
2570 nValue = static_cast<sal_Int32>(fValue);
2572 else
2574 rkProp.Value >>= nValue;
2575 if ( bUseFixedFloat )
2576 nValue <<= 16;
2579 return true;
2582 void EscherPropertyContainer::CreateCustomShapeProperties( const MSO_SPT eShapeType, const uno::Reference< drawing::XShape > & rXShape )
2584 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
2585 if ( !aXPropSet.is() )
2586 return;
2588 SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(rXShape));
2589 if(!pSdrObjCustomShape)
2591 return;
2594 SdrObjCustomShape& rSdrObjCustomShape = *pSdrObjCustomShape;
2595 uno::Any aGeoPropSet = aXPropSet->getPropertyValue( "CustomShapeGeometry" );
2596 uno::Sequence< beans::PropertyValue > aGeoPropSeq;
2597 if ( !(aGeoPropSet >>= aGeoPropSeq) )
2598 return;
2600 static const OUStringLiteral sViewBox ( u"ViewBox" );
2601 static const OUStringLiteral sTextRotateAngle ( u"TextRotateAngle" );
2602 static const OUStringLiteral sExtrusion ( u"Extrusion" );
2603 static const OUStringLiteral sEquations ( u"Equations" );
2604 static const OUStringLiteral sPath ( u"Path" );
2605 static const OUStringLiteral sTextPath ( u"TextPath" );
2606 static const OUStringLiteral sHandles ( u"Handles" );
2607 static const OUStringLiteral sAdjustmentValues ( u"AdjustmentValues" );
2609 bool bAdjustmentValuesProp = false;
2610 uno::Any aAdjustmentValuesProp;
2611 bool bPathCoordinatesProp = false;
2612 uno::Any aPathCoordinatesProp;
2614 sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
2615 uno::Sequence< beans::PropertyValues > aHandlesPropSeq;
2616 bool bPredefinedHandlesUsed = true;
2617 const bool bIsDefaultObject(
2618 IsDefaultObject(
2619 rSdrObjCustomShape,
2620 eShapeType));
2622 // convert property "Equations" into std::vector< EnhancedCustomShapeEquationEquation >
2623 std::vector< EnhancedCustomShapeEquation > aEquations;
2624 std::vector< sal_Int32 > aEquationOrder;
2625 ConvertEnhancedCustomShapeEquation(
2626 rSdrObjCustomShape,
2627 aEquations,
2628 aEquationOrder);
2630 sal_Int32 i, nCount = aGeoPropSeq.getLength();
2631 for ( i = 0; i < nCount; i++ )
2633 const beans::PropertyValue& rProp = aGeoPropSeq[ i ];
2634 if ( rProp.Name == sViewBox )
2636 if ( !bIsDefaultObject )
2638 awt::Rectangle aViewBox;
2639 if ( rProp.Value >>= aViewBox )
2641 AddOpt( DFF_Prop_geoLeft, aViewBox.X );
2642 AddOpt( DFF_Prop_geoTop, aViewBox.Y );
2643 AddOpt( DFF_Prop_geoRight, aViewBox.X + aViewBox.Width );
2644 AddOpt( DFF_Prop_geoBottom,aViewBox.Y + aViewBox.Height );
2648 else if ( rProp.Name == sTextRotateAngle )
2650 double f = 0;
2651 if ( rProp.Value >>= f )
2653 double fTextRotateAngle = fmod( f, 360.0 );
2654 if ( fTextRotateAngle < 0 )
2655 fTextRotateAngle = 360 + fTextRotateAngle;
2656 if ( ( fTextRotateAngle < 271.0 ) && ( fTextRotateAngle > 269.0 ) )
2657 AddOpt( DFF_Prop_cdirFont, mso_cdir90 );
2658 else if ( ( fTextRotateAngle < 181.0 ) && ( fTextRotateAngle > 179.0 ) )
2659 AddOpt( DFF_Prop_cdirFont, mso_cdir180 );
2660 else if ( ( fTextRotateAngle < 91.0 ) && ( fTextRotateAngle > 79.0 ) )
2661 AddOpt( DFF_Prop_cdirFont, mso_cdir270 );
2664 else if ( rProp.Name == sExtrusion )
2666 uno::Sequence< beans::PropertyValue > aExtrusionPropSeq;
2667 if ( rProp.Value >>= aExtrusionPropSeq )
2669 sal_uInt32 nLightFaceFlagsOrg, nLightFaceFlags;
2670 sal_uInt32 nFillHarshFlagsOrg, nFillHarshFlags;
2671 nLightFaceFlagsOrg = nLightFaceFlags = 0x000001;
2672 nFillHarshFlagsOrg = nFillHarshFlags = 0x00001e;
2673 if ( GetOpt( DFF_Prop_fc3DLightFace, nLightFaceFlags ) )
2674 nLightFaceFlagsOrg = nLightFaceFlags;
2675 if ( GetOpt( DFF_Prop_fc3DFillHarsh, nFillHarshFlags ) )
2676 nFillHarshFlagsOrg = nFillHarshFlags;
2678 sal_Int32 r, nrCount = aExtrusionPropSeq.getLength();
2679 for ( r = 0; r < nrCount; r++ )
2681 const beans::PropertyValue& rrProp = aExtrusionPropSeq[ r ];
2683 if ( rrProp.Name == sExtrusion )
2685 bool bExtrusionOn;
2686 if ( rrProp.Value >>= bExtrusionOn )
2688 nLightFaceFlags |= 0x80000;
2689 if ( bExtrusionOn )
2690 nLightFaceFlags |= 8;
2691 else
2692 nLightFaceFlags &=~8;
2695 else if ( rrProp.Name == "Brightness" )
2697 double fExtrusionBrightness = 0;
2698 if ( rrProp.Value >>= fExtrusionBrightness )
2699 AddOpt( DFF_Prop_c3DAmbientIntensity, static_cast<sal_Int32>( fExtrusionBrightness * 655.36 ) );
2701 else if ( rrProp.Name == "Depth" )
2703 double fDepth = 0;
2704 double fFraction = 0;
2705 drawing::EnhancedCustomShapeParameterPair aDepthParaPair;
2706 if ( ( rrProp.Value >>= aDepthParaPair ) && ( aDepthParaPair.First.Value >>= fDepth ) && ( aDepthParaPair.Second.Value >>= fFraction ) )
2708 double fForeDepth = fDepth * fFraction;
2709 double fBackDepth = fDepth - fForeDepth;
2711 fBackDepth *= 360.0;
2712 AddOpt( DFF_Prop_c3DExtrudeBackward, static_cast<sal_Int32>(fBackDepth) );
2714 if ( fForeDepth != 0.0 )
2716 fForeDepth *= 360.0;
2717 AddOpt( DFF_Prop_c3DExtrudeForward, static_cast<sal_Int32>(fForeDepth) );
2721 else if ( rrProp.Name == "Diffusion" )
2723 double fExtrusionDiffusion = 0;
2724 if ( rrProp.Value >>= fExtrusionDiffusion )
2725 AddOpt( DFF_Prop_c3DDiffuseAmt, static_cast<sal_Int32>( fExtrusionDiffusion * 655.36 ) );
2727 else if ( rrProp.Name == "NumberOfLineSegments" )
2729 sal_Int32 nExtrusionNumberOfLineSegments = 0;
2730 if ( rrProp.Value >>= nExtrusionNumberOfLineSegments )
2731 AddOpt( DFF_Prop_c3DTolerance, nExtrusionNumberOfLineSegments );
2733 else if ( rrProp.Name == "LightFace" )
2735 bool bExtrusionLightFace;
2736 if ( rrProp.Value >>= bExtrusionLightFace )
2738 nLightFaceFlags |= 0x10000;
2739 if ( bExtrusionLightFace )
2740 nLightFaceFlags |= 1;
2741 else
2742 nLightFaceFlags &=~1;
2745 else if ( rrProp.Name == "FirstLightHarsh" )
2747 bool bExtrusionFirstLightHarsh;
2748 if ( rrProp.Value >>= bExtrusionFirstLightHarsh )
2750 nFillHarshFlags |= 0x20000;
2751 if ( bExtrusionFirstLightHarsh )
2752 nFillHarshFlags |= 2;
2753 else
2754 nFillHarshFlags &=~2;
2757 else if ( rrProp.Name == "SecondLightHarsh" )
2759 bool bExtrusionSecondLightHarsh;
2760 if ( rrProp.Value >>= bExtrusionSecondLightHarsh )
2762 nFillHarshFlags |= 0x10000;
2763 if ( bExtrusionSecondLightHarsh )
2764 nFillHarshFlags |= 1;
2765 else
2766 nFillHarshFlags &=~1;
2769 else if ( rrProp.Name == "FirstLightLevel" )
2771 double fExtrusionFirstLightLevel = 0;
2772 if ( rrProp.Value >>= fExtrusionFirstLightLevel )
2773 AddOpt( DFF_Prop_c3DKeyIntensity, static_cast<sal_Int32>( fExtrusionFirstLightLevel * 655.36 ) );
2775 else if ( rrProp.Name == "SecondLightLevel" )
2777 double fExtrusionSecondLightLevel = 0;
2778 if ( rrProp.Value >>= fExtrusionSecondLightLevel )
2779 AddOpt( DFF_Prop_c3DFillIntensity, static_cast<sal_Int32>( fExtrusionSecondLightLevel * 655.36 ) );
2781 else if ( rrProp.Name == "FirstLightDirection" )
2783 drawing::Direction3D aExtrusionFirstLightDirection;
2784 if ( rrProp.Value >>= aExtrusionFirstLightDirection )
2786 AddOpt( DFF_Prop_c3DKeyX, static_cast<sal_Int32>(aExtrusionFirstLightDirection.DirectionX) );
2787 AddOpt( DFF_Prop_c3DKeyY, static_cast<sal_Int32>(aExtrusionFirstLightDirection.DirectionY) );
2788 AddOpt( DFF_Prop_c3DKeyZ, static_cast<sal_Int32>(aExtrusionFirstLightDirection.DirectionZ) );
2791 else if ( rrProp.Name == "SecondLightDirection" )
2793 drawing::Direction3D aExtrusionSecondLightPosition;
2794 if ( rrProp.Value >>= aExtrusionSecondLightPosition )
2796 AddOpt( DFF_Prop_c3DFillX, static_cast<sal_Int32>(aExtrusionSecondLightPosition.DirectionX) );
2797 AddOpt( DFF_Prop_c3DFillY, static_cast<sal_Int32>(aExtrusionSecondLightPosition.DirectionY) );
2798 AddOpt( DFF_Prop_c3DFillZ, static_cast<sal_Int32>(aExtrusionSecondLightPosition.DirectionZ) );
2801 else if ( rrProp.Name == "Metal" )
2803 bool bExtrusionMetal;
2804 if ( rrProp.Value >>= bExtrusionMetal )
2806 nLightFaceFlags |= 0x40000;
2807 if ( bExtrusionMetal )
2808 nLightFaceFlags |= 4;
2809 else
2810 nLightFaceFlags &=~4;
2813 else if ( rrProp.Name == "ShadeMode" )
2815 drawing::ShadeMode eExtrusionShadeMode;
2816 if ( rrProp.Value >>= eExtrusionShadeMode )
2818 sal_uInt32 nRenderMode;
2819 switch( eExtrusionShadeMode )
2821 default:
2822 case drawing::ShadeMode_FLAT :
2823 case drawing::ShadeMode_PHONG :
2824 case drawing::ShadeMode_SMOOTH :
2825 nRenderMode = mso_FullRender;
2826 break;
2827 case drawing::ShadeMode_DRAFT :
2829 nRenderMode = mso_Wireframe;
2831 break;
2833 AddOpt( DFF_Prop_c3DRenderMode, nRenderMode );
2836 else if ( rrProp.Name == "RotateAngle" )
2838 double fExtrusionAngleX = 0;
2839 double fExtrusionAngleY = 0;
2840 drawing::EnhancedCustomShapeParameterPair aRotateAnglePair;
2841 if ( ( rrProp.Value >>= aRotateAnglePair ) && ( aRotateAnglePair.First.Value >>= fExtrusionAngleX ) && ( aRotateAnglePair.Second.Value >>= fExtrusionAngleY ) )
2843 fExtrusionAngleX *= 65536;
2844 fExtrusionAngleY *= 65536;
2845 AddOpt( DFF_Prop_c3DXRotationAngle, static_cast<sal_Int32>(fExtrusionAngleX) );
2846 AddOpt( DFF_Prop_c3DYRotationAngle, static_cast<sal_Int32>(fExtrusionAngleY) );
2849 else if ( rrProp.Name == "RotationCenter" )
2851 drawing::Direction3D aExtrusionRotationCenter;
2852 if ( rrProp.Value >>= aExtrusionRotationCenter )
2854 // tdf#145904 X- and Y-component is fraction, Z-component in EMU
2855 AddOpt( DFF_Prop_c3DRotationCenterX, static_cast<sal_Int32>( aExtrusionRotationCenter.DirectionX * 65536.0 ) );
2856 AddOpt( DFF_Prop_c3DRotationCenterY, static_cast<sal_Int32>( aExtrusionRotationCenter.DirectionY * 65536.0 ) );
2857 AddOpt( DFF_Prop_c3DRotationCenterZ, static_cast<sal_Int32>( aExtrusionRotationCenter.DirectionZ * 360.0 ) );
2858 nFillHarshFlags &=~8; // don't use AutoRotationCenter;
2861 else if ( rrProp.Name == "Shininess" )
2863 double fExtrusionShininess = 0;
2864 if ( rrProp.Value >>= fExtrusionShininess )
2866 // ODF to MS Office conversion invers to msdffimp.cxx
2867 fExtrusionShininess = basegfx::fround(fExtrusionShininess / 10.0);
2868 AddOpt( DFF_Prop_c3DShininess, static_cast<sal_Int32>(fExtrusionShininess) );
2871 else if ( rrProp.Name == "Skew" )
2873 double fSkewAmount = 0;
2874 double fSkewAngle = 0;
2875 drawing::EnhancedCustomShapeParameterPair aSkewParaPair;
2876 if ( ( rrProp.Value >>= aSkewParaPair ) && ( aSkewParaPair.First.Value >>= fSkewAmount ) && ( aSkewParaPair.Second.Value >>= fSkewAngle ) )
2878 AddOpt( DFF_Prop_c3DSkewAmount, static_cast<sal_Int32>(fSkewAmount) );
2879 AddOpt( DFF_Prop_c3DSkewAngle, static_cast<sal_Int32>( fSkewAngle * 65536 ) );
2882 else if ( rrProp.Name == "Specularity" )
2884 double fExtrusionSpecularity = 0;
2885 if ( rrProp.Value >>= fExtrusionSpecularity )
2886 AddOpt( DFF_Prop_c3DSpecularAmt, static_cast<sal_Int32>( fExtrusionSpecularity * 655.36 ) );
2888 else if ( rrProp.Name == "ProjectionMode" )
2890 drawing::ProjectionMode eExtrusionProjectionMode;
2891 if ( rrProp.Value >>= eExtrusionProjectionMode )
2893 nFillHarshFlags |= 0x40000;
2894 if ( eExtrusionProjectionMode == drawing::ProjectionMode_PARALLEL )
2895 nFillHarshFlags |= 4;
2896 else
2897 nFillHarshFlags &=~4;
2900 else if ( rrProp.Name == "ViewPoint" )
2902 drawing::Position3D aExtrusionViewPoint;
2903 if ( rrProp.Value >>= aExtrusionViewPoint )
2905 aExtrusionViewPoint.PositionX *= 360.0;
2906 aExtrusionViewPoint.PositionY *= 360.0;
2907 aExtrusionViewPoint.PositionZ *= 360.0;
2908 AddOpt( DFF_Prop_c3DXViewpoint, static_cast<sal_Int32>(aExtrusionViewPoint.PositionX) );
2909 AddOpt( DFF_Prop_c3DYViewpoint, static_cast<sal_Int32>(aExtrusionViewPoint.PositionY) );
2910 AddOpt( DFF_Prop_c3DZViewpoint, static_cast<sal_Int32>(aExtrusionViewPoint.PositionZ) );
2913 else if ( rrProp.Name == "Origin" )
2915 double fExtrusionOriginX = 0;
2916 double fExtrusionOriginY = 0;
2917 drawing::EnhancedCustomShapeParameterPair aOriginPair;
2918 if ( ( rrProp.Value >>= aOriginPair ) && ( aOriginPair.First.Value >>= fExtrusionOriginX ) && ( aOriginPair.Second.Value >>= fExtrusionOriginY ) )
2920 AddOpt( DFF_Prop_c3DOriginX, static_cast<sal_Int32>( fExtrusionOriginX * 65536 ) );
2921 AddOpt( DFF_Prop_c3DOriginY, static_cast<sal_Int32>( fExtrusionOriginY * 65536 ) );
2924 else if ( rrProp.Name == "Color" )
2926 bool bExtrusionColor;
2927 if ( rrProp.Value >>= bExtrusionColor )
2929 nLightFaceFlags |= 0x20000;
2930 if ( bExtrusionColor )
2932 nLightFaceFlags |= 2;
2933 uno::Any aFillColor2;
2934 if ( EscherPropertyValueHelper::GetPropertyValue( aFillColor2, aXPropSet, "FillColor2", true ) )
2936 sal_uInt32 nFillColor = ImplGetColor( *o3tl::doAccess<sal_uInt32>(aFillColor2) );
2937 AddOpt( DFF_Prop_c3DExtrusionColor, nFillColor );
2940 else
2941 nLightFaceFlags &=~2;
2945 if ( nLightFaceFlags != nLightFaceFlagsOrg )
2946 AddOpt( DFF_Prop_fc3DLightFace, nLightFaceFlags );
2947 if ( nFillHarshFlags != nFillHarshFlagsOrg )
2948 AddOpt( DFF_Prop_fc3DFillHarsh, nFillHarshFlags );
2951 else if ( rProp.Name == sEquations )
2953 if ( !bIsDefaultObject )
2955 sal_uInt16 nElements = static_cast<sal_uInt16>(aEquations.size());
2956 if ( nElements )
2958 sal_uInt16 nElementSize = 8;
2959 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
2960 SvMemoryStream aMemStrm( nStreamSize );
2961 aMemStrm.WriteUInt16( nElements )
2962 .WriteUInt16( nElements )
2963 .WriteUInt16( nElementSize );
2965 for (auto const& equation : aEquations)
2967 aMemStrm.WriteUInt16( equation.nOperation )
2968 .WriteInt16(
2969 std::clamp(
2970 equation.nPara[ 0 ], sal_Int32(SAL_MIN_INT16),
2971 sal_Int32(SAL_MAX_INT16)) )
2972 .WriteInt16(
2973 std::clamp(
2974 equation.nPara[ 1 ], sal_Int32(SAL_MIN_INT16),
2975 sal_Int32(SAL_MAX_INT16)) )
2976 .WriteInt16(
2977 std::clamp(
2978 equation.nPara[ 2 ], sal_Int32(SAL_MIN_INT16),
2979 sal_Int32(SAL_MAX_INT16)) );
2982 AddOpt(DFF_Prop_pFormulas, true, 6, aMemStrm);
2984 else
2986 AddOpt(DFF_Prop_pFormulas, 0, true);
2990 else if ( rProp.Name == sPath )
2992 uno::Sequence< beans::PropertyValue > aPathPropSeq;
2993 if ( rProp.Value >>= aPathPropSeq )
2995 sal_uInt32 nPathFlags, nPathFlagsOrg;
2996 nPathFlagsOrg = nPathFlags = 0x39;
2997 if ( GetOpt( DFF_Prop_fFillOK, nPathFlags ) )
2998 nPathFlagsOrg = nPathFlags;
3000 sal_Int32 r, nrCount = aPathPropSeq.getLength();
3001 for ( r = 0; r < nrCount; r++ )
3003 const beans::PropertyValue& rrProp = aPathPropSeq[ r ];
3005 if ( rrProp.Name == "ExtrusionAllowed" )
3007 bool bExtrusionAllowed;
3008 if ( rrProp.Value >>= bExtrusionAllowed )
3010 nPathFlags |= 0x100000;
3011 if ( bExtrusionAllowed )
3012 nPathFlags |= 16;
3013 else
3014 nPathFlags &=~16;
3017 else if ( rrProp.Name == "ConcentricGradientFillAllowed" )
3019 bool bConcentricGradientFillAllowed;
3020 if ( rrProp.Value >>= bConcentricGradientFillAllowed )
3022 nPathFlags |= 0x20000;
3023 if ( bConcentricGradientFillAllowed )
3024 nPathFlags |= 2;
3025 else
3026 nPathFlags &=~2;
3029 else if ( rrProp.Name == "TextPathAllowed" )
3031 bool bTextPathAllowed;
3032 if ( rrProp.Value >>= bTextPathAllowed )
3034 nPathFlags |= 0x40000;
3035 if ( bTextPathAllowed )
3036 nPathFlags |= 4;
3037 else
3038 nPathFlags &=~4;
3041 else if ( rrProp.Name == "Coordinates" )
3043 if ( !bIsDefaultObject )
3045 aPathCoordinatesProp = rrProp.Value;
3046 bPathCoordinatesProp = true;
3049 else if ( rrProp.Name == "GluePoints" )
3051 if ( !bIsDefaultObject )
3053 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aGluePoints;
3054 if ( rrProp.Value >>= aGluePoints )
3056 // creating the vertices
3057 sal_uInt16 nElements = static_cast<sal_uInt16>(aGluePoints.getLength());
3058 if ( nElements )
3060 sal_uInt16 j, nElementSize = 8;
3061 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3062 SvMemoryStream aMemStrm( nStreamSize );
3063 aMemStrm.WriteUInt16( nElements )
3064 .WriteUInt16( nElements )
3065 .WriteUInt16( nElementSize );
3066 for( j = 0; j < nElements; j++ )
3068 sal_Int32 X = GetValueForEnhancedCustomShapeParameter( aGluePoints[ j ].First, aEquationOrder );
3069 sal_Int32 Y = GetValueForEnhancedCustomShapeParameter( aGluePoints[ j ].Second, aEquationOrder );
3070 aMemStrm.WriteInt32( X )
3071 .WriteInt32( Y );
3074 AddOpt(DFF_Prop_connectorPoints, true, 6, aMemStrm); // -6
3076 else
3078 AddOpt(DFF_Prop_connectorPoints, 0, true);
3083 else if ( rrProp.Name == "GluePointType" )
3085 sal_Int16 nGluePointType = sal_Int16();
3086 if ( rrProp.Value >>= nGluePointType )
3087 AddOpt( DFF_Prop_connectorType, static_cast<sal_uInt16>(nGluePointType) );
3089 else if ( rrProp.Name == "Segments" )
3091 if ( !bIsDefaultObject )
3093 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
3094 if ( rrProp.Value >>= aSegments )
3096 // creating seginfo
3097 if ( aSegments.hasElements() )
3099 sal_uInt16 j, nElements = static_cast<sal_uInt16>(aSegments.getLength());
3100 sal_uInt16 nElementSize = 2;
3101 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3102 SvMemoryStream aMemStrm( nStreamSize );
3103 aMemStrm.WriteUInt16( nElements )
3104 .WriteUInt16( nElements )
3105 .WriteUInt16( nElementSize );
3106 for ( j = 0; j < nElements; j++ )
3108 // The segment type is stored in the upper 3 bits
3109 // and segment count is stored in the lower 13
3110 // bits.
3112 // If the segment type is msopathEscape, the lower 13 bits
3113 // are divided in a 5 bit escape code and 8 bit
3114 // vertex count (not segment count!)
3115 sal_uInt16 nVal = static_cast<sal_uInt16>(aSegments[ j ].Count);
3116 switch( aSegments[ j ].Command )
3118 case drawing::EnhancedCustomShapeSegmentCommand::UNKNOWN :
3119 case drawing::EnhancedCustomShapeSegmentCommand::LINETO :
3120 break;
3121 case drawing::EnhancedCustomShapeSegmentCommand::MOVETO :
3122 nVal = (msopathMoveTo << 13);
3123 break;
3124 case drawing::EnhancedCustomShapeSegmentCommand::CURVETO :
3126 nVal |= (msopathCurveTo << 13);
3128 break;
3129 case drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH :
3131 nVal = 1;
3132 nVal |= (msopathClose << 13);
3134 break;
3135 case drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH :
3137 nVal = (msopathEnd << 13);
3139 break;
3140 case drawing::EnhancedCustomShapeSegmentCommand::NOFILL :
3142 nVal = (msopathEscape << 13) | (10 << 8);
3144 break;
3145 case drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE :
3147 nVal = (msopathEscape << 13) | (11 << 8);
3149 break;
3150 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
3152 nVal *= 3;
3153 nVal |= (msopathEscape << 13) | (1 << 8);
3155 break;
3156 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
3158 nVal *= 3;
3159 nVal |= (msopathEscape << 13) | (2 << 8);
3161 break;
3162 case drawing::EnhancedCustomShapeSegmentCommand::ARCTO :
3164 nVal <<= 2;
3165 nVal |= (msopathEscape << 13) | (3 << 8);
3167 break;
3168 case drawing::EnhancedCustomShapeSegmentCommand::ARC :
3170 nVal <<= 2;
3171 nVal |= (msopathEscape << 13) | (4 << 8);
3173 break;
3174 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
3176 nVal <<= 2;
3177 nVal |= (msopathEscape << 13) | (5 << 8);
3179 break;
3180 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
3182 nVal <<= 2;
3183 nVal |= (msopathEscape << 13) | (6 << 8);
3185 break;
3186 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
3188 nVal |= (msopathEscape << 13) | (7 << 8);
3190 break;
3191 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
3193 nVal |= (msopathEscape << 13) | (8 << 8);
3195 break;
3197 aMemStrm.WriteUInt16( nVal );
3200 AddOpt(DFF_Prop_pSegmentInfo, false, 6, aMemStrm);
3202 else
3204 AddOpt(DFF_Prop_pSegmentInfo, 0, true);
3209 else if ( rrProp.Name == "StretchX" )
3211 if ( !bIsDefaultObject )
3213 sal_Int32 nStretchX = 0;
3214 if ( rrProp.Value >>= nStretchX )
3215 AddOpt( DFF_Prop_stretchPointX, nStretchX );
3218 else if ( rrProp.Name == "StretchY" )
3220 if ( !bIsDefaultObject )
3222 sal_Int32 nStretchY = 0;
3223 if ( rrProp.Value >>= nStretchY )
3224 AddOpt( DFF_Prop_stretchPointY, nStretchY );
3227 else if ( rrProp.Name == "TextFrames" )
3229 if ( !bIsDefaultObject )
3231 uno::Sequence<drawing::EnhancedCustomShapeTextFrame> aPathTextFrames;
3232 if ( rrProp.Value >>= aPathTextFrames )
3234 if ( aPathTextFrames.hasElements() )
3236 sal_uInt16 j, nElements = static_cast<sal_uInt16>(aPathTextFrames.getLength());
3237 sal_uInt16 nElementSize = 16;
3238 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3239 SvMemoryStream aMemStrm( nStreamSize );
3240 aMemStrm.WriteUInt16( nElements )
3241 .WriteUInt16( nElements )
3242 .WriteUInt16( nElementSize );
3243 for ( j = 0; j < nElements; j++ )
3245 sal_Int32 nLeft = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].TopLeft.First, aEquationOrder );
3246 sal_Int32 nTop = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].TopLeft.Second, aEquationOrder );
3247 sal_Int32 nRight = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].BottomRight.First, aEquationOrder );
3248 sal_Int32 nBottom = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].BottomRight.Second, aEquationOrder );
3250 aMemStrm.WriteInt32( nLeft )
3251 .WriteInt32( nTop )
3252 .WriteInt32( nRight )
3253 .WriteInt32( nBottom );
3256 AddOpt(DFF_Prop_textRectangles, true, 6, aMemStrm);
3258 else
3260 AddOpt(DFF_Prop_textRectangles, 0, true);
3266 if ( nPathFlags != nPathFlagsOrg )
3267 AddOpt( DFF_Prop_fFillOK, nPathFlags );
3270 else if ( rProp.Name == sTextPath )
3272 uno::Sequence< beans::PropertyValue > aTextPathPropSeq;
3273 if ( rProp.Value >>= aTextPathPropSeq )
3275 sal_uInt32 nTextPathFlagsOrg, nTextPathFlags;
3276 nTextPathFlagsOrg = nTextPathFlags = 0xffff1000; // default
3277 if ( GetOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags ) )
3278 nTextPathFlagsOrg = nTextPathFlags;
3280 sal_Int32 r, nrCount = aTextPathPropSeq.getLength();
3281 for ( r = 0; r < nrCount; r++ )
3283 const beans::PropertyValue& rrProp = aTextPathPropSeq[ r ];
3285 if ( rrProp.Name == sTextPath )
3287 bool bTextPathOn;
3288 if ( rrProp.Value >>= bTextPathOn )
3290 nTextPathFlags |= 0x40000000;
3291 if ( bTextPathOn )
3293 nTextPathFlags |= 0x4000;
3295 sal_uInt32 nPathFlags = 0x39;
3296 GetOpt( DFF_Prop_fFillOK, nPathFlags ); // SJ: can be removed if we are supporting the TextPathAllowed property in XML
3297 nPathFlags |= 0x40004;
3298 AddOpt( DFF_Prop_fFillOK, nPathFlags );
3300 else
3301 nTextPathFlags &=~0x4000;
3304 else if ( rrProp.Name == "TextPathMode" )
3306 drawing::EnhancedCustomShapeTextPathMode eTextPathMode;
3307 if ( rrProp.Value >>= eTextPathMode )
3309 nTextPathFlags |= 0x05000000;
3310 nTextPathFlags &=~0x500; // TextPathMode_NORMAL
3311 if ( eTextPathMode == drawing::EnhancedCustomShapeTextPathMode_PATH )
3312 nTextPathFlags |= 0x100;
3313 else if ( eTextPathMode == drawing::EnhancedCustomShapeTextPathMode_SHAPE )
3314 nTextPathFlags |= 0x500;
3317 else if ( rrProp.Name == "ScaleX" )
3319 bool bTextPathScaleX;
3320 if ( rrProp.Value >>= bTextPathScaleX )
3322 nTextPathFlags |= 0x00400000;
3323 if ( bTextPathScaleX )
3324 nTextPathFlags |= 0x40;
3325 else
3326 nTextPathFlags &=~0x40;
3329 else if ( rrProp.Name == "SameLetterHeights" )
3331 bool bSameLetterHeights;
3332 if ( rrProp.Value >>= bSameLetterHeights )
3334 nTextPathFlags |= 0x00800000;
3335 if ( bSameLetterHeights )
3336 nTextPathFlags |= 0x80;
3337 else
3338 nTextPathFlags &=~0x80;
3342 if ( nTextPathFlags & 0x4000 ) // Is FontWork ?
3344 // FontWork Text
3345 OUString aText;
3346 uno::Reference< text::XSimpleText > xText( rXShape, uno::UNO_QUERY );
3347 if ( xText.is() )
3348 aText = xText->getString();
3349 if ( aText.isEmpty() )
3350 aText = "your text"; // TODO: moving into a resource
3351 AddOpt( DFF_Prop_gtextUNICODE, aText );
3353 // FontWork Font
3354 OUString aFontName;
3355 uno::Any aAny = aXPropSet->getPropertyValue( "CharFontName" );
3356 aAny >>= aFontName;
3357 if ( aFontName.isEmpty() )
3358 aFontName = "Arial Black";
3359 AddOpt( DFF_Prop_gtextFont, aFontName );
3361 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharScaleWidth", true ) )
3363 sal_Int16 nCharScaleWidth = 100;
3364 if ( aAny >>= nCharScaleWidth )
3366 if ( nCharScaleWidth != 100 )
3368 sal_Int32 nVal = nCharScaleWidth * 655;
3369 AddOpt( DFF_Prop_gtextSpacing, nVal );
3373 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharHeight", true ) )
3375 float fCharHeight = 0.0;
3376 if ( aAny >>= fCharHeight )
3378 sal_Int32 nTextSize = static_cast< sal_Int32 > ( fCharHeight * 65536 );
3379 AddOpt(ESCHER_Prop_gtextSize, nTextSize);
3382 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharKerning", true ) )
3384 sal_Int16 nCharKerning = sal_Int16();
3385 if ( aAny >>= nCharKerning )
3387 nTextPathFlags |= 0x10000000;
3388 if ( nCharKerning )
3389 nTextPathFlags |= 0x1000;
3390 else
3391 nTextPathFlags &=~0x1000;
3394 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharPosture", true ) )
3396 awt::FontSlant eFontSlant;
3397 if ( aAny >>= eFontSlant )
3399 nTextPathFlags |= 0x100010;
3400 if ( eFontSlant != awt::FontSlant_NONE )
3401 nTextPathFlags |= 0x10;
3402 else
3403 nTextPathFlags &=~0x10;
3406 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharWeight", true ) )
3408 float fFontWidth = 0;
3409 if ( aAny >>= fFontWidth )
3411 nTextPathFlags |= 0x200020;
3412 if ( fFontWidth > awt::FontWeight::NORMAL )
3413 nTextPathFlags |= 0x20;
3414 else
3415 nTextPathFlags &=~0x20;
3418 // export gTextAlign attr
3419 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "TextHorizontalAdjust", true ) )
3421 MSO_GeoTextAlign gTextAlign = mso_alignTextCenter;
3422 drawing::TextHorizontalAdjust eHA( drawing::TextHorizontalAdjust_LEFT );
3423 aAny >>= eHA;
3424 switch( eHA )
3426 case drawing::TextHorizontalAdjust_LEFT :
3427 gTextAlign = mso_alignTextLeft;
3428 break;
3429 case drawing::TextHorizontalAdjust_CENTER:
3430 gTextAlign = mso_alignTextCenter;
3431 break;
3432 case drawing::TextHorizontalAdjust_RIGHT:
3433 gTextAlign = mso_alignTextRight;
3434 break;
3435 case drawing::TextHorizontalAdjust_BLOCK:
3437 drawing::TextFitToSizeType const eFTS(
3438 rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_FITTOSIZE ).GetValue() );
3439 if (eFTS == drawing::TextFitToSizeType_ALLLINES ||
3440 eFTS == drawing::TextFitToSizeType_PROPORTIONAL)
3442 gTextAlign = mso_alignTextStretch;
3444 else
3446 gTextAlign = mso_alignTextWordJust;
3448 break;
3450 default:
3451 break;
3453 AddOpt(DFF_Prop_gtextAlign,gTextAlign);
3456 if((nTextPathFlags & 0x4000) != 0) // Is Font work
3458 OutlinerParaObject* pOutlinerParaObject(rSdrObjCustomShape.GetOutlinerParaObject());
3459 if ( pOutlinerParaObject && pOutlinerParaObject->IsEffectivelyVertical() )
3460 nTextPathFlags |= 0x2000;
3463 // Use gtextFStretch for Watermark like MSO does
3464 nTextPathFlags |= use_gtextFBestFit | gtextFBestFit
3465 | use_gtextFStretch | gtextFStretch
3466 | use_gtextFShrinkFit | gtextFShrinkFit;
3468 if ( nTextPathFlags != nTextPathFlagsOrg )
3469 AddOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags );
3472 else if ( rProp.Name == sHandles )
3474 if ( !bIsDefaultObject )
3476 bPredefinedHandlesUsed = false;
3477 if ( rProp.Value >>= aHandlesPropSeq )
3479 sal_uInt16 nElements = static_cast<sal_uInt16>(aHandlesPropSeq.getLength());
3480 if ( nElements )
3482 sal_uInt16 k, nElementSize = 36;
3483 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3484 SvMemoryStream aMemStrm( nStreamSize );
3485 aMemStrm.WriteUInt16( nElements )
3486 .WriteUInt16( nElements )
3487 .WriteUInt16( nElementSize );
3489 for ( k = 0; k < nElements; k++ )
3491 sal_uInt32 nFlags = 0;
3492 sal_Int32 nXPosition = 0;
3493 sal_Int32 nYPosition = 0;
3494 sal_Int32 nXMap = 0;
3495 sal_Int32 nYMap = 0;
3496 sal_Int32 nXRangeMin = 0x80000000;
3497 sal_Int32 nXRangeMax = 0x7fffffff;
3498 sal_Int32 nYRangeMin = 0x80000000;
3499 sal_Int32 nYRangeMax = 0x7fffffff;
3501 const uno::Sequence< beans::PropertyValue >& rPropSeq = aHandlesPropSeq[ k ];
3502 for ( const beans::PropertyValue& rPropVal: rPropSeq )
3504 if ( rPropVal.Name == "Position" )
3506 drawing::EnhancedCustomShapeParameterPair aPosition;
3507 if ( rPropVal.Value >>= aPosition )
3509 GetValueForEnhancedCustomShapeHandleParameter( nXPosition, aPosition.First );
3510 GetValueForEnhancedCustomShapeHandleParameter( nYPosition, aPosition.Second );
3513 else if ( rPropVal.Name == "MirroredX" )
3515 bool bMirroredX;
3516 if ( rPropVal.Value >>= bMirroredX )
3518 if ( bMirroredX )
3519 nFlags |= 1;
3522 else if ( rPropVal.Name == "MirroredY" )
3524 bool bMirroredY;
3525 if ( rPropVal.Value >>= bMirroredY )
3527 if ( bMirroredY )
3528 nFlags |= 2;
3531 else if ( rPropVal.Name == "Switched" )
3533 bool bSwitched;
3534 if ( rPropVal.Value >>= bSwitched )
3536 if ( bSwitched )
3537 nFlags |= 4;
3540 else if ( rPropVal.Name == "Polar" )
3542 drawing::EnhancedCustomShapeParameterPair aPolar;
3543 if ( rPropVal.Value >>= aPolar )
3545 if ( GetValueForEnhancedCustomShapeHandleParameter( nXMap, aPolar.First ) )
3546 nFlags |= 0x800;
3547 if ( GetValueForEnhancedCustomShapeHandleParameter( nYMap, aPolar.Second ) )
3548 nFlags |= 0x1000;
3549 nFlags |= 8;
3552 else if ( rPropVal.Name == "RadiusRangeMinimum" )
3554 nYRangeMin = sal_Int32(0xff4c0000); // the range of angles seems to be a not
3555 nYRangeMax = sal_Int32(0x00b40000); // used feature, so we are defaulting this
3557 drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum;
3558 if ( rPropVal.Value >>= aRadiusRangeMinimum )
3560 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMin, aRadiusRangeMinimum ) )
3561 nFlags |= 0x80;
3562 nFlags |= 0x2000;
3565 else if ( rPropVal.Name == "RadiusRangeMaximum" )
3567 nYRangeMin = sal_Int32(0xff4c0000); // the range of angles seems to be a not
3568 nYRangeMax = sal_Int32(0x00b40000); // used feature, so we are defaulting this
3570 drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum;
3571 if ( rPropVal.Value >>= aRadiusRangeMaximum )
3573 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMax, aRadiusRangeMaximum ) )
3574 nFlags |= 0x100;
3575 nFlags |= 0x2000;
3578 else if ( rPropVal.Name == "RangeXMinimum" )
3580 drawing::EnhancedCustomShapeParameter aXRangeMinimum;
3581 if ( rPropVal.Value >>= aXRangeMinimum )
3583 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMin, aXRangeMinimum ) )
3584 nFlags |= 0x80;
3585 nFlags |= 0x20;
3588 else if ( rPropVal.Name == "RangeXMaximum" )
3590 drawing::EnhancedCustomShapeParameter aXRangeMaximum;
3591 if ( rPropVal.Value >>= aXRangeMaximum )
3593 if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMax, aXRangeMaximum ) )
3594 nFlags |= 0x100;
3595 nFlags |= 0x20;
3598 else if ( rPropVal.Name == "RangeYMinimum" )
3600 drawing::EnhancedCustomShapeParameter aYRangeMinimum;
3601 if ( rPropVal.Value >>= aYRangeMinimum )
3603 if ( GetValueForEnhancedCustomShapeHandleParameter( nYRangeMin, aYRangeMinimum ) )
3604 nFlags |= 0x200;
3605 nFlags |= 0x20;
3608 else if ( rPropVal.Name == "RangeYMaximum" )
3610 drawing::EnhancedCustomShapeParameter aYRangeMaximum;
3611 if ( rPropVal.Value >>= aYRangeMaximum )
3613 if ( GetValueForEnhancedCustomShapeHandleParameter( nYRangeMax, aYRangeMaximum ) )
3614 nFlags |= 0x400;
3615 nFlags |= 0x20;
3619 aMemStrm.WriteUInt32( nFlags )
3620 .WriteInt32( nXPosition )
3621 .WriteInt32( nYPosition )
3622 .WriteInt32( nXMap )
3623 .WriteInt32( nYMap )
3624 .WriteInt32( nXRangeMin )
3625 .WriteInt32( nXRangeMax )
3626 .WriteInt32( nYRangeMin )
3627 .WriteInt32( nYRangeMax );
3629 if ( nFlags & 8 )
3630 nAdjustmentsWhichNeedsToBeConverted |= ( 1 << ( nYPosition - 0x100 ) );
3633 AddOpt(DFF_Prop_Handles, true, 6, aMemStrm);
3635 else
3637 AddOpt(DFF_Prop_Handles, 0, true);
3642 else if ( rProp.Name == sAdjustmentValues )
3644 // it is required, that the information which handle is polar has already be read,
3645 // so we are able to change the polar value to a fixed float
3646 aAdjustmentValuesProp = rProp.Value;
3647 bAdjustmentValuesProp = true;
3650 if ( bAdjustmentValuesProp )
3652 uno::Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq;
3653 if ( aAdjustmentValuesProp >>= aAdjustmentSeq )
3655 if ( bPredefinedHandlesUsed )
3656 LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );
3658 sal_Int32 k, nValue = 0, nAdjustmentValues = aAdjustmentSeq.getLength();
3659 for ( k = 0; k < nAdjustmentValues; k++ )
3660 if( GetAdjustmentValue( aAdjustmentSeq[ k ], k, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
3661 AddOpt( static_cast<sal_uInt16>( DFF_Prop_adjustValue + k ), static_cast<sal_uInt32>(nValue) );
3664 if( !bPathCoordinatesProp )
3665 return;
3667 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aCoordinates;
3668 if ( !(aPathCoordinatesProp >>= aCoordinates) )
3669 return;
3671 // creating the vertices
3672 if (aCoordinates.hasElements())
3674 sal_uInt16 j, nElements = static_cast<sal_uInt16>(aCoordinates.getLength());
3675 sal_uInt16 nElementSize = 8;
3676 sal_uInt32 nStreamSize = nElementSize * nElements + 6;
3677 SvMemoryStream aMemStrm( nStreamSize );
3678 aMemStrm.WriteUInt16( nElements )
3679 .WriteUInt16( nElements )
3680 .WriteUInt16( nElementSize );
3681 for( j = 0; j < nElements; j++ )
3683 sal_Int32 X = GetValueForEnhancedCustomShapeParameter( aCoordinates[ j ].First, aEquationOrder, true );
3684 sal_Int32 Y = GetValueForEnhancedCustomShapeParameter( aCoordinates[ j ].Second, aEquationOrder, true );
3685 aMemStrm.WriteInt32( X )
3686 .WriteInt32( Y );
3689 AddOpt(DFF_Prop_pVertices, true, 6, aMemStrm); // -6
3691 else
3693 AddOpt(DFF_Prop_pVertices, 0, true);
3697 MSO_SPT EscherPropertyContainer::GetCustomShapeType( const uno::Reference< drawing::XShape > & rXShape, ShapeFlag& nMirrorFlags, OUString& rShapeType, bool bOOXML )
3699 MSO_SPT eShapeType = mso_sptNil;
3700 nMirrorFlags = ShapeFlag::NONE;
3701 uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY );
3702 if ( aXPropSet.is() )
3706 uno::Any aGeoPropSet = aXPropSet->getPropertyValue( "CustomShapeGeometry" );
3707 uno::Sequence< beans::PropertyValue > aGeoPropSeq;
3708 if ( aGeoPropSet >>= aGeoPropSeq )
3710 sal_Int32 i, nCount = aGeoPropSeq.getLength();
3711 for ( i = 0; i < nCount; i++ )
3713 const beans::PropertyValue& rProp = aGeoPropSeq[ i ];
3714 if ( rProp.Name == "Type" )
3716 if ( rProp.Value >>= rShapeType )
3718 if (bOOXML)
3720 // In case of VML export, try to handle the
3721 // ooxml- prefix in rShapeType. If that fails,
3722 // just do the same as the binary export.
3723 eShapeType = msfilter::util::GETVMLShapeType(rShapeType);
3724 if (eShapeType == mso_sptNil)
3725 eShapeType = EnhancedCustomShapeTypeNames::Get(rShapeType);
3727 else
3728 eShapeType = EnhancedCustomShapeTypeNames::Get( rShapeType );
3731 else if ( rProp.Name == "MirroredX" )
3733 bool bMirroredX;
3734 if ( ( rProp.Value >>= bMirroredX ) && bMirroredX )
3735 nMirrorFlags |= ShapeFlag::FlipH;
3737 else if ( rProp.Name == "MirroredY" )
3739 bool bMirroredY;
3740 if ( ( rProp.Value >>= bMirroredY ) && bMirroredY )
3741 nMirrorFlags |= ShapeFlag::FlipV;
3746 catch( const uno::Exception& )
3750 return eShapeType;
3754 // Implement for form control export
3755 bool EscherPropertyContainer::CreateBlipPropertiesforOLEControl(const uno::Reference<beans::XPropertySet> & rXPropSet,
3756 const uno::Reference<drawing::XShape> & rXShape)
3758 SdrObject* pShape = SdrObject::getSdrObjectFromXShape(rXShape);
3759 if ( !pShape )
3760 return false;
3762 Graphic aGraphic(SdrExchangeView::GetObjGraphic(*pShape));
3763 const GraphicObject aGraphicObject(std::move(aGraphic));
3765 if (!aGraphicObject.GetUniqueID().isEmpty())
3767 if ( pGraphicProvider && pPicOutStrm && pShapeBoundRect )
3769 sal_uInt32 nBlibId = pGraphicProvider->GetBlibID(*pPicOutStrm, aGraphicObject);
3770 if ( nBlibId )
3772 AddOpt( ESCHER_Prop_pib, nBlibId, true );
3773 ImplCreateGraphicAttributes( rXPropSet, nBlibId, false );
3774 return true;
3779 return false;
3782 EscherPersistTable::EscherPersistTable()
3786 EscherPersistTable::~EscherPersistTable()
3790 bool EscherPersistTable::PtIsID( sal_uInt32 nID )
3792 for(auto const & pPtr : maPersistTable) {
3793 if ( pPtr->mnID == nID ) {
3794 return true;
3797 return false;
3800 void EscherPersistTable::PtInsert( sal_uInt32 nID, sal_uInt32 nOfs )
3802 maPersistTable.push_back( std::make_unique<EscherPersistEntry>( nID, nOfs ) );
3805 void EscherPersistTable::PtDelete( sal_uInt32 nID )
3807 auto it = std::find_if(maPersistTable.begin(), maPersistTable.end(),
3808 [&nID](const std::unique_ptr<EscherPersistEntry>& rxEntry) { return rxEntry->mnID == nID; });
3809 if (it != maPersistTable.end())
3810 maPersistTable.erase( it );
3813 sal_uInt32 EscherPersistTable::PtGetOffsetByID( sal_uInt32 nID )
3815 for(auto const & pPtr : maPersistTable) {
3816 if ( pPtr->mnID == nID ) {
3817 return pPtr->mnOffset;
3820 return 0;
3823 void EscherPersistTable::PtReplace( sal_uInt32 nID, sal_uInt32 nOfs )
3825 for(auto const & pPtr : maPersistTable) {
3826 if ( pPtr->mnID == nID ) {
3827 pPtr->mnOffset = nOfs;
3828 return;
3833 void EscherPersistTable::PtReplaceOrInsert( sal_uInt32 nID, sal_uInt32 nOfs )
3835 for(auto const & pPtr : maPersistTable) {
3836 if ( pPtr->mnID == nID ) {
3837 pPtr->mnOffset = nOfs;
3838 return;
3841 PtInsert( nID, nOfs );
3844 bool EscherPropertyValueHelper::GetPropertyValue(
3845 uno::Any& rAny,
3846 const uno::Reference<beans::XPropertySet> & rXPropSet,
3847 const OUString& rString,
3848 bool bTestPropertyAvailability)
3850 bool bRetValue = true;
3851 if ( bTestPropertyAvailability )
3853 bRetValue = false;
3856 uno::Reference<beans::XPropertySetInfo>
3857 aXPropSetInfo( rXPropSet->getPropertySetInfo() );
3858 if ( aXPropSetInfo.is() )
3859 bRetValue = aXPropSetInfo->hasPropertyByName( rString );
3861 catch( const uno::Exception& )
3863 bRetValue = false;
3866 if ( bRetValue )
3870 rAny = rXPropSet->getPropertyValue( rString );
3871 if ( !rAny.hasValue() )
3872 bRetValue = false;
3874 catch( const uno::Exception& )
3876 bRetValue = false;
3879 return bRetValue;
3882 beans::PropertyState EscherPropertyValueHelper::GetPropertyState(
3883 const uno::Reference<beans::XPropertySet> & rXPropSet,
3884 const OUString& rPropertyName )
3886 beans::PropertyState eRetValue = beans::PropertyState_AMBIGUOUS_VALUE;
3889 uno::Reference<beans::XPropertyState> aXPropState
3890 ( rXPropSet, uno::UNO_QUERY );
3891 if ( aXPropState.is() )
3892 eRetValue = aXPropState->getPropertyState( rPropertyName );
3894 catch( const uno::Exception& )
3897 return eRetValue;
3900 EscherBlibEntry::EscherBlibEntry( sal_uInt32 nPictureOffset, const GraphicObject& rObject, const OString& rId,
3901 const GraphicAttr* pGraphicAttr ) :
3902 maPrefMapMode ( rObject.GetPrefMapMode() ),
3903 maPrefSize ( rObject.GetPrefSize() ),
3904 mnPictureOffset ( nPictureOffset ),
3905 mnRefCount ( 1 ),
3906 mnSizeExtra ( 0 ),
3907 mbIsEmpty ( true )
3909 mbIsNativeGraphicPossible = ( pGraphicAttr == nullptr );
3910 meBlibType = UNKNOWN;
3911 mnSize = 0;
3913 sal_uInt32 nLen = static_cast<sal_uInt32>(rId.getLength());
3914 const char* pData = rId.getStr();
3915 GraphicType eType( rObject.GetType() );
3916 if (!(nLen && (eType != GraphicType::NONE)))
3917 return;
3919 mnIdentifier[ 0 ] = rtl_crc32( 0,pData, nLen );
3920 mnIdentifier[ 1 ] = 0;
3922 if ( pGraphicAttr )
3924 if ( pGraphicAttr->IsSpecialDrawMode()
3925 || pGraphicAttr->IsMirrored()
3926 || pGraphicAttr->IsCropped()
3927 || pGraphicAttr->IsRotated()
3928 || pGraphicAttr->IsTransparent()
3929 || pGraphicAttr->IsAdjusted() )
3931 SvMemoryStream aSt( sizeof( GraphicAttr ) );
3932 aSt.WriteUInt16( static_cast<sal_uInt16>(pGraphicAttr->GetDrawMode()) )
3933 .WriteUInt32( static_cast<sal_uInt32>(pGraphicAttr->GetMirrorFlags()) )
3934 .WriteInt32( pGraphicAttr->GetLeftCrop() )
3935 .WriteInt32( pGraphicAttr->GetTopCrop() )
3936 .WriteInt32( pGraphicAttr->GetRightCrop() )
3937 .WriteInt32( pGraphicAttr->GetBottomCrop() )
3938 .WriteUInt16( pGraphicAttr->GetRotation().get() )
3939 .WriteInt16( pGraphicAttr->GetLuminance() )
3940 .WriteInt16( pGraphicAttr->GetContrast() )
3941 .WriteInt16( pGraphicAttr->GetChannelR() )
3942 .WriteInt16( pGraphicAttr->GetChannelG() )
3943 .WriteInt16( pGraphicAttr->GetChannelB() )
3944 .WriteDouble( pGraphicAttr->GetGamma() );
3945 aSt.WriteBool( pGraphicAttr->IsInvert() )
3946 .WriteUChar( 255 - pGraphicAttr->GetAlpha() ); // transparency
3947 mnIdentifier[ 1 ] = rtl_crc32( 0, aSt.GetData(), aSt.Tell() );
3949 else
3950 mbIsNativeGraphicPossible = true;
3952 sal_uInt32 i, nTmp, n1, n2;
3953 n1 = n2 = 0;
3954 for ( i = 0; i < nLen; i++ )
3956 nTmp = n2 >> 28; // rotating 4 bit
3957 n2 <<= 4;
3958 n2 |= n1 >> 28;
3959 n1 <<= 4;
3960 n1 |= nTmp;
3961 n1 ^= *pData++ - '0';
3963 mnIdentifier[ 2 ] = n1;
3964 mnIdentifier[ 3 ] = n2;
3965 mbIsEmpty = false;
3968 void EscherBlibEntry::WriteBlibEntry( SvStream& rSt, bool bWritePictureOffset, sal_uInt32 nResize )
3970 sal_uInt32 nPictureOffset = bWritePictureOffset ? mnPictureOffset : 0;
3972 rSt.WriteUInt32( ( ESCHER_BSE << 16 ) | ( ( static_cast<sal_uInt16>(meBlibType) << 4 ) | 2 ) )
3973 .WriteUInt32( 36 + nResize )
3974 .WriteUChar( meBlibType );
3976 switch ( meBlibType )
3978 case EMF :
3979 case WMF : // converting EMF/WMF on OS2 to Pict
3980 rSt.WriteUChar( PICT );
3981 break;
3982 default:
3983 rSt.WriteUChar( meBlibType );
3986 rSt.WriteBytes(&mnIdentifier[0], 16);
3987 rSt.WriteUInt16( 0 )
3988 .WriteUInt32( mnSize + mnSizeExtra )
3989 .WriteUInt32( mnRefCount )
3990 .WriteUInt32( nPictureOffset )
3991 .WriteUInt32( 0 );
3994 EscherBlibEntry::~EscherBlibEntry()
3998 bool EscherBlibEntry::operator==( const EscherBlibEntry& rEscherBlibEntry ) const
4000 for ( int i = 0; i < 3; i++ )
4002 if ( mnIdentifier[ i ] != rEscherBlibEntry.mnIdentifier[ i ] )
4003 return false;
4005 return true;
4008 EscherGraphicProvider::EscherGraphicProvider( EscherGraphicProviderFlags nFlags ) :
4009 mnFlags ( nFlags )
4013 EscherGraphicProvider::~EscherGraphicProvider()
4017 void EscherGraphicProvider::SetNewBlipStreamOffset( sal_Int32 nOffset )
4019 for( size_t i = 0; i < mvBlibEntrys.size(); i++ )
4021 mvBlibEntrys[ i ]->mnPictureOffset += nOffset;
4025 sal_uInt32 EscherGraphicProvider::ImplInsertBlib( EscherBlibEntry* p_EscherBlibEntry )
4027 mvBlibEntrys.push_back( std::unique_ptr<EscherBlibEntry>(p_EscherBlibEntry) );
4028 return mvBlibEntrys.size();
4031 sal_uInt32 EscherGraphicProvider::GetBlibStoreContainerSize( SvStream const * pMergePicStreamBSE ) const
4033 sal_uInt32 nSize = 44 * mvBlibEntrys.size() + 8;
4034 if ( pMergePicStreamBSE )
4036 for ( size_t i = 0; i < mvBlibEntrys.size(); i++ )
4037 nSize += mvBlibEntrys[ i ]->mnSize + mvBlibEntrys[ i ]->mnSizeExtra;
4039 return nSize;
4042 void EscherGraphicProvider::WriteBlibStoreEntry(SvStream& rSt,
4043 sal_uInt32 nBlipId, sal_uInt32 nResize)
4045 if (nBlipId > mvBlibEntrys.size() || nBlipId == 0)
4046 return;
4047 mvBlibEntrys[nBlipId-1]->WriteBlibEntry(rSt, true/*bWritePictureOffSet*/, nResize);
4050 void EscherGraphicProvider::WriteBlibStoreContainer( SvStream& rSt, SvStream* pMergePicStreamBSE )
4052 sal_uInt32 nSize = GetBlibStoreContainerSize( pMergePicStreamBSE );
4053 if ( !nSize )
4054 return;
4056 rSt.WriteUInt32( ( ESCHER_BstoreContainer << 16 ) | 0x1f )
4057 .WriteUInt32( nSize - 8 );
4059 if ( pMergePicStreamBSE )
4061 sal_uInt32 nBlipSize, nOldPos = pMergePicStreamBSE->Tell();
4062 const sal_uInt32 nBuf = 0x40000; // 256KB buffer
4063 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ nBuf ]);
4065 for ( size_t i = 0; i < mvBlibEntrys.size(); i++ )
4067 EscherBlibEntry* pBlibEntry = mvBlibEntrys[ i ].get();
4069 ESCHER_BlibType nBlibType = pBlibEntry->meBlibType;
4070 nBlipSize = pBlibEntry->mnSize + pBlibEntry->mnSizeExtra;
4071 pBlibEntry->WriteBlibEntry( rSt, false, nBlipSize );
4073 // BLIP
4074 pMergePicStreamBSE->Seek( pBlibEntry->mnPictureOffset );
4075 sal_uInt16 n16;
4076 // record version and instance
4077 pMergePicStreamBSE->ReadUInt16( n16 );
4078 rSt.WriteUInt16( n16 );
4079 // record type
4080 pMergePicStreamBSE->ReadUInt16( n16 );
4081 rSt.WriteUInt16( ESCHER_BlipFirst + nBlibType );
4082 DBG_ASSERT( n16 == ESCHER_BlipFirst + nBlibType , "EscherGraphicProvider::WriteBlibStoreContainer: BLIP record types differ" );
4083 sal_uInt32 n32;
4084 // record size
4085 pMergePicStreamBSE->ReadUInt32( n32 );
4086 nBlipSize -= 8;
4087 rSt.WriteUInt32( nBlipSize );
4088 DBG_ASSERT( nBlipSize == n32, "EscherGraphicProvider::WriteBlibStoreContainer: BLIP sizes differ" );
4089 // record
4090 while ( nBlipSize )
4092 sal_uInt32 nBytes = std::min( nBlipSize, nBuf );
4093 pMergePicStreamBSE->ReadBytes(pBuf.get(), nBytes);
4094 rSt.WriteBytes(pBuf.get(), nBytes);
4095 nBlipSize -= nBytes;
4098 pMergePicStreamBSE->Seek( nOldPos );
4100 else
4102 for ( size_t i = 0; i < mvBlibEntrys.size(); i++ )
4103 mvBlibEntrys[ i ]->WriteBlibEntry( rSt, true );
4107 bool EscherGraphicProvider::GetPrefSize( const sal_uInt32 nBlibId, Size& rPrefSize, MapMode& rPrefMapMode )
4109 bool bInRange = nBlibId && ( ( nBlibId - 1 ) < mvBlibEntrys.size() );
4110 if ( bInRange )
4112 EscherBlibEntry* pEntry = mvBlibEntrys[ nBlibId - 1 ].get();
4113 rPrefSize = pEntry->maPrefSize;
4114 rPrefMapMode = pEntry->maPrefMapMode;
4116 return bInRange;
4119 sal_uInt32 EscherGraphicProvider::GetBlibID( SvStream& rPicOutStrm, GraphicObject const & rGraphicObject,
4120 const awt::Rectangle* pVisArea,
4121 const GraphicAttr* pGraphicAttr, const bool bOOxmlExport )
4123 sal_uInt32 nBlibId = 0;
4125 std::unique_ptr<EscherBlibEntry> p_EscherBlibEntry( new EscherBlibEntry( rPicOutStrm.Tell(), rGraphicObject, rGraphicObject.GetUniqueID(), pGraphicAttr ) );
4126 if ( !p_EscherBlibEntry->IsEmpty() )
4128 for ( size_t i = 0; i < mvBlibEntrys.size(); i++ )
4130 if ( *( mvBlibEntrys[ i ] ) == *p_EscherBlibEntry )
4132 mvBlibEntrys[ i ]->mnRefCount++;
4133 return i + 1;
4137 bool bUseNativeGraphic( false );
4139 Graphic aGraphic(rGraphicObject.GetTransformedGraphic(pGraphicAttr));
4140 GfxLink aGraphicLink;
4141 SvMemoryStream aStream;
4143 const sal_uInt8* pGraphicAry = nullptr;
4145 if ( p_EscherBlibEntry->mbIsNativeGraphicPossible && aGraphic.IsGfxLink() )
4147 aGraphicLink = aGraphic.GetGfxLink();
4149 p_EscherBlibEntry->mnSize = aGraphicLink.GetDataSize();
4150 pGraphicAry = aGraphicLink.GetData();
4152 if ( p_EscherBlibEntry->mnSize && pGraphicAry )
4154 switch ( aGraphicLink.GetType() )
4156 case GfxLinkType::NativeJpg : p_EscherBlibEntry->meBlibType = PEG; break;
4157 case GfxLinkType::NativePng : p_EscherBlibEntry->meBlibType = PNG; break;
4159 // #i15508# added BMP type for better exports; need to check this
4160 // checked - does not work that way, so keep out for now. It may
4161 // work somehow with direct DIB data, but that would need to be checked
4162 // carefully
4163 // for more comments please check RtfAttributeOutput::FlyFrameGraphic
4165 // case GfxLinkType::NativeBmp : p_EscherBlibEntry->meBlibType = DIB; break;
4167 case GfxLinkType::NativeWmf :
4169 if ( aGraphicLink.IsEMF() )
4171 p_EscherBlibEntry->meBlibType = EMF;
4173 else if ( pGraphicAry && ( p_EscherBlibEntry->mnSize > 0x2c ) )
4175 p_EscherBlibEntry->meBlibType = WMF;
4176 if ( ( pGraphicAry[ 0 ] == 0xd7 ) && ( pGraphicAry[ 1 ] == 0xcd )
4177 && ( pGraphicAry[ 2 ] == 0xc6 ) && ( pGraphicAry[ 3 ] == 0x9a ) )
4178 { // we have to get rid of the metafileheader
4179 pGraphicAry += 22;
4180 p_EscherBlibEntry->mnSize -= 22;
4184 break;
4185 default: break;
4187 if ( p_EscherBlibEntry->meBlibType != UNKNOWN )
4188 bUseNativeGraphic = true;
4191 if ( !bUseNativeGraphic )
4193 GraphicType eGraphicType = aGraphic.GetType();
4194 if ( ( eGraphicType == GraphicType::Bitmap ) || ( eGraphicType == GraphicType::GdiMetafile ) )
4196 ErrCode nErrCode;
4197 if ( !aGraphic.IsAnimated() )
4198 nErrCode = GraphicConverter::Export( aStream, aGraphic, ( eGraphicType == GraphicType::Bitmap ) ? ConvertDataFormat::PNG : ConvertDataFormat::EMF );
4199 else
4200 { // to store an animation, a gif has to be included into the msOG chunk of a png #I5583#
4201 GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter();
4202 SvMemoryStream aGIFStream;
4203 const char* const pString = "MSOFFICE9.0";
4204 aGIFStream.WriteBytes(pString, strlen(pString));
4205 nErrCode = rFilter.ExportGraphic( aGraphic, u"", aGIFStream,
4206 rFilter.GetExportFormatNumberForShortName( u"GIF" ) );
4207 SAL_WARN_IF(
4208 nErrCode != ERRCODE_NONE, "filter.ms",
4209 "ExportGraphic to GIF failed with " << nErrCode);
4210 if (nErrCode == ERRCODE_NONE)
4212 sal_uInt32 nGIFSreamLen = aGIFStream.Tell();
4213 uno::Sequence<sal_Int8> aGIFSeq( nGIFSreamLen );
4214 sal_Int8* pSeq = aGIFSeq.getArray();
4215 aGIFStream.Seek( STREAM_SEEK_TO_BEGIN );
4216 aGIFStream.ReadBytes(pSeq, nGIFSreamLen);
4217 beans::PropertyValue aChunkProp, aFilterProp;
4218 aChunkProp.Name = "msOG";
4219 aChunkProp.Value <<= aGIFSeq;
4220 uno::Sequence<beans::PropertyValue> aAdditionalChunkSequence{ aChunkProp };
4221 aFilterProp.Name = "AdditionalChunks";
4222 aFilterProp.Value <<= aAdditionalChunkSequence;
4223 uno::Sequence<beans::PropertyValue> aFilterData{ aFilterProp };
4224 nErrCode = rFilter.ExportGraphic( aGraphic, u"", aStream,
4225 rFilter.GetExportFormatNumberForShortName( u"PNG" ), &aFilterData );
4228 if ( nErrCode == ERRCODE_NONE )
4230 p_EscherBlibEntry->meBlibType = ( eGraphicType == GraphicType::Bitmap ) ? PNG : EMF;
4231 p_EscherBlibEntry->mnSize = aStream.TellEnd();
4232 pGraphicAry = static_cast<sal_uInt8 const *>(aStream.GetData());
4237 ESCHER_BlibType eBlibType = p_EscherBlibEntry->meBlibType;
4238 if ( p_EscherBlibEntry->mnSize && pGraphicAry && ( eBlibType != UNKNOWN ) )
4240 sal_uInt32 nExtra, nAtomSize = 0;
4241 sal_uInt32 nInstance, nUncompressedSize = p_EscherBlibEntry->mnSize;
4243 if ( mnFlags & EscherGraphicProviderFlags::UseInstances )
4245 rPicOutStrm.WriteUInt32( 0x7f90000 | static_cast<sal_uInt16>( mvBlibEntrys.size() << 4 ) )
4246 .WriteUInt32( 0 );
4247 nAtomSize = rPicOutStrm.Tell();
4248 if ( eBlibType == PNG )
4249 rPicOutStrm.WriteUInt16( 0x0606 );
4250 else if ( eBlibType == WMF )
4251 rPicOutStrm.WriteUInt16( 0x0403 );
4252 else if ( eBlibType == EMF )
4253 rPicOutStrm.WriteUInt16( 0x0402 );
4254 else if ( eBlibType == PEG )
4255 rPicOutStrm.WriteUInt16( 0x0505 );
4258 // fdo#69607 do not compress WMF files if we are in OOXML export
4259 if ( ( eBlibType == PEG ) || ( eBlibType == PNG ) // || ( eBlibType == DIB )) // #i15508#
4260 || ( ( ( eBlibType == WMF ) || ( eBlibType == EMF ) ) && bOOxmlExport ) )
4262 nExtra = 17;
4263 p_EscherBlibEntry->mnSizeExtra = nExtra + 8;
4265 // #i15508# type see SvxMSDffManager::GetBLIPDirect (checked, does not work this way)
4266 // see RtfAttributeOutput::FlyFrameGraphic for more comments
4267 // maybe it would work with direct DIB data, but that would need thorough testing
4268 if( eBlibType == PNG )
4270 nInstance = 0xf01e6e00;
4272 else // if( eBlibType == PEG )
4274 nInstance = 0xf01d46a0;
4276 //else // eBlibType == DIB
4278 // nInstance = 0xf01d7A80;
4281 // #i15508#
4282 //nInstance = ( eBlibType == PNG ) ? 0xf01e6e00 : 0xf01d46a0;
4285 rPicOutStrm.WriteUInt32( nInstance ).WriteUInt32( p_EscherBlibEntry->mnSize + nExtra );
4286 rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16);
4287 rPicOutStrm.WriteUChar( 0xff );
4288 rPicOutStrm.WriteBytes(pGraphicAry, p_EscherBlibEntry->mnSize);
4290 else
4292 ZCodec aZCodec( 0x8000, 0x8000 );
4293 aZCodec.BeginCompression();
4294 SvMemoryStream aDestStrm;
4295 aZCodec.Write( aDestStrm, pGraphicAry, p_EscherBlibEntry->mnSize );
4296 aZCodec.EndCompression();
4297 p_EscherBlibEntry->mnSize = aDestStrm.TellEnd();
4298 pGraphicAry = static_cast<sal_uInt8 const *>(aDestStrm.GetData());
4299 if ( p_EscherBlibEntry->mnSize && pGraphicAry )
4301 nExtra = eBlibType == WMF ? 0x42 : 0x32; // !EMF -> no change
4302 p_EscherBlibEntry->mnSizeExtra = nExtra + 8;
4303 nInstance = ( eBlibType == WMF ) ? 0xf01b2170 : 0xf01a3d40; // !EMF -> no change
4304 rPicOutStrm.WriteUInt32( nInstance ).WriteUInt32( p_EscherBlibEntry->mnSize + nExtra );
4305 if ( eBlibType == WMF ) // !EMF -> no change
4306 rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16);
4307 rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16);
4310 ##913##
4311 For Word the stored size of the graphic is critical the
4312 metafile boundaries must match the actual graphics
4313 boundaries, and the width and height must be in EMU's
4315 If you don't do it this way then objects edited in the
4316 msoffice app may show strange behaviour as the size jumps
4317 around, and the original size and scaling factor in word
4318 will be a very strange figure
4320 sal_uInt32 nPrefWidth = p_EscherBlibEntry->maPrefSize.Width();
4321 sal_uInt32 nPrefHeight = p_EscherBlibEntry->maPrefSize.Height();
4322 sal_uInt32 nWidth, nHeight;
4323 if ( pVisArea )
4325 nWidth = pVisArea->Width * 360;
4326 nHeight = pVisArea->Height * 360;
4328 else
4330 Size aPrefSize(lcl_SizeToEmu(p_EscherBlibEntry->maPrefSize, p_EscherBlibEntry->maPrefMapMode));
4331 nWidth = aPrefSize.Width() * 360;
4332 nHeight = aPrefSize.Height() * 360;
4334 rPicOutStrm.WriteUInt32( nUncompressedSize ) // WMFSize without FileHeader
4335 .WriteInt32( 0 ) // since we can't find out anymore what the original size of
4336 .WriteInt32( 0 ) // the WMF (without Fileheader) was we write 10cm / x
4337 .WriteUInt32( nPrefWidth )
4338 .WriteUInt32( nPrefHeight )
4339 .WriteUInt32( nWidth )
4340 .WriteUInt32( nHeight )
4341 .WriteUInt32( p_EscherBlibEntry->mnSize )
4342 .WriteUInt16( 0xfe00 ); // compression Flags
4343 rPicOutStrm.WriteBytes(pGraphicAry, p_EscherBlibEntry->mnSize);
4346 if ( nAtomSize )
4348 sal_uInt32 nPos = rPicOutStrm.Tell();
4349 rPicOutStrm.Seek( nAtomSize - 4 );
4350 rPicOutStrm.WriteUInt32( nPos - nAtomSize );
4351 rPicOutStrm.Seek( nPos );
4353 nBlibId = ImplInsertBlib( p_EscherBlibEntry.release() );
4356 return nBlibId;
4359 namespace {
4361 struct EscherConnectorRule
4363 sal_uInt32 nRuleId;
4364 sal_uInt32 nShapeA; // SPID of shape A
4365 sal_uInt32 nShapeB; // SPID of shape B
4366 sal_uInt32 nShapeC; // SPID of connector shape
4367 sal_uInt32 ncptiA; // Connection site Index of shape A
4368 sal_uInt32 ncptiB; // Connection site Index of shape B
4373 struct EscherShapeListEntry
4375 uno::Reference<drawing::XShape>aXShape;
4376 sal_uInt32 n_EscherId;
4378 EscherShapeListEntry(uno::Reference<drawing::XShape> xShape, sal_uInt32 nId)
4379 : aXShape(std::move(xShape))
4380 , n_EscherId(nId)
4384 sal_uInt32 EscherConnectorListEntry::GetClosestPoint( const tools::Polygon& rPoly, const awt::Point& rPoint )
4386 sal_uInt16 nCount = rPoly.GetSize();
4387 sal_uInt16 nClosest = nCount;
4388 double fDist = sal_uInt32(0xffffffff);
4389 while( nCount-- )
4391 double fDistance = hypot( rPoint.X - rPoly[ nCount ].X(), rPoint.Y - rPoly[ nCount ].Y() );
4392 if ( fDistance < fDist )
4394 nClosest = nCount;
4395 fDist = fDistance;
4398 return nClosest;
4402 // for rectangles for ellipses for polygons
4404 // nRule = 0 ->Top 0 ->Top nRule = index to a (Poly)Polygon point
4405 // 1 ->Left 2 ->Left
4406 // 2 ->Bottom 4 ->Bottom
4407 // 3 ->Right 6 ->Right
4409 sal_uInt32 EscherConnectorListEntry::GetConnectorRule( bool bFirst )
4411 sal_uInt32 nRule = 0;
4413 uno::Any aAny;
4414 awt::Point aRefPoint( bFirst ? maPointA : maPointB );
4415 uno::Reference<drawing::XShape>
4416 aXShape( bFirst ? mXConnectToA : mXConnectToB );
4418 OUString aString(aXShape->getShapeType());
4419 OStringBuffer aBuf(OUStringToOString(aString, RTL_TEXTENCODING_UTF8));
4420 aBuf.remove( 0, 13 ); // removing "com.sun.star."
4421 sal_Int16 nPos = aBuf.toString().indexOf("Shape");
4422 aBuf.remove(nPos, 5);
4423 OString aType = aBuf.makeStringAndClear();
4425 uno::Reference<beans::XPropertySet>
4426 aPropertySet( aXShape, uno::UNO_QUERY );
4428 if ((aType == "drawing.PolyPolygon") || (aType == "drawing.PolyLine"))
4430 if ( aPropertySet.is() )
4432 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet, "PolyPolygon" ) )
4434 auto pSourcePolyPolygon =
4435 o3tl::doAccess<drawing::PointSequenceSequence>(aAny);
4436 sal_Int32 nOuterSequenceCount = pSourcePolyPolygon->getLength();
4437 drawing::PointSequence const * pOuterSequence = pSourcePolyPolygon->getConstArray();
4439 if ( pOuterSequence )
4441 sal_Int32 a, b, nIndex = 0;
4442 sal_uInt32 nDistance = 0xffffffff;
4443 for( a = 0; a < nOuterSequenceCount; a++ )
4445 drawing::PointSequence const * pInnerSequence = pOuterSequence++;
4446 if ( pInnerSequence )
4448 awt::Point const * pArray = pInnerSequence->getConstArray();
4449 if ( pArray )
4451 for ( b = 0; b < pInnerSequence->getLength(); b++, nIndex++, pArray++ )
4453 sal_uInt32 nDist = static_cast<sal_uInt32>(hypot( aRefPoint.X - pArray->X, aRefPoint.Y - pArray->Y ));
4454 if ( nDist < nDistance )
4456 nRule = nIndex;
4457 nDistance = nDist;
4467 else if ((aType == "drawing.OpenBezier") || (aType == "drawing.OpenFreeHand") || (aType == "drawing.PolyLinePath")
4468 || (aType == "drawing.ClosedBezier") || ( aType == "drawing.ClosedFreeHand") || (aType == "drawing.PolyPolygonPath") )
4470 uno::Reference<beans::XPropertySet>
4471 aPropertySet2( aXShape, uno::UNO_QUERY );
4472 if ( aPropertySet2.is() )
4474 if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet2, "PolyPolygonBezier" ) )
4476 auto pSourcePolyPolygon =
4477 o3tl::doAccess<drawing::PolyPolygonBezierCoords>(aAny);
4478 sal_Int32 nOuterSequenceCount = pSourcePolyPolygon->Coordinates.getLength();
4480 // get pointer of inner sequences
4481 drawing::PointSequence const * pOuterSequence =
4482 pSourcePolyPolygon->Coordinates.getConstArray();
4483 drawing::FlagSequence const * pOuterFlags =
4484 pSourcePolyPolygon->Flags.getConstArray();
4486 if ( pOuterSequence && pOuterFlags )
4488 sal_Int32 a, b, nIndex = 0;
4489 sal_uInt32 nDistance = 0xffffffff;
4491 for ( a = 0; a < nOuterSequenceCount; a++ )
4493 drawing::PointSequence const * pInnerSequence = pOuterSequence++;
4494 drawing::FlagSequence const * pInnerFlags = pOuterFlags++;
4495 if ( pInnerSequence && pInnerFlags )
4497 awt::Point const * pArray = pInnerSequence->getConstArray();
4498 drawing::PolygonFlags const * pFlags = pInnerFlags->getConstArray();
4499 if ( pArray && pFlags )
4501 for ( b = 0; b < pInnerSequence->getLength(); b++, pArray++ )
4503 drawing::PolygonFlags ePolyFlags = *pFlags++;
4504 if ( ePolyFlags == drawing::PolygonFlags_CONTROL )
4505 continue;
4506 sal_uInt32 nDist = static_cast<sal_uInt32>(hypot( aRefPoint.X - pArray->X, aRefPoint.Y - pArray->Y ));
4507 if ( nDist < nDistance )
4509 nRule = nIndex;
4510 nDistance = nDist;
4512 nIndex++;
4521 else
4523 bool bRectangularConnection = true;
4525 if (aType == "drawing.Custom")
4527 if (auto pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(aXShape)))
4529 const SdrCustomShapeGeometryItem& rGeometryItem =
4530 pSdrObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
4532 OUString sShapeType;
4533 const uno::Any* pType = rGeometryItem.GetPropertyValueByName( "Type" );
4534 if ( pType )
4535 *pType >>= sShapeType;
4536 MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
4538 uno::Any* pGluePointType = const_cast<SdrCustomShapeGeometryItem&>(rGeometryItem).GetPropertyValueByName( "Path", "GluePointType" );
4540 sal_Int16 nGluePointType = sal_Int16();
4541 if ( !( pGluePointType &&
4542 ( *pGluePointType >>= nGluePointType ) ) )
4543 nGluePointType = GetCustomShapeConnectionTypeDefault( eSpType );
4545 if ( nGluePointType == drawing::EnhancedCustomShapeGluePointType::CUSTOM )
4547 const SdrGluePointList* pList = pSdrObjCustomShape->GetGluePointList();
4548 if ( pList )
4550 tools::Polygon aPoly;
4551 sal_uInt16 nNum, nCnt = pList->GetCount();
4552 if ( nCnt )
4554 for ( nNum = 0; nNum < nCnt; nNum++ )
4556 const SdrGluePoint& rGP = (*pList)[ nNum ];
4557 Point aPt(rGP.GetAbsolutePos(*pSdrObjCustomShape));
4558 aPoly.Insert( POLY_APPEND, aPt );
4560 nRule = GetClosestPoint( aPoly, aRefPoint );
4561 bRectangularConnection = false;
4565 else if ( nGluePointType == drawing::EnhancedCustomShapeGluePointType::SEGMENTS )
4567 tools::PolyPolygon aPolyPoly;
4568 rtl::Reference<SdrObject> pTemporaryConvertResultObject(pSdrObjCustomShape->DoConvertToPolyObj(true, true));
4569 SdrPathObj* pSdrPathObj(dynamic_cast< SdrPathObj* >(pTemporaryConvertResultObject.get()));
4571 if(pSdrPathObj)
4573 // #i74631# use explicit constructor here. Also XPolyPolygon is not necessary,
4574 // reducing to PolyPolygon
4575 aPolyPoly = tools::PolyPolygon(pSdrPathObj->GetPathPoly());
4578 // do *not* forget to delete the temporary used SdrObject - possible memory leak (!)
4579 pTemporaryConvertResultObject.clear();
4580 pSdrPathObj = nullptr;
4582 if(0 != aPolyPoly.Count())
4584 sal_Int16 nIndex = 0;
4585 sal_uInt16 a, b;
4586 sal_uInt32 nDistance = 0xffffffff;
4588 for ( a = 0; a < aPolyPoly.Count(); a++ )
4590 const tools::Polygon& rPoly = aPolyPoly.GetObject( a );
4591 for ( b = 0; b < rPoly.GetSize(); b++ )
4593 if ( rPoly.GetFlags( b ) != PolyFlags::Normal )
4594 continue;
4595 const Point& rPt = rPoly[ b ];
4596 sal_uInt32 nDist = static_cast<sal_uInt32>(hypot( aRefPoint.X - rPt.X(), aRefPoint.Y - rPt.Y() ));
4597 if ( nDist < nDistance )
4599 nRule = nIndex;
4600 nDistance = nDist;
4602 nIndex++;
4606 if ( nDistance != 0xffffffff )
4607 bRectangularConnection = false;
4612 if ( bRectangularConnection )
4614 awt::Point aPoint( aXShape->getPosition() );
4615 awt::Size aSize( aXShape->getSize() );
4617 tools::Rectangle aRect( Point( aPoint.X, aPoint.Y ), Size( aSize.Width, aSize.Height ) );
4618 Point aCenter( aRect.Center() );
4619 tools::Polygon aPoly( 4 );
4621 aPoly[ 0 ] = Point( aCenter.X(), aRect.Top() );
4622 aPoly[ 1 ] = Point( aRect.Left(), aCenter.Y() );
4623 aPoly[ 2 ] = Point( aCenter.X(), aRect.Bottom() );
4624 aPoly[ 3 ] = Point( aRect.Right(), aCenter.Y() );
4626 sal_Int32 nAngle = ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet, "RotateAngle", true ) )
4627 ? *o3tl::doAccess<sal_Int32>(aAny) : 0;
4628 if ( nAngle )
4629 aPoly.Rotate( aRect.TopLeft(), Degree10(static_cast<sal_Int16>( ( nAngle + 5 ) / 10 )) );
4630 nRule = GetClosestPoint( aPoly, aRefPoint );
4632 if (aType == "drawing.Ellipse")
4633 nRule <<= 1; // In PPT an ellipse has 8 ways to connect
4636 return nRule;
4639 EscherSolverContainer::EscherSolverContainer()
4643 EscherSolverContainer::~EscherSolverContainer()
4647 void EscherSolverContainer::AddShape( const uno::Reference<drawing::XShape> & rXShape, sal_uInt32 nId )
4649 maShapeList.push_back( std::make_unique<EscherShapeListEntry>( rXShape, nId ) );
4652 void EscherSolverContainer::AddConnector(
4653 const uno::Reference<drawing::XShape> & rConnector,
4654 const awt::Point& rPA,
4655 uno::Reference<drawing::XShape> const & rConA,
4656 const awt::Point& rPB,
4657 uno::Reference<drawing::XShape> const & rConB
4660 maConnectorList.push_back( std::make_unique<EscherConnectorListEntry>( rConnector, rPA, rConA, rPB, rConB ) );
4663 sal_uInt32 EscherSolverContainer::GetShapeId( const uno::Reference<drawing::XShape> & rXShape ) const
4665 for (auto const & pPtr : maShapeList)
4667 if ( rXShape == pPtr->aXShape )
4668 return pPtr->n_EscherId;
4670 return 0;
4673 void EscherSolverContainer::WriteSolver( SvStream& rStrm )
4675 sal_uInt32 nCount = maConnectorList.size();
4676 if ( !nCount )
4677 return;
4679 sal_uInt32 nRecHdPos, nCurrentPos, nSize;
4680 rStrm .WriteUInt16( ( nCount << 4 ) | 0xf ) // open an ESCHER_SolverContainer
4681 .WriteUInt16( ESCHER_SolverContainer )
4682 .WriteUInt32( 0 );
4684 nRecHdPos = rStrm.Tell() - 4;
4686 EscherConnectorRule aConnectorRule;
4687 aConnectorRule.nRuleId = 2;
4688 for (auto const & pPtr : maConnectorList)
4690 aConnectorRule.ncptiA = aConnectorRule.ncptiB = 0xffffffff;
4691 aConnectorRule.nShapeC = GetShapeId( pPtr->mXConnector );
4692 aConnectorRule.nShapeA = GetShapeId( pPtr->mXConnectToA );
4693 aConnectorRule.nShapeB = GetShapeId( pPtr->mXConnectToB );
4695 if ( aConnectorRule.nShapeC )
4697 if ( aConnectorRule.nShapeA )
4698 aConnectorRule.ncptiA = pPtr->GetConnectorRule( true );
4699 if ( aConnectorRule.nShapeB )
4700 aConnectorRule.ncptiB = pPtr->GetConnectorRule( false );
4702 rStrm .WriteUInt32( ( ESCHER_ConnectorRule << 16 ) | 1 ) // atom hd
4703 .WriteUInt32( 24 )
4704 .WriteUInt32( aConnectorRule.nRuleId )
4705 .WriteUInt32( aConnectorRule.nShapeA )
4706 .WriteUInt32( aConnectorRule.nShapeB )
4707 .WriteUInt32( aConnectorRule.nShapeC )
4708 .WriteUInt32( aConnectorRule.ncptiA )
4709 .WriteUInt32( aConnectorRule.ncptiB );
4711 aConnectorRule.nRuleId += 2;
4714 nCurrentPos = rStrm.Tell(); // close the ESCHER_SolverContainer
4715 nSize = ( nCurrentPos - nRecHdPos ) - 4;
4716 rStrm.Seek( nRecHdPos );
4717 rStrm.WriteUInt32( nSize );
4718 rStrm.Seek( nCurrentPos );
4721 EscherExGlobal::EscherExGlobal() :
4722 EscherGraphicProvider( EscherGraphicProviderFlags::NONE ),
4723 mpPicStrm( nullptr ),
4724 mbHasDggCont( false ),
4725 mbPicStrmQueried( false )
4729 EscherExGlobal::~EscherExGlobal()
4733 sal_uInt32 EscherExGlobal::GenerateDrawingId()
4735 // new drawing starts a new cluster in the cluster table (cluster identifiers are one-based)
4736 sal_uInt32 nClusterId = static_cast< sal_uInt32 >( maClusterTable.size() + 1 );
4737 // drawing identifiers are one-based
4738 sal_uInt32 nDrawingId = static_cast< sal_uInt32 >( maDrawingInfos.size() + 1 );
4739 // prepare new entries in the tables
4740 maClusterTable.emplace_back( nDrawingId );
4741 maDrawingInfos.emplace_back( nClusterId );
4742 // return the new drawing identifier
4743 return nDrawingId;
4746 sal_uInt32 EscherExGlobal::GenerateShapeId( sal_uInt32 nDrawingId, bool bIsInSpgr )
4748 // drawing identifier is one-based
4749 // make sure the drawing is valid (bnc#656503)
4750 if ( nDrawingId == 0 )
4751 return 0;
4752 // create index from the identifier
4753 size_t nDrawingIdx = nDrawingId - 1;
4754 OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GenerateShapeId - invalid drawing ID" );
4755 if( nDrawingIdx >= maDrawingInfos.size() )
4756 return 0;
4757 DrawingInfo& rDrawingInfo = maDrawingInfos[ nDrawingIdx ];
4759 // cluster identifier in drawing info struct is one-based
4760 ClusterEntry* pClusterEntry = &maClusterTable[ rDrawingInfo.mnClusterId - 1 ];
4762 // check cluster overflow, create new cluster entry
4763 if( pClusterEntry->mnNextShapeId == DFF_DGG_CLUSTER_SIZE )
4765 // start a new cluster in the cluster table
4766 maClusterTable.emplace_back( nDrawingId );
4767 pClusterEntry = &maClusterTable.back();
4768 // new size of maClusterTable is equal to one-based identifier of the new cluster
4769 rDrawingInfo.mnClusterId = static_cast< sal_uInt32 >( maClusterTable.size() );
4772 // build shape identifier from cluster identifier and next free cluster shape identifier
4773 rDrawingInfo.mnLastShapeId = static_cast< sal_uInt32 >( rDrawingInfo.mnClusterId * DFF_DGG_CLUSTER_SIZE + pClusterEntry->mnNextShapeId );
4774 // update free shape identifier in cluster entry
4775 ++pClusterEntry->mnNextShapeId;
4776 /* Old code has counted the shapes only, if we are in a SPGRCONTAINER. Is
4777 this really intended? Maybe it's always true... */
4778 if( bIsInSpgr )
4779 ++rDrawingInfo.mnShapeCount;
4781 // return the new shape identifier
4782 return rDrawingInfo.mnLastShapeId;
4785 sal_uInt32 EscherExGlobal::GetDrawingShapeCount( sal_uInt32 nDrawingId ) const
4787 size_t nDrawingIdx = nDrawingId - 1;
4788 OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GetDrawingShapeCount - invalid drawing ID" );
4789 return (nDrawingIdx < maDrawingInfos.size()) ? maDrawingInfos[ nDrawingIdx ].mnShapeCount : 0;
4792 sal_uInt32 EscherExGlobal::GetLastShapeId( sal_uInt32 nDrawingId ) const
4794 size_t nDrawingIdx = nDrawingId - 1;
4795 OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GetLastShapeId - invalid drawing ID" );
4796 return (nDrawingIdx < maDrawingInfos.size()) ? maDrawingInfos[ nDrawingIdx ].mnLastShapeId : 0;
4799 sal_uInt32 EscherExGlobal::GetDggAtomSize() const
4801 // 8 bytes header, 16 bytes fixed DGG data, 8 bytes for each cluster
4802 return static_cast< sal_uInt32 >( 24 + 8 * maClusterTable.size() );
4805 void EscherExGlobal::WriteDggAtom( SvStream& rStrm ) const
4807 sal_uInt32 nDggSize = GetDggAtomSize();
4809 // write the DGG record header (do not include the 8 bytes of the header in the data size)
4810 rStrm.WriteUInt32( ESCHER_Dgg << 16 ).WriteUInt32( nDggSize - 8 );
4812 // calculate and write the fixed DGG data
4813 sal_uInt32 nShapeCount = 0;
4814 sal_uInt32 nLastShapeId = 0;
4815 for (auto const& drawingInfo : maDrawingInfos)
4817 nShapeCount += drawingInfo.mnShapeCount;
4818 nLastShapeId = ::std::max( nLastShapeId, drawingInfo.mnLastShapeId );
4820 // the non-existing cluster with index #0 is counted too
4821 sal_uInt32 nClusterCount = static_cast< sal_uInt32 >( maClusterTable.size() + 1 );
4822 sal_uInt32 nDrawingCount = static_cast< sal_uInt32 >( maDrawingInfos.size() );
4823 rStrm.WriteUInt32( nLastShapeId ).WriteUInt32( nClusterCount ).WriteUInt32( nShapeCount ).WriteUInt32( nDrawingCount );
4825 // write the cluster table
4826 for (auto const& elem : maClusterTable)
4827 rStrm.WriteUInt32( elem.mnDrawingId ).WriteUInt32( elem.mnNextShapeId );
4830 SvStream* EscherExGlobal::QueryPictureStream()
4832 if( !mbPicStrmQueried )
4834 mpPicStrm = ImplQueryPictureStream();
4835 mbPicStrmQueried = true;
4837 return mpPicStrm;
4840 SvStream* EscherExGlobal::ImplQueryPictureStream()
4842 return nullptr;
4845 namespace {
4847 // Implementation of an empty stream that silently succeeds, but does nothing.
4849 // In fact, this is a hack. The right solution is to abstract EscherEx to be
4850 // able to work without SvStream; but at the moment it is better to live with
4851 // this I guess.
4852 class SvNullStream : public SvStream
4854 protected:
4855 virtual std::size_t GetData( void* pData, std::size_t nSize ) override { memset( pData, 0, nSize ); return nSize; }
4856 virtual std::size_t PutData( const void*, std::size_t nSize ) override { return nSize; }
4857 virtual sal_uInt64 SeekPos( sal_uInt64 nPos ) override { return nPos; }
4858 virtual void SetSize( sal_uInt64 ) override {}
4859 virtual void FlushData() override {}
4861 public:
4862 SvNullStream() {}
4867 EscherEx::EscherEx(std::shared_ptr<EscherExGlobal> xGlobal, SvStream* pOutStrm, bool bOOXML)
4868 : mxGlobal(std::move(xGlobal))
4869 , mpOutStrm(pOutStrm)
4870 , mnCurrentDg(0)
4871 , mnCountOfs(0)
4872 , mnGroupLevel(0)
4873 , mnHellLayerId(SDRLAYER_NOTFOUND)
4874 , mbEscherSpgr(false)
4875 , mbEscherDg(false)
4876 , mbOOXML(bOOXML)
4878 if (!mpOutStrm)
4880 mxOwnStrm = std::make_unique<SvNullStream>();
4881 mpOutStrm = mxOwnStrm.get();
4883 mnStrmStartOfs = mpOutStrm->Tell();
4884 mpImplEESdrWriter.reset( new ImplEESdrWriter( *this ) );
4887 EscherEx::~EscherEx()
4891 void EscherEx::Flush( SvStream* pPicStreamMergeBSE /* = NULL */ )
4893 if ( !mxGlobal->HasDggContainer() )
4894 return;
4896 // store the current stream position at ESCHER_Persist_CurrentPosition key
4897 PtReplaceOrInsert( ESCHER_Persist_CurrentPosition, mpOutStrm->Tell() );
4898 if ( DoSeek( ESCHER_Persist_Dgg ) )
4900 /* The DGG record is still not written. ESCHER_Persist_Dgg seeks
4901 to the place where the complete record has to be inserted. */
4902 InsertAtCurrentPos( mxGlobal->GetDggAtomSize() );
4903 mxGlobal->WriteDggAtom( *mpOutStrm );
4905 if ( mxGlobal->HasGraphics() )
4907 /* Calculate the total size of the BSTORECONTAINER including
4908 all BSE records containing the picture data contained in
4909 the passed in pPicStreamMergeBSE. */
4910 sal_uInt32 nBSCSize = mxGlobal->GetBlibStoreContainerSize( pPicStreamMergeBSE );
4911 if ( nBSCSize > 0 )
4913 InsertAtCurrentPos( nBSCSize );
4914 mxGlobal->WriteBlibStoreContainer( *mpOutStrm, pPicStreamMergeBSE );
4918 /* Forget the stream position stored for the DGG which is invalid
4919 after the call to InsertAtCurrentPos() anyway. */
4920 PtDelete( ESCHER_Persist_Dgg );
4922 // seek to initial position (may be different due to inserted DGG and BLIPs)
4923 mpOutStrm->Seek( PtGetOffsetByID( ESCHER_Persist_CurrentPosition ) );
4926 void EscherEx::InsertAtCurrentPos( sal_uInt32 nBytes )
4928 sal_uInt32 nSize, nType, nSource, nBufSize, nToCopy, nCurPos = mpOutStrm->Tell();
4930 // adjust persist table
4931 for(auto const & pPtr : maPersistTable) {
4932 sal_uInt32 nOfs = pPtr->mnOffset;
4933 if ( nOfs >= nCurPos ) {
4934 pPtr->mnOffset += nBytes;
4938 // adapt container and atom sizes
4939 mpOutStrm->Seek( mnStrmStartOfs );
4940 while ( mpOutStrm->Tell() < nCurPos )
4942 mpOutStrm->ReadUInt32( nType ).ReadUInt32( nSize );
4943 sal_uInt32 nEndOfRecord = mpOutStrm->Tell() + nSize;
4944 bool bContainer = (nType & 0x0F) == 0x0F;
4945 /* Expand the record, if the insertion position is inside, or if the
4946 position is at the end of a container (expands always), or at the
4947 end of an atom and bExpandEndOfAtom is set. */
4948 if ( (nCurPos < nEndOfRecord) || ((nCurPos == nEndOfRecord) && bContainer) )
4950 mpOutStrm->SeekRel( -4 );
4951 mpOutStrm->WriteUInt32( nSize + nBytes );
4952 if ( !bContainer )
4953 mpOutStrm->SeekRel( nSize );
4955 else
4956 mpOutStrm->SeekRel( nSize );
4958 for (auto & offset : mOffsets)
4960 if ( offset > nCurPos )
4961 offset += nBytes;
4963 nSource = mpOutStrm->TellEnd();
4964 nToCopy = nSource - nCurPos; // increase the size of the stream by nBytes
4965 std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ 0x40000 ]); // 256KB Buffer
4966 while ( nToCopy )
4968 nBufSize = ( nToCopy >= 0x40000 ) ? 0x40000 : nToCopy;
4969 nToCopy -= nBufSize;
4970 nSource -= nBufSize;
4971 mpOutStrm->Seek( nSource );
4972 mpOutStrm->ReadBytes(pBuf.get(), nBufSize);
4973 mpOutStrm->Seek( nSource + nBytes );
4974 mpOutStrm->WriteBytes(pBuf.get(), nBufSize);
4976 mpOutStrm->Seek( nCurPos );
4979 void EscherEx::InsertPersistOffset( sal_uInt32 nKey, sal_uInt32 nOffset )
4981 PtInsert( ESCHER_Persist_PrivateEntry | nKey, nOffset );
4984 void EscherEx::ReplacePersistOffset( sal_uInt32 nKey, sal_uInt32 nOffset )
4986 PtReplace( ESCHER_Persist_PrivateEntry | nKey, nOffset );
4989 void EscherEx::SetEditAs( const OUString& rEditAs )
4991 mEditAs = rEditAs;
4994 sal_uInt32 EscherEx::GetPersistOffset( sal_uInt32 nKey )
4996 return PtGetOffsetByID( ESCHER_Persist_PrivateEntry | nKey );
4999 bool EscherEx::DoSeek( sal_uInt32 nKey )
5001 sal_uInt32 nPos = PtGetOffsetByID( nKey );
5002 if ( nPos )
5003 mpOutStrm->Seek( nPos );
5004 else
5006 if (! PtIsID( nKey ) )
5007 return false;
5008 mpOutStrm->Seek( 0 );
5010 return true;
5013 bool EscherEx::SeekToPersistOffset( sal_uInt32 nKey )
5015 return DoSeek( ESCHER_Persist_PrivateEntry | nKey );
5018 void EscherEx::InsertAtPersistOffset( sal_uInt32 nKey, sal_uInt32 nValue )
5020 sal_uInt32 nOldPos = mpOutStrm->Tell();
5021 bool bRetValue = SeekToPersistOffset( nKey );
5022 if ( bRetValue )
5024 mpOutStrm->WriteUInt32( nValue );
5025 mpOutStrm->Seek( nOldPos );
5029 void EscherEx::OpenContainer( sal_uInt16 nEscherContainer, int nRecInstance )
5031 mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | 0xf ).WriteUInt16( nEscherContainer ).WriteUInt32( 0 );
5032 mOffsets.push_back( mpOutStrm->Tell() - 4 );
5033 mRecTypes.push_back( nEscherContainer );
5034 switch( nEscherContainer )
5036 case ESCHER_DggContainer :
5038 mxGlobal->SetDggContainer();
5039 mnCurrentDg = 0;
5040 /* Remember the current position as start position of the DGG
5041 record and BSTORECONTAINER, but do not write them actually.
5042 This will be done later in Flush() when the number of drawings,
5043 the size and contents of the FIDCL cluster table, and the size
5044 of the BLIP container are known. */
5045 PtReplaceOrInsert( ESCHER_Persist_Dgg, mpOutStrm->Tell() );
5047 break;
5049 case ESCHER_DgContainer :
5051 if ( mxGlobal->HasDggContainer() )
5053 if ( !mbEscherDg )
5055 mbEscherDg = true;
5056 mnCurrentDg = mxGlobal->GenerateDrawingId();
5057 AddAtom( 8, ESCHER_Dg, 0, mnCurrentDg );
5058 PtReplaceOrInsert( ESCHER_Persist_Dg | mnCurrentDg, mpOutStrm->Tell() );
5059 mpOutStrm->WriteUInt32( 0 ) // The number of shapes in this drawing
5060 .WriteUInt32( 0 ); // The last MSOSPID given to an SP in this DG
5064 break;
5066 case ESCHER_SpgrContainer :
5068 if ( mbEscherDg )
5070 mbEscherSpgr = true;
5073 break;
5075 case ESCHER_SpContainer :
5078 break;
5080 default:
5081 break;
5085 void EscherEx::CloseContainer()
5087 sal_uInt32 nSize, nPos = mpOutStrm->Tell();
5088 nSize = ( nPos - mOffsets.back() ) - 4;
5089 mpOutStrm->Seek( mOffsets.back() );
5090 mpOutStrm->WriteUInt32( nSize );
5092 switch( mRecTypes.back() )
5094 case ESCHER_DgContainer :
5096 if ( mbEscherDg )
5098 mbEscherDg = false;
5099 if ( DoSeek( ESCHER_Persist_Dg | mnCurrentDg ) )
5100 mpOutStrm->WriteUInt32( mxGlobal->GetDrawingShapeCount( mnCurrentDg ) ).WriteUInt32( mxGlobal->GetLastShapeId( mnCurrentDg ) );
5103 break;
5105 case ESCHER_SpgrContainer :
5107 if ( mbEscherSpgr )
5109 mbEscherSpgr = false;
5113 break;
5115 default:
5116 break;
5118 mOffsets.pop_back();
5119 mRecTypes.pop_back();
5120 mpOutStrm->Seek( nPos );
5123 void EscherEx::BeginAtom()
5125 mnCountOfs = mpOutStrm->Tell();
5126 mpOutStrm->WriteUInt32( 0 ).WriteUInt32( 0 ); // record header will be written later
5129 void EscherEx::EndAtom( sal_uInt16 nRecType, int nRecVersion, int nRecInstance )
5131 sal_uInt32 nOldPos = mpOutStrm->Tell();
5132 mpOutStrm->Seek( mnCountOfs );
5133 sal_uInt32 nSize = nOldPos - mnCountOfs;
5134 mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | ( nRecVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nSize - 8 );
5135 mpOutStrm->Seek( nOldPos );
5138 void EscherEx::AddAtom( sal_uInt32 nAtomSize, sal_uInt16 nRecType, int nRecVersion, int nRecInstance )
5140 mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | ( nRecVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nAtomSize );
5143 void EscherEx::AddChildAnchor( const tools::Rectangle& rRect )
5145 AddAtom( 16, ESCHER_ChildAnchor );
5146 mpOutStrm ->WriteInt32( rRect.Left() )
5147 .WriteInt32( rRect.Top() )
5148 .WriteInt32( rRect.Right() )
5149 .WriteInt32( rRect.Bottom() );
5152 void EscherEx::AddClientAnchor( const tools::Rectangle& rRect )
5154 AddAtom( 8, ESCHER_ClientAnchor );
5155 mpOutStrm->WriteInt16( rRect.Top() )
5156 .WriteInt16( rRect.Left() )
5157 .WriteInt16( rRect.GetWidth() + rRect.Left() )
5158 .WriteInt16( rRect.GetHeight() + rRect.Top() );
5161 EscherExHostAppData* EscherEx::EnterAdditionalTextGroup()
5163 return nullptr;
5166 sal_uInt32 EscherEx::EnterGroup( const OUString& rShapeName, const tools::Rectangle* pBoundRect )
5168 tools::Rectangle aRect;
5169 if( pBoundRect )
5170 aRect = *pBoundRect;
5172 OpenContainer( ESCHER_SpgrContainer );
5173 OpenContainer( ESCHER_SpContainer );
5174 AddAtom( 16, ESCHER_Spgr, 1 );
5175 PtReplaceOrInsert( ESCHER_Persist_Grouping_Snap | mnGroupLevel,
5176 mpOutStrm->Tell() );
5177 mpOutStrm ->WriteInt32( aRect.Left() ) // Bounding box for the grouped shapes to which they will be attached
5178 .WriteInt32( aRect.Top() )
5179 .WriteInt32( aRect.IsWidthEmpty() ? aRect.Left() : aRect.Right() )
5180 .WriteInt32( aRect.IsHeightEmpty() ? aRect.Top() : aRect.Bottom() );
5182 sal_uInt32 nShapeId = GenerateShapeId();
5183 if ( !mnGroupLevel )
5184 AddShape( ESCHER_ShpInst_Min, ShapeFlag::Group | ShapeFlag::Patriarch, nShapeId );
5185 else
5187 AddShape( ESCHER_ShpInst_Min, ShapeFlag::Group | ShapeFlag::HaveAnchor, nShapeId );
5188 EscherPropertyContainer aPropOpt;
5189 aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x00040004 );
5190 aPropOpt.AddOpt( ESCHER_Prop_dxWrapDistLeft, 0 );
5191 aPropOpt.AddOpt( ESCHER_Prop_dxWrapDistRight, 0 );
5193 // #i51348# shape name
5194 if( rShapeName.getLength() > 0 )
5195 aPropOpt.AddOpt( ESCHER_Prop_wzName, rShapeName );
5197 Commit( aPropOpt, aRect );
5198 if ( mnGroupLevel > 1 )
5199 AddChildAnchor( aRect );
5201 EscherExHostAppData* pAppData = mpImplEESdrWriter->ImplGetHostData();
5202 if( pAppData )
5204 if ( mnGroupLevel <= 1 )
5205 pAppData->WriteClientAnchor( *this, aRect );
5206 pAppData->WriteClientData( *this );
5209 CloseContainer(); // ESCHER_SpContainer
5210 mnGroupLevel++;
5211 return nShapeId;
5214 sal_uInt32 EscherEx::EnterGroup( const tools::Rectangle* pBoundRect )
5216 return EnterGroup( OUString(), pBoundRect );
5219 void EscherEx::SetGroupSnapRect( sal_uInt32 nGroupLevel, const tools::Rectangle& rRect )
5221 if ( nGroupLevel )
5223 sal_uInt32 nCurrentPos = mpOutStrm->Tell();
5224 if ( DoSeek( ESCHER_Persist_Grouping_Snap | ( nGroupLevel - 1 ) ) )
5226 mpOutStrm ->WriteInt32( rRect.Left() ) // Bounding box for the grouped shapes to which they will be attached
5227 .WriteInt32( rRect.Top() )
5228 .WriteInt32( rRect.Right() )
5229 .WriteInt32( rRect.Bottom() );
5230 mpOutStrm->Seek( nCurrentPos );
5235 void EscherEx::SetGroupLogicRect( sal_uInt32 nGroupLevel, const tools::Rectangle& rRect )
5237 if ( nGroupLevel )
5239 sal_uInt32 nCurrentPos = mpOutStrm->Tell();
5240 if ( DoSeek( ESCHER_Persist_Grouping_Logic | ( nGroupLevel - 1 ) ) )
5242 mpOutStrm->WriteInt16( rRect.Top() ).WriteInt16( rRect.Left() ).WriteInt16( rRect.Right() ).WriteInt16( rRect.Bottom() );
5243 mpOutStrm->Seek( nCurrentPos );
5248 void EscherEx::LeaveGroup()
5250 --mnGroupLevel;
5251 PtDelete( ESCHER_Persist_Grouping_Snap | mnGroupLevel );
5252 PtDelete( ESCHER_Persist_Grouping_Logic | mnGroupLevel );
5253 CloseContainer();
5256 void EscherEx::AddShape( sal_uInt32 nShpInstance, ShapeFlag nFlags, sal_uInt32 nShapeID )
5258 AddAtom( 8, ESCHER_Sp, 2, nShpInstance );
5260 if ( !nShapeID )
5261 nShapeID = GenerateShapeId();
5263 if (nFlags ^ ShapeFlag::Group) // no pure group shape
5265 if ( mnGroupLevel > 1 )
5266 nFlags |= ShapeFlag::Child; // this not a topmost shape
5268 mpOutStrm->WriteUInt32( nShapeID ).WriteUInt32( static_cast<sal_uInt32>(nFlags) );
5271 void EscherEx::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& )
5273 rProps.Commit( GetStream() );
5276 sal_uInt32 EscherEx::GetColor( const sal_uInt32 nSOColor )
5278 sal_uInt32 nColor = nSOColor & 0xff00; // Green
5279 nColor |= static_cast<sal_uInt8>(nSOColor) << 16; // Red
5280 nColor |= static_cast<sal_uInt8>( nSOColor >> 16 ); // Blue
5281 return nColor;
5284 sal_uInt32 EscherEx::GetColor( const Color& rSOColor )
5286 sal_uInt32 nColor = ( rSOColor.GetRed() << 16 );
5287 nColor |= ( rSOColor.GetGreen() << 8 );
5288 nColor |= rSOColor.GetBlue();
5289 nColor = GetColor( nColor );
5290 return nColor;
5293 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */