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 <sal/log.hxx>
36 using namespace ::com::sun::star
;
37 using namespace ::com::sun::star::uno
;
38 using namespace ::com::sun::star::beans
;
39 using namespace ::com::sun::star::drawing
;
41 namespace oox
{ namespace drawingml
{
43 CustomShapeProperties::CustomShapeProperties()
44 : mnShapePresetType ( -1 )
45 , mbShapeTypeOverride(false)
46 , mbMirroredX ( false )
47 , mbMirroredY ( false )
48 , mnTextRotateAngle ( 0 )
49 , mnTextCameraZRotateAngle ( 0 )
53 CustomShapeProperties::~CustomShapeProperties()
57 uno::Sequence
< sal_Int8
> const & CustomShapeProperties::getShapePresetTypeName() const
59 return StaticTokenMap::get().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
, const OUString
& 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 CustomShapeProperties::PresetDataMap
CustomShapeProperties::maPresetDataMap
;
94 static OUString
GetConnectorShapeType( sal_Int32 nType
)
97 "oox.drawingml", "preset: " << nType
<< " " << XML_straightConnector1
);
102 case XML_straightConnector1
:
111 void CustomShapeProperties::pushToPropSet(
112 const Reference
< XPropertySet
>& xPropSet
, const Reference
< XShape
> & xShape
, const awt::Size
&aSize
)
114 if ( mnShapePresetType
>= 0 )
116 SAL_INFO("oox.drawingml", "preset: " << mnShapePresetType
);
118 if (maPresetDataMap
.empty())
119 initializePresetDataMap();
121 PropertyMap aPropertyMap
;
122 PropertySet
aPropSet( xPropSet
);
124 OUString sConnectorShapeType
= GetConnectorShapeType( mnShapePresetType
);
126 if (sConnectorShapeType
.getLength() > 0)
130 "connector shape: " << sConnectorShapeType
<< " ("
131 << mnShapePresetType
<< ")");
132 //const uno::Reference < drawing::XShape > xShape( xPropSet, UNO_QUERY );
133 Reference
< drawing::XEnhancedCustomShapeDefaulter
> xDefaulter( xShape
, UNO_QUERY
);
134 if( xDefaulter
.is() ) {
135 xDefaulter
->createCustomShapeDefaults( sConnectorShapeType
);
136 aPropertyMap
.setProperty( PROP_Type
, sConnectorShapeType
);
139 else if (maPresetDataMap
.find(mnShapePresetType
) != maPresetDataMap
.end())
143 "found property map for preset: " << mnShapePresetType
);
145 aPropertyMap
= maPresetDataMap
[mnShapePresetType
];
147 aPropertyMap
.dumpCode( aPropertyMap
.makePropertySet() );
151 aPropertyMap
.setProperty( PROP_MirroredX
, mbMirroredX
);
152 aPropertyMap
.setProperty( PROP_MirroredY
, mbMirroredY
);
153 aPropertyMap
.setProperty( PROP_TextPreRotateAngle
, mnTextRotateAngle
);
154 aPropertyMap
.setProperty( PROP_TextCameraZRotateAngle
, mnTextCameraZRotateAngle
);
155 Sequence
< PropertyValue
> aSeq
= aPropertyMap
.makePropertyValueSequence();
156 aPropSet
.setProperty( PROP_CustomShapeGeometry
, aSeq
);
158 const OUString
sCustomShapeGeometry("CustomShapeGeometry");
159 const OUString
sAdjustmentValues("AdjustmentValues");
160 uno::Any aGeoPropSet
= xPropSet
->getPropertyValue( sCustomShapeGeometry
);
161 uno::Sequence
< beans::PropertyValue
> aGeoPropSeq
;
163 if (aGeoPropSet
>>= aGeoPropSeq
)
165 for ( const auto& rGeoProp
: std::as_const(aGeoPropSeq
) )
167 if ( rGeoProp
.Name
== sAdjustmentValues
)
169 OUString presetTextWarp
;
170 if ( rGeoProp
.Value
>>= presetTextWarp
)
172 aPropertyMap
.setProperty( PROP_PresetTextWarp
, presetTextWarp
);
178 if ( !maAdjustmentGuideList
.empty() )
180 const OUString sType
= "Type";
181 if ( aGeoPropSet
>>= aGeoPropSeq
)
183 for ( auto& rGeoProp
: aGeoPropSeq
)
185 if ( rGeoProp
.Name
== sAdjustmentValues
)
187 uno::Sequence
< css::drawing::EnhancedCustomShapeAdjustmentValue
> aAdjustmentSeq
;
188 if ( rGeoProp
.Value
>>= aAdjustmentSeq
)
191 for (auto const& adjustmentGuide
: maAdjustmentGuideList
)
193 if ( adjustmentGuide
.maName
.getLength() > 3 )
195 sal_Int32 nAdjustmentIndex
= adjustmentGuide
.maName
.copy( 3 ).toInt32() - 1;
196 if ( ( nAdjustmentIndex
>= 0 ) && ( nAdjustmentIndex
< aAdjustmentSeq
.getLength() ) )
198 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
199 aAdjustmentVal
.Value
<<= adjustmentGuide
.maFormula
.toInt32();
200 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
201 aAdjustmentVal
.Name
= adjustmentGuide
.maName
;
202 aAdjustmentSeq
[ nAdjustmentIndex
] = aAdjustmentVal
;
204 } else if ( aAdjustmentSeq
.hasElements() ) {
205 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
206 aAdjustmentVal
.Value
<<= adjustmentGuide
.maFormula
.toInt32();
207 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
208 aAdjustmentVal
.Name
= adjustmentGuide
.maName
;
209 aAdjustmentSeq
[ nIndex
++ ] = aAdjustmentVal
;
212 rGeoProp
.Value
<<= aAdjustmentSeq
;
213 xPropSet
->setPropertyValue( sCustomShapeGeometry
, Any( aGeoPropSeq
) );
216 else if ( rGeoProp
.Name
== sType
)
218 if ( sConnectorShapeType
.getLength() > 0 )
219 rGeoProp
.Value
<<= sConnectorShapeType
;
221 rGeoProp
.Value
<<= OUString( "ooxml-CustomShape" );
229 PropertyMap aPropertyMap
;
230 aPropertyMap
.setProperty( PROP_Type
, OUString( "ooxml-non-primitive" ));
231 aPropertyMap
.setProperty( PROP_MirroredX
, mbMirroredX
);
232 aPropertyMap
.setProperty( PROP_MirroredY
, mbMirroredY
);
233 // Note 1: If Equations are defined - they are processed using internal div by 360 coordinates
234 // while if they are not, standard ooxml coordinates are used.
235 // This size specifically affects scaling.
236 // Note 2: Width and Height are set to 0 to force scaling to 1.
237 awt::Rectangle
aViewBox( 0, 0, aSize
.Width
, aSize
.Height
);
238 if( !maGuideList
.empty() )
239 aViewBox
= awt::Rectangle( 0, 0, 0, 0 );
240 aPropertyMap
.setProperty( PROP_ViewBox
, aViewBox
);
242 Sequence
< EnhancedCustomShapeAdjustmentValue
> aAdjustmentValues( maAdjustmentGuideList
.size() );
243 for ( std::vector
<CustomShapeGuide
>::size_type i
= 0; i
< maAdjustmentGuideList
.size(); i
++ )
245 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
246 aAdjustmentVal
.Value
<<= maAdjustmentGuideList
[ i
].maFormula
.toInt32();
247 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
248 aAdjustmentVal
.Name
= maAdjustmentGuideList
[ i
].maName
;
249 aAdjustmentValues
[ i
] = aAdjustmentVal
;
251 aPropertyMap
.setProperty( PROP_AdjustmentValues
, aAdjustmentValues
);
255 aPath
.setProperty( PROP_Segments
, comphelper::containerToSequence(maSegments
) );
257 if ( maTextRect
.has() ) {
258 Sequence
< EnhancedCustomShapeTextFrame
> aTextFrames(1);
259 aTextFrames
[0].TopLeft
.First
= maTextRect
.get().l
;
260 aTextFrames
[0].TopLeft
.Second
= maTextRect
.get().t
;
261 aTextFrames
[0].BottomRight
.First
= maTextRect
.get().r
;
262 aTextFrames
[0].BottomRight
.Second
= maTextRect
.get().b
;
263 aPath
.setProperty( PROP_TextFrames
, aTextFrames
);
266 sal_uInt32 nParameterPairs
= 0;
267 for ( auto const & i
: maPath2DList
)
268 nParameterPairs
+= i
.parameter
.size();
270 Sequence
< EnhancedCustomShapeParameterPair
> aParameterPairs( nParameterPairs
);
272 for ( auto const & i
: maPath2DList
)
273 for ( auto const & j
: i
.parameter
)
274 aParameterPairs
[ k
++ ] = j
;
275 aPath
.setProperty( PROP_Coordinates
, aParameterPairs
);
277 if ( !maPath2DList
.empty() )
279 bool bAllZero
= true;
280 for ( auto const & i
: maPath2DList
)
289 Sequence
< awt::Size
> aSubViewSize( maPath2DList
.size() );
290 for ( std::vector
<Path2D
>::size_type i
=0; i
< maPath2DList
.size(); i
++ )
292 aSubViewSize
[i
].Width
= static_cast< sal_Int32
>( maPath2DList
[i
].w
);
293 aSubViewSize
[i
].Height
= static_cast< sal_Int32
>( maPath2DList
[i
].h
);
296 "set subpath " << i
<< " size: " << maPath2DList
[i
].w
297 << " x " << maPath2DList
[i
].h
);
299 aPath
.setProperty( PROP_SubViewSize
, aSubViewSize
);
303 Sequence
< PropertyValue
> aPathSequence
= aPath
.makePropertyValueSequence();
304 aPropertyMap
.setProperty( PROP_Path
, aPathSequence
);
306 Sequence
< OUString
> aEquations( maGuideList
.size() );
307 for ( std::vector
<CustomShapeGuide
>::size_type i
= 0; i
< maGuideList
.size(); i
++ )
308 aEquations
[ i
] = maGuideList
[ i
].maFormula
;
309 aPropertyMap
.setProperty( PROP_Equations
, aEquations
);
311 Sequence
< PropertyValues
> aHandles( maAdjustHandleList
.size() );
312 for ( std::vector
<AdjustHandle
>::size_type i
= 0; i
< maAdjustHandleList
.size(); i
++ )
315 // maAdjustmentHandle[ i ].gdRef1 ... maAdjustmentHandle[ i ].gdRef2 ... :(
316 // gdRef1 && gdRef2 -> we do not offer such reference, so it is difficult
317 // to determine the correct adjustment handle that should be updated with the adjustment
318 // position. here is the solution: the adjustment value that is used within the position
319 // has to be updated, in case the position is a formula the first usage of a
320 // adjustment value is decisive
321 if ( maAdjustHandleList
[ i
].polar
)
323 // Polar handles in DrawingML
324 // 1. don't have reference center, so PROP_Polar isn't needed.
325 // 2. position always use planar coordinates.
326 // 3. use RefAngle and RefR to specify adjustment value to be updated.
327 // 4. The unit of angular adjustment values are 6000th degree.
329 aHandle
.setProperty( PROP_Position
, maAdjustHandleList
[ i
].pos
);
330 if ( maAdjustHandleList
[ i
].gdRef1
.has() )
332 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef1
.get() );
334 aHandle
.setProperty( PROP_RefR
, nIndex
);
336 if ( maAdjustHandleList
[ i
].gdRef2
.has() )
338 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef2
.get() );
340 aHandle
.setProperty( PROP_RefAngle
, nIndex
);
342 if ( maAdjustHandleList
[ i
].min1
.has() )
343 aHandle
.setProperty( PROP_RadiusRangeMinimum
, maAdjustHandleList
[ i
].min1
.get());
344 if ( maAdjustHandleList
[ i
].max1
.has() )
345 aHandle
.setProperty( PROP_RadiusRangeMaximum
, maAdjustHandleList
[ i
].max1
.get());
347 /* TODO: AngleMin & AngleMax
348 if ( maAdjustHandleList[ i ].min2.has() )
349 aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].min2.get());
350 if ( maAdjustHandleList[ i ].max2.has() )
351 aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].max2.get());
356 aHandle
.setProperty( PROP_Position
, maAdjustHandleList
[ i
].pos
);
357 if ( maAdjustHandleList
[ i
].gdRef1
.has() )
359 // TODO: PROP_RefX and PROP_RefY are not yet part of our file format,
360 // so the handles will not work after save/reload
361 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef1
.get() );
363 aHandle
.setProperty( PROP_RefX
, nIndex
);
365 if ( maAdjustHandleList
[ i
].gdRef2
.has() )
367 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef2
.get() );
369 aHandle
.setProperty( PROP_RefY
, nIndex
);
371 if ( maAdjustHandleList
[ i
].min1
.has() )
372 aHandle
.setProperty( PROP_RangeXMinimum
, maAdjustHandleList
[ i
].min1
.get());
373 if ( maAdjustHandleList
[ i
].max1
.has() )
374 aHandle
.setProperty( PROP_RangeXMaximum
, maAdjustHandleList
[ i
].max1
.get());
375 if ( maAdjustHandleList
[ i
].min2
.has() )
376 aHandle
.setProperty( PROP_RangeYMinimum
, maAdjustHandleList
[ i
].min2
.get());
377 if ( maAdjustHandleList
[ i
].max2
.has() )
378 aHandle
.setProperty( PROP_RangeYMaximum
, maAdjustHandleList
[ i
].max2
.get());
380 aHandles
[ i
] = aHandle
.makePropertyValueSequence();
382 aPropertyMap
.setProperty( PROP_Handles
, aHandles
);
385 // Note that the script oox/source/drawingml/customshapes/generatePresetsData.pl looks
386 // for these ==cscode== and ==csdata== markers, so don't "clean up" these SAL_INFOs.
387 SAL_INFO("oox.cscode", "==cscode== begin");
388 aPropertyMap
.dumpCode( aPropertyMap
.makePropertySet() );
389 SAL_INFO("oox.cscode", "==cscode== end");
390 SAL_INFO("oox.csdata", "==csdata== begin");
391 aPropertyMap
.dumpData( aPropertyMap
.makePropertySet() );
392 SAL_INFO("oox.csdata", "==csdata== end");
394 // converting the vector to a sequence
395 Sequence
< PropertyValue
> aSeq
= aPropertyMap
.makePropertyValueSequence();
396 PropertySet
aPropSet( xPropSet
);
397 aPropSet
.setProperty( PROP_CustomShapeGeometry
, aSeq
);
403 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */