blob: abfebf6127398e59ec4519aec25c5c3d1f7202aa [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
#include "oox/vml/vmlshape.hxx"
#include <com/sun/star/beans/PropertyValues.hpp>
#include <com/sun/star/awt/XControlModel.hpp>
#include <com/sun/star/drawing/PointSequenceSequence.hpp>
#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
#include <com/sun/star/drawing/XShapes.hpp>
#include <com/sun/star/graphic/XGraphic.hpp>
#include <rtl/math.hxx>
#include <rtl/ustrbuf.hxx>
#include "oox/core/xmlfilterbase.hxx"
#include "oox/drawingml/shapepropertymap.hxx"
#include "oox/helper/containerhelper.hxx"
#include "oox/helper/graphichelper.hxx"
#include "oox/helper/propertyset.hxx"
#include "oox/ole/axcontrol.hxx"
#include "oox/ole/axcontrolfragment.hxx"
#include "oox/ole/oleobjecthelper.hxx"
#include "oox/vml/vmldrawing.hxx"
#include "oox/vml/vmlshapecontainer.hxx"
#include "oox/vml/vmltextbox.hxx"
namespace oox {
namespace vml {
// ============================================================================
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::graphic;
using namespace ::com::sun::star::uno;
using ::oox::core::XmlFilterBase;
using ::rtl::OUString;
using ::rtl::OUStringBuffer;
// ============================================================================
namespace {
const sal_Int32 VML_SHAPETYPE_PICTUREFRAME = 75;
const sal_Int32 VML_SHAPETYPE_HOSTCONTROL = 201;
// ----------------------------------------------------------------------------
Point lclGetAbsPoint( const Point& rRelPoint, const Rectangle& rShapeRect, const Rectangle& rCoordSys )
{
double fWidthRatio = static_cast< double >( rShapeRect.Width ) / rCoordSys.Width;
double fHeightRatio = static_cast< double >( rShapeRect.Height ) / rCoordSys.Height;
Point aAbsPoint;
aAbsPoint.X = static_cast< sal_Int32 >( rShapeRect.X + fWidthRatio * (rRelPoint.X - rCoordSys.X) + 0.5 );
aAbsPoint.Y = static_cast< sal_Int32 >( rShapeRect.Y + fHeightRatio * (rRelPoint.Y - rCoordSys.Y) + 0.5 );
return aAbsPoint;
}
Rectangle lclGetAbsRect( const Rectangle& rRelRect, const Rectangle& rShapeRect, const Rectangle& rCoordSys )
{
double fWidthRatio = static_cast< double >( rShapeRect.Width ) / rCoordSys.Width;
double fHeightRatio = static_cast< double >( rShapeRect.Height ) / rCoordSys.Height;
Rectangle aAbsRect;
aAbsRect.X = static_cast< sal_Int32 >( rShapeRect.X + fWidthRatio * (rRelRect.X - rCoordSys.X) + 0.5 );
aAbsRect.Y = static_cast< sal_Int32 >( rShapeRect.Y + fHeightRatio * (rRelRect.Y - rCoordSys.Y) + 0.5 );
aAbsRect.Width = static_cast< sal_Int32 >( fWidthRatio * rRelRect.Width + 0.5 );
aAbsRect.Height = static_cast< sal_Int32 >( fHeightRatio * rRelRect.Height + 0.5 );
return aAbsRect;
}
} // namespace
// ============================================================================
ShapeTypeModel::ShapeTypeModel()
{
}
void ShapeTypeModel::assignUsed( const ShapeTypeModel& rSource )
{
moShapeType.assignIfUsed( rSource.moShapeType );
moCoordPos.assignIfUsed( rSource.moCoordPos );
moCoordSize.assignIfUsed( rSource.moCoordSize );
/* The style properties position, left, top, width, height, margin-left,
margin-top are not derived from shape template to shape. */
maStrokeModel.assignUsed( rSource.maStrokeModel );
maFillModel.assignUsed( rSource.maFillModel );
moGraphicPath.assignIfUsed( rSource.moGraphicPath );
moGraphicTitle.assignIfUsed( rSource.moGraphicTitle );
}
// ----------------------------------------------------------------------------
ShapeType::ShapeType( Drawing& rDrawing ) :
mrDrawing( rDrawing )
{
}
ShapeType::~ShapeType()
{
}
sal_Int32 ShapeType::getShapeType() const
{
return maTypeModel.moShapeType.get( 0 );
}
OUString ShapeType::getGraphicPath() const
{
return maTypeModel.moGraphicPath.get( OUString() );
}
Rectangle ShapeType::getCoordSystem() const
{
Int32Pair aCoordPos = maTypeModel.moCoordPos.get( Int32Pair( 0, 0 ) );
Int32Pair aCoordSize = maTypeModel.moCoordSize.get( Int32Pair( 1000, 1000 ) );
return Rectangle( aCoordPos.first, aCoordPos.second, aCoordSize.first, aCoordSize.second );
}
Rectangle ShapeType::getRectangle( const ShapeParentAnchor* pParentAnchor ) const
{
return pParentAnchor ?
lclGetAbsRect( getRelRectangle(), pParentAnchor->maShapeRect, pParentAnchor->maCoordSys ) :
getAbsRectangle();
}
Rectangle ShapeType::getAbsRectangle() const
{
const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper();
return Rectangle(
ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maLeft, 0, true, true ) + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maMarginLeft, 0, true, true ),
ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maTop, 0, false, true ) + ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maMarginTop, 0, false, true ),
ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maWidth, 0, true, true ),
ConversionHelper::decodeMeasureToHmm( rGraphicHelper, maTypeModel.maHeight, 0, false, true ) );
}
Rectangle ShapeType::getRelRectangle() const
{
return Rectangle(
maTypeModel.maLeft.toInt32(),
maTypeModel.maTop.toInt32(),
maTypeModel.maWidth.toInt32(),
maTypeModel.maHeight.toInt32() );
}
// ============================================================================
ClientData::ClientData() :
mnObjType( XML_TOKEN_INVALID ),
mnTextHAlign( XML_Left ),
mnTextVAlign( XML_Top ),
mnCol( -1 ),
mnRow( -1 ),
mnChecked( VML_CLIENTDATA_UNCHECKED ),
mnDropStyle( XML_Combo ),
mnDropLines( 1 ),
mnVal( 0 ),
mnMin( 0 ),
mnMax( 0 ),
mnInc( 0 ),
mnPage( 0 ),
mnSelType( XML_Single ),
mnVTEdit( VML_CLIENTDATA_TEXT ),
mbPrintObject( true ),
mbVisible( false ),
mbDde( false ),
mbNo3D( false ),
mbNo3D2( false ),
mbMultiLine( false ),
mbVScroll( false ),
mbSecretEdit( false )
{
}
// ----------------------------------------------------------------------------
ShapeModel::ShapeModel()
{
}
ShapeModel::~ShapeModel()
{
}
TextBox& ShapeModel::createTextBox()
{
mxTextBox.reset( new TextBox );
return *mxTextBox;
}
ClientData& ShapeModel::createClientData()
{
mxClientData.reset( new ClientData );
return *mxClientData;
}
// ----------------------------------------------------------------------------
ShapeBase::ShapeBase( Drawing& rDrawing ) :
ShapeType( rDrawing )
{
}
void ShapeBase::finalizeFragmentImport()
{
// resolve shape template reference
if( (maShapeModel.maType.getLength() > 1) && (maShapeModel.maType[ 0 ] == '#') )
if( const ShapeType* pShapeType = mrDrawing.getShapes().getShapeTypeById( maShapeModel.maType.copy( 1 ), true ) )
maTypeModel.assignUsed( pShapeType->getTypeModel() );
}
OUString ShapeBase::getShapeName() const
{
if( maTypeModel.maShapeName.getLength() > 0 )
return maTypeModel.maShapeName;
OUString aBaseName = mrDrawing.getShapeBaseName( *this );
if( aBaseName.getLength() > 0 )
{
sal_Int32 nShapeIdx = mrDrawing.getLocalShapeIndex( getShapeId() );
if( nShapeIdx > 0 )
return OUStringBuffer( aBaseName ).append( sal_Unicode( ' ' ) ).append( nShapeIdx ).makeStringAndClear();
}
return OUString();
}
const ShapeType* ShapeBase::getChildTypeById( const OUString& ) const
{
return 0;
}
const ShapeBase* ShapeBase::getChildById( const OUString& ) const
{
return 0;
}
Reference< XShape > ShapeBase::convertAndInsert( const Reference< XShapes >& rxShapes, const ShapeParentAnchor* pParentAnchor ) const
{
Reference< XShape > xShape;
if( mrDrawing.isShapeSupported( *this ) )
{
/* Calculate shape rectangle. Applications may do something special
according to some imported shape client data (e.g. Excel cell anchor). */
Rectangle aShapeRect = calcShapeRectangle( pParentAnchor );
// convert the shape, if the calculated rectangle is not empty
if( ((aShapeRect.Width > 0) || (aShapeRect.Height > 0)) && rxShapes.is() )
{
xShape = implConvertAndInsert( rxShapes, aShapeRect );
if( xShape.is() )
{
// set imported or generated shape name (not supported by form controls)
PropertySet aShapeProp( xShape );
if( aShapeProp.hasProperty( PROP_Name ) )
aShapeProp.setProperty( PROP_Name, getShapeName() );
/* Notify the drawing that a new shape has been inserted. For
convenience, pass the rectangle that contains position and
size of the shape. */
bool bGroupChild = pParentAnchor != 0;
mrDrawing.notifyXShapeInserted( xShape, aShapeRect, *this, bGroupChild );
}
}
}
return xShape;
}
void ShapeBase::convertFormatting( const Reference< XShape >& rxShape, const ShapeParentAnchor* pParentAnchor ) const
{
if( rxShape.is() )
{
/* Calculate shape rectangle. Applications may do something special
according to some imported shape client data (e.g. Excel cell anchor). */
Rectangle aShapeRect = calcShapeRectangle( pParentAnchor );
// convert the shape, if the calculated rectangle is not empty
if( (aShapeRect.Width > 0) || (aShapeRect.Height > 0) )
{
rxShape->setPosition( Point( aShapeRect.X, aShapeRect.Y ) );
rxShape->setSize( Size( aShapeRect.Width, aShapeRect.Height ) );
convertShapeProperties( rxShape );
}
}
}
// protected ------------------------------------------------------------------
Rectangle ShapeBase::calcShapeRectangle( const ShapeParentAnchor* pParentAnchor ) const
{
/* Calculate shape rectangle. Applications may do something special
according to some imported shape client data (e.g. Excel cell anchor). */
Rectangle aShapeRect;
const ClientData* pClientData = getClientData();
if( !pClientData || !mrDrawing.convertClientAnchor( aShapeRect, pClientData->maAnchor ) )
aShapeRect = getRectangle( pParentAnchor );
return aShapeRect;
}
void ShapeBase::convertShapeProperties( const Reference< XShape >& rxShape ) const
{
::oox::drawingml::ShapePropertyMap aPropMap( mrDrawing.getFilter().getModelObjectHelper() );
const GraphicHelper& rGraphicHelper = mrDrawing.getFilter().getGraphicHelper();
maTypeModel.maStrokeModel.pushToPropMap( aPropMap, rGraphicHelper );
maTypeModel.maFillModel.pushToPropMap( aPropMap, rGraphicHelper );
PropertySet( rxShape ).setProperties( aPropMap );
}
// ============================================================================
SimpleShape::SimpleShape( Drawing& rDrawing, const OUString& rService ) :
ShapeBase( rDrawing ),
maService( rService )
{
}
Reference< XShape > SimpleShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const
{
Reference< XShape > xShape = mrDrawing.createAndInsertXShape( maService, rxShapes, rShapeRect );
convertShapeProperties( xShape );
return xShape;
}
// ============================================================================
RectangleShape::RectangleShape( Drawing& rDrawing ) :
SimpleShape( rDrawing, CREATE_OUSTRING( "com.sun.star.drawing.RectangleShape" ) )
{
}
// ============================================================================
EllipseShape::EllipseShape( Drawing& rDrawing ) :
SimpleShape( rDrawing, CREATE_OUSTRING( "com.sun.star.drawing.EllipseShape" ) )
{
}
// ============================================================================
PolyLineShape::PolyLineShape( Drawing& rDrawing ) :
SimpleShape( rDrawing, CREATE_OUSTRING( "com.sun.star.drawing.PolyLineShape" ) )
{
}
Reference< XShape > PolyLineShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const
{
Reference< XShape > xShape = SimpleShape::implConvertAndInsert( rxShapes, rShapeRect );
// polygon path
Rectangle aCoordSys = getCoordSystem();
if( !maShapeModel.maPoints.empty() && (aCoordSys.Width > 0) && (aCoordSys.Height > 0) )
{
::std::vector< Point > aAbsPoints;
for( ShapeModel::PointVector::const_iterator aIt = maShapeModel.maPoints.begin(), aEnd = maShapeModel.maPoints.end(); aIt != aEnd; ++aIt )
aAbsPoints.push_back( lclGetAbsPoint( *aIt, rShapeRect, aCoordSys ) );
PointSequenceSequence aPointSeq( 1 );
aPointSeq[ 0 ] = ContainerHelper::vectorToSequence( aAbsPoints );
PropertySet aPropSet( xShape );
aPropSet.setProperty( PROP_PolyPolygon, aPointSeq );
}
return xShape;
}
// ============================================================================
CustomShape::CustomShape( Drawing& rDrawing ) :
SimpleShape( rDrawing, CREATE_OUSTRING( "com.sun.star.drawing.CustomShape" ) )
{
}
Reference< XShape > CustomShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const
{
// try to create a custom shape
Reference< XShape > xShape = SimpleShape::implConvertAndInsert( rxShapes, rShapeRect );
if( xShape.is() ) try
{
// create the custom shape geometry
Reference< XEnhancedCustomShapeDefaulter > xDefaulter( xShape, UNO_QUERY_THROW );
xDefaulter->createCustomShapeDefaults( OUString::valueOf( getShapeType() ) );
// convert common properties
convertShapeProperties( xShape );
}
catch( Exception& )
{
}
return xShape;
}
// ============================================================================
ComplexShape::ComplexShape( Drawing& rDrawing ) :
CustomShape( rDrawing )
{
}
Reference< XShape > ComplexShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const
{
XmlFilterBase& rFilter = mrDrawing.getFilter();
sal_Int32 nShapeType = getShapeType();
OUString aGraphicPath = getGraphicPath();
// try to find registered OLE object info
if( const OleObjectInfo* pOleObjectInfo = mrDrawing.getOleObjectInfo( maTypeModel.maShapeId ) )
{
OSL_ENSURE( nShapeType == VML_SHAPETYPE_PICTUREFRAME, "ComplexShape::implConvertAndInsert - unexpected shape type" );
// if OLE object is embedded into a DrawingML shape (PPTX), do not create it here
if( pOleObjectInfo->mbDmlShape )
return Reference< XShape >();
PropertyMap aOleProps;
Size aOleSize( rShapeRect.Width, rShapeRect.Height );
if( rFilter.getOleObjectHelper().importOleObject( aOleProps, *pOleObjectInfo, aOleSize ) )
{
Reference< XShape > xShape = mrDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.OLE2Shape" ), rxShapes, rShapeRect );
if( xShape.is() )
{
// set the replacement graphic
if( aGraphicPath.getLength() > 0 )
{
Reference< XGraphic > xGraphic = rFilter.getGraphicHelper().importEmbeddedGraphic( aGraphicPath );
if( xGraphic.is() )
aOleProps[ PROP_Graphic ] <<= xGraphic;
}
PropertySet aPropSet( xShape );
aPropSet.setProperties( aOleProps );
return xShape;
}
}
}
// try to find registered form control info
const ControlInfo* pControlInfo = mrDrawing.getControlInfo( maTypeModel.maShapeId );
if( pControlInfo && (pControlInfo->maFragmentPath.getLength() > 0) )
{
OSL_ENSURE( nShapeType == VML_SHAPETYPE_HOSTCONTROL, "ComplexShape::implConvertAndInsert - unexpected shape type" );
OUString aShapeName = getShapeName();
if( aShapeName.getLength() > 0 )
{
OSL_ENSURE( aShapeName == pControlInfo->maName, "ComplexShape::implConvertAndInsert - control name mismatch" );
// load the control properties from fragment
::oox::ole::EmbeddedControl aControl( aShapeName );
if( rFilter.importFragment( new ::oox::ole::AxControlFragment( rFilter, pControlInfo->maFragmentPath, aControl ) ) )
{
// create and return the control shape (including control model)
sal_Int32 nCtrlIndex = -1;
Reference< XShape > xShape = mrDrawing.createAndInsertXControlShape( aControl, rxShapes, rShapeRect, nCtrlIndex );
// on error, proceed and try to create picture from replacement image
if( xShape.is() )
return xShape;
}
}
}
// host application wants to create the shape (do not try failed OLE controls again)
if( (nShapeType == VML_SHAPETYPE_HOSTCONTROL) && !pControlInfo )
{
OSL_ENSURE( getClientData(), "ComplexShape::implConvertAndInsert - missing client data" );
Reference< XShape > xShape = mrDrawing.createAndInsertClientXShape( *this, rxShapes, rShapeRect );
if( xShape.is() )
return xShape;
}
// try to create a picture object
if( aGraphicPath.getLength() > 0 )
{
Reference< XShape > xShape = mrDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.GraphicObjectShape" ), rxShapes, rShapeRect );
if( xShape.is() )
{
OUString aGraphicUrl = rFilter.getGraphicHelper().importEmbeddedGraphicObject( aGraphicPath );
if( aGraphicUrl.getLength() > 0 )
{
PropertySet aPropSet( xShape );
aPropSet.setProperty( PROP_GraphicURL, aGraphicUrl );
}
}
return xShape;
}
// default: try to create a custom shape
return CustomShape::implConvertAndInsert( rxShapes, rShapeRect );
}
// ============================================================================
GroupShape::GroupShape( Drawing& rDrawing ) :
ShapeBase( rDrawing ),
mxChildren( new ShapeContainer( rDrawing ) )
{
}
GroupShape::~GroupShape()
{
}
void GroupShape::finalizeFragmentImport()
{
// basic shape processing
ShapeBase::finalizeFragmentImport();
// finalize all child shapes
mxChildren->finalizeFragmentImport();
}
const ShapeType* GroupShape::getChildTypeById( const OUString& rShapeId ) const
{
return mxChildren->getShapeTypeById( rShapeId, true );
}
const ShapeBase* GroupShape::getChildById( const OUString& rShapeId ) const
{
return mxChildren->getShapeById( rShapeId, true );
}
Reference< XShape > GroupShape::implConvertAndInsert( const Reference< XShapes >& rxShapes, const Rectangle& rShapeRect ) const
{
Reference< XShape > xGroupShape;
// check that this shape contains children and a valid coordinate system
ShapeParentAnchor aParentAnchor;
aParentAnchor.maShapeRect = rShapeRect;
aParentAnchor.maCoordSys = getCoordSystem();
if( !mxChildren->empty() && (aParentAnchor.maCoordSys.Width > 0) && (aParentAnchor.maCoordSys.Height > 0) ) try
{
xGroupShape = mrDrawing.createAndInsertXShape( CREATE_OUSTRING( "com.sun.star.drawing.GroupShape" ), rxShapes, rShapeRect );
Reference< XShapes > xChildShapes( xGroupShape, UNO_QUERY_THROW );
mxChildren->convertAndInsert( xChildShapes, &aParentAnchor );
// no child shape has been created - delete the group shape
if( !xChildShapes->hasElements() )
{
rxShapes->remove( xGroupShape );
xGroupShape.clear();
}
}
catch( Exception& )
{
}
return xGroupShape;
}
// ============================================================================
} // namespace vml
} // namespace oox