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 <drawingml/customshapeproperties.hxx>
21 #include <oox/helper/propertymap.hxx>
22 #include <oox/helper/propertyset.hxx>
23 #include <oox/token/properties.hxx>
24 #include <oox/token/tokenmap.hxx>
25 #include <com/sun/star/awt/Rectangle.hpp>
26 #include <com/sun/star/awt/Size.hpp>
27 #include <com/sun/star/beans/PropertyValues.hpp>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
30 #include <com/sun/star/drawing/EnhancedCustomShapeTextFrame.hpp>
31 #include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp>
32 #include <com/sun/star/drawing/XShape.hpp>
33 #include <comphelper/sequence.hxx>
34 #include <o3tl/string_view.hxx>
35 #include <sal/log.hxx>
39 using namespace ::com::sun::star
;
40 using namespace ::com::sun::star::uno
;
41 using namespace ::com::sun::star::beans
;
42 using namespace ::com::sun::star::drawing
;
44 namespace oox::drawingml
{
46 CustomShapeProperties::CustomShapeProperties()
47 : mnShapePresetType ( -1 )
48 , mbShapeTypeOverride(false)
49 , mbMirroredX ( false )
50 , mbMirroredY ( false )
51 , mnTextPreRotateAngle ( 0 )
52 , mnTextCameraZRotateAngle ( 0 )
57 uno::Sequence
< sal_Int8
> const & CustomShapeProperties::getShapePresetTypeName() const
59 return StaticTokenMap().getUtf8TokenName( mnShapePresetType
);
62 sal_Int32
CustomShapeProperties::SetCustomShapeGuideValue( std::vector
< CustomShapeGuide
>& rGuideList
, const CustomShapeGuide
& rGuide
)
64 std::vector
<CustomShapeGuide
>::size_type nIndex
= 0;
65 for( ; nIndex
< rGuideList
.size(); nIndex
++ )
67 if ( rGuideList
[ nIndex
].maName
== rGuide
.maName
)
70 if ( nIndex
== rGuideList
.size() )
71 rGuideList
.push_back( rGuide
);
72 return static_cast< sal_Int32
>( nIndex
);
75 // returns the index into the guidelist for a given formula name,
76 // if the return value is < 0 then the guide value could not be found
77 sal_Int32
CustomShapeProperties::GetCustomShapeGuideValue( const std::vector
< CustomShapeGuide
>& rGuideList
, std::u16string_view rFormulaName
)
79 // traverse the list from the end, because guide names can be reused
80 // and current is the last one
81 // see a1 guide in gear6 custom shape preset as example
82 sal_Int32 nIndex
= static_cast< sal_Int32
>( rGuideList
.size() ) - 1;
83 for( ; nIndex
>= 0; nIndex
-- )
85 if ( rGuideList
[ nIndex
].maName
== rFormulaName
)
92 bool CustomShapeProperties::representsDefaultShape() const
94 return !((getShapePresetType() >= 0 || maPath2DList
.size() > 0) &&
95 getShapePresetType() != XML_Rect
&&
96 getShapePresetType() != XML_rect
);
99 CustomShapeProperties::PresetDataMap
CustomShapeProperties::maPresetDataMap
;
101 void CustomShapeProperties::pushToPropSet(
102 const Reference
< XPropertySet
>& xPropSet
, const awt::Size
&aSize
)
104 if ( mnShapePresetType
>= 0 )
106 SAL_INFO("oox.drawingml", "preset: " << mnShapePresetType
);
108 if (maPresetDataMap
.empty())
109 initializePresetDataMap();
111 PropertyMap aPropertyMap
;
112 PropertySet
aPropSet( xPropSet
);
114 if (maPresetDataMap
.find(mnShapePresetType
) != maPresetDataMap
.end())
118 "found property map for preset: " << mnShapePresetType
);
120 aPropertyMap
= maPresetDataMap
[mnShapePresetType
];
122 aPropertyMap
.dumpCode( aPropertyMap
.makePropertySet() );
126 aPropertyMap
.setProperty( PROP_MirroredX
, mbMirroredX
);
127 aPropertyMap
.setProperty( PROP_MirroredY
, mbMirroredY
);
128 aPropertyMap
.setProperty( PROP_TextPreRotateAngle
, mnTextPreRotateAngle
);
129 aPropertyMap
.setProperty( PROP_TextCameraZRotateAngle
, mnTextCameraZRotateAngle
);
130 if (moTextAreaRotateAngle
.has_value())
131 aPropertyMap
.setProperty(PROP_TextRotateAngle
, moTextAreaRotateAngle
.value());
132 Sequence
< PropertyValue
> aSeq
= aPropertyMap
.makePropertyValueSequence();
133 aPropSet
.setProperty( PROP_CustomShapeGeometry
, aSeq
);
135 if ( !maAdjustmentGuideList
.empty() )
137 static const OUStringLiteral
sCustomShapeGeometry(u
"CustomShapeGeometry");
138 static const OUStringLiteral
sAdjustmentValues(u
"AdjustmentValues");
139 uno::Any aGeoPropSet
= xPropSet
->getPropertyValue( sCustomShapeGeometry
);
140 uno::Sequence
< beans::PropertyValue
> aGeoPropSeq
;
141 if ( aGeoPropSet
>>= aGeoPropSeq
)
143 for ( auto& rGeoProp
: asNonConstRange(aGeoPropSeq
) )
145 if ( rGeoProp
.Name
== sAdjustmentValues
)
147 uno::Sequence
< css::drawing::EnhancedCustomShapeAdjustmentValue
> aAdjustmentSeq
;
148 if ( rGeoProp
.Value
>>= aAdjustmentSeq
)
150 auto aAdjustmentSeqRange
= asNonConstRange(aAdjustmentSeq
);
152 for (auto const& adjustmentGuide
: maAdjustmentGuideList
)
154 if ( adjustmentGuide
.maName
.getLength() > 3 )
156 sal_Int32 nAdjustmentIndex
= o3tl::toInt32(adjustmentGuide
.maName
.subView( 3 )) - 1;
157 if ( ( nAdjustmentIndex
>= 0 ) && ( nAdjustmentIndex
< aAdjustmentSeq
.getLength() ) )
159 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
160 aAdjustmentVal
.Value
<<= adjustmentGuide
.maFormula
.toInt32();
161 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
162 aAdjustmentVal
.Name
= adjustmentGuide
.maName
;
163 aAdjustmentSeqRange
[ nAdjustmentIndex
] = aAdjustmentVal
;
165 } else if ( aAdjustmentSeq
.hasElements() ) {
166 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
167 aAdjustmentVal
.Value
<<= adjustmentGuide
.maFormula
.toInt32();
168 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
169 aAdjustmentVal
.Name
= adjustmentGuide
.maName
;
170 if (nIndex
< aAdjustmentSeq
.getLength())
172 aAdjustmentSeqRange
[nIndex
] = aAdjustmentVal
;
177 rGeoProp
.Value
<<= aAdjustmentSeq
;
178 xPropSet
->setPropertyValue( sCustomShapeGeometry
, Any( aGeoPropSeq
) );
188 PropertyMap aPropertyMap
;
189 aPropertyMap
.setProperty( PROP_Type
, OUString( "ooxml-non-primitive" ));
190 aPropertyMap
.setProperty( PROP_MirroredX
, mbMirroredX
);
191 aPropertyMap
.setProperty( PROP_MirroredY
, mbMirroredY
);
192 if( mnTextPreRotateAngle
)
193 aPropertyMap
.setProperty( PROP_TextPreRotateAngle
, mnTextPreRotateAngle
);
194 if (moTextAreaRotateAngle
.has_value())
195 aPropertyMap
.setProperty(PROP_TextRotateAngle
, moTextAreaRotateAngle
.value());
196 // Note 1: If Equations are defined - they are processed using internal div by 360 coordinates
197 // while if they are not, standard ooxml coordinates are used.
198 // This size specifically affects scaling.
199 // Note 2: Width and Height are set to 0 to force scaling to 1.
200 awt::Rectangle
aViewBox( 0, 0, aSize
.Width
, aSize
.Height
);
201 // tdf#113187 Each ArcTo introduces two additional equations for converting start and swing
202 // angles. They do not count for test for use of standard ooxml coordinates
203 if (maGuideList
.size() - 2 * countArcTo() > 0)
204 aViewBox
= awt::Rectangle( 0, 0, 0, 0 );
205 aPropertyMap
.setProperty( PROP_ViewBox
, aViewBox
);
207 Sequence
< EnhancedCustomShapeAdjustmentValue
> aAdjustmentValues( maAdjustmentGuideList
.size() );
208 auto aAdjustmentValuesRange
= asNonConstRange(aAdjustmentValues
);
209 for ( std::vector
<CustomShapeGuide
>::size_type i
= 0; i
< maAdjustmentGuideList
.size(); i
++ )
211 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
212 aAdjustmentVal
.Value
<<= maAdjustmentGuideList
[ i
].maFormula
.toInt32();
213 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
214 aAdjustmentVal
.Name
= maAdjustmentGuideList
[ i
].maName
;
215 aAdjustmentValuesRange
[ i
] = aAdjustmentVal
;
217 aPropertyMap
.setProperty( PROP_AdjustmentValues
, aAdjustmentValues
);
221 aPath
.setProperty( PROP_Segments
, comphelper::containerToSequence(maSegments
) );
223 if ( maTextRect
.has_value() ) {
224 Sequence
< EnhancedCustomShapeTextFrame
> aTextFrames
{
225 { /* tl */ { maTextRect
.value().l
, maTextRect
.value().t
},
226 /* br */ { maTextRect
.value().r
, maTextRect
.value().b
} }
228 aPath
.setProperty( PROP_TextFrames
, aTextFrames
);
231 if (!maConnectionSiteList
.empty())
233 css::uno::Sequence
<EnhancedCustomShapeParameterPair
> seqGluePoints
;
234 seqGluePoints
.realloc(maConnectionSiteList
.size());
236 for (auto& rGluePoint
: asNonConstRange(seqGluePoints
))
238 rGluePoint
.First
.Value
= maConnectionSiteList
[nId
].pos
.First
.Value
;
239 rGluePoint
.First
.Type
= maConnectionSiteList
[nId
].pos
.First
.Type
;
240 rGluePoint
.Second
.Value
= maConnectionSiteList
[nId
].pos
.Second
.Value
;
241 rGluePoint
.Second
.Type
= maConnectionSiteList
[nId
].pos
.Second
.Type
;
244 aPath
.setProperty(PROP_GluePoints
, seqGluePoints
);
247 sal_uInt32 nParameterPairs
= 0;
248 for ( auto const & i
: maPath2DList
)
249 nParameterPairs
+= i
.parameter
.size();
251 Sequence
< EnhancedCustomShapeParameterPair
> aParameterPairs( nParameterPairs
);
252 auto aParameterPairsRange
= asNonConstRange(aParameterPairs
);
254 for ( auto const & i
: maPath2DList
)
255 for ( auto const & j
: i
.parameter
)
256 aParameterPairsRange
[ k
++ ] = j
;
257 aPath
.setProperty( PROP_Coordinates
, aParameterPairs
);
259 if ( !maPath2DList
.empty() )
261 bool bAllZero
= true;
262 for ( auto const & i
: maPath2DList
)
271 Sequence
< awt::Size
> aSubViewSize( maPath2DList
.size() );
272 std::transform(maPath2DList
.begin(), maPath2DList
.end(), aSubViewSize
.getArray(),
275 SAL_INFO("oox.cscode",
276 "set subpath; size: " << p2d
.w
<< " x " << p2d
.h
);
277 return awt::Size(p2d
.w
, p2d
.h
);
279 aPath
.setProperty( PROP_SubViewSize
, aSubViewSize
);
283 Sequence
< PropertyValue
> aPathSequence
= aPath
.makePropertyValueSequence();
284 aPropertyMap
.setProperty( PROP_Path
, aPathSequence
);
286 Sequence
< OUString
> aEquations( maGuideList
.size() );
287 std::transform(maGuideList
.begin(), maGuideList
.end(), aEquations
.getArray(),
288 [](const auto& g
) { return g
.maFormula
; });
289 aPropertyMap
.setProperty( PROP_Equations
, aEquations
);
291 Sequence
< PropertyValues
> aHandles( maAdjustHandleList
.size() );
292 auto aHandlesRange
= asNonConstRange(aHandles
);
293 for ( std::vector
<AdjustHandle
>::size_type i
= 0; i
< maAdjustHandleList
.size(); i
++ )
296 // maAdjustmentHandle[ i ].gdRef1 ... maAdjustmentHandle[ i ].gdRef2 ... :(
297 // gdRef1 && gdRef2 -> we do not offer such reference, so it is difficult
298 // to determine the correct adjustment handle that should be updated with the adjustment
299 // position. here is the solution: the adjustment value that is used within the position
300 // has to be updated, in case the position is a formula the first usage of a
301 // adjustment value is decisive
302 if ( maAdjustHandleList
[ i
].polar
)
304 // Polar handles in DrawingML
305 // 1. don't have reference center, so PROP_Polar isn't needed.
306 // 2. position always use planar coordinates.
307 // 3. use RefAngle and RefR to specify adjustment value to be updated.
308 // 4. The unit of angular adjustment values are 6000th degree.
310 aHandle
.setProperty( PROP_Position
, maAdjustHandleList
[ i
].pos
);
311 if ( maAdjustHandleList
[ i
].gdRef1
.has_value() )
313 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef1
.value() );
315 aHandle
.setProperty( PROP_RefR
, nIndex
);
317 if ( maAdjustHandleList
[ i
].gdRef2
.has_value() )
319 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef2
.value() );
321 aHandle
.setProperty( PROP_RefAngle
, nIndex
);
323 if ( maAdjustHandleList
[ i
].min1
.has_value() )
324 aHandle
.setProperty( PROP_RadiusRangeMinimum
, maAdjustHandleList
[ i
].min1
.value());
325 if ( maAdjustHandleList
[ i
].max1
.has_value() )
326 aHandle
.setProperty( PROP_RadiusRangeMaximum
, maAdjustHandleList
[ i
].max1
.value());
328 /* TODO: AngleMin & AngleMax
329 if ( maAdjustHandleList[ i ].min2.has() )
330 aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].min2.get());
331 if ( maAdjustHandleList[ i ].max2.has() )
332 aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].max2.get());
337 aHandle
.setProperty( PROP_Position
, maAdjustHandleList
[ i
].pos
);
338 if ( maAdjustHandleList
[ i
].gdRef1
.has_value() )
340 // TODO: PROP_RefX and PROP_RefY are not yet part of our file format,
341 // so the handles will not work after save/reload
342 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef1
.value() );
344 aHandle
.setProperty( PROP_RefX
, nIndex
);
346 if ( maAdjustHandleList
[ i
].gdRef2
.has_value() )
348 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef2
.value() );
350 aHandle
.setProperty( PROP_RefY
, nIndex
);
352 if ( maAdjustHandleList
[ i
].min1
.has_value() )
353 aHandle
.setProperty( PROP_RangeXMinimum
, maAdjustHandleList
[ i
].min1
.value());
354 if ( maAdjustHandleList
[ i
].max1
.has_value() )
355 aHandle
.setProperty( PROP_RangeXMaximum
, maAdjustHandleList
[ i
].max1
.value());
356 if ( maAdjustHandleList
[ i
].min2
.has_value() )
357 aHandle
.setProperty( PROP_RangeYMinimum
, maAdjustHandleList
[ i
].min2
.value());
358 if ( maAdjustHandleList
[ i
].max2
.has_value() )
359 aHandle
.setProperty( PROP_RangeYMaximum
, maAdjustHandleList
[ i
].max2
.value());
361 aHandlesRange
[ i
] = aHandle
.makePropertyValueSequence();
363 aPropertyMap
.setProperty( PROP_Handles
, aHandles
);
366 // Note that the script oox/source/drawingml/customshapes/generatePresetsData.pl looks
367 // for these ==cscode== and ==csdata== markers, so don't "clean up" these SAL_INFOs.
368 SAL_INFO("oox.cscode", "==cscode== begin");
369 aPropertyMap
.dumpCode( aPropertyMap
.makePropertySet() );
370 SAL_INFO("oox.cscode", "==cscode== end");
371 SAL_INFO("oox.csdata", "==csdata== begin");
372 aPropertyMap
.dumpData( aPropertyMap
.makePropertySet() );
373 SAL_INFO("oox.csdata", "==csdata== end");
375 // converting the vector to a sequence
376 Sequence
< PropertyValue
> aSeq
= aPropertyMap
.makePropertyValueSequence();
377 PropertySet
aPropSet( xPropSet
);
378 aPropSet
.setProperty( PROP_CustomShapeGeometry
, aSeq
);
384 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */