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 <o3tl/any.hxx>
21 #include <oox/token/tokens.hxx>
22 #include "epptooxml.hxx"
23 #include <sax/fshelper.hxx>
24 #include <sal/log.hxx>
26 #include <com/sun/star/animations/AnimationAdditiveMode.hpp>
27 #include <com/sun/star/animations/AnimationCalcMode.hpp>
28 #include <com/sun/star/animations/AnimationFill.hpp>
29 #include <com/sun/star/animations/AnimationNodeType.hpp>
30 #include <com/sun/star/animations/AnimationRestart.hpp>
31 #include <com/sun/star/animations/AnimationTransformType.hpp>
32 #include <com/sun/star/animations/AnimationValueType.hpp>
33 #include <com/sun/star/animations/AnimationColorSpace.hpp>
34 #include <com/sun/star/animations/Event.hpp>
35 #include <com/sun/star/animations/EventTrigger.hpp>
36 #include <com/sun/star/animations/Timing.hpp>
37 #include <com/sun/star/animations/ValuePair.hpp>
38 #include <com/sun/star/animations/XAnimateMotion.hpp>
39 #include <com/sun/star/animations/XAnimateTransform.hpp>
40 #include <com/sun/star/animations/XAnimationNode.hpp>
41 #include <com/sun/star/animations/XAnimationNodeSupplier.hpp>
42 #include <com/sun/star/animations/XAnimateColor.hpp>
43 #include <com/sun/star/animations/XCommand.hpp>
44 #include <com/sun/star/animations/XAudio.hpp>
45 #include <com/sun/star/animations/XTransitionFilter.hpp>
46 #include <com/sun/star/animations/XIterateContainer.hpp>
47 #include <com/sun/star/container/XEnumerationAccess.hpp>
48 #include <com/sun/star/presentation/EffectCommands.hpp>
49 #include <com/sun/star/presentation/EffectNodeType.hpp>
50 #include <com/sun/star/presentation/EffectPresetClass.hpp>
51 #include <com/sun/star/presentation/ParagraphTarget.hpp>
52 #include <com/sun/star/presentation/TextAnimationType.hpp>
53 #include <com/sun/star/text/XSimpleText.hpp>
54 #include <com/sun/star/drawing/XShape.hpp>
55 #include <oox/export/utils.hxx>
56 #include <oox/ppt/pptfilterhelpers.hxx>
57 #include <basegfx/polygon/b2dpolypolygontools.hxx>
59 #include "pptexanimations.hxx"
60 #include "pptx-animations.hxx"
61 #include "../ppt/pptanimations.hxx"
63 using namespace ::com::sun::star::animations
;
64 using namespace ::com::sun::star::container
;
65 using namespace ::com::sun::star::presentation
;
66 using namespace ::com::sun::star::uno
;
67 using namespace ::ppt
;
68 using namespace oox::drawingml
;
69 using namespace oox::core
;
72 using ::com::sun::star::beans::NamedValue
;
73 using ::com::sun::star::drawing::XDrawPage
;
74 using ::com::sun::star::drawing::XShape
;
75 using ::com::sun::star::text::XSimpleText
;
76 using ::sax_fastparser::FSHelperPtr
;
80 void WriteAnimationProperty(const FSHelperPtr
& pFS
, const Any
& rAny
, sal_Int32 nToken
= 0)
90 if ((aPair
.First
>>= x
) && (aPair
.Second
>>= y
))
94 // MS needs ending values but we have offset values.
98 pFS
->singleElementNS(XML_p
, nToken
, XML_x
, OString::number(x
* 100000), XML_y
,
99 OString::number(y
* 100000));
107 TypeClass aClass
= rAny
.getValueType().getTypeClass();
110 && (aClass
== TypeClass_LONG
|| aClass
== TypeClass_DOUBLE
|| aClass
== TypeClass_STRING
);
113 pFS
->startElementNS(XML_p
, nToken
);
115 switch (rAny
.getValueType().getTypeClass())
119 pFS
->singleElementNS(XML_a
, XML_srgbClr
, XML_val
, I32SHEX(nRgb
));
121 case TypeClass_DOUBLE
:
123 pFS
->singleElementNS(XML_p
, XML_fltVal
, XML_val
, OString::number(fDouble
));
125 case TypeClass_STRING
:
126 pFS
->singleElementNS(XML_p
, XML_strVal
, XML_val
,
127 (*o3tl::doAccess
<OUString
>(rAny
)).toUtf8());
134 pFS
->endElementNS(XML_p
, nToken
);
137 void WriteAnimateColorColor(const FSHelperPtr
& pFS
, const Any
& rAny
, sal_Int32 nToken
)
139 if (!rAny
.hasValue())
142 sal_Int32 nColor
= 0;
145 pFS
->startElementNS(XML_p
, nToken
);
147 if (nToken
== XML_by
)
149 // CT_TLByRgbColorTransform
150 SAL_WARN("sd.eppt", "Export p:rgb in p:by of animClr isn't implemented yet.");
155 pFS
->singleElementNS(XML_a
, XML_srgbClr
, XML_val
, I32SHEX(nColor
));
158 pFS
->endElementNS(XML_p
, nToken
);
161 Sequence
<double> aHSL(3);
162 if (!(rAny
>>= aHSL
))
165 pFS
->startElementNS(XML_p
, nToken
);
167 if (nToken
== XML_by
)
169 // CT_TLByHslColorTransform
170 pFS
->singleElementNS(XML_p
, XML_hsl
, XML_h
, OString::number(aHSL
[0] * 60000), // ST_Angel
171 XML_s
, OString::number(aHSL
[1] * 100000), XML_l
,
172 OString::number(aHSL
[2] * 100000));
177 SAL_WARN("sd.eppt", "Export p:hsl in p:from or p:to of animClr isn't implemented yet.");
180 pFS
->endElementNS(XML_p
, nToken
);
183 void WriteAnimateTo(const FSHelperPtr
& pFS
, const Any
& rValue
, const OUString
& rAttributeName
)
185 if (!rValue
.hasValue())
188 SAL_INFO("sd.eppt", "to attribute name: " << rAttributeName
.toUtf8());
190 WriteAnimationProperty(pFS
, AnimationExporter::convertAnimateValue(rValue
, rAttributeName
),
194 void WriteAnimateValues(const FSHelperPtr
& pFS
, const Reference
<XAnimate
>& rXAnimate
)
196 const Sequence
<double> aKeyTimes
= rXAnimate
->getKeyTimes();
197 if (!aKeyTimes
.hasElements())
199 const Sequence
<Any
> aValues
= rXAnimate
->getValues();
200 const OUString
& sFormula
= rXAnimate
->getFormula();
201 const OUString
& rAttributeName
= rXAnimate
->getAttributeName();
203 SAL_INFO("sd.eppt", "animate values, formula: " << sFormula
.toUtf8());
205 pFS
->startElementNS(XML_p
, XML_tavLst
);
207 for (int i
= 0; i
< aKeyTimes
.getLength(); i
++)
209 SAL_INFO("sd.eppt", "animate value " << i
<< ": " << aKeyTimes
[i
]);
210 if (aValues
[i
].hasValue())
212 pFS
->startElementNS(XML_p
, XML_tav
, XML_fmla
,
213 sFormula
.isEmpty() ? nullptr : sFormula
.toUtf8().getStr(), XML_tm
,
214 OString::number(static_cast<sal_Int32
>(aKeyTimes
[i
] * 100000.0)));
215 pFS
->startElementNS(XML_p
, XML_val
);
217 if (aValues
[i
] >>= aPair
)
219 WriteAnimationProperty(
220 pFS
, AnimationExporter::convertAnimateValue(aPair
.First
, rAttributeName
));
221 WriteAnimationProperty(
222 pFS
, AnimationExporter::convertAnimateValue(aPair
.Second
, rAttributeName
));
225 WriteAnimationProperty(
226 pFS
, AnimationExporter::convertAnimateValue(aValues
[i
], rAttributeName
));
228 pFS
->endElementNS(XML_p
, XML_val
);
229 pFS
->endElementNS(XML_p
, XML_tav
);
233 pFS
->endElementNS(XML_p
, XML_tavLst
);
236 // Write condition list ( either prevCondlst or nextCondlst ) of Seq.
237 void WriteAnimationCondListForSeq(const FSHelperPtr
& pFS
, sal_Int32 nToken
)
239 const char* pEvent
= (nToken
== XML_prevCondLst
) ? "onPrev" : "onNext";
241 pFS
->startElementNS(XML_p
, nToken
);
242 pFS
->startElementNS(XML_p
, XML_cond
, XML_evt
, pEvent
);
243 pFS
->startElementNS(XML_p
, XML_tgtEl
);
244 pFS
->singleElementNS(XML_p
, XML_sldTgt
);
245 pFS
->endElementNS(XML_p
, XML_tgtEl
);
246 pFS
->endElementNS(XML_p
, XML_cond
);
247 pFS
->endElementNS(XML_p
, nToken
);
250 const char* convertEventTrigger(sal_Int16 nTrigger
)
252 const char* pEvent
= nullptr;
255 case EventTrigger::ON_NEXT
:
258 case EventTrigger::ON_PREV
:
261 case EventTrigger::BEGIN_EVENT
:
264 case EventTrigger::END_EVENT
:
267 case EventTrigger::ON_BEGIN
:
270 case EventTrigger::ON_END
:
273 case EventTrigger::ON_CLICK
:
276 case EventTrigger::ON_DBL_CLICK
:
277 pEvent
= "onDblClick";
279 case EventTrigger::ON_STOP_AUDIO
:
280 pEvent
= "onStopAudio";
282 case EventTrigger::ON_MOUSE_ENTER
:
283 pEvent
= "onMouseOver"; // not exact?
285 case EventTrigger::ON_MOUSE_LEAVE
:
286 pEvent
= "onMouseOut";
292 void WriteAnimationAttributeName(const FSHelperPtr
& pFS
, const OUString
& rAttributeName
)
294 if (rAttributeName
.isEmpty())
297 pFS
->startElementNS(XML_p
, XML_attrNameLst
);
299 SAL_INFO("sd.eppt", "write attribute name: " << rAttributeName
.toUtf8());
301 if (rAttributeName
== "X;Y")
303 pFS
->startElementNS(XML_p
, XML_attrName
);
304 pFS
->writeEscaped("ppt_x");
305 pFS
->endElementNS(XML_p
, XML_attrName
);
307 pFS
->startElementNS(XML_p
, XML_attrName
);
308 pFS
->writeEscaped("ppt_y");
309 pFS
->endElementNS(XML_p
, XML_attrName
);
313 const oox::ppt::ImplAttributeNameConversion
* attrConv
314 = oox::ppt::getAttributeConversionList();
315 const char* pAttribute
= nullptr;
317 while (attrConv
->mpAPIName
!= nullptr)
319 if (rAttributeName
.equalsAscii(attrConv
->mpAPIName
))
321 pAttribute
= attrConv
->mpMSName
;
329 pFS
->startElementNS(XML_p
, XML_attrName
);
330 pFS
->writeEscaped(pAttribute
);
331 pFS
->endElementNS(XML_p
, XML_attrName
);
335 SAL_WARN("sd.eppt", "unhandled animation attribute name: " << rAttributeName
);
339 pFS
->endElementNS(XML_p
, XML_attrNameLst
);
342 bool isValidTarget(const Any
& rTarget
)
344 Reference
<XShape
> xShape
;
346 if ((rTarget
>>= xShape
) && xShape
.is())
349 ParagraphTarget aParagraphTarget
;
351 return (rTarget
>>= aParagraphTarget
) && aParagraphTarget
.Shape
.is();
354 /// extract ooxml node type from a XAnimationNode.
355 sal_Int32
extractNodeType(const Reference
<XAnimationNode
>& rXNode
)
357 sal_Int16 nType
= rXNode
->getType();
358 sal_Int32 xmlNodeType
= -1;
361 case AnimationNodeType::ITERATE
:
362 case AnimationNodeType::PAR
:
363 xmlNodeType
= XML_par
;
365 case AnimationNodeType::SEQ
:
366 xmlNodeType
= XML_seq
;
368 case AnimationNodeType::ANIMATE
:
369 xmlNodeType
= XML_anim
;
371 case AnimationNodeType::ANIMATEMOTION
:
372 xmlNodeType
= XML_animMotion
;
374 case AnimationNodeType::ANIMATETRANSFORM
:
376 Reference
<XAnimateTransform
> xTransform(rXNode
, UNO_QUERY
);
379 if (xTransform
->getTransformType() == AnimationTransformType::SCALE
)
380 xmlNodeType
= XML_animScale
;
381 else if (xTransform
->getTransformType() == AnimationTransformType::ROTATE
)
382 xmlNodeType
= XML_animRot
;
386 case AnimationNodeType::ANIMATECOLOR
:
387 xmlNodeType
= XML_animClr
;
389 case AnimationNodeType::SET
:
390 xmlNodeType
= XML_set
;
392 case AnimationNodeType::TRANSITIONFILTER
:
393 xmlNodeType
= XML_animEffect
;
395 case AnimationNodeType::COMMAND
:
396 xmlNodeType
= XML_cmd
;
398 case AnimationNodeType::AUDIO
:
399 xmlNodeType
= XML_audio
;
402 SAL_WARN("sd.eppt", "unhandled animation node: " << nType
);
408 /// Convert AnimationRestart to ST_TLTimeNodeRestartType value.
409 const char* convertAnimationRestart(sal_Int16 nRestart
)
411 const char* pRestart
= nullptr;
414 case AnimationRestart::ALWAYS
:
417 case AnimationRestart::WHEN_NOT_ACTIVE
:
418 pRestart
= "whenNotActive";
420 case AnimationRestart::NEVER
:
427 /// Convert EffectNodeType to ST_TLTimeNodeType
428 const char* convertEffectNodeType(sal_Int16 nType
)
430 const char* pNodeType
= nullptr;
433 case EffectNodeType::TIMING_ROOT
:
434 pNodeType
= "tmRoot";
436 case EffectNodeType::MAIN_SEQUENCE
:
437 pNodeType
= "mainSeq";
439 case EffectNodeType::ON_CLICK
:
440 pNodeType
= "clickEffect";
442 case EffectNodeType::AFTER_PREVIOUS
:
443 pNodeType
= "afterEffect";
445 case EffectNodeType::WITH_PREVIOUS
:
446 pNodeType
= "withEffect";
448 case EffectNodeType::INTERACTIVE_SEQUENCE
:
449 pNodeType
= "interactiveSeq";
455 /// Convert EffectPresetClass to ST_TLTimeNodePresetClassType
456 const char* convertEffectPresetClass(sal_Int16 nPresetClass
)
458 const char* pPresetClass
= nullptr;
459 switch (nPresetClass
)
461 case EffectPresetClass::ENTRANCE
:
462 pPresetClass
= "entr";
464 case EffectPresetClass::EXIT
:
465 pPresetClass
= "exit";
467 case EffectPresetClass::EMPHASIS
:
468 pPresetClass
= "emph";
470 case EffectPresetClass::MOTIONPATH
:
471 pPresetClass
= "path";
473 case EffectPresetClass::OLEACTION
:
474 pPresetClass
= "verb"; // ?
476 case EffectPresetClass::MEDIACALL
:
477 pPresetClass
= "mediacall";
483 /// convert AnimationFill to ST_TLTimeNodeFillType.
484 const char* convertAnimationFill(sal_Int16 nFill
)
486 const char* pFill
= nullptr;
489 case AnimationFill::FREEZE
:
492 case AnimationFill::HOLD
:
495 case AnimationFill::REMOVE
:
498 case AnimationFill::TRANSITION
:
499 pFill
= "transition";
505 /// Convert TextAnimationType to ST_IterateType.
506 const char* convertTextAnimationType(sal_Int16 nType
)
508 const char* sType
= nullptr;
511 case TextAnimationType::BY_PARAGRAPH
:
514 case TextAnimationType::BY_LETTER
:
517 case TextAnimationType::BY_WORD
:
527 typedef std::unique_ptr
<NodeContext
> NodeContextPtr
;
531 const Reference
<XAnimationNode
> mxNode
;
532 const bool mbMainSeqChild
;
534 std::vector
<NodeContextPtr
> maChildNodes
;
535 // if the node has valid target or contains at least one valid target.
538 // Attributes initialized from mxNode->getUserData().
539 sal_Int16 mnEffectNodeType
;
540 sal_Int16 mnEffectPresetClass
;
541 OUString msEffectPresetId
;
542 OUString msEffectPresetSubType
;
544 /// constructor helper for initializing user data.
547 /// constructor helper to initialize maChildNodes.
548 /// return true if at least one childnode is valid.
549 bool initChildNodes();
551 /// constructor helper to initialize mbValid
552 void initValid(bool bHasValidChild
, bool bIsIterateChild
);
555 NodeContext(const Reference
<XAnimationNode
>& xNode
, bool bMainSeqChild
, bool bIsIterateChild
);
556 const Reference
<XAnimationNode
>& getNode() const { return mxNode
; }
557 bool isMainSeqChild() const { return mbMainSeqChild
; }
558 sal_Int16
getEffectNodeType() const { return mnEffectNodeType
; }
559 sal_Int16
getEffectPresetClass() const { return mnEffectPresetClass
; }
560 const OUString
& getEffectPresetId() const { return msEffectPresetId
; }
561 const OUString
& getEffectPresetSubType() const { return msEffectPresetSubType
; }
562 bool isValid() const { return mbValid
; }
563 const std::vector
<NodeContextPtr
>& getChildNodes() const { return maChildNodes
; };
564 Any
getCondition(bool bBegin
) const;
571 Reference
<XShape
> mxShape
;
572 Reference
<XAnimationNode
> mxNode
;
574 Cond(const Any
& rAny
, bool bIsMainSeqChild
);
576 bool isValid() const { return msDelay
.getLength() || mpEvent
; }
577 const char* getDelay() const { return msDelay
.getLength() ? msDelay
.getStr() : nullptr; }
580 Cond::Cond(const Any
& rAny
, bool bIsMainSeqChild
)
583 bool bHasFDelay
= false;
588 if (rAny
>>= eTiming
)
590 if (eTiming
== Timing_INDEFINITE
)
591 msDelay
= "indefinite";
593 else if (rAny
>>= aEvent
)
595 if (aEvent
.Trigger
== EventTrigger::ON_NEXT
&& bIsMainSeqChild
)
596 msDelay
= "indefinite";
599 mpEvent
= convertEventTrigger(aEvent
.Trigger
);
600 if (!(aEvent
.Source
>>= mxShape
))
601 aEvent
.Source
>>= mxNode
;
603 if (aEvent
.Offset
>>= fDelay
)
607 else if (rAny
>>= fDelay
)
612 sal_Int32 nDelay
= static_cast<sal_uInt32
>(fDelay
* 1000.0);
613 msDelay
= OString::number(nDelay
);
617 class PPTXAnimationExport
619 void WriteAnimationNode(const NodeContextPtr
& pContext
);
620 void WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType
);
621 void WriteAnimationNodeAnimateInside(bool bSimple
, bool bWriteTo
= true);
622 void WriteAnimationNodeSeq();
623 void WriteAnimationNodeEffect();
624 void WriteAnimationNodeCommand();
625 void WriteAnimationNodeAudio();
626 void WriteAnimationNodeCommonPropsStart();
627 void WriteAnimationTarget(const Any
& rTarget
);
628 void WriteAnimationCondList(const Any
& rAny
, sal_Int32 nToken
);
629 void WriteAnimationCond(const Cond
& rCond
);
630 bool isMainSeqChild() const;
631 const Reference
<XAnimationNode
>& getCurrentNode() const;
633 PowerPointExport
& mrPowerPointExport
;
634 const FSHelperPtr
& mpFS
;
635 const NodeContext
* mpContext
;
637 std::map
<Reference
<XAnimationNode
>, sal_Int32
> maAnimationNodeIdMap
;
638 sal_Int32
GetNextAnimationNodeId(const Reference
<XAnimationNode
>& rNode
);
639 sal_Int32
GetAnimationNodeId(const Reference
<XAnimationNode
>& rNode
);
642 PPTXAnimationExport(PowerPointExport
& rExport
, const FSHelperPtr
& pFS
);
643 void WriteAnimations(const Reference
<XDrawPage
>& rXDrawPage
);
651 void WriteAnimations(const FSHelperPtr
& pFS
, const Reference
<XDrawPage
>& rXDrawPage
,
652 PowerPointExport
& rExport
)
654 PPTXAnimationExport
aAnimationExport(rExport
, pFS
);
655 aAnimationExport
.WriteAnimations(rXDrawPage
);
660 PPTXAnimationExport::PPTXAnimationExport(PowerPointExport
& rExport
, const FSHelperPtr
& pFS
)
661 : mrPowerPointExport(rExport
)
667 bool PPTXAnimationExport::isMainSeqChild() const
670 return mpContext
->isMainSeqChild();
673 const Reference
<XAnimationNode
>& PPTXAnimationExport::getCurrentNode() const
676 return mpContext
->getNode();
679 void PPTXAnimationExport::WriteAnimationTarget(const Any
& rTarget
)
681 sal_Int32 nParagraph
= -1;
682 bool bParagraphTarget
= false;
684 Reference
<XShape
> rXShape
;
689 ParagraphTarget aParagraphTarget
;
690 if (rTarget
>>= aParagraphTarget
)
691 rXShape
= aParagraphTarget
.Shape
;
694 nParagraph
= static_cast<sal_Int32
>(aParagraphTarget
.Paragraph
);
695 Reference
<XSimpleText
> xText(rXShape
, UNO_QUERY
);
698 bParagraphTarget
= true;
706 sal_Int32 nShapeID
= mrPowerPointExport
.GetShapeID(rXShape
);
708 mpFS
->startElementNS(XML_p
, XML_tgtEl
);
709 mpFS
->startElementNS(XML_p
, XML_spTgt
, XML_spid
, OString::number(nShapeID
));
710 if (bParagraphTarget
)
712 mpFS
->startElementNS(XML_p
, XML_txEl
);
713 mpFS
->singleElementNS(XML_p
, XML_pRg
, XML_st
, OString::number(nParagraph
), XML_end
,
714 OString::number(nParagraph
));
715 mpFS
->endElementNS(XML_p
, XML_txEl
);
717 mpFS
->endElementNS(XML_p
, XML_spTgt
);
718 mpFS
->endElementNS(XML_p
, XML_tgtEl
);
721 void PPTXAnimationExport::WriteAnimationCondList(const Any
& rAny
, sal_Int32 nToken
)
723 if (!rAny
.hasValue())
726 std::vector
<Cond
> aList
;
728 bool bIsMainSeqChild
= isMainSeqChild();
730 Sequence
<Any
> aCondSeq
;
731 if (rAny
>>= aCondSeq
)
733 for (const auto& rCond
: std::as_const(aCondSeq
))
735 Cond
aCond(rCond
, bIsMainSeqChild
);
737 aList
.push_back(aCond
);
742 Cond
aCond(rAny
, bIsMainSeqChild
);
744 aList
.push_back(aCond
);
747 if (aList
.size() > 0)
749 mpFS
->startElementNS(XML_p
, nToken
);
751 for (const Cond
& rCond
: aList
)
752 WriteAnimationCond(rCond
);
754 mpFS
->endElementNS(XML_p
, nToken
);
758 void PPTXAnimationExport::WriteAnimationCond(const Cond
& rCond
)
763 if (rCond
.mxShape
.is())
765 mpFS
->startElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay(), XML_evt
,
767 WriteAnimationTarget(makeAny(rCond
.mxShape
));
768 mpFS
->endElementNS(XML_p
, XML_cond
);
770 else if (rCond
.mxNode
.is() && (nId
= GetAnimationNodeId(rCond
.mxNode
)) != -1)
772 mpFS
->startElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay(), XML_evt
,
774 mpFS
->singleElementNS(XML_p
, XML_tn
, XML_val
, OString::number(nId
));
775 mpFS
->endElementNS(XML_p
, XML_cond
);
779 mpFS
->singleElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay(), XML_evt
,
784 mpFS
->singleElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay());
787 void PPTXAnimationExport::WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType
)
789 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
790 Reference
<XAnimate
> rXAnimate(rXNode
, UNO_QUERY
);
794 const char* pCalcMode
= nullptr;
795 const char* pValueType
= nullptr;
796 bool bSimple
= (nXmlNodeType
!= XML_anim
);
801 switch (rXAnimate
->getCalcMode())
803 case AnimationCalcMode::DISCRETE
:
804 pCalcMode
= "discrete";
806 case AnimationCalcMode::LINEAR
:
811 switch (AnimationExporter::GetValueTypeForAttributeName(rXAnimate
->getAttributeName()))
813 case AnimationValueType::STRING
:
816 case AnimationValueType::NUMBER
:
819 case AnimationValueType::COLOR
:
825 if (nXmlNodeType
== XML_animMotion
)
828 Reference
<XAnimateMotion
> xMotion(rXNode
, UNO_QUERY
);
831 xMotion
->getPath() >>= aPath
;
832 ::basegfx::B2DPolyPolygon aPolyPoly
;
833 if (::basegfx::utils::importFromSvgD(aPolyPoly
, aPath
, true, nullptr))
834 aPath
= ::basegfx::utils::exportToSvgD(aPolyPoly
, false, false, true, true);
837 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_origin
, "layout", XML_path
, aPath
.toUtf8());
839 else if (nXmlNodeType
== XML_animRot
)
841 // when const char* is nullptr, the attribute is completely omitted in the output
842 const char* pBy
= nullptr;
843 const char* pFrom
= nullptr;
844 const char* pTo
= nullptr;
845 OString aBy
, aFrom
, aTo
;
847 Reference
<XAnimateTransform
> xTransform(rXNode
, UNO_QUERY
);
851 if (xTransform
->getBy() >>= value
)
853 aBy
= OString::number(static_cast<int>(value
* PER_DEGREE
));
857 if (xTransform
->getFrom() >>= value
)
859 aFrom
= OString::number(static_cast<int>(value
* PER_DEGREE
));
860 pFrom
= aFrom
.getStr();
863 if (xTransform
->getTo() >>= value
)
865 aTo
= OString::number(static_cast<int>(value
* PER_DEGREE
));
870 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_by
, pBy
, XML_from
, pFrom
, XML_to
, pTo
);
872 else if (nXmlNodeType
== XML_animClr
)
874 Reference
<XAnimateColor
> xColor(rXNode
, UNO_QUERY
);
875 const char* pColorSpace
= "rgb";
876 const char* pDirection
= nullptr;
877 if (xColor
.is() && xColor
->getColorInterpolation() == AnimationColorSpace::HSL
)
879 // Note: from, to, by can still be specified in any supported format.
881 pDirection
= xColor
->getDirection() ? "cw" : "ccw";
883 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_clrSpc
, pColorSpace
, XML_dir
, pDirection
,
884 XML_calcmode
, pCalcMode
, XML_valueType
, pValueType
);
888 OUString sFrom
, sTo
, sBy
;
889 if (rXAnimate
.is() && nXmlNodeType
== XML_anim
)
891 OUString sAttributeName
= rXAnimate
->getAttributeName();
893 = AnimationExporter::convertAnimateValue(rXAnimate
->getFrom(), sAttributeName
);
895 Any aTo
= AnimationExporter::convertAnimateValue(rXAnimate
->getTo(), sAttributeName
);
897 Any aBy
= AnimationExporter::convertAnimateValue(rXAnimate
->getBy(), sAttributeName
);
901 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_calcmode
, pCalcMode
, XML_valueType
,
902 pValueType
, XML_from
,
903 sFrom
.isEmpty() ? nullptr : sFrom
.toUtf8().getStr(), XML_to
,
904 sTo
.isEmpty() ? nullptr : sTo
.toUtf8().getStr(), XML_by
,
905 sBy
.isEmpty() ? nullptr : sBy
.toUtf8().getStr());
906 bTo
= sTo
.isEmpty() && sFrom
.isEmpty() && sBy
.isEmpty();
909 WriteAnimationNodeAnimateInside(bSimple
, bTo
);
910 mpFS
->endElementNS(XML_p
, nXmlNodeType
);
913 void PPTXAnimationExport::WriteAnimationNodeAnimateInside(bool bSimple
, bool bWriteTo
)
915 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
916 Reference
<XAnimate
> rXAnimate(rXNode
, UNO_QUERY
);
920 const char* pAdditive
= nullptr;
924 switch (rXAnimate
->getAdditive())
926 case AnimationAdditiveMode::BASE
:
929 case AnimationAdditiveMode::SUM
:
932 case AnimationAdditiveMode::REPLACE
:
935 case AnimationAdditiveMode::MULTIPLY
:
938 case AnimationAdditiveMode::NONE
:
944 mpFS
->startElementNS(XML_p
, XML_cBhvr
, XML_additive
, pAdditive
);
945 WriteAnimationNodeCommonPropsStart();
947 Reference
<XIterateContainer
> xIterate(rXNode
->getParent(), UNO_QUERY
);
948 WriteAnimationTarget(xIterate
.is() ? xIterate
->getTarget() : rXAnimate
->getTarget());
950 Reference
<XAnimateTransform
> xTransform(rXNode
, UNO_QUERY
);
952 // The attribute name of AnimateTransform is "Transform", we have to fix it.
954 if (xTransform
.is() && xTransform
->getTransformType() == AnimationTransformType::ROTATE
)
957 WriteAnimationAttributeName(mpFS
, xTransform
.is() ? sNewAttr
: rXAnimate
->getAttributeName());
959 mpFS
->endElementNS(XML_p
, XML_cBhvr
);
960 WriteAnimateValues(mpFS
, rXAnimate
);
962 Reference
<XAnimateColor
> xColor(rXNode
, UNO_QUERY
);
966 WriteAnimateColorColor(mpFS
, xColor
->getBy(), XML_by
);
967 WriteAnimateColorColor(mpFS
, xColor
->getFrom(), XML_from
);
968 WriteAnimateColorColor(mpFS
, xColor
->getTo(), XML_to
);
970 else if (xTransform
.is() && xTransform
->getTransformType() == AnimationTransformType::SCALE
)
972 WriteAnimationProperty(mpFS
, rXAnimate
->getBy(), XML_by
);
973 WriteAnimationProperty(mpFS
, rXAnimate
->getFrom(), XML_from
);
974 WriteAnimationProperty(mpFS
, rXAnimate
->getTo(), XML_to
);
977 WriteAnimateTo(mpFS
, rXAnimate
->getTo(), rXAnimate
->getAttributeName());
980 void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart()
982 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
983 const char* pDuration
= nullptr;
984 const char* pRestart
= nullptr;
985 const char* pNodeType
= nullptr;
986 const char* pPresetClass
= nullptr;
987 const char* pFill
= nullptr;
988 double fDuration
= 0;
992 aAny
= rXNode
->getDuration();
997 if (aAny
>>= eTiming
)
999 if (eTiming
== Timing_INDEFINITE
)
1000 pDuration
= "indefinite";
1006 pRestart
= convertAnimationRestart(rXNode
->getRestart());
1008 sal_Int16 nType
= mpContext
->getEffectNodeType();
1011 pNodeType
= convertEffectNodeType(nType
);
1012 if (nType
== EffectNodeType::TIMING_ROOT
)
1015 pDuration
= "indefinite";
1019 else if (nType
== EffectNodeType::MAIN_SEQUENCE
)
1021 pDuration
= "indefinite";
1025 sal_uInt32 nPresetClass
= mpContext
->getEffectPresetClass();
1026 if (nPresetClass
!= DFF_ANIM_PRESS_CLASS_USER_DEFINED
)
1027 pPresetClass
= convertEffectPresetClass(nPresetClass
);
1029 sal_uInt32 nPresetId
= 0;
1030 bool bPresetId
= false;
1031 const OUString
& rPresetId
= mpContext
->getEffectPresetId();
1032 if (rPresetId
.getLength() > 0)
1034 nPresetId
= AnimationExporter::GetPresetID(rPresetId
, nPresetClass
, bPresetId
);
1038 sal_uInt32 nPresetSubType
= 0;
1039 bool bPresetSubType
= false;
1040 const OUString
& sPresetSubType
= mpContext
->getEffectPresetSubType();
1041 if (sPresetSubType
.getLength() > 0)
1044 = AnimationExporter::TranslatePresetSubType(nPresetClass
, nPresetId
, sPresetSubType
);
1045 bPresetSubType
= true;
1048 if (nType
!= EffectNodeType::TIMING_ROOT
&& nType
!= EffectNodeType::MAIN_SEQUENCE
)
1050 // it doesn't seem to work right on root and mainseq nodes
1051 sal_Int16 nFill
= AnimationExporter::GetFillMode(rXNode
, AnimationFill::AUTO
);
1052 pFill
= convertAnimationFill(nFill
);
1055 bool bAutoReverse
= rXNode
->getAutoReverse();
1057 mpFS
->startElementNS(
1058 XML_p
, XML_cTn
, XML_id
, OString::number(GetNextAnimationNodeId(rXNode
)), XML_dur
,
1059 fDuration
!= 0 ? OString::number(static_cast<sal_Int32
>(fDuration
* 1000.0)).getStr()
1061 XML_autoRev
, bAutoReverse
? "1" : nullptr, XML_restart
, pRestart
, XML_nodeType
, pNodeType
,
1062 XML_fill
, pFill
, XML_presetClass
, pPresetClass
, XML_presetID
,
1063 bPresetId
? OString::number(nPresetId
).getStr() : nullptr, XML_presetSubtype
,
1064 bPresetSubType
? OString::number(nPresetSubType
).getStr() : nullptr);
1066 WriteAnimationCondList(mpContext
->getCondition(true), XML_stCondLst
);
1067 WriteAnimationCondList(mpContext
->getCondition(false), XML_endCondLst
);
1069 if (rXNode
->getType() == AnimationNodeType::ITERATE
)
1071 Reference
<XIterateContainer
> xIterate(rXNode
, UNO_QUERY
);
1074 const char* sType
= convertTextAnimationType(xIterate
->getIterateType());
1076 mpFS
->startElementNS(XML_p
, XML_iterate
, XML_type
, sType
);
1077 mpFS
->singleElementNS(XML_p
, XML_tmAbs
, XML_val
,
1078 OString::number(xIterate
->getIterateInterval() * 1000));
1079 mpFS
->endElementNS(XML_p
, XML_iterate
);
1083 const std::vector
<NodeContextPtr
>& aChildNodes
= mpContext
->getChildNodes();
1084 if (!aChildNodes
.empty())
1086 mpFS
->startElementNS(XML_p
, XML_childTnLst
);
1087 for (const NodeContextPtr
& pChildContext
: aChildNodes
)
1089 if (pChildContext
->isValid())
1090 WriteAnimationNode(pChildContext
);
1092 mpFS
->endElementNS(XML_p
, XML_childTnLst
);
1094 mpFS
->endElementNS(XML_p
, XML_cTn
);
1097 void PPTXAnimationExport::WriteAnimationNodeSeq()
1099 SAL_INFO("sd.eppt", "write animation node SEQ");
1101 mpFS
->startElementNS(XML_p
, XML_seq
);
1103 WriteAnimationNodeCommonPropsStart();
1105 WriteAnimationCondListForSeq(mpFS
, XML_prevCondLst
);
1106 WriteAnimationCondListForSeq(mpFS
, XML_nextCondLst
);
1108 mpFS
->endElementNS(XML_p
, XML_seq
);
1111 void PPTXAnimationExport::WriteAnimationNodeEffect()
1113 SAL_INFO("sd.eppt", "write animation node FILTER");
1114 Reference
<XTransitionFilter
> xFilter(getCurrentNode(), UNO_QUERY
);
1117 const char* pFilter
= ::ppt::AnimationExporter::FindTransitionName(
1118 xFilter
->getTransition(), xFilter
->getSubtype(), xFilter
->getDirection());
1119 const char* pMode
= xFilter
->getMode() ? "in" : "out";
1120 mpFS
->startElementNS(XML_p
, XML_animEffect
, XML_filter
, pFilter
, XML_transition
, pMode
);
1122 WriteAnimationNodeAnimateInside(false);
1124 mpFS
->endElementNS(XML_p
, XML_animEffect
);
1128 void PPTXAnimationExport::WriteAnimationNodeCommand()
1130 SAL_INFO("sd.eppt", "write animation node COMMAND");
1131 Reference
<XCommand
> xCommand(getCurrentNode(), UNO_QUERY
);
1135 const char* pType
= "call";
1136 const char* pCommand
= nullptr;
1137 switch (xCommand
->getCommand())
1139 case EffectCommands::VERB
:
1141 pCommand
= "1"; /* FIXME hardcoded viewing */
1143 case EffectCommands::PLAY
:
1146 case EffectCommands::TOGGLEPAUSE
:
1147 pCommand
= "togglePause";
1149 case EffectCommands::STOP
:
1153 SAL_WARN("sd.eppt", "unknown command: " << xCommand
->getCommand());
1157 mpFS
->startElementNS(XML_p
, XML_cmd
, XML_type
, pType
, XML_cmd
, pCommand
);
1159 WriteAnimationNodeAnimateInside(false);
1160 mpFS
->startElementNS(XML_p
, XML_cBhvr
);
1161 WriteAnimationNodeCommonPropsStart();
1162 WriteAnimationTarget(xCommand
->getTarget());
1163 mpFS
->endElementNS(XML_p
, XML_cBhvr
);
1165 mpFS
->endElementNS(XML_p
, XML_cmd
);
1168 void PPTXAnimationExport::WriteAnimationNodeAudio()
1170 SAL_INFO("sd.eppt", "write animation node audio");
1171 Reference
<XAudio
> xAudio(getCurrentNode(), UNO_QUERY
);
1177 if (!(xAudio
.is() && (xAudio
->getSource() >>= sUrl
) && !sUrl
.isEmpty()
1178 && sUrl
.endsWithIgnoreAsciiCase(".wav")))
1181 mrPowerPointExport
.embedEffectAudio(mpFS
, sUrl
, sRelId
, sName
);
1183 mpFS
->startElementNS(XML_p
, XML_audio
);
1184 mpFS
->startElementNS(XML_p
, XML_cMediaNode
);
1186 mpFS
->startElementNS(XML_p
, XML_cTn
);
1187 WriteAnimationCondList(mpContext
->getCondition(true), XML_stCondLst
);
1188 WriteAnimationCondList(mpContext
->getCondition(false), XML_endCondLst
);
1189 mpFS
->endElementNS(XML_p
, XML_cTn
);
1191 mpFS
->startElementNS(XML_p
, XML_tgtEl
);
1192 mpFS
->singleElementNS(XML_p
, XML_sndTgt
, FSNS(XML_r
, XML_embed
),
1193 sRelId
.isEmpty() ? nullptr : sRelId
.toUtf8().getStr(), XML_name
,
1194 sUrl
.isEmpty() ? nullptr : sName
.toUtf8().getStr());
1195 mpFS
->endElementNS(XML_p
, XML_tgtEl
);
1197 mpFS
->endElementNS(XML_p
, XML_cMediaNode
);
1198 mpFS
->endElementNS(XML_p
, XML_audio
);
1201 void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr
& pContext
)
1203 const NodeContext
* pSavedContext
= mpContext
;
1204 mpContext
= pContext
.get();
1206 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
1208 SAL_INFO("sd.eppt", "export node type: " << rXNode
->getType());
1209 sal_Int32 xmlNodeType
= extractNodeType(rXNode
);
1211 switch (xmlNodeType
)
1214 mpFS
->startElementNS(XML_p
, xmlNodeType
);
1215 WriteAnimationNodeCommonPropsStart();
1216 mpFS
->endElementNS(XML_p
, xmlNodeType
);
1219 WriteAnimationNodeSeq();
1224 case XML_animMotion
:
1227 WriteAnimationNodeAnimate(xmlNodeType
);
1229 case XML_animEffect
:
1230 WriteAnimationNodeEffect();
1233 WriteAnimationNodeCommand();
1236 WriteAnimationNodeAudio();
1239 SAL_WARN("sd.eppt", "export ooxml node type: " << xmlNodeType
);
1243 mpContext
= pSavedContext
;
1246 void PPTXAnimationExport::WriteAnimations(const Reference
<XDrawPage
>& rXDrawPage
)
1248 Reference
<XAnimationNodeSupplier
> xNodeSupplier(rXDrawPage
, UNO_QUERY
);
1249 if (!xNodeSupplier
.is())
1252 const Reference
<XAnimationNode
> xNode(xNodeSupplier
->getAnimationNode());
1256 Reference
<XEnumerationAccess
> xEnumerationAccess(xNode
, UNO_QUERY
);
1257 if (!xEnumerationAccess
.is())
1260 Reference
<XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1261 if (!(xEnumeration
.is() && xEnumeration
->hasMoreElements()))
1264 auto pNodeContext
= std::make_unique
<NodeContext
>(xNode
, false, false);
1265 if (pNodeContext
->isValid())
1267 mpFS
->startElementNS(XML_p
, XML_timing
);
1268 mpFS
->startElementNS(XML_p
, XML_tnLst
);
1270 WriteAnimationNode(pNodeContext
);
1272 mpFS
->endElementNS(XML_p
, XML_tnLst
);
1273 mpFS
->endElementNS(XML_p
, XML_timing
);
1277 sal_Int32
PPTXAnimationExport::GetNextAnimationNodeId(const Reference
<XAnimationNode
>& xNode
)
1279 sal_Int32 nId
= mrPowerPointExport
.GetNextAnimationNodeID();
1280 maAnimationNodeIdMap
[xNode
] = nId
;
1284 sal_Int32
PPTXAnimationExport::GetAnimationNodeId(const Reference
<XAnimationNode
>& xNode
)
1287 const auto& aIter
= maAnimationNodeIdMap
.find(xNode
);
1288 if (aIter
!= maAnimationNodeIdMap
.end())
1290 nId
= aIter
->second
;
1295 NodeContext::NodeContext(const Reference
<XAnimationNode
>& xNode
, bool bMainSeqChild
,
1296 bool bIsIterateChild
)
1298 , mbMainSeqChild(bMainSeqChild
)
1300 , mnEffectNodeType(-1)
1301 , mnEffectPresetClass(DFF_ANIM_PRESS_CLASS_USER_DEFINED
)
1307 initValid(initChildNodes(), bIsIterateChild
);
1310 void NodeContext::initUserData()
1312 assert(mxNode
.is());
1314 Sequence
<NamedValue
> aUserData
= mxNode
->getUserData();
1315 const Any
* aIndexedData
[DFF_ANIM_PROPERTY_ID_COUNT
];
1316 AnimationExporter::GetUserData(aUserData
, aIndexedData
, sizeof(aIndexedData
));
1318 const Any
* pAny
= aIndexedData
[DFF_ANIM_NODE_TYPE
];
1320 *pAny
>>= mnEffectNodeType
;
1322 pAny
= aIndexedData
[DFF_ANIM_PRESET_CLASS
];
1324 *pAny
>>= mnEffectPresetClass
;
1326 pAny
= aIndexedData
[DFF_ANIM_PRESET_ID
];
1328 *pAny
>>= msEffectPresetId
;
1330 pAny
= aIndexedData
[DFF_ANIM_PRESET_SUB_TYPE
];
1332 *pAny
>>= msEffectPresetSubType
;
1335 void NodeContext::initValid(bool bHasValidChild
, bool bIsIterateChild
)
1337 sal_Int16 nType
= mxNode
->getType();
1339 if (nType
== AnimationNodeType::ITERATE
)
1341 Reference
<XIterateContainer
> xIterate(mxNode
, UNO_QUERY
);
1342 mbValid
= xIterate
.is() && (bIsIterateChild
|| isValidTarget(xIterate
->getTarget()))
1343 && !maChildNodes
.empty();
1345 else if (nType
== AnimationNodeType::COMMAND
)
1347 Reference
<XCommand
> xCommand(mxNode
, UNO_QUERY
);
1348 mbValid
= xCommand
.is() && (bIsIterateChild
|| isValidTarget(xCommand
->getTarget()));
1350 else if (nType
== AnimationNodeType::PAR
|| nType
== AnimationNodeType::SEQ
)
1352 mbValid
= bHasValidChild
;
1354 else if (nType
== AnimationNodeType::AUDIO
)
1356 Reference
<XAudio
> xAudio(mxNode
, UNO_QUERY
);
1359 = xAudio
.is() && (xAudio
->getSource() >>= sURL
) && sURL
.endsWithIgnoreAsciiCase(".wav");
1363 Reference
<XAnimate
> xAnimate(mxNode
, UNO_QUERY
);
1364 mbValid
= xAnimate
.is() && (bIsIterateChild
|| isValidTarget(xAnimate
->getTarget()));
1368 bool NodeContext::initChildNodes()
1370 bool bValid
= false;
1371 Reference
<XEnumerationAccess
> xEnumerationAccess(mxNode
, UNO_QUERY
);
1372 if (xEnumerationAccess
.is())
1374 Reference
<XEnumeration
> xEnumeration
= xEnumerationAccess
->createEnumeration();
1375 bool bIsMainSeq
= mnEffectNodeType
== EffectNodeType::MAIN_SEQUENCE
;
1376 bool bIsIterateChild
= mxNode
->getType() == AnimationNodeType::ITERATE
;
1377 if (xEnumeration
.is())
1379 while (xEnumeration
->hasMoreElements())
1381 Reference
<XAnimationNode
> xChildNode(xEnumeration
->nextElement(), UNO_QUERY
);
1382 if (xChildNode
.is())
1385 = std::make_unique
<NodeContext
>(xChildNode
, bIsMainSeq
, bIsIterateChild
);
1386 if (pChildContext
->isValid())
1388 maChildNodes
.push_back(std::move(pChildContext
));
1396 Any
NodeContext::getCondition(bool bBegin
) const
1399 = (mnEffectNodeType
!= EffectNodeType::INTERACTIVE_SEQUENCE
|| maChildNodes
.empty());
1400 const Reference
<XAnimationNode
>& rNode
= bParent
? mxNode
: maChildNodes
[0]->getNode();
1402 return bBegin
? rNode
->getBegin() : rNode
->getEnd();
1405 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */