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"
62 #include <comphelper/stl_types.hxx>
64 using namespace ::com::sun::star::animations
;
65 using namespace ::com::sun::star::container
;
66 using namespace ::com::sun::star::presentation
;
67 using namespace ::com::sun::star::uno
;
68 using namespace ::ppt
;
69 using namespace oox::drawingml
;
70 using namespace oox::core
;
73 using ::com::sun::star::beans::NamedValue
;
74 using ::com::sun::star::drawing::XDrawPage
;
75 using ::com::sun::star::drawing::XShape
;
76 using ::com::sun::star::text::XSimpleText
;
77 using ::sax_fastparser::FSHelperPtr
;
81 void WriteAnimationProperty(const FSHelperPtr
& pFS
, const Any
& rAny
, sal_Int32 nToken
= 0)
91 if ((aPair
.First
>>= x
) && (aPair
.Second
>>= y
))
95 // MS needs ending values but we have offset values.
99 pFS
->singleElementNS(XML_p
, nToken
, XML_x
, OString::number(x
* 100000), XML_y
,
100 OString::number(y
* 100000));
108 TypeClass aClass
= rAny
.getValueType().getTypeClass();
111 && (aClass
== TypeClass_LONG
|| aClass
== TypeClass_DOUBLE
|| aClass
== TypeClass_STRING
);
114 pFS
->startElementNS(XML_p
, nToken
);
116 switch (rAny
.getValueType().getTypeClass())
120 pFS
->singleElementNS(XML_a
, XML_srgbClr
, XML_val
, I32SHEX(nRgb
));
122 case TypeClass_DOUBLE
:
124 pFS
->singleElementNS(XML_p
, XML_fltVal
, XML_val
, OString::number(fDouble
));
126 case TypeClass_STRING
:
127 pFS
->singleElementNS(XML_p
, XML_strVal
, XML_val
,
128 (*o3tl::doAccess
<OUString
>(rAny
)).toUtf8());
135 pFS
->endElementNS(XML_p
, nToken
);
138 void WriteAnimateColorColor(const FSHelperPtr
& pFS
, const Any
& rAny
, sal_Int32 nToken
)
140 if (!rAny
.hasValue())
143 sal_Int32 nColor
= 0;
146 pFS
->startElementNS(XML_p
, nToken
);
148 if (nToken
== XML_by
)
150 // CT_TLByRgbColorTransform
151 SAL_WARN("sd.eppt", "Export p:rgb in p:by of animClr isn't implemented yet.");
156 pFS
->singleElementNS(XML_a
, XML_srgbClr
, XML_val
, I32SHEX(nColor
));
159 pFS
->endElementNS(XML_p
, nToken
);
162 Sequence
<double> aHSL(3);
163 if (!(rAny
>>= aHSL
))
166 pFS
->startElementNS(XML_p
, nToken
);
168 if (nToken
== XML_by
)
170 // CT_TLByHslColorTransform
171 pFS
->singleElementNS(XML_p
, XML_hsl
, XML_h
, OString::number(aHSL
[0] * 60000), // ST_Angel
172 XML_s
, OString::number(aHSL
[1] * 100000), XML_l
,
173 OString::number(aHSL
[2] * 100000));
178 SAL_WARN("sd.eppt", "Export p:hsl in p:from or p:to of animClr isn't implemented yet.");
181 pFS
->endElementNS(XML_p
, nToken
);
184 void WriteAnimateTo(const FSHelperPtr
& pFS
, const Any
& rValue
, const OUString
& rAttributeName
)
186 if (!rValue
.hasValue())
189 SAL_INFO("sd.eppt", "to attribute name: " << rAttributeName
.toUtf8());
191 WriteAnimationProperty(pFS
, AnimationExporter::convertAnimateValue(rValue
, rAttributeName
),
195 void WriteAnimateValues(const FSHelperPtr
& pFS
, const Reference
<XAnimate
>& rXAnimate
)
197 const Sequence
<double> aKeyTimes
= rXAnimate
->getKeyTimes();
198 if (!aKeyTimes
.hasElements())
200 const Sequence
<Any
> aValues
= rXAnimate
->getValues();
201 const OUString
& sFormula
= rXAnimate
->getFormula();
202 const OUString
& rAttributeName
= rXAnimate
->getAttributeName();
204 SAL_INFO("sd.eppt", "animate values, formula: " << sFormula
.toUtf8());
206 pFS
->startElementNS(XML_p
, XML_tavLst
);
208 for (int i
= 0; i
< aKeyTimes
.getLength(); i
++)
210 SAL_INFO("sd.eppt", "animate value " << i
<< ": " << aKeyTimes
[i
]);
211 if (aValues
[i
].hasValue())
213 pFS
->startElementNS(XML_p
, XML_tav
, XML_fmla
,
214 sFormula
.isEmpty() ? nullptr : sFormula
.toUtf8().getStr(), XML_tm
,
215 OString::number(static_cast<sal_Int32
>(aKeyTimes
[i
] * 100000.0)));
216 pFS
->startElementNS(XML_p
, XML_val
);
218 if (aValues
[i
] >>= aPair
)
220 WriteAnimationProperty(
221 pFS
, AnimationExporter::convertAnimateValue(aPair
.First
, rAttributeName
));
222 WriteAnimationProperty(
223 pFS
, AnimationExporter::convertAnimateValue(aPair
.Second
, rAttributeName
));
226 WriteAnimationProperty(
227 pFS
, AnimationExporter::convertAnimateValue(aValues
[i
], rAttributeName
));
229 pFS
->endElementNS(XML_p
, XML_val
);
230 pFS
->endElementNS(XML_p
, XML_tav
);
234 pFS
->endElementNS(XML_p
, XML_tavLst
);
237 // Write condition list ( either prevCondlst or nextCondlst ) of Seq.
238 void WriteAnimationCondListForSeq(const FSHelperPtr
& pFS
, sal_Int32 nToken
)
240 const char* pEvent
= (nToken
== XML_prevCondLst
) ? "onPrev" : "onNext";
242 pFS
->startElementNS(XML_p
, nToken
);
243 pFS
->startElementNS(XML_p
, XML_cond
, XML_evt
, pEvent
);
244 pFS
->startElementNS(XML_p
, XML_tgtEl
);
245 pFS
->singleElementNS(XML_p
, XML_sldTgt
);
246 pFS
->endElementNS(XML_p
, XML_tgtEl
);
247 pFS
->endElementNS(XML_p
, XML_cond
);
248 pFS
->endElementNS(XML_p
, nToken
);
251 const char* convertEventTrigger(sal_Int16 nTrigger
)
253 const char* pEvent
= nullptr;
256 case EventTrigger::ON_NEXT
:
259 case EventTrigger::ON_PREV
:
262 case EventTrigger::BEGIN_EVENT
:
265 case EventTrigger::END_EVENT
:
268 case EventTrigger::ON_BEGIN
:
271 case EventTrigger::ON_END
:
274 case EventTrigger::ON_CLICK
:
277 case EventTrigger::ON_DBL_CLICK
:
278 pEvent
= "onDblClick";
280 case EventTrigger::ON_STOP_AUDIO
:
281 pEvent
= "onStopAudio";
283 case EventTrigger::ON_MOUSE_ENTER
:
284 pEvent
= "onMouseOver"; // not exact?
286 case EventTrigger::ON_MOUSE_LEAVE
:
287 pEvent
= "onMouseOut";
293 void WriteAnimationAttributeName(const FSHelperPtr
& pFS
, const OUString
& rAttributeName
)
295 if (rAttributeName
.isEmpty())
298 pFS
->startElementNS(XML_p
, XML_attrNameLst
);
300 SAL_INFO("sd.eppt", "write attribute name: " << rAttributeName
.toUtf8());
302 if (rAttributeName
== "X;Y")
304 pFS
->startElementNS(XML_p
, XML_attrName
);
305 pFS
->writeEscaped("ppt_x");
306 pFS
->endElementNS(XML_p
, XML_attrName
);
308 pFS
->startElementNS(XML_p
, XML_attrName
);
309 pFS
->writeEscaped("ppt_y");
310 pFS
->endElementNS(XML_p
, XML_attrName
);
314 const oox::ppt::ImplAttributeNameConversion
* attrConv
315 = oox::ppt::getAttributeConversionList();
316 const char* pAttribute
= nullptr;
318 while (attrConv
->mpAPIName
!= nullptr)
320 if (rAttributeName
.equalsAscii(attrConv
->mpAPIName
))
322 pAttribute
= attrConv
->mpMSName
;
330 pFS
->startElementNS(XML_p
, XML_attrName
);
331 pFS
->writeEscaped(pAttribute
);
332 pFS
->endElementNS(XML_p
, XML_attrName
);
336 SAL_WARN("sd.eppt", "unhandled animation attribute name: " << rAttributeName
);
340 pFS
->endElementNS(XML_p
, XML_attrNameLst
);
343 bool isValidTarget(const Any
& rTarget
)
345 Reference
<XShape
> xShape
;
347 if ((rTarget
>>= xShape
) && xShape
.is())
350 ParagraphTarget aParagraphTarget
;
352 return (rTarget
>>= aParagraphTarget
) && aParagraphTarget
.Shape
.is();
355 /// extract ooxml node type from a XAnimationNode.
356 sal_Int32
extractNodeType(const Reference
<XAnimationNode
>& rXNode
)
358 sal_Int16 nType
= rXNode
->getType();
359 sal_Int32 xmlNodeType
= -1;
362 case AnimationNodeType::ITERATE
:
363 case AnimationNodeType::PAR
:
364 xmlNodeType
= XML_par
;
366 case AnimationNodeType::SEQ
:
367 xmlNodeType
= XML_seq
;
369 case AnimationNodeType::ANIMATE
:
370 xmlNodeType
= XML_anim
;
372 case AnimationNodeType::ANIMATEMOTION
:
373 xmlNodeType
= XML_animMotion
;
375 case AnimationNodeType::ANIMATETRANSFORM
:
377 Reference
<XAnimateTransform
> xTransform(rXNode
, UNO_QUERY
);
380 if (xTransform
->getTransformType() == AnimationTransformType::SCALE
)
381 xmlNodeType
= XML_animScale
;
382 else if (xTransform
->getTransformType() == AnimationTransformType::ROTATE
)
383 xmlNodeType
= XML_animRot
;
387 case AnimationNodeType::ANIMATECOLOR
:
388 xmlNodeType
= XML_animClr
;
390 case AnimationNodeType::SET
:
391 xmlNodeType
= XML_set
;
393 case AnimationNodeType::TRANSITIONFILTER
:
394 xmlNodeType
= XML_animEffect
;
396 case AnimationNodeType::COMMAND
:
397 xmlNodeType
= XML_cmd
;
399 case AnimationNodeType::AUDIO
:
400 xmlNodeType
= XML_audio
;
403 SAL_WARN("sd.eppt", "unhandled animation node: " << nType
);
409 /// Convert AnimationRestart to ST_TLTimeNodeRestartType value.
410 const char* convertAnimationRestart(sal_Int16 nRestart
)
412 const char* pRestart
= nullptr;
415 case AnimationRestart::ALWAYS
:
418 case AnimationRestart::WHEN_NOT_ACTIVE
:
419 pRestart
= "whenNotActive";
421 case AnimationRestart::NEVER
:
428 /// Convert EffectNodeType to ST_TLTimeNodeType
429 const char* convertEffectNodeType(sal_Int16 nType
)
431 const char* pNodeType
= nullptr;
434 case EffectNodeType::TIMING_ROOT
:
435 pNodeType
= "tmRoot";
437 case EffectNodeType::MAIN_SEQUENCE
:
438 pNodeType
= "mainSeq";
440 case EffectNodeType::ON_CLICK
:
441 pNodeType
= "clickEffect";
443 case EffectNodeType::AFTER_PREVIOUS
:
444 pNodeType
= "afterEffect";
446 case EffectNodeType::WITH_PREVIOUS
:
447 pNodeType
= "withEffect";
449 case EffectNodeType::INTERACTIVE_SEQUENCE
:
450 pNodeType
= "interactiveSeq";
456 /// Convert EffectPresetClass to ST_TLTimeNodePresetClassType
457 const char* convertEffectPresetClass(sal_Int16 nPresetClass
)
459 const char* pPresetClass
= nullptr;
460 switch (nPresetClass
)
462 case EffectPresetClass::ENTRANCE
:
463 pPresetClass
= "entr";
465 case EffectPresetClass::EXIT
:
466 pPresetClass
= "exit";
468 case EffectPresetClass::EMPHASIS
:
469 pPresetClass
= "emph";
471 case EffectPresetClass::MOTIONPATH
:
472 pPresetClass
= "path";
474 case EffectPresetClass::OLEACTION
:
475 pPresetClass
= "verb"; // ?
477 case EffectPresetClass::MEDIACALL
:
478 pPresetClass
= "mediacall";
484 /// convert AnimationFill to ST_TLTimeNodeFillType.
485 const char* convertAnimationFill(sal_Int16 nFill
)
487 const char* pFill
= nullptr;
490 case AnimationFill::FREEZE
:
493 case AnimationFill::HOLD
:
496 case AnimationFill::REMOVE
:
499 case AnimationFill::TRANSITION
:
500 pFill
= "transition";
506 /// Convert TextAnimationType to ST_IterateType.
507 const char* convertTextAnimationType(sal_Int16 nType
)
509 const char* sType
= nullptr;
512 case TextAnimationType::BY_PARAGRAPH
:
515 case TextAnimationType::BY_LETTER
:
518 case TextAnimationType::BY_WORD
:
528 typedef std::unique_ptr
<NodeContext
> NodeContextPtr
;
532 const Reference
<XAnimationNode
> mxNode
;
533 const bool mbMainSeqChild
;
535 std::vector
<NodeContextPtr
> maChildNodes
;
536 // if the node has valid target or contains at least one valid target.
539 // Attributes initialized from mxNode->getUserData().
540 sal_Int16 mnEffectNodeType
;
541 sal_Int16 mnEffectPresetClass
;
542 OUString msEffectPresetId
;
543 OUString msEffectPresetSubType
;
545 /// constructor helper for initializing user datas.
548 /// constructor helper to initialize maChildNodes.
549 /// return true if at least one childnode is valid.
550 bool initChildNodes();
552 /// constructor helper to initialize mbValid
553 void initValid(bool bHasValidChild
, bool bIsIterateChild
);
556 NodeContext(const Reference
<XAnimationNode
>& xNode
, bool bMainSeqChild
, bool bIsIterateChild
);
557 const Reference
<XAnimationNode
>& getNode() const { return mxNode
; }
558 bool isMainSeqChild() const { return mbMainSeqChild
; }
559 sal_Int16
getEffectNodeType() const { return mnEffectNodeType
; }
560 sal_Int16
getEffectPresetClass() const { return mnEffectPresetClass
; }
561 const OUString
& getEffectPresetId() const { return msEffectPresetId
; }
562 const OUString
& getEffectPresetSubType() const { return msEffectPresetSubType
; }
563 bool isValid() const { return mbValid
; }
564 const std::vector
<NodeContextPtr
>& getChildNodes() const { return maChildNodes
; };
565 Any
getCondition(bool bBegin
) const;
572 Reference
<XShape
> mxShape
;
573 Reference
<XAnimationNode
> mxNode
;
575 Cond(const Any
& rAny
, bool bIsMainSeqChild
);
577 bool isValid() { return msDelay
.getLength() || mpEvent
; }
578 const char* getDelay() const { return msDelay
.getLength() ? msDelay
.getStr() : nullptr; }
581 Cond::Cond(const Any
& rAny
, bool bIsMainSeqChild
)
584 bool bHasFDelay
= false;
589 if (rAny
>>= eTiming
)
591 if (eTiming
== Timing_INDEFINITE
)
592 msDelay
= "indefinite";
594 else if (rAny
>>= aEvent
)
596 if (aEvent
.Trigger
== EventTrigger::ON_NEXT
&& bIsMainSeqChild
)
597 msDelay
= "indefinite";
600 mpEvent
= convertEventTrigger(aEvent
.Trigger
);
601 if (!(aEvent
.Source
>>= mxShape
))
602 aEvent
.Source
>>= mxNode
;
604 if (aEvent
.Offset
>>= fDelay
)
608 else if (rAny
>>= fDelay
)
613 sal_Int32 nDelay
= static_cast<sal_uInt32
>(fDelay
* 1000.0);
614 msDelay
= OString::number(nDelay
);
618 class PPTXAnimationExport
620 void WriteAnimationNode(const NodeContextPtr
& pContext
);
621 void WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType
);
622 void WriteAnimationNodeAnimateInside(bool bSimple
, bool bWriteTo
= true);
623 void WriteAnimationNodeSeq();
624 void WriteAnimationNodeEffect();
625 void WriteAnimationNodeCommand();
626 void WriteAnimationNodeAudio();
627 void WriteAnimationNodeCommonPropsStart();
628 void WriteAnimationTarget(const Any
& rTarget
);
629 void WriteAnimationCondList(const Any
& rAny
, sal_Int32 nToken
);
630 void WriteAnimationCond(const Cond
& rCond
);
631 bool isMainSeqChild();
632 const Reference
<XAnimationNode
>& getCurrentNode();
634 PowerPointExport
& mrPowerPointExport
;
635 const FSHelperPtr
& mpFS
;
636 const NodeContext
* mpContext
;
638 std::map
<Reference
<XAnimationNode
>, sal_Int32
> maAnimationNodeIdMap
;
639 sal_Int32
GetNextAnimationNodeId(const Reference
<XAnimationNode
>& rNode
);
640 sal_Int32
GetAnimationNodeId(const Reference
<XAnimationNode
>& rNode
);
643 PPTXAnimationExport(PowerPointExport
& rExport
, const FSHelperPtr
& pFS
);
644 void WriteAnimations(const Reference
<XDrawPage
>& rXDrawPage
);
652 void WriteAnimations(const FSHelperPtr
& pFS
, const Reference
<XDrawPage
>& rXDrawPage
,
653 PowerPointExport
& rExport
)
655 PPTXAnimationExport
aAnimationExport(rExport
, pFS
);
656 aAnimationExport
.WriteAnimations(rXDrawPage
);
661 PPTXAnimationExport::PPTXAnimationExport(PowerPointExport
& rExport
, const FSHelperPtr
& pFS
)
662 : mrPowerPointExport(rExport
)
668 bool PPTXAnimationExport::isMainSeqChild()
671 return mpContext
->isMainSeqChild();
674 const Reference
<XAnimationNode
>& PPTXAnimationExport::getCurrentNode()
677 return mpContext
->getNode();
680 void PPTXAnimationExport::WriteAnimationTarget(const Any
& rTarget
)
682 sal_Int32 nParagraph
= -1;
683 bool bParagraphTarget
= false;
685 Reference
<XShape
> rXShape
;
690 ParagraphTarget aParagraphTarget
;
691 if (rTarget
>>= aParagraphTarget
)
692 rXShape
= aParagraphTarget
.Shape
;
695 nParagraph
= static_cast<sal_Int32
>(aParagraphTarget
.Paragraph
);
696 Reference
<XSimpleText
> xText(rXShape
, UNO_QUERY
);
699 bParagraphTarget
= true;
707 sal_Int32 nShapeID
= mrPowerPointExport
.GetShapeID(rXShape
);
709 mpFS
->startElementNS(XML_p
, XML_tgtEl
);
710 mpFS
->startElementNS(XML_p
, XML_spTgt
, XML_spid
, OString::number(nShapeID
));
711 if (bParagraphTarget
)
713 mpFS
->startElementNS(XML_p
, XML_txEl
);
714 mpFS
->singleElementNS(XML_p
, XML_pRg
, XML_st
, OString::number(nParagraph
), XML_end
,
715 OString::number(nParagraph
));
716 mpFS
->endElementNS(XML_p
, XML_txEl
);
718 mpFS
->endElementNS(XML_p
, XML_spTgt
);
719 mpFS
->endElementNS(XML_p
, XML_tgtEl
);
722 void PPTXAnimationExport::WriteAnimationCondList(const Any
& rAny
, sal_Int32 nToken
)
724 if (!rAny
.hasValue())
727 std::vector
<Cond
> aList
;
729 bool bIsMainSeqChild
= isMainSeqChild();
731 Sequence
<Any
> aCondSeq
;
732 if (rAny
>>= aCondSeq
)
734 for (int i
= 0; i
< aCondSeq
.getLength(); i
++)
736 Cond
aCond(aCondSeq
[i
], bIsMainSeqChild
);
738 aList
.push_back(aCond
);
743 Cond
aCond(rAny
, bIsMainSeqChild
);
745 aList
.push_back(aCond
);
748 if (aList
.size() > 0)
750 mpFS
->startElementNS(XML_p
, nToken
);
752 for (const Cond
& rCond
: aList
)
753 WriteAnimationCond(rCond
);
755 mpFS
->endElementNS(XML_p
, nToken
);
759 void PPTXAnimationExport::WriteAnimationCond(const Cond
& rCond
)
764 if (rCond
.mxShape
.is())
766 mpFS
->startElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay(), XML_evt
,
768 WriteAnimationTarget(makeAny(rCond
.mxShape
));
769 mpFS
->endElementNS(XML_p
, XML_cond
);
771 else if (rCond
.mxNode
.is() && (nId
= GetAnimationNodeId(rCond
.mxNode
)) != -1)
773 mpFS
->startElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay(), XML_evt
,
775 mpFS
->singleElementNS(XML_p
, XML_tn
, XML_val
, OString::number(nId
));
776 mpFS
->endElementNS(XML_p
, XML_cond
);
780 mpFS
->singleElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay(), XML_evt
,
785 mpFS
->singleElementNS(XML_p
, XML_cond
, XML_delay
, rCond
.getDelay());
788 void PPTXAnimationExport::WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType
)
790 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
791 Reference
<XAnimate
> rXAnimate(rXNode
, UNO_QUERY
);
795 const char* pCalcMode
= nullptr;
796 const char* pValueType
= nullptr;
797 bool bSimple
= (nXmlNodeType
!= XML_anim
);
802 switch (rXAnimate
->getCalcMode())
804 case AnimationCalcMode::DISCRETE
:
805 pCalcMode
= "discrete";
807 case AnimationCalcMode::LINEAR
:
812 switch (AnimationExporter::GetValueTypeForAttributeName(rXAnimate
->getAttributeName()))
814 case AnimationValueType::STRING
:
817 case AnimationValueType::NUMBER
:
820 case AnimationValueType::COLOR
:
826 if (nXmlNodeType
== XML_animMotion
)
829 Reference
<XAnimateMotion
> xMotion(rXNode
, UNO_QUERY
);
832 xMotion
->getPath() >>= aPath
;
833 ::basegfx::B2DPolyPolygon aPolyPoly
;
834 if (::basegfx::utils::importFromSvgD(aPolyPoly
, aPath
, true, nullptr))
835 aPath
= ::basegfx::utils::exportToSvgD(aPolyPoly
, false, false, true, true);
838 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_origin
, "layout", XML_path
, aPath
.toUtf8());
840 else if (nXmlNodeType
== XML_animRot
)
842 // when const char* is nullptr, the attribute is completely omitted in the output
843 const char* pBy
= nullptr;
844 const char* pFrom
= nullptr;
845 const char* pTo
= nullptr;
846 OString aBy
, aFrom
, aTo
;
848 Reference
<XAnimateTransform
> xTransform(rXNode
, UNO_QUERY
);
852 if (xTransform
->getBy() >>= value
)
854 aBy
= OString::number(static_cast<int>(value
* PER_DEGREE
));
858 if (xTransform
->getFrom() >>= value
)
860 aFrom
= OString::number(static_cast<int>(value
* PER_DEGREE
));
861 pFrom
= aFrom
.getStr();
864 if (xTransform
->getTo() >>= value
)
866 aTo
= OString::number(static_cast<int>(value
* PER_DEGREE
));
871 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_by
, pBy
, XML_from
, pFrom
, XML_to
, pTo
);
873 else if (nXmlNodeType
== XML_animClr
)
875 Reference
<XAnimateColor
> xColor(rXNode
, UNO_QUERY
);
876 const char* pColorSpace
= "rgb";
877 const char* pDirection
= nullptr;
878 if (xColor
.is() && xColor
->getColorInterpolation() == AnimationColorSpace::HSL
)
880 // Note: from, to, by can still be specified in any supported format.
882 pDirection
= xColor
->getDirection() ? "cw" : "ccw";
884 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_clrSpc
, pColorSpace
, XML_dir
, pDirection
,
885 XML_calcmode
, pCalcMode
, XML_valueType
, pValueType
);
889 OUString sFrom
, sTo
, sBy
;
890 if (rXAnimate
.is() && nXmlNodeType
== XML_anim
)
892 OUString sAttributeName
= rXAnimate
->getAttributeName();
894 = AnimationExporter::convertAnimateValue(rXAnimate
->getFrom(), sAttributeName
);
896 Any aTo
= AnimationExporter::convertAnimateValue(rXAnimate
->getTo(), sAttributeName
);
898 Any aBy
= AnimationExporter::convertAnimateValue(rXAnimate
->getBy(), sAttributeName
);
902 mpFS
->startElementNS(XML_p
, nXmlNodeType
, XML_calcmode
, pCalcMode
, XML_valueType
,
903 pValueType
, XML_from
,
904 sFrom
.isEmpty() ? nullptr : sFrom
.toUtf8().getStr(), XML_to
,
905 sTo
.isEmpty() ? nullptr : sTo
.toUtf8().getStr(), XML_by
,
906 sBy
.isEmpty() ? nullptr : sBy
.toUtf8().getStr());
907 bTo
= sTo
.isEmpty() && sFrom
.isEmpty() && sBy
.isEmpty();
910 WriteAnimationNodeAnimateInside(bSimple
, bTo
);
911 mpFS
->endElementNS(XML_p
, nXmlNodeType
);
914 void PPTXAnimationExport::WriteAnimationNodeAnimateInside(bool bSimple
, bool bWriteTo
)
916 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
917 Reference
<XAnimate
> rXAnimate(rXNode
, UNO_QUERY
);
921 const char* pAdditive
= nullptr;
925 switch (rXAnimate
->getAdditive())
927 case AnimationAdditiveMode::BASE
:
930 case AnimationAdditiveMode::SUM
:
933 case AnimationAdditiveMode::REPLACE
:
936 case AnimationAdditiveMode::MULTIPLY
:
939 case AnimationAdditiveMode::NONE
:
945 mpFS
->startElementNS(XML_p
, XML_cBhvr
, XML_additive
, pAdditive
);
946 WriteAnimationNodeCommonPropsStart();
948 Reference
<XIterateContainer
> xIterate(rXNode
->getParent(), UNO_QUERY
);
949 WriteAnimationTarget(xIterate
.is() ? xIterate
->getTarget() : rXAnimate
->getTarget());
951 Reference
<XAnimateTransform
> xTransform(rXNode
, UNO_QUERY
);
953 // The attribute name of AnimateTransform is "Transform", we have to fix it.
955 if (xTransform
.is() && xTransform
->getTransformType() == AnimationTransformType::ROTATE
)
958 WriteAnimationAttributeName(mpFS
, xTransform
.is() ? sNewAttr
: rXAnimate
->getAttributeName());
960 mpFS
->endElementNS(XML_p
, XML_cBhvr
);
961 WriteAnimateValues(mpFS
, rXAnimate
);
963 Reference
<XAnimateColor
> xColor(rXNode
, UNO_QUERY
);
967 WriteAnimateColorColor(mpFS
, xColor
->getBy(), XML_by
);
968 WriteAnimateColorColor(mpFS
, xColor
->getFrom(), XML_from
);
969 WriteAnimateColorColor(mpFS
, xColor
->getTo(), XML_to
);
971 else if (xTransform
.is() && xTransform
->getTransformType() == AnimationTransformType::SCALE
)
973 WriteAnimationProperty(mpFS
, rXAnimate
->getBy(), XML_by
);
974 WriteAnimationProperty(mpFS
, rXAnimate
->getFrom(), XML_from
);
975 WriteAnimationProperty(mpFS
, rXAnimate
->getTo(), XML_to
);
978 WriteAnimateTo(mpFS
, rXAnimate
->getTo(), rXAnimate
->getAttributeName());
981 void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart()
983 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
984 const char* pDuration
= nullptr;
985 const char* pRestart
= nullptr;
986 const char* pNodeType
= nullptr;
987 const char* pPresetClass
= nullptr;
988 const char* pFill
= nullptr;
989 double fDuration
= 0;
993 aAny
= rXNode
->getDuration();
998 if (aAny
>>= eTiming
)
1000 if (eTiming
== Timing_INDEFINITE
)
1001 pDuration
= "indefinite";
1007 pRestart
= convertAnimationRestart(rXNode
->getRestart());
1009 sal_Int16 nType
= mpContext
->getEffectNodeType();
1012 pNodeType
= convertEffectNodeType(nType
);
1013 if (nType
== EffectNodeType::TIMING_ROOT
)
1016 pDuration
= "indefinite";
1020 else if (nType
== EffectNodeType::MAIN_SEQUENCE
)
1022 pDuration
= "indefinite";
1026 sal_uInt32 nPresetClass
= mpContext
->getEffectPresetClass();
1027 if (nPresetClass
!= DFF_ANIM_PRESS_CLASS_USER_DEFINED
)
1028 pPresetClass
= convertEffectPresetClass(nPresetClass
);
1030 sal_uInt32 nPresetId
= 0;
1031 bool bPresetId
= false;
1032 const OUString
& rPresetId
= mpContext
->getEffectPresetId();
1033 if (rPresetId
.getLength() > 0)
1035 nPresetId
= AnimationExporter::GetPresetID(rPresetId
, nPresetClass
, bPresetId
);
1039 sal_uInt32 nPresetSubType
= 0;
1040 bool bPresetSubType
= false;
1041 const OUString
& sPresetSubType
= mpContext
->getEffectPresetSubType();
1042 if (sPresetSubType
.getLength() > 0)
1045 = AnimationExporter::TranslatePresetSubType(nPresetClass
, nPresetId
, sPresetSubType
);
1046 bPresetSubType
= true;
1049 if (nType
!= EffectNodeType::TIMING_ROOT
&& nType
!= EffectNodeType::MAIN_SEQUENCE
)
1051 // it doesn't seem to work right on root and mainseq nodes
1052 sal_Int16 nFill
= AnimationExporter::GetFillMode(rXNode
, AnimationFill::AUTO
);
1053 pFill
= convertAnimationFill(nFill
);
1056 bool bAutoReverse
= rXNode
->getAutoReverse();
1058 mpFS
->startElementNS(
1059 XML_p
, XML_cTn
, XML_id
, OString::number(GetNextAnimationNodeId(rXNode
)), XML_dur
,
1060 fDuration
!= 0 ? OString::number(static_cast<sal_Int32
>(fDuration
* 1000.0)).getStr()
1062 XML_autoRev
, bAutoReverse
? "1" : nullptr, XML_restart
, pRestart
, XML_nodeType
, pNodeType
,
1063 XML_fill
, pFill
, XML_presetClass
, pPresetClass
, XML_presetID
,
1064 bPresetId
? OString::number(nPresetId
).getStr() : nullptr, XML_presetSubtype
,
1065 bPresetSubType
? OString::number(nPresetSubType
).getStr() : nullptr);
1067 WriteAnimationCondList(mpContext
->getCondition(true), XML_stCondLst
);
1068 WriteAnimationCondList(mpContext
->getCondition(false), XML_endCondLst
);
1070 if (rXNode
->getType() == AnimationNodeType::ITERATE
)
1072 Reference
<XIterateContainer
> xIterate(rXNode
, UNO_QUERY
);
1075 const char* sType
= convertTextAnimationType(xIterate
->getIterateType());
1077 mpFS
->startElementNS(XML_p
, XML_iterate
, XML_type
, sType
);
1078 mpFS
->singleElementNS(XML_p
, XML_tmAbs
, XML_val
,
1079 OString::number(xIterate
->getIterateInterval() * 1000));
1080 mpFS
->endElementNS(XML_p
, XML_iterate
);
1084 const std::vector
<NodeContextPtr
>& aChildNodes
= mpContext
->getChildNodes();
1085 if (!aChildNodes
.empty())
1087 mpFS
->startElementNS(XML_p
, XML_childTnLst
);
1088 for (const NodeContextPtr
& pChildContext
: aChildNodes
)
1090 if (pChildContext
->isValid())
1091 WriteAnimationNode(pChildContext
);
1093 mpFS
->endElementNS(XML_p
, XML_childTnLst
);
1095 mpFS
->endElementNS(XML_p
, XML_cTn
);
1098 void PPTXAnimationExport::WriteAnimationNodeSeq()
1100 SAL_INFO("sd.eppt", "write animation node SEQ");
1102 mpFS
->startElementNS(XML_p
, XML_seq
);
1104 WriteAnimationNodeCommonPropsStart();
1106 WriteAnimationCondListForSeq(mpFS
, XML_prevCondLst
);
1107 WriteAnimationCondListForSeq(mpFS
, XML_nextCondLst
);
1109 mpFS
->endElementNS(XML_p
, XML_seq
);
1112 void PPTXAnimationExport::WriteAnimationNodeEffect()
1114 SAL_INFO("sd.eppt", "write animation node FILTER");
1115 Reference
<XTransitionFilter
> xFilter(getCurrentNode(), UNO_QUERY
);
1118 const char* pFilter
= ::ppt::AnimationExporter::FindTransitionName(
1119 xFilter
->getTransition(), xFilter
->getSubtype(), xFilter
->getDirection());
1120 const char* pMode
= xFilter
->getMode() ? "in" : "out";
1121 mpFS
->startElementNS(XML_p
, XML_animEffect
, XML_filter
, pFilter
, XML_transition
, pMode
);
1123 WriteAnimationNodeAnimateInside(false);
1125 mpFS
->endElementNS(XML_p
, XML_animEffect
);
1129 void PPTXAnimationExport::WriteAnimationNodeCommand()
1131 SAL_INFO("sd.eppt", "write animation node COMMAND");
1132 Reference
<XCommand
> xCommand(getCurrentNode(), UNO_QUERY
);
1136 const char* pType
= "call";
1137 const char* pCommand
= nullptr;
1138 switch (xCommand
->getCommand())
1140 case EffectCommands::VERB
:
1142 pCommand
= "1"; /* FIXME hardcoded viewing */
1144 case EffectCommands::PLAY
:
1147 case EffectCommands::TOGGLEPAUSE
:
1148 pCommand
= "togglePause";
1150 case EffectCommands::STOP
:
1154 SAL_WARN("sd.eppt", "unknown command: " << xCommand
->getCommand());
1158 mpFS
->startElementNS(XML_p
, XML_cmd
, XML_type
, pType
, XML_cmd
, pCommand
);
1160 WriteAnimationNodeAnimateInside(false);
1161 mpFS
->startElementNS(XML_p
, XML_cBhvr
);
1162 WriteAnimationNodeCommonPropsStart();
1163 WriteAnimationTarget(xCommand
->getTarget());
1164 mpFS
->endElementNS(XML_p
, XML_cBhvr
);
1166 mpFS
->endElementNS(XML_p
, XML_cmd
);
1169 void PPTXAnimationExport::WriteAnimationNodeAudio()
1171 SAL_INFO("sd.eppt", "write animation node audio");
1172 Reference
<XAudio
> xAudio(getCurrentNode(), UNO_QUERY
);
1178 if (!(xAudio
.is() && (xAudio
->getSource() >>= sUrl
) && !sUrl
.isEmpty()
1179 && sUrl
.endsWithIgnoreAsciiCase(".wav")))
1182 mrPowerPointExport
.embedEffectAudio(mpFS
, sUrl
, sRelId
, sName
);
1184 mpFS
->startElementNS(XML_p
, XML_audio
);
1185 mpFS
->startElementNS(XML_p
, XML_cMediaNode
);
1187 mpFS
->startElementNS(XML_p
, XML_cTn
);
1188 WriteAnimationCondList(mpContext
->getCondition(true), XML_stCondLst
);
1189 WriteAnimationCondList(mpContext
->getCondition(false), XML_endCondLst
);
1190 mpFS
->endElementNS(XML_p
, XML_cTn
);
1192 mpFS
->startElementNS(XML_p
, XML_tgtEl
);
1193 mpFS
->singleElementNS(XML_p
, XML_sndTgt
, FSNS(XML_r
, XML_embed
),
1194 sRelId
.isEmpty() ? nullptr : sRelId
.toUtf8().getStr(), XML_name
,
1195 sUrl
.isEmpty() ? nullptr : sName
.toUtf8().getStr());
1196 mpFS
->endElementNS(XML_p
, XML_tgtEl
);
1198 mpFS
->endElementNS(XML_p
, XML_cMediaNode
);
1199 mpFS
->endElementNS(XML_p
, XML_audio
);
1202 void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr
& pContext
)
1204 const NodeContext
* pSavedContext
= mpContext
;
1205 mpContext
= pContext
.get();
1207 const Reference
<XAnimationNode
>& rXNode
= getCurrentNode();
1209 SAL_INFO("sd.eppt", "export node type: " << rXNode
->getType());
1210 sal_Int32 xmlNodeType
= extractNodeType(rXNode
);
1212 switch (xmlNodeType
)
1215 mpFS
->startElementNS(XML_p
, xmlNodeType
);
1216 WriteAnimationNodeCommonPropsStart();
1217 mpFS
->endElementNS(XML_p
, xmlNodeType
);
1220 WriteAnimationNodeSeq();
1225 case XML_animMotion
:
1228 WriteAnimationNodeAnimate(xmlNodeType
);
1230 case XML_animEffect
:
1231 WriteAnimationNodeEffect();
1234 WriteAnimationNodeCommand();
1237 WriteAnimationNodeAudio();
1240 SAL_WARN("sd.eppt", "export ooxml node type: " << xmlNodeType
);
1244 mpContext
= pSavedContext
;
1247 void PPTXAnimationExport::WriteAnimations(const Reference
<XDrawPage
>& rXDrawPage
)
1249 Reference
<XAnimationNodeSupplier
> xNodeSupplier(rXDrawPage
, UNO_QUERY
);
1250 if (!xNodeSupplier
.is())
1253 const Reference
<XAnimationNode
> xNode(xNodeSupplier
->getAnimationNode());
1257 Reference
<XEnumerationAccess
> xEnumerationAccess(xNode
, UNO_QUERY
);
1258 if (!xEnumerationAccess
.is())
1261 Reference
<XEnumeration
> xEnumeration(xEnumerationAccess
->createEnumeration(), UNO_QUERY
);
1262 if (!(xEnumeration
.is() && xEnumeration
->hasMoreElements()))
1265 auto pNodeContext
= std::make_unique
<NodeContext
>(xNode
, false, false);
1266 if (pNodeContext
->isValid())
1268 mpFS
->startElementNS(XML_p
, XML_timing
);
1269 mpFS
->startElementNS(XML_p
, XML_tnLst
);
1271 WriteAnimationNode(pNodeContext
);
1273 mpFS
->endElementNS(XML_p
, XML_tnLst
);
1274 mpFS
->endElementNS(XML_p
, XML_timing
);
1278 sal_Int32
PPTXAnimationExport::GetNextAnimationNodeId(const Reference
<XAnimationNode
>& xNode
)
1280 sal_Int32 nId
= mrPowerPointExport
.GetNextAnimationNodeID();
1281 maAnimationNodeIdMap
[xNode
] = nId
;
1285 sal_Int32
PPTXAnimationExport::GetAnimationNodeId(const Reference
<XAnimationNode
>& xNode
)
1288 const auto& aIter
= maAnimationNodeIdMap
.find(xNode
);
1289 if (aIter
!= maAnimationNodeIdMap
.end())
1291 nId
= aIter
->second
;
1296 NodeContext::NodeContext(const Reference
<XAnimationNode
>& xNode
, bool bMainSeqChild
,
1297 bool bIsIterateChild
)
1299 , mbMainSeqChild(bMainSeqChild
)
1301 , mnEffectNodeType(-1)
1302 , mnEffectPresetClass(DFF_ANIM_PRESS_CLASS_USER_DEFINED
)
1308 initValid(initChildNodes(), bIsIterateChild
);
1311 void NodeContext::initUserData()
1313 assert(mxNode
.is());
1315 Sequence
<NamedValue
> aUserData
= mxNode
->getUserData();
1316 const Any
* aIndexedData
[DFF_ANIM_PROPERTY_ID_COUNT
];
1317 AnimationExporter::GetUserData(aUserData
, aIndexedData
, sizeof(aIndexedData
));
1319 const Any
* pAny
= aIndexedData
[DFF_ANIM_NODE_TYPE
];
1321 *pAny
>>= mnEffectNodeType
;
1323 pAny
= aIndexedData
[DFF_ANIM_PRESET_CLASS
];
1325 *pAny
>>= mnEffectPresetClass
;
1327 pAny
= aIndexedData
[DFF_ANIM_PRESET_ID
];
1329 *pAny
>>= msEffectPresetId
;
1331 pAny
= aIndexedData
[DFF_ANIM_PRESET_SUB_TYPE
];
1333 *pAny
>>= msEffectPresetSubType
;
1336 void NodeContext::initValid(bool bHasValidChild
, bool bIsIterateChild
)
1338 sal_Int16 nType
= mxNode
->getType();
1340 if (nType
== AnimationNodeType::ITERATE
)
1342 Reference
<XIterateContainer
> xIterate(mxNode
, UNO_QUERY
);
1343 mbValid
= xIterate
.is() && (bIsIterateChild
|| isValidTarget(xIterate
->getTarget()))
1344 && !maChildNodes
.empty();
1346 else if (nType
== AnimationNodeType::COMMAND
)
1348 Reference
<XCommand
> xCommand(mxNode
, UNO_QUERY
);
1349 mbValid
= xCommand
.is() && (bIsIterateChild
|| isValidTarget(xCommand
->getTarget()));
1351 else if (nType
== AnimationNodeType::PAR
|| nType
== AnimationNodeType::SEQ
)
1353 mbValid
= bHasValidChild
;
1355 else if (nType
== AnimationNodeType::AUDIO
)
1357 Reference
<XAudio
> xAudio(mxNode
, UNO_QUERY
);
1360 = xAudio
.is() && (xAudio
->getSource() >>= sURL
) && sURL
.endsWithIgnoreAsciiCase(".wav");
1364 Reference
<XAnimate
> xAnimate(mxNode
, UNO_QUERY
);
1365 mbValid
= xAnimate
.is() && (bIsIterateChild
|| isValidTarget(xAnimate
->getTarget()));
1369 bool NodeContext::initChildNodes()
1371 bool bValid
= false;
1372 Reference
<XEnumerationAccess
> xEnumerationAccess(mxNode
, UNO_QUERY
);
1373 if (xEnumerationAccess
.is())
1375 Reference
<XEnumeration
> xEnumeration(xEnumerationAccess
->createEnumeration(), UNO_QUERY
);
1376 bool bIsMainSeq
= mnEffectNodeType
== EffectNodeType::MAIN_SEQUENCE
;
1377 bool bIsIterateChild
= mxNode
->getType() == AnimationNodeType::ITERATE
;
1378 if (xEnumeration
.is())
1380 while (xEnumeration
->hasMoreElements())
1382 Reference
<XAnimationNode
> xChildNode(xEnumeration
->nextElement(), UNO_QUERY
);
1383 if (xChildNode
.is())
1386 = std::make_unique
<NodeContext
>(xChildNode
, bIsMainSeq
, bIsIterateChild
);
1387 if (pChildContext
->isValid())
1389 maChildNodes
.push_back(std::move(pChildContext
));
1397 Any
NodeContext::getCondition(bool bBegin
) const
1400 = (mnEffectNodeType
!= EffectNodeType::INTERACTIVE_SEQUENCE
|| maChildNodes
.empty());
1401 const Reference
<XAnimationNode
>& rNode
= bParent
? mxNode
: maChildNodes
[0]->getNode();
1403 return bBegin
? rNode
->getBegin() : rNode
->getEnd();
1406 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */