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 <o3tl/string_view.hxx>
35 #include <sal/log.hxx>
36 
37 #include <algorithm>
38 
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;
43 
44 namespace oox::drawingml {
45 
47 : mnShapePresetType ( -1 )
48 , mbShapeTypeOverride(false)
49 , mbMirroredX ( false )
50 , mbMirroredY ( false )
51 , mnTextRotateAngle ( 0 )
52 , mnTextCameraZRotateAngle ( 0 )
53 , mnArcNum ( 0 )
54 {
55 }
56 
57 uno::Sequence< sal_Int8 > const & CustomShapeProperties::getShapePresetTypeName() const
58 {
60 }
61 
62 sal_Int32 CustomShapeProperties::SetCustomShapeGuideValue( std::vector< CustomShapeGuide >& rGuideList, const CustomShapeGuide& rGuide )
63 {
64  std::vector<CustomShapeGuide>::size_type nIndex = 0;
65  for( ; nIndex < rGuideList.size(); nIndex++ )
66  {
67  if ( rGuideList[ nIndex ].maName == rGuide.maName )
68  break;
69  }
70  if ( nIndex == rGuideList.size() )
71  rGuideList.push_back( rGuide );
72  return static_cast< sal_Int32 >( nIndex );
73 }
74 
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 )
78 {
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-- )
84  {
85  if ( rGuideList[ nIndex ].maName == rFormulaName )
86  break;
87  }
88 
89  return nIndex;
90 }
91 
93 {
94  return !((getShapePresetType() >= 0 || maPath2DList.size() > 0) &&
95  getShapePresetType() != XML_Rect &&
96  getShapePresetType() != XML_rect);
97 }
98 
100 
102  const Reference < XPropertySet >& xPropSet, const awt::Size &aSize )
103 {
104  if ( mnShapePresetType >= 0 )
105  {
106  SAL_INFO("oox.drawingml", "preset: " << mnShapePresetType);
107 
108  if (maPresetDataMap.empty())
110 
111  PropertyMap aPropertyMap;
112  PropertySet aPropSet( xPropSet );
113 
115  {
116  SAL_INFO(
117  "oox.drawingml",
118  "found property map for preset: " << mnShapePresetType);
119 
120  aPropertyMap = maPresetDataMap[mnShapePresetType];
121 #ifdef DEBUG
122  aPropertyMap.dumpCode( aPropertyMap.makePropertySet() );
123 #endif
124  }
125 
126  aPropertyMap.setProperty( PROP_MirroredX, mbMirroredX );
127  aPropertyMap.setProperty( PROP_MirroredY, mbMirroredY );
128  aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextRotateAngle );
129  aPropertyMap.setProperty( PROP_TextCameraZRotateAngle, mnTextCameraZRotateAngle );
131  aPropSet.setProperty( PROP_CustomShapeGeometry, aSeq );
132 
133  if ( !maAdjustmentGuideList.empty() )
134  {
135  static const OUStringLiteral sCustomShapeGeometry(u"CustomShapeGeometry");
136  static const OUStringLiteral sAdjustmentValues(u"AdjustmentValues");
137  uno::Any aGeoPropSet = xPropSet->getPropertyValue( sCustomShapeGeometry );
138  uno::Sequence< beans::PropertyValue > aGeoPropSeq;
139  if ( aGeoPropSet >>= aGeoPropSeq )
140  {
141  for ( auto& rGeoProp : asNonConstRange(aGeoPropSeq) )
142  {
143  if ( rGeoProp.Name == sAdjustmentValues )
144  {
145  uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
146  if ( rGeoProp.Value >>= aAdjustmentSeq )
147  {
148  auto aAdjustmentSeqRange = asNonConstRange(aAdjustmentSeq);
149  int nIndex=0;
150  for (auto const& adjustmentGuide : maAdjustmentGuideList)
151  {
152  if ( adjustmentGuide.maName.getLength() > 3 )
153  {
154  sal_Int32 nAdjustmentIndex = o3tl::toInt32(adjustmentGuide.maName.subView( 3 )) - 1;
155  if ( ( nAdjustmentIndex >= 0 ) && ( nAdjustmentIndex < aAdjustmentSeq.getLength() ) )
156  {
157  EnhancedCustomShapeAdjustmentValue aAdjustmentVal;
158  aAdjustmentVal.Value <<= adjustmentGuide.maFormula.toInt32();
159  aAdjustmentVal.State = PropertyState_DIRECT_VALUE;
160  aAdjustmentVal.Name = adjustmentGuide.maName;
161  aAdjustmentSeqRange[ nAdjustmentIndex ] = aAdjustmentVal;
162  }
163  } else if ( aAdjustmentSeq.hasElements() ) {
164  EnhancedCustomShapeAdjustmentValue aAdjustmentVal;
165  aAdjustmentVal.Value <<= adjustmentGuide.maFormula.toInt32();
166  aAdjustmentVal.State = PropertyState_DIRECT_VALUE;
167  aAdjustmentVal.Name = adjustmentGuide.maName;
168  if (nIndex < aAdjustmentSeq.getLength())
169  {
170  aAdjustmentSeqRange[nIndex] = aAdjustmentVal;
171  ++nIndex;
172  }
173  }
174  }
175  rGeoProp.Value <<= aAdjustmentSeq;
176  xPropSet->setPropertyValue( sCustomShapeGeometry, Any( aGeoPropSeq ) );
177  break;
178  }
179  }
180  }
181  }
182  }
183  }
184  else
185  {
186  PropertyMap aPropertyMap;
187  aPropertyMap.setProperty( PROP_Type, OUString( "ooxml-non-primitive" ));
188  aPropertyMap.setProperty( PROP_MirroredX, mbMirroredX );
189  aPropertyMap.setProperty( PROP_MirroredY, mbMirroredY );
190  if( mnTextRotateAngle )
191  aPropertyMap.setProperty( PROP_TextPreRotateAngle, mnTextRotateAngle );
192  // Note 1: If Equations are defined - they are processed using internal div by 360 coordinates
193  // while if they are not, standard ooxml coordinates are used.
194  // This size specifically affects scaling.
195  // Note 2: Width and Height are set to 0 to force scaling to 1.
196  awt::Rectangle aViewBox( 0, 0, aSize.Width, aSize.Height );
197  if( !maGuideList.empty() )
198  aViewBox = awt::Rectangle( 0, 0, 0, 0 );
199  aPropertyMap.setProperty( PROP_ViewBox, aViewBox);
200 
202  auto aAdjustmentValuesRange = asNonConstRange(aAdjustmentValues);
203  for ( std::vector<CustomShapeGuide>::size_type i = 0; i < maAdjustmentGuideList.size(); i++ )
204  {
205  EnhancedCustomShapeAdjustmentValue aAdjustmentVal;
206  aAdjustmentVal.Value <<= maAdjustmentGuideList[ i ].maFormula.toInt32();
207  aAdjustmentVal.State = PropertyState_DIRECT_VALUE;
208  aAdjustmentVal.Name = maAdjustmentGuideList[ i ].maName;
209  aAdjustmentValuesRange[ i ] = aAdjustmentVal;
210  }
211  aPropertyMap.setProperty( PROP_AdjustmentValues, aAdjustmentValues);
212 
213  PropertyMap aPath;
214 
215  aPath.setProperty( PROP_Segments, comphelper::containerToSequence(maSegments) );
216 
217  if ( maTextRect.has_value() ) {
219  { /* tl */ { maTextRect.get().l, maTextRect.get().t },
220  /* br */ { maTextRect.get().r, maTextRect.get().b } }
221  };
222  aPath.setProperty( PROP_TextFrames, aTextFrames);
223  }
224 
225  sal_uInt32 nParameterPairs = 0;
226  for ( auto const & i: maPath2DList )
227  nParameterPairs += i.parameter.size();
228 
229  Sequence< EnhancedCustomShapeParameterPair > aParameterPairs( nParameterPairs );
230  auto aParameterPairsRange = asNonConstRange(aParameterPairs);
231  sal_uInt32 k = 0;
232  for ( auto const & i: maPath2DList )
233  for ( auto const & j: i.parameter )
234  aParameterPairsRange[ k++ ] = j;
235  aPath.setProperty( PROP_Coordinates, aParameterPairs);
236 
237  if ( !maPath2DList.empty() )
238  {
239  bool bAllZero = true;
240  for ( auto const & i: maPath2DList )
241  {
242  if ( i.w || i.h ) {
243  bAllZero = false;
244  break;
245  }
246  }
247 
248  if ( !bAllZero ) {
249  Sequence< awt::Size > aSubViewSize( maPath2DList.size() );
250  std::transform(maPath2DList.begin(), maPath2DList.end(), aSubViewSize.getArray(),
251  [](const auto& p2d)
252  {
253  SAL_INFO("oox.cscode",
254  "set subpath; size: " << p2d.w << " x " << p2d.h);
255  return awt::Size(p2d.w, p2d.h);
256  });
257  aPath.setProperty( PROP_SubViewSize, aSubViewSize);
258  }
259  }
260 
262  aPropertyMap.setProperty( PROP_Path, aPathSequence);
263 
264  Sequence< OUString > aEquations( maGuideList.size() );
265  std::transform(maGuideList.begin(), maGuideList.end(), aEquations.getArray(),
266  [](const auto& g) { return g.maFormula; });
267  aPropertyMap.setProperty( PROP_Equations, aEquations);
268 
270  auto aHandlesRange = asNonConstRange(aHandles);
271  for ( std::vector<AdjustHandle>::size_type i = 0; i < maAdjustHandleList.size(); i++ )
272  {
273  PropertyMap aHandle;
274  // maAdjustmentHandle[ i ].gdRef1 ... maAdjustmentHandle[ i ].gdRef2 ... :(
275  // gdRef1 && gdRef2 -> we do not offer such reference, so it is difficult
276  // to determine the correct adjustment handle that should be updated with the adjustment
277  // position. here is the solution: the adjustment value that is used within the position
278  // has to be updated, in case the position is a formula the first usage of a
279  // adjustment value is decisive
280  if ( maAdjustHandleList[ i ].polar )
281  {
282  // Polar handles in DrawingML
283  // 1. don't have reference center, so PROP_Polar isn't needed.
284  // 2. position always use planar coordinates.
285  // 3. use RefAngle and RefR to specify adjustment value to be updated.
286  // 4. The unit of angular adjustment values are 6000th degree.
287 
288  aHandle.setProperty( PROP_Position, maAdjustHandleList[ i ].pos);
289  if ( maAdjustHandleList[ i ].gdRef1.has_value() )
290  {
292  if ( nIndex >= 0 )
293  aHandle.setProperty( PROP_RefR, nIndex);
294  }
295  if ( maAdjustHandleList[ i ].gdRef2.has_value() )
296  {
298  if ( nIndex >= 0 )
299  aHandle.setProperty( PROP_RefAngle, nIndex);
300  }
301  if ( maAdjustHandleList[ i ].min1.has_value() )
302  aHandle.setProperty( PROP_RadiusRangeMinimum, maAdjustHandleList[ i ].min1.get());
303  if ( maAdjustHandleList[ i ].max1.has_value() )
304  aHandle.setProperty( PROP_RadiusRangeMaximum, maAdjustHandleList[ i ].max1.get());
305 
306  /* TODO: AngleMin & AngleMax
307  if ( maAdjustHandleList[ i ].min2.has() )
308  aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].min2.get());
309  if ( maAdjustHandleList[ i ].max2.has() )
310  aHandle.setProperty( PROP_ ] = maAdjustHandleList[ i ].max2.get());
311  */
312  }
313  else
314  {
315  aHandle.setProperty( PROP_Position, maAdjustHandleList[ i ].pos);
316  if ( maAdjustHandleList[ i ].gdRef1.has_value() )
317  {
318  // TODO: PROP_RefX and PROP_RefY are not yet part of our file format,
319  // so the handles will not work after save/reload
321  if ( nIndex >= 0 )
322  aHandle.setProperty( PROP_RefX, nIndex);
323  }
324  if ( maAdjustHandleList[ i ].gdRef2.has_value() )
325  {
327  if ( nIndex >= 0 )
328  aHandle.setProperty( PROP_RefY, nIndex);
329  }
330  if ( maAdjustHandleList[ i ].min1.has_value() )
331  aHandle.setProperty( PROP_RangeXMinimum, maAdjustHandleList[ i ].min1.get());
332  if ( maAdjustHandleList[ i ].max1.has_value() )
333  aHandle.setProperty( PROP_RangeXMaximum, maAdjustHandleList[ i ].max1.get());
334  if ( maAdjustHandleList[ i ].min2.has_value() )
335  aHandle.setProperty( PROP_RangeYMinimum, maAdjustHandleList[ i ].min2.get());
336  if ( maAdjustHandleList[ i ].max2.has_value() )
337  aHandle.setProperty( PROP_RangeYMaximum, maAdjustHandleList[ i ].max2.get());
338  }
339  aHandlesRange[ i ] = aHandle.makePropertyValueSequence();
340  }
341  aPropertyMap.setProperty( PROP_Handles, aHandles);
342 
343 #ifdef DEBUG
344  // Note that the script oox/source/drawingml/customshapes/generatePresetsData.pl looks
345  // for these ==cscode== and ==csdata== markers, so don't "clean up" these SAL_INFOs.
346  SAL_INFO("oox.cscode", "==cscode== begin");
347  aPropertyMap.dumpCode( aPropertyMap.makePropertySet() );
348  SAL_INFO("oox.cscode", "==cscode== end");
349  SAL_INFO("oox.csdata", "==csdata== begin");
350  aPropertyMap.dumpData( aPropertyMap.makePropertySet() );
351  SAL_INFO("oox.csdata", "==csdata== end");
352 #endif
353  // converting the vector to a sequence
355  PropertySet aPropSet( xPropSet );
356  aPropSet.setProperty( PROP_CustomShapeGeometry, aSeq );
357  }
358 }
359 
360 }
361 
362 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
TokenMap & StaticTokenMap()
Definition: tokenmap.cxx:90
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:51
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:72
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
bool representsDefaultShape() const
Returns whether or not the current CustomShapeProperties represent a default shape preset that is rec...
css::uno::Sequence< sal_Int8 > const & getUtf8TokenName(sal_Int32 nToken) const
Returns the UTF8 name of the passed token identifier as byte sequence.
Definition: tokenmap.hxx:49
float u
css::uno::Sequence< css::beans::PropertyValue > makePropertyValueSequence() const
Returns a sequence of property values, filled with all contained properties.
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
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)
void pushToPropSet(const css::uno::Reference< css::beans::XPropertySet > &xPropSet, const css::awt::Size &aSize)
OUString maName
Definition: dffdumper.cxx:160
std::vector< CustomShapeGuide > maAdjustmentGuideList
bool setProperty(sal_Int32 nPropId, const Type &rValue)
Puts the passed value into the property set.
bool m_bDetectedRangeSegmentation false