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