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