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 <sal/config.h>
22 #include <string_view>
24 #include <oox/vml/vmlshapecontext.hxx>
26 #include <oox/core/xmlfilterbase.hxx>
27 #include <oox/helper/attributelist.hxx>
28 #include <oox/helper/helper.hxx>
29 #include <oox/token/namespaces.hxx>
30 #include <oox/token/tokens.hxx>
31 #include <oox/vml/vmldrawing.hxx>
32 #include <oox/vml/vmlshape.hxx>
33 #include <oox/vml/vmlshapecontainer.hxx>
34 #include <oox/vml/vmltextboxcontext.hxx>
36 #include <osl/diagnose.h>
37 #include <filter/msfilter/escherex.hxx>
38 #include <o3tl/string_view.hxx>
42 using namespace ::com::sun::star
;
44 using ::oox::core::ContextHandler2
;
45 using ::oox::core::ContextHandler2Helper
;
46 using ::oox::core::ContextHandlerRef
;
50 /** Returns the boolean value from the specified VML attribute (if present).
52 std::optional
< bool > lclDecodeBool( const AttributeList
& rAttribs
, sal_Int32 nToken
)
54 std::optional
< OUString
> oValue
= rAttribs
.getString( nToken
);
55 if( oValue
.has_value() ) return std::optional
< bool >( ConversionHelper::decodeBool( oValue
.value() ) );
56 return std::optional
< bool >();
59 /** Returns the percentage value from the specified VML attribute (if present).
60 The value will be normalized (1.0 is returned for 100%).
62 std::optional
< double > lclDecodePercent( const AttributeList
& rAttribs
, sal_Int32 nToken
, double fDefValue
)
64 std::optional
< OUString
> oValue
= rAttribs
.getString( nToken
);
65 if( oValue
.has_value() ) return std::optional
< double >( ConversionHelper::decodePercent( oValue
.value(), fDefValue
) );
66 return std::optional
< double >();
69 /** #119750# Special method for opacity; it *should* be a percentage value, but there are cases
70 where a value relative to 0xffff (65536) is used, ending with an 'f'
72 std::optional
< double > lclDecodeOpacity( const AttributeList
& rAttribs
, sal_Int32 nToken
, double fDefValue
)
74 std::optional
< OUString
> oValue
= rAttribs
.getString( nToken
);
75 double fRetval(fDefValue
);
77 if( oValue
.has_value() )
79 const OUString
& aString(oValue
.value());
80 const sal_Int32
nLength(aString
.getLength());
84 if(aString
.endsWith("f"))
86 fRetval
= std::clamp(aString
.toDouble() / 65536.0, 0.0, 1.0);
90 fRetval
= ConversionHelper::decodePercent( aString
, fDefValue
);
95 return std::optional
< double >(fRetval
);
98 /** Returns the integer value pair from the specified VML attribute (if present).
100 std::optional
< Int32Pair
> lclDecodeInt32Pair( const AttributeList
& rAttribs
, sal_Int32 nToken
)
102 std::optional
< OUString
> oValue
= rAttribs
.getString( nToken
);
103 std::optional
< Int32Pair
> oRetValue
;
104 if( oValue
.has_value() )
106 std::u16string_view aValue1
, aValue2
;
107 ConversionHelper::separatePair( aValue1
, aValue2
, oValue
.value(), ',' );
108 oRetValue
= Int32Pair( o3tl::toInt32(aValue1
), o3tl::toInt32(aValue2
) );
113 /** Returns the percentage pair from the specified VML attribute (if present).
115 std::optional
< DoublePair
> lclDecodePercentPair( const AttributeList
& rAttribs
, sal_Int32 nToken
)
117 std::optional
< OUString
> oValue
= rAttribs
.getString( nToken
);
118 std::optional
< DoublePair
> oRetValue
;
119 if( oValue
.has_value() )
121 std::u16string_view aValue1
, aValue2
;
122 ConversionHelper::separatePair( aValue1
, aValue2
, oValue
.value(), ',' );
123 oRetValue
= DoublePair(
124 ConversionHelper::decodePercent( aValue1
, 0.0 ),
125 ConversionHelper::decodePercent( aValue2
, 0.0 ) );
130 /** Returns the boolean value from the passed string of an attribute in the x:
131 namespace (VML for spreadsheets). Supported values: f, t, False, True.
132 @param bDefaultForEmpty Default value for the empty string.
134 bool lclDecodeVmlxBool( std::u16string_view rValue
, bool bDefaultForEmpty
)
136 if( rValue
.empty() ) return bDefaultForEmpty
;
137 sal_Int32 nToken
= AttributeConversion::decodeToken( rValue
);
138 // anything else than 't' or 'True' is considered to be false, as specified
139 return (nToken
== XML_t
) || (nToken
== XML_True
);
144 ShapeLayoutContext::ShapeLayoutContext( ContextHandler2Helper
const & rParent
, Drawing
& rDrawing
) :
145 ContextHandler2( rParent
),
146 mrDrawing( rDrawing
)
150 ContextHandlerRef
ShapeLayoutContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
154 case O_TOKEN( idmap
):
156 OUString aBlockIds
= rAttribs
.getStringDefaulted( XML_data
);
157 sal_Int32 nIndex
= 0;
160 std::u16string_view aToken
= o3tl::trim(o3tl::getToken(aBlockIds
, 0, ' ', nIndex
));
161 if( !aToken
.empty() )
162 mrDrawing
.registerBlockId( o3tl::toInt32(aToken
) );
170 ClientDataContext::ClientDataContext( ContextHandler2Helper
const & rParent
,
171 ClientData
& rClientData
, const AttributeList
& rAttribs
) :
172 ContextHandler2( rParent
),
173 mrClientData( rClientData
)
175 mrClientData
.mnObjType
= rAttribs
.getToken( XML_ObjectType
, XML_TOKEN_INVALID
);
178 ContextHandlerRef
ClientDataContext::onCreateContext( sal_Int32
/*nElement*/, const AttributeList
& /*rAttribs*/ )
180 if( isRootElement() )
182 maElementText
.clear();
188 void ClientDataContext::onCharacters( const OUString
& rChars
)
190 /* Empty but existing elements have special meaning, e.g. 'true'. Collect
191 existing text and convert it in onEndElement(). */
192 maElementText
= rChars
;
195 void ClientDataContext::onEndElement()
197 switch( getCurrentElement() )
199 case VMLX_TOKEN( Anchor
): mrClientData
.maAnchor
= maElementText
; break;
200 case VMLX_TOKEN( FmlaMacro
): mrClientData
.maFmlaMacro
= maElementText
; break;
201 case VMLX_TOKEN( FmlaPict
): mrClientData
.maFmlaPict
= maElementText
; break;
202 case VMLX_TOKEN( FmlaLink
): mrClientData
.maFmlaLink
= maElementText
; break;
203 case VMLX_TOKEN( FmlaRange
): mrClientData
.maFmlaRange
= maElementText
; break;
204 case VMLX_TOKEN( FmlaGroup
): mrClientData
.maFmlaGroup
= maElementText
; break;
205 case VMLX_TOKEN( TextHAlign
): mrClientData
.mnTextHAlign
= AttributeConversion::decodeToken( maElementText
); break;
206 case VMLX_TOKEN( TextVAlign
): mrClientData
.mnTextVAlign
= AttributeConversion::decodeToken( maElementText
); break;
207 case VMLX_TOKEN( Column
): mrClientData
.mnCol
= maElementText
.toInt32(); break;
208 case VMLX_TOKEN( Row
): mrClientData
.mnRow
= maElementText
.toInt32(); break;
209 case VMLX_TOKEN( Checked
): mrClientData
.mnChecked
= maElementText
.toInt32(); break;
210 case VMLX_TOKEN( DropStyle
): mrClientData
.mnDropStyle
= AttributeConversion::decodeToken( maElementText
); break;
211 case VMLX_TOKEN( DropLines
): mrClientData
.mnDropLines
= maElementText
.toInt32(); break;
212 case VMLX_TOKEN( Val
): mrClientData
.mnVal
= maElementText
.toInt32(); break;
213 case VMLX_TOKEN( Min
): mrClientData
.mnMin
= maElementText
.toInt32(); break;
214 case VMLX_TOKEN( Max
): mrClientData
.mnMax
= maElementText
.toInt32(); break;
215 case VMLX_TOKEN( Inc
): mrClientData
.mnInc
= maElementText
.toInt32(); break;
216 case VMLX_TOKEN( Page
): mrClientData
.mnPage
= maElementText
.toInt32(); break;
217 case VMLX_TOKEN( SelType
): mrClientData
.mnSelType
= AttributeConversion::decodeToken( maElementText
); break;
218 case VMLX_TOKEN( VTEdit
): mrClientData
.mnVTEdit
= maElementText
.toInt32(); break;
219 case VMLX_TOKEN( PrintObject
): mrClientData
.mbPrintObject
= lclDecodeVmlxBool( maElementText
, true ); break;
220 case VMLX_TOKEN( Visible
): mrClientData
.mbVisible
= lclDecodeVmlxBool( maElementText
, true ); break;
221 case VMLX_TOKEN( DDE
): mrClientData
.mbDde
= lclDecodeVmlxBool( maElementText
, true ); break;
222 case VMLX_TOKEN( NoThreeD
): mrClientData
.mbNo3D
= lclDecodeVmlxBool( maElementText
, true ); break;
223 case VMLX_TOKEN( NoThreeD2
): mrClientData
.mbNo3D2
= lclDecodeVmlxBool( maElementText
, true ); break;
224 case VMLX_TOKEN( MultiLine
): mrClientData
.mbMultiLine
= lclDecodeVmlxBool( maElementText
, true ); break;
225 case VMLX_TOKEN( VScroll
): mrClientData
.mbVScroll
= lclDecodeVmlxBool( maElementText
, true ); break;
226 case VMLX_TOKEN( SecretEdit
): mrClientData
.mbSecretEdit
= lclDecodeVmlxBool( maElementText
, true ); break;
230 ShapeContextBase::ShapeContextBase( ContextHandler2Helper
const & rParent
) :
231 ContextHandler2( rParent
)
235 ContextHandlerRef
ShapeContextBase::createShapeContext( ContextHandler2Helper
const & rParent
,
236 ShapeContainer
& rShapes
, sal_Int32 nElement
, const AttributeList
& rAttribs
)
240 case O_TOKEN( shapelayout
):
241 return new ShapeLayoutContext( rParent
, rShapes
.getDrawing() );
243 case VML_TOKEN( shapetype
):
244 return new ShapeTypeContext( rParent
, rShapes
.createShapeType(), rAttribs
);
245 case VML_TOKEN( group
):
246 return new GroupShapeContext( rParent
, rShapes
.createShape
< GroupShape
>(), rAttribs
);
247 case VML_TOKEN( shape
):
248 if (rAttribs
.hasAttribute(XML_path
) &&
249 // tdf#122563 skip in the case of empty path
250 !rAttribs
.getStringDefaulted(XML_path
).isEmpty())
251 return new ShapeContext( rParent
, rShapes
.createShape
< BezierShape
>(), rAttribs
);
253 return new ShapeContext( rParent
, rShapes
.createShape
< ComplexShape
>(), rAttribs
);
254 case VML_TOKEN(background
):
255 case VML_TOKEN( rect
):
256 return new RectangleShapeContext( rParent
, rAttribs
, rShapes
.createShape
< RectangleShape
>() );
257 case VML_TOKEN( roundrect
):
258 return new ShapeContext( rParent
, rShapes
.createShape
< RectangleShape
>(), rAttribs
);
259 case VML_TOKEN( oval
):
260 return new ShapeContext( rParent
, rShapes
.createShape
< EllipseShape
>(), rAttribs
);
261 case VML_TOKEN( polyline
):
262 return new ShapeContext( rParent
, rShapes
.createShape
< PolyLineShape
>(), rAttribs
);
263 case VML_TOKEN( line
):
264 return new ShapeContext( rParent
, rShapes
.createShape
< LineShape
>(), rAttribs
);
265 case VML_TOKEN( curve
):
266 return new ShapeContext( rParent
, rShapes
.createShape
< BezierShape
>(), rAttribs
);
269 case VML_TOKEN( arc
):
270 case VML_TOKEN( diagram
):
271 case VML_TOKEN( image
):
272 return new ShapeContext( rParent
, rShapes
.createShape
< ComplexShape
>(), rAttribs
);
274 case W_TOKEN(control
):
275 return new ControlShapeContext( rParent
, rShapes
, rAttribs
);
280 ShapeTypeContext::ShapeTypeContext(ContextHandler2Helper
const & rParent
,
281 std::shared_ptr
<ShapeType
> const& pShapeType
,
282 const AttributeList
& rAttribs
)
283 : ShapeContextBase(rParent
)
284 , m_pShapeType(pShapeType
) // tdf#112311 keep it alive
285 , mrTypeModel( pShapeType
->getTypeModel() )
287 // shape identifier and shape name
288 bool bHasOspid
= rAttribs
.hasAttribute( O_TOKEN( spid
) );
289 mrTypeModel
.maShapeId
= rAttribs
.getXString( bHasOspid
? O_TOKEN( spid
) : XML_id
, OUString() );
290 mrTypeModel
.maLegacyId
= rAttribs
.getStringDefaulted( XML_id
);
291 OSL_ENSURE( !mrTypeModel
.maShapeId
.isEmpty(), "ShapeTypeContext::ShapeTypeContext - missing shape identifier" );
292 // builtin shape type identifier
293 mrTypeModel
.moShapeType
= rAttribs
.getInteger( O_TOKEN( spt
) );
294 // if the o:spid attribute exists, the id attribute contains the user-defined shape name
297 mrTypeModel
.maShapeName
= rAttribs
.getXString( XML_id
, OUString() );
298 // get ShapeType and ShapeId from name for compatibility
299 static constexpr OUString sShapeTypePrefix
= u
"shapetype_"_ustr
;
301 if( mrTypeModel
.maShapeName
.startsWith( sShapeTypePrefix
) )
303 mrTypeModel
.maShapeId
= mrTypeModel
.maShapeName
;
304 mrTypeModel
.moShapeType
= o3tl::toInt32(mrTypeModel
.maShapeName
.subView(sShapeTypePrefix
.getLength()));
306 else if (mrTypeModel
.maShapeName
.startsWith("_x0000_t", &tmp
))
308 mrTypeModel
.maShapeId
= mrTypeModel
.maShapeName
;
309 mrTypeModel
.moShapeType
= tmp
.toInt32();
313 // coordinate system position/size, CSS style
314 mrTypeModel
.moCoordPos
= lclDecodeInt32Pair( rAttribs
, XML_coordorigin
);
315 mrTypeModel
.moCoordSize
= lclDecodeInt32Pair( rAttribs
, XML_coordsize
);
316 setStyle( rAttribs
.getStringDefaulted( XML_style
) );
317 if( lclDecodeBool( rAttribs
, O_TOKEN( hr
)).value_or( false ))
318 { // MSO's handling of o:hr width is nowhere near what the spec says:
319 // - o:hrpct is not in % but in 0.1%
320 // - if o:hrpct is not given, 100% width is assumed
321 // - given width is used only if explicit o:hrpct="0" is given
322 OUString hrpct
= rAttribs
.getString( O_TOKEN( hrpct
), "1000" );
324 mrTypeModel
.maWidthPercent
= OUString::number( hrpct
.toInt32() );
325 mrTypeModel
.maWrapDistanceLeft
= "0";
326 mrTypeModel
.maWrapDistanceRight
= "0";
327 mrTypeModel
.maPositionHorizontal
= rAttribs
.getString( O_TOKEN( hralign
), "left" );
328 mrTypeModel
.moWrapType
= "topAndBottom";
331 // stroke settings (may be overridden by v:stroke element later)
332 mrTypeModel
.maStrokeModel
.moStroked
= lclDecodeBool( rAttribs
, XML_stroked
);
333 mrTypeModel
.maStrokeModel
.moColor
= rAttribs
.getString( XML_strokecolor
);
334 mrTypeModel
.maStrokeModel
.moWeight
= rAttribs
.getString( XML_strokeweight
);
336 // fill settings (may be overridden by v:fill element later)
337 mrTypeModel
.maFillModel
.moFilled
= lclDecodeBool( rAttribs
, XML_filled
);
338 mrTypeModel
.maFillModel
.moColor
= rAttribs
.getString( XML_fillcolor
);
340 // For roundrect we may have an arcsize attribute to read
341 mrTypeModel
.maArcsize
= rAttribs
.getStringDefaulted(XML_arcsize
);
343 mrTypeModel
.maEditAs
= rAttribs
.getStringDefaulted(XML_editas
);
345 mrTypeModel
.maAdjustments
= rAttribs
.getStringDefaulted(XML_adj
);
348 ContextHandlerRef
ShapeTypeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
350 if( isRootElement() ) switch( nElement
)
352 case VML_TOKEN( stroke
):
353 assignIfUsed( mrTypeModel
.maStrokeModel
.moStroked
, lclDecodeBool( rAttribs
, XML_on
) );
354 mrTypeModel
.maStrokeModel
.maStartArrow
.moArrowType
= rAttribs
.getToken( XML_startarrow
);
355 mrTypeModel
.maStrokeModel
.maStartArrow
.moArrowWidth
= rAttribs
.getToken( XML_startarrowwidth
);
356 mrTypeModel
.maStrokeModel
.maStartArrow
.moArrowLength
= rAttribs
.getToken( XML_startarrowlength
);
357 mrTypeModel
.maStrokeModel
.maEndArrow
.moArrowType
= rAttribs
.getToken( XML_endarrow
);
358 mrTypeModel
.maStrokeModel
.maEndArrow
.moArrowWidth
= rAttribs
.getToken( XML_endarrowwidth
);
359 mrTypeModel
.maStrokeModel
.maEndArrow
.moArrowLength
= rAttribs
.getToken( XML_endarrowlength
);
360 assignIfUsed( mrTypeModel
.maStrokeModel
.moColor
, rAttribs
.getString( XML_color
) );
361 mrTypeModel
.maStrokeModel
.moOpacity
= lclDecodeOpacity( rAttribs
, XML_opacity
, 1.0 );
362 assignIfUsed( mrTypeModel
.maStrokeModel
.moWeight
, rAttribs
.getString( XML_weight
) );
363 mrTypeModel
.maStrokeModel
.moDashStyle
= rAttribs
.getString( XML_dashstyle
);
364 mrTypeModel
.maStrokeModel
.moLineStyle
= rAttribs
.getToken( XML_linestyle
);
365 mrTypeModel
.maStrokeModel
.moEndCap
= rAttribs
.getToken( XML_endcap
);
366 mrTypeModel
.maStrokeModel
.moJoinStyle
= rAttribs
.getToken( XML_joinstyle
);
368 case VML_TOKEN( fill
):
370 // in DOCX shapes use r:id for the relationship id
371 // in XLSX they use o:relid
372 bool bHasORelId
= rAttribs
.hasAttribute( O_TOKEN(relid
) );
373 assignIfUsed( mrTypeModel
.maFillModel
.moFilled
, lclDecodeBool( rAttribs
, XML_on
) );
374 assignIfUsed( mrTypeModel
.maFillModel
.moColor
, rAttribs
.getString( XML_color
) );
375 mrTypeModel
.maFillModel
.moOpacity
= lclDecodeOpacity( rAttribs
, XML_opacity
, 1.0 );
376 mrTypeModel
.maFillModel
.moColor2
= rAttribs
.getString( XML_color2
);
377 mrTypeModel
.maFillModel
.moOpacity2
= lclDecodeOpacity( rAttribs
, XML_opacity2
, 1.0 );
378 mrTypeModel
.maFillModel
.moType
= rAttribs
.getToken( XML_type
);
379 mrTypeModel
.maFillModel
.moAngle
= rAttribs
.getInteger( XML_angle
);
380 mrTypeModel
.maFillModel
.moFocus
= lclDecodePercent( rAttribs
, XML_focus
, 0.0 );
381 mrTypeModel
.maFillModel
.moFocusPos
= lclDecodePercentPair( rAttribs
, XML_focusposition
);
382 mrTypeModel
.maFillModel
.moFocusSize
= lclDecodePercentPair( rAttribs
, XML_focussize
);
383 mrTypeModel
.maFillModel
.moBitmapPath
= decodeFragmentPath( rAttribs
, bHasORelId
? O_TOKEN(relid
) : R_TOKEN(id
) );
384 mrTypeModel
.maFillModel
.moRotate
= lclDecodeBool( rAttribs
, XML_rotate
);
387 case VML_TOKEN( imagedata
):
389 // shapes in docx use r:id for the relationship id
390 // in xlsx it they use o:relid
391 bool bHasORelId
= rAttribs
.hasAttribute( O_TOKEN( relid
) );
392 mrTypeModel
.moGraphicPath
= decodeFragmentPath( rAttribs
, bHasORelId
? O_TOKEN( relid
) : R_TOKEN( id
) );
393 mrTypeModel
.moGraphicTitle
= rAttribs
.getString( O_TOKEN( title
) );
395 // Get crop attributes.
396 mrTypeModel
.moCropBottom
= rAttribs
.getString(XML_cropbottom
);
397 mrTypeModel
.moCropLeft
= rAttribs
.getString(XML_cropleft
);
398 mrTypeModel
.moCropRight
= rAttribs
.getString(XML_cropright
);
399 mrTypeModel
.moCropTop
= rAttribs
.getString(XML_croptop
);
402 std::optional
<OUString
> oGain
= rAttribs
.getString(XML_gain
);
403 sal_Int32 nGain
= 0x10000;
404 if (oGain
.has_value() && oGain
.value().endsWith("f"))
406 nGain
= oGain
.value().toInt32();
410 nGain
*= 101; // 100 + 1 to round
414 mrTypeModel
.mnGain
= nGain
;
416 // Blacklevel / brightness.
417 std::optional
<OUString
> oBlacklevel
= rAttribs
.getString(XML_blacklevel
);
418 sal_Int16 nBlacklevel
= 0;
419 if (oBlacklevel
.has_value() && oBlacklevel
.value().endsWith("f"))
421 nBlacklevel
= oBlacklevel
.value().toInt32();
423 if (nBlacklevel
!= 0)
427 mrTypeModel
.mnBlacklevel
= nBlacklevel
;
430 case NMSP_vmlWord
| XML_wrap
:
431 mrTypeModel
.moWrapAnchorX
= rAttribs
.getString(XML_anchorx
);
432 mrTypeModel
.moWrapAnchorY
= rAttribs
.getString(XML_anchory
);
433 mrTypeModel
.moWrapType
= rAttribs
.getString(XML_type
);
434 mrTypeModel
.moWrapSide
= rAttribs
.getString(XML_side
);
436 case VML_TOKEN( shadow
):
438 mrTypeModel
.maShadowModel
.mbHasShadow
= true;
439 mrTypeModel
.maShadowModel
.moShadowOn
= lclDecodeBool(rAttribs
, XML_on
).value_or(false);
440 assignIfUsed(mrTypeModel
.maShadowModel
.moColor
, rAttribs
.getString(XML_color
));
441 assignIfUsed(mrTypeModel
.maShadowModel
.moOffset
, rAttribs
.getString(XML_offset
));
442 mrTypeModel
.maShadowModel
.moOpacity
= lclDecodePercent(rAttribs
, XML_opacity
, 1.0);
445 case VML_TOKEN( textpath
):
446 assignIfUsed(mrTypeModel
.maTextpathModel
.moString
, rAttribs
.getString(XML_string
));
447 assignIfUsed(mrTypeModel
.maTextpathModel
.moStyle
, rAttribs
.getString(XML_style
));
448 assignIfUsed(mrTypeModel
.maTextpathModel
.moTrim
, lclDecodeBool(rAttribs
, XML_trim
));
454 std::optional
< OUString
> ShapeTypeContext::decodeFragmentPath( const AttributeList
& rAttribs
, sal_Int32 nToken
) const
456 std::optional
< OUString
> oFragmentPath
;
457 std::optional
< OUString
> oRelId
= rAttribs
.getString( nToken
);
458 if( oRelId
.has_value() )
459 oFragmentPath
= getFragmentPathFromRelId( oRelId
.value() );
460 return oFragmentPath
;
463 void ShapeTypeContext::setStyle( std::u16string_view rStyle
)
465 sal_Int32 nIndex
= 0;
468 std::u16string_view aName
, aValue
;
469 if( ConversionHelper::separatePair( aName
, aValue
, o3tl::getToken(rStyle
, 0, ';', nIndex
), ':' ) )
471 if( aName
== u
"position" ) mrTypeModel
.maPosition
= aValue
;
472 else if( aName
== u
"z-index" ) mrTypeModel
.maZIndex
= aValue
;
473 else if( aName
== u
"left" ) mrTypeModel
.maLeft
= aValue
;
474 else if( aName
== u
"top" ) mrTypeModel
.maTop
= aValue
;
475 else if( aName
== u
"width" ) mrTypeModel
.maWidth
= aValue
;
476 else if( aName
== u
"height" ) mrTypeModel
.maHeight
= aValue
;
477 else if( aName
== u
"margin-left" ) mrTypeModel
.maMarginLeft
= aValue
;
478 else if( aName
== u
"margin-top" ) mrTypeModel
.maMarginTop
= aValue
;
479 else if( aName
== u
"mso-position-vertical-relative" ) mrTypeModel
.maPositionVerticalRelative
= aValue
;
480 else if( aName
== u
"mso-position-horizontal-relative" ) mrTypeModel
.maPositionHorizontalRelative
= aValue
;
481 else if( aName
== u
"mso-position-horizontal" ) mrTypeModel
.maPositionHorizontal
= aValue
;
482 else if( aName
== u
"mso-position-vertical" ) mrTypeModel
.maPositionVertical
= aValue
;
483 else if( aName
== u
"mso-width-percent" ) mrTypeModel
.maWidthPercent
= aValue
;
484 else if( aName
== u
"mso-width-relative" ) mrTypeModel
.maWidthRelative
= aValue
;
485 else if( aName
== u
"mso-height-percent" ) mrTypeModel
.maHeightPercent
= aValue
;
486 else if( aName
== u
"mso-height-relative" ) mrTypeModel
.maHeightRelative
= aValue
;
487 else if( aName
== u
"mso-fit-shape-to-text" ) mrTypeModel
.mbAutoHeight
= true;
488 else if( aName
== u
"rotation" ) mrTypeModel
.maRotation
= aValue
;
489 else if( aName
== u
"flip" ) mrTypeModel
.maFlip
= aValue
;
490 else if( aName
== u
"visibility" )
491 mrTypeModel
.mbVisible
= aValue
!= u
"hidden";
492 else if( aName
== u
"mso-wrap-style" ) mrTypeModel
.maWrapStyle
= aValue
;
493 else if ( aName
== u
"v-text-anchor" ) mrTypeModel
.maVTextAnchor
= aValue
;
494 else if ( aName
== u
"mso-wrap-distance-left" ) mrTypeModel
.maWrapDistanceLeft
= aValue
;
495 else if ( aName
== u
"mso-wrap-distance-right" ) mrTypeModel
.maWrapDistanceRight
= aValue
;
496 else if ( aName
== u
"mso-wrap-distance-top" ) mrTypeModel
.maWrapDistanceTop
= aValue
;
497 else if ( aName
== u
"mso-wrap-distance-bottom" ) mrTypeModel
.maWrapDistanceBottom
= aValue
;
502 ShapeContext::ShapeContext(ContextHandler2Helper
const& rParent
,
503 const std::shared_ptr
<ShapeBase
>& pShape
, const AttributeList
& rAttribs
)
504 : ShapeTypeContext(rParent
, pShape
, rAttribs
)
506 , mrShapeModel(pShape
->getShapeModel())
508 // collect shape specific attributes
509 mrShapeModel
.maType
= rAttribs
.getXString( XML_type
, OUString() );
511 setPoints( rAttribs
.getStringDefaulted( XML_points
) );
512 // line start and end positions
513 setFrom(rAttribs
.getStringDefaulted(XML_from
));
514 setTo(rAttribs
.getStringDefaulted(XML_to
));
515 setControl1(rAttribs
.getStringDefaulted(XML_control1
));
516 setControl2(rAttribs
.getStringDefaulted(XML_control2
));
517 setVmlPath(rAttribs
.getStringDefaulted(XML_path
));
518 setHyperlink(rAttribs
.getStringDefaulted(XML_href
));
521 ContextHandlerRef
ShapeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
523 // Excel specific shape client data
524 if( isRootElement() ) switch( nElement
)
526 case VML_TOKEN( textbox
):
528 // Calculate the shape type: map both <rect> and <v:shape> with a textbox shape type to
530 sal_Int32 nShapeType
= 0;
531 if (ShapeContainer
* pShapeContainer
= mrShape
.getContainer())
533 OUString aType
= mrShapeModel
.maType
;
534 if (!aType
.isEmpty() && aType
[0] == '#')
536 aType
= aType
.copy(1);
538 if (const ShapeType
* pShapeType
= pShapeContainer
->getShapeTypeById(aType
))
540 nShapeType
= pShapeType
->getTypeModel().moShapeType
.value();
543 mrShapeModel
.mbInGroup
= (getParentElement() == VML_TOKEN(group
));
545 // FIXME: the shape with textbox should be used for the next cases
546 if (getCurrentElement() == VML_TOKEN(rect
) || nShapeType
== ESCHER_ShpInst_TextBox
)
548 if (mrShapeModel
.mbInGroup
)
549 // FIXME: without this a text will be added into the group-shape instead of its
551 dynamic_cast<SimpleShape
&>(mrShape
).setService("com.sun.star.drawing.TextShape");
553 // FIXME: without this we does not handle some properties like shadow
554 dynamic_cast<SimpleShape
&>(mrShape
).setService("com.sun.star.text.TextFrame");
556 return new TextBoxContext( *this, mrShapeModel
.createTextBox(mrShape
.getTypeModel()), rAttribs
,
557 mrShape
.getDrawing().getFilter().getGraphicHelper());
559 case VMLX_TOKEN( ClientData
):
560 // tdf#41466 ActiveX control shapes with a textbox are transformed into a frame
561 // (see unit test testActiveXOptionButtonGroup)
562 dynamic_cast<SimpleShape
&>(mrShape
).setService("com.sun.star.text.TextFrame");
563 return new ClientDataContext( *this, mrShapeModel
.createClientData(), rAttribs
);
564 case VMLPPT_TOKEN( textdata
):
565 // Force RectangleShape, this is ugly :(
566 // and is there because of the lines above which change it to TextFrame
567 dynamic_cast< SimpleShape
& >( mrShape
).setService(
568 "com.sun.star.drawing.RectangleShape");
569 mrShapeModel
.maLegacyDiagramPath
= getFragmentPathFromRelId(rAttribs
.getStringDefaulted(XML_id
));
571 case O_TOKEN( signatureline
):
572 mrShapeModel
.mbIsSignatureLine
= true;
573 mrShapeModel
.maSignatureId
= rAttribs
.getStringDefaulted(XML_id
);
574 mrShapeModel
.maSignatureLineSuggestedSignerName
575 = rAttribs
.getStringDefaulted(O_TOKEN(suggestedsigner
));
576 mrShapeModel
.maSignatureLineSuggestedSignerTitle
577 = rAttribs
.getStringDefaulted(O_TOKEN(suggestedsigner2
));
578 mrShapeModel
.maSignatureLineSuggestedSignerEmail
579 = rAttribs
.getStringDefaulted(O_TOKEN(suggestedsigneremail
));
580 mrShapeModel
.maSignatureLineSigningInstructions
581 = rAttribs
.getStringDefaulted(O_TOKEN(signinginstructions
));
582 mrShapeModel
.mbSignatureLineShowSignDate
= ConversionHelper::decodeBool(
583 rAttribs
.getString(XML_showsigndate
, "t")); // default is true
584 mrShapeModel
.mbSignatureLineCanAddComment
= ConversionHelper::decodeBool(
585 rAttribs
.getString(XML_allowcomments
, "f")); // default is false
587 case O_TOKEN( lock
):
591 // handle remaining stuff in base class
592 return ShapeTypeContext::onCreateContext( nElement
, rAttribs
);
595 void ShapeContext::setPoints(std::u16string_view rPoints
)
597 mrShapeModel
.maPoints
.clear();
598 sal_Int32 nIndex
= 0;
602 sal_Int32 nX
= ConversionHelper::decodeMeasureToTwip(
603 mrShape
.getDrawing().getFilter().getGraphicHelper(), o3tl::getToken(rPoints
, 0, ',', nIndex
),
605 sal_Int32 nY
= ConversionHelper::decodeMeasureToTwip(
606 mrShape
.getDrawing().getFilter().getGraphicHelper(), o3tl::getToken(rPoints
, 0, ',', nIndex
),
608 mrShapeModel
.maPoints
.emplace_back(nX
, nY
);
610 // VML polyline has no size in its style attribute. Word writes the size to attribute
611 // coordsize with values in twip but without unit. For others we get size from points.
612 if (!mrShape
.getTypeModel().maWidth
.isEmpty() || !mrShape
.getTypeModel().maHeight
.isEmpty())
615 if (mrShape
.getTypeModel().moCoordSize
.has_value())
617 double fWidth
= mrShape
.getTypeModel().moCoordSize
.value().first
;
618 fWidth
= o3tl::convert(fWidth
, o3tl::Length::twip
, o3tl::Length::pt
);
619 double fHeight
= mrShape
.getTypeModel().moCoordSize
.value().second
;
620 fHeight
= o3tl::convert(fHeight
, o3tl::Length::twip
, o3tl::Length::pt
);
621 mrShape
.getTypeModel().maWidth
= OUString::number(fWidth
) + "pt";
622 mrShape
.getTypeModel().maHeight
= OUString::number(fHeight
) + "pt";
624 else if (mrShapeModel
.maPoints
.size())
626 double fMinX
= mrShapeModel
.maPoints
[0].X
;
627 double fMaxX
= mrShapeModel
.maPoints
[0].X
;
628 double fMinY
= mrShapeModel
.maPoints
[0].Y
;
629 double fMaxY
= mrShapeModel
.maPoints
[0].Y
;
630 for (const auto& rPoint
: mrShapeModel
.maPoints
)
632 if (rPoint
.X
< fMinX
)
634 else if (rPoint
.X
> fMaxX
)
636 if (rPoint
.Y
< fMinY
)
638 else if (rPoint
.Y
> fMaxY
)
641 mrShape
.getTypeModel().maWidth
643 o3tl::convert(fMaxX
- fMinX
, o3tl::Length::twip
, o3tl::Length::pt
))
645 mrShape
.getTypeModel().maHeight
647 o3tl::convert(fMaxY
- fMinY
, o3tl::Length::twip
, o3tl::Length::pt
))
649 // Set moCoordSize, otherwise default (1000,1000) is used.
650 mrShape
.getTypeModel().moCoordSize
=
651 Int32Pair(basegfx::fround(fMaxX
- fMinX
), basegfx::fround(fMaxY
- fMinY
));
655 void ShapeContext::setFrom( const OUString
& rPoints
)
657 if (!rPoints
.isEmpty())
658 mrShapeModel
.maFrom
= rPoints
;
661 void ShapeContext::setTo( const OUString
& rPoints
)
663 if (!rPoints
.isEmpty())
664 mrShapeModel
.maTo
= rPoints
;
667 void ShapeContext::setControl1( const OUString
& rPoints
)
669 if (!rPoints
.isEmpty())
670 mrShapeModel
.maControl1
= rPoints
;
673 void ShapeContext::setControl2( const OUString
& rPoints
)
675 if (!rPoints
.isEmpty())
676 mrShapeModel
.maControl2
= rPoints
;
678 void ShapeContext::setVmlPath( const OUString
& rPath
)
680 if (!rPath
.isEmpty())
681 mrShapeModel
.maVmlPath
= rPath
;
684 void ShapeContext::setHyperlink( const OUString
& rHyperlink
)
686 if (!rHyperlink
.isEmpty())
687 mrShapeModel
.maHyperlink
= rHyperlink
;
690 GroupShapeContext::GroupShapeContext(ContextHandler2Helper
const& rParent
,
691 const std::shared_ptr
<GroupShape
>& pShape
,
692 const AttributeList
& rAttribs
)
693 : ShapeContext(rParent
, pShape
, rAttribs
)
694 , mrShapes(pShape
->getChildren())
698 ContextHandlerRef
GroupShapeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
700 // try to create a context of an embedded shape
701 ContextHandlerRef xContext
= createShapeContext( *this, mrShapes
, nElement
, rAttribs
);
702 // handle remaining stuff of this shape in base class
703 return xContext
? xContext
: ShapeContext::onCreateContext( nElement
, rAttribs
);
706 RectangleShapeContext::RectangleShapeContext(ContextHandler2Helper
const& rParent
,
707 const AttributeList
& rAttribs
,
708 const std::shared_ptr
<RectangleShape
>& pShape
)
709 : ShapeContext(rParent
, pShape
, rAttribs
)
713 ContextHandlerRef
RectangleShapeContext::onCreateContext( sal_Int32 nElement
, const AttributeList
& rAttribs
)
715 // The parent class's context is fine
716 return ShapeContext::onCreateContext( nElement
, rAttribs
);
719 ControlShapeContext::ControlShapeContext( ::oox::core::ContextHandler2Helper
const & rParent
, ShapeContainer
& rShapes
, const AttributeList
& rAttribs
)
720 : ShapeContextBase (rParent
)
722 ::oox::vml::ControlInfo aInfo
;
723 aInfo
.maShapeId
= rAttribs
.getXString( W_TOKEN( shapeid
), OUString() );
724 aInfo
.maFragmentPath
= getFragmentPathFromRelId(rAttribs
.getStringDefaulted( R_TOKEN(id
)));
725 aInfo
.maName
= rAttribs
.getStringDefaulted( W_TOKEN( name
));
726 aInfo
.mbTextContentShape
= true;
727 rShapes
.getDrawing().registerControl(aInfo
);
732 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */