xref: /core/oox/source/export/drawingml.cxx (revision 3feaff84)
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>
25 #include <oox/core/xmlfilterbase.hxx>
26 #include <oox/export/drawingml.hxx>
27 #include <oox/export/utils.hxx>
28 #include <oox/helper/propertyset.hxx>
29 #include <oox/drawingml/color.hxx>
30 #include <drawingml/fillproperties.hxx>
31 #include <drawingml/textparagraph.hxx>
32 #include <oox/token/namespaces.hxx>
33 #include <oox/token/properties.hxx>
34 #include <oox/token/relationship.hxx>
35 #include <oox/token/tokens.hxx>
36 #include <oox/drawingml/drawingmltypes.hxx>
37 #include <svtools/unitconv.hxx>
38 #include <sax/fastattribs.hxx>
39 #include <tools/diagnose_ex.h>
40 #include <i18nlangtag/languagetag.hxx>
41 
42 #include <cstdio>
43 #include <com/sun/star/awt/CharSet.hpp>
44 #include <com/sun/star/awt/FontDescriptor.hpp>
45 #include <com/sun/star/awt/FontSlant.hpp>
46 #include <com/sun/star/awt/FontStrikeout.hpp>
47 #include <com/sun/star/awt/FontWeight.hpp>
48 #include <com/sun/star/awt/FontUnderline.hpp>
49 #include <com/sun/star/awt/Gradient.hpp>
50 #include <com/sun/star/beans/XPropertySet.hpp>
51 #include <com/sun/star/beans/XPropertyState.hpp>
52 #include <com/sun/star/beans/Property.hpp>
53 #include <com/sun/star/beans/XPropertySetInfo.hpp>
54 #include <com/sun/star/container/XEnumerationAccess.hpp>
55 #include <com/sun/star/container/XIndexAccess.hpp>
56 #include <com/sun/star/document/XStorageBasedDocument.hpp>
57 #include <com/sun/star/drawing/BitmapMode.hpp>
58 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
59 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
60 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
61 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
62 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
63 #include <com/sun/star/drawing/Hatch.hpp>
64 #include <com/sun/star/drawing/LineDash.hpp>
65 #include <com/sun/star/drawing/LineJoint.hpp>
66 #include <com/sun/star/drawing/LineStyle.hpp>
67 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
68 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
69 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
70 #include <com/sun/star/drawing/XShape.hpp>
71 #include <com/sun/star/drawing/XShapes.hpp>
72 #include <com/sun/star/drawing/FillStyle.hpp>
73 #include <com/sun/star/embed/ElementModes.hpp>
74 #include <com/sun/star/graphic/XGraphic.hpp>
75 #include <com/sun/star/i18n/ScriptType.hpp>
76 #include <com/sun/star/io/XOutputStream.hpp>
77 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
78 #include <com/sun/star/style/LineSpacing.hpp>
79 #include <com/sun/star/style/LineSpacingMode.hpp>
80 #include <com/sun/star/text/WritingMode.hpp>
81 #include <com/sun/star/text/WritingMode2.hpp>
82 #include <com/sun/star/text/GraphicCrop.hpp>
83 #include <com/sun/star/text/XText.hpp>
84 #include <com/sun/star/text/XTextContent.hpp>
85 #include <com/sun/star/text/XTextField.hpp>
86 #include <com/sun/star/text/XTextRange.hpp>
87 #include <com/sun/star/style/CaseMap.hpp>
88 #include <com/sun/star/xml/dom/XNodeList.hpp>
89 #include <com/sun/star/xml/sax/Writer.hpp>
90 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
91 
92 #include <comphelper/processfactory.hxx>
93 #include <comphelper/random.hxx>
94 #include <comphelper/seqstream.hxx>
95 #include <comphelper/storagehelper.hxx>
96 #include <comphelper/xmltools.hxx>
97 #include <o3tl/any.hxx>
98 #include <tools/stream.hxx>
99 #include <unotools/fontdefs.hxx>
100 #include <vcl/cvtgrf.hxx>
101 #include <vcl/graph.hxx>
102 #include <vcl/settings.hxx>
103 #include <vcl/GraphicObject.hxx>
104 #include <vcl/svapp.hxx>
105 #include <rtl/strbuf.hxx>
106 #include <sfx2/app.hxx>
107 #include <svl/languageoptions.hxx>
108 #include <filter/msfilter/escherex.hxx>
109 #include <filter/msfilter/util.hxx>
110 #include <editeng/outlobj.hxx>
111 #include <editeng/svxenum.hxx>
112 #include <editeng/unonames.hxx>
113 #include <editeng/unoprnms.hxx>
114 #include <editeng/flditem.hxx>
115 #include <svx/sdtfsitm.hxx>
116 #include <svx/svdoashp.hxx>
117 #include <svx/svdomedia.hxx>
118 #include <svx/unoapi.hxx>
119 #include <svx/unoshape.hxx>
120 #include <svx/EnhancedCustomShape2d.hxx>
121 
122 using namespace ::css;
123 using namespace ::css::beans;
124 using namespace ::css::drawing;
125 using namespace ::css::i18n;
126 using namespace ::css::style;
127 using namespace ::css::text;
128 using namespace ::css::uno;
129 using namespace ::css::container;
130 
131 using ::css::io::XOutputStream;
132 using ::sax_fastparser::FSHelperPtr;
133 using ::sax_fastparser::FastSerializerHelper;
134 
135 namespace
136 {
137 /// Extracts start or end alpha information from a transparency gradient.
138 sal_Int32 GetAlphaFromTransparenceGradient(const awt::Gradient& rGradient, bool bStart)
139 {
140     // Our alpha is a gray color value.
141     sal_uInt8 nRed = ::Color(bStart ? rGradient.StartColor : rGradient.EndColor).GetRed();
142     // drawingML alpha is a percentage on a 0..100000 scale.
143     return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
144 }
145 }
146 
147 namespace oox {
148 namespace drawingml {
149 
150 URLTransformer::~URLTransformer()
151 {
152 }
153 
154 OUString URLTransformer::getTransformedString(const OUString& rString) const
155 {
156     return rString;
157 }
158 
159 bool URLTransformer::isExternalURL(const OUString& /*rURL*/) const
160 {
161     return true;
162 }
163 
164 static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rDashName )
165     {
166         css::uno::Reference<css::lang::XMultiServiceFactory> xFact(xModel, css::uno::UNO_QUERY);
167         css::uno::Reference<css::container::XNameAccess> xNameAccess(
168             xFact->createInstance("com.sun.star.drawing.DashTable"),
169             css::uno::UNO_QUERY );
170         if(xNameAccess.is())
171         {
172             if (!xNameAccess->hasByName(rDashName))
173                 return css::uno::Any();
174 
175             return xNameAccess->getByName(rDashName);
176         }
177 
178         return css::uno::Any();
179     }
180 
181 namespace
182 {
183 void WriteRadialGradientPath(const awt::Gradient& rGradient, const FSHelperPtr& pFS)
184 {
185     pFS->startElementNS(XML_a, XML_path, XML_path, "circle");
186 
187     // Write the focus rectangle. Work with the focus point, and assume
188     // that it extends 50% in all directions.  The below
189     // left/top/right/bottom values are percentages, where 0 means the
190     // edge of the tile rectangle and 100% means the center of it.
191     rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList(
192         sax_fastparser::FastSerializerHelper::createAttrList());
193     sal_Int32 nLeftPercent = rGradient.XOffset * 2 - 50;
194     pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT));
195     sal_Int32 nTopPercent = rGradient.YOffset * 2 - 50;
196     pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT));
197     sal_Int32 nRightPercent = (100 - rGradient.XOffset) * 2 - 50;
198     pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT));
199     sal_Int32 nBottomPercent = (100 - rGradient.YOffset) * 2 - 50;
200     pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT));
201     sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList.get());
202     pFS->singleElementNS(XML_a, XML_fillToRect, xAttributeList);
203 
204     pFS->endElementNS(XML_a, XML_path);
205 }
206 }
207 
208 // not thread safe
209 int DrawingML::mnImageCounter = 1;
210 int DrawingML::mnWdpImageCounter = 1;
211 std::map<OUString, OUString> DrawingML::maWdpCache;
212 
213 void DrawingML::ResetCounters()
214 {
215     mnImageCounter = 1;
216     mnWdpImageCounter = 1;
217     maWdpCache.clear();
218 }
219 
220 bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName )
221 {
222     try
223     {
224         mAny = rXPropertySet->getPropertyValue(aName);
225         if (mAny.hasValue())
226             return true;
227     }
228     catch( const Exception& )
229     {
230         /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
231     }
232     return false;
233 }
234 
235 bool DrawingML::GetPropertyAndState( const Reference< XPropertySet >& rXPropertySet, const Reference< XPropertyState >& rXPropertyState, const OUString& aName, PropertyState& eState )
236 {
237     try
238     {
239         mAny = rXPropertySet->getPropertyValue(aName);
240         if (mAny.hasValue())
241         {
242             eState = rXPropertyState->getPropertyState(aName);
243             return true;
244         }
245     }
246     catch( const Exception& )
247     {
248         /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
249     }
250     return false;
251 }
252 
253 void DrawingML::WriteColor( ::Color nColor, sal_Int32 nAlpha )
254 {
255     // Transparency is a separate element.
256     OString sColor = OString::number(  sal_uInt32(nColor) & 0x00FFFFFF, 16 );
257     if( sColor.getLength() < 6 )
258     {
259         OStringBuffer sBuf( "0" );
260         int remains = 5 - sColor.getLength();
261 
262         while( remains > 0 )
263         {
264             sBuf.append( "0" );
265             remains--;
266         }
267 
268         sBuf.append( sColor );
269 
270         sColor = sBuf.getStr();
271     }
272     if( nAlpha < MAX_PERCENT )
273     {
274         mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
275         mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
276         mpFS->endElementNS( XML_a, XML_srgbClr );
277 
278     }
279     else
280     {
281         mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
282     }
283 }
284 
285 void DrawingML::WriteColor( const OUString& sColorSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
286 {
287     // prevent writing a tag with empty val attribute
288     if( sColorSchemeName.isEmpty() )
289         return;
290 
291     if( aTransformations.hasElements() )
292     {
293         mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
294         WriteColorTransformations( aTransformations, nAlpha );
295         mpFS->endElementNS( XML_a, XML_schemeClr );
296     }
297     else if(nAlpha < MAX_PERCENT)
298     {
299         mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
300         mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
301         mpFS->endElementNS( XML_a, XML_schemeClr );
302     }
303     else
304     {
305         mpFS->singleElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
306     }
307 }
308 
309 void DrawingML::WriteColorTransformations( const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
310 {
311     for( sal_Int32 i = 0; i < aTransformations.getLength(); i++ )
312     {
313         sal_Int32 nToken = Color::getColorTransformationToken( aTransformations[i].Name );
314         if( nToken != XML_TOKEN_INVALID && aTransformations[i].Value.hasValue() )
315         {
316             if(nToken == XML_alpha && nAlpha < MAX_PERCENT)
317             {
318                 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nAlpha));
319             }
320             else
321             {
322                 sal_Int32 nValue = aTransformations[i].Value.get<sal_Int32>();
323                 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nValue));
324             }
325         }
326     }
327 }
328 
329 void DrawingML::WriteSolidFill( ::Color nColor, sal_Int32 nAlpha )
330 {
331     mpFS->startElementNS(XML_a, XML_solidFill);
332     WriteColor( nColor, nAlpha );
333     mpFS->endElementNS( XML_a, XML_solidFill );
334 }
335 
336 void DrawingML::WriteSolidFill( const OUString& sSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
337 {
338     mpFS->startElementNS(XML_a, XML_solidFill);
339     WriteColor( sSchemeName, aTransformations, nAlpha );
340     mpFS->endElementNS( XML_a, XML_solidFill );
341 }
342 
343 void DrawingML::WriteSolidFill( const Reference< XPropertySet >& rXPropSet )
344 {
345     // get fill color
346     if ( !GetProperty( rXPropSet, "FillColor" ) )
347         return;
348     sal_uInt32 nFillColor = mAny.get<sal_uInt32>();
349 
350     // get InteropGrabBag and search the relevant attributes
351     OUString sColorFillScheme;
352     sal_uInt32 nOriginalColor = 0;
353     Sequence< PropertyValue > aStyleProperties, aTransformations;
354     if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
355     {
356         Sequence< PropertyValue > aGrabBag;
357         mAny >>= aGrabBag;
358         for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
359         {
360             if( aGrabBag[i].Name == "SpPrSolidFillSchemeClr" )
361                 aGrabBag[i].Value >>= sColorFillScheme;
362             else if( aGrabBag[i].Name == "OriginalSolidFillClr" )
363                 aGrabBag[i].Value >>= nOriginalColor;
364             else if( aGrabBag[i].Name == "StyleFillRef" )
365                 aGrabBag[i].Value >>= aStyleProperties;
366             else if( aGrabBag[i].Name == "SpPrSolidFillSchemeClrTransformations" )
367                 aGrabBag[i].Value >>= aTransformations;
368         }
369     }
370 
371     sal_Int32 nAlpha = MAX_PERCENT;
372     if( GetProperty( rXPropSet, "FillTransparence" ) )
373     {
374         sal_Int32 nTransparency = 0;
375         mAny >>= nTransparency;
376         // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
377         nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
378     }
379 
380     // write XML
381     if ( nFillColor != nOriginalColor )
382     {
383         // the user has set a different color for the shape
384         WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
385     }
386     else if ( !sColorFillScheme.isEmpty() )
387     {
388         // the shape had a scheme color and the user didn't change it
389         WriteSolidFill( sColorFillScheme, aTransformations, nAlpha );
390     }
391     else if ( aStyleProperties.hasElements() )
392     {
393         sal_uInt32 nThemeColor = 0;
394         sal_Int32 nThemeAlpha = MAX_PERCENT;
395         for( sal_Int32 i=0; i < aStyleProperties.getLength(); ++i )
396         {
397             if( aStyleProperties[i].Name == "Color" )
398             {
399                 aStyleProperties[i].Value >>= nThemeColor;
400             }
401             else if(aStyleProperties[i].Name == "Transformations" )
402             {
403                 Sequence< PropertyValue > aStyleTransformations;
404                 aStyleProperties[i].Value >>= aStyleTransformations;
405                 for( sal_Int32 j = 0; j < aStyleTransformations.getLength(); j++ )
406                 {
407                     if (aStyleTransformations[j].Name == "alpha" )
408                     {
409                         aStyleTransformations[j].Value >>= nThemeAlpha;
410                         break;
411                     }
412                 }
413             }
414         }
415         if ( nFillColor != nThemeColor || nAlpha != nThemeAlpha )
416             // the shape contains a theme but it wasn't being used
417             WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
418 
419         // in case the shape used the style color and the user didn't change it,
420         // we must not write a <a: solidFill> tag.
421     }
422     else
423     {
424         // the shape had a custom color and the user didn't change it
425         WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
426     }
427 }
428 
429 void DrawingML::WriteGradientStop(sal_uInt16 nStop, ::Color nColor, sal_Int32 nAlpha)
430 {
431     mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nStop * 1000));
432     WriteColor(nColor, nAlpha);
433     mpFS->endElementNS( XML_a, XML_gs );
434 }
435 
436 ::Color DrawingML::ColorWithIntensity( sal_uInt32 nColor, sal_uInt32 nIntensity )
437 {
438     return ::Color(( ( ( nColor & 0xff ) * nIntensity ) / 100 )
439         | ( ( ( ( ( nColor & 0xff00 ) >> 8 ) * nIntensity ) / 100 ) << 8 )
440         | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 ));
441 }
442 
443 bool DrawingML::EqualGradients( awt::Gradient aGradient1, awt::Gradient aGradient2 )
444 {
445     return aGradient1.Style == aGradient2.Style &&
446             aGradient1.StartColor == aGradient2.StartColor &&
447             aGradient1.EndColor == aGradient2.EndColor &&
448             aGradient1.Angle == aGradient2.Angle &&
449             aGradient1.Border == aGradient2.Border &&
450             aGradient1.XOffset == aGradient2.XOffset &&
451             aGradient1.YOffset == aGradient2.YOffset &&
452             aGradient1.StartIntensity == aGradient2.StartIntensity &&
453             aGradient1.EndIntensity == aGradient2.EndIntensity &&
454             aGradient1.StepCount == aGradient2.StepCount;
455 }
456 
457 void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet )
458 {
459     awt::Gradient aGradient;
460     if (GetProperty(rXPropSet, "FillGradient"))
461     {
462         aGradient = *o3tl::doAccess<awt::Gradient>(mAny);
463 
464         // get InteropGrabBag and search the relevant attributes
465         awt::Gradient aOriginalGradient;
466         Sequence< PropertyValue > aGradientStops;
467         if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
468         {
469             Sequence< PropertyValue > aGrabBag;
470             mAny >>= aGrabBag;
471             for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
472                 if( aGrabBag[i].Name == "GradFillDefinition" )
473                     aGrabBag[i].Value >>= aGradientStops;
474                 else if( aGrabBag[i].Name == "OriginalGradFill" )
475                     aGrabBag[i].Value >>= aOriginalGradient;
476         }
477 
478         // check if an ooxml gradient had been imported and if the user has modified it
479         // Gradient grab-bag depends on theme grab-bag, which is implemented
480         // only for DOCX.
481         if( EqualGradients( aOriginalGradient, aGradient ) && GetDocumentType() == DOCUMENT_DOCX)
482         {
483             // If we have no gradient stops that means original gradient were defined by a theme.
484             if( aGradientStops.hasElements() )
485             {
486                 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
487                 WriteGrabBagGradientFill(aGradientStops, aGradient);
488                 mpFS->endElementNS( XML_a, XML_gradFill );
489             }
490         }
491         else
492         {
493             mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
494             WriteGradientFill(aGradient, rXPropSet);
495             mpFS->endElementNS( XML_a, XML_gradFill );
496         }
497     }
498 }
499 
500 void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, awt::Gradient rGradient )
501 {
502     // write back the original gradient
503     mpFS->startElementNS(XML_a, XML_gsLst);
504 
505     // get original stops and write them
506     for( sal_Int32 i=0; i < aGradientStops.getLength(); ++i )
507     {
508         Sequence< PropertyValue > aGradientStop;
509         aGradientStops[i].Value >>= aGradientStop;
510 
511         // get values
512         OUString sSchemeClr;
513         double nPos = 0;
514         sal_Int16 nTransparency = 0;
515         ::Color nRgbClr;
516         Sequence< PropertyValue > aTransformations;
517         for( sal_Int32 j=0; j < aGradientStop.getLength(); ++j )
518         {
519             if( aGradientStop[j].Name == "SchemeClr" )
520                 aGradientStop[j].Value >>= sSchemeClr;
521             else if( aGradientStop[j].Name == "RgbClr" )
522                 aGradientStop[j].Value >>= nRgbClr;
523             else if( aGradientStop[j].Name == "Pos" )
524                 aGradientStop[j].Value >>= nPos;
525             else if( aGradientStop[j].Name == "Transparency" )
526                 aGradientStop[j].Value >>= nTransparency;
527             else if( aGradientStop[j].Name == "Transformations" )
528                 aGradientStop[j].Value >>= aTransformations;
529         }
530         // write stop
531         mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nPos * 100000.0).getStr());
532         if( sSchemeClr.isEmpty() )
533         {
534             // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
535             sal_Int32 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
536             WriteColor( nRgbClr, nAlpha );
537         }
538         else
539         {
540             WriteColor( sSchemeClr, aTransformations );
541         }
542         mpFS->endElementNS( XML_a, XML_gs );
543     }
544     mpFS->endElementNS( XML_a, XML_gsLst );
545 
546     switch (rGradient.Style)
547     {
548         default:
549             mpFS->singleElementNS(
550                 XML_a, XML_lin, XML_ang,
551                 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
552             break;
553         case awt::GradientStyle_RADIAL:
554             WriteRadialGradientPath(rGradient, mpFS);
555             break;
556     }
557 }
558 
559 void DrawingML::WriteGradientFill(awt::Gradient rGradient,
560                                   const uno::Reference<beans::XPropertySet>& rXPropSet)
561 {
562     switch( rGradient.Style )
563     {
564         default:
565         case awt::GradientStyle_LINEAR:
566         {
567             awt::Gradient aTransparenceGradient;
568             bool bTransparent = false;
569             if (rXPropSet.is() && GetProperty(rXPropSet, "FillTransparenceGradient"))
570             {
571                 aTransparenceGradient = *o3tl::doAccess<awt::Gradient>(mAny);
572                 bTransparent = true;
573             }
574 
575             mpFS->startElementNS(XML_a, XML_gsLst);
576             sal_Int32 nStartAlpha = MAX_PERCENT;
577             sal_Int32 nEndAlpha = MAX_PERCENT;
578             if (bTransparent)
579             {
580                 nStartAlpha = GetAlphaFromTransparenceGradient(aTransparenceGradient, true);
581                 nEndAlpha = GetAlphaFromTransparenceGradient(aTransparenceGradient, false);
582             }
583             WriteGradientStop(0, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity),
584                               nStartAlpha);
585             WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
586                               nEndAlpha);
587             mpFS->endElementNS( XML_a, XML_gsLst );
588             mpFS->singleElementNS(
589                 XML_a, XML_lin, XML_ang,
590                 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
591             break;
592         }
593 
594         case awt::GradientStyle_AXIAL:
595             mpFS->startElementNS(XML_a, XML_gsLst);
596             WriteGradientStop( 0, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
597             WriteGradientStop( 50, ColorWithIntensity( rGradient.StartColor, rGradient.StartIntensity ) );
598             WriteGradientStop( 100, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
599             mpFS->endElementNS( XML_a, XML_gsLst );
600             mpFS->singleElementNS(
601                 XML_a, XML_lin, XML_ang,
602                 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
603             break;
604 
605         case awt::GradientStyle_RADIAL:
606         {
607             mpFS->startElementNS(XML_a, XML_gsLst);
608             WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity));
609             if (rGradient.Border > 0 && rGradient.Border < 100)
610                 // Map border to an additional gradient stop, which has the
611                 // same color as the final stop.
612                 WriteGradientStop(
613                     100 - rGradient.Border,
614                     ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity));
615             WriteGradientStop(100,
616                               ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity));
617             mpFS->endElementNS(XML_a, XML_gsLst);
618 
619             WriteRadialGradientPath(rGradient, mpFS);
620             break;
621         }
622             /* I don't see how to apply transformation to gradients, so
623              * elliptical will end as radial and square as
624              * rectangular. also position offsets are not applied */
625         case awt::GradientStyle_ELLIPTICAL:
626         case awt::GradientStyle_RECT:
627         case awt::GradientStyle_SQUARE:
628             mpFS->startElementNS(XML_a, XML_gsLst);
629             WriteGradientStop( 0, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
630             WriteGradientStop( 100, ColorWithIntensity( rGradient.StartColor, rGradient.StartIntensity ) );
631             mpFS->endElementNS( XML_a, XML_gsLst );
632             mpFS->singleElementNS( XML_a, XML_path,
633                     XML_path, ( rGradient.Style == awt::GradientStyle_RADIAL || rGradient.Style == awt::GradientStyle_ELLIPTICAL ) ? "circle" : "rect" );
634             break;
635     }
636 }
637 
638 void DrawingML::WriteLineArrow( const Reference< XPropertySet >& rXPropSet, bool bLineStart )
639 {
640     ESCHER_LineEnd eLineEnd;
641     sal_Int32 nArrowLength;
642     sal_Int32 nArrowWidth;
643 
644     if ( EscherPropertyContainer::GetLineArrow( bLineStart, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
645     {
646         const char* len;
647         const char* type;
648         const char* width;
649 
650         switch( nArrowLength )
651         {
652             case ESCHER_LineShortArrow:
653                 len = "sm";
654                 break;
655             default:
656             case ESCHER_LineMediumLenArrow:
657                 len = "med";
658                 break;
659             case ESCHER_LineLongArrow:
660                 len = "lg";
661                 break;
662         }
663 
664         switch( eLineEnd )
665         {
666             default:
667             case ESCHER_LineNoEnd:
668                 type = "none";
669                 break;
670             case ESCHER_LineArrowEnd:
671                 type = "triangle";
672                 break;
673             case ESCHER_LineArrowStealthEnd:
674                 type = "stealth";
675                 break;
676             case ESCHER_LineArrowDiamondEnd:
677                 type = "diamond";
678                 break;
679             case ESCHER_LineArrowOvalEnd:
680                 type = "oval";
681                 break;
682             case ESCHER_LineArrowOpenEnd:
683                 type = "arrow";
684                 break;
685         }
686 
687         switch( nArrowWidth )
688         {
689             case ESCHER_LineNarrowArrow:
690                 width = "sm";
691                 break;
692             default:
693             case ESCHER_LineMediumWidthArrow:
694                 width = "med";
695                 break;
696             case ESCHER_LineWideArrow:
697                 width = "lg";
698                 break;
699         }
700 
701         mpFS->singleElementNS( XML_a, bLineStart ? XML_headEnd : XML_tailEnd,
702                                XML_len, len,
703                                XML_type, type,
704                                XML_w, width );
705     }
706 }
707 
708 void DrawingML::WriteOutline( const Reference<XPropertySet>& rXPropSet, Reference< frame::XModel > const & xModel )
709 {
710     drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
711 
712     if (GetProperty(rXPropSet, "LineStyle"))
713         mAny >>= aLineStyle;
714 
715     sal_uInt32 nLineWidth = 0;
716     ::Color nColor;
717     sal_Int32 nColorAlpha = MAX_PERCENT;
718     bool bColorSet = false;
719     const char* cap = nullptr;
720     drawing::LineDash aLineDash;
721     bool bDashSet = false;
722     bool bNoFill = false;
723 
724     // get InteropGrabBag and search the relevant attributes
725     OUString sColorFillScheme;
726 
727     ::Color nOriginalColor;
728     ::Color nStyleColor;
729     sal_uInt32 nStyleLineWidth = 0;
730 
731     Sequence<PropertyValue> aStyleProperties;
732     Sequence<PropertyValue> aTransformations;
733 
734     drawing::LineStyle aStyleLineStyle(drawing::LineStyle_NONE);
735     drawing::LineJoint aStyleLineJoint(drawing::LineJoint_NONE);
736 
737     if (GetProperty(rXPropSet, "InteropGrabBag"))
738     {
739         Sequence<PropertyValue> aGrabBag;
740         mAny >>= aGrabBag;
741 
742         for (sal_Int32 i=0; i < aGrabBag.getLength(); ++i)
743         {
744             if( aGrabBag[i].Name == "SpPrLnSolidFillSchemeClr" )
745                 aGrabBag[i].Value >>= sColorFillScheme;
746             else if( aGrabBag[i].Name == "OriginalLnSolidFillClr" )
747                 aGrabBag[i].Value >>= nOriginalColor;
748             else if( aGrabBag[i].Name == "StyleLnRef" )
749                 aGrabBag[i].Value >>= aStyleProperties;
750             else if( aGrabBag[i].Name == "SpPrLnSolidFillSchemeClrTransformations" )
751                 aGrabBag[i].Value >>= aTransformations;
752         }
753         if (aStyleProperties.hasElements())
754         {
755             for (sal_Int32 i=0; i < aStyleProperties.getLength(); ++i)
756             {
757                 if( aStyleProperties[i].Name == "Color" )
758                     aStyleProperties[i].Value >>= nStyleColor;
759                 else if( aStyleProperties[i].Name == "LineStyle" )
760                     aStyleProperties[i].Value >>= aStyleLineStyle;
761                 else if( aStyleProperties[i].Name == "LineJoint" )
762                     aStyleProperties[i].Value >>= aStyleLineJoint;
763                 else if( aStyleProperties[i].Name == "LineWidth" )
764                     aStyleProperties[i].Value >>= nStyleLineWidth;
765             }
766         }
767     }
768 
769     if (GetProperty(rXPropSet, "LineWidth"))
770         mAny >>= nLineWidth;
771 
772     switch (aLineStyle)
773     {
774         case drawing::LineStyle_NONE:
775             bNoFill = true;
776             break;
777         case drawing::LineStyle_DASH:
778             if (GetProperty(rXPropSet, "LineDash"))
779             {
780                 aLineDash = mAny.get<drawing::LineDash>();
781                 //this query is good for shapes, but in the case of charts it returns 0 values
782                 if (aLineDash.Dots == 0 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 0) {
783                     OUString aLineDashName;
784                     if (GetProperty(rXPropSet, "LineDashName"))
785                         mAny >>= aLineDashName;
786                     if (!aLineDashName.isEmpty() && xModel) {
787                         css::uno::Any aAny = getLineDash(xModel, aLineDashName);
788                         aAny >>= aLineDash;
789                     }
790                 }
791             }
792             else
793             {
794                 //export the linestyle of chart wall (plot area) and chart page
795                 OUString aLineDashName;
796                 if (GetProperty(rXPropSet, "LineDashName"))
797                     mAny >>= aLineDashName;
798                 if (!aLineDashName.isEmpty() && xModel) {
799                     css::uno::Any aAny = getLineDash(xModel, aLineDashName);
800                     aAny >>= aLineDash;
801                 }
802             }
803             bDashSet = true;
804             if (aLineDash.Style == DashStyle_ROUND || aLineDash.Style == DashStyle_ROUNDRELATIVE)
805             {
806                 cap = "rnd";
807             }
808 
809             SAL_INFO("oox.shape", "dash dots: " << aLineDash.Dots << " dashes: " << aLineDash.Dashes
810                     << " dotlen: " << aLineDash.DotLen << " dashlen: " << aLineDash.DashLen << " distance: " <<  aLineDash.Distance);
811 
812             [[fallthrough]];
813         case drawing::LineStyle_SOLID:
814         default:
815             if (GetProperty(rXPropSet, "LineColor"))
816             {
817                 nColor = ::Color(mAny.get<sal_uInt32>() & 0xffffff);
818                 bColorSet = true;
819             }
820             if (GetProperty(rXPropSet, "LineTransparence"))
821             {
822                 nColorAlpha = MAX_PERCENT - (mAny.get<sal_Int16>() * PER_PERCENT);
823             }
824             break;
825     }
826 
827     mpFS->startElementNS( XML_a, XML_ln,
828                           XML_cap, cap,
829                           XML_w, nLineWidth > 1 && nStyleLineWidth != nLineWidth ?
830                               OString::number(oox::drawingml::convertHmmToEmu(nLineWidth)).getStr() : nullptr );
831 
832     if( bColorSet )
833     {
834         if( nColor != nOriginalColor )
835         {
836             // the user has set a different color for the line
837             WriteSolidFill( nColor, nColorAlpha );
838         }
839         else if( !sColorFillScheme.isEmpty() )
840         {
841             // the line had a scheme color and the user didn't change it
842             WriteSolidFill( sColorFillScheme, aTransformations );
843         }
844         else if( aStyleProperties.hasElements() )
845         {
846             if( nColor != nStyleColor )
847                 // the line style defines some color but it wasn't being used
848                 WriteSolidFill( nColor );
849             // in case the shape used the style color and the user didn't change it,
850             // we must not write a <a: solidFill> tag.
851         }
852         else
853         {
854             WriteSolidFill( nColor, nColorAlpha );
855         }
856     }
857 
858     if( bDashSet && aStyleLineStyle != drawing::LineStyle_DASH )
859     {
860         // convert absolute dash/dot length to relative length
861         int relDotLen = nLineWidth ? aLineDash.DotLen / nLineWidth : 0;
862         int relDashLen = nLineWidth ? aLineDash.DashLen / nLineWidth : 0;
863         int relDistance = nLineWidth ? aLineDash.Distance / nLineWidth : 0;
864         // fixing relative values in the case of linewidths smaller than 1 pt
865         if (0 < nLineWidth && nLineWidth < 35) //35 HMM == 1 pt
866         {
867             relDotLen = relDotLen ? (relDotLen + 1) * (nLineWidth * 360.0 / 12700) : 0;
868             relDashLen = relDashLen ? (relDashLen + 1) * (nLineWidth * 360.0 / 12700) : 0;
869             relDistance = relDistance ? (relDistance + 1) * (nLineWidth * 360.0 / 12700) : 0;
870         }
871         // keep default mso preset linestyles (instead of custdash)
872         if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 0 && relDashLen == 0 && relDistance == 3)
873         {
874             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
875         }
876         else if (aLineDash.Dots == 0 && relDotLen == 0 && aLineDash.Dashes == 1 && relDashLen == 4 && relDistance == 3)
877         {
878             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
879         }
880         else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 4 && relDistance == 3)
881         {
882             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dashDot");
883         }
884         else if (aLineDash.Dots == 0 && relDotLen == 0 && aLineDash.Dashes == 1 && relDashLen == 8 && relDistance == 3)
885         {
886             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDash");
887         }
888         else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 8 && relDistance == 3)
889         {
890             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDot");
891         }
892         else if (aLineDash.Dots == 2 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 8 && relDistance == 3)
893         {
894             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDotDot");
895         }
896         else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 0 && relDashLen == 0 && relDistance == 1)
897         {
898             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
899         }
900         else if (aLineDash.Dots == 0 && relDotLen == 0 && aLineDash.Dashes == 1 && relDashLen == 3 && relDistance == 1)
901         {
902             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
903         }
904         else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 3 && relDistance == 1)
905         {
906             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDot");
907         }
908         else if (aLineDash.Dots == 2 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 3 && relDistance == 1)
909         {
910             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
911         }
912         /*convert some LO preset dashes to MSO preset dashes for oox interoperability
913         LO preset dashes which don't have equivalent in MSO preset dashes: 2 Dots 3 Dashes, Line with Fine Dots, 3 Dashes 3 Dots*/
914         //ultrafine Dashed, Ultrafine Dotted -> sysDot
915         else if ((aLineDash.Dots == 1 && aLineDash.DotLen == 51 && aLineDash.Dashes == 1 && aLineDash.DashLen == 51 && aLineDash.Distance == 51) ||
916                  (aLineDash.Dots == 1 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 50))
917         {
918             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
919         }
920         //Fine Dashed -> dash
921         else if (aLineDash.Dots == 1 && aLineDash.DotLen == 197 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 197)
922         {
923             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
924         }
925         //Fine Dotted -> dot
926         else if (aLineDash.Dots == 1 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 457)
927         {
928             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
929         }
930         //Line Style 9, Dashed -> sysDash
931         else if ((aLineDash.Dots == 1 && aLineDash.DotLen == 197 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 120) ||
932                  (aLineDash.Dots == 1 && aLineDash.DotLen == 197 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0  && aLineDash.Distance == 127))
933         {
934             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
935         }
936         //2 Dots 1 Dash -> sysDashDotDot
937         else if (aLineDash.Dots == 2 && aLineDash.DotLen == 0 && aLineDash.Dashes == 1 && aLineDash.DashLen == 203 && aLineDash.Distance == 203)
938         {
939             mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
940         }
941         else
942         {
943             mpFS->startElementNS(XML_a, XML_custDash);
944 
945             // Check that line-width is positive and distance between dashes\dots is positive
946             if ( nLineWidth > 0 && aLineDash.Distance > 0 )
947             {
948                 // Write 'dashes' first, and then 'dots'
949                 int i;
950                 sal_Int32 nSp = aLineDash.Distance * 100 / nLineWidth;
951                 if ( aLineDash.Dashes > 0 )
952                 {
953                     sal_Int32 nD = aLineDash.DashLen * 100 / nLineWidth;
954                     for( i = 0; i < aLineDash.Dashes; i ++ )
955                     {
956                         mpFS->singleElementNS( XML_a , XML_ds,
957                                                XML_d , write1000thOfAPercent(nD),
958                                                XML_sp, write1000thOfAPercent(nSp) );
959                     }
960                 }
961                 if ( aLineDash.Dots > 0 )
962                 {
963                     sal_Int32 nD = aLineDash.DotLen * 100 / nLineWidth;
964                     for( i = 0; i < aLineDash.Dots; i ++ )
965                     {
966                         mpFS->singleElementNS( XML_a, XML_ds,
967                                                XML_d , write1000thOfAPercent(nD),
968                                                XML_sp, write1000thOfAPercent(nSp) );
969                     }
970                 }
971             }
972 
973             SAL_WARN_IF(nLineWidth <= 0,
974                         "oox.shape", "while writing outline - custom dash - line width was < 0  : " << nLineWidth);
975             SAL_WARN_IF(aLineDash.Dashes < 0,
976                         "oox.shape", "while writing outline - custom dash - number of dashes was < 0  : " << aLineDash.Dashes);
977             SAL_WARN_IF(aLineDash.Dashes > 0 && aLineDash.DashLen <= 0,
978                         "oox.shape", "while writing outline - custom dash - dash length was < 0  : " << aLineDash.DashLen);
979             SAL_WARN_IF(aLineDash.Dots < 0,
980                         "oox.shape", "while writing outline - custom dash - number of dots was < 0  : " << aLineDash.Dots);
981             SAL_WARN_IF(aLineDash.Dots > 0 && aLineDash.DotLen <= 0,
982                         "oox.shape", "while writing outline - custom dash - dot length was < 0  : " << aLineDash.DotLen);
983             SAL_WARN_IF(aLineDash.Distance <= 0,
984                         "oox.shape", "while writing outline - custom dash - distance was < 0  : " << aLineDash.Distance);
985 
986             mpFS->endElementNS( XML_a, XML_custDash );
987         }
988     }
989 
990     if (!bNoFill && nLineWidth > 1 && GetProperty(rXPropSet, "LineJoint"))
991     {
992         LineJoint eLineJoint = mAny.get<LineJoint>();
993 
994         if( aStyleLineJoint == LineJoint_NONE || aStyleLineJoint != eLineJoint )
995         {
996             // style-defined line joint does not exist, or is different from the shape's joint
997             switch( eLineJoint )
998             {
999                 case LineJoint_NONE:
1000                 case LineJoint_BEVEL:
1001                     mpFS->singleElementNS(XML_a, XML_bevel);
1002                     break;
1003                 default:
1004                 case LineJoint_MIDDLE:
1005                 case LineJoint_MITER:
1006                     mpFS->singleElementNS(XML_a, XML_miter);
1007                     break;
1008                 case LineJoint_ROUND:
1009                     mpFS->singleElementNS(XML_a, XML_round);
1010                     break;
1011             }
1012         }
1013     }
1014 
1015     if( !bNoFill )
1016     {
1017         WriteLineArrow( rXPropSet, true );
1018         WriteLineArrow( rXPropSet, false );
1019     }
1020     else
1021     {
1022         mpFS->singleElementNS(XML_a, XML_noFill);
1023     }
1024 
1025     mpFS->endElementNS( XML_a, XML_ln );
1026 }
1027 
1028 const char* DrawingML::GetComponentDir()
1029 {
1030     switch ( meDocumentType )
1031     {
1032         case DOCUMENT_DOCX: return "word";
1033         case DOCUMENT_PPTX: return "ppt";
1034         case DOCUMENT_XLSX: return "xl";
1035     }
1036 
1037     return "unknown";
1038 }
1039 
1040 const char* DrawingML::GetRelationCompPrefix()
1041 {
1042     switch ( meDocumentType )
1043     {
1044         case DOCUMENT_DOCX: return "";
1045         case DOCUMENT_PPTX:
1046         case DOCUMENT_XLSX: return "../";
1047     }
1048 
1049     return "unknown";
1050 }
1051 
1052 OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia )
1053 {
1054     GfxLink aLink = rGraphic.GetGfxLink ();
1055     OUString sMediaType;
1056     const char* pExtension = "";
1057     OUString sRelId;
1058 
1059     SvMemoryStream aStream;
1060     const void* aData = aLink.GetData();
1061     std::size_t nDataSize = aLink.GetDataSize();
1062 
1063     switch ( aLink.GetType() )
1064     {
1065         case GfxLinkType::NativeGif:
1066             sMediaType = "image/gif";
1067             pExtension = ".gif";
1068             break;
1069 
1070         // #i15508# added BMP type for better exports
1071         // export not yet active, so adding for reference (not checked)
1072         case GfxLinkType::NativeBmp:
1073             sMediaType = "image/bmp";
1074             pExtension = ".bmp";
1075             break;
1076 
1077         case GfxLinkType::NativeJpg:
1078             sMediaType = "image/jpeg";
1079             pExtension = ".jpeg";
1080             break;
1081         case GfxLinkType::NativePng:
1082             sMediaType = "image/png";
1083             pExtension = ".png";
1084             break;
1085         case GfxLinkType::NativeTif:
1086             sMediaType = "image/tiff";
1087             pExtension = ".tif";
1088             break;
1089         case GfxLinkType::NativeWmf:
1090             sMediaType = "image/x-wmf";
1091             pExtension = ".wmf";
1092             break;
1093         case GfxLinkType::NativeMet:
1094             sMediaType = "image/x-met";
1095             pExtension = ".met";
1096             break;
1097         case GfxLinkType::NativePct:
1098             sMediaType = "image/x-pict";
1099             pExtension = ".pct";
1100             break;
1101         case GfxLinkType::NativeMov:
1102             sMediaType = "application/movie";
1103             pExtension = ".MOV";
1104             break;
1105         default:
1106         {
1107             GraphicType aType = rGraphic.GetType();
1108             if ( aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile)
1109             {
1110                 if ( aType == GraphicType::Bitmap )
1111                 {
1112                     (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::PNG );
1113                     sMediaType = "image/png";
1114                     pExtension = ".png";
1115                 }
1116                 else
1117                 {
1118                     (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::EMF );
1119                     sMediaType = "image/x-emf";
1120                     pExtension = ".emf";
1121                 }
1122             }
1123             else
1124             {
1125                 SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType) );
1126                 /*Earlier, even in case of unhandled graphic types we were
1127                   proceeding to write the image, which would eventually
1128                   write an empty image with a zero size, and return a valid
1129                   relationID, which is incorrect.
1130                   */
1131                 return sRelId;
1132             }
1133 
1134             aData = aStream.GetData();
1135             nDataSize = aStream.GetEndOfData();
1136             break;
1137         }
1138     }
1139 
1140     Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
1141                                                                       .appendAscii( GetComponentDir() )
1142                                                                       .append( "/media/image" )
1143                                                                       .append( static_cast<sal_Int32>(mnImageCounter) )
1144                                                                       .appendAscii( pExtension )
1145                                                                       .makeStringAndClear(),
1146                                                                       sMediaType );
1147     xOutStream->writeBytes( Sequence< sal_Int8 >( static_cast<const sal_Int8*>(aData), nDataSize ) );
1148     xOutStream->closeOutput();
1149 
1150     const OString sRelPathToMedia = "media/image";
1151     OString sRelationCompPrefix;
1152     if ( bRelPathToMedia )
1153         sRelationCompPrefix = "../";
1154     else
1155         sRelationCompPrefix = GetRelationCompPrefix();
1156     sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1157                                 oox::getRelationship(Relationship::IMAGE),
1158                                 OUStringBuffer()
1159                                 .appendAscii( sRelationCompPrefix.getStr() )
1160                                 .appendAscii( sRelPathToMedia.getStr() )
1161                                 .append( static_cast<sal_Int32>(mnImageCounter ++) )
1162                                 .appendAscii( pExtension )
1163                                 .makeStringAndClear() );
1164 
1165     return sRelId;
1166 }
1167 
1168 void DrawingML::WriteMediaNonVisualProperties(const css::uno::Reference<css::drawing::XShape>& xShape)
1169 {
1170     SdrMediaObj* pMediaObj = dynamic_cast<SdrMediaObj*>(GetSdrObjectFromXShape(xShape));
1171     if (!pMediaObj)
1172         return;
1173 
1174     // extension
1175     OUString aExtension;
1176     const OUString& rURL(pMediaObj->getURL());
1177     int nLastDot = rURL.lastIndexOf('.');
1178     if (nLastDot >= 0)
1179         aExtension = rURL.copy(nLastDot);
1180 
1181     bool bEmbed = rURL.startsWith("vnd.sun.star.Package:");
1182     Relationship eMediaType = Relationship::VIDEO;
1183 
1184     // mime type
1185 #if HAVE_FEATURE_AVMEDIA
1186     OUString aMimeType(pMediaObj->getMediaProperties().getMimeType());
1187 #else
1188     OUString aMimeType("none");
1189 #endif
1190     if (aMimeType == "application/vnd.sun.star.media")
1191     {
1192         // try to set something better
1193         // TODO fix the importer to actually set the mimetype on import
1194         if (aExtension.equalsIgnoreAsciiCase(".avi"))
1195             aMimeType = "video/x-msvideo";
1196         else if (aExtension.equalsIgnoreAsciiCase(".flv"))
1197             aMimeType = "video/x-flv";
1198         else if (aExtension.equalsIgnoreAsciiCase(".mp4"))
1199             aMimeType = "video/mp4";
1200         else if (aExtension.equalsIgnoreAsciiCase(".mov"))
1201             aMimeType = "video/quicktime";
1202         else if (aExtension.equalsIgnoreAsciiCase(".ogv"))
1203             aMimeType = "video/ogg";
1204         else if (aExtension.equalsIgnoreAsciiCase(".wmv"))
1205             aMimeType = "video/x-ms-wmv";
1206         else if (aExtension.equalsIgnoreAsciiCase(".wav"))
1207         {
1208             aMimeType = "audio/x-wav";
1209             eMediaType = Relationship::AUDIO;
1210         }
1211     }
1212 
1213     OUString aVideoFileRelId;
1214     OUString aMediaRelId;
1215 
1216     if (bEmbed)
1217     {
1218         // copy the video stream
1219         Reference<XOutputStream> xOutStream = mpFB->openFragmentStream(OUStringBuffer()
1220                                                                        .appendAscii(GetComponentDir())
1221                                                                        .append("/media/media")
1222                                                                        .append(static_cast<sal_Int32>(mnImageCounter))
1223                                                                        .append(aExtension)
1224                                                                        .makeStringAndClear(),
1225                                                                        aMimeType);
1226 
1227         uno::Reference<io::XInputStream> xInputStream(pMediaObj->GetInputStream());
1228         comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream);
1229 
1230         xOutStream->closeOutput();
1231 
1232         // create the relation
1233         OUString aPath = OUStringBuffer().appendAscii(GetRelationCompPrefix())
1234                                          .append("media/media")
1235                                          .append(static_cast<sal_Int32>(mnImageCounter++))
1236                                          .append(aExtension)
1237                                          .makeStringAndClear();
1238         aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), aPath);
1239         aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), aPath);
1240     }
1241     else
1242     {
1243         aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), rURL);
1244         aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), rURL);
1245     }
1246 
1247     GetFS()->startElementNS(XML_p, XML_nvPr);
1248 
1249     GetFS()->singleElementNS(XML_a, eMediaType == Relationship::VIDEO ? XML_videoFile : XML_audioFile,
1250                     FSNS(XML_r, XML_link), aVideoFileRelId.toUtf8());
1251 
1252     GetFS()->startElementNS(XML_p, XML_extLst);
1253     // media extensions; google this ID for details
1254     GetFS()->startElementNS(XML_p, XML_ext, XML_uri, "{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}");
1255 
1256     GetFS()->singleElementNS(XML_p14, XML_media,
1257             bEmbed? FSNS(XML_r, XML_embed): FSNS(XML_r, XML_link), aMediaRelId.toUtf8());
1258 
1259     GetFS()->endElementNS(XML_p, XML_ext);
1260     GetFS()->endElementNS(XML_p, XML_extLst);
1261 
1262     GetFS()->endElementNS(XML_p, XML_nvPr);
1263 }
1264 
1265 void DrawingML::WriteImageBrightnessContrastTransparence(uno::Reference<beans::XPropertySet> const & rXPropSet)
1266 {
1267     sal_Int16 nBright = 0;
1268     sal_Int32 nContrast = 0;
1269     sal_Int16 nTransparence = 0;
1270 
1271     if (GetProperty(rXPropSet, "AdjustLuminance"))
1272         nBright = mAny.get<sal_Int16>();
1273     if (GetProperty(rXPropSet, "AdjustContrast"))
1274         nContrast = mAny.get<sal_Int32>();
1275     if (GetProperty(rXPropSet, "Transparency"))
1276         nTransparence = mAny.get<sal_Int16>();
1277 
1278 
1279     if (nBright || nContrast)
1280     {
1281         mpFS->singleElementNS(XML_a, XML_lum,
1282                    XML_bright, nBright ? OString::number(nBright * 1000).getStr() : nullptr,
1283                    XML_contrast, nContrast ? OString::number(nContrast * 1000).getStr() : nullptr);
1284     }
1285 
1286     if (nTransparence)
1287     {
1288         sal_Int32 nAlphaMod = (100 - nTransparence ) * PER_PERCENT;
1289         mpFS->singleElementNS(XML_a, XML_alphaModFix, XML_amt, OString::number(nAlphaMod));
1290     }
1291 }
1292 
1293 OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const & rXPropSet,
1294                                       uno::Reference<graphic::XGraphic> const & rxGraphic,
1295                                       bool bRelPathToMedia)
1296 {
1297     OUString sRelId;
1298 
1299     if (!rxGraphic.is())
1300         return sRelId;
1301 
1302     Graphic aGraphic(rxGraphic);
1303     if (mpTextExport)
1304     {
1305         BitmapChecksum nChecksum = aGraphic.GetChecksum();
1306         sRelId = mpTextExport->FindRelId(nChecksum);
1307     }
1308     if (sRelId.isEmpty())
1309     {
1310         sRelId = WriteImage(aGraphic, bRelPathToMedia);
1311         if (mpTextExport)
1312         {
1313             BitmapChecksum nChecksum = aGraphic.GetChecksum();
1314             mpTextExport->CacheRelId(nChecksum, sRelId);
1315         }
1316     }
1317 
1318     mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId.toUtf8());
1319 
1320     WriteImageBrightnessContrastTransparence(rXPropSet);
1321 
1322     WriteArtisticEffect(rXPropSet);
1323 
1324     mpFS->endElementNS(XML_a, XML_blip);
1325 
1326     return sRelId;
1327 }
1328 
1329 void DrawingML::WriteXGraphicBlipMode(uno::Reference<beans::XPropertySet> const & rXPropSet,
1330                                       uno::Reference<graphic::XGraphic> const & rxGraphic)
1331 {
1332     BitmapMode eBitmapMode(BitmapMode_NO_REPEAT);
1333     if (GetProperty(rXPropSet, "FillBitmapMode"))
1334         mAny >>= eBitmapMode;
1335 
1336     SAL_INFO("oox.shape", "fill bitmap mode: " << int(eBitmapMode));
1337 
1338     switch (eBitmapMode)
1339     {
1340     case BitmapMode_REPEAT:
1341         mpFS->singleElementNS(XML_a, XML_tile);
1342         break;
1343     case BitmapMode_STRETCH:
1344         WriteXGraphicStretch(rXPropSet, rxGraphic);
1345         break;
1346     default:
1347         break;
1348     }
1349 }
1350 
1351 void DrawingML::WriteBlipOrNormalFill( const Reference< XPropertySet >& xPropSet, const OUString& rURLPropName )
1352 {
1353     // check for blip and otherwise fall back to normal fill
1354     // we always store normal fill properties but OOXML
1355     // uses a choice between our fill props and BlipFill
1356     if (GetProperty ( xPropSet, rURLPropName ))
1357         WriteBlipFill( xPropSet, rURLPropName );
1358     else
1359         WriteFill(xPropSet);
1360 }
1361 
1362 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName )
1363 {
1364     WriteBlipFill( rXPropSet, sURLPropName, XML_a );
1365 }
1366 
1367 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName, sal_Int32 nXmlNamespace )
1368 {
1369     if ( GetProperty( rXPropSet, sURLPropName ) )
1370     {
1371         uno::Reference<graphic::XGraphic> xGraphic;
1372         if (mAny.has<uno::Reference<awt::XBitmap>>())
1373         {
1374             uno::Reference<awt::XBitmap> xBitmap = mAny.get<uno::Reference<awt::XBitmap>>();
1375             if (xBitmap.is())
1376                 xGraphic.set(xBitmap, uno::UNO_QUERY);
1377         }
1378         else if (mAny.has<uno::Reference<graphic::XGraphic>>())
1379         {
1380             xGraphic = mAny.get<uno::Reference<graphic::XGraphic>>();
1381         }
1382 
1383         if (xGraphic.is())
1384         {
1385             bool bWriteMode = false;
1386             if (sURLPropName == "FillBitmap" || sURLPropName == "BackGraphic")
1387                 bWriteMode = true;
1388             WriteXGraphicBlipFill(rXPropSet, xGraphic, nXmlNamespace, bWriteMode);
1389         }
1390     }
1391 }
1392 
1393 void DrawingML::WriteXGraphicBlipFill(uno::Reference<beans::XPropertySet> const & rXPropSet,
1394                                       uno::Reference<graphic::XGraphic> const & rxGraphic,
1395                                       sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia)
1396 {
1397     if (!rxGraphic.is() )
1398         return;
1399 
1400     mpFS->startElementNS(nXmlNamespace , XML_blipFill, XML_rotWithShape, "0");
1401 
1402     WriteXGraphicBlip(rXPropSet, rxGraphic, bRelPathToMedia);
1403 
1404     if (bWriteMode)
1405     {
1406         WriteXGraphicBlipMode(rXPropSet, rxGraphic);
1407     }
1408     else if(GetProperty(rXPropSet, "FillBitmapStretch"))
1409     {
1410             bool bStretch = mAny.get<bool>();
1411 
1412             if (bStretch)
1413             {
1414                 WriteXGraphicStretch(rXPropSet, rxGraphic);
1415             }
1416     }
1417     mpFS->endElementNS(nXmlNamespace, XML_blipFill);
1418 }
1419 
1420 void DrawingML::WritePattFill( const Reference< XPropertySet >& rXPropSet )
1421 {
1422     if ( GetProperty( rXPropSet, "FillHatch" ) )
1423     {
1424         drawing::Hatch aHatch;
1425         mAny >>= aHatch;
1426         WritePattFill(rXPropSet, aHatch);
1427     }
1428 }
1429 
1430 void DrawingML::WritePattFill(const Reference<XPropertySet>& rXPropSet, const css::drawing::Hatch& rHatch)
1431 {
1432         mpFS->startElementNS(XML_a, XML_pattFill, XML_prst, GetHatchPattern(rHatch));
1433 
1434         mpFS->startElementNS(XML_a, XML_fgClr);
1435         WriteColor(::Color(rHatch.Color));
1436         mpFS->endElementNS( XML_a , XML_fgClr );
1437 
1438         ::Color nColor = COL_WHITE;
1439         sal_Int32 nAlpha  = 0;
1440 
1441         if ( GetProperty( rXPropSet, "FillBackground" ) )
1442         {
1443             bool isBackgroundFilled = false;
1444             mAny >>= isBackgroundFilled;
1445             if( isBackgroundFilled )
1446             {
1447                 nAlpha = MAX_PERCENT;
1448 
1449                 if( GetProperty( rXPropSet, "FillColor" ) )
1450                 {
1451                     mAny >>= nColor;
1452                 }
1453             }
1454         }
1455 
1456         mpFS->startElementNS(XML_a, XML_bgClr);
1457         WriteColor(nColor, nAlpha);
1458         mpFS->endElementNS( XML_a , XML_bgClr );
1459 
1460         mpFS->endElementNS( XML_a , XML_pattFill );
1461 }
1462 
1463 void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet, Size const & rOriginalSize, MapMode const & rMapMode)
1464 {
1465     if (GetProperty(rXPropSet, "GraphicCrop"))
1466     {
1467         Size aOriginalSize(rOriginalSize);
1468 
1469         // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over.
1470         if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
1471             aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM));
1472 
1473         css::text::GraphicCrop aGraphicCropStruct;
1474         mAny >>= aGraphicCropStruct;
1475 
1476         if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) )
1477         {
1478             mpFS->singleElementNS( XML_a, XML_srcRect,
1479                 XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())),
1480                 XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())),
1481                 XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())),
1482                 XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) );
1483         }
1484     }
1485 }
1486 
1487 void DrawingML::WriteSrcRectXGraphic(uno::Reference<beans::XPropertySet> const & rxPropertySet,
1488                                      uno::Reference<graphic::XGraphic> const & rxGraphic)
1489 {
1490     Graphic aGraphic(rxGraphic);
1491     Size aOriginalSize = aGraphic.GetPrefSize();
1492     const MapMode& rMapMode = aGraphic.GetPrefMapMode();
1493     WriteGraphicCropProperties(rxPropertySet, aOriginalSize, rMapMode);
1494 }
1495 
1496 void DrawingML::WriteXGraphicStretch(uno::Reference<beans::XPropertySet> const & rXPropSet,
1497                                      uno::Reference<graphic::XGraphic> const & rxGraphic)
1498 {
1499     mpFS->startElementNS(XML_a, XML_stretch);
1500 
1501     bool bCrop = false;
1502     if (GetProperty(rXPropSet, "GraphicCrop"))
1503     {
1504         css::text::GraphicCrop aGraphicCropStruct;
1505         mAny >>= aGraphicCropStruct;
1506 
1507         if ((0 != aGraphicCropStruct.Left)
1508          || (0 != aGraphicCropStruct.Top)
1509          || (0 != aGraphicCropStruct.Right)
1510          || (0 != aGraphicCropStruct.Bottom))
1511         {
1512             Graphic aGraphic(rxGraphic);
1513             Size aOriginalSize(aGraphic.GetPrefSize());
1514             mpFS->singleElementNS(XML_a, XML_fillRect,
1515                 XML_l, OString::number(((aGraphicCropStruct.Left)   * 100000) / aOriginalSize.Width()),
1516                 XML_t, OString::number(((aGraphicCropStruct.Top)    * 100000) / aOriginalSize.Height()),
1517                 XML_r, OString::number(((aGraphicCropStruct.Right)  * 100000) / aOriginalSize.Width()),
1518                 XML_b, OString::number(((aGraphicCropStruct.Bottom) * 100000) / aOriginalSize.Height()));
1519             bCrop = true;
1520         }
1521     }
1522 
1523     if (!bCrop)
1524     {
1525         mpFS->singleElementNS(XML_a, XML_fillRect);
1526     }
1527 
1528     mpFS->endElementNS(XML_a, XML_stretch);
1529 }
1530 
1531 void DrawingML::WriteTransformation(const tools::Rectangle& rRect,
1532         sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, sal_Int32 nRotation, bool bIsGroupShape)
1533 {
1534 
1535     mpFS->startElementNS( nXmlNamespace, XML_xfrm,
1536                           XML_flipH, bFlipH ? "1" : nullptr,
1537                           XML_flipV, bFlipV ? "1" : nullptr,
1538                           XML_rot, (nRotation % 21600000) ? OString::number(nRotation).getStr() : nullptr );
1539 
1540     sal_Int32 nLeft = rRect.Left();
1541     sal_Int32 nTop = rRect.Top();
1542     if (GetDocumentType() == DOCUMENT_DOCX && !m_xParent.is())
1543     {
1544         nLeft = 0;
1545         nTop = 0;
1546     }
1547 
1548     mpFS->singleElementNS(XML_a, XML_off,
1549         XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
1550         XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
1551     mpFS->singleElementNS(XML_a, XML_ext,
1552         XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1553         XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1554 
1555     if (GetDocumentType() != DOCUMENT_DOCX && bIsGroupShape)
1556     {
1557         mpFS->singleElementNS(XML_a, XML_chOff,
1558             XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
1559             XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
1560         mpFS->singleElementNS(XML_a, XML_chExt,
1561             XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1562             XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1563     }
1564 
1565     mpFS->endElementNS( nXmlNamespace, XML_xfrm );
1566 }
1567 
1568 void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, bool bSuppressRotation, bool bSuppressFlipping, bool bFlippedBeforeRotation )
1569 {
1570     SAL_INFO("oox.shape",  "write shape transformation");
1571 
1572     sal_Int32 nRotation=0;
1573     awt::Point aPos = rXShape->getPosition();
1574     awt::Size aSize = rXShape->getSize();
1575 
1576     bool bFlipHWrite = bFlipH && !bSuppressFlipping;
1577     bool bFlipVWrite = bFlipV && !bSuppressFlipping;
1578     bFlipH = bFlipH && !bFlippedBeforeRotation;
1579     bFlipV = bFlipV && !bFlippedBeforeRotation;
1580 
1581     if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
1582     {
1583         awt::Point aParentPos = m_xParent->getPosition();
1584         aPos.X -= aParentPos.X;
1585         aPos.Y -= aParentPos.Y;
1586     }
1587 
1588     if ( aSize.Width < 0 )
1589         aSize.Width = 1000;
1590     if ( aSize.Height < 0 )
1591         aSize.Height = 1000;
1592     if (!bSuppressRotation)
1593     {
1594         SdrObject* pShape = GetSdrObjectFromXShape( rXShape );
1595         nRotation = pShape ? pShape->GetRotateAngle() : 0;
1596         if ( nRotation != 0 && GetDocumentType() != DOCUMENT_DOCX )
1597         {
1598             int faccos=bFlipV ? -1 : 1;
1599             int facsin=bFlipH ? -1 : 1;
1600             aPos.X-=(1-faccos*cos(nRotation*F_PI18000))*aSize.Width/2-facsin*sin(nRotation*F_PI18000)*aSize.Height/2;
1601             aPos.Y-=(1-faccos*cos(nRotation*F_PI18000))*aSize.Height/2+facsin*sin(nRotation*F_PI18000)*aSize.Width/2;
1602         }
1603 
1604         // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
1605         uno::Reference<beans::XPropertySet> xPropertySet(rXShape, uno::UNO_QUERY);
1606         uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
1607         if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
1608             xPropertySet->getPropertyValue("RotateAngle") >>= nRotation;
1609     }
1610 
1611     // OOXML flips shapes before rotating them.
1612     if(bFlipH != bFlipV)
1613         nRotation = nRotation * -1 + 36000;
1614 
1615     WriteTransformation(tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)), nXmlNamespace,
1616             bFlipHWrite, bFlipVWrite, ExportRotateClockwisify(nRotation), IsGroupShape( rXShape ));
1617 }
1618 
1619 void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement, bool bCheckDirect,
1620                                     bool& rbOverridingCharHeight, sal_Int32& rnCharHeight )
1621 {
1622     Reference< XPropertySet > rXPropSet = rRun;
1623     Reference< XPropertyState > rXPropState( rRun, UNO_QUERY );
1624     OUString usLanguage;
1625     PropertyState eState;
1626     SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
1627     bool bComplex = ( nScriptType == SvtScriptType::COMPLEX );
1628     const char* bold = "0";
1629     const char* italic = nullptr;
1630     const char* underline = nullptr;
1631     const char* strikeout = nullptr;
1632     const char* cap = nullptr;
1633     sal_Int32 nSize = 1800;
1634     sal_Int32 nCharEscapement = 0;
1635     sal_Int32 nCharKerning = 0;
1636 
1637     if ( nElement == XML_endParaRPr && rbOverridingCharHeight )
1638     {
1639         nSize = rnCharHeight;
1640     }
1641     else if (GetProperty(rXPropSet, "CharHeight"))
1642     {
1643         nSize = static_cast<sal_Int32>(100*(*o3tl::doAccess<float>(mAny)));
1644         if ( nElement == XML_rPr )
1645         {
1646             rbOverridingCharHeight = true;
1647             rnCharHeight = nSize;
1648         }
1649     }
1650 
1651     if (GetProperty(rXPropSet, "CharKerning"))
1652         nCharKerning = static_cast<sal_Int32>(*o3tl::doAccess<sal_Int16>(mAny));
1653     /**  While setting values in propertymap,
1654     *    CharKerning converted using GetTextSpacingPoint
1655     *    i.e set @ https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/textcharacterproperties.cxx#129
1656     *    therefore to get original value CharKerning need to be convert.
1657     *    https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/drawingmltypes.cxx#95
1658     **/
1659     nCharKerning = ((nCharKerning * 720)-360) / 254;
1660 
1661     if ((bComplex && GetProperty(rXPropSet, "CharWeightComplex"))
1662         || GetProperty(rXPropSet, "CharWeight"))
1663     {
1664         if ( *o3tl::doAccess<float>(mAny) >= awt::FontWeight::SEMIBOLD )
1665             bold = "1";
1666     }
1667 
1668     if ((bComplex && GetProperty(rXPropSet, "CharPostureComplex"))
1669         || GetProperty(rXPropSet, "CharPosture"))
1670         switch ( *o3tl::doAccess<awt::FontSlant>(mAny) )
1671         {
1672             case awt::FontSlant_OBLIQUE :
1673             case awt::FontSlant_ITALIC :
1674                 italic = "1";
1675                 break;
1676             default:
1677                 break;
1678         }
1679 
1680     if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderline", eState)
1681          && eState == beans::PropertyState_DIRECT_VALUE)
1682         || GetProperty(rXPropSet, "CharUnderline"))
1683     {
1684         switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1685         {
1686             case awt::FontUnderline::SINGLE :
1687                 underline = "sng";
1688                 break;
1689             case awt::FontUnderline::DOUBLE :
1690                 underline = "dbl";
1691                 break;
1692             case awt::FontUnderline::DOTTED :
1693                 underline = "dotted";
1694                 break;
1695             case awt::FontUnderline::DASH :
1696                 underline = "dash";
1697                 break;
1698             case awt::FontUnderline::LONGDASH :
1699                 underline = "dashLong";
1700                 break;
1701             case awt::FontUnderline::DASHDOT :
1702                 underline = "dotDash";
1703                 break;
1704             case awt::FontUnderline::DASHDOTDOT :
1705                 underline = "dotDotDash";
1706                 break;
1707             case awt::FontUnderline::WAVE :
1708                 underline = "wavy";
1709                 break;
1710             case awt::FontUnderline::DOUBLEWAVE :
1711                 underline = "wavyDbl";
1712                 break;
1713             case awt::FontUnderline::BOLD :
1714                 underline = "heavy";
1715                 break;
1716             case awt::FontUnderline::BOLDDOTTED :
1717                 underline = "dottedHeavy";
1718                 break;
1719             case awt::FontUnderline::BOLDDASH :
1720                 underline = "dashHeavy";
1721                 break;
1722             case awt::FontUnderline::BOLDLONGDASH :
1723                 underline = "dashLongHeavy";
1724                 break;
1725             case awt::FontUnderline::BOLDDASHDOT :
1726                 underline = "dotDashHeavy";
1727                 break;
1728             case awt::FontUnderline::BOLDDASHDOTDOT :
1729                 underline = "dotDotDashHeavy";
1730                 break;
1731             case awt::FontUnderline::BOLDWAVE :
1732                 underline = "wavyHeavy";
1733                 break;
1734         }
1735     }
1736 
1737     if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharStrikeout", eState)
1738          && eState == beans::PropertyState_DIRECT_VALUE)
1739         || GetProperty(rXPropSet, "CharStrikeout"))
1740     {
1741         switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1742         {
1743             case awt::FontStrikeout::NONE :
1744                strikeout = "noStrike";
1745                break;
1746             case awt::FontStrikeout::SINGLE :
1747             // LibO supports further values of character
1748             // strikeout, OOXML standard (20.1.10.78,
1749             // ST_TextStrikeType) however specifies only
1750             // 3 - single, double and none. Approximate
1751             // the remaining ones by single strike (better
1752             // some strike than none at all).
1753             // TODO: figure out how to do this better
1754             case awt::FontStrikeout::BOLD :
1755             case awt::FontStrikeout::SLASH :
1756             case awt::FontStrikeout::X :
1757                strikeout = "sngStrike";
1758                break;
1759             case awt::FontStrikeout::DOUBLE :
1760                strikeout = "dblStrike";
1761                break;
1762         }
1763     }
1764 
1765     if (GetProperty(rXPropSet, "CharLocale"))
1766     {
1767         css::lang::Locale aLocale;
1768         mAny >>= aLocale;
1769         LanguageTag aLanguageTag( aLocale);
1770         if (!aLanguageTag.isSystemLocale())
1771             usLanguage = aLanguageTag.getBcp47MS();
1772     }
1773 
1774     if (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapement", eState)
1775         && eState == beans::PropertyState_DIRECT_VALUE)
1776         mAny >>= nCharEscapement;
1777 
1778     if (nCharEscapement
1779         && (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapementHeight", eState)
1780             && eState == beans::PropertyState_DIRECT_VALUE))
1781     {
1782         sal_uInt32 nCharEscapementHeight = 0;
1783         mAny >>= nCharEscapementHeight;
1784         nSize = (nSize * nCharEscapementHeight) / 100;
1785         // MSO uses default ~58% size
1786         nSize = (nSize / 0.58);
1787     }
1788 
1789     if (GetProperty(rXPropSet, "CharCaseMap"))
1790     {
1791         switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1792         {
1793             case CaseMap::UPPERCASE :
1794                 cap = "all";
1795                 break;
1796             case CaseMap::SMALLCAPS :
1797                 cap = "small";
1798                 break;
1799         }
1800     }
1801 
1802     mpFS->startElementNS( XML_a, nElement,
1803                           XML_b, bold,
1804                           XML_i, italic,
1805                           XML_lang, usLanguage.isEmpty() ? nullptr : usLanguage.toUtf8().getStr(),
1806                           XML_sz, OString::number(nSize),
1807             // For Condensed character spacing spc value is negative.
1808                           XML_spc, nCharKerning ? OString::number(nCharKerning).getStr() : nullptr,
1809                           XML_strike, strikeout,
1810                           XML_u, underline,
1811                           XML_baseline, nCharEscapement == 0 ? nullptr : OString::number(nCharEscapement*1000).getStr(),
1812                           XML_cap, cap );
1813 
1814     // mso doesn't like text color to be placed after typeface
1815     if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState)
1816          && eState == beans::PropertyState_DIRECT_VALUE)
1817         || GetProperty(rXPropSet, "CharColor"))
1818     {
1819         ::Color color( *o3tl::doAccess<sal_uInt32>(mAny) );
1820         SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO));
1821 
1822         // tdf#104219 In LibreOffice and MS Office, there are two types of colors:
1823         // Automatic and Fixed. OOXML is setting automatic color, by not providing color.
1824         if( color != COL_AUTO )
1825         {
1826             color.SetTransparency(0);
1827             // TODO: special handle embossed/engraved
1828             WriteSolidFill( color );
1829         }
1830     }
1831 
1832     if (underline
1833         && ((bCheckDirect
1834              && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderlineColor", eState)
1835              && eState == beans::PropertyState_DIRECT_VALUE)
1836             || GetProperty(rXPropSet, "CharUnderlineColor")))
1837     {
1838         ::Color color(*o3tl::doAccess<sal_uInt32>(mAny));
1839         // if color is automatic, then we shouldn't write information about color but to take color from character
1840         if( color != COL_AUTO )
1841         {
1842             mpFS->startElementNS(XML_a, XML_uFill);
1843             WriteSolidFill( color );
1844             mpFS->endElementNS( XML_a, XML_uFill );
1845         }
1846         else
1847         {
1848             mpFS->singleElementNS(XML_a, XML_uFillTx);
1849         }
1850     }
1851 
1852     if (GetProperty(rXPropSet, "CharFontName"))
1853     {
1854         const char* const pitch = nullptr;
1855         const char* const charset = nullptr;
1856         OUString usTypeface;
1857 
1858         mAny >>= usTypeface;
1859         OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
1860         if (!aSubstName.isEmpty())
1861             usTypeface = aSubstName;
1862 
1863         mpFS->singleElementNS( XML_a, XML_latin,
1864                                XML_typeface, usTypeface.toUtf8(),
1865                                XML_pitchFamily, pitch,
1866                                XML_charset, charset );
1867     }
1868 
1869     if ((bComplex
1870          && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameComplex", eState)
1871              && eState == beans::PropertyState_DIRECT_VALUE))
1872         || (!bComplex
1873             && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameAsian", eState)
1874                 && eState == beans::PropertyState_DIRECT_VALUE)))
1875     {
1876         const char* const pitch = nullptr;
1877         const char* const charset = nullptr;
1878         OUString usTypeface;
1879 
1880         mAny >>= usTypeface;
1881         OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
1882         if (!aSubstName.isEmpty())
1883             usTypeface = aSubstName;
1884 
1885         mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea,
1886                                XML_typeface, usTypeface.toUtf8(),
1887                                XML_pitchFamily, pitch,
1888                                XML_charset, charset );
1889     }
1890 
1891     if( bIsField )
1892     {
1893         Reference< XTextField > rXTextField;
1894         if (GetProperty(rXPropSet, "TextField"))
1895             mAny >>= rXTextField;
1896         if( rXTextField.is() )
1897             rXPropSet.set( rXTextField, UNO_QUERY );
1898     }
1899 
1900     // field properties starts here
1901     if (GetProperty(rXPropSet, "URL"))
1902     {
1903         OUString sURL;
1904 
1905         mAny >>= sURL;
1906         if( !sURL.isEmpty() ) {
1907             OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1908                                   oox::getRelationship(Relationship::HYPERLINK),
1909                                   sURL, true );
1910 
1911             mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8());
1912         }
1913     }
1914 
1915     mpFS->endElementNS( XML_a, nElement );
1916 }
1917 
1918 OUString DrawingML::GetFieldValue( const css::uno::Reference< css::text::XTextRange >& rRun, bool& bIsURLField )
1919 {
1920     Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
1921     OUString aFieldType, aFieldValue;
1922 
1923     if (GetProperty(rXPropSet, "TextPortionType"))
1924     {
1925         aFieldType = *o3tl::doAccess<OUString>(mAny);
1926         SAL_INFO("oox.shape", "field type: " << aFieldType);
1927     }
1928 
1929     if( aFieldType == "TextField" )
1930     {
1931         Reference< XTextField > rXTextField;
1932         if (GetProperty(rXPropSet, "TextField"))
1933             mAny >>= rXTextField;
1934         if( rXTextField.is() )
1935         {
1936             rXPropSet.set( rXTextField, UNO_QUERY );
1937             if( rXPropSet.is() )
1938             {
1939                 OUString aFieldKind( rXTextField->getPresentation( true ) );
1940                 SAL_INFO("oox.shape", "field kind: " << aFieldKind);
1941                 if( aFieldKind == "Page" )
1942                 {
1943                     aFieldValue = "slidenum";
1944                 }
1945                 else if( aFieldKind == "Pages" )
1946                 {
1947                     aFieldValue = "slidecount";
1948                 }
1949                 else if( aFieldKind == "PageName" )
1950                 {
1951                     aFieldValue = "slidename";
1952                 }
1953                 else if( aFieldKind == "URL" )
1954                 {
1955                     bIsURLField = true;
1956                     if (GetProperty(rXPropSet, "Representation"))
1957                         mAny >>= aFieldValue;
1958 
1959                 }
1960                 else if(aFieldKind == "Date")
1961                 {
1962                     sal_Int32 nNumFmt = -1;
1963                     rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
1964                     switch(static_cast<SvxDateFormat>(nNumFmt))
1965                     {
1966                         case SvxDateFormat::StdSmall:
1967                         case SvxDateFormat::A: aFieldValue = "datetime"; // 13/02/96
1968                                               break;
1969                         case SvxDateFormat::B: aFieldValue = "datetime1"; // 13/02/1996
1970                                               break;
1971                         case SvxDateFormat::StdBig:
1972                         case SvxDateFormat::D: aFieldValue = "datetime3"; // 13 February 1996
1973                                               break;
1974                         default: break;
1975                     }
1976                 }
1977                 else if(aFieldKind == "ExtTime")
1978                 {
1979                     sal_Int32 nNumFmt = -1;
1980                     rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
1981                     switch(static_cast<SvxTimeFormat>(nNumFmt))
1982                     {
1983                         case SvxTimeFormat::Standard:
1984                         case SvxTimeFormat::HH24_MM_SS:
1985                             aFieldValue = "datetime11"; // 13:49:38
1986                             break;
1987                         case SvxTimeFormat::HH24_MM:
1988                             aFieldValue = "datetime10"; // 13:49
1989                             break;
1990                         case SvxTimeFormat::HH12_MM:
1991                             aFieldValue = "datetime12"; // 01:49 PM
1992                             break;
1993                         case SvxTimeFormat::HH12_MM_SS:
1994                             aFieldValue = "datetime13"; // 01:49:38 PM
1995                             break;
1996                         default: break;
1997                     }
1998                 }
1999                 else if(aFieldKind == "ExtFile")
2000                 {
2001                     sal_Int32 nNumFmt = -1;
2002                     rXPropSet->getPropertyValue(UNO_TC_PROP_FILE_FORMAT) >>= nNumFmt;
2003                     switch(nNumFmt)
2004                     {
2005                         case 0: aFieldValue = "file"; // Path/File name
2006                                 break;
2007                         case 1: aFieldValue = "file1"; // Path
2008                                 break;
2009                         case 2: aFieldValue = "file2"; // File name without extension
2010                                 break;
2011                         case 3: aFieldValue = "file3"; // File name with extension
2012                     }
2013                 }
2014                 else if(aFieldKind == "Author")
2015                 {
2016                     aFieldValue = "author";
2017                 }
2018             }
2019         }
2020     }
2021     return aFieldValue;
2022 }
2023 
2024 void DrawingML::WriteRun( const Reference< XTextRange >& rRun,
2025                           bool& rbOverridingCharHeight, sal_Int32& rnCharHeight)
2026 {
2027     Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
2028     sal_Int16 nLevel = -1;
2029     if (GetProperty(rXPropSet, "NumberingLevel"))
2030         mAny >>= nLevel;
2031 
2032     bool bNumberingIsNumber = true;
2033     if (GetProperty(rXPropSet, "NumberingIsNumber"))
2034         mAny >>= bNumberingIsNumber;
2035 
2036     bool bIsURLField = false;
2037     OUString sFieldValue = GetFieldValue( rRun, bIsURLField );
2038     bool bWriteField  = !( sFieldValue.isEmpty() || bIsURLField );
2039 
2040     OUString sText = rRun->getString();
2041 
2042     //if there is no text following the bullet, add a space after the bullet
2043     if (nLevel !=-1 && bNumberingIsNumber && sText.isEmpty() )
2044          sText=" ";
2045 
2046     if ( bIsURLField )
2047         sText = sFieldValue;
2048 
2049     if( sText.isEmpty())
2050     {
2051         Reference< XPropertySet > xPropSet( rRun, UNO_QUERY );
2052 
2053         try
2054         {
2055             if( !xPropSet.is() || !( xPropSet->getPropertyValue( "PlaceholderText" ) >>= sText ) )
2056                 return;
2057             if( sText.isEmpty() )
2058                 return;
2059         }
2060         catch (const Exception &)
2061         {
2062             return;
2063         }
2064     }
2065 
2066     if (sText == "\n")
2067     {
2068         mpFS->singleElementNS(XML_a, XML_br);
2069     }
2070     else
2071     {
2072         if( bWriteField )
2073         {
2074             OString sUUID(comphelper::xml::generateGUIDString());
2075             mpFS->startElementNS( XML_a, XML_fld,
2076                                   XML_id, sUUID.getStr(),
2077                                   XML_type, sFieldValue.toUtf8() );
2078         }
2079         else
2080         {
2081             mpFS->startElementNS(XML_a, XML_r);
2082         }
2083 
2084         Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY );
2085         WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight );
2086         mpFS->startElementNS(XML_a, XML_t);
2087         mpFS->writeEscaped( sText );
2088         mpFS->endElementNS( XML_a, XML_t );
2089 
2090         if( bWriteField )
2091             mpFS->endElementNS( XML_a, XML_fld );
2092         else
2093             mpFS->endElementNS( XML_a, XML_r );
2094     }
2095 }
2096 
2097 static OUString GetAutoNumType(SvxNumType nNumberingType, bool bSDot, bool bPBehind, bool bPBoth)
2098 {
2099     OUString sPrefixSuffix;
2100 
2101     if (bPBoth)
2102         sPrefixSuffix = "ParenBoth";
2103     else if (bPBehind)
2104         sPrefixSuffix = "ParenR";
2105     else if (bSDot)
2106         sPrefixSuffix = "Period";
2107 
2108     switch( nNumberingType )
2109     {
2110         case SVX_NUM_CHARS_UPPER_LETTER_N :
2111         case SVX_NUM_CHARS_UPPER_LETTER :
2112             return "alphaUc" + sPrefixSuffix;
2113 
2114         case SVX_NUM_CHARS_LOWER_LETTER_N :
2115         case SVX_NUM_CHARS_LOWER_LETTER :
2116             return "alphaLc" + sPrefixSuffix;
2117 
2118         case SVX_NUM_ROMAN_UPPER :
2119             return "romanUc" + sPrefixSuffix;
2120 
2121         case SVX_NUM_ROMAN_LOWER :
2122             return "romanLc" + sPrefixSuffix;
2123 
2124         case SVX_NUM_ARABIC :
2125         {
2126             if (sPrefixSuffix.isEmpty())
2127                 return "arabicPlain";
2128             else
2129                 return "arabic" + sPrefixSuffix;
2130         }
2131         default:
2132             break;
2133     }
2134 
2135     return OUString();
2136 }
2137 
2138 void DrawingML::WriteParagraphNumbering(const Reference< XPropertySet >& rXPropSet, float fFirstCharHeight, sal_Int16 nLevel )
2139 {
2140     if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2141         return;
2142 
2143     Reference< XIndexAccess > rXIndexAccess;
2144 
2145     if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2146         return;
2147 
2148     SAL_INFO("oox.shape", "numbering rules");
2149 
2150     Sequence<PropertyValue> aPropertySequence;
2151     rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2152 
2153     if (!aPropertySequence.hasElements())
2154         return;
2155 
2156     sal_Int32 nPropertyCount = aPropertySequence.getLength();
2157 
2158     const PropertyValue* pPropValue = aPropertySequence.getArray();
2159 
2160     SvxNumType nNumberingType = SVX_NUM_NUMBER_NONE;
2161     bool bSDot = false;
2162     bool bPBehind = false;
2163     bool bPBoth = false;
2164     sal_Unicode aBulletChar = 0x2022; // a bullet
2165     awt::FontDescriptor aFontDesc;
2166     bool bHasFontDesc = false;
2167     uno::Reference<graphic::XGraphic> xGraphic;
2168     sal_Int16 nBulletRelSize = 0;
2169     sal_Int16 nStartWith = 1;
2170     ::Color nBulletColor;
2171     bool bHasBulletColor = false;
2172     awt::Size aGraphicSize;
2173 
2174     for ( sal_Int32 i = 0; i < nPropertyCount; i++ )
2175     {
2176         OUString aPropName( pPropValue[ i ].Name );
2177         SAL_INFO("oox.shape", "pro name: " << aPropName);
2178         if ( aPropName == "NumberingType" )
2179         {
2180             nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(pPropValue[i].Value));
2181         }
2182         else if ( aPropName == "Prefix" )
2183         {
2184             if( *o3tl::doAccess<OUString>(pPropValue[i].Value) == ")")
2185                 bPBoth = true;
2186         }
2187         else if ( aPropName == "Suffix" )
2188         {
2189             auto s = o3tl::doAccess<OUString>(pPropValue[i].Value);
2190             if( *s == ".")
2191                 bSDot = true;
2192             else if( *s == ")")
2193                 bPBehind = true;
2194         }
2195         else if(aPropName == "BulletColor")
2196         {
2197             nBulletColor = ::Color(*o3tl::doAccess<sal_uInt32>(pPropValue[i].Value));
2198             bHasBulletColor = true;
2199         }
2200         else if ( aPropName == "BulletChar" )
2201         {
2202             aBulletChar = (*o3tl::doAccess<OUString>(pPropValue[i].Value))[ 0 ];
2203         }
2204         else if ( aPropName == "BulletFont" )
2205         {
2206             aFontDesc = *o3tl::doAccess<awt::FontDescriptor>(pPropValue[i].Value);
2207             bHasFontDesc = true;
2208 
2209             // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font,
2210             // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used.
2211             // Because there might exist a lot of damaged documemts I added this two lines
2212             // which fixes the bullet problem for the export.
2213             if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") )
2214                 aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252;
2215 
2216         }
2217         else if ( aPropName == "BulletRelSize" )
2218         {
2219             nBulletRelSize = *o3tl::doAccess<sal_Int16>(pPropValue[i].Value);
2220         }
2221         else if ( aPropName == "StartWith" )
2222         {
2223             nStartWith = *o3tl::doAccess<sal_Int16>(pPropValue[i].Value);
2224         }
2225         else if (aPropName == "GraphicBitmap")
2226         {
2227             auto xBitmap = pPropValue[i].Value.get<uno::Reference<awt::XBitmap>>();
2228             xGraphic.set(xBitmap, uno::UNO_QUERY);
2229         }
2230         else if ( aPropName == "GraphicSize" )
2231         {
2232             aGraphicSize = *o3tl::doAccess<awt::Size>(pPropValue[i].Value);
2233             SAL_INFO("oox.shape", "graphic size: " << aGraphicSize.Width << "x" << aGraphicSize.Height);
2234         }
2235     }
2236 
2237     if (nNumberingType == SVX_NUM_NUMBER_NONE)
2238         return;
2239 
2240     Graphic aGraphic(xGraphic);
2241     if (xGraphic.is() && aGraphic.GetType() != GraphicType::NONE)
2242     {
2243         long nFirstCharHeightMm = TransformMetric(fFirstCharHeight * 100.f, FieldUnit::POINT, FieldUnit::MM);
2244         float fBulletSizeRel = aGraphicSize.Height / static_cast<float>(nFirstCharHeightMm) / OOX_BULLET_LIST_SCALE_FACTOR;
2245 
2246         OUString sRelationId;
2247 
2248         if (fBulletSizeRel < 1.0f)
2249         {
2250             // Add padding to get the bullet point centered in PPT
2251             Size aDestSize(64, 64);
2252             float fBulletSizeRelX = fBulletSizeRel / aGraphicSize.Height * aGraphicSize.Width;
2253             long nPaddingX = std::max<long>(0, std::lround((aDestSize.Width() - fBulletSizeRelX * aDestSize.Width()) / 2.f));
2254             long nPaddingY = std::lround((aDestSize.Height() - fBulletSizeRel * aDestSize.Height()) / 2.f);
2255             tools::Rectangle aDestRect(nPaddingX, nPaddingY, aDestSize.Width() - nPaddingX, aDestSize.Height() - nPaddingY);
2256 
2257             BitmapEx aSourceBitmap(aGraphic.GetBitmapEx());
2258             aSourceBitmap.Scale(aDestRect.GetSize());
2259             tools::Rectangle aSourceRect(Point(0, 0), aDestRect.GetSize());
2260             BitmapEx aDestBitmap(aDestSize, 24);
2261             aDestBitmap.CopyPixel(aDestRect, aSourceRect, &aSourceBitmap);
2262             Graphic aDestGraphic(aDestBitmap);
2263             sRelationId = WriteImage(aDestGraphic);
2264             fBulletSizeRel = 1.0f;
2265         }
2266         else
2267         {
2268             sRelationId = WriteImage(aGraphic);
2269         }
2270 
2271         mpFS->singleElementNS( XML_a, XML_buSzPct,
2272                                XML_val, OString::number(std::min<sal_Int32>(std::lround(100000.f * fBulletSizeRel), 400000)));
2273         mpFS->startElementNS(XML_a, XML_buBlip);
2274         mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelationId.toUtf8());
2275         mpFS->endElementNS( XML_a, XML_buBlip );
2276     }
2277     else
2278     {
2279         if(bHasBulletColor)
2280         {
2281                if (nBulletColor == COL_AUTO )
2282                {
2283                    nBulletColor = ::Color(mbIsBackgroundDark ? 0xffffff : 0x000000);
2284                }
2285                mpFS->startElementNS(XML_a, XML_buClr);
2286                WriteColor( nBulletColor );
2287                mpFS->endElementNS( XML_a, XML_buClr );
2288         }
2289 
2290         if( nBulletRelSize && nBulletRelSize != 100 )
2291             mpFS->singleElementNS( XML_a, XML_buSzPct,
2292                                    XML_val, OString::number(std::clamp<sal_Int32>(1000*nBulletRelSize, 25000, 400000)));
2293         if( bHasFontDesc )
2294         {
2295             if ( SVX_NUM_CHAR_SPECIAL == nNumberingType )
2296                 aBulletChar = SubstituteBullet( aBulletChar, aFontDesc );
2297             mpFS->singleElementNS( XML_a, XML_buFont,
2298                                    XML_typeface, aFontDesc.Name.toUtf8(),
2299                                    XML_charset, (aFontDesc.CharSet == awt::CharSet::SYMBOL) ? "2" : nullptr );
2300         }
2301 
2302         OUString aAutoNumType = GetAutoNumType( nNumberingType, bSDot, bPBehind, bPBoth );
2303 
2304         if (!aAutoNumType.isEmpty())
2305         {
2306             mpFS->singleElementNS(XML_a, XML_buAutoNum,
2307                                   XML_type, aAutoNumType.toUtf8(),
2308                                   XML_startAt, nStartWith > 1 ? OString::number(nStartWith).getStr() : nullptr);
2309         }
2310         else
2311         {
2312             mpFS->singleElementNS(XML_a, XML_buChar, XML_char, OUString(aBulletChar).toUtf8());
2313         }
2314     }
2315 }
2316 
2317 bool DrawingML::IsGroupShape( const Reference< XShape >& rXShape )
2318 {
2319     bool bRet = false;
2320     if ( rXShape.is() )
2321     {
2322         uno::Reference<lang::XServiceInfo> xServiceInfo(rXShape, uno::UNO_QUERY_THROW);
2323         bRet = xServiceInfo->supportsService("com.sun.star.drawing.GroupShape");
2324     }
2325     return bRet;
2326 }
2327 
2328 bool DrawingML::IsDiagram(const Reference<XShape>& rXShape)
2329 {
2330     uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
2331     if (!xPropSet.is())
2332         return false;
2333 
2334     // if the shape doesn't have the InteropGrabBag property, it's not a diagram
2335     uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
2336     OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
2337     if (!xPropSetInfo->hasPropertyByName(aName))
2338         return false;
2339 
2340     uno::Sequence<beans::PropertyValue> propList;
2341     xPropSet->getPropertyValue(aName) >>= propList;
2342     for (sal_Int32 nProp = 0; nProp < propList.getLength(); ++nProp)
2343     {
2344         // if we find any of the diagram components, it's a diagram
2345         OUString propName = propList[nProp].Name;
2346         if (propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle"
2347             || propName == "OOXColor" || propName == "OOXDrawing")
2348             return true;
2349     }
2350     return false;
2351 }
2352 
2353 sal_Int32 DrawingML::getBulletMarginIndentation (const Reference< XPropertySet >& rXPropSet,sal_Int16 nLevel, const OUString& propName)
2354 {
2355     if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2356         return 0;
2357 
2358     Reference< XIndexAccess > rXIndexAccess;
2359 
2360     if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2361         return 0;
2362 
2363     SAL_INFO("oox.shape", "numbering rules");
2364 
2365     Sequence<PropertyValue> aPropertySequence;
2366     rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2367 
2368     if (!aPropertySequence.hasElements())
2369         return 0;
2370 
2371     sal_Int32 nPropertyCount = aPropertySequence.getLength();
2372 
2373     const PropertyValue* pPropValue = aPropertySequence.getArray();
2374 
2375     for ( sal_Int32 i = 0; i < nPropertyCount; i++ )
2376     {
2377         OUString aPropName( pPropValue[ i ].Name );
2378         SAL_INFO("oox.shape", "pro name: " << aPropName);
2379         if ( aPropName == propName )
2380             return *o3tl::doAccess<sal_Int32>(pPropValue[i].Value);
2381     }
2382 
2383     return 0;
2384 }
2385 
2386 const char* DrawingML::GetAlignment( style::ParagraphAdjust nAlignment )
2387 {
2388     const char* sAlignment = nullptr;
2389 
2390     switch( nAlignment )
2391     {
2392         case style::ParagraphAdjust_CENTER:
2393             sAlignment = "ctr";
2394             break;
2395         case style::ParagraphAdjust_RIGHT:
2396             sAlignment = "r";
2397             break;
2398         case style::ParagraphAdjust_BLOCK:
2399             sAlignment = "just";
2400             break;
2401         default:
2402             ;
2403     }
2404 
2405     return sAlignment;
2406 }
2407 
2408 void DrawingML::WriteLinespacing( const LineSpacing& rSpacing )
2409 {
2410     if( rSpacing.Mode == LineSpacingMode::PROP )
2411     {
2412         mpFS->singleElementNS( XML_a, XML_spcPct,
2413                                XML_val, OString::number(static_cast<sal_Int32>(rSpacing.Height)*1000));
2414     }
2415     else
2416     {
2417         mpFS->singleElementNS( XML_a, XML_spcPts,
2418                                XML_val, OString::number(std::lround(rSpacing.Height / 25.4 * 72)));
2419     }
2420 }
2421 
2422 void DrawingML::WriteParagraphProperties( const Reference< XTextContent >& rParagraph, float fFirstCharHeight)
2423 {
2424     Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
2425     Reference< XPropertyState > rXPropState( rParagraph, UNO_QUERY );
2426     PropertyState eState;
2427 
2428     if( !rXPropSet.is() || !rXPropState.is() )
2429         return;
2430 
2431     sal_Int16 nLevel = -1;
2432     if (GetProperty(rXPropSet, "NumberingLevel"))
2433         mAny >>= nLevel;
2434 
2435     sal_Int16 nTmp = sal_Int16(style::ParagraphAdjust_LEFT);
2436     if (GetProperty(rXPropSet, "ParaAdjust"))
2437         mAny >>= nTmp;
2438     style::ParagraphAdjust nAlignment = static_cast<style::ParagraphAdjust>(nTmp);
2439 
2440     bool bHasLinespacing = false;
2441     LineSpacing aLineSpacing;
2442     if (GetPropertyAndState(rXPropSet, rXPropState, "ParaLineSpacing", eState)
2443         && eState == beans::PropertyState_DIRECT_VALUE)
2444         bHasLinespacing = ( mAny >>= aLineSpacing );
2445 
2446     bool bRtl = false;
2447     if (GetProperty(rXPropSet, "WritingMode"))
2448     {
2449         sal_Int16 nWritingMode;
2450         if( ( mAny >>= nWritingMode ) && nWritingMode == text::WritingMode2::RL_TB )
2451         {
2452             bRtl = true;
2453         }
2454     }
2455 
2456     sal_Int32 nParaLeftMargin = 0;
2457     sal_Int32 nParaFirstLineIndent = 0;
2458 
2459     if (GetProperty(rXPropSet, "ParaLeftMargin"))
2460         mAny >>= nParaLeftMargin;
2461     if (GetProperty(rXPropSet, "ParaFirstLineIndent"))
2462         mAny >>= nParaFirstLineIndent;
2463 
2464     sal_Int32 nParaTopMargin = 0;
2465     sal_Int32 nParaBottomMargin = 0;
2466 
2467     if (GetProperty(rXPropSet, "ParaTopMargin"))
2468         mAny >>= nParaTopMargin;
2469     if (GetProperty(rXPropSet, "ParaBottomMargin"))
2470         mAny >>= nParaBottomMargin;
2471 
2472     sal_Int32 nLeftMargin =  getBulletMarginIndentation ( rXPropSet, nLevel,"LeftMargin");
2473     sal_Int32 nLineIndentation = getBulletMarginIndentation ( rXPropSet, nLevel,"FirstLineOffset");
2474 
2475     if( nLevel != -1
2476         || nAlignment != style::ParagraphAdjust_LEFT
2477         || bHasLinespacing )
2478     {
2479         if (nParaLeftMargin) // For Paragraph
2480             mpFS->startElementNS( XML_a, XML_pPr,
2481                                XML_lvl, nLevel > 0 ? OString::number(nLevel).getStr() : nullptr,
2482                                XML_marL, nParaLeftMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nParaLeftMargin)).getStr() : nullptr,
2483                                XML_indent, nParaFirstLineIndent ? OString::number(oox::drawingml::convertHmmToEmu(nParaFirstLineIndent)).getStr() : nullptr,
2484                                XML_algn, GetAlignment( nAlignment ),
2485                                XML_rtl, bRtl ? ToPsz10(bRtl) : nullptr );
2486         else
2487             mpFS->startElementNS( XML_a, XML_pPr,
2488                                XML_lvl, nLevel > 0 ? OString::number(nLevel).getStr() : nullptr,
2489                                XML_marL, nLeftMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)).getStr() : nullptr,
2490                                XML_indent, nLineIndentation ? OString::number(oox::drawingml::convertHmmToEmu(nLineIndentation)).getStr() : nullptr,
2491                                XML_algn, GetAlignment( nAlignment ),
2492                                XML_rtl, bRtl ? ToPsz10(bRtl) : nullptr );
2493 
2494 
2495         if( bHasLinespacing )
2496         {
2497             mpFS->startElementNS(XML_a, XML_lnSpc);
2498             WriteLinespacing( aLineSpacing );
2499             mpFS->endElementNS( XML_a, XML_lnSpc );
2500         }
2501 
2502         if( nParaTopMargin != 0 )
2503         {
2504             mpFS->startElementNS(XML_a, XML_spcBef);
2505             {
2506                 mpFS->singleElementNS( XML_a, XML_spcPts,
2507                                        XML_val, OString::number(std::lround(nParaTopMargin / 25.4 * 72)));
2508             }
2509             mpFS->endElementNS( XML_a, XML_spcBef );
2510         }
2511 
2512         if( nParaBottomMargin != 0 )
2513         {
2514             mpFS->startElementNS(XML_a, XML_spcAft);
2515             {
2516                 mpFS->singleElementNS( XML_a, XML_spcPts,
2517                                        XML_val, OString::number(std::lround(nParaBottomMargin / 25.4 * 72)));
2518             }
2519             mpFS->endElementNS( XML_a, XML_spcAft );
2520         }
2521 
2522         WriteParagraphNumbering( rXPropSet, fFirstCharHeight, nLevel );
2523 
2524         mpFS->endElementNS( XML_a, XML_pPr );
2525     }
2526 }
2527 
2528 void DrawingML::WriteParagraph( const Reference< XTextContent >& rParagraph,
2529                                 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight )
2530 {
2531     Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY );
2532     if( !access.is() )
2533         return;
2534 
2535     Reference< XEnumeration > enumeration( access->createEnumeration() );
2536     if( !enumeration.is() )
2537         return;
2538 
2539     mpFS->startElementNS(XML_a, XML_p);
2540 
2541     bool bPropertiesWritten = false;
2542     while( enumeration->hasMoreElements() )
2543     {
2544         Reference< XTextRange > run;
2545         Any any ( enumeration->nextElement() );
2546 
2547         if (any >>= run)
2548         {
2549             if( !bPropertiesWritten )
2550             {
2551                 float fFirstCharHeight = rnCharHeight / 1000.;
2552                 Reference< XPropertySet > xFirstRunPropSet (run, UNO_QUERY);
2553                 Reference< XPropertySetInfo > xFirstRunPropSetInfo = xFirstRunPropSet->getPropertySetInfo();
2554                 if( xFirstRunPropSetInfo->hasPropertyByName("CharHeight") )
2555                     fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>();
2556                 WriteParagraphProperties( rParagraph, fFirstCharHeight );
2557                 bPropertiesWritten = true;
2558             }
2559             WriteRun( run, rbOverridingCharHeight, rnCharHeight );
2560         }
2561     }
2562     Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
2563     WriteRunProperties( rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight, rnCharHeight );
2564 
2565     mpFS->endElementNS( XML_a, XML_p );
2566 }
2567 
2568 void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUString& presetWarp, bool bBodyPr, bool bText, sal_Int32 nXmlNamespace )
2569 {
2570     Reference< XText > xXText( rXIface, UNO_QUERY );
2571     Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY );
2572 
2573     if( !xXText.is() )
2574         return;
2575 
2576     sal_Int32 nTextRotateAngle = 0;
2577     bool bIsFontworkShape(presetWarp.startsWith("text") && (presetWarp != "textNoShape"));
2578 
2579 #define DEFLRINS 254
2580 #define DEFTBINS 127
2581     sal_Int32 nLeft, nRight, nTop, nBottom;
2582     nLeft = nRight = DEFLRINS;
2583     nTop = nBottom = DEFTBINS;
2584 
2585     // top inset looks a bit different compared to ppt export
2586     // check if something related doesn't work as expected
2587     if (GetProperty(rXPropSet, "TextLeftDistance"))
2588         mAny >>= nLeft;
2589     if (GetProperty(rXPropSet, "TextRightDistance"))
2590         mAny >>= nRight;
2591     if (GetProperty(rXPropSet, "TextUpperDistance"))
2592         mAny >>= nTop;
2593     if (GetProperty(rXPropSet, "TextLowerDistance"))
2594         mAny >>= nBottom;
2595 
2596     TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP );
2597     const char* sVerticalAlignment = nullptr;
2598     if (GetProperty(rXPropSet, "TextVerticalAdjust"))
2599         mAny >>= eVerticalAlignment;
2600     if( eVerticalAlignment != TextVerticalAdjust_TOP )
2601         sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment);
2602 
2603     const char* sWritingMode = nullptr;
2604     bool bVertical = false;
2605     if (GetProperty(rXPropSet, "TextWritingMode"))
2606     {
2607         WritingMode eMode;
2608 
2609         if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL )
2610         {
2611             sWritingMode = "vert";
2612             bVertical = true;
2613         }
2614     }
2615 
2616     Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq;
2617     uno::Sequence<beans::PropertyValue> aTextPathSeq;
2618     bool bScaleX(false);
2619 
2620     if (GetProperty(rXPropSet, "CustomShapeGeometry"))
2621     {
2622         Sequence< PropertyValue > aProps;
2623         if ( mAny >>= aProps )
2624         {
2625             for ( sal_Int32 i = 0, nElems = aProps.getLength(); i < nElems; ++i )
2626             {
2627                 if ( aProps[ i ].Name == "TextPreRotateAngle" && ( aProps[ i ].Value >>= nTextRotateAngle ) )
2628                 {
2629                     if ( nTextRotateAngle == -90 )
2630                     {
2631                         sWritingMode = "vert";
2632                         bVertical = true;
2633                     }
2634                     else if ( nTextRotateAngle == -270 )
2635                     {
2636                         sWritingMode = "vert270";
2637                         bVertical = true;
2638                     }
2639                     if (!bIsFontworkShape)
2640                         break;
2641                 }
2642                 else if (aProps[i].Name == "AdjustmentValues")
2643                     aProps[i].Value >>= aAdjustmentSeq;
2644                 else if (aProps[i].Name == "TextPath")
2645                 {
2646                     aProps[i].Value >>= aTextPathSeq;
2647                     for (int k = 0; k < aTextPathSeq.getLength(); k++)
2648                     {
2649                         if (aTextPathSeq[k].Name == "ScaleX")
2650                             aTextPathSeq[k].Value >>= bScaleX;
2651                     }
2652                 }
2653             }
2654         }
2655     }
2656 
2657     bool bFromWordArt = !bScaleX
2658                         && ( presetWarp == "textArchDown" || presetWarp == "textArchUp"
2659                             || presetWarp == "textButton" || presetWarp == "textCircle");
2660 
2661     TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER );
2662     bool bHorizontalCenter = false;
2663     if (GetProperty(rXPropSet, "TextHorizontalAdjust"))
2664         mAny >>= eHorizontalAlignment;
2665     if( eHorizontalAlignment == TextHorizontalAdjust_CENTER )
2666         bHorizontalCenter = true;
2667     else if( bVertical && eHorizontalAlignment == TextHorizontalAdjust_LEFT )
2668         sVerticalAlignment = "b";
2669 
2670     bool bHasWrap = false;
2671     bool bWrap = false;
2672     // Only custom shapes obey the TextWordWrap option, normal text always wraps.
2673     if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextWordWrap"))
2674     {
2675         mAny >>= bWrap;
2676         bHasWrap = true;
2677     }
2678 
2679     if (bBodyPr)
2680     {
2681         const char* pWrap = bHasWrap && !bWrap ? "none" : nullptr;
2682         if (GetDocumentType() == DOCUMENT_DOCX)
2683         {
2684             // In case of DOCX, if we want to have the same effect as
2685             // TextShape's automatic word wrapping, then we need to set
2686             // wrapping to square.
2687             uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY);
2688             if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.TextShape"))
2689                 pWrap = "square";
2690         }
2691         mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr,
2692                                XML_wrap, pWrap,
2693                                XML_fromWordArt, bFromWordArt ? "1" : nullptr,
2694                                XML_lIns, (nLeft != DEFLRINS) ? OString::number(oox::drawingml::convertHmmToEmu(nLeft)).getStr() : nullptr,
2695                                XML_rIns, (nRight != DEFLRINS) ? OString::number(oox::drawingml::convertHmmToEmu(nRight)).getStr() : nullptr,
2696                                XML_tIns, (nTop != DEFTBINS) ? OString::number(oox::drawingml::convertHmmToEmu(nTop)).getStr() : nullptr,
2697                                XML_bIns, (nBottom != DEFTBINS) ? OString::number(oox::drawingml::convertHmmToEmu(nBottom)).getStr() : nullptr,
2698                                XML_anchor, sVerticalAlignment,
2699                                XML_anchorCtr, bHorizontalCenter ? "1" : nullptr,
2700                                XML_vert, sWritingMode,
2701                                XML_rot, (nTextRotateAngle != 0) ? oox::drawingml::calcRotationValue( nTextRotateAngle * 100 ).getStr() : nullptr );
2702         if (bIsFontworkShape)
2703         {
2704             if (aAdjustmentSeq.getLength() > 0)
2705             {
2706                 mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, presetWarp.toUtf8());
2707                 mpFS->startElementNS(XML_a, XML_avLst);
2708                 for (sal_Int32 i = 0, nElems = aAdjustmentSeq.getLength(); i < nElems; ++i )
2709                 {
2710                     OString sName = OString("adj") + (( nElems > 1 ) ? OString::number(i + 1) : OString());
2711                     double fValue(0.0);
2712                     if (aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE)
2713                         aAdjustmentSeq[i].Value >>= fValue;
2714                     else
2715                     {
2716                         sal_Int32 nNumber(0);
2717                         aAdjustmentSeq[i].Value >>= nNumber;
2718                         fValue = static_cast<double>(nNumber);
2719                     }
2720                     // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree
2721                     // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree.
2722                     // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import.
2723                     if (presetWarp == "textArchDown" || presetWarp == "textArchUp"
2724                         || presetWarp == "textButton" || presetWarp == "textCircle"
2725                         || ((i == 0) && (presetWarp == "textArchDownPour" || presetWarp == "textArchUpPour"
2726                         || presetWarp == "textButtonPour" || presetWarp == "textCirclePour")))
2727                     {
2728                         fValue *= 60000.0;
2729                     }
2730                     else if ((i == 1) && (presetWarp == "textDoubleWave1" || presetWarp == "textWave1"
2731                             || presetWarp == "textWave2" || presetWarp == "textWave4"))
2732                     {
2733                         fValue = fValue / 0.216 - 50000.0;
2734                     }
2735                     else if ((i == 1) && (presetWarp == "textArchDownPour" || presetWarp == "textArchUpPour"
2736                         || presetWarp == "textButtonPour" || presetWarp == "textCirclePour"))
2737                     {
2738                         fValue /= 0.108;
2739                     }
2740                     else
2741                     {
2742                         fValue /= 0.216;
2743                     }
2744                     OString sFmla = OString("val ") + OString::number(std::lround(fValue));
2745                     mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
2746                 }
2747                 mpFS->endElementNS( XML_a, XML_avLst );
2748                 mpFS->endElementNS(XML_a, XML_prstTxWarp);
2749             }
2750             else
2751             {
2752                 mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, presetWarp.toUtf8());
2753             }
2754         }
2755 
2756         if (GetDocumentType() == DOCUMENT_DOCX || GetDocumentType() == DOCUMENT_XLSX)
2757         {
2758             bool bTextAutoGrowHeight = false;
2759             if (GetProperty(rXPropSet, "TextAutoGrowHeight"))
2760                 mAny >>= bTextAutoGrowHeight;
2761             mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
2762         }
2763         if (GetDocumentType() == DOCUMENT_PPTX)
2764         {
2765             TextFitToSizeType eFit = TextFitToSizeType_NONE;
2766             if (GetProperty(rXPropSet, "TextFitToSize"))
2767                 mAny >>= eFit;
2768 
2769             if (eFit == TextFitToSizeType_AUTOFIT)
2770             {
2771                 const sal_Int32 MAX_SCALE_VAL = 100000;
2772                 sal_Int32 nFontScale = MAX_SCALE_VAL;
2773                 SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get());
2774                 if (pTextShape)
2775                 {
2776                     SdrTextObj* pTextObject = dynamic_cast<SdrTextObj*>(pTextShape->GetSdrObject());
2777                     if (pTextObject)
2778                     {
2779                         double fScaleY = pTextObject->GetFontScaleY();
2780                         nFontScale = static_cast<sal_uInt32>(fScaleY * 100) * 1000;
2781                     }
2782                 }
2783 
2784                 mpFS->singleElementNS(XML_a, XML_normAutofit, XML_fontScale,
2785                     ( nFontScale < MAX_SCALE_VAL && nFontScale > 0 ) ? OString::number(nFontScale).getStr() : nullptr);
2786             }
2787             else
2788             {
2789                 bool bTextAutoGrowHeight = false;
2790                 if (GetProperty(rXPropSet, "TextAutoGrowHeight"))
2791                     mAny >>= bTextAutoGrowHeight;
2792                 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
2793             }
2794         }
2795         mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr);
2796     }
2797 
2798     Reference< XEnumerationAccess > access( xXText, UNO_QUERY );
2799     if( !access.is() || !bText )
2800         return;
2801 
2802     Reference< XEnumeration > enumeration( access->createEnumeration() );
2803     if( !enumeration.is() )
2804         return;
2805 
2806     uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
2807     SdrObject* pSdrObject = xShape.is() ? GetSdrObjectFromXShape(xShape) : nullptr;
2808     const SdrTextObj* pTxtObj = dynamic_cast<SdrTextObj*>( pSdrObject );
2809     if (pTxtObj && mpTextExport)
2810     {
2811         const OutlinerParaObject* pParaObj = nullptr;
2812         bool bOwnParaObj = false;
2813 
2814         /*
2815         #i13885#
2816         When the object is actively being edited, that text is not set into
2817         the objects normal text object, but lives in a separate object.
2818         */
2819         if (pTxtObj->IsTextEditActive())
2820         {
2821             pParaObj = pTxtObj->GetEditOutlinerParaObject().release();
2822             bOwnParaObj = true;
2823         }
2824         else
2825             pParaObj = pTxtObj->GetOutlinerParaObject();
2826 
2827         if (pParaObj)
2828         {
2829             // this is reached only in case some text is attached to the shape
2830             mpTextExport->WriteOutliner(*pParaObj);
2831             if (bOwnParaObj)
2832                 delete pParaObj;
2833         }
2834         return;
2835     }
2836 
2837     bool bOverridingCharHeight = false;
2838     sal_Int32 nCharHeight = -1;
2839 
2840     while( enumeration->hasMoreElements() )
2841     {
2842         Reference< XTextContent > paragraph;
2843         Any any ( enumeration->nextElement() );
2844 
2845         if( any >>= paragraph)
2846             WriteParagraph( paragraph, bOverridingCharHeight, nCharHeight );
2847     }
2848 }
2849 
2850 void DrawingML::WritePresetShape( const char* pShape , std::vector< std::pair<sal_Int32,sal_Int32>> & rAvList )
2851 {
2852     mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2853     if ( !rAvList.empty() )
2854     {
2855 
2856         mpFS->startElementNS(XML_a, XML_avLst);
2857         for (auto const& elem : rAvList)
2858         {
2859             OString sName = OString("adj") + ( ( elem.first > 0 ) ? OString::number(elem.first) : OString() );
2860             OString sFmla = OString("val ") + OString::number( elem.second );
2861 
2862             mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
2863         }
2864         mpFS->endElementNS( XML_a, XML_avLst );
2865     }
2866     else
2867         mpFS->singleElementNS(XML_a, XML_avLst);
2868 
2869     mpFS->endElementNS(  XML_a, XML_prstGeom );
2870 }
2871 
2872 void DrawingML::WritePresetShape( const char* pShape )
2873 {
2874     mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2875     mpFS->singleElementNS(XML_a, XML_avLst);
2876     mpFS->endElementNS(  XML_a, XML_prstGeom );
2877 }
2878 
2879 static std::map< OString, std::vector<OString> > lcl_getAdjNames()
2880 {
2881     std::map< OString, std::vector<OString> > aRet;
2882 
2883     OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/oox-drawingml-adj-names");
2884     rtl::Bootstrap::expandMacros(aPath);
2885     SvFileStream aStream(aPath, StreamMode::READ);
2886     if (aStream.GetError() != ERRCODE_NONE)
2887         SAL_WARN("oox.shape", "failed to open oox-drawingml-adj-names");
2888     OString aLine;
2889     bool bNotDone = aStream.ReadLine(aLine);
2890     while (bNotDone)
2891     {
2892         sal_Int32 nIndex = 0;
2893         // Each line is in a "key\tvalue" format: read the key, the rest is the value.
2894         OString aKey = aLine.getToken(0, '\t', nIndex);
2895         OString aValue = aLine.copy(nIndex);
2896         aRet[aKey].push_back(aValue);
2897         bNotDone = aStream.ReadLine(aLine);
2898     }
2899     return aRet;
2900 }
2901 
2902 void DrawingML::WritePresetShape( const char* pShape, MSO_SPT eShapeType, bool bPredefinedHandlesUsed, const PropertyValue& rProp )
2903 {
2904     static std::map< OString, std::vector<OString> > aAdjMap = lcl_getAdjNames();
2905     // If there are predefined adj names for this shape type, look them up now.
2906     std::vector<OString> aAdjustments;
2907     if (aAdjMap.find(OString(pShape)) != aAdjMap.end())
2908         aAdjustments = aAdjMap[OString(pShape)];
2909 
2910     mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2911     mpFS->startElementNS(XML_a, XML_avLst);
2912 
2913     Sequence< drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
2914     if ( ( rProp.Value >>= aAdjustmentSeq )
2915          && eShapeType != mso_sptActionButtonForwardNext  // we have adjustments values for these type of shape, but MSO doesn't like them
2916          && eShapeType != mso_sptActionButtonBackPrevious // so they are now disabled
2917          && OString(pShape) != "rect" //some shape types are commented out in pCustomShapeTypeTranslationTable[] & are being defaulted to rect & rect does not have adjustment values/name.
2918         )
2919     {
2920         SAL_INFO("oox.shape", "adj seq len: " << aAdjustmentSeq.getLength());
2921         sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
2922         if ( bPredefinedHandlesUsed )
2923             EscherPropertyContainer::LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );
2924 
2925         sal_Int32 nValue, nLength = aAdjustmentSeq.getLength();
2926         // 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.
2927         // Sometimes there are more values than needed, so we ignore the excessive ones.
2928         if (aAdjustments.size() <= static_cast<sal_uInt32>(nLength))
2929         {
2930             for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAdjustments.size()); i++)
2931             {
2932                 if( EscherPropertyContainer::GetAdjustmentValue( aAdjustmentSeq[ i ], i, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
2933                 {
2934                     // If the document model doesn't have an adjustment name (e.g. shape was created from VML), then take it from the predefined list.
2935                     OString aAdjName = aAdjustmentSeq[i].Name.isEmpty()
2936                                            ? aAdjustments[i]
2937                                            : aAdjustmentSeq[i].Name.toUtf8();
2938 
2939                     mpFS->singleElementNS( XML_a, XML_gd,
2940                                        XML_name, aAdjName,
2941                                        XML_fmla, "val " + OString::number(nValue));
2942                 }
2943             }
2944         }
2945     }
2946 
2947     mpFS->endElementNS( XML_a, XML_avLst );
2948     mpFS->endElementNS(  XML_a, XML_prstGeom );
2949 }
2950 
2951 bool DrawingML::WriteCustomGeometry(
2952     const Reference< XShape >& rXShape,
2953     const SdrObjCustomShape& rSdrObjCustomShape)
2954 {
2955     uno::Reference< beans::XPropertySet > aXPropSet;
2956     uno::Any aAny( rXShape->queryInterface(cppu::UnoType<beans::XPropertySet>::get()));
2957 
2958     if ( ! (aAny >>= aXPropSet) )
2959         return false;
2960 
2961     try
2962     {
2963         aAny = aXPropSet->getPropertyValue( "CustomShapeGeometry" );
2964         if ( !aAny.hasValue() )
2965             return false;
2966     }
2967     catch( const ::uno::Exception& )
2968     {
2969         return false;
2970     }
2971 
2972 
2973     auto pGeometrySeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny);
2974 
2975     if ( pGeometrySeq )
2976     {
2977         for( int i = 0; i < pGeometrySeq->getLength(); ++i )
2978         {
2979             const beans::PropertyValue& rProp = (*pGeometrySeq)[ i ];
2980             if ( rProp.Name == "Path" )
2981             {
2982                 uno::Sequence<beans::PropertyValue> aPathProp;
2983                 rProp.Value >>= aPathProp;
2984 
2985                 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs;
2986                 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
2987                 uno::Sequence<awt::Size> aPathSize;
2988                 for (int j = 0; j < aPathProp.getLength(); ++j )
2989                 {
2990                     const beans::PropertyValue& rPathProp = aPathProp[j];
2991                     if (rPathProp.Name == "Coordinates")
2992                         rPathProp.Value >>= aPairs;
2993                     else if (rPathProp.Name == "Segments")
2994                         rPathProp.Value >>= aSegments;
2995                     else if (rPathProp.Name == "SubViewSize")
2996                         rPathProp.Value >>= aPathSize;
2997                 }
2998 
2999                 if ( !aPairs.hasElements() )
3000                     return false;
3001 
3002                 if ( !aSegments.hasElements() )
3003                 {
3004                     aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment>(4);
3005                     aSegments[0].Count = 1;
3006                     aSegments[0].Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
3007                     aSegments[1].Count = static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) ));
3008                     aSegments[1].Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
3009                     aSegments[2].Count = 0;
3010                     aSegments[2].Command = drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
3011                     aSegments[3].Count = 0;
3012                     aSegments[3].Command = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
3013                 }
3014 
3015                 int nExpectedPairCount = 0;
3016                 for( int j = 0; j < aSegments.getLength(); ++j )
3017                 {
3018                     nExpectedPairCount += aSegments[j].Count;
3019                 }
3020 
3021                 if ( nExpectedPairCount > aPairs.getLength() )
3022                 {
3023                     SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs.");
3024                     return false;
3025                 }
3026 
3027                 mpFS->startElementNS(XML_a, XML_custGeom);
3028                 mpFS->singleElementNS(XML_a, XML_avLst);
3029                 mpFS->singleElementNS(XML_a, XML_gdLst);
3030                 mpFS->singleElementNS(XML_a, XML_ahLst);
3031                 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t",
3032                                       XML_r, "r", XML_b, "b");
3033                 mpFS->startElementNS(XML_a, XML_pathLst);
3034 
3035                 if ( aPathSize.hasElements() )
3036                 {
3037                     mpFS->startElementNS( XML_a, XML_path,
3038                           XML_w, OString::number(aPathSize[0].Width),
3039                           XML_h, OString::number(aPathSize[0].Height) );
3040                 }
3041                 else
3042                 {
3043                     sal_Int32 nXMin(0);
3044                     aPairs[0].First.Value >>= nXMin;
3045                     sal_Int32 nXMax = nXMin;
3046                     sal_Int32 nYMin(0);
3047                     aPairs[0].Second.Value >>= nYMin;
3048                     sal_Int32 nYMax = nYMin;
3049 
3050                     for ( int j = 0; j < aPairs.getLength(); ++j )
3051                     {
3052                         sal_Int32 nX = GetCustomGeometryPointValue(aPairs[j].First, rSdrObjCustomShape);
3053                         sal_Int32 nY = GetCustomGeometryPointValue(aPairs[j].Second, rSdrObjCustomShape);
3054                         if (nX < nXMin)
3055                             nXMin = nX;
3056                         if (nY < nYMin)
3057                             nYMin = nY;
3058                         if (nX > nXMax)
3059                             nXMax = nX;
3060                         if (nY > nYMax)
3061                             nYMax = nY;
3062                     }
3063                     mpFS->startElementNS( XML_a, XML_path,
3064                           XML_w, OString::number(nXMax - nXMin),
3065                           XML_h, OString::number(nYMax - nYMin) );
3066                 }
3067 
3068 
3069                 int nPairIndex = 0;
3070                 bool bOK = true;
3071                 for (int j = 0; j < aSegments.getLength() && bOK; ++j)
3072                 {
3073                     if ( aSegments[ j ].Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
3074                     {
3075                         mpFS->singleElementNS(XML_a, XML_close);
3076                     }
3077                     for (int k = 0; k < aSegments[j].Count && bOK; ++k)
3078                     {
3079                         switch( aSegments[ j ].Command )
3080                         {
3081                             case drawing::EnhancedCustomShapeSegmentCommand::MOVETO :
3082                             {
3083                                 if (nPairIndex >= aPairs.getLength())
3084                                     bOK = false;
3085                                 else
3086                                 {
3087                                     mpFS->startElementNS(XML_a, XML_moveTo);
3088                                     WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
3089                                     mpFS->endElementNS( XML_a, XML_moveTo );
3090                                     nPairIndex++;
3091                                 }
3092                                 break;
3093                             }
3094                             case drawing::EnhancedCustomShapeSegmentCommand::LINETO :
3095                             {
3096                                 if (nPairIndex >= aPairs.getLength())
3097                                     bOK = false;
3098                                 else
3099                                 {
3100                                     mpFS->startElementNS(XML_a, XML_lnTo);
3101                                     WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
3102                                     mpFS->endElementNS( XML_a, XML_lnTo );
3103                                     nPairIndex++;
3104                                 }
3105                                 break;
3106                             }
3107                             case drawing::EnhancedCustomShapeSegmentCommand::CURVETO :
3108                             {
3109                                 if (nPairIndex + 2 >= aPairs.getLength())
3110                                     bOK = false;
3111                                 else
3112                                 {
3113                                     mpFS->startElementNS(XML_a, XML_cubicBezTo);
3114                                     for( sal_uInt8 l = 0; l <= 2; ++l )
3115                                     {
3116                                         WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3117                                     }
3118                                     mpFS->endElementNS( XML_a, XML_cubicBezTo );
3119                                     nPairIndex += 3;
3120                                 }
3121                                 break;
3122                             }
3123                             case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
3124                             case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
3125                             {
3126                                 nPairIndex += 3;
3127                                 break;
3128                             }
3129                             case drawing::EnhancedCustomShapeSegmentCommand::ARCTO :
3130                             case drawing::EnhancedCustomShapeSegmentCommand::ARC :
3131                             case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
3132                             case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
3133                             {
3134                                 nPairIndex += 4;
3135                                 break;
3136                             }
3137                             case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
3138                             case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
3139                             {
3140                                 nPairIndex++;
3141                                 break;
3142                             }
3143                             case drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO :
3144                             {
3145                                 if (nPairIndex + 1 >= aPairs.getLength())
3146                                     bOK = false;
3147                                 else
3148                                 {
3149                                     mpFS->startElementNS(XML_a, XML_quadBezTo);
3150                                     for( sal_uInt8 l = 0; l < 2; ++l )
3151                                     {
3152                                         WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3153                                     }
3154                                     mpFS->endElementNS( XML_a, XML_quadBezTo );
3155                                     nPairIndex += 2;
3156                                 }
3157                                 break;
3158                             }
3159                             case drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO :
3160                             {
3161                                 nPairIndex += 2;
3162                                 break;
3163                             }
3164                             default:
3165                                 // do nothing
3166                                 break;
3167                         }
3168                     }
3169                 }
3170                 mpFS->endElementNS( XML_a, XML_path );
3171                 mpFS->endElementNS( XML_a, XML_pathLst );
3172                 mpFS->endElementNS( XML_a, XML_custGeom );
3173                 return bOK;
3174             }
3175         }
3176     }
3177     return false;
3178 }
3179 
3180 void DrawingML::WriteCustomGeometryPoint(
3181     const drawing::EnhancedCustomShapeParameterPair& rParamPair,
3182     const SdrObjCustomShape& rSdrObjCustomShape)
3183 {
3184     sal_Int32 nX = GetCustomGeometryPointValue(rParamPair.First, rSdrObjCustomShape);
3185     sal_Int32 nY = GetCustomGeometryPointValue(rParamPair.Second, rSdrObjCustomShape);
3186 
3187     mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(nX), XML_y, OString::number(nY));
3188 }
3189 
3190 sal_Int32 DrawingML::GetCustomGeometryPointValue(
3191     const css::drawing::EnhancedCustomShapeParameter& rParam,
3192     const SdrObjCustomShape& rSdrObjCustomShape)
3193 {
3194     const EnhancedCustomShape2d aCustoShape2d(const_cast< SdrObjCustomShape& >(rSdrObjCustomShape));
3195     double fValue = 0.0;
3196     aCustoShape2d.GetParameter(fValue, rParam, false, false);
3197     sal_Int32 nValue(std::lround(fValue));
3198 
3199     return nValue;
3200 }
3201 
3202 void DrawingML::WritePolyPolygon( const tools::PolyPolygon& rPolyPolygon, const bool bClosed )
3203 {
3204     // In case of Writer, the parent element is <wps:spPr>, and there the
3205     // <a:custGeom> element is not optional.
3206     if (rPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX)
3207         return;
3208 
3209     mpFS->startElementNS(XML_a, XML_custGeom);
3210     mpFS->singleElementNS(XML_a, XML_avLst);
3211     mpFS->singleElementNS(XML_a, XML_gdLst);
3212     mpFS->singleElementNS(XML_a, XML_ahLst);
3213     mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b");
3214 
3215     mpFS->startElementNS( XML_a, XML_pathLst );
3216 
3217     const tools::Rectangle aRect( rPolyPolygon.GetBoundRect() );
3218 
3219     // Put all polygons of rPolyPolygon in the same path element
3220     // to subtract the overlapped areas.
3221     mpFS->startElementNS( XML_a, XML_path,
3222             XML_w, OString::number(aRect.GetWidth()),
3223             XML_h, OString::number(aRect.GetHeight()) );
3224 
3225     for( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i ++ )
3226     {
3227 
3228         const tools::Polygon& rPoly = rPolyPolygon[ i ];
3229 
3230         if( rPoly.GetSize() > 0 )
3231         {
3232             mpFS->startElementNS(XML_a, XML_moveTo);
3233 
3234             mpFS->singleElementNS( XML_a, XML_pt,
3235                                    XML_x, OString::number(rPoly[0].X() - aRect.Left()),
3236                                    XML_y, OString::number(rPoly[0].Y() - aRect.Top()) );
3237 
3238             mpFS->endElementNS( XML_a, XML_moveTo );
3239         }
3240 
3241         for( sal_uInt16 j = 1; j < rPoly.GetSize(); j ++ )
3242         {
3243             PolyFlags flags = rPoly.GetFlags(j);
3244             if( flags == PolyFlags::Control )
3245             {
3246                 // a:cubicBezTo can only contain 3 a:pt elements, so we need to make sure of this
3247                 if( j+2 < rPoly.GetSize() && rPoly.GetFlags(j+1) == PolyFlags::Control && rPoly.GetFlags(j+2) != PolyFlags::Control )
3248                 {
3249 
3250                     mpFS->startElementNS(XML_a, XML_cubicBezTo);
3251                     for( sal_uInt8 k = 0; k <= 2; ++k )
3252                     {
3253                         mpFS->singleElementNS(XML_a, XML_pt,
3254                                               XML_x, OString::number(rPoly[j+k].X() - aRect.Left()),
3255                                               XML_y, OString::number(rPoly[j+k].Y() - aRect.Top()));
3256 
3257                     }
3258                     mpFS->endElementNS( XML_a, XML_cubicBezTo );
3259                     j += 2;
3260                 }
3261             }
3262             else if( flags == PolyFlags::Normal )
3263             {
3264                 mpFS->startElementNS(XML_a, XML_lnTo);
3265                 mpFS->singleElementNS( XML_a, XML_pt,
3266                                        XML_x, OString::number(rPoly[j].X() - aRect.Left()),
3267                                        XML_y, OString::number(rPoly[j].Y() - aRect.Top()) );
3268                 mpFS->endElementNS( XML_a, XML_lnTo );
3269             }
3270         }
3271     }
3272     if (bClosed)
3273         mpFS->singleElementNS( XML_a, XML_close);
3274     mpFS->endElementNS( XML_a, XML_path );
3275 
3276     mpFS->endElementNS( XML_a, XML_pathLst );
3277 
3278     mpFS->endElementNS( XML_a, XML_custGeom );
3279 }
3280 
3281 void DrawingML::WriteConnectorConnections( EscherConnectorListEntry& rConnectorEntry, sal_Int32 nStartID, sal_Int32 nEndID )
3282 {
3283     if( nStartID != -1 )
3284     {
3285         mpFS->singleElementNS( XML_a, XML_stCxn,
3286                                XML_id, OString::number(nStartID),
3287                                XML_idx, OString::number(rConnectorEntry.GetConnectorRule(true)) );
3288     }
3289     if( nEndID != -1 )
3290     {
3291         mpFS->singleElementNS( XML_a, XML_endCxn,
3292                                XML_id, OString::number(nEndID),
3293                                XML_idx, OString::number(rConnectorEntry.GetConnectorRule(false)) );
3294     }
3295 }
3296 
3297 sal_Unicode DrawingML::SubstituteBullet( sal_Unicode cBulletId, css::awt::FontDescriptor& rFontDesc )
3298 {
3299     if ( IsStarSymbol(rFontDesc.Name) )
3300     {
3301         rtl_TextEncoding eCharSet = rFontDesc.CharSet;
3302         cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eCharSet, rFontDesc.Name);
3303         rFontDesc.CharSet = eCharSet;
3304     }
3305 
3306     return cBulletId;
3307 }
3308 
3309 sax_fastparser::FSHelperPtr DrawingML::CreateOutputStream (
3310     const OUString& sFullStream,
3311     const OUString& sRelativeStream,
3312     const Reference< XOutputStream >& xParentRelation,
3313     const char* sContentType,
3314     const char* sRelationshipType,
3315     OUString* pRelationshipId )
3316 {
3317     OUString sRelationshipId;
3318     if (xParentRelation.is())
3319         sRelationshipId = GetFB()->addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream );
3320     else
3321         sRelationshipId = GetFB()->addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream );
3322 
3323     if( pRelationshipId )
3324         *pRelationshipId = sRelationshipId;
3325 
3326     sax_fastparser::FSHelperPtr p = GetFB()->openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
3327 
3328     return p;
3329 }
3330 
3331 void DrawingML::WriteFill( const Reference< XPropertySet >& xPropSet )
3332 {
3333     if ( !GetProperty( xPropSet, "FillStyle" ) )
3334         return;
3335     FillStyle aFillStyle( FillStyle_NONE );
3336     xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle;
3337 
3338     if ( aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparence" ) )
3339     {
3340         // map full transparent background to no fill
3341         sal_Int16 nVal = 0;
3342         xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal;
3343         if ( nVal == 100 )
3344             aFillStyle = FillStyle_NONE;
3345     }
3346 
3347     switch( aFillStyle )
3348     {
3349     case FillStyle_SOLID :
3350         WriteSolidFill( xPropSet );
3351         break;
3352     case FillStyle_GRADIENT :
3353         WriteGradientFill( xPropSet );
3354         break;
3355     case FillStyle_BITMAP :
3356         WriteBlipFill( xPropSet, "FillBitmap" );
3357         break;
3358     case FillStyle_HATCH :
3359         WritePattFill( xPropSet );
3360         break;
3361     case FillStyle_NONE:
3362         mpFS->singleElementNS(XML_a, XML_noFill);
3363         break;
3364     default:
3365         ;
3366     }
3367 }
3368 
3369 void DrawingML::WriteStyleProperties( sal_Int32 nTokenId, const Sequence< PropertyValue >& aProperties )
3370 {
3371     if( aProperties.hasElements() )
3372     {
3373         OUString sSchemeClr;
3374         sal_uInt32 nIdx = 0;
3375         Sequence< PropertyValue > aTransformations;
3376         for( sal_Int32 i=0; i < aProperties.getLength(); ++i)
3377         {
3378             if( aProperties[i].Name == "SchemeClr" )
3379                 aProperties[i].Value >>= sSchemeClr;
3380             else if( aProperties[i].Name == "Idx" )
3381                 aProperties[i].Value >>= nIdx;
3382             else if( aProperties[i].Name == "Transformations" )
3383                 aProperties[i].Value >>= aTransformations;
3384         }
3385         mpFS->startElementNS(XML_a, nTokenId, XML_idx, OString::number(nIdx));
3386         WriteColor(sSchemeClr, aTransformations);
3387         mpFS->endElementNS( XML_a, nTokenId );
3388     }
3389     else
3390     {
3391         // write mock <a:*Ref> tag
3392         mpFS->singleElementNS(XML_a, nTokenId, XML_idx, OString::number(0));
3393     }
3394 }
3395 
3396 void DrawingML::WriteShapeStyle( const Reference< XPropertySet >& xPropSet )
3397 {
3398     // check existence of the grab bag
3399     if ( !GetProperty( xPropSet, "InteropGrabBag" ) )
3400         return;
3401 
3402     // extract the relevant properties from the grab bag
3403     Sequence< PropertyValue > aGrabBag;
3404     Sequence< PropertyValue > aFillRefProperties, aLnRefProperties, aEffectRefProperties;
3405     mAny >>= aGrabBag;
3406     for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i)
3407     {
3408         if( aGrabBag[i].Name == "StyleFillRef" )
3409             aGrabBag[i].Value >>= aFillRefProperties;
3410         else if( aGrabBag[i].Name == "StyleLnRef" )
3411             aGrabBag[i].Value >>= aLnRefProperties;
3412         else if( aGrabBag[i].Name == "StyleEffectRef" )
3413             aGrabBag[i].Value >>= aEffectRefProperties;
3414     }
3415 
3416     WriteStyleProperties( XML_lnRef, aLnRefProperties );
3417     WriteStyleProperties( XML_fillRef, aFillRefProperties );
3418     WriteStyleProperties( XML_effectRef, aEffectRefProperties );
3419 
3420     // write mock <a:fontRef>
3421     mpFS->singleElementNS(XML_a, XML_fontRef, XML_idx, "minor");
3422 }
3423 
3424 void DrawingML::WriteShapeEffect( const OUString& sName, const Sequence< PropertyValue >& aEffectProps )
3425 {
3426     if( !aEffectProps.hasElements() )
3427         return;
3428 
3429     // assign the proper tag and enable bContainsColor if necessary
3430     sal_Int32 nEffectToken = 0;
3431     bool bContainsColor = false;
3432     if( sName == "outerShdw" )
3433     {
3434         nEffectToken = FSNS( XML_a, XML_outerShdw );
3435         bContainsColor = true;
3436     }
3437     else if( sName == "innerShdw" )
3438     {
3439         nEffectToken = FSNS( XML_a, XML_innerShdw );
3440         bContainsColor = true;
3441     }
3442     else if( sName == "glow" )
3443     {
3444         nEffectToken = FSNS( XML_a, XML_glow );
3445         bContainsColor = true;
3446     }
3447     else if( sName == "softEdge" )
3448         nEffectToken = FSNS( XML_a, XML_softEdge );
3449     else if( sName == "reflection" )
3450         nEffectToken = FSNS( XML_a, XML_reflection );
3451     else if( sName == "blur" )
3452         nEffectToken = FSNS( XML_a, XML_blur );
3453 
3454     OUString sSchemeClr;
3455     ::Color nRgbClr;
3456     sal_Int32 nAlpha = MAX_PERCENT;
3457     Sequence< PropertyValue > aTransformations;
3458     sax_fastparser::FastAttributeList *aOuterShdwAttrList = FastSerializerHelper::createAttrList();
3459     sax_fastparser::XFastAttributeListRef xOuterShdwAttrList( aOuterShdwAttrList );
3460     for( sal_Int32 i=0; i < aEffectProps.getLength(); ++i )
3461     {
3462         if( aEffectProps[i].Name == "Attribs" )
3463         {
3464             // read tag attributes
3465             uno::Sequence< beans::PropertyValue > aOuterShdwProps;
3466             aEffectProps[i].Value >>= aOuterShdwProps;
3467             for( sal_Int32 j=0; j < aOuterShdwProps.getLength(); ++j )
3468             {
3469                 if( aOuterShdwProps[j].Name == "algn" )
3470                 {
3471                     OUString sVal;
3472                     aOuterShdwProps[j].Value >>= sVal;
3473                     aOuterShdwAttrList->add( XML_algn, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3474                 }
3475                 else if( aOuterShdwProps[j].Name == "blurRad" )
3476                 {
3477                     sal_Int32 nVal = 0;
3478                     aOuterShdwProps[j].Value >>= nVal;
3479                     aOuterShdwAttrList->add( XML_blurRad, OString::number( nVal ).getStr() );
3480                 }
3481                 else if( aOuterShdwProps[j].Name == "dir" )
3482                 {
3483                     sal_Int32 nVal = 0;
3484                     aOuterShdwProps[j].Value >>= nVal;
3485                     aOuterShdwAttrList->add( XML_dir, OString::number( nVal ).getStr() );
3486                 }
3487                 else if( aOuterShdwProps[j].Name == "dist" )
3488                 {
3489                     sal_Int32 nVal = 0;
3490                     aOuterShdwProps[j].Value >>= nVal;
3491                     aOuterShdwAttrList->add( XML_dist, OString::number( nVal ).getStr() );
3492                 }
3493                 else if( aOuterShdwProps[j].Name == "kx" )
3494                 {
3495                     sal_Int32 nVal = 0;
3496                     aOuterShdwProps[j].Value >>= nVal;
3497                     aOuterShdwAttrList->add( XML_kx, OString::number( nVal ).getStr() );
3498                 }
3499                 else if( aOuterShdwProps[j].Name == "ky" )
3500                 {
3501                     sal_Int32 nVal = 0;
3502                     aOuterShdwProps[j].Value >>= nVal;
3503                     aOuterShdwAttrList->add( XML_ky, OString::number( nVal ).getStr() );
3504                 }
3505                 else if( aOuterShdwProps[j].Name == "rotWithShape" )
3506                 {
3507                     sal_Int32 nVal = 0;
3508                     aOuterShdwProps[j].Value >>= nVal;
3509                     aOuterShdwAttrList->add( XML_rotWithShape, OString::number( nVal ).getStr() );
3510                 }
3511                 else if( aOuterShdwProps[j].Name == "sx" )
3512                 {
3513                     sal_Int32 nVal = 0;
3514                     aOuterShdwProps[j].Value >>= nVal;
3515                     aOuterShdwAttrList->add( XML_sx, OString::number( nVal ).getStr() );
3516                 }
3517                 else if( aOuterShdwProps[j].Name == "sy" )
3518                 {
3519                     sal_Int32 nVal = 0;
3520                     aOuterShdwProps[j].Value >>= nVal;
3521                     aOuterShdwAttrList->add( XML_sy, OString::number( nVal ).getStr() );
3522                 }
3523                 else if( aOuterShdwProps[j].Name == "rad" )
3524                 {
3525                     sal_Int32 nVal = 0;
3526                     aOuterShdwProps[j].Value >>= nVal;
3527                     aOuterShdwAttrList->add( XML_rad, OString::number( nVal ).getStr() );
3528                 }
3529                 else if( aOuterShdwProps[j].Name == "endA" )
3530                 {
3531                     sal_Int32 nVal = 0;
3532                     aOuterShdwProps[j].Value >>= nVal;
3533                     aOuterShdwAttrList->add( XML_endA, OString::number( nVal ).getStr() );
3534                 }
3535                 else if( aOuterShdwProps[j].Name == "endPos" )
3536                 {
3537                     sal_Int32 nVal = 0;
3538                     aOuterShdwProps[j].Value >>= nVal;
3539                     aOuterShdwAttrList->add( XML_endPos, OString::number( nVal ).getStr() );
3540                 }
3541                 else if( aOuterShdwProps[j].Name == "fadeDir" )
3542                 {
3543                     sal_Int32 nVal = 0;
3544                     aOuterShdwProps[j].Value >>= nVal;
3545                     aOuterShdwAttrList->add( XML_fadeDir, OString::number( nVal ).getStr() );
3546                 }
3547                 else if( aOuterShdwProps[j].Name == "stA" )
3548                 {
3549                     sal_Int32 nVal = 0;
3550                     aOuterShdwProps[j].Value >>= nVal;
3551                     aOuterShdwAttrList->add( XML_stA, OString::number( nVal ).getStr() );
3552                 }
3553                 else if( aOuterShdwProps[j].Name == "stPos" )
3554                 {
3555                     sal_Int32 nVal = 0;
3556                     aOuterShdwProps[j].Value >>= nVal;
3557                     aOuterShdwAttrList->add( XML_stPos, OString::number( nVal ).getStr() );
3558                 }
3559                 else if( aOuterShdwProps[j].Name == "grow" )
3560                 {
3561                     sal_Int32 nVal = 0;
3562                     aOuterShdwProps[j].Value >>= nVal;
3563                     aOuterShdwAttrList->add( XML_grow, OString::number( nVal ).getStr() );
3564                 }
3565             }
3566         }
3567         else if(aEffectProps[i].Name == "RgbClr")
3568         {
3569             aEffectProps[i].Value >>= nRgbClr;
3570         }
3571         else if(aEffectProps[i].Name == "RgbClrTransparency")
3572         {
3573             sal_Int32 nTransparency;
3574             if (aEffectProps[i].Value >>= nTransparency)
3575                 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
3576                 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
3577         }
3578         else if(aEffectProps[i].Name == "SchemeClr")
3579         {
3580             aEffectProps[i].Value >>= sSchemeClr;
3581         }
3582         else if(aEffectProps[i].Name == "SchemeClrTransformations")
3583         {
3584             aEffectProps[i].Value >>= aTransformations;
3585         }
3586     }
3587 
3588     if( nEffectToken > 0 )
3589     {
3590         mpFS->startElement( nEffectToken, xOuterShdwAttrList );
3591 
3592         if( bContainsColor )
3593         {
3594             if( sSchemeClr.isEmpty() )
3595                 WriteColor( nRgbClr, nAlpha );
3596             else
3597                 WriteColor( sSchemeClr, aTransformations );
3598         }
3599 
3600         mpFS->endElement( nEffectToken );
3601     }
3602 }
3603 
3604 static sal_Int32 lcl_CalculateDist(const double dX, const double dY)
3605 {
3606     return static_cast< sal_Int32 >(sqrt(dX*dX + dY*dY) * 360);
3607 }
3608 
3609 static sal_Int32 lcl_CalculateDir(const double dX, const double dY)
3610 {
3611     return (static_cast< sal_Int32 >(basegfx::rad2deg(atan2(dY,dX)) * 60000) + 21600000) % 21600000;
3612 }
3613 
3614 void DrawingML::WriteShapeEffects( const Reference< XPropertySet >& rXPropSet )
3615 {
3616     Sequence< PropertyValue > aGrabBag, aEffects, aOuterShdwProps;
3617     if( GetProperty( rXPropSet, "InteropGrabBag" ) )
3618     {
3619         mAny >>= aGrabBag;
3620         for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
3621         {
3622             if( aGrabBag[i].Name == "EffectProperties" )
3623             {
3624                 aGrabBag[i].Value >>= aEffects;
3625                 for( sal_Int32 j=0; j < aEffects.getLength(); ++j )
3626                 {
3627                     if( aEffects[j].Name == "outerShdw" )
3628                     {
3629                         aEffects[j].Value >>= aOuterShdwProps;
3630                         break;
3631                     }
3632                 }
3633                 break;
3634             }
3635         }
3636     }
3637 
3638     if( !aEffects.hasElements() )
3639     {
3640         bool bHasShadow = false;
3641         if( GetProperty( rXPropSet, "Shadow" ) )
3642             mAny >>= bHasShadow;
3643         if( bHasShadow )
3644         {
3645             Sequence< PropertyValue > aShadowGrabBag( 3 );
3646             Sequence< PropertyValue > aShadowAttribsGrabBag( 2 );
3647 
3648             double dX = +0.0, dY = +0.0;
3649             rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
3650             rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
3651 
3652             aShadowAttribsGrabBag[0].Name = "dist";
3653             aShadowAttribsGrabBag[0].Value <<= lcl_CalculateDist(dX, dY);
3654             aShadowAttribsGrabBag[1].Name = "dir";
3655             aShadowAttribsGrabBag[1].Value <<= lcl_CalculateDir(dX, dY);
3656 
3657             aShadowGrabBag[0].Name = "Attribs";
3658             aShadowGrabBag[0].Value <<= aShadowAttribsGrabBag;
3659             aShadowGrabBag[1].Name = "RgbClr";
3660             aShadowGrabBag[1].Value = rXPropSet->getPropertyValue( "ShadowColor" );
3661             aShadowGrabBag[2].Name = "RgbClrTransparency";
3662             aShadowGrabBag[2].Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
3663 
3664             mpFS->startElementNS(XML_a, XML_effectLst);
3665             WriteShapeEffect( "outerShdw", aShadowGrabBag );
3666             mpFS->endElementNS(XML_a, XML_effectLst);
3667         }
3668     }
3669     else
3670     {
3671         for( sal_Int32 i=0; i < aOuterShdwProps.getLength(); ++i )
3672         {
3673             if( aOuterShdwProps[i].Name == "Attribs" )
3674             {
3675                 Sequence< PropertyValue > aAttribsProps;
3676                 aOuterShdwProps[i].Value >>= aAttribsProps;
3677 
3678                 double dX = +0.0, dY = +0.0;
3679                 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
3680                 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
3681 
3682                 for( sal_Int32 j=0; j < aAttribsProps.getLength(); ++j )
3683                 {
3684                     if( aAttribsProps[j].Name == "dist" )
3685                     {
3686                         aAttribsProps[j].Value <<= lcl_CalculateDist(dX, dY);
3687                     }
3688                     else if( aAttribsProps[j].Name == "dir" )
3689                     {
3690                         aAttribsProps[j].Value <<= lcl_CalculateDir(dX, dY);
3691                     }
3692                 }
3693 
3694                 aOuterShdwProps[i].Value <<= aAttribsProps;
3695             }
3696             else if( aOuterShdwProps[i].Name == "RgbClr" )
3697             {
3698                 aOuterShdwProps[i].Value = rXPropSet->getPropertyValue( "ShadowColor" );
3699             }
3700             else if( aOuterShdwProps[i].Name == "RgbClrTransparency" )
3701             {
3702                 aOuterShdwProps[i].Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
3703             }
3704         }
3705 
3706         mpFS->startElementNS(XML_a, XML_effectLst);
3707         for( sal_Int32 i=0; i < aEffects.getLength(); ++i )
3708         {
3709             if( aEffects[i].Name == "outerShdw" )
3710             {
3711                 WriteShapeEffect( aEffects[i].Name, aOuterShdwProps );
3712             }
3713             else
3714             {
3715                 Sequence< PropertyValue > aEffectProps;
3716                 aEffects[i].Value >>= aEffectProps;
3717                 WriteShapeEffect( aEffects[i].Name, aEffectProps );
3718             }
3719         }
3720         mpFS->endElementNS(XML_a, XML_effectLst);
3721     }
3722 }
3723 
3724 void DrawingML::WriteShape3DEffects( const Reference< XPropertySet >& xPropSet )
3725 {
3726     // check existence of the grab bag
3727     if( !GetProperty( xPropSet, "InteropGrabBag" ) )
3728         return;
3729 
3730     // extract the relevant properties from the grab bag
3731     Sequence< PropertyValue > aGrabBag, aEffectProps, aLightRigProps, aShape3DProps;
3732     mAny >>= aGrabBag;
3733     for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
3734     {
3735         if( aGrabBag[i].Name == "3DEffectProperties" )
3736         {
3737             Sequence< PropertyValue > a3DEffectProps;
3738             aGrabBag[i].Value >>= a3DEffectProps;
3739             for( sal_Int32 j=0; j < a3DEffectProps.getLength(); ++j )
3740             {
3741                 if( a3DEffectProps[j].Name == "Camera" )
3742                     a3DEffectProps[j].Value >>= aEffectProps;
3743                 else if( a3DEffectProps[j].Name == "LightRig" )
3744                     a3DEffectProps[j].Value >>= aLightRigProps;
3745                 else if( a3DEffectProps[j].Name == "Shape3D" )
3746                     a3DEffectProps[j].Value >>= aShape3DProps;
3747             }
3748             break;
3749         }
3750     }
3751     if( !aEffectProps.hasElements() && !aLightRigProps.hasElements() && !aShape3DProps.hasElements() )
3752         return;
3753 
3754     bool bCameraRotationPresent = false;
3755     sax_fastparser::FastAttributeList *aCameraAttrList = FastSerializerHelper::createAttrList();
3756     sax_fastparser::XFastAttributeListRef xCameraAttrList( aCameraAttrList );
3757     sax_fastparser::FastAttributeList *aCameraRotationAttrList = FastSerializerHelper::createAttrList();
3758     sax_fastparser::XFastAttributeListRef xRotAttrList( aCameraRotationAttrList );
3759     for( sal_Int32 i=0; i < aEffectProps.getLength(); ++i )
3760     {
3761         if( aEffectProps[i].Name == "prst" )
3762         {
3763             OUString sVal;
3764             aEffectProps[i].Value >>= sVal;
3765             aCameraAttrList->add( XML_prst, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3766         }
3767         else if( aEffectProps[i].Name == "fov" )
3768         {
3769             float fVal = 0;
3770             aEffectProps[i].Value >>= fVal;
3771             aCameraAttrList->add( XML_fov, OString::number( fVal * 60000 ).getStr() );
3772         }
3773         else if( aEffectProps[i].Name == "zoom" )
3774         {
3775             float fVal = 1;
3776             aEffectProps[i].Value >>= fVal;
3777             aCameraAttrList->add( XML_zoom, OString::number( fVal * 100000 ).getStr() );
3778         }
3779         else if( aEffectProps[i].Name == "rotLat" ||
3780                 aEffectProps[i].Name == "rotLon" ||
3781                 aEffectProps[i].Name == "rotRev" )
3782         {
3783             sal_Int32 nVal = 0, nToken = XML_none;
3784             aEffectProps[i].Value >>= nVal;
3785             if( aEffectProps[i].Name == "rotLat" )
3786                 nToken = XML_lat;
3787             else if( aEffectProps[i].Name == "rotLon" )
3788                 nToken = XML_lon;
3789             else if( aEffectProps[i].Name == "rotRev" )
3790                 nToken = XML_rev;
3791             aCameraRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
3792             bCameraRotationPresent = true;
3793         }
3794     }
3795 
3796     bool bLightRigRotationPresent = false;
3797     sax_fastparser::FastAttributeList *aLightRigAttrList = FastSerializerHelper::createAttrList();
3798     sax_fastparser::XFastAttributeListRef xLightAttrList( aLightRigAttrList );
3799     sax_fastparser::FastAttributeList *aLightRigRotationAttrList = FastSerializerHelper::createAttrList();
3800     sax_fastparser::XFastAttributeListRef xLightRotAttrList( aLightRigRotationAttrList );
3801     for( sal_Int32 i=0; i < aLightRigProps.getLength(); ++i )
3802     {
3803         if( aLightRigProps[i].Name == "rig" || aLightRigProps[i].Name == "dir" )
3804         {
3805             OUString sVal;
3806             sal_Int32 nToken = XML_none;
3807             aLightRigProps[i].Value >>= sVal;
3808             if( aLightRigProps[i].Name == "rig" )
3809                 nToken = XML_rig;
3810             else if( aLightRigProps[i].Name == "dir" )
3811                 nToken = XML_dir;
3812             aLightRigAttrList->add( nToken, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3813         }
3814         else if( aLightRigProps[i].Name == "rotLat" ||
3815                 aLightRigProps[i].Name == "rotLon" ||
3816                 aLightRigProps[i].Name == "rotRev" )
3817         {
3818             sal_Int32 nVal = 0, nToken = XML_none;
3819             aLightRigProps[i].Value >>= nVal;
3820             if( aLightRigProps[i].Name == "rotLat" )
3821                 nToken = XML_lat;
3822             else if( aLightRigProps[i].Name == "rotLon" )
3823                 nToken = XML_lon;
3824             else if( aLightRigProps[i].Name == "rotRev" )
3825                 nToken = XML_rev;
3826             aLightRigRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
3827             bLightRigRotationPresent = true;
3828         }
3829     }
3830 
3831     mpFS->startElementNS(XML_a, XML_scene3d);
3832 
3833     if( aEffectProps.hasElements() )
3834     {
3835         mpFS->startElementNS( XML_a, XML_camera, xCameraAttrList );
3836         if( bCameraRotationPresent )
3837         {
3838             mpFS->singleElementNS( XML_a, XML_rot, xRotAttrList );
3839         }
3840         mpFS->endElementNS( XML_a, XML_camera );
3841     }
3842     else
3843     {
3844         // a:camera with Word default values - Word won't open the document if this is not present
3845         mpFS->singleElementNS(XML_a, XML_camera, XML_prst, "orthographicFront");
3846     }
3847 
3848     if( aEffectProps.hasElements() )
3849     {
3850         mpFS->startElementNS( XML_a, XML_lightRig, xLightAttrList );
3851         if( bLightRigRotationPresent )
3852         {
3853             mpFS->singleElementNS( XML_a, XML_rot, xLightRotAttrList );
3854         }
3855         mpFS->endElementNS( XML_a, XML_lightRig );
3856     }
3857     else
3858     {
3859         // a:lightRig with Word default values - Word won't open the document if this is not present
3860         mpFS->singleElementNS(XML_a, XML_lightRig, XML_rig, "threePt", XML_dir, "t");
3861     }
3862 
3863     mpFS->endElementNS( XML_a, XML_scene3d );
3864 
3865     if( !aShape3DProps.hasElements() )
3866         return;
3867 
3868     bool bBevelTPresent = false, bBevelBPresent = false;
3869     Sequence< PropertyValue > aExtrusionColorProps, aContourColorProps;
3870     sax_fastparser::FastAttributeList *aBevelTAttrList = FastSerializerHelper::createAttrList();
3871     sax_fastparser::XFastAttributeListRef xBevelTAttrList( aBevelTAttrList );
3872     sax_fastparser::FastAttributeList *aBevelBAttrList = FastSerializerHelper::createAttrList();
3873     sax_fastparser::XFastAttributeListRef xBevelBAttrList( aBevelBAttrList );
3874     sax_fastparser::FastAttributeList *aShape3DAttrList = FastSerializerHelper::createAttrList();
3875     for( sal_Int32 i=0; i < aShape3DProps.getLength(); ++i )
3876     {
3877         if( aShape3DProps[i].Name == "extrusionH" || aShape3DProps[i].Name == "contourW" || aShape3DProps[i].Name == "z" )
3878         {
3879             sal_Int32 nVal = 0, nToken = XML_none;
3880             aShape3DProps[i].Value >>= nVal;
3881             if( aShape3DProps[i].Name == "extrusionH" )
3882                 nToken = XML_extrusionH;
3883             else if( aShape3DProps[i].Name == "contourW" )
3884                 nToken = XML_contourW;
3885             else if( aShape3DProps[i].Name == "z" )
3886                 nToken = XML_z;
3887             aShape3DAttrList->add( nToken, OString::number( nVal ).getStr() );
3888         }
3889         else if( aShape3DProps[i].Name == "prstMaterial" )
3890         {
3891             OUString sVal;
3892             aShape3DProps[i].Value >>= sVal;
3893             aShape3DAttrList->add( XML_prstMaterial, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3894         }
3895         else if( aShape3DProps[i].Name == "extrusionClr" )
3896         {
3897             aShape3DProps[i].Value >>= aExtrusionColorProps;
3898         }
3899         else if( aShape3DProps[i].Name == "contourClr" )
3900         {
3901             aShape3DProps[i].Value >>= aContourColorProps;
3902         }
3903         else if( aShape3DProps[i].Name == "bevelT" || aShape3DProps[i].Name == "bevelB" )
3904         {
3905             Sequence< PropertyValue > aBevelProps;
3906             aShape3DProps[i].Value >>= aBevelProps;
3907             if ( !aBevelProps.hasElements() )
3908                 continue;
3909 
3910             sax_fastparser::FastAttributeList *aBevelAttrList = nullptr;
3911             if( aShape3DProps[i].Name == "bevelT" )
3912             {
3913                 bBevelTPresent = true;
3914                 aBevelAttrList = aBevelTAttrList;
3915             }
3916             else
3917             {
3918                 bBevelBPresent = true;
3919                 aBevelAttrList = aBevelBAttrList;
3920             }
3921             for( sal_Int32 j=0; j < aBevelProps.getLength(); ++j )
3922             {
3923                 if( aBevelProps[j].Name == "w" || aBevelProps[j].Name == "h" )
3924                 {
3925                     sal_Int32 nVal = 0, nToken = XML_none;
3926                     aBevelProps[j].Value >>= nVal;
3927                     if( aBevelProps[j].Name == "w" )
3928                         nToken = XML_w;
3929                     else if( aBevelProps[j].Name == "h" )
3930                         nToken = XML_h;
3931                     aBevelAttrList->add( nToken, OString::number( nVal ).getStr() );
3932                 }
3933                 else  if( aBevelProps[j].Name == "prst" )
3934                 {
3935                     OUString sVal;
3936                     aBevelProps[j].Value >>= sVal;
3937                     aBevelAttrList->add( XML_prst, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3938                 }
3939             }
3940 
3941         }
3942     }
3943 
3944     sax_fastparser::XFastAttributeListRef xAttrList( aShape3DAttrList );
3945     mpFS->startElementNS( XML_a, XML_sp3d, xAttrList );
3946     if( bBevelTPresent )
3947     {
3948         mpFS->singleElementNS( XML_a, XML_bevelT, xBevelTAttrList );
3949     }
3950     if( bBevelBPresent )
3951     {
3952         mpFS->singleElementNS( XML_a, XML_bevelB, xBevelBAttrList );
3953     }
3954     if( aExtrusionColorProps.hasElements() )
3955     {
3956         OUString sSchemeClr;
3957         ::Color nColor;
3958         sal_Int32 nTransparency(0);
3959         Sequence< PropertyValue > aColorTransformations;
3960         for( sal_Int32 i=0; i < aExtrusionColorProps.getLength(); ++i )
3961         {
3962             if( aExtrusionColorProps[i].Name == "schemeClr" )
3963                 aExtrusionColorProps[i].Value >>= sSchemeClr;
3964             else if( aExtrusionColorProps[i].Name == "schemeClrTransformations" )
3965                 aExtrusionColorProps[i].Value >>= aColorTransformations;
3966             else if( aExtrusionColorProps[i].Name == "rgbClr" )
3967                 aExtrusionColorProps[i].Value >>= nColor;
3968             else if( aExtrusionColorProps[i].Name == "rgbClrTransparency" )
3969                 aExtrusionColorProps[i].Value >>= nTransparency;
3970         }
3971         mpFS->startElementNS(XML_a, XML_extrusionClr);
3972 
3973         if( sSchemeClr.isEmpty() )
3974             WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
3975         else
3976             WriteColor( sSchemeClr, aColorTransformations );
3977 
3978         mpFS->endElementNS( XML_a, XML_extrusionClr );
3979     }
3980     if( aContourColorProps.hasElements() )
3981     {
3982         OUString sSchemeClr;
3983         ::Color nColor;
3984         sal_Int32 nTransparency(0);
3985         Sequence< PropertyValue > aColorTransformations;
3986         for( sal_Int32 i=0; i < aContourColorProps.getLength(); ++i )
3987         {
3988             if( aContourColorProps[i].Name == "schemeClr" )
3989                 aContourColorProps[i].Value >>= sSchemeClr;
3990             else if( aContourColorProps[i].Name == "schemeClrTransformations" )
3991                 aContourColorProps[i].Value >>= aColorTransformations;
3992             else if( aContourColorProps[i].Name == "rgbClr" )
3993                 aContourColorProps[i].Value >>= nColor;
3994             else if( aContourColorProps[i].Name == "rgbClrTransparency" )
3995                 aContourColorProps[i].Value >>= nTransparency;
3996         }
3997         mpFS->startElementNS(XML_a, XML_contourClr);
3998 
3999         if( sSchemeClr.isEmpty() )
4000             WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
4001         else
4002             WriteColor( sSchemeClr, aContourColorProps );
4003 
4004         mpFS->endElementNS( XML_a, XML_contourClr );
4005     }
4006     mpFS->endElementNS( XML_a, XML_sp3d );
4007 }
4008 
4009 void DrawingML::WriteArtisticEffect( const Reference< XPropertySet >& rXPropSet )
4010 {
4011     if( !GetProperty( rXPropSet, "InteropGrabBag" ) )
4012         return;
4013 
4014     PropertyValue aEffect;
4015     Sequence< PropertyValue > aGrabBag;
4016     mAny >>= aGrabBag;
4017     for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
4018     {
4019         if( aGrabBag[i].Name == "ArtisticEffectProperties" )
4020         {
4021             aGrabBag[i].Value >>= aEffect;
4022             break;
4023         }
4024     }
4025     sal_Int32 nEffectToken = ArtisticEffectProperties::getEffectToken( aEffect.Name );
4026     if( nEffectToken == XML_none )
4027         return;
4028 
4029     Sequence< PropertyValue > aAttrs;
4030     aEffect.Value >>= aAttrs;
4031     sax_fastparser::FastAttributeList *aAttrList = FastSerializerHelper::createAttrList();
4032     OString sRelId;
4033     for( sal_Int32 i=0; i < aAttrs.getLength(); ++i )
4034     {
4035         sal_Int32 nToken = ArtisticEffectProperties::getEffectToken( aAttrs[i].Name );
4036         if( nToken != XML_none )
4037         {
4038             sal_Int32 nVal = 0;
4039             aAttrs[i].Value >>= nVal;
4040             aAttrList->add( nToken, OString::number( nVal ).getStr() );
4041         }
4042         else if( aAttrs[i].Name == "OriginalGraphic" )
4043         {
4044             Sequence< PropertyValue > aGraphic;
4045             aAttrs[i].Value >>= aGraphic;
4046             Sequence< sal_Int8 > aGraphicData;
4047             OUString sGraphicId;
4048             for( sal_Int32 j=0; j < aGraphic.getLength(); ++j )
4049             {
4050                 if( aGraphic[j].Name == "Id" )
4051                     aGraphic[j].Value >>= sGraphicId;
4052                 else if( aGraphic[j].Name == "Data" )
4053                     aGraphic[j].Value >>= aGraphicData;
4054             }
4055             sRelId = WriteWdpPicture( sGraphicId, aGraphicData );
4056         }
4057     }
4058 
4059     mpFS->startElementNS(XML_a, XML_extLst);
4060     mpFS->startElementNS(XML_a, XML_ext, XML_uri, "{BEBA8EAE-BF5A-486C-A8C5-ECC9F3942E4B}");
4061     mpFS->startElementNS( XML_a14, XML_imgProps,
4062                           FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)).toUtf8() );
4063     mpFS->startElementNS(XML_a14, XML_imgLayer, FSNS(XML_r, XML_embed), sRelId);
4064     mpFS->startElementNS(XML_a14, XML_imgEffect);
4065 
4066     sax_fastparser::XFastAttributeListRef xAttrList( aAttrList );
4067     mpFS->singleElementNS( XML_a14, nEffectToken, xAttrList );
4068 
4069     mpFS->endElementNS( XML_a14, XML_imgEffect );
4070     mpFS->endElementNS( XML_a14, XML_imgLayer );
4071     mpFS->endElementNS( XML_a14, XML_imgProps );
4072     mpFS->endElementNS( XML_a, XML_ext );
4073     mpFS->endElementNS( XML_a, XML_extLst );
4074 }
4075 
4076 OString DrawingML::WriteWdpPicture( const OUString& rFileId, const Sequence< sal_Int8 >& rPictureData )
4077 {
4078     std::map<OUString, OUString>::iterator aCachedItem = maWdpCache.find( rFileId );
4079     if( aCachedItem != maWdpCache.end() )
4080         return OUStringToOString( aCachedItem->second, RTL_TEXTENCODING_UTF8 );
4081 
4082     OUString sFileName = "media/hdphoto" + OUString::number( mnWdpImageCounter++ ) + ".wdp";
4083     Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
4084                                                                       .appendAscii( GetComponentDir() )
4085                                                                       .append( "/" )
4086                                                                       .append( sFileName )
4087                                                                       .makeStringAndClear(),
4088                                                                       "image/vnd.ms-photo" );
4089     OUString sId;
4090     xOutStream->writeBytes( rPictureData );
4091     xOutStream->closeOutput();
4092 
4093     sId = mpFB->addRelation( mpFS->getOutputStream(),
4094                              oox::getRelationship(Relationship::HDPHOTO),
4095                              OUStringBuffer()
4096                              .appendAscii( GetRelationCompPrefix() )
4097                              .append( sFileName )
4098                              .makeStringAndClear() );
4099 
4100     maWdpCache[rFileId] = sId;
4101     return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
4102 }
4103 
4104 void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId)
4105 {
4106     uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
4107 
4108     uno::Reference<xml::dom::XDocument> dataDom;
4109     uno::Reference<xml::dom::XDocument> layoutDom;
4110     uno::Reference<xml::dom::XDocument> styleDom;
4111     uno::Reference<xml::dom::XDocument> colorDom;
4112     uno::Reference<xml::dom::XDocument> drawingDom;
4113     uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
4114     uno::Sequence<uno::Any> diagramDrawing;
4115 
4116     // retrieve the doms from the GrabBag
4117     uno::Sequence<beans::PropertyValue> propList;
4118     xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
4119     for (sal_Int32 nProp = 0; nProp < propList.getLength(); ++nProp)
4120     {
4121         OUString propName = propList[nProp].Name;
4122         if (propName == "OOXData")
4123             propList[nProp].Value >>= dataDom;
4124         else if (propName == "OOXLayout")
4125             propList[nProp].Value >>= layoutDom;
4126         else if (propName == "OOXStyle")
4127             propList[nProp].Value >>= styleDom;
4128         else if (propName == "OOXColor")
4129             propList[nProp].Value >>= colorDom;
4130         else if (propName == "OOXDrawing")
4131         {
4132             propList[nProp].Value >>= diagramDrawing;
4133             diagramDrawing[0]
4134                 >>= drawingDom; // if there is OOXDrawing property then set drawingDom here only.
4135         }
4136         else if (propName == "OOXDiagramDataRels")
4137             propList[nProp].Value >>= xDataRelSeq;
4138     }
4139 
4140     // check that we have the 4 mandatory XDocuments
4141     // if not, there was an error importing and we won't output anything
4142     if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is())
4143         return;
4144 
4145     // generate an unique id
4146     sax_fastparser::FastAttributeList* pDocPrAttrList
4147         = sax_fastparser::FastSerializerHelper::createAttrList();
4148     pDocPrAttrList->add(XML_id, OString::number(nDiagramId).getStr());
4149     OUString sName = "Diagram" + OUString::number(nDiagramId);
4150     pDocPrAttrList->add(XML_name, OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr());
4151     sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList);
4152 
4153     if (GetDocumentType() == DOCUMENT_DOCX)
4154     {
4155         mpFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef);
4156         mpFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
4157 
4158         mpFS->startElementNS(
4159             XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
4160             mpFB->getNamespaceURL(OOX_NS(dml)).toUtf8());
4161     }
4162     else
4163     {
4164         mpFS->startElementNS(XML_p, XML_nvGraphicFramePr);
4165 
4166         mpFS->singleElementNS(XML_p, XML_cNvPr, xDocPrAttrListRef);
4167         mpFS->singleElementNS(XML_p, XML_cNvGraphicFramePr);
4168 
4169         mpFS->startElementNS(XML_p, XML_nvPr);
4170         mpFS->startElementNS(XML_p, XML_extLst);
4171         // change tracking extension - required in PPTX
4172         mpFS->startElementNS(XML_p, XML_ext, XML_uri, "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}");
4173         mpFS->singleElementNS(XML_p14, XML_modId,
4174             FSNS(XML_xmlns, XML_p14), mpFB->getNamespaceURL(OOX_NS(p14)).toUtf8(),
4175             XML_val,
4176             OString::number(comphelper::rng::uniform_uint_distribution(1, SAL_MAX_UINT32)));
4177         mpFS->endElementNS(XML_p, XML_ext);
4178         mpFS->endElementNS(XML_p, XML_extLst);
4179         mpFS->endElementNS(XML_p, XML_nvPr);
4180 
4181         mpFS->endElementNS(XML_p, XML_nvGraphicFramePr);
4182 
4183         // store size and position of background shape instead of group shape
4184         // as some shapes may be outside
4185         css::uno::Reference<css::drawing::XShapes> xShapes(rXShape, uno::UNO_QUERY);
4186         if (xShapes.is() && xShapes->hasElements())
4187         {
4188             css::uno::Reference<css::drawing::XShape> xShapeBg(xShapes->getByIndex(0),
4189                                                                uno::UNO_QUERY);
4190             awt::Point aPos = xShapeBg->getPosition();
4191             awt::Size aSize = xShapeBg->getSize();
4192             WriteTransformation(
4193                 tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)),
4194                 XML_p, false, false, 0, false);
4195         }
4196 
4197         mpFS->startElementNS(XML_a, XML_graphic);
4198     }
4199 
4200     mpFS->startElementNS(XML_a, XML_graphicData, XML_uri,
4201                          "http://schemas.openxmlformats.org/drawingml/2006/diagram");
4202 
4203     OUString sRelationCompPrefix = OUString::createFromAscii(GetRelationCompPrefix());
4204 
4205     // add data relation
4206     OUString dataFileName = "diagrams/data" + OUString::number(nDiagramId) + ".xml";
4207     OString dataRelId = OUStringToOString(
4208         mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDATA),
4209                           sRelationCompPrefix + dataFileName),
4210         RTL_TEXTENCODING_UTF8);
4211 
4212     // add layout relation
4213     OUString layoutFileName = "diagrams/layout" + OUString::number(nDiagramId) + ".xml";
4214     OString layoutRelId
4215         = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4216                                               oox::getRelationship(Relationship::DIAGRAMLAYOUT),
4217                                               sRelationCompPrefix + layoutFileName),
4218                             RTL_TEXTENCODING_UTF8);
4219 
4220     // add style relation
4221     OUString styleFileName = "diagrams/quickStyle" + OUString::number(nDiagramId) + ".xml";
4222     OString styleRelId
4223         = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4224                                               oox::getRelationship(Relationship::DIAGRAMQUICKSTYLE),
4225                                               sRelationCompPrefix + styleFileName),
4226                             RTL_TEXTENCODING_UTF8);
4227 
4228     // add color relation
4229     OUString colorFileName = "diagrams/colors" + OUString::number(nDiagramId) + ".xml";
4230     OString colorRelId
4231         = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4232                                               oox::getRelationship(Relationship::DIAGRAMCOLORS),
4233                                               sRelationCompPrefix + colorFileName),
4234                             RTL_TEXTENCODING_UTF8);
4235 
4236     OUString drawingFileName;
4237     if (drawingDom.is())
4238     {
4239         // add drawing relation
4240         drawingFileName = "diagrams/drawing" + OUString::number(nDiagramId) + ".xml";
4241         OUString drawingRelId = mpFB->addRelation(
4242             mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDRAWING),
4243             sRelationCompPrefix + drawingFileName);
4244 
4245         // the data dom contains a reference to the drawing relation. We need to update it with the new generated
4246         // relation value before writing the dom to a file
4247 
4248         // Get the dsp:damaModelExt node from the dom
4249         uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS(
4250             "http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt");
4251 
4252         // There must be one element only so get it
4253         uno::Reference<xml::dom::XNode> node = nodeList->item(0);
4254 
4255         // Get the list of attributes of the node
4256         uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes();
4257 
4258         // Get the node with the relId attribute and set its new value
4259         uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId");
4260         relIdNode->setNodeValue(drawingRelId);
4261     }
4262 
4263     mpFS->singleElementNS(XML_dgm, XML_relIds,
4264         FSNS(XML_xmlns, XML_dgm), mpFB->getNamespaceURL(OOX_NS(dmlDiagram)).toUtf8(),
4265         FSNS(XML_xmlns, XML_r), mpFB->getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
4266         FSNS(XML_r, XML_dm), dataRelId, FSNS(XML_r, XML_lo), layoutRelId,
4267         FSNS(XML_r, XML_qs), styleRelId, FSNS(XML_r, XML_cs), colorRelId);
4268 
4269     mpFS->endElementNS(XML_a, XML_graphicData);
4270     mpFS->endElementNS(XML_a, XML_graphic);
4271 
4272     uno::Reference<xml::sax::XSAXSerializable> serializer;
4273     uno::Reference<xml::sax::XWriter> writer
4274         = xml::sax::Writer::create(comphelper::getProcessComponentContext());
4275 
4276     OUString sDir = OUString::createFromAscii(GetComponentDir());
4277 
4278     // write data file
4279     serializer.set(dataDom, uno::UNO_QUERY);
4280     uno::Reference<io::XOutputStream> xDataOutputStream = mpFB->openFragmentStream(
4281         sDir + "/" + dataFileName,
4282         "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
4283     writer->setOutputStream(xDataOutputStream);
4284     serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4285                           uno::Sequence<beans::StringPair>());
4286 
4287     // write the associated Images and rels for data file
4288     writeDiagramRels(xDataRelSeq, xDataOutputStream, "OOXDiagramDataRels", nDiagramId);
4289 
4290     // write layout file
4291     serializer.set(layoutDom, uno::UNO_QUERY);
4292     writer->setOutputStream(mpFB->openFragmentStream(
4293         sDir + "/" + layoutFileName,
4294         "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"));
4295     serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4296                           uno::Sequence<beans::StringPair>());
4297 
4298     // write style file
4299     serializer.set(styleDom, uno::UNO_QUERY);
4300     writer->setOutputStream(mpFB->openFragmentStream(
4301         sDir + "/" + styleFileName,
4302         "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"));
4303     serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4304                           uno::Sequence<beans::StringPair>());
4305 
4306     // write color file
4307     serializer.set(colorDom, uno::UNO_QUERY);
4308     writer->setOutputStream(mpFB->openFragmentStream(
4309         sDir + "/" + colorFileName,
4310         "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"));
4311     serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4312                           uno::Sequence<beans::StringPair>());
4313 
4314     // write drawing file
4315     if (drawingDom.is())
4316     {
4317         serializer.set(drawingDom, uno::UNO_QUERY);
4318         uno::Reference<io::XOutputStream> xDrawingOutputStream = mpFB->openFragmentStream(
4319             sDir + "/" + drawingFileName, "application/vnd.ms-office.drawingml.diagramDrawing+xml");
4320         writer->setOutputStream(xDrawingOutputStream);
4321         serializer->serialize(
4322             uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4323             uno::Sequence<beans::StringPair>());
4324 
4325         // write the associated Images and rels for drawing file
4326         uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq;
4327         diagramDrawing[1] >>= xDrawingRelSeq;
4328         writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, "OOXDiagramDrawingRels", nDiagramId);
4329     }
4330 }
4331 
4332 void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq,
4333                                  const uno::Reference<io::XOutputStream>& xOutStream,
4334                                  const OUString& sGrabBagProperyName, int nDiagramId)
4335 {
4336     // add image relationships of OOXData, OOXDiagram
4337     OUString sType(oox::getRelationship(Relationship::IMAGE));
4338     uno::Reference<xml::sax::XWriter> xWriter
4339         = xml::sax::Writer::create(comphelper::getProcessComponentContext());
4340     xWriter->setOutputStream(xOutStream);
4341 
4342     // retrieve the relationships from Sequence
4343     for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++)
4344     {
4345         // diagramDataRelTuple[0] => RID,
4346         // diagramDataRelTuple[1] => xInputStream
4347         // diagramDataRelTuple[2] => extension
4348         uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j];
4349 
4350         OUString sRelId;
4351         OUString sExtension;
4352         diagramDataRelTuple[0] >>= sRelId;
4353         diagramDataRelTuple[2] >>= sExtension;
4354         OUString sContentType;
4355         if (sExtension.equalsIgnoreAsciiCase(".WMF"))
4356             sContentType = "image/x-wmf";
4357         else
4358             sContentType = "image/" + sExtension.copy(1);
4359         sRelId = sRelId.copy(3);
4360 
4361         StreamDataSequence dataSeq;
4362         diagramDataRelTuple[1] >>= dataSeq;
4363         uno::Reference<io::XInputStream> dataImagebin(
4364             new ::comphelper::SequenceInputStream(dataSeq));
4365 
4366         //nDiagramId is used to make the name unique irrespective of the number of smart arts.
4367         OUString sFragment = "media/" + sGrabBagProperyName + OUString::number(nDiagramId) + "_"
4368                              + OUString::number(j) + sExtension;
4369 
4370         PropertySet aProps(xOutStream);
4371         aProps.setAnyProperty(PROP_RelId, uno::makeAny(sRelId.toInt32()));
4372 
4373         mpFB->addRelation(xOutStream, sType, "../" + sFragment);
4374 
4375         OUString sDir = OUString::createFromAscii(GetComponentDir());
4376         uno::Reference<io::XOutputStream> xBinOutStream
4377             = mpFB->openFragmentStream(sDir + "/" + sFragment, sContentType);
4378 
4379         try
4380         {
4381             comphelper::OStorageHelper::CopyInputToOutput(dataImagebin, xBinOutStream);
4382         }
4383         catch (const uno::Exception&)
4384         {
4385             TOOLS_WARN_EXCEPTION("oox.drawingml", "DrawingML::writeDiagramRels Failed to copy grabbaged Image");
4386         }
4387         dataImagebin->closeInput();
4388     }
4389 }
4390 
4391 }
4392 }
4393 
4394 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4395