/* -*- 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 <memory>
#include "eschesdo.hxx"
#include <o3tl/any.hxx>
#include <svx/svdobj.hxx>
#include <tools/poly.hxx>
#include <tools/debug.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <svx/unopage.hxx>
#include <com/sun/star/awt/Rectangle.hpp>
#include <com/sun/star/beans/PropertyValue.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/drawing/CircleKind.hpp>
#include <com/sun/star/drawing/FillStyle.hpp>
#include <comphelper/extract.hxx>
#include <com/sun/star/drawing/HomogenMatrix3.hpp>
#include <basegfx/matrix/b2dhommatrix.hxx>
using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::task;
constexpr o3tl::Length geUnitsSrc(o3tl::Length::mm100);
// PowerPoint: 576 dpi, WinWord: 1440 dpi, Excel: 1440 dpi
constexpr o3tl::Length geUnitsDest(o3tl::Length::twip);
ImplEESdrWriter::ImplEESdrWriter( EscherEx& rEx )
: mpEscherEx(&rEx)
, mpPicStrm(nullptr)
, mpHostAppData(nullptr)
, mbIsTitlePossible(false)
, mpSdrPage( nullptr )
{
}
Point ImplEESdrWriter::ImplMapPoint( const Point& rPoint )
{
return o3tl::convert( rPoint, geUnitsSrc, geUnitsDest );
}
Size ImplEESdrWriter::ImplMapSize( const Size& rSize )
{
Size aRetSize( o3tl::convert( rSize, geUnitsSrc, geUnitsDest ) );
if ( !aRetSize.Width() )
aRetSize.AdjustWidth( 1 );
if ( !aRetSize.Height() )
aRetSize.AdjustHeight( 1 );
return aRetSize;
}
void ImplEESdrWriter::ImplFlipBoundingBox( ImplEESdrObject& rObj, EscherPropertyContainer& rPropOpt )
{
sal_Int32 nAngle = rObj.GetAngle();
tools::Rectangle aRect( rObj.GetRect() );
// for position calculations, we normalize the angle between 0 and 90 degrees
if ( nAngle < 0 )
nAngle = ( 36000 + nAngle ) % 36000;
if ( nAngle % 18000 == 0 )
nAngle = 0;
while ( nAngle > 9000 )
nAngle = ( 18000 - ( nAngle % 18000 ) );
double fVal = basegfx::deg2rad<100>(nAngle);
double fCos = cos( fVal );
double fSin = sin( fVal );
double nWidthHalf = static_cast<double>(aRect.GetWidth()) / 2;
double nHeightHalf = static_cast<double>(aRect.GetHeight()) / 2;
// fdo#70838:
// when you rotate an object, the top-left corner of its bounding box is moved
// nXDiff and nYDiff pixels. To get their values we use these equations:
//
// fSin * nHeightHalf + fCos * nWidthHalf == nXDiff + nWidthHalf
// fSin * nWidthHalf + fCos * nHeightHalf == nYDiff + nHeightHalf
double nXDiff = fSin * nHeightHalf + fCos * nWidthHalf - nWidthHalf;
double nYDiff = fSin * nWidthHalf + fCos * nHeightHalf - nHeightHalf;
aRect.Move( static_cast<sal_Int32>(nXDiff), static_cast<sal_Int32>(nYDiff) );
// calculate the proper angle value to be saved
nAngle = rObj.GetAngle();
if ( nAngle < 0 )
nAngle = ( 36000 + nAngle ) % 36000;
else
nAngle = ( 36000 - ( nAngle % 36000 ) );
nAngle *= 655;
nAngle += 0x8000;
nAngle &=~0xffff; // nAngle round to full degrees
rPropOpt.AddOpt( ESCHER_Prop_Rotation, nAngle );
rObj.SetAngle( nAngle );
rObj.SetRect( aRect );
}
sal_uInt32 ImplEESdrWriter::ImplWriteShape( ImplEESdrObject& rObj,
EscherSolverContainer& rSolverContainer,
const bool bOOxmlExport )
{
sal_uInt32 nShapeID = 0;
sal_uInt16 nShapeType = 0;
bool bDontWriteText = false; // if a metafile is written as shape replacement, then the text is already part of the metafile
bool bAdditionalText = false;
sal_uInt32 nGrpShapeID = 0;
auto addShape = [this, &rObj, &rSolverContainer, &nShapeID, &nShapeType](sal_uInt16 nType, ShapeFlag nFlags)
{
nShapeType = nType;
nShapeID = mpEscherEx->GenerateShapeId();
rObj.SetShapeId( nShapeID );
mpEscherEx->AddShape( nType, nFlags, nShapeID );
rSolverContainer.AddShape( rObj.GetShapeRef(), nShapeID );
};
do {
mpHostAppData = mpEscherEx->StartShape( rObj.GetShapeRef(), (mpEscherEx->GetGroupLevel() > 1) ? &rObj.GetRect() : nullptr );
if ( mpHostAppData && mpHostAppData->DontWriteShape() )
break;
// #i51348# get shape name
OUString aShapeName;
if( const SdrObject* pSdrObj = rObj.GetSdrObject() )
if (!pSdrObj->GetName().isEmpty())
aShapeName = pSdrObj->GetName();
uno::Reference< drawing::XShape> xShape = rObj.GetShapeRef();
if (xShape.is())
{
uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY);
if (xPropertySet.is())
{
uno::Sequence<beans::PropertyValue> aGrabBag;
uno::Reference< XPropertySetInfo > xPropInfo = xPropertySet->getPropertySetInfo();
if ( xPropInfo.is() && xPropInfo->hasPropertyByName( u"InteropGrabBag"_ustr ) )
{
xPropertySet->getPropertyValue( u"InteropGrabBag"_ustr ) >>= aGrabBag;
for (const beans::PropertyValue& rProp : aGrabBag)
{
if (rProp.Name == "mso-edit-as")
{
OUString rEditAs;
rProp.Value >>= rEditAs;
mpEscherEx->SetEditAs(rEditAs);
break;
}
}
}
}
}
if( rObj.GetType() == "drawing.Group" )
{
Reference< XIndexAccess > xXIndexAccess( rObj.GetShapeRef(), UNO_QUERY );
if( xXIndexAccess.is() && 0 != xXIndexAccess->getCount() )
{
nShapeID = mpEscherEx->EnterGroup( aShapeName, &rObj.GetRect() );
nShapeType = ESCHER_ShpInst_Min;
for( sal_uInt32 n = 0, nCnt = xXIndexAccess->getCount();
n < nCnt; ++n )
{
ImplEESdrObject aObj( *o3tl::doAccess<Reference<XShape>>(
xXIndexAccess->getByIndex( n )) );
if( aObj.IsValid() )
{
aObj.SetOOXML(bOOxmlExport);
ImplWriteShape( aObj, rSolverContainer, bOOxmlExport );
}
}
mpEscherEx->LeaveGroup();
}
break;
}
rObj.SetAngle( rObj.ImplGetInt32PropertyValue( u"RotateAngle"_ustr ));
if( ( rObj.ImplGetPropertyValue( u"IsFontwork"_ustr ) &&
::cppu::any2bool( rObj.GetUsrAny() ) ) ||
rObj.GetType() == "drawing.Measure" )
{
rObj.SetType(u"drawing.dontknow"_ustr);
}
const css::awt::Size aSize100thmm( rObj.GetShapeRef()->getSize() );
const css::awt::Point aPoint100thmm( rObj.GetShapeRef()->getPosition() );
tools::Rectangle aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) );
if ( !mpPicStrm )
mpPicStrm = mpEscherEx->QueryPictureStream();
EscherPropertyContainer aPropOpt( mpEscherEx->GetGraphicProvider(), mpPicStrm, aRect100thmm );
// #i51348# shape name
if (!aShapeName.isEmpty())
aPropOpt.AddOpt( ESCHER_Prop_wzName, aShapeName );
if ( InteractionInfo* pInteraction = mpHostAppData ? mpHostAppData->GetInteractionInfo():nullptr )
{
const std::unique_ptr< SvMemoryStream >& pMemStrm = pInteraction->getHyperlinkRecord();
if (pMemStrm)
{
aPropOpt.AddOpt(ESCHER_Prop_pihlShape, false, 0, *pMemStrm);
}
aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080008 );
}
if ( rObj.GetType() == "drawing.Custom" )
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
ShapeFlag nMirrorFlags;
OUString sCustomShapeType;
MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( rObj.GetShapeRef(), nMirrorFlags, sCustomShapeType, rObj.GetOOXML() );
if ( sCustomShapeType == "col-502ad400" || sCustomShapeType == "col-60da8460" )
{
addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, u"MetaFile"_ustr, false ) )
{
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); // no fill
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); // no linestyle
SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rObj.GetShapeRef());
if ( pObj )
{
tools::Rectangle aBound = pObj->GetCurrentBoundRect();
Point aPosition( ImplMapPoint( aBound.TopLeft() ) );
Size aSize( ImplMapSize( aBound.GetSize() ) );
rObj.SetRect( tools::Rectangle( aPosition, aSize ) );
rObj.SetAngle( 0 );
bDontWriteText = true;
}
}
}
else
{
const Reference< XPropertySet > xPropSet = rObj.mXPropSet;
drawing::FillStyle eFS = drawing::FillStyle_NONE;
if(xPropSet.is())
{
uno::Reference< XPropertySetInfo > xPropInfo = xPropSet->getPropertySetInfo();
if ( xPropInfo.is() && xPropInfo->hasPropertyByName(u"FillStyle"_ustr))
xPropSet->getPropertyValue(u"FillStyle"_ustr) >>= eFS;
}
if (eFS == drawing::FillStyle_BITMAP && eShapeType == mso_sptMax)
{
// We can't map this custom shape to a DOC preset and it has a bitmap fill.
// Make sure that at least the bitmap fill is not lost.
addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, u"Bitmap"_ustr, false, true, true, bOOxmlExport ) )
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
}
else
{
addShape(sal::static_int_cast< sal_uInt16 >(eShapeType),
nMirrorFlags | ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor);
aPropOpt.CreateCustomShapeProperties( eShapeType, rObj.GetShapeRef() );
aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
if ( rObj.ImplGetText() )
{
if ( !aPropOpt.IsFontWork() )
aPropOpt.CreateTextProperties( rObj.mXPropSet, mpEscherEx->QueryTextID(
rObj.GetShapeRef(), rObj.GetShapeId() ), true, false );
}
}
}
}
else if ( rObj.GetType() == "drawing.Rectangle" )
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
sal_Int32 nRadius = rObj.ImplGetInt32PropertyValue(u"CornerRadius"_ustr);
if( nRadius )
{
nRadius = ImplMapSize( Size( nRadius, 0 )).Width();
addShape( ESCHER_ShpInst_RoundRectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
sal_Int32 nLength = rObj.GetRect().GetWidth();
if ( nLength > rObj.GetRect().GetHeight() )
nLength = rObj.GetRect().GetHeight();
nLength >>= 1;
if ( nRadius >= nLength || nLength == 0 )
nRadius = 0x2a30; // 0x2a30 is PPTs maximum radius
else
nRadius = ( 0x2a30 * nRadius ) / nLength;
aPropOpt.AddOpt( ESCHER_Prop_adjustValue, nRadius );
}
else
{
addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
}
aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
if( rObj.ImplGetText() )
aPropOpt.CreateTextProperties( rObj.mXPropSet,
mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() ), false, false );
}
else if ( rObj.GetType() == "drawing.Ellipse" )
{
CircleKind eCircleKind = CircleKind_FULL;
PolyStyle ePolyKind = PolyStyle();
if ( rObj.ImplGetPropertyValue( u"CircleKind"_ustr ) )
{
eCircleKind = *o3tl::doAccess<CircleKind>(rObj.GetUsrAny());
switch ( eCircleKind )
{
case CircleKind_SECTION :
{
ePolyKind = PolyStyle::Pie;
}
break;
case CircleKind_ARC :
{
ePolyKind = PolyStyle::Arc;
}
break;
case CircleKind_CUT :
{
ePolyKind = PolyStyle::Chord;
}
break;
default:
eCircleKind = CircleKind_FULL;
}
}
if ( eCircleKind == CircleKind_FULL )
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_Ellipse, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
}
else
{
sal_Int32 nStartAngle, nEndAngle;
if ( !rObj.ImplGetPropertyValue( u"CircleStartAngle"_ustr ) )
break;
nStartAngle = *o3tl::doAccess<sal_Int32>(rObj.GetUsrAny());
if( !rObj.ImplGetPropertyValue( u"CircleEndAngle"_ustr ) )
break;
nEndAngle = *o3tl::doAccess<sal_Int32>(rObj.GetUsrAny());
Point aStart, aEnd, aCenter;
aStart.setX( static_cast<sal_Int32>( cos( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ) );
aStart.setY( - static_cast<sal_Int32>( sin( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ) );
aEnd.setX( static_cast<sal_Int32>( cos( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ) );
aEnd.setY( - static_cast<sal_Int32>( sin( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ) );
const tools::Rectangle& rRect = aRect100thmm;
aCenter.setX( rRect.Left() + ( rRect.GetWidth() / 2 ) );
aCenter.setY( rRect.Top() + ( rRect.GetHeight() / 2 ) );
aStart.AdjustX(aCenter.X() );
aStart.AdjustY(aCenter.Y() );
aEnd.AdjustX(aCenter.X() );
aEnd.AdjustY(aCenter.Y() );
tools::Polygon aPolygon( rRect, aStart, aEnd, ePolyKind );
if( rObj.GetAngle() )
{
aPolygon.Rotate( rRect.TopLeft(), Degree10(static_cast<sal_Int16>( rObj.GetAngle() / 10 )) );
rObj.SetAngle( 0 );
}
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
css::awt::Rectangle aNewRect;
switch ( ePolyKind )
{
case PolyStyle::Pie :
case PolyStyle::Chord :
{
aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect, &aPolygon );
aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
}
break;
case PolyStyle::Arc :
{
aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect, &aPolygon );
aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
}
break;
}
rObj.SetRect( tools::Rectangle( ImplMapPoint( Point( aNewRect.X, aNewRect.Y ) ),
ImplMapSize( Size( aNewRect.Width, aNewRect.Height ) ) ) );
}
if ( rObj.ImplGetText() )
aPropOpt.CreateTextProperties( rObj.mXPropSet,
mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() ), false, false );
}
else if ( rObj.GetType() == "drawing.Control" )
{
const Reference< XPropertySet > xPropSet = rObj.mXPropSet;
const Reference<XPropertySetInfo> xPropInfo = xPropSet.is() ? xPropSet->getPropertySetInfo() : Reference<XPropertySetInfo>();
// This code is expected to be called only for DOCX/XLSX formats.
if (xPropInfo.is() && bOOxmlExport)
{
bool bInline = false;
if (xPropInfo->hasPropertyByName(u"AnchorType"_ustr))
{
text::TextContentAnchorType eAnchorType;
xPropSet->getPropertyValue(u"AnchorType"_ustr) >>= eAnchorType;
bInline = eAnchorType == text::TextContentAnchorType_AS_CHARACTER;
}
mpEscherEx->OpenContainer( ESCHER_SpContainer );
nShapeType = bInline ? ESCHER_ShpInst_PictureFrame : ESCHER_ShpInst_HostControl;
const ShapeFlag nFlags = ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor;
nShapeID = rObj.GetShapeId();
if (nShapeID)
{
mpEscherEx->AddShape(nShapeType, nFlags, nShapeID );
rSolverContainer.AddShape(rObj.GetShapeRef(), nShapeID);
}
else
{
addShape(nShapeType, nFlags);
}
}
else
break;
}
else if ( rObj.GetType() == "drawing.Connector" )
{
sal_uInt16 nSpType;
ShapeFlag nSpFlags;
css::awt::Rectangle aNewRect;
if ( ! aPropOpt.CreateConnectorProperties( rObj.GetShapeRef(),
rSolverContainer, aNewRect, nSpType, nSpFlags ) )
break;
rObj.SetRect( tools::Rectangle( ImplMapPoint( Point( aNewRect.X, aNewRect.Y ) ),
ImplMapSize( Size( aNewRect.Width, aNewRect.Height ) ) ) );
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( nSpType, nSpFlags );
}
else if ( rObj.GetType() == "drawing.Measure" )
{
break;
}
else if ( rObj.GetType() == "drawing.Line" )
{
css::awt::Rectangle aNewRect;
aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_LINE, false, aNewRect );
//i27942: Poly/Lines/Bezier do not support text.
mpEscherEx->OpenContainer( ESCHER_SpContainer );
ShapeFlag nFlags = ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor;
if( aNewRect.Height < 0 )
nFlags |= ShapeFlag::FlipV;
if( aNewRect.Width < 0 )
nFlags |= ShapeFlag::FlipH;
addShape( ESCHER_ShpInst_Line, nFlags );
aPropOpt.AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex );
aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
rObj.SetAngle( 0 );
}
else if ( rObj.GetType() == "drawing.PolyPolygon" )
{
if( rObj.ImplHasText() )
{
nGrpShapeID = ImplEnterAdditionalTextGroup( rObj.GetShapeRef(), &rObj.GetRect() );
bAdditionalText = true;
}
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
css::awt::Rectangle aNewRect;
aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect );
aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
rObj.SetAngle( 0 );
}
else if ( rObj.GetType() == "drawing.PolyLine" )
{
//i27942: Poly/Lines/Bezier do not support text.
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
css::awt::Rectangle aNewRect;
aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect );
aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
rObj.SetAngle( 0 );
}
else if ( rObj.GetType() == "drawing.OpenBezier" )
{
//i27942: Poly/Lines/Bezier do not support text.
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
css::awt::Rectangle aNewRect;
aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, true, aNewRect );
aPropOpt.CreateLineProperties( rObj.mXPropSet, false );
rObj.SetAngle( 0 );
}
else if ( rObj.GetType() == "drawing.ClosedBezier" )
{
if ( rObj.ImplHasText() )
{
nGrpShapeID = ImplEnterAdditionalTextGroup( rObj.GetShapeRef(), &rObj.GetRect() );
bAdditionalText = true;
}
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
css::awt::Rectangle aNewRect;
aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, true, aNewRect );
aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
rObj.SetAngle( 0 );
}
else if ( rObj.GetType() == "drawing.GraphicObject" )
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
// a GraphicObject can also be a ClickMe element
if( rObj.IsEmptyPresObj() )
{
addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveMaster | ShapeFlag::HaveAnchor );
sal_uInt32 nTxtBxId = mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() );
aPropOpt.AddOpt( ESCHER_Prop_lTxid, nTxtBxId );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 );
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 );
aPropOpt.AddOpt( ESCHER_Prop_hspMaster, 0 );
}
else
{
if( rObj.ImplGetText() )
{
/* SJ #i34951#: because M. documents are not allowing GraphicObjects containing text, we
have to create a simple Rectangle with fill bitmap instead (while not allowing BitmapMode_Repeat).
*/
addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, u"Graphic"_ustr, true, true, false ) )
{
aPropOpt.AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone );
aPropOpt.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 );
aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x8000000 );
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 );
if ( rObj.ImplGetText() )
aPropOpt.CreateTextProperties( rObj.mXPropSet,
mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() ), false, false );
}
}
else
{
addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, u"Graphic"_ustr, false, true, true, bOOxmlExport ) )
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
}
}
}
else if ( rObj.GetType() == "drawing.Text" )
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
aPropOpt.CreateFillProperties( rObj.mXPropSet, true );
if( rObj.ImplGetText() )
aPropOpt.CreateTextProperties( rObj.mXPropSet,
mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() ) );
}
else if ( rObj.GetType() == "drawing.Page" )
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x40004 );
aPropOpt.AddOpt( ESCHER_Prop_fFillOK, 0x100001 );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110011 );
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90008 );
aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x10001 );
}
else if ( rObj.GetType() == "drawing.Frame" )
{
break;
}
else if ( rObj.GetType() == "drawing.OLE2" )
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
if( rObj.IsEmptyPresObj() )
{
addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveMaster | ShapeFlag::HaveAnchor );
sal_uInt32 nTxtBxId = mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() );
aPropOpt.AddOpt( ESCHER_Prop_lTxid, nTxtBxId );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 );
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 );
aPropOpt.AddOpt( ESCHER_Prop_hspMaster, 0 );
}
else
{
//2do: could be made an option in HostAppData whether OLE object should be written or not
const bool bAppOLE = true;
addShape( ESCHER_ShpInst_PictureFrame,
ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | (bAppOLE ? ShapeFlag::OLEShape : ShapeFlag::NONE) );
if ( aPropOpt.CreateOLEGraphicProperties( rObj.GetShapeRef() ) )
{
if ( bAppOLE )
{ // snooped from Xcl hex dump, nobody knows the trouble I have seen
aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 );
aPropOpt.AddOpt( ESCHER_Prop_pictureId, 0x00000001 );
aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x08000041 );
aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x08000041 );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 );
aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x08000040 );
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash,0x00080008 );
aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080000 );
}
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
}
}
}
else if( '3' == rObj.GetType()[8] &&
'D' == rObj.GetType()[9] ) // drawing.3D
{
// SceneObject, CubeObject, SphereObject, LatheObject, ExtrudeObject, PolygonObject
if ( !rObj.ImplGetPropertyValue( u"Bitmap"_ustr ) )
break;
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, u"Bitmap"_ustr, false ) )
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
}
else if ( rObj.GetType() == "drawing.Caption" )
{
rObj.SetAngle( 0 );
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, u"MetaFile"_ustr, false ) )
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
}
else if ( rObj.GetType() == "drawing.dontknow" )
{
rObj.SetAngle( 0 );
mpEscherEx->OpenContainer( ESCHER_SpContainer );
addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, u"MetaFile"_ustr, false ) )
aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 );
}
else
{
break;
}
aPropOpt.CreateShadowProperties( rObj.mXPropSet );
if( SDRLAYER_NOTFOUND != mpEscherEx->GetHellLayerId() &&
rObj.ImplGetPropertyValue( u"LayerID"_ustr ) &&
*o3tl::doAccess<sal_Int16>(rObj.GetUsrAny()) == mpEscherEx->GetHellLayerId().get() )
{
aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x200020 );
}
{
tools::Rectangle aRect( rObj.GetRect() );
aRect.Normalize();
rObj.SetRect( aRect );
}
if( rObj.GetAngle() )
ImplFlipBoundingBox( rObj, aPropOpt );
aPropOpt.CreateShapeProperties( rObj.GetShapeRef() );
const SdrObject* sdrObj = rObj.GetSdrObject();
mpEscherEx->AddSdrObjectVMLObject(*sdrObj );
mpEscherEx->Commit( aPropOpt, rObj.GetRect());
if( mpEscherEx->GetGroupLevel() > 1 )
mpEscherEx->AddChildAnchor( rObj.GetRect() );
if ( mpHostAppData )
{ //! with AdditionalText the App has to control whether these are written or not
mpHostAppData->WriteClientAnchor( *mpEscherEx, rObj.GetRect() );
mpHostAppData->WriteClientData( *mpEscherEx );
if ( !bDontWriteText )
mpHostAppData->WriteClientTextbox( *mpEscherEx );
}
mpEscherEx->CloseContainer(); // ESCHER_SpContainer
if( bAdditionalText )
{
mpEscherEx->EndShape( nShapeType, nShapeID );
ImplWriteAdditionalText( rObj );
}
} while ( false );
if ( bAdditionalText )
mpEscherEx->EndShape( ESCHER_ShpInst_Min, nGrpShapeID );
else
mpEscherEx->EndShape( nShapeType, nShapeID );
return nShapeID;
}
void ImplEESdrWriter::ImplWriteAdditionalText( ImplEESdrObject& rObj )
{
sal_uInt32 nShapeID = 0;
sal_uInt16 nShapeType = 0;
do
{
mpHostAppData = mpEscherEx->StartShape( rObj.GetShapeRef(), (mpEscherEx->GetGroupLevel() > 1) ? &rObj.GetRect() : nullptr );
if ( mpHostAppData && mpHostAppData->DontWriteShape() )
break;
const css::awt::Size aSize100thmm( rObj.GetShapeRef()->getSize() );
const css::awt::Point aPoint100thmm( rObj.GetShapeRef()->getPosition() );
tools::Rectangle aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) );
if ( !mpPicStrm )
mpPicStrm = mpEscherEx->QueryPictureStream();
EscherPropertyContainer aPropOpt( mpEscherEx->GetGraphicProvider(), mpPicStrm, aRect100thmm );
rObj.SetAngle( rObj.ImplGetInt32PropertyValue( u"RotateAngle"_ustr ));
sal_Int32 nAngle = rObj.GetAngle();
if( rObj.GetType() == "drawing.Line" )
{
//2do: this does not work right
double fDist = hypot( rObj.GetRect().GetWidth(),
rObj.GetRect().GetHeight() );
rObj.SetRect( tools::Rectangle( Point(),
Point( static_cast<sal_Int32>( fDist ), -1 ) ) );
mpEscherEx->OpenContainer( ESCHER_SpContainer );
mpEscherEx->AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor );
if ( rObj.ImplGetText() )
aPropOpt.CreateTextProperties( rObj.mXPropSet,
mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() ) );
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x60006 ); // Size Shape To Fit Text
if ( nAngle < 0 )
nAngle = ( 36000 + nAngle ) % 36000;
if ( nAngle )
ImplFlipBoundingBox( rObj, aPropOpt );
}
else
{
mpEscherEx->OpenContainer( ESCHER_SpContainer );
nShapeID = mpEscherEx->GenerateShapeId();
nShapeType = ESCHER_ShpInst_TextBox;
mpEscherEx->AddShape( nShapeType, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor, nShapeID );
if ( rObj.ImplGetText() )
aPropOpt.CreateTextProperties( rObj.mXPropSet,
mpEscherEx->QueryTextID( rObj.GetShapeRef(),
rObj.GetShapeId() ) );
aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 );
aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 );
if( nAngle < 0 )
nAngle = ( 36000 + nAngle ) % 36000;
else
nAngle = ( 36000 - ( nAngle % 36000 ) );
nAngle *= 655;
nAngle += 0x8000;
nAngle &=~0xffff; // nAngle round to full degrees
aPropOpt.AddOpt( ESCHER_Prop_Rotation, nAngle );
mpEscherEx->SetGroupSnapRect( mpEscherEx->GetGroupLevel(),
rObj.GetRect() );
mpEscherEx->SetGroupLogicRect( mpEscherEx->GetGroupLevel(),
rObj.GetRect() );
}
rObj.SetAngle( nAngle );
aPropOpt.CreateShapeProperties( rObj.GetShapeRef() );
const SdrObject* sdrObj = rObj.GetSdrObject();
mpEscherEx->AddSdrObjectVMLObject(*sdrObj );
mpEscherEx->Commit( aPropOpt, rObj.GetRect());
// write the childanchor
mpEscherEx->AddChildAnchor( rObj.GetRect() );
#if defined EES_WRITE_EPP
// ClientAnchor
mpEscherEx->AddClientAnchor( maRect );
// ClientTextbox
mpEscherEx->OpenContainer( ESCHER_ClientTextbox );
mpEscherEx->AddAtom( 4, EPP_TextHeaderAtom );
*mpStrm << (sal_uInt32)EPP_TEXTTYPE_Other; // Text in a Shape
ImplWriteTextStyleAtom();
mpEscherEx->CloseContainer(); // ESCHER_ClientTextBox
#else // !EES_WRITE_EPP
if ( mpHostAppData )
{ //! the App has to control whether these are written or not
mpHostAppData->WriteClientAnchor( *mpEscherEx, rObj.GetRect() );
mpHostAppData->WriteClientData( *mpEscherEx );
mpHostAppData->WriteClientTextbox( *mpEscherEx );
}
#endif // EES_WRITE_EPP
mpEscherEx->CloseContainer(); // ESCHER_SpContainer
} while ( false );
mpEscherEx->LeaveGroup();
mpEscherEx->EndShape( nShapeType, nShapeID );
}
sal_uInt32 ImplEESdrWriter::ImplEnterAdditionalTextGroup( const Reference< XShape >& rShape,
const tools::Rectangle* pBoundRect )
{
mpHostAppData = mpEscherEx->EnterAdditionalTextGroup();
sal_uInt32 nGrpId = mpEscherEx->EnterGroup( pBoundRect );
mpHostAppData = mpEscherEx->StartShape( rShape, pBoundRect );
return nGrpId;
}
void ImplEESdrWriter::ImplInitPageValues()
{
mbIsTitlePossible = true; // With more than one title PowerPoint will fail.
}
void ImplEESdrWriter::ImplWritePage(
EscherSolverContainer& rSolverContainer, bool ooxmlExport )
{
ImplInitPageValues();
const sal_uInt32 nShapes = mXShapes->getCount();
for( sal_uInt32 n = 0; n < nShapes; ++n )
{
ImplEESdrObject aObj( *o3tl::doAccess<Reference<XShape>>(
mXShapes->getByIndex( n )) );
if( aObj.IsValid() )
{
ImplWriteShape( aObj, rSolverContainer, ooxmlExport );
}
}
}
ImplEESdrWriter::~ImplEESdrWriter()
{
DBG_ASSERT( !mpSolverContainer, "ImplEESdrWriter::~ImplEESdrWriter: unwritten SolverContainer" );
if (mXDrawPage.is())
mXDrawPage->dispose();
}
bool ImplEESdrWriter::ImplInitPage( const SdrPage& rPage )
{
rtl::Reference<SvxDrawPage> pSvxDrawPage;
if ( mpSdrPage != &rPage || !mXDrawPage.is() )
{
// eventually write SolverContainer of current page, deletes the Solver
ImplFlushSolverContainer();
mpSdrPage = nullptr;
if (mXDrawPage.is())
mXDrawPage->dispose();
pSvxDrawPage = new SvxDrawPage( const_cast<SdrPage*>(&rPage) );
mXDrawPage = pSvxDrawPage;
mXShapes = mXDrawPage;
if ( !mXShapes.is() )
return false;
ImplInitPageValues();
mpSdrPage = &rPage;
mpSolverContainer.reset( new EscherSolverContainer );
}
else
pSvxDrawPage = mXDrawPage;
return pSvxDrawPage != nullptr;
}
bool ImplEESdrWriter::ImplInitUnoShapes( const Reference< XShapes >& rxShapes )
{
// eventually write SolverContainer of current page, deletes the Solver
ImplFlushSolverContainer();
if( !rxShapes.is() )
return false;
mpSdrPage = nullptr;
mXDrawPage.clear();
mXShapes = rxShapes;
ImplInitPageValues();
mpSolverContainer.reset( new EscherSolverContainer );
return true;
}
void ImplEESdrWriter::ImplExitPage()
{
// close all groups before the solver container is written
while( mpEscherEx->GetGroupLevel() )
mpEscherEx->LeaveGroup();
ImplFlushSolverContainer();
mpSdrPage = nullptr; // reset page for next init
}
void ImplEESdrWriter::ImplFlushSolverContainer()
{
if ( mpSolverContainer )
{
mpSolverContainer->WriteSolver( mpEscherEx->GetStream() );
mpSolverContainer.reset();
}
}
void ImplEESdrWriter::ImplWriteCurrentPage(bool ooxmlExport)
{
assert(mpSolverContainer && "ImplEESdrWriter::ImplWriteCurrentPage: no SolverContainer");
ImplWritePage( *mpSolverContainer, ooxmlExport );
ImplExitPage();
}
sal_uInt32 ImplEESdrWriter::ImplWriteTheShape( ImplEESdrObject& rObj , bool ooxmlExport )
{
assert(mpSolverContainer && "ImplEESdrWriter::ImplWriteShape: no SolverContainer");
return ImplWriteShape( rObj, *mpSolverContainer, ooxmlExport );
}
void EscherEx::AddSdrPage( const SdrPage& rPage, bool ooxmlExport )
{
if ( mpImplEESdrWriter->ImplInitPage( rPage ) )
mpImplEESdrWriter->ImplWriteCurrentPage(ooxmlExport);
}
void EscherEx::AddUnoShapes( const Reference< XShapes >& rxShapes, bool ooxmlExport )
{
if ( mpImplEESdrWriter->ImplInitUnoShapes( rxShapes ) )
mpImplEESdrWriter->ImplWriteCurrentPage(ooxmlExport);
}
sal_uInt32 EscherEx::AddSdrObject(const SdrObject& rObj, bool ooxmlExport, sal_uInt32 nId)
{
ImplEESdrObject aObj(*mpImplEESdrWriter, rObj, mbOOXML , nId);
if( aObj.IsValid() )
return mpImplEESdrWriter->ImplWriteTheShape( aObj, ooxmlExport );
return 0;
}
void EscherEx::EndSdrObjectPage()
{
mpImplEESdrWriter->ImplExitPage();
}
EscherExHostAppData* EscherEx::StartShape( const Reference< XShape >& /* rShape */, const tools::Rectangle* /*pChildAnchor*/ )
{
return nullptr;
}
void EscherEx::EndShape( sal_uInt16 /* nShapeType */, sal_uInt32 /* nShapeID */ )
{
}
sal_uInt32 EscherEx::QueryTextID( const Reference< XShape >&, sal_uInt32 )
{
return 0;
}
// add a dummy rectangle shape into the escher stream
sal_uInt32 EscherEx::AddDummyShape()
{
OpenContainer( ESCHER_SpContainer );
sal_uInt32 nShapeID = GenerateShapeId();
AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor, nShapeID );
CloseContainer();
return nShapeID;
}
// static
const SdrObject* EscherEx::GetSdrObject( const Reference< XShape >& rShape )
{
const SdrObject* pRet = SdrObject::getSdrObjectFromXShape( rShape );
DBG_ASSERT( pRet, "EscherEx::GetSdrObject: no SdrObj" );
return pRet;
}
ImplEESdrObject::ImplEESdrObject( ImplEESdrWriter& rEx,
const SdrObject& rObj, bool bOOXML, sal_uInt32 nId) :
mnShapeId(nId),
mnTextSize( 0 ),
mnAngle( 0 ),
mbValid( false ),
mbPresObj( false ),
mbEmptyPresObj( false ),
mbOOXML(bOOXML)
{
SdrPage* pPage = rObj.getSdrPageFromSdrObject();
DBG_ASSERT( pPage, "ImplEESdrObject::ImplEESdrObject: no SdrPage" );
if( pPage && rEx.ImplInitPage( *pPage ) )
{
// why not declare a const parameter if the object will
// not be modified?
mXShape.set( const_cast<SdrObject*>(&rObj)->getUnoShape(), UNO_QUERY );
Init();
}
}
ImplEESdrObject::ImplEESdrObject( const Reference< XShape >& rShape ) :
mXShape( rShape ),
mnShapeId( 0 ),
mnTextSize( 0 ),
mnAngle( 0 ),
mbValid( false ),
mbPresObj( false ),
mbEmptyPresObj( false ),
mbOOXML(false)
{
Init();
}
ImplEESdrObject::~ImplEESdrObject()
{
}
static basegfx::B2DRange getUnrotatedGroupBoundRange(const Reference< XShape >& rxShape)
{
basegfx::B2DRange aRetval;
try
{
if(rxShape.is())
{
if(rxShape->getShapeType() == "com.sun.star.drawing.GroupShape")
{
// it's a group shape, iterate over children
const Reference< XIndexAccess > xXIndexAccess(rxShape, UNO_QUERY);
if(xXIndexAccess.is())
{
for(sal_uInt32 n(0), nCnt = xXIndexAccess->getCount(); n < nCnt; ++n)
{
const Reference< XShape > axShape(xXIndexAccess->getByIndex(n), UNO_QUERY);
if(axShape.is())
{
// we are calculating the bound for a group, correct rotation for sub-objects
// to get the unrotated bounds for the group
const basegfx::B2DRange aExtend(getUnrotatedGroupBoundRange(axShape));
aRetval.expand(aExtend);
}
}
}
}
else
{
// iT#s a xShape, get its transformation
const Reference< XPropertySet > xPropSet(rxShape, UNO_QUERY);
if(xPropSet.is())
{
const Any aAny = xPropSet->getPropertyValue(u"Transformation"_ustr);
if(aAny.hasValue())
{
HomogenMatrix3 aMatrix;
if(aAny >>= aMatrix)
{
basegfx::B2DHomMatrix aHomogenMatrix;
aHomogenMatrix.set(0, 0, aMatrix.Line1.Column1);
aHomogenMatrix.set(0, 1, aMatrix.Line1.Column2);
aHomogenMatrix.set(0, 2, aMatrix.Line1.Column3);
aHomogenMatrix.set(1, 0, aMatrix.Line2.Column1);
aHomogenMatrix.set(1, 1, aMatrix.Line2.Column2);
aHomogenMatrix.set(1, 2, aMatrix.Line2.Column3);
// For this to be a valid 2D transform matrix, the last row must be [0,0,1]
assert( aMatrix.Line3.Column1 == 0 );
assert( aMatrix.Line3.Column2 == 0 );
assert( aMatrix.Line3.Column3 == 1 );
basegfx::B2DVector aScale, aTranslate;
double fRotate, fShearX;
// decompose transformation
aHomogenMatrix.decompose(aScale, aTranslate, fRotate, fShearX);
// check if rotation needs to be corrected
if(!basegfx::fTools::equalZero(fRotate))
{
// to correct, keep in mind that ppt graphics are rotated around their center
const basegfx::B2DPoint aCenter(aHomogenMatrix * basegfx::B2DPoint(0.5, 0.5));
aHomogenMatrix.translate(-aCenter.getX(), -aCenter.getY());
aHomogenMatrix.rotate(-fRotate);
aHomogenMatrix.translate(aCenter.getX(), aCenter.getY());
}
// check if shear needs to be corrected (always correct shear,
// ppt does not know about it)
if(!basegfx::fTools::equalZero(fShearX))
{
const basegfx::B2DPoint aMinimum(aHomogenMatrix * basegfx::B2DPoint(0.0, 0.0));
aHomogenMatrix.translate(-aMinimum.getX(), -aMinimum.getY());
aHomogenMatrix.shearX(-fShearX);
aHomogenMatrix.translate(aMinimum.getX(), aMinimum.getY());
}
// create range. It's no longer rotated (or sheared), so use
// minimum and maximum values
aRetval.expand(aHomogenMatrix * basegfx::B2DPoint(0.0, 0.0));
aRetval.expand(aHomogenMatrix * basegfx::B2DPoint(1.0, 1.0));
}
}
}
}
}
}
catch(css::uno::Exception&)
{
}
return aRetval;
}
void ImplEESdrObject::Init()
{
mXPropSet.set( mXShape, UNO_QUERY );
if( !mXPropSet.is() )
return;
// detect name first to make below test (is group) work
mType = mXShape->getShapeType();
(void)mType.startsWith( "com.sun.star.", &mType ); // strip "com.sun.star."
(void)mType.endsWith( "Shape", &mType ); // strip "Shape"
if(GetType() == "drawing.Group")
{
// if it's a group, the unrotated range is needed for that group
const basegfx::B2DRange aUnrotatedRange(getUnrotatedGroupBoundRange(mXShape));
if (aUnrotatedRange.isEmpty())
{
SetRect(tools::Rectangle());
}
else
{
const Point aNewP(basegfx::fround<tools::Long>(aUnrotatedRange.getMinX()),
basegfx::fround<tools::Long>(aUnrotatedRange.getMinY()));
const Size aNewS(basegfx::fround<tools::Long>(aUnrotatedRange.getWidth()),
basegfx::fround<tools::Long>(aUnrotatedRange.getHeight()));
SetRect(ImplEESdrWriter::ImplMapPoint(aNewP), ImplEESdrWriter::ImplMapSize(aNewS));
}
}
else
{
// if it's no group, use position and size directly, rotated/sheared or not
const Point aOldP(mXShape->getPosition().X, mXShape->getPosition().Y);
const Size aOldS(mXShape->getSize().Width, mXShape->getSize().Height);
SetRect(ImplEESdrWriter::ImplMapPoint(aOldP), ImplEESdrWriter::ImplMapSize(aOldS));
}
if( ImplGetPropertyValue( u"IsPresentationObject"_ustr ) )
mbPresObj = ::cppu::any2bool( mAny );
if( mbPresObj && ImplGetPropertyValue( u"IsEmptyPresentationObject"_ustr ) )
mbEmptyPresObj = ::cppu::any2bool( mAny );
mbValid = true;
}
bool ImplEESdrObject::ImplGetPropertyValue( const OUString& rString )
{
bool bRetValue = false;
if( mbValid )
{
try
{
mAny = mXPropSet->getPropertyValue( rString );
if( mAny.hasValue() )
bRetValue = true;
}
catch( const css::uno::Exception& )
{
bRetValue = false;
}
}
return bRetValue;
}
void ImplEESdrObject::SetRect( const Point& rPos, const Size& rSz )
{
maRect = tools::Rectangle( rPos, rSz );
}
const SdrObject* ImplEESdrObject::GetSdrObject() const
{
return EscherEx::GetSdrObject( mXShape );
}
// loads and converts text from shape, result is saved in mnTextSize
sal_uInt32 ImplEESdrObject::ImplGetText()
{
Reference< XText > xXText( mXShape, UNO_QUERY );
mnTextSize = 0;
if (xXText.is())
{
try
{
mnTextSize = xXText->getString().getLength();
}
catch (const uno::RuntimeException&)
{
TOOLS_WARN_EXCEPTION("filter.ms", "ImplGetText");
}
}
return mnTextSize;
}
bool ImplEESdrObject::ImplHasText() const
{
Reference< XText > xXText( mXShape, UNO_QUERY );
return xXText.is() && !xXText->getString().isEmpty();
}
void ImplEESdrObject::SetOOXML(bool bOOXML)
{
mbOOXML = bOOXML;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V614 Uninitialized variable 'eAnchorType' used.
↑ V560 A part of conditional expression is always false: eFS == drawing::FillStyle_BITMAP.