bump product version to 6.3.0.0.beta1
[LibreOffice.git] / oox / source / export / drawingml.cxx
blobcd91bb12cbe03b15dd1c3fa0688eeadf2197f4ef
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 <i18nlangtag/languagetag.hxx>
41 #include <cstdio>
42 #include <com/sun/star/awt/CharSet.hpp>
43 #include <com/sun/star/awt/FontDescriptor.hpp>
44 #include <com/sun/star/awt/FontSlant.hpp>
45 #include <com/sun/star/awt/FontStrikeout.hpp>
46 #include <com/sun/star/awt/FontWeight.hpp>
47 #include <com/sun/star/awt/FontUnderline.hpp>
48 #include <com/sun/star/awt/Gradient.hpp>
49 #include <com/sun/star/beans/XPropertySet.hpp>
50 #include <com/sun/star/beans/XPropertyState.hpp>
51 #include <com/sun/star/beans/Property.hpp>
52 #include <com/sun/star/beans/XPropertySetInfo.hpp>
53 #include <com/sun/star/container/XEnumerationAccess.hpp>
54 #include <com/sun/star/container/XIndexAccess.hpp>
55 #include <com/sun/star/document/XStorageBasedDocument.hpp>
56 #include <com/sun/star/drawing/BitmapMode.hpp>
57 #include <com/sun/star/drawing/EnhancedCustomShapeAdjustmentValue.hpp>
58 #include <com/sun/star/drawing/EnhancedCustomShapeParameterType.hpp>
59 #include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp>
60 #include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp>
61 #include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp>
62 #include <com/sun/star/drawing/Hatch.hpp>
63 #include <com/sun/star/drawing/LineDash.hpp>
64 #include <com/sun/star/drawing/LineJoint.hpp>
65 #include <com/sun/star/drawing/LineStyle.hpp>
66 #include <com/sun/star/drawing/TextFitToSizeType.hpp>
67 #include <com/sun/star/drawing/TextHorizontalAdjust.hpp>
68 #include <com/sun/star/drawing/TextVerticalAdjust.hpp>
69 #include <com/sun/star/drawing/XShape.hpp>
70 #include <com/sun/star/drawing/FillStyle.hpp>
71 #include <com/sun/star/embed/ElementModes.hpp>
72 #include <com/sun/star/graphic/XGraphic.hpp>
73 #include <com/sun/star/i18n/ScriptType.hpp>
74 #include <com/sun/star/io/XOutputStream.hpp>
75 #include <com/sun/star/style/LineSpacing.hpp>
76 #include <com/sun/star/style/LineSpacingMode.hpp>
77 #include <com/sun/star/text/WritingMode.hpp>
78 #include <com/sun/star/text/WritingMode2.hpp>
79 #include <com/sun/star/text/GraphicCrop.hpp>
80 #include <com/sun/star/text/XText.hpp>
81 #include <com/sun/star/text/XTextContent.hpp>
82 #include <com/sun/star/text/XTextField.hpp>
83 #include <com/sun/star/text/XTextRange.hpp>
84 #include <com/sun/star/style/CaseMap.hpp>
85 #include <com/sun/star/xml/dom/XNodeList.hpp>
86 #include <com/sun/star/xml/sax/Writer.hpp>
87 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
89 #include <comphelper/processfactory.hxx>
90 #include <comphelper/random.hxx>
91 #include <comphelper/seqstream.hxx>
92 #include <comphelper/storagehelper.hxx>
93 #include <comphelper/xmltools.hxx>
94 #include <o3tl/any.hxx>
95 #include <tools/stream.hxx>
96 #include <unotools/fontdefs.hxx>
97 #include <vcl/cvtgrf.hxx>
98 #include <vcl/graph.hxx>
99 #include <vcl/settings.hxx>
100 #include <vcl/GraphicObject.hxx>
101 #include <vcl/svapp.hxx>
102 #include <rtl/strbuf.hxx>
103 #include <sfx2/app.hxx>
104 #include <svl/languageoptions.hxx>
105 #include <filter/msfilter/escherex.hxx>
106 #include <filter/msfilter/util.hxx>
107 #include <editeng/outlobj.hxx>
108 #include <editeng/svxenum.hxx>
109 #include <editeng/unonames.hxx>
110 #include <editeng/unoprnms.hxx>
111 #include <editeng/flditem.hxx>
112 #include <svx/sdtfsitm.hxx>
113 #include <svx/svdoashp.hxx>
114 #include <svx/svdomedia.hxx>
115 #include <svx/unoapi.hxx>
116 #include <svx/unoshape.hxx>
117 #include <svx/EnhancedCustomShape2d.hxx>
119 using namespace ::css;
120 using namespace ::css::beans;
121 using namespace ::css::drawing;
122 using namespace ::css::i18n;
123 using namespace ::css::style;
124 using namespace ::css::text;
125 using namespace ::css::uno;
126 using namespace ::css::container;
128 using ::css::io::XOutputStream;
129 using ::sax_fastparser::FSHelperPtr;
130 using ::sax_fastparser::FastSerializerHelper;
133 namespace oox {
134 namespace drawingml {
136 URLTransformer::~URLTransformer()
140 OUString URLTransformer::getTransformedString(const OUString& rString) const
142 return rString;
145 bool URLTransformer::isExternalURL(const OUString& /*rURL*/) const
147 return true;
150 static css::uno::Any getLineDash( const css::uno::Reference<css::frame::XModel>& xModel, const OUString& rDashName )
152 css::uno::Reference<css::lang::XMultiServiceFactory> xFact(xModel, css::uno::UNO_QUERY);
153 css::uno::Reference<css::container::XNameAccess> xNameAccess(
154 xFact->createInstance("com.sun.star.drawing.DashTable"),
155 css::uno::UNO_QUERY );
156 if(xNameAccess.is())
158 if (!xNameAccess->hasByName(rDashName))
159 return css::uno::Any();
161 return xNameAccess->getByName(rDashName);
164 return css::uno::Any();
167 namespace
169 void WriteRadialGradientPath(const awt::Gradient& rGradient, const FSHelperPtr& pFS)
171 pFS->startElementNS(XML_a, XML_path, XML_path, "circle");
173 // Write the focus rectangle. Work with the focus point, and assume
174 // that it extends 50% in all directions. The below
175 // left/top/right/bottom values are percentages, where 0 means the
176 // edge of the tile rectangle and 100% means the center of it.
177 rtl::Reference<sax_fastparser::FastAttributeList> pAttributeList(
178 sax_fastparser::FastSerializerHelper::createAttrList());
179 sal_Int32 nLeftPercent = rGradient.XOffset * 2 - 50;
180 pAttributeList->add(XML_l, OString::number(nLeftPercent * PER_PERCENT));
181 sal_Int32 nTopPercent = rGradient.YOffset * 2 - 50;
182 pAttributeList->add(XML_t, OString::number(nTopPercent * PER_PERCENT));
183 sal_Int32 nRightPercent = (100 - rGradient.XOffset) * 2 - 50;
184 pAttributeList->add(XML_r, OString::number(nRightPercent * PER_PERCENT));
185 sal_Int32 nBottomPercent = (100 - rGradient.YOffset) * 2 - 50;
186 pAttributeList->add(XML_b, OString::number(nBottomPercent * PER_PERCENT));
187 sax_fastparser::XFastAttributeListRef xAttributeList(pAttributeList.get());
188 pFS->singleElementNS(XML_a, XML_fillToRect, xAttributeList);
190 pFS->endElementNS(XML_a, XML_path);
194 // not thread safe
195 int DrawingML::mnImageCounter = 1;
196 int DrawingML::mnWdpImageCounter = 1;
197 std::map<OUString, OUString> DrawingML::maWdpCache;
199 void DrawingML::ResetCounters()
201 mnImageCounter = 1;
202 mnWdpImageCounter = 1;
203 maWdpCache.clear();
206 bool DrawingML::GetProperty( const Reference< XPropertySet >& rXPropertySet, const OUString& aName )
210 mAny = rXPropertySet->getPropertyValue(aName);
211 if (mAny.hasValue())
212 return true;
214 catch( const Exception& )
216 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
218 return false;
221 bool DrawingML::GetPropertyAndState( const Reference< XPropertySet >& rXPropertySet, const Reference< XPropertyState >& rXPropertyState, const OUString& aName, PropertyState& eState )
225 mAny = rXPropertySet->getPropertyValue(aName);
226 if (mAny.hasValue())
228 eState = rXPropertyState->getPropertyState(aName);
229 return true;
232 catch( const Exception& )
234 /* printf ("exception when trying to get value of property: %s\n", aName.toUtf8()); */
236 return false;
239 void DrawingML::WriteColor( ::Color nColor, sal_Int32 nAlpha )
241 // Transparency is a separate element.
242 OString sColor = OString::number( sal_uInt32(nColor) & 0x00FFFFFF, 16 );
243 if( sColor.getLength() < 6 )
245 OStringBuffer sBuf( "0" );
246 int remains = 5 - sColor.getLength();
248 while( remains > 0 )
250 sBuf.append( "0" );
251 remains--;
254 sBuf.append( sColor );
256 sColor = sBuf.getStr();
258 if( nAlpha < MAX_PERCENT )
260 mpFS->startElementNS(XML_a, XML_srgbClr, XML_val, sColor);
261 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
262 mpFS->endElementNS( XML_a, XML_srgbClr );
265 else
267 mpFS->singleElementNS(XML_a, XML_srgbClr, XML_val, sColor);
271 void DrawingML::WriteColor( const OUString& sColorSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
273 // prevent writing a tag with empty val attribute
274 if( sColorSchemeName.isEmpty() )
275 return;
277 if( aTransformations.hasElements() )
279 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
280 WriteColorTransformations( aTransformations, nAlpha );
281 mpFS->endElementNS( XML_a, XML_schemeClr );
283 else if(nAlpha < MAX_PERCENT)
285 mpFS->startElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
286 mpFS->singleElementNS(XML_a, XML_alpha, XML_val, OString::number(nAlpha));
287 mpFS->endElementNS( XML_a, XML_schemeClr );
289 else
291 mpFS->singleElementNS(XML_a, XML_schemeClr, XML_val, sColorSchemeName.toUtf8());
295 void DrawingML::WriteColorTransformations( const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
297 for( sal_Int32 i = 0; i < aTransformations.getLength(); i++ )
299 sal_Int32 nToken = Color::getColorTransformationToken( aTransformations[i].Name );
300 if( nToken != XML_TOKEN_INVALID && aTransformations[i].Value.hasValue() )
302 if(nToken == XML_alpha && nAlpha < MAX_PERCENT)
304 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nAlpha));
306 else
308 sal_Int32 nValue = aTransformations[i].Value.get<sal_Int32>();
309 mpFS->singleElementNS(XML_a, nToken, XML_val, OString::number(nValue));
315 void DrawingML::WriteSolidFill( ::Color nColor, sal_Int32 nAlpha )
317 mpFS->startElementNS(XML_a, XML_solidFill);
318 WriteColor( nColor, nAlpha );
319 mpFS->endElementNS( XML_a, XML_solidFill );
322 void DrawingML::WriteSolidFill( const OUString& sSchemeName, const Sequence< PropertyValue >& aTransformations, sal_Int32 nAlpha )
324 mpFS->startElementNS(XML_a, XML_solidFill);
325 WriteColor( sSchemeName, aTransformations, nAlpha );
326 mpFS->endElementNS( XML_a, XML_solidFill );
329 void DrawingML::WriteSolidFill( const Reference< XPropertySet >& rXPropSet )
331 // get fill color
332 if ( !GetProperty( rXPropSet, "FillColor" ) )
333 return;
334 sal_uInt32 nFillColor = mAny.get<sal_uInt32>();
336 // get InteropGrabBag and search the relevant attributes
337 OUString sColorFillScheme;
338 sal_uInt32 nOriginalColor = 0;
339 Sequence< PropertyValue > aStyleProperties, aTransformations;
340 if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
342 Sequence< PropertyValue > aGrabBag;
343 mAny >>= aGrabBag;
344 for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
346 if( aGrabBag[i].Name == "SpPrSolidFillSchemeClr" )
347 aGrabBag[i].Value >>= sColorFillScheme;
348 else if( aGrabBag[i].Name == "OriginalSolidFillClr" )
349 aGrabBag[i].Value >>= nOriginalColor;
350 else if( aGrabBag[i].Name == "StyleFillRef" )
351 aGrabBag[i].Value >>= aStyleProperties;
352 else if( aGrabBag[i].Name == "SpPrSolidFillSchemeClrTransformations" )
353 aGrabBag[i].Value >>= aTransformations;
357 sal_Int32 nAlpha = MAX_PERCENT;
358 if( GetProperty( rXPropSet, "FillTransparence" ) )
360 sal_Int32 nTransparency = 0;
361 mAny >>= nTransparency;
362 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
363 nAlpha = (MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
366 // write XML
367 if ( nFillColor != nOriginalColor )
369 // the user has set a different color for the shape
370 WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
372 else if ( !sColorFillScheme.isEmpty() )
374 // the shape had a scheme color and the user didn't change it
375 WriteSolidFill( sColorFillScheme, aTransformations, nAlpha );
377 else if ( aStyleProperties.hasElements() )
379 sal_uInt32 nThemeColor = 0;
380 sal_Int32 nThemeAlpha = MAX_PERCENT;
381 for( sal_Int32 i=0; i < aStyleProperties.getLength(); ++i )
383 if( aStyleProperties[i].Name == "Color" )
385 aStyleProperties[i].Value >>= nThemeColor;
387 else if(aStyleProperties[i].Name == "Transformations" )
389 Sequence< PropertyValue > aStyleTransformations;
390 aStyleProperties[i].Value >>= aStyleTransformations;
391 for( sal_Int32 j = 0; j < aStyleTransformations.getLength(); j++ )
393 if (aStyleTransformations[j].Name == "alpha" )
395 aStyleTransformations[j].Value >>= nThemeAlpha;
396 break;
401 if ( nFillColor != nThemeColor || nAlpha != nThemeAlpha )
402 // the shape contains a theme but it wasn't being used
403 WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
405 // in case the shape used the style color and the user didn't change it,
406 // we must not write a <a: solidFill> tag.
408 else
410 // the shape had a custom color and the user didn't change it
411 WriteSolidFill( ::Color(nFillColor & 0xffffff), nAlpha );
415 void DrawingML::WriteGradientStop( sal_uInt16 nStop, ::Color nColor )
417 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nStop * 1000));
418 WriteColor( nColor );
419 mpFS->endElementNS( XML_a, XML_gs );
422 ::Color DrawingML::ColorWithIntensity( sal_uInt32 nColor, sal_uInt32 nIntensity )
424 return ::Color(( ( ( nColor & 0xff ) * nIntensity ) / 100 )
425 | ( ( ( ( ( nColor & 0xff00 ) >> 8 ) * nIntensity ) / 100 ) << 8 )
426 | ( ( ( ( ( nColor & 0xff0000 ) >> 8 ) * nIntensity ) / 100 ) << 8 ));
429 bool DrawingML::EqualGradients( awt::Gradient aGradient1, awt::Gradient aGradient2 )
431 return aGradient1.Style == aGradient2.Style &&
432 aGradient1.StartColor == aGradient2.StartColor &&
433 aGradient1.EndColor == aGradient2.EndColor &&
434 aGradient1.Angle == aGradient2.Angle &&
435 aGradient1.Border == aGradient2.Border &&
436 aGradient1.XOffset == aGradient2.XOffset &&
437 aGradient1.YOffset == aGradient2.YOffset &&
438 aGradient1.StartIntensity == aGradient2.StartIntensity &&
439 aGradient1.EndIntensity == aGradient2.EndIntensity &&
440 aGradient1.StepCount == aGradient2.StepCount;
443 void DrawingML::WriteGradientFill( const Reference< XPropertySet >& rXPropSet )
445 awt::Gradient aGradient;
446 if (GetProperty(rXPropSet, "FillGradient"))
448 aGradient = *o3tl::doAccess<awt::Gradient>(mAny);
450 // get InteropGrabBag and search the relevant attributes
451 awt::Gradient aOriginalGradient;
452 Sequence< PropertyValue > aGradientStops;
453 if ( GetProperty( rXPropSet, "InteropGrabBag" ) )
455 Sequence< PropertyValue > aGrabBag;
456 mAny >>= aGrabBag;
457 for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
458 if( aGrabBag[i].Name == "GradFillDefinition" )
459 aGrabBag[i].Value >>= aGradientStops;
460 else if( aGrabBag[i].Name == "OriginalGradFill" )
461 aGrabBag[i].Value >>= aOriginalGradient;
464 // check if an ooxml gradient had been imported and if the user has modified it
465 // Gradient grab-bag depends on theme grab-bag, which is implemented
466 // only for DOCX.
467 if( EqualGradients( aOriginalGradient, aGradient ) && GetDocumentType() == DOCUMENT_DOCX)
469 // If we have no gradient stops that means original gradient were defined by a theme.
470 if( aGradientStops.hasElements() )
472 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
473 WriteGrabBagGradientFill(aGradientStops, aGradient);
474 mpFS->endElementNS( XML_a, XML_gradFill );
477 else
479 mpFS->startElementNS(XML_a, XML_gradFill, XML_rotWithShape, "0");
480 WriteGradientFill(aGradient);
481 mpFS->endElementNS( XML_a, XML_gradFill );
486 void DrawingML::WriteGrabBagGradientFill( const Sequence< PropertyValue >& aGradientStops, awt::Gradient rGradient )
488 // write back the original gradient
489 mpFS->startElementNS(XML_a, XML_gsLst);
491 // get original stops and write them
492 for( sal_Int32 i=0; i < aGradientStops.getLength(); ++i )
494 Sequence< PropertyValue > aGradientStop;
495 aGradientStops[i].Value >>= aGradientStop;
497 // get values
498 OUString sSchemeClr;
499 double nPos = 0;
500 sal_Int16 nTransparency = 0;
501 ::Color nRgbClr;
502 Sequence< PropertyValue > aTransformations;
503 for( sal_Int32 j=0; j < aGradientStop.getLength(); ++j )
505 if( aGradientStop[j].Name == "SchemeClr" )
506 aGradientStop[j].Value >>= sSchemeClr;
507 else if( aGradientStop[j].Name == "RgbClr" )
508 aGradientStop[j].Value >>= nRgbClr;
509 else if( aGradientStop[j].Name == "Pos" )
510 aGradientStop[j].Value >>= nPos;
511 else if( aGradientStop[j].Name == "Transparency" )
512 aGradientStop[j].Value >>= nTransparency;
513 else if( aGradientStop[j].Name == "Transformations" )
514 aGradientStop[j].Value >>= aTransformations;
516 // write stop
517 mpFS->startElementNS(XML_a, XML_gs, XML_pos, OString::number(nPos * 100000.0).getStr());
518 if( sSchemeClr.isEmpty() )
520 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
521 sal_Int32 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
522 WriteColor( nRgbClr, nAlpha );
524 else
526 WriteColor( sSchemeClr, aTransformations );
528 mpFS->endElementNS( XML_a, XML_gs );
530 mpFS->endElementNS( XML_a, XML_gsLst );
532 switch (rGradient.Style)
534 default:
535 mpFS->singleElementNS(
536 XML_a, XML_lin, XML_ang,
537 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
538 break;
539 case awt::GradientStyle_RADIAL:
540 WriteRadialGradientPath(rGradient, mpFS);
541 break;
545 void DrawingML::WriteGradientFill( awt::Gradient rGradient )
547 switch( rGradient.Style )
549 default:
550 case awt::GradientStyle_LINEAR:
551 mpFS->startElementNS(XML_a, XML_gsLst);
552 WriteGradientStop( 0, ColorWithIntensity( rGradient.StartColor, rGradient.StartIntensity ) );
553 WriteGradientStop( 100, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
554 mpFS->endElementNS( XML_a, XML_gsLst );
555 mpFS->singleElementNS(
556 XML_a, XML_lin, XML_ang,
557 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
558 break;
560 case awt::GradientStyle_AXIAL:
561 mpFS->startElementNS(XML_a, XML_gsLst);
562 WriteGradientStop( 0, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
563 WriteGradientStop( 50, ColorWithIntensity( rGradient.StartColor, rGradient.StartIntensity ) );
564 WriteGradientStop( 100, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
565 mpFS->endElementNS( XML_a, XML_gsLst );
566 mpFS->singleElementNS(
567 XML_a, XML_lin, XML_ang,
568 OString::number((((3600 - rGradient.Angle + 900) * 6000) % 21600000)));
569 break;
571 case awt::GradientStyle_RADIAL:
573 mpFS->startElementNS(XML_a, XML_gsLst);
574 WriteGradientStop(0, ColorWithIntensity(rGradient.EndColor, rGradient.EndIntensity));
575 if (rGradient.Border > 0 && rGradient.Border < 100)
576 // Map border to an additional gradient stop, which has the
577 // same color as the final stop.
578 WriteGradientStop(
579 100 - rGradient.Border,
580 ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity));
581 WriteGradientStop(100,
582 ColorWithIntensity(rGradient.StartColor, rGradient.StartIntensity));
583 mpFS->endElementNS(XML_a, XML_gsLst);
585 WriteRadialGradientPath(rGradient, mpFS);
586 break;
588 /* I don't see how to apply transformation to gradients, so
589 * elliptical will end as radial and square as
590 * rectangular. also position offsets are not applied */
591 case awt::GradientStyle_ELLIPTICAL:
592 case awt::GradientStyle_RECT:
593 case awt::GradientStyle_SQUARE:
594 mpFS->startElementNS(XML_a, XML_gsLst);
595 WriteGradientStop( 0, ColorWithIntensity( rGradient.EndColor, rGradient.EndIntensity ) );
596 WriteGradientStop( 100, ColorWithIntensity( rGradient.StartColor, rGradient.StartIntensity ) );
597 mpFS->endElementNS( XML_a, XML_gsLst );
598 mpFS->singleElementNS( XML_a, XML_path,
599 XML_path, ( rGradient.Style == awt::GradientStyle_RADIAL || rGradient.Style == awt::GradientStyle_ELLIPTICAL ) ? "circle" : "rect" );
600 break;
604 void DrawingML::WriteLineArrow( const Reference< XPropertySet >& rXPropSet, bool bLineStart )
606 ESCHER_LineEnd eLineEnd;
607 sal_Int32 nArrowLength;
608 sal_Int32 nArrowWidth;
610 if ( EscherPropertyContainer::GetLineArrow( bLineStart, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) )
612 const char* len;
613 const char* type;
614 const char* width;
616 switch( nArrowLength )
618 case ESCHER_LineShortArrow:
619 len = "sm";
620 break;
621 default:
622 case ESCHER_LineMediumLenArrow:
623 len = "med";
624 break;
625 case ESCHER_LineLongArrow:
626 len = "lg";
627 break;
630 switch( eLineEnd )
632 default:
633 case ESCHER_LineNoEnd:
634 type = "none";
635 break;
636 case ESCHER_LineArrowEnd:
637 type = "triangle";
638 break;
639 case ESCHER_LineArrowStealthEnd:
640 type = "stealth";
641 break;
642 case ESCHER_LineArrowDiamondEnd:
643 type = "diamond";
644 break;
645 case ESCHER_LineArrowOvalEnd:
646 type = "oval";
647 break;
648 case ESCHER_LineArrowOpenEnd:
649 type = "arrow";
650 break;
653 switch( nArrowWidth )
655 case ESCHER_LineNarrowArrow:
656 width = "sm";
657 break;
658 default:
659 case ESCHER_LineMediumWidthArrow:
660 width = "med";
661 break;
662 case ESCHER_LineWideArrow:
663 width = "lg";
664 break;
667 mpFS->singleElementNS( XML_a, bLineStart ? XML_headEnd : XML_tailEnd,
668 XML_len, len,
669 XML_type, type,
670 XML_w, width );
674 void DrawingML::WriteOutline( const Reference<XPropertySet>& rXPropSet, Reference< frame::XModel > const & xModel )
676 drawing::LineStyle aLineStyle( drawing::LineStyle_NONE );
678 if (GetProperty(rXPropSet, "LineStyle"))
679 mAny >>= aLineStyle;
681 sal_uInt32 nLineWidth = 0;
682 ::Color nColor;
683 sal_Int32 nColorAlpha = MAX_PERCENT;
684 bool bColorSet = false;
685 const char* cap = nullptr;
686 drawing::LineDash aLineDash;
687 bool bDashSet = false;
688 bool bNoFill = false;
690 // get InteropGrabBag and search the relevant attributes
691 OUString sColorFillScheme;
693 ::Color nOriginalColor;
694 ::Color nStyleColor;
695 sal_uInt32 nStyleLineWidth = 0;
697 Sequence<PropertyValue> aStyleProperties;
698 Sequence<PropertyValue> aTransformations;
700 drawing::LineStyle aStyleLineStyle(drawing::LineStyle_NONE);
701 drawing::LineJoint aStyleLineJoint(drawing::LineJoint_NONE);
703 if (GetProperty(rXPropSet, "InteropGrabBag"))
705 Sequence<PropertyValue> aGrabBag;
706 mAny >>= aGrabBag;
708 for (sal_Int32 i=0; i < aGrabBag.getLength(); ++i)
710 if( aGrabBag[i].Name == "SpPrLnSolidFillSchemeClr" )
711 aGrabBag[i].Value >>= sColorFillScheme;
712 else if( aGrabBag[i].Name == "OriginalLnSolidFillClr" )
713 aGrabBag[i].Value >>= nOriginalColor;
714 else if( aGrabBag[i].Name == "StyleLnRef" )
715 aGrabBag[i].Value >>= aStyleProperties;
716 else if( aGrabBag[i].Name == "SpPrLnSolidFillSchemeClrTransformations" )
717 aGrabBag[i].Value >>= aTransformations;
719 if (aStyleProperties.hasElements())
721 for (sal_Int32 i=0; i < aStyleProperties.getLength(); ++i)
723 if( aStyleProperties[i].Name == "Color" )
724 aStyleProperties[i].Value >>= nStyleColor;
725 else if( aStyleProperties[i].Name == "LineStyle" )
726 aStyleProperties[i].Value >>= aStyleLineStyle;
727 else if( aStyleProperties[i].Name == "LineJoint" )
728 aStyleProperties[i].Value >>= aStyleLineJoint;
729 else if( aStyleProperties[i].Name == "LineWidth" )
730 aStyleProperties[i].Value >>= nStyleLineWidth;
735 if (GetProperty(rXPropSet, "LineWidth"))
736 mAny >>= nLineWidth;
738 switch (aLineStyle)
740 case drawing::LineStyle_NONE:
741 bNoFill = true;
742 break;
743 case drawing::LineStyle_DASH:
744 if (GetProperty(rXPropSet, "LineDash"))
746 aLineDash = mAny.get<drawing::LineDash>();
747 //this query is good for shapes, but in the case of charts it returns 0 values
748 if (aLineDash.Dots == 0 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 0) {
749 OUString aLineDashName;
750 if (GetProperty(rXPropSet, "LineDashName"))
751 mAny >>= aLineDashName;
752 if (!aLineDashName.isEmpty() && xModel) {
753 css::uno::Any aAny = getLineDash(xModel, aLineDashName);
754 aAny >>= aLineDash;
758 else
760 //export the linestyle of chart wall (plot area) and chart page
761 OUString aLineDashName;
762 if (GetProperty(rXPropSet, "LineDashName"))
763 mAny >>= aLineDashName;
764 if (!aLineDashName.isEmpty() && xModel) {
765 css::uno::Any aAny = getLineDash(xModel, aLineDashName);
766 aAny >>= aLineDash;
769 bDashSet = true;
770 if (aLineDash.Style == DashStyle_ROUND || aLineDash.Style == DashStyle_ROUNDRELATIVE)
772 cap = "rnd";
775 SAL_INFO("oox.shape", "dash dots: " << aLineDash.Dots << " dashes: " << aLineDash.Dashes
776 << " dotlen: " << aLineDash.DotLen << " dashlen: " << aLineDash.DashLen << " distance: " << aLineDash.Distance);
778 [[fallthrough]];
779 case drawing::LineStyle_SOLID:
780 default:
781 if (GetProperty(rXPropSet, "LineColor"))
783 nColor = ::Color(mAny.get<sal_uInt32>() & 0xffffff);
784 bColorSet = true;
786 if (GetProperty(rXPropSet, "LineTransparence"))
788 nColorAlpha = MAX_PERCENT - (mAny.get<sal_Int16>() * PER_PERCENT);
790 break;
793 mpFS->startElementNS( XML_a, XML_ln,
794 XML_cap, cap,
795 XML_w, nLineWidth > 1 && nStyleLineWidth != nLineWidth ?
796 OString::number(oox::drawingml::convertHmmToEmu(nLineWidth)).getStr() : nullptr );
798 if( bColorSet )
800 if( nColor != nOriginalColor )
802 // the user has set a different color for the line
803 WriteSolidFill( nColor, nColorAlpha );
805 else if( !sColorFillScheme.isEmpty() )
807 // the line had a scheme color and the user didn't change it
808 WriteSolidFill( sColorFillScheme, aTransformations );
810 else if( aStyleProperties.hasElements() )
812 if( nColor != nStyleColor )
813 // the line style defines some color but it wasn't being used
814 WriteSolidFill( nColor );
815 // in case the shape used the style color and the user didn't change it,
816 // we must not write a <a: solidFill> tag.
818 else
820 WriteSolidFill( nColor, nColorAlpha );
824 if( bDashSet && aStyleLineStyle != drawing::LineStyle_DASH )
826 // convert absolute dash/dot length to relative length
827 int relDotLen = nLineWidth ? aLineDash.DotLen / nLineWidth : 0;
828 int relDashLen = nLineWidth ? aLineDash.DashLen / nLineWidth : 0;
829 int relDistance = nLineWidth ? aLineDash.Distance / nLineWidth : 0;
830 // fixing relative values in the case of linewidths smaller than 1 pt
831 if (0 < nLineWidth && nLineWidth < 35) //35 HMM == 1 pt
833 relDotLen = relDotLen ? (relDotLen + 1) * (nLineWidth * 360.0 / 12700) : 0;
834 relDashLen = relDashLen ? (relDashLen + 1) * (nLineWidth * 360.0 / 12700) : 0;
835 relDistance = relDistance ? (relDistance + 1) * (nLineWidth * 360.0 / 12700) : 0;
837 // keep default mso preset linestyles (instead of custdash)
838 if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 0 && relDashLen == 0 && relDistance == 3)
840 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
842 else if (aLineDash.Dots == 0 && relDotLen == 0 && aLineDash.Dashes == 1 && relDashLen == 4 && relDistance == 3)
844 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
846 else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 4 && relDistance == 3)
848 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dashDot");
850 else if (aLineDash.Dots == 0 && relDotLen == 0 && aLineDash.Dashes == 1 && relDashLen == 8 && relDistance == 3)
852 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDash");
854 else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 8 && relDistance == 3)
856 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDot");
858 else if (aLineDash.Dots == 2 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 8 && relDistance == 3)
860 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "lgDashDotDot");
862 else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 0 && relDashLen == 0 && relDistance == 1)
864 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
866 else if (aLineDash.Dots == 0 && relDotLen == 0 && aLineDash.Dashes == 1 && relDashLen == 3 && relDistance == 1)
868 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
870 else if (aLineDash.Dots == 1 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 3 && relDistance == 1)
872 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDot");
874 else if (aLineDash.Dots == 2 && relDotLen == 1 && aLineDash.Dashes == 1 && relDashLen == 3 && relDistance == 1)
876 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
878 /*convert some LO preset dashes to MSO preset dashes for oox interoperability
879 LO preset dashes which don't have equivalent in MSO preset dashes: 2 Dots 3 Dashes, Line with Fine Dots, 3 Dashes 3 Dots*/
880 //ultrafine Dashed, Ultrafine Dotted -> sysDot
881 else if ((aLineDash.Dots == 1 && aLineDash.DotLen == 51 && aLineDash.Dashes == 1 && aLineDash.DashLen == 51 && aLineDash.Distance == 51) ||
882 (aLineDash.Dots == 1 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 50))
884 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDot");
886 //Fine Dashed -> dash
887 else if (aLineDash.Dots == 1 && aLineDash.DotLen == 197 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 197)
889 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dash");
891 //Fine Dotted -> dot
892 else if (aLineDash.Dots == 1 && aLineDash.DotLen == 0 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 457)
894 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "dot");
896 //Line Style 9, Dashed -> sysDash
897 else if ((aLineDash.Dots == 1 && aLineDash.DotLen == 197 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 120) ||
898 (aLineDash.Dots == 1 && aLineDash.DotLen == 197 && aLineDash.Dashes == 0 && aLineDash.DashLen == 0 && aLineDash.Distance == 127))
900 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDash");
902 //2 Dots 1 Dash -> sysDashDotDot
903 else if (aLineDash.Dots == 2 && aLineDash.DotLen == 0 && aLineDash.Dashes == 1 && aLineDash.DashLen == 203 && aLineDash.Distance == 203)
905 mpFS->singleElementNS(XML_a, XML_prstDash, XML_val, "sysDashDotDot");
907 else
909 mpFS->startElementNS(XML_a, XML_custDash);
911 // Check that line-width is positive and distance between dashes\dots is positive
912 if ( nLineWidth > 0 && aLineDash.Distance > 0 )
914 // Write 'dashes' first, and then 'dots'
915 int i;
916 sal_Int32 nSp = aLineDash.Distance * 100 / nLineWidth;
917 if ( aLineDash.Dashes > 0 )
919 sal_Int32 nD = aLineDash.DashLen * 100 / nLineWidth;
920 for( i = 0; i < aLineDash.Dashes; i ++ )
922 mpFS->singleElementNS( XML_a , XML_ds,
923 XML_d , write1000thOfAPercent(nD),
924 XML_sp, write1000thOfAPercent(nSp) );
927 if ( aLineDash.Dots > 0 )
929 sal_Int32 nD = aLineDash.DotLen * 100 / nLineWidth;
930 for( i = 0; i < aLineDash.Dots; i ++ )
932 mpFS->singleElementNS( XML_a, XML_ds,
933 XML_d , write1000thOfAPercent(nD),
934 XML_sp, write1000thOfAPercent(nSp) );
939 SAL_WARN_IF(nLineWidth <= 0,
940 "oox.shape", "while writing outline - custom dash - line width was < 0 : " << nLineWidth);
941 SAL_WARN_IF(aLineDash.Dashes < 0,
942 "oox.shape", "while writing outline - custom dash - number of dashes was < 0 : " << aLineDash.Dashes);
943 SAL_WARN_IF(aLineDash.Dashes > 0 && aLineDash.DashLen <= 0,
944 "oox.shape", "while writing outline - custom dash - dash length was < 0 : " << aLineDash.DashLen);
945 SAL_WARN_IF(aLineDash.Dots < 0,
946 "oox.shape", "while writing outline - custom dash - number of dots was < 0 : " << aLineDash.Dots);
947 SAL_WARN_IF(aLineDash.Dots > 0 && aLineDash.DotLen <= 0,
948 "oox.shape", "while writing outline - custom dash - dot length was < 0 : " << aLineDash.DotLen);
949 SAL_WARN_IF(aLineDash.Distance <= 0,
950 "oox.shape", "while writing outline - custom dash - distance was < 0 : " << aLineDash.Distance);
952 mpFS->endElementNS( XML_a, XML_custDash );
956 if (!bNoFill && nLineWidth > 1 && GetProperty(rXPropSet, "LineJoint"))
958 LineJoint eLineJoint = mAny.get<LineJoint>();
960 if( aStyleLineJoint == LineJoint_NONE || aStyleLineJoint != eLineJoint )
962 // style-defined line joint does not exist, or is different from the shape's joint
963 switch( eLineJoint )
965 case LineJoint_NONE:
966 case LineJoint_BEVEL:
967 mpFS->singleElementNS(XML_a, XML_bevel);
968 break;
969 default:
970 case LineJoint_MIDDLE:
971 case LineJoint_MITER:
972 mpFS->singleElementNS(XML_a, XML_miter);
973 break;
974 case LineJoint_ROUND:
975 mpFS->singleElementNS(XML_a, XML_round);
976 break;
981 if( !bNoFill )
983 WriteLineArrow( rXPropSet, true );
984 WriteLineArrow( rXPropSet, false );
986 else
988 mpFS->singleElementNS(XML_a, XML_noFill);
991 mpFS->endElementNS( XML_a, XML_ln );
994 const char* DrawingML::GetComponentDir()
996 switch ( meDocumentType )
998 case DOCUMENT_DOCX: return "word";
999 case DOCUMENT_PPTX: return "ppt";
1000 case DOCUMENT_XLSX: return "xl";
1003 return "unknown";
1006 const char* DrawingML::GetRelationCompPrefix()
1008 switch ( meDocumentType )
1010 case DOCUMENT_DOCX: return "";
1011 case DOCUMENT_PPTX:
1012 case DOCUMENT_XLSX: return "../";
1015 return "unknown";
1018 OUString DrawingML::WriteImage( const Graphic& rGraphic , bool bRelPathToMedia )
1020 GfxLink aLink = rGraphic.GetGfxLink ();
1021 OUString sMediaType;
1022 const char* pExtension = "";
1023 OUString sRelId;
1025 SvMemoryStream aStream;
1026 const void* aData = aLink.GetData();
1027 std::size_t nDataSize = aLink.GetDataSize();
1029 switch ( aLink.GetType() )
1031 case GfxLinkType::NativeGif:
1032 sMediaType = "image/gif";
1033 pExtension = ".gif";
1034 break;
1036 // #i15508# added BMP type for better exports
1037 // export not yet active, so adding for reference (not checked)
1038 case GfxLinkType::NativeBmp:
1039 sMediaType = "image/bmp";
1040 pExtension = ".bmp";
1041 break;
1043 case GfxLinkType::NativeJpg:
1044 sMediaType = "image/jpeg";
1045 pExtension = ".jpeg";
1046 break;
1047 case GfxLinkType::NativePng:
1048 sMediaType = "image/png";
1049 pExtension = ".png";
1050 break;
1051 case GfxLinkType::NativeTif:
1052 sMediaType = "image/tiff";
1053 pExtension = ".tif";
1054 break;
1055 case GfxLinkType::NativeWmf:
1056 sMediaType = "image/x-wmf";
1057 pExtension = ".wmf";
1058 break;
1059 case GfxLinkType::NativeMet:
1060 sMediaType = "image/x-met";
1061 pExtension = ".met";
1062 break;
1063 case GfxLinkType::NativePct:
1064 sMediaType = "image/x-pict";
1065 pExtension = ".pct";
1066 break;
1067 case GfxLinkType::NativeMov:
1068 sMediaType = "application/movie";
1069 pExtension = ".MOV";
1070 break;
1071 default:
1073 GraphicType aType = rGraphic.GetType();
1074 if ( aType == GraphicType::Bitmap || aType == GraphicType::GdiMetafile)
1076 if ( aType == GraphicType::Bitmap )
1078 (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::PNG );
1079 sMediaType = "image/png";
1080 pExtension = ".png";
1082 else
1084 (void)GraphicConverter::Export( aStream, rGraphic, ConvertDataFormat::EMF );
1085 sMediaType = "image/x-emf";
1086 pExtension = ".emf";
1089 else
1091 SAL_WARN("oox.shape", "unhandled graphic type " << static_cast<int>(aType) );
1092 /*Earlier, even in case of unhandled graphic types we were
1093 proceeding to write the image, which would eventually
1094 write an empty image with a zero size, and return a valid
1095 relationID, which is incorrect.
1097 return sRelId;
1100 aData = aStream.GetData();
1101 nDataSize = aStream.GetEndOfData();
1102 break;
1106 Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
1107 .appendAscii( GetComponentDir() )
1108 .append( "/media/image" )
1109 .append( static_cast<sal_Int32>(mnImageCounter) )
1110 .appendAscii( pExtension )
1111 .makeStringAndClear(),
1112 sMediaType );
1113 xOutStream->writeBytes( Sequence< sal_Int8 >( static_cast<const sal_Int8*>(aData), nDataSize ) );
1114 xOutStream->closeOutput();
1116 const OString sRelPathToMedia = "media/image";
1117 OString sRelationCompPrefix;
1118 if ( bRelPathToMedia )
1119 sRelationCompPrefix = "../";
1120 else
1121 sRelationCompPrefix = GetRelationCompPrefix();
1122 sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1123 oox::getRelationship(Relationship::IMAGE),
1124 OUStringBuffer()
1125 .appendAscii( sRelationCompPrefix.getStr() )
1126 .appendAscii( sRelPathToMedia.getStr() )
1127 .append( static_cast<sal_Int32>(mnImageCounter ++) )
1128 .appendAscii( pExtension )
1129 .makeStringAndClear() );
1131 return sRelId;
1134 void DrawingML::WriteMediaNonVisualProperties(const css::uno::Reference<css::drawing::XShape>& xShape)
1136 SdrMediaObj* pMediaObj = dynamic_cast<SdrMediaObj*>(GetSdrObjectFromXShape(xShape));
1137 if (!pMediaObj)
1138 return;
1140 // extension
1141 OUString aExtension;
1142 const OUString& rURL(pMediaObj->getURL());
1143 int nLastDot = rURL.lastIndexOf('.');
1144 if (nLastDot >= 0)
1145 aExtension = rURL.copy(nLastDot);
1147 bool bEmbed = rURL.startsWith("vnd.sun.star.Package:");
1148 Relationship eMediaType = Relationship::VIDEO;
1150 // mime type
1151 #if HAVE_FEATURE_AVMEDIA
1152 OUString aMimeType(pMediaObj->getMediaProperties().getMimeType());
1153 #else
1154 OUString aMimeType("none");
1155 #endif
1156 if (aMimeType == "application/vnd.sun.star.media")
1158 // try to set something better
1159 // TODO fix the importer to actually set the mimetype on import
1160 if (aExtension.equalsIgnoreAsciiCase(".avi"))
1161 aMimeType = "video/x-msvideo";
1162 else if (aExtension.equalsIgnoreAsciiCase(".flv"))
1163 aMimeType = "video/x-flv";
1164 else if (aExtension.equalsIgnoreAsciiCase(".mp4"))
1165 aMimeType = "video/mp4";
1166 else if (aExtension.equalsIgnoreAsciiCase(".mov"))
1167 aMimeType = "video/quicktime";
1168 else if (aExtension.equalsIgnoreAsciiCase(".ogv"))
1169 aMimeType = "video/ogg";
1170 else if (aExtension.equalsIgnoreAsciiCase(".wmv"))
1171 aMimeType = "video/x-ms-wmv";
1172 else if (aExtension.equalsIgnoreAsciiCase(".wav"))
1174 aMimeType = "audio/x-wav";
1175 eMediaType = Relationship::AUDIO;
1179 OUString aVideoFileRelId;
1180 OUString aMediaRelId;
1182 if (bEmbed)
1184 // copy the video stream
1185 Reference<XOutputStream> xOutStream = mpFB->openFragmentStream(OUStringBuffer()
1186 .appendAscii(GetComponentDir())
1187 .append("/media/media")
1188 .append(static_cast<sal_Int32>(mnImageCounter))
1189 .append(aExtension)
1190 .makeStringAndClear(),
1191 aMimeType);
1193 uno::Reference<io::XInputStream> xInputStream(pMediaObj->GetInputStream());
1194 comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream);
1196 xOutStream->closeOutput();
1198 // create the relation
1199 OUString aPath = OUStringBuffer().appendAscii(GetRelationCompPrefix())
1200 .append("media/media")
1201 .append(static_cast<sal_Int32>(mnImageCounter++))
1202 .append(aExtension)
1203 .makeStringAndClear();
1204 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), aPath);
1205 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), aPath);
1207 else
1209 aVideoFileRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(eMediaType), rURL);
1210 aMediaRelId = mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::MEDIA), rURL);
1213 GetFS()->startElementNS(XML_p, XML_nvPr);
1215 GetFS()->singleElementNS(XML_a, eMediaType == Relationship::VIDEO ? XML_videoFile : XML_audioFile,
1216 FSNS(XML_r, XML_link), aVideoFileRelId.toUtf8());
1218 GetFS()->startElementNS(XML_p, XML_extLst);
1219 // media extensions; google this ID for details
1220 GetFS()->startElementNS(XML_p, XML_ext, XML_uri, "{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}");
1222 GetFS()->singleElementNS(XML_p14, XML_media,
1223 bEmbed? FSNS(XML_r, XML_embed): FSNS(XML_r, XML_link), aMediaRelId.toUtf8());
1225 GetFS()->endElementNS(XML_p, XML_ext);
1226 GetFS()->endElementNS(XML_p, XML_extLst);
1228 GetFS()->endElementNS(XML_p, XML_nvPr);
1231 void DrawingML::WriteImageBrightnessContrastTransparence(uno::Reference<beans::XPropertySet> const & rXPropSet)
1233 sal_Int16 nBright = 0;
1234 sal_Int32 nContrast = 0;
1235 sal_Int16 nTransparence = 0;
1237 if (GetProperty(rXPropSet, "AdjustLuminance"))
1238 nBright = mAny.get<sal_Int16>();
1239 if (GetProperty(rXPropSet, "AdjustContrast"))
1240 nContrast = mAny.get<sal_Int32>();
1241 if (GetProperty(rXPropSet, "Transparency"))
1242 nTransparence = mAny.get<sal_Int16>();
1245 if (nBright || nContrast)
1247 mpFS->singleElementNS(XML_a, XML_lum,
1248 XML_bright, nBright ? OString::number(nBright * 1000).getStr() : nullptr,
1249 XML_contrast, nContrast ? OString::number(nContrast * 1000).getStr() : nullptr);
1252 if (nTransparence)
1254 sal_Int32 nAlphaMod = (100 - nTransparence ) * PER_PERCENT;
1255 mpFS->singleElementNS(XML_a, XML_alphaModFix, XML_amt, OString::number(nAlphaMod));
1259 OUString DrawingML::WriteXGraphicBlip(uno::Reference<beans::XPropertySet> const & rXPropSet,
1260 uno::Reference<graphic::XGraphic> const & rxGraphic,
1261 bool bRelPathToMedia)
1263 OUString sRelId;
1265 if (!rxGraphic.is())
1266 return sRelId;
1268 Graphic aGraphic(rxGraphic);
1269 if (mpTextExport)
1271 BitmapChecksum nChecksum = aGraphic.GetChecksum();
1272 sRelId = mpTextExport->FindRelId(nChecksum);
1274 if (sRelId.isEmpty())
1276 sRelId = WriteImage(aGraphic, bRelPathToMedia);
1277 if (mpTextExport)
1279 BitmapChecksum nChecksum = aGraphic.GetChecksum();
1280 mpTextExport->CacheRelId(nChecksum, sRelId);
1284 mpFS->startElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelId.toUtf8());
1286 WriteImageBrightnessContrastTransparence(rXPropSet);
1288 WriteArtisticEffect(rXPropSet);
1290 mpFS->endElementNS(XML_a, XML_blip);
1292 return sRelId;
1295 void DrawingML::WriteXGraphicBlipMode(uno::Reference<beans::XPropertySet> const & rXPropSet,
1296 uno::Reference<graphic::XGraphic> const & rxGraphic)
1298 BitmapMode eBitmapMode(BitmapMode_NO_REPEAT);
1299 if (GetProperty(rXPropSet, "FillBitmapMode"))
1300 mAny >>= eBitmapMode;
1302 SAL_INFO("oox.shape", "fill bitmap mode: " << int(eBitmapMode));
1304 switch (eBitmapMode)
1306 case BitmapMode_REPEAT:
1307 mpFS->singleElementNS(XML_a, XML_tile);
1308 break;
1309 case BitmapMode_STRETCH:
1310 WriteXGraphicStretch(rXPropSet, rxGraphic);
1311 break;
1312 default:
1313 break;
1317 void DrawingML::WriteBlipOrNormalFill( const Reference< XPropertySet >& xPropSet, const OUString& rURLPropName )
1319 // check for blip and otherwise fall back to normal fill
1320 // we always store normal fill properties but OOXML
1321 // uses a choice between our fill props and BlipFill
1322 if (GetProperty ( xPropSet, rURLPropName ))
1323 WriteBlipFill( xPropSet, rURLPropName );
1324 else
1325 WriteFill(xPropSet);
1328 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName )
1330 WriteBlipFill( rXPropSet, sURLPropName, XML_a );
1333 void DrawingML::WriteBlipFill( const Reference< XPropertySet >& rXPropSet, const OUString& sURLPropName, sal_Int32 nXmlNamespace )
1335 if ( GetProperty( rXPropSet, sURLPropName ) )
1337 uno::Reference<graphic::XGraphic> xGraphic;
1338 if (mAny.has<uno::Reference<awt::XBitmap>>())
1340 uno::Reference<awt::XBitmap> xBitmap = mAny.get<uno::Reference<awt::XBitmap>>();
1341 if (xBitmap.is())
1342 xGraphic.set(xBitmap, uno::UNO_QUERY);
1344 else if (mAny.has<uno::Reference<graphic::XGraphic>>())
1346 xGraphic = mAny.get<uno::Reference<graphic::XGraphic>>();
1349 if (xGraphic.is())
1351 bool bWriteMode = false;
1352 if (sURLPropName == "FillBitmap" || sURLPropName == "BackGraphic")
1353 bWriteMode = true;
1354 WriteXGraphicBlipFill(rXPropSet, xGraphic, nXmlNamespace, bWriteMode);
1359 void DrawingML::WriteXGraphicBlipFill(uno::Reference<beans::XPropertySet> const & rXPropSet,
1360 uno::Reference<graphic::XGraphic> const & rxGraphic,
1361 sal_Int32 nXmlNamespace, bool bWriteMode, bool bRelPathToMedia)
1363 if (!rxGraphic.is() )
1364 return;
1366 mpFS->startElementNS(nXmlNamespace , XML_blipFill, XML_rotWithShape, "0");
1368 WriteXGraphicBlip(rXPropSet, rxGraphic, bRelPathToMedia);
1370 if (bWriteMode)
1372 WriteXGraphicBlipMode(rXPropSet, rxGraphic);
1374 else if(GetProperty(rXPropSet, "FillBitmapStretch"))
1376 bool bStretch = mAny.get<bool>();
1378 if (bStretch)
1380 WriteXGraphicStretch(rXPropSet, rxGraphic);
1383 mpFS->endElementNS(nXmlNamespace, XML_blipFill);
1386 void DrawingML::WritePattFill( const Reference< XPropertySet >& rXPropSet )
1388 if ( GetProperty( rXPropSet, "FillHatch" ) )
1390 drawing::Hatch aHatch;
1391 mAny >>= aHatch;
1392 WritePattFill(rXPropSet, aHatch);
1396 void DrawingML::WritePattFill(const Reference<XPropertySet>& rXPropSet, const css::drawing::Hatch& rHatch)
1398 mpFS->startElementNS(XML_a, XML_pattFill, XML_prst, GetHatchPattern(rHatch));
1400 mpFS->startElementNS(XML_a, XML_fgClr);
1401 WriteColor(::Color(rHatch.Color));
1402 mpFS->endElementNS( XML_a , XML_fgClr );
1404 ::Color nColor = COL_WHITE;
1405 sal_Int32 nAlpha = 0;
1407 if ( GetProperty( rXPropSet, "FillBackground" ) )
1409 bool isBackgroundFilled = false;
1410 mAny >>= isBackgroundFilled;
1411 if( isBackgroundFilled )
1413 nAlpha = MAX_PERCENT;
1415 if( GetProperty( rXPropSet, "FillColor" ) )
1417 mAny >>= nColor;
1422 mpFS->startElementNS(XML_a, XML_bgClr);
1423 WriteColor(nColor, nAlpha);
1424 mpFS->endElementNS( XML_a , XML_bgClr );
1426 mpFS->endElementNS( XML_a , XML_pattFill );
1429 void DrawingML::WriteGraphicCropProperties(uno::Reference<beans::XPropertySet> const & rXPropSet, Size const & rOriginalSize, MapMode const & rMapMode)
1431 if (GetProperty(rXPropSet, "GraphicCrop"))
1433 Size aOriginalSize(rOriginalSize);
1435 // GraphicCrop is in mm100, so in case the original size is in pixels, convert it over.
1436 if (rMapMode.GetMapUnit() == MapUnit::MapPixel)
1437 aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aOriginalSize, MapMode(MapUnit::Map100thMM));
1439 css::text::GraphicCrop aGraphicCropStruct;
1440 mAny >>= aGraphicCropStruct;
1442 if ( (0 != aGraphicCropStruct.Left) || (0 != aGraphicCropStruct.Top) || (0 != aGraphicCropStruct.Right) || (0 != aGraphicCropStruct.Bottom) )
1444 mpFS->singleElementNS( XML_a, XML_srcRect,
1445 XML_l, OString::number(rtl::math::round(aGraphicCropStruct.Left * 100000.0 / aOriginalSize.Width())),
1446 XML_t, OString::number(rtl::math::round(aGraphicCropStruct.Top * 100000.0 / aOriginalSize.Height())),
1447 XML_r, OString::number(rtl::math::round(aGraphicCropStruct.Right * 100000.0 / aOriginalSize.Width())),
1448 XML_b, OString::number(rtl::math::round(aGraphicCropStruct.Bottom * 100000.0 / aOriginalSize.Height())) );
1453 void DrawingML::WriteSrcRectXGraphic(uno::Reference<beans::XPropertySet> const & rxPropertySet,
1454 uno::Reference<graphic::XGraphic> const & rxGraphic)
1456 Graphic aGraphic(rxGraphic);
1457 Size aOriginalSize = aGraphic.GetPrefSize();
1458 const MapMode& rMapMode = aGraphic.GetPrefMapMode();
1459 WriteGraphicCropProperties(rxPropertySet, aOriginalSize, rMapMode);
1462 void DrawingML::WriteXGraphicStretch(uno::Reference<beans::XPropertySet> const & rXPropSet,
1463 uno::Reference<graphic::XGraphic> const & rxGraphic)
1465 mpFS->startElementNS(XML_a, XML_stretch);
1467 bool bCrop = false;
1468 if (GetProperty(rXPropSet, "GraphicCrop"))
1470 css::text::GraphicCrop aGraphicCropStruct;
1471 mAny >>= aGraphicCropStruct;
1473 if ((0 != aGraphicCropStruct.Left)
1474 || (0 != aGraphicCropStruct.Top)
1475 || (0 != aGraphicCropStruct.Right)
1476 || (0 != aGraphicCropStruct.Bottom))
1478 Graphic aGraphic(rxGraphic);
1479 Size aOriginalSize(aGraphic.GetPrefSize());
1480 mpFS->singleElementNS(XML_a, XML_fillRect,
1481 XML_l, OString::number(((aGraphicCropStruct.Left) * 100000) / aOriginalSize.Width()),
1482 XML_t, OString::number(((aGraphicCropStruct.Top) * 100000) / aOriginalSize.Height()),
1483 XML_r, OString::number(((aGraphicCropStruct.Right) * 100000) / aOriginalSize.Width()),
1484 XML_b, OString::number(((aGraphicCropStruct.Bottom) * 100000) / aOriginalSize.Height()));
1485 bCrop = true;
1489 if (!bCrop)
1491 mpFS->singleElementNS(XML_a, XML_fillRect);
1494 mpFS->endElementNS(XML_a, XML_stretch);
1497 void DrawingML::WriteTransformation(const tools::Rectangle& rRect,
1498 sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, sal_Int32 nRotation, bool bIsGroupShape)
1501 mpFS->startElementNS( nXmlNamespace, XML_xfrm,
1502 XML_flipH, bFlipH ? "1" : nullptr,
1503 XML_flipV, bFlipV ? "1" : nullptr,
1504 XML_rot, (nRotation % 21600000) ? OString::number(nRotation).getStr() : nullptr );
1506 sal_Int32 nLeft = rRect.Left();
1507 sal_Int32 nTop = rRect.Top();
1508 if (GetDocumentType() == DOCUMENT_DOCX && !m_xParent.is())
1510 nLeft = 0;
1511 nTop = 0;
1514 mpFS->singleElementNS(XML_a, XML_off,
1515 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
1516 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
1517 mpFS->singleElementNS(XML_a, XML_ext,
1518 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1519 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1521 if (GetDocumentType() != DOCUMENT_DOCX && bIsGroupShape)
1523 mpFS->singleElementNS(XML_a, XML_chOff,
1524 XML_x, OString::number(oox::drawingml::convertHmmToEmu(nLeft)),
1525 XML_y, OString::number(oox::drawingml::convertHmmToEmu(nTop)));
1526 mpFS->singleElementNS(XML_a, XML_chExt,
1527 XML_cx, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetWidth())),
1528 XML_cy, OString::number(oox::drawingml::convertHmmToEmu(rRect.GetHeight())));
1531 mpFS->endElementNS( nXmlNamespace, XML_xfrm );
1534 void DrawingML::WriteShapeTransformation( const Reference< XShape >& rXShape, sal_Int32 nXmlNamespace, bool bFlipH, bool bFlipV, bool bSuppressRotation, bool bSuppressFlipping, bool bFlippedBeforeRotation )
1536 SAL_INFO("oox.shape", "write shape transformation");
1538 sal_Int32 nRotation=0;
1539 awt::Point aPos = rXShape->getPosition();
1540 awt::Size aSize = rXShape->getSize();
1542 bool bFlipHWrite = bFlipH && !bSuppressFlipping;
1543 bool bFlipVWrite = bFlipV && !bSuppressFlipping;
1544 bFlipH = bFlipH && !bFlippedBeforeRotation;
1545 bFlipV = bFlipV && !bFlippedBeforeRotation;
1547 if (GetDocumentType() == DOCUMENT_DOCX && m_xParent.is())
1549 awt::Point aParentPos = m_xParent->getPosition();
1550 aPos.X -= aParentPos.X;
1551 aPos.Y -= aParentPos.Y;
1554 if ( aSize.Width < 0 )
1555 aSize.Width = 1000;
1556 if ( aSize.Height < 0 )
1557 aSize.Height = 1000;
1558 if (!bSuppressRotation)
1560 SdrObject* pShape = GetSdrObjectFromXShape( rXShape );
1561 nRotation = pShape ? pShape->GetRotateAngle() : 0;
1562 if ( nRotation != 0 && GetDocumentType() != DOCUMENT_DOCX )
1564 int faccos=bFlipV ? -1 : 1;
1565 int facsin=bFlipH ? -1 : 1;
1566 aPos.X-=(1-faccos*cos(nRotation*F_PI18000))*aSize.Width/2-facsin*sin(nRotation*F_PI18000)*aSize.Height/2;
1567 aPos.Y-=(1-faccos*cos(nRotation*F_PI18000))*aSize.Height/2+facsin*sin(nRotation*F_PI18000)*aSize.Width/2;
1570 // The RotateAngle property's value is independent from any flipping, and that's exactly what we need here.
1571 uno::Reference<beans::XPropertySet> xPropertySet(rXShape, uno::UNO_QUERY);
1572 uno::Reference<beans::XPropertySetInfo> xPropertySetInfo = xPropertySet->getPropertySetInfo();
1573 if (xPropertySetInfo->hasPropertyByName("RotateAngle"))
1574 xPropertySet->getPropertyValue("RotateAngle") >>= nRotation;
1577 // OOXML flips shapes before rotating them.
1578 if(bFlipH != bFlipV)
1579 nRotation = nRotation * -1 + 36000;
1581 WriteTransformation(tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)), nXmlNamespace,
1582 bFlipHWrite, bFlipVWrite, ExportRotateClockwisify(nRotation), IsGroupShape( rXShape ));
1585 void DrawingML::WriteRunProperties( const Reference< XPropertySet >& rRun, bool bIsField, sal_Int32 nElement, bool bCheckDirect,
1586 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight )
1588 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
1589 Reference< XPropertyState > rXPropState( rRun, UNO_QUERY );
1590 OUString usLanguage;
1591 PropertyState eState;
1592 SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() );
1593 bool bComplex = ( nScriptType == SvtScriptType::COMPLEX );
1594 const char* bold = "0";
1595 const char* italic = nullptr;
1596 const char* underline = nullptr;
1597 const char* strikeout = nullptr;
1598 const char* cap = nullptr;
1599 sal_Int32 nSize = 1800;
1600 sal_Int32 nCharEscapement = 0;
1601 sal_Int32 nCharKerning = 0;
1603 if ( nElement == XML_endParaRPr && rbOverridingCharHeight )
1605 nSize = rnCharHeight;
1607 else if (GetProperty(rXPropSet, "CharHeight"))
1609 nSize = static_cast<sal_Int32>(100*(*o3tl::doAccess<float>(mAny)));
1610 if ( nElement == XML_rPr )
1612 rbOverridingCharHeight = true;
1613 rnCharHeight = nSize;
1617 if (GetProperty(rXPropSet, "CharKerning"))
1618 nCharKerning = static_cast<sal_Int32>(*o3tl::doAccess<sal_Int16>(mAny));
1619 /** While setting values in propertymap,
1620 * CharKerning converted using GetTextSpacingPoint
1621 * i.e set @ https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/textcharacterproperties.cxx#129
1622 * therefore to get original value CharKerning need to be convert.
1623 * https://opengrok.libreoffice.org/xref/core/oox/source/drawingml/drawingmltypes.cxx#95
1625 nCharKerning = ((nCharKerning * 720)-360) / 254;
1627 if ((bComplex && GetProperty(rXPropSet, "CharWeightComplex"))
1628 || GetProperty(rXPropSet, "CharWeight"))
1630 if ( *o3tl::doAccess<float>(mAny) >= awt::FontWeight::SEMIBOLD )
1631 bold = "1";
1634 if ((bComplex && GetProperty(rXPropSet, "CharPostureComplex"))
1635 || GetProperty(rXPropSet, "CharPosture"))
1636 switch ( *o3tl::doAccess<awt::FontSlant>(mAny) )
1638 case awt::FontSlant_OBLIQUE :
1639 case awt::FontSlant_ITALIC :
1640 italic = "1";
1641 break;
1642 default:
1643 break;
1646 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderline", eState)
1647 && eState == beans::PropertyState_DIRECT_VALUE)
1648 || GetProperty(rXPropSet, "CharUnderline"))
1650 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1652 case awt::FontUnderline::SINGLE :
1653 underline = "sng";
1654 break;
1655 case awt::FontUnderline::DOUBLE :
1656 underline = "dbl";
1657 break;
1658 case awt::FontUnderline::DOTTED :
1659 underline = "dotted";
1660 break;
1661 case awt::FontUnderline::DASH :
1662 underline = "dash";
1663 break;
1664 case awt::FontUnderline::LONGDASH :
1665 underline = "dashLong";
1666 break;
1667 case awt::FontUnderline::DASHDOT :
1668 underline = "dotDash";
1669 break;
1670 case awt::FontUnderline::DASHDOTDOT :
1671 underline = "dotDotDash";
1672 break;
1673 case awt::FontUnderline::WAVE :
1674 underline = "wavy";
1675 break;
1676 case awt::FontUnderline::DOUBLEWAVE :
1677 underline = "wavyDbl";
1678 break;
1679 case awt::FontUnderline::BOLD :
1680 underline = "heavy";
1681 break;
1682 case awt::FontUnderline::BOLDDOTTED :
1683 underline = "dottedHeavy";
1684 break;
1685 case awt::FontUnderline::BOLDDASH :
1686 underline = "dashHeavy";
1687 break;
1688 case awt::FontUnderline::BOLDLONGDASH :
1689 underline = "dashLongHeavy";
1690 break;
1691 case awt::FontUnderline::BOLDDASHDOT :
1692 underline = "dotDashHeavy";
1693 break;
1694 case awt::FontUnderline::BOLDDASHDOTDOT :
1695 underline = "dotDotDashHeavy";
1696 break;
1697 case awt::FontUnderline::BOLDWAVE :
1698 underline = "wavyHeavy";
1699 break;
1703 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharStrikeout", eState)
1704 && eState == beans::PropertyState_DIRECT_VALUE)
1705 || GetProperty(rXPropSet, "CharStrikeout"))
1707 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1709 case awt::FontStrikeout::NONE :
1710 strikeout = "noStrike";
1711 break;
1712 case awt::FontStrikeout::SINGLE :
1713 // LibO supports further values of character
1714 // strikeout, OOXML standard (20.1.10.78,
1715 // ST_TextStrikeType) however specifies only
1716 // 3 - single, double and none. Approximate
1717 // the remaining ones by single strike (better
1718 // some strike than none at all).
1719 // TODO: figure out how to do this better
1720 case awt::FontStrikeout::BOLD :
1721 case awt::FontStrikeout::SLASH :
1722 case awt::FontStrikeout::X :
1723 strikeout = "sngStrike";
1724 break;
1725 case awt::FontStrikeout::DOUBLE :
1726 strikeout = "dblStrike";
1727 break;
1731 if (GetProperty(rXPropSet, "CharLocale"))
1733 css::lang::Locale aLocale;
1734 mAny >>= aLocale;
1735 LanguageTag aLanguageTag( aLocale);
1736 if (!aLanguageTag.isSystemLocale())
1737 usLanguage = aLanguageTag.getBcp47MS();
1740 if (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapement", eState)
1741 && eState == beans::PropertyState_DIRECT_VALUE)
1742 mAny >>= nCharEscapement;
1744 if (nCharEscapement
1745 && (GetPropertyAndState(rXPropSet, rXPropState, "CharEscapementHeight", eState)
1746 && eState == beans::PropertyState_DIRECT_VALUE))
1748 sal_uInt32 nCharEscapementHeight = 0;
1749 mAny >>= nCharEscapementHeight;
1750 nSize = (nSize * nCharEscapementHeight) / 100;
1751 // MSO uses default ~58% size
1752 nSize = (nSize / 0.58);
1755 if (GetProperty(rXPropSet, "CharCaseMap"))
1757 switch ( *o3tl::doAccess<sal_Int16>(mAny) )
1759 case CaseMap::UPPERCASE :
1760 cap = "all";
1761 break;
1762 case CaseMap::SMALLCAPS :
1763 cap = "small";
1764 break;
1768 mpFS->startElementNS( XML_a, nElement,
1769 XML_b, bold,
1770 XML_i, italic,
1771 XML_lang, usLanguage.isEmpty() ? nullptr : usLanguage.toUtf8().getStr(),
1772 XML_sz, OString::number(nSize),
1773 // For Condensed character spacing spc value is negative.
1774 XML_spc, nCharKerning ? OString::number(nCharKerning).getStr() : nullptr,
1775 XML_strike, strikeout,
1776 XML_u, underline,
1777 XML_baseline, nCharEscapement == 0 ? nullptr : OString::number(nCharEscapement*1000).getStr(),
1778 XML_cap, cap );
1780 // mso doesn't like text color to be placed after typeface
1781 if ((bCheckDirect && GetPropertyAndState(rXPropSet, rXPropState, "CharColor", eState)
1782 && eState == beans::PropertyState_DIRECT_VALUE)
1783 || GetProperty(rXPropSet, "CharColor"))
1785 ::Color color( *o3tl::doAccess<sal_uInt32>(mAny) );
1786 SAL_INFO("oox.shape", "run color: " << sal_uInt32(color) << " auto: " << sal_uInt32(COL_AUTO));
1788 // tdf#104219 In LibreOffice and MS Office, there are two types of colors:
1789 // Automatic and Fixed. OOXML is setting automatic color, by not providing color.
1790 if( color != COL_AUTO )
1792 color.SetTransparency(0);
1793 // TODO: special handle embossed/engraved
1794 WriteSolidFill( color );
1798 if (underline
1799 && ((bCheckDirect
1800 && GetPropertyAndState(rXPropSet, rXPropState, "CharUnderlineColor", eState)
1801 && eState == beans::PropertyState_DIRECT_VALUE)
1802 || GetProperty(rXPropSet, "CharUnderlineColor")))
1804 ::Color color(*o3tl::doAccess<sal_uInt32>(mAny));
1805 // if color is automatic, then we shouldn't write information about color but to take color from character
1806 if( color != COL_AUTO )
1808 mpFS->startElementNS(XML_a, XML_uFill);
1809 WriteSolidFill( color );
1810 mpFS->endElementNS( XML_a, XML_uFill );
1812 else
1814 mpFS->singleElementNS(XML_a, XML_uFillTx);
1818 if (GetProperty(rXPropSet, "CharFontName"))
1820 const char* const pitch = nullptr;
1821 const char* const charset = nullptr;
1822 OUString usTypeface;
1824 mAny >>= usTypeface;
1825 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
1826 if (!aSubstName.isEmpty())
1827 usTypeface = aSubstName;
1829 mpFS->singleElementNS( XML_a, XML_latin,
1830 XML_typeface, usTypeface.toUtf8(),
1831 XML_pitchFamily, pitch,
1832 XML_charset, charset );
1835 if ((bComplex
1836 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameComplex", eState)
1837 && eState == beans::PropertyState_DIRECT_VALUE))
1838 || (!bComplex
1839 && (GetPropertyAndState(rXPropSet, rXPropState, "CharFontNameAsian", eState)
1840 && eState == beans::PropertyState_DIRECT_VALUE)))
1842 const char* const pitch = nullptr;
1843 const char* const charset = nullptr;
1844 OUString usTypeface;
1846 mAny >>= usTypeface;
1847 OUString aSubstName( GetSubsFontName( usTypeface, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) );
1848 if (!aSubstName.isEmpty())
1849 usTypeface = aSubstName;
1851 mpFS->singleElementNS( XML_a, bComplex ? XML_cs : XML_ea,
1852 XML_typeface, usTypeface.toUtf8(),
1853 XML_pitchFamily, pitch,
1854 XML_charset, charset );
1857 if( bIsField )
1859 Reference< XTextField > rXTextField;
1860 if (GetProperty(rXPropSet, "TextField"))
1861 mAny >>= rXTextField;
1862 if( rXTextField.is() )
1863 rXPropSet.set( rXTextField, UNO_QUERY );
1866 // field properties starts here
1867 if (GetProperty(rXPropSet, "URL"))
1869 OUString sURL;
1871 mAny >>= sURL;
1872 if( !sURL.isEmpty() ) {
1873 OUString sRelId = mpFB->addRelation( mpFS->getOutputStream(),
1874 oox::getRelationship(Relationship::HYPERLINK),
1875 sURL, true );
1877 mpFS->singleElementNS(XML_a, XML_hlinkClick, FSNS(XML_r, XML_id), sRelId.toUtf8());
1881 mpFS->endElementNS( XML_a, nElement );
1884 OUString DrawingML::GetFieldValue( const css::uno::Reference< css::text::XTextRange >& rRun, bool& bIsURLField )
1886 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
1887 OUString aFieldType, aFieldValue;
1889 if (GetProperty(rXPropSet, "TextPortionType"))
1891 aFieldType = *o3tl::doAccess<OUString>(mAny);
1892 SAL_INFO("oox.shape", "field type: " << aFieldType);
1895 if( aFieldType == "TextField" )
1897 Reference< XTextField > rXTextField;
1898 if (GetProperty(rXPropSet, "TextField"))
1899 mAny >>= rXTextField;
1900 if( rXTextField.is() )
1902 rXPropSet.set( rXTextField, UNO_QUERY );
1903 if( rXPropSet.is() )
1905 OUString aFieldKind( rXTextField->getPresentation( true ) );
1906 SAL_INFO("oox.shape", "field kind: " << aFieldKind);
1907 if( aFieldKind == "Page" )
1909 aFieldValue = "slidenum";
1911 else if( aFieldKind == "Pages" )
1913 aFieldValue = "slidecount";
1915 else if( aFieldKind == "PageName" )
1917 aFieldValue = "slidename";
1919 else if( aFieldKind == "URL" )
1921 bIsURLField = true;
1922 if (GetProperty(rXPropSet, "Representation"))
1923 mAny >>= aFieldValue;
1926 else if(aFieldKind == "Date")
1928 sal_Int32 nNumFmt = -1;
1929 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
1930 switch(static_cast<SvxDateFormat>(nNumFmt))
1932 case SvxDateFormat::StdSmall:
1933 case SvxDateFormat::A: aFieldValue = "datetime"; // 13/02/96
1934 break;
1935 case SvxDateFormat::B: aFieldValue = "datetime1"; // 13/02/1996
1936 break;
1937 case SvxDateFormat::StdBig:
1938 case SvxDateFormat::D: aFieldValue = "datetime3"; // 13 February 1996
1939 break;
1940 default: break;
1943 else if(aFieldKind == "ExtTime")
1945 sal_Int32 nNumFmt = -1;
1946 rXPropSet->getPropertyValue(UNO_TC_PROP_NUMFORMAT) >>= nNumFmt;
1947 switch(static_cast<SvxTimeFormat>(nNumFmt))
1949 case SvxTimeFormat::Standard:
1950 case SvxTimeFormat::HH24_MM_SS:
1951 aFieldValue = "datetime11"; // 13:49:38
1952 break;
1953 case SvxTimeFormat::HH24_MM:
1954 aFieldValue = "datetime10"; // 13:49
1955 break;
1956 case SvxTimeFormat::HH12_MM:
1957 aFieldValue = "datetime12"; // 01:49 PM
1958 break;
1959 case SvxTimeFormat::HH12_MM_SS:
1960 aFieldValue = "datetime13"; // 01:49:38 PM
1961 break;
1962 default: break;
1965 else if(aFieldKind == "ExtFile")
1967 sal_Int32 nNumFmt = -1;
1968 rXPropSet->getPropertyValue(UNO_TC_PROP_FILE_FORMAT) >>= nNumFmt;
1969 switch(nNumFmt)
1971 case 0: aFieldValue = "file"; // Path/File name
1972 break;
1973 case 1: aFieldValue = "file1"; // Path
1974 break;
1975 case 2: aFieldValue = "file2"; // File name without extension
1976 break;
1977 case 3: aFieldValue = "file3"; // File name with extension
1980 else if(aFieldKind == "Author")
1982 aFieldValue = "author";
1987 return aFieldValue;
1990 void DrawingML::WriteRun( const Reference< XTextRange >& rRun,
1991 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight)
1993 Reference< XPropertySet > rXPropSet( rRun, UNO_QUERY );
1994 sal_Int16 nLevel = -1;
1995 if (GetProperty(rXPropSet, "NumberingLevel"))
1996 mAny >>= nLevel;
1998 bool bNumberingIsNumber = true;
1999 if (GetProperty(rXPropSet, "NumberingIsNumber"))
2000 mAny >>= bNumberingIsNumber;
2002 bool bIsURLField = false;
2003 OUString sFieldValue = GetFieldValue( rRun, bIsURLField );
2004 bool bWriteField = !( sFieldValue.isEmpty() || bIsURLField );
2006 OUString sText = rRun->getString();
2008 //if there is no text following the bullet, add a space after the bullet
2009 if (nLevel !=-1 && bNumberingIsNumber && sText.isEmpty() )
2010 sText=" ";
2012 if ( bIsURLField )
2013 sText = sFieldValue;
2015 if( sText.isEmpty())
2017 Reference< XPropertySet > xPropSet( rRun, UNO_QUERY );
2021 if( !xPropSet.is() || !( xPropSet->getPropertyValue( "PlaceholderText" ) >>= sText ) )
2022 return;
2023 if( sText.isEmpty() )
2024 return;
2026 catch (const Exception &)
2028 return;
2032 if (sText == "\n")
2034 mpFS->singleElementNS(XML_a, XML_br);
2036 else
2038 if( bWriteField )
2040 OString sUUID(comphelper::xml::generateGUIDString());
2041 mpFS->startElementNS( XML_a, XML_fld,
2042 XML_id, sUUID.getStr(),
2043 XML_type, sFieldValue.toUtf8() );
2045 else
2047 mpFS->startElementNS(XML_a, XML_r);
2050 Reference< XPropertySet > xPropSet( rRun, uno::UNO_QUERY );
2051 WriteRunProperties( xPropSet, bIsURLField, XML_rPr, true, rbOverridingCharHeight, rnCharHeight );
2052 mpFS->startElementNS(XML_a, XML_t);
2053 mpFS->writeEscaped( sText );
2054 mpFS->endElementNS( XML_a, XML_t );
2056 if( bWriteField )
2057 mpFS->endElementNS( XML_a, XML_fld );
2058 else
2059 mpFS->endElementNS( XML_a, XML_r );
2063 static OUString GetAutoNumType(SvxNumType nNumberingType, bool bSDot, bool bPBehind, bool bPBoth)
2065 OUString sPrefixSuffix;
2067 if (bPBoth)
2068 sPrefixSuffix = "ParenBoth";
2069 else if (bPBehind)
2070 sPrefixSuffix = "ParenR";
2071 else if (bSDot)
2072 sPrefixSuffix = "Period";
2074 switch( nNumberingType )
2076 case SVX_NUM_CHARS_UPPER_LETTER_N :
2077 case SVX_NUM_CHARS_UPPER_LETTER :
2078 return "alphaUc" + sPrefixSuffix;
2080 case SVX_NUM_CHARS_LOWER_LETTER_N :
2081 case SVX_NUM_CHARS_LOWER_LETTER :
2082 return "alphaLc" + sPrefixSuffix;
2084 case SVX_NUM_ROMAN_UPPER :
2085 return "romanUc" + sPrefixSuffix;
2087 case SVX_NUM_ROMAN_LOWER :
2088 return "romanLc" + sPrefixSuffix;
2090 case SVX_NUM_ARABIC :
2092 if (sPrefixSuffix.isEmpty())
2093 return OUString("arabicPlain");
2094 else
2095 return "arabic" + sPrefixSuffix;
2097 default:
2098 break;
2101 return OUString();
2104 void DrawingML::WriteParagraphNumbering(const Reference< XPropertySet >& rXPropSet, float fFirstCharHeight, sal_Int16 nLevel )
2106 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2107 return;
2109 Reference< XIndexAccess > rXIndexAccess;
2111 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2112 return;
2114 SAL_INFO("oox.shape", "numbering rules");
2116 Sequence<PropertyValue> aPropertySequence;
2117 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2119 if (!aPropertySequence.hasElements())
2120 return;
2122 sal_Int32 nPropertyCount = aPropertySequence.getLength();
2124 const PropertyValue* pPropValue = aPropertySequence.getArray();
2126 SvxNumType nNumberingType = SVX_NUM_NUMBER_NONE;
2127 bool bSDot = false;
2128 bool bPBehind = false;
2129 bool bPBoth = false;
2130 sal_Unicode aBulletChar = 0x2022; // a bullet
2131 awt::FontDescriptor aFontDesc;
2132 bool bHasFontDesc = false;
2133 uno::Reference<graphic::XGraphic> xGraphic;
2134 sal_Int16 nBulletRelSize = 0;
2135 sal_Int16 nStartWith = 1;
2136 ::Color nBulletColor;
2137 bool bHasBulletColor = false;
2138 awt::Size aGraphicSize;
2140 for ( sal_Int32 i = 0; i < nPropertyCount; i++ )
2142 OUString aPropName( pPropValue[ i ].Name );
2143 SAL_INFO("oox.shape", "pro name: " << aPropName);
2144 if ( aPropName == "NumberingType" )
2146 nNumberingType = static_cast<SvxNumType>(*o3tl::doAccess<sal_Int16>(pPropValue[i].Value));
2148 else if ( aPropName == "Prefix" )
2150 if( *o3tl::doAccess<OUString>(pPropValue[i].Value) == ")")
2151 bPBoth = true;
2153 else if ( aPropName == "Suffix" )
2155 auto s = o3tl::doAccess<OUString>(pPropValue[i].Value);
2156 if( *s == ".")
2157 bSDot = true;
2158 else if( *s == ")")
2159 bPBehind = true;
2161 else if(aPropName == "BulletColor")
2163 nBulletColor = ::Color(*o3tl::doAccess<sal_uInt32>(pPropValue[i].Value));
2164 bHasBulletColor = true;
2166 else if ( aPropName == "BulletChar" )
2168 aBulletChar = (*o3tl::doAccess<OUString>(pPropValue[i].Value))[ 0 ];
2170 else if ( aPropName == "BulletFont" )
2172 aFontDesc = *o3tl::doAccess<awt::FontDescriptor>(pPropValue[i].Value);
2173 bHasFontDesc = true;
2175 // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font,
2176 // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used.
2177 // Because there might exist a lot of damaged documemts I added this two lines
2178 // which fixes the bullet problem for the export.
2179 if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") )
2180 aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252;
2183 else if ( aPropName == "BulletRelSize" )
2185 nBulletRelSize = *o3tl::doAccess<sal_Int16>(pPropValue[i].Value);
2187 else if ( aPropName == "StartWith" )
2189 nStartWith = *o3tl::doAccess<sal_Int16>(pPropValue[i].Value);
2191 else if (aPropName == "GraphicBitmap")
2193 auto xBitmap = pPropValue[i].Value.get<uno::Reference<awt::XBitmap>>();
2194 xGraphic.set(xBitmap, uno::UNO_QUERY);
2196 else if ( aPropName == "GraphicSize" )
2198 aGraphicSize = *o3tl::doAccess<awt::Size>(pPropValue[i].Value);
2199 SAL_INFO("oox.shape", "graphic size: " << aGraphicSize.Width << "x" << aGraphicSize.Height);
2203 if (nNumberingType == SVX_NUM_NUMBER_NONE)
2204 return;
2206 Graphic aGraphic(xGraphic);
2207 if (xGraphic.is() && aGraphic.GetType() != GraphicType::NONE)
2209 long nFirstCharHeightMm = TransformMetric(fFirstCharHeight * 100.f, FieldUnit::POINT, FieldUnit::MM);
2210 float fBulletSizeRel = aGraphicSize.Height / static_cast<float>(nFirstCharHeightMm) / OOX_BULLET_LIST_SCALE_FACTOR;
2212 OUString sRelationId;
2214 if (fBulletSizeRel < 1.0f)
2216 // Add padding to get the bullet point centered in PPT
2217 Size aDestSize(64, 64);
2218 float fBulletSizeRelX = fBulletSizeRel / aGraphicSize.Height * aGraphicSize.Width;
2219 long nPaddingX = std::max<long>(0, std::lround((aDestSize.Width() - fBulletSizeRelX * aDestSize.Width()) / 2.f));
2220 long nPaddingY = std::lround((aDestSize.Height() - fBulletSizeRel * aDestSize.Height()) / 2.f);
2221 tools::Rectangle aDestRect(nPaddingX, nPaddingY, aDestSize.Width() - nPaddingX, aDestSize.Height() - nPaddingY);
2223 AlphaMask aMask(aDestSize);
2224 aMask.Erase(255);
2225 BitmapEx aSourceBitmap(aGraphic.GetBitmapEx());
2226 aSourceBitmap.Scale(aDestRect.GetSize());
2227 tools::Rectangle aSourceRect(Point(0, 0), aDestRect.GetSize());
2228 BitmapEx aDestBitmap(Bitmap(aDestSize, 24), aMask);
2229 aDestBitmap.CopyPixel(aDestRect, aSourceRect, &aSourceBitmap);
2230 Graphic aDestGraphic(aDestBitmap);
2231 sRelationId = WriteImage(aDestGraphic);
2232 fBulletSizeRel = 1.0f;
2234 else
2236 sRelationId = WriteImage(aGraphic);
2239 mpFS->singleElementNS( XML_a, XML_buSzPct,
2240 XML_val, OString::number(std::min<sal_Int32>(std::lround(100000.f * fBulletSizeRel), 400000)));
2241 mpFS->startElementNS(XML_a, XML_buBlip);
2242 mpFS->singleElementNS(XML_a, XML_blip, FSNS(XML_r, XML_embed), sRelationId.toUtf8());
2243 mpFS->endElementNS( XML_a, XML_buBlip );
2245 else
2247 if(bHasBulletColor)
2249 if (nBulletColor == COL_AUTO )
2251 nBulletColor = ::Color(mbIsBackgroundDark ? 0xffffff : 0x000000);
2253 mpFS->startElementNS(XML_a, XML_buClr);
2254 WriteColor( nBulletColor );
2255 mpFS->endElementNS( XML_a, XML_buClr );
2258 if( nBulletRelSize && nBulletRelSize != 100 )
2259 mpFS->singleElementNS( XML_a, XML_buSzPct,
2260 XML_val, OString::number(std::clamp<sal_Int32>(1000*nBulletRelSize, 25000, 400000)));
2261 if( bHasFontDesc )
2263 if ( SVX_NUM_CHAR_SPECIAL == nNumberingType )
2264 aBulletChar = SubstituteBullet( aBulletChar, aFontDesc );
2265 mpFS->singleElementNS( XML_a, XML_buFont,
2266 XML_typeface, aFontDesc.Name.toUtf8(),
2267 XML_charset, (aFontDesc.CharSet == awt::CharSet::SYMBOL) ? "2" : nullptr );
2270 OUString aAutoNumType = GetAutoNumType( nNumberingType, bSDot, bPBehind, bPBoth );
2272 if (!aAutoNumType.isEmpty())
2274 mpFS->singleElementNS(XML_a, XML_buAutoNum,
2275 XML_type, aAutoNumType.toUtf8(),
2276 XML_startAt, nStartWith > 1 ? OString::number(nStartWith).getStr() : nullptr);
2278 else
2280 mpFS->singleElementNS(XML_a, XML_buChar, XML_char, OUString(aBulletChar).toUtf8());
2285 bool DrawingML::IsGroupShape( const Reference< XShape >& rXShape )
2287 bool bRet = false;
2288 if ( rXShape.is() )
2290 uno::Reference<lang::XServiceInfo> xServiceInfo(rXShape, uno::UNO_QUERY_THROW);
2291 bRet = xServiceInfo->supportsService("com.sun.star.drawing.GroupShape");
2293 return bRet;
2296 bool DrawingML::IsDiagram(const Reference<XShape>& rXShape)
2298 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
2299 if (!xPropSet.is())
2300 return false;
2302 // if the shape doesn't have the InteropGrabBag property, it's not a diagram
2303 uno::Reference<beans::XPropertySetInfo> xPropSetInfo = xPropSet->getPropertySetInfo();
2304 OUString aName = UNO_NAME_MISC_OBJ_INTEROPGRABBAG;
2305 if (!xPropSetInfo->hasPropertyByName(aName))
2306 return false;
2308 uno::Sequence<beans::PropertyValue> propList;
2309 xPropSet->getPropertyValue(aName) >>= propList;
2310 for (sal_Int32 nProp = 0; nProp < propList.getLength(); ++nProp)
2312 // if we find any of the diagram components, it's a diagram
2313 OUString propName = propList[nProp].Name;
2314 if (propName == "OOXData" || propName == "OOXLayout" || propName == "OOXStyle"
2315 || propName == "OOXColor" || propName == "OOXDrawing")
2316 return true;
2318 return false;
2321 sal_Int32 DrawingML::getBulletMarginIndentation (const Reference< XPropertySet >& rXPropSet,sal_Int16 nLevel, const OUString& propName)
2323 if (nLevel < 0 || !GetProperty(rXPropSet, "NumberingRules"))
2324 return 0;
2326 Reference< XIndexAccess > rXIndexAccess;
2328 if (!(mAny >>= rXIndexAccess) || nLevel >= rXIndexAccess->getCount())
2329 return 0;
2331 SAL_INFO("oox.shape", "numbering rules");
2333 Sequence<PropertyValue> aPropertySequence;
2334 rXIndexAccess->getByIndex(nLevel) >>= aPropertySequence;
2336 if (!aPropertySequence.hasElements())
2337 return 0;
2339 sal_Int32 nPropertyCount = aPropertySequence.getLength();
2341 const PropertyValue* pPropValue = aPropertySequence.getArray();
2343 for ( sal_Int32 i = 0; i < nPropertyCount; i++ )
2345 OUString aPropName( pPropValue[ i ].Name );
2346 SAL_INFO("oox.shape", "pro name: " << aPropName);
2347 if ( aPropName == propName )
2348 return *o3tl::doAccess<sal_Int32>(pPropValue[i].Value);
2351 return 0;
2354 const char* DrawingML::GetAlignment( style::ParagraphAdjust nAlignment )
2356 const char* sAlignment = nullptr;
2358 switch( nAlignment )
2360 case style::ParagraphAdjust_CENTER:
2361 sAlignment = "ctr";
2362 break;
2363 case style::ParagraphAdjust_RIGHT:
2364 sAlignment = "r";
2365 break;
2366 case style::ParagraphAdjust_BLOCK:
2367 sAlignment = "just";
2368 break;
2369 default:
2373 return sAlignment;
2376 void DrawingML::WriteLinespacing( const LineSpacing& rSpacing )
2378 if( rSpacing.Mode == LineSpacingMode::PROP )
2380 mpFS->singleElementNS( XML_a, XML_spcPct,
2381 XML_val, OString::number(static_cast<sal_Int32>(rSpacing.Height)*1000));
2383 else
2385 mpFS->singleElementNS( XML_a, XML_spcPts,
2386 XML_val, OString::number(std::lround(rSpacing.Height / 25.4 * 72)));
2390 void DrawingML::WriteParagraphProperties( const Reference< XTextContent >& rParagraph, float fFirstCharHeight)
2392 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
2393 Reference< XPropertyState > rXPropState( rParagraph, UNO_QUERY );
2394 PropertyState eState;
2396 if( !rXPropSet.is() || !rXPropState.is() )
2397 return;
2399 sal_Int16 nLevel = -1;
2400 if (GetProperty(rXPropSet, "NumberingLevel"))
2401 mAny >>= nLevel;
2403 sal_Int16 nTmp = sal_Int16(style::ParagraphAdjust_LEFT);
2404 if (GetProperty(rXPropSet, "ParaAdjust"))
2405 mAny >>= nTmp;
2406 style::ParagraphAdjust nAlignment = static_cast<style::ParagraphAdjust>(nTmp);
2408 bool bHasLinespacing = false;
2409 LineSpacing aLineSpacing;
2410 if (GetPropertyAndState(rXPropSet, rXPropState, "ParaLineSpacing", eState)
2411 && eState == beans::PropertyState_DIRECT_VALUE)
2412 bHasLinespacing = ( mAny >>= aLineSpacing );
2414 bool bRtl = false;
2415 if (GetProperty(rXPropSet, "WritingMode"))
2417 sal_Int16 nWritingMode;
2418 if( ( mAny >>= nWritingMode ) && nWritingMode == text::WritingMode2::RL_TB )
2420 bRtl = true;
2424 sal_Int32 nParaLeftMargin = 0;
2425 sal_Int32 nParaFirstLineIndent = 0;
2427 if (GetProperty(rXPropSet, "ParaLeftMargin"))
2428 mAny >>= nParaLeftMargin;
2429 if (GetProperty(rXPropSet, "ParaFirstLineIndent"))
2430 mAny >>= nParaFirstLineIndent;
2432 sal_Int32 nParaTopMargin = 0;
2433 sal_Int32 nParaBottomMargin = 0;
2435 if (GetProperty(rXPropSet, "ParaTopMargin"))
2436 mAny >>= nParaTopMargin;
2437 if (GetProperty(rXPropSet, "ParaBottomMargin"))
2438 mAny >>= nParaBottomMargin;
2440 sal_Int32 nLeftMargin = getBulletMarginIndentation ( rXPropSet, nLevel,"LeftMargin");
2441 sal_Int32 nLineIndentation = getBulletMarginIndentation ( rXPropSet, nLevel,"FirstLineOffset");
2443 if( nLevel != -1
2444 || nAlignment != style::ParagraphAdjust_LEFT
2445 || bHasLinespacing )
2447 if (nParaLeftMargin) // For Paragraph
2448 mpFS->startElementNS( XML_a, XML_pPr,
2449 XML_lvl, nLevel > 0 ? OString::number(nLevel).getStr() : nullptr,
2450 XML_marL, nParaLeftMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nParaLeftMargin)).getStr() : nullptr,
2451 XML_indent, nParaFirstLineIndent ? OString::number(oox::drawingml::convertHmmToEmu(nParaFirstLineIndent)).getStr() : nullptr,
2452 XML_algn, GetAlignment( nAlignment ),
2453 XML_rtl, bRtl ? ToPsz10(bRtl) : nullptr );
2454 else
2455 mpFS->startElementNS( XML_a, XML_pPr,
2456 XML_lvl, nLevel > 0 ? OString::number(nLevel).getStr() : nullptr,
2457 XML_marL, nLeftMargin > 0 ? OString::number(oox::drawingml::convertHmmToEmu(nLeftMargin)).getStr() : nullptr,
2458 XML_indent, nLineIndentation ? OString::number(oox::drawingml::convertHmmToEmu(nLineIndentation)).getStr() : nullptr,
2459 XML_algn, GetAlignment( nAlignment ),
2460 XML_rtl, bRtl ? ToPsz10(bRtl) : nullptr );
2463 if( bHasLinespacing )
2465 mpFS->startElementNS(XML_a, XML_lnSpc);
2466 WriteLinespacing( aLineSpacing );
2467 mpFS->endElementNS( XML_a, XML_lnSpc );
2470 if( nParaTopMargin != 0 )
2472 mpFS->startElementNS(XML_a, XML_spcBef);
2474 mpFS->singleElementNS( XML_a, XML_spcPts,
2475 XML_val, OString::number(std::lround(nParaTopMargin / 25.4 * 72)));
2477 mpFS->endElementNS( XML_a, XML_spcBef );
2480 if( nParaBottomMargin != 0 )
2482 mpFS->startElementNS(XML_a, XML_spcAft);
2484 mpFS->singleElementNS( XML_a, XML_spcPts,
2485 XML_val, OString::number(std::lround(nParaBottomMargin / 25.4 * 72)));
2487 mpFS->endElementNS( XML_a, XML_spcAft );
2490 WriteParagraphNumbering( rXPropSet, fFirstCharHeight, nLevel );
2492 mpFS->endElementNS( XML_a, XML_pPr );
2496 void DrawingML::WriteParagraph( const Reference< XTextContent >& rParagraph,
2497 bool& rbOverridingCharHeight, sal_Int32& rnCharHeight )
2499 Reference< XEnumerationAccess > access( rParagraph, UNO_QUERY );
2500 if( !access.is() )
2501 return;
2503 Reference< XEnumeration > enumeration( access->createEnumeration() );
2504 if( !enumeration.is() )
2505 return;
2507 mpFS->startElementNS(XML_a, XML_p);
2509 bool bPropertiesWritten = false;
2510 while( enumeration->hasMoreElements() )
2512 Reference< XTextRange > run;
2513 Any any ( enumeration->nextElement() );
2515 if (any >>= run)
2517 if( !bPropertiesWritten )
2519 float fFirstCharHeight = rnCharHeight / 1000.;
2520 Reference< XPropertySet > xFirstRunPropSet (run, UNO_QUERY);
2521 Reference< XPropertySetInfo > xFirstRunPropSetInfo = xFirstRunPropSet->getPropertySetInfo();
2522 if( xFirstRunPropSetInfo->hasPropertyByName("CharHeight") )
2523 fFirstCharHeight = xFirstRunPropSet->getPropertyValue("CharHeight").get<float>();
2524 WriteParagraphProperties( rParagraph, fFirstCharHeight );
2525 bPropertiesWritten = true;
2527 WriteRun( run, rbOverridingCharHeight, rnCharHeight );
2530 Reference< XPropertySet > rXPropSet( rParagraph, UNO_QUERY );
2531 WriteRunProperties( rXPropSet, false, XML_endParaRPr, false, rbOverridingCharHeight, rnCharHeight );
2533 mpFS->endElementNS( XML_a, XML_p );
2536 void DrawingML::WriteText( const Reference< XInterface >& rXIface, const OUString& presetWarp, bool bBodyPr, bool bText, sal_Int32 nXmlNamespace )
2538 Reference< XText > xXText( rXIface, UNO_QUERY );
2539 Reference< XPropertySet > rXPropSet( rXIface, UNO_QUERY );
2541 if( !xXText.is() )
2542 return;
2544 sal_Int32 nTextRotateAngle = 0;
2546 #define DEFLRINS 254
2547 #define DEFTBINS 127
2548 sal_Int32 nLeft, nRight, nTop, nBottom;
2549 nLeft = nRight = DEFLRINS;
2550 nTop = nBottom = DEFTBINS;
2552 // top inset looks a bit different compared to ppt export
2553 // check if something related doesn't work as expected
2554 if (GetProperty(rXPropSet, "TextLeftDistance"))
2555 mAny >>= nLeft;
2556 if (GetProperty(rXPropSet, "TextRightDistance"))
2557 mAny >>= nRight;
2558 if (GetProperty(rXPropSet, "TextUpperDistance"))
2559 mAny >>= nTop;
2560 if (GetProperty(rXPropSet, "TextLowerDistance"))
2561 mAny >>= nBottom;
2563 TextVerticalAdjust eVerticalAlignment( TextVerticalAdjust_TOP );
2564 const char* sVerticalAlignment = nullptr;
2565 if (GetProperty(rXPropSet, "TextVerticalAdjust"))
2566 mAny >>= eVerticalAlignment;
2567 if( eVerticalAlignment != TextVerticalAdjust_TOP )
2568 sVerticalAlignment = GetTextVerticalAdjust(eVerticalAlignment);
2570 const char* sWritingMode = nullptr;
2571 bool bVertical = false;
2572 if (GetProperty(rXPropSet, "TextWritingMode"))
2574 WritingMode eMode;
2576 if( ( mAny >>= eMode ) && eMode == WritingMode_TB_RL )
2578 sWritingMode = "vert";
2579 bVertical = true;
2583 if (GetProperty(rXPropSet, "CustomShapeGeometry"))
2585 Sequence< PropertyValue > aProps;
2586 if ( mAny >>= aProps )
2588 for ( sal_Int32 i = 0, nElems = aProps.getLength(); i < nElems; ++i )
2590 if ( aProps[ i ].Name == "TextPreRotateAngle" && ( aProps[ i ].Value >>= nTextRotateAngle ) )
2592 if ( nTextRotateAngle == -90 )
2594 sWritingMode = "vert";
2595 bVertical = true;
2597 else if ( nTextRotateAngle == -270 )
2599 sWritingMode = "vert270";
2600 bVertical = true;
2602 break;
2608 TextHorizontalAdjust eHorizontalAlignment( TextHorizontalAdjust_CENTER );
2609 bool bHorizontalCenter = false;
2610 if (GetProperty(rXPropSet, "TextHorizontalAdjust"))
2611 mAny >>= eHorizontalAlignment;
2612 if( eHorizontalAlignment == TextHorizontalAdjust_CENTER )
2613 bHorizontalCenter = true;
2614 else if( bVertical && eHorizontalAlignment == TextHorizontalAdjust_LEFT )
2615 sVerticalAlignment = "b";
2617 bool bHasWrap = false;
2618 bool bWrap = false;
2619 // Only custom shapes obey the TextWordWrap option, normal text always wraps.
2620 if (dynamic_cast<SvxCustomShape*>(rXIface.get()) && GetProperty(rXPropSet, "TextWordWrap"))
2622 mAny >>= bWrap;
2623 bHasWrap = true;
2626 if (bBodyPr)
2628 const char* pWrap = bHasWrap && !bWrap ? "none" : nullptr;
2629 if (GetDocumentType() == DOCUMENT_DOCX)
2631 // In case of DOCX, if we want to have the same effect as
2632 // TextShape's automatic word wrapping, then we need to set
2633 // wrapping to square.
2634 uno::Reference<lang::XServiceInfo> xServiceInfo(rXIface, uno::UNO_QUERY);
2635 if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.drawing.TextShape"))
2636 pWrap = "square";
2638 mpFS->startElementNS( (nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr,
2639 XML_wrap, pWrap,
2640 XML_lIns, (nLeft != DEFLRINS) ? OString::number(oox::drawingml::convertHmmToEmu(nLeft)).getStr() : nullptr,
2641 XML_rIns, (nRight != DEFLRINS) ? OString::number(oox::drawingml::convertHmmToEmu(nRight)).getStr() : nullptr,
2642 XML_tIns, (nTop != DEFTBINS) ? OString::number(oox::drawingml::convertHmmToEmu(nTop)).getStr() : nullptr,
2643 XML_bIns, (nBottom != DEFTBINS) ? OString::number(oox::drawingml::convertHmmToEmu(nBottom)).getStr() : nullptr,
2644 XML_anchor, sVerticalAlignment,
2645 XML_anchorCtr, bHorizontalCenter ? "1" : nullptr,
2646 XML_vert, sWritingMode,
2647 XML_rot, (nTextRotateAngle != 0) ? oox::drawingml::calcRotationValue( nTextRotateAngle * 100 ).getStr() : nullptr );
2648 if( !presetWarp.isEmpty())
2650 mpFS->singleElementNS(XML_a, XML_prstTxWarp, XML_prst, presetWarp.toUtf8());
2652 if (GetDocumentType() == DOCUMENT_DOCX || GetDocumentType() == DOCUMENT_XLSX)
2654 bool bTextAutoGrowHeight = false;
2655 if (GetProperty(rXPropSet, "TextAutoGrowHeight"))
2656 mAny >>= bTextAutoGrowHeight;
2657 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
2659 if (GetDocumentType() == DOCUMENT_PPTX)
2661 TextFitToSizeType eFit = TextFitToSizeType_NONE;
2662 if (GetProperty(rXPropSet, "TextFitToSize"))
2663 mAny >>= eFit;
2665 if (eFit == TextFitToSizeType_AUTOFIT)
2667 const sal_Int32 MAX_SCALE_VAL = 100000;
2668 sal_Int32 nFontScale = MAX_SCALE_VAL;
2669 SvxShapeText* pTextShape = dynamic_cast<SvxShapeText*>(rXIface.get());
2670 if (pTextShape)
2672 SdrTextObj* pTextObject = dynamic_cast<SdrTextObj*>(pTextShape->GetSdrObject());
2673 if (pTextObject)
2675 double fScaleY = pTextObject->GetFontScaleY();
2676 nFontScale = static_cast<sal_uInt32>(fScaleY * 100) * 1000;
2680 mpFS->singleElementNS(XML_a, XML_normAutofit, XML_fontScale,
2681 ( nFontScale < MAX_SCALE_VAL && nFontScale > 0 ) ? OString::number(nFontScale).getStr() : nullptr);
2683 else
2685 bool bTextAutoGrowHeight = false;
2686 if (GetProperty(rXPropSet, "TextAutoGrowHeight"))
2687 mAny >>= bTextAutoGrowHeight;
2688 mpFS->singleElementNS(XML_a, (bTextAutoGrowHeight ? XML_spAutoFit : XML_noAutofit));
2691 mpFS->endElementNS((nXmlNamespace ? nXmlNamespace : XML_a), XML_bodyPr);
2694 Reference< XEnumerationAccess > access( xXText, UNO_QUERY );
2695 if( !access.is() || !bText )
2696 return;
2698 Reference< XEnumeration > enumeration( access->createEnumeration() );
2699 if( !enumeration.is() )
2700 return;
2702 uno::Reference<drawing::XShape> xShape(rXIface, uno::UNO_QUERY);
2703 SdrObject* pSdrObject = xShape.is() ? GetSdrObjectFromXShape(xShape) : nullptr;
2704 const SdrTextObj* pTxtObj = dynamic_cast<SdrTextObj*>( pSdrObject );
2705 if (pTxtObj && mpTextExport)
2707 const OutlinerParaObject* pParaObj = nullptr;
2708 bool bOwnParaObj = false;
2711 #i13885#
2712 When the object is actively being edited, that text is not set into
2713 the objects normal text object, but lives in a separate object.
2715 if (pTxtObj->IsTextEditActive())
2717 pParaObj = pTxtObj->GetEditOutlinerParaObject().release();
2718 bOwnParaObj = true;
2720 else
2721 pParaObj = pTxtObj->GetOutlinerParaObject();
2723 if (pParaObj)
2725 // this is reached only in case some text is attached to the shape
2726 mpTextExport->WriteOutliner(*pParaObj);
2727 if (bOwnParaObj)
2728 delete pParaObj;
2730 return;
2733 bool bOverridingCharHeight = false;
2734 sal_Int32 nCharHeight = -1;
2736 while( enumeration->hasMoreElements() )
2738 Reference< XTextContent > paragraph;
2739 Any any ( enumeration->nextElement() );
2741 if( any >>= paragraph)
2742 WriteParagraph( paragraph, bOverridingCharHeight, nCharHeight );
2746 void DrawingML::WritePresetShape( const char* pShape , std::vector< std::pair<sal_Int32,sal_Int32>> & rAvList )
2748 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2749 if ( !rAvList.empty() )
2752 mpFS->startElementNS(XML_a, XML_avLst);
2753 for (auto const& elem : rAvList)
2755 OString sName = OString("adj") + ( ( elem.first > 0 ) ? OString::number(elem.first) : OString() );
2756 OString sFmla = OString("val ") + OString::number( elem.second );
2758 mpFS->singleElementNS(XML_a, XML_gd, XML_name, sName, XML_fmla, sFmla);
2760 mpFS->endElementNS( XML_a, XML_avLst );
2762 else
2763 mpFS->singleElementNS(XML_a, XML_avLst);
2765 mpFS->endElementNS( XML_a, XML_prstGeom );
2768 void DrawingML::WritePresetShape( const char* pShape )
2770 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2771 mpFS->singleElementNS(XML_a, XML_avLst);
2772 mpFS->endElementNS( XML_a, XML_prstGeom );
2775 static std::map< OString, std::vector<OString> > lcl_getAdjNames()
2777 std::map< OString, std::vector<OString> > aRet;
2779 OUString aPath("$BRAND_BASE_DIR/" LIBO_SHARE_FOLDER "/filter/oox-drawingml-adj-names");
2780 rtl::Bootstrap::expandMacros(aPath);
2781 SvFileStream aStream(aPath, StreamMode::READ);
2782 if (aStream.GetError() != ERRCODE_NONE)
2783 SAL_WARN("oox.shape", "failed to open oox-drawingml-adj-names");
2784 OString aLine;
2785 bool bNotDone = aStream.ReadLine(aLine);
2786 while (bNotDone)
2788 sal_Int32 nIndex = 0;
2789 // Each line is in a "key\tvalue" format: read the key, the rest is the value.
2790 OString aKey = aLine.getToken(0, '\t', nIndex);
2791 OString aValue = aLine.copy(nIndex);
2792 aRet[aKey].push_back(aValue);
2793 bNotDone = aStream.ReadLine(aLine);
2795 return aRet;
2798 void DrawingML::WritePresetShape( const char* pShape, MSO_SPT eShapeType, bool bPredefinedHandlesUsed, const PropertyValue& rProp )
2800 static std::map< OString, std::vector<OString> > aAdjMap = lcl_getAdjNames();
2801 // If there are predefined adj names for this shape type, look them up now.
2802 std::vector<OString> aAdjustments;
2803 if (aAdjMap.find(OString(pShape)) != aAdjMap.end())
2804 aAdjustments = aAdjMap[OString(pShape)];
2806 mpFS->startElementNS(XML_a, XML_prstGeom, XML_prst, pShape);
2807 mpFS->startElementNS(XML_a, XML_avLst);
2809 Sequence< drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq;
2810 if ( ( rProp.Value >>= aAdjustmentSeq )
2811 && eShapeType != mso_sptActionButtonForwardNext // we have adjustments values for these type of shape, but MSO doesn't like them
2812 && eShapeType != mso_sptActionButtonBackPrevious // so they are now disabled
2813 && OString(pShape) != "rect" //some shape types are commented out in pCustomShapeTypeTranslationTable[] & are being defaulted to rect & rect does not have adjustment values/name.
2816 SAL_INFO("oox.shape", "adj seq len: " << aAdjustmentSeq.getLength());
2817 sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0;
2818 if ( bPredefinedHandlesUsed )
2819 EscherPropertyContainer::LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted );
2821 sal_Int32 nValue, nLength = aAdjustmentSeq.getLength();
2822 // 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.
2823 // Sometimes there are more values than needed, so we ignore the excessive ones.
2824 if (aAdjustments.size() <= static_cast<sal_uInt32>(nLength))
2826 for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAdjustments.size()); i++)
2828 if( EscherPropertyContainer::GetAdjustmentValue( aAdjustmentSeq[ i ], i, nAdjustmentsWhichNeedsToBeConverted, nValue ) )
2830 // If the document model doesn't have an adjustment name (e.g. shape was created from VML), then take it from the predefined list.
2831 OString aAdjName = aAdjustmentSeq[i].Name.isEmpty()
2832 ? aAdjustments[i]
2833 : aAdjustmentSeq[i].Name.toUtf8();
2835 mpFS->singleElementNS( XML_a, XML_gd,
2836 XML_name, aAdjName,
2837 XML_fmla, "val " + OString::number(nValue));
2843 mpFS->endElementNS( XML_a, XML_avLst );
2844 mpFS->endElementNS( XML_a, XML_prstGeom );
2847 bool DrawingML::WriteCustomGeometry(
2848 const Reference< XShape >& rXShape,
2849 const SdrObjCustomShape& rSdrObjCustomShape)
2851 uno::Reference< beans::XPropertySet > aXPropSet;
2852 uno::Any aAny( rXShape->queryInterface(cppu::UnoType<beans::XPropertySet>::get()));
2854 if ( ! (aAny >>= aXPropSet) )
2855 return false;
2859 aAny = aXPropSet->getPropertyValue( "CustomShapeGeometry" );
2860 if ( !aAny.hasValue() )
2861 return false;
2863 catch( const ::uno::Exception& )
2865 return false;
2869 auto pGeometrySeq = o3tl::tryAccess<uno::Sequence<beans::PropertyValue>>(aAny);
2871 if ( pGeometrySeq )
2873 for( int i = 0; i < pGeometrySeq->getLength(); ++i )
2875 const beans::PropertyValue& rProp = (*pGeometrySeq)[ i ];
2876 if ( rProp.Name == "Path" )
2878 uno::Sequence<beans::PropertyValue> aPathProp;
2879 rProp.Value >>= aPathProp;
2881 uno::Sequence<drawing::EnhancedCustomShapeParameterPair> aPairs;
2882 uno::Sequence<drawing::EnhancedCustomShapeSegment> aSegments;
2883 uno::Sequence<awt::Size> aPathSize;
2884 for (int j = 0; j < aPathProp.getLength(); ++j )
2886 const beans::PropertyValue& rPathProp = aPathProp[j];
2887 if (rPathProp.Name == "Coordinates")
2888 rPathProp.Value >>= aPairs;
2889 else if (rPathProp.Name == "Segments")
2890 rPathProp.Value >>= aSegments;
2891 else if (rPathProp.Name == "SubViewSize")
2892 rPathProp.Value >>= aPathSize;
2895 if ( !aPairs.hasElements() )
2896 return false;
2898 if ( !aSegments.hasElements() )
2900 aSegments = uno::Sequence<drawing::EnhancedCustomShapeSegment>(4);
2901 aSegments[0].Count = 1;
2902 aSegments[0].Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO;
2903 aSegments[1].Count = static_cast<sal_Int16>(std::min( aPairs.getLength() - 1, sal_Int32(32767) ));
2904 aSegments[1].Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO;
2905 aSegments[2].Count = 0;
2906 aSegments[2].Command = drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH;
2907 aSegments[3].Count = 0;
2908 aSegments[3].Command = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH;
2911 int nExpectedPairCount = 0;
2912 for( int j = 0; j < aSegments.getLength(); ++j )
2914 nExpectedPairCount += aSegments[j].Count;
2917 if ( nExpectedPairCount > aPairs.getLength() )
2919 SAL_WARN("oox.shape", "Segments need " << nExpectedPairCount << " coordinates, but Coordinates have only " << aPairs.getLength() << " pairs.");
2920 return false;
2923 mpFS->startElementNS(XML_a, XML_custGeom);
2924 mpFS->singleElementNS(XML_a, XML_avLst);
2925 mpFS->singleElementNS(XML_a, XML_gdLst);
2926 mpFS->singleElementNS(XML_a, XML_ahLst);
2927 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "l", XML_t, "t",
2928 XML_r, "r", XML_b, "b");
2929 mpFS->startElementNS(XML_a, XML_pathLst);
2931 if ( aPathSize.hasElements() )
2933 mpFS->startElementNS( XML_a, XML_path,
2934 XML_w, OString::number(aPathSize[0].Width),
2935 XML_h, OString::number(aPathSize[0].Height) );
2937 else
2939 sal_Int32 nXMin(0);
2940 aPairs[0].First.Value >>= nXMin;
2941 sal_Int32 nXMax = nXMin;
2942 sal_Int32 nYMin(0);
2943 aPairs[0].Second.Value >>= nYMin;
2944 sal_Int32 nYMax = nYMin;
2946 for ( int j = 0; j < aPairs.getLength(); ++j )
2948 sal_Int32 nX = GetCustomGeometryPointValue(aPairs[j].First, rSdrObjCustomShape);
2949 sal_Int32 nY = GetCustomGeometryPointValue(aPairs[j].Second, rSdrObjCustomShape);
2950 if (nX < nXMin)
2951 nXMin = nX;
2952 if (nY < nYMin)
2953 nYMin = nY;
2954 if (nX > nXMax)
2955 nXMax = nX;
2956 if (nY > nYMax)
2957 nYMax = nY;
2959 mpFS->startElementNS( XML_a, XML_path,
2960 XML_w, OString::number(nXMax - nXMin),
2961 XML_h, OString::number(nYMax - nYMin) );
2965 int nPairIndex = 0;
2966 bool bOK = true;
2967 for (int j = 0; j < aSegments.getLength() && bOK; ++j)
2969 if ( aSegments[ j ].Command == drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH )
2971 mpFS->singleElementNS(XML_a, XML_close);
2973 for (int k = 0; k < aSegments[j].Count && bOK; ++k)
2975 switch( aSegments[ j ].Command )
2977 case drawing::EnhancedCustomShapeSegmentCommand::MOVETO :
2979 if (nPairIndex >= aPairs.getLength())
2980 bOK = false;
2981 else
2983 mpFS->startElementNS(XML_a, XML_moveTo);
2984 WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
2985 mpFS->endElementNS( XML_a, XML_moveTo );
2986 nPairIndex++;
2988 break;
2990 case drawing::EnhancedCustomShapeSegmentCommand::LINETO :
2992 if (nPairIndex >= aPairs.getLength())
2993 bOK = false;
2994 else
2996 mpFS->startElementNS(XML_a, XML_lnTo);
2997 WriteCustomGeometryPoint(aPairs[nPairIndex], rSdrObjCustomShape);
2998 mpFS->endElementNS( XML_a, XML_lnTo );
2999 nPairIndex++;
3001 break;
3003 case drawing::EnhancedCustomShapeSegmentCommand::CURVETO :
3005 if (nPairIndex + 2 >= aPairs.getLength())
3006 bOK = false;
3007 else
3009 mpFS->startElementNS(XML_a, XML_cubicBezTo);
3010 for( sal_uInt8 l = 0; l <= 2; ++l )
3012 WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3014 mpFS->endElementNS( XML_a, XML_cubicBezTo );
3015 nPairIndex += 3;
3017 break;
3019 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO :
3020 case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE :
3022 nPairIndex += 3;
3023 break;
3025 case drawing::EnhancedCustomShapeSegmentCommand::ARCTO :
3026 case drawing::EnhancedCustomShapeSegmentCommand::ARC :
3027 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO :
3028 case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC :
3030 nPairIndex += 4;
3031 break;
3033 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX :
3034 case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY :
3036 nPairIndex++;
3037 break;
3039 case drawing::EnhancedCustomShapeSegmentCommand::QUADRATICCURVETO :
3041 if (nPairIndex + 1 >= aPairs.getLength())
3042 bOK = false;
3043 else
3045 mpFS->startElementNS(XML_a, XML_quadBezTo);
3046 for( sal_uInt8 l = 0; l < 2; ++l )
3048 WriteCustomGeometryPoint(aPairs[nPairIndex+l], rSdrObjCustomShape);
3050 mpFS->endElementNS( XML_a, XML_quadBezTo );
3051 nPairIndex += 2;
3053 break;
3055 case drawing::EnhancedCustomShapeSegmentCommand::ARCANGLETO :
3057 nPairIndex += 2;
3058 break;
3060 default:
3061 // do nothing
3062 break;
3066 mpFS->endElementNS( XML_a, XML_path );
3067 mpFS->endElementNS( XML_a, XML_pathLst );
3068 mpFS->endElementNS( XML_a, XML_custGeom );
3069 return bOK;
3073 return false;
3076 void DrawingML::WriteCustomGeometryPoint(
3077 const drawing::EnhancedCustomShapeParameterPair& rParamPair,
3078 const SdrObjCustomShape& rSdrObjCustomShape)
3080 sal_Int32 nX = GetCustomGeometryPointValue(rParamPair.First, rSdrObjCustomShape);
3081 sal_Int32 nY = GetCustomGeometryPointValue(rParamPair.Second, rSdrObjCustomShape);
3083 mpFS->singleElementNS(XML_a, XML_pt, XML_x, OString::number(nX), XML_y, OString::number(nY));
3086 sal_Int32 DrawingML::GetCustomGeometryPointValue(
3087 const css::drawing::EnhancedCustomShapeParameter& rParam,
3088 const SdrObjCustomShape& rSdrObjCustomShape)
3090 const EnhancedCustomShape2d aCustoShape2d(const_cast< SdrObjCustomShape& >(rSdrObjCustomShape));
3091 double fValue = 0.0;
3092 aCustoShape2d.GetParameter(fValue, rParam, false, false);
3093 sal_Int32 nValue(std::lround(fValue));
3095 return nValue;
3098 void DrawingML::WritePolyPolygon( const tools::PolyPolygon& rPolyPolygon )
3100 // In case of Writer, the parent element is <wps:spPr>, and there the
3101 // <a:custGeom> element is not optional.
3102 if (rPolyPolygon.Count() < 1 && GetDocumentType() != DOCUMENT_DOCX)
3103 return;
3105 mpFS->startElementNS(XML_a, XML_custGeom);
3106 mpFS->singleElementNS(XML_a, XML_avLst);
3107 mpFS->singleElementNS(XML_a, XML_gdLst);
3108 mpFS->singleElementNS(XML_a, XML_ahLst);
3109 mpFS->singleElementNS(XML_a, XML_rect, XML_l, "0", XML_t, "0", XML_r, "r", XML_b, "b");
3111 mpFS->startElementNS( XML_a, XML_pathLst );
3113 const tools::Rectangle aRect( rPolyPolygon.GetBoundRect() );
3115 // Put all polygons of rPolyPolygon in the same path element
3116 // to subtract the overlapped areas.
3117 mpFS->startElementNS( XML_a, XML_path,
3118 XML_w, OString::number(aRect.GetWidth()),
3119 XML_h, OString::number(aRect.GetHeight()) );
3121 for( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i ++ )
3124 const tools::Polygon& rPoly = rPolyPolygon[ i ];
3126 if( rPoly.GetSize() > 0 )
3128 mpFS->startElementNS(XML_a, XML_moveTo);
3130 mpFS->singleElementNS( XML_a, XML_pt,
3131 XML_x, OString::number(rPoly[0].X() - aRect.Left()),
3132 XML_y, OString::number(rPoly[0].Y() - aRect.Top()) );
3134 mpFS->endElementNS( XML_a, XML_moveTo );
3137 for( sal_uInt16 j = 1; j < rPoly.GetSize(); j ++ )
3139 PolyFlags flags = rPoly.GetFlags(j);
3140 if( flags == PolyFlags::Control )
3142 // a:cubicBezTo can only contain 3 a:pt elements, so we need to make sure of this
3143 if( j+2 < rPoly.GetSize() && rPoly.GetFlags(j+1) == PolyFlags::Control && rPoly.GetFlags(j+2) != PolyFlags::Control )
3146 mpFS->startElementNS(XML_a, XML_cubicBezTo);
3147 for( sal_uInt8 k = 0; k <= 2; ++k )
3149 mpFS->singleElementNS(XML_a, XML_pt,
3150 XML_x, OString::number(rPoly[j+k].X() - aRect.Left()),
3151 XML_y, OString::number(rPoly[j+k].Y() - aRect.Top()));
3154 mpFS->endElementNS( XML_a, XML_cubicBezTo );
3155 j += 2;
3158 else if( flags == PolyFlags::Normal )
3160 mpFS->startElementNS(XML_a, XML_lnTo);
3161 mpFS->singleElementNS( XML_a, XML_pt,
3162 XML_x, OString::number(rPoly[j].X() - aRect.Left()),
3163 XML_y, OString::number(rPoly[j].Y() - aRect.Top()) );
3164 mpFS->endElementNS( XML_a, XML_lnTo );
3168 mpFS->endElementNS( XML_a, XML_path );
3170 mpFS->endElementNS( XML_a, XML_pathLst );
3172 mpFS->endElementNS( XML_a, XML_custGeom );
3175 void DrawingML::WriteConnectorConnections( EscherConnectorListEntry& rConnectorEntry, sal_Int32 nStartID, sal_Int32 nEndID )
3177 if( nStartID != -1 )
3179 mpFS->singleElementNS( XML_a, XML_stCxn,
3180 XML_id, OString::number(nStartID),
3181 XML_idx, OString::number(rConnectorEntry.GetConnectorRule(true)) );
3183 if( nEndID != -1 )
3185 mpFS->singleElementNS( XML_a, XML_endCxn,
3186 XML_id, OString::number(nEndID),
3187 XML_idx, OString::number(rConnectorEntry.GetConnectorRule(false)) );
3191 sal_Unicode DrawingML::SubstituteBullet( sal_Unicode cBulletId, css::awt::FontDescriptor& rFontDesc )
3193 if ( IsStarSymbol(rFontDesc.Name) )
3195 rtl_TextEncoding eCharSet = rFontDesc.CharSet;
3196 cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eCharSet, rFontDesc.Name);
3197 rFontDesc.CharSet = eCharSet;
3200 return cBulletId;
3203 sax_fastparser::FSHelperPtr DrawingML::CreateOutputStream (
3204 const OUString& sFullStream,
3205 const OUString& sRelativeStream,
3206 const Reference< XOutputStream >& xParentRelation,
3207 const char* sContentType,
3208 const char* sRelationshipType,
3209 OUString* pRelationshipId )
3211 OUString sRelationshipId;
3212 if (xParentRelation.is())
3213 sRelationshipId = GetFB()->addRelation( xParentRelation, OUString::createFromAscii( sRelationshipType), sRelativeStream );
3214 else
3215 sRelationshipId = GetFB()->addRelation( OUString::createFromAscii( sRelationshipType ), sRelativeStream );
3217 if( pRelationshipId )
3218 *pRelationshipId = sRelationshipId;
3220 sax_fastparser::FSHelperPtr p = GetFB()->openFragmentStreamWithSerializer( sFullStream, OUString::createFromAscii( sContentType ) );
3222 return p;
3225 void DrawingML::WriteFill( const Reference< XPropertySet >& xPropSet )
3227 if ( !GetProperty( xPropSet, "FillStyle" ) )
3228 return;
3229 FillStyle aFillStyle( FillStyle_NONE );
3230 xPropSet->getPropertyValue( "FillStyle" ) >>= aFillStyle;
3232 if ( aFillStyle == FillStyle_SOLID && GetProperty( xPropSet, "FillTransparence" ) )
3234 // map full transparent background to no fill
3235 sal_Int16 nVal = 0;
3236 xPropSet->getPropertyValue( "FillTransparence" ) >>= nVal;
3237 if ( nVal == 100 )
3238 aFillStyle = FillStyle_NONE;
3241 switch( aFillStyle )
3243 case FillStyle_SOLID :
3244 WriteSolidFill( xPropSet );
3245 break;
3246 case FillStyle_GRADIENT :
3247 WriteGradientFill( xPropSet );
3248 break;
3249 case FillStyle_BITMAP :
3250 WriteBlipFill( xPropSet, "FillBitmap" );
3251 break;
3252 case FillStyle_HATCH :
3253 WritePattFill( xPropSet );
3254 break;
3255 case FillStyle_NONE:
3256 mpFS->singleElementNS(XML_a, XML_noFill);
3257 break;
3258 default:
3263 void DrawingML::WriteStyleProperties( sal_Int32 nTokenId, const Sequence< PropertyValue >& aProperties )
3265 if( aProperties.hasElements() )
3267 OUString sSchemeClr;
3268 sal_uInt32 nIdx = 0;
3269 Sequence< PropertyValue > aTransformations;
3270 for( sal_Int32 i=0; i < aProperties.getLength(); ++i)
3272 if( aProperties[i].Name == "SchemeClr" )
3273 aProperties[i].Value >>= sSchemeClr;
3274 else if( aProperties[i].Name == "Idx" )
3275 aProperties[i].Value >>= nIdx;
3276 else if( aProperties[i].Name == "Transformations" )
3277 aProperties[i].Value >>= aTransformations;
3279 mpFS->startElementNS(XML_a, nTokenId, XML_idx, OString::number(nIdx));
3280 WriteColor(sSchemeClr, aTransformations);
3281 mpFS->endElementNS( XML_a, nTokenId );
3283 else
3285 // write mock <a:*Ref> tag
3286 mpFS->singleElementNS(XML_a, nTokenId, XML_idx, OString::number(0));
3290 void DrawingML::WriteShapeStyle( const Reference< XPropertySet >& xPropSet )
3292 // check existence of the grab bag
3293 if ( !GetProperty( xPropSet, "InteropGrabBag" ) )
3294 return;
3296 // extract the relevant properties from the grab bag
3297 Sequence< PropertyValue > aGrabBag;
3298 Sequence< PropertyValue > aFillRefProperties, aLnRefProperties, aEffectRefProperties;
3299 mAny >>= aGrabBag;
3300 for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i)
3302 if( aGrabBag[i].Name == "StyleFillRef" )
3303 aGrabBag[i].Value >>= aFillRefProperties;
3304 else if( aGrabBag[i].Name == "StyleLnRef" )
3305 aGrabBag[i].Value >>= aLnRefProperties;
3306 else if( aGrabBag[i].Name == "StyleEffectRef" )
3307 aGrabBag[i].Value >>= aEffectRefProperties;
3310 WriteStyleProperties( XML_lnRef, aLnRefProperties );
3311 WriteStyleProperties( XML_fillRef, aFillRefProperties );
3312 WriteStyleProperties( XML_effectRef, aEffectRefProperties );
3314 // write mock <a:fontRef>
3315 mpFS->singleElementNS(XML_a, XML_fontRef, XML_idx, "minor");
3318 void DrawingML::WriteShapeEffect( const OUString& sName, const Sequence< PropertyValue >& aEffectProps )
3320 if( !aEffectProps.hasElements() )
3321 return;
3323 // assign the proper tag and enable bContainsColor if necessary
3324 sal_Int32 nEffectToken = 0;
3325 bool bContainsColor = false;
3326 if( sName == "outerShdw" )
3328 nEffectToken = FSNS( XML_a, XML_outerShdw );
3329 bContainsColor = true;
3331 else if( sName == "innerShdw" )
3333 nEffectToken = FSNS( XML_a, XML_innerShdw );
3334 bContainsColor = true;
3336 else if( sName == "glow" )
3338 nEffectToken = FSNS( XML_a, XML_glow );
3339 bContainsColor = true;
3341 else if( sName == "softEdge" )
3342 nEffectToken = FSNS( XML_a, XML_softEdge );
3343 else if( sName == "reflection" )
3344 nEffectToken = FSNS( XML_a, XML_reflection );
3345 else if( sName == "blur" )
3346 nEffectToken = FSNS( XML_a, XML_blur );
3348 OUString sSchemeClr;
3349 ::Color nRgbClr;
3350 sal_Int32 nAlpha = MAX_PERCENT;
3351 Sequence< PropertyValue > aTransformations;
3352 sax_fastparser::FastAttributeList *aOuterShdwAttrList = FastSerializerHelper::createAttrList();
3353 sax_fastparser::XFastAttributeListRef xOuterShdwAttrList( aOuterShdwAttrList );
3354 for( sal_Int32 i=0; i < aEffectProps.getLength(); ++i )
3356 if( aEffectProps[i].Name == "Attribs" )
3358 // read tag attributes
3359 uno::Sequence< beans::PropertyValue > aOuterShdwProps;
3360 aEffectProps[i].Value >>= aOuterShdwProps;
3361 for( sal_Int32 j=0; j < aOuterShdwProps.getLength(); ++j )
3363 if( aOuterShdwProps[j].Name == "algn" )
3365 OUString sVal;
3366 aOuterShdwProps[j].Value >>= sVal;
3367 aOuterShdwAttrList->add( XML_algn, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3369 else if( aOuterShdwProps[j].Name == "blurRad" )
3371 sal_Int32 nVal = 0;
3372 aOuterShdwProps[j].Value >>= nVal;
3373 aOuterShdwAttrList->add( XML_blurRad, OString::number( nVal ).getStr() );
3375 else if( aOuterShdwProps[j].Name == "dir" )
3377 sal_Int32 nVal = 0;
3378 aOuterShdwProps[j].Value >>= nVal;
3379 aOuterShdwAttrList->add( XML_dir, OString::number( nVal ).getStr() );
3381 else if( aOuterShdwProps[j].Name == "dist" )
3383 sal_Int32 nVal = 0;
3384 aOuterShdwProps[j].Value >>= nVal;
3385 aOuterShdwAttrList->add( XML_dist, OString::number( nVal ).getStr() );
3387 else if( aOuterShdwProps[j].Name == "kx" )
3389 sal_Int32 nVal = 0;
3390 aOuterShdwProps[j].Value >>= nVal;
3391 aOuterShdwAttrList->add( XML_kx, OString::number( nVal ).getStr() );
3393 else if( aOuterShdwProps[j].Name == "ky" )
3395 sal_Int32 nVal = 0;
3396 aOuterShdwProps[j].Value >>= nVal;
3397 aOuterShdwAttrList->add( XML_ky, OString::number( nVal ).getStr() );
3399 else if( aOuterShdwProps[j].Name == "rotWithShape" )
3401 sal_Int32 nVal = 0;
3402 aOuterShdwProps[j].Value >>= nVal;
3403 aOuterShdwAttrList->add( XML_rotWithShape, OString::number( nVal ).getStr() );
3405 else if( aOuterShdwProps[j].Name == "sx" )
3407 sal_Int32 nVal = 0;
3408 aOuterShdwProps[j].Value >>= nVal;
3409 aOuterShdwAttrList->add( XML_sx, OString::number( nVal ).getStr() );
3411 else if( aOuterShdwProps[j].Name == "sy" )
3413 sal_Int32 nVal = 0;
3414 aOuterShdwProps[j].Value >>= nVal;
3415 aOuterShdwAttrList->add( XML_sy, OString::number( nVal ).getStr() );
3417 else if( aOuterShdwProps[j].Name == "rad" )
3419 sal_Int32 nVal = 0;
3420 aOuterShdwProps[j].Value >>= nVal;
3421 aOuterShdwAttrList->add( XML_rad, OString::number( nVal ).getStr() );
3423 else if( aOuterShdwProps[j].Name == "endA" )
3425 sal_Int32 nVal = 0;
3426 aOuterShdwProps[j].Value >>= nVal;
3427 aOuterShdwAttrList->add( XML_endA, OString::number( nVal ).getStr() );
3429 else if( aOuterShdwProps[j].Name == "endPos" )
3431 sal_Int32 nVal = 0;
3432 aOuterShdwProps[j].Value >>= nVal;
3433 aOuterShdwAttrList->add( XML_endPos, OString::number( nVal ).getStr() );
3435 else if( aOuterShdwProps[j].Name == "fadeDir" )
3437 sal_Int32 nVal = 0;
3438 aOuterShdwProps[j].Value >>= nVal;
3439 aOuterShdwAttrList->add( XML_fadeDir, OString::number( nVal ).getStr() );
3441 else if( aOuterShdwProps[j].Name == "stA" )
3443 sal_Int32 nVal = 0;
3444 aOuterShdwProps[j].Value >>= nVal;
3445 aOuterShdwAttrList->add( XML_stA, OString::number( nVal ).getStr() );
3447 else if( aOuterShdwProps[j].Name == "stPos" )
3449 sal_Int32 nVal = 0;
3450 aOuterShdwProps[j].Value >>= nVal;
3451 aOuterShdwAttrList->add( XML_stPos, OString::number( nVal ).getStr() );
3453 else if( aOuterShdwProps[j].Name == "grow" )
3455 sal_Int32 nVal = 0;
3456 aOuterShdwProps[j].Value >>= nVal;
3457 aOuterShdwAttrList->add( XML_grow, OString::number( nVal ).getStr() );
3461 else if(aEffectProps[i].Name == "RgbClr")
3463 aEffectProps[i].Value >>= nRgbClr;
3465 else if(aEffectProps[i].Name == "RgbClrTransparency")
3467 sal_Int32 nTransparency;
3468 if (aEffectProps[i].Value >>= nTransparency)
3469 // Calculate alpha value (see oox/source/drawingml/color.cxx : getTransparency())
3470 nAlpha = MAX_PERCENT - ( PER_PERCENT * nTransparency );
3472 else if(aEffectProps[i].Name == "SchemeClr")
3474 aEffectProps[i].Value >>= sSchemeClr;
3476 else if(aEffectProps[i].Name == "SchemeClrTransformations")
3478 aEffectProps[i].Value >>= aTransformations;
3482 if( nEffectToken > 0 )
3484 mpFS->startElement( nEffectToken, xOuterShdwAttrList );
3486 if( bContainsColor )
3488 if( sSchemeClr.isEmpty() )
3489 WriteColor( nRgbClr, nAlpha );
3490 else
3491 WriteColor( sSchemeClr, aTransformations );
3494 mpFS->endElement( nEffectToken );
3498 static sal_Int32 lcl_CalculateDist(const double dX, const double dY)
3500 return static_cast< sal_Int32 >(sqrt(dX*dX + dY*dY) * 360);
3503 static sal_Int32 lcl_CalculateDir(const double dX, const double dY)
3505 return (static_cast< sal_Int32 >(basegfx::rad2deg(atan2(dY,dX)) * 60000) + 21600000) % 21600000;
3508 void DrawingML::WriteShapeEffects( const Reference< XPropertySet >& rXPropSet )
3510 Sequence< PropertyValue > aGrabBag, aEffects, aOuterShdwProps;
3511 if( GetProperty( rXPropSet, "InteropGrabBag" ) )
3513 mAny >>= aGrabBag;
3514 for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
3516 if( aGrabBag[i].Name == "EffectProperties" )
3518 aGrabBag[i].Value >>= aEffects;
3519 for( sal_Int32 j=0; j < aEffects.getLength(); ++j )
3521 if( aEffects[j].Name == "outerShdw" )
3523 aEffects[j].Value >>= aOuterShdwProps;
3524 break;
3527 break;
3532 if( !aEffects.hasElements() )
3534 bool bHasShadow = false;
3535 if( GetProperty( rXPropSet, "Shadow" ) )
3536 mAny >>= bHasShadow;
3537 if( bHasShadow )
3539 Sequence< PropertyValue > aShadowGrabBag( 3 );
3540 Sequence< PropertyValue > aShadowAttribsGrabBag( 2 );
3542 double dX = +0.0, dY = +0.0;
3543 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
3544 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
3546 aShadowAttribsGrabBag[0].Name = "dist";
3547 aShadowAttribsGrabBag[0].Value <<= lcl_CalculateDist(dX, dY);
3548 aShadowAttribsGrabBag[1].Name = "dir";
3549 aShadowAttribsGrabBag[1].Value <<= lcl_CalculateDir(dX, dY);
3551 aShadowGrabBag[0].Name = "Attribs";
3552 aShadowGrabBag[0].Value <<= aShadowAttribsGrabBag;
3553 aShadowGrabBag[1].Name = "RgbClr";
3554 aShadowGrabBag[1].Value = rXPropSet->getPropertyValue( "ShadowColor" );
3555 aShadowGrabBag[2].Name = "RgbClrTransparency";
3556 aShadowGrabBag[2].Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
3558 mpFS->startElementNS(XML_a, XML_effectLst);
3559 WriteShapeEffect( "outerShdw", aShadowGrabBag );
3560 mpFS->endElementNS(XML_a, XML_effectLst);
3563 else
3565 for( sal_Int32 i=0; i < aOuterShdwProps.getLength(); ++i )
3567 if( aOuterShdwProps[i].Name == "Attribs" )
3569 Sequence< PropertyValue > aAttribsProps;
3570 aOuterShdwProps[i].Value >>= aAttribsProps;
3572 double dX = +0.0, dY = +0.0;
3573 rXPropSet->getPropertyValue( "ShadowXDistance" ) >>= dX;
3574 rXPropSet->getPropertyValue( "ShadowYDistance" ) >>= dY;
3576 for( sal_Int32 j=0; j < aAttribsProps.getLength(); ++j )
3578 if( aAttribsProps[j].Name == "dist" )
3580 aAttribsProps[j].Value <<= lcl_CalculateDist(dX, dY);
3582 else if( aAttribsProps[j].Name == "dir" )
3584 aAttribsProps[j].Value <<= lcl_CalculateDir(dX, dY);
3588 aOuterShdwProps[i].Value <<= aAttribsProps;
3590 else if( aOuterShdwProps[i].Name == "RgbClr" )
3592 aOuterShdwProps[i].Value = rXPropSet->getPropertyValue( "ShadowColor" );
3594 else if( aOuterShdwProps[i].Name == "RgbClrTransparency" )
3596 aOuterShdwProps[i].Value = rXPropSet->getPropertyValue( "ShadowTransparence" );
3600 mpFS->startElementNS(XML_a, XML_effectLst);
3601 for( sal_Int32 i=0; i < aEffects.getLength(); ++i )
3603 if( aEffects[i].Name == "outerShdw" )
3605 WriteShapeEffect( aEffects[i].Name, aOuterShdwProps );
3607 else
3609 Sequence< PropertyValue > aEffectProps;
3610 aEffects[i].Value >>= aEffectProps;
3611 WriteShapeEffect( aEffects[i].Name, aEffectProps );
3614 mpFS->endElementNS(XML_a, XML_effectLst);
3618 void DrawingML::WriteShape3DEffects( const Reference< XPropertySet >& xPropSet )
3620 // check existence of the grab bag
3621 if( !GetProperty( xPropSet, "InteropGrabBag" ) )
3622 return;
3624 // extract the relevant properties from the grab bag
3625 Sequence< PropertyValue > aGrabBag, aEffectProps, aLightRigProps, aShape3DProps;
3626 mAny >>= aGrabBag;
3627 for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
3629 if( aGrabBag[i].Name == "3DEffectProperties" )
3631 Sequence< PropertyValue > a3DEffectProps;
3632 aGrabBag[i].Value >>= a3DEffectProps;
3633 for( sal_Int32 j=0; j < a3DEffectProps.getLength(); ++j )
3635 if( a3DEffectProps[j].Name == "Camera" )
3636 a3DEffectProps[j].Value >>= aEffectProps;
3637 else if( a3DEffectProps[j].Name == "LightRig" )
3638 a3DEffectProps[j].Value >>= aLightRigProps;
3639 else if( a3DEffectProps[j].Name == "Shape3D" )
3640 a3DEffectProps[j].Value >>= aShape3DProps;
3642 break;
3645 if( !aEffectProps.hasElements() && !aLightRigProps.hasElements() && !aShape3DProps.hasElements() )
3646 return;
3648 bool bCameraRotationPresent = false;
3649 sax_fastparser::FastAttributeList *aCameraAttrList = FastSerializerHelper::createAttrList();
3650 sax_fastparser::XFastAttributeListRef xCameraAttrList( aCameraAttrList );
3651 sax_fastparser::FastAttributeList *aCameraRotationAttrList = FastSerializerHelper::createAttrList();
3652 sax_fastparser::XFastAttributeListRef xRotAttrList( aCameraRotationAttrList );
3653 for( sal_Int32 i=0; i < aEffectProps.getLength(); ++i )
3655 if( aEffectProps[i].Name == "prst" )
3657 OUString sVal;
3658 aEffectProps[i].Value >>= sVal;
3659 aCameraAttrList->add( XML_prst, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3661 else if( aEffectProps[i].Name == "fov" )
3663 float fVal = 0;
3664 aEffectProps[i].Value >>= fVal;
3665 aCameraAttrList->add( XML_fov, OString::number( fVal * 60000 ).getStr() );
3667 else if( aEffectProps[i].Name == "zoom" )
3669 float fVal = 1;
3670 aEffectProps[i].Value >>= fVal;
3671 aCameraAttrList->add( XML_zoom, OString::number( fVal * 100000 ).getStr() );
3673 else if( aEffectProps[i].Name == "rotLat" ||
3674 aEffectProps[i].Name == "rotLon" ||
3675 aEffectProps[i].Name == "rotRev" )
3677 sal_Int32 nVal = 0, nToken = XML_none;
3678 aEffectProps[i].Value >>= nVal;
3679 if( aEffectProps[i].Name == "rotLat" )
3680 nToken = XML_lat;
3681 else if( aEffectProps[i].Name == "rotLon" )
3682 nToken = XML_lon;
3683 else if( aEffectProps[i].Name == "rotRev" )
3684 nToken = XML_rev;
3685 aCameraRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
3686 bCameraRotationPresent = true;
3690 bool bLightRigRotationPresent = false;
3691 sax_fastparser::FastAttributeList *aLightRigAttrList = FastSerializerHelper::createAttrList();
3692 sax_fastparser::XFastAttributeListRef xLightAttrList( aLightRigAttrList );
3693 sax_fastparser::FastAttributeList *aLightRigRotationAttrList = FastSerializerHelper::createAttrList();
3694 sax_fastparser::XFastAttributeListRef xLightRotAttrList( aLightRigRotationAttrList );
3695 for( sal_Int32 i=0; i < aLightRigProps.getLength(); ++i )
3697 if( aLightRigProps[i].Name == "rig" || aLightRigProps[i].Name == "dir" )
3699 OUString sVal;
3700 sal_Int32 nToken = XML_none;
3701 aLightRigProps[i].Value >>= sVal;
3702 if( aLightRigProps[i].Name == "rig" )
3703 nToken = XML_rig;
3704 else if( aLightRigProps[i].Name == "dir" )
3705 nToken = XML_dir;
3706 aLightRigAttrList->add( nToken, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3708 else if( aLightRigProps[i].Name == "rotLat" ||
3709 aLightRigProps[i].Name == "rotLon" ||
3710 aLightRigProps[i].Name == "rotRev" )
3712 sal_Int32 nVal = 0, nToken = XML_none;
3713 aLightRigProps[i].Value >>= nVal;
3714 if( aLightRigProps[i].Name == "rotLat" )
3715 nToken = XML_lat;
3716 else if( aLightRigProps[i].Name == "rotLon" )
3717 nToken = XML_lon;
3718 else if( aLightRigProps[i].Name == "rotRev" )
3719 nToken = XML_rev;
3720 aLightRigRotationAttrList->add( nToken, OString::number( nVal ).getStr() );
3721 bLightRigRotationPresent = true;
3725 mpFS->startElementNS(XML_a, XML_scene3d);
3727 if( aEffectProps.hasElements() )
3729 mpFS->startElementNS( XML_a, XML_camera, xCameraAttrList );
3730 if( bCameraRotationPresent )
3732 mpFS->singleElementNS( XML_a, XML_rot, xRotAttrList );
3734 mpFS->endElementNS( XML_a, XML_camera );
3736 else
3738 // a:camera with Word default values - Word won't open the document if this is not present
3739 mpFS->singleElementNS(XML_a, XML_camera, XML_prst, "orthographicFront");
3742 if( aEffectProps.hasElements() )
3744 mpFS->startElementNS( XML_a, XML_lightRig, xLightAttrList );
3745 if( bLightRigRotationPresent )
3747 mpFS->singleElementNS( XML_a, XML_rot, xLightRotAttrList );
3749 mpFS->endElementNS( XML_a, XML_lightRig );
3751 else
3753 // a:lightRig with Word default values - Word won't open the document if this is not present
3754 mpFS->singleElementNS(XML_a, XML_lightRig, XML_rig, "threePt", XML_dir, "t");
3757 mpFS->endElementNS( XML_a, XML_scene3d );
3759 if( !aShape3DProps.hasElements() )
3760 return;
3762 bool bBevelTPresent = false, bBevelBPresent = false;
3763 Sequence< PropertyValue > aExtrusionColorProps, aContourColorProps;
3764 sax_fastparser::FastAttributeList *aBevelTAttrList = FastSerializerHelper::createAttrList();
3765 sax_fastparser::XFastAttributeListRef xBevelTAttrList( aBevelTAttrList );
3766 sax_fastparser::FastAttributeList *aBevelBAttrList = FastSerializerHelper::createAttrList();
3767 sax_fastparser::XFastAttributeListRef xBevelBAttrList( aBevelBAttrList );
3768 sax_fastparser::FastAttributeList *aShape3DAttrList = FastSerializerHelper::createAttrList();
3769 for( sal_Int32 i=0; i < aShape3DProps.getLength(); ++i )
3771 if( aShape3DProps[i].Name == "extrusionH" || aShape3DProps[i].Name == "contourW" || aShape3DProps[i].Name == "z" )
3773 sal_Int32 nVal = 0, nToken = XML_none;
3774 aShape3DProps[i].Value >>= nVal;
3775 if( aShape3DProps[i].Name == "extrusionH" )
3776 nToken = XML_extrusionH;
3777 else if( aShape3DProps[i].Name == "contourW" )
3778 nToken = XML_contourW;
3779 else if( aShape3DProps[i].Name == "z" )
3780 nToken = XML_z;
3781 aShape3DAttrList->add( nToken, OString::number( nVal ).getStr() );
3783 else if( aShape3DProps[i].Name == "prstMaterial" )
3785 OUString sVal;
3786 aShape3DProps[i].Value >>= sVal;
3787 aShape3DAttrList->add( XML_prstMaterial, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3789 else if( aShape3DProps[i].Name == "extrusionClr" )
3791 aShape3DProps[i].Value >>= aExtrusionColorProps;
3793 else if( aShape3DProps[i].Name == "contourClr" )
3795 aShape3DProps[i].Value >>= aContourColorProps;
3797 else if( aShape3DProps[i].Name == "bevelT" || aShape3DProps[i].Name == "bevelB" )
3799 Sequence< PropertyValue > aBevelProps;
3800 aShape3DProps[i].Value >>= aBevelProps;
3801 if ( !aBevelProps.hasElements() )
3802 continue;
3804 sax_fastparser::FastAttributeList *aBevelAttrList = nullptr;
3805 if( aShape3DProps[i].Name == "bevelT" )
3807 bBevelTPresent = true;
3808 aBevelAttrList = aBevelTAttrList;
3810 else
3812 bBevelBPresent = true;
3813 aBevelAttrList = aBevelBAttrList;
3815 for( sal_Int32 j=0; j < aBevelProps.getLength(); ++j )
3817 if( aBevelProps[j].Name == "w" || aBevelProps[j].Name == "h" )
3819 sal_Int32 nVal = 0, nToken = XML_none;
3820 aBevelProps[j].Value >>= nVal;
3821 if( aBevelProps[j].Name == "w" )
3822 nToken = XML_w;
3823 else if( aBevelProps[j].Name == "h" )
3824 nToken = XML_h;
3825 aBevelAttrList->add( nToken, OString::number( nVal ).getStr() );
3827 else if( aBevelProps[j].Name == "prst" )
3829 OUString sVal;
3830 aBevelProps[j].Value >>= sVal;
3831 aBevelAttrList->add( XML_prst, OUStringToOString( sVal, RTL_TEXTENCODING_UTF8 ).getStr() );
3838 sax_fastparser::XFastAttributeListRef xAttrList( aShape3DAttrList );
3839 mpFS->startElementNS( XML_a, XML_sp3d, xAttrList );
3840 if( bBevelTPresent )
3842 mpFS->singleElementNS( XML_a, XML_bevelT, xBevelTAttrList );
3844 if( bBevelBPresent )
3846 mpFS->singleElementNS( XML_a, XML_bevelB, xBevelBAttrList );
3848 if( aExtrusionColorProps.hasElements() )
3850 OUString sSchemeClr;
3851 ::Color nColor;
3852 sal_Int32 nTransparency(0);
3853 Sequence< PropertyValue > aColorTransformations;
3854 for( sal_Int32 i=0; i < aExtrusionColorProps.getLength(); ++i )
3856 if( aExtrusionColorProps[i].Name == "schemeClr" )
3857 aExtrusionColorProps[i].Value >>= sSchemeClr;
3858 else if( aExtrusionColorProps[i].Name == "schemeClrTransformations" )
3859 aExtrusionColorProps[i].Value >>= aColorTransformations;
3860 else if( aExtrusionColorProps[i].Name == "rgbClr" )
3861 aExtrusionColorProps[i].Value >>= nColor;
3862 else if( aExtrusionColorProps[i].Name == "rgbClrTransparency" )
3863 aExtrusionColorProps[i].Value >>= nTransparency;
3865 mpFS->startElementNS(XML_a, XML_extrusionClr);
3867 if( sSchemeClr.isEmpty() )
3868 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
3869 else
3870 WriteColor( sSchemeClr, aColorTransformations );
3872 mpFS->endElementNS( XML_a, XML_extrusionClr );
3874 if( aContourColorProps.hasElements() )
3876 OUString sSchemeClr;
3877 ::Color nColor;
3878 sal_Int32 nTransparency(0);
3879 Sequence< PropertyValue > aColorTransformations;
3880 for( sal_Int32 i=0; i < aContourColorProps.getLength(); ++i )
3882 if( aContourColorProps[i].Name == "schemeClr" )
3883 aContourColorProps[i].Value >>= sSchemeClr;
3884 else if( aContourColorProps[i].Name == "schemeClrTransformations" )
3885 aContourColorProps[i].Value >>= aColorTransformations;
3886 else if( aContourColorProps[i].Name == "rgbClr" )
3887 aContourColorProps[i].Value >>= nColor;
3888 else if( aContourColorProps[i].Name == "rgbClrTransparency" )
3889 aContourColorProps[i].Value >>= nTransparency;
3891 mpFS->startElementNS(XML_a, XML_contourClr);
3893 if( sSchemeClr.isEmpty() )
3894 WriteColor( nColor, MAX_PERCENT - ( PER_PERCENT * nTransparency ) );
3895 else
3896 WriteColor( sSchemeClr, aContourColorProps );
3898 mpFS->endElementNS( XML_a, XML_contourClr );
3900 mpFS->endElementNS( XML_a, XML_sp3d );
3903 void DrawingML::WriteArtisticEffect( const Reference< XPropertySet >& rXPropSet )
3905 if( !GetProperty( rXPropSet, "InteropGrabBag" ) )
3906 return;
3908 PropertyValue aEffect;
3909 Sequence< PropertyValue > aGrabBag;
3910 mAny >>= aGrabBag;
3911 for( sal_Int32 i=0; i < aGrabBag.getLength(); ++i )
3913 if( aGrabBag[i].Name == "ArtisticEffectProperties" )
3915 aGrabBag[i].Value >>= aEffect;
3916 break;
3919 sal_Int32 nEffectToken = ArtisticEffectProperties::getEffectToken( aEffect.Name );
3920 if( nEffectToken == XML_none )
3921 return;
3923 Sequence< PropertyValue > aAttrs;
3924 aEffect.Value >>= aAttrs;
3925 sax_fastparser::FastAttributeList *aAttrList = FastSerializerHelper::createAttrList();
3926 OString sRelId;
3927 for( sal_Int32 i=0; i < aAttrs.getLength(); ++i )
3929 sal_Int32 nToken = ArtisticEffectProperties::getEffectToken( aAttrs[i].Name );
3930 if( nToken != XML_none )
3932 sal_Int32 nVal = 0;
3933 aAttrs[i].Value >>= nVal;
3934 aAttrList->add( nToken, OString::number( nVal ).getStr() );
3936 else if( aAttrs[i].Name == "OriginalGraphic" )
3938 Sequence< PropertyValue > aGraphic;
3939 aAttrs[i].Value >>= aGraphic;
3940 Sequence< sal_Int8 > aGraphicData;
3941 OUString sGraphicId;
3942 for( sal_Int32 j=0; j < aGraphic.getLength(); ++j )
3944 if( aGraphic[j].Name == "Id" )
3945 aGraphic[j].Value >>= sGraphicId;
3946 else if( aGraphic[j].Name == "Data" )
3947 aGraphic[j].Value >>= aGraphicData;
3949 sRelId = WriteWdpPicture( sGraphicId, aGraphicData );
3953 mpFS->startElementNS(XML_a, XML_extLst);
3954 mpFS->startElementNS(XML_a, XML_ext, XML_uri, "{BEBA8EAE-BF5A-486C-A8C5-ECC9F3942E4B}");
3955 mpFS->startElementNS( XML_a14, XML_imgProps,
3956 FSNS(XML_xmlns, XML_a14), mpFB->getNamespaceURL(OOX_NS(a14)).toUtf8() );
3957 mpFS->startElementNS(XML_a14, XML_imgLayer, FSNS(XML_r, XML_embed), sRelId);
3958 mpFS->startElementNS(XML_a14, XML_imgEffect);
3960 sax_fastparser::XFastAttributeListRef xAttrList( aAttrList );
3961 mpFS->singleElementNS( XML_a14, nEffectToken, xAttrList );
3963 mpFS->endElementNS( XML_a14, XML_imgEffect );
3964 mpFS->endElementNS( XML_a14, XML_imgLayer );
3965 mpFS->endElementNS( XML_a14, XML_imgProps );
3966 mpFS->endElementNS( XML_a, XML_ext );
3967 mpFS->endElementNS( XML_a, XML_extLst );
3970 OString DrawingML::WriteWdpPicture( const OUString& rFileId, const Sequence< sal_Int8 >& rPictureData )
3972 std::map<OUString, OUString>::iterator aCachedItem = maWdpCache.find( rFileId );
3973 if( aCachedItem != maWdpCache.end() )
3974 return OUStringToOString( aCachedItem->second, RTL_TEXTENCODING_UTF8 );
3976 OUString sFileName = "media/hdphoto" + OUString::number( mnWdpImageCounter++ ) + ".wdp";
3977 Reference< XOutputStream > xOutStream = mpFB->openFragmentStream( OUStringBuffer()
3978 .appendAscii( GetComponentDir() )
3979 .append( "/" )
3980 .append( sFileName )
3981 .makeStringAndClear(),
3982 "image/vnd.ms-photo" );
3983 OUString sId;
3984 xOutStream->writeBytes( rPictureData );
3985 xOutStream->closeOutput();
3987 sId = mpFB->addRelation( mpFS->getOutputStream(),
3988 oox::getRelationship(Relationship::HDPHOTO),
3989 OUStringBuffer()
3990 .appendAscii( GetRelationCompPrefix() )
3991 .append( sFileName )
3992 .makeStringAndClear() );
3994 maWdpCache[rFileId] = sId;
3995 return OUStringToOString( sId, RTL_TEXTENCODING_UTF8 );
3998 void DrawingML::WriteDiagram(const css::uno::Reference<css::drawing::XShape>& rXShape, int nDiagramId)
4000 uno::Reference<beans::XPropertySet> xPropSet(rXShape, uno::UNO_QUERY);
4002 uno::Reference<xml::dom::XDocument> dataDom;
4003 uno::Reference<xml::dom::XDocument> layoutDom;
4004 uno::Reference<xml::dom::XDocument> styleDom;
4005 uno::Reference<xml::dom::XDocument> colorDom;
4006 uno::Reference<xml::dom::XDocument> drawingDom;
4007 uno::Sequence<uno::Sequence<uno::Any>> xDataRelSeq;
4008 uno::Sequence<uno::Any> diagramDrawing;
4010 // retrieve the doms from the GrabBag
4011 uno::Sequence<beans::PropertyValue> propList;
4012 xPropSet->getPropertyValue(UNO_NAME_MISC_OBJ_INTEROPGRABBAG) >>= propList;
4013 for (sal_Int32 nProp = 0; nProp < propList.getLength(); ++nProp)
4015 OUString propName = propList[nProp].Name;
4016 if (propName == "OOXData")
4017 propList[nProp].Value >>= dataDom;
4018 else if (propName == "OOXLayout")
4019 propList[nProp].Value >>= layoutDom;
4020 else if (propName == "OOXStyle")
4021 propList[nProp].Value >>= styleDom;
4022 else if (propName == "OOXColor")
4023 propList[nProp].Value >>= colorDom;
4024 else if (propName == "OOXDrawing")
4026 propList[nProp].Value >>= diagramDrawing;
4027 diagramDrawing[0]
4028 >>= drawingDom; // if there is OOXDrawing property then set drawingDom here only.
4030 else if (propName == "OOXDiagramDataRels")
4031 propList[nProp].Value >>= xDataRelSeq;
4034 // check that we have the 4 mandatory XDocuments
4035 // if not, there was an error importing and we won't output anything
4036 if (!dataDom.is() || !layoutDom.is() || !styleDom.is() || !colorDom.is())
4037 return;
4039 // generate an unique id
4040 sax_fastparser::FastAttributeList* pDocPrAttrList
4041 = sax_fastparser::FastSerializerHelper::createAttrList();
4042 pDocPrAttrList->add(XML_id, OString::number(nDiagramId).getStr());
4043 OUString sName = "Diagram" + OUString::number(nDiagramId);
4044 pDocPrAttrList->add(XML_name, OUStringToOString(sName, RTL_TEXTENCODING_UTF8).getStr());
4045 sax_fastparser::XFastAttributeListRef xDocPrAttrListRef(pDocPrAttrList);
4047 if (GetDocumentType() == DOCUMENT_DOCX)
4049 mpFS->singleElementNS(XML_wp, XML_docPr, xDocPrAttrListRef);
4050 mpFS->singleElementNS(XML_wp, XML_cNvGraphicFramePr);
4052 mpFS->startElementNS(
4053 XML_a, XML_graphic, FSNS(XML_xmlns, XML_a),
4054 mpFB->getNamespaceURL(OOX_NS(dml)).toUtf8());
4056 else
4058 mpFS->startElementNS(XML_p, XML_nvGraphicFramePr);
4060 mpFS->singleElementNS(XML_p, XML_cNvPr, xDocPrAttrListRef);
4061 mpFS->singleElementNS(XML_p, XML_cNvGraphicFramePr);
4063 mpFS->startElementNS(XML_p, XML_nvPr);
4064 mpFS->startElementNS(XML_p, XML_extLst);
4065 // change tracking extension - required in PPTX
4066 mpFS->startElementNS(XML_p, XML_ext, XML_uri, "{D42A27DB-BD31-4B8C-83A1-F6EECF244321}");
4067 mpFS->singleElementNS(XML_p14, XML_modId,
4068 FSNS(XML_xmlns, XML_p14), mpFB->getNamespaceURL(OOX_NS(p14)).toUtf8(),
4069 XML_val,
4070 OString::number(comphelper::rng::uniform_uint_distribution(1, SAL_MAX_UINT32)));
4071 mpFS->endElementNS(XML_p, XML_ext);
4072 mpFS->endElementNS(XML_p, XML_extLst);
4073 mpFS->endElementNS(XML_p, XML_nvPr);
4075 mpFS->endElementNS(XML_p, XML_nvGraphicFramePr);
4077 awt::Point aPos = rXShape->getPosition();
4078 awt::Size aSize = rXShape->getSize();
4079 WriteTransformation(tools::Rectangle(Point(aPos.X, aPos.Y), Size(aSize.Width, aSize.Height)),
4080 XML_p, false, false, 0, false);
4082 mpFS->startElementNS(XML_a, XML_graphic);
4085 mpFS->startElementNS(XML_a, XML_graphicData, XML_uri,
4086 "http://schemas.openxmlformats.org/drawingml/2006/diagram");
4088 OUString sRelationCompPrefix = OUString::createFromAscii(GetRelationCompPrefix());
4090 // add data relation
4091 OUString dataFileName = "diagrams/data" + OUString::number(nDiagramId) + ".xml";
4092 OString dataRelId = OUStringToOString(
4093 mpFB->addRelation(mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDATA),
4094 sRelationCompPrefix + dataFileName),
4095 RTL_TEXTENCODING_UTF8);
4097 // add layout relation
4098 OUString layoutFileName = "diagrams/layout" + OUString::number(nDiagramId) + ".xml";
4099 OString layoutRelId
4100 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4101 oox::getRelationship(Relationship::DIAGRAMLAYOUT),
4102 sRelationCompPrefix + layoutFileName),
4103 RTL_TEXTENCODING_UTF8);
4105 // add style relation
4106 OUString styleFileName = "diagrams/quickStyle" + OUString::number(nDiagramId) + ".xml";
4107 OString styleRelId
4108 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4109 oox::getRelationship(Relationship::DIAGRAMQUICKSTYLE),
4110 sRelationCompPrefix + styleFileName),
4111 RTL_TEXTENCODING_UTF8);
4113 // add color relation
4114 OUString colorFileName = "diagrams/colors" + OUString::number(nDiagramId) + ".xml";
4115 OString colorRelId
4116 = OUStringToOString(mpFB->addRelation(mpFS->getOutputStream(),
4117 oox::getRelationship(Relationship::DIAGRAMCOLORS),
4118 sRelationCompPrefix + colorFileName),
4119 RTL_TEXTENCODING_UTF8);
4121 OUString drawingFileName;
4122 if (drawingDom.is())
4124 // add drawing relation
4125 drawingFileName = "diagrams/drawing" + OUString::number(nDiagramId) + ".xml";
4126 OUString drawingRelId = mpFB->addRelation(
4127 mpFS->getOutputStream(), oox::getRelationship(Relationship::DIAGRAMDRAWING),
4128 sRelationCompPrefix + drawingFileName);
4130 // the data dom contains a reference to the drawing relation. We need to update it with the new generated
4131 // relation value before writing the dom to a file
4133 // Get the dsp:damaModelExt node from the dom
4134 uno::Reference<xml::dom::XNodeList> nodeList = dataDom->getElementsByTagNameNS(
4135 "http://schemas.microsoft.com/office/drawing/2008/diagram", "dataModelExt");
4137 // There must be one element only so get it
4138 uno::Reference<xml::dom::XNode> node = nodeList->item(0);
4140 // Get the list of attributes of the node
4141 uno::Reference<xml::dom::XNamedNodeMap> nodeMap = node->getAttributes();
4143 // Get the node with the relId attribute and set its new value
4144 uno::Reference<xml::dom::XNode> relIdNode = nodeMap->getNamedItem("relId");
4145 relIdNode->setNodeValue(drawingRelId);
4148 mpFS->singleElementNS(XML_dgm, XML_relIds,
4149 FSNS(XML_xmlns, XML_dgm), mpFB->getNamespaceURL(OOX_NS(dmlDiagram)).toUtf8(),
4150 FSNS(XML_xmlns, XML_r), mpFB->getNamespaceURL(OOX_NS(officeRel)).toUtf8(),
4151 FSNS(XML_r, XML_dm), dataRelId, FSNS(XML_r, XML_lo), layoutRelId,
4152 FSNS(XML_r, XML_qs), styleRelId, FSNS(XML_r, XML_cs), colorRelId);
4154 mpFS->endElementNS(XML_a, XML_graphicData);
4155 mpFS->endElementNS(XML_a, XML_graphic);
4157 uno::Reference<xml::sax::XSAXSerializable> serializer;
4158 uno::Reference<xml::sax::XWriter> writer
4159 = xml::sax::Writer::create(comphelper::getProcessComponentContext());
4161 OUString sDir = OUString::createFromAscii(GetComponentDir());
4163 // write data file
4164 serializer.set(dataDom, uno::UNO_QUERY);
4165 uno::Reference<io::XOutputStream> xDataOutputStream = mpFB->openFragmentStream(
4166 sDir + "/" + dataFileName,
4167 "application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml");
4168 writer->setOutputStream(xDataOutputStream);
4169 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4170 uno::Sequence<beans::StringPair>());
4172 // write the associated Images and rels for data file
4173 writeDiagramRels(xDataRelSeq, xDataOutputStream, "OOXDiagramDataRels", nDiagramId);
4175 // write layout file
4176 serializer.set(layoutDom, uno::UNO_QUERY);
4177 writer->setOutputStream(mpFB->openFragmentStream(
4178 sDir + "/" + layoutFileName,
4179 "application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml"));
4180 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4181 uno::Sequence<beans::StringPair>());
4183 // write style file
4184 serializer.set(styleDom, uno::UNO_QUERY);
4185 writer->setOutputStream(mpFB->openFragmentStream(
4186 sDir + "/" + styleFileName,
4187 "application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml"));
4188 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4189 uno::Sequence<beans::StringPair>());
4191 // write color file
4192 serializer.set(colorDom, uno::UNO_QUERY);
4193 writer->setOutputStream(mpFB->openFragmentStream(
4194 sDir + "/" + colorFileName,
4195 "application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml"));
4196 serializer->serialize(uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4197 uno::Sequence<beans::StringPair>());
4199 // write drawing file
4200 if (drawingDom.is())
4202 serializer.set(drawingDom, uno::UNO_QUERY);
4203 uno::Reference<io::XOutputStream> xDrawingOutputStream = mpFB->openFragmentStream(
4204 sDir + "/" + drawingFileName, "application/vnd.ms-office.drawingml.diagramDrawing+xml");
4205 writer->setOutputStream(xDrawingOutputStream);
4206 serializer->serialize(
4207 uno::Reference<xml::sax::XDocumentHandler>(writer, uno::UNO_QUERY_THROW),
4208 uno::Sequence<beans::StringPair>());
4210 // write the associated Images and rels for drawing file
4211 uno::Sequence<uno::Sequence<uno::Any>> xDrawingRelSeq;
4212 diagramDrawing[1] >>= xDrawingRelSeq;
4213 writeDiagramRels(xDrawingRelSeq, xDrawingOutputStream, "OOXDiagramDrawingRels", nDiagramId);
4217 void DrawingML::writeDiagramRels(const uno::Sequence<uno::Sequence<uno::Any>>& xRelSeq,
4218 const uno::Reference<io::XOutputStream>& xOutStream,
4219 const OUString& sGrabBagProperyName, int nDiagramId)
4221 // add image relationships of OOXData, OOXDiagram
4222 OUString sType(oox::getRelationship(Relationship::IMAGE));
4223 uno::Reference<xml::sax::XWriter> xWriter
4224 = xml::sax::Writer::create(comphelper::getProcessComponentContext());
4225 xWriter->setOutputStream(xOutStream);
4227 // retrieve the relationships from Sequence
4228 for (sal_Int32 j = 0; j < xRelSeq.getLength(); j++)
4230 // diagramDataRelTuple[0] => RID,
4231 // diagramDataRelTuple[1] => xInputStream
4232 // diagramDataRelTuple[2] => extension
4233 uno::Sequence<uno::Any> diagramDataRelTuple = xRelSeq[j];
4235 OUString sRelId;
4236 OUString sExtension;
4237 diagramDataRelTuple[0] >>= sRelId;
4238 diagramDataRelTuple[2] >>= sExtension;
4239 OUString sContentType;
4240 if (sExtension.equalsIgnoreAsciiCase(".WMF"))
4241 sContentType = "image/x-wmf";
4242 else
4243 sContentType = "image/" + sExtension.copy(1);
4244 sRelId = sRelId.copy(3);
4246 StreamDataSequence dataSeq;
4247 diagramDataRelTuple[1] >>= dataSeq;
4248 uno::Reference<io::XInputStream> dataImagebin(
4249 new ::comphelper::SequenceInputStream(dataSeq));
4251 //nDiagramId is used to make the name unique irrespective of the number of smart arts.
4252 OUString sFragment = "media/" + sGrabBagProperyName + OUString::number(nDiagramId) + "_"
4253 + OUString::number(j) + sExtension;
4255 PropertySet aProps(xOutStream);
4256 aProps.setAnyProperty(PROP_RelId, uno::makeAny(sRelId.toInt32()));
4258 mpFB->addRelation(xOutStream, sType, "../" + sFragment);
4260 OUString sDir = OUString::createFromAscii(GetComponentDir());
4261 uno::Reference<io::XOutputStream> xBinOutStream
4262 = mpFB->openFragmentStream(sDir + "/" + sFragment, sContentType);
4266 comphelper::OStorageHelper::CopyInputToOutput(dataImagebin, xBinOutStream);
4268 catch (const uno::Exception& rException)
4270 SAL_WARN("oox.drawingml", "DrawingML::writeDiagramRels Failed to copy grabbaged Image: "
4271 << rException);
4273 dataImagebin->closeInput();
4280 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */