LibreOffice Module oox (master)  1
customshapeproperties.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
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>
35 
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;
40 
41 namespace oox::drawingml {
42 
44 : mnShapePresetType ( -1 )
45 , mbShapeTypeOverride(false)
46 , mbMirroredX ( false )
47 , mbMirroredY ( false )
48 , mnTextRotateAngle ( 0 )
49 , mnTextCameraZRotateAngle ( 0 )
50 , mnArcNum ( 0 )
51 {
52 }
53 
54 uno::Sequence< sal_Int8 > const & CustomShapeProperties::getShapePresetTypeName() const
55 {
56  return StaticTokenMap::get().getUtf8TokenName( mnShapePresetType );
57 }
58 
59 sal_Int32 CustomShapeProperties::SetCustomShapeGuideValue( std::vector< CustomShapeGuide >& rGuideList, const CustomShapeGuide& rGuide )
60 {
61  std::vector<CustomShapeGuide>::size_type nIndex = 0;
62  for( ; nIndex < rGuideList.size(); nIndex++ )
63  {
64  if ( rGuideList[ nIndex ].maName == rGuide.maName )
65  break;
66  }
67  if ( nIndex == rGuideList.size() )
68  rGuideList.push_back( rGuide );
69  return static_cast< sal_Int32 >( nIndex );
70 }
71 
72 // returns the index into the guidelist for a given formula name,
73 // if the return value is < 0 then the guide value could not be found
74 sal_Int32 CustomShapeProperties::GetCustomShapeGuideValue( const std::vector< CustomShapeGuide >& rGuideList, std::u16string_view rFormulaName )
75 {
76  // traverse the list from the end, because guide names can be reused
77  // and current is the last one
78  // see a1 guide in gear6 custom shape preset as example
79  sal_Int32 nIndex = static_cast< sal_Int32 >( rGuideList.size() ) - 1;
80  for( ; nIndex >= 0; nIndex-- )
81  {
82  if ( rGuideList[ nIndex ].maName == rFormulaName )
83  break;
84  }
85 
86  return nIndex;
87 }
88 
90 
91 static OUString GetConnectorShapeType( sal_Int32 nType )
92 {
93  SAL_INFO(
94  "oox.drawingml", "preset: " << nType << " " << XML_straightConnector1);
95 
96  OUString sType;
97  switch( nType )
98  {
99  case XML_straightConnector1:
100  sType = "mso-spt32";
101  break;
102  default:
103  break;
104  }
105  return sType;
106 }
107 
109  const Reference < XPropertySet >& xPropSet, const Reference < XShape > & xShape, const awt::Size &aSize )
110 {
111  if ( mnShapePresetType >= 0 )
112  {
113  SAL_INFO("oox.drawingml", "preset: " << mnShapePresetType);
114 
115  if (maPresetDataMap.empty())
117 
118  PropertyMap aPropertyMap;
119  PropertySet aPropSet( xPropSet );
120 
121  OUString sConnectorShapeType = GetConnectorShapeType( mnShapePresetType );
122 
123  if (sConnectorShapeType.getLength() > 0)
124  {
125  SAL_INFO(
126  "oox.drawingml",
127  "connector shape: " << sConnectorShapeType << " ("
128  << mnShapePresetType << ")");
129  //const uno::Reference < drawing::XShape > xShape( xPropSet, UNO_QUERY );
130  Reference< drawing::XEnhancedCustomShapeDefaulter > xDefaulter( xShape, UNO_QUERY );
131  if( xDefaulter.is() ) {
132  xDefaulter->createCustomShapeDefaults( sConnectorShapeType );
133  aPropertyMap.setProperty( PROP_Type, sConnectorShapeType );
134  }
135  }
136  else if (maPresetDataMap.find(mnShapePresetType) != maPresetDataMap.end())
137  {
138  SAL_INFO(
139  "oox.drawingml",
140  "found property map for preset: " << mnShapePresetType);
141 
142  aPropertyMap = maPresetDataMap[mnShapePresetType];
143 #ifdef DEBUG
144  aPropertyMap.dumpCode( aPropertyMap.makePropertySet() );
145 #endif
146  }
147 
148  aPropertyMap.setProperty( PROP_MirroredX, mbMirroredX );
149  aPropertyMap.setProperty( PROP_MirroredY, mbMirroredY );
150  aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextRotateAngle );
151  aPropertyMap.setProperty( PROP_TextCameraZRotateAngle, mnTextCameraZRotateAngle );
153  aPropSet.setProperty( PROP_CustomShapeGeometry, aSeq );
154 
155  static const OUStringLiteral sCustomShapeGeometry(u"CustomShapeGeometry");
156  static const OUStringLiteral sAdjustmentValues(u"AdjustmentValues");
157  uno::Any aGeoPropSet = xPropSet->getPropertyValue( sCustomShapeGeometry );
158  uno::Sequence< beans::PropertyValue > aGeoPropSeq;
159 
160  // ToDo: Using sAdjustmentValues in this "if" looks nonsense.
161  // It was introduced in revision acd2c909, which introduced the property "PresetTextWarp"
162  // for interoperability with Word.
163  if (aGeoPropSet >>= aGeoPropSeq)
164  {
165  for ( const auto& rGeoProp : std::as_const(aGeoPropSeq) )
166  {
167  if ( rGeoProp.Name == sAdjustmentValues )
168  {
169  OUString presetTextWarp;
170  if ( rGeoProp.Value >>= presetTextWarp )
171  {
172  aPropertyMap.setProperty( PROP_PresetTextWarp, presetTextWarp );
173  }
174  }
175  }
176  }
177 
178  if ( !maAdjustmentGuideList.empty() )
179  {
180  static const OUStringLiteral sType = u"Type";
181  if ( aGeoPropSet >>= aGeoPropSeq )
182  {
183  for ( auto& rGeoProp : aGeoPropSeq )
184  {
185  if ( rGeoProp.Name == sAdjustmentValues )
186  {
187  uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
188  if ( rGeoProp.Value >>= aAdjustmentSeq )
189  {
190  int nIndex=0;
191  for (auto const& adjustmentGuide : maAdjustmentGuideList)
192  {
193  if ( adjustmentGuide.maName.getLength() > 3 )
194  {
195  sal_Int32 nAdjustmentIndex = adjustmentGuide.maName.copy( 3 ).toInt32() - 1;
196  if ( ( nAdjustmentIndex >= 0 ) && ( nAdjustmentIndex < aAdjustmentSeq.getLength() ) )
197  {
198  EnhancedCustomShapeAdjustmentValue aAdjustmentVal;
199  aAdjustmentVal.Value <<= adjustmentGuide.maFormula.toInt32();
200  aAdjustmentVal.State = PropertyState_DIRECT_VALUE;
201  aAdjustmentVal.Name = adjustmentGuide.maName;
202  aAdjustmentSeq[ nAdjustmentIndex ] = aAdjustmentVal;
203  }
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  if (nIndex < aAdjustmentSeq.getLength())
210  {
211  aAdjustmentSeq[nIndex] = aAdjustmentVal;
212  ++nIndex;
213  }
214  }
215  }
216  rGeoProp.Value <<= aAdjustmentSeq;
217  xPropSet->setPropertyValue( sCustomShapeGeometry, Any( aGeoPropSeq ) );
218  }
219  }
220  else if ( rGeoProp.Name == sType )
221  {
222  if ( sConnectorShapeType.getLength() > 0 )
223  rGeoProp.Value <<= sConnectorShapeType;
224  else
225  rGeoProp.Value <<= OUString( "ooxml-CustomShape" );
226  }
227  }
228  }
229  }
230  }
231  else
232  {
233  PropertyMap aPropertyMap;
234  aPropertyMap.setProperty( PROP_Type, OUString( "ooxml-non-primitive" ));
235  aPropertyMap.setProperty( PROP_MirroredX, mbMirroredX );
236  aPropertyMap.setProperty( PROP_MirroredY, mbMirroredY );
237  if( mnTextRotateAngle )
238  aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextRotateAngle );
239  // Note 1: If Equations are defined - they are processed using internal div by 360 coordinates
240  // while if they are not, standard ooxml coordinates are used.
241  // This size specifically affects scaling.
242  // Note 2: Width and Height are set to 0 to force scaling to 1.
243  awt::Rectangle aViewBox( 0, 0, aSize.Width, aSize.Height );
244  if( !maGuideList.empty() )
245  aViewBox = awt::Rectangle( 0, 0, 0, 0 );
246  aPropertyMap.setProperty( PROP_ViewBox, aViewBox);
247 
249  for ( std::vector<CustomShapeGuide>::size_type i = 0; i < maAdjustmentGuideList.size(); i++ )
250  {
251  EnhancedCustomShapeAdjustmentValue aAdjustmentVal;
252  aAdjustmentVal.Value <<= maAdjustmentGuideList[ i ].maFormula.toInt32();
253  aAdjustmentVal.State = PropertyState_DIRECT_VALUE;
254  aAdjustmentVal.Name = maAdjustmentGuideList[ i ].maName;
255  aAdjustmentValues[ i ] = aAdjustmentVal;
256  }
257  aPropertyMap.setProperty( PROP_AdjustmentValues, aAdjustmentValues);
258 
259  PropertyMap aPath;
260 
261  aPath.setProperty( PROP_Segments, comphelper::containerToSequence(maSegments) );
262 
263  if ( maTextRect.has() ) {
265  aTextFrames[0].TopLeft.First = maTextRect.get().l;
266  aTextFrames[0].TopLeft.Second = maTextRect.get().t;
267  aTextFrames[0].BottomRight.First = maTextRect.get().r;
268  aTextFrames[0].BottomRight.Second = maTextRect.get().b;
269  aPath.setProperty( PROP_TextFrames, aTextFrames);
270  }
271 
272  sal_uInt32 nParameterPairs = 0;
273  for ( auto const & i: maPath2DList )
274  nParameterPairs += i.parameter.size();
275 
276  Sequence< EnhancedCustomShapeParameterPair > aParameterPairs( nParameterPairs );
277  sal_uInt32 k = 0;
278  for ( auto const & i: maPath2DList )
279  for ( auto const & j: i.parameter )
280  aParameterPairs[ k++ ] = j;
281  aPath.setProperty( PROP_Coordinates, aParameterPairs);
282 
283  if ( !maPath2DList.empty() )
284  {
285  bool bAllZero = true;
286  for ( auto const & i: maPath2DList )
287  {
288  if ( i.w || i.h ) {
289  bAllZero = false;
290  break;
291  }
292  }
293 
294  if ( !bAllZero ) {
295  Sequence< awt::Size > aSubViewSize( maPath2DList.size() );
296  for ( std::vector<Path2D>::size_type i=0; i < maPath2DList.size(); i++ )
297  {
298  aSubViewSize[i].Width = static_cast< sal_Int32 >( maPath2DList[i].w );
299  aSubViewSize[i].Height = static_cast< sal_Int32 >( maPath2DList[i].h );
300  SAL_INFO(
301  "oox.cscode",
302  "set subpath " << i << " size: " << maPath2DList[i].w
303  << " x " << maPath2DList[i].h);
304  }
305  aPath.setProperty( PROP_SubViewSize, aSubViewSize);
306  }
307  }
308 
310  aPropertyMap.setProperty( PROP_Path, aPathSequence);
311 
312  Sequence< OUString > aEquations( maGuideList.size() );
313  for ( std::vector<CustomShapeGuide>::size_type i = 0; i < maGuideList.size(); i++ )
314  aEquations[ i ] = maGuideList[ i ].maFormula;
315  aPropertyMap.setProperty( PROP_Equations, aEquations);
316 
318  for ( std::vector<AdjustHandle>::size_type i = 0; i < maAdjustHandleList.size(); i++ )
319  {
320  PropertyMap aHandle;
321  // maAdjustmentHandle[ i ].gdRef1 ... maAdjustmentHandle[ i ].gdRef2 ... :(
322  // gdRef1 && gdRef2 -> we do not offer such reference, so it is difficult
323  // to determine the correct adjustment handle that should be updated with the adjustment
324  // position. here is the solution: the adjustment value that is used within the position
325  // has to be updated, in case the position is a formula the first usage of a
326  // adjustment value is decisive
327  if ( maAdjustHandleList[ i ].polar )
328  {
329  // Polar handles in DrawingML
330  // 1. don't have reference center, so PROP_Polar isn't needed.
331  // 2. position always use planar coordinates.
332  // 3. use RefAngle and RefR to specify adjustment value to be updated.
333  // 4. The unit of angular adjustment values are 6000th degree.
334 
335  aHandle.setProperty( PROP_Position, maAdjustHandleList[ i ].pos);
336  if ( maAdjustHandleList[ i ].gdRef1.has() )
337  {
339  if ( nIndex >= 0 )
340  aHandle.setProperty( PROP_RefR, nIndex);
341  }
342  if ( maAdjustHandleList[ i ].gdRef2.has() )
343  {
345  if ( nIndex >= 0 )
346  aHandle.setProperty( PROP_RefAngle, nIndex);
347  }
348  if ( maAdjustHandleList[ i ].min1.has() )
349  aHandle.setProperty( PROP_RadiusRangeMinimum, maAdjustHandleList[ i ].min1.get());
350  if ( maAdjustHandleList[ i ].max1.has() )
351  aHandle.setProperty( PROP_RadiusRangeMaximum, maAdjustHandleList[ i ].max1.get());
352 
353  /* TODO: AngleMin & AngleMax
354  if ( maAdjustHandleList[ i ].min2.has() )
355  aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].min2.get());
356  if ( maAdjustHandleList[ i ].max2.has() )
357  aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].max2.get());
358  */
359  }
360  else
361  {
362  aHandle.setProperty( PROP_Position, maAdjustHandleList[ i ].pos);
363  if ( maAdjustHandleList[ i ].gdRef1.has() )
364  {
365  // TODO: PROP_RefX and PROP_RefY are not yet part of our file format,
366  // so the handles will not work after save/reload
368  if ( nIndex >= 0 )
369  aHandle.setProperty( PROP_RefX, nIndex);
370  }
371  if ( maAdjustHandleList[ i ].gdRef2.has() )
372  {
374  if ( nIndex >= 0 )
375  aHandle.setProperty( PROP_RefY, nIndex);
376  }
377  if ( maAdjustHandleList[ i ].min1.has() )
378  aHandle.setProperty( PROP_RangeXMinimum, maAdjustHandleList[ i ].min1.get());
379  if ( maAdjustHandleList[ i ].max1.has() )
380  aHandle.setProperty( PROP_RangeXMaximum, maAdjustHandleList[ i ].max1.get());
381  if ( maAdjustHandleList[ i ].min2.has() )
382  aHandle.setProperty( PROP_RangeYMinimum, maAdjustHandleList[ i ].min2.get());
383  if ( maAdjustHandleList[ i ].max2.has() )
384  aHandle.setProperty( PROP_RangeYMaximum, maAdjustHandleList[ i ].max2.get());
385  }
386  aHandles[ i ] = aHandle.makePropertyValueSequence();
387  }
388  aPropertyMap.setProperty( PROP_Handles, aHandles);
389 
390 #ifdef DEBUG
391  // Note that the script oox/source/drawingml/customshapes/generatePresetsData.pl looks
392  // for these ==cscode== and ==csdata== markers, so don't "clean up" these SAL_INFOs.
393  SAL_INFO("oox.cscode", "==cscode== begin");
394  aPropertyMap.dumpCode( aPropertyMap.makePropertySet() );
395  SAL_INFO("oox.cscode", "==cscode== end");
396  SAL_INFO("oox.csdata", "==csdata== begin");
397  aPropertyMap.dumpData( aPropertyMap.makePropertySet() );
398  SAL_INFO("oox.csdata", "==csdata== end");
399 #endif
400  // converting the vector to a sequence
402  PropertySet aPropSet( xPropSet );
403  aPropSet.setProperty( PROP_CustomShapeGeometry, aSeq );
404  }
405 }
406 
407 }
408 
409 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static void dumpData(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
sal_Int32 nIndex
A helper that maps property identifiers to property values.
Definition: propertymap.hxx:52
static sal_Int32 GetCustomShapeGuideValue(const std::vector< CustomShapeGuide > &rGuideList, std::u16string_view rFormulaName)
bool setProperty(sal_Int32 nPropId, Type &&rValue)
Sets the specified property to the passed value.
Definition: propertymap.hxx:70
std::unordered_map< sal_Int32, PropertyMap > PresetDataMap
std::vector< CustomShapeGuide > maGuideList
size_t pos
css::uno::Reference< css::beans::XPropertySet > makePropertySet() const
Creates a property set supporting the XPropertySet interface and inserts all properties.
std::vector< AdjustHandle > maAdjustHandleList
void pushToPropSet(const css::uno::Reference< css::beans::XPropertySet > &xPropSet, const css::uno::Reference< css::drawing::XShape > &xShape, const css::awt::Size &aSize)
OptionalString sType
OUString maFormula
float u
css::uno::Sequence< css::beans::PropertyValue > makePropertyValueSequence() const
Returns a sequence of property values, filled with all contained properties.
sal_Int32 w
A wrapper for a UNO property set.
Definition: propertyset.hxx:57
std::vector< css::drawing::EnhancedCustomShapeSegment > maSegments
css::uno::Sequence< sal_Int8 > const & getShapePresetTypeName() const
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
#define SAL_INFO(area, stream)
static void dumpCode(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Sequence< sal_Int8 > aSeq
static sal_Int32 SetCustomShapeGuideValue(std::vector< CustomShapeGuide > &rGuideList, const CustomShapeGuide &rGuide)
OUString maName
Definition: dffdumper.cxx:160
std::vector< CustomShapeGuide > maAdjustmentGuideList
sal_Int32 h
static OUString GetConnectorShapeType(sal_Int32 nType)
bool setProperty(sal_Int32 nPropId, const Type &rValue)
Puts the passed value into the property set.