LibreOffice Module oox (master)  1
drawingml.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 
20 #include <config_features.h>
21 
22 #include <config_folders.h>
23 #include <rtl/bootstrap.hxx>
24 #include <sal/log.hxx>
26 #include <oox/export/drawingml.hxx>
27 #include <oox/export/utils.hxx>
29 #include <oox/drawingml/color.hxx>
32 #include <oox/token/namespaces.hxx>
33 #include <oox/token/properties.hxx>
35 #include <oox/token/tokens.hxx>
37 #include <svtools/unitconv.hxx>
38 #include <sax/fastattribs.hxx>
39 #include <tools/diagnose_ex.h>
44 
45 #include <numeric>
46 #include <string_view>
47 
48 #include <com/sun/star/awt/CharSet.hpp>
49 #include <com/sun/star/awt/FontDescriptor.hpp>
50 #include <com/sun/star/awt/FontSlant.hpp>
51 #include <com/sun/star/awt/FontStrikeout.hpp>
52 #include <com/sun/star/awt/FontWeight.hpp>
53 #include <com/sun/star/awt/FontUnderline.hpp>
54 #include <com/sun/star/awt/Gradient.hpp>
55 #include <com/sun/star/beans/XPropertySet.hpp>
56 #include <com/sun/star/beans/XPropertyState.hpp>
57 #include <com/sun/star/beans/XPropertySetInfo.hpp>
58 #include <com/sun/star/container/XEnumerationAccess.hpp>
59 #include <com/sun/star/container/XIndexAccess.hpp>
60 #include <com/sun/star/container/XNameAccess.hpp>
61 #include <com/sun/star/drawing/BitmapMode.hpp>
62 #include <com/sun/star/drawing/ColorMode.hpp>
63 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
64 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
65 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
66 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
67 #include <com/sun/star/drawing/Hatch.hpp>
68 #include <com/sun/star/drawing/LineDash.hpp>
69 #include <com/sun/star/drawing/LineJoint.hpp>
70 #include <com/sun/star/drawing/LineStyle.hpp>
71 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
72 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
73 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
74 #include <com/sun/star/drawing/XShape.hpp>
75 #include <com/sun/star/drawing/XShapes.hpp>
76 #include <com/sun/star/drawing/FillStyle.hpp>
77 #include <com/sun/star/frame/XModel.hpp>
78 #include <com/sun/star/graphic/XGraphic.hpp>
79 #include <com/sun/star/i18n/ScriptType.hpp>
80 #include <com/sun/star/i18n/BreakIterator.hpp>
81 #include <com/sun/star/i18n/XBreakIterator.hpp>
82 #include <com/sun/star/io/XOutputStream.hpp>
83 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
84 #include <com/sun/star/style/LineSpacing.hpp>
85 #include <com/sun/star/style/LineSpacingMode.hpp>
86 #include <com/sun/star/text/WritingMode.hpp>
87 #include <com/sun/star/text/WritingMode2.hpp>
88 #include <com/sun/star/text/GraphicCrop.hpp>
89 #include <com/sun/star/text/XText.hpp>
90 #include <com/sun/star/text/XTextColumns.hpp>
91 #include <com/sun/star/text/XTextContent.hpp>
92 #include <com/sun/star/text/XTextField.hpp>
93 #include <com/sun/star/text/XTextRange.hpp>
94 #include <com/sun/star/text/XTextFrame.hpp>
95 #include <com/sun/star/style/CaseMap.hpp>
96 #include <com/sun/star/xml/dom/XNodeList.hpp>
97 #include <com/sun/star/xml/sax/Writer.hpp>
98 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
99 #include <com/sun/star/container/XNamed.hpp>
100 #include <com/sun/star/drawing/XDrawPages.hpp>
101 #include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
102 
103 #include <comphelper/random.hxx>
104 #include <comphelper/seqstream.hxx>
106 #include <comphelper/xmltools.hxx>
107 #include <o3tl/any.hxx>
108 #include <o3tl/safeint.hxx>
109 #include <o3tl/unit_conversion.hxx>
110 #include <tools/stream.hxx>
111 #include <unotools/fontdefs.hxx>
112 #include <vcl/cvtgrf.hxx>
113 #include <vcl/graph.hxx>
114 #include <vcl/svapp.hxx>
115 #include <rtl/strbuf.hxx>
117 #include <filter/msfilter/util.hxx>
118 #include <editeng/outlobj.hxx>
119 #include <editeng/svxenum.hxx>
120 #include <editeng/unonames.hxx>
121 #include <editeng/unoprnms.hxx>
122 #include <editeng/flditem.hxx>
123 #include <svx/svdoashp.hxx>
124 #include <svx/svdomedia.hxx>
125 #include <svx/unoapi.hxx>
126 #include <svx/unoshape.hxx>
129 
130 using namespace ::css;
131 using namespace ::css::beans;
132 using namespace ::css::drawing;
133 using namespace ::css::i18n;
134 using namespace ::css::style;
135 using namespace ::css::text;
136 using namespace ::css::uno;
137 using namespace ::css::container;
138 
139 using ::css::io::XOutputStream;
140 using ::sax_fastparser::FSHelperPtr;
141 using ::sax_fastparser::FastSerializerHelper;
142 
143 namespace
144 {
146 sal_Int32 GetAlphaFromTransparenceGradient(const awt::Gradient& rGradient, bool bStart)
147 {
148  // Our alpha is a gray color value.
149  sal_uInt8 nRed = ::Color(ColorTransparency, bStart ? rGradient.StartColor : rGradient.EndColor).GetRed();
150  // drawingML alpha is a percentage on a 0..100000 scale.
151  return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
152 }
153 }
154 
155 namespace oox::drawingml {
156 
158 {
159 }
160 
161 OUString URLTransformer::getTransformedString(const OUString& rString) const
162 {
163  return rString;
164 }
165 
166 bool URLTransformer::isExternalURL(const OUString& rURL) const
167 {
168  bool bExternal = true;
169  if (rURL.startsWith("#"))
170  bExternal = false;
171  return bExternal;
172 }
173 
174 static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rDashName )
175  {
176  css::uno::Reference<css::lang::XMultiServiceFactory> xFact(xModel, css::uno::UNO_QUERY);
177  css::uno::Reference<css::container::XNameAccess> xNameAccess(
178  xFact->createInstance("com.sun.star.drawing.DashTable"),
179  css::uno::UNO_QUERY );
180  if(xNameAccess.is())
181  {
182  if (!xNameAccess->hasByName(rDashName))
183  return css::uno::Any();
184 
185  return xNameAccess->getByName(rDashName);
186  }
187 
188  return css::uno::Any();
189  }
190 
191 namespace
192 {
193 void WriteGradientPath(const awt::Gradient& rGradient, const FSHelperPtr& pFS, const bool bCircle)
194 {
195  pFS->startElementNS(XML_a, XML_path, XML_path, bCircle ? "circle" : "rect");
196 
197  // Write the focus rectangle. Work with the focus point, and assume
198  // that it extends 50% in all directions. The below
199  // left/top/right/bottom values are percentages, where 0 means the
200  // edge of the tile rectangle and 100% means the center of it.
203  sal_Int32 nLeftPercent = rGradient.XOffset;
204  pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT));
205  sal_Int32 nTopPercent = rGradient.YOffset;
206  pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT));
207  sal_Int32 nRightPercent = 100 - rGradient.XOffset;
208  pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT));
209  sal_Int32 nBottomPercent = 100 - rGradient.YOffset;
210  pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT));
211  pFS->singleElementNS(XML_a, XML_fillToRect, pAttributeList);
212 
213  pFS->endElementNS(XML_a, XML_path);
214 }
215 }
216 
217 // not thread safe
220 std::map<OUString, OUString> DrawingML::maWdpCache;
221 sal_Int32 DrawingML::mnDrawingMLCount = 0;
222 sal_Int32 DrawingML::mnVmlCount = 0;
223 
224 sal_Int16 DrawingML::GetScriptType(const OUString& rStr)
225 {
226  if (rStr.getLength() > 0)
227  {
228  static Reference<css::i18n::XBreakIterator> xBreakIterator =
229  css::i18n::BreakIterator::create(comphelper::getProcessComponentContext());
230 
231  sal_Int16 nScriptType = xBreakIterator->getScriptType(rStr, 0);
232 
233  if (nScriptType == css::i18n::ScriptType::WEAK)
234  {
235  sal_Int32 nPos = xBreakIterator->nextScript(rStr, 0, nScriptType);
236  if (nPos < rStr.getLength())
237  nScriptType = xBreakIterator->getScriptType(rStr, nPos);
238 
239  }
240 
241  if (nScriptType != css::i18n::ScriptType::WEAK)
242  return nScriptType;
243  }
244 
245  return css::i18n::ScriptType::LATIN;
246 }
247 
249 {
250  mnImageCounter = 1;
251  mnWdpImageCounter = 1;
252  maWdpCache.clear();
253 }
254 
256 {
257  mnDrawingMLCount = 0;
258  mnVmlCount = 0;
259 }
260 
261 bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName )
262 {
263  try
264  {
265  mAny = rXPropertySet->getPropertyValue(aName);
266  if (mAny.hasValue())
267  return true;
268  }
269  catch( const Exception& )
270  {
271  /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
272  }
273  return false;
274 }
275 
276 bool DrawingML::GetPropertyAndState( const Reference< XPropertySet >& rXPropertySet, const Reference< XPropertyState >& rXPropertyState, const OUString& aName, PropertyState& eState )
277 {
278  try
279  {
280  mAny = rXPropertySet->getPropertyValue(aName);
281  if (mAny.hasValue())
282  {
283  eState = rXPropertyState->getPropertyState(aName);
284  return true;
285  }
286  }
287  catch( const Exception& )
288  {
289  /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
290  }
291  return false;
292 }
293 
294 namespace
295 {
297 OString getColorStr(const ::Color nColor)
298 {
299  // Transparency is a separate element.
300  OString sColor = OString::number(sal_uInt32(nColor) & 0x00FFFFFF, 16);
301  if (sColor.getLength() < 6)
302  {
303  OStringBuffer sBuf("0");
304  int remains = 5 - sColor.getLength();
305 
306  while (remains > 0)
307  {
308  sBuf.append("0");
309  remains--;
310  }
311 
312  sBuf.append(sColor);
313 
314  sColor = sBuf.getStr();
315  }
316  return sColor;
317 }
318 }
319 
320 void DrawingML::WriteColor( ::Color nColor, sal_Int32 nAlpha )
321 {
322  const auto sColor = getColorStr(nColor);
323  if( nAlpha < MAX_PERCENT )
324  {
325  mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
326  mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
327  mpFS->endElementNS( XML_a, XML_srgbClr );
328 
329  }
330  else
331  {
332  mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
333  }
334 }
335 
336 void DrawingML::WriteColor( const OUString& sColorSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
337 {
338  // prevent writing a tag with empty val attribute
339  if( sColorSchemeName.isEmpty() )
340  return;
341 
342  if( aTransformations.hasElements() )
343  {
344  mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName);
345  WriteColorTransformations( aTransformations, nAlpha );
346  mpFS->endElementNS( XML_a, XML_schemeClr );
347  }
348  else if(nAlpha < MAX_PERCENT)
349  {
350  mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName);
351  mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
352  mpFS->endElementNS( XML_a, XML_schemeClr );
353  }
354  else
355  {
356  mpFS->singleElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName);
357  }
358 }
359 
360 void DrawingML::WriteColor( const ::Color nColor, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
361 {
362  const auto sColor = getColorStr(nColor);
363  if( aTransformations.hasElements() )
364  {
365  mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
366  WriteColorTransformations(aTransformations, nAlpha);
367  mpFS->endElementNS(XML_a, XML_srgbClr);
368  }
369  else if(nAlpha < MAX_PERCENT)
370  {
371  mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
372  mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
373  mpFS->endElementNS(XML_a, XML_srgbClr);
374  }
375  else
376  {
377  mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
378  }
379 }
380 
381 void DrawingML::WriteColorTransformations( const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
382 {
383  for( const auto& rTransformation : aTransformations )
384  {
385  sal_Int32 nToken = Color::getColorTransformationToken( rTransformation.Name );
386  if( nToken != XML_TOKEN_INVALID && rTransformation.Value.hasValue() )
387  {
388  if(nToken == XML_alpha && nAlpha < MAX_PERCENT)
389  {
390  mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nAlpha));
391  }
392  else
393  {
394  sal_Int32 nValue = rTransformation.Value.get<sal_Int32>();
395  mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nValue));
396  }
397  }
398  }
399 }
400 
401 void DrawingML::WriteSolidFill( ::Color nColor, sal_Int32 nAlpha )
402 {
403  mpFS->startElementNS(XML_a, XML_solidFill);
404  WriteColor( nColor, nAlpha );
405  mpFS->endElementNS( XML_a, XML_solidFill );
406 }
407 
408 void DrawingML::WriteSolidFill( const OUString& sSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
409 {
410  mpFS->startElementNS(XML_a, XML_solidFill);
411  WriteColor( sSchemeName, aTransformations, nAlpha );
412  mpFS->endElementNS( XML_a, XML_solidFill );
413 }
414 
415 void DrawingML::WriteSolidFill( const ::Color nColor, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
416 {
417  mpFS->startElementNS(XML_a, XML_solidFill);
418  WriteColor(nColor, aTransformations, nAlpha);
419  mpFS->endElementNS(XML_a, XML_solidFill);
420 }
421 
423 {
424  // get fill color
425  if ( !GetProperty( rXPropSet, "FillColor" ) )
426  return;
427  sal_uInt32 nFillColor = mAny.get<sal_uInt32>();
428 
429  // get InteropGrabBag and search the relevant attributes
430  OUString sColorFillScheme;
431  sal_uInt32 nOriginalColor = 0;
432  Sequence< PropertyValue > aStyleProperties, aTransformations;
433  if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
434  {
435  Sequence< PropertyValue > aGrabBag;
436  mAny >>= aGrabBag;
437  for( const auto& rProp : std::as_const(aGrabBag) )
438  {
439  if( rProp.Name == "SpPrSolidFillSchemeClr" )
440  rProp.Value >>= sColorFillScheme;
441  else if( rProp.Name == "OriginalSolidFillClr" )
442  rProp.Value >>= nOriginalColor;
443  else if( rProp.Name == "StyleFillRef" )
444  rProp.Value >>= aStyleProperties;
445  else if( rProp.Name == "SpPrSolidFillSchemeClrTransformations" )
446  rProp.Value >>= aTransformations;
447  }
448  }
449 
450  sal_Int32 nAlpha = MAX_PERCENT;
451  if( GetProperty( rXPropSet, "FillTransparence" ) )
452  {
453  sal_Int32 nTransparency = 0;
454  mAny >>= nTransparency;
455  // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
456  nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
457  }
458 
459  // OOXML has no separate transparence gradient but uses transparency in the gradient stops.
460  // So we merge transparency and color and use gradient fill in such case.
461  awt::Gradient aTransparenceGradient;
462  bool bNeedGradientFill(false);
463  if (GetProperty(rXPropSet, "FillTransparenceGradient"))
464  {
465  mAny >>= aTransparenceGradient;
466  if (aTransparenceGradient.StartColor != aTransparenceGradient.EndColor)
467  bNeedGradientFill = true;
468  else if (aTransparenceGradient.StartColor != 0)
469  nAlpha = GetAlphaFromTransparenceGradient(aTransparenceGradient, true);
470  }
471 
472  // write XML
473  if (bNeedGradientFill)
474  {
475  awt::Gradient aPseudoColorGradient;
476  aPseudoColorGradient.XOffset = aTransparenceGradient.XOffset;
477  aPseudoColorGradient.YOffset = aTransparenceGradient.YOffset;
478  aPseudoColorGradient.StartIntensity = 100;
479  aPseudoColorGradient.EndIntensity = 100;
480  aPseudoColorGradient.Angle = aTransparenceGradient.Angle;
481  aPseudoColorGradient.Border = aTransparenceGradient.Border;
482  aPseudoColorGradient.Style = aTransparenceGradient.Style;
483  aPseudoColorGradient.StartColor = nFillColor;
484  aPseudoColorGradient.EndColor = nFillColor;
485  aPseudoColorGradient.StepCount = aTransparenceGradient.StepCount;
486  mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
487  WriteGradientFill(aPseudoColorGradient, aTransparenceGradient);
488  mpFS->endElementNS( XML_a, XML_gradFill );
489  }
490  else if ( nFillColor != nOriginalColor )
491  {
492  // the user has set a different color for the shape
493  WriteSolidFill( ::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha );
494  }
495  else if ( !sColorFillScheme.isEmpty() )
496  {
497  // the shape had a scheme color and the user didn't change it
498  WriteSolidFill( sColorFillScheme, aTransformations, nAlpha );
499  }
500  else
501  {
502  // the shape had a custom color and the user didn't change it
503  // tdf#124013
504  WriteSolidFill( ::Color(ColorTransparency, nFillColor & 0xffffff), nAlpha );
505  }
506 }
507 
508 void DrawingML::WriteGradientStop(sal_uInt16 nStop, ::Color nColor, sal_Int32 nAlpha)
509 {
510  mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nStop * 1000));
511  WriteColor(nColor, nAlpha);
512  mpFS->endElementNS( XML_a, XML_gs );
513 }
514 
515 ::Color DrawingML::ColorWithIntensity( sal_uInt32 nColor, sal_uInt32 nIntensity )
516 {
517  return ::Color(ColorTransparency, ( ( ( nColor & 0xff ) * nIntensity ) / 100 )
518  | ( ( ( ( ( nColor & 0xff00 ) >> 8 ) * nIntensity ) / 100 ) << 8 )
519  | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 ));
520 }
521 
522 bool DrawingML::EqualGradients( awt::Gradient aGradient1, awt::Gradient aGradient2 )
523 {
524  return aGradient1.Style == aGradient2.Style &&
525  aGradient1.StartColor == aGradient2.StartColor &&
526  aGradient1.EndColor == aGradient2.EndColor &&
527  aGradient1.Angle == aGradient2.Angle &&
528  aGradient1.Border == aGradient2.Border &&
529  aGradient1.XOffset == aGradient2.XOffset &&
530  aGradient1.YOffset == aGradient2.YOffset &&
531  aGradient1.StartIntensity == aGradient2.StartIntensity &&
532  aGradient1.EndIntensity == aGradient2.EndIntensity &&
533  aGradient1.StepCount == aGradient2.StepCount;
534 }
535 
537 {
538  awt::Gradient aGradient;
539  if (!GetProperty(rXPropSet, "FillGradient"))
540  return;
541 
542  aGradient = *o3tl::doAccess<awt::Gradient>(mAny);
543 
544  // get InteropGrabBag and search the relevant attributes
545  awt::Gradient aOriginalGradient;
546  Sequence< PropertyValue > aGradientStops;
547  if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
548  {
549  Sequence< PropertyValue > aGrabBag;
550  mAny >>= aGrabBag;
551  for( const auto& rProp : std::as_const(aGrabBag) )
552  if( rProp.Name == "GradFillDefinition" )
553  rProp.Value >>= aGradientStops;
554  else if( rProp.Name == "OriginalGradFill" )
555  rProp.Value >>= aOriginalGradient;
556  }
557 
558  // check if an ooxml gradient had been imported and if the user has modified it
559  // Gradient grab-bag depends on theme grab-bag, which is implemented
560  // only for DOCX.
561  if( EqualGradients( aOriginalGradient, aGradient ) && GetDocumentType() == DOCUMENT_DOCX)
562  {
563  // If we have no gradient stops that means original gradient were defined by a theme.
564  if( aGradientStops.hasElements() )
565  {
566  mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
567  WriteGrabBagGradientFill(aGradientStops, aGradient);
568  mpFS->endElementNS( XML_a, XML_gradFill );
569  }
570  }
571  else
572  {
573  awt::Gradient aTransparenceGradient;
574  mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
575  OUString sFillTransparenceGradientName;
576  if (GetProperty(rXPropSet, "FillTransparenceGradientName")
577  && (mAny >>= sFillTransparenceGradientName)
578  && !sFillTransparenceGradientName.isEmpty())
579  {
580  if (GetProperty(rXPropSet, "FillTransparenceGradient"))
581  aTransparenceGradient = *o3tl::doAccess<awt::Gradient>(mAny);
582  }
583  else if (GetProperty(rXPropSet, "FillTransparence"))
584  {
585  // currently only StartColor and EndColor are evaluated in WriteGradientFill()
586  sal_Int32 nTransparency = 0;
587  mAny >>= nTransparency;
588  // convert percent to gray color
589  nTransparency = nTransparency * 255/100;
590  const sal_Int32 aGrayColor = static_cast<sal_Int32>( nTransparency | nTransparency << 8 | nTransparency << 16 );
591  aTransparenceGradient.StartColor = aGrayColor;
592  aTransparenceGradient.EndColor = aGrayColor;
593  }
594  WriteGradientFill(aGradient, aTransparenceGradient);
595  mpFS->endElementNS(XML_a, XML_gradFill);
596  }
597 }
598 
599 void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, awt::Gradient rGradient )
600 {
601  // write back the original gradient
602  mpFS->startElementNS(XML_a, XML_gsLst);
603 
604  // get original stops and write them
605  for( const auto& rGradientStop : aGradientStops )
606  {
607  Sequence< PropertyValue > aGradientStop;
608  rGradientStop.Value >>= aGradientStop;
609 
610  // get values
611  OUString sSchemeClr;
612  double nPos = 0;
613  sal_Int16 nTransparency = 0;
614  ::Color nRgbClr;
615  Sequence< PropertyValue > aTransformations;
616  for( const auto& rProp : std::as_const(aGradientStop) )
617  {
618  if( rProp.Name == "SchemeClr" )
619  rProp.Value >>= sSchemeClr;
620  else if( rProp.Name == "RgbClr" )
621  rProp.Value >>= nRgbClr;
622  else if( rProp.Name == "Pos" )
623  rProp.Value >>= nPos;
624  else if( rProp.Name == "Transparency" )
625  rProp.Value >>= nTransparency;
626  else if( rProp.Name == "Transformations" )
627  rProp.Value >>= aTransformations;
628  }
629  // write stop
630  mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nPos * 100000.0).getStr());
631  if( sSchemeClr.isEmpty() )
632  {
633  // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
634  sal_Int32 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
635  WriteColor( nRgbClr, nAlpha );
636  }
637  else
638  {
639  WriteColor( sSchemeClr, aTransformations );
640  }
641  mpFS->endElementNS( XML_a, XML_gs );
642  }
643  mpFS->endElementNS( XML_a, XML_gsLst );
644 
645  switch (rGradient.Style)
646  {
647  default:
648  mpFS->singleElementNS(
649  XML_a, XML_lin, XML_ang,
650  OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000));
651  break;
652  case awt::GradientStyle_RADIAL:
653  WriteGradientPath(rGradient, mpFS, true);
654  break;
655  }
656 }
657 
658 void DrawingML::WriteGradientFill(awt::Gradient rGradient, awt::Gradient rTransparenceGradient,
659  const uno::Reference<beans::XPropertySet>& rXPropSet)
660 {
661  sal_Int32 nStartAlpha;
662  sal_Int32 nEndAlpha;
663  if( rXPropSet.is() && GetProperty(rXPropSet, "FillTransparence") )
664  {
665  sal_Int32 nTransparency = 0;
666  mAny >>= nTransparency;
667  nStartAlpha = nEndAlpha = (MAX_PERCENT - (PER_PERCENT * nTransparency));
668  }
669  else
670  {
671  nStartAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, true);
672  nEndAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, false);
673  }
674  switch( rGradient.Style )
675  {
676  default:
677  case awt::GradientStyle_LINEAR:
678  {
679  mpFS->startElementNS(XML_a, XML_gsLst);
680  WriteGradientStop(rGradient.Border, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity),
681  nStartAlpha);
682  WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
683  nEndAlpha);
684  mpFS->endElementNS( XML_a, XML_gsLst );
685  mpFS->singleElementNS(
686  XML_a, XML_lin, XML_ang,
687  OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000));
688  break;
689  }
690 
691  case awt::GradientStyle_AXIAL:
692  {
693  mpFS->startElementNS(XML_a, XML_gsLst);
694  WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
695  nEndAlpha);
696  if (rGradient.Border > 0 && rGradient.Border < 100)
697  {
698  WriteGradientStop(rGradient.Border/2,
699  ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
700  nEndAlpha);
701  }
702  WriteGradientStop(50, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity),
703  nStartAlpha);
704  if (rGradient.Border > 0 && rGradient.Border < 100)
705  {
706  WriteGradientStop(100 - rGradient.Border/2,
707  ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
708  nEndAlpha);
709  }
710  WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
711  nEndAlpha);
712  mpFS->endElementNS(XML_a, XML_gsLst);
713  mpFS->singleElementNS(
714  XML_a, XML_lin, XML_ang,
715  OString::number(((3600 - rGradient.Angle + 900) * 6000) % 21600000));
716  break;
717  }
718 
719  case awt::GradientStyle_RADIAL:
720  case awt::GradientStyle_ELLIPTICAL:
721  case awt::GradientStyle_RECT:
722  case awt::GradientStyle_SQUARE:
723  {
724  mpFS->startElementNS(XML_a, XML_gsLst);
725  WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
726  nEndAlpha);
727  if (rGradient.Border > 0 && rGradient.Border < 100)
728  {
729  // Map border to an additional gradient stop, which has the
730  // same color as the final stop.
731  WriteGradientStop(100 - rGradient.Border,
732  ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity),
733  nStartAlpha);
734  }
735  WriteGradientStop(100,
736  ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity),
737  nStartAlpha);
738  mpFS->endElementNS(XML_a, XML_gsLst);
739 
740  WriteGradientPath(rGradient, mpFS, rGradient.Style == awt::GradientStyle_RADIAL || rGradient.Style == awt::GradientStyle_ELLIPTICAL);
741  break;
742  }
743  }
744 }
745 
746 void DrawingML::WriteLineArrow( const Reference< XPropertySet >& rXPropSet, bool bLineStart )
747 {
748  ESCHER_LineEnd eLineEnd;
749  sal_Int32 nArrowLength;
750  sal_Int32 nArrowWidth;
751 
752  if ( !EscherPropertyContainer::GetLineArrow( bLineStart, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
753  return;
754 
755  const char* len;
756  const char* type;
757  const char* width;
758 
759  switch( nArrowLength )
760  {
762  len = "sm";
763  break;
764  default:
766  len = "med";
767  break;
769  len = "lg";
770  break;
771  }
772 
773  switch( eLineEnd )
774  {
775  default:
776  case ESCHER_LineNoEnd:
777  type = "none";
778  break;
779  case ESCHER_LineArrowEnd:
780  type = "triangle";
781  break;
783  type = "stealth";
784  break;
786  type = "diamond";
787  break;
789  type = "oval";
790  break;
792  type = "arrow";
793  break;
794  }
795 
796  switch( nArrowWidth )
797  {
799  width = "sm";
800  break;
801  default:
803  width = "med";
804  break;
806  width = "lg";
807  break;
808  }
809 
810  mpFS->singleElementNS( XML_a, bLineStart ? XML_headEnd : XML_tailEnd,
811  XML_len, len,
812  XML_type, type,
813  XML_w, width );
814 }
815 
817 {
818  drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
819  if (GetProperty(rXPropSet, "LineStyle"))
820  mAny >>= aLineStyle;
821 
822  const LineCap aLineCap = GetProperty(rXPropSet, "LineCap") ? mAny.get<drawing::LineCap>() : LineCap_BUTT;
823 
824  sal_uInt32 nLineWidth = 0;
825  sal_uInt32 nEmuLineWidth = 0;
826  ::Color nColor;
827  sal_Int32 nColorAlpha = MAX_PERCENT;
828  bool bColorSet = false;
829  const char* cap = nullptr;
830  drawing::LineDash aLineDash;
831  bool bDashSet = false;
832  bool bNoFill = false;
833 
834 
835  // get InteropGrabBag and search the relevant attributes
836  OUString sColorFillScheme;
837  ::Color aResolvedColorFillScheme;
838 
839  ::Color nOriginalColor;
840  ::Color nStyleColor;
841  sal_uInt32 nStyleLineWidth = 0;
842 
843  Sequence<PropertyValue> aStyleProperties;
844  Sequence<PropertyValue> aTransformations;
845 
846  drawing::LineStyle aStyleLineStyle(drawing::LineStyle_NONE);
847  drawing::LineJoint aStyleLineJoint(drawing::LineJoint_NONE);
848 
849  if (GetProperty(rXPropSet, "InteropGrabBag"))
850  {
851  Sequence<PropertyValue> aGrabBag;
852  mAny >>= aGrabBag;
853 
854  for (const auto& rProp : std::as_const(aGrabBag))
855  {
856  if( rProp.Name == "SpPrLnSolidFillSchemeClr" )
857  rProp.Value >>= sColorFillScheme;
858  if( rProp.Name == "SpPrLnSolidFillResolvedSchemeClr" )
859  rProp.Value >>= aResolvedColorFillScheme;
860  else if( rProp.Name == "OriginalLnSolidFillClr" )
861  rProp.Value >>= nOriginalColor;
862  else if( rProp.Name == "StyleLnRef" )
863  rProp.Value >>= aStyleProperties;
864  else if( rProp.Name == "SpPrLnSolidFillSchemeClrTransformations" )
865  rProp.Value >>= aTransformations;
866  else if( rProp.Name == "EmuLineWidth" )
867  rProp.Value >>= nEmuLineWidth;
868  }
869  for (const auto& rStyleProp : std::as_const(aStyleProperties))
870  {
871  if( rStyleProp.Name == "Color" )
872  rStyleProp.Value >>= nStyleColor;
873  else if( rStyleProp.Name == "LineStyle" )
874  rStyleProp.Value >>= aStyleLineStyle;
875  else if( rStyleProp.Name == "LineJoint" )
876  rStyleProp.Value >>= aStyleLineJoint;
877  else if( rStyleProp.Name == "LineWidth" )
878  rStyleProp.Value >>= nStyleLineWidth;
879  }
880  }
881 
882  if (GetProperty(rXPropSet, "LineWidth"))
883  mAny >>= nLineWidth;
884 
885  switch (aLineStyle)
886  {
887  case drawing::LineStyle_NONE:
888  bNoFill = true;
889  break;
890  case drawing::LineStyle_DASH:
891  if (GetProperty(rXPropSet, "LineDash"))
892  {
893  aLineDash = mAny.get<drawing::LineDash>();
894  //this query is good for shapes, but in the case of charts it returns 0 values
895  if (aLineDash.Dots == 0 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 0) {
896  OUString aLineDashName;
897  if (GetProperty(rXPropSet, "LineDashName"))
898  mAny >>= aLineDashName;
899  if (!aLineDashName.isEmpty() && xModel) {
900  css::uno::Any aAny = getLineDash(xModel, aLineDashName);
901  aAny >>= aLineDash;
902  }
903  }
904  }
905  else
906  {
907  //export the linestyle of chart wall (plot area) and chart page
908  OUString aLineDashName;
909  if (GetProperty(rXPropSet, "LineDashName"))
910  mAny >>= aLineDashName;
911  if (!aLineDashName.isEmpty() && xModel) {
912  css::uno::Any aAny = getLineDash(xModel, aLineDashName);
913  aAny >>= aLineDash;
914  }
915  }
916  bDashSet = true;
917  if (aLineDash.Style == DashStyle_ROUND || aLineDash.Style == DashStyle_ROUNDRELATIVE)
918  {
919  cap = "rnd";
920  }
921 
922  SAL_INFO("oox.shape", "dash dots: " << aLineDash.Dots << " dashes: " << aLineDash.Dashes
923  << " dotlen: " << aLineDash.DotLen << " dashlen: " << aLineDash.DashLen << " distance: " << aLineDash.Distance);
924 
925  [[fallthrough]];
926  case drawing::LineStyle_SOLID:
927  default:
928  if (GetProperty(rXPropSet, "LineColor"))
929  {
930  nColor = ::Color(ColorTransparency, mAny.get<sal_uInt32>() & 0xffffff);
931  bColorSet = true;
932  }
933  if (GetProperty(rXPropSet, "LineTransparence"))
934  {
935  nColorAlpha = MAX_PERCENT - (mAny.get<sal_Int16>() * PER_PERCENT);
936  }
937  if (aLineCap == LineCap_ROUND)
938  cap = "rnd";
939  else if (aLineCap == LineCap_SQUARE)
940  cap = "sq";
941  break;
942  }
943 
944  // if the line-width was not modified after importing then the original EMU value will be exported to avoid unexpected conversion (rounding) error
945  if (nEmuLineWidth == 0 || static_cast<sal_uInt32>(oox::drawingml::convertEmuToHmm(nEmuLineWidth)) != nLineWidth)
946  nEmuLineWidth = oox::drawingml::convertHmmToEmu(nLineWidth);
947  mpFS->startElementNS( XML_a, XML_ln,
948  XML_cap, cap,
949  XML_w, sax_fastparser::UseIf(OString::number(nEmuLineWidth),
950  nLineWidth == 0 || (nLineWidth > 1 && nStyleLineWidth != nLineWidth)) );
951 
952  if( bColorSet )
953  {
954  if( nColor != nOriginalColor )
955  {
956  // the user has set a different color for the line
957  WriteSolidFill( nColor, nColorAlpha );
958  }
959  else if( !sColorFillScheme.isEmpty() )
960  {
961  // the line had a scheme color and the user didn't change it
962  WriteSolidFill( aResolvedColorFillScheme, aTransformations );
963  }
964  else
965  {
966  WriteSolidFill( nColor, nColorAlpha );
967  }
968  }
969 
970  if( bDashSet && aStyleLineStyle != drawing::LineStyle_DASH )
971  {
972  // Try to detect if it might come from ms preset line style import.
973  // MS Office styles are always relative, both binary and OOXML.
974  // "dot" is always the first dash and "dash" the second one. All OOXML presets linestyles
975  // start with the longer one. Definitions are in OOXML part 1, 20.1.10.49
976  // The tests are strict, for to not catch styles from standard.sod (as of Aug 2019).
977  bool bIsConverted = false;
978 
979  bool bIsRelative(aLineDash.Style == DashStyle_RECTRELATIVE || aLineDash.Style == DashStyle_ROUNDRELATIVE);
980  if ( bIsRelative && aLineDash.Dots == 1)
981  { // The length were tweaked on import in case of prstDash. Revert it here.
982  sal_uInt32 nDotLen = aLineDash.DotLen;
983  sal_uInt32 nDashLen = aLineDash.DashLen;
984  sal_uInt32 nDistance = aLineDash.Distance;
985  if (aLineCap != LineCap_BUTT && nDistance >= 99)
986  {
987  nDistance -= 99;
988  nDotLen += 99;
989  if (nDashLen > 0)
990  nDashLen += 99;
991  }
992  // LO uses length 0 for 100%, if the attribute is missing in ODF.
993  // Other applications might write 100%. Make is unique for the conditions.
994  if (nDotLen == 0)
995  nDotLen = 100;
996  if (nDashLen == 0 && aLineDash.Dashes > 0)
997  nDashLen = 100;
998  bIsConverted = true;
999  if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
1000  {
1001  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
1002  }
1003  else if (nDotLen == 400 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
1004  {
1005  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
1006  }
1007  else if (nDotLen == 400 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
1008  {
1009  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dashDot");
1010  }
1011  else if (nDotLen == 800 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 300)
1012  {
1013  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDash");
1014  }
1015  else if (nDotLen == 800 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 300)
1016  {
1017  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDot");
1018  }
1019  else if (nDotLen == 800 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 300)
1020  {
1021  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDotDot");
1022  }
1023  else if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
1024  {
1025  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
1026  }
1027  else if (nDotLen == 300 && aLineDash.Dashes == 0 && nDashLen == 0 && nDistance == 100)
1028  {
1029  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
1030  }
1031  else if (nDotLen == 300 && aLineDash.Dashes == 1 && nDashLen == 100 && nDistance == 100)
1032  {
1033  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDot");
1034  }
1035  else if (nDotLen == 300 && aLineDash.Dashes == 2 && nDashLen == 100 && nDistance == 100)
1036  {
1037  mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
1038  }
1039  else
1040  bIsConverted = false;
1041  }
1042  // Do not map our own line styles to OOXML prstDash values, because custDash gives better results.
1043  if (!bIsConverted)
1044  {
1045  mpFS->startElementNS(XML_a, XML_custDash);
1046  // In case of hairline we would need the current pixel size. Instead use a reasonable
1047  // ersatz for it. The value is the same as SMALLEST_DASH_WIDTH in xattr.cxx.
1048  // (And it makes sure fLineWidth is not zero in below division.)
1049  double fLineWidth = nLineWidth > 0 ? nLineWidth : 26.95;
1050  int i;
1051  double fSp = bIsRelative ? aLineDash.Distance : aLineDash.Distance * 100.0 / fLineWidth;
1052  // LO uses line width, in case Distance is zero. MS Office would use a space of zero length.
1053  // So set 100% explicitly.
1054  if (aLineDash.Distance <= 0)
1055  fSp = 100.0;
1056  // In case of custDash, round caps are included in dash length in MS Office. Square caps are added
1057  // to dash length, same as in ODF. Change the length values accordingly.
1058  if (aLineCap == LineCap_ROUND && fSp > 99.0)
1059  fSp -= 99.0;
1060 
1061  if (aLineDash.Dots > 0)
1062  {
1063  double fD = bIsRelative ? aLineDash.DotLen : aLineDash.DotLen * 100.0 / fLineWidth;
1064  // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended.
1065  if (aLineDash.DotLen == 0)
1066  fD = 100.0;
1067  // Tweak dash length, see above.
1068  if (aLineCap == LineCap_ROUND && fSp > 99.0)
1069  fD += 99.0;
1070 
1071  for( i = 0; i < aLineDash.Dots; i ++ )
1072  {
1073  mpFS->singleElementNS( XML_a, XML_ds,
1074  XML_d , write1000thOfAPercent(fD),
1075  XML_sp, write1000thOfAPercent(fSp) );
1076  }
1077  }
1078  if ( aLineDash.Dashes > 0 )
1079  {
1080  double fD = bIsRelative ? aLineDash.DashLen : aLineDash.DashLen * 100.0 / fLineWidth;
1081  // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended.
1082  if (aLineDash.DashLen == 0)
1083  fD = 100.0;
1084  // Tweak dash length, see above.
1085  if (aLineCap == LineCap_ROUND && fSp > 99.0)
1086  fD += 99.0;
1087 
1088  for( i = 0; i < aLineDash.Dashes; i ++ )
1089  {
1090  mpFS->singleElementNS( XML_a , XML_ds,
1091  XML_d , write1000thOfAPercent(fD),
1092  XML_sp, write1000thOfAPercent(fSp) );
1093  }
1094  }
1095 
1096  SAL_WARN_IF(nLineWidth <= 0,
1097  "oox.shape", "while writing outline - custom dash - line width was < 0 : " << nLineWidth);
1098  SAL_WARN_IF(aLineDash.Dashes < 0,
1099  "oox.shape", "while writing outline - custom dash - number of dashes was < 0 : " << aLineDash.Dashes);
1100  SAL_WARN_IF(aLineDash.Dashes > 0 && aLineDash.DashLen <= 0,
1101  "oox.shape", "while writing outline - custom dash - dash length was < 0 : " << aLineDash.DashLen);
1102  SAL_WARN_IF(aLineDash.Dots < 0,
1103  "oox.shape", "while writing outline - custom dash - number of dots was < 0 : " << aLineDash.Dots);
1104  SAL_WARN_IF(aLineDash.Dots > 0 && aLineDash.DotLen <= 0,
1105  "oox.shape", "while writing outline - custom dash - dot length was < 0 : " << aLineDash.DotLen);
1106  SAL_WARN_IF(aLineDash.Distance <= 0,
1107  "oox.shape", "while writing outline - custom dash - distance was < 0 : " << aLineDash.Distance);
1108 
1109  mpFS->endElementNS( XML_a, XML_custDash );
1110  }
1111  }
1112 
1113  if (!bNoFill && nLineWidth > 1 && GetProperty(rXPropSet, "LineJoint"))
1114  {
1115  LineJoint eLineJoint = mAny.get<LineJoint>();
1116 
1117  if( aStyleLineJoint == LineJoint_NONE || aStyleLineJoint != eLineJoint )
1118  {
1119  // style-defined line joint does not exist, or is different from the shape's joint
1120  switch( eLineJoint )
1121  {
1122  case LineJoint_NONE:
1123  case LineJoint_BEVEL:
1124  mpFS->singleElementNS(XML_a, XML_bevel);
1125  break;
1126  default:
1127  case LineJoint_MIDDLE:
1128  case LineJoint_MITER:
1129  mpFS->singleElementNS(XML_a, XML_miter);
1130  break;
1131  case LineJoint_ROUND:
1132  mpFS->singleElementNS(XML_a, XML_round);
1133  break;
1134  }
1135  }
1136  }
1137 
1138  if( !bNoFill )
1139  {
1140  WriteLineArrow( rXPropSet, true );
1141  WriteLineArrow( rXPropSet, false );
1142  }
1143  else
1144  {
1145  mpFS->singleElementNS(XML_a, XML_noFill);
1146  }
1147 
1148  mpFS->endElementNS( XML_a, XML_ln );
1149 }
1150 
1151 const char* DrawingML::GetComponentDir() const
1152 {
1153  switch ( meDocumentType )
1154  {
1155  case DOCUMENT_DOCX: return "word";
1156  case DOCUMENT_PPTX: return "ppt";
1157  case DOCUMENT_XLSX: return "xl";
1158  }
1159 
1160  return "unknown";
1161 }
1162 
1164 {
1165  switch ( meDocumentType )
1166  {
1167  case DOCUMENT_DOCX: return "";
1168  case DOCUMENT_PPTX:
1169  case DOCUMENT_XLSX: return "../";
1170  }
1171 
1172  return "unknown";
1173 }
1174 
1175 OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia, OUString* pFileName )
1176 {
1177  GfxLink aLink = rGraphic.GetGfxLink ();
1178  OUString sMediaType;
1179  const char* pExtension = "";
1180  OUString sRelId;
1181 
1182  SvMemoryStream aStream;
1183  const void* aData = aLink.GetData();
1184  std::size_t nDataSize = aLink.GetDataSize();
1185 
1186  switch ( aLink.GetType() )
1187  {
1188  case GfxLinkType::NativeGif:
1189  sMediaType = "image/gif";
1190  pExtension = ".gif";
1191  break;
1192 
1193  // #i15508# added BMP type for better exports
1194  // export not yet active, so adding for reference (not checked)
1195  case GfxLinkType::NativeBmp:
1196  sMediaType = "image/bmp";
1197  pExtension = ".bmp";
1198  break;
1199 
1200  case GfxLinkType::NativeJpg:
1201  sMediaType = "image/jpeg";
1202  pExtension = ".jpeg";
1203  break;
1204  case GfxLinkType::NativePng:
1205  sMediaType = "image/png";
1206  pExtension = ".png";
1207  break;
1208  case GfxLinkType::NativeTif:
1209  sMediaType = "image/tiff";
1210  pExtension = ".tif";
1211  break;
1212  case GfxLinkType::NativeWmf:
1213  sMediaType = "image/x-wmf";
1214  pExtension = ".wmf";
1215  break;
1216  case GfxLinkType::NativeMet:
1217  sMediaType = "image/x-met";
1218  pExtension = ".met";
1219  break;
1220  case GfxLinkType::NativePct:
1221  sMediaType = "image/x-pict";
1222  pExtension = ".pct";
1223  break;
1224  case GfxLinkType::NativeMov:
1225  sMediaType = "application/movie";
1226  pExtension = ".MOV";
1227  break;
1228  default:
1229  {
1230  GraphicType aType = rGraphic.GetType();
1231  if ( aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile)
1232  {
1233  if ( aType == GraphicType::Bitmap )
1234  {
1235  (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::PNG );
1236  sMediaType = "image/png";
1237  pExtension = ".png";
1238  }
1239  else
1240  {
1241  (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::EMF );
1242  sMediaType = "image/x-emf";
1243  pExtension = ".emf";
1244  }
1245  }
1246  else
1247  {
1248  SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType) );
1249  /*Earlier, even in case of unhandled graphic types we were
1250  proceeding to write the image, which would eventually
1251  write an empty image with a zero size, and return a valid
1252  relationID, which is incorrect.
1253  */
1254  return sRelId;
1255  }
1256 
1257  aData = aStream.GetData();
1258  nDataSize = aStream.GetEndOfData();
1259  break;
1260  }
1261  }
1262 
1263  Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
1264  .appendAscii( GetComponentDir() )
1265  .append( "/media/image" +
1266  OUString::number(mnImageCounter) )
1267  .appendAscii( pExtension )
1268  .makeStringAndClear(),
1269  sMediaType );
1270  xOutStream->writeBytes( Sequence< sal_Int8 >( static_cast<const sal_Int8*>(aData), nDataSize ) );
1271  xOutStream->closeOutput();
1272 
1273  const OString sRelPathToMedia = "media/image";
1274  OString sRelationCompPrefix;
1275  if ( bRelPathToMedia )
1276  sRelationCompPrefix = "../";
1277  else
1278  sRelationCompPrefix = GetRelationCompPrefix();
1279  OUString sPath = OUStringBuffer()
1280  .appendAscii( sRelationCompPrefix.getStr() )
1281  .appendAscii( sRelPathToMedia.getStr() )
1282  .append( static_cast<sal_Int32>(mnImageCounter ++) )
1283  .appendAscii( pExtension )
1284  .makeStringAndClear();
1285  sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1287  sPath );
1288 
1289  if (pFileName)
1290  *pFileName = sPath;
1291  return sRelId;
1292 }
1293 
1294 void DrawingML::WriteMediaNonVisualProperties(const css::uno::Reference<css::drawing::XShape>& xShape)
1295 {
1296  SdrMediaObj* pMediaObj = dynamic_cast<SdrMediaObj*>(SdrObject::getSdrObjectFromXShape(xShape));
1297  if (!pMediaObj)
1298  return;
1299 
1300  // extension
1301  OUString aExtension;
1302  const OUString& rURL(pMediaObj->getURL());
1303  int nLastDot = rURL.lastIndexOf('.');
1304  if (nLastDot >= 0)
1305  aExtension = rURL.copy(nLastDot);
1306 
1307  bool bEmbed = rURL.startsWith("vnd.sun.star.Package:");
1308  Relationship eMediaType = Relationship::VIDEO;
1309 
1310  // mime type
1311 #if HAVE_FEATURE_AVMEDIA
1312  OUString aMimeType(pMediaObj->getMediaProperties().getMimeType());
1313 #else
1314  OUString aMimeType("none");
1315 #endif
1316  if (aMimeType == "application/vnd.sun.star.media")
1317  {
1318  // try to set something better
1319  // TODO fix the importer to actually set the mimetype on import
1320  if (aExtension.equalsIgnoreAsciiCase(".avi"))
1321  aMimeType = "video/x-msvideo";
1322  else if (aExtension.equalsIgnoreAsciiCase(".flv"))
1323  aMimeType = "video/x-flv";
1324  else if (aExtension.equalsIgnoreAsciiCase(".mp4"))
1325  aMimeType = "video/mp4";
1326  else if (aExtension.equalsIgnoreAsciiCase(".mov"))
1327  aMimeType = "video/quicktime";
1328  else if (aExtension.equalsIgnoreAsciiCase(".ogv"))
1329  aMimeType = "video/ogg";
1330  else if (aExtension.equalsIgnoreAsciiCase(".wmv"))
1331  aMimeType = "video/x-ms-wmv";
1332  else if (aExtension.equalsIgnoreAsciiCase(".wav"))
1333  {
1334  aMimeType = "audio/x-wav";
1335  eMediaType = Relationship::AUDIO;
1336  }
1337  else if (aExtension.equalsIgnoreAsciiCase(".m4a"))
1338  {
1339  aMimeType = "audio/mp4";
1340  eMediaType = Relationship::AUDIO;
1341  }
1342  }
1343 
1344  OUString aVideoFileRelId;
1345  OUString aMediaRelId;
1346 
1347  if (bEmbed)
1348  {
1349  // copy the video stream
1350  Reference<XOutputStream> xOutStream = mpFB->openFragmentStream(OUStringBuffer()
1351  .appendAscii(GetComponentDir())
1352  .append("/media/media" +
1353  OUString::number(mnImageCounter) +
1354  aExtension)
1355  .makeStringAndClear(),
1356  aMimeType);
1357 
1358  uno::Reference<io::XInputStream> xInputStream(pMediaObj->GetInputStream());
1359  comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream);
1360 
1361  xOutStream->closeOutput();
1362 
1363  // create the relation
1364  OUString aPath = OUStringBuffer().appendAscii(GetRelationCompPrefix())
1365  .append("media/media" + OUString::number(mnImageCounter++) + aExtension)
1366  .makeStringAndClear();
1367  aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), aPath);
1368  aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), aPath);
1369  }
1370  else
1371  {
1372  aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), rURL);
1373  aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), rURL);
1374  }
1375 
1376  GetFS()->startElementNS(XML_p, XML_nvPr);
1377 
1378  GetFS()->singleElementNS(XML_a, eMediaType == Relationship::VIDEO ? XML_videoFile : XML_audioFile,
1379  FSNS(XML_r, XML_link), aVideoFileRelId);
1380 
1381  GetFS()->startElementNS(XML_p, XML_extLst);
1382  // media extensions; google this ID for details
1383  GetFS()->startElementNS(XML_p, XML_ext, XML_uri, "{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}");
1384 
1385  GetFS()->singleElementNS(XML_p14, XML_media,
1386  bEmbed? FSNS(XML_r, XML_embed): FSNS(XML_r, XML_link), aMediaRelId);
1387 
1388  GetFS()->endElementNS(XML_p, XML_ext);
1389  GetFS()->endElementNS(XML_p, XML_extLst);
1390 
1391  GetFS()->endElementNS(XML_p, XML_nvPr);
1392 }
1393 
1394 void DrawingML::WriteImageBrightnessContrastTransparence(uno::Reference<beans::XPropertySet> const & rXPropSet)
1395 {
1396  sal_Int16 nBright = 0;
1397  sal_Int32 nContrast = 0;
1398  sal_Int32 nTransparence = 0;
1399 
1400  if (GetProperty(rXPropSet, "AdjustLuminance"))
1401  nBright = mAny.get<sal_Int16>();
1402  if (GetProperty(rXPropSet, "AdjustContrast"))
1403  nContrast = mAny.get<sal_Int32>();
1404  // Used for shapes with picture fill
1405  if (GetProperty(rXPropSet, "FillTransparence"))
1406  nTransparence = mAny.get<sal_Int32>();
1407  // Used for pictures
1408  if (nTransparence == 0 && GetProperty(rXPropSet, "Transparency"))
1409  nTransparence = static_cast<sal_Int32>(mAny.get<sal_Int16>());
1410 
1411  if (GetProperty(rXPropSet, "GraphicColorMode"))
1412  {
1413  drawing::ColorMode aColorMode;
1414  mAny >>= aColorMode;
1415  if (aColorMode == drawing::ColorMode_GREYS)
1416  mpFS->singleElementNS(XML_a, XML_grayscl);
1417  else if (aColorMode == drawing::ColorMode_MONO)
1418  //black/white has a 0,5 threshold in LibreOffice
1419  mpFS->singleElementNS(XML_a, XML_biLevel, XML_thresh, OString::number(50000));
1420  else if (aColorMode == drawing::ColorMode_WATERMARK)
1421  {
1422  //map watermark with mso washout
1423  nBright = 70;
1424  nContrast = -70;
1425  }
1426  }
1427 
1428 
1429  if (nBright || nContrast)
1430  {
1431  mpFS->singleElementNS(XML_a, XML_lum,
1432  XML_bright, sax_fastparser::UseIf(OString::number(nBright * 1000), nBright != 0),
1433  XML_contrast, sax_fastparser::UseIf(OString::number(nContrast * 1000), nContrast != 0));
1434  }
1435 
1436  if (nTransparence)
1437  {
1438  sal_Int32 nAlphaMod = (100 - nTransparence ) * PER_PERCENT;
1439  mpFS->singleElementNS(XML_a, XML_alphaModFix, XML_amt, OString::number(nAlphaMod));
1440  }
1441 }
1442 
1443 OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const & rXPropSet,
1444  uno::Reference<graphic::XGraphic> const & rxGraphic,
1445  bool bRelPathToMedia)
1446 {
1447  OUString sRelId;
1448  OUString sFileName;
1449 
1450  if (!rxGraphic.is())
1451  return sRelId;
1452 
1453  Graphic aGraphic(rxGraphic);
1454  if (mpTextExport)
1455  {
1456  BitmapChecksum nChecksum = aGraphic.GetChecksum();
1457  sRelId = mpTextExport->FindRelId(nChecksum);
1458  sFileName = mpTextExport->FindFileName(nChecksum);
1459  }
1460  if (sRelId.isEmpty())
1461  {
1462  sRelId = WriteImage(aGraphic, bRelPathToMedia, &sFileName);
1463  if (mpTextExport)
1464  {
1465  BitmapChecksum nChecksum = aGraphic.GetChecksum();
1466  mpTextExport->CacheRelId(nChecksum, sRelId, sFileName);
1467  }
1468  }
1469  else
1470  {
1471  // Include the same relation again. This makes it possible to
1472  // reuse an image across different headers.
1473  sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1475  sFileName );
1476  }
1477 
1478  mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId);
1479 
1481 
1482  WriteArtisticEffect(rXPropSet);
1483 
1484  mpFS->endElementNS(XML_a, XML_blip);
1485 
1486  return sRelId;
1487 }
1488 
1489 void DrawingML::WriteXGraphicBlipMode(uno::Reference<beans::XPropertySet> const & rXPropSet,
1490  uno::Reference<graphic::XGraphic> const & rxGraphic)
1491 {
1492  BitmapMode eBitmapMode(BitmapMode_NO_REPEAT);
1493  if (GetProperty(rXPropSet, "FillBitmapMode"))
1494  mAny >>= eBitmapMode;
1495 
1496  SAL_INFO("oox.shape", "fill bitmap mode: " << int(eBitmapMode));
1497 
1498  switch (eBitmapMode)
1499  {
1500  case BitmapMode_REPEAT:
1501  mpFS->singleElementNS(XML_a, XML_tile);
1502  break;
1503  case BitmapMode_STRETCH:
1504  WriteXGraphicStretch(rXPropSet, rxGraphic);
1505  break;
1506  default:
1507  break;
1508  }
1509 }
1510 
1511 void DrawingML::WriteBlipOrNormalFill( const Reference< XPropertySet >& xPropSet, const OUString& rURLPropName )
1512 {
1513  // check for blip and otherwise fall back to normal fill
1514  // we always store normal fill properties but OOXML
1515  // uses a choice between our fill props and BlipFill
1516  if (GetProperty ( xPropSet, rURLPropName ))
1517  WriteBlipFill( xPropSet, rURLPropName );
1518  else
1519  WriteFill(xPropSet);
1520 }
1521 
1522 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName )
1523 {
1524  WriteBlipFill( rXPropSet, sURLPropName, XML_a );
1525 }
1526 
1527 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName, sal_Int32 nXmlNamespace )
1528 {
1529  if ( !GetProperty( rXPropSet, sURLPropName ) )
1530  return;
1531 
1532  uno::Reference<graphic::XGraphic> xGraphic;
1533  if (mAny.has<uno::Reference<awt::XBitmap>>())
1534  {
1535  uno::Reference<awt::XBitmap> xBitmap = mAny.get<uno::Reference<awt::XBitmap>>();
1536  if (xBitmap.is())
1537  xGraphic.set(xBitmap, uno::UNO_QUERY);
1538  }
1539  else if (mAny.has<uno::Reference<graphic::XGraphic>>())
1540  {
1541  xGraphic = mAny.get<uno::Reference<graphic::XGraphic>>();
1542  }
1543 
1544  if (xGraphic.is())
1545  {
1546  bool bWriteMode = false;
1547  if (sURLPropName == "FillBitmap" || sURLPropName == "BackGraphic")
1548  bWriteMode = true;
1549  WriteXGraphicBlipFill(rXPropSet, xGraphic, nXmlNamespace, bWriteMode);
1550  }
1551 }
1552 
1553 void DrawingML::WriteXGraphicBlipFill(uno::Reference<beans::XPropertySet> const & rXPropSet,
1554  uno::Reference<graphic::XGraphic> const & rxGraphic,
1555  sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia)
1556 {
1557  if (!rxGraphic.is() )
1558  return;
1559 
1560  mpFS->startElementNS(nXmlNamespace , XML_blipFill, XML_rotWithShape, "0");
1561 
1562  WriteXGraphicBlip(rXPropSet, rxGraphic, bRelPathToMedia);
1563 
1564  if (GetDocumentType() != DOCUMENT_DOCX)
1565  {
1566  // Write the crop rectangle of Impress as a source rectangle.
1567  WriteSrcRectXGraphic(rXPropSet, rxGraphic);
1568  }
1569 
1570  if (bWriteMode)
1571  {
1572  WriteXGraphicBlipMode(rXPropSet, rxGraphic);
1573  }
1574  else if(GetProperty(rXPropSet, "FillBitmapStretch"))
1575  {
1576  bool bStretch = mAny.get<bool>();
1577 
1578  if (bStretch)
1579  {
1580  WriteXGraphicStretch(rXPropSet, rxGraphic);
1581  }
1582  }
1583  mpFS->endElementNS(nXmlNamespace, XML_blipFill);
1584 }
1585 
1586 void DrawingML::WritePattFill( const Reference< XPropertySet >& rXPropSet )
1587 {
1588  if ( GetProperty( rXPropSet, "FillHatch" ) )
1589  {
1590  drawing::Hatch aHatch;
1591  mAny >>= aHatch;
1592  WritePattFill(rXPropSet, aHatch);
1593  }
1594 }
1595 
1596 void DrawingML::WritePattFill(const Reference<XPropertySet>& rXPropSet, const css::drawing::Hatch& rHatch)
1597 {
1598  mpFS->startElementNS(XML_a, XML_pattFill, XML_prst, GetHatchPattern(rHatch));
1599 
1600  mpFS->startElementNS(XML_a, XML_fgClr);
1601  WriteColor(::Color(ColorTransparency, rHatch.Color));
1602  mpFS->endElementNS( XML_a , XML_fgClr );
1603 
1604  ::Color nColor = COL_WHITE;
1605  sal_Int32 nAlpha = 0;
1606 
1607  if ( GetProperty( rXPropSet, "FillBackground" ) )
1608  {
1609  bool isBackgroundFilled = false;
1610  mAny >>= isBackgroundFilled;
1611  if( isBackgroundFilled )
1612  {
1613  nAlpha = MAX_PERCENT;
1614 
1615  if( GetProperty( rXPropSet, "FillColor" ) )
1616  {
1617  mAny >>= nColor;
1618  }
1619  }
1620  }
1621 
1622  mpFS->startElementNS(XML_a, XML_bgClr);
1623  WriteColor(nColor, nAlpha);
1624  mpFS->endElementNS( XML_a , XML_bgClr );
1625 
1626  mpFS->endElementNS( XML_a , XML_pattFill );
1627 }
1628 
1629 void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet,
1630  Size const & rOriginalSize,
1631  MapMode const & rMapMode)
1632 {
1633  if (!GetProperty(rXPropSet, "GraphicCrop"))
1634  return;
1635 
1636  css::text::GraphicCrop aGraphicCropStruct;
1637  mAny >>= aGraphicCropStruct;
1638 
1639  if(GetProperty(rXPropSet, "CustomShapeGeometry"))
1640  {
1641  // tdf#134210 GraphicCrop property is handled in import filter because of LibreOffice has not core
1642  // feature. We cropped the bitmap physically and MSO shouldn't crop bitmap one more time. When we
1643  // have core feature for graphic cropping in custom shapes, we should uncomment the code anymore.
1644 
1645  mpFS->singleElementNS( XML_a, XML_srcRect);
1646  }
1647  else
1648  {
1649  Size aOriginalSize(rOriginalSize);
1650 
1651  // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over.
1652  if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
1653  aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM));
1654 
1655  if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) )
1656  {
1657  mpFS->singleElementNS( XML_a, XML_srcRect,
1658  XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())),
1659  XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())),
1660  XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())),
1661  XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) );
1662  }
1663  }
1664 }
1665 
1666 void DrawingML::WriteSrcRectXGraphic(uno::Reference<beans::XPropertySet> const & rxPropertySet,
1667  uno::Reference<graphic::XGraphic> const & rxGraphic)
1668 {
1669  Graphic aGraphic(rxGraphic);
1670  Size aOriginalSize = aGraphic.GetPrefSize();
1671  const MapMode& rMapMode = aGraphic.GetPrefMapMode();
1672  WriteGraphicCropProperties(rxPropertySet, aOriginalSize, rMapMode);
1673 }
1674 
1675 void DrawingML::WriteXGraphicStretch(uno::Reference<beans::XPropertySet> const & rXPropSet,
1676  uno::Reference<graphic::XGraphic> const & rxGraphic)
1677 {
1678  if (GetDocumentType() != DOCUMENT_DOCX)
1679  {
1680  // Limiting the area used for stretching is not supported in Impress.
1681  mpFS->singleElementNS(XML_a, XML_stretch);
1682  return;
1683  }
1684 
1685  mpFS->startElementNS(XML_a, XML_stretch);
1686 
1687  bool bCrop = false;
1688  if (GetProperty(rXPropSet, "GraphicCrop"))
1689  {
1690  css::text::GraphicCrop aGraphicCropStruct;
1691  mAny >>= aGraphicCropStruct;
1692 
1693  if ((0 != aGraphicCropStruct.Left)
1694  || (0 != aGraphicCropStruct.Top)
1695  || (0 != aGraphicCropStruct.Right)
1696  || (0 != aGraphicCropStruct.Bottom))
1697  {
1698  Graphic aGraphic(rxGraphic);
1699  Size aOriginalSize(aGraphic.GetPrefSize());
1700  mpFS->singleElementNS(XML_a, XML_fillRect,
1701  XML_l, OString::number(((aGraphicCropStruct.Left) * 100000) / aOriginalSize.Width()),
1702  XML_t, OString::number(((aGraphicCropStruct.Top) * 100000) / aOriginalSize.Height()),
1703  XML_r, OString::number(((aGraphicCropStruct.Right) * 100000) / aOriginalSize.Width()),
1704  XML_b, OString::number(((aGraphicCropStruct.Bottom) * 100000) / aOriginalSize.Height()));
1705  bCrop = true;
1706  }
1707  }
1708 
1709  if (!bCrop)
1710  {
1711  mpFS->singleElementNS(XML_a, XML_fillRect);
1712  }
1713 
1714  mpFS->endElementNS(XML_a, XML_stretch);
1715 }
1716 
1717 namespace
1718 {
1719 bool IsTopGroupObj(const uno::Reference<drawing::XShape>& xShape)
1720 {
1722  if (!pObject)
1723  return false;
1724 
1725  if (pObject->getParentSdrObjectFromSdrObject())
1726  return false;
1727 
1728  return pObject->IsGroupObject();
1729 }
1730 }
1731 
1733  sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, sal_Int32 nRotation, bool bIsGroupShape)
1734 {
1735 
1736  mpFS->startElementNS( nXmlNamespace, XML_xfrm,
1737  XML_flipH, sax_fastparser::UseIf("1", bFlipH),
1738  XML_flipV, sax_fastparser::UseIf("1", bFlipV),
1739  XML_rot, sax_fastparser::UseIf(OString::number(nRotation), nRotation % 21600000 != 0));
1740 
1741  sal_Int32 nLeft = rRect.Left();
1742  sal_Int32 nTop = rRect.Top();
1743  if (GetDocumentType() == DOCUMENT_DOCX && !m_xParent.is())
1744  {
1745  nLeft = 0;
1746  nTop = 0;
1747  }
1748  sal_Int32 nChildLeft = nLeft;
1749  sal_Int32 nChildTop = nTop;
1750 
1751  mpFS->singleElementNS(XML_a, XML_off,
1752  XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
1753  XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
1754  mpFS->singleElementNS(XML_a, XML_ext,
1755  XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1756  XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1757 
1758  if (bIsGroupShape && (GetDocumentType() != DOCUMENT_DOCX || IsTopGroupObj(xShape)))
1759  {
1760  mpFS->singleElementNS(XML_a, XML_chOff,
1761  XML_x, OString::number(oox::drawingml::convertHmmToEmu(nChildLeft)),
1762  XML_y, OString::number(oox::drawingml::convertHmmToEmu(nChildTop)));
1763  mpFS->singleElementNS(XML_a, XML_chExt,
1764  XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1765  XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1766  }
1767 
1768  mpFS->endElementNS( nXmlNamespace, XML_xfrm );
1769 }
1770 
1771 void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, bool bSuppressRotation, bool bSuppressFlipping, bool bFlippedBeforeRotation )
1772 {
1773  SAL_INFO("oox.shape", "write shape transformation");
1774 
1775  Degree100 nRotation;
1776  Degree100 nCameraRotation;
1777  awt::Point aPos = rXShape->getPosition();
1778  awt::Size aSize = rXShape->getSize();
1779 
1780  bool bFlipHWrite = bFlipH && !bSuppressFlipping;
1781  bool bFlipVWrite = bFlipV && !bSuppressFlipping;
1782  bFlipH = bFlipH && !bFlippedBeforeRotation;
1783  bFlipV = bFlipV && !bFlippedBeforeRotation;
1784 
1785  if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
1786  {
1787  awt::Point aParentPos = m_xParent->getPosition();
1788  aPos.X -= aParentPos.X;
1789  aPos.Y -= aParentPos.Y;
1790  }
1791 
1792  if ( aSize.Width < 0 )
1793  aSize.Width = 1000;
1794  if ( aSize.Height < 0 )
1795  aSize.Height = 1000;
1796  if (!bSuppressRotation)
1797  {
1798  SdrObject* pShape = SdrObject::getSdrObjectFromXShape(rXShape);
1799  nRotation = pShape ? pShape->GetRotateAngle() : 0_deg100;
1800  if ( GetDocumentType() != DOCUMENT_DOCX )
1801  {
1802  int faccos=bFlipV ? -1 : 1;
1803  int facsin=bFlipH ? -1 : 1;
1804  aPos.X-=(1-faccos*cos(nRotation.get()*F_PI18000))*aSize.Width/2-facsin*sin(nRotation.get()*F_PI18000)*aSize.Height/2;
1805  aPos.Y-=(1-faccos*cos(nRotation.get()*F_PI18000))*aSize.Height/2+facsin*sin(nRotation.get()*F_PI18000)*aSize.Width/2;
1806  }
1807  else if (m_xParent.is() && nRotation != 0_deg100)
1808  {
1809  // Position for rotated shapes inside group is not set by DocxSdrExport.
1810  basegfx::B2DRange aRect(-aSize.Width / 2.0, -aSize.Height / 2.0, aSize.Width / 2.0,
1811  aSize.Height / 2.0);
1812  basegfx::B2DHomMatrix aRotateMatrix =
1814  aRect.transform(aRotateMatrix);
1815  aPos.X += -aSize.Width / 2.0 - aRect.getMinX();
1816  aPos.Y += -aSize.Height / 2.0 - aRect.getMinY();
1817  }
1818 
1819  // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
1820  uno::Reference<beans::XPropertySet> xPropertySet(rXShape, uno::UNO_QUERY);
1821  uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
1822  if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
1823  {
1824  sal_Int32 nTmp;
1825  if (xPropertySet->getPropertyValue("RotateAngle") >>= nTmp)
1826  nRotation = Degree100(nTmp);
1827  }
1828  // tdf#133037: restore original rotate angle before output
1829  if (nRotation && xPropertySetInfo->hasPropertyByName(UNO_NAME_MISC_OBJ_INTEROPGRABBAG))
1830  {
1831  uno::Sequence<beans::PropertyValue> aGrabBagProps;
1832  xPropertySet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= aGrabBagProps;
1833  auto p3DEffectProps = std::find_if(std::cbegin(aGrabBagProps), std::cend(aGrabBagProps),
1834  [](const PropertyValue& rProp) { return rProp.Name == "3DEffectProperties"; });
1835  if (p3DEffectProps != std::cend(aGrabBagProps))
1836  {
1837  uno::Sequence<beans::PropertyValue> a3DEffectProps;
1838  p3DEffectProps->Value >>= a3DEffectProps;
1839  auto pCameraProps = std::find_if(std::cbegin(a3DEffectProps), std::cend(a3DEffectProps),
1840  [](const PropertyValue& rProp) { return rProp.Name == "Camera"; });
1841  if (pCameraProps != std::cend(a3DEffectProps))
1842  {
1843  uno::Sequence<beans::PropertyValue> aCameraProps;
1844  pCameraProps->Value >>= aCameraProps;
1845  auto pZRotationProp = std::find_if(std::cbegin(aCameraProps), std::cend(aCameraProps),
1846  [](const PropertyValue& rProp) { return rProp.Name == "rotRev"; });
1847  if (pZRotationProp != std::cend(aCameraProps))
1848  {
1849  sal_Int32 nTmp = 0;
1850  pZRotationProp->Value >>= nTmp;
1851  nCameraRotation = NormAngle36000(Degree100(nTmp / -600));
1852  }
1853  }
1854  }
1855  }
1856  }
1857 
1858  // OOXML flips shapes before rotating them.
1859  if(bFlipH != bFlipV)
1860  nRotation = Degree100(nRotation.get() * -1 + 36000);
1861 
1862  WriteTransformation(rXShape, tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)), nXmlNamespace,
1863  bFlipHWrite, bFlipVWrite, ExportRotateClockwisify(nRotation + nCameraRotation), IsGroupShape( rXShape ));
1864 }
1865 
1866 static OUString lcl_GetTarget(const css::uno::Reference<css::frame::XModel>& xModel, std::u16string_view rURL)
1867 {
1868  Reference<drawing::XDrawPagesSupplier> xDPS(xModel, uno::UNO_QUERY_THROW);
1869  Reference<drawing::XDrawPages> xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW);
1870  sal_uInt32 nPageCount = xDrawPages->getCount();
1871  OUString sTarget;
1872 
1873  for (sal_uInt32 i = 0; i < nPageCount; ++i)
1874  {
1875  Reference<XDrawPage> xDrawPage;
1876  xDrawPages->getByIndex(i) >>= xDrawPage;
1877  Reference<container::XNamed> xNamed(xDrawPage, UNO_QUERY);
1878  if (!xNamed)
1879  continue;
1880  OUString sSlideName = "#" + xNamed->getName();
1881  if (rURL == sSlideName)
1882  {
1883  sTarget = "slide" + OUString::number(i + 1) + ".xml";
1884  break;
1885  }
1886  }
1887 
1888  return sTarget;
1889 }
1890 
1891 void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement,
1892  bool bCheckDirect,bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
1893  sal_Int16 nScriptType, const Reference< XPropertySet >& rXShapePropSet)
1894 {
1895  Reference< XPropertySet > rXPropSet = rRun;
1896  Reference< XPropertyState > rXPropState( rRun, UNO_QUERY );
1897  OUString usLanguage;
1898  PropertyState eState;
1899  bool bComplex = ( nScriptType == css::i18n::ScriptType::COMPLEX );
1900  const char* bold = "0";
1901  const char* italic = nullptr;
1902  const char* underline = nullptr;
1903  const char* strikeout = nullptr;
1904  const char* cap = nullptr;
1905  sal_Int32 nSize = 1800;
1906  sal_Int32 nCharEscapement = 0;
1907  sal_Int32 nCharKerning = 0;
1908 
1909  if ( nElement == XML_endParaRPr && rbOverridingCharHeight )
1910  {
1911  nSize = rnCharHeight;
1912  }
1913  else if (GetProperty(rXPropSet, "CharHeight"))
1914  {
1915  nSize = static_cast<sal_Int32>(100*(*o3tl::doAccess<float>(mAny)));
1916  if ( nElement == XML_rPr || nElement == XML_defRPr )
1917  {
1918  rbOverridingCharHeight = true;
1919  rnCharHeight = nSize;
1920  }
1921  }
1922 
1923  if (GetProperty(rXPropSet, "CharKerning"))
1924  nCharKerning = static_cast<sal_Int32>(*o3tl::doAccess<sal_Int16>(mAny));
1931  nCharKerning = ((nCharKerning * 720)-360) / 254;
1932 
1933  if ((bComplex && GetProperty(rXPropSet, "CharWeightComplex"))
1934  || GetProperty(rXPropSet, "CharWeight"))
1935  {
1936  if ( *o3tl::doAccess<float>(mAny) >= awt::FontWeight::SEMIBOLD )
1937  bold = "1";
1938  }
1939 
1940  if ((bComplex && GetProperty(rXPropSet, "CharPostureComplex"))
1941  || GetProperty(rXPropSet, "CharPosture"))
1942  switch ( *o3tl::doAccess<awt::FontSlant>(mAny) )
1943  {
1944  case awt::FontSlant_OBLIQUE :
1945  case awt::FontSlant_ITALIC :
1946  italic = "1";
1947  break;
1948  default:
1949  break;
1950  }
1951 
1952  if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderline", eState)
1953  && eState == beans::PropertyState_DIRECT_VALUE)
1954  || GetProperty(rXPropSet, "CharUnderline"))
1955  {
1956  switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1957  {
1958  case awt::FontUnderline::SINGLE :
1959  underline = "sng";
1960  break;
1961  case awt::FontUnderline::DOUBLE :
1962  underline = "dbl";
1963  break;
1964  case awt::FontUnderline::DOTTED :
1965  underline = "dotted";
1966  break;
1967  case awt::FontUnderline::DASH :
1968  underline = "dash";
1969  break;
1970  case awt::FontUnderline::LONGDASH :
1971  underline = "dashLong";
1972  break;
1973  case awt::FontUnderline::DASHDOT :
1974  underline = "dotDash";
1975  break;
1976  case awt::FontUnderline::DASHDOTDOT :
1977  underline = "dotDotDash";
1978  break;
1979  case awt::FontUnderline::WAVE :
1980  underline = "wavy";
1981  break;
1982  case awt::FontUnderline::DOUBLEWAVE :
1983  underline = "wavyDbl";
1984  break;
1985  case awt::FontUnderline::BOLD :
1986  underline = "heavy";
1987  break;
1988  case awt::FontUnderline::BOLDDOTTED :
1989  underline = "dottedHeavy";
1990  break;
1991  case awt::FontUnderline::BOLDDASH :
1992  underline = "dashHeavy";
1993  break;
1994  case awt::FontUnderline::BOLDLONGDASH :
1995  underline = "dashLongHeavy";
1996  break;
1997  case awt::FontUnderline::BOLDDASHDOT :
1998  underline = "dotDashHeavy";
1999  break;
2000  case awt::FontUnderline::BOLDDASHDOTDOT :
2001  underline = "dotDotDashHeavy";
2002  break;
2003  case awt::FontUnderline::BOLDWAVE :
2004  underline = "wavyHeavy";
2005  break;
2006  }
2007  }
2008 
2009  if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharStrikeout", eState)
2010  && eState == beans::PropertyState_DIRECT_VALUE)
2011  || GetProperty(rXPropSet, "CharStrikeout"))
2012  {
2013  switch ( *o3tl::doAccess<sal_Int16>(mAny) )
2014  {
2015  case awt::FontStrikeout::NONE :
2016  strikeout = "noStrike";
2017  break;
2018  case awt::FontStrikeout::SINGLE :
2019  // LibO supports further values of character
2020  // strikeout, OOXML standard (20.1.10.78,
2021  // ST_TextStrikeType) however specifies only
2022  // 3 - single, double and none. Approximate
2023  // the remaining ones by single strike (better
2024  // some strike than none at all).
2025  // TODO: figure out how to do this better
2026  case awt::FontStrikeout::BOLD :
2027  case awt::FontStrikeout::SLASH :
2028  case awt::FontStrikeout::X :
2029  strikeout = "sngStrike";
2030  break;
2031  case awt::FontStrikeout::DOUBLE :
2032  strikeout = "dblStrike";
2033  break;
2034  }
2035  }
2036 
2037  bool bLang = false;
2038  switch(nScriptType)
2039  {
2040  case css::i18n::ScriptType::ASIAN:
2041  bLang = GetProperty(rXPropSet, "CharLocaleAsian"); break;
2042  case css::i18n::ScriptType::COMPLEX:
2043  bLang = GetProperty(rXPropSet, "CharLocaleComplex"); break;
2044  default:
2045  bLang = GetProperty(rXPropSet, "CharLocale"); break;
2046  }
2047 
2048  if (bLang)
2049  {
2050  css::lang::Locale aLocale;
2051  mAny >>= aLocale;
2052  LanguageTag aLanguageTag( aLocale);
2053  if (!aLanguageTag.isSystemLocale())
2054  usLanguage = aLanguageTag.getBcp47MS();
2055  }
2056 
2057  if (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapement", eState)
2058  && eState == beans::PropertyState_DIRECT_VALUE)
2059  mAny >>= nCharEscapement;
2060 
2061  if (nCharEscapement
2062  && (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapementHeight", eState)
2063  && eState == beans::PropertyState_DIRECT_VALUE))
2064  {
2065  sal_uInt32 nCharEscapementHeight = 0;
2066  mAny >>= nCharEscapementHeight;
2067  nSize = (nSize * nCharEscapementHeight) / 100;
2068  // MSO uses default ~58% size
2069  nSize = (nSize / 0.58);
2070  }
2071 
2072  if (GetProperty(rXPropSet, "CharCaseMap"))
2073  {
2074  switch ( *o3tl::doAccess<sal_Int16>(mAny) )
2075  {
2076  case CaseMap::UPPERCASE :
2077  cap = "all";
2078  break;
2079  case CaseMap::SMALLCAPS :
2080  cap = "small";
2081  break;
2082  }
2083  }
2084 
2085  mpFS->startElementNS( XML_a, nElement,
2086  XML_b, bold,
2087  XML_i, italic,
2088  XML_lang, sax_fastparser::UseIf(usLanguage, !usLanguage.isEmpty()),
2089  XML_sz, OString::number(nSize),
2090  // For Condensed character spacing spc value is negative.
2091  XML_spc, sax_fastparser::UseIf(OString::number(nCharKerning), nCharKerning != 0),
2092  XML_strike, strikeout,
2093  XML_u, underline,
2094  XML_baseline, sax_fastparser::UseIf(OString::number(nCharEscapement*1000), nCharEscapement != 0),
2095  XML_cap, cap );
2096 
2097  // Fontwork-shapes in LO have text outline and fill from shape stroke and shape fill
2098  // PowerPoint has this as run properties
2099  if (IsFontworkShape(rXShapePropSet))
2100  {
2101  WriteOutline(rXShapePropSet);
2102  WriteBlipOrNormalFill(rXShapePropSet, "Graphic");
2103  WriteShapeEffects(rXShapePropSet);
2104  }
2105  else
2106  {
2107  // mso doesn't like text color to be placed after typeface
2108  if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState)
2109  && eState == beans::PropertyState_DIRECT_VALUE)
2110  || GetProperty(rXPropSet, "CharColor"))
2111  {
2112  ::Color color( ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny) );
2113  SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO));
2114 
2115  // WriteSolidFill() handles MAX_PERCENT as "no transparency".
2116  sal_Int32 nTransparency = MAX_PERCENT;
2117  if (rXPropSet->getPropertySetInfo()->hasPropertyByName("CharTransparence"))
2118  {
2119  rXPropSet->getPropertyValue("CharTransparence") >>= nTransparency;
2120  // UNO scale is 0..100, OOXML scale is 0..100000; also UNO tracks transparency, OOXML
2121  // tracks opacity.
2122  nTransparency = MAX_PERCENT - (nTransparency * PER_PERCENT);
2123  }
2124 
2125  // tdf#104219 In LibreOffice and MS Office, there are two types of colors:
2126  // Automatic and Fixed. OOXML is setting automatic color, by not providing color.
2127  if( color != COL_AUTO )
2128  {
2129  color.SetAlpha(255);
2130  // TODO: special handle embossed/engraved
2131  WriteSolidFill(color, nTransparency);
2132  }
2133  }
2134  }
2135 
2136  // tdf#128096, exporting XML_highlight to docx already works fine,
2137  // so make sure this code is only run when exporting to pptx, just in case
2138  if (GetDocumentType() == DOCUMENT_PPTX)
2139  {
2140  if (GetProperty(rXPropSet, "CharBackColor"))
2141  {
2142  ::Color color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny));
2143  if( color != COL_AUTO )
2144  {
2145  mpFS->startElementNS(XML_a, XML_highlight);
2146  WriteColor( color );
2147  mpFS->endElementNS( XML_a, XML_highlight );
2148  }
2149  }
2150  }
2151 
2152  if (underline
2153  && ((bCheckDirect
2154  && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderlineColor", eState)
2155  && eState == beans::PropertyState_DIRECT_VALUE)
2156  || GetProperty(rXPropSet, "CharUnderlineColor")))
2157  {
2158  ::Color color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(mAny));
2159  // if color is automatic, then we shouldn't write information about color but to take color from character
2160  if( color != COL_AUTO )
2161  {
2162  mpFS->startElementNS(XML_a, XML_uFill);
2163  WriteSolidFill( color );
2164  mpFS->endElementNS( XML_a, XML_uFill );
2165  }
2166  else
2167  {
2168  mpFS->singleElementNS(XML_a, XML_uFillTx);
2169  }
2170  }
2171 
2172  if (GetProperty(rXPropSet, "CharFontName"))
2173  {
2174  const char* const pitch = nullptr;
2175  const char* const charset = nullptr;
2176  OUString usTypeface;
2177 
2178  mAny >>= usTypeface;
2179  OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
2180  if (!aSubstName.isEmpty())
2181  usTypeface = aSubstName;
2182 
2183  mpFS->singleElementNS( XML_a, XML_latin,
2184  XML_typeface, usTypeface,
2185  XML_pitchFamily, pitch,
2186  XML_charset, charset );
2187  }
2188 
2189  if ((bComplex
2190  && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameComplex", eState)
2191  && eState == beans::PropertyState_DIRECT_VALUE))
2192  || (!bComplex
2193  && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameAsian", eState)
2194  && eState == beans::PropertyState_DIRECT_VALUE)))
2195  {
2196  const char* const pitch = nullptr;
2197  const char* const charset = nullptr;
2198  OUString usTypeface;
2199 
2200  mAny >>= usTypeface;
2201  OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
2202  if (!aSubstName.isEmpty())
2203  usTypeface = aSubstName;
2204 
2205  mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea,
2206  XML_typeface, usTypeface,
2207  XML_pitchFamily, pitch,
2208  XML_charset, charset );
2209  }
2210 
2211  if( bIsField )
2212  {
2213  Reference< XTextField > rXTextField;
2214  if (GetProperty(rXPropSet, "TextField"))
2215  mAny >>= rXTextField;
2216  if( rXTextField.is() )
2217  rXPropSet.set( rXTextField, UNO_QUERY );
2218  }
2219 
2220  // field properties starts here
2221  if (GetProperty(rXPropSet, "URL"))
2222  {
2223  OUString sURL;
2224 
2225  mAny >>= sURL;
2226  if (!sURL.isEmpty())
2227  {
2228  if (!sURL.match("#action?jump="))
2229  {
2230  bool bExtURL = URLTransformer().isExternalURL(sURL);
2231  sURL = bExtURL ? sURL : lcl_GetTarget(GetFB()->getModel(), sURL);
2232 
2233  OUString sRelId
2234  = mpFB->addRelation(mpFS->getOutputStream(),
2237  sURL, bExtURL);
2238 
2239  if (bExtURL)
2240  mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId);
2241  else
2242  mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId,
2243  XML_action, "ppaction://hlinksldjump");
2244  }
2245  else
2246  {
2247  sal_Int32 nIndex = sURL.indexOf('=');
2248  OUString aDestination(sURL.copy(nIndex + 1));
2249  mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), "", XML_action,
2250  "ppaction://hlinkshowjump?jump=" + aDestination);
2251  }
2252  }
2253  }
2254  mpFS->endElementNS( XML_a, nElement );
2255 }
2256 
2257 OUString DrawingML::GetFieldValue( const css::uno::Reference< css::text::XTextRange >& rRun, bool& bIsURLField )
2258 {
2259  Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
2260  OUString aFieldType, aFieldValue;
2261 
2262  if (GetProperty(rXPropSet, "TextPortionType"))
2263  {
2264  aFieldType = *o3tl::doAccess<OUString>(mAny);
2265  SAL_INFO("oox.shape", "field type: " << aFieldType);
2266  }
2267 
2268  if( aFieldType == "TextField" )
2269  {
2270  Reference< XTextField > rXTextField;
2271  if (GetProperty(rXPropSet, "TextField"))
2272  mAny >>= rXTextField;
2273  if( rXTextField.is() )
2274  {
2275  rXPropSet.set( rXTextField, UNO_QUERY );
2276  if( rXPropSet.is() )
2277  {
2278  OUString aFieldKind( rXTextField->getPresentation( true ) );
2279  SAL_INFO("oox.shape", "field kind: " << aFieldKind);
2280  if( aFieldKind == "Page" )
2281  {
2282  aFieldValue = "slidenum";
2283  }
2284  else if( aFieldKind == "Pages" )
2285  {
2286  aFieldValue = "slidecount";
2287  }
2288  else if( aFieldKind == "PageName" )
2289  {
2290  aFieldValue = "slidename";
2291  }
2292  else if( aFieldKind == "URL" )
2293  {
2294  bIsURLField = true;
2295  if (GetProperty(rXPropSet, "Representation"))
2296  mAny >>= aFieldValue;
2297 
2298  }
2299  else if(aFieldKind == "Date")
2300  {
2301  sal_Int32 nNumFmt = -1;
2302  rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
2303  aFieldValue = GetDatetimeTypeFromDate(static_cast<SvxDateFormat>(nNumFmt));
2304  }
2305  else if(aFieldKind == "ExtTime")
2306  {
2307  sal_Int32 nNumFmt = -1;
2308  rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
2309  aFieldValue = GetDatetimeTypeFromTime(static_cast<SvxTimeFormat>(nNumFmt));
2310  }
2311  else if(aFieldKind == "ExtFile")
2312  {
2313  sal_Int32 nNumFmt = -1;
2314  rXPropSet->getPropertyValue(UNO_TC_PROP_FILE_FORMAT) >>= nNumFmt;
2315  switch(nNumFmt)
2316  {
2317  case 0: aFieldValue = "file"; // Path/File name
2318  break;
2319  case 1: aFieldValue = "file1"; // Path
2320  break;
2321  case 2: aFieldValue = "file2"; // File name without extension
2322  break;
2323  case 3: aFieldValue = "file3"; // File name with extension
2324  }
2325  }
2326  else if(aFieldKind == "Author")
2327  {
2328  aFieldValue = "author";
2329  }
2330  }
2331  }
2332  }
2333  return aFieldValue;
2334 }
2335 
2337 {
2338  return GetDatetimeTypeFromDateTime(eDate, SvxTimeFormat::AppDefault);
2339 }
2340 
2342 {
2343  return GetDatetimeTypeFromDateTime(SvxDateFormat::AppDefault, eTime);
2344 }
2345 
2347 {
2348  OUString aDateField;
2349  switch (eDate)
2350  {
2351  case SvxDateFormat::StdSmall:
2352  case SvxDateFormat::A:
2353  aDateField = "datetime";
2354  break;
2355  case SvxDateFormat::B:
2356  aDateField = "datetime1"; // 13/02/1996
2357  break;
2358  case SvxDateFormat::C:
2359  aDateField = "datetime5";
2360  break;
2361  case SvxDateFormat::D:
2362  aDateField = "datetime3"; // 13 February 1996
2363  break;
2364  case SvxDateFormat::StdBig:
2365  case SvxDateFormat::E:
2366  case SvxDateFormat::F:
2367  aDateField = "datetime2";
2368  break;
2369  default:
2370  break;
2371  }
2372 
2373  OUString aTimeField;
2374  switch (eTime)
2375  {
2376  case SvxTimeFormat::Standard:
2377  case SvxTimeFormat::HH24_MM_SS:
2378  case SvxTimeFormat::HH24_MM_SS_00:
2379  aTimeField = "datetime11"; // 13:49:38
2380  break;
2381  case SvxTimeFormat::HH24_MM:
2382  aTimeField = "datetime10"; // 13:49
2383  break;
2384  case SvxTimeFormat::HH12_MM:
2385  case SvxTimeFormat::HH12_MM_AMPM:
2386  aTimeField = "datetime12"; // 01:49 PM
2387  break;
2388  case SvxTimeFormat::HH12_MM_SS:
2389  case SvxTimeFormat::HH12_MM_SS_AMPM:
2390  case SvxTimeFormat::HH12_MM_SS_00:
2391  case SvxTimeFormat::HH12_MM_SS_00_AMPM:
2392  aTimeField = "datetime13"; // 01:49:38 PM
2393  break;
2394  default:
2395  break;
2396  }
2397 
2398  if (!aDateField.isEmpty() && aTimeField.isEmpty())
2399  return aDateField;
2400  else if (!aTimeField.isEmpty() && aDateField.isEmpty())
2401  return aTimeField;
2402  else if (!aDateField.isEmpty() && !aTimeField.isEmpty())
2403  {
2404  if (aTimeField == "datetime11" || aTimeField == "datetime13")
2405  // only datetime format that has Date and HH:MM:SS
2406  return "datetime9"; // dd/mm/yyyy H:MM:SS
2407  else
2408  // only datetime format that has Date and HH:MM
2409  return "datetime8"; // dd/mm/yyyy H:MM
2410  }
2411  else
2412  return "";
2413 }
2414 
2416  bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
2417  const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet)
2418 {
2419  Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
2420  sal_Int16 nLevel = -1;
2421  if (GetProperty(rXPropSet, "NumberingLevel"))
2422  mAny >>= nLevel;
2423 
2424  bool bNumberingIsNumber = true;
2425  if (GetProperty(rXPropSet, "NumberingIsNumber"))
2426  mAny >>= bNumberingIsNumber;
2427 
2428  bool bIsURLField = false;
2429  OUString sFieldValue = GetFieldValue( rRun, bIsURLField );
2430  bool bWriteField = !( sFieldValue.isEmpty() || bIsURLField );
2431 
2432  OUString sText = rRun->getString();
2433 
2434  //if there is no text following the bullet, add a space after the bullet
2435  if (nLevel !=-1 && bNumberingIsNumber && sText.isEmpty() )
2436  sText=" ";
2437 
2438  if ( bIsURLField )
2439  sText = sFieldValue;
2440 
2441  if( sText.isEmpty())
2442  {
2443  Reference< XPropertySet > xPropSet( rRun, UNO_QUERY );
2444 
2445  try
2446  {
2447  if( !xPropSet.is() || !( xPropSet->getPropertyValue( "PlaceholderText" ) >>= sText ) )
2448  return;
2449  if( sText.isEmpty() )
2450  return;
2451  }
2452  catch (const Exception &)
2453  {
2454  return;
2455  }
2456  }
2457 
2458  if (sText == "\n")
2459  {
2460  mpFS->singleElementNS(XML_a, XML_br);
2461  }
2462  else
2463  {
2464  if( bWriteField )
2465  {
2466  OString sUUID(comphelper::xml::generateGUIDString());
2467  mpFS->startElementNS( XML_a, XML_fld,
2468  XML_id, sUUID.getStr(),
2469  XML_type, sFieldValue );
2470  }
2471  else
2472  {
2473  mpFS->startElementNS(XML_a, XML_r);
2474  }
2475 
2476  Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY );
2477 
2478  WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight, GetScriptType(sText), rXShapePropSet);
2479  mpFS->startElementNS(XML_a, XML_t);
2480  mpFS->writeEscaped( sText );
2481  mpFS->endElementNS( XML_a, XML_t );
2482 
2483  if( bWriteField )
2484  mpFS->endElementNS( XML_a, XML_fld );
2485  else
2486  mpFS->endElementNS( XML_a, XML_r );
2487  }
2488 }
2489 
2490 static OUString GetAutoNumType(SvxNumType nNumberingType, bool bSDot, bool bPBehind, bool bPBoth)
2491 {
2492  OUString sPrefixSuffix;
2493 
2494  if (bPBoth)
2495  sPrefixSuffix = "ParenBoth";
2496  else if (bPBehind)
2497  sPrefixSuffix = "ParenR";
2498  else if (bSDot)
2499  sPrefixSuffix = "Period";
2500 
2501  switch( nNumberingType )
2502  {
2505  return "alphaUc" + sPrefixSuffix;
2506 
2509  return "alphaLc" + sPrefixSuffix;
2510 
2511  case SVX_NUM_ROMAN_UPPER :
2512  return "romanUc" + sPrefixSuffix;
2513 
2514  case SVX_NUM_ROMAN_LOWER :
2515  return "romanLc" + sPrefixSuffix;
2516 
2517  case SVX_NUM_ARABIC :
2518  {
2519  if (sPrefixSuffix.isEmpty())
2520  return "arabicPlain";
2521  else
2522  return "arabic" + sPrefixSuffix;
2523  }
2524  default:
2525  break;
2526  }
2527 
2528  return OUString();
2529 }
2530 
2531 void DrawingML::WriteParagraphNumbering(const Reference< XPropertySet >& rXPropSet, float fFirstCharHeight, sal_Int16 nLevel )
2532 {
2533  if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2534  return;
2535 
2536  Reference< XIndexAccess > rXIndexAccess;
2537 
2538  if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2539  return;
2540 
2541  SAL_INFO("oox.shape", "numbering rules");
2542 
2543  Sequence<PropertyValue> aPropertySequence;
2544  rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2545 
2546  if (!aPropertySequence.hasElements())
2547  return;
2548 
2549  SvxNumType nNumberingType = SVX_NUM_NUMBER_NONE;
2550  bool bSDot = false;
2551  bool bPBehind = false;
2552  bool bPBoth = false;
2553  sal_Unicode aBulletChar = 0x2022; // a bullet
2554  awt::FontDescriptor aFontDesc;
2555  bool bHasFontDesc = false;
2556  uno::Reference<graphic::XGraphic> xGraphic;
2557  sal_Int16 nBulletRelSize = 0;
2558  sal_Int16 nStartWith = 1;
2559  ::Color nBulletColor;
2560  bool bHasBulletColor = false;
2561  awt::Size aGraphicSize;
2562 
2563  for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) )
2564  {
2565  OUString aPropName( rPropValue.Name );
2566  SAL_INFO("oox.shape", "pro name: " << aPropName);
2567  if ( aPropName == "NumberingType" )
2568  {
2569  nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(rPropValue.Value));
2570  }
2571  else if ( aPropName == "Prefix" )
2572  {
2573  if( *o3tl::doAccess<OUString>(rPropValue.Value) == ")")
2574  bPBoth = true;
2575  }
2576  else if ( aPropName == "Suffix" )
2577  {
2578  auto s = o3tl::doAccess<OUString>(rPropValue.Value);
2579  if( *s == ".")
2580  bSDot = true;
2581  else if( *s == ")")
2582  bPBehind = true;
2583  }
2584  else if(aPropName == "BulletColor")
2585  {
2586  nBulletColor = ::Color(ColorTransparency, *o3tl::doAccess<sal_uInt32>(rPropValue.Value));
2587  bHasBulletColor = true;
2588  }
2589  else if ( aPropName == "BulletChar" )
2590  {
2591  aBulletChar = (*o3tl::doAccess<OUString>(rPropValue.Value))[ 0 ];
2592  }
2593  else if ( aPropName == "BulletFont" )
2594  {
2595  aFontDesc = *o3tl::doAccess<awt::FontDescriptor>(rPropValue.Value);
2596  bHasFontDesc = true;
2597 
2598  // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font,
2599  // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used.
2600  // Because there might exist a lot of damaged documents I added this two lines
2601  // which fixes the bullet problem for the export.
2602  if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") )
2603  aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252;
2604 
2605  }
2606  else if ( aPropName == "BulletRelSize" )
2607  {
2608  nBulletRelSize = *o3tl::doAccess<sal_Int16>(rPropValue.Value);
2609  }
2610  else if ( aPropName == "StartWith" )
2611  {
2612  nStartWith = *o3tl::doAccess<sal_Int16>(rPropValue.Value);
2613  }
2614  else if (aPropName == "GraphicBitmap")
2615  {
2616  auto xBitmap = rPropValue.Value.get<uno::Reference<awt::XBitmap>>();
2617  xGraphic.set(xBitmap, uno::UNO_QUERY);
2618  }
2619  else if ( aPropName == "GraphicSize" )
2620  {
2621  aGraphicSize = *o3tl::doAccess<awt::Size>(rPropValue.Value);
2622  SAL_INFO("oox.shape", "graphic size: " << aGraphicSize.Width << "x" << aGraphicSize.Height);
2623  }
2624  }
2625 
2626  if (nNumberingType == SVX_NUM_NUMBER_NONE)
2627  return;
2628 
2629  Graphic aGraphic(xGraphic);
2630  if (xGraphic.is() && aGraphic.GetType() != GraphicType::NONE)
2631  {
2632  tools::Long nFirstCharHeightMm = TransformMetric(fFirstCharHeight * 100.f, FieldUnit::POINT, FieldUnit::MM);
2633  float fBulletSizeRel = aGraphicSize.Height / static_cast<float>(nFirstCharHeightMm) / OOX_BULLET_LIST_SCALE_FACTOR;
2634 
2635  OUString sRelationId;
2636 
2637  if (fBulletSizeRel < 1.0f)
2638  {
2639  // Add padding to get the bullet point centered in PPT
2640  Size aDestSize(64, 64);
2641  float fBulletSizeRelX = fBulletSizeRel / aGraphicSize.Height * aGraphicSize.Width;
2642  tools::Long nPaddingX = std::max<tools::Long>(0, std::lround((aDestSize.Width() - fBulletSizeRelX * aDestSize.Width()) / 2.f));
2643  tools::Long nPaddingY = std::lround((aDestSize.Height() - fBulletSizeRel * aDestSize.Height()) / 2.f);
2644  tools::Rectangle aDestRect(nPaddingX, nPaddingY, aDestSize.Width() - nPaddingX, aDestSize.Height() - nPaddingY);
2645 
2646  AlphaMask aMask(aDestSize);
2647  aMask.Erase(255);
2648  BitmapEx aSourceBitmap(aGraphic.GetBitmapEx());
2649  aSourceBitmap.Scale(aDestRect.GetSize());
2650  tools::Rectangle aSourceRect(Point(0, 0), aDestRect.GetSize());
2651  BitmapEx aDestBitmap(Bitmap(aDestSize, vcl::PixelFormat::N24_BPP), aMask);
2652  aDestBitmap.CopyPixel(aDestRect, aSourceRect, &aSourceBitmap);
2653  Graphic aDestGraphic(aDestBitmap);
2654  sRelationId = WriteImage(aDestGraphic);
2655  fBulletSizeRel = 1.0f;
2656  }
2657  else
2658  {
2659  sRelationId = WriteImage(aGraphic);
2660  }
2661 
2662  mpFS->singleElementNS( XML_a, XML_buSzPct,
2663  XML_val, OString::number(std::min<sal_Int32>(std::lround(100000.f * fBulletSizeRel), 400000)));
2664  mpFS->startElementNS(XML_a, XML_buBlip);
2665  mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelationId);
2666  mpFS->endElementNS( XML_a, XML_buBlip );
2667  }
2668  else
2669  {
2670  if(bHasBulletColor)
2671  {
2672  if (nBulletColor == COL_AUTO )
2673  {
2674  nBulletColor = ::Color(ColorTransparency, mbIsBackgroundDark ? 0xffffff : 0x000000);
2675  }
2676  mpFS->startElementNS(XML_a, XML_buClr);
2677  WriteColor( nBulletColor );
2678  mpFS->endElementNS( XML_a, XML_buClr );
2679  }
2680 
2681  if( nBulletRelSize && nBulletRelSize != 100 )
2682  mpFS->singleElementNS( XML_a, XML_buSzPct,
2683  XML_val, OString::number(std::clamp<sal_Int32>(1000*nBulletRelSize, 25000, 400000)));
2684  if( bHasFontDesc )
2685  {
2686  if ( SVX_NUM_CHAR_SPECIAL == nNumberingType )
2687  aBulletChar = SubstituteBullet( aBulletChar, aFontDesc );
2688  mpFS->singleElementNS( XML_a, XML_buFont,
2689  XML_typeface, aFontDesc.Name,
2690  XML_charset, sax_fastparser::UseIf("2", aFontDesc.CharSet == awt::CharSet::SYMBOL));
2691  }
2692 
2693  OUString aAutoNumType = GetAutoNumType( nNumberingType, bSDot, bPBehind, bPBoth );
2694 
2695  if (!aAutoNumType.isEmpty())
2696  {
2697  mpFS->singleElementNS(XML_a, XML_buAutoNum,
2698  XML_type, aAutoNumType,
2699  XML_startAt, sax_fastparser::UseIf(OString::number(nStartWith), nStartWith > 1));
2700  }
2701  else
2702  {
2703  mpFS->singleElementNS(XML_a, XML_buChar, XML_char, OUString(aBulletChar));
2704  }
2705  }
2706 }
2707 
2709 {
2710  css::uno::Sequence<css::style::TabStop> aTabStops;
2711  if (GetProperty(rXPropSet, "ParaTabStops"))
2712  aTabStops = *o3tl::doAccess<css::uno::Sequence<css::style::TabStop>>(mAny);
2713 
2714  if (aTabStops.getLength() > 0)
2715  mpFS->startElementNS(XML_a, XML_tabLst);
2716 
2717  for (const css::style::TabStop& rTabStop : std::as_const(aTabStops))
2718  {
2719  OString sPosition = OString::number(GetPointFromCoordinate(rTabStop.Position));
2720  OString sAlignment;
2721  switch (rTabStop.Alignment)
2722  {
2723  case css::style::TabAlign_DECIMAL:
2724  sAlignment = "dec";
2725  break;
2726  case css::style::TabAlign_RIGHT:
2727  sAlignment = "r";
2728  break;
2729  case css::style::TabAlign_CENTER:
2730  sAlignment = "ctr";
2731  break;
2732  case css::style::TabAlign_LEFT:
2733  default:
2734  sAlignment = "l";
2735  }
2736  mpFS->singleElementNS(XML_a, XML_tab, XML_algn, sAlignment, XML_pos, sPosition);
2737  }
2738  if (aTabStops.getLength() > 0)
2739  mpFS->endElementNS(XML_a, XML_tabLst);
2740 }
2741 
2743 {
2744  bool bRet = false;
2745  if ( rXShape.is() )
2746  {
2747  uno::Reference<lang::XServiceInfo> xServiceInfo(rXShape, uno::UNO_QUERY_THROW);
2748  bRet = xServiceInfo->supportsService("com.sun.star.drawing.GroupShape");
2749  }
2750  return bRet;
2751 }
2752 
2754 {
2755  uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
2756  if (!xPropSet.is())
2757  return false;
2758 
2759  // if the shape doesn't have the InteropGrabBag property, it's not a diagram
2760  uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
2762  if (!xPropSetInfo->hasPropertyByName(aName))
2763  return false;
2764 
2765  uno::Sequence<beans::PropertyValue> propList;
2766  xPropSet->getPropertyValue(aName) >>= propList;
2767  return std::any_of(std::cbegin(propList), std::cend(propList),
2768  [](const beans::PropertyValue& rProp) {
2769  // if we find any of the diagram components, it's a diagram
2770  OUString propName = rProp.Name;
2771  return propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle"
2772  || propName == "OOXColor" || propName == "OOXDrawing";
2773  });
2774 }
2775 
2776 sal_Int32 DrawingML::getBulletMarginIndentation (const Reference< XPropertySet >& rXPropSet,sal_Int16 nLevel, std::u16string_view propName)
2777 {
2778  if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2779  return 0;
2780 
2781  Reference< XIndexAccess > rXIndexAccess;
2782 
2783  if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2784  return 0;
2785 
2786  SAL_INFO("oox.shape", "numbering rules");
2787 
2788  Sequence<PropertyValue> aPropertySequence;
2789  rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2790 
2791  if (!aPropertySequence.hasElements())
2792  return 0;
2793 
2794  for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) )
2795  {
2796  OUString aPropName( rPropValue.Name );
2797  SAL_INFO("oox.shape", "pro name: " << aPropName);
2798  if ( aPropName == propName )
2799  return *o3tl::doAccess<sal_Int32>(rPropValue.Value);
2800  }
2801 
2802  return 0;
2803 }
2804 
2805 const char* DrawingML::GetAlignment( style::ParagraphAdjust nAlignment )
2806 {
2807  const char* sAlignment = nullptr;
2808 
2809  switch( nAlignment )
2810  {
2811  case style::ParagraphAdjust_CENTER:
2812  sAlignment = "ctr";
2813  break;
2814  case style::ParagraphAdjust_RIGHT:
2815  sAlignment = "r";
2816  break;
2817  case style::ParagraphAdjust_BLOCK:
2818  sAlignment = "just";
2819  break;
2820  default:
2821  ;
2822  }
2823 
2824  return sAlignment;
2825 }
2826 
2827 void DrawingML::WriteLinespacing( const LineSpacing& rSpacing )
2828 {
2829  if( rSpacing.Mode == LineSpacingMode::PROP )
2830  {
2831  mpFS->singleElementNS( XML_a, XML_spcPct,
2832  XML_val, OString::number(static_cast<sal_Int32>(rSpacing.Height)*1000));
2833  }
2834  else
2835  {
2836  mpFS->singleElementNS( XML_a, XML_spcPts,
2837  XML_val, OString::number(std::lround(rSpacing.Height / 25.4 * 72)));
2838  }
2839 }
2840 
2841 bool DrawingML::WriteParagraphProperties( const Reference< XTextContent >& rParagraph, float fFirstCharHeight, sal_Int32 nElement)
2842 {
2843  Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
2844  Reference< XPropertyState > rXPropState( rParagraph, UNO_QUERY );
2845  PropertyState eState;
2846 
2847  if( !rXPropSet.is() || !rXPropState.is() )
2848  return false;
2849 
2850  sal_Int16 nLevel = -1;
2851  if (GetProperty(rXPropSet, "NumberingLevel"))
2852  mAny >>= nLevel;
2853 
2854  sal_Int16 nTmp = sal_Int16(style::ParagraphAdjust_LEFT);
2855  if (GetProperty(rXPropSet, "ParaAdjust"))
2856  mAny >>= nTmp;
2857  style::ParagraphAdjust nAlignment = static_cast<style::ParagraphAdjust>(nTmp);
2858 
2859  bool bHasLinespacing = false;
2860  LineSpacing aLineSpacing;
2861  if (GetPropertyAndState(rXPropSet, rXPropState, "ParaLineSpacing", eState)
2862  && eState == beans::PropertyState_DIRECT_VALUE)
2863  bHasLinespacing = ( mAny >>= aLineSpacing );
2864 
2865  bool bRtl = false;
2866  if (GetProperty(rXPropSet, "WritingMode"))
2867  {
2868  sal_Int16 nWritingMode;
2869  if( ( mAny >>= nWritingMode ) && nWritingMode == text::WritingMode2::RL_TB )
2870  {
2871  bRtl = true;
2872  }
2873  }
2874 
2875  sal_Int32 nParaLeftMargin = 0;
2876  sal_Int32 nParaFirstLineIndent = 0;
2877 
2878  if (GetProperty(rXPropSet, "ParaLeftMargin"))
2879  mAny >>= nParaLeftMargin;
2880  if (GetProperty(rXPropSet, "ParaFirstLineIndent"))
2881  mAny >>= nParaFirstLineIndent;
2882 
2883  sal_Int32 nParaTopMargin = 0;
2884  sal_Int32 nParaBottomMargin = 0;
2885 
2886  if (GetProperty(rXPropSet, "ParaTopMargin"))
2887  mAny >>= nParaTopMargin;
2888  if (GetProperty(rXPropSet, "ParaBottomMargin"))
2889  mAny >>= nParaBottomMargin;
2890 
2891  sal_Int32 nLeftMargin = getBulletMarginIndentation ( rXPropSet, nLevel,u"LeftMargin");
2892  sal_Int32 nLineIndentation = getBulletMarginIndentation ( rXPropSet, nLevel,u"FirstLineOffset");
2893 
2894  if( !(nLevel != -1
2895  || nAlignment != style::ParagraphAdjust_LEFT
2896  || bHasLinespacing) )
2897  return false;
2898 
2899  if (nParaLeftMargin) // For Paragraph
2900  mpFS->startElementNS( XML_a, nElement,
2901  XML_lvl, sax_fastparser::UseIf(OString::number(nLevel), nLevel > 0),
2902  XML_marL, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nParaLeftMargin)), nParaLeftMargin > 0),
2903  XML_indent, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nParaFirstLineIndent)), nParaFirstLineIndent != 0),
2904  XML_algn, GetAlignment( nAlignment ),
2905  XML_rtl, sax_fastparser::UseIf(ToPsz10(bRtl), bRtl));
2906  else
2907  mpFS->startElementNS( XML_a, nElement,
2908  XML_lvl, sax_fastparser::UseIf(OString::number(nLevel), nLevel > 0),
2909  XML_marL, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)), nLeftMargin > 0),
2910  XML_indent, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLineIndentation)), nLineIndentation != 0),
2911  XML_algn, GetAlignment( nAlignment ),
2912  XML_rtl, sax_fastparser::UseIf(ToPsz10(bRtl), bRtl));
2913 
2914 
2915  if( bHasLinespacing )
2916  {
2917  mpFS->startElementNS(XML_a, XML_lnSpc);
2918  WriteLinespacing( aLineSpacing );
2919  mpFS->endElementNS( XML_a, XML_lnSpc );
2920  }
2921 
2922  if( nParaTopMargin != 0 )
2923  {
2924  mpFS->startElementNS(XML_a, XML_spcBef);
2925  {
2926  mpFS->singleElementNS( XML_a, XML_spcPts,
2927  XML_val, OString::number(std::lround(nParaTopMargin / 25.4 * 72)));
2928  }
2929  mpFS->endElementNS( XML_a, XML_spcBef );
2930  }
2931 
2932  if( nParaBottomMargin != 0 )
2933  {
2934  mpFS->startElementNS(XML_a, XML_spcAft);
2935  {
2936  mpFS->singleElementNS( XML_a, XML_spcPts,
2937  XML_val, OString::number(std::lround(nParaBottomMargin / 25.4 * 72)));
2938  }
2939  mpFS->endElementNS( XML_a, XML_spcAft );
2940  }
2941 
2942  WriteParagraphNumbering( rXPropSet, fFirstCharHeight, nLevel );
2943 
2944  WriteParagraphTabStops( rXPropSet );
2945 
2946  // do not end element for lstStyles since, defRPr should be stacked inside it
2947  if( nElement != XML_lvl1pPr )
2948  mpFS->endElementNS( XML_a, nElement );
2949 
2950  return true;
2951 }
2952 
2953 void DrawingML::WriteLstStyles(const css::uno::Reference<css::text::XTextContent>& rParagraph,
2954  bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
2955  const css::uno::Reference<css::beans::XPropertySet>& rXShapePropSet)
2956 {
2957  Reference<XEnumerationAccess> xAccess(rParagraph, UNO_QUERY);
2958  if (!xAccess.is())
2959  return;
2960 
2961  Reference<XEnumeration> xEnumeration(xAccess->createEnumeration());
2962  if (!xEnumeration.is())
2963  return;
2964 
2965 
2966  Reference<XTextRange> rRun;
2967 
2968  if (!xEnumeration->hasMoreElements())
2969  return;
2970 
2971  Any aAny(xEnumeration->nextElement());
2972  if (aAny >>= rRun)
2973  {
2974  float fFirstCharHeight = rnCharHeight / 1000.;
2975  Reference<XPropertySet> xFirstRunPropSet(rRun, UNO_QUERY);
2976  Reference<XPropertySetInfo> xFirstRunPropSetInfo
2977  = xFirstRunPropSet->getPropertySetInfo();
2978 
2979  if (xFirstRunPropSetInfo->hasPropertyByName("CharHeight"))
2980  fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>();
2981 
2982  mpFS->startElementNS(XML_a, XML_lstStyle);
2983  if( !WriteParagraphProperties(rParagraph, fFirstCharHeight, XML_lvl1pPr) )
2984  mpFS->startElementNS(XML_a, XML_lvl1pPr);
2985  WriteRunProperties(xFirstRunPropSet, false, XML_defRPr, true, rbOverridingCharHeight,
2986  rnCharHeight, GetScriptType(rRun->getString()), rXShapePropSet);
2987  mpFS->endElementNS(XML_a, XML_lvl1pPr);
2988  mpFS->endElementNS(XML_a, XML_lstStyle);
2989  }
2990 }
2991 
2993  bool& rbOverridingCharHeight, sal_Int32& rnCharHeight,
2994  const css::uno::Reference< css::beans::XPropertySet >& rXShapePropSet)
2995 {
2996  Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY );
2997  if( !access.is() )
2998  return;
2999 
3000  Reference< XEnumeration > enumeration( access->createEnumeration() );
3001  if( !enumeration.is() )
3002  return;
3003 
3004  mpFS->startElementNS(XML_a, XML_p);
3005 
3006  bool bPropertiesWritten = false;
3007  while( enumeration->hasMoreElements() )
3008  {
3010  Any any ( enumeration->nextElement() );
3011 
3012  if (any >>= run)
3013  {
3014  if( !bPropertiesWritten )
3015  {
3016  float fFirstCharHeight = rnCharHeight / 1000.;
3017  Reference< XPropertySet > xFirstRunPropSet (run, UNO_QUERY);
3018  Reference< XPropertySetInfo > xFirstRunPropSetInfo = xFirstRunPropSet->getPropertySetInfo();
3019  if( xFirstRunPropSetInfo->hasPropertyByName("CharHeight") )
3020  fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>();
3021  WriteParagraphProperties(rParagraph, fFirstCharHeight, XML_pPr);
3022  bPropertiesWritten = true;
3023  }
3024  WriteRun( run, rbOverridingCharHeight, rnCharHeight, rXShapePropSet);
3025  }
3026  }
3027  Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
3028  sal_Int16 nDummy = -1;
3029  WriteRunProperties(rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight,
3030  rnCharHeight, nDummy, rXShapePropSet);
3031 
3032  mpFS->endElementNS( XML_a, XML_p );
3033 }
3034 
3035 bool DrawingML::IsFontworkShape(const css::uno::Reference<css::beans::XPropertySet>& rXShapePropSet)
3036 {
3037  bool bResult(false);
3038  if (rXShapePropSet.is())
3039  {
3040  Sequence<PropertyValue> aCustomShapeGeometryProps;
3041  if (GetProperty(rXShapePropSet, "CustomShapeGeometry"))
3042  {
3043  mAny >>= aCustomShapeGeometryProps;
3044  uno::Sequence<beans::PropertyValue> aTextPathSeq;
3045  for (const auto& rProp : std::as_const(aCustomShapeGeometryProps))
3046  {
3047  if (rProp.Name == "TextPath")
3048  {
3049  rProp.Value >>= aTextPathSeq;
3050  for (const auto& rTextPathItem : std::as_const(aTextPathSeq))
3051  {
3052  if (rTextPathItem.Name == "TextPath")
3053  {
3054  rTextPathItem.Value >>= bResult;
3055  break;
3056  }
3057  }
3058  break;
3059  }
3060  }
3061  }
3062  }
3063  return bResult;
3064 }
3065 
3066 void DrawingML::WriteText(const Reference<XInterface>& rXIface, bool bBodyPr, bool bText,
3067  sal_Int32 nXmlNamespace, bool bWritePropertiesAsLstStyles)
3068 {
3069  // ToDo: Fontwork in DOCX
3070  Reference< XText > xXText( rXIface, UNO_QUERY );
3071  if( !xXText.is() )
3072  return;
3073 
3074  Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY );
3075 
3076  sal_Int32 nTextPreRotateAngle = 0;
3077  double nTextRotateAngle = 0;
3078 
3079 #define DEFLRINS 254
3080 #define DEFTBINS 127
3081  sal_Int32 nLeft, nRight, nTop, nBottom;
3082  nLeft = nRight = DEFLRINS;
3083  nTop = nBottom = DEFTBINS;
3084 
3085  // top inset looks a bit different compared to ppt export
3086  // check if something related doesn't work as expected
3087  if (GetProperty(rXPropSet, "TextLeftDistance"))
3088  mAny >>= nLeft;
3089  if (GetProperty(rXPropSet, "TextRightDistance"))
3090  mAny >>= nRight;
3091  if (GetProperty(rXPropSet, "TextUpperDistance"))
3092  mAny >>= nTop;
3093  if (GetProperty(rXPropSet, "TextLowerDistance"))
3094  mAny >>= nBottom;
3095 
3096  TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP );
3097  const char* sVerticalAlignment = nullptr;
3098  if (GetProperty(rXPropSet, "TextVerticalAdjust"))
3099  mAny >>= eVerticalAlignment;
3100  sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment);
3101 
3102  const char* sWritingMode = nullptr;
3103  bool bVertical = false;
3104  if (GetProperty(rXPropSet, "TextWritingMode"))
3105  {
3107 
3108  if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL )
3109  {
3110  sWritingMode = "eaVert";
3111  bVertical = true;
3112  }
3113  }
3114 
3115  bool bIsFontworkShape(IsFontworkShape(rXPropSet));
3117  uno::Sequence<beans::PropertyValue> aTextPathSeq;
3118  bool bScaleX(false);
3119  OUString sShapeType("non-primitive");
3120  // ToDo move to InteropGrabBag
3121  OUString sMSWordPresetTextWarp;
3122 
3123  if (GetProperty(rXPropSet, "CustomShapeGeometry"))
3124  {
3126  if ( mAny >>= aProps )
3127  {
3128  for ( const auto& rProp : std::as_const(aProps) )
3129  {
3130  if ( rProp.Name == "TextPreRotateAngle" && ( rProp.Value >>= nTextPreRotateAngle ) )
3131  {
3132  if ( nTextPreRotateAngle == -90 )
3133  {
3134  sWritingMode = "vert";
3135  bVertical = true;
3136  }
3137  else if ( nTextPreRotateAngle == -270 )
3138  {
3139  sWritingMode = "vert270";
3140  bVertical = true;
3141  }
3142  }
3143  else if (rProp.Name == "AdjustmentValues")
3144  rProp.Value >>= aAdjustmentSeq;
3145  else if( rProp.Name == "TextRotateAngle" )
3146  rProp.Value >>= nTextRotateAngle;
3147  else if (rProp.Name == "Type")
3148  rProp.Value >>= sShapeType;
3149  else if (rProp.Name == "TextPath")
3150  {
3151  rProp.Value >>= aTextPathSeq;
3152  for (const auto& rTextPathItem : std::as_const(aTextPathSeq))
3153  {
3154  if (rTextPathItem.Name == "ScaleX")
3155  rTextPathItem.Value >>= bScaleX;
3156  }
3157  }
3158  else if (rProp.Name == "PresetTextWarp")
3159  rProp.Value >>= sMSWordPresetTextWarp;
3160  }
3161  }
3162  }
3163  else
3164  {
3165  if (mpTextExport)
3166  {
3167  uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
3168  if (xShape)
3169  {
3170  auto xTextFrame = mpTextExport->GetUnoTextFrame(xShape);
3171  if (xTextFrame)
3172  {
3173  uno::Reference<beans::XPropertySet> xPropSet(xTextFrame, uno::UNO_QUERY);
3174  auto aAny = xPropSet->getPropertyValue("WritingMode");
3175  sal_Int16 nWritingMode;
3176  if (aAny >>= nWritingMode)
3177  {
3178  switch (nWritingMode)
3179  {
3180  case WritingMode2::TB_RL:
3181  sWritingMode = "vert";
3182  bVertical = true;
3183  break;
3184  case WritingMode2::BT_LR:
3185  sWritingMode = "vert270";
3186  bVertical = true;
3187  break;
3188  default:
3189  break;
3190  }
3191  }
3192  }
3193  }
3194  }
3195  }
3196  OUString sPresetWarp(PresetGeometryTypeNames::GetMsoName(sShapeType));
3197  // ODF may have user defined TextPath, use "textPlain" as ersatz.
3198  if (sPresetWarp.isEmpty())
3199  sPresetWarp = bIsFontworkShape ? std::u16string_view(u"textPlain") : std::u16string_view(u"textNoShape");
3200 
3201  bool bFromWordArt = !bScaleX
3202  && ( sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp"
3203  || sPresetWarp == "textButton" || sPresetWarp == "textCircle");
3204 
3205  TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER );
3206  bool bHorizontalCenter = false;
3207  if (GetProperty(rXPropSet, "TextHorizontalAdjust"))
3208  mAny >>= eHorizontalAlignment;
3209  if( eHorizontalAlignment == TextHorizontalAdjust_CENTER )
3210  bHorizontalCenter = true;
3211  else if( bVertical && eHorizontalAlignment == TextHorizontalAdjust_LEFT )
3212  sVerticalAlignment = "b";
3213 
3214  bool bHasWrap = false;
3215  bool bWrap = false;
3216  // Only custom shapes obey the TextWordWrap option, normal text always wraps.
3217  if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextWordWrap"))
3218  {
3219  mAny >>= bWrap;
3220  bHasWrap = true;
3221  }
3222 
3223  if (bBodyPr)
3224  {
3225  const char* pWrap = (bHasWrap && !bWrap) || bIsFontworkShape ? "none" : nullptr;
3226  if (GetDocumentType() == DOCUMENT_DOCX)
3227  {
3228  // In case of DOCX, if we want to have the same effect as
3229  // TextShape's automatic word wrapping, then we need to set
3230  // wrapping to square.
3231  uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY);
3232  if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.TextShape"))
3233  pWrap = "square";
3234  }
3235 
3236  std::optional<OUString> sHorzOverflow;
3237  std::optional<OUString> sVertOverflow;
3238  sal_Int32 nShapeRotateAngle = rXPropSet->getPropertyValue("RotateAngle").get<sal_Int32>() / 300;
3239  sal_Int16 nCols = 0;
3240  sal_Int32 nColSpacing = -1;
3241  if (GetProperty(rXPropSet, "TextColumns"))
3242  {
3243  if (css::uno::Reference<css::text::XTextColumns> xCols{ mAny, css::uno::UNO_QUERY })
3244  {
3245  nCols = xCols->getColumnCount();
3246  if (css::uno::Reference<css::beans::XPropertySet> xProps{ mAny,
3247  css::uno::UNO_QUERY })
3248  {
3249  if (GetProperty(xProps, "AutomaticDistance"))
3250  mAny >>= nColSpacing;
3251  }
3252  }
3253  }
3254 
3255  std::optional<OString> isUpright;
3256  if (GetProperty(rXPropSet, "InteropGrabBag"))
3257  {
3258  if (rXPropSet->getPropertySetInfo()->hasPropertyByName("InteropGrabBag"))
3259  {
3260  bool bUpright = false;
3261  sal_Int32 nOldShapeRotation = 0;
3262  sal_Int32 nOldTextRotation = 0;
3263  uno::Sequence<beans::PropertyValue> aGrabBag;
3264  rXPropSet->getPropertyValue("InteropGrabBag") >>= aGrabBag;
3265  for (const auto& aProp : std::as_const(aGrabBag))
3266  {
3267  if (aProp.Name == "Upright")
3268  {
3269  aProp.Value >>= bUpright;
3270  isUpright = OString(bUpright ? "1" : "0");
3271  }
3272  else if (aProp.Name == "horzOverflow")
3273  {
3274  OUString sValue;
3275  aProp.Value >>= sValue;
3276  sHorzOverflow = sValue;
3277  }
3278  else if (aProp.Name == "vertOverflow")
3279  {
3280  OUString sValue;
3281  aProp.Value >>= sValue;
3282  sVertOverflow = sValue;
3283  }
3284  }
3285  if (bUpright)
3286  {
3287  for (const auto& aProp : std::as_const(aGrabBag))
3288  {
3289  if (aProp.Name == "nShapeRotationAtImport")
3290  aProp.Value >>= nOldShapeRotation;
3291  else if (aProp.Name == "nTextRotationAtImport")
3292  aProp.Value >>= nOldTextRotation;
3293  }
3294  // So our shape with the textbox in it was not rotated.
3295  // Keep upright and make the preRotateAngle 0, it is an attribute
3296  // of textBodyPr and must be 0 when upright is true, otherwise
3297  // bad rotation happens in MSO.
3298  if (nShapeRotateAngle == nOldShapeRotation && nShapeRotateAngle == nOldTextRotation)
3299  nTextPreRotateAngle = 0;
3300  // So we rotated the shape, in this case lose upright and do
3301  // as LO normally does.
3302  else
3303  isUpright.reset();
3304  }
3305  }
3306  }
3307 
3308  mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr,
3309  XML_numCol, sax_fastparser::UseIf(OString::number(nCols), nCols > 0),
3310  XML_spcCol, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nColSpacing)), nCols > 0 && nColSpacing >= 0),
3311  XML_wrap, pWrap,
3312  XML_horzOverflow, sHorzOverflow,
3313  XML_vertOverflow, sVertOverflow,
3314  XML_fromWordArt, sax_fastparser::UseIf("1", bFromWordArt),
3315  XML_lIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nLeft)), nLeft != DEFLRINS),
3316  XML_rIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nRight)), nRight != DEFLRINS),
3317  XML_tIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nTop)), nTop != DEFTBINS),
3318  XML_bIns, sax_fastparser::UseIf(OString::number(oox::drawingml::convertHmmToEmu(nBottom)), nBottom != DEFTBINS),
3319  XML_anchor, sVerticalAlignment,
3320  XML_anchorCtr, sax_fastparser::UseIf("1", bHorizontalCenter),
3321  XML_vert, sWritingMode,
3322  XML_upright, isUpright,
3323  XML_rot, sax_fastparser::UseIf(oox::drawingml::calcRotationValue((nTextPreRotateAngle + nTextRotateAngle) * 100), (nTextPreRotateAngle + nTextRotateAngle) != 0));
3324  if (bIsFontworkShape)
3325  {
3326  if (aAdjustmentSeq.hasElements())
3327  {
3328  mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp);
3329  mpFS->startElementNS(XML_a, XML_avLst);
3330  bool bHasTwoHandles(
3331  sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour"
3332  || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour"
3333  || sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1"
3334  || sPresetWarp == "textWave2" || sPresetWarp == "textWave4");
3335  for (sal_Int32 i = 0, nElems = aAdjustmentSeq.getLength(); i < nElems; ++i )
3336  {
3337  OString sName = "adj" + (bHasTwoHandles ? OString::number(i + 1) : OString());
3338  double fValue(0.0);
3339  if (aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE)
3340  aAdjustmentSeq[i].Value >>= fValue;
3341  else
3342  {
3343  sal_Int32 nNumber(0);
3344  aAdjustmentSeq[i].Value >>= nNumber;
3345  fValue = static_cast<double>(nNumber);
3346  }
3347  // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree
3348  // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree.
3349  // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import.
3350  if (sPresetWarp == "textArchDown" || sPresetWarp == "textArchUp"
3351  || sPresetWarp == "textButton" || sPresetWarp == "textCircle"
3352  || ((i == 0)
3353  && (sPresetWarp == "textArchDownPour" || sPresetWarp == "textArchUpPour"
3354  || sPresetWarp == "textButtonPour" || sPresetWarp == "textCirclePour")))
3355  {
3356  fValue *= 60000.0;
3357  if (fValue < 0)
3358  fValue += 21600000;
3359  }
3360  else if ((i == 1)
3361  && (sPresetWarp == "textDoubleWave1" || sPresetWarp == "textWave1"
3362  || sPresetWarp == "textWave2" || sPresetWarp == "textWave4"))
3363  {
3364  fValue = fValue / 0.216 - 50000.0;
3365  }
3366  else if ((i == 1)
3367  && (sPresetWarp == "textArchDownPour"
3368  || sPresetWarp == "textArchUpPour"
3369  || sPresetWarp == "textButtonPour"
3370  || sPresetWarp == "textCirclePour"))
3371  {
3372  fValue /= 0.108;
3373  }
3374  else
3375  {
3376  fValue /= 0.216;
3377  }
3378  OString sFmla = "val " + OString::number(std::lround(fValue));
3379  mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
3380  // There exists faulty Favorite shapes with one handle but two adjustment values.
3381  if (!bHasTwoHandles)
3382  break;
3383  }
3384  mpFS->endElementNS(XML_a, XML_avLst);
3385  mpFS->endElementNS(XML_a, XML_prstTxWarp);
3386  }
3387  else
3388  {
3389  mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sPresetWarp);
3390  }
3391  }
3392  else if (GetDocumentType() == DOCUMENT_DOCX)
3393  {
3394  // interim solution for fdo#80897, roundtrip DOCX > LO > DOCX
3395  if (!sMSWordPresetTextWarp.isEmpty())
3396  mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, sMSWordPresetTextWarp);
3397  }
3398 
3400  {
3401  // tdf#112312: only custom shapes obey the TextAutoGrowHeight option
3402  bool bTextAutoGrowHeight = false;
3403  uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
3404  auto pSdrObjCustomShape = xShape.is() ? dynamic_cast<SdrObjCustomShape*>(SdrObject::getSdrObjectFromXShape(xShape)) : nullptr;
3405  if (pSdrObjCustomShape && GetProperty(rXPropSet, "TextAutoGrowHeight"))
3406  {
3407  mAny >>= bTextAutoGrowHeight;
3408  }
3409  mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
3410  }
3411  if (GetDocumentType() == DOCUMENT_PPTX)
3412  {
3413  TextFitToSizeType eFit = TextFitToSizeType_NONE;
3414  if (GetProperty(rXPropSet, "TextFitToSize"))
3415  mAny >>= eFit;
3416 
3417  if (eFit == TextFitToSizeType_AUTOFIT)
3418  {
3419  const sal_Int32 MAX_SCALE_VAL = 100000;
3420  sal_Int32 nFontScale = MAX_SCALE_VAL;
3421  SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get());
3422  if (pTextShape)
3423  {
3424  SdrTextObj* pTextObject = dynamic_cast<SdrTextObj*>(pTextShape->GetSdrObject());
3425  if (pTextObject)
3426  nFontScale = pTextObject->GetFontScaleY() * 1000;
3427  }
3428 
3429  mpFS->singleElementNS(XML_a, XML_normAutofit, XML_fontScale,
3430  sax_fastparser::UseIf(OString::number(nFontScale), nFontScale < MAX_SCALE_VAL && nFontScale > 0));
3431  }
3432  else
3433  {
3434  // tdf#127030: Only custom shapes obey the TextAutoGrowHeight option.
3435  bool bTextAutoGrowHeight = false;
3436  if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextAutoGrowHeight"))
3437  mAny >>= bTextAutoGrowHeight;
3438  mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
3439  }
3440  }
3441 
3442  WriteShape3DEffects( rXPropSet );
3443 
3444  mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr);
3445  }
3446 
3447  Reference< XEnumerationAccess > access( xXText, UNO_QUERY );
3448  if( !access.is() || !bText )
3449  return;
3450 
3451  Reference< XEnumeration > enumeration( access->createEnumeration() );
3452  if( !enumeration.is() )
3453  return;
3454 
3455  uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
3456  SdrObject* pSdrObject = xShape.is() ? SdrObject::getSdrObjectFromXShape(xShape) : nullptr;
3457  const SdrTextObj* pTxtObj = dynamic_cast<SdrTextObj*>( pSdrObject );
3458  if (pTxtObj && mpTextExport)
3459  {
3461 
3462  /*
3463  #i13885#
3464  When the object is actively being edited, that text is not set into
3465  the objects normal text object, but lives in a separate object.
3466  */
3467  if (pTxtObj->IsTextEditActive())
3468  {
3469  pParaObj = pTxtObj->CreateEditOutlinerParaObject();
3470  }
3471  else if (pTxtObj->GetOutlinerParaObject())
3472  pParaObj = *pTxtObj->GetOutlinerParaObject();
3473 
3474  if (pParaObj)
3475  {
3476  // this is reached only in case some text is attached to the shape
3477  mpTextExport->WriteOutliner(*pParaObj);
3478  }
3479  return;
3480  }
3481 
3482  bool bOverridingCharHeight = false;
3483  sal_Int32 nCharHeight = -1;
3484  bool bFirstParagraph = true;
3485 
3486  while( enumeration->hasMoreElements() )
3487  {
3489  Any any ( enumeration->nextElement() );
3490 
3491  if( any >>= paragraph)
3492  {
3493  if (bFirstParagraph && bWritePropertiesAsLstStyles)
3494  WriteLstStyles(paragraph, bOverridingCharHeight, nCharHeight, rXPropSet);
3495 
3496  WriteParagraph(paragraph, bOverridingCharHeight, nCharHeight, rXPropSet);
3497  bFirstParagraph = false;
3498  }
3499  }
3500 }
3501 
3502 void DrawingML::WritePresetShape( const char* pShape , std::vector< std::pair<sal_Int32,sal_Int32>> & rAvList )
3503 {
3504  mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
3505  if ( !rAvList.empty() )
3506  {
3507 
3508  mpFS->startElementNS(XML_a, XML_avLst);
3509  for (auto const& elem : rAvList)
3510  {
3511  OString sName = "adj" + ( ( elem.first > 0 ) ? OString::number(elem.first) : OString() );
3512  OString sFmla = "val " + OString::number( elem.second );
3513 
3514  mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
3515  }
3516  mpFS->endElementNS( XML_a, XML_avLst );
3517  }
3518  else
3519  mpFS->singleElementNS(XML_a, XML_avLst);
3520 
3521  mpFS->endElementNS( XML_a, XML_prstGeom );
3522 }
3523 
3524 void DrawingML::WritePresetShape( const char* pShape )
3525 {
3526  mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
3527  mpFS->singleElementNS(XML_a, XML_avLst);
3528  mpFS->endElementNS( XML_a, XML_prstGeom );
3529 }
3530 
3531 static std::map< OString, std::vector<OString> > lcl_getAdjNames()
3532 {
3533  std::map< OString, std::vector<OString> > aRet;
3534 
3535  OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/oox-drawingml-adj-names");
3536  rtl::Bootstrap::expandMacros(aPath);
3537  SvFileStream aStream(aPath, StreamMode::READ);
3538  if (aStream.GetError() != ERRCODE_NONE)
3539  SAL_WARN("oox.shape", "failed to open oox-drawingml-adj-names");
3540  OString aLine;
3541  bool bNotDone = aStream.ReadLine(aLine);
3542  while (bNotDone)
3543  {
3544  sal_Int32 nIndex = 0;
3545  // Each line is in a "key\tvalue" format: read the key, the rest is the value.
3546  OString aKey = aLine.getToken(0, '\t', nIndex);
3547  OString aValue = aLine.copy(nIndex);
3548  aRet[aKey].push_back(aValue);
3549  bNotDone = aStream.ReadLine(aLine);
3550  }
3551  return aRet;
3552 }
3553 
3554 void DrawingML::WritePresetShape( const char* pShape, MSO_SPT eShapeType, bool bPredefinedHandlesUsed, const PropertyValue& rProp )
3555 {
3556  static std::map< OString, std::vector<OString> > aAdjMap = lcl_getAdjNames();
3557  // If there are predefined adj names for this shape type, look them up now.
3558  std::vector<OString> aAdjustments;
3559  if (aAdjMap.find(OString(pShape)) != aAdjMap.end())
3560  aAdjustments = aAdjMap[OString(pShape)];
3561 
3562  mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
3563  mpFS->startElementNS(XML_a, XML_avLst);
3564 
3565  Sequence< drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
3566  if ( ( rProp.Value >>= aAdjustmentSeq )
3567  && eShapeType != mso_sptActionButtonForwardNext // we have adjustments values for these type of shape, but MSO doesn't like them
3568  && eShapeType != mso_sptActionButtonBackPrevious // so they are now disabled
3569  && pShape != std::string_view("rect") //some shape types are commented out in pCustomShapeTypeTranslationTable[] & are being defaulted to rect & rect does not have adjustment values/name.
3570  )
3571  {
3572  SAL_INFO("oox.shape", "adj seq len: " << aAdjustmentSeq.getLength());
3573  sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
3574  if ( bPredefinedHandlesUsed )
3575  EscherPropertyContainer::LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );
3576 
3577  sal_Int32 nValue, nLength = aAdjustmentSeq.getLength();
3578  // aAdjustments will give info about the number of adj values for a particular geometry. For example for hexagon aAdjustments.size() will be 2 and for circular arrow it will be 5 as per lcl_getAdjNames.
3579  // Sometimes there are more values than needed, so we ignore the excessive ones.
3580  if (aAdjustments.size() <= o3tl::make_unsigned(nLength))
3581  {
3582  for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAdjustments.size()); i++)
3583  {
3584  if( EscherPropertyContainer::GetAdjustmentValue( aAdjustmentSeq[ i ], i, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
3585  {
3586  // If the document model doesn't have an adjustment name (e.g. shape was created from VML), then take it from the predefined list.
3587  OString aAdjName = aAdjustmentSeq[i].Name.isEmpty()
3588  ? aAdjustments[i]
3589  : aAdjustmentSeq[i].Name.toUtf8();
3590 
3591  mpFS->singleElementNS( XML_a, XML_gd,
3592  XML_name, aAdjName,
3593  XML_fmla, "val " + OString::number(nValue));
3594  }
3595  }
3596  }
3597  }
3598 
3599  mpFS->endElementNS( XML_a, XML_avLst );
3600  mpFS->endElementNS( XML_a, XML_prstGeom );
3601 }
3602 
3604  const Reference< XShape >& rXShape,
3605  const SdrObjCustomShape& rSdrObjCustomShape)
3606 {
3607  uno::Reference< beans::XPropertySet > aXPropSet;
3608  uno::Any aAny( rXShape->queryInterface(cppu::UnoType<beans::XPropertySet>::get()));
3609 
3610  if ( ! (aAny >>= aXPropSet) )
3611  return false;
3612 
3613  try
3614  {
3615  aAny = aXPropSet->getPropertyValue( "CustomShapeGeometry" );
3616  if ( !aAny.hasValue() )
3617  return false;
3618  }
3619  catch( const ::uno::Exception& )
3620  {
3621  return false;
3622  }
3623 
3624 
3625  auto pGeometrySeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny);
3626 
3627  if ( pGeometrySeq )
3628  {
3629  for( const beans::PropertyValue& rProp : *pGeometrySeq )
3630  {
3631  if ( rProp.Name == "Path" )
3632  {
3633  uno::Sequence<beans::PropertyValue> aPathProp;
3634  rProp.Value >>= aPathProp;
3635 
3636  uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs;
3637  uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
3638  uno::Sequence<awt::Size> aPathSize;
3639  for (const beans::PropertyValue& rPathProp : std::as_const(aPathProp))
3640  {
3641  if (rPathProp.Name == "Coordinates")
3642  rPathProp.Value >>= aPairs;
3643  else if (rPathProp.Name == "Segments")
3644  rPathProp.Value >>= aSegments;
3645  else if (rPathProp.Name == "SubViewSize")
3646  rPathProp.Value >>= aPathSize;
3647  }
3648 
3649  if ( !aPairs.hasElements() )
3650  return false;
3651 
3652  if ( !aSegments.hasElements() )
3653  {
3654  aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment>
3655  {
3656  { drawing::EnhancedCustomShapeSegmentCommand::MOVETO, 1 },
3657  { drawing::EnhancedCustomShapeSegmentCommand::LINETO,
3658  static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) )) },
3659  { drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH, 0 },
3660  { drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH, 0 }
3661  };
3662  };
3663 
3664  int nExpectedPairCount = std::accumulate(std::cbegin(aSegments), std::cend(aSegments), 0,
3665  [](const int nSum, const drawing::EnhancedCustomShapeSegment& rSegment) { return nSum + rSegment.Count; });
3666 
3667  if ( nExpectedPairCount > aPairs.getLength() )
3668  {
3669  SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs.");
3670  return false;
3671  }
3672 
3673  mpFS->startElementNS(XML_a, XML_custGeom);
3674  mpFS->singleElementNS(XML_a, XML_avLst);
3675  mpFS->singleElementNS(XML_a, XML_gdLst);
3676  mpFS->singleElementNS(XML_a, XML_ahLst);
3677  mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t",
3678  XML_r, "r", XML_b, "b");
3679  mpFS->startElementNS(XML_a, XML_pathLst);
3680 
3681  if ( aPathSize.hasElements() )
3682  {
3683  mpFS->startElementNS( XML_a, XML_path,
3684  XML_w, OString::number(aPathSize[0].Width),
3685  XML_h, OString::number(aPathSize[0].Height) );
3686  }
3687  else
3688  {
3689  sal_Int32 nXMin(0);
3690  aPairs[0].First.Value >>= nXMin;
3691  sal_Int32 nXMax = nXMin;
3692  sal_Int32 nYMin(0);
3693  aPairs[0].Second.Value >>= nYMin;
3694  sal_Int32 nYMax = nYMin;
3695 
3696  for ( const auto& rPair : std::as_const(aPairs) )
3697  {
3698  sal_Int32 nX = GetCustomGeometryPointValue(rPair.First, rSdrObjCustomShape);
3699  sal_Int32 nY = GetCustomGeometryPointValue(rPair.Second, rSdrObjCustomShape);
3700  if (nX < nXMin)
3701  nXMin = nX;
3702  if (nY < nYMin)
3703  nYMin = nY;
3704  if (nX > nXMax)
3705  nXMax = nX;
3706  if (nY > nYMax)
3707  nYMax = nY;
3708  }
3709  mpFS->startElementNS( XML_a, XML_path,
3710  XML_w, OString::number(nXMax - nXMin),
3711  XML_h, OString::number(nYMax - nYMin) );
3712  }
3713 
3714 
3715  int nPairIndex = 0;
3716  bool bOK = true;
3717  for (const auto& rSegment : std::as_const(aSegments))
3718  {
3719  if ( rSegment.Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
3720  {
3721  mpFS->singleElementNS(XML_a, XML_close);
3722  }
3723  for (int k = 0; k < rSegment.Count && bOK; ++k)
3724  {
3725  switch( rSegment.Command )
3726  {
3727  case drawing::EnhancedCustomShapeSegmentCommand::MOVETO :
3728  {
3729  if (nPairIndex >= aPairs.getLength())
3730  bOK = false;
3731  else
3732  {
3733  mpFS->startElementNS(XML_a, XML_moveTo);
3734  WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
3735  mpFS->endElementNS( XML_a, XML_moveTo );
3736  nPairIndex++;
3737  }
3738  break;
3739  }
3740  case drawing::EnhancedCustomShapeSegmentCommand::LINETO :
3741  {
3742  if (nPairIndex >= aPairs.getLength())
3743  bOK = false;
3744  else
3745  {
3746  mpFS->startElementNS(XML_a, XML_lnTo);
3747  WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
3748  mpFS->endElementNS( XML_a, XML_lnTo );
3749  nPairIndex++;
3750  }
3751  break;
3752  }
3753  case drawing::EnhancedCustomShapeSegmentCommand::CURVETO :
3754  {
3755  if (nPairIndex + 2 >= aPairs.getLength())
3756  bOK = false;
3757  else
3758  {
3759  mpFS->startElementNS(XML_a, XML_cubicBezTo);
3760  for( sal_uInt8 l = 0; l <= 2; ++l )
3761  {
3762  WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3763  }
3764  mpFS->endElementNS( XML_a, XML_cubicBezTo );
3765  nPairIndex += 3;
3766  }
3767  break;
3768  }
3769  case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
3770  case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
3771  {
3772  nPairIndex += 3;
3773  break;
3774  }
3775  case drawing::EnhancedCustomShapeSegmentCommand::ARCTO :
3776  case drawing::EnhancedCustomShapeSegmentCommand::ARC :
3777  case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
3778  case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
3779  {
3780  nPairIndex += 4;
3781  break;
3782  }
3783  case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
3784  case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
3785  {
3786  nPairIndex++;
3787  break;
3788  }
3789  case drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO :
3790  {
3791  if (nPairIndex + 1 >= aPairs.getLength())
3792  bOK = false;
3793  else
3794  {
3795  mpFS->startElementNS(XML_a, XML_quadBezTo);
3796  for( sal_uInt8 l = 0; l < 2; ++l )
3797  {
3798  WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3799  }
3800  mpFS->endElementNS( XML_a, XML_quadBezTo );
3801  nPairIndex += 2;
3802  }
3803  break;
3804  }
3805  case drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO :
3806  {
3807  if (nPairIndex + 1 >= aPairs.getLength())
3808  bOK = false;
3809  else
3810  {
3811  const EnhancedCustomShape2d aCustoShape2d(
3812  const_cast<SdrObjCustomShape&>(rSdrObjCustomShape));
3813  double fWR = 0.0;
3814  aCustoShape2d.GetParameter(fWR, aPairs[nPairIndex].First, false,
3815  false);
3816  double fHR = 0.0;
3817  aCustoShape2d.GetParameter(fHR, aPairs[nPairIndex].Second,
3818  false, false);
3819  double fStartAngle = 0.0;
3820  aCustoShape2d.GetParameter(
3821  fStartAngle, aPairs[nPairIndex + 1].First, false, false);
3822  sal_Int32 nStartAng(std::lround(fStartAngle * 60000));
3823  double fSwingAng = 0.0;
3824  aCustoShape2d.GetParameter(
3825  fSwingAng, aPairs[nPairIndex + 1].Second, false, false);
3826  sal_Int32 nSwingAng(std::lround(fSwingAng * 60000));
3827  mpFS->singleElement(FSNS(XML_a, XML_arcTo),
3828  XML_wR, OString::number(fWR),
3829  XML_hR, OString::number(fHR),
3830  XML_stAng, OString::number(nStartAng),
3831  XML_swAng, OString::number(nSwingAng));
3832  nPairIndex += 2;
3833  }
3834  break;
3835  }
3836  default:
3837  // do nothing
3838  break;
3839  }
3840  }
3841  if (!bOK)
3842  break;
3843  }
3844  mpFS->endElementNS( XML_a, XML_path );
3845  mpFS->endElementNS( XML_a, XML_pathLst );
3846  mpFS->endElementNS( XML_a, XML_custGeom );
3847  return bOK;
3848  }
3849  }
3850  }
3851  return false;
3852 }
3853 
3855  const drawing::EnhancedCustomShapeParameterPair& rParamPair,
3856  const SdrObjCustomShape& rSdrObjCustomShape)
3857 {
3858  sal_Int32 nX = GetCustomGeometryPointValue(rParamPair.First, rSdrObjCustomShape);
3859  sal_Int32 nY = GetCustomGeometryPointValue(rParamPair.Second, rSdrObjCustomShape);
3860 
3861  mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(nX), XML_y, OString::number(nY));
3862 }
3863 
3865  const css::drawing::EnhancedCustomShapeParameter& rParam,
3866  const SdrObjCustomShape& rSdrObjCustomShape)
3867 {
3868  const EnhancedCustomShape2d aCustoShape2d(const_cast< SdrObjCustomShape& >(rSdrObjCustomShape));
3869  double fValue = 0.0;
3870  aCustoShape2d.GetParameter(fValue, rParam, false, false);
3871  sal_Int32 nValue(std::lround(fValue));
3872 
3873  return nValue;
3874 }
3875 
3876 // version for SdrObjCustomShape
3877 void DrawingML::WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape,
3878  const tools::PolyPolygon& rPolyPolygon, const bool bClosed)
3879 {
3880  // In case of Writer, the parent element is <wps:spPr>, and there the
3881  // <a:custGeom> element is not optional.
3882  if (rPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX)
3883  return;
3884 
3885  mpFS->startElementNS(XML_a, XML_custGeom);
3886  mpFS->singleElementNS(XML_a, XML_avLst);
3887  mpFS->singleElementNS(XML_a, XML_gdLst);
3888  mpFS->singleElementNS(XML_a, XML_ahLst);
3889  mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b");
3890 
3891  mpFS->startElementNS( XML_a, XML_pathLst );
3892 
3893  const tools::Rectangle aRect( rPolyPolygon.GetBoundRect() );
3894 
3895  // tdf#101122
3896  std::optional<OString> sFill;
3897  if (HasEnhancedCustomShapeSegmentCommand(rXShape, css::drawing::EnhancedCustomShapeSegmentCommand::NOFILL))
3898  sFill = "none"; // for possible values see ST_PathFillMode in OOXML standard
3899 
3900  // Put all polygons of rPolyPolygon in the same path element
3901  // to subtract the overlapped areas.
3902  mpFS->startElementNS( XML_a, XML_path,
3903  XML_fill, sFill,
3904  XML_w, OString::number(aRect.GetWidth()),
3905  XML_h, OString::number(aRect.GetHeight()) );
3906 
3907  for( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i ++ )
3908  {
3909 
3910  const tools::Polygon& rPoly = rPolyPolygon[ i ];
3911 
3912  if( rPoly.GetSize() > 0 )
3913  {
3914  mpFS->startElementNS(XML_a, XML_moveTo);
3915 
3916  mpFS->singleElementNS( XML_a, XML_pt,
3917  XML_x, OString::number(rPoly[0].X() - aRect.Left()),
3918  XML_y, OString::number(rPoly[0].Y() - aRect.Top()) );
3919 
3920  mpFS->endElementNS( XML_a, XML_moveTo );
3921  }
3922 
3923  for( sal_uInt16 j = 1; j < rPoly.GetSize(); j ++ )
3924  {
3925  PolyFlags flags = rPoly.GetFlags(j);
3926  if( flags == PolyFlags::Control )
3927  {
3928  // a:cubicBezTo can only contain 3 a:pt elements, so we need to make sure of this
3929  if( j+2 < rPoly.GetSize() && rPoly.GetFlags(j+1) == PolyFlags::Control && rPoly.GetFlags(j+2) != PolyFlags::Control )
3930  {
3931 
3932  mpFS->startElementNS(XML_a, XML_cubicBezTo);
3933  for( sal_uInt8 k = 0; k <= 2; ++k )
3934  {
3935  mpFS->singleElementNS(XML_a, XML_pt,
3936  XML_x, OString::number(rPoly[j+k].X() - aRect.Left()),
3937  XML_y, OString::number(rPoly[j+k].Y() - aRect.Top()));
3938 
3939  }
3940  mpFS->endElementNS( XML_a, XML_cubicBezTo );
3941  j += 2;
3942  }
3943  }
3944  else if( flags == PolyFlags::Normal )
3945  {
3946  mpFS->startElementNS(XML_a, XML_lnTo);
3947  mpFS->singleElementNS( XML_a, XML_pt,
3948  XML_x, OString::number(rPoly[j].X() - aRect.Left()),
3949  XML_y, OString::number(rPoly[j].Y() - aRect.Top()) );
3950  mpFS->endElementNS( XML_a, XML_lnTo );
3951  }
3952  }
3953  }
3954  if (bClosed)
3955  mpFS->singleElementNS( XML_a, XML_close);
3956  mpFS->endElementNS( XML_a, XML_path );
3957 
3958  mpFS->endElementNS( XML_a, XML_pathLst );
3959 
3960  mpFS->endElementNS( XML_a, XML_custGeom );
3961 }
3962 
3963 // version for SdrPathObj
3964 void DrawingML::WritePolyPolygon(const css::uno::Reference<css::drawing::XShape>& rXShape,
3965  const bool bClosed)
3966 {
3968  // In case of Writer, the parent element is <wps:spPr>, and there the
3969  // <a:custGeom> element is not optional.
3970  if (aPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX)
3971  return;
3972 
3973  mpFS->startElementNS(XML_a, XML_custGeom);
3974  mpFS->singleElementNS(XML_a, XML_avLst);
3975  mpFS->singleElementNS(XML_a, XML_gdLst);
3976  mpFS->singleElementNS(XML_a, XML_ahLst);
3977  mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b");
3978 
3979  mpFS->startElementNS(XML_a, XML_pathLst);
3980 
3981  awt::Size aSize = rXShape->getSize();
3982  awt::Point aPos = rXShape->getPosition();
3983  Reference<XPropertySet> xPropertySet(rXShape, UNO_QUERY);
3984  uno::Reference<XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
3985  if (xPropertySetInfo->hasPropertyByName("AnchorPosition"))
3986  {
3987  awt::Point aAnchorPosition;
3988  xPropertySet->getPropertyValue("AnchorPosition") >>= aAnchorPosition;
3989  aPos.X += aAnchorPosition.X;
3990  aPos.Y += aAnchorPosition.Y;
3991  }
3992 
3993  // Only closed SdrPathObj can be filled
3994  std::optional<OString> sFill;
3995  if (!bClosed)
3996  sFill = "none"; // for possible values see ST_PathFillMode in OOXML standard
3997 
3998  // Put all polygons of rPolyPolygon in the same path element
3999  // to subtract the overlapped areas.
4000  mpFS->startElementNS(XML_a, XML_path, XML_fill, sFill, XML_w, OString::number(aSize.Width),
4001  XML_h, OString::number(aSize.Height));
4002 
4003  for (sal_uInt16 i = 0; i < aPolyPolygon.Count(); i++)
4004  {
4005  const tools::Polygon& aPoly = aPolyPolygon[i];
4006 
4007  if (aPoly.GetSize() > 0)
4008  {
4009  mpFS->startElementNS(XML_a, XML_moveTo);
4010 
4011  mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(aPoly[0].X() - aPos.X),
4012  XML_y, OString::number(aPoly[0].Y() - aPos.Y));
4013 
4014  mpFS->endElementNS(XML_a, XML_moveTo);
4015  }
4016 
4017  for (sal_uInt16 j = 1; j < aPoly.GetSize(); j++)
4018  {
4019  PolyFlags flags = aPoly.GetFlags(j);
4020  if (flags == PolyFlags::Control)
4021  {
4022  // a:cubicBezTo can only contain 3 a:pt elements, so we need to make sure of this
4023  if (j + 2 < aPoly.GetSize() && aPoly.GetFlags(j + 1) == PolyFlags::Control
4024  && aPoly.GetFlags(j + 2) != PolyFlags::Control)
4025  {
4026  mpFS->startElementNS(XML_a, XML_cubicBezTo);
4027  for (sal_uInt8 k = 0; k <= 2; ++k)
4028  {
4029  mpFS->singleElementNS(XML_a, XML_pt, XML_x,
4030  OString::number(aPoly[j + k].X() - aPos.X), XML_y,
4031  OString::number(aPoly[j + k].Y() - aPos.Y));
4032  }
4033  mpFS->endElementNS(XML_a, XML_cubicBezTo);
4034  j += 2;
4035  }
4036  }
4037  else if (flags == PolyFlags::Normal)
4038  {
4039  mpFS->startElementNS(XML_a, XML_lnTo);
4040  mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(aPoly[j].X() - aPos.X),
4041  XML_y, OString::number(aPoly[j].Y() - aPos.Y));
4042  mpFS->endElementNS(XML_a, XML_lnTo);
4043  }
4044  }
4045  }
4046  if (bClosed)
4047  mpFS->singleElementNS(XML_a, XML_close);
4048  mpFS->endElementNS(XML_a, XML_path);
4049 
4050  mpFS->endElementNS(XML_a, XML_pathLst);
4051 
4052  mpFS->endElementNS(XML_a, XML_custGeom);
4053 }
4054 
4055 void DrawingML::WriteConnectorConnections( EscherConnectorListEntry& rConnectorEntry, sal_Int32 nStartID, sal_Int32 nEndID )
4056 {
4057  if( nStartID != -1 )
4058  {
4059  mpFS->singleElementNS( XML_a, XML_stCxn,
4060  XML_id, OString::number(nStartID),
4061  XML_idx, OString::number(rConnectorEntry.GetConnectorRule(true)) );
4062  }
4063  if( nEndID != -1 )
4064  {
4065  mpFS->singleElementNS( XML_a, XML_endCxn,
4066  XML_id, OString::number(nEndID),
4067  XML_idx, OString::number(rConnectorEntry.GetConnectorRule(false)) );
4068  }
4069 }
4070 
4071 sal_Unicode DrawingML::SubstituteBullet( sal_Unicode cBulletId, css::awt::FontDescriptor& rFontDesc )
4072 {
4073  if ( IsStarSymbol(rFontDesc.Name) )
4074  {
4075  rtl_TextEncoding eCharSet = rFontDesc.CharSet;
4076  cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eCharSet, rFontDesc.Name);
4077  rFontDesc.CharSet = eCharSet;
4078  }
4079 
4080  return cBulletId;
4081 }
4082 
4084  const OUString& sFullStream,
4085  std::u16string_view sRelativeStream,
4086  const Reference< XOutputStream >& xParentRelation,
4087  const char* sContentType,
4088  const char* sRelationshipType,
4089  OUString* pRelationshipId )
4090 {
4091  OUString sRelationshipId;
4092  if (xParentRelation.is())
4093  sRelationshipId = GetFB()->addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream );
4094  else
4095  sRelationshipId = GetFB()->addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream );
4096 
4097  if( pRelationshipId )
4098  *pRelationshipId = sRelationshipId;
4099 
4100  sax_fastparser::FSHelperPtr p = GetFB()->openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
4101 
4102  return p;
4103 }
4104 
4106 {
4107  if ( !GetProperty( xPropSet, "FillStyle" ) )
4108  return;
4109  FillStyle aFillStyle( FillStyle_NONE );
4110  xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle;
4111 
4112  // map full transparent background to no fill
4113  if ( aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparence" ) )
4114  {
4115  sal_Int16 nVal = 0;
4116  xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal;
4117  if ( nVal == 100 )
4118  aFillStyle = FillStyle_NONE;
4119  }
4120  if (aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparenceGradient"))
4121  {
4122  awt::Gradient aTransparenceGradient;
4123  mAny >>= aTransparenceGradient;
4124  if (aTransparenceGradient.StartColor == 0xffffff && aTransparenceGradient.EndColor == 0xffffff)
4125  aFillStyle = FillStyle_NONE;
4126  }
4127 
4128  switch( aFillStyle )
4129  {
4130  case FillStyle_SOLID :
4131  WriteSolidFill( xPropSet );
4132  break;
4133  case FillStyle_GRADIENT :
4134  WriteGradientFill( xPropSet );
4135  break;
4136  case FillStyle_BITMAP :
4137  WriteBlipFill( xPropSet, "FillBitmap" );
4138  break;
4139  case FillStyle_HATCH :
4140  WritePattFill( xPropSet );
4141  break;
4142  case FillStyle_NONE:
4143  mpFS->singleElementNS(XML_a, XML_noFill);
4144  break;
4145  default:
4146  ;
4147  }
4148 }
4149 
4150 void DrawingML::WriteStyleProperties( sal_Int32 nTokenId, const Sequence< PropertyValue >& aProperties )
4151 {
4152  if( aProperties.hasElements() )
4153  {
4154  OUString sSchemeClr;
4155  sal_uInt32 nIdx = 0;
4156  Sequence< PropertyValue > aTransformations;
4157  for( const auto& rProp : aProperties)
4158  {
4159  if( rProp.Name == "SchemeClr" )
4160  rProp.Value >>= sSchemeClr;
4161  else if( rProp.Name == "Idx" )
4162  rProp.Value >>= nIdx;
4163  else if( rProp.Name == "Transformations" )
4164  rProp.Value >>= aTransformations;
4165  }
4166  mpFS->startElementNS(XML_a, nTokenId, XML_idx, OString::number(nIdx));
4167  WriteColor(sSchemeClr, aTransformations);
4168  mpFS->endElementNS( XML_a, nTokenId );
4169  }
4170  else
4171  {
4172  // write mock <a:*Ref> tag
4173  mpFS->singleElementNS(XML_a, nTokenId, XML_idx, OString::number(0));
4174  }
4175 }
4176 
4178 {
4179  // check existence of the grab bag
4180  if ( !GetProperty( xPropSet, "InteropGrabBag" ) )
4181  return;
4182 
4183  // extract the relevant properties from the grab bag
4184  Sequence< PropertyValue > aGrabBag;
4185  Sequence< PropertyValue > aFillRefProperties, aLnRefProperties, aEffectRefProperties;
4186  mAny >>= aGrabBag;
4187  for( const auto& rProp : std::as_const(aGrabBag))
4188  {
4189  if( rProp.Name == "StyleFillRef" )
4190  rProp.Value >>= aFillRefProperties;
4191  else if( rProp.Name == "StyleLnRef" )
4192  rProp.Value >>= aLnRefProperties;
4193  else if( rProp.Name == "StyleEffectRef" )
4194  rProp.Value >>= aEffectRefProperties;
4195  }
4196 
4197  WriteStyleProperties( XML_lnRef, aLnRefProperties );
4198  WriteStyleProperties( XML_fillRef, aFillRefProperties );
4199  WriteStyleProperties( XML_effectRef, aEffectRefProperties );
4200 
4201  // write mock <a:fontRef>
4202  mpFS->singleElementNS(XML_a, XML_fontRef, XML_idx, "minor");
4203 }
4204 
4205 void DrawingML::WriteShapeEffect( std::u16string_view sName, const Sequence< PropertyValue >& aEffectProps )
4206 {
4207  if( !aEffectProps.hasElements() )
4208  return;
4209 
4210  // assign the proper tag and enable bContainsColor if necessary
4211  sal_Int32 nEffectToken = 0;
4212  bool bContainsColor = false;
4213  if( sName == u"outerShdw" )
4214  {
4215  nEffectToken = FSNS( XML_a, XML_outerShdw );
4216  bContainsColor = true;
4217  }
4218  else if( sName == u"innerShdw" )
4219  {
4220  nEffectToken = FSNS( XML_a, XML_innerShdw );
4221  bContainsColor = true;
4222  }
4223  else if( sName == u"glow" )
4224  {
4225  nEffectToken = FSNS( XML_a, XML_glow );
4226  bContainsColor = true;
4227  }
4228  else if( sName == u"softEdge" )
4229  nEffectToken = FSNS( XML_a, XML_softEdge );
4230  else if( sName == u"reflection" )
4231  nEffectToken = FSNS( XML_a, XML_reflection );
4232  else if( sName == u"blur" )
4233  nEffectToken = FSNS( XML_a, XML_blur );
4234 
4235  OUString sSchemeClr;
4236  ::Color nRgbClr;
4237  sal_Int32 nAlpha = MAX_PERCENT;
4238  Sequence< PropertyValue > aTransformations;
4239  rtl::Reference<sax_fastparser::FastAttributeList> aOuterShdwAttrList = FastSerializerHelper::createAttrList();
4240  for( const auto& rEffectProp : aEffectProps )
4241  {
4242  if( rEffectProp.Name == "Attribs" )
4243  {
4244  // read tag attributes
4245  uno::Sequence< beans::PropertyValue > aOuterShdwProps;
4246  rEffectProp.Value >>= aOuterShdwProps;
4247  for( const auto& rOuterShdwProp : std::as_const(aOuterShdwProps) )
4248  {
4249  if( rOuterShdwProp.Name == "algn" )
4250  {
4251  OUString sVal;
4252  rOuterShdwProp.Value >>= sVal;
4253  aOuterShdwAttrList->add( XML_algn, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ) );
4254  }
4255  else if( rOuterShdwProp.Name == "blurRad" )
4256  {
4257  sal_Int64 nVal = 0;
4258  rOuterShdwProp.Value >>= nVal;
4259  aOuterShdwAttrList->add( XML_blurRad, OString::number( nVal ).getStr() );
4260  }
4261  else if( rOuterShdwProp.Name == "dir" )
4262  {
4263  sal_Int32 nVal = 0;
4264  rOuterShdwProp.Value >>= nVal;
4265  aOuterShdwAttrList->add( XML_dir, OString::number( nVal ).getStr() );
4266  }
4267  else if( rOuterShdwProp.Name == "dist" )
4268  {
4269  sal_Int32 nVal = 0;
4270  rOuterShdwProp.Value >>= nVal;
4271  aOuterShdwAttrList->add( XML_dist, OString::number( nVal ).getStr() );
4272  }
4273  else if( rOuterShdwProp.Name == "kx" )
4274  {
4275  sal_Int32 nVal = 0;
4276  rOuterShdwProp.Value >>= nVal;
4277  aOuterShdwAttrList->add( XML_kx, OString::number( nVal ).getStr() );
4278  }
4279  else if( rOuterShdwProp.Name == "ky" )
4280  {
4281  sal_Int32 nVal = 0;
4282  rOuterShdwProp.Value >>= nVal;
4283  aOuterShdwAttrList->add( XML_ky, OString::number( nVal ).getStr() );
4284  }
4285  else if( rOuterShdwProp.Name == "rotWithShape" )
4286  {
4287  sal_Int32 nVal = 0;
4288  rOuterShdwProp.Value >>= nVal;
4289  aOuterShdwAttrList->add( XML_rotWithShape, OString::number( nVal ).getStr() );
4290  }
4291  else if( rOuterShdwProp.Name == "sx" )
4292  {
4293  sal_Int32 nVal = 0;
4294  rOuterShdwProp.Value >>= nVal;
4295  aOuterShdwAttrList->add( XML_sx, OString::number( nVal ).getStr() );
4296  }
4297  else if( rOuterShdwProp.Name == "sy" )
4298  {
4299  sal_Int32 nVal = 0;
4300  rOuterShdwProp.Value >>= nVal;
4301  aOuterShdwAttrList->add( XML_sy, OString::number( nVal ).getStr() );
4302  }
4303  else if( rOuterShdwProp.Name == "rad" )
4304  {
4305  sal_Int64 nVal = 0;
4306  rOuterShdwProp.Value >>= nVal;
4307  aOuterShdwAttrList->add( XML_rad, OString::number( nVal ).getStr() );
4308  }
4309  else if( rOuterShdwProp.Name == "endA" )
4310  {
4311  sal_Int32 nVal = 0;
4312  rOuterShdwProp.Value >>= nVal;
4313  aOuterShdwAttrList->add( XML_endA, OString::number( nVal ).getStr() );
4314  }
4315  else if( rOuterShdwProp.Name == "endPos" )
4316  {
4317  sal_Int32 nVal = 0;
4318  rOuterShdwProp.Value >>= nVal;
4319  aOuterShdwAttrList->add( XML_endPos, OString::number( nVal ).getStr() );
4320  }
4321  else if( rOuterShdwProp.Name == "fadeDir" )
4322  {
4323  sal_Int32 nVal = 0;
4324  rOuterShdwProp.Value >>= nVal;
4325  aOuterShdwAttrList->add( XML_fadeDir, OString::number( nVal ).getStr() );
4326  }
4327  else if( rOuterShdwProp.Name == "stA" )
4328  {
4329  sal_Int32 nVal = 0;
4330  rOuterShdwProp.Value >>= nVal;
4331  aOuterShdwAttrList->add( XML_stA, OString::number( nVal ).getStr() );
4332  }
4333  else if( rOuterShdwProp.Name == "stPos" )
4334  {
4335  sal_Int32 nVal = 0;
4336  rOuterShdwProp.Value >>= nVal;
4337  aOuterShdwAttrList->add( XML_stPos, OString::number( nVal ).getStr() );
4338  }
4339  else if( rOuterShdwProp.Name == "grow" )
4340  {
4341  sal_Int32 nVal = 0;
4342  rOuterShdwProp.Value >>= nVal;
4343  aOuterShdwAttrList->add( XML_grow, OString::number( nVal ).getStr() );
4344  }
4345  }
4346  }
4347  else if(rEffectProp.Name == "RgbClr")
4348  {
4349  rEffectProp.Value >>= nRgbClr;
4350  }
4351  else if(rEffectProp.Name == "RgbClrTransparency")
4352  {
4353  sal_Int32 nTransparency;
4354  if (rEffectProp.Value >>= nTransparency)
4355  // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
4356  nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
4357  }
4358  else if(rEffectProp.Name == "SchemeClr")
4359  {
4360  rEffectProp.Value >>= sSchemeClr;
4361  }
4362  else if(rEffectProp.Name == "SchemeClrTransformations")
4363  {
4364  rEffectProp.Value >>= aTransformations;
4365  }
4366  }
4367 
4368  if( nEffectToken <= 0 )
4369  return;
4370 
4371  mpFS->startElement( nEffectToken, aOuterShdwAttrList );
4372 
4373  if( bContainsColor )
4374  {
4375  if( sSchemeClr.isEmpty() )
4376  WriteColor( nRgbClr, nAlpha );
4377  else
4378  WriteColor( sSchemeClr, aTransformations );
4379  }
4380 
4381  mpFS->endElement( nEffectToken );
4382 }
4383 
4384 static sal_Int32 lcl_CalculateDist(const double dX, const double dY)
4385 {
4386  return static_cast< sal_Int32 >(sqrt(dX*dX + dY*dY) * 360);
4387 }
4388 
4389 static sal_Int32 lcl_CalculateDir(const double dX, const double dY)
4390 {
4391  return (static_cast< sal_Int32 >(basegfx::rad2deg(atan2(dY,dX)) * 60000) + 21600000) % 21600000;
4392 }
4393 
4395 {
4396  Sequence< PropertyValue > aGrabBag, aEffects, aOuterShdwProps;
4397  bool bHasInteropGrabBag = rXPropSet->getPropertySetInfo()->hasPropertyByName("InteropGrabBag");
4398  if (bHasInteropGrabBag && GetProperty(rXPropSet, "InteropGrabBag"))
4399  {
4400  mAny >>= aGrabBag;
4401  auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
4402  [](const PropertyValue& rProp) { return rProp.Name == "EffectProperties"; });
4403  if (pProp != std::cend(aGrabBag))
4404  {
4405  pProp->Value >>= aEffects;
4406  auto pEffect = std::find_if(std::cbegin(aEffects), std::cend(aEffects),
4407  [](const PropertyValue& rEffect) { return rEffect.Name == "outerShdw"; });
4408  if (pEffect != std::cend(aEffects))
4409  pEffect->Value >>= aOuterShdwProps;
4410  }
4411  }
4412 
4413  // tdf#132201: the order of effects is important. Effects order (CT_EffectList in ECMA-376):
4414  // blur -> fillOverlay -> glow -> innerShdw -> outerShdw -> prstShdw -> reflection -> softEdge
4415 
4416  if( !aEffects.hasElements() )
4417  {
4418  bool bHasShadow = false;
4419  if( GetProperty( rXPropSet, "Shadow" ) )
4420  mAny >>= bHasShadow;
4421  bool bHasEffects = bHasShadow;
4422  if (!bHasEffects && GetProperty(rXPropSet, "GlowEffectRadius"))
4423  {
4424  sal_Int32 rad = 0;
4425  mAny >>= rad;
4426  bHasEffects = rad > 0;
4427  }
4428  if (!bHasEffects && GetProperty(rXPropSet, "SoftEdgeRadius"))
4429  {
4430  sal_Int32 rad = 0;
4431  mAny >>= rad;
4432  bHasEffects = rad > 0;
4433  }
4434 
4435  if (bHasEffects)
4436  {
4437  mpFS->startElementNS(XML_a, XML_effectLst);
4438  WriteGlowEffect(rXPropSet);
4439  if( bHasShadow )
4440  {
4441  Sequence< PropertyValue > aShadowGrabBag( 3 );
4442  Sequence< PropertyValue > aShadowAttribsGrabBag( 4 );
4443 
4444  double dX = +0.0, dY = +0.0;
4445  sal_Int32 nBlur =0;
4446  rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
4447  rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
4448  rXPropSet->getPropertyValue( "ShadowBlur" ) >>= nBlur;
4449 
4450  aShadowAttribsGrabBag[0].Name = "dist";
4451  aShadowAttribsGrabBag[0].Value <<= lcl_CalculateDist(dX, dY);
4452  aShadowAttribsGrabBag[1].Name = "dir";
4453  aShadowAttribsGrabBag[1].Value <<= lcl_CalculateDir(dX, dY);
4454  aShadowAttribsGrabBag[2].Name = "blurRad";
4455  aShadowAttribsGrabBag[2].Value <<= oox::drawingml::convertHmmToEmu(nBlur);
4456  aShadowAttribsGrabBag[3].Name = "rotWithShape";
4457  aShadowAttribsGrabBag[3].Value <<= false; //ooxml default is 'true', so must write it
4458 
4459  aShadowGrabBag[0].Name = "Attribs";
4460  aShadowGrabBag[0].Value <<= aShadowAttribsGrabBag;
4461  aShadowGrabBag[1].Name = "RgbClr";
4462  aShadowGrabBag[1].Value = rXPropSet->getPropertyValue( "ShadowColor" );
4463  aShadowGrabBag[2].Name = "RgbClrTransparency";
4464  aShadowGrabBag[2].Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
4465 
4466  WriteShapeEffect( u"outerShdw", aShadowGrabBag );
4467  }
4468  WriteSoftEdgeEffect(rXPropSet);
4469  mpFS->endElementNS(XML_a, XML_effectLst);
4470  }
4471  }
4472  else
4473  {
4474  for( auto& rOuterShdwProp : asNonConstRange(aOuterShdwProps) )
4475  {
4476  if( rOuterShdwProp.Name == "Attribs" )
4477  {
4478  Sequence< PropertyValue > aAttribsProps;
4479  rOuterShdwProp.Value >>= aAttribsProps;
4480 
4481  double dX = +0.0, dY = +0.0;
4482  sal_Int32 nBlur =0;
4483  rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
4484  rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
4485  rXPropSet->getPropertyValue( "ShadowBlur" ) >>= nBlur;
4486 
4487 
4488  for( auto& rAttribsProp : asNonConstRange(aAttribsProps) )
4489  {
4490  if( rAttribsProp.Name == "dist" )
4491  {
4492  rAttribsProp.Value <<= lcl_CalculateDist(dX, dY);
4493  }
4494  else if( rAttribsProp.Name == "dir" )
4495  {
4496  rAttribsProp.Value <<= lcl_CalculateDir(dX, dY);
4497  }
4498  else if( rAttribsProp.Name == "blurRad" )
4499  {
4500  rAttribsProp.Value <<= oox::drawingml::convertHmmToEmu(nBlur);
4501  }
4502  }
4503 
4504  rOuterShdwProp.Value <<= aAttribsProps;
4505  }
4506  else if( rOuterShdwProp.Name == "RgbClr" )
4507  {
4508  rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowColor" );
4509  }
4510  else if( rOuterShdwProp.Name == "RgbClrTransparency" )
4511  {
4512  rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
4513  }
4514  }
4515 
4516  mpFS->startElementNS(XML_a, XML_effectLst);
4517  bool bGlowWritten = false;
4518  for( const auto& rEffect : std::as_const(aEffects) )
4519  {
4520  if (!bGlowWritten
4521  && (rEffect.Name == "innerShdw" || rEffect.Name == "outerShdw"
4522  || rEffect.Name == "prstShdw" || rEffect.Name == "reflection"
4523  || rEffect.Name == "softEdge"))
4524  {
4525  WriteGlowEffect(rXPropSet);
4526  bGlowWritten = true;
4527  }
4528 
4529  if( rEffect.Name == "outerShdw" )
4530  {
4531  WriteShapeEffect( rEffect.Name, aOuterShdwProps );
4532  }
4533  else
4534  {
4535  Sequence< PropertyValue > aEffectProps;
4536  rEffect.Value >>= aEffectProps;
4537  WriteShapeEffect( rEffect.Name, aEffectProps );
4538  }
4539  }
4540  if (!bGlowWritten)
4541  WriteGlowEffect(rXPropSet);
4542  WriteSoftEdgeEffect(rXPropSet); // the last
4543 
4544  mpFS->endElementNS(XML_a, XML_effectLst);
4545  }
4546 }
4547 
4549 {
4550  if (!rXPropSet->getPropertySetInfo()->hasPropertyByName("GlowEffectRadius"))
4551  {
4552  return;
4553  }
4554 
4555  sal_Int32 nRad = 0;
4556  rXPropSet->getPropertyValue("GlowEffectRadius") >>= nRad;
4557  if (!nRad)
4558  return;
4559 
4560  Sequence< PropertyValue > aGlowAttribs(1);
4561  aGlowAttribs[0].Name = "rad";
4562  aGlowAttribs[0].Value <<= oox::drawingml::convertHmmToEmu(nRad);
4563  Sequence< PropertyValue > aGlowProps(3);
4564  aGlowProps[0].Name = "Attribs";
4565  aGlowProps[0].Value <<= aGlowAttribs;
4566  aGlowProps[1].Name = "RgbClr";
4567  aGlowProps[1].Value = rXPropSet->getPropertyValue("GlowEffectColor");
4568  aGlowProps[2].Name = "RgbClrTransparency";
4569  aGlowProps[2].Value = rXPropSet->getPropertyValue("GlowEffectTransparency");
4570  // TODO other stuff like saturation or luminance
4571 
4572  WriteShapeEffect(u"glow", aGlowProps);
4573 }
4574 
4575 void DrawingML::WriteSoftEdgeEffect(const css::uno::Reference<css::beans::XPropertySet>& rXPropSet)
4576 {
4577  if (!rXPropSet->getPropertySetInfo()->hasPropertyByName("SoftEdgeRadius"))
4578  {
4579  return;
4580  }
4581 
4582  sal_Int32 nRad = 0;
4583  rXPropSet->getPropertyValue("SoftEdgeRadius") >>= nRad;
4584  if (!nRad)
4585  return;
4586 
4587  css::uno::Sequence<css::beans::PropertyValue> aAttribs(1);
4588  aAttribs[0].Name = "rad";
4589  aAttribs[0].Value <<= oox::drawingml::convertHmmToEmu(nRad);
4590  css::uno::Sequence<css::beans::PropertyValue> aProps(1);
4591  aProps[0].Name = "Attribs";
4592  aProps[0].Value <<= aAttribs;
4593 
4594  WriteShapeEffect(u"softEdge", aProps);
4595 }
4596 
4598  const css::uno::Reference<css::drawing::XShape>& rXShape, const sal_Int16 nCommand)
4599 {
4600  try
4601  {
4602  uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY_THROW);
4603  if (!GetProperty(xPropSet, "CustomShapeGeometry"))
4604  return false;
4605  Sequence<PropertyValue> aCustomShapeGeometryProps;
4606  mAny >>= aCustomShapeGeometryProps;
4607  for (const beans::PropertyValue& rGeomProp : std::as_const(aCustomShapeGeometryProps))
4608  {
4609  if (rGeomProp.Name == "Path")
4610  {
4611  uno::Sequence<beans::PropertyValue> aPathProps;
4612  rGeomProp.Value >>= aPathProps;
4613  for (const beans::PropertyValue& rPathProp : std::as_const(aPathProps))
4614  {
4615  if (rPathProp.Name == "Segments")
4616  {
4617  uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
4618  rPathProp.Value >>= aSegments;
4619  for (const auto& rSegment : std::as_const(aSegments))
4620  {
4621  if (rSegment.Command == nCommand)
4622  return true;
4623  }
4624  }
4625  }
4626  }
4627  }
4628  }
4629  catch (const ::uno::Exception&)
4630  {
4631  }
4632  return false;
4633 }
4634 
4636 {
4637  // check existence of the grab bag
4638  if( !GetProperty( xPropSet, "InteropGrabBag" ) )
4639  return;
4640 
4641  // extract the relevant properties from the grab bag
4642  Sequence< PropertyValue > aGrabBag, aEffectProps, aLightRigProps, aShape3DProps;
4643  mAny >>= aGrabBag;
4644  auto pShapeProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
4645  [](const PropertyValue& rProp) { return rProp.Name == "3DEffectProperties"; });
4646  if (pShapeProp != std::cend(aGrabBag))
4647  {
4648  Sequence< PropertyValue > a3DEffectProps;
4649  pShapeProp->Value >>= a3DEffectProps;
4650  for( const auto& r3DEffectProp : std::as_const(a3DEffectProps) )
4651  {
4652  if( r3DEffectProp.Name == "Camera" )
4653  r3DEffectProp.Value >>= aEffectProps;
4654  else if( r3DEffectProp.Name == "LightRig" )
4655  r3DEffectProp.Value >>= aLightRigProps;
4656  else if( r3DEffectProp.Name == "Shape3D" )
4657  r3DEffectProp.Value >>= aShape3DProps;
4658  }
4659  }
4660 
4661  auto pTextProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
4662  [](const PropertyValue& rProp) { return rProp.Name == "Text3DEffectProperties"; });
4663 
4664  if (pTextProp != std::cend(aGrabBag))
4665  {
4666  Sequence< PropertyValue > a3DEffectProps;
4667  pTextProp->Value >>= a3DEffectProps;
4668  for( const auto& r3DEffectProp : std::as_const(a3DEffectProps) )
4669  {
4670  if( r3DEffectProp.Name == "Camera" )
4671  r3DEffectProp.Value >>= aEffectProps;
4672  else if( r3DEffectProp.Name == "LightRig" )
4673  r3DEffectProp.Value >>= aLightRigProps;
4674  else if( r3DEffectProp.Name == "Shape3D" )
4675  r3DEffectProp.Value >>= aShape3DProps;
4676  }
4677  }
4678 
4679  if( !aEffectProps.hasElements() && !aLightRigProps.hasElements() && !aShape3DProps.hasElements() )
4680  return;
4681 
4682  bool bCameraRotationPresent = false;
4683  rtl::Reference<sax_fastparser::FastAttributeList> aCameraAttrList = FastSerializerHelper::createAttrList();
4684  rtl::Reference<sax_fastparser::FastAttributeList> aCameraRotationAttrList = FastSerializerHelper::createAttrList();
4685  for( const auto& rEffectProp : std::as_const(aEffectProps) )
4686  {
4687  if( rEffectProp.Name == "prst" )
4688  {
4689  OUString sVal;
4690  rEffectProp.Value >>= sVal;
4691  aCameraAttrList->add(XML_prst, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8));
4692  }
4693  else if( rEffectProp.Name == "fov" )
4694  {
4695  float fVal = 0;
4696  rEffectProp.Value >>= fVal;
4697  aCameraAttrList->add( XML_fov, OString::number( fVal * 60000 ).getStr() );
4698  }
4699  else if( rEffectProp.Name == "zoom" )
4700  {
4701  float fVal = 1;
4702  rEffectProp.Value >>= fVal;
4703  aCameraAttrList->add( XML_zoom, OString::number( fVal * 100000 ).getStr() );
4704  }
4705  else if( rEffectProp.Name == "rotLat" ||
4706  rEffectProp.Name == "rotLon" ||
4707  rEffectProp.Name == "rotRev" )
4708  {
4709  sal_Int32 nVal = 0, nToken = XML_none;
4710  rEffectProp.Value >>= nVal;
4711  if( rEffectProp.Name == "rotLat" )
4712  nToken = XML_lat;
4713  else if( rEffectProp.Name == "rotLon" )
4714  nToken = XML_lon;
4715  else if( rEffectProp.Name == "rotRev" )
4716  nToken = XML_rev;
4717  aCameraRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
4718  bCameraRotationPresent = true;
4719  }
4720  }
4721 
4722  bool bLightRigRotationPresent = false;
4723  rtl::Reference<sax_fastparser::FastAttributeList> aLightRigAttrList = FastSerializerHelper::createAttrList();
4724  rtl::Reference<sax_fastparser::FastAttributeList> aLightRigRotationAttrList = FastSerializerHelper::createAttrList();
4725  for( const auto& rLightRigProp : std::as_const(aLightRigProps) )
4726  {
4727  if( rLightRigProp.Name == "rig" || rLightRigProp.Name == "dir" )
4728  {
4729  OUString sVal;
4730  sal_Int32 nToken = XML_none;
4731  rLightRigProp.Value >>= sVal;
4732  if( rLightRigProp.Name == "rig" )
4733  nToken = XML_rig;
4734  else if( rLightRigProp.Name == "dir" )
4735  nToken = XML_dir;
4736  aLightRigAttrList->add(nToken, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8));
4737  }
4738  else if( rLightRigProp.Name == "rotLat" ||
4739  rLightRigProp.Name == "rotLon" ||
4740  rLightRigProp.Name == "rotRev" )
4741  {
4742  sal_Int32 nVal = 0, nToken = XML_none;
4743  rLightRigProp.Value >>= nVal;
4744  if( rLightRigProp.Name == "rotLat" )
4745  nToken = XML_lat;
4746  else if( rLightRigProp.Name == "rotLon" )
4747  nToken = XML_lon;
4748  else if( rLightRigProp.Name == "rotRev" )
4749  nToken = XML_rev;
4750  aLightRigRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
4751  bLightRigRotationPresent = true;
4752  }
4753  }
4754 
4755  mpFS->startElementNS(XML_a, XML_scene3d);
4756 
4757  if( aEffectProps.hasElements() )
4758  {
4759  mpFS->startElementNS( XML_a, XML_camera, aCameraAttrList );
4760  if( bCameraRotationPresent )
4761  {
4762  mpFS->singleElementNS( XML_a, XML_rot, aCameraRotationAttrList );
4763  }
4764  mpFS->endElementNS( XML_a, XML_camera );
4765  }
4766  else
4767  {
4768  // a:camera with Word default values - Word won't open the document if this is not present
4769  mpFS->singleElementNS(XML_a, XML_camera, XML_prst, "orthographicFront");
4770  }
4771 
4772  if( aEffectProps.hasElements() )
4773  {
4774  mpFS->startElementNS( XML_a, XML_lightRig, aLightRigAttrList );
4775  if( bLightRigRotationPresent )
4776  {
4777  mpFS->singleElementNS( XML_a, XML_rot, aLightRigRotationAttrList );
4778  }
4779  mpFS->endElementNS( XML_a, XML_lightRig );
4780  }
4781  else
4782  {
4783  // a:lightRig with Word default values - Word won't open the document if this is not present
4784  mpFS->singleElementNS(XML_a, XML_lightRig, XML_rig, "threePt", XML_dir, "t");
4785  }
4786 
4787  mpFS->endElementNS( XML_a, XML_scene3d );
4788 
4789  if( !aShape3DProps.hasElements() )
4790  return;
4791 
4792  bool bBevelTPresent = false, bBevelBPresent = false;
4793  Sequence< PropertyValue > aExtrusionColorProps, aContourColorProps;
4794  rtl::Reference<sax_fastparser::FastAttributeList> aBevelTAttrList = FastSerializerHelper::createAttrList();
4795  rtl::Reference<sax_fastparser::FastAttributeList> aBevelBAttrList = FastSerializerHelper::createAttrList();
4796  rtl::Reference<sax_fastparser::FastAttributeList> aShape3DAttrList = FastSerializerHelper::createAttrList();
4797  for( const auto& rShape3DProp : std::as_const(aShape3DProps) )
4798  {
4799  if( rShape3DProp.Name == "extrusionH" || rShape3DProp.Name == "contourW" || rShape3DProp.Name == "z" )
4800  {
4801  sal_Int32 nVal = 0, nToken = XML_none;
4802  rShape3DProp.Value >>= nVal;
4803  if( rShape3DProp.Name == "extrusionH" )
4804  nToken = XML_extrusionH;
4805  else if( rShape3DProp.Name == "contourW" )
4806  nToken = XML_contourW;
4807  else if( rShape3DProp.Name == "z" )
4808  nToken = XML_z;
4809  aShape3DAttrList->add( nToken, OString::number( nVal ).getStr() );
4810  }
4811  else if( rShape3DProp.Name == "prstMaterial" )
4812  {
4813  OUString sVal;
4814  rShape3DProp.Value >>= sVal;
4815  aShape3DAttrList->add(XML_prstMaterial, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8));
4816  }
4817  else if( rShape3DProp.Name == "extrusionClr" )
4818  {
4819  rShape3DProp.Value >>= aExtrusionColorProps;
4820  }
4821  else if( rShape3DProp.Name == "contourClr" )
4822  {
4823  rShape3DProp.Value >>= aContourColorProps;
4824  }
4825  else if( rShape3DProp.Name == "bevelT" || rShape3DProp.Name == "bevelB" )
4826  {
4827  Sequence< PropertyValue > aBevelProps;
4828  rShape3DProp.Value >>= aBevelProps;
4829  if ( !aBevelProps.hasElements() )
4830  continue;
4831 
4833  if( rShape3DProp.Name == "bevelT" )
4834  {
4835  bBevelTPresent = true;
4836  aBevelAttrList = aBevelTAttrList;
4837  }
4838  else
4839  {
4840  bBevelBPresent = true;
4841  aBevelAttrList = aBevelBAttrList;
4842  }
4843  for( const auto& rBevelProp : std::as_const(aBevelProps) )
4844  {
4845  if( rBevelProp.Name == "w" || rBevelProp.Name == "h" )
4846  {
4847  sal_Int32 nVal = 0, nToken = XML_none;
4848  rBevelProp.Value >>= nVal;
4849  if( rBevelProp.Name == "w" )
4850  nToken = XML_w;
4851  else if( rBevelProp.Name == "h" )
4852  nToken = XML_h;
4853  aBevelAttrList->add( nToken, OString::number( nVal ).getStr() );
4854  }
4855  else if( rBevelProp.Name == "prst" )
4856  {
4857  OUString sVal;
4858  rBevelProp.Value >>= sVal;
4859  aBevelAttrList->add(XML_prst, OUStringToOString(sVal, RTL_TEXTENCODING_UTF8));
4860  }
4861  }
4862 
4863  }
4864  }
4865 
4866  mpFS->startElementNS( XML_a, XML_sp3d, aShape3DAttrList );
4867  if( bBevelTPresent )
4868  {
4869  mpFS->singleElementNS( XML_a, XML_bevelT, aBevelTAttrList );
4870  }
4871  if( bBevelBPresent )
4872  {
4873  mpFS->singleElementNS( XML_a, XML_bevelB, aBevelBAttrList );
4874  }
4875  if( aExtrusionColorProps.hasElements() )
4876  {
4877  OUString sSchemeClr;
4878  ::Color nColor;
4879  sal_Int32 nTransparency(0);
4880  Sequence< PropertyValue > aColorTransformations;
4881  for( const auto& rExtrusionColorProp : std::as_const(aExtrusionColorProps) )
4882  {
4883  if( rExtrusionColorProp.Name == "schemeClr" )
4884  rExtrusionColorProp.Value >>= sSchemeClr;
4885  else if( rExtrusionColorProp.Name == "schemeClrTransformations" )
4886  rExtrusionColorProp.Value >>= aColorTransformations;
4887  else if( rExtrusionColorProp.Name == "rgbClr" )
4888  rExtrusionColorProp.Value >>= nColor;
4889  else if( rExtrusionColorProp.Name == "rgbClrTransparency" )
4890  rExtrusionColorProp.Value >>= nTransparency;
4891  }
4892  mpFS->startElementNS(XML_a, XML_extrusionClr);
4893 
4894  if( sSchemeClr.isEmpty() )
4895  WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
4896  else
4897  WriteColor( sSchemeClr, aColorTransformations );
4898 
4899  mpFS->endElementNS( XML_a, XML_extrusionClr );
4900  }
4901  if( aContourColorProps.hasElements() )
4902  {
4903  OUString sSchemeClr;
4904  ::Color nColor;
4905  sal_Int32 nTransparency(0);
4906  Sequence< PropertyValue > aColorTransformations;
4907  for( const auto& rContourColorProp : std::as_const(aContourColorProps) )
4908  {
4909  if( rContourColorProp.Name == "schemeClr" )
4910  rContourColorProp.Value >>= sSchemeClr;
4911  else if( rContourColorProp.Name == "schemeClrTransformations" )
4912  rContourColorProp.Value >>= aColorTransformations;
4913  else if( rContourColorProp.Name == "rgbClr" )
4914  rContourColorProp.Value >>= nColor;
4915  else if( rContourColorProp.Name == "rgbClrTransparency" )
4916  rContourColorProp.Value >>= nTransparency;
4917  }
4918  mpFS->startElementNS(XML_a, XML_contourClr);
4919 
4920  if( sSchemeClr.isEmpty() )
4921  WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
4922  else
4923  WriteColor( sSchemeClr, aContourColorProps );
4924 
4925  mpFS->endElementNS( XML_a, XML_contourClr );
4926  }
4927  mpFS->endElementNS( XML_a, XML_sp3d );
4928 }
4929 
4931 {
4932  if( !GetProperty( rXPropSet, "InteropGrabBag" ) )
4933  return;
4934 
4935  PropertyValue aEffect;
4936  Sequence< PropertyValue > aGrabBag;
4937  mAny >>= aGrabBag;
4938  auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
4939  [](const PropertyValue& rProp) { return rProp.Name == "ArtisticEffectProperties"; });
4940  if (pProp != std::cend(aGrabBag))
4941  pProp->Value >>= aEffect;
4942  sal_Int32 nEffectToken = ArtisticEffectProperties::getEffectToken( aEffect.Name );
4943  if( nEffectToken == XML_none )
4944  return;
4945 
4947  aEffect.Value >>= aAttrs;
4948  rtl::Reference<sax_fastparser::FastAttributeList> aAttrList = FastSerializerHelper::createAttrList();
4949  OString sRelId;
4950  for( const auto& rAttr : std::as_const(aAttrs) )
4951  {
4952  sal_Int32 nToken = ArtisticEffectProperties::getEffectToken( rAttr.Name );
4953  if( nToken != XML_none )
4954  {
4955  sal_Int32 nVal = 0;
4956  rAttr.Value >>= nVal;
4957  aAttrList->add( nToken, OString::number( nVal ).getStr() );
4958  }
4959  else if( rAttr.Name == "OriginalGraphic" )
4960  {
4961  Sequence< PropertyValue > aGraphic;
4962  rAttr.Value >>= aGraphic;
4963  Sequence< sal_Int8 > aGraphicData;
4964  OUString sGraphicId;
4965  for( const auto& rProp : std::as_const(aGraphic) )
4966  {
4967  if( rProp.Name == "Id" )
4968  rProp.Value >>= sGraphicId;
4969  else if( rProp.Name == "Data" )
4970  rProp.Value >>= aGraphicData;
4971  }
4972  sRelId = WriteWdpPicture( sGraphicId, aGraphicData );
4973  }
4974  }
4975 
4976  mpFS->startElementNS(XML_a, XML_extLst);
4977  mpFS->startElementNS(XML_a, XML_ext, XML_uri, "{BEBA8EAE-BF5A-486C-A8C5-ECC9F3942E4B}");
4978  mpFS->startElementNS( XML_a14, XML_imgProps,
4979  FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)) );
4980  mpFS->startElementNS(XML_a14, XML_imgLayer, FSNS(XML_r, XML_embed), sRelId);
4981  mpFS->startElementNS(XML_a14, XML_imgEffect);
4982 
4983  mpFS->singleElementNS( XML_a14, nEffectToken, aAttrList );
4984 
4985  mpFS->endElementNS( XML_a14, XML_imgEffect );
4986  mpFS->endElementNS( XML_a14, XML_imgLayer );
4987  mpFS->endElementNS( XML_a14, XML_imgProps );
4988  mpFS->endElementNS( XML_a, XML_ext );
4989  mpFS->endElementNS( XML_a, XML_extLst );
4990 }
4991 
4992 OString DrawingML::WriteWdpPicture( const OUString& rFileId, const Sequence< sal_Int8 >& rPictureData )
4993 {
4994  std::map<OUString, OUString>::iterator aCachedItem = maWdpCache.find( rFileId );
4995  if( aCachedItem != maWdpCache.end() )
4996  return OUStringToOString( aCachedItem->second, RTL_TEXTENCODING_UTF8 );
4997 
4998  OUString sFileName = "media/hdphoto" + OUString::number( mnWdpImageCounter++ ) + ".wdp";
4999  Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
5000  .appendAscii( GetComponentDir() )
5001  .append( "/" + sFileName )
5002  .makeStringAndClear(),
5003  "image/vnd.ms-photo" );
5004  OUString sId;
5005  xOutStream->writeBytes( rPictureData );
5006  xOutStream->closeOutput();
5007 
5008  sId = mpFB->addRelation( mpFS->getOutputStream(),
5010  OUStringBuffer()
5011  .appendAscii( GetRelationCompPrefix() )
5012  .append( sFileName )
5013  .makeStringAndClear() );
5014 
5015  maWdpCache[rFileId] = sId;
5016  return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
5017 }
5018 
5019 void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId)
5020 {
5021  uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
5022 
5023  uno::Reference<xml::dom::XDocument> dataDom;
5024  uno::Reference<xml::dom::XDocument> layoutDom;
5025  uno::Reference<xml::dom::XDocument> styleDom;
5026  uno::Reference<xml::dom::XDocument> colorDom;
5027  uno::Reference<xml::dom::XDocument> drawingDom;
5028  uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
5029  uno::Sequence<uno::Any> diagramDrawing;
5030 
5031  // retrieve the doms from the GrabBag
5032  uno::Sequence<beans::PropertyValue> propList;
5033  xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
5034  for (const auto& rProp : std::as_const(propList))
5035  {
5036  OUString propName = rProp.Name;
5037  if (propName == "OOXData")
5038  rProp.Value >>= dataDom;
5039  else if (propName == "OOXLayout")
5040  rProp.Value >>= layoutDom;
5041  else if (propName == "OOXStyle")
5042  rProp.Value >>= styleDom;
5043  else if (propName == "OOXColor")
5044  rProp.Value >>= colorDom;
5045  else if (propName == "OOXDrawing")
5046  {
5047  rProp.Value >>= diagramDrawing;
5048  diagramDrawing[0]
5049  >>= drawingDom; // if there is OOXDrawing property then set drawingDom here only.
5050  }
5051  else if (propName == "OOXDiagramDataRels")
5052  rProp.Value >>= xDataRelSeq;
5053  }
5054 
5055  // check that we have the 4 mandatory XDocuments
5056  // if not, there was an error importing and we won't output anything
5057  if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is())
5058  return;
5059 
5060  // generate a unique id
5063  pDocPrAttrList->add(XML_id, OString::number(nDiagramId).getStr());
5064  OUString sName = "Diagram" + OUString::number(nDiagramId);
5065  pDocPrAttrList->add(XML_name, OUStringToOString(sName, RTL_TEXTENCODING_UTF8));
5066 
5067  if (GetDocumentType() == DOCUMENT_DOCX)
5068  {
5069  mpFS->singleElementNS(XML_wp, XML_docPr, pDocPrAttrList);
5070  mpFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
5071 
5072  mpFS->startElementNS(XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
5073  mpFB->getNamespaceURL(OOX_NS(dml)));
5074  }
5075  else
5076  {
5077  mpFS->startElementNS(XML_p, XML_nvGraphicFramePr);
5078 
5079  mpFS->singleElementNS(XML_p, XML_cNvPr, pDocPrAttrList);
5080  mpFS->singleElementNS(XML_p, XML_cNvGraphicFramePr);
5081 
5082  mpFS->startElementNS(XML_p, XML_nvPr);
5083  mpFS->startElementNS(XML_p, XML_extLst);
5084  // change tracking extension - required in PPTX
5085  mpFS->startElementNS(XML_p, XML_ext, XML_uri, "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}");
5086  mpFS->singleElementNS(XML_p14, XML_modId,
5087  FSNS(XML_xmlns, XML_p14), mpFB->getNamespaceURL(OOX_NS(p14)),
5088  XML_val,
5090  mpFS->endElementNS(XML_p, XML_ext);
5091  mpFS->endElementNS(XML_p, XML_extLst);
5092  mpFS->endElementNS(XML_p, XML_nvPr);
5093 
5094  mpFS->endElementNS(XML_p, XML_nvGraphicFramePr);
5095 
5096  // store size and position of background shape instead of group shape
5097  // as some shapes may be outside
5098  css::uno::Reference<css::drawing::XShapes> xShapes(rXShape, uno::UNO_QUERY);
5099  if (xShapes.is() && xShapes->hasElements())
5100  {
5101  css::uno::Reference<css::drawing::XShape> xShapeBg(xShapes->getByIndex(0),
5102  uno::UNO_QUERY);
5103  awt::Point aPos = xShapeBg->getPosition();
5104  awt::Size aSize = xShapeBg->getSize();
5106  xShapeBg, tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)),
5107  XML_p, false, false, 0, false);
5108  }
5109 
5110  mpFS->startElementNS(XML_a, XML_graphic);
5111  }
5112 
5113  mpFS->startElementNS(XML_a, XML_graphicData, XML_uri,
5114  "http://schemas.openxmlformats.org/drawingml/2006/diagram");
5115 
5116  OUString sRelationCompPrefix = OUString::createFromAscii(GetRelationCompPrefix());
5117 
5118  // add data relation
5119  OUString dataFileName = "diagrams/data" + OUString::number(nDiagramId) + ".xml";
5120  OString dataRelId = OUStringToOString(
5122  OUStringConcatenation(sRelationCompPrefix + dataFileName)),
5123  RTL_TEXTENCODING_UTF8);
5124 
5125  // add layout relation
5126  OUString layoutFileName = "diagrams/layout" + OUString::number(nDiagramId) + ".xml";
5127  OString layoutRelId
5128  = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
5130  OUStringConcatenation(sRelationCompPrefix + layoutFileName)),
5131  RTL_TEXTENCODING_UTF8);
5132 
5133  // add style relation
5134  OUString styleFileName = "diagrams/quickStyle" + OUString::number(nDiagramId) + ".xml";
5135  OString styleRelId
5136  = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
5138  OUStringConcatenation(sRelationCompPrefix + styleFileName)),
5139  RTL_TEXTENCODING_UTF8);
5140 
5141  // add color relation
5142  OUString colorFileName = "diagrams/colors" + OUString::number(nDiagramId) + ".xml";
5143  OString colorRelId
5144  = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
5146  OUStringConcatenation(sRelationCompPrefix + colorFileName)),
5147  RTL_TEXTENCODING_UTF8);
5148 
5149  OUString drawingFileName;
5150  if (drawingDom.is())
5151  {
5152  // add drawing relation
5153  drawingFileName = "diagrams/drawing" + OUString::number(nDiagramId) + ".xml";
5154  OUString drawingRelId = mpFB->addRelation(
5156  OUStringConcatenation(sRelationCompPrefix + drawingFileName));
5157 
5158  // the data dom contains a reference to the drawing relation. We need to update it with the new generated
5159  // relation value before writing the dom to a file
5160 
5161  // Get the dsp:damaModelExt node from the dom
5162  uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS(
5163  "http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt");
5164 
5165  // There must be one element only so get it
5166  uno::Reference<xml::dom::XNode> node = nodeList->item(0);
5167 
5168  // Get the list of attributes of the node
5169  uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes();
5170 
5171  // Get the node with the relId attribute and set its new value
5172  uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId");
5173  relIdNode->setNodeValue(drawingRelId);
5174  }
5175 
5176  mpFS->singleElementNS(XML_dgm, XML_relIds,
5177  FSNS(XML_xmlns, XML_dgm), mpFB->getNamespaceURL(OOX_NS(dmlDiagram)),
5178  FSNS(XML_xmlns, XML_r), mpFB->getNamespaceURL(OOX_NS(officeRel)),
5179  FSNS(XML_r, XML_dm), dataRelId, FSNS(XML_r, XML_lo), layoutRelId,
5180  FSNS(XML_r, XML_qs), styleRelId, FSNS(XML_r, XML_cs), colorRelId);
5181 
5182  mpFS->endElementNS(XML_a, XML_graphicData);
5183  mpFS->endElementNS(XML_a, XML_graphic);
5184 
5185  uno::Reference<xml::sax::XSAXSerializable> serializer;
5186  uno::Reference<xml::sax::XWriter> writer
5187  = xml::sax::Writer::create(comphelper::getProcessComponentContext());
5188 
5189  OUString sDir = OUString::createFromAscii(GetComponentDir());
5190 
5191  // write data file
5192  serializer.set(dataDom, uno::UNO_QUERY);
5193  uno::Reference<io::XOutputStream> xDataOutputStream = mpFB->openFragmentStream(
5194  sDir + "/" + dataFileName,
5195  "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
5196  writer->setOutputStream(xDataOutputStream);
5197  serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
5198  uno::Sequence<beans::StringPair>());
5199 
5200  // write the associated Images and rels for data file
5201  writeDiagramRels(xDataRelSeq, xDataOutputStream, u"OOXDiagramDataRels", nDiagramId);
5202 
5203  // write layout file
5204  serializer.set(layoutDom, uno::UNO_QUERY);
5205  writer->setOutputStream(mpFB->openFragmentStream(
5206  sDir + "/" + layoutFileName,
5207  "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"));
5208  serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
5209  uno::Sequence<beans::StringPair>());
5210 
5211  // write style file
5212  serializer.set(styleDom, uno::UNO_QUERY);
5213  writer->setOutputStream(mpFB->openFragmentStream(
5214  sDir + "/" + styleFileName,
5215  "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"));
5216  serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
5217  uno::Sequence<beans::StringPair>());
5218 
5219  // write color file
5220  serializer.set(colorDom, uno::UNO_QUERY);
5221  writer->setOutputStream(mpFB->openFragmentStream(
5222  sDir + "/" + colorFileName,
5223  "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"));
5224  serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
5225  uno::Sequence<beans::StringPair>());
5226 
5227  // write drawing file
5228  if (!drawingDom.is())
5229  return;
5230 
5231  serializer.set(drawingDom, uno::UNO_QUERY);
5232  uno::Reference<io::XOutputStream> xDrawingOutputStream = mpFB->openFragmentStream(
5233  sDir + "/" + drawingFileName, "application/vnd.ms-office.drawingml.diagramDrawing+xml");
5234  writer->setOutputStream(xDrawingOutputStream);
5235  serializer->serialize(
5236  uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
5237  uno::Sequence<beans::StringPair>());
5238 
5239  // write the associated Images and rels for drawing file
5240  uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq;
5241  diagramDrawing[1] >>= xDrawingRelSeq;
5242  writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, u"OOXDiagramDrawingRels", nDiagramId);
5243 }
5244 
5245 void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq,
5246  const uno::Reference<io::XOutputStream>& xOutStream,
5247  std::u16string_view sGrabBagProperyName, int nDiagramId)
5248 {
5249  // add image relationships of OOXData, OOXDiagram
5251  uno::Reference<xml::sax::XWriter> xWriter
5252  = xml::sax::Writer::create(comphelper::getProcessComponentContext());
5253  xWriter->setOutputStream(xOutStream);
5254 
5255  // retrieve the relationships from Sequence
5256  for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++)
5257  {
5258  // diagramDataRelTuple[0] => RID,
5259  // diagramDataRelTuple[1] => xInputStream
5260  // diagramDataRelTuple[2] => extension
5261  uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j];
5262 
5263  OUString sRelId;
5264  OUString sExtension;
5265  diagramDataRelTuple[0] >>= sRelId;
5266  diagramDataRelTuple[2] >>= sExtension;
5267  OUString sContentType;
5268  if (sExtension.equalsIgnoreAsciiCase(".WMF"))
5269  sContentType = "image/x-wmf";
5270  else
5271  sContentType = OUString::Concat("image/") + sExtension.subView(1);
5272  sRelId = sRelId.copy(3);
5273 
5274  StreamDataSequence dataSeq;
5275  diagramDataRelTuple[1] >>= dataSeq;
5276  uno::Reference<io::XInputStream> dataImagebin(
5277  new ::comphelper::SequenceInputStream(dataSeq));
5278 
5279  //nDiagramId is used to make the name unique irrespective of the number of smart arts.
5280  OUString sFragment = OUString::Concat("media/") + sGrabBagProperyName
5281  + OUString::number(nDiagramId) + "_"
5282  + OUString::number(j) + sExtension;
5283 
5284  PropertySet aProps(xOutStream);
5285  aProps.setAnyProperty(PROP_RelId, uno::makeAny(sRelId.toInt32()));
5286 
5287  mpFB->addRelation(xOutStream, sType, OUStringConcatenation("../" + sFragment));
5288 
5289  OUString sDir = OUString::createFromAscii(GetComponentDir());
5290  uno::Reference<io::XOutputStream> xBinOutStream
5291  = mpFB->openFragmentStream(sDir + "/" + sFragment, sContentType);
5292 
5293  try
5294  {
5295  comphelper::OStorageHelper::CopyInputToOutput(dataImagebin, xBinOutStream);
5296  }
5297  catch (const uno::Exception&)
5298  {
5299  TOOLS_WARN_EXCEPTION("oox.drawingml", "DrawingML::writeDiagramRels Failed to copy grabbaged Image");
5300  }
5301  dataImagebin->closeInput();
5302  }
5303 }
5304 
5305 void DrawingML::WriteFromTo(const uno::Reference<css::drawing::XShape>& rXShape, const awt::Size& aPageSize,
5306  const FSHelperPtr& pDrawing)
5307 {
5308  awt::Point aTopLeft = rXShape->getPosition();
5309  awt::Size aSize = rXShape->getSize();
5310 
5312  if (pObj)
5313  {
5314  Degree100 nRotation = pObj->GetRotateAngle();
5315  if (nRotation)
5316  {
5317  sal_Int16 nHalfWidth = aSize.Width / 2;
5318  sal_Int16 nHalfHeight = aSize.Height / 2;
5319  // aTopLeft needs correction for rotated customshapes
5320  if (pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE)
5321  {
5322  // Center of bounding box of the rotated shape
5323  const auto aSnapRectCenter(pObj->GetSnapRect().Center());
5324  aTopLeft.X = aSnapRectCenter.X() - nHalfWidth;
5325  aTopLeft.Y = aSnapRectCenter.Y() - nHalfHeight;
5326  }
5327 
5328  // MSO changes the anchor positions at these angles and that does an extra 90 degrees
5329  // rotation on our shapes, so we output it in such position that MSO
5330  // can draw this shape correctly.
5331  if ((nRotation >= 4500_deg100 && nRotation < 13500_deg100) || (nRotation >= 22500_deg100 && nRotation < 31500_deg100))
5332  {
5333  aTopLeft.X = aTopLeft.X - nHalfHeight + nHalfWidth;
5334  aTopLeft.Y = aTopLeft.Y - nHalfWidth + nHalfHeight;
5335 
5336  std::swap(aSize.Width, aSize.Height);
5337  }
5338  }
5339  }
5340 
5341  tools::Rectangle aLocation(aTopLeft.X, aTopLeft.Y, aTopLeft.X + aSize.Width, aTopLeft.Y + aSize.Height);
5342  double nXpos = static_cast<double>(aLocation.TopLeft().getX()) / static_cast<double>(aPageSize.Width);
5343  double nYpos = static_cast<double>(aLocation.TopLeft().getY()) / static_cast<double>(aPageSize.Height);
5344 
5345  pDrawing->startElement(FSNS(XML_cdr, XML_from));
5346  pDrawing->startElement(FSNS(XML_cdr, XML_x));
5347  pDrawing->write(nXpos);
5348  pDrawing->endElement(FSNS(XML_cdr, XML_x));
5349  pDrawing->startElement(FSNS(XML_cdr, XML_y));
5350  pDrawing->write(nYpos);
5351  pDrawing->endElement(FSNS(XML_cdr, XML_y));
5352  pDrawing->endElement(FSNS(XML_cdr, XML_from));
5353 
5354  nXpos = static_cast<double>(aLocation.BottomRight().getX()) / static_cast<double>(aPageSize.Width);
5355  nYpos = static_cast<double>(aLocation.BottomRight().getY()) / static_cast<double>(aPageSize.Height);
5356 
5357  pDrawing->startElement(FSNS(XML_cdr, XML_to));
5358  pDrawing->startElement(FSNS(XML_cdr, XML_x));
5359  pDrawing->write(nXpos);
5360  pDrawing->endElement(FSNS(XML_cdr, XML_x));
5361  pDrawing->startElement(FSNS(XML_cdr, XML_y));
5362  pDrawing->write(nYpos);
5363  pDrawing->endElement(FSNS(XML_cdr, XML_y));
5364  pDrawing->endElement(FSNS(XML_cdr, XML_to));
5365 }
5366 
5367 }
5368 
5369 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_uInt16 Count() const
ESCHER_LineMediumLenArrow
void GetParameter(double &rParameterReturnValue, const css::drawing::EnhancedCustomShapeParameter &, const bool bReplaceGeoWidth, const bool bReplaceGeoHeight) const
void WritePattFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
BitmapMode
sal_uInt64 BitmapChecksum
SVX_NUM_CHARS_UPPER_LETTER_N
const char * sName
Definition: olehelper.cxx:92
sal_Int32 nIndex
void WriteConnectorConnections(EscherConnectorListEntry &rConnectorEntry, sal_Int32 nStartID, sal_Int32 nEndID)
Definition: drawingml.cxx:4055
::Color ColorWithIntensity(sal_uInt32 nColor, sal_uInt32 nIntensity)
Definition: drawingml.cxx:515
sal_Int32 convertEmuToHmm(sal_Int64 nValue)
Converts the passed 64-bit integer value from EMUs to 1/100 mm.
sal_uInt8 GetRed() const
#define UNO_TC_PROP_FILE_FORMAT
static sal_Int32 getEffectToken(const OUString &sName)
Translate effect strings to tokens.
ESCHER_LineNarrowArrow
sal_Unicode bestFitOpenSymbolToMSFont(sal_Unicode cBullet, rtl_TextEncoding &r_ioChrSet, OUString &r_ioFontName)
void WriteXGraphicBlipFill(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet, css::uno::Reference< css::graphic::XGraphic > const &rxGraphic, sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia=false)
Definition: drawingml.cxx:1553
TextVerticalAdjust GetTextVerticalAdjust(sal_Int32 nToken)
const sal_Int32 PER_PERCENT
static void CopyInputToOutput(const css::uno::Reference< css::io::XInputStream > &xInput, const css::uno::Reference< css::io::XOutputStream > &xOutput)
void WriteArtisticEffect(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:4930
void WriteSoftEdgeEffect(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:4575
void WriteImageBrightnessContrastTransparence(css::uno::Reference< css::beans::XPropertySet > const &rXPropSet)
Definition: drawingml.cxx:1394
constexpr tools::Long Left() const
BitmapChecksum GetChecksum() const
tools::Long const nLeftMargin
bool Scale(const Size &rNewSize, BmpScaleFlag nScaleFlag=BmpScaleFlag::Default)
void WriteShapeTransformation(const css::uno::Reference< css::drawing::XShape > &rXShape, sal_Int32 nXmlNamespace, bool bFlipH=false, bool bFlipV=false, bool bSuppressRotation=false, bool bSuppressFlipping=false, bool bFlippedBeforeRotation=false)
Definition: drawingml.cxx:1771
SVX_NUM_NUMBER_NONE
sal_Int32 ExportRotateClockwisify(Degree100 input)
Definition: drawingml.hxx:116
void WriteBlipFill(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, const OUString &sURLPropName)
long Long
void WriteText(const css::uno::Reference< css::uno::XInterface > &rXIface, bool bBodyPr, bool bText=true, sal_Int32 nXmlNamespace=0, bool bWritePropertiesAsLstStyles=false)
Definition: drawingml.cxx:3066
static SdrObject * getSdrObjectFromXShape(const css::uno::Reference< css::uno::XInterface > &xInt)
static OUString GetDatetimeTypeFromTime(SvxTimeFormat eTime)
Gets OOXML datetime field type from LO Time format.
Definition: drawingml.cxx:2341
constexpr::Color COL_AUTO(ColorTransparency, 0xFF, 0xFF, 0xFF, 0xFF)
SVX_NUM_CHARS_UPPER_LETTER
void WriteColorTransformations(const css::uno::Sequence< css::beans::PropertyValue > &aTransformations, sal_Int32 nAlpha=MAX_PERCENT)
Definition: drawingml.cxx:381
GraphicType
static std::map< OString, std::vector< OString > > lcl_getAdjNames()
Definition: drawingml.cxx:3531
OUString GetSubsFontName(const OUString &rName, SubsFontFlags nFlags)
#define UNO_TC_PROP_NUMFORMAT
void WriteShape3DEffects(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet)
Definition: drawingml.cxx:4635
tools::Long TransformMetric(tools::Long nVal, FieldUnit aOld, FieldUnit aNew)
double toRadians(Degree10 x)
css::uno::Reference< css::io::XOutputStream > openFragmentStream(const OUString &rStreamName, const OUString &rMediaType)
Opens and returns the specified output stream from the base storage with specified media type...
void WriteGrabBagGradientFill(const css::uno::Sequence< css::beans::PropertyValue > &aGradientStops, css::awt::Gradient rGradient)
Definition: drawingml.cxx:599
::sax_fastparser::FSHelperPtr mpFS
Definition: drawingml.hxx:160
static sal_Unicode SubstituteBullet(sal_Unicode cBulletId, css::awt::FontDescriptor &rFontDesc)
Definition: drawingml.cxx:4071
void WriteLineArrow(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, bool bLineStart)
Definition: drawingml.cxx:746
virtual SdrObjKind GetObjIdentifier() const
bool IsTextEditActive() const
EmbeddedObjectRef * pObject
ESCHER_LineArrowOvalEnd
static constexpr const char * ToPsz10(bool b)
Definition: utils.hxx:49
static sal_Int32 mnVmlCount
Definition: drawingml.hxx:150
virtual void CacheRelId(BitmapChecksum nChecksum, const OUString &rRelId, const OUString &rFileName)=0
Store the RelId and filename of a graphic based on its checksum.
constexpr double rad2deg(double v)
bool GetProperty(const css::uno::Reference< css::beans::XPropertySet > &rXPropSet, const OUString &aName)
Definition: drawingml.cxx:261
void WriteShapeEffect(std::u16string_view sName, const css::uno::Sequence< css::beans::PropertyValue > &aEffectProps)
Definition: drawingml.cxx:4205
unsigned int uniform_uint_distribution(unsigned int a, unsigned int b)
const char * GetRelationCompPrefix() const
Definition: drawingml.cxx:1163
SvxNumType
virtual const tools::Rectangle & GetSnapRect() const
void WriteLstStyles(const css::uno::Reference< css::text::XTextContent > &rParagraph, bool &rbOverridingCharHeight, sal_Int32 &rnCharHeight, const css::uno::Reference< css::beans::XPropertySet > &rXShapePropSet)
<