xref: /aoo41x/main/oox/source/vml/vmlshapecontext.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "oox/vml/vmlshapecontext.hxx"
29 
30 #include "oox/vml/vmldrawing.hxx"
31 #include "oox/vml/vmlshape.hxx"
32 #include "oox/vml/vmlshapecontainer.hxx"
33 #include "oox/vml/vmltextboxcontext.hxx"
34 
35 namespace oox {
36 namespace vml {
37 
38 // ============================================================================
39 
40 using namespace ::com::sun::star::awt;
41 
42 using ::oox::core::ContextHandler2;
43 using ::oox::core::ContextHandler2Helper;
44 using ::oox::core::ContextHandlerRef;
45 using ::rtl::OUString;
46 
47 // ============================================================================
48 
49 namespace {
50 
51 /** Returns the boolean value from the specified VML attribute (if present).
52  */
53 OptValue< bool > lclDecodeBool( const AttributeList& rAttribs, sal_Int32 nToken )
54 {
55     OptValue< OUString > oValue = rAttribs.getString( nToken );
56     if( oValue.has() ) return OptValue< bool >( ConversionHelper::decodeBool( oValue.get() ) );
57     return OptValue< bool >();
58 }
59 
60 /** Returns the percentage value from the specified VML attribute (if present).
61     The value will be normalized (1.0 is returned for 100%).
62  */
63 OptValue< double > lclDecodePercent( const AttributeList& rAttribs, sal_Int32 nToken, double fDefValue )
64 {
65     OptValue< OUString > oValue = rAttribs.getString( nToken );
66     if( oValue.has() ) return OptValue< double >( ConversionHelper::decodePercent( oValue.get(), fDefValue ) );
67     return OptValue< double >();
68 }
69 
70 /** Returns the integer value pair from the specified VML attribute (if present).
71  */
72 OptValue< Int32Pair > lclDecodeInt32Pair( const AttributeList& rAttribs, sal_Int32 nToken )
73 {
74     OptValue< OUString > oValue = rAttribs.getString( nToken );
75     OptValue< Int32Pair > oRetValue;
76     if( oValue.has() )
77     {
78         OUString aValue1, aValue2;
79         ConversionHelper::separatePair( aValue1, aValue2, oValue.get(), ',' );
80         oRetValue = Int32Pair( aValue1.toInt32(), aValue2.toInt32() );
81     }
82     return oRetValue;
83 }
84 
85 /** Returns the percentage pair from the specified VML attribute (if present).
86  */
87 OptValue< DoublePair > lclDecodePercentPair( const AttributeList& rAttribs, sal_Int32 nToken )
88 {
89     OptValue< OUString > oValue = rAttribs.getString( nToken );
90     OptValue< DoublePair > oRetValue;
91     if( oValue.has() )
92     {
93         OUString aValue1, aValue2;
94         ConversionHelper::separatePair( aValue1, aValue2, oValue.get(), ',' );
95         oRetValue = DoublePair(
96             ConversionHelper::decodePercent( aValue1, 0.0 ),
97             ConversionHelper::decodePercent( aValue2, 0.0 ) );
98     }
99     return oRetValue;
100 }
101 
102 /** Returns the boolean value from the passed string of an attribute in the x:
103     namespace (VML for spreadsheets). Supported values: f, t, False, True.
104     @param bDefaultForEmpty  Default value for the empty string.
105  */
106 bool lclDecodeVmlxBool( const OUString& rValue, bool bDefaultForEmpty )
107 {
108     if( rValue.getLength() == 0 ) return bDefaultForEmpty;
109     sal_Int32 nToken = AttributeConversion::decodeToken( rValue );
110     // anything else than 't' or 'True' is considered to be false, as specified
111     return (nToken == XML_t) || (nToken == XML_True);
112 }
113 
114 } // namespace
115 
116 // ============================================================================
117 
118 ShapeLayoutContext::ShapeLayoutContext( ContextHandler2Helper& rParent, Drawing& rDrawing ) :
119     ContextHandler2( rParent ),
120     mrDrawing( rDrawing )
121 {
122 }
123 
124 
125 ContextHandlerRef ShapeLayoutContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
126 {
127     switch( nElement )
128     {
129         case O_TOKEN( idmap ):
130         {
131             OUString aBlockIds = rAttribs.getString( XML_data, OUString() );
132             sal_Int32 nIndex = 0;
133             while( nIndex >= 0 )
134             {
135                 OUString aToken = aBlockIds.getToken( 0, ' ', nIndex ).trim();
136                 if( aToken.getLength() > 0 )
137                     mrDrawing.registerBlockId( aToken.toInt32() );
138             }
139         }
140         break;
141     }
142     return 0;
143 }
144 
145 // ============================================================================
146 
147 ClientDataContext::ClientDataContext( ContextHandler2Helper& rParent,
148         ClientData& rClientData, const AttributeList& rAttribs ) :
149     ContextHandler2( rParent ),
150     mrClientData( rClientData )
151 {
152     mrClientData.mnObjType = rAttribs.getToken( XML_ObjectType, XML_TOKEN_INVALID );
153 }
154 
155 ContextHandlerRef ClientDataContext::onCreateContext( sal_Int32 /*nElement*/, const AttributeList& /*rAttribs*/ )
156 {
157     if( isRootElement() )
158     {
159         maElementText = OUString();
160         return this;
161     }
162     return 0;
163 }
164 
165 void ClientDataContext::onCharacters( const OUString& rChars )
166 {
167     /*  Empty but existing elements have special meaning, e.g. 'true'. Collect
168         existing text and convert it in onEndElement(). */
169     maElementText = rChars;
170 }
171 
172 void ClientDataContext::onEndElement()
173 {
174     switch( getCurrentElement() )
175     {
176         case VMLX_TOKEN( Anchor ):      mrClientData.maAnchor = maElementText;                                          break;
177         case VMLX_TOKEN( FmlaMacro ):   mrClientData.maFmlaMacro = maElementText;                                       break;
178         case VMLX_TOKEN( FmlaPict ):    mrClientData.maFmlaPict = maElementText;                                        break;
179         case VMLX_TOKEN( FmlaLink ):    mrClientData.maFmlaLink = maElementText;                                        break;
180         case VMLX_TOKEN( FmlaRange ):   mrClientData.maFmlaRange = maElementText;                                       break;
181         case VMLX_TOKEN( FmlaGroup ):   mrClientData.maFmlaGroup = maElementText;                                       break;
182         case VMLX_TOKEN( TextHAlign ):  mrClientData.mnTextHAlign = AttributeConversion::decodeToken( maElementText );  break;
183         case VMLX_TOKEN( TextVAlign ):  mrClientData.mnTextVAlign = AttributeConversion::decodeToken( maElementText );  break;
184         case VMLX_TOKEN( Column ):      mrClientData.mnCol = maElementText.toInt32();                                   break;
185         case VMLX_TOKEN( Row ):         mrClientData.mnRow = maElementText.toInt32();                                   break;
186         case VMLX_TOKEN( Checked ):     mrClientData.mnChecked = maElementText.toInt32();                               break;
187         case VMLX_TOKEN( DropStyle ):   mrClientData.mnDropStyle = AttributeConversion::decodeToken( maElementText );   break;
188         case VMLX_TOKEN( DropLines ):   mrClientData.mnDropLines = maElementText.toInt32();                             break;
189         case VMLX_TOKEN( Val ):         mrClientData.mnVal = maElementText.toInt32();                                   break;
190         case VMLX_TOKEN( Min ):         mrClientData.mnMin = maElementText.toInt32();                                   break;
191         case VMLX_TOKEN( Max ):         mrClientData.mnMax = maElementText.toInt32();                                   break;
192         case VMLX_TOKEN( Inc ):         mrClientData.mnInc = maElementText.toInt32();                                   break;
193         case VMLX_TOKEN( Page ):        mrClientData.mnPage = maElementText.toInt32();                                  break;
194         case VMLX_TOKEN( SelType ):     mrClientData.mnSelType = AttributeConversion::decodeToken( maElementText );     break;
195         case VMLX_TOKEN( VTEdit ):      mrClientData.mnVTEdit = maElementText.toInt32();                                break;
196         case VMLX_TOKEN( PrintObject ): mrClientData.mbPrintObject = lclDecodeVmlxBool( maElementText, true );          break;
197         case VMLX_TOKEN( Visible ):     mrClientData.mbVisible = lclDecodeVmlxBool( maElementText, true );              break;
198         case VMLX_TOKEN( DDE ):         mrClientData.mbDde = lclDecodeVmlxBool( maElementText, true );                  break;
199         case VMLX_TOKEN( NoThreeD ):    mrClientData.mbNo3D = lclDecodeVmlxBool( maElementText, true );                 break;
200         case VMLX_TOKEN( NoThreeD2 ):   mrClientData.mbNo3D2 = lclDecodeVmlxBool( maElementText, true );                break;
201         case VMLX_TOKEN( MultiLine ):   mrClientData.mbMultiLine = lclDecodeVmlxBool( maElementText, true );            break;
202         case VMLX_TOKEN( VScroll ):     mrClientData.mbVScroll = lclDecodeVmlxBool( maElementText, true );              break;
203         case VMLX_TOKEN( SecretEdit ):  mrClientData.mbSecretEdit = lclDecodeVmlxBool( maElementText, true );           break;
204     }
205 }
206 
207 // ============================================================================
208 
209 ShapeContextBase::ShapeContextBase( ContextHandler2Helper& rParent ) :
210     ContextHandler2( rParent )
211 {
212 }
213 
214 /*static*/ ContextHandlerRef ShapeContextBase::createShapeContext( ContextHandler2Helper& rParent,
215         ShapeContainer& rShapes, sal_Int32 nElement, const AttributeList& rAttribs )
216 {
217     switch( nElement )
218     {
219         case O_TOKEN( shapelayout ):
220             return new ShapeLayoutContext( rParent, rShapes.getDrawing() );
221 
222         case VML_TOKEN( shapetype ):
223             return new ShapeTypeContext( rParent, rShapes.createShapeType(), rAttribs );
224         case VML_TOKEN( group ):
225             return new GroupShapeContext( rParent, rShapes.createShape< GroupShape >(), rAttribs );
226         case VML_TOKEN( shape ):
227             return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs );
228         case VML_TOKEN( rect ):
229         case VML_TOKEN( roundrect ):
230             return new ShapeContext( rParent, rShapes.createShape< RectangleShape >(), rAttribs );
231         case VML_TOKEN( oval ):
232             return new ShapeContext( rParent, rShapes.createShape< EllipseShape >(), rAttribs );
233         case VML_TOKEN( polyline ):
234             return new ShapeContext( rParent, rShapes.createShape< PolyLineShape >(), rAttribs );
235 
236         // TODO:
237         case VML_TOKEN( arc ):
238         case VML_TOKEN( curve ):
239         case VML_TOKEN( line ):
240         case VML_TOKEN( diagram ):
241         case VML_TOKEN( image ):
242             return new ShapeContext( rParent, rShapes.createShape< ComplexShape >(), rAttribs );
243     }
244     return 0;
245 }
246 
247 // ============================================================================
248 
249 ShapeTypeContext::ShapeTypeContext( ContextHandler2Helper& rParent, ShapeType& rShapeType, const AttributeList& rAttribs ) :
250     ShapeContextBase( rParent ),
251     mrTypeModel( rShapeType.getTypeModel() )
252 {
253     // shape identifier and shape name
254     bool bHasOspid = rAttribs.hasAttribute( O_TOKEN( spid ) );
255     mrTypeModel.maShapeId = rAttribs.getXString( bHasOspid ? O_TOKEN( spid ) : XML_id, OUString() );
256     OSL_ENSURE( mrTypeModel.maShapeId.getLength() > 0, "ShapeTypeContext::ShapeTypeContext - missing shape identifier" );
257     // if the o:spid attribute exists, the id attribute contains the user-defined shape name
258     if( bHasOspid )
259         mrTypeModel.maShapeName = rAttribs.getXString( XML_id, OUString() );
260     // builtin shape type identifier
261     mrTypeModel.moShapeType = rAttribs.getInteger( O_TOKEN( spt ) );
262 
263     // coordinate system position/size, CSS style
264     mrTypeModel.moCoordPos = lclDecodeInt32Pair( rAttribs, XML_coordorigin );
265     mrTypeModel.moCoordSize = lclDecodeInt32Pair( rAttribs, XML_coordsize );
266     setStyle( rAttribs.getString( XML_style, OUString() ) );
267 
268     // stroke settings (may be overridden by v:stroke element later)
269     mrTypeModel.maStrokeModel.moStroked = lclDecodeBool( rAttribs, XML_stroked );
270     mrTypeModel.maStrokeModel.moColor = rAttribs.getString( XML_strokecolor );
271     mrTypeModel.maStrokeModel.moWeight = rAttribs.getString( XML_strokeweight );
272 
273     // fill settings (may be overridden by v:fill element later)
274     mrTypeModel.maFillModel.moFilled = lclDecodeBool( rAttribs, XML_filled );
275     mrTypeModel.maFillModel.moColor = rAttribs.getString( XML_fillcolor );
276 }
277 
278 ContextHandlerRef ShapeTypeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
279 {
280     if( isRootElement() ) switch( nElement )
281     {
282         case VML_TOKEN( stroke ):
283             mrTypeModel.maStrokeModel.moStroked.assignIfUsed( lclDecodeBool( rAttribs, XML_on ) );
284             mrTypeModel.maStrokeModel.maStartArrow.moArrowType = rAttribs.getToken( XML_startarrow );
285             mrTypeModel.maStrokeModel.maStartArrow.moArrowWidth = rAttribs.getToken( XML_startarrowwidth );
286             mrTypeModel.maStrokeModel.maStartArrow.moArrowLength = rAttribs.getToken( XML_startarrowlength );
287             mrTypeModel.maStrokeModel.maEndArrow.moArrowType = rAttribs.getToken( XML_endarrow );
288             mrTypeModel.maStrokeModel.maEndArrow.moArrowWidth = rAttribs.getToken( XML_endarrowwidth );
289             mrTypeModel.maStrokeModel.maEndArrow.moArrowLength = rAttribs.getToken( XML_endarrowlength );
290             mrTypeModel.maStrokeModel.moColor.assignIfUsed( rAttribs.getString( XML_color ) );
291             mrTypeModel.maStrokeModel.moOpacity = lclDecodePercent( rAttribs, XML_opacity, 1.0 );
292             mrTypeModel.maStrokeModel.moWeight.assignIfUsed( rAttribs.getString( XML_weight ) );
293             mrTypeModel.maStrokeModel.moDashStyle = rAttribs.getString( XML_dashstyle );
294             mrTypeModel.maStrokeModel.moLineStyle = rAttribs.getToken( XML_linestyle );
295             mrTypeModel.maStrokeModel.moEndCap = rAttribs.getToken( XML_endcap );
296             mrTypeModel.maStrokeModel.moJoinStyle = rAttribs.getToken( XML_joinstyle );
297         break;
298         case VML_TOKEN( fill ):
299             mrTypeModel.maFillModel.moFilled.assignIfUsed( lclDecodeBool( rAttribs, XML_on ) );
300             mrTypeModel.maFillModel.moColor.assignIfUsed( rAttribs.getString( XML_color ) );
301             mrTypeModel.maFillModel.moOpacity = lclDecodePercent( rAttribs, XML_opacity, 1.0 );
302             mrTypeModel.maFillModel.moColor2 = rAttribs.getString( XML_color2 );
303             mrTypeModel.maFillModel.moOpacity2 = lclDecodePercent( rAttribs, XML_opacity2, 1.0 );
304             mrTypeModel.maFillModel.moType = rAttribs.getToken( XML_type );
305             mrTypeModel.maFillModel.moAngle = rAttribs.getInteger( XML_angle );
306             mrTypeModel.maFillModel.moFocus = lclDecodePercent( rAttribs, XML_focus, 0.0 );
307             mrTypeModel.maFillModel.moFocusPos = lclDecodePercentPair( rAttribs, XML_focusposition );
308             mrTypeModel.maFillModel.moFocusSize = lclDecodePercentPair( rAttribs, XML_focussize );
309             mrTypeModel.maFillModel.moBitmapPath = decodeFragmentPath( rAttribs, O_TOKEN( relid ) );
310             mrTypeModel.maFillModel.moRotate = lclDecodeBool( rAttribs, XML_rotate );
311         break;
312         case VML_TOKEN( imagedata ):
313             mrTypeModel.moGraphicPath = decodeFragmentPath( rAttribs, O_TOKEN( relid ) );
314             mrTypeModel.moGraphicTitle = rAttribs.getString( O_TOKEN( title ) );
315         break;
316     }
317     return 0;
318 }
319 
320 OptValue< OUString > ShapeTypeContext::decodeFragmentPath( const AttributeList& rAttribs, sal_Int32 nToken ) const
321 {
322     OptValue< OUString > oFragmentPath;
323     OptValue< OUString > oRelId = rAttribs.getString( nToken );
324     if( oRelId.has() )
325         oFragmentPath = getFragmentPathFromRelId( oRelId.get() );
326     return oFragmentPath;
327 }
328 
329 void ShapeTypeContext::setStyle( const OUString& rStyle )
330 {
331     sal_Int32 nIndex = 0;
332     while( nIndex >= 0 )
333     {
334         OUString aName, aValue;
335         if( ConversionHelper::separatePair( aName, aValue, rStyle.getToken( 0, ';', nIndex ), ':' ) )
336         {
337                  if( aName.equalsAscii( "position" ) )      mrTypeModel.maPosition = aValue;
338             else if( aName.equalsAscii( "left" ) )          mrTypeModel.maLeft = aValue;
339             else if( aName.equalsAscii( "top" ) )           mrTypeModel.maTop = aValue;
340             else if( aName.equalsAscii( "width" ) )         mrTypeModel.maWidth = aValue;
341             else if( aName.equalsAscii( "height" ) )        mrTypeModel.maHeight = aValue;
342             else if( aName.equalsAscii( "margin-left" ) )   mrTypeModel.maMarginLeft = aValue;
343             else if( aName.equalsAscii( "margin-top" ) )    mrTypeModel.maMarginTop = aValue;
344         }
345     }
346 }
347 
348 // ============================================================================
349 
350 ShapeContext::ShapeContext( ContextHandler2Helper& rParent, ShapeBase& rShape, const AttributeList& rAttribs ) :
351     ShapeTypeContext( rParent, rShape, rAttribs ),
352     mrShapeModel( rShape.getShapeModel() )
353 {
354     // collect shape specific attributes
355     mrShapeModel.maType = rAttribs.getXString( XML_type, OUString() );
356     // polyline path
357     setPoints( rAttribs.getString( XML_points, OUString() ) );
358 }
359 
360 ContextHandlerRef ShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
361 {
362     // Excel specific shape client data
363     if( isRootElement() ) switch( nElement )
364     {
365         case VML_TOKEN( textbox ):
366             return new TextBoxContext( *this, mrShapeModel.createTextBox(), rAttribs );
367         case VMLX_TOKEN( ClientData ):
368             return new ClientDataContext( *this, mrShapeModel.createClientData(), rAttribs );
369     }
370     // handle remaining stuff in base class
371     return ShapeTypeContext::onCreateContext( nElement, rAttribs );
372 }
373 
374 void ShapeContext::setPoints( const OUString& rPoints )
375 {
376     mrShapeModel.maPoints.clear();
377     sal_Int32 nIndex = 0;
378     while( nIndex >= 0 )
379     {
380         sal_Int32 nX = rPoints.getToken( 0, ',', nIndex ).toInt32();
381         sal_Int32 nY = rPoints.getToken( 0, ',', nIndex ).toInt32();
382         mrShapeModel.maPoints.push_back( Point( nX, nY ) );
383     }
384 }
385 
386 // ============================================================================
387 
388 GroupShapeContext::GroupShapeContext( ContextHandler2Helper& rParent, GroupShape& rShape, const AttributeList& rAttribs ) :
389     ShapeContext( rParent, rShape, rAttribs ),
390     mrShapes( rShape.getChildren() )
391 {
392 }
393 
394 ContextHandlerRef GroupShapeContext::onCreateContext( sal_Int32 nElement, const AttributeList& rAttribs )
395 {
396     // try to create a context of an embedded shape
397     ContextHandlerRef xContext = createShapeContext( *this, mrShapes, nElement, rAttribs );
398     // handle remaining stuff of this shape in base class
399     return xContext.get() ? xContext : ShapeContext::onCreateContext( nElement, rAttribs );
400 }
401 
402 // ============================================================================
403 
404 } // namespace vml
405 } // namespace oox
406 
407