1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 #include "oox/drawingml/customshapeproperties.hxx" 25 #include "oox/helper/helper.hxx" 26 #include "oox/helper/propertymap.hxx" 27 #include "oox/helper/propertyset.hxx" 28 #include <com/sun/star/awt/Rectangle.hpp> 29 #include <com/sun/star/beans/XMultiPropertySet.hpp> 30 #include <com/sun/star/lang/XMultiServiceFactory.hpp> 31 #include <com/sun/star/graphic/XGraphicTransformer.hpp> 32 #include <com/sun/star/drawing/XShape.hpp> 33 #include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp> 34 #include <basegfx/numeric/ftools.hxx> 35 36 using rtl::OUString; 37 using namespace ::oox::core; 38 using namespace ::com::sun::star; 39 using namespace ::com::sun::star::uno; 40 using namespace ::com::sun::star::beans; 41 using namespace ::com::sun::star::graphic; 42 using namespace ::com::sun::star::drawing; 43 44 namespace oox { namespace drawingml { 45 46 CustomShapeProperties::CustomShapeProperties() 47 : mbMirroredX ( sal_False ) 48 , mbMirroredY ( sal_False ) 49 , mnTextRotation(0) // #119920# Add missing extra text rotation 50 { 51 } 52 CustomShapeProperties::~CustomShapeProperties() 53 { 54 } 55 56 sal_Int32 CustomShapeProperties::SetCustomShapeGuideValue( std::vector< CustomShapeGuide >& rGuideList, const CustomShapeGuide& rGuide ) 57 { 58 sal_uInt32 nIndex = 0; 59 for( ; nIndex < rGuideList.size(); nIndex++ ) 60 { 61 if ( rGuideList[ nIndex ].maName == rGuide.maName ) 62 break; 63 } 64 if ( nIndex == rGuideList.size() ) 65 rGuideList.push_back( rGuide ); 66 return static_cast< sal_Int32 >( nIndex ); 67 } 68 69 // returns the index into the guidelist for a given formula name, 70 // if the return value is < 0 then the guide value could not be found 71 sal_Int32 CustomShapeProperties::GetCustomShapeGuideValue( const std::vector< CustomShapeGuide >& rGuideList, const rtl::OUString& rFormulaName ) 72 { 73 sal_Int32 nIndex = 0; 74 for( ; nIndex < static_cast< sal_Int32 >( rGuideList.size() ); nIndex++ ) 75 { 76 if ( rGuideList[ nIndex ].maName == rFormulaName ) 77 break; 78 } 79 if ( nIndex == static_cast< sal_Int32 >( rGuideList.size() ) ) 80 nIndex = -1; 81 return nIndex; 82 } 83 84 void CustomShapeProperties::apply( const CustomShapePropertiesPtr& /* rSourceCustomShapeProperties */ ) 85 { 86 // not sure if this needs to be implemented 87 } 88 89 bool setOrCreatePropertyValue( 90 uno::Sequence< beans::PropertyValue >& rPropSeq, 91 const OUString& rName, 92 const uno::Any& rAny) 93 { 94 const sal_Int32 nCount(rPropSeq.getLength()); 95 96 for(sal_Int32 a(0); a < nCount; a++) 97 { 98 beans::PropertyValue& rEntry = rPropSeq[a]; 99 100 if(rEntry.Name.equals(rName)) 101 { 102 rEntry.Value = rAny; 103 return false; 104 } 105 } 106 107 beans::PropertyValue aNewValue; 108 109 aNewValue.Name = rName; 110 aNewValue.Value = rAny; 111 112 rPropSeq.realloc(nCount + 1); 113 rPropSeq[nCount] = aNewValue; 114 115 return true; 116 } 117 118 void CustomShapeProperties::pushToPropSet( const ::oox::core::FilterBase& /* rFilterBase */, 119 const Reference < XPropertySet >& xPropSet, const Reference < XShape > & xShape ) const 120 { 121 if ( maShapePresetType.getLength() ) 122 { 123 //const uno::Reference < drawing::XShape > xShape( xPropSet, UNO_QUERY ); 124 Reference< drawing::XEnhancedCustomShapeDefaulter > xDefaulter( xShape, UNO_QUERY ); 125 const OUString sCustomShapeGeometry( RTL_CONSTASCII_USTRINGPARAM( "CustomShapeGeometry" ) ); 126 uno::Sequence< beans::PropertyValue > aGeoPropSeq; 127 uno::Any aGeoPropSet; 128 bool bValuesAdded(false); 129 130 if( xDefaulter.is() ) 131 { 132 xDefaulter->createCustomShapeDefaults( maShapePresetType ); 133 } 134 135 if(mbMirroredX || mbMirroredY || mnTextRotation) 136 { 137 // #121371# set these values, but do *not* set a completely new 138 // "CustomShapeGeometry", this would reset the evtl. already created 139 // "Type" entry 140 aGeoPropSet = xPropSet->getPropertyValue(sCustomShapeGeometry); 141 142 if(aGeoPropSet >>= aGeoPropSeq) 143 { 144 uno::Any aAny; 145 146 if(mbMirroredX) // TTTT: remove again after aw080, make it part of object transformation 147 { 148 const rtl::OUString sMirroredX(RTL_CONSTASCII_USTRINGPARAM("MirroredX")); 149 aAny <<= mbMirroredX; 150 bValuesAdded = setOrCreatePropertyValue(aGeoPropSeq, sMirroredX, aAny); 151 } 152 153 if(mbMirroredY) // TTTT: remove again after aw080, make it part of object transformation 154 { 155 const rtl::OUString sMirroredY(RTL_CONSTASCII_USTRINGPARAM("MirroredY")); 156 aAny <<= mbMirroredY; 157 bValuesAdded = setOrCreatePropertyValue(aGeoPropSeq, sMirroredY, aAny); 158 } 159 160 if(mnTextRotation) 161 { 162 const rtl::OUString sTextRotateAngle(RTL_CONSTASCII_USTRINGPARAM("TextRotateAngle")); 163 aAny <<= (double)mnTextRotation; 164 bValuesAdded = setOrCreatePropertyValue(aGeoPropSeq, sTextRotateAngle, aAny); 165 } 166 } 167 } 168 169 if ( maAdjustmentGuideList.size() ) 170 { 171 if(!aGeoPropSeq.getLength()) 172 { 173 aGeoPropSet = xPropSet->getPropertyValue( sCustomShapeGeometry ); 174 aGeoPropSet >>= aGeoPropSeq; 175 } 176 177 sal_Int32 i, nCount = aGeoPropSeq.getLength(); 178 179 for ( i = 0; i < nCount; i++ ) 180 { 181 const rtl::OUString sAdjustmentValues( RTL_CONSTASCII_USTRINGPARAM( "AdjustmentValues" ) ); 182 const OUString sType = CREATE_OUSTRING( "Type" ); 183 184 if ( aGeoPropSeq[ i ].Name.equals( sAdjustmentValues ) ) 185 { 186 uno::Sequence< com::sun::star::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq; 187 if ( aGeoPropSeq[ i ].Value >>= aAdjustmentSeq ) 188 { 189 std::vector< CustomShapeGuide >::const_iterator aIter( maAdjustmentGuideList.begin() ); 190 while( aIter != maAdjustmentGuideList.end() ) 191 { 192 if ( (*aIter).maName.getLength() > 3 ) 193 { 194 sal_Int32 nAdjustmentIndex = (*aIter).maName.copy( 3 ).toInt32() - 1; 195 if ( ( nAdjustmentIndex >= 0 ) && ( nAdjustmentIndex < aAdjustmentSeq.getLength() ) ) 196 { 197 EnhancedCustomShapeAdjustmentValue aAdjustmentVal; 198 199 sal_Int32 nValue((*aIter).maFormula.toInt32()); 200 201 // #124703# The ms control point coordinates are relative to the 202 // object center in the range [-50000 .. 50000] while our customshapes 203 // use a range from [0 .. 21600], so adapt the value as needed 204 nValue = basegfx::fround((double(nValue) + 50000.0) * (21600.0 / 100000.0)); 205 206 aAdjustmentVal.Value <<= nValue; 207 aAdjustmentVal.State = PropertyState_DIRECT_VALUE; 208 aAdjustmentSeq[ nAdjustmentIndex ] = aAdjustmentVal; 209 } 210 } 211 aIter++; 212 } 213 aGeoPropSeq[ i ].Value <<= aAdjustmentSeq; 214 xPropSet->setPropertyValue( sCustomShapeGeometry, Any( aGeoPropSeq ) ); 215 } 216 } 217 else if ( aGeoPropSeq[ i ].Name.equals( sType ) ) 218 { 219 aGeoPropSeq[ i ].Value <<= maShapePresetType; 220 } 221 } 222 } 223 224 if(bValuesAdded) 225 { 226 aGeoPropSet <<= aGeoPropSeq; 227 xPropSet->setPropertyValue(sCustomShapeGeometry, aGeoPropSet); 228 } 229 } 230 else 231 { 232 sal_uInt32 i; 233 PropertyMap aPropertyMap; 234 aPropertyMap[ PROP_Type ] <<= CREATE_OUSTRING( "non-primitive" ); 235 aPropertyMap[ PROP_MirroredX ] <<= Any( mbMirroredX ); 236 aPropertyMap[ PROP_MirroredY ] <<= Any( mbMirroredY ); 237 238 if(mnTextRotation) 239 { 240 aPropertyMap[ PROP_TextRotation ] <<= Any(mnTextRotation); 241 } 242 243 awt::Size aSize( xShape->getSize() ); 244 awt::Rectangle aViewBox( 0, 0, aSize.Width * 360, aSize.Height * 360 ); 245 if ( maPath2DList.size() ) 246 { // TODO: each polygon may have its own size, but I think it is rather been used 247 // so we are only taking care of the first 248 if ( maPath2DList[ 0 ].w ) 249 aViewBox.Width = static_cast< sal_Int32 >( maPath2DList[ 0 ].w ); 250 if ( maPath2DList[ 0 ].h ) 251 aViewBox.Height = static_cast< sal_Int32 >( maPath2DList[ 0 ].h ); 252 } 253 aPropertyMap[ PROP_ViewBox ] <<= aViewBox; 254 255 Sequence< EnhancedCustomShapeAdjustmentValue > aAdjustmentValues( maAdjustmentGuideList.size() ); 256 for ( i = 0; i < maAdjustmentGuideList.size(); i++ ) 257 { 258 EnhancedCustomShapeAdjustmentValue aAdjustmentVal; 259 aAdjustmentVal.Value <<= maAdjustmentGuideList[ i ].maFormula.toInt32(); 260 aAdjustmentVal.State = PropertyState_DIRECT_VALUE; 261 aAdjustmentValues[ i ] = aAdjustmentVal; 262 } 263 aPropertyMap[ PROP_AdjustmentValues ] <<= aAdjustmentValues; 264 265 Sequence< rtl::OUString > aEquations( maGuideList.size() ); 266 for ( i = 0; i < maGuideList.size(); i++ ) 267 aEquations[ i ] = maGuideList[ i ].maFormula; 268 aPropertyMap[ PROP_Equations ] <<= aEquations; 269 270 PropertyMap aPath; 271 Sequence< EnhancedCustomShapeSegment > aSegments( maSegments.size() ); 272 for ( i = 0; i < maSegments.size(); i++ ) 273 aSegments[ i ] = maSegments[ i ]; 274 aPath[ PROP_Segments ] <<= aSegments; 275 sal_uInt32 j, k, nParameterPairs = 0; 276 for ( i = 0; i < maPath2DList.size(); i++ ) 277 nParameterPairs += maPath2DList[ i ].parameter.size(); 278 Sequence< EnhancedCustomShapeParameterPair > aParameterPairs( nParameterPairs ); 279 for ( i = 0, k = 0; i < maPath2DList.size(); i++ ) 280 for ( j = 0; j < maPath2DList[ i ].parameter.size(); j++ ) 281 aParameterPairs[ k++ ] = maPath2DList[ i ].parameter[ j ]; 282 aPath[ PROP_Coordinates ] <<= aParameterPairs; 283 Sequence< PropertyValue > aPathSequence = aPath.makePropertyValueSequence(); 284 aPropertyMap[ PROP_Path ] <<= aPathSequence; 285 286 Sequence< PropertyValues > aHandles( maAdjustHandleList.size() ); 287 for ( i = 0; i < maAdjustHandleList.size(); i++ ) 288 { 289 PropertyMap aHandle; 290 // maAdjustmentHandle[ i ].gdRef1 ... maAdjustmentHandle[ i ].gdRef2 ... :( 291 // gdRef1 && gdRef2 -> we do not offer such reference, so it is difficult 292 // to determine the correct adjustment handle that should be updated with the adjustment 293 // position. here is the solution: the adjustment value that is used within the position 294 // has to be updated, in case the position is a formula the first usage of a 295 // adjument value is decisive 296 if ( maAdjustHandleList[ i ].polar ) 297 { 298 aHandle[ PROP_Position ] <<= maAdjustHandleList[ i ].pos; 299 if ( maAdjustHandleList[ i ].min1.has() ) 300 aHandle[ PROP_RadiusRangeMinimum ] <<= maAdjustHandleList[ i ].min1.get(); 301 if ( maAdjustHandleList[ i ].max1.has() ) 302 aHandle[ PROP_RadiusRangeMaximum ] <<= maAdjustHandleList[ i ].max1.get(); 303 304 /* TODO: AngleMin & AngleMax 305 if ( maAdjustHandleList[ i ].min2.has() ) 306 aHandle[ PROP_ ] = maAdjustHandleList[ i ].min2.get(); 307 if ( maAdjustHandleList[ i ].max2.has() ) 308 aHandle[ PROP_ ] = maAdjustHandleList[ i ].max2.get(); 309 */ 310 } 311 else 312 { 313 aHandle[ PROP_Position ] <<= maAdjustHandleList[ i ].pos; 314 if ( maAdjustHandleList[ i ].gdRef1.has() ) 315 { 316 // TODO: PROP_RefX and PROP_RefY are not yet part of our file format, 317 // so the handles will not work after save/reload 318 sal_Int32 nIndex = GetCustomShapeGuideValue( maAdjustmentGuideList, maAdjustHandleList[ i ].gdRef1.get() ); 319 if ( nIndex >= 0 ) 320 aHandle[ PROP_RefX ] <<= nIndex; 321 } 322 if ( maAdjustHandleList[ i ].gdRef2.has() ) 323 { 324 sal_Int32 nIndex = GetCustomShapeGuideValue( maAdjustmentGuideList, maAdjustHandleList[ i ].gdRef2.get() ); 325 if ( nIndex >= 0 ) 326 aHandle[ PROP_RefY ] <<= nIndex; 327 } 328 if ( maAdjustHandleList[ i ].min1.has() ) 329 aHandle[ PROP_RangeXMinimum ] <<= maAdjustHandleList[ i ].min1.get(); 330 if ( maAdjustHandleList[ i ].max1.has() ) 331 aHandle[ PROP_RangeXMaximum ] <<= maAdjustHandleList[ i ].max1.get(); 332 if ( maAdjustHandleList[ i ].min2.has() ) 333 aHandle[ PROP_RangeYMinimum ] <<= maAdjustHandleList[ i ].min2.get(); 334 if ( maAdjustHandleList[ i ].max2.has() ) 335 aHandle[ PROP_RangeYMaximum ] <<= maAdjustHandleList[ i ].max2.get(); 336 } 337 aHandles[ i ] = aHandle.makePropertyValueSequence(); 338 } 339 aPropertyMap[ PROP_Handles ] <<= aHandles; 340 341 // converting the vector to a sequence 342 Sequence< PropertyValue > aSeq = aPropertyMap.makePropertyValueSequence(); 343 PropertySet aPropSet( xPropSet ); 344 aPropSet.setProperty( PROP_CustomShapeGeometry, aSeq ); 345 } 346 } 347 348 double CustomShapeProperties::getValue( const std::vector< CustomShapeGuide >& rGuideList, sal_uInt32 nIndex ) const 349 { 350 double fRet = 0.0; 351 if ( nIndex < rGuideList.size() ) 352 { 353 354 } 355 return fRet; 356 } 357 358 } } 359