Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / oox / source / export / drawingml.cxx
blob2cc160d05b57402d5f823083768f432b720ed698
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
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 <comphelper/processfactory.hxx>
41 #include <i18nlangtag/languagetag.hxx>
43 #include <cstdio>
44 #include <numeric>
45 #include <com/sun/star/awt/CharSet.hpp>
46 #include <com/sun/star/awt/FontDescriptor.hpp>
47 #include <com/sun/star/awt/FontSlant.hpp>
48 #include <com/sun/star/awt/FontStrikeout.hpp>
49 #include <com/sun/star/awt/FontWeight.hpp>
50 #include <com/sun/star/awt/FontUnderline.hpp>
51 #include <com/sun/star/awt/Gradient.hpp>
52 #include <com/sun/star/beans/XPropertySet.hpp>
53 #include <com/sun/star/beans/XPropertyState.hpp>
54 #include <com/sun/star/beans/Property.hpp>
55 #include <com/sun/star/beans/XPropertySetInfo.hpp>
56 #include <com/sun/star/container/XEnumerationAccess.hpp>
57 #include <com/sun/star/container/XIndexAccess.hpp>
58 #include <com/sun/star/document/XStorageBasedDocument.hpp>
59 #include <com/sun/star/drawing/BitmapMode.hpp>
60 #include <com/sun/star/drawing/ColorMode.hpp>
61 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
62 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
63 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
64 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
65 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
66 #include <com/sun/star/drawing/Hatch.hpp>
67 #include <com/sun/star/drawing/LineDash.hpp>
68 #include <com/sun/star/drawing/LineJoint.hpp>
69 #include <com/sun/star/drawing/LineStyle.hpp>
70 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
71 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
72 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
73 #include <com/sun/star/drawing/XShape.hpp>
74 #include <com/sun/star/drawing/XShapes.hpp>
75 #include <com/sun/star/drawing/FillStyle.hpp>
76 #include <com/sun/star/embed/ElementModes.hpp>
77 #include <com/sun/star/graphic/XGraphic.hpp>
78 #include <com/sun/star/i18n/ScriptType.hpp>
79 #include <com/sun/star/i18n/BreakIterator.hpp>
80 #include <com/sun/star/i18n/XBreakIterator.hpp>
81 #include <com/sun/star/io/XOutputStream.hpp>
82 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
83 #include <com/sun/star/style/LineSpacing.hpp>
84 #include <com/sun/star/style/LineSpacingMode.hpp>
85 #include <com/sun/star/text/WritingMode.hpp>
86 #include <com/sun/star/text/WritingMode2.hpp>
87 #include <com/sun/star/text/GraphicCrop.hpp>
88 #include <com/sun/star/text/XText.hpp>
89 #include <com/sun/star/text/XTextContent.hpp>
90 #include <com/sun/star/text/XTextField.hpp>
91 #include <com/sun/star/text/XTextRange.hpp>
92 #include <com/sun/star/style/CaseMap.hpp>
93 #include <com/sun/star/xml/dom/XNodeList.hpp>
94 #include <com/sun/star/xml/sax/Writer.hpp>
95 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
97 #include <comphelper/random.hxx>
98 #include <comphelper/seqstream.hxx>
99 #include <comphelper/storagehelper.hxx>
100 #include <comphelper/xmltools.hxx>
101 #include <o3tl/any.hxx>
102 #include <tools/stream.hxx>
103 #include <unotools/fontdefs.hxx>
104 #include <vcl/cvtgrf.hxx>
105 #include <vcl/graph.hxx>
106 #include <vcl/settings.hxx>
107 #include <vcl/GraphicObject.hxx>
108 #include <vcl/svapp.hxx>
109 #include <rtl/strbuf.hxx>
110 #include <sfx2/app.hxx>
111 #include <svl/languageoptions.hxx>
112 #include <filter/msfilter/escherex.hxx>
113 #include <filter/msfilter/util.hxx>
114 #include <editeng/outlobj.hxx>
115 #include <editeng/svxenum.hxx>
116 #include <editeng/unonames.hxx>
117 #include <editeng/unoprnms.hxx>
118 #include <editeng/flditem.hxx>
119 #include <svx/sdtfsitm.hxx>
120 #include <svx/svdoashp.hxx>
121 #include <svx/svdomedia.hxx>
122 #include <svx/unoapi.hxx>
123 #include <svx/unoshape.hxx>
124 #include <svx/EnhancedCustomShape2d.hxx>
126 using namespace ::css;
127 using namespace ::css::beans;
128 using namespace ::css::drawing;
129 using namespace ::css::i18n;
130 using namespace ::css::style;
131 using namespace ::css::text;
132 using namespace ::css::uno;
133 using namespace ::css::container;
135 using ::css::io::XOutputStream;
136 using ::sax_fastparser::FSHelperPtr;
137 using ::sax_fastparser::FastSerializerHelper;
139 namespace
141 /// Extracts start or end alpha information from a transparency gradient.
142 sal_Int32 GetAlphaFromTransparenceGradient(const awt::Gradient& rGradient, bool bStart)
144 // Our alpha is a gray color value.
145 sal_uInt8 nRed = ::Color(bStart ? rGradient.StartColor : rGradient.EndColor).GetRed();
146 // drawingML alpha is a percentage on a 0..100000 scale.
147 return (255 - nRed) * oox::drawingml::MAX_PERCENT / 255;
151 namespace oox {
152 namespace drawingml {
154 URLTransformer::~URLTransformer()
158 OUString URLTransformer::getTransformedString(const OUString& rString) const
160 return rString;
163 bool URLTransformer::isExternalURL(const OUString& /*rURL*/) const
165 return true;
168 static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rDashName )
170 css::uno::Reference<css::lang::XMultiServiceFactory> xFact(xModel, css::uno::UNO_QUERY);
171 css::uno::Reference<css::container::XNameAccess> xNameAccess(
172 xFact->createInstance("com.sun.star.drawing.DashTable"),
173 css::uno::UNO_QUERY );
174 if(xNameAccess.is())
176 if (!xNameAccess->hasByName(rDashName))
177 return css::uno::Any();
179 return xNameAccess->getByName(rDashName);
182 return css::uno::Any();
185 namespace
187 void WriteRadialGradientPath(const awt::Gradient& rGradient, const FSHelperPtr& pFS)
189 pFS->startElementNS(XML_a, XML_path, XML_path, "circle");
191 // Write the focus rectangle. Work with the focus point, and assume
192 // that it extends 50% in all directions. The below
193 // left/top/right/bottom values are percentages, where 0 means the
194 // edge of the tile rectangle and 100% means the center of it.
195 rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList(
196 sax_fastparser::FastSerializerHelper::createAttrList());
197 sal_Int32 nLeftPercent = rGradient.XOffset * 2 - 50;
198 pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT));
199 sal_Int32 nTopPercent = rGradient.YOffset * 2 - 50;
200 pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT));
201 sal_Int32 nRightPercent = (100 - rGradient.XOffset) * 2 - 50;
202 pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT));
203 sal_Int32 nBottomPercent = (100 - rGradient.YOffset) * 2 - 50;
204 pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT));
205 sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList.get());
206 pFS->singleElementNS(XML_a, XML_fillToRect, xAttributeList);
208 pFS->endElementNS(XML_a, XML_path);
212 // not thread safe
213 int DrawingML::mnImageCounter = 1;
214 int DrawingML::mnWdpImageCounter = 1;
215 std::map<OUString, OUString> DrawingML::maWdpCache;
217 sal_Int16 DrawingML::GetScriptType(const OUString& rStr)
219 if (rStr.getLength() > 0)
221 static Reference<css::i18n::XBreakIterator> xBreakIterator =
222 css::i18n::BreakIterator::create(comphelper::getProcessComponentContext());
224 sal_Int16 nScriptType = xBreakIterator->getScriptType(rStr, 0);
226 if (nScriptType == css::i18n::ScriptType::WEAK)
228 sal_Int32 nPos = xBreakIterator->nextScript(rStr, 0, nScriptType);
229 if (nPos < rStr.getLength())
230 nScriptType = xBreakIterator->getScriptType(rStr, nPos);
234 if (nScriptType != css::i18n::ScriptType::WEAK)
235 return nScriptType;
238 return css::i18n::ScriptType::LATIN;
241 void DrawingML::ResetCounters()
243 mnImageCounter = 1;
244 mnWdpImageCounter = 1;
245 maWdpCache.clear();
248 bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName )
252 mAny = rXPropertySet->getPropertyValue(aName);
253 if (mAny.hasValue())
254 return true;
256 catch( const Exception& )
258 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
260 return false;
263 bool DrawingML::GetPropertyAndState( const Reference< XPropertySet >& rXPropertySet, const Reference< XPropertyState >& rXPropertyState, const OUString& aName, PropertyState& eState )
267 mAny = rXPropertySet->getPropertyValue(aName);
268 if (mAny.hasValue())
270 eState = rXPropertyState->getPropertyState(aName);
271 return true;
274 catch( const Exception& )
276 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
278 return false;
281 void DrawingML::WriteColor( ::Color nColor, sal_Int32 nAlpha )
283 // Transparency is a separate element.
284 OString sColor = OString::number( sal_uInt32(nColor) & 0x00FFFFFF, 16 );
285 if( sColor.getLength() < 6 )
287 OStringBuffer sBuf( "0" );
288 int remains = 5 - sColor.getLength();
290 while( remains > 0 )
292 sBuf.append( "0" );
293 remains--;
296 sBuf.append( sColor );
298 sColor = sBuf.getStr();
300 if( nAlpha < MAX_PERCENT )
302 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
303 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
304 mpFS->endElementNS( XML_a, XML_srgbClr );
307 else
309 mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
313 void DrawingML::WriteColor( const OUString& sColorSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
315 // prevent writing a tag with empty val attribute
316 if( sColorSchemeName.isEmpty() )
317 return;
319 if( aTransformations.hasElements() )
321 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
322 WriteColorTransformations( aTransformations, nAlpha );
323 mpFS->endElementNS( XML_a, XML_schemeClr );
325 else if(nAlpha < MAX_PERCENT)
327 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
328 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
329 mpFS->endElementNS( XML_a, XML_schemeClr );
331 else
333 mpFS->singleElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
337 void DrawingML::WriteColorTransformations( const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
339 for( const auto& rTransformation : aTransformations )
341 sal_Int32 nToken = Color::getColorTransformationToken( rTransformation.Name );
342 if( nToken != XML_TOKEN_INVALID && rTransformation.Value.hasValue() )
344 if(nToken == XML_alpha && nAlpha < MAX_PERCENT)
346 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nAlpha));
348 else
350 sal_Int32 nValue = rTransformation.Value.get<sal_Int32>();
351 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nValue));
357 void DrawingML::WriteSolidFill( ::Color nColor, sal_Int32 nAlpha )
359 mpFS->startElementNS(XML_a, XML_solidFill);
360 WriteColor( nColor, nAlpha );
361 mpFS->endElementNS( XML_a, XML_solidFill );
364 void DrawingML::WriteSolidFill( const OUString& sSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
366 mpFS->startElementNS(XML_a, XML_solidFill);
367 WriteColor( sSchemeName, aTransformations, nAlpha );
368 mpFS->endElementNS( XML_a, XML_solidFill );
371 void DrawingML::WriteSolidFill( const Reference< XPropertySet >& rXPropSet )
373 // get fill color
374 if ( !GetProperty( rXPropSet, "FillColor" ) )
375 return;
376 sal_uInt32 nFillColor = mAny.get<sal_uInt32>();
378 // get InteropGrabBag and search the relevant attributes
379 OUString sColorFillScheme;
380 sal_uInt32 nOriginalColor = 0;
381 Sequence< PropertyValue > aStyleProperties, aTransformations;
382 if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
384 Sequence< PropertyValue > aGrabBag;
385 mAny >>= aGrabBag;
386 for( const auto& rProp : std::as_const(aGrabBag) )
388 if( rProp.Name == "SpPrSolidFillSchemeClr" )
389 rProp.Value >>= sColorFillScheme;
390 else if( rProp.Name == "OriginalSolidFillClr" )
391 rProp.Value >>= nOriginalColor;
392 else if( rProp.Name == "StyleFillRef" )
393 rProp.Value >>= aStyleProperties;
394 else if( rProp.Name == "SpPrSolidFillSchemeClrTransformations" )
395 rProp.Value >>= aTransformations;
399 sal_Int32 nAlpha = MAX_PERCENT;
400 if( GetProperty( rXPropSet, "FillTransparence" ) )
402 sal_Int32 nTransparency = 0;
403 mAny >>= nTransparency;
404 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
405 nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
408 // write XML
409 if ( nFillColor != nOriginalColor )
411 // the user has set a different color for the shape
412 WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
414 else if ( !sColorFillScheme.isEmpty() )
416 // the shape had a scheme color and the user didn't change it
417 WriteSolidFill( sColorFillScheme, aTransformations, nAlpha );
419 else if ( aStyleProperties.hasElements() )
421 sal_uInt32 nThemeColor = 0;
422 sal_Int32 nThemeAlpha = MAX_PERCENT;
423 for( const auto& rStyleProp : std::as_const(aStyleProperties) )
425 if( rStyleProp.Name == "Color" )
427 rStyleProp.Value >>= nThemeColor;
429 else if(rStyleProp.Name == "Transformations" )
431 Sequence< PropertyValue > aStyleTransformations;
432 rStyleProp.Value >>= aStyleTransformations;
433 auto pProp = std::find_if(std::cbegin(aStyleTransformations), std::cend(aStyleTransformations),
434 [](const PropertyValue& rProp) { return rProp.Name == "alpha"; });
435 if (pProp != std::cend(aStyleTransformations))
436 pProp->Value >>= nThemeAlpha;
439 if ( nFillColor != nThemeColor || nAlpha != nThemeAlpha )
440 // the shape contains a theme but it wasn't being used
441 WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
443 // in case the shape used the style color and the user didn't change it,
444 // we must not write a <a: solidFill> tag.
446 else
448 // the shape had a custom color and the user didn't change it
449 WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
453 void DrawingML::WriteGradientStop(sal_uInt16 nStop, ::Color nColor, sal_Int32 nAlpha)
455 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nStop * 1000));
456 WriteColor(nColor, nAlpha);
457 mpFS->endElementNS( XML_a, XML_gs );
460 ::Color DrawingML::ColorWithIntensity( sal_uInt32 nColor, sal_uInt32 nIntensity )
462 return ::Color(( ( ( nColor & 0xff ) * nIntensity ) / 100 )
463 | ( ( ( ( ( nColor & 0xff00 ) >> 8 ) * nIntensity ) / 100 ) << 8 )
464 | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 ));
467 bool DrawingML::EqualGradients( awt::Gradient aGradient1, awt::Gradient aGradient2 )
469 return aGradient1.Style == aGradient2.Style &&
470 aGradient1.StartColor == aGradient2.StartColor &&
471 aGradient1.EndColor == aGradient2.EndColor &&
472 aGradient1.Angle == aGradient2.Angle &&
473 aGradient1.Border == aGradient2.Border &&
474 aGradient1.XOffset == aGradient2.XOffset &&
475 aGradient1.YOffset == aGradient2.YOffset &&
476 aGradient1.StartIntensity == aGradient2.StartIntensity &&
477 aGradient1.EndIntensity == aGradient2.EndIntensity &&
478 aGradient1.StepCount == aGradient2.StepCount;
481 void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet )
483 awt::Gradient aGradient;
484 awt::Gradient aTransparenceGradient;
485 if (GetProperty(rXPropSet, "FillGradient"))
487 aGradient = *o3tl::doAccess<awt::Gradient>(mAny);
489 // get InteropGrabBag and search the relevant attributes
490 awt::Gradient aOriginalGradient;
491 Sequence< PropertyValue > aGradientStops;
492 if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
494 Sequence< PropertyValue > aGrabBag;
495 mAny >>= aGrabBag;
496 for( const auto& rProp : std::as_const(aGrabBag) )
497 if( rProp.Name == "GradFillDefinition" )
498 rProp.Value >>= aGradientStops;
499 else if( rProp.Name == "OriginalGradFill" )
500 rProp.Value >>= aOriginalGradient;
503 // check if an ooxml gradient had been imported and if the user has modified it
504 // Gradient grab-bag depends on theme grab-bag, which is implemented
505 // only for DOCX.
506 if( EqualGradients( aOriginalGradient, aGradient ) && GetDocumentType() == DOCUMENT_DOCX)
508 // If we have no gradient stops that means original gradient were defined by a theme.
509 if( aGradientStops.hasElements() )
511 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
512 WriteGrabBagGradientFill(aGradientStops, aGradient);
513 mpFS->endElementNS( XML_a, XML_gradFill );
516 else
518 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
519 if( GetProperty(rXPropSet, "FillTransparenceGradient") )
520 aTransparenceGradient = *o3tl::doAccess<awt::Gradient>(mAny);
521 WriteGradientFill(aGradient, aTransparenceGradient);
522 mpFS->endElementNS( XML_a, XML_gradFill );
527 void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, awt::Gradient rGradient )
529 // write back the original gradient
530 mpFS->startElementNS(XML_a, XML_gsLst);
532 // get original stops and write them
533 for( const auto& rGradientStop : aGradientStops )
535 Sequence< PropertyValue > aGradientStop;
536 rGradientStop.Value >>= aGradientStop;
538 // get values
539 OUString sSchemeClr;
540 double nPos = 0;
541 sal_Int16 nTransparency = 0;
542 ::Color nRgbClr;
543 Sequence< PropertyValue > aTransformations;
544 for( const auto& rProp : std::as_const(aGradientStop) )
546 if( rProp.Name == "SchemeClr" )
547 rProp.Value >>= sSchemeClr;
548 else if( rProp.Name == "RgbClr" )
549 rProp.Value >>= nRgbClr;
550 else if( rProp.Name == "Pos" )
551 rProp.Value >>= nPos;
552 else if( rProp.Name == "Transparency" )
553 rProp.Value >>= nTransparency;
554 else if( rProp.Name == "Transformations" )
555 rProp.Value >>= aTransformations;
557 // write stop
558 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nPos * 100000.0).getStr());
559 if( sSchemeClr.isEmpty() )
561 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
562 sal_Int32 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
563 WriteColor( nRgbClr, nAlpha );
565 else
567 WriteColor( sSchemeClr, aTransformations );
569 mpFS->endElementNS( XML_a, XML_gs );
571 mpFS->endElementNS( XML_a, XML_gsLst );
573 switch (rGradient.Style)
575 default:
576 mpFS->singleElementNS(
577 XML_a, XML_lin, XML_ang,
578 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
579 break;
580 case awt::GradientStyle_RADIAL:
581 WriteRadialGradientPath(rGradient, mpFS);
582 break;
586 void DrawingML::WriteGradientFill(awt::Gradient rGradient, awt::Gradient rTransparenceGradient,
587 const uno::Reference<beans::XPropertySet>& rXPropSet)
589 switch( rGradient.Style )
591 default:
592 case awt::GradientStyle_LINEAR:
594 mpFS->startElementNS(XML_a, XML_gsLst);
595 sal_Int32 nStartAlpha;
596 sal_Int32 nEndAlpha;
597 if( rXPropSet.is() && GetProperty(rXPropSet, "FillTransparence") )
599 sal_Int32 nTransparency = 0;
600 mAny >>= nTransparency;
601 nStartAlpha = nEndAlpha = (MAX_PERCENT - (PER_PERCENT * nTransparency));
603 else
605 nStartAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, true);
606 nEndAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, false);
608 WriteGradientStop(rGradient.Border, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity),
609 nStartAlpha);
610 WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
611 nEndAlpha);
612 mpFS->endElementNS( XML_a, XML_gsLst );
613 mpFS->singleElementNS(
614 XML_a, XML_lin, XML_ang,
615 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
616 break;
619 case awt::GradientStyle_AXIAL:
621 mpFS->startElementNS(XML_a, XML_gsLst);
622 sal_Int32 nStartAlpha;
623 sal_Int32 nEndAlpha;
624 if (rXPropSet.is() && GetProperty(rXPropSet, "FillTransparence"))
626 sal_Int32 nTransparency = 0;
627 mAny >>= nTransparency;
628 nStartAlpha = nEndAlpha = (MAX_PERCENT - (PER_PERCENT * nTransparency));
630 else
632 nStartAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, true);
633 nEndAlpha = GetAlphaFromTransparenceGradient(rTransparenceGradient, false);
635 WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
636 nEndAlpha);
637 WriteGradientStop(50, ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity),
638 nStartAlpha);
639 WriteGradientStop(100, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity),
640 nEndAlpha);
641 mpFS->endElementNS(XML_a, XML_gsLst);
642 mpFS->singleElementNS(
643 XML_a, XML_lin, XML_ang,
644 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
645 break;
648 case awt::GradientStyle_RADIAL:
650 mpFS->startElementNS(XML_a, XML_gsLst);
651 WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity));
652 if (rGradient.Border > 0 && rGradient.Border < 100)
653 // Map border to an additional gradient stop, which has the
654 // same color as the final stop.
655 WriteGradientStop(
656 100 - rGradient.Border,
657 ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity));
658 WriteGradientStop(100,
659 ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity));
660 mpFS->endElementNS(XML_a, XML_gsLst);
662 WriteRadialGradientPath(rGradient, mpFS);
663 break;
665 /* I don't see how to apply transformation to gradients, so
666 * elliptical will end as radial and square as
667 * rectangular. also position offsets are not applied */
668 case awt::GradientStyle_ELLIPTICAL:
669 case awt::GradientStyle_RECT:
670 case awt::GradientStyle_SQUARE:
671 mpFS->startElementNS(XML_a, XML_gsLst);
672 WriteGradientStop( 0, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
673 WriteGradientStop( 100, ColorWithIntensity( rGradient.StartColor, rGradient.StartIntensity ) );
674 mpFS->endElementNS( XML_a, XML_gsLst );
675 mpFS->singleElementNS( XML_a, XML_path,
676 XML_path, ( rGradient.Style == awt::GradientStyle_RADIAL || rGradient.Style == awt::GradientStyle_ELLIPTICAL ) ? "circle" : "rect" );
677 break;
681 void DrawingML::WriteLineArrow( const Reference< XPropertySet >& rXPropSet, bool bLineStart )
683 ESCHER_LineEnd eLineEnd;
684 sal_Int32 nArrowLength;
685 sal_Int32 nArrowWidth;
687 if ( EscherPropertyContainer::GetLineArrow( bLineStart, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
689 const char* len;
690 const char* type;
691 const char* width;
693 switch( nArrowLength )
695 case ESCHER_LineShortArrow:
696 len = "sm";
697 break;
698 default:
699 case ESCHER_LineMediumLenArrow:
700 len = "med";
701 break;
702 case ESCHER_LineLongArrow:
703 len = "lg";
704 break;
707 switch( eLineEnd )
709 default:
710 case ESCHER_LineNoEnd:
711 type = "none";
712 break;
713 case ESCHER_LineArrowEnd:
714 type = "triangle";
715 break;
716 case ESCHER_LineArrowStealthEnd:
717 type = "stealth";
718 break;
719 case ESCHER_LineArrowDiamondEnd:
720 type = "diamond";
721 break;
722 case ESCHER_LineArrowOvalEnd:
723 type = "oval";
724 break;
725 case ESCHER_LineArrowOpenEnd:
726 type = "arrow";
727 break;
730 switch( nArrowWidth )
732 case ESCHER_LineNarrowArrow:
733 width = "sm";
734 break;
735 default:
736 case ESCHER_LineMediumWidthArrow:
737 width = "med";
738 break;
739 case ESCHER_LineWideArrow:
740 width = "lg";
741 break;
744 mpFS->singleElementNS( XML_a, bLineStart ? XML_headEnd : XML_tailEnd,
745 XML_len, len,
746 XML_type, type,
747 XML_w, width );
751 void DrawingML::WriteOutline( const Reference<XPropertySet>& rXPropSet, Reference< frame::XModel > const & xModel )
753 drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
755 if (GetProperty(rXPropSet, "LineStyle"))
756 mAny >>= aLineStyle;
758 sal_uInt32 nLineWidth = 0;
759 ::Color nColor;
760 sal_Int32 nColorAlpha = MAX_PERCENT;
761 bool bColorSet = false;
762 const char* cap = nullptr;
763 drawing::LineDash aLineDash;
764 bool bDashSet = false;
765 bool bNoFill = false;
767 // get InteropGrabBag and search the relevant attributes
768 OUString sColorFillScheme;
770 ::Color nOriginalColor;
771 ::Color nStyleColor;
772 sal_uInt32 nStyleLineWidth = 0;
774 Sequence<PropertyValue> aStyleProperties;
775 Sequence<PropertyValue> aTransformations;
777 drawing::LineStyle aStyleLineStyle(drawing::LineStyle_NONE);
778 drawing::LineJoint aStyleLineJoint(drawing::LineJoint_NONE);
780 if (GetProperty(rXPropSet, "InteropGrabBag"))
782 Sequence<PropertyValue> aGrabBag;
783 mAny >>= aGrabBag;
785 for (const auto& rProp : std::as_const(aGrabBag))
787 if( rProp.Name == "SpPrLnSolidFillSchemeClr" )
788 rProp.Value >>= sColorFillScheme;
789 else if( rProp.Name == "OriginalLnSolidFillClr" )
790 rProp.Value >>= nOriginalColor;
791 else if( rProp.Name == "StyleLnRef" )
792 rProp.Value >>= aStyleProperties;
793 else if( rProp.Name == "SpPrLnSolidFillSchemeClrTransformations" )
794 rProp.Value >>= aTransformations;
796 for (const auto& rStyleProp : std::as_const(aStyleProperties))
798 if( rStyleProp.Name == "Color" )
799 rStyleProp.Value >>= nStyleColor;
800 else if( rStyleProp.Name == "LineStyle" )
801 rStyleProp.Value >>= aStyleLineStyle;
802 else if( rStyleProp.Name == "LineJoint" )
803 rStyleProp.Value >>= aStyleLineJoint;
804 else if( rStyleProp.Name == "LineWidth" )
805 rStyleProp.Value >>= nStyleLineWidth;
809 if (GetProperty(rXPropSet, "LineWidth"))
810 mAny >>= nLineWidth;
812 switch (aLineStyle)
814 case drawing::LineStyle_NONE:
815 bNoFill = true;
816 break;
817 case drawing::LineStyle_DASH:
818 if (GetProperty(rXPropSet, "LineDash"))
820 aLineDash = mAny.get<drawing::LineDash>();
821 //this query is good for shapes, but in the case of charts it returns 0 values
822 if (aLineDash.Dots == 0 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 0) {
823 OUString aLineDashName;
824 if (GetProperty(rXPropSet, "LineDashName"))
825 mAny >>= aLineDashName;
826 if (!aLineDashName.isEmpty() && xModel) {
827 css::uno::Any aAny = getLineDash(xModel, aLineDashName);
828 aAny >>= aLineDash;
832 else
834 //export the linestyle of chart wall (plot area) and chart page
835 OUString aLineDashName;
836 if (GetProperty(rXPropSet, "LineDashName"))
837 mAny >>= aLineDashName;
838 if (!aLineDashName.isEmpty() && xModel) {
839 css::uno::Any aAny = getLineDash(xModel, aLineDashName);
840 aAny >>= aLineDash;
843 bDashSet = true;
844 if (aLineDash.Style == DashStyle_ROUND || aLineDash.Style == DashStyle_ROUNDRELATIVE)
846 cap = "rnd";
849 SAL_INFO("oox.shape", "dash dots: " << aLineDash.Dots << " dashes: " << aLineDash.Dashes
850 << " dotlen: " << aLineDash.DotLen << " dashlen: " << aLineDash.DashLen << " distance: " << aLineDash.Distance);
852 [[fallthrough]];
853 case drawing::LineStyle_SOLID:
854 default:
855 if (GetProperty(rXPropSet, "LineColor"))
857 nColor = ::Color(mAny.get<sal_uInt32>() & 0xffffff);
858 bColorSet = true;
860 if (GetProperty(rXPropSet, "LineTransparence"))
862 nColorAlpha = MAX_PERCENT - (mAny.get<sal_Int16>() * PER_PERCENT);
864 if (GetProperty(rXPropSet, "LineCap"))
866 const LineCap aLineCap = mAny.get<drawing::LineCap>();
867 if (aLineCap == LineCap_ROUND)
868 cap = "rnd";
869 else if (aLineCap == LineCap_SQUARE)
870 cap = "sq";
872 break;
875 mpFS->startElementNS( XML_a, XML_ln,
876 XML_cap, cap,
877 XML_w, nLineWidth > 1 && nStyleLineWidth != nLineWidth ?
878 OString::number(oox::drawingml::convertHmmToEmu(nLineWidth)).getStr() : nullptr );
880 if( bColorSet )
882 if( nColor != nOriginalColor )
884 // the user has set a different color for the line
885 WriteSolidFill( nColor, nColorAlpha );
887 else if( !sColorFillScheme.isEmpty() )
889 // the line had a scheme color and the user didn't change it
890 WriteSolidFill( sColorFillScheme, aTransformations );
892 else if( aStyleProperties.hasElements() )
894 if( nColor != nStyleColor )
895 // the line style defines some color but it wasn't being used
896 WriteSolidFill( nColor );
897 // in case the shape used the style color and the user didn't change it,
898 // we must not write a <a: solidFill> tag.
900 else
902 WriteSolidFill( nColor, nColorAlpha );
906 if( bDashSet && aStyleLineStyle != drawing::LineStyle_DASH )
908 // Try to detect if it might come from ms preset line style import.
909 // MS Office styles are always relative, both binary and OOXML.
910 // "dot" is always the first dash and "dash" the second one. All OOXML presets linestyles
911 // start with the longer one. Definitions are in OOXML part 1, 20.1.10.49
912 // The tests are strict, for to not catch styles from standard.sod (as of Aug 2019).
913 bool bIsConverted = false;
914 bool bIsRelative(aLineDash.Style == DashStyle_RECTRELATIVE || aLineDash.Style == DashStyle_ROUNDRELATIVE);
915 if ( bIsRelative && aLineDash.Dots == 1)
917 // LO uses length 0 for 100%, if the attribute is missing in ODF.
918 // Other applications might write 100%. Make is unique for the conditions.
919 sal_uInt32 nDotLen = (aLineDash.DotLen == 0) ? 100 : aLineDash.DotLen;
920 sal_uInt32 nDashLen = (aLineDash.DashLen == 0 && aLineDash.Dashes > 0) ? 100 : aLineDash.DashLen;
921 bIsConverted = true;
922 if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && aLineDash.Distance == 300)
924 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
926 else if (nDotLen == 400 && aLineDash.Dashes == 0 && nDashLen == 0 && aLineDash.Distance == 300)
928 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
930 else if (nDotLen == 400 && aLineDash.Dashes == 1 && nDashLen == 100 && aLineDash.Distance == 300)
932 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dashDot");
934 else if (nDotLen == 800 && aLineDash.Dashes == 0 && nDashLen == 0 && aLineDash.Distance == 300)
936 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDash");
938 else if (nDotLen == 800 && aLineDash.Dashes == 1 && nDashLen == 100 && aLineDash.Distance == 300)
940 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDot");
942 else if (nDotLen == 800 && aLineDash.Dashes == 2 && nDashLen == 100 && aLineDash.Distance == 300)
944 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDotDot");
946 else if (nDotLen == 100 && aLineDash.Dashes == 0 && nDashLen == 0 && aLineDash.Distance == 100)
948 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
950 else if (nDotLen == 300 && aLineDash.Dashes == 0 && nDashLen == 0 && aLineDash.Distance == 100)
952 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
954 else if (nDotLen == 300 && aLineDash.Dashes == 1 && nDashLen == 100 && aLineDash.Distance == 100)
956 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDot");
958 else if (nDotLen == 300 && aLineDash.Dashes == 2 && nDashLen == 100 && aLineDash.Distance == 100)
960 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
962 else
963 bIsConverted = false;
965 // Do not map our own line styles to OOXML prstDash values, because custDash gives better results.
966 if (!bIsConverted)
968 mpFS->startElementNS(XML_a, XML_custDash);
969 // In case of hairline we would need the current pixel size. Instead use a reasonable
970 // ersatz for it. The value is the same as SMALLEST_DASH_WIDTH in xattr.cxx.
971 // (And it makes sure fLineWidth is not zero in below division.)
972 double fLineWidth = nLineWidth > 0 ? nLineWidth : 26.95;
973 int i;
974 double fSp = bIsRelative ? aLineDash.Distance : aLineDash.Distance * 100.0 / fLineWidth;
975 // LO uses line width, in case Distance is zero. MS Office would use a space of zero length.
976 // So set 100% explicitly.
977 if (aLineDash.Distance <= 0)
978 fSp = 100.0;
979 if ( aLineDash.Dots > 0 )
981 double fD = bIsRelative ? aLineDash.DotLen : aLineDash.DotLen * 100.0 / fLineWidth;
982 // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended.
983 if (aLineDash.DotLen == 0)
984 fD = 100.0;
985 for( i = 0; i < aLineDash.Dots; i ++ )
987 mpFS->singleElementNS( XML_a, XML_ds,
988 XML_d , write1000thOfAPercent(fD),
989 XML_sp, write1000thOfAPercent(fSp) );
992 if ( aLineDash.Dashes > 0 )
994 double fD = bIsRelative ? aLineDash.DashLen : aLineDash.DashLen * 100.0 / fLineWidth;
995 // LO sets length to 0, if attribute is missing in ODF. Then a relative length of 100% is intended.
996 if (aLineDash.DashLen == 0)
997 fD = 100.0;
998 for( i = 0; i < aLineDash.Dashes; i ++ )
1000 mpFS->singleElementNS( XML_a , XML_ds,
1001 XML_d , write1000thOfAPercent(fD),
1002 XML_sp, write1000thOfAPercent(fSp) );
1006 SAL_WARN_IF(nLineWidth <= 0,
1007 "oox.shape", "while writing outline - custom dash - line width was < 0 : " << nLineWidth);
1008 SAL_WARN_IF(aLineDash.Dashes < 0,
1009 "oox.shape", "while writing outline - custom dash - number of dashes was < 0 : " << aLineDash.Dashes);
1010 SAL_WARN_IF(aLineDash.Dashes > 0 && aLineDash.DashLen <= 0,
1011 "oox.shape", "while writing outline - custom dash - dash length was < 0 : " << aLineDash.DashLen);
1012 SAL_WARN_IF(aLineDash.Dots < 0,
1013 "oox.shape", "while writing outline - custom dash - number of dots was < 0 : " << aLineDash.Dots);
1014 SAL_WARN_IF(aLineDash.Dots > 0 && aLineDash.DotLen <= 0,
1015 "oox.shape", "while writing outline - custom dash - dot length was < 0 : " << aLineDash.DotLen);
1016 SAL_WARN_IF(aLineDash.Distance <= 0,
1017 "oox.shape", "while writing outline - custom dash - distance was < 0 : " << aLineDash.Distance);
1019 mpFS->endElementNS( XML_a, XML_custDash );
1023 if (!bNoFill && nLineWidth > 1 && GetProperty(rXPropSet, "LineJoint"))
1025 LineJoint eLineJoint = mAny.get<LineJoint>();
1027 if( aStyleLineJoint == LineJoint_NONE || aStyleLineJoint != eLineJoint )
1029 // style-defined line joint does not exist, or is different from the shape's joint
1030 switch( eLineJoint )
1032 case LineJoint_NONE:
1033 case LineJoint_BEVEL:
1034 mpFS->singleElementNS(XML_a, XML_bevel);
1035 break;
1036 default:
1037 case LineJoint_MIDDLE:
1038 case LineJoint_MITER:
1039 mpFS->singleElementNS(XML_a, XML_miter);
1040 break;
1041 case LineJoint_ROUND:
1042 mpFS->singleElementNS(XML_a, XML_round);
1043 break;
1048 if( !bNoFill )
1050 WriteLineArrow( rXPropSet, true );
1051 WriteLineArrow( rXPropSet, false );
1053 else
1055 mpFS->singleElementNS(XML_a, XML_noFill);
1058 mpFS->endElementNS( XML_a, XML_ln );
1061 const char* DrawingML::GetComponentDir() const
1063 switch ( meDocumentType )
1065 case DOCUMENT_DOCX: return "word";
1066 case DOCUMENT_PPTX: return "ppt";
1067 case DOCUMENT_XLSX: return "xl";
1070 return "unknown";
1073 const char* DrawingML::GetRelationCompPrefix() const
1075 switch ( meDocumentType )
1077 case DOCUMENT_DOCX: return "";
1078 case DOCUMENT_PPTX:
1079 case DOCUMENT_XLSX: return "../";
1082 return "unknown";
1085 OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia )
1087 GfxLink aLink = rGraphic.GetGfxLink ();
1088 OUString sMediaType;
1089 const char* pExtension = "";
1090 OUString sRelId;
1092 SvMemoryStream aStream;
1093 const void* aData = aLink.GetData();
1094 std::size_t nDataSize = aLink.GetDataSize();
1096 switch ( aLink.GetType() )
1098 case GfxLinkType::NativeGif:
1099 sMediaType = "image/gif";
1100 pExtension = ".gif";
1101 break;
1103 // #i15508# added BMP type for better exports
1104 // export not yet active, so adding for reference (not checked)
1105 case GfxLinkType::NativeBmp:
1106 sMediaType = "image/bmp";
1107 pExtension = ".bmp";
1108 break;
1110 case GfxLinkType::NativeJpg:
1111 sMediaType = "image/jpeg";
1112 pExtension = ".jpeg";
1113 break;
1114 case GfxLinkType::NativePng:
1115 sMediaType = "image/png";
1116 pExtension = ".png";
1117 break;
1118 case GfxLinkType::NativeTif:
1119 sMediaType = "image/tiff";
1120 pExtension = ".tif";
1121 break;
1122 case GfxLinkType::NativeWmf:
1123 sMediaType = "image/x-wmf";
1124 pExtension = ".wmf";
1125 break;
1126 case GfxLinkType::NativeMet:
1127 sMediaType = "image/x-met";
1128 pExtension = ".met";
1129 break;
1130 case GfxLinkType::NativePct:
1131 sMediaType = "image/x-pict";
1132 pExtension = ".pct";
1133 break;
1134 case GfxLinkType::NativeMov:
1135 sMediaType = "application/movie";
1136 pExtension = ".MOV";
1137 break;
1138 default:
1140 GraphicType aType = rGraphic.GetType();
1141 if ( aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile)
1143 if ( aType == GraphicType::Bitmap )
1145 (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::PNG );
1146 sMediaType = "image/png";
1147 pExtension = ".png";
1149 else
1151 (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::EMF );
1152 sMediaType = "image/x-emf";
1153 pExtension = ".emf";
1156 else
1158 SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType) );
1159 /*Earlier, even in case of unhandled graphic types we were
1160 proceeding to write the image, which would eventually
1161 write an empty image with a zero size, and return a valid
1162 relationID, which is incorrect.
1164 return sRelId;
1167 aData = aStream.GetData();
1168 nDataSize = aStream.GetEndOfData();
1169 break;
1173 Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
1174 .appendAscii( GetComponentDir() )
1175 .append( "/media/image" )
1176 .append( static_cast<sal_Int32>(mnImageCounter) )
1177 .appendAscii( pExtension )
1178 .makeStringAndClear(),
1179 sMediaType );
1180 xOutStream->writeBytes( Sequence< sal_Int8 >( static_cast<const sal_Int8*>(aData), nDataSize ) );
1181 xOutStream->closeOutput();
1183 const OString sRelPathToMedia = "media/image";
1184 OString sRelationCompPrefix;
1185 if ( bRelPathToMedia )
1186 sRelationCompPrefix = "../";
1187 else
1188 sRelationCompPrefix = GetRelationCompPrefix();
1189 sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1190 oox::getRelationship(Relationship::IMAGE),
1191 OUStringBuffer()
1192 .appendAscii( sRelationCompPrefix.getStr() )
1193 .appendAscii( sRelPathToMedia.getStr() )
1194 .append( static_cast<sal_Int32>(mnImageCounter ++) )
1195 .appendAscii( pExtension )
1196 .makeStringAndClear() );
1198 return sRelId;
1201 void DrawingML::WriteMediaNonVisualProperties(const css::uno::Reference<css::drawing::XShape>& xShape)
1203 SdrMediaObj* pMediaObj = dynamic_cast<SdrMediaObj*>(GetSdrObjectFromXShape(xShape));
1204 if (!pMediaObj)
1205 return;
1207 // extension
1208 OUString aExtension;
1209 const OUString& rURL(pMediaObj->getURL());
1210 int nLastDot = rURL.lastIndexOf('.');
1211 if (nLastDot >= 0)
1212 aExtension = rURL.copy(nLastDot);
1214 bool bEmbed = rURL.startsWith("vnd.sun.star.Package:");
1215 Relationship eMediaType = Relationship::VIDEO;
1217 // mime type
1218 #if HAVE_FEATURE_AVMEDIA
1219 OUString aMimeType(pMediaObj->getMediaProperties().getMimeType());
1220 #else
1221 OUString aMimeType("none");
1222 #endif
1223 if (aMimeType == "application/vnd.sun.star.media")
1225 // try to set something better
1226 // TODO fix the importer to actually set the mimetype on import
1227 if (aExtension.equalsIgnoreAsciiCase(".avi"))
1228 aMimeType = "video/x-msvideo";
1229 else if (aExtension.equalsIgnoreAsciiCase(".flv"))
1230 aMimeType = "video/x-flv";
1231 else if (aExtension.equalsIgnoreAsciiCase(".mp4"))
1232 aMimeType = "video/mp4";
1233 else if (aExtension.equalsIgnoreAsciiCase(".mov"))
1234 aMimeType = "video/quicktime";
1235 else if (aExtension.equalsIgnoreAsciiCase(".ogv"))
1236 aMimeType = "video/ogg";
1237 else if (aExtension.equalsIgnoreAsciiCase(".wmv"))
1238 aMimeType = "video/x-ms-wmv";
1239 else if (aExtension.equalsIgnoreAsciiCase(".wav"))
1241 aMimeType = "audio/x-wav";
1242 eMediaType = Relationship::AUDIO;
1246 OUString aVideoFileRelId;
1247 OUString aMediaRelId;
1249 if (bEmbed)
1251 // copy the video stream
1252 Reference<XOutputStream> xOutStream = mpFB->openFragmentStream(OUStringBuffer()
1253 .appendAscii(GetComponentDir())
1254 .append("/media/media")
1255 .append(static_cast<sal_Int32>(mnImageCounter))
1256 .append(aExtension)
1257 .makeStringAndClear(),
1258 aMimeType);
1260 uno::Reference<io::XInputStream> xInputStream(pMediaObj->GetInputStream());
1261 comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream);
1263 xOutStream->closeOutput();
1265 // create the relation
1266 OUString aPath = OUStringBuffer().appendAscii(GetRelationCompPrefix())
1267 .append("media/media")
1268 .append(static_cast<sal_Int32>(mnImageCounter++))
1269 .append(aExtension)
1270 .makeStringAndClear();
1271 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), aPath);
1272 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), aPath);
1274 else
1276 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), rURL);
1277 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), rURL);
1280 GetFS()->startElementNS(XML_p, XML_nvPr);
1282 GetFS()->singleElementNS(XML_a, eMediaType == Relationship::VIDEO ? XML_videoFile : XML_audioFile,
1283 FSNS(XML_r, XML_link), aVideoFileRelId.toUtf8());
1285 GetFS()->startElementNS(XML_p, XML_extLst);
1286 // media extensions; google this ID for details
1287 GetFS()->startElementNS(XML_p, XML_ext, XML_uri, "{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}");
1289 GetFS()->singleElementNS(XML_p14, XML_media,
1290 bEmbed? FSNS(XML_r, XML_embed): FSNS(XML_r, XML_link), aMediaRelId.toUtf8());
1292 GetFS()->endElementNS(XML_p, XML_ext);
1293 GetFS()->endElementNS(XML_p, XML_extLst);
1295 GetFS()->endElementNS(XML_p, XML_nvPr);
1298 void DrawingML::WriteImageBrightnessContrastTransparence(uno::Reference<beans::XPropertySet> const & rXPropSet)
1300 sal_Int16 nBright = 0;
1301 sal_Int32 nContrast = 0;
1302 sal_Int32 nTransparence = 0;
1304 if (GetProperty(rXPropSet, "AdjustLuminance"))
1305 nBright = mAny.get<sal_Int16>();
1306 if (GetProperty(rXPropSet, "AdjustContrast"))
1307 nContrast = mAny.get<sal_Int32>();
1308 // Used for shapes with picture fill
1309 if (GetProperty(rXPropSet, "FillTransparence"))
1310 nTransparence = mAny.get<sal_Int32>();
1311 // Used for pictures
1312 if (nTransparence == 0 && GetProperty(rXPropSet, "Transparency"))
1313 nTransparence = static_cast<sal_Int32>(mAny.get<sal_Int16>());
1315 if (GetProperty(rXPropSet, "GraphicColorMode"))
1317 drawing::ColorMode aColorMode;
1318 mAny >>= aColorMode;
1319 if (aColorMode == drawing::ColorMode_GREYS)
1320 mpFS->singleElementNS(XML_a, XML_grayscl);
1321 else if (aColorMode == drawing::ColorMode_MONO)
1322 //black/white has a 0,5 threshold in LibreOffice
1323 mpFS->singleElementNS(XML_a, XML_biLevel, XML_thresh, OString::number(50000));
1324 else if (aColorMode == drawing::ColorMode_WATERMARK)
1326 //map watermark with mso washout
1327 nBright = 70;
1328 nContrast = -70;
1333 if (nBright || nContrast)
1335 mpFS->singleElementNS(XML_a, XML_lum,
1336 XML_bright, nBright ? OString::number(nBright * 1000).getStr() : nullptr,
1337 XML_contrast, nContrast ? OString::number(nContrast * 1000).getStr() : nullptr);
1340 if (nTransparence)
1342 sal_Int32 nAlphaMod = (100 - nTransparence ) * PER_PERCENT;
1343 mpFS->singleElementNS(XML_a, XML_alphaModFix, XML_amt, OString::number(nAlphaMod));
1347 OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const & rXPropSet,
1348 uno::Reference<graphic::XGraphic> const & rxGraphic,
1349 bool bRelPathToMedia)
1351 OUString sRelId;
1353 if (!rxGraphic.is())
1354 return sRelId;
1356 Graphic aGraphic(rxGraphic);
1357 if (mpTextExport)
1359 BitmapChecksum nChecksum = aGraphic.GetChecksum();
1360 sRelId = mpTextExport->FindRelId(nChecksum);
1362 if (sRelId.isEmpty())
1364 sRelId = WriteImage(aGraphic, bRelPathToMedia);
1365 if (mpTextExport)
1367 BitmapChecksum nChecksum = aGraphic.GetChecksum();
1368 mpTextExport->CacheRelId(nChecksum, sRelId);
1372 mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId.toUtf8());
1374 WriteImageBrightnessContrastTransparence(rXPropSet);
1376 WriteArtisticEffect(rXPropSet);
1378 mpFS->endElementNS(XML_a, XML_blip);
1380 return sRelId;
1383 void DrawingML::WriteXGraphicBlipMode(uno::Reference<beans::XPropertySet> const & rXPropSet,
1384 uno::Reference<graphic::XGraphic> const & rxGraphic)
1386 BitmapMode eBitmapMode(BitmapMode_NO_REPEAT);
1387 if (GetProperty(rXPropSet, "FillBitmapMode"))
1388 mAny >>= eBitmapMode;
1390 SAL_INFO("oox.shape", "fill bitmap mode: " << int(eBitmapMode));
1392 switch (eBitmapMode)
1394 case BitmapMode_REPEAT:
1395 mpFS->singleElementNS(XML_a, XML_tile);
1396 break;
1397 case BitmapMode_STRETCH:
1398 WriteXGraphicStretch(rXPropSet, rxGraphic);
1399 break;
1400 default:
1401 break;
1405 void DrawingML::WriteBlipOrNormalFill( const Reference< XPropertySet >& xPropSet, const OUString& rURLPropName )
1407 // check for blip and otherwise fall back to normal fill
1408 // we always store normal fill properties but OOXML
1409 // uses a choice between our fill props and BlipFill
1410 if (GetProperty ( xPropSet, rURLPropName ))
1411 WriteBlipFill( xPropSet, rURLPropName );
1412 else
1413 WriteFill(xPropSet);
1416 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName )
1418 WriteBlipFill( rXPropSet, sURLPropName, XML_a );
1421 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName, sal_Int32 nXmlNamespace )
1423 if ( GetProperty( rXPropSet, sURLPropName ) )
1425 uno::Reference<graphic::XGraphic> xGraphic;
1426 if (mAny.has<uno::Reference<awt::XBitmap>>())
1428 uno::Reference<awt::XBitmap> xBitmap = mAny.get<uno::Reference<awt::XBitmap>>();
1429 if (xBitmap.is())
1430 xGraphic.set(xBitmap, uno::UNO_QUERY);
1432 else if (mAny.has<uno::Reference<graphic::XGraphic>>())
1434 xGraphic = mAny.get<uno::Reference<graphic::XGraphic>>();
1437 if (xGraphic.is())
1439 bool bWriteMode = false;
1440 if (sURLPropName == "FillBitmap" || sURLPropName == "BackGraphic")
1441 bWriteMode = true;
1442 WriteXGraphicBlipFill(rXPropSet, xGraphic, nXmlNamespace, bWriteMode);
1447 void DrawingML::WriteXGraphicBlipFill(uno::Reference<beans::XPropertySet> const & rXPropSet,
1448 uno::Reference<graphic::XGraphic> const & rxGraphic,
1449 sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia)
1451 if (!rxGraphic.is() )
1452 return;
1454 mpFS->startElementNS(nXmlNamespace , XML_blipFill, XML_rotWithShape, "0");
1456 WriteXGraphicBlip(rXPropSet, rxGraphic, bRelPathToMedia);
1458 if (bWriteMode)
1460 WriteXGraphicBlipMode(rXPropSet, rxGraphic);
1462 else if(GetProperty(rXPropSet, "FillBitmapStretch"))
1464 bool bStretch = mAny.get<bool>();
1466 if (bStretch)
1468 WriteXGraphicStretch(rXPropSet, rxGraphic);
1471 mpFS->endElementNS(nXmlNamespace, XML_blipFill);
1474 void DrawingML::WritePattFill( const Reference< XPropertySet >& rXPropSet )
1476 if ( GetProperty( rXPropSet, "FillHatch" ) )
1478 drawing::Hatch aHatch;
1479 mAny >>= aHatch;
1480 WritePattFill(rXPropSet, aHatch);
1484 void DrawingML::WritePattFill(const Reference<XPropertySet>& rXPropSet, const css::drawing::Hatch& rHatch)
1486 mpFS->startElementNS(XML_a, XML_pattFill, XML_prst, GetHatchPattern(rHatch));
1488 mpFS->startElementNS(XML_a, XML_fgClr);
1489 WriteColor(::Color(rHatch.Color));
1490 mpFS->endElementNS( XML_a , XML_fgClr );
1492 ::Color nColor = COL_WHITE;
1493 sal_Int32 nAlpha = 0;
1495 if ( GetProperty( rXPropSet, "FillBackground" ) )
1497 bool isBackgroundFilled = false;
1498 mAny >>= isBackgroundFilled;
1499 if( isBackgroundFilled )
1501 nAlpha = MAX_PERCENT;
1503 if( GetProperty( rXPropSet, "FillColor" ) )
1505 mAny >>= nColor;
1510 mpFS->startElementNS(XML_a, XML_bgClr);
1511 WriteColor(nColor, nAlpha);
1512 mpFS->endElementNS( XML_a , XML_bgClr );
1514 mpFS->endElementNS( XML_a , XML_pattFill );
1517 void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet, Size const & rOriginalSize, MapMode const & rMapMode)
1519 if (GetProperty(rXPropSet, "GraphicCrop"))
1521 Size aOriginalSize(rOriginalSize);
1523 // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over.
1524 if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
1525 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM));
1527 css::text::GraphicCrop aGraphicCropStruct;
1528 mAny >>= aGraphicCropStruct;
1530 if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) )
1532 mpFS->singleElementNS( XML_a, XML_srcRect,
1533 XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())),
1534 XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())),
1535 XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())),
1536 XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) );
1541 void DrawingML::WriteSrcRectXGraphic(uno::Reference<beans::XPropertySet> const & rxPropertySet,
1542 uno::Reference<graphic::XGraphic> const & rxGraphic)
1544 Graphic aGraphic(rxGraphic);
1545 Size aOriginalSize = aGraphic.GetPrefSize();
1546 const MapMode& rMapMode = aGraphic.GetPrefMapMode();
1547 WriteGraphicCropProperties(rxPropertySet, aOriginalSize, rMapMode);
1550 void DrawingML::WriteXGraphicStretch(uno::Reference<beans::XPropertySet> const & rXPropSet,
1551 uno::Reference<graphic::XGraphic> const & rxGraphic)
1553 mpFS->startElementNS(XML_a, XML_stretch);
1555 bool bCrop = false;
1556 if (GetProperty(rXPropSet, "GraphicCrop"))
1558 css::text::GraphicCrop aGraphicCropStruct;
1559 mAny >>= aGraphicCropStruct;
1561 if ((0 != aGraphicCropStruct.Left)
1562 || (0 != aGraphicCropStruct.Top)
1563 || (0 != aGraphicCropStruct.Right)
1564 || (0 != aGraphicCropStruct.Bottom))
1566 Graphic aGraphic(rxGraphic);
1567 Size aOriginalSize(aGraphic.GetPrefSize());
1568 mpFS->singleElementNS(XML_a, XML_fillRect,
1569 XML_l, OString::number(((aGraphicCropStruct.Left) * 100000) / aOriginalSize.Width()),
1570 XML_t, OString::number(((aGraphicCropStruct.Top) * 100000) / aOriginalSize.Height()),
1571 XML_r, OString::number(((aGraphicCropStruct.Right) * 100000) / aOriginalSize.Width()),
1572 XML_b, OString::number(((aGraphicCropStruct.Bottom) * 100000) / aOriginalSize.Height()));
1573 bCrop = true;
1577 if (!bCrop)
1579 mpFS->singleElementNS(XML_a, XML_fillRect);
1582 mpFS->endElementNS(XML_a, XML_stretch);
1585 void DrawingML::WriteTransformation(const tools::Rectangle& rRect,
1586 sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, sal_Int32 nRotation, bool bIsGroupShape)
1589 mpFS->startElementNS( nXmlNamespace, XML_xfrm,
1590 XML_flipH, bFlipH ? "1" : nullptr,
1591 XML_flipV, bFlipV ? "1" : nullptr,
1592 XML_rot, (nRotation % 21600000) ? OString::number(nRotation).getStr() : nullptr );
1594 sal_Int32 nLeft = rRect.Left();
1595 sal_Int32 nTop = rRect.Top();
1596 if (GetDocumentType() == DOCUMENT_DOCX && !m_xParent.is())
1598 nLeft = 0;
1599 nTop = 0;
1602 mpFS->singleElementNS(XML_a, XML_off,
1603 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
1604 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
1605 mpFS->singleElementNS(XML_a, XML_ext,
1606 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1607 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1609 if (GetDocumentType() != DOCUMENT_DOCX && bIsGroupShape)
1611 mpFS->singleElementNS(XML_a, XML_chOff,
1612 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
1613 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
1614 mpFS->singleElementNS(XML_a, XML_chExt,
1615 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1616 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1619 mpFS->endElementNS( nXmlNamespace, XML_xfrm );
1622 void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, bool bSuppressRotation, bool bSuppressFlipping, bool bFlippedBeforeRotation )
1624 SAL_INFO("oox.shape", "write shape transformation");
1626 sal_Int32 nRotation=0;
1627 awt::Point aPos = rXShape->getPosition();
1628 awt::Size aSize = rXShape->getSize();
1630 bool bFlipHWrite = bFlipH && !bSuppressFlipping;
1631 bool bFlipVWrite = bFlipV && !bSuppressFlipping;
1632 bFlipH = bFlipH && !bFlippedBeforeRotation;
1633 bFlipV = bFlipV && !bFlippedBeforeRotation;
1635 if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
1637 awt::Point aParentPos = m_xParent->getPosition();
1638 aPos.X -= aParentPos.X;
1639 aPos.Y -= aParentPos.Y;
1642 if ( aSize.Width < 0 )
1643 aSize.Width = 1000;
1644 if ( aSize.Height < 0 )
1645 aSize.Height = 1000;
1646 if (!bSuppressRotation)
1648 SdrObject* pShape = GetSdrObjectFromXShape( rXShape );
1649 nRotation = pShape ? pShape->GetRotateAngle() : 0;
1650 if ( nRotation != 0 && GetDocumentType() != DOCUMENT_DOCX )
1652 int faccos=bFlipV ? -1 : 1;
1653 int facsin=bFlipH ? -1 : 1;
1654 aPos.X-=(1-faccos*cos(nRotation*F_PI18000))*aSize.Width/2-facsin*sin(nRotation*F_PI18000)*aSize.Height/2;
1655 aPos.Y-=(1-faccos*cos(nRotation*F_PI18000))*aSize.Height/2+facsin*sin(nRotation*F_PI18000)*aSize.Width/2;
1658 // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
1659 uno::Reference<beans::XPropertySet> xPropertySet(rXShape, uno::UNO_QUERY);
1660 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
1661 if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
1662 xPropertySet->getPropertyValue("RotateAngle") >>= nRotation;
1665 // OOXML flips shapes before rotating them.
1666 if(bFlipH != bFlipV)
1667 nRotation = nRotation * -1 + 36000;
1669 WriteTransformation(tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)), nXmlNamespace,
1670 bFlipHWrite, bFlipVWrite, ExportRotateClockwisify(nRotation), IsGroupShape( rXShape ));
1673 void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement, bool bCheckDirect,
1674 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight, sal_Int16 nScriptType )
1676 Reference< XPropertySet > rXPropSet = rRun;
1677 Reference< XPropertyState > rXPropState( rRun, UNO_QUERY );
1678 OUString usLanguage;
1679 PropertyState eState;
1680 bool bComplex = ( nScriptType == css::i18n::ScriptType::COMPLEX );
1681 const char* bold = "0";
1682 const char* italic = nullptr;
1683 const char* underline = nullptr;
1684 const char* strikeout = nullptr;
1685 const char* cap = nullptr;
1686 sal_Int32 nSize = 1800;
1687 sal_Int32 nCharEscapement = 0;
1688 sal_Int32 nCharKerning = 0;
1690 if ( nElement == XML_endParaRPr && rbOverridingCharHeight )
1692 nSize = rnCharHeight;
1694 else if (GetProperty(rXPropSet, "CharHeight"))
1696 nSize = static_cast<sal_Int32>(100*(*o3tl::doAccess<float>(mAny)));
1697 if ( nElement == XML_rPr )
1699 rbOverridingCharHeight = true;
1700 rnCharHeight = nSize;
1704 if (GetProperty(rXPropSet, "CharKerning"))
1705 nCharKerning = static_cast<sal_Int32>(*o3tl::doAccess<sal_Int16>(mAny));
1706 /** While setting values in propertymap,
1707 * CharKerning converted using GetTextSpacingPoint
1708 * i.e set @ https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/textcharacterproperties.cxx#129
1709 * therefore to get original value CharKerning need to be convert.
1710 * https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/drawingmltypes.cxx#95
1712 nCharKerning = ((nCharKerning * 720)-360) / 254;
1714 if ((bComplex && GetProperty(rXPropSet, "CharWeightComplex"))
1715 || GetProperty(rXPropSet, "CharWeight"))
1717 if ( *o3tl::doAccess<float>(mAny) >= awt::FontWeight::SEMIBOLD )
1718 bold = "1";
1721 if ((bComplex && GetProperty(rXPropSet, "CharPostureComplex"))
1722 || GetProperty(rXPropSet, "CharPosture"))
1723 switch ( *o3tl::doAccess<awt::FontSlant>(mAny) )
1725 case awt::FontSlant_OBLIQUE :
1726 case awt::FontSlant_ITALIC :
1727 italic = "1";
1728 break;
1729 default:
1730 break;
1733 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderline", eState)
1734 && eState == beans::PropertyState_DIRECT_VALUE)
1735 || GetProperty(rXPropSet, "CharUnderline"))
1737 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1739 case awt::FontUnderline::SINGLE :
1740 underline = "sng";
1741 break;
1742 case awt::FontUnderline::DOUBLE :
1743 underline = "dbl";
1744 break;
1745 case awt::FontUnderline::DOTTED :
1746 underline = "dotted";
1747 break;
1748 case awt::FontUnderline::DASH :
1749 underline = "dash";
1750 break;
1751 case awt::FontUnderline::LONGDASH :
1752 underline = "dashLong";
1753 break;
1754 case awt::FontUnderline::DASHDOT :
1755 underline = "dotDash";
1756 break;
1757 case awt::FontUnderline::DASHDOTDOT :
1758 underline = "dotDotDash";
1759 break;
1760 case awt::FontUnderline::WAVE :
1761 underline = "wavy";
1762 break;
1763 case awt::FontUnderline::DOUBLEWAVE :
1764 underline = "wavyDbl";
1765 break;
1766 case awt::FontUnderline::BOLD :
1767 underline = "heavy";
1768 break;
1769 case awt::FontUnderline::BOLDDOTTED :
1770 underline = "dottedHeavy";
1771 break;
1772 case awt::FontUnderline::BOLDDASH :
1773 underline = "dashHeavy";
1774 break;
1775 case awt::FontUnderline::BOLDLONGDASH :
1776 underline = "dashLongHeavy";
1777 break;
1778 case awt::FontUnderline::BOLDDASHDOT :
1779 underline = "dotDashHeavy";
1780 break;
1781 case awt::FontUnderline::BOLDDASHDOTDOT :
1782 underline = "dotDotDashHeavy";
1783 break;
1784 case awt::FontUnderline::BOLDWAVE :
1785 underline = "wavyHeavy";
1786 break;
1790 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharStrikeout", eState)
1791 && eState == beans::PropertyState_DIRECT_VALUE)
1792 || GetProperty(rXPropSet, "CharStrikeout"))
1794 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1796 case awt::FontStrikeout::NONE :
1797 strikeout = "noStrike";
1798 break;
1799 case awt::FontStrikeout::SINGLE :
1800 // LibO supports further values of character
1801 // strikeout, OOXML standard (20.1.10.78,
1802 // ST_TextStrikeType) however specifies only
1803 // 3 - single, double and none. Approximate
1804 // the remaining ones by single strike (better
1805 // some strike than none at all).
1806 // TODO: figure out how to do this better
1807 case awt::FontStrikeout::BOLD :
1808 case awt::FontStrikeout::SLASH :
1809 case awt::FontStrikeout::X :
1810 strikeout = "sngStrike";
1811 break;
1812 case awt::FontStrikeout::DOUBLE :
1813 strikeout = "dblStrike";
1814 break;
1818 bool bLang = false;
1819 switch(nScriptType)
1821 case css::i18n::ScriptType::ASIAN:
1822 bLang = GetProperty(rXPropSet, "CharLocaleAsian"); break;
1823 case css::i18n::ScriptType::COMPLEX:
1824 bLang = GetProperty(rXPropSet, "CharLocaleComplex"); break;
1825 default:
1826 bLang = GetProperty(rXPropSet, "CharLocale"); break;
1829 if (bLang)
1831 css::lang::Locale aLocale;
1832 mAny >>= aLocale;
1833 LanguageTag aLanguageTag( aLocale);
1834 if (!aLanguageTag.isSystemLocale())
1835 usLanguage = aLanguageTag.getBcp47MS();
1838 if (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapement", eState)
1839 && eState == beans::PropertyState_DIRECT_VALUE)
1840 mAny >>= nCharEscapement;
1842 if (nCharEscapement
1843 && (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapementHeight", eState)
1844 && eState == beans::PropertyState_DIRECT_VALUE))
1846 sal_uInt32 nCharEscapementHeight = 0;
1847 mAny >>= nCharEscapementHeight;
1848 nSize = (nSize * nCharEscapementHeight) / 100;
1849 // MSO uses default ~58% size
1850 nSize = (nSize / 0.58);
1853 if (GetProperty(rXPropSet, "CharCaseMap"))
1855 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1857 case CaseMap::UPPERCASE :
1858 cap = "all";
1859 break;
1860 case CaseMap::SMALLCAPS :
1861 cap = "small";
1862 break;
1866 mpFS->startElementNS( XML_a, nElement,
1867 XML_b, bold,
1868 XML_i, italic,
1869 XML_lang, usLanguage.isEmpty() ? nullptr : usLanguage.toUtf8().getStr(),
1870 XML_sz, OString::number(nSize),
1871 // For Condensed character spacing spc value is negative.
1872 XML_spc, nCharKerning ? OString::number(nCharKerning).getStr() : nullptr,
1873 XML_strike, strikeout,
1874 XML_u, underline,
1875 XML_baseline, nCharEscapement == 0 ? nullptr : OString::number(nCharEscapement*1000).getStr(),
1876 XML_cap, cap );
1878 // mso doesn't like text color to be placed after typeface
1879 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState)
1880 && eState == beans::PropertyState_DIRECT_VALUE)
1881 || GetProperty(rXPropSet, "CharColor"))
1883 ::Color color( *o3tl::doAccess<sal_uInt32>(mAny) );
1884 SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO));
1886 // tdf#104219 In LibreOffice and MS Office, there are two types of colors:
1887 // Automatic and Fixed. OOXML is setting automatic color, by not providing color.
1888 if( color != COL_AUTO )
1890 color.SetTransparency(0);
1891 // TODO: special handle embossed/engraved
1892 WriteSolidFill( color );
1896 // tdf#128096, exporting XML_highlight to docx already works fine,
1897 // so make sure this code is only run when exporting to pptx, just in case
1898 if (GetDocumentType() == DOCUMENT_PPTX)
1900 if (GetProperty(rXPropSet, "CharBackColor"))
1902 ::Color color(*o3tl::doAccess<sal_uInt32>(mAny));
1903 if( color != COL_AUTO )
1905 mpFS->startElementNS(XML_a, XML_highlight);
1906 WriteColor( color );
1907 mpFS->endElementNS( XML_a, XML_highlight );
1912 if (underline
1913 && ((bCheckDirect
1914 && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderlineColor", eState)
1915 && eState == beans::PropertyState_DIRECT_VALUE)
1916 || GetProperty(rXPropSet, "CharUnderlineColor")))
1918 ::Color color(*o3tl::doAccess<sal_uInt32>(mAny));
1919 // if color is automatic, then we shouldn't write information about color but to take color from character
1920 if( color != COL_AUTO )
1922 mpFS->startElementNS(XML_a, XML_uFill);
1923 WriteSolidFill( color );
1924 mpFS->endElementNS( XML_a, XML_uFill );
1926 else
1928 mpFS->singleElementNS(XML_a, XML_uFillTx);
1932 if (GetProperty(rXPropSet, "CharFontName"))
1934 const char* const pitch = nullptr;
1935 const char* const charset = nullptr;
1936 OUString usTypeface;
1938 mAny >>= usTypeface;
1939 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
1940 if (!aSubstName.isEmpty())
1941 usTypeface = aSubstName;
1943 mpFS->singleElementNS( XML_a, XML_latin,
1944 XML_typeface, usTypeface.toUtf8(),
1945 XML_pitchFamily, pitch,
1946 XML_charset, charset );
1949 if ((bComplex
1950 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameComplex", eState)
1951 && eState == beans::PropertyState_DIRECT_VALUE))
1952 || (!bComplex
1953 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameAsian", eState)
1954 && eState == beans::PropertyState_DIRECT_VALUE)))
1956 const char* const pitch = nullptr;
1957 const char* const charset = nullptr;
1958 OUString usTypeface;
1960 mAny >>= usTypeface;
1961 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
1962 if (!aSubstName.isEmpty())
1963 usTypeface = aSubstName;
1965 mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea,
1966 XML_typeface, usTypeface.toUtf8(),
1967 XML_pitchFamily, pitch,
1968 XML_charset, charset );
1971 if( bIsField )
1973 Reference< XTextField > rXTextField;
1974 if (GetProperty(rXPropSet, "TextField"))
1975 mAny >>= rXTextField;
1976 if( rXTextField.is() )
1977 rXPropSet.set( rXTextField, UNO_QUERY );
1980 // field properties starts here
1981 if (GetProperty(rXPropSet, "URL"))
1983 OUString sURL;
1985 mAny >>= sURL;
1986 if( !sURL.isEmpty() ) {
1987 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1988 oox::getRelationship(Relationship::HYPERLINK),
1989 sURL, true );
1991 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8());
1995 mpFS->endElementNS( XML_a, nElement );
1998 OUString DrawingML::GetFieldValue( const css::uno::Reference< css::text::XTextRange >& rRun, bool& bIsURLField )
2000 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
2001 OUString aFieldType, aFieldValue;
2003 if (GetProperty(rXPropSet, "TextPortionType"))
2005 aFieldType = *o3tl::doAccess<OUString>(mAny);
2006 SAL_INFO("oox.shape", "field type: " << aFieldType);
2009 if( aFieldType == "TextField" )
2011 Reference< XTextField > rXTextField;
2012 if (GetProperty(rXPropSet, "TextField"))
2013 mAny >>= rXTextField;
2014 if( rXTextField.is() )
2016 rXPropSet.set( rXTextField, UNO_QUERY );
2017 if( rXPropSet.is() )
2019 OUString aFieldKind( rXTextField->getPresentation( true ) );
2020 SAL_INFO("oox.shape", "field kind: " << aFieldKind);
2021 if( aFieldKind == "Page" )
2023 aFieldValue = "slidenum";
2025 else if( aFieldKind == "Pages" )
2027 aFieldValue = "slidecount";
2029 else if( aFieldKind == "PageName" )
2031 aFieldValue = "slidename";
2033 else if( aFieldKind == "URL" )
2035 bIsURLField = true;
2036 if (GetProperty(rXPropSet, "Representation"))
2037 mAny >>= aFieldValue;
2040 else if(aFieldKind == "Date")
2042 sal_Int32 nNumFmt = -1;
2043 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
2044 switch(static_cast<SvxDateFormat>(nNumFmt))
2046 case SvxDateFormat::StdSmall:
2047 case SvxDateFormat::A: aFieldValue = "datetime"; // 13/02/96
2048 break;
2049 case SvxDateFormat::B: aFieldValue = "datetime1"; // 13/02/1996
2050 break;
2051 case SvxDateFormat::StdBig:
2052 case SvxDateFormat::D: aFieldValue = "datetime3"; // 13 February 1996
2053 break;
2054 default: break;
2057 else if(aFieldKind == "ExtTime")
2059 sal_Int32 nNumFmt = -1;
2060 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
2061 switch(static_cast<SvxTimeFormat>(nNumFmt))
2063 case SvxTimeFormat::Standard:
2064 case SvxTimeFormat::HH24_MM_SS:
2065 aFieldValue = "datetime11"; // 13:49:38
2066 break;
2067 case SvxTimeFormat::HH24_MM:
2068 aFieldValue = "datetime10"; // 13:49
2069 break;
2070 case SvxTimeFormat::HH12_MM:
2071 aFieldValue = "datetime12"; // 01:49 PM
2072 break;
2073 case SvxTimeFormat::HH12_MM_SS:
2074 aFieldValue = "datetime13"; // 01:49:38 PM
2075 break;
2076 default: break;
2079 else if(aFieldKind == "ExtFile")
2081 sal_Int32 nNumFmt = -1;
2082 rXPropSet->getPropertyValue(UNO_TC_PROP_FILE_FORMAT) >>= nNumFmt;
2083 switch(nNumFmt)
2085 case 0: aFieldValue = "file"; // Path/File name
2086 break;
2087 case 1: aFieldValue = "file1"; // Path
2088 break;
2089 case 2: aFieldValue = "file2"; // File name without extension
2090 break;
2091 case 3: aFieldValue = "file3"; // File name with extension
2094 else if(aFieldKind == "Author")
2096 aFieldValue = "author";
2101 return aFieldValue;
2104 void DrawingML::WriteRun( const Reference< XTextRange >& rRun,
2105 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight)
2107 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
2108 sal_Int16 nLevel = -1;
2109 if (GetProperty(rXPropSet, "NumberingLevel"))
2110 mAny >>= nLevel;
2112 bool bNumberingIsNumber = true;
2113 if (GetProperty(rXPropSet, "NumberingIsNumber"))
2114 mAny >>= bNumberingIsNumber;
2116 bool bIsURLField = false;
2117 OUString sFieldValue = GetFieldValue( rRun, bIsURLField );
2118 bool bWriteField = !( sFieldValue.isEmpty() || bIsURLField );
2120 OUString sText = rRun->getString();
2122 //if there is no text following the bullet, add a space after the bullet
2123 if (nLevel !=-1 && bNumberingIsNumber && sText.isEmpty() )
2124 sText=" ";
2126 if ( bIsURLField )
2127 sText = sFieldValue;
2129 if( sText.isEmpty())
2131 Reference< XPropertySet > xPropSet( rRun, UNO_QUERY );
2135 if( !xPropSet.is() || !( xPropSet->getPropertyValue( "PlaceholderText" ) >>= sText ) )
2136 return;
2137 if( sText.isEmpty() )
2138 return;
2140 catch (const Exception &)
2142 return;
2146 if (sText == "\n")
2148 mpFS->singleElementNS(XML_a, XML_br);
2150 else
2152 if( bWriteField )
2154 OString sUUID(comphelper::xml::generateGUIDString());
2155 mpFS->startElementNS( XML_a, XML_fld,
2156 XML_id, sUUID.getStr(),
2157 XML_type, sFieldValue.toUtf8() );
2159 else
2161 mpFS->startElementNS(XML_a, XML_r);
2164 Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY );
2166 WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight, GetScriptType(sText) );
2167 mpFS->startElementNS(XML_a, XML_t);
2168 mpFS->writeEscaped( sText );
2169 mpFS->endElementNS( XML_a, XML_t );
2171 if( bWriteField )
2172 mpFS->endElementNS( XML_a, XML_fld );
2173 else
2174 mpFS->endElementNS( XML_a, XML_r );
2178 static OUString GetAutoNumType(SvxNumType nNumberingType, bool bSDot, bool bPBehind, bool bPBoth)
2180 OUString sPrefixSuffix;
2182 if (bPBoth)
2183 sPrefixSuffix = "ParenBoth";
2184 else if (bPBehind)
2185 sPrefixSuffix = "ParenR";
2186 else if (bSDot)
2187 sPrefixSuffix = "Period";
2189 switch( nNumberingType )
2191 case SVX_NUM_CHARS_UPPER_LETTER_N :
2192 case SVX_NUM_CHARS_UPPER_LETTER :
2193 return "alphaUc" + sPrefixSuffix;
2195 case SVX_NUM_CHARS_LOWER_LETTER_N :
2196 case SVX_NUM_CHARS_LOWER_LETTER :
2197 return "alphaLc" + sPrefixSuffix;
2199 case SVX_NUM_ROMAN_UPPER :
2200 return "romanUc" + sPrefixSuffix;
2202 case SVX_NUM_ROMAN_LOWER :
2203 return "romanLc" + sPrefixSuffix;
2205 case SVX_NUM_ARABIC :
2207 if (sPrefixSuffix.isEmpty())
2208 return "arabicPlain";
2209 else
2210 return "arabic" + sPrefixSuffix;
2212 default:
2213 break;
2216 return OUString();
2219 void DrawingML::WriteParagraphNumbering(const Reference< XPropertySet >& rXPropSet, float fFirstCharHeight, sal_Int16 nLevel )
2221 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2222 return;
2224 Reference< XIndexAccess > rXIndexAccess;
2226 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2227 return;
2229 SAL_INFO("oox.shape", "numbering rules");
2231 Sequence<PropertyValue> aPropertySequence;
2232 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2234 if (!aPropertySequence.hasElements())
2235 return;
2237 SvxNumType nNumberingType = SVX_NUM_NUMBER_NONE;
2238 bool bSDot = false;
2239 bool bPBehind = false;
2240 bool bPBoth = false;
2241 sal_Unicode aBulletChar = 0x2022; // a bullet
2242 awt::FontDescriptor aFontDesc;
2243 bool bHasFontDesc = false;
2244 uno::Reference<graphic::XGraphic> xGraphic;
2245 sal_Int16 nBulletRelSize = 0;
2246 sal_Int16 nStartWith = 1;
2247 ::Color nBulletColor;
2248 bool bHasBulletColor = false;
2249 awt::Size aGraphicSize;
2251 for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) )
2253 OUString aPropName( rPropValue.Name );
2254 SAL_INFO("oox.shape", "pro name: " << aPropName);
2255 if ( aPropName == "NumberingType" )
2257 nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(rPropValue.Value));
2259 else if ( aPropName == "Prefix" )
2261 if( *o3tl::doAccess<OUString>(rPropValue.Value) == ")")
2262 bPBoth = true;
2264 else if ( aPropName == "Suffix" )
2266 auto s = o3tl::doAccess<OUString>(rPropValue.Value);
2267 if( *s == ".")
2268 bSDot = true;
2269 else if( *s == ")")
2270 bPBehind = true;
2272 else if(aPropName == "BulletColor")
2274 nBulletColor = ::Color(*o3tl::doAccess<sal_uInt32>(rPropValue.Value));
2275 bHasBulletColor = true;
2277 else if ( aPropName == "BulletChar" )
2279 aBulletChar = (*o3tl::doAccess<OUString>(rPropValue.Value))[ 0 ];
2281 else if ( aPropName == "BulletFont" )
2283 aFontDesc = *o3tl::doAccess<awt::FontDescriptor>(rPropValue.Value);
2284 bHasFontDesc = true;
2286 // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font,
2287 // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used.
2288 // Because there might exist a lot of damaged documemts I added this two lines
2289 // which fixes the bullet problem for the export.
2290 if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") )
2291 aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252;
2294 else if ( aPropName == "BulletRelSize" )
2296 nBulletRelSize = *o3tl::doAccess<sal_Int16>(rPropValue.Value);
2298 else if ( aPropName == "StartWith" )
2300 nStartWith = *o3tl::doAccess<sal_Int16>(rPropValue.Value);
2302 else if (aPropName == "GraphicBitmap")
2304 auto xBitmap = rPropValue.Value.get<uno::Reference<awt::XBitmap>>();
2305 xGraphic.set(xBitmap, uno::UNO_QUERY);
2307 else if ( aPropName == "GraphicSize" )
2309 aGraphicSize = *o3tl::doAccess<awt::Size>(rPropValue.Value);
2310 SAL_INFO("oox.shape", "graphic size: " << aGraphicSize.Width << "x" << aGraphicSize.Height);
2314 if (nNumberingType == SVX_NUM_NUMBER_NONE)
2315 return;
2317 Graphic aGraphic(xGraphic);
2318 if (xGraphic.is() && aGraphic.GetType() != GraphicType::NONE)
2320 long nFirstCharHeightMm = TransformMetric(fFirstCharHeight * 100.f, FieldUnit::POINT, FieldUnit::MM);
2321 float fBulletSizeRel = aGraphicSize.Height / static_cast<float>(nFirstCharHeightMm) / OOX_BULLET_LIST_SCALE_FACTOR;
2323 OUString sRelationId;
2325 if (fBulletSizeRel < 1.0f)
2327 // Add padding to get the bullet point centered in PPT
2328 Size aDestSize(64, 64);
2329 float fBulletSizeRelX = fBulletSizeRel / aGraphicSize.Height * aGraphicSize.Width;
2330 long nPaddingX = std::max<long>(0, std::lround((aDestSize.Width() - fBulletSizeRelX * aDestSize.Width()) / 2.f));
2331 long nPaddingY = std::lround((aDestSize.Height() - fBulletSizeRel * aDestSize.Height()) / 2.f);
2332 tools::Rectangle aDestRect(nPaddingX, nPaddingY, aDestSize.Width() - nPaddingX, aDestSize.Height() - nPaddingY);
2334 BitmapEx aSourceBitmap(aGraphic.GetBitmapEx());
2335 aSourceBitmap.Scale(aDestRect.GetSize());
2336 tools::Rectangle aSourceRect(Point(0, 0), aDestRect.GetSize());
2337 BitmapEx aDestBitmap(aDestSize, 24);
2338 aDestBitmap.CopyPixel(aDestRect, aSourceRect, &aSourceBitmap);
2339 Graphic aDestGraphic(aDestBitmap);
2340 sRelationId = WriteImage(aDestGraphic);
2341 fBulletSizeRel = 1.0f;
2343 else
2345 sRelationId = WriteImage(aGraphic);
2348 mpFS->singleElementNS( XML_a, XML_buSzPct,
2349 XML_val, OString::number(std::min<sal_Int32>(std::lround(100000.f * fBulletSizeRel), 400000)));
2350 mpFS->startElementNS(XML_a, XML_buBlip);
2351 mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelationId.toUtf8());
2352 mpFS->endElementNS( XML_a, XML_buBlip );
2354 else
2356 if(bHasBulletColor)
2358 if (nBulletColor == COL_AUTO )
2360 nBulletColor = ::Color(mbIsBackgroundDark ? 0xffffff : 0x000000);
2362 mpFS->startElementNS(XML_a, XML_buClr);
2363 WriteColor( nBulletColor );
2364 mpFS->endElementNS( XML_a, XML_buClr );
2367 if( nBulletRelSize && nBulletRelSize != 100 )
2368 mpFS->singleElementNS( XML_a, XML_buSzPct,
2369 XML_val, OString::number(std::clamp<sal_Int32>(1000*nBulletRelSize, 25000, 400000)));
2370 if( bHasFontDesc )
2372 if ( SVX_NUM_CHAR_SPECIAL == nNumberingType )
2373 aBulletChar = SubstituteBullet( aBulletChar, aFontDesc );
2374 mpFS->singleElementNS( XML_a, XML_buFont,
2375 XML_typeface, aFontDesc.Name.toUtf8(),
2376 XML_charset, (aFontDesc.CharSet == awt::CharSet::SYMBOL) ? "2" : nullptr );
2379 OUString aAutoNumType = GetAutoNumType( nNumberingType, bSDot, bPBehind, bPBoth );
2381 if (!aAutoNumType.isEmpty())
2383 mpFS->singleElementNS(XML_a, XML_buAutoNum,
2384 XML_type, aAutoNumType.toUtf8(),
2385 XML_startAt, nStartWith > 1 ? OString::number(nStartWith).getStr() : nullptr);
2387 else
2389 mpFS->singleElementNS(XML_a, XML_buChar, XML_char, OUString(aBulletChar).toUtf8());
2394 bool DrawingML::IsGroupShape( const Reference< XShape >& rXShape )
2396 bool bRet = false;
2397 if ( rXShape.is() )
2399 uno::Reference<lang::XServiceInfo> xServiceInfo(rXShape, uno::UNO_QUERY_THROW);
2400 bRet = xServiceInfo->supportsService("com.sun.star.drawing.GroupShape");
2402 return bRet;
2405 bool DrawingML::IsDiagram(const Reference<XShape>& rXShape)
2407 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
2408 if (!xPropSet.is())
2409 return false;
2411 // if the shape doesn't have the InteropGrabBag property, it's not a diagram
2412 uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
2413 OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
2414 if (!xPropSetInfo->hasPropertyByName(aName))
2415 return false;
2417 uno::Sequence<beans::PropertyValue> propList;
2418 xPropSet->getPropertyValue(aName) >>= propList;
2419 return std::any_of(std::cbegin(propList), std::cend(propList),
2420 [](const beans::PropertyValue& rProp) {
2421 // if we find any of the diagram components, it's a diagram
2422 OUString propName = rProp.Name;
2423 return propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle"
2424 || propName == "OOXColor" || propName == "OOXDrawing";
2428 sal_Int32 DrawingML::getBulletMarginIndentation (const Reference< XPropertySet >& rXPropSet,sal_Int16 nLevel, const OUString& propName)
2430 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2431 return 0;
2433 Reference< XIndexAccess > rXIndexAccess;
2435 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2436 return 0;
2438 SAL_INFO("oox.shape", "numbering rules");
2440 Sequence<PropertyValue> aPropertySequence;
2441 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2443 if (!aPropertySequence.hasElements())
2444 return 0;
2446 for ( const PropertyValue& rPropValue : std::as_const(aPropertySequence) )
2448 OUString aPropName( rPropValue.Name );
2449 SAL_INFO("oox.shape", "pro name: " << aPropName);
2450 if ( aPropName == propName )
2451 return *o3tl::doAccess<sal_Int32>(rPropValue.Value);
2454 return 0;
2457 const char* DrawingML::GetAlignment( style::ParagraphAdjust nAlignment )
2459 const char* sAlignment = nullptr;
2461 switch( nAlignment )
2463 case style::ParagraphAdjust_CENTER:
2464 sAlignment = "ctr";
2465 break;
2466 case style::ParagraphAdjust_RIGHT:
2467 sAlignment = "r";
2468 break;
2469 case style::ParagraphAdjust_BLOCK:
2470 sAlignment = "just";
2471 break;
2472 default:
2476 return sAlignment;
2479 void DrawingML::WriteLinespacing( const LineSpacing& rSpacing )
2481 if( rSpacing.Mode == LineSpacingMode::PROP )
2483 mpFS->singleElementNS( XML_a, XML_spcPct,
2484 XML_val, OString::number(static_cast<sal_Int32>(rSpacing.Height)*1000));
2486 else
2488 mpFS->singleElementNS( XML_a, XML_spcPts,
2489 XML_val, OString::number(std::lround(rSpacing.Height / 25.4 * 72)));
2493 void DrawingML::WriteParagraphProperties( const Reference< XTextContent >& rParagraph, float fFirstCharHeight)
2495 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
2496 Reference< XPropertyState > rXPropState( rParagraph, UNO_QUERY );
2497 PropertyState eState;
2499 if( !rXPropSet.is() || !rXPropState.is() )
2500 return;
2502 sal_Int16 nLevel = -1;
2503 if (GetProperty(rXPropSet, "NumberingLevel"))
2504 mAny >>= nLevel;
2506 sal_Int16 nTmp = sal_Int16(style::ParagraphAdjust_LEFT);
2507 if (GetProperty(rXPropSet, "ParaAdjust"))
2508 mAny >>= nTmp;
2509 style::ParagraphAdjust nAlignment = static_cast<style::ParagraphAdjust>(nTmp);
2511 bool bHasLinespacing = false;
2512 LineSpacing aLineSpacing;
2513 if (GetPropertyAndState(rXPropSet, rXPropState, "ParaLineSpacing", eState)
2514 && eState == beans::PropertyState_DIRECT_VALUE)
2515 bHasLinespacing = ( mAny >>= aLineSpacing );
2517 bool bRtl = false;
2518 if (GetProperty(rXPropSet, "WritingMode"))
2520 sal_Int16 nWritingMode;
2521 if( ( mAny >>= nWritingMode ) && nWritingMode == text::WritingMode2::RL_TB )
2523 bRtl = true;
2527 sal_Int32 nParaLeftMargin = 0;
2528 sal_Int32 nParaFirstLineIndent = 0;
2530 if (GetProperty(rXPropSet, "ParaLeftMargin"))
2531 mAny >>= nParaLeftMargin;
2532 if (GetProperty(rXPropSet, "ParaFirstLineIndent"))
2533 mAny >>= nParaFirstLineIndent;
2535 sal_Int32 nParaTopMargin = 0;
2536 sal_Int32 nParaBottomMargin = 0;
2538 if (GetProperty(rXPropSet, "ParaTopMargin"))
2539 mAny >>= nParaTopMargin;
2540 if (GetProperty(rXPropSet, "ParaBottomMargin"))
2541 mAny >>= nParaBottomMargin;
2543 sal_Int32 nLeftMargin = getBulletMarginIndentation ( rXPropSet, nLevel,"LeftMargin");
2544 sal_Int32 nLineIndentation = getBulletMarginIndentation ( rXPropSet, nLevel,"FirstLineOffset");
2546 if( nLevel != -1
2547 || nAlignment != style::ParagraphAdjust_LEFT
2548 || bHasLinespacing )
2550 if (nParaLeftMargin) // For Paragraph
2551 mpFS->startElementNS( XML_a, XML_pPr,
2552 XML_lvl, nLevel > 0 ? OString::number(nLevel).getStr() : nullptr,
2553 XML_marL, nParaLeftMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nParaLeftMargin)).getStr() : nullptr,
2554 XML_indent, nParaFirstLineIndent ? OString::number(oox::drawingml::convertHmmToEmu(nParaFirstLineIndent)).getStr() : nullptr,
2555 XML_algn, GetAlignment( nAlignment ),
2556 XML_rtl, bRtl ? ToPsz10(bRtl) : nullptr );
2557 else
2558 mpFS->startElementNS( XML_a, XML_pPr,
2559 XML_lvl, nLevel > 0 ? OString::number(nLevel).getStr() : nullptr,
2560 XML_marL, nLeftMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)).getStr() : nullptr,
2561 XML_indent, nLineIndentation ? OString::number(oox::drawingml::convertHmmToEmu(nLineIndentation)).getStr() : nullptr,
2562 XML_algn, GetAlignment( nAlignment ),
2563 XML_rtl, bRtl ? ToPsz10(bRtl) : nullptr );
2566 if( bHasLinespacing )
2568 mpFS->startElementNS(XML_a, XML_lnSpc);
2569 WriteLinespacing( aLineSpacing );
2570 mpFS->endElementNS( XML_a, XML_lnSpc );
2573 if( nParaTopMargin != 0 )
2575 mpFS->startElementNS(XML_a, XML_spcBef);
2577 mpFS->singleElementNS( XML_a, XML_spcPts,
2578 XML_val, OString::number(std::lround(nParaTopMargin / 25.4 * 72)));
2580 mpFS->endElementNS( XML_a, XML_spcBef );
2583 if( nParaBottomMargin != 0 )
2585 mpFS->startElementNS(XML_a, XML_spcAft);
2587 mpFS->singleElementNS( XML_a, XML_spcPts,
2588 XML_val, OString::number(std::lround(nParaBottomMargin / 25.4 * 72)));
2590 mpFS->endElementNS( XML_a, XML_spcAft );
2593 WriteParagraphNumbering( rXPropSet, fFirstCharHeight, nLevel );
2595 mpFS->endElementNS( XML_a, XML_pPr );
2599 void DrawingML::WriteParagraph( const Reference< XTextContent >& rParagraph,
2600 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight )
2602 Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY );
2603 if( !access.is() )
2604 return;
2606 Reference< XEnumeration > enumeration( access->createEnumeration() );
2607 if( !enumeration.is() )
2608 return;
2610 mpFS->startElementNS(XML_a, XML_p);
2612 bool bPropertiesWritten = false;
2613 while( enumeration->hasMoreElements() )
2615 Reference< XTextRange > run;
2616 Any any ( enumeration->nextElement() );
2618 if (any >>= run)
2620 if( !bPropertiesWritten )
2622 float fFirstCharHeight = rnCharHeight / 1000.;
2623 Reference< XPropertySet > xFirstRunPropSet (run, UNO_QUERY);
2624 Reference< XPropertySetInfo > xFirstRunPropSetInfo = xFirstRunPropSet->getPropertySetInfo();
2625 if( xFirstRunPropSetInfo->hasPropertyByName("CharHeight") )
2626 fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>();
2627 WriteParagraphProperties( rParagraph, fFirstCharHeight );
2628 bPropertiesWritten = true;
2630 WriteRun( run, rbOverridingCharHeight, rnCharHeight );
2633 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
2634 WriteRunProperties( rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight, rnCharHeight );
2636 mpFS->endElementNS( XML_a, XML_p );
2639 void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUString& presetWarp, bool bBodyPr, bool bText, sal_Int32 nXmlNamespace )
2641 Reference< XText > xXText( rXIface, UNO_QUERY );
2642 Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY );
2644 if( !xXText.is() )
2645 return;
2647 sal_Int32 nTextRotateAngle = 0;
2648 bool bIsFontworkShape(presetWarp.startsWith("text") && (presetWarp != "textNoShape"));
2650 #define DEFLRINS 254
2651 #define DEFTBINS 127
2652 sal_Int32 nLeft, nRight, nTop, nBottom;
2653 nLeft = nRight = DEFLRINS;
2654 nTop = nBottom = DEFTBINS;
2656 // top inset looks a bit different compared to ppt export
2657 // check if something related doesn't work as expected
2658 if (GetProperty(rXPropSet, "TextLeftDistance"))
2659 mAny >>= nLeft;
2660 if (GetProperty(rXPropSet, "TextRightDistance"))
2661 mAny >>= nRight;
2662 if (GetProperty(rXPropSet, "TextUpperDistance"))
2663 mAny >>= nTop;
2664 if (GetProperty(rXPropSet, "TextLowerDistance"))
2665 mAny >>= nBottom;
2667 TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP );
2668 const char* sVerticalAlignment = nullptr;
2669 if (GetProperty(rXPropSet, "TextVerticalAdjust"))
2670 mAny >>= eVerticalAlignment;
2671 if( eVerticalAlignment != TextVerticalAdjust_TOP )
2672 sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment);
2674 const char* sWritingMode = nullptr;
2675 bool bVertical = false;
2676 if (GetProperty(rXPropSet, "TextWritingMode"))
2678 WritingMode eMode;
2680 if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL )
2682 sWritingMode = "vert";
2683 bVertical = true;
2687 Sequence<drawing::EnhancedCustomShapeAdjustmentValue> aAdjustmentSeq;
2688 uno::Sequence<beans::PropertyValue> aTextPathSeq;
2689 bool bScaleX(false);
2691 if (GetProperty(rXPropSet, "CustomShapeGeometry"))
2693 Sequence< PropertyValue > aProps;
2694 if ( mAny >>= aProps )
2696 for ( const auto& rProp : std::as_const(aProps) )
2698 if ( rProp.Name == "TextPreRotateAngle" && ( rProp.Value >>= nTextRotateAngle ) )
2700 if ( nTextRotateAngle == -90 )
2702 sWritingMode = "vert";
2703 bVertical = true;
2705 else if ( nTextRotateAngle == -270 )
2707 sWritingMode = "vert270";
2708 bVertical = true;
2710 if (!bIsFontworkShape)
2711 break;
2713 else if (rProp.Name == "AdjustmentValues")
2714 rProp.Value >>= aAdjustmentSeq;
2715 else if (rProp.Name == "TextPath")
2717 rProp.Value >>= aTextPathSeq;
2718 for (const auto& rTextPath : std::as_const(aTextPathSeq))
2720 if (rTextPath.Name == "ScaleX")
2721 rTextPath.Value >>= bScaleX;
2728 bool bFromWordArt = !bScaleX
2729 && ( presetWarp == "textArchDown" || presetWarp == "textArchUp"
2730 || presetWarp == "textButton" || presetWarp == "textCircle");
2732 TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER );
2733 bool bHorizontalCenter = false;
2734 if (GetProperty(rXPropSet, "TextHorizontalAdjust"))
2735 mAny >>= eHorizontalAlignment;
2736 if( eHorizontalAlignment == TextHorizontalAdjust_CENTER )
2737 bHorizontalCenter = true;
2738 else if( bVertical && eHorizontalAlignment == TextHorizontalAdjust_LEFT )
2739 sVerticalAlignment = "b";
2741 bool bHasWrap = false;
2742 bool bWrap = false;
2743 // Only custom shapes obey the TextWordWrap option, normal text always wraps.
2744 if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextWordWrap"))
2746 mAny >>= bWrap;
2747 bHasWrap = true;
2750 if (bBodyPr)
2752 const char* pWrap = bHasWrap && !bWrap ? "none" : nullptr;
2753 if (GetDocumentType() == DOCUMENT_DOCX)
2755 // In case of DOCX, if we want to have the same effect as
2756 // TextShape's automatic word wrapping, then we need to set
2757 // wrapping to square.
2758 uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY);
2759 if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.TextShape"))
2760 pWrap = "square";
2762 mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr,
2763 XML_wrap, pWrap,
2764 XML_fromWordArt, bFromWordArt ? "1" : nullptr,
2765 XML_lIns, (nLeft != DEFLRINS) ? OString::number(oox::drawingml::convertHmmToEmu(nLeft)).getStr() : nullptr,
2766 XML_rIns, (nRight != DEFLRINS) ? OString::number(oox::drawingml::convertHmmToEmu(nRight)).getStr() : nullptr,
2767 XML_tIns, (nTop != DEFTBINS) ? OString::number(oox::drawingml::convertHmmToEmu(nTop)).getStr() : nullptr,
2768 XML_bIns, (nBottom != DEFTBINS) ? OString::number(oox::drawingml::convertHmmToEmu(nBottom)).getStr() : nullptr,
2769 XML_anchor, sVerticalAlignment,
2770 XML_anchorCtr, bHorizontalCenter ? "1" : nullptr,
2771 XML_vert, sWritingMode,
2772 XML_rot, (nTextRotateAngle != 0) ? oox::drawingml::calcRotationValue( nTextRotateAngle * 100 ).getStr() : nullptr );
2773 if (bIsFontworkShape)
2775 if (aAdjustmentSeq.hasElements())
2777 mpFS->startElementNS(XML_a, XML_prstTxWarp, XML_prst, presetWarp.toUtf8());
2778 mpFS->startElementNS(XML_a, XML_avLst);
2779 for (sal_Int32 i = 0, nElems = aAdjustmentSeq.getLength(); i < nElems; ++i )
2781 OString sName = "adj" + (( nElems > 1 ) ? OString::number(i + 1) : OString());
2782 double fValue(0.0);
2783 if (aAdjustmentSeq[i].Value.getValueTypeClass() == TypeClass_DOUBLE)
2784 aAdjustmentSeq[i].Value >>= fValue;
2785 else
2787 sal_Int32 nNumber(0);
2788 aAdjustmentSeq[i].Value >>= nNumber;
2789 fValue = static_cast<double>(nNumber);
2791 // Convert from binary coordinate system with viewBox "0 0 21600 21600" and simple degree
2792 // to DrawingML with coordinate range 0..100000 and angle in 1/60000 degree.
2793 // Reverse to conversion in lcl_createPresetShape in drawingml/shape.cxx on import.
2794 if (presetWarp == "textArchDown" || presetWarp == "textArchUp"
2795 || presetWarp == "textButton" || presetWarp == "textCircle"
2796 || ((i == 0) && (presetWarp == "textArchDownPour" || presetWarp == "textArchUpPour"
2797 || presetWarp == "textButtonPour" || presetWarp == "textCirclePour")))
2799 fValue *= 60000.0;
2801 else if ((i == 1) && (presetWarp == "textDoubleWave1" || presetWarp == "textWave1"
2802 || presetWarp == "textWave2" || presetWarp == "textWave4"))
2804 fValue = fValue / 0.216 - 50000.0;
2806 else if ((i == 1) && (presetWarp == "textArchDownPour" || presetWarp == "textArchUpPour"
2807 || presetWarp == "textButtonPour" || presetWarp == "textCirclePour"))
2809 fValue /= 0.108;
2811 else
2813 fValue /= 0.216;
2815 OString sFmla = "val " + OString::number(std::lround(fValue));
2816 mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
2818 mpFS->endElementNS( XML_a, XML_avLst );
2819 mpFS->endElementNS(XML_a, XML_prstTxWarp);
2821 else
2823 mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, presetWarp.toUtf8());
2827 if (GetDocumentType() == DOCUMENT_DOCX || GetDocumentType() == DOCUMENT_XLSX)
2829 bool bTextAutoGrowHeight = false;
2830 if (GetProperty(rXPropSet, "TextAutoGrowHeight"))
2831 mAny >>= bTextAutoGrowHeight;
2832 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
2834 if (GetDocumentType() == DOCUMENT_PPTX)
2836 TextFitToSizeType eFit = TextFitToSizeType_NONE;
2837 if (GetProperty(rXPropSet, "TextFitToSize"))
2838 mAny >>= eFit;
2840 if (eFit == TextFitToSizeType_AUTOFIT)
2842 const sal_Int32 MAX_SCALE_VAL = 100000;
2843 sal_Int32 nFontScale = MAX_SCALE_VAL;
2844 SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get());
2845 if (pTextShape)
2847 SdrTextObj* pTextObject = dynamic_cast<SdrTextObj*>(pTextShape->GetSdrObject());
2848 if (pTextObject)
2850 double fScaleY = pTextObject->GetFontScaleY();
2851 nFontScale = static_cast<sal_uInt32>(fScaleY * 100) * 1000;
2855 mpFS->singleElementNS(XML_a, XML_normAutofit, XML_fontScale,
2856 ( nFontScale < MAX_SCALE_VAL && nFontScale > 0 ) ? OString::number(nFontScale).getStr() : nullptr);
2858 else
2860 // tdf#127030: Only custom shapes obey the TextAutoGrowHeight option.
2861 bool bTextAutoGrowHeight = false;
2862 if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextAutoGrowHeight"))
2863 mAny >>= bTextAutoGrowHeight;
2864 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
2867 mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr);
2870 Reference< XEnumerationAccess > access( xXText, UNO_QUERY );
2871 if( !access.is() || !bText )
2872 return;
2874 Reference< XEnumeration > enumeration( access->createEnumeration() );
2875 if( !enumeration.is() )
2876 return;
2878 uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
2879 SdrObject* pSdrObject = xShape.is() ? GetSdrObjectFromXShape(xShape) : nullptr;
2880 const SdrTextObj* pTxtObj = dynamic_cast<SdrTextObj*>( pSdrObject );
2881 if (pTxtObj && mpTextExport)
2883 const OutlinerParaObject* pParaObj = nullptr;
2884 bool bOwnParaObj = false;
2887 #i13885#
2888 When the object is actively being edited, that text is not set into
2889 the objects normal text object, but lives in a separate object.
2891 if (pTxtObj->IsTextEditActive())
2893 pParaObj = pTxtObj->GetEditOutlinerParaObject().release();
2894 bOwnParaObj = true;
2896 else
2897 pParaObj = pTxtObj->GetOutlinerParaObject();
2899 if (pParaObj)
2901 // this is reached only in case some text is attached to the shape
2902 mpTextExport->WriteOutliner(*pParaObj);
2903 if (bOwnParaObj)
2904 delete pParaObj;
2906 return;
2909 bool bOverridingCharHeight = false;
2910 sal_Int32 nCharHeight = -1;
2912 while( enumeration->hasMoreElements() )
2914 Reference< XTextContent > paragraph;
2915 Any any ( enumeration->nextElement() );
2917 if( any >>= paragraph)
2918 WriteParagraph( paragraph, bOverridingCharHeight, nCharHeight );
2922 void DrawingML::WritePresetShape( const char* pShape , std::vector< std::pair<sal_Int32,sal_Int32>> & rAvList )
2924 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2925 if ( !rAvList.empty() )
2928 mpFS->startElementNS(XML_a, XML_avLst);
2929 for (auto const& elem : rAvList)
2931 OString sName = "adj" + ( ( elem.first > 0 ) ? OString::number(elem.first) : OString() );
2932 OString sFmla = "val " + OString::number( elem.second );
2934 mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
2936 mpFS->endElementNS( XML_a, XML_avLst );
2938 else
2939 mpFS->singleElementNS(XML_a, XML_avLst);
2941 mpFS->endElementNS( XML_a, XML_prstGeom );
2944 void DrawingML::WritePresetShape( const char* pShape )
2946 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2947 mpFS->singleElementNS(XML_a, XML_avLst);
2948 mpFS->endElementNS( XML_a, XML_prstGeom );
2951 static std::map< OString, std::vector<OString> > lcl_getAdjNames()
2953 std::map< OString, std::vector<OString> > aRet;
2955 OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/oox-drawingml-adj-names");
2956 rtl::Bootstrap::expandMacros(aPath);
2957 SvFileStream aStream(aPath, StreamMode::READ);
2958 if (aStream.GetError() != ERRCODE_NONE)
2959 SAL_WARN("oox.shape", "failed to open oox-drawingml-adj-names");
2960 OString aLine;
2961 bool bNotDone = aStream.ReadLine(aLine);
2962 while (bNotDone)
2964 sal_Int32 nIndex = 0;
2965 // Each line is in a "key\tvalue" format: read the key, the rest is the value.
2966 OString aKey = aLine.getToken(0, '\t', nIndex);
2967 OString aValue = aLine.copy(nIndex);
2968 aRet[aKey].push_back(aValue);
2969 bNotDone = aStream.ReadLine(aLine);
2971 return aRet;
2974 void DrawingML::WritePresetShape( const char* pShape, MSO_SPT eShapeType, bool bPredefinedHandlesUsed, const PropertyValue& rProp )
2976 static std::map< OString, std::vector<OString> > aAdjMap = lcl_getAdjNames();
2977 // If there are predefined adj names for this shape type, look them up now.
2978 std::vector<OString> aAdjustments;
2979 if (aAdjMap.find(OString(pShape)) != aAdjMap.end())
2980 aAdjustments = aAdjMap[OString(pShape)];
2982 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2983 mpFS->startElementNS(XML_a, XML_avLst);
2985 Sequence< drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
2986 if ( ( rProp.Value >>= aAdjustmentSeq )
2987 && eShapeType != mso_sptActionButtonForwardNext // we have adjustments values for these type of shape, but MSO doesn't like them
2988 && eShapeType != mso_sptActionButtonBackPrevious // so they are now disabled
2989 && OString(pShape) != "rect" //some shape types are commented out in pCustomShapeTypeTranslationTable[] & are being defaulted to rect & rect does not have adjustment values/name.
2992 SAL_INFO("oox.shape", "adj seq len: " << aAdjustmentSeq.getLength());
2993 sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
2994 if ( bPredefinedHandlesUsed )
2995 EscherPropertyContainer::LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );
2997 sal_Int32 nValue, nLength = aAdjustmentSeq.getLength();
2998 // 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.
2999 // Sometimes there are more values than needed, so we ignore the excessive ones.
3000 if (aAdjustments.size() <= static_cast<sal_uInt32>(nLength))
3002 for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAdjustments.size()); i++)
3004 if( EscherPropertyContainer::GetAdjustmentValue( aAdjustmentSeq[ i ], i, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
3006 // If the document model doesn't have an adjustment name (e.g. shape was created from VML), then take it from the predefined list.
3007 OString aAdjName = aAdjustmentSeq[i].Name.isEmpty()
3008 ? aAdjustments[i]
3009 : aAdjustmentSeq[i].Name.toUtf8();
3011 mpFS->singleElementNS( XML_a, XML_gd,
3012 XML_name, aAdjName,
3013 XML_fmla, "val " + OString::number(nValue));
3019 mpFS->endElementNS( XML_a, XML_avLst );
3020 mpFS->endElementNS( XML_a, XML_prstGeom );
3023 bool DrawingML::WriteCustomGeometry(
3024 const Reference< XShape >& rXShape,
3025 const SdrObjCustomShape& rSdrObjCustomShape)
3027 uno::Reference< beans::XPropertySet > aXPropSet;
3028 uno::Any aAny( rXShape->queryInterface(cppu::UnoType<beans::XPropertySet>::get()));
3030 if ( ! (aAny >>= aXPropSet) )
3031 return false;
3035 aAny = aXPropSet->getPropertyValue( "CustomShapeGeometry" );
3036 if ( !aAny.hasValue() )
3037 return false;
3039 catch( const ::uno::Exception& )
3041 return false;
3045 auto pGeometrySeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny);
3047 if ( pGeometrySeq )
3049 for( int i = 0; i < pGeometrySeq->getLength(); ++i )
3051 const beans::PropertyValue& rProp = (*pGeometrySeq)[ i ];
3052 if ( rProp.Name == "Path" )
3054 uno::Sequence<beans::PropertyValue> aPathProp;
3055 rProp.Value >>= aPathProp;
3057 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs;
3058 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
3059 uno::Sequence<awt::Size> aPathSize;
3060 for (const beans::PropertyValue& rPathProp : std::as_const(aPathProp))
3062 if (rPathProp.Name == "Coordinates")
3063 rPathProp.Value >>= aPairs;
3064 else if (rPathProp.Name == "Segments")
3065 rPathProp.Value >>= aSegments;
3066 else if (rPathProp.Name == "SubViewSize")
3067 rPathProp.Value >>= aPathSize;
3070 if ( !aPairs.hasElements() )
3071 return false;
3073 if ( !aSegments.hasElements() )
3075 aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment>(4);
3076 aSegments[0].Count = 1;
3077 aSegments[0].Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
3078 aSegments[1].Count = static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) ));
3079 aSegments[1].Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
3080 aSegments[2].Count = 0;
3081 aSegments[2].Command = drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
3082 aSegments[3].Count = 0;
3083 aSegments[3].Command = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
3086 int nExpectedPairCount = std::accumulate(std::cbegin(aSegments), std::cend(aSegments), 0,
3087 [](const int nSum, const drawing::EnhancedCustomShapeSegment& rSegment) { return nSum + rSegment.Count; });
3089 if ( nExpectedPairCount > aPairs.getLength() )
3091 SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs.");
3092 return false;
3095 mpFS->startElementNS(XML_a, XML_custGeom);
3096 mpFS->singleElementNS(XML_a, XML_avLst);
3097 mpFS->singleElementNS(XML_a, XML_gdLst);
3098 mpFS->singleElementNS(XML_a, XML_ahLst);
3099 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t",
3100 XML_r, "r", XML_b, "b");
3101 mpFS->startElementNS(XML_a, XML_pathLst);
3103 if ( aPathSize.hasElements() )
3105 mpFS->startElementNS( XML_a, XML_path,
3106 XML_w, OString::number(aPathSize[0].Width),
3107 XML_h, OString::number(aPathSize[0].Height) );
3109 else
3111 sal_Int32 nXMin(0);
3112 aPairs[0].First.Value >>= nXMin;
3113 sal_Int32 nXMax = nXMin;
3114 sal_Int32 nYMin(0);
3115 aPairs[0].Second.Value >>= nYMin;
3116 sal_Int32 nYMax = nYMin;
3118 for ( const auto& rPair : std::as_const(aPairs) )
3120 sal_Int32 nX = GetCustomGeometryPointValue(rPair.First, rSdrObjCustomShape);
3121 sal_Int32 nY = GetCustomGeometryPointValue(rPair.Second, rSdrObjCustomShape);
3122 if (nX < nXMin)
3123 nXMin = nX;
3124 if (nY < nYMin)
3125 nYMin = nY;
3126 if (nX > nXMax)
3127 nXMax = nX;
3128 if (nY > nYMax)
3129 nYMax = nY;
3131 mpFS->startElementNS( XML_a, XML_path,
3132 XML_w, OString::number(nXMax - nXMin),
3133 XML_h, OString::number(nYMax - nYMin) );
3137 int nPairIndex = 0;
3138 bool bOK = true;
3139 for (const auto& rSegment : std::as_const(aSegments))
3141 if ( rSegment.Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
3143 mpFS->singleElementNS(XML_a, XML_close);
3145 for (int k = 0; k < rSegment.Count && bOK; ++k)
3147 switch( rSegment.Command )
3149 case drawing::EnhancedCustomShapeSegmentCommand::MOVETO :
3151 if (nPairIndex >= aPairs.getLength())
3152 bOK = false;
3153 else
3155 mpFS->startElementNS(XML_a, XML_moveTo);
3156 WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
3157 mpFS->endElementNS( XML_a, XML_moveTo );
3158 nPairIndex++;
3160 break;
3162 case drawing::EnhancedCustomShapeSegmentCommand::LINETO :
3164 if (nPairIndex >= aPairs.getLength())
3165 bOK = false;
3166 else
3168 mpFS->startElementNS(XML_a, XML_lnTo);
3169 WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
3170 mpFS->endElementNS( XML_a, XML_lnTo );
3171 nPairIndex++;
3173 break;
3175 case drawing::EnhancedCustomShapeSegmentCommand::CURVETO :
3177 if (nPairIndex + 2 >= aPairs.getLength())
3178 bOK = false;
3179 else
3181 mpFS->startElementNS(XML_a, XML_cubicBezTo);
3182 for( sal_uInt8 l = 0; l <= 2; ++l )
3184 WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3186 mpFS->endElementNS( XML_a, XML_cubicBezTo );
3187 nPairIndex += 3;
3189 break;
3191 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
3192 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
3194 nPairIndex += 3;
3195 break;
3197 case drawing::EnhancedCustomShapeSegmentCommand::ARCTO :
3198 case drawing::EnhancedCustomShapeSegmentCommand::ARC :
3199 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
3200 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
3202 nPairIndex += 4;
3203 break;
3205 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
3206 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
3208 nPairIndex++;
3209 break;
3211 case drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO :
3213 if (nPairIndex + 1 >= aPairs.getLength())
3214 bOK = false;
3215 else
3217 mpFS->startElementNS(XML_a, XML_quadBezTo);
3218 for( sal_uInt8 l = 0; l < 2; ++l )
3220 WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3222 mpFS->endElementNS( XML_a, XML_quadBezTo );
3223 nPairIndex += 2;
3225 break;
3227 case drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO :
3229 nPairIndex += 2;
3230 break;
3232 default:
3233 // do nothing
3234 break;
3237 if (!bOK)
3238 break;
3240 mpFS->endElementNS( XML_a, XML_path );
3241 mpFS->endElementNS( XML_a, XML_pathLst );
3242 mpFS->endElementNS( XML_a, XML_custGeom );
3243 return bOK;
3247 return false;
3250 void DrawingML::WriteCustomGeometryPoint(
3251 const drawing::EnhancedCustomShapeParameterPair& rParamPair,
3252 const SdrObjCustomShape& rSdrObjCustomShape)
3254 sal_Int32 nX = GetCustomGeometryPointValue(rParamPair.First, rSdrObjCustomShape);
3255 sal_Int32 nY = GetCustomGeometryPointValue(rParamPair.Second, rSdrObjCustomShape);
3257 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(nX), XML_y, OString::number(nY));
3260 sal_Int32 DrawingML::GetCustomGeometryPointValue(
3261 const css::drawing::EnhancedCustomShapeParameter& rParam,
3262 const SdrObjCustomShape& rSdrObjCustomShape)
3264 const EnhancedCustomShape2d aCustoShape2d(const_cast< SdrObjCustomShape& >(rSdrObjCustomShape));
3265 double fValue = 0.0;
3266 aCustoShape2d.GetParameter(fValue, rParam, false, false);
3267 sal_Int32 nValue(std::lround(fValue));
3269 return nValue;
3272 void DrawingML::WritePolyPolygon( const tools::PolyPolygon& rPolyPolygon, const bool bClosed )
3274 // In case of Writer, the parent element is <wps:spPr>, and there the
3275 // <a:custGeom> element is not optional.
3276 if (rPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX)
3277 return;
3279 mpFS->startElementNS(XML_a, XML_custGeom);
3280 mpFS->singleElementNS(XML_a, XML_avLst);
3281 mpFS->singleElementNS(XML_a, XML_gdLst);
3282 mpFS->singleElementNS(XML_a, XML_ahLst);
3283 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b");
3285 mpFS->startElementNS( XML_a, XML_pathLst );
3287 const tools::Rectangle aRect( rPolyPolygon.GetBoundRect() );
3289 // Put all polygons of rPolyPolygon in the same path element
3290 // to subtract the overlapped areas.
3291 mpFS->startElementNS( XML_a, XML_path,
3292 XML_w, OString::number(aRect.GetWidth()),
3293 XML_h, OString::number(aRect.GetHeight()) );
3295 for( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i ++ )
3298 const tools::Polygon& rPoly = rPolyPolygon[ i ];
3300 if( rPoly.GetSize() > 0 )
3302 mpFS->startElementNS(XML_a, XML_moveTo);
3304 mpFS->singleElementNS( XML_a, XML_pt,
3305 XML_x, OString::number(rPoly[0].X() - aRect.Left()),
3306 XML_y, OString::number(rPoly[0].Y() - aRect.Top()) );
3308 mpFS->endElementNS( XML_a, XML_moveTo );
3311 for( sal_uInt16 j = 1; j < rPoly.GetSize(); j ++ )
3313 PolyFlags flags = rPoly.GetFlags(j);
3314 if( flags == PolyFlags::Control )
3316 // a:cubicBezTo can only contain 3 a:pt elements, so we need to make sure of this
3317 if( j+2 < rPoly.GetSize() && rPoly.GetFlags(j+1) == PolyFlags::Control && rPoly.GetFlags(j+2) != PolyFlags::Control )
3320 mpFS->startElementNS(XML_a, XML_cubicBezTo);
3321 for( sal_uInt8 k = 0; k <= 2; ++k )
3323 mpFS->singleElementNS(XML_a, XML_pt,
3324 XML_x, OString::number(rPoly[j+k].X() - aRect.Left()),
3325 XML_y, OString::number(rPoly[j+k].Y() - aRect.Top()));
3328 mpFS->endElementNS( XML_a, XML_cubicBezTo );
3329 j += 2;
3332 else if( flags == PolyFlags::Normal )
3334 mpFS->startElementNS(XML_a, XML_lnTo);
3335 mpFS->singleElementNS( XML_a, XML_pt,
3336 XML_x, OString::number(rPoly[j].X() - aRect.Left()),
3337 XML_y, OString::number(rPoly[j].Y() - aRect.Top()) );
3338 mpFS->endElementNS( XML_a, XML_lnTo );
3342 if (bClosed)
3343 mpFS->singleElementNS( XML_a, XML_close);
3344 mpFS->endElementNS( XML_a, XML_path );
3346 mpFS->endElementNS( XML_a, XML_pathLst );
3348 mpFS->endElementNS( XML_a, XML_custGeom );
3351 void DrawingML::WriteConnectorConnections( EscherConnectorListEntry& rConnectorEntry, sal_Int32 nStartID, sal_Int32 nEndID )
3353 if( nStartID != -1 )
3355 mpFS->singleElementNS( XML_a, XML_stCxn,
3356 XML_id, OString::number(nStartID),
3357 XML_idx, OString::number(rConnectorEntry.GetConnectorRule(true)) );
3359 if( nEndID != -1 )
3361 mpFS->singleElementNS( XML_a, XML_endCxn,
3362 XML_id, OString::number(nEndID),
3363 XML_idx, OString::number(rConnectorEntry.GetConnectorRule(false)) );
3367 sal_Unicode DrawingML::SubstituteBullet( sal_Unicode cBulletId, css::awt::FontDescriptor& rFontDesc )
3369 if ( IsStarSymbol(rFontDesc.Name) )
3371 rtl_TextEncoding eCharSet = rFontDesc.CharSet;
3372 cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eCharSet, rFontDesc.Name);
3373 rFontDesc.CharSet = eCharSet;
3376 return cBulletId;
3379 sax_fastparser::FSHelperPtr DrawingML::CreateOutputStream (
3380 const OUString& sFullStream,
3381 const OUString& sRelativeStream,
3382 const Reference< XOutputStream >& xParentRelation,
3383 const char* sContentType,
3384 const char* sRelationshipType,
3385 OUString* pRelationshipId )
3387 OUString sRelationshipId;
3388 if (xParentRelation.is())
3389 sRelationshipId = GetFB()->addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream );
3390 else
3391 sRelationshipId = GetFB()->addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream );
3393 if( pRelationshipId )
3394 *pRelationshipId = sRelationshipId;
3396 sax_fastparser::FSHelperPtr p = GetFB()->openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
3398 return p;
3401 void DrawingML::WriteFill( const Reference< XPropertySet >& xPropSet )
3403 if ( !GetProperty( xPropSet, "FillStyle" ) )
3404 return;
3405 FillStyle aFillStyle( FillStyle_NONE );
3406 xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle;
3408 if ( aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparence" ) )
3410 // map full transparent background to no fill
3411 sal_Int16 nVal = 0;
3412 xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal;
3413 if ( nVal == 100 )
3414 aFillStyle = FillStyle_NONE;
3417 switch( aFillStyle )
3419 case FillStyle_SOLID :
3420 WriteSolidFill( xPropSet );
3421 break;
3422 case FillStyle_GRADIENT :
3423 WriteGradientFill( xPropSet );
3424 break;
3425 case FillStyle_BITMAP :
3426 WriteBlipFill( xPropSet, "FillBitmap" );
3427 break;
3428 case FillStyle_HATCH :
3429 WritePattFill( xPropSet );
3430 break;
3431 case FillStyle_NONE:
3432 mpFS->singleElementNS(XML_a, XML_noFill);
3433 break;
3434 default:
3439 void DrawingML::WriteStyleProperties( sal_Int32 nTokenId, const Sequence< PropertyValue >& aProperties )
3441 if( aProperties.hasElements() )
3443 OUString sSchemeClr;
3444 sal_uInt32 nIdx = 0;
3445 Sequence< PropertyValue > aTransformations;
3446 for( const auto& rProp : aProperties)
3448 if( rProp.Name == "SchemeClr" )
3449 rProp.Value >>= sSchemeClr;
3450 else if( rProp.Name == "Idx" )
3451 rProp.Value >>= nIdx;
3452 else if( rProp.Name == "Transformations" )
3453 rProp.Value >>= aTransformations;
3455 mpFS->startElementNS(XML_a, nTokenId, XML_idx, OString::number(nIdx));
3456 WriteColor(sSchemeClr, aTransformations);
3457 mpFS->endElementNS( XML_a, nTokenId );
3459 else
3461 // write mock <a:*Ref> tag
3462 mpFS->singleElementNS(XML_a, nTokenId, XML_idx, OString::number(0));
3466 void DrawingML::WriteShapeStyle( const Reference< XPropertySet >& xPropSet )
3468 // check existence of the grab bag
3469 if ( !GetProperty( xPropSet, "InteropGrabBag" ) )
3470 return;
3472 // extract the relevant properties from the grab bag
3473 Sequence< PropertyValue > aGrabBag;
3474 Sequence< PropertyValue > aFillRefProperties, aLnRefProperties, aEffectRefProperties;
3475 mAny >>= aGrabBag;
3476 for( const auto& rProp : std::as_const(aGrabBag))
3478 if( rProp.Name == "StyleFillRef" )
3479 rProp.Value >>= aFillRefProperties;
3480 else if( rProp.Name == "StyleLnRef" )
3481 rProp.Value >>= aLnRefProperties;
3482 else if( rProp.Name == "StyleEffectRef" )
3483 rProp.Value >>= aEffectRefProperties;
3486 WriteStyleProperties( XML_lnRef, aLnRefProperties );
3487 WriteStyleProperties( XML_fillRef, aFillRefProperties );
3488 WriteStyleProperties( XML_effectRef, aEffectRefProperties );
3490 // write mock <a:fontRef>
3491 mpFS->singleElementNS(XML_a, XML_fontRef, XML_idx, "minor");
3494 void DrawingML::WriteShapeEffect( const OUString& sName, const Sequence< PropertyValue >& aEffectProps )
3496 if( !aEffectProps.hasElements() )
3497 return;
3499 // assign the proper tag and enable bContainsColor if necessary
3500 sal_Int32 nEffectToken = 0;
3501 bool bContainsColor = false;
3502 if( sName == "outerShdw" )
3504 nEffectToken = FSNS( XML_a, XML_outerShdw );
3505 bContainsColor = true;
3507 else if( sName == "innerShdw" )
3509 nEffectToken = FSNS( XML_a, XML_innerShdw );
3510 bContainsColor = true;
3512 else if( sName == "glow" )
3514 nEffectToken = FSNS( XML_a, XML_glow );
3515 bContainsColor = true;
3517 else if( sName == "softEdge" )
3518 nEffectToken = FSNS( XML_a, XML_softEdge );
3519 else if( sName == "reflection" )
3520 nEffectToken = FSNS( XML_a, XML_reflection );
3521 else if( sName == "blur" )
3522 nEffectToken = FSNS( XML_a, XML_blur );
3524 OUString sSchemeClr;
3525 ::Color nRgbClr;
3526 sal_Int32 nAlpha = MAX_PERCENT;
3527 Sequence< PropertyValue > aTransformations;
3528 sax_fastparser::FastAttributeList *aOuterShdwAttrList = FastSerializerHelper::createAttrList();
3529 sax_fastparser::XFastAttributeListRef xOuterShdwAttrList( aOuterShdwAttrList );
3530 for( const auto& rEffectProp : aEffectProps )
3532 if( rEffectProp.Name == "Attribs" )
3534 // read tag attributes
3535 uno::Sequence< beans::PropertyValue > aOuterShdwProps;
3536 rEffectProp.Value >>= aOuterShdwProps;
3537 for( const auto& rOuterShdwProp : std::as_const(aOuterShdwProps) )
3539 if( rOuterShdwProp.Name == "algn" )
3541 OUString sVal;
3542 rOuterShdwProp.Value >>= sVal;
3543 aOuterShdwAttrList->add( XML_algn, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3545 else if( rOuterShdwProp.Name == "blurRad" )
3547 sal_Int32 nVal = 0;
3548 rOuterShdwProp.Value >>= nVal;
3549 aOuterShdwAttrList->add( XML_blurRad, OString::number( nVal ).getStr() );
3551 else if( rOuterShdwProp.Name == "dir" )
3553 sal_Int32 nVal = 0;
3554 rOuterShdwProp.Value >>= nVal;
3555 aOuterShdwAttrList->add( XML_dir, OString::number( nVal ).getStr() );
3557 else if( rOuterShdwProp.Name == "dist" )
3559 sal_Int32 nVal = 0;
3560 rOuterShdwProp.Value >>= nVal;
3561 aOuterShdwAttrList->add( XML_dist, OString::number( nVal ).getStr() );
3563 else if( rOuterShdwProp.Name == "kx" )
3565 sal_Int32 nVal = 0;
3566 rOuterShdwProp.Value >>= nVal;
3567 aOuterShdwAttrList->add( XML_kx, OString::number( nVal ).getStr() );
3569 else if( rOuterShdwProp.Name == "ky" )
3571 sal_Int32 nVal = 0;
3572 rOuterShdwProp.Value >>= nVal;
3573 aOuterShdwAttrList->add( XML_ky, OString::number( nVal ).getStr() );
3575 else if( rOuterShdwProp.Name == "rotWithShape" )
3577 sal_Int32 nVal = 0;
3578 rOuterShdwProp.Value >>= nVal;
3579 aOuterShdwAttrList->add( XML_rotWithShape, OString::number( nVal ).getStr() );
3581 else if( rOuterShdwProp.Name == "sx" )
3583 sal_Int32 nVal = 0;
3584 rOuterShdwProp.Value >>= nVal;
3585 aOuterShdwAttrList->add( XML_sx, OString::number( nVal ).getStr() );
3587 else if( rOuterShdwProp.Name == "sy" )
3589 sal_Int32 nVal = 0;
3590 rOuterShdwProp.Value >>= nVal;
3591 aOuterShdwAttrList->add( XML_sy, OString::number( nVal ).getStr() );
3593 else if( rOuterShdwProp.Name == "rad" )
3595 sal_Int32 nVal = 0;
3596 rOuterShdwProp.Value >>= nVal;
3597 aOuterShdwAttrList->add( XML_rad, OString::number( nVal ).getStr() );
3599 else if( rOuterShdwProp.Name == "endA" )
3601 sal_Int32 nVal = 0;
3602 rOuterShdwProp.Value >>= nVal;
3603 aOuterShdwAttrList->add( XML_endA, OString::number( nVal ).getStr() );
3605 else if( rOuterShdwProp.Name == "endPos" )
3607 sal_Int32 nVal = 0;
3608 rOuterShdwProp.Value >>= nVal;
3609 aOuterShdwAttrList->add( XML_endPos, OString::number( nVal ).getStr() );
3611 else if( rOuterShdwProp.Name == "fadeDir" )
3613 sal_Int32 nVal = 0;
3614 rOuterShdwProp.Value >>= nVal;
3615 aOuterShdwAttrList->add( XML_fadeDir, OString::number( nVal ).getStr() );
3617 else if( rOuterShdwProp.Name == "stA" )
3619 sal_Int32 nVal = 0;
3620 rOuterShdwProp.Value >>= nVal;
3621 aOuterShdwAttrList->add( XML_stA, OString::number( nVal ).getStr() );
3623 else if( rOuterShdwProp.Name == "stPos" )
3625 sal_Int32 nVal = 0;
3626 rOuterShdwProp.Value >>= nVal;
3627 aOuterShdwAttrList->add( XML_stPos, OString::number( nVal ).getStr() );
3629 else if( rOuterShdwProp.Name == "grow" )
3631 sal_Int32 nVal = 0;
3632 rOuterShdwProp.Value >>= nVal;
3633 aOuterShdwAttrList->add( XML_grow, OString::number( nVal ).getStr() );
3637 else if(rEffectProp.Name == "RgbClr")
3639 rEffectProp.Value >>= nRgbClr;
3641 else if(rEffectProp.Name == "RgbClrTransparency")
3643 sal_Int32 nTransparency;
3644 if (rEffectProp.Value >>= nTransparency)
3645 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
3646 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
3648 else if(rEffectProp.Name == "SchemeClr")
3650 rEffectProp.Value >>= sSchemeClr;
3652 else if(rEffectProp.Name == "SchemeClrTransformations")
3654 rEffectProp.Value >>= aTransformations;
3658 if( nEffectToken > 0 )
3660 mpFS->startElement( nEffectToken, xOuterShdwAttrList );
3662 if( bContainsColor )
3664 if( sSchemeClr.isEmpty() )
3665 WriteColor( nRgbClr, nAlpha );
3666 else
3667 WriteColor( sSchemeClr, aTransformations );
3670 mpFS->endElement( nEffectToken );
3674 static sal_Int32 lcl_CalculateDist(const double dX, const double dY)
3676 return static_cast< sal_Int32 >(sqrt(dX*dX + dY*dY) * 360);
3679 static sal_Int32 lcl_CalculateDir(const double dX, const double dY)
3681 return (static_cast< sal_Int32 >(basegfx::rad2deg(atan2(dY,dX)) * 60000) + 21600000) % 21600000;
3684 void DrawingML::WriteShapeEffects( const Reference< XPropertySet >& rXPropSet )
3686 Sequence< PropertyValue > aGrabBag, aEffects, aOuterShdwProps;
3687 if( GetProperty( rXPropSet, "InteropGrabBag" ) )
3689 mAny >>= aGrabBag;
3690 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
3691 [](const PropertyValue& rProp) { return rProp.Name == "EffectProperties"; });
3692 if (pProp != std::cend(aGrabBag))
3694 pProp->Value >>= aEffects;
3695 auto pEffect = std::find_if(std::cbegin(aEffects), std::cend(aEffects),
3696 [](const PropertyValue& rEffect) { return rEffect.Name == "outerShdw"; });
3697 if (pEffect != std::cend(aEffects))
3698 pEffect->Value >>= aOuterShdwProps;
3702 if( !aEffects.hasElements() )
3704 bool bHasShadow = false;
3705 if( GetProperty( rXPropSet, "Shadow" ) )
3706 mAny >>= bHasShadow;
3707 if( bHasShadow )
3709 Sequence< PropertyValue > aShadowGrabBag( 3 );
3710 Sequence< PropertyValue > aShadowAttribsGrabBag( 2 );
3712 double dX = +0.0, dY = +0.0;
3713 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
3714 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
3716 aShadowAttribsGrabBag[0].Name = "dist";
3717 aShadowAttribsGrabBag[0].Value <<= lcl_CalculateDist(dX, dY);
3718 aShadowAttribsGrabBag[1].Name = "dir";
3719 aShadowAttribsGrabBag[1].Value <<= lcl_CalculateDir(dX, dY);
3721 aShadowGrabBag[0].Name = "Attribs";
3722 aShadowGrabBag[0].Value <<= aShadowAttribsGrabBag;
3723 aShadowGrabBag[1].Name = "RgbClr";
3724 aShadowGrabBag[1].Value = rXPropSet->getPropertyValue( "ShadowColor" );
3725 aShadowGrabBag[2].Name = "RgbClrTransparency";
3726 aShadowGrabBag[2].Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
3728 mpFS->startElementNS(XML_a, XML_effectLst);
3729 WriteShapeEffect( "outerShdw", aShadowGrabBag );
3730 mpFS->endElementNS(XML_a, XML_effectLst);
3733 else
3735 for( auto& rOuterShdwProp : aOuterShdwProps )
3737 if( rOuterShdwProp.Name == "Attribs" )
3739 Sequence< PropertyValue > aAttribsProps;
3740 rOuterShdwProp.Value >>= aAttribsProps;
3742 double dX = +0.0, dY = +0.0;
3743 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
3744 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
3746 for( auto& rAttribsProp : aAttribsProps )
3748 if( rAttribsProp.Name == "dist" )
3750 rAttribsProp.Value <<= lcl_CalculateDist(dX, dY);
3752 else if( rAttribsProp.Name == "dir" )
3754 rAttribsProp.Value <<= lcl_CalculateDir(dX, dY);
3758 rOuterShdwProp.Value <<= aAttribsProps;
3760 else if( rOuterShdwProp.Name == "RgbClr" )
3762 rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowColor" );
3764 else if( rOuterShdwProp.Name == "RgbClrTransparency" )
3766 rOuterShdwProp.Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
3770 mpFS->startElementNS(XML_a, XML_effectLst);
3771 for( const auto& rEffect : std::as_const(aEffects) )
3773 if( rEffect.Name == "outerShdw" )
3775 WriteShapeEffect( rEffect.Name, aOuterShdwProps );
3777 else
3779 Sequence< PropertyValue > aEffectProps;
3780 rEffect.Value >>= aEffectProps;
3781 WriteShapeEffect( rEffect.Name, aEffectProps );
3784 mpFS->endElementNS(XML_a, XML_effectLst);
3788 void DrawingML::WriteShape3DEffects( const Reference< XPropertySet >& xPropSet )
3790 // check existence of the grab bag
3791 if( !GetProperty( xPropSet, "InteropGrabBag" ) )
3792 return;
3794 // extract the relevant properties from the grab bag
3795 Sequence< PropertyValue > aGrabBag, aEffectProps, aLightRigProps, aShape3DProps;
3796 mAny >>= aGrabBag;
3797 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
3798 [](const PropertyValue& rProp) { return rProp.Name == "3DEffectProperties"; });
3799 if (pProp != std::cend(aGrabBag))
3801 Sequence< PropertyValue > a3DEffectProps;
3802 pProp->Value >>= a3DEffectProps;
3803 for( const auto& r3DEffectProp : std::as_const(a3DEffectProps) )
3805 if( r3DEffectProp.Name == "Camera" )
3806 r3DEffectProp.Value >>= aEffectProps;
3807 else if( r3DEffectProp.Name == "LightRig" )
3808 r3DEffectProp.Value >>= aLightRigProps;
3809 else if( r3DEffectProp.Name == "Shape3D" )
3810 r3DEffectProp.Value >>= aShape3DProps;
3813 if( !aEffectProps.hasElements() && !aLightRigProps.hasElements() && !aShape3DProps.hasElements() )
3814 return;
3816 bool bCameraRotationPresent = false;
3817 sax_fastparser::FastAttributeList *aCameraAttrList = FastSerializerHelper::createAttrList();
3818 sax_fastparser::XFastAttributeListRef xCameraAttrList( aCameraAttrList );
3819 sax_fastparser::FastAttributeList *aCameraRotationAttrList = FastSerializerHelper::createAttrList();
3820 sax_fastparser::XFastAttributeListRef xRotAttrList( aCameraRotationAttrList );
3821 for( const auto& rEffectProp : std::as_const(aEffectProps) )
3823 if( rEffectProp.Name == "prst" )
3825 OUString sVal;
3826 rEffectProp.Value >>= sVal;
3827 aCameraAttrList->add( XML_prst, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3829 else if( rEffectProp.Name == "fov" )
3831 float fVal = 0;
3832 rEffectProp.Value >>= fVal;
3833 aCameraAttrList->add( XML_fov, OString::number( fVal * 60000 ).getStr() );
3835 else if( rEffectProp.Name == "zoom" )
3837 float fVal = 1;
3838 rEffectProp.Value >>= fVal;
3839 aCameraAttrList->add( XML_zoom, OString::number( fVal * 100000 ).getStr() );
3841 else if( rEffectProp.Name == "rotLat" ||
3842 rEffectProp.Name == "rotLon" ||
3843 rEffectProp.Name == "rotRev" )
3845 sal_Int32 nVal = 0, nToken = XML_none;
3846 rEffectProp.Value >>= nVal;
3847 if( rEffectProp.Name == "rotLat" )
3848 nToken = XML_lat;
3849 else if( rEffectProp.Name == "rotLon" )
3850 nToken = XML_lon;
3851 else if( rEffectProp.Name == "rotRev" )
3852 nToken = XML_rev;
3853 aCameraRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
3854 bCameraRotationPresent = true;
3858 bool bLightRigRotationPresent = false;
3859 sax_fastparser::FastAttributeList *aLightRigAttrList = FastSerializerHelper::createAttrList();
3860 sax_fastparser::XFastAttributeListRef xLightAttrList( aLightRigAttrList );
3861 sax_fastparser::FastAttributeList *aLightRigRotationAttrList = FastSerializerHelper::createAttrList();
3862 sax_fastparser::XFastAttributeListRef xLightRotAttrList( aLightRigRotationAttrList );
3863 for( const auto& rLightRigProp : std::as_const(aLightRigProps) )
3865 if( rLightRigProp.Name == "rig" || rLightRigProp.Name == "dir" )
3867 OUString sVal;
3868 sal_Int32 nToken = XML_none;
3869 rLightRigProp.Value >>= sVal;
3870 if( rLightRigProp.Name == "rig" )
3871 nToken = XML_rig;
3872 else if( rLightRigProp.Name == "dir" )
3873 nToken = XML_dir;
3874 aLightRigAttrList->add( nToken, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3876 else if( rLightRigProp.Name == "rotLat" ||
3877 rLightRigProp.Name == "rotLon" ||
3878 rLightRigProp.Name == "rotRev" )
3880 sal_Int32 nVal = 0, nToken = XML_none;
3881 rLightRigProp.Value >>= nVal;
3882 if( rLightRigProp.Name == "rotLat" )
3883 nToken = XML_lat;
3884 else if( rLightRigProp.Name == "rotLon" )
3885 nToken = XML_lon;
3886 else if( rLightRigProp.Name == "rotRev" )
3887 nToken = XML_rev;
3888 aLightRigRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
3889 bLightRigRotationPresent = true;
3893 mpFS->startElementNS(XML_a, XML_scene3d);
3895 if( aEffectProps.hasElements() )
3897 mpFS->startElementNS( XML_a, XML_camera, xCameraAttrList );
3898 if( bCameraRotationPresent )
3900 mpFS->singleElementNS( XML_a, XML_rot, xRotAttrList );
3902 mpFS->endElementNS( XML_a, XML_camera );
3904 else
3906 // a:camera with Word default values - Word won't open the document if this is not present
3907 mpFS->singleElementNS(XML_a, XML_camera, XML_prst, "orthographicFront");
3910 if( aEffectProps.hasElements() )
3912 mpFS->startElementNS( XML_a, XML_lightRig, xLightAttrList );
3913 if( bLightRigRotationPresent )
3915 mpFS->singleElementNS( XML_a, XML_rot, xLightRotAttrList );
3917 mpFS->endElementNS( XML_a, XML_lightRig );
3919 else
3921 // a:lightRig with Word default values - Word won't open the document if this is not present
3922 mpFS->singleElementNS(XML_a, XML_lightRig, XML_rig, "threePt", XML_dir, "t");
3925 mpFS->endElementNS( XML_a, XML_scene3d );
3927 if( !aShape3DProps.hasElements() )
3928 return;
3930 bool bBevelTPresent = false, bBevelBPresent = false;
3931 Sequence< PropertyValue > aExtrusionColorProps, aContourColorProps;
3932 sax_fastparser::FastAttributeList *aBevelTAttrList = FastSerializerHelper::createAttrList();
3933 sax_fastparser::XFastAttributeListRef xBevelTAttrList( aBevelTAttrList );
3934 sax_fastparser::FastAttributeList *aBevelBAttrList = FastSerializerHelper::createAttrList();
3935 sax_fastparser::XFastAttributeListRef xBevelBAttrList( aBevelBAttrList );
3936 sax_fastparser::FastAttributeList *aShape3DAttrList = FastSerializerHelper::createAttrList();
3937 for( const auto& rShape3DProp : std::as_const(aShape3DProps) )
3939 if( rShape3DProp.Name == "extrusionH" || rShape3DProp.Name == "contourW" || rShape3DProp.Name == "z" )
3941 sal_Int32 nVal = 0, nToken = XML_none;
3942 rShape3DProp.Value >>= nVal;
3943 if( rShape3DProp.Name == "extrusionH" )
3944 nToken = XML_extrusionH;
3945 else if( rShape3DProp.Name == "contourW" )
3946 nToken = XML_contourW;
3947 else if( rShape3DProp.Name == "z" )
3948 nToken = XML_z;
3949 aShape3DAttrList->add( nToken, OString::number( nVal ).getStr() );
3951 else if( rShape3DProp.Name == "prstMaterial" )
3953 OUString sVal;
3954 rShape3DProp.Value >>= sVal;
3955 aShape3DAttrList->add( XML_prstMaterial, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3957 else if( rShape3DProp.Name == "extrusionClr" )
3959 rShape3DProp.Value >>= aExtrusionColorProps;
3961 else if( rShape3DProp.Name == "contourClr" )
3963 rShape3DProp.Value >>= aContourColorProps;
3965 else if( rShape3DProp.Name == "bevelT" || rShape3DProp.Name == "bevelB" )
3967 Sequence< PropertyValue > aBevelProps;
3968 rShape3DProp.Value >>= aBevelProps;
3969 if ( !aBevelProps.hasElements() )
3970 continue;
3972 sax_fastparser::FastAttributeList *aBevelAttrList = nullptr;
3973 if( rShape3DProp.Name == "bevelT" )
3975 bBevelTPresent = true;
3976 aBevelAttrList = aBevelTAttrList;
3978 else
3980 bBevelBPresent = true;
3981 aBevelAttrList = aBevelBAttrList;
3983 for( const auto& rBevelProp : std::as_const(aBevelProps) )
3985 if( rBevelProp.Name == "w" || rBevelProp.Name == "h" )
3987 sal_Int32 nVal = 0, nToken = XML_none;
3988 rBevelProp.Value >>= nVal;
3989 if( rBevelProp.Name == "w" )
3990 nToken = XML_w;
3991 else if( rBevelProp.Name == "h" )
3992 nToken = XML_h;
3993 aBevelAttrList->add( nToken, OString::number( nVal ).getStr() );
3995 else if( rBevelProp.Name == "prst" )
3997 OUString sVal;
3998 rBevelProp.Value >>= sVal;
3999 aBevelAttrList->add( XML_prst, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
4006 sax_fastparser::XFastAttributeListRef xAttrList( aShape3DAttrList );
4007 mpFS->startElementNS( XML_a, XML_sp3d, xAttrList );
4008 if( bBevelTPresent )
4010 mpFS->singleElementNS( XML_a, XML_bevelT, xBevelTAttrList );
4012 if( bBevelBPresent )
4014 mpFS->singleElementNS( XML_a, XML_bevelB, xBevelBAttrList );
4016 if( aExtrusionColorProps.hasElements() )
4018 OUString sSchemeClr;
4019 ::Color nColor;
4020 sal_Int32 nTransparency(0);
4021 Sequence< PropertyValue > aColorTransformations;
4022 for( const auto& rExtrusionColorProp : std::as_const(aExtrusionColorProps) )
4024 if( rExtrusionColorProp.Name == "schemeClr" )
4025 rExtrusionColorProp.Value >>= sSchemeClr;
4026 else if( rExtrusionColorProp.Name == "schemeClrTransformations" )
4027 rExtrusionColorProp.Value >>= aColorTransformations;
4028 else if( rExtrusionColorProp.Name == "rgbClr" )
4029 rExtrusionColorProp.Value >>= nColor;
4030 else if( rExtrusionColorProp.Name == "rgbClrTransparency" )
4031 rExtrusionColorProp.Value >>= nTransparency;
4033 mpFS->startElementNS(XML_a, XML_extrusionClr);
4035 if( sSchemeClr.isEmpty() )
4036 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
4037 else
4038 WriteColor( sSchemeClr, aColorTransformations );
4040 mpFS->endElementNS( XML_a, XML_extrusionClr );
4042 if( aContourColorProps.hasElements() )
4044 OUString sSchemeClr;
4045 ::Color nColor;
4046 sal_Int32 nTransparency(0);
4047 Sequence< PropertyValue > aColorTransformations;
4048 for( const auto& rContourColorProp : std::as_const(aContourColorProps) )
4050 if( rContourColorProp.Name == "schemeClr" )
4051 rContourColorProp.Value >>= sSchemeClr;
4052 else if( rContourColorProp.Name == "schemeClrTransformations" )
4053 rContourColorProp.Value >>= aColorTransformations;
4054 else if( rContourColorProp.Name == "rgbClr" )
4055 rContourColorProp.Value >>= nColor;
4056 else if( rContourColorProp.Name == "rgbClrTransparency" )
4057 rContourColorProp.Value >>= nTransparency;
4059 mpFS->startElementNS(XML_a, XML_contourClr);
4061 if( sSchemeClr.isEmpty() )
4062 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
4063 else
4064 WriteColor( sSchemeClr, aContourColorProps );
4066 mpFS->endElementNS( XML_a, XML_contourClr );
4068 mpFS->endElementNS( XML_a, XML_sp3d );
4071 void DrawingML::WriteArtisticEffect( const Reference< XPropertySet >& rXPropSet )
4073 if( !GetProperty( rXPropSet, "InteropGrabBag" ) )
4074 return;
4076 PropertyValue aEffect;
4077 Sequence< PropertyValue > aGrabBag;
4078 mAny >>= aGrabBag;
4079 auto pProp = std::find_if(std::cbegin(aGrabBag), std::cend(aGrabBag),
4080 [](const PropertyValue& rProp) { return rProp.Name == "ArtisticEffectProperties"; });
4081 if (pProp != std::cend(aGrabBag))
4082 pProp->Value >>= aEffect;
4083 sal_Int32 nEffectToken = ArtisticEffectProperties::getEffectToken( aEffect.Name );
4084 if( nEffectToken == XML_none )
4085 return;
4087 Sequence< PropertyValue > aAttrs;
4088 aEffect.Value >>= aAttrs;
4089 sax_fastparser::FastAttributeList *aAttrList = FastSerializerHelper::createAttrList();
4090 OString sRelId;
4091 for( const auto& rAttr : std::as_const(aAttrs) )
4093 sal_Int32 nToken = ArtisticEffectProperties::getEffectToken( rAttr.Name );
4094 if( nToken != XML_none )
4096 sal_Int32 nVal = 0;
4097 rAttr.Value >>= nVal;
4098 aAttrList->add( nToken, OString::number( nVal ).getStr() );
4100 else if( rAttr.Name == "OriginalGraphic" )
4102 Sequence< PropertyValue > aGraphic;
4103 rAttr.Value >>= aGraphic;
4104 Sequence< sal_Int8 > aGraphicData;
4105 OUString sGraphicId;
4106 for( const auto& rProp : std::as_const(aGraphic) )
4108 if( rProp.Name == "Id" )
4109 rProp.Value >>= sGraphicId;
4110 else if( rProp.Name == "Data" )
4111 rProp.Value >>= aGraphicData;
4113 sRelId = WriteWdpPicture( sGraphicId, aGraphicData );
4117 mpFS->startElementNS(XML_a, XML_extLst);
4118 mpFS->startElementNS(XML_a, XML_ext, XML_uri, "{BEBA8EAE-BF5A-486C-A8C5-ECC9F3942E4B}");
4119 mpFS->startElementNS( XML_a14, XML_imgProps,
4120 FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)).toUtf8() );
4121 mpFS->startElementNS(XML_a14, XML_imgLayer, FSNS(XML_r, XML_embed), sRelId);
4122 mpFS->startElementNS(XML_a14, XML_imgEffect);
4124 sax_fastparser::XFastAttributeListRef xAttrList( aAttrList );
4125 mpFS->singleElementNS( XML_a14, nEffectToken, xAttrList );
4127 mpFS->endElementNS( XML_a14, XML_imgEffect );
4128 mpFS->endElementNS( XML_a14, XML_imgLayer );
4129 mpFS->endElementNS( XML_a14, XML_imgProps );
4130 mpFS->endElementNS( XML_a, XML_ext );
4131 mpFS->endElementNS( XML_a, XML_extLst );
4134 OString DrawingML::WriteWdpPicture( const OUString& rFileId, const Sequence< sal_Int8 >& rPictureData )
4136 std::map<OUString, OUString>::iterator aCachedItem = maWdpCache.find( rFileId );
4137 if( aCachedItem != maWdpCache.end() )
4138 return OUStringToOString( aCachedItem->second, RTL_TEXTENCODING_UTF8 );
4140 OUString sFileName = "media/hdphoto" + OUString::number( mnWdpImageCounter++ ) + ".wdp";
4141 Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
4142 .appendAscii( GetComponentDir() )
4143 .append( "/" )
4144 .append( sFileName )
4145 .makeStringAndClear(),
4146 "image/vnd.ms-photo" );
4147 OUString sId;
4148 xOutStream->writeBytes( rPictureData );
4149 xOutStream->closeOutput();
4151 sId = mpFB->addRelation( mpFS->getOutputStream(),
4152 oox::getRelationship(Relationship::HDPHOTO),
4153 OUStringBuffer()
4154 .appendAscii( GetRelationCompPrefix() )
4155 .append( sFileName )
4156 .makeStringAndClear() );
4158 maWdpCache[rFileId] = sId;
4159 return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
4162 void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId)
4164 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
4166 uno::Reference<xml::dom::XDocument> dataDom;
4167 uno::Reference<xml::dom::XDocument> layoutDom;
4168 uno::Reference<xml::dom::XDocument> styleDom;
4169 uno::Reference<xml::dom::XDocument> colorDom;
4170 uno::Reference<xml::dom::XDocument> drawingDom;
4171 uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
4172 uno::Sequence<uno::Any> diagramDrawing;
4174 // retrieve the doms from the GrabBag
4175 uno::Sequence<beans::PropertyValue> propList;
4176 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
4177 for (const auto& rProp : std::as_const(propList))
4179 OUString propName = rProp.Name;
4180 if (propName == "OOXData")
4181 rProp.Value >>= dataDom;
4182 else if (propName == "OOXLayout")
4183 rProp.Value >>= layoutDom;
4184 else if (propName == "OOXStyle")
4185 rProp.Value >>= styleDom;
4186 else if (propName == "OOXColor")
4187 rProp.Value >>= colorDom;
4188 else if (propName == "OOXDrawing")
4190 rProp.Value >>= diagramDrawing;
4191 diagramDrawing[0]
4192 >>= drawingDom; // if there is OOXDrawing property then set drawingDom here only.
4194 else if (propName == "OOXDiagramDataRels")
4195 rProp.Value >>= xDataRelSeq;
4198 // check that we have the 4 mandatory XDocuments
4199 // if not, there was an error importing and we won't output anything
4200 if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is())
4201 return;
4203 // generate a unique id
4204 sax_fastparser::FastAttributeList* pDocPrAttrList
4205 = sax_fastparser::FastSerializerHelper::createAttrList();
4206 pDocPrAttrList->add(XML_id, OString::number(nDiagramId).getStr());
4207 OUString sName = "Diagram" + OUString::number(nDiagramId);
4208 pDocPrAttrList->add(XML_name, OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr());
4209 sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList);
4211 if (GetDocumentType() == DOCUMENT_DOCX)
4213 mpFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef);
4214 mpFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
4216 mpFS->startElementNS(
4217 XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
4218 mpFB->getNamespaceURL(OOX_NS(dml)).toUtf8());
4220 else
4222 mpFS->startElementNS(XML_p, XML_nvGraphicFramePr);
4224 mpFS->singleElementNS(XML_p, XML_cNvPr, xDocPrAttrListRef);
4225 mpFS->singleElementNS(XML_p, XML_cNvGraphicFramePr);
4227 mpFS->startElementNS(XML_p, XML_nvPr);
4228 mpFS->startElementNS(XML_p, XML_extLst);
4229 // change tracking extension - required in PPTX
4230 mpFS->startElementNS(XML_p, XML_ext, XML_uri, "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}");
4231 mpFS->singleElementNS(XML_p14, XML_modId,
4232 FSNS(XML_xmlns, XML_p14), mpFB->getNamespaceURL(OOX_NS(p14)).toUtf8(),
4233 XML_val,
4234 OString::number(comphelper::rng::uniform_uint_distribution(1, SAL_MAX_UINT32)));
4235 mpFS->endElementNS(XML_p, XML_ext);
4236 mpFS->endElementNS(XML_p, XML_extLst);
4237 mpFS->endElementNS(XML_p, XML_nvPr);
4239 mpFS->endElementNS(XML_p, XML_nvGraphicFramePr);
4241 // store size and position of background shape instead of group shape
4242 // as some shapes may be outside
4243 css::uno::Reference<css::drawing::XShapes> xShapes(rXShape, uno::UNO_QUERY);
4244 if (xShapes.is() && xShapes->hasElements())
4246 css::uno::Reference<css::drawing::XShape> xShapeBg(xShapes->getByIndex(0),
4247 uno::UNO_QUERY);
4248 awt::Point aPos = xShapeBg->getPosition();
4249 awt::Size aSize = xShapeBg->getSize();
4250 WriteTransformation(
4251 tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)),
4252 XML_p, false, false, 0, false);
4255 mpFS->startElementNS(XML_a, XML_graphic);
4258 mpFS->startElementNS(XML_a, XML_graphicData, XML_uri,
4259 "http://schemas.openxmlformats.org/drawingml/2006/diagram");
4261 OUString sRelationCompPrefix = OUString::createFromAscii(GetRelationCompPrefix());
4263 // add data relation
4264 OUString dataFileName = "diagrams/data" + OUString::number(nDiagramId) + ".xml";
4265 OString dataRelId = OUStringToOString(
4266 mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDATA),
4267 sRelationCompPrefix + dataFileName),
4268 RTL_TEXTENCODING_UTF8);
4270 // add layout relation
4271 OUString layoutFileName = "diagrams/layout" + OUString::number(nDiagramId) + ".xml";
4272 OString layoutRelId
4273 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4274 oox::getRelationship(Relationship::DIAGRAMLAYOUT),
4275 sRelationCompPrefix + layoutFileName),
4276 RTL_TEXTENCODING_UTF8);
4278 // add style relation
4279 OUString styleFileName = "diagrams/quickStyle" + OUString::number(nDiagramId) + ".xml";
4280 OString styleRelId
4281 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4282 oox::getRelationship(Relationship::DIAGRAMQUICKSTYLE),
4283 sRelationCompPrefix + styleFileName),
4284 RTL_TEXTENCODING_UTF8);
4286 // add color relation
4287 OUString colorFileName = "diagrams/colors" + OUString::number(nDiagramId) + ".xml";
4288 OString colorRelId
4289 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4290 oox::getRelationship(Relationship::DIAGRAMCOLORS),
4291 sRelationCompPrefix + colorFileName),
4292 RTL_TEXTENCODING_UTF8);
4294 OUString drawingFileName;
4295 if (drawingDom.is())
4297 // add drawing relation
4298 drawingFileName = "diagrams/drawing" + OUString::number(nDiagramId) + ".xml";
4299 OUString drawingRelId = mpFB->addRelation(
4300 mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDRAWING),
4301 sRelationCompPrefix + drawingFileName);
4303 // the data dom contains a reference to the drawing relation. We need to update it with the new generated
4304 // relation value before writing the dom to a file
4306 // Get the dsp:damaModelExt node from the dom
4307 uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS(
4308 "http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt");
4310 // There must be one element only so get it
4311 uno::Reference<xml::dom::XNode> node = nodeList->item(0);
4313 // Get the list of attributes of the node
4314 uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes();
4316 // Get the node with the relId attribute and set its new value
4317 uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId");
4318 relIdNode->setNodeValue(drawingRelId);
4321 mpFS->singleElementNS(XML_dgm, XML_relIds,
4322 FSNS(XML_xmlns, XML_dgm), mpFB->getNamespaceURL(OOX_NS(dmlDiagram)).toUtf8(),
4323 FSNS(XML_xmlns, XML_r), mpFB->getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
4324 FSNS(XML_r, XML_dm), dataRelId, FSNS(XML_r, XML_lo), layoutRelId,
4325 FSNS(XML_r, XML_qs), styleRelId, FSNS(XML_r, XML_cs), colorRelId);
4327 mpFS->endElementNS(XML_a, XML_graphicData);
4328 mpFS->endElementNS(XML_a, XML_graphic);
4330 uno::Reference<xml::sax::XSAXSerializable> serializer;
4331 uno::Reference<xml::sax::XWriter> writer
4332 = xml::sax::Writer::create(comphelper::getProcessComponentContext());
4334 OUString sDir = OUString::createFromAscii(GetComponentDir());
4336 // write data file
4337 serializer.set(dataDom, uno::UNO_QUERY);
4338 uno::Reference<io::XOutputStream> xDataOutputStream = mpFB->openFragmentStream(
4339 sDir + "/" + dataFileName,
4340 "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
4341 writer->setOutputStream(xDataOutputStream);
4342 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4343 uno::Sequence<beans::StringPair>());
4345 // write the associated Images and rels for data file
4346 writeDiagramRels(xDataRelSeq, xDataOutputStream, "OOXDiagramDataRels", nDiagramId);
4348 // write layout file
4349 serializer.set(layoutDom, uno::UNO_QUERY);
4350 writer->setOutputStream(mpFB->openFragmentStream(
4351 sDir + "/" + layoutFileName,
4352 "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"));
4353 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4354 uno::Sequence<beans::StringPair>());
4356 // write style file
4357 serializer.set(styleDom, uno::UNO_QUERY);
4358 writer->setOutputStream(mpFB->openFragmentStream(
4359 sDir + "/" + styleFileName,
4360 "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"));
4361 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4362 uno::Sequence<beans::StringPair>());
4364 // write color file
4365 serializer.set(colorDom, uno::UNO_QUERY);
4366 writer->setOutputStream(mpFB->openFragmentStream(
4367 sDir + "/" + colorFileName,
4368 "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"));
4369 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4370 uno::Sequence<beans::StringPair>());
4372 // write drawing file
4373 if (drawingDom.is())
4375 serializer.set(drawingDom, uno::UNO_QUERY);
4376 uno::Reference<io::XOutputStream> xDrawingOutputStream = mpFB->openFragmentStream(
4377 sDir + "/" + drawingFileName, "application/vnd.ms-office.drawingml.diagramDrawing+xml");
4378 writer->setOutputStream(xDrawingOutputStream);
4379 serializer->serialize(
4380 uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4381 uno::Sequence<beans::StringPair>());
4383 // write the associated Images and rels for drawing file
4384 uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq;
4385 diagramDrawing[1] >>= xDrawingRelSeq;
4386 writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, "OOXDiagramDrawingRels", nDiagramId);
4390 void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq,
4391 const uno::Reference<io::XOutputStream>& xOutStream,
4392 const OUString& sGrabBagProperyName, int nDiagramId)
4394 // add image relationships of OOXData, OOXDiagram
4395 OUString sType(oox::getRelationship(Relationship::IMAGE));
4396 uno::Reference<xml::sax::XWriter> xWriter
4397 = xml::sax::Writer::create(comphelper::getProcessComponentContext());
4398 xWriter->setOutputStream(xOutStream);
4400 // retrieve the relationships from Sequence
4401 for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++)
4403 // diagramDataRelTuple[0] => RID,
4404 // diagramDataRelTuple[1] => xInputStream
4405 // diagramDataRelTuple[2] => extension
4406 uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j];
4408 OUString sRelId;
4409 OUString sExtension;
4410 diagramDataRelTuple[0] >>= sRelId;
4411 diagramDataRelTuple[2] >>= sExtension;
4412 OUString sContentType;
4413 if (sExtension.equalsIgnoreAsciiCase(".WMF"))
4414 sContentType = "image/x-wmf";
4415 else
4416 sContentType = "image/" + sExtension.copy(1);
4417 sRelId = sRelId.copy(3);
4419 StreamDataSequence dataSeq;
4420 diagramDataRelTuple[1] >>= dataSeq;
4421 uno::Reference<io::XInputStream> dataImagebin(
4422 new ::comphelper::SequenceInputStream(dataSeq));
4424 //nDiagramId is used to make the name unique irrespective of the number of smart arts.
4425 OUString sFragment = "media/" + sGrabBagProperyName + OUString::number(nDiagramId) + "_"
4426 + OUString::number(j) + sExtension;
4428 PropertySet aProps(xOutStream);
4429 aProps.setAnyProperty(PROP_RelId, uno::makeAny(sRelId.toInt32()));
4431 mpFB->addRelation(xOutStream, sType, "../" + sFragment);
4433 OUString sDir = OUString::createFromAscii(GetComponentDir());
4434 uno::Reference<io::XOutputStream> xBinOutStream
4435 = mpFB->openFragmentStream(sDir + "/" + sFragment, sContentType);
4439 comphelper::OStorageHelper::CopyInputToOutput(dataImagebin, xBinOutStream);
4441 catch (const uno::Exception&)
4443 TOOLS_WARN_EXCEPTION("oox.drawingml", "DrawingML::writeDiagramRels Failed to copy grabbaged Image");
4445 dataImagebin->closeInput();
4452 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */