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>
35 using namespace ::com::sun::star
;
36 using namespace ::com::sun::star::uno
;
37 using namespace ::com::sun::star::beans
;
38 using namespace ::com::sun::star::drawing
;
40 namespace oox
{ namespace drawingml
{
42 CustomShapeProperties::CustomShapeProperties()
43 : mnShapePresetType ( -1 )
44 , mbShapeTypeOverride(false)
45 , mbMirroredX ( false )
46 , mbMirroredY ( false )
47 , mnTextRotateAngle ( 0 )
51 CustomShapeProperties::~CustomShapeProperties()
55 uno::Sequence
< sal_Int8
> CustomShapeProperties::getShapePresetTypeName() const
57 return StaticTokenMap::get().getUtf8TokenName( mnShapePresetType
);
60 sal_Int32
CustomShapeProperties::SetCustomShapeGuideValue( std::vector
< CustomShapeGuide
>& rGuideList
, const CustomShapeGuide
& rGuide
)
62 std::vector
<CustomShapeGuide
>::size_type nIndex
= 0;
63 for( ; nIndex
< rGuideList
.size(); nIndex
++ )
65 if ( rGuideList
[ nIndex
].maName
== rGuide
.maName
)
68 if ( nIndex
== rGuideList
.size() )
69 rGuideList
.push_back( rGuide
);
70 return static_cast< sal_Int32
>( nIndex
);
73 // returns the index into the guidelist for a given formula name,
74 // if the return value is < 0 then the guide value could not be found
75 sal_Int32
CustomShapeProperties::GetCustomShapeGuideValue( const std::vector
< CustomShapeGuide
>& rGuideList
, const OUString
& rFormulaName
)
77 // traverse the list from the end, because guide names can be reused
78 // and current is the last one
79 // see a1 guide in gear6 custom shape preset as example
80 sal_Int32 nIndex
= static_cast< sal_Int32
>( rGuideList
.size() ) - 1;
81 for( ; nIndex
>= 0; nIndex
-- )
83 if ( rGuideList
[ nIndex
].maName
== rFormulaName
)
90 CustomShapeProperties::PresetDataMap
CustomShapeProperties::maPresetDataMap
;
92 static OUString
GetConnectorShapeType( sal_Int32 nType
)
95 "oox.drawingml", "preset: " << nType
<< " " << XML_straightConnector1
);
100 case XML_straightConnector1
:
109 void CustomShapeProperties::pushToPropSet(
110 const Reference
< XPropertySet
>& xPropSet
, const Reference
< XShape
> & xShape
, const awt::Size
&aSize
)
112 if ( mnShapePresetType
>= 0 )
114 SAL_INFO("oox.drawingml", "preset: " << mnShapePresetType
);
116 if (maPresetDataMap
.empty())
117 initializePresetDataMap();
119 PropertyMap aPropertyMap
;
120 PropertySet
aPropSet( xPropSet
);
122 OUString sConnectorShapeType
= GetConnectorShapeType( mnShapePresetType
);
124 if (sConnectorShapeType
.getLength() > 0)
128 "connector shape: " << sConnectorShapeType
<< " ("
129 << mnShapePresetType
<< ")");
130 //const uno::Reference < drawing::XShape > xShape( xPropSet, UNO_QUERY );
131 Reference
< drawing::XEnhancedCustomShapeDefaulter
> xDefaulter( xShape
, UNO_QUERY
);
132 if( xDefaulter
.is() ) {
133 xDefaulter
->createCustomShapeDefaults( sConnectorShapeType
);
134 aPropertyMap
.setProperty( PROP_Type
, sConnectorShapeType
);
137 else if (maPresetDataMap
.find(mnShapePresetType
) != maPresetDataMap
.end())
141 "found property map for preset: " << mnShapePresetType
);
143 aPropertyMap
= maPresetDataMap
[mnShapePresetType
];
145 aPropertyMap
.dumpCode( aPropertyMap
.makePropertySet() );
149 aPropertyMap
.setProperty( PROP_MirroredX
, mbMirroredX
);
150 aPropertyMap
.setProperty( PROP_MirroredY
, mbMirroredY
);
151 aPropertyMap
.setProperty( PROP_TextPreRotateAngle
, mnTextRotateAngle
);
152 aPropertyMap
.setProperty( PROP_IsPostRotateAngle
, true); // For OpenXML Imports
153 Sequence
< PropertyValue
> aSeq
= aPropertyMap
.makePropertyValueSequence();
154 aPropSet
.setProperty( PROP_CustomShapeGeometry
, aSeq
);
156 const OUString
sCustomShapeGeometry("CustomShapeGeometry");
157 uno::Any aGeoPropSet
= xPropSet
->getPropertyValue( sCustomShapeGeometry
);
158 uno::Sequence
< beans::PropertyValue
> aGeoPropSeq
;
160 sal_Int32 i
, nCount
= 0;
161 if (aGeoPropSet
>>= aGeoPropSeq
)
163 nCount
= aGeoPropSeq
.getLength();
164 for ( i
= 0; i
< nCount
; i
++ )
166 const OUString
sAdjustmentValues("AdjustmentValues");
167 if ( aGeoPropSeq
[ i
].Name
.equals( sAdjustmentValues
) )
169 OUString presetTextWarp
;
170 if ( aGeoPropSeq
[ i
].Value
>>= presetTextWarp
)
172 aPropertyMap
.setProperty( PROP_PresetTextWarp
, presetTextWarp
);
178 if ( maAdjustmentGuideList
.size() )
180 const OUString sType
= "Type";
181 if ( aGeoPropSet
>>= aGeoPropSeq
)
183 nCount
= aGeoPropSeq
.getLength();
184 for ( i
= 0; i
< nCount
; i
++ )
186 const OUString
sAdjustmentValues("AdjustmentValues");
187 if ( aGeoPropSeq
[ i
].Name
.equals( sAdjustmentValues
) )
189 uno::Sequence
< css::drawing::EnhancedCustomShapeAdjustmentValue
> aAdjustmentSeq
;
190 if ( aGeoPropSeq
[ i
].Value
>>= aAdjustmentSeq
)
193 for (std::vector
< CustomShapeGuide
>::const_iterator
aIter( maAdjustmentGuideList
.begin() ), aEnd(maAdjustmentGuideList
.end());
194 aIter
!= aEnd
; ++aIter
)
196 if ( (*aIter
).maName
.getLength() > 3 )
198 sal_Int32 nAdjustmentIndex
= (*aIter
).maName
.copy( 3 ).toInt32() - 1;
199 if ( ( nAdjustmentIndex
>= 0 ) && ( nAdjustmentIndex
< aAdjustmentSeq
.getLength() ) )
201 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
202 aAdjustmentVal
.Value
<<= (*aIter
).maFormula
.toInt32();
203 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
204 aAdjustmentVal
.Name
= (*aIter
).maName
;
205 aAdjustmentSeq
[ nAdjustmentIndex
] = aAdjustmentVal
;
207 } else if ( aAdjustmentSeq
.getLength() > 0 ) {
208 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
209 aAdjustmentVal
.Value
<<= (*aIter
).maFormula
.toInt32();
210 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
211 aAdjustmentVal
.Name
= (*aIter
).maName
;
212 aAdjustmentSeq
[ nIndex
++ ] = aAdjustmentVal
;
215 aGeoPropSeq
[ i
].Value
<<= aAdjustmentSeq
;
216 xPropSet
->setPropertyValue( sCustomShapeGeometry
, Any( aGeoPropSeq
) );
219 else if ( aGeoPropSeq
[ i
].Name
.equals( sType
) )
221 if ( sConnectorShapeType
.getLength() > 0 )
222 aGeoPropSeq
[ i
].Value
<<= sConnectorShapeType
;
224 aGeoPropSeq
[ i
].Value
<<= OUString( "ooxml-CustomShape" );
232 PropertyMap aPropertyMap
;
233 aPropertyMap
.setProperty( PROP_Type
, OUString( "ooxml-non-primitive" ));
234 aPropertyMap
.setProperty( PROP_MirroredX
, mbMirroredX
);
235 aPropertyMap
.setProperty( PROP_MirroredY
, mbMirroredY
);
236 // Note 1: If Equations are defined - they are processed using internal div by 360 coordinates
237 // while if they are not, standard ooxml coordinates are used.
238 // This size specifically affects scaling.
239 // Note 2: Width and Height are set to 0 to force scaling to 1.
240 awt::Rectangle
aViewBox( 0, 0, aSize
.Width
, aSize
.Height
);
241 if( maGuideList
.size() )
242 aViewBox
= awt::Rectangle( 0, 0, 0, 0 );
243 aPropertyMap
.setProperty( PROP_ViewBox
, aViewBox
);
245 Sequence
< EnhancedCustomShapeAdjustmentValue
> aAdjustmentValues( maAdjustmentGuideList
.size() );
246 for ( std::vector
<CustomShapeGuide
>::size_type i
= 0; i
< maAdjustmentGuideList
.size(); i
++ )
248 EnhancedCustomShapeAdjustmentValue aAdjustmentVal
;
249 aAdjustmentVal
.Value
<<= maAdjustmentGuideList
[ i
].maFormula
.toInt32();
250 aAdjustmentVal
.State
= PropertyState_DIRECT_VALUE
;
251 aAdjustmentVal
.Name
= maAdjustmentGuideList
[ i
].maName
;
252 aAdjustmentValues
[ i
] = aAdjustmentVal
;
254 aPropertyMap
.setProperty( PROP_AdjustmentValues
, aAdjustmentValues
);
258 aPath
.setProperty( PROP_Segments
, comphelper::containerToSequence(maSegments
) );
260 if ( maTextRect
.has() ) {
261 Sequence
< EnhancedCustomShapeTextFrame
> aTextFrames(1);
262 aTextFrames
[0].TopLeft
.First
= maTextRect
.get().l
;
263 aTextFrames
[0].TopLeft
.Second
= maTextRect
.get().t
;
264 aTextFrames
[0].BottomRight
.First
= maTextRect
.get().r
;
265 aTextFrames
[0].BottomRight
.Second
= maTextRect
.get().b
;
266 aPath
.setProperty( PROP_TextFrames
, aTextFrames
);
269 sal_uInt32 nParameterPairs
= 0;
270 for ( auto const & i
: maPath2DList
)
271 nParameterPairs
+= i
.parameter
.size();
273 Sequence
< EnhancedCustomShapeParameterPair
> aParameterPairs( nParameterPairs
);
275 for ( auto const & i
: maPath2DList
)
276 for ( auto const & j
: i
.parameter
)
277 aParameterPairs
[ k
++ ] = j
;
278 aPath
.setProperty( PROP_Coordinates
, aParameterPairs
);
280 if ( maPath2DList
.size() )
282 bool bAllZero
= true;
283 for ( auto const & i
: maPath2DList
)
292 Sequence
< awt::Size
> aSubViewSize( maPath2DList
.size() );
293 for ( std::vector
<Path2D
>::size_type i
=0; i
< maPath2DList
.size(); i
++ )
295 aSubViewSize
[i
].Width
= static_cast< sal_Int32
>( maPath2DList
[i
].w
);
296 aSubViewSize
[i
].Height
= static_cast< sal_Int32
>( maPath2DList
[i
].h
);
299 "set subpath " << i
<< " size: " << maPath2DList
[i
].w
300 << " x " << maPath2DList
[i
].h
);
302 aPath
.setProperty( PROP_SubViewSize
, aSubViewSize
);
306 Sequence
< PropertyValue
> aPathSequence
= aPath
.makePropertyValueSequence();
307 aPropertyMap
.setProperty( PROP_Path
, aPathSequence
);
309 Sequence
< OUString
> aEquations( maGuideList
.size() );
310 for ( std::vector
<CustomShapeGuide
>::size_type i
= 0; i
< maGuideList
.size(); i
++ )
311 aEquations
[ i
] = maGuideList
[ i
].maFormula
;
312 aPropertyMap
.setProperty( PROP_Equations
, aEquations
);
314 Sequence
< PropertyValues
> aHandles( maAdjustHandleList
.size() );
315 for ( std::vector
<AdjustHandle
>::size_type i
= 0; i
< maAdjustHandleList
.size(); i
++ )
318 // maAdjustmentHandle[ i ].gdRef1 ... maAdjustmentHandle[ i ].gdRef2 ... :(
319 // gdRef1 && gdRef2 -> we do not offer such reference, so it is difficult
320 // to determine the correct adjustment handle that should be updated with the adjustment
321 // position. here is the solution: the adjustment value that is used within the position
322 // has to be updated, in case the position is a formula the first usage of a
323 // adjustment value is decisive
324 if ( maAdjustHandleList
[ i
].polar
)
326 // Polar handles in DrawingML
327 // 1. don't have reference center, so PROP_Polar isn't needed.
328 // 2. position always use planar coordinates.
329 // 3. use RefAngle and RefR to specify adjustment value to be updated.
330 // 4. The unit of angular adjustment values are 6000th degree.
332 aHandle
.setProperty( PROP_Position
, maAdjustHandleList
[ i
].pos
);
333 if ( maAdjustHandleList
[ i
].gdRef1
.has() )
335 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef1
.get() );
337 aHandle
.setProperty( PROP_RefR
, nIndex
);
339 if ( maAdjustHandleList
[ i
].gdRef2
.has() )
341 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef2
.get() );
343 aHandle
.setProperty( PROP_RefAngle
, nIndex
);
345 if ( maAdjustHandleList
[ i
].min1
.has() )
346 aHandle
.setProperty( PROP_RadiusRangeMinimum
, maAdjustHandleList
[ i
].min1
.get());
347 if ( maAdjustHandleList
[ i
].max1
.has() )
348 aHandle
.setProperty( PROP_RadiusRangeMaximum
, maAdjustHandleList
[ i
].max1
.get());
350 /* TODO: AngleMin & AngleMax
351 if ( maAdjustHandleList[ i ].min2.has() )
352 aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].min2.get());
353 if ( maAdjustHandleList[ i ].max2.has() )
354 aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].max2.get());
359 aHandle
.setProperty( PROP_Position
, maAdjustHandleList
[ i
].pos
);
360 if ( maAdjustHandleList
[ i
].gdRef1
.has() )
362 // TODO: PROP_RefX and PROP_RefY are not yet part of our file format,
363 // so the handles will not work after save/reload
364 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef1
.get() );
366 aHandle
.setProperty( PROP_RefX
, nIndex
);
368 if ( maAdjustHandleList
[ i
].gdRef2
.has() )
370 sal_Int32 nIndex
= GetCustomShapeGuideValue( maAdjustmentGuideList
, maAdjustHandleList
[ i
].gdRef2
.get() );
372 aHandle
.setProperty( PROP_RefY
, nIndex
);
374 if ( maAdjustHandleList
[ i
].min1
.has() )
375 aHandle
.setProperty( PROP_RangeXMinimum
, maAdjustHandleList
[ i
].min1
.get());
376 if ( maAdjustHandleList
[ i
].max1
.has() )
377 aHandle
.setProperty( PROP_RangeXMaximum
, maAdjustHandleList
[ i
].max1
.get());
378 if ( maAdjustHandleList
[ i
].min2
.has() )
379 aHandle
.setProperty( PROP_RangeYMinimum
, maAdjustHandleList
[ i
].min2
.get());
380 if ( maAdjustHandleList
[ i
].max2
.has() )
381 aHandle
.setProperty( PROP_RangeYMaximum
, maAdjustHandleList
[ i
].max2
.get());
383 aHandles
[ i
] = aHandle
.makePropertyValueSequence();
385 aPropertyMap
.setProperty( PROP_Handles
, aHandles
);
388 // Note that the script oox/source/drawingml/customshapes/generatePresetsData.pl looks
389 // for these ==cscode== and ==csdata== markers, so don't "clean up" these SAL_INFOs.
390 SAL_INFO("oox.cscode", "==cscode== begin");
391 aPropertyMap
.dumpCode( aPropertyMap
.makePropertySet() );
392 SAL_INFO("oox.cscode", "==cscode== end");
393 SAL_INFO("oox.csdata", "==csdata== begin");
394 aPropertyMap
.dumpData( aPropertyMap
.makePropertySet() );
395 SAL_INFO("oox.csdata", "==csdata== end");
397 // converting the vector to a sequence
398 Sequence
< PropertyValue
> aSeq
= aPropertyMap
.makePropertyValueSequence();
399 PropertySet
aPropSet( xPropSet
);
400 aPropSet
.setProperty( PROP_CustomShapeGeometry
, aSeq
);
406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */