1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: vmlshape.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #include "oox/vml/vmlshape.hxx"
32 #include <rtl/math.hxx>
33 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
34 #include <com/sun/star/beans/PropertyValues.hpp>
35 #include <com/sun/star/awt/XControlModel.hpp>
36 #include <com/sun/star/drawing/FillStyle.hpp>
37 #include <com/sun/star/drawing/PointSequenceSequence.hpp>
38 #include <com/sun/star/drawing/XControlShape.hpp>
39 #include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
40 #include <com/sun/star/drawing/XShapes.hpp>
41 #include <com/sun/star/graphic/XGraphic.hpp>
42 #include "properties.hxx"
43 #include "oox/helper/propertymap.hxx"
44 #include "oox/helper/propertyset.hxx"
45 #include "oox/core/xmlfilterbase.hxx"
46 #include "oox/ole/axcontrol.hxx"
47 #include "oox/ole/axcontrolfragment.hxx"
48 #include "oox/ole/oleobjecthelper.hxx"
49 #include "oox/vml/vmldrawing.hxx"
50 #include "oox/vml/vmlshapecontainer.hxx"
52 using ::rtl::OUString
;
53 using ::com::sun::star::uno::Exception
;
54 using ::com::sun::star::uno::Reference
;
55 using ::com::sun::star::uno::UNO_QUERY
;
56 using ::com::sun::star::uno::UNO_QUERY_THROW
;
57 using ::com::sun::star::uno::UNO_SET_THROW
;
58 using ::com::sun::star::lang::XMultiServiceFactory
;
59 using ::com::sun::star::awt::Point
;
60 using ::com::sun::star::awt::Rectangle
;
61 using ::com::sun::star::awt::Size
;
62 using ::com::sun::star::awt::XControlModel
;
63 using ::com::sun::star::graphic::XGraphic
;
64 using ::com::sun::star::drawing::PointSequenceSequence
;
65 using ::com::sun::star::drawing::XControlShape
;
66 using ::com::sun::star::drawing::XEnhancedCustomShapeDefaulter
;
67 using ::com::sun::star::drawing::XShape
;
68 using ::com::sun::star::drawing::XShapes
;
69 using ::oox::core::XmlFilterBase
;
74 // ============================================================================
78 sal_Int32
lclGetMeasure( const XmlFilterBase
& /*rFilter*/, const OUString
& rValue
, sal_Int32 nRefValue
)
80 // default for missing values is 0
81 if( rValue
.getLength() == 0 )
84 // TODO: according to spec, value may contain "auto"
85 if( rValue
.equalsAscii( "auto" ) )
88 // extract the double value and find start position of unit characters
89 rtl_math_ConversionStatus eConvStatus
= rtl_math_ConversionStatus_Ok
;
90 sal_Int32 nEndPos
= 0;
91 double fValue
= ::rtl::math::stringToDouble( rValue
, '.', '\0', &eConvStatus
, &nEndPos
);
92 if( (eConvStatus
!= rtl_math_ConversionStatus_Ok
) || (fValue
== 0.0) )
95 // process trailing unit, convert to 1/100 mm
96 static const OUString saPx
= CREATE_OUSTRING( "px" );
97 OUString aUnit
= ((0 < nEndPos
) && (nEndPos
< rValue
.getLength())) ? rValue
.copy( nEndPos
) : saPx
;
98 if( aUnit
.getLength() == 2 )
100 sal_Unicode cChar1
= aUnit
[ 0 ];
101 sal_Unicode cChar2
= aUnit
[ 1 ];
102 if( (cChar1
== 'i') && (cChar2
== 'n') ) // 1 inch = 2540 1/100mm
104 else if( (cChar1
== 'c') && (cChar2
== 'm') ) // 1 cm = 1000 1/100mm
106 else if( (cChar1
== 'm') && (cChar2
== 'm') ) // 1 mm = 100 1/100mm
108 else if( (cChar1
== 'p') && (cChar2
== 't') ) // 1 point = 1/72 inch
109 fValue
*= 2540.0 / 72.0;
110 else if( (cChar1
== 'p') && (cChar2
== 'c') ) // 1 pica = 1/6 inch
111 fValue
*= 2540.0 / 6.0;
112 else if( (cChar1
== 'e') && (cChar2
== 'm') ) // relative to refvalue
114 else if( (cChar1
== 'p') && (cChar2
== 'x') ) // 1 pixel, dependent on output device
117 else if( (aUnit
.getLength() == 1) && (aUnit
[ 0 ] == '%') )
119 fValue
*= nRefValue
/ 100.0;
123 OSL_ENSURE( false, "lclGetMeasure - unknown measure unit" );
126 return static_cast< sal_Int32
>( fValue
+ 0.5 );
129 Point
lclGetAbsPoint( const Point
& rRelPoint
, const Rectangle
& rShapeRect
, const Rectangle
& rCoordSys
)
131 double fWidthRatio
= static_cast< double >( rShapeRect
.Width
) / rCoordSys
.Width
;
132 double fHeightRatio
= static_cast< double >( rShapeRect
.Height
) / rCoordSys
.Height
;
134 aAbsPoint
.X
= static_cast< sal_Int32
>( rShapeRect
.X
+ fWidthRatio
* (rRelPoint
.X
- rCoordSys
.X
) + 0.5 );
135 aAbsPoint
.Y
= static_cast< sal_Int32
>( rShapeRect
.Y
+ fHeightRatio
* (rRelPoint
.Y
- rCoordSys
.Y
) + 0.5 );
139 Rectangle
lclGetAbsRect( const Rectangle
& rRelRect
, const Rectangle
& rShapeRect
, const Rectangle
& rCoordSys
)
141 double fWidthRatio
= static_cast< double >( rShapeRect
.Width
) / rCoordSys
.Width
;
142 double fHeightRatio
= static_cast< double >( rShapeRect
.Height
) / rCoordSys
.Height
;
144 aAbsRect
.X
= static_cast< sal_Int32
>( rShapeRect
.X
+ fWidthRatio
* (rRelRect
.X
- rCoordSys
.X
) + 0.5 );
145 aAbsRect
.Y
= static_cast< sal_Int32
>( rShapeRect
.Y
+ fHeightRatio
* (rRelRect
.Y
- rCoordSys
.Y
) + 0.5 );
146 aAbsRect
.Width
= static_cast< sal_Int32
>( fWidthRatio
* rRelRect
.Width
+ 0.5 );
147 aAbsRect
.Height
= static_cast< sal_Int32
>( fHeightRatio
* rRelRect
.Height
+ 0.5 );
151 Reference
< XShape
> lclCreateXShape( const XmlFilterBase
& rFilter
, const OUString
& rService
)
153 OSL_ENSURE( rService
.getLength() > 0, "lclCreateXShape - missing UNO shape service name" );
154 Reference
< XShape
> xShape
;
157 Reference
< XMultiServiceFactory
> xFactory( rFilter
.getModel(), UNO_QUERY_THROW
);
158 xShape
.set( xFactory
->createInstance( rService
), UNO_QUERY_THROW
);
163 OSL_ENSURE( xShape
.is(), "lclCreateXShape - cannot instanciate shape object" );
167 void lclInsertXShape( const Reference
< XShapes
>& rxShapes
, const Reference
< XShape
>& rxShape
, const Rectangle
& rShapeRect
)
169 OSL_ENSURE( rxShapes
.is(), "lclInsertXShape - missing XShapes container" );
170 OSL_ENSURE( rxShape
.is(), "lclInsertXShape - missing XShape" );
171 if( rxShapes
.is() && rxShape
.is() ) try
173 // insert shape into passed shape collection (maybe drawpage or group shape)
174 rxShapes
->add( rxShape
);
176 rxShape
->setPosition( Point( rShapeRect
.X
, rShapeRect
.Y
) );
177 rxShape
->setSize( Size( rShapeRect
.Width
, rShapeRect
.Height
) );
184 Reference
< XShape
> lclCreateAndInsertXShape( const XmlFilterBase
& rFilter
,
185 const Reference
< XShapes
>& rxShapes
, const OUString
& rService
, const Rectangle
& rShapeRect
)
187 Reference
< XShape
> xShape
= lclCreateXShape( rFilter
, rService
);
188 lclInsertXShape( rxShapes
, xShape
, rShapeRect
);
194 // ============================================================================
196 ShapeTypeModel::ShapeTypeModel()
200 void ShapeTypeModel::assignUsed( const ShapeTypeModel
& rSource
)
202 monShapeType
.assignIfUsed( rSource
.monShapeType
);
203 monCoordLeft
.assignIfUsed( rSource
.monCoordLeft
);
204 monCoordTop
.assignIfUsed( rSource
.monCoordTop
);
205 monCoordWidth
.assignIfUsed( rSource
.monCoordWidth
);
206 monCoordHeight
.assignIfUsed( rSource
.monCoordHeight
);
207 /* The style properties position, left, top, width, height, margin-left,
208 margin-top are not derived from shape template to shape. */
209 mobStroked
.assignIfUsed( rSource
.mobStroked
);
210 moStrokeColor
.assignIfUsed( rSource
.moStrokeColor
);
211 mobFilled
.assignIfUsed( rSource
.mobFilled
);
212 moFillColor
.assignIfUsed( rSource
.moFillColor
);
213 moGraphicPath
.assignIfUsed( rSource
.moGraphicPath
);
214 moGraphicTitle
.assignIfUsed( rSource
.moGraphicTitle
);
217 // ----------------------------------------------------------------------------
219 ShapeType::ShapeType( const Drawing
& rDrawing
) :
220 mrDrawing( rDrawing
)
224 ShapeType::~ShapeType()
228 OUString
ShapeType::getGraphicPath() const
230 return maTypeModel
.moGraphicPath
.get( OUString() );
233 Rectangle
ShapeType::getCoordSystem() const
236 maTypeModel
.monCoordLeft
.get( 0 ),
237 maTypeModel
.monCoordTop
.get( 0 ),
238 maTypeModel
.monCoordWidth
.get( 1000 ),
239 maTypeModel
.monCoordHeight
.get( 1000 ) );
242 Rectangle
ShapeType::getRectangle( const ShapeParentAnchor
* pParentAnchor
) const
244 return pParentAnchor
?
245 lclGetAbsRect( getRelRectangle(), pParentAnchor
->maShapeRect
, pParentAnchor
->maCoordSys
) :
249 Rectangle
ShapeType::getAbsRectangle() const
251 const XmlFilterBase
& rFilter
= mrDrawing
.getFilter();
253 lclGetMeasure( rFilter
, maTypeModel
.maLeft
, 0 ) + lclGetMeasure( rFilter
, maTypeModel
.maMarginLeft
, 0 ),
254 lclGetMeasure( rFilter
, maTypeModel
.maTop
, 0 ) + lclGetMeasure( rFilter
, maTypeModel
.maMarginTop
, 0 ),
255 lclGetMeasure( rFilter
, maTypeModel
.maWidth
, 0 ),
256 lclGetMeasure( rFilter
, maTypeModel
.maHeight
, 0 ) );
259 Rectangle
ShapeType::getRelRectangle() const
262 maTypeModel
.maLeft
.toInt32(),
263 maTypeModel
.maTop
.toInt32(),
264 maTypeModel
.maWidth
.toInt32(),
265 maTypeModel
.maHeight
.toInt32() );
268 // ============================================================================
270 ShapeClientData::ShapeClientData() :
271 mnObjType( XML_TOKEN_INVALID
),
272 mbPrintObject( true )
276 // ----------------------------------------------------------------------------
278 ShapeModel::ShapeModel()
282 ShapeClientData
& ShapeModel::createClientData()
284 mxClientData
.reset( new ShapeClientData
);
285 return *mxClientData
;
288 // ----------------------------------------------------------------------------
290 ShapeBase::ShapeBase( const Drawing
& rDrawing
) :
291 ShapeType( rDrawing
)
295 void ShapeBase::finalizeFragmentImport()
297 // resolve shape template reference
298 if( (maShapeModel
.maType
.getLength() > 1) && (maShapeModel
.maType
[ 0 ] == '#') )
299 if( const ShapeType
* pShapeType
= mrDrawing
.getShapes().getShapeTypeById( maShapeModel
.maType
.copy( 1 ), true ) )
300 maTypeModel
.assignUsed( pShapeType
->getTypeModel() );
303 const ShapeType
* ShapeBase::getChildTypeById( const OUString
& ) const
308 const ShapeBase
* ShapeBase::getChildById( const OUString
& ) const
313 Reference
< XShape
> ShapeBase::convertAndInsert( const Reference
< XShapes
>& rxShapes
, const ShapeParentAnchor
* pParentAnchor
) const
315 Reference
< XShape
> xShape
;
316 /* Calculate shape rectangle. Applications may do something special
317 according to some imported shape client data (e.g. Excel cell anchor). */
318 Rectangle aShapeRect
;
319 if( !maShapeModel
.mxClientData
.get() || !mrDrawing
.convertShapeClientAnchor( aShapeRect
, maShapeModel
.mxClientData
->maAnchor
) )
320 aShapeRect
= getRectangle( pParentAnchor
);
321 // convert the shape, if the calculated rectangle is not empty
322 if( ((aShapeRect
.Width
> 0) || (aShapeRect
.Height
> 0)) && rxShapes
.is() )
323 xShape
= implConvertAndInsert( rxShapes
, aShapeRect
);
327 // protected ------------------------------------------------------------------
329 void ShapeBase::convertShapeProperties( const Reference
< XShape
>& rxShape
) const
332 PropertySet
aPropSet( rxShape
);
335 bool bFilled
= maTypeModel
.mobFilled
.get( true );
336 aPropSet
.setProperty( PROP_FillStyle
, bFilled
? ::com::sun::star::drawing::FillStyle_SOLID
: ::com::sun::star::drawing::FillStyle_NONE
);
339 // ============================================================================
341 SimpleShape::SimpleShape( const Drawing
& rDrawing
, const OUString
& rService
) :
342 ShapeBase( rDrawing
),
343 maService( rService
)
347 Reference
< XShape
> SimpleShape::implConvertAndInsert( const Reference
< XShapes
>& rxShapes
, const Rectangle
& rShapeRect
) const
349 Reference
< XShape
> xShape
= lclCreateAndInsertXShape( mrDrawing
.getFilter(), rxShapes
, maService
, rShapeRect
);
350 convertShapeProperties( xShape
);
354 // ============================================================================
356 RectangleShape::RectangleShape( const Drawing
& rDrawing
) :
357 SimpleShape( rDrawing
, CREATE_OUSTRING( "com.sun.star.drawing.RectangleShape" ) )
361 // ============================================================================
363 EllipseShape::EllipseShape( const Drawing
& rDrawing
) :
364 SimpleShape( rDrawing
, CREATE_OUSTRING( "com.sun.star.drawing.EllipseShape" ) )
368 // ============================================================================
370 PolyLineShape::PolyLineShape( const Drawing
& rDrawing
) :
371 SimpleShape( rDrawing
, CREATE_OUSTRING( "com.sun.star.drawing.PolyLineShape" ) )
375 Reference
< XShape
> PolyLineShape::implConvertAndInsert( const Reference
< XShapes
>& rxShapes
, const Rectangle
& rShapeRect
) const
377 Reference
< XShape
> xShape
= SimpleShape::implConvertAndInsert( rxShapes
, rShapeRect
);
379 Rectangle aCoordSys
= getCoordSystem();
380 if( !maShapeModel
.maPoints
.empty() && (aCoordSys
.Width
> 0) && (aCoordSys
.Height
> 0) )
382 ::std::vector
< Point
> aAbsPoints
;
383 for( ShapeModel::PointVector::const_iterator aIt
= maShapeModel
.maPoints
.begin(), aEnd
= maShapeModel
.maPoints
.end(); aIt
!= aEnd
; ++aIt
)
384 aAbsPoints
.push_back( lclGetAbsPoint( *aIt
, rShapeRect
, aCoordSys
) );
385 PointSequenceSequence
aPointSeq( 1 );
386 aPointSeq
[ 0 ] = ContainerHelper::vectorToSequence( aAbsPoints
);
387 PropertySet
aPropSet( xShape
);
388 aPropSet
.setProperty( PROP_PolyPolygon
, aPointSeq
);
393 // ============================================================================
395 CustomShape::CustomShape( const Drawing
& rDrawing
) :
396 SimpleShape( rDrawing
, CREATE_OUSTRING( "com.sun.star.drawing.CustomShape" ) )
400 Reference
< XShape
> CustomShape::implConvertAndInsert( const Reference
< XShapes
>& rxShapes
, const Rectangle
& rShapeRect
) const
402 // try to create a custom shape
403 Reference
< XShape
> xShape
= SimpleShape::implConvertAndInsert( rxShapes
, rShapeRect
);
404 if( xShape
.is() ) try
406 // create the custom shape geometry
407 Reference
< XEnhancedCustomShapeDefaulter
> xDefaulter( xShape
, UNO_QUERY_THROW
);
408 xDefaulter
->createCustomShapeDefaults( OUString::valueOf( maTypeModel
.monShapeType
.get( 0 ) ) );
409 // convert common properties
410 convertShapeProperties( xShape
);
418 // ============================================================================
420 ComplexShape::ComplexShape( const Drawing
& rDrawing
) :
421 CustomShape( rDrawing
)
425 Reference
< XShape
> ComplexShape::implConvertAndInsert( const Reference
< XShapes
>& rxShapes
, const Rectangle
& rShapeRect
) const
427 XmlFilterBase
& rFilter
= mrDrawing
.getFilter();
428 OUString aGraphicPath
= getGraphicPath();
430 // try to find registered OLE object info
431 if( const OleObjectInfo
* pOleObjectInfo
= mrDrawing
.getOleObjectInfo( maTypeModel
.maShapeId
) )
433 // if OLE object is embedded into a DrawingML shape (PPTX), do not create it here
434 if( pOleObjectInfo
->mbDmlShape
)
435 return Reference
< XShape
>();
437 PropertyMap aOleProps
;
438 Size
aOleSize( rShapeRect
.Width
, rShapeRect
.Height
);
439 if( rFilter
.getOleObjectHelper().importOleObject( aOleProps
, *pOleObjectInfo
, aOleSize
) )
441 Reference
< XShape
> xShape
= lclCreateAndInsertXShape( rFilter
, rxShapes
, CREATE_OUSTRING( "com.sun.star.drawing.OLE2Shape" ), rShapeRect
);
444 // set the replacement graphic
445 if( aGraphicPath
.getLength() > 0 )
447 Reference
< XGraphic
> xGraphic
= rFilter
.importEmbeddedGraphic( aGraphicPath
);
449 aOleProps
[ PROP_Graphic
] <<= xGraphic
;
452 PropertySet
aPropSet( xShape
);
453 aPropSet
.setProperties( aOleProps
);
460 // try to find registered form control info
461 const ControlInfo
* pControlInfo
= mrDrawing
.getControlInfo( maTypeModel
.maShapeId
);
462 if( pControlInfo
&& (pControlInfo
->maFragmentPath
.getLength() > 0) && (maTypeModel
.maName
.getLength() > 0) )
464 OSL_ENSURE( maTypeModel
.maName
== pControlInfo
->maName
, "ComplexShape::implConvertAndInsert - control name mismatch" );
465 ::oox::ole::AxControl
aControl( maTypeModel
.maName
);
466 // load the control properties from fragment
467 if( rFilter
.importFragment( new ::oox::ole::AxControlFragment( rFilter
, pControlInfo
->maFragmentPath
, aControl
) ) ) try
469 // create control model and insert it into the form of the draw page
470 Reference
< XControlModel
> xCtrlModel( aControl
.convertAndInsert( mrDrawing
.getControlHelper() ), UNO_SET_THROW
);
471 if( maShapeModel
.mxClientData
.get() )
472 mrDrawing
.convertControlClientData( xCtrlModel
, *maShapeModel
.mxClientData
);
474 // create the control shape, set control model at the shape
475 Reference
< XShape
> xShape
= lclCreateAndInsertXShape(
476 rFilter
, rxShapes
, CREATE_OUSTRING( "com.sun.star.drawing.ControlShape" ), rShapeRect
);
477 Reference
< XControlShape
> xCtrlShape( xShape
, UNO_QUERY
); // do not throw, but always return the shape
478 if( xCtrlShape
.is() )
479 xCtrlShape
->setControl( xCtrlModel
);
485 // on error, proceed and try to create picture from replacement image
488 // try to create a picture object
489 if( aGraphicPath
.getLength() > 0 )
491 Reference
< XShape
> xShape
= lclCreateAndInsertXShape( rFilter
, rxShapes
, CREATE_OUSTRING( "com.sun.star.drawing.GraphicObjectShape" ), rShapeRect
);
494 OUString aGraphicUrl
= rFilter
.importEmbeddedGraphicObject( aGraphicPath
);
495 if( aGraphicUrl
.getLength() > 0 )
497 PropertySet
aPropSet( xShape
);
498 aPropSet
.setProperty( PROP_GraphicURL
, aGraphicUrl
);
504 // default: try to create a custom shape
505 return CustomShape::implConvertAndInsert( rxShapes
, rShapeRect
);
508 // ============================================================================
510 GroupShape::GroupShape( const Drawing
& rDrawing
) :
511 ShapeBase( rDrawing
),
512 mxChildren( new ShapeContainer( rDrawing
) )
516 GroupShape::~GroupShape()
520 void GroupShape::finalizeFragmentImport()
522 // basic shape processing
523 ShapeBase::finalizeFragmentImport();
524 // finalize all child shapes
525 mxChildren
->finalizeFragmentImport();
528 const ShapeType
* GroupShape::getChildTypeById( const OUString
& rShapeId
) const
530 return mxChildren
->getShapeTypeById( rShapeId
, true );
533 const ShapeBase
* GroupShape::getChildById( const OUString
& rShapeId
) const
535 return mxChildren
->getShapeById( rShapeId
, true );
538 Reference
< XShape
> GroupShape::implConvertAndInsert( const Reference
< XShapes
>& rxShapes
, const Rectangle
& rShapeRect
) const
540 Reference
< XShape
> xShape
;
541 // check that this shape contains children and a valid coordinate system
542 ShapeParentAnchor aParentAnchor
;
543 aParentAnchor
.maShapeRect
= rShapeRect
;
544 aParentAnchor
.maCoordSys
= getCoordSystem();
545 if( !mxChildren
->empty() && (aParentAnchor
.maCoordSys
.Width
> 0) && (aParentAnchor
.maCoordSys
.Height
> 0) ) try
547 xShape
= lclCreateAndInsertXShape( mrDrawing
.getFilter(), rxShapes
, CREATE_OUSTRING( "com.sun.star.drawing.GroupShape" ), rShapeRect
);
548 Reference
< XShapes
> xShapes( xShape
, UNO_QUERY_THROW
);
549 mxChildren
->convertAndInsert( xShapes
, &aParentAnchor
);
557 // ============================================================================