summaryrefslogtreecommitdiffstats
path: root/filter/source/msfilter/msdffimp.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'filter/source/msfilter/msdffimp.cxx')
-rw-r--r--filter/source/msfilter/msdffimp.cxx7610
1 files changed, 7610 insertions, 0 deletions
diff --git a/filter/source/msfilter/msdffimp.cxx b/filter/source/msfilter/msdffimp.cxx
new file mode 100644
index 000000000..66bac102a
--- /dev/null
+++ b/filter/source/msfilter/msdffimp.cxx
@@ -0,0 +1,7610 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/embed/Aspects.hpp>
+#include <com/sun/star/embed/XEmbeddedObject.hpp>
+
+#include <math.h>
+#include <limits>
+#include <limits.h>
+#include <utility>
+#include <vector>
+
+#include <o3tl/any.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/file.hxx>
+#include <tools/solar.h>
+#include <sal/log.hxx>
+#include <rtl/math.hxx>
+
+#include <comphelper/classids.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/streamwrap.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include <comphelper/seqstream.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/sequence.hxx>
+#include <sot/exchange.hxx>
+#include <sot/storinfo.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/wmf.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/BitmapTools.hxx>
+#include "viscache.hxx"
+
+// SvxItem-Mapping. Is needed to successfully include the SvxItem-Header
+#include <editeng/eeitem.hxx>
+#include <editeng/editdata.hxx>
+#include <tools/stream.hxx>
+#include <tools/debug.hxx>
+#include <tools/zcodec.hxx>
+#include <filter/msfilter/escherex.hxx>
+#include <basegfx/numeric/ftools.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include <com/sun/star/drawing/Position3D.hpp>
+#include <com/sun/star/drawing/Direction3D.hpp>
+#include <editeng/charscaleitem.hxx>
+#include <editeng/kernitem.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <tools/urlobj.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <sot/storage.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/fcontnr.hxx>
+#include <svx/xbtmpit.hxx>
+#include <svx/xsflclit.hxx>
+#include <svx/xflgrit.hxx>
+#include <svx/xflftrit.hxx>
+#include <svx/sdgcpitm.hxx>
+#include <svx/sdgmoitm.hxx>
+#include <svx/svdmodel.hxx>
+#include <svx/svdobj.hxx>
+#include <svx/svdpage.hxx>
+#include <svx/svdogrp.hxx>
+#include <svx/svdograf.hxx>
+#include <svx/svdotext.hxx>
+#include <svx/svdorect.hxx>
+#include <svx/svdoedge.hxx>
+#include <svx/svdoutl.hxx>
+#include <svx/svdoole2.hxx>
+#include <svx/svdopath.hxx>
+#include <svx/xlntrit.hxx>
+#include <svx/xfillit0.hxx>
+#include <svx/xflbmtit.hxx>
+#include <svx/xflclit.hxx>
+#include <svx/xfltrit.hxx>
+#include <svx/xflbmsxy.hxx>
+#include <svx/xflbmsli.hxx>
+#include <editeng/frmdir.hxx>
+#include <editeng/frmdiritem.hxx>
+#include <svx/svdtrans.hxx>
+#include <svx/sxenditm.hxx>
+#include <svx/sdgluitm.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/wghtitem.hxx>
+#include <editeng/postitem.hxx>
+#include <editeng/udlnitem.hxx>
+#include <editeng/crossedoutitem.hxx>
+#include <editeng/shdditem.hxx>
+#include <editeng/fontitem.hxx>
+#include <svx/sxekitm.hxx>
+#include <svx/xpoly.hxx>
+#include <svx/xlineit0.hxx>
+#include <svx/xlncapit.hxx>
+#include <svx/xlinjoit.hxx>
+#include <svx/xlndsit.hxx>
+#include <svx/xlnclit.hxx>
+#include <svx/xlnwtit.hxx>
+#include <svx/xlnstwit.hxx>
+#include <svx/xlnedwit.hxx>
+#include <svx/xlnstit.hxx>
+#include <svx/xlnedit.hxx>
+#include <svx/xlnstcit.hxx>
+#include <svx/xlnedcit.hxx>
+#include <svx/sdasitm.hxx>
+#include <svx/sdggaitm.hxx>
+#include <svx/sdshcitm.hxx>
+#include <svx/sdshitm.hxx>
+#include <svx/sdshtitm.hxx>
+#include <svx/sdsxyitm.hxx>
+#include <svx/sdtagitm.hxx>
+#include <svx/sdtcfitm.hxx>
+#include <svx/sdtditm.hxx>
+#include <svx/sdtfsitm.hxx>
+#include <svx/sdtmfitm.hxx>
+#include <filter/msfilter/classids.hxx>
+#include <filter/msfilter/msdffimp.hxx>
+#include <editeng/outliner.hxx>
+#include <editeng/outlobj.hxx>
+#include <com/sun/star/drawing/ShadeMode.hpp>
+#include <vcl/dibtools.hxx>
+#include <vcl/svapp.hxx>
+#include <svx/svdoashp.hxx>
+#include <svx/EnhancedCustomShapeTypeNames.hxx>
+#include <svx/EnhancedCustomShapeGeometry.hxx>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeGluePointType.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeTextPathMode.hpp>
+#include <com/sun/star/drawing/EnhancedCustomShapeMetalType.hpp>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/ProjectionMode.hpp>
+#include <svx/EnhancedCustomShape2d.hxx>
+#include <rtl/ustring.hxx>
+#include <svtools/embedhlp.hxx>
+#include <memory>
+
+using namespace ::com::sun::star ;
+using namespace ::com::sun::star::drawing;
+using namespace uno ;
+using namespace beans ;
+using namespace drawing ;
+using namespace container ;
+
+// static counter for OLE-Objects
+static sal_uInt32 nMSOleObjCntr = 0;
+constexpr OUStringLiteral MSO_OLE_Obj = u"MSO_OLE_Obj";
+
+namespace {
+/* Office File Formats - 2.2.23 */
+enum class OfficeArtBlipRecInstance : sal_uInt32
+{
+ EMF = 0x3D4, // defined in section 2.2.24.
+ WMF = 0x216, // defined in section 2.2.25.
+ PICT = 0x542, // as defined in section 2.2.26.
+ JPEG_RGB = 0x46A, // defined in section 2.2.27.
+ JPEG_CMYK = 0x6E2, // defined in section 2.2.27.
+ PNG = 0x6E0, // defined in section 2.2.28.
+ DIB = 0x7A8, // defined in section 2.2.29.
+ TIFF = 0x6E4 // defined in section 2.2.30.
+};
+
+struct SvxMSDffBLIPInfo
+{
+ sal_uLong nFilePos; ///< offset of the BLIP in data stream
+ explicit SvxMSDffBLIPInfo(sal_uLong nFPos)
+ : nFilePos(nFPos)
+ {
+ }
+};
+
+}
+
+/// the following will be sorted by the order of their appearance:
+struct SvxMSDffBLIPInfos : public std::vector<SvxMSDffBLIPInfo> {};
+
+/************************************************************************/
+void Impl_OlePres::Write( SvStream & rStm )
+{
+ WriteClipboardFormat( rStm, SotClipboardFormatId::GDIMETAFILE );
+ rStm.WriteInt32( 4 ); // a TargetDevice that's always empty
+ rStm.WriteUInt32( nAspect );
+ rStm.WriteInt32( -1 ); //L-Index always -1
+ rStm.WriteInt32( nAdvFlags );
+ rStm.WriteInt32( 0 ); //Compression
+ rStm.WriteInt32( aSize.Width() );
+ rStm.WriteInt32( aSize.Height() );
+ sal_uInt64 nPos = rStm.Tell();
+ rStm.WriteInt32( 0 );
+
+ if( nFormat == SotClipboardFormatId::GDIMETAFILE && pMtf )
+ {
+ // Always to 1/100 mm, until Mtf-Solution found
+ // Assumption (no scaling, no origin translation)
+ DBG_ASSERT( pMtf->GetPrefMapMode().GetScaleX() == Fraction( 1, 1 ),
+ "x-scale in the Mtf is wrong" );
+ DBG_ASSERT( pMtf->GetPrefMapMode().GetScaleY() == Fraction( 1, 1 ),
+ "y-scale in the Mtf is wrong" );
+ DBG_ASSERT( pMtf->GetPrefMapMode().GetOrigin() == Point(),
+ "origin-shift in the Mtf is wrong" );
+ MapUnit nMU = pMtf->GetPrefMapMode().GetMapUnit();
+ if( MapUnit::Map100thMM != nMU )
+ {
+ Size aPrefS( pMtf->GetPrefSize() );
+ Size aS = OutputDevice::LogicToLogic(aPrefS, MapMode(nMU), MapMode(MapUnit::Map100thMM));
+
+ pMtf->Scale( Fraction( aS.Width(), aPrefS.Width() ),
+ Fraction( aS.Height(), aPrefS.Height() ) );
+ pMtf->SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ pMtf->SetPrefSize( aS );
+ }
+ WriteWindowMetafileBits( rStm, *pMtf );
+ }
+ else
+ {
+ OSL_FAIL( "unknown format" );
+ }
+ sal_uInt64 nEndPos = rStm.Tell();
+ rStm.Seek( nPos );
+ rStm.WriteUInt32( nEndPos - nPos - 4 );
+ rStm.Seek( nEndPos );
+}
+
+DffPropertyReader::DffPropertyReader( const SvxMSDffManager& rMan )
+ : rManager(rMan)
+ , mnFix16Angle(0)
+ , mbRotateGranientFillWithAngle(false)
+{
+ InitializePropSet( DFF_msofbtOPT );
+}
+
+void DffPropertyReader::SetDefaultPropSet( SvStream& rStCtrl, sal_uInt32 nOffsDgg ) const
+{
+ const_cast<DffPropertyReader*>(this)->pDefaultPropSet.reset();
+ sal_uInt64 nOldPos = rStCtrl.Tell();
+ bool bOk = checkSeek(rStCtrl, nOffsDgg);
+ DffRecordHeader aRecHd;
+ if (bOk)
+ bOk = ReadDffRecordHeader( rStCtrl, aRecHd );
+ if (bOk && aRecHd.nRecType == DFF_msofbtDggContainer)
+ {
+ if ( SvxMSDffManager::SeekToRec( rStCtrl, DFF_msofbtOPT, aRecHd.GetRecEndFilePos() ) )
+ {
+ const_cast<DffPropertyReader*>(this)->pDefaultPropSet.reset( new DffPropSet );
+ ReadDffPropSet( rStCtrl, *pDefaultPropSet );
+ }
+ }
+ rStCtrl.Seek( nOldPos );
+}
+
+#ifdef DBG_CUSTOMSHAPE
+void DffPropertyReader::ReadPropSet( SvStream& rIn, SvxMSDffClientData* pClientData, sal_uInt32 nShapeId ) const
+#else
+void DffPropertyReader::ReadPropSet( SvStream& rIn, SvxMSDffClientData* pClientData ) const
+#endif
+{
+ sal_uInt64 nFilePos = rIn.Tell();
+ ReadDffPropSet( rIn, const_cast<DffPropertyReader&>(*this) );
+
+ if ( IsProperty( DFF_Prop_hspMaster ) )
+ {
+ if ( rManager.SeekToShape( rIn, pClientData, GetPropertyValue( DFF_Prop_hspMaster, 0 ) ) )
+ {
+ DffRecordHeader aRecHd;
+ bool bOk = ReadDffRecordHeader(rIn, aRecHd);
+ if (bOk && SvxMSDffManager::SeekToRec(rIn, DFF_msofbtOPT, aRecHd.GetRecEndFilePos()))
+ {
+ rIn |= const_cast<DffPropertyReader&>(*this);
+ }
+ }
+ }
+
+ const_cast<DffPropertyReader*>(this)->mnFix16Angle = Fix16ToAngle( GetPropertyValue( DFF_Prop_Rotation, 0 ) );
+
+#ifdef DBG_CUSTOMSHAPE
+
+ OUString aURLStr;
+
+ if( osl::FileBase::getFileURLFromSystemPath( OUString("d:\\ashape.dbg"), aURLStr ) == osl::FileBase::E_None )
+ {
+ std::unique_ptr<SvStream> xOut(::utl::UcbStreamHelper::CreateStream( aURLStr, StreamMode::WRITE ));
+
+ if( xOut )
+ {
+ xOut->Seek( STREAM_SEEK_TO_END );
+
+ if ( IsProperty( DFF_Prop_adjustValue ) || IsProperty( DFF_Prop_pVertices ) )
+ {
+ xOut->WriteLine( "" );
+ OString aString("ShapeId: " + OString::number(nShapeId));
+ xOut->WriteLine(aString);
+ }
+ for ( sal_uInt32 i = DFF_Prop_adjustValue; i <= DFF_Prop_adjust10Value; i++ )
+ {
+ if ( IsProperty( i ) )
+ {
+ OString aString("Prop_adjustValue" + OString::number( ( i - DFF_Prop_adjustValue ) + 1 ) +
+ ":" + OString::number(GetPropertyValue(i)) );
+ xOut->WriteLine(aString);
+ }
+ }
+ sal_Int32 i;
+ for ( i = 320; i < 383; i++ )
+ {
+ if ( ( i >= DFF_Prop_adjustValue ) && ( i <= DFF_Prop_adjust10Value ) )
+ continue;
+ if ( IsProperty( i ) )
+ {
+ if ( SeekToContent( i, rIn ) )
+ {
+ sal_Int32 nLen = (sal_Int32)GetPropertyValue( i );
+ if ( nLen )
+ {
+ xOut->WriteLine( "" );
+ OStringBuffer aDesc("Property:" + OString::number(i) +
+ " Size:" + OString::number(nLen));
+ xOut->WriteLine(aDesc.makeStringAndClear());
+ sal_Int16 nNumElem, nNumElemMem, nNumSize;
+ rIn >> nNumElem >> nNumElemMem >> nNumSize;
+ aDesc.append("Entries: " + OString::number(nNumElem) +
+ " Size:" + OString::number(nNumSize));
+ xOut->WriteLine(aDesc.makeStringAndClear());
+ if ( nNumSize < 0 )
+ nNumSize = ( ( -nNumSize ) >> 2 );
+ if ( !nNumSize )
+ nNumSize = 16;
+ nLen -= 6;
+ while ( nLen > 0 )
+ {
+ for ( sal_uInt32 j = 0; nLen && ( j < ( nNumSize >> 1 ) ); j++ )
+ {
+ for ( sal_uInt32 k = 0; k < 2; k++ )
+ {
+ if ( nLen )
+ {
+ sal_uInt8 nVal;
+ rIn >> nVal;
+ if ( ( nVal >> 4 ) > 9 )
+ *xOut << (sal_uInt8)( ( nVal >> 4 ) + 'A' - 10 );
+ else
+ *xOut << (sal_uInt8)( ( nVal >> 4 ) + '0' );
+
+ if ( ( nVal & 0xf ) > 9 )
+ *xOut << (sal_uInt8)( ( nVal & 0xf ) + 'A' - 10 );
+ else
+ *xOut << (sal_uInt8)( ( nVal & 0xf ) + '0' );
+
+ nLen--;
+ }
+ }
+ *xOut << (char)( ' ' );
+ }
+ xOut->WriteLine( OString() );
+ }
+ }
+ }
+ else
+ {
+ OString aString("Property" + OString::number(i) +
+ ":" + OString::number(GetPropertyValue(i)));
+ xOut->WriteLine(aString);
+ }
+ }
+ }
+ }
+ }
+
+#endif
+
+ rIn.Seek( nFilePos );
+}
+
+
+Degree100 DffPropertyReader::Fix16ToAngle( sal_Int32 nContent )
+{
+ Degree100 nAngle(0);
+ if ( nContent )
+ {
+ nAngle = Degree100(( static_cast<sal_Int16>( nContent >> 16) * 100L ) + ( ( ( nContent & 0x0000ffff) * 100L ) >> 16 ));
+ nAngle = NormAngle36000( -nAngle );
+ }
+ return nAngle;
+}
+
+DffPropertyReader::~DffPropertyReader()
+{
+}
+
+static SvStream& operator>>( SvStream& rIn, SvxMSDffConnectorRule& rRule )
+{
+ sal_uInt32 nRuleId;
+ rIn.ReadUInt32( nRuleId )
+ .ReadUInt32( rRule.nShapeA )
+ .ReadUInt32( rRule.nShapeB )
+ .ReadUInt32( rRule.nShapeC )
+ .ReadUInt32( rRule.ncptiA )
+ .ReadUInt32( rRule.ncptiB );
+
+ return rIn;
+}
+
+SvxMSDffSolverContainer::SvxMSDffSolverContainer()
+{
+}
+
+SvxMSDffSolverContainer::~SvxMSDffSolverContainer()
+{
+}
+
+SvStream& ReadSvxMSDffSolverContainer( SvStream& rIn, SvxMSDffSolverContainer& rContainer )
+{
+ DffRecordHeader aHd;
+ bool bOk = ReadDffRecordHeader( rIn, aHd );
+ if (!bOk || aHd.nRecType != DFF_msofbtSolverContainer)
+ return rIn;
+
+ DffRecordHeader aCRule;
+ auto nEndPos = DffPropSet::SanitizeEndPos(rIn, aHd.GetRecEndFilePos());
+ while ( rIn.good() && ( rIn.Tell() < nEndPos ) )
+ {
+ if (!ReadDffRecordHeader(rIn, aCRule))
+ break;
+ if ( aCRule.nRecType == DFF_msofbtConnectorRule )
+ {
+ std::unique_ptr<SvxMSDffConnectorRule> pRule(new SvxMSDffConnectorRule);
+ rIn >> *pRule;
+ rContainer.aCList.push_back( std::move(pRule) );
+ }
+ if (!aCRule.SeekToEndOfRecord(rIn))
+ break;
+ }
+ return rIn;
+}
+
+void SvxMSDffManager::SolveSolver( const SvxMSDffSolverContainer& rSolver )
+{
+ size_t i, nCnt;
+ for ( i = 0, nCnt = rSolver.aCList.size(); i < nCnt; i++ )
+ {
+ SvxMSDffConnectorRule* pPtr = rSolver.aCList[ i ].get();
+ if ( pPtr->pCObj )
+ {
+ for ( int nN = 0; nN < 2; nN++ )
+ {
+ SdrObject* pO;
+ sal_uInt32 nC;
+ ShapeFlag nSpFlags;
+ if ( !nN )
+ {
+ pO = pPtr->pAObj;
+ nC = pPtr->ncptiA;
+ nSpFlags = pPtr->nSpFlagsA;
+ }
+ else
+ {
+ pO = pPtr->pBObj;
+ nC = pPtr->ncptiB;
+ nSpFlags = pPtr->nSpFlagsB;
+ }
+ if ( pO )
+ {
+ SdrGluePoint aGluePoint;
+ Reference< XShape > aXShape( pO->getUnoShape(), UNO_QUERY );
+ Reference< XShape > aXConnector( pPtr->pCObj->getUnoShape(), UNO_QUERY );
+ SdrGluePointList* pList = pO->ForceGluePointList();
+
+ sal_Int32 nId = nC;
+ SdrInventor nInventor = pO->GetObjInventor();
+
+ if( nInventor == SdrInventor::Default )
+ {
+ bool bValidGluePoint = false;
+ SdrObjKind nObjId = pO->GetObjIdentifier();
+ switch( nObjId )
+ {
+ case SdrObjKind::Group :
+ case SdrObjKind::Graphic :
+ case SdrObjKind::Rectangle :
+ case SdrObjKind::Text :
+ case SdrObjKind::Page :
+ case SdrObjKind::TitleText :
+ case SdrObjKind::OutlineText :
+ {
+ if ( nC & 1 )
+ {
+ if ( nSpFlags & ShapeFlag::FlipH )
+ nC ^= 2; // 1 <-> 3
+ }
+ else
+ {
+ if ( nSpFlags & ShapeFlag::FlipV )
+ nC ^= 1; // 0 <-> 2
+ }
+ switch( nC )
+ {
+ case 0 :
+ nId = 0; // SdrAlign::VERT_TOP;
+ break;
+ case 1 :
+ nId = 3; // SdrAlign::HORZ_RIGHT;
+ break;
+ case 2 :
+ nId = 2; // SdrAlign::VERT_BOTTOM;
+ break;
+ case 3 :
+ nId = 1; // SdrAlign::HORZ_LEFT;
+ break;
+ }
+ if ( nId <= 3 )
+ bValidGluePoint = true;
+ }
+ break;
+ case SdrObjKind::Polygon :
+ case SdrObjKind::PolyLine :
+ case SdrObjKind::Line :
+ case SdrObjKind::PathLine :
+ case SdrObjKind::PathFill :
+ case SdrObjKind::FreehandLine :
+ case SdrObjKind::FreehandFill :
+ case SdrObjKind::SplineLine :
+ case SdrObjKind::SplineFill :
+ case SdrObjKind::PathPoly :
+ case SdrObjKind::PathPolyLine :
+ {
+ if (pList)
+ {
+ if (pList->GetCount() > nC )
+ {
+ bValidGluePoint = true;
+ nId = static_cast<sal_Int32>((*pList)[ static_cast<sal_uInt16>(nC)].GetId() + 3 );
+ }
+ else
+ {
+ bool bNotFound = true;
+
+ tools::PolyPolygon aPolyPoly( EscherPropertyContainer::GetPolyPolygon( aXShape ) );
+ sal_uInt16 k, j, nPolySize = aPolyPoly.Count();
+ if ( nPolySize )
+ {
+ tools::Rectangle aBoundRect( aPolyPoly.GetBoundRect() );
+ if ( aBoundRect.GetWidth() && aBoundRect.GetHeight() )
+ {
+ sal_uInt32 nPointCount = 0;
+ for ( k = 0; bNotFound && ( k < nPolySize ); k++ )
+ {
+ const tools::Polygon& rPolygon = aPolyPoly.GetObject( k );
+ for ( j = 0; bNotFound && ( j < rPolygon.GetSize() ); j++ )
+ {
+ PolyFlags eFlags = rPolygon.GetFlags( j );
+ if ( eFlags == PolyFlags::Normal )
+ {
+ if ( nC == nPointCount )
+ {
+ const Point& rPoint = rPolygon.GetPoint( j );
+ double fXRel = rPoint.X() - aBoundRect.Left();
+ double fYRel = rPoint.Y() - aBoundRect.Top();
+ sal_Int32 nWidth = aBoundRect.GetWidth();
+ if ( !nWidth )
+ nWidth = 1;
+ sal_Int32 nHeight= aBoundRect.GetHeight();
+ if ( !nHeight )
+ nHeight = 1;
+ fXRel /= static_cast<double>(nWidth);
+ fXRel *= 10000;
+ fYRel /= static_cast<double>(nHeight);
+ fYRel *= 10000;
+ aGluePoint.SetPos( Point( static_cast<sal_Int32>(fXRel), static_cast<sal_Int32>(fYRel) ) );
+ aGluePoint.SetPercent( true );
+ aGluePoint.SetAlign( SdrAlign::VERT_TOP | SdrAlign::HORZ_LEFT );
+ aGluePoint.SetEscDir( SdrEscapeDirection::SMART );
+ nId = static_cast<sal_Int32>((*pList)[ pList->Insert( aGluePoint ) ].GetId() + 3 );
+ bNotFound = false;
+ }
+ nPointCount++;
+ }
+ }
+ }
+ }
+ }
+ if ( !bNotFound )
+ {
+ bValidGluePoint = true;
+ }
+ }
+ }
+ }
+ break;
+
+ case SdrObjKind::CustomShape :
+ {
+ const SfxPoolItem& aCustomShape = static_cast<SdrObjCustomShape*>(pO)->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY );
+ SdrCustomShapeGeometryItem aGeometryItem( static_cast<const SdrCustomShapeGeometryItem&>(aCustomShape) );
+ static const OUStringLiteral sPath( u"Path" );
+ sal_Int16 nGluePointType = EnhancedCustomShapeGluePointType::SEGMENTS;
+ css::uno::Any* pAny = aGeometryItem.GetPropertyValueByName( sPath, "GluePointType" );
+ if ( pAny )
+ *pAny >>= nGluePointType;
+ else
+ {
+ OUString sShapeType;
+ pAny = aGeometryItem.GetPropertyValueByName( "Type" );
+ if ( pAny )
+ *pAny >>= sShapeType;
+ MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType );
+ nGluePointType = GetCustomShapeConnectionTypeDefault( eSpType );
+ }
+ if ( nGluePointType == EnhancedCustomShapeGluePointType::CUSTOM )
+ {
+ if ( pList && ( pList->GetCount() > nC ) )
+ {
+ bValidGluePoint = true;
+ nId = static_cast<sal_Int32>((*pList)[ static_cast<sal_uInt16>(nC)].GetId() + 3 );
+ }
+ }
+ else if ( nGluePointType == EnhancedCustomShapeGluePointType::RECT )
+ {
+ if ( nC & 1 )
+ {
+ if ( nSpFlags & ShapeFlag::FlipH )
+ nC ^= 2; // 1 <-> 3
+ }
+ else
+ {
+ if ( nSpFlags & ShapeFlag::FlipV )
+ nC ^= 1; // 0 <-> 2
+ }
+ switch( nC )
+ {
+ case 0 :
+ nId = 0; // SdrAlign::VERT_TOP;
+ break;
+ case 1 :
+ nId = 3; // SdrAlign::HORZ_RIGHT;
+ break;
+ case 2 :
+ nId = 2; // SdrAlign::VERT_BOTTOM;
+ break;
+ case 3 :
+ nId = 1; // SdrAlign::HORZ_LEFT;
+ break;
+ }
+ if ( nId <= 3 )
+ bValidGluePoint = true;
+ }
+ else if ( nGluePointType == EnhancedCustomShapeGluePointType::SEGMENTS )
+ {
+ sal_uInt32 nPt = nC;
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > aSegments;
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, "Segments" );
+ if ( pAny && (*pAny >>= aSegments) )
+ {
+ nPt = 0;
+ for ( sal_Int32 k = 1; nC && ( k < aSegments.getLength() ); k++ )
+ {
+ sal_Int16 j, nCnt2 = aSegments[ k ].Count;
+ if ( aSegments[ k ].Command != EnhancedCustomShapeSegmentCommand::UNKNOWN )
+ {
+ for ( j = 0; nC && ( j < nCnt2 ); j++ )
+ {
+ switch( aSegments[ k ].Command )
+ {
+ case EnhancedCustomShapeSegmentCommand::ENDSUBPATH :
+ case EnhancedCustomShapeSegmentCommand::CLOSESUBPATH :
+ case EnhancedCustomShapeSegmentCommand::LINETO :
+ case EnhancedCustomShapeSegmentCommand::MOVETO :
+ {
+ nC--;
+ nPt++;
+ }
+ break;
+ case EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
+ case EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
+ break;
+
+ case EnhancedCustomShapeSegmentCommand::CURVETO :
+ {
+ nC--;
+ nPt += 3;
+ }
+ break;
+
+ case EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
+ case EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
+ {
+ nC--;
+ nPt += 3;
+ }
+ break;
+ case EnhancedCustomShapeSegmentCommand::ARCTO :
+ case EnhancedCustomShapeSegmentCommand::ARC :
+ case EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
+ case EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
+ {
+ nC--;
+ nPt += 4;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, "Coordinates" );
+ if ( pAny )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aCoordinates;
+ *pAny >>= aCoordinates;
+ if ( nPt < o3tl::make_unsigned(aCoordinates.getLength()) )
+ {
+ nId = 4;
+ css::drawing::EnhancedCustomShapeParameterPair& rPara = aCoordinates.getArray()[ nPt ];
+ sal_Int32 nX = 0, nY = 0;
+ if ( ( rPara.First.Value >>= nX ) && ( rPara.Second.Value >>= nY ) )
+ {
+ static const OUStringLiteral sGluePoints( u"GluePoints" );
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aGluePoints;
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints );
+ if ( pAny )
+ *pAny >>= aGluePoints;
+ sal_Int32 nGluePoints = aGluePoints.getLength();
+ aGluePoints.realloc( nGluePoints + 1 );
+ auto pGluePoints = aGluePoints.getArray();
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pGluePoints[ nGluePoints ].First, nX );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pGluePoints[ nGluePoints ].Second, nY );
+ PropertyValue aProp;
+ aProp.Name = sGluePoints;
+ aProp.Value <<= aGluePoints;
+ aGeometryItem.SetPropertyValue( sPath, aProp );
+ bValidGluePoint = true;
+ static_cast<SdrObjCustomShape*>(pO)->SetMergedItem( aGeometryItem );
+ SdrGluePointList* pLst = pO->ForceGluePointList();
+ if ( pLst->GetCount() > nGluePoints )
+ nId = static_cast<sal_Int32>((*pLst)[ static_cast<sal_uInt16>(nGluePoints) ].GetId() + 3 );
+ }
+ }
+ }
+ }
+ }
+ break;
+ default: ;
+ }
+ if ( bValidGluePoint )
+ {
+ Reference< XPropertySet > xPropSet( aXConnector, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ if ( nN )
+ {
+ OUString aPropName( "EndShape" );
+ SetPropValue( Any(aXShape), xPropSet, aPropName );
+ aPropName = "EndGluePointIndex";
+ SetPropValue( Any(nId), xPropSet, aPropName );
+ }
+ else
+ {
+ OUString aPropName( "StartShape" );
+ SetPropValue( Any(aXShape), xPropSet, aPropName );
+ aPropName = "StartGluePointIndex";
+ SetPropValue( Any(nId), xPropSet, aPropName );
+ }
+
+ // Not sure what this is good for, repaint or broadcast of object change.
+ //( Thus I am adding repaint here
+ pO->SetChanged();
+ pO->BroadcastObjectChange();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static basegfx::B2DPolyPolygon GetLineArrow( const sal_Int32 nLineWidth, const sal_uInt32 eLineEnd,
+ const sal_uInt32 eLineWidth, const sal_uInt32 eLineLength,
+ sal_Int32& rnArrowWidth, bool& rbArrowCenter,
+ OUString& rsArrowName, bool bScaleArrow )
+{
+ basegfx::B2DPolyPolygon aRetPolyPoly;
+ // 70 100mm = 2pt = 40 twip. In MS, line width less than 2pt has the same size arrow as 2pt
+ //If the unit is twip. Make all use this unit especially the critical value 70/40.
+ sal_Int32 nLineWidthCritical = bScaleArrow ? 40 : 70;
+ double fLineWidth = nLineWidth < nLineWidthCritical ? nLineWidthCritical : nLineWidth;
+
+ double fLengthMul, fWidthMul;
+ sal_Int32 nLineNumber;
+ switch( eLineLength )
+ {
+ default :
+ case mso_lineMediumLenArrow : fLengthMul = 3.0; nLineNumber = 2; break;
+ case mso_lineShortArrow : fLengthMul = 2.0; nLineNumber = 1; break;
+ case mso_lineLongArrow : fLengthMul = 5.0; nLineNumber = 3; break;
+ }
+ switch( eLineWidth )
+ {
+ default :
+ case mso_lineMediumWidthArrow : fWidthMul = 3.0; nLineNumber += 3; break;
+ case mso_lineNarrowArrow : fWidthMul = 2.0; break;
+ case mso_lineWideArrow : fWidthMul = 5.0; nLineNumber += 6; break;
+ }
+
+ rbArrowCenter = false;
+ OUStringBuffer aArrowName;
+ switch ( eLineEnd )
+ {
+ case mso_lineArrowEnd :
+ {
+ basegfx::B2DPolygon aTriangle;
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50, 0.0 ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth, fLengthMul * fLineWidth ));
+ aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth ));
+ aTriangle.setClosed(true);
+ aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle);
+ aArrowName.append("msArrowEnd ");
+ }
+ break;
+
+ case mso_lineArrowOpenEnd :
+ {
+ switch( eLineLength )
+ {
+ default :
+ case mso_lineMediumLenArrow : fLengthMul = 4.5; break;
+ case mso_lineShortArrow : fLengthMul = 3.5; break;
+ case mso_lineLongArrow : fLengthMul = 6.0; break;
+ }
+ switch( eLineWidth )
+ {
+ default :
+ case mso_lineMediumWidthArrow : fWidthMul = 4.5; break;
+ case mso_lineNarrowArrow : fWidthMul = 3.5; break;
+ case mso_lineWideArrow : fWidthMul = 6.0; break;
+ }
+ basegfx::B2DPolygon aTriangle;
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , 0.0 ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth, fLengthMul * fLineWidth * 0.91 ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.85, fLengthMul * fLineWidth ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50, fLengthMul * fLineWidth * 0.36 ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.15, fLengthMul * fLineWidth ));
+ aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth * 0.91 ));
+ aTriangle.setClosed(true);
+ aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle);
+ aArrowName.append("msArrowOpenEnd ");
+ }
+ break;
+ case mso_lineArrowStealthEnd :
+ {
+ basegfx::B2DPolygon aTriangle;
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , 0.0 ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth , fLengthMul * fLineWidth ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , fLengthMul * fLineWidth * 0.60 ));
+ aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth ));
+ aTriangle.setClosed(true);
+ aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle);
+ aArrowName.append("msArrowStealthEnd ");
+ }
+ break;
+ case mso_lineArrowDiamondEnd :
+ {
+ basegfx::B2DPolygon aTriangle;
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , 0.0 ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth , fLengthMul * fLineWidth * 0.50 ));
+ aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , fLengthMul * fLineWidth ));
+ aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth * 0.50 ));
+ aTriangle.setClosed(true);
+ aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle);
+ rbArrowCenter = true;
+ aArrowName.append("msArrowDiamondEnd ");
+ }
+ break;
+ case mso_lineArrowOvalEnd :
+ {
+ aRetPolyPoly = basegfx::B2DPolyPolygon(
+ XPolygon(
+ Point( static_cast<sal_Int32>( fWidthMul * fLineWidth * 0.50 ), 0 ),
+ static_cast<sal_Int32>( fWidthMul * fLineWidth * 0.50 ),
+ static_cast<sal_Int32>( fLengthMul * fLineWidth * 0.50 ),
+ 0_deg100, 36000_deg100 ).getB2DPolygon() );
+ rbArrowCenter = true;
+ aArrowName.append("msArrowOvalEnd ");
+ }
+ break;
+ default: break;
+ }
+ aArrowName.append(nLineNumber);
+ rsArrowName = aArrowName.makeStringAndClear();
+ rnArrowWidth = static_cast<sal_Int32>( fLineWidth * fWidthMul );
+
+ return aRetPolyPoly;
+}
+
+void DffPropertyReader::ApplyLineAttributes( SfxItemSet& rSet, const MSO_SPT eShapeType ) const // #i28269#
+{
+ sal_uInt32 nLineFlags(GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 ));
+
+ if(!IsHardAttribute( DFF_Prop_fLine ) && !IsCustomShapeStrokedByDefault( eShapeType ))
+ {
+ nLineFlags &= ~0x08;
+ }
+
+ if ( nLineFlags & 8 )
+ {
+ // Line Attributes
+ sal_Int32 nLineWidth = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_lineWidth, 9525 ));
+
+ // support LineCap
+ auto eLineCap = GetPropertyValue(DFF_Prop_lineEndCapStyle, mso_lineEndCapFlat);
+
+ switch(eLineCap)
+ {
+ default: /* case mso_lineEndCapFlat */
+ {
+ // no need to set, it is the default. If this changes, this needs to be activated
+ // rSet.Put(XLineCapItem(css::drawing::LineCap_BUTT));
+ break;
+ }
+ case mso_lineEndCapRound:
+ {
+ rSet.Put(XLineCapItem(css::drawing::LineCap_ROUND));
+ break;
+ }
+ case mso_lineEndCapSquare:
+ {
+ rSet.Put(XLineCapItem(css::drawing::LineCap_SQUARE));
+ break;
+ }
+ }
+
+ auto eLineDashing = GetPropertyValue( DFF_Prop_lineDashing, mso_lineSolid);
+ if (eLineDashing == mso_lineSolid || nLineWidth < 0)
+ rSet.Put(XLineStyleItem( drawing::LineStyle_SOLID ) );
+ else
+ {
+ // Despite of naming "dot" and "dash", that are all dashes and a "dot" can be longer
+ // than a "dash". The naming indicates the order, "dot" is always the first dash and
+ // "dash" is always the second dash. MS Office always starts with the longer dash, so
+ // set it here accordingly.
+ // The preset from binary is essentially the same as from OOXML. So here the same
+ // setting is used as in oox import. The comment corresponds to
+ // "dots, dotLen, dashes, dashLen, distance" there.
+ // MS Office uses always relative length, so no need to consider nLineWidth
+ // here. Values are of kind 300 for 300% in css::drawing::DashStyle, for example.
+
+ sal_uInt16 nDots = 1; // in all cases, "solid" is treated above
+ // initialize, will be changed if necessary
+ sal_uInt32 nDotLen = 300;
+ sal_uInt16 nDashes = 0;
+ sal_uInt32 nDashLen = 0;
+ sal_uInt32 nDistance = 300;
+ switch ( eLineDashing )
+ {
+ default:
+ case mso_lineDotSys : // 1 1 0 0 1
+ {
+ nDotLen =100;
+ nDistance = 100;
+ }
+ break;
+
+ case mso_lineDashGEL : // 1 4 0 0 3
+ {
+ nDotLen = 400;
+ }
+ break;
+
+ case mso_lineDashDotGEL : // 1 4 1 1 3
+ {
+ nDotLen = 400;
+ nDashes = 1;
+ nDashLen = 100;
+ }
+ break;
+
+ case mso_lineLongDashGEL : // 1 8 0 0 3
+ {
+ nDotLen = 800;
+ }
+ break;
+
+ case mso_lineLongDashDotGEL : // 1 8 1 1 3
+ {
+ nDotLen = 800;
+ nDashes = 1;
+ nDashLen = 100;
+ }
+ break;
+
+ case mso_lineLongDashDotDotGEL: // 1 8 2 1 3
+ {
+ nDotLen = 800;
+ nDashes = 2;
+ nDashLen = 100;
+ }
+ break;
+
+ case mso_lineDotGEL: // 1 1 0 0 3
+ {
+ nDotLen = 100;
+ }
+ break;
+
+ case mso_lineDashSys: // 1 3 0 0 1
+ {
+ nDistance = 100;
+ }
+ break;
+
+ case mso_lineDashDotSys: // 1 3 1 1 1
+ {
+ nDashes = 1;
+ nDashLen = 100;
+ nDistance = 100;
+ }
+ break;
+
+ case mso_lineDashDotDotSys: // 1 3 2 1 1
+ {
+ nDashes = 2;
+ nDashLen = 100;
+ nDistance = 100;
+ }
+ break;
+ }
+ rSet.Put( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECTRELATIVE, nDots, nDotLen, nDashes, nDashLen, nDistance ) ) );
+ rSet.Put( XLineStyleItem( drawing::LineStyle_DASH ) );
+ }
+ rSet.Put( XLineColorItem( OUString(), rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_lineColor, 0 ) ) ) );
+ if ( IsProperty( DFF_Prop_lineOpacity ) )
+ {
+ double nTrans = GetPropertyValue(DFF_Prop_lineOpacity, 0x10000);
+ nTrans = (nTrans * 100) / 65536;
+ rSet.Put(XLineTransparenceItem(
+ sal_uInt16(100 - ::rtl::math::round(nTrans))));
+ }
+
+ rManager.ScaleEmu( nLineWidth );
+ rSet.Put( XLineWidthItem( nLineWidth ) );
+
+ // SJ: LineJoint (setting each time a line is set, because our internal joint type has another default)
+ MSO_LineJoin eLineJointDefault = mso_lineJoinMiter;
+ if ( eShapeType == mso_sptMin )
+ eLineJointDefault = mso_lineJoinRound;
+ auto eLineJoint = GetPropertyValue(DFF_Prop_lineJoinStyle, eLineJointDefault);
+ css::drawing::LineJoint eXLineJoint( css::drawing::LineJoint_MITER );
+ if ( eLineJoint == mso_lineJoinBevel )
+ eXLineJoint = css::drawing::LineJoint_BEVEL;
+ else if ( eLineJoint == mso_lineJoinRound )
+ eXLineJoint = css::drawing::LineJoint_ROUND;
+ rSet.Put( XLineJointItem( eXLineJoint ) );
+
+ if ( nLineFlags & 0x10 )
+ {
+ bool bScaleArrows = rManager.pSdrModel->GetScaleUnit() == MapUnit::MapTwip;
+
+ // LineStart
+
+ if ( IsProperty( DFF_Prop_lineStartArrowhead ) )
+ {
+ auto eLineEnd = GetPropertyValue(DFF_Prop_lineStartArrowhead, 0);
+ auto eWidth = GetPropertyValue(DFF_Prop_lineStartArrowWidth, mso_lineMediumWidthArrow);
+ auto eLength = GetPropertyValue(DFF_Prop_lineStartArrowLength, mso_lineMediumLenArrow);
+
+ sal_Int32 nArrowWidth;
+ bool bArrowCenter;
+ OUString aArrowName;
+ basegfx::B2DPolyPolygon aPolyPoly(GetLineArrow( nLineWidth, eLineEnd, eWidth, eLength, nArrowWidth, bArrowCenter, aArrowName, bScaleArrows ));
+
+ rSet.Put( XLineStartWidthItem( nArrowWidth ) );
+ rSet.Put( XLineStartItem( aArrowName, aPolyPoly) );
+ rSet.Put( XLineStartCenterItem( bArrowCenter ) );
+ }
+
+ // LineEnd
+
+ if ( IsProperty( DFF_Prop_lineEndArrowhead ) )
+ {
+ auto eLineEnd = GetPropertyValue(DFF_Prop_lineEndArrowhead, 0);
+ auto eWidth = GetPropertyValue(DFF_Prop_lineEndArrowWidth, mso_lineMediumWidthArrow);
+ auto eLength = GetPropertyValue(DFF_Prop_lineEndArrowLength, mso_lineMediumLenArrow);
+
+ sal_Int32 nArrowWidth;
+ bool bArrowCenter;
+ OUString aArrowName;
+ basegfx::B2DPolyPolygon aPolyPoly(GetLineArrow( nLineWidth, eLineEnd, eWidth, eLength, nArrowWidth, bArrowCenter, aArrowName, bScaleArrows ));
+
+ rSet.Put( XLineEndWidthItem( nArrowWidth ) );
+ rSet.Put( XLineEndItem( aArrowName, aPolyPoly ) );
+ rSet.Put( XLineEndCenterItem( bArrowCenter ) );
+ }
+ }
+ }
+ else
+ rSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) );
+}
+
+namespace {
+
+struct ShadeColor
+{
+ Color aColor;
+ double fDist;
+
+ ShadeColor( const Color& rC, double fR ) : aColor( rC ), fDist( fR ) {};
+};
+
+}
+
+static void GetShadeColors( const SvxMSDffManager& rManager, const DffPropertyReader& rProperties, SvStream& rIn, std::vector< ShadeColor >& rShadeColors )
+{
+ sal_uInt64 nPos = rIn.Tell();
+ if ( rProperties.IsProperty( DFF_Prop_fillShadeColors ) )
+ {
+ sal_uInt16 i = 0, nNumElem = 0;
+ bool bOk = false;
+ if (rProperties.SeekToContent(DFF_Prop_fillShadeColors, rIn))
+ {
+ sal_uInt16 nNumElemReserved = 0, nSize = 0;
+ rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemReserved ).ReadUInt16( nSize );
+ //sanity check that the stream is long enough to fulfill nNumElem * 2 sal_Int32s
+ bOk = rIn.remainingSize() / (2*sizeof(sal_Int32)) >= nNumElem;
+ }
+ if (bOk)
+ {
+ for ( ; i < nNumElem; i++ )
+ {
+ sal_Int32 nColor(0);
+ sal_Int32 nDist(0);
+
+ rIn.ReadInt32( nColor ).ReadInt32( nDist );
+ rShadeColors.emplace_back( rManager.MSO_CLR_ToColor( nColor, DFF_Prop_fillColor ), 1.0 - ( nDist / 65536.0 ) );
+ }
+ }
+ }
+ if ( rShadeColors.empty() )
+ {
+ rShadeColors.emplace_back( rManager.MSO_CLR_ToColor( rProperties.GetPropertyValue( DFF_Prop_fillBackColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillBackColor ), 0 );
+ rShadeColors.emplace_back( rManager.MSO_CLR_ToColor( rProperties.GetPropertyValue( DFF_Prop_fillColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillColor ), 1 );
+ }
+ rIn.Seek( nPos );
+}
+
+static void ApplyRectangularGradientAsBitmap( const SvxMSDffManager& rManager, SvStream& rIn, SfxItemSet& rSet, const std::vector< ShadeColor >& rShadeColors, const DffObjData& rObjData, Degree100 nFix16Angle )
+{
+ Size aBitmapSizePixel( static_cast< sal_Int32 >( ( rObjData.aBoundRect.GetWidth() / 2540.0 ) * 90.0 ), // we will create a bitmap with 90 dpi
+ static_cast< sal_Int32 >( ( rObjData.aBoundRect.GetHeight() / 2540.0 ) * 90.0 ) );
+ if (aBitmapSizePixel.IsEmpty() || aBitmapSizePixel.Width() > 1024 || aBitmapSizePixel.Height() > 1024)
+ return;
+
+ double fFocusX = rManager.GetPropertyValue( DFF_Prop_fillToRight, 0 ) / 65536.0;
+ double fFocusY = rManager.GetPropertyValue( DFF_Prop_fillToBottom, 0 ) / 65536.0;
+
+ vcl::bitmap::RawBitmap aBitmap(aBitmapSizePixel, 24);
+
+ for ( tools::Long nY = 0; nY < aBitmapSizePixel.Height(); nY++ )
+ {
+ for ( tools::Long nX = 0; nX < aBitmapSizePixel.Width(); nX++ )
+ {
+ double fX = static_cast< double >( nX ) / aBitmapSizePixel.Width();
+ double fY = static_cast< double >( nY ) / aBitmapSizePixel.Height();
+
+ double fD, fDist;
+ if ( fX < fFocusX )
+ {
+ if ( fY < fFocusY )
+ {
+ if ( fX > fY )
+ {
+ fDist = fY;
+ fD = fFocusY;
+ }
+ else
+ {
+ fDist = fX;
+ fD = fFocusX;
+ }
+ }
+ else
+ {
+ if ( fX > ( 1 - fY ) )
+ {
+ fDist = 1 - fY;
+ fD = 1 - fFocusY;
+ }
+ else
+ {
+ fDist = fX;
+ fD = fFocusX;
+ }
+ }
+ }
+ else
+ {
+ if ( fY < fFocusY )
+ {
+ if ( ( 1 - fX ) > fY )
+ {
+ fDist = fY;
+ fD = fFocusY;
+ }
+ else
+ {
+ fDist = 1 - fX;
+ fD = 1 - fFocusX;
+ }
+ }
+ else
+ {
+ if ( ( 1 - fX ) > ( 1 - fY ) )
+ {
+ fDist = 1 - fY;
+ fD = 1 - fFocusY;
+ }
+ else
+ {
+ fDist = 1 - fX;
+ fD = 1 - fFocusX;
+ }
+ }
+ }
+ if ( fD != 0.0 )
+ fDist /= fD;
+
+ double fA = 0.0;
+ Color aColorA = rShadeColors.front().aColor;
+ double fB = 1.0;
+ Color aColorB( aColorA );
+ for ( const auto& rShadeColor : rShadeColors )
+ {
+ if ( fA <= rShadeColor.fDist && rShadeColor.fDist <= fDist )
+ {
+ fA = rShadeColor.fDist;
+ aColorA = rShadeColor.aColor;
+ }
+ if ( fDist < rShadeColor.fDist && rShadeColor.fDist <= fB )
+ {
+ fB = rShadeColor.fDist;
+ aColorB = rShadeColor.aColor;
+ }
+ }
+ double fRed = aColorA.GetRed(), fGreen = aColorA.GetGreen(), fBlue = aColorA.GetBlue();
+ double fD1 = fB - fA;
+ if ( fD1 != 0.0 )
+ {
+ fRed += ( ( ( fDist - fA ) * ( aColorB.GetRed() - aColorA.GetRed() ) ) / fD1 ); // + aQuantErrCurrScan[ nX ].fRed;
+ fGreen += ( ( ( fDist - fA ) * ( aColorB.GetGreen() - aColorA.GetGreen() ) ) / fD1 ); // + aQuantErrCurrScan[ nX ].fGreen;
+ fBlue += ( ( ( fDist - fA ) * ( aColorB.GetBlue() - aColorA.GetBlue() ) ) / fD1 ); // + aQuantErrCurrScan[ nX ].fBlue;
+ }
+ sal_Int16 nRed = static_cast< sal_Int16 >( fRed + 0.5 );
+ sal_Int16 nGreen = static_cast< sal_Int16 >( fGreen + 0.5 );
+ sal_Int16 nBlue = static_cast< sal_Int16 >( fBlue + 0.5 );
+ if ( nRed < 0 )
+ nRed = 0;
+ if ( nRed > 255 )
+ nRed = 255;
+ if ( nGreen < 0 )
+ nGreen = 0;
+ if ( nGreen > 255 )
+ nGreen = 255;
+ if ( nBlue < 0 )
+ nBlue = 0;
+ if ( nBlue > 255 )
+ nBlue = 255;
+
+ aBitmap.SetPixel(nY, nX, Color(static_cast<sal_Int8>(nRed), static_cast<sal_Int8>(nGreen), static_cast<sal_Int8>(nBlue)));
+ }
+ }
+ BitmapEx aBitmapEx = vcl::bitmap::CreateFromData( std::move(aBitmap) );
+
+ if ( nFix16Angle )
+ {
+ bool bRotateWithShape = true; // sal_True seems to be default
+ sal_uInt64 nPos = rIn.Tell();
+ if ( const_cast< SvxMSDffManager& >( rManager ).maShapeRecords.SeekToContent( rIn, DFF_msofbtUDefProp, SEEK_FROM_CURRENT_AND_RESTART ) )
+ {
+ const_cast< SvxMSDffManager& >( rManager ).maShapeRecords.Current()->SeekToBegOfRecord( rIn );
+ DffPropertyReader aSecPropSet( rManager );
+ aSecPropSet.ReadPropSet( rIn, nullptr );
+ sal_Int32 nSecFillProperties = aSecPropSet.GetPropertyValue( DFF_Prop_fNoFillHitTest, 0x200020 );
+ bRotateWithShape = ( nSecFillProperties & 0x0020 );
+ }
+ rIn.Seek( nPos );
+ if ( bRotateWithShape )
+ {
+ // convert from 100th to 10th degrees
+ aBitmapEx.Rotate( to<Degree10>(nFix16Angle), rShadeColors[ 0 ].aColor );
+
+ BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE;
+ if ( rObjData.nSpFlags & ShapeFlag::FlipV )
+ nMirrorFlags |= BmpMirrorFlags::Vertical;
+ if ( rObjData.nSpFlags & ShapeFlag::FlipH )
+ nMirrorFlags |= BmpMirrorFlags::Horizontal;
+ if ( nMirrorFlags != BmpMirrorFlags::NONE )
+ aBitmapEx.Mirror( nMirrorFlags );
+ }
+ }
+
+ rSet.Put(XFillBmpTileItem(false));
+ rSet.Put(XFillBitmapItem(OUString(), Graphic(aBitmapEx)));
+}
+
+void DffPropertyReader::ApplyFillAttributes( SvStream& rIn, SfxItemSet& rSet, const DffObjData& rObjData ) const
+{
+ sal_uInt32 nFillFlags(GetPropertyValue( DFF_Prop_fNoFillHitTest, 0 ));
+
+ std::vector< ShadeColor > aShadeColors;
+ GetShadeColors( rManager, *this, rIn, aShadeColors );
+
+ if(!IsHardAttribute( DFF_Prop_fFilled ) && !IsCustomShapeFilledByDefault( rObjData.eShapeType ))
+ {
+ nFillFlags &= ~0x10;
+ }
+
+ if ( nFillFlags & 0x10 )
+ {
+ auto eMSO_FillType = GetPropertyValue(DFF_Prop_fillType, mso_fillSolid);
+ drawing::FillStyle eXFill = drawing::FillStyle_NONE;
+ switch( eMSO_FillType )
+ {
+ case mso_fillSolid : // Fill with a solid color
+ eXFill = drawing::FillStyle_SOLID;
+ break;
+ case mso_fillPattern : // Fill with a pattern (bitmap)
+ case mso_fillTexture : // A texture (pattern with its own color map)
+ case mso_fillPicture : // Center a picture in the shape
+ eXFill = drawing::FillStyle_BITMAP;
+ break;
+ case mso_fillShadeCenter : // Shade from bounding rectangle to end point
+ {
+ //If it is imported as a bitmap, it will not work well with transparency especially 100
+ //But the gradient look well comparing with imported as gradient. And rotate with shape
+ //also works better. So here just keep it.
+ if ( rObjData.aBoundRect.IsEmpty() )// size of object needed to be able
+ eXFill = drawing::FillStyle_GRADIENT; // to create a bitmap substitution
+ else
+ eXFill = drawing::FillStyle_BITMAP;
+ }
+ break;
+ case mso_fillShade : // Shade from start to end points
+ case mso_fillShadeShape : // Shade from shape outline to end point
+ case mso_fillShadeScale : // Similar to mso_fillShade, but the fillAngle
+ case mso_fillShadeTitle : // special type - shade to title --- for PP
+ eXFill = drawing::FillStyle_GRADIENT;
+ break;
+// case mso_fillBackground : // Use the background fill color/pattern
+ default: break;
+ }
+ rSet.Put( XFillStyleItem( eXFill ) );
+
+ double dTrans = 1.0;
+ double dBackTrans = 1.0;
+ if (IsProperty(DFF_Prop_fillOpacity))
+ {
+ dTrans = GetPropertyValue(DFF_Prop_fillOpacity, 0) / 65536.0;
+ if ( eXFill != drawing::FillStyle_GRADIENT )
+ {
+ dTrans = dTrans * 100;
+ rSet.Put(XFillTransparenceItem(
+ sal_uInt16(100 - ::rtl::math::round(dTrans))));
+ }
+ }
+
+ if ( IsProperty(DFF_Prop_fillBackOpacity) )
+ dBackTrans = GetPropertyValue(DFF_Prop_fillBackOpacity, 0) / 65536.0;
+
+ if ( ( eMSO_FillType == mso_fillShadeCenter ) && ( eXFill == drawing::FillStyle_BITMAP ) )
+ {
+ ApplyRectangularGradientAsBitmap( rManager, rIn, rSet, aShadeColors, rObjData, mnFix16Angle );
+ }
+ else if ( eXFill == drawing::FillStyle_GRADIENT )
+ {
+ ImportGradientColor ( rSet, eMSO_FillType, dTrans , dBackTrans );
+ }
+ else if ( eXFill == drawing::FillStyle_BITMAP )
+ {
+ if( IsProperty( DFF_Prop_fillBlip ) )
+ {
+ Graphic aGraf;
+ // first try to get BLIP from cache
+ bool bOK = const_cast<SvxMSDffManager&>(rManager).GetBLIP( GetPropertyValue( DFF_Prop_fillBlip, 0 ), aGraf );
+ // then try directly from stream (i.e. Excel chart hatches/bitmaps)
+ if ( !bOK )
+ bOK = SeekToContent( DFF_Prop_fillBlip, rIn ) && SvxMSDffManager::GetBLIPDirect( rIn, aGraf );
+ if ( bOK )
+ {
+ if ( eMSO_FillType == mso_fillPattern )
+ {
+ Bitmap aBmp( aGraf.GetBitmapEx().GetBitmap() );
+ if (aBmp.GetSizePixel().Width() == 8 &&
+ aBmp.GetSizePixel().Height() == 8 &&
+ aBmp.getPixelFormat() == vcl::PixelFormat::N1_BPP)
+ {
+ Color aCol1( COL_WHITE ), aCol2( COL_WHITE );
+
+ if ( IsProperty( DFF_Prop_fillColor ) )
+ aCol1 = rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillColor, 0 ), DFF_Prop_fillColor );
+
+ if ( IsProperty( DFF_Prop_fillBackColor ) )
+ aCol2 = rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillBackColor, 0 ), DFF_Prop_fillBackColor );
+
+ // Create a bitmap for the pattern with expected colors
+ vcl::bitmap::RawBitmap aResult(Size(8, 8), 24);
+ {
+ Bitmap::ScopedReadAccess pRead(aBmp);
+
+ for (tools::Long y = 0; y < aResult.Height(); ++y)
+ {
+ Scanline pScanlineRead = pRead->GetScanline( y );
+ for (tools::Long x = 0; x < aResult.Width(); ++x)
+ {
+ Color aReadColor;
+ if (pRead->HasPalette())
+ aReadColor = pRead->GetPaletteColor(pRead->GetIndexFromData(pScanlineRead, x));
+ else
+ aReadColor = pRead->GetPixelFromData(pScanlineRead, x);
+
+ if (aReadColor == Color(0))
+ aResult.SetPixel(y, x, aCol2);
+ else
+ aResult.SetPixel(y, x, aCol1);
+ }
+ }
+ }
+ aGraf = Graphic(vcl::bitmap::CreateFromData(std::move(aResult)));
+ }
+
+ rSet.Put(XFillBitmapItem(OUString(), aGraf));
+ }
+ else if ( eMSO_FillType == mso_fillTexture )
+ {
+ rSet.Put(XFillBmpTileItem(true));
+ rSet.Put(XFillBitmapItem(OUString(), aGraf));
+ rSet.Put(XFillBmpSizeXItem(GetPropertyValue(DFF_Prop_fillWidth, 0) / 360));
+ rSet.Put(XFillBmpSizeYItem(GetPropertyValue(DFF_Prop_fillHeight, 0) / 360));
+ rSet.Put(XFillBmpSizeLogItem(true));
+ }
+ else
+ {
+ rSet.Put(XFillBitmapItem(OUString(), aGraf));
+ rSet.Put(XFillBmpTileItem(false));
+ }
+ }
+ }
+ }
+ }
+ else
+ rSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) );
+}
+
+void DffPropertyReader::ApplyCustomShapeTextAttributes( SfxItemSet& rSet ) const
+{
+ bool bVerticalText = false;
+ sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 25 * 3600 ) / 360; // 0.25 cm (emu)
+ sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 25 * 3600 ) / 360; // 0.25 cm (emu)
+ sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 13 * 3600 ) / 360; // 0.13 cm (emu)
+ sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 13 * 3600 ) /360; // 0.13 cm (emu)
+
+ SdrTextVertAdjust eTVA;
+ SdrTextHorzAdjust eTHA;
+
+ if ( IsProperty( DFF_Prop_txflTextFlow ) )
+ {
+ auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF;
+ switch( eTextFlow )
+ {
+ case mso_txflTtoBA : /* #68110# */ // Top to Bottom @-font, oben -> unten
+ case mso_txflTtoBN : // Top to Bottom non-@, oben -> unten
+ case mso_txflVertN : // Vertical, non-@, oben -> unten
+ bVerticalText = true; // nTextRotationAngle += 27000;
+ break;
+ default: break;
+ }
+ }
+ sal_Int32 nFontDirection = GetPropertyValue( DFF_Prop_cdirFont, mso_cdir0 );
+ if ( ( nFontDirection == 1 ) || ( nFontDirection == 3 ) )
+ bVerticalText = !bVerticalText;
+
+ if ( bVerticalText )
+ {
+ eTHA = SDRTEXTHORZADJUST_CENTER;
+
+ // read text anchor
+ sal_uInt32 eTextAnchor = GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop );
+
+ switch( eTextAnchor )
+ {
+ case mso_anchorTop:
+ case mso_anchorTopCentered:
+ case mso_anchorTopBaseline:
+ case mso_anchorTopCenteredBaseline:
+ eTHA = SDRTEXTHORZADJUST_RIGHT;
+ break;
+
+ case mso_anchorMiddle :
+ case mso_anchorMiddleCentered:
+ eTHA = SDRTEXTHORZADJUST_CENTER;
+ break;
+
+ case mso_anchorBottom:
+ case mso_anchorBottomCentered:
+ case mso_anchorBottomBaseline:
+ case mso_anchorBottomCenteredBaseline:
+ eTHA = SDRTEXTHORZADJUST_LEFT;
+ break;
+ }
+ // if there is a 100% use of following attributes, the textbox can been aligned also in vertical direction
+ switch ( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ case mso_anchorTopCenteredBaseline:
+ case mso_anchorBottomCenteredBaseline:
+ eTVA = SDRTEXTVERTADJUST_CENTER;
+ break;
+
+ default :
+ eTVA = SDRTEXTVERTADJUST_TOP;
+ break;
+ }
+ }
+ else
+ {
+ eTVA = SDRTEXTVERTADJUST_CENTER;
+
+ // read text anchor
+ sal_uInt32 eTextAnchor = GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop );
+
+ switch( eTextAnchor )
+ {
+ case mso_anchorTop:
+ case mso_anchorTopCentered:
+ case mso_anchorTopBaseline:
+ case mso_anchorTopCenteredBaseline:
+ eTVA = SDRTEXTVERTADJUST_TOP;
+ break;
+
+ case mso_anchorMiddle :
+ case mso_anchorMiddleCentered:
+ eTVA = SDRTEXTVERTADJUST_CENTER;
+ break;
+
+ case mso_anchorBottom:
+ case mso_anchorBottomCentered:
+ case mso_anchorBottomBaseline:
+ case mso_anchorBottomCenteredBaseline:
+ eTVA = SDRTEXTVERTADJUST_BOTTOM;
+ break;
+ }
+ // if there is a 100% usage of following attributes, the textbox can be aligned also in horizontal direction
+ switch ( eTextAnchor )
+ {
+ case mso_anchorTopCentered :
+ case mso_anchorMiddleCentered :
+ case mso_anchorBottomCentered :
+ case mso_anchorTopCenteredBaseline:
+ case mso_anchorBottomCenteredBaseline:
+ eTHA = SDRTEXTHORZADJUST_CENTER; // the text has to be displayed using the full width;
+ break;
+
+ default :
+ eTHA = SDRTEXTHORZADJUST_LEFT;
+ break;
+ }
+ }
+ rSet.Put( SvxFrameDirectionItem( bVerticalText ? SvxFrameDirection::Vertical_RL_TB : SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) );
+
+ rSet.Put( SdrTextVertAdjustItem( eTVA ) );
+ rSet.Put( SdrTextHorzAdjustItem( eTHA ) );
+
+ rSet.Put( makeSdrTextLeftDistItem( nTextLeft ) );
+ rSet.Put( makeSdrTextRightDistItem( nTextRight ) );
+ rSet.Put( makeSdrTextUpperDistItem( nTextTop ) );
+ rSet.Put( makeSdrTextLowerDistItem( nTextBottom ) );
+
+ rSet.Put( makeSdrTextWordWrapItem( GetPropertyValue(DFF_Prop_WrapText, mso_wrapSquare) != mso_wrapNone ) );
+ rSet.Put( makeSdrTextAutoGrowHeightItem( ( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 2 ) != 0 ) );
+}
+
+void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxItemSet& rSet, const DffObjData& rObjData ) const
+{
+
+ sal_uInt32 nAdjustmentsWhichNeedsToBeConverted = 0;
+
+
+ // creating SdrCustomShapeGeometryItem
+
+ typedef std::vector< beans::PropertyValue > PropVec;
+
+ // aPropVec will be filled with all PropertyValues
+ PropVec aPropVec;
+ PropertyValue aProp;
+
+
+ // "Type" property, including the predefined CustomShape type name
+
+ aProp.Name = "Type";
+ aProp.Value <<= EnhancedCustomShapeTypeNames::Get( rObjData.eShapeType );
+ aPropVec.push_back( aProp );
+
+
+ // "ViewBox"
+
+
+ sal_Int32 nCoordWidth = 21600; // needed to replace handle type center with absolute value
+ sal_Int32 nCoordHeight= 21600;
+ if ( IsProperty( DFF_Prop_geoLeft ) || IsProperty( DFF_Prop_geoTop ) || IsProperty( DFF_Prop_geoRight ) || IsProperty( DFF_Prop_geoBottom ) )
+ {
+ css::awt::Rectangle aViewBox;
+ aViewBox.X = GetPropertyValue( DFF_Prop_geoLeft, 0 );
+ aViewBox.Y = GetPropertyValue( DFF_Prop_geoTop, 0 );
+ aViewBox.Width = nCoordWidth = o3tl::saturating_sub<sal_Int32>(GetPropertyValue(DFF_Prop_geoRight, 21600), aViewBox.X);
+ aViewBox.Height = nCoordHeight = o3tl::saturating_sub<sal_Int32>(GetPropertyValue(DFF_Prop_geoBottom, 21600), aViewBox.Y);
+ aProp.Name = "ViewBox";
+ aProp.Value <<= aViewBox;
+ aPropVec.push_back( aProp );
+ }
+
+ // TextRotateAngle
+
+ if ( IsProperty( DFF_Prop_txflTextFlow ) || IsProperty( DFF_Prop_cdirFont ) )
+ {
+ sal_Int32 nTextRotateAngle = 0;
+ auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF;
+
+ if ( eTextFlow == mso_txflBtoT ) // Bottom to Top non-@
+ nTextRotateAngle += 90;
+ switch( GetPropertyValue( DFF_Prop_cdirFont, mso_cdir0 ) ) // SJ: mso_cdir90 and mso_cdir270 will be simulated by
+ { // activating vertical writing for the text objects
+ case mso_cdir90 :
+ {
+ if ( eTextFlow == mso_txflTtoBA )
+ nTextRotateAngle -= 180;
+ }
+ break;
+ case mso_cdir180: nTextRotateAngle -= 180; break;
+ case mso_cdir270:
+ {
+ if ( eTextFlow != mso_txflTtoBA )
+ nTextRotateAngle -= 180;
+ }
+ break;
+ default: break;
+ }
+ if ( nTextRotateAngle )
+ {
+ double fTextRotateAngle = nTextRotateAngle;
+ aProp.Name = "TextRotateAngle";
+ aProp.Value <<= fTextRotateAngle;
+ aPropVec.push_back( aProp );
+ }
+ }
+
+ // "Extrusion" PropertySequence element
+
+ bool bExtrusionOn = ( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 8 ) != 0;
+ if ( bExtrusionOn )
+ {
+ PropVec aExtrusionPropVec;
+
+ // "Extrusion"
+ aProp.Name = "Extrusion";
+ aProp.Value <<= bExtrusionOn;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "Brightness"
+ // MS Office default 0x00004E20 16.16 FixedPoint, 20000/65536=0.30517, ODF default 33%.
+ // Thus must set value even if default.
+ double fBrightness = 20000.0;
+ if ( IsProperty( DFF_Prop_c3DAmbientIntensity ) )
+ {
+ // Value must be in range 0.0 to 1.0 in MS Office binary specification, but larger
+ // values are in fact interpreted.
+ fBrightness = GetPropertyValue( DFF_Prop_c3DAmbientIntensity, 0 );
+ }
+ fBrightness /= 655.36;
+ aProp.Name = "Brightness";
+ aProp.Value <<= fBrightness;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "Depth" in 1/100mm
+ if ( IsProperty( DFF_Prop_c3DExtrudeBackward ) || IsProperty( DFF_Prop_c3DExtrudeForward ) )
+ {
+ double fBackDepth = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DExtrudeBackward, 1270 * 360 ))) / 360.0;
+ double fForeDepth = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DExtrudeForward, 0 ))) / 360.0;
+ double fDepth = fBackDepth + fForeDepth;
+ double fFraction = fDepth != 0.0 ? fForeDepth / fDepth : 0;
+ EnhancedCustomShapeParameterPair aDepthParaPair;
+ aDepthParaPair.First.Value <<= fDepth;
+ aDepthParaPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aDepthParaPair.Second.Value <<= fFraction;
+ aDepthParaPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aProp.Name = "Depth";
+ aProp.Value <<= aDepthParaPair;
+ aExtrusionPropVec.push_back( aProp );
+ }
+ // "Diffusion"
+ // ODF default is 0%, MS Office default is 100%. Thus must set value even if default.
+ double fDiffusion = 100;
+ if ( IsProperty( DFF_Prop_c3DDiffuseAmt ) )
+ {
+ fDiffusion = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DDiffuseAmt, 0 ));
+ fDiffusion /= 655.36;
+ }
+ aProp.Name = "Diffusion";
+ aProp.Value <<= fDiffusion;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "NumberOfLineSegments"
+ if ( IsProperty( DFF_Prop_c3DTolerance ) )
+ {
+ aProp.Name = "NumberOfLineSegments";
+ aProp.Value <<= static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DTolerance, 0 ));
+ aExtrusionPropVec.push_back( aProp );
+ }
+ // "LightFace"
+ bool bExtrusionLightFace = ( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 1 ) != 0;
+ aProp.Name = "LightFace";
+ aProp.Value <<= bExtrusionLightFace;
+ aExtrusionPropVec.push_back( aProp );
+ // "FirstLightHarsh"
+ bool bExtrusionFirstLightHarsh = ( GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 2 ) != 0;
+ aProp.Name = "FirstLightHarsh";
+ aProp.Value <<= bExtrusionFirstLightHarsh;
+ aExtrusionPropVec.push_back( aProp );
+ // "SecondLightHarsh"
+ bool bExtrusionSecondLightHarsh = ( GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 1 ) != 0;
+ aProp.Name = "SecondLightHarsh";
+ aProp.Value <<= bExtrusionSecondLightHarsh;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "FirstLightLevel"
+ // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%.
+ // Thus must set value even if default.
+ double fFirstLightLevel = 38000.0;
+ if ( IsProperty( DFF_Prop_c3DKeyIntensity ) )
+ {
+ // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here.
+ fFirstLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyIntensity, 0 ));
+ }
+ fFirstLightLevel /= 655.36;
+ aProp.Name = "FirstLightLevel";
+ aProp.Value <<= fFirstLightLevel;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "SecondLightLevel"
+ // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%.
+ // Thus must set value even if default.
+ double fSecondLightLevel = 38000.0;
+ if ( IsProperty( DFF_Prop_c3DFillIntensity ) )
+ {
+ // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here.
+ fSecondLightLevel = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillIntensity, 0 ));
+ }
+ fSecondLightLevel /= 655.36;
+ aProp.Name = "SecondLightLevel";
+ aProp.Value <<= fSecondLightLevel;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "FirstLightDirection"
+ if ( IsProperty( DFF_Prop_c3DKeyX ) || IsProperty( DFF_Prop_c3DKeyY ) || IsProperty( DFF_Prop_c3DKeyZ ) )
+ {
+ double fLightX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyX, 50000 )));
+ double fLightY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyY, 0 )));
+ double fLightZ = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DKeyZ, 10000 )));
+ css::drawing::Direction3D aExtrusionFirstLightDirection( fLightX, fLightY, fLightZ );
+ aProp.Name = "FirstLightDirection";
+ aProp.Value <<= aExtrusionFirstLightDirection;
+ aExtrusionPropVec.push_back( aProp );
+ }
+ // "SecondLightDirection"
+ if ( IsProperty( DFF_Prop_c3DFillX ) || IsProperty( DFF_Prop_c3DFillY ) || IsProperty( DFF_Prop_c3DFillZ ) )
+ {
+ double fLight2X = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillX, sal_uInt32(-50000) )));
+ double fLight2Y = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillY, 0 )));
+ double fLight2Z = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DFillZ, 10000 )));
+ css::drawing::Direction3D aExtrusionSecondLightDirection( fLight2X, fLight2Y, fLight2Z );
+ aProp.Name = "SecondLightDirection";
+ aProp.Value <<= aExtrusionSecondLightDirection;
+ aExtrusionPropVec.push_back( aProp );
+ }
+
+ // "Metal"
+ bool bExtrusionMetal = ( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 4 ) != 0;
+ aProp.Name = "Metal";
+ aProp.Value <<= bExtrusionMetal;
+ aExtrusionPropVec.push_back( aProp );
+ aProp.Name = "MetalType";
+ aProp.Value <<= css::drawing::EnhancedCustomShapeMetalType::MetalMSCompatible;
+ aExtrusionPropVec.push_back(aProp);
+
+ // "ShadeMode"
+ if ( IsProperty( DFF_Prop_c3DRenderMode ) )
+ {
+ sal_uInt32 nExtrusionRenderMode = GetPropertyValue( DFF_Prop_c3DRenderMode, 0 );
+ css::drawing::ShadeMode eExtrusionShadeMode( css::drawing::ShadeMode_FLAT );
+ if ( nExtrusionRenderMode == mso_Wireframe )
+ eExtrusionShadeMode = css::drawing::ShadeMode_DRAFT;
+
+ aProp.Name = "ShadeMode";
+ aProp.Value <<= eExtrusionShadeMode;
+ aExtrusionPropVec.push_back( aProp );
+ }
+ // "RotateAngle" in Degree
+ if ( IsProperty( DFF_Prop_c3DXRotationAngle ) || IsProperty( DFF_Prop_c3DYRotationAngle ) )
+ {
+ double fAngleX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXRotationAngle, 0 ))) / 65536.0;
+ double fAngleY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DYRotationAngle, 0 ))) / 65536.0;
+ EnhancedCustomShapeParameterPair aRotateAnglePair;
+ aRotateAnglePair.First.Value <<= fAngleX;
+ aRotateAnglePair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aRotateAnglePair.Second.Value <<= fAngleY;
+ aRotateAnglePair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aProp.Name = "RotateAngle";
+ aProp.Value <<= aRotateAnglePair;
+ aExtrusionPropVec.push_back( aProp );
+ }
+
+ // "AutoRotationCenter"
+ if ( ( GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 8 ) == 0 )
+ {
+ // "RotationCenter"
+ if ( IsProperty( DFF_Prop_c3DRotationCenterX ) || IsProperty( DFF_Prop_c3DRotationCenterY ) || IsProperty( DFF_Prop_c3DRotationCenterZ ) )
+ {
+ // tdf#145904 X- and Y-component is fraction, Z-component in EMU
+ css::drawing::Direction3D aRotationCenter(
+ static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DRotationCenterX, 0 ))) / 65536.0,
+ static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DRotationCenterY, 0 ))) / 65536.0,
+ static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DRotationCenterZ, 0 ))) / 360.0 );
+
+ aProp.Name = "RotationCenter";
+ aProp.Value <<= aRotationCenter;
+ aExtrusionPropVec.push_back( aProp );
+ }
+ }
+ // "Shininess"
+ // MS Office default 5, ODF default 50%.
+ if ( IsProperty( DFF_Prop_c3DShininess ) )
+ {
+ double fShininess = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DShininess, 0 ));
+ fShininess *= 10.0; // error in [MS ODRAW] (2021), type is not FixedPoint but long.
+ aProp.Name = "Shininess";
+ aProp.Value <<= fShininess;
+ aExtrusionPropVec.push_back( aProp );
+ }
+
+ // "Skew"
+ // MS Office angle file value is 16.16 FixedPoint, default 0xFF790000,
+ // -8847360/65536=-135, ODF default 45. Thus must set value even if default.
+ double fSkewAngle = -135.0;
+ // MS Office amount file value is signed integer in range 0xFFFFFF9C to 0x00000064,
+ // default 0x00000032, ODF default 50.0
+ double fSkewAmount = 50.0;
+ if ( IsProperty( DFF_Prop_c3DSkewAmount ) || IsProperty( DFF_Prop_c3DSkewAngle ) )
+ {
+ fSkewAmount = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAmount, 50 ));
+ fSkewAngle = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSkewAngle, sal::static_int_cast< sal_uInt32 >(-135 * 65536) ));
+ fSkewAngle /= 65536.0;
+ }
+ EnhancedCustomShapeParameterPair aSkewPair;
+ aSkewPair.First.Value <<= fSkewAmount;
+ aSkewPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aSkewPair.Second.Value <<= fSkewAngle;
+ aSkewPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aProp.Name = "Skew";
+ aProp.Value <<= aSkewPair;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "Specularity"
+ // Type Fixed point 16.16, percent in API
+ if ( IsProperty( DFF_Prop_c3DSpecularAmt ) )
+ {
+ double fSpecularity = static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DSpecularAmt, 0 ));
+ fSpecularity /= 655.36;
+ aProp.Name = "Specularity";
+ aProp.Value <<= fSpecularity;
+ aExtrusionPropVec.push_back( aProp );
+ }
+ // "ProjectionMode"
+ ProjectionMode eProjectionMode = (GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 4) ? ProjectionMode_PARALLEL : ProjectionMode_PERSPECTIVE;
+ aProp.Name = "ProjectionMode";
+ aProp.Value <<= eProjectionMode;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "ViewPoint" in 1/100mm
+ // MS Office default 1250000 EMU=3472.222 Hmm, ODF default 3.5cm
+ // Thus must set value even if default.
+ double fViewX = 1250000.0 / 360.0;
+ double fViewY = -1250000.0 / 360.0;;
+ double fViewZ = 9000000.0 / 360.0;
+ if ( IsProperty( DFF_Prop_c3DXViewpoint ) || IsProperty( DFF_Prop_c3DYViewpoint ) || IsProperty( DFF_Prop_c3DZViewpoint ) )
+ {
+ fViewX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DXViewpoint, 1250000 ))) / 360.0;
+ fViewY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DYViewpoint, sal_uInt32(-1250000) )))/ 360.0;
+ fViewZ = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DZViewpoint, 9000000 ))) / 360.0;
+ }
+ css::drawing::Position3D aExtrusionViewPoint( fViewX, fViewY, fViewZ );
+ aProp.Name = "ViewPoint";
+ aProp.Value <<= aExtrusionViewPoint;
+ aExtrusionPropVec.push_back( aProp );
+
+ // "Origin"
+ if ( IsProperty( DFF_Prop_c3DOriginX ) || IsProperty( DFF_Prop_c3DOriginY ) )
+ {
+ double fOriginX = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DOriginX, 32768 )));
+ double fOriginY = static_cast<double>(static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_c3DOriginY, sal_uInt32(-32768) )));
+ fOriginX /= 65536;
+ fOriginY /= 65536;
+ EnhancedCustomShapeParameterPair aOriginPair;
+ aOriginPair.First.Value <<= fOriginX;
+ aOriginPair.First.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aOriginPair.Second.Value <<= fOriginY;
+ aOriginPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL;
+ aProp.Name = "Origin";
+ aProp.Value <<= aOriginPair;
+ aExtrusionPropVec.push_back( aProp );
+ }
+ // "ExtrusionColor"
+ bool bExtrusionColor = IsProperty( DFF_Prop_c3DExtrusionColor ); // ( GetPropertyValue( DFF_Prop_fc3DLightFace ) & 2 ) != 0;
+ aProp.Name = "Color";
+ aProp.Value <<= bExtrusionColor;
+ aExtrusionPropVec.push_back( aProp );
+ if ( IsProperty( DFF_Prop_c3DExtrusionColor ) )
+ rSet.Put( XSecondaryFillColorItem( OUString(), rManager.MSO_CLR_ToColor(
+ GetPropertyValue( DFF_Prop_c3DExtrusionColor, 0 ), DFF_Prop_c3DExtrusionColor ) ) );
+ // pushing the whole Extrusion element
+ aProp.Name = "Extrusion";
+ aProp.Value <<= comphelper::containerToSequence(aExtrusionPropVec);
+ aPropVec.push_back( aProp );
+ }
+
+
+ // "Equations" PropertySequence element
+
+ if ( IsProperty( DFF_Prop_pFormulas ) )
+ {
+ sal_uInt16 nNumElem = 0;
+
+ if ( SeekToContent( DFF_Prop_pFormulas, rIn ) )
+ {
+ sal_uInt16 nNumElemMem = 0;
+ sal_uInt16 nElemSize = 8;
+ rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemMem ).ReadUInt16( nElemSize );
+ }
+ if ( nNumElem <= 128 )
+ {
+ uno::Sequence< OUString > aEquations( nNumElem );
+ for ( auto& rEquation : asNonConstRange(aEquations) )
+ {
+ sal_Int16 nP1(0), nP2(0), nP3(0);
+ sal_uInt16 nFlags(0);
+ rIn.ReadUInt16( nFlags ).ReadInt16( nP1 ).ReadInt16( nP2 ).ReadInt16( nP3 );
+ rEquation = EnhancedCustomShape2d::GetEquation( nFlags, nP1, nP2, nP3 );
+ }
+ // pushing the whole Equations element
+ aProp.Name = "Equations";
+ aProp.Value <<= aEquations;
+ aPropVec.push_back( aProp );
+ }
+ }
+
+
+ // "Handles" PropertySequence element
+
+ if ( IsProperty( DFF_Prop_Handles ) )
+ {
+ sal_uInt16 nNumElem = 0;
+ sal_uInt16 nElemSize = 36;
+
+ if ( SeekToContent( DFF_Prop_Handles, rIn ) )
+ {
+ sal_uInt16 nNumElemMem = 0;
+ rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemMem ).ReadUInt16( nElemSize );
+ }
+ bool bImport = false;
+ if (nElemSize == 36)
+ {
+ //sanity check that the stream is long enough to fulfill nNumElem * nElemSize;
+ bImport = rIn.remainingSize() / nElemSize >= nNumElem;
+ }
+ if (bImport)
+ {
+ uno::Sequence< beans::PropertyValues > aHandles( nNumElem );
+ auto aHandlesRange = asNonConstRange(aHandles);
+ for (sal_uInt32 i = 0; i < nNumElem; ++i)
+ {
+ PropVec aHandlePropVec;
+ sal_uInt32 nFlagsTmp(0);
+ sal_Int32 nPositionX(0), nPositionY(0), nCenterX(0), nCenterY(0), nRangeXMin(0), nRangeXMax(0), nRangeYMin(0), nRangeYMax(0);
+ rIn.ReadUInt32( nFlagsTmp )
+ .ReadInt32( nPositionX )
+ .ReadInt32( nPositionY )
+ .ReadInt32( nCenterX )
+ .ReadInt32( nCenterY )
+ .ReadInt32( nRangeXMin )
+ .ReadInt32( nRangeXMax )
+ .ReadInt32( nRangeYMin )
+ .ReadInt32( nRangeYMax );
+ SvxMSDffHandleFlags nFlags = static_cast<SvxMSDffHandleFlags>(nFlagsTmp);
+ if ( nPositionX == 2 ) // replacing center position with absolute value
+ nPositionX = nCoordWidth / 2;
+ if ( nPositionY == 2 )
+ nPositionY = nCoordHeight / 2;
+ EnhancedCustomShapeParameterPair aPosition;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, nPositionX, true, true );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, nPositionY, true, false );
+ aProp.Name = "Position";
+ aProp.Value <<= aPosition;
+ aHandlePropVec.push_back( aProp );
+
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X )
+ {
+ aProp.Name = "MirroredX";
+ aProp.Value <<= true;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y )
+ {
+ aProp.Name = "MirroredY";
+ aProp.Value <<= true;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( nFlags & SvxMSDffHandleFlags::SWITCHED )
+ {
+ aProp.Name = "Switched";
+ aProp.Value <<= true;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( nFlags & SvxMSDffHandleFlags::POLAR )
+ {
+ if ( nCenterX == 2 )
+ nCenterX = nCoordWidth / 2;
+ if ( nCenterY == 2 )
+ nCenterY = nCoordHeight / 2;
+ if ((nPositionY >= 0x256 || nPositionY <= 0x107) && i < sizeof(sal_uInt32) * 8) // position y
+ nAdjustmentsWhichNeedsToBeConverted |= ( 1U << i );
+ EnhancedCustomShapeParameterPair aPolar;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPolar.First, nCenterX, bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPolar.Second, nCenterY, bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
+ aProp.Name = "Polar";
+ aProp.Value <<= aPolar;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( nFlags & SvxMSDffHandleFlags::MAP )
+ {
+ if ( nCenterX == 2 )
+ nCenterX = nCoordWidth / 2;
+ if ( nCenterY == 2 )
+ nCenterY = nCoordHeight / 2;
+ EnhancedCustomShapeParameterPair aMap;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aMap.First, nCenterX, bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aMap.Second, nCenterY, bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false );
+ aProp.Name = "Map";
+ aProp.Value <<= aMap;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( nFlags & SvxMSDffHandleFlags::RANGE )
+ {
+ if ( static_cast<sal_uInt32>(nRangeXMin) != 0x80000000 )
+ {
+ if ( nRangeXMin == 2 )
+ nRangeXMin = nCoordWidth / 2;
+ EnhancedCustomShapeParameter aRangeXMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, nRangeXMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
+ aProp.Name = "RangeXMinimum";
+ aProp.Value <<= aRangeXMinimum;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( static_cast<sal_uInt32>(nRangeXMax) != 0x7fffffff )
+ {
+ if ( nRangeXMax == 2 )
+ nRangeXMax = nCoordWidth / 2;
+ EnhancedCustomShapeParameter aRangeXMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, nRangeXMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
+ aProp.Name = "RangeXMaximum";
+ aProp.Value <<= aRangeXMaximum;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( static_cast<sal_uInt32>(nRangeYMin) != 0x80000000 )
+ {
+ if ( nRangeYMin == 2 )
+ nRangeYMin = nCoordHeight / 2;
+ EnhancedCustomShapeParameter aRangeYMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, nRangeYMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true );
+ aProp.Name = "RangeYMinimum";
+ aProp.Value <<= aRangeYMinimum;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( static_cast<sal_uInt32>(nRangeYMax) != 0x7fffffff )
+ {
+ if ( nRangeYMax == 2 )
+ nRangeYMax = nCoordHeight / 2;
+ EnhancedCustomShapeParameter aRangeYMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, nRangeYMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false );
+ aProp.Name = "RangeYMaximum";
+ aProp.Value <<= aRangeYMaximum;
+ aHandlePropVec.push_back( aProp );
+ }
+ }
+ if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE )
+ {
+ if ( static_cast<sal_uInt32>(nRangeXMin) != 0x7fffffff )
+ {
+ if ( nRangeXMin == 2 )
+ nRangeXMin = nCoordWidth / 2;
+ EnhancedCustomShapeParameter aRadiusRangeMinimum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, nRangeXMin,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true );
+ aProp.Name = "RadiusRangeMinimum";
+ aProp.Value <<= aRadiusRangeMinimum;
+ aHandlePropVec.push_back( aProp );
+ }
+ if ( static_cast<sal_uInt32>(nRangeXMax) != 0x80000000 )
+ {
+ if ( nRangeXMax == 2 )
+ nRangeXMax = nCoordWidth / 2;
+ EnhancedCustomShapeParameter aRadiusRangeMaximum;
+ EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, nRangeXMax,
+ bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false );
+ aProp.Name = "RadiusRangeMaximum";
+ aProp.Value <<= aRadiusRangeMaximum;
+ aHandlePropVec.push_back( aProp );
+ }
+ }
+ if ( !aHandlePropVec.empty() )
+ {
+ aHandlesRange[ i ] = comphelper::containerToSequence(aHandlePropVec);
+ }
+ }
+ // pushing the whole Handles element
+ aProp.Name = "Handles";
+ aProp.Value <<= aHandles;
+ aPropVec.push_back( aProp );
+ }
+ }
+ else
+ {
+ const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( rObjData.eShapeType );
+ if ( pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles )
+ {
+ sal_uInt32 i, nCnt = pDefCustomShape->nHandles;
+ const SvxMSDffHandle* pData = pDefCustomShape->pHandles;
+ for ( i = 0; i < nCnt; i++, pData++ )
+ {
+ if ( pData->nFlags & SvxMSDffHandleFlags::POLAR )
+ {
+ if ( ( pData->nPositionY >= 0x256 ) || ( pData->nPositionY <= 0x107 ) )
+ nAdjustmentsWhichNeedsToBeConverted |= ( 1U << i );
+ }
+ }
+ }
+ }
+
+ // "Path" PropertySequence element
+
+ {
+ PropVec aPathPropVec;
+
+ // "Path/ExtrusionAllowed"
+ if ( IsHardAttribute( DFF_Prop_f3DOK ) )
+ {
+ bool bExtrusionAllowed = ( GetPropertyValue( DFF_Prop_fFillOK, 0 ) & 16 ) != 0;
+ aProp.Name = "ExtrusionAllowed";
+ aProp.Value <<= bExtrusionAllowed;
+ aPathPropVec.push_back( aProp );
+ }
+ // "Path/ConcentricGradientFillAllowed"
+ if ( IsHardAttribute( DFF_Prop_fFillShadeShapeOK ) )
+ {
+ bool bConcentricGradientFillAllowed = ( GetPropertyValue( DFF_Prop_fFillOK, 0 ) & 2 ) != 0;
+ aProp.Name = "ConcentricGradientFillAllowed";
+ aProp.Value <<= bConcentricGradientFillAllowed;
+ aPathPropVec.push_back( aProp );
+ }
+ // "Path/TextPathAllowed"
+ if ( IsHardAttribute( DFF_Prop_fGtextOK ) || ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x4000 ) )
+ {
+ bool bTextPathAllowed = ( GetPropertyValue( DFF_Prop_fFillOK, 0 ) & 4 ) != 0;
+ aProp.Name = "TextPathAllowed";
+ aProp.Value <<= bTextPathAllowed;
+ aPathPropVec.push_back( aProp );
+ }
+ // Path/Coordinates
+ if ( IsProperty( DFF_Prop_pVertices ) )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aCoordinates;
+ sal_uInt16 nNumElemVert = 0;
+ sal_uInt16 nElemSizeVert = 8;
+
+ if ( SeekToContent( DFF_Prop_pVertices, rIn ) )
+ {
+ sal_uInt16 nNumElemMemVert = 0;
+ rIn.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
+ // If this value is 0xFFF0 then this record is an array of truncated 8 byte elements. Only the 4
+ // low-order bytes are recorded
+ if (nElemSizeVert == 0xFFF0)
+ nElemSizeVert = 4;
+ }
+ //sanity check that the stream is long enough to fulfill nNumElem * nElemSize;
+ bool bImport = nElemSizeVert && (rIn.remainingSize() / nElemSizeVert >= nNumElemVert);
+ if (bImport)
+ {
+ aCoordinates.realloc( nNumElemVert );
+ for (auto& rCoordinate : asNonConstRange(aCoordinates))
+ {
+ sal_Int32 nX(0), nY(0);
+
+ if ( nElemSizeVert == 8 )
+ {
+ rIn.ReadInt32( nX )
+ .ReadInt32( nY );
+ }
+ else
+ {
+ // The mso-spt19 (arc) uses this. But it needs unsigned integer. I don't
+ // know if other shape types also need it. They can be added as necessary.
+ bool bNeedsUnsigned = rObjData.eShapeType == mso_sptArc;
+ if (bNeedsUnsigned)
+ {
+ sal_uInt16 nTmpA(0), nTmpB(0);
+ rIn.ReadUInt16(nTmpA)
+ .ReadUInt16(nTmpB);
+ nX = nTmpA;
+ nY = nTmpB;
+ }
+ else
+ {
+ sal_Int16 nTmpA(0), nTmpB(0);
+ rIn.ReadInt16( nTmpA )
+ .ReadInt16( nTmpB );
+ nX = nTmpA;
+ nY = nTmpB;
+ }
+ }
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rCoordinate.First, nX );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rCoordinate.Second, nY );
+ }
+ }
+ aProp.Name = "Coordinates";
+ aProp.Value <<= aCoordinates;
+ aPathPropVec.push_back( aProp );
+ }
+ // Path/Segments
+ if ( IsProperty( DFF_Prop_pSegmentInfo ) )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > aSegments;
+
+ sal_uInt16 nNumElemSeg = 0;
+
+ if ( SeekToContent( DFF_Prop_pSegmentInfo, rIn ) )
+ {
+ sal_uInt16 nNumElemMemSeg = 0;
+ sal_uInt16 nElemSizeSeg = 2;
+ rIn.ReadUInt16( nNumElemSeg ).ReadUInt16( nNumElemMemSeg ).ReadUInt16( nElemSizeSeg );
+ }
+ sal_uInt64 nMaxEntriesPossible = rIn.remainingSize() / sizeof(sal_uInt16);
+ if (nNumElemSeg > nMaxEntriesPossible)
+ {
+ SAL_WARN("filter.ms", "NumElem list is longer than remaining bytes, ppt or parser is wrong");
+ nNumElemSeg = nMaxEntriesPossible;
+ }
+ if ( nNumElemSeg )
+ {
+ aSegments.realloc( nNumElemSeg );
+ for (auto& rSegment : asNonConstRange(aSegments))
+ {
+ sal_uInt16 nTmp(0);
+ rIn.ReadUInt16( nTmp );
+ sal_Int16 nCommand = EnhancedCustomShapeSegmentCommand::UNKNOWN;
+ sal_Int16 nCnt = static_cast<sal_Int16>( nTmp & 0x1fff );//Last 13 bits for segment points number
+ switch( nTmp >> 13 )//First 3 bits for command type
+ {
+ case 0x0:
+ nCommand = EnhancedCustomShapeSegmentCommand::LINETO;
+ if ( !nCnt ) nCnt = 1;
+ break;
+ case 0x1:
+ nCommand = EnhancedCustomShapeSegmentCommand::CURVETO;
+ if ( !nCnt ) nCnt = 1;
+ break;
+ case 0x2:
+ nCommand = EnhancedCustomShapeSegmentCommand::MOVETO;
+ if ( !nCnt ) nCnt = 1;
+ break;
+ case 0x3:
+ nCommand = EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
+ nCnt = 0;
+ break;
+ case 0x4:
+ nCommand = EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
+ nCnt = 0;
+ break;
+ case 0x5:
+ case 0x6:
+ {
+ switch ( ( nTmp >> 8 ) & 0x1f )//5 bits next to command type is for path escape type
+ {
+ case 0x0:
+ {
+ //It is msopathEscapeExtension which is transformed into LINETO.
+ //If issue happens, I think this part can be comment so that it will be taken as unknown command.
+ //When export, origin data will be export without any change.
+ nCommand = EnhancedCustomShapeSegmentCommand::LINETO;
+ if ( !nCnt )
+ nCnt = 1;
+ }
+ break;
+ case 0x1:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO;
+ nCnt = ( nTmp & 0xff ) / 3;
+ }
+ break;
+ case 0x2:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE;
+ nCnt = ( nTmp & 0xff ) / 3;
+ }
+ break;
+ case 0x3:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::ARCTO;
+ nCnt = ( nTmp & 0xff ) >> 2;
+ };
+ break;
+ case 0x4:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::ARC;
+ nCnt = ( nTmp & 0xff ) >> 2;
+ }
+ break;
+ case 0x5:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO;
+ nCnt = ( nTmp & 0xff ) >> 2;
+ }
+ break;
+ case 0x6:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::CLOCKWISEARC;
+ nCnt = ( nTmp & 0xff ) >> 2;
+ }
+ break;
+ case 0x7:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX;
+ nCnt = nTmp & 0xff;
+ }
+ break;
+ case 0x8:
+ {
+ nCommand = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY;
+ nCnt = nTmp & 0xff;
+ }
+ break;
+ case 0xa: nCommand = EnhancedCustomShapeSegmentCommand::NOFILL; nCnt = 0; break;
+ case 0xb: nCommand = EnhancedCustomShapeSegmentCommand::NOSTROKE; nCnt = 0; break;
+ }
+ }
+ break;
+ }
+ // if the command is unknown, we will store all the data in nCnt, so it will be possible to export without loss
+ if ( nCommand == EnhancedCustomShapeSegmentCommand::UNKNOWN )
+ nCnt = static_cast<sal_Int16>(nTmp);
+ rSegment.Command = nCommand;
+ rSegment.Count = nCnt;
+ }
+ }
+ aProp.Name = "Segments";
+ aProp.Value <<= aSegments;
+ aPathPropVec.push_back( aProp );
+ }
+ // Path/StretchX
+ if ( IsProperty( DFF_Prop_stretchPointX ) )
+ {
+ sal_Int32 nStretchX = GetPropertyValue( DFF_Prop_stretchPointX, 0 );
+ aProp.Name = "StretchX";
+ aProp.Value <<= nStretchX;
+ aPathPropVec.push_back( aProp );
+ }
+ // Path/StretchX
+ if ( IsProperty( DFF_Prop_stretchPointY ) )
+ {
+ sal_Int32 nStretchY = GetPropertyValue( DFF_Prop_stretchPointY, 0 );
+ aProp.Name = "StretchY";
+ aProp.Value <<= nStretchY;
+ aPathPropVec.push_back( aProp );
+ }
+ // Path/TextFrames
+ if ( IsProperty( DFF_Prop_textRectangles ) )
+ {
+ sal_uInt16 nNumElem = 0;
+ sal_uInt16 nElemSize = 16;
+
+ if ( SeekToContent( DFF_Prop_textRectangles, rIn ) )
+ {
+ sal_uInt16 nNumElemMem = 0;
+ rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemMem ).ReadUInt16( nElemSize );
+ }
+ bool bImport = false;
+ if (nElemSize == 16)
+ {
+ //sanity check that the stream is long enough to fulfill nNumElem * nElemSize;
+ bImport = rIn.remainingSize() / nElemSize >= nNumElem;
+ }
+ if (bImport)
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > aTextFrames( nNumElem );
+ for (auto& rTextFrame : asNonConstRange(aTextFrames))
+ {
+ sal_Int32 nLeft(0), nTop(0), nRight(0), nBottom(0);
+
+ rIn.ReadInt32( nLeft )
+ .ReadInt32( nTop )
+ .ReadInt32( nRight )
+ .ReadInt32( nBottom );
+
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.TopLeft.First, nLeft );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.TopLeft.Second, nTop );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.BottomRight.First, nRight );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.BottomRight.Second, nBottom);
+ }
+ aProp.Name = "TextFrames";
+ aProp.Value <<= aTextFrames;
+ aPathPropVec.push_back( aProp );
+ }
+ }
+ //Path/GluePoints
+ if ( IsProperty( DFF_Prop_connectorPoints ) )
+ {
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aGluePoints;
+ sal_uInt16 nNumElemVert = 0;
+ sal_uInt16 nElemSizeVert = 8;
+
+ if ( SeekToContent( DFF_Prop_connectorPoints, rIn ) )
+ {
+ sal_uInt16 nNumElemMemVert = 0;
+ rIn.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
+ // If this value is 0xFFF0 then this record is an array of truncated 8 byte elements. Only the 4
+ // low-order bytes are recorded
+ if (nElemSizeVert == 0xFFF0)
+ nElemSizeVert = 4;
+ }
+
+ // sanity check that the stream is long enough to fulfill nNumElemVert * nElemSizeVert;
+ bool bImport = nElemSizeVert && (rIn.remainingSize() / nElemSizeVert >= nNumElemVert);
+ if (bImport)
+ {
+ aGluePoints.realloc( nNumElemVert );
+ for (auto& rGluePoint : asNonConstRange(aGluePoints))
+ {
+ sal_Int32 nX(0), nY(0);
+ if ( nElemSizeVert == 8 )
+ {
+ rIn.ReadInt32( nX )
+ .ReadInt32( nY );
+ }
+ else
+ {
+ sal_Int16 nTmpA(0), nTmpB(0);
+
+ rIn.ReadInt16( nTmpA )
+ .ReadInt16( nTmpB );
+
+ nX = nTmpA;
+ nY = nTmpB;
+ }
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rGluePoint.First, nX );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rGluePoint.Second, nY );
+ }
+ }
+ aProp.Name = "GluePoints";
+ aProp.Value <<= aGluePoints;
+ aPathPropVec.push_back( aProp );
+ }
+ if ( IsProperty( DFF_Prop_connectorType ) )
+ {
+ sal_Int16 nGluePointType = static_cast<sal_uInt16>(GetPropertyValue( DFF_Prop_connectorType, 0 ));
+ aProp.Name = "GluePointType";
+ aProp.Value <<= nGluePointType;
+ aPathPropVec.push_back( aProp );
+ }
+ // pushing the whole Path element
+ if ( !aPathPropVec.empty() )
+ {
+ aProp.Name = "Path";
+ aProp.Value <<= comphelper::containerToSequence(aPathPropVec);
+ aPropVec.push_back( aProp );
+ }
+ }
+
+ // "TextPath" PropertySequence element
+
+ bool bTextPathOn = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x4000 ) != 0;
+ if ( bTextPathOn )
+ {
+ PropVec aTextPathPropVec;
+
+ // TextPath
+ aProp.Name = "TextPath";
+ aProp.Value <<= bTextPathOn;
+ aTextPathPropVec.push_back( aProp );
+
+ // TextPathMode
+ bool bTextPathFitPath = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x100 ) != 0;
+
+ bool bTextPathFitShape;
+ if ( IsHardAttribute( DFF_Prop_gtextFStretch ) )
+ bTextPathFitShape = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x400 ) != 0;
+ else
+ {
+ bTextPathFitShape = true;
+ switch( rObjData.eShapeType )
+ {
+ case mso_sptTextArchUpCurve :
+ case mso_sptTextArchDownCurve :
+ case mso_sptTextCircleCurve :
+ case mso_sptTextButtonCurve :
+ bTextPathFitShape = false;
+ break;
+ default : break;
+ }
+ }
+ EnhancedCustomShapeTextPathMode eTextPathMode( EnhancedCustomShapeTextPathMode_NORMAL );
+ if ( bTextPathFitShape )
+ eTextPathMode = EnhancedCustomShapeTextPathMode_SHAPE;
+ else if ( bTextPathFitPath )
+ eTextPathMode = EnhancedCustomShapeTextPathMode_PATH;
+ aProp.Name = "TextPathMode";
+ aProp.Value <<= eTextPathMode;
+ aTextPathPropVec.push_back( aProp );
+
+ // ScaleX
+ bool bTextPathScaleX = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x40 ) != 0;
+ aProp.Name = "ScaleX";
+ aProp.Value <<= bTextPathScaleX;
+ aTextPathPropVec.push_back( aProp );
+ // SameLetterHeights
+ bool bSameLetterHeight = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x80 ) != 0;
+ aProp.Name = "SameLetterHeights";
+ aProp.Value <<= bSameLetterHeight;
+ aTextPathPropVec.push_back( aProp );
+
+ // pushing the whole TextPath element
+ aProp.Name = "TextPath";
+ aProp.Value <<= comphelper::containerToSequence(aTextPathPropVec);
+ aPropVec.push_back( aProp );
+ }
+
+ // "AdjustmentValues" // The AdjustmentValues are imported at last, because depending to the type of the
+ //////////////////////// handle (POLAR) we will convert the adjustment value from a fixed float to double
+
+ // checking the last used adjustment handle, so we can determine how many handles are to allocate
+ sal_uInt32 i = DFF_Prop_adjust10Value;
+ while ( ( i >= DFF_Prop_adjustValue ) && !IsProperty( i ) )
+ i--;
+ sal_Int32 nAdjustmentValues = ( i - DFF_Prop_adjustValue ) + 1;
+ if ( nAdjustmentValues )
+ {
+ uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq( nAdjustmentValues );
+ auto pAdjustmentSeq = aAdjustmentSeq.getArray();
+ while( --nAdjustmentValues >= 0 )
+ {
+ sal_Int32 nValue = 0;
+ beans::PropertyState ePropertyState = beans::PropertyState_DEFAULT_VALUE;
+ if ( IsProperty( i ) )
+ {
+ nValue = GetPropertyValue( i, 0 );
+ ePropertyState = beans::PropertyState_DIRECT_VALUE;
+ }
+ if ( nAdjustmentsWhichNeedsToBeConverted & ( 1 << ( i - DFF_Prop_adjustValue ) ) )
+ {
+ double fValue = nValue;
+ fValue /= 65536;
+ pAdjustmentSeq[ nAdjustmentValues ].Value <<= fValue;
+ }
+ else
+ pAdjustmentSeq[ nAdjustmentValues ].Value <<= nValue;
+ pAdjustmentSeq[ nAdjustmentValues ].State = ePropertyState;
+ i--;
+ }
+ aProp.Name = "AdjustmentValues";
+ aProp.Value <<= aAdjustmentSeq;
+ aPropVec.push_back( aProp );
+ }
+
+ // creating the whole property set
+ rSet.Put( SdrCustomShapeGeometryItem( comphelper::containerToSequence(aPropVec) ) );
+}
+
+void DffPropertyReader::ApplyAttributes( SvStream& rIn, SfxItemSet& rSet ) const
+{
+ DffRecordHeader aHdTemp;
+ DffObjData aDffObjTemp( aHdTemp, tools::Rectangle(), 0 );
+ ApplyAttributes( rIn, rSet, aDffObjTemp );
+}
+
+void DffPropertyReader::ApplyAttributes( SvStream& rIn, SfxItemSet& rSet, DffObjData const & rObjData ) const
+{
+ bool bHasShadow = false;
+ bool bNonZeroShadowOffset = false;
+
+ if ( IsProperty( DFF_Prop_gtextSize ) )
+ rSet.Put( SvxFontHeightItem( rManager.ScalePt( GetPropertyValue( DFF_Prop_gtextSize, 0 ) ), 100, EE_CHAR_FONTHEIGHT ) );
+ sal_uInt32 nFontAttributes = GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 );
+ if ( nFontAttributes & 0x20 )
+ rSet.Put( SvxWeightItem( (nFontAttributes & 0x20) ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT ) );
+ if ( nFontAttributes & 0x10 )
+ rSet.Put( SvxPostureItem( (nFontAttributes & 0x10) ? ITALIC_NORMAL : ITALIC_NONE, EE_CHAR_ITALIC ) );
+ if ( nFontAttributes & 0x08 )
+ rSet.Put( SvxUnderlineItem( (nFontAttributes & 0x08) ? LINESTYLE_SINGLE : LINESTYLE_NONE, EE_CHAR_UNDERLINE ) );
+ if ( nFontAttributes & 0x40 )
+ rSet.Put( SvxShadowedItem( (nFontAttributes & 0x40) != 0, EE_CHAR_SHADOW ) );
+// if ( nFontAttributes & 0x02 )
+// rSet.Put( SvxCaseMapItem( nFontAttributes & 0x02 ? SvxCaseMap::SmallCaps : SvxCaseMap::NotMapped ) );
+ if ( nFontAttributes & 0x01 )
+ rSet.Put( SvxCrossedOutItem( (nFontAttributes & 0x01) ? STRIKEOUT_SINGLE : STRIKEOUT_NONE, EE_CHAR_STRIKEOUT ) );
+ if ( IsProperty( DFF_Prop_fillColor ) )
+ rSet.Put( XFillColorItem( OUString(), rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillColor, 0 ), DFF_Prop_fillColor ) ) );
+ if ( IsProperty( DFF_Prop_shadowColor ) )
+ rSet.Put( makeSdrShadowColorItem( rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_shadowColor, 0 ), DFF_Prop_shadowColor ) ) );
+ else
+ {
+ //The default value for this property is 0x00808080
+ rSet.Put( makeSdrShadowColorItem( rManager.MSO_CLR_ToColor( 0x00808080, DFF_Prop_shadowColor ) ) );
+ }
+ if ( IsProperty( DFF_Prop_shadowOpacity ) )
+ rSet.Put( makeSdrShadowTransparenceItem( static_cast<sal_uInt16>( ( 0x10000 - GetPropertyValue( DFF_Prop_shadowOpacity, 0 ) ) / 655 ) ) );
+ if ( IsProperty( DFF_Prop_shadowOffsetX ) )
+ {
+ sal_Int32 nVal = static_cast< sal_Int32 >( GetPropertyValue( DFF_Prop_shadowOffsetX, 0 ) );
+ rManager.ScaleEmu( nVal );
+ rSet.Put( makeSdrShadowXDistItem( nVal ) );
+ bNonZeroShadowOffset = ( nVal > 0 );
+ }
+ if ( IsProperty( DFF_Prop_shadowOffsetY ) )
+ {
+ sal_Int32 nVal = static_cast< sal_Int32 >( GetPropertyValue( DFF_Prop_shadowOffsetY, 0 ) );
+ rManager.ScaleEmu( nVal );
+ rSet.Put( makeSdrShadowYDistItem( nVal ) );
+ bNonZeroShadowOffset = ( nVal > 0 );
+ }
+ if ( IsProperty( DFF_Prop_fshadowObscured ) )
+ {
+ bHasShadow = ( GetPropertyValue( DFF_Prop_fshadowObscured, 0 ) & 2 ) != 0;
+ if ( bHasShadow )
+ {
+ if ( !IsProperty( DFF_Prop_shadowOffsetX ) )
+ rSet.Put( makeSdrShadowXDistItem( 35 ) );
+ if ( !IsProperty( DFF_Prop_shadowOffsetY ) )
+ rSet.Put( makeSdrShadowYDistItem( 35 ) );
+ }
+ }
+ if ( IsProperty( DFF_Prop_shadowType ) )
+ {
+ auto eShadowType = GetPropertyValue(DFF_Prop_shadowType, 0);
+ if( eShadowType != mso_shadowOffset && !bNonZeroShadowOffset )
+ {
+ //0.12" == 173 twip == 302 100mm
+ sal_uInt32 nDist = rManager.pSdrModel->GetScaleUnit() == MapUnit::MapTwip ? 173: 302;
+ rSet.Put( makeSdrShadowXDistItem( nDist ) );
+ rSet.Put( makeSdrShadowYDistItem( nDist ) );
+ }
+ }
+ if ( bHasShadow )
+ {
+ static bool bCheckShadow(false); // loplugin:constvars:ignore
+
+ // #i124477# Found no reason not to set shadow, esp. since it is applied to evtl. existing text
+ // and will lead to an error if in PPT someone used text and added the object shadow to the
+ // object carrying that text. I found no cases where this leads to problems (the old bugtracker
+ // task #160376# from sj is unfortunately no longer available). Keeping the code for now
+ // to allow easy fallback when this shows problems in the future
+ if(bCheckShadow)
+ {
+ // #160376# sj: activating shadow only if fill and or linestyle is used
+ // this is required because of the latest drawing layer core changes.
+ // #i104085# is related to this.
+ sal_uInt32 nLineFlags(GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 ));
+ if(!IsHardAttribute( DFF_Prop_fLine ) && !IsCustomShapeStrokedByDefault( rObjData.eShapeType ))
+ nLineFlags &= ~0x08;
+ sal_uInt32 nFillFlags(GetPropertyValue( DFF_Prop_fNoFillHitTest, 0 ));
+ if(!IsHardAttribute( DFF_Prop_fFilled ) && !IsCustomShapeFilledByDefault( rObjData.eShapeType ))
+ nFillFlags &= ~0x10;
+ if ( nFillFlags & 0x10 )
+ {
+ auto eMSO_FillType = GetPropertyValue(DFF_Prop_fillType, mso_fillSolid);
+ switch( eMSO_FillType )
+ {
+ case mso_fillSolid :
+ case mso_fillPattern :
+ case mso_fillTexture :
+ case mso_fillPicture :
+ case mso_fillShade :
+ case mso_fillShadeCenter :
+ case mso_fillShadeShape :
+ case mso_fillShadeScale :
+ case mso_fillShadeTitle :
+ break;
+ default:
+ nFillFlags &=~0x10; // no fillstyle used
+ break;
+ }
+ }
+ if ( ( ( nLineFlags & 0x08 ) == 0 ) && ( ( nFillFlags & 0x10 ) == 0 ) && ( rObjData.eShapeType != mso_sptPictureFrame )) // if there is no fillstyle and linestyle
+ bHasShadow = false; // we are turning shadow off.
+ }
+
+ if ( bHasShadow )
+ rSet.Put( makeSdrShadowItem( bHasShadow ) );
+ }
+ ApplyLineAttributes( rSet, rObjData.eShapeType ); // #i28269#
+ ApplyFillAttributes( rIn, rSet, rObjData );
+ if ( rObjData.eShapeType != mso_sptNil || IsProperty( DFF_Prop_pVertices ) )
+ {
+ ApplyCustomShapeGeometryAttributes( rIn, rSet, rObjData );
+ ApplyCustomShapeTextAttributes( rSet );
+ if ( rManager.GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_EXCEL )
+ {
+ if ( mnFix16Angle || ( rObjData.nSpFlags & ShapeFlag::FlipV ) )
+ CheckAndCorrectExcelTextRotation( rIn, rSet, rObjData );
+ }
+ }
+}
+
+void DffPropertyReader::CheckAndCorrectExcelTextRotation( SvStream& rIn, SfxItemSet& rSet, DffObjData const & rObjData ) const
+{
+ bool bRotateTextWithShape = rObjData.bRotateTextWithShape;
+ if ( rObjData.bOpt2 ) // sj: #158494# is the second property set available ? if then we have to check the xml data of
+ { // the shape, because the textrotation of Excel 2003 and greater versions is stored there
+ // (upright property of the textbox)
+ if ( rManager.pSecPropSet->SeekToContent( DFF_Prop_metroBlob, rIn ) )
+ {
+ sal_uInt32 nLen = rManager.pSecPropSet->GetPropertyValue( DFF_Prop_metroBlob, 0 );
+ if ( nLen )
+ {
+ css::uno::Sequence< sal_Int8 > aXMLDataSeq( nLen );
+ rIn.ReadBytes(aXMLDataSeq.getArray(), nLen);
+ css::uno::Reference< css::io::XInputStream > xInputStream
+ ( new ::comphelper::SequenceInputStream( aXMLDataSeq ) );
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+ css::uno::Reference< css::embed::XStorage > xStorage
+ ( ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream(
+ OFOPXML_STORAGE_FORMAT_STRING, xInputStream, xContext, true ) );
+ if ( xStorage.is() )
+ {
+ css::uno::Reference< css::embed::XStorage >
+ xStorageDRS( xStorage->openStorageElement( "drs", css::embed::ElementModes::SEEKABLEREAD ) );
+ if ( xStorageDRS.is() )
+ {
+ css::uno::Reference< css::io::XStream > xShapeXMLStream( xStorageDRS->openStreamElement( "shapexml.xml", css::embed::ElementModes::SEEKABLEREAD ) );
+ if ( xShapeXMLStream.is() )
+ {
+ css::uno::Reference< css::io::XInputStream > xShapeXMLInputStream( xShapeXMLStream->getInputStream() );
+ if ( xShapeXMLInputStream.is() )
+ {
+ css::uno::Sequence< sal_Int8 > aSeq;
+ sal_Int32 nBytesRead = xShapeXMLInputStream->readBytes( aSeq, 0x7fffffff );
+ if ( nBytesRead )
+ { // for only one property I spare to use a XML parser at this point, this
+ // should be enhanced if needed
+
+ bRotateTextWithShape = true; // using the correct xml default
+ const char* pArry = reinterpret_cast< char* >( aSeq.getArray() );
+ const char* const pUpright = "upright=";
+ const char* pEnd = pArry + nBytesRead;
+ const char* pPtr = pArry;
+ while( ( pPtr + 12 ) < pEnd )
+ {
+ if ( !memcmp( pUpright, pPtr, 8 ) )
+ {
+ bRotateTextWithShape = ( pPtr[ 9 ] != '1' ) && ( pPtr[ 9 ] != 't' );
+ break;
+ }
+ else
+ pPtr++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ catch( css::uno::Exception& )
+ {
+ }
+ }
+ }
+ }
+ if ( bRotateTextWithShape )
+ return;
+
+ const css::uno::Any* pAny;
+ SdrCustomShapeGeometryItem aGeometryItem(rSet.Get( SDRATTR_CUSTOMSHAPE_GEOMETRY ));
+ static const OUStringLiteral sTextRotateAngle( u"TextRotateAngle" );
+ pAny = aGeometryItem.GetPropertyValueByName( sTextRotateAngle );
+ double fExtraTextRotateAngle = 0.0;
+ if ( pAny )
+ *pAny >>= fExtraTextRotateAngle;
+
+ if ( rManager.mnFix16Angle )
+ fExtraTextRotateAngle += toDegrees(mnFix16Angle);
+ if ( rObjData.nSpFlags & ShapeFlag::FlipV )
+ fExtraTextRotateAngle -= 180.0;
+
+ css::beans::PropertyValue aTextRotateAngle;
+ aTextRotateAngle.Name = sTextRotateAngle;
+ aTextRotateAngle.Value <<= fExtraTextRotateAngle;
+ aGeometryItem.SetPropertyValue( aTextRotateAngle );
+ rSet.Put( aGeometryItem );
+}
+
+
+void DffPropertyReader::ImportGradientColor( SfxItemSet& aSet, sal_uInt32 eMSO_FillType, double dTrans , double dBackTrans) const
+{
+ //MS Focus prop will impact the start and end color position. And AOO does not
+ //support this prop. So need some swap for the two color to keep fidelity with AOO and MS shape.
+ //So below var is defined.
+ sal_Int32 nChgColors = 0;
+ sal_Int32 nAngleFix16 = GetPropertyValue( DFF_Prop_fillAngle, 0 );
+ if(nAngleFix16 >= 0)
+ nChgColors ^= 1;
+
+ //Translate a MS clockwise(+) or count clockwise angle(-) into an AOO count clock wise angle
+ Degree10 nAngle( 3600_deg10 - to<Degree10>( Fix16ToAngle(nAngleFix16) ) );
+ //Make sure this angle belongs to 0~3600
+ while ( nAngle >= 3600_deg10 ) nAngle -= 3600_deg10;
+ while ( nAngle < 0_deg10 ) nAngle += 3600_deg10;
+
+ //Rotate angle
+ if ( mbRotateGranientFillWithAngle )
+ {
+ sal_Int32 nRotateAngle = GetPropertyValue( DFF_Prop_Rotation, 0 );
+ //nAngle is a clockwise angle. If nRotateAngle is a clockwise angle, then gradient needs to be rotated a little less
+ //or it needs to be rotated a little more
+ nAngle -= to<Degree10>(Fix16ToAngle(nRotateAngle));
+ }
+ while ( nAngle >= 3600_deg10 ) nAngle -= 3600_deg10;
+ while ( nAngle < 0_deg10 ) nAngle += 3600_deg10;
+
+ css::awt::GradientStyle eGrad = css::awt::GradientStyle_LINEAR;
+
+ sal_Int32 nFocus = GetPropertyValue( DFF_Prop_fillFocus, 0 );
+ if ( !nFocus )
+ nChgColors ^= 1;
+ else if ( nFocus < 0 )//If it is a negative focus, the color will be swapped
+ {
+ nFocus = o3tl::saturating_toggle_sign(nFocus);
+ nChgColors ^= 1;
+ }
+
+ if( nFocus > 40 && nFocus < 60 )
+ {
+ eGrad = css::awt::GradientStyle_AXIAL;//A axial gradient other than linear
+ nChgColors ^= 1;
+ }
+ //if the type is linear or axial, just save focus to nFocusX and nFocusY for export
+ //Core function does no need them. They serve for rect gradient(CenterXY).
+ sal_uInt16 nFocusX = static_cast<sal_uInt16>(nFocus);
+ sal_uInt16 nFocusY = static_cast<sal_uInt16>(nFocus);
+
+ switch( eMSO_FillType )
+ {
+ case mso_fillShadeShape :
+ {
+ eGrad = css::awt::GradientStyle_RECT;
+ nFocusY = nFocusX = 50;
+ nChgColors ^= 1;
+ }
+ break;
+ case mso_fillShadeCenter :
+ {
+ eGrad = css::awt::GradientStyle_RECT;
+ //A MS fillTo prop specifies the relative position of the left boundary
+ //of the center rectangle in a concentric shaded fill. Use 100 or 0 to keep fidelity
+ nFocusX=(GetPropertyValue( DFF_Prop_fillToRight, 0 )==0x10000) ? 100 : 0;
+ nFocusY=(GetPropertyValue( DFF_Prop_fillToBottom,0 )==0x10000) ? 100 : 0;
+ nChgColors ^= 1;
+ }
+ break;
+ default: break;
+ }
+
+ Color aCol1( rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillColor ) );
+ Color aCol2( rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillBackColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillBackColor ) );
+ if ( nChgColors )
+ {
+ //Swap start and end color
+ Color aZwi( aCol1 );
+ aCol1 = aCol2;
+ aCol2 = aZwi;
+ //Swap two colors' transparency
+ double dTemp = dTrans;
+ dTrans = dBackTrans;
+ dBackTrans = dTemp;
+ }
+
+ //Construct gradient item
+ XGradient aGrad( aCol2, aCol1, eGrad, nAngle, nFocusX, nFocusY );
+ //Intensity has been merged into color. So here just set is as 100
+ aGrad.SetStartIntens( 100 );
+ aGrad.SetEndIntens( 100 );
+ aSet.Put( XFillGradientItem( OUString(), aGrad ) );
+ //Construct transparency item. This item can coordinate with both solid and gradient.
+ if ( dTrans < 1.0 || dBackTrans < 1.0 )
+ {
+ sal_uInt8 nStartCol = static_cast<sal_uInt8>( (1 - dTrans )* 255 );
+ sal_uInt8 nEndCol = static_cast<sal_uInt8>( ( 1- dBackTrans ) * 255 );
+ aCol1 = Color(nStartCol, nStartCol, nStartCol);
+ aCol2 = Color(nEndCol, nEndCol, nEndCol);
+
+ XGradient aGrad2( aCol2 , aCol1 , eGrad, nAngle, nFocusX, nFocusY );
+ aSet.Put( XFillFloatTransparenceItem( OUString(), aGrad2 ) );
+ }
+}
+
+
+//- Record Manager ----------------------------------------------------------
+
+
+DffRecordList::DffRecordList( DffRecordList* pList ) :
+ nCount ( 0 ),
+ nCurrent ( 0 ),
+ pPrev ( pList )
+{
+ if ( pList )
+ pList->pNext.reset( this );
+}
+
+DffRecordList::~DffRecordList()
+{
+}
+
+DffRecordManager::DffRecordManager() :
+ DffRecordList ( nullptr ),
+ pCList ( static_cast<DffRecordList*>(this) )
+{
+}
+
+DffRecordManager::DffRecordManager( SvStream& rIn ) :
+ DffRecordList ( nullptr ),
+ pCList ( static_cast<DffRecordList*>(this) )
+{
+ Consume( rIn );
+}
+
+void DffRecordManager::Consume( SvStream& rIn, sal_uInt32 nStOfs )
+{
+ Clear();
+ sal_uInt64 nOldPos = rIn.Tell();
+ if ( !nStOfs )
+ {
+ DffRecordHeader aHd;
+ bool bOk = ReadDffRecordHeader( rIn, aHd );
+ if (bOk && aHd.nRecVer == DFF_PSFLAG_CONTAINER)
+ nStOfs = aHd.GetRecEndFilePos();
+ }
+ if ( !nStOfs )
+ return;
+
+ pCList = this;
+ while ( pCList->pNext )
+ pCList = pCList->pNext.get();
+ while (rIn.good() && ( ( rIn.Tell() + 8 ) <= nStOfs ))
+ {
+ if ( pCList->nCount == DFF_RECORD_MANAGER_BUF_SIZE )
+ pCList = new DffRecordList( pCList );
+ if (!ReadDffRecordHeader(rIn, pCList->mHd[ pCList->nCount ]))
+ break;
+ bool bSeekSucceeded = pCList->mHd[ pCList->nCount++ ].SeekToEndOfRecord(rIn);
+ if (!bSeekSucceeded)
+ break;
+ }
+ rIn.Seek( nOldPos );
+}
+
+void DffRecordManager::Clear()
+{
+ pCList = this;
+ pNext.reset();
+ nCurrent = 0;
+ nCount = 0;
+}
+
+DffRecordHeader* DffRecordManager::Current()
+{
+ DffRecordHeader* pRet = nullptr;
+ if ( pCList->nCurrent < pCList->nCount )
+ pRet = &pCList->mHd[ pCList->nCurrent ];
+ return pRet;
+}
+
+DffRecordHeader* DffRecordManager::First()
+{
+ DffRecordHeader* pRet = nullptr;
+ pCList = this;
+ if ( pCList->nCount )
+ {
+ pCList->nCurrent = 0;
+ pRet = &pCList->mHd[ 0 ];
+ }
+ return pRet;
+}
+
+DffRecordHeader* DffRecordManager::Next()
+{
+ DffRecordHeader* pRet = nullptr;
+ sal_uInt32 nC = pCList->nCurrent + 1;
+ if ( nC < pCList->nCount )
+ {
+ pCList->nCurrent++;
+ pRet = &pCList->mHd[ nC ];
+ }
+ else if ( pCList->pNext )
+ {
+ pCList = pCList->pNext.get();
+ pCList->nCurrent = 0;
+ pRet = &pCList->mHd[ 0 ];
+ }
+ return pRet;
+}
+
+DffRecordHeader* DffRecordManager::Prev()
+{
+ DffRecordHeader* pRet = nullptr;
+ sal_uInt32 nCur = pCList->nCurrent;
+ if ( !nCur && pCList->pPrev )
+ {
+ pCList = pCList->pPrev;
+ nCur = pCList->nCount;
+ }
+ if ( nCur-- )
+ {
+ pCList->nCurrent = nCur;
+ pRet = &pCList->mHd[ nCur ];
+ }
+ return pRet;
+}
+
+DffRecordHeader* DffRecordManager::Last()
+{
+ DffRecordHeader* pRet = nullptr;
+ while ( pCList->pNext )
+ pCList = pCList->pNext.get();
+ sal_uInt32 nCnt = pCList->nCount;
+ if ( nCnt-- )
+ {
+ pCList->nCurrent = nCnt;
+ pRet = &pCList->mHd[ nCnt ];
+ }
+ return pRet;
+}
+
+bool DffRecordManager::SeekToContent( SvStream& rIn, sal_uInt16 nRecId, DffSeekToContentMode eMode )
+{
+ DffRecordHeader* pHd = GetRecordHeader( nRecId, eMode );
+ if ( pHd )
+ {
+ pHd->SeekToContent( rIn );
+ return true;
+ }
+ else
+ return false;
+}
+
+DffRecordHeader* DffRecordManager::GetRecordHeader( sal_uInt16 nRecId, DffSeekToContentMode eMode )
+{
+ sal_uInt32 nOldCurrent = pCList->nCurrent;
+ DffRecordList* pOldList = pCList;
+ DffRecordHeader* pHd;
+
+ if ( eMode == SEEK_FROM_BEGINNING )
+ pHd = First();
+ else
+ pHd = Next();
+
+ while ( pHd )
+ {
+ if ( pHd->nRecType == nRecId )
+ break;
+ pHd = Next();
+ }
+ if ( !pHd && eMode == SEEK_FROM_CURRENT_AND_RESTART )
+ {
+ DffRecordHeader* pBreak = &pOldList->mHd[ nOldCurrent ];
+ pHd = First();
+ if ( pHd )
+ {
+ while ( pHd != pBreak )
+ {
+ if ( pHd->nRecType == nRecId )
+ break;
+ pHd = Next();
+ }
+ if ( pHd->nRecType != nRecId )
+ pHd = nullptr;
+ }
+ }
+ if ( !pHd )
+ {
+ pCList = pOldList;
+ pOldList->nCurrent = nOldCurrent;
+ }
+ return pHd;
+}
+
+
+// private methods
+
+
+bool CompareSvxMSDffShapeInfoById::operator() (
+ std::shared_ptr<SvxMSDffShapeInfo> const& lhs,
+ std::shared_ptr<SvxMSDffShapeInfo> const& rhs) const
+{
+ return lhs->nShapeId < rhs->nShapeId;
+}
+
+bool CompareSvxMSDffShapeInfoByTxBxComp::operator() (
+ std::shared_ptr<SvxMSDffShapeInfo> const& lhs,
+ std::shared_ptr<SvxMSDffShapeInfo> const& rhs) const
+{
+ return lhs->nTxBxComp < rhs->nTxBxComp;
+}
+
+void SvxMSDffManager::Scale( sal_Int32& rVal ) const
+{
+ if ( bNeedMap )
+ rVal = BigMulDiv( rVal, nMapMul, nMapDiv );
+}
+
+void SvxMSDffManager::Scale( Point& rPos ) const
+{
+ rPos.AdjustX(nMapXOfs );
+ rPos.AdjustY(nMapYOfs );
+ if ( bNeedMap )
+ {
+ rPos.setX( BigMulDiv( rPos.X(), nMapMul, nMapDiv ) );
+ rPos.setY( BigMulDiv( rPos.Y(), nMapMul, nMapDiv ) );
+ }
+}
+
+void SvxMSDffManager::Scale( Size& rSiz ) const
+{
+ if ( bNeedMap )
+ {
+ rSiz.setWidth( BigMulDiv( rSiz.Width(), nMapMul, nMapDiv ) );
+ rSiz.setHeight( BigMulDiv( rSiz.Height(), nMapMul, nMapDiv ) );
+ }
+}
+
+void SvxMSDffManager::ScaleEmu( sal_Int32& rVal ) const
+{
+ rVal = BigMulDiv( rVal, nEmuMul, nEmuDiv );
+}
+
+sal_uInt32 SvxMSDffManager::ScalePt( sal_uInt32 nVal ) const
+{
+ MapUnit eMap = pSdrModel->GetScaleUnit();
+ Fraction aFact( GetMapFactor( MapUnit::MapPoint, eMap ).X() );
+ tools::Long aMul = aFact.GetNumerator();
+ tools::Long aDiv = aFact.GetDenominator() * 65536;
+ aFact = Fraction( aMul, aDiv ); // try again to shorten it
+ return BigMulDiv( nVal, aFact.GetNumerator(), aFact.GetDenominator() );
+}
+
+sal_Int32 SvxMSDffManager::ScalePoint( sal_Int32 nVal ) const
+{
+ return BigMulDiv( nVal, nPntMul, nPntDiv );
+};
+
+void SvxMSDffManager::SetModel(SdrModel* pModel, tools::Long nApplicationScale)
+{
+ pSdrModel = pModel;
+ if( pModel && (0 < nApplicationScale) )
+ {
+ // PPT works in units of 576DPI
+ // WW on the other side uses twips, i.e. 1440DPI.
+ MapUnit eMap = pSdrModel->GetScaleUnit();
+ Fraction aFact( GetMapFactor(MapUnit::MapInch, eMap).X() );
+ tools::Long nMul=aFact.GetNumerator();
+ tools::Long nDiv=aFact.GetDenominator()*nApplicationScale;
+ aFact=Fraction(nMul,nDiv); // try again to shorten it
+ // For 100TH_MM -> 2540/576=635/144
+ // For Twip -> 1440/576=5/2
+ nMapMul = aFact.GetNumerator();
+ nMapDiv = aFact.GetDenominator();
+ bNeedMap = nMapMul!=nMapDiv;
+
+ // MS-DFF-Properties are mostly given in EMU (English Metric Units)
+ // 1mm=36000emu, 1twip=635emu
+ aFact=GetMapFactor(MapUnit::Map100thMM,eMap).X();
+ nMul=aFact.GetNumerator();
+ nDiv=aFact.GetDenominator()*360;
+ aFact=Fraction(nMul,nDiv); // try again to shorten it
+ // For 100TH_MM -> 1/360
+ // For Twip -> 14,40/(25,4*360)=144/91440=1/635
+ nEmuMul=aFact.GetNumerator();
+ nEmuDiv=aFact.GetDenominator();
+
+ // And something for typographic Points
+ aFact=GetMapFactor(MapUnit::MapPoint,eMap).X();
+ nPntMul=aFact.GetNumerator();
+ nPntDiv=aFact.GetDenominator();
+ }
+ else
+ {
+ pModel = nullptr;
+ nMapMul = nMapDiv = nMapXOfs = nMapYOfs = nEmuMul = nEmuDiv = nPntMul = nPntDiv = 0;
+ bNeedMap = false;
+ }
+}
+
+bool SvxMSDffManager::SeekToShape( SvStream& rSt, SvxMSDffClientData* /* pClientData */, sal_uInt32 nId ) const
+{
+ bool bRet = false;
+ if ( !maFidcls.empty() )
+ {
+ sal_uInt64 nOldPos = rSt.Tell();
+ sal_uInt32 nSec = ( nId >> 10 ) - 1;
+ if ( nSec < mnIdClusters )
+ {
+ OffsetMap::const_iterator it = maDgOffsetTable.find( maFidcls[ nSec ].dgid );
+ if ( it != maDgOffsetTable.end() )
+ {
+ sal_uInt64 nOfs = it->second;
+ rSt.Seek( nOfs );
+ DffRecordHeader aEscherF002Hd;
+ bool bOk = ReadDffRecordHeader( rSt, aEscherF002Hd );
+ sal_uLong nEscherF002End = bOk ? aEscherF002Hd.GetRecEndFilePos() : 0;
+ while (rSt.good() && rSt.Tell() < nEscherF002End)
+ {
+ DffRecordHeader aEscherObjListHd;
+ if (!ReadDffRecordHeader(rSt, aEscherObjListHd))
+ break;
+ if ( aEscherObjListHd.nRecVer != 0xf )
+ {
+ bool bSeekSuccess = aEscherObjListHd.SeekToEndOfRecord(rSt);
+ if (!bSeekSuccess)
+ break;
+ }
+ else if ( aEscherObjListHd.nRecType == DFF_msofbtSpContainer )
+ {
+ DffRecordHeader aShapeHd;
+ if ( SeekToRec( rSt, DFF_msofbtSp, aEscherObjListHd.GetRecEndFilePos(), &aShapeHd ) )
+ {
+ sal_uInt32 nShapeId(0);
+ rSt.ReadUInt32( nShapeId );
+ if ( nId == nShapeId )
+ {
+ aEscherObjListHd.SeekToBegOfRecord( rSt );
+ bRet = true;
+ break;
+ }
+ }
+ bool bSeekSuccess = aEscherObjListHd.SeekToEndOfRecord(rSt);
+ if (!bSeekSuccess)
+ break;
+ }
+ }
+ }
+ }
+ if ( !bRet )
+ rSt.Seek( nOldPos );
+ }
+ return bRet;
+}
+
+bool SvxMSDffManager::SeekToRec( SvStream& rSt, sal_uInt16 nRecId, sal_uLong nMaxFilePos, DffRecordHeader* pRecHd, sal_uLong nSkipCount )
+{
+ bool bRet = false;
+ sal_uInt64 nOldFPos = rSt.Tell(); // store FilePos to restore it later if necessary
+ do
+ {
+ DffRecordHeader aHd;
+ if (!ReadDffRecordHeader(rSt, aHd))
+ break;
+ if (aHd.nRecLen > nMaxLegalDffRecordLength)
+ break;
+ if ( aHd.nRecType == nRecId )
+ {
+ if ( nSkipCount )
+ nSkipCount--;
+ else
+ {
+ bRet = true;
+ if ( pRecHd != nullptr )
+ *pRecHd = aHd;
+ else
+ {
+ bool bSeekSuccess = aHd.SeekToBegOfRecord(rSt);
+ if (!bSeekSuccess)
+ {
+ bRet = false;
+ break;
+ }
+ }
+ }
+ }
+ if ( !bRet )
+ {
+ bool bSeekSuccess = aHd.SeekToEndOfRecord(rSt);
+ if (!bSeekSuccess)
+ break;
+ }
+ }
+ while ( rSt.good() && rSt.Tell() < nMaxFilePos && !bRet );
+ if ( !bRet )
+ rSt.Seek( nOldFPos ); // restore original FilePos
+ return bRet;
+}
+
+bool SvxMSDffManager::SeekToRec2( sal_uInt16 nRecId1, sal_uInt16 nRecId2, sal_uLong nMaxFilePos ) const
+{
+ bool bRet = false;
+ sal_uInt64 nOldFPos = rStCtrl.Tell(); // remember FilePos for conditionally later restoration
+ do
+ {
+ DffRecordHeader aHd;
+ if (!ReadDffRecordHeader(rStCtrl, aHd))
+ break;
+ if ( aHd.nRecType == nRecId1 || aHd.nRecType == nRecId2 )
+ {
+ bRet = true;
+ bool bSeekSuccess = aHd.SeekToBegOfRecord(rStCtrl);
+ if (!bSeekSuccess)
+ {
+ bRet = false;
+ break;
+ }
+ }
+ if ( !bRet )
+ {
+ bool bSeekSuccess = aHd.SeekToEndOfRecord(rStCtrl);
+ if (!bSeekSuccess)
+ break;
+ }
+ }
+ while ( rStCtrl.good() && rStCtrl.Tell() < nMaxFilePos && !bRet );
+ if ( !bRet )
+ rStCtrl.Seek( nOldFPos ); // restore FilePos
+ return bRet;
+}
+
+
+bool SvxMSDffManager::GetColorFromPalette( sal_uInt16 /* nNum */, Color& rColor ) const
+{
+ // This method has to be overwritten in the class
+ // derived for the excel export
+ rColor = COL_WHITE;
+ return true;
+}
+
+// sj: the documentation is not complete, especially in ppt the normal rgb for text
+// color is written as 0xfeRRGGBB, this can't be explained by the documentation, nearly
+// every bit in the upper code is set -> so there seems to be a special handling for
+// ppt text colors, i decided not to fix this in MSO_CLR_ToColor because of possible
+// side effects, instead MSO_TEXT_CLR_ToColor is called for PPT text colors, to map
+// the color code to something that behaves like the other standard color codes used by
+// fill and line color
+Color SvxMSDffManager::MSO_TEXT_CLR_ToColor( sal_uInt32 nColorCode ) const
+{
+ // for text colors: Header is 0xfeRRGGBB
+ if ( ( nColorCode & 0xfe000000 ) == 0xfe000000 )
+ nColorCode &= 0x00ffffff;
+ else
+ {
+ // for colorscheme colors the color index are the lower three bits of the upper byte
+ if ( ( nColorCode & 0xf8000000 ) == 0 ) // this must be a colorscheme index
+ {
+ nColorCode >>= 24;
+ nColorCode |= 0x8000000;
+ }
+ }
+ return MSO_CLR_ToColor( nColorCode );
+}
+
+Color SvxMSDffManager::MSO_CLR_ToColor( sal_uInt32 nColorCode, sal_uInt16 nContentProperty ) const
+{
+ Color aColor( mnDefaultColor );
+
+ // for text colors: Header is 0xfeRRGGBB
+ if ( ( nColorCode & 0xfe000000 ) == 0xfe000000 ) // sj: it needs to be checked if 0xfe is used in
+ nColorCode &= 0x00ffffff; // other cases than ppt text -> if not this code can be removed
+
+ sal_uInt8 nUpper = static_cast<sal_uInt8>( nColorCode >> 24 );
+
+ // sj: below change from 0x1b to 0x19 was done because of i84812 (0x02 -> rgb color),
+ // now I have some problems to fix i104685 (there the color value is 0x02000000 which requires
+ // a 0x2 scheme color to be displayed properly), the color docu seems to be incomplete
+ if( nUpper & 0x19 ) // if( nUpper & 0x1f )
+ {
+ if( ( nUpper & 0x08 ) || ( ( nUpper & 0x10 ) == 0 ) )
+ {
+ // SCHEMECOLOR
+ if ( !GetColorFromPalette( ( nUpper & 8 ) ? static_cast<sal_uInt16>(nColorCode) : nUpper, aColor ) )
+ {
+ switch( nContentProperty )
+ {
+ case DFF_Prop_pictureTransparent :
+ case DFF_Prop_shadowColor :
+ case DFF_Prop_fillBackColor :
+ case DFF_Prop_fillColor :
+ aColor = COL_WHITE;
+ break;
+ case DFF_Prop_lineColor :
+ {
+ aColor = COL_BLACK;
+ }
+ break;
+ }
+ }
+ }
+ else // SYSCOLOR
+ {
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ sal_uInt16 nParameter = sal_uInt16(( nColorCode >> 16 ) & 0x00ff); // the HiByte of nParameter is not zero, an exclusive AND is helping :o
+ sal_uInt16 nFunctionBits = static_cast<sal_uInt16>( ( nColorCode & 0x00000f00 ) >> 8 );
+ sal_uInt16 nAdditionalFlags = static_cast<sal_uInt16>( ( nColorCode & 0x0000f000) >> 8 );
+ sal_uInt16 nColorIndex = sal_uInt16(nColorCode & 0x00ff);
+ sal_uInt32 nPropColor = 0;
+
+ sal_uInt16 nCProp = 0;
+
+ switch ( nColorIndex )
+ {
+ case mso_syscolorButtonFace : aColor = rStyleSettings.GetFaceColor(); break;
+ case mso_syscolorWindowText : aColor = rStyleSettings.GetWindowTextColor(); break;
+ case mso_syscolorMenu : aColor = rStyleSettings.GetMenuColor(); break;
+ case mso_syscolor3DLight :
+ case mso_syscolorButtonHighlight :
+ case mso_syscolorHighlight : aColor = rStyleSettings.GetHighlightColor(); break;
+ case mso_syscolorHighlightText : aColor = rStyleSettings.GetHighlightTextColor(); break;
+ case mso_syscolorCaptionText : aColor = rStyleSettings.GetMenuTextColor(); break;
+ case mso_syscolorActiveCaption : aColor = rStyleSettings.GetHighlightColor(); break;
+ case mso_syscolorButtonShadow : aColor = rStyleSettings.GetShadowColor(); break;
+ case mso_syscolorButtonText : aColor = rStyleSettings.GetButtonTextColor(); break;
+ case mso_syscolorGrayText : aColor = rStyleSettings.GetDeactiveColor(); break;
+ case mso_syscolorInactiveCaption : aColor = rStyleSettings.GetDeactiveColor(); break;
+ case mso_syscolorInactiveCaptionText : aColor = rStyleSettings.GetDeactiveColor(); break;
+ case mso_syscolorInfoBackground : aColor = rStyleSettings.GetFaceColor(); break;
+ case mso_syscolorInfoText : aColor = rStyleSettings.GetLabelTextColor(); break;
+ case mso_syscolorMenuText : aColor = rStyleSettings.GetMenuTextColor(); break;
+ case mso_syscolorScrollbar : aColor = rStyleSettings.GetFaceColor(); break;
+ case mso_syscolorWindow : aColor = rStyleSettings.GetWindowColor(); break;
+ case mso_syscolorWindowFrame : aColor = rStyleSettings.GetWindowColor(); break;
+
+ case mso_colorFillColor :
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff );
+ nCProp = DFF_Prop_fillColor;
+ }
+ break;
+ case mso_colorLineOrFillColor : // ( use the line color only if there is a line )
+ {
+ if ( GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 ) & 8 )
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_lineColor, 0 );
+ nCProp = DFF_Prop_lineColor;
+ }
+ else
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff );
+ nCProp = DFF_Prop_fillColor;
+ }
+ }
+ break;
+ case mso_colorLineColor :
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_lineColor, 0 );
+ nCProp = DFF_Prop_lineColor;
+ }
+ break;
+ case mso_colorShadowColor :
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_shadowColor, 0x808080 );
+ nCProp = DFF_Prop_shadowColor;
+ }
+ break;
+ case mso_colorThis : // ( use this color ... )
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); //?????????????
+ nCProp = DFF_Prop_fillColor;
+ }
+ break;
+ case mso_colorFillBackColor :
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_fillBackColor, 0xffffff );
+ nCProp = DFF_Prop_fillBackColor;
+ }
+ break;
+ case mso_colorLineBackColor :
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_lineBackColor, 0xffffff );
+ nCProp = DFF_Prop_lineBackColor;
+ }
+ break;
+ case mso_colorFillThenLine : // ( use the fillcolor unless no fill and line )
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); //?????????????
+ nCProp = DFF_Prop_fillColor;
+ }
+ break;
+ case mso_colorIndexMask : // ( extract the color index ) ?
+ {
+ nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); //?????????????
+ nCProp = DFF_Prop_fillColor;
+ }
+ break;
+ }
+ if ( nCProp && ( nPropColor & 0x10000000 ) == 0 ) // beware of looping recursive
+ aColor = MSO_CLR_ToColor( nPropColor, nCProp );
+
+ if( nAdditionalFlags & 0x80 ) // make color gray
+ {
+ sal_uInt8 nZwi = aColor.GetLuminance();
+ aColor = Color( nZwi, nZwi, nZwi );
+ }
+ switch( nFunctionBits )
+ {
+ case 0x01 : // darken color by parameter
+ {
+ aColor.SetRed( sal::static_int_cast< sal_uInt8 >( ( nParameter * aColor.GetRed() ) >> 8 ) );
+ aColor.SetGreen( sal::static_int_cast< sal_uInt8 >( ( nParameter * aColor.GetGreen() ) >> 8 ) );
+ aColor.SetBlue( sal::static_int_cast< sal_uInt8 >( ( nParameter * aColor.GetBlue() ) >> 8 ) );
+ }
+ break;
+ case 0x02 : // lighten color by parameter
+ {
+ sal_uInt16 nInvParameter = ( 0x00ff - nParameter ) * 0xff;
+ aColor.SetRed( sal::static_int_cast< sal_uInt8 >( ( nInvParameter + ( nParameter * aColor.GetRed() ) ) >> 8 ) );
+ aColor.SetGreen( sal::static_int_cast< sal_uInt8 >( ( nInvParameter + ( nParameter * aColor.GetGreen() ) ) >> 8 ) );
+ aColor.SetBlue( sal::static_int_cast< sal_uInt8 >( ( nInvParameter + ( nParameter * aColor.GetBlue() ) ) >> 8 ) );
+ }
+ break;
+ case 0x03 : // add grey level RGB(p,p,p)
+ {
+ sal_Int16 nR = static_cast<sal_Int16>(aColor.GetRed()) + static_cast<sal_Int16>(nParameter);
+ sal_Int16 nG = static_cast<sal_Int16>(aColor.GetGreen()) + static_cast<sal_Int16>(nParameter);
+ sal_Int16 nB = static_cast<sal_Int16>(aColor.GetBlue()) + static_cast<sal_Int16>(nParameter);
+ if ( nR > 0x00ff )
+ nR = 0x00ff;
+ if ( nG > 0x00ff )
+ nG = 0x00ff;
+ if ( nB > 0x00ff )
+ nB = 0x00ff;
+ aColor = Color( static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG), static_cast<sal_uInt8>(nB) );
+ }
+ break;
+ case 0x04 : // subtract grey level RGB(p,p,p)
+ {
+ sal_Int16 nR = static_cast<sal_Int16>(aColor.GetRed()) - static_cast<sal_Int16>(nParameter);
+ sal_Int16 nG = static_cast<sal_Int16>(aColor.GetGreen()) - static_cast<sal_Int16>(nParameter);
+ sal_Int16 nB = static_cast<sal_Int16>(aColor.GetBlue()) - static_cast<sal_Int16>(nParameter);
+ if ( nR < 0 )
+ nR = 0;
+ if ( nG < 0 )
+ nG = 0;
+ if ( nB < 0 )
+ nB = 0;
+ aColor = Color( static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG), static_cast<sal_uInt8>(nB) );
+ }
+ break;
+ case 0x05 : // subtract from gray level RGB(p,p,p)
+ {
+ sal_Int16 nR = static_cast<sal_Int16>(nParameter) - static_cast<sal_Int16>(aColor.GetRed());
+ sal_Int16 nG = static_cast<sal_Int16>(nParameter) - static_cast<sal_Int16>(aColor.GetGreen());
+ sal_Int16 nB = static_cast<sal_Int16>(nParameter) - static_cast<sal_Int16>(aColor.GetBlue());
+ if ( nR < 0 )
+ nR = 0;
+ if ( nG < 0 )
+ nG = 0;
+ if ( nB < 0 )
+ nB = 0;
+ aColor = Color( static_cast<sal_uInt8>(nR), static_cast<sal_uInt8>(nG), static_cast<sal_uInt8>(nB) );
+ }
+ break;
+ case 0x06 : // per component: black if < p, white if >= p
+ {
+ aColor.SetRed( aColor.GetRed() < nParameter ? 0x00 : 0xff );
+ aColor.SetGreen( aColor.GetGreen() < nParameter ? 0x00 : 0xff );
+ aColor.SetBlue( aColor.GetBlue() < nParameter ? 0x00 : 0xff );
+ }
+ break;
+ }
+ if ( nAdditionalFlags & 0x40 ) // top-bit invert
+ aColor = Color( aColor.GetRed() ^ 0x80, aColor.GetGreen() ^ 0x80, aColor.GetBlue() ^ 0x80 );
+
+ if ( nAdditionalFlags & 0x20 ) // invert color
+ aColor = Color(0xff - aColor.GetRed(), 0xff - aColor.GetGreen(), 0xff - aColor.GetBlue());
+ }
+ }
+ else if ( ( nUpper & 4 ) && ( ( nColorCode & 0xfffff8 ) == 0 ) )
+ { // case of nUpper == 4 powerpoint takes this as argument for a colorschemecolor
+ GetColorFromPalette( nUpper, aColor );
+ }
+ else // attributed hard, maybe with hint to SYSTEMRGB
+ aColor = Color( static_cast<sal_uInt8>(nColorCode), static_cast<sal_uInt8>( nColorCode >> 8 ), static_cast<sal_uInt8>( nColorCode >> 16 ) );
+ return aColor;
+}
+
+void SvxMSDffManager::ReadObjText( SvStream& rStream, SdrObject* pObj )
+{
+ DffRecordHeader aRecHd;
+ if (!ReadDffRecordHeader(rStream, aRecHd))
+ return;
+ if( aRecHd.nRecType != DFF_msofbtClientTextbox && aRecHd.nRecType != 0x1022 )
+ return;
+
+ while (rStream.good() && rStream.Tell() < aRecHd.GetRecEndFilePos())
+ {
+ DffRecordHeader aHd;
+ if (!ReadDffRecordHeader(rStream, aHd))
+ break;
+ switch( aHd.nRecType )
+ {
+ case DFF_PST_TextBytesAtom:
+ case DFF_PST_TextCharsAtom:
+ {
+ bool bUniCode = ( aHd.nRecType == DFF_PST_TextCharsAtom );
+ sal_uInt32 nBytes = aHd.nRecLen;
+ OUString aStr = MSDFFReadZString( rStream, nBytes, bUniCode );
+ ReadObjText( aStr, pObj );
+ }
+ break;
+ default:
+ break;
+ }
+ bool bSeekSuccess = aHd.SeekToEndOfRecord(rStream);
+ if (!bSeekSuccess)
+ break;
+ }
+}
+
+// sj: I just want to set a string for a text object that may contain multiple
+// paragraphs. If I now take a look at the following code I get the impression that
+// our outliner is too complicate to be used properly,
+void SvxMSDffManager::ReadObjText( const OUString& rText, SdrObject* pObj )
+{
+ SdrTextObj* pText = dynamic_cast<SdrTextObj*>( pObj );
+ if ( !pText )
+ return;
+
+ SdrOutliner& rOutliner = pText->ImpGetDrawOutliner();
+ rOutliner.Init( OutlinerMode::TextObject );
+
+ bool bOldUpdateMode = rOutliner.SetUpdateLayout( false );
+ rOutliner.SetVertical( pText->IsVerticalWriting() );
+
+ sal_Int32 nParaIndex = 0;
+ sal_Int32 nParaSize;
+ const sal_Unicode* pBuf = rText.getStr();
+ const sal_Unicode* pEnd = rText.getStr() + rText.getLength();
+
+ while( pBuf < pEnd )
+ {
+ const sal_Unicode* pCurrent = pBuf;
+
+ for ( nParaSize = 0; pBuf < pEnd; )
+ {
+ sal_Unicode nChar = *pBuf++;
+ if ( nChar == 0xa )
+ {
+ if ( ( pBuf < pEnd ) && ( *pBuf == 0xd ) )
+ pBuf++;
+ break;
+ }
+ else if ( nChar == 0xd )
+ {
+ if ( ( pBuf < pEnd ) && ( *pBuf == 0xa ) )
+ pBuf++;
+ break;
+ }
+ else
+ ++nParaSize;
+ }
+ ESelection aSelection( nParaIndex, 0, nParaIndex, 0 );
+ OUString aParagraph( pCurrent, nParaSize );
+ if ( !nParaIndex && aParagraph.isEmpty() ) // SJ: we are crashing if the first paragraph is empty ?
+ aParagraph += " "; // otherwise these two lines can be removed.
+ rOutliner.Insert( aParagraph, nParaIndex );
+ rOutliner.SetParaAttribs( nParaIndex, rOutliner.GetEmptyItemSet() );
+
+ SfxItemSet aParagraphAttribs( rOutliner.GetEmptyItemSet() );
+ if ( !aSelection.nStartPos )
+ aParagraphAttribs.Put( SfxBoolItem( EE_PARA_BULLETSTATE, false ) );
+ aSelection.nStartPos = 0;
+ rOutliner.QuickSetAttribs( aParagraphAttribs, aSelection );
+ nParaIndex++;
+ }
+ std::optional<OutlinerParaObject> pNewText = rOutliner.CreateParaObject();
+ rOutliner.Clear();
+ rOutliner.SetUpdateLayout( bOldUpdateMode );
+ pText->SetOutlinerParaObject( std::move(pNewText) );
+ // tdf#143315: restore stylesheet applied to Outliner's nodes when SdrTextObj initializes
+ // its attributes, but removed by Outliner::Init, which calls Outliner::Clear.
+ pText->SetStyleSheet(pText->GetStyleSheet(), true);
+}
+
+//static
+OUString SvxMSDffManager::MSDFFReadZString(SvStream& rIn,
+ sal_uInt32 nLen, bool bUniCode)
+{
+ if (!nLen)
+ return OUString();
+
+ OUString sBuf;
+
+ if( bUniCode )
+ sBuf = read_uInt16s_ToOUString(rIn, nLen/2);
+ else
+ sBuf = read_uInt8s_ToOUString(rIn, nLen, RTL_TEXTENCODING_MS_1252);
+
+ return comphelper::string::stripEnd(sBuf, 0);
+}
+
+static Size lcl_GetPrefSize(const Graphic& rGraf, const MapMode& aWanted)
+{
+ MapMode aPrefMapMode(rGraf.GetPrefMapMode());
+ if (aPrefMapMode == aWanted)
+ return rGraf.GetPrefSize();
+ Size aRetSize;
+ if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel)
+ {
+ aRetSize = Application::GetDefaultDevice()->PixelToLogic(
+ rGraf.GetPrefSize(), aWanted);
+ }
+ else
+ {
+ aRetSize = OutputDevice::LogicToLogic(
+ rGraf.GetPrefSize(), rGraf.GetPrefMapMode(), aWanted);
+ }
+ return aRetSize;
+}
+
+// sj: if the parameter pSet is null, then the resulting crop bitmap will be stored in rGraf,
+// otherwise rGraf is untouched and pSet is used to store the corresponding SdrGrafCropItem
+static void lcl_ApplyCropping( const DffPropSet& rPropSet, SfxItemSet* pSet, Graphic& rGraf )
+{
+ sal_Int32 nCropTop = static_cast<sal_Int32>(rPropSet.GetPropertyValue( DFF_Prop_cropFromTop, 0 ));
+ sal_Int32 nCropBottom = static_cast<sal_Int32>(rPropSet.GetPropertyValue( DFF_Prop_cropFromBottom, 0 ));
+ sal_Int32 nCropLeft = static_cast<sal_Int32>(rPropSet.GetPropertyValue( DFF_Prop_cropFromLeft, 0 ));
+ sal_Int32 nCropRight = static_cast<sal_Int32>(rPropSet.GetPropertyValue( DFF_Prop_cropFromRight, 0 ));
+
+ if( !(nCropTop || nCropBottom || nCropLeft || nCropRight) )
+ return;
+
+ double fFactor;
+ Size aCropSize;
+ BitmapEx aCropBitmap;
+ sal_uInt32 nTop( 0 ), nBottom( 0 ), nLeft( 0 ), nRight( 0 );
+
+ // Cropping has to be applied on a loaded graphic.
+ rGraf.makeAvailable();
+
+ if ( pSet ) // use crop attributes ?
+ aCropSize = lcl_GetPrefSize(rGraf, MapMode(MapUnit::Map100thMM));
+ else
+ {
+ aCropBitmap = rGraf.GetBitmapEx();
+ aCropSize = aCropBitmap.GetSizePixel();
+ }
+ if ( nCropTop )
+ {
+ fFactor = static_cast<double>(nCropTop) / 65536.0;
+ nTop = static_cast<sal_uInt32>( ( static_cast<double>( aCropSize.Height() + 1 ) * fFactor ) + 0.5 );
+ }
+ if ( nCropBottom )
+ {
+ fFactor = static_cast<double>(nCropBottom) / 65536.0;
+ nBottom = static_cast<sal_uInt32>( ( static_cast<double>( aCropSize.Height() + 1 ) * fFactor ) + 0.5 );
+ }
+ if ( nCropLeft )
+ {
+ fFactor = static_cast<double>(nCropLeft) / 65536.0;
+ nLeft = static_cast<sal_uInt32>( ( static_cast<double>( aCropSize.Width() + 1 ) * fFactor ) + 0.5 );
+ }
+ if ( nCropRight )
+ {
+ fFactor = static_cast<double>(nCropRight) / 65536.0;
+ nRight = static_cast<sal_uInt32>( ( static_cast<double>( aCropSize.Width() + 1 ) * fFactor ) + 0.5 );
+ }
+ if ( pSet ) // use crop attributes ?
+ pSet->Put( SdrGrafCropItem( nLeft, nTop, nRight, nBottom ) );
+ else
+ {
+ tools::Rectangle aCropRect( nLeft, nTop, aCropSize.Width() - nRight, aCropSize.Height() - nBottom );
+ aCropBitmap.Crop( aCropRect );
+ rGraf = aCropBitmap;
+ }
+}
+
+SdrObject* SvxMSDffManager::ImportGraphic( SvStream& rSt, SfxItemSet& rSet, const DffObjData& rObjData )
+{
+ SdrObject* pRet = nullptr;
+ OUString aLinkFileName;
+ tools::Rectangle aVisArea;
+
+ auto eFlags = GetPropertyValue(DFF_Prop_pibFlags, mso_blipflagDefault);
+ sal_uInt32 nBlipId = GetPropertyValue( DFF_Prop_pib, 0 );
+ bool bGrfRead = false,
+
+ // Graphic linked
+ bLinkGrf = 0 != ( eFlags & mso_blipflagLinkToFile );
+ {
+ OUString aFileName;
+ Graphic aGraf; // be sure this graphic is deleted before swapping out
+ if( SeekToContent( DFF_Prop_pibName, rSt ) )
+ aFileName = MSDFFReadZString( rSt, GetPropertyValue( DFF_Prop_pibName, 0 ), true );
+
+ // AND, OR the following:
+ if( !( eFlags & mso_blipflagDoNotSave ) ) // Graphic embedded
+ {
+ bGrfRead = GetBLIP( nBlipId, aGraf, &aVisArea );
+ if ( !bGrfRead )
+ {
+ /*
+ Still no luck, lets look at the end of this record for a FBSE pool,
+ this fallback is a specific case for how word does it sometimes
+ */
+ bool bOk = rObjData.rSpHd.SeekToEndOfRecord( rSt );
+ DffRecordHeader aHd;
+ if (bOk)
+ {
+ bOk = ReadDffRecordHeader(rSt, aHd);
+ }
+ if (bOk && DFF_msofbtBSE == aHd.nRecType)
+ {
+ const sal_uLong nSkipBLIPLen = 20;
+ const sal_uLong nSkipShapePos = 4;
+ const sal_uLong nSkipBLIP = 4;
+ const sal_uLong nSkip =
+ nSkipBLIPLen + 4 + nSkipShapePos + 4 + nSkipBLIP;
+
+ if (nSkip <= aHd.nRecLen)
+ {
+ rSt.SeekRel(nSkip);
+ if (ERRCODE_NONE == rSt.GetError())
+ bGrfRead = GetBLIPDirect( rSt, aGraf, &aVisArea );
+ }
+ }
+ }
+ }
+ if ( bGrfRead )
+ {
+ // the writer is doing its own cropping, so this part affects only impress and calc,
+ // unless we're inside a group, in which case writer doesn't crop either
+ if (( GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_CROP_BITMAPS ) || rObjData.nCalledByGroup != 0 )
+ lcl_ApplyCropping( *this, !bool( rObjData.nSpFlags & ShapeFlag::OLEShape ) ? &rSet : nullptr, aGraf );
+
+ if ( IsProperty( DFF_Prop_pictureTransparent ) )
+ {
+ sal_uInt32 nTransColor = GetPropertyValue( DFF_Prop_pictureTransparent, 0 );
+
+ if ( aGraf.GetType() == GraphicType::Bitmap )
+ {
+ BitmapEx aBitmapEx( aGraf.GetBitmapEx() );
+ aBitmapEx.CombineMaskOr( MSO_CLR_ToColor( nTransColor, DFF_Prop_pictureTransparent ), 9 );
+ aGraf = aBitmapEx;
+ }
+ }
+
+ sal_Int32 nContrast = GetPropertyValue( DFF_Prop_pictureContrast, 0x10000 );
+ /*
+ 0x10000 is msoffice 50%
+ < 0x10000 is in units of 1/50th of 0x10000 per 1%
+ > 0x10000 is in units where
+ a msoffice x% is stored as 50/(100-x) * 0x10000
+
+ plus, a (ui) microsoft % ranges from 0 to 100, OOO
+ from -100 to 100, so also normalize into that range
+ */
+ if ( nContrast > 0x10000 )
+ {
+ double fX = nContrast;
+ fX /= 0x10000;
+ fX /= 51; // 50 + 1 to round
+ fX = 1/fX;
+ nContrast = static_cast<sal_Int32>(fX);
+ nContrast -= 100;
+ nContrast = -nContrast;
+ nContrast = (nContrast-50)*2;
+ }
+ else if ( nContrast == 0x10000 )
+ nContrast = 0;
+ else
+ {
+ if (o3tl::checked_multiply<sal_Int32>(nContrast, 101, nContrast)) //100 + 1 to round
+ {
+ SAL_WARN("filter.ms", "bad Contrast value:" << nContrast);
+ nContrast = 0;
+ }
+ else
+ {
+ nContrast /= 0x10000;
+ nContrast -= 100;
+ }
+ }
+ sal_Int16 nBrightness = static_cast<sal_Int16>( static_cast<sal_Int32>(GetPropertyValue( DFF_Prop_pictureBrightness, 0 )) / 327 );
+ sal_Int32 nGamma = GetPropertyValue( DFF_Prop_pictureGamma, 0x10000 );
+ GraphicDrawMode eDrawMode = GraphicDrawMode::Standard;
+ switch ( GetPropertyValue( DFF_Prop_pictureActive, 0 ) & 6 )
+ {
+ case 4 : eDrawMode = GraphicDrawMode::Greys; break;
+ case 6 : eDrawMode = GraphicDrawMode::Mono; break;
+ case 0 :
+ {
+ //office considers the converted values of (in OOo) 70 to be the
+ //"watermark" values, which can vary slightly due to rounding from the
+ //above values
+ if (( nContrast == -70 ) && ( nBrightness == 70 ))
+ {
+ nContrast = 0;
+ nBrightness = 0;
+ eDrawMode = GraphicDrawMode::Watermark;
+ };
+ }
+ break;
+ }
+
+ if ( nContrast || nBrightness || ( nGamma != 0x10000 ) || ( eDrawMode != GraphicDrawMode::Standard ) )
+ {
+ // MSO uses a different algorithm for contrast+brightness, LO applies contrast before brightness,
+ // while MSO apparently applies half of brightness before contrast and half after. So if only
+ // contrast or brightness need to be altered, the result is the same, but if both are involved,
+ // there's no way to map that, so just force a conversion of the image.
+ bool needsConversion = nContrast != 0 && nBrightness != 0;
+ if ( !bool(rObjData.nSpFlags & ShapeFlag::OLEShape) && !needsConversion )
+ {
+ if ( nBrightness )
+ rSet.Put( SdrGrafLuminanceItem( nBrightness ) );
+ if ( nContrast )
+ rSet.Put( SdrGrafContrastItem( static_cast<sal_Int16>(nContrast) ) );
+ if ( nGamma != 0x10000 )
+ rSet.Put( SdrGrafGamma100Item( nGamma / 655 ) );
+ if ( eDrawMode != GraphicDrawMode::Standard )
+ rSet.Put( SdrGrafModeItem( eDrawMode ) );
+ }
+ else
+ {
+ if ( eDrawMode == GraphicDrawMode::Watermark )
+ {
+ nContrast = 60;
+ nBrightness = 70;
+ eDrawMode = GraphicDrawMode::Standard;
+ }
+ switch ( aGraf.GetType() )
+ {
+ case GraphicType::Bitmap :
+ {
+ BitmapEx aBitmapEx( aGraf.GetBitmapEx() );
+ if ( nBrightness || nContrast || ( nGamma != 0x10000 ) )
+ aBitmapEx.Adjust( nBrightness, static_cast<sal_Int16>(nContrast), 0, 0, 0, static_cast<double>(nGamma) / 0x10000, false, true );
+ if ( eDrawMode == GraphicDrawMode::Greys )
+ aBitmapEx.Convert( BmpConversion::N8BitGreys );
+ else if ( eDrawMode == GraphicDrawMode::Mono )
+ aBitmapEx.Convert( BmpConversion::N1BitThreshold );
+ aGraf = aBitmapEx;
+
+ }
+ break;
+
+ case GraphicType::GdiMetafile :
+ {
+ GDIMetaFile aGdiMetaFile( aGraf.GetGDIMetaFile() );
+ if ( nBrightness || nContrast || ( nGamma != 0x10000 ) )
+ aGdiMetaFile.Adjust( nBrightness, static_cast<sal_Int16>(nContrast), 0, 0, 0, static_cast<double>(nGamma) / 0x10000, false, true );
+ if ( eDrawMode == GraphicDrawMode::Greys )
+ aGdiMetaFile.Convert( MtfConversion::N8BitGreys );
+ else if ( eDrawMode == GraphicDrawMode::Mono )
+ aGdiMetaFile.Convert( MtfConversion::N1BitThreshold );
+ aGraf = aGdiMetaFile;
+ }
+ break;
+ default: break;
+ }
+ }
+ }
+ }
+
+ // should it be an OLE object?
+ if( bGrfRead && !bLinkGrf && IsProperty( DFF_Prop_pictureId ) )
+ {
+ // TODO/LATER: in future probably the correct aspect should be provided here
+ // #i32596# - pass <nCalledByGroup> to method
+ pRet = ImportOLE( GetPropertyValue( DFF_Prop_pictureId, 0 ), aGraf, rObjData.aBoundRect, aVisArea, rObjData.nCalledByGroup );
+ }
+ if( !pRet )
+ {
+ pRet = new SdrGrafObj(*pSdrModel);
+ if( bGrfRead )
+ static_cast<SdrGrafObj*>(pRet)->SetGraphic( aGraf );
+
+ if( bLinkGrf && !bGrfRead ) // sj: #i55484# if the graphic was embedded ( bGrfRead == true ) then
+ { // we do not need to set a link. TODO: not to lose the information where the graphic is linked from
+ INetURLObject aAbsURL;
+ if ( !INetURLObject( maBaseURL ).GetNewAbsURL( aFileName, &aAbsURL ) )
+ {
+ OUString aValidURL;
+ if( osl::FileBase::getFileURLFromSystemPath( aFileName, aValidURL ) == osl::FileBase::E_None )
+ aAbsURL = INetURLObject( aValidURL );
+ }
+ if( aAbsURL.GetProtocol() != INetProtocol::NotValid )
+ {
+ aLinkFileName = aAbsURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri );
+ }
+ else
+ aLinkFileName = aFileName;
+ }
+ }
+
+ // set the size from BLIP if there is one
+ if ( bGrfRead && !aVisArea.IsEmpty() )
+ pRet->SetBLIPSizeRectangle( aVisArea );
+
+ if (pRet->GetName().isEmpty()) // SJ 22.02.00 : PPT OLE IMPORT:
+ { // name is already set in ImportOLE !!
+ // JP 01.12.99: SetName before SetModel - because in the other order the Bug 70098 is active
+ if ( ( eFlags & mso_blipflagType ) != mso_blipflagComment )
+ {
+ INetURLObject aURL;
+ aURL.SetSmartURL( aFileName );
+ pRet->SetName( aURL.getBase() );
+ }
+ else
+ pRet->SetName( aFileName );
+ }
+ }
+ pRet->NbcSetLogicRect( rObjData.aBoundRect );
+
+ if (SdrGrafObj* pGrafObj = dynamic_cast<SdrGrafObj*>(pRet))
+ {
+ if( aLinkFileName.getLength() )
+ {
+ pGrafObj->SetGraphicLink( aLinkFileName );
+ Graphic aGraphic(pGrafObj->GetGraphic());
+ aGraphic.setOriginURL(aLinkFileName);
+ }
+
+ if ( bLinkGrf && !bGrfRead )
+ {
+ Graphic aGraf(pGrafObj->GetGraphic());
+ lcl_ApplyCropping( *this, &rSet, aGraf );
+ }
+ }
+
+ return pRet;
+}
+
+// PptSlidePersistEntry& rPersistEntry, SdPage* pPage
+SdrObject* SvxMSDffManager::ImportObj( SvStream& rSt, SvxMSDffClientData& rClientData,
+ tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect, int nCalledByGroup, sal_Int32* pShapeId )
+{
+ SdrObject* pRet = nullptr;
+ DffRecordHeader aObjHd;
+ bool bOk = ReadDffRecordHeader(rSt, aObjHd);
+ if (bOk && aObjHd.nRecType == DFF_msofbtSpgrContainer)
+ {
+ pRet = ImportGroup( aObjHd, rSt, rClientData, rClientRect, rGlobalChildRect, nCalledByGroup, pShapeId );
+ }
+ else if (bOk && aObjHd.nRecType == DFF_msofbtSpContainer)
+ {
+ pRet = ImportShape( aObjHd, rSt, rClientData, rClientRect, rGlobalChildRect, nCalledByGroup, pShapeId );
+ }
+ aObjHd.SeekToBegOfRecord( rSt ); // restore FilePos
+ return pRet;
+}
+
+SdrObject* SvxMSDffManager::ImportGroup( const DffRecordHeader& rHd, SvStream& rSt, SvxMSDffClientData& rClientData,
+ tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect,
+ int nCalledByGroup, sal_Int32* pShapeId )
+{
+ SdrObject* pRet = nullptr;
+
+ if( pShapeId )
+ *pShapeId = 0;
+
+ if (!rHd.SeekToContent(rSt))
+ return pRet;
+
+ DffRecordHeader aRecHd; // the first atom has to be the SpContainer for the GroupObject
+ bool bOk = ReadDffRecordHeader(rSt, aRecHd);
+ if (bOk && aRecHd.nRecType == DFF_msofbtSpContainer)
+ {
+ mnFix16Angle = 0_deg100;
+ if (!aRecHd.SeekToBegOfRecord(rSt))
+ return pRet;
+ pRet = ImportObj( rSt, rClientData, rClientRect, rGlobalChildRect, nCalledByGroup + 1, pShapeId );
+ if ( pRet )
+ {
+ Degree100 nGroupRotateAngle(0);
+ ShapeFlag nSpFlags = nGroupShapeFlags;
+ nGroupRotateAngle = mnFix16Angle;
+
+ tools::Rectangle aClientRect( rClientRect );
+
+ tools::Rectangle aGlobalChildRect;
+ if ( !nCalledByGroup || rGlobalChildRect.IsEmpty() )
+ aGlobalChildRect = GetGlobalChildAnchor( rHd, rSt, aClientRect );
+ else
+ aGlobalChildRect = rGlobalChildRect;
+
+ if ( ( nGroupRotateAngle > 4500_deg100 && nGroupRotateAngle <= 13500_deg100 )
+ || ( nGroupRotateAngle > 22500_deg100 && nGroupRotateAngle <= 31500_deg100 ) )
+ {
+ sal_Int32 nHalfWidth = ( aClientRect.GetWidth() + 1 ) >> 1;
+ sal_Int32 nHalfHeight = ( aClientRect.GetHeight() + 1 ) >> 1;
+ Point aTopLeft( aClientRect.Left() + nHalfWidth - nHalfHeight,
+ aClientRect.Top() + nHalfHeight - nHalfWidth );
+ const tools::Long nRotatedWidth = aClientRect.GetHeight();
+ const tools::Long nRotatedHeight = aClientRect.GetWidth();
+ Size aNewSize(nRotatedWidth, nRotatedHeight);
+ tools::Rectangle aNewRect( aTopLeft, aNewSize );
+ aClientRect = aNewRect;
+ }
+
+ // now importing the inner objects of the group
+ if (!aRecHd.SeekToEndOfRecord(rSt))
+ return pRet;
+
+ while (rSt.good() && ( rSt.Tell() < rHd.GetRecEndFilePos()))
+ {
+ DffRecordHeader aRecHd2;
+ if (!ReadDffRecordHeader(rSt, aRecHd2))
+ break;
+ if ( aRecHd2.nRecType == DFF_msofbtSpgrContainer )
+ {
+ tools::Rectangle aGroupClientAnchor, aGroupChildAnchor;
+ GetGroupAnchors( aRecHd2, rSt, aGroupClientAnchor, aGroupChildAnchor, aClientRect, aGlobalChildRect );
+ if (!aRecHd2.SeekToBegOfRecord(rSt))
+ return pRet;
+ sal_Int32 nShapeId;
+ SdrObject* pTmp = ImportGroup( aRecHd2, rSt, rClientData, aGroupClientAnchor, aGroupChildAnchor, nCalledByGroup + 1, &nShapeId );
+ if (pTmp)
+ {
+ SdrObjGroup* pGroup = dynamic_cast<SdrObjGroup*>(pRet);
+ if (pGroup && pGroup->GetSubList())
+ {
+ pGroup->GetSubList()->NbcInsertObject(pTmp);
+ if (nShapeId)
+ insertShapeId(nShapeId, pTmp);
+ }
+ else
+ FreeObj(rClientData, pTmp);
+ }
+ }
+ else if ( aRecHd2.nRecType == DFF_msofbtSpContainer )
+ {
+ if (!aRecHd2.SeekToBegOfRecord(rSt))
+ return pRet;
+ sal_Int32 nShapeId;
+ SdrObject* pTmp = ImportShape( aRecHd2, rSt, rClientData, aClientRect, aGlobalChildRect, nCalledByGroup + 1, &nShapeId );
+ if (pTmp)
+ {
+ SdrObjGroup* pGroup = dynamic_cast<SdrObjGroup*>(pRet);
+ if (pGroup && pGroup->GetSubList())
+ {
+ pGroup->GetSubList()->NbcInsertObject(pTmp);
+ if (nShapeId)
+ insertShapeId(nShapeId, pTmp);
+ }
+ else
+ FreeObj(rClientData, pTmp);
+ }
+ }
+ if (!aRecHd2.SeekToEndOfRecord(rSt))
+ return pRet;
+ }
+
+ if ( nGroupRotateAngle )
+ pRet->NbcRotate( aClientRect.Center(), nGroupRotateAngle );
+ if ( nSpFlags & ShapeFlag::FlipV )
+ { // BoundRect in aBoundRect
+ Point aLeft( aClientRect.Left(), ( aClientRect.Top() + aClientRect.Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ pRet->NbcMirror( aLeft, aRight );
+ }
+ if ( nSpFlags & ShapeFlag::FlipH )
+ { // BoundRect in aBoundRect
+ Point aTop( ( aClientRect.Left() + aClientRect.Right() ) >> 1, aClientRect.Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ pRet->NbcMirror( aTop, aBottom );
+ }
+ }
+ }
+ if (o3tl::make_unsigned(nCalledByGroup) < maPendingGroupData.size())
+ {
+ // finalization for this group is pending, do it now
+ pRet = FinalizeObj(maPendingGroupData.back().first, pRet);
+ maPendingGroupData.pop_back();
+ }
+ return pRet;
+}
+
+SdrObject* SvxMSDffManager::ImportShape( const DffRecordHeader& rHd, SvStream& rSt, SvxMSDffClientData& rClientData,
+ tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect,
+ int nCalledByGroup, sal_Int32* pShapeId )
+{
+ if( pShapeId )
+ *pShapeId = 0;
+
+ if (!rHd.SeekToBegOfRecord(rSt))
+ return nullptr;
+
+ DffObjData aObjData( rHd, rClientRect, nCalledByGroup );
+
+ aObjData.bRotateTextWithShape = ( GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_EXCEL ) == 0;
+ maShapeRecords.Consume( rSt );
+ if( maShapeRecords.SeekToContent( rSt,
+ DFF_msofbtUDefProp ) )
+ {
+ sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
+ while( 5 < nBytesLeft )
+ {
+ sal_uInt16 nPID(0);
+ rSt.ReadUInt16(nPID);
+ if (!rSt.good())
+ break;
+ sal_uInt32 nUDData(0);
+ rSt.ReadUInt32(nUDData);
+ if (!rSt.good())
+ break;
+ if (nPID == 447)
+ {
+ mbRotateGranientFillWithAngle = nUDData & 0x20;
+ break;
+ }
+ nBytesLeft -= 6;
+ }
+ }
+ aObjData.bShapeType = maShapeRecords.SeekToContent( rSt, DFF_msofbtSp );
+ if ( aObjData.bShapeType )
+ {
+ sal_uInt32 temp(0);
+ rSt.ReadUInt32( aObjData.nShapeId )
+ .ReadUInt32( temp );
+ aObjData.nSpFlags = ShapeFlag(temp);
+ aObjData.eShapeType = static_cast<MSO_SPT>(maShapeRecords.Current()->nRecInstance);
+ }
+ else
+ {
+ aObjData.nShapeId = 0;
+ aObjData.nSpFlags = ShapeFlag::NONE;
+ aObjData.eShapeType = mso_sptNil;
+ }
+
+ if( pShapeId )
+ *pShapeId = aObjData.nShapeId;
+
+ aObjData.bOpt = maShapeRecords.SeekToContent( rSt, DFF_msofbtOPT, SEEK_FROM_CURRENT_AND_RESTART );
+ if ( aObjData.bOpt )
+ {
+ if (!maShapeRecords.Current()->SeekToBegOfRecord(rSt))
+ return nullptr;
+#ifdef DBG_AUTOSHAPE
+ ReadPropSet( rSt, &rClientData, (sal_uInt32)aObjData.eShapeType );
+#else
+ ReadPropSet( rSt, &rClientData );
+#endif
+ }
+ else
+ {
+ InitializePropSet( DFF_msofbtOPT ); // get the default PropSet
+ static_cast<DffPropertyReader*>(this)->mnFix16Angle = 0_deg100;
+ }
+
+ aObjData.bOpt2 = maShapeRecords.SeekToContent( rSt, DFF_msofbtUDefProp, SEEK_FROM_CURRENT_AND_RESTART );
+ if ( aObjData.bOpt2 )
+ {
+ maShapeRecords.Current()->SeekToBegOfRecord( rSt );
+ pSecPropSet.reset( new DffPropertyReader( *this ) );
+ pSecPropSet->ReadPropSet( rSt, nullptr );
+ }
+
+ aObjData.bChildAnchor = maShapeRecords.SeekToContent( rSt, DFF_msofbtChildAnchor, SEEK_FROM_CURRENT_AND_RESTART );
+ if ( aObjData.bChildAnchor )
+ {
+ sal_Int32 l(0), o(0), r(0), u(0);
+ rSt.ReadInt32( l ).ReadInt32( o ).ReadInt32( r ).ReadInt32( u );
+ Scale( l );
+ Scale( o );
+ Scale( r );
+ Scale( u );
+ aObjData.aChildAnchor = tools::Rectangle( l, o, r, u );
+ sal_Int32 nWidth, nHeight;
+ if (!rGlobalChildRect.IsEmpty() && !rClientRect.IsEmpty() && rGlobalChildRect.GetWidth() && rGlobalChildRect.GetHeight() &&
+ !o3tl::checked_sub(r, l, nWidth) && !o3tl::checked_sub(u, o, nHeight))
+ {
+ double fXScale = static_cast<double>(rClientRect.GetWidth()) / static_cast<double>(rGlobalChildRect.GetWidth());
+ double fYScale = static_cast<double>(rClientRect.GetHeight()) / static_cast<double>(rGlobalChildRect.GetHeight());
+ double fl = ( ( l - rGlobalChildRect.Left() ) * fXScale ) + rClientRect.Left();
+ double fo = ( ( o - rGlobalChildRect.Top() ) * fYScale ) + rClientRect.Top();
+ double fWidth = nWidth * fXScale;
+ double fHeight = nHeight * fYScale;
+ aObjData.aChildAnchor = tools::Rectangle( Point( fl, fo ), Size( fWidth + 1, fHeight + 1 ) );
+ }
+ }
+
+ aObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt, DFF_msofbtClientAnchor, SEEK_FROM_CURRENT_AND_RESTART );
+ if ( aObjData.bClientAnchor )
+ ProcessClientAnchor2( rSt, *maShapeRecords.Current(), aObjData );
+
+ if ( aObjData.bChildAnchor )
+ aObjData.aBoundRect = aObjData.aChildAnchor;
+
+ if ( aObjData.nSpFlags & ShapeFlag::Background )
+ aObjData.aBoundRect = tools::Rectangle( Point(), Size( 1, 1 ) );
+
+ SdrObjectUniquePtr xRet;
+
+ tools::Rectangle aTextRect;
+ if ( !aObjData.aBoundRect.IsEmpty() )
+ { // apply rotation to the BoundingBox BEFORE an object has been generated
+ if( mnFix16Angle )
+ {
+ Degree100 nAngle = mnFix16Angle;
+ if ( ( nAngle > 4500_deg100 && nAngle <= 13500_deg100 ) || ( nAngle > 22500_deg100 && nAngle <= 31500_deg100 ) )
+ {
+ sal_Int32 nHalfWidth = ( aObjData.aBoundRect.GetWidth() + 1 ) >> 1;
+ sal_Int32 nHalfHeight = ( aObjData.aBoundRect.GetHeight() + 1 ) >> 1;
+ Point aTopLeft( aObjData.aBoundRect.Left() + nHalfWidth - nHalfHeight,
+ aObjData.aBoundRect.Top() + nHalfHeight - nHalfWidth );
+ Size aNewSize( aObjData.aBoundRect.GetHeight(), aObjData.aBoundRect.GetWidth() );
+ tools::Rectangle aNewRect( aTopLeft, aNewSize );
+ aObjData.aBoundRect = aNewRect;
+ }
+ }
+ aTextRect = aObjData.aBoundRect;
+ bool bGraphic = IsProperty( DFF_Prop_pib ) ||
+ IsProperty( DFF_Prop_pibName ) ||
+ IsProperty( DFF_Prop_pibFlags );
+
+ if ( aObjData.nSpFlags & ShapeFlag::Group )
+ {
+ xRet.reset(new SdrObjGroup(*pSdrModel));
+ /* After CWS aw033 has been integrated, an empty group object
+ cannot store its resulting bounding rectangle anymore. We have
+ to return this rectangle via rClientRect now, but only, if
+ caller has not passed an own bounding ractangle. */
+ if ( rClientRect.IsEmpty() )
+ rClientRect = aObjData.aBoundRect;
+ nGroupShapeFlags = aObjData.nSpFlags;
+ }
+ else if ( ( aObjData.eShapeType != mso_sptNil ) || IsProperty( DFF_Prop_pVertices ) || bGraphic )
+ {
+ SfxItemSet aSet( pSdrModel->GetItemPool() );
+
+ bool bIsConnector = ( ( aObjData.eShapeType >= mso_sptStraightConnector1 ) && ( aObjData.eShapeType <= mso_sptCurvedConnector5 ) );
+ Degree100 nObjectRotation = mnFix16Angle;
+ ShapeFlag nSpFlags = aObjData.nSpFlags;
+
+ if ( bGraphic )
+ {
+ if (!mbSkipImages) {
+ xRet.reset(ImportGraphic(rSt, aSet, aObjData)); // SJ: #68396# is no longer true (fixed in ppt2000)
+ ApplyAttributes( rSt, aSet, aObjData );
+ xRet->SetMergedItemSet(aSet);
+ }
+ }
+ else if ( aObjData.eShapeType == mso_sptLine && !( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 8 ) )
+ {
+ basegfx::B2DPolygon aPoly;
+ aPoly.append(basegfx::B2DPoint(aObjData.aBoundRect.Left(), aObjData.aBoundRect.Top()));
+ aPoly.append(basegfx::B2DPoint(aObjData.aBoundRect.Right(), aObjData.aBoundRect.Bottom()));
+ xRet.reset(new SdrPathObj(
+ *pSdrModel,
+ SdrObjKind::Line,
+ basegfx::B2DPolyPolygon(aPoly)));
+ ApplyAttributes( rSt, aSet, aObjData );
+ xRet->SetMergedItemSet(aSet);
+ }
+ else
+ {
+ if ( GetCustomShapeContent( aObjData.eShapeType ) || IsProperty( DFF_Prop_pVertices ) )
+ {
+
+ ApplyAttributes( rSt, aSet, aObjData );
+
+ xRet.reset(new SdrObjCustomShape(*pSdrModel));
+
+ sal_uInt32 ngtextFStrikethrough = GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 );
+ bool bIsFontwork = ( ngtextFStrikethrough & 0x4000 ) != 0;
+
+ // in case of a FontWork, the text is set by the escher import
+ if ( bIsFontwork )
+ {
+ OUString aObjectText;
+ OUString aFontName;
+
+ if ( SeekToContent( DFF_Prop_gtextFont, rSt ) )
+ {
+ SvxFontItem aLatin(EE_CHAR_FONTINFO), aAsian(EE_CHAR_FONTINFO_CJK), aComplex(EE_CHAR_FONTINFO_CTL);
+ GetDefaultFonts( aLatin, aAsian, aComplex );
+
+ aFontName = MSDFFReadZString( rSt, GetPropertyValue( DFF_Prop_gtextFont, 0 ), true );
+ aSet.Put( SvxFontItem( aLatin.GetFamily(), aFontName, aLatin.GetStyleName(),
+ PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO ));
+ aSet.Put( SvxFontItem( aLatin.GetFamily(), aFontName, aLatin.GetStyleName(),
+ PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO_CJK ) );
+ aSet.Put( SvxFontItem( aLatin.GetFamily(), aFontName, aLatin.GetStyleName(),
+ PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO_CTL ) );
+ }
+
+ // SJ: applying fontattributes for Fontwork :
+ if ( IsHardAttribute( DFF_Prop_gtextFItalic ) )
+ aSet.Put( SvxPostureItem( ( ngtextFStrikethrough & 0x0010 ) != 0 ? ITALIC_NORMAL : ITALIC_NONE, EE_CHAR_ITALIC ) );
+
+ if ( IsHardAttribute( DFF_Prop_gtextFBold ) )
+ aSet.Put( SvxWeightItem( ( ngtextFStrikethrough & 0x0020 ) != 0 ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT ) );
+
+ // SJ TODO: Vertical Writing is not correct, instead
+ // this should be replaced through "CharacterRotation"
+ // by 90 degrees, therefore a new Item has to be
+ // supported by svx core, api and xml file format
+ static_cast<SdrObjCustomShape*>(xRet.get())->SetVerticalWriting( ( ngtextFStrikethrough & 0x2000 ) != 0 );
+
+ if ( SeekToContent( DFF_Prop_gtextUNICODE, rSt ) )
+ {
+ aObjectText = MSDFFReadZString( rSt, GetPropertyValue( DFF_Prop_gtextUNICODE, 0 ), true );
+ ReadObjText(aObjectText, xRet.get());
+ }
+
+ auto eGeoTextAlign = GetPropertyValue(DFF_Prop_gtextAlign, mso_alignTextCenter);
+ {
+ SdrTextHorzAdjust eHorzAdjust;
+ switch( eGeoTextAlign )
+ {
+ case mso_alignTextLetterJust :
+ case mso_alignTextWordJust :
+ case mso_alignTextStretch : eHorzAdjust = SDRTEXTHORZADJUST_BLOCK; break;
+ default:
+ case mso_alignTextInvalid :
+ case mso_alignTextCenter : eHorzAdjust = SDRTEXTHORZADJUST_CENTER; break;
+ case mso_alignTextLeft : eHorzAdjust = SDRTEXTHORZADJUST_LEFT; break;
+ case mso_alignTextRight : eHorzAdjust = SDRTEXTHORZADJUST_RIGHT; break;
+ }
+ aSet.Put( SdrTextHorzAdjustItem( eHorzAdjust ) );
+
+ drawing::TextFitToSizeType eFTS = drawing::TextFitToSizeType_NONE;
+ if ( eGeoTextAlign == mso_alignTextStretch )
+ eFTS = drawing::TextFitToSizeType_ALLLINES;
+ aSet.Put( SdrTextFitToSizeTypeItem( eFTS ) );
+ }
+ if ( IsProperty( DFF_Prop_gtextSpacing ) )
+ {
+ sal_Int32 nTextWidth = GetPropertyValue( DFF_Prop_gtextSpacing, 1 << 16 ) / 655;
+ if ( nTextWidth != 100 )
+ aSet.Put( SvxCharScaleWidthItem( static_cast<sal_uInt16>(nTextWidth), EE_CHAR_FONTWIDTH ) );
+ }
+ if ( ngtextFStrikethrough & 0x1000 ) // SJ: Font Kerning On ?
+ aSet.Put( SvxKerningItem( 1, EE_CHAR_KERNING ) );
+
+ // #i119496# the resize autoshape to fit text attr of word art in MS PPT is always false
+ aSet.Put(makeSdrTextAutoGrowHeightItem(false));
+ aSet.Put(makeSdrTextAutoGrowWidthItem(false));
+
+ bool bWithPadding = !( ngtextFStrikethrough & use_gtextFBestFit
+ && ngtextFStrikethrough & use_gtextFShrinkFit
+ && ngtextFStrikethrough & use_gtextFStretch
+ && ngtextFStrikethrough & gtextFBestFit
+ && ngtextFStrikethrough & gtextFShrinkFit
+ && ngtextFStrikethrough & gtextFStretch );
+
+ if ( bWithPadding )
+ {
+ // trim, remove additional space
+ VclPtr<VirtualDevice> pDevice = VclPtr<VirtualDevice>::Create();
+ vcl::Font aFont = pDevice->GetFont();
+ aFont.SetFamilyName( aFontName );
+ aFont.SetFontSize( Size( 0, 96 ) );
+ pDevice->SetFont( aFont );
+
+ auto nTextWidth = pDevice->GetTextWidth( aObjectText );
+ OUString aObjName = GetPropertyString( DFF_Prop_wzName, rSt );
+ if ( nTextWidth && aObjData.eShapeType == mso_sptTextPlainText
+ && aObjName.match( "PowerPlusWaterMarkObject" ) )
+ {
+ double fRatio = static_cast<double>(pDevice->GetTextHeight()) / nTextWidth;
+ sal_Int32 nNewHeight = fRatio * aObjData.aBoundRect.getWidth();
+ sal_Int32 nPaddingY = aObjData.aBoundRect.getHeight() - nNewHeight;
+
+ if ( nPaddingY > 0 )
+ aObjData.aBoundRect.setHeight( nNewHeight );
+ }
+ }
+ }
+ xRet->SetMergedItemSet( aSet );
+
+ // sj: taking care of rtl, ltr. In case of fontwork mso. seems not to be able to set
+ // proper text directions, instead the text default is depending to the string.
+ // so we have to calculate the a text direction from string:
+ if ( bIsFontwork )
+ {
+ OutlinerParaObject* pParaObj = static_cast<SdrObjCustomShape*>(xRet.get())->GetOutlinerParaObject();
+ if ( pParaObj )
+ {
+ SdrOutliner& rOutliner = static_cast<SdrObjCustomShape*>(xRet.get())->ImpGetDrawOutliner();
+ rOutliner.SetStyleSheetPool(static_cast< SfxStyleSheetPool* >(xRet->getSdrModelFromSdrObject().GetStyleSheetPool()));
+ bool bOldUpdateMode = rOutliner.SetUpdateLayout( false );
+ rOutliner.SetText( *pParaObj );
+ ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT);
+ pVirDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+ sal_Int32 i, nParagraphs = rOutliner.GetParagraphCount();
+ if ( nParagraphs )
+ {
+ bool bCreateNewParaObject = false;
+ for ( i = 0; i < nParagraphs; i++ )
+ {
+ OUString aString(rOutliner.GetText(rOutliner.GetParagraph(i)));
+ bool bIsRTL = pVirDev->GetTextIsRTL(aString, 0, aString.getLength());
+ if ( bIsRTL )
+ {
+ SfxItemSet aSet2( rOutliner.GetParaAttribs( i ) );
+ aSet2.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) );
+ rOutliner.SetParaAttribs( i, aSet2 );
+ bCreateNewParaObject = true;
+ }
+ }
+ if ( bCreateNewParaObject )
+ {
+ std::optional<OutlinerParaObject> pNewText = rOutliner.CreateParaObject();
+ rOutliner.Init( OutlinerMode::TextObject );
+ static_cast<SdrObjCustomShape*>(xRet.get())->NbcSetOutlinerParaObject( std::move(pNewText) );
+ }
+ }
+ rOutliner.Clear();
+ rOutliner.SetUpdateLayout( bOldUpdateMode );
+ }
+ }
+
+ // mso_sptArc special treating
+ // tdf#124029: A new custom shape is generated from prototype 'msoArc'. Values, which are
+ // read here, are adapted and merged. The shape type is changed, so this code
+ // applies only if importing arcs from MS Office.
+ if ( aObjData.eShapeType == mso_sptArc )
+ {
+ static const OUStringLiteral sAdjustmentValues( u"AdjustmentValues" );
+ static const OUStringLiteral sViewBox( u"ViewBox" );
+ static const OUStringLiteral sPath( u"Path" );
+ SdrCustomShapeGeometryItem aGeometryItem( static_cast<SdrObjCustomShape*>(xRet.get())->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ PropertyValue aPropVal;
+
+ // The default arc goes form -90deg to 0deg. Replace general defaults used
+ // when read from stream with this specific values.
+ double fStartAngle(-90.0);
+ double fEndAngle(0.0);
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > seqAdjustmentValues;
+ const uno::Any* pAny = aGeometryItem.GetPropertyValueByName(sAdjustmentValues);
+ if (pAny && (*pAny >>= seqAdjustmentValues) && seqAdjustmentValues.getLength() > 1)
+ {
+ auto pseqAdjustmentValues = seqAdjustmentValues.getArray();
+ if (seqAdjustmentValues[0].State == css::beans::PropertyState_DEFAULT_VALUE)
+ {
+ pseqAdjustmentValues[0].Value <<= -90.0;
+ pseqAdjustmentValues[0].State = com::sun::star::beans::PropertyState_DIRECT_VALUE;
+ }
+ if (seqAdjustmentValues[1].State == css::beans::PropertyState_DEFAULT_VALUE)
+ {
+ pseqAdjustmentValues[1].Value <<= 0.0;
+ pseqAdjustmentValues[1].State = com::sun::star::beans::PropertyState_DIRECT_VALUE;
+ }
+ seqAdjustmentValues[0].Value >>= fStartAngle;
+ seqAdjustmentValues[1].Value >>= fEndAngle;
+ aPropVal.Name = sAdjustmentValues;
+ aPropVal.Value <<= seqAdjustmentValues;
+ aGeometryItem.SetPropertyValue(aPropVal);
+ }
+
+ // arc first command is always wr -- clockwise arc
+ // the parameters are : (left,top),(right,bottom),start(x,y),end(x,y)
+ // The left/top vertex of the frame rectangle of the sector is the origin
+ // of the shape internal coordinate system in MS Office. The default arc
+ // has an ellipse frame rectangle with LT(-21600,0) and
+ // RB(21600,43200) in this coordinate system.
+ basegfx::B2DRectangle aEllipseRect_MS(-21600.0, 0.0, 21600.0, 43200.0);
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates;
+ pAny = aGeometryItem.GetPropertyValueByName( sPath, "Coordinates" );
+ if (pAny && (*pAny >>= seqCoordinates) && (seqCoordinates.getLength() >= 2))
+ {
+ auto const nL
+ = *o3tl::doAccess<sal_Int32>(seqCoordinates[0].First.Value);
+ auto const nT
+ = *o3tl::doAccess<sal_Int32>(seqCoordinates[0].Second.Value);
+ auto const nR
+ = *o3tl::doAccess<sal_Int32>(seqCoordinates[1].First.Value);
+ auto const nB
+ = *o3tl::doAccess<sal_Int32>(seqCoordinates[1].Second.Value);
+ aEllipseRect_MS = basegfx::B2DRectangle(nL, nT, nR, nB);
+ }
+
+ // MS Office uses the pie frame rectangle as reference for outer position
+ // and size of the shape and for text in the shape. We can get this rectangle
+ // from imported viewBox or from the arc geometry.
+ basegfx::B2DRectangle aPieRect_MS(0.0 , 0.0, 21600.0, 21600.0);
+ pAny = aGeometryItem.GetPropertyValueByName(sPath,sViewBox);
+ css::awt::Rectangle aImportedViewBox;
+ if (pAny && (*pAny >>= aImportedViewBox))
+ {
+ aPieRect_MS = basegfx::B2DRectangle( aImportedViewBox.X,
+ aImportedViewBox.Y,
+ aImportedViewBox.X + aImportedViewBox.Width,
+ aImportedViewBox.Y + aImportedViewBox.Height);
+ }
+ else
+ {
+ double fRadStartAngle(basegfx::deg2rad(NormAngle360(fStartAngle)));
+ double fRadEndAngle(basegfx::deg2rad(NormAngle360(fEndAngle)));
+ basegfx::B2DPoint aCenter(aEllipseRect_MS.getCenter());
+ basegfx::B2DPolygon aTempPie(
+ basegfx::utils::createPolygonFromEllipseSegment(
+ aCenter,
+ aEllipseRect_MS.getWidth() * 0.5,
+ aEllipseRect_MS.getHeight() * 0.5,
+ fRadStartAngle,
+ fRadEndAngle));
+ aTempPie.append(aCenter);
+ aPieRect_MS = aTempPie.getB2DRange();
+ }
+
+ // MS Office uses for mso_sptArc a frame rectangle (=resize handles)
+ // which encloses only the sector, LibreOffice uses for custom shapes as
+ // default a frame rectangle, which encloses the entire ellipse. That would
+ // result in wrong positions in Writer and Calc, see tdf#124029.
+ // We workaround this problem, by setting a suitable viewBox.
+ bool bIsImportPPT(GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_PPT);
+ if (bIsImportPPT || aPieRect_MS.getWidth() == 0 || aPieRect_MS.getHeight() == 0)
+ { // clear item, so that default from EnhancedCustomShapeGeometry is used
+ aGeometryItem.ClearPropertyValue(sViewBox);
+ }
+ else
+ {
+ double fX((aPieRect_MS.getMinX() - aEllipseRect_MS.getMinX()) / 2.0);
+ double fY((aPieRect_MS.getMinY() - aEllipseRect_MS.getMinY()) / 2.0);
+ css::awt::Rectangle aViewBox_LO; // in LO coordinate system
+ aViewBox_LO.X = static_cast<sal_Int32>(fX);
+ aViewBox_LO.Y = static_cast<sal_Int32>(fY);
+ aViewBox_LO.Width = static_cast<sal_Int32>(aPieRect_MS.getWidth() / 2.0);
+ aViewBox_LO.Height = static_cast<sal_Int32>(aPieRect_MS.getHeight() / 2.0);
+ aPropVal.Name = sViewBox;
+ aPropVal.Value <<= aViewBox_LO;
+ aGeometryItem.SetPropertyValue(aPropVal);
+ }
+
+ // aObjData.aBoundRect contains position and size of the sector in (outer)
+ // logic coordinates, e.g. for PPT in 1/100 mm, for Word in twips.
+ // For Impress the default viewBox is used, so adapt aObjData.aBoundRect.
+ tools::Rectangle aOldBoundRect(aObjData.aBoundRect); // backup, needed later on
+ if (bIsImportPPT)
+ {
+ double fLogicXOfs(0.0); // LogicLeft_LO = LogicLeft_MS + fXLogicOfs
+ double fLogicYOfs(0.0);
+ double fLogicPieWidth(aObjData.aBoundRect.getWidth());
+ double fLogicPieHeight(aObjData.aBoundRect.getHeight());
+ double fLogicEllipseWidth(0.0); // to be LogicWidth_LO
+ double fLogicEllipseHeight(0.0);
+ if (aPieRect_MS.getWidth())
+ {
+ // fXScale = ratio 'logic length' : 'shape internal length'
+ double fXScale = fLogicPieWidth / aPieRect_MS.getWidth();
+ if (nSpFlags & ShapeFlag::FlipH)
+ fLogicXOfs = (aPieRect_MS.getMaxX() - aEllipseRect_MS.getMaxX()) * fXScale;
+ else
+ fLogicXOfs = (aEllipseRect_MS.getMinX() - aPieRect_MS.getMinX()) * fXScale;
+ fLogicEllipseWidth = aEllipseRect_MS.getWidth() * fXScale;
+ }
+ if (aPieRect_MS.getHeight())
+ {
+ double fYScale = fLogicPieHeight / aPieRect_MS.getHeight();
+ if (nSpFlags & ShapeFlag::FlipV)
+ fLogicYOfs = (aPieRect_MS.getMaxY() - aEllipseRect_MS.getMaxY()) * fYScale;
+ else
+ fLogicYOfs = (aEllipseRect_MS.getMinY() - aPieRect_MS.getMinY()) * fYScale;
+ fLogicEllipseHeight = aEllipseRect_MS.getHeight() * fYScale;
+ }
+ aObjData.aBoundRect = tools::Rectangle(
+ Point(aOldBoundRect.Left() + static_cast<sal_Int32>(fLogicXOfs),
+ aOldBoundRect.Top() + static_cast<sal_Int32>(fLogicYOfs)),
+ Size(static_cast<sal_Int32>(fLogicEllipseWidth),
+ static_cast<sal_Int32>(fLogicEllipseHeight)));
+ }
+ // else nothing to do. aObjData.aBoundRect corresponds to changed viewBox.
+
+ // creating the text frame -> scaling into (0,0),(21600,21600) destination coordinate system
+ double fTextFrameScaleX = 0.0;
+ double fTextFrameScaleY = 0.0;
+ if (aEllipseRect_MS.getWidth())
+ fTextFrameScaleX = 21600.0 / aEllipseRect_MS.getWidth();
+ if (aEllipseRect_MS.getHeight())
+ fTextFrameScaleY = 21600.0 / aEllipseRect_MS.getHeight();
+
+ sal_Int32 nLeft = static_cast<sal_Int32>((aPieRect_MS.getMinX() - aEllipseRect_MS.getMinX()) * fTextFrameScaleX );
+ sal_Int32 nTop = static_cast<sal_Int32>((aPieRect_MS.getMinY() - aEllipseRect_MS.getMinY()) * fTextFrameScaleY );
+ sal_Int32 nRight = static_cast<sal_Int32>((aPieRect_MS.getMaxX() - aEllipseRect_MS.getMinX()) * fTextFrameScaleX );
+ sal_Int32 nBottom= static_cast<sal_Int32>((aPieRect_MS.getMaxY() - aEllipseRect_MS.getMinY()) * fTextFrameScaleY );
+ css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > aTextFrame( 1 );
+ auto pTextFrame = aTextFrame.getArray();
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].TopLeft.First, nLeft );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].TopLeft.Second, nTop );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].BottomRight.First, nRight );
+ EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].BottomRight.Second,nBottom );
+ PropertyValue aProp;
+ aProp.Name = "TextFrames";
+ aProp.Value <<= aTextFrame;
+ aGeometryItem.SetPropertyValue( sPath, aProp );
+
+ // sj: taking care of the different rotation points, since the new arc is having a bigger snaprect
+ if ( mnFix16Angle )
+ {
+ Degree100 nAngle = mnFix16Angle;
+ if ( nSpFlags & ShapeFlag::FlipH )
+ nAngle = 36000_deg100 - nAngle;
+ if ( nSpFlags & ShapeFlag::FlipV )
+ nAngle = -nAngle;
+ double a = toRadians(nAngle);
+ double ss = sin( a );
+ double cc = cos( a );
+ Point aP1( aOldBoundRect.TopLeft() );
+ Point aC1( aObjData.aBoundRect.Center() );
+ Point aP2( aOldBoundRect.TopLeft() );
+ Point aC2( aOldBoundRect.Center() );
+ RotatePoint( aP1, aC1, ss, cc );
+ RotatePoint( aP2, aC2, ss, cc );
+ aObjData.aBoundRect.Move( aP2.X() - aP1.X(), aP2.Y() - aP1.Y() );
+ }
+
+ // clearing items, so MergeDefaultAttributes will set the corresponding
+ // defaults from EnhancedCustomShapeGeometry
+ aGeometryItem.ClearPropertyValue( "Handles" );
+ aGeometryItem.ClearPropertyValue( "Equations" );
+ aGeometryItem.ClearPropertyValue( sPath );
+
+ static_cast<SdrObjCustomShape*>(xRet.get())->SetMergedItem( aGeometryItem );
+ static_cast<SdrObjCustomShape*>(xRet.get())->MergeDefaultAttributes();
+
+ // now setting a new name, so the above correction is only done once when importing from ms
+ SdrCustomShapeGeometryItem aGeoName( static_cast<SdrObjCustomShape*>(xRet.get())->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) );
+ aPropVal.Name = "Type";
+ aPropVal.Value <<= OUString( "mso-spt100" );
+ aGeoName.SetPropertyValue( aPropVal );
+ static_cast<SdrObjCustomShape*>(xRet.get())->SetMergedItem( aGeoName );
+ }
+ else
+ static_cast<SdrObjCustomShape*>(xRet.get())->MergeDefaultAttributes();
+
+ xRet->SetSnapRect( aObjData.aBoundRect );
+ EnhancedCustomShape2d aCustomShape2d(static_cast<SdrObjCustomShape&>(*xRet));
+ aTextRect = aCustomShape2d.GetTextRect();
+
+ if( bIsConnector )
+ {
+ if( nObjectRotation )
+ xRet->NbcRotate( aObjData.aBoundRect.Center(), nObjectRotation );
+ // mirrored horizontally?
+ if ( nSpFlags & ShapeFlag::FlipH )
+ {
+ tools::Rectangle aBndRect(xRet->GetSnapRect());
+ Point aTop( ( aBndRect.Left() + aBndRect.Right() ) >> 1, aBndRect.Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ xRet->NbcMirror( aTop, aBottom );
+ }
+ // mirrored vertically?
+ if ( nSpFlags & ShapeFlag::FlipV )
+ {
+ tools::Rectangle aBndRect(xRet->GetSnapRect());
+ Point aLeft( aBndRect.Left(), ( aBndRect.Top() + aBndRect.Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ xRet->NbcMirror( aLeft, aRight );
+ }
+ basegfx::B2DPolyPolygon aPoly( static_cast<SdrObjCustomShape*>(xRet.get())->GetLineGeometry( true ) );
+
+ xRet.reset(new SdrEdgeObj(*pSdrModel));
+ ApplyAttributes( rSt, aSet, aObjData );
+ xRet->SetLogicRect( aObjData.aBoundRect );
+ xRet->SetMergedItemSet(aSet);
+
+ // connectors
+ auto eConnectorStyle = GetPropertyValue(DFF_Prop_cxstyle, mso_cxstyleStraight);
+
+ static_cast<SdrEdgeObj*>(xRet.get())->ConnectToNode(true, nullptr);
+ static_cast<SdrEdgeObj*>(xRet.get())->ConnectToNode(false, nullptr);
+
+ Point aPoint1( aObjData.aBoundRect.TopLeft() );
+ Point aPoint2( aObjData.aBoundRect.BottomRight() );
+
+ // pay attention to the rotations
+ if ( nObjectRotation )
+ {
+ double a = toRadians(nObjectRotation);
+ Point aCenter( aObjData.aBoundRect.Center() );
+ double ss = sin(a);
+ double cc = cos(a);
+
+ RotatePoint(aPoint1, aCenter, ss, cc);
+ RotatePoint(aPoint2, aCenter, ss, cc);
+
+ // #i120437# reset rotation, it is part of the path and shall not be applied again
+ nObjectRotation = 0_deg100;
+ }
+
+ // rotate/mirror line within the area as we need it
+ if ( nSpFlags & ShapeFlag::FlipH )
+ {
+ sal_Int32 n = aPoint1.X();
+ aPoint1.setX( aPoint2.X() );
+ aPoint2.setX( n );
+
+ // #i120437# reset hor flip
+ nSpFlags &= ~ShapeFlag::FlipH;
+ }
+ if ( nSpFlags & ShapeFlag::FlipV )
+ {
+ sal_Int32 n = aPoint1.Y();
+ aPoint1.setY( aPoint2.Y() );
+ aPoint2.setY( n );
+
+ // #i120437# reset ver flip
+ nSpFlags &= ~ShapeFlag::FlipV;
+ }
+
+ xRet->NbcSetPoint(aPoint1, 0); // start point
+ xRet->NbcSetPoint(aPoint2, 1); // endpoint
+
+ sal_Int32 n1HorzDist, n1VertDist, n2HorzDist, n2VertDist;
+ n1HorzDist = n1VertDist = n2HorzDist = n2VertDist = 0;
+ switch( eConnectorStyle )
+ {
+ case mso_cxstyleBent:
+ {
+ aSet.Put( SdrEdgeKindItem( SdrEdgeKind::OrthoLines ) );
+ n1HorzDist = n1VertDist = n2HorzDist = n2VertDist = 630;
+ }
+ break;
+ case mso_cxstyleCurved:
+ aSet.Put( SdrEdgeKindItem( SdrEdgeKind::Bezier ) );
+ break;
+ default: // mso_cxstyleStraight || mso_cxstyleNone
+ aSet.Put( SdrEdgeKindItem( SdrEdgeKind::OneLine ) );
+ break;
+ }
+ aSet.Put( SdrEdgeNode1HorzDistItem( n1HorzDist ) );
+ aSet.Put( SdrEdgeNode1VertDistItem( n1VertDist ) );
+ aSet.Put( SdrEdgeNode2HorzDistItem( n2HorzDist ) );
+ aSet.Put( SdrEdgeNode2VertDistItem( n2VertDist ) );
+
+ static_cast<SdrEdgeObj*>(xRet.get())->SetEdgeTrackPath( aPoly );
+ xRet->SetMergedItemSet(aSet);
+ }
+ if ( aObjData.eShapeType == mso_sptLine )
+ {
+ xRet->SetMergedItemSet(aSet);
+ static_cast<SdrObjCustomShape*>(xRet.get())->MergeDefaultAttributes();
+ }
+ }
+ }
+
+ if (xRet)
+ {
+ if( nObjectRotation )
+ xRet->NbcRotate( aObjData.aBoundRect.Center(), nObjectRotation );
+ // mirrored horizontally?
+ if ( nSpFlags & ShapeFlag::FlipH )
+ {
+ tools::Rectangle aBndRect(xRet->GetSnapRect());
+ Point aTop( ( aBndRect.Left() + aBndRect.Right() ) >> 1, aBndRect.Top() );
+ Point aBottom( aTop.X(), aTop.Y() + 1000 );
+ xRet->NbcMirror(aTop, aBottom);
+ }
+ // mirrored vertically?
+ if ( nSpFlags & ShapeFlag::FlipV )
+ {
+ tools::Rectangle aBndRect(xRet->GetSnapRect());
+ Point aLeft( aBndRect.Left(), ( aBndRect.Top() + aBndRect.Bottom() ) >> 1 );
+ Point aRight( aLeft.X() + 1000, aLeft.Y() );
+ xRet->NbcMirror(aLeft, aRight);
+ }
+ }
+ }
+ }
+
+ // #i51348# #118052# name of the shape
+ if (xRet)
+ {
+ OUString aObjName = GetPropertyString( DFF_Prop_wzName, rSt );
+ if( !aObjName.isEmpty() )
+ xRet->SetName(aObjName);
+ }
+
+ xRet.reset(ProcessObj(rSt, aObjData, rClientData, aTextRect, xRet.release()));
+
+ if (xRet)
+ {
+ sal_Int32 nGroupProperties( GetPropertyValue( DFF_Prop_fPrint, 0 ) );
+ const bool bVisible = ( ( nGroupProperties & 2 ) == 0 );
+ xRet->SetVisible( bVisible );
+ // In Excel hidden means not printed
+ if ( !bVisible )
+ {
+ xRet->SetPrintable(false);
+ }
+ else
+ {
+ // This property isn't used in Excel anymore, leaving it for legacy reasons
+ xRet->SetPrintable( ( nGroupProperties & 1 ) != 0 );
+ }
+ }
+
+ //Import alt text as description
+ if (xRet && SeekToContent(DFF_Prop_wzDescription, rSt))
+ {
+ OUString aAltText = MSDFFReadZString(rSt, GetPropertyValue(DFF_Prop_wzDescription, 0), true);
+ xRet->SetDescription(aAltText);
+ }
+
+ // If this shape opens a new group, push back its object data because
+ // finalization will be called when nested objects have been imported;
+ // otherwise, just finalize here
+ if (o3tl::make_unsigned(nCalledByGroup) > maPendingGroupData.size())
+ {
+ auto xHdClone = std::make_shared<DffRecordHeader>(aObjData.rSpHd);
+ maPendingGroupData.emplace_back(DffObjData(xHdClone, aObjData), xHdClone );
+ }
+ else
+ {
+ xRet.reset(FinalizeObj(aObjData, xRet.release()));
+ }
+ return xRet.release();
+}
+
+tools::Rectangle SvxMSDffManager::GetGlobalChildAnchor( const DffRecordHeader& rHd, SvStream& rSt, tools::Rectangle& aClientRect )
+{
+ tools::Rectangle aChildAnchor;
+ if (!rHd.SeekToContent(rSt))
+ return aChildAnchor;
+
+ bool bIsClientRectRead = false;
+ while ( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < rHd.GetRecEndFilePos() ) )
+ {
+ DffRecordHeader aShapeHd;
+ if (!ReadDffRecordHeader(rSt, aShapeHd))
+ break;
+ if ( ( aShapeHd.nRecType == DFF_msofbtSpContainer ) ||
+ ( aShapeHd.nRecType == DFF_msofbtSpgrContainer ) )
+ {
+ DffRecordHeader aShapeHd2( aShapeHd );
+ if ( aShapeHd.nRecType == DFF_msofbtSpgrContainer )
+ ReadDffRecordHeader( rSt, aShapeHd2 );
+ while (rSt.good() && rSt.Tell() < aShapeHd2.GetRecEndFilePos())
+ {
+ DffRecordHeader aShapeAtom;
+ if (!ReadDffRecordHeader(rSt, aShapeAtom))
+ break;
+
+ if ( aShapeAtom.nRecType == DFF_msofbtClientAnchor )
+ {
+ if ( GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_PPT )
+ {
+ sal_Int32 l(0), t(0), r(0), b(0);
+ if ( aShapeAtom.nRecLen == 16 )
+ {
+ rSt.ReadInt32( l ).ReadInt32( t ).ReadInt32( r ).ReadInt32( b );
+ }
+ else
+ {
+ sal_Int16 ls(0), ts(0), rs(0), bs(0);
+ rSt.ReadInt16( ts ).ReadInt16( ls ).ReadInt16( rs ).ReadInt16( bs ); // the order of coordinates is a bit strange...
+ l = ls;
+ t = ts;
+ r = rs;
+ b = bs;
+ }
+ Scale( l );
+ Scale( t );
+ Scale( r );
+ Scale( b );
+ if ( bIsClientRectRead )
+ {
+ tools::Rectangle aChild( l, t, r, b );
+ aChildAnchor.Union( aChild );
+ }
+ else
+ {
+ aClientRect = tools::Rectangle( l, t, r, b );
+ bIsClientRectRead = true;
+ }
+ }
+ break;
+ }
+ else if ( aShapeAtom.nRecType == DFF_msofbtChildAnchor )
+ {
+ sal_Int32 l(0), o(0), r(0), u(0);
+ rSt.ReadInt32( l ).ReadInt32( o ).ReadInt32( r ).ReadInt32( u );
+ Scale( l );
+ Scale( o );
+ Scale( r );
+ Scale( u );
+ tools::Rectangle aChild( l, o, r, u );
+ aChildAnchor.Union( aChild );
+ break;
+ }
+ if (!aShapeAtom.SeekToEndOfRecord(rSt))
+ break;
+ }
+ }
+ if (!aShapeHd.SeekToEndOfRecord(rSt))
+ break;
+ }
+ return aChildAnchor;
+}
+
+void SvxMSDffManager::GetGroupAnchors( const DffRecordHeader& rHd, SvStream& rSt,
+ tools::Rectangle& rGroupClientAnchor, tools::Rectangle& rGroupChildAnchor,
+ const tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect )
+{
+ if (!rHd.SeekToContent(rSt))
+ return;
+
+ bool bFirst = true;
+ DffRecordHeader aShapeHd;
+ while (rSt.good() && rSt.Tell() < rHd.GetRecEndFilePos())
+ {
+ if (!ReadDffRecordHeader(rSt, aShapeHd))
+ break;
+ if ( ( aShapeHd.nRecType == DFF_msofbtSpContainer ) ||
+ ( aShapeHd.nRecType == DFF_msofbtSpgrContainer ) )
+ {
+ DffRecordHeader aShapeHd2( aShapeHd );
+ if ( aShapeHd.nRecType == DFF_msofbtSpgrContainer )
+ ReadDffRecordHeader( rSt, aShapeHd2 );
+ while (rSt.good() && rSt.Tell() < aShapeHd2.GetRecEndFilePos())
+ {
+ DffRecordHeader aShapeAtom;
+ if (!ReadDffRecordHeader(rSt, aShapeAtom))
+ break;
+ if ( aShapeAtom.nRecType == DFF_msofbtChildAnchor )
+ {
+ sal_Int32 l(0), o(0), r(0), u(0);
+ rSt.ReadInt32( l ).ReadInt32( o ).ReadInt32( r ).ReadInt32( u );
+ Scale( l );
+ Scale( o );
+ Scale( r );
+ Scale( u );
+ tools::Rectangle aChild( l, o, r, u );
+
+ if ( bFirst )
+ {
+ if ( !rGlobalChildRect.IsEmpty() && !rClientRect.IsEmpty() && rGlobalChildRect.GetWidth() && rGlobalChildRect.GetHeight() )
+ {
+ double fWidth = o3tl::saturating_sub(r, l);
+ double fHeight= o3tl::saturating_sub(u, o);
+ double fXScale = static_cast<double>(rClientRect.GetWidth()) / static_cast<double>(rGlobalChildRect.GetWidth());
+ double fYScale = static_cast<double>(rClientRect.GetHeight()) / static_cast<double>(rGlobalChildRect.GetHeight());
+ double fl = ( ( l - rGlobalChildRect.Left() ) * fXScale ) + rClientRect.Left();
+ double fo = ( ( o - rGlobalChildRect.Top() ) * fYScale ) + rClientRect.Top();
+ fWidth *= fXScale;
+ fHeight *= fYScale;
+ rGroupClientAnchor = tools::Rectangle( Point( static_cast<sal_Int32>(fl), static_cast<sal_Int32>(fo) ), Size( static_cast<sal_Int32>( fWidth + 1 ), static_cast<sal_Int32>( fHeight + 1 ) ) );
+ }
+ bFirst = false;
+ }
+ else
+ rGroupChildAnchor.Union( aChild );
+ break;
+ }
+ if (!aShapeAtom.SeekToEndOfRecord(rSt))
+ break;
+ }
+ }
+ if (!aShapeHd.SeekToEndOfRecord(rSt))
+ break;
+ }
+}
+
+SvxMSDffImportRec* SvxMSDffImportData::find(const SdrObject* pObj)
+{
+ auto it = m_ObjToRecMap.find(pObj);
+ if (it != m_ObjToRecMap.end())
+ return it->second;
+ return nullptr;
+}
+
+void SvxMSDffImportData::insert(std::unique_ptr<SvxMSDffImportRec> pImpRec)
+{
+ auto aRet = m_Records.insert(std::move(pImpRec));
+ bool bSuccess = aRet.second;
+ if (bSuccess)
+ {
+ SvxMSDffImportRec* pRec = aRet.first->get();
+ m_ObjToRecMap[pRec->pObj] = pRec;
+ }
+}
+
+void SvxMSDffImportData::NotifyFreeObj(SdrObject* pObj)
+{
+ if (SvxMSDffImportRec* pRecord = find(pObj))
+ {
+ m_ObjToRecMap.erase(pObj);
+ pRecord->pObj = nullptr;
+ }
+}
+
+void SvxMSDffManager::NotifyFreeObj(SvxMSDffClientData& rData, SdrObject* pObj)
+{
+ if (SdrObjGroup* pGroup = dynamic_cast<SdrObjGroup*>(pObj))
+ {
+ SdrObjList* pSubList = pGroup->GetSubList();
+ size_t nObjCount = pSubList->GetObjCount();
+ for (size_t i = 0; i < nObjCount; ++i)
+ NotifyFreeObj(rData, pSubList->GetObj(i));
+ }
+
+ rData.NotifyFreeObj(pObj);
+}
+
+void SvxMSDffManager::FreeObj(SvxMSDffClientData& rData, SdrObject* pObj)
+{
+ NotifyFreeObj(rData, pObj);
+ SdrObject::Free(pObj);
+}
+
+SdrObject* SvxMSDffManager::ProcessObj(SvStream& rSt,
+ DffObjData& rObjData,
+ SvxMSDffClientData& rData,
+ tools::Rectangle& rTextRect,
+ SdrObject* pObj
+ )
+{
+ if( !rTextRect.IsEmpty() )
+ {
+ SvxMSDffImportData& rImportData = static_cast<SvxMSDffImportData&>(rData);
+ SvxMSDffImportRec* pImpRec = new SvxMSDffImportRec;
+ bool bDeleteImpRec = true;
+ SvxMSDffImportRec* pTextImpRec = pImpRec;
+ bool bDeleteTextImpRec = false;
+
+ // fill Import Record with data
+ pImpRec->nShapeId = rObjData.nShapeId;
+ pImpRec->eShapeType = rObjData.eShapeType;
+
+ auto eWrapMode = GetPropertyValue(DFF_Prop_WrapText, mso_wrapSquare);
+ rObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt,
+ DFF_msofbtClientAnchor,
+ SEEK_FROM_CURRENT_AND_RESTART );
+ if( rObjData.bClientAnchor )
+ ProcessClientAnchor( rSt,
+ maShapeRecords.Current()->nRecLen,
+ pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen );
+
+ rObjData.bClientData = maShapeRecords.SeekToContent( rSt,
+ DFF_msofbtClientData,
+ SEEK_FROM_CURRENT_AND_RESTART );
+ if( rObjData.bClientData )
+ ProcessClientData( rSt,
+ maShapeRecords.Current()->nRecLen,
+ pImpRec->pClientDataBuffer, pImpRec->nClientDataLen );
+
+
+ // process user (== Winword) defined parameters in 0xF122 record
+ if( maShapeRecords.SeekToContent( rSt,
+ DFF_msofbtUDefProp,
+ SEEK_FROM_CURRENT_AND_RESTART )
+ && maShapeRecords.Current()->nRecLen )
+ {
+ sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen;
+ while( 5 < nBytesLeft )
+ {
+ sal_uInt16 nPID(0);
+ rSt.ReadUInt16(nPID);
+ if (!rSt.good())
+ break;
+ sal_uInt32 nUDData(0);
+ rSt.ReadUInt32(nUDData);
+ switch (nPID)
+ {
+ case 0x038F: pImpRec->nXAlign = nUDData; break;
+ case 0x0390:
+ pImpRec->nXRelTo = nUDData;
+ break;
+ case 0x0391: pImpRec->nYAlign = nUDData; break;
+ case 0x0392:
+ pImpRec->nYRelTo = nUDData;
+ break;
+ case 0x03BF: pImpRec->nGroupShapeBooleanProperties = nUDData; break;
+ case 0x0393:
+ // This seems to correspond to o:hrpct from .docx (even including
+ // the difference that it's in 0.1% even though the .docx spec
+ // says it's in 1%).
+ pImpRec->relativeHorizontalWidth = nUDData;
+ break;
+ case 0x0394:
+ // And this is really just a guess, but a mere presence of this
+ // flag makes a horizontal rule be as wide as the page (unless
+ // overridden by something), so it probably matches o:hr from .docx.
+ pImpRec->isHorizontalRule = true;
+ break;
+ }
+ if (!rSt.good())
+ break;
+ nBytesLeft -= 6;
+ }
+ }
+
+ // text frame, also Title or Outline
+ SdrObject* pOrgObj = pObj;
+ SdrRectObj* pTextObj = nullptr;
+ sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 );
+ if( nTextId )
+ {
+ SfxItemSet aSet( pSdrModel->GetItemPool() );
+
+ //Originally anything that as a mso_sptTextBox was created as a
+ //textbox, this was changed for #88277# to be created as a simple
+ //rect to keep impress happy. For the rest of us we'd like to turn
+ //it back into a textbox again.
+ bool bTextFrame = (pImpRec->eShapeType == mso_sptTextBox);
+ if (!bTextFrame)
+ {
+ //Either
+ //a) it's a simple text object or
+ //b) it's a rectangle with text and square wrapping.
+ bTextFrame =
+ (
+ (pImpRec->eShapeType == mso_sptTextSimple) ||
+ (
+ (pImpRec->eShapeType == mso_sptRectangle)
+ && (eWrapMode == mso_wrapSquare)
+ && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() )
+ )
+ );
+ }
+
+ if (bTextFrame)
+ {
+ SdrObject::Free( pObj );
+ pObj = pOrgObj = nullptr;
+ }
+
+ // Distance of Textbox to its surrounding Customshape
+ sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440L);
+ sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440L );
+ sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720L );
+ sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720L );
+
+ ScaleEmu( nTextLeft );
+ ScaleEmu( nTextRight );
+ ScaleEmu( nTextTop );
+ ScaleEmu( nTextBottom );
+
+ Degree100 nTextRotationAngle(0);
+ bool bVerticalText = false;
+ if ( IsProperty( DFF_Prop_txflTextFlow ) )
+ {
+ auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF;
+ switch( eTextFlow )
+ {
+ case mso_txflBtoT:
+ nTextRotationAngle = 9000_deg100;
+ break;
+ case mso_txflVertN:
+ case mso_txflTtoBN:
+ nTextRotationAngle = 27000_deg100;
+ break;
+ case mso_txflTtoBA:
+ bVerticalText = true;
+ break;
+ case mso_txflHorzA:
+ bVerticalText = true;
+ nTextRotationAngle = 9000_deg100;
+ break;
+ case mso_txflHorzN:
+ default :
+ break;
+ }
+ }
+
+ if (nTextRotationAngle)
+ {
+ switch (nTextRotationAngle.get())
+ {
+ case 9000:
+ {
+ tools::Long nWidth = rTextRect.GetWidth();
+ rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
+ rTextRect.SetBottom( rTextRect.Top() + nWidth );
+
+ sal_Int32 nOldTextLeft = nTextLeft;
+ sal_Int32 nOldTextRight = nTextRight;
+ sal_Int32 nOldTextTop = nTextTop;
+ sal_Int32 nOldTextBottom = nTextBottom;
+
+ nTextLeft = nOldTextBottom;
+ nTextRight = nOldTextTop;
+ nTextTop = nOldTextLeft;
+ nTextBottom = nOldTextRight;
+ }
+ break;
+ case 27000:
+ {
+ tools::Long nWidth = rTextRect.GetWidth();
+ rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() );
+ rTextRect.SetBottom( rTextRect.Top() + nWidth );
+
+ sal_Int32 nOldTextLeft = nTextLeft;
+ sal_Int32 nOldTextRight = nTextRight;
+ sal_Int32 nOldTextTop = nTextTop;
+ sal_Int32 nOldTextBottom = nTextBottom;
+
+ nTextLeft = nOldTextTop;
+ nTextRight = nOldTextBottom;
+ nTextTop = nOldTextRight;
+ nTextBottom = nOldTextLeft;
+ }
+ break;
+ }
+ }
+
+ pTextObj = new SdrRectObj(
+ *pSdrModel,
+ SdrObjKind::Text,
+ rTextRect);
+ pTextImpRec = new SvxMSDffImportRec(*pImpRec);
+ bDeleteTextImpRec = true;
+
+ // the vertical paragraph indents are part of the BoundRect,
+ // here we 'remove' them by calculating
+ tools::Rectangle aNewRect(rTextRect);
+ aNewRect.AdjustBottom( -(nTextTop + nTextBottom) );
+ aNewRect.AdjustRight( -(nTextLeft + nTextRight) );
+
+ // Only if it's a simple textbox may Writer replace
+ // the object with a frame, otherwise
+ if( bTextFrame )
+ {
+ auto const pTmpRec = std::make_shared<SvxMSDffShapeInfo>(0, pImpRec->nShapeId);
+
+ SvxMSDffShapeInfos_ById::const_iterator const it =
+ m_xShapeInfosById->find(pTmpRec);
+ if (it != m_xShapeInfosById->end())
+ {
+ SvxMSDffShapeInfo& rInfo = **it;
+ pTextImpRec->bReplaceByFly = rInfo.bReplaceByFly;
+ }
+ }
+
+ if( !pObj )
+ ApplyAttributes( rSt, aSet, rObjData );
+
+ bool bFitText = false;
+ if (GetPropertyValue(DFF_Prop_FitTextToShape, 0) & 2)
+ {
+ aSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
+ aSet.Put( makeSdrTextMinFrameHeightItem(
+ aNewRect.Bottom() - aNewRect.Top() ) );
+ aSet.Put( makeSdrTextMinFrameWidthItem(
+ aNewRect.Right() - aNewRect.Left() ) );
+ bFitText = true;
+ }
+ else
+ {
+ aSet.Put( makeSdrTextAutoGrowHeightItem( false ) );
+ aSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
+ }
+
+ switch (GetPropertyValue(DFF_Prop_WrapText, mso_wrapSquare))
+ {
+ case mso_wrapNone :
+ aSet.Put( makeSdrTextAutoGrowWidthItem( true ) );
+ if (bFitText)
+ {
+ //can't do autowidth in flys #i107184#
+ pTextImpRec->bReplaceByFly = false;
+ }
+ break;
+ case mso_wrapByPoints :
+ aSet.Put( makeSdrTextContourFrameItem( true ) );
+ break;
+ default: break;
+ }
+
+ // set margins at the border of the textbox
+ aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) );
+ aSet.Put( makeSdrTextRightDistItem( nTextRight ) );
+ aSet.Put( makeSdrTextUpperDistItem( nTextTop ) );
+ aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) );
+ pTextImpRec->nDxTextLeft = nTextLeft;
+ pTextImpRec->nDyTextTop = nTextTop;
+ pTextImpRec->nDxTextRight = nTextRight;
+ pTextImpRec->nDyTextBottom = nTextBottom;
+
+ // read text anchor
+ if ( IsProperty( DFF_Prop_anchorText ) )
+ {
+ auto eTextAnchor = GetPropertyValue(DFF_Prop_anchorText, 0);
+
+ SdrTextVertAdjust eTVA = SDRTEXTVERTADJUST_CENTER;
+ bool bTVASet(false);
+ bool bTHASet(false);
+
+ switch( eTextAnchor )
+ {
+ case mso_anchorTop:
+ {
+ eTVA = SDRTEXTVERTADJUST_TOP;
+ bTVASet = true;
+ }
+ break;
+ case mso_anchorTopCentered:
+ {
+ eTVA = SDRTEXTVERTADJUST_TOP;
+ bTVASet = true;
+ bTHASet = true;
+ }
+ break;
+
+ case mso_anchorMiddle:
+ bTVASet = true;
+ break;
+ case mso_anchorMiddleCentered:
+ {
+ bTVASet = true;
+ bTHASet = true;
+ }
+ break;
+ case mso_anchorBottom:
+ {
+ eTVA = SDRTEXTVERTADJUST_BOTTOM;
+ bTVASet = true;
+ }
+ break;
+ case mso_anchorBottomCentered:
+ {
+ eTVA = SDRTEXTVERTADJUST_BOTTOM;
+ bTVASet = true;
+ bTHASet = true;
+ }
+ break;
+ default : break;
+ }
+ // insert
+ if ( bTVASet )
+ aSet.Put( SdrTextVertAdjustItem( eTVA ) );
+ if ( bTHASet )
+ aSet.Put( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_CENTER ) );
+ }
+
+ pTextObj->SetMergedItemSet(aSet);
+
+ if (bVerticalText)
+ pTextObj->SetVerticalWriting(true);
+
+ if (nTextRotationAngle)
+ {
+ tools::Long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ?
+ rTextRect.GetWidth() : rTextRect.GetHeight();
+ nMinWH /= 2;
+ Point aPivot(rTextRect.TopLeft());
+ aPivot.AdjustX(nMinWH );
+ aPivot.AdjustY(nMinWH );
+ pTextObj->SdrAttrObj::NbcRotate(aPivot, nTextRotationAngle);
+ }
+
+ // rotate text with shape?
+ if ( mnFix16Angle )
+ {
+ double a = toRadians(mnFix16Angle);
+ pTextObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle,
+ sin( a ), cos( a ) );
+ }
+
+ if( !pObj )
+ {
+ pObj = pTextObj;
+ }
+ else
+ {
+ if( pTextObj != pObj )
+ {
+ SdrObject* pGroup = new SdrObjGroup(*pSdrModel);
+ pGroup->GetSubList()->NbcInsertObject( pObj );
+ pGroup->GetSubList()->NbcInsertObject( pTextObj );
+ if (pOrgObj == pObj)
+ pOrgObj = pGroup;
+ else
+ pOrgObj = pObj;
+ pObj = pGroup;
+ }
+ }
+ }
+ else if( !pObj )
+ {
+ // simple rectangular objects are ignored by ImportObj() :-(
+ // this is OK for Draw but not for Calc and Writer
+ // cause here these objects have a default border
+ pObj = new SdrRectObj(
+ *pSdrModel,
+ rTextRect);
+
+ pOrgObj = pObj;
+ SfxItemSet aSet( pSdrModel->GetItemPool() );
+ ApplyAttributes( rSt, aSet, rObjData );
+
+ SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR );
+ if( SfxItemState::DEFAULT == eState )
+ aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) );
+ pObj->SetMergedItemSet(aSet);
+ }
+
+ //Means that fBehindDocument is set
+ if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20)
+ pImpRec->bDrawHell = true;
+ else
+ pImpRec->bDrawHell = false;
+ if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02)
+ pImpRec->bHidden = true;
+ pTextImpRec->bDrawHell = pImpRec->bDrawHell;
+ pTextImpRec->bHidden = pImpRec->bHidden;
+ pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 );
+ pTextImpRec->nNextShapeId=pImpRec->nNextShapeId;
+
+ if ( nTextId )
+ {
+ pTextImpRec->aTextId.nTxBxS = static_cast<sal_uInt16>( nTextId >> 16 );
+ pTextImpRec->aTextId.nSequence = static_cast<sal_uInt16>(nTextId);
+ }
+
+ pTextImpRec->nDxWrapDistLeft = GetPropertyValue(
+ DFF_Prop_dxWrapDistLeft, 114935L ) / 635L;
+ pTextImpRec->nDyWrapDistTop = GetPropertyValue(
+ DFF_Prop_dyWrapDistTop, 0 ) / 635L;
+ pTextImpRec->nDxWrapDistRight = GetPropertyValue(
+ DFF_Prop_dxWrapDistRight, 114935L ) / 635L;
+ pTextImpRec->nDyWrapDistBottom = GetPropertyValue(
+ DFF_Prop_dyWrapDistBottom, 0 ) / 635L;
+ // 16.16 fraction times total image width or height, as appropriate.
+
+ if (SeekToContent(DFF_Prop_pWrapPolygonVertices, rSt))
+ {
+ pTextImpRec->pWrapPolygon.reset();
+ sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(8);
+ rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert );
+ // If this value is 0xFFF0 then this record is an array of truncated 8 byte elements. Only the 4
+ // low-order bytes are recorded
+ if (nElemSizeVert == 0xFFF0)
+ nElemSizeVert = 4;
+
+ // sanity check that the stream is long enough to fulfill nNumElemVert * nElemSizeVert;
+ bool bOk = nElemSizeVert && (rSt.remainingSize() / nElemSizeVert >= nNumElemVert);
+ if (bOk)
+ {
+ pTextImpRec->pWrapPolygon = tools::Polygon(nNumElemVert);
+ for (sal_uInt16 i = 0; i < nNumElemVert; ++i)
+ {
+ sal_Int32 nX(0), nY(0);
+ if (nElemSizeVert == 8)
+ rSt.ReadInt32( nX ).ReadInt32( nY );
+ else
+ {
+ sal_Int16 nSmallX(0), nSmallY(0);
+ rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY );
+ nX = nSmallX;
+ nY = nSmallY;
+ }
+ (*(pTextImpRec->pWrapPolygon))[i].setX( nX );
+ (*(pTextImpRec->pWrapPolygon))[i].setY( nY );
+ }
+ }
+ }
+
+ pImpRec->nCropFromTop = GetPropertyValue(
+ DFF_Prop_cropFromTop, 0 );
+ pImpRec->nCropFromBottom = GetPropertyValue(
+ DFF_Prop_cropFromBottom, 0 );
+ pImpRec->nCropFromLeft = GetPropertyValue(
+ DFF_Prop_cropFromLeft, 0 );
+ pImpRec->nCropFromRight = GetPropertyValue(
+ DFF_Prop_cropFromRight, 0 );
+
+ pImpRec->bVFlip = bool(rObjData.nSpFlags & ShapeFlag::FlipV);
+ pImpRec->bHFlip = bool(rObjData.nSpFlags & ShapeFlag::FlipH);
+
+ sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 );
+ pImpRec->eLineStyle = (nLineFlags & 8)
+ ? static_cast<MSO_LineStyle>(GetPropertyValue(
+ DFF_Prop_lineStyle,
+ mso_lineSimple ))
+ : MSO_LineStyle_NONE;
+ pTextImpRec->eLineStyle = pImpRec->eLineStyle;
+
+ pImpRec->eLineDashing = static_cast<MSO_LineDashing>(GetPropertyValue(
+ DFF_Prop_lineDashing, mso_lineSolid ));
+ pTextImpRec->eLineDashing = pImpRec->eLineDashing;
+
+ if( pImpRec->nShapeId )
+ {
+ // amend the import record list
+ if( pOrgObj )
+ {
+ pImpRec->pObj = pOrgObj;
+ rImportData.insert(std::unique_ptr<SvxMSDffImportRec>(pImpRec));
+ bDeleteImpRec = false;
+ if (pImpRec == pTextImpRec)
+ bDeleteTextImpRec = false;
+ }
+
+ if( pTextObj && (pOrgObj != pTextObj) )
+ {
+ // Modify ShapeId (must be unique)
+ pImpRec->nShapeId |= 0x8000000;
+ pTextImpRec->pObj = pTextObj;
+ rImportData.insert(std::unique_ptr<SvxMSDffImportRec>(pTextImpRec));
+ bDeleteTextImpRec = false;
+ if (pTextImpRec == pImpRec)
+ bDeleteImpRec = false;
+ }
+
+ // entry in the z-order-list in order to complement the pointer to this object
+ /*Only store objects which are not deep inside the tree*/
+ if( ( rObjData.nCalledByGroup == 0 )
+ ||
+ ( (rObjData.nSpFlags & ShapeFlag::Group)
+ && (rObjData.nCalledByGroup < 2) )
+ )
+ StoreShapeOrder( pImpRec->nShapeId,
+ ( static_cast<sal_uLong>(pImpRec->aTextId.nTxBxS) << 16 )
+ + pImpRec->aTextId.nSequence, pObj );
+ }
+
+ if (bDeleteImpRec)
+ delete pImpRec;
+
+ if (bDeleteTextImpRec)
+ delete pTextImpRec;
+ }
+
+ return pObj;
+};
+
+SdrObject* SvxMSDffManager::FinalizeObj(DffObjData& /* rObjData */, SdrObject* pObj)
+{
+ return pObj;
+}
+
+
+void SvxMSDffManager::StoreShapeOrder(sal_uLong nId,
+ sal_uLong nTxBx,
+ SdrObject* pObject,
+ SwFlyFrameFormat* pFly) const
+{
+ for (const auto& pOrder : m_aShapeOrders)
+ {
+ if (pOrder->nShapeId == nId)
+ {
+ pOrder->nTxBxComp = nTxBx;
+ pOrder->pObj = pObject;
+ pOrder->pFly = pFly;
+ }
+ }
+}
+
+
+void SvxMSDffManager::ExchangeInShapeOrder( SdrObject const * pOldObject,
+ sal_uLong nTxBx,
+ SdrObject* pObject) const
+{
+ for (const auto& pOrder : m_aShapeOrders)
+ {
+ if (pOrder->pObj == pOldObject)
+ {
+ pOrder->pFly = nullptr;
+ pOrder->pObj = pObject;
+ pOrder->nTxBxComp = nTxBx;
+ }
+ }
+}
+
+
+void SvxMSDffManager::RemoveFromShapeOrder( SdrObject const * pObject ) const
+{
+ for (const auto& pOrder : m_aShapeOrders)
+ {
+ if (pOrder->pObj == pObject)
+ {
+ pOrder->pObj = nullptr;
+ pOrder->pFly = nullptr;
+ pOrder->nTxBxComp = 0;
+ }
+ }
+}
+
+
+// exported class: Public Methods
+
+SvxMSDffManager::SvxMSDffManager(SvStream& rStCtrl_,
+ OUString aBaseURL,
+ sal_uInt32 nOffsDgg_,
+ SvStream* pStData_,
+ SdrModel* pSdrModel_,// see SetModel() below
+ tools::Long nApplicationScale,
+ Color mnDefaultColor_,
+ SvStream* pStData2_,
+ bool bSkipImages )
+ :DffPropertyReader( *this ),
+ m_pBLIPInfos( new SvxMSDffBLIPInfos ),
+ m_xShapeInfosByTxBxComp( new SvxMSDffShapeInfos_ByTxBxComp ),
+ nOffsDgg( nOffsDgg_ ),
+ nBLIPCount( USHRT_MAX ), // initialize with error, since we first check if the
+ nGroupShapeFlags(ShapeFlag::NONE), // ensure initialization here, as some corrupted
+ // files may yield to this being uninitialized
+ maBaseURL(std::move( aBaseURL )),
+ mnIdClusters(0),
+ rStCtrl( rStCtrl_ ),
+ pStData( pStData_ ),
+ pStData2( pStData2_ ),
+ nSvxMSDffSettings( 0 ),
+ nSvxMSDffOLEConvFlags( 0 ),
+ mnDefaultColor( mnDefaultColor_),
+ mbSkipImages (bSkipImages)
+{
+ SetModel( pSdrModel_, nApplicationScale );
+
+ // remember FilePos of the stream(s)
+ sal_uInt64 nOldPosCtrl = rStCtrl.Tell();
+ sal_uInt64 nOldPosData = pStData ? pStData->Tell() : nOldPosCtrl;
+
+ // if no data stream is given we assume that the BLIPs
+ // are in the control stream
+ if( !pStData )
+ pStData = &rStCtrl;
+
+ SetDefaultPropSet( rStCtrl, nOffsDgg );
+
+ // read control stream, if successful set nBLIPCount
+ GetCtrlData( nOffsDgg );
+
+ // check Text-Box-Story-Chain-Infos
+ CheckTxBxStoryChain();
+
+ // restore old FilePos of the stream(s)
+ rStCtrl.Seek( nOldPosCtrl );
+ if( &rStCtrl != pStData )
+ pStData->Seek( nOldPosData );
+}
+
+SvxMSDffManager::SvxMSDffManager( SvStream& rStCtrl_, OUString aBaseURL )
+ :DffPropertyReader( *this ),
+ m_pBLIPInfos( new SvxMSDffBLIPInfos ),
+ m_xShapeInfosByTxBxComp( new SvxMSDffShapeInfos_ByTxBxComp ),
+ nOffsDgg( 0 ),
+ nBLIPCount( USHRT_MAX ), // initialize with error, since we first have to check
+ nGroupShapeFlags(ShapeFlag::NONE),
+ maBaseURL(std::move( aBaseURL )),
+ mnIdClusters(0),
+ rStCtrl( rStCtrl_ ),
+ pStData( nullptr ),
+ pStData2( nullptr ),
+ nSvxMSDffSettings( 0 ),
+ nSvxMSDffOLEConvFlags( 0 ),
+ mnDefaultColor( COL_DEFAULT ),
+ mbSkipImages(false)
+{
+ SetModel( nullptr, 0 );
+}
+
+SvxMSDffManager::~SvxMSDffManager()
+{
+}
+
+void SvxMSDffManager::InitSvxMSDffManager( sal_uInt32 nOffsDgg_, SvStream* pStData_, sal_uInt32 nOleConvFlags )
+{
+ nOffsDgg = nOffsDgg_;
+ pStData = pStData_;
+ nSvxMSDffOLEConvFlags = nOleConvFlags;
+
+ // remember FilePos of the stream(s)
+ sal_uInt64 nOldPosCtrl = rStCtrl.Tell();
+
+ SetDefaultPropSet( rStCtrl, nOffsDgg );
+
+ // insert fidcl cluster table
+ GetFidclData( nOffsDgg );
+
+ // read control stream, if successful, set nBLIPCount
+ GetCtrlData( nOffsDgg );
+
+ // check Text-Box-Story-Chain-Infos
+ CheckTxBxStoryChain();
+
+ // restore old FilePos of the stream(s)
+ rStCtrl.Seek( nOldPosCtrl );
+}
+
+void SvxMSDffManager::SetDgContainer( SvStream& rSt )
+{
+ sal_uInt64 nFilePos = rSt.Tell();
+ DffRecordHeader aDgContHd;
+ bool bOk = ReadDffRecordHeader(rSt, aDgContHd);
+ // insert this container only if there is also a DggAtom
+ if (bOk && SeekToRec(rSt, DFF_msofbtDg, aDgContHd.GetRecEndFilePos()))
+ {
+ DffRecordHeader aRecHd;
+ if (ReadDffRecordHeader(rSt, aRecHd))
+ {
+ sal_uInt32 nDrawingId = aRecHd.nRecInstance;
+ maDgOffsetTable[nDrawingId] = nFilePos;
+ }
+ }
+ rSt.Seek(nFilePos);
+}
+
+void SvxMSDffManager::GetFidclData( sal_uInt32 nOffsDggL )
+{
+ if (!nOffsDggL)
+ return;
+
+ sal_uInt64 nOldPos = rStCtrl.Tell();
+
+ if (nOffsDggL == rStCtrl.Seek(nOffsDggL))
+ {
+ DffRecordHeader aRecHd;
+ bool bOk = ReadDffRecordHeader(rStCtrl, aRecHd);
+
+ DffRecordHeader aDggAtomHd;
+ if (bOk && SeekToRec(rStCtrl, DFF_msofbtDgg, aRecHd.GetRecEndFilePos(), &aDggAtomHd))
+ {
+ aDggAtomHd.SeekToContent( rStCtrl );
+ sal_uInt32 nCurMaxShapeId;
+ sal_uInt32 nDummy;
+ rStCtrl.ReadUInt32( nCurMaxShapeId )
+ .ReadUInt32( mnIdClusters )
+ .ReadUInt32( nDummy )
+ .ReadUInt32( nDummy ); // nDrawingsSaved
+
+ if ( mnIdClusters-- > 2 )
+ {
+ const std::size_t nFIDCLsize = sizeof(sal_uInt32) * 2;
+ if ( aDggAtomHd.nRecLen == ( mnIdClusters * nFIDCLsize + 16 ) )
+ {
+ sal_uInt64 nMaxEntriesPossible = rStCtrl.remainingSize() / nFIDCLsize;
+ SAL_WARN_IF(nMaxEntriesPossible < mnIdClusters,
+ "filter.ms", "FIDCL list longer than remaining bytes, ppt or parser is wrong");
+ mnIdClusters = std::min(nMaxEntriesPossible, static_cast<sal_uInt64>(mnIdClusters));
+
+ maFidcls.resize(mnIdClusters);
+ for (sal_uInt32 i = 0; i < mnIdClusters; ++i)
+ {
+ sal_uInt32 cspidCur; ///< number of SPIDs used so far
+ rStCtrl.ReadUInt32( maFidcls[ i ].dgid )
+ .ReadUInt32( cspidCur );
+ }
+ }
+ }
+ }
+ }
+ rStCtrl.Seek( nOldPos );
+}
+
+void SvxMSDffManager::CheckTxBxStoryChain()
+{
+ m_xShapeInfosById.reset(new SvxMSDffShapeInfos_ById);
+ // mangle old Info array, sorted by nTxBxComp
+ sal_uInt32 nChain = std::numeric_limits<sal_uInt32>::max();
+ bool bSetReplaceFALSE = false;
+ for (SvxMSDffShapeInfos_ByTxBxComp::iterator iter =
+ m_xShapeInfosByTxBxComp->begin(),
+ mark = m_xShapeInfosByTxBxComp->begin();
+ iter != m_xShapeInfosByTxBxComp->end(); ++iter)
+ {
+ std::shared_ptr<SvxMSDffShapeInfo> const pObj = *iter;
+ if( pObj->nTxBxComp )
+ {
+ // group change?
+ // the text id also contains an internal drawing container id
+ // to distinguish between text id of drawing objects in different
+ // drawing containers.
+ if( nChain != pObj->nTxBxComp )
+ {
+ // reset mark and helper flag
+ mark = iter;
+ nChain = pObj->nTxBxComp;
+ bSetReplaceFALSE = !pObj->bReplaceByFly;
+ }
+ else if( !pObj->bReplaceByFly )
+ {
+ // object that must NOT be replaced by frame?
+ bSetReplaceFALSE = true;
+ // maybe reset flags in start of group
+ for (SvxMSDffShapeInfos_ByTxBxComp::iterator itemp = mark;
+ itemp != iter; ++itemp)
+ {
+ (*itemp)->bReplaceByFly = false;
+ }
+ }
+
+ if( bSetReplaceFALSE )
+ {
+ pObj->bReplaceByFly = false;
+ }
+ }
+ // copy all Shape Info objects to m_xShapeInfosById, sorted by nShapeId
+ pObj->nTxBxComp = pObj->nTxBxComp & 0xFFFF0000;
+ m_xShapeInfosById->insert( pObj );
+ }
+ // free original array but don't free its elements
+ m_xShapeInfosByTxBxComp.reset();
+}
+
+
+/*****************************************************************************
+
+ Reading the Shape-Infos in the Ctor:
+ ---------------------------------
+ remembering the Shape-Ids and the associated Blip-Numbers and TextBox-Infos
+ ========= ============ =============
+ and remembering the File-Offsets for each Blip
+ ============
+******************************************************************************/
+void SvxMSDffManager::GetCtrlData(sal_uInt32 nOffsDggL)
+{
+ // position control stream
+ if (!checkSeek(rStCtrl, nOffsDggL))
+ return;
+
+ sal_uInt8 nVer;
+ sal_uInt16 nInst;
+ sal_uInt16 nFbt;
+ sal_uInt32 nLength;
+ if( !ReadCommonRecordHeader( rStCtrl, nVer, nInst, nFbt, nLength ) ) return;
+
+ sal_uInt64 nPos = nOffsDggL + DFF_COMMON_RECORD_HEADER_SIZE;
+
+ // case A: first Drawing Group Container, then n times Drawing Container
+ if( DFF_msofbtDggContainer != nFbt )
+ return;
+
+ bool bOk;
+ GetDrawingGroupContainerData( rStCtrl, nLength );
+
+ sal_uInt64 nMaxStrPos = rStCtrl.TellEnd();
+
+ nPos += nLength;
+ sal_uInt16 nDrawingContainerId = 1;
+ do
+ {
+ if (!checkSeek(rStCtrl, nPos))
+ break;
+
+ bOk = ReadCommonRecordHeader( rStCtrl, nVer, nInst, nFbt, nLength ) && ( DFF_msofbtDgContainer == nFbt );
+
+ if( !bOk )
+ {
+ nPos++; // ????????? TODO: trying to get a one-hit wonder, this code should be rewritten...
+ if (nPos != rStCtrl.Seek(nPos))
+ break;
+ bOk = ReadCommonRecordHeader( rStCtrl, nVer, nInst, nFbt, nLength )
+ && ( DFF_msofbtDgContainer == nFbt );
+ }
+ if( bOk )
+ {
+ GetDrawingContainerData( rStCtrl, nLength, nDrawingContainerId );
+ }
+ nPos += DFF_COMMON_RECORD_HEADER_SIZE + nLength;
+ ++nDrawingContainerId;
+ }
+ while( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( nPos < nMaxStrPos ) && bOk );
+}
+
+
+// from here on: Drawing Group Container i.e. document-wide valid data
+
+void SvxMSDffManager::GetDrawingGroupContainerData( SvStream& rSt, sal_uInt32 nLenDgg )
+{
+ sal_uInt8 nVer;
+ sal_uInt16 nInst;
+ sal_uInt16 nFbt;
+ sal_uInt32 nLength;
+
+ sal_uInt32 nLenBStoreCont = 0, nLenFBSE = 0;
+ sal_uLong nRead = 0;
+
+ // search for a BStore Container
+ bool bOk = true;
+ do
+ {
+ if (!ReadCommonRecordHeader(rSt, nVer, nInst, nFbt, nLength))
+ return;
+ nRead += DFF_COMMON_RECORD_HEADER_SIZE + nLength;
+ if (DFF_msofbtBstoreContainer == nFbt)
+ {
+ nLenBStoreCont = nLength;
+ break;
+ }
+ bOk = checkSeek(rSt, rSt.Tell() + nLength);
+ }
+ while (bOk && nRead < nLenDgg);
+
+ if (!bOk || !nLenBStoreCont)
+ return;
+
+ // Read all atoms of the containers from the BStore container and store all
+ // relevant data of all contained FBSEs in out pointer array.
+ // We also count all found FBSEs in member nBLIPCount.
+
+ const sal_uLong nSkipBLIPLen = 20; // skip to get to the nBLIPLen
+ const sal_uLong nSkipBLIPPos = 4; // thereafter skip up to nBLIPPos
+
+ sal_uInt32 nBLIPLen = 0, nBLIPPos = 0;
+
+ nRead = 0;
+ do
+ {
+ if(!ReadCommonRecordHeader( rSt, nVer, nInst, nFbt, nLength)) return;
+ nRead += DFF_COMMON_RECORD_HEADER_SIZE + nLength;
+ if( DFF_msofbtBSE == nFbt && /* magic value from spec */ 0x2 == nVer )
+ {
+ nLenFBSE = nLength;
+ // is FBSE big enough for our data
+ bOk = ( nSkipBLIPLen + 4 + nSkipBLIPPos + 4 <= nLenFBSE );
+
+ if (bOk)
+ {
+ rSt.SeekRel( nSkipBLIPLen );
+ rSt.ReadUInt32( nBLIPLen );
+ rSt.SeekRel( nSkipBLIPPos );
+ rSt.ReadUInt32( nBLIPPos );
+ bOk = rSt.GetError() == ERRCODE_NONE;
+
+ nLength -= nSkipBLIPLen+ 4 + nSkipBLIPPos + 4;
+ }
+
+ if (bOk)
+ {
+ // specialty:
+ // If nBLIPLen is less than nLenFBSE AND nBLIPPos is NULL,
+ // then we assume, that the image is in FBSE!
+ if( (!nBLIPPos) && (nBLIPLen < nLenFBSE) )
+ nBLIPPos = rSt.Tell() + 4;
+
+ if( USHRT_MAX == nBLIPCount )
+ nBLIPCount = 1;
+ else
+ nBLIPCount++;
+
+ // now save the info for later access
+ m_pBLIPInfos->push_back(SvxMSDffBLIPInfo(nBLIPPos));
+ }
+ if (!checkSeek(rSt, rSt.Tell() + nLength))
+ return; // invalid offset
+ }
+ else return; // invalid input
+ }
+ while( nRead < nLenBStoreCont );
+}
+
+
+// from now on: Drawing Container which means Pages (Sheet, Slide) - wide valid data
+// ================= ======
+
+void SvxMSDffManager::GetDrawingContainerData( SvStream& rSt, sal_uInt32 nLenDg,
+ sal_uInt16 nDrawingContainerId )
+{
+ sal_uInt8 nVer;sal_uInt16 nInst;sal_uInt16 nFbt;sal_uInt32 nLength;
+
+ sal_uLong nReadDg = 0;
+
+ // We are now in a drawing container (one per each page) and
+ // we now have to iterate through all contained shape group containers
+ do
+ {
+ if (!ReadCommonRecordHeader(rSt, nVer, nInst, nFbt, nLength))
+ return;
+ nReadDg += DFF_COMMON_RECORD_HEADER_SIZE;
+ // Patriarch found (the upmost shape group container) ?
+ if (DFF_msofbtSpgrContainer == nFbt)
+ {
+ if (!GetShapeGroupContainerData(rSt, nLength, true, nDrawingContainerId))
+ return;
+ }
+ // empty Shape Container ? (outside of shape group container)
+ else if (DFF_msofbtSpContainer == nFbt)
+ {
+ if (!GetShapeContainerData(
+ rSt, nLength, std::numeric_limits<sal_uInt64>::max(), nDrawingContainerId))
+ return;
+ }
+ else
+ {
+ if (!checkSeek(rSt, rSt.Tell() + nLength))
+ return;
+ }
+ nReadDg += nLength;
+ }
+ while( nReadDg < nLenDg );
+}
+
+bool SvxMSDffManager::GetShapeGroupContainerData( SvStream& rSt,
+ sal_uInt32 nLenShapeGroupCont,
+ bool bPatriarch,
+ sal_uInt16 nDrawingContainerId )
+{
+ sal_uInt8 nVer;sal_uInt16 nInst;sal_uInt16 nFbt;sal_uInt32 nLength;
+ sal_uInt64 nStartShapeGroupCont = rSt.Tell();
+ // We are now in a shape group container (conditionally multiple per page)
+ // and we now have to iterate through all contained shape containers
+ bool bFirst = !bPatriarch;
+ sal_uLong nReadSpGrCont = 0;
+ do
+ {
+ if( !ReadCommonRecordHeader( rSt, nVer, nInst, nFbt, nLength ) )
+ return false;
+ nReadSpGrCont += DFF_COMMON_RECORD_HEADER_SIZE;
+ // Shape Container?
+ if( DFF_msofbtSpContainer == nFbt )
+ {
+ sal_uInt64 nGroupOffs = bFirst ? nStartShapeGroupCont - DFF_COMMON_RECORD_HEADER_SIZE : std::numeric_limits<sal_uInt64>::max();
+ if ( !GetShapeContainerData( rSt, nLength, nGroupOffs, nDrawingContainerId ) )
+ return false;
+ bFirst = false;
+ }
+ // nested shape group container ?
+ else if( DFF_msofbtSpgrContainer == nFbt )
+ {
+ if ( !GetShapeGroupContainerData( rSt, nLength, false, nDrawingContainerId ) )
+ return false;
+ }
+ else
+ {
+ if (!checkSeek(rSt, rSt.Tell() + nLength))
+ return false;
+ }
+ nReadSpGrCont += nLength;
+ }
+ while( nReadSpGrCont < nLenShapeGroupCont );
+ // position the stream correctly
+ rSt.Seek( nStartShapeGroupCont + nLenShapeGroupCont );
+ return true;
+}
+
+bool SvxMSDffManager::GetShapeContainerData( SvStream& rSt,
+ sal_uInt32 nLenShapeCont,
+ sal_uInt64 nPosGroup,
+ sal_uInt16 nDrawingContainerId )
+{
+ sal_uInt8 nVer;sal_uInt16 nInst;sal_uInt16 nFbt;sal_uInt32 nLength;
+ sal_uInt64 nStartShapeCont = rSt.Tell();
+
+ // We are in a shape container (possibly more than one per shape group) and we now
+ // have to fetch the shape id and file position (to be able to access them again later)
+ // and the first BStore reference (if present).
+ sal_uInt32 nLenShapePropTbl = 0;
+ sal_uLong nReadSpCont = 0;
+
+ // Store file offset of the shape containers or respectively the group(!).
+ sal_uInt64 nStartOffs = (std::numeric_limits<sal_uInt64>::max() > nPosGroup) ?
+ nPosGroup : nStartShapeCont - DFF_COMMON_RECORD_HEADER_SIZE;
+ SvxMSDffShapeInfo aInfo( nStartOffs );
+
+ // Can the shape be replaced with a frame?
+ // (provided that it is a TextBox and the text is not rotated)
+ bool bCanBeReplaced = nPosGroup >= std::numeric_limits<sal_uInt64>::max();
+
+ // we don't know yet whether it's a TextBox
+ MSO_SPT eShapeType = mso_sptNil;
+
+ // analyze Shape
+
+ do
+ {
+ if(!ReadCommonRecordHeader( rSt, nVer, nInst, nFbt, nLength)) return false;
+ nReadSpCont += DFF_COMMON_RECORD_HEADER_SIZE;
+ // FSP ?
+ if( ( DFF_msofbtSp == nFbt ) && ( 4 <= nLength ) )
+ {
+ // we've found the FSP: note Shape Type and Id!
+ eShapeType = static_cast<MSO_SPT>(nInst);
+ rSt.ReadUInt32( aInfo.nShapeId );
+ rSt.SeekRel( nLength - 4 );
+ nReadSpCont += nLength;
+ }
+ else if( DFF_msofbtOPT == nFbt ) // Shape Property Table ?
+ {
+ // We've found the Property Table:
+ // search for the Blip Property!
+ sal_uLong nPropRead = 0;
+ nLenShapePropTbl = nLength;
+ auto nStartShapePropTbl = rSt.Tell();
+ do
+ {
+ sal_uInt16 nPropId(0);
+ sal_uInt32 nPropVal(0);
+
+ rSt.ReadUInt16( nPropId )
+ .ReadUInt32( nPropVal );
+ nPropRead += 6;
+
+ switch( nPropId )
+ {
+ case DFF_Prop_txflTextFlow :
+ //Writer can now handle vertical textflows in its
+ //native frames, to only need to do this for the
+ //other two formats
+
+ //Writer will handle all textflow except BtoT
+ if (GetSvxMSDffSettings() &
+ (SVXMSDFF_SETTINGS_IMPORT_PPT |
+ SVXMSDFF_SETTINGS_IMPORT_EXCEL))
+ {
+ if( 0 != nPropVal )
+ bCanBeReplaced = false;
+ }
+ else if (
+ (nPropVal != mso_txflHorzN) &&
+ (nPropVal != mso_txflTtoBA)
+ )
+ {
+ bCanBeReplaced = false;
+ }
+ break;
+ case DFF_Prop_cdirFont :
+ //Writer can now handle right to left and left
+ //to right in its native frames, so only do
+ //this for the other two formats.
+ if (GetSvxMSDffSettings() &
+ (SVXMSDFF_SETTINGS_IMPORT_PPT |
+ SVXMSDFF_SETTINGS_IMPORT_EXCEL))
+ {
+ if( 0 != nPropVal )
+ bCanBeReplaced = false;
+ }
+ break;
+ case DFF_Prop_Rotation :
+ if( 0 != nPropVal )
+ bCanBeReplaced = false;
+ break;
+
+ case DFF_Prop_gtextFStrikethrough :
+ if( ( 0x20002000 & nPropVal ) == 0x20002000 )
+ bCanBeReplaced = false;
+ break;
+
+ case DFF_Prop_fc3DLightFace :
+ if( ( 0x00080008 & nPropVal ) == 0x00080008 )
+ bCanBeReplaced = false;
+ break;
+
+ case DFF_Prop_WrapText :
+ //TODO: eWrapMode = (MSO_WrapMode)nPropVal;
+ break;
+
+ default:
+ {
+ // is the Bit set and valid?
+ if( 0x4000 == ( nPropId & 0xC000 ) )
+ {
+ // Blip Property found: remember BStore Idx!
+ nPropRead = nLenShapePropTbl;
+ }
+ else if( 0x8000 & nPropId )
+ {
+ // complex Prop found:
+ // Length is always 6. The length of the appended extra data
+ // after the actual prop table is of different size.
+ nPropVal = 6;
+ }
+ }
+ break;
+ }
+ }
+ while (rSt.good() && nPropRead < nLenShapePropTbl);
+ rSt.Seek( nStartShapePropTbl + nLenShapePropTbl );
+ nReadSpCont += nLenShapePropTbl;
+ }
+ else if( ( DFF_msofbtClientTextbox == nFbt ) && ( 4 == nLength ) ) // Text-Box-Story-Entry found
+ {
+ rSt.ReadUInt32( aInfo.nTxBxComp );
+ // Add internal drawing container id to text id.
+ // Note: The text id uses the first two bytes, while the internal
+ // drawing container id used the second two bytes.
+ aInfo.nTxBxComp = ( aInfo.nTxBxComp & 0xFFFF0000 ) +
+ nDrawingContainerId;
+ DBG_ASSERT( (aInfo.nTxBxComp & 0x0000FFFF) == nDrawingContainerId,
+ "<SvxMSDffManager::GetShapeContainerData(..)> - internal drawing container Id could not be correctly merged into DFF_msofbtClientTextbox value." );
+ }
+ else
+ {
+ if (!checkSeek(rSt, rSt.Tell() + nLength))
+ {
+ SAL_WARN("filter.ms", "remaining record longer than available data, ppt or parser is wrong");
+ break;
+ }
+ nReadSpCont += nLength;
+ }
+ }
+ while( nReadSpCont < nLenShapeCont );
+
+
+ // Now possibly store the information for subsequent accesses to the shape
+
+ if( aInfo.nShapeId )
+ {
+ // Possibly allow replacement of textboxes with frames
+ if( bCanBeReplaced
+ && aInfo.nTxBxComp
+ && (
+ ( eShapeType == mso_sptTextSimple )
+ || ( eShapeType == mso_sptTextBox )
+ || ( eShapeType == mso_sptRectangle )
+ || ( eShapeType == mso_sptRoundRectangle )
+ ) )
+ {
+ aInfo.bReplaceByFly = true;
+ }
+ m_xShapeInfosByTxBxComp->insert(std::make_shared<SvxMSDffShapeInfo>(
+ aInfo));
+ m_aShapeOrders.push_back(std::make_unique<SvxMSDffShapeOrder>(
+ aInfo.nShapeId ));
+ }
+
+ // and position the Stream correctly again
+ rSt.Seek( nStartShapeCont + nLenShapeCont );
+ return true;
+}
+
+
+/*****************************************************************************
+
+ Access to a shape at runtime (via the Shape-Id)
+ ----------------------------
+******************************************************************************/
+bool SvxMSDffManager::GetShape(sal_uLong nId, SdrObject*& rpShape,
+ SvxMSDffImportData& rData)
+{
+ auto const pTmpRec = std::make_shared<SvxMSDffShapeInfo>(0, nId);
+
+ SvxMSDffShapeInfos_ById::const_iterator const it =
+ m_xShapeInfosById->find(pTmpRec);
+ if (it == m_xShapeInfosById->end())
+ return false;
+
+ // Possibly delete old error flag.
+ if( rStCtrl.GetError() )
+ rStCtrl.ResetError();
+ // store FilePos of the stream(s)
+ sal_uInt64 nOldPosCtrl = rStCtrl.Tell();
+ sal_uInt64 nOldPosData = pStData ? pStData->Tell() : nOldPosCtrl;
+ // jump to the shape in the control stream
+ sal_uInt64 const nFilePos((*it)->nFilePos);
+ bool bSeeked = (nFilePos == rStCtrl.Seek(nFilePos));
+
+ // if it failed, reset error statusF
+ if (!bSeeked || rStCtrl.GetError())
+ rStCtrl.ResetError();
+ else
+ rpShape = ImportObj( rStCtrl, rData, rData.aParentRect, rData.aParentRect, /*nCalledByGroup*/0, /*pShapeId*/nullptr );
+
+ // restore old FilePos of the stream(s)
+ rStCtrl.Seek( nOldPosCtrl );
+ if( &rStCtrl != pStData && pStData )
+ pStData->Seek( nOldPosData );
+ return ( nullptr != rpShape );
+}
+
+
+/** Access to a BLIP at runtime (if the Blip-Number is already known)
+ */
+bool SvxMSDffManager::GetBLIP( sal_uLong nIdx_, Graphic& rGraphic, tools::Rectangle* pVisArea )
+{
+ if (!pStData)
+ return false;
+
+ bool bOk = false; // initialize result variable
+
+ // check if a graphic for this blipId is already imported
+ if (nIdx_)
+ {
+ auto iter = aEscherBlipCache.find(nIdx_);
+
+ if (iter != aEscherBlipCache.end())
+ {
+ /* if this entry is available */
+ rGraphic = iter->second;
+ if (rGraphic.GetType() != GraphicType::NONE)
+ bOk = true;
+ else
+ aEscherBlipCache.erase(iter);
+ }
+ }
+
+ if (!bOk)
+ {
+ sal_uInt16 nIdx = sal_uInt16( nIdx_ );
+ if (!nIdx || (m_pBLIPInfos->size() < nIdx))
+ return false;
+
+ // possibly delete old error flag(s)
+ if( rStCtrl.GetError() )
+ rStCtrl.ResetError();
+ if( ( &rStCtrl != pStData )
+ && pStData->GetError() )
+ pStData->ResetError();
+
+ // remember FilePos of the stream(s)
+ sal_uInt64 nOldPosCtrl = rStCtrl.Tell();
+ sal_uInt64 nOldPosData = pStData->Tell();
+
+ // fetch matching info struct out of the pointer array
+ SvxMSDffBLIPInfo& rInfo = (*m_pBLIPInfos)[ nIdx-1 ];
+ // jump to the BLIP atom in the data stream
+ bOk = checkSeek(*pStData, rInfo.nFilePos);
+ // possibly reset error status
+ if (!bOk || pStData->GetError())
+ pStData->ResetError();
+ else
+ bOk = GetBLIPDirect( *pStData, rGraphic, pVisArea );
+ if( pStData2 && !bOk )
+ {
+ // Error, but the is a second chance: There is a second
+ // data stream in which the graphic could be stored!
+ if( pStData2->GetError() )
+ pStData2->ResetError();
+ sal_uInt64 nOldPosData2 = pStData2->Tell();
+ // jump to the BLIP atom in the second data stream
+ bOk = checkSeek(*pStData2, rInfo.nFilePos);
+ // reset error status if necessary
+ if (!bOk || pStData2->GetError())
+ pStData2->ResetError();
+ else
+ bOk = GetBLIPDirect( *pStData2, rGraphic, pVisArea );
+ // restore of FilePos of the second data stream
+ pStData2->Seek( nOldPosData2 );
+ }
+ // restore old FilePos of the stream(s)
+ rStCtrl.Seek( nOldPosCtrl );
+ if( &rStCtrl != pStData )
+ pStData->Seek( nOldPosData );
+
+ if (bOk)
+ {
+ // create new BlipCacheEntry for this graphic
+ aEscherBlipCache.insert(std::make_pair(nIdx_, rGraphic));
+ }
+ }
+
+ return bOk;
+}
+
+/* access to a BLIP at runtime (with correctly positioned stream)
+ ---------------------------------
+******************************************************************************/
+bool SvxMSDffManager::GetBLIPDirect( SvStream& rBLIPStream, Graphic& rData, tools::Rectangle* pVisArea )
+{
+ sal_uInt64 nOldPos = rBLIPStream.Tell();
+
+ ErrCode nRes = ERRCODE_GRFILTER_OPENERROR; // initialize error variable
+
+ // check whether it's really a BLIP
+ sal_uInt32 nLength;
+ sal_uInt16 nInst, nFbt( 0 );
+ sal_uInt8 nVer;
+ if( ReadCommonRecordHeader( rBLIPStream, nVer, nInst, nFbt, nLength) && ( 0xF018 <= nFbt ) && ( 0xF117 >= nFbt ) )
+ {
+ Size aMtfSize100;
+ bool bMtfBLIP = false;
+ bool bZCodecCompression = false;
+ // now position it exactly at the beginning of the embedded graphic
+ sal_uLong nSkip = (nInst & 0x0001) ? 32 : 16;
+ const OfficeArtBlipRecInstance aRecInstanse = OfficeArtBlipRecInstance(nInst & 0xFFFE);
+ switch (aRecInstanse)
+ {
+ case OfficeArtBlipRecInstance::EMF:
+ case OfficeArtBlipRecInstance::WMF:
+ case OfficeArtBlipRecInstance::PICT:
+ {
+ rBLIPStream.SeekRel(nSkip + 20);
+
+ // read in size of metafile in English Metric Units (EMUs)
+ sal_Int32 width(0), height(0);
+ rBLIPStream.ReadInt32(width).ReadInt32(height);
+ aMtfSize100.setWidth(width);
+ aMtfSize100.setHeight(height);
+
+ // 1 EMU = 1/360,000 of a centimeter
+ // scale to 1/100mm
+ aMtfSize100.setWidth(aMtfSize100.Width() / 360);
+ aMtfSize100.setHeight(aMtfSize100.Height() / 360);
+
+ if (pVisArea) // seem that we currently are skipping the visarea position
+ *pVisArea = tools::Rectangle(Point(), aMtfSize100);
+
+ // skip rest of header
+ nSkip = 6;
+ bMtfBLIP = bZCodecCompression = true;
+ }
+ break;
+ case OfficeArtBlipRecInstance::JPEG_RGB:
+ case OfficeArtBlipRecInstance::JPEG_CMYK:
+ case OfficeArtBlipRecInstance::PNG:
+ case OfficeArtBlipRecInstance::DIB:
+ case OfficeArtBlipRecInstance::TIFF:
+ nSkip += 1; // Skip one byte tag
+ break;
+ }
+ rBLIPStream.SeekRel( nSkip );
+
+ SvStream* pGrStream = &rBLIPStream;
+ std::unique_ptr<SvMemoryStream> xOut;
+ if( bZCodecCompression )
+ {
+ xOut.reset(new SvMemoryStream( 0x8000, 0x4000 ));
+ ZCodec aZCodec( 0x8000, 0x8000 );
+ aZCodec.BeginCompression();
+ aZCodec.Decompress( rBLIPStream, *xOut );
+ aZCodec.EndCompression();
+ xOut->Seek( STREAM_SEEK_TO_BEGIN );
+ xOut->SetResizeOffset( 0 ); // sj: #i102257# setting ResizeOffset of 0 prevents from seeking
+ // behind the stream end (allocating too much memory)
+ pGrStream = xOut.get();
+ }
+
+#ifdef DEBUG_FILTER_MSDFFIMP
+ // extract graphics from ole storage into "dbggfxNNN.*"
+ static sal_Int32 nGrfCount;
+
+ OUString aFileName = "dbggfx" + OUString::number(nGrfCount++);
+ switch (aRecInstanse)
+ {
+ case OfficeArtBlipRecInstance::WMF:
+ aFileName += ".wmf";
+ break;
+ case OfficeArtBlipRecInstance::EMF:
+ aFileName += ".emf";
+ break;
+ case OfficeArtBlipRecInstance::PICT:
+ aFileName += ".pct";
+ break;
+ case OfficeArtBlipRecInstance::JPEG_RGB:
+ case OfficeArtBlipRecInstance::JPEG_CMYK:
+ aFileName += ".jpg";
+ break;
+ case OfficeArtBlipRecInstance::PNG:
+ aFileName += ".png";
+ break;
+ case OfficeArtBlipRecInstance::DIB:
+ aFileName += ".bmp";
+ break;
+ case OfficeArtBlipRecInstance::TIFF:
+ aFileName += ".tif";
+ break;
+ }
+
+
+ OUString aURLStr;
+ if( osl::FileBase::getFileURLFromSystemPath( Application::GetAppFileName(), aURLStr ) == osl::FileBase::E_None )
+ {
+ INetURLObject aURL( aURLStr );
+
+ aURL.removeSegment();
+ aURL.removeFinalSlash();
+ aURL.Append( aFileName );
+
+ aURLStr = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ SAL_INFO("filter.ms", "dumping " << aURLStr);
+
+ std::unique_ptr<SvStream> pDbgOut(::utl::UcbStreamHelper::CreateStream(aURLStr, StreamMode::TRUNC | StreamMode::WRITE));
+
+ if( pDbgOut )
+ {
+ if ( bZCodecCompression )
+ {
+ pDbgOut->WriteBytes(xOut->GetData(), xOut->TellEnd());
+ xOut->Seek(STREAM_SEEK_TO_BEGIN);
+ }
+ else
+ {
+ sal_Int32 nDbgLen = nLength - nSkip;
+ if ( nDbgLen )
+ {
+ std::vector<char> aData(nDbgLen);
+ pGrStream->ReadBytes(aData.data(), nDbgLen);
+ pDbgOut->WriteBytes(aData.data(), nDbgLen);
+ pGrStream->SeekRel(-nDbgLen);
+ }
+ }
+ }
+ }
+#endif
+ if (aRecInstanse == OfficeArtBlipRecInstance::DIB)
+ { // getting the DIBs immediately
+ Bitmap aNew;
+ if( ReadDIB(aNew, *pGrStream, false) )
+ {
+ rData = Graphic(BitmapEx(aNew));
+ nRes = ERRCODE_NONE;
+ }
+ }
+ else
+ { // and unleash our filter
+ GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
+ // ImportUnloadedGraphic() may simply read the entire rest of the stream,
+ // which may be very large if the whole document is large. Limit the read
+ // size to the size of this record.
+ sal_uInt64 maxSize = pGrStream == &rBLIPStream ? nLength : 0;
+ Graphic aGraphic;
+
+ // Size available in metafile header.
+ if (aMtfSize100.getWidth() && aMtfSize100.getHeight())
+ aGraphic = rGF.ImportUnloadedGraphic(*pGrStream, maxSize, &aMtfSize100);
+ else
+ aGraphic = rGF.ImportUnloadedGraphic(*pGrStream, maxSize);
+
+ if (!aGraphic.IsNone())
+ {
+ rData = aGraphic;
+ nRes = ERRCODE_NONE;
+ }
+ else
+ nRes = rGF.ImportGraphic( rData, u"", *pGrStream );
+
+ // SJ: I40472, sometimes the aspect ratio (aMtfSize100) does not match and we get scaling problems,
+ // then it is better to use the prefsize that is stored within the metafile. Bug #72846# for what the
+ // scaling has been implemented does not happen anymore.
+ //
+ // For pict graphics we will furthermore scale the metafile, because font scaling leads to error if the
+ // dxarray is empty (this has been solved in wmf/emf but not for pict)
+ if (bMtfBLIP && (ERRCODE_NONE == nRes) && (rData.GetType() == GraphicType::GdiMetafile)
+ && (aRecInstanse == OfficeArtBlipRecInstance::PICT))
+ {
+ if ( ( aMtfSize100.Width() >= 1000 ) && ( aMtfSize100.Height() >= 1000 ) )
+ { // #75956#, scaling does not work properly, if the graphic is less than 1cm
+ GDIMetaFile aMtf( rData.GetGDIMetaFile() );
+ const Size aOldSize( aMtf.GetPrefSize() );
+
+ if( aOldSize.Width() && ( aOldSize.Width() != aMtfSize100.Width() ) &&
+ aOldSize.Height() && ( aOldSize.Height() != aMtfSize100.Height() ) )
+ {
+ aMtf.Scale( static_cast<double>(aMtfSize100.Width()) / aOldSize.Width(),
+ static_cast<double>(aMtfSize100.Height()) / aOldSize.Height() );
+ aMtf.SetPrefSize( aMtfSize100 );
+ aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ rData = aMtf;
+ }
+ }
+ }
+ }
+ // reset error status if necessary
+ if ( ERRCODE_IO_PENDING == pGrStream->GetError() )
+ pGrStream->ResetError();
+ }
+ rBLIPStream.Seek( nOldPos ); // restore old FilePos of the stream
+
+ return ( ERRCODE_NONE == nRes ); // return result
+}
+
+/* also static */
+bool SvxMSDffManager::ReadCommonRecordHeader(SvStream& rSt,
+ sal_uInt8& rVer, sal_uInt16& rInst, sal_uInt16& rFbt, sal_uInt32& rLength)
+{
+ sal_uInt16 nTmp(0);
+ rSt.ReadUInt16( nTmp ).ReadUInt16( rFbt ).ReadUInt32( rLength );
+ rVer = sal::static_int_cast< sal_uInt8 >(nTmp & 15);
+ rInst = nTmp >> 4;
+ if (!rSt.good())
+ return false;
+ if (rLength > nMaxLegalDffRecordLength)
+ return false;
+ return true;
+}
+
+void SvxMSDffManager::ProcessClientAnchor(SvStream& rStData, sal_uInt32 nDatLen,
+ std::unique_ptr<char[]>& rpBuff, sal_uInt32& rBuffLen )
+{
+ if( nDatLen )
+ {
+ rBuffLen = std::min(rStData.remainingSize(), static_cast<sal_uInt64>(nDatLen));
+ rpBuff.reset( new char[rBuffLen] );
+ rBuffLen = rStData.ReadBytes(rpBuff.get(), rBuffLen);
+ }
+}
+
+void SvxMSDffManager::ProcessClientData(SvStream& rStData, sal_uInt32 nDatLen,
+ std::unique_ptr<char[]>& rpBuff, sal_uInt32& rBuffLen )
+{
+ if( nDatLen )
+ {
+ rBuffLen = std::min(rStData.remainingSize(), static_cast<sal_uInt64>(nDatLen));
+ rpBuff.reset( new char[rBuffLen] );
+ rBuffLen = rStData.ReadBytes(rpBuff.get(), rBuffLen);
+ }
+}
+
+
+void SvxMSDffManager::ProcessClientAnchor2( SvStream& /* rSt */, DffRecordHeader& /* rHd */ , DffObjData& /* rObj */ )
+{
+ // will be overridden by SJ in Draw
+}
+
+bool SvxMSDffManager::GetOLEStorageName( sal_uInt32, OUString&, tools::SvRef<SotStorage>&, uno::Reference < embed::XStorage >& ) const
+{
+ return false;
+}
+
+bool SvxMSDffManager::ShapeHasText( sal_uLong /* nShapeId */, sal_uLong /* nFilePos */ ) const
+{
+ return true;
+}
+
+// #i32596# - add new parameter <_nCalledByGroup>
+SdrObject* SvxMSDffManager::ImportOLE( sal_uInt32 nOLEId,
+ const Graphic& rGrf,
+ const tools::Rectangle& rBoundRect,
+ const tools::Rectangle& rVisArea,
+ const int /* _nCalledByGroup */ ) const
+{
+ SdrObject* pRet = nullptr;
+ OUString sStorageName;
+ tools::SvRef<SotStorage> xSrcStg;
+ ErrCode nError = ERRCODE_NONE;
+ uno::Reference < embed::XStorage > xDstStg;
+ if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg ))
+ pRet = CreateSdrOLEFromStorage(
+ *GetModel(),
+ sStorageName,
+ xSrcStg,
+ xDstStg,
+ rGrf,
+ rBoundRect,
+ rVisArea,
+ pStData,
+ nError,
+ nSvxMSDffOLEConvFlags,
+ embed::Aspects::MSOLE_CONTENT,
+ maBaseURL);
+ return pRet;
+}
+
+bool SvxMSDffManager::MakeContentStream( SotStorage * pStor, const GDIMetaFile & rMtf )
+{
+ tools::SvRef<SotStorageStream> xStm = pStor->OpenSotStream(SVEXT_PERSIST_STREAM);
+ xStm->SetVersion( pStor->GetVersion() );
+ xStm->SetBufferSize( 8192 );
+
+ Impl_OlePres aEle;
+ // Convert the size in 1/100 mm
+ // If a not applicable MapUnit (device dependent) is used,
+ // SV tries to guess a best match for the right value
+ Size aSize = rMtf.GetPrefSize();
+ const MapMode& aMMSrc = rMtf.GetPrefMapMode();
+ MapMode aMMDst( MapUnit::Map100thMM );
+ aSize = OutputDevice::LogicToLogic( aSize, aMMSrc, aMMDst );
+ aEle.SetSize( aSize );
+ aEle.SetAspect( ASPECT_CONTENT );
+ aEle.SetAdviseFlags( 2 );
+ aEle.SetMtf( rMtf );
+ aEle.Write( *xStm );
+
+ xStm->SetBufferSize( 0 );
+ return xStm->GetError() == ERRCODE_NONE;
+}
+
+namespace {
+
+struct ClsIDs {
+ sal_uInt32 nId;
+ const char* pSvrName;
+ const char* pDspName;
+};
+
+}
+
+const ClsIDs aClsIDs[] = {
+
+ { 0x000212F0, "MSWordArt", "Microsoft Word Art" },
+ { 0x000212F0, "MSWordArt.2", "Microsoft Word Art 2.0" },
+
+ // MS Apps
+ { 0x00030000, "ExcelWorksheet", "Microsoft Excel Worksheet" },
+ { 0x00030001, "ExcelChart", "Microsoft Excel Chart" },
+ { 0x00030002, "ExcelMacrosheet", "Microsoft Excel Macro" },
+ { 0x00030003, "WordDocument", "Microsoft Word Document" },
+ { 0x00030004, "MSPowerPoint", "Microsoft PowerPoint" },
+ { 0x00030005, "MSPowerPointSho", "Microsoft PowerPoint Slide Show"},
+ { 0x00030006, "MSGraph", "Microsoft Graph" },
+ { 0x00030007, "MSDraw", "Microsoft Draw" },
+ { 0x00030008, "Note-It", "Microsoft Note-It" },
+ { 0x00030009, "WordArt", "Microsoft Word Art" },
+ { 0x0003000a, "PBrush", "Microsoft PaintBrush Picture" },
+ { 0x0003000b, "Equation", "Microsoft Equation Editor" },
+ { 0x0003000c, "Package", "Package" },
+ { 0x0003000d, "SoundRec", "Sound" },
+ { 0x0003000e, "MPlayer", "Media Player" },
+ // MS Demos
+ { 0x0003000f, "ServerDemo", "OLE 1.0 Server Demo" },
+ { 0x00030010, "Srtest", "OLE 1.0 Test Demo" },
+ { 0x00030011, "SrtInv", "OLE 1.0 Inv Demo" },
+ { 0x00030012, "OleDemo", "OLE 1.0 Demo" },
+
+ // Coromandel / Dorai Swamy / 718-793-7963
+ { 0x00030013, "CoromandelIntegra", "Coromandel Integra" },
+ { 0x00030014, "CoromandelObjServer","Coromandel Object Server" },
+
+ // 3-d Visions Corp / Peter Hirsch / 310-325-1339
+ { 0x00030015, "StanfordGraphics", "Stanford Graphics" },
+
+ // Deltapoint / Nigel Hearne / 408-648-4000
+ { 0x00030016, "DGraphCHART", "DeltaPoint Graph Chart" },
+ { 0x00030017, "DGraphDATA", "DeltaPoint Graph Data" },
+
+ // Corel / Richard V. Woodend / 613-728-8200 x1153
+ { 0x00030018, "PhotoPaint", "Corel PhotoPaint" },
+ { 0x00030019, "CShow", "Corel Show" },
+ { 0x0003001a, "CorelChart", "Corel Chart" },
+ { 0x0003001b, "CDraw", "Corel Draw" },
+
+ // Inset Systems / Mark Skiba / 203-740-2400
+ { 0x0003001c, "HJWIN1.0", "Inset Systems" },
+
+ // Mark V Systems / Mark McGraw / 818-995-7671
+ { 0x0003001d, "ObjMakerOLE", "MarkV Systems Object Maker" },
+
+ // IdentiTech / Mike Gilger / 407-951-9503
+ { 0x0003001e, "FYI", "IdentiTech FYI" },
+ { 0x0003001f, "FYIView", "IdentiTech FYI Viewer" },
+
+ // Inventa Corporation / Balaji Varadarajan / 408-987-0220
+ { 0x00030020, "Stickynote", "Inventa Sticky Note" },
+
+ // ShapeWare Corp. / Lori Pearce / 206-467-6723
+ { 0x00030021, "ShapewareVISIO10", "Shapeware Visio 1.0" },
+ { 0x00030022, "ImportServer", "Spaheware Import Server" },
+
+ // test app SrTest
+ { 0x00030023, "SrvrTest", "OLE 1.0 Server Test" },
+
+ // test app ClTest. Doesn't really work as a server but is in reg db
+ { 0x00030025, "Cltest", "OLE 1.0 Client Test" },
+
+ // Microsoft ClipArt Gallery Sherry Larsen-Holmes
+ { 0x00030026, "MS_ClipArt_Gallery", "Microsoft ClipArt Gallery" },
+ // Microsoft Project Cory Reina
+ { 0x00030027, "MSProject", "Microsoft Project" },
+
+ // Microsoft Works Chart
+ { 0x00030028, "MSWorksChart", "Microsoft Works Chart" },
+
+ // Microsoft Works Spreadsheet
+ { 0x00030029, "MSWorksSpreadsheet", "Microsoft Works Spreadsheet" },
+
+ // AFX apps - Dean McCrory
+ { 0x0003002A, "MinSvr", "AFX Mini Server" },
+ { 0x0003002B, "HierarchyList", "AFX Hierarchy List" },
+ { 0x0003002C, "BibRef", "AFX BibRef" },
+ { 0x0003002D, "MinSvrMI", "AFX Mini Server MI" },
+ { 0x0003002E, "TestServ", "AFX Test Server" },
+
+ // Ami Pro
+ { 0x0003002F, "AmiProDocument", "Ami Pro Document" },
+
+ // WordPerfect Presentations For Windows
+ { 0x00030030, "WPGraphics", "WordPerfect Presentation" },
+ { 0x00030031, "WPCharts", "WordPerfect Chart" },
+
+ // MicroGrafx Charisma
+ { 0x00030032, "Charisma", "MicroGrafx Charisma" },
+ { 0x00030033, "Charisma_30", "MicroGrafx Charisma 3.0" },
+ { 0x00030034, "CharPres_30", "MicroGrafx Charisma 3.0 Pres" },
+ // MicroGrafx Draw
+ { 0x00030035, "Draw", "MicroGrafx Draw" },
+ // MicroGrafx Designer
+ { 0x00030036, "Designer_40", "MicroGrafx Designer 4.0" },
+
+ // STAR DIVISION
+ { 0x00043AD2, "FontWork", "Star FontWork" },
+
+ { 0, "", "" } };
+
+
+bool SvxMSDffManager::ConvertToOle2( SvStream& rStm, sal_uInt32 nReadLen,
+ const GDIMetaFile * pMtf, const tools::SvRef<SotStorage>& rDest )
+{
+ bool bMtfRead = false;
+ tools::SvRef<SotStorageStream> xOle10Stm = rDest->OpenSotStream( "\1Ole10Native",
+ StreamMode::WRITE| StreamMode::SHARE_DENYALL );
+ if( xOle10Stm->GetError() )
+ return false;
+
+ OUString aSvrName;
+ sal_uInt32 nDummy0;
+ sal_uInt32 nDummy1;
+ sal_uInt32 nBytesRead = 0;
+ do
+ {
+ sal_uInt32 nType(0);
+ sal_uInt32 nRecType(0);
+ sal_uInt32 nStrLen(0);
+
+ rStm.ReadUInt32( nType );
+ rStm.ReadUInt32( nRecType );
+ rStm.ReadUInt32( nStrLen );
+ if( nStrLen )
+ {
+ if( 0x10000L > nStrLen )
+ {
+ std::unique_ptr<char[]> pBuf(new char[ nStrLen ]);
+ rStm.ReadBytes(pBuf.get(), nStrLen);
+ aSvrName = OUString( pBuf.get(), static_cast<sal_uInt16>(nStrLen)-1, osl_getThreadTextEncoding() );
+ }
+ else
+ break;
+ }
+ rStm.ReadUInt32( nDummy0 );
+ rStm.ReadUInt32( nDummy1 );
+ sal_uInt32 nDataLen(0);
+ rStm.ReadUInt32( nDataLen );
+
+ nBytesRead += 6 * sizeof( sal_uInt32 ) + nStrLen + nDataLen;
+
+ if (rStm.good() && nReadLen > nBytesRead && nDataLen)
+ {
+ if( xOle10Stm.is() )
+ {
+ std::unique_ptr<sal_uInt8[]> pData(new sal_uInt8[ nDataLen ]);
+ rStm.ReadBytes(pData.get(), nDataLen);
+
+ // write to ole10 stream
+ xOle10Stm->WriteUInt32( nDataLen );
+ xOle10Stm->WriteBytes(pData.get(), nDataLen);
+ xOle10Stm = tools::SvRef<SotStorageStream>();
+
+ // set the compobj stream
+ const ClsIDs* pIds;
+ for( pIds = aClsIDs; pIds->nId; pIds++ )
+ {
+ if( aSvrName == OUString::createFromAscii(pIds->pSvrName) )
+ break;
+ }
+
+ if( pIds->nId )
+ {
+ // found!
+ SotClipboardFormatId nCbFmt = SotExchange::RegisterFormatName( aSvrName );
+ rDest->SetClass( SvGlobalName( pIds->nId, 0, 0, 0xc0,0,0,0,0,0,0,0x46 ), nCbFmt,
+ OUString::createFromAscii( pIds->pDspName ) );
+ }
+ else
+ {
+ SotClipboardFormatId nCbFmt = SotExchange::RegisterFormatName( aSvrName );
+ rDest->SetClass( SvGlobalName(), nCbFmt, aSvrName );
+ }
+ }
+ else if( nRecType == 5 && !pMtf )
+ {
+ sal_uInt64 nPos = rStm.Tell();
+ sal_uInt16 sz[4];
+ rStm.ReadBytes( sz, 8 );
+ Graphic aGraphic;
+ if( ERRCODE_NONE == GraphicConverter::Import( rStm, aGraphic ) && aGraphic.GetType() != GraphicType::NONE )
+ {
+ const GDIMetaFile& rMtf = aGraphic.GetGDIMetaFile();
+ MakeContentStream( rDest.get(), rMtf );
+ bMtfRead = true;
+ }
+ // set behind the data
+ rStm.Seek( nPos + nDataLen );
+ }
+ else
+ rStm.SeekRel( nDataLen );
+ }
+ } while (rStm.good() && nReadLen >= nBytesRead);
+
+ if( !bMtfRead && pMtf )
+ {
+ MakeContentStream( rDest.get(), *pMtf );
+ return true;
+ }
+
+ return false;
+}
+
+static const char* GetInternalServerName_Impl( const SvGlobalName& aGlobName )
+{
+ if ( aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_60 )
+ || aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 ) )
+ return "swriter";
+ else if ( aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_60 )
+ || aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 ) )
+ return "scalc";
+ else if ( aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_60 )
+ || aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) )
+ return "simpress";
+ else if ( aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_60 )
+ || aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) )
+ return "sdraw";
+ else if ( aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_60 )
+ || aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 ) )
+ return "smath";
+ else if ( aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_60 )
+ || aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 ) )
+ return "schart";
+ return nullptr;
+}
+
+OUString SvxMSDffManager::GetFilterNameFromClassID( const SvGlobalName& aGlobName )
+{
+ if ( aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_60 ) )
+ return "StarOffice XML (Writer)";
+
+ if ( aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 ) )
+ return "writer8";
+
+ if ( aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_60 ) )
+ return "StarOffice XML (Calc)";
+
+ if ( aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 ) )
+ return "calc8";
+
+ if ( aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_60 ) )
+ return "StarOffice XML (Impress)";
+
+ if ( aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) )
+ return "impress8";
+
+ if ( aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_60 ) )
+ return "StarOffice XML (Draw)";
+
+ if ( aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) )
+ return "draw8";
+
+ if ( aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_60 ) )
+ return "StarOffice XML (Math)";
+
+ if ( aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 ) )
+ return "math8";
+
+ if ( aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_60 ) )
+ return "StarOffice XML (Chart)";
+
+ if ( aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 ) )
+ return "chart8";
+
+ return OUString();
+}
+
+void SvxMSDffManager::ExtractOwnStream(SotStorage& rSrcStg, SvMemoryStream& rMemStream)
+{
+ tools::SvRef<SotStorageStream> xStr
+ = rSrcStg.OpenSotStream("package_stream", StreamMode::STD_READ);
+ xStr->ReadStream(rMemStream);
+}
+
+css::uno::Reference < css::embed::XEmbeddedObject > SvxMSDffManager::CheckForConvertToSOObj( sal_uInt32 nConvertFlags,
+ SotStorage& rSrcStg, const uno::Reference < embed::XStorage >& rDestStorage,
+ const Graphic& rGrf,
+ const tools::Rectangle& rVisArea, OUString const& rBaseURL)
+{
+ uno::Reference < embed::XEmbeddedObject > xObj;
+ SvGlobalName aStgNm = rSrcStg.GetClassName();
+ const char* pName = GetInternalServerName_Impl( aStgNm );
+ OUString sStarName;
+ if ( pName )
+ sStarName = OUString::createFromAscii( pName );
+ else if ( nConvertFlags )
+ {
+ static struct ObjImpType
+ {
+ sal_uInt32 nFlag;
+ const char* pFactoryNm;
+ // GlobalNameId
+ sal_uInt32 n1;
+ sal_uInt16 n2, n3;
+ sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15;
+ } const aArr[] = {
+ { OLE_MATHTYPE_2_STARMATH, "smath", MSO_EQUATION3_CLASSID },
+ { OLE_MATHTYPE_2_STARMATH, "smath", MSO_EQUATION2_CLASSID },
+ { OLE_WINWORD_2_STARWRITER, "swriter", MSO_WW8_CLASSID },
+ // Excel table
+ { OLE_EXCEL_2_STARCALC, "scalc", MSO_EXCEL5_CLASSID },
+ { OLE_EXCEL_2_STARCALC, "scalc", MSO_EXCEL8_CLASSID },
+ // 114465: additional Excel OLE chart classId to above.
+ { OLE_EXCEL_2_STARCALC, "scalc", MSO_EXCEL8_CHART_CLASSID },
+ // PowerPoint presentation
+ { OLE_POWERPOINT_2_STARIMPRESS, "simpress", MSO_PPT8_CLASSID },
+ // PowerPoint slide
+ { OLE_POWERPOINT_2_STARIMPRESS, "simpress", MSO_PPT8_SLIDE_CLASSID },
+ { 0, nullptr,
+ 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 }
+ };
+
+ for( const ObjImpType* pArr = aArr; pArr->nFlag; ++pArr )
+ {
+ if( nConvertFlags & pArr->nFlag )
+ {
+ SvGlobalName aTypeName( pArr->n1, pArr->n2, pArr->n3,
+ pArr->b8, pArr->b9, pArr->b10, pArr->b11,
+ pArr->b12, pArr->b13, pArr->b14, pArr->b15 );
+
+ if ( aStgNm == aTypeName )
+ {
+ sStarName = OUString::createFromAscii( pArr->pFactoryNm );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( sStarName.getLength() )
+ {
+ //TODO/MBA: check if (and when) storage and stream will be destroyed!
+ std::shared_ptr<const SfxFilter> pFilter;
+ SvMemoryStream aMemStream;
+ if ( pName )
+ {
+ // TODO/LATER: perhaps we need to retrieve VisArea and Metafile from the storage also
+ SvxMSDffManager::ExtractOwnStream(rSrcStg, aMemStream);
+ }
+ else
+ {
+ tools::SvRef<SotStorage> xStorage = new SotStorage( false, aMemStream );
+ rSrcStg.CopyTo( xStorage.get() );
+ xStorage->Commit();
+ xStorage.clear();
+ OUString aType = SfxFilter::GetTypeFromStorage( rSrcStg );
+ if (aType.getLength() && !utl::ConfigManager::IsFuzzing())
+ {
+ SfxFilterMatcher aMatch( sStarName );
+ pFilter = aMatch.GetFilter4EA( aType );
+ }
+ }
+
+#ifdef DEBUG_FILTER_MSFILTER
+ // extract embedded ole streams into "/tmp/embedded_stream_NNN"
+ static sal_Int32 nOleCount(0);
+ OUString aTmpName("/tmp/embedded_stream_");
+ aTmpName += OUString::number(nOleCount++);
+ aTmpName += ".bin";
+ SvFileStream aTmpStream(aTmpName,StreamMode::READ|StreamMode::WRITE|StreamMode::TRUNC);
+ xMemStream->Seek(0);
+ aTmpStream.WriteStream(*xMemStream);
+ aTmpStream.Close();
+#endif
+ if ( pName || pFilter )
+ {
+ //Reuse current ole name
+ OUString aDstStgName = MSO_OLE_Obj + OUString::number(nMSOleObjCntr);
+
+ OUString aFilterName;
+ if ( pFilter )
+ aFilterName = pFilter->GetName();
+ else
+ aFilterName = SvxMSDffManager::GetFilterNameFromClassID( aStgNm );
+
+ uno::Sequence<beans::PropertyValue> aMedium(aFilterName.isEmpty() ? 3 : 4);
+ auto pMedium = aMedium.getArray();
+ pMedium[0].Name = "InputStream";
+ uno::Reference < io::XInputStream > xStream = new ::utl::OSeekableInputStreamWrapper( aMemStream );
+ pMedium[0].Value <<= xStream;
+ pMedium[1].Name = "URL";
+ pMedium[1].Value <<= OUString( "private:stream" );
+ pMedium[2].Name = "DocumentBaseURL";
+ pMedium[2].Value <<= rBaseURL;
+
+ if ( !aFilterName.isEmpty() )
+ {
+ pMedium[3].Name = "FilterName";
+ pMedium[3].Value <<= aFilterName;
+ }
+
+ OUString aName( aDstStgName );
+ comphelper::EmbeddedObjectContainer aCnt( rDestStorage );
+ xObj = aCnt.InsertEmbeddedObject(aMedium, aName, &rBaseURL);
+
+ if ( !xObj.is() )
+ {
+ if( !aFilterName.isEmpty() )
+ {
+ // throw the filter parameter away as workaround
+ aMedium.realloc( 2 );
+ xObj = aCnt.InsertEmbeddedObject(aMedium, aName, &rBaseURL);
+ }
+
+ if ( !xObj.is() )
+ return xObj;
+ }
+
+ // JP 26.10.2001: Bug 93374 / 91928 the writer
+ // objects need the correct visarea needs the
+ // correct visarea, but this is not true for
+ // PowerPoint (see bugdoc 94908b)
+ // SJ: 19.11.2001 bug 94908, also chart objects
+ // needs the correct visarea
+
+ // If pName is set this is an own embedded object, it should have the correct size internally
+ // TODO/LATER: it might make sense in future to set the size stored in internal object
+ if( !pName && ( sStarName == "swriter" || sStarName == "scalc" ) )
+ {
+ // TODO/LATER: ViewAspect must be passed from outside!
+ sal_Int64 nViewAspect = embed::Aspects::MSOLE_CONTENT;
+ MapMode aMapMode( VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nViewAspect ) ) );
+ Size aSz;
+ if ( rVisArea.IsEmpty() )
+ aSz = lcl_GetPrefSize(rGrf, aMapMode );
+ else
+ {
+ aSz = rVisArea.GetSize();
+ aSz = OutputDevice::LogicToLogic( aSz, MapMode( MapUnit::Map100thMM ), aMapMode );
+ }
+
+ // don't modify the object
+ //TODO/LATER: remove those hacks, that needs to be done differently!
+ //xIPObj->EnableSetModified( sal_False );
+ awt::Size aSize;
+ aSize.Width = aSz.Width();
+ aSize.Height = aSz.Height();
+ xObj->setVisualAreaSize( nViewAspect, aSize );
+ //xIPObj->EnableSetModified( sal_True );
+ }
+ else if ( sStarName == "smath" )
+ { // SJ: force the object to recalc its visarea
+ //TODO/LATER: wait for PrinterChangeNotification
+ //xIPObj->OnDocumentPrinterChanged( NULL );
+ }
+ }
+ }
+
+ return xObj;
+}
+
+// TODO/MBA: code review and testing!
+SdrOle2Obj* SvxMSDffManager::CreateSdrOLEFromStorage(
+ SdrModel& rSdrModel,
+ const OUString& rStorageName,
+ tools::SvRef<SotStorage> const & rSrcStorage,
+ const uno::Reference < embed::XStorage >& xDestStorage,
+ const Graphic& rGrf,
+ const tools::Rectangle& rBoundRect,
+ const tools::Rectangle& rVisArea,
+ SvStream* pDataStrm,
+ ErrCode& rError,
+ sal_uInt32 nConvertFlags,
+ sal_Int64 nRecommendedAspect,
+ OUString const& rBaseURL)
+{
+ sal_Int64 nAspect = nRecommendedAspect;
+ SdrOle2Obj* pRet = nullptr;
+ if( rSrcStorage.is() && xDestStorage.is() && rStorageName.getLength() )
+ {
+ comphelper::EmbeddedObjectContainer aCnt( xDestStorage );
+ // does the 01Ole-Stream exist at all?
+ // (that's not the case for e.g. Fontwork )
+ // If that's not the case -> include it as graphic
+ bool bValidStorage = false;
+ OUString aDstStgName = MSO_OLE_Obj + OUString::number( ++nMSOleObjCntr );
+
+ {
+ tools::SvRef<SotStorage> xObjStg = rSrcStorage->OpenSotStorage( rStorageName );
+ if( xObjStg.is() )
+ {
+ {
+ sal_uInt8 aTestA[10]; // exist the \1CompObj-Stream ?
+ tools::SvRef<SotStorageStream> xSrcTst = xObjStg->OpenSotStream( "\1CompObj" );
+ bValidStorage = xSrcTst.is() && sizeof( aTestA ) ==
+ xSrcTst->ReadBytes(aTestA, sizeof(aTestA));
+ if( !bValidStorage )
+ {
+ // or the \1Ole-Stream ?
+ xSrcTst = xObjStg->OpenSotStream( "\1Ole" );
+ bValidStorage = xSrcTst.is() && sizeof(aTestA) ==
+ xSrcTst->ReadBytes(aTestA, sizeof(aTestA));
+ }
+ }
+
+ if( bValidStorage )
+ {
+ if ( nAspect != embed::Aspects::MSOLE_ICON )
+ {
+ // check whether the object is iconified one
+ // usually this information is already known, the only exception
+ // is a kind of embedded objects in Word documents
+ // TODO/LATER: should the caller be notified if the aspect changes in future?
+
+ tools::SvRef<SotStorageStream> xObjInfoSrc = xObjStg->OpenSotStream(
+ "\3ObjInfo", StreamMode::STD_READ );
+ if ( xObjInfoSrc.is() && !xObjInfoSrc->GetError() )
+ {
+ sal_uInt8 nByte = 0;
+ xObjInfoSrc->ReadUChar( nByte );
+ if ( ( nByte >> 4 ) & embed::Aspects::MSOLE_ICON )
+ nAspect = embed::Aspects::MSOLE_ICON;
+ }
+ }
+
+ uno::Reference < embed::XEmbeddedObject > xObj( CheckForConvertToSOObj(
+ nConvertFlags, *xObjStg, xDestStorage, rGrf,
+ rVisArea, rBaseURL));
+ if ( xObj.is() )
+ {
+ // remember file name to use in the title bar
+ INetURLObject aURL(rBaseURL);
+ xObj->setContainerName(aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset));
+
+ svt::EmbeddedObjectRef aObj( xObj, nAspect );
+
+ // TODO/LATER: need MediaType
+ aObj.SetGraphic( rGrf, OUString() );
+
+ // TODO/MBA: check setting of PersistName
+ pRet = new SdrOle2Obj(
+ rSdrModel,
+ aObj,
+ OUString(),
+ rBoundRect);
+
+ // we have the Object, don't create another
+ bValidStorage = false;
+ }
+ }
+ }
+ }
+
+ if( bValidStorage )
+ {
+ // object is not an own object
+ tools::SvRef<SotStorage> xObjStor = SotStorage::OpenOLEStorage( xDestStorage, aDstStgName, StreamMode::READWRITE );
+
+ if ( xObjStor.is() )
+ {
+ tools::SvRef<SotStorage> xSrcStor = rSrcStorage->OpenSotStorage( rStorageName, StreamMode::READ );
+ xSrcStor->CopyTo( xObjStor.get() );
+
+ if( !xObjStor->GetError() )
+ xObjStor->Commit();
+
+ if( xObjStor->GetError() )
+ {
+ rError = xObjStor->GetError();
+ bValidStorage = false;
+ }
+ else if( !xObjStor.is() )
+ bValidStorage = false;
+ }
+ }
+ else if( pDataStrm )
+ {
+ sal_uInt32 nLen(0), nDummy(0);
+ pDataStrm->ReadUInt32( nLen ).ReadUInt32( nDummy );
+ if( ERRCODE_NONE != pDataStrm->GetError() ||
+ // Id in BugDoc - exist there other Ids?
+ // The ConvertToOle2 - does not check for consistent
+ 0x30008 != nDummy )
+ bValidStorage = false;
+ else
+ {
+ // or is it an OLE-1 Stream in the DataStream?
+ tools::SvRef<SotStorage> xObjStor = SotStorage::OpenOLEStorage( xDestStorage, aDstStgName );
+ //TODO/MBA: remove metafile conversion from ConvertToOle2
+ //when is this code used?!
+ GDIMetaFile aMtf;
+ bValidStorage = ConvertToOle2( *pDataStrm, nLen, &aMtf, xObjStor );
+ xObjStor->Commit();
+ }
+ }
+
+ if( bValidStorage )
+ {
+ uno::Reference < embed::XEmbeddedObject > xObj = aCnt.GetEmbeddedObject( aDstStgName );
+ if( xObj.is() )
+ {
+ // remember file name to use in the title bar
+ INetURLObject aURL( rBaseURL );
+ xObj->setContainerName( aURL.GetLastName( INetURLObject::DecodeMechanism::WithCharset ) );
+
+ // the visual area must be retrieved from the metafile (object doesn't know it so far)
+
+ if ( nAspect != embed::Aspects::MSOLE_ICON )
+ {
+ // working with visual area can switch the object to running state
+ try
+ {
+ awt::Size aAwtSz;
+ // the provided visual area should be used, if there is any
+ if ( rVisArea.IsEmpty() )
+ {
+ MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) );
+ Size aSz(lcl_GetPrefSize(rGrf, MapMode(aMapUnit)));
+ aAwtSz.Width = aSz.Width();
+ aAwtSz.Height = aSz.Height();
+ }
+ else
+ {
+ aAwtSz.Width = rVisArea.GetWidth();
+ aAwtSz.Height = rVisArea.GetHeight();
+ }
+ //xInplaceObj->EnableSetModified( sal_False );
+ xObj->setVisualAreaSize( nAspect, aAwtSz );
+ //xInplaceObj->EnableSetModified( sal_True );
+ }
+ catch( const uno::Exception& )
+ {
+ OSL_FAIL( "Could not set visual area of the object!" );
+ }
+ }
+
+ svt::EmbeddedObjectRef aObj( xObj, nAspect );
+
+ // TODO/LATER: need MediaType
+ aObj.SetGraphic( rGrf, OUString() );
+
+ pRet = new SdrOle2Obj(
+ rSdrModel,
+ aObj,
+ aDstStgName,
+ rBoundRect);
+ }
+ }
+ }
+
+ return pRet;
+}
+
+bool SvxMSDffManager::SetPropValue( const uno::Any& rAny, const uno::Reference< css::beans::XPropertySet > & rXPropSet,
+ const OUString& rPropName )
+{
+ bool bRetValue = false;
+ try
+ {
+ uno::Reference< beans::XPropertySetInfo >
+ aXPropSetInfo( rXPropSet->getPropertySetInfo() );
+ if ( aXPropSetInfo.is() )
+ bRetValue = aXPropSetInfo->hasPropertyByName( rPropName );
+ }
+ catch( const uno::Exception& )
+ {
+ bRetValue = false;
+ }
+ if ( bRetValue )
+ {
+ try
+ {
+ rXPropSet->setPropertyValue( rPropName, rAny );
+ bRetValue = true;
+ }
+ catch( const uno::Exception& )
+ {
+ bRetValue = false;
+ }
+ }
+ return bRetValue;
+}
+
+SvxMSDffImportRec::SvxMSDffImportRec()
+ : pObj( nullptr ),
+ nClientAnchorLen( 0 ),
+ nClientDataLen( 0 ),
+ nXAlign( 0 ), // position n cm from left
+ nYAlign( 0 ), // position n cm below
+ nGroupShapeBooleanProperties(0), // 16 settings: LayoutInCell/AllowOverlap/BehindDocument...
+ nFlags( ShapeFlag::NONE ),
+ nDxTextLeft( 144 ),
+ nDyTextTop( 72 ),
+ nDxTextRight( 144 ),
+ nDyTextBottom( 72 ),
+ nDxWrapDistLeft( 0 ),
+ nDyWrapDistTop( 0 ),
+ nDxWrapDistRight( 0 ),
+ nDyWrapDistBottom(0 ),
+ nCropFromTop( 0 ),
+ nCropFromBottom( 0 ),
+ nCropFromLeft( 0 ),
+ nCropFromRight( 0 ),
+ nNextShapeId( 0 ),
+ nShapeId( 0 ),
+ eShapeType( mso_sptNil ),
+ relativeHorizontalWidth( -1 ),
+ isHorizontalRule( false )
+{
+ eLineStyle = mso_lineSimple; // GPF-Bug #66227#
+ eLineDashing = mso_lineSolid;
+ bDrawHell = false;
+ bHidden = false;
+
+ bReplaceByFly = false;
+ bVFlip = false;
+ bHFlip = false;
+ bAutoWidth = false;
+}
+
+SvxMSDffImportRec::SvxMSDffImportRec(const SvxMSDffImportRec& rCopy)
+ : pObj( rCopy.pObj ),
+ nXAlign( rCopy.nXAlign ),
+ nXRelTo( rCopy.nXRelTo ),
+ nYAlign( rCopy.nYAlign ),
+ nYRelTo( rCopy.nYRelTo ),
+ nGroupShapeBooleanProperties(rCopy.nGroupShapeBooleanProperties),
+ nFlags( rCopy.nFlags ),
+ nDxTextLeft( rCopy.nDxTextLeft ),
+ nDyTextTop( rCopy.nDyTextTop ),
+ nDxTextRight( rCopy.nDxTextRight ),
+ nDyTextBottom( rCopy.nDyTextBottom ),
+ nDxWrapDistLeft( rCopy.nDxWrapDistLeft ),
+ nDyWrapDistTop( rCopy.nDyWrapDistTop ),
+ nDxWrapDistRight( rCopy.nDxWrapDistRight ),
+ nDyWrapDistBottom(rCopy.nDyWrapDistBottom ),
+ nCropFromTop( rCopy.nCropFromTop ),
+ nCropFromBottom( rCopy.nCropFromBottom ),
+ nCropFromLeft( rCopy.nCropFromLeft ),
+ nCropFromRight( rCopy.nCropFromRight ),
+ aTextId( rCopy.aTextId ),
+ nNextShapeId( rCopy.nNextShapeId ),
+ nShapeId( rCopy.nShapeId ),
+ eShapeType( rCopy.eShapeType ),
+ relativeHorizontalWidth( rCopy.relativeHorizontalWidth ),
+ isHorizontalRule( rCopy.isHorizontalRule )
+{
+ eLineStyle = rCopy.eLineStyle; // GPF-Bug #66227#
+ eLineDashing = rCopy.eLineDashing;
+ bDrawHell = rCopy.bDrawHell;
+ bHidden = rCopy.bHidden;
+ bReplaceByFly = rCopy.bReplaceByFly;
+ bAutoWidth = rCopy.bAutoWidth;
+ bVFlip = rCopy.bVFlip;
+ bHFlip = rCopy.bHFlip;
+ nClientAnchorLen = rCopy.nClientAnchorLen;
+ if( rCopy.nClientAnchorLen )
+ {
+ pClientAnchorBuffer.reset( new char[ nClientAnchorLen ] );
+ memcpy( pClientAnchorBuffer.get(),
+ rCopy.pClientAnchorBuffer.get(),
+ nClientAnchorLen );
+ }
+ else
+ pClientAnchorBuffer = nullptr;
+
+ nClientDataLen = rCopy.nClientDataLen;
+ if( rCopy.nClientDataLen )
+ {
+ pClientDataBuffer.reset( new char[ nClientDataLen ] );
+ memcpy( pClientDataBuffer.get(),
+ rCopy.pClientDataBuffer.get(),
+ nClientDataLen );
+ }
+ else
+ pClientDataBuffer = nullptr;
+
+ if (rCopy.pWrapPolygon)
+ pWrapPolygon = rCopy.pWrapPolygon;
+}
+
+SvxMSDffImportRec::~SvxMSDffImportRec()
+{
+}
+
+void SvxMSDffManager::insertShapeId( sal_Int32 nShapeId, SdrObject* pShape )
+{
+ maShapeIdContainer[nShapeId] = pShape;
+}
+
+void SvxMSDffManager::removeShapeId( SdrObject const * pShape )
+{
+ SvxMSDffShapeIdContainer::iterator aIter = std::find_if(maShapeIdContainer.begin(), maShapeIdContainer.end(),
+ [&pShape](const SvxMSDffShapeIdContainer::value_type& rEntry) { return rEntry.second == pShape; });
+ if (aIter != maShapeIdContainer.end())
+ maShapeIdContainer.erase( aIter );
+}
+
+SdrObject* SvxMSDffManager::getShapeForId( sal_Int32 nShapeId )
+{
+ SvxMSDffShapeIdContainer::iterator aIter( maShapeIdContainer.find(nShapeId) );
+ return aIter != maShapeIdContainer.end() ? (*aIter).second : nullptr;
+}
+
+SvxMSDffImportData::SvxMSDffImportData(const tools::Rectangle& rParentRect)
+ : aParentRect(rParentRect)
+{
+}
+
+SvxMSDffImportData::~SvxMSDffImportData()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */