1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <oox/vml/vmlshapecontext.hxx>
22 #include <oox/core/xmlfilterbase.hxx>
23 #include <oox/helper/attributelist.hxx>
24 #include <oox/helper/helper.hxx>
25 #include <oox/token/namespaces.hxx>
26 #include <oox/token/tokens.hxx>
27 #include <oox/vml/vmldrawing.hxx>
28 #include <oox/vml/vmlshape.hxx>
29 #include <oox/vml/vmlshapecontainer.hxx>
30 #include <oox/vml/vmltextboxcontext.hxx>
32 #include <osl/diagnose.h>
33 #include <filter/msfilter/escherex.hxx>
37 using namespace ::com::sun::star
;
39 using ::oox::core::ContextHandler2
;
40 using ::oox::core::ContextHandler2Helper
;
41 using ::oox::core::ContextHandlerRef
;
45 /** Returns the boolean value from the specified VML attribute (if present).
47 OptValue
< bool > lclDecodeBool( const AttributeList
& rAttribs
, sal_Int32 nToken
)
49 OptValue
< OUString
> oValue
= rAttribs
.getString( nToken
);
50 if( oValue
.has() ) return OptValue
< bool >( ConversionHelper::decodeBool( oValue
.get() ) );
51 return OptValue
< bool >();
54 /** Returns the percentage value from the specified VML attribute (if present).
55 The value will be normalized (1.0 is returned for 100%).
57 OptValue
< double > lclDecodePercent( const AttributeList
& rAttribs
, sal_Int32 nToken
, double fDefValue
)
59 OptValue
< OUString
> oValue
= rAttribs
.getString( nToken
);
60 if( oValue
.has() ) return OptValue
< double >( ConversionHelper::decodePercent( oValue
.get(), fDefValue
) );
61 return OptValue
< double >();
64 /** #119750# Special method for opacity; it *should* be a percentage value, but there are cases
65 where a value relative to 0xffff (65536) is used, ending with an 'f'
67 OptValue
< double > lclDecodeOpacity( const AttributeList
& rAttribs
, sal_Int32 nToken
, double fDefValue
)
69 OptValue
< OUString
> oValue
= rAttribs
.getString( nToken
);
70 double fRetval(fDefValue
);
74 const OUString
& aString(oValue
.get());
75 const sal_Int32
nLength(aString
.getLength());
79 if(aString
.endsWith("f"))
81 fRetval
= std::clamp(aString
.toDouble() / 65536.0, 0.0, 1.0);
85 fRetval
= ConversionHelper::decodePercent( aString
, fDefValue
);
90 return OptValue
< double >(fRetval
);
93 /** Returns the integer value pair from the specified VML attribute (if present).
95 OptValue
< Int32Pair
> lclDecodeInt32Pair( const AttributeList
& rAttribs
, sal_Int32 nToken
)
97 OptValue
< OUString
> oValue
= rAttribs
.getString( nToken
);
98 OptValue
< Int32Pair
> oRetValue
;
101 OUString aValue1
, aValue2
;
102 ConversionHelper::separatePair( aValue1
, aValue2
, oValue
.get(), ',' );
103 oRetValue
= Int32Pair( aValue1
.toInt32(), aValue2
.toInt32() );
108 /** Returns the percentage pair from the specified VML attribute (if present).
110 OptValue
< DoublePair
> lclDecodePercentPair( const AttributeList
& rAttribs
, sal_Int32 nToken
)
112 OptValue
< OUString
> oValue
= rAttribs
.getString( nToken
);
113 OptValue
< DoublePair
> oRetValue
;
116 OUString aValue1
, aValue2
;
117 ConversionHelper::separatePair( aValue1
, aValue2
, oValue
.get(), ',' );
118 oRetValue
= DoublePair(
119 ConversionHelper::decodePercent( aValue1
, 0.0 ),
120 ConversionHelper::decodePercent( aValue2
, 0.0 ) );
125 /** Returns the boolean value from the passed string of an attribute in the x:
126 namespace (VML for spreadsheets). Supported values: f, t, False, True.
127 @param bDefaultForEmpty Default value for the empty string.
129 bool lclDecodeVmlxBool( const OUString
& rValue
, bool bDefaultForEmpty
)
131 if( rValue
.isEmpty() ) return bDefaultForEmpty
;
132 sal_Int32 nToken
= AttributeConversion::decodeToken( rValue
);
133 // anything else than 't' or 'True' is considered to be false, as specified
134 return (nToken
== XML_t
) || (nToken
== XML_True
);
139 ShapeLayoutContext::ShapeLayoutContext( ContextHandler2Helper
const & rParent
, Drawing
& rDrawing
) :
140 ContextHandler2( rParent
),
141 mrDrawing( rDrawing
)
145 ContextHandlerRef
ShapeLayoutContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
149 case O_TOKEN( idmap
):
151 OUString aBlockIds
= rAttribs
.getString( XML_data
, OUString() );
152 sal_Int32 nIndex
= 0;
155 OUString aToken
= aBlockIds
.getToken( 0, ' ', nIndex
).trim();
156 if( !aToken
.isEmpty() )
157 mrDrawing
.registerBlockId( aToken
.toInt32() );
165 ClientDataContext::ClientDataContext( ContextHandler2Helper
const & rParent
,
166 ClientData
& rClientData
, const AttributeList
& rAttribs
) :
167 ContextHandler2( rParent
),
168 mrClientData( rClientData
)
170 mrClientData
.mnObjType
= rAttribs
.getToken( XML_ObjectType
, XML_TOKEN_INVALID
);
173 ContextHandlerRef
ClientDataContext::onCreateContext( sal_Int32
/*nElement*/, const AttributeList
& /*rAttribs*/ )
175 if( isRootElement() )
177 maElementText
.clear();
183 void ClientDataContext::onCharacters( const OUString
& rChars
)
185 /* Empty but existing elements have special meaning, e.g. 'true'. Collect
186 existing text and convert it in onEndElement(). */
187 maElementText
= rChars
;
190 void ClientDataContext::onEndElement()
192 switch( getCurrentElement() )
194 case VMLX_TOKEN( Anchor
): mrClientData
.maAnchor
= maElementText
; break;
195 case VMLX_TOKEN( FmlaMacro
): mrClientData
.maFmlaMacro
= maElementText
; break;
196 case VMLX_TOKEN( FmlaPict
): mrClientData
.maFmlaPict
= maElementText
; break;
197 case VMLX_TOKEN( FmlaLink
): mrClientData
.maFmlaLink
= maElementText
; break;
198 case VMLX_TOKEN( FmlaRange
): mrClientData
.maFmlaRange
= maElementText
; break;
199 case VMLX_TOKEN( FmlaGroup
): mrClientData
.maFmlaGroup
= maElementText
; break;
200 case VMLX_TOKEN( TextHAlign
): mrClientData
.mnTextHAlign
= AttributeConversion::decodeToken( maElementText
); break;
201 case VMLX_TOKEN( TextVAlign
): mrClientData
.mnTextVAlign
= AttributeConversion::decodeToken( maElementText
); break;
202 case VMLX_TOKEN( Column
): mrClientData
.mnCol
= maElementText
.toInt32(); break;
203 case VMLX_TOKEN( Row
): mrClientData
.mnRow
= maElementText
.toInt32(); break;
204 case VMLX_TOKEN( Checked
): mrClientData
.mnChecked
= maElementText
.toInt32(); break;
205 case VMLX_TOKEN( DropStyle
): mrClientData
.mnDropStyle
= AttributeConversion::decodeToken( maElementText
); break;
206 case VMLX_TOKEN( DropLines
): mrClientData
.mnDropLines
= maElementText
.toInt32(); break;
207 case VMLX_TOKEN( Val
): mrClientData
.mnVal
= maElementText
.toInt32(); break;
208 case VMLX_TOKEN( Min
): mrClientData
.mnMin
= maElementText
.toInt32(); break;
209 case VMLX_TOKEN( Max
): mrClientData
.mnMax
= maElementText
.toInt32(); break;
210 case VMLX_TOKEN( Inc
): mrClientData
.mnInc
= maElementText
.toInt32(); break;
211 case VMLX_TOKEN( Page
): mrClientData
.mnPage
= maElementText
.toInt32(); break;
212 case VMLX_TOKEN( SelType
): mrClientData
.mnSelType
= AttributeConversion::decodeToken( maElementText
); break;
213 case VMLX_TOKEN( VTEdit
): mrClientData
.mnVTEdit
= maElementText
.toInt32(); break;
214 case VMLX_TOKEN( PrintObject
): mrClientData
.mbPrintObject
= lclDecodeVmlxBool( maElementText
, true ); break;
215 case VMLX_TOKEN( Visible
): mrClientData
.mbVisible
= lclDecodeVmlxBool( maElementText
, true ); break;
216 case VMLX_TOKEN( DDE
): mrClientData
.mbDde
= lclDecodeVmlxBool( maElementText
, true ); break;
217 case VMLX_TOKEN( NoThreeD
): mrClientData
.mbNo3D
= lclDecodeVmlxBool( maElementText
, true ); break;
218 case VMLX_TOKEN( NoThreeD2
): mrClientData
.mbNo3D2
= lclDecodeVmlxBool( maElementText
, true ); break;
219 case VMLX_TOKEN( MultiLine
): mrClientData
.mbMultiLine
= lclDecodeVmlxBool( maElementText
, true ); break;
220 case VMLX_TOKEN( VScroll
): mrClientData
.mbVScroll
= lclDecodeVmlxBool( maElementText
, true ); break;
221 case VMLX_TOKEN( SecretEdit
): mrClientData
.mbSecretEdit
= lclDecodeVmlxBool( maElementText
, true ); break;
225 ShapeContextBase::ShapeContextBase( ContextHandler2Helper
const & rParent
) :
226 ContextHandler2( rParent
)
230 ContextHandlerRef
ShapeContextBase::createShapeContext( ContextHandler2Helper
const & rParent
,
231 ShapeContainer
& rShapes
, sal_Int32 nElement
, const AttributeList
& rAttribs
)
235 case O_TOKEN( shapelayout
):
236 return new ShapeLayoutContext( rParent
, rShapes
.getDrawing() );
238 case VML_TOKEN( shapetype
):
239 return new ShapeTypeContext( rParent
, rShapes
.createShapeType(), rAttribs
);
240 case VML_TOKEN( group
):
241 return new GroupShapeContext( rParent
, rShapes
.createShape
< GroupShape
>(), rAttribs
);
242 case VML_TOKEN( shape
):
243 if (rAttribs
.hasAttribute(XML_path
) &&
244 // tdf#122563 skip in the case of empty path
245 !rAttribs
.getString(XML_path
, "").isEmpty())
246 return new ShapeContext( rParent
, rShapes
.createShape
< BezierShape
>(), rAttribs
);
248 return new ShapeContext( rParent
, rShapes
.createShape
< ComplexShape
>(), rAttribs
);
249 case VML_TOKEN( rect
):
250 return new RectangleShapeContext( rParent
, rAttribs
, rShapes
.createShape
< RectangleShape
>() );
251 case VML_TOKEN( roundrect
):
252 return new ShapeContext( rParent
, rShapes
.createShape
< RectangleShape
>(), rAttribs
);
253 case VML_TOKEN( oval
):
254 return new ShapeContext( rParent
, rShapes
.createShape
< EllipseShape
>(), rAttribs
);
255 case VML_TOKEN( polyline
):
256 return new ShapeContext( rParent
, rShapes
.createShape
< PolyLineShape
>(), rAttribs
);
257 case VML_TOKEN( line
):
258 return new ShapeContext( rParent
, rShapes
.createShape
< LineShape
>(), rAttribs
);
259 case VML_TOKEN( curve
):
260 return new ShapeContext( rParent
, rShapes
.createShape
< BezierShape
>(), rAttribs
);
263 case VML_TOKEN( arc
):
264 case VML_TOKEN( diagram
):
265 case VML_TOKEN( image
):
266 return new ShapeContext( rParent
, rShapes
.createShape
< ComplexShape
>(), rAttribs
);
268 case W_TOKEN(control
):
269 return new ControlShapeContext( rParent
, rShapes
, rAttribs
);
274 ShapeTypeContext::ShapeTypeContext(ContextHandler2Helper
const & rParent
,
275 std::shared_ptr
<ShapeType
> const& pShapeType
,
276 const AttributeList
& rAttribs
)
277 : ShapeContextBase(rParent
)
278 , m_pShapeType(pShapeType
) // tdf#112311 keep it alive
279 , mrTypeModel( pShapeType
->getTypeModel() )
281 // shape identifier and shape name
282 bool bHasOspid
= rAttribs
.hasAttribute( O_TOKEN( spid
) );
283 mrTypeModel
.maShapeId
= rAttribs
.getXString( bHasOspid
? O_TOKEN( spid
) : XML_id
, OUString() );
284 mrTypeModel
.maLegacyId
= rAttribs
.getString( XML_id
, OUString() );
285 OSL_ENSURE( !mrTypeModel
.maShapeId
.isEmpty(), "ShapeTypeContext::ShapeTypeContext - missing shape identifier" );
286 // builtin shape type identifier
287 mrTypeModel
.moShapeType
= rAttribs
.getInteger( O_TOKEN( spt
) );
288 // if the o:spid attribute exists, the id attribute contains the user-defined shape name
291 mrTypeModel
.maShapeName
= rAttribs
.getXString( XML_id
, OUString() );
292 // get ShapeType and ShapeId from name for compatibility
293 static constexpr OUStringLiteral sShapeTypePrefix
= u
"shapetype_";
294 if( mrTypeModel
.maShapeName
.startsWith( sShapeTypePrefix
) )
296 mrTypeModel
.maShapeId
= mrTypeModel
.maShapeName
;
297 mrTypeModel
.moShapeType
= mrTypeModel
.maShapeName
.copy(sShapeTypePrefix
.getLength()).toInt32();
301 // coordinate system position/size, CSS style
302 mrTypeModel
.moCoordPos
= lclDecodeInt32Pair( rAttribs
, XML_coordorigin
);
303 mrTypeModel
.moCoordSize
= lclDecodeInt32Pair( rAttribs
, XML_coordsize
);
304 setStyle( rAttribs
.getString( XML_style
, OUString() ) );
305 if( lclDecodeBool( rAttribs
, O_TOKEN( hr
)).get( false ))
306 { // MSO's handling of o:hr width is nowhere near what the spec says:
307 // - o:hrpct is not in % but in 0.1%
308 // - if o:hrpct is not given, 100% width is assumed
309 // - given width is used only if explicit o:hrpct="0" is given
310 OUString hrpct
= rAttribs
.getString( O_TOKEN( hrpct
), "1000" );
312 mrTypeModel
.maWidthPercent
= OUString::number( hrpct
.toInt32() );
313 mrTypeModel
.maWrapDistanceLeft
= "0";
314 mrTypeModel
.maWrapDistanceRight
= "0";
315 mrTypeModel
.maPositionHorizontal
= rAttribs
.getString( O_TOKEN( hralign
), "left" );
316 mrTypeModel
.moWrapType
= "topAndBottom";
319 // stroke settings (may be overridden by v:stroke element later)
320 mrTypeModel
.maStrokeModel
.moStroked
= lclDecodeBool( rAttribs
, XML_stroked
);
321 mrTypeModel
.maStrokeModel
.moColor
= rAttribs
.getString( XML_strokecolor
);
322 mrTypeModel
.maStrokeModel
.moWeight
= rAttribs
.getString( XML_strokeweight
);
324 // fill settings (may be overridden by v:fill element later)
325 mrTypeModel
.maFillModel
.moFilled
= lclDecodeBool( rAttribs
, XML_filled
);
326 mrTypeModel
.maFillModel
.moColor
= rAttribs
.getString( XML_fillcolor
);
328 // For roundrect we may have an arcsize attribute to read
329 mrTypeModel
.maArcsize
= rAttribs
.getString(XML_arcsize
, OUString());
331 mrTypeModel
.maEditAs
= rAttribs
.getString(XML_editas
, OUString());
333 mrTypeModel
.maAdjustments
= rAttribs
.getString(XML_adj
, OUString());
336 ContextHandlerRef
ShapeTypeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
338 if( isRootElement() ) switch( nElement
)
340 case VML_TOKEN( stroke
):
341 mrTypeModel
.maStrokeModel
.moStroked
.assignIfUsed( lclDecodeBool( rAttribs
, XML_on
) );
342 mrTypeModel
.maStrokeModel
.maStartArrow
.moArrowType
= rAttribs
.getToken( XML_startarrow
);
343 mrTypeModel
.maStrokeModel
.maStartArrow
.moArrowWidth
= rAttribs
.getToken( XML_startarrowwidth
);
344 mrTypeModel
.maStrokeModel
.maStartArrow
.moArrowLength
= rAttribs
.getToken( XML_startarrowlength
);
345 mrTypeModel
.maStrokeModel
.maEndArrow
.moArrowType
= rAttribs
.getToken( XML_endarrow
);
346 mrTypeModel
.maStrokeModel
.maEndArrow
.moArrowWidth
= rAttribs
.getToken( XML_endarrowwidth
);
347 mrTypeModel
.maStrokeModel
.maEndArrow
.moArrowLength
= rAttribs
.getToken( XML_endarrowlength
);
348 mrTypeModel
.maStrokeModel
.moColor
.assignIfUsed( rAttribs
.getString( XML_color
) );
349 mrTypeModel
.maStrokeModel
.moOpacity
= lclDecodeOpacity( rAttribs
, XML_opacity
, 1.0 );
350 mrTypeModel
.maStrokeModel
.moWeight
.assignIfUsed( rAttribs
.getString( XML_weight
) );
351 mrTypeModel
.maStrokeModel
.moDashStyle
= rAttribs
.getString( XML_dashstyle
);
352 mrTypeModel
.maStrokeModel
.moLineStyle
= rAttribs
.getToken( XML_linestyle
);
353 mrTypeModel
.maStrokeModel
.moEndCap
= rAttribs
.getToken( XML_endcap
);
354 mrTypeModel
.maStrokeModel
.moJoinStyle
= rAttribs
.getToken( XML_joinstyle
);
356 case VML_TOKEN( fill
):
358 // in DOCX shapes use r:id for the relationship id
359 // in XLSX they use o:relid
360 bool bHasORelId
= rAttribs
.hasAttribute( O_TOKEN(relid
) );
361 mrTypeModel
.maFillModel
.moFilled
.assignIfUsed( lclDecodeBool( rAttribs
, XML_on
) );
362 mrTypeModel
.maFillModel
.moColor
.assignIfUsed( rAttribs
.getString( XML_color
) );
363 mrTypeModel
.maFillModel
.moOpacity
= lclDecodeOpacity( rAttribs
, XML_opacity
, 1.0 );
364 mrTypeModel
.maFillModel
.moColor2
= rAttribs
.getString( XML_color2
);
365 mrTypeModel
.maFillModel
.moOpacity2
= lclDecodeOpacity( rAttribs
, XML_opacity2
, 1.0 );
366 mrTypeModel
.maFillModel
.moType
= rAttribs
.getToken( XML_type
);
367 mrTypeModel
.maFillModel
.moAngle
= rAttribs
.getInteger( XML_angle
);
368 mrTypeModel
.maFillModel
.moFocus
= lclDecodePercent( rAttribs
, XML_focus
, 0.0 );
369 mrTypeModel
.maFillModel
.moFocusPos
= lclDecodePercentPair( rAttribs
, XML_focusposition
);
370 mrTypeModel
.maFillModel
.moFocusSize
= lclDecodePercentPair( rAttribs
, XML_focussize
);
371 mrTypeModel
.maFillModel
.moBitmapPath
= decodeFragmentPath( rAttribs
, bHasORelId
? O_TOKEN(relid
) : R_TOKEN(id
) );
372 mrTypeModel
.maFillModel
.moRotate
= lclDecodeBool( rAttribs
, XML_rotate
);
375 case VML_TOKEN( imagedata
):
377 // shapes in docx use r:id for the relationship id
378 // in xlsx it they use o:relid
379 bool bHasORelId
= rAttribs
.hasAttribute( O_TOKEN( relid
) );
380 mrTypeModel
.moGraphicPath
= decodeFragmentPath( rAttribs
, bHasORelId
? O_TOKEN( relid
) : R_TOKEN( id
) );
381 mrTypeModel
.moGraphicTitle
= rAttribs
.getString( O_TOKEN( title
) );
383 // Get crop attributes.
384 mrTypeModel
.moCropBottom
= rAttribs
.getString(XML_cropbottom
);
385 mrTypeModel
.moCropLeft
= rAttribs
.getString(XML_cropleft
);
386 mrTypeModel
.moCropRight
= rAttribs
.getString(XML_cropright
);
387 mrTypeModel
.moCropTop
= rAttribs
.getString(XML_croptop
);
390 case NMSP_vmlWord
| XML_wrap
:
391 mrTypeModel
.moWrapAnchorX
= rAttribs
.getString(XML_anchorx
);
392 mrTypeModel
.moWrapAnchorY
= rAttribs
.getString(XML_anchory
);
393 mrTypeModel
.moWrapType
= rAttribs
.getString(XML_type
);
394 mrTypeModel
.moWrapSide
= rAttribs
.getString(XML_side
);
396 case VML_TOKEN( shadow
):
398 mrTypeModel
.maShadowModel
.mbHasShadow
= true;
399 mrTypeModel
.maShadowModel
.moShadowOn
= lclDecodeBool(rAttribs
, XML_on
).get(false);
400 mrTypeModel
.maShadowModel
.moColor
.assignIfUsed(rAttribs
.getString(XML_color
));
401 mrTypeModel
.maShadowModel
.moOffset
.assignIfUsed(rAttribs
.getString(XML_offset
));
402 mrTypeModel
.maShadowModel
.moOpacity
= lclDecodePercent(rAttribs
, XML_opacity
, 1.0);
405 case VML_TOKEN( textpath
):
406 mrTypeModel
.maTextpathModel
.moString
.assignIfUsed(rAttribs
.getString(XML_string
));
407 mrTypeModel
.maTextpathModel
.moStyle
.assignIfUsed(rAttribs
.getString(XML_style
));
408 mrTypeModel
.maTextpathModel
.moTrim
.assignIfUsed(lclDecodeBool(rAttribs
, XML_trim
));
414 OptValue
< OUString
> ShapeTypeContext::decodeFragmentPath( const AttributeList
& rAttribs
, sal_Int32 nToken
) const
416 OptValue
< OUString
> oFragmentPath
;
417 OptValue
< OUString
> oRelId
= rAttribs
.getString( nToken
);
419 oFragmentPath
= getFragmentPathFromRelId( oRelId
.get() );
420 return oFragmentPath
;
423 void ShapeTypeContext::setStyle( const OUString
& rStyle
)
425 sal_Int32 nIndex
= 0;
428 OUString aName
, aValue
;
429 if( ConversionHelper::separatePair( aName
, aValue
, rStyle
.getToken( 0, ';', nIndex
), ':' ) )
431 if( aName
== "position" ) mrTypeModel
.maPosition
= aValue
;
432 else if( aName
== "z-index" ) mrTypeModel
.maZIndex
= aValue
;
433 else if( aName
== "left" ) mrTypeModel
.maLeft
= aValue
;
434 else if( aName
== "top" ) mrTypeModel
.maTop
= aValue
;
435 else if( aName
== "width" ) mrTypeModel
.maWidth
= aValue
;
436 else if( aName
== "height" ) mrTypeModel
.maHeight
= aValue
;
437 else if( aName
== "margin-left" ) mrTypeModel
.maMarginLeft
= aValue
;
438 else if( aName
== "margin-top" ) mrTypeModel
.maMarginTop
= aValue
;
439 else if( aName
== "mso-position-vertical-relative" ) mrTypeModel
.maPositionVerticalRelative
= aValue
;
440 else if( aName
== "mso-position-horizontal-relative" ) mrTypeModel
.maPositionHorizontalRelative
= aValue
;
441 else if( aName
== "mso-position-horizontal" ) mrTypeModel
.maPositionHorizontal
= aValue
;
442 else if( aName
== "mso-position-vertical" ) mrTypeModel
.maPositionVertical
= aValue
;
443 else if( aName
== "mso-width-percent" ) mrTypeModel
.maWidthPercent
= aValue
;
444 else if( aName
== "mso-width-relative" ) mrTypeModel
.maWidthRelative
= aValue
;
445 else if( aName
== "mso-height-percent" ) mrTypeModel
.maHeightPercent
= aValue
;
446 else if( aName
== "mso-height-relative" ) mrTypeModel
.maHeightRelative
= aValue
;
447 else if( aName
== "mso-fit-shape-to-text" ) mrTypeModel
.mbAutoHeight
= true;
448 else if( aName
== "rotation" ) mrTypeModel
.maRotation
= aValue
;
449 else if( aName
== "flip" ) mrTypeModel
.maFlip
= aValue
;
450 else if( aName
== "visibility" )
451 mrTypeModel
.mbVisible
= aValue
!= "hidden";
452 else if( aName
== "mso-wrap-style" ) mrTypeModel
.maWrapStyle
= aValue
;
453 else if ( aName
== "v-text-anchor" ) mrTypeModel
.maVTextAnchor
= aValue
;
454 else if ( aName
== "mso-wrap-distance-left" ) mrTypeModel
.maWrapDistanceLeft
= aValue
;
455 else if ( aName
== "mso-wrap-distance-right" ) mrTypeModel
.maWrapDistanceRight
= aValue
;
456 else if ( aName
== "mso-wrap-distance-top" ) mrTypeModel
.maWrapDistanceTop
= aValue
;
457 else if ( aName
== "mso-wrap-distance-bottom" ) mrTypeModel
.maWrapDistanceBottom
= aValue
;
462 ShapeContext::ShapeContext(ContextHandler2Helper
const& rParent
,
463 const std::shared_ptr
<ShapeBase
>& pShape
, const AttributeList
& rAttribs
)
464 : ShapeTypeContext(rParent
, pShape
, rAttribs
)
466 , mrShapeModel(pShape
->getShapeModel())
468 // collect shape specific attributes
469 mrShapeModel
.maType
= rAttribs
.getXString( XML_type
, OUString() );
471 setPoints( rAttribs
.getString( XML_points
, OUString() ) );
472 // line start and end positions
473 setFrom(rAttribs
.getString(XML_from
, OUString()));
474 setTo(rAttribs
.getString(XML_to
, OUString()));
475 setControl1(rAttribs
.getString(XML_control1
, OUString()));
476 setControl2(rAttribs
.getString(XML_control2
, OUString()));
477 setVmlPath(rAttribs
.getString(XML_path
, OUString()));
480 ContextHandlerRef
ShapeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
482 // Excel specific shape client data
483 if( isRootElement() ) switch( nElement
)
485 case VML_TOKEN( textbox
):
487 // Calculate the shape type: map both <rect> and <v:shape> with a textbox shape type to
489 sal_Int32 nShapeType
= 0;
490 if (ShapeContainer
* pShapeContainer
= mrShape
.getContainer())
492 OUString aType
= mrShapeModel
.maType
;
493 if (!aType
.isEmpty() && aType
[0] == '#')
495 aType
= aType
.copy(1);
497 if (const ShapeType
* pShapeType
= pShapeContainer
->getShapeTypeById(aType
))
499 nShapeType
= pShapeType
->getTypeModel().moShapeType
.get();
502 mrShapeModel
.mbInGroup
= (getParentElement() == VML_TOKEN(group
));
504 // FIXME: the shape with textbox should be used for the next cases
505 if (getCurrentElement() == VML_TOKEN(rect
) || nShapeType
== ESCHER_ShpInst_TextBox
)
507 if (mrShapeModel
.mbInGroup
)
508 // FIXME: without this a text will be added into the group-shape instead of its
510 dynamic_cast<SimpleShape
&>(mrShape
).setService("com.sun.star.drawing.TextShape");
512 // FIXME: without this we does not handle some properties like shadow
513 dynamic_cast<SimpleShape
&>(mrShape
).setService("com.sun.star.text.TextFrame");
515 return new TextBoxContext( *this, mrShapeModel
.createTextBox(mrShape
.getTypeModel()), rAttribs
,
516 mrShape
.getDrawing().getFilter().getGraphicHelper());
518 case VMLX_TOKEN( ClientData
):
519 // tdf#41466 ActiveX control shapes with a textbox are transformed into a frame
520 // (see unit test testActiveXOptionButtonGroup)
521 dynamic_cast<SimpleShape
&>(mrShape
).setService("com.sun.star.text.TextFrame");
522 return new ClientDataContext( *this, mrShapeModel
.createClientData(), rAttribs
);
523 case VMLPPT_TOKEN( textdata
):
524 // Force RectangleShape, this is ugly :(
525 // and is there because of the lines above which change it to TextFrame
526 dynamic_cast< SimpleShape
& >( mrShape
).setService(
527 "com.sun.star.drawing.RectangleShape");
528 mrShapeModel
.maLegacyDiagramPath
= getFragmentPathFromRelId(rAttribs
.getString(XML_id
, OUString()));
530 case O_TOKEN( signatureline
):
531 mrShapeModel
.mbIsSignatureLine
= true;
532 mrShapeModel
.maSignatureId
= rAttribs
.getString(XML_id
, OUString());
533 mrShapeModel
.maSignatureLineSuggestedSignerName
534 = rAttribs
.getString(O_TOKEN(suggestedsigner
), OUString());
535 mrShapeModel
.maSignatureLineSuggestedSignerTitle
536 = rAttribs
.getString(O_TOKEN(suggestedsigner2
), OUString());
537 mrShapeModel
.maSignatureLineSuggestedSignerEmail
538 = rAttribs
.getString(O_TOKEN(suggestedsigneremail
), OUString());
539 mrShapeModel
.maSignatureLineSigningInstructions
540 = rAttribs
.getString(O_TOKEN(signinginstructions
), OUString());
541 mrShapeModel
.mbSignatureLineShowSignDate
= ConversionHelper::decodeBool(
542 rAttribs
.getString(XML_showsigndate
, "t")); // default is true
543 mrShapeModel
.mbSignatureLineCanAddComment
= ConversionHelper::decodeBool(
544 rAttribs
.getString(XML_allowcomments
, "f")); // default is false
546 case O_TOKEN( lock
):
550 // handle remaining stuff in base class
551 return ShapeTypeContext::onCreateContext( nElement
, rAttribs
);
554 void ShapeContext::setPoints( const OUString
& rPoints
)
556 mrShapeModel
.maPoints
.clear();
557 sal_Int32 nIndex
= 0;
561 sal_Int32 nX
= rPoints
.getToken( 0, ',', nIndex
).toInt32();
562 sal_Int32 nY
= rPoints
.getToken( 0, ',', nIndex
).toInt32();
563 mrShapeModel
.maPoints
.emplace_back( nX
, nY
);
567 void ShapeContext::setFrom( const OUString
& rPoints
)
569 if (!rPoints
.isEmpty())
570 mrShapeModel
.maFrom
= rPoints
;
573 void ShapeContext::setTo( const OUString
& rPoints
)
575 if (!rPoints
.isEmpty())
576 mrShapeModel
.maTo
= rPoints
;
579 void ShapeContext::setControl1( const OUString
& rPoints
)
581 if (!rPoints
.isEmpty())
582 mrShapeModel
.maControl1
= rPoints
;
585 void ShapeContext::setControl2( const OUString
& rPoints
)
587 if (!rPoints
.isEmpty())
588 mrShapeModel
.maControl2
= rPoints
;
590 void ShapeContext::setVmlPath( const OUString
& rPath
)
592 if (!rPath
.isEmpty())
593 mrShapeModel
.maVmlPath
= rPath
;
596 GroupShapeContext::GroupShapeContext(ContextHandler2Helper
const& rParent
,
597 const std::shared_ptr
<GroupShape
>& pShape
,
598 const AttributeList
& rAttribs
)
599 : ShapeContext(rParent
, pShape
, rAttribs
)
600 , mrShapes(pShape
->getChildren())
604 ContextHandlerRef
GroupShapeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
606 // try to create a context of an embedded shape
607 ContextHandlerRef xContext
= createShapeContext( *this, mrShapes
, nElement
, rAttribs
);
608 // handle remaining stuff of this shape in base class
609 return xContext
? xContext
: ShapeContext::onCreateContext( nElement
, rAttribs
);
612 RectangleShapeContext::RectangleShapeContext(ContextHandler2Helper
const& rParent
,
613 const AttributeList
& rAttribs
,
614 const std::shared_ptr
<RectangleShape
>& pShape
)
615 : ShapeContext(rParent
, pShape
, rAttribs
)
619 ContextHandlerRef
RectangleShapeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
621 // The parent class's context is fine
622 return ShapeContext::onCreateContext( nElement
, rAttribs
);
625 ControlShapeContext::ControlShapeContext( ::oox::core::ContextHandler2Helper
const & rParent
, ShapeContainer
& rShapes
, const AttributeList
& rAttribs
)
626 : ShapeContextBase (rParent
)
628 ::oox::vml::ControlInfo aInfo
;
629 aInfo
.maShapeId
= rAttribs
.getXString( W_TOKEN( shapeid
), OUString() );
630 aInfo
.maFragmentPath
= getFragmentPathFromRelId(rAttribs
.getString( R_TOKEN(id
), OUString() ));
631 aInfo
.maName
= rAttribs
.getString( W_TOKEN( name
), OUString() );
632 aInfo
.mbTextContentShape
= true;
633 rShapes
.getDrawing().registerControl(aInfo
);
638 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */